-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Image and shape clip paths #1453
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
Changes from all commits
06523d6
ae820ca
aeca8ae
2102b0f
10fe521
521015a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,16 +16,28 @@ var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); | |
module.exports = function draw(gd) { | ||
var fullLayout = gd._fullLayout, | ||
imageDataAbove = [], | ||
imageDataSubplot = [], | ||
imageDataBelow = []; | ||
imageDataSubplot = {}, | ||
imageDataBelow = [], | ||
subplot, | ||
i; | ||
|
||
// Sort into top, subplot, and bottom layers | ||
for(var i = 0; i < fullLayout.images.length; i++) { | ||
for(i = 0; i < fullLayout.images.length; i++) { | ||
var img = fullLayout.images[i]; | ||
|
||
if(img.visible) { | ||
if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') { | ||
imageDataSubplot.push(img); | ||
subplot = img.xref + img.yref; | ||
|
||
var plotinfo = fullLayout._plots[subplot]; | ||
if(plotinfo.mainplot) { | ||
subplot = plotinfo.mainplot.id; | ||
} | ||
|
||
if(!imageDataSubplot[subplot]) { | ||
imageDataSubplot[subplot] = []; | ||
} | ||
imageDataSubplot[subplot].push(img); | ||
} else if(img.layer === 'above') { | ||
imageDataAbove.push(img); | ||
} else { | ||
|
@@ -143,36 +155,52 @@ module.exports = function draw(gd) { | |
yId = ya ? ya._id : '', | ||
clipAxes = xId + yId; | ||
|
||
if(clipAxes) { | ||
thisImage.call(Drawing.setClipUrl, 'clip' + fullLayout._uid + clipAxes); | ||
} | ||
thisImage.call(Drawing.setClipUrl, clipAxes ? | ||
('clip' + fullLayout._uid + clipAxes) : | ||
null | ||
); | ||
} | ||
|
||
var imagesBelow = fullLayout._imageLowerLayer.selectAll('image') | ||
.data(imageDataBelow), | ||
imagesSubplot = fullLayout._imageSubplotLayer.selectAll('image') | ||
.data(imageDataSubplot), | ||
imagesAbove = fullLayout._imageUpperLayer.selectAll('image') | ||
.data(imageDataAbove); | ||
|
||
imagesBelow.enter().append('image'); | ||
imagesSubplot.enter().append('image'); | ||
imagesAbove.enter().append('image'); | ||
|
||
imagesBelow.exit().remove(); | ||
imagesSubplot.exit().remove(); | ||
imagesAbove.exit().remove(); | ||
|
||
imagesBelow.each(function(d) { | ||
setImage.bind(this)(d); | ||
applyAttributes.bind(this)(d); | ||
}); | ||
imagesSubplot.each(function(d) { | ||
setImage.bind(this)(d); | ||
applyAttributes.bind(this)(d); | ||
}); | ||
imagesAbove.each(function(d) { | ||
setImage.bind(this)(d); | ||
applyAttributes.bind(this)(d); | ||
}); | ||
|
||
var allSubplots = Object.keys(fullLayout._plots); | ||
for(i = 0; i < allSubplots.length; i++) { | ||
subplot = allSubplots[i]; | ||
var subplotObj = fullLayout._plots[subplot]; | ||
|
||
// filter out overlaid plots (which havd their images on the main plot) | ||
// and gl2d plots (which don't support below images, at least not yet) | ||
if(!subplotObj.imagelayer) continue; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks good 👍 |
||
|
||
var imagesOnSubplot = subplotObj.imagelayer.selectAll('image') | ||
// even if there are no images on this subplot, we need to run | ||
// enter and exit in case there were previously | ||
.data(imageDataSubplot[subplot] || []); | ||
|
||
imagesOnSubplot.enter().append('image'); | ||
imagesOnSubplot.exit().remove(); | ||
|
||
imagesOnSubplot.each(function(d) { | ||
setImage.bind(this)(d); | ||
applyAttributes.bind(this)(d); | ||
}); | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,7 +42,7 @@ function draw(gd) { | |
// Remove previous shapes before drawing new in shapes in fullLayout.shapes | ||
fullLayout._shapeUpperLayer.selectAll('path').remove(); | ||
fullLayout._shapeLowerLayer.selectAll('path').remove(); | ||
fullLayout._shapeSubplotLayer.selectAll('path').remove(); | ||
fullLayout._shapeSubplotLayers.selectAll('path').remove(); | ||
|
||
for(var i = 0; i < fullLayout.shapes.length; i++) { | ||
if(fullLayout.shapes[i].visible) { | ||
|
@@ -55,8 +55,6 @@ function draw(gd) { | |
} | ||
|
||
function drawOne(gd, index) { | ||
var i, n; | ||
|
||
// remove the existing shape if there is one. | ||
// because indices can change, we need to look in all shape layers | ||
gd._fullLayout._paper | ||
|
@@ -70,28 +68,17 @@ function drawOne(gd, index) { | |
// TODO: use d3 idioms instead of deleting and redrawing every time | ||
if(!optionsIn || options.visible === false) return; | ||
|
||
var clipAxes; | ||
if(options.layer !== 'below') { | ||
clipAxes = (options.xref + options.yref).replace(/paper/g, ''); | ||
drawShape(gd._fullLayout._shapeUpperLayer); | ||
} | ||
else if(options.xref === 'paper' && options.yref === 'paper') { | ||
clipAxes = ''; | ||
else if(options.xref === 'paper' || options.yref === 'paper') { | ||
drawShape(gd._fullLayout._shapeLowerLayer); | ||
} | ||
else { | ||
var plots = gd._fullLayout._plots || {}, | ||
subplots = Object.keys(plots), | ||
plotinfo; | ||
|
||
for(i = 0, n = subplots.length; i < n; i++) { | ||
plotinfo = plots[subplots[i]]; | ||
clipAxes = subplots[i]; | ||
var plotinfo = gd._fullLayout._plots[options.xref + options.yref], | ||
mainPlot = plotinfo.mainplot || plotinfo; | ||
|
||
if(isShapeInSubplot(gd, options, plotinfo)) { | ||
drawShape(plotinfo.shapelayer); | ||
} | ||
} | ||
drawShape(mainPlot.shapelayer); | ||
} | ||
|
||
function drawShape(shapeLayer) { | ||
|
@@ -110,10 +97,15 @@ function drawOne(gd, index) { | |
.call(Color.fill, options.fillcolor) | ||
.call(Drawing.dashLine, options.line.dash, options.line.width); | ||
|
||
if(clipAxes) { | ||
path.call(Drawing.setClipUrl, | ||
'clip' + gd._fullLayout._uid + clipAxes); | ||
} | ||
// note that for layer="below" the clipAxes can be different from the | ||
// subplot we're drawing this in. This could cause problems if the shape | ||
// spans two subplots. See https://github.com/plotly/plotly.js/issues/1452 | ||
var clipAxes = (options.xref + options.yref).replace(/paper/g, ''); | ||
|
||
path.call(Drawing.setClipUrl, clipAxes ? | ||
('clip' + gd._fullLayout._uid + clipAxes) : | ||
null | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice way to unset a clip path. 👍 |
||
); | ||
|
||
if(gd._context.editable) setupDragElement(gd, path, options, index); | ||
} | ||
|
@@ -271,15 +263,6 @@ function setupDragElement(gd, shapePath, shapeOptions, index) { | |
} | ||
} | ||
|
||
function isShapeInSubplot(gd, shape, plotinfo) { | ||
var xa = Axes.getFromId(gd, plotinfo.id, 'x')._id, | ||
ya = Axes.getFromId(gd, plotinfo.id, 'y')._id, | ||
isBelow = shape.layer === 'below', | ||
inSuplotAxis = (xa === shape.xref || ya === shape.yref), | ||
isNotAnOverlaidSubplot = !!plotinfo.shapelayer; | ||
return isBelow && inSuplotAxis && isNotAnOverlaidSubplot; | ||
} | ||
|
||
function getPathString(gd, options) { | ||
var type = options.type, | ||
xa = Axes.getFromId(gd, options.xref), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -308,8 +308,7 @@ Plotly.plot = function(gd, data, layout, config) { | |
|
||
// keep reference to shape layers in subplots | ||
var layerSubplot = fullLayout._paper.selectAll('.layer-subplot'); | ||
fullLayout._imageSubplotLayer = layerSubplot.selectAll('.imagelayer'); | ||
fullLayout._shapeSubplotLayer = layerSubplot.selectAll('.shapelayer'); | ||
fullLayout._shapeSubplotLayers = layerSubplot.selectAll('.shapelayer'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✏️ nicely done |
||
|
||
// styling separate from drawing | ||
Plots.style(gd); | ||
|
@@ -2855,11 +2854,20 @@ function makePlotFramework(gd) { | |
fullLayout._topdefs = fullLayout._toppaper.append('defs') | ||
.attr('id', 'topdefs-' + fullLayout._uid); | ||
|
||
fullLayout._bgLayer = fullLayout._paper.append('g') | ||
.classed('bglayer', true); | ||
|
||
fullLayout._draggers = fullLayout._paper.append('g') | ||
.classed('draglayer', true); | ||
|
||
// lower shape layer | ||
// (only for shapes to be drawn below the whole plot) | ||
// lower shape/image layer - note that this is behind | ||
// all subplots data/grids but above the backgrounds | ||
// except inset subplots, whose backgrounds are drawn | ||
// inside their own group so that they appear above | ||
// the data for the main subplot | ||
// lower shapes and images which are fully referenced to | ||
// a subplot still get drawn within the subplot's group | ||
// so they will work correctly on insets | ||
var layerBelow = fullLayout._paper.append('g') | ||
.classed('layer-below', true); | ||
fullLayout._imageLowerLayer = layerBelow.append('g') | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needed to unset the clip path if you change from data-referenced to fully paper-referenced.