Skip to content

Instantly share code, notes, and snippets.

@steveruizok
Created September 21, 2021 11:56
Show Gist options
  • Save steveruizok/39b5c0831a68105ef66eeae47dfc3a37 to your computer and use it in GitHub Desktop.
Save steveruizok/39b5c0831a68105ef66eeae47dfc3a37 to your computer and use it in GitHub Desktop.
Perfect freehand in Baku's pen tool.
/*
{
"id": "bga2899d28",
"label": "Perfect",
"icon": "✍︎",
"parameters": [
{
"name": "size",
"type": "float",
"default": "16"
},
{
"name": "thinning",
"type": "float",
"default": ".5"
},
{
"name": "streamline",
"type": "float",
"default": ".5"
},
{
"name": "smoothing",
"type": "float",
"default": ".5"
},
{
"name": "strokeColor",
"type": "color",
"default": "#282a2e"
},
{
"name": "smoothing",
"type": "float",
"default": ".5"
},
{
"name": "strokeWidth",
"type": "float",
"default": 2
},
{
"name": "strokeColor",
"type": "color",
"default": "#282a2e"
},
{
"name": "fillColor",
"type": "color",
"default": "#282a2e"
}
]
}
*/
function Y(e,t,u,S=g=>g){return e*S(.5-t*(.5-u))}function p(e,t){return[e[0]+t[0],e[1]+t[1]]}function m(e,t){return[e[0]-t[0],e[1]-t[1]]}function f(e,t){return[e[0]*t,e[1]*t]}function fe(e,t){return[e[0]/t,e[1]/t]}function I(e){return[e[1],-e[0]]}function re(e,t){return e[0]*t[0]+e[1]*t[1]}function oe(e,t){return e[0]===t[0]&&e[1]===t[1]}function be(e){return Math.hypot(e[0],e[1])}function ge(e){return e[0]*e[0]+e[1]*e[1]}function Z(e,t){return ge(m(e,t))}function K(e){return fe(e,be(e))}function se(e,t){return Math.hypot(e[1]-t[1],e[0]-t[0])}function ue(e,t){return f(p(e,t),.5)}function V(e,t,u){let S=Math.sin(u),g=Math.cos(u),y=e[0]-t[0],o=e[1]-t[1],x=y*g-o*S,L=y*S+o*g;return[x+t[0],L+t[1]]}function N(e,t,u){return p(e,f(m(t,e),u))}function $(e,t,u){return p(e,f(t,u))}var{min:_,PI:de}=Math,ie=.275,j=de+1e-4;function pe(e,t={}){let{size:u=16,smoothing:S=.5,thinning:g=.5,simulatePressure:y=!0,easing:o=n=>n,start:x={},end:L={},last:q=!1}=t,{cap:k=!0,taper:P=0,easing:C=n=>n*(2-n)}=x,{cap:b=!0,taper:a=0,easing:T=n=>--n*n*n+1}=L;if(e.length===0||u<=0)return[];let U=e[e.length-1].runningLength,B=Math.pow(u*S,2),O=[],v=[],X=e.slice(0,10).reduce((n,i)=>{let r=i.pressure;if(y){let s=_(1,i.distance/u),l=_(1,1-s);r=_(1,n+(l-n)*(s*ie))}return(n+r)/2},e[0].pressure),c=Y(u,g,e[e.length-1].pressure,o),J,A=e[0].vector,z=e[0].point,F=z,E=z,M=F;for(let n=0;n<e.length-1;n++){let{pressure:i}=e[n],{point:r,vector:s,distance:l,runningLength:R}=e[n];if(U-R<3)continue;if(g){if(y){let D=_(1,l/u),W=_(1,1-D);i=_(1,X+(W-X)*(D*ie))}c=Y(u,g,i,o)}else c=u/2;J===void 0&&(J=c);let ae=R<P?C(R/P):1,le=U-R<a?T((U-R)/a):1;c=Math.max(.01,c*Math.min(ae,le));let ee=e[n+1].vector,te=re(s,ee);if(te<0){let D=f(I(A),c);for(let W=1/13,H=0;H<=1;H+=W)E=V(m(r,D),r,j*H),O.push(E),M=V(p(r,D),r,j*-H),v.push(M);z=E,F=M;continue}let ne=f(I(N(ee,s,te)),c);E=m(r,ne),(n===0||Z(z,E)>B)&&(O.push(E),z=E),M=p(r,ne),(n===0||Z(F,M)>B)&&(v.push(M),F=M),X=i,A=s}let d=e[0].point.slice(0,2),h=e.length>1?e[e.length-1].point.slice(0,2):p(e[0].point,[1,1]),Q=O.length<=1||v.length<=1,w=[],G=[];if(Q){if(!(P||a)||q){let n=$(d,K(I(m(d,h))),-(J||c)),i=[];for(let r=1/13,s=r;s<=1;s+=r)i.push(V(n,d,j*2*s));return i}}else{if(P||a&&Q)w.push(d,p(d,[.1,0]));else if(k)for(let r=1/13,s=r;s<=1;s+=r){let l=V(v[0],d,j*s);w.push(l)}else{let r=m(O[0],v[0]),s=f(r,.5),l=f(r,.51);w.push(m(d,s),m(d,l),p(d,l),p(d,s))}let n=ue(O[O.length-1],v[v.length-1]),i=I(K(m(h,n)));if(a||P&&Q)G.push(h,p(h,[1,0]));else if(b){let r=$(h,i,c);for(let s=1/29,l=0;l<=1;l+=s){let R=V(r,h,j*3*l);G.push(R)}}else G.push(p(h,f(i,c)),p(h,f(i,c*.99)),m(h,f(i,c*.99)),m(h,f(i,c)))}return O.concat(G,v.reverse(),w)}function me(e,t={}){var C;let{streamline:u=.5,size:S=16,last:g=!1}=t;if(e.length===0)return[];let y=.15+(1-u)*.85,o=Array.isArray(e[0])?e:e.map(({x:b,y:a,pressure:T=.5})=>[b,a,T]);o.length===1&&(o=[...o,[...p(o[0],[1,1]),...o[0].slice(2)]]);let x=[{point:[o[0][0],o[0][1]],pressure:o[0][2]||.25,vector:[1,1],distance:0,runningLength:0}],L=!1,q=0,k=x[0],P=o.length-1;for(let b=1;b<o.length;b++){let a=g&&b===P?o[b]:N(k.point,o[b],y);if(oe(k.point,a))continue;let T=se(a,k.point);if(q+=T,b<P&&!L){if(q<S)continue;L=!0}k={point:a,pressure:o[b][2]||.5,vector:K(m(k.point,a)),distance:T,runningLength:q},x.push(k)}return x[0].vector=((C=x[1])==null?void 0:C.vector)||[0,0],x}function ce(e,t={}){return pe(me(e,t),t)}var qe=ce;
function getSvgPathFromStroke(stroke) {
if (!stroke.length) return ''
const d = stroke.reduce(
(acc, [x0, y0], i, arr) => {
const [x1, y1] = arr[(i + 1) % arr.length]
acc.push(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2)
return acc
},
['M', ...stroke[0], 'Q']
)
d.push('Z')
return d.join(' ')
}
let stroke, path, points = []
const getStroke = ce
function begin() {
}
function press() {
points = []
points.push([mouse.x, mouse.y])
path=new Path()
path.strokeColor = strokeColor
path.fillColor = fillColor
path.strokeWidth = strokeWidth
}
function drag() {
points.push([mouse.x, mouse.y])
stroke = getStroke(points, {
size,
thinning,
streamline,
smoothing,
})
pathData = getSvgPathFromStroke(stroke)
// Remove the old path
path?.remove()
// Create a new path
path=new Path(pathData)
path.strokeColor = strokeColor
path.fillColor = fillColor
path.strokeWidth = strokeWidth
}
function release() {
}
function move() {
}
function end() {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment