Skip to content

Commit 69dee75

Browse files
committed
refactor make_trace_groups to more general lib function
instead of cartesian-specific
1 parent 07aee8f commit 69dee75

File tree

10 files changed

+778
-798
lines changed

10 files changed

+778
-798
lines changed

src/lib/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ lib.clearThrottle = throttleModule.clear;
118118

119119
lib.getGraphDiv = require('./get_graph_div');
120120

121+
lib.makeTraceGroups = require('./make_trace_groups');
122+
121123
lib._ = require('./localize');
122124

123125
lib.notifier = require('./notifier');

src/plots/cartesian/make_trace_groups.js renamed to src/lib/make_trace_groups.js

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,18 @@
99

1010
'use strict';
1111

12-
var d3 = require('d3');
13-
1412
/**
15-
* helper for cartesian trace types to manage trace groups and plot traces
16-
* into them
13+
* General helper to manage trace groups based on calcdata
1714
*
18-
* @param {div} gd: plot div
19-
* @param {object} plotinfo: the cartesian subplot info object
20-
* @param {array} cdModule: array of calcdata items for this
21-
* module and subplot combination
2215
* @param {d3.selection} traceLayer: a selection containing a single group
2316
* to draw these traces into
17+
* @param {array} cdModule: array of calcdata items for this
18+
* module and subplot combination. Assumes the calcdata item for each
19+
* trace is an array with the fullData trace attached to the first item.
2420
* @param {string} cls: the class attribute to give each trace group
2521
* so you can give multiple classes separated by spaces
26-
* @param {function} plotOneFn: a function that will plot one trace
27-
* takes arguments:
28-
* gd
29-
* plotinfo
30-
* cd: calcdata array for this one trace
31-
* plotGroup: d3 selection of the single group to draw into
3222
*/
33-
module.exports = function makeTraceGroups(gd, plotinfo, cdModule, traceLayer, cls, plotOneFn) {
23+
module.exports = function makeTraceGroups(traceLayer, cdModule, cls) {
3424
var traces = traceLayer.selectAll('g.' + cls.replace(/\s/g, '.'))
3525
.data(cdModule, function(cd) { return cd[0].trace.uid; });
3626

@@ -39,10 +29,7 @@ module.exports = function makeTraceGroups(gd, plotinfo, cdModule, traceLayer, cl
3929
traces.enter().append('g')
4030
.attr('class', cls);
4131

42-
traces.each(function(cd) {
43-
plotOneFn(gd, plotinfo, cd, d3.select(this));
44-
})
45-
.order();
32+
traces.order();
4633

4734
return traces;
4835
};

src/traces/bar/plot.js

Lines changed: 98 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ var svgTextUtils = require('../../lib/svg_text_utils');
1919
var Color = require('../../components/color');
2020
var Drawing = require('../../components/drawing');
2121
var Registry = require('../../registry');
22-
var makeTraceGroups = require('../../plots/cartesian/make_trace_groups');
2322

2423
var attributes = require('./attributes'),
2524
attributeText = attributes.text,
@@ -32,128 +31,128 @@ var attributes = require('./attributes'),
3231
var TEXTPAD = 3;
3332

3433
module.exports = function plot(gd, plotinfo, cdbar, barLayer) {
35-
var bartraces = makeTraceGroups(gd, plotinfo, cdbar, barLayer, 'trace bars', plotOne);
36-
37-
// error bars are on the top
38-
Registry.getComponentMethod('errorbars', 'plot')(bartraces, plotinfo);
39-
};
40-
41-
function plotOne(gd, plotinfo, cd, plotGroup) {
4234
var xa = plotinfo.xaxis;
4335
var ya = plotinfo.yaxis;
4436
var fullLayout = gd._fullLayout;
45-
var cd0 = cd[0];
46-
var t = cd0.t;
47-
var trace = cd0.trace;
4837

49-
if(!plotinfo.isRangePlot) cd0.node3 = plotGroup;
38+
var bartraces = Lib.makeTraceGroups(barLayer, cdbar, 'trace bars').each(function(cd) {
39+
var plotGroup = d3.select(this);
40+
var cd0 = cd[0];
41+
var t = cd0.t;
42+
var trace = cd0.trace;
5043

51-
var poffset = t.poffset;
52-
var poffsetIsArray = Array.isArray(poffset);
44+
if(!plotinfo.isRangePlot) cd0.node3 = plotGroup;
5345

54-
var pointGroup = Lib.ensureSingle(plotGroup, 'g', 'points');
46+
var poffset = t.poffset;
47+
var poffsetIsArray = Array.isArray(poffset);
5548

56-
var bars = pointGroup.selectAll('g.point').data(Lib.identity);
49+
var pointGroup = Lib.ensureSingle(plotGroup, 'g', 'points');
5750

58-
bars.enter().append('g')
59-
.classed('point', true);
51+
var bars = pointGroup.selectAll('g.point').data(Lib.identity);
6052

61-
bars.exit().remove();
53+
bars.enter().append('g')
54+
.classed('point', true);
6255

63-
bars.each(function(di, i) {
64-
var bar = d3.select(this);
56+
bars.exit().remove();
6557

66-
// now display the bar
67-
// clipped xf/yf (2nd arg true): non-positive
68-
// log values go off-screen by plotwidth
69-
// so you see them continue if you drag the plot
70-
var p0 = di.p + ((poffsetIsArray) ? poffset[i] : poffset),
71-
p1 = p0 + di.w,
72-
s0 = di.b,
73-
s1 = s0 + di.s;
58+
bars.each(function(di, i) {
59+
var bar = d3.select(this);
7460

75-
var x0, x1, y0, y1;
76-
if(trace.orientation === 'h') {
77-
y0 = ya.c2p(p0, true);
78-
y1 = ya.c2p(p1, true);
79-
x0 = xa.c2p(s0, true);
80-
x1 = xa.c2p(s1, true);
61+
// now display the bar
62+
// clipped xf/yf (2nd arg true): non-positive
63+
// log values go off-screen by plotwidth
64+
// so you see them continue if you drag the plot
65+
var p0 = di.p + ((poffsetIsArray) ? poffset[i] : poffset),
66+
p1 = p0 + di.w,
67+
s0 = di.b,
68+
s1 = s0 + di.s;
8169

82-
// for selections
83-
di.ct = [x1, (y0 + y1) / 2];
84-
}
85-
else {
86-
x0 = xa.c2p(p0, true);
87-
x1 = xa.c2p(p1, true);
88-
y0 = ya.c2p(s0, true);
89-
y1 = ya.c2p(s1, true);
70+
var x0, x1, y0, y1;
71+
if(trace.orientation === 'h') {
72+
y0 = ya.c2p(p0, true);
73+
y1 = ya.c2p(p1, true);
74+
x0 = xa.c2p(s0, true);
75+
x1 = xa.c2p(s1, true);
9076

91-
// for selections
92-
di.ct = [(x0 + x1) / 2, y1];
93-
}
77+
// for selections
78+
di.ct = [x1, (y0 + y1) / 2];
79+
}
80+
else {
81+
x0 = xa.c2p(p0, true);
82+
x1 = xa.c2p(p1, true);
83+
y0 = ya.c2p(s0, true);
84+
y1 = ya.c2p(s1, true);
9485

95-
if(!isNumeric(x0) || !isNumeric(x1) ||
96-
!isNumeric(y0) || !isNumeric(y1) ||
97-
x0 === x1 || y0 === y1) {
98-
bar.remove();
99-
return;
100-
}
86+
// for selections
87+
di.ct = [(x0 + x1) / 2, y1];
88+
}
89+
90+
if(!isNumeric(x0) || !isNumeric(x1) ||
91+
!isNumeric(y0) || !isNumeric(y1) ||
92+
x0 === x1 || y0 === y1) {
93+
bar.remove();
94+
return;
95+
}
10196

102-
var lw = (di.mlw + 1 || trace.marker.line.width + 1 ||
103-
(di.trace ? di.trace.marker.line.width : 0) + 1) - 1,
104-
offset = d3.round((lw / 2) % 1, 2);
97+
var lw = (di.mlw + 1 || trace.marker.line.width + 1 ||
98+
(di.trace ? di.trace.marker.line.width : 0) + 1) - 1,
99+
offset = d3.round((lw / 2) % 1, 2);
105100

106-
function roundWithLine(v) {
107-
// if there are explicit gaps, don't round,
108-
// it can make the gaps look crappy
109-
return (fullLayout.bargap === 0 && fullLayout.bargroupgap === 0) ?
110-
d3.round(Math.round(v) - offset, 2) : v;
111-
}
101+
function roundWithLine(v) {
102+
// if there are explicit gaps, don't round,
103+
// it can make the gaps look crappy
104+
return (fullLayout.bargap === 0 && fullLayout.bargroupgap === 0) ?
105+
d3.round(Math.round(v) - offset, 2) : v;
106+
}
112107

113-
function expandToVisible(v, vc) {
114-
// if it's not in danger of disappearing entirely,
115-
// round more precisely
116-
return Math.abs(v - vc) >= 2 ? roundWithLine(v) :
117-
// but if it's very thin, expand it so it's
118-
// necessarily visible, even if it might overlap
119-
// its neighbor
120-
(v > vc ? Math.ceil(v) : Math.floor(v));
121-
}
108+
function expandToVisible(v, vc) {
109+
// if it's not in danger of disappearing entirely,
110+
// round more precisely
111+
return Math.abs(v - vc) >= 2 ? roundWithLine(v) :
112+
// but if it's very thin, expand it so it's
113+
// necessarily visible, even if it might overlap
114+
// its neighbor
115+
(v > vc ? Math.ceil(v) : Math.floor(v));
116+
}
122117

123-
if(!gd._context.staticPlot) {
124-
// if bars are not fully opaque or they have a line
125-
// around them, round to integer pixels, mainly for
126-
// safari so we prevent overlaps from its expansive
127-
// pixelation. if the bars ARE fully opaque and have
128-
// no line, expand to a full pixel to make sure we
129-
// can see them
130-
var op = Color.opacity(di.mc || trace.marker.color),
131-
fixpx = (op < 1 || lw > 0.01) ?
132-
roundWithLine : expandToVisible;
133-
x0 = fixpx(x0, x1);
134-
x1 = fixpx(x1, x0);
135-
y0 = fixpx(y0, y1);
136-
y1 = fixpx(y1, y0);
137-
}
118+
if(!gd._context.staticPlot) {
119+
// if bars are not fully opaque or they have a line
120+
// around them, round to integer pixels, mainly for
121+
// safari so we prevent overlaps from its expansive
122+
// pixelation. if the bars ARE fully opaque and have
123+
// no line, expand to a full pixel to make sure we
124+
// can see them
125+
var op = Color.opacity(di.mc || trace.marker.color),
126+
fixpx = (op < 1 || lw > 0.01) ?
127+
roundWithLine : expandToVisible;
128+
x0 = fixpx(x0, x1);
129+
x1 = fixpx(x1, x0);
130+
y0 = fixpx(y0, y1);
131+
y1 = fixpx(y1, y0);
132+
}
138133

139-
Lib.ensureSingle(bar, 'path')
140-
.style('vector-effect', 'non-scaling-stroke')
141-
.attr('d',
142-
'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z')
143-
.call(Drawing.setClipUrl, plotinfo.layerClipId);
134+
Lib.ensureSingle(bar, 'path')
135+
.style('vector-effect', 'non-scaling-stroke')
136+
.attr('d',
137+
'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z')
138+
.call(Drawing.setClipUrl, plotinfo.layerClipId);
144139

145-
appendBarText(gd, bar, cd, i, x0, x1, y0, y1);
140+
appendBarText(gd, bar, cd, i, x0, x1, y0, y1);
146141

147-
if(plotinfo.layerClipId) {
148-
Drawing.hideOutsideRangePoint(di, bar.select('text'), xa, ya, trace.xcalendar, trace.ycalendar);
149-
}
142+
if(plotinfo.layerClipId) {
143+
Drawing.hideOutsideRangePoint(di, bar.select('text'), xa, ya, trace.xcalendar, trace.ycalendar);
144+
}
145+
});
146+
147+
// lastly, clip points groups of `cliponaxis !== false` traces
148+
// on `plotinfo._hasClipOnAxisFalse === true` subplots
149+
var hasClipOnAxisFalse = cd0.trace.cliponaxis === false;
150+
Drawing.setClipUrl(plotGroup, hasClipOnAxisFalse ? null : plotinfo.layerClipId);
150151
});
151152

152-
// lastly, clip points groups of `cliponaxis !== false` traces
153-
// on `plotinfo._hasClipOnAxisFalse === true` subplots
154-
var hasClipOnAxisFalse = cd0.trace.cliponaxis === false;
155-
Drawing.setClipUrl(plotGroup, hasClipOnAxisFalse ? null : plotinfo.layerClipId);
156-
}
153+
// error bars are on the top
154+
Registry.getComponentMethod('errorbars', 'plot')(bartraces, plotinfo);
155+
};
157156

158157
function appendBarText(gd, bar, calcTrace, i, x0, x1, y0, y1) {
159158
var textPosition;

src/traces/box/plot.js

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,62 +12,59 @@ var d3 = require('d3');
1212

1313
var Lib = require('../../lib');
1414
var Drawing = require('../../components/drawing');
15-
var makeTraceGroups = require('../../plots/cartesian/make_trace_groups');
1615

1716
// constants for dynamic jitter (ie less jitter for sparser points)
1817
var JITTERCOUNT = 5; // points either side of this to include
1918
var JITTERSPREAD = 0.01; // fraction of IQR to count as "dense"
2019

2120
function plot(gd, plotinfo, cdbox, boxLayer) {
22-
makeTraceGroups(gd, plotinfo, cdbox, boxLayer, 'trace boxes', plotOne);
23-
}
24-
25-
function plotOne(gd, plotinfo, cd, plotGroup) {
2621
var fullLayout = gd._fullLayout;
2722
var xa = plotinfo.xaxis;
2823
var ya = plotinfo.yaxis;
29-
var cd0 = cd[0];
30-
var t = cd0.t;
31-
var trace = cd0.trace;
32-
if(!plotinfo.isRangePlot) cd0.node3 = plotGroup;
3324
var numBoxes = fullLayout._numBoxes;
34-
3525
var groupFraction = (1 - fullLayout.boxgap);
36-
3726
var group = (fullLayout.boxmode === 'group' && numBoxes > 1);
38-
// box half width
39-
var bdPos = t.dPos * groupFraction * (1 - fullLayout.boxgroupgap) / (group ? numBoxes : 1);
40-
// box center offset
41-
var bPos = group ? 2 * t.dPos * (-0.5 + (t.num + 0.5) / numBoxes) * groupFraction : 0;
42-
// whisker width
43-
var wdPos = bdPos * trace.whiskerwidth;
44-
45-
if(trace.visible !== true || t.empty) {
46-
plotGroup.remove();
47-
return;
48-
}
4927

50-
var posAxis, valAxis;
28+
Lib.makeTraceGroups(boxLayer, cdbox, 'trace boxes').each(function(cd) {
29+
var plotGroup = d3.select(this);
30+
var cd0 = cd[0];
31+
var t = cd0.t;
32+
var trace = cd0.trace;
33+
if(!plotinfo.isRangePlot) cd0.node3 = plotGroup;
34+
// box half width
35+
var bdPos = t.dPos * groupFraction * (1 - fullLayout.boxgroupgap) / (group ? numBoxes : 1);
36+
// box center offset
37+
var bPos = group ? 2 * t.dPos * (-0.5 + (t.num + 0.5) / numBoxes) * groupFraction : 0;
38+
// whisker width
39+
var wdPos = bdPos * trace.whiskerwidth;
40+
41+
if(trace.visible !== true || t.empty) {
42+
plotGroup.remove();
43+
return;
44+
}
5145

52-
if(trace.orientation === 'h') {
53-
posAxis = ya;
54-
valAxis = xa;
55-
} else {
56-
posAxis = xa;
57-
valAxis = ya;
58-
}
46+
var posAxis, valAxis;
47+
48+
if(trace.orientation === 'h') {
49+
posAxis = ya;
50+
valAxis = xa;
51+
} else {
52+
posAxis = xa;
53+
valAxis = ya;
54+
}
5955

60-
// save the box size and box position for use by hover
61-
t.bPos = bPos;
62-
t.bdPos = bdPos;
63-
t.wdPos = wdPos;
64-
// half-width within which to accept hover for this box
65-
// always split the distance to the closest box
66-
t.wHover = t.dPos * (group ? groupFraction / numBoxes : 1);
67-
68-
plotBoxAndWhiskers(plotGroup, {pos: posAxis, val: valAxis}, trace, t);
69-
plotPoints(plotGroup, {x: xa, y: ya}, trace, t);
70-
plotBoxMean(plotGroup, {pos: posAxis, val: valAxis}, trace, t);
56+
// save the box size and box position for use by hover
57+
t.bPos = bPos;
58+
t.bdPos = bdPos;
59+
t.wdPos = wdPos;
60+
// half-width within which to accept hover for this box
61+
// always split the distance to the closest box
62+
t.wHover = t.dPos * (group ? groupFraction / numBoxes : 1);
63+
64+
plotBoxAndWhiskers(plotGroup, {pos: posAxis, val: valAxis}, trace, t);
65+
plotPoints(plotGroup, {x: xa, y: ya}, trace, t);
66+
plotBoxMean(plotGroup, {pos: posAxis, val: valAxis}, trace, t);
67+
});
7168
}
7269

7370
function plotBoxAndWhiskers(sel, axes, trace, t) {

0 commit comments

Comments
 (0)