Skip to content

Commit faaf894

Browse files
committed
mv bPos, bdPos computation to Box.crossTraceCalc
.. out of Box.plot and Violin.plot, AND use them to provide exact (!) findExtremes values.
1 parent ace30bd commit faaf894

File tree

4 files changed

+88
-101
lines changed

4 files changed

+88
-101
lines changed

src/traces/box/cross_trace_calc.js

Lines changed: 84 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@ var Lib = require('../../lib');
1313

1414
var orientations = ['v', 'h'];
1515

16-
17-
function getPosition(di) {
18-
return di.pos;
19-
}
20-
2116
function crossTraceCalc(gd, plotinfo) {
2217
var calcdata = gd.calcdata;
2318
var xa = plotinfo.xaxis;
@@ -27,8 +22,6 @@ function crossTraceCalc(gd, plotinfo) {
2722
var orientation = orientations[i];
2823
var posAxis = orientation === 'h' ? ya : xa;
2924
var boxList = [];
30-
var minPad = 0;
31-
var maxPad = 0;
3225

3326
// make list of boxes / candlesticks
3427
// For backward compatibility, candlesticks are treated as if they *are* box traces here
@@ -45,27 +38,24 @@ function crossTraceCalc(gd, plotinfo) {
4538
trace.yaxis === ya._id
4639
) {
4740
boxList.push(j);
48-
49-
if(trace.boxpoints) {
50-
minPad = Math.max(minPad, trace.jitter - trace.pointpos - 1);
51-
maxPad = Math.max(maxPad, trace.jitter + trace.pointpos - 1);
52-
}
5341
}
5442
}
5543

56-
setPositionOffset('box', gd, boxList, posAxis, [minPad, maxPad]);
44+
setPositionOffset('box', gd, boxList, posAxis);
5745
}
5846
}
5947

