-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
scatter marker color gradients #1620
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 8 commits
541460a
ff6c910
105ce9b
a074259
1b0a6bb
d1c6b51
e3af5eb
ec4d9ee
21650c7
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 |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
|
||
var d3 = require('d3'); | ||
var isNumeric = require('fast-isnumeric'); | ||
var tinycolor = require('tinycolor2'); | ||
|
||
var Registry = require('../../registry'); | ||
var Color = require('../color'); | ||
|
@@ -202,7 +203,7 @@ drawing.symbolNumber = function(v) { | |
return Math.floor(Math.max(v, 0)); | ||
}; | ||
|
||
function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerLine) { | ||
function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerLine, gd) { | ||
// only scatter & box plots get marker path and opacity | ||
// bars, histograms don't | ||
if(Registry.traceIs(trace, 'symbols')) { | ||
|
@@ -237,6 +238,8 @@ function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerL | |
}); | ||
} | ||
|
||
var perPointGradient = false; | ||
|
||
// 'so' is suspected outliers, for box plots | ||
var fillColor, | ||
lineColor, | ||
|
@@ -256,8 +259,12 @@ function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerL | |
else if(Array.isArray(markerLine.color)) lineColor = Color.defaultLine; | ||
else lineColor = markerLine.color; | ||
|
||
if(Array.isArray(marker.color)) { | ||
fillColor = Color.defaultLine; | ||
perPointGradient = true; | ||
} | ||
|
||
if('mc' in d) fillColor = d.mcc = markerScale(d.mc); | ||
else if(Array.isArray(marker.color)) fillColor = Color.defaultLine; | ||
else fillColor = marker.color || 'rgba(0,0,0,0)'; | ||
} | ||
|
||
|
@@ -271,24 +278,93 @@ function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerL | |
}); | ||
} | ||
else { | ||
sel.style('stroke-width', lineWidth + 'px') | ||
.call(Color.fill, fillColor); | ||
sel.style('stroke-width', lineWidth + 'px'); | ||
|
||
var markerGradient = marker.gradient; | ||
|
||
var gradientType = d.mgt; | ||
if(gradientType) perPointGradient = true; | ||
else gradientType = markerGradient && markerGradient.type; | ||
|
||
if(gradientType && gradientType !== 'none') { | ||
var gradientColor = d.mgc; | ||
if(gradientColor) perPointGradient = true; | ||
else gradientColor = markerGradient.color; | ||
|
||
var gradientID = 'g' + gd._fullLayout._uid + '-' + trace.uid; | ||
if(perPointGradient) gradientID += '-' + d.i; | ||
|
||
sel.call(drawing.gradient, gd, gradientID, gradientType, fillColor, gradientColor); | ||
} | ||
else { | ||
sel.call(Color.fill, fillColor); | ||
} | ||
|
||
if(lineWidth) { | ||
sel.call(Color.stroke, lineColor); | ||
} | ||
} | ||
} | ||
|
||
drawing.singlePointStyle = function(d, sel, trace) { | ||
var marker = trace.marker, | ||
markerLine = marker.line; | ||
var HORZGRADIENT = {x1: 1, x2: 0, y1: 0, y2: 0}; | ||
var VERTGRADIENT = {x1: 0, x2: 0, y1: 1, y2: 0}; | ||
|
||
// allow array marker and marker line colors to be | ||
// scaled by given max and min to colorscales | ||
var markerScale = drawing.tryColorscale(marker, ''), | ||
lineScale = drawing.tryColorscale(marker, 'line'); | ||
drawing.gradient = function(sel, gd, gradientID, type, color1, color2) { | ||
var gradient = gd._fullLayout._defs.select('.gradients') | ||
.selectAll('#' + gradientID) | ||
.data([type + color1 + color2], Lib.identity); | ||
|
||
gradient.exit().remove(); | ||
|
||
gradient.enter() | ||
.append(type === 'radial' ? 'radialGradient' : 'linearGradient') | ||
.each(function() { | ||
var el = d3.select(this); | ||
if(type === 'horizontal') el.attr(HORZGRADIENT); | ||
else if(type === 'vertical') el.attr(VERTGRADIENT); | ||
|
||
el.attr('id', gradientID); | ||
|
||
var tc1 = tinycolor(color1); | ||
var tc2 = tinycolor(color2); | ||
|
||
el.append('stop').attr({ | ||
offset: '0%', | ||
'stop-color': Color.tinyRGB(tc2), | ||
'stop-opacity': tc2.getAlpha() | ||
}); | ||
|
||
el.append('stop').attr({ | ||
offset: '100%', | ||
'stop-color': Color.tinyRGB(tc1), | ||
'stop-opacity': tc1.getAlpha() | ||
}); | ||
}); | ||
|
||
sel.style({ | ||
fill: 'url(#' + gradientID + ')', | ||
'fill-opacity': null | ||
}); | ||
}; | ||
|
||
/* | ||
* Make the gradients container and clear out any previous gradients. | ||
* We never collect all the gradients we need in one place, | ||
* so we can't ever remove gradients that have stopped being useful, | ||
* except all at once before a full redraw. | ||
* The upside of this is arbitrary points can share gradient defs | ||
*/ | ||
drawing.initGradients = function(gd) { | ||
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 pattern. Controlling gradient |
||
var gradientsGroup = gd._fullLayout._defs.selectAll('.gradients').data([0]); | ||
gradientsGroup.enter().append('g').classed('gradients', true); | ||
|
||
gradientsGroup.selectAll('linearGradient,radialGradient').remove(); | ||
}; | ||
|
||
drawing.singlePointStyle = function(d, sel, trace, markerScale, lineScale, gd) { | ||
var marker = trace.marker; | ||
|
||
singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerLine); | ||
singlePointStyle(d, sel, trace, markerScale, lineScale, marker, marker.line, gd); | ||
|
||
}; | ||
|
||
|
@@ -298,11 +374,12 @@ drawing.pointStyle = function(s, trace) { | |
// allow array marker and marker line colors to be | ||
// scaled by given max and min to colorscales | ||
var marker = trace.marker; | ||
var markerScale = drawing.tryColorscale(marker, ''), | ||
lineScale = drawing.tryColorscale(marker, 'line'); | ||
var markerScale = drawing.tryColorscale(marker, ''); | ||
var lineScale = drawing.tryColorscale(marker, 'line'); | ||
var gd = Lib.getPlotDiv(s.node()); | ||
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. I wonder if it might be better to add a ref to those gradient defs in 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. I'll leave it for now - I agree this is suboptimal but neither of these alternatives seems particularly simpler to me. If I had to pick one I'd probably go for the first actually... stashing the |
||
|
||
s.each(function(d) { | ||
drawing.singlePointStyle(d, d3.select(this), trace, markerScale, lineScale); | ||
drawing.singlePointStyle(d, d3.select(this), trace, markerScale, lineScale, gd); | ||
}); | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -153,6 +153,9 @@ Plotly.plot = function(gd, data, layout, config) { | |
makePlotFramework(gd); | ||
} | ||
|
||
// clear gradient defs on each .plot call, because we know we'll loop through all traces | ||
Drawing.initGradients(gd); | ||
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. It would be nice to add jasmine test checking that |
||
|
||
// save initial show spikes once per graph | ||
if(graphWasEmpty) Plotly.Axes.saveShowSpikeInitial(gd); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,9 @@ var Lib = require('../../lib'); | |
// arrayOk attributes, merge them into calcdata array | ||
module.exports = function arraysToCalcdata(cd, trace) { | ||
|
||
// so each point knows which index it originally came from | ||
for(var i = 0; i < cd.length; i++) cd[i].i = i; | ||
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. @etpinard to my comment earlier today about not using BUT once we have that, we could get rid of the rest of these I'm not going to do that now, but could be a nice 🚀 at some point. 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. Good 👁️ here and nice fix. But, I think the solution would be to make |
||
|
||
Lib.mergeArray(trace.text, cd, 'tx'); | ||
Lib.mergeArray(trace.hovertext, cd, 'htx'); | ||
Lib.mergeArray(trace.customdata, cd, 'data'); | ||
|
@@ -37,5 +40,11 @@ module.exports = function arraysToCalcdata(cd, trace) { | |
Lib.mergeArray(markerLine.color, cd, 'mlc'); | ||
Lib.mergeArray(markerLine.width, cd, 'mlw'); | ||
} | ||
|
||
var markerGradient = marker.gradient; | ||
if(markerGradient && markerGradient.type !== 'none') { | ||
Lib.mergeArray(markerGradient.type, cd, 'mgt'); | ||
Lib.mergeArray(markerGradient.color, cd, 'mgc'); | ||
} | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,12 +15,18 @@ var colorscaleDefaults = require('../../components/colorscale/defaults'); | |
|
||
var subTypes = require('./subtypes'); | ||
|
||
|
||
/* | ||
* opts: object of flags to control features not all marker users support | ||
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. Thanks for 🔒 ing this pattern. 🎉 |
||
* noLine: caller does not support marker lines | ||
* gradient: caller supports gradients | ||
*/ | ||
module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) { | ||
var isBubble = subTypes.isBubble(traceIn), | ||
lineColor = (traceIn.line || {}).color, | ||
defaultMLC; | ||
|
||
opts = opts || {}; | ||
|
||
// marker.color inherit from line.color (even if line.color is an array) | ||
if(lineColor) defaultColor = lineColor; | ||
|
||
|
@@ -33,7 +39,7 @@ module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout | |
colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'}); | ||
} | ||
|
||
if(!(opts || {}).noLine) { | ||
if(!opts.noLine) { | ||
// if there's a line with a different color than the marker, use | ||
// that line color as the default marker line color | ||
// (except when it's an array) | ||
|
@@ -57,4 +63,11 @@ module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout | |
coerce('marker.sizemin'); | ||
coerce('marker.sizemode'); | ||
} | ||
|
||
if(opts.gradient) { | ||
var gradientType = coerce('marker.gradient.type'); | ||
if(gradientType !== 'none') { | ||
coerce('marker.gradient.color'); | ||
} | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -107,7 +107,8 @@ module.exports = { | |
line: extendFlat({}, | ||
{width: scatterMarkerLineAttrs.width}, | ||
colorAttributes('marker'.line) | ||
) | ||
), | ||
gradient: scatterMarkerAttrs.gradient | ||
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. TODO: test image with carpet plots. Somehow 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.
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. another TODO: looks like this PR broke 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. Can you clarify which PR? Do you mean scattercarpet or this PR? Edit: realizing you probably mean these attributes need to be either added+implemented or properly avoided in gl2d. 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. 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. scattercarpet image test in 105ce9b |
||
}, colorAttributes('marker'), { | ||
showscale: scatterMarkerAttrs.showscale, | ||
colorbar: colorbarAttrs | ||
|
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.
I suspect this algo would work for adding gradients to bar traces? Would be a cool feature 😏
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.
yep, would be cool to add bar gradients... for another PR