layershift 0.1.2 → 0.2.0

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.
@@ -1,4 +1,4 @@
1
- var Layershift=(function(C){"use strict";var _=typeof document<"u"?document.currentScript:null;class V{worker;currentBuffer;pendingTimeSec=null;workerBusy=!1;disposed=!1;constructor(t,e){this.worker=t,this.currentBuffer=new Uint8Array(e)}static async create(t,e,r){const i=new Worker(new URL("/assets/depth-worker-CMcEa805.js",_&&_.tagName.toUpperCase()==="SCRIPT"&&_.src||new URL("layershift.js",document.baseURI).href),{type:"module"}),a=e*r,c=new V(i,a),n=t.frames.map(l=>{const h=new Uint8Array(l.length);return h.set(l),h.buffer});return await new Promise((l,h)=>{const d=setTimeout(()=>h(new Error("Worker init timeout")),1e4);i.onmessage=p=>{p.data.type==="ready"&&(clearTimeout(d),l())},i.onerror=p=>{clearTimeout(d),h(p)},i.postMessage({type:"init",frames:n,meta:{frameCount:t.meta.frameCount,fps:t.meta.fps,width:t.meta.width,height:t.meta.height},targetWidth:e,targetHeight:r},n)}),i.onmessage=l=>{if(l.data.type==="result"&&(c.currentBuffer=l.data.data,c.workerBusy=!1,c.pendingTimeSec!==null)){const h=c.pendingTimeSec;c.pendingTimeSec=null,c.requestSample(h)}},c}sample(t){return this.requestSample(t),this.currentBuffer}requestSample(t){if(!this.disposed){if(this.workerBusy){this.pendingTimeSec=t;return}this.workerBusy=!0,this.worker.postMessage({type:"sample",timeSec:t})}}dispose(){this.disposed=!0,this.worker.terminate()}}class k{constructor(t,e,r){this.depthData=t,this.targetWidth=e,this.targetHeight=r;const i=t.meta.width*t.meta.height,a=e*r;this.interpolatedDepth=new Float32Array(i),this.bilateralOutput=new Float32Array(i),this.resizedDepth=new Float32Array(a),this.uint8Output=new Uint8Array(a)}interpolatedDepth;resizedDepth;bilateralOutput;uint8Output;lastFrameIndex=-1;lastNextFrameIndex=-1;lastLerpFactor=-1;sample(t){const e=D(t*this.depthData.meta.fps,0,this.depthData.meta.frameCount-1),r=Math.floor(e),i=Math.min(r+1,this.depthData.meta.frameCount-1),a=e-r,c=r!==this.lastFrameIndex||i!==this.lastNextFrameIndex,n=Math.abs(a-this.lastLerpFactor)>.001;if(!c&&!n)return this.uint8Output;this.lastFrameIndex=r,this.lastNextFrameIndex=i,this.lastLerpFactor=a;const l=1-a,h=this.depthData.frames[r],d=this.depthData.frames[i];for(let u=0;u<this.interpolatedDepth.length;u+=1)this.interpolatedDepth[u]=(h[u]*l+d[u]*a)/255;Y(this.interpolatedDepth,this.depthData.meta.width,this.depthData.meta.height,this.bilateralOutput);const p=this.targetWidth!==this.depthData.meta.width||this.targetHeight!==this.depthData.meta.height;p&&q(this.bilateralOutput,this.depthData.meta.width,this.depthData.meta.height,this.targetWidth,this.targetHeight,this.resizedDepth);const s=p?this.resizedDepth:this.bilateralOutput;for(let u=0;u<this.uint8Output.length;u+=1)this.uint8Output[u]=s[u]*255+.5|0;return this.uint8Output}}async function X(o,t,e){const[r,i]=await Promise.all([B(t),N(o)]);return G(i,r)}async function B(o){const t=await fetch(o);if(!t.ok)throw new Error(`Failed to fetch depth metadata (${t.status} ${t.statusText}).`);const e=await t.json();return W(e),{frameCount:e.frameCount,fps:e.fps,width:e.width,height:e.height,sourceFps:e.sourceFps}}async function N(o,t){const e=await fetch(o);if(!e.ok)throw new Error(`Failed to fetch depth data (${e.status} ${e.statusText}).`);e.headers.get("content-length");const r=e.body;if(!r)return new Uint8Array(await e.arrayBuffer());const i=[];let a=0;const c=r.getReader();for(;;){const{done:h,value:d}=await c.read();if(h)break;d&&(i.push(d),a+=d.byteLength)}const n=new Uint8Array(a);let l=0;for(const h of i)n.set(h,l),l+=h.byteLength;return n}function G(o,t){if(o.byteLength<4)throw new Error("Depth data binary is missing the frame-count header.");const r=new DataView(o.buffer,o.byteOffset,o.byteLength).getUint32(0,!0),i=t.width*t.height,a=4+r*i;if(o.byteLength!==a)throw new Error(`Depth data byte length mismatch. Expected ${a} bytes, received ${o.byteLength}.`);if(r!==t.frameCount)throw new Error(`Depth frame count mismatch between metadata (${t.frameCount}) and binary header (${r}).`);const c=o.subarray(4),n=new Array(r);for(let l=0;l<r;l+=1){const h=l*i;n[l]=c.subarray(h,h+i)}return{meta:t,frames:n}}function W(o){if(!o||typeof o.frameCount!="number"||typeof o.fps!="number"||typeof o.width!="number"||typeof o.height!="number"||typeof o.sourceFps!="number")throw new Error("Depth metadata is malformed.");if(!Number.isFinite(o.frameCount)||!Number.isFinite(o.fps)||!Number.isFinite(o.width)||!Number.isFinite(o.height)||!Number.isFinite(o.sourceFps)||o.frameCount<=0||o.fps<=0||o.width<=0||o.height<=0||o.sourceFps<=0)throw new Error("Depth metadata contains invalid numeric values.")}function q(o,t,e,r,i,a){const c=t/r,n=e/i;for(let l=0;l<i;l+=1){const h=(l+.5)*n-.5,d=D(Math.floor(h),0,e-1),p=D(d+1,0,e-1),s=h-d;for(let u=0;u<r;u+=1){const m=(u+.5)*c-.5,v=D(Math.floor(m),0,t-1),T=D(v+1,0,t-1),b=m-v,E=o[d*t+v],S=o[d*t+T],F=o[p*t+v],f=o[p*t+T],x=E+(S-E)*b,M=F+(f-F)*b;a[l*r+u]=x+(M-x)*s}}}function Y(o,t,e,r){for(let c=0;c<e;c+=1)for(let n=0;n<t;n+=1){const l=c*t+n,h=o[l];let d=1,p=h;for(let s=-2;s<=2;s+=1){const u=c+s;if(!(u<0||u>=e))for(let m=-2;m<=2;m+=1){if(m===0&&s===0)continue;const v=n+m;if(v<0||v>=t)continue;const T=o[u*t+v],b=m*m+s*s,E=T-h,S=Math.exp(-b/2.25-E*E/.01);d+=S,p+=T*S}}r[l]=p/d}}function D(o,t,e){return Math.min(e,Math.max(t,o))}const $={parallaxStrength:.05,contrastLow:.05,contrastHigh:.95,verticalReduction:.5,dofStart:.6,dofStrength:.4,pomSteps:16,overscanPadding:.08};function j(o,t,e){const r=new Float32Array(256);if(o.length===0||t<=0||e<=0)return H(r);const i=Z(o.length),a=t*e;let c=0;const n=new Uint32Array(256);for(const f of i){const x=o[f],M=Math.min(x.length,a);for(let I=0;I<M;I+=1)n[x[I]]+=1;c+=M}if(c===0)return H(r);const l=1/c;for(let f=0;f<256;f+=1)r[f]=n[f]*l;const h=new Float32Array(256);h[0]=r[0];for(let f=1;f<256;f+=1)h[f]=h[f-1]+r[f];const d=A(h,.05),p=A(h,.25),s=A(h,.5),u=A(h,.75),m=A(h,.95);let v=0;for(let f=0;f<256;f+=1)v+=f/255*r[f];let T=0;for(let f=0;f<256;f+=1){const x=f/255-v;T+=r[f]*x*x}const b=Math.sqrt(T),E=m-d,S=u-p,F=J(r);return{mean:v,stdDev:b,p5:d,p25:p,median:s,p75:u,p95:m,effectiveRange:E,iqr:S,bimodality:F,histogram:r}}function K(o){if(o.effectiveRange<.05||o.stdDev<.02)return{...$};const t=o.effectiveRange-.5,e=o.bimodality-.4,r=g(.05-t*.03+e*.01,.035,.065),i=g(o.p5-.03,0,.25),a=g(o.p95+.03,.75,1),c=g((r-.03)/.05,0,1),n=g(.6-c*.25,.35,.6),l=g(.6-t*.2,.5,.7),h=g(.4+t*.2,.25,.5),d=16,p=g(r+.03,.06,.1);return{parallaxStrength:r,contrastLow:i,contrastHigh:a,verticalReduction:n,dofStart:l,dofStrength:h,pomSteps:d,overscanPadding:p}}function Z(o){if(o<=0)return[];if(o===1)return[0];const t=o-1,e=[0,Math.floor(o/4),Math.floor(o/2),Math.floor(3*o/4),t],r=new Set,i=[];for(const a of e)r.has(a)||(r.add(a),i.push(a));return i}function A(o,t){for(let e=0;e<256;e+=1)if(o[e]>=t)return e/255;return 1}function J(o){const t=new Float32Array(256);for(let s=0;s<256;s+=1){let u=0,m=0;for(let v=s-2;v<=s+2;v+=1)v>=0&&v<256&&(u+=o[v],m+=1);t[s]=u/m}let e=0;for(let s=0;s<256;s+=1)e+=t[s];e/=256;const r=e*2,i=25,a=[];for(let s=1;s<255;s+=1)t[s]>t[s-1]&&t[s]>t[s+1]&&t[s]>=r&&a.push({bin:s,height:t[s]});if(t[0]>t[1]&&t[0]>=r&&a.push({bin:0,height:t[0]}),t[255]>t[254]&&t[255]>=r&&a.push({bin:255,height:t[255]}),a.sort((s,u)=>u.height-s.height),a.length<2)return 0;const c=a[0];let n=null;for(let s=1;s<a.length;s+=1)if(Math.abs(a[s].bin-c.bin)>=i){n=a[s];break}if(!n)return 0;const l=Math.min(c.bin,n.bin),h=Math.max(c.bin,n.bin);let d=1/0;for(let s=l;s<=h;s+=1)t[s]<d&&(d=t[s]);const p=Math.min(c.height,n.height);return p<=0?0:g(1-d/p,0,1)}function H(o){return{mean:0,stdDev:0,p5:0,p25:0,median:0,p75:0,p95:0,effectiveRange:0,iqr:0,bimodality:0,histogram:o}}function g(o,t,e){return Math.min(e,Math.max(t,o))}const Q=`#version 300 es
1
+ var Layershift=(function(et){"use strict";var ut=typeof document<"u"?document.currentScript:null;class it{worker;currentBuffer;pendingTimeSec=null;workerBusy=!1;disposed=!1;constructor(t,e){this.worker=t,this.currentBuffer=new Uint8Array(e)}static async create(t,e,i){const r=new Worker(new URL("/assets/depth-worker-CMcEa805.js",ut&&ut.tagName.toUpperCase()==="SCRIPT"&&ut.src||new URL("layershift.js",document.baseURI).href),{type:"module"}),o=e*i,s=new it(r,o),a=t.frames.map(h=>{const l=new Uint8Array(h.length);return l.set(h),l.buffer});return await new Promise((h,l)=>{const u=setTimeout(()=>l(new Error("Worker init timeout")),1e4);r.onmessage=f=>{f.data.type==="ready"&&(clearTimeout(u),h())},r.onerror=f=>{clearTimeout(u),l(f)},r.postMessage({type:"init",frames:a,meta:{frameCount:t.meta.frameCount,fps:t.meta.fps,width:t.meta.width,height:t.meta.height},targetWidth:e,targetHeight:i},a)}),r.onmessage=h=>{if(h.data.type==="result"&&(s.currentBuffer=h.data.data,s.workerBusy=!1,s.pendingTimeSec!==null)){const l=s.pendingTimeSec;s.pendingTimeSec=null,s.requestSample(l)}},s}sample(t){return this.requestSample(t),this.currentBuffer}requestSample(t){if(!this.disposed){if(this.workerBusy){this.pendingTimeSec=t;return}this.workerBusy=!0,this.worker.postMessage({type:"sample",timeSec:t})}}dispose(){this.disposed=!0,this.worker.terminate()}}class gt{constructor(t,e,i){this.depthData=t,this.targetWidth=e,this.targetHeight=i;const r=t.meta.width*t.meta.height,o=e*i;this.interpolatedDepth=new Float32Array(r),this.bilateralOutput=new Float32Array(r),this.resizedDepth=new Float32Array(o),this.uint8Output=new Uint8Array(o)}interpolatedDepth;resizedDepth;bilateralOutput;uint8Output;lastFrameIndex=-1;lastNextFrameIndex=-1;lastLerpFactor=-1;sample(t){const e=j(t*this.depthData.meta.fps,0,this.depthData.meta.frameCount-1),i=Math.floor(e),r=Math.min(i+1,this.depthData.meta.frameCount-1),o=e-i,s=i!==this.lastFrameIndex||r!==this.lastNextFrameIndex,a=Math.abs(o-this.lastLerpFactor)>.001;if(!s&&!a)return this.uint8Output;this.lastFrameIndex=i,this.lastNextFrameIndex=r,this.lastLerpFactor=o;const h=1-o,l=this.depthData.frames[i],u=this.depthData.frames[r];for(let d=0;d<this.interpolatedDepth.length;d+=1)this.interpolatedDepth[d]=(l[d]*h+u[d]*o)/255;Ot(this.interpolatedDepth,this.depthData.meta.width,this.depthData.meta.height,this.bilateralOutput);const f=this.targetWidth!==this.depthData.meta.width||this.targetHeight!==this.depthData.meta.height;f&&Vt(this.bilateralOutput,this.depthData.meta.width,this.depthData.meta.height,this.targetWidth,this.targetHeight,this.resizedDepth);const c=f?this.resizedDepth:this.bilateralOutput;for(let d=0;d<this.uint8Output.length;d+=1)this.uint8Output[d]=c[d]*255+.5|0;return this.uint8Output}}async function vt(n,t,e){const[i,r]=await Promise.all([Ct(t),Mt(n)]);return _t(r,i)}async function Ct(n){const t=await fetch(n);if(!t.ok)throw new Error(`Failed to fetch depth metadata (${t.status} ${t.statusText}).`);const e=await t.json();return It(e),{frameCount:e.frameCount,fps:e.fps,width:e.width,height:e.height,sourceFps:e.sourceFps}}async function Mt(n,t){const e=await fetch(n);if(!e.ok)throw new Error(`Failed to fetch depth data (${e.status} ${e.statusText}).`);e.headers.get("content-length");const i=e.body;if(!i)return new Uint8Array(await e.arrayBuffer());const r=[];let o=0;const s=i.getReader();for(;;){const{done:l,value:u}=await s.read();if(l)break;u&&(r.push(u),o+=u.byteLength)}const a=new Uint8Array(o);let h=0;for(const l of r)a.set(l,h),h+=l.byteLength;return a}function _t(n,t){if(n.byteLength<4)throw new Error("Depth data binary is missing the frame-count header.");const i=new DataView(n.buffer,n.byteOffset,n.byteLength).getUint32(0,!0),r=t.width*t.height,o=4+i*r;if(n.byteLength!==o)throw new Error(`Depth data byte length mismatch. Expected ${o} bytes, received ${n.byteLength}.`);if(i!==t.frameCount)throw new Error(`Depth frame count mismatch between metadata (${t.frameCount}) and binary header (${i}).`);const s=n.subarray(4),a=new Array(i);for(let h=0;h<i;h+=1){const l=h*r;a[h]=s.subarray(l,l+r)}return{meta:t,frames:a}}function It(n){if(!n||typeof n.frameCount!="number"||typeof n.fps!="number"||typeof n.width!="number"||typeof n.height!="number"||typeof n.sourceFps!="number")throw new Error("Depth metadata is malformed.");if(!Number.isFinite(n.frameCount)||!Number.isFinite(n.fps)||!Number.isFinite(n.width)||!Number.isFinite(n.height)||!Number.isFinite(n.sourceFps)||n.frameCount<=0||n.fps<=0||n.width<=0||n.height<=0||n.sourceFps<=0)throw new Error("Depth metadata contains invalid numeric values.")}function Vt(n,t,e,i,r,o){const s=t/i,a=e/r;for(let h=0;h<r;h+=1){const l=(h+.5)*a-.5,u=j(Math.floor(l),0,e-1),f=j(u+1,0,e-1),c=l-u;for(let d=0;d<i;d+=1){const m=(d+.5)*s-.5,g=j(Math.floor(m),0,t-1),U=j(g+1,0,t-1),y=m-g,E=n[u*t+g],p=n[u*t+U],v=n[f*t+g],T=n[f*t+U],x=E+(p-E)*y,A=v+(T-v)*y;o[h*i+d]=x+(A-x)*c}}}function Ot(n,t,e,i){for(let s=0;s<e;s+=1)for(let a=0;a<t;a+=1){const h=s*t+a,l=n[h];let u=1,f=l;for(let c=-2;c<=2;c+=1){const d=s+c;if(!(d<0||d>=e))for(let m=-2;m<=2;m+=1){if(m===0&&c===0)continue;const g=a+m;if(g<0||g>=t)continue;const U=n[d*t+g],y=m*m+c*c,E=U-l,p=Math.exp(-y/2.25-E*E/.01);u+=p,f+=U*p}}i[h]=f/u}}function j(n,t,e){return Math.min(e,Math.max(t,n))}const kt={parallaxStrength:.05,contrastLow:.05,contrastHigh:.95,verticalReduction:.5,dofStart:.6,dofStrength:.4,pomSteps:16,overscanPadding:.08};function Ht(n,t,e){const i=new Float32Array(256);if(n.length===0||t<=0||e<=0)return xt(i);const r=Xt(n.length),o=t*e;let s=0;const a=new Uint32Array(256);for(const T of r){const x=n[T],A=Math.min(x.length,o);for(let S=0;S<A;S+=1)a[x[S]]+=1;s+=A}if(s===0)return xt(i);const h=1/s;for(let T=0;T<256;T+=1)i[T]=a[T]*h;const l=new Float32Array(256);l[0]=i[0];for(let T=1;T<256;T+=1)l[T]=l[T-1]+i[T];const u=Y(l,.05),f=Y(l,.25),c=Y(l,.5),d=Y(l,.75),m=Y(l,.95);let g=0;for(let T=0;T<256;T+=1)g+=T/255*i[T];let U=0;for(let T=0;T<256;T+=1){const x=T/255-g;U+=i[T]*x*x}const y=Math.sqrt(U),E=m-u,p=d-f,v=Nt(i);return{mean:g,stdDev:y,p5:u,p25:f,median:c,p75:d,p95:m,effectiveRange:E,iqr:p,bimodality:v,histogram:i}}function Bt(n){if(n.effectiveRange<.05||n.stdDev<.02)return{...kt};const t=n.effectiveRange-.5,e=n.bimodality-.4,i=V(.05-t*.03+e*.01,.035,.065),r=V(n.p5-.03,0,.25),o=V(n.p95+.03,.75,1),s=V((i-.03)/.05,0,1),a=V(.6-s*.25,.35,.6),h=V(.6-t*.2,.5,.7),l=V(.4+t*.2,.25,.5),u=16,f=V(i+.03,.06,.1);return{parallaxStrength:i,contrastLow:r,contrastHigh:o,verticalReduction:a,dofStart:h,dofStrength:l,pomSteps:u,overscanPadding:f}}function Xt(n){if(n<=0)return[];if(n===1)return[0];const t=n-1,e=[0,Math.floor(n/4),Math.floor(n/2),Math.floor(3*n/4),t],i=new Set,r=[];for(const o of e)i.has(o)||(i.add(o),r.push(o));return r}function Y(n,t){for(let e=0;e<256;e+=1)if(n[e]>=t)return e/255;return 1}function Nt(n){const t=new Float32Array(256);for(let c=0;c<256;c+=1){let d=0,m=0;for(let g=c-2;g<=c+2;g+=1)g>=0&&g<256&&(d+=n[g],m+=1);t[c]=d/m}let e=0;for(let c=0;c<256;c+=1)e+=t[c];e/=256;const i=e*2,r=25,o=[];for(let c=1;c<255;c+=1)t[c]>t[c-1]&&t[c]>t[c+1]&&t[c]>=i&&o.push({bin:c,height:t[c]});if(t[0]>t[1]&&t[0]>=i&&o.push({bin:0,height:t[0]}),t[255]>t[254]&&t[255]>=i&&o.push({bin:255,height:t[255]}),o.sort((c,d)=>d.height-c.height),o.length<2)return 0;const s=o[0];let a=null;for(let c=1;c<o.length;c+=1)if(Math.abs(o[c].bin-s.bin)>=r){a=o[c];break}if(!a)return 0;const h=Math.min(s.bin,a.bin),l=Math.max(s.bin,a.bin);let u=1/0;for(let c=h;c<=l;c+=1)t[c]<u&&(u=t[c]);const f=Math.min(s.height,a.height);return f<=0?0:V(1-u/f,0,1)}function xt(n){return{mean:0,stdDev:0,p5:0,p25:0,median:0,p75:0,p95:0,effectiveRange:0,iqr:0,bimodality:0,histogram:n}}function V(n,t,e){return Math.min(e,Math.max(t,n))}const zt=`#version 300 es
2
2
  in vec2 aPosition;
3
3
 
4
4
  // UV coordinates for cover-fit + overscan.
@@ -19,7 +19,7 @@ var Layershift=(function(C){"use strict";var _=typeof document<"u"?document.curr
19
19
  vScreenUv = baseUv;
20
20
  gl_Position = vec4(aPosition, 0.0, 1.0);
21
21
  }
22
- `,tt=`#version 300 es
22
+ `,Wt=`#version 300 es
23
23
  precision highp float;
24
24
 
25
25
  // ---- Uniforms ----
@@ -187,10 +187,10 @@ var Layershift=(function(C){"use strict";var _=typeof document<"u"?document.curr
187
187
 
188
188
  fragColor = color;
189
189
  }
190
- `,L={contrastLow:.05,contrastHigh:.95,verticalReduction:.5,dofStart:.6,dofStrength:.4};function z(o,t,e){const r=o.createShader(t);if(!r)throw new Error("Failed to create shader.");if(o.shaderSource(r,e),o.compileShader(r),!o.getShaderParameter(r,o.COMPILE_STATUS)){const i=o.getShaderInfoLog(r)??"";throw o.deleteShader(r),new Error(`Shader compilation failed:
191
- ${i}`)}return r}function et(o,t,e){const r=o.createProgram();if(!r)throw new Error("Failed to create program.");if(o.attachShader(r,t),o.attachShader(r,e),o.linkProgram(r),!o.getProgramParameter(r,o.LINK_STATUS)){const i=o.getProgramInfoLog(r)??"";throw o.deleteProgram(r),new Error(`Program linking failed:
192
- ${i}`)}return o.detachShader(r,t),o.detachShader(r,e),o.deleteShader(t),o.deleteShader(e),r}function it(o,t){return{uImage:o.getUniformLocation(t,"uImage"),uDepth:o.getUniformLocation(t,"uDepth"),uOffset:o.getUniformLocation(t,"uOffset"),uStrength:o.getUniformLocation(t,"uStrength"),uPomEnabled:o.getUniformLocation(t,"uPomEnabled"),uPomSteps:o.getUniformLocation(t,"uPomSteps"),uContrastLow:o.getUniformLocation(t,"uContrastLow"),uContrastHigh:o.getUniformLocation(t,"uContrastHigh"),uVerticalReduction:o.getUniformLocation(t,"uVerticalReduction"),uDofStart:o.getUniformLocation(t,"uDofStart"),uDofStrength:o.getUniformLocation(t,"uDofStrength"),uImageTexelSize:o.getUniformLocation(t,"uImageTexelSize"),uUvOffset:o.getUniformLocation(t,"uUvOffset"),uUvScale:o.getUniformLocation(t,"uUvScale")}}class R{static RESIZE_DEBOUNCE_MS=100;static MAX_POM_STEPS=64;canvas;gl=null;program=null;uniforms=null;vao=null;videoTexture=null;depthTexture=null;container;depthWidth=0;depthHeight=0;videoAspect=1.7777777777777777;readDepth=null;readInput=null;playbackVideo=null;onVideoFrame=null;animationFrameHandle=0;rvfcHandle=0;rvfcSupported=!1;resizeObserver=null;resizeTimer=null;uvOffset=[0,0];uvScale=[1,1];config;constructor(t,e){this.container=t,this.config={parallaxStrength:e.parallaxStrength,pomEnabled:e.pomEnabled,pomSteps:e.pomSteps,overscanPadding:e.overscanPadding,contrastLow:e.contrastLow??L.contrastLow,contrastHigh:e.contrastHigh??L.contrastHigh,verticalReduction:e.verticalReduction??L.verticalReduction,dofStart:e.dofStart??L.dofStart,dofStrength:e.dofStrength??L.dofStrength},this.canvas=document.createElement("canvas");const r=this.canvas.getContext("webgl2",{antialias:!1,alpha:!1,desynchronized:!0,powerPreference:"high-performance"});if(!r)throw new Error("WebGL 2 is not supported.");this.gl=r,"drawingBufferColorSpace"in r&&(r.drawingBufferColorSpace="srgb"),r.clearColor(0,0,0,1),r.pixelStorei(r.UNPACK_FLIP_Y_WEBGL,!0),this.container.appendChild(this.canvas),this.initGPUResources(),this.setupResizeHandling(),this.canvas.addEventListener("webglcontextlost",this.handleContextLost),this.canvas.addEventListener("webglcontextrestored",this.handleContextRestored)}initialize(t,e,r){const i=this.gl;i&&(this.disposeTextures(),this.videoAspect=t.videoWidth/t.videoHeight,this.depthWidth=e,this.depthHeight=r,this.videoTexture=i.createTexture(),i.activeTexture(i.TEXTURE0),i.bindTexture(i.TEXTURE_2D,this.videoTexture),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),this.depthTexture=i.createTexture(),i.activeTexture(i.TEXTURE1),i.bindTexture(i.TEXTURE_2D,this.depthTexture),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.texStorage2D(i.TEXTURE_2D,1,i.R8,e,r),this.program&&this.uniforms&&(i.useProgram(this.program),i.uniform1i(this.uniforms.uImage,0),i.uniform1i(this.uniforms.uDepth,1),i.uniform1f(this.uniforms.uStrength,this.config.parallaxStrength),i.uniform1i(this.uniforms.uPomEnabled,this.config.pomEnabled?1:0),i.uniform1i(this.uniforms.uPomSteps,this.config.pomSteps),i.uniform1f(this.uniforms.uContrastLow,this.config.contrastLow),i.uniform1f(this.uniforms.uContrastHigh,this.config.contrastHigh),i.uniform1f(this.uniforms.uVerticalReduction,this.config.verticalReduction),i.uniform1f(this.uniforms.uDofStart,this.config.dofStart),i.uniform1f(this.uniforms.uDofStrength,this.config.dofStrength),i.uniform2f(this.uniforms.uImageTexelSize,1/t.videoWidth,1/t.videoHeight)),this.recalculateViewportLayout())}start(t,e,r,i){this.stop(),this.playbackVideo=t,this.readDepth=e,this.readInput=r,this.onVideoFrame=i??null,this.rvfcSupported=R.isRVFCSupported(),this.rvfcSupported&&(this.rvfcHandle=t.requestVideoFrameCallback(this.videoFrameLoop)),this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop)}stop(){this.animationFrameHandle&&(window.cancelAnimationFrame(this.animationFrameHandle),this.animationFrameHandle=0),this.rvfcHandle&&this.playbackVideo&&(this.playbackVideo.cancelVideoFrameCallback(this.rvfcHandle),this.rvfcHandle=0),this.playbackVideo=null,this.readDepth=null,this.readInput=null,this.onVideoFrame=null,this.rvfcSupported=!1}dispose(){this.stop(),this.disposeTextures(),this.disposeGPUResources(),this.canvas.removeEventListener("webglcontextlost",this.handleContextLost),this.canvas.removeEventListener("webglcontextrestored",this.handleContextRestored),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),window.removeEventListener("resize",this.scheduleResizeRecalculate),this.resizeTimer!==null&&(window.clearTimeout(this.resizeTimer),this.resizeTimer=null)}initGPUResources(){const t=this.gl;if(!t)return;const e=tt.replace("#version 300 es",`#version 300 es
193
- #define MAX_POM_STEPS ${R.MAX_POM_STEPS}`),r=z(t,t.VERTEX_SHADER,Q),i=z(t,t.FRAGMENT_SHADER,e);this.program=et(t,r,i),this.uniforms=it(t,this.program);const a=new Float32Array([-1,-1,1,-1,-1,1,1,1]);this.vao=t.createVertexArray(),t.bindVertexArray(this.vao);const c=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,c),t.bufferData(t.ARRAY_BUFFER,a,t.STATIC_DRAW);const n=t.getAttribLocation(this.program,"aPosition");t.enableVertexAttribArray(n),t.vertexAttribPointer(n,2,t.FLOAT,!1,0,0),t.bindVertexArray(null),t.disable(t.DEPTH_TEST)}static isRVFCSupported(){return"requestVideoFrameCallback"in HTMLVideoElement.prototype}videoFrameLoop=(t,e)=>{const r=this.playbackVideo;if(!r)return;this.rvfcHandle=r.requestVideoFrameCallback(this.videoFrameLoop);const i=e.mediaTime??r.currentTime;this.updateDepthTexture(i),this.onVideoFrame&&this.onVideoFrame(i,e.presentedFrames??0)};renderLoop=()=>{this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop);const t=this.gl,e=this.playbackVideo;if(!(!t||!this.program||!this.uniforms||!this.vao)&&!(!e||e.readyState<HTMLMediaElement.HAVE_CURRENT_DATA)){if(t.useProgram(this.program),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this.videoTexture),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,e),this.rvfcSupported||this.updateDepthTexture(e.currentTime),this.readInput){const r=this.readInput();t.uniform2f(this.uniforms.uOffset,-r.x,r.y)}t.bindVertexArray(this.vao),t.drawArrays(t.TRIANGLE_STRIP,0,4)}};updateDepthTexture(t){const e=this.gl;if(!e||!this.readDepth||!this.depthTexture)return;const r=this.readDepth(t);e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.depthTexture),e.texSubImage2D(e.TEXTURE_2D,0,0,0,this.depthWidth,this.depthHeight,e.RED,e.UNSIGNED_BYTE,r)}setupResizeHandling(){typeof ResizeObserver<"u"&&(this.resizeObserver=new ResizeObserver(()=>{this.scheduleResizeRecalculate()}),this.resizeObserver.observe(this.container)),window.addEventListener("resize",this.scheduleResizeRecalculate),this.recalculateViewportLayout()}scheduleResizeRecalculate=()=>{this.resizeTimer!==null&&window.clearTimeout(this.resizeTimer),this.resizeTimer=window.setTimeout(()=>{this.resizeTimer=null,this.recalculateViewportLayout()},R.RESIZE_DEBOUNCE_MS)};recalculateViewportLayout(){const t=this.gl;if(!t)return;const{width:e,height:r}=this.getViewportSize(),i=Math.min(window.devicePixelRatio,2),a=Math.round(e*i),c=Math.round(r*i);(this.canvas.width!==a||this.canvas.height!==c)&&(this.canvas.width=a,this.canvas.height=c,t.viewport(0,0,a,c));const n=e/r,l=this.config.parallaxStrength+this.config.overscanPadding;let h=1,d=1;n>this.videoAspect?d=this.videoAspect/n:h=n/this.videoAspect;const p=1+l*2;h/=p,d/=p,this.uvOffset=[(1-h)/2,(1-d)/2],this.uvScale=[h,d],this.program&&this.uniforms&&(t.useProgram(this.program),t.uniform2f(this.uniforms.uUvOffset,this.uvOffset[0],this.uvOffset[1]),t.uniform2f(this.uniforms.uUvScale,this.uvScale[0],this.uvScale[1]))}getViewportSize(){const t=Math.max(1,Math.round(this.container.clientWidth||window.innerWidth)),e=Math.max(1,Math.round(this.container.clientHeight||window.innerHeight));return{width:t,height:e}}handleContextLost=t=>{t.preventDefault(),this.animationFrameHandle&&(window.cancelAnimationFrame(this.animationFrameHandle),this.animationFrameHandle=0)};handleContextRestored=()=>{const t=this.canvas.getContext("webgl2");t&&(this.gl=t,t.clearColor(0,0,0,1),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!0),this.initGPUResources(),this.playbackVideo&&this.depthWidth>0&&this.initialize(this.playbackVideo,this.depthWidth,this.depthHeight),this.playbackVideo&&(this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop)))};disposeTextures(){const t=this.gl;t&&(this.videoTexture&&(t.deleteTexture(this.videoTexture),this.videoTexture=null),this.depthTexture&&(t.deleteTexture(this.depthTexture),this.depthTexture=null))}disposeGPUResources(){const t=this.gl;t&&(this.program&&(t.deleteProgram(this.program),this.program=null),this.vao&&(t.deleteVertexArray(this.vao),this.vao=null),this.uniforms=null)}}const w={parallaxX:.4,parallaxY:1,parallaxMax:30,overscan:.05,autoplay:!0,loop:!0,muted:!0};class O{constructor(t,e=.08,r=.06){this.host=t,this.lerpFactor=e,this.motionLerpFactor=r,this.host.addEventListener("mousemove",this.handleMouseMove),this.host.addEventListener("mouseleave",this.resetPointerTarget),this.host.addEventListener("touchstart",this.handleTouchStart,{passive:!0}),this.host.addEventListener("touchmove",this.handleTouchMove,{passive:!0}),this.host.addEventListener("touchend",this.handleTouchEnd,{passive:!0}),this.host.addEventListener("touchcancel",this.handleTouchEnd,{passive:!0})}pointerTarget={x:0,y:0};motionTarget={x:0,y:0};smoothedOutput={x:0,y:0};usingMotionInput=!1;motionListenerAttached=!1;motionRequested=!1;touchActive=!1;touchAnchorX=0;touchAnchorY=0;lerpFactor;motionLerpFactor;static TOUCH_DRAG_RANGE=100;update(){const t=this.touchActive?this.pointerTarget:this.usingMotionInput?this.motionTarget:this.pointerTarget,e=this.usingMotionInput&&!this.touchActive?this.motionLerpFactor:this.lerpFactor;return this.smoothedOutput.x=P(this.smoothedOutput.x,t.x,e),this.smoothedOutput.y=P(this.smoothedOutput.y,t.y,e),this.smoothedOutput}dispose(){this.host.removeEventListener("mousemove",this.handleMouseMove),this.host.removeEventListener("mouseleave",this.resetPointerTarget),this.host.removeEventListener("touchstart",this.handleTouchStart),this.host.removeEventListener("touchmove",this.handleTouchMove),this.host.removeEventListener("touchend",this.handleTouchEnd),this.host.removeEventListener("touchcancel",this.handleTouchEnd),this.motionListenerAttached&&(window.removeEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!1)}handleMouseMove=t=>{const e=this.host.getBoundingClientRect(),r=(t.clientX-e.left)/e.width*2-1,i=(t.clientY-e.top)/e.height*2-1;this.pointerTarget.x=y(r,-1,1),this.pointerTarget.y=y(i,-1,1)};resetPointerTarget=()=>{this.pointerTarget.x=0,this.pointerTarget.y=0};handleTouchStart=t=>{const e=t.touches[0];e&&(this.touchActive=!0,this.touchAnchorX=e.clientX,this.touchAnchorY=e.clientY,this.pointerTarget.x=0,this.pointerTarget.y=0,this.motionRequested||(this.motionRequested=!0,this.requestMotionPermission()))};handleTouchMove=t=>{const e=t.touches[0];if(!e)return;const r=e.clientX-this.touchAnchorX,i=e.clientY-this.touchAnchorY,a=O.TOUCH_DRAG_RANGE;this.pointerTarget.x=y(r/a,-1,1),this.pointerTarget.y=y(i/a,-1,1)};handleTouchEnd=()=>{this.touchActive=!1,this.pointerTarget.x=0,this.pointerTarget.y=0};async requestMotionPermission(){if(typeof DeviceOrientationEvent>"u")return;const t=DeviceOrientationEvent;if(typeof t.requestPermission=="function")try{if(await t.requestPermission()!=="granted")return}catch{return}this.motionListenerAttached||(window.addEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!0),this.usingMotionInput=!0}handleDeviceOrientation=t=>{const e=y((t.gamma??0)/45,-1,1),r=y((t.beta??0)/45,-1,1);this.motionTarget.x=P(this.motionTarget.x,e,this.motionLerpFactor),this.motionTarget.y=P(this.motionTarget.y,r,this.motionLerpFactor)}}class U extends HTMLElement{static TAG_NAME="layershift-parallax";static get observedAttributes(){return["src","depth-src","depth-meta","parallax-x","parallax-y","parallax-max","layers","overscan","autoplay","loop","muted"]}shadow;container=null;renderer=null;inputHandler=null;depthWorker=null;video=null;initialized=!1;abortController=null;loopCount=0;constructor(){super(),this.shadow=this.attachShadow({mode:"open"})}getAttrFloat(t,e){const r=this.getAttribute(t);if(r===null)return e;const i=parseFloat(r);return Number.isFinite(i)?i:e}getAttrBool(t,e){if(!this.hasAttribute(t))return e;const r=this.getAttribute(t);return!(r==="false"||r==="0")}get parallaxX(){return this.getAttrFloat("parallax-x",w.parallaxX)}get parallaxY(){return this.getAttrFloat("parallax-y",w.parallaxY)}get parallaxMax(){return this.getAttrFloat("parallax-max",w.parallaxMax)}get overscan(){return this.getAttrFloat("overscan",w.overscan)}get shouldAutoplay(){return this.getAttrBool("autoplay",w.autoplay)}get shouldLoop(){return this.getAttrBool("loop",w.loop)}get shouldMute(){return this.getAttrBool("muted",w.muted)}emit(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}attachVideoEventListeners(t){t.addEventListener("play",()=>{this.emit("layershift-parallax:play",{currentTime:t.currentTime})}),t.addEventListener("pause",()=>{this.emit("layershift-parallax:pause",{currentTime:t.currentTime})}),t.addEventListener("ended",()=>{t.loop&&(this.loopCount+=1,this.emit("layershift-parallax:loop",{loopCount:this.loopCount}))})}connectedCallback(){this.setupShadowDOM(),this.init()}disconnectedCallback(){this.dispose()}attributeChangedCallback(t,e,r){["src","depth-src","depth-meta"].includes(t)&&(this.initialized?(this.dispose(),this.setupShadowDOM(),this.init()):this.isConnected&&this.getAttribute("src")&&this.getAttribute("depth-src")&&this.getAttribute("depth-meta")&&this.init())}setupShadowDOM(){this.shadow.innerHTML="";const t=document.createElement("style");t.textContent=`
190
+ `,q={contrastLow:.05,contrastHigh:.95,verticalReduction:.5,dofStart:.6,dofStrength:.4};function Tt(n,t,e){const i=n.createShader(t);if(!i)throw new Error("Failed to create shader.");if(n.shaderSource(i,e),n.compileShader(i),!n.getShaderParameter(i,n.COMPILE_STATUS)){const r=n.getShaderInfoLog(i)??"";throw n.deleteShader(i),new Error(`Shader compilation failed:
191
+ ${r}`)}return i}function Gt(n,t,e){const i=n.createProgram();if(!i)throw new Error("Failed to create program.");if(n.attachShader(i,t),n.attachShader(i,e),n.linkProgram(i),!n.getProgramParameter(i,n.LINK_STATUS)){const r=n.getProgramInfoLog(i)??"";throw n.deleteProgram(i),new Error(`Program linking failed:
192
+ ${r}`)}return n.detachShader(i,t),n.detachShader(i,e),n.deleteShader(t),n.deleteShader(e),i}function jt(n,t){return{uImage:n.getUniformLocation(t,"uImage"),uDepth:n.getUniformLocation(t,"uDepth"),uOffset:n.getUniformLocation(t,"uOffset"),uStrength:n.getUniformLocation(t,"uStrength"),uPomEnabled:n.getUniformLocation(t,"uPomEnabled"),uPomSteps:n.getUniformLocation(t,"uPomSteps"),uContrastLow:n.getUniformLocation(t,"uContrastLow"),uContrastHigh:n.getUniformLocation(t,"uContrastHigh"),uVerticalReduction:n.getUniformLocation(t,"uVerticalReduction"),uDofStart:n.getUniformLocation(t,"uDofStart"),uDofStrength:n.getUniformLocation(t,"uDofStrength"),uImageTexelSize:n.getUniformLocation(t,"uImageTexelSize"),uUvOffset:n.getUniformLocation(t,"uUvOffset"),uUvScale:n.getUniformLocation(t,"uUvScale")}}class Z{static RESIZE_DEBOUNCE_MS=100;static MAX_POM_STEPS=64;canvas;gl=null;program=null;uniforms=null;vao=null;videoTexture=null;depthTexture=null;container;depthWidth=0;depthHeight=0;videoAspect=1.7777777777777777;readDepth=null;readInput=null;playbackVideo=null;onVideoFrame=null;animationFrameHandle=0;rvfcHandle=0;rvfcSupported=!1;resizeObserver=null;resizeTimer=null;uvOffset=[0,0];uvScale=[1,1];config;constructor(t,e){this.container=t,this.config={parallaxStrength:e.parallaxStrength,pomEnabled:e.pomEnabled,pomSteps:e.pomSteps,overscanPadding:e.overscanPadding,contrastLow:e.contrastLow??q.contrastLow,contrastHigh:e.contrastHigh??q.contrastHigh,verticalReduction:e.verticalReduction??q.verticalReduction,dofStart:e.dofStart??q.dofStart,dofStrength:e.dofStrength??q.dofStrength},this.canvas=document.createElement("canvas");const i=this.canvas.getContext("webgl2",{antialias:!1,alpha:!1,desynchronized:!0,powerPreference:"high-performance"});if(!i)throw new Error("WebGL 2 is not supported.");this.gl=i,"drawingBufferColorSpace"in i&&(i.drawingBufferColorSpace="srgb"),i.clearColor(0,0,0,1),i.pixelStorei(i.UNPACK_FLIP_Y_WEBGL,!0),this.container.appendChild(this.canvas),this.initGPUResources(),this.setupResizeHandling(),this.canvas.addEventListener("webglcontextlost",this.handleContextLost),this.canvas.addEventListener("webglcontextrestored",this.handleContextRestored)}initialize(t,e,i){const r=this.gl;r&&(this.disposeTextures(),this.videoAspect=t.videoWidth/t.videoHeight,this.depthWidth=e,this.depthHeight=i,this.videoTexture=r.createTexture(),r.activeTexture(r.TEXTURE0),r.bindTexture(r.TEXTURE_2D,this.videoTexture),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MIN_FILTER,r.LINEAR),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MAG_FILTER,r.LINEAR),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),this.depthTexture=r.createTexture(),r.activeTexture(r.TEXTURE1),r.bindTexture(r.TEXTURE_2D,this.depthTexture),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MIN_FILTER,r.LINEAR),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MAG_FILTER,r.LINEAR),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),r.texStorage2D(r.TEXTURE_2D,1,r.R8,e,i),this.program&&this.uniforms&&(r.useProgram(this.program),r.uniform1i(this.uniforms.uImage,0),r.uniform1i(this.uniforms.uDepth,1),r.uniform1f(this.uniforms.uStrength,this.config.parallaxStrength),r.uniform1i(this.uniforms.uPomEnabled,this.config.pomEnabled?1:0),r.uniform1i(this.uniforms.uPomSteps,this.config.pomSteps),r.uniform1f(this.uniforms.uContrastLow,this.config.contrastLow),r.uniform1f(this.uniforms.uContrastHigh,this.config.contrastHigh),r.uniform1f(this.uniforms.uVerticalReduction,this.config.verticalReduction),r.uniform1f(this.uniforms.uDofStart,this.config.dofStart),r.uniform1f(this.uniforms.uDofStrength,this.config.dofStrength),r.uniform2f(this.uniforms.uImageTexelSize,1/t.videoWidth,1/t.videoHeight)),this.recalculateViewportLayout())}start(t,e,i,r){this.stop(),this.playbackVideo=t,this.readDepth=e,this.readInput=i,this.onVideoFrame=r??null,this.rvfcSupported=Z.isRVFCSupported(),this.rvfcSupported&&(this.rvfcHandle=t.requestVideoFrameCallback(this.videoFrameLoop)),this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop)}stop(){this.animationFrameHandle&&(window.cancelAnimationFrame(this.animationFrameHandle),this.animationFrameHandle=0),this.rvfcHandle&&this.playbackVideo&&(this.playbackVideo.cancelVideoFrameCallback(this.rvfcHandle),this.rvfcHandle=0),this.playbackVideo=null,this.readDepth=null,this.readInput=null,this.onVideoFrame=null,this.rvfcSupported=!1}dispose(){this.stop(),this.disposeTextures(),this.disposeGPUResources(),this.canvas.removeEventListener("webglcontextlost",this.handleContextLost),this.canvas.removeEventListener("webglcontextrestored",this.handleContextRestored),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),window.removeEventListener("resize",this.scheduleResizeRecalculate),this.resizeTimer!==null&&(window.clearTimeout(this.resizeTimer),this.resizeTimer=null)}initGPUResources(){const t=this.gl;if(!t)return;const e=Wt.replace("#version 300 es",`#version 300 es
193
+ #define MAX_POM_STEPS ${Z.MAX_POM_STEPS}`),i=Tt(t,t.VERTEX_SHADER,zt),r=Tt(t,t.FRAGMENT_SHADER,e);this.program=Gt(t,i,r),this.uniforms=jt(t,this.program);const o=new Float32Array([-1,-1,1,-1,-1,1,1,1]);this.vao=t.createVertexArray(),t.bindVertexArray(this.vao);const s=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,s),t.bufferData(t.ARRAY_BUFFER,o,t.STATIC_DRAW);const a=t.getAttribLocation(this.program,"aPosition");t.enableVertexAttribArray(a),t.vertexAttribPointer(a,2,t.FLOAT,!1,0,0),t.bindVertexArray(null),t.disable(t.DEPTH_TEST)}static isRVFCSupported(){return"requestVideoFrameCallback"in HTMLVideoElement.prototype}videoFrameLoop=(t,e)=>{const i=this.playbackVideo;if(!i)return;this.rvfcHandle=i.requestVideoFrameCallback(this.videoFrameLoop);const r=e.mediaTime??i.currentTime;this.updateDepthTexture(r),this.onVideoFrame&&this.onVideoFrame(r,e.presentedFrames??0)};renderLoop=()=>{this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop);const t=this.gl,e=this.playbackVideo;if(!(!t||!this.program||!this.uniforms||!this.vao)&&!(!e||e.readyState<HTMLMediaElement.HAVE_CURRENT_DATA)){if(t.useProgram(this.program),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this.videoTexture),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,e),this.rvfcSupported||this.updateDepthTexture(e.currentTime),this.readInput){const i=this.readInput();t.uniform2f(this.uniforms.uOffset,-i.x,i.y)}t.bindVertexArray(this.vao),t.drawArrays(t.TRIANGLE_STRIP,0,4)}};updateDepthTexture(t){const e=this.gl;if(!e||!this.readDepth||!this.depthTexture)return;const i=this.readDepth(t);e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.depthTexture),e.texSubImage2D(e.TEXTURE_2D,0,0,0,this.depthWidth,this.depthHeight,e.RED,e.UNSIGNED_BYTE,i)}setupResizeHandling(){typeof ResizeObserver<"u"&&(this.resizeObserver=new ResizeObserver(()=>{this.scheduleResizeRecalculate()}),this.resizeObserver.observe(this.container)),window.addEventListener("resize",this.scheduleResizeRecalculate),this.recalculateViewportLayout()}scheduleResizeRecalculate=()=>{this.resizeTimer!==null&&window.clearTimeout(this.resizeTimer),this.resizeTimer=window.setTimeout(()=>{this.resizeTimer=null,this.recalculateViewportLayout()},Z.RESIZE_DEBOUNCE_MS)};recalculateViewportLayout(){const t=this.gl;if(!t)return;const{width:e,height:i}=this.getViewportSize(),r=Math.min(window.devicePixelRatio,2),o=Math.round(e*r),s=Math.round(i*r);(this.canvas.width!==o||this.canvas.height!==s)&&(this.canvas.width=o,this.canvas.height=s,t.viewport(0,0,o,s));const a=e/i,h=this.config.parallaxStrength+this.config.overscanPadding;let l=1,u=1;a>this.videoAspect?u=this.videoAspect/a:l=a/this.videoAspect;const f=1+h*2;l/=f,u/=f,this.uvOffset=[(1-l)/2,(1-u)/2],this.uvScale=[l,u],this.program&&this.uniforms&&(t.useProgram(this.program),t.uniform2f(this.uniforms.uUvOffset,this.uvOffset[0],this.uvOffset[1]),t.uniform2f(this.uniforms.uUvScale,this.uvScale[0],this.uvScale[1]))}getViewportSize(){const t=Math.max(1,Math.round(this.container.clientWidth||window.innerWidth)),e=Math.max(1,Math.round(this.container.clientHeight||window.innerHeight));return{width:t,height:e}}handleContextLost=t=>{t.preventDefault(),this.animationFrameHandle&&(window.cancelAnimationFrame(this.animationFrameHandle),this.animationFrameHandle=0)};handleContextRestored=()=>{const t=this.canvas.getContext("webgl2");t&&(this.gl=t,t.clearColor(0,0,0,1),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!0),this.initGPUResources(),this.playbackVideo&&this.depthWidth>0&&this.initialize(this.playbackVideo,this.depthWidth,this.depthHeight),this.playbackVideo&&(this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop)))};disposeTextures(){const t=this.gl;t&&(this.videoTexture&&(t.deleteTexture(this.videoTexture),this.videoTexture=null),this.depthTexture&&(t.deleteTexture(this.depthTexture),this.depthTexture=null))}disposeGPUResources(){const t=this.gl;t&&(this.program&&(t.deleteProgram(this.program),this.program=null),this.vao&&(t.deleteVertexArray(this.vao),this.vao=null),this.uniforms=null)}}const B={parallaxX:.4,parallaxY:1,parallaxMax:30,overscan:.05,autoplay:!0,loop:!0,muted:!0};let Yt=class Lt{constructor(t,e=.08,i=.06){this.host=t,this.lerpFactor=e,this.motionLerpFactor=i,this.host.addEventListener("mousemove",this.handleMouseMove),this.host.addEventListener("mouseleave",this.resetPointerTarget),this.host.addEventListener("touchstart",this.handleTouchStart,{passive:!0}),this.host.addEventListener("touchmove",this.handleTouchMove,{passive:!0}),this.host.addEventListener("touchend",this.handleTouchEnd,{passive:!0}),this.host.addEventListener("touchcancel",this.handleTouchEnd,{passive:!0})}pointerTarget={x:0,y:0};motionTarget={x:0,y:0};smoothedOutput={x:0,y:0};usingMotionInput=!1;motionListenerAttached=!1;motionRequested=!1;touchActive=!1;touchAnchorX=0;touchAnchorY=0;lerpFactor;motionLerpFactor;static TOUCH_DRAG_RANGE=100;update(){const t=this.touchActive?this.pointerTarget:this.usingMotionInput?this.motionTarget:this.pointerTarget,e=this.usingMotionInput&&!this.touchActive?this.motionLerpFactor:this.lerpFactor;return this.smoothedOutput.x=ot(this.smoothedOutput.x,t.x,e),this.smoothedOutput.y=ot(this.smoothedOutput.y,t.y,e),this.smoothedOutput}dispose(){this.host.removeEventListener("mousemove",this.handleMouseMove),this.host.removeEventListener("mouseleave",this.resetPointerTarget),this.host.removeEventListener("touchstart",this.handleTouchStart),this.host.removeEventListener("touchmove",this.handleTouchMove),this.host.removeEventListener("touchend",this.handleTouchEnd),this.host.removeEventListener("touchcancel",this.handleTouchEnd),this.motionListenerAttached&&(window.removeEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!1)}handleMouseMove=t=>{const e=this.host.getBoundingClientRect(),i=(t.clientX-e.left)/e.width*2-1,r=(t.clientY-e.top)/e.height*2-1;this.pointerTarget.x=N(i,-1,1),this.pointerTarget.y=N(r,-1,1)};resetPointerTarget=()=>{this.pointerTarget.x=0,this.pointerTarget.y=0};handleTouchStart=t=>{const e=t.touches[0];e&&(this.touchActive=!0,this.touchAnchorX=e.clientX,this.touchAnchorY=e.clientY,this.pointerTarget.x=0,this.pointerTarget.y=0,this.motionRequested||(this.motionRequested=!0,this.requestMotionPermission()))};handleTouchMove=t=>{const e=t.touches[0];if(!e)return;const i=e.clientX-this.touchAnchorX,r=e.clientY-this.touchAnchorY,o=Lt.TOUCH_DRAG_RANGE;this.pointerTarget.x=N(i/o,-1,1),this.pointerTarget.y=N(r/o,-1,1)};handleTouchEnd=()=>{this.touchActive=!1,this.pointerTarget.x=0,this.pointerTarget.y=0};async requestMotionPermission(){if(typeof DeviceOrientationEvent>"u")return;const t=DeviceOrientationEvent;if(typeof t.requestPermission=="function")try{if(await t.requestPermission()!=="granted")return}catch{return}this.motionListenerAttached||(window.addEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!0),this.usingMotionInput=!0}handleDeviceOrientation=t=>{const e=N((t.gamma??0)/45,-1,1),i=N((t.beta??0)/45,-1,1);this.motionTarget.x=ot(this.motionTarget.x,e,this.motionLerpFactor),this.motionTarget.y=ot(this.motionTarget.y,i,this.motionLerpFactor)}};class rt extends HTMLElement{static TAG_NAME="layershift-parallax";static get observedAttributes(){return["src","depth-src","depth-meta","parallax-x","parallax-y","parallax-max","layers","overscan","autoplay","loop","muted"]}shadow;container=null;renderer=null;inputHandler=null;depthWorker=null;video=null;initialized=!1;abortController=null;loopCount=0;constructor(){super(),this.shadow=this.attachShadow({mode:"open"})}getAttrFloat(t,e){const i=this.getAttribute(t);if(i===null)return e;const r=parseFloat(i);return Number.isFinite(r)?r:e}getAttrBool(t,e){if(!this.hasAttribute(t))return e;const i=this.getAttribute(t);return!(i==="false"||i==="0")}get parallaxX(){return this.getAttrFloat("parallax-x",B.parallaxX)}get parallaxY(){return this.getAttrFloat("parallax-y",B.parallaxY)}get parallaxMax(){return this.getAttrFloat("parallax-max",B.parallaxMax)}get overscan(){return this.getAttrFloat("overscan",B.overscan)}get shouldAutoplay(){return this.getAttrBool("autoplay",B.autoplay)}get shouldLoop(){return this.getAttrBool("loop",B.loop)}get shouldMute(){return this.getAttrBool("muted",B.muted)}emit(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}attachVideoEventListeners(t){t.addEventListener("play",()=>{this.emit("layershift-parallax:play",{currentTime:t.currentTime})}),t.addEventListener("pause",()=>{this.emit("layershift-parallax:pause",{currentTime:t.currentTime})}),t.addEventListener("ended",()=>{t.loop&&(this.loopCount+=1,this.emit("layershift-parallax:loop",{loopCount:this.loopCount}))})}connectedCallback(){this.setupShadowDOM(),this.init()}disconnectedCallback(){this.dispose()}attributeChangedCallback(t,e,i){["src","depth-src","depth-meta"].includes(t)&&(this.initialized?(this.dispose(),this.setupShadowDOM(),this.init()):this.isConnected&&this.getAttribute("src")&&this.getAttribute("depth-src")&&this.getAttribute("depth-meta")&&this.init())}setupShadowDOM(){this.shadow.innerHTML="";const t=document.createElement("style");t.textContent=`
194
194
  :host {
195
195
  display: block;
196
196
  width: 100%;
@@ -210,4 +210,534 @@ ${i}`)}return o.detachShader(r,t),o.detachShader(r,e),o.deleteShader(t),o.delete
210
210
  width: 100%;
211
211
  height: 100%;
212
212
  }
213
- `,this.shadow.appendChild(t),this.container=document.createElement("div"),this.container.className="container",this.shadow.appendChild(this.container)}async init(){const t=this.getAttribute("src"),e=this.getAttribute("depth-src"),r=this.getAttribute("depth-meta");if(!t||!e||!r){const i="src, depth-src, and depth-meta attributes are required.";console.warn(`<layershift-parallax>: ${i}`),this.emit("layershift-parallax:error",{message:i});return}if(this.container){this.abortController=new AbortController;try{const[i,a]=await Promise.all([this.createVideoElement(t),X(e,r)]);if(this.abortController.signal.aborted){i.remove();return}this.video=i,this.loopCount=0,this.attachVideoEventListeners(i);const c=j(a.frames,a.meta.width,a.meta.height),n=K(c),l=this.hasAttribute("parallax-max")?this.parallaxMax/Math.max(i.videoWidth,1):n.parallaxStrength,h=this.hasAttribute("overscan")?this.overscan:n.overscanPadding;let d;try{const u=await V.create(a,a.meta.width,a.meta.height);this.depthWorker=u,d=m=>u.sample(m)}catch{const u=new k(a,a.meta.width,a.meta.height);d=m=>u.sample(m)}if(this.abortController.signal.aborted){i.remove(),this.depthWorker?.dispose(),this.depthWorker=null;return}this.renderer=new R(this.container,{parallaxStrength:l,pomEnabled:!0,pomSteps:n.pomSteps,overscanPadding:h,contrastLow:n.contrastLow,contrastHigh:n.contrastHigh,verticalReduction:n.verticalReduction,dofStart:n.dofStart,dofStrength:n.dofStrength}),this.renderer.initialize(i,a.meta.width,a.meta.height),this.inputHandler=new O(this);const p=this.parallaxX,s=this.parallaxY;if(this.renderer.start(i,d,()=>{const u=this.inputHandler.update();return{x:u.x*p,y:u.y*s}},(u,m)=>{this.emit("layershift-parallax:frame",{currentTime:u,frameNumber:m})}),this.shouldAutoplay){i.currentTime=0;try{await i.play()}catch{}}this.initialized=!0,this.emit("layershift-parallax:ready",{videoWidth:i.videoWidth,videoHeight:i.videoHeight,duration:i.duration,depthProfile:c,derivedParams:n})}catch(i){const a=i instanceof Error?i.message:"Failed to initialize.";console.error("<layershift-parallax>: Failed to initialize.",i),this.emit("layershift-parallax:error",{message:a})}}}async createVideoElement(t){const e=document.createElement("video");return e.crossOrigin="anonymous",e.setAttribute("crossorigin","anonymous"),e.playsInline=!0,e.setAttribute("playsinline",""),e.setAttribute("webkit-playsinline","true"),e.muted=this.shouldMute,e.defaultMuted=this.shouldMute,this.shouldMute&&e.setAttribute("muted",""),e.loop=this.shouldLoop,e.preload="auto",e.style.display="none",e.src=t,this.shadow.appendChild(e),await new Promise((r,i)=>{if(e.readyState>=HTMLMediaElement.HAVE_METADATA){r();return}const a=()=>{n(),r()},c=()=>{n(),i(new Error("Failed to load video metadata."))},n=()=>{e.removeEventListener("loadedmetadata",a),e.removeEventListener("error",c)};e.addEventListener("loadedmetadata",a),e.addEventListener("error",c),e.load()}),e}dispose(){this.abortController?.abort(),this.abortController=null,this.renderer?.dispose(),this.renderer=null,this.inputHandler?.dispose(),this.inputHandler=null,this.depthWorker?.dispose(),this.depthWorker=null,this.video&&(this.video.pause(),this.video.removeAttribute("src"),this.video.load(),this.video.remove(),this.video=null),this.initialized=!1,this.loopCount=0,this.container=null}}function y(o,t,e){return Math.min(e,Math.max(t,o))}function P(o,t,e){return o+(t-o)*e}return customElements.get(U.TAG_NAME)||customElements.define(U.TAG_NAME,U),C.LayershiftElement=U,Object.defineProperty(C,Symbol.toStringTag,{value:"Module"}),C})({});
213
+ `,this.shadow.appendChild(t),this.container=document.createElement("div"),this.container.className="container",this.shadow.appendChild(this.container)}async init(){const t=this.getAttribute("src"),e=this.getAttribute("depth-src"),i=this.getAttribute("depth-meta");if(!t||!e||!i){const r="src, depth-src, and depth-meta attributes are required.";console.warn(`<layershift-parallax>: ${r}`),this.emit("layershift-parallax:error",{message:r});return}if(this.container){this.abortController=new AbortController;try{const[r,o]=await Promise.all([this.createVideoElement(t),vt(e,i)]);if(this.abortController.signal.aborted){r.remove();return}this.video=r,this.loopCount=0,this.attachVideoEventListeners(r);const s=Ht(o.frames,o.meta.width,o.meta.height),a=Bt(s),h=this.hasAttribute("parallax-max")?this.parallaxMax/Math.max(r.videoWidth,1):a.parallaxStrength,l=this.hasAttribute("overscan")?this.overscan:a.overscanPadding;let u;try{const d=await it.create(o,o.meta.width,o.meta.height);this.depthWorker=d,u=m=>d.sample(m)}catch{const d=new gt(o,o.meta.width,o.meta.height);u=m=>d.sample(m)}if(this.abortController.signal.aborted){r.remove(),this.depthWorker?.dispose(),this.depthWorker=null;return}this.renderer=new Z(this.container,{parallaxStrength:h,pomEnabled:!0,pomSteps:a.pomSteps,overscanPadding:l,contrastLow:a.contrastLow,contrastHigh:a.contrastHigh,verticalReduction:a.verticalReduction,dofStart:a.dofStart,dofStrength:a.dofStrength}),this.renderer.initialize(r,o.meta.width,o.meta.height),this.inputHandler=new Yt(this);const f=this.parallaxX,c=this.parallaxY;if(this.renderer.start(r,u,()=>{const d=this.inputHandler.update();return{x:d.x*f,y:d.y*c}},(d,m)=>{this.emit("layershift-parallax:frame",{currentTime:d,frameNumber:m})}),this.shouldAutoplay){r.currentTime=0;try{await r.play()}catch{}}this.initialized=!0,this.emit("layershift-parallax:ready",{videoWidth:r.videoWidth,videoHeight:r.videoHeight,duration:r.duration,depthProfile:s,derivedParams:a})}catch(r){const o=r instanceof Error?r.message:"Failed to initialize.";console.error("<layershift-parallax>: Failed to initialize.",r),this.emit("layershift-parallax:error",{message:o})}}}async createVideoElement(t){const e=document.createElement("video");return e.crossOrigin="anonymous",e.setAttribute("crossorigin","anonymous"),e.playsInline=!0,e.setAttribute("playsinline",""),e.setAttribute("webkit-playsinline","true"),e.muted=this.shouldMute,e.defaultMuted=this.shouldMute,this.shouldMute&&e.setAttribute("muted",""),e.loop=this.shouldLoop,e.preload="auto",e.style.display="none",e.src=t,this.shadow.appendChild(e),await new Promise((i,r)=>{if(e.readyState>=HTMLMediaElement.HAVE_METADATA){i();return}const o=()=>{a(),i()},s=()=>{a(),r(new Error("Failed to load video metadata."))},a=()=>{e.removeEventListener("loadedmetadata",o),e.removeEventListener("error",s)};e.addEventListener("loadedmetadata",o),e.addEventListener("error",s),e.load()}),e}dispose(){this.abortController?.abort(),this.abortController=null,this.renderer?.dispose(),this.renderer=null,this.inputHandler?.dispose(),this.inputHandler=null,this.depthWorker?.dispose(),this.depthWorker=null,this.video&&(this.video.pause(),this.video.removeAttribute("src"),this.video.load(),this.video.remove(),this.video=null),this.initialized=!1,this.loopCount=0,this.container=null}}function N(n,t,e){return Math.min(e,Math.max(t,n))}function ot(n,t,e){return n+(t-n)*e}const qt=`#version 300 es
214
+ in vec2 aPosition;
215
+ uniform vec2 uMeshScale;
216
+ void main() {
217
+ gl_Position = vec4(aPosition * uMeshScale, 0.0, 1.0);
218
+ }
219
+ `,Zt=`#version 300 es
220
+ precision lowp float;
221
+ out vec4 fragColor;
222
+ void main() { fragColor = vec4(0.0); }
223
+ `,$t=`#version 300 es
224
+ in vec2 aPosition;
225
+ uniform vec2 uMeshScale;
226
+ void main() {
227
+ gl_Position = vec4(aPosition * uMeshScale, 0.0, 1.0);
228
+ }
229
+ `,Jt=`#version 300 es
230
+ precision lowp float;
231
+ out vec4 fragColor;
232
+ void main() { fragColor = vec4(1.0); }
233
+ `,Kt=`#version 300 es
234
+ in vec2 aPosition;
235
+ out vec2 vUv;
236
+ void main() {
237
+ vUv = aPosition * 0.5 + 0.5;
238
+ gl_Position = vec4(aPosition, 0.0, 1.0);
239
+ }
240
+ `,Qt=`#version 300 es
241
+ precision highp float;
242
+ uniform sampler2D uMask;
243
+ uniform vec2 uTexelSize;
244
+ in vec2 vUv;
245
+ out vec2 fragSeed;
246
+
247
+ void main() {
248
+ float center = texture(uMask, vUv).r;
249
+ float left = texture(uMask, vUv + vec2(-uTexelSize.x, 0.0)).r;
250
+ float right = texture(uMask, vUv + vec2( uTexelSize.x, 0.0)).r;
251
+ float up = texture(uMask, vUv + vec2(0.0, uTexelSize.y)).r;
252
+ float down = texture(uMask, vUv + vec2(0.0, -uTexelSize.y)).r;
253
+
254
+ bool isEdge = (step(0.5, center) != step(0.5, left)) ||
255
+ (step(0.5, center) != step(0.5, right)) ||
256
+ (step(0.5, center) != step(0.5, up)) ||
257
+ (step(0.5, center) != step(0.5, down));
258
+
259
+ if (isEdge) {
260
+ fragSeed = vUv;
261
+ } else {
262
+ fragSeed = vec2(-1.0);
263
+ }
264
+ }
265
+ `,te=`#version 300 es
266
+ in vec2 aPosition;
267
+ out vec2 vUv;
268
+ void main() {
269
+ vUv = aPosition * 0.5 + 0.5;
270
+ gl_Position = vec4(aPosition, 0.0, 1.0);
271
+ }
272
+ `,ee=`#version 300 es
273
+ precision highp float;
274
+ uniform sampler2D uSeedTex;
275
+ uniform float uStepSize;
276
+ in vec2 vUv;
277
+ out vec2 fragSeed;
278
+
279
+ void main() {
280
+ vec2 bestSeed = texture(uSeedTex, vUv).rg;
281
+ float bestDist = (bestSeed.x < 0.0) ? 1.0e10 : distance(vUv, bestSeed);
282
+
283
+ for (int dy = -1; dy <= 1; dy++) {
284
+ for (int dx = -1; dx <= 1; dx++) {
285
+ if (dx == 0 && dy == 0) continue;
286
+ vec2 offset = vec2(float(dx), float(dy)) * uStepSize;
287
+ vec2 sampleUv = vUv + offset;
288
+ if (sampleUv.x < 0.0 || sampleUv.x > 1.0 || sampleUv.y < 0.0 || sampleUv.y > 1.0) continue;
289
+ vec2 neighborSeed = texture(uSeedTex, sampleUv).rg;
290
+ if (neighborSeed.x < 0.0) continue;
291
+ float d = distance(vUv, neighborSeed);
292
+ if (d < bestDist) {
293
+ bestDist = d;
294
+ bestSeed = neighborSeed;
295
+ }
296
+ }
297
+ }
298
+
299
+ fragSeed = bestSeed;
300
+ }
301
+ `,ie=`#version 300 es
302
+ in vec2 aPosition;
303
+ out vec2 vUv;
304
+ void main() {
305
+ vUv = aPosition * 0.5 + 0.5;
306
+ gl_Position = vec4(aPosition, 0.0, 1.0);
307
+ }
308
+ `,re=`#version 300 es
309
+ precision highp float;
310
+ uniform sampler2D uSeedTex;
311
+ uniform sampler2D uMask;
312
+ uniform float uBevelWidth;
313
+ in vec2 vUv;
314
+ out vec4 fragDist;
315
+
316
+ void main() {
317
+ float mask = texture(uMask, vUv).r;
318
+ if (mask < 0.5) {
319
+ fragDist = vec4(0.0);
320
+ return;
321
+ }
322
+
323
+ vec2 seed = texture(uSeedTex, vUv).rg;
324
+ if (seed.x < 0.0) {
325
+ fragDist = vec4(1.0);
326
+ return;
327
+ }
328
+
329
+ float d = distance(vUv, seed);
330
+ float normalized = clamp(d / max(uBevelWidth, 0.001), 0.0, 1.0);
331
+ fragDist = vec4(normalized, 0.0, 0.0, 1.0);
332
+ }
333
+ `,oe=`#version 300 es
334
+ in vec2 aPosition;
335
+ uniform vec2 uUvOffset;
336
+ uniform vec2 uUvScale;
337
+ out vec2 vUv;
338
+ out vec2 vScreenUv;
339
+ void main() {
340
+ vec2 baseUv = aPosition * 0.5 + 0.5;
341
+ vUv = baseUv * uUvScale + uUvOffset;
342
+ vScreenUv = baseUv;
343
+ gl_Position = vec4(aPosition, 0.0, 1.0);
344
+ }
345
+ `,ne=`#version 300 es
346
+ precision highp float;
347
+
348
+ #define MAX_POM_STEPS 32
349
+
350
+ uniform sampler2D uImage;
351
+ uniform sampler2D uDepth;
352
+ uniform vec2 uOffset;
353
+ uniform float uStrength;
354
+ uniform int uPomSteps;
355
+
356
+ // Lens transform: remap depth curve for exaggerated/compressed depth feel
357
+ uniform float uDepthPower; // >1 = telephoto, <1 = wide-angle
358
+ uniform float uDepthScale; // multiplier on depth range
359
+ uniform float uDepthBias; // shift depth origin
360
+
361
+ // Depth-adaptive contrast
362
+ uniform float uContrastLow;
363
+ uniform float uContrastHigh;
364
+ uniform float uVerticalReduction;
365
+
366
+ // DOF
367
+ uniform float uDofStart;
368
+ uniform float uDofStrength;
369
+ uniform vec2 uImageTexelSize;
370
+
371
+ // Interior mood
372
+ uniform float uFogDensity; // volumetric fog bias (0 = none, 0.3 = subtle)
373
+ uniform vec3 uFogColor; // fog tint color
374
+ uniform float uColorShift; // warm/cool grading shift
375
+ uniform float uBrightnessBias; // overall brightness adjustment
376
+
377
+ in vec2 vUv;
378
+ in vec2 vScreenUv;
379
+
380
+ layout(location = 0) out vec4 fragColor;
381
+ layout(location = 1) out vec4 fragDepth;
382
+
383
+ // Apply lens transform to raw depth
384
+ float lensDepth(float raw) {
385
+ float d = smoothstep(uContrastLow, uContrastHigh, raw);
386
+ d = pow(d, uDepthPower) * uDepthScale + uDepthBias;
387
+ return clamp(d, 0.0, 1.0);
388
+ }
389
+
390
+ float edgeFade(vec2 uv) {
391
+ float margin = uStrength * 1.5;
392
+ float fadeX = smoothstep(0.0, margin, uv.x) * smoothstep(0.0, margin, 1.0 - uv.x);
393
+ float fadeY = smoothstep(0.0, margin, uv.y) * smoothstep(0.0, margin, 1.0 - uv.y);
394
+ return fadeX * fadeY;
395
+ }
396
+
397
+ // POM ray-march with lens-transformed depth
398
+ vec2 pomDisplace(vec2 uv, out float hitDepth) {
399
+ float layerD = 1.0 / float(uPomSteps);
400
+ vec2 scaledOffset = uOffset;
401
+ scaledOffset.y *= uVerticalReduction;
402
+ vec2 deltaUV = scaledOffset * uStrength / float(uPomSteps);
403
+ float currentLayerDepth = 0.0;
404
+ vec2 currentUV = uv;
405
+ float fade = edgeFade(uv);
406
+
407
+ for (int i = 0; i < MAX_POM_STEPS; i++) {
408
+ if (i >= uPomSteps) break;
409
+ float raw = texture(uDepth, currentUV).r;
410
+ float depthAtUV = 1.0 - lensDepth(raw);
411
+ if (currentLayerDepth > depthAtUV) {
412
+ vec2 prevUV = currentUV - deltaUV;
413
+ float prevLayerD = currentLayerDepth - layerD;
414
+ float prevRaw = texture(uDepth, prevUV).r;
415
+ float prevDepthAtUV = 1.0 - lensDepth(prevRaw);
416
+ float afterD = depthAtUV - currentLayerDepth;
417
+ float beforeD = prevDepthAtUV - prevLayerD;
418
+ float t = afterD / (afterD - beforeD);
419
+ vec2 hitUV = mix(currentUV, prevUV, t);
420
+ hitDepth = mix(depthAtUV, prevDepthAtUV, t);
421
+ return mix(uv, hitUV, fade);
422
+ }
423
+ currentUV += deltaUV;
424
+ currentLayerDepth += layerD;
425
+ }
426
+ hitDepth = 1.0 - lensDepth(texture(uDepth, currentUV).r);
427
+ return mix(uv, currentUV, fade);
428
+ }
429
+
430
+ void main() {
431
+ float hitDepth;
432
+ vec2 displaced = pomDisplace(vUv, hitDepth);
433
+ displaced = clamp(displaced, vec2(0.0), vec2(1.0));
434
+
435
+ vec4 color = texture(uImage, displaced);
436
+
437
+ // DOF: blur far objects
438
+ float rawDepthAtHit = texture(uDepth, displaced).r;
439
+ float lensD = lensDepth(rawDepthAtHit);
440
+ float dof = smoothstep(uDofStart, 1.0, lensD) * uDofStrength;
441
+ if (dof > 0.01) {
442
+ vec2 ts = uImageTexelSize;
443
+ vec4 blurred = (
444
+ texture(uImage, displaced + vec2( ts.x, 0.0)) +
445
+ texture(uImage, displaced + vec2(-ts.x, 0.0)) +
446
+ texture(uImage, displaced + vec2( 0.0, ts.y)) +
447
+ texture(uImage, displaced + vec2( 0.0, -ts.y)) +
448
+ texture(uImage, displaced + vec2( ts.x, ts.y)) +
449
+ texture(uImage, displaced + vec2(-ts.x, -ts.y)) +
450
+ texture(uImage, displaced + vec2( ts.x, -ts.y)) +
451
+ texture(uImage, displaced + vec2(-ts.x, ts.y))
452
+ ) * 0.125;
453
+ color = mix(color, blurred, dof);
454
+ }
455
+
456
+ // Volumetric fog bias: far objects fade into fog color
457
+ float fogFactor = smoothstep(0.3, 1.0, lensD) * uFogDensity;
458
+ color.rgb = mix(color.rgb, uFogColor, fogFactor);
459
+
460
+ // Color grading shift: warm near, cool far (or vice versa)
461
+ float gradeAmount = (lensD - 0.5) * uColorShift;
462
+ color.r += gradeAmount * 0.08;
463
+ color.b -= gradeAmount * 0.08;
464
+
465
+ // Brightness bias
466
+ color.rgb *= (1.0 + uBrightnessBias);
467
+
468
+ // Subtle vignette inside portal
469
+ float dist = length(vScreenUv - 0.5) * 1.4;
470
+ color.rgb *= 1.0 - pow(dist, 3.0) * 0.3;
471
+
472
+ fragColor = color;
473
+ // Write lens-transformed depth to second attachment for boundary effects
474
+ fragDepth = vec4(lensD, 0.0, 0.0, 1.0);
475
+ }
476
+ `,se=`#version 300 es
477
+ in vec2 aPosition;
478
+ out vec2 vUv;
479
+ void main() {
480
+ vUv = aPosition * 0.5 + 0.5;
481
+ gl_Position = vec4(aPosition, 0.0, 1.0);
482
+ }
483
+ `,ae=`#version 300 es
484
+ precision highp float;
485
+ uniform sampler2D uInteriorColor;
486
+ uniform sampler2D uDistField;
487
+ uniform float uEdgeOcclusionWidth; // how far edge darkening extends
488
+ uniform float uEdgeOcclusionStrength; // how strong (0=none, 1=full black)
489
+
490
+ in vec2 vUv;
491
+ out vec4 fragColor;
492
+
493
+ // sRGB ↔ linear conversions for correct lighting math
494
+ vec3 toLinear(vec3 s) {
495
+ return mix(s / 12.92, pow((s + 0.055) / 1.055, vec3(2.4)), step(0.04045, s));
496
+ }
497
+ vec3 toSRGB(vec3 l) {
498
+ return mix(l * 12.92, 1.055 * pow(l, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, l));
499
+ }
500
+
501
+ void main() {
502
+ vec4 color = texture(uInteriorColor, vUv);
503
+ float dist = texture(uDistField, vUv).r; // 0=edge, 1=deep interior
504
+
505
+ // Emissive passthrough: preserve original video luminance.
506
+ // Only apply a subtle edge occlusion ramp to sell chamfer→interior depth.
507
+ vec3 linear = toLinear(color.rgb);
508
+ float occ = smoothstep(0.0, uEdgeOcclusionWidth, dist);
509
+ linear *= mix(1.0 - uEdgeOcclusionStrength, 1.0, occ);
510
+
511
+ fragColor = vec4(toSRGB(linear), color.a);
512
+ }
513
+ `,he=`#version 300 es
514
+ in vec2 aPosition;
515
+ in vec2 aNormal;
516
+ uniform float uRimWidth;
517
+ uniform vec2 uMeshScale;
518
+ out vec2 vNormal;
519
+ out vec2 vEdgeUv; // screen-space UV for sampling FBO textures
520
+ out float vEdgeDist; // 0 at edge, 1 at outer extent
521
+
522
+ void main() {
523
+ vec2 scaledPos = aPosition * uMeshScale;
524
+ vec2 scaledNormal = normalize(aNormal * uMeshScale);
525
+ vec2 pos = scaledPos + scaledNormal * uRimWidth;
526
+
527
+ // Pass screen-space UV of this fragment for FBO sampling
528
+ vEdgeUv = pos * 0.5 + 0.5;
529
+ vNormal = scaledNormal;
530
+
531
+ // Distance from the actual edge (0) to the outer rim extent (1)
532
+ vEdgeDist = length(pos - scaledPos) / max(uRimWidth, 0.001);
533
+
534
+ gl_Position = vec4(pos, 0.0, 1.0);
535
+ }
536
+ `,le=`#version 300 es
537
+ precision highp float;
538
+
539
+ uniform sampler2D uInteriorColor;
540
+ uniform sampler2D uInteriorDepth;
541
+ uniform sampler2D uDistField;
542
+ uniform float uRimIntensity;
543
+ uniform vec3 uRimColor;
544
+ uniform float uRefractionStrength;
545
+ uniform float uChromaticStrength;
546
+ uniform float uOcclusionIntensity;
547
+ uniform vec2 uTexelSize; // 1.0 / viewport resolution
548
+
549
+ // Volumetric edge wall
550
+ uniform float uEdgeThickness;
551
+ uniform float uEdgeSpecular;
552
+ uniform vec3 uEdgeColor;
553
+ uniform vec2 uLightDir;
554
+ uniform float uBevelIntensity;
555
+
556
+ in vec2 vNormal;
557
+ in vec2 vEdgeUv;
558
+ in float vEdgeDist;
559
+ out vec4 fragColor;
560
+
561
+ void main() {
562
+ // Clamp UV to valid range for texture sampling
563
+ vec2 sampleUv = clamp(vEdgeUv, vec2(0.001), vec2(0.999));
564
+
565
+ // Sample interior depth at this boundary location
566
+ float interiorDepth = texture(uInteriorDepth, sampleUv).r;
567
+
568
+ // === DEPTH-REACTIVE RIM (structural seam) ===
569
+ float depthReactivity = 1.0 - interiorDepth; // 1=near, 0=far
570
+ float rimProfile = 1.0 - smoothstep(0.0, 1.0, vEdgeDist);
571
+ rimProfile = pow(rimProfile, 1.5); // sharper falloff = more structural
572
+
573
+ float depthPressure = mix(0.2, 1.0, depthReactivity * depthReactivity);
574
+ float rim = rimProfile * depthPressure * uRimIntensity;
575
+
576
+ vec3 rimCol = uRimColor;
577
+ rimCol.r += depthReactivity * 0.15;
578
+ rimCol.g += depthReactivity * 0.05;
579
+
580
+ // === REFRACTION DISTORTION ===
581
+ vec2 ts = uTexelSize * 3.0;
582
+ float dLeft = texture(uInteriorDepth, sampleUv + vec2(-ts.x, 0.0)).r;
583
+ float dRight = texture(uInteriorDepth, sampleUv + vec2( ts.x, 0.0)).r;
584
+ float dUp = texture(uInteriorDepth, sampleUv + vec2(0.0, ts.y)).r;
585
+ float dDown = texture(uInteriorDepth, sampleUv + vec2(0.0, -ts.y)).r;
586
+ vec2 depthGradient = vec2(dRight - dLeft, dUp - dDown);
587
+ vec2 refractUv = sampleUv + depthGradient * uRefractionStrength * rimProfile;
588
+ refractUv = clamp(refractUv, vec2(0.001), vec2(0.999));
589
+
590
+ vec4 refractedColor = texture(uInteriorColor, refractUv);
591
+
592
+ // === CHROMATIC FRINGE ===
593
+ float chromaticAmount = uChromaticStrength * depthReactivity * rimProfile;
594
+ vec2 chromaticDir = vNormal * chromaticAmount;
595
+ float cr = texture(uInteriorColor, refractUv + chromaticDir).r;
596
+ float cg = refractedColor.g;
597
+ float cb = texture(uInteriorColor, refractUv - chromaticDir).b;
598
+ vec3 chromaticColor = vec3(cr, cg, cb);
599
+
600
+ // === OCCLUSION CONTACT SHADOW ===
601
+ float occlusionAmount = smoothstep(0.4, 0.0, interiorDepth) * uOcclusionIntensity * rimProfile;
602
+
603
+ // === VOLUMETRIC EDGE WALL ===
604
+ // Sample distance field to get the inner-side distance at this boundary location
605
+ float edgeDist = texture(uDistField, sampleUv).r;
606
+ float wallZone = smoothstep(uEdgeThickness, 0.0, edgeDist) * rimProfile;
607
+
608
+ // Wall lighting from distance field gradient
609
+ vec2 dtx = vec2(1.0) / vec2(textureSize(uDistField, 0));
610
+ float wdL = texture(uDistField, sampleUv + vec2(-dtx.x, 0.0)).r;
611
+ float wdR = texture(uDistField, sampleUv + vec2( dtx.x, 0.0)).r;
612
+ float wdU = texture(uDistField, sampleUv + vec2(0.0, dtx.y)).r;
613
+ float wdD = texture(uDistField, sampleUv + vec2(0.0, -dtx.y)).r;
614
+ vec2 wallNormal = vec2(wdR - wdL, wdU - wdD);
615
+ float wnLen = length(wallNormal);
616
+ if (wnLen > 0.001) wallNormal /= wnLen;
617
+
618
+ float wallSpec = pow(max(dot(wallNormal, uLightDir), 0.0), 16.0) * uEdgeSpecular;
619
+ vec3 wallColor = mix(refractedColor.rgb * 0.4, uEdgeColor, 0.3);
620
+ wallColor += vec3(wallSpec);
621
+
622
+ // === COMPOSITE ===
623
+ vec3 color = mix(refractedColor.rgb, chromaticColor, min(chromaticAmount * 10.0, 1.0));
624
+ color *= (1.0 - occlusionAmount * 0.4);
625
+
626
+ // Blend in volumetric wall
627
+ color = mix(color, wallColor, wallZone * uBevelIntensity);
628
+
629
+ // Add rim energy on top
630
+ color += rimCol * rim;
631
+
632
+ // Alpha: rim edge fades out
633
+ float alpha = rimProfile * max(rim, occlusionAmount + chromaticAmount * 5.0 + wallZone * 0.5);
634
+ alpha = clamp(alpha, 0.0, 1.0);
635
+
636
+ fragColor = vec4(color * alpha, alpha);
637
+ }
638
+ `,ce=`#version 300 es
639
+ in vec2 aPosition;
640
+ in vec3 aNormal3;
641
+ in float aLerpT; // 0 = inner (at silhouette), 1 = outer edge
642
+ uniform vec2 uMeshScale;
643
+ out vec3 vNormal;
644
+ out vec2 vScreenUv;
645
+ out float vLerpT;
646
+
647
+ void main() {
648
+ vec2 sp = aPosition * uMeshScale;
649
+ vNormal = aNormal3;
650
+ vScreenUv = sp * 0.5 + 0.5;
651
+ vLerpT = aLerpT;
652
+ gl_Position = vec4(sp, 0.0, 1.0);
653
+ }
654
+ `,ue=`#version 300 es
655
+ precision highp float;
656
+ uniform vec3 uLightDir3;
657
+ uniform vec3 uChamferColor;
658
+ uniform float uChamferAmbient;
659
+ uniform float uChamferSpecular;
660
+ uniform float uChamferShininess;
661
+ uniform sampler2D uInteriorColor;
662
+ uniform vec2 uTexelSize; // 1 / viewport resolution
663
+
664
+ in vec3 vNormal;
665
+ in vec2 vScreenUv;
666
+ in float vLerpT;
667
+ out vec4 fragColor;
668
+
669
+ vec3 toLinear(vec3 s) {
670
+ return mix(s / 12.92, pow((s + 0.055) / 1.055, vec3(2.4)), step(0.04045, s));
671
+ }
672
+ vec3 toSRGB(vec3 l) {
673
+ return mix(l * 12.92, 1.055 * pow(l, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, l));
674
+ }
675
+
676
+ // Approximate gaussian blur via 13-tap poisson disc, radius scaled by vLerpT.
677
+ vec3 blurSample(vec2 center, float radius) {
678
+ // Poisson disc offsets (normalized to unit circle)
679
+ const vec2 offsets[12] = vec2[12](
680
+ vec2(-0.326, -0.406), vec2(-0.840, -0.074), vec2(-0.696, 0.457),
681
+ vec2(-0.203, 0.621), vec2( 0.962, -0.195), vec2( 0.473, -0.480),
682
+ vec2( 0.519, 0.767), vec2( 0.185, -0.893), vec2( 0.507, 0.064),
683
+ vec2(-0.321, -0.860), vec2(-0.791, 0.557), vec2( 0.330, 0.418)
684
+ );
685
+ vec3 sum = texture(uInteriorColor, center).rgb;
686
+ for (int i = 0; i < 12; i++) {
687
+ vec2 uv = center + offsets[i] * radius;
688
+ uv = clamp(uv, vec2(0.001), vec2(0.999));
689
+ sum += texture(uInteriorColor, uv).rgb;
690
+ }
691
+ return sum / 13.0;
692
+ }
693
+
694
+ void main() {
695
+ vec3 N = normalize(vNormal);
696
+ vec3 L = normalize(uLightDir3);
697
+ vec3 V = vec3(0.0, 0.0, -1.0); // orthographic view direction
698
+
699
+ // Blinn-Phong lighting in linear space
700
+ float diff = max(dot(N, L), 0.0);
701
+ vec3 H = normalize(L + V);
702
+ float spec = pow(max(dot(N, H), 0.0), uChamferShininess) * uChamferSpecular;
703
+
704
+ // Sample interior video with progressive blur (sharper at inner edge)
705
+ vec2 uv = clamp(vScreenUv, vec2(0.001), vec2(0.999));
706
+ float blurRadius = vLerpT * 12.0 * length(uTexelSize);
707
+ vec3 videoSample = blurRadius > 0.0001
708
+ ? blurSample(uv, blurRadius)
709
+ : texture(uInteriorColor, uv).rgb;
710
+
711
+ // Base color: video tinted through chamfer color (like frosted glass)
712
+ vec3 video = toLinear(videoSample);
713
+ vec3 tint = toLinear(uChamferColor);
714
+ // Blend: mostly video near inner edge, more tinted at outer edge
715
+ vec3 base = mix(video, video * tint * 3.0, vLerpT * 0.5);
716
+
717
+ // Apply Blinn-Phong
718
+ vec3 lit = base * (uChamferAmbient + (1.0 - uChamferAmbient) * diff) + vec3(spec);
719
+ fragColor = vec4(toSRGB(lit), 1.0);
720
+ }
721
+ `;function L(n,t,e){const i=n.createShader(t);if(!i)throw new Error("Failed to create shader.");if(n.shaderSource(i,e),n.compileShader(i),!n.getShaderParameter(i,n.COMPILE_STATUS)){const r=n.getShaderInfoLog(i)??"";throw n.deleteShader(i),new Error(`Shader compilation failed:
722
+ ${r}`)}return i}function O(n,t,e){const i=n.createProgram();if(!i)throw new Error("Failed to create program.");if(n.attachShader(i,t),n.attachShader(i,e),n.linkProgram(i),!n.getProgramParameter(i,n.LINK_STATUS)){const r=n.getProgramInfoLog(i)??"";throw n.deleteProgram(i),new Error(`Program linking failed:
723
+ ${r}`)}return n.detachShader(i,t),n.detachShader(i,e),n.deleteShader(t),n.deleteShader(e),i}function fe(n){const t=[];let e=0;for(let i=0;i<n.length-2;i+=2){const r=n[i],o=n[i+1],s=n[i+2],a=n[i+3],h=s-r,l=a-o,u=Math.sqrt(h*h+l*l);if(u<1e-6)continue;const f=-l/u,c=h/u;t.push(r,o,f,c,r,o,-f,-c,s,a,f,c,s,a,f,c,r,o,-f,-c,s,a,-f,-c),e+=6}return{vertices:new Float32Array(t),count:e}}function de(n,t,e,i,r){if(i<=0)return{vertices:new Float32Array(0),count:0};const o=r*Math.PI/180,s=-Math.cos(o),a=Math.sin(o),h=[];let l=0;for(let u=0;u<t.length;u++){const f=t[u],m=((u+1<t.length?t[u+1]:n.length)-f)/2;if(m<3)continue;const g=m-1;let U=0;for(let x=0;x<g;x++){const A=f+x*2,S=n[A],F=n[A+1],P=n[A+2],_=n[A+3];U+=S*_-P*F}const y=U>=0?1:-1,E=[],p=[];for(let x=0;x<g;x++){const A=f+x*2,S=n[A+2]-n[A],F=n[A+3]-n[A+1],P=Math.sqrt(S*S+F*F);P<1e-8?(E.push(x>0?E[x-1]:0),p.push(x>0?p[x-1]:0)):(E.push(-F/P*y),p.push(S/P*y))}const v=[],T=[];for(let x=0;x<g;x++){const A=(x-1+g)%g;let S=E[A]+E[x],F=p[A]+p[x];const P=Math.sqrt(S*S+F*F);P>1e-8?(S/=P,F/=P):(S=E[x],F=p[x]),v.push(S),T.push(F)}for(let x=0;x<g;x++){const A=x,S=(x+1)%g,F=f+x*2,P=f+(x+1)%g*2,_=n[F],k=n[F+1],D=n[P],R=n[P+1],C=v[A]*a,M=T[A]*a,H=s,I=v[S]*a,G=T[S]*a,tt=s,ct=_+v[A]*i,wt=k+T[A]*i,Ne=D+v[S]*i,ze=R+T[S]*i;h.push(_,k,C,M,H,0),h.push(ct,wt,C,M,H,1),h.push(D,R,I,G,tt,0),h.push(D,R,I,G,tt,0),h.push(ct,wt,C,M,H,1),h.push(Ne,ze,I,G,tt,1),l+=6}}return{vertices:new Float32Array(h),count:l}}class ft{static RESIZE_DEBOUNCE_MS=100;canvas;gl=null;container;stencilProgram=null;maskProgram=null;jfaSeedProgram=null;jfaFloodProgram=null;jfaDistProgram=null;interiorProgram=null;compositeProgram=null;boundaryProgram=null;chamferProgram=null;stencilUniforms={};maskUniforms={};jfaSeedUniforms={};jfaFloodUniforms={};jfaDistUniforms={};interiorUniforms={};compositeUniforms={};boundaryUniforms={};chamferUniforms={};quadVao=null;stencilVao=null;stencilIndexCount=0;maskVao=null;boundaryVao=null;boundaryVertexCount=0;chamferVao=null;chamferVertexCount=0;videoTexture=null;depthTexture=null;interiorFbo=null;interiorColorTex=null;interiorDepthTex=null;fboWidth=0;fboHeight=0;maskFbo=null;maskTex=null;jfaPingFbo=null;jfaPingTex=null;jfaPongFbo=null;jfaPongTex=null;distFbo=null;distTex=null;jfaWidth=0;jfaHeight=0;distFieldDirty=!0;depthWidth=0;depthHeight=0;videoAspect=1.7777777777777777;meshAspect=1;meshScaleX=.65;meshScaleY=.65;readDepth=null;readInput=null;playbackVideo=null;onVideoFrame=null;animationFrameHandle=0;rvfcHandle=0;rvfcSupported=!1;resizeObserver=null;resizeTimer=null;uvOffset=[0,0];uvScale=[1,1];lightDirX=-.707;lightDirY=.707;lightDir3=[-.5,.7,-.3];config;constructor(t,e){this.container=t,this.config={...e};const i=this.config.bevelLightAngle*Math.PI/180;this.lightDirX=Math.cos(i),this.lightDirY=Math.sin(i);const r=this.config.lightDirection,o=Math.sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]);o>1e-6&&(this.lightDir3=[r[0]/o,r[1]/o,r[2]/o]),this.canvas=document.createElement("canvas");const s=this.canvas.getContext("webgl2",{antialias:!0,alpha:!0,premultipliedAlpha:!0,stencil:!0,desynchronized:!0,powerPreference:"high-performance"});if(!s)throw new Error("WebGL 2 is not supported.");this.gl=s,"drawingBufferColorSpace"in s&&(s.drawingBufferColorSpace="srgb"),s.clearColor(0,0,0,0),s.pixelStorei(s.UNPACK_FLIP_Y_WEBGL,!0),this.container.appendChild(this.canvas),this.initGPUResources(),this.setupResizeHandling(),this.canvas.addEventListener("webglcontextlost",this.handleContextLost),this.canvas.addEventListener("webglcontextrestored",this.handleContextRestored)}initialize(t,e,i,r){const o=this.gl;o&&(this.disposeTextures(),this.disposeFBO(),this.disposeJFA(),this.disposeStencilGeometry(),this.disposeBoundaryGeometry(),this.disposeChamferGeometry(),this.videoAspect=t.videoWidth/t.videoHeight,this.meshAspect=r.aspect,this.depthWidth=e,this.depthHeight=i,this.videoTexture=o.createTexture(),o.activeTexture(o.TEXTURE0),o.bindTexture(o.TEXTURE_2D,this.videoTexture),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MIN_FILTER,o.LINEAR),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MAG_FILTER,o.LINEAR),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_S,o.CLAMP_TO_EDGE),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_T,o.CLAMP_TO_EDGE),this.depthTexture=o.createTexture(),o.activeTexture(o.TEXTURE1),o.bindTexture(o.TEXTURE_2D,this.depthTexture),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MIN_FILTER,o.LINEAR),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MAG_FILTER,o.LINEAR),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_S,o.CLAMP_TO_EDGE),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_T,o.CLAMP_TO_EDGE),o.texStorage2D(o.TEXTURE_2D,1,o.R8,e,i),this.uploadStencilMesh(r),this.uploadMaskMesh(r),this.uploadBoundaryMesh(r),this.uploadChamferMesh(r),this.interiorProgram&&(o.useProgram(this.interiorProgram),o.uniform1i(this.interiorUniforms.uImage,0),o.uniform1i(this.interiorUniforms.uDepth,1),o.uniform1f(this.interiorUniforms.uStrength,this.config.parallaxStrength),o.uniform1i(this.interiorUniforms.uPomSteps,this.config.pomSteps),o.uniform1f(this.interiorUniforms.uDepthPower,this.config.depthPower),o.uniform1f(this.interiorUniforms.uDepthScale,this.config.depthScale),o.uniform1f(this.interiorUniforms.uDepthBias,this.config.depthBias),o.uniform1f(this.interiorUniforms.uContrastLow,this.config.contrastLow),o.uniform1f(this.interiorUniforms.uContrastHigh,this.config.contrastHigh),o.uniform1f(this.interiorUniforms.uVerticalReduction,this.config.verticalReduction),o.uniform1f(this.interiorUniforms.uDofStart,this.config.dofStart),o.uniform1f(this.interiorUniforms.uDofStrength,this.config.dofStrength),o.uniform2f(this.interiorUniforms.uImageTexelSize,1/t.videoWidth,1/t.videoHeight),o.uniform1f(this.interiorUniforms.uFogDensity,this.config.fogDensity),o.uniform3f(this.interiorUniforms.uFogColor,...this.config.fogColor),o.uniform1f(this.interiorUniforms.uColorShift,this.config.colorShift),o.uniform1f(this.interiorUniforms.uBrightnessBias,this.config.brightnessBias)),this.compositeProgram&&(o.useProgram(this.compositeProgram),o.uniform1i(this.compositeUniforms.uInteriorColor,2),o.uniform1i(this.compositeUniforms.uDistField,4),o.uniform1f(this.compositeUniforms.uEdgeOcclusionWidth,this.config.edgeOcclusionWidth),o.uniform1f(this.compositeUniforms.uEdgeOcclusionStrength,this.config.edgeOcclusionStrength)),this.chamferProgram&&(o.useProgram(this.chamferProgram),o.uniform3f(this.chamferUniforms.uLightDir3,...this.lightDir3),o.uniform3f(this.chamferUniforms.uChamferColor,...this.config.chamferColor),o.uniform1f(this.chamferUniforms.uChamferAmbient,this.config.chamferAmbient),o.uniform1f(this.chamferUniforms.uChamferSpecular,this.config.chamferSpecular),o.uniform1f(this.chamferUniforms.uChamferShininess,this.config.chamferShininess),o.uniform1i(this.chamferUniforms.uInteriorColor,2)),this.boundaryProgram&&(o.useProgram(this.boundaryProgram),o.uniform1i(this.boundaryUniforms.uInteriorColor,2),o.uniform1i(this.boundaryUniforms.uInteriorDepth,3),o.uniform1i(this.boundaryUniforms.uDistField,4),o.uniform1f(this.boundaryUniforms.uRimIntensity,this.config.rimLightIntensity),o.uniform3f(this.boundaryUniforms.uRimColor,...this.config.rimLightColor),o.uniform1f(this.boundaryUniforms.uRefractionStrength,this.config.refractionStrength),o.uniform1f(this.boundaryUniforms.uChromaticStrength,this.config.chromaticStrength),o.uniform1f(this.boundaryUniforms.uOcclusionIntensity,this.config.occlusionIntensity),o.uniform1f(this.boundaryUniforms.uEdgeThickness,this.config.edgeThickness),o.uniform1f(this.boundaryUniforms.uEdgeSpecular,this.config.edgeSpecular),o.uniform3f(this.boundaryUniforms.uEdgeColor,...this.config.edgeColor),o.uniform2f(this.boundaryUniforms.uLightDir,this.lightDirX,this.lightDirY),o.uniform1f(this.boundaryUniforms.uBevelIntensity,this.config.bevelIntensity)),this.recalculateViewportLayout())}uploadStencilMesh(t){const e=this.gl;if(!e||!this.stencilProgram)return;this.stencilVao=e.createVertexArray(),e.bindVertexArray(this.stencilVao);const i=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,i),e.bufferData(e.ARRAY_BUFFER,t.vertices,e.STATIC_DRAW);const r=e.getAttribLocation(this.stencilProgram,"aPosition");e.enableVertexAttribArray(r),e.vertexAttribPointer(r,2,e.FLOAT,!1,0,0);const o=e.createBuffer();e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,o),e.bufferData(e.ELEMENT_ARRAY_BUFFER,t.indices,e.STATIC_DRAW),this.stencilIndexCount=t.indices.length,e.bindVertexArray(null)}uploadMaskMesh(t){const e=this.gl;if(!e||!this.maskProgram)return;this.maskVao=e.createVertexArray(),e.bindVertexArray(this.maskVao);const i=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,i),e.bufferData(e.ARRAY_BUFFER,t.vertices,e.STATIC_DRAW);const r=e.getAttribLocation(this.maskProgram,"aPosition");e.enableVertexAttribArray(r),e.vertexAttribPointer(r,2,e.FLOAT,!1,0,0);const o=e.createBuffer();e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,o),e.bufferData(e.ELEMENT_ARRAY_BUFFER,t.indices,e.STATIC_DRAW),e.bindVertexArray(null)}uploadBoundaryMesh(t){const e=this.gl;if(!e||!this.boundaryProgram)return;const i=fe(t.edgeVertices);if(i.count===0)return;this.boundaryVao=e.createVertexArray(),e.bindVertexArray(this.boundaryVao);const r=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,r),e.bufferData(e.ARRAY_BUFFER,i.vertices,e.STATIC_DRAW);const o=16,s=e.getAttribLocation(this.boundaryProgram,"aPosition");e.enableVertexAttribArray(s),e.vertexAttribPointer(s,2,e.FLOAT,!1,o,0);const a=e.getAttribLocation(this.boundaryProgram,"aNormal");a>=0&&(e.enableVertexAttribArray(a),e.vertexAttribPointer(a,2,e.FLOAT,!1,o,8)),this.boundaryVertexCount=i.count,e.bindVertexArray(null)}uploadChamferMesh(t){const e=this.gl;if(!e||!this.chamferProgram||this.config.chamferWidth<=0)return;const i=de(t.edgeVertices,t.contourOffsets,t.contourIsHole,this.config.chamferWidth,this.config.chamferAngle);if(i.count===0)return;this.chamferVao=e.createVertexArray(),e.bindVertexArray(this.chamferVao);const r=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,r),e.bufferData(e.ARRAY_BUFFER,i.vertices,e.STATIC_DRAW);const o=24,s=e.getAttribLocation(this.chamferProgram,"aPosition");e.enableVertexAttribArray(s),e.vertexAttribPointer(s,2,e.FLOAT,!1,o,0);const a=e.getAttribLocation(this.chamferProgram,"aNormal3");a>=0&&(e.enableVertexAttribArray(a),e.vertexAttribPointer(a,3,e.FLOAT,!1,o,8));const h=e.getAttribLocation(this.chamferProgram,"aLerpT");h>=0&&(e.enableVertexAttribArray(h),e.vertexAttribPointer(h,1,e.FLOAT,!1,o,20)),this.chamferVertexCount=i.count,e.bindVertexArray(null)}disposeChamferGeometry(){const t=this.gl;t&&(this.chamferVao&&(t.deleteVertexArray(this.chamferVao),this.chamferVao=null),this.chamferVertexCount=0)}createFBO(t,e){const i=this.gl;if(!i)return;this.disposeFBO(),this.fboWidth=t,this.fboHeight=e,this.interiorFbo=i.createFramebuffer(),i.bindFramebuffer(i.FRAMEBUFFER,this.interiorFbo),this.interiorColorTex=i.createTexture(),i.activeTexture(i.TEXTURE2),i.bindTexture(i.TEXTURE_2D,this.interiorColorTex),i.texStorage2D(i.TEXTURE_2D,1,i.RGBA8,t,e),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.framebufferTexture2D(i.FRAMEBUFFER,i.COLOR_ATTACHMENT0,i.TEXTURE_2D,this.interiorColorTex,0),this.interiorDepthTex=i.createTexture(),i.activeTexture(i.TEXTURE3),i.bindTexture(i.TEXTURE_2D,this.interiorDepthTex),i.texStorage2D(i.TEXTURE_2D,1,i.RGBA8,t,e),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.framebufferTexture2D(i.FRAMEBUFFER,i.COLOR_ATTACHMENT1,i.TEXTURE_2D,this.interiorDepthTex,0),i.drawBuffers([i.COLOR_ATTACHMENT0,i.COLOR_ATTACHMENT1]);const r=i.checkFramebufferStatus(i.FRAMEBUFFER);r!==i.FRAMEBUFFER_COMPLETE&&console.error("Interior FBO incomplete:",r),i.bindFramebuffer(i.FRAMEBUFFER,null)}createJFAResources(t,e){const i=this.gl;if(!i)return;this.disposeJFA();const r=Math.max(1,Math.round(t/2)),o=Math.max(1,Math.round(e/2));this.jfaWidth=r,this.jfaHeight=o;const s=(a,h,l,u)=>{const f=i.createFramebuffer();return i.bindFramebuffer(i.FRAMEBUFFER,f),i.bindTexture(i.TEXTURE_2D,a),i.texStorage2D(i.TEXTURE_2D,1,h,l,u),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.framebufferTexture2D(i.FRAMEBUFFER,i.COLOR_ATTACHMENT0,i.TEXTURE_2D,a,0),i.bindFramebuffer(i.FRAMEBUFFER,null),f};this.maskTex=i.createTexture(),this.maskFbo=s(this.maskTex,i.R8,r,o),this.jfaPingTex=i.createTexture(),this.jfaPingFbo=s(this.jfaPingTex,i.RG16F,r,o),this.jfaPongTex=i.createTexture(),this.jfaPongFbo=s(this.jfaPongTex,i.RG16F,r,o),this.distTex=i.createTexture(),this.distFbo=s(this.distTex,i.RGBA8,r,o),this.distFieldDirty=!0}computeDistanceField(){const t=this.gl;if(!t||!this.maskFbo||!this.maskVao||!this.quadVao||!this.jfaPingFbo||!this.jfaPongFbo||!this.distFbo)return;const e=this.jfaWidth,i=this.jfaHeight;if(e===0||i===0)return;t.viewport(0,0,e,i),t.disable(t.STENCIL_TEST),t.disable(t.BLEND),t.bindFramebuffer(t.FRAMEBUFFER,this.maskFbo),t.clearColor(0,0,0,1),t.clear(t.COLOR_BUFFER_BIT),t.useProgram(this.maskProgram),t.uniform2f(this.maskUniforms.uMeshScale,this.meshScaleX,this.meshScaleY),t.bindVertexArray(this.maskVao),t.drawElements(t.TRIANGLES,this.stencilIndexCount,t.UNSIGNED_SHORT,0),t.bindFramebuffer(t.FRAMEBUFFER,this.jfaPingFbo),t.clearColor(-1,-1,0,0),t.clear(t.COLOR_BUFFER_BIT),t.useProgram(this.jfaSeedProgram),t.activeTexture(t.TEXTURE5),t.bindTexture(t.TEXTURE_2D,this.maskTex),t.uniform1i(this.jfaSeedUniforms.uMask,5),t.uniform2f(this.jfaSeedUniforms.uTexelSize,1/e,1/i),t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4);const r=Math.max(e,i),o=[];let s=Math.ceil(r/2);for(;s>=1;)o.push(s),s=Math.floor(s/2);t.useProgram(this.jfaFloodProgram);let a=this.jfaPingTex,h=this.jfaPongFbo,l=this.jfaPongTex;for(let f=0;f<o.length;f++){const c=o[f]/Math.max(e,i);t.bindFramebuffer(t.FRAMEBUFFER,h),t.activeTexture(t.TEXTURE5),t.bindTexture(t.TEXTURE_2D,a),t.uniform1i(this.jfaFloodUniforms.uSeedTex,5),t.uniform1f(this.jfaFloodUniforms.uStepSize,c),t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4);const d=a,m=h;a=l,h=m===this.jfaPongFbo?this.jfaPingFbo:this.jfaPongFbo,l=d}t.bindFramebuffer(t.FRAMEBUFFER,this.distFbo),t.clearColor(0,0,0,1),t.clear(t.COLOR_BUFFER_BIT),t.useProgram(this.jfaDistProgram),t.activeTexture(t.TEXTURE5),t.bindTexture(t.TEXTURE_2D,a),t.uniform1i(this.jfaDistUniforms.uSeedTex,5),t.activeTexture(t.TEXTURE6),t.bindTexture(t.TEXTURE_2D,this.maskTex),t.uniform1i(this.jfaDistUniforms.uMask,6);const u=Math.max(this.config.bevelWidth,this.config.edgeOcclusionWidth);t.uniform1f(this.jfaDistUniforms.uBevelWidth,u),t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4),t.activeTexture(t.TEXTURE4),t.bindTexture(t.TEXTURE_2D,this.distTex),t.bindFramebuffer(t.FRAMEBUFFER,null),this.distFieldDirty=!1}start(t,e,i,r){this.stop(),this.playbackVideo=t,this.readDepth=e,this.readInput=i,this.onVideoFrame=r??null,this.rvfcSupported="requestVideoFrameCallback"in HTMLVideoElement.prototype,this.rvfcSupported&&(this.rvfcHandle=t.requestVideoFrameCallback(this.videoFrameLoop)),this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop)}stop(){this.animationFrameHandle&&(window.cancelAnimationFrame(this.animationFrameHandle),this.animationFrameHandle=0),this.rvfcHandle&&this.playbackVideo&&(this.playbackVideo.cancelVideoFrameCallback(this.rvfcHandle),this.rvfcHandle=0),this.playbackVideo=null,this.readDepth=null,this.readInput=null,this.onVideoFrame=null,this.rvfcSupported=!1}dispose(){this.stop(),this.disposeTextures(),this.disposeFBO(),this.disposeJFA(),this.disposeStencilGeometry(),this.disposeBoundaryGeometry(),this.disposeChamferGeometry(),this.disposeGPUResources(),this.canvas.removeEventListener("webglcontextlost",this.handleContextLost),this.canvas.removeEventListener("webglcontextrestored",this.handleContextRestored),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),window.removeEventListener("resize",this.scheduleResizeRecalculate),this.resizeTimer!==null&&(window.clearTimeout(this.resizeTimer),this.resizeTimer=null)}initGPUResources(){const t=this.gl;if(!t)return;this.stencilProgram=O(t,L(t,t.VERTEX_SHADER,qt),L(t,t.FRAGMENT_SHADER,Zt)),this.stencilUniforms={uMeshScale:t.getUniformLocation(this.stencilProgram,"uMeshScale")},this.maskProgram=O(t,L(t,t.VERTEX_SHADER,$t),L(t,t.FRAGMENT_SHADER,Jt)),this.maskUniforms={uMeshScale:t.getUniformLocation(this.maskProgram,"uMeshScale")},this.jfaSeedProgram=O(t,L(t,t.VERTEX_SHADER,Kt),L(t,t.FRAGMENT_SHADER,Qt)),this.jfaSeedUniforms=this.getUniforms(this.jfaSeedProgram,["uMask","uTexelSize"]),this.jfaFloodProgram=O(t,L(t,t.VERTEX_SHADER,te),L(t,t.FRAGMENT_SHADER,ee)),this.jfaFloodUniforms=this.getUniforms(this.jfaFloodProgram,["uSeedTex","uStepSize"]),this.jfaDistProgram=O(t,L(t,t.VERTEX_SHADER,ie),L(t,t.FRAGMENT_SHADER,re)),this.jfaDistUniforms=this.getUniforms(this.jfaDistProgram,["uSeedTex","uMask","uBevelWidth"]),this.interiorProgram=O(t,L(t,t.VERTEX_SHADER,oe),L(t,t.FRAGMENT_SHADER,ne)),this.interiorUniforms=this.getUniforms(this.interiorProgram,["uImage","uDepth","uOffset","uStrength","uPomSteps","uDepthPower","uDepthScale","uDepthBias","uContrastLow","uContrastHigh","uVerticalReduction","uDofStart","uDofStrength","uImageTexelSize","uFogDensity","uFogColor","uColorShift","uBrightnessBias","uUvOffset","uUvScale"]),this.compositeProgram=O(t,L(t,t.VERTEX_SHADER,se),L(t,t.FRAGMENT_SHADER,ae)),this.compositeUniforms=this.getUniforms(this.compositeProgram,["uInteriorColor","uDistField","uEdgeOcclusionWidth","uEdgeOcclusionStrength"]),this.boundaryProgram=O(t,L(t,t.VERTEX_SHADER,he),L(t,t.FRAGMENT_SHADER,le)),this.boundaryUniforms=this.getUniforms(this.boundaryProgram,["uInteriorColor","uInteriorDepth","uDistField","uRimIntensity","uRimColor","uRimWidth","uMeshScale","uRefractionStrength","uChromaticStrength","uOcclusionIntensity","uTexelSize","uEdgeThickness","uEdgeSpecular","uEdgeColor","uLightDir","uBevelIntensity"]),this.chamferProgram=O(t,L(t,t.VERTEX_SHADER,ce),L(t,t.FRAGMENT_SHADER,ue)),this.chamferUniforms=this.getUniforms(this.chamferProgram,["uMeshScale","uLightDir3","uChamferColor","uChamferAmbient","uChamferSpecular","uChamferShininess","uInteriorColor","uTexelSize"]);const e=new Float32Array([-1,-1,1,-1,-1,1,1,1]);this.quadVao=t.createVertexArray(),t.bindVertexArray(this.quadVao);const i=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,i),t.bufferData(t.ARRAY_BUFFER,e,t.STATIC_DRAW);const r=t.getAttribLocation(this.interiorProgram,"aPosition");t.enableVertexAttribArray(r),t.vertexAttribPointer(r,2,t.FLOAT,!1,0,0),t.bindVertexArray(null),t.disable(t.DEPTH_TEST)}getUniforms(t,e){const i=this.gl,r={};for(const o of e)r[o]=i.getUniformLocation(t,o);return r}videoFrameLoop=(t,e)=>{const i=this.playbackVideo;if(!i)return;this.rvfcHandle=i.requestVideoFrameCallback(this.videoFrameLoop);const r=e.mediaTime??i.currentTime;this.updateDepthTexture(r),this.onVideoFrame&&this.onVideoFrame(r,e.presentedFrames??0)};renderLoop=()=>{this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop);const t=this.gl,e=this.playbackVideo;if(!t||!this.interiorProgram||!this.quadVao||!e||e.readyState<HTMLMediaElement.HAVE_CURRENT_DATA||!this.interiorFbo||!this.interiorColorTex||!this.interiorDepthTex)return;this.distFieldDirty&&this.maskVao&&this.distFbo&&(this.computeDistanceField(),t.viewport(0,0,this.canvas.width,this.canvas.height)),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this.videoTexture),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,e),this.rvfcSupported||this.updateDepthTexture(e.currentTime);let i=0,r=0;if(this.readInput){const o=this.readInput();i=-o.x,r=o.y}t.bindFramebuffer(t.FRAMEBUFFER,this.interiorFbo),t.viewport(0,0,this.fboWidth,this.fboHeight),t.clearColor(0,0,0,1),t.clear(t.COLOR_BUFFER_BIT),t.useProgram(this.interiorProgram),t.uniform2f(this.interiorUniforms.uOffset,i,r),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this.videoTexture),t.activeTexture(t.TEXTURE1),t.bindTexture(t.TEXTURE_2D,this.depthTexture),t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4),t.bindFramebuffer(t.FRAMEBUFFER,null),t.clearColor(0,0,0,0),t.viewport(0,0,this.canvas.width,this.canvas.height),t.clear(t.COLOR_BUFFER_BIT|t.STENCIL_BUFFER_BIT),this.stencilVao&&this.stencilProgram&&this.stencilIndexCount>0&&(t.enable(t.STENCIL_TEST),t.stencilFunc(t.ALWAYS,1,255),t.stencilOp(t.KEEP,t.KEEP,t.REPLACE),t.stencilMask(255),t.colorMask(!1,!1,!1,!1),t.useProgram(this.stencilProgram),t.bindVertexArray(this.stencilVao),t.drawElements(t.TRIANGLES,this.stencilIndexCount,t.UNSIGNED_SHORT,0),t.colorMask(!0,!0,!0,!0)),t.stencilFunc(t.EQUAL,1,255),t.stencilMask(0),t.activeTexture(t.TEXTURE2),t.bindTexture(t.TEXTURE_2D,this.interiorColorTex),t.activeTexture(t.TEXTURE3),t.bindTexture(t.TEXTURE_2D,this.interiorDepthTex),t.activeTexture(t.TEXTURE4),t.bindTexture(t.TEXTURE_2D,this.distTex),t.useProgram(this.compositeProgram),t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4),t.disable(t.STENCIL_TEST),this.chamferVao&&this.chamferProgram&&this.chamferVertexCount>0&&(t.useProgram(this.chamferProgram),t.uniform2f(this.chamferUniforms.uMeshScale,this.meshScaleX,this.meshScaleY),t.uniform2f(this.chamferUniforms.uTexelSize,1/this.canvas.width,1/this.canvas.height),t.bindVertexArray(this.chamferVao),t.drawArrays(t.TRIANGLES,0,this.chamferVertexCount)),this.boundaryVao&&this.boundaryProgram&&this.boundaryVertexCount>0&&this.config.rimLightIntensity>0&&(t.enable(t.BLEND),t.blendFunc(t.SRC_ALPHA,t.ONE_MINUS_SRC_ALPHA),t.useProgram(this.boundaryProgram),t.bindVertexArray(this.boundaryVao),t.drawArrays(t.TRIANGLES,0,this.boundaryVertexCount),t.disable(t.BLEND))};updateDepthTexture(t){const e=this.gl;if(!e||!this.readDepth||!this.depthTexture)return;const i=this.readDepth(t);e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.depthTexture),e.texSubImage2D(e.TEXTURE_2D,0,0,0,this.depthWidth,this.depthHeight,e.RED,e.UNSIGNED_BYTE,i)}setupResizeHandling(){typeof ResizeObserver<"u"&&(this.resizeObserver=new ResizeObserver(()=>{this.scheduleResizeRecalculate()}),this.resizeObserver.observe(this.container)),window.addEventListener("resize",this.scheduleResizeRecalculate),this.recalculateViewportLayout()}scheduleResizeRecalculate=()=>{this.resizeTimer!==null&&window.clearTimeout(this.resizeTimer),this.resizeTimer=window.setTimeout(()=>{this.resizeTimer=null,this.recalculateViewportLayout()},ft.RESIZE_DEBOUNCE_MS)};recalculateViewportLayout(){const t=this.gl;if(!t)return;const{width:e,height:i}=this.getViewportSize(),r=Math.min(window.devicePixelRatio,2),o=Math.round(e*r),s=Math.round(i*r);(this.canvas.width!==o||this.canvas.height!==s)&&(this.canvas.width=o,this.canvas.height=s,t.viewport(0,0,o,s)),(this.fboWidth!==o||this.fboHeight!==s)&&this.createFBO(o,s);const a=Math.max(1,Math.round(o/2)),h=Math.max(1,Math.round(s/2));(this.jfaWidth!==a||this.jfaHeight!==h)&&this.createJFAResources(o,s);const l=e/i,u=this.config.parallaxStrength+this.config.overscanPadding;let f=1,c=1;l>this.videoAspect?c=this.videoAspect/l:f=l/this.videoAspect;const d=1+u*2;f/=d,c/=d,this.uvOffset=[(1-f)/2,(1-c)/2],this.uvScale=[f,c],this.interiorProgram&&(t.useProgram(this.interiorProgram),t.uniform2f(this.interiorUniforms.uUvOffset,this.uvOffset[0],this.uvOffset[1]),t.uniform2f(this.interiorUniforms.uUvScale,this.uvScale[0],this.uvScale[1]));const m=.65;this.meshScaleX=m,this.meshScaleY=m,l>this.meshAspect?this.meshScaleX=m*(this.meshAspect/l):this.meshScaleY=m*(l/this.meshAspect),this.stencilProgram&&(t.useProgram(this.stencilProgram),t.uniform2f(this.stencilUniforms.uMeshScale,this.meshScaleX,this.meshScaleY)),this.boundaryProgram&&(t.useProgram(this.boundaryProgram),t.uniform2f(this.boundaryUniforms.uMeshScale,this.meshScaleX,this.meshScaleY),t.uniform1f(this.boundaryUniforms.uRimWidth,this.config.rimLightWidth),t.uniform2f(this.boundaryUniforms.uTexelSize,1/o,1/s)),this.chamferProgram&&(t.useProgram(this.chamferProgram),t.uniform2f(this.chamferUniforms.uMeshScale,this.meshScaleX,this.meshScaleY)),this.distFieldDirty=!0}getViewportSize(){const t=Math.max(1,Math.round(this.container.clientWidth||window.innerWidth)),e=Math.max(1,Math.round(this.container.clientHeight||window.innerHeight));return{width:t,height:e}}handleContextLost=t=>{t.preventDefault(),this.animationFrameHandle&&(window.cancelAnimationFrame(this.animationFrameHandle),this.animationFrameHandle=0)};handleContextRestored=()=>{const t=this.canvas.getContext("webgl2",{alpha:!0,premultipliedAlpha:!0,stencil:!0});t&&(this.gl=t,t.clearColor(0,0,0,0),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!0),this.initGPUResources(),this.playbackVideo&&(this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop)))};disposeTextures(){const t=this.gl;t&&(this.videoTexture&&(t.deleteTexture(this.videoTexture),this.videoTexture=null),this.depthTexture&&(t.deleteTexture(this.depthTexture),this.depthTexture=null))}disposeFBO(){const t=this.gl;t&&(this.interiorColorTex&&(t.deleteTexture(this.interiorColorTex),this.interiorColorTex=null),this.interiorDepthTex&&(t.deleteTexture(this.interiorDepthTex),this.interiorDepthTex=null),this.interiorFbo&&(t.deleteFramebuffer(this.interiorFbo),this.interiorFbo=null),this.fboWidth=0,this.fboHeight=0)}disposeJFA(){const t=this.gl;t&&(this.maskTex&&(t.deleteTexture(this.maskTex),this.maskTex=null),this.maskFbo&&(t.deleteFramebuffer(this.maskFbo),this.maskFbo=null),this.jfaPingTex&&(t.deleteTexture(this.jfaPingTex),this.jfaPingTex=null),this.jfaPingFbo&&(t.deleteFramebuffer(this.jfaPingFbo),this.jfaPingFbo=null),this.jfaPongTex&&(t.deleteTexture(this.jfaPongTex),this.jfaPongTex=null),this.jfaPongFbo&&(t.deleteFramebuffer(this.jfaPongFbo),this.jfaPongFbo=null),this.distTex&&(t.deleteTexture(this.distTex),this.distTex=null),this.distFbo&&(t.deleteFramebuffer(this.distFbo),this.distFbo=null),this.jfaWidth=0,this.jfaHeight=0,this.distFieldDirty=!0)}disposeStencilGeometry(){const t=this.gl;t&&(this.stencilVao&&(t.deleteVertexArray(this.stencilVao),this.stencilVao=null),this.maskVao&&(t.deleteVertexArray(this.maskVao),this.maskVao=null),this.stencilIndexCount=0)}disposeBoundaryGeometry(){const t=this.gl;t&&(this.boundaryVao&&(t.deleteVertexArray(this.boundaryVao),this.boundaryVao=null),this.boundaryVertexCount=0)}disposeGPUResources(){const t=this.gl;t&&(this.stencilProgram&&(t.deleteProgram(this.stencilProgram),this.stencilProgram=null),this.maskProgram&&(t.deleteProgram(this.maskProgram),this.maskProgram=null),this.jfaSeedProgram&&(t.deleteProgram(this.jfaSeedProgram),this.jfaSeedProgram=null),this.jfaFloodProgram&&(t.deleteProgram(this.jfaFloodProgram),this.jfaFloodProgram=null),this.jfaDistProgram&&(t.deleteProgram(this.jfaDistProgram),this.jfaDistProgram=null),this.interiorProgram&&(t.deleteProgram(this.interiorProgram),this.interiorProgram=null),this.compositeProgram&&(t.deleteProgram(this.compositeProgram),this.compositeProgram=null),this.boundaryProgram&&(t.deleteProgram(this.boundaryProgram),this.boundaryProgram=null),this.chamferProgram&&(t.deleteProgram(this.chamferProgram),this.chamferProgram=null),this.quadVao&&(t.deleteVertexArray(this.quadVao),this.quadVao=null),this.stencilUniforms={},this.maskUniforms={},this.jfaSeedUniforms={},this.jfaFloodUniforms={},this.jfaDistUniforms={},this.interiorUniforms={},this.compositeUniforms={},this.boundaryUniforms={},this.chamferUniforms={})}}async function me(n){const t=await fetch(n);if(!t.ok)throw new Error(`Failed to fetch SVG: ${t.status} ${t.statusText}`);const e=await t.text();return pe(e)}function pe(n){const i=new DOMParser().parseFromString(n,"image/svg+xml").querySelector("svg");if(!i)throw new Error("No <svg> element found in document.");const r=ge(i);if(r.length===0)throw new Error("No path data found in SVG.");let o=1/0,s=1/0,a=-1/0,h=-1/0;for(const D of r)for(let R=0;R<D.length;R+=2)o=Math.min(o,D[R]),a=Math.max(a,D[R]),s=Math.min(s,D[R+1]),h=Math.max(h,D[R+1]);const l=a-o,u=h-s,f=(o+a)/2,c=(s+h)/2,d=2/Math.max(l,u),m=l/u,g=r.map(D=>{const R=[];for(let C=0;C<D.length;C+=2)R.push((D[C]-f)*d),R.push(-((D[C+1]-c)*d));return R}),U=Se(g),y=[],E=[];for(const D of U){const{flatCoords:R,holeIndices:C}=Ae(D),M=Re(R,C),H=y.length/2;for(const I of M)E.push(I+H);for(const I of R)y.push(I)}const p=y,v=E,T=[],x=[],A=[],S=At(g);for(let D=0;D<g.length;D++){const R=g[D];x.push(T.length),A.push(S[D]);for(let C=0;C<R.length;C++)T.push(R[C]);R.length>=2&&T.push(R[0],R[1])}let F=1/0,P=1/0,_=-1/0,k=-1/0;for(let D=0;D<p.length;D+=2)F=Math.min(F,p[D]),_=Math.max(_,p[D]),P=Math.min(P,p[D+1]),k=Math.max(k,p[D+1]);return{vertices:new Float32Array(p),indices:new Uint16Array(v),edgeVertices:new Float32Array(T),contourOffsets:x,contourIsHole:A,bounds:{minX:F,maxX:_,minY:P,maxY:k},aspect:m}}function ge(n){const t=[];return n.querySelectorAll("path").forEach(h=>{const l=h.getAttribute("d");if(!l)return;const u=Te(l);t.push(...u)}),n.querySelectorAll("polygon").forEach(h=>{const l=h.getAttribute("points");if(!l)return;const u=Et(l);u.length>=6&&t.push(u)}),n.querySelectorAll("polyline").forEach(h=>{const l=h.getAttribute("points");if(!l)return;const u=Et(l);u.length>=6&&t.push(u)}),n.querySelectorAll("rect").forEach(h=>{const l=parseFloat(h.getAttribute("x")||"0"),u=parseFloat(h.getAttribute("y")||"0"),f=parseFloat(h.getAttribute("width")||"0"),c=parseFloat(h.getAttribute("height")||"0");f>0&&c>0&&t.push([l,u,l+f,u,l+f,u+c,l,u+c])}),n.querySelectorAll("circle").forEach(h=>{const l=parseFloat(h.getAttribute("cx")||"0"),u=parseFloat(h.getAttribute("cy")||"0"),f=parseFloat(h.getAttribute("r")||"0");f>0&&t.push(ve(l,u,f))}),n.querySelectorAll("ellipse").forEach(h=>{const l=parseFloat(h.getAttribute("cx")||"0"),u=parseFloat(h.getAttribute("cy")||"0"),f=parseFloat(h.getAttribute("rx")||"0"),c=parseFloat(h.getAttribute("ry")||"0");f>0&&c>0&&t.push(xe(l,u,f,c))}),t}function Et(n){const t=[],e=n.trim().split(/[\s,]+/);for(let i=0;i<e.length-1;i+=2){const r=parseFloat(e[i]),o=parseFloat(e[i+1]);Number.isFinite(r)&&Number.isFinite(o)&&t.push(r,o)}return t}function ve(n,t,e,i=64){const r=[];for(let o=0;o<i;o++){const s=2*Math.PI*o/i;r.push(n+e*Math.cos(s),t+e*Math.sin(s))}return r}function xe(n,t,e,i,r=64){const o=[];for(let s=0;s<r;s++){const a=2*Math.PI*s/r;o.push(n+e*Math.cos(a),t+i*Math.sin(a))}return o}function Te(n){const t=[];let e=[],i=0,r=0,o=0,s=0,a=0,h=0,l="";const u=Ee(n);let f=0;function c(){return f>=u.length?0:parseFloat(u[f++])}for(;f<u.length;){const d=u[f];let m;/^[a-zA-Z]$/.test(d)?(m=d,f++):m=l==="M"?"L":l==="m"?"l":l;const g=m===m.toLowerCase();switch(m.toUpperCase()){case"M":{e.length>0&&t.push(e),e=[];const y=c()+(g?i:0),E=c()+(g?r:0);i=y,r=E,o=y,s=E,e.push(i,r),a=i,h=r;break}case"L":{i=c()+(g?i:0),r=c()+(g?r:0),e.push(i,r),a=i,h=r;break}case"H":{i=c()+(g?i:0),e.push(i,r),a=i,h=r;break}case"V":{r=c()+(g?r:0),e.push(i,r),a=i,h=r;break}case"C":{const y=c()+(g?i:0),E=c()+(g?r:0),p=c()+(g?i:0),v=c()+(g?r:0),T=c()+(g?i:0),x=c()+(g?r:0);$(e,i,r,y,E,p,v,T,x),i=T,r=x,a=p,h=v;break}case"S":{const y=2*i-a,E=2*r-h,p=c()+(g?i:0),v=c()+(g?r:0),T=c()+(g?i:0),x=c()+(g?r:0);$(e,i,r,y,E,p,v,T,x),i=T,r=x,a=p,h=v;break}case"Q":{const y=c()+(g?i:0),E=c()+(g?r:0),p=c()+(g?i:0),v=c()+(g?r:0);bt(e,i,r,y,E,p,v),i=p,r=v,a=y,h=E;break}case"T":{const y=2*i-a,E=2*r-h,p=c()+(g?i:0),v=c()+(g?r:0);bt(e,i,r,y,E,p,v),i=p,r=v,a=y,h=E;break}case"A":{const y=c(),E=c(),p=c(),v=c(),T=c(),x=c()+(g?i:0),A=c()+(g?r:0);ye(e,i,r,y,E,p,!!v,!!T,x,A),i=x,r=A,a=i,h=r;break}case"Z":{i=o,r=s,e.length>0&&t.push(e),e=[],a=i,h=r;break}default:f++;break}l=m}return e.length>=6&&t.push(e),t}function Ee(n){const t=[],e=/([a-zA-Z])|([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)/g;let i;for(;(i=e.exec(n))!==null;)t.push(i[0]);return t}const be=.5;function $(n,t,e,i,r,o,s,a,h,l=0){if(l>12){n.push(a,h);return}const u=a-t,f=h-e,c=Math.sqrt(u*u+f*f);if(c<1e-6){n.push(a,h);return}const d=Math.abs((i-a)*f-(r-h)*u)/c,m=Math.abs((o-a)*f-(s-h)*u)/c;if(d+m<be){n.push(a,h);return}const g=(t+i)/2,U=(e+r)/2,y=(i+o)/2,E=(r+s)/2,p=(o+a)/2,v=(s+h)/2,T=(g+y)/2,x=(U+E)/2,A=(y+p)/2,S=(E+v)/2,F=(T+A)/2,P=(x+S)/2;$(n,t,e,g,U,T,x,F,P,l+1),$(n,F,P,A,S,p,v,a,h,l+1)}function bt(n,t,e,i,r,o,s){const a=t+.6666666666666666*(i-t),h=e+2/3*(r-e),l=o+2/3*(i-o),u=s+2/3*(r-s);$(n,t,e,a,h,l,u,o,s)}function ye(n,t,e,i,r,o,s,a,h,l){if(i===0||r===0){n.push(h,l);return}let u=Math.abs(i),f=Math.abs(r);const c=o*Math.PI/180,d=Math.cos(c),m=Math.sin(c),g=(t-h)/2,U=(e-l)/2,y=d*g+m*U,E=-m*g+d*U;let p=y*y/(u*u)+E*E/(f*f);if(p>1){const M=Math.sqrt(p);u*=M,f*=M,p=1}const v=u*u,T=f*f,x=y*y,A=E*E;let S=Math.max(0,(v*T-v*A-T*x)/(v*A+T*x));S=Math.sqrt(S),s===a&&(S=-S);const F=S*(u*E)/f,P=S*-(f*y)/u,_=d*F-m*P+(t+h)/2,k=m*F+d*P+(e+l)/2,D=yt(1,0,(y-F)/u,(E-P)/f);let R=yt((y-F)/u,(E-P)/f,(-y-F)/u,(-E-P)/f);!a&&R>0&&(R-=2*Math.PI),a&&R<0&&(R+=2*Math.PI);const C=Math.max(4,Math.ceil(Math.abs(R)/(Math.PI/16)));for(let M=1;M<=C;M++){const H=D+M/C*R,I=Math.cos(H),G=Math.sin(H),tt=d*u*I-m*f*G+_,ct=m*u*I+d*f*G+k;n.push(tt,ct)}}function yt(n,t,e,i){const r=n*i-t*e<0?-1:1,o=n*e+t*i,s=Math.sqrt(n*n+t*t),a=Math.sqrt(e*e+i*i),h=o/(s*a);return r*Math.acos(Math.max(-1,Math.min(1,h)))}function Ae(n){const t=[],e=[];for(let i=0;i<n.length;i++){i>0&&e.push(t.length/2);for(const r of n[i])t.push(r)}return{flatCoords:t,holeIndices:e}}function At(n){const t=n.length,e=n.map(r=>Math.abs(St(r))),i=new Array(t).fill(!1);for(let r=0;r<t;r++){let o=0;const s=n[r][0],a=n[r][1];for(let h=0;h<t;h++)r!==h&&e[h]>e[r]&&Rt(s,a,n[h])&&o++;i[r]=o%2===1}return i}function Se(n){if(n.length<=1)return[n];const t=At(n),e=n.map((s,a)=>{const h=St(s);return{index:a,contour:s,area:h,isOuter:!t[a]}}),i=e.filter(s=>s.isOuter),r=e.filter(s=>!s.isOuter);if(i.length===0)return n.map(s=>[s]);const o=i.map(s=>({outer:s.contour,holes:[]}));for(const s of r){const a=s.contour[0],h=s.contour[1];let l=-1,u=1/0;for(let f=0;f<i.length;f++)if(Rt(a,h,i[f].contour)){const c=Math.abs(i[f].area);c<u&&(u=c,l=f)}l>=0?o[l].holes.push(s.contour):o.push({outer:s.contour,holes:[]})}return o.map(s=>[s.outer,...s.holes])}function St(n){let t=0;const e=n.length;for(let i=0;i<e;i+=2){const r=n[i],o=n[i+1],s=n[(i+2)%e],a=n[(i+3)%e];t+=r*a-s*o}return t/2}function Rt(n,t,e){let i=!1;const r=e.length;for(let o=0,s=r-2;o<r;s=o,o+=2){const a=e[o],h=e[o+1],l=e[s],u=e[s+1];h>t!=u>t&&n<(l-a)*(t-h)/(u-h)+a&&(i=!i)}return i}function Re(n,t,e=2){const i=t&&t.length>0,r=i?t[0]*e:n.length;let o=Dt(n,0,r,e,!0);const s=[];if(!o||o.next===o.prev)return s;i&&(o=we(n,t,o,e));let a=1/0,h=1/0,l=-1/0,u=-1/0,f=0;if(n.length>80*e){for(let c=0;c<r;c+=e){const d=n[c],m=n[c+1];d<a&&(a=d),m<h&&(h=m),d>l&&(l=d),m>u&&(u=m)}f=Math.max(l-a,u-h),f=f!==0?32767/f:0}return J(o,s,e,a,h,f,0),s}function Dt(n,t,e,i,r){let o=null;if(r===Be(n,t,e,i)>0)for(let s=t;s<e;s+=i)o=Pt(s,n[s],n[s+1],o);else for(let s=e-i;s>=t;s-=i)o=Pt(s,n[s],n[s+1],o);return o&&nt(o,o.next)&&(Q(o),o=o.next),o?(o.next.prev=o,o.prev.next=o,o.next):null}function X(n,t){t||(t=n);let e=n,i;do if(i=!1,!e.steiner&&(nt(e,e.next)||w(e.prev,e,e.next)===0)){if(Q(e),e=t=e.prev,e===e.next)break;i=!0}else e=e.next;while(i||e!==t);return t}function J(n,t,e,i,r,o,s){if(!n)return;!s&&o&&_e(n,i,r,o);let a=n,h,l;for(;n.prev!==n.next;){if(h=n.prev,l=n.next,o?Fe(n,i,r,o):De(n)){t.push(h.i/e,n.i/e,l.i/e),Q(n),n=l.next,a=l.next;continue}if(n=l,n===a){s?s===1?(n=Ue(X(n),t,e),J(n,t,e,i,r,o,2)):s===2&&Pe(n,t,e,i,r,o):J(X(n),t,e,i,r,o,1);break}}}function De(n){const t=n.prev,e=n,i=n.next;if(w(t,e,i)>=0)return!1;const r=t.x,o=e.x,s=i.x,a=t.y,h=e.y,l=i.y,u=r<o?r<s?r:s:o<s?o:s,f=a<h?a<l?a:l:h<l?h:l,c=r>o?r>s?r:s:o>s?o:s,d=a>h?a>l?a:l:h>l?h:l;let m=i.next;for(;m!==t;){if(m.x>=u&&m.x<=c&&m.y>=f&&m.y<=d&&z(r,a,o,h,s,l,m.x,m.y)&&w(m.prev,m,m.next)>=0)return!1;m=m.next}return!0}function Fe(n,t,e,i){const r=n.prev,o=n,s=n.next;if(w(r,o,s)>=0)return!1;const a=r.x,h=o.x,l=s.x,u=r.y,f=o.y,c=s.y,d=a<h?a<l?a:l:h<l?h:l,m=u<f?u<c?u:c:f<c?f:c,g=a>h?a>l?a:l:h>l?h:l,U=u>f?u>c?u:c:f>c?f:c,y=dt(d,m,t,e,i),E=dt(g,U,t,e,i);let p=n.prevZ,v=n.nextZ;for(;p&&p.z>=y&&v&&v.z<=E;){if(p.x>=d&&p.x<=g&&p.y>=m&&p.y<=U&&p!==r&&p!==s&&z(a,u,h,f,l,c,p.x,p.y)&&w(p.prev,p,p.next)>=0||(p=p.prevZ,v.x>=d&&v.x<=g&&v.y>=m&&v.y<=U&&v!==r&&v!==s&&z(a,u,h,f,l,c,v.x,v.y)&&w(v.prev,v,v.next)>=0))return!1;v=v.nextZ}for(;p&&p.z>=y;){if(p.x>=d&&p.x<=g&&p.y>=m&&p.y<=U&&p!==r&&p!==s&&z(a,u,h,f,l,c,p.x,p.y)&&w(p.prev,p,p.next)>=0)return!1;p=p.prevZ}for(;v&&v.z<=E;){if(v.x>=d&&v.x<=g&&v.y>=m&&v.y<=U&&v!==r&&v!==s&&z(a,u,h,f,l,c,v.x,v.y)&&w(v.prev,v,v.next)>=0)return!1;v=v.nextZ}return!0}function Ue(n,t,e){let i=n;do{const r=i.prev,o=i.next.next;!nt(r,o)&&Ft(r,i,i.next,o)&&K(r,o)&&K(o,r)&&(t.push(r.i/e,i.i/e,o.i/e),Q(i),Q(i.next),i=n=o),i=i.next}while(i!==n);return X(i)}function Pe(n,t,e,i,r,o){let s=n;do{let a=s.next.next;for(;a!==s.prev;){if(s.i!==a.i&&Oe(s,a)){let h=Ut(s,a);s=X(s,s.next),h=X(h,h.next),J(s,t,e,i,r,o,0),J(h,t,e,i,r,o,0);return}a=a.next}s=s.next}while(s!==n)}function we(n,t,e,i){const r=[];for(let o=0;o<t.length;o++){const s=t[o]*i,a=o<t.length-1?t[o+1]*i:n.length,h=Dt(n,s,a,i,!1);h&&(h===h.next&&(h.steiner=!0),r.push(Ve(h)))}r.sort((o,s)=>o.x-s.x);for(const o of r)e=Le(o,e);return e}function Le(n,t){const e=Ce(n,t);if(!e)return t;const i=Ut(e,n);return X(i,i.next),X(e,e.next)}function Ce(n,t){let e=t;const i=n.x,r=n.y;let o=-1/0,s=null;do{if(r<=e.y&&r>=e.next.y&&e.next.y!==e.y){const f=e.x+(r-e.y)/(e.next.y-e.y)*(e.next.x-e.x);if(f<=i&&f>o&&(o=f,s=e.x<e.next.x?e:e.next,f===i))return s}e=e.next}while(e!==t);if(!s)return null;const a=s,h=s.x,l=s.y;let u=1/0;e=s;do{if(i>=e.x&&e.x>=h&&i!==e.x&&z(r<l?i:o,r,h,l,r<l?o:i,r,e.x,e.y)){const f=Math.abs(r-e.y)/(i-e.x);K(e,n)&&(f<u||f===u&&(e.x>s.x||Me(s,e)))&&(s=e,u=f)}e=e.next}while(e!==a);return s}function Me(n,t){return w(n.prev,n,t.prev)<0&&w(t.next,n,n.next)<0}function _e(n,t,e,i){let r=n;do r.z===0&&(r.z=dt(r.x,r.y,t,e,i)),r.prevZ=r.prev,r.nextZ=r.next,r=r.next;while(r!==n);r.prevZ.nextZ=null,r.prevZ=null,Ie(r)}function Ie(n){let t=1,e;do{let i=n;n=null;let r=null;for(e=0;i;){e++;let o=i,s=0;for(let h=0;h<t&&(s++,o=o.nextZ,!!o);h++);let a=t;for(;s>0||a>0&&o;){let h;s!==0&&(a===0||!o||i.z<=o.z)?(h=i,i=i.nextZ,s--):(h=o,o=o.nextZ,a--),r?r.nextZ=h:n=h,h.prevZ=r,r=h}i=o}r.nextZ=null,t*=2}while(e>1);return n}function dt(n,t,e,i,r){let o=(n-e)*r|0,s=(t-i)*r|0;return o=(o|o<<8)&16711935,o=(o|o<<4)&252645135,o=(o|o<<2)&858993459,o=(o|o<<1)&1431655765,s=(s|s<<8)&16711935,s=(s|s<<4)&252645135,s=(s|s<<2)&858993459,s=(s|s<<1)&1431655765,o|s<<1}function Ve(n){let t=n,e=n;do(t.x<e.x||t.x===e.x&&t.y<e.y)&&(e=t),t=t.next;while(t!==n);return e}function z(n,t,e,i,r,o,s,a){return(r-s)*(t-a)-(n-s)*(o-a)>=0&&(n-s)*(i-a)-(e-s)*(t-a)>=0&&(e-s)*(o-a)-(r-s)*(i-a)>=0}function Oe(n,t){return n.next.i!==t.i&&n.prev.i!==t.i&&!ke(n,t)&&(K(n,t)&&K(t,n)&&He(n,t)&&(w(n.prev,n,t.prev)!==0||w(n,t.prev,t)!==0)||nt(n,t)&&w(n.prev,n,n.next)>0&&w(t.prev,t,t.next)>0)}function w(n,t,e){return(t.y-n.y)*(e.x-t.x)-(t.x-n.x)*(e.y-t.y)}function nt(n,t){return n.x===t.x&&n.y===t.y}function Ft(n,t,e,i){const r=at(w(n,t,e)),o=at(w(n,t,i)),s=at(w(e,i,n)),a=at(w(e,i,t));return!!(r!==o&&s!==a||r===0&&st(n,e,t)||o===0&&st(n,i,t)||s===0&&st(e,n,i)||a===0&&st(e,t,i))}function st(n,t,e){return t.x<=Math.max(n.x,e.x)&&t.x>=Math.min(n.x,e.x)&&t.y<=Math.max(n.y,e.y)&&t.y>=Math.min(n.y,e.y)}function at(n){return n>0?1:n<0?-1:0}function ke(n,t){let e=n;do{if(e.i!==n.i&&e.next.i!==n.i&&e.i!==t.i&&e.next.i!==t.i&&Ft(e,e.next,n,t))return!0;e=e.next}while(e!==n);return!1}function K(n,t){return w(n.prev,n,n.next)<0?w(n,t,n.next)>=0&&w(n,n.prev,t)>=0:w(n,t,n.prev)<0||w(n,n.next,t)<0}function He(n,t){let e=n,i=!1;const r=(n.x+t.x)/2,o=(n.y+t.y)/2;do e.y>o!=e.next.y>o&&e.next.y!==e.y&&r<(e.next.x-e.x)*(o-e.y)/(e.next.y-e.y)+e.x&&(i=!i),e=e.next;while(e!==n);return i}function Ut(n,t){const e=mt(n.i,n.x,n.y),i=mt(t.i,t.x,t.y),r=n.next,o=t.prev;return n.next=t,t.prev=n,e.next=r,r.prev=e,i.next=e,e.prev=i,o.next=i,i.prev=o,i}function Pt(n,t,e,i){const r=mt(n,t,e);return i?(r.next=i.next,r.prev=i,i.next.prev=r,i.next=r):(r.prev=r,r.next=r),r}function Q(n){n.next.prev=n.prev,n.prev.next=n.next,n.prevZ&&(n.prevZ.nextZ=n.nextZ),n.nextZ&&(n.nextZ.prevZ=n.prevZ)}function mt(n,t,e){return{i:n,x:t,y:e,prev:null,next:null,z:0,prevZ:null,nextZ:null,steiner:!1}}function Be(n,t,e,i){let r=0;for(let o=t,s=e-i;o<e;o+=i)r+=(n[s]-n[o])*(n[o+1]+n[s+1]),s=o;return r}const b={parallaxX:.4,parallaxY:.8,parallaxMax:30,overscan:.06,pomSteps:16,rimIntensity:.6,rimColor:"#ffffff",rimWidth:.025,refractionStrength:.015,chromaticStrength:.008,occlusionIntensity:.4,depthPower:.7,depthScale:1.2,depthBias:-.05,fogDensity:.15,fogColor:"#1a1a2e",colorShift:.6,brightnessBias:.05,contrastLow:.02,contrastHigh:.98,verticalReduction:.5,dofStart:.5,dofStrength:.5,bevelIntensity:.5,bevelWidth:.04,bevelDarkening:.2,bevelDesaturation:.12,bevelLightAngle:135,edgeThickness:.01,edgeSpecular:.35,edgeColor:"#a0a0a0",chamferWidth:.025,chamferAngle:45,chamferColor:"#262630",chamferAmbient:.12,chamferSpecular:.3,chamferShininess:24,edgeOcclusionWidth:.03,edgeOcclusionStrength:.2,lightDirection:"-0.5,0.7,-0.3",autoplay:!0,loop:!0,muted:!0};class pt{constructor(t,e=.08,i=.06){this.host=t,this.lerpFactor=e,this.motionLerpFactor=i,this.host.addEventListener("mousemove",this.handleMouseMove),this.host.addEventListener("mouseleave",this.resetPointerTarget),this.host.addEventListener("touchstart",this.handleTouchStart,{passive:!0}),this.host.addEventListener("touchmove",this.handleTouchMove,{passive:!0}),this.host.addEventListener("touchend",this.handleTouchEnd,{passive:!0}),this.host.addEventListener("touchcancel",this.handleTouchEnd,{passive:!0})}pointerTarget={x:0,y:0};motionTarget={x:0,y:0};smoothedOutput={x:0,y:0};usingMotionInput=!1;motionListenerAttached=!1;motionRequested=!1;touchActive=!1;touchAnchorX=0;touchAnchorY=0;lerpFactor;motionLerpFactor;static TOUCH_DRAG_RANGE=100;update(){const t=this.touchActive?this.pointerTarget:this.usingMotionInput?this.motionTarget:this.pointerTarget,e=this.usingMotionInput&&!this.touchActive?this.motionLerpFactor:this.lerpFactor;return this.smoothedOutput.x=lt(this.smoothedOutput.x,t.x,e),this.smoothedOutput.y=lt(this.smoothedOutput.y,t.y,e),this.smoothedOutput}dispose(){this.host.removeEventListener("mousemove",this.handleMouseMove),this.host.removeEventListener("mouseleave",this.resetPointerTarget),this.host.removeEventListener("touchstart",this.handleTouchStart),this.host.removeEventListener("touchmove",this.handleTouchMove),this.host.removeEventListener("touchend",this.handleTouchEnd),this.host.removeEventListener("touchcancel",this.handleTouchEnd),this.motionListenerAttached&&(window.removeEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!1)}handleMouseMove=t=>{const e=this.host.getBoundingClientRect(),i=(t.clientX-e.left)/e.width*2-1,r=(t.clientY-e.top)/e.height*2-1;this.pointerTarget.x=W(i,-1,1),this.pointerTarget.y=W(r,-1,1)};resetPointerTarget=()=>{this.pointerTarget.x=0,this.pointerTarget.y=0};handleTouchStart=t=>{const e=t.touches[0];e&&(this.touchActive=!0,this.touchAnchorX=e.clientX,this.touchAnchorY=e.clientY,this.pointerTarget.x=0,this.pointerTarget.y=0,this.motionRequested||(this.motionRequested=!0,this.requestMotionPermission()))};handleTouchMove=t=>{const e=t.touches[0];if(!e)return;const i=e.clientX-this.touchAnchorX,r=e.clientY-this.touchAnchorY,o=pt.TOUCH_DRAG_RANGE;this.pointerTarget.x=W(i/o,-1,1),this.pointerTarget.y=W(r/o,-1,1)};handleTouchEnd=()=>{this.touchActive=!1,this.pointerTarget.x=0,this.pointerTarget.y=0};async requestMotionPermission(){if(typeof DeviceOrientationEvent>"u")return;const t=DeviceOrientationEvent;if(typeof t.requestPermission=="function")try{if(await t.requestPermission()!=="granted")return}catch{return}this.motionListenerAttached||(window.addEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!0),this.usingMotionInput=!0}handleDeviceOrientation=t=>{const e=W((t.gamma??0)/45,-1,1),i=W((t.beta??0)/45,-1,1);this.motionTarget.x=lt(this.motionTarget.x,e,this.motionLerpFactor),this.motionTarget.y=lt(this.motionTarget.y,i,this.motionLerpFactor)}}class ht extends HTMLElement{static TAG_NAME="layershift-portal";static get observedAttributes(){return["src","depth-src","depth-meta","logo-src","parallax-x","parallax-y","parallax-max","overscan","pom-steps","rim-intensity","rim-color","rim-width","refraction-strength","chromatic-strength","occlusion-intensity","depth-power","depth-scale","depth-bias","fog-density","fog-color","color-shift","brightness-bias","contrast-low","contrast-high","vertical-reduction","dof-start","dof-strength","bevel-intensity","bevel-width","bevel-darkening","bevel-desaturation","bevel-light-angle","edge-thickness","edge-specular","edge-color","chamfer-width","chamfer-angle","chamfer-color","chamfer-ambient","chamfer-specular","chamfer-shininess","edge-occlusion-width","edge-occlusion-strength","light-direction","autoplay","loop","muted"]}shadow;container=null;renderer=null;inputHandler=null;depthWorker=null;video=null;mesh=null;initialized=!1;abortController=null;loopCount=0;constructor(){super(),this.shadow=this.attachShadow({mode:"open"})}getAttrFloat(t,e){const i=this.getAttribute(t);if(i===null)return e;const r=parseFloat(i);return Number.isFinite(r)?r:e}getAttrBool(t,e){if(!this.hasAttribute(t))return e;const i=this.getAttribute(t);return!(i==="false"||i==="0")}getAttrColor(t,e){const i=this.getAttribute(t)??e;return Xe(i)}getAttrVec3(t,e){const r=(this.getAttribute(t)??e).split(",").map(s=>parseFloat(s.trim()));if(r.length>=3&&r.every(Number.isFinite))return[r[0],r[1],r[2]];const o=e.split(",").map(s=>parseFloat(s.trim()));return[o[0],o[1],o[2]]}get parallaxX(){return this.getAttrFloat("parallax-x",b.parallaxX)}get parallaxY(){return this.getAttrFloat("parallax-y",b.parallaxY)}get parallaxMax(){return this.getAttrFloat("parallax-max",b.parallaxMax)}get overscan(){return this.getAttrFloat("overscan",b.overscan)}get pomSteps(){return this.getAttrFloat("pom-steps",b.pomSteps)}get rimIntensity(){return this.getAttrFloat("rim-intensity",b.rimIntensity)}get rimWidth(){return this.getAttrFloat("rim-width",b.rimWidth)}get rimColor(){return this.getAttrColor("rim-color",b.rimColor)}get refractionStrength(){return this.getAttrFloat("refraction-strength",b.refractionStrength)}get chromaticStrength(){return this.getAttrFloat("chromatic-strength",b.chromaticStrength)}get occlusionIntensity(){return this.getAttrFloat("occlusion-intensity",b.occlusionIntensity)}get depthPower(){return this.getAttrFloat("depth-power",b.depthPower)}get depthScale(){return this.getAttrFloat("depth-scale",b.depthScale)}get depthBias(){return this.getAttrFloat("depth-bias",b.depthBias)}get fogDensity(){return this.getAttrFloat("fog-density",b.fogDensity)}get fogColor(){return this.getAttrColor("fog-color",b.fogColor)}get colorShift(){return this.getAttrFloat("color-shift",b.colorShift)}get brightnessBias(){return this.getAttrFloat("brightness-bias",b.brightnessBias)}get contrastLow(){return this.getAttrFloat("contrast-low",b.contrastLow)}get contrastHigh(){return this.getAttrFloat("contrast-high",b.contrastHigh)}get verticalReduction(){return this.getAttrFloat("vertical-reduction",b.verticalReduction)}get dofStart(){return this.getAttrFloat("dof-start",b.dofStart)}get dofStrength(){return this.getAttrFloat("dof-strength",b.dofStrength)}get bevelIntensity(){return this.getAttrFloat("bevel-intensity",b.bevelIntensity)}get bevelWidth(){return this.getAttrFloat("bevel-width",b.bevelWidth)}get bevelDarkening(){return this.getAttrFloat("bevel-darkening",b.bevelDarkening)}get bevelDesaturation(){return this.getAttrFloat("bevel-desaturation",b.bevelDesaturation)}get bevelLightAngle(){return this.getAttrFloat("bevel-light-angle",b.bevelLightAngle)}get edgeThickness(){return this.getAttrFloat("edge-thickness",b.edgeThickness)}get edgeSpecular(){return this.getAttrFloat("edge-specular",b.edgeSpecular)}get edgeColor(){return this.getAttrColor("edge-color",b.edgeColor)}get chamferWidth(){return this.getAttrFloat("chamfer-width",b.chamferWidth)}get chamferAngle(){return this.getAttrFloat("chamfer-angle",b.chamferAngle)}get chamferColor(){return this.getAttrColor("chamfer-color",b.chamferColor)}get chamferAmbient(){return this.getAttrFloat("chamfer-ambient",b.chamferAmbient)}get chamferSpecular(){return this.getAttrFloat("chamfer-specular",b.chamferSpecular)}get chamferShininess(){return this.getAttrFloat("chamfer-shininess",b.chamferShininess)}get edgeOcclusionWidth(){return this.getAttrFloat("edge-occlusion-width",b.edgeOcclusionWidth)}get edgeOcclusionStrength(){return this.getAttrFloat("edge-occlusion-strength",b.edgeOcclusionStrength)}get lightDirection3(){return this.getAttrVec3("light-direction",b.lightDirection)}get shouldAutoplay(){return this.getAttrBool("autoplay",b.autoplay)}get shouldLoop(){return this.getAttrBool("loop",b.loop)}get shouldMute(){return this.getAttrBool("muted",b.muted)}emit(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}attachVideoEventListeners(t){t.addEventListener("play",()=>{this.emit("layershift-portal:play",{currentTime:t.currentTime})}),t.addEventListener("pause",()=>{this.emit("layershift-portal:pause",{currentTime:t.currentTime})}),t.addEventListener("ended",()=>{t.loop&&(this.loopCount+=1,this.emit("layershift-portal:loop",{loopCount:this.loopCount}))})}connectedCallback(){this.setupShadowDOM(),this.init()}disconnectedCallback(){this.dispose()}attributeChangedCallback(t,e,i){["src","depth-src","depth-meta","logo-src"].includes(t)&&(this.initialized?(this.dispose(),this.setupShadowDOM(),this.init()):this.isConnected&&this.getAttribute("src")&&this.getAttribute("depth-src")&&this.getAttribute("depth-meta")&&this.getAttribute("logo-src")&&this.init())}setupShadowDOM(){this.shadow.innerHTML="";const t=document.createElement("style");t.textContent=`
724
+ :host {
725
+ display: block;
726
+ width: 100%;
727
+ height: 100%;
728
+ position: relative;
729
+ overflow: hidden;
730
+ background: transparent;
731
+ }
732
+ .container {
733
+ width: 100%;
734
+ height: 100%;
735
+ position: absolute;
736
+ inset: 0;
737
+ }
738
+ canvas {
739
+ display: block;
740
+ width: 100%;
741
+ height: 100%;
742
+ }
743
+ `,this.shadow.appendChild(t),this.container=document.createElement("div"),this.container.className="container",this.shadow.appendChild(this.container)}async init(){const t=this.getAttribute("src"),e=this.getAttribute("depth-src"),i=this.getAttribute("depth-meta"),r=this.getAttribute("logo-src");if(!t||!e||!i||!r){const o="src, depth-src, depth-meta, and logo-src attributes are required.";console.warn(`<layershift-portal>: ${o}`),this.emit("layershift-portal:error",{message:o});return}if(this.container){this.abortController=new AbortController;try{const[o,s,a]=await Promise.all([this.createVideoElement(t),vt(e,i),me(r)]);if(this.abortController.signal.aborted){o.remove();return}this.video=o,this.mesh=a,this.loopCount=0,this.attachVideoEventListeners(o);const h=this.parallaxMax/Math.max(o.videoWidth,1);let l;try{const d=await it.create(s,s.meta.width,s.meta.height);this.depthWorker=d,l=m=>d.sample(m)}catch{const d=new gt(s,s.meta.width,s.meta.height);l=m=>d.sample(m)}if(this.abortController.signal.aborted){o.remove(),this.depthWorker?.dispose(),this.depthWorker=null;return}const u={parallaxStrength:h,overscanPadding:this.overscan,pomSteps:this.pomSteps,rimLightIntensity:this.rimIntensity,rimLightColor:this.rimColor,rimLightWidth:this.rimWidth,refractionStrength:this.refractionStrength,chromaticStrength:this.chromaticStrength,occlusionIntensity:this.occlusionIntensity,depthPower:this.depthPower,depthScale:this.depthScale,depthBias:this.depthBias,fogDensity:this.fogDensity,fogColor:this.fogColor,colorShift:this.colorShift,brightnessBias:this.brightnessBias,contrastLow:this.contrastLow,contrastHigh:this.contrastHigh,verticalReduction:this.verticalReduction,dofStart:this.dofStart,dofStrength:this.dofStrength,bevelIntensity:this.bevelIntensity,bevelWidth:this.bevelWidth,bevelDarkening:this.bevelDarkening,bevelDesaturation:this.bevelDesaturation,bevelLightAngle:this.bevelLightAngle,edgeThickness:this.edgeThickness,edgeSpecular:this.edgeSpecular,edgeColor:this.edgeColor,chamferWidth:this.chamferWidth,chamferAngle:this.chamferAngle,chamferColor:this.chamferColor,chamferAmbient:this.chamferAmbient,chamferSpecular:this.chamferSpecular,chamferShininess:this.chamferShininess,edgeOcclusionWidth:this.edgeOcclusionWidth,edgeOcclusionStrength:this.edgeOcclusionStrength,lightDirection:this.lightDirection3};this.renderer=new ft(this.container,u),this.renderer.initialize(o,s.meta.width,s.meta.height,a),this.inputHandler=new pt(this);const f=this.parallaxX,c=this.parallaxY;if(this.renderer.start(o,l,()=>{const d=this.inputHandler.update();return{x:d.x*f,y:d.y*c}},(d,m)=>{this.emit("layershift-portal:frame",{currentTime:d,frameNumber:m})}),this.shouldAutoplay){o.currentTime=0;try{await o.play()}catch{}}this.initialized=!0,this.emit("layershift-portal:ready",{videoWidth:o.videoWidth,videoHeight:o.videoHeight,duration:o.duration})}catch(o){const s=o instanceof Error?o.message:"Failed to initialize.";console.error("<layershift-portal>: Failed to initialize.",o),this.emit("layershift-portal:error",{message:s})}}}async createVideoElement(t){const e=document.createElement("video");return e.crossOrigin="anonymous",e.setAttribute("crossorigin","anonymous"),e.playsInline=!0,e.setAttribute("playsinline",""),e.setAttribute("webkit-playsinline","true"),e.muted=this.shouldMute,e.defaultMuted=this.shouldMute,this.shouldMute&&e.setAttribute("muted",""),e.loop=this.shouldLoop,e.preload="auto",e.style.display="none",e.src=t,this.shadow.appendChild(e),await new Promise((i,r)=>{if(e.readyState>=HTMLMediaElement.HAVE_METADATA){i();return}const o=()=>{a(),i()},s=()=>{a(),r(new Error("Failed to load video metadata."))},a=()=>{e.removeEventListener("loadedmetadata",o),e.removeEventListener("error",s)};e.addEventListener("loadedmetadata",o),e.addEventListener("error",s),e.load()}),e}dispose(){this.abortController?.abort(),this.abortController=null,this.renderer?.dispose(),this.renderer=null,this.inputHandler?.dispose(),this.inputHandler=null,this.depthWorker?.dispose(),this.depthWorker=null,this.video&&(this.video.pause(),this.video.removeAttribute("src"),this.video.load(),this.video.remove(),this.video=null),this.mesh=null,this.initialized=!1,this.loopCount=0,this.container=null}}function W(n,t,e){return Math.min(e,Math.max(t,n))}function lt(n,t,e){return n+(t-n)*e}function Xe(n){const t=n.replace("#","");if(t.length===3){const e=parseInt(t[0]+t[0],16)/255,i=parseInt(t[1]+t[1],16)/255,r=parseInt(t[2]+t[2],16)/255;return[e,i,r]}if(t.length===6){const e=parseInt(t.substring(0,2),16)/255,i=parseInt(t.substring(2,4),16)/255,r=parseInt(t.substring(4,6),16)/255;return[e,i,r]}return[0,0,0]}return customElements.get(rt.TAG_NAME)||customElements.define(rt.TAG_NAME,rt),customElements.get(ht.TAG_NAME)||customElements.define(ht.TAG_NAME,ht),et.LayershiftElement=rt,et.LayershiftPortalElement=ht,Object.defineProperty(et,Symbol.toStringTag,{value:"Module"}),et})({});