Skip to content

Commit 72689c4

Browse files
committed
return blank streamtube meshgrid when encountered arbitrary coordinates
1 parent 69f0939 commit 72689c4

File tree

3 files changed

+184
-27
lines changed

3 files changed

+184
-27
lines changed

src/traces/streamtube/calc.js

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88

99
'use strict';
1010

11+
var Lib = require('../../lib');
1112
var colorscaleCalc = require('../../components/colorscale/calc');
1213

1314
module.exports = function calc(gd, trace) {
14-
var i;
15+
var i, j, k;
1516

1617
var u = trace.u;
1718
var v = trace.v;
@@ -60,44 +61,95 @@ module.exports = function calc(gd, trace) {
6061
var filledX;
6162
var filledY;
6263
var filledZ;
63-
var prevX;
64-
var prevY;
65-
var prevZ;
64+
var firstX;
65+
var firstY;
66+
var firstZ;
6667
if(len) {
67-
prevX = x[0];
68-
prevY = y[0];
69-
prevZ = z[0];
68+
firstX = x[0];
69+
firstY = y[0];
70+
firstZ = z[0];
7071
}
7172

7273
for(i = 0; i < len; i++) {
73-
var xx = x[i];
74-
xMax = Math.max(xMax, xx);
75-
xMin = Math.min(xMin, xx);
74+
xMax = Math.max(xMax, x[i]);
75+
xMin = Math.min(xMin, x[i]);
7676

77-
var yy = y[i];
78-
yMax = Math.max(yMax, yy);
79-
yMin = Math.min(yMin, yy);
77+
yMax = Math.max(yMax, y[i]);
78+
yMin = Math.min(yMin, y[i]);
8079

81-
var zz = z[i];
82-
zMax = Math.max(zMax, zz);
83-
zMin = Math.min(zMin, zz);
80+
zMax = Math.max(zMax, z[i]);
81+
zMin = Math.min(zMin, z[i]);
8482

85-
if(!filledX && xx !== prevX) {
83+
if(!filledX && x[i] !== firstX) {
8684
filledX = true;
8785
gridFill += 'x';
88-
} else if(!filledY && yy !== prevY) {
86+
} else if(!filledY && y[i] !== firstY) {
8987
filledY = true;
9088
gridFill += 'y';
91-
} else if(!filledZ && zz !== prevZ) {
89+
} else if(!filledZ && z[i] !== firstZ) {
9290
filledZ = true;
9391
gridFill += 'z';
9492
}
9593
}
96-
// fill if not filled - case of having 1 dimension
94+
// fill if not filled - case of having dimension(s) with one item
9795
if(!filledX) gridFill += 'x';
9896
if(!filledY) gridFill += 'y';
9997
if(!filledZ) gridFill += 'z';
10098

99+
var valsx = distinctVals(trace.x.slice(0, len));
100+
var valsy = distinctVals(trace.y.slice(0, len));
101+
var valsz = distinctVals(trace.z.slice(0, len));
102+
103+
var getArray = function(c) { return c === 'x' ? x : c === 'y' ? y : z; };
104+
var getVals = function(c) { return c === 'x' ? valsx : c === 'y' ? valsy : valsz; };
105+
var getLength = function(c) { return getVals(c).length; };
106+
var getDir = function(c) { return (+(c[c.length - 1] - c[0])) * 2 + 1; };
107+
108+
var arrK = getArray(gridFill[0]);
109+
var arrJ = getArray(gridFill[1]);
110+
var arrI = getArray(gridFill[2]);
111+
var nk = getLength(gridFill[0]);
112+
var nj = getLength(gridFill[1]);
113+
var ni = getLength(gridFill[2]);
114+
115+
var arbitrary = false;
116+
117+
var getIndex = function(_i, _j, _k) {
118+
return nk * (nj * _i + _j) + _k;
119+
};
120+
121+
var dirK = getDir(getArray(gridFill[0]));
122+
var dirJ = getDir(getArray(gridFill[1]));
123+
var dirI = getDir(getArray(gridFill[2]));
124+
125+
for(i = 0; i < ni - 1; i++) {
126+
for(j = 0; j < nj - 1; j++) {
127+
for(k = 0; k < nk - 1; k++) {
128+
var q000 = getIndex(i, j, k);
129+
var q001 = getIndex(i, j, k + 1);
130+
var q010 = getIndex(i, j + 1, k);
131+
var q100 = getIndex(i + 1, j, k);
132+
133+
if(
134+
!(arrK[q000] * dirK < arrK[q001] * dirK) ||
135+
!(arrJ[q000] * dirJ < arrJ[q010] * dirJ) ||
136+
!(arrI[q000] * dirI < arrI[q100] * dirI)
137+
) {
138+
arbitrary = true;
139+
}
140+
141+
if(arbitrary) break;
142+
}
143+
if(arbitrary) break;
144+
}
145+
if(arbitrary) break;
146+
}
147+
148+
if(arbitrary) {
149+
Lib.warn('Encountered arbitrary coordinates! Unable to input data grid.');
150+
len = 0;
151+
}
152+
101153
for(i = 0; i < slen; i++) {
102154
var sx = startx[i];
103155
xMax = Math.max(xMax, sx);
@@ -118,5 +170,12 @@ module.exports = function calc(gd, trace) {
118170
trace._xbnds = [xMin, xMax];
119171
trace._ybnds = [yMin, yMax];
120172
trace._zbnds = [zMin, zMax];
173+
trace._valsx = valsx;
174+
trace._valsy = valsy;
175+
trace._valsz = valsz;
121176
trace._gridFill = gridFill;
122177
};
178+
179+
function distinctVals(col) {
180+
return Lib.distinctVals(col).vals;
181+
}

src/traces/streamtube/convert.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,6 @@ proto.handlePick = function(selection) {
6262
}
6363
};
6464

65-
function distinctVals(col) {
66-
return Lib.distinctVals(col).vals;
67-
}
68-
6965
function getDfltStartingPositions(vec) {
7066
var len = vec.length;
7167
var s;
@@ -108,9 +104,9 @@ function convert(scene, trace) {
108104
len
109105
);
110106

111-
var valsx = distinctVals(trace.x.slice(0, len));
112-
var valsy = distinctVals(trace.y.slice(0, len));
113-
var valsz = distinctVals(trace.z.slice(0, len));
107+
var valsx = trace._valsx;
108+
var valsy = trace._valsy;
109+
var valsz = trace._valsz;
114110

115111
// Over-specified mesh case, this would error in tube2mesh
116112
if(valsx.length * valsy.length * valsz.length > len) {

test/jasmine/tests/streamtube_test.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,108 @@ 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);
266+
}
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+
};
281+
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+
}
289+
290+
Plotly.plot(gd, fig).then(function() {
291+
_assert('with negative steps', {
292+
positionsLength: 6336,
293+
cellsLength: 2112
294+
});
295+
})
296+
.catch(failTest)
297+
.then(done);
298+
});
299+
300+
it('@gl should return blank mesh grid if encountered arbitrary coordinates', function(done) {
301+
var x = [];
302+
var y = [];
303+
var z = [];
304+
var u = [];
305+
var v = [];
306+
var w = [];
307+
308+
for(var i = 0; i < 3; i++) {
309+
for(var j = i; j < 4; j++) {
310+
for(var k = j; k < 5; k++) {
311+
x.push(i);
312+
y.push(j);
313+
z.push(k);
314+
u.push(1);
315+
v.push(1);
316+
w.push(1);
317+
}
318+
}
319+
}
320+
321+
var fig = {
322+
data: [{
323+
type: 'streamtube',
324+
x: x,
325+
y: y,
326+
z: z,
327+
u: u,
328+
v: v,
329+
w: w
330+
}]
331+
};
332+
333+
function _assert(msg, exp) {
334+
var scene = gd._fullLayout.scene._scene;
335+
var objs = scene.glplot.objects;
336+
expect(objs.length).toBe(1, 'one gl-vis object - ' + msg);
337+
expect(exp.positionsLength).toBe(objs[0].positions.length, 'positions length - ' + msg);
338+
expect(exp.cellsLength).toBe(objs[0].cells.length, 'cells length - ' + msg);
339+
}
340+
341+
Plotly.plot(gd, fig).then(function() {
342+
_assert('arbitrary coordinates', {
343+
positionsLength: 0,
344+
cellsLength: 0
345+
});
346+
})
347+
.catch(failTest)
348+
.then(done);
349+
});
350+
249351
it('@gl should add/clear gl objects correctly', function(done) {
250352
var fig = Lib.extendDeep({}, require('@mocks/gl3d_streamtube-simple.json'));
251353
var trace = Lib.extendDeep({}, fig.data[0]);

0 commit comments

Comments
 (0)