Skip to content

Commit fae0803

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 050c0cb + b5eed7e commit fae0803

File tree

5 files changed

+179
-74
lines changed

5 files changed

+179
-74
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"test-syntax": "node test/syntax_test.js",
3636
"test-bundle": "node tasks/test_bundle.js",
3737
"test": "npm run citest-jasmine && npm run test-image && npm run test-syntax && npm run test-bundle",
38+
"start": "npm run start-test_dashboard",
3839
"start-test_dashboard": "node devtools/test_dashboard/server.js",
3940
"start-image_viewer": "node devtools/image_viewer/server.js",
4041
"baseline": "./tasks/baseline.sh",

src/components/shapes/index.js

Lines changed: 79 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ function linearToData(ax) { return ax.type === 'category' ? ax.l2c : ax.l2d; }
8989

9090
shapes.drawAll = function(gd) {
9191
var fullLayout = gd._fullLayout;
92-
fullLayout._shapelayer.selectAll('path').remove();
9392
for(var i = 0; i < fullLayout.shapes.length; i++) {
9493
shapes.draw(gd, i);
9594
}
@@ -115,86 +114,106 @@ shapes.add = function(gd) {
115114
// if opt is blank, val can be 'add' or a full options object to add a new
116115
// annotation at that point in the array, or 'remove' to delete this one
117116
shapes.draw = function(gd, index, opt, value) {
118-
var layout = gd.layout,
119-
fullLayout = gd._fullLayout,
120-
i;
121-
122-
// TODO: abstract out these drawAll, add, and remove blocks for shapes and annotations
123117
if(!isNumeric(index) || index===-1) {
124118
// no index provided - we're operating on ALL shapes
125119
if(!index && Array.isArray(value)) {
126-
// a whole annotation array is passed in
127-
// (as in, redo of delete all)
128-
layout.shapes = value;
129-
shapes.supplyLayoutDefaults(layout, fullLayout);
130-
shapes.drawAll(gd);
120+
replaceAllShapes(gd, value);
131121
return;
132122
}
133123
else if(value==='remove') {
134-
// delete all
135-
delete layout.shapes;
136-
fullLayout.shapes = [];
137-
shapes.drawAll(gd);
124+
deleteAllShapes(gd);
138125
return;
139126
}
140127
else if(opt && value!=='add') {
141-
// make the same change to all shapes
142-
for(i = 0; i < fullLayout.shapes.length; i++) {
143-
shapes.draw(gd, i, opt, value);
144-
}
128+
updateAllShapes(gd, opt, value);
145129
return;
146130
}
147131
else {
148132
// add a new empty annotation
149-
index = fullLayout.shapes.length;
150-
fullLayout.shapes.push({});
133+
index = gd._fullLayout.shapes.length;
134+
gd._fullLayout.shapes.push({});
151135
}
152136
}
153137

154138
if(!opt && value) {
155139
if(value==='remove') {
156-
fullLayout._shapelayer.selectAll('[data-index="'+index+'"]')
157-
.remove();
158-
fullLayout.shapes.splice(index,1);
159-
layout.shapes.splice(index,1);
160-
for(i=index; i<fullLayout.shapes.length; i++) {
161-
fullLayout._shapelayer
162-
.selectAll('[data-index="'+(i+1)+'"]')
163-
.attr('data-index',String(i));
164-
165-
// redraw all shapes past the removed one,
166-
// so they bind to the right events
167-
shapes.draw(gd,i);
168-
}
140+
deleteShape(gd, index);
169141
return;
170142
}
171143
else if(value==='add' || Plotly.Lib.isPlainObject(value)) {
172-
fullLayout.shapes.splice(index,0,{});
144+
insertShape(gd, index, value);
145+
}
146+
}
173147

174-
var rule = Plotly.Lib.isPlainObject(value) ?
175-
Plotly.Lib.extendFlat({}, value) :
176-
{text: 'New text'};
148+
updateShape(gd, index, opt, value);
149+
};
177150

178-
if(layout.shapes) {
179-
layout.shapes.splice(index, 0, rule);
180-
} else {
181-
layout.shapes = [rule];
182-
}
151+
function replaceAllShapes(gd, newShapes) {
152+
gd.layout.shapes = newShapes;
153+
shapes.supplyLayoutDefaults(gd.layout, gd._fullLayout);
154+
shapes.drawAll(gd);
155+
}
183156

184-
for(i=fullLayout.shapes.length-1; i>index; i--) {
185-
fullLayout._shapelayer
186-
.selectAll('[data-index="'+(i-1)+'"]')
187-
.attr('data-index',String(i));
188-
shapes.draw(gd,i);
189-
}
190-
}
157+
function deleteAllShapes(gd) {
158+
delete gd.layout.shapes;
159+
gd._fullLayout.shapes = [];
160+
shapes.drawAll(gd);
161+
}
162+
163+
function updateAllShapes(gd, opt, value) {
164+
for(var i = 0; i < gd._fullLayout.shapes.length; i++) {
165+
shapes.draw(gd, i, opt, value);
166+
}
167+
}
168+
169+
function deleteShape(gd, index) {
170+
gd._fullLayout._shapelayer.selectAll('[data-index="' + index + '"]')
171+
.remove();
172+
173+
gd._fullLayout.shapes.splice(index, 1);
174+
175+
gd.layout.shapes.splice(index, 1);
176+
177+
for(var i = index; i < gd._fullLayout.shapes.length; i++) {
178+
// redraw all shapes past the removed one,
179+
// so they bind to the right events
180+
gd._fullLayout._shapelayer
181+
.selectAll('[data-index="' + (i+1) + '"]')
182+
.attr('data-index', String(i));
183+
shapes.draw(gd, i);
184+
}
185+
}
186+
187+
function insertShape(gd, index, newShape) {
188+
gd._fullLayout.shapes.splice(index, 0, {});
189+
190+
var rule = Plotly.Lib.isPlainObject(newShape) ?
191+
Plotly.Lib.extendFlat({}, newShape) :
192+
{text: 'New text'};
193+
194+
if(gd.layout.shapes) {
195+
gd.layout.shapes.splice(index, 0, rule);
196+
} else {
197+
gd.layout.shapes = [rule];
191198
}
192199

200+
for(var i = gd._fullLayout.shapes.length - 1; i > index; i--) {
201+
gd._fullLayout._shapelayer
202+
.selectAll('[data-index="' + (i - 1) + '"]')
203+
.attr('data-index', String(i));
204+
shapes.draw(gd, i);
205+
}
206+
}
207+
208+
function updateShape(gd, index, opt, value) {
209+
var i;
210+
193211
// remove the existing shape if there is one
194-
fullLayout._shapelayer.selectAll('[data-index="'+index+'"]').remove();
212+
gd._fullLayout._shapelayer.selectAll('[data-index="' + index + '"]')
213+
.remove();
195214

196215
// remember a few things about what was already there,
197-
var optionsIn = layout.shapes[index];
216+
var optionsIn = gd.layout.shapes[index];
198217

199218
// (from annos...) not sure how we're getting here... but C12 is seeing a bug
200219
// where we fail here when they add/remove annotations
@@ -261,8 +280,8 @@ shapes.draw = function(gd, index, opt, value) {
261280
optionsIn[posAttr] = position;
262281
}
263282

264-
var options = handleShapeDefaults(optionsIn, fullLayout);
265-
fullLayout.shapes[index] = options;
283+
var options = handleShapeDefaults(optionsIn, gd._fullLayout);
284+
gd._fullLayout.shapes[index] = options;
266285

267286
var attrs = {
268287
'data-index': String(index),
@@ -273,15 +292,18 @@ shapes.draw = function(gd, index, opt, value) {
273292

274293
var lineColor = options.line.width ? options.line.color : 'rgba(0,0,0,0)';
275294

276-
var path = fullLayout._shapelayer.append('path')
295+
var path = gd._fullLayout._shapelayer.append('path')
277296
.attr(attrs)
278297
.style('opacity', options.opacity)
279298
.call(Plotly.Color.stroke, lineColor)
280299
.call(Plotly.Color.fill, options.fillcolor)
281300
.call(Plotly.Drawing.dashLine, options.line.dash, options.line.width);
282301

283-
if(clipAxes) path.call(Plotly.Drawing.setClipUrl, 'clip' + fullLayout._uid + clipAxes);
284-
};
302+
if(clipAxes) {
303+
path.call(Plotly.Drawing.setClipUrl,
304+
'clip' + gd._fullLayout._uid + clipAxes);
305+
}
306+
}
285307

286308
function decodeDate(convertToPx) {
287309
return function(v) { return convertToPx(v.replace('_', ' ')); };

src/traces/scatter3d/convert.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,15 +432,15 @@ proto.dispose = function() {
432432
this.scatterPlot.dispose();
433433
}
434434
if(this.errorBars) {
435-
this.scene.remove(this.errorBars);
435+
this.scene.glplot.remove(this.errorBars);
436436
this.errorBars.dispose();
437437
}
438438
if(this.textMarkers) {
439439
this.scene.glplot.remove(this.textMarkers);
440440
this.textMarkers.dispose();
441441
}
442442
if(this.delaunayMesh) {
443-
this.scene.glplot.remove(this.textMarkers);
443+
this.scene.glplot.remove(this.delaunayMesh);
444444
this.delaunayMesh.dispose();
445445
}
446446
};

test/jasmine/tests/gl_plot_interact_test.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,17 @@ describe('Test gl plot interactions', function() {
7373

7474
beforeEach(function(done) {
7575
gd = createGraphDiv();
76-
Plotly.plot(gd, mock.data, mock.layout).then(function() {
76+
77+
var mockCopy = Lib.extendDeep({}, mock);
78+
79+
// lines, markers, text, error bars and surfaces each
80+
// correspond to one glplot object
81+
mockCopy.data[0].mode = 'lines+markers+text';
82+
mockCopy.data[0].error_z = { value: 10 };
83+
mockCopy.data[0].surfaceaxis = 2;
84+
mockCopy.layout.showlegend = true;
85+
86+
Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() {
7787
delay(done);
7888
});
7989
});
@@ -190,6 +200,22 @@ describe('Test gl plot interactions', function() {
190200
});
191201
});
192202

203+
it('should be able to toggle visibility', function(done) {
204+
var objects = gd._fullLayout.scene._scene.glplot.objects;
205+
206+
expect(objects.length).toEqual(5);
207+
208+
Plotly.restyle(gd, 'visible', 'legendonly').then(function() {
209+
expect(objects.length).toEqual(0);
210+
211+
return Plotly.restyle(gd, 'visible', true);
212+
}).then(function() {
213+
expect(objects.length).toEqual(5);
214+
215+
done();
216+
});
217+
});
218+
193219
});
194220

195221
describe('gl2d plots', function() {

test/jasmine/tests/shapes_test.js

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ var createGraphDiv = require('../assets/create_graph_div');
77
var destroyGraphDiv = require('../assets/destroy_graph_div');
88

99

10-
describe('Test shapes nodes', function() {
10+
describe('Test shapes:', function() {
1111
'use strict';
1212

1313
var mock = require('@mocks/shapes.json');
@@ -28,26 +28,82 @@ describe('Test shapes nodes', function() {
2828
return d3.selectAll('.shapelayer').size();
2929
}
3030

31-
function countPaths() {
31+
function countShapePaths() {
3232
return d3.selectAll('.shapelayer > path').size();
3333
}
3434

35-
it('has one *shapelayer* node', function() {
36-
expect(countShapeLayers()).toEqual(1);
37-
});
35+
describe('DOM', function() {
36+
it('has one *shapelayer* node', function() {
37+
expect(countShapeLayers()).toEqual(1);
38+
});
39+
40+
it('has as many *path* nodes as there are shapes', function() {
41+
expect(countShapePaths()).toEqual(mock.layout.shapes.length);
42+
});
3843

39-
it('has as many *path* nodes as there are shapes', function() {
40-
expect(countPaths()).toEqual(mock.layout.shapes.length);
44+
it('should be able to get relayout', function(done) {
45+
expect(countShapeLayers()).toEqual(1);
46+
expect(countShapePaths()).toEqual(mock.layout.shapes.length);
47+
48+
Plotly.relayout(gd, {height: 200, width: 400}).then(function() {
49+
expect(countShapeLayers()).toEqual(1);
50+
expect(countShapePaths()).toEqual(mock.layout.shapes.length);
51+
}).then(done);
52+
});
4153
});
4254

43-
it('should be able to get relayout', function(done) {
44-
expect(countShapeLayers()).toEqual(1);
45-
expect(countPaths()).toEqual(mock.layout.shapes.length);
55+
function countShapes(gd) {
56+
return gd.layout.shapes ?
57+
gd.layout.shapes.length :
58+
0;
59+
}
60+
61+
function getLastShape(gd) {
62+
return gd.layout.shapes ?
63+
gd.layout.shapes[gd.layout.shapes.length - 1] :
64+
null;
65+
}
4666

47-
Plotly.relayout(gd, {height: 200, width: 400}).then(function() {
48-
expect(countShapeLayers()).toEqual(1);
49-
expect(countPaths()).toEqual(mock.layout.shapes.length);
50-
done();
67+
function getRandomShape() {
68+
return {
69+
x0: Math.random(),
70+
y0: Math.random(),
71+
x1: Math.random(),
72+
y1: Math.random()
73+
};
74+
}
75+
76+
describe('Plotly.relayout', function() {
77+
it('should be able to add a shape', function(done) {
78+
var pathCount = countShapePaths();
79+
var index = countShapes(gd);
80+
var shape = getRandomShape();
81+
82+
Plotly.relayout(gd, 'shapes[' + index + ']', shape).then(function() {
83+
expect(countShapeLayers()).toEqual(1);
84+
expect(countShapePaths()).toEqual(pathCount + 1);
85+
expect(getLastShape(gd)).toEqual(shape);
86+
expect(countShapes(gd)).toEqual(index + 1);
87+
}).then(done);
88+
});
89+
90+
it('should be able to remove a shape', function(done) {
91+
var pathCount = countShapePaths();
92+
var index = countShapes(gd);
93+
var shape = getRandomShape();
94+
95+
Plotly.relayout(gd, 'shapes[' + index + ']', shape).then(function() {
96+
expect(countShapeLayers()).toEqual(1);
97+
expect(countShapePaths()).toEqual(pathCount + 1);
98+
expect(getLastShape(gd)).toEqual(shape);
99+
expect(countShapes(gd)).toEqual(index + 1);
100+
}).then(function() {
101+
Plotly.relayout(gd, 'shapes[' + index + ']', 'remove');
102+
}).then(function() {
103+
expect(countShapeLayers()).toEqual(1);
104+
expect(countShapePaths()).toEqual(pathCount);
105+
expect(countShapes(gd)).toEqual(index);
106+
}).then(done);
51107
});
52108
});
53109
});

0 commit comments

Comments
 (0)