perfect-freehand
Advanced tools
Comparing version 0.5.2 to 0.5.3
@@ -1,1 +0,1 @@ | ||
var me=Object.defineProperty;var ge=e=>me(e,"__esModule",{value:!0});var ke=(e,t)=>{ge(e);for(var n in t)me(e,n,{get:t[n],enumerable:!0})};ke(exports,{default:()=>he,getStrokeOutlinePoints:()=>de,getStrokePoints:()=>be});function b(e,t){return[e[0]+t[0],e[1]+t[1]]}function d(e,t){return[e[0]-t[0],e[1]-t[1]]}function J(e,t){return[t[0]-e[0],t[1]-e[1]]}function p(e,t){return[e[0]*t,e[1]*t]}function Pe(e,t){return[e[0]/t,e[1]/t]}function E(e){return[e[1],-e[0]]}function ae(e,t){return e[0]*t[0]+e[1]*t[1]}function Se(e){return Math.hypot(e[0],e[1])}function ye(e){return e[0]*e[0]+e[1]*e[1]}function ee(e,t){return ye(d(e,t))}function I(e){return Pe(e,Se(e))}function W(e,t){return Math.hypot(e[1]-t[1],e[0]-t[0])}function le(e,t){return p(b(e,t),.5)}function C(e,t,n){let c=Math.sin(n),i=Math.cos(n),P=e[0]-t[0],h=e[1]-t[1],O=P*i-h*c,q=P*c+h*i;return[O+t[0],q+t[1]]}function w(e,t,n){return b(e,p(J(e,t),n))}function F(e,t){return e[0]===t[0]&&e[1]===t[1]}function ve(e,t,n){return e*(1-n)+t*n}function te(e,t,n){return Math.max(t,Math.min(n,e))}function fe(e){return Array.isArray(e[0])?e.map(([t,n,c=.5])=>[t,n,c]):e.map(({x:t,y:n,pressure:c=.5})=>[t,n,c])}function X(e,t,n,c=.5){return t?(c=te(n(c),0,1),(t<0?ve(e,e+e*te(t,-.95,-.05),c):ve(e-e*te(t,.05,.95),e,c))/2):e/2}var{min:ne,PI:N}=Math;function be(e,t={}){let{streamline:n=.5}=t,{simulatePressure:c=!0}=t;n=n/(c?4:2);let i=fe(e);if(i.length===0)return[];i.length===1&&i.push([...b(i[0],[1,1]),i[0][2]]);let P=[{point:[i[0][0],i[0][1]],pressure:i[0][2],vector:[0,0],distance:0,runningLength:0}];for(let h=1,O=0,q=i[h],M=P[O];h<i.length;h++,q=i[h],M=P[O]){let L=w(M.point,q,1-n);if(F(M.point,L))continue;let Y=I(d(M.point,L)),S=W(L,M.point),Z=M.runningLength+S,_={point:L,pressure:q[2],vector:Y,distance:S,runningLength:Z};P.push(_),O+=1}return P}function de(e,t={}){let{size:n=8,thinning:c=.5,smoothing:i=.5,simulatePressure:P=!0,easing:h=u=>u,start:O={},end:q={},last:M=!1}=t,{streamline:L=.5}=t;L/=2;let{cap:Y=!0,taper:S=0,easing:Z=u=>u*(2-u)}=O,{cap:_=!0,taper:R=0,easing:xe=u=>--u*u*u+1}=q,V=e.length;if(V===0)return[];let re=e[V-1].runningLength,v=[],g=[],$=e.slice(0,5).reduce((u,y)=>(u+y.pressure)/2,e[0].pressure),k=X(n,c,h,e[V-1].pressure),ue,oe=e[0].vector,j=e[0].point,G=j,a=j,m=G,Q=!0;for(let u=0;u<V-1;u++){let{pressure:y}=e[u],{point:l,vector:o,distance:f,runningLength:r}=e[u];if(u>0&&Q&&r<n/2)continue;if(Q&&(Q=!1),c){if(P){let U=ne(1,1-f/n),D=ne(1,f/n);y=ne(1,$+(U-$)*(D/2))}k=X(n,c,h,y)}else k=n/2;ue===void 0&&(ue=k);let s=r<S?Z(r/S):1,H=re-r<R?xe((re-r)/R):1;k=Math.max(.01,k*Math.min(s,H));let se=e[u+1].vector,A=ae(o,se);if(A<0){let U=p(E(oe),k);for(let D=0;D<1;D+=.2)m=C(b(l,U),l,N*-D),a=C(d(l,U),l,N*D),g.push(m),v.push(a);j=a,G=m;continue}let ce=p(E(w(se,o,A)),k);a=d(l,ce),m=b(l,ce);let ie=u<2||A<.25,pe=Math.pow(Math.max((r>n?n:n/2)*i,1),2);(ie||ee(j,a)>pe)&&(v.push(w(j,a,L)),j=a),(ie||ee(G,m)>pe)&&(g.push(w(G,m,L)),G=m),$=y,oe=o}let x=e[0],B=e[V-1],z=Q||g.length<2||v.length<2;if(z&&(!(S||R)||M)){let u=0;for(let o=0;o<V;o++){let{pressure:f,runningLength:r}=e[o];if(r>n){u=X(n,c,h,f);break}}let y=d(x.point,p(E(I(J(B.point,x.point))),u||k)),l=[];for(let o=0,f=.1;o<=1;o+=f)l.push(C(y,x.point,N*2*o));return l}let K=[],T=[];if(v.length>1&&g.length>1){m=g[1];for(let r=1;r<v.length;r++)if(!F(m,v[r])){a=v[r];break}if(Y||S)if(!S&&!(R&&z)){if(!F(m,a)){let r=d(x.point,p(I(J(m,a)),W(m,a)/2));for(let s=0,H=.1;s<=1;s+=H)K.push(C(r,x.point,N*s));v.shift(),g.shift()}}else K.push(x.point,b(x.point,[.1,.1]));else if(!F(m,a)){let r=I(J(m,a)),s=W(m,a)/2;K.push(d(x.point,p(r,s*.95))),K.push(d(x.point,p(r,s))),K.push(b(x.point,p(r,s))),K.push(b(x.point,p(r,s*.95))),v.shift(),g.shift()}let u=v[v.length-1],y=g[g.length-1],l=le(u,y),o=t.last?B.point:w(l,B.point,.618),f=I(d(o,l));if(_||R)if(!R&&!(S&&z)){let r=b(o,p(E(f),k));for(let s=0,H=.15;s<=1;s+=H)T.push(C(r,o,N*3*s))}else T.push(o);else{let r=w(l,o,.95),s=k*.95;T.push(b(r,p(E(f),s))),T.push(b(o,p(E(f),s))),T.push(d(o,p(E(f),s))),T.push(d(r,p(E(f),s)))}}return v.concat(T,g.reverse(),K)}function he(e,t={}){return de(be(e,t),t)} | ||
var me=Object.defineProperty;var xe=e=>me(e,"__esModule",{value:!0});var ge=(e,t)=>{xe(e);for(var n in t)me(e,n,{get:t[n],enumerable:!0})};ge(exports,{default:()=>Ee,getStrokeOutlinePoints:()=>de,getStrokePoints:()=>fe});function d(e,t){return[e[0]+t[0],e[1]+t[1]]}function h(e,t){return[e[0]-t[0],e[1]-t[1]]}function W(e,t){return[t[0]-e[0],t[1]-e[1]]}function i(e,t){return[e[0]*t,e[1]*t]}function ke(e,t){return[e[0]/t,e[1]/t]}function y(e){return[e[1],-e[0]]}function le(e,t){return e[0]*t[0]+e[1]*t[1]}function Se(e){return Math.hypot(e[0],e[1])}function Pe(e){return e[0]*e[0]+e[1]*e[1]}function te(e,t){return Pe(h(e,t))}function I(e){return ke(e,Se(e))}function F(e,t){return Math.hypot(e[1]-t[1],e[0]-t[0])}function ae(e,t){return i(d(e,t),.5)}function G(e,t,n){let c=Math.sin(n),w=Math.cos(n),p=e[0]-t[0],k=e[1]-t[1],v=p*w-k*c,R=p*c+k*w;return[v+t[0],R+t[1]]}function H(e,t,n){return d(e,i(W(e,t),n))}function X(e,t){return e[0]===t[0]&&e[1]===t[1]}function be(e,t,n){return e*(1-n)+t*n}function ne(e,t,n){return Math.max(t,Math.min(n,e))}function ve(e){return Array.isArray(e[0])?e.map(([t,n,c=.5])=>[t,n,c]):e.map(({x:t,y:n,pressure:c=.5})=>[t,n,c])}function $(e,t,n,c=.5){return t?(c=ne(n(c),0,1),(t<0?be(e,e+e*ne(t,-.95,-.05),c):be(e-e*ne(t,.05,.95),e,c))/2):e/2}var{min:J,PI:Y}=Math;function fe(e,t={}){let{streamline:n=.5,last:c=!1}=t,{simulatePressure:w=!0}=t;if(e.length===0)return[];n=n/(w?3:2);let p=ve(e);p.length===1&&p.push([...d(p[0],[1,1]),p[0][2]]);let k=[],v={point:[p[0][0],p[0][1]],pressure:p[0][2],vector:[0,0],distance:0,runningLength:0};k.push(v);let R=p.length,V;for(let E=0;E<R;E++){V=p[E];let K=c&&E===R-1?V:H(v.point,V,1-n);if(X(v.point,K))continue;let M=I(h(v.point,K)),Z=F(K,v.point),B=v.runningLength+Z;v={point:K,pressure:V[2],vector:M,distance:Z,runningLength:B},k.push(v)}return k}function de(e,t={}){let{size:n=8,thinning:c=.5,smoothing:w=.5,simulatePressure:p=!0,easing:k=r=>r,start:v={},end:R={},last:V=!1}=t,{streamline:E=.5}=t;E/=2;let{cap:K=!0,taper:M=0,easing:Z=r=>r*(2-r)}=v,{cap:B=!0,taper:T=0,easing:he=r=>--r*r*r+1}=R,j=e.length;if(j===0)return[];let re=e[j-1].runningLength,f=[],S=[],z=e.slice(0,10).reduce((r,g)=>{let a=g.pressure;if(p){let o=J(1,g.distance/n),b=J(1,1-o);a=J(1,r+(b-r)*(o/4))}return(r+a)/2},e[0].pressure),P=$(n,c,k,e[j-1].pressure),ue,oe=e[0].vector,C=e[0].point,N=C,m=C,l=N,_=!0;for(let r=0;r<j-1;r++){let{pressure:g}=e[r],{point:a,vector:o,distance:b,runningLength:u}=e[r];if(r>0&&_&&u<n/2)continue;if(_&&(_=!1),c){if(p){let U=J(1,b/n),D=J(1,1-U);g=J(1,z+(D-z)*(U/4))}P=$(n,c,k,g)}else P=n/2;ue===void 0&&(ue=P);let s=u<M?Z(u/M):1,Q=re-u<T?he((re-u)/T):1;P=Math.max(.01,P*Math.min(s,Q));let O=e[r+1].vector,ee=le(o,O);if(ee<0){let U=i(y(oe),P);for(let D=0;D<1;D+=.2)l=G(d(a,U),a,Y*-D),m=G(h(a,U),a,Y*D),S.push(l),f.push(m);C=m,N=l;continue}let ce=i(y(H(O,o,ee)),P);m=h(a,ce),l=d(a,ce);let ie=r<2||ee<.25,pe=Math.pow(Math.max((u>n?n:n/2)*w,1),2);(ie||te(C,m)>pe)&&(f.push(H(C,m,E)),C=m),(ie||te(N,l)>pe)&&(S.push(H(N,l,E)),N=l),z=g,oe=o}let x=e[0],se=e[j-1],A=_||S.length<2||f.length<2;if(A&&(!(M||T)||V)){let r=0;for(let o=0;o<j;o++){let{pressure:b,runningLength:u}=e[o];if(u>n){r=$(n,c,k,b);break}}let g=h(x.point,i(y(I(W(se.point,x.point))),r||P)),a=[];for(let o=0,b=.1;o<=1;o+=b)a.push(G(g,x.point,Y*2*o));return a}let L=[],q=[];if(f.length>1&&S.length>1){l=S[1];for(let u=1;u<f.length;u++)if(!X(l,f[u])){m=f[u];break}if(K||M)if(!M&&!(T&&A)){if(!X(l,m)){let u=h(x.point,i(I(W(l,m)),F(l,m)/2));for(let s=0,Q=.1;s<=1;s+=Q){let O=G(u,x.point,Y*s);if(F(O,m)<1)break;L.push(O)}f.shift(),S.shift()}}else L.push(x.point,d(x.point,[.1,.1]));else if(!X(l,m)){let u=I(W(l,m)),s=F(l,m)/2;L.push(h(x.point,i(u,s*.95))),L.push(h(x.point,i(u,s))),L.push(d(x.point,i(u,s))),L.push(d(x.point,i(u,s*.95))),f.shift(),S.shift()}let r=f[f.length-1],g=S[S.length-1],a=ae(r,g),o=se.point,b=I(h(o,a));if(B||T)if(!T&&!(M&&A)){let u=d(o,i(y(b),P));for(let s=0,Q=.1;s<=1;s+=Q){let O=G(u,o,Y*3*s);if(F(O,g)<1)break;q.push(O)}}else q.push(o);else{let u=H(a,o,.95),s=P*.95;q.push(d(u,i(y(b),s))),q.push(d(o,i(y(b),s))),q.push(h(o,i(y(b),s))),q.push(h(u,i(y(b),s)))}}return f.concat(q,S.reverse(),L)}function ye(e,t={}){return de(fe(e,t),t)}var Ee=ye; |
@@ -1,1 +0,316 @@ | ||
function b(e,t){return[e[0]+t[0],e[1]+t[1]]}function d(e,t){return[e[0]-t[0],e[1]-t[1]]}function J(e,t){return[t[0]-e[0],t[1]-e[1]]}function p(e,t){return[e[0]*t,e[1]*t]}function be(e,t){return[e[0]/t,e[1]/t]}function E(e){return[e[1],-e[0]]}function me(e,t){return e[0]*t[0]+e[1]*t[1]}function de(e){return Math.hypot(e[0],e[1])}function he(e){return e[0]*e[0]+e[1]*e[1]}function ee(e,t){return he(d(e,t))}function I(e){return be(e,de(e))}function W(e,t){return Math.hypot(e[1]-t[1],e[0]-t[0])}function ae(e,t){return p(b(e,t),.5)}function C(e,t,n){let c=Math.sin(n),i=Math.cos(n),P=e[0]-t[0],h=e[1]-t[1],O=P*i-h*c,q=P*c+h*i;return[O+t[0],q+t[1]]}function w(e,t,n){return b(e,p(J(e,t),n))}function F(e,t){return e[0]===t[0]&&e[1]===t[1]}function le(e,t,n){return e*(1-n)+t*n}function te(e,t,n){return Math.max(t,Math.min(n,e))}function ve(e){return Array.isArray(e[0])?e.map(([t,n,c=.5])=>[t,n,c]):e.map(({x:t,y:n,pressure:c=.5})=>[t,n,c])}function X(e,t,n,c=.5){return t?(c=te(n(c),0,1),(t<0?le(e,e+e*te(t,-.95,-.05),c):le(e-e*te(t,.05,.95),e,c))/2):e/2}var{min:ne,PI:N}=Math;function xe(e,t={}){let{streamline:n=.5}=t,{simulatePressure:c=!0}=t;n=n/(c?4:2);let i=ve(e);if(i.length===0)return[];i.length===1&&i.push([...b(i[0],[1,1]),i[0][2]]);let P=[{point:[i[0][0],i[0][1]],pressure:i[0][2],vector:[0,0],distance:0,runningLength:0}];for(let h=1,O=0,q=i[h],M=P[O];h<i.length;h++,q=i[h],M=P[O]){let L=w(M.point,q,1-n);if(F(M.point,L))continue;let Y=I(d(M.point,L)),S=W(L,M.point),Z=M.runningLength+S,_={point:L,pressure:q[2],vector:Y,distance:S,runningLength:Z};P.push(_),O+=1}return P}function ge(e,t={}){let{size:n=8,thinning:c=.5,smoothing:i=.5,simulatePressure:P=!0,easing:h=u=>u,start:O={},end:q={},last:M=!1}=t,{streamline:L=.5}=t;L/=2;let{cap:Y=!0,taper:S=0,easing:Z=u=>u*(2-u)}=O,{cap:_=!0,taper:R=0,easing:fe=u=>--u*u*u+1}=q,V=e.length;if(V===0)return[];let re=e[V-1].runningLength,v=[],g=[],$=e.slice(0,5).reduce((u,y)=>(u+y.pressure)/2,e[0].pressure),k=X(n,c,h,e[V-1].pressure),ue,oe=e[0].vector,j=e[0].point,G=j,a=j,m=G,Q=!0;for(let u=0;u<V-1;u++){let{pressure:y}=e[u],{point:l,vector:o,distance:f,runningLength:r}=e[u];if(u>0&&Q&&r<n/2)continue;if(Q&&(Q=!1),c){if(P){let U=ne(1,1-f/n),D=ne(1,f/n);y=ne(1,$+(U-$)*(D/2))}k=X(n,c,h,y)}else k=n/2;ue===void 0&&(ue=k);let s=r<S?Z(r/S):1,H=re-r<R?fe((re-r)/R):1;k=Math.max(.01,k*Math.min(s,H));let se=e[u+1].vector,A=me(o,se);if(A<0){let U=p(E(oe),k);for(let D=0;D<1;D+=.2)m=C(b(l,U),l,N*-D),a=C(d(l,U),l,N*D),g.push(m),v.push(a);j=a,G=m;continue}let ce=p(E(w(se,o,A)),k);a=d(l,ce),m=b(l,ce);let ie=u<2||A<.25,pe=Math.pow(Math.max((r>n?n:n/2)*i,1),2);(ie||ee(j,a)>pe)&&(v.push(w(j,a,L)),j=a),(ie||ee(G,m)>pe)&&(g.push(w(G,m,L)),G=m),$=y,oe=o}let x=e[0],B=e[V-1],z=Q||g.length<2||v.length<2;if(z&&(!(S||R)||M)){let u=0;for(let o=0;o<V;o++){let{pressure:f,runningLength:r}=e[o];if(r>n){u=X(n,c,h,f);break}}let y=d(x.point,p(E(I(J(B.point,x.point))),u||k)),l=[];for(let o=0,f=.1;o<=1;o+=f)l.push(C(y,x.point,N*2*o));return l}let K=[],T=[];if(v.length>1&&g.length>1){m=g[1];for(let r=1;r<v.length;r++)if(!F(m,v[r])){a=v[r];break}if(Y||S)if(!S&&!(R&&z)){if(!F(m,a)){let r=d(x.point,p(I(J(m,a)),W(m,a)/2));for(let s=0,H=.1;s<=1;s+=H)K.push(C(r,x.point,N*s));v.shift(),g.shift()}}else K.push(x.point,b(x.point,[.1,.1]));else if(!F(m,a)){let r=I(J(m,a)),s=W(m,a)/2;K.push(d(x.point,p(r,s*.95))),K.push(d(x.point,p(r,s))),K.push(b(x.point,p(r,s))),K.push(b(x.point,p(r,s*.95))),v.shift(),g.shift()}let u=v[v.length-1],y=g[g.length-1],l=ae(u,y),o=t.last?B.point:w(l,B.point,.618),f=I(d(o,l));if(_||R)if(!R&&!(S&&z)){let r=b(o,p(E(f),k));for(let s=0,H=.15;s<=1;s+=H)T.push(C(r,o,N*3*s))}else T.push(o);else{let r=w(l,o,.95),s=k*.95;T.push(b(r,p(E(f),s))),T.push(b(o,p(E(f),s))),T.push(d(o,p(E(f),s))),T.push(d(r,p(E(f),s)))}}return v.concat(T,g.reverse(),K)}function ke(e,t={}){return ge(xe(e,t),t)}export{ke as default,ge as getStrokeOutlinePoints,xe as getStrokePoints}; | ||
// src/vec.ts | ||
function add(A, B) { | ||
return [A[0] + B[0], A[1] + B[1]]; | ||
} | ||
function sub(A, B) { | ||
return [A[0] - B[0], A[1] - B[1]]; | ||
} | ||
function vec(A, B) { | ||
return [B[0] - A[0], B[1] - A[1]]; | ||
} | ||
function mul(A, n) { | ||
return [A[0] * n, A[1] * n]; | ||
} | ||
function div(A, n) { | ||
return [A[0] / n, A[1] / n]; | ||
} | ||
function per(A) { | ||
return [A[1], -A[0]]; | ||
} | ||
function dpr(A, B) { | ||
return A[0] * B[0] + A[1] * B[1]; | ||
} | ||
function len(A) { | ||
return Math.hypot(A[0], A[1]); | ||
} | ||
function len2(A) { | ||
return A[0] * A[0] + A[1] * A[1]; | ||
} | ||
function dist2(A, B) { | ||
return len2(sub(A, B)); | ||
} | ||
function uni(A) { | ||
return div(A, len(A)); | ||
} | ||
function dist(A, B) { | ||
return Math.hypot(A[1] - B[1], A[0] - B[0]); | ||
} | ||
function med(A, B) { | ||
return mul(add(A, B), 0.5); | ||
} | ||
function rotAround(A, C, r) { | ||
const s = Math.sin(r); | ||
const c = Math.cos(r); | ||
const px = A[0] - C[0]; | ||
const py = A[1] - C[1]; | ||
const nx = px * c - py * s; | ||
const ny = px * s + py * c; | ||
return [nx + C[0], ny + C[1]]; | ||
} | ||
function lrp(A, B, t) { | ||
return add(A, mul(vec(A, B), t)); | ||
} | ||
function isEqual(a, b) { | ||
return a[0] === b[0] && a[1] === b[1]; | ||
} | ||
// src/utils.ts | ||
function lerp(y1, y2, mu) { | ||
return y1 * (1 - mu) + y2 * mu; | ||
} | ||
function clamp(n, a, b) { | ||
return Math.max(a, Math.min(b, n)); | ||
} | ||
function toPointsArray(points) { | ||
if (Array.isArray(points[0])) { | ||
return points.map(([x, y, pressure = 0.5]) => [ | ||
x, | ||
y, | ||
pressure | ||
]); | ||
} else { | ||
return points.map(({ x, y, pressure = 0.5 }) => [x, y, pressure]); | ||
} | ||
} | ||
function getStrokeRadius(size, thinning, easing, pressure = 0.5) { | ||
if (!thinning) | ||
return size / 2; | ||
pressure = clamp(easing(pressure), 0, 1); | ||
return (thinning < 0 ? lerp(size, size + size * clamp(thinning, -0.95, -0.05), pressure) : lerp(size - size * clamp(thinning, 0.05, 0.95), size, pressure)) / 2; | ||
} | ||
// src/index.ts | ||
var { min, PI } = Math; | ||
function getStrokePoints(points, options = {}) { | ||
let { streamline = 0.5, last: isComplete = false } = options; | ||
const { simulatePressure = true } = options; | ||
if (points.length === 0) | ||
return []; | ||
streamline = streamline / (simulatePressure ? 3 : 2); | ||
const pts = toPointsArray(points); | ||
if (pts.length === 1) | ||
pts.push([...add(pts[0], [1, 1]), pts[0][2]]); | ||
const strokePoints = []; | ||
let prev = { | ||
point: [pts[0][0], pts[0][1]], | ||
pressure: pts[0][2], | ||
vector: [0, 0], | ||
distance: 0, | ||
runningLength: 0 | ||
}; | ||
strokePoints.push(prev); | ||
const len3 = pts.length; | ||
let curr; | ||
for (let i = 0; i < len3; i++) { | ||
curr = pts[i]; | ||
const point = isComplete && i === len3 - 1 ? curr : lrp(prev.point, curr, 1 - streamline); | ||
if (isEqual(prev.point, point)) | ||
continue; | ||
const vector = uni(sub(prev.point, point)); | ||
const distance = dist(point, prev.point); | ||
const runningLength = prev.runningLength + distance; | ||
prev = { | ||
point, | ||
pressure: curr[2], | ||
vector, | ||
distance, | ||
runningLength | ||
}; | ||
strokePoints.push(prev); | ||
} | ||
return strokePoints; | ||
} | ||
function getStrokeOutlinePoints(points, options = {}) { | ||
const { | ||
size = 8, | ||
thinning = 0.5, | ||
smoothing = 0.5, | ||
simulatePressure = true, | ||
easing = (t) => t, | ||
start = {}, | ||
end = {}, | ||
last: isComplete = false | ||
} = options; | ||
let { streamline = 0.5 } = options; | ||
streamline /= 2; | ||
const { | ||
cap: capStart = true, | ||
taper: taperStart = 0, | ||
easing: taperStartEase = (t) => t * (2 - t) | ||
} = start; | ||
const { | ||
cap: capEnd = true, | ||
taper: taperEnd = 0, | ||
easing: taperEndEase = (t) => --t * t * t + 1 | ||
} = end; | ||
const len3 = points.length; | ||
if (len3 === 0) | ||
return []; | ||
const totalLength = points[len3 - 1].runningLength; | ||
const leftPts = []; | ||
const rightPts = []; | ||
let prevPressure = points.slice(0, 10).reduce((acc, curr) => { | ||
let pressure = curr.pressure; | ||
if (simulatePressure) { | ||
const sp = min(1, curr.distance / size); | ||
const rp = min(1, 1 - sp); | ||
pressure = min(1, acc + (rp - acc) * (sp / 4)); | ||
} | ||
return (acc + pressure) / 2; | ||
}, points[0].pressure); | ||
let radius = getStrokeRadius(size, thinning, easing, points[len3 - 1].pressure); | ||
let firstRadius = void 0; | ||
let prevVector = points[0].vector; | ||
let pl = points[0].point; | ||
let pr = pl; | ||
let tl = pl; | ||
let tr = pr; | ||
let short = true; | ||
for (let i = 0; i < len3 - 1; i++) { | ||
let { pressure } = points[i]; | ||
const { point, vector, distance, runningLength } = points[i]; | ||
if (i > 0 && short && runningLength < size / 2) { | ||
continue; | ||
} else if (short) { | ||
short = false; | ||
} | ||
if (thinning) { | ||
if (simulatePressure) { | ||
const sp = min(1, distance / size); | ||
const rp = min(1, 1 - sp); | ||
pressure = min(1, prevPressure + (rp - prevPressure) * (sp / 4)); | ||
} | ||
radius = getStrokeRadius(size, thinning, easing, pressure); | ||
} else { | ||
radius = size / 2; | ||
} | ||
if (firstRadius === void 0) { | ||
firstRadius = radius; | ||
} | ||
const ts = runningLength < taperStart ? taperStartEase(runningLength / taperStart) : 1; | ||
const te = totalLength - runningLength < taperEnd ? taperEndEase((totalLength - runningLength) / taperEnd) : 1; | ||
radius = Math.max(0.01, radius * Math.min(ts, te)); | ||
const nextVector = points[i + 1].vector; | ||
const dpr2 = dpr(vector, nextVector); | ||
if (dpr2 < 0) { | ||
const offset2 = mul(per(prevVector), radius); | ||
for (let t = 0; t < 1; t += 0.2) { | ||
tr = rotAround(add(point, offset2), point, PI * -t); | ||
tl = rotAround(sub(point, offset2), point, PI * t); | ||
rightPts.push(tr); | ||
leftPts.push(tl); | ||
} | ||
pl = tl; | ||
pr = tr; | ||
continue; | ||
} | ||
const offset = mul(per(lrp(nextVector, vector, dpr2)), radius); | ||
tl = sub(point, offset); | ||
tr = add(point, offset); | ||
const alwaysAdd = i < 2 || dpr2 < 0.25; | ||
const minDistance = Math.pow(Math.max((runningLength > size ? size : size / 2) * smoothing, 1), 2); | ||
if (alwaysAdd || dist2(pl, tl) > minDistance) { | ||
leftPts.push(lrp(pl, tl, streamline)); | ||
pl = tl; | ||
} | ||
if (alwaysAdd || dist2(pr, tr) > minDistance) { | ||
rightPts.push(lrp(pr, tr, streamline)); | ||
pr = tr; | ||
} | ||
prevPressure = pressure; | ||
prevVector = vector; | ||
} | ||
const firstPoint = points[0]; | ||
const lastPoint = points[len3 - 1]; | ||
const isVeryShort = short || rightPts.length < 2 || leftPts.length < 2; | ||
if (isVeryShort && (!(taperStart || taperEnd) || isComplete)) { | ||
let ir = 0; | ||
for (let i = 0; i < len3; i++) { | ||
const { pressure, runningLength } = points[i]; | ||
if (runningLength > size) { | ||
ir = getStrokeRadius(size, thinning, easing, pressure); | ||
break; | ||
} | ||
} | ||
const start2 = sub(firstPoint.point, mul(per(uni(vec(lastPoint.point, firstPoint.point))), ir || radius)); | ||
const dotPts = []; | ||
for (let t = 0, step = 0.1; t <= 1; t += step) { | ||
dotPts.push(rotAround(start2, firstPoint.point, PI * 2 * t)); | ||
} | ||
return dotPts; | ||
} | ||
const startCap = []; | ||
const endCap = []; | ||
if (leftPts.length > 1 && rightPts.length > 1) { | ||
tr = rightPts[1]; | ||
for (let i = 1; i < leftPts.length; i++) { | ||
if (!isEqual(tr, leftPts[i])) { | ||
tl = leftPts[i]; | ||
break; | ||
} | ||
} | ||
if (capStart || taperStart) { | ||
if (!taperStart && !(taperEnd && isVeryShort)) { | ||
if (!isEqual(tr, tl)) { | ||
const start2 = sub(firstPoint.point, mul(uni(vec(tr, tl)), dist(tr, tl) / 2)); | ||
for (let t = 0, step = 0.1; t <= 1; t += step) { | ||
const pt = rotAround(start2, firstPoint.point, PI * t); | ||
if (dist(pt, tl) < 1) | ||
break; | ||
startCap.push(pt); | ||
} | ||
leftPts.shift(); | ||
rightPts.shift(); | ||
} | ||
} else { | ||
startCap.push(firstPoint.point, add(firstPoint.point, [0.1, 0.1])); | ||
} | ||
} else { | ||
if (!isEqual(tr, tl)) { | ||
const vector2 = uni(vec(tr, tl)); | ||
const dist3 = dist(tr, tl) / 2; | ||
startCap.push(sub(firstPoint.point, mul(vector2, dist3 * 0.95))); | ||
startCap.push(sub(firstPoint.point, mul(vector2, dist3))); | ||
startCap.push(add(firstPoint.point, mul(vector2, dist3))); | ||
startCap.push(add(firstPoint.point, mul(vector2, dist3 * 0.95))); | ||
leftPts.shift(); | ||
rightPts.shift(); | ||
} | ||
} | ||
const ll = leftPts[leftPts.length - 1]; | ||
const lr = rightPts[rightPts.length - 1]; | ||
const mid = med(ll, lr); | ||
const last = lastPoint.point; | ||
const vector = uni(sub(last, mid)); | ||
if (capEnd || taperEnd) { | ||
if (!taperEnd && !(taperStart && isVeryShort)) { | ||
const start2 = add(last, mul(per(vector), radius)); | ||
for (let t = 0, step = 0.1; t <= 1; t += step) { | ||
const pt = rotAround(start2, last, PI * 3 * t); | ||
if (dist(pt, lr) < 1) | ||
break; | ||
endCap.push(pt); | ||
} | ||
} else { | ||
endCap.push(last); | ||
} | ||
} else { | ||
const justBefore = lrp(mid, last, 0.95); | ||
const r = radius * 0.95; | ||
endCap.push(add(justBefore, mul(per(vector), r))); | ||
endCap.push(add(last, mul(per(vector), r))); | ||
endCap.push(sub(last, mul(per(vector), r))); | ||
endCap.push(sub(justBefore, mul(per(vector), r))); | ||
} | ||
} | ||
return leftPts.concat(endCap, rightPts.reverse(), startCap); | ||
} | ||
function getStroke(points, options = {}) { | ||
return getStrokeOutlinePoints(getStrokePoints(points, options), options); | ||
} | ||
var src_default = getStroke; | ||
export { | ||
src_default as default, | ||
getStrokeOutlinePoints, | ||
getStrokePoints | ||
}; |
import type { StrokeOptions, StrokePoint } from './types'; | ||
/** | ||
* ## getStrokePoints | ||
* @description Get points for a stroke. | ||
* @description Get points for a stroke. Returns an array of objects with an adjusted point, pressure, vector, distance, and runningLength. | ||
* @param points An array of points (as `[x, y, pressure]` or `{x, y, pressure}`). Pressure is optional. | ||
@@ -9,7 +9,7 @@ * @param streamline How much to streamline the stroke. | ||
*/ | ||
export declare function getStrokePoints<T extends number[], K extends { | ||
export declare function getStrokePoints(points: number[][] | { | ||
x: number; | ||
y: number; | ||
pressure?: number; | ||
}>(points: (T | K)[], options?: StrokeOptions): StrokePoint[]; | ||
}[], options?: StrokeOptions): StrokePoint[]; | ||
/** | ||
@@ -33,4 +33,4 @@ * ## getStrokeOutlinePoints | ||
* @description Returns a stroke as an array of outline points. | ||
* @param points An array of points (as `[x, y, pressure]` or `{x, y, pressure}`). Pressure is optional. | ||
* @param options An (optional) object with options. | ||
* @param points An array of points (as `[x, y, pressure]` or `{x, y, pressure}`). Pressure is optional in both cases. | ||
* @param options (optional) An object with options. | ||
* @param options.size The base size (diameter) of the stroke. | ||
@@ -45,7 +45,9 @@ * @param options.thinning The effect of pressure on the stroke's size. | ||
*/ | ||
export default function getStroke<T extends number[], K extends { | ||
declare function getStroke(points: number[][], options?: StrokeOptions): number[][]; | ||
declare function getStroke(points: { | ||
x: number; | ||
y: number; | ||
pressure?: number; | ||
}>(points: (T | K)[], options?: StrokeOptions): number[][]; | ||
}[], options?: StrokeOptions): number[][]; | ||
export default getStroke; | ||
export { StrokeOptions }; |
@@ -23,5 +23,10 @@ export interface StrokeOptions { | ||
pressure: number; | ||
distance: number; | ||
vector: number[]; | ||
distance: number; | ||
runningLength: number; | ||
} | ||
export declare type InputPoint = { | ||
x: number; | ||
y: number; | ||
pressure?: number; | ||
} | number[]; |
export declare function lerp(y1: number, y2: number, mu: number): number; | ||
export declare function clamp(n: number, a: number, b: number): number; | ||
/** | ||
* Convert an array of points to the correct format ([x, y, radius]) | ||
* @param points | ||
* @returns | ||
* Convert an array of points to the correct format ([x, y, pressure]) | ||
* @param points The points to format, an array of arrays or objects | ||
* @returns number[][] | ||
*/ | ||
@@ -22,2 +22,1 @@ export declare function toPointsArray<T extends number[], K extends { | ||
export declare function getStrokeRadius(size: number, thinning: number, easing: (t: number) => number, pressure?: number): number; | ||
export declare function withoutDuplicates(pts: number[][]): number[][]; |
{ | ||
"version": "0.5.2", | ||
"version": "0.5.3", | ||
"name": "perfect-freehand", | ||
@@ -35,16 +35,32 @@ "private": false, | ||
"devDependencies": { | ||
"@babel/core": "^7.15.0", | ||
"@babel/plugin-syntax-import-meta": "^7.10.4", | ||
"@babel/preset-env": "^7.15.0", | ||
"@babel/preset-react": "^7.14.5", | ||
"@babel/preset-typescript": "^7.15.0", | ||
"@testing-library/jest-dom": "^5.14.1", | ||
"@testing-library/react": "^12.0.0", | ||
"@types/jest": "^27.0.1", | ||
"@types/node": "^15.0.1", | ||
"@types/react": "^17.0.16", | ||
"@types/react": "^17.0.19", | ||
"@types/react-dom": "^17.0.9", | ||
"esbuild": "^0.12.21", | ||
"eslint": "^7.22.0", | ||
"@typescript-eslint/eslint-plugin": "^4.19.0", | ||
"@typescript-eslint/parser": "^4.19.0", | ||
"babel-jest": "^27.1.0", | ||
"eslint": "^7.32.0", | ||
"fake-indexeddb": "^3.1.3", | ||
"jest": "^27.1.0", | ||
"lerna": "^3.15.0", | ||
"react": "^17.0.2", | ||
"react-dom": "^17.0.2", | ||
"ts-node": "^9.1.1", | ||
"ts-jest": "^27.0.5", | ||
"tslib": "^2.3.0", | ||
"typedoc": "^0.20.35", | ||
"typescript": "^4.3.5" | ||
"typedoc": "^0.21.9", | ||
"typescript": "^4.4.2" | ||
}, | ||
"gitHead": "c13ecc2363bcd33a72f3c49cf72496794f2cc726" | ||
"dependencies": { | ||
"@tldraw/core": "^0.0.53", | ||
"rko": "^0.5.19" | ||
}, | ||
"gitHead": "d2a334f923a58ffa869060b5ce80281f18af919d" | ||
} |
185
src/index.ts
@@ -9,3 +9,3 @@ import { toPointsArray, getStrokeRadius } from './utils' | ||
* ## getStrokePoints | ||
* @description Get points for a stroke. | ||
* @description Get points for a stroke. Returns an array of objects with an adjusted point, pressure, vector, distance, and runningLength. | ||
* @param points An array of points (as `[x, y, pressure]` or `{x, y, pressure}`). Pressure is optional. | ||
@@ -15,41 +15,68 @@ * @param streamline How much to streamline the stroke. | ||
*/ | ||
export function getStrokePoints< | ||
T extends number[], | ||
K extends { x: number; y: number; pressure?: number } | ||
>(points: (T | K)[], options = {} as StrokeOptions): StrokePoint[] { | ||
let { streamline = 0.5 } = options | ||
export function getStrokePoints( | ||
points: number[][] | { x: number; y: number; pressure?: number }[], | ||
options = {} as StrokeOptions | ||
): StrokePoint[] { | ||
let { streamline = 0.5, last: isComplete = false } = options | ||
const { simulatePressure = true } = options | ||
streamline = streamline / (simulatePressure ? 4 : 2) | ||
// If we don't have any points, return an empty array. | ||
if (points.length === 0) return [] | ||
// Knock down the streamline (more if we're simulating pressure). | ||
streamline = streamline / (simulatePressure ? 3 : 2) | ||
// Whatever the input is, make sure that the points are in number[][]. | ||
const pts = toPointsArray(points) | ||
if (pts.length === 0) return [] | ||
// If there's only one point, add another point at a 1pt offset. | ||
if (pts.length === 1) pts.push([...vec.add(pts[0], [1, 1]), pts[0][2]]) | ||
const strokePoints: StrokePoint[] = [ | ||
{ | ||
point: [pts[0][0], pts[0][1]], | ||
pressure: pts[0][2], | ||
vector: [0, 0], | ||
distance: 0, | ||
runningLength: 0, | ||
}, | ||
] | ||
// The strokePoints array will hold the points for the stroke. | ||
// Start it out with the first point, which needs no adjustment. | ||
const strokePoints: StrokePoint[] = [] | ||
for ( | ||
let i = 1, j = 0, curr = pts[i], prev = strokePoints[j]; | ||
i < pts.length; | ||
i++, curr = pts[i], prev = strokePoints[j] | ||
) { | ||
const point = vec.lrp(prev.point, curr, 1 - streamline) | ||
let prev: StrokePoint = { | ||
point: [pts[0][0], pts[0][1]], | ||
pressure: pts[0][2], | ||
vector: [0, 0], | ||
distance: 0, | ||
runningLength: 0, | ||
} | ||
strokePoints.push(prev) | ||
// Iterate through all of the points. | ||
const len = pts.length | ||
let curr: number[] | ||
for (let i = 0; i < len; i++) { | ||
curr = pts[i] | ||
// If we're at the last point, then add the actual input point. | ||
// Otherwise, using the streamline option, interpolate a new point | ||
// between the previous point the current point. | ||
// More streamline = closer to the previous point; | ||
// less streamline = closer to the current point. | ||
// This takes a lot of the "noise" out of the input points. | ||
const point = | ||
isComplete && i === len - 1 | ||
? curr | ||
: vec.lrp(prev.point, curr, 1 - streamline) | ||
// If the new point is the same as the previous point, skip ahead. | ||
if (vec.isEqual(prev.point, point)) continue | ||
// What's the vector from the current point to the previous point? | ||
const vector = vec.uni(vec.sub(prev.point, point)) | ||
// How far is the new point from the previous point? | ||
const distance = vec.dist(point, prev.point) | ||
// Add this distance to the total "running length" of the line. | ||
const runningLength = prev.runningLength + distance | ||
const strokePoint = { | ||
// Create a new strokepoint (it will be the new "previous" one) | ||
prev = { | ||
point, | ||
@@ -62,33 +89,5 @@ pressure: curr[2], | ||
strokePoints.push(strokePoint) | ||
j += 1 // only increment j if we add an item to strokePoints | ||
strokePoints.push(prev) | ||
} | ||
/* | ||
Align vectors at the end of the line | ||
Starting from the last point, work back until we've traveled more than | ||
half of the line's size (width). Take the current point's vector and then | ||
work forward, setting all remaining points' vectors to this vector. This | ||
removes the "noise" at the end of the line and allows for a better-facing | ||
end cap. | ||
*/ | ||
// Update the length to the length of the strokePoints array. | ||
// const len = strokePoints.length | ||
// const totalLength = strokePoints[len - 1].runningLength | ||
// for (let i = len - 2; i > 1; i--) { | ||
// const { runningLength, vector } = strokePoints[i] | ||
// const dpr = vec.dpr(strokePoints[i - 1].vector, strokePoints[i].vector) | ||
// if (totalLength - runningLength > size / 2 || dpr < 0.8) { | ||
// for (let j = i; j < len; j++) { | ||
// strokePoints[j].vector = vector | ||
// } | ||
// break | ||
// } | ||
// } | ||
return strokePoints | ||
@@ -155,10 +154,21 @@ } | ||
// Previous pressure (start with average of first five pressures) | ||
let prevPressure = points | ||
.slice(0, 5) | ||
.reduce((acc, cur) => (acc + cur.pressure) / 2, points[0].pressure) | ||
// Previous pressure (start with average of first five pressures, | ||
// in order to prevent fat starts for every line. Drawn lines | ||
// almost always start slow! | ||
let prevPressure = points.slice(0, 10).reduce((acc, curr) => { | ||
let pressure = curr.pressure | ||
if (simulatePressure) { | ||
const sp = min(1, curr.distance / size) | ||
const rp = min(1, 1 - sp) | ||
pressure = min(1, acc + (rp - acc) * (sp / 4)) | ||
} | ||
return (acc + pressure) / 2 | ||
}, points[0].pressure) | ||
// The current radius | ||
let radius = getStrokeRadius(size, thinning, easing, points[len - 1].pressure) | ||
// The radius of the first saved point | ||
let firstRadius: number | undefined = undefined | ||
@@ -182,4 +192,4 @@ | ||
Iterating through the points and populate the rightPts and leftPts arrays, | ||
skipping the first and last pointsm, which will get caps later on. | ||
Iterating through the points and populate the rightPts and leftPts arrays, | ||
skipping the first and last pointsm, which will get caps later on. | ||
*/ | ||
@@ -206,6 +216,9 @@ | ||
if (thinning) { | ||
// If we're simulating pressure, then do so based on the distance | ||
// between the current point and the previous point, and the size | ||
// of the stroke. | ||
if (simulatePressure) { | ||
const rp = min(1, 1 - distance / size) | ||
const sp = min(1, distance / size) | ||
pressure = min(1, prevPressure + (rp - prevPressure) * (sp / 2)) | ||
const rp = min(1, 1 - sp) | ||
pressure = min(1, prevPressure + (rp - prevPressure) * (sp / 4)) | ||
} | ||
@@ -388,3 +401,5 @@ | ||
for (let t = 0, step = 0.1; t <= 1; t += step) { | ||
startCap.push(vec.rotAround(start, firstPoint.point, PI * t)) | ||
const pt = vec.rotAround(start, firstPoint.point, PI * t) | ||
if (vec.dist(pt, tl) < 1) break | ||
startCap.push(pt) | ||
} | ||
@@ -422,8 +437,14 @@ leftPts.shift() | ||
const lastLeft = leftPts[leftPts.length - 1] | ||
const lastRight = rightPts[rightPts.length - 1] | ||
const mid = vec.med(lastLeft, lastRight) | ||
const last = options.last | ||
? lastPoint.point | ||
: vec.lrp(mid, lastPoint.point, 0.618) | ||
// The last left point | ||
const ll = leftPts[leftPts.length - 1] | ||
// The last right point | ||
const lr = rightPts[rightPts.length - 1] | ||
// The point between the two | ||
const mid = vec.med(ll, lr) | ||
// The last provided point | ||
const last = lastPoint.point | ||
const vector = vec.uni(vec.sub(last, mid)) | ||
@@ -433,7 +454,11 @@ | ||
if (!taperEnd && !(taperStart && isVeryShort)) { | ||
// Draw the end cap | ||
const start = vec.add(last, vec.mul(vec.per(vector), radius)) | ||
for (let t = 0, step = 0.15; t <= 1; t += step) { | ||
endCap.push(vec.rotAround(start, last, PI * 3 * t)) | ||
for (let t = 0, step = 0.1; t <= 1; t += step) { | ||
const pt = vec.rotAround(start, last, PI * 3 * t) | ||
if (vec.dist(pt, lr) < 1) break | ||
endCap.push(pt) | ||
} | ||
} else { | ||
// Just push the last point to the line | ||
endCap.push(last) | ||
@@ -463,4 +488,4 @@ } | ||
* @description Returns a stroke as an array of outline points. | ||
* @param points An array of points (as `[x, y, pressure]` or `{x, y, pressure}`). Pressure is optional. | ||
* @param options An (optional) object with options. | ||
* @param points An array of points (as `[x, y, pressure]` or `{x, y, pressure}`). Pressure is optional in both cases. | ||
* @param options (optional) An object with options. | ||
* @param options.size The base size (diameter) of the stroke. | ||
@@ -475,9 +500,15 @@ * @param options.thinning The effect of pressure on the stroke's size. | ||
*/ | ||
export default function getStroke< | ||
T extends number[], | ||
K extends { x: number; y: number; pressure?: number } | ||
>(points: (T | K)[], options: StrokeOptions = {} as StrokeOptions): number[][] { | ||
function getStroke(points: number[][], options?: StrokeOptions): number[][] | ||
function getStroke( | ||
points: { x: number; y: number; pressure?: number }[], | ||
options?: StrokeOptions | ||
): number[][] | ||
function getStroke( | ||
points: number[][] | { x: number; y: number; pressure?: number }[], | ||
options: StrokeOptions = {} as StrokeOptions | ||
): number[][] { | ||
return getStrokeOutlinePoints(getStrokePoints(points, options), options) | ||
} | ||
export default getStroke | ||
export { StrokeOptions } |
@@ -24,5 +24,7 @@ export interface StrokeOptions { | ||
pressure: number | ||
distance: number | ||
vector: number[] | ||
distance: number | ||
runningLength: number | ||
} | ||
export type InputPoint = { x: number; y: number; pressure?: number } | number[] |
@@ -12,5 +12,5 @@ import { isEqual } from './vec' | ||
/** | ||
* Convert an array of points to the correct format ([x, y, radius]) | ||
* @param points | ||
* @returns | ||
* Convert an array of points to the correct format ([x, y, pressure]) | ||
* @param points The points to format, an array of arrays or objects | ||
* @returns number[][] | ||
*/ | ||
@@ -28,7 +28,9 @@ export function toPointsArray< | ||
} else { | ||
return (points as { | ||
x: number | ||
y: number | ||
pressure?: number | ||
}[]).map(({ x, y, pressure = 0.5 }) => [x, y, pressure]) | ||
return ( | ||
points as { | ||
x: number | ||
y: number | ||
pressure?: number | ||
}[] | ||
).map(({ x, y, pressure = 0.5 }) => [x, y, pressure]) | ||
} | ||
@@ -59,15 +61,1 @@ } | ||
} | ||
export function withoutDuplicates(pts: number[][]) { | ||
const unique: number[][] = [] | ||
let prev: number[] | undefined = undefined | ||
for (let pt of pts) { | ||
if (prev && isEqual(prev, pt)) continue | ||
unique.push(pt) | ||
prev = pt | ||
} | ||
return pts | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
92141
1428
2
24
+ Added@tldraw/core@^0.0.53
+ Addedrko@^0.5.19
+ Added@tldraw/core@0.0.53(transitive)
+ Addeddeepmerge@4.3.1(transitive)
+ Addedidb-keyval@5.1.5(transitive)
+ Addedismobilejs@1.1.1(transitive)
+ Addedjs-tokens@4.0.0(transitive)
+ Addedloose-envify@1.4.0(transitive)
+ Addedobject-assign@4.1.1(transitive)
+ Addedreact@17.0.2(transitive)
+ Addedreact-dom@17.0.2(transitive)
+ Addedreact-use-gesture@9.1.3(transitive)
+ Addedrko@0.5.25(transitive)
+ Addedsafari-14-idb-fix@1.0.6(transitive)
+ Addedscheduler@0.20.2(transitive)
+ Addedzustand@3.7.2(transitive)