root/as3/snapfit/trunk/src/snapfit/shape/QBezier.as

リビジョン 2161, 12.6 kB (コミッタ: nutsu, コミット時期: 3 年 前)

パス上の長さから位置だしとか

Line 
1 //
2 // Licensed under the MIT License
3 //
4 // Copyright (C) 2008-2009  TAKANAWA Tomoaki (http://nutsu.com) and
5 //                                                      Spark project (www.libspark.org)
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy
8 // of this software and associated documentation files (the "Software"), to deal
9 // in the Software without restriction, including without limitation the rights
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 // copies of the Software, and to permit persons to whom the Software is
12 // furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 // THE SOFTWARE.
24 //
25
26 package snapfit.shape
27 {
28         import flash.geom.Point;
29         import flash.geom.Rectangle;
30        
31         public class QBezier implements IPath{
32                
33                 //start point
34                 private var _x0:Number;
35                 private var _y0:Number;
36                
37                 //end point
38                 private var _x1:Number;
39                 private var _y1:Number;
40                
41                 //control point
42                 private var _cx:Number;
43                 private var _cy:Number;
44                
45                 //length of bezier line
46                 private var _length:Number;
47                
48                 //fixed numbers for integration
49                 private var XY:Number;
50                 private var B:Number;
51                 private var C:Number;
52                 private var CS:Number;
53                 private var CS2:Number;
54                 private var INTG_0:Number;
55                
56                 //
57                 private var __err_d:Number = 0.1;
58                
59                 /**
60                  * Quadratic Bezier Constructor
61                  * @param       start x
62                  * @param       start y
63                  * @param       end x
64                  * @param       end y
65                  * @param       control x
66                  * @param       control y
67                  */
68                 public function QBezier(  x0:Number, y0:Number, cx:Number, cy:Number, x1:Number, y1:Number )
69                 {
70                         _x0 = x0;
71                         _y0 = y0;
72                         _cx = cx;
73                         _cy = cy;
74                         _x1 = x1;
75                         _y1 = y1;
76                         _length = -1;
77                 }
78                
79                 /**
80                  * Clone
81                  */
82                 public function clone():QBezier
83                 {
84                         var c:QBezier = new QBezier( _x0, _y0, _cx, _cy, _x1, _y1 );
85                         if ( isNaN(_length) == false )
86                         {
87                                 c._length  = _length;
88                                 c.XY = XY; c.B = B; c.C = C; c.CS = CS; c.CS2 = CS2; c.INTG_0 = INTG_0;
89                         }
90                         return c;
91                 }
92                
93                 //--------------------------------------------------------------------------------------------------- F
94                
95                 /**
96                  * Quadratic Bezier Function
97                  */
98                 public static function point( a:Number, b:Number, c:Number, t:Number ):Number
99                 {
100                         var tp:Number = 1.0 - t;
101                         return a * tp * tp + 2 * b * t * tp + c * t * t;
102                 }
103                
104                 /**
105                  * Quadratic Bezier Tangent
106                  */
107                 public static function tangent( a:Number, b:Number, c:Number, t:Number ):Number
108                 {
109                         return 2*( t*( a + c - 2*b ) - a + b );
110                 }
111                
112                 /**
113                 * Quadratic Bezier Function
114                 * @param        t( 0~1.0 )
115                 * @return       座標
116                 */
117                 public function f(t:Number):PathPoint
118                 {
119                         var tp:Number = 1.0 - t;
120                         return new PathPoint( _x0*tp*tp + 2*_cx*t*tp + _x1*t*t, _y0*tp*tp + 2*_cy*t*tp + _y1*t*t );
121                 }
122                
123                 /**
124                 * Diff of Bezier Function
125                 * @param        t( 0~1.0 )
126                 * @return       ベクトル
127                 */
128                 public function diff(t:Number):PathPoint
129                 {
130                         return new PathPoint( 2*( t*( _x0 + _x1 - 2*_cx ) - _x0 + _cx ) , 2*( t*( _y0 + _y1 - 2*_cy ) - _y0 + _cy ) );
131                 }
132                
133                 public function vector( t:Number ):PathVector
134                 {
135                         var tp:Number = 1.0 - t;
136                         return new PathVector( _x0*tp*tp + 2*_cx*t*tp + _x1*t*t, _y0*tp*tp + 2*_cy*t*tp + _y1*t*t,
137                                                                    2*( t*( _x0 + _x1 - 2*_cx ) - _x0 + _cx ) , 2*( t*( _y0 + _y1 - 2*_cy ) - _y0 + _cy ) );
138                 }
139                
140                 //--------------------------------------------------------------------------------------------------- LENGTH_T
141                
142                 /**
143                 * Length of bezier curve
144                 * @return       長さ
145                 */
146                 public function get length():Number
147                 {
148                         if( _length<0 )
149                                 __integrateInit();
150                         return _length;
151                 }
152                
153                 /**
154                  * get coordinates by length
155                  * @param       len
156                  */
157                 public function lengthToPoint( len:Number ):PathPoint
158                 {
159                         return f( lengthToValue( len ) );
160                 }
161                
162                 /**
163                 * integrate of bezier curve
164                 * @param        t( 0~1.0 )
165                 * @return       積分値
166                 */
167                 public function integrate(t:Number):Number
168                 {
169                         return (integrateF(t) - INTG_0);
170                 }
171                
172                 /**
173                 * get value from length
174                 * @param        len             target lenth (0,length)
175                 * @return       t value
176                 */
177                 public function lengthToValue( len:Number ):Number
178                 {
179                         if( len<0 || len>length )
180                                 return Number.NaN;
181                         else
182                                 return __seek( len, __err_d );
183                 }
184                
185                 /**
186                 * seek value
187                 * @param        len             target length
188                 * @param        d               許容誤差
189                 * @param        t0              check t value
190                 * @param        td              next check t value
191                 * @return       t value
192                 */
193                 private function __seek( len:Number, d:Number = 0.1, t0:Number = 0.5, td:Number = 0.25  ):Number
194                 {
195                         var lent0:Number = integrate(t0);
196                         if( Math.abs( len-lent0 )<d )
197                                 return t0;
198                         else
199                                 return __seek( len, d, (lent0<len) ? t0+td : t0-td, td/2 );
200                 }
201                
202                 //------------------------------------------------------------------------------CUT
203                
204                 /**
205                  * Split Bezier at t.
206                  * @param       t       ( 0 to 1 )
207                  * @return      QBezier[]
208                  */
209                 public function split( t:Number ):Array
210                 {
211                         if( t<0 || t>1 )
212                                 throw new ArgumentError("parameter t:0<t<1");
213                        
214                         if ( t == 0 )
215                         {
216                                 return [ null, clone() ];
217                         }
218                         else if ( t == 1 )
219                         {
220                                 return [ clone(), null ];
221                         }
222                         else
223                         {
224                                 var tp:Number = 1-t;
225                                 var ax:Number = _x0*tp + _cx*t;
226                                 var ay:Number = _y0*tp + _cy*t;
227                                 var bx:Number = _cx*tp + _x1*t;
228                                 var by:Number = _cy*tp + _y1*t;
229                                 var px:Number = ax*tp + bx*t;
230                                 var py:Number = ay*tp + by*t;
231                                 return [ new QBezier( _x0, _y0, ax, ay, px, py ), new QBezier( px, py, bx, by, _x1, _y1 )];
232                         }
233                 }
234                
235                 /**
236                  * Clip Bezier between t0 to t1.
237                  * @param       t0
238                  * @param       t1
239                  * @return      QBezier
240                  */
241                 public function clip( t0:Number, t1:Number ):QBezier
242                 {
243                         //両端の座標とベクトル
244                         var tp0:Number = 1-t0;
245                         var tp1:Number = 1-t1;
246                         var tx0:Number = _x0*tp0*tp0 + 2*_cx*t0*tp0 + _x1*t0*t0;
247                         var ty0:Number = _y0*tp0*tp0 + 2*_cy*t0*tp0 + _y1*t0*t0;
248                         var tx1:Number = _x0*tp1*tp1 + 2*_cx*t1*tp1 + _x1*t1*t1;
249                         var ty1:Number = _y0*tp1*tp1 + 2*_cy*t1*tp1 + _y1*t1*t1;
250                         var vx0:Number = 2*( t0*( _x0 + _x1 - 2*_cx ) - _x0 + _cx );
251                         var vy0:Number = 2*( t0*( _y0 + _y1 - 2*_cy ) - _y0 + _cy );
252                         var vx1:Number = 2*( t1*( _x0 + _x1 - 2*_cx ) - _x0 + _cx );
253                         var vy1:Number = 2*( t1*( _y0 + _y1 - 2*_cy ) - _y0 + _cy );
254                         //両端の差分
255                         var dx:Number = tx1 - tx0;
256                         var dy:Number = ty1 - ty0;
257                        
258                         // Point(dx,dy) = a*(pv0 + pv1) のaを求める
259                         var a:Number = ( dx != 0 ) ? dx/(vx0+vx1) : dy/(vy0+vy1);
260                        
261                         return new QBezier( tx0, ty0, tx0+a*vx0, ty0+a*vy0, tx1, ty1 );
262                 }
263                
264                 /**
265                  * Clip Bezier by rectangle
266                  * @param       Cut Rectangle
267                  * @return      QBezier[]
268                  */
269                 /*
270                 public function clipByRect( cutRect:Rectangle ):Array
271                 {
272                         var ts:Array = clipTValueByRect(cutRect);
273                         //result
274                         var res:Array = [];
275                         for( var i:int=0; i<ts.length ;i+=2 )
276                                 res.push( clip(ts[i],ts[i+1]) );
277                        
278                         return res;
279                 }
280                 */
281                 /**
282                  * Clip tVlaue by rectangle
283                  * @param       Cut Rectangle
284                  * @return      t[]
285                  * @private
286                  */
287                 /*
288                 internal function clipTValueByRect( cutRect:Rectangle ):Array
289                 {
290                         var rect:Rectangle = getRect();
291                         if ( cutRect.intersects( rect ) )
292                         {
293                                 //内包かどうか
294                                 if ( cutRect.containsRect( rect ) )
295                                 {
296                                         return [0,1];
297                                 }
298                                
299                                 var ts:Array = QBezierUtil.intersectionTByRect(this,cutRect);
300                                 if( cutRect.contains( _x0, _y0 ) && ts[0]>1e-10 ) ts.unshift(0);
301                                 if( cutRect.contains( _x1, _y1 ) && ts[ts.length-1]<(1-1e-10) ) ts.push(1);
302                                
303                                 //端点のrect.containsが微妙に外れる場合の誤差処理
304                                 if ( ts.length == 1 )
305                                 {
306                                         if( cutRect.containsPoint( f(ts[0]-1e-10) ) ){
307                                                 ts.unshift( 0 );
308                                         }else{
309                                                 ts.push( 1 );
310                                         }
311                                 }
312                                
313                                 //端点が交点の場合
314                                 if ( ts.length == 3 )
315                                 {
316                                         if( cutRect.containsPoint( f((ts[1]+ts[0])/2) ) ){
317                                                 ts.pop();
318                                         }else{
319                                                 ts.shift();
320                                         }
321                                 }
322                                
323                                 //result
324                                 return ts;     
325                         }
326                         else
327                         {
328                                 return [];
329                         }
330                 }
331                 */
332                 //------------------------------------------------------------------------------INTERSECTION
333                
334                 /**
335                  * Rectangle of bezier curve
336                  */
337                 public function getRect():Rectangle
338                 {
339                         var xlim:Number = _x0;
340                         var ylim:Number = _y0;
341                         var xt:Number = (_x0 - _cx)/( _x0 + _x1 - 2*_cx );
342                         var yt:Number = (_y0 - _cy)/( _y0 + _y1 - 2*_cy );
343                        
344                         //xt,ytがInfinity,NaNのときも切り捨て
345                         if( xt>0 && xt<1 )
346                                 xlim = _x0*(1-xt)*(1-xt) + 2*_cx*xt*(1-xt) + _x1*xt*xt;
347                        
348                         if( yt>0 && yt<1 )
349                                 ylim = _y0*(1-yt)*(1-yt) + 2*_cy*yt*(1-yt) + _y1*yt*yt;
350                        
351                         var rx0:Number = Math.min( _x0, Math.min( _x1, xlim ) );
352                         var ry0:Number = Math.min( _y0, Math.min( _y1, ylim ) );
353                         var rx1:Number = Math.max( _x0, Math.max( _x1, xlim ) );
354                         var ry1:Number = Math.max( _y0, Math.max( _y1, ylim ) );
355                         return new Rectangle( rx0, ry0, rx1-rx0, ry1-ry0 );
356                 }
357                
358                 /**
359                  * fat line for intersection
360                  */
361                 public function getFatLine():Array
362                 {
363                         var vx:Number = _x1 - _x0;
364                         var vy:Number = _y1 - _y0;
365                         var vcx:Number = point( _x0, _cx, _x1, 0.5 ) - _x0;
366                         var vcy:Number = point( _y0, _cy, _y1, 0.5 ) - _y0;
367                         var vlen:Number = Math.sqrt( vx * vx + vy * vy );
368                         var d:Number = (vx * vcy - vy * vcx) / vlen;
369                         vx /= vlen;
370                         vy /= vlen;
371                         var vdx:Number = -vy * d;
372                         var vdy:Number = vx * d;
373                         return [ _x0, _y0, _x1, _y1, _x1 + vdx, _y1 + vdy, _x0 + vdx, _y0 + vdy ];
374                 }
375                
376                 //--------------------------------------------------------------------------------------------------- INTEGRATE
377                
378                 /**
379                 * 積分定数初期化
380                 */
381                 private function __integrateInit():void
382                 {
383                         var kx:Number = _x0 + _x1 - 2 * _cx;
384                         var ky:Number = _y0 + _y1 - 2 * _cy;
385                         var ax:Number = - _x0 + _cx;
386                         var ay:Number = - _y0 + _cy;
387                        
388                         if( kx==0 && ky==0 )
389                         {
390                                 XY = 0;
391                                 B  = 0;
392                                 C  = 0;
393                                 CS = CS2 = 1.0;
394                                 _length = 0;
395                         }
396                         else
397                         {
398                                 //積分計算の為の定数
399                                 XY = kx*kx + ky*ky;
400                                 B  = ( ax*kx + ay*ky )/XY;
401                                 C  = ( ax*ax + ay*ay )/XY - B*B;
402                                 if( C>1e-10 ){
403                                         CS  = Math.sqrt(C);
404                                         CS2 = 0.0;
405                                 }else{
406                                         C = 0;
407                                         CS = CS2 = 1.0;
408                                 }
409                                 INTG_0  = integrateF(0.0);
410                                
411                                 //長さ
412                                 _length = integrate(1.0);
413                         }
414                 }
415                
416                 /**
417                 * 積分関数
418                 * @param        t( 0~1.0 )
419                 * @return       積分結果
420                 */
421                 private function integrateF( t:Number ):Number
422                 {
423                         var BT:Number  = B+t;
424                         var BTS:Number = Math.sqrt( BT*BT+C );
425                         return Math.sqrt(XY) * ( BTS*BT + C * Math.log( (BT + BTS)/CS + CS2 ) );
426                 }
427                        
428                 //--------------------------------------------------------------------------------------------------- COORDINATES
429                
430                 /**
431                 * offset coordinates
432                 * @param        x offset
433                 * @param        y offset
434                 */
435                 public function offset( x:Number, y:Number ):void
436                 {
437                         _x0 += x;
438                         _y0 += y;
439                         _x1 += x;
440                         _y1 += y;
441                         _cx += x;
442                         _cx += y;
443                 }
444                        
445                 public function get point0():Point{ return new Point(_x0,_y0); }
446                 public function set point0(p:Point):void
447                 {
448                         _x0 = p.x; _y0 = p.y; _length = Number.NaN;
449                 }
450                
451                 public function get point1():Point{ return new Point(_x1,_y1); }
452                 public function set point1(p:Point):void
453                 {
454                         _x1 = p.x; _y1 = p.y; _length = Number.NaN;
455                 }
456                
457                 public function get control():Point{ return new Point(_cx,_cy); }
458                 public function set control(p:Point):void
459                 {
460                         _cx = p.x; _cx = p.y; _length = -1;
461                 }
462                
463                 //------
464                
465                 public function get x0():Number{ return _x0; }
466                 public function set x0(v:Number):void
467                 {
468                         _x0 = v; _length = -1;
469                 }
470                
471                 public function get y0():Number{ return _y0; }
472                 public function set y0(v:Number):void
473                 {
474                         _y0 = v; _length = -1;
475                 }
476                 public function get x1():Number{ return _x1; }
477                 public function set x1(v:Number):void
478                 {
479                         _x1 = v; _length = -1;
480                 }
481                
482                 public function get y1():Number{ return _y1; }
483                 public function set y1(v:Number):void
484                 {
485                         _y1 = v; _length = -1;
486                 }
487                
488                 public function get cx():Number{ return _cx; }
489                 public function set cx(v:Number):void
490                 {
491                         _cx = v; _length = -1;
492                 }
493                
494                 public function get cy():Number{ return _cy; }
495                 public function set cy(v:Number):void
496                 {
497                         _cy = v; _length = -1;
498                 }
499         }
500 }
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。