| 1 | package mkmap
|
|---|
| 2 | {
|
|---|
| 3 | import flash.display.*;
|
|---|
| 4 | import flash.events.*;
|
|---|
| 5 | import flash.geom.*;
|
|---|
| 6 | public class PolyLine extends CanvasObject
|
|---|
| 7 | {
|
|---|
| 8 | private var mFirst:Segment;
|
|---|
| 9 | private var mLast:Segment;
|
|---|
| 10 | private var mCanvas:MCanvas;
|
|---|
| 11 | private var mCornerR:int;
|
|---|
| 12 |
|
|---|
| 13 | private var mThick:int;
|
|---|
| 14 | private var mLineColor:uint;
|
|---|
| 15 |
|
|---|
| 16 | function PolyLine(c:MCanvas, initY:int = 10)
|
|---|
| 17 | {
|
|---|
| 18 | mCornerR = 19;
|
|---|
| 19 | mThick = 6;
|
|---|
| 20 | mLineColor = 0x4466ff;
|
|---|
| 21 |
|
|---|
| 22 | setInitialSegment(new Segment(10, initY, 110, initY+100));
|
|---|
| 23 | mCanvas = c;
|
|---|
| 24 | mouseEnabled = false;
|
|---|
| 25 | }
|
|---|
| 26 |
|
|---|
| 27 | public function set lineColor(c:uint):void
|
|---|
| 28 | {
|
|---|
| 29 | mLineColor = c;
|
|---|
| 30 | updateView();
|
|---|
| 31 | }
|
|---|
| 32 |
|
|---|
| 33 | private function setInitialSegment(s:Segment):void
|
|---|
| 34 | {
|
|---|
| 35 | mFirst = s;
|
|---|
| 36 | mLast = s;
|
|---|
| 37 |
|
|---|
| 38 | s.next = null;
|
|---|
| 39 | s.prev = null;
|
|---|
| 40 |
|
|---|
| 41 | var hb:AnchorButton = new AnchorButton(false);
|
|---|
| 42 | var tb:AnchorButton = new AnchorButton(false);
|
|---|
| 43 | var mb:AnchorButton = new AnchorButton(true);
|
|---|
| 44 |
|
|---|
| 45 | addChild(hb);
|
|---|
| 46 | addChild(tb);
|
|---|
| 47 | addChild(mb);
|
|---|
| 48 | hb.onMoved = onAnchorMove;
|
|---|
| 49 | tb.onMoved = onAnchorMove;
|
|---|
| 50 | mb.onMoved = onAnchorMove;
|
|---|
| 51 |
|
|---|
| 52 | updateView();
|
|---|
| 53 |
|
|---|
| 54 | s.headAnc = hb;
|
|---|
| 55 | s.midAnc = mb;
|
|---|
| 56 | s.tailAnc = tb;
|
|---|
| 57 | s.updateAnchors();
|
|---|
| 58 |
|
|---|
| 59 | tb.backSegment = s;
|
|---|
| 60 | mb.middleSegment = s;
|
|---|
| 61 | }
|
|---|
| 62 |
|
|---|
| 63 | private function mid_point(x1:int, y1:int, x2:int, y2:int, t:Number, pout:Point):void
|
|---|
| 64 | {
|
|---|
| 65 | var vx:Number = x2 - x1;
|
|---|
| 66 | var vy:Number = y2 - y1;
|
|---|
| 67 |
|
|---|
| 68 | var nrm:Number = Math.sqrt(vx*vx + vy*vy);
|
|---|
| 69 | if (nrm < t)
|
|---|
| 70 | {
|
|---|
| 71 | pout.x = x2;
|
|---|
| 72 | pout.y = y2;
|
|---|
| 73 | return;
|
|---|
| 74 | }
|
|---|
| 75 |
|
|---|
| 76 | var fct:Number = (nrm-t) / nrm;
|
|---|
| 77 | pout.x = x1 + vx*fct;
|
|---|
| 78 | pout.y = y1 + vy*fct;
|
|---|
| 79 | }
|
|---|
| 80 |
|
|---|
| 81 | private var _tmpPt1:Point = new Point();
|
|---|
| 82 | private var _tmpPt2:Point = new Point();
|
|---|
| 83 | private function updateView():void
|
|---|
| 84 | {
|
|---|
| 85 | var s:Segment;
|
|---|
| 86 | graphics.clear();
|
|---|
| 87 |
|
|---|
| 88 | var g:Graphics = graphics;
|
|---|
| 89 |
|
|---|
| 90 | for (var phase:int = 0;phase < 2;phase++)
|
|---|
| 91 | {
|
|---|
| 92 | g.lineStyle(mThick + (1-phase)*2, (phase == 0) ? 0xffffff : mLineColor, 1, true, LineScaleMode.NONE, CapsStyle.ROUND);
|
|---|
| 93 | for(s = mFirst; s != null; s=s.next)
|
|---|
| 94 | {
|
|---|
| 95 | if (s == mFirst)
|
|---|
| 96 | g.moveTo(s.x1, s.y1);
|
|---|
| 97 |
|
|---|
| 98 | if (s.next != null)
|
|---|
| 99 | {
|
|---|
| 100 | var nx:Segment = s.next;
|
|---|
| 101 | mid_point(s.x1, s.y1, s.x2, s.y2, mCornerR, _tmpPt1);
|
|---|
| 102 | mid_point(nx.x2, nx.y2, nx.x1, nx.y1, mCornerR, _tmpPt2);
|
|---|
| 103 | g.lineTo(_tmpPt1.x, _tmpPt1.y);
|
|---|
| 104 | g.curveTo(s.x2, s.y2, _tmpPt2.x, _tmpPt2.y);
|
|---|
| 105 | }
|
|---|
| 106 | else
|
|---|
| 107 | g.lineTo(s.x2, s.y2);
|
|---|
| 108 | }
|
|---|
| 109 | }
|
|---|
| 110 |
|
|---|
| 111 | for(s = mFirst; s != null; s=s.next)
|
|---|
| 112 | drawCenterLine(s);
|
|---|
| 113 | }
|
|---|
| 114 |
|
|---|
| 115 | private function onAnchorMove(anc:AnchorButton, e:MouseEvent):void
|
|---|
| 116 | {
|
|---|
| 117 | var p:Point = new Point();
|
|---|
| 118 | mCanvas.getSnappedMousePosition(p);
|
|---|
| 119 |
|
|---|
| 120 | if (anc.middleSegment != null)
|
|---|
| 121 | {
|
|---|
| 122 | if (doFold(anc.middleSegment, p.x, p.y))
|
|---|
| 123 | updateView();
|
|---|
| 124 | }
|
|---|
| 125 | else if (anc.backSegment != null)
|
|---|
| 126 | {
|
|---|
| 127 | moveEndAnchor(anc.backSegment, anc.backSegment.next, p.x, p.y);
|
|---|
| 128 | checkUnfold(anc);
|
|---|
| 129 | updateView();
|
|---|
| 130 | anc.backSegment.updateAnchors();
|
|---|
| 131 | if (anc.backSegment.next)
|
|---|
| 132 | anc.backSegment.next.updateAnchors();
|
|---|
| 133 | }
|
|---|
| 134 | else
|
|---|
| 135 | {
|
|---|
| 136 | if (anc == mFirst.headAnc)
|
|---|
| 137 | {
|
|---|
| 138 | moveEndAnchor(null, mFirst, p.x, p.y);
|
|---|
| 139 | updateView();
|
|---|
| 140 | mFirst.updateAnchors();
|
|---|
| 141 | if (mFirst.next)
|
|---|
| 142 | mFirst.next.updateAnchors();
|
|---|
| 143 | }
|
|---|
| 144 | }
|
|---|
| 145 | }
|
|---|
| 146 |
|
|---|
| 147 | private function distance2_from_sprite(x:int, y:int, s:Sprite):Number
|
|---|
| 148 | {
|
|---|
| 149 | var dx:int = x - s.x;
|
|---|
| 150 | var dy:int = y - s.y;
|
|---|
| 151 | return dx*dx + dy*dy;
|
|---|
| 152 | }
|
|---|
| 153 |
|
|---|
| 154 | private function divideSegmentAt(x:int, y:int, s:Segment):Segment
|
|---|
| 155 | {
|
|---|
| 156 | var oldNext:Segment = s.next;
|
|---|
| 157 | var newSeg:Segment = new Segment(x, y, s.x2, s.y2);
|
|---|
| 158 |
|
|---|
| 159 | var tb:AnchorButton = new AnchorButton(false);
|
|---|
| 160 | var mb:AnchorButton = new AnchorButton(true);
|
|---|
| 161 | addChild(tb);
|
|---|
| 162 | addChild(mb);
|
|---|
| 163 | tb.onMoved = onAnchorMove;
|
|---|
| 164 | mb.onMoved = onAnchorMove;
|
|---|
| 165 | tb.backSegment = newSeg;
|
|---|
| 166 | mb.middleSegment = newSeg;
|
|---|
| 167 |
|
|---|
| 168 | newSeg.headAnc = s.tailAnc;
|
|---|
| 169 | newSeg.midAnc = mb;
|
|---|
| 170 | newSeg.tailAnc = tb;
|
|---|
| 171 |
|
|---|
| 172 | s.x2 = x;
|
|---|
| 173 | s.y2 = y;
|
|---|
| 174 |
|
|---|
| 175 | s.next = newSeg;
|
|---|
| 176 | newSeg.prev = s;
|
|---|
| 177 |
|
|---|
| 178 | newSeg.next = oldNext;
|
|---|
| 179 | if (oldNext)
|
|---|
| 180 | {
|
|---|
| 181 | oldNext.prev = newSeg;
|
|---|
| 182 | oldNext.headAnc = tb;
|
|---|
| 183 | }
|
|---|
| 184 |
|
|---|
| 185 | if (s == mLast)
|
|---|
| 186 | mLast = newSeg;
|
|---|
| 187 |
|
|---|
| 188 | s.updateAnchors();
|
|---|
| 189 | newSeg.updateAnchors();
|
|---|
| 190 | if (oldNext)
|
|---|
| 191 | oldNext.updateAnchors();
|
|---|
| 192 |
|
|---|
| 193 | return newSeg;
|
|---|
| 194 | }
|
|---|
| 195 |
|
|---|
| 196 | private function doFold(seg:Segment, x:int, y:int):Boolean
|
|---|
| 197 | {
|
|---|
| 198 | if (distance2_from_sprite(x, y, seg.midAnc) > 400)
|
|---|
| 199 | {
|
|---|
| 200 | seg.midAnc.forceRelease();
|
|---|
| 201 |
|
|---|
| 202 | divideSegmentAt(x, y, seg);
|
|---|
| 203 | seg.tailAnc.forceDown();
|
|---|
| 204 |
|
|---|
| 205 | return true;
|
|---|
| 206 | }
|
|---|
| 207 |
|
|---|
| 208 | return false;
|
|---|
| 209 | }
|
|---|
| 210 |
|
|---|
| 211 | private function nearZero(v:Number):Boolean
|
|---|
| 212 | {
|
|---|
| 213 | return (v > -0.01 && v < 0.01);
|
|---|
| 214 | }
|
|---|
| 215 |
|
|---|
| 216 | private function calcNormalDP(vx1:Number, vy1:Number, vx2:Number, vy2:Number):Number
|
|---|
| 217 | {
|
|---|
| 218 | var nrm1:Number = Math.sqrt(vx1*vx1 + vy1*vy1);
|
|---|
| 219 | if (nearZero(nrm1))
|
|---|
| 220 | return 0;
|
|---|
| 221 |
|
|---|
| 222 | var nrm2:Number = Math.sqrt(vx2*vx2 + vy2*vy2);
|
|---|
| 223 | if (nearZero(nrm2))
|
|---|
| 224 | return 0;
|
|---|
| 225 |
|
|---|
| 226 | vx1 /= nrm1;
|
|---|
| 227 | vy1 /= nrm1;
|
|---|
| 228 | vx2 /= nrm2;
|
|---|
| 229 | vy2 /= nrm2;
|
|---|
| 230 |
|
|---|
| 231 | return vx1*vx2 + vy1*vy2;
|
|---|
| 232 | }
|
|---|
| 233 |
|
|---|
| 234 | private function checkUnfold(anc:AnchorButton):Boolean
|
|---|
| 235 | {
|
|---|
| 236 | var seg1:Segment = anc.backSegment;
|
|---|
| 237 | if (seg1 == null)
|
|---|
| 238 | return false;
|
|---|
| 239 |
|
|---|
| 240 | var seg2:Segment = seg1.next;
|
|---|
| 241 | if (seg2 == null)
|
|---|
| 242 | return false;
|
|---|
| 243 |
|
|---|
| 244 | var vx1:int = seg1.x2 - seg1.x1;
|
|---|
| 245 | var vy1:int = seg1.y2 - seg1.y1;
|
|---|
| 246 |
|
|---|
| 247 | var vx2:int = seg2.x2 - seg2.x1;
|
|---|
| 248 | var vy2:int = seg2.y2 - seg2.y1;
|
|---|
| 249 |
|
|---|
| 250 | if (calcNormalDP(vx1, vy1, vx2, vy2) > 0.98)
|
|---|
| 251 | unfold(seg1, seg2);
|
|---|
| 252 |
|
|---|
| 253 | return true;
|
|---|
| 254 | }
|
|---|
| 255 |
|
|---|
| 256 | private function unfold(seg1:Segment, seg2:Segment):void
|
|---|
| 257 | {
|
|---|
| 258 | // remove seg2
|
|---|
| 259 |
|
|---|
| 260 | seg1.x2 = seg2.x2;
|
|---|
| 261 | seg1.y2 = seg2.y2;
|
|---|
| 262 |
|
|---|
| 263 | seg1.next = seg2.next;
|
|---|
| 264 | if (seg2.next)
|
|---|
| 265 | seg2.next.prev = seg1;
|
|---|
| 266 |
|
|---|
| 267 | // remove anchors
|
|---|
| 268 | seg2.midAnc.onMoved = null;
|
|---|
| 269 | removeChild(seg2.midAnc);
|
|---|
| 270 |
|
|---|
| 271 | seg1.tailAnc.onMoved = null;
|
|---|
| 272 | removeChild(seg1.tailAnc);
|
|---|
| 273 |
|
|---|
| 274 | seg1.tailAnc = seg2.tailAnc;
|
|---|
| 275 | seg1.tailAnc.backSegment = seg1;
|
|---|
| 276 |
|
|---|
| 277 | if (seg2 == mLast)
|
|---|
| 278 | mLast = seg1;
|
|---|
| 279 | }
|
|---|
| 280 |
|
|---|
| 281 | private function moveEndAnchor(seg1:Segment, seg2:Segment, x:int, y:int):void
|
|---|
| 282 | {
|
|---|
| 283 | if (seg1 != null)
|
|---|
| 284 | {
|
|---|
| 285 | seg1.x2 = x;
|
|---|
| 286 | seg1.y2 = y;
|
|---|
| 287 | }
|
|---|
| 288 |
|
|---|
| 289 | if (seg2 != null)
|
|---|
| 290 | {
|
|---|
| 291 | seg2.x1 = x;
|
|---|
| 292 | seg2.y1 = y;
|
|---|
| 293 | }
|
|---|
| 294 |
|
|---|
| 295 | }
|
|---|
| 296 |
|
|---|
| 297 | private function drawCenterLine(s:Segment):void
|
|---|
| 298 | {
|
|---|
| 299 | var g:Graphics = graphics;
|
|---|
| 300 | g.lineStyle(3, 0xffffff, 1, true);
|
|---|
| 301 | g.moveTo(s.x1, s.y1);
|
|---|
| 302 | g.lineTo(s.x2, s.y2);
|
|---|
| 303 |
|
|---|
| 304 | g.lineStyle(1, 0x000000, 1, true);
|
|---|
| 305 | g.moveTo(s.x1, s.y1);
|
|---|
| 306 | g.lineTo(s.x2, s.y2);
|
|---|
| 307 | }
|
|---|
| 308 |
|
|---|
| 309 | private static function makeHexColor(c:uint):String
|
|---|
| 310 | {
|
|---|
| 311 | var R:String = ((c&0xff0000) >> 16).toString(16);
|
|---|
| 312 | var G:String = ((c&0x00ff00) >> 8).toString(16);
|
|---|
| 313 | var B:String = ( c&0x0000ff ).toString(16);
|
|---|
| 314 |
|
|---|
| 315 | if (R.length<2) R = "0"+R;
|
|---|
| 316 | if (G.length<2) G = "0"+G;
|
|---|
| 317 | if (B.length<2) B = "0"+B;
|
|---|
| 318 |
|
|---|
| 319 | return R+G+B;
|
|---|
| 320 | }
|
|---|
| 321 |
|
|---|
| 322 | public override function serialize():String
|
|---|
| 323 | {
|
|---|
| 324 | var attrs:Array = [];
|
|---|
| 325 | var points:Array = [];
|
|---|
| 326 |
|
|---|
| 327 | attrs.push("color=\"#"+makeHexColor(mLineColor)+"\"");
|
|---|
| 328 |
|
|---|
| 329 | for(var s:Segment = mFirst; s != null; s=s.next)
|
|---|
| 330 | {
|
|---|
| 331 | points.push(s.x1+" "+s.y1);
|
|---|
| 332 | if (s.next == null)
|
|---|
| 333 | points.push(s.x2+" "+s.y2);
|
|---|
| 334 | }
|
|---|
| 335 |
|
|---|
| 336 | return "<polyline " +attrs.join(" ")+ ">" + points.join(",") + "</polyline>"
|
|---|
| 337 | }
|
|---|
| 338 | }
|
|---|
| 339 | } |
|---|