shaderpad 1.0.0-beta.40 → 1.0.0-beta.42

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.
Files changed (52) hide show
  1. package/README.md +323 -41
  2. package/dist/chunk-5CBGNOA3.mjs +10 -0
  3. package/dist/chunk-5CBGNOA3.mjs.map +1 -0
  4. package/dist/chunk-JRSBIGBN.mjs +7 -0
  5. package/dist/chunk-JRSBIGBN.mjs.map +1 -0
  6. package/dist/index.d.mts +7 -30
  7. package/dist/index.d.ts +7 -30
  8. package/dist/index.js +3 -4
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +1 -1
  11. package/dist/plugins/face.d.mts +1 -3
  12. package/dist/plugins/face.d.ts +1 -3
  13. package/dist/plugins/face.js +53 -76
  14. package/dist/plugins/face.js.map +1 -1
  15. package/dist/plugins/face.mjs +51 -79
  16. package/dist/plugins/face.mjs.map +1 -1
  17. package/dist/plugins/hands.d.mts +1 -3
  18. package/dist/plugins/hands.d.ts +1 -3
  19. package/dist/plugins/hands.js +20 -16
  20. package/dist/plugins/hands.js.map +1 -1
  21. package/dist/plugins/hands.mjs +14 -15
  22. package/dist/plugins/hands.mjs.map +1 -1
  23. package/dist/plugins/helpers.js +1 -1
  24. package/dist/plugins/helpers.js.map +1 -1
  25. package/dist/plugins/helpers.mjs +1 -1
  26. package/dist/plugins/helpers.mjs.map +1 -1
  27. package/dist/plugins/mediapipe-common.d.mts +18 -0
  28. package/dist/plugins/mediapipe-common.d.ts +18 -0
  29. package/dist/plugins/mediapipe-common.js +7 -0
  30. package/dist/plugins/mediapipe-common.js.map +1 -0
  31. package/dist/plugins/mediapipe-common.mjs +2 -0
  32. package/dist/plugins/mediapipe-common.mjs.map +1 -0
  33. package/dist/plugins/pose.d.mts +1 -3
  34. package/dist/plugins/pose.d.ts +1 -3
  35. package/dist/plugins/pose.js +53 -49
  36. package/dist/plugins/pose.js.map +1 -1
  37. package/dist/plugins/pose.mjs +30 -30
  38. package/dist/plugins/pose.mjs.map +1 -1
  39. package/dist/plugins/save.d.mts +1 -1
  40. package/dist/plugins/save.d.ts +1 -1
  41. package/dist/plugins/save.js +1 -1
  42. package/dist/plugins/save.js.map +1 -1
  43. package/dist/plugins/save.mjs.map +1 -1
  44. package/dist/plugins/segmenter.d.mts +1 -3
  45. package/dist/plugins/segmenter.d.ts +1 -3
  46. package/dist/plugins/segmenter.js +17 -13
  47. package/dist/plugins/segmenter.js.map +1 -1
  48. package/dist/plugins/segmenter.mjs +11 -11
  49. package/dist/plugins/segmenter.mjs.map +1 -1
  50. package/package.json +1 -1
  51. package/dist/chunk-6C6DVCZI.mjs +0 -11
  52. package/dist/chunk-6C6DVCZI.mjs.map +0 -1
