Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4e48cd7

Browse filesBrowse files
committed
fix violin box/meanline/pts removal
1 parent 47349c7 commit 4e48cd7
Copy full SHA for 4e48cd7

File tree

Expand file treeCollapse file tree

4 files changed

+149
-85
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+149
-85
lines changed

‎src/traces/box/plot.js

Copy file name to clipboardExpand all lines: src/traces/box/plot.js
+29-22Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,9 @@ function plot(gd, plotinfo, cdbox, boxLayer) {
7373
// always split the distance to the closest box
7474
t.wHover = t.dPos * (group ? groupFraction / numBoxes : 1);
7575

76-
// boxes and whiskers
7776
plotBoxAndWhiskers(sel, {pos: posAxis, val: valAxis}, trace, t);
78-
79-
// draw points, if desired
80-
if(trace.boxpoints) {
81-
plotPoints(sel, {x: xa, y: ya}, trace, t);
82-
}
83-
84-
// draw mean (and stdev diamond) if desired
85-
if(trace.boxmean) {
86-
plotBoxMean(sel, {pos: posAxis, val: valAxis}, trace, t);
87-
}
77+
plotPoints(sel, {x: xa, y: ya}, trace, t);
78+
plotBoxMean(sel, {pos: posAxis, val: valAxis}, trace, t);
8879
});
8980
}
9081

@@ -109,7 +100,14 @@ function plotBoxAndWhiskers(sel, axes, trace, t) {
109100
bdPos1 = t.bdPos;
110101
}
111102

112-
var paths = sel.selectAll('path.box').data(Lib.identity);
103+
var fn;
104+
if(trace.type === 'box' ||
105+
(trace.type === 'violin' && (trace.box || {}).visible)
106+
) {
107+
fn = Lib.identity;
108+
}
109+
110+
var paths = sel.selectAll('path.box').data(fn || []);
113111

