// // Licensed under the MIT License // // Copyright (C) 2008-2009 TAKANAWA Tomoaki (http://nutsu.com) and // Spark project (www.libspark.org) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // package snapfit.shape { import flash.geom.Point; import flash.geom.Rectangle; public class QBezier implements IPath{ //start point private var _x0:Number; private var _y0:Number; //end point private var _x1:Number; private var _y1:Number; //control point private var _cx:Number; private var _cy:Number; //length of bezier line private var _length:Number; //fixed numbers for integration private var XY:Number; private var B:Number; private var C:Number; private var CS:Number; private var CS2:Number; private var INTG_0:Number; // private var __err_d:Number = 0.1; /** * Quadratic Bezier Constructor * @param start x * @param start y * @param end x * @param end y * @param control x * @param control y */ public function QBezier( x0:Number, y0:Number, cx:Number, cy:Number, x1:Number, y1:Number ) { _x0 = x0; _y0 = y0; _cx = cx; _cy = cy; _x1 = x1; _y1 = y1; _length = -1; } /** * Clone */ public function clone():QBezier { var c:QBezier = new QBezier( _x0, _y0, _cx, _cy, _x1, _y1 ); if ( isNaN(_length) == false ) { c._length = _length; c.XY = XY; c.B = B; c.C = C; c.CS = CS; c.CS2 = CS2; c.INTG_0 = INTG_0; } return c; } //--------------------------------------------------------------------------------------------------- F /** * Quadratic Bezier Function */ public static function point( a:Number, b:Number, c:Number, t:Number ):Number { var tp:Number = 1.0 - t; return a * tp * tp + 2 * b * t * tp + c * t * t; } /** * Quadratic Bezier Tangent */ public static function tangent( a:Number, b:Number, c:Number, t:Number ):Number { return 2*( t*( a + c - 2*b ) - a + b ); } /** * Quadratic Bezier Function * @param t( 0~1.0 ) * @return 座標 */ public function f(t:Number):PathPoint { var tp:Number = 1.0 - t; return new PathPoint( _x0*tp*tp + 2*_cx*t*tp + _x1*t*t, _y0*tp*tp + 2*_cy*t*tp + _y1*t*t ); } /** * Diff of Bezier Function * @param t( 0~1.0 ) * @return ベクトル */ public function diff(t:Number):PathPoint { return new PathPoint( 2*( t*( _x0 + _x1 - 2*_cx ) - _x0 + _cx ) , 2*( t*( _y0 + _y1 - 2*_cy ) - _y0 + _cy ) ); } public function vector( t:Number ):PathVector { var tp:Number = 1.0 - t; return new PathVector( _x0*tp*tp + 2*_cx*t*tp + _x1*t*t, _y0*tp*tp + 2*_cy*t*tp + _y1*t*t, 2*( t*( _x0 + _x1 - 2*_cx ) - _x0 + _cx ) , 2*( t*( _y0 + _y1 - 2*_cy ) - _y0 + _cy ) ); } //--------------------------------------------------------------------------------------------------- LENGTH_T /** * Length of bezier curve * @return 長さ */ public function get length():Number { if( _length<0 ) __integrateInit(); return _length; } /** * get coordinates by length * @param len */ public function lengthToPoint( len:Number ):PathPoint { return f( lengthToValue( len ) ); } /** * integrate of bezier curve * @param t( 0~1.0 ) * @return 積分値 */ public function integrate(t:Number):Number { return (integrateF(t) - INTG_0); } /** * get value from length * @param len target lenth (0,length) * @return t value */ public function lengthToValue( len:Number ):Number { if( len<0 || len>length ) return Number.NaN; else return __seek( len, __err_d ); } /** * seek value * @param len target length * @param d 許容誤差 * @param t0 check t value * @param td next check t value * @return t value */ private function __seek( len:Number, d:Number = 0.1, t0:Number = 0.5, td:Number = 0.25 ):Number { var lent0:Number = integrate(t0); if( Math.abs( len-lent0 )1 ) throw new ArgumentError("parameter t:01e-10 ) ts.unshift(0); if( cutRect.contains( _x1, _y1 ) && ts[ts.length-1]<(1-1e-10) ) ts.push(1); //端点のrect.containsが微妙に外れる場合の誤差処理 if ( ts.length == 1 ) { if( cutRect.containsPoint( f(ts[0]-1e-10) ) ){ ts.unshift( 0 ); }else{ ts.push( 1 ); } } //端点が交点の場合 if ( ts.length == 3 ) { if( cutRect.containsPoint( f((ts[1]+ts[0])/2) ) ){ ts.pop(); }else{ ts.shift(); } } //result return ts; } else { return []; } } */ //------------------------------------------------------------------------------INTERSECTION /** * Rectangle of bezier curve */ public function getRect():Rectangle { var xlim:Number = _x0; var ylim:Number = _y0; var xt:Number = (_x0 - _cx)/( _x0 + _x1 - 2*_cx ); var yt:Number = (_y0 - _cy)/( _y0 + _y1 - 2*_cy ); //xt,ytがInfinity,NaNのときも切り捨て if( xt>0 && xt<1 ) xlim = _x0*(1-xt)*(1-xt) + 2*_cx*xt*(1-xt) + _x1*xt*xt; if( yt>0 && yt<1 ) ylim = _y0*(1-yt)*(1-yt) + 2*_cy*yt*(1-yt) + _y1*yt*yt; var rx0:Number = Math.min( _x0, Math.min( _x1, xlim ) ); var ry0:Number = Math.min( _y0, Math.min( _y1, ylim ) ); var rx1:Number = Math.max( _x0, Math.max( _x1, xlim ) ); var ry1:Number = Math.max( _y0, Math.max( _y1, ylim ) ); return new Rectangle( rx0, ry0, rx1-rx0, ry1-ry0 ); } /** * fat line for intersection */ public function getFatLine():Array { var vx:Number = _x1 - _x0; var vy:Number = _y1 - _y0; var vcx:Number = point( _x0, _cx, _x1, 0.5 ) - _x0; var vcy:Number = point( _y0, _cy, _y1, 0.5 ) - _y0; var vlen:Number = Math.sqrt( vx * vx + vy * vy ); var d:Number = (vx * vcy - vy * vcx) / vlen; vx /= vlen; vy /= vlen; var vdx:Number = -vy * d; var vdy:Number = vx * d; return [ _x0, _y0, _x1, _y1, _x1 + vdx, _y1 + vdy, _x0 + vdx, _y0 + vdy ]; } //--------------------------------------------------------------------------------------------------- INTEGRATE /** * 積分定数初期化 */ private function __integrateInit():void { var kx:Number = _x0 + _x1 - 2 * _cx; var ky:Number = _y0 + _y1 - 2 * _cy; var ax:Number = - _x0 + _cx; var ay:Number = - _y0 + _cy; if( kx==0 && ky==0 ) { XY = 0; B = 0; C = 0; CS = CS2 = 1.0; _length = 0; } else { //積分計算の為の定数 XY = kx*kx + ky*ky; B = ( ax*kx + ay*ky )/XY; C = ( ax*ax + ay*ay )/XY - B*B; if( C>1e-10 ){ CS = Math.sqrt(C); CS2 = 0.0; }else{ C = 0; CS = CS2 = 1.0; } INTG_0 = integrateF(0.0); //長さ _length = integrate(1.0); } } /** * 積分関数 * @param t( 0~1.0 ) * @return 積分結果 */ private function integrateF( t:Number ):Number { var BT:Number = B+t; var BTS:Number = Math.sqrt( BT*BT+C ); return Math.sqrt(XY) * ( BTS*BT + C * Math.log( (BT + BTS)/CS + CS2 ) ); } //--------------------------------------------------------------------------------------------------- COORDINATES /** * offset coordinates * @param x offset * @param y offset */ public function offset( x:Number, y:Number ):void { _x0 += x; _y0 += y; _x1 += x; _y1 += y; _cx += x; _cx += y; } public function get point0():Point{ return new Point(_x0,_y0); } public function set point0(p:Point):void { _x0 = p.x; _y0 = p.y; _length = Number.NaN; } public function get point1():Point{ return new Point(_x1,_y1); } public function set point1(p:Point):void { _x1 = p.x; _y1 = p.y; _length = Number.NaN; } public function get control():Point{ return new Point(_cx,_cy); } public function set control(p:Point):void { _cx = p.x; _cx = p.y; _length = -1; } //------ public function get x0():Number{ return _x0; } public function set x0(v:Number):void { _x0 = v; _length = -1; } public function get y0():Number{ return _y0; } public function set y0(v:Number):void { _y0 = v; _length = -1; } public function get x1():Number{ return _x1; } public function set x1(v:Number):void { _x1 = v; _length = -1; } public function get y1():Number{ return _y1; } public function set y1(v:Number):void { _y1 = v; _length = -1; } public function get cx():Number{ return _cx; } public function set cx(v:Number):void { _cx = v; _length = -1; } public function get cy():Number{ return _cy; } public function set cy(v:Number):void { _cy = v; _length = -1; } } }