Skip to content

Commit 6ffc379

Browse files
committed
implement violin 'inner' style options
- i.e. showinnerbox & showmeanline and friends.
1 parent 4a40fc7 commit 6ffc379

File tree

5 files changed

+196
-10
lines changed

5 files changed

+196
-10
lines changed

src/traces/violin/attributes.js

+64-6
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,6 @@ module.exports = {
9999
].join(' ')
100100
},
101101

102-
// TODO need attribute(s) similar to 'boxmean' to toggle lines for:
103-
// - mean
104-
// - median
105-
// - std
106-
// - quartiles
107-
108102
line: {
109103
color: {
110104
valType: 'color',
@@ -123,6 +117,70 @@ module.exports = {
123117
editType: 'plot'
124118
},
125119
fillcolor: boxAttrs.fillcolor,
120+
121+
// TODO update description
122+
points: boxAttrs.boxpoints,
123+
jitter: boxAttrs.jitter,
124+
pointpos: boxAttrs.pointpos,
125+
marker: boxAttrs.marker,
126+
text: boxAttrs.text,
127+
128+
showinnerbox: {
129+
valType: 'boolean',
130+
dflt: false,
131+
role: 'info',
132+
editType: 'plot',
133+
description: '.'
134+
},
135+
innerboxwidth: {
136+
valType: 'number',
137+
min: 0,
138+
max: 1,
139+
dflt: 0.25,
140+
role: 'info',
141+
editType: 'plot',
142+
description: '...'
143+
},
144+
innerboxlinecolor: {
145+
valType: 'color',
146+
role: 'style',
147+
editType: 'style',
148+
description: ''
149+
},
150+
innerboxfillcolor: {
151+
valType: 'color',
152+
role: 'style',
153+
editType: 'style',
154+
description: ''
155+
},
156+
innerboxlinewidth: {
157+
valType: 'number',
158+
min: 0,
159+
role: 'style',
160+
editType: 'style',
161+
description: ''
162+
},
163+
164+
showmeanline: {
165+
valType: 'boolean',
166+
dflt: false,
167+
role: 'info',
168+
editType: 'plot',
169+
description: 'Toggle'
170+
},
171+
meanlinecolor: {
172+
valType: 'color',
173+
role: 'style',
174+
editType: 'style',
175+
description: ''
176+
},
177+
meanlinewidth: {
178+
valType: 'number',
179+
min: 0,
180+
role: 'style',
181+
editType: 'style',
182+
description: ''
183+
},
126184
hoveron: boxAttrs.hoveron
127185

128186
side: {

src/traces/violin/defaults.js

+28
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
1818
function coerce(attr, dflt) {
1919
return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
2020
}
21+
function coerce2(attr, dflt) {
22+
return Lib.coerce2(traceIn, traceOut, attributes, attr, dflt);
23+
}
2124

2225
boxDefaults.handleSampleDefaults(traceIn, traceOut, coerce, layout);
2326
if(traceOut.visible === false) return;
@@ -33,6 +36,31 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
3336
if(Array.isArray(span)) spanmodeDflt = 'manual';
3437
coerce('spanmode', spanmodeDflt);
3538

39+
var lineColor = coerce('line.color', (traceIn.marker || {}).color || defaultColor);
40+
var lineWidth = coerce('line.width');
41+
var fillColor = coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5));
3642

3743
boxDefaults.handlePointsDefaults(traceIn, traceOut, coerce, {prefix: ''});
44+
45+
var show;
46+
47+
var innerBoxWidth = coerce2('innerboxwidth');
48+
var innerBoxFillColor = coerce2('innerboxfillcolor', fillColor);
49+
var innerBoxLineColor = coerce2('innerboxlinecolor', lineColor);
50+
var innerBoxLineWidth = coerce2('innerboxlinewidth', lineWidth);
51+
show = coerce('showinnerbox', Boolean(innerBoxWidth || innerBoxFillColor || innerBoxLineColor || innerBoxLineWidth));
52+
if(!show) {
53+
delete traceOut.innerboxwidth;
54+
delete traceOut.innerboxfillcolor;
55+
delete traceOut.innerboxlinecolor;
56+
delete traceOut.innerboxlinewidth;
57+
}
58+
59+
var meanLineColor = coerce2('meanlinecolor', lineColor);
60+
var meanLineWidth = coerce2('meanlinewidth', lineWidth);
61+
show = coerce('showmeanline', Boolean(meanLineColor || meanLineWidth));
62+
if(!show) {
63+
delete traceOut.meanlinecolor;
64+
delete traceOut.meanlinewidth;
65+
}
3866
};

src/traces/violin/helpers.js

+27
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,31 @@ exports.makeKDE = function(calcItem, trace, vals) {
3535
};
3636
};
3737

