Merge branch 'master' of github.com:systemed/potlatch2
[potlatch2.git] / net / systemeD / potlatch2 / tools / Parallelise.as
1 package net.systemeD.potlatch2.tools {
2
3     import net.systemeD.halcyon.connection.*;
4
5           /** Tool to create a parallel copy of an existing way. First call the constructor Parallelise() passing it the 
6           * way that will be parallelised. This performs some initialisation. Then call draw(), passing it an offset,
7           * as many times as you like. Each time it recomputes the parallel way. There is no finalisation. 
8           * <p>This is intended to work with the SelectedParallelWay controller state.</p>*/
9         public class Parallelise {
10                 private var originalWay:Way;
11                 public var parallelWay:Way;
12                 private var connection:Connection;
13                 private var offsetx:Array=[];
14                 private var offsety:Array=[];
15                 private var df:Array=[];
16                 private var nodes:Object={};
17                 
18                 /** Initialises parallelisation process, adding an entry to global undo stack.
19                  * @param way The way to be duplicated. 
20                  * */
21                 public function Parallelise(way:Way) {
22                         var a:Number, b:Number, h:Number, i:uint, j:uint, k:int;
23                         connection  = way.connection;
24                         originalWay = way;
25                         parallelWay = connection.createWay({}, [], MainUndoStack.getGlobalStack().addAction);
26
27                         for (i=0; i<originalWay.length; i++) {
28                                 j=(i+1) % originalWay.length;
29                                 a=originalWay.getNode(i).latp - originalWay.getNode(j).latp;
30                                 b=originalWay.getNode(j).lon  - originalWay.getNode(i).lon;
31                                 h=Math.sqrt(a*a+b*b);
32                                 if (h!=0) { a=a/h; b=b/h; }
33                                          else { a=0; b=0; }
34                                 offsetx[i]=a;
35                                 offsety[i]=b;
36                         }
37
38                         for (i=0; i<originalWay.length; i++) {
39                                 j=(i+1) % originalWay.length;
40                                 k=i-1; if (k==-1) { k=originalWay.length-2; }   // ** it's -2 because if this is an area, node[length-1] is the same as node[0]
41                                 a=det(offsetx[i]-offsetx[k],
42                                           offsety[i]-offsety[k],
43                                           originalWay.getNode(j).lon  - originalWay.getNode(i).lon,
44                                           originalWay.getNode(j).latp - originalWay.getNode(i).latp);
45                                 b=det(originalWay.getNode(i).lon  - originalWay.getNode(k).lon,
46                                           originalWay.getNode(i).latp - originalWay.getNode(k).latp,
47                                           originalWay.getNode(j).lon  - originalWay.getNode(i).lon,
48                                           originalWay.getNode(j).latp - originalWay.getNode(i).latp);
49                                 if (b!=0) { df[i]=a/b; } else { df[i]=0; }
50                         }
51
52                 }
53
54                 /** Compute the shape of the parallel way, implicitly causing it to be drawn if onscreen. Closed ways are ok. 
55                  * @param offset How far, in lon/latp units, should the parallel way be. Can be negative. */
56                 public function draw(offset:Number):void {
57                         var x:Number, y:Number;
58                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Draw parallel way");
59                         parallelWay.suspend();
60                         for (var i:int=0; i<originalWay.length; i++) {
61                                 if (i==0) {
62                                         if (originalWay.isArea()) {
63                                                 x=originalWay.getNode(i).lon + offset * (offsetx[originalWay.length-2] + df[i] * (originalWay.getNode(i).lon - originalWay.getNode(originalWay.length-2).lon ));
64                                                 y=originalWay.getNode(i).latp+ offset * (offsety[originalWay.length-2] + df[i] * (originalWay.getNode(i).latp- originalWay.getNode(originalWay.length-2).latp));
65                                         } else {
66                                                 x=originalWay.getNode(0).lon + offset * offsetx[0];
67                                                 y=originalWay.getNode(0).latp+ offset * offsety[0];
68                                         }
69                                 } else if (i==originalWay.length-1) {
70                                         if (originalWay.isArea()) { continue; }         // node[length-1] is the same as node[0] if it's an area, so skip
71                                         x=originalWay.getNode(i).lon + offset * offsetx[i-1];
72                                         y=originalWay.getNode(i).latp+ offset * offsety[i-1];
73                                 } else {
74                                         x=originalWay.getNode(i).lon + offset * (offsetx[i-1] + df[i] * (originalWay.getNode(i).lon - originalWay.getNode(i-1).lon ));
75                                         y=originalWay.getNode(i).latp+ offset * (offsety[i-1] + df[i] * (originalWay.getNode(i).latp- originalWay.getNode(i-1).latp));
76                                 }
77                                 if (nodes[i]) {
78                                         nodes[i].setLonLatp(x,y,undo.push);
79                                 } else {
80                                         nodes[i] = connection.createNode({},Node.latp2lat(y),x,undo.push);
81                                         parallelWay.appendNode(nodes[i], undo.push);
82                                 }
83                         }
84
85             if  ( originalWay.isArea() && parallelWay.getLastNode() != parallelWay.getNode(0) ) {
86                 parallelWay.appendNode(nodes[0],undo.push);
87             }
88
89                         parallelWay.resume();
90                         undo.doAction();                // don't actually add it to the undo stack, just do it!
91                 }
92                 
93                 /** Compute determinant. */
94                 private function det(a:Number,b:Number,c:Number,d:Number):Number { return a*d-b*c; }
95
96         }
97 }