| 1 |
/* Copyright (C) 2001-2007 Peter Selinger and nitoyon. |
|---|
| 2 |
Original code(Potrace v1.8) by Peter Selinger. |
|---|
| 3 |
Ported to ActionScript 3.0 by nitoyon. |
|---|
| 4 |
This file is part of PotrAs. It is free software and it is covered |
|---|
| 5 |
by the GNU General Public License. See the file COPYING for details. */ |
|---|
| 6 |
|
|---|
| 7 |
package com.nitoyon.potras |
|---|
| 8 |
{ |
|---|
| 9 |
import flash.display.BitmapData; |
|---|
| 10 |
import flash.geom.*; |
|---|
| 11 |
|
|---|
| 12 |
public class ProcessPath |
|---|
| 13 |
{ |
|---|
| 14 |
/** |
|---|
| 15 |
* it suffices that this is longer than any |
|---|
| 16 |
* path; it need not be really infinite |
|---|
| 17 |
*/ |
|---|
| 18 |
private static const INFTY:int = 10000000; |
|---|
| 19 |
|
|---|
| 20 |
public static const POTRACE_CURVETO:int = 1; |
|---|
| 21 |
public static const POTRACE_CORNER:int = 2; |
|---|
| 22 |
|
|---|
| 23 |
/** |
|---|
| 24 |
* On success, returns a Potrace state st with st->status == |
|---|
| 25 |
* POTRACE_STATUS_OK. On failure, returns NULL if no Potrace state |
|---|
| 26 |
* could be created (with errno set), or returns an incomplete Potrace |
|---|
| 27 |
* state (with st->status == POTRACE_STATUS_INCOMPLETE). Complete or |
|---|
| 28 |
* incomplete Potrace state can be freed with potrace_state_free(). |
|---|
| 29 |
*/ |
|---|
| 30 |
public static function processPath(pathList:Array):ClosedPathList |
|---|
| 31 |
{ |
|---|
| 32 |
var curveList:ClosedPathList = new ClosedPathList(); |
|---|
| 33 |
|
|---|
| 34 |
for(var i:int = 0; i < pathList.length; i++) |
|---|
| 35 |
{ |
|---|
| 36 |
var sums:Array = ProcessPath.calcSums(pathList[i].priv as Array) as Array; |
|---|
| 37 |
var lons:Array = ProcessPath.calcLon(pathList[i].priv as Array); |
|---|
| 38 |
var po:Array = ProcessPath.bestPolygon(pathList[i].priv as Array, lons, sums); |
|---|
| 39 |
var vertex:Array = ProcessPath.adjustVertices(pathList[i].priv as Array, sums, po); |
|---|
| 40 |
var curve:ClosedPath = ProcessPath.smooth(vertex, pathList[i].sign, .9); |
|---|
| 41 |
|
|---|
| 42 |
curveList.$a.push(curve); |
|---|
| 43 |
} |
|---|
| 44 |
|
|---|
| 45 |
return curveList; |
|---|
| 46 |
} |
|---|
| 47 |
|
|---|
| 48 |
/** |
|---|
| 49 |
* Preparation: fill in the sum* fields of a path (used for later |
|---|
| 50 |
* rapid summing). Return 0 on success, 1 with errno set on |
|---|
| 51 |
* failure. |
|---|
| 52 |
*/ |
|---|
| 53 |
public static function calcSums(pt:Array):Array |
|---|
| 54 |
{ |
|---|
| 55 |
var n:int = pt.length; |
|---|
| 56 |
|
|---|
| 57 |
var sums:Array = new Array(n + 1); |
|---|
| 58 |
|
|---|
| 59 |
// origin |
|---|
| 60 |
var x0:int = pt[0].x; |
|---|
| 61 |
var y0:int = pt[0].y; |
|---|
| 62 |
|
|---|
| 63 |
// preparatory computation for later fast summing |
|---|
| 64 |
sums[0] = new Sums(); |
|---|
| 65 |
sums[0].x2 = sums[0].xy = sums[0].y2 = sums[0].x = sums[0].y = 0; |
|---|
| 66 |
for(var i:int = 0; i < n; i++) |
|---|
| 67 |
{ |
|---|
| 68 |
var x:int = pt[i].x - x0; |
|---|
| 69 |
var y:int = pt[i].y - y0; |
|---|
| 70 |
sums[i + 1] = new Sums(); |
|---|
| 71 |
sums[i + 1].x = sums[i].x + x; |
|---|
| 72 |
sums[i + 1].y = sums[i].y + y; |
|---|
| 73 |
sums[i + 1].x2 = sums[i].x2 + x*x; |
|---|
| 74 |
sums[i + 1].xy = sums[i].xy + x*y; |
|---|
| 75 |
sums[i + 1].y2 = sums[i].y2 + y*y; |
|---|
| 76 |
} |
|---|
| 77 |
|
|---|
| 78 |
|
|---|
| 79 |
return sums; |
|---|
| 80 |
} |
|---|
| 81 |
|
|---|
| 82 |
/** |
|---|
| 83 |
* Stage 1: determine the straight subpaths (Sec. 2.2.1). Fill in the |
|---|
| 84 |
* "lon" component of a path object (based on pt/len). For each i, |
|---|
| 85 |
* lon[i] is the furthest index such that a straight line can be drawn |
|---|
| 86 |
* from i to lon[i]. Return 1 on error with errno set, else 0. |
|---|
| 87 |
*/ |
|---|
| 88 |
public static function calcLon(pt:Array):Array |
|---|
| 89 |
{ |
|---|
| 90 |
var lon:Array = []; |
|---|
| 91 |
var n:int = pt.length; |
|---|
| 92 |
var j:Number; |
|---|
| 93 |
|
|---|
| 94 |
// initialize the nc data structure. Point from each point to the |
|---|
| 95 |
// furthest future point to which it is connected by a vertical or |
|---|
| 96 |
// horizontal segment. We take advantage of the fact that there is |
|---|
| 97 |
// always a direction change at 0 (due to the path decomposition |
|---|
| 98 |
// algorithm). But even if this were not so, there is no harm, as |
|---|
| 99 |
// in practice, correctness does not depend on the word "furthest" |
|---|
| 100 |
// above. |
|---|
| 101 |
var nc:Array = []; |
|---|
| 102 |
var k:int = 0; |
|---|
| 103 |
for(var i:int = n - 1; i >= 0; i--) |
|---|
| 104 |
{ |
|---|
| 105 |
if(pt[i].x != pt[k].x && pt[i].y != pt[k].y) |
|---|
| 106 |
{ |
|---|
| 107 |
k = i + 1; // necessarily i<n-1 in this case |
|---|
| 108 |
} |
|---|
| 109 |
nc[i] = k; |
|---|
| 110 |
} |
|---|
| 111 |
|
|---|
| 112 |
// determine pivot points: for each i, let pivk[i] be the furthest k |
|---|
| 113 |
// such that all j with i<j<k lie on a line connecting i,k. */ |
|---|
| 114 |
var pivk:Array = []; |
|---|
| 115 |
var ct:Array; |
|---|
| 116 |
var dir:int; |
|---|
| 117 |
var k1:int; |
|---|
| 118 |
var constraint0:Point = new Point(); |
|---|
| 119 |
var constraint1:Point = new Point(); |
|---|
| 120 |
var cur:Point = new Point(); |
|---|
| 121 |
var off:Point = new Point(); |
|---|
| 122 |
var dk:Point = new Point(); |
|---|
| 123 |
for(i = n - 1; i >= 0; i--) |
|---|
| 124 |
{ |
|---|
| 125 |
ct = [0, 0, 0, 0]; |
|---|
| 126 |
|
|---|
| 127 |
// keep track of "directions" that have occurred |
|---|
| 128 |
dir = (3 + 3 * (pt[mod(i + 1, n)].x - pt[i].x) + (pt[mod(i + 1, n)].y - pt[i].y)) / 2; |
|---|
| 129 |
ct[dir]++; |
|---|
| 130 |
constraint0.x = constraint0.y = constraint1.x = constraint1.y = 0; |
|---|
| 131 |
|
|---|
| 132 |
// find the next k such that no straight line from i to k |
|---|
| 133 |
k = nc[i]; |
|---|
| 134 |
k1 = i; |
|---|
| 135 |
var foundk:Boolean = false; |
|---|
| 136 |
while(true) |
|---|
| 137 |
{ |
|---|
| 138 |
dir = (3 + 3 * sign(pt[k].x - pt[k1].x) + sign(pt[k].y - pt[k1].y)) / 2; |
|---|
| 139 |
ct[dir]++; |
|---|
| 140 |
|
|---|
| 141 |
// if all four "directions" have occurred, cut this path |
|---|
| 142 |
if(ct[0] && ct[1] && ct[2] && ct[3]) |
|---|
| 143 |
{ |
|---|
| 144 |
pivk[i] = k1; |
|---|
| 145 |
foundk = true; |
|---|
| 146 |
break; |
|---|
| 147 |
} |
|---|
| 148 |
|
|---|
| 149 |
cur.x = pt[k].x - pt[i].x; |
|---|
| 150 |
cur.y = pt[k].y - pt[i].y; |
|---|
| 151 |
|
|---|
| 152 |
// see if current constraint is violated |
|---|
| 153 |
if(xprod(constraint0, cur) > 0 || xprod(constraint1, cur) < 0) |
|---|
| 154 |
{ |
|---|
| 155 |
break; |
|---|
| 156 |
} |
|---|
| 157 |
|
|---|
| 158 |
// else, update constraint |
|---|
| 159 |
if (Math.abs(cur.x) <= 1 && Math.abs(cur.y) <= 1) |
|---|
| 160 |
{ |
|---|
| 161 |
// no constraint |
|---|
| 162 |
} |
|---|
| 163 |
else |
|---|
| 164 |
{ |
|---|
| 165 |
off.x = cur.x + ((-cur.y >= 0 && (-cur.y > 0 || cur.x < 0)) ? 1 : -1); |
|---|
| 166 |
off.y = cur.y + ((-cur.x <= 0 && (-cur.x < 0 || cur.y < 0)) ? 1 : -1); |
|---|
| 167 |
if(xprod(constraint0, off) <= 0) |
|---|
| 168 |
{ |
|---|
| 169 |
constraint0.x = off.x; |
|---|
| 170 |
constraint0.y = off.y; |
|---|
| 171 |
} |
|---|
| 172 |
|
|---|
| 173 |
off.x = cur.x + ((-cur.y <= 0 && (-cur.y < 0 || cur.x < 0)) ? 1 : -1); |
|---|
| 174 |
off.y = cur.y + ((-cur.x >= 0 && (-cur.x > 0 || cur.y < 0)) ? 1 : -1); |
|---|
| 175 |
if(xprod(constraint1, off) >= 0) |
|---|
| 176 |
{ |
|---|
| 177 |
constraint1.x = off.x; |
|---|
| 178 |
constraint1.y = off.y; |
|---|
| 179 |
} |
|---|
| 180 |
} |
|---|
| 181 |
|
|---|
| 182 |
k1 = k; |
|---|
| 183 |
k = nc[k1]; |
|---|
| 184 |
if(!cyclic(k, i, k1)) |
|---|
| 185 |
{ |
|---|
| 186 |
break; |
|---|
| 187 |
} |
|---|
| 188 |
} |
|---|
| 189 |
|
|---|
| 190 |
// constraint |
|---|
| 191 |
if(!foundk) |
|---|
| 192 |
{ |
|---|
| 193 |
dk.x = sign(pt[k].x-pt[k1].x); |
|---|
| 194 |
dk.y = sign(pt[k].y-pt[k1].y); |
|---|
| 195 |
cur.x = pt[k1].x - pt[i].x; |
|---|
| 196 |
cur.y = pt[k1].y - pt[i].y; |
|---|
| 197 |
|
|---|
| 198 |
// find largest integer j such that xprod(constraint0, cur+j*dk) >= 0 |
|---|
| 199 |
// and xprod(constraint1, cur+j*dk) <= 0. Use bilinearity of xprod. |
|---|
| 200 |
var a:int = xprod(constraint0, cur); |
|---|
| 201 |
var b:int = xprod(constraint0, dk); |
|---|
| 202 |
var c:int = xprod(constraint1, cur); |
|---|
| 203 |
var d:int = xprod(constraint1, dk); |
|---|
| 204 |
|
|---|
| 205 |
// find largest integer j such that a+j*b>=0 and c+j*d<=0. |
|---|
| 206 |
// This can be solved with integer arithmetic. |
|---|
| 207 |
j = INFTY; |
|---|
| 208 |
if(b > 0) |
|---|
| 209 |
{ |
|---|
| 210 |
j = floordiv(-a, b); |
|---|
| 211 |
} |
|---|
| 212 |
if(d < 0) |
|---|
| 213 |
{ |
|---|
| 214 |
j = Math.min(j, floordiv(c, -d)); |
|---|
| 215 |
} |
|---|
| 216 |
pivk[i] = mod(k1 + j, n); |
|---|
| 217 |
} |
|---|
| 218 |
|
|---|
| 219 |
// foundk |
|---|
| 220 |
} // for i |
|---|
| 221 |
|
|---|
| 222 |
// clean up: for each i, let lon[i] be the largest k such that for |
|---|
| 223 |
// all i' with i<=i'<k, i'<k<=pivk[i']. |
|---|
| 224 |
j = pivk[n - 1]; |
|---|
| 225 |
lon[n - 1] = j; |
|---|
| 226 |
for(i = n - 2; i >= 0; i--) |
|---|
| 227 |
{ |
|---|
| 228 |
if(cyclic(i + 1, pivk[i], j)) |
|---|
| 229 |
{ |
|---|
| 230 |
j = pivk[i]; |
|---|
| 231 |
} |
|---|
| 232 |
lon[i] = j; |
|---|
| 233 |
} |
|---|
| 234 |
|
|---|
| 235 |
for(i = n - 1; cyclic(mod(i + 1, n), j, lon[i]); i--) |
|---|
| 236 |
{ |
|---|
| 237 |
lon[i] = j; |
|---|
| 238 |
} |
|---|
| 239 |
|
|---|
| 240 |
return lon; |
|---|
| 241 |
} |
|---|
| 242 |
|
|---|
| 243 |
/** |
|---|
| 244 |
* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). |
|---|
| 245 |
* |
|---|
| 246 |
* find the optimal polygon. Fill in the m and po components. Return 1 |
|---|
| 247 |
* on failure with errno set, else 0. Non-cyclic version: assumes i=0 |
|---|
| 248 |
* is in the polygon. Fixme: ### implement cyclic version. |
|---|
| 249 |
*/ |
|---|
| 250 |
public static function bestPolygon(pt:Array, lon:Array, sums:Array):Array |
|---|
| 251 |
{ |
|---|
| 252 |
var i:int, j:int, k:int; |
|---|
| 253 |
var n:int = pt.length; |
|---|
| 254 |
|
|---|
| 255 |
var clip0:Array = new Array(n); // clip0[n]: longest segment pointer, non-cyclic |
|---|
| 256 |
var clip1:Array = new Array(n + 1); // clip1[n+1]: backwards segment pointer, non-cyclic |
|---|
| 257 |
|
|---|
| 258 |
// calculate clipped paths |
|---|
| 259 |
for(i = 0; i < n; i++) |
|---|
| 260 |
{ |
|---|
| 261 |
var c:int = mod(lon[mod(i - 1, n)] - 1, n); |
|---|
| 262 |
if(c == i) |
|---|
| 263 |
{ |
|---|
| 264 |
c = mod(i + 1, n); |
|---|
| 265 |
} |
|---|
| 266 |
if(c < i) |
|---|
| 267 |
{ |
|---|
| 268 |
clip0[i] = n; |
|---|
| 269 |
} |
|---|
| 270 |
else |
|---|
| 271 |
{ |
|---|
| 272 |
clip0[i] = c; |
|---|
| 273 |
} |
|---|
| 274 |
} |
|---|
| 275 |
|
|---|
| 276 |
// calculate backwards path clipping, non-cyclic. j <= clip0[i] iff clip1[j] <= i, for i,j=0..n. |
|---|
| 277 |
j = 1; |
|---|
| 278 |
for(i = 0; i < n; i++) |
|---|
| 279 |
{ |
|---|
| 280 |
while(j <= clip0[i]) |
|---|
| 281 |
{ |
|---|
| 282 |
clip1[j] = i; |
|---|
| 283 |
j++; |
|---|
| 284 |
} |
|---|
| 285 |
} |
|---|
| 286 |
|
|---|
| 287 |
var seg0:Array = new Array(n + 1); // seg0[m+1]: forward segment bounds, m<=n |
|---|
| 288 |
var seg1:Array = new Array(n + 1); // seg1[m+1]: backward segment bounds, m<=n |
|---|
| 289 |
|
|---|
| 290 |
// calculate seg0[j] = longest path from 0 with j segments |
|---|
| 291 |
i = 0; |
|---|
| 292 |
for(j = 0; i < n; j++) |
|---|
| 293 |
{ |
|---|
| 294 |
seg0[j] = i; |
|---|
| 295 |
i = clip0[i]; |
|---|
| 296 |
} |
|---|
| 297 |
seg0[j] = n; |
|---|
| 298 |
var m:int = j; |
|---|
| 299 |
|
|---|
| 300 |
// calculate seg1[j] = longest path to n with m-j segments |
|---|
| 301 |
i = n; |
|---|
| 302 |
for(j = m; j > 0; j--) |
|---|
| 303 |
{ |
|---|
| 304 |
seg1[j] = i; |
|---|
| 305 |
i = clip1[i]; |
|---|
| 306 |
} |
|---|
| 307 |
seg1[0] = 0; |
|---|
| 308 |
|
|---|
| 309 |
// now find the shortest path with m segments, based on penalty3 |
|---|
| 310 |
// note: the outer 2 loops jointly have at most n interations, thus |
|---|
| 311 |
// the worst-case behavior here is quadratic. In practice, it is |
|---|
| 312 |
// close to linear since the inner loop tends to be short. |
|---|
| 313 |
var pen:Array = new Array(n + 1); // pen[n+1]: penalty vector |
|---|
| 314 |
var prev:Array = new Array(n + 1); // prev[n+1]: best path pointer vector |
|---|
| 315 |
var thispen:Number; |
|---|
| 316 |
pen[0] = 0; |
|---|
| 317 |
for(j = 1; j <= m; j++) |
|---|
| 318 |
{ |
|---|
| 319 |
for(i = seg1[j]; i <= seg0[j]; i++) |
|---|
| 320 |
{ |
|---|
| 321 |
var best:Number = -1; |
|---|
| 322 |
for(k = seg0[j - 1]; k >= clip1[i]; k--) |
|---|
| 323 |
{ |
|---|
| 324 |
thispen = penalty3(pt, k, i, sums) + pen[k]; |
|---|
| 325 |
if(best < 0 || thispen < best) |
|---|
| 326 |
{ |
|---|
| 327 |
prev[i] = k; |
|---|
| 328 |
best = thispen; |
|---|
| 329 |
} |
|---|
| 330 |
} |
|---|
| 331 |
pen[i] = best; |
|---|
| 332 |
} |
|---|
| 333 |
} |
|---|
| 334 |
|
|---|
| 335 |
// read off shortest path |
|---|
| 336 |
var po:Array = new Array(m); |
|---|
| 337 |
for(i = n, j = m - 1; i > 0; j--) |
|---|
| 338 |
{ |
|---|
| 339 |
po[j] = i = prev[i]; |
|---|
| 340 |
} |
|---|
| 341 |
|
|---|
| 342 |
return po; |
|---|
| 343 |
} |
|---|
| 344 |
|
|---|
| 345 |
/** |
|---|
| 346 |
* Auxiliary function: calculate the penalty of an edge from i to j in |
|---|
| 347 |
* the given path. This needs the "lon" and "sum*" data. |
|---|
| 348 |
*/ |
|---|
| 349 |
private static function penalty3(pt:Array, i:int, j:int, sums:Array):Number |
|---|
| 350 |
{ |
|---|
| 351 |
var n:int = pt.length; |
|---|
| 352 |
|
|---|
| 353 |
// assume 0 <= i < j <= n |
|---|
| 354 |
var r:int = 0; // rotations from i to j |
|---|
| 355 |
|
|---|
| 356 |
if(j >= n) |
|---|
| 357 |
{ |
|---|
| 358 |
j -= n; |
|---|
| 359 |
r += 1; |
|---|
| 360 |
} |
|---|
| 361 |
|
|---|
| 362 |
var x:Number = sums[j + 1].x - sums[i].x + r * sums[n].x; |
|---|
| 363 |
var y:Number = sums[j + 1].y - sums[i].y + r * sums[n].y; |
|---|
| 364 |
var x2:Number = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2; |
|---|
| 365 |
var xy:Number = sums[j + 1].xy - sums[i].xy + r * sums[n].xy; |
|---|
| 366 |
var y2:Number = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2; |
|---|
| 367 |
var k:Number = j + 1 - i + r * n; |
|---|
| 368 |
|
|---|
| 369 |
var px:Number = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; |
|---|
| 370 |
var py:Number = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; |
|---|
| 371 |
var ey:Number = (pt[j].x - pt[i].x); |
|---|
| 372 |
var ex:Number = -(pt[j].y - pt[i].y); |
|---|
| 373 |
|
|---|
| 374 |
var a:Number = ((x2 - 2 * x * px) / k + px * px); |
|---|
| 375 |
var b:Number = ((xy - x * py - y * px) / k + px * py); |
|---|
| 376 |
var c:Number = ((y2 - 2 * y * py) / k + py * py); |
|---|
| 377 |
|
|---|
| 378 |
var s:Number = ex * ex * a + 2 * ex * ey * b + ey * ey * c; |
|---|
| 379 |
|
|---|
| 380 |
return Math.sqrt(s); |
|---|
| 381 |
} |
|---|
| 382 |
|
|---|
| 383 |
/** |
|---|
| 384 |
/* Stage 3: vertex adjustment (Sec. 2.3.1). |
|---|
| 385 |
* |
|---|
| 386 |
* Adjust vertices of optimal polygon: calculate the intersection of |
|---|
| 387 |
* the two "optimal" line segments, then move it into the unit square |
|---|
| 388 |
* if it lies outside. Return 1 with errno set on error; 0 on |
|---|
| 389 |
* success. |
|---|
| 390 |
*/ |
|---|
| 391 |
public static function adjustVertices(pt:Array, sums:Array, po:Array):Array |
|---|
| 392 |
{ |
|---|
| 393 |
var m:int = po.length; |
|---|
| 394 |
var n:int = pt.length; |
|---|
| 395 |
|
|---|
| 396 |
var i:int, j:int, k:int, l:int; |
|---|
| 397 |
|
|---|
| 398 |
// represent each line segment as a singular quadratic form; the |
|---|
| 399 |
// distance of a point (x,y) from the line segment will be |
|---|
| 400 |
// (x,y,1)Q(x,y,1)^t, where Q=q[i]. |
|---|
| 401 |
var q:Array = []; |
|---|
| 402 |
var v:Point3D = new Point3D(); |
|---|
| 403 |
var ctr:Point = new Point(); |
|---|
| 404 |
var dir:Point = new Point(); |
|---|
| 405 |
for(i = 0; i < m; i++) |
|---|
| 406 |
{ |
|---|
| 407 |
// calculate "optimal" point-slope representation for each line segment |
|---|
| 408 |
j = po[(i + 1) % m]; |
|---|
| 409 |
j = mod(j - po[i], n) + po[i]; |
|---|
| 410 |
ctr.x = ctr.y = dir.x = dir.y = 0; |
|---|
| 411 |
pointslope(pt, sums, po[i], j, ctr, dir); |
|---|
| 412 |
|
|---|
| 413 |
q[i] = new QuadraticForm(); |
|---|
| 414 |
var d1:Number = dir.x * dir.x + dir.y * dir.y; |
|---|
| 415 |
if(d1 != 0.0) |
|---|
| 416 |
{ |
|---|
| 417 |
v[0] = dir.y; |
|---|
| 418 |
v[1] = -dir.x; |
|---|
| 419 |
v[2] = -v[1] * ctr.y - v[0] * ctr.x |
|---|
| 420 |
q[i].fromVectorMultiply(v).scalar(1 / d1); |
|---|
| 421 |
} |
|---|
| 422 |
} |
|---|
| 423 |
|
|---|
| 424 |
// now calculate the "intersections" of consecutive segments. |
|---|
| 425 |
// Instead of using the actual intersection, we find the point |
|---|
| 426 |
// within a given unit square which minimizes the square distance to |
|---|
| 427 |
// the two lines. |
|---|
| 428 |
var vertex:Array = []; |
|---|
| 429 |
var p0:Point = pt[0].clone(); |
|---|
| 430 |
var s:Point = new Point(); |
|---|
| 431 |
var w:Point = new Point(); |
|---|
| 432 |
var _q:QuadraticForm = new QuadraticForm(); |
|---|
| 433 |
var minCoord:Point = new Point; |
|---|
| 434 |
for(i = 0; i < m; i++) |
|---|
| 435 |
{ |
|---|
| 436 |
var z:int; |
|---|
| 437 |
vertex[i] = new Point(); |
|---|
| 438 |
|
|---|
| 439 |
// let s be the vertex, in coordinates relative to x0/y0 |
|---|
| 440 |
s.x = pt[po[i]].x - p0.x; |
|---|
| 441 |
s.y = pt[po[i]].y - p0.y; |
|---|
| 442 |
|
|---|
| 443 |
// intersect segments i-1 and i |
|---|
| 444 |
j = mod(i - 1, m); |
|---|
| 445 |
|
|---|
| 446 |
// add quadratic forms |
|---|
| 447 |
var Q:QuadraticForm = q[j].clone().add(q[i]); |
|---|
| 448 |
|
|---|
| 449 |
while(1) |
|---|
| 450 |
{ |
|---|
| 451 |
// minimize the quadratic form Q on the unit square |
|---|
| 452 |
// find intersection |
|---|
| 453 |
var det:Number = Q[0][0] * Q[1][1] - Q[0][1] * Q[1][0]; |
|---|
| 454 |
if(det != 0.0) |
|---|
| 455 |
{ |
|---|
| 456 |
w.x = (-Q[0][2] * Q[1][1] + Q[1][2] * Q[0][1]) / det; |
|---|
| 457 |
w.y = ( Q[0][2] * Q[1][0] - Q[1][2] * Q[0][0]) / det; |
|---|
| 458 |
break; |
|---|
| 459 |
} |
|---|
| 460 |
|
|---|
| 461 |
// matrix is singular - lines are parallel. Add another, |
|---|
| 462 |
// orthogonal axis, through the center of the unit square |
|---|
| 463 |
if(Q[0][0] > Q[1][1]) |
|---|
| 464 |
{ |
|---|
| 465 |
v[0] = -Q[0][1]; |
|---|
| 466 |
v[1] = Q[0][0]; |
|---|
| 467 |
} |
|---|
| 468 |
else if(Q[1][1]) |
|---|
| 469 |
{ |
|---|
| 470 |
v[0] = -Q[1][1]; |
|---|
| 471 |
v[1] = Q[1][0]; |
|---|
| 472 |
} |
|---|
| 473 |
else |
|---|
| 474 |
{ |
|---|
| 475 |
v[0] = 1; |
|---|
| 476 |
v[1] = 0; |
|---|
| 477 |
} |
|---|
| 478 |
|
|---|
| 479 |
var d:Number = v[0] * v[0] + v[1] * v[1]; |
|---|
| 480 |
v[2] = - v[1] * s.y - v[0] * s.x; |
|---|
| 481 |
Q.add(_q.fromVectorMultiply(v)).scalar(1 / d); |
|---|
| 482 |
} |
|---|
| 483 |
|
|---|
| 484 |
var dx:Number = Math.abs(w.x - s.x); |
|---|
| 485 |
var dy:Number = Math.abs(w.y - s.y); |
|---|
| 486 |
if(dx <= .5 && dy <= .5) |
|---|
| 487 |
{ |
|---|
| 488 |
vertex[i].x = w.x + p0.x; |
|---|
| 489 |
vertex[i].y = w.y + p0.y; |
|---|
| 490 |
continue; |
|---|
| 491 |
} |
|---|
| 492 |
|
|---|
| 493 |
// the minimum was not in the unit square; now minimize quadratic |
|---|
| 494 |
// on boundary of square |
|---|
| 495 |
var min:Number, cand:Number; // minimum and candidate for minimum of quad. form |
|---|
| 496 |
minCoord.x = s.x; minCoord.y = s.y; // coordinates of minimum |
|---|
| 497 |
min = Q.apply(s); |
|---|
| 498 |
|
|---|
| 499 |
if(Q[0][0] != 0.0) |
|---|
| 500 |
{ |
|---|
| 501 |
for(z = 0; z < 2; z++) // value of the y-coordinate |
|---|
| 502 |
{ |
|---|
| 503 |
w.y = s.y - 0.5 + z; |
|---|
| 504 |
w.x = - (Q[0][1] * w.y + Q[0][2]) / Q[0][0]; |
|---|
| 505 |
dx = (w.x - s.x > 0 ? w.x - s.x : s.x - w.x); |
|---|
| 506 |
cand = Q.apply(w); |
|---|
| 507 |
if(dx <= .5 && cand < min) |
|---|
| 508 |
{ |
|---|
| 509 |
min = cand; |
|---|
| 510 |
minCoord.x = w.x; minCoord.y = w.y; |
|---|
| 511 |
} |
|---|
| 512 |
} |
|---|
| 513 |
} |
|---|
| 514 |
|
|---|
| 515 |
if(Q[1][1] != 0.0) |
|---|
| 516 |
{ |
|---|
| 517 |
for(z = 0; z < 2; z++) // value of the x-coordinate |
|---|
| 518 |
{ |
|---|
| 519 |
w.x = s.x - 0.5 + z; |
|---|
| 520 |
w.y = - (Q[1][0] * w.x + Q[1][2]) / Q[1][1]; |
|---|
| 521 |
dy = (w.y - s.y > 0 ? w.y - s.y : s.y - w.y); |
|---|
| 522 |
cand = Q.apply(w); |
|---|
| 523 |
if(dy <= .5 && cand < min) |
|---|
| 524 |
{ |
|---|
| 525 |
min = cand; |
|---|
| 526 |
minCoord.x = w.x; minCoord.y = w.y; |
|---|
| 527 |
} |
|---|
| 528 |
} |
|---|
| 529 |
} |
|---|
| 530 |
|
|---|
| 531 |
// check four corners |
|---|
| 532 |
for(l = 0; l < 2; l++) |
|---|
| 533 |
{ |
|---|
| 534 |
for(k = 0; k < 2; k++) |
|---|
| 535 |
{ |
|---|
| 536 |
w.x = s.x - 0.5 + l; |
|---|
| 537 |
w.y = s.y - 0.5 + k; |
|---|
| 538 |
cand = Q.apply(w); |
|---|
| 539 |
if(cand < min) |
|---|
| 540 |
{ |
|---|
| 541 |
min = cand; |
|---|
| 542 |
minCoord.x = w.x; minCoord.y = w.y; |
|---|
| 543 |
} |
|---|
| 544 |
} |
|---|
| 545 |
} |
|---|
| 546 |
|
|---|
| 547 |
vertex[i].x = minCoord.x + p0.x; |
|---|
| 548 |
vertex[i].y = minCoord.y + p0.y; |
|---|
| 549 |
continue; |
|---|
| 550 |
} |
|---|
| 551 |
|
|---|
| 552 |
return vertex; |
|---|
| 553 |
} |
|---|
| 554 |
|
|---|
| 555 |
/** |
|---|
| 556 |
* Stage 4: smoothing and corner analysis (Sec. 2.3.3) |
|---|
| 557 |
* |
|---|
| 558 |
* Always succeeds and returns 0 |
|---|
| 559 |
*/ |
|---|
| 560 |
public static function smooth(vertex:Array, sign:String, alphamax:Number = 1.0):ClosedPath |
|---|
| 561 |
{ |
|---|
| 562 |
var m:int = vertex.length; |
|---|
| 563 |
var closedPath:ClosedPath = new ClosedPath(); |
|---|
| 564 |
|
|---|
| 565 |
var i:int; |
|---|
| 566 |
|
|---|
| 567 |
if(sign == '-') |
|---|
| 568 |
{ |
|---|
| 569 |
// reverse orientation of negative paths |
|---|
| 570 |
var tmp:Point; |
|---|
| 571 |
for(i = 0, j = m - 1; i < j; i++, j--) |
|---|
| 572 |
{ |
|---|
| 573 |
tmp = vertex[i]; |
|---|
| 574 |
vertex[i] = vertex[j]; |
|---|
| 575 |
vertex[j] = tmp; |
|---|
| 576 |
} |
|---|
| 577 |
} |
|---|
| 578 |
|
|---|
| 579 |
// examine each vertex and find its best fit |
|---|
| 580 |
var p2:Point = new Point(); |
|---|
| 581 |
var p3:Point = new Point(); |
|---|
| 582 |
var p4:Point = new Point(); |
|---|
| 583 |
for(i = 0; i < m; i++) |
|---|
| 584 |
{ |
|---|
| 585 |
var j:int = (i + 1) % m; |
|---|
| 586 |
var k:int = (i + 2) % m; |
|---|
| 587 |
interval(1 / 2.0, vertex[k], vertex[j], p4); |
|---|
| 588 |
|
|---|
| 589 |
var curve:Curve = new Curve(); |
|---|
| 590 |
curve.vertex.x =vertex[j].x; |
|---|
| 591 |
curve.vertex.y =vertex[j].y; |
|---|
| 592 |
|
|---|
| 593 |
var denom:Number = ddenom(vertex[i], vertex[k]); |
|---|
| 594 |
var alpha:Number; |
|---|
| 595 |
if(denom != 0.0) |
|---|
| 596 |
{ |
|---|
| 597 |
var dd:Number = dpara(vertex[i], vertex[j], vertex[k]) / denom; |
|---|
| 598 |
dd = dd < 0 ? -dd : dd; |
|---|
| 599 |
alpha = dd > 1 ? (1 - 1.0 / dd) / 0.75 : 0; |
|---|
| 600 |
} |
|---|
| 601 |
else |
|---|
| 602 |
{ |
|---|
| 603 |
alpha = 4 / 3.0; |
|---|
| 604 |
} |
|---|
| 605 |
curve.alpha0 = alpha; // remember "original" value of alpha |
|---|
| 606 |
|
|---|
| 607 |
if(alpha > alphamax) // pointed corner |
|---|
| 608 |
{ |
|---|
| 609 |
curve.tag = POTRACE_CORNER; |
|---|
| 610 |
curve.c[0].x = 0; curve.c[0].y = 0; |
|---|
| 611 |
curve.c[1].x = vertex[j].x; curve.c[1].y = vertex[j].y; |
|---|
| 612 |
curve.c[2].x = p4.x; curve.c[2].y = p4.y; |
|---|
| 613 |
} |
|---|
| 614 |
else |
|---|
| 615 |
{ |
|---|
| 616 |
if(alpha < 0.55) |
|---|
| 617 |
{ |
|---|
| 618 |
alpha = 0.55; |
|---|
| 619 |
} |
|---|
| 620 |
else if(alpha > 1) |
|---|
| 621 |
{ |
|---|
| 622 |
alpha = 1; |
|---|
| 623 |
} |
|---|
| 624 |
interval(.5 + .5 * alpha, vertex[i], vertex[j], p2); |
|---|
| 625 |
interval(.5 + .5 * alpha, vertex[k], vertex[j], p3); |
|---|
| 626 |
curve.tag = POTRACE_CURVETO; |
|---|
| 627 |
curve.c[0].x = p2.x; curve.c[0].y = p2.y; |
|---|
| 628 |
curve.c[1].x = p3.x; curve.c[1].y = p3.y; |
|---|
| 629 |
curve.c[2].x = p4.x; curve.c[2].y = p4.y; |
|---|
| 630 |
} |
|---|
| 631 |
curve.alpha = alpha; // store the "cropped" value of alpha |
|---|
| 632 |
curve.beta = 0.5; |
|---|
| 633 |
|
|---|
| 634 |
closedPath.$a[j] = curve; |
|---|
| 635 |
} |
|---|
| 636 |
|
|---|
| 637 |
return closedPath; |
|---|
| 638 |
} |
|---|
| 639 |
|
|---|
| 640 |
/** |
|---|
| 641 |
* determine the center and slope of the line i..j. Assume i<j. Needs |
|---|
| 642 |
* "sum" components of p to be set. |
|---|
| 643 |
*/ |
|---|
| 644 |
public static function pointslope(pt:Array, sums:Array, i:int, j:int, ctr:Point, dir:Point):void |
|---|
| 645 |
{ |
|---|
| 646 |
// assume i<j |
|---|
| 647 |
|
|---|
| 648 |
var n:int = pt.length; |
|---|
| 649 |
|
|---|
| 650 |
var x:Number, y:Number, x2:Number, xy:Number, y2:Number; |
|---|
| 651 |
var k:Number; |
|---|
| 652 |
var a:Number, b:Number, c:Number, lambda2:Number, l:Number; |
|---|
| 653 |
var r:int = 0; // rotations from i to j |
|---|
| 654 |
|
|---|
| 655 |
while (j >= n) |
|---|
| 656 |
{ |
|---|
| 657 |
j -= n; |
|---|
| 658 |
r += 1; |
|---|
| 659 |
} |
|---|
| 660 |
while(i >= n) |
|---|
| 661 |
{ |
|---|
| 662 |
i -= n; |
|---|
| 663 |
r -= 1; |
|---|
| 664 |
} |
|---|
| 665 |
while(j < 0) |
|---|
| 666 |
{ |
|---|
| 667 |
j += n; |
|---|
| 668 |
r -= 1; |
|---|
| 669 |
} |
|---|
| 670 |
while(i < 0) |
|---|
| 671 |
{ |
|---|
| 672 |
i += n; |
|---|
| 673 |
r += 1; |
|---|
| 674 |
} |
|---|
| 675 |
|
|---|
| 676 |
x = sums[j + 1].x - sums[i].x + r * sums[n].x; |
|---|
| 677 |
y = sums[j + 1].y - sums[i].y + r * sums[n].y; |
|---|
| 678 |
x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2; |
|---|
| 679 |
xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy; |
|---|
| 680 |
y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2; |
|---|
| 681 |
k = j + 1 - i + r * n; |
|---|
| 682 |
|
|---|
| 683 |
ctr.x = x / k; |
|---|
| 684 |
ctr.y = y / k; |
|---|
| 685 |
|
|---|
| 686 |
a = (x2 - x * x / k) / k; |
|---|
| 687 |
b = (xy - x * y / k) / k; |
|---|
| 688 |
c = (y2 - y * y / k) / k; |
|---|
| 689 |
|
|---|
| 690 |
lambda2 = (a + c + Math.sqrt((a - c)*(a - c) + 4 * b * b)) / 2; // larger e.value |
|---|
| 691 |
|
|---|
| 692 |
// now find e.vector for lambda2 |
|---|
| 693 |
a -= lambda2; |
|---|
| 694 |
c -= lambda2; |
|---|
| 695 |
|
|---|
| 696 |
if(Math.abs(a) >= Math.abs(c)) |
|---|
| 697 |
{ |
|---|
| 698 |
l = Math.sqrt(a * a + b * b); |
|---|
| 699 |
if(l != 0) |
|---|
| 700 |
{ |
|---|
| 701 |
dir.x = -b / l; |
|---|
| 702 |
dir.y = a / l; |
|---|
| 703 |
} |
|---|
| 704 |
} |
|---|
| 705 |
else |
|---|
| 706 |
{ |
|---|
| 707 |
l = Math.sqrt(c * c + b * b); |
|---|
| 708 |
if (l != 0) |
|---|
| 709 |
{ |
|---|
| 710 |
dir.x = -c / l; |
|---|
| 711 |
dir.y = b / l; |
|---|
| 712 |
} |
|---|
| 713 |
} |
|---|
| 714 |
if(l == 0) |
|---|
| 715 |
{ |
|---|
| 716 |
dir.x = dir.y = 0; // sometimes this can happen when k=4: |
|---|
| 717 |
// the two eigenvalues coincide |
|---|
| 718 |
} |
|---|
| 719 |
} |
|---|
| 720 |
|
|---|
| 721 |
/** |
|---|
| 722 |
* range over the straight line segment [a,b] when lambda ranges over [0,1] |
|---|
| 723 |
*/ |
|---|
| 724 |
public static function interval(lambda:Number, a:Point, b:Point, ret:Point):void |
|---|
| 725 |
{ |
|---|
| 726 |
ret.x = a.x + lambda * (b.x - a.x); |
|---|
| 727 |
ret.y = a.y + lambda * (b.y - a.y); |
|---|
| 728 |
} |
|---|
| 729 |
|
|---|
| 730 |
/** |
|---|
| 731 |
* some useful macros. Note: the "mod" macro works correctly for |
|---|
| 732 |
* negative a. Also note that the test for a>=n, while redundant, |
|---|
| 733 |
* speeds up the mod function by 70% in the average case (significant |
|---|
| 734 |
* since the program spends about 16% of its time here - or 40% |
|---|
| 735 |
* without the test). The "floordiv" macro returns the largest integer |
|---|
| 736 |
* <= a/n, and again this works correctly for negative a, as long as |
|---|
| 737 |
* a,n are integers and n>0. |
|---|
| 738 |
*/ |
|---|
| 739 |
private static function mod(a:int, n:int):int |
|---|
| 740 |
{ |
|---|
| 741 |
return a >= n ? a % n : a >= 0 ? a : n - 1 - (-1 - a) % n; |
|---|
| 742 |
} |
|---|
| 743 |
|
|---|
| 744 |
private static function floordiv(a:int, n:int):int |
|---|
| 745 |
{ |
|---|
| 746 |
return a >= 0 ? a / n : -1 - (-1 - a) / n; |
|---|
| 747 |
} |
|---|
| 748 |
|
|---|
| 749 |
/** |
|---|
| 750 |
* calculate p1 x p2 |
|---|
| 751 |
*/ |
|---|
| 752 |
private static function xprod(p1:Point, p2:Point):int |
|---|
| 753 |
{ |
|---|
| 754 |
return p1.x * p2.y - p1.y * p2.x; |
|---|
| 755 |
} |
|---|
| 756 |
|
|---|
| 757 |
/** |
|---|
| 758 |
* sign |
|---|
| 759 |
*/ |
|---|
| 760 |
private static function sign(x:Number):int |
|---|
| 761 |
{ |
|---|
| 762 |
return (x > 0 ? 1 : x < 0 ? -1 : 0); |
|---|
| 763 |
} |
|---|
| 764 |
|
|---|
| 765 |
/** |
|---|
| 766 |
* return a direction that is 90 degrees counterclockwise from p2-p0, |
|---|
| 767 |
* but then restricted to one of the major wind directions (n, nw, w, etc) |
|---|
| 768 |
*/ |
|---|
| 769 |
public static function dorth_infty(p0:Point, p2:Point):Point |
|---|
| 770 |
{ |
|---|
| 771 |
return new Point( |
|---|
| 772 |
sign(p2.x - p0.x), |
|---|
| 773 |
-sign(p2.y - p0.y) |
|---|
| 774 |
); |
|---|
| 775 |
} |
|---|
| 776 |
|
|---|
| 777 |
/** |
|---|
| 778 |
* return (p1-p0)x(p2-p0), the area of the parallelogram |
|---|
| 779 |
*/ |
|---|
| 780 |
static public function dpara(p0:Point, p1:Point, p2:Point):Number |
|---|
| 781 |
{ |
|---|
| 782 |
var x1:Number = p1.x - p0.x; |
|---|
| 783 |
var y1:Number = p1.y - p0.y; |
|---|
| 784 |
var x2:Number = p2.x - p0.x; |
|---|
| 785 |
var y2:Number = p2.y - p0.y; |
|---|
| 786 |
|
|---|
| 787 |
return x1 * y2 - x2 * y1; |
|---|
| 788 |
} |
|---|
| 789 |
|
|---|
| 790 |
/** |
|---|
| 791 |
* ddenom/dpara have the property that the square of radius 1 centered |
|---|
| 792 |
* at p1 intersects the line p0p2 iff |dpara(p0,p1,p2)| <= ddenom(p0,p2) |
|---|
| 793 |
*/ |
|---|
| 794 |
public static function ddenom(p0:Point, p2:Point):Number |
|---|
| 795 |
{ |
|---|
| 796 |
var r:Point = dorth_infty(p0, p2); |
|---|
| 797 |
return r.y * (p2.x - p0.x) - r.x * (p2.y - p0.y); |
|---|
| 798 |
} |
|---|
| 799 |
|
|---|
| 800 |
/** |
|---|
| 801 |
* return 1 if a <= b < c < a, in a cyclic sense (mod n) |
|---|
| 802 |
*/ |
|---|
| 803 |
private static function cyclic(a:int, b:int, c:int):Boolean |
|---|
| 804 |
{ |
|---|
| 805 |
if (a <= c) |
|---|
| 806 |
{ |
|---|
| 807 |
return (a <= b) && (b < c); |
|---|
| 808 |
} |
|---|
| 809 |
else |
|---|
| 810 |
{ |
|---|
| 811 |
return (a <= b) || (b < c); |
|---|
| 812 |
} |
|---|
| 813 |
} |
|---|
| 814 |
} |
|---|
| 815 |
} |
|---|
| 816 |
|
|---|
| 817 |
|
|---|
| 818 |
internal class Sums |
|---|
| 819 |
{ |
|---|
| 820 |
public var x:Number; |
|---|
| 821 |
public var y:Number; |
|---|
| 822 |
public var x2:Number; |
|---|
| 823 |
public var xy:Number; |
|---|
| 824 |
public var y2:Number; |
|---|
| 825 |
} |
|---|
| 826 |
|
|---|
| 827 |
import flash.geom.Point; |
|---|
| 828 |
import flash.utils.Proxy; |
|---|
| 829 |
import flash.utils.flash_proxy; |
|---|
| 830 |
import flash.errors.IllegalOperationError; |
|---|
| 831 |
|
|---|
| 832 |
internal class Point3D extends Proxy |
|---|
| 833 |
{ |
|---|
| 834 |
private var $v:Array; |
|---|
| 835 |
|
|---|
| 836 |
public function Point3D(x:Number = 0.0, y:Number = 0.0, z:Number = 0.0):void |
|---|
| 837 |
{ |
|---|
| 838 |
$v = [x, y, z]; |
|---|
| 839 |
} |
|---|
| 840 |
|
|---|
| 841 |
flash_proxy override function getProperty(name:*):* |
|---|
| 842 |
{ |
|---|
| 843 |
if(name == 0 || name == "x") return $v[0]; |
|---|
| 844 |
if(name == 1 || name == "y") return $v[1]; |
|---|
| 845 |
if(name == 2 || name == "z") return $v[2]; |
|---|
| 846 |
return undefined; |
|---|
| 847 |
} |
|---|
| 848 |
|
|---|
| 849 |
flash_proxy override function hasProperty(name:*):Boolean |
|---|
| 850 |
{ |
|---|
| 851 |
return flash_proxy.getProperty(name) != undefined; |
|---|
| 852 |
} |
|---|
| 853 |
|
|---|
| 854 |
flash_proxy override function setProperty(name:*, value:*):void |
|---|
| 855 |
{ |
|---|
| 856 |
if(name == 0 || name == "x"){$v[0] = Number(value); return;} |
|---|
| 857 |
if(name == 1 || name == "y"){$v[1] = Number(value); return;} |
|---|
| 858 |
if(name == 2 || name == "z"){$v[2] = Number(value); return;} |
|---|
| 859 |
throw new IllegalOperationError(); |
|---|
| 860 |
} |
|---|
| 861 |
|
|---|
| 862 |
public function toString():String |
|---|
| 863 |
{ |
|---|
| 864 |
return "(" + $v[0] + "," + $v[1] + "," + $v[2] + ")"; |
|---|
| 865 |
} |
|---|
| 866 |
} |
|---|
| 867 |
|
|---|
| 868 |
/** |
|---|
| 869 |
* the type of (affine) quadratic forms, represented as symmetric 3x3 |
|---|
| 870 |
* matrices. The value of the quadratic form at a vector (x,y) is v^t |
|---|
| 871 |
* Q v, where v = (x,y,1)^t. |
|---|
| 872 |
*/ |
|---|
| 873 |
internal class QuadraticForm extends Proxy |
|---|
| 874 |
{ |
|---|
| 875 |
private var $m:Array; |
|---|
| 876 |
|
|---|
| 877 |
public function QuadraticForm():void |
|---|
| 878 |
{ |
|---|
| 879 |
$m = []; |
|---|
| 880 |
$m[0] = new Point3D(); |
|---|
| 881 |
$m[1] = new Point3D(); |
|---|
| 882 |
$m[2] = new Point3D(); |
|---|
| 883 |
} |
|---|
| 884 |
|
|---|
| 885 |
flash_proxy override function getProperty(name:*):* |
|---|
| 886 |
{ |
|---|
| 887 |
if(name == 0) return $m[0]; |
|---|
| 888 |
if(name == 1) return $m[1]; |
|---|
| 889 |
if(name == 2) return $m[2]; |
|---|
| 890 |
return undefined; |
|---|
| 891 |
} |
|---|
| 892 |
|
|---|
| 893 |
flash_proxy override function hasProperty(name:*):Boolean |
|---|
| 894 |
{ |
|---|
| 895 |
return flash_proxy.getProperty(name) != undefined; |
|---|
| 896 |
} |
|---|
| 897 |
|
|---|
| 898 |
flash_proxy override function setProperty(name:*, value:*):void |
|---|
| 899 |
{ |
|---|
| 900 |
throw new IllegalOperationError(); |
|---|
| 901 |
} |
|---|
| 902 |
|
|---|
| 903 |
/** |
|---|
| 904 |
* Apply quadratic form Q to vector w = (w.x,w.y) |
|---|
| 905 |
*/ |
|---|
| 906 |
public function apply(w:Point):Number |
|---|
| 907 |
{ |
|---|
| 908 |
var v:Array = [w.x, w.y, 1]; |
|---|
| 909 |
var sum:Number = 0.0; |
|---|
| 910 |
|
|---|
| 911 |
for(var i:int = 0; i < 3; i++) |
|---|
| 912 |
{ |
|---|
| 913 |
for(var j:int = 0; j < 3; j++) |
|---|
| 914 |
{ |
|---|
| 915 |
sum += v[i] * $m[i][j] * v[j]; |
|---|
| 916 |
} |
|---|
| 917 |
} |
|---|
| 918 |
return sum; |
|---|
| 919 |
} |
|---|
| 920 |
|
|---|
| 921 |
public function clone():QuadraticForm |
|---|
| 922 |
{ |
|---|
| 923 |
var ret:QuadraticForm = new QuadraticForm(); |
|---|
| 924 |
for(var i:int = 0; i < 3; i++) |
|---|
| 925 |
{ |
|---|
| 926 |
for(var j:int = 0; j < 3; j++) |
|---|
| 927 |
{ |
|---|
| 928 |
ret[i][j] = $m[i][j]; |
|---|
| 929 |
} |
|---|
| 930 |
} |
|---|
| 931 |
return ret; |
|---|
| 932 |
} |
|---|
| 933 |
|
|---|
| 934 |
public function add(m2:QuadraticForm):QuadraticForm |
|---|
| 935 |
{ |
|---|
| 936 |
for(var i:int = 0; i < 3; i++) |
|---|
| 937 |
{ |
|---|
| 938 |
for(var j:int = 0; j < 3; j++) |
|---|
| 939 |
{ |
|---|
| 940 |
$m[i][j] += m2[i][j]; |
|---|
| 941 |
} |
|---|
| 942 |
} |
|---|
| 943 |
return this; |
|---|
| 944 |
} |
|---|
| 945 |
|
|---|
| 946 |
public function scalar(s:Number):QuadraticForm |
|---|
| 947 |
{ |
|---|
| 948 |
for(var i:int = 0; i < 3; i++) |
|---|
| 949 |
{ |
|---|
| 950 |
for(var j:int = 0; j < 3; j++) |
|---|
| 951 |
{ |
|---|
| 952 |
$m[i][j] *= s; |
|---|
| 953 |
} |
|---|
| 954 |
} |
|---|
| 955 |
return this; |
|---|
| 956 |
} |
|---|
| 957 |
|
|---|
| 958 |
public function fromVectorMultiply(v:Point3D):QuadraticForm |
|---|
| 959 |
{ |
|---|
| 960 |
for(var i:int = 0; i < 3; i++) |
|---|
| 961 |
{ |
|---|
| 962 |
for(var j:int = 0; j < 3; j++) |
|---|
| 963 |
{ |
|---|
| 964 |
$m[i][j] = v[i] * v[j]; |
|---|
| 965 |
} |
|---|
| 966 |
} |
|---|
| 967 |
return this; |
|---|
| 968 |
} |
|---|
| 969 |
|
|---|
| 970 |
public function toString():String |
|---|
| 971 |
{ |
|---|
| 972 |
return "[" + |
|---|
| 973 |
$m[0][0] + "," + $m[0][1] + "," + $m[0][2] + "," + |
|---|
| 974 |
$m[1][0] + "," + $m[1][1] + "," + $m[1][2] + "," + |
|---|
| 975 |
$m[2][0] + "," + $m[2][1] + "," + $m[2][2] + "]"; |
|---|
| 976 |
} |
|---|
| 977 |
} |
|---|