114112
paths.enter().append('path')
115113
.style('vector-effect', 'non-scaling-stroke')
@@ -187,16 +185,18 @@ function plotPoints(sel, axes, trace, t) {
187185
// repeatable pseudo-random number generator
188186
Lib.seedPseudoRandom();
189187

190-
var gPoints = sel.selectAll('g.points')
191-
// since box plot points get an extra level of nesting, each
192-
// box needs the trace styling info
193-
.data(function(d) {
194-
d.forEach(function(v) {
195-
v.t = t;
196-
v.trace = trace;
197-
});
198-
return d;
188+
// since box plot points get an extra level of nesting, each
189+
// box needs the trace styling info
190+
var fn = function(d) {
191+
d.forEach(function(v) {
192+
v.t = t;
193+
v.trace = trace;
199194
});
195+
return d;
196+
};
197+
198+
var gPoints = sel.selectAll('g.points')
199+
.data(mode ? fn : []);
200200

201201
gPoints.enter().append('g')
202202
.attr('class', 'points');
@@ -306,7 +306,14 @@ function plotBoxMean(sel, axes, trace, t) {
306306
bdPos1 = t.bdPos;
307307
}
308308

309-
var paths = sel.selectAll('path.mean').data(Lib.identity);
309+
var fn;
310+
if(trace.type === 'box' && trace.boxmean ||
311+
(trace.type === 'violin' && (trace.box || {}).visible && (trace.meanline || {}).visible)
312+
) {
313+
fn = Lib.identity;
314+
}
315+
316+
var paths = sel.selectAll('path.mean').data(fn || []);
310317

311318
paths.enter().append('path')
312319
.attr('class', 'mean')

‎src/traces/violin/plot.js

Copy file name to clipboardExpand all lines: src/traces/violin/plot.js
+52-59Lines changed: 52 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ module.exports = function plot(gd, plotinfo, cd, violinLayer) {
6969
var hasBothSides = trace.side === 'both';
7070
var hasPositiveSide = hasBothSides || trace.side === 'positive';
7171
var hasNegativeSide = hasBothSides || trace.side === 'negative';
72-
var hasBox = trace.box && trace.box.visible;
73-
var hasMeanLine = trace.meanline && trace.meanline.visible;
7472
var groupStats = fullLayout._violinScaleGroupStats[trace.scalegroup];
7573

7674
var violins = sel.selectAll('path.violin').data(Lib.identity);
@@ -149,66 +147,61 @@ module.exports = function plot(gd, plotinfo, cd, violinLayer) {
149147
d.pathLength = d.path.getTotalLength() / (hasBothSides ? 2 : 1);
150148
});
151149

152-
if(hasBox) {
153-
var boxWidth = trace.box.width;
154-
var boxLineWidth = trace.box.line.width;
155-
var bdPosScaled;
156-
var bPosPxOffset;
150+
var boxAttrs = trace.box || {};
151+
var boxWidth = boxAttrs.width;
152+
var boxLineWidth = (boxAttrs.line || {}).width;
153+
var bdPosScaled;
154+
var bPosPxOffset;
155+
156+
if(hasBothSides) {
157+
bdPosScaled = bdPos * boxWidth;
158+
bPosPxOffset = 0;
159+
} else if(hasPositiveSide) {
160+
bdPosScaled = [0, bdPos * boxWidth / 2];
161+
bPosPxOffset = -boxLineWidth;
162+
} else {
163+
bdPosScaled = [bdPos * boxWidth / 2, 0];
164+
bPosPxOffset = boxLineWidth;
165+
}
157166

158-
if(hasBothSides) {
159-
bdPosScaled = bdPos * boxWidth;
160-
bPosPxOffset = 0;
161-
} else if(hasPositiveSide) {
162-
bdPosScaled = [0, bdPos * boxWidth / 2];
163-
bPosPxOffset = -boxLineWidth;
164-
} else {
165-
bdPosScaled = [bdPos * boxWidth / 2, 0];
166-
bPosPxOffset = boxLineWidth;
167-
}
167+
// inner box
168+
boxPlot.plotBoxAndWhiskers(sel, {pos: posAxis, val: valAxis}, trace, {
169+
bPos: bPos,
170+
bdPos: bdPosScaled,
171+
bPosPxOffset: bPosPxOffset
172+
});
168173

169-
boxPlot.plotBoxAndWhiskers(sel, {pos: posAxis, val: valAxis}, trace, {
170-
bPos: bPos,
171-
bdPos: bdPosScaled,
172-
bPosPxOffset: bPosPxOffset
173-
});
174-
175-
// if both box and meanline are visible, show mean line inside box
176-
if(hasMeanLine) {
177-
boxPlot.plotBoxMean(sel, {pos: posAxis, val: valAxis}, trace, {
178-
bPos: bPos,
179-
bdPos: bdPosScaled,
180-
bPosPxOffset: bPosPxOffset
181-
});
182-
}
183-
}
184-
else {
185-
if(hasMeanLine) {
186-
var meanPaths = sel.selectAll('path.mean').data(Lib.identity);
187-
188-
meanPaths.enter().append('path')
189-
.attr('class', 'mean')
190-
.style({
191-
fill: 'none',
192-
'vector-effect': 'non-scaling-stroke'
193-
});
194-
195-
meanPaths.exit().remove();
196-
197-
meanPaths.each(function(d) {
198-
var v = valAxis.c2p(d.mean, true);
199-
var p = helpers.getPositionOnKdePath(d, trace, v);
200-
201-
d3.select(this).attr('d',
202-
trace.orientation === 'h' ?
203-
'M' + v + ',' + p[0] + 'V' + p[1] :
204-
'M' + p[0] + ',' + v + 'H' + p[1]
205-
);
206-
});
207-
}
208-
}
174+
// meanline insider box
175+
boxPlot.plotBoxMean(sel, {pos: posAxis, val: valAxis}, trace, {
176+
bPos: bPos,
177+
bdPos: bdPosScaled,
178+
bPosPxOffset: bPosPxOffset
179+
});
209180

210-
if(trace.points) {
211-
boxPlot.plotPoints(sel, {x: xa, y: ya}, trace, t);
181+
var fn;
182+
if(!(trace.box || {}).visible && (trace.meanline || {}).visible) {
183+
fn = Lib.identity;
212184
}
185+
186+
// N.B. use different class name than boxPlot.plotBoxMean,
187+
// to avoid selectAll conflict
188+
var meanPaths = sel.selectAll('path.meanline').data(fn || []);
189+
meanPaths.enter().append('path')
190+
.attr('class', 'meanline')
191+
.style('fill', 'none')
192+
.style('vector-effect', 'non-scaling-stroke');
193+
meanPaths.exit().remove();
194+
meanPaths.each(function(d) {
195+
var v = valAxis.c2p(d.mean, true);
196+
var p = helpers.getPositionOnKdePath(d, trace, v);
197+
198+
d3.select(this).attr('d',
199+
trace.orientation === 'h' ?
200+
'M' + v + ',' + p[0] + 'V' + p[1] :
201+
'M' + p[0] + ',' + v + 'H' + p[1]
202+
);
203+
});
204+
205+
boxPlot.plotPoints(sel, {x: xa, y: ya}, trace, t);
213206
});
214207
};

‎src/traces/violin/style.js

Copy file name to clipboardExpand all lines: src/traces/violin/style.js
+10-4Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,17 @@ module.exports = function style(gd, cd) {
3535
.call(Color.stroke, boxLine.color)
3636
.call(Color.fill, box.fillcolor);
3737

38+
var meanLineStyle = {
39+
'stroke-width': meanLineWidth + 'px',
40+
'stroke-dasharray': (2 * meanLineWidth) + 'px,' + meanLineWidth + 'px'
41+
};
42+
3843
sel.selectAll('path.mean')
39-
.style({
40-
'stroke-width': meanLineWidth + 'px',
41-
'stroke-dasharray': (2 * meanLineWidth) + 'px,' + meanLineWidth + 'px'
42-
})
44+
.style(meanLineStyle)
45+
.call(Color.stroke, meanline.color);
46+
47+
sel.selectAll('path.meanline')
48+
.style(meanLineStyle)
4349
.call(Color.stroke, meanline.color);
4450

4551
stylePoints(sel, trace, gd);

‎test/jasmine/tests/violin_test.js

Copy file name to clipboardExpand all lines: test/jasmine/tests/violin_test.js
+58Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,3 +493,61 @@ describe('Test violin hover:', function() {
493493
});
494494
});
495495
});
496+
497+
describe('Test violin restyle:', function() {
498+
var gd;
499+
500+
beforeEach(function() {
501+
gd = createGraphDiv();
502+
});
503+
504+
afterEach(destroyGraphDiv);
505+
506+
it('should be able to add/remove innner parts', function(done) {
507+
var fig = Lib.extendDeep({}, require('@mocks/violin_old-faithful.json'));
508+
// start with just 1 violin
509+
delete fig.data[0].meanline;
510+
delete fig.data[0].points;
511+
512+
function _assertOne(msg, exp, trace3, k, query) {
513+
expect(trace3.selectAll(query).size())
514+
.toBe(exp[k] || 0, k + ' - ' + msg);
515+
}
516+
517+
function _assert(msg, exp) {
518+
var trace3 = d3.select(gd).select('.violinlayer > .trace');
519+
_assertOne(msg, exp, trace3, 'violinCnt', 'path.violin');
520+
_assertOne(msg, exp, trace3, 'boxCnt', 'path.box');
521+
_assertOne(msg, exp, trace3, 'meanlineInBoxCnt', 'path.mean');
522+
_assertOne(msg, exp, trace3, 'meanlineOutOfBoxCnt', 'path.meanline');
523+
_assertOne(msg, exp, trace3, 'ptsCnt', 'path.point');
524+
}
525+
526+
Plotly.plot(gd, fig)
527+
.then(function() {
528+
_assert('base', {violinCnt: 1});
529+
})
530+
.then(function() { return Plotly.restyle(gd, 'box.visible', true); })
531+
.then(function() {
532+
_assert('with inner box', {violinCnt: 1, boxCnt: 1});
533+
})
534+
.then(function() { return Plotly.restyle(gd, 'meanline.visible', true); })
535+
.then(function() {
536+
_assert('with inner box & meanline', {violinCnt: 1, boxCnt: 1, meanlineInBoxCnt: 1});
537+
})
538+
.then(function() { return Plotly.restyle(gd, 'box.visible', false); })
539+
.then(function() {
540+
_assert('with meanline', {violinCnt: 1, meanlineOutOfBoxCnt: 1});
541+
})
542+
.then(function() { return Plotly.restyle(gd, 'points', 'all'); })
543+
.then(function() {
544+
_assert('with meanline & pts', {violinCnt: 1, meanlineOutOfBoxCnt: 1, ptsCnt: 272});
545+
})
546+
.then(function() { return Plotly.restyle(gd, 'meanline.visible', false); })
547+
.then(function() {
548+
_assert('with pts', {violinCnt: 1, ptsCnt: 272});
549+
})
550+
.catch(failTest)
551+
.then(done);
552+
});
553+
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.