From 36c82f953e60e63eb827a74ff431efd0ea1f136d Mon Sep 17 00:00:00 2001 From: bql Date: Tue, 26 May 2026 10:14:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E5=8D=B7=E7=A7=AF?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 12 + package-lock.json | 2584 +++++++++++++++++++++++++++++ package.json | 25 + src/App.css | 24 + src/App.jsx | 80 + src/components/ControlPanel.css | 150 ++ src/components/ControlPanel.jsx | 454 +++++ src/components/EnvelopeViewer.css | 119 ++ src/components/EnvelopeViewer.jsx | 428 +++++ src/components/ModelViewer.css | 16 + src/components/ModelViewer.jsx | 128 ++ src/index.css | 19 + src/main.jsx | 10 + src/utils/webgpuConvolution.js | 309 ++++ src/workers/repairWorker.js | 155 ++ vite.config.js | 6 + 16 files changed, 4519 insertions(+) create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/App.css create mode 100644 src/App.jsx create mode 100644 src/components/ControlPanel.css create mode 100644 src/components/ControlPanel.jsx create mode 100644 src/components/EnvelopeViewer.css create mode 100644 src/components/EnvelopeViewer.jsx create mode 100644 src/components/ModelViewer.css create mode 100644 src/components/ModelViewer.jsx create mode 100644 src/index.css create mode 100644 src/main.jsx create mode 100644 src/utils/webgpuConvolution.js create mode 100644 src/workers/repairWorker.js create mode 100644 vite.config.js diff --git a/index.html b/index.html new file mode 100644 index 0000000..d8c33d0 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + 深度图3D模型修复与刀路生成 + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..830f6da --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2584 @@ +{ + "name": "depth-map-3d-repair", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "depth-map-3d-repair", + "version": "0.1.0", + "dependencies": { + "@react-three/drei": "^9.92.0", + "@react-three/fiber": "^8.15.0", + "clipper2-wasm": "^0.1.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "three": "^0.160.0" + }, + "devDependencies": { + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@vitejs/plugin-react": "^4.2.0", + "vite": "^5.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@dimforge/rapier3d-compat": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", + "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", + "license": "Apache-2.0" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mediapipe/tasks-vision": { + "version": "0.10.17", + "resolved": "https://registry.npmmirror.com/@mediapipe/tasks-vision/-/tasks-vision-0.10.17.tgz", + "integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==", + "license": "Apache-2.0" + }, + "node_modules/@monogrid/gainmap-js": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/@monogrid/gainmap-js/-/gainmap-js-3.4.0.tgz", + "integrity": "sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==", + "license": "MIT", + "dependencies": { + "promise-worker-transferable": "^1.0.4" + }, + "peerDependencies": { + "three": ">= 0.159.0" + } + }, + "node_modules/@react-spring/animated": { + "version": "9.7.5", + "resolved": "https://registry.npmmirror.com/@react-spring/animated/-/animated-9.7.5.tgz", + "integrity": "sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==", + "license": "MIT", + "dependencies": { + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.7.5", + "resolved": "https://registry.npmmirror.com/@react-spring/core/-/core-9.7.5.tgz", + "integrity": "sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w==", + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~9.7.5", + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "9.7.5", + "resolved": "https://registry.npmmirror.com/@react-spring/rafz/-/rafz-9.7.5.tgz", + "integrity": "sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw==", + "license": "MIT" + }, + "node_modules/@react-spring/shared": { + "version": "9.7.5", + "resolved": "https://registry.npmmirror.com/@react-spring/shared/-/shared-9.7.5.tgz", + "integrity": "sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==", + "license": "MIT", + "dependencies": { + "@react-spring/rafz": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/three": { + "version": "9.7.5", + "resolved": "https://registry.npmmirror.com/@react-spring/three/-/three-9.7.5.tgz", + "integrity": "sha512-RxIsCoQfUqOS3POmhVHa1wdWS0wyHAUway73uRLp3GAL5U2iYVNdnzQsep6M2NZ994BlW8TcKuMtQHUqOsy6WA==", + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~9.7.5", + "@react-spring/core": "~9.7.5", + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "@react-three/fiber": ">=6.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "three": ">=0.126" + } + }, + "node_modules/@react-spring/types": { + "version": "9.7.5", + "resolved": "https://registry.npmmirror.com/@react-spring/types/-/types-9.7.5.tgz", + "integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==", + "license": "MIT" + }, + "node_modules/@react-three/drei": { + "version": "9.122.0", + "resolved": "https://registry.npmmirror.com/@react-three/drei/-/drei-9.122.0.tgz", + "integrity": "sha512-SEO/F/rBCTjlLez7WAlpys+iGe9hty4rNgjZvgkQeXFSiwqD4Hbk/wNHMAbdd8vprO2Aj81mihv4dF5bC7D0CA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mediapipe/tasks-vision": "0.10.17", + "@monogrid/gainmap-js": "^3.0.6", + "@react-spring/three": "~9.7.5", + "@use-gesture/react": "^10.3.1", + "camera-controls": "^2.9.0", + "cross-env": "^7.0.3", + "detect-gpu": "^5.0.56", + "glsl-noise": "^0.0.0", + "hls.js": "^1.5.17", + "maath": "^0.10.8", + "meshline": "^3.3.1", + "react-composer": "^5.0.3", + "stats-gl": "^2.2.8", + "stats.js": "^0.17.0", + "suspend-react": "^0.1.3", + "three-mesh-bvh": "^0.7.8", + "three-stdlib": "^2.35.6", + "troika-three-text": "^0.52.0", + "tunnel-rat": "^0.1.2", + "utility-types": "^3.11.0", + "zustand": "^5.0.1" + }, + "peerDependencies": { + "@react-three/fiber": "^8", + "react": "^18", + "react-dom": "^18", + "three": ">=0.137" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/@react-three/fiber": { + "version": "8.18.0", + "resolved": "https://registry.npmmirror.com/@react-three/fiber/-/fiber-8.18.0.tgz", + "integrity": "sha512-FYZZqD0UUHUswKz3LQl2Z7H24AhD14XGTsIRw3SJaXUxyfVMi+1yiZGmqTcPt/CkPpdU7rrxqcyQ1zJE5DjvIQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@types/react-reconciler": "^0.26.7", + "@types/webxr": "*", + "base64-js": "^1.5.1", + "buffer": "^6.0.3", + "its-fine": "^1.0.6", + "react-reconciler": "^0.27.0", + "react-use-measure": "^2.1.7", + "scheduler": "^0.21.0", + "suspend-react": "^0.1.3", + "zustand": "^3.7.1" + }, + "peerDependencies": { + "expo": ">=43.0", + "expo-asset": ">=8.4", + "expo-file-system": ">=11.0", + "expo-gl": ">=11.0", + "react": ">=18 <19", + "react-dom": ">=18 <19", + "react-native": ">=0.64", + "three": ">=0.133" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + }, + "expo-asset": { + "optional": true + }, + "expo-file-system": { + "optional": true + }, + "expo-gl": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/@react-three/fiber/node_modules/zustand": { + "version": "3.7.2", + "resolved": "https://registry.npmmirror.com/zustand/-/zustand-3.7.2.tgz", + "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", + "license": "MIT", + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmmirror.com/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/draco3d": { + "version": "1.4.10", + "resolved": "https://registry.npmmirror.com/@types/draco3d/-/draco3d-1.4.10.tgz", + "integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==", + "license": "MIT" + }, + "node_modules/@types/emscripten": { + "version": "1.41.5", + "resolved": "https://registry.npmmirror.com/@types/emscripten/-/emscripten-1.41.5.tgz", + "integrity": "sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/offscreencanvas": { + "version": "2019.7.3", + "resolved": "https://registry.npmmirror.com/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", + "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.29", + "resolved": "https://registry.npmmirror.com/@types/react/-/react-18.3.29.tgz", + "integrity": "sha512-ch0qJdr2JY0r04NXSprbK6TXOgnaJ1Tz23fm5W+z0/CBah6BSBc3n96h7K9GOtwh0HrilNWHIBzE1Ko4Dcw/Wg==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/react-reconciler": { + "version": "0.26.7", + "resolved": "https://registry.npmmirror.com/@types/react-reconciler/-/react-reconciler-0.26.7.tgz", + "integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmmirror.com/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.184.1", + "resolved": "https://registry.npmmirror.com/@types/three/-/three-0.184.1.tgz", + "integrity": "sha512-6q4VdiqVsrTRqmk62/BnlcAvIrnDM0zf2ZDVKI5kZiniWrSaOHaQzmbp+BNzoggc/8tgW412pL//wZIxu2PPTA==", + "license": "MIT", + "dependencies": { + "@dimforge/rapier3d-compat": "~0.12.0", + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": ">=0.5.17", + "fflate": "~0.8.2", + "meshoptimizer": "~1.1.1" + } + }, + "node_modules/@types/webxr": { + "version": "0.5.24", + "resolved": "https://registry.npmmirror.com/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", + "license": "MIT" + }, + "node_modules/@use-gesture/core": { + "version": "10.3.1", + "resolved": "https://registry.npmmirror.com/@use-gesture/core/-/core-10.3.1.tgz", + "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", + "license": "MIT" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.1", + "resolved": "https://registry.npmmirror.com/@use-gesture/react/-/react-10.3.1.tgz", + "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", + "license": "MIT", + "dependencies": { + "@use-gesture/core": "10.3.1" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.32", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", + "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/camera-controls": { + "version": "2.10.1", + "resolved": "https://registry.npmmirror.com/camera-controls/-/camera-controls-2.10.1.tgz", + "integrity": "sha512-KnaKdcvkBJ1Irbrzl8XD6WtZltkRjp869Jx8c0ujs9K+9WD+1D7ryBsCiVqJYUqt6i/HR5FxT7RLASieUD+Q5w==", + "license": "MIT", + "peerDependencies": { + "three": ">=0.126.1" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/clipper2-wasm": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/clipper2-wasm/-/clipper2-wasm-0.1.0.tgz", + "integrity": "sha512-iEeDL8o+o9SBk20jMCsK/udSen2Be6VJT6j3pA2lTxABtamwEi2LKH8CQPOUir2wIFipaRS8fn6MCuMGai1iDA==", + "license": "BSL-1.0", + "dependencies": { + "@types/emscripten": "^1.39.10" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmmirror.com/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-gpu": { + "version": "5.0.70", + "resolved": "https://registry.npmmirror.com/detect-gpu/-/detect-gpu-5.0.70.tgz", + "integrity": "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==", + "license": "MIT", + "dependencies": { + "webgl-constants": "^1.1.1" + } + }, + "node_modules/draco3d": { + "version": "1.5.7", + "resolved": "https://registry.npmmirror.com/draco3d/-/draco3d-1.5.7.tgz", + "integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==", + "license": "Apache-2.0" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.361", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", + "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glsl-noise": { + "version": "0.0.0", + "resolved": "https://registry.npmmirror.com/glsl-noise/-/glsl-noise-0.0.0.tgz", + "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==", + "license": "MIT" + }, + "node_modules/hls.js": { + "version": "1.6.16", + "resolved": "https://registry.npmmirror.com/hls.js/-/hls.js-1.6.16.tgz", + "integrity": "sha512-VSIRpLfRwlAAdGL4wiTucx2ScRipo0ed1FBatWkyt832jC4CReKstga6yIhYVwGu9LOBjuX9wzmRMeQdBJtzEA==", + "license": "Apache-2.0" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/its-fine": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/its-fine/-/its-fine-1.2.5.tgz", + "integrity": "sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==", + "license": "MIT", + "dependencies": { + "@types/react-reconciler": "^0.28.0" + }, + "peerDependencies": { + "react": ">=18.0" + } + }, + "node_modules/its-fine/node_modules/@types/react-reconciler": { + "version": "0.28.9", + "resolved": "https://registry.npmmirror.com/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", + "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/maath": { + "version": "0.10.8", + "resolved": "https://registry.npmmirror.com/maath/-/maath-0.10.8.tgz", + "integrity": "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==", + "license": "MIT", + "peerDependencies": { + "@types/three": ">=0.134.0", + "three": ">=0.134.0" + } + }, + "node_modules/meshline": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/meshline/-/meshline-3.3.1.tgz", + "integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==", + "license": "MIT", + "peerDependencies": { + "three": ">=0.137" + } + }, + "node_modules/meshoptimizer": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/meshoptimizer/-/meshoptimizer-1.1.1.tgz", + "integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.46", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", + "license": "ISC" + }, + "node_modules/promise-worker-transferable": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz", + "integrity": "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==", + "license": "Apache-2.0", + "dependencies": { + "is-promise": "^2.1.0", + "lie": "^3.0.2" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmmirror.com/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-composer": { + "version": "5.0.3", + "resolved": "https://registry.npmmirror.com/react-composer/-/react-composer-5.0.3.tgz", + "integrity": "sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.6.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-dom/node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-reconciler": { + "version": "0.27.0", + "resolved": "https://registry.npmmirror.com/react-reconciler/-/react-reconciler-0.27.0.tgz", + "integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.21.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-use-measure": { + "version": "2.1.7", + "resolved": "https://registry.npmmirror.com/react-use-measure/-/react-use-measure-2.1.7.tgz", + "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.13", + "react-dom": ">=16.13" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.21.0", + "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.21.0.tgz", + "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stats-gl": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/stats-gl/-/stats-gl-2.4.2.tgz", + "integrity": "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==", + "license": "MIT", + "dependencies": { + "@types/three": "*", + "three": "^0.170.0" + }, + "peerDependencies": { + "@types/three": "*", + "three": "*" + } + }, + "node_modules/stats-gl/node_modules/three": { + "version": "0.170.0", + "resolved": "https://registry.npmmirror.com/three/-/three-0.170.0.tgz", + "integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==", + "license": "MIT" + }, + "node_modules/stats.js": { + "version": "0.17.0", + "resolved": "https://registry.npmmirror.com/stats.js/-/stats.js-0.17.0.tgz", + "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==", + "license": "MIT" + }, + "node_modules/suspend-react": { + "version": "0.1.3", + "resolved": "https://registry.npmmirror.com/suspend-react/-/suspend-react-0.1.3.tgz", + "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=17.0" + } + }, + "node_modules/three": { + "version": "0.160.1", + "resolved": "https://registry.npmmirror.com/three/-/three-0.160.1.tgz", + "integrity": "sha512-Bgl2wPJypDOZ1stAxwfWAcJ0WQf7QzlptsxkjYiURPz+n5k4RBDLsq+6f9Y75TYxn6aHLcWz+JNmwTOXWrQTBQ==", + "license": "MIT" + }, + "node_modules/three-mesh-bvh": { + "version": "0.7.8", + "resolved": "https://registry.npmmirror.com/three-mesh-bvh/-/three-mesh-bvh-0.7.8.tgz", + "integrity": "sha512-BGEZTOIC14U0XIRw3tO4jY7IjP7n7v24nv9JXS1CyeVRWOCkcOMhRnmENUjuV39gktAw4Ofhr0OvIAiTspQrrw==", + "deprecated": "Deprecated due to three.js version incompatibility. Please use v0.8.0, instead.", + "license": "MIT", + "peerDependencies": { + "three": ">= 0.151.0" + } + }, + "node_modules/three-stdlib": { + "version": "2.36.1", + "resolved": "https://registry.npmmirror.com/three-stdlib/-/three-stdlib-2.36.1.tgz", + "integrity": "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==", + "license": "MIT", + "dependencies": { + "@types/draco3d": "^1.4.0", + "@types/offscreencanvas": "^2019.6.4", + "@types/webxr": "^0.5.2", + "draco3d": "^1.4.1", + "fflate": "^0.6.9", + "potpack": "^1.0.1" + }, + "peerDependencies": { + "three": ">=0.128.0" + } + }, + "node_modules/three-stdlib/node_modules/fflate": { + "version": "0.6.10", + "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.6.10.tgz", + "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==", + "license": "MIT" + }, + "node_modules/troika-three-text": { + "version": "0.52.4", + "resolved": "https://registry.npmmirror.com/troika-three-text/-/troika-three-text-0.52.4.tgz", + "integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==", + "license": "MIT", + "dependencies": { + "bidi-js": "^1.0.2", + "troika-three-utils": "^0.52.4", + "troika-worker-utils": "^0.52.0", + "webgl-sdf-generator": "1.1.1" + }, + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-three-utils": { + "version": "0.52.4", + "resolved": "https://registry.npmmirror.com/troika-three-utils/-/troika-three-utils-0.52.4.tgz", + "integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==", + "license": "MIT", + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-worker-utils": { + "version": "0.52.0", + "resolved": "https://registry.npmmirror.com/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz", + "integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==", + "license": "MIT" + }, + "node_modules/tunnel-rat": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/tunnel-rat/-/tunnel-rat-0.1.2.tgz", + "integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==", + "license": "MIT", + "dependencies": { + "zustand": "^4.3.2" + } + }, + "node_modules/tunnel-rat/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmmirror.com/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmmirror.com/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/webgl-constants": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/webgl-constants/-/webgl-constants-1.1.1.tgz", + "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" + }, + "node_modules/webgl-sdf-generator": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", + "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/zustand": { + "version": "5.0.13", + "resolved": "https://registry.npmmirror.com/zustand/-/zustand-5.0.13.tgz", + "integrity": "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..ceb9e6f --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "depth-map-3d-repair", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "three": "^0.160.0", + "@react-three/fiber": "^8.15.0", + "@react-three/drei": "^9.92.0", + "clipper2-wasm": "^0.1.0" + }, + "devDependencies": { + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@vitejs/plugin-react": "^4.2.0", + "vite": "^5.0.0" + } +} diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..22a3721 --- /dev/null +++ b/src/App.css @@ -0,0 +1,24 @@ +.app { + display: flex; + width: 100vw; + height: 100vh; + background: #1a1a2e; +} + +.left-panel { + width: 50%; + display: flex; + flex-direction: column; + border-right: 2px solid #333; +} + +.right-panel { + width: 50%; + display: flex; + flex-direction: column; +} + +.viewer-container { + flex: 1; + position: relative; +} diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..c9bdc6b --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,80 @@ +import React, { useState, useCallback } from 'react' +import ModelViewer from './components/ModelViewer' +import EnvelopeViewer from './components/EnvelopeViewer' +import ControlPanel from './components/ControlPanel' +import './App.css' + +const MATERIAL_SIZE = 100 // mm + +function App() { + const [model, setModel] = useState(null) + const [upperEnvelope, setUpperEnvelope] = useState(null) + const [convolutionEnvelope, setConvolutionEnvelope] = useState(null) + const [repairedEnvelope, setRepairedEnvelope] = useState(null) + const [toolpathEnvelope, setToolpathEnvelope] = useState(null) + const [showUpper, setShowUpper] = useState(false) + const [showConvolution, setShowConvolution] = useState(false) + const [showRepaired, setShowRepaired] = useState(false) + const [showToolpath, setShowToolpath] = useState(false) + + const handleModelImport = useCallback((loadedModel) => { + setModel(loadedModel) + setUpperEnvelope(null) + setConvolutionEnvelope(null) + setRepairedEnvelope(null) + setToolpathEnvelope(null) + setShowUpper(false) + setShowConvolution(false) + setShowRepaired(false) + setShowToolpath(false) + }, []) + + const handleGenerateUpperEnvelope = useCallback((envelope) => { + setUpperEnvelope(envelope) + setShowUpper(true) + }, []) + + const handleRepairAndGenerate = useCallback((convolution, toolpath, repaired) => { + setConvolutionEnvelope(convolution) + setRepairedEnvelope(repaired) + setToolpathEnvelope(toolpath) + setShowConvolution(true) + setShowRepaired(true) + setShowToolpath(true) + }, []) + + return ( +
+
+ +
+ +
+
+
+ setShowUpper(!showUpper)} + onToggleConvolution={() => setShowConvolution(!showConvolution)} + onToggleRepaired={() => setShowRepaired(!showRepaired)} + onToggleToolpath={() => setShowToolpath(!showToolpath)} + /> +
+
+ ) +} + +export default App diff --git a/src/components/ControlPanel.css b/src/components/ControlPanel.css new file mode 100644 index 0000000..3a6f5ce --- /dev/null +++ b/src/components/ControlPanel.css @@ -0,0 +1,150 @@ +.control-panel { + padding: 16px; + background: #16213e; + border-bottom: 2px solid #333; + max-height: 40vh; + overflow-y: auto; +} + +.panel-header h2 { + color: #e94560; + margin-bottom: 16px; + font-size: 18px; +} + +.control-section { + margin-bottom: 16px; + padding: 12px; + background: #1a1a2e; + border-radius: 8px; +} + +.control-section h3 { + color: #fff; + font-size: 14px; + margin-bottom: 8px; +} + +.btn { + padding: 8px 16px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + margin-right: 8px; + margin-bottom: 8px; + transition: opacity 0.2s; +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn-primary { + background: #0f3460; + color: #fff; +} + +.btn-primary:hover:not(:disabled) { + background: #1a4a7a; +} + +.btn-success { + background: #16c79a; + color: #fff; +} + +.btn-success:hover:not(:disabled) { + background: #19a87f; +} + +.btn-warning { + background: #f5a623; + color: #fff; +} + +.btn-warning:hover:not(:disabled) { + background: #d4891a; +} + +.info-text { + color: #aaa; + font-size: 12px; + margin-top: 8px; +} + +.compute-mode-selector { + display: flex; + gap: 8px; + margin-bottom: 12px; +} + +.compute-mode-selector .toggle-btn { + padding: 6px 14px; + border: 2px solid #444; + background: #1a1a2e; + color: #aaa; + border-radius: 4px; + cursor: pointer; + font-size: 12px; + transition: all 0.2s; +} + +.compute-mode-selector .toggle-btn.active { + border-color: #4a90d9; + background: #1a4a7a; + color: #fff; +} + +.compute-mode-selector .toggle-btn:disabled { + opacity: 0.3; + cursor: not-allowed; +} + +.tool-selector { + display: flex; + flex-direction: column; + gap: 8px; + margin-bottom: 12px; +} + +.tool-selector label { + color: #fff; + font-size: 13px; + cursor: pointer; +} + +.tool-selector input { + margin-right: 8px; +} + +.progress-container { + margin-top: 12px; + display: flex; + align-items: center; + gap: 12px; +} + +.progress-bar { + flex: 1; + height: 8px; + background: #333; + border-radius: 4px; + overflow: hidden; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, #f5a623, #16c79a); + border-radius: 4px; + transition: width 0.1s ease; +} + +.progress-text { + color: #aaa; + font-size: 12px; + white-space: nowrap; + min-width: 100px; + text-align: right; +} diff --git a/src/components/ControlPanel.jsx b/src/components/ControlPanel.jsx new file mode 100644 index 0000000..782f93c --- /dev/null +++ b/src/components/ControlPanel.jsx @@ -0,0 +1,454 @@ +import React, { useRef, useState, useEffect } from 'react' +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' +import { STLLoader } from 'three/examples/jsm/loaders/STLLoader' +import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader' +import * as THREE from 'three' +import { runWebGPUConvolution } from '../utils/webgpuConvolution' +import './ControlPanel.css' + +const MATERIAL_SIZE = 100 + +function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, onRepairAndGenerate }) { + const fileInputRef = useRef(null) + const workerRef = useRef(null) + const selectedToolRef = useRef('ball') + const [isGenerating, setIsGenerating] = useState(false) + const [isRepairing, setIsRepairing] = useState(false) + const [repairProgress, setRepairProgress] = useState(0) + const [repairIteration, setRepairIteration] = useState(0) + const [selectedTool, setSelectedTool] = useState('ball') + const [computeMode, setComputeMode] = useState('worker') + const [webgpuAvailable, setWebgpuAvailable] = useState(false) + + useEffect(() => { + if (navigator.gpu) { + navigator.gpu.requestAdapter().then(adapter => { + setWebgpuAvailable(!!adapter) + }).catch(() => { + setWebgpuAvailable(false) + }) + } + }, []) + + useEffect(() => { + selectedToolRef.current = selectedTool + }, [selectedTool]) + + useEffect(() => { + workerRef.current = new Worker( + new URL('../workers/repairWorker.js', import.meta.url), + { type: 'module' } + ) + + workerRef.current.onmessage = (e) => { + if (e.data.type === 'progress') { + setRepairProgress(e.data.progress) + setRepairIteration(e.data.iteration) + } else if (e.data.type === 'complete') { + const { convolutionMap, toolpathMap, repairedMap, iterations } = e.data + + const convolutionPoints = [] + const toolpathPoints = [] + const repairedPoints = [] + const halfSize = MATERIAL_SIZE / 2 + const resolution = 0.1 + const gridSize = Math.ceil(MATERIAL_SIZE / resolution) + const gridOffset = halfSize + + for (let gy = 0; gy < gridSize; gy++) { + for (let gx = 0; gx < gridSize; gx++) { + const idx = gy * gridSize + gx + const cz = convolutionMap[idx] + const tz = toolpathMap[idx] + const rz = repairedMap[idx] + if (cz !== -Infinity) { + const x = gx * resolution - gridOffset + const y = gy * resolution - gridOffset + convolutionPoints.push({ x, y, z: cz }) + } + if (tz !== -Infinity) { + const x = gx * resolution - gridOffset + const y = gy * resolution - gridOffset + toolpathPoints.push({ x, y, z: tz }) + } + if (rz !== -Infinity) { + const x = gx * resolution - gridOffset + const y = gy * resolution - gridOffset + repairedPoints.push({ x, y, z: rz }) + } + } + } + + onRepairAndGenerate( + { + depthMap: new Float32Array(convolutionMap), + gridSize, + resolution, + points: convolutionPoints + }, + { + depthMap: new Float32Array(toolpathMap), + gridSize, + resolution, + points: toolpathPoints + }, + { + depthMap: new Float32Array(repairedMap), + gridSize, + resolution, + points: repairedPoints + } + ) + + setIsRepairing(false) + setRepairProgress(100) + console.log(`修复完成,迭代次数: ${iterations}`) + } + } + + return () => { + if (workerRef.current) { + workerRef.current.terminate() + } + } + }, [onRepairAndGenerate]) + + const handleFileSelect = async (e) => { + const file = e.target.files[0] + if (!file) return + + const extension = file.name.split('.').pop().toLowerCase() + const url = URL.createObjectURL(file) + + try { + let geometry = null + + if (extension === 'stl') { + const loader = new STLLoader() + geometry = await new Promise((resolve, reject) => { + loader.load(url, resolve, undefined, reject) + }) + } else if (extension === 'obj') { + const loader = new OBJLoader() + const obj = await new Promise((resolve, reject) => { + loader.load(url, resolve, undefined, reject) + }) + obj.traverse((child) => { + if (child.isMesh) { + geometry = child.geometry.clone() + } + }) + } else if (extension === 'gltf' || extension === 'glb') { + const loader = new GLTFLoader() + const gltf = await new Promise((resolve, reject) => { + loader.load(url, resolve, undefined, reject) + }) + gltf.scene.traverse((child) => { + if (child.isMesh) { + geometry = child.geometry.clone() + } + }) + } else { + alert('不支持的文件格式,请导入 STL、OBJ 或 GLTF/GLB 文件') + return + } + + if (!geometry) { + alert('无法加载模型几何体') + return + } + + geometry.computeBoundingBox() + const bbox = geometry.boundingBox + const size = new THREE.Vector3() + bbox.getSize(size) + const center = new THREE.Vector3() + bbox.getCenter(center) + + const maxDim = Math.max(size.x, size.y, size.z) + let scale = 1 + if (maxDim > MATERIAL_SIZE) { + scale = MATERIAL_SIZE / maxDim + } + + geometry.scale(scale, scale, scale) + geometry.translate(-center.x * scale, -center.y * scale, -center.z * scale) + + geometry.computeBoundingBox() + + onModelImport({ + geometry, + name: file.name, + bbox: geometry.boundingBox.clone() + }) + } catch (error) { + console.error('加载模型失败:', error) + alert('加载模型失败: ' + error.message) + } finally { + URL.revokeObjectURL(url) + } + + e.target.value = '' + } + + const generateUpperEnvelope = async () => { + if (!model) return + setIsGenerating(true) + + try { + await new Promise(resolve => setTimeout(resolve, 50)) + + const geometry = model.geometry + const resolution = 0.1 + const gridSize = Math.ceil(MATERIAL_SIZE / resolution) + + const depthMap = new Float32Array(gridSize * gridSize).fill(-Infinity) + + const vertices = geometry.attributes.position.array + const bbox = geometry.boundingBox + + const halfSize = MATERIAL_SIZE / 2 + const gridOffset = halfSize + + for (let i = 0; i < vertices.length; i += 3) { + const x = vertices[i] + const y = vertices[i + 1] + const z = vertices[i + 2] + + const gridX = Math.floor((x + gridOffset) / resolution) + const gridY = Math.floor((y + gridOffset) / resolution) + + if (gridX >= 0 && gridX < gridSize && gridY >= 0 && gridY < gridSize) { + const idx = gridY * gridSize + gridX + if (z > depthMap[idx]) { + depthMap[idx] = z + } + } + } + + console.log('depthMap 采样完成, gridSize:', gridSize, '有效像素:', depthMap.filter(z => z !== -Infinity).length) + + const surfacePoints = [] + for (let gy = 0; gy < gridSize; gy++) { + for (let gx = 0; gx < gridSize; gx++) { + const idx = gy * gridSize + gx + const z = depthMap[idx] + if (z !== -Infinity) { + const x = gx * resolution - gridOffset + const y = gy * resolution - gridOffset + surfacePoints.push({ x, y, z }) + } + } + } + + onGenerateUpper({ + depthMap, + gridSize, + resolution, + points: surfacePoints, + bbox: bbox.clone() + }) + } catch (error) { + console.error('生成上包络面失败:', error) + alert('生成上包络面失败: ' + error.message) + } finally { + setIsGenerating(false) + } + } + + const repairAndGenerate = async () => { + if (!upperEnvelope) return + setIsRepairing(true) + setRepairProgress(0) + setRepairIteration(0) + + let toolRadius = 0 + let toolAngle = 0 + let tipRadius = 0 + + if (selectedTool === 'ball') { + toolRadius = 3.175 / 2 + toolAngle = 0 + tipRadius = 0 + } else if (selectedTool === 'taper15') { + toolRadius = 3.175 / 2 + toolAngle = 15 + tipRadius = 0.05 + } else if (selectedTool === 'taper30') { + toolRadius = 3.175 / 2 + toolAngle = 30 + tipRadius = 0.05 + } + + if (computeMode === 'webgpu' && webgpuAvailable) { + try { + const result = await runWebGPUConvolution( + upperEnvelope.depthMap, + upperEnvelope.gridSize, + upperEnvelope.resolution, + selectedTool, + toolRadius, + toolAngle, + tipRadius, + (msg) => { + if (msg.type === 'progress') { + setRepairProgress(msg.progress) + setRepairIteration(msg.iteration) + } + } + ) + + const { convolutionMap, toolpathMap, repairedMap } = result + const { gridSize, resolution } = upperEnvelope + + const surfacePoints = (map) => { + const points = [] + for (let gy = 0; gy < gridSize; gy++) { + for (let gx = 0; gx < gridSize; gx++) { + const idx = gy * gridSize + gx + const z = map[idx] + if (z !== -Infinity) { + const x = gx * resolution - MATERIAL_SIZE / 2 + const y = gy * resolution - MATERIAL_SIZE / 2 + points.push({ x, y, z }) + } + } + } + return points + } + + onRepairAndGenerate( + { depthMap: new Float32Array(convolutionMap), gridSize, resolution, points: surfacePoints(convolutionMap) }, + { depthMap: new Float32Array(toolpathMap), gridSize, resolution, points: surfacePoints(toolpathMap) }, + { depthMap: new Float32Array(repairedMap), gridSize, resolution, points: surfacePoints(repairedMap) } + ) + } catch (error) { + console.error('WebGPU 计算失败:', error) + alert('WebGPU 计算失败: ' + error.message) + } finally { + setIsRepairing(false) + } + } else { + workerRef.current.postMessage({ + depthMap: upperEnvelope.depthMap, + gridSize: upperEnvelope.gridSize, + resolution: upperEnvelope.resolution, + toolType: selectedTool, + toolRadius, + toolAngle, + tipRadius + }) + } + } + + return ( +
+
+

控制面板

+
+ +
+

1. 导入模型

+ + + {model &&

已加载: {model.name}

} +
+ +
+

2. 生成上包络面

+ + {upperEnvelope &&

上包络面已生成

} +
+ +
+

3. 修复并生成刀路

+
+ + +
+
+ + + +
+ + {isRepairing && ( +
+
+
+
+ + 迭代 {repairIteration} | {repairProgress.toFixed(1)}% + +
+ )} +
+
+ ) +} + +export default ControlPanel diff --git a/src/components/EnvelopeViewer.css b/src/components/EnvelopeViewer.css new file mode 100644 index 0000000..d61b88c --- /dev/null +++ b/src/components/EnvelopeViewer.css @@ -0,0 +1,119 @@ +.envelope-viewer { + display: flex; + flex-direction: column; + height: 100%; + position: relative; +} + +.viewer-controls { + display: flex; + gap: 8px; + padding: 12px; + background: #16213e; + border-bottom: 2px solid #333; +} + +.toggle-btn { + padding: 8px 16px; + border: 2px solid #444; + background: #1a1a2e; + color: #aaa; + border-radius: 4px; + cursor: pointer; + font-size: 13px; + transition: all 0.2s; +} + +.toggle-btn:disabled { + opacity: 0.3; + cursor: not-allowed; +} + +.toggle-btn:not(:disabled):hover { + border-color: #666; + color: #fff; +} + +.toggle-btn.active { + border-color: #16c79a; + color: #16c79a; + background: rgba(22, 199, 154, 0.1); +} + +.render-mode-switch { + display: flex; + align-items: center; + gap: 8px; +} + +.switch-label { + color: #666; + font-size: 13px; + transition: color 0.2s; +} + +.switch-label.active { + color: #16c79a; +} + +.switch { + position: relative; + display: inline-block; + width: 44px; + height: 22px; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #333; + transition: 0.3s; + border-radius: 22px; +} + +.slider:before { + position: absolute; + content: ""; + height: 16px; + width: 16px; + left: 3px; + bottom: 3px; + background-color: #aaa; + transition: 0.3s; + border-radius: 50%; +} + +input:checked + .slider { + background-color: #16c79a; +} + +input:checked + .slider:before { + transform: translateX(22px); + background-color: #fff; +} + +.viewer-container { + flex: 1; + position: relative; +} + +.placeholder { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: #666; + font-size: 16px; + text-align: center; + pointer-events: none; +} diff --git a/src/components/EnvelopeViewer.jsx b/src/components/EnvelopeViewer.jsx new file mode 100644 index 0000000..1320b1f --- /dev/null +++ b/src/components/EnvelopeViewer.jsx @@ -0,0 +1,428 @@ +import React, { useRef, useEffect, useState } from 'react' +import * as THREE from 'three' +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' +import './EnvelopeViewer.css' + +const MATERIAL_SIZE = 100 + +function EnvelopeViewer({ + upperEnvelope, + convolutionEnvelope, + repairedEnvelope, + toolpathEnvelope, + showUpper, + showConvolution, + showRepaired, + showToolpath, + onToggleUpper, + onToggleConvolution, + onToggleRepaired, + onToggleToolpath +}) { + const [renderMode, setRenderMode] = useState('points') + const containerRef = useRef(null) + const sceneRef = useRef(null) + const cameraRef = useRef(null) + const rendererRef = useRef(null) + const controlsRef = useRef(null) + const upperMeshRef = useRef(null) + const upperPointsRef = useRef(null) + const convolutionMeshRef = useRef(null) + const convolutionPointsRef = useRef(null) + const repairedMeshRef = useRef(null) + const repairedPointsRef = useRef(null) + const toolpathMeshRef = useRef(null) + const toolpathPointsRef = useRef(null) + + useEffect(() => { + if (!containerRef.current) return + + const scene = new THREE.Scene() + scene.background = new THREE.Color(0x1a1a2e) + sceneRef.current = scene + + const camera = new THREE.PerspectiveCamera( + 50, + containerRef.current.clientWidth / containerRef.current.clientHeight, + 0.1, + 1000 + ) + camera.position.set(80, -80, 80) + camera.lookAt(0, 0, 0) + cameraRef.current = camera + + const renderer = new THREE.WebGLRenderer({ antialias: true }) + renderer.setSize(containerRef.current.clientWidth, containerRef.current.clientHeight) + renderer.setPixelRatio(window.devicePixelRatio) + containerRef.current.appendChild(renderer.domElement) + rendererRef.current = renderer + + const controls = new OrbitControls(camera, renderer.domElement) + controls.enableDamping = true + controls.dampingFactor = 0.05 + controls.target.set(0, 0, 0) + controlsRef.current = controls + + const halfSize = MATERIAL_SIZE / 2 + + const gridHelper = new THREE.GridHelper(MATERIAL_SIZE, 10, 0x444444, 0x222222) + gridHelper.rotation.x = Math.PI / 2 + scene.add(gridHelper) + + const boxGeometry = new THREE.BoxGeometry(MATERIAL_SIZE, MATERIAL_SIZE, MATERIAL_SIZE) + const edges = new THREE.EdgesGeometry(boxGeometry) + const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0x444444 })) + scene.add(line) + + const ambientLight = new THREE.AmbientLight(0xffffff, 0.6) + scene.add(ambientLight) + + const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8) + directionalLight.position.set(50, 100, 50) + scene.add(directionalLight) + + const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.4) + directionalLight2.position.set(-50, 50, -50) + scene.add(directionalLight2) + + const axesHelper = new THREE.AxesHelper(60) + scene.add(axesHelper) + + const animate = () => { + requestAnimationFrame(animate) + controls.update() + renderer.render(scene, camera) + } + animate() + + const handleResize = () => { + if (!containerRef.current || !camera || !renderer) return + camera.aspect = containerRef.current.clientWidth / containerRef.current.clientHeight + camera.updateProjectionMatrix() + renderer.setSize(containerRef.current.clientWidth, containerRef.current.clientHeight) + } + window.addEventListener('resize', handleResize) + + return () => { + window.removeEventListener('resize', handleResize) + if (containerRef.current && renderer.domElement) { + containerRef.current.removeChild(renderer.domElement) + } + renderer.dispose() + } + }, []) + + const createSurfaceMesh = (envelope, color, zOffset = 0) => { + if (!envelope || !envelope.depthMap || envelope.depthMap.length === 0) return null + + const { gridSize, resolution, depthMap } = envelope + const halfSize = MATERIAL_SIZE / 2 + + let validCount = 0 + const totalPixels = gridSize * gridSize + for (let i = 0; i < totalPixels; i++) { + if (depthMap[i] !== -Infinity) validCount++ + } + + const density = validCount / totalPixels + console.log('createSurfaceMesh: density =', density.toFixed(4), 'validCount =', validCount) + + const geometry = new THREE.BufferGeometry() + const vertices = [] + const indices = [] + + for (let gy = 0; gy < gridSize - 1; gy++) { + for (let gx = 0; gx < gridSize - 1; gx++) { + const idx1 = gy * gridSize + gx + const idx2 = gy * gridSize + (gx + 1) + const idx3 = (gy + 1) * gridSize + gx + const idx4 = (gy + 1) * gridSize + (gx + 1) + + const z1 = depthMap[idx1] + const z2 = depthMap[idx2] + const z3 = depthMap[idx3] + const z4 = depthMap[idx4] + + if (z1 === -Infinity || z2 === -Infinity || z3 === -Infinity || z4 === -Infinity) continue + + const x1 = gx * resolution - halfSize + const y1 = gy * resolution - halfSize + const x2 = (gx + 1) * resolution - halfSize + const y2 = (gy + 1) * resolution - halfSize + + const idx = vertices.length / 3 + vertices.push(x1, y1, z1 + zOffset) + vertices.push(x2, y1, z2 + zOffset) + vertices.push(x1, y2, z3 + zOffset) + vertices.push(x2, y2, z4 + zOffset) + + indices.push(idx, idx + 2, idx + 1) + indices.push(idx + 1, idx + 2, idx + 3) + } + } + + if (vertices.length === 0) return null + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)) + geometry.setIndex(indices) + geometry.computeVertexNormals() + + const material = new THREE.MeshPhongMaterial({ + color, + specular: 0x111111, + shininess: 30, + side: THREE.DoubleSide, + transparent: true, + opacity: 0.75, + polygonOffset: true, + polygonOffsetFactor: 1, + polygonOffsetUnits: 1 + }) + + return new THREE.Mesh(geometry, material) + } + + const createPointCloud = (envelope, color, zOffset = 0) => { + if (!envelope || !envelope.depthMap || envelope.depthMap.length === 0) return null + + const { gridSize, resolution, depthMap } = envelope + const halfSize = MATERIAL_SIZE / 2 + + const positions = [] + const colors = [] + const c = new THREE.Color(color) + + for (let gy = 0; gy < gridSize; gy++) { + for (let gx = 0; gx < gridSize; gx++) { + const idx = gy * gridSize + gx + const z = depthMap[idx] + if (z === -Infinity) continue + + const x = gx * resolution - halfSize + const y = gy * resolution - halfSize + positions.push(x, y, z + zOffset) + colors.push(c.r, c.g, c.b) + } + } + + if (positions.length === 0) return null + + console.log('PointCloud 生成完成, 点数:', positions.length / 3) + + const geometry = new THREE.BufferGeometry() + geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)) + geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)) + + const material = new THREE.PointsMaterial({ + size: resolution * 0.8, + vertexColors: true, + transparent: true, + opacity: 0.8, + sizeAttenuation: true + }) + + return new THREE.Points(geometry, material) + } + + useEffect(() => { + if (!sceneRef.current) return + + if (upperMeshRef.current) { + sceneRef.current.remove(upperMeshRef.current) + upperMeshRef.current.geometry.dispose() + upperMeshRef.current.material.dispose() + upperMeshRef.current = null + } + if (upperPointsRef.current) { + sceneRef.current.remove(upperPointsRef.current) + upperPointsRef.current.geometry.dispose() + upperPointsRef.current.material.dispose() + upperPointsRef.current = null + } + + if (showUpper && upperEnvelope) { + const mesh = createSurfaceMesh(upperEnvelope, 0x16c79a, 0) + if (mesh) { + mesh.visible = renderMode === 'mesh' + sceneRef.current.add(mesh) + upperMeshRef.current = mesh + } + const points = createPointCloud(upperEnvelope, 0x16c79a, 0) + if (points) { + points.visible = renderMode === 'points' + sceneRef.current.add(points) + upperPointsRef.current = points + } + } + }, [showUpper, upperEnvelope]) + + useEffect(() => { + if (upperMeshRef.current) upperMeshRef.current.visible = renderMode === 'mesh' + if (upperPointsRef.current) upperPointsRef.current.visible = renderMode === 'points' + }, [renderMode]) + + useEffect(() => { + if (!sceneRef.current) return + + if (convolutionMeshRef.current) { + sceneRef.current.remove(convolutionMeshRef.current) + convolutionMeshRef.current.geometry.dispose() + convolutionMeshRef.current.material.dispose() + convolutionMeshRef.current = null + } + if (convolutionPointsRef.current) { + sceneRef.current.remove(convolutionPointsRef.current) + convolutionPointsRef.current.geometry.dispose() + convolutionPointsRef.current.material.dispose() + convolutionPointsRef.current = null + } + + if (showConvolution && convolutionEnvelope) { + const mesh = createSurfaceMesh(convolutionEnvelope, 0x4a90d9, 0) + if (mesh) { + mesh.visible = renderMode === 'mesh' + sceneRef.current.add(mesh) + convolutionMeshRef.current = mesh + } + const points = createPointCloud(convolutionEnvelope, 0x4a90d9, 0) + if (points) { + points.visible = renderMode === 'points' + sceneRef.current.add(points) + convolutionPointsRef.current = points + } + } + }, [showConvolution, convolutionEnvelope]) + + useEffect(() => { + if (convolutionMeshRef.current) convolutionMeshRef.current.visible = renderMode === 'mesh' + if (convolutionPointsRef.current) convolutionPointsRef.current.visible = renderMode === 'points' + }, [renderMode]) + + useEffect(() => { + if (!sceneRef.current) return + + if (repairedMeshRef.current) { + sceneRef.current.remove(repairedMeshRef.current) + repairedMeshRef.current.geometry.dispose() + repairedMeshRef.current.material.dispose() + repairedMeshRef.current = null + } + if (repairedPointsRef.current) { + sceneRef.current.remove(repairedPointsRef.current) + repairedPointsRef.current.geometry.dispose() + repairedPointsRef.current.material.dispose() + repairedPointsRef.current = null + } + + if (showRepaired && repairedEnvelope) { + const mesh = createSurfaceMesh(repairedEnvelope, 0xf5a623, 0) + if (mesh) { + mesh.visible = renderMode === 'mesh' + sceneRef.current.add(mesh) + repairedMeshRef.current = mesh + } + const points = createPointCloud(repairedEnvelope, 0xf5a623, 0) + if (points) { + points.visible = renderMode === 'points' + sceneRef.current.add(points) + repairedPointsRef.current = points + } + } + }, [showRepaired, repairedEnvelope]) + + useEffect(() => { + if (repairedMeshRef.current) repairedMeshRef.current.visible = renderMode === 'mesh' + if (repairedPointsRef.current) repairedPointsRef.current.visible = renderMode === 'points' + }, [renderMode]) + + useEffect(() => { + if (!sceneRef.current) return + + if (toolpathMeshRef.current) { + sceneRef.current.remove(toolpathMeshRef.current) + toolpathMeshRef.current.geometry.dispose() + toolpathMeshRef.current.material.dispose() + toolpathMeshRef.current = null + } + if (toolpathPointsRef.current) { + sceneRef.current.remove(toolpathPointsRef.current) + toolpathPointsRef.current.geometry.dispose() + toolpathPointsRef.current.material.dispose() + toolpathPointsRef.current = null + } + + if (showToolpath && toolpathEnvelope) { + const mesh = createSurfaceMesh(toolpathEnvelope, 0xe94560, 0) + if (mesh) { + mesh.visible = renderMode === 'mesh' + sceneRef.current.add(mesh) + toolpathMeshRef.current = mesh + } + const points = createPointCloud(toolpathEnvelope, 0xe94560, 0) + if (points) { + points.visible = renderMode === 'points' + sceneRef.current.add(points) + toolpathPointsRef.current = points + } + } + }, [showToolpath, toolpathEnvelope]) + + useEffect(() => { + if (toolpathMeshRef.current) toolpathMeshRef.current.visible = renderMode === 'mesh' + if (toolpathPointsRef.current) toolpathPointsRef.current.visible = renderMode === 'points' + }, [renderMode]) + + return ( +
+
+
+ 点云 + + 网格 +
+ + + + +
+
+ {!upperEnvelope && !convolutionEnvelope && !repairedEnvelope && !toolpathEnvelope && ( +
请先导入模型并生成包络面
+ )} +
+
+ ) +} + +export default EnvelopeViewer diff --git a/src/components/ModelViewer.css b/src/components/ModelViewer.css new file mode 100644 index 0000000..456c34a --- /dev/null +++ b/src/components/ModelViewer.css @@ -0,0 +1,16 @@ +.model-viewer { + width: 100%; + height: 100%; + position: relative; +} + +.placeholder { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: #666; + font-size: 16px; + text-align: center; + pointer-events: none; +} diff --git a/src/components/ModelViewer.jsx b/src/components/ModelViewer.jsx new file mode 100644 index 0000000..97c88ec --- /dev/null +++ b/src/components/ModelViewer.jsx @@ -0,0 +1,128 @@ +import React, { useRef, useEffect, useState } from 'react' +import * as THREE from 'three' +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' +import './ModelViewer.css' + +const MATERIAL_SIZE = 100 + +function ModelViewer({ model }) { + const containerRef = useRef(null) + const sceneRef = useRef(null) + const cameraRef = useRef(null) + const rendererRef = useRef(null) + const controlsRef = useRef(null) + const modelMeshRef = useRef(null) + + useEffect(() => { + if (!containerRef.current) return + + const scene = new THREE.Scene() + scene.background = new THREE.Color(0x1a1a2e) + sceneRef.current = scene + + const camera = new THREE.PerspectiveCamera( + 50, + containerRef.current.clientWidth / containerRef.current.clientHeight, + 0.1, + 1000 + ) + camera.position.set(80, -80, 80) + camera.lookAt(0, 0, 0) + cameraRef.current = camera + + const renderer = new THREE.WebGLRenderer({ antialias: true }) + renderer.setSize(containerRef.current.clientWidth, containerRef.current.clientHeight) + renderer.setPixelRatio(window.devicePixelRatio) + containerRef.current.appendChild(renderer.domElement) + rendererRef.current = renderer + + const controls = new OrbitControls(camera, renderer.domElement) + controls.enableDamping = true + controls.dampingFactor = 0.05 + controls.target.set(0, 0, 0) + controlsRef.current = controls + + const halfSize = MATERIAL_SIZE / 2 + + const gridHelper = new THREE.GridHelper(MATERIAL_SIZE, 10, 0x444444, 0x222222) + gridHelper.rotation.x = Math.PI / 2 + scene.add(gridHelper) + + const boxGeometry = new THREE.BoxGeometry(MATERIAL_SIZE, MATERIAL_SIZE, MATERIAL_SIZE) + const edges = new THREE.EdgesGeometry(boxGeometry) + const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0x444444 })) + line.position.set(0, 0, 0) + scene.add(line) + + const ambientLight = new THREE.AmbientLight(0xffffff, 0.6) + scene.add(ambientLight) + + const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8) + directionalLight.position.set(50, 100, 50) + scene.add(directionalLight) + + const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.4) + directionalLight2.position.set(-50, 50, -50) + scene.add(directionalLight2) + + const axesHelper = new THREE.AxesHelper(60) + scene.add(axesHelper) + + const animate = () => { + requestAnimationFrame(animate) + controls.update() + renderer.render(scene, camera) + } + animate() + + const handleResize = () => { + if (!containerRef.current || !camera || !renderer) return + camera.aspect = containerRef.current.clientWidth / containerRef.current.clientHeight + camera.updateProjectionMatrix() + renderer.setSize(containerRef.current.clientWidth, containerRef.current.clientHeight) + } + window.addEventListener('resize', handleResize) + + return () => { + window.removeEventListener('resize', handleResize) + if (containerRef.current && renderer.domElement) { + containerRef.current.removeChild(renderer.domElement) + } + renderer.dispose() + } + }, []) + + useEffect(() => { + if (!sceneRef.current) return + + if (modelMeshRef.current) { + sceneRef.current.remove(modelMeshRef.current) + modelMeshRef.current.geometry.dispose() + if (modelMeshRef.current.material) { + modelMeshRef.current.material.dispose() + } + modelMeshRef.current = null + } + + if (model) { + const material = new THREE.MeshPhongMaterial({ + color: 0x4a90d9, + specular: 0x111111, + shininess: 30, + side: THREE.DoubleSide + }) + + const mesh = new THREE.Mesh(model.geometry, material) + sceneRef.current.add(mesh) + modelMeshRef.current = mesh + } + }, [model]) + + return ( +
+ {!model &&
请导入3D模型
} +
+ ) +} + +export default ModelViewer diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..ddaf4bd --- /dev/null +++ b/src/index.css @@ -0,0 +1,19 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + overflow: hidden; +} + +#root { + width: 100vw; + height: 100vh; +} diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..5cc5991 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')).render( + + + , +) diff --git a/src/utils/webgpuConvolution.js b/src/utils/webgpuConvolution.js new file mode 100644 index 0000000..1908ee9 --- /dev/null +++ b/src/utils/webgpuConvolution.js @@ -0,0 +1,309 @@ +const EMPTY_VALUE = -50.0 + +const WEBGPU_COMPUTE_SHADER = ` +struct Params { + gridSize: u32, + kernelRadius: i32, + kernelSize: u32, + centerProfileZ: f32, +} + +struct KernelEntry { + kx: i32, + ky: i32, + profileZ: f32, + _pad: f32, +} + +@group(0) @binding(0) var inputMap: array; +@group(0) @binding(1) var outputMap: array; +@group(0) @binding(2) var params: Params; +@group(0) @binding(3) var kernel: array; + +@compute @workgroup_size(16, 16) +fn convolutionMain(@builtin(global_invocation_id) global_id: vec3) { + let gx = global_id.x; + let gy = global_id.y; + let gs = params.gridSize; + + if (gx >= gs || gy >= gs) { + return; + } + + let centerIdx = gy * gs + gx; + let currentZ = inputMap[centerIdx]; + + if (currentZ < -49.9) { + outputMap[centerIdx] = -50.0; + return; + } + + var convolutionZ = currentZ; + + for (var i: u32 = 0u; i < params.kernelSize; i = i + 1u) { + let kx = kernel[i].kx; + let ky = kernel[i].ky; + let profileZ = kernel[i].profileZ; + + let nx = i32(gx) + kx; + let ny = i32(gy) + ky; + + if (nx < 0 || nx >= i32(gs) || ny < 0 || ny >= i32(gs)) { + continue; + } + + let neighborIdx = u32(ny) * gs + u32(nx); + let neighborZ = inputMap[neighborIdx]; + if (neighborZ < -49.9) { + continue; + } + + let candidateZ = neighborZ + profileZ; + if (candidateZ > convolutionZ) { + convolutionZ = candidateZ; + } + } + + outputMap[centerIdx] = convolutionZ; +} + +@compute @workgroup_size(16, 16) +fn toolpathMain(@builtin(global_invocation_id) global_id: vec3) { + let gx = global_id.x; + let gy = global_id.y; + let gs = params.gridSize; + + if (gx >= gs || gy >= gs) { + return; + } + + let idx = gy * gs + gx; + let convZ = inputMap[idx]; + + if (convZ < -49.9) { + outputMap[idx] = -50.0; + } else { + outputMap[idx] = convZ - params.centerProfileZ; + } +} + +@compute @workgroup_size(16, 16) +fn repairedMain(@builtin(global_invocation_id) global_id: vec3) { + let gx = global_id.x; + let gy = global_id.y; + let gs = params.gridSize; + + if (gx >= gs || gy >= gs) { + return; + } + + let centerIdx = gy * gs + gx; + let convolutionZ = inputMap[centerIdx]; + + if (convolutionZ < -49.9) { + outputMap[centerIdx] = -50.0; + return; + } + + var repairedZ = convolutionZ; + + for (var i: u32 = 0u; i < params.kernelSize; i = i + 1u) { + let kx = kernel[i].kx; + let ky = kernel[i].ky; + let profileZ = kernel[i].profileZ; + + let nx = i32(gx) + kx; + let ny = i32(gy) + ky; + + if (nx < 0 || nx >= i32(gs) || ny < 0 || ny >= i32(gs)) { + continue; + } + + let neighborIdx = u32(ny) * gs + u32(nx); + let neighborConvolutionZ = inputMap[neighborIdx]; + if (neighborConvolutionZ < -49.9) { + continue; + } + + let surfaceZ = neighborConvolutionZ - profileZ; + if (surfaceZ < repairedZ) { + repairedZ = surfaceZ; + } + } + + outputMap[centerIdx] = repairedZ; +} +` + +export async function runWebGPUConvolution(depthMap, gridSize, resolution, toolType, toolRadius, toolAngle, tipRadius, onProgress) { + if (!navigator.gpu) { + throw new Error('WebGPU not supported') + } + + const adapter = await navigator.gpu.requestAdapter() + if (!adapter) { + throw new Error('No WebGPU adapter found') + } + + const device = await adapter.requestDevice() + + const gpuInputMap = new Float32Array(depthMap.length) + for (let i = 0; i < depthMap.length; i++) { + gpuInputMap[i] = depthMap[i] === -Infinity ? EMPTY_VALUE : depthMap[i] + } + + const kernelRadius = Math.ceil(toolRadius / resolution) + + const computeToolProfile = (dist) => { + if (toolType === 'ball') { + if (dist <= toolRadius) { + return Math.sqrt(toolRadius * toolRadius - dist * dist) + } + return 0 + } else { + const halfAngle = toolAngle * Math.PI / 180 / 2 + const tanHalf = Math.tan(halfAngle) + if (dist <= tipRadius) { + return (toolRadius - tipRadius) / tanHalf + } + return (toolRadius - dist) / tanHalf + } + } + + const kernel = [] + const maxDist = toolRadius + for (let ky = -kernelRadius; ky <= kernelRadius; ky++) { + for (let kx = -kernelRadius; kx <= kernelRadius; kx++) { + const dx = kx * resolution + const dy = ky * resolution + const dist = Math.sqrt(dx * dx + dy * dy) + if (dist <= maxDist) { + const profileZ = computeToolProfile(dist) + kernel.push({ kx, ky, profileZ }) + } + } + } + + const centerProfileZ = kernel.find(k => k.kx === 0 && k.ky === 0)?.profileZ || 0 + + const kernelData = new ArrayBuffer(kernel.length * 16) + const kernelView = new DataView(kernelData) + kernel.forEach((k, i) => { + kernelView.setInt32(i * 16, k.kx, true) + kernelView.setInt32(i * 16 + 4, k.ky, true) + kernelView.setFloat32(i * 16 + 8, k.profileZ, true) + kernelView.setFloat32(i * 16 + 12, 0, true) + }) + + const paramsData = new ArrayBuffer(16) + const paramsView = new DataView(paramsData) + paramsView.setUint32(0, gridSize, true) + paramsView.setInt32(4, kernelRadius, true) + paramsView.setUint32(8, kernel.length, true) + paramsView.setFloat32(12, centerProfileZ, true) + + const inputBuffer = device.createBuffer({ + size: depthMap.byteLength, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC, + }) + + const outputBuffer = device.createBuffer({ + size: depthMap.byteLength, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC, + }) + + const stagingBuffer = device.createBuffer({ + size: depthMap.byteLength, + usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST, + }) + + const kernelBuffer = device.createBuffer({ + size: kernelData.byteLength, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, + }) + + const paramsBuffer = device.createBuffer({ + size: paramsData.byteLength, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + }) + + device.queue.writeBuffer(inputBuffer, 0, gpuInputMap) + device.queue.writeBuffer(kernelBuffer, 0, kernelData) + device.queue.writeBuffer(paramsBuffer, 0, paramsData) + + const shaderModule = device.createShaderModule({ code: WEBGPU_COMPUTE_SHADER }) + + const runPass = async (entryPoint, input, output) => { + const bindGroupLayout = device.createBindGroupLayout({ + entries: [ + { binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } }, + { binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'storage' } }, + { binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'uniform' } }, + { binding: 3, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } }, + ] + }) + + const pipeline = device.createComputePipeline({ + layout: device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] }), + compute: { module: shaderModule, entryPoint } + }) + + const bindGroup = device.createBindGroup({ + layout: bindGroupLayout, + entries: [ + { binding: 0, resource: { buffer: input } }, + { binding: 1, resource: { buffer: output } }, + { binding: 2, resource: { buffer: paramsBuffer } }, + { binding: 3, resource: { buffer: kernelBuffer } }, + ] + }) + + const commandEncoder = device.createCommandEncoder() + const passEncoder = commandEncoder.beginComputePass() + passEncoder.setPipeline(pipeline) + passEncoder.setBindGroup(0, bindGroup) + passEncoder.dispatchWorkgroups(Math.ceil(gridSize / 16), Math.ceil(gridSize / 16)) + passEncoder.end() + commandEncoder.copyBufferToBuffer(output, 0, stagingBuffer, 0, depthMap.byteLength) + device.queue.submit([commandEncoder.finish()]) + + await stagingBuffer.mapAsync(GPUMapMode.READ) + const result = new Float32Array(stagingBuffer.getMappedRange().slice(0)) + stagingBuffer.unmap() + return result + } + + if (onProgress) onProgress({ type: 'progress', progress: 5, iteration: 1 }) + + const convolutionMap = await runPass('convolutionMain', inputBuffer, outputBuffer) + if (onProgress) onProgress({ type: 'progress', progress: 35, iteration: 1 }) + + device.queue.writeBuffer(inputBuffer, 0, convolutionMap) + const toolpathMap = await runPass('toolpathMain', inputBuffer, outputBuffer) + if (onProgress) onProgress({ type: 'progress', progress: 50, iteration: 1 }) + + device.queue.writeBuffer(inputBuffer, 0, convolutionMap) + const repairedMap = await runPass('repairedMain', inputBuffer, outputBuffer) + if (onProgress) onProgress({ type: 'progress', progress: 95, iteration: 1 }) + + inputBuffer.destroy() + outputBuffer.destroy() + stagingBuffer.destroy() + kernelBuffer.destroy() + paramsBuffer.destroy() + device.destroy() + + const convertBack = (arr) => { + const result = new Float32Array(arr.length) + for (let i = 0; i < arr.length; i++) { + result[i] = arr[i] < -49.9 ? -Infinity : arr[i] + } + return result + } + + return { + convolutionMap: convertBack(convolutionMap), + toolpathMap: convertBack(toolpathMap), + repairedMap: convertBack(repairedMap) + } +} diff --git a/src/workers/repairWorker.js b/src/workers/repairWorker.js new file mode 100644 index 0000000..02a1811 --- /dev/null +++ b/src/workers/repairWorker.js @@ -0,0 +1,155 @@ +self.onmessage = function(e) { + const { depthMap, gridSize, resolution, toolType, toolRadius, toolAngle, tipRadius } = e.data + + const inputMap = new Float32Array(depthMap) + const convolutionMap = new Float32Array(depthMap.length) + convolutionMap.fill(-Infinity) + const repairedMap = new Float32Array(depthMap.length) + repairedMap.fill(-Infinity) + + const kernelRadius = toolType === 'ball' + ? Math.ceil(toolRadius / resolution) + : Math.ceil(toolRadius / resolution) + + const computeToolProfile = (dist) => { + if (toolType === 'ball') { + if (dist <= toolRadius) { + return Math.sqrt(toolRadius * toolRadius - dist * dist) + } + return 0 + } else { + const halfAngle = toolAngle * Math.PI / 180 / 2 + const tanHalf = Math.tan(halfAngle) + if (dist <= tipRadius) { + return (toolRadius - tipRadius) / tanHalf + } + return (toolRadius - dist) / tanHalf + } + } + + const precomputeKernel = () => { + const kernel = [] + const maxDist = toolType === 'ball' ? toolRadius : toolRadius + for (let ky = -kernelRadius; ky <= kernelRadius; ky++) { + for (let kx = -kernelRadius; kx <= kernelRadius; kx++) { + const dx = kx * resolution + const dy = ky * resolution + const dist = Math.sqrt(dx * dx + dy * dy) + if (dist <= maxDist) { + const profileZ = computeToolProfile(dist) + kernel.push({ kx, ky, profileZ }) + } + } + } + return kernel + } + + const kernel = precomputeKernel() + + let processedCount = 0 + const totalPixels = gridSize * gridSize + + for (let gy = 0; gy < gridSize; gy++) { + for (let gx = 0; gx < gridSize; gx++) { + const centerIdx = gy * gridSize + gx + const currentZ = inputMap[centerIdx] + + if (currentZ === -Infinity) { + processedCount++ + continue + } + + let convolutionZ = currentZ + + for (const { kx, ky, profileZ } of kernel) { + const nx = gx + kx + const ny = gy + ky + + if (nx < 0 || nx >= gridSize || ny < 0 || ny >= gridSize) continue + + const neighborIdx = ny * gridSize + nx + const neighborZ = inputMap[neighborIdx] + if (neighborZ === -Infinity) continue + + const candidateZ = neighborZ + profileZ + if (candidateZ > convolutionZ) { + convolutionZ = candidateZ + } + } + + convolutionMap[centerIdx] = convolutionZ + processedCount++ + + if (gy % 10 === 0) { + const progress = (processedCount / totalPixels) * 33 + self.postMessage({ + type: 'progress', + progress: Math.min(progress, 32.9), + iteration: 1 + }) + } + } + } + + const centerProfileZ = kernel.find(k => k.kx === 0 && k.ky === 0)?.profileZ || 0 + const toolpathMap = new Float32Array(depthMap.length) + for (let i = 0; i < depthMap.length; i++) { + if (convolutionMap[i] === -Infinity) { + toolpathMap[i] = -Infinity + } else { + toolpathMap[i] = convolutionMap[i] - centerProfileZ + } + } + + processedCount = 0 + + for (let gy = 0; gy < gridSize; gy++) { + for (let gx = 0; gx < gridSize; gx++) { + const centerIdx = gy * gridSize + gx + const convolutionZ = convolutionMap[centerIdx] + + if (convolutionZ === -Infinity) { + processedCount++ + continue + } + + let repairedZ = convolutionZ + + for (const { kx, ky, profileZ } of kernel) { + const nx = gx + kx + const ny = gy + ky + + if (nx < 0 || nx >= gridSize || ny < 0 || ny >= gridSize) continue + + const neighborIdx = ny * gridSize + nx + const neighborConvolutionZ = convolutionMap[neighborIdx] + if (neighborConvolutionZ === -Infinity) continue + + const surfaceZ = neighborConvolutionZ - profileZ + if (surfaceZ < repairedZ) { + repairedZ = surfaceZ + } + } + + repairedMap[centerIdx] = repairedZ + processedCount++ + + if (gy % 10 === 0) { + const progress = 50 + (processedCount / totalPixels) * 50 + self.postMessage({ + type: 'progress', + progress: Math.min(progress, 99.9), + iteration: 1 + }) + } + } + } + + self.postMessage({ + type: 'complete', + convolutionMap, + toolpathMap, + repairedMap, + iterations: 1 + }) +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..9ffcc67 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], +})