Skip to content

Commit 70de159

Browse files
authored
Merge pull request #1831 from plotly/fix-scatterternary-lasso
Fix scatterternary lasso/select drag modes
2 parents d47ace6 + 5a48568 commit 70de159

File tree

4 files changed

+128
-39
lines changed

4 files changed

+128
-39
lines changed

src/plots/cartesian/select.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ var MINSELECT = constants.MINSELECT;
2323
function getAxId(ax) { return ax._id; }
2424

2525
module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
26-
var plot = dragOptions.gd._fullLayout._zoomlayer,
26+
var zoomLayer = dragOptions.gd._fullLayout._zoomlayer,
2727
dragBBox = dragOptions.element.getBoundingClientRect(),
2828
xs = dragOptions.plotinfo.xaxis._offset,
2929
ys = dragOptions.plotinfo.yaxis._offset,
@@ -43,15 +43,15 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
4343
pts = filteredPolygon([[x0, y0]], constants.BENDPX);
4444
}
4545

46-
var outlines = plot.selectAll('path.select-outline').data([1, 2]);
46+
var outlines = zoomLayer.selectAll('path.select-outline').data([1, 2]);
4747

4848
outlines.enter()
4949
.append('path')
5050
.attr('class', function(d) { return 'select-outline select-outline-' + d; })
5151
.attr('transform', 'translate(' + xs + ', ' + ys + ')')
5252
.attr('d', path0 + 'Z');
5353

54-
var corners = plot.append('path')
54+
var corners = zoomLayer.append('path')
5555
.attr('class', 'zoombox-corners')
5656
.style({
5757
fill: color.background,

src/plots/ternary/index.js

+4
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,8 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout)
6969
oldTernary.clipDef.remove();
7070
}
7171
}
72+
73+
if(oldFullLayout._zoomlayer) {
74+
oldFullLayout._zoomlayer.selectAll('.select-outline').remove();
75+
}
7276
};

src/plots/ternary/ternary.js

+13-9
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ proto.makeFramework = function() {
8888
'backplot',
8989
'grids',
9090
'frontplot',
91-
'zoom',
9291
'aaxis', 'baxis', 'caxis', 'axlines'
9392
];
9493
var toplevel = _this.plotContainer.selectAll('g.toplevel')
@@ -123,10 +122,6 @@ proto.makeFramework = function() {
123122

124123
_this.plotContainer.selectAll('.backplot,.frontplot,.grids')
125124
.call(Drawing.setClipUrl, clipId);
126-
127-
if(!_this.graphDiv._context.staticPlot) {
128-
_this.initInteractions();
129-
}
130125
};
131126

132127
var w_over_h = Math.sqrt(4 / 3);
@@ -263,7 +258,7 @@ proto.adjustLayout = function(ternaryLayout, graphSize) {
263258
_this.layers.plotbg.select('path').attr('d', triangleClip);
264259

265260
var plotTransform = 'translate(' + x0 + ',' + y0 + ')';
266-
_this.plotContainer.selectAll('.scatterlayer,.maplayer,.zoom')
261+
_this.plotContainer.selectAll('.scatterlayer,.maplayer')
267262
.attr('transform', plotTransform);
268263

269264
// TODO: shift axes to accommodate linewidth*sin(30) tick mark angle
@@ -303,6 +298,10 @@ proto.adjustLayout = function(ternaryLayout, graphSize) {
303298
'M' + (x0 + w / 2) + ',' + y0 + 'l' + (w / 2) + ',' + h : 'M0,0')
304299
.call(Color.stroke, caxis.linecolor || '#000')
305300
.style('stroke-width', (caxis.linewidth || 0) + 'px');
301+
302+
if(!_this.graphDiv._context.staticPlot) {
303+
_this.initInteractions();
304+
}
306305
};
307306

