add undo for creating stuff
[potlatch2.git] / net / systemeD / potlatch2 / tools / Circularise.as
1 package net.systemeD.potlatch2.tools {
2         import net.systemeD.halcyon.Map;
3         import net.systemeD.halcyon.connection.CompositeUndoableAction;
4         import net.systemeD.halcyon.connection.Way;
5         import net.systemeD.halcyon.connection.Node;
6         import net.systemeD.halcyon.connection.MainUndoStack;
7
8         public class Circularise {
9
10                 public static function circularise(way:Way,map:Map):void {
11                         if (way.length<4) { return; }
12
13                         var a:Node=way.getNode(0);
14                         var b:Node=way.getNode(way.length-1);
15                         if (a!=b) { return; }
16
17             new Circularise(way, map).run();
18         }
19         
20         private var way:Way;
21         private var map:Map;
22         
23         // centre
24         private var cx:Number=0;
25         private var cy:Number=0;
26         
27         // distance to centre
28                 private var d:Number=0;
29         
30         // our undoable
31         private var action:CompositeUndoableAction = new CompositeUndoableAction("Circularise");
32
33         // recording the node lats lons so we're not relying on instant update
34         private var lats:Array = [];
35                 private var lons:Array = [];
36
37         function Circularise(way:Way, map:Map) {
38             this.way = way;
39             this.map = map;
40         }
41         
42         private function run():void {
43             calculateCentre();
44             calculateCentreDistance();
45
46             var i:uint;
47             var j:uint;
48             var n:Node;
49             
50                         // Move each node
51                         for (i=0; i<way.length-1; i++) {
52                                 n=way.getNode(i);
53                                 var c:Number=Math.sqrt(Math.pow(n.lon-cx,2)+Math.pow(n.latp-cy,2));
54                                 var lat:Number = cy+(n.latp-cy)/c*d;
55                                 var lon:Number = cx+(n.lon -cx)/c*d;
56                                 n.setLonLatp(lon, lat, action.push);
57                                 
58                                 // record the lat lons we're using as the node won't update
59                                 // till later
60                                 lats.push(lat);
61                                 lons.push(lon);
62                         }
63
64                         // Insert extra nodes to make circle
65                         // clockwise: angles decrease, wrapping round from -170 to 170
66                         i=0;
67                         var clockwise:Boolean=way.clockwise;
68                         var diff:Number, ang:Number;
69                         while (i<lons.length) {
70                                 j=(i+1) % lons.length;
71                                 var a1:Number=Math.atan2(lons[i]-cx, lats[i]-cy)*(180/Math.PI);
72                                 var a2:Number=Math.atan2(lons[j]-cx, lats[j]-cy)*(180/Math.PI);
73
74                                 if (clockwise) {
75                                         if (a2>a1) { a2=a2-360; }
76                                         diff=a1-a2;
77                                         if (diff>20) {
78                                                 for (ang=a1-20; ang>a2+10; ang-=20) {
79                                                     insertNode(ang, i+1);
80                                                         j++; i++;
81                                                 }
82                                         }
83                                 } else {
84                                         if (a1>a2) { a1=a1-360; }
85                                         diff=a2-a1;
86                                         if (diff>20) {
87                                                 for (ang=a1+20; ang<a2-10; ang+=20) {
88                                                     insertNode(ang, i+1);
89                                                         j++; i++;
90                                                 }
91                                         }
92                                 }
93                                 i++;
94                         }
95
96                         MainUndoStack.getGlobalStack().addAction(action);
97                 }
98
99         private function calculateCentre():void {
100                         // Find centre-point
101                         // ** should be refactored to be within Way.as, so we can share with WayUI.as
102                         var b:Node=way.getNode(way.length-1);
103                         var patharea:Number=0;
104                         var lx:Number=b.lon;
105                         var ly:Number=b.latp;
106                         var i:uint, n:Node;
107                         for (i=0; i<way.length; i++) {
108                                 n=way.getNode(i);
109                                 var sc:Number = (lx*n.latp-n.lon*ly);
110                                 cx += (lx+n.lon )*sc;
111                                 cy += (ly+n.latp)*sc;
112                                 patharea += sc;
113                                 lx=n.lon; ly=n.latp;
114                         }
115                         patharea/=2;
116                         cx/=patharea*6;
117                         cy/=patharea*6;
118         }
119
120         private function calculateCentreDistance():void {
121                         // Average distance to centre
122                         // (first + last are the same node, don't use twice)
123                         for (var i:uint = 0; i < way.length - 1; i++) {
124                                 d+=Math.sqrt(Math.pow(way.getNode(i).lon -cx,2)+
125                                                          Math.pow(way.getNode(i).latp-cy,2));
126                         }
127                         d /= way.length - 1;
128             }
129
130                 private function insertNode(ang:Number, index:int):void {
131                         var lat:Number = cy+Math.cos(ang*Math.PI/180)*d;
132                         var lon:Number = cx+Math.sin(ang*Math.PI/180)*d;
133                         lats.splice(index, 0, lat);
134                         lons.splice(index, 0, lon);
135                         var newNode:Node = map.connection.createNode({}, map.latp2lat(lat), lon, action.push);
136                         way.insertNode(index, newNode, action.push);
137                 }
138         }
139 }