Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e8fc236

Browse filesBrowse files
committed
Merge pull request plotly#378 from n-riesco/issue-148-and-359
Shape clean up
2 parents 537d87e + 92e46e3 commit e8fc236
Copy full SHA for e8fc236

File tree

Expand file treeCollapse file tree

2 files changed

+149
-71
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+149
-71
lines changed

‎src/components/shapes/index.js

Copy file name to clipboardExpand all lines: src/components/shapes/index.js
+79-57Lines 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('_', ' ')); };

‎test/jasmine/tests/shapes_test.js

Copy file name to clipboardExpand all lines: test/jasmine/tests/shapes_test.js
+70-14Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.