@ue-too/curve 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,5 +1,1780 @@
1
- import{PointCal as $}from"@ue-too/math";var v=[-0.06405689286260563,0.06405689286260563,-0.1911188674736163,0.1911188674736163,-0.3150426796961634,0.3150426796961634,-0.4337935076260451,0.4337935076260451,-0.5454214713888396,0.5454214713888396,-0.6480936519369755,0.6480936519369755,-0.7401241915785544,0.7401241915785544,-0.820001985973903,0.820001985973903,-0.8864155270044011,0.8864155270044011,-0.9382745520027328,0.9382745520027328,-0.9747285559713095,0.9747285559713095,-0.9951872199970213,0.9951872199970213],a=[0.12793819534675216,0.12793819534675216,0.1258374563468283,0.1258374563468283,0.12167047292780339,0.12167047292780339,0.1155056680537256,0.1155056680537256,0.10744427011596563,0.10744427011596563,0.09761865210411388,0.09761865210411388,0.08619016153195327,0.08619016153195327,0.0733464814110803,0.0733464814110803,0.05929858491543678,0.05929858491543678,0.04427743881741981,0.04427743881741981,0.028531388628933663,0.028531388628933663,0.0123412297999872,0.0123412297999872];class j{controlPoints;dControlPoints=[];arcLengthLUT={controlPoints:[],arcLengthLUT:[]};_fullLength;lengthCache=new Map;getCacheStats(){return{size:this.lengthCache.size,hitRate:0}}preWarmCache(z=100){let G=1/z;for(let N=0;N<=1;N+=G)this.lengthAtT(N)}clearCache(){this.lengthCache.clear()}constructor(z){this.controlPoints=z,this.dControlPoints=this.getDerivativeControlPoints(this.controlPoints),this._fullLength=this.calculateFullLength(),this.arcLengthLUT={controlPoints:[],arcLengthLUT:[]},this.clearCache()}getPointbyPercentage(z){let G=this.arcLengthLUT.controlPoints.length!=this.controlPoints.length;G=G||this.arcLengthLUT.controlPoints.reduce((W,O,_)=>{return W||!$.isEqual(O,this.controlPoints[_])},!1);let N=[];if(G)this.arcLengthLUT=this.getArcLengthLUT(1000);N=[...this.arcLengthLUT.arcLengthLUT.map((W)=>W.length)];let Q=z*this.fullLength,k=0,J=N.length-1;while(k<=J){let W=Math.floor((k+J)/2);if(N[W]==Q)return this.get((W+1)/N.length);else if(N[W]<Q)k=W+1;else J=W-1}return k>=N.length?this.get(1):this.get((k+1)/N.length)}derivativeByPercentage(z){let G=this.arcLengthLUT.controlPoints.length!=this.controlPoints.length;G=G||this.arcLengthLUT.controlPoints.reduce((O,_,Z)=>{return O||!$.isEqual(_,this.controlPoints[Z])},!1);let N=[];if(G)this.arcLengthLUT=this.getArcLengthLUT(1000);N=[...this.arcLengthLUT.arcLengthLUT.map((O)=>O.length)];let Q=z*this.fullLength,k=0,J=N.length-1,W;while(k<=J){let O=Math.floor((k+J)/2);if(N[O]==Q)return W=(O+1)/N.length,this.derivative(W);else if(N[O]<Q)k=O+1;else J=O-1}return W=k>=N.length?1:(k+1)/N.length,this.derivative(W)}getDerivativeControlPoints(z){let G=[];for(let N=1;N<z.length;N++)G.push($.multiplyVectorByScalar($.subVector(z[N],z[N-1]),z.length-1));return G}validateTVal(z){if(z>1||z<0)throw new d("tVal is greater than 1 or less than 0")}getControlPoints(){return this.controlPoints}setControlPoints(z){this.controlPoints=z,this.dControlPoints=this.getDerivativeControlPoints(this.controlPoints),this._fullLength=this.calculateFullLength(),this.arcLengthLUT={controlPoints:[],arcLengthLUT:[]},this.clearCache()}setControlPointAtIndex(z,G){if(z<0||z>=this.controlPoints.length)return!1;return this.controlPoints[z]=G,this.dControlPoints=this.getDerivativeControlPoints(this.controlPoints),this._fullLength=this.calculateFullLength(),this.arcLengthLUT={controlPoints:[],arcLengthLUT:[]},this.clearCache(),!0}compute(z){this.validateTVal(z);let G=this.controlPoints;while(G.length>1){let N=G.slice(1);for(let Q=0;Q<N.length;Q++)N[Q]=$.addVector($.multiplyVectorByScalar(G[Q],1-z),$.multiplyVectorByScalar(G[Q+1],z));G=N}return G[0]}get(z){if(this.validateTVal(z),this.controlPoints.length==3){let G=$.multiplyVectorByScalar(this.controlPoints[0],(1-z)*(1-z)),N=$.multiplyVectorByScalar(this.controlPoints[1],2*(1-z)*z),Q=$.multiplyVectorByScalar(this.controlPoints[2],z*z);return $.addVector($.addVector(G,N),Q)}if(this.controlPoints.length==4){let G=$.multiplyVectorByScalar(this.controlPoints[0],(1-z)*(1-z)*(1-z)),N=$.multiplyVectorByScalar(this.controlPoints[1],3*(1-z)*(1-z)*z),Q=$.multiplyVectorByScalar(this.controlPoints[2],3*(1-z)*z*z),k=$.multiplyVectorByScalar(this.controlPoints[3],z*z*z);return $.addVector($.addVector(G,N),$.addVector(Q,k))}return this.compute(z)}getLUT(z=100){let G=1/z,N=[],Q=0;N.push(this.get(Q));for(let k=0;k<z;k+=1){if(Q+=G,Q>1&&Q-G<1||k==z-1)Q=1;N.push(this.get(Q))}return N}getLUTWithTVal(z){if(z==null)z=100;let G=1/z,N=[],Q=0;N.push({point:this.get(Q),tVal:Q});for(let k=0;k<z;k+=1){if(Q+=G,Q>1&&Q-G<1||k==z-1)Q=1;N.push({point:this.get(Q),tVal:Q})}return N}get fullLength(){return this._fullLength}calculateFullLength(){return this.lengthAtT(1)}lengthAtT(z){this.validateTVal(z);let G=Math.round(z*1e6)/1e6;if(this.lengthCache.has(G))return this.lengthCache.get(G);let N=z/2,Q=v.length,k=0;for(let W=0,O;W<Q;W++)O=N*v[W]+N,k+=a[W]*$.magnitude(this.derivative(O));let J=N*k;return this.lengthCache.set(G,J),J}derivative(z){return y(z,this.dControlPoints)}derivativeNormalized(z){return $.unitVector(y(z,this.dControlPoints))}getArcLengthLUT(z=50){if(this.arcLengthLUT.controlPoints.length!==this.controlPoints.length||this.arcLengthLUT.controlPoints.some((N,Q)=>!$.isEqual(N,this.controlPoints[Q]))||this.arcLengthLUT.arcLengthLUT.length===0){this.clearCache();let N=[],Q=1/z;for(let k=0;k<=1;k+=Q)N.push({tVal:k,length:this.lengthAtT(k)});this.arcLengthLUT={controlPoints:[...this.controlPoints],arcLengthLUT:N}}return this.arcLengthLUT}splitIntoCurves(z){let G=this.split(z);return[new j(G[0]),new j(G[1])]}split(z){if(this.validateTVal(z),this.controlPoints.length==3){let F=this.controlPoints[0],H=$.subVector($.multiplyVectorByScalar(this.controlPoints[1],z),$.multiplyVectorByScalar(this.controlPoints[0],z-1)),E=$.subVector($.multiplyVectorByScalar(this.controlPoints[2],z*z),$.multiplyVectorByScalar(this.controlPoints[1],2*z*(z-1)));E=$.addVector(E,$.multiplyVectorByScalar(this.controlPoints[0],(z-1)*(z-1)));let B=$.subVector($.multiplyVectorByScalar(this.controlPoints[2],z),$.multiplyVectorByScalar(this.controlPoints[1],z-1)),q=this.controlPoints[2];return[[F,H,E],[E,B,q]]}let G=this.controlPoints[0],N=$.subVector($.multiplyVectorByScalar(this.controlPoints[1],z),$.multiplyVectorByScalar(this.controlPoints[0],z-1)),Q=$.addVector($.multiplyVectorByScalar(this.controlPoints[2],z*z),$.addVector($.multiplyVectorByScalar(this.controlPoints[1],-(2*z*(z-1))),$.multiplyVectorByScalar(this.controlPoints[0],(z-1)*(z-1)))),k=$.multiplyVectorByScalar(this.controlPoints[3],z*z*z),J=$.multiplyVectorByScalar(this.controlPoints[2],-(3*z*z*(z-1))),W=$.multiplyVectorByScalar(this.controlPoints[1],3*z*(z-1)*(z-1)),O=$.multiplyVectorByScalar(this.controlPoints[0],-((z-1)*(z-1)*(z-1))),_=$.addVector(O,$.addVector(W,$.addVector(k,J))),Z=$.addVector($.addVector($.multiplyVectorByScalar(this.controlPoints[3],z*z),$.multiplyVectorByScalar(this.controlPoints[2],-(2*z*(z-1)))),$.multiplyVectorByScalar(this.controlPoints[1],(z-1)*(z-1))),K=$.addVector($.multiplyVectorByScalar(this.controlPoints[3],z),$.multiplyVectorByScalar(this.controlPoints[2],-(z-1))),X=this.controlPoints[3];return[[G,N,Q,_],[_,Z,K,X]]}splitIn3WithControlPoints(z,G){if(G<z)console.warn("tVal2 is less than tVal, swapping them"),[z,G]=[G,z];let N=this.split(z),Q=new j(N[1]),k=x(G,z,1,0,1),J=Q.split(k);return[N[0],J[0],J[1]]}splitIn3Curves(z,G){if(G<z)console.warn("tVal2 is less than tVal, swapping them"),[z,G]=[G,z];let N=this.split(z),Q=new j(N[1]),k=x(G,z,1,0,1),J=Q.split(k);return[new j(N[0]),new j(J[0]),new j(J[1])]}splitAndTakeMidCurve(z,G){let[N,Q,k]=this.splitIn3Curves(z,G);return Q}getProjection(z){let N=Number.MAX_VALUE,Q=0,k=this.get(0),J=0,W=this.getLUTWithTVal(500);W.forEach((Z,K)=>{let X=$.distanceBetweenPoints(Z.point,z);if(X<N)N=X,k={...Z.point},Q=Z.tVal,J=K});let O=W[J].tVal,_=W[J].tVal;if(J<W.length-1)_=W[J+1].tVal;if(J>0)O=W[J-1].tVal;while(O<_&&_-O>0.00001){let Z=O+(_-O)/2,K=Z-O,X=Z+K/2,F=Z+K/2,H=N;if(X<=1&&X>=0){let E=$.distanceBetweenPoints(this.get(X),z);if(E<N)N=E,k=this.get(X),Q=X,_=X+K/2,O=X-K/2}if(F<=1&&F>=0){let E=$.distanceBetweenPoints(this.get(F),z);if(E<N)N=E,k=this.get(F),Q=F,_=F+K/2,O=F-K/2}if(H==N)break}return{projection:k,tVal:Q}}findArcs(z){let G=0,N=[];while(G<1){let Q=this.findArcStartingAt(z,G);if(Q==null||Q.arc==null)break;if(N.push(Q.arc),G=Q.arc.endT,G>=1)break}return N}findArcStartingAt(z,G){let N=1,Q=G+(N-G)/2,k={good:!1},J=0;while(!0){if(J++,Q=G+(N-G)/2,N>1||Q>1)if(k.good)return k;else return null;let W=this.get(G),O=this.get(N),_=this.get(Q),Z=this.fitArc(W,O,_);if(!Z.exists||Z.center==null||Z.radius==null)return null;let K=N-Q,X=Q-K/2,F=Q+K/2,H=this.get(X),E=this.get(F),B=$.distanceBetweenPoints(H,Z.center),q=$.distanceBetweenPoints(E,Z.center);if(Math.abs(B-Z.radius)>z||Math.abs(q-Z.radius)>z){if(k.good==!0)return k;k.good=!1,N=Q}else{if(k.good=!0,Z.startPoint!==void 0&&Z.endPoint!==void 0)k.arc={center:Z.center,radius:Z.radius,startPoint:Z.startPoint,endPoint:Z.endPoint,startT:G,endT:N};N=N+(Q-G)}}}fitArc(z,G,N){let Q=[[z.x,z.y,1],[N.x,N.y,1],[G.x,G.y,1]];if(this.determinant3by3(Q)==0)return{exists:!1};let k=[[z.x*z.x+z.y*z.y,z.y,1],[N.x*N.x+N.y*N.y,N.y,1],[G.x*G.x+G.y*G.y,G.y,1]],J=[[z.x*z.x+z.y*z.y,z.x,1],[N.x*N.x+N.y*N.y,N.x,1],[G.x*G.x+G.y*G.y,G.x,1]],W=[[z.x*z.x+z.y*z.y,z.x,z.y],[N.x*N.x+N.y*N.y,N.x,N.y],[G.x*G.x+G.y*G.y,G.x,G.y]],O=0.5*(this.determinant3by3(k)/this.determinant3by3(Q)),_=-0.5*(this.determinant3by3(J)/this.determinant3by3(Q)),Z=Math.sqrt(O*O+_*_+this.determinant3by3(W)/this.determinant3by3(Q));return{exists:!0,center:{x:O,y:_},radius:Z,startPoint:z,endPoint:G}}determinant3by3(z){let G=z[0][0],N=z[0][1],Q=z[0][2],k=z[1][0],J=z[1][1],W=z[1][2],O=z[2][0],_=z[2][1],Z=z[2][2];return G*(J*Z-W*_)-N*(k*Z-O*W)+Q*(k*_-J*O)}curvature(z){let G=y(z,this.dControlPoints),N=y(z,this.getDerivativeControlPoints(this.dControlPoints)),Q=G.x*N.y-N.x*G.y,k=Math.pow(G.x*G.x+G.y*G.y,1.5);if(k==0)return NaN;return Q/k}secondDerivative(z){return y(z,this.getDerivativeControlPoints(this.dControlPoints))}getCoefficientOfTTerms(){return this.getCoefficientOfTTermsWithControlPoints(this.controlPoints)}getDerivativeCoefficients(){return this.getCoefficientOfTTermsWithControlPoints(this.dControlPoints)}getCoefficientOfTTermsWithControlPoints(z){let G=[],N=[];if(z.length==3)N=[[1,0,0],[-2,2,0],[1,-2,1]];else if(z.length==4)N=[[1,0,0,0],[-3,3,0,0],[3,-6,3,0],[-1,3,-3,1]];else if(z.length==2)N=[[1,0],[-1,1]];else throw Error("number of control points is wrong");for(let Q=0;Q<z.length;Q++)G.push(z.reduce((k,J,W)=>{return{x:k.x+N[Q][W]*J.x,y:k.y+N[Q][W]*J.y}},{x:0,y:0}));return G}getControlPointsAlignedWithXAxis(){let z=$.unitVectorFromA2B(this.controlPoints[0],this.controlPoints[this.controlPoints.length-1]),G=$.angleFromA2B({x:1,y:0},z),N=this.controlPoints[0],Q=[{x:0,y:0}];for(let k=1;k<this.controlPoints.length;k++){let J=$.subVector(this.controlPoints[k],N),W=$.rotatePoint(J,-G);Q.push(W)}return Q}getExtrema(){let z={x:[],y:[]},G=this.getDerivativeCoefficients(),N=[0,0,0,0],Q=[0,0,0,0];G.forEach((W,O)=>{N[3-O]=W.x,Q[3-O]=W.y});let k=w(N[0],N[1],N[2],N[3]),J=w(Q[0],Q[1],Q[2],Q[3]);if(k.forEach((W)=>{if(W>=0&&W<=1)z.x.push(W)}),J.forEach((W)=>{if(W>=0&&W<=1)z.y.push(W)}),G.length>=3){N=[0,0,0,0],Q=[0,0,0,0],this.getCoefficientOfTTermsWithControlPoints(this.getDerivativeControlPoints(this.dControlPoints)).forEach((Z,K)=>{N[3-K]=Z.x,Q[3-K]=Z.y});let O=w(N[0],N[1],N[2],N[3]),_=w(Q[0],Q[1],Q[2],Q[3]);O.forEach((Z)=>{if(Z>=0&&Z<=1)z.x.push(Z)}),_.forEach((Z)=>{if(Z>=0&&Z<=1)z.y.push(Z)})}return z}translateRotateControlPoints(z,G){let N=[];for(let Q=0;Q<this.controlPoints.length;Q++)N.push($.rotatePoint($.addVector(this.controlPoints[Q],z),G));return N}getLineIntersections(z){let G=z.getTranslationRotationToAlginXAxis(),N=[],Q=this.translateRotateControlPoints(G.translation,G.rotationAngle),k=this.getCoefficientOfTTermsWithControlPoints(Q),J=[0,0,0,0];return k.forEach((O,_)=>{J[3-_]=O.y}),w(J[0],J[1],J[2],J[3]).forEach((O)=>{if(O>=0&&O<=1){if(z.pointInLine(this.get(O)))N.push(O)}}),N}getSelfIntersections(){let[z,G]=this.split(0.5),N=new j(z),Q=new j(G),k=V(N,Q);return k.forEach((J)=>{J.selfT=J.selfT*0.5,J.otherT=J.otherT*0.5+0.5}),k.shift(),k}getCircleIntersections(z,G){let N=this.getLUTWithTVal(500),Q=Number.MAX_VALUE,k=0,J=N[0].point,W=N[0].tVal;N.forEach((F,H)=>{let E=Math.abs($.distanceBetweenPoints(z,F.point));if(E<Q)Q=E,k=H});let O=N.map((F,H)=>{return{...F,distance:0}});Q=Number.MAX_VALUE;let _=0,Z=0,K=[];while(++Z<25){let F=this.findClosest(z.x,z.y,O,G,5,O[_-2]?.distance,O[_-1]?.distance);if(F<_)break;if(F>0&&F==_)break;K.push(F),_=F+2}let X=[];return K.forEach((F)=>{let H=this.refineBinary(this,z.x,z.y,O,F,G);if(H!=null)X.push({intersection:H.point,tVal:H.tVal})}),X}advanceAtTWithLength(z,G){let Q=this.lengthAtT(z)+G;if(z===0&&G<0)return{type:"beforeCurve",remainLength:-G};if(z===1&&G>0)return{type:"afterCurve",remainLength:G};if(Q>this.fullLength)return{type:"afterCurve",remainLength:Q-this.fullLength};else if(Q<0)return{type:"beforeCurve",remainLength:-Q};if(this.arcLengthLUT.arcLengthLUT.length===0)this.arcLengthLUT=this.getArcLengthLUT(1000);let k=this.arcLengthLUT.arcLengthLUT,J=0,W=k.length-1;while(J<=W){let B=Math.floor((J+W)/2),q=k[B].length;if(D(q,Q,0.01)){let M=k[B].tVal,I=this.get(M);return{type:"withinCurve",tVal:M,point:I}}else if(q<Q)J=B+1;else W=B-1}if(W<0)W=1,J=0;if(J>=k.length)W=k.length-1,J=k.length-2;let O=k[W],_=k[J],Z=_.length-O.length,K=_.tVal-O.tVal;if(Z===0){let B=this.get(O.tVal);return{type:"withinCurve",tVal:O.tVal,point:B}}let X=(Q-O.length)/Z,F=O.tVal+X*K,H=Math.max(0,Math.min(1,F)),E=this.get(H);return{type:"withinCurve",tVal:H,point:E}}advanceByDistance(z,G){let N=z,Q=G,k=0.01;if(G>this.fullLength)return{type:"afterCurve",remainLength:G-this.fullLength};else if(G<0)return{type:"beforeCurve",remainLength:-G};while(Q>0&&N<1){let J=this.get(N),W=Math.min(N+k,1),O=this.get(W),_=Math.sqrt(Math.pow(O.x-J.x,2)+Math.pow(O.y-J.y,2));if(_>=Q){let Z=Q/_;return{type:"withinCurve",tVal:N+Z*(W-N),point:this.get(N+Z*(W-N))}}Q-=_,N=W}return{type:"withinCurve",tVal:N,point:this.get(N)}}refineBinary(z,G,N,Q,k,J=0,W=0.01){let O=Q[k],_=1,Z=Number.MAX_SAFE_INTEGER;do{let K=k===0?0:k-1,X=k===Q.length-1?Q.length-1:k+1,F=Q[K].tVal,H=Q[X].tVal,E=[],B=(H-F)/4;if(B<0.001)break;E.push(Q[K]);for(let q=1;q<=3;q++){let M=z.get(F+q*B),I=Math.abs($.distanceBetweenPoints(M,{x:G,y:N})-J);if(I<Z)Z=I,O={point:M,tVal:F+q*B,distance:I},k=q;E.push({point:M,tVal:F+q*B,distance:I})}E.push(Q[X]),Q=E}while(_++<25);if(J&&Z>W)O=void 0;return O}findClosest(z,G,N,Q,k=5,J,W){let O=Number.MAX_SAFE_INTEGER,_=J||O,Z=W||O,K=-1;for(let X=0,F=N.length;X<F;X++){let H=N[X].point;if(N[X].distance=Math.abs($.distanceBetweenPoints({x:z,y:G},H)-Q),Z<k&&_>Z&&Z<N[X].distance){K=X-1;break}if(N[X].distance<O)O=N[X].distance;_=Z,Z=N[X].distance}return K}getCurveIntersections(z,G){return V(this,z,G)}get AABB(){let z=this.getExtrema(),G=[0,1],N={x:Number.MAX_VALUE,y:Number.MAX_VALUE},Q={x:-Number.MAX_VALUE,y:-Number.MAX_VALUE};return z.x.forEach((k)=>{G.push(k)}),z.y.forEach((k)=>{G.push(k)}),G.forEach((k)=>{let J=this.get(k);N.x=Math.min(N.x,J.x),N.y=Math.min(N.y,J.y),Q.x=Math.max(Q.x,J.x),Q.y=Math.max(Q.y,J.y)}),{min:N,max:Q}}normal(z){let G=this.derivative(z),N=Math.sqrt(G.x*G.x+G.y*G.y);return{tVal:z,direction:{x:-G.y/N,y:G.x/N}}}}function r(z){let G,N=0,Q=0,k=0.01,J,W=[],O=[],_=z.getExtrema().x;if(_.indexOf(0)===-1)_=[0].concat(_);if(_.indexOf(1)===-1)_.push(1);for(N=_[0],G=1;G<_.length;G++)Q=_[G],J=z.splitAndTakeMidCurve(N,Q),W.push(J),N=Q;return W.forEach((Z)=>{N=0,Q=0;while(Q<=1)for(Q=N+k;Q<=1+k;Q+=k){let K=Math.min(Q,1);if(J=Z.splitAndTakeMidCurve(N,K),!t(J)){if(Q-=k,Math.abs(N-Q)<k)return[];let X=Math.min(Q,1);J=Z.splitAndTakeMidCurve(N,X),O.push(J),N=X;break}}if(N<1)J=Z.splitAndTakeMidCurve(N,1),O.push(J)}),O}function e(z){let G=z.getControlPoints(),N=[G[0]],Q=G.length;for(let k=1;k<Q;k++){let J=G[k],W=G[k-1];N[k]={x:(Q-k)/Q*J.x+k/Q*W.x,y:(Q-k)/Q*J.y+k/Q*W.y}}return N[Q]=G[Q-1],new j(N)}function g(z,G,N){if(N!==void 0){let J=z.get(G),W=z.normal(G).direction;return{c:J,n:W,x:J.x+W.x*N,y:J.y+W.y*N}}let Q=z.getControlPoints();if(f(z)){let J=z.normal(0).direction,W=Q.map(function(O){return{x:O.x+G*J.x,y:O.y+G*J.y}});return[new j(W)]}return r(z).map(function(J){if(f(J))return g(J,G)[0];return c(J,G)})}function O0(z,G){let Q=z.getLUTWithTVal(100).map((J)=>{let W=$.unitVector(z.derivative(J.tVal)),O={x:-W.y,y:W.x};return{x:J.point.x+O.x*G,y:J.point.y+O.y*G}}),k={min:{x:Q[0].x,y:Q[0].y},max:{x:Q[0].x,y:Q[0].y}};return Q.forEach((J)=>{if(J.x<k.min.x)k.min.x=J.x;if(J.x>k.max.x)k.max.x=J.x;if(J.y<k.min.y)k.min.y=J.y;if(J.y>k.max.y)k.max.y=J.y}),{points:Q,aabb:k}}function C(z,G,N,Q){let{x:k,y:J}=z,W=G.x,O=G.y,_=N.x,Z=N.y,K=Q.x,X=Q.y;return o(k,J,W,O,_,Z,K,X)}function o(z,G,N,Q,k,J,W,O){let _=(z*Q-G*N)*(k-W)-(z-N)*(k*O-J*W),Z=(z*Q-G*N)*(J-O)-(G-Q)*(k*O-J*W),K=(z-N)*(J-O)-(G-Q)*(k-W);if(K==0)return!1;return{x:_/K,y:Z/K}}function f(z){let G=z.getControlPoints().length-1,N=z.getControlPoints(),Q=n(N,{p1:N[0],p2:N[G]}),k=$.distanceBetweenPoints(N[0],N[G]);return Q.reduce((W,O)=>W+Math.abs(O.y),0)<k/50}function c(z,G){let N=z.getControlPoints().length-1,Q=void 0;if(typeof G==="function")Q=G;if(Q&&N===2)return c(e(z),Q);let k=z.getControlPoints();if(f(z))return p(z,z.normal(0).direction,Q?Q(0):G,Q?Q(1):G);let J=Q?Q(0):G,W=Q?Q(1):G,O=[g(z,0,10),g(z,1,10)],_=[],Z=C(O[0],O[0].c,O[1],O[1].c);if(!Z)return p(z,z.normal(0).direction,J,W);if([0,1].forEach(function(K){let X=JSON.parse(JSON.stringify(k[K*N])),F=O[K];X.x+=(K?W:J)*F.n.x,X.y+=(K?W:J)*F.n.y,_[K*N]=X}),!Q)return[0,1].forEach((K)=>{if(N===2&&!!K)return;let X=_[K*N],F=z.derivative(K),H={x:X.x+F.x,y:X.y+F.y},E=C(X,H,Z,k[K+1]);if(E)_[K+1]=E;else{let B=k[K+1],q=z.normal((K+1)/N).direction;_[K+1]={x:B.x+(K?W:J)*q.x,y:B.y+(K?W:J)*q.y}}}),new j(_);return[0,1].forEach(function(K){if(N===2&&!!K)return;let X=k[K+1],F={x:X.x-Z.x,y:X.y-Z.y},H=Q((K+1)/N),E=Math.sqrt(F.x*F.x+F.y*F.y);F.x/=E,F.y/=E,_[K+1]={x:X.x+H*F.x,y:X.y+H*F.y}}),new j(_)}function n(z,G){let N=G.p1.x,Q=G.p1.y,k=-Math.atan2(G.p2.y-Q,G.p2.x-N),J=function(W){return{x:(W.x-N)*Math.cos(k)-(W.y-Q)*Math.sin(k),y:(W.x-N)*Math.sin(k)+(W.y-Q)*Math.cos(k)}};return z.map(J)}function x(z,G,N,Q,k){let J=N-G,W=k-Q,O=z-G,_=O/J;return Q+W*_}class d extends Error{constructor(z){super(z)}}function i(z,G){if(z.min.x<=G.max.x&&G.min.x<=z.max.x&&z.min.y<=G.max.y&&G.min.y<=z.max.y)return!0;return!1}function D(z,G,N){return Math.abs(z-G)<=(N||0.000001)}function U(z){if(z<0)return-Math.pow(-z,0.3333333333333333);return Math.pow(z,0.3333333333333333)}function A(z){return 0<=z&&z<=1}function W0(z,G,N,Q){let k=3*z-6*G+3*N,J=-3*z+3*G,W=z,O=-z+3*G-3*N+Q;if(D(O,0)){if(D(k,0)){if(D(J,0))return[];return[-W/J].filter(A)}let R=Math.sqrt(J*J-4*k*W),L=2*k;return[(R-J)/L,(-J-R)/L].filter(A)}k/=O,J/=O,W/=O;let _=(3*J-k*k)/3,Z=_/3,K=(2*k*k*k-9*k*J+27*W)/27,X=K/2,F=X*X+Z*Z*Z,H,E,B,q,M;if(F<0){let R=-_/3,L=R*R*R,m=Math.sqrt(L),b=-K/(2*m),s=b<-1?-1:b>1?1:b,T=Math.acos(s),l=U(m),h=2*l;return B=h*Math.cos(T/3)-k/3,q=h*Math.cos((T+2*Math.PI)/3)-k/3,M=h*Math.cos((T+4*Math.PI)/3)-k/3,[B,q,M].filter(A)}if(F===0)return H=X<0?U(-X):-U(X),B=2*H-k/3,q=-H-k/3,[B,q].filter(A);var I=Math.sqrt(F);return H=U(I-X),E=U(I+X),B=H-E-k/3,[B].filter(A)}function V(z,G,N=0.01){let k=[{curve1:{curve:z,startTVal:0,endTVal:1},curve2:{curve:G,startTVal:0,endTVal:1}}],J=[];while(k.length>0){let O=k.length;for(let _=0;_<O;_++){let Z=k.shift();if(Z==null)break;let K=Z.curve1.curve.AABB,X=Z.curve2.curve.AABB,F=i(K,X);if(Z.curve1.curve.fullLength<0.5&&Z.curve2.curve.fullLength<0.5){J.push({intersection:Z.curve1.curve.get(0.5),tVal1:(Z.curve1.startTVal+Z.curve1.endTVal)*0.5,tVal2:(Z.curve2.startTVal+Z.curve2.endTVal)*0.5});continue}if(F){let[H,E]=Z.curve1.curve.split(0.5),[B,q]=Z.curve2.curve.split(0.5);k.push({curve1:{curve:new j(H),startTVal:Z.curve1.startTVal,endTVal:Z.curve1.startTVal+(Z.curve1.endTVal-Z.curve1.startTVal)*0.5},curve2:{curve:new j(B),startTVal:Z.curve2.startTVal,endTVal:Z.curve2.startTVal+(Z.curve2.endTVal-Z.curve2.startTVal)*0.5}}),k.push({curve1:{curve:new j(H),startTVal:Z.curve1.startTVal,endTVal:Z.curve1.startTVal+(Z.curve1.endTVal-Z.curve1.startTVal)*0.5},curve2:{curve:new j(q),startTVal:Z.curve2.startTVal+(Z.curve2.endTVal-Z.curve2.startTVal)*0.5,endTVal:Z.curve2.endTVal}}),k.push({curve1:{curve:new j(E),startTVal:Z.curve1.startTVal+(Z.curve1.endTVal-Z.curve1.startTVal)*0.5,endTVal:Z.curve1.endTVal},curve2:{curve:new j(B),startTVal:Z.curve2.startTVal,endTVal:Z.curve2.startTVal+(Z.curve2.endTVal-Z.curve2.startTVal)*0.5}}),k.push({curve1:{curve:new j(E),startTVal:Z.curve1.startTVal+(Z.curve1.endTVal-Z.curve1.startTVal)*0.5,endTVal:Z.curve1.endTVal},curve2:{curve:new j(q),startTVal:Z.curve2.startTVal+(Z.curve2.endTVal-Z.curve2.startTVal)*0.5,endTVal:Z.curve2.endTVal}})}}}let W=[];J.sort((O,_)=>O.tVal1-_.tVal1);for(let O of J){let _=!1;for(let Z of W){let K=D(O.tVal1,Z.selfT,N),X=D(O.tVal2,Z.otherT,N);if(K&&X){_=!0;break}let F=D(O.tVal1,Z.selfT,N*10),H=D(O.tVal2,Z.otherT,N*10);if(F||H){let E=z.get(O.tVal1),B=G.get(O.tVal2),q=z.get(Z.selfT),M=G.get(Z.otherT),I=$.distanceBetweenPoints(E,q),R=$.distanceBetweenPoints(B,M);if(I<N*100&&R<N*100){_=!0;break}}}if(!_)W.push({selfT:O.tVal1,otherT:O.tVal2})}return W}function w(z,G,N,Q){if(Math.abs(z)<0.00000001){if(z=G,G=N,N=Q,Math.abs(z)<0.00000001){if(z=G,G=N,Math.abs(z)<0.00000001)return[];return[-G/z]}let O=G*G-4*z*N;if(Math.abs(O)<0.00000001)return[-G/(2*z)];else if(O>0)return[(-G+Math.sqrt(O))/(2*z),(-G-Math.sqrt(O))/(2*z)];return[]}let k=(3*z*N-G*G)/(3*z*z),J=(2*G*G*G-9*z*G*N+27*z*z*Q)/(27*z*z*z),W;if(Math.abs(k)<0.00000001)W=[u(-J)];else if(Math.abs(J)<0.00000001)W=[0].concat(k<0?[Math.sqrt(-k),-Math.sqrt(-k)]:[]);else{let O=J*J/4+k*k*k/27;if(Math.abs(O)<0.00000001)W=[-1.5*J/k,3*J/k];else if(O>0){let _=u(-J/2-Math.sqrt(O)),Z=u(-J/2+Math.sqrt(O));W=[_-k/(3*_)]}else{let _=2*Math.sqrt(-k/3),Z=Math.acos(3*J/k/_)/3,K=2*Math.PI/3;W=[_*Math.cos(Z),_*Math.cos(Z-K),_*Math.cos(Z-2*K)]}}for(let O=0;O<W.length;O++)W[O]-=G/(3*z);return W}function u(z){var G=Math.pow(Math.abs(z),0.3333333333333333);return z<0?-G:G}function y(z,G){let N=[...G];while(N.length>1){let Q=N.slice(1);for(let k=0;k<Q.length;k++)Q[k]=$.addVector($.multiplyVectorByScalar(N[k],1-z),$.multiplyVectorByScalar(N[k+1],z));N=Q}return N[0]}function t(z){if(z.getControlPoints().length===4){let k=z.getControlPoints(),J=$.subVector(k[3],k[0]),W=$.subVector(k[1],k[0]),O=$.subVector(k[2],k[0]),_=$.angleFromA2B(J,W),Z=$.angleFromA2B(J,O);if(_>0&&Z<0||_<0&&Z>0)return!1}let G=z.normal(0).direction,N=z.normal(1).direction,Q=G.x*N.x+G.y*N.y;return Math.abs(Math.acos(Q))<Math.PI/3}function p(z,G,N,Q){let k=z.getControlPoints().length-1,J=z.getControlPoints(),W=J.map((O,_)=>(1-_/k)*N+_/k*Q);return new j(J.map((O,_)=>({x:O.x+W[_]*G.x,y:O.y+W[_]*G.y})))}import{PointCal as S}from"@ue-too/math";class z0{startPoint;endPoint;constructor(z,G){this.startPoint=z,this.endPoint=G}getStartPoint(){return this.startPoint}getEndPoint(){return this.endPoint}intersectionWithAnotherLine(z){return G0(this.startPoint,this.endPoint,z.getStartPoint(),z.getEndPoint())}projectPoint(z){return N0(z,this.getStartPoint(),this.getEndPoint())}length(){return S.distanceBetweenPoints(this.startPoint,this.endPoint)}getTranslationRotationToAlginXAxis(){let z=S.subVector({x:0,y:0},this.startPoint),G=S.angleFromA2B(S.subVector(this.endPoint,this.startPoint),{x:1,y:0});return{translation:z,rotationAngle:G}}pointInLine(z){let G=S.unitVectorFromA2B(this.startPoint,this.endPoint),N=S.subVector(z,this.startPoint),Q=S.dotProduct(N,G),k=S.unitVector(N),J=S.distanceBetweenPoints(this.startPoint,this.endPoint)*0.0001;return Q<=S.distanceBetweenPoints(this.startPoint,this.endPoint)&&Q>=0&&Math.abs(k.x-G.x)<0.0001&&Math.abs(k.y-G.y)<0.0001}lerp(z){return S.linearInterpolation(this.startPoint,this.endPoint,z)}}function G0(z,G,N,Q){let k=(Q.x-N.x)*(z.y-N.y)-(Q.y-N.y)*(z.x-N.x),J=(Q.y-N.y)*(G.x-z.x)-(Q.x-N.x)*(G.y-z.y);if(J===0)return{intersects:!1};let W=k/J;if(W>=0&&W<=1)return{intersects:!0,intersection:S.linearInterpolation(z,G,W),offset:W};else return{intersects:!1}}function N0(z,G,N){let Q=S.unitVector(S.subVector(N,G)),k=S.subVector(z,G),J=S.dotProduct(k,Q);if(J<0||J>S.magnitude(S.subVector(N,G)))return{within:!1};return{within:!0,projectionPoint:S.addVector(G,S.multiplyVectorByScalar(Q,J)),offset:J/S.magnitude(S.subVector(N,G))}}import{PointCal as Y}from"@ue-too/math";class P{position;leftHandle;rightHandle;constructor(z,G,N){this.position=z,this.leftHandle=G,this.rightHandle=N}setPosition(z,G,N){let Q=Y.subVector(z,this.position);if(this.position=z,this.leftHandle.position=Y.addVector(this.leftHandle.position,Q),this.rightHandle.position=Y.addVector(this.rightHandle.position,Q),this.leftHandle.type=="VECTOR"&&G){let k=Y.subVector(G.getPosition(),this.position);if(k=Y.multiplyVectorByScalar(k,0.3333333333333333),this.leftHandle.position=Y.addVector(this.position,k),this.rightHandle.type=="ALIGNED"){let J=Y.subVector(this.rightHandle.position,this.position),W=Y.magnitude(J),O=Y.unitVectorFromA2B(this.leftHandle.position,this.position);this.rightHandle.position=Y.addVector(this.position,Y.multiplyVectorByScalar(O,W))}}if(this.rightHandle.type=="VECTOR"&&N){let k=Y.subVector(N.getPosition(),this.position);if(k=Y.multiplyVectorByScalar(k,0.3333333333333333),this.rightHandle.position=Y.addVector(this.position,k),this.leftHandle.type=="ALIGNED"){let J=Y.distanceBetweenPoints(this.leftHandle.position,this.position),W=Y.subVector(this.position,this.rightHandle.position);this.leftHandle.position=Y.addVector(this.position,Y.multiplyVectorByScalar(W,J))}}if(G!==void 0&&G.getRightHandle().type=="VECTOR"){let k=Y.subVector(this.position,G.getPosition());k=Y.multiplyVectorByScalar(k,0.3333333333333333),G.setRightHandlePosition(Y.addVector(G.getPosition(),k))}if(N!==void 0&&N.getLeftHandle().type=="VECTOR"){let k=Y.subVector(this.position,N.getPosition());k=Y.multiplyVectorByScalar(k,0.3333333333333333),N.setLeftHandlePosition(Y.addVector(N.getPosition(),k))}}getPosition(){return this.position}setLeftHandleTypeVector(z){if(this.rightHandle.type!="VECTOR")this.rightHandle.type="FREE";let G;if(z==null)G=Y.subVector(this.leftHandle.position,this.position);else G=Y.subVector(z.getPosition(),this.position),G=Y.multiplyVectorByScalar(G,0.3333333333333333);this.leftHandle.position=Y.addVector(this.position,G)}setLeftHandleTypeAligned(){if(this.leftHandle.type="ALIGNED",this.rightHandle.type=="VECTOR"){let z=Y.unitVectorFromA2B(this.rightHandle.position,this.position),G=Y.distanceBetweenPoints(this.position,this.leftHandle.position);this.leftHandle.position=Y.addVector(this.position,Y.multiplyVectorByScalar(z,G))}}setLeftHandleTypeFree(){this.leftHandle.type="FREE"}setRightHandleTypeVector(z){if(this.leftHandle.type!="VECTOR")this.leftHandle.type="FREE";let G;if(z==null)G=Y.subVector(this.rightHandle.position,this.position);else G=Y.subVector(z.getPosition(),this.position),G=Y.multiplyVectorByScalar(G,0.3333333333333333);this.rightHandle.position=Y.addVector(this.position,G)}setRightHandleTypeAligned(){if(this.rightHandle.type="ALIGNED",this.leftHandle.type=="VECTOR"){let z=Y.unitVectorFromA2B(this.leftHandle.position,this.position),G=Y.distanceBetweenPoints(this.position,this.rightHandle.position);this.rightHandle.position=Y.addVector(this.position,Y.multiplyVectorByScalar(z,G))}}setRightHandleTypeFree(){this.rightHandle.type="FREE"}setLeftHandlePosition(z){switch(this.leftHandle.type){case"ALIGNED":if(this.rightHandle.type=="VECTOR"){let N=Y.subVector(z,this.position),Q=Y.unitVectorFromA2B(this.rightHandle.position,this.position),k=Y.dotProduct(N,Q),J=Y.multiplyVectorByScalar(Q,k);this.leftHandle.position=Y.addVector(this.position,J)}else if(this.rightHandle.type=="ALIGNED"){this.leftHandle.position=z;let N=Y.distanceBetweenPoints(this.rightHandle.position,this.position),Q=Y.unitVectorFromA2B(this.leftHandle.position,this.position),k=Y.multiplyVectorByScalar(Q,N);this.rightHandle.position=Y.addVector(k,this.position)}else this.leftHandle.position=z;break;case"FREE":this.leftHandle.position=z;break;case"VECTOR":break;default:throw Error("Unknown left handle type for control point")}}setRightHandlePosition(z){switch(this.rightHandle.type){case"ALIGNED":if(this.leftHandle.type=="VECTOR"){let N=Y.subVector(z,this.position),Q=Y.unitVectorFromA2B(this.leftHandle.position,this.position),k=Y.dotProduct(N,Q),J=Y.multiplyVectorByScalar(Q,k);this.rightHandle.position=Y.addVector(this.position,J)}else if(this.rightHandle.type=="ALIGNED"){this.rightHandle.position=z;let N=Y.distanceBetweenPoints(this.leftHandle.position,this.position),Q=Y.unitVectorFromA2B(this.rightHandle.position,this.position),k=Y.multiplyVectorByScalar(Q,N);this.leftHandle.position=Y.addVector(k,this.position)}else this.rightHandle.position=z;break;case"FREE":this.rightHandle.position=z;break;case"VECTOR":break;default:throw Error("Unknown left handle type for control point")}}getLeftHandle(){return this.leftHandle}getRightHandle(){return this.rightHandle}}class Q0{controlPoints;constructor(z=[]){this.controlPoints=z}getControlPoints(){return this.controlPoints}appendControlPoint(z){let G=Y.addVector(z,{x:-100,y:0}),N=Y.addVector(z,{x:100,y:0}),J=new P(z,{position:G,type:"FREE"},{position:N,type:"FREE"});this.controlPoints.push(J)}setLeftHandlePositionOfControlPoint(z,G){if(z>=this.controlPoints.length||z<0)return;this.controlPoints[z].setLeftHandlePosition(G)}setRightHandlePositionOfControlPoint(z,G){if(z>=this.controlPoints.length||z<0)return;this.controlPoints[z].setRightHandlePosition(G)}setPositionOfControlPoint(z,G){if(z>=this.controlPoints.length||z<0)return;let N=void 0,Q=void 0;if(z+1<this.controlPoints.length)Q=this.controlPoints[z+1];if(z-1>=0)N=this.controlPoints[z-1];this.controlPoints[z].setPosition(G,N,Q)}}class k0{lines;constructor(z){this.lines=z}append(z){this.lines.push(z)}clear(){this.lines=[]}prepend(z){this.lines.unshift(z)}getLines(){return this.lines}getLength(){let z=0;return this.lines.forEach((G)=>{z+=G.length()}),z}getPercentages(){let z=this.getLength(),G=0,N=[];return this.lines.forEach((Q)=>{let J=Q.length()/z,W=G;G+=J;let O=G;N.push({start:W,end:O})}),N[N.length-1].end=1,N}getPointByPercentage(z){if(z<0||z>1)throw Error("Percentage must be between 0 and 1");let G=this.getPercentages(),N=0,Q=G.length-1;while(N<=Q){let O=Math.floor((N+Q)/2);if(z<G[O].end)Q=O-1;else if(z>G[O].end)N=O+1;else{N=O;break}}let k=this.lines[N],J=G[N],W=(z-J.start)/(J.end-J.start);return k.lerp(W)}}export{w as solveCubic,r as reduce,N0 as projectPointOntoLine,O0 as offset2,g as offset,G0 as getLineIntersection,V as getIntersectionsBetweenCurves,W0 as getCubicRoots,U as cuberoot2,u as cuberoot,y as computeWithControlPoints,D as approximately,A as accept,d as TValOutofBoundError,k0 as Path,z0 as Line,P as ControlPoint,Q0 as CompositeBCurve,j as BCurve,i as AABBIntersects};
1
+ // src/b-curve.ts
2
+ import { PointCal } from "@ue-too/math";
3
+ var T = [
4
+ -0.06405689286260563,
5
+ 0.06405689286260563,
6
+ -0.1911188674736163,
7
+ 0.1911188674736163,
8
+ -0.3150426796961634,
9
+ 0.3150426796961634,
10
+ -0.4337935076260451,
11
+ 0.4337935076260451,
12
+ -0.5454214713888396,
13
+ 0.5454214713888396,
14
+ -0.6480936519369755,
15
+ 0.6480936519369755,
16
+ -0.7401241915785544,
17
+ 0.7401241915785544,
18
+ -0.820001985973903,
19
+ 0.820001985973903,
20
+ -0.8864155270044011,
21
+ 0.8864155270044011,
22
+ -0.9382745520027328,
23
+ 0.9382745520027328,
24
+ -0.9747285559713095,
25
+ 0.9747285559713095,
26
+ -0.9951872199970213,
27
+ 0.9951872199970213
28
+ ];
29
+ var C = [
30
+ 0.12793819534675216,
31
+ 0.12793819534675216,
32
+ 0.1258374563468283,
33
+ 0.1258374563468283,
34
+ 0.12167047292780339,
35
+ 0.12167047292780339,
36
+ 0.1155056680537256,
37
+ 0.1155056680537256,
38
+ 0.10744427011596563,
39
+ 0.10744427011596563,
40
+ 0.09761865210411388,
41
+ 0.09761865210411388,
42
+ 0.08619016153195327,
43
+ 0.08619016153195327,
44
+ 0.0733464814110803,
45
+ 0.0733464814110803,
46
+ 0.05929858491543678,
47
+ 0.05929858491543678,
48
+ 0.04427743881741981,
49
+ 0.04427743881741981,
50
+ 0.028531388628933663,
51
+ 0.028531388628933663,
52
+ 0.0123412297999872,
53
+ 0.0123412297999872
54
+ ];
2
55
 