60-
function setPositionOffset(traceType, gd, boxList, posAxis, pad) {
48+
function setPositionOffset(traceType, gd, boxList, posAxis) {
6149
var calcdata = gd.calcdata;
6250
var fullLayout = gd._fullLayout;
63-
var pointList = [];
51+
var axId = posAxis._id;
52+
var axLetter = axId.charAt(0);
6453

6554
// N.B. reused in violin
6655
var numKey = traceType === 'violin' ? '_numViolins' : '_numBoxes';
6756

6857
var i, j, calcTrace;
58+
var pointList = [];
6959

7060
// make list of box points
7161
for(i = 0; i < boxList.length; i++) {
@@ -78,9 +68,8 @@ function setPositionOffset(traceType, gd, boxList, posAxis, pad) {
7868
if(!pointList.length) return;
7969

8070
// box plots - update dPos based on multiple traces
81-
// and then use for posAxis autorange
8271
var boxdv = Lib.distinctVals(pointList);
83-
var dPos = boxdv.minDiff / 2;
72+
var dPos0 = boxdv.minDiff / 2;
8473

8574
// if there's no duplication of x points,
8675
// disable 'group' mode by setting counter to 1
@@ -91,33 +80,92 @@ function setPositionOffset(traceType, gd, boxList, posAxis, pad) {
9180
// check for forced minimum dtick
9281
Axes.minDtick(posAxis, boxdv.minDiff, boxdv.vals[0], true);
9382

94-
var gap = fullLayout[traceType + 'gap'];
95-
var groupgap = fullLayout[traceType + 'groupgap'];
96-
var padfactor = (1 - gap) * (1 - groupgap) * dPos / fullLayout[numKey];
83+
var num = fullLayout[numKey];
84+
var group = (fullLayout[traceType + 'mode'] === 'group' && num > 1);
85+
var groupFraction = 1 - fullLayout[traceType + 'gap'];
86+
var groupGapFraction = 1 - fullLayout[traceType + 'groupgap'];
9787

98-
// Find maximum trace width
99-
// we baseline this at dPos
10088
for(i = 0; i < boxList.length; i++) {
10189
calcTrace = calcdata[boxList[i]];
102-
// set the width of this box
103-
// override dPos with trace.width if present
104-
var thisDPos = calcTrace[0].t.dPos = (calcTrace[0].trace.width / 2) || dPos;
105-
var positions = calcTrace.map(getPosition);
106-
// autoscale the x axis - including space for points if they're off the side
107-
// TODO: this will overdo it if the outermost boxes don't have
108-
// their points as far out as the other boxes
90+
10991
var trace = calcTrace[0].trace;
110-
var vpadminus_pos_side = (trace.pointpos <= 0) ? (-trace.pointpos) * padfactor * fullLayout[numKey] : 0;
111-
var vpadplus_neg_side = (trace.pointpos >= 0) ? trace.pointpos * padfactor * fullLayout[numKey] : 0;
112-
var side = calcTrace[0].trace.side;
113-
var vpadminus = (side === 'positive') ? vpadminus_pos_side : (thisDPos + pad[0] * padfactor);
114-
var vpadplus = (side === 'negative') ? vpadplus_neg_side : (thisDPos + pad[1] * padfactor);
115-
var extremes = Axes.findExtremes(posAxis, positions, {
92+
var t = calcTrace[0].t;
93+
var width = trace.width;
94+
var side = trace.side;
95+
var pointpos = trace.pointpos;
96+
97+
// position coordinate delta
98+
var dPos;
99+
// box half width;
100+
var bdPos;
101+
// box center offset
102+
var bPos;
103+
// half-width within which to accept hover for this box/violin
104+
// always split the distance to the closest box/violin
105+
var wHover;
106+
107+
if(width) {
108+
dPos = bdPos = wHover = width / 2;
109+
bPos = 0;
110+
} else {
111+
dPos = dPos0;
112+
bdPos = dPos * groupFraction * groupGapFraction / (group ? num : 1);
113+
bPos = group ? 2 * dPos * (-0.5 + (t.num + 0.5) / num) * groupFraction : 0;
114+
wHover = dPos * (group ? groupFraction / num : 1);
115+
}
116+
t.dPos = dPos;
117+
t.bPos = bPos;
118+
t.bdPos = bdPos;
119+
t.wHover = wHover;
120+
121+
var edge = bPos + bdPos;
122+
// data-space padding
123+
var vpadplus = 0;
124+
var vpadminus = 0;
125+
// pixel-space padding
126+
var ppadplus;
127+
var ppadminus;
128+
129+
if(side === 'positive') {
130+
vpadplus = edge;
131+
vpadminus = bPos;
132+
} else if(side === 'negative') {
133+
vpadplus = bPos;
134+
vpadminus = edge;
135+
} else {
136+
vpadplus = edge;
137+
vpadminus = edge;
138+
}
139+
140+
if(trace.boxpoints || trace.points) {
141+
var jitter = trace.jitter;
142+
var ms = trace.marker.size / 2;
143+
144+
if((pointpos + jitter) > 0) {
145+
ppadplus = ms;
146+
var pp = bPos + bdPos * (pointpos + jitter);
147+
if(pp > vpadplus) vpadplus = pp;
148+
}
149+
if((pointpos - jitter) < 0) {
150+
ppadminus = ms;
151+
var pm = -bPos - bdPos * (pointpos - jitter);
152+
if(pm > vpadminus) vpadminus = pm;
153+
}
154+
}
155+
156+
// calcdata[i][j] are in ascending order
157+
var firstPos = calcTrace[0].pos;
158+
var lastPos = calcTrace[calcTrace.length - 1].pos;
159+
160+
trace._extremes[axId] = Axes.findExtremes(posAxis, [firstPos, lastPos], {
116161
vpadminus: vpadminus,
117162
vpadplus: vpadplus,
163+
// N.B. SVG px-space positive/negative
164+
ppadminus: {x: ppadminus, y: ppadplus}[axLetter],
165+
ppadplus: {x: ppadplus, y: ppadminus}[axLetter],
166+
// add 5% of both sides
118167
padded: true
119168
});
120-
calcTrace[0].trace._extremes[posAxis._id] = extremes;
121169
}
122170
}
123171

src/traces/box/plot.js

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,8 @@ var JITTERCOUNT = 5; // points either side of this to include
1818
var JITTERSPREAD = 0.01; // fraction of IQR to count as "dense"
1919

2020
function plot(gd, plotinfo, cdbox, boxLayer) {
21-
var fullLayout = gd._fullLayout;
2221
var xa = plotinfo.xaxis;
2322
var ya = plotinfo.yaxis;
24-
var numBoxes = fullLayout._numBoxes;
25-
var group = (fullLayout.boxmode === 'group' && numBoxes > 1);
26-
var groupFraction = (1 - fullLayout.boxgap);
27-
var groupGapFraction = 1 - fullLayout.boxgroupgap;
2823

2924
Lib.makeTraceGroups(boxLayer, cdbox, 'trace boxes').each(function(cd) {
3025
var plotGroup = d3.select(this);
@@ -33,23 +28,8 @@ function plot(gd, plotinfo, cdbox, boxLayer) {
3328
var trace = cd0.trace;
3429
if(!plotinfo.isRangePlot) cd0.node3 = plotGroup;
3530

36-
// position coordinate delta
37-
var dPos = t.dPos;
38-
// box half width;
39-
var bdPos;
40-
// box center offset
41-
var bPos;
42-
43-
if(trace.width) {
44-
bdPos = dPos;
45-
bPos = 0;
46-
} else {
47-
bdPos = dPos * groupFraction * groupGapFraction / (group ? numBoxes : 1);
48-
bPos = group ? 2 * dPos * (-0.5 + (t.num + 0.5) / numBoxes) * groupFraction : 0;
49-
}
50-
5131
// whisker width
52-
var wdPos = bdPos * trace.whiskerwidth;
32+
t.wdPos = t.bdPos * trace.whiskerwidth;
5333

5434
if(trace.visible !== true || t.empty) {
5535
plotGroup.remove();
@@ -66,14 +46,6 @@ function plot(gd, plotinfo, cdbox, boxLayer) {
6646
valAxis = ya;
6747
}
6848

69-
// save the box size and box position for use by hover
70-
t.bPos = bPos;
71-
t.bdPos = bdPos;
72-
t.wdPos = wdPos;
73-
// half-width within which to accept hover for this box
74-
// always split the distance to the closest box
75-
t.wHover = t.dPos * (group ? groupFraction / numBoxes : 1);
76-
7749
plotBoxAndWhiskers(plotGroup, {pos: posAxis, val: valAxis}, trace, t);
7850
plotPoints(plotGroup, {x: xa, y: ya}, trace, t);
7951
plotBoxMean(plotGroup, {pos: posAxis, val: valAxis}, trace, t);

src/traces/violin/cross_trace_calc.js

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ module.exports = function crossTraceCalc(gd, plotinfo) {
2020
var orientation = orientations[i];
2121
var posAxis = orientation === 'h' ? ya : xa;
2222
var violinList = [];
23-
var minPad = 0;
24-
var maxPad = 0;
2523

2624
for(var j = 0; j < calcdata.length; j++) {
2725
var cd = calcdata[j];
@@ -35,14 +33,9 @@ module.exports = function crossTraceCalc(gd, plotinfo) {
3533
trace.yaxis === ya._id
3634
) {
3735
violinList.push(j);
38-
39-
if(trace.points !== false) {
40-
minPad = Math.max(minPad, trace.jitter - trace.pointpos - 1);
41-
maxPad = Math.max(maxPad, trace.jitter + trace.pointpos - 1);
42-
}
4336
}
4437
}
4538

46-
setPositionOffset('violin', gd, violinList, posAxis, [minPad, maxPad]);
39+
setPositionOffset('violin', gd, violinList, posAxis);
4740
}
4841
};

src/traces/violin/plot.js

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) {
2020
var fullLayout = gd._fullLayout;
2121
var xa = plotinfo.xaxis;
2222
var ya = plotinfo.yaxis;
23-
var numViolins = fullLayout._numViolins;
24-
var group = (fullLayout.violinmode === 'group' && numViolins > 1);
25-
var groupFraction = 1 - fullLayout.violingap;
26-
var groupGapFraction = 1 - fullLayout.violingroupgap;
2723

2824
function makePath(pts) {
2925
var segments = linePoints(pts, {
@@ -44,35 +40,13 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) {
4440
var trace = cd0.trace;
4541
if(!plotinfo.isRangePlot) cd0.node3 = plotGroup;
4642

47-
// position coordinate delta
48-
var dPos = t.dPos;
49-
// violin max half width
50-
var bdPos;
51-
// violin center offset
52-
var bPos;
53-
// half-width within which to accept hover for this violin
54-
// always split the distance to the closest violin
55-
var wHover;
56-
57-
if(trace.width) {
58-
bdPos = dPos;
59-
bPos = 0;
60-
wHover = dPos;
61-
} else {
62-
bdPos = dPos * groupFraction * groupGapFraction / (group ? numViolins : 1);
63-
bPos = group ? 2 * dPos * (-0.5 + (t.num + 0.5) / numViolins) * groupFraction : 0;
64-
wHover = dPos * (group ? groupFraction / numViolins : 1);
65-
}
66-
67-
t.bdPos = bdPos;
68-
t.bPos = bPos;
69-
t.wHover = wHover;
70-
7143
if(trace.visible !== true || t.empty) {
7244
plotGroup.remove();
7345
return;
7446
}
7547

48+
var bPos = t.bPos;
49+
var bdPos = t.bdPos;
7650
var valAxis = plotinfo[t.valLetter + 'axis'];
7751
var posAxis = plotinfo[t.posLetter + 'axis'];
7852
var hasBothSides = trace.side === 'both';

0 commit comments

Comments
 (0)