@rdme/curve-2d
Version:
2d curves in typescript
10 lines (9 loc) • 13.2 kB
JavaScript
/**
* Curve2D (@rdme/curve-2d)
* 2d curves in typescript
*
* (c) 2022 Propero Team <team@propero.dev>
* @license MIT
*/
"use strict";function t(t,e,n){return t*(1-n)+e*n}function e(t,e,n){const r=n-e;return(t=(t-e)%r)<0&&(t+=r),e+t}Object.defineProperty(exports,"__esModule",{value:!0});const n=2*Math.PI;function r(t){return e(t,0,n)}function s(t,e,s,i=!1){return t=r(t),e=r(e),(s=r(s))<e&&(e-=n),s<t&&(t-=n),t>=e&&t<s?!i:i}const{atan2:i,sqrt:o,pow:u,cos:a,sin:l,PI:c}=Math,h=2*c;class p{constructor(t,e){var n,r,s,i;"object"==typeof t?Array.isArray(t)?(this.x=null!==(n=t[0])&&void 0!==n?n:0,this.y=null!==(r=t[1])&&void 0!==r?r:0):(this.x=null!==(s=t.x)&&void 0!==s?s:0,this.y=null!==(i=t.y)&&void 0!==i?i:0):(this.x=null!=t?t:0,this.y=null!=e?e:0)}clone(){return new p(this)}get angle(){const t=i(this.x,this.y);return t<0?t+h:t}set angle(t){const e=this.length;this.x=l(t)*e,this.y=a(t)*e}get length(){return o(u(this.x,2)+u(this.y,2))}set length(t){const e=this.angle;this.x=l(e)*t,this.y=a(e)*t}get normal(){return p.fromAngle(this.angle)}get values(){return[this.x,this.y]}rotate(t,e={x:0,y:0}){const n=this.subtract(e);return n.angle+=t,n.add(e)}add(t){return new p(this.x+t.x,this.y+t.y)}subtract(t){return new p(this.x-t.x,this.y-t.y)}distance(t){return o(u(this.x-t.x,2)+u(this.y-t.y,2))}scale(t,e={x:0,y:0}){const n=this.subtract(e);return n.length*=t,n.add(e)}exponentiate(t){return p.fromAngle(this.angle,u(this.length,t))}multiplySymmetric(t){return new p(this.x*t.x,this.y*t.y)}multiplyAsymmetric(t){return new p(this.x*t.y,this.y*t.x)}multiplyValues(t){return new p(this.x*t,this.y*t)}divideValues(t){return new p(this.x/t,this.y/t)}lerp(e,n){return new p(t(this.x,e.x,n),t(this.y,e.y,n))}static fromAngle(t,e=1){return new p(l(t)*e,a(t)*e)}}const{min:g,abs:_}=Math;class d{invalidate(){this._length=this._lut=this._percentFn=void 0}defaultPrecision(){return 100}length(t){if(void 0!==this._length)return this._length;const e=this.getLut(t);let n=0;for(let t=0;t<e.length-1;++t)n+=e[t].distance(e[t+1]);return this._length=n}getLut(t=this.defaultPrecision(),e=!0){var n;if(!t)return[this.get(0),this.get(1)];if((null===(n=this._lut)||void 0===n?void 0:n.length)===t+1)return this._lut;const r=1/t,s=Array.from({length:t+1},((t,e)=>this.get(g(1,e*r))));return e&&(this._lut=s,this._length=this._percentFn=void 0),s}split(t){return[this.getSegment(0,t),this.getSegment(t,1)]}getPercentageFn(e){const n=this.getLut(e);if(this._percentFn)return this._percentFn;const r=d._getCumulativeLut(n);return this._percentFn=e=>{if(e>=.999)return 1;if(e<=.001)return 0;const[[n,s],[i,o]]=r.sort((([t],[n])=>_(t-e)-_(n-e))),u=_(e-n);return t(s,o,u/(u+_(e-i)))}}getByPercent(t,e){return this.get(this.getPercentageFn(e)(t))}_applyReverse(t){var e;if(null===(e=t._lut)||void 0===e||e.reverse(),t._percentFn){const e=t._percentFn;"__reverse"in e?t._percentFn=e.__reverse:(t._percentFn=t=>1-e(1-t),t._percentFn.__reverse=e)}return t}_applyClone(t){var e;return t._lut=null===(e=this._lut)||void 0===e?void 0:e.map((t=>new p(t))),t._length=this._length,t._percentFn=this._percentFn,t}static _getCumulativeLut(t){const e=Array(t.length),n=t.length,r=1/(t.length-1);let s=0;for(let e=0;e<t.length-1;++e)s+=t[e].distance(t[e+1]);let i=t[0],o=0;e[0]=[0,0];for(let u=1;u<n;++u)e[u]=[(o+=i.distance(t[u]))/s,u*r],i=t[u];return e}}function f(t,e,n,r){var s,i=arguments.length,o=i<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(t,e,n,r);else for(var u=t.length-1;u>=0;u--)(s=t[u])&&(o=(i<3?s(o):i>3?s(e,n,o):s(e,n))||o);return i>3&&o&&Object.defineProperty(e,n,o),o}function v(t,e){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(t,e)}function y(t){return function(e,n){Object.defineProperty(e,t,{get:function(){return this[n]},set:function(t){this[n]=t,this.invalidate()}})}}function m(t){return function(e,n){Object.defineProperty(e,t,{get:function(){return this[n]},set:function(t){this[n]=new p(t),this.invalidate()}})}}function w(t){return function(e,n){Object.defineProperty(e,t,{get:function(){return this[n]},set:function(t){this[n]=r(t),this.invalidate()}})}}class x extends d{constructor(t){super(),this.points=t}reverse(){return this._applyReverse(this.clone())}scale(t,e){const n=this.clone();return n.points=n.points.map((n=>n.scale(t,e))),n.invalidate(),n}rotate(t,e){const n=this.clone();return n.points=n.points.map((n=>n.rotate(t,e))),n.invalidate(),n}translate(t){const e=this.clone();return e.points=e.points.map((e=>e.add(t))),e.invalidate(),e}_applyReverse(t){var e;return null===(e=(t=super._applyReverse(t))._points)||void 0===e||e.reverse(),t}_applyClone(t){return(t=super._applyClone(t))._points=this._points.map((t=>new p(t))),t}}var b;f([(b="points",function(t,e){Object.defineProperty(t,b,{get:function(){return this[e].map((t=>new p(t)))},set:function(t){this[e]=t.map((t=>new p(t))),this.invalidate()}})}),v("design:type",Array)],x.prototype,"_points",void 0);class A extends d{constructor(t,e){super(),this._start=new p(t),this._end=new p(e)}tangent(t){const e=this.get(t),n=e.add(this._end.subtract(this._start));return new A(e,n)}length(){return this._start.distance(this._end)}getPercentageFn(){return t=>t}getByPercent(t){return this.get(t)}clone(){return new A(this._start,this._end)}get(t){return this._start.lerp(this._end,t)}getSegment(t,e){return new A(this.get(t),this.get(e))}reverse(){return new A(this._end,this._start)}rotate(t,e){return new A(this._start.rotate(t,e),this._end.rotate(t,e))}scale(t,e){return new A(this._start.scale(t,e),this._end.scale(t,e))}translate(t){return new A(this._start.add(t),this._end.add(t))}get direction(){return this._end.subtract(this._start)}}f([m("start"),v("design:type",p)],A.prototype,"_start",void 0),f([m("end"),v("design:type",p)],A.prototype,"_end",void 0);const{abs:C,pow:P,sqrt:j,PI:S}=Math,F=2*S;class O extends d{constructor(t,e,n,s,i=!1){super(),this._center=new p(t),this._radius=e,this._startAngle=r("object"==typeof n?new p(n).angle:n),this._endAngle=r("object"==typeof s?new p(s).angle:s),this._counterClockwise=i}clone(){const{_center:t,_radius:e,_startAngle:n,_endAngle:r,_counterClockwise:s}=this;return this._applyClone(new O(t,e,n,r,s))}get(t){const{_startAngle:e,arcSpan:n,_center:r,_radius:s,_counterClockwise:i}=this,o=i?e-n*t:e+n*t;return r.add(p.fromAngle(o,s))}get arcSpan(){const{_startAngle:t,_endAngle:e,_counterClockwise:n}=this;let r=C(e-t);return(n&&e>t||!n&&t>e)&&(r=F-r),r}length(){const{_radius:t,_startAngle:e,_endAngle:n}=this;return C(e-n)*t}getPercentageFn(){return t=>t}getByPercent(t){return this.get(t)}getSegment(t,e){const{_startAngle:n,arcSpan:r,_center:s,_radius:i,_counterClockwise:o}=this;return new O(s,i,n+r*t,n+r*e,o)}reverse(){const t=this.clone();return t._endAngle=this._startAngle,t._startAngle=this._endAngle,t._counterClockwise=!this._counterClockwise,t.invalidate(),t}rotate(t,e){const n=this.clone();return n._center=n._center.rotate(t,e),n._startAngle=(n._startAngle+t)%F,n._endAngle=(n._endAngle+t)%F,n.invalidate(),n}scale(t,e){const n=this.clone();return n._center=n._center.scale(t,e),n._radius*=t,n.invalidate(),n}translate(t){const e=this.clone();return e._center=e._center.add(t),e.invalidate(),e}static fromPoints({x:t,y:n},{x:r,y:i},{x:o,y:u}){const a=e(new p(r-t,i-n).angle,0,S)-e(new p(r-o,i-u).angle,0,S);if(C(a)<1e-6||C(C(a)-S)<1e-6)return new A({x:t,y:n},{x:o,y:u});const l=t-r,c=t-o,h=n-i,g=n-u,_=u-n,d=i-n,f=o-t,v=r-t,y=P(t,2)-P(o,2),m=P(n,2)-P(u,2),w=P(r,2)-P(t,2),x=P(i,2)-P(n,2),b=(y*l+m*l+w*c+x*c)/(2*(_*l-d*c)),F=(y*h+m*h+w*g+x*g)/(2*(f*h-v*g)),R=-P(t,2)-P(n,2)-2*F*t-2*b*n,k=j(F*F+b*b-R),z=new p(-F,-b),D=new p(t,n).subtract(z).angle,M=new p(r,i).subtract(z).angle,B=new p(o,u).subtract(z).angle,L=s(M,D,B,!0);return new O(z,k,D,B,L)}}f([m("center"),v("design:type",p)],O.prototype,"_center",void 0),f([w("startAngle"),v("design:type",Number)],O.prototype,"_startAngle",void 0),f([w("endAngle"),v("design:type",Number)],O.prototype,"_endAngle",void 0),f([y("radius"),v("design:type",Number)],O.prototype,"_radius",void 0),f([y("counterClockwise"),v("design:type",Boolean)],O.prototype,"_counterClockwise",void 0);class R extends x{constructor(t){super(t)}get(t){const e=this._points,n=this.order;if(0===t)return e[0];if(1===t)return e[n];switch(n){case 0:return e[0];case 1:return e[0].lerp(e[1],t);case 2:return R._getQuadratic(e[0],e[1],e[2],t);case 3:return R._getCubic(e[0],e[1],e[2],e[3],t);default:return R._getDeCasteljau(e,t)}}getSegment(t,e){if(t===e)return new R([this.get(t)]);if(t>e)return this.getSegment(e,t).reverse();if(0===t)return new R(R._getDeCasteljauPoints(this._points,e));if(1===e)return new R(R._getDeCasteljauPoints(this._points.slice().reverse(),1-t)).reverse();const n=R._getDeCasteljauPoints(this._points,e).reverse();return new R(R._getDeCasteljauPoints(n,(1-t)*e)).reverse()}clone(){return this._applyClone(new R(this._points))}get order(){return this._points.length-1}length(t){const e=this.order;return 0===e?0:1===e?this._points[0].distance(this._points[1]):super.length(t)}catmull(t=this._points.length+2){return super.catmull(t)}simplify(t){return this.beziers(t)}static _getQuadratic(t,e,n,r){const s=1-r,i=s*s,o=s*r*2,u=r*r;return new p(i*t.x+o*e.x+u*n.x,i*t.y+o*e.y+u*n.y)}static _getCubic(t,e,n,r,s){const i=1-s,o=i*i,u=s*s,a=o*i,l=o*s*3,c=i*u*3,h=s*u;return new p(a*t.x+l*e.x+c*r.x+h*r.x,a*t.y+l*e.y+c*n.y+h*r.y)}static _getDeCasteljau(t,e){const n=t.slice();for(;n.length>1;){for(let t=0;t<n.length-1;++t)n[t]=n[t].lerp(n[t+1],e);n.splice(n.length-1,1)}return n[0]}static _getDeCasteljauPoints(t,e){const n=t.slice();for(let t=n.length-1;t>0;--t)for(let r=0;r<t-1;++r)n[r]=n[r].lerp(n[r+1],e);return n}}const{pow:k}=Math;class z extends x{constructor(t,e=.5){super(t),this._alpha=e}clone(){const{_points:t,alpha:e}=this;return this._applyClone(new z(t,e))}get(t){const{_points:[e,n,r,s],_alpha:i}=this,{_knotInterval:o,_remap:u}=z;if(0===t)return n;if(1===t)return r;const a=o(e,n,i),l=o(n,r,i)+a,c=o(r,s,i)+l,h=(l-a)*t+a,p=u(e,n,0,a,h),g=u(n,r,a,l,h),_=u(r,s,l,c,h),d=u(p,g,0,l,h),f=u(g,_,a,c,h);return u(d,f,a,l,h)}getSegment(t,e){return this.bezier.getSegment(t,e)}get bezier(){const{_points:[t,e,n,r],alpha:s}=this,i=e.add(n.subtract(t).divideValues(6*s)),o=n.subtract(r.subtract(e).divideValues(6*s));return new R([e,i,o,n])}beziers(){return[this.bezier]}static _knotInterval(t,e,n){return k(e.distance(t),n)}static _remap(t,e,n,r,s){return t.lerp(e,(s-n)/(r-n))}}f([y("alpha"),v("design:type",Number)],z.prototype,"_alpha",void 0);const{floor:D}=Math;class M extends d{constructor(t){super(),this._curves=t.map((t=>t.clone()))}clone(){return this._applyClone(new M(this._curves))}get(t){const[e,n]=this._curveAndOffset(t);return e.get(n)}getSegment(t,e){if(t===e){const[e,n]=this._curveAndOffset(t);return e.getSegment(n,n)}if(0===t&&1===e)return this.clone();if(t>e)return this.getSegment(e,t).reverse();const[n,r,s]=this._curveAndOffset(t),[i,o,u]=this._curveAndOffset(t);return s===u?n.getSegment(r,o):new M([n.getSegment(r,1),...this._curves.slice(s+1,u-1).map((t=>t.clone())),i.getSegment(0,o)])}_curveAndOffset(t){const{_curves:e}=this;if(0===t)return[e[0],0,0];if(1===t)return[e[e.length-1],1,e.length-1];const n=D(t*e.length);return[e[n],t*e.length-n,n]}_applyReverse(t){return super._applyReverse(t),t._curves=t._curves.map((t=>t.reverse())),t}reverse(){return new M(this._curves.map((t=>t.reverse())).reverse())}scale(t,e){return new M(this._curves.map((n=>n.scale(t,e))))}rotate(t,e){return new M(this._curves.map((n=>n.rotate(t,e))))}translate(t){return new M(this._curves.map((e=>e.translate(t))))}}class B extends M{constructor(t){super([]),this.points=t}clone(){const t=new B(this.points);return t._curves=this._curves.map((t=>t.clone())),this._applyClone(t)}get points(){const t=this._curves,[,...e]=t[t.length-1].points;return[...t.map((t=>t.points[0])),...e]}set points(t){const e=[];for(let n=0;n<t.length-3;++n)e.push(new z(t.slice(n,n+4)));this._curves=e,this.invalidate()}catmull(){return this}beziers(){return this._curves.map((t=>t.bezier))}static fromPointsOnCurve(t){if((t=t.map((t=>new p(t)))).length<=1)throw new RangeError("Too few points to construct catmull curve with");const e=t[0],n=t[1],r=t[t.length-2],s=t[t.length-1],i=e.subtract(n).multiplyValues(6),o=s.subtract(r).multiplyValues(6),u=e.add(i),a=s.add(o);return new B([u,...t,a])}static fromCurve(t,e,n){return B.fromPointsOnCurve(t.getLut(e,n))}}Object.defineProperties(d.prototype,{beziers:{value:function(t,e){return this.catmull(t,e).beziers()},enumerable:!1},catmull:{value:function(t,e){return B.fromCurve(this,t,e)},enumerable:!1},tangent:{value:function(t,e=1e-7){const n=t<e?t:t-e,r=t>1-e?t:t+e,s=this.get(n),i=this.get(r),o=i.subtract(s),u=o.length/(r-n),a=s.lerp(i,.5),l=s.add(p.fromAngle(o.angle,u));return new A(a,l)},enumerable:!1}}),exports.Arc2d=O,exports.Bezier2d=R,exports.Catmull2d=B,exports.CatmullRomSpline2d=z,exports.Curve2d=x,exports.CurveBase2d=d,exports.Line2d=A,exports.PolyCurve2d=M,exports.Vec2d=p,exports.angleBetween=s,exports.clamp=function(t,e=0,n=1){return t>n?n:t<e?e:t},exports.clampAngle=r,exports.clampModulo=e,exports.lerp=t;
//# sourceMappingURL=main.js.map