Skip to content

Commit 07ce3d2

Browse files
committed
fix issue 4427 - streamtube should handle string values
1 parent 239a837 commit 07ce3d2

File tree

3 files changed

+124
-70
lines changed

3 files changed

+124
-70
lines changed

src/traces/streamtube/calc.js

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ module.exports = function calc(gd, trace) {
2525
var slen = 0;
2626
var startx, starty, startz;
2727
if(trace.starts) {
28-
startx = trace.starts.x || [];
29-
starty = trace.starts.y || [];
30-
startz = trace.starts.z || [];
28+
startx = filter(trace.starts.x || []);
29+
starty = filter(trace.starts.y || []);
30+
startz = filter(trace.starts.z || []);
3131
slen = Math.min(startx.length, starty.length, startz.length);
3232
}
33+
trace._startsX = startx || [];
34+
trace._startsY = starty || [];
35+
trace._startsZ = startz || [];
3336

3437
var normMax = 0;
3538
var normMin = Infinity;
@@ -61,13 +64,18 @@ module.exports = function calc(gd, trace) {
6164
var filledX;
6265
var filledY;
6366
var filledZ;
64-
var firstX;
65-
var firstY;
66-
var firstZ;
67+
var firstX, lastX;
68+
var firstY, lastY;
69+
var firstZ, lastZ;
6770
if(len) {
68-
firstX = x[0];
69-
firstY = y[0];
70-
firstZ = z[0];
71+
firstX = +x[0];
72+
firstY = +y[0];
73+
firstZ = +z[0];
74+
}
75+
if(len > 1) {
76+
lastX = +x[len - 1];
77+
lastY = +y[len - 1];
78+
lastZ = +z[len - 1];
7179
}
7280

7381
for(i = 0; i < len; i++) {
@@ -80,15 +88,15 @@ module.exports = function calc(gd, trace) {
8088
zMax = Math.max(zMax, z[i]);
8189
zMin = Math.min(zMin, z[i]);
8290

83-
if(!filledX && x[i] !== firstX) {
91+
if(!filledX && (+x[i]) !== firstX) {
8492
filledX = true;
8593
gridFill += 'x';
8694
}
87-
if(!filledY && y[i] !== firstY) {
95+
if(!filledY && (+y[i]) !== firstY) {
8896
filledY = true;
8997
gridFill += 'y';
9098
}
91-
if(!filledZ && z[i] !== firstZ) {
99+
if(!filledZ && (+z[i]) !== firstZ) {
92100
filledZ = true;
93101
gridFill += 'z';
94102
}
@@ -98,13 +106,13 @@ module.exports = function calc(gd, trace) {
98106
if(!filledY) gridFill += 'y';
99107
if(!filledZ) gridFill += 'z';
100108

101-
var Xs = distinctVals(trace.x.slice(0, len));
102-
var Ys = distinctVals(trace.y.slice(0, len));
103-
var Zs = distinctVals(trace.z.slice(0, len));
109+
var Xs = distinctVals(filter(trace.x, len));
110+
var Ys = distinctVals(filter(trace.y, len));
111+
var Zs = distinctVals(filter(trace.z, len));
104112

105-
gridFill = gridFill.replace('x', (x[0] > x[len - 1] ? '-' : '+') + 'x');
106-
gridFill = gridFill.replace('y', (y[0] > y[len - 1] ? '-' : '+') + 'y');
107-
gridFill = gridFill.replace('z', (z[0] > z[len - 1] ? '-' : '+') + 'z');
113+
gridFill = gridFill.replace('x', (firstX > lastX ? '-' : '+') + 'x');
114+
gridFill = gridFill.replace('y', (firstY > lastY ? '-' : '+') + 'y');
115+
gridFill = gridFill.replace('z', (firstZ > lastZ ? '-' : '+') + 'z');
108116

109117
var empty = function() {
110118
len = 0;
@@ -118,7 +126,7 @@ module.exports = function calc(gd, trace) {
118126

119127
var getArray = function(c) { return c === 'x' ? x : c === 'y' ? y : z; };
120128
var getVals = function(c) { return c === 'x' ? Xs : c === 'y' ? Ys : Zs; };
121-
var getDir = function(c) { return (c[len - 1] < c[0]) ? -1 : 1; };
129+
var getDir = function(c) { return lessThan(c[len - 1], c[0]) ? -1 : 1; };
122130

123131
var arrK = getArray(gridFill[1]);
124132
var arrJ = getArray(gridFill[3]);
@@ -146,9 +154,9 @@ module.exports = function calc(gd, trace) {
146154
var q100 = getIndex(i + 1, j, k);
147155

148156
if(
149-
!(arrK[q000] * dirK < arrK[q001] * dirK) ||
150-
!(arrJ[q000] * dirJ < arrJ[q010] * dirJ) ||
151-
!(arrI[q000] * dirI < arrI[q100] * dirI)
157+
!lessThan(arrK[q000] * dirK, arrK[q001] * dirK) ||
158+
!lessThan(arrJ[q000] * dirJ, arrJ[q010] * dirJ) ||
159+
!lessThan(arrI[q000] * dirI, arrI[q100] * dirI)
152160
) {
153161
arbitrary = true;
154162
}
@@ -194,3 +202,17 @@ module.exports = function calc(gd, trace) {
194202
function distinctVals(col) {
195203
return Lib.distinctVals(col).vals;
196204
}
205+
206+
function filter(arr, len) {
207+
if(len === undefined) len = arr.length;
208+
209+
var values = [];
210+
for(var i = 0; i < len; i++) {
211+
values[i] = +arr[i];
212+
}
213+
return values;
214+
}
215+
216+
function lessThan(a, b) {
217+
return +a < +b;
218+
}

src/traces/streamtube/convert.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ function convert(scene, trace) {
122122
var slen = trace._slen;
123123
if(slen) {
124124
tubeOpts.startingPositions = zip3(
125-
toDataCoords(trace.starts.x.slice(0, slen), 'xaxis'),
126-
toDataCoords(trace.starts.y.slice(0, slen), 'yaxis'),
127-
toDataCoords(trace.starts.z.slice(0, slen), 'zaxis')
125+
toDataCoords(trace._startsX, 'xaxis'),
126+
toDataCoords(trace._startsY, 'yaxis'),
127+
toDataCoords(trace._startsZ, 'zaxis')
128128
);
129129
} else {
130130
// Default starting positions:

test/jasmine/tests/streamtube_test.js

Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -246,55 +246,87 @@ describe('Test streamtube interactions', function() {
246246
.then(done);
247247
});
248248

249-
it('@gl should work with negative grid steps', function(done) {
250-
var x = [];
251-
var y = [];
252-
var z = [];
253-
var u = [];
254-
var v = [];
255-
var w = [];
256-
257-
for(var i = 0; i < 3; i++) {
258-
for(var j = 0; j < 4; j++) {
259-
for(var k = 0; k < 5; k++) {
260-
x.push(-i);
261-
y.push(-j);
262-
z.push(-k);
263-
u.push(1);
264-
v.push(1);
265-
w.push(1);
249+
[ // list of directions
250+
'number',
251+
'string'
252+
].forEach(function(format) {
253+
[ // list of directions
254+
[-1, -1, -1],
255+
[-1, -1, 1],
256+
[-1, 1, -1],
257+
[1, -1, -1],
258+
[1, 1, -1],
259+
[1, -1, 1],
260+
[-1, 1, 1],
261+
[1, 1, 1]
262+
].forEach(function(dir) {
263+
it('@gl should work with grid steps: ' + dir + ' and values in ' + format + ' format.', function(done) {
264+
var x = [];
265+
var y = [];
266+
var z = [];
267+
var u = [];
268+
var v = [];
269+
var w = [];
270+
271+
for(var i = 0; i < 3; i++) {
272+
for(var j = 0; j < 4; j++) {
273+
for(var k = 0; k < 5; k++) {
274+
var newU = 1;
275+
var newV = 1;
276+
var newW = 1;
277+
var newX = i * dir[0];
278+
var newY = j * dir[1];
279+
var newZ = k * dir[2];
280+
281+
if(format === 'string') {
282+
newU = String(newU);
283+
newV = String(newV);
284+
newW = String(newW);
285+
newX = String(newX);
286+
newY = String(newY);
287+
newZ = String(newZ);
288+
}
289+
290+
u.push(newU);
291+
v.push(newV);
292+
w.push(newW);
293+
x.push(newX);
294+
y.push(newY);
295+
z.push(newZ);
296+
}
297+
}
266298
}
267-
}
268-
}
269-
270-
var fig = {
271-
data: [{
272-
type: 'streamtube',
273-
x: x,
274-
y: y,
275-
z: z,
276-
u: u,
277-
v: v,
278-
w: w
279-
}]
280-
};
281299

282-
function _assert(msg, exp) {
283-
var scene = gd._fullLayout.scene._scene;
284-
var objs = scene.glplot.objects;
285-
expect(objs.length).toBe(1, 'one gl-vis object - ' + msg);
286-
expect(exp.positionsLength).toBe(objs[0].positions.length, 'positions length - ' + msg);
287-
expect(exp.cellsLength).toBe(objs[0].cells.length, 'cells length - ' + msg);
288-
}
300+
var fig = {
301+
data: [{
302+
type: 'streamtube',
303+
x: x,
304+
y: y,
305+
z: z,
306+
u: u,
307+
v: v,
308+
w: w
309+
}]
310+
};
311+
312+
function _assert(msg, exp) {
313+
var scene = gd._fullLayout.scene._scene;
314+
var objs = scene.glplot.objects;
315+
expect(objs.length).toBe(1, 'one gl-vis object - ' + msg);
316+
expect(exp.positionsLength).toBe(objs[0].positions.length, 'positions length - ' + msg);
317+
expect(exp.cellsLength).toBe(objs[0].cells.length, 'cells length - ' + msg);
318+
}
289319

290-
Plotly.plot(gd, fig).then(function() {
291-
_assert('with negative steps', {
292-
positionsLength: 6336,
293-
cellsLength: 2112
320+
Plotly.plot(gd, fig).then(function() {
321+
_assert('lengths', {
322+
positionsLength: 6336,
323+
cellsLength: 2112
324+
});
325+
})
326+
.catch(failTest)
327+
.then(done);
294328
});
295-
})
296-
.catch(failTest)
297-
.then(done);
329+
});
298330
});
299331

300332
it('@gl should return blank mesh grid if encountered arbitrary coordinates', function(done) {

0 commit comments

Comments
 (0)