9
9
10
10
'use strict' ;
11
11
12
+ var polybool = require ( 'poly-bool' ) ;
12
13
var polygon = require ( '../../lib/polygon' ) ;
13
14
var throttle = require ( '../../lib/throttle' ) ;
14
15
var color = require ( '../../components/color' ) ;
@@ -19,6 +20,7 @@ var constants = require('./constants');
19
20
20
21
var filteredPolygon = polygon . filter ;
21
22
var polygonTester = polygon . tester ;
23
+ var multipolygonTester = polygon . multitester ;
22
24
var MINSELECT = constants . MINSELECT ;
23
25
24
26
function getAxId ( ax ) { return ax . _id ; }
@@ -39,10 +41,10 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
39
41
xAxisIds = dragOptions . xaxes . map ( getAxId ) ,
40
42
yAxisIds = dragOptions . yaxes . map ( getAxId ) ,
41
43
allAxes = dragOptions . xaxes . concat ( dragOptions . yaxes ) ,
42
- pts ;
44
+ filterPoly , testPoly , mergedPolygons , currentPolygon ;
43
45
44
46
if ( mode === 'lasso' ) {
45
- pts = filteredPolygon ( [ [ x0 , y0 ] ] , constants . BENDPX ) ;
47
+ filterPoly = filteredPolygon ( [ [ x0 , y0 ] ] , constants . BENDPX ) ;
46
48
}
47
49
48
50
var outlines = zoomLayer . selectAll ( 'path.select-outline' ) . data ( [ 1 , 2 ] ) ;
@@ -115,34 +117,33 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
115
117
fillRangeItems = plotinfo . fillRangeItems ;
116
118
} else {
117
119
if ( mode === 'select' ) {
118
- fillRangeItems = function ( eventData , poly ) {
120
+ fillRangeItems = function ( eventData , currentPolygon ) {
119
121
var ranges = eventData . range = { } ;
120
122
121
123
for ( i = 0 ; i < allAxes . length ; i ++ ) {
122
124
var ax = allAxes [ i ] ;
123
125
var axLetter = ax . _id . charAt ( 0 ) ;
126
+ var x = axLetter === 'x' ;
124
127
125
128
ranges [ ax . _id ] = [
126
- ax . p2d ( poly [ axLetter + 'min' ] ) ,
127
- ax . p2d ( poly [ axLetter + 'max' ] )
129
+ ax . p2d ( currentPolygon [ 0 ] [ x ? 0 : 1 ] ) ,
130
+ ax . p2d ( currentPolygon [ 2 ] [ x ? 0 : 1 ] )
128
131
] . sort ( ascending ) ;
129
132
}
130
133
} ;
131
134
} else {
132
- fillRangeItems = function ( eventData , poly , pts ) {
135
+ fillRangeItems = function ( eventData , currentPolygon , filterPoly ) {
133
136
var dataPts = eventData . lassoPoints = { } ;
134
137
135
138
for ( i = 0 ; i < allAxes . length ; i ++ ) {
136
139
var ax = allAxes [ i ] ;
137
- dataPts [ ax . _id ] = pts . filtered . map ( axValue ( ax ) ) ;
140
+ dataPts [ ax . _id ] = filterPoly . filtered . map ( axValue ( ax ) ) ;
138
141
}
139
142
} ;
140
143
}
141
144
}
142
145
143
146
dragOptions . moveFn = function ( dx0 , dy0 ) {
144
- var poly ;
145
-
146
147
x1 = Math . max ( 0 , Math . min ( pw , dx0 + x0 ) ) ;
147
148
y1 = Math . max ( 0 , Math . min ( ph , dy0 + y0 ) ) ;
148
149
@@ -152,46 +153,66 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
152
153
if ( mode === 'select' ) {
153
154
if ( dy < Math . min ( dx * 0.6 , MINSELECT ) ) {
154
155
// horizontal motion: make a vertical box
155
- poly = polygonTester ( [ [ x0 , 0 ] , [ x0 , ph ] , [ x1 , ph ] , [ x1 , 0 ] ] ) ;
156
+ currentPolygon = [ [ x0 , 0 ] , [ x0 , ph ] , [ x1 , ph ] , [ x1 , 0 ] ] ;
156
157
// extras to guide users in keeping a straight selection
157
- corners . attr ( 'd' , 'M' + poly . xmin + ',' + ( y0 - MINSELECT ) +
158
+ corners . attr ( 'd' , 'M' + Math . min ( x0 , x1 ) + ',' + ( y0 - MINSELECT ) +
158
159
'h-4v' + ( 2 * MINSELECT ) + 'h4Z' +
159
- 'M' + ( poly . xmax - 1 ) + ',' + ( y0 - MINSELECT ) +
160
+ 'M' + ( Math . max ( x0 , x1 ) - 1 ) + ',' + ( y0 - MINSELECT ) +
160
161
'h4v' + ( 2 * MINSELECT ) + 'h-4Z' ) ;
161
162
162
163
}
163
164
else if ( dx < Math . min ( dy * 0.6 , MINSELECT ) ) {
164
165
// vertical motion: make a horizontal box
165
- poly = polygonTester ( [ [ 0 , y0 ] , [ 0 , y1 ] , [ pw , y1 ] , [ pw , y0 ] ] ) ;
166
- corners . attr ( 'd' , 'M' + ( x0 - MINSELECT ) + ',' + poly . ymin +
166
+ currentPolygon = [ [ 0 , y0 ] , [ 0 , y1 ] , [ pw , y1 ] , [ pw , y0 ] ] ;
167
+ corners . attr ( 'd' , 'M' + ( x0 - MINSELECT ) + ',' + Math . min ( y0 , y1 ) +
167
168
'v-4h' + ( 2 * MINSELECT ) + 'v4Z' +
168
- 'M' + ( x0 - MINSELECT ) + ',' + ( poly . ymax - 1 ) +
169
+ 'M' + ( x0 - MINSELECT ) + ',' + ( Math . max ( y0 , y1 ) - 1 ) +
169
170
'v4h' + ( 2 * MINSELECT ) + 'v-4Z' ) ;
170
171
}
171
172
else {
172
173
// diagonal motion
173
- poly = polygonTester ( [ [ x0 , y0 ] , [ x0 , y1 ] , [ x1 , y1 ] , [ x1 , y0 ] ] ) ;
174
+ currentPolygon = [ [ x0 , y0 ] , [ x0 , y1 ] , [ x1 , y1 ] , [ x1 , y0 ] ] ;
174
175
corners . attr ( 'd' , 'M0,0Z' ) ;
175
176
}
176
- outlines . attr ( 'd' , 'M' + poly . xmin + ',' + poly . ymin +
177
- 'H' + ( poly . xmax - 1 ) + 'V' + ( poly . ymax - 1 ) +
178
- 'H' + poly . xmin + 'Z' ) ;
179
177
}
180
178
else if ( mode === 'lasso' ) {
181
- pts . addPt ( [ x1 , y1 ] ) ;
182
- poly = polygonTester ( pts . filtered ) ;
183
- outlines . attr ( 'd' , 'M' + pts . filtered . join ( 'L' ) + 'Z' ) ;
179
+ filterPoly . addPt ( [ x1 , y1 ] ) ;
180
+ currentPolygon = filterPoly . filtered ;
181
+ }
182
+
183
+ // create outline & tester
184
+ if ( dragOptions . polygons . length ) {
185
+ mergedPolygons = polybool ( dragOptions . mergedPolygons , [ currentPolygon ] , 'or' ) ;
186
+ testPoly = multipolygonTester ( dragOptions . polygons . concat ( [ currentPolygon ] ) ) ;
187
+ }
188
+ else {
189
+ mergedPolygons = [ currentPolygon ] ;
190
+ testPoly = polygonTester ( currentPolygon ) ;
191
+ }
192
+
193
+ // draw selection
194
+ var paths = [ ] ;
195
+ for ( i = 0 ; i < mergedPolygons . length ; i ++ ) {
196
+ var ppts = mergedPolygons [ i ] ;
197
+ paths . push ( ppts . join ( 'L' ) + 'L' + ppts [ 0 ] ) ;
184
198
}
199
+ outlines . attr ( 'd' , 'M' + paths . join ( 'M' ) + 'Z' ) ;
185
200
186
201
throttle . throttle (
187
202
throttleID ,
188
203
constants . SELECTDELAY ,
189
204
function ( ) {
190
205
selection = [ ] ;
206
+
207
+ var traceSelections = [ ] , traceSelection ;
191
208
for ( i = 0 ; i < searchTraces . length ; i ++ ) {
192
209
searchInfo = searchTraces [ i ] ;
210
+
211
+ traceSelection = searchInfo . selectPoints ( searchInfo , testPoly ) ;
212
+ traceSelections . push ( traceSelection ) ;
213
+
193
214
var thisSelection = fillSelectionItem (
194
- searchInfo . selectPoints ( searchInfo , poly ) , searchInfo
215
+ traceSelection , searchInfo
195
216
) ;
196
217
if ( selection . length ) {
197
218
for ( var j = 0 ; j < thisSelection . length ; j ++ ) {
@@ -202,7 +223,7 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
202
223
}
203
224
204
225
eventData = { points : selection } ;
205
- fillRangeItems ( eventData , poly , pts ) ;
226
+ fillRangeItems ( eventData , currentPolygon , filterPoly ) ;
206
227
dragOptions . gd . emit ( 'plotly_selecting' , eventData ) ;
207
228
}
208
229
) ;
@@ -226,6 +247,12 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
226
247
else {
227
248
dragOptions . gd . emit ( 'plotly_selected' , eventData ) ;
228
249
}
250
+
251
+ // save last polygons
252
+ dragOptions . polygons . push ( currentPolygon ) ;
253
+
254
+ // we have to keep reference to arrays, therefore just replace items
255
+ dragOptions . mergedPolygons . splice . apply ( dragOptions . mergedPolygons , [ 0 , dragOptions . mergedPolygons . length ] . concat ( mergedPolygons ) ) ;
229
256
} ) ;
230
257
} ;
231
258
} ;
0 commit comments