Control-drag for selection by area
authorRichard Fairhurst <richard@systemeD.net>
Sun, 12 Jun 2011 06:59:00 +0000 (07:59 +0100)
committerRichard Fairhurst <richard@systemeD.net>
Sun, 12 Jun 2011 06:59:00 +0000 (07:59 +0100)
net/systemeD/halcyon/connection/Way.as
net/systemeD/potlatch2/controller/ControllerState.as
net/systemeD/potlatch2/controller/SelectArea.as [new file with mode: 0644]

index 8cefcf1..d5f991c 100644 (file)
@@ -51,7 +51,7 @@ package net.systemeD.halcyon.connection {
                            (edge_b>top    && edge_b>top   ) || deleted) { return false; }
                        return true;
                }
-        
+
         public function getNode(index:uint):Node {
             return nodes[index];
         }
@@ -291,5 +291,51 @@ package net.systemeD.halcyon.connection {
                        }
                        return null;
                }
+
+               public function intersects(left:Number,right:Number,top:Number,bottom:Number):Boolean {
+                       // simple test first: are any nodes contained?
+                       for (var i:uint=0; i<nodes.length; i++) {
+                               if (nodes[i].within(left,right,top,bottom)) return true;
+                       }
+                       // more complex test: do any segments cross?
+                       for (i=0; i<nodes.length-1; i++) {
+                               if (lineIntersectsRectangle(
+                                       nodes[i  ].lon, nodes[i  ].lat,
+                                       nodes[i+1].lon, nodes[i+1].lat,
+                                       left,right,top,bottom)) return true;
+                       }
+                       return false;
+               }
+               
+               private function lineIntersectsRectangle(x0:Number, y0:Number, x1:Number, y1:Number, l:Number, r:Number, b:Number, t:Number):Boolean {
+                       // from http://sebleedelisle.com/2009/05/super-fast-trianglerectangle-intersection-test/
+                       // note that t and b are transposed above because we're dealing with lat (top=90), not AS3 pixels (top=0)
+                       var m:Number = (y1-y0) / (x1-x0);
+                       var c:Number = y0 -(m*x0);
+                       var top_intersection:Number, bottom_intersection:Number;
+                       var toptrianglepoint:Number, bottomtrianglepoint:Number;
+
+                       if (m>0) {
+                               top_intersection = (m*l  + c);
+                               bottom_intersection = (m*r  + c);
+                       } else {
+                               top_intersection = (m*r  + c);
+                               bottom_intersection = (m*l  + c);
+                       }
+
+                       if (y0<y1) {
+                               toptrianglepoint = y0;
+                               bottomtrianglepoint = y1;
+                       } else {
+                               toptrianglepoint = y1;
+                               bottomtrianglepoint = y0;
+                       }
+
+                       var topoverlap:Number = top_intersection>toptrianglepoint ? top_intersection : toptrianglepoint;
+                       var botoverlap:Number = bottom_intersection<bottomtrianglepoint ? bottom_intersection : bottomtrianglepoint;
+                       return (topoverlap<botoverlap) && (!((botoverlap<t) || (topoverlap>b)));
+               }
+
+
     }
 }
