Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c7c0a4a

Browse files
committedJan 22, 2018
Split gl_plot_interact by two
1 parent e43507e commit c7c0a4a

File tree

2 files changed

+591
-564
lines changed

2 files changed

+591
-564
lines changed
 
Lines changed: 591 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,591 @@
1+
var d3 = require('d3');
2+
3+
var Plotly = require('@lib/index');
4+
var Plots = require('@src/plots/plots');
5+
var Lib = require('@src/lib');
6+
var Drawing = require('@src/components/drawing');
7+
8+
var createGraphDiv = require('../assets/create_graph_div');
9+
var destroyGraphDiv = require('../assets/destroy_graph_div');
10+
var fail = require('../assets/fail_test');
11+
var mouseEvent = require('../assets/mouse_event');
12+
var drag = require('../assets/drag');
13+
var selectButton = require('../assets/modebar_button');
14+
var delay = require('../assets/delay');
15+
var readPixel = require('../assets/read_pixel');
16+
17+
function countCanvases() {
18+
return d3.selectAll('canvas').size();
19+
}
20+
21+
describe('Test removal of gl contexts', function() {
22+
var gd;
23+
24+
beforeEach(function() {
25+
gd = createGraphDiv();
26+
});
27+
28+
afterEach(destroyGraphDiv);
29+
30+
it('Plots.cleanPlot should remove gl context from the graph div of a gl2d plot', function(done) {
31+
Plotly.plot(gd, [{
32+
type: 'scattergl',
33+
x: [1, 2, 3],
34+
y: [2, 1, 3]
35+
}])
36+
.then(function() {
37+
expect(gd._fullLayout._plots.xy._scene).toBeDefined();
38+
Plots.cleanPlot([], {}, gd._fullData, gd._fullLayout);
39+
40+
expect(gd._fullLayout._plots.xy._scene).toBeUndefined();
41+
})
42+
.then(done);
43+
});
44+
45+
it('Plotly.newPlot should remove gl context from the graph div of a gl2d plot', function(done) {
46+
var firstGlplotObject, firstGlContext, firstCanvas;
47+
48+
Plotly.plot(gd, [{
49+
type: 'scattergl',
50+
x: [1, 2, 3],
51+
y: [2, 1, 3]
52+
}])
53+
.then(function() {
54+
firstGlplotObject = gd._fullLayout._plots.xy._scene;
55+
firstGlContext = firstGlplotObject.scatter2d.gl;
56+
firstCanvas = firstGlContext.canvas;
57+
58+
expect(firstGlplotObject).toBeDefined();
59+
expect(firstGlContext).toBeDefined();
60+
expect(firstGlContext instanceof WebGLRenderingContext);
61+
62+
return Plotly.newPlot(gd, [{
63+
type: 'scattergl',
64+
x: [1, 2, 3],
65+
y: [2, 1, 3]
66+
}], {});
67+
})
68+
.then(function() {
69+
var secondGlplotObject = gd._fullLayout._plots.xy._scene;
70+
var secondGlContext = secondGlplotObject.scatter2d.gl;
71+
var secondCanvas = secondGlContext.canvas;
72+
73+
expect(Object.keys(gd._fullLayout._plots).length === 1);
74+
expect(secondGlplotObject).not.toBe(firstGlplotObject);
75+
expect(firstGlplotObject.gl === null);
76+
expect(secondGlContext instanceof WebGLRenderingContext);
77+
expect(secondGlContext).not.toBe(firstGlContext);
78+
79+
expect(
80+
firstCanvas.parentNode === null ||
81+
firstCanvas !== secondCanvas && firstGlContext.isContextLost()
82+
);
83+
})
84+
.then(done);
85+
});
86+
});
87+
88+
describe('Test gl plot side effects', function() {
89+
var gd;
90+
91+
beforeEach(function() {
92+
gd = createGraphDiv();
93+
});
94+
95+
afterEach(function() {
96+
Plotly.purge(gd);
97+
destroyGraphDiv();
98+
});
99+
100+
it('should not draw the rangeslider', function(done) {
101+
var data = [{
102+
x: [1, 2, 3],
103+
y: [2, 3, 4],
104+
type: 'scattergl'
105+
}, {
106+
x: [1, 2, 3],
107+
y: [2, 3, 4],
108+
type: 'scatter'
109+
}];
110+
111+
var layout = {
112+
xaxis: { rangeslider: { visible: true } }
113+
};
114+
115+
Plotly.plot(gd, data, layout).then(function() {
116+
var rangeSlider = document.getElementsByClassName('range-slider')[0];
117+
expect(rangeSlider).not.toBeDefined();
118+
})
119+
.then(done);
120+
});
121+
122+
it('should be able to replot from a blank graph', function(done) {
123+
124+
function countCanvases(cnt) {
125+
var nodes = d3.selectAll('canvas');
126+
expect(nodes.size()).toEqual(cnt);
127+
}
128+
129+
var data = [{
130+
type: 'scattergl',
131+
x: [1, 2, 3],
132+
y: [2, 1, 2]
133+
}];
134+
135+
Plotly.plot(gd, [])
136+
.then(function() {
137+
countCanvases(0);
138+
139+
return Plotly.plot(gd, data);
140+
})
141+
.then(function() {
142+
countCanvases(3);
143+
144+
return Plotly.purge(gd);
145+
})
146+
.then(function() {
147+
countCanvases(0);
148+
149+
return Plotly.plot(gd, data);
150+
})
151+
.then(function() {
152+
countCanvases(3);
153+
154+
return Plotly.deleteTraces(gd, [0]);
155+
})
156+
.then(function() {
157+
countCanvases(0);
158+
159+
return Plotly.purge(gd);
160+
})
161+
.then(done);
162+
});
163+
164+
it('should be able to switch trace type', function(done) {
165+
Plotly.newPlot(gd, [{
166+
type: 'parcoords',
167+
x: [1, 2, 3],
168+
y: [2, 1, 2],
169+
dimensions: [
170+
{
171+
constraintrange: [200, 700],
172+
label: 'Block height',
173+
values: [321, 534, 542, 674, 31, 674, 124, 246, 456, 743]
174+
}
175+
]
176+
}])
177+
.then(function() {
178+
expect(d3.selectAll('canvas').size()).toEqual(3);
179+
180+
return Plotly.restyle(gd, 'type', 'scatter');
181+
})
182+
.then(function() {
183+
expect(d3.selectAll('canvas').size()).toEqual(0);
184+
})
185+
.then(done);
186+
});
187+
});
188+
189+
describe('Test gl2d plots', function() {
190+
var gd;
191+
192+
var mock = require('@mocks/gl2d_10.json');
193+
194+
beforeEach(function() {
195+
jasmine.DEFAULT_TIMEOUT_INTERVAL = 4000;
196+
gd = createGraphDiv();
197+
});
198+
199+
afterEach(function() {
200+
Plotly.purge(gd);
201+
destroyGraphDiv();
202+
});
203+
204+
function mouseTo(p0, p1) {
205+
var node = d3.select('.nsewdrag[data-subplot="xy"]').node();
206+
var dx = p1[0] - p0[0];
207+
var dy = p1[1] - p0[1];
208+
return drag(node, dx, dy, null, p0[0], p0[1]);
209+
}
210+
211+
function select(path) {
212+
return new Promise(function(resolve) {
213+
gd.once('plotly_selected', resolve);
214+
215+
var len = path.length;
216+
217+
// do selection
218+
Lib.clearThrottle();
219+
mouseEvent('mousemove', path[0][0], path[0][1]);
220+
mouseEvent('mousedown', path[0][0], path[0][1]);
221+
222+
path.slice(1, len).forEach(function(pt) {
223+
Lib.clearThrottle();
224+
mouseEvent('mousemove', pt[0], pt[1]);
225+
});
226+
227+
mouseEvent('mouseup', path[len - 1][0], path[len - 1][1]);
228+
});
229+
}
230+
231+
it('should respond to drag interactions', function(done) {
232+
var _mock = Lib.extendDeep({}, mock);
233+
234+
var relayoutCallback = jasmine.createSpy('relayoutCallback');
235+
236+
var originalX = [-0.3037383177570093, 5.303738317757009];
237+
var originalY = [-0.5, 6.1];
238+
var newX = [-0.5, 5];
239+
var newY = [-1.7, 4.95];
240+
var precision = 1;
241+
242+
Plotly.newPlot(gd, _mock)
243+
.then(delay(20))
244+
.then(function() {
245+
expect(gd.layout.xaxis.autorange).toBe(true);
246+
expect(gd.layout.yaxis.autorange).toBe(true);
247+
expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
248+
expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
249+
250+
// Switch to pan mode
251+
var buttonPan = selectButton(gd._fullLayout._modeBar, 'pan2d');
252+
expect(buttonPan.isActive()).toBe(false, 'initially, zoom is active');
253+
buttonPan.click();
254+
expect(buttonPan.isActive()).toBe(true, 'switched on dragmode');
255+
256+
// Switching mode must not change visible range
257+
expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
258+
expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
259+
})
260+
.then(delay(200))
261+
.then(function() {
262+
gd.on('plotly_relayout', relayoutCallback);
263+
})
264+
.then(function() {
265+
// Drag scene along the X axis
266+
return mouseTo([200, 200], [220, 200]);
267+
})
268+
.then(function() {
269+
expect(gd.layout.xaxis.autorange).toBe(false);
270+
expect(gd.layout.yaxis.autorange).toBe(false);
271+
expect(gd.layout.xaxis.range).toBeCloseToArray(newX, precision);
272+
expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
273+
})
274+
.then(function() {
275+
// Drag scene back along the X axis
276+
return mouseTo([220, 200], [200, 200]);
277+
})
278+
.then(function() {
279+
expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
280+
expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
281+
})
282+
.then(function() {
283+
// Drag scene along the Y axis
284+
return mouseTo([200, 200], [200, 150]);
285+
})
286+
.then(function() {
287+
expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
288+
expect(gd.layout.yaxis.range).toBeCloseToArray(newY, precision);
289+
})
290+
.then(function() {
291+
// Drag scene back along the Y axis
292+
return mouseTo([200, 150], [200, 200]);
293+
})
294+
.then(function() {
295+
expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
296+
expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
297+
})
298+
.then(function() {
299+
// Drag scene along both the X and Y axis
300+
return mouseTo([200, 200], [220, 150]);
301+
})
302+
.then(function() {
303+
expect(gd.layout.xaxis.range).toBeCloseToArray(newX, precision);
304+
expect(gd.layout.yaxis.range).toBeCloseToArray(newY, precision);
305+
})
306+
.then(function() {
307+
// Drag scene back along the X and Y axis
308+
return mouseTo([220, 150], [200, 200]);
309+
})
310+
.then(function() {
311+
expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
312+
expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
313+
})
314+
.then(delay(200))
315+
.then(function() {
316+
// callback count expectation: X and back; Y and back; XY and back
317+
expect(relayoutCallback).toHaveBeenCalledTimes(6);
318+
319+
// a callback value structure and contents check
320+
expect(relayoutCallback).toHaveBeenCalledWith(jasmine.objectContaining({
321+
'xaxis.range[0]': jasmine.any(Number),
322+
'xaxis.range[1]': jasmine.any(Number),
323+
'yaxis.range[0]': jasmine.any(Number),
324+
'yaxis.range[1]': jasmine.any(Number)
325+
}));
326+
})
327+
.catch(fail)
328+
.then(done);
329+
});
330+
331+
it('should be able to toggle visibility', function(done) {
332+
var _mock = Lib.extendDeep({}, mock);
333+
_mock.data[0].line.width = 5;
334+
335+
Plotly.plot(gd, _mock)
336+
.then(delay(20))
337+
.then(function() {
338+
return Plotly.restyle(gd, 'visible', 'legendonly');
339+
})
340+
.then(function() {
341+
expect(gd.querySelector('.gl-canvas-context')).toBe(null);
342+
343+
return Plotly.restyle(gd, 'visible', true);
344+
})
345+
.then(function() {
346+
expect(readPixel(gd.querySelector('.gl-canvas-context'), 108, 100)[0]).not.toBe(0);
347+
348+
return Plotly.restyle(gd, 'visible', false);
349+
})
350+
.then(function() {
351+
expect(gd.querySelector('.gl-canvas-context')).toBe(null);
352+
353+
return Plotly.restyle(gd, 'visible', true);
354+
})
355+
.then(function() {
356+
expect(readPixel(gd.querySelector('.gl-canvas-context'), 108, 100)[0]).not.toBe(0);
357+
})
358+
.catch(fail)
359+
.then(done);
360+
});
361+
362+
it('should display selection of big number of points', function(done) {
363+
// generate large number of points
364+
var x = [], y = [], n = 2e2, N = n * n;
365+
for(var i = 0; i < N; i++) {
366+
x.push((i % n) / n);
367+
y.push(i / N);
368+
}
369+
370+
var mock = {
371+
data: [{
372+
x: x, y: y, type: 'scattergl', mode: 'markers'
373+
}],
374+
layout: {
375+
dragmode: 'select'
376+
}
377+
};
378+
379+
Plotly.plot(gd, mock)
380+
.then(delay(1000))
381+
.then(select([[160, 100], [180, 100]]))
382+
.then(delay(1000))
383+
.then(function() {
384+
expect(readPixel(gd.querySelector('.gl-canvas-context'), 168, 100)[3]).toBe(0);
385+
expect(readPixel(gd.querySelector('.gl-canvas-context'), 158, 100)[3]).not.toBe(0);
386+
expect(readPixel(gd.querySelector('.gl-canvas-focus'), 168, 100)[3]).not.toBe(0);
387+
})
388+
.catch(fail)
389+
.then(done);
390+
});
391+
392+
it('should be able to toggle from svg to gl', function(done) {
393+
Plotly.plot(gd, [{
394+
y: [1, 2, 1],
395+
}])
396+
.then(function() {
397+
expect(countCanvases()).toBe(0);
398+
expect(d3.selectAll('.scatterlayer > .trace').size()).toBe(1);
399+
400+
return Plotly.restyle(gd, 'type', 'scattergl');
401+
})
402+
.then(function() {
403+
expect(countCanvases()).toBe(3);
404+
expect(d3.selectAll('.scatterlayer > .trace').size()).toBe(0);
405+
406+
return Plotly.restyle(gd, 'type', 'scatter');
407+
})
408+
.then(function() {
409+
expect(countCanvases()).toBe(0);
410+
expect(d3.selectAll('.scatterlayer > .trace').size()).toBe(1);
411+
})
412+
.catch(fail)
413+
.then(done);
414+
});
415+
416+
it('supports 1D and 2D Zoom', function(done) {
417+
var centerX;
418+
var centerY;
419+
420+
Plotly.newPlot(gd, [{
421+
type: 'scattergl', x: [1, 15], y: [1, 15]
422+
}], {
423+
width: 400,
424+
height: 400,
425+
margin: {t: 100, b: 100, l: 100, r: 100},
426+
xaxis: {range: [0, 16]},
427+
yaxis: {range: [0, 16]}
428+
})
429+
.then(function() {
430+
var bBox = gd.getBoundingClientRect();
431+
centerX = bBox.left + 200;
432+
centerY = bBox.top + 200;
433+
434+
return mouseTo([centerX, centerY], [centerX - 5, centerY + 5]);
435+
})
436+
.then(function() {
437+
// no change - too small
438+
expect(gd.layout.xaxis.range).toBeCloseToArray([0, 16], 3);
439+
expect(gd.layout.yaxis.range).toBeCloseToArray([0, 16], 3);
440+
})
441+
.then(function() {
442+
return mouseTo([centerX - 50, centerY], [centerX + 50, centerY + 50]);
443+
})
444+
.then(function() {
445+
// 2D
446+
expect(gd.layout.xaxis.range).toBeCloseToArray([4, 12], 3);
447+
expect(gd.layout.yaxis.range).toBeCloseToArray([4, 8], 3);
448+
})
449+
.then(function() {
450+
return mouseTo([centerX - 50, centerY], [centerX, centerY + 5]);
451+
})
452+
.then(function() {
453+
// x only
454+
expect(gd.layout.xaxis.range).toBeCloseToArray([6, 8], 3);
455+
expect(gd.layout.yaxis.range).toBeCloseToArray([4, 8], 3);
456+
})
457+
.then(function() {
458+
return mouseTo([centerX, centerY - 50], [centerX - 5, centerY + 50]);
459+
})
460+
.then(function() {
461+
// y only
462+
expect(gd.layout.xaxis.range).toBeCloseToArray([6, 8], 3);
463+
expect(gd.layout.yaxis.range).toBeCloseToArray([5, 7], 3);
464+
})
465+
.catch(fail)
466+
.then(done);
467+
});
468+
469+
it('supports axis constraints with zoom', function(done) {
470+
var centerX;
471+
var centerY;
472+
473+
Plotly.newPlot(gd, [{
474+
type: 'scattergl', x: [1, 15], y: [1, 15]
475+
}], {
476+
width: 400,
477+
height: 400,
478+
margin: {t: 100, b: 100, l: 100, r: 100},
479+
xaxis: {range: [0, 16]},
480+
yaxis: {range: [0, 16]}
481+
})
482+
.then(function() {
483+
var bBox = gd.getBoundingClientRect();
484+
centerX = bBox.left + 200;
485+
centerY = bBox.top + 200;
486+
487+
return Plotly.relayout(gd, {
488+
'yaxis.scaleanchor': 'x',
489+
'yaxis.scaleratio': 2
490+
});
491+
})
492+
.then(function() {
493+
// x range is adjusted to fit constraint
494+
expect(gd.layout.xaxis.range).toBeCloseToArray([-8, 24], 3);
495+
expect(gd.layout.yaxis.range).toBeCloseToArray([0, 16], 3);
496+
})
497+
.then(function() {
498+
return mouseTo([centerX, centerY], [centerX - 5, centerY + 5]);
499+
})
500+
.then(function() {
501+
// no change - too small
502+
expect(gd.layout.xaxis.range).toBeCloseToArray([-8, 24], 3);
503+
expect(gd.layout.yaxis.range).toBeCloseToArray([0, 16], 3);
504+
})
505+
.then(function() {
506+
// now there should only be 2D zooming
507+
// dy>>dx
508+
return mouseTo([centerX, centerY], [centerX - 1, centerY - 50]);
509+
})
510+
.then(function() {
511+
expect(gd.layout.xaxis.range).toBeCloseToArray([0, 8], 3);
512+
expect(gd.layout.yaxis.range).toBeCloseToArray([8, 12], 3);
513+
})
514+
.then(function() {
515+
return mouseTo([centerX, centerY], [centerX + 50, centerY + 1]);
516+
})
517+
.then(function() {
518+
// dx>>dy
519+
expect(gd.layout.xaxis.range).toBeCloseToArray([4, 6], 3);
520+
expect(gd.layout.yaxis.range).toBeCloseToArray([9, 10], 3);
521+
})
522+
.then(function() {
523+
return Plotly.relayout(gd, {
524+
'xaxis.autorange': true,
525+
'yaxis.autorange': true
526+
});
527+
})
528+
.then(function() {
529+
expect(gd.layout.xaxis.range).toBeCloseToArray([-7.6, 23.6], 1);
530+
expect(gd.layout.yaxis.range).toBeCloseToArray([0.2, 15.8], 1);
531+
})
532+
.catch(fail)
533+
.then(done);
534+
});
535+
536+
it('should change plot type with incomplete data', function(done) {
537+
Plotly.plot(gd, [{}]);
538+
expect(function() {
539+
Plotly.restyle(gd, {type: 'scattergl', x: [[1]]}, 0);
540+
}).not.toThrow();
541+
542+
expect(function() {
543+
Plotly.restyle(gd, {y: [[1]]}, 0);
544+
}).not.toThrow();
545+
546+
done();
547+
});
548+
549+
it('data-referenced annotations should update on drag', function(done) {
550+
function assertAnnotation(xy) {
551+
var ann = d3.select('g.annotation-text-g').select('g');
552+
var translate = Drawing.getTranslate(ann);
553+
554+
expect(translate.x).toBeWithin(xy[0], 8);
555+
expect(translate.y).toBeWithin(xy[1], 8);
556+
}
557+
558+
Plotly.newPlot(gd, [{
559+
type: 'scattergl',
560+
x: [1, 2, 3],
561+
y: [2, 1, 2]
562+
}], {
563+
annotations: [{
564+
x: 2,
565+
y: 1,
566+
text: 'text'
567+
}],
568+
dragmode: 'pan'
569+
})
570+
.then(function() {
571+
assertAnnotation([327, 312]);
572+
})
573+
.then(function() {
574+
return mouseTo([250, 200], [200, 150]);
575+
})
576+
.then(function() {
577+
assertAnnotation([277, 262]);
578+
})
579+
.then(function() {
580+
return Plotly.relayout(gd, {
581+
'xaxis.range': [1.5, 2.5],
582+
'yaxis.range': [1, 1.5]
583+
});
584+
})
585+
.then(function() {
586+
assertAnnotation([327, 331]);
587+
})
588+
.catch(fail)
589+
.then(done);
590+
});
591+
});

