Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 2k
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
541460aff6c910105ce9ba0742591b0a6bbd1c6b51e3af5ebec4d9ee21650c7File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
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) { | ||
| 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()); | ||
Contributor 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 CollaboratorAuthor 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); | ||
Contributor 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; | ||
CollaboratorAuthor 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. Contributor 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 | ||
Contributor 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 | ||
CollaboratorAuthor 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 Contributor 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.
Contributor 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
| ||
| }, colorAttributes('marker'), { | ||
| showscale: scatterMarkerAttrs.showscale, | ||
| colorbar: colorbarAttrs | ||
Uh oh!
There was an error while loading. Please reload this page.
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.
Nice pattern. Controlling gradient
defsin one place should help us in #581 too.