Skip to content
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

Persistent selections for cartesian subplots #6243

Merged
merged 105 commits into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from 94 commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
e008c0b
move selection code into components/selections
archmoj May 18, 2022
c09a693
add selections, newselection & activeselection
archmoj Jun 20, 2022
49c2d28
adjustments in shapes for selections
archmoj Jun 20, 2022
e71617d
drag css no londer needed
archmoj Jun 20, 2022
c7e49e7
selections in core and plot_api
archmoj Jun 20, 2022
c6d954d
selections in plots
archmoj Jun 20, 2022
b952125
selections in plot schema
archmoj Jun 20, 2022
b8c1b6f
TODO comment for x0, x1 dflt
archmoj Jun 20, 2022
11d5a71
one outline
archmoj Jun 20, 2022
9976a76
add optional eraseselection button to modebar
archmoj Jun 20, 2022
1107600
test selections in axes_breaks-gridlines
archmoj Jun 21, 2022
b183c95
test selections in 2dhistogram_contour_subplots
archmoj Jun 21, 2022
152f19c
test selections in 12
archmoj Jun 21, 2022
fe4b29b
test selections in multicategory-mirror
archmoj Jun 21, 2022
952262f
test selections in bar-offsetgroups
archmoj Jun 21, 2022
c477571
potential fix for splom
archmoj Jun 21, 2022
66c2b75
skip one splom test for now
archmoj Jun 21, 2022
c7d0444
adjust scattergl_select tests
archmoj Jun 21, 2022
6ff3801
adjust select tests
archmoj Jun 21, 2022
a56ef0f
improve activating selections by adding a sensory element
archmoj Jun 22, 2022
addd602
add selections tests
archmoj Jun 22, 2022
adf7163
handle null selections e.g. non-sufficient x y coords
archmoj Jun 24, 2022
8986bc5
fix test - no visible attr for selections yet
archmoj Jun 24, 2022
683b89e
refactor selections draw
archmoj Jun 24, 2022
732b585
refactor - replace keys with vars
archmoj Jun 27, 2022
ca17b3e
refactor var i
archmoj Jun 27, 2022
bd99eb9
handle selections in multiple subplots for splom
archmoj Jun 27, 2022
e6c0693
refactor - use separate variable for pts loop
archmoj Jun 27, 2022
7573d73
refactor - make it easier to follow variables
archmoj Jun 27, 2022
4b341c7
refator - put regl update code inside a function
archmoj Jun 27, 2022
14ea4c8
fix multiple selections
archmoj Jun 27, 2022
efa4766
additional fixes in determineSearchTraces for splom
archmoj Jun 28, 2022
54f7450
revert parts of changes in determineSearchTraces
archmoj Jun 28, 2022
ebca656
avoid naming conflict with newSelections fn
archmoj Jun 29, 2022
df859b2
use evenodd fill-rule for selections
archmoj Jun 30, 2022
c4c806f
no open path selection at least for now
archmoj Jun 30, 2022
c1379c5
drop inverted keys in mergePolygons
archmoj Jun 30, 2022
761ef5b
fix getting polygons when having multiple polygons in one selection
archmoj Jun 30, 2022
c7ff1ce
fix selections with multiple polygons and more
archmoj Jun 30, 2022
a5d0e60
use reversed outlines in tests
archmoj Jul 5, 2022
7cfc09f
no need to determineSearchTraces without selectionTesters
archmoj Jul 5, 2022
5ca52a0
add early return for empty lists in multiTester
archmoj Jul 5, 2022
0434489
fix for scattergl relayout selections
archmoj Jul 5, 2022
8366640
revise outline vertex modifiers to match rangeslider & zoom box
archmoj Jul 5, 2022
9ce4079
rename Region > Group controllers in respect to multi-region shapes
archmoj Jul 5, 2022
f3a9538
edge controllers for rects
archmoj Jul 5, 2022
5bdec43
test selections multi polygons scattergl
archmoj Jul 5, 2022
45d9962
provide new path for eraseselection button
archmoj Jul 5, 2022
b3025e6
display interactive selections for second outlines
archmoj Jul 6, 2022
6618912
centralize get subtract logic
archmoj Jul 6, 2022
897ec0c
fix conversion for draft selection polygons
archmoj Jul 6, 2022
0e76709
for now drop test case of combination of click and drag selections
archmoj Jul 6, 2022
d28cb4f
no activeselection.fillcolor by default
archmoj Jul 6, 2022
4138cf0
make it easier to activate selections using all pointer-events
archmoj Jul 6, 2022
d5e53d9
revise selections.path description
archmoj Jul 7, 2022
d83ca89
newselection immediate and gradual modes
archmoj Jul 7, 2022
44801d5
different names for lists not to be confused with fn requires
archmoj Jul 7, 2022
c0c70be
handle immediate selections for cartesian subplots
archmoj Jul 7, 2022
fed9db9
activate immediate selection right after creation
archmoj Jul 7, 2022
095cef0
keep gradual newselection.mode in tests
archmoj Jul 7, 2022
07e09cb
adjust the size of active outline controllers
archmoj Jul 7, 2022
34d639d
remove active selection on double click and persist selections
archmoj Jul 7, 2022
faa1217
move display_outlines from shapes/draw_newshape to shapes
archmoj Jul 7, 2022
2e8d2d9
drop eraseselection button
archmoj Jul 7, 2022
20724d7
eraseActiveSelection now internal
archmoj Jul 7, 2022
8c7ead6
clear outline controllers at the start of selection draw
archmoj Jul 8, 2022
5507076
adjust controllers for tiny rect selections
archmoj Jul 8, 2022
cb2c710
restrict active selection removal to select modes
archmoj Jul 8, 2022
914cfe8
clear outline controllers before making new ones
archmoj Jul 8, 2022
693d1d0
persistent selection across splom subplots - keep selecting
archmoj Jul 8, 2022
6eba38d
test selections in splom_iris-matching
archmoj Jul 8, 2022
14a0e22
avoid missing splom scenes
archmoj Jul 8, 2022
60c8464
avoid undefined length to pass plot_api tests
archmoj Jul 8, 2022
0db958c
emit plotly_selected at the end of reselect
archmoj Jul 8, 2022
6cdff0f
use mergedPolygons in eventData - potential fix for issue 4095
archmoj Jul 8, 2022
1126808
Revert "emit plotly_selected at the end of reselect"
archmoj Jul 8, 2022
c75a902
fix to pass plot_api test
archmoj Jul 8, 2022
5322550
return eventData from reselect
archmoj Jul 8, 2022
d9f6f50
Revert "use mergedPolygons in eventData - potential fix for issue 4095"
archmoj Jul 8, 2022
703299e
draft log for PR 6243
archmoj Jul 8, 2022
f6d9d94
move clearSelectionsCache into doneFn throttle
archmoj Jul 9, 2022
33d0085
emit selected after selection edits
archmoj Jul 9, 2022
420cefc
rewrite fillRangeItems in select.js so that in does not depend on sel…
archmoj Jul 11, 2022
5060946
move makeFillRangeItems outside preSelect
archmoj Jul 11, 2022
6c1f418
update comment
archmoj Jul 11, 2022
9c9a8c8
move override fillRangeItems outside prepSelect
archmoj Jul 11, 2022
5ea9055
reorder reselect args
archmoj Jul 11, 2022
190efea
revise reselect args
archmoj Jul 11, 2022
211548b
correct allSearchTraces var name
archmoj Jul 11, 2022
ad2e740
provide range and lassoPoints for modified selections
archmoj Jul 11, 2022
37a664a
include range and lassoPoints depending on rect and lasso
archmoj Jul 11, 2022
b9d2db8
use mergedPolygons in plotly_selecting event
archmoj Jul 11, 2022
4623196
provide ranges for rect selection edits in plotly_selected
archmoj Jul 12, 2022
3632827
add comment regarding new selection plotly_selected points
archmoj Jul 12, 2022
8b959ea
emit plotly_deselect on selection removal and update subplot state
archmoj Jul 12, 2022
ab9da47
move handle_outlines into shapes beside display_outlines
archmoj Jul 12, 2022
23b812a
clearSelect > clearOutline
archmoj Jul 12, 2022
8776d49
add all the points to new selection points in event data
archmoj Jul 12, 2022
d2832df
emit selected not deselect when selections remain from deselect
archmoj Jul 12, 2022
293abfb
use fullLayout references in _deselect
archmoj Jul 12, 2022
0f03f1f
fix subplot state on deselect single selections
archmoj Jul 12, 2022
b927b88
do not regenerate scattergl when computed
archmoj Jul 12, 2022
78f3700
adjust splom test
archmoj Jul 12, 2022
feb4657
clear subplot selections before adding new selection in immediate mod…
archmoj Jul 13, 2022
46709ab
provide layout.selections in plotly_selected events
archmoj Jul 13, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions draftlogs/add_6243.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- add `selections`, `newselection` and `activeselection` options to have
persistent and editable selections over cartesian subplots [[#6243](https://github.com/plotly/plotly.js/pull/6243)]
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
"mouse-wheel": "^1.2.0",
"native-promise-only": "^0.8.1",
"parse-svg-path": "^0.1.2",
"point-in-polygon": "^1.1.0",
"polybooljs": "^1.2.0",
"probe-image-size": "^7.2.3",
"regl": "npm:@plotly/regl@^2.1.2",
Expand Down
85 changes: 85 additions & 0 deletions src/components/selections/attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'use strict';

var annAttrs = require('../annotations/attributes');
var scatterLineAttrs = require('../../traces/scatter/attributes').line;
var dash = require('../drawing/attributes').dash;
var extendFlat = require('../../lib/extend').extendFlat;
var overrideAll = require('../../plot_api/edit_types').overrideAll;
var templatedArray = require('../../plot_api/plot_template').templatedArray;
var axisPlaceableObjs = require('../../constants/axis_placeable_objects');

module.exports = overrideAll(templatedArray('selection', {
type: {
valType: 'enumerated',
values: ['rect', 'path'],
alexcjohnson marked this conversation as resolved.
Show resolved Hide resolved
description: [
'Specifies the selection type to be drawn.',

'If *rect*, a rectangle is drawn linking',
'(`x0`,`y0`), (`x1`,`y0`), (`x1`,`y1`) and (`x0`,`y1`).',

'If *path*, draw a custom SVG path using `path`.'
].join(' ')
},

xref: extendFlat({}, annAttrs.xref, {
description: [
'Sets the selection\'s x coordinate axis.',
axisPlaceableObjs.axisRefDescription('x', 'left', 'right')
].join(' ')
}),

yref: extendFlat({}, annAttrs.yref, {
description: [
'Sets the selection\'s x coordinate axis.',
axisPlaceableObjs.axisRefDescription('y', 'bottom', 'top')
].join(' ')
}),

x0: {
valType: 'any',
description: 'Sets the selection\'s starting x position.'
},
x1: {
valType: 'any',
description: 'Sets the selection\'s end x position.'
},

y0: {
valType: 'any',
description: 'Sets the selection\'s starting y position.'
},
y1: {
valType: 'any',
description: 'Sets the selection\'s end y position.'
},

path: {
valType: 'string',
editType: 'arraydraw',
description: [
'For `type` *path* - a valid SVG path similar to `shapes.path` in data coordinates.',
'Allowed segments are: M, L and Z.'
].join(' ')
},

opacity: {
valType: 'number',
min: 0,
max: 1,
dflt: 0.7,
editType: 'arraydraw',
description: 'Sets the opacity of the selection.'
},

line: {
color: scatterLineAttrs.color,
width: extendFlat({}, scatterLineAttrs.width, {
min: 1,
dflt: 1
}),
dash: extendFlat({}, dash, {
dflt: 'dot'
})
},
}), 'arraydraw', 'from-root');
15 changes: 15 additions & 0 deletions src/components/selections/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

module.exports = {
// max pixels off straight before a lasso select line counts as bent
BENDPX: 1.5,

// smallest dimension allowed for a select box
MINSELECT: 12,

// throttling limit (ms) for selectPoints calls
SELECTDELAY: 100,

// cache ID suffix for throttle
SELECTID: '-select',
};
103 changes: 103 additions & 0 deletions src/components/selections/defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
'use strict';

var Lib = require('../../lib');
var Axes = require('../../plots/cartesian/axes');
var handleArrayContainerDefaults = require('../../plots/array_container_defaults');

var attributes = require('./attributes');
var helpers = require('../shapes/helpers');

module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
handleArrayContainerDefaults(layoutIn, layoutOut, {
name: 'selections',
handleItemDefaults: handleSelectionDefaults
});

// Drop rect selections with undefined x0, y0, x1, x1 values.
// In future we may accept partially defined rects e.g.
// a case with only x0 and x1 may be used to define
// [-Infinity, +Infinity] range on the y axis, etc.
var selections = layoutOut.selections;
for(var i = 0; i < selections.length; i++) {
var selection = selections[i];
if(!selection) continue;
if(selection.path === undefined) {
if(
selection.x0 === undefined ||
selection.x1 === undefined ||
selection.y0 === undefined ||
selection.y1 === undefined
) {
layoutOut.selections[i] = null;
}
}
}
};

function handleSelectionDefaults(selectionIn, selectionOut, fullLayout) {
function coerce(attr, dflt) {
return Lib.coerce(selectionIn, selectionOut, attributes, attr, dflt);
}

var path = coerce('path');
var dfltType = path ? 'path' : 'rect';
var selectionType = coerce('type', dfltType);
var noPath = selectionType !== 'path';
if(noPath) delete selectionOut.path;

coerce('opacity');
coerce('line.color');
coerce('line.width');
coerce('line.dash');

// positioning
var axLetters = ['x', 'y'];
for(var i = 0; i < 2; i++) {
var axLetter = axLetters[i];
var gdMock = {_fullLayout: fullLayout};
var ax;
var pos2r;
var r2pos;

// xref, yref
var axRef = Axes.coerceRef(selectionIn, selectionOut, gdMock, axLetter);

// axRefType is 'range' for selections
ax = Axes.getFromId(gdMock, axRef);
ax._selectionIndices.push(selectionOut._index);
r2pos = helpers.rangeToShapePosition(ax);
pos2r = helpers.shapePositionToRange(ax);

// Coerce x0, x1, y0, y1
if(noPath) {
// hack until V3.0 when log has regular range behavior - make it look like other
// ranges to send to coerce, then put it back after
// this is all to give reasonable default position behavior on log axes, which is
// a pretty unimportant edge case so we could just ignore this.
var attr0 = axLetter + '0';
var attr1 = axLetter + '1';
var in0 = selectionIn[attr0];
var in1 = selectionIn[attr1];
selectionIn[attr0] = pos2r(selectionIn[attr0], true);
selectionIn[attr1] = pos2r(selectionIn[attr1], true);

Axes.coercePosition(selectionOut, gdMock, coerce, axRef, attr0);
Axes.coercePosition(selectionOut, gdMock, coerce, axRef, attr1);

var p0 = selectionOut[attr0];
var p1 = selectionOut[attr1];

if(p0 !== undefined && p1 !== undefined) {
// hack part 2
selectionOut[attr0] = r2pos(p0);
selectionOut[attr1] = r2pos(p1);
selectionIn[attr0] = in0;
selectionIn[attr1] = in1;
}
}
}

if(noPath) {
Lib.noneOrAll(selectionIn, selectionOut, ['x0', 'x1', 'y0', 'y1']);
}
}