@@ -1,20 +1,24 @@
1
- "use strict";var G=Object.create;var I=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var X=Object.getOwnPropertyNames;var B=Object.getPrototypeOf,Y=Object.prototype.hasOwnProperty;var $=(a,i)=>{for(var t in i)I(a,t,{get:i[t],enumerable:!0})},O=(a,i,t,L)=>{if(i&&typeof i=="object"||typeof i=="function")for(let o of X(i))!Y.call(a,o)&&o!==t&&I(a,o,{get:()=>i[o],enumerable:!(L=K(i,o))||L.enumerable});return a};var j=(a,i,t)=>(t=a!=null?G(B(a)):{},O(i||!a||!a.__esModule?I(t,"default",{value:a,enumerable:!0}):t,a)),W=a=>O(I({},"__esModule",{value:!0}),a);var nn={};$(nn,{default:()=>Q});module.exports=W(nn);var v=21,Z=1,H=v+Z,q=[0,0,5,9,13,17];function J(a){let{textureName:i,options:t}=a,L="https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task";return function(o,b){let{injectGLSL:F,gl:y}=b,m=null,A=null,C=-1,M="VIDEO",_=new Map,w=t?.maxHands??2,l=512,N=0,r=null;async function P(){try{let{FilesetResolver:e,HandLandmarker:n}=await import("@mediapipe/tasks-vision");A=await e.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"),m=await n.createFromOptions(A,{baseOptions:{modelAssetPath:t?.modelPath||L,delegate:"GPU"},canvas:new OffscreenCanvas(1,1),runningMode:M,numHands:t?.maxHands??2,minHandDetectionConfidence:t?.minHandDetectionConfidence??.5,minHandPresenceConfidence:t?.minHandPresenceConfidence??.5,minTrackingConfidence:t?.minTrackingConfidence??.5})}catch(e){throw console.error("[Hands Plugin] Failed to initialize:",e),e}}function U(e,n,u){let s=1/0,c=-1/0,d=1/0,k=-1/0,T=0,f=0;for(let z of u){let R=(n*H+z)*4,S=e[R],D=e[R+1];s=Math.min(s,S),c=Math.max(c,S),d=Math.min(d,D),k=Math.max(k,D),T+=e[R+2],f+=e[R+3]}let h=(s+c)/2,x=(d+k)/2,p=T/u.length,g=f/u.length;return[h,x,p,g]}function V(e,n){if(!r)return;let u=e.length,s=u*H;for(let d=0;d<u;++d){let k=e[d],T=n[d]?.[0]?.categoryName==="Right";for(let x=0;x<v;++x){let p=k[x],g=(d*H+x)*4;r[g]=p.x,r[g+1]=1-p.y,r[g+2]=p.z??0,r[g+3]=T?1:0}let f=U(r,d,q),h=(d*H+v)*4;r[h]=f[0],r[h+1]=f[1],r[h+2]=f[2],r[h+3]=T?1:0}let c=Math.ceil(s/l);o.updateTextures({u_handLandmarksTex:{data:r,width:l,height:c,isPartial:!0}})}function E(e){if(!e.landmarks||!r)return;let n=e.landmarks.length;V(e.landmarks,e.handedness),o.updateUniforms({u_nHands:n}),t?.onResults?.(e)}o.registerHook("init",async()=>{o.initializeUniform("u_maxHands","int",w),o.initializeUniform("u_nHands","int",0);let e=w*H;N=Math.ceil(e/l);let n=l*N*4;r=new Float32Array(n),o.initializeTexture("u_handLandmarksTex",{data:r,width:l,height:N},{internalFormat:y.RGBA32F,type:y.FLOAT,minFilter:y.NEAREST,magFilter:y.NEAREST}),await P(),t?.onReady?.()}),o.registerHook("updateTextures",async e=>{let n=e[i];if(!(!n||(_.get(i)!==n&&(C=-1),_.set(i,n),!m)))try{let s=n instanceof HTMLVideoElement?"VIDEO":"IMAGE";if(M!==s&&(M=s,await m.setOptions({runningMode:M})),n instanceof HTMLVideoElement){if(n.videoWidth===0||n.videoHeight===0||n.readyState<2)return;if(n.currentTime!==C){C=n.currentTime;let c=m.detectForVideo(n,performance.now());E(c)}}else if(n instanceof HTMLImageElement||n instanceof HTMLCanvasElement){if(n.width===0||n.height===0)return;let c=m.detect(n);E(c)}}catch(s){console.error("[Hands Plugin] Detection error:",s)}}),o.registerHook("destroy",()=>{m&&(m.close(),m=null),A=null,_.clear(),r=null}),F(`
1
+ "use strict";var B=Object.create;var S=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var X=Object.getPrototypeOf,q=Object.prototype.hasOwnProperty;var Y=(e,o)=>{for(var a in o)S(e,a,{get:o[a],enumerable:!0})},R=(e,o,a,s)=>{if(o&&typeof o=="object"||typeof o=="function")for(let t of j(o))!q.call(e,t)&&t!==a&&S(e,t,{get:()=>o[t],enumerable:!(s=W(o,t))||s.enumerable});return e};var w=(e,o,a)=>(a=e!=null?B(X(e)):{},R(o||!e||!e.__esModule?S(a,"default",{value:e,enumerable:!0}):a,e)),J=e=>R(S({},"__esModule",{value:!0}),e);var ie={};Y(ie,{default:()=>ae});module.exports=J(ie);var se={data:new Uint8Array(4),width:1,height:1};function $(e){return e instanceof HTMLVideoElement||e instanceof HTMLImageElement||e instanceof HTMLCanvasElement||e instanceof OffscreenCanvas}function V(e){return JSON.stringify(e,Object.keys(e).sort())}function U(e,o,a,s,t=0){let i=1/0,u=-1/0,m=1/0,d=-1/0,f=0,g=0;for(let h of a){let c=(t+o*s+h)*4,D=e[c],L=e[c+1];i=Math.min(i,D),u=Math.max(u,D),m=Math.min(m,L),d=Math.max(d,L),f+=e[c+2],g+=e[c+3]}return[(i+u)/2,(m+d)/2,f/a.length,g/a.length]}var O=null;function z(){return O||(O=import("@mediapipe/tasks-vision").then(({FilesetResolver:e})=>e.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.22-rc.20250304/wasm"))),O}function K(e){return{historyParams:e?", framesAgo":"",fn:e?(s,t,i,u)=>{let m=i.replace(/\w+ /g,""),d=i?`${i}, int framesAgo`:"int framesAgo",f=m?`${m}, 0`:"0";return`${s} ${t}(${d}) {
2
+ ${u}
3
+ }
4
+ ${s} ${t}(${i}) { return ${t}(${f}); }`}:(s,t,i,u)=>`${s} ${t}(${i}) {
5
+ ${u}
6
+ }`}}var C=21,Z=1,H=C+Z,Q=[0,0,5,9,13,17],p=512,k=1,ee={modelPath:"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task",maxHands:2,minHandDetectionConfidence:.5,minHandPresenceConfidence:.5,minTrackingConfidence:.5},b=new Map;function ne(e,o,a){let s=e.landmarks.data,t=o.length;s[0]=t;for(let i=0;i<t;++i){let u=o[i],m=a[i]?.[0]?.categoryName==="Right";for(let g=0;g<C;++g){let h=u[g],c=(k+i*H+g)*4;s[c]=h.x,s[c+1]=1-h.y,s[c+2]=h.z??0,s[c+3]=m?1:0}let d=U(s,i,Q,H,k),f=(k+i*H+C)*4;s[f]=d[0],s[f+1]=d[1],s[f+2]=d[2],s[f+3]=m?1:0}e.state.nHands=t}function te(e){let{textureName:o,options:{history:a,...s}={}}=e,t={...ee,...s},i=V({...t,textureName:o}),u=t.maxHands*H+k,m=Math.ceil(u/p);return function(d,f){let{injectGLSL:g,gl:h,emitHook:c}=f,L=b.get(i)?.landmarks.data??new Float32Array(p*m*4),n=null,I=!1;function y(){if(!n)return;let{nHands:r}=n.state,l=r*H+k,x=Math.ceil(l/p);d.updateTextures({u_handLandmarksTex:{data:n.landmarks.data,width:p,height:x,isPartial:!0}},{skipHistoryWrite:I}),d.updateUniforms({u_nHands:r}),c("hands:result",n.state.result)}async function G(){if(b.has(i))n=b.get(i);else{let[r,{HandLandmarker:l}]=await Promise.all([z(),import("@mediapipe/tasks-vision")]),x=new OffscreenCanvas(1,1);n={landmarker:await l.createFromOptions(r,{baseOptions:{modelAssetPath:t.modelPath,delegate:"GPU"},canvas:x,runningMode:"VIDEO",numHands:t.maxHands,minHandDetectionConfidence:t.minHandDetectionConfidence,minHandPresenceConfidence:t.minHandPresenceConfidence,minTrackingConfidence:t.minTrackingConfidence}),canvas:x,subscribers:new Map,maxHands:t.maxHands,state:{runningMode:"VIDEO",source:null,videoTime:-1,resultTimestamp:0,result:null,pending:Promise.resolve(),nHands:0},landmarks:{data:L,textureHeight:m}},b.set(i,n)}n.subscribers.set(y,!1)}let v=G();d.on("init",()=>{d.initializeUniform("u_maxHands","int",t.maxHands),d.initializeUniform("u_nHands","int",0),d.initializeTexture("u_handLandmarksTex",{data:L,width:p,height:m},{internalFormat:h.RGBA32F,type:h.FLOAT,minFilter:h.NEAREST,magFilter:h.NEAREST,history:a}),v.then(()=>c("hands:ready"))}),d.on("initializeTexture",(r,l)=>{r===o&&$(l)&&E(l)}),d.on("updateTextures",(r,l)=>{let x=r[o];$(x)&&(I=l?.skipHistoryWrite??!1,E(x))});let P=0;async function E(r){let l=performance.now(),x=++P;await v,n&&(n.state.pending=n.state.pending.then(async()=>{if(x!==P||!n)return;let A=r instanceof HTMLVideoElement?"VIDEO":"IMAGE";n.state.runningMode!==A&&(n.state.runningMode=A,await n.landmarker.setOptions({runningMode:A}));let _=!1;if(r!==n.state.source?(n.state.source=r,n.state.videoTime=-1,_=!0):r instanceof HTMLVideoElement?r.currentTime!==n.state.videoTime&&(n.state.videoTime=r.currentTime,_=!0):r instanceof HTMLImageElement||l-n.state.resultTimestamp>2&&(_=!0),_){let T;if(r instanceof HTMLVideoElement){if(r.videoWidth===0||r.videoHeight===0||r.readyState<2)return;T=n.landmarker.detectForVideo(r,l)}else{if(r.width===0||r.height===0)return;T=n.landmarker.detect(r)}if(T){n.state.resultTimestamp=l,n.state.result=T,ne(n,T.landmarks,T.handedness);for(let F of n.subscribers.keys())F(),n.subscribers.set(F,!0)}}else n.state.result&&!n.subscribers.get(y)&&(y(),n.subscribers.set(y,!0))}),await n.state.pending)}d.on("destroy",()=>{n&&(n.subscribers.delete(y),n.subscribers.size===0&&(n.landmarker.close(),b.delete(i))),n=null});let{fn:M,historyParams:N}=K(a);g(`
2
7
  uniform int u_maxHands;
3
8
  uniform int u_nHands;
4
- uniform sampler2D u_handLandmarksTex;
5
-
6
- vec4 handLandmark(int handIndex, int landmarkIndex) {
7
- int i = handIndex * ${H} + landmarkIndex;
8
- int x = i % ${l};
9
- int y = i / ${l};
10
- return texelFetch(u_handLandmarksTex, ivec2(x, y), 0);
11
- }
12
-
13
- float isRightHand(int handIndex) {
14
- return handLandmark(handIndex, 0).w;
15
- }
9
+ uniform highp sampler2D${a?"Array":""} u_handLandmarksTex;${a?`
10
+ uniform int u_handLandmarksTexFrameOffset;`:""}
16
11
 
17
- float isLeftHand(int handIndex) {
18
- return 1.0 - handLandmark(handIndex, 0).w;
19
- }`)}}var Q=J;
12
+ ${M("int","nHandsAt","",a?`
13
+ int layer = (u_handLandmarksTexFrameOffset - framesAgo + ${a}) % ${a};
14
+ return int(texelFetch(u_handLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`:`
15
+ return int(texelFetch(u_handLandmarksTex, ivec2(0, 0), 0).r + 0.5);`)}
16
+ ${M("vec4","handLandmark","int handIndex, int landmarkIndex",`int i = ${k} + handIndex * ${H} + landmarkIndex;
17
+ int x = i % ${p};
18
+ int y = i / ${p};${a?`
19
+ int layer = (u_handLandmarksTexFrameOffset - framesAgo + ${a}) % ${a};
20
+ return texelFetch(u_handLandmarksTex, ivec3(x, y, layer), 0);`:`
21
+ return texelFetch(u_handLandmarksTex, ivec2(x, y), 0);`}`)}
22
+ ${M("float","isRightHand","int handIndex",`return handLandmark(handIndex, 0${N}).w;`)}
23
+ ${M("float","isLeftHand","int handIndex",`return 1.0 - handLandmark(handIndex, 0${N}).w;`)}`)}}var ae=te;
20
24
  //# sourceMappingURL=hands.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/hands.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { HandLandmarker, HandLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface HandsPluginOptions {\n\tmodelPath?: string;\n\tmaxHands?: number;\n\tminHandDetectionConfidence?: number;\n\tminHandPresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\tonReady?: () => void;\n\tonResults?: (results: HandLandmarkerResult) => void;\n}\n\nconst STANDARD_LANDMARK_COUNT = 21; // See https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker#models.\nconst CUSTOM_LANDMARK_COUNT = 1;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\nconst HAND_CENTER_LANDMARKS = [0, 0, 5, 9, 13, 17] as const; // Wrist + MCP joints, weighted toward wrist.\n\nfunction hands(config: { textureName: string; options?: HandsPluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, gl } = context;\n\n\t\tlet handLandmarker: HandLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tlet runningMode: 'IMAGE' | 'VIDEO' = 'VIDEO';\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxHands = options?.maxHands ?? 2;\n\n\t\tconst LANDMARKS_TEXTURE_WIDTH = 512;\n\t\tlet landmarksTextureHeight = 0;\n\t\tlet landmarksDataArray: Float32Array | null = null;\n\n\t\tasync function initializeHandLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, HandLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\thandLandmarker = await HandLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\tcanvas: new OffscreenCanvas(1, 1),\n\t\t\t\t\trunningMode: runningMode,\n\t\t\t\t\tnumHands: options?.maxHands ?? 2,\n\t\t\t\t\tminHandDetectionConfidence: options?.minHandDetectionConfidence ?? 0.5,\n\t\t\t\t\tminHandPresenceConfidence: options?.minHandPresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Hands Plugin] Failed to initialize:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateBoundingBoxCenter(\n\t\t\tlandmarksDataArray: Float32Array,\n\t\t\thandIdx: number,\n\t\t\tlandmarkIndices: readonly number[]\n\t\t): [number, number, number, number] {\n\t\t\tlet minX = Infinity,\n\t\t\t\tmaxX = -Infinity,\n\t\t\t\tminY = Infinity,\n\t\t\t\tmaxY = -Infinity,\n\t\t\t\tavgZ = 0,\n\t\t\t\tavgVisibility = 0;\n\n\t\t\tfor (const idx of landmarkIndices) {\n\t\t\t\tconst dataIdx = (handIdx * LANDMARK_COUNT + idx) * 4;\n\t\t\t\tconst x = landmarksDataArray[dataIdx];\n\t\t\t\tconst y = landmarksDataArray[dataIdx + 1];\n\t\t\t\tminX = Math.min(minX, x);\n\t\t\t\tmaxX = Math.max(maxX, x);\n\t\t\t\tminY = Math.min(minY, y);\n\t\t\t\tmaxY = Math.max(maxY, y);\n\t\t\t\tavgZ += landmarksDataArray[dataIdx + 2];\n\t\t\t\tavgVisibility += landmarksDataArray[dataIdx + 3];\n\t\t\t}\n\n\t\t\tconst centerX = (minX + maxX) / 2;\n\t\t\tconst centerY = (minY + maxY) / 2;\n\t\t\tconst centerZ = avgZ / landmarkIndices.length;\n\t\t\tconst centerVisibility = avgVisibility / landmarkIndices.length;\n\t\t\treturn [centerX, centerY, centerZ, centerVisibility];\n\t\t}\n\n\t\tfunction updateLandmarksTexture(hands: NormalizedLandmark[][], handedness: { categoryName: string }[][]) {\n\t\t\tif (!landmarksDataArray) return;\n\n\t\t\tconst nHands = hands.length;\n\t\t\tconst totalLandmarks = nHands * LANDMARK_COUNT;\n\n\t\t\tfor (let handIdx = 0; handIdx < nHands; ++handIdx) {\n\t\t\t\tconst landmarks = hands[handIdx];\n\t\t\t\tconst isRightHand = handedness[handIdx]?.[0]?.categoryName === 'Right';\n\t\t\t\tfor (let lmIdx = 0; lmIdx < STANDARD_LANDMARK_COUNT; ++lmIdx) {\n\t\t\t\t\tconst landmark = landmarks[lmIdx];\n\t\t\t\t\tconst dataIdx = (handIdx * LANDMARK_COUNT + lmIdx) * 4;\n\t\t\t\t\tlandmarksDataArray[dataIdx] = landmark.x;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 1] = 1 - landmark.y;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 2] = landmark.z ?? 0;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 3] = isRightHand ? 1 : 0;\n\t\t\t\t}\n\n\t\t\t\tconst handCenter = calculateBoundingBoxCenter(landmarksDataArray, handIdx, HAND_CENTER_LANDMARKS);\n\t\t\t\tconst handCenterIdx = (handIdx * LANDMARK_COUNT + STANDARD_LANDMARK_COUNT) * 4;\n\t\t\t\tlandmarksDataArray[handCenterIdx] = handCenter[0];\n\t\t\t\tlandmarksDataArray[handCenterIdx + 1] = handCenter[1];\n\t\t\t\tlandmarksDataArray[handCenterIdx + 2] = handCenter[2];\n\t\t\t\tlandmarksDataArray[handCenterIdx + 3] = isRightHand ? 1 : 0;\n\t\t\t}\n\n\t\t\tconst rowsToUpdate = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tshaderPad.updateTextures({\n\t\t\t\tu_handLandmarksTex: {\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\tisPartial: true,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tfunction processHandResults(result: HandLandmarkerResult) {\n\t\t\tif (!result.landmarks || !landmarksDataArray) return;\n\n\t\t\tconst nHands = result.landmarks.length;\n\t\t\tupdateLandmarksTexture(result.landmarks, result.handedness);\n\t\t\tshaderPad.updateUniforms({ u_nHands: nHands });\n\n\t\t\toptions?.onResults?.(result);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeUniform('u_maxHands', 'int', maxHands);\n\t\t\tshaderPad.initializeUniform('u_nHands', 'int', 0);\n\n\t\t\tconst totalLandmarks = maxHands * LANDMARK_COUNT;\n\t\t\tlandmarksTextureHeight = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tconst textureSize = LANDMARKS_TEXTURE_WIDTH * landmarksTextureHeight * 4;\n\t\t\tlandmarksDataArray = new Float32Array(textureSize);\n\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_handLandmarksTex',\n\t\t\t\t{\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: landmarksTextureHeight,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tinternalFormat: gl.RGBA32F,\n\t\t\t\t\ttype: gl.FLOAT,\n\t\t\t\t\tminFilter: gl.NEAREST,\n\t\t\t\t\tmagFilter: gl.NEAREST,\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tawait initializeHandLandmarker();\n\t\t\toptions?.onReady?.();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', async (updates: Record<string, TextureSource>) => {\n\t\t\tconst source = updates[textureName];\n\t\t\tif (!source) return;\n\n\t\t\tconst previousSource = textureSources.get(textureName);\n\t\t\tif (previousSource !== source) {\n\t\t\t\tlastVideoTime = -1;\n\t\t\t}\n\n\t\t\ttextureSources.set(textureName, source);\n\t\t\tif (!handLandmarker) return;\n\n\t\t\ttry {\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (runningMode !== requiredMode) {\n\t\t\t\t\trunningMode = requiredMode;\n\t\t\t\t\tawait handLandmarker.setOptions({ runningMode: runningMode });\n\t\t\t\t}\n\n\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\tconst result = handLandmarker.detectForVideo(source, performance.now());\n\t\t\t\t\t\tprocessHandResults(result);\n\t\t\t\t\t}\n\t\t\t\t} else if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement) {\n\t\t\t\t\tif (source.width === 0 || source.height === 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst result = handLandmarker.detect(source);\n\t\t\t\t\tprocessHandResults(result);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Hands Plugin] Detection error:', error);\n\t\t\t}\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (handLandmarker) {\n\t\t\t\thandLandmarker.close();\n\t\t\t\thandLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tlandmarksDataArray = null;\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxHands;\nuniform int u_nHands;\nuniform sampler2D u_handLandmarksTex;\n\nvec4 handLandmark(int handIndex, int landmarkIndex) {\n\tint i = handIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};\n\treturn texelFetch(u_handLandmarksTex, ivec2(x, y), 0);\n}\n\nfloat isRightHand(int handIndex) {\n\treturn handLandmark(handIndex, 0).w;\n}\n\nfloat isLeftHand(int handIndex) {\n\treturn 1.0 - handLandmark(handIndex, 0).w;\n}`);\n\t};\n}\n\nexport default hands;\n"],"mappings":"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","names":["hands_exports","__export","hands_default","__toCommonJS","STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","HAND_CENTER_LANDMARKS","hands","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","gl","handLandmarker","vision","lastVideoTime","runningMode","textureSources","maxHands","LANDMARKS_TEXTURE_WIDTH","landmarksTextureHeight","landmarksDataArray","initializeHandLandmarker","FilesetResolver","HandLandmarker","error","calculateBoundingBoxCenter","handIdx","landmarkIndices","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","dataIdx","x","y","centerX","centerY","centerZ","centerVisibility","updateLandmarksTexture","handedness","nHands","totalLandmarks","landmarks","isRightHand","lmIdx","landmark","handCenter","handCenterIdx","rowsToUpdate","processHandResults","result","textureSize","updates","source","requiredMode"]}
1
+ {"version":3,"sources":["../../src/plugins/hands.ts","../../src/plugins/mediapipe-common.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '..';\nimport {\n\tcalculateBoundingBoxCenter,\n\tgenerateGLSLFn,\n\tgetSharedFileset,\n\thashOptions,\n\tisMediaPipeSource,\n\tMediaPipeSource,\n} from './mediapipe-common';\nimport type { HandLandmarker, HandLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface HandsPluginOptions {\n\tmodelPath?: string;\n\tmaxHands?: number;\n\tminHandDetectionConfidence?: number;\n\tminHandPresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\thistory?: number;\n}\n\nconst STANDARD_LANDMARK_COUNT = 21; // See https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker#models.\nconst CUSTOM_LANDMARK_COUNT = 1;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\nconst HAND_CENTER_LANDMARKS = [0, 0, 5, 9, 13, 17] as const; // Wrist + MCP joints, weighted toward wrist.\nconst LANDMARKS_TEXTURE_WIDTH = 512;\nconst N_LANDMARK_METADATA_SLOTS = 1;\n\nconst DEFAULT_HANDS_OPTIONS: Required<Omit<HandsPluginOptions, 'history'>> = {\n\tmodelPath:\n\t\t'https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task',\n\tmaxHands: 2,\n\tminHandDetectionConfidence: 0.5,\n\tminHandPresenceConfidence: 0.5,\n\tminTrackingConfidence: 0.5,\n};\n\ninterface Detector {\n\tlandmarker: HandLandmarker;\n\tcanvas: OffscreenCanvas;\n\tsubscribers: Map<() => void, boolean>;\n\tmaxHands: number;\n\tstate: {\n\t\trunningMode: 'IMAGE' | 'VIDEO';\n\t\tsource: MediaPipeSource | null;\n\t\tvideoTime: number;\n\t\tresultTimestamp: number;\n\t\tresult: HandLandmarkerResult | null;\n\t\tpending: Promise<void>;\n\t\tnHands: number;\n\t};\n\tlandmarks: {\n\t\tdata: Float32Array;\n\t\ttextureHeight: number;\n\t};\n}\nconst sharedDetectors = new Map<string, Detector>();\n\nfunction updateLandmarksData(\n\tdetector: Detector,\n\thands: NormalizedLandmark[][],\n\thandedness: { categoryName: string }[][]\n) {\n\tconst data = detector.landmarks.data;\n\tconst nHands = hands.length;\n\n\tdata[0] = nHands;\n\n\tfor (let handIdx = 0; handIdx < nHands; ++handIdx) {\n\t\tconst landmarks = hands[handIdx];\n\t\tconst isRightHand = handedness[handIdx]?.[0]?.categoryName === 'Right';\n\t\tfor (let lmIdx = 0; lmIdx < STANDARD_LANDMARK_COUNT; ++lmIdx) {\n\t\t\tconst landmark = landmarks[lmIdx];\n\t\t\tconst dataIdx = (N_LANDMARK_METADATA_SLOTS + handIdx * LANDMARK_COUNT + lmIdx) * 4;\n\t\t\tdata[dataIdx] = landmark.x;\n\t\t\tdata[dataIdx + 1] = 1 - landmark.y;\n\t\t\tdata[dataIdx + 2] = landmark.z ?? 0;\n\t\t\tdata[dataIdx + 3] = isRightHand ? 1 : 0;\n\t\t}\n\n\t\tconst handCenter = calculateBoundingBoxCenter(data, handIdx, HAND_CENTER_LANDMARKS, LANDMARK_COUNT, N_LANDMARK_METADATA_SLOTS);\n\t\tconst handCenterIdx = (N_LANDMARK_METADATA_SLOTS + handIdx * LANDMARK_COUNT + STANDARD_LANDMARK_COUNT) * 4;\n\t\tdata[handCenterIdx] = handCenter[0];\n\t\tdata[handCenterIdx + 1] = handCenter[1];\n\t\tdata[handCenterIdx + 2] = handCenter[2];\n\t\tdata[handCenterIdx + 3] = isRightHand ? 1 : 0;\n\t}\n\n\tdetector.state.nHands = nHands;\n}\n\nfunction hands(config: { textureName: string; options?: HandsPluginOptions }) {\n\tconst { textureName, options: { history, ...mediapipeOptions } = {} } = config;\n\tconst options = { ...DEFAULT_HANDS_OPTIONS, ...mediapipeOptions };\n\tconst optionsKey = hashOptions({ ...options, textureName });\n\n\tconst nLandmarksMax = options.maxHands * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\tconst textureHeight = Math.ceil(nLandmarksMax / LANDMARKS_TEXTURE_WIDTH);\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, gl, emitHook } = context;\n\n\t\tconst existingDetector = sharedDetectors.get(optionsKey);\n\t\tconst landmarksData =\n\t\t\texistingDetector?.landmarks.data ?? new Float32Array(LANDMARKS_TEXTURE_WIDTH * textureHeight * 4);\n\t\tlet detector: Detector | null = null;\n\t\tlet skipHistoryWrite = false;\n\n\t\tfunction onResult() {\n\t\t\tif (!detector) return;\n\t\t\tconst { nHands } = detector.state;\n\t\t\tconst nSlots = nHands * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\t\t\tconst rowsToUpdate = Math.ceil(nSlots / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tshaderPad.updateTextures(\n\t\t\t\t{\n\t\t\t\t\tu_handLandmarksTex: {\n\t\t\t\t\t\tdata: detector.landmarks.data,\n\t\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\t\tisPartial: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ skipHistoryWrite }\n\t\t\t);\n\t\t\tshaderPad.updateUniforms({ u_nHands: nHands });\n\t\t\temitHook('hands:result', detector.state.result);\n\t\t}\n\n\t\tasync function initializeDetector() {\n\t\t\tif (sharedDetectors.has(optionsKey)) {\n\t\t\t\tdetector = sharedDetectors.get(optionsKey)!;\n\t\t\t} else {\n\t\t\t\tconst [mediaPipe, { HandLandmarker }] = await Promise.all([\n\t\t\t\t\tgetSharedFileset(),\n\t\t\t\t\timport('@mediapipe/tasks-vision'),\n\t\t\t\t]);\n\t\t\t\tconst mediapipeCanvas = new OffscreenCanvas(1, 1);\n\t\t\t\tconst handLandmarker = await HandLandmarker.createFromOptions(mediaPipe, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options.modelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\tcanvas: mediapipeCanvas,\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumHands: options.maxHands,\n\t\t\t\t\tminHandDetectionConfidence: options.minHandDetectionConfidence,\n\t\t\t\t\tminHandPresenceConfidence: options.minHandPresenceConfidence,\n\t\t\t\t\tminTrackingConfidence: options.minTrackingConfidence,\n\t\t\t\t});\n\n\t\t\t\tdetector = {\n\t\t\t\t\tlandmarker: handLandmarker,\n\t\t\t\t\tcanvas: mediapipeCanvas,\n\t\t\t\t\tsubscribers: new Map(),\n\t\t\t\t\tmaxHands: options.maxHands,\n\t\t\t\t\tstate: {\n\t\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\t\tsource: null,\n\t\t\t\t\t\tvideoTime: -1,\n\t\t\t\t\t\tresultTimestamp: 0,\n\t\t\t\t\t\tresult: null,\n\t\t\t\t\t\tpending: Promise.resolve(),\n\t\t\t\t\t\tnHands: 0,\n\t\t\t\t\t},\n\t\t\t\t\tlandmarks: {\n\t\t\t\t\t\tdata: landmarksData,\n\t\t\t\t\t\ttextureHeight,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\tsharedDetectors.set(optionsKey, detector);\n\t\t\t}\n\n\t\t\tdetector.subscribers.set(onResult, false);\n\t\t}\n\t\tconst initPromise = initializeDetector();\n\n\t\tshaderPad.on('init', () => {\n\t\t\tshaderPad.initializeUniform('u_maxHands', 'int', options.maxHands);\n\t\t\tshaderPad.initializeUniform('u_nHands', 'int', 0);\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_handLandmarksTex',\n\t\t\t\t{ data: landmarksData, width: LANDMARKS_TEXTURE_WIDTH, height: textureHeight },\n\t\t\t\t{ internalFormat: gl.RGBA32F, type: gl.FLOAT, minFilter: gl.NEAREST, magFilter: gl.NEAREST, history }\n\t\t\t);\n\t\t\tinitPromise.then(() => emitHook('hands:ready'));\n\t\t});\n\n\t\tshaderPad.on('initializeTexture', (name: string, source: TextureSource) => {\n\t\t\tif (name === textureName && isMediaPipeSource(source)) detectHands(source);\n\t\t});\n\n\t\tshaderPad.on(\n\t\t\t'updateTextures',\n\t\t\t(updates: Record<string, TextureSource>, options?: { skipHistoryWrite?: boolean }) => {\n\t\t\t\tconst source = updates[textureName];\n\t\t\t\tif (isMediaPipeSource(source)) {\n\t\t\t\t\tskipHistoryWrite = options?.skipHistoryWrite ?? false;\n\t\t\t\t\tdetectHands(source);\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\n\t\tlet nDetectionCalls = 0;\n\t\tasync function detectHands(source: MediaPipeSource) {\n\t\t\tconst now = performance.now();\n\t\t\tconst callOrder = ++nDetectionCalls;\n\t\t\tawait initPromise;\n\t\t\tif (!detector) return;\n\n\t\t\tdetector.state.pending = detector.state.pending.then(async () => {\n\t\t\t\tif (callOrder !== nDetectionCalls || !detector) return;\n\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (detector.state.runningMode !== requiredMode) {\n\t\t\t\t\tdetector.state.runningMode = requiredMode;\n\t\t\t\t\tawait detector.landmarker.setOptions({ runningMode: requiredMode });\n\t\t\t\t}\n\n\t\t\t\tlet shouldDetect = false;\n\n\t\t\t\tif (source !== detector.state.source) {\n\t\t\t\t\tdetector.state.source = source;\n\t\t\t\t\tdetector.state.videoTime = -1;\n\t\t\t\t\tshouldDetect = true;\n\t\t\t\t} else if (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.currentTime !== detector.state.videoTime) {\n\t\t\t\t\t\tdetector.state.videoTime = source.currentTime;\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t} else if (!(source instanceof HTMLImageElement)) {\n\t\t\t\t\tif (now - detector.state.resultTimestamp > 2) {\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (shouldDetect) {\n\t\t\t\t\tlet result: HandLandmarkerResult | undefined;\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) return;\n\t\t\t\t\t\tresult = detector.landmarker.detectForVideo(source, now);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (source.width === 0 || source.height === 0) return;\n\t\t\t\t\t\tresult = detector.landmarker.detect(source);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\tdetector.state.resultTimestamp = now;\n\t\t\t\t\t\tdetector.state.result = result;\n\t\t\t\t\t\tupdateLandmarksData(detector, result.landmarks, result.handedness);\n\t\t\t\t\t\tfor (const cb of detector.subscribers.keys()) {\n\t\t\t\t\t\t\tcb();\n\t\t\t\t\t\t\tdetector.subscribers.set(cb, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (detector.state.result && !detector.subscribers.get(onResult)) {\n\t\t\t\t\tonResult();\n\t\t\t\t\tdetector.subscribers.set(onResult, true);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait detector.state.pending;\n\t\t}\n\n\t\tshaderPad.on('destroy', () => {\n\t\t\tif (detector) {\n\t\t\t\tdetector.subscribers.delete(onResult);\n\t\t\t\tif (detector.subscribers.size === 0) {\n\t\t\t\t\tdetector.landmarker.close();\n\t\t\t\t\tsharedDetectors.delete(optionsKey);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdetector = null;\n\t\t});\n\n\t\tconst { fn, historyParams } = generateGLSLFn(history);\n\n\t\tinjectGLSL(`\nuniform int u_maxHands;\nuniform int u_nHands;\nuniform highp sampler2D${history ? 'Array' : ''} u_handLandmarksTex;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_handLandmarksTexFrameOffset;`\n\t\t\t\t: ''\n\t\t}\n\n${fn(\n\t'int',\n\t'nHandsAt',\n\t'',\n\thistory\n\t\t? `\n\tint layer = (u_handLandmarksTexFrameOffset - framesAgo + ${history}) % ${history};\n\treturn int(texelFetch(u_handLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`\n\t\t: `\n\treturn int(texelFetch(u_handLandmarksTex, ivec2(0, 0), 0).r + 0.5);`\n)}\n${fn(\n\t'vec4',\n\t'handLandmark',\n\t'int handIndex, int landmarkIndex',\n\t`int i = ${N_LANDMARK_METADATA_SLOTS} + handIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};${\n\t\thistory\n\t\t\t? `\n\tint layer = (u_handLandmarksTexFrameOffset - framesAgo + ${history}) % ${history};\n\treturn texelFetch(u_handLandmarksTex, ivec3(x, y, layer), 0);`\n\t\t\t: `\n\treturn texelFetch(u_handLandmarksTex, ivec2(x, y), 0);`\n\t}`\n)}\n${fn('float', 'isRightHand', 'int handIndex', `return handLandmark(handIndex, 0${historyParams}).w;`)}\n${fn('float', 'isLeftHand', 'int handIndex', `return 1.0 - handLandmark(handIndex, 0${historyParams}).w;`)}`);\n\t};\n}\n\nexport default hands;\n","import { TextureSource } from '..';\n\nexport const dummyTexture = { data: new Uint8Array(4), width: 1, height: 1 };\n\nexport type MediaPipeSource = HTMLVideoElement | HTMLImageElement | HTMLCanvasElement | OffscreenCanvas;\n\nexport function isMediaPipeSource(source: TextureSource): source is MediaPipeSource {\n\treturn (\n\t\tsource instanceof HTMLVideoElement ||\n\t\tsource instanceof HTMLImageElement ||\n\t\tsource instanceof HTMLCanvasElement ||\n\t\tsource instanceof OffscreenCanvas\n\t);\n}\n\nexport function hashOptions(options: object): string {\n\treturn JSON.stringify(options, Object.keys(options).sort());\n}\n\nexport function calculateBoundingBoxCenter(\n\tdata: Float32Array,\n\tentityIdx: number,\n\tlandmarkIndices: readonly number[] | number[],\n\tlandmarkCount: number,\n\toffset: number = 0\n): [number, number, number, number] {\n\tlet minX = Infinity,\n\t\tmaxX = -Infinity,\n\t\tminY = Infinity,\n\t\tmaxY = -Infinity,\n\t\tavgZ = 0,\n\t\tavgVisibility = 0;\n\n\tfor (const idx of landmarkIndices) {\n\t\tconst dataIdx = (offset + entityIdx * landmarkCount + idx) * 4;\n\t\tconst x = data[dataIdx];\n\t\tconst y = data[dataIdx + 1];\n\t\tminX = Math.min(minX, x);\n\t\tmaxX = Math.max(maxX, x);\n\t\tminY = Math.min(minY, y);\n\t\tmaxY = Math.max(maxY, y);\n\t\tavgZ += data[dataIdx + 2];\n\t\tavgVisibility += data[dataIdx + 3];\n\t}\n\n\treturn [\n\t\t(minX + maxX) / 2,\n\t\t(minY + maxY) / 2,\n\t\tavgZ / landmarkIndices.length,\n\t\tavgVisibility / landmarkIndices.length,\n\t];\n}\n\nlet filesetPromise: Promise<any> | null = null;\nexport function getSharedFileset(): Promise<any> {\n\tif (!filesetPromise) {\n\t\tfilesetPromise = import('@mediapipe/tasks-vision').then(({ FilesetResolver }) =>\n\t\t\tFilesetResolver.forVisionTasks(\n\t\t\t\t`https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${__MEDIAPIPE_TASKS_VISION_VERSION__}/wasm`\n\t\t\t)\n\t\t);\n\t}\n\treturn filesetPromise;\n}\n\nexport function generateGLSLFn(history: number | undefined) {\n\tconst historyParams = history ? ', framesAgo' : '';\n\tconst fn = history\n\t\t? (returnType: string, name: string, args: string, body: string) => {\n\t\t\t\tconst argsOnly = args.replace(/\\w+ /g, '');\n\t\t\t\tconst historyArgs = args ? `${args}, int framesAgo` : 'int framesAgo';\n\t\t\t\tconst callArgs = argsOnly ? `${argsOnly}, 0` : '0';\n\t\t\t\treturn `${returnType} ${name}(${historyArgs}) {\\n${body}\\n}\n${returnType} ${name}(${args}) { return ${name}(${callArgs}); }`;\n\t\t }\n\t\t: (returnType: string, name: string, args: string, body: string) =>\n\t\t\t\t`${returnType} ${name}(${args}) {\\n${body}\\n}`;\n\treturn { historyParams, fn };\n}\n"],"mappings":"0jBAAA,IAAAA,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,eAAAC,EAAAH,ICEO,IAAMI,GAAe,CAAE,KAAM,IAAI,WAAW,CAAC,EAAG,MAAO,EAAG,OAAQ,CAAE,EAIpE,SAASC,EAAkBC,EAAkD,CACnF,OACCA,aAAkB,kBAClBA,aAAkB,kBAClBA,aAAkB,mBAClBA,aAAkB,eAEpB,CAEO,SAASC,EAAYC,EAAyB,CACpD,OAAO,KAAK,UAAUA,EAAS,OAAO,KAAKA,CAAO,EAAE,KAAK,CAAC,CAC3D,CAEO,SAASC,EACfC,EACAC,EACAC,EACAC,EACAC,EAAiB,EACkB,CACnC,IAAIC,EAAO,IACVC,EAAO,KACPC,EAAO,IACPC,EAAO,KACPC,EAAO,EACPC,EAAgB,EAEjB,QAAWC,KAAOT,EAAiB,CAClC,IAAMU,GAAWR,EAASH,EAAYE,EAAgBQ,GAAO,EACvDE,EAAIb,EAAKY,CAAO,EAChBE,EAAId,EAAKY,EAAU,CAAC,EAC1BP,EAAO,KAAK,IAAIA,EAAMQ,CAAC,EACvBP,EAAO,KAAK,IAAIA,EAAMO,CAAC,EACvBN,EAAO,KAAK,IAAIA,EAAMO,CAAC,EACvBN,EAAO,KAAK,IAAIA,EAAMM,CAAC,EACvBL,GAAQT,EAAKY,EAAU,CAAC,EACxBF,GAAiBV,EAAKY,EAAU,CAAC,CAClC,CAEA,MAAO,EACLP,EAAOC,GAAQ,GACfC,EAAOC,GAAQ,EAChBC,EAAOP,EAAgB,OACvBQ,EAAgBR,EAAgB,MACjC,CACD,CAEA,IAAIa,EAAsC,KACnC,SAASC,GAAiC,CAChD,OAAKD,IACJA,EAAiB,OAAO,yBAAyB,EAAE,KAAK,CAAC,CAAE,gBAAAE,CAAgB,IAC1EA,EAAgB,eACf,+EACD,CACD,GAEMF,CACR,CAEO,SAASG,EAAeC,EAA6B,CAY3D,MAAO,CAAE,cAXaA,EAAU,cAAgB,GAWxB,GAVbA,EACR,CAACC,EAAoBC,EAAcC,EAAcC,IAAiB,CAClE,IAAMC,EAAWF,EAAK,QAAQ,QAAS,EAAE,EACnCG,EAAcH,EAAO,GAAGA,CAAI,kBAAoB,gBAChDI,EAAWF,EAAW,GAAGA,CAAQ,MAAQ,IAC/C,MAAO,GAAGJ,CAAU,IAAIC,CAAI,IAAII,CAAW;AAAA,EAAQF,CAAI;AAAA;AAAA,EACzDH,CAAU,IAAIC,CAAI,IAAIC,CAAI,cAAcD,CAAI,IAAIK,CAAQ,MACtD,EACA,CAACN,EAAoBC,EAAcC,EAAcC,IACjD,GAAGH,CAAU,IAAIC,CAAI,IAAIC,CAAI;AAAA,EAAQC,CAAI;AAAA,EACjB,CAC5B,CD1DA,IAAMI,EAA0B,GAC1BC,EAAwB,EACxBC,EAAiBF,EAA0BC,EAC3CE,EAAwB,CAAC,EAAG,EAAG,EAAG,EAAG,GAAI,EAAE,EAC3CC,EAA0B,IAC1BC,EAA4B,EAE5BC,GAAuE,CAC5E,UACC,iHACD,SAAU,EACV,2BAA4B,GAC5B,0BAA2B,GAC3B,sBAAuB,EACxB,EAqBMC,EAAkB,IAAI,IAE5B,SAASC,GACRC,EACAC,EACAC,EACC,CACD,IAAMC,EAAOH,EAAS,UAAU,KAC1BI,EAASH,EAAM,OAErBE,EAAK,CAAC,EAAIC,EAEV,QAASC,EAAU,EAAGA,EAAUD,EAAQ,EAAEC,EAAS,CAClD,IAAMC,EAAYL,EAAMI,CAAO,EACzBE,EAAcL,EAAWG,CAAO,IAAI,CAAC,GAAG,eAAiB,QAC/D,QAASG,EAAQ,EAAGA,EAAQjB,EAAyB,EAAEiB,EAAO,CAC7D,IAAMC,EAAWH,EAAUE,CAAK,EAC1BE,GAAWd,EAA4BS,EAAUZ,EAAiBe,GAAS,EACjFL,EAAKO,CAAO,EAAID,EAAS,EACzBN,EAAKO,EAAU,CAAC,EAAI,EAAID,EAAS,EACjCN,EAAKO,EAAU,CAAC,EAAID,EAAS,GAAK,EAClCN,EAAKO,EAAU,CAAC,EAAIH,EAAc,EAAI,CACvC,CAEA,IAAMI,EAAaC,EAA2BT,EAAME,EAASX,EAAuBD,EAAgBG,CAAyB,EACvHiB,GAAiBjB,EAA4BS,EAAUZ,EAAiBF,GAA2B,EACzGY,EAAKU,CAAa,EAAIF,EAAW,CAAC,EAClCR,EAAKU,EAAgB,CAAC,EAAIF,EAAW,CAAC,EACtCR,EAAKU,EAAgB,CAAC,EAAIF,EAAW,CAAC,EACtCR,EAAKU,EAAgB,CAAC,EAAIN,EAAc,EAAI,CAC7C,CAEAP,EAAS,MAAM,OAASI,CACzB,CAEA,SAASH,GAAMa,EAA+D,CAC7E,GAAM,CAAE,YAAAC,EAAa,QAAS,CAAE,QAAAC,EAAS,GAAGC,CAAiB,EAAI,CAAC,CAAE,EAAIH,EAClEI,EAAU,CAAE,GAAGrB,GAAuB,GAAGoB,CAAiB,EAC1DE,EAAaC,EAAY,CAAE,GAAGF,EAAS,YAAAH,CAAY,CAAC,EAEpDM,EAAgBH,EAAQ,SAAWzB,EAAiBG,EACpD0B,EAAgB,KAAK,KAAKD,EAAgB1B,CAAuB,EAEvE,OAAO,SAAU4B,EAAsBC,EAAwB,CAC9D,GAAM,CAAE,WAAAC,EAAY,GAAAC,EAAI,SAAAC,CAAS,EAAIH,EAG/BI,EADmB9B,EAAgB,IAAIqB,CAAU,GAEpC,UAAU,MAAQ,IAAI,aAAaxB,EAA0B2B,EAAgB,CAAC,EAC7FtB,EAA4B,KAC5B6B,EAAmB,GAEvB,SAASC,GAAW,CACnB,GAAI,CAAC9B,EAAU,OACf,GAAM,CAAE,OAAAI,CAAO,EAAIJ,EAAS,MACtB+B,EAAS3B,EAASX,EAAiBG,EACnCoC,EAAe,KAAK,KAAKD,EAASpC,CAAuB,EAC/D4B,EAAU,eACT,CACC,mBAAoB,CACnB,KAAMvB,EAAS,UAAU,KACzB,MAAOL,EACP,OAAQqC,EACR,UAAW,EACZ,CACD,EACA,CAAE,iBAAAH,CAAiB,CACpB,EACAN,EAAU,eAAe,CAAE,SAAUnB,CAAO,CAAC,EAC7CuB,EAAS,eAAgB3B,EAAS,MAAM,MAAM,CAC/C,CAEA,eAAeiC,GAAqB,CACnC,GAAInC,EAAgB,IAAIqB,CAAU,EACjCnB,EAAWF,EAAgB,IAAIqB,CAAU,MACnC,CACN,GAAM,CAACe,EAAW,CAAE,eAAAC,CAAe,CAAC,EAAI,MAAM,QAAQ,IAAI,CACzDC,EAAiB,EACjB,OAAO,yBAAyB,CACjC,CAAC,EACKC,EAAkB,IAAI,gBAAgB,EAAG,CAAC,EAchDrC,EAAW,CACV,WAdsB,MAAMmC,EAAe,kBAAkBD,EAAW,CACxE,YAAa,CACZ,eAAgBhB,EAAQ,UACxB,SAAU,KACX,EACA,OAAQmB,EACR,YAAa,QACb,SAAUnB,EAAQ,SAClB,2BAA4BA,EAAQ,2BACpC,0BAA2BA,EAAQ,0BACnC,sBAAuBA,EAAQ,qBAChC,CAAC,EAIA,OAAQmB,EACR,YAAa,IAAI,IACjB,SAAUnB,EAAQ,SAClB,MAAO,CACN,YAAa,QACb,OAAQ,KACR,UAAW,GACX,gBAAiB,EACjB,OAAQ,KACR,QAAS,QAAQ,QAAQ,EACzB,OAAQ,CACT,EACA,UAAW,CACV,KAAMU,EACN,cAAAN,CACD,CACD,EACAxB,EAAgB,IAAIqB,EAAYnB,CAAQ,CACzC,CAEAA,EAAS,YAAY,IAAI8B,EAAU,EAAK,CACzC,CACA,IAAMQ,EAAcL,EAAmB,EAEvCV,EAAU,GAAG,OAAQ,IAAM,CAC1BA,EAAU,kBAAkB,aAAc,MAAOL,EAAQ,QAAQ,EACjEK,EAAU,kBAAkB,WAAY,MAAO,CAAC,EAChDA,EAAU,kBACT,qBACA,CAAE,KAAMK,EAAe,MAAOjC,EAAyB,OAAQ2B,CAAc,EAC7E,CAAE,eAAgBI,EAAG,QAAS,KAAMA,EAAG,MAAO,UAAWA,EAAG,QAAS,UAAWA,EAAG,QAAS,QAAAV,CAAQ,CACrG,EACAsB,EAAY,KAAK,IAAMX,EAAS,aAAa,CAAC,CAC/C,CAAC,EAEDJ,EAAU,GAAG,oBAAqB,CAACgB,EAAcC,IAA0B,CACtED,IAASxB,GAAe0B,EAAkBD,CAAM,GAAGE,EAAYF,CAAM,CAC1E,CAAC,EAEDjB,EAAU,GACT,iBACA,CAACoB,EAAwCzB,IAA6C,CACrF,IAAMsB,EAASG,EAAQ5B,CAAW,EAC9B0B,EAAkBD,CAAM,IAC3BX,EAAmBX,GAAS,kBAAoB,GAChDwB,EAAYF,CAAM,EAEpB,CACD,EAEA,IAAII,EAAkB,EACtB,eAAeF,EAAYF,EAAyB,CACnD,IAAMK,EAAM,YAAY,IAAI,EACtBC,EAAY,EAAEF,EACpB,MAAMN,EACDtC,IAELA,EAAS,MAAM,QAAUA,EAAS,MAAM,QAAQ,KAAK,SAAY,CAChE,GAAI8C,IAAcF,GAAmB,CAAC5C,EAAU,OAEhD,IAAM+C,EAAeP,aAAkB,iBAAmB,QAAU,QAChExC,EAAS,MAAM,cAAgB+C,IAClC/C,EAAS,MAAM,YAAc+C,EAC7B,MAAM/C,EAAS,WAAW,WAAW,CAAE,YAAa+C,CAAa,CAAC,GAGnE,IAAIC,EAAe,GAiBnB,GAfIR,IAAWxC,EAAS,MAAM,QAC7BA,EAAS,MAAM,OAASwC,EACxBxC,EAAS,MAAM,UAAY,GAC3BgD,EAAe,IACLR,aAAkB,iBACxBA,EAAO,cAAgBxC,EAAS,MAAM,YACzCA,EAAS,MAAM,UAAYwC,EAAO,YAClCQ,EAAe,IAEJR,aAAkB,kBAC1BK,EAAM7C,EAAS,MAAM,gBAAkB,IAC1CgD,EAAe,IAIbA,EAAc,CACjB,IAAIC,EACJ,GAAIT,aAAkB,iBAAkB,CACvC,GAAIA,EAAO,aAAe,GAAKA,EAAO,cAAgB,GAAKA,EAAO,WAAa,EAAG,OAClFS,EAASjD,EAAS,WAAW,eAAewC,EAAQK,CAAG,CACxD,KAAO,CACN,GAAIL,EAAO,QAAU,GAAKA,EAAO,SAAW,EAAG,OAC/CS,EAASjD,EAAS,WAAW,OAAOwC,CAAM,CAC3C,CAEA,GAAIS,EAAQ,CACXjD,EAAS,MAAM,gBAAkB6C,EACjC7C,EAAS,MAAM,OAASiD,EACxBlD,GAAoBC,EAAUiD,EAAO,UAAWA,EAAO,UAAU,EACjE,QAAWC,KAAMlD,EAAS,YAAY,KAAK,EAC1CkD,EAAG,EACHlD,EAAS,YAAY,IAAIkD,EAAI,EAAI,CAEnC,CACD,MAAWlD,EAAS,MAAM,QAAU,CAACA,EAAS,YAAY,IAAI8B,CAAQ,IACrEA,EAAS,EACT9B,EAAS,YAAY,IAAI8B,EAAU,EAAI,EAEzC,CAAC,EAED,MAAM9B,EAAS,MAAM,QACtB,CAEAuB,EAAU,GAAG,UAAW,IAAM,CACzBvB,IACHA,EAAS,YAAY,OAAO8B,CAAQ,EAChC9B,EAAS,YAAY,OAAS,IACjCA,EAAS,WAAW,MAAM,EAC1BF,EAAgB,OAAOqB,CAAU,IAGnCnB,EAAW,IACZ,CAAC,EAED,GAAM,CAAE,GAAAmD,EAAI,cAAAC,CAAc,EAAIC,EAAerC,CAAO,EAEpDS,EAAW;AAAA;AAAA;AAAA,yBAGYT,EAAU,QAAU,EAAE,uBAC5CA,EACG;AAAA,4CAEA,EACJ;AAAA;AAAA,EAEAmC,EACD,MACA,WACA,GACAnC,EACG;AAAA,4DACwDA,CAAO,OAAOA,CAAO;AAAA,6EAE7E;AAAA,qEAEJ,CAAC;AAAA,EACCmC,EACD,OACA,eACA,mCACA,WAAWvD,CAAyB,kBAAkBH,CAAc;AAAA,eACtDE,CAAuB;AAAA,eACvBA,CAAuB,IACpCqB,EACG;AAAA,4DACuDA,CAAO,OAAOA,CAAO;AAAA,gEAE5E;AAAA,wDAEJ,EACD,CAAC;AAAA,EACCmC,EAAG,QAAS,cAAe,gBAAiB,mCAAmCC,CAAa,MAAM,CAAC;AAAA,EACnGD,EAAG,QAAS,aAAc,gBAAiB,yCAAyCC,CAAa,MAAM,CAAC,EAAE,CAC3G,CACD,CAEA,IAAOE,GAAQrD","names":["hands_exports","__export","hands_default","__toCommonJS","dummyTexture","isMediaPipeSource","source","hashOptions","options","calculateBoundingBoxCenter","data","entityIdx","landmarkIndices","landmarkCount","offset","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","dataIdx","x","y","filesetPromise","getSharedFileset","FilesetResolver","generateGLSLFn","history","returnType","name","args","body","argsOnly","historyArgs","callArgs","STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","HAND_CENTER_LANDMARKS","LANDMARKS_TEXTURE_WIDTH","N_LANDMARK_METADATA_SLOTS","DEFAULT_HANDS_OPTIONS","sharedDetectors","updateLandmarksData","detector","hands","handedness","data","nHands","handIdx","landmarks","isRightHand","lmIdx","landmark","dataIdx","handCenter","calculateBoundingBoxCenter","handCenterIdx","config","textureName","history","mediapipeOptions","options","optionsKey","hashOptions","nLandmarksMax","textureHeight","shaderPad","context","injectGLSL","gl","emitHook","landmarksData","skipHistoryWrite","onResult","nSlots","rowsToUpdate","initializeDetector","mediaPipe","HandLandmarker","getSharedFileset","mediapipeCanvas","initPromise","name","source","isMediaPipeSource","detectHands","updates","nDetectionCalls","now","callOrder","requiredMode","shouldDetect","result","cb","fn","historyParams","generateGLSLFn","hands_default"]}
@@ -1,20 +1,19 @@
1
- var C=21,V=1,x=C+V,z=[0,0,5,9,13,17];function G(E){let{textureName:y,options:o}=E,S="https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task";return function(d,D){let{injectGLSL:O,gl:T}=D,s=null,M=null,R=-1,p="VIDEO",I=new Map,_=o?.maxHands??2,c=512,A=0,t=null;async function b(){try{let{FilesetResolver:e,HandLandmarker:n}=await import("@mediapipe/tasks-vision");M=await e.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"),s=await n.createFromOptions(M,{baseOptions:{modelAssetPath:o?.modelPath||S,delegate:"GPU"},canvas:new OffscreenCanvas(1,1),runningMode:p,numHands:o?.maxHands??2,minHandDetectionConfidence:o?.minHandDetectionConfidence??.5,minHandPresenceConfidence:o?.minHandPresenceConfidence??.5,minTrackingConfidence:o?.minTrackingConfidence??.5})}catch(e){throw console.error("[Hands Plugin] Failed to initialize:",e),e}}function F(e,n,m){let i=1/0,r=-1/0,a=1/0,g=-1/0,H=0,l=0;for(let U of m){let L=(n*x+U)*4,v=e[L],w=e[L+1];i=Math.min(i,v),r=Math.max(r,v),a=Math.min(a,w),g=Math.max(g,w),H+=e[L+2],l+=e[L+3]}let u=(i+r)/2,f=(a+g)/2,k=H/m.length,h=l/m.length;return[u,f,k,h]}function P(e,n){if(!t)return;let m=e.length,i=m*x;for(let a=0;a<m;++a){let g=e[a],H=n[a]?.[0]?.categoryName==="Right";for(let f=0;f<C;++f){let k=g[f],h=(a*x+f)*4;t[h]=k.x,t[h+1]=1-k.y,t[h+2]=k.z??0,t[h+3]=H?1:0}let l=F(t,a,z),u=(a*x+C)*4;t[u]=l[0],t[u+1]=l[1],t[u+2]=l[2],t[u+3]=H?1:0}let r=Math.ceil(i/c);d.updateTextures({u_handLandmarksTex:{data:t,width:c,height:r,isPartial:!0}})}function N(e){if(!e.landmarks||!t)return;let n=e.landmarks.length;P(e.landmarks,e.handedness),d.updateUniforms({u_nHands:n}),o?.onResults?.(e)}d.registerHook("init",async()=>{d.initializeUniform("u_maxHands","int",_),d.initializeUniform("u_nHands","int",0);let e=_*x;A=Math.ceil(e/c);let n=c*A*4;t=new Float32Array(n),d.initializeTexture("u_handLandmarksTex",{data:t,width:c,height:A},{internalFormat:T.RGBA32F,type:T.FLOAT,minFilter:T.NEAREST,magFilter:T.NEAREST}),await b(),o?.onReady?.()}),d.registerHook("updateTextures",async e=>{let n=e[y];if(!(!n||(I.get(y)!==n&&(R=-1),I.set(y,n),!s)))try{let i=n instanceof HTMLVideoElement?"VIDEO":"IMAGE";if(p!==i&&(p=i,await s.setOptions({runningMode:p})),n instanceof HTMLVideoElement){if(n.videoWidth===0||n.videoHeight===0||n.readyState<2)return;if(n.currentTime!==R){R=n.currentTime;let r=s.detectForVideo(n,performance.now());N(r)}}else if(n instanceof HTMLImageElement||n instanceof HTMLCanvasElement){if(n.width===0||n.height===0)return;let r=s.detect(n);N(r)}}catch(i){console.error("[Hands Plugin] Detection error:",i)}}),d.registerHook("destroy",()=>{s&&(s.close(),s=null),M=null,I.clear(),t=null}),O(`
1
+ import{b as M,c as E,d as P,e as F,f as w}from"../chunk-JRSBIGBN.mjs";var y=21,U=1,g=y+U,z=[0,0,5,9,13,17],c=512,k=1,V={modelPath:"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task",maxHands:2,minHandDetectionConfidence:.5,minHandPresenceConfidence:.5,minTrackingConfidence:.5},T=new Map;function K(L,u,r){let s=L.landmarks.data,t=u.length;s[0]=t;for(let a=0;a<t;++a){let _=u[a],f=r[a]?.[0]?.categoryName==="Right";for(let x=0;x<y;++x){let m=_[x],l=(k+a*g+x)*4;s[l]=m.x,s[l+1]=1-m.y,s[l+2]=m.z??0,s[l+3]=f?1:0}let i=P(s,a,z,g,k),h=(k+a*g+y)*4;s[h]=i[0],s[h+1]=i[1],s[h+2]=i[2],s[h+3]=f?1:0}L.state.nHands=t}function G(L){let{textureName:u,options:{history:r,...s}={}}=L,t={...V,...s},a=E({...t,textureName:u}),_=t.maxHands*g+k,f=Math.ceil(_/c);return function(i,h){let{injectGLSL:x,gl:m,emitHook:l}=h,O=T.get(a)?.landmarks.data??new Float32Array(c*f*4),e=null,C=!1;function H(){if(!e)return;let{nHands:n}=e.state,d=n*g+k,o=Math.ceil(d/c);i.updateTextures({u_handLandmarksTex:{data:e.landmarks.data,width:c,height:o,isPartial:!0}},{skipHistoryWrite:C}),i.updateUniforms({u_nHands:n}),l("hands:result",e.state.result)}async function $(){if(T.has(a))e=T.get(a);else{let[n,{HandLandmarker:d}]=await Promise.all([F(),import("@mediapipe/tasks-vision")]),o=new OffscreenCanvas(1,1);e={landmarker:await d.createFromOptions(n,{baseOptions:{modelAssetPath:t.modelPath,delegate:"GPU"},canvas:o,runningMode:"VIDEO",numHands:t.maxHands,minHandDetectionConfidence:t.minHandDetectionConfidence,minHandPresenceConfidence:t.minHandPresenceConfidence,minTrackingConfidence:t.minTrackingConfidence}),canvas:o,subscribers:new Map,maxHands:t.maxHands,state:{runningMode:"VIDEO",source:null,videoTime:-1,resultTimestamp:0,result:null,pending:Promise.resolve(),nHands:0},landmarks:{data:O,textureHeight:f}},T.set(a,e)}e.subscribers.set(H,!1)}let S=$();i.on("init",()=>{i.initializeUniform("u_maxHands","int",t.maxHands),i.initializeUniform("u_nHands","int",0),i.initializeTexture("u_handLandmarksTex",{data:O,width:c,height:f},{internalFormat:m.RGBA32F,type:m.FLOAT,minFilter:m.NEAREST,magFilter:m.NEAREST,history:r}),S.then(()=>l("hands:ready"))}),i.on("initializeTexture",(n,d)=>{n===u&&M(d)&&v(d)}),i.on("updateTextures",(n,d)=>{let o=n[u];M(o)&&(C=d?.skipHistoryWrite??!1,v(o))});let N=0;async function v(n){let d=performance.now(),o=++N;await S,e&&(e.state.pending=e.state.pending.then(async()=>{if(o!==N||!e)return;let A=n instanceof HTMLVideoElement?"VIDEO":"IMAGE";e.state.runningMode!==A&&(e.state.runningMode=A,await e.landmarker.setOptions({runningMode:A}));let b=!1;if(n!==e.state.source?(e.state.source=n,e.state.videoTime=-1,b=!0):n instanceof HTMLVideoElement?n.currentTime!==e.state.videoTime&&(e.state.videoTime=n.currentTime,b=!0):n instanceof HTMLImageElement||d-e.state.resultTimestamp>2&&(b=!0),b){let p;if(n instanceof HTMLVideoElement){if(n.videoWidth===0||n.videoHeight===0||n.readyState<2)return;p=e.landmarker.detectForVideo(n,d)}else{if(n.width===0||n.height===0)return;p=e.landmarker.detect(n)}if(p){e.state.resultTimestamp=d,e.state.result=p,K(e,p.landmarks,p.handedness);for(let R of e.subscribers.keys())R(),e.subscribers.set(R,!0)}}else e.state.result&&!e.subscribers.get(H)&&(H(),e.subscribers.set(H,!0))}),await e.state.pending)}i.on("destroy",()=>{e&&(e.subscribers.delete(H),e.subscribers.size===0&&(e.landmarker.close(),T.delete(a))),e=null});let{fn:D,historyParams:I}=w(r);x(`
2
2
  uniform int u_maxHands;
3
3
  uniform int u_nHands;
4
- uniform sampler2D u_handLandmarksTex;
4
+ uniform highp sampler2D${r?"Array":""} u_handLandmarksTex;${r?`
5
+ uniform int u_handLandmarksTexFrameOffset;`:""}
5
6
 
6
- vec4 handLandmark(int handIndex, int landmarkIndex) {
7
- int i = handIndex * ${x} + landmarkIndex;
7
+ ${D("int","nHandsAt","",r?`
8
+ int layer = (u_handLandmarksTexFrameOffset - framesAgo + ${r}) % ${r};
9
+ return int(texelFetch(u_handLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`:`
10
+ return int(texelFetch(u_handLandmarksTex, ivec2(0, 0), 0).r + 0.5);`)}
11
+ ${D("vec4","handLandmark","int handIndex, int landmarkIndex",`int i = ${k} + handIndex * ${g} + landmarkIndex;
8
12
  int x = i % ${c};
9
- int y = i / ${c};
10
- return texelFetch(u_handLandmarksTex, ivec2(x, y), 0);
11
- }
12
-
13
- float isRightHand(int handIndex) {
14
- return handLandmark(handIndex, 0).w;
15
- }
16
-
17
- float isLeftHand(int handIndex) {
18
- return 1.0 - handLandmark(handIndex, 0).w;
19
- }`)}}var K=G;export{K as default};
13
+ int y = i / ${c};${r?`
14
+ int layer = (u_handLandmarksTexFrameOffset - framesAgo + ${r}) % ${r};
15
+ return texelFetch(u_handLandmarksTex, ivec3(x, y, layer), 0);`:`
16
+ return texelFetch(u_handLandmarksTex, ivec2(x, y), 0);`}`)}
17
+ ${D("float","isRightHand","int handIndex",`return handLandmark(handIndex, 0${I}).w;`)}
18
+ ${D("float","isLeftHand","int handIndex",`return 1.0 - handLandmark(handIndex, 0${I}).w;`)}`)}}var j=G;export{j as default};
20
19
  //# sourceMappingURL=hands.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/hands.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { HandLandmarker, HandLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface HandsPluginOptions {\n\tmodelPath?: string;\n\tmaxHands?: number;\n\tminHandDetectionConfidence?: number;\n\tminHandPresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\tonReady?: () => void;\n\tonResults?: (results: HandLandmarkerResult) => void;\n}\n\nconst STANDARD_LANDMARK_COUNT = 21; // See https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker#models.\nconst CUSTOM_LANDMARK_COUNT = 1;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\nconst HAND_CENTER_LANDMARKS = [0, 0, 5, 9, 13, 17] as const; // Wrist + MCP joints, weighted toward wrist.\n\nfunction hands(config: { textureName: string; options?: HandsPluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, gl } = context;\n\n\t\tlet handLandmarker: HandLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tlet runningMode: 'IMAGE' | 'VIDEO' = 'VIDEO';\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxHands = options?.maxHands ?? 2;\n\n\t\tconst LANDMARKS_TEXTURE_WIDTH = 512;\n\t\tlet landmarksTextureHeight = 0;\n\t\tlet landmarksDataArray: Float32Array | null = null;\n\n\t\tasync function initializeHandLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, HandLandmarker } = await import('@mediapipe/tasks-vision');\n\t\t\t\tvision = await FilesetResolver.forVisionTasks(\n\t\t\t\t\t'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n\t\t\t\t);\n\n\t\t\t\thandLandmarker = await HandLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\tcanvas: new OffscreenCanvas(1, 1),\n\t\t\t\t\trunningMode: runningMode,\n\t\t\t\t\tnumHands: options?.maxHands ?? 2,\n\t\t\t\t\tminHandDetectionConfidence: options?.minHandDetectionConfidence ?? 0.5,\n\t\t\t\t\tminHandPresenceConfidence: options?.minHandPresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Hands Plugin] Failed to initialize:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tfunction calculateBoundingBoxCenter(\n\t\t\tlandmarksDataArray: Float32Array,\n\t\t\thandIdx: number,\n\t\t\tlandmarkIndices: readonly number[]\n\t\t): [number, number, number, number] {\n\t\t\tlet minX = Infinity,\n\t\t\t\tmaxX = -Infinity,\n\t\t\t\tminY = Infinity,\n\t\t\t\tmaxY = -Infinity,\n\t\t\t\tavgZ = 0,\n\t\t\t\tavgVisibility = 0;\n\n\t\t\tfor (const idx of landmarkIndices) {\n\t\t\t\tconst dataIdx = (handIdx * LANDMARK_COUNT + idx) * 4;\n\t\t\t\tconst x = landmarksDataArray[dataIdx];\n\t\t\t\tconst y = landmarksDataArray[dataIdx + 1];\n\t\t\t\tminX = Math.min(minX, x);\n\t\t\t\tmaxX = Math.max(maxX, x);\n\t\t\t\tminY = Math.min(minY, y);\n\t\t\t\tmaxY = Math.max(maxY, y);\n\t\t\t\tavgZ += landmarksDataArray[dataIdx + 2];\n\t\t\t\tavgVisibility += landmarksDataArray[dataIdx + 3];\n\t\t\t}\n\n\t\t\tconst centerX = (minX + maxX) / 2;\n\t\t\tconst centerY = (minY + maxY) / 2;\n\t\t\tconst centerZ = avgZ / landmarkIndices.length;\n\t\t\tconst centerVisibility = avgVisibility / landmarkIndices.length;\n\t\t\treturn [centerX, centerY, centerZ, centerVisibility];\n\t\t}\n\n\t\tfunction updateLandmarksTexture(hands: NormalizedLandmark[][], handedness: { categoryName: string }[][]) {\n\t\t\tif (!landmarksDataArray) return;\n\n\t\t\tconst nHands = hands.length;\n\t\t\tconst totalLandmarks = nHands * LANDMARK_COUNT;\n\n\t\t\tfor (let handIdx = 0; handIdx < nHands; ++handIdx) {\n\t\t\t\tconst landmarks = hands[handIdx];\n\t\t\t\tconst isRightHand = handedness[handIdx]?.[0]?.categoryName === 'Right';\n\t\t\t\tfor (let lmIdx = 0; lmIdx < STANDARD_LANDMARK_COUNT; ++lmIdx) {\n\t\t\t\t\tconst landmark = landmarks[lmIdx];\n\t\t\t\t\tconst dataIdx = (handIdx * LANDMARK_COUNT + lmIdx) * 4;\n\t\t\t\t\tlandmarksDataArray[dataIdx] = landmark.x;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 1] = 1 - landmark.y;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 2] = landmark.z ?? 0;\n\t\t\t\t\tlandmarksDataArray[dataIdx + 3] = isRightHand ? 1 : 0;\n\t\t\t\t}\n\n\t\t\t\tconst handCenter = calculateBoundingBoxCenter(landmarksDataArray, handIdx, HAND_CENTER_LANDMARKS);\n\t\t\t\tconst handCenterIdx = (handIdx * LANDMARK_COUNT + STANDARD_LANDMARK_COUNT) * 4;\n\t\t\t\tlandmarksDataArray[handCenterIdx] = handCenter[0];\n\t\t\t\tlandmarksDataArray[handCenterIdx + 1] = handCenter[1];\n\t\t\t\tlandmarksDataArray[handCenterIdx + 2] = handCenter[2];\n\t\t\t\tlandmarksDataArray[handCenterIdx + 3] = isRightHand ? 1 : 0;\n\t\t\t}\n\n\t\t\tconst rowsToUpdate = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tshaderPad.updateTextures({\n\t\t\t\tu_handLandmarksTex: {\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\tisPartial: true,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tfunction processHandResults(result: HandLandmarkerResult) {\n\t\t\tif (!result.landmarks || !landmarksDataArray) return;\n\n\t\t\tconst nHands = result.landmarks.length;\n\t\t\tupdateLandmarksTexture(result.landmarks, result.handedness);\n\t\t\tshaderPad.updateUniforms({ u_nHands: nHands });\n\n\t\t\toptions?.onResults?.(result);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeUniform('u_maxHands', 'int', maxHands);\n\t\t\tshaderPad.initializeUniform('u_nHands', 'int', 0);\n\n\t\t\tconst totalLandmarks = maxHands * LANDMARK_COUNT;\n\t\t\tlandmarksTextureHeight = Math.ceil(totalLandmarks / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tconst textureSize = LANDMARKS_TEXTURE_WIDTH * landmarksTextureHeight * 4;\n\t\t\tlandmarksDataArray = new Float32Array(textureSize);\n\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_handLandmarksTex',\n\t\t\t\t{\n\t\t\t\t\tdata: landmarksDataArray,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: landmarksTextureHeight,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tinternalFormat: gl.RGBA32F,\n\t\t\t\t\ttype: gl.FLOAT,\n\t\t\t\t\tminFilter: gl.NEAREST,\n\t\t\t\t\tmagFilter: gl.NEAREST,\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tawait initializeHandLandmarker();\n\t\t\toptions?.onReady?.();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', async (updates: Record<string, TextureSource>) => {\n\t\t\tconst source = updates[textureName];\n\t\t\tif (!source) return;\n\n\t\t\tconst previousSource = textureSources.get(textureName);\n\t\t\tif (previousSource !== source) {\n\t\t\t\tlastVideoTime = -1;\n\t\t\t}\n\n\t\t\ttextureSources.set(textureName, source);\n\t\t\tif (!handLandmarker) return;\n\n\t\t\ttry {\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (runningMode !== requiredMode) {\n\t\t\t\t\trunningMode = requiredMode;\n\t\t\t\t\tawait handLandmarker.setOptions({ runningMode: runningMode });\n\t\t\t\t}\n\n\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\tconst result = handLandmarker.detectForVideo(source, performance.now());\n\t\t\t\t\t\tprocessHandResults(result);\n\t\t\t\t\t}\n\t\t\t\t} else if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement) {\n\t\t\t\t\tif (source.width === 0 || source.height === 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst result = handLandmarker.detect(source);\n\t\t\t\t\tprocessHandResults(result);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Hands Plugin] Detection error:', error);\n\t\t\t}\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (handLandmarker) {\n\t\t\t\thandLandmarker.close();\n\t\t\t\thandLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tlandmarksDataArray = null;\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxHands;\nuniform int u_nHands;\nuniform sampler2D u_handLandmarksTex;\n\nvec4 handLandmark(int handIndex, int landmarkIndex) {\n\tint i = handIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};\n\treturn texelFetch(u_handLandmarksTex, ivec2(x, y), 0);\n}\n\nfloat isRightHand(int handIndex) {\n\treturn handLandmark(handIndex, 0).w;\n}\n\nfloat isLeftHand(int handIndex) {\n\treturn 1.0 - handLandmark(handIndex, 0).w;\n}`);\n\t};\n}\n\nexport default hands;\n"],"mappings":"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","names":["STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","HAND_CENTER_LANDMARKS","hands","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","gl","handLandmarker","vision","lastVideoTime","runningMode","textureSources","maxHands","LANDMARKS_TEXTURE_WIDTH","landmarksTextureHeight","landmarksDataArray","initializeHandLandmarker","FilesetResolver","HandLandmarker","error","calculateBoundingBoxCenter","handIdx","landmarkIndices","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","dataIdx","x","y","centerX","centerY","centerZ","centerVisibility","updateLandmarksTexture","handedness","nHands","totalLandmarks","landmarks","isRightHand","lmIdx","landmark","handCenter","handCenterIdx","rowsToUpdate","processHandResults","result","textureSize","updates","source","requiredMode","hands_default"]}
1
+ {"version":3,"sources":["../../src/plugins/hands.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '..';\nimport {\n\tcalculateBoundingBoxCenter,\n\tgenerateGLSLFn,\n\tgetSharedFileset,\n\thashOptions,\n\tisMediaPipeSource,\n\tMediaPipeSource,\n} from './mediapipe-common';\nimport type { HandLandmarker, HandLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface HandsPluginOptions {\n\tmodelPath?: string;\n\tmaxHands?: number;\n\tminHandDetectionConfidence?: number;\n\tminHandPresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\thistory?: number;\n}\n\nconst STANDARD_LANDMARK_COUNT = 21; // See https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker#models.\nconst CUSTOM_LANDMARK_COUNT = 1;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\nconst HAND_CENTER_LANDMARKS = [0, 0, 5, 9, 13, 17] as const; // Wrist + MCP joints, weighted toward wrist.\nconst LANDMARKS_TEXTURE_WIDTH = 512;\nconst N_LANDMARK_METADATA_SLOTS = 1;\n\nconst DEFAULT_HANDS_OPTIONS: Required<Omit<HandsPluginOptions, 'history'>> = {\n\tmodelPath:\n\t\t'https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task',\n\tmaxHands: 2,\n\tminHandDetectionConfidence: 0.5,\n\tminHandPresenceConfidence: 0.5,\n\tminTrackingConfidence: 0.5,\n};\n\ninterface Detector {\n\tlandmarker: HandLandmarker;\n\tcanvas: OffscreenCanvas;\n\tsubscribers: Map<() => void, boolean>;\n\tmaxHands: number;\n\tstate: {\n\t\trunningMode: 'IMAGE' | 'VIDEO';\n\t\tsource: MediaPipeSource | null;\n\t\tvideoTime: number;\n\t\tresultTimestamp: number;\n\t\tresult: HandLandmarkerResult | null;\n\t\tpending: Promise<void>;\n\t\tnHands: number;\n\t};\n\tlandmarks: {\n\t\tdata: Float32Array;\n\t\ttextureHeight: number;\n\t};\n}\nconst sharedDetectors = new Map<string, Detector>();\n\nfunction updateLandmarksData(\n\tdetector: Detector,\n\thands: NormalizedLandmark[][],\n\thandedness: { categoryName: string }[][]\n) {\n\tconst data = detector.landmarks.data;\n\tconst nHands = hands.length;\n\n\tdata[0] = nHands;\n\n\tfor (let handIdx = 0; handIdx < nHands; ++handIdx) {\n\t\tconst landmarks = hands[handIdx];\n\t\tconst isRightHand = handedness[handIdx]?.[0]?.categoryName === 'Right';\n\t\tfor (let lmIdx = 0; lmIdx < STANDARD_LANDMARK_COUNT; ++lmIdx) {\n\t\t\tconst landmark = landmarks[lmIdx];\n\t\t\tconst dataIdx = (N_LANDMARK_METADATA_SLOTS + handIdx * LANDMARK_COUNT + lmIdx) * 4;\n\t\t\tdata[dataIdx] = landmark.x;\n\t\t\tdata[dataIdx + 1] = 1 - landmark.y;\n\t\t\tdata[dataIdx + 2] = landmark.z ?? 0;\n\t\t\tdata[dataIdx + 3] = isRightHand ? 1 : 0;\n\t\t}\n\n\t\tconst handCenter = calculateBoundingBoxCenter(data, handIdx, HAND_CENTER_LANDMARKS, LANDMARK_COUNT, N_LANDMARK_METADATA_SLOTS);\n\t\tconst handCenterIdx = (N_LANDMARK_METADATA_SLOTS + handIdx * LANDMARK_COUNT + STANDARD_LANDMARK_COUNT) * 4;\n\t\tdata[handCenterIdx] = handCenter[0];\n\t\tdata[handCenterIdx + 1] = handCenter[1];\n\t\tdata[handCenterIdx + 2] = handCenter[2];\n\t\tdata[handCenterIdx + 3] = isRightHand ? 1 : 0;\n\t}\n\n\tdetector.state.nHands = nHands;\n}\n\nfunction hands(config: { textureName: string; options?: HandsPluginOptions }) {\n\tconst { textureName, options: { history, ...mediapipeOptions } = {} } = config;\n\tconst options = { ...DEFAULT_HANDS_OPTIONS, ...mediapipeOptions };\n\tconst optionsKey = hashOptions({ ...options, textureName });\n\n\tconst nLandmarksMax = options.maxHands * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\tconst textureHeight = Math.ceil(nLandmarksMax / LANDMARKS_TEXTURE_WIDTH);\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, gl, emitHook } = context;\n\n\t\tconst existingDetector = sharedDetectors.get(optionsKey);\n\t\tconst landmarksData =\n\t\t\texistingDetector?.landmarks.data ?? new Float32Array(LANDMARKS_TEXTURE_WIDTH * textureHeight * 4);\n\t\tlet detector: Detector | null = null;\n\t\tlet skipHistoryWrite = false;\n\n\t\tfunction onResult() {\n\t\t\tif (!detector) return;\n\t\t\tconst { nHands } = detector.state;\n\t\t\tconst nSlots = nHands * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\t\t\tconst rowsToUpdate = Math.ceil(nSlots / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tshaderPad.updateTextures(\n\t\t\t\t{\n\t\t\t\t\tu_handLandmarksTex: {\n\t\t\t\t\t\tdata: detector.landmarks.data,\n\t\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\t\tisPartial: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ skipHistoryWrite }\n\t\t\t);\n\t\t\tshaderPad.updateUniforms({ u_nHands: nHands });\n\t\t\temitHook('hands:result', detector.state.result);\n\t\t}\n\n\t\tasync function initializeDetector() {\n\t\t\tif (sharedDetectors.has(optionsKey)) {\n\t\t\t\tdetector = sharedDetectors.get(optionsKey)!;\n\t\t\t} else {\n\t\t\t\tconst [mediaPipe, { HandLandmarker }] = await Promise.all([\n\t\t\t\t\tgetSharedFileset(),\n\t\t\t\t\timport('@mediapipe/tasks-vision'),\n\t\t\t\t]);\n\t\t\t\tconst mediapipeCanvas = new OffscreenCanvas(1, 1);\n\t\t\t\tconst handLandmarker = await HandLandmarker.createFromOptions(mediaPipe, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options.modelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\tcanvas: mediapipeCanvas,\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumHands: options.maxHands,\n\t\t\t\t\tminHandDetectionConfidence: options.minHandDetectionConfidence,\n\t\t\t\t\tminHandPresenceConfidence: options.minHandPresenceConfidence,\n\t\t\t\t\tminTrackingConfidence: options.minTrackingConfidence,\n\t\t\t\t});\n\n\t\t\t\tdetector = {\n\t\t\t\t\tlandmarker: handLandmarker,\n\t\t\t\t\tcanvas: mediapipeCanvas,\n\t\t\t\t\tsubscribers: new Map(),\n\t\t\t\t\tmaxHands: options.maxHands,\n\t\t\t\t\tstate: {\n\t\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\t\tsource: null,\n\t\t\t\t\t\tvideoTime: -1,\n\t\t\t\t\t\tresultTimestamp: 0,\n\t\t\t\t\t\tresult: null,\n\t\t\t\t\t\tpending: Promise.resolve(),\n\t\t\t\t\t\tnHands: 0,\n\t\t\t\t\t},\n\t\t\t\t\tlandmarks: {\n\t\t\t\t\t\tdata: landmarksData,\n\t\t\t\t\t\ttextureHeight,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\tsharedDetectors.set(optionsKey, detector);\n\t\t\t}\n\n\t\t\tdetector.subscribers.set(onResult, false);\n\t\t}\n\t\tconst initPromise = initializeDetector();\n\n\t\tshaderPad.on('init', () => {\n\t\t\tshaderPad.initializeUniform('u_maxHands', 'int', options.maxHands);\n\t\t\tshaderPad.initializeUniform('u_nHands', 'int', 0);\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_handLandmarksTex',\n\t\t\t\t{ data: landmarksData, width: LANDMARKS_TEXTURE_WIDTH, height: textureHeight },\n\t\t\t\t{ internalFormat: gl.RGBA32F, type: gl.FLOAT, minFilter: gl.NEAREST, magFilter: gl.NEAREST, history }\n\t\t\t);\n\t\t\tinitPromise.then(() => emitHook('hands:ready'));\n\t\t});\n\n\t\tshaderPad.on('initializeTexture', (name: string, source: TextureSource) => {\n\t\t\tif (name === textureName && isMediaPipeSource(source)) detectHands(source);\n\t\t});\n\n\t\tshaderPad.on(\n\t\t\t'updateTextures',\n\t\t\t(updates: Record<string, TextureSource>, options?: { skipHistoryWrite?: boolean }) => {\n\t\t\t\tconst source = updates[textureName];\n\t\t\t\tif (isMediaPipeSource(source)) {\n\t\t\t\t\tskipHistoryWrite = options?.skipHistoryWrite ?? false;\n\t\t\t\t\tdetectHands(source);\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\n\t\tlet nDetectionCalls = 0;\n\t\tasync function detectHands(source: MediaPipeSource) {\n\t\t\tconst now = performance.now();\n\t\t\tconst callOrder = ++nDetectionCalls;\n\t\t\tawait initPromise;\n\t\t\tif (!detector) return;\n\n\t\t\tdetector.state.pending = detector.state.pending.then(async () => {\n\t\t\t\tif (callOrder !== nDetectionCalls || !detector) return;\n\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (detector.state.runningMode !== requiredMode) {\n\t\t\t\t\tdetector.state.runningMode = requiredMode;\n\t\t\t\t\tawait detector.landmarker.setOptions({ runningMode: requiredMode });\n\t\t\t\t}\n\n\t\t\t\tlet shouldDetect = false;\n\n\t\t\t\tif (source !== detector.state.source) {\n\t\t\t\t\tdetector.state.source = source;\n\t\t\t\t\tdetector.state.videoTime = -1;\n\t\t\t\t\tshouldDetect = true;\n\t\t\t\t} else if (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.currentTime !== detector.state.videoTime) {\n\t\t\t\t\t\tdetector.state.videoTime = source.currentTime;\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t} else if (!(source instanceof HTMLImageElement)) {\n\t\t\t\t\tif (now - detector.state.resultTimestamp > 2) {\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (shouldDetect) {\n\t\t\t\t\tlet result: HandLandmarkerResult | undefined;\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) return;\n\t\t\t\t\t\tresult = detector.landmarker.detectForVideo(source, now);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (source.width === 0 || source.height === 0) return;\n\t\t\t\t\t\tresult = detector.landmarker.detect(source);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\tdetector.state.resultTimestamp = now;\n\t\t\t\t\t\tdetector.state.result = result;\n\t\t\t\t\t\tupdateLandmarksData(detector, result.landmarks, result.handedness);\n\t\t\t\t\t\tfor (const cb of detector.subscribers.keys()) {\n\t\t\t\t\t\t\tcb();\n\t\t\t\t\t\t\tdetector.subscribers.set(cb, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (detector.state.result && !detector.subscribers.get(onResult)) {\n\t\t\t\t\tonResult();\n\t\t\t\t\tdetector.subscribers.set(onResult, true);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait detector.state.pending;\n\t\t}\n\n\t\tshaderPad.on('destroy', () => {\n\t\t\tif (detector) {\n\t\t\t\tdetector.subscribers.delete(onResult);\n\t\t\t\tif (detector.subscribers.size === 0) {\n\t\t\t\t\tdetector.landmarker.close();\n\t\t\t\t\tsharedDetectors.delete(optionsKey);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdetector = null;\n\t\t});\n\n\t\tconst { fn, historyParams } = generateGLSLFn(history);\n\n\t\tinjectGLSL(`\nuniform int u_maxHands;\nuniform int u_nHands;\nuniform highp sampler2D${history ? 'Array' : ''} u_handLandmarksTex;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_handLandmarksTexFrameOffset;`\n\t\t\t\t: ''\n\t\t}\n\n${fn(\n\t'int',\n\t'nHandsAt',\n\t'',\n\thistory\n\t\t? `\n\tint layer = (u_handLandmarksTexFrameOffset - framesAgo + ${history}) % ${history};\n\treturn int(texelFetch(u_handLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`\n\t\t: `\n\treturn int(texelFetch(u_handLandmarksTex, ivec2(0, 0), 0).r + 0.5);`\n)}\n${fn(\n\t'vec4',\n\t'handLandmark',\n\t'int handIndex, int landmarkIndex',\n\t`int i = ${N_LANDMARK_METADATA_SLOTS} + handIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};${\n\t\thistory\n\t\t\t? `\n\tint layer = (u_handLandmarksTexFrameOffset - framesAgo + ${history}) % ${history};\n\treturn texelFetch(u_handLandmarksTex, ivec3(x, y, layer), 0);`\n\t\t\t: `\n\treturn texelFetch(u_handLandmarksTex, ivec2(x, y), 0);`\n\t}`\n)}\n${fn('float', 'isRightHand', 'int handIndex', `return handLandmark(handIndex, 0${historyParams}).w;`)}\n${fn('float', 'isLeftHand', 'int handIndex', `return 1.0 - handLandmark(handIndex, 0${historyParams}).w;`)}`);\n\t};\n}\n\nexport default hands;\n"],"mappings":"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","names":["STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","HAND_CENTER_LANDMARKS","LANDMARKS_TEXTURE_WIDTH","N_LANDMARK_METADATA_SLOTS","DEFAULT_HANDS_OPTIONS","sharedDetectors","updateLandmarksData","detector","hands","handedness","data","nHands","handIdx","landmarks","isRightHand","lmIdx","landmark","dataIdx","handCenter","calculateBoundingBoxCenter","handCenterIdx","config","textureName","history","mediapipeOptions","options","optionsKey","hashOptions","nLandmarksMax","textureHeight","shaderPad","context","injectGLSL","gl","emitHook","landmarksData","skipHistoryWrite","onResult","nSlots","rowsToUpdate","initializeDetector","mediaPipe","HandLandmarker","getSharedFileset","mediapipeCanvas","initPromise","name","source","isMediaPipeSource","detectHands","updates","nDetectionCalls","now","callOrder","requiredMode","shouldDetect","result","cb","fn","historyParams","generateGLSLFn","hands_default"]}
@@ -1,4 +1,4 @@
1
- "use strict";var o=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var s=Object.prototype.hasOwnProperty;var x=(t,e)=>{for(var i in e)o(t,i,{get:e[i],enumerable:!0})},f=(t,e,i,u)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of c(e))!s.call(t,r)&&r!==i&&o(t,r,{get:()=>e[r],enumerable:!(u=a(e,r))||u.enumerable});return t};var l=t=>f(o({},"__esModule",{value:!0}),t);var p={};x(p,{default:()=>h});module.exports=l(p);var n=`uniform vec2 u_resolution;
1
+ "use strict";var i=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var a=Object.getOwnPropertyNames;var s=Object.prototype.hasOwnProperty;var x=(t,e)=>{for(var o in e)i(t,o,{get:e[o],enumerable:!0})},f=(t,e,o,u)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of a(e))!s.call(t,r)&&r!==o&&i(t,r,{get:()=>e[r],enumerable:!(u=c(e,r))||u.enumerable});return t};var l=t=>f(i({},"__esModule",{value:!0}),t);var p={};x(p,{default:()=>h});module.exports=l(p);var n=`uniform vec2 u_resolution;
2
2
 
3
3
  // Apply aspect ratio correction (object-fit: contain)
4
4
  vec2 fitContain(vec2 uv, vec2 textureSize) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/helpers.ts","../../src/plugins/helpers.glsl"],"sourcesContent":["import ShaderPad, { PluginContext } from '../index';\nimport helpersGLSL from './helpers.glsl';\n\nfunction helpers() {\n\treturn function (_shader: ShaderPad, context: PluginContext) {\n\t\tcontext.injectGLSL(helpersGLSL);\n\t};\n}\n\nexport default helpers;\n","uniform vec2 u_resolution;\n\n// Apply aspect ratio correction (object-fit: contain)\nvec2 fitContain(vec2 uv, vec2 textureSize) {\n\tvec2 scale = u_resolution.xy * textureSize.yx / (u_resolution.yx * textureSize.xy);\n\treturn (uv - 0.5) * max(scale, vec2(1.0)) + 0.5;\n}\n\n// Apply aspect ratio correction (object-fit: cover)\nvec2 fitCover(vec2 uv, vec2 textureSize) {\n\tvec2 scale = u_resolution.xy * textureSize.yx / (u_resolution.yx * textureSize.xy);\n\treturn (uv - 0.5) * min(scale, vec2(1.0)) + 0.5;\n}\n\n// Get the array index for a history texture\nfloat historyZ(highp sampler2DArray tex, int frameOffset, int framesAgo) {\n\tint historyDepth = textureSize(tex, 0).z;\n\tint z = (historyDepth + frameOffset - framesAgo) % historyDepth;\n\treturn float(z);\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,IAAA,eAAAC,EAAAH,GCAA,IAAAI,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EDGA,SAASC,GAAU,CAClB,OAAO,SAAUC,EAAoBC,EAAwB,CAC5DA,EAAQ,WAAWC,CAAW,CAC/B,CACD,CAEA,IAAOA,EAAQH","names":["helpers_exports","__export","helpers_default","__toCommonJS","helpers_default","helpers","_shader","context","helpers_default"]}
1
+ {"version":3,"sources":["../../src/plugins/helpers.ts","../../src/plugins/helpers.glsl"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '..';\nimport helpersGLSL from './helpers.glsl';\n\nfunction helpers() {\n\treturn function (_shader: ShaderPad, context: PluginContext) {\n\t\tcontext.injectGLSL(helpersGLSL);\n\t};\n}\n\nexport default helpers;\n","uniform vec2 u_resolution;\n\n// Apply aspect ratio correction (object-fit: contain)\nvec2 fitContain(vec2 uv, vec2 textureSize) {\n\tvec2 scale = u_resolution.xy * textureSize.yx / (u_resolution.yx * textureSize.xy);\n\treturn (uv - 0.5) * max(scale, vec2(1.0)) + 0.5;\n}\n\n// Apply aspect ratio correction (object-fit: cover)\nvec2 fitCover(vec2 uv, vec2 textureSize) {\n\tvec2 scale = u_resolution.xy * textureSize.yx / (u_resolution.yx * textureSize.xy);\n\treturn (uv - 0.5) * min(scale, vec2(1.0)) + 0.5;\n}\n\n// Get the array index for a history texture\nfloat historyZ(highp sampler2DArray tex, int frameOffset, int framesAgo) {\n\tint historyDepth = textureSize(tex, 0).z;\n\tint z = (historyDepth + frameOffset - framesAgo) % historyDepth;\n\treturn float(z);\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,IAAA,eAAAC,EAAAH,GCAA,IAAAI,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EDGA,SAASC,GAAU,CAClB,OAAO,SAAUC,EAAoBC,EAAwB,CAC5DA,EAAQ,WAAWC,CAAW,CAC/B,CACD,CAEA,IAAOA,EAAQH","names":["helpers_exports","__export","helpers_default","__toCommonJS","helpers_default","helpers","_shader","context","helpers_default"]}
@@ -18,5 +18,5 @@ float historyZ(highp sampler2DArray tex, int frameOffset, int framesAgo) {
18
18
  int z = (historyDepth + frameOffset - framesAgo) % historyDepth;
19
19
  return float(z);
20
20
  }
21
- `;function i(){return function(o,t){t.injectGLSL(e)}}var a=i;export{a as default};
21
+ `;function o(){return function(i,t){t.injectGLSL(e)}}var c=o;export{c as default};
22
22
  //# sourceMappingURL=helpers.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/helpers.glsl","../../src/plugins/helpers.ts"],"sourcesContent":["uniform vec2 u_resolution;\n\n// Apply aspect ratio correction (object-fit: contain)\nvec2 fitContain(vec2 uv, vec2 textureSize) {\n\tvec2 scale = u_resolution.xy * textureSize.yx / (u_resolution.yx * textureSize.xy);\n\treturn (uv - 0.5) * max(scale, vec2(1.0)) + 0.5;\n}\n\n// Apply aspect ratio correction (object-fit: cover)\nvec2 fitCover(vec2 uv, vec2 textureSize) {\n\tvec2 scale = u_resolution.xy * textureSize.yx / (u_resolution.yx * textureSize.xy);\n\treturn (uv - 0.5) * min(scale, vec2(1.0)) + 0.5;\n}\n\n// Get the array index for a history texture\nfloat historyZ(highp sampler2DArray tex, int frameOffset, int framesAgo) {\n\tint historyDepth = textureSize(tex, 0).z;\n\tint z = (historyDepth + frameOffset - framesAgo) % historyDepth;\n\treturn float(z);\n}\n","import ShaderPad, { PluginContext } from '../index';\nimport helpersGLSL from './helpers.glsl';\n\nfunction helpers() {\n\treturn function (_shader: ShaderPad, context: PluginContext) {\n\t\tcontext.injectGLSL(helpersGLSL);\n\t};\n}\n\nexport default helpers;\n"],"mappings":"AAAA,IAAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ECGA,SAASC,GAAU,CAClB,OAAO,SAAUC,EAAoBC,EAAwB,CAC5DA,EAAQ,WAAWC,CAAW,CAC/B,CACD,CAEA,IAAOA,EAAQH","names":["helpers_default","helpers","_shader","context","helpers_default"]}
1
+ {"version":3,"sources":["../../src/plugins/helpers.glsl","../../src/plugins/helpers.ts"],"sourcesContent":["uniform vec2 u_resolution;\n\n// Apply aspect ratio correction (object-fit: contain)\nvec2 fitContain(vec2 uv, vec2 textureSize) {\n\tvec2 scale = u_resolution.xy * textureSize.yx / (u_resolution.yx * textureSize.xy);\n\treturn (uv - 0.5) * max(scale, vec2(1.0)) + 0.5;\n}\n\n// Apply aspect ratio correction (object-fit: cover)\nvec2 fitCover(vec2 uv, vec2 textureSize) {\n\tvec2 scale = u_resolution.xy * textureSize.yx / (u_resolution.yx * textureSize.xy);\n\treturn (uv - 0.5) * min(scale, vec2(1.0)) + 0.5;\n}\n\n// Get the array index for a history texture\nfloat historyZ(highp sampler2DArray tex, int frameOffset, int framesAgo) {\n\tint historyDepth = textureSize(tex, 0).z;\n\tint z = (historyDepth + frameOffset - framesAgo) % historyDepth;\n\treturn float(z);\n}\n","import ShaderPad, { PluginContext, TextureSource } from '..';\nimport helpersGLSL from './helpers.glsl';\n\nfunction helpers() {\n\treturn function (_shader: ShaderPad, context: PluginContext) {\n\t\tcontext.injectGLSL(helpersGLSL);\n\t};\n}\n\nexport default helpers;\n"],"mappings":"AAAA,IAAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ECGA,SAASC,GAAU,CAClB,OAAO,SAAUC,EAAoBC,EAAwB,CAC5DA,EAAQ,WAAWC,CAAW,CAC/B,CACD,CAEA,IAAOA,EAAQH","names":["helpers_default","helpers","_shader","context","helpers_default"]}
@@ -0,0 +1,18 @@
1
+ import { TextureSource } from '../index.mjs';
2
+
3
+ declare const dummyTexture: {
4
+ data: Uint8Array<ArrayBuffer>;
5
+ width: number;
6
+ height: number;
7
+ };
8
+ type MediaPipeSource = HTMLVideoElement | HTMLImageElement | HTMLCanvasElement | OffscreenCanvas;
9
+ declare function isMediaPipeSource(source: TextureSource): source is MediaPipeSource;
10
+ declare function hashOptions(options: object): string;
11
+ declare function calculateBoundingBoxCenter(data: Float32Array, entityIdx: number, landmarkIndices: readonly number[] | number[], landmarkCount: number, offset?: number): [number, number, number, number];
12
+ declare function getSharedFileset(): Promise<any>;
13
+ declare function generateGLSLFn(history: number | undefined): {
14
+ historyParams: string;
15
+ fn: (returnType: string, name: string, args: string, body: string) => string;
16
+ };
17
+
18
+ export { type MediaPipeSource, calculateBoundingBoxCenter, dummyTexture, generateGLSLFn, getSharedFileset, hashOptions, isMediaPipeSource };
@@ -0,0 +1,18 @@
1
+ import { TextureSource } from '../index.js';
2
+
3
+ declare const dummyTexture: {
4
+ data: Uint8Array<ArrayBuffer>;
5
+ width: number;
6
+ height: number;
7
+ };
8
+ type MediaPipeSource = HTMLVideoElement | HTMLImageElement | HTMLCanvasElement | OffscreenCanvas;
9
+ declare function isMediaPipeSource(source: TextureSource): source is MediaPipeSource;
10
+ declare function hashOptions(options: object): string;
11
+ declare function calculateBoundingBoxCenter(data: Float32Array, entityIdx: number, landmarkIndices: readonly number[] | number[], landmarkCount: number, offset?: number): [number, number, number, number];
12
+ declare function getSharedFileset(): Promise<any>;
13
+ declare function generateGLSLFn(history: number | undefined): {
14
+ historyParams: string;
15
+ fn: (returnType: string, name: string, args: string, body: string) => string;
16
+ };
17
+
18
+ export { type MediaPipeSource, calculateBoundingBoxCenter, dummyTexture, generateGLSLFn, getSharedFileset, hashOptions, isMediaPipeSource };
@@ -0,0 +1,7 @@
1
+ "use strict";var $=Object.create;var c=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var d=Object.getPrototypeOf,S=Object.prototype.hasOwnProperty;var I=(n,e)=>{for(var t in e)c(n,t,{get:e[t],enumerable:!0})},y=(n,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of b(e))!S.call(n,r)&&r!==t&&c(n,r,{get:()=>e[r],enumerable:!(s=M(e,r))||s.enumerable});return n};var P=(n,e,t)=>(t=n!=null?$(d(n)):{},y(e||!n||!n.__esModule?c(t,"default",{value:n,enumerable:!0}):t,n)),T=n=>y(c({},"__esModule",{value:!0}),n);var H={};I(H,{calculateBoundingBoxCenter:()=>L,dummyTexture:()=>A,generateGLSLFn:()=>_,getSharedFileset:()=>O,hashOptions:()=>v,isMediaPipeSource:()=>E});module.exports=T(H);var A={data:new Uint8Array(4),width:1,height:1};function E(n){return n instanceof HTMLVideoElement||n instanceof HTMLImageElement||n instanceof HTMLCanvasElement||n instanceof OffscreenCanvas}function v(n){return JSON.stringify(n,Object.keys(n).sort())}function L(n,e,t,s,r=0){let i=1/0,o=-1/0,a=1/0,m=-1/0,u=0,l=0;for(let h of t){let f=(r+e*s+h)*4,x=n[f],p=n[f+1];i=Math.min(i,x),o=Math.max(o,x),a=Math.min(a,p),m=Math.max(m,p),u+=n[f+2],l+=n[f+3]}return[(i+o)/2,(a+m)/2,u/t.length,l/t.length]}var g=null;function O(){return g||(g=import("@mediapipe/tasks-vision").then(({FilesetResolver:n})=>n.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.22-rc.20250304/wasm"))),g}function _(n){return{historyParams:n?", framesAgo":"",fn:n?(s,r,i,o)=>{let a=i.replace(/\w+ /g,""),m=i?`${i}, int framesAgo`:"int framesAgo",u=a?`${a}, 0`:"0";return`${s} ${r}(${m}) {
2
+ ${o}
3
+ }
4
+ ${s} ${r}(${i}) { return ${r}(${u}); }`}:(s,r,i,o)=>`${s} ${r}(${i}) {
5
+ ${o}
6
+ }`}}0&&(module.exports={calculateBoundingBoxCenter,dummyTexture,generateGLSLFn,getSharedFileset,hashOptions,isMediaPipeSource});
7
+ //# sourceMappingURL=mediapipe-common.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/plugins/mediapipe-common.ts"],"sourcesContent":["import { TextureSource } from '..';\n\nexport const dummyTexture = { data: new Uint8Array(4), width: 1, height: 1 };\n\nexport type MediaPipeSource = HTMLVideoElement | HTMLImageElement | HTMLCanvasElement | OffscreenCanvas;\n\nexport function isMediaPipeSource(source: TextureSource): source is MediaPipeSource {\n\treturn (\n\t\tsource instanceof HTMLVideoElement ||\n\t\tsource instanceof HTMLImageElement ||\n\t\tsource instanceof HTMLCanvasElement ||\n\t\tsource instanceof OffscreenCanvas\n\t);\n}\n\nexport function hashOptions(options: object): string {\n\treturn JSON.stringify(options, Object.keys(options).sort());\n}\n\nexport function calculateBoundingBoxCenter(\n\tdata: Float32Array,\n\tentityIdx: number,\n\tlandmarkIndices: readonly number[] | number[],\n\tlandmarkCount: number,\n\toffset: number = 0\n): [number, number, number, number] {\n\tlet minX = Infinity,\n\t\tmaxX = -Infinity,\n\t\tminY = Infinity,\n\t\tmaxY = -Infinity,\n\t\tavgZ = 0,\n\t\tavgVisibility = 0;\n\n\tfor (const idx of landmarkIndices) {\n\t\tconst dataIdx = (offset + entityIdx * landmarkCount + idx) * 4;\n\t\tconst x = data[dataIdx];\n\t\tconst y = data[dataIdx + 1];\n\t\tminX = Math.min(minX, x);\n\t\tmaxX = Math.max(maxX, x);\n\t\tminY = Math.min(minY, y);\n\t\tmaxY = Math.max(maxY, y);\n\t\tavgZ += data[dataIdx + 2];\n\t\tavgVisibility += data[dataIdx + 3];\n\t}\n\n\treturn [\n\t\t(minX + maxX) / 2,\n\t\t(minY + maxY) / 2,\n\t\tavgZ / landmarkIndices.length,\n\t\tavgVisibility / landmarkIndices.length,\n\t];\n}\n\nlet filesetPromise: Promise<any> | null = null;\nexport function getSharedFileset(): Promise<any> {\n\tif (!filesetPromise) {\n\t\tfilesetPromise = import('@mediapipe/tasks-vision').then(({ FilesetResolver }) =>\n\t\t\tFilesetResolver.forVisionTasks(\n\t\t\t\t`https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${__MEDIAPIPE_TASKS_VISION_VERSION__}/wasm`\n\t\t\t)\n\t\t);\n\t}\n\treturn filesetPromise;\n}\n\nexport function generateGLSLFn(history: number | undefined) {\n\tconst historyParams = history ? ', framesAgo' : '';\n\tconst fn = history\n\t\t? (returnType: string, name: string, args: string, body: string) => {\n\t\t\t\tconst argsOnly = args.replace(/\\w+ /g, '');\n\t\t\t\tconst historyArgs = args ? `${args}, int framesAgo` : 'int framesAgo';\n\t\t\t\tconst callArgs = argsOnly ? `${argsOnly}, 0` : '0';\n\t\t\t\treturn `${returnType} ${name}(${historyArgs}) {\\n${body}\\n}\n${returnType} ${name}(${args}) { return ${name}(${callArgs}); }`;\n\t\t }\n\t\t: (returnType: string, name: string, args: string, body: string) =>\n\t\t\t\t`${returnType} ${name}(${args}) {\\n${body}\\n}`;\n\treturn { historyParams, fn };\n}\n"],"mappings":"0jBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,gCAAAE,EAAA,iBAAAC,EAAA,mBAAAC,EAAA,qBAAAC,EAAA,gBAAAC,EAAA,sBAAAC,IAAA,eAAAC,EAAAR,GAEO,IAAMG,EAAe,CAAE,KAAM,IAAI,WAAW,CAAC,EAAG,MAAO,EAAG,OAAQ,CAAE,EAIpE,SAASI,EAAkBE,EAAkD,CACnF,OACCA,aAAkB,kBAClBA,aAAkB,kBAClBA,aAAkB,mBAClBA,aAAkB,eAEpB,CAEO,SAASH,EAAYI,EAAyB,CACpD,OAAO,KAAK,UAAUA,EAAS,OAAO,KAAKA,CAAO,EAAE,KAAK,CAAC,CAC3D,CAEO,SAASR,EACfS,EACAC,EACAC,EACAC,EACAC,EAAiB,EACkB,CACnC,IAAIC,EAAO,IACVC,EAAO,KACPC,EAAO,IACPC,EAAO,KACPC,EAAO,EACPC,EAAgB,EAEjB,QAAWC,KAAOT,EAAiB,CAClC,IAAMU,GAAWR,EAASH,EAAYE,EAAgBQ,GAAO,EACvD,EAAIX,EAAKY,CAAO,EAChBC,EAAIb,EAAKY,EAAU,CAAC,EAC1BP,EAAO,KAAK,IAAIA,EAAM,CAAC,EACvBC,EAAO,KAAK,IAAIA,EAAM,CAAC,EACvBC,EAAO,KAAK,IAAIA,EAAMM,CAAC,EACvBL,EAAO,KAAK,IAAIA,EAAMK,CAAC,EACvBJ,GAAQT,EAAKY,EAAU,CAAC,EACxBF,GAAiBV,EAAKY,EAAU,CAAC,CAClC,CAEA,MAAO,EACLP,EAAOC,GAAQ,GACfC,EAAOC,GAAQ,EAChBC,EAAOP,EAAgB,OACvBQ,EAAgBR,EAAgB,MACjC,CACD,CAEA,IAAIY,EAAsC,KACnC,SAASpB,GAAiC,CAChD,OAAKoB,IACJA,EAAiB,OAAO,yBAAyB,EAAE,KAAK,CAAC,CAAE,gBAAAC,CAAgB,IAC1EA,EAAgB,eACf,+EACD,CACD,GAEMD,CACR,CAEO,SAASrB,EAAeuB,EAA6B,CAY3D,MAAO,CAAE,cAXaA,EAAU,cAAgB,GAWxB,GAVbA,EACR,CAACC,EAAoBC,EAAcC,EAAcC,IAAiB,CAClE,IAAMC,EAAWF,EAAK,QAAQ,QAAS,EAAE,EACnCG,EAAcH,EAAO,GAAGA,CAAI,kBAAoB,gBAChDI,EAAWF,EAAW,GAAGA,CAAQ,MAAQ,IAC/C,MAAO,GAAGJ,CAAU,IAAIC,CAAI,IAAII,CAAW;AAAA,EAAQF,CAAI;AAAA;AAAA,EACzDH,CAAU,IAAIC,CAAI,IAAIC,CAAI,cAAcD,CAAI,IAAIK,CAAQ,MACtD,EACA,CAACN,EAAoBC,EAAcC,EAAcC,IACjD,GAAGH,CAAU,IAAIC,CAAI,IAAIC,CAAI;AAAA,EAAQC,CAAI;AAAA,EACjB,CAC5B","names":["mediapipe_common_exports","__export","calculateBoundingBoxCenter","dummyTexture","generateGLSLFn","getSharedFileset","hashOptions","isMediaPipeSource","__toCommonJS","source","options","data","entityIdx","landmarkIndices","landmarkCount","offset","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","dataIdx","y","filesetPromise","FilesetResolver","history","returnType","name","args","body","argsOnly","historyArgs","callArgs"]}
@@ -0,0 +1,2 @@
1
+ import{a,b,c,d,e,f}from"../chunk-JRSBIGBN.mjs";export{d as calculateBoundingBoxCenter,a as dummyTexture,f as generateGLSLFn,e as getSharedFileset,c as hashOptions,b as isMediaPipeSource};
2
+ //# sourceMappingURL=mediapipe-common.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,5 +1,4 @@
1
1
  import ShaderPad, { PluginContext } from '../index.mjs';
2
- import { PoseLandmarkerResult } from '@mediapipe/tasks-vision';
3
2
 
4
3
  interface PosePluginOptions {
5
4
  modelPath?: string;
@@ -7,8 +6,7 @@ interface PosePluginOptions {
7
6
  minPoseDetectionConfidence?: number;
8
7
  minPosePresenceConfidence?: number;
9
8
  minTrackingConfidence?: number;
10
- onReady?: () => void;
11
- onResults?: (results: PoseLandmarkerResult) => void;
9
+ history?: number;
12
10
  }
13
11
  declare function pose(config: {
14
12
  textureName: string;
@@ -1,5 +1,4 @@
1
1
  import ShaderPad, { PluginContext } from '../index.js';
2
- import { PoseLandmarkerResult } from '@mediapipe/tasks-vision';
3
2
 
4
3
  interface PosePluginOptions {
5
4
  modelPath?: string;
@@ -7,8 +6,7 @@ interface PosePluginOptions {
7
6
  minPoseDetectionConfidence?: number;
8
7
  minPosePresenceConfidence?: number;
9
8
  minTrackingConfidence?: number;
10
- onReady?: () => void;
11
- onResults?: (results: PoseLandmarkerResult) => void;
9
+ history?: number;
12
10
  }
13
11
  declare function pose(config: {
14
12
  textureName: string;