Skip to content

Commit c3f210f

Browse files
committed
test handling of cmin, cmax and z that are zero or negative
1 parent 3962593 commit c3f210f

2 files changed

Lines changed: 134 additions & 1 deletion

File tree

test/jasmine/tests/colorbar_test.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,5 +589,56 @@ describe('Test colorbar:', function() {
589589
// // Verify a linear tick like '500' was NOT generated
590590
expect(tickTexts).not.toContain('500');
591591
});
592+
593+
it('should fall back to a linear colorbar when zmin is explicitly non-positive, instead of a NaN-derived range', function(done) {
594+
Plotly.newPlot(gd, [{
595+
type: 'heatmap',
596+
z: [[-5, 10, 100], [10, 100, 1000]],
597+
zmin: -5,
598+
zmax: 1000,
599+
colorbar: {
600+
type: 'log',
601+
tickmode: 'auto'
602+
}
603+
}])
604+
.then(function() {
605+
var ax;
606+
gd._fullLayout._infolayer.selectAll('g.colorbar').each(function(opts) {
607+
ax = opts._axis;
608+
});
609+
610+
// a log colorbar can't represent a non-positive zmin, so the
611+
// whole colorbar (and its mocked axis) falls back to linear
612+
// instead of producing a NaN/folded log range
613+
expect(gd._fullData[0].colorbar.type).toBe('linear');
614+
expect(ax.type).toBe('linear');
615+
expect(ax.range).toEqual([-5, 1000]);
616+
})
617+
.then(done, done.fail);
618+
});
619+
620+
it('should mask out non-positive z values and stay log when autoscaling with mixed-sign data', function(done) {
621+
Plotly.newPlot(gd, [{
622+
type: 'heatmap',
623+
z: [[-5, 2, 100], [10, 100, 1000]],
624+
colorbar: {
625+
type: 'log',
626+
tickmode: 'auto'
627+
}
628+
}])
629+
.then(function() {
630+
var ax;
631+
gd._fullLayout._infolayer.selectAll('g.colorbar').each(function(opts) {
632+
ax = opts._axis;
633+
});
634+
635+
expect(gd._fullData[0].colorbar.type).toBe('log');
636+
expect(ax.type).toBe('log');
637+
// -5 is masked out of autorange; smallest positive value (2) is zmin
638+
expect(ax.range[0]).toBeCloseTo(Math.log10(2), 5);
639+
expect(ax.range[1]).toBeCloseTo(Math.log10(1000), 5);
640+
})
641+
.then(done, done.fail);
642+
});
592643
});
593644
});

test/jasmine/tests/colorscale_test.js

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,53 @@ describe('Test colorscale:', function() {
654654
expect(fullLayout.coloraxis.cmax).toBe(4);
655655
expect(fullLayout.coloraxis._cmax).toBe(4);
656656
});
657+
658+
it('should fall back to a linear colorbar when zmin/zmax is explicitly non-positive for a log colorbar', function() {
659+
trace = {
660+
type: 'heatmap',
661+
z: [[-5, 10, 100], [10, 100, 1000]],
662+
zmin: -5,
663+
zmax: 1000,
664+
zauto: false,
665+
colorbar: {type: 'log'}
666+
};
667+
gd = _supply(trace);
668+
calcColorscale(gd, trace, {vals: trace.z, containerStr: '', cLetter: 'z'});
669+
670+
expect(trace.colorbar.type).toBe('linear');
671+
expect(trace._zmin).toBe(-5);
672+
expect(trace._zmax).toBe(1000);
673+
});
674+
675+
it('should fall back to a linear colorbar when autoscaling finds no positive values for a log colorbar', function() {
676+
trace = {
677+
type: 'heatmap',
678+
z: [[-5, -10], [-100, -1]],
679+
colorbar: {type: 'log'}
680+
};
681+
gd = _supply(trace);
682+
calcColorscale(gd, trace, {vals: trace.z, containerStr: '', cLetter: 'z'});
683+
684+
expect(trace.colorbar.type).toBe('linear');
685+
expect(trace._zmin).toBe(-100);
686+
expect(trace._zmax).toBe(-1);
687+
});
688+
689+
it('should mask out non-positive values when autoscaling a log colorbar, like log cartesian axes do', function() {
690+
trace = {
691+
type: 'heatmap',
692+
z: [[-5, 2, 100], [10, 100, 1000]],
693+
colorbar: {type: 'log'}
694+
};
695+
gd = _supply(trace);
696+
calcColorscale(gd, trace, {vals: trace.z, containerStr: '', cLetter: 'z'});
697+
698+
// stays log - there is positive data to scale from
699+
expect(trace.colorbar.type).toBe('log');
700+
// -5 is masked out; the smallest positive value (2) becomes zmin
701+
expect(trace._zmin).toBe(2);
702+
expect(trace._zmax).toBe(1000);
703+
});
657704
});
658705

659706
describe('extractScale + makeColorScaleFunc', function() {
@@ -1247,7 +1294,42 @@ describe('Test colorscale restyle calls:', function() {
12471294
expect(colorFn(0)).toEqual('rgba(0,0,0,0)');
12481295
expect(colorFn(-10)).toEqual('rgba(0,0,0,0)');
12491296
});
1250-
1297+
1298+
it('should return numeric rgba arrays (not strings) when called with returnArray/noNumericCheck, as heatmap rendering does', function() {
1299+
var trace = {
1300+
cmin: 1,
1301+
cmax: 100,
1302+
colorscale: [
1303+
[0, 'rgb(0, 0, 0)'],
1304+
[1, 'rgb(200, 100, 50)']
1305+
],
1306+
colorbar: {
1307+
type: 'log'
1308+
}
1309+
};
1310+
1311+
var colorFn = makeColorScaleFuncFromTrace(trace, {noNumericCheck: true, returnArray: true});
1312+
1313+
// log10(1) = 0 -> maps to 0% of the scale
1314+
var cLow = colorFn(1);
1315+
expect(cLow).toEqual([0, 0, 0, 1]);
1316+
expect(typeof cLow[0]).toBe('number');
1317+
1318+
// log10(100) = 2 -> maps to 100% of the scale
1319+
var cHigh = colorFn(100);
1320+
expect(cHigh).toEqual([200, 100, 50, 1]);
1321+
1322+
// zero/negative must clamp to a numeric transparent array, not a css string,
1323+
// since callers like heatmap/plot.js index directly into the result (c[0], c[1], c[2])
1324+
var cZero = colorFn(0);
1325+
expect(cZero).toEqual([0, 0, 0, 0]);
1326+
expect(typeof cZero[0]).toBe('number');
1327+
1328+
var cNeg = colorFn(-10);
1329+
expect(cNeg).toEqual([0, 0, 0, 0]);
1330+
expect(typeof cNeg[0]).toBe('number');
1331+
});
1332+
12511333
it('should safely fall back to linear if type is undefined or linear', function() {
12521334
var trace = {
12531335
cmin: 1,

0 commit comments

Comments
 (0)