Merge branch 'master' into history
[potlatch2.git] / net / systemeD / halcyon / NodeUI.as
1 package net.systemeD.halcyon {
2
3         import flash.display.*;
4         import flash.events.*;
5         import flash.text.AntiAliasType;
6         import flash.text.GridFitType;
7         import flash.text.TextField;
8         import flash.text.TextFormat;
9         import flash.geom.Matrix;
10         import flash.geom.Point;
11         import net.systemeD.halcyon.styleparser.*;
12     import net.systemeD.halcyon.connection.*;
13     import net.systemeD.halcyon.ImageBank;
14         
15         /** The graphical representation of a Node (including POIs and nodes that are part of Ways). */
16         public class NodeUI extends EntityUI {
17                 
18                 public var loaded:Boolean=false;
19                 private var iconnames:Object={};                        // name of icon on each subpart
20                 private var heading:Number=0;                           // heading within way
21                 private var rotation:Number=0;                          // rotation applied to this POI
22                 private static const NO_LAYER:int=-99999;
23
24                 /**
25                  * @param node The corresponding Node.
26                  * @param paint MapPaint object to attach this NodeUI to.
27                  * @param heading Optional angle.
28                  * @param layer Which layer on the MapPaint object it sits on. @default Top layer
29                  * @param stateClasses A settings object definining the initial state of the node (eg, highlighted, hover...) */
30                 public function NodeUI(node:Node, paint:MapPaint, heading:Number=0, layer:int=NO_LAYER, stateClasses:Object=null) {
31                         super(node,paint);
32                         if (layer==NO_LAYER) { this.layer=paint.maxlayer; } else { this.layer=layer; }
33                         this.heading = heading;
34                         if (stateClasses) {
35                                 for (var state:String in stateClasses) {
36                                         if (stateClasses[state]) { this.stateClasses[state]=stateClasses[state]; }
37                                 }
38                         }
39                         entity.addEventListener(Connection.NODE_MOVED, nodeMoved, false, 0, true);
40             entity.addEventListener(Connection.NODE_ALTERED, nodeAltered, false, 0, true);
41             entity.addEventListener(Connection.ENTITY_DRAGGED, nodeDragged, false, 0, true);
42             attachRelationListeners();
43                         redraw();
44                 }
45                 
46                 public function removeEventListeners():void {
47                         removeGenericEventListeners();
48                         entity.removeEventListener(Connection.NODE_MOVED, nodeMoved);
49             entity.removeEventListener(Connection.NODE_ALTERED, nodeAltered);
50             entity.removeEventListener(Connection.ENTITY_DRAGGED, nodeDragged);
51                         removeRelationListeners();
52                 }
53
54                 /** Respond to movement event. */
55                 public function nodeMoved(event:Event):void {
56                     updatePosition();
57                 }
58
59         private function nodeAltered(event:Event):void {
60             redraw();
61         }
62
63                 private function nodeDragged(event:EntityDraggedEvent):void {
64                         updatePosition(event.xDelta,event.yDelta);
65                 }
66
67                 /** Update settings then draw node. */
68                 override public function doRedraw():Boolean {
69                         if (!paint.ready) { return false; }
70                         if (entity.deleted) { return false; }
71
72                         var tags:Object = entity.getTagsCopy();
73                         setStateClass('poi', !entity.hasParentWays);
74                         setStateClass('junction', entity.numParentWays>1);
75             setStateClass('hasTags', entity.hasInterestingTags());
76             setStateClass('dupe', Node(entity).isDupe());
77                         tags=applyStateClasses(tags);
78                         if (entity.status) { tags['_status']=entity.status; }
79                         if (!styleList || !styleList.isValidAt(paint.map.scale)) {
80                                 styleList=paint.ruleset.getStyles(entity,tags,paint.map.scale); 
81                         }
82
83                         var suggestedLayer:Number=styleList.layerOverride();
84                         if (!isNaN(suggestedLayer)) { layer=suggestedLayer; }
85
86                         var inWay:Boolean=entity.hasParentWays;
87                         var hasStyles:Boolean=styleList.hasStyles();
88                         
89                         removeSprites(); iconnames={};
90                         return renderFromStyle(tags);
91                 }
92
93                 /** Assemble the layers of icons to draw the node, with hit zone if interactive. */
94                 private function renderFromStyle(tags:Object):Boolean {
95                         var r:Boolean=false;                    // ** rendered
96                         var maxwidth:Number=4;                  // biggest width
97                         var w:Number;
98                         var icon:Sprite;
99                         interactive=false;
100                         for each (var subpart:String in styleList.subparts) {
101
102                                 if (styleList.pointStyles[subpart]) {
103                                         var s:PointStyle=styleList.pointStyles[subpart];
104                                         interactive||=s.interactive;
105                                         r=true;
106                                         if (s.rotation) { rotation=s.rotation; }
107                                         if (s.icon_image!=iconnames[subpart]) {
108                                                 icon=new Sprite();
109                                                 iconnames[subpart]=s.icon_image;
110                                                 addToLayer(icon,STROKESPRITE,s.sublayer);
111                                                 if (s.icon_image=='square') {
112                                                         // draw square
113                                                         w=styleIcon(icon,subpart);
114                                                         icon.graphics.drawRect(0,0,w,w);
115                                                         if (s.interactive) { maxwidth=Math.max(w,maxwidth); }
116
117                                                 } else if (s.icon_image=='circle') {
118                                                         // draw circle
119                                                         w=styleIcon(icon,subpart);
120                                                         icon.graphics.drawCircle(w,w,w);
121                                                         if (s.interactive) { maxwidth=Math.max(w,maxwidth); }
122
123                                                 } else if (ImageBank.getInstance().hasImage(s.icon_image)) {
124                                                         // load icon from library
125                                                         icon.addChild(ImageBank.getInstance().getAsDisplayObject(s.icon_image));
126 //                                                      addHitSprite(icon.width);                       // ** check this - we're doing it below too
127                                                         loaded=true; updatePosition();          // ** check this
128                                                         if (s.interactive) { maxwidth=Math.max(icon.width,maxwidth); }
129                                                 }
130                                         }
131                                 }
132
133                                 // name sprite
134                                 var a:String='', t:TextStyle;
135                                 if (styleList.textStyles[subpart]) {
136                                         t=styleList.textStyles[subpart];
137                                         interactive||=t.interactive;
138                                         a=tags[t.text];
139                                 }
140
141                                 if (a) { 
142                                         var name:Sprite=new Sprite();
143                                         addToLayer(name,NAMESPRITE);
144                                         t.writeNameLabel(name,a,0,0);
145                     loaded=true;
146                                 }
147                         }
148                         if (!r) { return false; }
149                         if (interactive) { addHitSprite(maxwidth); }
150                         updatePosition();
151                         return true;
152                 }
153
154
155                 private function styleIcon(icon:Sprite, subpart:String):Number {
156                         loaded=true;
157
158                         // get colours
159                         if (styleList.shapeStyles[subpart]) {
160                                 var s:ShapeStyle=styleList.shapeStyles[subpart];
161                                 if (!isNaN(s.color)) { icon.graphics.beginFill(s.color, s.opacity ? s.opacity : 1);
162                                         }
163                                 if (s.casing_width || !isNaN(s.casing_color)) {
164                                         icon.graphics.lineStyle(s.casing_width ? s.casing_width : 1,
165                                                                                         s.casing_color ? s.casing_color : 0,
166                                                                                         s.casing_opacity ? s.casing_opacity : 1);
167                                 }
168                         }
169
170                         // return width
171                         return styleList.pointStyles[subpart].icon_width;
172                 }
173
174                 private function addHitSprite(w:uint):void {
175             hitzone = new Sprite();
176             hitzone.graphics.lineStyle(4, 0x000000, 1, false, "normal", CapsStyle.ROUND, JointStyle.ROUND);
177                         hitzone.graphics.beginFill(0);
178                         hitzone.graphics.drawRect(0,0,w,w);
179                         hitzone.visible = false;
180                         setListenSprite();
181                 }
182
183                 private function updatePosition(xDelta:Number=0,yDelta:Number=0):void {
184                         if (!loaded) { return; }
185
186                         var baseX:Number=paint.map.lon2coord(Node(entity).lon);
187                         var baseY:Number=paint.map.latp2coord(Node(entity).latp);
188                         for (var i:uint=0; i<sprites.length; i++) {
189                                 var d:DisplayObject=sprites[i];
190                                 d.x=0; d.y=0; d.rotation=0;
191
192                                 var m:Matrix=new Matrix();
193                                 m.translate(-d.width/2,-d.height/2);
194                                 m.rotate(rotation);
195                                 m.translate(baseX+xDelta,baseY+yDelta);
196                                 d.transform.matrix=m;
197                         }
198                 }
199         public function hitTest(x:Number, y:Number):Node {
200             if (hitzone && hitzone.hitTestPoint(x,y,true)) { return entity as Node; }
201             return null;
202         }
203                 
204         }
205 }