Skip to content

[take 2] Implement scatter cliponaxis: false #1861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Jul 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c28b26a
lint in drawing/
etpinard Jul 7, 2017
8f72d1f
implement cliponaxis
etpinard Jul 7, 2017
1909034
hide outside range points on drag and transitions
etpinard Jul 7, 2017
4b5c4bb
implement `cliponaxis: false` for scatterternary
etpinard Jul 7, 2017
d46cae7
merge custom shared assertions into single assets/ module
etpinard Jul 7, 2017
7228d5d
add cliponaxis false mock
etpinard Jul 7, 2017
fe1f889
showcase new cliponaxis logic in ternary_markers
etpinard Jul 7, 2017
89ee533
add scatter and scatterternary cliponaxis tests
etpinard Jul 7, 2017
e4d7889
fix panning for ternary lines under `cliponaxis: false`
etpinard Jul 7, 2017
091473b
fix lint
etpinard Jul 10, 2017
9becb7c
hide points using `display: 'none'` instead of visibility attr
etpinard Jul 11, 2017
62ab845
remove obsolete code from old d.{xp|yp} optimization attempt
etpinard Jul 11, 2017
0496144
stash layer clipId value (null or same as clipId)
etpinard Jul 11, 2017
7b62b73
handle cliponaxis false/true traces coexisting on same subplot
etpinard Jul 11, 2017
02b9fbc
rename 'layers' cartesian constant -> 'traceLayerClasses'
etpinard Jul 11, 2017
1c85de6
add 'layer' axis attribute
etpinard Jul 11, 2017
e44aa4d
improve cliponaxis_false mock
etpinard Jul 11, 2017
e84701f
replace strict-d3 IIFE with browserify 'require' transform
etpinard Jul 12, 2017
e234827
fixes #1873 - apply transform on <clipPath> child
etpinard Jul 12, 2017
6f494c6
talk about 'cliponaxis' in axis 'layer' description and vice-versa
etpinard Jul 12, 2017
bbb31f4
Merge pull request #1871 from plotly/axis-layer-above-below
etpinard Jul 13, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion devtools/test_dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

