Skip to content

Add multiselect #2140

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Nov 13, 2017
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"ndarray-fill": "^1.0.2",
"ndarray-homography": "^1.0.0",
"ndarray-ops": "^1.2.2",
"polybooljs": "^1.2.0",
"regl": "^1.3.0",
"right-now": "^1.0.0",
"robust-orientation": "^1.1.3",
Expand Down
64 changes: 61 additions & 3 deletions src/lib/polygon.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@ var polygon = module.exports = {};
* returns boolean: is pt inside the polygon (including on its edges)
*/
polygon.tester = function tester(ptsIn) {
if(Array.isArray(ptsIn[0][0])) return polygon.multitester(ptsIn);

var pts = ptsIn.slice(),
xmin = pts[0][0],
xmax = xmin,
ymin = pts[0][1],
ymax = ymin;
ymax = ymin,
i;

pts.push(pts[0]);
for(var i = 1; i < pts.length; i++) {
for(i = 1; i < pts.length; i++) {
xmin = Math.min(xmin, pts[i][0]);
xmax = Math.max(xmax, pts[i][0]);
ymin = Math.min(ymin, pts[i][1]);
Expand Down Expand Up @@ -149,14 +152,69 @@ polygon.tester = function tester(ptsIn) {
return crossings % 2 === 1;
}

// detect if poly is degenerate
var degenerate = true;
var lastPt = pts[0];
for(i = 1; i < pts.length; i++) {
if(lastPt[0] !== pts[i][0] || lastPt[1] !== pts[i][1]) {
degenerate = false;
break;
}
}

return {
xmin: xmin,
xmax: xmax,
ymin: ymin,
ymax: ymax,
pts: pts,
contains: isRect ? rectContains : contains,
isRect: isRect
isRect: isRect,
degenerate: degenerate
};
};

/**
* Test multiple polygons
*/
polygon.multitester = function multitester(list) {
var testers = [],
xmin = list[0][0][0],
xmax = xmin,
ymin = list[0][0][1],
ymax = ymin;

for(var i = 0; i < list.length; i++) {
var tester = polygon.tester(list[i]);
tester.subtract = list[i].subtract;
testers.push(tester);
xmin = Math.min(xmin, tester.xmin);
xmax = Math.max(xmax, tester.xmax);
ymin = Math.min(ymin, tester.ymin);
ymax = Math.max(ymax, tester.ymax);
}

function contains(pt, arg) {
var yes = false;
for(var i = 0; i < testers.length; i++) {
if(testers[i].contains(pt, arg)) {
// if contained by subtract polygon - exclude the point
yes = testers[i].subtract === false;
}
}

return yes;
}

return {
xmin: xmin,
xmax: xmax,
ymin: ymin,
ymax: ymax,
pts: [],
contains: contains,
isRect: false,
degenerate: false
};
};

Expand Down
55 changes: 51 additions & 4 deletions src/plots/cartesian/dragbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,10 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
// to pan (or to zoom if it already is pan) on shift
if(e.shiftKey) {
if(dragModeNow === 'pan') dragModeNow = 'zoom';
else dragModeNow = 'pan';
else if(!isSelectOrLasso(dragModeNow)) dragModeNow = 'pan';
}
else if(e.ctrlKey) {
dragModeNow = 'pan';
}
}
// all other draggers just pan
Expand Down Expand Up @@ -168,13 +171,31 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
else if(isSelectOrLasso(dragModeNow)) {
dragOptions.xaxes = xa;
dragOptions.yaxes = ya;

// take over selection polygons from prev mode, if any
if((e.shiftKey || e.altKey) && (plotinfo.selection && plotinfo.selection.polygons) && !dragOptions.polygons) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to move this block into prepSelect so that all subplot types that support selections can allow multi-select? Right now, only cartesian and gl2d plot types allow it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

dragOptions.polygons = plotinfo.selection.polygons;
dragOptions.mergedPolygons = plotinfo.selection.mergedPolygons;
}
// create new polygons, if shift mode
else if((!e.shiftKey && !e.altKey) || ((e.shiftKey || e.altKey) && !plotinfo.selection)) {
plotinfo.selection = {};
plotinfo.selection.polygons = dragOptions.polygons = [];
plotinfo.selection.mergedPolygons = dragOptions.mergedPolygons = [];
}

prepSelect(e, startX, startY, dragOptions, dragModeNow);
}
}
};

dragElement.init(dragOptions);

// FIXME: this hack highlights selection once we enter select/lasso mode
if(isSelectOrLasso(gd._fullLayout.dragmode) && plotinfo.selection) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of this behavior. @dfcreative is there a particular reason why you added this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not relevant since #2135

showSelect(zoomlayer, dragOptions);
}

var x0,
y0,
box,
Expand Down Expand Up @@ -526,6 +547,9 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
}

updateSubplots([x0, y0, pw - dx, ph - dy]);

if(plotinfo.ondrag) plotinfo.ondrag.call([x0, y0, pw - dx, ph - dy]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed


ticksAndAnnotations(yActive, xActive);
}

Expand Down Expand Up @@ -902,6 +926,31 @@ function clearSelect(zoomlayer) {
zoomlayer.selectAll('.select-outline').remove();
}

function showSelect(zoomlayer, dragOptions) {
var outlines = zoomlayer.selectAll('path.select-outline').data([1, 2]),
plotinfo = dragOptions.plotinfo,
xaxis = plotinfo.xaxis,
yaxis = plotinfo.yaxis,
selection = plotinfo.selection,
polygons = selection.mergedPolygons,
xs = xaxis._offset,
ys = yaxis._offset,
paths = [];

for(var i = 0; i < polygons.length; i++) {
var ppts = polygons[i];
paths.push(ppts.join('L') + 'L' + ppts[0]);
}

if(paths.length) {
outlines.enter()
.append('path')
.attr('class', function(d) { return 'select-outline select-outline-' + d; })
.attr('transform', 'translate(' + xs + ', ' + ys + ')')
.attr('d', 'M' + paths.join('M') + 'Z');
}
}

function updateZoombox(zb, corners, box, path0, dimmed, lum) {
zb.attr('d',
path0 + 'M' + (box.l) + ',' + (box.t) + 'v' + (box.h) +
Expand All @@ -924,9 +973,7 @@ function removeZoombox(gd) {
}

function isSelectOrLasso(dragmode) {
var modes = ['lasso', 'select'];

return modes.indexOf(dragmode) !== -1;
return dragmode === 'lasso' || dragmode === 'select';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Way better 🐎 . Thanks!

}

function xCorners(box, y0) {
Expand Down
Loading