38+
exports.getPositionOnKdePath = function(calcItem, trace, valuePx) {
39+
var posLetter, valLetter;
40+
41+
if(trace.orientation === 'h') {
42+
posLetter = 'y';
43+
valLetter = 'x';
44+
} else {
45+
posLetter = 'x';
46+
valLetter = 'y';
47+
}
48+
49+
var pointOnPath = Lib.findPointOnPath(
50+
calcItem.path,
51+
valuePx,
52+
valLetter,
53+
{pathLength: calcItem.pathLength}
54+
);
55+
56+
var posCenterPx = calcItem.posCenterPx;
57+
var posOnPath0 = pointOnPath[posLetter];
58+
var posOnPath1 = trace.side === 'both' ?
59+
2 * posCenterPx - posOnPath0 :
60+
posCenterPx;
61+
62+
return [posOnPath0, posOnPath1];
63+
};
64+
3865
exports.extractVal = function(o) { return o.v; };

src/traces/violin/plot.js

+62-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ var Lib = require('../../lib');
1313
var Drawing = require('../../components/drawing');
1414
var boxPlot = require('../box/plot');
1515
var linePoints = require('../scatter/line_points');
16+
var helpers = require('./helpers');
1617

1718
module.exports = function plot(gd, plotinfo, cd) {
1819
var fullLayout = gd._fullLayout;
@@ -128,10 +129,68 @@ module.exports = function plot(gd, plotinfo, cd) {
128129

129130
});
130131

131-
if(trace.points) {
132-
boxPlot.plotPoints(sel, plotinfo, trace, t);
132+
if(trace.showinnerbox) {
133+
var innerBoxWidth = trace.innerboxwidth;
134+
var innerBoxLineWidth = trace.innerboxlinewidth;
135+
var bdPosScaled;
136+
var bPosPxOffset;
137+
138+
if(hasBothSides) {
139+
bdPosScaled = bdPos * innerBoxWidth;
140+
bPosPxOffset = 0;
141+
} else if(hasPositiveSide) {
142+
bdPosScaled = [0, bdPos * innerBoxWidth / 2];
143+
bPosPxOffset = -innerBoxLineWidth;
144+
} else {
145+
bdPosScaled = [bdPos * innerBoxWidth / 2, 0];
146+
bPosPxOffset = innerBoxLineWidth;
147+
}
148+
149+
// do not draw whiskers on inner boxes
150+
trace.whiskerwidth = 0;
151+
152+
boxPlot.plotBoxAndWhiskers(sel, {pos: posAxis, val: valAxis}, trace, {
153+
bPos: bPos,
154+
bdPos: bdPosScaled,
155+
bPosPxOffset: bPosPxOffset
156+
});
157+
158+
// if both showinnerbox and showmeanline are turned on, show mean
159+
// line inside inner box
160+
161+
if(trace.showmeanline) {
162+
boxPlot.plotBoxMean(sel, {pos: posAxis, val: valAxis}, trace, {
163+
bPos: bPos,
164+
bdPos: bdPosScaled,
165+
bPosPxOffset: bPosPxOffset
166+
});
167+
}
168+
}
169+
else {
170+
if(trace.showmeanline) {
171+
sel.selectAll('path.mean')
172+
.data(Lib.identity)
173+
.enter().append('path')
174+
.attr('class', 'mean')
175+
.style({
176+
fill: 'none',
177+
'vector-effect': 'non-scaling-stroke'
178+
})
179+
.each(function(d) {
180+
var v = valAxis.c2p(d.mean, true);
181+
var p = helpers.getPositionOnKdePath(d, trace, v);
182+
183+
d3.select(this).attr('d',
184+
trace.orientation === 'h' ?
185+
'M' + v + ',' + p[0] + 'V' + p[1] :
186+
'M' + p[0] + ',' + v + 'H' + p[1]
187+
);
188+
});
189+
}
133190
}
134191

135-
// TODO quartile line etc
192+
if(trace.points) {
193+
boxPlot.plotPoints(sel, {x: xa, y: ya}, trace, t);
194+
}
136195
});
137196
};

src/traces/violin/style.js

+15-1
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,25 @@ module.exports = function style(gd) {
2121
var sel = d3.select(this);
2222

2323
sel.selectAll('path.violin')
24-
.style('stroke-width', '2px')
24+
.style('stroke-width', trace.line.width + 'px')
2525
.call(Color.stroke, trace.line.color)
2626
.call(Color.fill, trace.fillcolor);
2727

28+
sel.selectAll('path.box')
29+
.style('stroke-width', trace.innerboxlinewidth + 'px')
30+
.call(Color.stroke, trace.innerboxlinecolor)
31+
.call(Color.fill, trace.innerboxfillcolor);
32+
2833
sel.selectAll('g.points path')
2934
.call(Drawing.pointStyle, trace, gd);
35+
36+
var meanLineWidth = trace.meanlinewidth;
37+
38+
sel.selectAll('path.mean')
39+
.style({
40+
'stroke-width': meanLineWidth + 'px',
41+
'stroke-dasharray': (2 * meanLineWidth) + 'px,' + meanLineWidth + 'px'
42+
})
43+
.call(Color.stroke, trace.meanlinecolor);
3044
});
3145
};

0 commit comments

Comments
 (0)