Skip to content

Commit fe709d0

Browse files
authored
Merge pull request #2910 from plotly/gradient-colorbar
Use new multistop gradients for continuous colorbars
2 parents 7bba266 + c17b9bc commit fe709d0

12 files changed

+138
-110
lines changed

src/components/colorbar/connect.js

+2-14
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
'use strict';
1111

12-
var Colorscale = require('../colorscale');
1312
var drawColorbar = require('./draw');
1413

1514
/**
@@ -48,20 +47,9 @@ module.exports = function connectColorbar(gd, cd, moduleOpts) {
4847
gd._fullLayout._infolayer.selectAll('.' + cbId).remove();
4948
if(!container || !container.showscale) return;
5049

51-
var zmin = container[moduleOpts.min];
52-
var zmax = container[moduleOpts.max];
53-
5450
var cb = cd[0].t.cb = drawColorbar(gd, cbId);
55-
var sclFunc = Colorscale.makeColorScaleFunc(
56-
Colorscale.extractScale(
57-
container.colorscale,
58-
zmin,
59-
zmax
60-
),
61-
{ noNumericCheck: true }
62-
);
6351

64-
cb.fillcolor(sclFunc)
65-
.filllevels({start: zmin, end: zmax, size: (zmax - zmin) / 254})
52+
cb.fillgradient(container.colorscale)
53+
.zrange([container[moduleOpts.min], container[moduleOpts.max]])
6654
.options(container.colorbar)();
6755
};

src/components/colorbar/draw.js

+36-19
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ module.exports = function draw(gd, id) {
3939
// opts: options object, containing everything from attributes
4040
// plus a few others that are the equivalent of the colorbar "data"
4141
var opts = {};
42-
Object.keys(attributes).forEach(function(k) {
42+
for(var k in attributes) {
4343
opts[k] = null;
44-
});
44+
}
4545
// fillcolor can be a d3 scale, domain is z values, range is colors
4646
// or leave it out for no fill,
4747
// or set to a string constant for single-color fill
@@ -57,17 +57,23 @@ module.exports = function draw(gd, id) {
5757
// contour map) if this is omitted, fillcolors will be
5858
// evaluated halfway between levels
5959
opts.filllevels = null;
60+
// for continuous colorscales: fill with a gradient instead of explicit levels
61+
// value should be the colorscale [[0, c0], [v1, c1], ..., [1, cEnd]]
62+
opts.fillgradient = null;
63+
// when using a gradient, we need the data range specified separately
64+
opts.zrange = null;
6065

6166
function component() {
6267
var fullLayout = gd._fullLayout,
6368
gs = fullLayout._size;
6469
if((typeof opts.fillcolor !== 'function') &&
65-
(typeof opts.line.color !== 'function')) {
70+
(typeof opts.line.color !== 'function') &&
71+
!opts.fillgradient) {
6672
fullLayout._infolayer.selectAll('g.' + id).remove();
6773
return;
6874
}
69-
var zrange = d3.extent(((typeof opts.fillcolor === 'function') ?
70-
opts.fillcolor : opts.line.color).domain());
75+
var zrange = opts.zrange || (d3.extent(((typeof opts.fillcolor === 'function') ?
76+
opts.fillcolor : opts.line.color).domain()));
7177
var linelevels = [];
7278
var filllevels = [];
7379
var linecolormap = typeof opts.line.color === 'function' ?
@@ -87,7 +93,10 @@ module.exports = function draw(gd, id) {
8793
if(l > zr0 && l < zr1) linelevels.push(l);
8894
}
8995

90-
if(typeof opts.fillcolor === 'function') {
96+
if(opts.fillgradient) {
97+
filllevels = [0];
98+
}
99+
else if(typeof opts.fillcolor === 'function') {
91100
if(opts.filllevels) {
92101
l0 = opts.filllevels.end + opts.filllevels.size / 100;
93102
ls = opts.filllevels.size;
@@ -358,6 +367,12 @@ module.exports = function draw(gd, id) {
358367
.classed(cn.cbfill, true)
359368
.style('stroke', 'none');
360369
fills.exit().remove();
370+
371+
var zBounds = zrange
372+
.map(cbAxisOut.c2p)
373+
.map(Math.round)
374+
.sort(function(a, b) { return a - b; });
375+
361376
fills.each(function(d, i) {
362377
var z = [
363378
(i === 0) ? zrange[0] :
@@ -370,25 +385,27 @@ module.exports = function draw(gd, id) {
370385

371386
// offset the side adjoining the next rectangle so they
372387
// overlap, to prevent antialiasing gaps
373-
if(i !== filllevels.length - 1) {
374-
z[1] += (z[1] > z[0]) ? 1 : -1;
375-
}
376-
377-
378-
// Tinycolor can't handle exponents and
379-
// at this scale, removing it makes no difference.
380-
var colorString = fillcolormap(d).replace('e-', ''),
381-
opaqueColor = tinycolor(colorString).toHexString();
388+
z[1] = Lib.constrain(z[1] + (z[1] > z[0]) ? 1 : -1, zBounds[0], zBounds[1]);
382389

383390
// Colorbar cannot currently support opacities so we
384391
// use an opaque fill even when alpha channels present
385-
d3.select(this).attr({
392+
var fillEl = d3.select(this).attr({
386393
x: xLeft,
387394
width: Math.max(thickPx, 2),
388395
y: d3.min(z),
389396
height: Math.max(d3.max(z) - d3.min(z), 2),
390-
fill: opaqueColor
391397
});
398+
399+
if(opts.fillgradient) {
400+
Drawing.gradient(fillEl, gd, id, 'vertical',
401+
opts.fillgradient, 'fill');
402+
}
403+
else {
404+
// Tinycolor can't handle exponents and
405+
// at this scale, removing it makes no difference.
406+
var colorString = fillcolormap(d).replace('e-', '');
407+
fillEl.attr('fill', tinycolor(colorString).toHexString());
408+
}
392409
});
393410

394411
var lines = container.select('.cblines')
@@ -650,13 +667,13 @@ module.exports = function draw(gd, id) {
650667

651668
// or use .options to set multiple options at once via a dictionary
652669
component.options = function(o) {
653-
Object.keys(o).forEach(function(name) {
670+
for(var name in o) {
654671
// in case something random comes through
655672
// that's not an option, ignore it
656673
if(typeof component[name] === 'function') {
657674
component[name](o[name]);
658675
}
659-
});
676+
}
660677
return component;
661678
};
662679

src/traces/contour/colorbar.js

+10-17
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,25 @@ var endPlus = require('./end_plus');
1616

1717

1818
module.exports = function colorbar(gd, cd) {
19-
var trace = cd[0].trace,
20-
cbId = 'cb' + trace.uid;
19+
var trace = cd[0].trace;
20+
var cbId = 'cb' + trace.uid;
2121

2222
gd._fullLayout._infolayer.selectAll('.' + cbId).remove();
2323

2424
if(!trace.showscale) return;
2525

26-
var cb = drawColorbar(gd, cbId);
27-
cd[0].t.cb = cb;
26+
var cb = cd[0].t.cb = drawColorbar(gd, cbId);
2827

29-
var contours = trace.contours,
30-
line = trace.line,
31-
cs = contours.size || 1,
32-
coloring = contours.coloring;
28+
var contours = trace.contours;
29+
var line = trace.line;
30+
var cs = contours.size || 1;
31+
var coloring = contours.coloring;
3332

3433
var colorMap = makeColorMap(trace, {isColorbar: true});
3534

36-
if(coloring === 'heatmap') {
37-
cb.filllevels({
38-
start: trace.zmin,
39-
end: trace.zmax,
40-
size: (trace.zmax - trace.zmin) / 254
41-
});
42-
}
43-
44-
cb.fillcolor((coloring === 'fill' || coloring === 'heatmap') ? colorMap : '')
35+
cb.fillgradient(coloring === 'heatmap' ? trace.colorscale : '')
36+
.zrange(coloring === 'heatmap' ? [trace.zmin, trace.zmax] : '')
37+
.fillcolor((coloring === 'fill') ? colorMap : '')
4538
.line({
4639
color: coloring === 'lines' ? colorMap : line.color,
4740
width: contours.showlines !== false ? line.width : 0,

test/image/baselines/16.png

45 Bytes
Loading
Loading
-17 Bytes
Loading
Loading
30 Bytes
Loading
469 Bytes
Loading
-13 Bytes
Loading
79 Bytes
Loading

0 commit comments

Comments
 (0)