refactoring and some bugfixes
[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 isBackground:Boolean = true;                 // is it a background layer or the core paint object?
20                 public var sublayerIndex:Object={};                             // hash of index->position
21
22                 private const VERYBIG:Number=Math.pow(2,16);
23
24                 // Set up layering
25
26                 public function MapPaint(map:Map,minlayer:int,maxlayer:int) {
27                         mouseEnabled=false;
28
29                         this.map=map;
30                         this.minlayer=minlayer;
31                         this.maxlayer=maxlayer;
32                         sublayerIndex[1]=0;
33                         var s:Sprite, l:int;
34
35                         // Add paint sprites
36                         for (l=minlayer; l<=maxlayer; l++) {                    // each layer (10 is +5, 0 is -5)
37                                 s = getPaintSprite();                                           //  |
38                                 s.addChild(getPaintSprite());                           //      | 0 fill
39                                 s.addChild(getPaintSprite());                           //      | 1 casing
40                                 var t:Sprite = getPaintSprite();                        //  | 2 stroke
41                                 t.addChild(getPaintSprite());                           //  |  | sublayer
42                                 s.addChild(t);                                                          //  |  |
43                                 s.addChild(getPaintSprite());                           //      | 3 names
44                                 addChild(s);                                                            //  |
45                         }
46                         
47                         // Add hit sprites
48                         for (l=minlayer; l<=maxlayer; l++) {                    // each layer (21 is +5, 11 is -5)
49                                 s = getHitSprite();                                                     //  |
50                                 s.addChild(getHitSprite());                                     //      | 0 way hit tests
51                                 s.addChild(getHitSprite());                                 //  | 1 node hit tests
52                                 addChild(s);
53                         }
54                 }
55                 
56                 public function getPaintSpriteAt(l:int):Sprite {
57                         return getChildAt(l-minlayer) as Sprite;
58                 }
59
60                 public function getHitSpriteAt(l:int):Sprite {
61                         return getChildAt((l-minlayer) + (maxlayer-minlayer+1)) as Sprite;
62                 }
63                 
64                 public function get ready():Boolean {
65                         if (!ruleset) { return false; }
66                         if (!ruleset.loaded) { return false; }
67                         return true;
68                 }
69
70                 public function sublayer(layer:int,sublayer:Number):Sprite {
71                         var l:DisplayObject;
72                         var o:DisplayObject;
73                         var index:String, ix:Number;
74                         if (!sublayerIndex.hasOwnProperty(sublayer)) {
75                                 // work out which position to add at
76                                 var lowestAbove:Number=VERYBIG;
77                                 var lowestAbovePos:int=-1;
78                                 var indexLength:uint=0;
79                                 for (index in sublayerIndex) {
80                                         ix=Number(index);
81                                         if (ix>sublayer && ix<lowestAbove) {
82                                                 lowestAbove=ix;
83                                                 lowestAbovePos=sublayerIndex[index];
84                                         }
85                                         indexLength++;
86                                 }
87                                 if (lowestAbovePos==-1) { lowestAbovePos=indexLength; }
88                         
89                                 // add sprites
90                                 for (var i:int=minlayer; i<=maxlayer; i++) {
91                                         l=getChildAt(i-minlayer);
92                                         o=(l as Sprite).getChildAt(2);
93                                         (o as Sprite).addChildAt(getPaintSprite(),lowestAbovePos);
94                                 }
95                         
96                                 // update index
97                                 // (we do it in this rather indirect way because if you alter sublayerIndex directly
98                                 //  within the loop, it confuses the iterator)
99                                 var toUpdate:Array=[];
100                                 for (index in sublayerIndex) {
101                                         ix=Number(index);
102                                         if (ix>sublayer) { toUpdate.push(index); }
103                                 }
104                                 for each (index in toUpdate) { sublayerIndex[index]++; }
105                                 sublayerIndex[sublayer]=lowestAbovePos;
106                         }
107
108                         l=getChildAt(layer-minlayer);
109                         o=(l as Sprite).getChildAt(2);
110                         return ((o as Sprite).getChildAt(sublayerIndex[sublayer]) as Sprite);
111                 }
112
113                 public function updateEntityUIs(o:Object, redraw:Boolean, remove:Boolean):void {
114                         var way:Way, node:Node;
115
116                         for each (way in o.waysInside) {
117                                 if (!wayuis[way.id]) { createWayUI(way); }
118                                 else if (redraw) { wayuis[way.id].recalculate(); wayuis[way.id].redraw(); }
119                         }
120
121                         if (remove) {
122                                 for each (way in o.waysOutside) {
123                                         if (wayuis[way.id] && !wayuis[way.id].purgable) {
124                                                 if (redraw) { wayuis[way.id].recalculate(); wayuis[way.id].redraw(); }
125                                         } else {
126                                                 deleteWayUI(way);
127                                         }
128                                 }
129                         }
130
131                         for each (node in o.nodesInside) {
132                                 if (!nodeuis[node.id]) { createNodeUI(node); }
133                                 else if (redraw) { nodeuis[node.id].redraw(); }
134                         }
135
136                         if (remove) {
137                                 for each (node in o.nodesOutside) { 
138                                         if (nodeuis[node.id] && !nodeuis[node.id].purgable) {
139                                                 if (redraw) { nodeuis[node.id].redraw(); }
140                                         } else {
141                                                 deleteNodeUI(node);
142                                         }
143                                 }
144                         }
145                 }
146
147                 public function createWayUI(way:Way):WayUI {
148                         if (!wayuis[way.id]) {
149                                 wayuis[way.id]=new WayUI(way,this);
150                                 way.addEventListener(Connection.WAY_DELETED, wayDeleted);
151                         }
152                         return wayuis[way.id];
153                 }
154
155                 public function wayDeleted(event:EntityEvent):void {
156                         deleteWayUI(event.entity as Way);
157                 }
158
159         public function createNodeUI(node:Node):NodeUI {
160             if (!nodeuis[node.id]) {
161               nodeuis[node.id]=new NodeUI(node,this,0);
162             } else {
163               nodeuis[node.id].redraw();
164             }
165             return nodeuis[node.id];
166         }
167
168                 public function deleteWayUI(way:Way):void {
169                         way.removeEventListener(Connection.WAY_DELETED, wayDeleted);
170                         if (wayuis[way.id]) {
171                                 wayuis[way.id].redrawMultis();
172                                 wayuis[way.id].removeSprites();
173                                 wayuis[way.id].removeEventListeners();
174                                 delete wayuis[way.id];
175                         }
176                         for (var i:uint=0; i<way.length; i++) {
177                                 var node:Node=way.getNode(i);
178                                 if (nodeuis[node.id]) { deleteNodeUI(node); }
179                         }
180                 }
181
182                 public function deleteNodeUI(node:Node):void {
183                         if (!nodeuis[node.id]) { return; }
184                         if (!nodeuis[node.id].purgable) { return; }
185                         nodeuis[node.id].removeSprites();
186                         delete nodeuis[node.id];
187                 }
188                 
189                 public function renumberWayUI(way:Way,oldID:Number):void {
190                         if (!wayuis[oldID]) { return; }
191                         wayuis[way.id]=wayuis[oldID];
192                         delete wayuis[oldID];
193                 }
194
195                 public function renumberNodeUI(node:Node,oldID:Number):void {
196                         if (!nodeuis[oldID]) { return; }
197                         nodeuis[node.id]=nodeuis[oldID];
198                         delete nodeuis[oldID];
199                 }
200
201         private function getPaintSprite():Sprite {
202             var s:Sprite = new Sprite();
203             s.mouseEnabled = false;
204             s.mouseChildren = false;
205             return s;
206         }
207
208         private function getHitSprite():Sprite {
209             var s:Sprite = new Sprite();
210             return s;
211         }
212
213                 public function redraw():void {
214                         for each (var w:WayUI in wayuis) { w.recalculate(); w.invalidateStyleList(); w.redraw(); }
215             /* sometimes (e.g. in Map.setStyle) Mappaint.redrawPOIs() is called immediately afterwards anyway. FIXME? */
216                         for each (var p:NodeUI in nodeuis) { p.invalidateStyleList(); p.redraw(); }
217                 }
218
219                 public function redrawPOIs():void {
220                         for each (var p:NodeUI in nodeuis) { p.invalidateStyleList(); p.redraw(); }
221                 }
222                 
223                 public function findSource():VectorLayer {
224                         var v:VectorLayer;
225                         for each (v in map.vectorlayers) {
226                                 if (v.paint==this) { return v; }
227                         }
228                         return null;
229                 }
230         }
231 }