webgl2-sdf 0.0.7 → 0.0.8
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/browser/index.min.js +1 -1
- package/node/debug-shaders.js +2 -2
- package/node/debug-shaders.js.map +1 -1
- package/node/generate-sdf.d.ts +80 -9
- package/node/generate-sdf.js +92 -15
- package/node/generate-sdf.js.map +1 -1
- package/node/main-program.d.ts +2 -1
- package/node/main-program.js +6 -2
- package/node/main-program.js.map +1 -1
- package/node/shaders/fragment.d.ts +4 -2
- package/node/shaders/fragment.js +79 -55
- package/node/shaders/fragment.js.map +1 -1
- package/node/webgl-utils/get-web-gl-context.js +2 -0
- package/node/webgl-utils/get-web-gl-context.js.map +1 -1
- package/package.json +1 -1
- package/src/debug-shaders.ts +2 -2
- package/src/generate-sdf.ts +116 -26
- package/src/main-program.ts +18 -7
- package/src/shaders/fragment.ts +86 -57
- package/src/webgl-utils/get-web-gl-context.ts +3 -0
package/browser/index.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const n=32,e={};function t(n,e,t){const r=n.createShader(t);return n.shaderSource(r,e),n.compileShader(r),r}function r(n,e,t){const{gl:r,textures:o}=n;r.activeTexture(r.TEXTURE0+e);let s=o[t];return s?r.bindTexture(r.TEXTURE_2D,s.tex):(s=o[t]={tex:r.createTexture()},r.bindTexture(r.TEXTURE_2D,s.tex),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MIN_FILTER,r.NEAREST),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MAG_FILTER,r.NEAREST),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_S,r.CLAMP_TO_EDGE),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_T,r.CLAMP_TO_EDGE)),s}const{BYTE:o,UNSIGNED_BYTE:s,SHORT:i,UNSIGNED_SHORT:l,INT:c,UNSIGNED_INT:u,FLOAT:a,HALF_FLOAT:d,INT_2_10_10_10_REV:f,UNSIGNED_INT_2_10_10_10_REV:g}=WebGL2RenderingContext;function h(n){return(e,t,r)=>{const{gl:o,uniformBlocks:s,program:i}=n;let l=s[e];if(void 0===s[e]){const n=o.getUniformBlockIndex(i,e);o.uniformBlockBinding(i,n,t);const r=o.createBuffer();o.bindBuffer(o.UNIFORM_BUFFER,r),l={blockName:e,blockIndex:n,buf:r},s[e]=l}else o.bindBuffer(o.UNIFORM_BUFFER,l.buf);const{buf:c}=l;o.bufferData(o.UNIFORM_BUFFER,r,o.STATIC_DRAW),o.bindBufferBase(o.UNIFORM_BUFFER,t,c)}}function x(n){const e=n[0][0],t=n[0][1];for(let r=1;r<n.length;r++)if(e!==n[r][0]||t!==n[r][1])return!1;return!0}function I(n,e){if(0===e)return n[0];if(1===e)return n[n.length-1];if(4===n.length){const[[t,r],[o,s],[i,l],[c,u]]=n,a=t+(o-t)*e,d=o+(i-o)*e,f=a+(d-a)*e,g=r+(s-r)*e,h=s+(l-s)*e,x=g+(h-g)*e;return[f+(d+(i+(c-i)*e-d)*e-f)*e,x+(h+(l+(u-l)*e-h)*e-x)*e]}if(3===n.length){const[[t,r],[o,s],[i,l]]=n,c=t+(o-t)*e,u=r+(s-r)*e;return[c+(o+(i-o)*e-c)*e,u+(s+(l-s)*e-u)*e]}if(2===n.length){const[[t,r],[o,s]]=n;return[t+(o-t)*e,r+(s-r)*e]}if(1===n.length)return n[0];throw new Error("The given bezier curve must be of order <= 3.")}const{sqrt:_}=Math;function p(n,e){const t=n[0],r=n[1],o=e[0],s=e[1],i=r-s,l=o-t,c=t*s-o*r,u=_(i*i+l*l);return function(n){const e=n[0],o=n[1];return 0!==u?(i*e+l*o+c)/u:_((e-t)**2+(o-r)**2)}}{const n=[10,1];p([6,2],[6,2])(n)}function v(n,e,t){if(4===n.length)return function(n,e,t){return 0===e?1===t?n:function(n,e){const t=n[0],r=n[1],o=n[2],s=n[3],i=t[0],l=t[1],c=r[0],u=r[1],a=o[0],d=o[1],f=i-e*(i-c),g=c-e*(c-a),h=f-e*(f-g),x=l-e*(l-u),I=u-e*(u-d),_=x-e*(x-I);return[t,[f,x],[h,_],[h-e*(h-(g-e*(g-(a-e*(a-s[0]))))),_-e*(_-(I-e*(I-(d-e*(d-s[1])))))]]}(n,t):1===t?function(n,e){const t=n[0],r=n[1],o=n[2],s=n[3],i=t[0],l=t[1],c=r[0],u=r[1],a=o[0],d=o[1],f=i-e*(i-c),g=c-e*(c-a),h=a-e*(a-s[0]),x=f-e*(f-g),I=g-e*(g-h),_=l-e*(l-u),p=u-e*(u-d),v=d-e*(d-s[1]),m=_-e*(_-p),C=p-e*(p-v);return[[x-e*(x-I),m-e*(m-C)],[I,C],[h,v],s]}(n,e):function(n,e,t){const r=n[0],o=n[1],s=n[2],i=n[3],l=r[0],c=r[1],u=o[0],a=o[1],d=s[0],f=s[1],g=e*e,h=e*g,x=t*t,I=t*x,_=e*t,p=l-u,v=d-u,m=p+v,C=e*p,S=t*p,R=i[0]-l-3*v,E=c-a,T=f-a,b=E+T,P=e*E,w=t*E,A=i[1]-c-3*T;return[[h*R+(3*e*(e*m-p)+l),h*A+(3*e*(e*b-E)+c)],[_*(e*R+2*m)+(g*m+l-(S+2*C)),_*(e*A+2*b)+(g*b+c-(w+2*P))],[_*(t*R+2*m)+(x*m+l-(2*S+C)),_*(t*A+2*b)+(x*b+c-(2*w+P))],[I*R+(3*t*(t*m-p)+l),I*A+(3*t*(t*b-E)+c)]]}(n,e,t)}(n,e,t);if(3===n.length)return function(n,e,t){return 0===e?1===t?n:function(n,e){const t=n[0],r=n[1],o=n[2],s=t[0],i=t[1],l=r[0],c=r[1],u=e*e,a=s-l,d=i-c;return[t,[-e*a+s,-e*d+i],[u*(a+(o[0]-l))-(2*e*a-s),u*(d+(o[1]-c))-(2*e*d-i)]]}(n,t):1===t?function(n,e){const t=n[0],r=n[1],o=n[2],s=t[0],i=t[1],l=r[0],c=r[1],u=e*e,a=s-l,d=o[0]-l,f=i-c,g=o[1]-c;return[[u*(a+d)-(2*e*a-s),u*(f+g)-(2*e*f-i)],[e*d+l,e*g+c],o]}(n,e):function(n,e,t){const r=n[0],o=n[1],s=n[2],i=r[0],l=r[1],c=o[0],u=o[1],a=e*e,d=t*t,f=e*t,g=i-c,h=g+(s[0]-c),x=l-u,I=x+(s[1]-u);return[[a*h-(2*e*g-i),a*I-(2*e*x-l)],[f*h-(g*(t+e)-i),f*I-(x*(t+e)-l)],[d*h-(2*t*g-i),d*I-(2*t*x-l)]]}(n,e,t)}(n,e,t);throw new Error("The given bezier curve must be of order 2 or 3.")}function m(n,e){return[e[0]-n[0],e[1]-n[1]]}function C(n,e){return n[0]*e[0]+n[1]*e[1]}function S(n){const e=m(n[0],n[1]),t=m(n[1],n[3]),r=m(n[3],n[0]),o=m(n[0],n[2]),s=m(n[2],n[3]);return C(r,e)>0||C(t,r)>0||C(r,o)>0||C(s,r)>0}const{abs:R,max:E}=Math;function T(n){const e=p(n[0],n[3]),t=e(n[1]),r=e(n[2]);return(t*r<=0?4/9:3/4)*E(R(t),R(r))}function b(n){const e=m(n[0],n[1]),t=m(n[1],n[2]),r=m(n[2],n[0]);return C(r,e)>0||C(t,r)>0}const{abs:P}=Math;function w(n){if(n[0][0]===n[1][0]&&n[0][1]===n[1][1])return 0;const e=I(n,.5),t=p(n[0],n[2]);return P(t(e))}function A(n){const e=3===n.length?function(n){const e=[0],t=[1];for(;;){const r=e[e.length-1],o=t[t.length-1],s=v(n,r,o);if(!b(s)&&w(s)<=.5){if(e.push(t.pop()),1===o)return e;continue}const i=(r+o)/2;t.push(i)}}(n):function(n){const e=[0],t=[1];for(;;){const r=e[e.length-1],o=t[t.length-1],s=v(n,r,o);if(!S(s)&&T(s)<=.5){if(e.push(t.pop()),1===o)return e;continue}const i=(r+o)/2;t.push(i)}}(n),t=[];for(let r=0;r<e.length-1;r++){const o=I(n,e[r]),s=I(n,e[r+1]);t.push([o,s])}return t}function D(n){return Math.sqrt(n[0]*n[0]+n[1]*n[1])}const y=function(){const e=[{from:0,u:0,v:0}];for(let t=0;t<128;t++){e.push({from:t+.5,u:t+1,v:0}),e.push({from:t+.5,u:-t-1,v:0}),e.push({from:t+.5,u:0,v:t+1}),e.push({from:t+.5,u:0,v:-t-1});for(let r=0;r<n;r++){const n=D([.5+t,.5+r]),o=t+1,s=r+1;e.push({from:n,u:o,v:s}),0!==o&&e.push({from:n,u:-o,v:s}),0!==s&&e.push({from:n,u:o,v:-s}),0!==o&&0!==s&&e.push({from:n,u:-o,v:-s})}e.sort((n,e)=>n.from-e.from)}return e}(),{max:U,E:F}=Math;const{min:N,max:M,SQRT2:k}=Math;function L(e,t,r,o,s){const i=function(e,t,r,o,s){return function(i,l,c){let u=function(n){let e=function(n){let e=0,t=y.length-1,r=0;for(;e<=t;){r=e+t>>>1;const o=y[r].from;if(o===n)return r;n>o?e=r+1:t=r-1}return r}(n);if(n=U(0,n),0===e)return 0;for(;y[e].from===y[e-1].from;)e--;return e}((c-k*r)/r),a=Number.POSITIVE_INFINITY;for(;;){if(u>=y.length){a=r*(y[y.length-1].from-k);break}const{u:c,v:d,from:f}=y[u];if(r*f>o+k*r){a=r*(f-2*k);break}const g=c+i,h=d+l;if(g<0||g>=t+2*s||h<0||h>=n+2*s){u++;continue}const{lineSegs:x}=e[g][h];if(x.length>0){a=r*(f-k);break}u++}const{closeCells:d}=e[i][l];let f=M(0,u-1);for(;f<y.length;){const{from:c,u,v:g}=y[f];if(r*c>N(a,o)+2*k*r)break;const h=u+i,x=g+l;if(h<0||h>=t+2*s||x<0||x>=n+2*s){f++;continue}const{lineSegs:I}=e[h][x];I.length>0&&d.push((2*s+n)*h+x),f++}return a}}(e,t,r,o,s);let l=0,c=0;for(let e=s;e<t+s;e++){c=l;for(let t=s;t<n+s;t++)c=i(e,t,c),t===s&&(l=c)}}const{floor:B,ceil:O}=Math;function X(n,e,t,r,o,s){const i=r*s,[[l,c],[u,a]]=o,d=e+i,f=t+i,g=function(n,e){const[t,r]=n,[o,s]=t,[i,l]=r,[[c,u],[a,d]]=e,f=i-o,g=l-s,h=[];if(0!==f){const n=(c-o)/f;if(n>=0&&n<=1){const e=s+n*g;e>=u&&e<=d&&h.push({t:n,p:[c,e]})}const e=(a-o)/f;if(e>=0&&e<=1){const n=s+e*g;n>=u&&n<=d&&h.push({t:e,p:[a,n]})}}if(0!==g){const n=(u-s)/g;if(n>=0&&n<=1){const e=o+n*f;e>=c&&e<=a&&h.push({t:n,p:[e,u]})}const e=(d-s)/g;if(e>=0&&e<=1){const n=o+e*f;n>=c&&n<=a&&h.push({t:e,p:[n,d]})}}const x=new Set,I=h.filter(n=>{const{t:e}=n;return!x.has(e)&&(x.add(e),!0)});return I.sort((n,e)=>n.t-e.t).map(n=>n.p)}(o,[[-i,-i],[d,f]]),h=l>-i&&l<d&&c>-i&&c<f,x=u>-i&&u<d&&a>-i&&a<f;if(g.length<2&&!h&&!x)return;const I=u-l,_=a-c,p=u>l?r:-r,v=a>c?r:-r;let m=l,C=c,S=0;const R=p>0,E=v>0,T=R?B:O,b=E?B:O;for(;;){let e=r*T((m+p)/r),t=r*b((C+v)/r);const o=(e-l)/I,x=(t-c)/_,P=o<x&&0!==I||0===_,w=P?e:l+x*I,A=P?c+o*_:t;if((o>1||0===I)&&(x>1||0===_)){const e=B(u/r)+s,t=B(a/r)+s;n[e]?.[t]?.lineSegs.push([[m,C],[u,a]]);break}if(w<=-i||A<=-i||w>=d||A>=f){if(!h&&0===S){[m,C]=g[0],S++;continue}{const e=T(m/r)-(R?0:1)+s,t=b(C/r)-(E?0:1)+s,o=[[m,C],g[S]];n[e]?.[t]?.lineSegs.push(o);break}}const D=T(m/r)-(R?0:1)+s,y=b(C/r)-(E?0:1)+s;n[D]?.[y]?.lineSegs.push([[m,C],[w,A]]),m=w,C=A}}const j=256,{floor:G,ceil:Y}=Math;function Q(n,e,t){const r=e/n.length,[o,s]=t,i=function(n,e){const[t,r]=n,[o,s]=t,[i,l]=r,[c,u,a]=e,d=i-o,f=l-s,g=[],h=[];if(0!==d){const n=(c-o)/d;if(n>=0&&n<=1){const e=s+n*f;e>=u&&e<=a&&(g.push(n),h.push([c,e]))}}if(0!==f){const n=(u-s)/f;if(n>=0&&n<=1){const e=o+n*d;e<=c&&(g.push(n),h.push([e,u]))}const e=(a-s)/f;if(e>=0&&e<=1){const n=o+e*d;n<=c&&(g.push(e),h.push([n,a]))}}return 2===g.length?g[0]<g[1]?h:[h[1],h[0]]:h}(t,[0,0,e]),[l,c]=o,[u,a]=s,d=u-l,f=a-c,g=l<0&&c>0&&c<e,h=u<0&&a>0&&a<e;if(i.length<2&&!g&&!h&&(0!==l||0!==d||c<=0&&a<=0||c>=e&&a>=e))return;if(0===f)return;const x=a>c?r:-r;let I=l,_=c,p=0;const v=u>l,m=a>c,C=m?G:Y;for(;;){let t=v&&I<0||!v&&I>0?0:Number.NEGATIVE_INFINITY,o=r*C((_+x)/r);const u=(t-l)/d,a=(o-c)/f,h=u<a&&0!==d,S=h?0:l+a*d,R=h?c+u*f:o;if((u>1||0===d)&&a>1){const e=G(s[1]/r);n[e]?.push([[I,_],s]);break}if(S>=0&&(0!==l||0!==d)||R<=0||R>=e){if(!g&&0===p){[I,_]=i[0],p++;continue}{const e=C(_/r)-(m?0:1),t=[[I,_],i[p]];n[e]?.push(t);break}}const E=C(_/r)-(m?0:1);n[E]?.push([[I,_],[S,R]]),I=S,_=R}}function V(e,t,o,s,i,l,c,u,f,g,I,_,p,v,m,C){const{gl:S}=e,R=[],E=.03125,T=.03125;R.push(0,0,E,0,0,T),R.push(E,0,E,T,0,T);const b=new Float32Array(R),P=(w=t,(n,e,...t)=>{const{gl:r,uniforms:o}=w,s=o[e]||(o[e]=r.getUniformLocation(w.program,e));r[`uniform${n}`](s,...t)});var w;const D=function(n){return(e,t,r,o,s,i=0,l=0,c=0)=>{const{gl:u,attributes:f}=n,g=f[e]=f[e]??{buf:u.createBuffer(),loc:u.getAttribLocation(n.program,e),data:null},{loc:h,buf:x}=g;u.bindBuffer(u.ARRAY_BUFFER,x),r===a||r===d?u.vertexAttribPointer(h,t,r,!1,l,c):u.vertexAttribIPointer(h,t,r,l,c),u.enableVertexAttribArray(h),0!==i&&u.vertexAttribDivisor(h,i),s!==g.data&&(u.bufferData(u.ARRAY_BUFFER,s,o),g.data=s)}}(t),{lineSegPtCoords_Arr:y,segIdxs_PerCell_Range_Arr:U,closeCellIdxs_PerCell_Arr:F,closeCellIdxs_PerCell_Range_Arr:N,crossCellIdxs_PerCell_Arr:M,crossCellIdxs_perCell_Range_Arr:k,segIdxs_PerStrip_Range_Arr:B}=function(e,t,r,o,s,i,l,c=[0,0,t,r],u=1){const a=function(n,e,t,r){if(0===n[0]&&0===n[1]&&n[2]===e&&n[3]===t)return r;const[o,s,i,l]=n,c=e/i,u=t/l,a=[];for(let n=0;n<r.length;n++){const e=r[n],t=[];for(let n=0;n<e.length;n++){const r=e[n],i=[];for(let n=0;n<r.length;n++){const[e,t]=r[n],l=c*e-o,a=u*t-s;i.push([l,a])}t.push(i)}a.push(t)}return a}(c,t,r/u,e),d=function(n){let e=[];for(let t=0;t<n.length;t++){const r=n[t];for(let n=0;n<r.length;n++){const t=r[n];if(x(t))continue;if(2===t.length){e.push(t);continue}const o=A(t);e.push(...o.filter(n=>!x(n)))}}return e}(a),f=function(e,t){const r=[];for(let o=0;o<e+2*t;o++){const e=[];for(let r=0;r<n+2*t;r++)e.push({lineSegs:[],closeCells:[],crossingCells:[]});r.push(e)}return r}(o,l),g=new Array(n).fill(void 0).map(n=>[]);for(let n=0;n<d.length;n++){const e=d[n];X(f,t,r,s,e,l),Q(g,r,e)}L(f,o,s,i,l),function(e,t,r){for(let o=r;o<t+r;o++)for(let t=r;t<n+r;t++){const s=e[o][t].crossingCells;for(let i=r;i<=o;i++)0!==e[i][t].lineSegs.length&&s.push((n+2*r)*i+t)}}(f,o,l);const h=[],I=[],_=[],p=[],v=[],m=[];let C=0,S=0,R=0;for(let e=0;e<o+2*l;e++)for(let t=0;t<n+2*l;t++){const r=f[e][t];if(e>=l&&e<o+l&&t>=l&&t<n+l){const{closeCells:n,crossingCells:e}=r,t=e.length,o=n.length;v.push(...e),_.push(...n),m.push(C,t),p.push(S,o),C+=t,S+=o}const{lineSegs:s}=r,i=s.length;I.push(R,i),R+=i,h.push(...s.flat(2))}const E=[];for(let e=0;e<n;e++){const n=g[e],t=n.length;E.push([R,t]),R+=t,h.push(...n.flat(2))}for(;_.length%j!==0;)_.push(0);for(;v.length%j!==0;)v.push(0);for(;h.length%1024!=0;)h.push(0);const T=new Float32Array(h),b=new Int32Array(I),P=new Int32Array(p);return{lineSegPtCoords_Arr:T,segIdxs_PerCell_Range_Arr:b,closeCellIdxs_PerCell_Arr:new Int32Array(_),closeCellIdxs_PerCell_Range_Arr:P,crossCellIdxs_PerCell_Arr:new Int32Array(v),crossCellIdxs_perCell_Range_Arr:new Int32Array(m),segIdxs_PerStrip_Range_Arr:new Int32Array(E.flat())}}(o,I,_,p,v,i,m,s,C);D("aUV",2,S.FLOAT,S.STATIC_DRAW,b),D("aCrossIdxRangePerCell",2,S.INT,S.STATIC_DRAW,k,1),D("aCloseCellIdxRangePerCell",2,S.INT,S.STATIC_DRAW,N,1),P("2f","uWidthHeight",I,_),P("1f","uMaxDistance",i),P("1i","uIncl",(l?1:0)+(c?2:0)),P("4f","uCustom",...u),h(t)("SegIdxRangePerCellBlock",0,U),h(t)("SegIdxRangePerStripBlock",1,B),r(e,0,"segs"),S.texImage2D(S.TEXTURE_2D,0,S.RGBA32F,j,y.length/4/j,0,S.RGBA,S.FLOAT,y);const O=S.getUniformLocation(t.program,"uSegs");S.uniform1i(O,0),r(e,1,"closeCellIdxsPerCell"),S.texImage2D(S.TEXTURE_2D,0,S.R32I,j,F.length/j,0,S.RED_INTEGER,S.INT,F);const G=S.getUniformLocation(t.program,"uCloseCellIdxs");S.uniform1i(G,1),r(e,2,"crossCellIdxsPerCell"),S.texImage2D(S.TEXTURE_2D,0,S.R32I,j,M.length/j,0,S.RED_INTEGER,S.INT,M);const Y=S.getUniformLocation(t.program,"uCrossCellIdxs");S.uniform1i(Y,2),C>1&&(S.enable(S.SCISSOR_TEST),S.scissor(f,g,I,_/C)),S.viewport(f,g,I,_),S.drawArraysInstanced(S.TRIANGLES,0,6,p*n),C>1&&S.disable(S.SCISSOR_TEST)}function z(n){const e=[n.p,n.initialPoint];return n.prev2ndCubicControlPoint=void 0,n.prev2ndQuadraticControlPoint=void 0,e}const H={c:function(n){const e=[n.p,[n.vals[0],n.vals[1]],[n.vals[2],n.vals[3]],[n.vals[4],n.vals[5]]];return n.prev2ndCubicControlPoint=e[2],n.prev2ndQuadraticControlPoint=void 0,e},h:function(n){const e=[n.p,[n.vals[0],n.p[1]]];return n.prev2ndCubicControlPoint=void 0,n.prev2ndQuadraticControlPoint=void 0,e},l:function(n){const e=[n.p,n.vals];return n.prev2ndCubicControlPoint=void 0,n.prev2ndQuadraticControlPoint=void 0,e},q:function(n){const e=[n.vals[0],n.vals[1]],t=[n.vals[2],n.vals[3]];return n.prev2ndCubicControlPoint=void 0,n.prev2ndQuadraticControlPoint=e,[n.p,e,t]},s:function(n){const e=n.prev2ndCubicControlPoint?[n.p[0]-n.prev2ndCubicControlPoint[0]+n.p[0],n.p[1]-n.prev2ndCubicControlPoint[1]+n.p[1]]:n.p,t=[n.p,e,[n.vals[0],n.vals[1]],[n.vals[2],n.vals[3]]];return n.prev2ndCubicControlPoint=t[2],n.prev2ndQuadraticControlPoint=void 0,t},t:function(n){const e=n.prev2ndQuadraticControlPoint?[n.p[0]-n.prev2ndQuadraticControlPoint[0]+n.p[0],n.p[1]-n.prev2ndQuadraticControlPoint[1]+n.p[1]]:n.p,t=[n.vals[0],n.vals[1]];return n.prev2ndCubicControlPoint=void 0,n.prev2ndQuadraticControlPoint=e,[n.p,e,t]},v:function(n){const e=[n.p,[n.p[0],n.vals[0]]];return n.prev2ndCubicControlPoint=void 0,n.prev2ndQuadraticControlPoint=void 0,e},z};function W(n){let e=0,t=0,r=1,o=0,s=1,i=1;const l=n._currentIndex;if(n._skipOptionalSpaces(),n._currentIndex<n._endIndex&&"+"===n._string[n._currentIndex]?n._currentIndex+=1:n._currentIndex<n._endIndex&&"-"===n._string[n._currentIndex]&&(n._currentIndex+=1,s=-1),n._currentIndex===n._endIndex||(n._string[n._currentIndex]<"0"||n._string[n._currentIndex]>"9")&&"."!==n._string[n._currentIndex])throw new Error("The first character of a number must be one of [0-9+-.].");const c=n._currentIndex;for(;n._currentIndex<n._endIndex&&n._string[n._currentIndex]>="0"&&n._string[n._currentIndex]<="9";)n._currentIndex+=1;if(n._currentIndex!==c){let e=n._currentIndex-1,r=1;for(;e>=c;)t+=r*(Number(n._string[e])-0),e-=1,r*=10}if(n._currentIndex<n._endIndex&&"."===n._string[n._currentIndex]){if(n._currentIndex+=1,n._currentIndex>=n._endIndex||n._string[n._currentIndex]<"0"||n._string[n._currentIndex]>"9")throw new Error("There must be a least one digit following the .");for(;n._currentIndex<n._endIndex&&n._string[n._currentIndex]>="0"&&n._string[n._currentIndex]<="9";)r*=10,o+=Number(n._string.charAt(n._currentIndex))/r,n._currentIndex+=1}if(n._currentIndex!==l&&n._currentIndex+1<n._endIndex&&("e"===n._string[n._currentIndex]||"E"===n._string[n._currentIndex])&&"x"!==n._string[n._currentIndex+1]&&"m"!==n._string[n._currentIndex+1]){if(n._currentIndex+=1,"+"===n._string[n._currentIndex]?n._currentIndex+=1:"-"===n._string[n._currentIndex]&&(n._currentIndex+=1,i=-1),n._currentIndex>=n._endIndex||n._string[n._currentIndex]<"0"||n._string[n._currentIndex]>"9")throw new Error("There must be an exponent.");for(;n._currentIndex<n._endIndex&&n._string[n._currentIndex]>="0"&&n._string[n._currentIndex]<="9";)e*=10,e+=Number(n._string[n._currentIndex]),n._currentIndex+=1}let u=t+o;if(u*=s,e&&(u*=Math.pow(10,i*e)),l===n._currentIndex)throw new Error("Internal error: startIndex === source._currentIndex");return n._skipOptionalSpacesOrDelimiter(),u}const q={Z:"Z",M:"M",L:"L",C:"C",Q:"Q",A:"A",H:"H",V:"V",S:"S",T:"T",z:"Z",m:"m",l:"l",c:"c",q:"q",a:"a",h:"h",v:"v",s:"s",t:"t"};class Z{_string;_currentIndex;_endIndex;_prevCommand;constructor(n){this._string=n,this._currentIndex=0,this._endIndex=this._string.length,this._prevCommand=void 0,this._skipOptionalSpaces()}parseSegment(){const n=this._string[this._currentIndex];let e,t=q[n];if(void 0===t){if(void 0===this._prevCommand)throw new Error("Implicit command not allowed for first commands.");if(!("+"===n||"-"===n||"."===n||n>="0"&&n<="9")||"Z"===this._prevCommand)throw new Error("Remaining coordinates not found for implicit command");t="M"===this._prevCommand?"L":"m"===this._prevCommand?"l":this._prevCommand}else this._currentIndex+=1;this._prevCommand=t;const r=t.toUpperCase();if("H"===r||"V"===r?e=[W(this)]:"M"===r||"L"===r||"T"===r?e=[W(this),W(this)]:"S"===r||"Q"===r?e=[W(this),W(this),W(this),W(this)]:"C"===r?e=[W(this),W(this),W(this),W(this),W(this),W(this)]:"A"===r?e=[W(this),W(this),W(this),this._parseArcFlag(),this._parseArcFlag(),W(this),W(this)]:"Z"===r&&(this._skipOptionalSpaces(),e=[]),void 0===e)throw new Error("Unknown command");return{type:t,values:e}}hasMoreData(){return this._currentIndex<this._endIndex}initialCommandIsMoveTo(){if(!this.hasMoreData())return!0;const n=q[this._string[this._currentIndex]];return"M"===n||"m"===n}_isCurrentSpace(){const n=this._string[this._currentIndex];return n<=" "&&(" "===n||"\n"===n||"\t"===n||"\r"===n||"\f"===n)}_skipOptionalSpaces(){for(;this._currentIndex<this._endIndex&&this._isCurrentSpace();)this._currentIndex+=1;return this._currentIndex<this._endIndex}_skipOptionalSpacesOrDelimiter(){return!(this._currentIndex<this._endIndex&&!this._isCurrentSpace()&&","!==this._string[this._currentIndex])&&(this._skipOptionalSpaces()&&this._currentIndex<this._endIndex&&","===this._string[this._currentIndex]&&(this._currentIndex+=1,this._skipOptionalSpaces()),this._currentIndex<this._endIndex)}_parseArcFlag(){if(this._currentIndex>=this._endIndex)throw new Error("Unable to parse arc flag");let n;const e=this._string[this._currentIndex];if(this._currentIndex+=1,"0"===e)n=0;else{if("1"!==e)throw new Error("Unable to parse arc flag - arc flag must be 0 or 1");n=1}return this._skipOptionalSpacesOrDelimiter(),n}}const{ceil:$,min:J,max:K}=Math;function nn(r,o,s,i,l,c=0,u=0,a,d=!0,f=!0,g,h=0){const x="string"==typeof o?function(n){if(0===n.length)return[];if("m"!==n[0].type.toLowerCase())throw new Error("Invalid SVG - every new path must start with an M or m.");const e={p:[0,0]},t=[];let r,o=[];for(let s=0;s<n.length;s++){const i=n[s],l=i.type.toLowerCase();if(e.vals=i.values,i.type===l)if("v"===l)e.vals[0]+=e.p[1];else if("a"===l)e.vals[5]+=e.p[0],e.vals[6]+=e.p[1];else for(let n=0;n<e.vals.length;n++)e.vals[n]+=e.p[n%2];if("m"===l){o.length&&("z"!==r&&o.push(z(e)),t.push(o),o=[]),e.initialPoint=e.p=e.vals,r=l;continue}const c=H[l];if(!c)throw new Error("Invalid SVG - command not recognized.");const u=c(e);e.p=u[u.length-1],o.push(u),r=l}return o.length>0&&("z"!==r&&o.push(z(e)),t.push(o)),t}(function(n){if(!n.length)return[];const e=new Z(n),t=[];if(!e.initialCommandIsMoveTo())throw new Error("Path must start with m or M");for(;e.hasMoreData();)t.push(e.parseSegment());return t}(o)):o;let I=1;if(i/l>4){const n=i/4;I=n/l,l=n}const _=l/n,p=K(i,l),v=$(i/_),m=2*$(J(a,p)/_/2),C=function(n,e,r,o){const{gl:s,programs:i}=n;if(i[e])return i[e];const l=s.createProgram(),c=t(s,"#version 300 es\n\nprecision highp float;\n\nuniform vec2 uWidthHeight;\nin vec2 aUV;\nin ivec2 aCloseCellIdxRangePerCell;\nin ivec2 aCrossIdxRangePerCell;\nout vec2 vXY;\nflat out int instanceId;\nflat out ivec2 closeCellIdxRange;\nflat out ivec2 crossCellIdxRange;\n\n\nvoid main() {\n instanceId = gl_InstanceID;\n closeCellIdxRange = aCloseCellIdxRangePerCell;\n crossCellIdxRange = aCrossIdxRangePerCell;\n\n // drawn column-by-column\n float i = float(instanceId / 32); // column index\n float j = float(instanceId % 32); // row index\n\n vec2 trans = vec2(\n i / float(32),\n j / float(32)\n );\n\n vec2 uv = aUV + trans;\n\n float width = uWidthHeight.x;\n float height = uWidthHeight.y;\n\n vXY = vec2(\n height * uv.x,\n height * uv.y\n );\n\n float aspectRatio = width / height;\n\n gl_Position = vec4(\n vec2(\n (2.0*(uv.x / aspectRatio) - 1.0),\n (2.0*uv.y - 1.0)\n ),\n 0.0, 1.0\n );\n}\n",s.VERTEX_SHADER),u=t(s,o,s.FRAGMENT_SHADER);return s.attachShader(l,c),s.attachShader(l,u),s.linkProgram(l),i[e]={gl:s,program:l,attributes:{},uniforms:{},uniformBlocks:{},vertexShader:c,fragmentShader:u},i[e]}(r,`main${v}-${m}`,0,function(t,r){const o=e[1024*t+r];if(void 0!==o)return o;const s=`#version 300 es\n\nprecision highp float;\n\nuniform float uMaxDistance;\nuniform highp sampler2D uSegs;\nuniform highp isampler2D uCloseCellIdxs;\nuniform highp isampler2D uCrossCellIdxs;\nuniform int uIncl; // bit 0 -> incl inside, bit 1 -> incl outside\n\nuniform SegIdxRangePerCellBlock {\n ivec4 uSegIdxRangePerCell[${(n+2*r)*(t+2*r)/2}];\n};\nuniform SegIdxRangePerStripBlock {\n ivec4 uSegIdxRangePerStrip[16];\n};\n\nin vec2 vXY;\nflat in int instanceId;\nflat in ivec2 closeCellIdxRange;\nflat in ivec2 crossCellIdxRange;\nout vec4 FragColor;\n\n\nfloat absDistToSegment(vec2 point, vec2 lineA, vec2 lineB) {\n vec2 lineDir = lineB - lineA;\n float lenSq = dot(lineDir, lineDir);\n float t = clamp(dot(point - lineA, lineDir) / lenSq, 0.0, 1.0);\n vec2 linePt = lineA + t*lineDir;\n\n return distance(point, linePt);\n}\n\n\nvoid main() {\n ///////////////////////////////////////////////////////////////////////////\n // Project a ray to the left to check if it crosses the segment in order\n // to find the fragment's winding number to determine whether fragment\n // is inside or outside the shape.\n\n int crossIdxS = crossCellIdxRange.x;\n int crossLen = crossCellIdxRange.y;\n float winds = 0.0;\n // Iterate over all relevant cell indexes\n for (int i = crossIdxS; i < crossIdxS + crossLen; i++) {\n int crossIdx = texelFetch(uCrossCellIdxs, ivec2(i%256, i/256), 0).x;\n\n ivec2 uSegIdxRange = crossIdx % 2 == 0\n ? uSegIdxRangePerCell[crossIdx / 2].xy\n : uSegIdxRangePerCell[crossIdx / 2].zw;\n\n int segIdx = uSegIdxRange.x;\n int segLen = uSegIdxRange.y;\n\n for (int j = segIdx; j < segIdx + segLen; j++) {\n // Fetch segment from texture\n vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);\n\n // line segment's min-y is excluded\n bool crossing =\n (seg.y > vXY.y != seg.w > vXY.y) &&\n (vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);\n\n bool crossingUp = seg.y < seg.w;\n\n winds += crossing ? (crossingUp ? 1.0 : -1.0) : 0.0;\n }\n }\n\n {\n int cellIdx = (instanceId % 32);\n\n bool isEven = cellIdx % 2 == 0;\n\n ivec4 uSegIdxRange = uSegIdxRangePerStrip[cellIdx / 2];\n int segIdx = isEven ? uSegIdxRange.x : uSegIdxRange.z;\n int segLen = isEven ? uSegIdxRange.y : uSegIdxRange.w;\n \n\n for (int j = segIdx; j < segIdx + segLen; j++) {\n // Fetch segment from texture\n vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);\n\n // line segment's min-y is excluded\n bool crossing =\n (seg.y > vXY.y != seg.w > vXY.y) &&\n (vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);\n\n bool crossingUp = seg.y < seg.w;\n\n winds += crossing ? (crossingUp ? 1.0 : -1.0) : 0.0;\n }\n }\n\n\n bool inside = winds != 0.0;\n ///////////////////////////////////////////////////////////////////////////\n\n ///////////////////////////////////////////////////////////////////////////\n float res = 1.0; // sdf result\n\n if ((inside && (uIncl % 2 != 0)) || (!inside && (uIncl > 1))) {\n int cellIdxS = closeCellIdxRange.x;\n int cellLen = closeCellIdxRange.y;\n // Iterate over all relevant cell indexes\n for (int i = cellIdxS; i < cellIdxS + cellLen; i++) {\n int cellIdx = texelFetch(uCloseCellIdxs, ivec2(i%256, i/256), 0).x;\n\n ivec2 uSegIdxRange = cellIdx % 2 == 0\n ? uSegIdxRangePerCell[cellIdx / 2].xy\n : uSegIdxRangePerCell[cellIdx / 2].zw;\n\n int segIdx = uSegIdxRange.x;\n int segLen = uSegIdxRange.y;\n\n for (int j = segIdx; j < segIdx + segLen; j++) {\n // Fetch segment from texture\n vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);\n\n // Find unsigned distance to the segment; only the nearest will be kept\n float d = absDistToSegment(vXY, seg.xy, seg.zw);\n // Apply exponential transform\n float val = clamp(d / uMaxDistance, 0.0, 1.0);\n\n res = min(res, val);\n }\n }\n }\n ///////////////////////////////////////////////////////////////////////////\n\n // DEBUG!\n // float alpha = ((instanceId + instanceId/32) % 2 == 0 ? 0.3 : 0.5);\n\n\n float exponent = 2;\n res = pow(1.0 - val, exponent) * 0.5;\n\n float alpha = res == 1.0 ? 0.0 : 0.5;\n float red = inside ? 0.2 : 0.8;\n float green = abs(sin(25.0 * res));\n float blue = 0.5;\n\n FragColor = vec4(red, green, blue, alpha);\n}\n`;return e[1024*t+r]=s,s}(v,m)),{gl:S}=r;S.useProgram(C.program),V(r,C,x,s,a,d,f,g,c,u,i,l,v,_,m,I)}function en(n){if(void 0===n)return;const{gl:e,programs:t,textures:r}=n;for(let n in t){const{attributes:r,fragmentShader:o,vertexShader:s,uniformBlocks:i,program:l}=t[n];for(let n in i){const{buf:t}=i[n];e.deleteBuffer(t)}for(let n in r){const{buf:t}=r[n];e.deleteBuffer(t)}e.deleteShader(o),e.deleteShader(s),e.deleteProgram(l)}for(let n in r){const{tex:t}=r[n];e.deleteTexture(t)}}const tn=new WeakMap;function rn(n){{const e=tn.get(n);if(e)return e}const e={},t={},r={gl:n,textures:t,programs:e};return n.canvas.addEventListener("webglcontextlost",o=>{on(e),on(t),tn.delete(n),r.onContextLoss?.(o)},!1),tn.set(n,r),r}function on(n){Object.keys(n).forEach(e=>{delete n[e]})}export{en as freeGlContext,nn as generateSdf,rn as getWebGlContext};
|
|
1
|
+
const n=32,e="#version 300 es\n\nprecision highp float;\n\nuniform vec2 uWidthHeight;\nin vec2 aUV;\nin ivec2 aCloseCellIdxRangePerCell;\nin ivec2 aCrossIdxRangePerCell;\nout vec2 vXY;\nflat out int instanceId;\nflat out ivec2 closeCellIdxRange;\nflat out ivec2 crossCellIdxRange;\n\n\nvoid main() {\n instanceId = gl_InstanceID;\n closeCellIdxRange = aCloseCellIdxRangePerCell;\n crossCellIdxRange = aCrossIdxRangePerCell;\n\n // drawn column-by-column\n float i = float(instanceId / 32); // column index\n float j = float(instanceId % 32); // row index\n\n vec2 trans = vec2(\n i / float(32),\n j / float(32)\n );\n\n vec2 uv = aUV + trans;\n\n float width = uWidthHeight.x;\n float height = uWidthHeight.y;\n\n vXY = vec2(\n height * uv.x,\n height * uv.y\n );\n\n float aspectRatio = width / height;\n\n gl_Position = vec4(\n vec2(\n (2.0*(uv.x / aspectRatio) - 1.0),\n (2.0*uv.y - 1.0)\n ),\n 0.0, 1.0\n );\n}\n",t={};function r(e,r,o,s){const i=2**32*(256*e+r)+s,c=t[i];if(void 0!==c)return c;const l=`#version 300 es\n\nprecision highp float;\n\nuniform float uMaxDistance;\nuniform highp sampler2D uSegs;\nuniform highp isampler2D uCloseCellIdxs;\nuniform highp isampler2D uCrossCellIdxs;\n// bit 0 -> calc distance inside\n// bit 1 -> calc distance outside\n// bit 2 -> calc whether fragment is inside or outside (else outside is assumed with winds == 0.0)\nuniform int uTestInOut;\nuniform vec4 uCustom;\n\nuniform SegIdxRangePerCellBlock {\n ivec4 uSegIdxRangePerCell[${(n+2*r)*(e+2*r)/2}];\n};\nuniform SegIdxRangePerStripBlock {\n ivec4 uSegIdxRangePerStrip[16];\n};\n\nin vec2 vXY;\nflat in int instanceId;\nflat in ivec2 closeCellIdxRange;\nflat in ivec2 crossCellIdxRange;\nout vec4 FragColor;\n\n// testing!!\n// float rand(vec2 co) {\n// return mod(uCustom.w * fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453), 1.0);\n// }\n\nfloat absDistToSegment(vec2 point, vec2 lineA, vec2 lineB) {\n vec2 lineDir = lineB - lineA;\n float lenSq = dot(lineDir, lineDir);\n float t = clamp(dot(point - lineA, lineDir) / lenSq, 0.0, 1.0);\n vec2 linePt = lineA + t*lineDir;\n\n return distance(point, linePt);\n}\n\n\nvoid main() {\n ///////////////////////////////////////////////////////////////////////////\n // Calculate \`winds\`:\n //\n // Project a ray to the left to check if it crosses the segment in order\n // to find the fragment's winding number to determine whether fragment\n // is inside or outside the shape.\n ///////////////////////////////////////////////////////////////////////////\n\n float winds = 0.0;\n if ((uTestInOut & 4) == 0) {\n int crossIdxS = crossCellIdxRange.x;\n int crossLen = crossCellIdxRange.y;\n // Iterate over all relevant cell indexes\n for (int i = crossIdxS; i < crossIdxS + crossLen; i++) {\n int crossIdx = texelFetch(uCrossCellIdxs, ivec2(i%256, i/256), 0).x;\n\n ivec2 uSegIdxRange = crossIdx % 2 == 0\n ? uSegIdxRangePerCell[crossIdx / 2].xy\n : uSegIdxRangePerCell[crossIdx / 2].zw;\n\n int segIdx = uSegIdxRange.x;\n int segLen = uSegIdxRange.y;\n\n for (int j = segIdx; j < segIdx + segLen; j++) {\n // Fetch segment from texture\n vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);\n\n // line segment's min-y is excluded\n bool crossing =\n (seg.y > vXY.y != seg.w > vXY.y) &&\n (vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);\n\n bool crossingUp = seg.y < seg.w;\n\n winds += crossing ? (crossingUp ? -1.0 : 1.0) : 0.0;\n }\n }\n\n {\n int cellIdx = (instanceId % 32);\n\n bool isEven = cellIdx % 2 == 0;\n\n ivec4 uSegIdxRange = uSegIdxRangePerStrip[cellIdx / 2];\n int segIdx = isEven ? uSegIdxRange.x : uSegIdxRange.z;\n int segLen = isEven ? uSegIdxRange.y : uSegIdxRange.w;\n \n\n for (int j = segIdx; j < segIdx + segLen; j++) {\n // Fetch segment from texture\n vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);\n\n // line segment's min-y is excluded\n bool crossing =\n (seg.y > vXY.y != seg.w > vXY.y) &&\n (vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);\n\n bool crossingUp = seg.y < seg.w;\n\n winds += crossing ? (crossingUp ? -1.0 : 1.0) : 0.0;\n }\n }\n }\n bool inside = winds != 0.0;\n ///////////////////////////////////////////////////////////////////////////\n // Calculate \`res\`: the distance to the nearest curve\n ///////////////////////////////////////////////////////////////////////////\n float res = 1.0; // sdf result\n\n if ((inside && ((uTestInOut & 1) != 0)) || (!inside && ((uTestInOut & 2) != 0))) {\n int cellIdxS = closeCellIdxRange.x;\n int cellLen = closeCellIdxRange.y;\n // Iterate over all relevant cell indexes\n for (int i = cellIdxS; i < cellIdxS + cellLen; i++) {\n int cellIdx = texelFetch(uCloseCellIdxs, ivec2(i%256, i/256), 0).x;\n\n ivec2 uSegIdxRange = cellIdx % 2 == 0\n ? uSegIdxRangePerCell[cellIdx / 2].xy\n : uSegIdxRangePerCell[cellIdx / 2].zw;\n\n int segIdx = uSegIdxRange.x;\n int segLen = uSegIdxRange.y;\n\n for (int j = segIdx; j < segIdx + segLen; j++) {\n // Fetch segment from texture\n vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);\n\n // Find normalized unsigned distance to the segment; only the nearest will be kept\n float d = absDistToSegment(vXY, seg.xy, seg.zw);\n float val = clamp(d / uMaxDistance, 0.0, 1.0);\n\n res = min(res, val);\n }\n }\n }\n ///////////////////////////////////////////////////////////////////////////\n\n // DEBUG!\n // float alpha = ((instanceId + instanceId/32) % 2 == 0 ? 0.3 : 0.5);\n\n \n \n float exponent = uCustom.x;\n res = pow(1.0 - res, exponent);\n float alpha = res <= 0.0 ? 0.0 : 0.5;\n float red = inside ? 0.2 : 0.8;\n float green = abs(sin(25.0 * res));\n float blue = 0.5;\n\n\n FragColor = vec4(red, green, blue, alpha);\n}\n`;return t[i]=l,l}function o(n,e,t){const r=n.createShader(t);return n.shaderSource(r,e),n.compileShader(r),r}function s(n,e,t){const{gl:r,textures:o}=n;r.activeTexture(r.TEXTURE0+e);let s=o[t];return s?r.bindTexture(r.TEXTURE_2D,s.tex):(s=o[t]={tex:r.createTexture()},r.bindTexture(r.TEXTURE_2D,s.tex),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MIN_FILTER,r.NEAREST),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MAG_FILTER,r.NEAREST),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_S,r.CLAMP_TO_EDGE),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_T,r.CLAMP_TO_EDGE)),s}const{BYTE:i,UNSIGNED_BYTE:c,SHORT:l,UNSIGNED_SHORT:u,INT:a,UNSIGNED_INT:d,FLOAT:f,HALF_FLOAT:g,INT_2_10_10_10_REV:h,UNSIGNED_INT_2_10_10_10_REV:x}=WebGL2RenderingContext;function I(n){return(e,t,r)=>{const{gl:o,uniformBlocks:s,program:i}=n;let c=s[e];if(void 0===s[e]){const n=o.getUniformBlockIndex(i,e);o.uniformBlockBinding(i,n,t);const r=o.createBuffer();o.bindBuffer(o.UNIFORM_BUFFER,r),c={blockName:e,blockIndex:n,buf:r},s[e]=c}else o.bindBuffer(o.UNIFORM_BUFFER,c.buf);const{buf:l}=c;o.bufferData(o.UNIFORM_BUFFER,r,o.STATIC_DRAW),o.bindBufferBase(o.UNIFORM_BUFFER,t,l)}}function _(n){const e=n[0][0],t=n[0][1];for(let r=1;r<n.length;r++)if(e!==n[r][0]||t!==n[r][1])return!1;return!0}function p(n,e){if(0===e)return n[0];if(1===e)return n[n.length-1];if(4===n.length){const[[t,r],[o,s],[i,c],[l,u]]=n,a=t+(o-t)*e,d=o+(i-o)*e,f=a+(d-a)*e,g=r+(s-r)*e,h=s+(c-s)*e,x=g+(h-g)*e;return[f+(d+(i+(l-i)*e-d)*e-f)*e,x+(h+(c+(u-c)*e-h)*e-x)*e]}if(3===n.length){const[[t,r],[o,s],[i,c]]=n,l=t+(o-t)*e,u=r+(s-r)*e;return[l+(o+(i-o)*e-l)*e,u+(s+(c-s)*e-u)*e]}if(2===n.length){const[[t,r],[o,s]]=n;return[t+(o-t)*e,r+(s-r)*e]}if(1===n.length)return n[0];throw new Error("The given bezier curve must be of order <= 3.")}const{sqrt:v}=Math;function m(n,e){const t=n[0],r=n[1],o=e[0],s=e[1],i=r-s,c=o-t,l=t*s-o*r,u=v(i*i+c*c);return function(n){const e=n[0],o=n[1];return 0!==u?(i*e+c*o+l)/u:v((e-t)**2+(o-r)**2)}}{const n=[10,1];m([6,2],[6,2])(n)}function C(n,e,t){if(4===n.length)return function(n,e,t){return 0===e?1===t?n:function(n,e){const t=n[0],r=n[1],o=n[2],s=n[3],i=t[0],c=t[1],l=r[0],u=r[1],a=o[0],d=o[1],f=i-e*(i-l),g=l-e*(l-a),h=f-e*(f-g),x=c-e*(c-u),I=u-e*(u-d),_=x-e*(x-I);return[t,[f,x],[h,_],[h-e*(h-(g-e*(g-(a-e*(a-s[0]))))),_-e*(_-(I-e*(I-(d-e*(d-s[1])))))]]}(n,t):1===t?function(n,e){const t=n[0],r=n[1],o=n[2],s=n[3],i=t[0],c=t[1],l=r[0],u=r[1],a=o[0],d=o[1],f=i-e*(i-l),g=l-e*(l-a),h=a-e*(a-s[0]),x=f-e*(f-g),I=g-e*(g-h),_=c-e*(c-u),p=u-e*(u-d),v=d-e*(d-s[1]),m=_-e*(_-p),C=p-e*(p-v);return[[x-e*(x-I),m-e*(m-C)],[I,C],[h,v],s]}(n,e):function(n,e,t){const r=n[0],o=n[1],s=n[2],i=n[3],c=r[0],l=r[1],u=o[0],a=o[1],d=s[0],f=s[1],g=e*e,h=e*g,x=t*t,I=t*x,_=e*t,p=c-u,v=d-u,m=p+v,C=e*p,S=t*p,R=i[0]-c-3*v,E=l-a,T=f-a,b=E+T,w=e*E,A=t*E,P=i[1]-l-3*T;return[[h*R+(3*e*(e*m-p)+c),h*P+(3*e*(e*b-E)+l)],[_*(e*R+2*m)+(g*m+c-(S+2*C)),_*(e*P+2*b)+(g*b+l-(A+2*w))],[_*(t*R+2*m)+(x*m+c-(2*S+C)),_*(t*P+2*b)+(x*b+l-(2*A+w))],[I*R+(3*t*(t*m-p)+c),I*P+(3*t*(t*b-E)+l)]]}(n,e,t)}(n,e,t);if(3===n.length)return function(n,e,t){return 0===e?1===t?n:function(n,e){const t=n[0],r=n[1],o=n[2],s=t[0],i=t[1],c=r[0],l=r[1],u=e*e,a=s-c,d=i-l;return[t,[-e*a+s,-e*d+i],[u*(a+(o[0]-c))-(2*e*a-s),u*(d+(o[1]-l))-(2*e*d-i)]]}(n,t):1===t?function(n,e){const t=n[0],r=n[1],o=n[2],s=t[0],i=t[1],c=r[0],l=r[1],u=e*e,a=s-c,d=o[0]-c,f=i-l,g=o[1]-l;return[[u*(a+d)-(2*e*a-s),u*(f+g)-(2*e*f-i)],[e*d+c,e*g+l],o]}(n,e):function(n,e,t){const r=n[0],o=n[1],s=n[2],i=r[0],c=r[1],l=o[0],u=o[1],a=e*e,d=t*t,f=e*t,g=i-l,h=g+(s[0]-l),x=c-u,I=x+(s[1]-u);return[[a*h-(2*e*g-i),a*I-(2*e*x-c)],[f*h-(g*(t+e)-i),f*I-(x*(t+e)-c)],[d*h-(2*t*g-i),d*I-(2*t*x-c)]]}(n,e,t)}(n,e,t);throw new Error("The given bezier curve must be of order 2 or 3.")}function S(n,e){return[e[0]-n[0],e[1]-n[1]]}function R(n,e){return n[0]*e[0]+n[1]*e[1]}function E(n){const e=S(n[0],n[1]),t=S(n[1],n[3]),r=S(n[3],n[0]),o=S(n[0],n[2]),s=S(n[2],n[3]);return R(r,e)>0||R(t,r)>0||R(r,o)>0||R(s,r)>0}const{abs:T,max:b}=Math;function w(n){const e=m(n[0],n[3]),t=e(n[1]),r=e(n[2]);return(t*r<=0?4/9:3/4)*b(T(t),T(r))}function A(n){const e=S(n[0],n[1]),t=S(n[1],n[2]),r=S(n[2],n[0]);return R(r,e)>0||R(t,r)>0}const{abs:P}=Math;function D(n){if(n[0][0]===n[1][0]&&n[0][1]===n[1][1])return 0;const e=p(n,.5),t=m(n[0],n[2]);return P(t(e))}function y(n){const e=3===n.length?function(n){const e=[0],t=[1];for(;;){const r=e[e.length-1],o=t[t.length-1],s=C(n,r,o);if(!A(s)&&D(s)<=.5){if(e.push(t.pop()),1===o)return e;continue}const i=(r+o)/2;t.push(i)}}(n):function(n){const e=[0],t=[1];for(;;){const r=e[e.length-1],o=t[t.length-1],s=C(n,r,o);if(!E(s)&&w(s)<=.5){if(e.push(t.pop()),1===o)return e;continue}const i=(r+o)/2;t.push(i)}}(n),t=[];for(let r=0;r<e.length-1;r++){const o=p(n,e[r]),s=p(n,e[r+1]);t.push([o,s])}return t}function F(n){return Math.sqrt(n[0]*n[0]+n[1]*n[1])}const U=function(){const e=[{from:0,u:0,v:0}];for(let t=0;t<128;t++){e.push({from:t+.5,u:t+1,v:0}),e.push({from:t+.5,u:-t-1,v:0}),e.push({from:t+.5,u:0,v:t+1}),e.push({from:t+.5,u:0,v:-t-1});for(let r=0;r<n;r++){const n=F([.5+t,.5+r]),o=t+1,s=r+1;e.push({from:n,u:o,v:s}),0!==o&&e.push({from:n,u:-o,v:s}),0!==s&&e.push({from:n,u:o,v:-s}),0!==o&&0!==s&&e.push({from:n,u:-o,v:-s})}e.sort((n,e)=>n.from-e.from)}return e}(),{max:N,E:M}=Math;const{min:L,max:O,SQRT2:k}=Math;function B(e,t,r,o,s){const i=function(e,t,r,o,s){return function(i,c,l){let u=function(n){let e=function(n){let e=0,t=U.length-1,r=0;for(;e<=t;){r=e+t>>>1;const o=U[r].from;if(o===n)return r;n>o?e=r+1:t=r-1}return r}(n);if(n=N(0,n),0===e)return 0;for(;U[e].from===U[e-1].from;)e--;return e}((l-k*r)/r),a=Number.POSITIVE_INFINITY;for(;;){if(u>=U.length){a=r*(U[U.length-1].from-k);break}const{u:l,v:d,from:f}=U[u];if(r*f>o+k*r){a=r*(f-2*k);break}const g=l+i,h=d+c;if(g<0||g>=t+2*s||h<0||h>=n+2*s){u++;continue}const{lineSegs:x}=e[g][h];if(x.length>0){a=r*(f-k);break}u++}const{closeCells:d}=e[i][c];let f=O(0,u-1);for(;f<U.length;){const{from:l,u,v:g}=U[f];if(r*l>L(a,o)+2*k*r)break;const h=u+i,x=g+c;if(h<0||h>=t+2*s||x<0||x>=n+2*s){f++;continue}const{lineSegs:I}=e[h][x];I.length>0&&d.push((2*s+n)*h+x),f++}return a}}(e,t,r,o,s);let c=0,l=0;for(let e=s;e<t+s;e++){l=c;for(let t=s;t<n+s;t++)l=i(e,t,l),t===s&&(c=l)}}const{floor:X,ceil:j}=Math;function G(n,e,t,r,o,s){const i=r*s,[[c,l],[u,a]]=o,d=e+i,f=t+i,g=function(n,e){const[t,r]=n,[o,s]=t,[i,c]=r,[[l,u],[a,d]]=e,f=i-o,g=c-s,h=[];if(0!==f){const n=(l-o)/f;if(n>=0&&n<=1){const e=s+n*g;e>=u&&e<=d&&h.push({t:n,p:[l,e]})}const e=(a-o)/f;if(e>=0&&e<=1){const n=s+e*g;n>=u&&n<=d&&h.push({t:e,p:[a,n]})}}if(0!==g){const n=(u-s)/g;if(n>=0&&n<=1){const e=o+n*f;e>=l&&e<=a&&h.push({t:n,p:[e,u]})}const e=(d-s)/g;if(e>=0&&e<=1){const n=o+e*f;n>=l&&n<=a&&h.push({t:e,p:[n,d]})}}const x=new Set,I=h.filter(n=>{const{t:e}=n;return!x.has(e)&&(x.add(e),!0)});return I.sort((n,e)=>n.t-e.t).map(n=>n.p)}(o,[[-i,-i],[d,f]]),h=c>-i&&c<d&&l>-i&&l<f,x=u>-i&&u<d&&a>-i&&a<f;if(g.length<2&&!h&&!x)return;const I=u-c,_=a-l,p=u>c?r:-r,v=a>l?r:-r;let m=c,C=l,S=0;const R=p>0,E=v>0,T=R?X:j,b=E?X:j;for(;;){let e=r*T((m+p)/r),t=r*b((C+v)/r);const o=(e-c)/I,x=(t-l)/_,w=o<x&&0!==I||0===_,A=w?e:c+x*I,P=w?l+o*_:t;if((o>1||0===I)&&(x>1||0===_)){const e=X(u/r)+s,t=X(a/r)+s;n[e]?.[t]?.lineSegs.push([[m,C],[u,a]]);break}if(A<=-i||P<=-i||A>=d||P>=f){if(!h&&0===S){[m,C]=g[0],S++;continue}{const e=T(m/r)-(R?0:1)+s,t=b(C/r)-(E?0:1)+s,o=[[m,C],g[S]];n[e]?.[t]?.lineSegs.push(o);break}}const D=T(m/r)-(R?0:1)+s,y=b(C/r)-(E?0:1)+s;n[D]?.[y]?.lineSegs.push([[m,C],[A,P]]),m=A,C=P}}const Y=256,{floor:Q,ceil:V}=Math;function z(n,e,t){const r=e/n.length,[o,s]=t,i=function(n,e){const[t,r]=n,[o,s]=t,[i,c]=r,[l,u,a]=e,d=i-o,f=c-s,g=[],h=[];if(0!==d){const n=(l-o)/d;if(n>=0&&n<=1){const e=s+n*f;e>=u&&e<=a&&(g.push(n),h.push([l,e]))}}if(0!==f){const n=(u-s)/f;if(n>=0&&n<=1){const e=o+n*d;e<=l&&(g.push(n),h.push([e,u]))}const e=(a-s)/f;if(e>=0&&e<=1){const n=o+e*d;n<=l&&(g.push(e),h.push([n,a]))}}return 2===g.length?g[0]<g[1]?h:[h[1],h[0]]:h}(t,[0,0,e]),[c,l]=o,[u,a]=s,d=u-c,f=a-l,g=c<0&&l>0&&l<e,h=u<0&&a>0&&a<e;if(i.length<2&&!g&&!h&&(0!==c||0!==d||l<=0&&a<=0||l>=e&&a>=e))return;if(0===f)return;const x=a>l?r:-r;let I=c,_=l,p=0;const v=u>c,m=a>l,C=m?Q:V;for(;;){let t=v&&I<0||!v&&I>0?0:Number.NEGATIVE_INFINITY,o=r*C((_+x)/r);const u=(t-c)/d,a=(o-l)/f,h=u<a&&0!==d,S=h?0:c+a*d,R=h?l+u*f:o;if((u>1||0===d)&&a>1){const e=Q(s[1]/r);n[e]?.push([[I,_],s]);break}if(S>=0&&(0!==c||0!==d)||R<=0||R>=e){if(!g&&0===p){[I,_]=i[0],p++;continue}{const e=C(_/r)-(m?0:1),t=[[I,_],i[p]];n[e]?.push(t);break}}const E=C(_/r)-(m?0:1);n[E]?.push([[I,_],[S,R]]),I=S,_=R}}function H(e,t,r,o,i,c,l,u,a,d,h,x){const{gl:p}=e,{x:v=0,y:m=0,testInteriorExterior:C=!0,calcSdfForInside:S=!0,calcSdfForOutside:R=!0,customData:E=[1,0,0,0],glslRgbaCalcStr:T}=u,b=[],w=.03125,A=.03125;b.push(0,0,w,0,0,A),b.push(w,0,w,A,0,A);const P=new Float32Array(b),D=(F=t,(n,e,...t)=>{const{gl:r,uniforms:o}=F,s=o[e]||(o[e]=r.getUniformLocation(F.program,e));r[`uniform${n}`](s,...t)});var F;const U=function(n){return(e,t,r,o,s,i=0,c=0,l=0)=>{const{gl:u,attributes:a}=n,d=a[e]=a[e]??{buf:u.createBuffer(),loc:u.getAttribLocation(n.program,e),data:null},{loc:h,buf:x}=d;u.bindBuffer(u.ARRAY_BUFFER,x),r===f||r===g?u.vertexAttribPointer(h,t,r,!1,c,l):u.vertexAttribIPointer(h,t,r,c,l),u.enableVertexAttribArray(h),0!==i&&u.vertexAttribDivisor(h,i),s!==d.data&&(u.bufferData(u.ARRAY_BUFFER,s,o),d.data=s)}}(t),{lineSegPtCoords_Arr:N,segIdxs_PerCell_Range_Arr:M,closeCellIdxs_PerCell_Arr:L,closeCellIdxs_PerCell_Range_Arr:O,crossCellIdxs_PerCell_Arr:k,crossCellIdxs_perCell_Range_Arr:X,segIdxs_PerStrip_Range_Arr:j}=function(e,t,r,o,s,i,c,l=[0,0,t,r],u=1){const a=function(n,e,t,r){if(0===n[0]&&0===n[1]&&n[2]===e&&n[3]===t)return r;const[o,s,i,c]=n,l=e/i,u=t/c,a=[];for(let n=0;n<r.length;n++){const e=r[n],t=[];for(let n=0;n<e.length;n++){const r=e[n],i=[];for(let n=0;n<r.length;n++){const[e,t]=r[n],c=l*e-o,a=u*t-s;i.push([c,a])}t.push(i)}a.push(t)}return a}(l,t,r/u,e),d=function(n){let e=[];for(let t=0;t<n.length;t++){const r=n[t];for(let n=0;n<r.length;n++){const t=r[n];if(_(t))continue;if(2===t.length){e.push(t);continue}const o=y(t);e.push(...o.filter(n=>!_(n)))}}return e}(a),f=function(e,t){const r=[];for(let o=0;o<e+2*t;o++){const e=[];for(let r=0;r<n+2*t;r++)e.push({lineSegs:[],closeCells:[],crossingCells:[]});r.push(e)}return r}(o,c),g=new Array(n).fill(void 0).map(n=>[]);for(let n=0;n<d.length;n++){const e=d[n];G(f,t,r,s,e,c),z(g,r,e)}B(f,o,s,i,c),function(e,t,r){for(let o=r;o<t+r;o++)for(let t=r;t<n+r;t++){const s=e[o][t].crossingCells;for(let i=r;i<=o;i++)0!==e[i][t].lineSegs.length&&s.push((n+2*r)*i+t)}}(f,o,c);const h=[],x=[],I=[],p=[],v=[],m=[];let C=0,S=0,R=0;for(let e=0;e<o+2*c;e++)for(let t=0;t<n+2*c;t++){const r=f[e][t];if(e>=c&&e<o+c&&t>=c&&t<n+c){const{closeCells:n,crossingCells:e}=r,t=e.length,o=n.length;v.push(...e),I.push(...n),m.push(C,t),p.push(S,o),C+=t,S+=o}const{lineSegs:s}=r,i=s.length;x.push(R,i),R+=i,h.push(...s.flat(2))}const E=[];for(let e=0;e<n;e++){const n=g[e],t=n.length;E.push([R,t]),R+=t,h.push(...n.flat(2))}for(;I.length%Y!==0;)I.push(0);for(;v.length%Y!==0;)v.push(0);for(;h.length%1024!=0;)h.push(0);const T=new Float32Array(h),b=new Int32Array(x),w=new Int32Array(p);return{lineSegPtCoords_Arr:T,segIdxs_PerCell_Range_Arr:b,closeCellIdxs_PerCell_Arr:new Int32Array(I),closeCellIdxs_PerCell_Range_Arr:w,crossCellIdxs_PerCell_Arr:new Int32Array(v),crossCellIdxs_perCell_Range_Arr:new Int32Array(m),segIdxs_PerStrip_Range_Arr:new Int32Array(E.flat())}}(r,c,l,a,d,i,h,o,x);U("aUV",2,p.FLOAT,p.STATIC_DRAW,P),U("aCrossIdxRangePerCell",2,p.INT,p.STATIC_DRAW,X,1),U("aCloseCellIdxRangePerCell",2,p.INT,p.STATIC_DRAW,O,1),D("2f","uWidthHeight",c,l),D("1f","uMaxDistance",i),D("1i","uTestInOut",(S?1:0)+(R?2:0)+(C?4:0)),D("4f","uCustom",...E),I(t)("SegIdxRangePerCellBlock",0,M),I(t)("SegIdxRangePerStripBlock",1,j),s(e,0,"segs"),p.texImage2D(p.TEXTURE_2D,0,p.RGBA32F,Y,N.length/4/Y,0,p.RGBA,p.FLOAT,N);const Q=p.getUniformLocation(t.program,"uSegs");p.uniform1i(Q,0),s(e,1,"closeCellIdxsPerCell"),p.texImage2D(p.TEXTURE_2D,0,p.R32I,Y,L.length/Y,0,p.RED_INTEGER,p.INT,L);const V=p.getUniformLocation(t.program,"uCloseCellIdxs");p.uniform1i(V,1),s(e,2,"crossCellIdxsPerCell"),p.texImage2D(p.TEXTURE_2D,0,p.R32I,Y,k.length/Y,0,p.RED_INTEGER,p.INT,k);const H=p.getUniformLocation(t.program,"uCrossCellIdxs");p.uniform1i(H,2),x>1&&(p.enable(p.SCISSOR_TEST),p.scissor(v,m,c,l/x)),p.viewport(v,m,c,l),p.colorMask(!0,!0,!0,!0),p.drawArraysInstanced(p.TRIANGLES,0,6,a*n),x>1&&p.disable(p.SCISSOR_TEST)}function W(n){const e=[n.p,n.initialPoint];return n.prev2ndCubicControlPoint=void 0,n.prev2ndQuadraticControlPoint=void 0,e}const q={c:function(n){const e=[n.p,[n.vals[0],n.vals[1]],[n.vals[2],n.vals[3]],[n.vals[4],n.vals[5]]];return n.prev2ndCubicControlPoint=e[2],n.prev2ndQuadraticControlPoint=void 0,e},h:function(n){const e=[n.p,[n.vals[0],n.p[1]]];return n.prev2ndCubicControlPoint=void 0,n.prev2ndQuadraticControlPoint=void 0,e},l:function(n){const e=[n.p,n.vals];return n.prev2ndCubicControlPoint=void 0,n.prev2ndQuadraticControlPoint=void 0,e},q:function(n){const e=[n.vals[0],n.vals[1]],t=[n.vals[2],n.vals[3]];return n.prev2ndCubicControlPoint=void 0,n.prev2ndQuadraticControlPoint=e,[n.p,e,t]},s:function(n){const e=n.prev2ndCubicControlPoint?[n.p[0]-n.prev2ndCubicControlPoint[0]+n.p[0],n.p[1]-n.prev2ndCubicControlPoint[1]+n.p[1]]:n.p,t=[n.p,e,[n.vals[0],n.vals[1]],[n.vals[2],n.vals[3]]];return n.prev2ndCubicControlPoint=t[2],n.prev2ndQuadraticControlPoint=void 0,t},t:function(n){const e=n.prev2ndQuadraticControlPoint?[n.p[0]-n.prev2ndQuadraticControlPoint[0]+n.p[0],n.p[1]-n.prev2ndQuadraticControlPoint[1]+n.p[1]]:n.p,t=[n.vals[0],n.vals[1]];return n.prev2ndCubicControlPoint=void 0,n.prev2ndQuadraticControlPoint=e,[n.p,e,t]},v:function(n){const e=[n.p,[n.p[0],n.vals[0]]];return n.prev2ndCubicControlPoint=void 0,n.prev2ndQuadraticControlPoint=void 0,e},z:W};function Z(n){let e=0,t=0,r=1,o=0,s=1,i=1;const c=n._currentIndex;if(n._skipOptionalSpaces(),n._currentIndex<n._endIndex&&"+"===n._string[n._currentIndex]?n._currentIndex+=1:n._currentIndex<n._endIndex&&"-"===n._string[n._currentIndex]&&(n._currentIndex+=1,s=-1),n._currentIndex===n._endIndex||(n._string[n._currentIndex]<"0"||n._string[n._currentIndex]>"9")&&"."!==n._string[n._currentIndex])throw new Error("The first character of a number must be one of [0-9+-.].");const l=n._currentIndex;for(;n._currentIndex<n._endIndex&&n._string[n._currentIndex]>="0"&&n._string[n._currentIndex]<="9";)n._currentIndex+=1;if(n._currentIndex!==l){let e=n._currentIndex-1,r=1;for(;e>=l;)t+=r*(Number(n._string[e])-0),e-=1,r*=10}if(n._currentIndex<n._endIndex&&"."===n._string[n._currentIndex]){if(n._currentIndex+=1,n._currentIndex>=n._endIndex||n._string[n._currentIndex]<"0"||n._string[n._currentIndex]>"9")throw new Error("There must be a least one digit following the .");for(;n._currentIndex<n._endIndex&&n._string[n._currentIndex]>="0"&&n._string[n._currentIndex]<="9";)r*=10,o+=Number(n._string.charAt(n._currentIndex))/r,n._currentIndex+=1}if(n._currentIndex!==c&&n._currentIndex+1<n._endIndex&&("e"===n._string[n._currentIndex]||"E"===n._string[n._currentIndex])&&"x"!==n._string[n._currentIndex+1]&&"m"!==n._string[n._currentIndex+1]){if(n._currentIndex+=1,"+"===n._string[n._currentIndex]?n._currentIndex+=1:"-"===n._string[n._currentIndex]&&(n._currentIndex+=1,i=-1),n._currentIndex>=n._endIndex||n._string[n._currentIndex]<"0"||n._string[n._currentIndex]>"9")throw new Error("There must be an exponent.");for(;n._currentIndex<n._endIndex&&n._string[n._currentIndex]>="0"&&n._string[n._currentIndex]<="9";)e*=10,e+=Number(n._string[n._currentIndex]),n._currentIndex+=1}let u=t+o;if(u*=s,e&&(u*=Math.pow(10,i*e)),c===n._currentIndex)throw new Error("Internal error: startIndex === source._currentIndex");return n._skipOptionalSpacesOrDelimiter(),u}const $={Z:"Z",M:"M",L:"L",C:"C",Q:"Q",A:"A",H:"H",V:"V",S:"S",T:"T",z:"Z",m:"m",l:"l",c:"c",q:"q",a:"a",h:"h",v:"v",s:"s",t:"t"};class J{_string;_currentIndex;_endIndex;_prevCommand;constructor(n){this._string=n,this._currentIndex=0,this._endIndex=this._string.length,this._prevCommand=void 0,this._skipOptionalSpaces()}parseSegment(){const n=this._string[this._currentIndex];let e,t=$[n];if(void 0===t){if(void 0===this._prevCommand)throw new Error("Implicit command not allowed for first commands.");if(!("+"===n||"-"===n||"."===n||n>="0"&&n<="9")||"Z"===this._prevCommand)throw new Error("Remaining coordinates not found for implicit command");t="M"===this._prevCommand?"L":"m"===this._prevCommand?"l":this._prevCommand}else this._currentIndex+=1;this._prevCommand=t;const r=t.toUpperCase();if("H"===r||"V"===r?e=[Z(this)]:"M"===r||"L"===r||"T"===r?e=[Z(this),Z(this)]:"S"===r||"Q"===r?e=[Z(this),Z(this),Z(this),Z(this)]:"C"===r?e=[Z(this),Z(this),Z(this),Z(this),Z(this),Z(this)]:"A"===r?e=[Z(this),Z(this),Z(this),this._parseArcFlag(),this._parseArcFlag(),Z(this),Z(this)]:"Z"===r&&(this._skipOptionalSpaces(),e=[]),void 0===e)throw new Error("Unknown command");return{type:t,values:e}}hasMoreData(){return this._currentIndex<this._endIndex}initialCommandIsMoveTo(){if(!this.hasMoreData())return!0;const n=$[this._string[this._currentIndex]];return"M"===n||"m"===n}_isCurrentSpace(){const n=this._string[this._currentIndex];return n<=" "&&(" "===n||"\n"===n||"\t"===n||"\r"===n||"\f"===n)}_skipOptionalSpaces(){for(;this._currentIndex<this._endIndex&&this._isCurrentSpace();)this._currentIndex+=1;return this._currentIndex<this._endIndex}_skipOptionalSpacesOrDelimiter(){return!(this._currentIndex<this._endIndex&&!this._isCurrentSpace()&&","!==this._string[this._currentIndex])&&(this._skipOptionalSpaces()&&this._currentIndex<this._endIndex&&","===this._string[this._currentIndex]&&(this._currentIndex+=1,this._skipOptionalSpaces()),this._currentIndex<this._endIndex)}_parseArcFlag(){if(this._currentIndex>=this._endIndex)throw new Error("Unable to parse arc flag");let n;const e=this._string[this._currentIndex];if(this._currentIndex+=1,"0"===e)n=0;else{if("1"!==e)throw new Error("Unable to parse arc flag - arc flag must be 0 or 1");n=1}return this._skipOptionalSpacesOrDelimiter(),n}}const{ceil:K,min:nn,max:en}=Math,tn={x:0,y:0,testInteriorExterior:!0,calcSdfForInside:!0,calcSdfForOutside:!0,customData:[1,0,0,0]};function rn(t,s,i,c,l,u,a=tn){const d="string"==typeof s?function(n){if(0===n.length)return[];if("m"!==n[0].type.toLowerCase())throw new Error("Invalid SVG - every new path must start with an M or m.");const e={p:[0,0]},t=[];let r,o=[];for(let s=0;s<n.length;s++){const i=n[s],c=i.type.toLowerCase();if(e.vals=i.values,i.type===c)if("v"===c)e.vals[0]+=e.p[1];else if("a"===c)e.vals[5]+=e.p[0],e.vals[6]+=e.p[1];else for(let n=0;n<e.vals.length;n++)e.vals[n]+=e.p[n%2];if("m"===c){o.length&&("z"!==r&&o.push(W(e)),t.push(o),o=[]),e.initialPoint=e.p=e.vals,r=c;continue}const l=q[c];if(!l)throw new Error("Invalid SVG - command not recognized.");const u=l(e);e.p=u[u.length-1],o.push(u),r=c}return o.length>0&&("z"!==r&&o.push(W(e)),t.push(o)),t}(function(n){if(!n.length)return[];const e=new J(n),t=[];if(!e.initialCommandIsMoveTo())throw new Error("Path must start with m or M");for(;e.hasMoreData();)t.push(e.parseSegment());return t}(s)):s;let f=1;if(c/l>4){const n=c/4;f=n/l,l=n}const g=l/n,h=en(c,l),x=K(c/g),I=2*K(nn(u,h)/g/2),{glslRgbaCalcStr:_}=a,p=function(n){if(!n)return 0;let e=5381;for(let t=0;t<n.length;t++)e=(e<<5)+e+n.charCodeAt(t);return e>>>0}(_||""),v=function(n,e,t,r){const{gl:s,programs:i}=n;if(i[e])return i[e];const c=s.createProgram(),l=o(s,t,s.VERTEX_SHADER),u=o(s,r,s.FRAGMENT_SHADER);return s.attachShader(c,l),s.attachShader(c,u),s.linkProgram(c),i[e]={gl:s,program:c,attributes:{},uniforms:{},uniformBlocks:{},vertexShader:l,fragmentShader:u},i[e]}(t,`main${x}-${I}-${p}`,e,r(x,I,0,p)),{gl:m}=t;m.useProgram(v.program),H(t,v,d,i,u,c,l,a,x,g,I,f)}function on(n){if(void 0===n)return;const{gl:e,programs:t,textures:r}=n;for(let n in t){const{attributes:r,fragmentShader:o,vertexShader:s,uniformBlocks:i,program:c}=t[n];for(let n in i){const{buf:t}=i[n];e.deleteBuffer(t)}for(let n in r){const{buf:t}=r[n];e.deleteBuffer(t)}e.deleteShader(o),e.deleteShader(s),e.deleteProgram(c)}for(let n in r){const{tex:t}=r[n];e.deleteTexture(t)}}function sn(n,e,t){const r=n.createShader(e);if(n.shaderSource(r,t),n.compileShader(r),!n.getShaderParameter(r,n.COMPILE_STATUS))throw new Error(n.getShaderInfoLog(r))}const cn=new WeakMap;function ln(n){{const e=cn.get(n);if(e)return e}!function(n){try{sn(n,n.VERTEX_SHADER,e),sn(n,n.FRAGMENT_SHADER,r(32,2,0,0))}catch(n){throw console.log(n),n}}(n);const t={},o={},s={gl:n,textures:o,programs:t};return n.canvas.addEventListener("webglcontextlost",e=>{un(t),un(o),cn.delete(n),s.onContextLoss?.(e)},!1),cn.set(n,s),s}function un(n){Object.keys(n).forEach(e=>{delete n[e]})}export{on as freeGlContext,rn as generateSdf,ln as getWebGlContext};
|
package/node/debug-shaders.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getFragment } from "./shaders/fragment.js";
|
|
1
|
+
import { getFragment, GLSL_DEFAULT } from "./shaders/fragment.js";
|
|
2
2
|
import { vertex } from "./shaders/vertex.js";
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
@@ -17,7 +17,7 @@ function debugGlsl(gl, type, shaderStr) {
|
|
|
17
17
|
function debugShaders(gl) {
|
|
18
18
|
try {
|
|
19
19
|
debugGlsl(gl, gl.VERTEX_SHADER, vertex);
|
|
20
|
-
debugGlsl(gl, gl.FRAGMENT_SHADER, getFragment(32,
|
|
20
|
+
debugGlsl(gl, gl.FRAGMENT_SHADER, getFragment(32, 2, GLSL_DEFAULT, 0));
|
|
21
21
|
}
|
|
22
22
|
catch (e) {
|
|
23
23
|
console.log(e);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug-shaders.js","sourceRoot":"","sources":["../src/debug-shaders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"debug-shaders.js","sourceRoot":"","sources":["../src/debug-shaders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C;;;;;GAKG;AACH,SAAS,SAAS,CACV,EAA0B,EAC1B,IAAyD,EACzD,SAAiB;IAErB,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAE,CAAC;IACjC,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAC9B,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IACpB,IAAI,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAE,CAAC,CAAC;IAC7C,CAAC;AACL,CAAC;AAGD,SAAS,YAAY,CACb,EAA0B;IAE9B,IAAI,CAAC;QACD,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QACxC,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,eAAe,EAAE,WAAW,CAAC,EAAE,EAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACf,MAAM,CAAC,CAAC;IACZ,CAAC;AACL,CAAC;AAGD,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,CAAA"}
|
package/node/generate-sdf.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { GlContext } from './types/gl-context.js';
|
|
2
|
+
interface SdfOptions {
|
|
3
|
+
/** the position where to draw, x-coordinate */
|
|
4
|
+
readonly x?: number | undefined;
|
|
5
|
+
readonly y?: number | undefined;
|
|
6
|
+
readonly testInteriorExterior?: boolean | undefined;
|
|
7
|
+
readonly calcSdfForInside?: boolean | undefined;
|
|
8
|
+
readonly calcSdfForOutside?: boolean | undefined;
|
|
9
|
+
readonly customData?: [number, number, number, number] | undefined;
|
|
10
|
+
readonly glslRgbaCalcStr?: string | undefined;
|
|
11
|
+
}
|
|
2
12
|
/**
|
|
3
13
|
* Generates an sdf (signed distance field) from the given bezier curves,
|
|
4
14
|
* viewbox, etc. and renders the result
|
|
@@ -9,16 +19,77 @@ import { GlContext } from './types/gl-context.js';
|
|
|
9
19
|
* thereof) given by given by their ordered control points,
|
|
10
20
|
* e.g. `[ [[0,0],[1,1],[2,1],[2,0]], [[2,0],[7,2],[1,5],[8,6]], ... ]` **OR**
|
|
11
21
|
* * an SVG string, e.g. "M26.53 478.83 C028.89 481.61 031.33 484.32 ..."
|
|
22
|
+
* @param viewbox the viewbox given as `[x1,x2,y1,y2]` (**not as** `[x,y,widht,height]`)
|
|
12
23
|
* @param width the width of the drawing rectangle
|
|
13
24
|
* @param height the height of the drawing rectangle
|
|
14
|
-
* @param viewbox the viewbox
|
|
15
25
|
* @param maxDistance maximum sdf distance
|
|
16
|
-
* @param
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* @param x the position where to draw, x-coordinate
|
|
20
|
-
* @param y the position where to draw, y-coordinate
|
|
21
|
-
* @param
|
|
26
|
+
* @param options additional options (see below)
|
|
27
|
+
*
|
|
28
|
+
* **The following are properties of the `options` parameters**
|
|
29
|
+
* @param x defaults to `0`; the position where to draw on the canvas, x-coordinate
|
|
30
|
+
* @param y defaults to `0`; the position where to draw on the canvas, y-coordinate
|
|
31
|
+
* @param testInteriorExterior defaults to `true`;
|
|
32
|
+
* if `false` winds will always be `0.0` and only an un-signed sdf can be calculated since all
|
|
33
|
+
* fragments are considered outside
|
|
34
|
+
* @param calcSdfForInside defaults to `true`;
|
|
35
|
+
* if `false` the sdf will not be calculate for the inside of the shape, in the shader, `res` will always be `1.0`
|
|
36
|
+
* @param calcSdfForOutside defaults to `true`;
|
|
37
|
+
* if `false` the sdf will not be calculate for the outside of the shape, in the shader, `res` will always be `1.0`
|
|
38
|
+
* @param customData optional custom data (must be an array of 4 numbers) to send to
|
|
39
|
+
* the fragment shader as a uniform, e.g. exponent, scale, a timer, or whatever
|
|
40
|
+
* @param glslRgbaCalcStr a glsl string (#version 300 es) inserted at the end of
|
|
41
|
+
* the fragment shader to modify the output frag color in any way (you can also discard the fragment);
|
|
42
|
+
* see below for available variables that can be used;
|
|
43
|
+
*
|
|
44
|
+
* defaults to (designed to match webgl-sdf-generator)
|
|
45
|
+
* ```glsl
|
|
46
|
+
* float exponent = uCustom.x;
|
|
47
|
+
* res = (pow(1.0 - res, exponent) * 0.5) * (inside ? -1.0 : 1.0);
|
|
48
|
+
* float red = res;
|
|
49
|
+
* float green = res;
|
|
50
|
+
* float blue = res;
|
|
51
|
+
* float alpha = res;
|
|
52
|
+
*
|
|
53
|
+
* ```
|
|
54
|
+
* You must define and assign \`red\`, \`green\`, \`blue\` and \`alpha\`.
|
|
55
|
+
*
|
|
56
|
+
* Usable variables:
|
|
57
|
+
* ```glsl
|
|
58
|
+
* // the result of the distance calculation for this fragment, a value from 0.0 to 1.0
|
|
59
|
+
* // with 1.0 occuring when the fragment is >= maxDistance away and 0.0 when the
|
|
60
|
+
* // fragment is exactly on a curve
|
|
61
|
+
* float res
|
|
62
|
+
*
|
|
63
|
+
* // the number of anti-clockwise winds around the fragment, a value != 0
|
|
64
|
+
* // means the fragment is inside the shape
|
|
65
|
+
* float winds
|
|
66
|
+
*
|
|
67
|
+
* // 4 custom values set via an options parameter of `generateSdf`; defaults to `[1,0,0,0]`;
|
|
68
|
+
* // the first value is used as the exponent within the default `glslRgbaCalcStr`
|
|
69
|
+
* vec4 uCustom;
|
|
70
|
+
*
|
|
71
|
+
* // the max distance value supplied via `generateSdf`
|
|
72
|
+
* float uMaxDistance
|
|
73
|
+
*
|
|
74
|
+
* // bit 0 -> calc sdf when fragment is inside; defaults to 1,
|
|
75
|
+
* // bit 1 -> calc sdf when fragment is outside; defaults to 1
|
|
76
|
+
* // bit 1 -> calc `winds` (required for signing the distance); defaults to 1
|
|
77
|
+
* // note: when the distance calculation is not done (via options from `generateSdf`),
|
|
78
|
+
* `res` will be set to 1.0 (max distance away)
|
|
79
|
+
* int uTestInOut
|
|
80
|
+
*
|
|
81
|
+
* // the original x,y coordinates of the fragment in the original space provided
|
|
82
|
+
* // via the `viewbox` in `generateSdf`, e.g. the very bottom-left fragment
|
|
83
|
+
* // will have vXY == (viebox[0], viebox[1]) and the very top right will have
|
|
84
|
+
* // coordinates vXY == (viebox[2], viebox[3])
|
|
85
|
+
* vec2 vXY
|
|
86
|
+
*
|
|
87
|
+
* // whether the point is inside or outside the shape, often used to sign `res`
|
|
88
|
+
* // it is identical to `winds != 0`
|
|
89
|
+
* bool inside
|
|
90
|
+
*
|
|
91
|
+
* // pretty much useless unless you want to create a checkerboard pattern for no good reason
|
|
92
|
+
* int instanceId
|
|
22
93
|
*/
|
|
23
|
-
declare function generateSdf(glContext: GlContext, bezierCurves_or_svgStr: (number[][])[][] | string, viewbox: [number, number, number, number], width: number, height: number,
|
|
24
|
-
export { generateSdf };
|
|
94
|
+
declare function generateSdf(glContext: GlContext, bezierCurves_or_svgStr: (number[][])[][] | string, viewbox: [number, number, number, number], width: number, height: number, maxDistance: number, options?: SdfOptions): void;
|
|
95
|
+
export { generateSdf, SdfOptions };
|
package/node/generate-sdf.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
// import { getWebGLContext } from './webgl-utils/get-gl-context.js';
|
|
2
2
|
import { vertex } from './shaders/vertex.js';
|
|
3
|
-
import { getFragment } from './shaders/fragment.js';
|
|
3
|
+
import { getFragment, GLSL_DEFAULT } from './shaders/fragment.js';
|
|
4
4
|
import { initProgram } from './webgl-utils/use-program.js';
|
|
5
5
|
import { mainProgram } from './main-program.js';
|
|
6
6
|
import { ROW_COUNT } from './row-count.js';
|
|
7
7
|
import { getPathsFromStr } from './svg/get-paths-from-str.js';
|
|
8
8
|
import { MAX_ASPECT_RATIO_BEFORE_STRETCH } from './max-aspect-ratio-before-stretch.js';
|
|
9
9
|
const { ceil, min, max } = Math;
|
|
10
|
+
const defaultSdfOptions = {
|
|
11
|
+
x: 0, y: 0,
|
|
12
|
+
testInteriorExterior: true,
|
|
13
|
+
calcSdfForInside: true, calcSdfForOutside: true,
|
|
14
|
+
customData: [1, 0, 0, 0]
|
|
15
|
+
};
|
|
10
16
|
/**
|
|
11
17
|
* Generates an sdf (signed distance field) from the given bezier curves,
|
|
12
18
|
* viewbox, etc. and renders the result
|
|
@@ -17,24 +23,82 @@ const { ceil, min, max } = Math;
|
|
|
17
23
|
* thereof) given by given by their ordered control points,
|
|
18
24
|
* e.g. `[ [[0,0],[1,1],[2,1],[2,0]], [[2,0],[7,2],[1,5],[8,6]], ... ]` **OR**
|
|
19
25
|
* * an SVG string, e.g. "M26.53 478.83 C028.89 481.61 031.33 484.32 ..."
|
|
26
|
+
* @param viewbox the viewbox given as `[x1,x2,y1,y2]` (**not as** `[x,y,widht,height]`)
|
|
20
27
|
* @param width the width of the drawing rectangle
|
|
21
28
|
* @param height the height of the drawing rectangle
|
|
22
|
-
* @param viewbox the viewbox
|
|
23
29
|
* @param maxDistance maximum sdf distance
|
|
24
|
-
* @param
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* @param x the position where to draw, x-coordinate
|
|
28
|
-
* @param y the position where to draw, y-coordinate
|
|
29
|
-
* @param
|
|
30
|
+
* @param options additional options (see below)
|
|
31
|
+
*
|
|
32
|
+
* **The following are properties of the `options` parameters**
|
|
33
|
+
* @param x defaults to `0`; the position where to draw on the canvas, x-coordinate
|
|
34
|
+
* @param y defaults to `0`; the position where to draw on the canvas, y-coordinate
|
|
35
|
+
* @param testInteriorExterior defaults to `true`;
|
|
36
|
+
* if `false` winds will always be `0.0` and only an un-signed sdf can be calculated since all
|
|
37
|
+
* fragments are considered outside
|
|
38
|
+
* @param calcSdfForInside defaults to `true`;
|
|
39
|
+
* if `false` the sdf will not be calculate for the inside of the shape, in the shader, `res` will always be `1.0`
|
|
40
|
+
* @param calcSdfForOutside defaults to `true`;
|
|
41
|
+
* if `false` the sdf will not be calculate for the outside of the shape, in the shader, `res` will always be `1.0`
|
|
42
|
+
* @param customData optional custom data (must be an array of 4 numbers) to send to
|
|
43
|
+
* the fragment shader as a uniform, e.g. exponent, scale, a timer, or whatever
|
|
44
|
+
* @param glslRgbaCalcStr a glsl string (#version 300 es) inserted at the end of
|
|
45
|
+
* the fragment shader to modify the output frag color in any way (you can also discard the fragment);
|
|
46
|
+
* see below for available variables that can be used;
|
|
47
|
+
*
|
|
48
|
+
* defaults to (designed to match webgl-sdf-generator)
|
|
49
|
+
* ```glsl
|
|
50
|
+
* float exponent = uCustom.x;
|
|
51
|
+
* res = (pow(1.0 - res, exponent) * 0.5) * (inside ? -1.0 : 1.0);
|
|
52
|
+
* float red = res;
|
|
53
|
+
* float green = res;
|
|
54
|
+
* float blue = res;
|
|
55
|
+
* float alpha = res;
|
|
56
|
+
*
|
|
57
|
+
* ```
|
|
58
|
+
* You must define and assign \`red\`, \`green\`, \`blue\` and \`alpha\`.
|
|
59
|
+
*
|
|
60
|
+
* Usable variables:
|
|
61
|
+
* ```glsl
|
|
62
|
+
* // the result of the distance calculation for this fragment, a value from 0.0 to 1.0
|
|
63
|
+
* // with 1.0 occuring when the fragment is >= maxDistance away and 0.0 when the
|
|
64
|
+
* // fragment is exactly on a curve
|
|
65
|
+
* float res
|
|
66
|
+
*
|
|
67
|
+
* // the number of anti-clockwise winds around the fragment, a value != 0
|
|
68
|
+
* // means the fragment is inside the shape
|
|
69
|
+
* float winds
|
|
70
|
+
*
|
|
71
|
+
* // 4 custom values set via an options parameter of `generateSdf`; defaults to `[1,0,0,0]`;
|
|
72
|
+
* // the first value is used as the exponent within the default `glslRgbaCalcStr`
|
|
73
|
+
* vec4 uCustom;
|
|
74
|
+
*
|
|
75
|
+
* // the max distance value supplied via `generateSdf`
|
|
76
|
+
* float uMaxDistance
|
|
77
|
+
*
|
|
78
|
+
* // bit 0 -> calc sdf when fragment is inside; defaults to 1,
|
|
79
|
+
* // bit 1 -> calc sdf when fragment is outside; defaults to 1
|
|
80
|
+
* // bit 1 -> calc `winds` (required for signing the distance); defaults to 1
|
|
81
|
+
* // note: when the distance calculation is not done (via options from `generateSdf`),
|
|
82
|
+
* `res` will be set to 1.0 (max distance away)
|
|
83
|
+
* int uTestInOut
|
|
84
|
+
*
|
|
85
|
+
* // the original x,y coordinates of the fragment in the original space provided
|
|
86
|
+
* // via the `viewbox` in `generateSdf`, e.g. the very bottom-left fragment
|
|
87
|
+
* // will have vXY == (viebox[0], viebox[1]) and the very top right will have
|
|
88
|
+
* // coordinates vXY == (viebox[2], viebox[3])
|
|
89
|
+
* vec2 vXY
|
|
90
|
+
*
|
|
91
|
+
* // whether the point is inside or outside the shape, often used to sign `res`
|
|
92
|
+
* // it is identical to `winds != 0`
|
|
93
|
+
* bool inside
|
|
94
|
+
*
|
|
95
|
+
* // pretty much useless unless you want to create a checkerboard pattern for no good reason
|
|
96
|
+
* int instanceId
|
|
30
97
|
*/
|
|
31
|
-
function generateSdf(glContext, bezierCurves_or_svgStr, viewbox, width, height,
|
|
32
|
-
// TODO
|
|
33
|
-
channel = 0) {
|
|
98
|
+
function generateSdf(glContext, bezierCurves_or_svgStr, viewbox, width, height, maxDistance, options = defaultSdfOptions) {
|
|
34
99
|
const psss = typeof bezierCurves_or_svgStr === 'string'
|
|
35
100
|
? getPathsFromStr(bezierCurves_or_svgStr)
|
|
36
101
|
: bezierCurves_or_svgStr;
|
|
37
|
-
// const glContext = getWebGLContext(gl);
|
|
38
102
|
let stretch = 1;
|
|
39
103
|
const aspectRatio = width / height;
|
|
40
104
|
if (aspectRatio > MAX_ASPECT_RATIO_BEFORE_STRETCH) {
|
|
@@ -46,11 +110,24 @@ channel = 0) {
|
|
|
46
110
|
const maxDim = max(width, height);
|
|
47
111
|
const colCount = ceil(width / cellSize);
|
|
48
112
|
const padCount = 2 * ceil(min(maxDistance, maxDim) / cellSize / 2);
|
|
49
|
-
const
|
|
113
|
+
const { glslRgbaCalcStr } = options;
|
|
114
|
+
const hash = calcStrHash(glslRgbaCalcStr || '');
|
|
115
|
+
const programMain = initProgram(glContext, `main${colCount}-${padCount}-${hash}`, vertex, getFragment(colCount, padCount, glslRgbaCalcStr || GLSL_DEFAULT, hash));
|
|
50
116
|
const { gl } = glContext;
|
|
51
|
-
// debugShaders(gl); // comment for production
|
|
52
117
|
gl.useProgram(programMain.program);
|
|
53
|
-
mainProgram(glContext, programMain, psss, viewbox, maxDistance,
|
|
118
|
+
mainProgram(glContext, programMain, psss, viewbox, maxDistance, width, height, options, colCount, cellSize, padCount, stretch);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Calculates and returns a hash of the given string
|
|
122
|
+
*/
|
|
123
|
+
function calcStrHash(str) {
|
|
124
|
+
if (!str)
|
|
125
|
+
return 0;
|
|
126
|
+
let hash = 5381;
|
|
127
|
+
for (let i = 0; i < str.length; i++) {
|
|
128
|
+
hash = ((hash << 5) + hash) + str.charCodeAt(i);
|
|
129
|
+
}
|
|
130
|
+
return hash >>> 0; // Convert to unsigned 32-bit integer
|
|
54
131
|
}
|
|
55
132
|
export { generateSdf };
|
|
56
133
|
//# sourceMappingURL=generate-sdf.js.map
|
package/node/generate-sdf.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-sdf.js","sourceRoot":"","sources":["../src/generate-sdf.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"generate-sdf.js","sourceRoot":"","sources":["../src/generate-sdf.ts"],"names":[],"mappings":"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"}
|
package/node/main-program.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Program } from './types/program.js';
|
|
2
2
|
import { GlContext } from './types/gl-context.js';
|
|
3
|
-
|
|
3
|
+
import { SdfOptions } from './generate-sdf.js';
|
|
4
|
+
declare function mainProgram(glContext: GlContext, programMain: Program, psss: number[][][][], viewbox: [number, number, number, number], maxDistance: number, width: number, height: number, options: SdfOptions, colCount: number, cellSize: number, padCount: number, stretch: number): void;
|
|
4
5
|
export { mainProgram };
|
package/node/main-program.js
CHANGED
|
@@ -8,8 +8,9 @@ import { ROW_COUNT } from './row-count.js';
|
|
|
8
8
|
const SEG_TEX_INDEX = 0;
|
|
9
9
|
const CELL_TEX_INDEX = 1;
|
|
10
10
|
const CROSS_TEX_INDEX = 2;
|
|
11
|
-
function mainProgram(glContext, programMain, psss, viewbox, maxDistance,
|
|
11
|
+
function mainProgram(glContext, programMain, psss, viewbox, maxDistance, width, height, options, colCount, cellSize, padCount, stretch) {
|
|
12
12
|
const { gl } = glContext;
|
|
13
|
+
const { x = 0, y = 0, testInteriorExterior = true, calcSdfForInside = true, calcSdfForOutside = true, customData = [1, 0, 0, 0], glslRgbaCalcStr } = options;
|
|
13
14
|
const vertices = [];
|
|
14
15
|
const x0 = 0;
|
|
15
16
|
const y0 = 0;
|
|
@@ -33,7 +34,9 @@ function mainProgram(glContext, programMain, psss, viewbox, maxDistance, inclIns
|
|
|
33
34
|
// Init/update uniforms
|
|
34
35
|
setUniform_('2f', 'uWidthHeight', width, height);
|
|
35
36
|
setUniform_('1f', 'uMaxDistance', maxDistance);
|
|
36
|
-
setUniform_('1i', '
|
|
37
|
+
setUniform_('1i', 'uTestInOut', (calcSdfForInside ? 1 : 0) +
|
|
38
|
+
(calcSdfForOutside ? 2 : 0) +
|
|
39
|
+
(testInteriorExterior ? 4 : 0));
|
|
37
40
|
setUniform_('4f', 'uCustom', ...customData);
|
|
38
41
|
setUniformBlock(programMain)('SegIdxRangePerCellBlock', 0, segIdxs_PerCell_Range_Arr);
|
|
39
42
|
setUniformBlock(programMain)('SegIdxRangePerStripBlock', 1, segIdxs_PerStrip_Range_Arr);
|
|
@@ -89,6 +92,7 @@ function mainProgram(glContext, programMain, psss, viewbox, maxDistance, inclIns
|
|
|
89
92
|
gl.scissor(x, y, width, height / stretch);
|
|
90
93
|
}
|
|
91
94
|
gl.viewport(x, y, width, height);
|
|
95
|
+
gl.colorMask(true, true, true, true);
|
|
92
96
|
// draw a square colCount * ROW_COUNT times - 6 vertics
|
|
93
97
|
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, colCount * ROW_COUNT);
|
|
94
98
|
if (stretch > 1) {
|
package/node/main-program.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-program.js","sourceRoot":"","sources":["../src/main-program.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAErE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"main-program.js","sourceRoot":"","sources":["../src/main-program.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
declare
|
|
2
|
-
|
|
1
|
+
declare const GLSL_PATTERN1 = "\n float exponent = uCustom.x;\n res = pow(1.0 - res, exponent);\n float alpha = res <= 0.0 ? 0.0 : 0.5;\n float red = inside ? 0.2 : 0.8;\n float green = abs(sin(25.0 * res));\n float blue = 0.5;\n";
|
|
2
|
+
declare const GLSL_DEFAULT = "\n float exponent = uCustom.x;\n res = (pow(1.0 - res, exponent) * 0.5) * (inside ? -1.0 : 1.0);\n float red = res;\n float green = res;\n float blue = res;\n float alpha = res;\n";
|
|
3
|
+
declare function getFragment(colCount: number, padCount: number, calcFragColorStr: string, hash: number): string;
|
|
4
|
+
export { getFragment, GLSL_PATTERN1, GLSL_DEFAULT };
|
package/node/shaders/fragment.js
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
import { ROW_COUNT } from "../row-count.js";
|
|
2
2
|
const cache = {};
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const GLSL_PATTERN1 = `
|
|
4
|
+
float exponent = uCustom.x;
|
|
5
|
+
res = pow(1.0 - res, exponent);
|
|
6
|
+
float alpha = res <= 0.0 ? 0.0 : 0.5;
|
|
7
|
+
float red = inside ? 0.2 : 0.8;
|
|
8
|
+
float green = abs(sin(25.0 * res));
|
|
9
|
+
float blue = 0.5;
|
|
10
|
+
`;
|
|
11
|
+
const GLSL_DEFAULT = `
|
|
12
|
+
float exponent = uCustom.x;
|
|
13
|
+
res = (pow(1.0 - res, exponent) * 0.5) * (inside ? -1.0 : 1.0);
|
|
14
|
+
float red = res;
|
|
15
|
+
float green = res;
|
|
16
|
+
float blue = res;
|
|
17
|
+
float alpha = res;
|
|
18
|
+
`;
|
|
19
|
+
function getFragment(colCount, padCount, calcFragColorStr, hash) {
|
|
20
|
+
// `colCount` and `padCount` can take at most 8 bits and `has` 32 bits and
|
|
21
|
+
// we have at least 53 bits to play with so we're fine
|
|
22
|
+
const key = (2 ** 32) * (256 * colCount + padCount) + hash;
|
|
23
|
+
const fragment = cache[key];
|
|
5
24
|
if (fragment !== undefined) {
|
|
6
25
|
return fragment;
|
|
7
26
|
}
|
|
@@ -14,7 +33,11 @@ uniform float uMaxDistance;
|
|
|
14
33
|
uniform highp sampler2D uSegs;
|
|
15
34
|
uniform highp isampler2D uCloseCellIdxs;
|
|
16
35
|
uniform highp isampler2D uCrossCellIdxs;
|
|
17
|
-
|
|
36
|
+
// bit 0 -> calc distance inside
|
|
37
|
+
// bit 1 -> calc distance outside
|
|
38
|
+
// bit 2 -> calc whether fragment is inside or outside (else outside is assumed with winds == 0.0)
|
|
39
|
+
uniform int uTestInOut;
|
|
40
|
+
uniform vec4 uCustom;
|
|
18
41
|
|
|
19
42
|
uniform SegIdxRangePerCellBlock {
|
|
20
43
|
ivec4 uSegIdxRangePerCell[${(ROW_COUNT + 2 * padCount) * (colCount + 2 * padCount) / 2}];
|
|
@@ -29,6 +52,10 @@ flat in ivec2 closeCellIdxRange;
|
|
|
29
52
|
flat in ivec2 crossCellIdxRange;
|
|
30
53
|
out vec4 FragColor;
|
|
31
54
|
|
|
55
|
+
// testing!!
|
|
56
|
+
// float rand(vec2 co) {
|
|
57
|
+
// return mod(uCustom.w * fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453), 1.0);
|
|
58
|
+
// }
|
|
32
59
|
|
|
33
60
|
float absDistToSegment(vec2 point, vec2 lineA, vec2 lineB) {
|
|
34
61
|
vec2 lineDir = lineB - lineA;
|
|
@@ -42,72 +69,75 @@ float absDistToSegment(vec2 point, vec2 lineA, vec2 lineB) {
|
|
|
42
69
|
|
|
43
70
|
void main() {
|
|
44
71
|
///////////////////////////////////////////////////////////////////////////
|
|
72
|
+
// Calculate \`winds\`:
|
|
73
|
+
//
|
|
45
74
|
// Project a ray to the left to check if it crosses the segment in order
|
|
46
75
|
// to find the fragment's winding number to determine whether fragment
|
|
47
76
|
// is inside or outside the shape.
|
|
77
|
+
///////////////////////////////////////////////////////////////////////////
|
|
48
78
|
|
|
49
|
-
int crossIdxS = crossCellIdxRange.x;
|
|
50
|
-
int crossLen = crossCellIdxRange.y;
|
|
51
79
|
float winds = 0.0;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
int
|
|
80
|
+
if ((uTestInOut & 4) == 0) {
|
|
81
|
+
int crossIdxS = crossCellIdxRange.x;
|
|
82
|
+
int crossLen = crossCellIdxRange.y;
|
|
83
|
+
// Iterate over all relevant cell indexes
|
|
84
|
+
for (int i = crossIdxS; i < crossIdxS + crossLen; i++) {
|
|
85
|
+
int crossIdx = texelFetch(uCrossCellIdxs, ivec2(i%256, i/256), 0).x;
|
|
55
86
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
87
|
+
ivec2 uSegIdxRange = crossIdx % 2 == 0
|
|
88
|
+
? uSegIdxRangePerCell[crossIdx / 2].xy
|
|
89
|
+
: uSegIdxRangePerCell[crossIdx / 2].zw;
|
|
59
90
|
|
|
60
|
-
|
|
61
|
-
|
|
91
|
+
int segIdx = uSegIdxRange.x;
|
|
92
|
+
int segLen = uSegIdxRange.y;
|
|
62
93
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
94
|
+
for (int j = segIdx; j < segIdx + segLen; j++) {
|
|
95
|
+
// Fetch segment from texture
|
|
96
|
+
vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
|
|
66
97
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
98
|
+
// line segment's min-y is excluded
|
|
99
|
+
bool crossing =
|
|
100
|
+
(seg.y > vXY.y != seg.w > vXY.y) &&
|
|
101
|
+
(vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);
|
|
71
102
|
|
|
72
|
-
|
|
103
|
+
bool crossingUp = seg.y < seg.w;
|
|
73
104
|
|
|
74
|
-
|
|
105
|
+
winds += crossing ? (crossingUp ? -1.0 : 1.0) : 0.0;
|
|
106
|
+
}
|
|
75
107
|
}
|
|
76
|
-
}
|
|
77
108
|
|
|
78
|
-
|
|
79
|
-
|
|
109
|
+
{
|
|
110
|
+
int cellIdx = (instanceId % ${ROW_COUNT});
|
|
80
111
|
|
|
81
|
-
|
|
112
|
+
bool isEven = cellIdx % 2 == 0;
|
|
82
113
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
114
|
+
ivec4 uSegIdxRange = uSegIdxRangePerStrip[cellIdx / 2];
|
|
115
|
+
int segIdx = isEven ? uSegIdxRange.x : uSegIdxRange.z;
|
|
116
|
+
int segLen = isEven ? uSegIdxRange.y : uSegIdxRange.w;
|
|
117
|
+
|
|
87
118
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
119
|
+
for (int j = segIdx; j < segIdx + segLen; j++) {
|
|
120
|
+
// Fetch segment from texture
|
|
121
|
+
vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
|
|
91
122
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
123
|
+
// line segment's min-y is excluded
|
|
124
|
+
bool crossing =
|
|
125
|
+
(seg.y > vXY.y != seg.w > vXY.y) &&
|
|
126
|
+
(vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);
|
|
96
127
|
|
|
97
|
-
|
|
128
|
+
bool crossingUp = seg.y < seg.w;
|
|
98
129
|
|
|
99
|
-
|
|
130
|
+
winds += crossing ? (crossingUp ? -1.0 : 1.0) : 0.0;
|
|
131
|
+
}
|
|
100
132
|
}
|
|
101
133
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
134
|
bool inside = winds != 0.0;
|
|
105
135
|
///////////////////////////////////////////////////////////////////////////
|
|
106
|
-
|
|
136
|
+
// Calculate \`res\`: the distance to the nearest curve
|
|
107
137
|
///////////////////////////////////////////////////////////////////////////
|
|
108
138
|
float res = 1.0; // sdf result
|
|
109
139
|
|
|
110
|
-
if ((inside && (
|
|
140
|
+
if ((inside && ((uTestInOut & 1) != 0)) || (!inside && ((uTestInOut & 2) != 0))) {
|
|
111
141
|
int cellIdxS = closeCellIdxRange.x;
|
|
112
142
|
int cellLen = closeCellIdxRange.y;
|
|
113
143
|
// Iterate over all relevant cell indexes
|
|
@@ -125,9 +155,8 @@ void main() {
|
|
|
125
155
|
// Fetch segment from texture
|
|
126
156
|
vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
|
|
127
157
|
|
|
128
|
-
// Find unsigned distance to the segment; only the nearest will be kept
|
|
158
|
+
// Find normalized unsigned distance to the segment; only the nearest will be kept
|
|
129
159
|
float d = absDistToSegment(vXY, seg.xy, seg.zw);
|
|
130
|
-
// Apply exponential transform
|
|
131
160
|
float val = clamp(d / uMaxDistance, 0.0, 1.0);
|
|
132
161
|
|
|
133
162
|
res = min(res, val);
|
|
@@ -139,20 +168,15 @@ void main() {
|
|
|
139
168
|
// DEBUG!
|
|
140
169
|
// float alpha = ((instanceId + instanceId/${ROW_COUNT}) % 2 == 0 ? 0.3 : 0.5);
|
|
141
170
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
res = pow(1.0 - val, exponent) * 0.5;
|
|
145
|
-
|
|
146
|
-
float alpha = res == 1.0 ? 0.0 : 0.5;
|
|
147
|
-
float red = inside ? 0.2 : 0.8;
|
|
148
|
-
float green = abs(sin(25.0 * res));
|
|
149
|
-
float blue = 0.5;
|
|
171
|
+
|
|
172
|
+
${GLSL_PATTERN1}
|
|
150
173
|
|
|
151
174
|
FragColor = vec4(red, green, blue, alpha);
|
|
152
175
|
}
|
|
153
176
|
`;
|
|
154
|
-
cache[
|
|
177
|
+
cache[key] = main_Fragment;
|
|
155
178
|
return main_Fragment;
|
|
156
179
|
}
|
|
157
|
-
|
|
180
|
+
// ${calcFragColorStr}
|
|
181
|
+
export { getFragment, GLSL_PATTERN1, GLSL_DEFAULT };
|
|
158
182
|
//# sourceMappingURL=fragment.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fragment.js","sourceRoot":"","sources":["../../src/shaders/fragment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,KAAK,GAAkD,EAAE,CAAC;AAGhE,SAAS,WAAW,CACZ,QAAgB,EAChB,QAAgB;
|
|
1
|
+
{"version":3,"file":"fragment.js","sourceRoot":"","sources":["../../src/shaders/fragment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,KAAK,GAAkD,EAAE,CAAC;AAGhE,MAAM,aAAa,GAAG;;;;;;;CAOrB,CAAC;AAGF,MAAM,YAAY,GAAG;;;;;;;CAOpB,CAAC;AAGF,SAAS,WAAW,CACZ,QAAgB,EAChB,QAAgB,EAChB,gBAAwB,EACxB,IAAY;IAEhB,0EAA0E;IAC1E,sDAAsD;IACtD,MAAM,GAAG,GAAG,CAAC,CAAC,IAAE,EAAE,CAAC,GAAC,CAAC,GAAG,GAAC,QAAQ,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC;IACrD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAAC,OAAO,QAAQ,CAAC;IAAC,CAAC;IAEpD,MAAM,aAAa;IACnB,QAAQ,CAAA;;;;;;;;;;;;;;;gCAewB,CAAC,SAAS,GAAG,CAAC,GAAC,QAAQ,CAAC,GAAC,CAAC,QAAQ,GAAG,CAAC,GAAC,QAAQ,CAAC,GAAG,CAAC;;;iCAGnD,SAAS,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0CAgEJ,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iDA2DF,SAAS;;;MAGpD,aAAa;;;;CAIlB,CAAA;IAEG,KAAK,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC;IAC3B,OAAO,aAAa,CAAC;AACzB,CAAC;AAED,sBAAsB;AACtB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { debugShaders } from "../debug-shaders.js";
|
|
1
2
|
const cache = new WeakMap();
|
|
2
3
|
/**
|
|
3
4
|
* Returns a `GlContext` by reference via a cache of `WebGL2RenderingContext`s.
|
|
@@ -13,6 +14,7 @@ function getWebGlContext(gl) {
|
|
|
13
14
|
return glContext;
|
|
14
15
|
}
|
|
15
16
|
}
|
|
17
|
+
debugShaders(gl);
|
|
16
18
|
const programs = {};
|
|
17
19
|
const textures = {};
|
|
18
20
|
const glContext = { gl, textures, programs };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-web-gl-context.js","sourceRoot":"","sources":["../../src/webgl-utils/get-web-gl-context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"get-web-gl-context.js","sourceRoot":"","sources":["../../src/webgl-utils/get-web-gl-context.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,MAAM,KAAK,GAAG,IAAI,OAAO,EAAqC,CAAC;AAG/D;;;;;;GAMG;AACH,SAAS,eAAe,CAChB,EAA0B;IAE9B,CAAC;QACG,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,SAAS,EAAE,CAAC;YAAC,OAAO,SAAS,CAAC;QAAC,CAAC;IACxC,CAAC;IAED,YAAY,CAAC,EAAE,CAAC,CAAC;IAEjB,MAAM,QAAQ,GAAgC,EAAE,CAAC;IACjD,MAAM,QAAQ,GAAgC,EAAE,CAAC;IAEjD,MAAM,SAAS,GAAc,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IAExD,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,KAAK,CAAC,EAAE;QACnD,iGAAiG;QAEjG,cAAc,CAAC,QAAQ,CAAC,CAAC;QACzB,cAAc,CAAC,QAAQ,CAAC,CAAC;QACzB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEjB,SAAS,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC,EAAE,KAAK,CAAC,CAAC;IAEV,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAEzB,OAAO,SAAS,CAAC;AACrB,CAAC;AAGD,SAAS,cAAc,CAAC,CAA6B;IACjD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC;AAGD,OAAO,EAAE,eAAe,EAAE,CAAA"}
|
package/package.json
CHANGED
package/src/debug-shaders.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getFragment } from "./shaders/fragment.js";
|
|
1
|
+
import { getFragment, GLSL_DEFAULT } from "./shaders/fragment.js";
|
|
2
2
|
import { vertex } from "./shaders/vertex.js";
|
|
3
3
|
|
|
4
4
|
|
|
@@ -27,7 +27,7 @@ function debugShaders(
|
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
29
|
debugGlsl(gl, gl.VERTEX_SHADER, vertex);
|
|
30
|
-
debugGlsl(gl, gl.FRAGMENT_SHADER, getFragment(32,
|
|
30
|
+
debugGlsl(gl, gl.FRAGMENT_SHADER, getFragment(32,2, GLSL_DEFAULT, 0));
|
|
31
31
|
} catch (e) {
|
|
32
32
|
console.log(e);
|
|
33
33
|
throw e;
|
package/src/generate-sdf.ts
CHANGED
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
// import { getWebGLContext } from './webgl-utils/get-gl-context.js';
|
|
2
2
|
import { vertex } from './shaders/vertex.js';
|
|
3
|
-
import { getFragment } from './shaders/fragment.js';
|
|
3
|
+
import { getFragment, GLSL_DEFAULT } from './shaders/fragment.js';
|
|
4
4
|
import { initProgram } from './webgl-utils/use-program.js';
|
|
5
5
|
import { mainProgram } from './main-program.js';
|
|
6
6
|
import { ROW_COUNT } from './row-count.js';
|
|
7
7
|
import { getPathsFromStr } from './svg/get-paths-from-str.js';
|
|
8
8
|
import { MAX_ASPECT_RATIO_BEFORE_STRETCH } from './max-aspect-ratio-before-stretch.js';
|
|
9
9
|
import { GlContext } from './types/gl-context.js';
|
|
10
|
-
import { debugShaders } from './debug-shaders.js';
|
|
11
10
|
|
|
12
11
|
const { ceil, min, max } = Math;
|
|
13
12
|
|
|
14
13
|
|
|
14
|
+
interface SdfOptions {
|
|
15
|
+
/** the position where to draw, x-coordinate */
|
|
16
|
+
readonly x?: number | undefined;
|
|
17
|
+
readonly y?: number | undefined;
|
|
18
|
+
readonly testInteriorExterior?: boolean | undefined;
|
|
19
|
+
readonly calcSdfForInside?: boolean | undefined;
|
|
20
|
+
readonly calcSdfForOutside?: boolean | undefined;
|
|
21
|
+
readonly customData?: [number,number,number,number] | undefined;
|
|
22
|
+
readonly glslRgbaCalcStr?: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
const defaultSdfOptions: SdfOptions = {
|
|
27
|
+
x: 0, y: 0,
|
|
28
|
+
testInteriorExterior: true,
|
|
29
|
+
calcSdfForInside: true, calcSdfForOutside: true,
|
|
30
|
+
customData: [1,0,0,0]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
15
34
|
/**
|
|
16
35
|
* Generates an sdf (signed distance field) from the given bezier curves,
|
|
17
36
|
* viewbox, etc. and renders the result
|
|
@@ -22,16 +41,77 @@ const { ceil, min, max } = Math;
|
|
|
22
41
|
* thereof) given by given by their ordered control points,
|
|
23
42
|
* e.g. `[ [[0,0],[1,1],[2,1],[2,0]], [[2,0],[7,2],[1,5],[8,6]], ... ]` **OR**
|
|
24
43
|
* * an SVG string, e.g. "M26.53 478.83 C028.89 481.61 031.33 484.32 ..."
|
|
44
|
+
* @param viewbox the viewbox given as `[x1,x2,y1,y2]` (**not as** `[x,y,widht,height]`)
|
|
25
45
|
* @param width the width of the drawing rectangle
|
|
26
46
|
* @param height the height of the drawing rectangle
|
|
27
|
-
* @param viewbox the viewbox
|
|
28
47
|
* @param maxDistance maximum sdf distance
|
|
29
|
-
* @param
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* @param x the position where to draw, x-coordinate
|
|
33
|
-
* @param y the position where to draw, y-coordinate
|
|
34
|
-
* @param
|
|
48
|
+
* @param options additional options (see below)
|
|
49
|
+
*
|
|
50
|
+
* **The following are properties of the `options` parameters**
|
|
51
|
+
* @param x defaults to `0`; the position where to draw on the canvas, x-coordinate
|
|
52
|
+
* @param y defaults to `0`; the position where to draw on the canvas, y-coordinate
|
|
53
|
+
* @param testInteriorExterior defaults to `true`;
|
|
54
|
+
* if `false` winds will always be `0.0` and only an un-signed sdf can be calculated since all
|
|
55
|
+
* fragments are considered outside
|
|
56
|
+
* @param calcSdfForInside defaults to `true`;
|
|
57
|
+
* if `false` the sdf will not be calculate for the inside of the shape, in the shader, `res` will always be `1.0`
|
|
58
|
+
* @param calcSdfForOutside defaults to `true`;
|
|
59
|
+
* if `false` the sdf will not be calculate for the outside of the shape, in the shader, `res` will always be `1.0`
|
|
60
|
+
* @param customData optional custom data (must be an array of 4 numbers) to send to
|
|
61
|
+
* the fragment shader as a uniform, e.g. exponent, scale, a timer, or whatever
|
|
62
|
+
* @param glslRgbaCalcStr a glsl string (#version 300 es) inserted at the end of
|
|
63
|
+
* the fragment shader to modify the output frag color in any way (you can also discard the fragment);
|
|
64
|
+
* see below for available variables that can be used;
|
|
65
|
+
*
|
|
66
|
+
* defaults to (designed to match webgl-sdf-generator)
|
|
67
|
+
* ```glsl
|
|
68
|
+
* float exponent = uCustom.x;
|
|
69
|
+
* res = (pow(1.0 - res, exponent) * 0.5) * (inside ? -1.0 : 1.0);
|
|
70
|
+
* float red = res;
|
|
71
|
+
* float green = res;
|
|
72
|
+
* float blue = res;
|
|
73
|
+
* float alpha = res;
|
|
74
|
+
*
|
|
75
|
+
* ```
|
|
76
|
+
* You must define and assign \`red\`, \`green\`, \`blue\` and \`alpha\`.
|
|
77
|
+
*
|
|
78
|
+
* Usable variables:
|
|
79
|
+
* ```glsl
|
|
80
|
+
* // the result of the distance calculation for this fragment, a value from 0.0 to 1.0
|
|
81
|
+
* // with 1.0 occuring when the fragment is >= maxDistance away and 0.0 when the
|
|
82
|
+
* // fragment is exactly on a curve
|
|
83
|
+
* float res
|
|
84
|
+
*
|
|
85
|
+
* // the number of anti-clockwise winds around the fragment, a value != 0
|
|
86
|
+
* // means the fragment is inside the shape
|
|
87
|
+
* float winds
|
|
88
|
+
*
|
|
89
|
+
* // 4 custom values set via an options parameter of `generateSdf`; defaults to `[1,0,0,0]`;
|
|
90
|
+
* // the first value is used as the exponent within the default `glslRgbaCalcStr`
|
|
91
|
+
* vec4 uCustom;
|
|
92
|
+
*
|
|
93
|
+
* // the max distance value supplied via `generateSdf`
|
|
94
|
+
* float uMaxDistance
|
|
95
|
+
*
|
|
96
|
+
* // bit 0 -> calc sdf when fragment is inside; defaults to 1,
|
|
97
|
+
* // bit 1 -> calc sdf when fragment is outside; defaults to 1
|
|
98
|
+
* // bit 1 -> calc `winds` (required for signing the distance); defaults to 1
|
|
99
|
+
* // note: when the distance calculation is not done (via options from `generateSdf`),
|
|
100
|
+
* `res` will be set to 1.0 (max distance away)
|
|
101
|
+
* int uTestInOut
|
|
102
|
+
*
|
|
103
|
+
* // the original x,y coordinates of the fragment in the original space provided
|
|
104
|
+
* // via the `viewbox` in `generateSdf`, e.g. the very bottom-left fragment
|
|
105
|
+
* // will have vXY == (viebox[0], viebox[1]) and the very top right will have
|
|
106
|
+
* // coordinates vXY == (viebox[2], viebox[3])
|
|
107
|
+
* vec2 vXY
|
|
108
|
+
*
|
|
109
|
+
* // whether the point is inside or outside the shape, often used to sign `res`
|
|
110
|
+
* // it is identical to `winds != 0`
|
|
111
|
+
* bool inside
|
|
112
|
+
*
|
|
113
|
+
* // pretty much useless unless you want to create a checkerboard pattern for no good reason
|
|
114
|
+
* int instanceId
|
|
35
115
|
*/
|
|
36
116
|
function generateSdf(
|
|
37
117
|
glContext: GlContext,
|
|
@@ -39,21 +119,13 @@ function generateSdf(
|
|
|
39
119
|
viewbox: [number,number,number,number],
|
|
40
120
|
width: number,
|
|
41
121
|
height: number,
|
|
42
|
-
x = 0, y = 0,
|
|
43
122
|
maxDistance: number,
|
|
44
|
-
|
|
45
|
-
inclOutside = true,
|
|
46
|
-
customData: [number,number,number,number],
|
|
47
|
-
|
|
48
|
-
// TODO
|
|
49
|
-
channel = 0) {
|
|
123
|
+
options: SdfOptions = defaultSdfOptions) {
|
|
50
124
|
|
|
51
125
|
const psss = typeof bezierCurves_or_svgStr === 'string'
|
|
52
126
|
? getPathsFromStr(bezierCurves_or_svgStr)
|
|
53
127
|
: bezierCurves_or_svgStr;
|
|
54
128
|
|
|
55
|
-
// const glContext = getWebGLContext(gl);
|
|
56
|
-
|
|
57
129
|
let stretch = 1;
|
|
58
130
|
const aspectRatio = width/height;
|
|
59
131
|
if (aspectRatio > MAX_ASPECT_RATIO_BEFORE_STRETCH) {
|
|
@@ -69,23 +141,41 @@ function generateSdf(
|
|
|
69
141
|
|
|
70
142
|
const padCount = 2*ceil(min(maxDistance, maxDim)/cellSize/2);
|
|
71
143
|
|
|
144
|
+
const { glslRgbaCalcStr } = options;
|
|
145
|
+
const hash = calcStrHash(glslRgbaCalcStr || '');
|
|
146
|
+
|
|
72
147
|
const programMain = initProgram(
|
|
73
|
-
glContext, `main${colCount}-${padCount}`,
|
|
74
|
-
vertex, getFragment(colCount, padCount)
|
|
148
|
+
glContext, `main${colCount}-${padCount}-${hash}`,
|
|
149
|
+
vertex, getFragment(colCount, padCount, glslRgbaCalcStr || GLSL_DEFAULT, hash)
|
|
75
150
|
);
|
|
76
151
|
|
|
77
152
|
const { gl } = glContext;
|
|
78
153
|
|
|
79
|
-
// debugShaders(gl); // comment for production
|
|
80
|
-
|
|
81
154
|
gl.useProgram(programMain.program);
|
|
82
155
|
mainProgram(
|
|
83
156
|
glContext, programMain,
|
|
84
|
-
psss, viewbox, maxDistance,
|
|
85
|
-
|
|
86
|
-
|
|
157
|
+
psss, viewbox, maxDistance,
|
|
158
|
+
width, height,
|
|
159
|
+
options,
|
|
160
|
+
colCount, cellSize, padCount, stretch,
|
|
161
|
+
|
|
87
162
|
);
|
|
88
163
|
}
|
|
89
164
|
|
|
90
165
|
|
|
91
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Calculates and returns a hash of the given string
|
|
168
|
+
*/
|
|
169
|
+
function calcStrHash(str: string) {
|
|
170
|
+
if (!str) return 0;
|
|
171
|
+
|
|
172
|
+
let hash = 5381;
|
|
173
|
+
for (let i=0; i<str.length; i++) {
|
|
174
|
+
hash = ((hash << 5) + hash) + str.charCodeAt(i);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return hash >>> 0; // Convert to unsigned 32-bit integer
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
export { generateSdf, SdfOptions }
|
package/src/main-program.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { GlContext } from './types/gl-context.js';
|
|
|
7
7
|
import { prepareBuffers } from './prepare-buffers.js';
|
|
8
8
|
import { TEX_WIDTH } from './tex-width.js';
|
|
9
9
|
import { ROW_COUNT } from './row-count.js';
|
|
10
|
+
import { SdfOptions } from './generate-sdf.js';
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
const SEG_TEX_INDEX = 0;
|
|
@@ -21,14 +22,11 @@ function mainProgram(
|
|
|
21
22
|
psss: number[][][][],
|
|
22
23
|
viewbox: [number,number,number,number],
|
|
23
24
|
maxDistance: number,
|
|
24
|
-
inclInside: boolean,
|
|
25
|
-
inclOutside: boolean,
|
|
26
|
-
customData: [number, number, number, number],
|
|
27
|
-
|
|
28
|
-
x: number,
|
|
29
|
-
y: number,
|
|
30
25
|
width: number,
|
|
31
26
|
height: number,
|
|
27
|
+
|
|
28
|
+
options: SdfOptions,
|
|
29
|
+
|
|
32
30
|
colCount: number,
|
|
33
31
|
cellSize: number,
|
|
34
32
|
padCount: number,
|
|
@@ -36,6 +34,13 @@ function mainProgram(
|
|
|
36
34
|
|
|
37
35
|
const { gl } = glContext;
|
|
38
36
|
|
|
37
|
+
const {
|
|
38
|
+
x = 0, y = 0, testInteriorExterior = true,
|
|
39
|
+
calcSdfForInside = true, calcSdfForOutside = true,
|
|
40
|
+
customData = [1,0,0,0],
|
|
41
|
+
glslRgbaCalcStr
|
|
42
|
+
} = options;
|
|
43
|
+
|
|
39
44
|
const vertices: number[] = [];
|
|
40
45
|
const x0 = 0;
|
|
41
46
|
const y0 = 0;
|
|
@@ -82,7 +87,11 @@ function mainProgram(
|
|
|
82
87
|
// Init/update uniforms
|
|
83
88
|
setUniform_('2f', 'uWidthHeight', width, height);
|
|
84
89
|
setUniform_('1f', 'uMaxDistance', maxDistance);
|
|
85
|
-
setUniform_('1i', '
|
|
90
|
+
setUniform_('1i', 'uTestInOut',
|
|
91
|
+
(calcSdfForInside ? 1 : 0) +
|
|
92
|
+
(calcSdfForOutside ? 2 : 0) +
|
|
93
|
+
(testInteriorExterior ? 4 : 0)
|
|
94
|
+
);
|
|
86
95
|
setUniform_('4f', 'uCustom', ...customData);
|
|
87
96
|
|
|
88
97
|
setUniformBlock(programMain)('SegIdxRangePerCellBlock', 0, segIdxs_PerCell_Range_Arr);
|
|
@@ -154,6 +163,8 @@ function mainProgram(
|
|
|
154
163
|
|
|
155
164
|
gl.viewport(x, y, width, height);
|
|
156
165
|
|
|
166
|
+
gl.colorMask(true, true, true, true);
|
|
167
|
+
|
|
157
168
|
// draw a square colCount * ROW_COUNT times - 6 vertics
|
|
158
169
|
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, colCount*ROW_COUNT);
|
|
159
170
|
|
package/src/shaders/fragment.ts
CHANGED
|
@@ -4,11 +4,36 @@ import { ROW_COUNT } from "../row-count.js";
|
|
|
4
4
|
const cache: { [padCount_times_colCount: number]: string } = {};
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
const GLSL_PATTERN1 = `
|
|
8
|
+
float exponent = uCustom.x;
|
|
9
|
+
res = pow(1.0 - res, exponent);
|
|
10
|
+
float alpha = res <= 0.0 ? 0.0 : 0.5;
|
|
11
|
+
float red = inside ? 0.2 : 0.8;
|
|
12
|
+
float green = abs(sin(25.0 * res));
|
|
13
|
+
float blue = 0.5;
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const GLSL_DEFAULT = `
|
|
18
|
+
float exponent = uCustom.x;
|
|
19
|
+
res = (pow(1.0 - res, exponent) * 0.5) * (inside ? -1.0 : 1.0);
|
|
20
|
+
float red = res;
|
|
21
|
+
float green = res;
|
|
22
|
+
float blue = res;
|
|
23
|
+
float alpha = res;
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
|
|
7
27
|
function getFragment(
|
|
8
28
|
colCount: number,
|
|
9
|
-
padCount: number
|
|
10
|
-
|
|
11
|
-
|
|
29
|
+
padCount: number,
|
|
30
|
+
calcFragColorStr: string,
|
|
31
|
+
hash: number): string {
|
|
32
|
+
|
|
33
|
+
// `colCount` and `padCount` can take at most 8 bits and `has` 32 bits and
|
|
34
|
+
// we have at least 53 bits to play with so we're fine
|
|
35
|
+
const key = (2**32)*(256*colCount + padCount) + hash;
|
|
36
|
+
const fragment = cache[key];
|
|
12
37
|
if (fragment !== undefined) { return fragment; }
|
|
13
38
|
|
|
14
39
|
const main_Fragment =
|
|
@@ -20,7 +45,11 @@ uniform float uMaxDistance;
|
|
|
20
45
|
uniform highp sampler2D uSegs;
|
|
21
46
|
uniform highp isampler2D uCloseCellIdxs;
|
|
22
47
|
uniform highp isampler2D uCrossCellIdxs;
|
|
23
|
-
|
|
48
|
+
// bit 0 -> calc distance inside
|
|
49
|
+
// bit 1 -> calc distance outside
|
|
50
|
+
// bit 2 -> calc whether fragment is inside or outside (else outside is assumed with winds == 0.0)
|
|
51
|
+
uniform int uTestInOut;
|
|
52
|
+
uniform vec4 uCustom;
|
|
24
53
|
|
|
25
54
|
uniform SegIdxRangePerCellBlock {
|
|
26
55
|
ivec4 uSegIdxRangePerCell[${(ROW_COUNT + 2*padCount)*(colCount + 2*padCount) / 2}];
|
|
@@ -35,6 +64,10 @@ flat in ivec2 closeCellIdxRange;
|
|
|
35
64
|
flat in ivec2 crossCellIdxRange;
|
|
36
65
|
out vec4 FragColor;
|
|
37
66
|
|
|
67
|
+
// testing!!
|
|
68
|
+
// float rand(vec2 co) {
|
|
69
|
+
// return mod(uCustom.w * fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453), 1.0);
|
|
70
|
+
// }
|
|
38
71
|
|
|
39
72
|
float absDistToSegment(vec2 point, vec2 lineA, vec2 lineB) {
|
|
40
73
|
vec2 lineDir = lineB - lineA;
|
|
@@ -48,72 +81,75 @@ float absDistToSegment(vec2 point, vec2 lineA, vec2 lineB) {
|
|
|
48
81
|
|
|
49
82
|
void main() {
|
|
50
83
|
///////////////////////////////////////////////////////////////////////////
|
|
84
|
+
// Calculate \`winds\`:
|
|
85
|
+
//
|
|
51
86
|
// Project a ray to the left to check if it crosses the segment in order
|
|
52
87
|
// to find the fragment's winding number to determine whether fragment
|
|
53
88
|
// is inside or outside the shape.
|
|
89
|
+
///////////////////////////////////////////////////////////////////////////
|
|
54
90
|
|
|
55
|
-
int crossIdxS = crossCellIdxRange.x;
|
|
56
|
-
int crossLen = crossCellIdxRange.y;
|
|
57
91
|
float winds = 0.0;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
int
|
|
92
|
+
if ((uTestInOut & 4) == 0) {
|
|
93
|
+
int crossIdxS = crossCellIdxRange.x;
|
|
94
|
+
int crossLen = crossCellIdxRange.y;
|
|
95
|
+
// Iterate over all relevant cell indexes
|
|
96
|
+
for (int i = crossIdxS; i < crossIdxS + crossLen; i++) {
|
|
97
|
+
int crossIdx = texelFetch(uCrossCellIdxs, ivec2(i%256, i/256), 0).x;
|
|
61
98
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
99
|
+
ivec2 uSegIdxRange = crossIdx % 2 == 0
|
|
100
|
+
? uSegIdxRangePerCell[crossIdx / 2].xy
|
|
101
|
+
: uSegIdxRangePerCell[crossIdx / 2].zw;
|
|
65
102
|
|
|
66
|
-
|
|
67
|
-
|
|
103
|
+
int segIdx = uSegIdxRange.x;
|
|
104
|
+
int segLen = uSegIdxRange.y;
|
|
68
105
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
106
|
+
for (int j = segIdx; j < segIdx + segLen; j++) {
|
|
107
|
+
// Fetch segment from texture
|
|
108
|
+
vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
|
|
72
109
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
110
|
+
// line segment's min-y is excluded
|
|
111
|
+
bool crossing =
|
|
112
|
+
(seg.y > vXY.y != seg.w > vXY.y) &&
|
|
113
|
+
(vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);
|
|
77
114
|
|
|
78
|
-
|
|
115
|
+
bool crossingUp = seg.y < seg.w;
|
|
79
116
|
|
|
80
|
-
|
|
117
|
+
winds += crossing ? (crossingUp ? -1.0 : 1.0) : 0.0;
|
|
118
|
+
}
|
|
81
119
|
}
|
|
82
|
-
}
|
|
83
120
|
|
|
84
|
-
|
|
85
|
-
|
|
121
|
+
{
|
|
122
|
+
int cellIdx = (instanceId % ${ROW_COUNT});
|
|
86
123
|
|
|
87
|
-
|
|
124
|
+
bool isEven = cellIdx % 2 == 0;
|
|
88
125
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
126
|
+
ivec4 uSegIdxRange = uSegIdxRangePerStrip[cellIdx / 2];
|
|
127
|
+
int segIdx = isEven ? uSegIdxRange.x : uSegIdxRange.z;
|
|
128
|
+
int segLen = isEven ? uSegIdxRange.y : uSegIdxRange.w;
|
|
129
|
+
|
|
93
130
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
131
|
+
for (int j = segIdx; j < segIdx + segLen; j++) {
|
|
132
|
+
// Fetch segment from texture
|
|
133
|
+
vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
|
|
97
134
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
135
|
+
// line segment's min-y is excluded
|
|
136
|
+
bool crossing =
|
|
137
|
+
(seg.y > vXY.y != seg.w > vXY.y) &&
|
|
138
|
+
(vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);
|
|
102
139
|
|
|
103
|
-
|
|
140
|
+
bool crossingUp = seg.y < seg.w;
|
|
104
141
|
|
|
105
|
-
|
|
142
|
+
winds += crossing ? (crossingUp ? -1.0 : 1.0) : 0.0;
|
|
143
|
+
}
|
|
106
144
|
}
|
|
107
145
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
146
|
bool inside = winds != 0.0;
|
|
111
147
|
///////////////////////////////////////////////////////////////////////////
|
|
112
|
-
|
|
148
|
+
// Calculate \`res\`: the distance to the nearest curve
|
|
113
149
|
///////////////////////////////////////////////////////////////////////////
|
|
114
150
|
float res = 1.0; // sdf result
|
|
115
151
|
|
|
116
|
-
if ((inside && (
|
|
152
|
+
if ((inside && ((uTestInOut & 1) != 0)) || (!inside && ((uTestInOut & 2) != 0))) {
|
|
117
153
|
int cellIdxS = closeCellIdxRange.x;
|
|
118
154
|
int cellLen = closeCellIdxRange.y;
|
|
119
155
|
// Iterate over all relevant cell indexes
|
|
@@ -131,9 +167,8 @@ void main() {
|
|
|
131
167
|
// Fetch segment from texture
|
|
132
168
|
vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
|
|
133
169
|
|
|
134
|
-
// Find unsigned distance to the segment; only the nearest will be kept
|
|
170
|
+
// Find normalized unsigned distance to the segment; only the nearest will be kept
|
|
135
171
|
float d = absDistToSegment(vXY, seg.xy, seg.zw);
|
|
136
|
-
// Apply exponential transform
|
|
137
172
|
float val = clamp(d / uMaxDistance, 0.0, 1.0);
|
|
138
173
|
|
|
139
174
|
res = min(res, val);
|
|
@@ -145,22 +180,16 @@ void main() {
|
|
|
145
180
|
// DEBUG!
|
|
146
181
|
// float alpha = ((instanceId + instanceId/${ROW_COUNT}) % 2 == 0 ? 0.3 : 0.5);
|
|
147
182
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
res = pow(1.0 - val, exponent) * 0.5;
|
|
151
|
-
|
|
152
|
-
float alpha = res == 1.0 ? 0.0 : 0.5;
|
|
153
|
-
float red = inside ? 0.2 : 0.8;
|
|
154
|
-
float green = abs(sin(25.0 * res));
|
|
155
|
-
float blue = 0.5;
|
|
183
|
+
|
|
184
|
+
${GLSL_PATTERN1}
|
|
156
185
|
|
|
157
186
|
FragColor = vec4(red, green, blue, alpha);
|
|
158
187
|
}
|
|
159
188
|
`
|
|
160
189
|
|
|
161
|
-
cache[
|
|
190
|
+
cache[key] = main_Fragment;
|
|
162
191
|
return main_Fragment;
|
|
163
192
|
}
|
|
164
193
|
|
|
165
|
-
|
|
166
|
-
export { getFragment }
|
|
194
|
+
// ${calcFragColorStr}
|
|
195
|
+
export { getFragment, GLSL_PATTERN1, GLSL_DEFAULT }
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Texture } from "../types/texture.js";
|
|
2
2
|
import type { Program } from "../types/program.js";
|
|
3
3
|
import type { GlContext } from "../types/gl-context.js";
|
|
4
|
+
import { debugShaders } from "../debug-shaders.js";
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
const cache = new WeakMap<WebGL2RenderingContext, GlContext>();
|
|
@@ -21,6 +22,8 @@ function getWebGlContext(
|
|
|
21
22
|
if (glContext) { return glContext; }
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
debugShaders(gl);
|
|
26
|
+
|
|
24
27
|
const programs: { [index:string]: Program } = {};
|
|
25
28
|
const textures: { [index:string]: Texture } = {};
|
|
26
29
|
|