root/as3/PotrAs/src/com/nitoyon/potras/ProcessPath.as

リビジョン 91, 24.8 kB (コミッタ: nitoyon, コミット時期: 4 年 前)

高速化した
mx 名前空間を使わないようにした

Line 
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 }
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。