fix dupe nodes stuff
[potlatch2.git] / net / systemeD / halcyon / MapPaint.as
1 package net.systemeD.halcyon {
2
3         import flash.display.Sprite;
4         import flash.display.DisplayObject;
5         import net.systemeD.halcyon.NodeUI;
6         import net.systemeD.halcyon.WayUI;
7         import net.systemeD.halcyon.connection.*;
8         import net.systemeD.halcyon.styleparser.RuleSet;
9         import net.systemeD.halcyon.Globals;
10
11         public class MapPaint extends Sprite {
12
13                 public var map:Map;
14                 public var minlayer:int;
15                 public var maxlayer:int;
16                 public var ruleset:RuleSet;                                             // rules
17                 public var wayuis:Object=new Object();                  // sprites for ways and (POI/tagged) nodes
18                 public var nodeuis:Object=new Object();                 //      |
19         public var markeruis:Object=new Object();
20                 public var isBackground:Boolean = true;                 // is it a background layer or the core paint object?
21                 public var sublayerIndex:Object={};                             // hash of index->position
22
23                 private const VERYBIG:Number=Math.pow(2,16);
24                 private static const NO_LAYER:int=-99999;               // same as NodeUI
25
26                 // Set up layering
27
28                 public function MapPaint(map:Map,minlayer:int,maxlayer:int) {
29                         mouseEnabled=false;
30
31                         this.map=map;
32                         this.minlayer=minlayer;
33                         this.maxlayer=maxlayer;
34                         sublayerIndex[1]=0;
35                         var s:Sprite, l:int;
36
37                         // Add paint sprites
38                         for (l=minlayer; l<=maxlayer; l++) {                    // each layer (10 is +5, 0 is -5)
39                                 s = getPaintSprite();                                           //      |
40                                 s.addChild(getPaintSprite());                           //      | 0 fill
41                                 s.addChild(getPaintSprite());                           //      | 1 casing
42                                 var t:Sprite = getPaintSprite();                        //      | 2 stroke
43                                 t.addChild(getPaintSprite());                           //      |  | sublayer
44                                 s.addChild(t);                                                          //      |  |
45                                 s.addChild(getPaintSprite());                           //      | 3 names
46                                 addChild(s);                                                            //      |
47                         }
48                         
49                         // Add hit sprites
50                         for (l=minlayer; l<=maxlayer; l++) {                    // each layer (21 is +5, 11 is -5)
51                                 s = getHitSprite();                                                     //      |
52                                 s.addChild(getHitSprite());                                     //      | 0 way hit tests
53                                 s.addChild(getHitSprite());                                     //      | 1 node hit tests
54                                 addChild(s);
55                         }
56                 }
57                 
58                 public function getPaintSpriteAt(l:int):Sprite {
59                         return getChildAt(l-minlayer) as Sprite;
60                 }
61
62                 public function getHitSpriteAt(l:int):Sprite {
63                         return getChildAt((l-minlayer) + (maxlayer-minlayer+1)) as Sprite;
64                 }
65                 
66                 public function get ready():Boolean {
67                         if (!ruleset) { return false; }
68                         if (!ruleset.loaded) { return false; }
69                         return true;
70                 }
71
72                 public function sublayer(layer:int,sublayer:Number):Sprite {
73                         var l:DisplayObject;
74                         var o:DisplayObject;
75                         var index:String, ix:Number;
76                         if (!sublayerIndex.hasOwnProperty(sublayer)) {
77                                 // work out which position to add at
78                                 var lowestAbove:Number=VERYBIG;
79                                 var lowestAbovePos:int=-1;
80                                 var indexLength:uint=0;
81                                 for (index in sublayerIndex) {
82                                         ix=Number(index);
83                                         if (ix>sublayer && ix<lowestAbove) {
84                                                 lowestAbove=ix;
85                                                 lowestAbovePos=sublayerIndex[index];
86                                         }
87                                         indexLength++;
88                                 }
89                                 if (lowestAbovePos==-1) { lowestAbovePos=indexLength; }
90                         
91                                 // add sprites
92                                 for (var i:int=minlayer; i<=maxlayer; i++) {
93                                         l=getChildAt(i-minlayer);
94                                         o=(l as Sprite).getChildAt(2);
95                                         (o as Sprite).addChildAt(getPaintSprite(),lowestAbovePos);
96                                 }
97                         
98                                 // update index
99                                 // (we do it in this rather indirect way because if you alter sublayerIndex directly
100                                 //      within the loop, it confuses the iterator)
101                                 var toUpdate:Array=[];
102                                 for (index in sublayerIndex) {
103                                         ix=Number(index);
104                                         if (ix>sublayer) { toUpdate.push(index); }
105                                 }
106                                 for each (index in toUpdate) { sublayerIndex[index]++; }
107                                 sublayerIndex[sublayer]=lowestAbovePos;
108                         }
109
110                         l=getChildAt(layer-minlayer);
111                         o=(l as Sprite).getChildAt(2);
112                         return ((o as Sprite).getChildAt(sublayerIndex[sublayer]) as Sprite);
113                 }
114
115         /**
116         * Update, and if necessary, create / remove UIs for the given objects.
117         * The object is effectively lists of objects split into inside/outside pairs, e.g.
118         * { waysInside: [], waysOutside: [] } where each is a array of entities either inside
119         * or outside this current view window. UIs for the entities on "inside" lists will be created if necessary.
120         * Flags control redrawing existing entities and removing UIs from entities no longer in view.
121         *
122         * @param o The object containing all the relevant entites.
123         * @param redraw If true, all UIs for entities on "inside" lists will be redrawn
124         * @param remove If true, all UIs for entites on "outside" lists will be removed. The purgable flag on UIs
125                         can override this, for example for selected objects.
126         */
127                 public function updateEntityUIs(o:Object, redraw:Boolean, remove:Boolean):void {
128                         var way:Way, poi:Node, marker:Marker;
129
130                         for each (way in o.waysInside) {
131                                 if (!wayuis[way.id]) { createWayUI(way); }
132                                 else if (redraw) { wayuis[way.id].recalculate(); wayuis[way.id].redraw(); }
133                         }
134
135                         if (remove) {
136                                 for each (way in o.waysOutside) {
137                                         if (wayuis[way.id] && !wayuis[way.id].purgable) {
138                                                 if (redraw) { wayuis[way.id].recalculate(); wayuis[way.id].redraw(); }
139                                         } else {
140                                                 deleteWayUI(way);
141                                         }
142                                 }
143                         }
144
145                         for each (poi in o.poisInside) {
146                                 if (!nodeuis[poi.id]) { createNodeUI(poi); }
147                                 else if (redraw) { nodeuis[poi.id].redraw(); }
148                         }
149
150                         if (remove) {
151                                 for each (poi in o.poisOutside) { 
152                                         if (nodeuis[poi.id] && !nodeuis[poi.id].purgable) {
153                                                 if (redraw) { nodeuis[poi.id].redraw(); }
154                                         } else {
155                                                 deleteNodeUI(poi);
156                                         }
157                                 }
158                         }
159
160             for each (marker in o.markersInside) {
161                 if (!markeruis[marker.id]) { createMarkerUI(marker); }
162                 else if (redraw) { markeruis[marker.id].redraw(); }
163             }
164
165             if (remove) {
166                 for each (marker in o.markersOutside) {
167                     if (markeruis[marker.id] && !markeruis[marker.id].purgable) {
168                         if (redraw) { markeruis[marker.id].redraw(); }
169                     } else {
170                         deleteMarkerUI(marker);
171                     }
172                 }
173             }
174                 }
175
176                 public function createWayUI(way:Way):WayUI {
177                         if (!wayuis[way.id]) {
178                                 wayuis[way.id]=new WayUI(way,this);
179                                 way.addEventListener(Connection.WAY_DELETED, wayDeleted);
180                         }
181                         return wayuis[way.id];
182                 }
183
184                 public function wayDeleted(event:EntityEvent):void {
185                         deleteWayUI(event.entity as Way);
186                 }
187
188                 public function deleteWayUI(way:Way):void {
189                         way.removeEventListener(Connection.WAY_DELETED, wayDeleted);
190                         if (wayuis[way.id]) {
191                                 wayuis[way.id].redrawMultis();
192                                 wayuis[way.id].removeSprites();
193                                 wayuis[way.id].removeEventListeners();
194                                 delete wayuis[way.id];
195                         }
196                         for (var i:uint=0; i<way.length; i++) {
197                                 var node:Node=way.getNode(i);
198                                 if (nodeuis[node.id]) { deleteNodeUI(node); }
199                         }
200                 }
201
202                 public function createNodeUI(node:Node,rotation:Number=0,layer:int=NO_LAYER,stateClasses:Object=null):NodeUI {
203                         if (!nodeuis[node.id]) {
204                                 nodeuis[node.id]=new NodeUI(node,this,rotation,layer,stateClasses);
205                                 node.addEventListener(Connection.NODE_DELETED, nodeDeleted);
206                         } else {
207                                 for (var state:String in stateClasses) {
208                                         nodeuis[node.id].setStateClass(state,stateClasses[state]);
209                                 }
210                                 nodeuis[node.id].redraw();
211                         }
212                         return nodeuis[node.id];
213                 }
214
215                 public function nodeDeleted(event:EntityEvent):void {
216                         deleteNodeUI(event.entity as Node);
217                 }
218
219                 public function deleteNodeUI(node:Node):void {
220                         if (!nodeuis[node.id]) { return; }
221                         node.removeEventListener(Connection.NODE_DELETED, nodeDeleted);
222                         nodeuis[node.id].removeSprites();
223                         nodeuis[node.id].removeEventListeners();
224                         delete nodeuis[node.id];
225                 }
226
227         public function createMarkerUI(marker:Marker,rotation:Number=0,layer:int=NO_LAYER,stateClasses:Object=null):MarkerUI {
228             if (!markeruis[marker.id]) {
229                 markeruis[marker.id]=new MarkerUI(marker,this,rotation,layer,stateClasses);
230                 marker.addEventListener(Connection.NODE_DELETED, markerDeleted);
231             } else {
232                 for (var state:String in stateClasses) {
233                     markeruis[marker.id].setStateClass(state,stateClasses[state]);
234                 }
235                 markeruis[marker.id].redraw();
236             }
237             return markeruis[marker.id];
238         }
239
240         public function markerDeleted(event:EntityEvent):void {
241             deleteMarkerUI(event.entity as Marker);
242         }
243
244         public function deleteMarkerUI(marker:Marker):void {
245             if (!markeruis[marker.id]) { return; }
246             marker.removeEventListener(Connection.NODE_DELETED, markerDeleted);
247             markeruis[marker.id].removeSprites();
248             markeruis[marker.id].removeEventListeners();
249             delete markeruis[marker.id];
250         }
251                 
252                 public function renumberWayUI(way:Way,oldID:Number):void {
253                         if (!wayuis[oldID]) { return; }
254                         wayuis[way.id]=wayuis[oldID];
255                         delete wayuis[oldID];
256                 }
257
258                 public function renumberNodeUI(node:Node,oldID:Number):void {
259                         if (!nodeuis[oldID]) { return; }
260                         nodeuis[node.id]=nodeuis[oldID];
261                         delete nodeuis[oldID];
262                 }
263
264                 private function getPaintSprite():Sprite {
265                         var s:Sprite = new Sprite();
266                         s.mouseEnabled = false;
267                         s.mouseChildren = false;
268                         return s;
269                 }
270
271                 private function getHitSprite():Sprite {
272                         var s:Sprite = new Sprite();
273                         return s;
274                 }
275
276                 public function redraw():void {
277                         for each (var w:WayUI in wayuis) { w.recalculate(); w.invalidateStyleList(); w.redraw(); }
278                         /* sometimes (e.g. in Map.setStyle) Mappaint.redrawPOIs() is called immediately afterwards anyway. FIXME? */
279                         for each (var p:NodeUI in nodeuis) { p.invalidateStyleList(); p.redraw(); }
280             for each (var m:MarkerUI in markeruis) { m.invalidateStyleList(); m.redraw(); }
281                 }
282
283                 public function redrawPOIs():void {
284                         for each (var p:NodeUI in nodeuis) { p.invalidateStyleList(); p.redraw(); }
285             for each (var m:MarkerUI in markeruis) { m.invalidateStyleList(); m.redraw(); }
286                 }
287                 
288                 public function findSource():VectorLayer {
289                         var v:VectorLayer;
290                         for each (v in map.vectorlayers) {
291                                 if (v.paint==this) { return v; }
292                         }
293                         return null;
294                 }
295         }
296 }