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 1dbaea5

Browse filesBrowse files
authored
Merge pull request plotly#1861 from plotly/cliponaxis-false-take2
[take 2] Implement scatter `cliponaxis: false`
2 parents edf52ca + bbb31f4 commit 1dbaea5
Copy full SHA for 1dbaea5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner
Expand file treeCollapse file tree

42 files changed

+960
-212
lines changed

‎devtools/test_dashboard/index.html

Copy file name to clipboardExpand all lines: devtools/test_dashboard/index.html
-1Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
<script type="text/javascript" src="../../dist/extras/mathjax/MathJax.js?config=TeX-AMS-MML_SVG"></script>
2323
<script id="source" type="text/javascript" src="../../build/plotly.js"></script>
24-
<script type="text/javascript" src="../../test/image/strict-d3.js" charset="utf-8"></script>
2524
<script type="text/javascript" src="../../build/test_dashboard-bundle.js"></script>
2625
</body>
2726
</html>

‎src/components/drawing/index.js

Copy file name to clipboardExpand all lines: src/components/drawing/index.js
+22-5Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,8 @@ drawing.setRect = function(s, x, y, w, h) {
6868
* false if selection could not get translated
6969
*/
7070
drawing.translatePoint = function(d, sel, xa, ya) {
71-
// put xp and yp into d if pixel scaling is already done
72-
var x = d.xp || xa.c2p(d.x),
73-
y = d.yp || ya.c2p(d.y);
71+
var x = xa.c2p(d.x);
72+
var y = ya.c2p(d.y);
7473

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

89-
drawing.translatePoints = function(s, xa, ya, trace) {
88+
drawing.translatePoints = function(s, xa, ya) {
9089
s.each(function(d) {
9190
var sel = d3.select(this);
92-
drawing.translatePoint(d, sel, xa, ya, trace);
91+
drawing.translatePoint(d, sel, xa, ya);
92+
});
93+
};
94+
95+
drawing.hideOutsideRangePoint = function(d, sel, xa, ya) {
96+
sel.attr(
97+
'display',
98+
xa.isPtWithinRange(d) && ya.isPtWithinRange(d) ? null : 'none'
99+
);
100+
};
101+
102+
drawing.hideOutsideRangePoints = function(points, subplot) {
103+
if(!subplot._hasClipOnAxisFalse) return;
104+
105+
var xa = subplot.xaxis;
106+
var ya = subplot.yaxis;
107+
108+
points.each(function(d) {
109+
drawing.hideOutsideRangePoint(d, d3.select(this), xa, ya);
93110
});
94111
};
95112

‎src/components/errorbars/plot.js

Copy file name to clipboardExpand all lines: src/components/errorbars/plot.js
+5-2Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212
var d3 = require('d3');
1313
var isNumeric = require('fast-isnumeric');
1414

15+
var Drawing = require('../drawing');
1516
var subTypes = require('../../traces/scatter/subtypes');
1617

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

20-
var xa = plotinfo.xaxis,
21-
ya = plotinfo.yaxis;
21+
var xa = plotinfo.xaxis;
22+
var ya = plotinfo.yaxis;
2223

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

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

64+
Drawing.setClipUrl(errorbars, plotinfo.layerClipId);
65+
6366
errorbars.each(function(d) {
6467
var errorbar = d3.select(this);
6568
var coords = errorCoords(d, xa, ya);

‎src/plot_api/subroutines.js

Copy file name to clipboardExpand all lines: src/plot_api/subroutines.js
+25-2Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ var Drawing = require('../components/drawing');
2020
var Titles = require('../components/titles');
2121
var ModeBar = require('../components/modebar');
2222
var initInteractions = require('../plots/cartesian/graph_interact');
23+
var cartesianConstants = require('../plots/cartesian/constants');
2324

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

167-
168168
plotinfo.plot.call(Drawing.setTranslate, xa._offset, ya._offset);
169-
plotinfo.plot.call(Drawing.setClipUrl, plotinfo.clipId);
169+
170+
var plotClipId;
171+
var layerClipId;
172+
173+
if(plotinfo._hasClipOnAxisFalse) {
174+
plotClipId = null;
175+
layerClipId = plotinfo.clipId;
176+
} else {
177+
plotClipId = plotinfo.clipId;
178+
layerClipId = null;
179+
}
180+
181+
Drawing.setClipUrl(plotinfo.plot, plotClipId);
182+
183+
for(i = 0; i < cartesianConstants.traceLayerClasses.length; i++) {
184+
var layer = cartesianConstants.traceLayerClasses[i];
185+
if(layer !== 'scatterlayer') {
186+
plotinfo.plot.selectAll('g.' + layer).call(Drawing.setClipUrl, layerClipId);
187+
}
188+
}
189+
190+
// stash layer clipId value (null or same as clipId)
191+
// to DRY up Drawing.setClipUrl calls downstream
192+
plotinfo.layerClipId = layerClipId;
170193

171194
var xlw = Drawing.crispRound(gd, xa.linewidth, 1),
172195
ylw = Drawing.crispRound(gd, ya.linewidth, 1),

‎src/plots/cartesian/constants.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/constants.js
+22-1Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,26 @@ module.exports = {
4949

5050
// last resort axis ranges for x and y axes if we have no data
5151
DFLTRANGEX: [-1, 6],
52-
DFLTRANGEY: [-1, 4]
52+
DFLTRANGEY: [-1, 4],
53+
54+
// Layers to keep trace types in the right order.
55+
// from back to front:
56+
// 1. heatmaps, 2D histos and contour maps
57+
// 2. bars / 1D histos
58+
// 3. errorbars for bars and scatter
59+
// 4. scatter
60+
// 5. box plots
61+
traceLayerClasses: [
62+
'imagelayer',
63+
'maplayer',
64+
'barlayer',
65+
'carpetlayer',
66+
'boxlayer',
67+
'scatterlayer'
68+
],
69+
70+
layerValue2layerClass: {
71+
'above traces': 'above',
72+
'below traces': 'below'
73+
}
5374
};

‎src/plots/cartesian/dragbox.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/dragbox.js
+15-12Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -743,23 +743,26 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
743743
var plotDx = xa2._offset - clipDx / xScaleFactor2,
744744
plotDy = ya2._offset - clipDy / yScaleFactor2;
745745

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

750+
var scatterPoints = subplot.plot.select('.scatterlayer').selectAll('.points');
751+
750752
subplot.plot
751753
.call(Drawing.setTranslate, plotDx, plotDy)
752-
.call(Drawing.setScale, 1 / xScaleFactor2, 1 / yScaleFactor2)
753-
754-
// This is specifically directed at scatter traces, applying an inverse
755-
// scale to individual points to counteract the scale of the trace
756-
// as a whole:
757-
.select('.scatterlayer').selectAll('.points').selectAll('.point')
758-
.call(Drawing.setPointGroupScale, xScaleFactor2, yScaleFactor2);
759-
760-
subplot.plot.select('.scatterlayer')
761-
.selectAll('.points').selectAll('.textpoint')
762-
.call(Drawing.setTextPointsScale, xScaleFactor2, yScaleFactor2);
754+
.call(Drawing.setScale, 1 / xScaleFactor2, 1 / yScaleFactor2);
755+
756+
// This is specifically directed at scatter traces, applying an inverse
757+
// scale to individual points to counteract the scale of the trace
758+
// as a whole:
759+
scatterPoints.selectAll('.point')
760+
.call(Drawing.setPointGroupScale, xScaleFactor2, yScaleFactor2)
761+
.call(Drawing.hideOutsideRangePoints, subplot);
762+
763+
scatterPoints.selectAll('.textpoint')
764+
.call(Drawing.setTextPointsScale, xScaleFactor2, yScaleFactor2)
765+
.call(Drawing.hideOutsideRangePoints, subplot);
763766
}
764767
}
765768

‎src/plots/cartesian/index.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/index.js
+47-29Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -289,24 +289,10 @@ function makeSubplotData(gd) {
289289
}
290290

291291
function makeSubplotLayer(plotinfo) {
292-
var plotgroup = plotinfo.plotgroup,
293-
id = plotinfo.id;
294-
295-
// Layers to keep plot types in the right order.
296-
// from back to front:
297-
// 1. heatmaps, 2D histos and contour maps
298-
// 2. bars / 1D histos
299-
// 3. errorbars for bars and scatter
300-
// 4. scatter
301-
// 5. box plots
302-
function joinPlotLayers(parent) {
303-
joinLayer(parent, 'g', 'imagelayer');
304-
joinLayer(parent, 'g', 'maplayer');
305-
joinLayer(parent, 'g', 'barlayer');
306-
joinLayer(parent, 'g', 'carpetlayer');
307-
joinLayer(parent, 'g', 'boxlayer');
308-
joinLayer(parent, 'g', 'scatterlayer');
309-
}
292+
var plotgroup = plotinfo.plotgroup;
293+
var id = plotinfo.id;
294+
var xLayer = constants.layerValue2layerClass[plotinfo.xaxis.layer];
295+
var yLayer = constants.layerValue2layerClass[plotinfo.yaxis.layer];
310296

311297
if(!plotinfo.mainplot) {
312298
var backLayer = joinLayer(plotgroup, 'g', 'layer-subplot');
@@ -319,19 +305,36 @@ function makeSubplotLayer(plotinfo) {
319305
plotinfo.zerolinelayer = joinLayer(plotgroup, 'g', 'zerolinelayer');
320306
plotinfo.overzero = joinLayer(plotgroup, 'g', 'overzero');
321307

308+
joinLayer(plotgroup, 'path', 'xlines-below');
309+
joinLayer(plotgroup, 'path', 'ylines-below');
310+
plotinfo.overlinesBelow = joinLayer(plotgroup, 'g', 'overlines-below');
311+
312+
joinLayer(plotgroup, 'g', 'xaxislayer-below');
313+
joinLayer(plotgroup, 'g', 'yaxislayer-below');
314+
plotinfo.overaxesBelow = joinLayer(plotgroup, 'g', 'overaxes-below');
315+
322316
plotinfo.plot = joinLayer(plotgroup, 'g', 'plot');
323317
plotinfo.overplot = joinLayer(plotgroup, 'g', 'overplot');
324318

325-
plotinfo.xlines = joinLayer(plotgroup, 'path', 'xlines');
326-
plotinfo.ylines = joinLayer(plotgroup, 'path', 'ylines');
327-
plotinfo.overlines = joinLayer(plotgroup, 'g', 'overlines');
319+
joinLayer(plotgroup, 'path', 'xlines-above');
320+
joinLayer(plotgroup, 'path', 'ylines-above');
321+
plotinfo.overlinesAbove = joinLayer(plotgroup, 'g', 'overlines-above');
328322

329-
plotinfo.xaxislayer = joinLayer(plotgroup, 'g', 'xaxislayer');
330-
plotinfo.yaxislayer = joinLayer(plotgroup, 'g', 'yaxislayer');
331-
plotinfo.overaxes = joinLayer(plotgroup, 'g', 'overaxes');
323+
joinLayer(plotgroup, 'g', 'xaxislayer-above');
324+
joinLayer(plotgroup, 'g', 'yaxislayer-above');
325+
plotinfo.overaxesAbove = joinLayer(plotgroup, 'g', 'overaxes-above');
326+
327+
// set refs to correct layers as determined by 'axis.layer'
328+
plotinfo.xlines = plotgroup.select('.xlines-' + xLayer);
329+
plotinfo.ylines = plotgroup.select('.ylines-' + yLayer);
330+
plotinfo.xaxislayer = plotgroup.select('.xaxislayer-' + xLayer);
331+
plotinfo.yaxislayer = plotgroup.select('.yaxislayer-' + yLayer);
332332
}
333333
else {
334334
var mainplotinfo = plotinfo.mainplotinfo;
335+
var mainplotgroup = mainplotinfo.plotgroup;
336+
var xId = id + '-x';
337+
var yId = id + '-y';
335338

336339
// now make the components of overlaid subplots
337340
// overlays don't have backgrounds, and append all
@@ -341,15 +344,30 @@ function makeSubplotLayer(plotinfo) {
341344
plotinfo.gridlayer = joinLayer(mainplotinfo.overgrid, 'g', id);
342345
plotinfo.zerolinelayer = joinLayer(mainplotinfo.overzero, 'g', id);
343346

347+
joinLayer(mainplotinfo.overlinesBelow, 'path', xId);
348+
joinLayer(mainplotinfo.overlinesBelow, 'path', yId);
349+
joinLayer(mainplotinfo.overaxesBelow, 'g', xId);
350+
joinLayer(mainplotinfo.overaxesBelow, 'g', yId);
351+
344352
plotinfo.plot = joinLayer(mainplotinfo.overplot, 'g', id);
345-
plotinfo.xlines = joinLayer(mainplotinfo.overlines, 'path', id + '-x');
346-
plotinfo.ylines = joinLayer(mainplotinfo.overlines, 'path', id + '-y');
347-
plotinfo.xaxislayer = joinLayer(mainplotinfo.overaxes, 'g', id + '-x');
348-
plotinfo.yaxislayer = joinLayer(mainplotinfo.overaxes, 'g', id + '-y');
353+
354+
joinLayer(mainplotinfo.overlinesAbove, 'path', xId);
355+
joinLayer(mainplotinfo.overlinesAbove, 'path', yId);
356+
joinLayer(mainplotinfo.overaxesAbove, 'g', xId);
357+
joinLayer(mainplotinfo.overaxesAbove, 'g', yId);
358+
359+
// set refs to correct layers as determined by 'abovetraces'
360+
plotinfo.xlines = mainplotgroup.select('.overlines-' + xLayer).select('.' + xId);
361+
plotinfo.ylines = mainplotgroup.select('.overlines-' + yLayer).select('.' + yId);
362+
plotinfo.xaxislayer = mainplotgroup.select('.overaxes-' + xLayer).select('.' + xId);
363+
plotinfo.yaxislayer = mainplotgroup.select('.overaxes-' + yLayer).select('.' + yId);
349364
}
350365

351366
// common attributes for all subplots, overlays or not
352-
plotinfo.plot.call(joinPlotLayers);
367+
368+
for(var i = 0; i < constants.traceLayerClasses.length; i++) {
369+
joinLayer(plotinfo.plot, 'g', constants.traceLayerClasses[i]);
370+
}
353371

354372
plotinfo.xlines
355373
.style('fill', 'none')

‎src/plots/cartesian/layout_attributes.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/layout_attributes.js
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,20 @@ module.exports = {
573573
'If *false*, this axis does not overlay any same-letter axes.'
574574
].join(' ')
575575
},
576+
layer: {
577+
valType: 'enumerated',
578+
values: ['above traces', 'below traces'],
579+
dflt: 'above traces',
580+
role: 'info',
581+
description: [
582+
'Sets the layer on which this axis is displayed.',
583+
'If *above traces*, this axis is displayed above all the subplot\'s traces',
584+
'If *below traces*, this axis is displayed below all the subplot\'s traces,',
585+
'but above the grid lines.',
586+
'Useful when used together with scatter-like traces with `cliponaxis`',
587+
'set to *false* to show markers and/or text nodes above this axis.'
588+
].join(' ')
589+
},
576590
domain: {
577591
valType: 'info_array',
578592
role: 'info',

‎src/plots/cartesian/position_defaults.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/position_defaults.js
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,7 @@ module.exports = function handlePositionDefaults(containerIn, containerOut, coer
5959
Lib.noneOrAll(containerIn.domain, containerOut.domain, [0, 1]);
6060
}
6161

62+
coerce('layer');
63+
6264
return containerOut;
6365
};

‎src/plots/cartesian/set_convert.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/set_convert.js
+15-3Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ function fromLog(v) {
5858
module.exports = function setConvert(ax, fullLayout) {
5959
fullLayout = fullLayout || {};
6060

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

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

342343
// set scaling to pixels
343344
ax.setScale = function(usePrivateRange) {
344-
var gs = fullLayout._size,
345-
axLetter = ax._id.charAt(0);
345+
var gs = fullLayout._size;
346346

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

438+
if(axLetter === 'x') {
439+
ax.isPtWithinRange = function(d) {
440+
var x = d.x;
441+
return x >= ax.range[0] && x <= ax.range[1];
442+
};
443+
} else {
444+
ax.isPtWithinRange = function(d) {
445+
var y = d.y;
446+
return y >= ax.range[0] && y <= ax.range[1];
447+
};
448+
}
449+
438450
// for autoranging: arrays of objects:
439451
// {val: axis value, pad: pixel padding}
440452
// on the low and high sides

0 commit comments

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