3
- //# debugId=601401469D08996164756E2164756E21
56
+ class BCurve {
57
+ controlPoints;
58
+ dControlPoints = [];
59
+ arcLengthLUT = {
60
+ controlPoints: [],
61
+ arcLengthLUT: []
62
+ };
63
+ _fullLength;
64
+ static LENGTH_CACHE_MAX_SIZE = 2048;
65
+ lengthCache = new Map;
66
+ getCacheStats() {
67
+ return {
68
+ size: this.lengthCache.size,
69
+ hitRate: 0
70
+ };
71
+ }
72
+ preWarmCache(steps = 100) {
73
+ const tSteps = 1 / steps;
74
+ for (let tVal = 0;tVal <= 1; tVal += tSteps) {
75
+ this.lengthAtT(tVal);
76
+ }
77
+ }
78
+ clearCache() {
79
+ this.lengthCache.clear();
80
+ }
81
+ constructor(controlPoints) {
82
+ this.controlPoints = controlPoints;
83
+ this.dControlPoints = this.getDerivativeControlPoints(this.controlPoints);
84
+ this._fullLength = this.calculateFullLength();
85
+ this.arcLengthLUT = { controlPoints: [], arcLengthLUT: [] };
86
+ this.clearCache();
87
+ }
88
+ getPointbyPercentage(percentage) {
89
+ let controlPointsChangedSinceLastArcLengthLUT = this.arcLengthLUT.controlPoints.length != this.controlPoints.length;
90
+ controlPointsChangedSinceLastArcLengthLUT = controlPointsChangedSinceLastArcLengthLUT || this.arcLengthLUT.controlPoints.reduce((prevVal, curVal, index) => {
91
+ return prevVal || !PointCal.isEqual(curVal, this.controlPoints[index]);
92
+ }, false);
93
+ let points = [];
94
+ if (controlPointsChangedSinceLastArcLengthLUT) {
95
+ this.arcLengthLUT = this.getArcLengthLUT(1000);
96
+ }
97
+ points = [...this.arcLengthLUT.arcLengthLUT.map((item) => item.length)];
98
+ let targetLength = percentage * this.fullLength;
99
+ let low = 0;
100
+ let high = points.length - 1;
101
+ while (low <= high) {
102
+ let mid = Math.floor((low + high) / 2);
103
+ if (points[mid] == targetLength) {
104
+ return this.get((mid + 1) / points.length);
105
+ } else if (points[mid] < targetLength) {
106
+ low = mid + 1;
107
+ } else {
108
+ high = mid - 1;
109
+ }
110
+ }
111
+ return low >= points.length ? this.get(1) : this.get((low + 1) / points.length);
112
+ }
113
+ derivativeByPercentage(percentage) {
114
+ let controlPointsChangedSinceLastArcLengthLUT = this.arcLengthLUT.controlPoints.length != this.controlPoints.length;
115
+ controlPointsChangedSinceLastArcLengthLUT = controlPointsChangedSinceLastArcLengthLUT || this.arcLengthLUT.controlPoints.reduce((prevVal, curVal, index) => {
116
+ return prevVal || !PointCal.isEqual(curVal, this.controlPoints[index]);
117
+ }, false);
118
+ let points = [];
119
+ if (controlPointsChangedSinceLastArcLengthLUT) {
120
+ this.arcLengthLUT = this.getArcLengthLUT(1000);
121
+ }
122
+ points = [...this.arcLengthLUT.arcLengthLUT.map((item) => item.length)];
123
+ let targetLength = percentage * this.fullLength;
124
+ let low = 0;
125
+ let high = points.length - 1;
126
+ let tVal;
127
+ while (low <= high) {
128
+ let mid = Math.floor((low + high) / 2);
129
+ if (points[mid] == targetLength) {
130
+ tVal = (mid + 1) / points.length;
131
+ return this.derivative(tVal);
132
+ } else if (points[mid] < targetLength) {
133
+ low = mid + 1;
134
+ } else {
135
+ high = mid - 1;
136
+ }
137
+ }
138
+ tVal = low >= points.length ? 1 : (low + 1) / points.length;
139
+ return this.derivative(tVal);
140
+ }
141
+ getDerivativeControlPoints(controlPoints) {
142
+ const derivativeControlPoints = [];
143
+ for (let index = 1;index < controlPoints.length; index++) {
144
+ derivativeControlPoints.push(PointCal.multiplyVectorByScalar(PointCal.subVector(controlPoints[index], controlPoints[index - 1]), controlPoints.length - 1));
145
+ }
146
+ return derivativeControlPoints;
147
+ }
148
+ validateTVal(tVal) {
149
+ if (tVal > 1 || tVal < 0) {
150
+ throw new TValOutofBoundError("tVal is greater than 1 or less than 0");
151
+ }
152
+ }
153
+ getControlPoints() {
154
+ return this.controlPoints;
155
+ }
156
+ setControlPoints(controlPoints) {
157
+ this.controlPoints = controlPoints;
158
+ this.dControlPoints = this.getDerivativeControlPoints(this.controlPoints);
159
+ this._fullLength = this.calculateFullLength();
160
+ this.arcLengthLUT = { controlPoints: [], arcLengthLUT: [] };
161
+ this.clearCache();
162
+ }
163
+ setControlPointAtIndex(index, newPoint) {
164
+ if (index < 0 || index >= this.controlPoints.length) {
165
+ return false;
166
+ }
167
+ this.controlPoints[index] = newPoint;
168
+ this.dControlPoints = this.getDerivativeControlPoints(this.controlPoints);
169
+ this._fullLength = this.calculateFullLength();
170
+ this.arcLengthLUT = { controlPoints: [], arcLengthLUT: [] };
171
+ this.clearCache();
172
+ return true;
173
+ }
174
+ compute(tVal) {
175
+ this.validateTVal(tVal);
176
+ let points = this.controlPoints;
177
+ while (points.length > 1) {
178
+ let lowerLevelPoints = points.slice(1);
179
+ for (let index = 0;index < lowerLevelPoints.length; index++) {
180
+ lowerLevelPoints[index] = PointCal.addVector(PointCal.multiplyVectorByScalar(points[index], 1 - tVal), PointCal.multiplyVectorByScalar(points[index + 1], tVal));
181
+ }
182
+ points = lowerLevelPoints;
183
+ }
184
+ return points[0];
185
+ }
186
+ get(tVal) {
187
+ this.validateTVal(tVal);
188
+ if (this.controlPoints.length == 3) {
189
+ let firstTerm = PointCal.multiplyVectorByScalar(this.controlPoints[0], (1 - tVal) * (1 - tVal));
190
+ let secondTerm = PointCal.multiplyVectorByScalar(this.controlPoints[1], 2 * (1 - tVal) * tVal);
191
+ let thirdTerm = PointCal.multiplyVectorByScalar(this.controlPoints[2], tVal * tVal);
192
+ let res = PointCal.addVector(PointCal.addVector(firstTerm, secondTerm), thirdTerm);
193
+ return res;
194
+ }
195
+ if (this.controlPoints.length == 4) {
196
+ let firstTerm = PointCal.multiplyVectorByScalar(this.controlPoints[0], (1 - tVal) * (1 - tVal) * (1 - tVal));
197
+ let secondTerm = PointCal.multiplyVectorByScalar(this.controlPoints[1], 3 * (1 - tVal) * (1 - tVal) * tVal);
198
+ let thirdTerm = PointCal.multiplyVectorByScalar(this.controlPoints[2], 3 * (1 - tVal) * tVal * tVal);
199
+ let forthTerm = PointCal.multiplyVectorByScalar(this.controlPoints[3], tVal * tVal * tVal);
200
+ let res = PointCal.addVector(PointCal.addVector(firstTerm, secondTerm), PointCal.addVector(thirdTerm, forthTerm));
201
+ return res;
202
+ }
203
+ return this.compute(tVal);
204
+ }
205
+ getLUT(steps = 100) {
206
+ const stepSpan = 1 / steps;
207
+ const res = [];
208
+ let tVal = 0;
209
+ res.push(this.get(tVal));
210
+ for (let index = 0;index < steps; index += 1) {
211
+ tVal += stepSpan;
212
+ if (tVal > 1 && tVal - stepSpan < 1 || index == steps - 1) {
213
+ tVal = 1;
214
+ }
215
+ res.push(this.get(tVal));
216
+ }
217
+ return res;
218
+ }
219
+ getLUTWithTVal(steps) {
220
+ if (steps == undefined) {
221
+ steps = 100;
222
+ }
223
+ const stepSpan = 1 / steps;
224
+ const res = [];
225
+ let tVal = 0;
226
+ res.push({ point: this.get(tVal), tVal });
227
+ for (let index = 0;index < steps; index += 1) {
228
+ tVal += stepSpan;
229
+ if (tVal > 1 && tVal - stepSpan < 1 || index == steps - 1) {
230
+ tVal = 1;
231
+ }
232
+ res.push({ point: this.get(tVal), tVal });
233
+ }
234
+ return res;
235
+ }
236
+ get fullLength() {
237
+ return this._fullLength;
238
+ }
239
+ calculateFullLength() {
240
+ return this.lengthAtT(1);
241
+ }
242
+ lengthAtT(tVal) {
243
+ this.validateTVal(tVal);
244
+ const cacheKey = Math.round(tVal * 1e6) / 1e6;
245
+ if (this.lengthCache.has(cacheKey)) {
246
+ return this.lengthCache.get(cacheKey);
247
+ }
248
+ const z = tVal / 2, len = T.length;
249
+ let sum = 0;
250
+ for (let i = 0, t;i < len; i++) {
251
+ t = z * T[i] + z;
252
+ sum += C[i] * PointCal.magnitude(this.derivative(t));
253
+ }
254
+ const result = z * sum;
255
+ if (this.lengthCache.size >= BCurve.LENGTH_CACHE_MAX_SIZE) {
256
+ const firstKey = this.lengthCache.keys().next().value;
257
+ this.lengthCache.delete(firstKey);
258
+ }
259
+ this.lengthCache.set(cacheKey, result);
260
+ return result;
261
+ }
262
+ derivative(tVal) {
263
+ return computeWithControlPoints(tVal, this.dControlPoints);
264
+ }
265
+ derivativeNormalized(tVal) {
266
+ return PointCal.unitVector(computeWithControlPoints(tVal, this.dControlPoints));
267
+ }
268
+ getArcLengthLUT(steps = 50) {
269
+ const controlPointsChanged = this.arcLengthLUT.controlPoints.length !== this.controlPoints.length || this.arcLengthLUT.controlPoints.some((cp, index) => !PointCal.isEqual(cp, this.controlPoints[index]));
270
+ if (controlPointsChanged || this.arcLengthLUT.arcLengthLUT.length === 0) {
271
+ this.clearCache();
272
+ let res = [];
273
+ let tSteps = 1 / steps;
274
+ for (let tVal = 0;tVal <= 1; tVal += tSteps) {
275
+ res.push({ tVal, length: this.lengthAtT(tVal) });
276
+ }
277
+ this.arcLengthLUT = {
278
+ controlPoints: [...this.controlPoints],
279
+ arcLengthLUT: res
280
+ };
281
+ }
282
+ return this.arcLengthLUT;
283
+ }
284
+ splitIntoCurves(tVal) {
285
+ const res = this.split(tVal);
286
+ return [new BCurve(res[0]), new BCurve(res[1])];
287
+ }
288
+ split(tVal) {
289
+ this.validateTVal(tVal);
290
+ if (this.controlPoints.length == 3) {
291
+ let newControlPoint12 = this.controlPoints[0];
292
+ let newControlPoint22 = PointCal.subVector(PointCal.multiplyVectorByScalar(this.controlPoints[1], tVal), PointCal.multiplyVectorByScalar(this.controlPoints[0], tVal - 1));
293
+ let newControlPoint32 = PointCal.subVector(PointCal.multiplyVectorByScalar(this.controlPoints[2], tVal * tVal), PointCal.multiplyVectorByScalar(this.controlPoints[1], 2 * tVal * (tVal - 1)));
294
+ newControlPoint32 = PointCal.addVector(newControlPoint32, PointCal.multiplyVectorByScalar(this.controlPoints[0], (tVal - 1) * (tVal - 1)));
295
+ let newControlPoint42 = PointCal.subVector(PointCal.multiplyVectorByScalar(this.controlPoints[2], tVal), PointCal.multiplyVectorByScalar(this.controlPoints[1], tVal - 1));
296
+ let newControlPoint52 = this.controlPoints[2];
297
+ return [
298
+ [newControlPoint12, newControlPoint22, newControlPoint32],
299
+ [newControlPoint32, newControlPoint42, newControlPoint52]
300
+ ];
301
+ }
302
+ let newControlPoint1 = this.controlPoints[0];
303
+ let newControlPoint2 = PointCal.subVector(PointCal.multiplyVectorByScalar(this.controlPoints[1], tVal), PointCal.multiplyVectorByScalar(this.controlPoints[0], tVal - 1));
304
+ let newControlPoint3 = PointCal.addVector(PointCal.multiplyVectorByScalar(this.controlPoints[2], tVal * tVal), PointCal.addVector(PointCal.multiplyVectorByScalar(this.controlPoints[1], -(2 * tVal * (tVal - 1))), PointCal.multiplyVectorByScalar(this.controlPoints[0], (tVal - 1) * (tVal - 1))));
305
+ let term1 = PointCal.multiplyVectorByScalar(this.controlPoints[3], tVal * tVal * tVal);
306
+ let term2 = PointCal.multiplyVectorByScalar(this.controlPoints[2], -(3 * tVal * tVal * (tVal - 1)));
307
+ let term3 = PointCal.multiplyVectorByScalar(this.controlPoints[1], 3 * tVal * (tVal - 1) * (tVal - 1));
308
+ let term4 = PointCal.multiplyVectorByScalar(this.controlPoints[0], -((tVal - 1) * (tVal - 1) * (tVal - 1)));
309
+ let newControlPoint4 = PointCal.addVector(term4, PointCal.addVector(term3, PointCal.addVector(term1, term2)));
310
+ let newControlPoint5 = PointCal.addVector(PointCal.addVector(PointCal.multiplyVectorByScalar(this.controlPoints[3], tVal * tVal), PointCal.multiplyVectorByScalar(this.controlPoints[2], -(2 * tVal * (tVal - 1)))), PointCal.multiplyVectorByScalar(this.controlPoints[1], (tVal - 1) * (tVal - 1)));
311
+ let newControlPoint6 = PointCal.addVector(PointCal.multiplyVectorByScalar(this.controlPoints[3], tVal), PointCal.multiplyVectorByScalar(this.controlPoints[2], -(tVal - 1)));
312
+ let newControlPoint7 = this.controlPoints[3];
313
+ return [
314
+ [
315
+ newControlPoint1,
316
+ newControlPoint2,
317
+ newControlPoint3,
318
+ newControlPoint4
319
+ ],
320
+ [
321
+ newControlPoint4,
322
+ newControlPoint5,
323
+ newControlPoint6,
324
+ newControlPoint7
325
+ ]
326
+ ];
327
+ }
328
+ splitIn3WithControlPoints(tVal, tVal2) {
329
+ if (tVal2 < tVal) {
330
+ console.warn("tVal2 is less than tVal, swapping them");
331
+ [tVal, tVal2] = [tVal2, tVal];
332
+ }
333
+ const firstSplit = this.split(tVal);
334
+ const secondHalf = new BCurve(firstSplit[1]);
335
+ const mappedTVal2 = map(tVal2, tVal, 1, 0, 1);
336
+ const secondSplit = secondHalf.split(mappedTVal2);
337
+ return [firstSplit[0], secondSplit[0], secondSplit[1]];
338
+ }
339
+ splitIn3Curves(tVal, tVal2) {
340
+ if (tVal2 < tVal) {
341
+ console.warn("tVal2 is less than tVal, swapping them");
342
+ [tVal, tVal2] = [tVal2, tVal];
343
+ }
344
+ const firstSplit = this.split(tVal);
345
+ const secondHalf = new BCurve(firstSplit[1]);
346
+ const mappedTVal2 = map(tVal2, tVal, 1, 0, 1);
347
+ const secondSplit = secondHalf.split(mappedTVal2);
348
+ return [
349
+ new BCurve(firstSplit[0]),
350
+ new BCurve(secondSplit[0]),
351
+ new BCurve(secondSplit[1])
352
+ ];
353
+ }
354
+ splitAndTakeMidCurve(tVal, tVal2) {
355
+ const [firstSplit, secondSplit, thirdSplit] = this.splitIn3Curves(tVal, tVal2);
356
+ return secondSplit;
357
+ }
358
+ getProjection(point) {
359
+ const threshold = 0.00001;
360
+ let distance = Number.MAX_VALUE;
361
+ let preliminaryProjectionTVal = 0;
362
+ let preliminaryProjectionPoint = this.get(0);
363
+ let preliminaryProjectionIndex = 0;
364
+ const LUT = this.getLUTWithTVal(500);
365
+ LUT.forEach((curvePoint, index) => {
366
+ const curDistance = PointCal.distanceBetweenPoints(curvePoint.point, point);
367
+ if (curDistance < distance) {
368
+ distance = curDistance;
369
+ preliminaryProjectionPoint = { ...curvePoint.point };
370
+ preliminaryProjectionTVal = curvePoint.tVal;
371
+ preliminaryProjectionIndex = index;
372
+ }
373
+ });
374
+ let low = LUT[preliminaryProjectionIndex].tVal;
375
+ let high = LUT[preliminaryProjectionIndex].tVal;
376
+ if (preliminaryProjectionIndex < LUT.length - 1) {
377
+ high = LUT[preliminaryProjectionIndex + 1].tVal;
378
+ }
379
+ if (preliminaryProjectionIndex > 0) {
380
+ low = LUT[preliminaryProjectionIndex - 1].tVal;
381
+ }
382
+ while (low < high && high - low > threshold) {
383
+ let mid = low + (high - low) / 2;
384
+ let halfSpan = mid - low;
385
+ let lowMidMid = mid + halfSpan / 2;
386
+ let highMidMid = mid + halfSpan / 2;
387
+ let prevDist = distance;
388
+ if (lowMidMid <= 1 && lowMidMid >= 0) {
389
+ let curDist = PointCal.distanceBetweenPoints(this.get(lowMidMid), point);
390
+ if (curDist < distance) {
391
+ distance = curDist;
392
+ preliminaryProjectionPoint = this.get(lowMidMid);
393
+ preliminaryProjectionTVal = lowMidMid;
394
+ high = lowMidMid + halfSpan / 2;
395
+ low = lowMidMid - halfSpan / 2;
396
+ }
397
+ }
398
+ if (highMidMid <= 1 && highMidMid >= 0) {
399
+ let curDist = PointCal.distanceBetweenPoints(this.get(highMidMid), point);
400
+ if (curDist < distance) {
401
+ distance = curDist;
402
+ preliminaryProjectionPoint = this.get(highMidMid);
403
+ preliminaryProjectionTVal = highMidMid;
404
+ high = highMidMid + halfSpan / 2;
405
+ low = highMidMid - halfSpan / 2;
406
+ }
407
+ }
408
+ if (prevDist == distance) {
409
+ break;
410
+ }
411
+ }
412
+ return {
413
+ projection: preliminaryProjectionPoint,
414
+ tVal: preliminaryProjectionTVal
415
+ };
416
+ }
417
+ findArcs(errorThreshold) {
418
+ let low = 0;
419
+ const res = [];
420
+ while (low < 1) {
421
+ let loopRes = this.findArcStartingAt(errorThreshold, low);
422
+ if (loopRes == null || loopRes.arc == undefined) {
423
+ break;
424
+ }
425
+ res.push(loopRes.arc);
426
+ low = loopRes.arc.endT;
427
+ if (low >= 1) {
428
+ break;
429
+ }
430
+ }
431
+ return res;
432
+ }
433
+ getArcs(errorThreshold) {
434
+ return this.findArcs(errorThreshold);
435
+ }
436
+ findArcStartingAt(errorThreshold, low) {
437
+ let high = 1;
438
+ let mid = low + (high - low) / 2;
439
+ let prevArc = { good: false };
440
+ let count = 0;
441
+ while (true) {
442
+ count++;
443
+ mid = low + (high - low) / 2;
444
+ if (high > 1 || mid > 1) {
445
+ if (prevArc.good) {
446
+ return prevArc;
447
+ } else {
448
+ return null;
449
+ }
450
+ }
451
+ const lowPoint = this.get(low);
452
+ const highPoint = this.get(high);
453
+ const midPoint = this.get(mid);
454
+ const fitArcRes = this.fitArc(lowPoint, highPoint, midPoint);
455
+ if (!fitArcRes.exists || fitArcRes.center == null || fitArcRes.radius == null) {
456
+ return null;
457
+ }
458
+ const n = high - mid;
459
+ const e1 = mid - n / 2;
460
+ const e2 = mid + n / 2;
461
+ const checkPoint1 = this.get(e1);
462
+ const checkPoint2 = this.get(e2);
463
+ const checkRadius = PointCal.distanceBetweenPoints(checkPoint1, fitArcRes.center);
464
+ const checkRadius2 = PointCal.distanceBetweenPoints(checkPoint2, fitArcRes.center);
465
+ if (Math.abs(checkRadius - fitArcRes.radius) > errorThreshold || Math.abs(checkRadius2 - fitArcRes.radius) > errorThreshold) {
466
+ if (prevArc.good == true) {
467
+ return prevArc;
468
+ }
469
+ prevArc.good = false;
470
+ high = mid;
471
+ } else {
472
+ prevArc.good = true;
473
+ if (fitArcRes.startPoint !== undefined && fitArcRes.endPoint !== undefined) {
474
+ prevArc.arc = {
475
+ center: fitArcRes.center,
476
+ radius: fitArcRes.radius,
477
+ startPoint: fitArcRes.startPoint,
478
+ endPoint: fitArcRes.endPoint,
479
+ startT: low,
480
+ endT: high
481
+ };
482
+ }
483
+ high = high + (mid - low);
484
+ }
485
+ }
486
+ }
487
+ fitArc(startPoint, endPoint, midPoint) {
488
+ const M11 = [
489
+ [startPoint.x, startPoint.y, 1],
490
+ [midPoint.x, midPoint.y, 1],
491
+ [endPoint.x, endPoint.y, 1]
492
+ ];
493
+ if (this.determinant3by3(M11) == 0) {
494
+ return { exists: false };
495
+ }
496
+ const M12 = [
497
+ [
498
+ startPoint.x * startPoint.x + startPoint.y * startPoint.y,
499
+ startPoint.y,
500
+ 1
501
+ ],
502
+ [midPoint.x * midPoint.x + midPoint.y * midPoint.y, midPoint.y, 1],
503
+ [endPoint.x * endPoint.x + endPoint.y * endPoint.y, endPoint.y, 1]
504
+ ];
505
+ const M13 = [
506
+ [
507
+ startPoint.x * startPoint.x + startPoint.y * startPoint.y,
508
+ startPoint.x,
509
+ 1
510
+ ],
511
+ [midPoint.x * midPoint.x + midPoint.y * midPoint.y, midPoint.x, 1],
512
+ [endPoint.x * endPoint.x + endPoint.y * endPoint.y, endPoint.x, 1]
513
+ ];
514
+ const M14 = [
515
+ [
516
+ startPoint.x * startPoint.x + startPoint.y * startPoint.y,
517
+ startPoint.x,
518
+ startPoint.y
519
+ ],
520
+ [
521
+ midPoint.x * midPoint.x + midPoint.y * midPoint.y,
522
+ midPoint.x,
523
+ midPoint.y
524
+ ],
525
+ [
526
+ endPoint.x * endPoint.x + endPoint.y * endPoint.y,
527
+ endPoint.x,
528
+ endPoint.y
529
+ ]
530
+ ];
531
+ const centerX = 1 / 2 * (this.determinant3by3(M12) / this.determinant3by3(M11));
532
+ const centerY = -1 / 2 * (this.determinant3by3(M13) / this.determinant3by3(M11));
533
+ const radius = Math.sqrt(centerX * centerX + centerY * centerY + this.determinant3by3(M14) / this.determinant3by3(M11));
534
+ return {
535
+ exists: true,
536
+ center: { x: centerX, y: centerY },
537
+ radius,
538
+ startPoint,
539
+ endPoint
540
+ };
541
+ }
542
+ determinant3by3(matrix) {
543
+ const a = matrix[0][0];
544
+ const b = matrix[0][1];
545
+ const c = matrix[0][2];
546
+ const d = matrix[1][0];
547
+ const e = matrix[1][1];
548
+ const f = matrix[1][2];
549
+ const g = matrix[2][0];
550
+ const h = matrix[2][1];
551
+ const i = matrix[2][2];
552
+ return a * (e * i - f * h) - b * (d * i - g * f) + c * (d * h - e * g);
553
+ }
554
+ curvature(tVal) {
555
+ const derivative = computeWithControlPoints(tVal, this.dControlPoints);
556
+ const secondDerivative = computeWithControlPoints(tVal, this.getDerivativeControlPoints(this.dControlPoints));
557
+ const numerator = derivative.x * secondDerivative.y - secondDerivative.x * derivative.y;
558
+ const denominator = Math.pow(derivative.x * derivative.x + derivative.y * derivative.y, 3 / 2);
559
+ if (denominator == 0)
560
+ return NaN;
561
+ return numerator / denominator;
562
+ }
563
+ secondDerivative(tVal) {
564
+ return computeWithControlPoints(tVal, this.getDerivativeControlPoints(this.dControlPoints));
565
+ }
566
+ getCoefficientOfTTerms() {
567
+ return this.getCoefficientOfTTermsWithControlPoints(this.controlPoints);
568
+ }
569
+ getDerivativeCoefficients() {
570
+ return this.getCoefficientOfTTermsWithControlPoints(this.dControlPoints);
571
+ }
572
+ getCoefficientOfTTermsWithControlPoints(controlPoints) {
573
+ const terms = [];
574
+ let matrix = [];
575
+ if (controlPoints.length == 3) {
576
+ matrix = [
577
+ [1, 0, 0],
578
+ [-2, 2, 0],
579
+ [1, -2, 1]
580
+ ];
581
+ } else if (controlPoints.length == 4) {
582
+ matrix = [
583
+ [1, 0, 0, 0],
584
+ [-3, 3, 0, 0],
585
+ [3, -6, 3, 0],
586
+ [-1, 3, -3, 1]
587
+ ];
588
+ } else if (controlPoints.length == 2) {
589
+ matrix = [
590
+ [1, 0],
591
+ [-1, 1]
592
+ ];
593
+ } else {
594
+ throw new Error("number of control points is wrong");
595
+ }
596
+ for (let index = 0;index < controlPoints.length; index++) {
597
+ terms.push(controlPoints.reduce((prevVal, curVal, jindex) => {
598
+ return {
599
+ x: prevVal.x + matrix[index][jindex] * curVal.x,
600
+ y: prevVal.y + matrix[index][jindex] * curVal.y
601
+ };
602
+ }, { x: 0, y: 0 }));
603
+ }
604
+ return terms;
605
+ }
606
+ getControlPointsAlignedWithXAxis() {
607
+ const alignedAxis = PointCal.unitVectorFromA2B(this.controlPoints[0], this.controlPoints[this.controlPoints.length - 1]);
608
+ const angle = PointCal.angleFromA2B({ x: 1, y: 0 }, alignedAxis);
609
+ const startingPoint = this.controlPoints[0];
610
+ const res = [{ x: 0, y: 0 }];
611
+ for (let index = 1;index < this.controlPoints.length; index++) {
612
+ const vector = PointCal.subVector(this.controlPoints[index], startingPoint);
613
+ const rotatedVector = PointCal.rotatePoint(vector, -angle);
614
+ res.push(rotatedVector);
615
+ }
616
+ return res;
617
+ }
618
+ getExtrema() {
619
+ const res = { x: [], y: [] };
620
+ const derivativeCoefficients = this.getDerivativeCoefficients();
621
+ let xCoefficients = [0, 0, 0, 0];
622
+ let yCoefficients = [0, 0, 0, 0];
623
+ derivativeCoefficients.forEach((coefficient, index) => {
624
+ xCoefficients[3 - index] = coefficient.x;
625
+ yCoefficients[3 - index] = coefficient.y;
626
+ });
627
+ const xRoots = solveCubic(xCoefficients[0], xCoefficients[1], xCoefficients[2], xCoefficients[3]);
628
+ const yRoots = solveCubic(yCoefficients[0], yCoefficients[1], yCoefficients[2], yCoefficients[3]);
629
+ xRoots.forEach((root) => {
630
+ if (root >= 0 && root <= 1) {
631
+ res.x.push(root);
632
+ }
633
+ });
634
+ yRoots.forEach((root) => {
635
+ if (root >= 0 && root <= 1) {
636
+ res.y.push(root);
637
+ }
638
+ });
639
+ if (derivativeCoefficients.length >= 3) {
640
+ xCoefficients = [0, 0, 0, 0];
641
+ yCoefficients = [0, 0, 0, 0];
642
+ const secondDerivativeCoefficients = this.getCoefficientOfTTermsWithControlPoints(this.getDerivativeControlPoints(this.dControlPoints));
643
+ secondDerivativeCoefficients.forEach((coefficient, index) => {
644
+ xCoefficients[3 - index] = coefficient.x;
645
+ yCoefficients[3 - index] = coefficient.y;
646
+ });
647
+ const secondXRoots = solveCubic(xCoefficients[0], xCoefficients[1], xCoefficients[2], xCoefficients[3]);
648
+ const secondYRoots = solveCubic(yCoefficients[0], yCoefficients[1], yCoefficients[2], yCoefficients[3]);
649
+ secondXRoots.forEach((root) => {
650
+ if (root >= 0 && root <= 1) {
651
+ res.x.push(root);
652
+ }
653
+ });
654
+ secondYRoots.forEach((root) => {
655
+ if (root >= 0 && root <= 1) {
656
+ res.y.push(root);
657
+ }
658
+ });
659
+ }
660
+ return res;
661
+ }
662
+ translateRotateControlPoints(translation, rotationAngle) {
663
+ const res = [];
664
+ for (let index = 0;index < this.controlPoints.length; index++) {
665
+ res.push(PointCal.rotatePoint(PointCal.addVector(this.controlPoints[index], translation), rotationAngle));
666
+ }
667
+ return res;
668
+ }
669
+ getLineIntersections(line) {
670
+ const translationRotation = line.getTranslationRotationToAlginXAxis();
671
+ const res = [];
672
+ const alignedControlPoints = this.translateRotateControlPoints(translationRotation.translation, translationRotation.rotationAngle);
673
+ const coefficients = this.getCoefficientOfTTermsWithControlPoints(alignedControlPoints);
674
+ let yCoefficients = [0, 0, 0, 0];
675
+ coefficients.forEach((coefficient, index) => {
676
+ yCoefficients[3 - index] = coefficient.y;
677
+ });
678
+ const yRoots = solveCubic(yCoefficients[0], yCoefficients[1], yCoefficients[2], yCoefficients[3]);
679
+ yRoots.forEach((root) => {
680
+ if (root >= 0 && root <= 1) {
681
+ if (line.pointInLine(this.get(root))) {
682
+ res.push(root);
683
+ }
684
+ }
685
+ });
686
+ return res;
687
+ }
688
+ getSelfIntersections() {
689
+ const [subCurveControlPoints1, subCurveControlPoints2] = this.split(0.5);
690
+ const subCurve1 = new BCurve(subCurveControlPoints1);
691
+ const subCurve2 = new BCurve(subCurveControlPoints2);
692
+ let initialRes = getIntersectionsBetweenCurves(subCurve1, subCurve2);
693
+ initialRes.forEach((intersection) => {
694
+ intersection.selfT = intersection.selfT * 0.5;
695
+ intersection.otherT = intersection.otherT * 0.5 + 0.5;
696
+ });
697
+ initialRes.shift();
698
+ return initialRes;
699
+ }
700
+ getCircleIntersections(circleCenter, circleRadius) {
701
+ const LUT = this.getLUTWithTVal(500);
702
+ let distanceError = Number.MAX_VALUE;
703
+ let preliminaryIntersectionIndex = 0;
704
+ let preliminaryIntersectionPoint = LUT[0].point;
705
+ let preliminaryIntersectionTVal = LUT[0].tVal;
706
+ LUT.forEach((curvePoint, index) => {
707
+ let curDistanceError = Math.abs(PointCal.distanceBetweenPoints(circleCenter, curvePoint.point));
708
+ if (curDistanceError < distanceError) {
709
+ distanceError = curDistanceError;
710
+ preliminaryIntersectionIndex = index;
711
+ }
712
+ });
713
+ const LUTD = LUT.map((curvePoint, index) => {
714
+ return { ...curvePoint, distance: 0 };
715
+ });
716
+ distanceError = Number.MAX_VALUE;
717
+ let start = 0;
718
+ let count = 0;
719
+ let indices = [];
720
+ while (++count < 25) {
721
+ let i = this.findClosest(circleCenter.x, circleCenter.y, LUTD, circleRadius, 5, LUTD[start - 2]?.distance, LUTD[start - 1]?.distance);
722
+ if (i < start)
723
+ break;
724
+ if (i > 0 && i == start)
725
+ break;
726
+ indices.push(i);
727
+ start = i + 2;
728
+ }
729
+ const finalList = [];
730
+ indices.forEach((index) => {
731
+ let res = this.refineBinary(this, circleCenter.x, circleCenter.y, LUTD, index, circleRadius);
732
+ if (res != null) {
733
+ finalList.push({ intersection: res.point, tVal: res.tVal });
734
+ }
735
+ });
736
+ return finalList;
737
+ }
738
+ advanceAtTWithLength(tVal, length) {
739
+ const currentLength = this.lengthAtT(tVal);
740
+ const targetLength = currentLength + length;
741
+ if (tVal === 0 && length < 0) {
742
+ return { type: "beforeCurve", remainLength: -length };
743
+ }
744
+ if (tVal === 1 && length > 0) {
745
+ return { type: "afterCurve", remainLength: length };
746
+ }
747
+ if (targetLength > this.fullLength) {
748
+ return {
749
+ type: "afterCurve",
750
+ remainLength: targetLength - this.fullLength
751
+ };
752
+ } else if (targetLength < 0) {
753
+ return { type: "beforeCurve", remainLength: -targetLength };
754
+ }
755
+ if (this.arcLengthLUT.arcLengthLUT.length === 0) {
756
+ this.arcLengthLUT = this.getArcLengthLUT(1000);
757
+ }
758
+ const points = this.arcLengthLUT.arcLengthLUT;
759
+ let low = 0;
760
+ let high = points.length - 1;
761
+ while (low <= high) {
762
+ const mid = Math.floor((low + high) / 2);
763
+ const midLength = points[mid].length;
764
+ if (approximately(midLength, targetLength, 0.000001)) {
765
+ const resultTVal = points[mid].tVal;
766
+ const point2 = this.get(resultTVal);
767
+ return { type: "withinCurve", tVal: resultTVal, point: point2 };
768
+ } else if (midLength < targetLength) {
769
+ low = mid + 1;
770
+ } else {
771
+ high = mid - 1;
772
+ }
773
+ }
774
+ if (high < 0) {
775
+ high = 1;
776
+ low = 0;
777
+ }
778
+ if (low >= points.length) {
779
+ high = points.length - 1;
780
+ low = points.length - 2;
781
+ }
782
+ const p1 = points[high];
783
+ const p2 = points[low];
784
+ const lengthRange = p2.length - p1.length;
785
+ const tRange = p2.tVal - p1.tVal;
786
+ if (lengthRange === 0) {
787
+ const point2 = this.get(p1.tVal);
788
+ return { type: "withinCurve", tVal: p1.tVal, point: point2 };
789
+ }
790
+ const ratio = (targetLength - p1.length) / lengthRange;
791
+ const interpolatedT = p1.tVal + ratio * tRange;
792
+ const clampedT = Math.max(0, Math.min(1, interpolatedT));
793
+ const point = this.get(clampedT);
794
+ return { type: "withinCurve", tVal: clampedT, point };
795
+ }
796
+ advanceByDistance(startT, distance) {
797
+ let currentT = startT;
798
+ let remainingDistance = distance;
799
+ const stepSize = 0.01;
800
+ if (distance > this.fullLength) {
801
+ return {
802
+ type: "afterCurve",
803
+ remainLength: distance - this.fullLength
804
+ };
805
+ } else if (distance < 0) {
806
+ return { type: "beforeCurve", remainLength: -distance };
807
+ }
808
+ while (remainingDistance > 0 && currentT < 1) {
809
+ const currentPoint = this.get(currentT);
810
+ const nextT = Math.min(currentT + stepSize, 1);
811
+ const nextPoint = this.get(nextT);
812
+ const segmentLength = Math.sqrt(Math.pow(nextPoint.x - currentPoint.x, 2) + Math.pow(nextPoint.y - currentPoint.y, 2));
813
+ if (segmentLength >= remainingDistance) {
814
+ const ratio = remainingDistance / segmentLength;
815
+ return {
816
+ type: "withinCurve",
817
+ tVal: currentT + ratio * (nextT - currentT),
818
+ point: this.get(currentT + ratio * (nextT - currentT))
819
+ };
820
+ }
821
+ remainingDistance -= segmentLength;
822
+ currentT = nextT;
823
+ }
824
+ return {
825
+ type: "withinCurve",
826
+ tVal: currentT,
827
+ point: this.get(currentT)
828
+ };
829
+ }
830
+ refineBinary(curve, x, y, LUT, i, targetDistance = 0, epsilon = 0.01) {
831
+ let q = LUT[i], count = 1, distance = Number.MAX_SAFE_INTEGER;
832
+ do {
833
+ let i1 = i === 0 ? 0 : i - 1, i2 = i === LUT.length - 1 ? LUT.length - 1 : i + 1, t1 = LUT[i1].tVal, t2 = LUT[i2].tVal, lut = [], step = (t2 - t1) / 4;
834
+ if (step < 0.001)
835
+ break;
836
+ lut.push(LUT[i1]);
837
+ for (let j = 1;j <= 3; j++) {
838
+ let n = curve.get(t1 + j * step);
839
+ let nDistance = Math.abs(PointCal.distanceBetweenPoints(n, { x, y }) - targetDistance);
840
+ if (nDistance < distance) {
841
+ distance = nDistance;
842
+ q = { point: n, tVal: t1 + j * step, distance: nDistance };
843
+ i = j;
844
+ }
845
+ lut.push({
846
+ point: n,
847
+ tVal: t1 + j * step,
848
+ distance: nDistance
849
+ });
850
+ }
851
+ lut.push(LUT[i2]);
852
+ LUT = lut;
853
+ } while (count++ < 25);
854
+ if (targetDistance && distance > epsilon) {
855
+ q = undefined;
856
+ }
857
+ return q;
858
+ }
859
+ findClosest(x, y, LUT, circleRadius, distanceEpsilon = 5, pd2, pd1) {
860
+ let distance = Number.MAX_SAFE_INTEGER, prevDistance2 = pd2 || distance, prevDistance1 = pd1 || distance, i = -1;
861
+ for (let index = 0, e = LUT.length;index < e; index++) {
862
+ let p = LUT[index].point;
863
+ LUT[index].distance = Math.abs(PointCal.distanceBetweenPoints({ x, y }, p) - circleRadius);
864
+ if (prevDistance1 < distanceEpsilon && prevDistance2 > prevDistance1 && prevDistance1 < LUT[index].distance) {
865
+ i = index - 1;
866
+ break;
867
+ }
868
+ if (LUT[index].distance < distance) {
869
+ distance = LUT[index].distance;
870
+ }
871
+ prevDistance2 = prevDistance1;
872
+ prevDistance1 = LUT[index].distance;
873
+ }
874
+ return i;
875
+ }
876
+ getCurveIntersections(curve, deduplicationTolerance) {
877
+ return getIntersectionsBetweenCurves(this, curve, deduplicationTolerance);
878
+ }
879
+ get AABB() {
880
+ const extrema = this.getExtrema();
881
+ const tVals = [0, 1];
882
+ let min = { x: Number.MAX_VALUE, y: Number.MAX_VALUE };
883
+ let max = { x: -Number.MAX_VALUE, y: -Number.MAX_VALUE };
884
+ extrema.x.forEach((tVal) => {
885
+ tVals.push(tVal);
886
+ });
887
+ extrema.y.forEach((tVal) => {
888
+ tVals.push(tVal);
889
+ });
890
+ tVals.forEach((tVal) => {
891
+ const curPoint = this.get(tVal);
892
+ min.x = Math.min(min.x, curPoint.x);
893
+ min.y = Math.min(min.y, curPoint.y);
894
+ max.x = Math.max(max.x, curPoint.x);
895
+ max.y = Math.max(max.y, curPoint.y);
896
+ });
897
+ return { min, max };
898
+ }
899
+ normal(tVal) {
900
+ const d = this.derivative(tVal);
901
+ const q = Math.sqrt(d.x * d.x + d.y * d.y);
902
+ return { tVal, direction: { x: -d.y / q, y: d.x / q } };
903
+ }
904
+ }
905
+ function reduce(curve) {
906
+ let i, t1 = 0, t2 = 0, step = 0.01, segment, pass1 = [], pass2 = [];
907
+ let extrema = curve.getExtrema().x;
908
+ if (extrema.indexOf(0) === -1) {
909
+ extrema = [0].concat(extrema);
910
+ }
911
+ if (extrema.indexOf(1) === -1) {
912
+ extrema.push(1);
913
+ }
914
+ for (t1 = extrema[0], i = 1;i < extrema.length; i++) {
915
+ t2 = extrema[i];
916
+ segment = curve.splitAndTakeMidCurve(t1, t2);
917
+ pass1.push(segment);
918
+ t1 = t2;
919
+ }
920
+ pass1.forEach((p1) => {
921
+ t1 = 0;
922
+ t2 = 0;
923
+ while (t2 <= 1) {
924
+ for (t2 = t1 + step;t2 <= 1 + step; t2 += step) {
925
+ const clampedT2 = Math.min(t2, 1);
926
+ segment = p1.splitAndTakeMidCurve(t1, clampedT2);
927
+ if (!curveIsSimple(segment)) {
928
+ t2 -= step;
929
+ if (Math.abs(t1 - t2) < step) {
930
+ return [];
931
+ }
932
+ const finalT2 = Math.min(t2, 1);
933
+ segment = p1.splitAndTakeMidCurve(t1, finalT2);
934
+ pass2.push(segment);
935
+ t1 = finalT2;
936
+ break;
937
+ }
938
+ }
939
+ }
940
+ if (t1 < 1) {
941
+ segment = p1.splitAndTakeMidCurve(t1, 1);
942
+ pass2.push(segment);
943
+ }
944
+ });
945
+ return pass2;
946
+ }
947
+ function raiseCurveOrder(curve) {
948
+ const p = curve.getControlPoints(), np = [p[0]], k = p.length;
949
+ for (let i = 1;i < k; i++) {
950
+ const pi = p[i];
951
+ const pim = p[i - 1];
952
+ np[i] = {
953
+ x: (k - i) / k * pi.x + i / k * pim.x,
954
+ y: (k - i) / k * pi.y + i / k * pim.y
955
+ };
956
+ }
957
+ np[k] = p[k - 1];
958
+ return new BCurve(np);
959
+ }
960
+ function offset(curve, t, d) {
961
+ if (d !== undefined) {
962
+ const c = curve.get(t), n = curve.normal(t).direction;
963
+ const ret = {
964
+ c,
965
+ n,
966
+ x: c.x + n.x * d,
967
+ y: c.y + n.y * d
968
+ };
969
+ return ret;
970
+ }
971
+ const points = curve.getControlPoints();
972
+ const linear = curveIsLinear(curve);
973
+ if (linear) {
974
+ const nv = curve.normal(0).direction, coords = points.map(function(p) {
975
+ const ret = {
976
+ x: p.x + t * nv.x,
977
+ y: p.y + t * nv.y
978
+ };
979
+ return ret;
980
+ });
981
+ return [new BCurve(coords)];
982
+ }
983
+ return reduce(curve).map(function(s) {
984
+ if (curveIsLinear(s)) {
985
+ return offset(s, t)[0];
986
+ }
987
+ return scaleCurve(s, t);
988
+ });
989
+ }
990
+ function offset2(curve, d) {
991
+ const lut = curve.getLUTWithTVal(100);
992
+ const res = lut.map((item) => {
993
+ const derivative = PointCal.unitVector(curve.derivative(item.tVal));
994
+ const normal = { x: -derivative.y, y: derivative.x };
995
+ const offsetPoint = {
996
+ x: item.point.x + normal.x * d,
997
+ y: item.point.y + normal.y * d
998
+ };
999
+ return offsetPoint;
1000
+ });
1001
+ const aabb = {
1002
+ min: { x: res[0].x, y: res[0].y },
1003
+ max: { x: res[0].x, y: res[0].y }
1004
+ };
1005
+ res.forEach((offsetPoint) => {
1006
+ if (offsetPoint.x < aabb.min.x) {
1007
+ aabb.min.x = offsetPoint.x;
1008
+ }
1009
+ if (offsetPoint.x > aabb.max.x) {
1010
+ aabb.max.x = offsetPoint.x;
1011
+ }
1012
+ if (offsetPoint.y < aabb.min.y) {
1013
+ aabb.min.y = offsetPoint.y;
1014
+ }
1015
+ if (offsetPoint.y > aabb.max.y) {
1016
+ aabb.max.y = offsetPoint.y;
1017
+ }
1018
+ });
1019
+ return { points: res, aabb };
1020
+ }
1021
+ function lli4(p1, p2, p3, p4) {
1022
+ const { x: x1, y: y1 } = p1, x2 = p2.x, y2 = p2.y, x3 = p3.x, y3 = p3.y, x4 = p4.x, y4 = p4.y;
1023
+ return lli8(x1, y1, x2, y2, x3, y3, x4, y4);
1024
+ }
1025
+ function lli8(x1, y1, x2, y2, x3, y3, x4, y4) {
1026
+ const nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4);
1027
+ const ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4);
1028
+ const d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
1029
+ if (d == 0) {
1030
+ return false;
1031
+ }
1032
+ return { x: nx / d, y: ny / d };
1033
+ }
1034
+ function curveIsLinear(curve) {
1035
+ const order = curve.getControlPoints().length - 1;
1036
+ const points = curve.getControlPoints();
1037
+ const alignedPoints = alignPointsToLine(points, {
1038
+ p1: points[0],
1039
+ p2: points[order]
1040
+ });
1041
+ const baseLength = PointCal.distanceBetweenPoints(points[0], points[order]);
1042
+ const linear = alignedPoints.reduce((t, p) => t + Math.abs(p.y), 0) < baseLength / 50;
1043
+ return linear;
1044
+ }
1045
+ function scaleCurve(curve, d) {
1046
+ const order = curve.getControlPoints().length - 1;
1047
+ let distanceFn = undefined;
1048
+ if (typeof d === "function") {
1049
+ distanceFn = d;
1050
+ }
1051
+ if (distanceFn && order === 2) {
1052
+ return scaleCurve(raiseCurveOrder(curve), distanceFn);
1053
+ }
1054
+ const points = curve.getControlPoints();
1055
+ if (curveIsLinear(curve)) {
1056
+ return translate(curve, curve.normal(0).direction, distanceFn ? distanceFn(0) : d, distanceFn ? distanceFn(1) : d);
1057
+ }
1058
+ const r1 = distanceFn ? distanceFn(0) : d;
1059
+ const r2 = distanceFn ? distanceFn(1) : d;
1060
+ const v = [offset(curve, 0, 10), offset(curve, 1, 10)];
1061
+ const np = [];
1062
+ const o = lli4(v[0], v[0].c, v[1], v[1].c);
1063
+ if (!o) {
1064
+ return translate(curve, curve.normal(0).direction, r1, r2);
1065
+ }
1066
+ [0, 1].forEach(function(t) {
1067
+ const p = JSON.parse(JSON.stringify(points[t * order]));
1068
+ const vt = v[t];
1069
+ p.x += (t ? r2 : r1) * vt.n.x;
1070
+ p.y += (t ? r2 : r1) * vt.n.y;
1071
+ np[t * order] = p;
1072
+ });
1073
+ if (!distanceFn) {
1074
+ [0, 1].forEach((t) => {
1075
+ if (order === 2 && !!t)
1076
+ return;
1077
+ const p = np[t * order];
1078
+ const derivativeAtT = curve.derivative(t);
1079
+ const p2 = { x: p.x + derivativeAtT.x, y: p.y + derivativeAtT.y };
1080
+ const intersection = lli4(p, p2, o, points[t + 1]);
1081
+ if (intersection) {
1082
+ np[t + 1] = intersection;
1083
+ } else {
1084
+ const originalPoint = points[t + 1];
1085
+ const normal = curve.normal((t + 1) / order).direction;
1086
+ np[t + 1] = {
1087
+ x: originalPoint.x + (t ? r2 : r1) * normal.x,
1088
+ y: originalPoint.y + (t ? r2 : r1) * normal.y
1089
+ };
1090
+ }
1091
+ });
1092
+ return new BCurve(np);
1093
+ }
1094
+ [0, 1].forEach(function(t) {
1095
+ if (order === 2 && !!t)
1096
+ return;
1097
+ const p = points[t + 1];
1098
+ const ov = {
1099
+ x: p.x - o.x,
1100
+ y: p.y - o.y
1101
+ };
1102
+ let rc = distanceFn((t + 1) / order);
1103
+ const m = Math.sqrt(ov.x * ov.x + ov.y * ov.y);
1104
+ ov.x /= m;
1105
+ ov.y /= m;
1106
+ np[t + 1] = {
1107
+ x: p.x + rc * ov.x,
1108
+ y: p.y + rc * ov.y
1109
+ };
1110
+ });
1111
+ return new BCurve(np);
1112
+ }
1113
+ function alignPointsToLine(points, line) {
1114
+ const tx = line.p1.x, ty = line.p1.y, a = -Math.atan2(line.p2.y - ty, line.p2.x - tx), d = function(v) {
1115
+ return {
1116
+ x: (v.x - tx) * Math.cos(a) - (v.y - ty) * Math.sin(a),
1117
+ y: (v.x - tx) * Math.sin(a) + (v.y - ty) * Math.cos(a)
1118
+ };
1119
+ };
1120
+ return points.map(d);
1121
+ }
1122
+ function map(v, ds, de, ts, te) {
1123
+ const d1 = de - ds, d2 = te - ts, v2 = v - ds, r = v2 / d1;
1124
+ return ts + d2 * r;
1125
+ }
1126
+
1127
+ class TValOutofBoundError extends Error {
1128
+ constructor(message) {
1129
+ super(message);
1130
+ }
1131
+ }
1132
+ function AABBIntersects(AABB1, AABB2) {
1133
+ if (AABB1.min.x <= AABB2.max.x && AABB2.min.x <= AABB1.max.x && AABB1.min.y <= AABB2.max.y && AABB2.min.y <= AABB1.max.y) {
1134
+ return true;
1135
+ }
1136
+ return false;
1137
+ }
1138
+ function approximately(a, b, precision) {
1139
+ const epsilon = 0.000001;
1140
+ return Math.abs(a - b) <= (precision || epsilon);
1141
+ }
1142
+ function cuberoot2(v) {
1143
+ if (v < 0)
1144
+ return -Math.pow(-v, 1 / 3);
1145
+ return Math.pow(v, 1 / 3);
1146
+ }
1147
+ function accept(t) {
1148
+ return 0 <= t && t <= 1;
1149
+ }
1150
+ function getCubicRoots(pa, pb, pc, pd) {
1151
+ let a = 3 * pa - 6 * pb + 3 * pc, b = -3 * pa + 3 * pb, c = pa, d = -pa + 3 * pb - 3 * pc + pd;
1152
+ if (approximately(d, 0)) {
1153
+ if (approximately(a, 0)) {
1154
+ if (approximately(b, 0)) {
1155
+ return [];
1156
+ }
1157
+ return [-c / b].filter(accept);
1158
+ }
1159
+ let q3 = Math.sqrt(b * b - 4 * a * c), a2 = 2 * a;
1160
+ return [(q3 - b) / a2, (-b - q3) / a2].filter(accept);
1161
+ }
1162
+ a /= d;
1163
+ b /= d;
1164
+ c /= d;
1165
+ let p = (3 * b - a * a) / 3, p3 = p / 3, q = (2 * a * a * a - 9 * a * b + 27 * c) / 27, q2 = q / 2, discriminant = q2 * q2 + p3 * p3 * p3;
1166
+ let u1, v1, root1, root2, root3;
1167
+ if (discriminant < 0) {
1168
+ let mp3 = -p / 3, mp33 = mp3 * mp3 * mp3, r = Math.sqrt(mp33), t = -q / (2 * r), cosphi = t < -1 ? -1 : t > 1 ? 1 : t, phi = Math.acos(cosphi), crtr = cuberoot2(r), t1 = 2 * crtr;
1169
+ root1 = t1 * Math.cos(phi / 3) - a / 3;
1170
+ root2 = t1 * Math.cos((phi + 2 * Math.PI) / 3) - a / 3;
1171
+ root3 = t1 * Math.cos((phi + 4 * Math.PI) / 3) - a / 3;
1172
+ return [root1, root2, root3].filter(accept);
1173
+ }
1174
+ if (discriminant === 0) {
1175
+ u1 = q2 < 0 ? cuberoot2(-q2) : -cuberoot2(q2);
1176
+ root1 = 2 * u1 - a / 3;
1177
+ root2 = -u1 - a / 3;
1178
+ return [root1, root2].filter(accept);
1179
+ }
1180
+ var sd = Math.sqrt(discriminant);
1181
+ u1 = cuberoot2(sd - q2);
1182
+ v1 = cuberoot2(sd + q2);
1183
+ root1 = u1 - v1 - a / 3;
1184
+ return [root1].filter(accept);
1185
+ }
1186
+ function getIntersectionsBetweenCurves(curve, curve2, deduplicationTolerance = 0.01) {
1187
+ const threshold = 0.5;
1188
+ let pairs = [
1189
+ {
1190
+ curve1: { curve, startTVal: 0, endTVal: 1 },
1191
+ curve2: { curve: curve2, startTVal: 0, endTVal: 1 }
1192
+ }
1193
+ ];
1194
+ const finalRes = [];
1195
+ while (pairs.length > 0) {
1196
+ let curLength = pairs.length;
1197
+ for (let index = 0;index < curLength; index++) {
1198
+ let pair = pairs.shift();
1199
+ if (pair == undefined) {
1200
+ break;
1201
+ }
1202
+ let aabb1 = pair.curve1.curve.AABB;
1203
+ let aabb2 = pair.curve2.curve.AABB;
1204
+ let intersects = AABBIntersects(aabb1, aabb2);
1205
+ if (pair.curve1.curve.fullLength < threshold && pair.curve2.curve.fullLength < threshold) {
1206
+ finalRes.push({
1207
+ intersection: pair.curve1.curve.get(0.5),
1208
+ tVal1: (pair.curve1.startTVal + pair.curve1.endTVal) * 0.5,
1209
+ tVal2: (pair.curve2.startTVal + pair.curve2.endTVal) * 0.5
1210
+ });
1211
+ continue;
1212
+ }
1213
+ if (intersects) {
1214
+ let [subCurveControlPoints1, subCurveControlPoints2] = pair.curve1.curve.split(0.5);
1215
+ let [subCurveControlPoints3, subCurveControlPoints4] = pair.curve2.curve.split(0.5);
1216
+ pairs.push({
1217
+ curve1: {
1218
+ curve: new BCurve(subCurveControlPoints1),
1219
+ startTVal: pair.curve1.startTVal,
1220
+ endTVal: pair.curve1.startTVal + (pair.curve1.endTVal - pair.curve1.startTVal) * 0.5
1221
+ },
1222
+ curve2: {
1223
+ curve: new BCurve(subCurveControlPoints3),
1224
+ startTVal: pair.curve2.startTVal,
1225
+ endTVal: pair.curve2.startTVal + (pair.curve2.endTVal - pair.curve2.startTVal) * 0.5
1226
+ }
1227
+ });
1228
+ pairs.push({
1229
+ curve1: {
1230
+ curve: new BCurve(subCurveControlPoints1),
1231
+ startTVal: pair.curve1.startTVal,
1232
+ endTVal: pair.curve1.startTVal + (pair.curve1.endTVal - pair.curve1.startTVal) * 0.5
1233
+ },
1234
+ curve2: {
1235
+ curve: new BCurve(subCurveControlPoints4),
1236
+ startTVal: pair.curve2.startTVal + (pair.curve2.endTVal - pair.curve2.startTVal) * 0.5,
1237
+ endTVal: pair.curve2.endTVal
1238
+ }
1239
+ });
1240
+ pairs.push({
1241
+ curve1: {
1242
+ curve: new BCurve(subCurveControlPoints2),
1243
+ startTVal: pair.curve1.startTVal + (pair.curve1.endTVal - pair.curve1.startTVal) * 0.5,
1244
+ endTVal: pair.curve1.endTVal
1245
+ },
1246
+ curve2: {
1247
+ curve: new BCurve(subCurveControlPoints3),
1248
+ startTVal: pair.curve2.startTVal,
1249
+ endTVal: pair.curve2.startTVal + (pair.curve2.endTVal - pair.curve2.startTVal) * 0.5
1250
+ }
1251
+ });
1252
+ pairs.push({
1253
+ curve1: {
1254
+ curve: new BCurve(subCurveControlPoints2),
1255
+ startTVal: pair.curve1.startTVal + (pair.curve1.endTVal - pair.curve1.startTVal) * 0.5,
1256
+ endTVal: pair.curve1.endTVal
1257
+ },
1258
+ curve2: {
1259
+ curve: new BCurve(subCurveControlPoints4),
1260
+ startTVal: pair.curve2.startTVal + (pair.curve2.endTVal - pair.curve2.startTVal) * 0.5,
1261
+ endTVal: pair.curve2.endTVal
1262
+ }
1263
+ });
1264
+ }
1265
+ }
1266
+ }
1267
+ const tVals = [];
1268
+ finalRes.sort((a, b) => a.tVal1 - b.tVal1);
1269
+ for (const intersection of finalRes) {
1270
+ let isDuplicate = false;
1271
+ for (const existing of tVals) {
1272
+ const selfTClose = approximately(intersection.tVal1, existing.selfT, deduplicationTolerance);
1273
+ const otherTClose = approximately(intersection.tVal2, existing.otherT, deduplicationTolerance);
1274
+ if (selfTClose && otherTClose) {
1275
+ isDuplicate = true;
1276
+ break;
1277
+ }
1278
+ const selfTVeryClose = approximately(intersection.tVal1, existing.selfT, deduplicationTolerance * 10);
1279
+ const otherTVeryClose = approximately(intersection.tVal2, existing.otherT, deduplicationTolerance * 10);
1280
+ if (selfTVeryClose || otherTVeryClose) {
1281
+ const point1 = curve.get(intersection.tVal1);
1282
+ const point2 = curve2.get(intersection.tVal2);
1283
+ const existingPoint1 = curve.get(existing.selfT);
1284
+ const existingPoint2 = curve2.get(existing.otherT);
1285
+ const distance1 = PointCal.distanceBetweenPoints(point1, existingPoint1);
1286
+ const distance2 = PointCal.distanceBetweenPoints(point2, existingPoint2);
1287
+ if (distance1 < deduplicationTolerance * 100 && distance2 < deduplicationTolerance * 100) {
1288
+ isDuplicate = true;
1289
+ break;
1290
+ }
1291
+ }
1292
+ }
1293
+ if (!isDuplicate) {
1294
+ tVals.push({
1295
+ selfT: intersection.tVal1,
1296
+ otherT: intersection.tVal2
1297
+ });
1298
+ }
1299
+ }
1300
+ return tVals;
1301
+ }
1302
+ function solveCubic(a, b, c, d) {
1303
+ if (Math.abs(a) < 0.00000001) {
1304
+ a = b;
1305
+ b = c;
1306
+ c = d;
1307
+ if (Math.abs(a) < 0.00000001) {
1308
+ a = b;
1309
+ b = c;
1310
+ if (Math.abs(a) < 0.00000001)
1311
+ return [];
1312
+ return [-b / a];
1313
+ }
1314
+ let D = b * b - 4 * a * c;
1315
+ if (Math.abs(D) < 0.00000001)
1316
+ return [-b / (2 * a)];
1317
+ else if (D > 0)
1318
+ return [
1319
+ (-b + Math.sqrt(D)) / (2 * a),
1320
+ (-b - Math.sqrt(D)) / (2 * a)
1321
+ ];
1322
+ return [];
1323
+ }
1324
+ let p = (3 * a * c - b * b) / (3 * a * a);
1325
+ let q = (2 * b * b * b - 9 * a * b * c + 27 * a * a * d) / (27 * a * a * a);
1326
+ let roots;
1327
+ if (Math.abs(p) < 0.00000001) {
1328
+ roots = [cuberoot(-q)];
1329
+ } else if (Math.abs(q) < 0.00000001) {
1330
+ roots = [0].concat(p < 0 ? [Math.sqrt(-p), -Math.sqrt(-p)] : []);
1331
+ } else {
1332
+ let D = q * q / 4 + p * p * p / 27;
1333
+ if (Math.abs(D) < 0.00000001) {
1334
+ roots = [-1.5 * q / p, 3 * q / p];
1335
+ } else if (D > 0) {
1336
+ let u = cuberoot(-q / 2 - Math.sqrt(D));
1337
+ let v = cuberoot(-q / 2 + Math.sqrt(D));
1338
+ roots = [u - p / (3 * u)];
1339
+ } else {
1340
+ let u = 2 * Math.sqrt(-p / 3);
1341
+ let t = Math.acos(3 * q / p / u) / 3;
1342
+ let k = 2 * Math.PI / 3;
1343
+ roots = [
1344
+ u * Math.cos(t),
1345
+ u * Math.cos(t - k),
1346
+ u * Math.cos(t - 2 * k)
1347
+ ];
1348
+ }
1349
+ }
1350
+ for (let i = 0;i < roots.length; i++)
1351
+ roots[i] -= b / (3 * a);
1352
+ return roots;
1353
+ }
1354
+ function cuberoot(x) {
1355
+ var y = Math.pow(Math.abs(x), 1 / 3);
1356
+ return x < 0 ? -y : y;
1357
+ }
1358
+ function computeWithControlPoints(tVal, controlPoints) {
1359
+ let points = [...controlPoints];
1360
+ while (points.length > 1) {
1361
+ let lowerLevelPoints = points.slice(1);
1362
+ for (let index = 0;index < lowerLevelPoints.length; index++) {
1363
+ lowerLevelPoints[index] = PointCal.addVector(PointCal.multiplyVectorByScalar(points[index], 1 - tVal), PointCal.multiplyVectorByScalar(points[index + 1], tVal));
1364
+ }
1365
+ points = lowerLevelPoints;
1366
+ }
1367
+ return points[0];
1368
+ }
1369
+ function curveIsSimple(curve) {
1370
+ if (curve.getControlPoints().length === 4) {
1371
+ const points = curve.getControlPoints();
1372
+ const p0ToP3Vector = PointCal.subVector(points[3], points[0]);
1373
+ const p0ToP1Vector = PointCal.subVector(points[1], points[0]);
1374
+ const p0ToP2Vector = PointCal.subVector(points[2], points[0]);
1375
+ const a1 = PointCal.angleFromA2B(p0ToP3Vector, p0ToP1Vector);
1376
+ const a2 = PointCal.angleFromA2B(p0ToP3Vector, p0ToP2Vector);
1377
+ if (a1 > 0 && a2 < 0 || a1 < 0 && a2 > 0)
1378
+ return false;
1379
+ }
1380
+ const n1 = curve.normal(0).direction;
1381
+ const n2 = curve.normal(1).direction;
1382
+ let s = n1.x * n2.x + n1.y * n2.y;
1383
+ return Math.abs(Math.acos(s)) < Math.PI / 3;
1384
+ }
1385
+ function translate(curve, vector, d1, d2) {
1386
+ const order = curve.getControlPoints().length - 1;
1387
+ const points = curve.getControlPoints();
1388
+ const d = points.map((_, i) => (1 - i / order) * d1 + i / order * d2);
1389
+ return new BCurve(points.map((p, i) => ({
1390
+ x: p.x + d[i] * vector.x,
1391
+ y: p.y + d[i] * vector.y
1392
+ })));
1393
+ }
1394
+ // src/line.ts
1395
+ import { PointCal as PointCal2 } from "@ue-too/math";
1396
+
1397
+ class Line {
1398
+ startPoint;
1399
+ endPoint;
1400
+ constructor(startPoint, endPoint) {
1401
+ this.startPoint = startPoint;
1402
+ this.endPoint = endPoint;
1403
+ }
1404
+ getStartPoint() {
1405
+ return this.startPoint;
1406
+ }
1407
+ getEndPoint() {
1408
+ return this.endPoint;
1409
+ }
1410
+ intersectionWithAnotherLine(lineToIntersect) {
1411
+ return getLineIntersection(this.startPoint, this.endPoint, lineToIntersect.getStartPoint(), lineToIntersect.getEndPoint());
1412
+ }
1413
+ projectPoint(point) {
1414
+ return projectPointOntoLine(point, this.getStartPoint(), this.getEndPoint());
1415
+ }
1416
+ length() {
1417
+ return PointCal2.distanceBetweenPoints(this.startPoint, this.endPoint);
1418
+ }
1419
+ getTranslationRotationToAlginXAxis() {
1420
+ const translation = PointCal2.subVector({ x: 0, y: 0 }, this.startPoint);
1421
+ const rotationAngle = PointCal2.angleFromA2B(PointCal2.subVector(this.endPoint, this.startPoint), { x: 1, y: 0 });
1422
+ return { translation, rotationAngle };
1423
+ }
1424
+ pointInLine(point) {
1425
+ const baseVector = PointCal2.unitVectorFromA2B(this.startPoint, this.endPoint);
1426
+ const start2PointVector = PointCal2.subVector(point, this.startPoint);
1427
+ const length = PointCal2.dotProduct(start2PointVector, baseVector);
1428
+ const start2PointUnitVector = PointCal2.unitVector(start2PointVector);
1429
+ const errorThreshold = PointCal2.distanceBetweenPoints(this.startPoint, this.endPoint) * 0.0001;
1430
+ return length <= PointCal2.distanceBetweenPoints(this.startPoint, this.endPoint) && length >= 0 && Math.abs(start2PointUnitVector.x - baseVector.x) < 0.0001 && Math.abs(start2PointUnitVector.y - baseVector.y) < 0.0001;
1431
+ }
1432
+ lerp(ratio) {
1433
+ return PointCal2.linearInterpolation(this.startPoint, this.endPoint, ratio);
1434
+ }
1435
+ }
1436
+ function getLineIntersection(startPoint, endPoint, startPoint2, endPoint2) {
1437
+ const numerator = (endPoint2.x - startPoint2.x) * (startPoint.y - startPoint2.y) - (endPoint2.y - startPoint2.y) * (startPoint.x - startPoint2.x);
1438
+ const denominator = (endPoint2.y - startPoint2.y) * (endPoint.x - startPoint.x) - (endPoint2.x - startPoint2.x) * (endPoint.y - startPoint.y);
1439
+ if (denominator === 0) {
1440
+ return { intersects: false };
1441
+ }
1442
+ const t = numerator / denominator;
1443
+ if (t >= 0 && t <= 1) {
1444
+ return {
1445
+ intersects: true,
1446
+ intersection: PointCal2.linearInterpolation(startPoint, endPoint, t),
1447
+ offset: t
1448
+ };
1449
+ } else {
1450
+ return {
1451
+ intersects: false
1452
+ };
1453
+ }
1454
+ }
1455
+ function projectPointOntoLine(point, lineStartPoint, lineEndPoint) {
1456
+ const baseVector = PointCal2.unitVector(PointCal2.subVector(lineEndPoint, lineStartPoint));
1457
+ const vectorToPoint = PointCal2.subVector(point, lineStartPoint);
1458
+ const res = PointCal2.dotProduct(vectorToPoint, baseVector);
1459
+ if (res < 0 || res > PointCal2.magnitude(PointCal2.subVector(lineEndPoint, lineStartPoint))) {
1460
+ return {
1461
+ within: false
1462
+ };
1463
+ }
1464
+ return {
1465
+ within: true,
1466
+ projectionPoint: PointCal2.addVector(lineStartPoint, PointCal2.multiplyVectorByScalar(baseVector, res)),
1467
+ offset: res / PointCal2.magnitude(PointCal2.subVector(lineEndPoint, lineStartPoint))
1468
+ };
1469
+ }
1470
+ // src/composite-curve.ts
1471
+ import { PointCal as PointCal3 } from "@ue-too/math";
1472
+
1473
+ class ControlPoint {
1474
+ position;
1475
+ leftHandle;
1476
+ rightHandle;
1477
+ constructor(position, leftHandle, rightHandle) {
1478
+ this.position = position;
1479
+ this.leftHandle = leftHandle;
1480
+ this.rightHandle = rightHandle;
1481
+ }
1482
+ setPosition(destinationPosition, prevControlPoint, nextControlPoint) {
1483
+ let diff = PointCal3.subVector(destinationPosition, this.position);
1484
+ this.position = destinationPosition;
1485
+ this.leftHandle.position = PointCal3.addVector(this.leftHandle.position, diff);
1486
+ this.rightHandle.position = PointCal3.addVector(this.rightHandle.position, diff);
1487
+ if (this.leftHandle.type == "VECTOR" && prevControlPoint) {
1488
+ let relativeVector = PointCal3.subVector(prevControlPoint.getPosition(), this.position);
1489
+ relativeVector = PointCal3.multiplyVectorByScalar(relativeVector, 1 / 3);
1490
+ this.leftHandle.position = PointCal3.addVector(this.position, relativeVector);
1491
+ if (this.rightHandle.type == "ALIGNED") {
1492
+ let relativeVector2 = PointCal3.subVector(this.rightHandle.position, this.position);
1493
+ let mag = PointCal3.magnitude(relativeVector2);
1494
+ let direction = PointCal3.unitVectorFromA2B(this.leftHandle.position, this.position);
1495
+ this.rightHandle.position = PointCal3.addVector(this.position, PointCal3.multiplyVectorByScalar(direction, mag));
1496
+ }
1497
+ }
1498
+ if (this.rightHandle.type == "VECTOR" && nextControlPoint) {
1499
+ let relativeVector = PointCal3.subVector(nextControlPoint.getPosition(), this.position);
1500
+ relativeVector = PointCal3.multiplyVectorByScalar(relativeVector, 1 / 3);
1501
+ this.rightHandle.position = PointCal3.addVector(this.position, relativeVector);
1502
+ if (this.leftHandle.type == "ALIGNED") {
1503
+ let mag = PointCal3.distanceBetweenPoints(this.leftHandle.position, this.position);
1504
+ let direction = PointCal3.subVector(this.position, this.rightHandle.position);
1505
+ this.leftHandle.position = PointCal3.addVector(this.position, PointCal3.multiplyVectorByScalar(direction, mag));
1506
+ }
1507
+ }
1508
+ if (prevControlPoint !== undefined && prevControlPoint.getRightHandle().type == "VECTOR") {
1509
+ let relativeVector = PointCal3.subVector(this.position, prevControlPoint.getPosition());
1510
+ relativeVector = PointCal3.multiplyVectorByScalar(relativeVector, 1 / 3);
1511
+ prevControlPoint.setRightHandlePosition(PointCal3.addVector(prevControlPoint.getPosition(), relativeVector));
1512
+ }
1513
+ if (nextControlPoint !== undefined && nextControlPoint.getLeftHandle().type == "VECTOR") {
1514
+ let relativeVector = PointCal3.subVector(this.position, nextControlPoint.getPosition());
1515
+ relativeVector = PointCal3.multiplyVectorByScalar(relativeVector, 1 / 3);
1516
+ nextControlPoint.setLeftHandlePosition(PointCal3.addVector(nextControlPoint.getPosition(), relativeVector));
1517
+ }
1518
+ }
1519
+ getPosition() {
1520
+ return this.position;
1521
+ }
1522
+ setLeftHandleTypeVector(prevControlPoint) {
1523
+ if (this.rightHandle.type != "VECTOR") {
1524
+ this.rightHandle.type = "FREE";
1525
+ }
1526
+ let relativeVector;
1527
+ if (prevControlPoint == undefined) {
1528
+ relativeVector = PointCal3.subVector(this.leftHandle.position, this.position);
1529
+ } else {
1530
+ relativeVector = PointCal3.subVector(prevControlPoint.getPosition(), this.position);
1531
+ relativeVector = PointCal3.multiplyVectorByScalar(relativeVector, 1 / 3);
1532
+ }
1533
+ this.leftHandle.position = PointCal3.addVector(this.position, relativeVector);
1534
+ }
1535
+ setLeftHandleTypeAligned() {
1536
+ this.leftHandle.type = "ALIGNED";
1537
+ if (this.rightHandle.type == "VECTOR") {
1538
+ let direction = PointCal3.unitVectorFromA2B(this.rightHandle.position, this.position);
1539
+ let mag = PointCal3.distanceBetweenPoints(this.position, this.leftHandle.position);
1540
+ this.leftHandle.position = PointCal3.addVector(this.position, PointCal3.multiplyVectorByScalar(direction, mag));
1541
+ }
1542
+ }
1543
+ setLeftHandleTypeFree() {
1544
+ this.leftHandle.type = "FREE";
1545
+ }
1546
+ setRightHandleTypeVector(nextControlPoint) {
1547
+ if (this.leftHandle.type != "VECTOR") {
1548
+ this.leftHandle.type = "FREE";
1549
+ }
1550
+ let relativeVector;
1551
+ if (nextControlPoint == undefined) {
1552
+ relativeVector = PointCal3.subVector(this.rightHandle.position, this.position);
1553
+ } else {
1554
+ relativeVector = PointCal3.subVector(nextControlPoint.getPosition(), this.position);
1555
+ relativeVector = PointCal3.multiplyVectorByScalar(relativeVector, 1 / 3);
1556
+ }
1557
+ this.rightHandle.position = PointCal3.addVector(this.position, relativeVector);
1558
+ }
1559
+ setRightHandleTypeAligned() {
1560
+ this.rightHandle.type = "ALIGNED";
1561
+ if (this.leftHandle.type == "VECTOR") {
1562
+ let direciton = PointCal3.unitVectorFromA2B(this.leftHandle.position, this.position);
1563
+ let mag = PointCal3.distanceBetweenPoints(this.position, this.rightHandle.position);
1564
+ this.rightHandle.position = PointCal3.addVector(this.position, PointCal3.multiplyVectorByScalar(direciton, mag));
1565
+ }
1566
+ }
1567
+ setRightHandleTypeFree() {
1568
+ this.rightHandle.type = "FREE";
1569
+ }
1570
+ setLeftHandlePosition(destPos) {
1571
+ let leftHandleType = this.leftHandle.type;
1572
+ switch (leftHandleType) {
1573
+ case "ALIGNED":
1574
+ if (this.rightHandle.type == "VECTOR") {
1575
+ let diff = PointCal3.subVector(destPos, this.position);
1576
+ let rightHandleDiff = PointCal3.unitVectorFromA2B(this.rightHandle.position, this.position);
1577
+ let resMag = PointCal3.dotProduct(diff, rightHandleDiff);
1578
+ let res = PointCal3.multiplyVectorByScalar(rightHandleDiff, resMag);
1579
+ this.leftHandle.position = PointCal3.addVector(this.position, res);
1580
+ } else if (this.rightHandle.type == "ALIGNED") {
1581
+ this.leftHandle.position = destPos;
1582
+ let mag = PointCal3.distanceBetweenPoints(this.rightHandle.position, this.position);
1583
+ let direction = PointCal3.unitVectorFromA2B(this.leftHandle.position, this.position);
1584
+ let res = PointCal3.multiplyVectorByScalar(direction, mag);
1585
+ this.rightHandle.position = PointCal3.addVector(res, this.position);
1586
+ } else {
1587
+ this.leftHandle.position = destPos;
1588
+ }
1589
+ break;
1590
+ case "FREE":
1591
+ this.leftHandle.position = destPos;
1592
+ break;
1593
+ case "VECTOR":
1594
+ break;
1595
+ default:
1596
+ throw new Error(`Unknown left handle type for control point`);
1597
+ }
1598
+ }
1599
+ setRightHandlePosition(destPos) {
1600
+ let rightHandleType = this.rightHandle.type;
1601
+ switch (rightHandleType) {
1602
+ case "ALIGNED":
1603
+ if (this.leftHandle.type == "VECTOR") {
1604
+ let diff = PointCal3.subVector(destPos, this.position);
1605
+ let leftHandleDiff = PointCal3.unitVectorFromA2B(this.leftHandle.position, this.position);
1606
+ let resMag = PointCal3.dotProduct(diff, leftHandleDiff);
1607
+ let res = PointCal3.multiplyVectorByScalar(leftHandleDiff, resMag);
1608
+ this.rightHandle.position = PointCal3.addVector(this.position, res);
1609
+ } else if (this.rightHandle.type == "ALIGNED") {
1610
+ this.rightHandle.position = destPos;
1611
+ let mag = PointCal3.distanceBetweenPoints(this.leftHandle.position, this.position);
1612
+ let direction = PointCal3.unitVectorFromA2B(this.rightHandle.position, this.position);
1613
+ let res = PointCal3.multiplyVectorByScalar(direction, mag);
1614
+ this.leftHandle.position = PointCal3.addVector(res, this.position);
1615
+ } else {
1616
+ this.rightHandle.position = destPos;
1617
+ }
1618
+ break;
1619
+ case "FREE":
1620
+ this.rightHandle.position = destPos;
1621
+ break;
1622
+ case "VECTOR":
1623
+ break;
1624
+ default:
1625
+ throw new Error(`Unknown left handle type for control point`);
1626
+ }
1627
+ }
1628
+ getLeftHandle() {
1629
+ return this.leftHandle;
1630
+ }
1631
+ getRightHandle() {
1632
+ return this.rightHandle;
1633
+ }
1634
+ }
1635
+
1636
+ class CompositeBCurve {
1637
+ controlPoints;
1638
+ constructor(controlPoints = []) {
1639
+ this.controlPoints = controlPoints;
1640
+ }
1641
+ getControlPoints() {
1642
+ return this.controlPoints;
1643
+ }
1644
+ appendControlPoint(position) {
1645
+ let leftHandlePosition = PointCal3.addVector(position, {
1646
+ x: -100,
1647
+ y: 0
1648
+ });
1649
+ let rightHandlePosition = PointCal3.addVector(position, {
1650
+ x: 100,
1651
+ y: 0
1652
+ });
1653
+ let leftHandlePoint = {
1654
+ position: leftHandlePosition,
1655
+ type: "FREE"
1656
+ };
1657
+ let rightHandlePoint = {
1658
+ position: rightHandlePosition,
1659
+ type: "FREE"
1660
+ };
1661
+ let newControlPoint = new ControlPoint(position, leftHandlePoint, rightHandlePoint);
1662
+ this.controlPoints.push(newControlPoint);
1663
+ }
1664
+ setLeftHandlePositionOfControlPoint(controlPointIndex, destPos) {
1665
+ if (controlPointIndex >= this.controlPoints.length || controlPointIndex < 0) {
1666
+ return;
1667
+ }
1668
+ this.controlPoints[controlPointIndex].setLeftHandlePosition(destPos);
1669
+ }
1670
+ setRightHandlePositionOfControlPoint(controlPointIndex, destPos) {
1671
+ if (controlPointIndex >= this.controlPoints.length || controlPointIndex < 0) {
1672
+ return;
1673
+ }
1674
+ this.controlPoints[controlPointIndex].setRightHandlePosition(destPos);
1675
+ }
1676
+ setPositionOfControlPoint(controlPointIndex, destPos) {
1677
+ if (controlPointIndex >= this.controlPoints.length || controlPointIndex < 0) {
1678
+ return;
1679
+ }
1680
+ let prevControlPoint = undefined;
1681
+ let nextControlPoint = undefined;
1682
+ if (controlPointIndex + 1 < this.controlPoints.length) {
1683
+ nextControlPoint = this.controlPoints[controlPointIndex + 1];
1684
+ }
1685
+ if (controlPointIndex - 1 >= 0) {
1686
+ prevControlPoint = this.controlPoints[controlPointIndex - 1];
1687
+ }
1688
+ this.controlPoints[controlPointIndex].setPosition(destPos, prevControlPoint, nextControlPoint);
1689
+ }
1690
+ }
1691
+ // src/path.ts
1692
+ class Path {
1693
+ lines;
1694
+ constructor(lines) {
1695
+ this.lines = lines;
1696
+ }
1697
+ append(line) {
1698
+ this.lines.push(line);
1699
+ }
1700
+ clear() {
1701
+ this.lines = [];
1702
+ }
1703
+ prepend(line) {
1704
+ this.lines.unshift(line);
1705
+ }
1706
+ getLines() {
1707
+ return this.lines;
1708
+ }
1709
+ getLength() {
1710
+ let res = 0;
1711
+ this.lines.forEach((line) => {
1712
+ res += line.length();
1713
+ });
1714
+ return res;
1715
+ }
1716
+ getPercentages() {
1717
+ const length = this.getLength();
1718
+ let currentCurvePercentage = 0;
1719
+ const res = [];
1720
+ this.lines.forEach((line) => {
1721
+ const lineLength = line.length();
1722
+ const linePercentage = lineLength / length;
1723
+ let start = currentCurvePercentage;
1724
+ currentCurvePercentage += linePercentage;
1725
+ let end = currentCurvePercentage;
1726
+ res.push({ start, end });
1727
+ });
1728
+ res[res.length - 1].end = 1;
1729
+ return res;
1730
+ }
1731
+ getPointByPercentage(percentage) {
1732
+ if (percentage < 0 || percentage > 1) {
1733
+ throw new Error("Percentage must be between 0 and 1");
1734
+ }
1735
+ const percentages = this.getPercentages();
1736
+ let left = 0;
1737
+ let right = percentages.length - 1;
1738
+ while (left <= right) {
1739
+ const mid = Math.floor((left + right) / 2);
1740
+ if (percentage < percentages[mid].end) {
1741
+ right = mid - 1;
1742
+ } else if (percentage > percentages[mid].end) {
1743
+ left = mid + 1;
1744
+ } else {
1745
+ left = mid;
1746
+ break;
1747
+ }
1748
+ }
1749
+ const line = this.lines[left];
1750
+ const linePercentage = percentages[left];
1751
+ const ratio = (percentage - linePercentage.start) / (linePercentage.end - linePercentage.start);
1752
+ return line.lerp(ratio);
1753
+ }
1754
+ }
1755
+ export {
1756
+ solveCubic,
1757
+ reduce,
1758
+ projectPointOntoLine,
1759
+ offset2,
1760
+ offset,
1761
+ getLineIntersection,
1762
+ getIntersectionsBetweenCurves,
1763
+ getCubicRoots,
1764
+ cuberoot2,
1765
+ cuberoot,
1766
+ computeWithControlPoints,
1767
+ approximately,
1768
+ accept,
1769
+ TValOutofBoundError,
1770
+ Path,
1771
+ Line,
1772
+ ControlPoint,
1773
+ CompositeBCurve,
1774
+ BCurve,
1775
+ AABBIntersects
1776
+ };
1777
+
1778
+ //# debugId=8C425ECBCBA1EB0F64756E2164756E21
4
1779
 
5
1780
  //# sourceMappingURL=index.js.map