Skip to content

Commit c8f38ff

Browse files
committed
factor out box defaults and boxpoint plot methods
... in preparation for violin
1 parent 8f34227 commit c8f38ff

File tree

4 files changed

+158
-117
lines changed

4 files changed

+158
-117
lines changed

src/traces/box/defaults.js

+30-13
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,25 @@ var Color = require('../../components/color');
1414

1515
var attributes = require('./attributes');
1616

17-
module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
17+
function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
1818
function coerce(attr, dflt) {
1919
return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
2020
}
2121

22+
handleSampleDefaults(traceIn, traceOut, coerce, layout);
23+
if(traceOut.visible === false) return;
24+
25+
coerce('line.color', (traceIn.marker || {}).color || defaultColor);
26+
coerce('line.width');
27+
coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5));
28+
29+
coerce('whiskerwidth');
30+
coerce('boxmean');
31+
32+
handlePointsDefaults(traceIn, traceOut, coerce, {prefix: 'box'});
33+
}
34+
35+
function handleSampleDefaults(traceIn, traceOut, coerce, layout) {
2236
var y = coerce('y');
2337
var x = coerce('x');
2438

@@ -39,25 +53,22 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
3953
handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
4054

4155
coerce('orientation', defaultOrientation);
56+
}
4257

43-
coerce('line.color', (traceIn.marker || {}).color || defaultColor);
44-
coerce('line.width');
45-
coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5));
46-
47-
coerce('whiskerwidth');
48-
coerce('boxmean');
58+
function handlePointsDefaults(traceIn, traceOut, coerce, opts) {
59+
var prefix = opts.prefix;
4960

5061
var outlierColorDflt = Lib.coerce2(traceIn, traceOut, attributes, 'marker.outliercolor');
5162
var lineoutliercolor = coerce('marker.line.outliercolor');
5263

53-
var boxpoints = coerce(
54-
'boxpoints',
64+
var points = coerce(
65+
prefix + 'points',
5566
(outlierColorDflt || lineoutliercolor) ? 'suspectedoutliers' : undefined
5667
);
5768

58-
if(boxpoints) {
59-
coerce('jitter', boxpoints === 'all' ? 0.3 : 0);
60-
coerce('pointpos', boxpoints === 'all' ? -1.5 : 0);
69+
if(points) {
70+
coerce('jitter', points === 'all' ? 0.3 : 0);
71+
coerce('pointpos', points === 'all' ? -1.5 : 0);
6172

6273
coerce('marker.symbol');
6374
coerce('marker.opacity');
@@ -66,7 +77,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
6677
coerce('marker.line.color');
6778
coerce('marker.line.width');
6879

69-
if(boxpoints === 'suspectedoutliers') {
80+
if(points === 'suspectedoutliers') {
7081
coerce('marker.line.outliercolor', traceOut.marker.color);
7182
coerce('marker.line.outlierwidth');
7283
}
@@ -77,4 +88,10 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
7788
}
7889

7990
coerce('hoveron');
91+
}
92+
93+
module.exports = {
94+
supplyDefaults: supplyDefaults,
95+
handleSampleDefaults: handleSampleDefaults,
96+
handlePointsDefaults: handlePointsDefaults
8097
};

src/traces/box/index.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ var Box = {};
1212

1313
Box.attributes = require('./attributes');
1414
Box.layoutAttributes = require('./layout_attributes');
15-
Box.supplyDefaults = require('./defaults');
16-
Box.supplyLayoutDefaults = require('./layout_defaults');
15+
Box.supplyDefaults = require('./defaults').supplyDefaults;
16+
Box.supplyLayoutDefaults = require('./layout_defaults').supplyLayoutDefaults;
1717
Box.calc = require('./calc');
1818
Box.setPositions = require('./set_positions');
19-
Box.plot = require('./plot');
19+
Box.plot = require('./plot').plot;
2020
Box.style = require('./style');
2121
Box.hoverPoints = require('./hover');
2222
Box.selectPoints = require('./select');

src/traces/box/layout_defaults.js

+16-8
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ var Registry = require('../../registry');
1212
var Lib = require('../../lib');
1313
var layoutAttributes = require('./layout_attributes');
1414

15-
module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
16-
function coerce(attr, dflt) {
17-
return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
18-
}
19-
15+
function _supply(layoutIn, layoutOut, fullData, coerce, prefix) {
2016
var hasBoxes;
2117
for(var i = 0; i < fullData.length; i++) {
2218
if(Registry.traceIs(fullData[i], 'box')) {
@@ -26,7 +22,19 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
2622
}
2723
if(!hasBoxes) return;
2824

29-
coerce('boxmode');
30-
coerce('boxgap');
31-
coerce('boxgroupgap');
25+
coerce(prefix + 'mode');
26+
coerce(prefix + 'gap');
27+
coerce(prefix + 'groupgap');
28+
}
29+
30+
function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
31+
function coerce(attr, dflt) {
32+
return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
33+
}
34+
_supply(layoutIn, layoutOut, fullData, coerce, 'box');
35+
}
36+
37+
module.exports = {
38+
supplyLayoutDefaults: supplyLayoutDefaults,
39+
_supply: _supply
3240
};

src/traces/box/plot.js

+109-93
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function rand() {
3333
var JITTERCOUNT = 5; // points either side of this to include
3434
var JITTERSPREAD = 0.01; // fraction of IQR to count as "dense"
3535

36-
module.exports = function plot(gd, plotinfo, cdbox) {
36+
function plot(gd, plotinfo, cdbox) {
3737
var fullLayout = gd._fullLayout;
3838
var xa = plotinfo.xaxis;
3939
var ya = plotinfo.yaxis;
@@ -78,9 +78,6 @@ module.exports = function plot(gd, plotinfo, cdbox) {
7878
t.bPos = bPos;
7979
t.bdPos = bdPos;
8080

81-
// repeatable pseudorandom number generator
82-
seed();
83-
8481
// boxes and whiskers
8582
sel.selectAll('path.box')
8683
.data(Lib.identity)
@@ -121,95 +118,7 @@ module.exports = function plot(gd, plotinfo, cdbox) {
121118

122119
// draw points, if desired
123120
if(trace.boxpoints) {
124-
sel.selectAll('g.points')
125-
// since box plot points get an extra level of nesting, each
126-
// box needs the trace styling info
127-
.data(function(d) {
128-
d.forEach(function(v) {
129-
v.t = t;
130-
v.trace = trace;
131-
});
132-
return d;
133-
})
134-
.enter().append('g')
135-
.attr('class', 'points')
136-
.selectAll('path')
137-
.data(function(d) {
138-
var i;
139-
140-
var pts = trace.boxpoints === 'all' ?
141-
d.pts :
142-
d.pts.filter(function(pt) { return (pt.v < d.lf || pt.v > d.uf); });
143-
144-
// normally use IQR, but if this is 0 or too small, use max-min
145-
var typicalSpread = Math.max((d.max - d.min) / 10, d.q3 - d.q1);
146-
var minSpread = typicalSpread * 1e-9;
147-
var spreadLimit = typicalSpread * JITTERSPREAD;
148-
var jitterFactors = [];
149-
var maxJitterFactor = 0;
150-
var newJitter;
151-
152-
// dynamic jitter
153-
if(trace.jitter) {
154-
if(typicalSpread === 0) {
155-
// edge case of no spread at all: fall back to max jitter
156-
maxJitterFactor = 1;
157-
jitterFactors = new Array(pts.length);
158-
for(i = 0; i < pts.length; i++) {
159-
jitterFactors[i] = 1;
160-
}
161-
} else {
162-
for(i = 0; i < pts.length; i++) {
163-
var i0 = Math.max(0, i - JITTERCOUNT);
164-
var pmin = pts[i0].v;
165-
var i1 = Math.min(pts.length - 1, i + JITTERCOUNT);
166-
var pmax = pts[i1].v;
167-
168-
if(trace.boxpoints !== 'all') {
169-
if(pts[i].v < d.lf) pmax = Math.min(pmax, d.lf);
170-
else pmin = Math.max(pmin, d.uf);
171-
}
172-
173-
var jitterFactor = Math.sqrt(spreadLimit * (i1 - i0) / (pmax - pmin + minSpread)) || 0;
174-
jitterFactor = Lib.constrain(Math.abs(jitterFactor), 0, 1);
175-
176-
jitterFactors.push(jitterFactor);
177-
maxJitterFactor = Math.max(jitterFactor, maxJitterFactor);
178-
}
179-
}
180-
newJitter = trace.jitter * 2 / maxJitterFactor;
181-
}
182-
183-
// fills in 'x' and 'y' in calcdata 'pts' item
184-
for(i = 0; i < pts.length; i++) {
185-
var pt = pts[i];
186-
var v = pt.v;
187-
188-
var jitterOffset = trace.jitter ?
189-
(newJitter * jitterFactors[i] * (rand() - 0.5)) :
190-
0;
191-
192-
var posPx = d.pos + bPos + bdPos * (trace.pointpos + jitterOffset);
193-
194-
if(trace.orientation === 'h') {
195-
pt.y = posPx;
196-
pt.x = v;
197-
} else {
198-
pt.x = posPx;
199-
pt.y = v;
200-
}
201-
202-
// tag suspected outliers
203-
if(trace.boxpoints === 'suspectedoutliers' && v < d.uo && v > d.lo) {
204-
pt.so = true;
205-
}
206-
}
207-
208-
return pts;
209-
})
210-
.enter().append('path')
211-
.classed('point', true)
212-
.call(Drawing.translatePoints, xa, ya);
121+
plotPoints(sel, plotinfo, trace, t);
213122
}
214123

215124
// draw mean (and stdev diamond) if desired
@@ -244,4 +153,111 @@ module.exports = function plot(gd, plotinfo, cdbox) {
244153
});
245154
}
246155
});
156+
}
157+
158+
function plotPoints(sel, plotinfo, trace, t) {
159+
var xa = plotinfo.xaxis;
160+
var ya = plotinfo.yaxis;
161+
var bdPos = t.bdPos;
162+
var bPos = t.bPos;
163+
164+
var mode = trace.boxpoints;
165+
166+
// repeatable pseudorandom number generator
167+
seed();
168+
169+
sel.selectAll('g.points')
170+
// since box plot points get an extra level of nesting, each
171+
// box needs the trace styling info
172+
.data(function(d) {
173+
d.forEach(function(v) {
174+
v.t = t;
175+
v.trace = trace;
176+
});
177+
return d;
178+
})
179+
.enter().append('g')
180+
.attr('class', 'points')
181+
.selectAll('path')
182+
.data(function(d) {
183+
var i;
184+
185+
var pts = mode === 'all' ?
186+
d.pts :
187+
d.pts.filter(function(pt) { return (pt.v < d.lf || pt.v > d.uf); });
188+
189+
// normally use IQR, but if this is 0 or too small, use max-min
190+
var typicalSpread = Math.max((d.max - d.min) / 10, d.q3 - d.q1);
191+
var minSpread = typicalSpread * 1e-9;
192+
var spreadLimit = typicalSpread * JITTERSPREAD;
193+
var jitterFactors = [];
194+
var maxJitterFactor = 0;
195+
var newJitter;
196+
197+
// dynamic jitter
198+
if(trace.jitter) {
199+
if(typicalSpread === 0) {
200+
// edge case of no spread at all: fall back to max jitter
201+
maxJitterFactor = 1;
202+
jitterFactors = new Array(pts.length);
203+
for(i = 0; i < pts.length; i++) {
204+
jitterFactors[i] = 1;
205+
}
206+
} else {
207+
for(i = 0; i < pts.length; i++) {
208+
var i0 = Math.max(0, i - JITTERCOUNT);
209+
var pmin = pts[i0].v;
210+
var i1 = Math.min(pts.length - 1, i + JITTERCOUNT);
211+
var pmax = pts[i1].v;
212+
213+
if(mode !== 'all') {
214+
if(pts[i].v < d.lf) pmax = Math.min(pmax, d.lf);
215+
else pmin = Math.max(pmin, d.uf);
216+
}
217+
218+
var jitterFactor = Math.sqrt(spreadLimit * (i1 - i0) / (pmax - pmin + minSpread)) || 0;
219+
jitterFactor = Lib.constrain(Math.abs(jitterFactor), 0, 1);
220+
221+
jitterFactors.push(jitterFactor);
222+
maxJitterFactor = Math.max(jitterFactor, maxJitterFactor);
223+
}
224+
}
225+
newJitter = trace.jitter * 2 / maxJitterFactor;
226+
}
227+
228+
// fills in 'x' and 'y' in calcdata 'pts' item
229+
for(i = 0; i < pts.length; i++) {
230+
var pt = pts[i];
231+
var v = pt.v;
232+
233+
var jitterOffset = trace.jitter ?
234+
(newJitter * jitterFactors[i] * (rand() - 0.5)) :
235+
0;
236+
237+
var posPx = d.pos + bPos + bdPos * (trace.pointpos + jitterOffset);
238+
239+
if(trace.orientation === 'h') {
240+
pt.y = posPx;
241+
pt.x = v;
242+
} else {
243+
pt.x = posPx;
244+
pt.y = v;
245+
}
246+
247+
// tag suspected outliers
248+
if(mode === 'suspectedoutliers' && v < d.uo && v > d.lo) {
249+
pt.so = true;
250+
}
251+
}
252+
253+
return pts;
254+
})
255+
.enter().append('path')
256+
.classed('point', true)
257+
.call(Drawing.translatePoints, xa, ya);
258+
}
259+
260+
module.exports = {
261+
plot: plot,
262+
plotPoints: plotPoints
247263
};

0 commit comments

Comments
 (0)