index e7641c3..c9645ae 100644 (file)
@@ -94,14 +94,14 @@ package net.systemeD.potlatch2.controller {
             var focus:Entity = getTopLevelFocusEntity(entity);
 
                        if ( paint && paint.isBackground ) {
-                if ( event.type == MouseEvent.MOUSE_DOWN && entity.connection is SnapshotConnection) {
-                    if (entity is Way) { return new SelectedBackgroundWay(entity as Way); }
-                    else if (entity is Node) { return new SelectedBackgroundNode(entity as Node, paint); }
-                } else if ( event.type == MouseEvent.MOUSE_DOWN && ((event.shiftKey && event.ctrlKey) || event.altKey) ) {
+                               if (event.type == MouseEvent.MOUSE_DOWN && ((event.shiftKey && event.ctrlKey) || event.altKey) ) {
                                        // alt-click to pull data out of vector background layer
                                        var newEntity:Entity=paint.pullThrough(entity,editableLayer);
-                                       if (entity is Way) { return new SelectedWay(newEntity as Way); }
+                                       if      (entity is Way ) { return new SelectedWay(newEntity as Way); }
                                        else if (entity is Node) { return new SelectedPOINode(newEntity as Node); }
+                } else if (event.type == MouseEvent.MOUSE_DOWN) {
+                    if      (entity is Way ) { return new SelectedBackgroundWay(entity as Way); }
+                    else if (entity is Node) { return new SelectedBackgroundNode(entity as Node, paint); }
                 } else if (event.type == MouseEvent.MOUSE_DOWN && entity is Marker) {
                     return new SelectedMarker(entity as Marker, paint);
                                } else if ( event.type == MouseEvent.MOUSE_UP ) {
@@ -123,6 +123,8 @@ package net.systemeD.potlatch2.controller {
                                        return new DragSelection(selection, event);
                                } else if (entity) {
                                        return new DragSelection([entity], event);
+                               } else if (event.ctrlKey) {
+                                       return new SelectArea(event.localX,event.localY);
                                }
             } else if ( event.type == MouseEvent.CLICK && focus == null && map.dragstate!=map.DRAGGING && this is SelectedMarker) {
                 // this is identical to the below, but needed for unselecting markers on vector background layers.
diff --git a/net/systemeD/potlatch2/controller/SelectArea.as b/net/systemeD/potlatch2/controller/SelectArea.as
new file mode 100644 (file)
index 0000000..73cb2fc
--- /dev/null
@@ -0,0 +1,69 @@
+package net.systemeD.potlatch2.controller {
+
+       import flash.display.*;
+       import flash.events.*;
+       import net.systemeD.halcyon.connection.*;
+
+    public class SelectArea extends ControllerState {
+
+               private var startX:Number;
+               private var startY:Number;
+               private var endX:Number;
+               private var endY:Number;
+               private var box:Shape;
+               private const TOLERANCE:uint=4;
+
+               public function SelectArea(x:Number,y:Number) {
+                       startX=endX=x;
+                       startY=endY=y;
+               }
+
+        override public function processMouseEvent(event:MouseEvent, entity:Entity):ControllerState {
+            if (event.type==MouseEvent.MOUSE_MOVE) { 
+                               // ** FIXME: weird things happen if you mouse-over the drag-and-drop panel
+                               endX=event.localX;
+                               endY=event.localY;
+                               drawSelectionBox();
+                       } else if (event.type==MouseEvent.MOUSE_UP) { 
+                               // select everything within boundary
+                               var a:Number;
+                               if (startX>endX) { a=startX; startX=endX; endX=a; }
+                               if (startY>endY) { a=startY; startY=endY; endY=a; }
+                               if (endX-startX<TOLERANCE && endY-startY<TOLERANCE) { return new NoSelection(); }
+                               var left:Number=controller.map.coord2lon(startX);
+                               var right:Number=controller.map.coord2lon(endX);
+                               var top:Number=controller.map.coord2lat(startY);
+                               var bottom:Number=controller.map.coord2lat(endY);
+                               var entities:Object=editableLayer.connection.getObjectsByBbox(left,right,top,bottom);
+                               for each (var way:Way  in entities.waysInside) { if (way.intersects(left,right,top,bottom)) toggleSelection(way); }
+                               for each (var poi:Node in entities.poisInside) { toggleSelection(poi); }
+                               return controller.findStateForSelection(selection);
+                       }
+            return this;
+        }
+
+               private function drawSelectionBox():void {
+                       box.graphics.clear();
+                       box.graphics.beginFill(0xDDDDFF,0.5);
+                       box.graphics.lineStyle(1,0xFF0000);
+                       box.graphics.drawRect(startX,startY,endX-startX,endY-startY);
+               }
+               
+               override public function enterState():void {
+                       box=new Shape();
+                       var l:DisplayObject=editableLayer.getPaintSpriteAt(editableLayer.maxlayer);
+                       var o:DisplayObject=Sprite(l).getChildAt(3);
+                       (o as Sprite).addChild(box);
+                       controller.map.draggable=false;
+               }
+               override public function exitState(newState:ControllerState):void {
+                       box.parent.removeChild(box);
+                       controller.map.draggable=true;
+                       if (!newState.isSelectionState()) { controller.updateSelectionUI(); }
+               }
+               override public function toString():String {
+                       return "SelectArea";
+               }
+       }
+
+}