308307
proto.drawAxes = function(doTitles) {
@@ -382,13 +381,16 @@ proto.initInteractions = function() {
382381
var _this = this,
383382
dragger = _this.layers.plotbg.select('path').node(),
384383
gd = _this.graphDiv,
385-
zoomContainer = _this.layers.zoom;
384+
zoomContainer = gd._fullLayout._zoomlayer;
386385

387386
// use plotbg for the main interactions
388387
var dragOptions = {
389388
element: dragger,
390389
gd: gd,
391-
plotinfo: {plot: zoomContainer},
390+
plotinfo: {
391+
xaxis: _this.xaxis,
392+
yaxis: _this.yaxis
393+
},
392394
doubleclick: doubleClick,
393395
subplot: _this.id,
394396
prepFn: function(e, startX, startY) {
@@ -441,6 +443,7 @@ proto.initInteractions = function() {
441443

442444
zb = zoomContainer.append('path')
443445
.attr('class', 'zoombox')
446+
.attr('transform', 'translate(' + _this.x0 + ', ' + _this.y0 + ')')
444447
.style({
445448
'fill': lum > 0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)',
446449
'stroke-width': 0
@@ -449,6 +452,7 @@ proto.initInteractions = function() {
449452

450453
corners = zoomContainer.append('path')
451454
.attr('class', 'zoombox-corners')
455+
.attr('transform', 'translate(' + _this.x0 + ', ' + _this.y0 + ')')
452456
.style({
453457
fill: Color.background,
454458
stroke: Color.defaultLine,
@@ -603,7 +607,7 @@ proto.initInteractions = function() {
603607
// until we get around to persistent selections, remove the outline
604608
// here. The selection itself will be removed when the plot redraws
605609
// at the end.
606-
_this.plotContainer.selectAll('.select-outline').remove();
610+
zoomContainer.selectAll('.select-outline').remove();
607611
}
608612

609613
function doubleClick() {

test/jasmine/tests/select_test.js

+108-27
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var doubleClick = require('../assets/double_click');
66

77
var createGraphDiv = require('../assets/create_graph_div');
88
var destroyGraphDiv = require('../assets/destroy_graph_div');
9+
var fail = require('../assets/fail_test');
910
var mouseEvent = require('../assets/mouse_event');
1011
var customMatchers = require('../assets/custom_matchers');
1112

@@ -16,7 +17,7 @@ describe('select box and lasso', function() {
1617
var selectPath = [[93, 193], [143, 193]];
1718
var lassoPath = [[316, 171], [318, 239], [335, 243], [328, 169]];
1819

19-
beforeEach(function() {
20+
beforeAll(function() {
2021
jasmine.addMatchers(customMatchers);
2122
});
2223

@@ -59,6 +60,13 @@ describe('select box and lasso', function() {
5960
});
6061
}
6162

63+
function assertSelectionNodes(cornerCnt, outlineCnt) {
64+
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
65+
.toBe(cornerCnt, 'selection corner count');
66+
expect(d3.selectAll('.zoomlayer > .select-outline').size())
67+
.toBe(outlineCnt, 'selection outline count');
68+
}
69+
6270
describe('select elements', function() {
6371
var mockCopy = Lib.extendDeep({}, mock);
6472
mockCopy.layout.dragmode = 'select';
@@ -80,30 +88,21 @@ describe('select box and lasso', function() {
8088
y2 = 50;
8189

8290
gd.once('plotly_selecting', function() {
83-
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
84-
.toEqual(1);
85-
expect(d3.selectAll('.zoomlayer > .select-outline').size())
86-
.toEqual(2);
91+
assertSelectionNodes(1, 2);
8792
});
8893

8994
gd.once('plotly_selected', function() {
90-
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
91-
.toEqual(0);
92-
expect(d3.selectAll('.zoomlayer > .select-outline').size())
93-
.toEqual(2);
95+
assertSelectionNodes(0, 2);
9496
});
9597

9698
gd.once('plotly_deselect', function() {
97-
expect(d3.selectAll('.zoomlayer > .select-outline').size())
98-
.toEqual(0);
99+
assertSelectionNodes(0, 0);
99100
});
100101

101102
mouseEvent('mousemove', x0, y0);
102-
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
103-
.toEqual(0);
103+
assertSelectionNodes(0, 0);
104104

105105
drag([[x0, y0], [x1, y1]]);
106-
107106
doubleClick(x2, y2).then(done);
108107
});
109108
});
@@ -129,30 +128,21 @@ describe('select box and lasso', function() {
129128
y2 = 50;
130129

131130
gd.once('plotly_selecting', function() {
132-
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
133-
.toEqual(1);
134-
expect(d3.selectAll('.zoomlayer > .select-outline').size())
135-
.toEqual(2);
131+
assertSelectionNodes(1, 2);
136132
});
137133

138134
gd.once('plotly_selected', function() {
139-
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
140-
.toEqual(0);
141-
expect(d3.selectAll('.zoomlayer > .select-outline').size())
142-
.toEqual(2);
135+
assertSelectionNodes(0, 2);
143136
});
144137

145138
gd.once('plotly_deselect', function() {
146-
expect(d3.selectAll('.zoomlayer > .select-outline').size())
147-
.toEqual(0);
139+
assertSelectionNodes(0, 0);
148140
});
149141

150142
mouseEvent('mousemove', x0, y0);
151-
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
152-
.toEqual(0);
143+
assertSelectionNodes(0, 0);
153144

154145
drag([[x0, y0], [x1, y1]]);
155-
156146
doubleClick(x2, y2).then(done);
157147
});
158148
});
@@ -379,4 +369,95 @@ describe('select box and lasso', function() {
379369
})
380370
.then(done);
381371
});
372+
373+
it('should work on scatterternary traces', function(done) {
374+
var fig = Lib.extendDeep({}, require('@mocks/ternary_simple'));
375+
var gd = createGraphDiv();
376+
var pts = [];
377+
378+
fig.layout.width = 800;
379+
fig.layout.dragmode = 'select';
380+
381+
function assertPoints(expected) {
382+
expect(pts.length).toBe(expected.length, 'selected points length');
383+
384+
pts.forEach(function(p, i) {
385+
var e = expected[i];
386+
expect(p.a).toBe(e.a, 'selected pt a val');
387+
expect(p.b).toBe(e.b, 'selected pt b val');
388+
expect(p.c).toBe(e.c, 'selected pt c val');
389+
});
390+
pts = [];
391+
}
392+
393+
Plotly.plot(gd, fig).then(function() {
394+
gd.on('plotly_selected', function(data) {
395+
pts = data.points;
396+
});
397+
398+
assertSelectionNodes(0, 0);
399+
drag([[400, 200], [445, 235]]);
400+
assertSelectionNodes(0, 2);
401+
assertPoints([{ a: 0.5, b: 0.25, c: 0.25 }]);
402+
403+
return Plotly.relayout(gd, 'dragmode', 'lasso');
404+
})
405+
.then(function() {
406+
assertSelectionNodes(0, 0);
407+
drag([[400, 200], [445, 200], [445, 235], [400, 235], [400, 200]]);
408+
assertSelectionNodes(0, 2);
409+
assertPoints([{ a: 0.5, b: 0.25, c: 0.25 }]);
410+
411+
// should work after a relayout too
412+
return Plotly.relayout(gd, 'width', 400);
413+
})
414+
.then(function() {
415+
assertSelectionNodes(0, 0);
416+
drag([[200, 200], [230, 200], [230, 230], [200, 230], [200, 200]]);
417+
assertSelectionNodes(0, 2);
418+
assertPoints([{ a: 0.5, b: 0.25, c: 0.25 }]);
419+
})
420+
.catch(fail)
421+
.then(done);
422+
});
423+
424+
it('should work on scattercarpet traces', function(done) {
425+
var fig = Lib.extendDeep({}, require('@mocks/scattercarpet'));
426+
var gd = createGraphDiv();
427+
var pts = [];
428+
429+
fig.layout.dragmode = 'select';
430+
431+
function assertPoints(expected) {
432+
expect(pts.length).toBe(expected.length, 'selected points length');
433+
434+
pts.forEach(function(p, i) {
435+
var e = expected[i];
436+
expect(p.a).toBe(e.a, 'selected pt a val');
437+
expect(p.b).toBe(e.b, 'selected pt b val');
438+
});
439+
pts = [];
440+
}
441+
442+
Plotly.plot(gd, fig).then(function() {
443+
gd.on('plotly_selected', function(data) {
444+
pts = data.points;
445+
});
446+
447+
assertSelectionNodes(0, 0);
448+
drag([[300, 200], [400, 250]]);
449+
assertSelectionNodes(0, 2);
450+
assertPoints([{ a: 0.2, b: 1.5 }]);
451+
452+
return Plotly.relayout(gd, 'dragmode', 'lasso');
453+
})
454+
.then(function() {
455+
assertSelectionNodes(0, 0);
456+
drag([[300, 200], [400, 200], [400, 250], [300, 250], [300, 200]]);
457+
assertSelectionNodes(0, 2);
458+
assertPoints([{ a: 0.2, b: 1.5 }]);
459+
})
460+
.catch(fail)
461+
.then(done);
462+
});
382463
});

0 commit comments

Comments
 (0)