perfect-freehand
Advanced tools
Comparing version 0.2.4 to 0.2.5
@@ -0,1 +1,10 @@ | ||
# 0.2.5 | ||
- Improves caps for start and end. | ||
- Improves handling of short moves. | ||
# 0.2.4 | ||
- Improves sharp corners. | ||
# 0.2.3 | ||
@@ -2,0 +11,0 @@ |
import polygonClipping from 'polygon-clipping'; | ||
export declare function lerpAngles(a0: number, a1: number, t: number): number; | ||
export interface StrokePointsOptions { | ||
@@ -3,0 +4,0 @@ streamline?: number; |
@@ -53,3 +53,4 @@ 'use strict'; | ||
var hypot = Math.hypot, | ||
var abs = Math.abs, | ||
hypot = Math.hypot, | ||
cos = Math.cos, | ||
@@ -60,4 +61,5 @@ max = Math.max, | ||
atan2 = Math.atan2, | ||
PI = Math.PI; | ||
var TAU = PI / 2; | ||
PI = Math.PI, | ||
TAU = PI / 2, | ||
PI2 = PI * 2; | ||
@@ -69,3 +71,3 @@ function projectPoint(x0, y0, a, d) { | ||
function shortAngleDist(a0, a1) { | ||
var max = Math.PI * 2; | ||
var max = PI2; | ||
var da = (a1 - a0) % max; | ||
@@ -75,2 +77,6 @@ return 2 * da % max - da; | ||
function lerpAngles(a0, a1, t) { | ||
return a0 + shortAngleDist(a0, a1) * t; | ||
} | ||
function angleDelta(a0, a1) { | ||
@@ -88,2 +94,10 @@ return shortAngleDist(a0, a1); | ||
function getAngle(x0, y0, x1, y1) { | ||
return atan2(y1 - y0, x1 - x0); | ||
} | ||
function getDistance(x0, y0, x1, y1) { | ||
return hypot(y1 - y0, x1 - x0); | ||
} | ||
function clamp(n, a, b) { | ||
@@ -154,9 +168,11 @@ return max(a, min(b, n)); | ||
x = px + (ix - px) * (1 - streamline); | ||
y = py + (iy - py) * (1 - streamline); // Angle | ||
y = py + (iy - py) * (1 - streamline); // Distance | ||
angle = atan2(y - py, x - px); // Distance | ||
distance = getDistance(x, y, px, py); // Angle | ||
distance = hypot(y - py, x - px); | ||
angle = getAngle(px, py, x, y); // If distance is very short, blend the angles | ||
if (distance < 1) angle = lerpAngles(prev[2], angle, 0.5); | ||
length += distance; | ||
prev = [x, y, ip, angle, distance, length]; | ||
prev = [x, y, angle, ip, distance, length]; | ||
pts.push(prev); | ||
@@ -193,11 +209,22 @@ } // Assign second angle to first point | ||
return []; | ||
} // Draw a kind of shitty shape around the start and end points. | ||
} | ||
var _points$ = points[0], | ||
x0 = _points$[0], | ||
y0 = _points$[1], | ||
_points = points[len - 1], | ||
x1 = _points[0], | ||
y1 = _points[1], | ||
p = points[len - 1][3], | ||
leftPts = [], | ||
rightPts = [], | ||
size = clamp(minSize + (maxSize - minSize) * (p ? p : 0.5), minSize, maxSize), | ||
angle = x0 === x1 ? 0 : getAngle(x0, y0, x1, y1); | ||
var p0 = points[0], | ||
p1 = points[len - 1], | ||
size = p0[2] === p1[2] ? maxSize : minSize + (maxSize - minSize) * p1[2], | ||
a = p0 === p1 ? Math.random() * (PI * 2) : atan2(p1[1] - p0[1], p1[0] - p0[0]), | ||
m = getPointBetween(p0[0], p0[1], p1[0], p1[1], 0.5); | ||
return [projectPoint(m[0], m[1], a + TAU, size), projectPoint(p0[0], p0[1], a + PI, size), projectPoint(m[0], m[1], a - TAU, size), projectPoint(p1[0], p1[1], a, size), projectPoint(m[0], m[1], a + TAU, size)]; | ||
for (var t = 0, step = 0.1; t <= 1; t += step) { | ||
leftPts.push(projectPoint(x1, y1, angle + TAU - t * PI, size - 1)); | ||
rightPts.push(projectPoint(x0, y0, angle + TAU + t * PI, size - 1)); | ||
} | ||
return leftPts.concat(rightPts.reverse()); | ||
} | ||
@@ -236,6 +263,6 @@ /** | ||
pp = 0.5, | ||
prev = p1, | ||
started = false, | ||
length = 0, | ||
leftPts = [], | ||
rightPts = [], | ||
leftPts = [p0], | ||
rightPts = [p0], | ||
d0, | ||
@@ -251,9 +278,14 @@ d1; | ||
for (var i = 1; i < len; i++) { | ||
var _points2 = points[i - 1], | ||
px = _points2[0], | ||
py = _points2[1], | ||
pa = _points2[2]; | ||
var _points$i = points[i], | ||
x = _points$i[0], | ||
y = _points$i[1], | ||
ip = _points$i[2], | ||
angle = _points$i[3], | ||
distance = _points$i[4]; | ||
length += distance; // Size | ||
angle = _points$i[2], | ||
ip = _points$i[3], | ||
distance = _points$i[4], | ||
clen = _points$i[5]; | ||
length += clen; // Size | ||
@@ -272,2 +304,19 @@ if (pressure) { | ||
size = maxSize; | ||
} // Handle line start | ||
if (!started && length > size / 2) { | ||
var _points$2 = points[0], | ||
sx = _points$2[0], | ||
sy = _points$2[1]; | ||
for (var t = 0, step = 0.25; t <= 1; t += step) { | ||
m0 = projectPoint(sx, sy, angle + TAU + t * PI, size - 1); | ||
leftPts.push(m0); | ||
m1 = projectPoint(sx, sy, angle - TAU + t * -PI, size - 1); | ||
rightPts.push(m1); | ||
} | ||
started = true; | ||
continue; | ||
} // 3. Shape | ||
@@ -279,55 +328,35 @@ | ||
p1 = projectPoint(x, y, angle + TAU, size); // right | ||
// // Add more points to the first and p1 points | ||
if (i === 0) { | ||
t0 = p0; | ||
t1 = p1; | ||
var delta = angleDelta(pa, angle); // Handle sharp corners differently | ||
for (var t = 0, step = 0.33; t <= 1; t += step) { | ||
m1 = projectPoint(prev[0], prev[1], angle - TAU + t * -PI, size * 2); | ||
if (i === points.length - 1 || abs(delta) > PI * 0.75 && length > size) { | ||
var _getPointBetween = getPointBetween(px, py, x, y, 0.5), | ||
mx = _getPointBetween[0], | ||
my = _getPointBetween[1]; | ||
for (var _t = 0, _step = 0.25; _t <= 1; _t += _step) { | ||
m0 = projectPoint(mx, my, pa - TAU + _t * PI, size - 1); | ||
leftPts.push(m0); | ||
m1 = projectPoint(mx, my, pa + TAU + _t * -PI, size - 1); | ||
rightPts.push(m1); | ||
t1 = m1; | ||
} | ||
t0 = m0; | ||
t1 = m1; | ||
} else { | ||
var delta = angleDelta(angle, prev[2]); // Handle sharp corners differently | ||
// Project sideways | ||
d0 = getDistance(p0[0], p0[1], t0[0], t0[1]); | ||
if (Math.abs(delta) > PI * 0.72 && length > size * 2) { | ||
if (delta > 0) { | ||
m0 = prev; | ||
leftPts.push(m0); | ||
t0 = m0; | ||
if (d0 > smooth) { | ||
leftPts.push(m0); | ||
m0 = getPointBetween(t0[0], t0[1], p0[0], p0[1], 0.5); | ||
t0 = p0; | ||
} | ||
for (var _t = 0, _step = 0.3; _t <= 1; _t += _step) { | ||
m1 = projectPoint(prev[0], prev[1], angle - TAU + _t * -PI, size); | ||
rightPts.push(m1); | ||
t1 = m1; | ||
} | ||
} else { | ||
for (var _t2 = 0, _step2 = 0.3; _t2 <= 1; _t2 += _step2) { | ||
m0 = projectPoint(prev[0], prev[1], angle + TAU + _t2 * PI, size); | ||
leftPts.push(m0); | ||
t0 = m0; | ||
} | ||
d1 = getDistance(p1[0], p1[1], t1[0], t1[1]); | ||
m1 = prev; | ||
rightPts.push(m1); | ||
t1 = m1; | ||
} | ||
} else { | ||
// Project sideways | ||
d0 = Math.hypot(p0[0] - t0[0], p0[1] - t0[1]); | ||
if (d0 > smooth) { | ||
leftPts.push(m0); | ||
m0 = getPointBetween(t0[0], t0[1], p0[0], p0[1], 0.5); | ||
t0 = p0; | ||
} | ||
d1 = Math.hypot(p1[0] - t1[0], p1[1] - t1[1]); | ||
if (d1 > smooth) { | ||
rightPts.push(m1); | ||
m1 = getPointBetween(t1[0], t1[1], p1[0], p1[1], 0.5); | ||
t1 = p1; | ||
} | ||
if (d1 > smooth) { | ||
rightPts.push(m1); | ||
m1 = getPointBetween(t1[0], t1[1], p1[0], p1[1], 0.5); | ||
t1 = p1; | ||
} | ||
@@ -337,7 +366,4 @@ } | ||
pp = ip; | ||
prev = [x, y, angle]; | ||
} | ||
leftPts.push(prev); | ||
rightPts.push(prev); | ||
return leftPts.concat(rightPts.reverse()); | ||
@@ -377,3 +403,3 @@ } | ||
totalLength = ps[ps.length - 1][5], | ||
pts = totalLength < maxSize * 2 ? getShortStrokeOutlinePoints(ps, options) : getStrokeOutlinePoints(ps, options), | ||
pts = totalLength < maxSize ? getShortStrokeOutlinePoints(ps, options) : getStrokeOutlinePoints(ps, options), | ||
d = []; // If the length is too short, just draw a dot. | ||
@@ -385,7 +411,7 @@ // If we're clipping the path, then find the polygon and add its faces. | ||
for (var _iterator = _createForOfIteratorHelperLoose(poly), _step3; !(_step3 = _iterator()).done;) { | ||
var face = _step3.value; | ||
for (var _iterator = _createForOfIteratorHelperLoose(poly), _step2; !(_step2 = _iterator()).done;) { | ||
var face = _step2.value; | ||
for (var _iterator2 = _createForOfIteratorHelperLoose(face), _step4; !(_step4 = _iterator2()).done;) { | ||
var verts = _step4.value; | ||
for (var _iterator2 = _createForOfIteratorHelperLoose(face), _step3; !(_step3 = _iterator2()).done;) { | ||
var verts = _step3.value; | ||
var v0 = verts[0]; | ||
@@ -397,5 +423,5 @@ var v1 = verts[1]; | ||
for (var i = 1; i < verts.length; i++) { | ||
var _getPointBetween = getPointBetween(v0[0], v0[1], v1[0], v1[1], 0.5), | ||
mpx = _getPointBetween[0], | ||
mpy = _getPointBetween[1]; | ||
var _getPointBetween2 = getPointBetween(v0[0], v0[1], v1[0], v1[1], 0.5), | ||
mpx = _getPointBetween2[0], | ||
mpy = _getPointBetween2[1]; | ||
@@ -416,5 +442,5 @@ d.push(" Q " + v0[0] + "," + v0[1] + " " + mpx + "," + mpy); | ||
for (var _i = 1; _i < pts.length; _i++) { | ||
var _getPointBetween2 = getPointBetween(_v[0], _v[1], _v2[0], _v2[1], 0.5), | ||
_mpx = _getPointBetween2[0], | ||
_mpy = _getPointBetween2[1]; | ||
var _getPointBetween3 = getPointBetween(_v[0], _v[1], _v2[0], _v2[1], 0.5), | ||
_mpx = _getPointBetween3[0], | ||
_mpy = _getPointBetween3[1]; | ||
@@ -427,2 +453,3 @@ d.push("Q " + _v[0] + "," + _v[1] + " " + _mpx + "," + _mpy); | ||
d.push('Z'); | ||
return d.join(' '); | ||
@@ -436,2 +463,3 @@ } | ||
exports.getStrokePoints = getStrokePoints; | ||
exports.lerpAngles = lerpAngles; | ||
//# sourceMappingURL=perfect-freehand.cjs.development.js.map |
@@ -1,2 +0,2 @@ | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var r,t=(r=require("polygon-clipping"))&&"object"==typeof r&&"default"in r?r.default:r;function e(r,t){(null==t||t>r.length)&&(t=r.length);for(var e=0,n=new Array(t);e<t;e++)n[e]=r[e];return n}function n(r,t){var n;if("undefined"==typeof Symbol||null==r[Symbol.iterator]){if(Array.isArray(r)||(n=function(r,t){if(r){if("string"==typeof r)return e(r,void 0);var n=Object.prototype.toString.call(r).slice(8,-1);return"Object"===n&&r.constructor&&(n=r.constructor.name),"Map"===n||"Set"===n?Array.from(r):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?e(r,void 0):void 0}}(r))||t&&r&&"number"==typeof r.length){n&&(r=n);var o=0;return function(){return o>=r.length?{done:!0}:{done:!1,value:r[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=r[Symbol.iterator]()).next.bind(n)}var o=Math.hypot,i=Math.cos,a=Math.max,u=Math.min,v=Math.sin,s=Math.atan2,f=Math.PI,h=f/2;function l(r,t,e,n){return[i(e)*n+r,v(e)*n+t]}function p(r,t,e,n,o){return void 0===o&&(o=.5),[r+(e-r)*o,t+(n-t)*o]}function d(r,t){void 0===t&&(t={});var e,n,i,a=t.streamline,u=void 0===a?.5:a,v=function(r){return Array.isArray(r[0])?r.map((function(r){var t=r[2];return[r[0],r[1],void 0===t?.5:t]})):r.map((function(r){var t=r.pressure;return[r.x,r.y,void 0===t?.5:t]}))}(r),f=0,h=.01,l=v.length,p=[].concat(v[0],[0,0,0]),d=[p];if(0===l)return[];for(var c=1;c<l;c++){var m=v[c],y=m[2],g=p[0],M=p[1];i=s((n=M+(m[1]-M)*(1-u))-M,(e=g+(m[0]-g)*(1-u))-g),h=o(n-M,e-g),d.push(p=[e,n,y,i,h,f+=h])}return d.length>1&&(d[0][2]=d[1][2]),d}function c(r,t){void 0===t&&(t={});var e=t.minSize,n=void 0===e?2.5:e,o=t.maxSize,i=void 0===o?8:o,a=r.length;if(0===a)return[];var u=r[0],v=r[a-1],d=u[2]===v[2]?i:n+(i-n)*v[2],c=u===v?Math.random()*(2*f):s(v[1]-u[1],v[0]-u[0]),m=p(u[0],u[1],v[0],v[1],.5);return[l(m[0],m[1],c+h,d),l(u[0],u[1],c+f,d),l(m[0],m[1],c-h,d),l(v[0],v[1],c,d),l(m[0],m[1],c+h,d)]}function m(r,t){void 0===t&&(t={});var e=t.simulatePressure,n=void 0===e||e,o=t.pressure,i=void 0===o||o,v=t.minSize,s=void 0===v?2.5:v,d=t.maxSize,c=void 0===d?8:d,m=t.smooth,y=void 0===m?8:m,g=r.length,M=r[0],b=r[0],S=M,x=b,A=M,P=M,j=0,z=.5,I=b,O=0,k=[],w=[];if(0===g)return[];for(var Q=1;Q<g;Q++){var _=r[Q],q=_[0],C=_[1],E=_[2],T=_[3],U=_[4];if(O+=U,i){if(n){var $=u(1-U/c,1),B=u(U/c,1);E=u(1,z+B/2*($-z))}j=a(s,u(c,s+E*(c-s)))}else j=c;if(M=l(q,C,T-h,j),b=l(q,C,T+h,j),0===Q){S=M,x=b;for(var D=0;D<=1;D+=.33)P=l(I[0],I[1],T-h+D*-f,2*j),w.push(P),x=P}else{var F=function(r,t){var e=2*Math.PI,n=(t-r)%e;return 2*n%e-n}(T,I[2]);if(Math.abs(F)>.72*f&&O>2*j)if(F>0){k.push(A=I),S=A;for(var G=0;G<=1;G+=.3)P=l(I[0],I[1],T-h+G*-f,j),w.push(P),x=P}else{for(var H=0;H<=1;H+=.3)A=l(I[0],I[1],T+h+H*f,j),k.push(A),S=A;w.push(P=I),x=P}else Math.hypot(M[0]-S[0],M[1]-S[1])>y&&(k.push(A),A=p(S[0],S[1],M[0],M[1],.5),S=M),Math.hypot(b[0]-x[0],b[1]-x[1])>y&&(w.push(P),P=p(x[0],x[1],b[0],b[1],.5),x=b)}z=E,I=[q,C,T]}return k.push(I),w.push(I),k.concat(w.reverse())}function y(r){return t.union([r])}exports.clipPath=y,exports.default=function(r,t){if(void 0===t&&(t={}),0===r.length)return"";var e=t.clip,o=void 0===e||e,i=t.maxSize,a=void 0===i?8:i,u=d(r,t),v=u[u.length-1][5]<2*a?c(u,t):m(u,t),s=[];if(o)for(var f,h=n(y(v));!(f=h()).done;)for(var l,g=n(f.value);!(l=g()).done;){var M=l.value,b=M[0],S=M[1];M.push(b),s.push("M "+b[0]+" "+b[1]);for(var x=1;x<M.length;x++){var A=p(b[0],b[1],S[0],S[1],.5);s.push(" Q "+b[0]+","+b[1]+" "+A[0]+","+A[1]),b=S,S=M[x+1]}}else{var P=v[0],j=v[1];v.push(P),s.push("M "+P[0]+" "+P[1]);for(var z=1;z<v.length;z++){var I=p(P[0],P[1],j[0],j[1],.5);s.push("Q "+P[0]+","+P[1]+" "+I[0]+","+I[1]),P=j,j=v[z+1]}}return s.join(" ")},exports.getShortStrokeOutlinePoints=c,exports.getStrokeOutlinePoints=m,exports.getStrokePoints=d; | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var r,t=(r=require("polygon-clipping"))&&"object"==typeof r&&"default"in r?r.default:r;function e(r,t){(null==t||t>r.length)&&(t=r.length);for(var e=0,n=new Array(t);e<t;e++)n[e]=r[e];return n}function n(r,t){var n;if("undefined"==typeof Symbol||null==r[Symbol.iterator]){if(Array.isArray(r)||(n=function(r,t){if(r){if("string"==typeof r)return e(r,void 0);var n=Object.prototype.toString.call(r).slice(8,-1);return"Object"===n&&r.constructor&&(n=r.constructor.name),"Map"===n||"Set"===n?Array.from(r):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?e(r,void 0):void 0}}(r))||t&&r&&"number"==typeof r.length){n&&(r=n);var o=0;return function(){return o>=r.length?{done:!0}:{done:!1,value:r[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=r[Symbol.iterator]()).next.bind(n)}var o=Math.abs,i=Math.hypot,u=Math.cos,a=Math.max,s=Math.min,v=Math.sin,f=Math.atan2,l=Math.PI,p=l/2,h=2*l;function c(r,t,e,n){return[u(e)*n+r,v(e)*n+t]}function d(r,t){var e=(t-r)%h;return 2*e%h-e}function m(r,t,e){return r+d(r,t)*e}function g(r,t,e,n,o){return void 0===o&&(o=.5),[r+(e-r)*o,t+(n-t)*o]}function y(r,t,e,n){return f(n-t,e-r)}function b(r,t,e,n){return i(n-t,e-r)}function S(r,t,e){return a(t,s(e,r))}function x(r,t){void 0===t&&(t={});var e,n,o,i=t.streamline,u=void 0===i?.5:i,a=function(r){return Array.isArray(r[0])?r.map((function(r){var t=r[2];return[r[0],r[1],void 0===t?.5:t]})):r.map((function(r){var t=r.pressure;return[r.x,r.y,void 0===t?.5:t]}))}(r),s=0,v=.01,f=a.length,l=[].concat(a[0],[0,0,0]),p=[l];if(0===f)return[];for(var h=1;h<f;h++){var c=a[h],d=c[2],g=l[0],S=l[1];v=b(e=g+(c[0]-g)*(1-u),n=S+(c[1]-S)*(1-u),g,S),o=y(g,S,e,n),v<1&&(o=m(l[2],o,.5)),p.push(l=[e,n,o,d,v,s+=v])}return p.length>1&&(p[0][2]=p[1][2]),p}function M(r,t){void 0===t&&(t={});var e=t.minSize,n=void 0===e?2.5:e,o=t.maxSize,i=void 0===o?8:o,u=r.length;if(0===u)return[];for(var a=r[0],s=a[0],v=a[1],f=r[u-1],h=f[0],d=f[1],m=[],g=[],b=S(n+(i-n)*(r[u-1][3]||.5),n,i),x=s===h?0:y(s,v,h,d),M=0;M<=1;M+=.1)m.push(c(h,d,x+p-M*l,b-1)),g.push(c(s,v,x+p+M*l,b-1));return m.concat(g.reverse())}function A(r,t){void 0===t&&(t={});var e=t.simulatePressure,n=void 0===e||e,i=t.pressure,u=void 0===i||i,a=t.minSize,v=void 0===a?2.5:a,f=t.maxSize,h=void 0===f?8:f,m=t.smooth,y=void 0===m?8:m,x=r.length,M=r[0],A=r[0],P=M,j=A,z=M,O=M,I=0,k=.5,w=!1,Q=0,_=[M],q=[M];if(0===x)return[];for(var C=1;C<x;C++){var E=r[C-1],T=E[0],U=E[1],Z=E[2],$=r[C],B=$[0],D=$[1],F=$[2],G=$[3],H=$[4];if(Q+=$[5],u){if(n){var J=s(1-H/h,1),K=s(H/h,1);G=s(1,k+K/2*(J-k))}I=S(v+G*(h-v),v,h)}else I=h;if(!w&&Q>I/2){for(var L=r[0],N=L[0],R=L[1],V=0;V<=1;V+=.25)z=c(N,R,F+p+V*l,I-1),_.push(z),O=c(N,R,F-p+V*-l,I-1),q.push(O);w=!0}else{M=c(B,D,F-p,I),A=c(B,D,F+p,I);var W=d(Z,F);if(C===r.length-1||o(W)>.75*l&&Q>I){for(var X=g(T,U,B,D,.5),Y=X[0],rr=X[1],tr=0;tr<=1;tr+=.25)z=c(Y,rr,Z-p+tr*l,I-1),_.push(z),O=c(Y,rr,Z+p+tr*-l,I-1),q.push(O);P=z,j=O}else b(M[0],M[1],P[0],P[1])>y&&(_.push(z),z=g(P[0],P[1],M[0],M[1],.5),P=M),b(A[0],A[1],j[0],j[1])>y&&(q.push(O),O=g(j[0],j[1],A[0],A[1],.5),j=A);k=G}}return _.concat(q.reverse())}function P(r){return t.union([r])}exports.clipPath=P,exports.default=function(r,t){if(void 0===t&&(t={}),0===r.length)return"";var e=t.clip,o=void 0===e||e,i=t.maxSize,u=void 0===i?8:i,a=x(r,t),s=a[a.length-1][5]<u?M(a,t):A(a,t),v=[];if(o)for(var f,l=n(P(s));!(f=l()).done;)for(var p,h=n(f.value);!(p=h()).done;){var c=p.value,d=c[0],m=c[1];c.push(d),v.push("M "+d[0]+" "+d[1]);for(var y=1;y<c.length;y++){var b=g(d[0],d[1],m[0],m[1],.5);v.push(" Q "+d[0]+","+d[1]+" "+b[0]+","+b[1]),d=m,m=c[y+1]}}else{var S=s[0],j=s[1];s.push(S),v.push("M "+S[0]+" "+S[1]);for(var z=1;z<s.length;z++){var O=g(S[0],S[1],j[0],j[1],.5);v.push("Q "+S[0]+","+S[1]+" "+O[0]+","+O[1]),S=j,j=s[z+1]}}return v.push("Z"),v.join(" ")},exports.getShortStrokeOutlinePoints=M,exports.getStrokeOutlinePoints=A,exports.getStrokePoints=x,exports.lerpAngles=m; | ||
//# sourceMappingURL=perfect-freehand.cjs.production.min.js.map |
@@ -47,3 +47,4 @@ import polygonClipping from 'polygon-clipping'; | ||
var hypot = Math.hypot, | ||
var abs = Math.abs, | ||
hypot = Math.hypot, | ||
cos = Math.cos, | ||
@@ -54,4 +55,5 @@ max = Math.max, | ||
atan2 = Math.atan2, | ||
PI = Math.PI; | ||
var TAU = PI / 2; | ||
PI = Math.PI, | ||
TAU = PI / 2, | ||
PI2 = PI * 2; | ||
@@ -63,3 +65,3 @@ function projectPoint(x0, y0, a, d) { | ||
function shortAngleDist(a0, a1) { | ||
var max = Math.PI * 2; | ||
var max = PI2; | ||
var da = (a1 - a0) % max; | ||
@@ -69,2 +71,6 @@ return 2 * da % max - da; | ||
function lerpAngles(a0, a1, t) { | ||
return a0 + shortAngleDist(a0, a1) * t; | ||
} | ||
function angleDelta(a0, a1) { | ||
@@ -82,2 +88,10 @@ return shortAngleDist(a0, a1); | ||
function getAngle(x0, y0, x1, y1) { | ||
return atan2(y1 - y0, x1 - x0); | ||
} | ||
function getDistance(x0, y0, x1, y1) { | ||
return hypot(y1 - y0, x1 - x0); | ||
} | ||
function clamp(n, a, b) { | ||
@@ -148,9 +162,11 @@ return max(a, min(b, n)); | ||
x = px + (ix - px) * (1 - streamline); | ||
y = py + (iy - py) * (1 - streamline); // Angle | ||
y = py + (iy - py) * (1 - streamline); // Distance | ||
angle = atan2(y - py, x - px); // Distance | ||
distance = getDistance(x, y, px, py); // Angle | ||
distance = hypot(y - py, x - px); | ||
angle = getAngle(px, py, x, y); // If distance is very short, blend the angles | ||
if (distance < 1) angle = lerpAngles(prev[2], angle, 0.5); | ||
length += distance; | ||
prev = [x, y, ip, angle, distance, length]; | ||
prev = [x, y, angle, ip, distance, length]; | ||
pts.push(prev); | ||
@@ -187,11 +203,22 @@ } // Assign second angle to first point | ||
return []; | ||
} // Draw a kind of shitty shape around the start and end points. | ||
} | ||
var _points$ = points[0], | ||
x0 = _points$[0], | ||
y0 = _points$[1], | ||
_points = points[len - 1], | ||
x1 = _points[0], | ||
y1 = _points[1], | ||
p = points[len - 1][3], | ||
leftPts = [], | ||
rightPts = [], | ||
size = clamp(minSize + (maxSize - minSize) * (p ? p : 0.5), minSize, maxSize), | ||
angle = x0 === x1 ? 0 : getAngle(x0, y0, x1, y1); | ||
var p0 = points[0], | ||
p1 = points[len - 1], | ||
size = p0[2] === p1[2] ? maxSize : minSize + (maxSize - minSize) * p1[2], | ||
a = p0 === p1 ? Math.random() * (PI * 2) : atan2(p1[1] - p0[1], p1[0] - p0[0]), | ||
m = getPointBetween(p0[0], p0[1], p1[0], p1[1], 0.5); | ||
return [projectPoint(m[0], m[1], a + TAU, size), projectPoint(p0[0], p0[1], a + PI, size), projectPoint(m[0], m[1], a - TAU, size), projectPoint(p1[0], p1[1], a, size), projectPoint(m[0], m[1], a + TAU, size)]; | ||
for (var t = 0, step = 0.1; t <= 1; t += step) { | ||
leftPts.push(projectPoint(x1, y1, angle + TAU - t * PI, size - 1)); | ||
rightPts.push(projectPoint(x0, y0, angle + TAU + t * PI, size - 1)); | ||
} | ||
return leftPts.concat(rightPts.reverse()); | ||
} | ||
@@ -230,6 +257,6 @@ /** | ||
pp = 0.5, | ||
prev = p1, | ||
started = false, | ||
length = 0, | ||
leftPts = [], | ||
rightPts = [], | ||
leftPts = [p0], | ||
rightPts = [p0], | ||
d0, | ||
@@ -245,9 +272,14 @@ d1; | ||
for (var i = 1; i < len; i++) { | ||
var _points2 = points[i - 1], | ||
px = _points2[0], | ||
py = _points2[1], | ||
pa = _points2[2]; | ||
var _points$i = points[i], | ||
x = _points$i[0], | ||
y = _points$i[1], | ||
ip = _points$i[2], | ||
angle = _points$i[3], | ||
distance = _points$i[4]; | ||
length += distance; // Size | ||
angle = _points$i[2], | ||
ip = _points$i[3], | ||
distance = _points$i[4], | ||
clen = _points$i[5]; | ||
length += clen; // Size | ||
@@ -266,2 +298,19 @@ if (pressure) { | ||
size = maxSize; | ||
} // Handle line start | ||
if (!started && length > size / 2) { | ||
var _points$2 = points[0], | ||
sx = _points$2[0], | ||
sy = _points$2[1]; | ||
for (var t = 0, step = 0.25; t <= 1; t += step) { | ||
m0 = projectPoint(sx, sy, angle + TAU + t * PI, size - 1); | ||
leftPts.push(m0); | ||
m1 = projectPoint(sx, sy, angle - TAU + t * -PI, size - 1); | ||
rightPts.push(m1); | ||
} | ||
started = true; | ||
continue; | ||
} // 3. Shape | ||
@@ -273,55 +322,35 @@ | ||
p1 = projectPoint(x, y, angle + TAU, size); // right | ||
// // Add more points to the first and p1 points | ||
if (i === 0) { | ||
t0 = p0; | ||
t1 = p1; | ||
var delta = angleDelta(pa, angle); // Handle sharp corners differently | ||
for (var t = 0, step = 0.33; t <= 1; t += step) { | ||
m1 = projectPoint(prev[0], prev[1], angle - TAU + t * -PI, size * 2); | ||
if (i === points.length - 1 || abs(delta) > PI * 0.75 && length > size) { | ||
var _getPointBetween = getPointBetween(px, py, x, y, 0.5), | ||
mx = _getPointBetween[0], | ||
my = _getPointBetween[1]; | ||
for (var _t = 0, _step = 0.25; _t <= 1; _t += _step) { | ||
m0 = projectPoint(mx, my, pa - TAU + _t * PI, size - 1); | ||
leftPts.push(m0); | ||
m1 = projectPoint(mx, my, pa + TAU + _t * -PI, size - 1); | ||
rightPts.push(m1); | ||
t1 = m1; | ||
} | ||
t0 = m0; | ||
t1 = m1; | ||
} else { | ||
var delta = angleDelta(angle, prev[2]); // Handle sharp corners differently | ||
// Project sideways | ||
d0 = getDistance(p0[0], p0[1], t0[0], t0[1]); | ||
if (Math.abs(delta) > PI * 0.72 && length > size * 2) { | ||
if (delta > 0) { | ||
m0 = prev; | ||
leftPts.push(m0); | ||
t0 = m0; | ||
if (d0 > smooth) { | ||
leftPts.push(m0); | ||
m0 = getPointBetween(t0[0], t0[1], p0[0], p0[1], 0.5); | ||
t0 = p0; | ||
} | ||
for (var _t = 0, _step = 0.3; _t <= 1; _t += _step) { | ||
m1 = projectPoint(prev[0], prev[1], angle - TAU + _t * -PI, size); | ||
rightPts.push(m1); | ||
t1 = m1; | ||
} | ||
} else { | ||
for (var _t2 = 0, _step2 = 0.3; _t2 <= 1; _t2 += _step2) { | ||
m0 = projectPoint(prev[0], prev[1], angle + TAU + _t2 * PI, size); | ||
leftPts.push(m0); | ||
t0 = m0; | ||
} | ||
d1 = getDistance(p1[0], p1[1], t1[0], t1[1]); | ||
m1 = prev; | ||
rightPts.push(m1); | ||
t1 = m1; | ||
} | ||
} else { | ||
// Project sideways | ||
d0 = Math.hypot(p0[0] - t0[0], p0[1] - t0[1]); | ||
if (d0 > smooth) { | ||
leftPts.push(m0); | ||
m0 = getPointBetween(t0[0], t0[1], p0[0], p0[1], 0.5); | ||
t0 = p0; | ||
} | ||
d1 = Math.hypot(p1[0] - t1[0], p1[1] - t1[1]); | ||
if (d1 > smooth) { | ||
rightPts.push(m1); | ||
m1 = getPointBetween(t1[0], t1[1], p1[0], p1[1], 0.5); | ||
t1 = p1; | ||
} | ||
if (d1 > smooth) { | ||
rightPts.push(m1); | ||
m1 = getPointBetween(t1[0], t1[1], p1[0], p1[1], 0.5); | ||
t1 = p1; | ||
} | ||
@@ -331,7 +360,4 @@ } | ||
pp = ip; | ||
prev = [x, y, angle]; | ||
} | ||
leftPts.push(prev); | ||
rightPts.push(prev); | ||
return leftPts.concat(rightPts.reverse()); | ||
@@ -371,3 +397,3 @@ } | ||
totalLength = ps[ps.length - 1][5], | ||
pts = totalLength < maxSize * 2 ? getShortStrokeOutlinePoints(ps, options) : getStrokeOutlinePoints(ps, options), | ||
pts = totalLength < maxSize ? getShortStrokeOutlinePoints(ps, options) : getStrokeOutlinePoints(ps, options), | ||
d = []; // If the length is too short, just draw a dot. | ||
@@ -379,7 +405,7 @@ // If we're clipping the path, then find the polygon and add its faces. | ||
for (var _iterator = _createForOfIteratorHelperLoose(poly), _step3; !(_step3 = _iterator()).done;) { | ||
var face = _step3.value; | ||
for (var _iterator = _createForOfIteratorHelperLoose(poly), _step2; !(_step2 = _iterator()).done;) { | ||
var face = _step2.value; | ||
for (var _iterator2 = _createForOfIteratorHelperLoose(face), _step4; !(_step4 = _iterator2()).done;) { | ||
var verts = _step4.value; | ||
for (var _iterator2 = _createForOfIteratorHelperLoose(face), _step3; !(_step3 = _iterator2()).done;) { | ||
var verts = _step3.value; | ||
var v0 = verts[0]; | ||
@@ -391,5 +417,5 @@ var v1 = verts[1]; | ||
for (var i = 1; i < verts.length; i++) { | ||
var _getPointBetween = getPointBetween(v0[0], v0[1], v1[0], v1[1], 0.5), | ||
mpx = _getPointBetween[0], | ||
mpy = _getPointBetween[1]; | ||
var _getPointBetween2 = getPointBetween(v0[0], v0[1], v1[0], v1[1], 0.5), | ||
mpx = _getPointBetween2[0], | ||
mpy = _getPointBetween2[1]; | ||
@@ -410,5 +436,5 @@ d.push(" Q " + v0[0] + "," + v0[1] + " " + mpx + "," + mpy); | ||
for (var _i = 1; _i < pts.length; _i++) { | ||
var _getPointBetween2 = getPointBetween(_v[0], _v[1], _v2[0], _v2[1], 0.5), | ||
_mpx = _getPointBetween2[0], | ||
_mpy = _getPointBetween2[1]; | ||
var _getPointBetween3 = getPointBetween(_v[0], _v[1], _v2[0], _v2[1], 0.5), | ||
_mpx = _getPointBetween3[0], | ||
_mpy = _getPointBetween3[1]; | ||
@@ -421,2 +447,3 @@ d.push("Q " + _v[0] + "," + _v[1] + " " + _mpx + "," + _mpy); | ||
d.push('Z'); | ||
return d.join(' '); | ||
@@ -426,3 +453,3 @@ } | ||
export default getPath; | ||
export { clipPath, getShortStrokeOutlinePoints, getStrokeOutlinePoints, getStrokePoints }; | ||
export { clipPath, getShortStrokeOutlinePoints, getStrokeOutlinePoints, getStrokePoints, lerpAngles }; | ||
//# sourceMappingURL=perfect-freehand.esm.js.map |
{ | ||
"version": "0.2.4", | ||
"version": "0.2.5", | ||
"license": "MIT", | ||
@@ -4,0 +4,0 @@ "main": "dist/index.js", |
171
src/index.ts
@@ -5,4 +5,5 @@ import polygonClipping from 'polygon-clipping' | ||
const { hypot, cos, max, min, sin, atan2, PI } = Math | ||
const TAU = PI / 2 | ||
const { abs, hypot, cos, max, min, sin, atan2, PI } = Math, | ||
TAU = PI / 2, | ||
PI2 = PI * 2 | ||
@@ -14,3 +15,3 @@ function projectPoint(x0: number, y0: number, a: number, d: number) { | ||
function shortAngleDist(a0: number, a1: number) { | ||
var max = Math.PI * 2 | ||
var max = PI2 | ||
var da = (a1 - a0) % max | ||
@@ -20,2 +21,6 @@ return ((2 * da) % max) - da | ||
export function lerpAngles(a0: number, a1: number, t: number) { | ||
return a0 + shortAngleDist(a0, a1) * t | ||
} | ||
function angleDelta(a0: number, a1: number) { | ||
@@ -34,3 +39,10 @@ return shortAngleDist(a0, a1) | ||
} | ||
function getAngle(x0: number, y0: number, x1: number, y1: number) { | ||
return atan2(y1 - y0, x1 - x0) | ||
} | ||
function getDistance(x0: number, y0: number, x1: number, y1: number) { | ||
return hypot(y1 - y0, x1 - x0) | ||
} | ||
function clamp(n: number, a: number, b: number) { | ||
@@ -117,9 +129,13 @@ return max(a, min(b, n)) | ||
// Distance | ||
distance = getDistance(x, y, px, py) | ||
// Angle | ||
angle = atan2(y - py, x - px) | ||
angle = getAngle(px, py, x, y) | ||
// Distance | ||
distance = hypot(y - py, x - px) | ||
// If distance is very short, blend the angles | ||
if (distance < 1) angle = lerpAngles(prev[2], angle, 0.5) | ||
length += distance | ||
prev = [x, y, ip, angle, distance, length] | ||
prev = [x, y, angle, ip, distance, length] | ||
pts.push(prev) | ||
@@ -147,3 +163,2 @@ } | ||
const { minSize = 2.5, maxSize = 8 } = options | ||
const len = points.length | ||
@@ -156,19 +171,20 @@ | ||
// Draw a kind of shitty shape around the start and end points. | ||
const p0 = points[0], | ||
p1 = points[len - 1], | ||
size = p0[2] === p1[2] ? maxSize : minSize + (maxSize - minSize) * p1[2], | ||
a = | ||
p0 === p1 | ||
? Math.random() * (PI * 2) | ||
: atan2(p1[1] - p0[1], p1[0] - p0[0]), | ||
m = getPointBetween(p0[0], p0[1], p1[0], p1[1], 0.5) | ||
const [x0, y0] = points[0], | ||
[x1, y1] = points[len - 1], | ||
p = points[len - 1][3], | ||
leftPts: number[][] = [], | ||
rightPts: number[][] = [], | ||
size = clamp( | ||
minSize + (maxSize - minSize) * (p ? p : 0.5), | ||
minSize, | ||
maxSize | ||
), | ||
angle = x0 === x1 ? 0 : getAngle(x0, y0, x1, y1) | ||
return [ | ||
projectPoint(m[0], m[1], a + TAU, size), | ||
projectPoint(p0[0], p0[1], a + PI, size), | ||
projectPoint(m[0], m[1], a - TAU, size), | ||
projectPoint(p1[0], p1[1], a, size), | ||
projectPoint(m[0], m[1], a + TAU, size), | ||
] | ||
for (let t = 0, step = 0.1; t <= 1; t += step) { | ||
leftPts.push(projectPoint(x1, y1, angle + TAU - t * PI, size - 1)) | ||
rightPts.push(projectPoint(x0, y0, angle + TAU + t * PI, size - 1)) | ||
} | ||
return leftPts.concat(rightPts.reverse()) | ||
} | ||
@@ -203,6 +219,6 @@ | ||
pp = 0.5, | ||
prev = p1, | ||
started = false, | ||
length = 0, | ||
leftPts: number[][] = [], | ||
rightPts: number[][] = [], | ||
leftPts: number[][] = [p0], | ||
rightPts: number[][] = [p0], | ||
d0: number, | ||
@@ -219,5 +235,7 @@ d1: number | ||
for (let i = 1; i < len; i++) { | ||
let [x, y, ip, angle, distance] = points[i] | ||
length += distance | ||
const [px, py, pa] = points[i - 1] | ||
let [x, y, angle, ip, distance, clen] = points[i] | ||
length += clen | ||
// Size | ||
@@ -227,3 +245,3 @@ if (pressure) { | ||
// Simulate pressure by accellerating the reported pressure. | ||
let rp = min(1 - distance / maxSize, 1) | ||
const rp = min(1 - distance / maxSize, 1) | ||
const sp = min(distance / maxSize, 1) | ||
@@ -238,2 +256,17 @@ ip = min(1, pp + (rp - pp) * (sp / 2)) | ||
// Handle line start | ||
if (!started && length > size / 2) { | ||
const [sx, sy] = points[0] | ||
for (let t = 0, step = 0.25; t <= 1; t += step) { | ||
m0 = projectPoint(sx, sy, angle + TAU + t * PI, size - 1) | ||
leftPts.push(m0) | ||
m1 = projectPoint(sx, sy, angle - TAU + t * -PI, size - 1) | ||
rightPts.push(m1) | ||
} | ||
started = true | ||
continue | ||
} | ||
// 3. Shape | ||
@@ -243,53 +276,31 @@ p0 = projectPoint(x, y, angle - TAU, size) // left | ||
// // Add more points to the first and p1 points | ||
if (i === 0) { | ||
t0 = p0 | ||
t1 = p1 | ||
const delta = angleDelta(pa, angle) | ||
for (let t = 0, step = 0.33; t <= 1; t += step) { | ||
m1 = projectPoint(prev[0], prev[1], angle - TAU + t * -PI, size * 2) | ||
// Handle sharp corners differently | ||
if (i === points.length - 1 || (abs(delta) > PI * 0.75 && length > size)) { | ||
const [mx, my] = getPointBetween(px, py, x, y, 0.5) | ||
for (let t = 0, step = 0.25; t <= 1; t += step) { | ||
m0 = projectPoint(mx, my, pa - TAU + t * PI, size - 1) | ||
leftPts.push(m0) | ||
m1 = projectPoint(mx, my, pa + TAU + t * -PI, size - 1) | ||
rightPts.push(m1) | ||
t1 = m1 | ||
} | ||
t0 = m0 | ||
t1 = m1 | ||
} else { | ||
const delta = angleDelta(angle, prev[2]) | ||
// Project sideways | ||
d0 = getDistance(p0[0], p0[1], t0[0], t0[1]) | ||
if (d0 > smooth) { | ||
leftPts.push(m0) | ||
m0 = getPointBetween(t0[0], t0[1], p0[0], p0[1], 0.5) | ||
t0 = p0 | ||
} | ||
// Handle sharp corners differently | ||
if (Math.abs(delta) > PI * 0.72 && length > size * 2) { | ||
if (delta > 0) { | ||
m0 = prev | ||
leftPts.push(m0) | ||
t0 = m0 | ||
for (let t = 0, step = 0.3; t <= 1; t += step) { | ||
m1 = projectPoint(prev[0], prev[1], angle - TAU + t * -PI, size) | ||
rightPts.push(m1) | ||
t1 = m1 | ||
} | ||
} else { | ||
for (let t = 0, step = 0.3; t <= 1; t += step) { | ||
m0 = projectPoint(prev[0], prev[1], angle + TAU + t * PI, size) | ||
leftPts.push(m0) | ||
t0 = m0 | ||
} | ||
m1 = prev | ||
rightPts.push(m1) | ||
t1 = m1 | ||
} | ||
} else { | ||
// Project sideways | ||
d0 = Math.hypot(p0[0] - t0[0], p0[1] - t0[1]) | ||
if (d0 > smooth) { | ||
leftPts.push(m0) | ||
m0 = getPointBetween(t0[0], t0[1], p0[0], p0[1], 0.5) | ||
t0 = p0 | ||
} | ||
d1 = Math.hypot(p1[0] - t1[0], p1[1] - t1[1]) | ||
if (d1 > smooth) { | ||
rightPts.push(m1) | ||
m1 = getPointBetween(t1[0], t1[1], p1[0], p1[1], 0.5) | ||
t1 = p1 | ||
} | ||
d1 = getDistance(p1[0], p1[1], t1[0], t1[1]) | ||
if (d1 > smooth) { | ||
rightPts.push(m1) | ||
m1 = getPointBetween(t1[0], t1[1], p1[0], p1[1], 0.5) | ||
t1 = p1 | ||
} | ||
@@ -299,8 +310,4 @@ } | ||
pp = ip | ||
prev = [x, y, angle] | ||
} | ||
leftPts.push(prev) | ||
rightPts.push(prev) | ||
return leftPts.concat(rightPts.reverse()) | ||
@@ -337,3 +344,3 @@ } | ||
pts = | ||
totalLength < maxSize * 2 | ||
totalLength < maxSize | ||
? getShortStrokeOutlinePoints(ps, options) | ||
@@ -378,3 +385,5 @@ : getStrokeOutlinePoints(ps, options), | ||
d.push('Z') | ||
return d.join(' ') | ||
} |
Sorry, the diff of this file is not supported yet
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
108372
1123