Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cef9e66

Browse files
committed
fix trace.contours edge cases and relayout bug with autocontour
1 parent bfa30db commit cef9e66

File tree

5 files changed

+164
-25
lines changed

5 files changed

+164
-25
lines changed

src/traces/contour/attributes.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,15 @@ module.exports = extendFlat({}, {
4444
},
4545
ncontours: {
4646
valType: 'integer',
47-
dflt: 0,
47+
dflt: 15,
48+
min: 1,
4849
role: 'style',
4950
description: [
5051
'Sets the maximum number of contour levels. The actual number',
5152
'of contours will be chosen automatically to be less than or',
5253
'equal to the value of `ncontours`.',
53-
'Has an effect only if `autocontour` is *true*.'
54+
'Has an effect only if `autocontour` is *true* or if',
55+
'`contours.size` is missing.'
5456
].join(' ')
5557
},
5658

@@ -59,19 +61,29 @@ module.exports = extendFlat({}, {
5961
valType: 'number',
6062
dflt: null,
6163
role: 'style',
62-
description: 'Sets the starting contour level value.'
64+
description: [
65+
'Sets the starting contour level value.',
66+
'Must be less than `contours.end`'
67+
].join(' ')
6368
},
6469
end: {
6570
valType: 'number',
6671
dflt: null,
6772
role: 'style',
68-
description: 'Sets the end contour level value.'
73+
description: [
74+
'Sets the end contour level value.',
75+
'Must be more than `contours.start`'
76+
].join(' ')
6977
},
7078
size: {
7179
valType: 'number',
7280
dflt: null,
81+
min: 0,
7382
role: 'style',
74-
description: 'Sets the step between each contour level.'
83+
description: [
84+
'Sets the step between each contour level.',
85+
'Must be positive.'
86+
].join(' ')
7587
},
7688
coloring: {
7789
valType: 'enumerated',

src/traces/contour/calc.js

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
'use strict';
1111

1212
var Axes = require('../../plots/cartesian/axes');
13+
var extendFlat = require('../../lib').extendFlat;
1314
var heatmapCalc = require('../heatmap/calc');
1415

1516

@@ -22,30 +23,72 @@ module.exports = function calc(gd, trace) {
2223

2324
// check if we need to auto-choose contour levels
2425
if(trace.autocontour !== false) {
25-
var dummyAx = {
26-
type: 'linear',
27-
range: [trace.zmin, trace.zmax]
28-
};
26+
var dummyAx = autoContours(trace.zmin, trace.zmax, trace.ncontours);
2927

30-
Axes.autoTicks(
31-
dummyAx,
32-
(trace.zmax - trace.zmin) / (trace.ncontours || 15)
33-
);
28+
contours.size = dummyAx.dtick;
3429

3530
contours.start = Axes.tickFirst(dummyAx);
36-
contours.size = dummyAx.dtick;
3731
dummyAx.range.reverse();
3832
contours.end = Axes.tickFirst(dummyAx);
3933

4034
if(contours.start === trace.zmin) contours.start += contours.size;
4135
if(contours.end === trace.zmax) contours.end -= contours.size;
4236

43-
// so rounding errors don't cause us to miss the last contour
44-
contours.end += contours.size / 100;
37+
// if you set a small ncontours, *and* the ends are exactly on zmin/zmax
38+
// there's an edge case where start > end now. Make sure there's at least
39+
// one meaningful contour, put it midway between the crossed values
40+
if(contours.start > contours.end) {
41+
contours.start = contours.end = (contours.start + contours.end) / 2;
42+
}
4543

4644
// copy auto-contour info back to the source data.
47-
trace._input.contours = contours;
45+
trace._input.contours = extendFlat({}, contours);
46+
}
47+
else {
48+
// sanity checks on manually-supplied start/end/size
49+
var start = contours.start,
50+
end = contours.end,
51+
inputContours = trace._input.contours;
52+
53+
if(start > end) {
54+
contours.start = inputContours.start = end;
55+
end = contours.end = inputContours.end = start;
56+
start = contours.start;
57+
}
58+
59+
if(!(contours.size > 0)) {
60+
var sizeOut;
61+
if(start === end) sizeOut = 1;
62+
else sizeOut = autoContours(start, end, trace.ncontours).dtick;
63+
64+
inputContours.size = contours.size = sizeOut;
65+
}
4866
}
4967

5068
return cd;
5169
};
70+
71+
/*
72+
* autoContours: make a dummy axis object with dtick we can use
73+
* as contours.size, and if needed we can use Axes.tickFirst
74+
* with this axis object to calculate the start and end too
75+
*
76+
* start: the value to start the contours at
77+
* end: the value to end at (must be > start)
78+
* ncontours: max number of contours to make, like roughDTick
79+
*
80+
* returns: an axis object
81+
*/
82+
function autoContours(start, end, ncontours) {
83+
var dummyAx = {
84+
type: 'linear',
85+
range: [start, end]
86+
};
87+
88+
Axes.autoTicks(
89+
dummyAx,
90+
(end - start) / (ncontours || 15)
91+
);
92+
93+
return dummyAx;
94+
}

src/traces/contour/defaults.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,19 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
3333

3434
var contourStart = Lib.coerce2(traceIn, traceOut, attributes, 'contours.start'),
3535
contourEnd = Lib.coerce2(traceIn, traceOut, attributes, 'contours.end'),
36-
autocontour = coerce('autocontour', !(contourStart && contourEnd));
36+
missingEnd = (contourStart === false) || (contourEnd === false),
3737

38-
if(autocontour) coerce('ncontours');
39-
else coerce('contours.size');
38+
// normally we only need size if autocontour is off. But contour.calc
39+
// pushes its calculated contour size back to the input trace, so for
40+
// things like restyle that can call supplyDefaults without calc
41+
// after the initial draw, we can just reuse the previous calculation
42+
contourSize = coerce('contours.size'),
43+
autoContour;
44+
45+
if(missingEnd) autoContour = traceOut.autocontour = true;
46+
else autoContour = coerce('autocontour', false);
47+
48+
if(autoContour || !contourSize) coerce('ncontours');
4049

4150
handleStyleDefaults(traceIn, traceOut, coerce, layout);
4251
};

src/traces/contour/plot.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,9 @@ function plotOne(gd, plotinfo, cd) {
8080
}
8181

8282
function emptyPathinfo(contours, plotinfo, cd0) {
83-
var cs = contours.size || 1,
83+
var cs = contours.size,
8484
pathinfo = [];
85+
8586
for(var ci = contours.start; ci < contours.end + cs / 10; ci += cs) {
8687
pathinfo.push({
8788
level: ci,
@@ -103,6 +104,11 @@ function emptyPathinfo(contours, plotinfo, cd0) {
103104
z: cd0.z,
104105
smoothing: cd0.trace.line.smoothing
105106
});
107+
108+
if(pathinfo.length > 1000) {
109+
Lib.warn('Too many contours, clipping at 1000', contours);
110+
break;
111+
}
106112
}
107113
return pathinfo;
108114
}

test/jasmine/tests/contour_test.js

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ describe('contour defaults', function() {
3434
[0.625, 1.25, 3.125, 6.25]],
3535
contours: {
3636
start: 4,
37-
end: 14,
38-
size: 0.5
37+
end: 14
38+
// missing size does NOT set autocontour true
39+
// even though in calc we set an autosize.
3940
}
4041
};
4142
supplyDefaults(traceIn, traceOut, defaultColor, layout);
@@ -46,7 +47,8 @@ describe('contour defaults', function() {
4647
z: [[10, 10.625, 12.5, 15.625],
4748
[5.625, 6.25, 8.125, 11.25],
4849
[2.5, 3.125, 5.0, 8.125],
49-
[0.625, 1.25, 3.125, 6.25]]
50+
[0.625, 1.25, 3.125, 6.25]],
51+
contours: {start: 4} // you need at least start and end
5052
};
5153
supplyDefaults(traceIn, traceOut, defaultColor, layout);
5254
expect(traceOut.autocontour).toBe(true);
@@ -183,7 +185,9 @@ describe('contour calc', function() {
183185
Plots.supplyDefaults(gd);
184186
var fullTrace = gd._fullData[0];
185187

186-
return Contour.calc(gd, fullTrace)[0];
188+
var out = Contour.calc(gd, fullTrace)[0];
189+
out.trace = fullTrace;
190+
return out;
187191
}
188192

189193
it('should fill in bricks if x/y not given', function() {
@@ -269,4 +273,69 @@ describe('contour calc', function() {
269273
expect(out.y).toBeCloseToArray([0, 1]);
270274
expect(out.z).toBeCloseTo2DArray([[1, 2, 3], [3, 1, 2]]);
271275
});
276+
277+
it('should make nice autocontour values', function() {
278+
var incompleteContours = [
279+
undefined,
280+
{start: 12},
281+
{end: 45},
282+
{start: 2, size: 2} // size gets ignored
283+
];
284+
285+
var contoursFinal = [
286+
// fully auto. These are *not* exactly the output contours objects,
287+
// I put the input ncontours in here too.
288+
{inputNcontours: undefined, start: 0.5, end: 4.5, size: 0.5},
289+
// explicit ncontours
290+
{inputNcontours: 6, start: 1, end: 4, size: 1},
291+
// edge case where low ncontours makes start and end cross
292+
{inputNcontours: 2, start: 2.5, end: 2.5, size: 5}
293+
];
294+
295+
incompleteContours.forEach(function(contoursIn) {
296+
contoursFinal.forEach(function(spec) {
297+
var out = _calc({
298+
z: [[0, 2], [3, 5]],
299+
contours: contoursIn,
300+
ncontours: spec.inputNcontours
301+
}).trace;
302+
303+
['start', 'end', 'size'].forEach(function(attr) {
304+
expect(out.contours[attr]).toBe(spec[attr], [contoursIn, attr]);
305+
// all these get copied back to the input trace
306+
expect(out._input.contours[attr]).toBe(spec[attr], [contoursIn, attr]);
307+
});
308+
});
309+
});
310+
});
311+
312+
it('should supply size and reorder start/end if autocontour is off', function() {
313+
var specs = [
314+
{start: 1, end: 100, ncontours: undefined, size: 10},
315+
{start: 1, end: 100, ncontours: 5, size: 20},
316+
{start: 10, end: 10, ncontours: 10, size: 1}
317+
];
318+
319+
specs.forEach(function(spec) {
320+
[
321+
[spec.start, spec.end, 'normal'],
322+
[spec.end, spec.start, 'reversed']
323+
].forEach(function(v) {
324+
var startIn = v[0],
325+
endIn = v[1],
326+
order = v[2];
327+
328+
var out = _calc({
329+
z: [[1, 2], [3, 4]],
330+
contours: {start: startIn, end: endIn},
331+
ncontours: spec.ncontours
332+
}).trace;
333+
334+
['start', 'end', 'size'].forEach(function(attr) {
335+
expect(out.contours[attr]).toBe(spec[attr], [spec, order, attr]);
336+
expect(out._input.contours[attr]).toBe(spec[attr], [spec, order, attr]);
337+
});
338+
});
339+
});
340+
});
272341
});

0 commit comments

Comments
 (0)