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