🎉 init: pose editor

pull/6/head
Yu Zhu 2023-03-05 16:41:58 +08:00
commit 739523269f
17 changed files with 18388 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

21
index.html Normal file
View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<canvas id="canvas" tabindex="-1"></canvas>
<div class="gallery">
<a id="test"><img id="pose"></a>
<img id="depth">
</div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

26
package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "open-pose-editor",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"dat.gui": "^0.7.9",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"three": "^0.150.1"
},
"devDependencies": {
"@types/dat.gui": "^0.7.7",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@types/three": "^0.149.0",
"@vitejs/plugin-react": "^3.1.0",
"typescript": "^4.9.3",
"vite": "^4.1.0"
}
}

892
pnpm-lock.yaml Normal file
View File

@ -0,0 +1,892 @@
lockfileVersion: 5.4
specifiers:
'@types/dat.gui': ^0.7.7
'@types/react': ^18.0.27
'@types/react-dom': ^18.0.10
'@types/three': ^0.149.0
'@vitejs/plugin-react': ^3.1.0
dat.gui: ^0.7.9
react: ^18.2.0
react-dom: ^18.2.0
three: ^0.150.1
typescript: ^4.9.3
vite: ^4.1.0
dependencies:
dat.gui: 0.7.9
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
three: 0.150.1
devDependencies:
'@types/dat.gui': 0.7.7
'@types/react': 18.0.28
'@types/react-dom': 18.0.11
'@types/three': 0.149.0
'@vitejs/plugin-react': 3.1.0_vite@4.1.4
typescript: 4.9.5
vite: 4.1.4
packages:
/@ampproject/remapping/2.2.0:
resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
engines: {node: '>=6.0.0'}
dependencies:
'@jridgewell/gen-mapping': 0.1.1
'@jridgewell/trace-mapping': 0.3.17
dev: true
/@babel/code-frame/7.18.6:
resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/highlight': 7.18.6
dev: true
/@babel/compat-data/7.21.0:
resolution: {integrity: sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/core/7.21.0:
resolution: {integrity: sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==}
engines: {node: '>=6.9.0'}
dependencies:
'@ampproject/remapping': 2.2.0
'@babel/code-frame': 7.18.6
'@babel/generator': 7.21.1
'@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.0
'@babel/helper-module-transforms': 7.21.2
'@babel/helpers': 7.21.0
'@babel/parser': 7.21.2
'@babel/template': 7.20.7
'@babel/traverse': 7.21.2
'@babel/types': 7.21.2
convert-source-map: 1.9.0
debug: 4.3.4
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.0
transitivePeerDependencies:
- supports-color
dev: true
/@babel/generator/7.21.1:
resolution: {integrity: sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.2
'@jridgewell/gen-mapping': 0.3.2
'@jridgewell/trace-mapping': 0.3.17
jsesc: 2.5.2
dev: true
/@babel/helper-compilation-targets/7.20.7_@babel+core@7.21.0:
resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/compat-data': 7.21.0
'@babel/core': 7.21.0
'@babel/helper-validator-option': 7.21.0
browserslist: 4.21.5
lru-cache: 5.1.1
semver: 6.3.0
dev: true
/@babel/helper-environment-visitor/7.18.9:
resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-function-name/7.21.0:
resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.20.7
'@babel/types': 7.21.2
dev: true
/@babel/helper-hoist-variables/7.18.6:
resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.2
dev: true
/@babel/helper-module-imports/7.18.6:
resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.2
dev: true
/@babel/helper-module-transforms/7.21.2:
resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-module-imports': 7.18.6
'@babel/helper-simple-access': 7.20.2
'@babel/helper-split-export-declaration': 7.18.6
'@babel/helper-validator-identifier': 7.19.1
'@babel/template': 7.20.7
'@babel/traverse': 7.21.2
'@babel/types': 7.21.2
transitivePeerDependencies:
- supports-color
dev: true
/@babel/helper-plugin-utils/7.20.2:
resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-simple-access/7.20.2:
resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.2
dev: true
/@babel/helper-split-export-declaration/7.18.6:
resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.2
dev: true
/@babel/helper-string-parser/7.19.4:
resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-validator-identifier/7.19.1:
resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-validator-option/7.21.0:
resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helpers/7.21.0:
resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.20.7
'@babel/traverse': 7.21.2
'@babel/types': 7.21.2
transitivePeerDependencies:
- supports-color
dev: true
/@babel/highlight/7.18.6:
resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-validator-identifier': 7.19.1
chalk: 2.4.2
js-tokens: 4.0.0
dev: true
/@babel/parser/7.21.2:
resolution: {integrity: sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.21.2
dev: true
/@babel/plugin-transform-react-jsx-self/7.21.0_@babel+core@7.21.0:
resolution: {integrity: sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.21.0
'@babel/helper-plugin-utils': 7.20.2
dev: true
/@babel/plugin-transform-react-jsx-source/7.19.6_@babel+core@7.21.0:
resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.21.0
'@babel/helper-plugin-utils': 7.20.2
dev: true
/@babel/template/7.20.7:
resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/parser': 7.21.2
'@babel/types': 7.21.2
dev: true
/@babel/traverse/7.21.2:
resolution: {integrity: sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/generator': 7.21.1
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.21.0
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.21.2
'@babel/types': 7.21.2
debug: 4.3.4
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: true
/@babel/types/7.21.2:
resolution: {integrity: sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.19.4
'@babel/helper-validator-identifier': 7.19.1
to-fast-properties: 2.0.0
dev: true
/@esbuild/android-arm/0.16.17:
resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm64/0.16.17:
resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-x64/0.16.17:
resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-arm64/0.16.17:
resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-x64/0.16.17:
resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-arm64/0.16.17:
resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-x64/0.16.17:
resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm/0.16.17:
resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm64/0.16.17:
resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ia32/0.16.17:
resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64/0.16.17:
resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-mips64el/0.16.17:
resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ppc64/0.16.17:
resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-riscv64/0.16.17:
resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-s390x/0.16.17:
resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-x64/0.16.17:
resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/netbsd-x64/0.16.17:
resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openbsd-x64/0.16.17:
resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/sunos-x64/0.16.17:
resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-arm64/0.16.17:
resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-ia32/0.16.17:
resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-x64/0.16.17:
resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@jridgewell/gen-mapping/0.1.1:
resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==}
engines: {node: '>=6.0.0'}
dependencies:
'@jridgewell/set-array': 1.1.2
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
/@jridgewell/gen-mapping/0.3.2:
resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
engines: {node: '>=6.0.0'}
dependencies:
'@jridgewell/set-array': 1.1.2
'@jridgewell/sourcemap-codec': 1.4.14
'@jridgewell/trace-mapping': 0.3.17
dev: true
/@jridgewell/resolve-uri/3.1.0:
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
engines: {node: '>=6.0.0'}
dev: true
/@jridgewell/set-array/1.1.2:
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
engines: {node: '>=6.0.0'}
dev: true
/@jridgewell/sourcemap-codec/1.4.14:
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
dev: true
/@jridgewell/trace-mapping/0.3.17:
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
dependencies:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
/@types/dat.gui/0.7.7:
resolution: {integrity: sha512-CxLCme0He5Jk3uQwfO/fGZMyNhb/ypANzqX0yU9lviBQMlen5SOvQTBQ/Cd9x5mFlUAK5Tk8RgvTyLj1nYkz+w==}
dev: true
/@types/prop-types/15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
dev: true
/@types/react-dom/18.0.11:
resolution: {integrity: sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==}
dependencies:
'@types/react': 18.0.28
dev: true
/@types/react/18.0.28:
resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==}
dependencies:
'@types/prop-types': 15.7.5
'@types/scheduler': 0.16.2
csstype: 3.1.1
dev: true
/@types/scheduler/0.16.2:
resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
dev: true
/@types/three/0.149.0:
resolution: {integrity: sha512-fgNBm9LWc65ER/W0cvoXdC0iMy7Ke9e2CONmEr6Jt8sDSY3sw4DgOubZfmdZ747dkPhbQrgRQAWwDEr2S/7IEg==}
dependencies:
'@types/webxr': 0.5.1
dev: true
/@types/webxr/0.5.1:
resolution: {integrity: sha512-xlFXPfgJR5vIuDefhaHuUM9uUgvPaXB6GKdXy2gdEh8gBWQZ2ul24AJz3foUd8NNKlSTQuWYJpCb1/pL81m1KQ==}
dev: true
/@vitejs/plugin-react/3.1.0_vite@4.1.4:
resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
vite: ^4.1.0-beta.0
dependencies:
'@babel/core': 7.21.0
'@babel/plugin-transform-react-jsx-self': 7.21.0_@babel+core@7.21.0
'@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.21.0
magic-string: 0.27.0
react-refresh: 0.14.0
vite: 4.1.4
transitivePeerDependencies:
- supports-color
dev: true
/ansi-styles/3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
dependencies:
color-convert: 1.9.3
dev: true
/browserslist/4.21.5:
resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
caniuse-lite: 1.0.30001460
electron-to-chromium: 1.4.318
node-releases: 2.0.10
update-browserslist-db: 1.0.10_browserslist@4.21.5
dev: true
/caniuse-lite/1.0.30001460:
resolution: {integrity: sha512-Bud7abqjvEjipUkpLs4D7gR0l8hBYBHoa+tGtKJHvT2AYzLp1z7EmVkUT4ERpVUfca8S2HGIVs883D8pUH1ZzQ==}
dev: true
/chalk/2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
dependencies:
ansi-styles: 3.2.1
escape-string-regexp: 1.0.5
supports-color: 5.5.0
dev: true
/color-convert/1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
color-name: 1.1.3
dev: true
/color-name/1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
dev: true
/convert-source-map/1.9.0:
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
dev: true
/csstype/3.1.1:
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
dev: true
/dat.gui/0.7.9:
resolution: {integrity: sha512-sCNc1OHobc+Erc1HqiswYgHdVNpSJUlk/Hz8vzOCsER7rl+oF/4+v8GXFUyCgtXpoCX6+bnmg07DedLvBLwYKQ==}
dev: false
/debug/4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.2
dev: true
/electron-to-chromium/1.4.318:
resolution: {integrity: sha512-EQHZE/yO9Sg6gIP9VezPltDWFA8pMnv7dNRb4Y0ukels3iyXTH313gbHvK/tZwUF+jeh5F5fDf1rqWb8ZWX8fw==}
dev: true
/esbuild/0.16.17:
resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.16.17
'@esbuild/android-arm64': 0.16.17
'@esbuild/android-x64': 0.16.17
'@esbuild/darwin-arm64': 0.16.17
'@esbuild/darwin-x64': 0.16.17
'@esbuild/freebsd-arm64': 0.16.17
'@esbuild/freebsd-x64': 0.16.17
'@esbuild/linux-arm': 0.16.17
'@esbuild/linux-arm64': 0.16.17
'@esbuild/linux-ia32': 0.16.17
'@esbuild/linux-loong64': 0.16.17
'@esbuild/linux-mips64el': 0.16.17
'@esbuild/linux-ppc64': 0.16.17
'@esbuild/linux-riscv64': 0.16.17
'@esbuild/linux-s390x': 0.16.17
'@esbuild/linux-x64': 0.16.17
'@esbuild/netbsd-x64': 0.16.17
'@esbuild/openbsd-x64': 0.16.17
'@esbuild/sunos-x64': 0.16.17
'@esbuild/win32-arm64': 0.16.17
'@esbuild/win32-ia32': 0.16.17
'@esbuild/win32-x64': 0.16.17
dev: true
/escalade/3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
engines: {node: '>=6'}
dev: true
/escape-string-regexp/1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
dev: true
/fsevents/2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
/function-bind/1.1.1:
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
dev: true
/gensync/1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
dev: true
/globals/11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
dev: true
/has-flag/3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
dev: true
/has/1.0.3:
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
engines: {node: '>= 0.4.0'}
dependencies:
function-bind: 1.1.1
dev: true
/is-core-module/2.11.0:
resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==}
dependencies:
has: 1.0.3
dev: true
/js-tokens/4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
/jsesc/2.5.2:
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
engines: {node: '>=4'}
hasBin: true
dev: true
/json5/2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
hasBin: true
dev: true
/loose-envify/1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
dependencies:
js-tokens: 4.0.0
dev: false
/lru-cache/5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
dependencies:
yallist: 3.1.1
dev: true
/magic-string/0.27.0:
resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
/nanoid/3.3.4:
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
dev: true
/node-releases/2.0.10:
resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
dev: true
/path-parse/1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true
/picocolors/1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
dev: true
/postcss/8.4.21:
resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.4
picocolors: 1.0.0
source-map-js: 1.0.2
dev: true
/react-dom/18.2.0_react@18.2.0:
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
peerDependencies:
react: ^18.2.0
dependencies:
loose-envify: 1.4.0
react: 18.2.0
scheduler: 0.23.0
dev: false
/react-refresh/0.14.0:
resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==}
engines: {node: '>=0.10.0'}
dev: true
/react/18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'}
dependencies:
loose-envify: 1.4.0
dev: false
/resolve/1.22.1:
resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
hasBin: true
dependencies:
is-core-module: 2.11.0
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
dev: true
/rollup/3.18.0:
resolution: {integrity: sha512-J8C6VfEBjkvYPESMQYxKHxNOh4A5a3FlP+0BETGo34HEcE4eTlgCrO2+eWzlu2a/sHs2QUkZco+wscH7jhhgWg==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
optionalDependencies:
fsevents: 2.3.2
dev: true
/scheduler/0.23.0:
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
dependencies:
loose-envify: 1.4.0
dev: false
/semver/6.3.0:
resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
hasBin: true
dev: true
/source-map-js/1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
dev: true
/supports-color/5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
dependencies:
has-flag: 3.0.0
dev: true
/supports-preserve-symlinks-flag/1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
dev: true
/three/0.150.1:
resolution: {integrity: sha512-5C1MqKUWaHYo13BX0Q64qcdwImgnnjSOFgBscOzAo8MYCzEtqfQqorEKMcajnA3FHy1yVlIe9AmaMQ0OQracNA==}
dev: false
/to-fast-properties/2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
dev: true
/typescript/4.9.5:
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
/update-browserslist-db/1.0.10_browserslist@4.21.5:
resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
dependencies:
browserslist: 4.21.5
escalade: 3.1.1
picocolors: 1.0.0
dev: true
/vite/4.1.4:
resolution: {integrity: sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
'@types/node': '>= 14'
less: '*'
sass: '*'
stylus: '*'
sugarss: '*'
terser: ^5.4.0
peerDependenciesMeta:
'@types/node':
optional: true
less:
optional: true
sass:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
dependencies:
esbuild: 0.16.17
postcss: 8.4.21
resolve: 1.22.1
rollup: 3.18.0
optionalDependencies:
fsevents: 2.3.2
dev: true
/yallist/3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
dev: true

16558
public/models/hand.obj Normal file

File diff suppressed because it is too large Load Diff

315
src/body.ts Normal file
View File

@ -0,0 +1,315 @@
import * as THREE from "three"
import { Object3D } from "three"
const coco_body_keypoints = [
"nose",
"neck",
"right_shoulder",
"right_elbow",
"right_wrist",
"left_shoulder",
"left_elbow",
"left_wrist",
"right_hip",
"right_knee",
"right_ankle",
"left_hip",
"left_knee",
"left_ankle",
"right_eye",
"left_eye",
"right_ear",
"left_ear",
]
const connect_keypoints = [
[1, 2],
[1, 5],
[2, 3],
[3, 4],
[5, 6],
[6, 7],
[1, 8],
[8, 9],
[9, 10],
[1, 11],
[11, 12],
[12, 13],
[0, 1],
[0, 14],
[14, 16],
[0, 15],
[15, 17],
] as const
const connect_color = [
[255, 0, 0],// [1, 2], 0
[255, 85, 0],// [1, 5], 1
[255, 170, 0],// [2, 3], 2
[255, 255, 0],// [3, 4], 3
[170, 255, 0],// [5, 6], 4
[85, 255, 0],// [6, 7], 5
[0, 255, 0],// [1, 8], 6
[0, 255, 85],// [8, 9], 7
[0, 255, 170],// [9, 10], 8
[0, 255, 255],// [1, 11], 9
[0, 170, 255],// [11, 12], 10
[0, 85, 255],// [12, 13], 11
[0, 0, 255],// [0, 1], 12
[85, 0, 255],// [0, 14], 13
[170, 0, 255],// [14, 16], 14
[255, 0, 255],// [0, 15], 15
[255, 0, 170],// [15, 17], 16
[255, 0, 85],// 17
] as const
const JointRadius = 1
function ToHexColor([r, g, b]: readonly [number, number, number]) {
return (r << 16) + (g << 8) + b
}
function SearchColor(start: number, end: number) {
const index = connect_keypoints.findIndex(([s, e]) => s === start && e === end)
if (typeof index !== "undefined") {
const [r, g, b] = connect_color[index]
return (r << 16) + (g << 8) + b
}
return null
}
function GetColorOfLink(start: Object3D, end: Object3D) {
if (!start.name || !end.name)
return null;
const indexStart = coco_body_keypoints.indexOf(start.name)
const indexEnd = coco_body_keypoints.indexOf(end.name)
if (indexStart === -1 || indexEnd === -1)
return null
if (indexStart > indexEnd)
return SearchColor(indexEnd, indexStart)
else
return SearchColor(indexStart, indexEnd)
}
function GetPresetColorOfJoint(name: string) {
const index = coco_body_keypoints.indexOf(name)
return index !== -1 ? ToHexColor(connect_color[index]) : 0x0
}
function CreateLink(parent: Object3D, endObject: THREE.Object3D) {
CreateLink2(parent, endObject, new THREE.Vector3(0, 0, 0))
}
function CreateLink2(parent: Object3D, endObject: THREE.Object3D, start: THREE.Object3D | THREE.Vector3) {
const startPosition = start instanceof THREE.Vector3 ? start.clone() : start.position.clone()
const endPostion = endObject.position
const distance = startPosition.distanceTo(endPostion)
const presetColor = GetColorOfLink(start instanceof THREE.Vector3 ? parent : start, endObject)
const material = new THREE.MeshBasicMaterial({ color: presetColor ?? 0x0, opacity: 0.6, transparent: true });
const mesh = new THREE.Mesh(new THREE.SphereGeometry(JointRadius), material);
mesh.name = parent.name
// 将拉伸后的球体放在中点并计算旋转轴和jiaodu
const origin = startPosition.clone().add(endPostion).multiplyScalar(0.5)
const v = endPostion.clone().sub(startPosition)
const unit = new THREE.Vector3(1, 0, 0)
const axis = unit.clone().cross(v)
const angle = unit.clone().angleTo(v)
mesh.scale.copy(new THREE.Vector3(distance / 2, 1, 1))
mesh.position.copy(origin);
mesh.setRotationFromAxisAngle(axis.normalize(), angle);
parent.add(mesh);
}
function Joint(name: string) {
const object = new THREE.Mesh(
new THREE.SphereGeometry(JointRadius),
new THREE.MeshBasicMaterial({ color: GetPresetColorOfJoint(name) })
)
object.name = name
return object
}
function Torso(x: number, y: number, z: number) {
const width = 30
const height = 50
let object = new THREE.Group()
object.name = "torso"
object.translateX(x);
object.translateY(y);
object.translateZ(z);
const neck = Joint("neck")
neck.translateY(height / 2 - 5);
object.add(neck)
const right_shoulder = Joint("right_shoulder")
right_shoulder.translateX(-width / 2 - 2);
right_shoulder.translateY(height / 2 - 5);
const left_shoulder = Joint("left_shoulder")
left_shoulder.translateX(width / 2 + 2);
left_shoulder.translateY(height / 2 - 5);
object.add(right_shoulder)
object.add(left_shoulder)
const right_hip = Joint("right_hip")
right_hip.translateX(-width / 2 + 8);
right_hip.translateY(-height / 2);
const left_hip = Joint("left_hip")
left_hip.translateX(width / 2 - 8);
left_hip.translateY(-height / 2);
CreateLink2(object, neck, right_hip)
CreateLink2(object, neck, left_hip)
CreateLink2(object, neck, right_shoulder)
CreateLink2(object, neck, left_shoulder)
object.add(right_hip)
object.add(left_hip)
return { neck, torso: object, right_shoulder, left_shoulder, right_hip, left_hip }
}
function Head(x: number, y: number, z: number) {
const nose = Joint("nose")
nose.translateX(x);
nose.translateY(y);
nose.translateZ(z);
const right_eye = Joint("right_eye")
right_eye.translateX(-3);
right_eye.translateY(3);
right_eye.translateZ(-3);
const left_eye = Joint("left_eye")
left_eye.translateX(3);
left_eye.translateY(3);
left_eye.translateZ(-3);
CreateLink(nose, right_eye)
CreateLink(nose, left_eye)
nose.add(right_eye)
nose.add(left_eye)
const right_ear = Joint("right_ear")
right_ear.translateX(-4);
right_ear.translateY(-3);
right_ear.translateZ(-8);
const left_ear = Joint("left_ear")
left_ear.translateX(4);
left_ear.translateY(-3);
left_ear.translateZ(-8);
CreateLink(right_eye, right_ear)
CreateLink(left_eye, left_ear)
right_eye.add(right_ear)
left_eye.add(left_ear)
return { nose, right_eye, left_eye, right_ear, left_ear }
}
function Elbow(name: string, x: number, y: number, z: number) {
const object = Joint(name)
object.translateX(x);
object.translateY(y);
object.translateZ(z);
return object
}
function Knee(name: string, x: number, y: number, z: number) {
const object = Joint(name)
object.translateX(x);
object.translateY(y);
object.translateZ(z);
return object
}
function Ankle(name: string, x: number, y: number, z: number) {
const object = Joint(name)
object.translateX(x);
object.translateY(y);
object.translateZ(z);;
return object
}
export function CreateBody(hand: Object3D) {
const { torso, right_shoulder, left_shoulder, right_hip, left_hip, neck } = Torso(0, 115, 0)
const { nose, left_ear, right_ear, right_eye, left_eye } = Head(0, 20, 14)
const right_elbow = Elbow("right_elbow", 0, -25, 0)
const left_elbow = Elbow("left_elbow", 0, -25, 0)
const right_wrist = Elbow("right_wrist", 0, -25, 0)
const left_wrist = Elbow("left_wrist", 0, -25, 0)
const right_knee = Knee("right_knee", 0, -40, 0)
const left_knee = Knee("left_knee", 0, -40, 0)
const right_ankle = Ankle("right_ankle", 0, -36, 0)
const left_ankle = Ankle("left_ankle", 0, -36, 0)
neck.add(nose)
right_shoulder.add(right_elbow)
left_shoulder.add(left_elbow)
right_elbow.add(right_wrist)
left_elbow.add(left_wrist)
right_hip.add(right_knee)
left_hip.add(left_knee)
right_knee.add(right_ankle)
left_knee.add(left_ankle)
CreateLink(neck, nose)
CreateLink(right_shoulder, right_elbow)
CreateLink(left_shoulder, left_elbow)
CreateLink(right_elbow, right_wrist)
CreateLink(left_elbow, left_wrist)
CreateLink(right_hip, right_knee)
CreateLink(left_hip, left_knee)
CreateLink(right_knee, right_ankle)
CreateLink(left_knee, left_ankle)
const right_hand = hand.clone()
const left_hand = hand.clone()
// left_hand.scale.x = -1
// left_hand.scale.y = -1
right_hand.rotateZ(Math.PI)
right_hand.rotateY(-Math.PI / 2)
right_hand.translateY(17)
right_hand.rotateX(-Math.PI / 12)
right_hand.scale.multiplyScalar(0.8)
left_hand.scale.x = -1
left_hand.rotateZ(Math.PI)
left_hand.rotateY(Math.PI / 2)
left_hand.translateY(17)
left_hand.rotateX(-Math.PI / 12)
left_hand.scale.multiplyScalar(0.8)
right_elbow.add(right_hand)
left_elbow.add(left_hand)
return torso
}

4
src/config.ts Normal file
View File

@ -0,0 +1,4 @@
export const options: Record<string, any> = {
clearColor: 0xaaaaaa,
moveMode: false,
}

333
src/editor.ts Normal file
View File

@ -0,0 +1,333 @@
import * as THREE from "three"
import { MeshDepthMaterial, Object3D } from "three";
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { TransformControls } from 'three/examples/jsm/controls/TransformControls';
import Stats from "three/examples/jsm/libs/stats.module";
import { CreateBody } from "./body";
import { options } from "./config";
import { SetScreenShot } from "./image";
import { LoadObjFile } from "./loader";
import { getCurrentTime } from "./util";
const pickableObjectNames: string[] = ["torso",
"neck",
"right_shoulder",
"left_shoulder",
"right_elbow",
"left_elbow",
"right_hip",
"left_hip",
"right_knee",
"left_knee",
]
export class BodyEditor {
renderer: THREE.WebGLRenderer
scene: THREE.Scene
gridHelper: THREE.GridHelper
axesHelper: THREE.AxesHelper
camera: THREE.PerspectiveCamera
orbitControls: OrbitControls
transformControl: TransformControls
dlight: THREE.DirectionalLight
alight: THREE.AmbientLight
raycaster = new THREE.Raycaster()
IsClick = false
templateBody: Object3D | null = null
stats: Stats
constructor(canvas: HTMLCanvasElement) {
this.renderer = new THREE.WebGLRenderer({
canvas,
antialias: true,
logarithmicDepthBuffer: true
});
this.renderer.setClearColor(options.clearColor, 1.0)
this.scene = new THREE.Scene();
this.gridHelper = new THREE.GridHelper(800, 200)
this.axesHelper = new THREE.AxesHelper(1000);
this.scene.add(this.gridHelper)
this.scene.add(this.axesHelper);
let aspect = window.innerWidth / window.innerHeight;
this.camera = new THREE.PerspectiveCamera(60, aspect, 0.1, 1000);
this.camera.position.set(0, 100, 200)
this.camera.lookAt(0, 100, 0);
this.camera.near = 130
this.camera.far = 600
this.camera.updateProjectionMatrix();
this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
this.orbitControls.target = new THREE.Vector3(0, 100, 0);
this.orbitControls.update();
this.transformControl = new TransformControls(this.camera, this.renderer.domElement);
this.transformControl.setMode("rotate");//旋转
// transformControl.setSize(0.4);
this.transformControl.setSpace("local");
this.transformControl.addEventListener('change', () => this.renderer.render(this.scene, this.camera));
this.transformControl.addEventListener('mouseDown', () => {
this.orbitControls.enabled = false
})
this.transformControl.addEventListener('mouseUp', () => {
this.orbitControls.enabled = true
})
this.scene.add(this.transformControl);
// Light
this.dlight = new THREE.DirectionalLight(0xffffff, 1.0);
this.dlight.position.set(0, 160, 1000);
this.scene.add(this.dlight);
this.alight = new THREE.AmbientLight(0xffffff, 0.5);
this.scene.add(this.alight);
document.addEventListener('mousedown', () => this.IsClick = true, false)
document.addEventListener('mousemove', () => this.IsClick = false, false)
document.addEventListener('mouseup', this.onDocumentMouseDown.bind(this), false)
window.addEventListener('resize', this.handleResize.bind(this))
this.stats = Stats()
document.body.appendChild(this.stats.dom)
this.render()
this.handleResize()
}
onDocumentMouseDown(event: MouseEvent) {
this.raycaster.setFromCamera(
{
x: (event.clientX / this.renderer.domElement.clientWidth) * 2 - 1,
y: -(event.clientY / this.renderer.domElement.clientHeight) * 2 + 1
},
this.camera
)
let intersects: THREE.Intersection[] = this.raycaster.intersectObjects(this.scene.children.filter(o => o?.name === "torso"), true)
let intersectedObject: THREE.Object3D | null = intersects.length > 0 ? intersects[0].object : null
const name = intersectedObject ? intersectedObject.name : ""
let obj: Object3D | null = intersectedObject
console.log(obj?.name)
if (this.IsClick) {
if (!obj) {
this.transformControl.detach()
return
}
if (options.moveMode) {
while (obj) {
if (obj?.name !== "torso")
obj = obj.parent
else
break
}
while (obj) {
if (obj?.parent?.name == name)
obj = obj.parent
else
break
}
if (obj) {
console.log(obj.name)
this.transformControl.setMode("translate")
this.transformControl.setSpace("world")
this.transformControl.attach(obj)
}
}
else if (pickableObjectNames.includes(name)) {
while (obj) {
if (obj?.parent?.name == name)
obj = obj.parent
else
break
}
if (obj) {
this.transformControl.setMode("rotate")
this.transformControl.setSpace("local")
this.transformControl.attach(obj)
}
}
}
}
Capture() {
this.transformControl.detach()
this.axesHelper.visible = false
this.gridHelper.visible = false
this.renderer.setClearColor(0x000000)
this.scene.children
.filter(o => o?.name === "torso")
.forEach((o) => {
o.traverse((child) => {
if (child?.name === "038F_05SET_04SHOT") {
child.visible = false
}
})
})
this.renderer.render(this.scene, this.camera);//renderer为three.js里的渲染器scene为场景 camera为相机
let imgData = this.renderer.domElement.toDataURL("image/png");//这里可以选择png格式jpeg格式
const fileName = "pose_" + getCurrentTime()
this.axesHelper.visible = true
this.gridHelper.visible = true
this.renderer.setClearColor(options.clearColor)
this.scene.children
.filter(o => o?.name === "torso")
.forEach((o) => {
o.traverse((child) => {
if (child?.name === "038F_05SET_04SHOT") {
child.visible = true
}
})
})
return {
imgData, fileName
}
}
CaptureDepth() {
this.transformControl.detach()
this.axesHelper.visible = false
this.gridHelper.visible = false
this.renderer.setClearColor(0x000000)
const map = new Map<Object3D, Object3D | null>()
this.scene.children
.filter(o => o?.name === "torso")
.forEach((o) => {
o.traverse((child) => {
if (child?.name === "038F_05SET_04SHOT") {
map.set(child, child.parent)
this.scene.attach(child)
}
})
o.visible = false
})
this.renderer.render(this.scene, this.camera);//renderer为three.js里的渲染器scene为场景 camera为相机
let imgData = this.renderer.domElement.toDataURL("image/png");//这里可以选择png格式jpeg格式
const fileName = "pose_" + getCurrentTime()
this.axesHelper.visible = true
this.gridHelper.visible = true
this.renderer.setClearColor(options.clearColor)
for (const [k, v] of map.entries()) {
v?.attach(k)
}
this.scene.children
.filter(o => o?.name === "torso")
.forEach((o) => {
o.visible = true
})
return {
imgData, fileName
}
}
MakeImages() {
{
const { imgData, fileName } = this.Capture()
SetScreenShot("pose", imgData, fileName)
}
{
const { imgData, fileName } = this.CaptureDepth()
SetScreenShot("depth", imgData, fileName)
}
}
CopyBody() {
if (!this.templateBody)
return
const body = this.templateBody.clone()
const list = this.scene.children
.filter(o => o?.name === "torso")
.filter(o => o.position.x === 0)
.map(o => Math.ceil(o.position.z / 30))
body.translateZ((Math.min(...list) - 1) * 30)
this.scene.add(body)
}
get Width() {
return this.renderer.domElement.clientWidth
}
get Height() {
return this.renderer.domElement.clientHeight
}
render() {
requestAnimationFrame(this.render.bind(this));
this.renderer.render(this.scene, this.camera);
this.stats.update()
}
handleResize() {
const canvas = this.renderer.domElement
this.camera.aspect = canvas.clientWidth / canvas.clientHeight
this.camera.updateProjectionMatrix()
console.log(canvas.clientWidth, canvas.clientHeight)
this.renderer.setSize(canvas.clientWidth, canvas.clientHeight, false)
}
async loadBodyData() {
const object = await LoadObjFile('/models/hand.obj')
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.material = new MeshDepthMaterial();
}
});
this.templateBody = CreateBody(object)
// scene.add( object );
const body = this.templateBody.clone()
this.scene.add(body);
this.dlight.target = body;
}
get CameraNear() {
return this.camera.near
}
set CameraNear(value: number) {
this.camera.near = value
this.camera.updateProjectionMatrix();
}
get CameraFar() {
return this.camera.far
}
set CameraFar(value: number) {
this.camera.far = value
this.camera.updateProjectionMatrix();
}
}

17
src/image.ts Normal file
View File

@ -0,0 +1,17 @@
import { download } from "./util"
document.querySelectorAll(".gallery img").forEach(img => img.addEventListener("click", () => {
const image = document.querySelector("img")
const title = image?.getAttribute("title") ?? ""
const url = image?.getAttribute("src") ?? ""
download(url, title)
}))
export function SetScreenShot(id: string, url: string, name: string) {
const img = document.querySelector<HTMLImageElement>(`.gallery #${id}`)
if (img) {
img.src = url
img.title = name
}
}

30
src/index.css Normal file
View File

@ -0,0 +1,30 @@
* {
margin: 0;
padding: 0;
}
body {
width: 100vw;
height: 100vh;
}
canvas {
display: block;
width: inherit;
height: inherit;
}
.gallery {
height: 140px;
position: fixed;
bottom: 50px;
right: 50px;
}
.gallery img {
width: 100px;
height: 140px;
object-fit: contain;
background-color: gray;
}

54
src/loader.ts Normal file
View File

@ -0,0 +1,54 @@
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
import * as THREE from "three"
// const fbxLoader = new FBXLoader()
// fbxLoader.load(
// '/models/rp_mei_posed_001_100k.fbx',
// (object) => {
// // object.traverse(function (child) {
// // if ((child as THREE.Mesh).isMesh) {
// // // (child as THREE.Mesh).material = material
// // if ((child as THREE.Mesh).material) {
// // ((child as THREE.Mesh).material as THREE.MeshBasicMaterial).transparent = false
// // }
// // }
// // })
// // object.scale.set(.01, .01, .01)
// scene.add(object)
// },
// (xhr) => {
// console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
// },
// (error) => {
// console.log(error)
// }
// )
// instantiate a loader
const loader = new OBJLoader();
export async function LoadObjFile(url: string): Promise<THREE.Group> {
return new Promise((resolve, reject) => {
// load a resource
loader.load(
// resource URL
url,
// called when resource is loaded
function (object) {
resolve(object)
},
// called when loading is in progresses
function (xhr) {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
},
// called when loading has errors
function (error) {
console.log('An error happened');
reject(error)
}
);
})
}

58
src/main.tsx Normal file
View File

@ -0,0 +1,58 @@
import * as THREE from "three"
import * as dat from 'dat.gui';
import "./index.css"
import { options } from "./config";
import { BodyEditor } from "./editor";
const editor = new BodyEditor(document.querySelector<HTMLCanvasElement>("#canvas")!)
const gui = new dat.GUI();
window.addEventListener('keydown', function (event) {
switch (event.code) {
case 'KeyX':
options.moveMode = true
gui.updateDisplay()
break
}
})
window.addEventListener('keyup', function (event) {
switch (event.code) {
case 'KeyX':
options.moveMode = false
gui.updateDisplay()
break
}
})
options["width"] = editor.Width
options["height"] = editor.Height
gui.add(editor, 'Width').name("宽度");
gui.add(editor, 'Height').name("高度");
function UpdateSize() {
options["width"] = editor.Width
options["height"] = editor.Height
gui.updateDisplay()
}
UpdateSize()
gui.add(editor, 'MakeImages').name("骨架图/深度图");
gui.add(editor, 'CopyBody').name("复制骨架");
gui.add(options, 'moveMode').name("移动模式 (按X切换)");
gui.add(editor, "CameraNear", 0.1, 1000).name("相机near")
gui.add(editor, "CameraFar", 0.1, 1000).name("相机far")
window.addEventListener('resize', () => {
UpdateSize()
})
editor.loadBodyData()

15
src/util.ts Normal file
View File

@ -0,0 +1,15 @@
export function download(href: string, title: string) {
const a = document.createElement('a');
a.setAttribute('href', href);
a.setAttribute('download', title);
a.click();
}
export function getCurrentTime() {
let today = new Date();
let date = today.getFullYear() + '_' + (today.getMonth() + 1) + '_' + today.getDate();
let time = today.getHours() + "_" + today.getMinutes() + "_" + today.getSeconds();
let dateTime = date + '_' + time;
return dateTime;
}

1
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

21
tsconfig.json Normal file
View File

@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

9
tsconfig.node.json Normal file
View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

10
vite.config.ts Normal file
View File

@ -0,0 +1,10 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
define: {
global: {},
},
plugins: [react()],
})