shaderpad 1.0.0-beta.20 → 1.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/plugins/face.d.mts +1 -1
- package/dist/plugins/face.d.ts +1 -1
- package/dist/plugins/face.js +2 -2
- package/dist/plugins/face.js.map +1 -1
- package/dist/plugins/face.mjs +1 -1
- package/dist/plugins/face.mjs.map +1 -1
- package/dist/plugins/hands.d.mts +1 -1
- package/dist/plugins/hands.d.ts +1 -1
- package/dist/plugins/hands.js +2 -2
- package/dist/plugins/hands.js.map +1 -1
- package/dist/plugins/hands.mjs +1 -1
- package/dist/plugins/hands.mjs.map +1 -1
- package/dist/plugins/helpers.d.mts +1 -1
- package/dist/plugins/helpers.d.ts +1 -1
- package/dist/plugins/helpers.js +2 -2
- package/dist/plugins/helpers.js.map +1 -1
- package/dist/plugins/helpers.mjs +1 -1
- package/dist/plugins/helpers.mjs.map +1 -1
- package/dist/plugins/pose.d.mts +1 -1
- package/dist/plugins/pose.d.ts +1 -1
- package/dist/plugins/pose.js +2 -2
- package/dist/plugins/pose.js.map +1 -1
- package/dist/plugins/pose.mjs +1 -1
- package/dist/plugins/pose.mjs.map +1 -1
- package/dist/plugins/save.d.mts +1 -1
- package/dist/plugins/save.d.ts +1 -1
- package/dist/plugins/save.js +1 -1
- package/dist/plugins/save.js.map +1 -1
- package/dist/plugins/save.mjs +1 -1
- package/dist/plugins/save.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugins/pose.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { PoseLandmarker, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface PosePluginOptions {\n\tmodelPath?: string;\n\tmaxPoses?: number;\n\tminPoseDetectionConfidence?: number;\n\tminPosePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputSegmentationMasks?: boolean;\n}\n\nconst LANDMARK_COUNT = 33; // See https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker#pose_landmarker_model.\n\nexport function pose(config: { textureName: string; options?: PosePluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL } = context;\n\n\t\tlet poseLandmarker: PoseLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxPoses = options?.maxPoses ?? 1;\n\t\tconst maskWidth = 512;\n\t\tconst maskHeight = 512;\n\t\tconst poseMaskCanvas = document.createElement('canvas');\n\t\tposeMaskCanvas.width = maskWidth;\n\t\tposeMaskCanvas.height = maskHeight;\n\t\tconst poseMaskCtx = poseMaskCanvas.getContext('2d')!;\n\t\tposeMaskCtx.globalCompositeOperation = 'lighten'; // Keep the highest value of each channel.\n\t\tconst poseConnections: { start: number; end: number }[] = [];\n\n\t\tasync function initializePoseLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, PoseLandmarker } = 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\t\t\t\tposeConnections.push(...PoseLandmarker.POSE_CONNECTIONS);\n\t\t\t\tposeLandmarker = await PoseLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumPoses: options?.maxPoses ?? 1,\n\t\t\t\t\tminPoseDetectionConfidence: options?.minPoseDetectionConfidence ?? 0.5,\n\t\t\t\t\tminPosePresenceConfidence: options?.minPosePresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t\toutputSegmentationMasks: options?.outputSegmentationMasks ?? false,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Pose Plugin] Failed to initialize Pose Landmarker:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tasync function updateMaskTexture(poses: NormalizedLandmark[][], segmentationMasks?: ImageData[]) {\n\t\t\tif (!poseLandmarker) {\n\t\t\t\tconsole.warn('[Pose Plugin] Cannot update mask: poseLandmarker missing');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tposeMaskCtx.clearRect(0, 0, poseMaskCanvas.width, poseMaskCanvas.height);\n\n\t\t\t\t// Draw the segmentation masks.\n\t\t\t\tif (segmentationMasks && segmentationMasks.length > 0) {\n\t\t\t\t\t// Combine all segmentation masks (for multiple poses).\n\t\t\t\t\tsegmentationMasks.forEach(mask => {\n\t\t\t\t\t\t// Resize mask to canvas size if needed.\n\t\t\t\t\t\tif (mask.width !== poseMaskCanvas.width || mask.height !== poseMaskCanvas.height) {\n\t\t\t\t\t\t\tconst tempCanvas = document.createElement('canvas');\n\t\t\t\t\t\t\ttempCanvas.width = mask.width;\n\t\t\t\t\t\t\ttempCanvas.height = mask.height;\n\t\t\t\t\t\t\tconst tempCtx = tempCanvas.getContext('2d')!;\n\t\t\t\t\t\t\ttempCtx.putImageData(mask, 0, 0);\n\t\t\t\t\t\t\tposeMaskCtx.drawImage(tempCanvas, 0, 0, poseMaskCanvas.width, poseMaskCanvas.height);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tposeMaskCtx.putImageData(mask, 0, 0);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Draw the skeleton.\n\t\t\t\tif (poseConnections.length) {\n\t\t\t\t\tconst lineWidth = Math.max(2, poseMaskCanvas.width / 256);\n\t\t\t\t\tposeMaskCtx.lineWidth = lineWidth;\n\t\t\t\t\tposeMaskCtx.lineCap = 'round';\n\t\t\t\t\tposeMaskCtx.strokeStyle = '#00f';\n\n\t\t\t\t\tposes.forEach(landmarks => {\n\t\t\t\t\t\tposeConnections.forEach(({ start, end }) => {\n\t\t\t\t\t\t\tconst a = landmarks[start];\n\t\t\t\t\t\t\tconst b = landmarks[end];\n\t\t\t\t\t\t\tif (!a || !b) return;\n\t\t\t\t\t\t\tif ((a.visibility ?? 1) < 0.3 || (b.visibility ?? 1) < 0.3) return;\n\t\t\t\t\t\t\tposeMaskCtx.beginPath();\n\t\t\t\t\t\t\tposeMaskCtx.moveTo(a.x * poseMaskCanvas.width, a.y * poseMaskCanvas.height);\n\t\t\t\t\t\t\tposeMaskCtx.lineTo(b.x * poseMaskCanvas.width, b.y * poseMaskCanvas.height);\n\t\t\t\t\t\t\tposeMaskCtx.stroke();\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tshaderPad.updateTextures({ u_poseMask: poseMaskCanvas });\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Pose Plugin] Failed to generate mask texture:', error);\n\t\t\t}\n\t\t}\n\n\t\tfunction processPoseResults(result: any) {\n\t\t\tif (!result.landmarks) return;\n\n\t\t\tupdateMaskTexture(result.landmarks, result.segmentationMasks).catch(error => {\n\t\t\t\tconsole.warn('[Pose Plugin] Mask texture update error:', error);\n\t\t\t});\n\n\t\t\tconst nPoses = result.landmarks.length;\n\t\t\tconst updates: Parameters<typeof shaderPad.updateUniforms>[0] = { u_nPoses: nPoses };\n\t\t\tif (nPoses) {\n\t\t\t\tupdates.u_poseLandmarks = result.landmarks.flatMap((landmarks: NormalizedLandmark[]) =>\n\t\t\t\t\tlandmarks.map(landmark => [landmark.x, 1.0 - landmark.y])\n\t\t\t\t);\n\t\t\t}\n\t\t\tshaderPad.updateUniforms(updates);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_poseMask', poseMaskCanvas);\n\t\t\tshaderPad.initializeUniform('u_maxPoses', 'int', maxPoses);\n\t\t\tshaderPad.initializeUniform('u_nPoses', 'int', 0);\n\t\t\tconst defaultPoseData: [number, number][] = Array.from({ length: maxPoses * LANDMARK_COUNT }, () => [\n\t\t\t\t0.5, 0.5,\n\t\t\t]);\n\t\t\tshaderPad.initializeUniform('u_poseLandmarks', 'float', defaultPoseData, {\n\t\t\t\tarrayLength: maxPoses * LANDMARK_COUNT,\n\t\t\t});\n\n\t\t\tawait initializePoseLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\t\tif (name !== textureName) return;\n\t\t\t\ttextureSources.set(name, source);\n\t\t\t\tif (!poseLandmarker) return;\n\t\t\t\ttry {\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\t\tconst timestamp = performance.now();\n\t\t\t\t\t\t\tconst result = poseLandmarker.detectForVideo(source, timestamp);\n\t\t\t\t\t\t\tprocessPoseResults(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (source instanceof HTMLImageElement) {\n\t\t\t\t\t\tconst result = poseLandmarker.detect(source);\n\t\t\t\t\t\tprocessPoseResults(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error('[Pose Plugin] Pose detection error:', error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (poseLandmarker) {\n\t\t\t\tposeLandmarker.close();\n\t\t\t\tposeLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tposeMaskCanvas.remove();\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxPoses;\nuniform int u_nPoses;\nuniform vec2 u_poseLandmarks[${maxPoses * LANDMARK_COUNT}];\nuniform sampler2D u_poseMask;\nvec2 poseLandmark(int poseIndex, int landmarkIndex) {\n\treturn u_poseLandmarks[poseIndex * ${LANDMARK_COUNT} + landmarkIndex];\n}\nfloat getBody(vec2 pos) { return texture(u_poseMask, pos).g; }\nfloat getSkeleton(vec2 pos) { return texture(u_poseMask, pos).b; }`);\n\t};\n}\n"],"mappings":"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","names":["LANDMARK_COUNT","pose","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","poseLandmarker","vision","lastVideoTime","textureSources","maxPoses","maskWidth","maskHeight","poseMaskCanvas","poseMaskCtx","poseConnections","initializePoseLandmarker","FilesetResolver","PoseLandmarker","error","updateMaskTexture","poses","segmentationMasks","mask","tempCanvas","lineWidth","landmarks","start","end","a","b","processPoseResults","result","nPoses","updates","landmark","defaultPoseData","name","source","timestamp"]}
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/pose.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '../index';\nimport type { PoseLandmarker, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface PosePluginOptions {\n\tmodelPath?: string;\n\tmaxPoses?: number;\n\tminPoseDetectionConfidence?: number;\n\tminPosePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputSegmentationMasks?: boolean;\n}\n\nconst LANDMARK_COUNT = 33; // See https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker#pose_landmarker_model.\n\nfunction pose(config: { textureName: string; options?: PosePluginOptions }) {\n\tconst { textureName, options } = config;\n\tconst defaultModelPath =\n\t\t'https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task';\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL } = context;\n\n\t\tlet poseLandmarker: PoseLandmarker | null = null;\n\t\tlet vision: any = null;\n\t\tlet lastVideoTime = -1;\n\t\tconst textureSources = new Map<string, TextureSource>();\n\t\tconst maxPoses = options?.maxPoses ?? 1;\n\t\tconst maskWidth = 512;\n\t\tconst maskHeight = 512;\n\t\tconst poseMaskCanvas = document.createElement('canvas');\n\t\tposeMaskCanvas.width = maskWidth;\n\t\tposeMaskCanvas.height = maskHeight;\n\t\tconst poseMaskCtx = poseMaskCanvas.getContext('2d')!;\n\t\tposeMaskCtx.globalCompositeOperation = 'lighten'; // Keep the highest value of each channel.\n\t\tconst poseConnections: { start: number; end: number }[] = [];\n\n\t\tasync function initializePoseLandmarker() {\n\t\t\ttry {\n\t\t\t\tconst { FilesetResolver, PoseLandmarker } = 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\t\t\t\tposeConnections.push(...PoseLandmarker.POSE_CONNECTIONS);\n\t\t\t\tposeLandmarker = await PoseLandmarker.createFromOptions(vision, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options?.modelPath || defaultModelPath,\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumPoses: options?.maxPoses ?? 1,\n\t\t\t\t\tminPoseDetectionConfidence: options?.minPoseDetectionConfidence ?? 0.5,\n\t\t\t\t\tminPosePresenceConfidence: options?.minPosePresenceConfidence ?? 0.5,\n\t\t\t\t\tminTrackingConfidence: options?.minTrackingConfidence ?? 0.5,\n\t\t\t\t\toutputSegmentationMasks: options?.outputSegmentationMasks ?? false,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Pose Plugin] Failed to initialize Pose Landmarker:', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tasync function updateMaskTexture(poses: NormalizedLandmark[][], segmentationMasks?: ImageData[]) {\n\t\t\tif (!poseLandmarker) {\n\t\t\t\tconsole.warn('[Pose Plugin] Cannot update mask: poseLandmarker missing');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tposeMaskCtx.clearRect(0, 0, poseMaskCanvas.width, poseMaskCanvas.height);\n\n\t\t\t\t// Draw the segmentation masks.\n\t\t\t\tif (segmentationMasks && segmentationMasks.length > 0) {\n\t\t\t\t\t// Combine all segmentation masks (for multiple poses).\n\t\t\t\t\tsegmentationMasks.forEach(mask => {\n\t\t\t\t\t\t// Resize mask to canvas size if needed.\n\t\t\t\t\t\tif (mask.width !== poseMaskCanvas.width || mask.height !== poseMaskCanvas.height) {\n\t\t\t\t\t\t\tconst tempCanvas = document.createElement('canvas');\n\t\t\t\t\t\t\ttempCanvas.width = mask.width;\n\t\t\t\t\t\t\ttempCanvas.height = mask.height;\n\t\t\t\t\t\t\tconst tempCtx = tempCanvas.getContext('2d')!;\n\t\t\t\t\t\t\ttempCtx.putImageData(mask, 0, 0);\n\t\t\t\t\t\t\tposeMaskCtx.drawImage(tempCanvas, 0, 0, poseMaskCanvas.width, poseMaskCanvas.height);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tposeMaskCtx.putImageData(mask, 0, 0);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Draw the skeleton.\n\t\t\t\tif (poseConnections.length) {\n\t\t\t\t\tconst lineWidth = Math.max(2, poseMaskCanvas.width / 256);\n\t\t\t\t\tposeMaskCtx.lineWidth = lineWidth;\n\t\t\t\t\tposeMaskCtx.lineCap = 'round';\n\t\t\t\t\tposeMaskCtx.strokeStyle = '#00f';\n\n\t\t\t\t\tposes.forEach(landmarks => {\n\t\t\t\t\t\tposeConnections.forEach(({ start, end }) => {\n\t\t\t\t\t\t\tconst a = landmarks[start];\n\t\t\t\t\t\t\tconst b = landmarks[end];\n\t\t\t\t\t\t\tif (!a || !b) return;\n\t\t\t\t\t\t\tif ((a.visibility ?? 1) < 0.3 || (b.visibility ?? 1) < 0.3) return;\n\t\t\t\t\t\t\tposeMaskCtx.beginPath();\n\t\t\t\t\t\t\tposeMaskCtx.moveTo(a.x * poseMaskCanvas.width, a.y * poseMaskCanvas.height);\n\t\t\t\t\t\t\tposeMaskCtx.lineTo(b.x * poseMaskCanvas.width, b.y * poseMaskCanvas.height);\n\t\t\t\t\t\t\tposeMaskCtx.stroke();\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tshaderPad.updateTextures({ u_poseMask: poseMaskCanvas });\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[Pose Plugin] Failed to generate mask texture:', error);\n\t\t\t}\n\t\t}\n\n\t\tfunction processPoseResults(result: any) {\n\t\t\tif (!result.landmarks) return;\n\n\t\t\tupdateMaskTexture(result.landmarks, result.segmentationMasks).catch(error => {\n\t\t\t\tconsole.warn('[Pose Plugin] Mask texture update error:', error);\n\t\t\t});\n\n\t\t\tconst nPoses = result.landmarks.length;\n\t\t\tconst updates: Parameters<typeof shaderPad.updateUniforms>[0] = { u_nPoses: nPoses };\n\t\t\tif (nPoses) {\n\t\t\t\tupdates.u_poseLandmarks = result.landmarks.flatMap((landmarks: NormalizedLandmark[]) =>\n\t\t\t\t\tlandmarks.map(landmark => [landmark.x, 1.0 - landmark.y])\n\t\t\t\t);\n\t\t\t}\n\t\t\tshaderPad.updateUniforms(updates);\n\t\t}\n\n\t\tshaderPad.registerHook('init', async () => {\n\t\t\tshaderPad.initializeTexture('u_poseMask', poseMaskCanvas);\n\t\t\tshaderPad.initializeUniform('u_maxPoses', 'int', maxPoses);\n\t\t\tshaderPad.initializeUniform('u_nPoses', 'int', 0);\n\t\t\tconst defaultPoseData: [number, number][] = Array.from({ length: maxPoses * LANDMARK_COUNT }, () => [\n\t\t\t\t0.5, 0.5,\n\t\t\t]);\n\t\t\tshaderPad.initializeUniform('u_poseLandmarks', 'float', defaultPoseData, {\n\t\t\t\tarrayLength: maxPoses * LANDMARK_COUNT,\n\t\t\t});\n\n\t\t\tawait initializePoseLandmarker();\n\t\t});\n\n\t\tshaderPad.registerHook('updateTextures', (updates: Record<string, TextureSource>) => {\n\t\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\t\tif (name !== textureName) return;\n\t\t\t\ttextureSources.set(name, source);\n\t\t\t\tif (!poseLandmarker) return;\n\t\t\t\ttry {\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.currentTime !== lastVideoTime) {\n\t\t\t\t\t\t\tlastVideoTime = source.currentTime;\n\t\t\t\t\t\t\tconst timestamp = performance.now();\n\t\t\t\t\t\t\tconst result = poseLandmarker.detectForVideo(source, timestamp);\n\t\t\t\t\t\t\tprocessPoseResults(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (source instanceof HTMLImageElement) {\n\t\t\t\t\t\tconst result = poseLandmarker.detect(source);\n\t\t\t\t\t\tprocessPoseResults(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error('[Pose Plugin] Pose detection error:', error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tshaderPad.registerHook('destroy', () => {\n\t\t\tif (poseLandmarker) {\n\t\t\t\tposeLandmarker.close();\n\t\t\t\tposeLandmarker = null;\n\t\t\t}\n\t\t\tvision = null;\n\t\t\ttextureSources.clear();\n\t\t\tposeMaskCanvas.remove();\n\t\t});\n\n\t\tinjectGLSL(`\nuniform int u_maxPoses;\nuniform int u_nPoses;\nuniform vec2 u_poseLandmarks[${maxPoses * LANDMARK_COUNT}];\nuniform sampler2D u_poseMask;\nvec2 poseLandmark(int poseIndex, int landmarkIndex) {\n\treturn u_poseLandmarks[poseIndex * ${LANDMARK_COUNT} + landmarkIndex];\n}\nfloat getBody(vec2 pos) { return texture(u_poseMask, pos).g; }\nfloat getSkeleton(vec2 pos) { return texture(u_poseMask, pos).b; }`);\n\t};\n}\n\nexport default pose;\n"],"mappings":"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","names":["LANDMARK_COUNT","pose","config","textureName","options","defaultModelPath","shaderPad","context","injectGLSL","poseLandmarker","vision","lastVideoTime","textureSources","maxPoses","maskWidth","maskHeight","poseMaskCanvas","poseMaskCtx","poseConnections","initializePoseLandmarker","FilesetResolver","PoseLandmarker","error","updateMaskTexture","poses","segmentationMasks","mask","tempCanvas","lineWidth","landmarks","start","end","a","b","processPoseResults","result","nPoses","updates","landmark","defaultPoseData","name","source","timestamp","pose_default"]}
|
package/dist/plugins/save.d.mts
CHANGED
package/dist/plugins/save.d.ts
CHANGED
package/dist/plugins/save.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var s=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var h=Object.prototype.hasOwnProperty;var p=(a,e)=>{for(var
|
|
1
|
+
"use strict";var s=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var h=Object.prototype.hasOwnProperty;var p=(a,e)=>{for(var t in e)s(a,t,{get:e[t],enumerable:!0})},u=(a,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of g(e))!h.call(a,n)&&n!==t&&s(a,n,{get:()=>e[n],enumerable:!(r=l(e,n))||r.enumerable});return a};var w=a=>u(s({},"__esModule",{value:!0}),a);var f={};p(f,{default:()=>P});module.exports=w(f);function v(){return function(a,e){let{gl:t,canvas:r}=e,n=document.createElement("a");a.save=async function(o){if(t.clear(t.COLOR_BUFFER_BIT),t.drawArrays(t.TRIANGLES,0,6),o&&!`${o}`.toLowerCase().endsWith(".png")&&(o=`${o}.png`),o=o||"export.png","ongesturechange"in window)try{let i=await new Promise(c=>r.toBlob(c,"image/png")),d=new File([i],o,{type:i.type});if(navigator.canShare?.({files:[d]})){await navigator.share({files:[d]});return}}catch(i){console.warn("Web Share API failed:",i)}else n.download=o,n.href=r.toDataURL(),n.click()}}}var P=v;
|
|
2
2
|
//# sourceMappingURL=save.js.map
|
package/dist/plugins/save.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugins/save.ts"],"sourcesContent":["import ShaderPad, { PluginContext } from '../index';\n\ndeclare module '../index' {\n\tinterface ShaderPad {\n\t\tsave(filename: string): Promise<void>;\n\t}\n}\n\
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/save.ts"],"sourcesContent":["import ShaderPad, { PluginContext } from '../index';\n\ndeclare module '../index' {\n\tinterface ShaderPad {\n\t\tsave(filename: string): Promise<void>;\n\t}\n}\n\nfunction save() {\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { gl, canvas } = context;\n\t\tconst downloadLink = document.createElement('a');\n\n\t\t(shaderPad as any).save = async function (filename: string) {\n\t\t\tgl.clear(gl.COLOR_BUFFER_BIT);\n\t\t\tgl.drawArrays(gl.TRIANGLES, 0, 6);\n\n\t\t\tif (filename && !`${filename}`.toLowerCase().endsWith('.png')) {\n\t\t\t\tfilename = `${filename}.png`;\n\t\t\t}\n\t\t\tfilename = filename || 'export.png';\n\t\t\tif ('ongesturechange' in window) {\n\t\t\t\t// Mobile.\n\t\t\t\ttry {\n\t\t\t\t\tconst blob: Blob = await new Promise(resolve =>\n\t\t\t\t\t\tcanvas.toBlob(resolve as BlobCallback, 'image/png')\n\t\t\t\t\t);\n\t\t\t\t\tconst file = new File([blob], filename, { type: blob.type });\n\n\t\t\t\t\tif (navigator.canShare?.({ files: [file] })) {\n\t\t\t\t\t\tawait navigator.share({ files: [file] });\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn('Web Share API failed:', error);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Desktop.\n\t\t\t\tdownloadLink.download = filename;\n\t\t\t\tdownloadLink.href = canvas.toDataURL();\n\t\t\t\tdownloadLink.click();\n\t\t\t}\n\t\t};\n\t};\n}\n\n// Type helper.\nexport type WithSave<T extends ShaderPad> = T & {\n\tsave(filename: string): Promise<void>;\n};\n\nexport default save;\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,IAAA,eAAAC,EAAAH,GAQA,SAASI,GAAO,CACf,OAAO,SAAUC,EAAsBC,EAAwB,CAC9D,GAAM,CAAE,GAAAC,EAAI,OAAAC,CAAO,EAAIF,EACjBG,EAAe,SAAS,cAAc,GAAG,EAE9CJ,EAAkB,KAAO,eAAgBK,EAAkB,CAQ3D,GAPAH,EAAG,MAAMA,EAAG,gBAAgB,EAC5BA,EAAG,WAAWA,EAAG,UAAW,EAAG,CAAC,EAE5BG,GAAY,CAAC,GAAGA,CAAQ,GAAG,YAAY,EAAE,SAAS,MAAM,IAC3DA,EAAW,GAAGA,CAAQ,QAEvBA,EAAWA,GAAY,aACnB,oBAAqB,OAExB,GAAI,CACH,IAAMC,EAAa,MAAM,IAAI,QAAQC,GACpCJ,EAAO,OAAOI,EAAyB,WAAW,CACnD,EACMC,EAAO,IAAI,KAAK,CAACF,CAAI,EAAGD,EAAU,CAAE,KAAMC,EAAK,IAAK,CAAC,EAE3D,GAAI,UAAU,WAAW,CAAE,MAAO,CAACE,CAAI,CAAE,CAAC,EAAG,CAC5C,MAAM,UAAU,MAAM,CAAE,MAAO,CAACA,CAAI,CAAE,CAAC,EACvC,MACD,CACD,OAASC,EAAO,CACf,QAAQ,KAAK,wBAAyBA,CAAK,CAC5C,MAGAL,EAAa,SAAWC,EACxBD,EAAa,KAAOD,EAAO,UAAU,EACrCC,EAAa,MAAM,CAErB,CACD,CACD,CAOA,IAAOP,EAAQE","names":["save_exports","__export","save_default","__toCommonJS","save","shaderPad","context","gl","canvas","downloadLink","filename","blob","resolve","file","error"]}
|
package/dist/plugins/save.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function c(){return function(i,s){let{gl:a,canvas:o}=s,
|
|
1
|
+
function c(){return function(i,s){let{gl:a,canvas:o}=s,n=document.createElement("a");i.save=async function(e){if(a.clear(a.COLOR_BUFFER_BIT),a.drawArrays(a.TRIANGLES,0,6),e&&!`${e}`.toLowerCase().endsWith(".png")&&(e=`${e}.png`),e=e||"export.png","ongesturechange"in window)try{let t=await new Promise(d=>o.toBlob(d,"image/png")),r=new File([t],e,{type:t.type});if(navigator.canShare?.({files:[r]})){await navigator.share({files:[r]});return}}catch(t){console.warn("Web Share API failed:",t)}else n.download=e,n.href=o.toDataURL(),n.click()}}}var l=c;export{l as default};
|
|
2
2
|
//# sourceMappingURL=save.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugins/save.ts"],"sourcesContent":["import ShaderPad, { PluginContext } from '../index';\n\ndeclare module '../index' {\n\tinterface ShaderPad {\n\t\tsave(filename: string): Promise<void>;\n\t}\n}\n\
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/save.ts"],"sourcesContent":["import ShaderPad, { PluginContext } from '../index';\n\ndeclare module '../index' {\n\tinterface ShaderPad {\n\t\tsave(filename: string): Promise<void>;\n\t}\n}\n\nfunction save() {\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { gl, canvas } = context;\n\t\tconst downloadLink = document.createElement('a');\n\n\t\t(shaderPad as any).save = async function (filename: string) {\n\t\t\tgl.clear(gl.COLOR_BUFFER_BIT);\n\t\t\tgl.drawArrays(gl.TRIANGLES, 0, 6);\n\n\t\t\tif (filename && !`${filename}`.toLowerCase().endsWith('.png')) {\n\t\t\t\tfilename = `${filename}.png`;\n\t\t\t}\n\t\t\tfilename = filename || 'export.png';\n\t\t\tif ('ongesturechange' in window) {\n\t\t\t\t// Mobile.\n\t\t\t\ttry {\n\t\t\t\t\tconst blob: Blob = await new Promise(resolve =>\n\t\t\t\t\t\tcanvas.toBlob(resolve as BlobCallback, 'image/png')\n\t\t\t\t\t);\n\t\t\t\t\tconst file = new File([blob], filename, { type: blob.type });\n\n\t\t\t\t\tif (navigator.canShare?.({ files: [file] })) {\n\t\t\t\t\t\tawait navigator.share({ files: [file] });\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn('Web Share API failed:', error);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Desktop.\n\t\t\t\tdownloadLink.download = filename;\n\t\t\t\tdownloadLink.href = canvas.toDataURL();\n\t\t\t\tdownloadLink.click();\n\t\t\t}\n\t\t};\n\t};\n}\n\n// Type helper.\nexport type WithSave<T extends ShaderPad> = T & {\n\tsave(filename: string): Promise<void>;\n};\n\nexport default save;\n"],"mappings":"AAQA,SAASA,GAAO,CACf,OAAO,SAAUC,EAAsBC,EAAwB,CAC9D,GAAM,CAAE,GAAAC,EAAI,OAAAC,CAAO,EAAIF,EACjBG,EAAe,SAAS,cAAc,GAAG,EAE9CJ,EAAkB,KAAO,eAAgBK,EAAkB,CAQ3D,GAPAH,EAAG,MAAMA,EAAG,gBAAgB,EAC5BA,EAAG,WAAWA,EAAG,UAAW,EAAG,CAAC,EAE5BG,GAAY,CAAC,GAAGA,CAAQ,GAAG,YAAY,EAAE,SAAS,MAAM,IAC3DA,EAAW,GAAGA,CAAQ,QAEvBA,EAAWA,GAAY,aACnB,oBAAqB,OAExB,GAAI,CACH,IAAMC,EAAa,MAAM,IAAI,QAAQC,GACpCJ,EAAO,OAAOI,EAAyB,WAAW,CACnD,EACMC,EAAO,IAAI,KAAK,CAACF,CAAI,EAAGD,EAAU,CAAE,KAAMC,EAAK,IAAK,CAAC,EAE3D,GAAI,UAAU,WAAW,CAAE,MAAO,CAACE,CAAI,CAAE,CAAC,EAAG,CAC5C,MAAM,UAAU,MAAM,CAAE,MAAO,CAACA,CAAI,CAAE,CAAC,EACvC,MACD,CACD,OAASC,EAAO,CACf,QAAQ,KAAK,wBAAyBA,CAAK,CAC5C,MAGAL,EAAa,SAAWC,EACxBD,EAAa,KAAOD,EAAO,UAAU,EACrCC,EAAa,MAAM,CAErB,CACD,CACD,CAOA,IAAOM,EAAQX","names":["save","shaderPad","context","gl","canvas","downloadLink","filename","blob","resolve","file","error","save_default"]}
|