‎test/jasmine/tests/gl_plot_interact_test.js renamed to ‎test/jasmine/tests/gl3d_plot_interact_test.js

Lines changed: 0 additions & 564 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ var createGraphDiv = require('../assets/create_graph_div');
99
var destroyGraphDiv = require('../assets/destroy_graph_div');
1010
var fail = require('../assets/fail_test');
1111
var mouseEvent = require('../assets/mouse_event');
12-
var drag = require('../assets/drag');
1312
var selectButton = require('../assets/modebar_button');
1413
var delay = require('../assets/delay');
15-
var readPixel = require('../assets/read_pixel');
1614

1715
var customAssertions = require('../assets/custom_assertions');
1816
var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle;
@@ -1239,21 +1237,6 @@ describe('Test removal of gl contexts', function() {
12391237
.then(done);
12401238
});
12411239

1242-
it('Plots.cleanPlot should remove gl context from the graph div of a gl2d plot', function(done) {
1243-
Plotly.plot(gd, [{
1244-
type: 'scattergl',
1245-
x: [1, 2, 3],
1246-
y: [2, 1, 3]
1247-
}])
1248-
.then(function() {
1249-
expect(gd._fullLayout._plots.xy._scene).toBeDefined();
1250-
Plots.cleanPlot([], {}, gd._fullData, gd._fullLayout);
1251-
1252-
expect(gd._fullLayout._plots.xy._scene).toBeUndefined();
1253-
})
1254-
.then(done);
1255-
});
1256-
12571240
it('Plotly.newPlot should remove gl context from the graph div of a gl3d plot', function(done) {
12581241
var firstGlplotObject, firstGlContext, firstCanvas;
12591242

@@ -1297,551 +1280,4 @@ describe('Test removal of gl contexts', function() {
12971280
})
12981281
.then(done);
12991282
});
1300-
1301-
it('Plotly.newPlot should remove gl context from the graph div of a gl2d plot', function(done) {
1302-
var firstGlplotObject, firstGlContext, firstCanvas;
1303-
1304-
Plotly.plot(gd, [{
1305-
type: 'scattergl',
1306-
x: [1, 2, 3],
1307-
y: [2, 1, 3]
1308-
}])
1309-
.then(function() {
1310-
firstGlplotObject = gd._fullLayout._plots.xy._scene;
1311-
firstGlContext = firstGlplotObject.scatter2d.gl;
1312-
firstCanvas = firstGlContext.canvas;
1313-
1314-
expect(firstGlplotObject).toBeDefined();
1315-
expect(firstGlContext).toBeDefined();
1316-
expect(firstGlContext instanceof WebGLRenderingContext);
1317-
1318-
return Plotly.newPlot(gd, [{
1319-
type: 'scattergl',
1320-
x: [1, 2, 3],
1321-
y: [2, 1, 3]
1322-
}], {});
1323-
})
1324-
.then(function() {
1325-
var secondGlplotObject = gd._fullLayout._plots.xy._scene;
1326-
var secondGlContext = secondGlplotObject.scatter2d.gl;
1327-
var secondCanvas = secondGlContext.canvas;
1328-
1329-
expect(Object.keys(gd._fullLayout._plots).length === 1);
1330-
expect(secondGlplotObject).not.toBe(firstGlplotObject);
1331-
expect(firstGlplotObject.gl === null);
1332-
expect(secondGlContext instanceof WebGLRenderingContext);
1333-
expect(secondGlContext).not.toBe(firstGlContext);
1334-
1335-
expect(
1336-
firstCanvas.parentNode === null ||
1337-
firstCanvas !== secondCanvas && firstGlContext.isContextLost()
1338-
);
1339-
})
1340-
.then(done);
1341-
});
1342-
});
1343-
1344-
describe('Test gl plot side effects', function() {
1345-
var gd;
1346-
1347-
beforeEach(function() {
1348-
gd = createGraphDiv();
1349-
});
1350-
1351-
afterEach(function() {
1352-
Plotly.purge(gd);
1353-
destroyGraphDiv();
1354-
});
1355-
1356-
it('should not draw the rangeslider', function(done) {
1357-
var data = [{
1358-
x: [1, 2, 3],
1359-
y: [2, 3, 4],
1360-
type: 'scattergl'
1361-
}, {
1362-
x: [1, 2, 3],
1363-
y: [2, 3, 4],
1364-
type: 'scatter'
1365-
}];
1366-
1367-
var layout = {
1368-
xaxis: { rangeslider: { visible: true } }
1369-
};
1370-
1371-
Plotly.plot(gd, data, layout).then(function() {
1372-
var rangeSlider = document.getElementsByClassName('range-slider')[0];
1373-
expect(rangeSlider).not.toBeDefined();
1374-
})
1375-
.then(done);
1376-
});
1377-
1378-
it('should be able to replot from a blank graph', function(done) {
1379-
1380-
function countCanvases(cnt) {
1381-
var nodes = d3.selectAll('canvas');
1382-
expect(nodes.size()).toEqual(cnt);
1383-
}
1384-
1385-
var data = [{
1386-
type: 'scattergl',
1387-
x: [1, 2, 3],
1388-
y: [2, 1, 2]
1389-
}];
1390-
1391-
Plotly.plot(gd, [])
1392-
.then(function() {
1393-
countCanvases(0);
1394-
1395-
return Plotly.plot(gd, data);
1396-
})
1397-
.then(function() {
1398-
countCanvases(3);
1399-
1400-
return Plotly.purge(gd);
1401-
})
1402-
.then(function() {
1403-
countCanvases(0);
1404-
1405-
return Plotly.plot(gd, data);
1406-
})
1407-
.then(function() {
1408-
countCanvases(3);
1409-
1410-
return Plotly.deleteTraces(gd, [0]);
1411-
})
1412-
.then(function() {
1413-
countCanvases(0);
1414-
1415-
return Plotly.purge(gd);
1416-
})
1417-
.then(done);
1418-
});
1419-
1420-
it('should be able to switch trace type', function(done) {
1421-
Plotly.newPlot(gd, [{
1422-
type: 'parcoords',
1423-
x: [1, 2, 3],
1424-
y: [2, 1, 2],
1425-
dimensions: [
1426-
{
1427-
constraintrange: [200, 700],
1428-
label: 'Block height',
1429-
values: [321, 534, 542, 674, 31, 674, 124, 246, 456, 743]
1430-
}
1431-
]
1432-
}])
1433-
.then(function() {
1434-
expect(d3.selectAll('canvas').size()).toEqual(3);
1435-
1436-
return Plotly.restyle(gd, 'type', 'scatter');
1437-
})
1438-
.then(function() {
1439-
expect(d3.selectAll('canvas').size()).toEqual(0);
1440-
})
1441-
.then(done);
1442-
});
1443-
});
1444-
1445-
describe('Test gl2d plots', function() {
1446-
var gd;
1447-
1448-
var mock = require('@mocks/gl2d_10.json');
1449-
1450-
beforeEach(function() {
1451-
jasmine.DEFAULT_TIMEOUT_INTERVAL = 4000;
1452-
gd = createGraphDiv();
1453-
});
1454-
1455-
afterEach(function() {
1456-
Plotly.purge(gd);
1457-
destroyGraphDiv();
1458-
});
1459-
1460-
function mouseTo(p0, p1) {
1461-
var node = d3.select('.nsewdrag[data-subplot="xy"]').node();
1462-
var dx = p1[0] - p0[0];
1463-
var dy = p1[1] - p0[1];
1464-
return drag(node, dx, dy, null, p0[0], p0[1]);
1465-
}
1466-
1467-
function select(path) {
1468-
return new Promise(function(resolve) {
1469-
gd.once('plotly_selected', resolve);
1470-
1471-
var len = path.length;
1472-
1473-
// do selection
1474-
Lib.clearThrottle();
1475-
mouseEvent('mousemove', path[0][0], path[0][1]);
1476-
mouseEvent('mousedown', path[0][0], path[0][1]);
1477-
1478-
path.slice(1, len).forEach(function(pt) {
1479-
Lib.clearThrottle();
1480-
mouseEvent('mousemove', pt[0], pt[1]);
1481-
});
1482-
1483-
mouseEvent('mouseup', path[len - 1][0], path[len - 1][1]);
1484-
});
1485-
}
1486-
1487-
it('should respond to drag interactions', function(done) {
1488-
var _mock = Lib.extendDeep({}, mock);
1489-
1490-
var relayoutCallback = jasmine.createSpy('relayoutCallback');
1491-
1492-
var originalX = [-0.3037383177570093, 5.303738317757009];
1493-
var originalY = [-0.5, 6.1];
1494-
var newX = [-0.5, 5];
1495-
var newY = [-1.7, 4.95];
1496-
var precision = 1;
1497-
1498-
Plotly.newPlot(gd, _mock)
1499-
.then(delay(20))
1500-
.then(function() {
1501-
expect(gd.layout.xaxis.autorange).toBe(true);
1502-
expect(gd.layout.yaxis.autorange).toBe(true);
1503-
expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
1504-
expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
1505-
1506-
// Switch to pan mode
1507-
var buttonPan = selectButton(gd._fullLayout._modeBar, 'pan2d');
1508-
expect(buttonPan.isActive()).toBe(false, 'initially, zoom is active');
1509-
buttonPan.click();
1510-
expect(buttonPan.isActive()).toBe(true, 'switched on dragmode');
1511-
1512-
// Switching mode must not change visible range
1513-
expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
1514-
expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
1515-
})
1516-
.then(delay(200))
1517-
.then(function() {
1518-
gd.on('plotly_relayout', relayoutCallback);
1519-
})
1520-
.then(function() {
1521-
// Drag scene along the X axis
1522-
return mouseTo([200, 200], [220, 200]);
1523-
})
1524-
.then(function() {
1525-
expect(gd.layout.xaxis.autorange).toBe(false);
1526-
expect(gd.layout.yaxis.autorange).toBe(false);
1527-
expect(gd.layout.xaxis.range).toBeCloseToArray(newX, precision);
1528-
expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
1529-
})
1530-
.then(function() {
1531-
// Drag scene back along the X axis
1532-
return mouseTo([220, 200], [200, 200]);
1533-
})
1534-
.then(function() {
1535-
expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
1536-
expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
1537-
})
1538-
.then(function() {
1539-
// Drag scene along the Y axis
1540-
return mouseTo([200, 200], [200, 150]);
1541-
})
1542-
.then(function() {
1543-
expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
1544-
expect(gd.layout.yaxis.range).toBeCloseToArray(newY, precision);
1545-
})
1546-
.then(function() {
1547-
// Drag scene back along the Y axis
1548-
return mouseTo([200, 150], [200, 200]);
1549-
})
1550-
.then(function() {
1551-
expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
1552-
expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
1553-
})
1554-
.then(function() {
1555-
// Drag scene along both the X and Y axis
1556-
return mouseTo([200, 200], [220, 150]);
1557-
})
1558-
.then(function() {
1559-
expect(gd.layout.xaxis.range).toBeCloseToArray(newX, precision);
1560-
expect(gd.layout.yaxis.range).toBeCloseToArray(newY, precision);
1561-
})
1562-
.then(function() {
1563-
// Drag scene back along the X and Y axis
1564-
return mouseTo([220, 150], [200, 200]);
1565-
})
1566-
.then(function() {
1567-
expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
1568-
expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
1569-
})
1570-
.then(delay(200))
1571-
.then(function() {
1572-
// callback count expectation: X and back; Y and back; XY and back
1573-
expect(relayoutCallback).toHaveBeenCalledTimes(6);
1574-
1575-
// a callback value structure and contents check
1576-
expect(relayoutCallback).toHaveBeenCalledWith(jasmine.objectContaining({
1577-
'xaxis.range[0]': jasmine.any(Number),
1578-
'xaxis.range[1]': jasmine.any(Number),
1579-
'yaxis.range[0]': jasmine.any(Number),
1580-
'yaxis.range[1]': jasmine.any(Number)
1581-
}));
1582-
})
1583-
.catch(fail)
1584-
.then(done);
1585-
});
1586-
1587-
it('should be able to toggle visibility', function(done) {
1588-
var _mock = Lib.extendDeep({}, mock);
1589-
_mock.data[0].line.width = 5;
1590-
1591-
Plotly.plot(gd, _mock)
1592-
.then(delay(20))
1593-
.then(function() {
1594-
return Plotly.restyle(gd, 'visible', 'legendonly');
1595-
})
1596-
.then(function() {
1597-
expect(gd.querySelector('.gl-canvas-context')).toBe(null);
1598-
1599-
return Plotly.restyle(gd, 'visible', true);
1600-
})
1601-
.then(function() {
1602-
expect(readPixel(gd.querySelector('.gl-canvas-context'), 108, 100)[0]).not.toBe(0);
1603-
1604-
return Plotly.restyle(gd, 'visible', false);
1605-
})
1606-
.then(function() {
1607-
expect(gd.querySelector('.gl-canvas-context')).toBe(null);
1608-
1609-
return Plotly.restyle(gd, 'visible', true);
1610-
})
1611-
.then(function() {
1612-
expect(readPixel(gd.querySelector('.gl-canvas-context'), 108, 100)[0]).not.toBe(0);
1613-
})
1614-
.catch(fail)
1615-
.then(done);
1616-
});
1617-
1618-
it('should display selection of big number of points', function(done) {
1619-
// generate large number of points
1620-
var x = [], y = [], n = 2e2, N = n * n;
1621-
for(var i = 0; i < N; i++) {
1622-
x.push((i % n) / n);
1623-
y.push(i / N);
1624-
}
1625-
1626-
var mock = {
1627-
data: [{
1628-
x: x, y: y, type: 'scattergl', mode: 'markers'
1629-
}],
1630-
layout: {
1631-
dragmode: 'select'
1632-
}
1633-
};
1634-
1635-
Plotly.plot(gd, mock)
1636-
.then(delay(1000))
1637-
.then(select([[160, 100], [180, 100]]))
1638-
.then(delay(1000))
1639-
.then(function() {
1640-
expect(readPixel(gd.querySelector('.gl-canvas-context'), 168, 100)[3]).toBe(0);
1641-
expect(readPixel(gd.querySelector('.gl-canvas-context'), 158, 100)[3]).not.toBe(0);
1642-
expect(readPixel(gd.querySelector('.gl-canvas-focus'), 168, 100)[3]).not.toBe(0);
1643-
})
1644-
.catch(fail)
1645-
.then(done);
1646-
});
1647-
1648-
it('should be able to toggle from svg to gl', function(done) {
1649-
Plotly.plot(gd, [{
1650-
y: [1, 2, 1],
1651-
}])
1652-
.then(function() {
1653-
expect(countCanvases()).toBe(0);
1654-
expect(d3.selectAll('.scatterlayer > .trace').size()).toBe(1);
1655-
1656-
return Plotly.restyle(gd, 'type', 'scattergl');
1657-
})
1658-
.then(function() {
1659-
expect(countCanvases()).toBe(3);
1660-
expect(d3.selectAll('.scatterlayer > .trace').size()).toBe(0);
1661-
1662-
return Plotly.restyle(gd, 'type', 'scatter');
1663-
})
1664-
.then(function() {
1665-
expect(countCanvases()).toBe(0);
1666-
expect(d3.selectAll('.scatterlayer > .trace').size()).toBe(1);
1667-
})
1668-
.catch(fail)
1669-
.then(done);
1670-
});
1671-
1672-
it('supports 1D and 2D Zoom', function(done) {
1673-
var centerX;
1674-
var centerY;
1675-
1676-
Plotly.newPlot(gd, [{
1677-
type: 'scattergl', x: [1, 15], y: [1, 15]
1678-
}], {
1679-
width: 400,
1680-
height: 400,
1681-
margin: {t: 100, b: 100, l: 100, r: 100},
1682-
xaxis: {range: [0, 16]},
1683-
yaxis: {range: [0, 16]}
1684-
})
1685-
.then(function() {
1686-
var bBox = gd.getBoundingClientRect();
1687-
centerX = bBox.left + 200;
1688-
centerY = bBox.top + 200;
1689-
1690-
return mouseTo([centerX, centerY], [centerX - 5, centerY + 5]);
1691-
})
1692-
.then(function() {
1693-
// no change - too small
1694-
expect(gd.layout.xaxis.range).toBeCloseToArray([0, 16], 3);
1695-
expect(gd.layout.yaxis.range).toBeCloseToArray([0, 16], 3);
1696-
})
1697-
.then(function() {
1698-
return mouseTo([centerX - 50, centerY], [centerX + 50, centerY + 50]);
1699-
})
1700-
.then(function() {
1701-
// 2D
1702-
expect(gd.layout.xaxis.range).toBeCloseToArray([4, 12], 3);
1703-
expect(gd.layout.yaxis.range).toBeCloseToArray([4, 8], 3);
1704-
})
1705-
.then(function() {
1706-
return mouseTo([centerX - 50, centerY], [centerX, centerY + 5]);
1707-
})
1708-
.then(function() {
1709-
// x only
1710-
expect(gd.layout.xaxis.range).toBeCloseToArray([6, 8], 3);
1711-
expect(gd.layout.yaxis.range).toBeCloseToArray([4, 8], 3);
1712-
})
1713-
.then(function() {
1714-
return mouseTo([centerX, centerY - 50], [centerX - 5, centerY + 50]);
1715-
})
1716-
.then(function() {
1717-
// y only
1718-
expect(gd.layout.xaxis.range).toBeCloseToArray([6, 8], 3);
1719-
expect(gd.layout.yaxis.range).toBeCloseToArray([5, 7], 3);
1720-
})
1721-
.catch(fail)
1722-
.then(done);
1723-
});
1724-
1725-
it('supports axis constraints with zoom', function(done) {
1726-
var centerX;
1727-
var centerY;
1728-
1729-
Plotly.newPlot(gd, [{
1730-
type: 'scattergl', x: [1, 15], y: [1, 15]
1731-
}], {
1732-
width: 400,
1733-
height: 400,
1734-
margin: {t: 100, b: 100, l: 100, r: 100},
1735-
xaxis: {range: [0, 16]},
1736-
yaxis: {range: [0, 16]}
1737-
})
1738-
.then(function() {
1739-
var bBox = gd.getBoundingClientRect();
1740-
centerX = bBox.left + 200;
1741-
centerY = bBox.top + 200;
1742-
1743-
return Plotly.relayout(gd, {
1744-
'yaxis.scaleanchor': 'x',
1745-
'yaxis.scaleratio': 2
1746-
});
1747-
})
1748-
.then(function() {
1749-
// x range is adjusted to fit constraint
1750-
expect(gd.layout.xaxis.range).toBeCloseToArray([-8, 24], 3);
1751-
expect(gd.layout.yaxis.range).toBeCloseToArray([0, 16], 3);
1752-
})
1753-
.then(function() {
1754-
return mouseTo([centerX, centerY], [centerX - 5, centerY + 5]);
1755-
})
1756-
.then(function() {
1757-
// no change - too small
1758-
expect(gd.layout.xaxis.range).toBeCloseToArray([-8, 24], 3);
1759-
expect(gd.layout.yaxis.range).toBeCloseToArray([0, 16], 3);
1760-
})
1761-
.then(function() {
1762-
// now there should only be 2D zooming
1763-
// dy>>dx
1764-
return mouseTo([centerX, centerY], [centerX - 1, centerY - 50]);
1765-
})
1766-
.then(function() {
1767-
expect(gd.layout.xaxis.range).toBeCloseToArray([0, 8], 3);
1768-
expect(gd.layout.yaxis.range).toBeCloseToArray([8, 12], 3);
1769-
})
1770-
.then(function() {
1771-
return mouseTo([centerX, centerY], [centerX + 50, centerY + 1]);
1772-
})
1773-
.then(function() {
1774-
// dx>>dy
1775-
expect(gd.layout.xaxis.range).toBeCloseToArray([4, 6], 3);
1776-
expect(gd.layout.yaxis.range).toBeCloseToArray([9, 10], 3);
1777-
})
1778-
.then(function() {
1779-
return Plotly.relayout(gd, {
1780-
'xaxis.autorange': true,
1781-
'yaxis.autorange': true
1782-
});
1783-
})
1784-
.then(function() {
1785-
expect(gd.layout.xaxis.range).toBeCloseToArray([-7.6, 23.6], 1);
1786-
expect(gd.layout.yaxis.range).toBeCloseToArray([0.2, 15.8], 1);
1787-
})
1788-
.catch(fail)
1789-
.then(done);
1790-
});
1791-
1792-
it('should change plot type with incomplete data', function(done) {
1793-
Plotly.plot(gd, [{}]);
1794-
expect(function() {
1795-
Plotly.restyle(gd, {type: 'scattergl', x: [[1]]}, 0);
1796-
}).not.toThrow();
1797-
1798-
expect(function() {
1799-
Plotly.restyle(gd, {y: [[1]]}, 0);
1800-
}).not.toThrow();
1801-
1802-
done();
1803-
});
1804-
1805-
it('data-referenced annotations should update on drag', function(done) {
1806-
function assertAnnotation(xy) {
1807-
var ann = d3.select('g.annotation-text-g').select('g');
1808-
var translate = Drawing.getTranslate(ann);
1809-
1810-
expect(translate.x).toBeWithin(xy[0], 8);
1811-
expect(translate.y).toBeWithin(xy[1], 8);
1812-
}
1813-
1814-
Plotly.newPlot(gd, [{
1815-
type: 'scattergl',
1816-
x: [1, 2, 3],
1817-
y: [2, 1, 2]
1818-
}], {
1819-
annotations: [{
1820-
x: 2,
1821-
y: 1,
1822-
text: 'text'
1823-
}],
1824-
dragmode: 'pan'
1825-
})
1826-
.then(function() {
1827-
assertAnnotation([327, 312]);
1828-
})
1829-
.then(function() {
1830-
return mouseTo([250, 200], [200, 150]);
1831-
})
1832-
.then(function() {
1833-
assertAnnotation([277, 262]);
1834-
})
1835-
.then(function() {
1836-
return Plotly.relayout(gd, {
1837-
'xaxis.range': [1.5, 2.5],
1838-
'yaxis.range': [1, 1.5]
1839-
});
1840-
})
1841-
.then(function() {
1842-
assertAnnotation([327, 331]);
1843-
})
1844-
.catch(fail)
1845-
.then(done);
1846-
});
18471283
});

0 commit comments

Comments
 (0)
Please sign in to comment.