Skip to content

Commit 2e7f533

Browse files
authored
Merge pull request #3549 from plotly/cmid
Add midpoint attr to color scales
2 parents 7da95e1 + ff67812 commit 2e7f533

File tree

9 files changed

+153
-7
lines changed

9 files changed

+153
-7
lines changed

src/components/colorscale/attributes.js

+17
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ module.exports = function colorScaleAttrs(context, opts) {
8787
var auto = cLetter + 'auto';
8888
var min = cLetter + 'min';
8989
var max = cLetter + 'max';
90+
var mid = cLetter + 'mid';
91+
var autoFull = code(contextHead + auto);
9092
var minFull = code(contextHead + min);
9193
var maxFull = code(contextHead + max);
9294
var minmaxFull = minFull + ' and ' + maxFull;
@@ -160,6 +162,21 @@ module.exports = function colorScaleAttrs(context, opts) {
160162
].join('')
161163
};
162164

165+
attrs[mid] = {
166+
valType: 'number',
167+
role: 'info',
168+
dflt: null,
169+
editType: 'calc',
170+
impliedEdits: autoImpliedEdits,
171+
description: [
172+
'Sets the mid-point of the color domain by scaling ', minFull,
173+
' and/or ', maxFull, ' to be equidistant to this point.',
174+
effectDesc,
175+
' Value should have the same units as ', colorAttrFull, '. ',
176+
'Has no effect when ', autoFull, ' is `false`.'
177+
].join('')
178+
};
179+
163180
attrs.colorscale = {
164181
valType: 'colorscale',
165182
role: 'style',

src/components/colorscale/calc.js

+11
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ module.exports = function calc(gd, trace, opts) {
2323
var autoAttr = cLetter + 'auto';
2424
var minAttr = cLetter + 'min';
2525
var maxAttr = cLetter + 'max';
26+
var midAttr = cLetter + 'mid';
2627
var auto = container[autoAttr];
2728
var min = container[minAttr];
2829
var max = container[maxAttr];
30+
var mid = container[midAttr];
2931
var scl = container.colorscale;
3032

3133
if(auto !== false || min === undefined) {
@@ -36,6 +38,15 @@ module.exports = function calc(gd, trace, opts) {
3638
max = Lib.aggNums(Math.max, null, vals);
3739
}
3840

41+
if(auto !== false && mid !== undefined) {
42+
if(max - mid > mid - min) {
43+
min = mid - (max - mid);
44+
}
45+
else if(max - mid < mid - min) {
46+
max = mid + (mid - min);
47+
}
48+
}
49+
3950
if(min === max) {
4051
min -= 0.5;
4152
max += 0.5;

src/components/colorscale/defaults.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,14 @@ module.exports = function colorScaleDefaults(traceIn, traceOut, layout, coerce,
3333
var minIn = containerIn[cLetter + 'min'];
3434
var maxIn = containerIn[cLetter + 'max'];
3535
var validMinMax = isNumeric(minIn) && isNumeric(maxIn) && (minIn < maxIn);
36-
coerce(prefix + cLetter + 'auto', !validMinMax);
37-
coerce(prefix + cLetter + 'min');
38-
coerce(prefix + cLetter + 'max');
36+
var auto = coerce(prefix + cLetter + 'auto', !validMinMax);
37+
38+
if(auto) {
39+
coerce(prefix + cLetter + 'mid');
40+
} else {
41+
coerce(prefix + cLetter + 'min');
42+
coerce(prefix + cLetter + 'max');
43+
}
3944

4045
// handles both the trace case (autocolorscale is false by default) and
4146
// the marker and marker.line case (autocolorscale is true by default)

src/traces/scattermapbox/attributes.js

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ module.exports = overrideAll({
9393
cauto: markerAttrs.cauto,
9494
cmax: markerAttrs.cmax,
9595
cmin: markerAttrs.cmin,
96+
cmid: markerAttrs.cmid,
9697
autocolorscale: markerAttrs.autocolorscale,
9798
reversescale: markerAttrs.reversescale,
9899
showscale: markerAttrs.showscale,

test/image/baselines/cmid-zmid.png

26.8 KB
Loading

test/image/mocks/cmid-zmid.json

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"data": [{
3+
"y": [1, 2, 1],
4+
"mode": "markers",
5+
"marker": {
6+
"size": 20,
7+
"color": [-1, 1, 10],
8+
"cmid": "0",
9+
"colorbar": {
10+
"len": 0.3,
11+
"y": "1",
12+
"yanchor": "top",
13+
"title": {"text": "marker.cmid=0", "side": "right"}
14+
}
15+
}
16+
}, {
17+
"type": "heatmap",
18+
"z": [[1,2], [2, 3]],
19+
"zmid": -1,
20+
"colorbar": {
21+
"len": 0.3,
22+
"y": "0.5",
23+
"yanchor": "middle",
24+
"title": {"text": "zmid=-1", "side": "right"}
25+
}
26+
}, {
27+
"type": "bar",
28+
"y": [0.5, 1, 0.5],
29+
"name": "marker.line.cmid=2",
30+
"hoverlabel": {"namelength": -1},
31+
"marker": {
32+
"line": {
33+
"width": 2,
34+
"color": [-1, -1, -10],
35+
"cmid": 2
36+
}
37+
}
38+
}],
39+
"layout": {
40+
"title": {"text": "Traces with set <b>cmid</b> / <b>zmid</b>"},
41+
"showlegend": false
42+
}
43+
}

test/jasmine/tests/plot_api_test.js

+71
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,77 @@ describe('Test plot api', function() {
11951195
.then(done);
11961196
});
11971197

1198+
it('turns on cauto when cmid is edited', function(done) {
1199+
function _assert(msg, exp) {
1200+
return function() {
1201+
var mk = gd._fullData[0].marker;
1202+
for(var k in exp) {
1203+
expect(mk[k]).toBe(exp[k], [msg, k].join(' - '));
1204+
}
1205+
};
1206+
}
1207+
1208+
function _restyle(arg) {
1209+
return function() { return Plotly.restyle(gd, arg); };
1210+
}
1211+
1212+
Plotly.plot(gd, [{
1213+
mode: 'markers',
1214+
y: [1, 2, 1],
1215+
marker: { color: [1, -1, 4] }
1216+
}])
1217+
.then(_assert('base', {
1218+
cauto: true,
1219+
cmid: undefined,
1220+
cmin: -1,
1221+
cmax: 4
1222+
}))
1223+
.then(_restyle({'marker.cmid': 0}))
1224+
.then(_assert('set cmid=0', {
1225+
cauto: true,
1226+
cmid: 0,
1227+
cmin: -4,
1228+
cmax: 4
1229+
}))
1230+
.then(_restyle({'marker.cmid': -2}))
1231+
.then(_assert('set cmid=-2', {
1232+
cauto: true,
1233+
cmid: -2,
1234+
cmin: -8,
1235+
cmax: 4
1236+
}))
1237+
.then(_restyle({'marker.cmid': 2}))
1238+
.then(_assert('set cmid=2', {
1239+
cauto: true,
1240+
cmid: 2,
1241+
cmin: -1,
1242+
cmax: 5
1243+
}))
1244+
.then(_restyle({'marker.cmin': 0}))
1245+
.then(_assert('set cmin=0', {
1246+
cauto: false,
1247+
cmid: undefined,
1248+
cmin: 0,
1249+
cmax: 5
1250+
}))
1251+
.then(_restyle({'marker.cmax': 10}))
1252+
.then(_assert('set cmin=0 + cmax=10', {
1253+
cauto: false,
1254+
cmid: undefined,
1255+
cmin: 0,
1256+
cmax: 10
1257+
}))
1258+
.then(_restyle({'marker.cauto': true, 'marker.cmid': null}))
1259+
.then(_assert('back to cauto=true', {
1260+
cauto: true,
1261+
cmid: undefined,
1262+
cmin: -1,
1263+
cmax: 4
1264+
}))
1265+
.catch(failTest)
1266+
.then(done);
1267+
});
1268+
11981269
it('turns off autobin when you edit bin specs', function(done) {
11991270
// test retained (modified) for backward compat with new autobin logic
12001271
var start0 = 0.2;

test/jasmine/tests/scattergeo_test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ describe('Test scattergeo defaults', function() {
9090
describe('Test scattergeo calc', function() {
9191

9292
function _calc(opts) {
93-
var base = { type: 'scattermapbox' };
93+
var base = { type: 'scattergeo' };
9494
var trace = Lib.extendFlat({}, base, opts);
9595
var gd = { data: [trace] };
9696

test/jasmine/tests/surface_test.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,14 @@ describe('Test surface', function() {
127127
it('should coerce \'c\' attributes with \'c\' values regardless of `\'z\' if \'c\' is present', function() {
128128
traceIn = {
129129
z: [[1, 2, 3], [2, 1, 2]],
130-
zauto: false,
131130
zmin: 0,
132131
zmax: 10,
133-
cauto: true,
134132
cmin: -10,
135133
cmax: 20
136134
};
137135

138136
supplyDefaults(traceIn, traceOut, defaultColor, layout);
139-
expect(traceOut.cauto).toEqual(true);
137+
expect(traceOut.cauto).toEqual(false);
140138
expect(traceOut.cmin).toEqual(-10);
141139
expect(traceOut.cmax).toEqual(20);
142140
});

0 commit comments

Comments
 (0)