Implement speed-up for the case of a drawn way intersecting with another way. Do...
authorSteve Bennett <stevagewp@gmail.com>
Sun, 19 Dec 2010 11:02:37 +0000 (11:02 +0000)
committerSteve Bennett <stevagewp@gmail.com>
Sun, 19 Dec 2010 11:02:37 +0000 (11:02 +0000)
Also a bunch of ASDoc documentation.

net/systemeD/halcyon/EntityUI.as
net/systemeD/halcyon/MapPaint.as
net/systemeD/halcyon/NodeUI.as
net/systemeD/halcyon/WayUI.as
net/systemeD/potlatch2/EditController.as
net/systemeD/potlatch2/controller/DrawWay.as

index b4ba411..69800e0 100644 (file)
@@ -9,22 +9,37 @@ package net.systemeD.halcyon {
        import net.systemeD.halcyon.styleparser.RuleSet;
     import net.systemeD.halcyon.connection.*;
 
+       /** Parent class of representations of map Entities, with properties about how they should be drawn. */ 
        public class EntityUI {
 
+               /** The entity represented by this class. */
                protected var entity:Entity;
-               protected var styleList:StyleList;                              // current StyleList for this entity
-               protected var sprites:Array=new Array();                // instances in display list
-               protected var listenSprite:Sprite=new Sprite(); // clickable sprite to receive events
-               protected var hitzone:Sprite;                                   // hitzone for above
-               protected var stateClasses:Object=new Object(); // special context-sensitive classes, e.g. :hover
-               protected var layer:Number=0;                                   // map layer
-               protected var suspended:Boolean=false;                  // suspend redrawing?
-               protected var redrawDue:Boolean=false;                  // redraw called while suspended?
-               protected var clearLimit:uint=0;                                // sprite to clear back to
-               public var paint:MapPaint;                                              // reference to parent MapPaint
-               public var ruleset:RuleSet;                                             // reference to ruleset in operation
-               public var interactive:Boolean=true;                    // does object respond to clicks?
-               public var purgable:Boolean=true;                               // can it be deleted when offscreen?
+               /** Current StyleList for this entity. */
+               protected var styleList:StyleList;
+               /** Instances in display list */
+               protected var sprites:Array=new Array();
+               /** The clickable sprite that will receive events. */
+               protected var listenSprite:Sprite=new Sprite();
+               /** Hitzone for the sprite */
+               protected var hitzone:Sprite;
+               /** Special context-sensitive classes such as :hover. */
+               protected var stateClasses:Object=new Object();
+               /** Map layer */
+               protected var layer:Number=0;
+               /** Is drawing suspended? */
+               protected var suspended:Boolean=false;  
+               /** Redraw called while suspended? */
+               protected var redrawDue:Boolean=false;
+               /** Sprite to clear back to */
+               protected var clearLimit:uint=0;
+               /** Reference to parent MapPaint */
+               public var paint:MapPaint;      
+               /** Reference to ruleset (MapCSS) in operation */
+               public var ruleset:RuleSet;
+               /** Does object respond to clicks? */
+               public var interactive:Boolean=true;
+               /** Can it be deleted when offscreen? */
+               public var purgable:Boolean=true;
 
                protected const FILLSPRITE:uint=0;
                protected const CASINGSPRITE:uint=1;
@@ -39,6 +54,7 @@ package net.systemeD.halcyon {
                        gridFitType: GridFitType.NONE
                };
 
+               /** Constructor function, adds a bunch of event listeners. */
                public function EntityUI(entity:Entity, paint:MapPaint) {
                        this.entity=entity;
                        this.paint=paint;
@@ -56,6 +72,7 @@ package net.systemeD.halcyon {
                        listenSprite.addEventListener(MouseEvent.MOUSE_MOVE, mouseEvent);
                }
 
+               /** Remove the default event listeners. */
                protected function removeGenericEventListeners():void {
             entity.removeEventListener(Connection.TAG_CHANGED, tagChanged);
                        entity.removeEventListener(Connection.ADDED_TO_RELATION, relationAdded);
@@ -110,7 +127,7 @@ package net.systemeD.halcyon {
 
                // -----------------------------------------------------------------
 
-               // Add object (stroke/fill/roadname) to layer sprite
+               /** Add object (stroke/fill/roadname) to layer sprite*/
                
                protected function addToLayer(s:DisplayObject,t:uint,sublayer:int=-1):void {
                        var l:Sprite, o:Sprite;
@@ -146,6 +163,7 @@ package net.systemeD.halcyon {
                        }
                }
 
+               /** Remove all sprites associated with this entity, and clear hitzone. */
                public function removeSprites():void {
                        while (sprites.length>clearLimit) {
                                var d:DisplayObject=sprites.pop();
@@ -207,23 +225,25 @@ package net.systemeD.halcyon {
                        return "[EntityUI "+entity+"]";
                }
 
-               // Redraw control
+               /** Request redraw */
                
                public function redraw():Boolean {
                        if (suspended) { redrawDue=true; return false; }
                        return doRedraw();
                }
                
+               /** Actually do the redraw. To be overwritten. */
                public function doRedraw():Boolean {
-                       // to be overwritten
                        return false;
                }
                
+               /** Temporarily suspend redrawing of object. */
                public function suspendRedraw(event:EntityEvent):void {
                        suspended=true;
                        redrawDue=false;
                }
                
+               /** Resume redrawing. */
                public function resumeRedraw(event:EntityEvent):void {
                        suspended=false;
                        if (redrawDue) { 
index 47b944f..62e0a37 100644 (file)
@@ -11,15 +11,25 @@ package net.systemeD.halcyon {
        /** Manages the drawing of map entities, allocating their sprites etc. */
        public class MapPaint extends Sprite {
                
+               /** Access Map object */
                public var map:Map;
+               /** Entities on layers below minlayer will not be shown by this paint object. (Confirm?) */
                public var minlayer:int;
+               /** Entities on layers above maxlayer will not be shown by this paint object. (Confirm?) */
                public var maxlayer:int;
-               public var ruleset:RuleSet;                                             // rules
+               /** The MapCSS rules used for drawing entities. */
+               public var ruleset:RuleSet;                                             
+               /** WayUI objects attached to Way entities that are currently visible. */
                public var wayuis:Object=new Object();                  // sprites for ways and (POI/tagged) nodes
-               public var nodeuis:Object=new Object();                 //      |
+               /** NodeUI objects attached to POI/tagged node entities that are currently visible. */
+               // confirm this - it seems to me all nodes in ways get nodeuis? --Steve B
+               public var nodeuis:Object=new Object();
+               /** MarkerUI objects attached to Marker entities that are currently visible. */
         public var markeruis:Object=new Object();
-               public var isBackground:Boolean = true;                 // is it a background layer or the core paint object?
-               public var sublayerIndex:Object={};                             // hash of index->position
+        /** Is this a background layer or the core paint object? */
+               public var isBackground:Boolean = true;
+               /** Hash of index->position */
+               public var sublayerIndex:Object={};
 
                private const VERYBIG:Number=Math.pow(2,16);
                private static const NO_LAYER:int=-99999;               // same as NodeUI
@@ -56,6 +66,7 @@ package net.systemeD.halcyon {
                        }
                }
                
+               /** Returns the paint surface for the given layer. */
                public function getPaintSpriteAt(l:int):Sprite {
                        return getChildAt(l-minlayer) as Sprite;
                }
@@ -64,6 +75,7 @@ package net.systemeD.halcyon {
                        return getChildAt((l-minlayer) + (maxlayer-minlayer+1)) as Sprite;
                }
                
+               /** Is ruleset loaded? */
                public function get ready():Boolean {
                        if (!ruleset) { return false; }
                        if (!ruleset.loaded) { return false; }
@@ -124,6 +136,8 @@ package net.systemeD.halcyon {
         * @param redraw If true, all UIs for entities on "inside" lists will be redrawn
         * @param remove If true, all UIs for entites on "outside" lists will be removed. The purgable flag on UIs
                         can override this, for example for selected objects.
+        * fixme? add smarter behaviour for way nodes - remove NodeUIs from way nodes off screen, create them for ones
+        * that scroll onto screen (for highlights etc)
         */
                public function updateEntityUIs(o:Object, redraw:Boolean, remove:Boolean):void {
                        var way:Way, poi:Node, marker:Marker;
@@ -131,6 +145,7 @@ package net.systemeD.halcyon {
                        for each (way in o.waysInside) {
                                if (!wayuis[way.id]) { createWayUI(way); }
                                else if (redraw) { wayuis[way.id].recalculate(); wayuis[way.id].redraw(); }
+                               else wayuis[way.id].updateHighlights();//dubious
                        }
 
                        if (remove) {
@@ -183,6 +198,7 @@ package net.systemeD.halcyon {
                        return wayuis[way.id];
                }
 
+               /** Respond to event by removing the WayUI. */
                public function wayDeleted(event:EntityEvent):void {
                        deleteWayUI(event.entity as Way);
                }
@@ -216,6 +232,7 @@ package net.systemeD.halcyon {
                        return nodeuis[node.id];
                }
 
+               /** Respond to event by deleting NodeUI. */
                public function nodeDeleted(event:EntityEvent):void {
                        deleteNodeUI(event.entity as Node);
                }
@@ -243,6 +260,7 @@ package net.systemeD.halcyon {
             return markeruis[marker.id];
         }
 
+        /** Respond to event by deleting MarkerUI. */
         public function markerDeleted(event:EntityEvent):void {
             deleteMarkerUI(event.entity as Marker);
         }
index 04347c8..3d03bdd 100644 (file)
@@ -72,6 +72,7 @@ package net.systemeD.halcyon {
                        return renderFromStyle(tags);
                }
 
+               /** Assemble the layers of icons to draw the node, with hit zone if interactive. */
                private function renderFromStyle(tags:Object):Boolean {
                        var r:Boolean=false;                    // ** rendered
                        var maxwidth:Number=4;                  // biggest width
index ce398c4..2bd5c7a 100644 (file)
@@ -11,19 +11,29 @@ package net.systemeD.halcyon {
     import net.systemeD.halcyon.connection.*;
        import net.systemeD.halcyon.Globals;
 
+       /** The graphical representation of a Way. */ 
        public class WayUI extends EntityUI {
 
-               public var pathlength:Number;                           // length of path
-               public var patharea:Number;                                     // area of path
-               public var centroid_x:Number;                           // centroid
+               /** Total length of way */
+               public var pathlength:Number;
+               /** Area of the way */
+               public var patharea:Number;
+               /** X coord of the centre of the area */
+               public var centroid_x:Number;
+               /** Y coord of the centre of the area */
                public var centroid_y:Number;                           //  |
-               public var heading:Array=new Array();           // angle at each node
-               public var drawExcept:Number;                           // vertex to draw exclusively, or not at all (used by DragWayNode)
-               public var drawOnly:Number;                                     //  |
-               private var indexStart:uint;                            //  |
-               private var indexEnd:uint;                                      //  |
+               /** Angle at each node */
+               public var heading:Array=new Array();
+               /** vertex to draw exclusively, or not at all (used by DragWayNode) */ 
+               public var drawExcept:Number;
+               /** " */
+               public var drawOnly:Number;
+               private var indexStart:uint;
+               private var indexEnd:uint;
                public var nameformat:TextFormat;
                private var recalculateDue:Boolean=false;
+               // Store the temporary highlight settings applied to all nodes.
+               private var nodehighlightsettings: Object={};
 
                private const NODESIZE:uint=6;
 
@@ -103,17 +113,20 @@ package net.systemeD.halcyon {
                        redrawMultis();
                }
 
+               /** Don't redraw until further notice. */
                override public function suspendRedraw(event:EntityEvent):void {
                        super.suspendRedraw(event);
                        recalculateDue=false;
                }
                
+               /** Continue redrawing as normal. */
                override public function resumeRedraw(event:EntityEvent):void {
                        suspended=false;
                        if (recalculateDue) { recalculate(); }
                        super.resumeRedraw(event);
                }
 
+               /** Redraw other ways that are the "outer" part of a multipolygon of which we are the "inner" */
                public function redrawMultis():void {
                        var multis:Array=entity.findParentRelationsOfType('multipolygon','inner');
                        for each (var m:Relation in multis) {
@@ -125,21 +138,44 @@ package net.systemeD.halcyon {
                                }
                        }
                }
-
+        
+        /** Mark every node in this way as highlighted, and redraw it. 
+        * 
+        * @param settings Style state that it applies to. @see EntityUI#setStateClass()
+        *  */
         public function setHighlightOnNodes(settings:Object):void {
                        for (var i:uint = 0; i < Way(entity).length; i++) {
                 var node:Node = Way(entity).getNode(i);
                                if (paint.nodeuis[node.id]) {
-                                       paint.nodeuis[node.id].setHighlight(settings);
-                                       paint.nodeuis[node.id].redraw();
+                                       // Speed things up a bit by only setting the highlight if it's either:
+                                       // a) an "un-highlight" (so we don't leave mess behind when scrolling)
+                                       // b) currently onscreen
+                                       // Currently this means if you highlight an object then scroll, nodes will scroll
+                                       // into view that should be highlighted but aren't.
+                                       if (settings.hoverway==false || 
+                                           settings.selectedway==false || 
+                                           node.lat >=  paint.map.edge_b && node.lat <= paint.map.edge_t &&
+                                           node.lon >= paint.map.edge_l && node.lon <= paint.map.edge_r) {
+                                           paint.nodeuis[node.id].setHighlight(settings); // Triggers redraw if required
+                                       }
+                                       if (settings.selectedway  || settings.hoverway)
+                                               nodehighlightsettings=settings;
+                                       else
+                                          nodehighlightsettings={}; 
                                }
                        }
         }
+        
+        // An ugly hack to allow nodes that have recently scrolled into view to get highlighted.
+        public function updateHighlights():void {
+               if (nodehighlightsettings)
+                  setHighlightOnNodes(nodehighlightsettings);
+        }
 
                // ------------------------------------------------------------------------------------------
-               // Calculate length etc.
-               // ** this could be made scale-independent - would speed up redraw
-               
+               /** Calculate pathlength, patharea, centroid_x, centroid_y, heading[] 
+               * ** this could be made scale-independent - would speed up redraw
+               */
                public function recalculate():void {
                        if (suspended) { recalculateDue=true; return; }
                        
@@ -186,8 +222,8 @@ package net.systemeD.halcyon {
                }
 
                // ------------------------------------------------------------------------------------------
-               // Redraw
-
+       
+               /** Go through the complex process of drawing this way, including applying styles, casings, fills, fonts... */
                override public function doRedraw():Boolean {
                        interactive=false;
                        removeSprites();
@@ -324,7 +360,7 @@ package net.systemeD.halcyon {
                // ------------------------------------------------------------------------------------------
                // Drawing support functions
 
-               // Draw solid polyline
+               /** Draw solid polyline */
                
                public function solidLines(g:Graphics,inners:Array):void {
                        solidLine(g);
@@ -358,7 +394,7 @@ package net.systemeD.halcyon {
                        }
                }
 
-               // Draw dashed polyline
+               /** Draw dashed polyline */
                
                private function dashedLine(g:Graphics,dashes:Array):Array {
                        var way:Way=entity as Way;
@@ -413,7 +449,7 @@ package net.systemeD.halcyon {
                                 else { g.moveTo(x,y); }
                }
 
-               // Draw decoration (arrows etc.)
+               /** Draw decoration (arrows etc.) */
                
                private function lineDecoration(g:Graphics,s:ShapeStyle,segments:Array):void {
                        var c:int=s.color ? s.color : 0;
@@ -442,9 +478,9 @@ package net.systemeD.halcyon {
                }
 
                
-               // Find point partway (0-1) along a path
-               // returns (x,y,angle)
-               // inspired by senocular's Path.as
+               /** Find point partway (0-1) along a path
+                 * @return (x,y,angle)
+                 * inspired by senocular's Path.as */
                
                private function pointAt(t:Number):Array {
                        var way:Way=entity as Way;
@@ -463,8 +499,9 @@ package net.systemeD.halcyon {
                        return new Array(0, 0, 0);
                }
 
-               // Draw name along path
-               // based on code by Tom Carden
+               /** Draw name along path
+                * based on code by Tom Carden
+                * */
                
                private function writeNameOnPath(s:Sprite,a:String,textOffset:Number=0):void {
 
@@ -539,8 +576,8 @@ package net.systemeD.halcyon {
                }
 
                // ------------------------------------------------------------------------------------------
-               // Interaction
-
+               /* Interaction */
+        // TODO: can this be sped up? Hit testing for long ways (that go off the screen) seems to be very slow. */
                public function hitTest(x:Number, y:Number):Way {
                        if (hitzone.hitTestPoint(x,y,true)) { return entity as Way; }
                        return null;
index b3aabcc..fd4bec0 100644 (file)
@@ -12,6 +12,8 @@ package net.systemeD.potlatch2 {
        import flash.geom.*;
        import flash.ui.Keyboard;
 
+    /** Controller for the main map editing window itself. The logic that responds to mouse and keyboard events is all 
+    * buried in various ControllerState classes. */
     public class EditController implements MapController {
 
         private var _map:Map;
@@ -35,6 +37,7 @@ package net.systemeD.potlatch2 {
                [Embed(source="../../../embedded/pen_so.png")]          public var pen_so:Class;
                [Embed(source="../../../embedded/pen_plus.png")]        public var pen_plus:Class;
                
+        /** Constructor function: needs the map information, a panel to edit tags with, and the toolbox to manipulate ways with. */
         public function EditController(map:Map, tagViewer:TagViewer, toolbox:Toolbox) {
             this._map = map;
             setState(new NoSelection());
@@ -63,10 +66,12 @@ package net.systemeD.potlatch2 {
             _connection = map.connection;
         }
 
+        /** Accesses map object. */
         public function get map():Map {
             return _map;
         }
         
+        /** Accesss connection object. */
         public function get connection():Connection {
             return _connection;
         }
@@ -96,6 +101,7 @@ package net.systemeD.potlatch2 {
             setState(newState);            
                }
 
+               /** Is the given key currently pressed? */
                public function keyDown(key:Number):Boolean {
                        return Boolean(keys[key]);
                }
@@ -135,6 +141,7 @@ package net.systemeD.potlatch2 {
                        return true;
                }
 
+        /** Exit the current state and switch to a new one. */
         public function setState(newState:ControllerState):void {
             if ( newState == state )
                 return;
@@ -147,6 +154,7 @@ package net.systemeD.potlatch2 {
             state.enterState();
         }
 
+               /** Given what is currently selected (or not), find the matching ControllerState. */
                public function findStateForSelection(sel:Array):ControllerState {
                        if (sel.length==0) { return new NoSelection(); }
                        else if (sel.length>1) { return new SelectedMultiple(sel); }
@@ -159,6 +167,7 @@ package net.systemeD.potlatch2 {
                        }
                }
 
+               /** Set a mouse pointer. */
                public function setCursor(cursor:Class):void {
                        CursorManager.removeAllCursors();
                        if (cursor && cursorsEnabled) { CursorManager.setCursor(cursor,2,-4,0); }
index d2ad610..11487b7 100644 (file)
@@ -44,7 +44,7 @@ package net.systemeD.potlatch2.controller {
 
                        if ( event.type == MouseEvent.MOUSE_UP ) {
                 controller.map.mouseUpHandler(); // in case you're still in the drag-tolerance zone, and mouse up over something.
-                               if ( entity == null || isBackground ) {
+                               if ( entity == null || isBackground ) { // didn't hit anything: extend the way by one node.
                                        node = createAndAddNode(event, MainUndoStack.getGlobalStack().addAction);
                     controller.map.setHighlight(node, { selectedway: true });
                     controller.map.setPurgable([node], false);
@@ -53,7 +53,7 @@ package net.systemeD.potlatch2.controller {
                                } else if ( entity is Node ) {
                                        if (entity==lastClick && (new Date().getTime()-lastClickTime.getTime())<1000) {
                                                if (Way(firstSelected).length==1 && Way(firstSelected).getNode(0).parentWays.length==1) {
-                                                       // double-click to create new POI
+                                                       // Actually the user double-clicked to make a new node, they didn't want to draw a way at all.
                             stopDrawing();
                             MainUndoStack.getGlobalStack().undo(); // undo the BeginWayAction that (presumably?) just happened
                             
@@ -71,6 +71,7 @@ package net.systemeD.potlatch2.controller {
                         // clicked slowly on the end node - do nothing
                         return this;
                                        } else {
+                                               // hit a node, add it to this way and carry on
                                                appendNode(entity as Node, MainUndoStack.getGlobalStack().addAction);
                                                if (focus is Way) {
                           controller.map.setHighlightOnNodes(focus as Way, { hoverway: false });
@@ -79,6 +80,7 @@ package net.systemeD.potlatch2.controller {
                                                resetElastic(entity as Node);
                                                lastClick=entity;
                                                if (Way(firstSelected).getNode(0)==Way(firstSelected).getLastNode()) {
+                                                       // the node just hit completes a loop, so stop drawing.
                                                        return new SelectedWay(firstSelected as Way);
                                                }
                                        }
@@ -108,15 +110,21 @@ package net.systemeD.potlatch2.controller {
                                }
                                lastClickTime=new Date();
                        } else if ( event.type == MouseEvent.MOUSE_MOVE && elastic ) {
+                               // mouse is roaming around freely
                                mouse = new Point(
                                                  controller.map.coord2lon(event.localX),
                                                  controller.map.coord2latp(event.localY));
                                elastic.end = mouse;
                        } else if ( event.type == MouseEvent.ROLL_OVER && !isBackground ) {
+                               // mouse has floated over something
                                if (focus is Way && focus!=firstSelected) {
+                                       // floating over another way, highlight its nodes
                                        hoverEntity=focus;
+                                       // FIXME this call is incredibly slow for long ways, really hurts usability
                                        controller.map.setHighlightOnNodes(focus as Way, { hoverway: true });
                                }
+                               // set cursor depending on whether we're floating over the start of this way, 
+                               // another random node, a possible junction...
                                if (entity is Node && focus is Way && Way(focus).endsWith(Node(entity))) {
                                        if (focus==firstSelected) { controller.setCursor(controller.pen_so); }
                                                             else { controller.setCursor(controller.pen_o); }
@@ -160,12 +168,12 @@ package net.systemeD.potlatch2.controller {
 
                override public function processKeyboardEvent(event:KeyboardEvent):ControllerState {
                        switch (event.keyCode) {
-                               case 13:                                        return keyExitDrawing();
-                               case 27:                                        return keyExitDrawing();
+                               case Keyboard.ENTER:                                    return keyExitDrawing();
+                               case Keyboard.ESCAPE:                                   return keyExitDrawing();
                                case Keyboard.DELETE:           return backspaceNode(MainUndoStack.getGlobalStack().addAction);
                                case Keyboard.BACKSPACE:        return backspaceNode(MainUndoStack.getGlobalStack().addAction);
-                               case 189:                                       return backspaceNode(MainUndoStack.getGlobalStack().addAction);
-                               case 82:                                        repeatTags(firstSelected); return this;
+                               case 189: /* minus */       return backspaceNode(MainUndoStack.getGlobalStack().addAction);
+                               case 82: /* R */            repeatTags(firstSelected); return this;
                        }
                        var cs:ControllerState = sharedKeyboardEvents(event);
                        return cs ? cs : this;