diff --git a/src/traces/box/plot.js b/src/traces/box/plot.js index d82eabfcf88..ea99fc26071 100644 --- a/src/traces/box/plot.js +++ b/src/traces/box/plot.js @@ -52,8 +52,11 @@ function plot(gd, plotinfo, cdbox, boxLayer) { } function plotBoxAndWhiskers(sel, axes, trace, t) { - var posAxis = axes.pos; + var isHorizontal = trace.orientation === 'h'; var valAxis = axes.val; + var posAxis = axes.pos; + var posHasRangeBreaks = !!posAxis.rangebreaks; + var bPos = t.bPos; var wdPos = t.wdPos || 0; var bPosPxOffset = t.bPosPxOffset || 0; @@ -87,11 +90,15 @@ function plotBoxAndWhiskers(sel, axes, trace, t) { if(d.empty) return 'M0,0Z'; var lcenter = posAxis.c2l(d.pos + bPos, true); - var posc = posAxis.l2p(lcenter) + bPosPxOffset; + var pos0 = posAxis.l2p(lcenter - bdPos0) + bPosPxOffset; var pos1 = posAxis.l2p(lcenter + bdPos1) + bPosPxOffset; - var posw0 = posAxis.l2p(lcenter - wdPos) + bPosPxOffset; - var posw1 = posAxis.l2p(lcenter + wdPos) + bPosPxOffset; + var posc = posHasRangeBreaks ? (pos0 + pos1) / 2 : posAxis.l2p(lcenter) + bPosPxOffset; + + var r = trace.whiskerwidth; + var posw0 = posHasRangeBreaks ? pos0 * r + (1 - r) * posc : posAxis.l2p(lcenter - wdPos) + bPosPxOffset; + var posw1 = posHasRangeBreaks ? pos1 * r + (1 - r) * posc : posAxis.l2p(lcenter + wdPos) + bPosPxOffset; + var posm0 = posAxis.l2p(lcenter - bdPos0 * nw) + bPosPxOffset; var posm1 = posAxis.l2p(lcenter + bdPos1 * nw) + bPosPxOffset; var q1 = valAxis.c2p(d.q1, true); @@ -115,30 +122,45 @@ function plotBoxAndWhiskers(sel, axes, trace, t) { var ln = valAxis.c2p(d.ln, true); var un = valAxis.c2p(d.un, true); - if(trace.orientation === 'h') { + if(isHorizontal) { d3.select(this).attr('d', 'M' + m + ',' + posm0 + 'V' + posm1 + // median line 'M' + q1 + ',' + pos0 + 'V' + pos1 + // left edge - (notched ? 'H' + ln + 'L' + m + ',' + posm1 + 'L' + un + ',' + pos1 : '') + // top notched edge + (notched ? + 'H' + ln + 'L' + m + ',' + posm1 + 'L' + un + ',' + pos1 : + '' + ) + // top notched edge 'H' + q3 + // end of the top edge 'V' + pos0 + // right edge (notched ? 'H' + un + 'L' + m + ',' + posm0 + 'L' + ln + ',' + pos0 : '') + // bottom notched edge 'Z' + // end of the box 'M' + q1 + ',' + posc + 'H' + lf + 'M' + q3 + ',' + posc + 'H' + uf + // whiskers - ((whiskerWidth === 0) ? '' : // whisker caps - 'M' + lf + ',' + posw0 + 'V' + posw1 + 'M' + uf + ',' + posw0 + 'V' + posw1)); + (whiskerWidth === 0 ? + '' : // whisker caps + 'M' + lf + ',' + posw0 + 'V' + posw1 + 'M' + uf + ',' + posw0 + 'V' + posw1 + ) + ); } else { d3.select(this).attr('d', 'M' + posm0 + ',' + m + 'H' + posm1 + // median line 'M' + pos0 + ',' + q1 + 'H' + pos1 + // top of the box - (notched ? 'V' + ln + 'L' + posm1 + ',' + m + 'L' + pos1 + ',' + un : '') + // notched right edge + (notched ? + 'V' + ln + 'L' + posm1 + ',' + m + 'L' + pos1 + ',' + un : + '' + ) + // notched right edge 'V' + q3 + // end of the right edge 'H' + pos0 + // bottom of the box - (notched ? 'V' + un + 'L' + posm0 + ',' + m + 'L' + pos0 + ',' + ln : '') + // notched left edge + (notched ? + 'V' + un + 'L' + posm0 + ',' + m + 'L' + pos0 + ',' + ln : + '' + ) + // notched left edge 'Z' + // end of the box 'M' + posc + ',' + q1 + 'V' + lf + 'M' + posc + ',' + q3 + 'V' + uf + // whiskers - ((whiskerWidth === 0) ? '' : // whisker caps - 'M' + posw0 + ',' + lf + 'H' + posw1 + 'M' + posw0 + ',' + uf + 'H' + posw1)); + (whiskerWidth === 0 ? + '' : // whisker caps + 'M' + posw0 + ',' + lf + 'H' + posw1 + 'M' + posw0 + ',' + uf + 'H' + posw1 + ) + ); } }); } @@ -254,8 +276,10 @@ function plotPoints(sel, axes, trace, t) { } function plotBoxMean(sel, axes, trace, t) { - var posAxis = axes.pos; var valAxis = axes.val; + var posAxis = axes.pos; + var posHasRangeBreaks = !!posAxis.rangebreaks; + var bPos = t.bPos; var bPosPxOffset = t.bPosPxOffset || 0; @@ -289,9 +313,11 @@ function plotBoxMean(sel, axes, trace, t) { paths.each(function(d) { var lcenter = posAxis.c2l(d.pos + bPos, true); - var posc = posAxis.l2p(lcenter) + bPosPxOffset; + var pos0 = posAxis.l2p(lcenter - bdPos0) + bPosPxOffset; var pos1 = posAxis.l2p(lcenter + bdPos1) + bPosPxOffset; + var posc = posHasRangeBreaks ? (pos0 + pos1) / 2 : posAxis.l2p(lcenter) + bPosPxOffset; + var m = valAxis.c2p(d.mean, true); var sl = valAxis.c2p(d.mean - d.sd, true); var sh = valAxis.c2p(d.mean + d.sd, true); diff --git a/src/traces/ohlc/plot.js b/src/traces/ohlc/plot.js index 2de3869f1ab..afff8010825 100644 --- a/src/traces/ohlc/plot.js +++ b/src/traces/ohlc/plot.js @@ -13,8 +13,9 @@ var d3 = require('d3'); var Lib = require('../../lib'); module.exports = function plot(gd, plotinfo, cdOHLC, ohlcLayer) { - var xa = plotinfo.xaxis; var ya = plotinfo.yaxis; + var xa = plotinfo.xaxis; + var posHasRangeBreaks = !!xa.rangebreaks; Lib.makeTraceGroups(ohlcLayer, cdOHLC, 'trace ohlc').each(function(cd) { var plotGroup = d3.select(this); @@ -38,9 +39,9 @@ module.exports = function plot(gd, plotinfo, cdOHLC, ohlcLayer) { paths.attr('d', function(d) { if(d.empty) return 'M0,0Z'; - var x = xa.c2p(d.pos, true); var xo = xa.c2p(d.pos - tickLen, true); var xc = xa.c2p(d.pos + tickLen, true); + var x = posHasRangeBreaks ? (xo + xc) / 2 : xa.c2p(d.pos, true); var yo = ya.c2p(d.o, true); var yh = ya.c2p(d.h, true); diff --git a/test/image/baselines/axes_breaks-ohlc_candlestick_box.png b/test/image/baselines/axes_breaks-ohlc_candlestick_box.png new file mode 100644 index 00000000000..7d52eb94b60 Binary files /dev/null and b/test/image/baselines/axes_breaks-ohlc_candlestick_box.png differ diff --git a/test/image/baselines/axes_breaks-rangeslider.png b/test/image/baselines/axes_breaks-rangeslider.png index 4b4565951be..10c47a94354 100644 Binary files a/test/image/baselines/axes_breaks-rangeslider.png and b/test/image/baselines/axes_breaks-rangeslider.png differ diff --git a/test/image/mocks/axes_breaks-ohlc_candlestick_box.json b/test/image/mocks/axes_breaks-ohlc_candlestick_box.json new file mode 100644 index 00000000000..633434a9c73 --- /dev/null +++ b/test/image/mocks/axes_breaks-ohlc_candlestick_box.json @@ -0,0 +1,156 @@ +{ + "data": [ + { + "name": "candlestick", + "type": "candlestick", + "x": [ + "2017-01-09 00:00", + "2017-01-16 00:00" + ], + "high": [ + 3, + 4 + ], + "open": [ + 2, + 3 + ], + "close": [ + 1, + 2 + ], + "low": [ + 0, + 1 + ] + }, + { + "name": "box", + "type": "box", + "yaxis": "y2", + "x": [ + "2017-01-09 00:00", + "2017-01-16 00:00" + ], + "upperfence": [ + 4, + 5 + ], + "q3": [ + 3, + 4 + ], + "median": [ + 2, + 3 + ], + "q1": [ + 1, + 2 + ], + "lowerfence": [ + 0, + 1 + ] + }, + { + "name": "box", + "type": "box", + "yaxis": "y3", + "whiskerwidth": 1, + "x": [ + "2017-01-09 00:00", + "2017-01-16 00:00" + ], + "upperfence": [ + 4, + 5 + ], + "q3": [ + 3, + 4 + ], + "median": [ + 2, + 3 + ], + "q1": [ + 1, + 2 + ], + "lowerfence": [ + 0, + 1 + ] + }, + { + "name": "ohlc", + "type": "ohlc", + "yaxis": "y4", + "x": [ + "2017-01-09 00:00", + "2017-01-16 00:00" + ], + "high": [ + 3, + 4 + ], + "open": [ + 2, + 3 + ], + "close": [ + 1, + 2 + ], + "low": [ + 0, + 1 + ] + } + ], + "layout": { + "width": 600, + "height": 800, + "showlegend": false, + "xaxis": { + "rangebreaks": [ + { + "pattern": "day of week", + "bounds": [ + 6, + 1 + ] + } + ] + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0.225, + 0.375 + ] + }, + "yaxis2": { + "anchor": "x", + "domain": [ + 0.425, + 0.575 + ] + }, + "yaxis3": { + "anchor": "x", + "domain": [ + 0.625, + 0.775 + ] + }, + "yaxis4": { + "anchor": "x", + "domain": [ + 0.825, + 1 + ] + } + } +} diff --git a/test/jasmine/tests/mock_test.js b/test/jasmine/tests/mock_test.js index 9b4280441dc..1ab0744b095 100644 --- a/test/jasmine/tests/mock_test.js +++ b/test/jasmine/tests/mock_test.js @@ -62,6 +62,7 @@ var list = [ 'axes_breaks-heatmap2d', 'axes_breaks-histogram2d', 'axes_breaks-night_autorange-reversed', + 'axes_breaks-ohlc_candlestick_box', 'axes_breaks-overlap', 'axes_breaks-rangeslider', 'axes_breaks-reversed-without-pattern', @@ -1103,6 +1104,7 @@ figs['axes_breaks-heatmap1d'] = require('@mocks/axes_breaks-heatmap1d'); figs['axes_breaks-heatmap2d'] = require('@mocks/axes_breaks-heatmap2d'); figs['axes_breaks-histogram2d'] = require('@mocks/axes_breaks-histogram2d'); figs['axes_breaks-night_autorange-reversed'] = require('@mocks/axes_breaks-night_autorange-reversed'); +figs['axes_breaks-ohlc_candlestick_box'] = require('@mocks/axes_breaks-ohlc_candlestick_box'); figs['axes_breaks-overlap'] = require('@mocks/axes_breaks-overlap'); figs['axes_breaks-rangeslider'] = require('@mocks/axes_breaks-rangeslider'); figs['axes_breaks-reversed-without-pattern'] = require('@mocks/axes_breaks-reversed-without-pattern');