<script type="text/javascript" src="../../dist/extras/mathjax/MathJax.js?config=TeX-AMS-MML_SVG"></script>
<script id="source" type="text/javascript" src="../../build/plotly.js"></script>
<script type="text/javascript" src="../../test/image/strict-d3.js" charset="utf-8"></script>
<script type="text/javascript" src="../../build/test_dashboard-bundle.js"></script>
</body>
</html>
27 changes: 22 additions & 5 deletions src/components/drawing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,8 @@ drawing.setRect = function(s, x, y, w, h) {
* false if selection could not get translated
*/
drawing.translatePoint = function(d, sel, xa, ya) {
// put xp and yp into d if pixel scaling is already done
var x = d.xp || xa.c2p(d.x),
y = d.yp || ya.c2p(d.y);
var x = xa.c2p(d.x);
var y = ya.c2p(d.y);

if(isNumeric(x) && isNumeric(y) && sel.node()) {
// for multiline text this works better
Expand All @@ -86,10 +85,28 @@ drawing.translatePoint = function(d, sel, xa, ya) {
return true;
};

drawing.translatePoints = function(s, xa, ya, trace) {
drawing.translatePoints = function(s, xa, ya) {
s.each(function(d) {
var sel = d3.select(this);
drawing.translatePoint(d, sel, xa, ya, trace);
drawing.translatePoint(d, sel, xa, ya);
});
};

drawing.hideOutsideRangePoint = function(d, sel, xa, ya) {
sel.attr(
'display',
xa.isPtWithinRange(d) && ya.isPtWithinRange(d) ? null : 'none'
);
};

drawing.hideOutsideRangePoints = function(points, subplot) {
if(!subplot._hasClipOnAxisFalse) return;

var xa = subplot.xaxis;
var ya = subplot.yaxis;

points.each(function(d) {
drawing.hideOutsideRangePoint(d, d3.select(this), xa, ya);
});
};

Expand Down
7 changes: 5 additions & 2 deletions src/components/errorbars/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
var d3 = require('d3');
var isNumeric = require('fast-isnumeric');

var Drawing = require('../drawing');
var subTypes = require('../../traces/scatter/subtypes');

module.exports = function plot(traces, plotinfo, transitionOpts) {
var isNew;

var xa = plotinfo.xaxis,
ya = plotinfo.yaxis;
var xa = plotinfo.xaxis;
var ya = plotinfo.yaxis;

var hasAnimation = transitionOpts && transitionOpts.duration > 0;

Expand Down Expand Up @@ -60,6 +61,8 @@ module.exports = function plot(traces, plotinfo, transitionOpts) {
.style('opacity', 1);
}

Drawing.setClipUrl(errorbars, plotinfo.layerClipId);

errorbars.each(function(d) {
var errorbar = d3.select(this);
var coords = errorCoords(d, xa, ya);
Expand Down
27 changes: 25 additions & 2 deletions src/plot_api/subroutines.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var Drawing = require('../components/drawing');
var Titles = require('../components/titles');
var ModeBar = require('../components/modebar');
var initInteractions = require('../plots/cartesian/graph_interact');
var cartesianConstants = require('../plots/cartesian/constants');

exports.layoutStyles = function(gd) {
return Lib.syncOrAsync([Plots.doAutoMargin, exports.lsInner], gd);
Expand Down Expand Up @@ -164,9 +165,31 @@ exports.lsInner = function(gd) {
'height': ya._length
});


plotinfo.plot.call(Drawing.setTranslate, xa._offset, ya._offset);
plotinfo.plot.call(Drawing.setClipUrl, plotinfo.clipId);

var plotClipId;
var layerClipId;

if(plotinfo._hasClipOnAxisFalse) {
plotClipId = null;
layerClipId = plotinfo.clipId;
} else {
plotClipId = plotinfo.clipId;
layerClipId = null;
}

Drawing.setClipUrl(plotinfo.plot, plotClipId);

for(i = 0; i < cartesianConstants.traceLayerClasses.length; i++) {
var layer = cartesianConstants.traceLayerClasses[i];
if(layer !== 'scatterlayer') {
plotinfo.plot.selectAll('g.' + layer).call(Drawing.setClipUrl, layerClipId);
}
}

// stash layer clipId value (null or same as clipId)
// to DRY up Drawing.setClipUrl calls downstream
plotinfo.layerClipId = layerClipId;

var xlw = Drawing.crispRound(gd, xa.linewidth, 1),
ylw = Drawing.crispRound(gd, ya.linewidth, 1),
Expand Down
23 changes: 22 additions & 1 deletion src/plots/cartesian/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,26 @@ module.exports = {

// last resort axis ranges for x and y axes if we have no data
DFLTRANGEX: [-1, 6],
DFLTRANGEY: [-1, 4]
DFLTRANGEY: [-1, 4],

// Layers to keep trace types in the right order.
// from back to front:
// 1. heatmaps, 2D histos and contour maps
// 2. bars / 1D histos
// 3. errorbars for bars and scatter
// 4. scatter
// 5. box plots
traceLayerClasses: [
'imagelayer',
'maplayer',
'barlayer',
'carpetlayer',
'boxlayer',
'scatterlayer'
],

layerValue2layerClass: {
'above traces': 'above',
'below traces': 'below'
}
};
27 changes: 15 additions & 12 deletions src/plots/cartesian/dragbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -743,23 +743,26 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
var plotDx = xa2._offset - clipDx / xScaleFactor2,
plotDy = ya2._offset - clipDy / yScaleFactor2;

fullLayout._defs.selectAll('#' + subplot.clipId)
fullLayout._defs.select('#' + subplot.clipId + '> rect')
.call(Drawing.setTranslate, clipDx, clipDy)
.call(Drawing.setScale, xScaleFactor2, yScaleFactor2);

var scatterPoints = subplot.plot.select('.scatterlayer').selectAll('.points');

subplot.plot
.call(Drawing.setTranslate, plotDx, plotDy)
.call(Drawing.setScale, 1 / xScaleFactor2, 1 / yScaleFactor2)

// This is specifically directed at scatter traces, applying an inverse
// scale to individual points to counteract the scale of the trace
// as a whole:
.select('.scatterlayer').selectAll('.points').selectAll('.point')
.call(Drawing.setPointGroupScale, xScaleFactor2, yScaleFactor2);

subplot.plot.select('.scatterlayer')
.selectAll('.points').selectAll('.textpoint')
.call(Drawing.setTextPointsScale, xScaleFactor2, yScaleFactor2);
.call(Drawing.setScale, 1 / xScaleFactor2, 1 / yScaleFactor2);

// This is specifically directed at scatter traces, applying an inverse
// scale to individual points to counteract the scale of the trace
// as a whole:
scatterPoints.selectAll('.point')
.call(Drawing.setPointGroupScale, xScaleFactor2, yScaleFactor2)
.call(Drawing.hideOutsideRangePoints, subplot);

scatterPoints.selectAll('.textpoint')
.call(Drawing.setTextPointsScale, xScaleFactor2, yScaleFactor2)
.call(Drawing.hideOutsideRangePoints, subplot);
}
}

Expand Down
76 changes: 47 additions & 29 deletions src/plots/cartesian/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,24 +294,10 @@ function makeSubplotData(gd) {
}

function makeSubplotLayer(plotinfo) {
var plotgroup = plotinfo.plotgroup,
id = plotinfo.id;

// Layers to keep plot types in the right order.
// from back to front:
// 1. heatmaps, 2D histos and contour maps
// 2. bars / 1D histos
// 3. errorbars for bars and scatter
// 4. scatter
// 5. box plots
function joinPlotLayers(parent) {
joinLayer(parent, 'g', 'imagelayer');
joinLayer(parent, 'g', 'maplayer');
joinLayer(parent, 'g', 'barlayer');
joinLayer(parent, 'g', 'carpetlayer');
joinLayer(parent, 'g', 'boxlayer');
joinLayer(parent, 'g', 'scatterlayer');
}
var plotgroup = plotinfo.plotgroup;
var id = plotinfo.id;
var xLayer = constants.layerValue2layerClass[plotinfo.xaxis.layer];
var yLayer = constants.layerValue2layerClass[plotinfo.yaxis.layer];

if(!plotinfo.mainplot) {
var backLayer = joinLayer(plotgroup, 'g', 'layer-subplot');
Expand All @@ -324,19 +310,36 @@ function makeSubplotLayer(plotinfo) {
plotinfo.zerolinelayer = joinLayer(plotgroup, 'g', 'zerolinelayer');
plotinfo.overzero = joinLayer(plotgroup, 'g', 'overzero');

joinLayer(plotgroup, 'path', 'xlines-below');
joinLayer(plotgroup, 'path', 'ylines-below');
plotinfo.overlinesBelow = joinLayer(plotgroup, 'g', 'overlines-below');

joinLayer(plotgroup, 'g', 'xaxislayer-below');
joinLayer(plotgroup, 'g', 'yaxislayer-below');
plotinfo.overaxesBelow = joinLayer(plotgroup, 'g', 'overaxes-below');

plotinfo.plot = joinLayer(plotgroup, 'g', 'plot');
plotinfo.overplot = joinLayer(plotgroup, 'g', 'overplot');

plotinfo.xlines = joinLayer(plotgroup, 'path', 'xlines');
plotinfo.ylines = joinLayer(plotgroup, 'path', 'ylines');
plotinfo.overlines = joinLayer(plotgroup, 'g', 'overlines');
joinLayer(plotgroup, 'path', 'xlines-above');
joinLayer(plotgroup, 'path', 'ylines-above');
plotinfo.overlinesAbove = joinLayer(plotgroup, 'g', 'overlines-above');

plotinfo.xaxislayer = joinLayer(plotgroup, 'g', 'xaxislayer');
plotinfo.yaxislayer = joinLayer(plotgroup, 'g', 'yaxislayer');
plotinfo.overaxes = joinLayer(plotgroup, 'g', 'overaxes');
joinLayer(plotgroup, 'g', 'xaxislayer-above');
joinLayer(plotgroup, 'g', 'yaxislayer-above');
plotinfo.overaxesAbove = joinLayer(plotgroup, 'g', 'overaxes-above');

// set refs to correct layers as determined by 'axis.layer'
plotinfo.xlines = plotgroup.select('.xlines-' + xLayer);
plotinfo.ylines = plotgroup.select('.ylines-' + yLayer);
plotinfo.xaxislayer = plotgroup.select('.xaxislayer-' + xLayer);
plotinfo.yaxislayer = plotgroup.select('.yaxislayer-' + yLayer);
}
else {
var mainplotinfo = plotinfo.mainplotinfo;
var mainplotgroup = mainplotinfo.plotgroup;
var xId = id + '-x';
var yId = id + '-y';

// now make the components of overlaid subplots
// overlays don't have backgrounds, and append all
Expand All @@ -346,15 +349,30 @@ function makeSubplotLayer(plotinfo) {
plotinfo.gridlayer = joinLayer(mainplotinfo.overgrid, 'g', id);
plotinfo.zerolinelayer = joinLayer(mainplotinfo.overzero, 'g', id);

joinLayer(mainplotinfo.overlinesBelow, 'path', xId);
joinLayer(mainplotinfo.overlinesBelow, 'path', yId);
joinLayer(mainplotinfo.overaxesBelow, 'g', xId);
joinLayer(mainplotinfo.overaxesBelow, 'g', yId);

plotinfo.plot = joinLayer(mainplotinfo.overplot, 'g', id);
plotinfo.xlines = joinLayer(mainplotinfo.overlines, 'path', id + '-x');
plotinfo.ylines = joinLayer(mainplotinfo.overlines, 'path', id + '-y');
plotinfo.xaxislayer = joinLayer(mainplotinfo.overaxes, 'g', id + '-x');
plotinfo.yaxislayer = joinLayer(mainplotinfo.overaxes, 'g', id + '-y');

joinLayer(mainplotinfo.overlinesAbove, 'path', xId);
joinLayer(mainplotinfo.overlinesAbove, 'path', yId);
joinLayer(mainplotinfo.overaxesAbove, 'g', xId);
joinLayer(mainplotinfo.overaxesAbove, 'g', yId);

// set refs to correct layers as determined by 'abovetraces'
plotinfo.xlines = mainplotgroup.select('.overlines-' + xLayer).select('.' + xId);
plotinfo.ylines = mainplotgroup.select('.overlines-' + yLayer).select('.' + yId);
plotinfo.xaxislayer = mainplotgroup.select('.overaxes-' + xLayer).select('.' + xId);
plotinfo.yaxislayer = mainplotgroup.select('.overaxes-' + yLayer).select('.' + yId);
}

// common attributes for all subplots, overlays or not
plotinfo.plot.call(joinPlotLayers);

for(var i = 0; i < constants.traceLayerClasses.length; i++) {
joinLayer(plotinfo.plot, 'g', constants.traceLayerClasses[i]);
}

plotinfo.xlines
.style('fill', 'none')
Expand Down
14 changes: 14 additions & 0 deletions src/plots/cartesian/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,20 @@ module.exports = {
'If *false*, this axis does not overlay any same-letter axes.'
].join(' ')
},
layer: {
valType: 'enumerated',
values: ['above traces', 'below traces'],
dflt: 'above traces',
role: 'info',
description: [
'Sets the layer on which this axis is displayed.',
'If *above traces*, this axis is displayed above all the subplot\'s traces',
'If *below traces*, this axis is displayed below all the subplot\'s traces,',
'but above the grid lines.',
'Useful when used together with scatter-like traces with `cliponaxis`',
'set to *false* to show markers and/or text nodes above this axis.'
].join(' ')
},
domain: {
valType: 'info_array',
role: 'info',
Expand Down
2 changes: 2 additions & 0 deletions src/plots/cartesian/position_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,7 @@ module.exports = function handlePositionDefaults(containerIn, containerOut, coer
Lib.noneOrAll(containerIn.domain, containerOut.domain, [0, 1]);
}

coerce('layer');

return containerOut;
};
18 changes: 15 additions & 3 deletions src/plots/cartesian/set_convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ function fromLog(v) {
module.exports = function setConvert(ax, fullLayout) {
fullLayout = fullLayout || {};

var axLetter = (ax._id || 'x').charAt(0);

// clipMult: how many axis lengths past the edge do we render?
// for panning, 1-2 would suffice, but for zooming more is nice.
// also, clipping can affect the direction of lines off the edge...
Expand Down Expand Up @@ -277,7 +279,6 @@ module.exports = function setConvert(ax, fullLayout) {
ax.cleanRange = function(rangeAttr) {
if(!rangeAttr) rangeAttr = 'range';
var range = Lib.nestedProperty(ax, rangeAttr).get(),
axLetter = (ax._id || 'x').charAt(0),
i, dflt;

if(ax.type === 'date') dflt = Lib.dfltRange(ax.calendar);
Expand Down Expand Up @@ -341,8 +342,7 @@ module.exports = function setConvert(ax, fullLayout) {

// set scaling to pixels
ax.setScale = function(usePrivateRange) {
var gs = fullLayout._size,
axLetter = ax._id.charAt(0);
var gs = fullLayout._size;

// TODO cleaner way to handle this case
if(!ax._categories) ax._categories = [];
Expand Down Expand Up @@ -435,6 +435,18 @@ module.exports = function setConvert(ax, fullLayout) {
);
};

if(axLetter === 'x') {
ax.isPtWithinRange = function(d) {
var x = d.x;
return x >= ax.range[0] && x <= ax.range[1];
};
} else {
ax.isPtWithinRange = function(d) {
var y = d.y;
return y >= ax.range[0] && y <= ax.range[1];
};
}

// for autoranging: arrays of objects:
// {val: axis value, pad: pixel padding}
// on the low and high sides
Expand Down
Loading