99
1010'use strict' ;
1111
12+ var polybool = require ( 'polybooljs' ) ;
1213var polygon = require ( '../../lib/polygon' ) ;
1314var throttle = require ( '../../lib/throttle' ) ;
1415var color = require ( '../../components/color' ) ;
@@ -19,6 +20,7 @@ var constants = require('./constants');
1920
2021var filteredPolygon = polygon . filter ;
2122var polygonTester = polygon . tester ;
23+ var multipolygonTester = polygon . multitester ;
2224var MINSELECT = constants . MINSELECT ;
2325
2426function getAxId ( ax ) { return ax . _id ; }
@@ -39,10 +41,24 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
3941 xAxisIds = dragOptions . xaxes . map ( getAxId ) ,
4042 yAxisIds = dragOptions . yaxes . map ( getAxId ) ,
4143 allAxes = dragOptions . xaxes . concat ( dragOptions . yaxes ) ,
42- pts ;
44+ filterPoly , testPoly , mergedPolygons , currentPolygon ,
45+ subtract = e . altKey ;
46+
47+
48+ // take over selection polygons from prev mode, if any
49+ if ( ( e . shiftKey || e . altKey ) && ( plotinfo . selection && plotinfo . selection . polygons ) && ! dragOptions . polygons ) {
50+ dragOptions . polygons = plotinfo . selection . polygons ;
51+ dragOptions . mergedPolygons = plotinfo . selection . mergedPolygons ;
52+ }
53+ // create new polygons, if shift mode
54+ else if ( ( ! e . shiftKey && ! e . altKey ) || ( ( e . shiftKey || e . altKey ) && ! plotinfo . selection ) ) {
55+ plotinfo . selection = { } ;
56+ plotinfo . selection . polygons = dragOptions . polygons = [ ] ;
57+ plotinfo . selection . mergedPolygons = dragOptions . mergedPolygons = [ ] ;
58+ }
4359
4460 if ( mode === 'lasso' ) {
45- pts = filteredPolygon ( [ [ x0 , y0 ] ] , constants . BENDPX ) ;
61+ filterPoly = filteredPolygon ( [ [ x0 , y0 ] ] , constants . BENDPX ) ;
4662 }
4763
4864 var outlines = zoomLayer . selectAll ( 'path.select-outline' ) . data ( [ 1 , 2 ] ) ;
@@ -129,20 +145,18 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
129145 }
130146 } ;
131147 } else {
132- fillRangeItems = function ( eventData , poly , pts ) {
148+ fillRangeItems = function ( eventData , currentPolygon , filterPoly ) {
133149 var dataPts = eventData . lassoPoints = { } ;
134150
135151 for ( i = 0 ; i < allAxes . length ; i ++ ) {
136152 var ax = allAxes [ i ] ;
137- dataPts [ ax . _id ] = pts . filtered . map ( axValue ( ax ) ) ;
153+ dataPts [ ax . _id ] = filterPoly . filtered . map ( axValue ( ax ) ) ;
138154 }
139155 } ;
140156 }
141157 }
142158
143159 dragOptions . moveFn = function ( dx0 , dy0 ) {
144- var poly ;
145-
146160 x1 = Math . max ( 0 , Math . min ( pw , dx0 + x0 ) ) ;
147161 y1 = Math . max ( 0 , Math . min ( ph , dy0 + y0 ) ) ;
148162
@@ -152,46 +166,79 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
152166 if ( mode === 'select' ) {
153167 if ( dy < Math . min ( dx * 0.6 , MINSELECT ) ) {
154168 // horizontal motion: make a vertical box
155- poly = polygonTester ( [ [ x0 , 0 ] , [ x0 , ph ] , [ x1 , ph ] , [ x1 , 0 ] ] ) ;
169+ currentPolygon = [ [ x0 , 0 ] , [ x0 , ph ] , [ x1 , ph ] , [ x1 , 0 ] ] ;
170+ currentPolygon . xmin = Math . min ( x0 , x1 ) ;
171+ currentPolygon . xmax = Math . max ( x0 , x1 ) ;
172+ currentPolygon . ymin = Math . min ( 0 , ph ) ;
173+ currentPolygon . ymax = Math . max ( 0 , ph ) ;
156174 // extras to guide users in keeping a straight selection
157- corners . attr ( 'd' , 'M' + poly . xmin + ',' + ( y0 - MINSELECT ) +
175+ corners . attr ( 'd' , 'M' + currentPolygon . xmin + ',' + ( y0 - MINSELECT ) +
158176 'h-4v' + ( 2 * MINSELECT ) + 'h4Z' +
159- 'M' + ( poly . xmax - 1 ) + ',' + ( y0 - MINSELECT ) +
177+ 'M' + ( currentPolygon . xmax - 1 ) + ',' + ( y0 - MINSELECT ) +
160178 'h4v' + ( 2 * MINSELECT ) + 'h-4Z' ) ;
161179
162180 }
163181 else if ( dx < Math . min ( dy * 0.6 , MINSELECT ) ) {
164182 // 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 +
183+ currentPolygon = [ [ 0 , y0 ] , [ 0 , y1 ] , [ pw , y1 ] , [ pw , y0 ] ] ;
184+ currentPolygon . xmin = Math . min ( 0 , pw ) ;
185+ currentPolygon . xmax = Math . max ( 0 , pw ) ;
186+ currentPolygon . ymin = Math . min ( y0 , y1 ) ;
187+ currentPolygon . ymax = Math . max ( y0 , y1 ) ;
188+ corners . attr ( 'd' , 'M' + ( x0 - MINSELECT ) + ',' + currentPolygon . ymin +
167189 'v-4h' + ( 2 * MINSELECT ) + 'v4Z' +
168- 'M' + ( x0 - MINSELECT ) + ',' + ( poly . ymax - 1 ) +
190+ 'M' + ( x0 - MINSELECT ) + ',' + ( currentPolygon . ymax - 1 ) +
169191 'v4h' + ( 2 * MINSELECT ) + 'v-4Z' ) ;
170192 }
171193 else {
172194 // diagonal motion
173- poly = polygonTester ( [ [ x0 , y0 ] , [ x0 , y1 ] , [ x1 , y1 ] , [ x1 , y0 ] ] ) ;
195+ currentPolygon = [ [ x0 , y0 ] , [ x0 , y1 ] , [ x1 , y1 ] , [ x1 , y0 ] ] ;
196+ currentPolygon . xmin = Math . min ( x0 , x1 ) ;
197+ currentPolygon . xmax = Math . max ( x0 , x1 ) ;
198+ currentPolygon . ymin = Math . min ( y0 , y1 ) ;
199+ currentPolygon . ymax = Math . max ( y0 , y1 ) ;
174200 corners . attr ( 'd' , 'M0,0Z' ) ;
175201 }
176- outlines . attr ( 'd' , 'M' + poly . xmin + ',' + poly . ymin +
177- 'H' + ( poly . xmax - 1 ) + 'V' + ( poly . ymax - 1 ) +
178- 'H' + poly . xmin + 'Z' ) ;
179202 }
180203 else if ( mode === 'lasso' ) {
181- pts . addPt ( [ x1 , y1 ] ) ;
182- poly = polygonTester ( pts . filtered ) ;
183- outlines . attr ( 'd' , 'M' + pts . filtered . join ( 'L' ) + 'Z' ) ;
204+ filterPoly . addPt ( [ x1 , y1 ] ) ;
205+ currentPolygon = filterPoly . filtered ;
184206 }
185207
208+ // create outline & tester
209+ if ( dragOptions . polygons && dragOptions . polygons . length ) {
210+ mergedPolygons = mergePolygons ( dragOptions . mergedPolygons , currentPolygon , subtract ) ;
211+ currentPolygon . subtract = subtract ;
212+ testPoly = multipolygonTester ( dragOptions . polygons . concat ( [ currentPolygon ] ) ) ;
213+ }
214+ else {
215+ mergedPolygons = [ currentPolygon ] ;
216+ testPoly = polygonTester ( currentPolygon ) ;
217+ }
218+
219+ // draw selection
220+ var paths = [ ] ;
221+ for ( i = 0 ; i < mergedPolygons . length ; i ++ ) {
222+ var ppts = mergedPolygons [ i ] ;
223+ paths . push ( ppts . join ( 'L' ) + 'L' + ppts [ 0 ] ) ;
224+ }
225+ outlines . attr ( 'd' , 'M' + paths . join ( 'M' ) + 'Z' ) ;
226+
186227 throttle . throttle (
187228 throttleID ,
188229 constants . SELECTDELAY ,
189230 function ( ) {
190231 selection = [ ] ;
232+
233+ var traceSelections = [ ] , traceSelection ;
191234 for ( i = 0 ; i < searchTraces . length ; i ++ ) {
192235 searchInfo = searchTraces [ i ] ;
236+
237+ traceSelection = searchInfo . selectPoints ( searchInfo , testPoly ) ;
238+ traceSelections . push ( traceSelection ) ;
239+
193240 var thisSelection = fillSelectionItem (
194- searchInfo . selectPoints ( searchInfo , poly ) , searchInfo
241+ traceSelection , searchInfo
195242 ) ;
196243 if ( selection . length ) {
197244 for ( var j = 0 ; j < thisSelection . length ; j ++ ) {
@@ -202,14 +249,15 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
202249 }
203250
204251 eventData = { points : selection } ;
205- fillRangeItems ( eventData , poly , pts ) ;
252+ fillRangeItems ( eventData , currentPolygon , filterPoly ) ;
206253 dragOptions . gd . emit ( 'plotly_selecting' , eventData ) ;
207254 }
208255 ) ;
209256 } ;
210257
211258 dragOptions . doneFn = function ( dragged , numclicks ) {
212259 corners . remove ( ) ;
260+
213261 throttle . done ( throttleID ) . then ( function ( ) {
214262 throttle . clear ( throttleID ) ;
215263
@@ -226,10 +274,46 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
226274 else {
227275 dragOptions . gd . emit ( 'plotly_selected' , eventData ) ;
228276 }
277+
278+ if ( currentPolygon && dragOptions . polygons ) {
279+ // save last polygons
280+ currentPolygon . subtract = subtract ;
281+ dragOptions . polygons . push ( currentPolygon ) ;
282+
283+ // we have to keep reference to arrays container
284+ dragOptions . mergedPolygons . length = 0 ;
285+ [ ] . push . apply ( dragOptions . mergedPolygons , mergedPolygons ) ;
286+ }
229287 } ) ;
230288 } ;
231289} ;
232290
291+ function mergePolygons ( list , poly , subtract ) {
292+ var res ;
293+
294+ if ( subtract ) {
295+ res = polybool . difference ( {
296+ regions : list ,
297+ inverted : false
298+ } , {
299+ regions : [ poly ] ,
300+ inverted : false
301+ } ) ;
302+
303+ return res . regions ;
304+ }
305+
306+ res = polybool . union ( {
307+ regions : list ,
308+ inverted : false
309+ } , {
310+ regions : [ poly ] ,
311+ inverted : false
312+ } ) ;
313+
314+ return res . regions ;
315+ }
316+
233317function fillSelectionItem ( selection , searchInfo ) {
234318 if ( Array . isArray ( selection ) ) {
235319 var trace = searchInfo . cd [ 0 ] . trace ;
0 commit comments