99
1010'use strict' ;
1111
12+ var polybool = require ( 'poly-bool' ) ;
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,10 @@ 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 ;
4345
4446 if ( mode === 'lasso' ) {
45- pts = filteredPolygon ( [ [ x0 , y0 ] ] , constants . BENDPX ) ;
47+ filterPoly = filteredPolygon ( [ [ x0 , y0 ] ] , constants . BENDPX ) ;
4648 }
4749
4850 var outlines = zoomLayer . selectAll ( 'path.select-outline' ) . data ( [ 1 , 2 ] ) ;
@@ -115,34 +117,33 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
115117 fillRangeItems = plotinfo . fillRangeItems ;
116118 } else {
117119 if ( mode === 'select' ) {
118- fillRangeItems = function ( eventData , poly ) {
120+ fillRangeItems = function ( eventData , currentPolygon ) {
119121 var ranges = eventData . range = { } ;
120122
121123 for ( i = 0 ; i < allAxes . length ; i ++ ) {
122124 var ax = allAxes [ i ] ;
123125 var axLetter = ax . _id . charAt ( 0 ) ;
126+ var x = axLetter === 'x' ;
124127
125128 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 ] )
128131 ] . sort ( ascending ) ;
129132 }
130133 } ;
131134 } else {
132- fillRangeItems = function ( eventData , poly , pts ) {
135+ fillRangeItems = function ( eventData , currentPolygon , filterPoly ) {
133136 var dataPts = eventData . lassoPoints = { } ;
134137
135138 for ( i = 0 ; i < allAxes . length ; i ++ ) {
136139 var ax = allAxes [ i ] ;
137- dataPts [ ax . _id ] = pts . filtered . map ( axValue ( ax ) ) ;
140+ dataPts [ ax . _id ] = filterPoly . filtered . map ( axValue ( ax ) ) ;
138141 }
139142 } ;
140143 }
141144 }
142145
143146 dragOptions . moveFn = function ( dx0 , dy0 ) {
144- var poly ;
145-
146147 x1 = Math . max ( 0 , Math . min ( pw , dx0 + x0 ) ) ;
147148 y1 = Math . max ( 0 , Math . min ( ph , dy0 + y0 ) ) ;
148149
@@ -152,46 +153,66 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
152153 if ( mode === 'select' ) {
153154 if ( dy < Math . min ( dx * 0.6 , MINSELECT ) ) {
154155 // 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 ] ] ;
156157 // 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 ) +
158159 'h-4v' + ( 2 * MINSELECT ) + 'h4Z' +
159- 'M' + ( poly . xmax - 1 ) + ',' + ( y0 - MINSELECT ) +
160+ 'M' + ( Math . max ( x0 , x1 ) - 1 ) + ',' + ( y0 - MINSELECT ) +
160161 'h4v' + ( 2 * MINSELECT ) + 'h-4Z' ) ;
161162
162163 }
163164 else if ( dx < Math . min ( dy * 0.6 , MINSELECT ) ) {
164165 // 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 ) +
167168 'v-4h' + ( 2 * MINSELECT ) + 'v4Z' +
168- 'M' + ( x0 - MINSELECT ) + ',' + ( poly . ymax - 1 ) +
169+ 'M' + ( x0 - MINSELECT ) + ',' + ( Math . max ( y0 , y1 ) - 1 ) +
169170 'v4h' + ( 2 * MINSELECT ) + 'v-4Z' ) ;
170171 }
171172 else {
172173 // diagonal motion
173- poly = polygonTester ( [ [ x0 , y0 ] , [ x0 , y1 ] , [ x1 , y1 ] , [ x1 , y0 ] ] ) ;
174+ currentPolygon = [ [ x0 , y0 ] , [ x0 , y1 ] , [ x1 , y1 ] , [ x1 , y0 ] ] ;
174175 corners . attr ( 'd' , 'M0,0Z' ) ;
175176 }
176- outlines . attr ( 'd' , 'M' + poly . xmin + ',' + poly . ymin +
177- 'H' + ( poly . xmax - 1 ) + 'V' + ( poly . ymax - 1 ) +
178- 'H' + poly . xmin + 'Z' ) ;
179177 }
180178 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 ] ) ;
184198 }
199+ outlines . attr ( 'd' , 'M' + paths . join ( 'M' ) + 'Z' ) ;
185200
186201 throttle . throttle (
187202 throttleID ,
188203 constants . SELECTDELAY ,
189204 function ( ) {
190205 selection = [ ] ;
206+
207+ var traceSelections = [ ] , traceSelection ;
191208 for ( i = 0 ; i < searchTraces . length ; i ++ ) {
192209 searchInfo = searchTraces [ i ] ;
210+
211+ traceSelection = searchInfo . selectPoints ( searchInfo , testPoly ) ;
212+ traceSelections . push ( traceSelection ) ;
213+
193214 var thisSelection = fillSelectionItem (
194- searchInfo . selectPoints ( searchInfo , poly ) , searchInfo
215+ traceSelection , searchInfo
195216 ) ;
196217 if ( selection . length ) {
197218 for ( var j = 0 ; j < thisSelection . length ; j ++ ) {
@@ -202,7 +223,7 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
202223 }
203224
204225 eventData = { points : selection } ;
205- fillRangeItems ( eventData , poly , pts ) ;
226+ fillRangeItems ( eventData , currentPolygon , filterPoly ) ;
206227 dragOptions . gd . emit ( 'plotly_selecting' , eventData ) ;
207228 }
208229 ) ;
@@ -226,6 +247,12 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
226247 else {
227248 dragOptions . gd . emit ( 'plotly_selected' , eventData ) ;
228249 }
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 ) ) ;
229256 } ) ;
230257 } ;
231258} ;
0 commit comments