From 5f959b129d602ca2dcab32dd92270b83449f58aa Mon Sep 17 00:00:00 2001 From: crosstyan Date: Fri, 11 Jul 2025 15:11:09 +0800 Subject: [PATCH] x --- protocol.py | 16 ++ src/App.tsx | 272 +++++++++++++++++---- src/Boxing.tsx | 634 ++++++++++++++++++++++++++++++++++++++++++++++++ src/Yeu_305.tsx | 538 ++++++++++++++++++++++++++++++++++++++++ src/main.tsx | 6 +- src/test.cpp | 23 ++ test | Bin 0 -> 238152 bytes test.py | 19 ++ 8 files changed, 1464 insertions(+), 44 deletions(-) create mode 100644 protocol.py create mode 100644 src/Boxing.tsx create mode 100644 src/Yeu_305.tsx create mode 100644 src/test.cpp create mode 100755 test create mode 100644 test.py diff --git a/protocol.py b/protocol.py new file mode 100644 index 0000000..5c9740e --- /dev/null +++ b/protocol.py @@ -0,0 +1,16 @@ +from typing import Protocol, TypeVar, Generic + +T = TypeVar("T") + + +class Adder(Generic[T], Protocol): + def add(self, a: T, b: T) -> T: ... + + +class AdderImpl(Adder[int]): + acc: int + + def add(self, a: int, b: int): + self.acc = a + b + return self.acc + diff --git a/src/App.tsx b/src/App.tsx index be7b974..c887e16 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,14 +1,17 @@ import { Grid, useBVH, useGLTF, CameraControls, AccumulativeShadows, OrbitControls, Stats } from '@react-three/drei' import { Camera, Canvas, useFrame, useThree, useLoader, RenderCallback, RootState } from '@react-three/fiber' +import { Text as DreiText, TextProps as DreiTextProps } from '@react-three/drei'; import * as THREE from 'three' import { FontLoader } from 'three/addons/loaders/FontLoader.js' import { TextGeometry } from 'three/addons/geometries/TextGeometry.js' import HelvetikerRegular from "three/examples/fonts/helvetiker_regular.typeface.json" -import { useEffect, useRef, useState, JSX } from 'react' +import { useEffect, useRef, useState, JSX, forwardRef } from 'react' // import POSE_3D_ from "./assets/result_ae_01_ae_08.json" -import POSE_3D_ from "./assets/temp_result.json" -import POSE_3D_MANY_ from "./assets/many_people_all_3d_pose.json" -import POSE_3D_04_02_ from "./assets/res.json" +// import POSE_3D_ from "./assets/temp_result.json" +// import POSE_3D_MANY_ from "./assets/many_people_all_3d_pose.json" +import POSE_3D_04_02_ from "/home/admin/Code/CVTH3PE/samples/Test_QuanCheng.json" + +import optical3dPointsRaw from "/home/admin/Code/CVTH3PE/samples/optical_3d_points.json" // 133, 3 type PosePoints3D = [number, number, number][] @@ -17,13 +20,33 @@ type PosePoints3D = [number, number, number][] type AnimePosePoints3D = PosePoints3D[] interface Skeleton0402 { - "a": PosePoints3D - "b": PosePoints3D + "1": PosePoints3D[], + "4": PosePoints3D[], + // "b": PosePoints3D } -const POSE_3D = POSE_3D_ as AnimePosePoints3D -const POSE_3D_MANY = POSE_3D_MANY_ as AnimePosePoints3D[] // N F 133 3 +interface CameraDescriptor { + transformation_matrx: number[] // 16 (4x4) + camera_matrix: number[] // 9 (3x3) +} + +// 扩展 Drei 的 TextProps 接口 +interface TextProps extends DreiTextProps { + position?: [number, number, number]; + fontSize?: number; + color?: string; + billboard?: boolean; +} + +// 创建类型安全的 Text 组件 +const Text = forwardRef((props, ref) => { + return ; +}); + +// const POSE_3D = POSE_3D_ as AnimePosePoints3D +// const POSE_3D_MANY = POSE_3D_MANY_ as AnimePosePoints3D[] // N F 133 3 const POSE_3D_04_02 = POSE_3D_04_02_ as Skeleton0402 + // const POSE_3D_04_02 = POSE_3D_04_02_ as AnimePosePoints3D const THREE_ADDONS = { FontLoader, @@ -111,41 +134,92 @@ const DEFAULT_TRANSFORMATION_MATRIX = [ const DEFAULT_NEAR = 0.05 const DEFAULT_FAR = 1 const CAMERA_EXTRINSIC_MATRIX_MAP: Record = { - "AE_01": [ - 0.37408302, -0.91907411, 0.12395429, 1.18976111, 0.17243349, - -0.06239751, -0.98304285, -0.06429779, 0.91122367, 0.38911351, - 0.13513731, 2.51940833, 0., 0., 0., - 1. + + "5601": [ + 0.6356, 0.0095, 0.772, -0.4025, -0.0361, -0.9985, 0.042, + 0.0892, 0.7712, -0.0546, -0.6343, 5.0822, 0., 0., + 0., 1. ] as const, - "AE_1A": [ - 0.92998171, -0.36696694, -0.02166301, 2.21643671, -0.05110403, - -0.07070226, -0.99618752, -0.72948697, 0.36403626, 0.92754324, - -0.0845053, 6.45800206, 0., 0., 0., - 1. + "5602": [ + 0.0638, 0.0474, -0.9968, -0.4955, 0.1185, -0.9922, -0.0396, + 0.1454, -0.9909, -0.1156, -0.0689, 5.0618, 0., 0., + 0., 1. ] as const, - "AE_08": [ - 0.98195914, -0.18888337, -0.00890642, 1.43011854, -0.02247979, - -0.06984105, -0.99730481, -0.61678831, 0.18775226, 0.97951279, - -0.07282712, 5.81983825, 0., 0., 0., - 1. - ] as const + "5603": [ + -0.5541, -0.0196, 0.8322, 0.9785, -0.0576, -0.9964, -0.0619, + -0.0225, 0.8304, -0.0823, 0.551, 6.8712, 0., 0., + 0., 1. + ] as const, + "5604": [ + 0.0448, -0.0137, 0.9989, 0.165, -0.0717, -0.9974, -0.0105, + 0.2167, 0.9964, -0.0712, -0.0457, 5.1217, 0., 0., + 0., 1. + ] as const, + "5605": [ + -0.6795, 0.0297, -0.733, -0.7666, 0.0113, -0.9986, -0.051, + 0.4234, -0.7335, -0.043, 0.6783, 6.1889, 0., 0., + 0., 1. + ] as const, + "5606": [ + 0.6502, 0.0654, -0.757, -0.2844, 0.216, -0.9711, 0.1016, + -0.8482, -0.7285, -0.2296, -0.6455, 5.2607, 0., 0., + 0., 1. + ] as const, + "5607": [ + 0.8406, 0.0516, -0.5393, -0.3939, -0.0916, -0.9676, -0.2354, + 0.2677, -0.5339, 0.2473, -0.8086, 2.2953, 0., 0., + 0., 1. + ] as const, + "5608": [ + 0.93, 0.0182, 0.3672, 0.6156, 0.0133, -0.9998, 0.0159, + 0.1594, 0.3674, -0.0099, -0.93, 7.6299, 0., 0., + 0., 1. + ] as const, + "5609": [ + 0.8488, 0.0485, -0.5265, -0.1715, 0.0611, -0.9981, 0.0065, + -0.0113, -0.5252, -0.0377, -0.8502, 6.8236, 0., 0., + 0., 1. + ] as const, + } const CAMERA_INTRINSIC_MATRIX_MAP: Record = { - "AE_01": [ - 1806.82137617, 0., 1230.53175624, 0., - 1809.75580378, 766.36204406, 0., 0., - 1. + "5601": [ + 2686.004, 0., 1470.4911, 0., 2699.927, 765.5127, + 0., 0., 1. ] as const, - "AE_1A": [ - 3467.39715751, 0., 1000.62548655, 0., - 3473.7168112, 831.64048503, 0., 0., - 1. + "5602": [ + 2686.004, 0., 1470.4911, 0., 2699.927, 765.5127, + 0., 0., 1. + ] as const, + "5603": [ + 2791.3838, 0., 1258.1116, 0., 2790.6707, 788.1486, + 0., 0., 1. + ] as const, + "5604": [ + 2789.2568, 0., 1231.131, 0., 2787.0845, 677.6938, + 0., 0., 1. + ] as const, + "5605": [ + 2644.6814, 0., 1285.3489, 0., 2644.9702, 627.2081, + 0., 0., 1. + ] as const, + "5606": [ + 1919.8364, 0., 1201.1659, 0., 1908.0964, 982.5976, + 0., 0., 1. + ] as const, + "5607": [ + 1806.8214, 0., 1230.5317, 0., 1809.7559, 766.3621, + 0., 0., 1. + ] as const, + "5608": [ + + 3467.3972, 0., 1000.6255, 0., 3473.7168, 831.6405, + 0., 0., 1. + ] as const, + "5609": [ + 2785.4392, 0., 1254.9827, 0., 2788.1045, 738.8298, + 0., 0., 1. ] as const, - "AE_08": [ - 2785.43931794, 0., 1254.98272372, 0., - 2788.10437965, 738.82985324, 0., 0., - 1. - ] as const } const IMAGE_WIDTH = 2560 @@ -163,7 +237,112 @@ const intrinsicToFov = (intrinsic: number[], image_size: { width: number, height return { fov_x, fov_y } } +const calculaterCubeVersices = (position:number[], dimensions: number[])=>{ + const [cx, cy,cz] = position + const [width, height, depth] = dimensions + const halfWidth = width / 2 + const halfHeight = height / 2 + const halfDepth = depth / 2 + + return [ + [cx - halfWidth, cy - halfHeight, cz - halfDepth], + [cx + halfWidth, cy - halfHeight, cz - halfDepth], + [cx + halfWidth, cy + halfHeight, cz - halfDepth], + [cx - halfWidth, cy + halfHeight, cz - halfDepth], + [cx - halfWidth, cy - halfHeight, cz + halfDepth], + [cx + halfWidth, cy - halfHeight, cz + halfDepth], + [cx + halfWidth, cy + halfHeight, cz + halfDepth], + [cx - halfWidth, cy + halfHeight, cz + halfDepth] + ] +} + + const Scene = () => { + + // 处理 optical_3d_points.json 数据,转换为点数组 + const optical3dPoints: [number, number, number][] = (optical3dPointsRaw as any[]).map( + (item) => Array.isArray(item) && Array.isArray(item[0]) ? item[0] as [number, number, number] : item as [number, number, number] + ) + + // 绘制静态3D曲线组件 + const Optical3DLine = () => { + if (!optical3dPoints || optical3dPoints.length < 2) return null; + // 转为three.js Vector3数组 + const points = optical3dPoints.map(([x, y, z]) => new THREE.Vector3(x, y, z)); + const curve = new THREE.CatmullRomCurve3(points); + const curvePoints = curve.getPoints(optical3dPoints.length * 5); // 插值更平滑 + // 生成 position 属性的 Float32Array + const positions = new Float32Array(curvePoints.length * 3); + curvePoints.forEach((v, i) => { + positions[i * 3] = v.x; + positions[i * 3 + 1] = v.y; + positions[i * 3 + 2] = v.z; + }); + return ( + <> + {/* 曲线 */} + + + + + + + {/* 端点小球 */} + {points.map((p, i) => ( + + + + + ))} + + ); + }; + + // 定义立方体绘制组件 + const Cube = () =>{ + + const vertices = calculaterCubeVersices([0.205+0.2 ,0.205+0.50,-0.205-0.45],[0.65,1.8,1]) + + return ( + <> + {/** 原点位置,相对于六面体中心偏移 */} + {/** 边长,宽:1.5米,高:1.5米,深度:1米 */} + + + + {/* 顶点标记(带坐标系和文本) */} + {vertices.map(([x, y, z], index) => ( + + {/* 顶点处的小标记点 */} + + + + + + {/* 坐标轴(X:红, Y:绿, Z:蓝) */} + + + {/* 坐标文本(始终面向相机) */} + + {`P${index}: (${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)})`} + + + ))} + + + ) + } + function Floor() { return ( @@ -253,7 +432,7 @@ const Scene = () => { const cameraCvt = CV_TO_GL_MAT.clone() // Convert from Z-up to Y-up first (this affects world coordinates) - const worldCvt = Z_UP_TO_Y_UP.clone() + // const worldCvt = Z_UP_TO_Y_UP.clone() // Final transformation: // 1. Convert world from Z-up to Y-up @@ -263,7 +442,7 @@ const Scene = () => { final .multiply(cameraCvt) .multiply(Rt) - .multiply(worldCvt) + // .multiply(worldCvt) // Invert to get the camera-to-world transform final.invert() @@ -329,10 +508,14 @@ const Scene = () => { // Transform a joint position using the coordinate system conversion const transformJointPosition = (j: [number, number, number]) => { + // const [x, y, z] = j + // const V = new THREE.Vector3(x, y, z) + // const worldCvt = Z_UP_TO_Y_UP_PRIME.clone() + // V.applyMatrix4(worldCvt) + // return V + const [x, y, z] = j const V = new THREE.Vector3(x, y, z) - const worldCvt = Z_UP_TO_Y_UP_PRIME.clone() - V.applyMatrix4(worldCvt) return V } @@ -423,8 +606,8 @@ const Scene = () => { // , // ] const skeletons = [ - , - , + , + // , ] const cameras = Object.entries(CAMERA_EXTRINSIC_MATRIX_MAP).map(([key, value]) => { @@ -435,6 +618,7 @@ const Scene = () => { return }) + // 在场景中添加立方体 const scene = ( {/* */} @@ -442,6 +626,8 @@ const Scene = () => { {/* */} { } + {/** 新增立方体 */} + {/** 新增静态3D曲线 */} {cameras} {skeletons} ) diff --git a/src/Boxing.tsx b/src/Boxing.tsx new file mode 100644 index 0000000..2fd0c75 --- /dev/null +++ b/src/Boxing.tsx @@ -0,0 +1,634 @@ +import { Grid, useBVH, useGLTF, CameraControls, AccumulativeShadows, OrbitControls, Stats } from '@react-three/drei' +import { Camera, Canvas, useFrame, useThree, useLoader, RenderCallback, RootState } from '@react-three/fiber' +import { Text as DreiText, TextProps as DreiTextProps } from '@react-three/drei'; +import * as THREE from 'three' +import { FontLoader } from 'three/addons/loaders/FontLoader.js' +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js' +import HelvetikerRegular from "three/examples/fonts/helvetiker_regular.typeface.json" +import { useEffect, useRef, useState, JSX, forwardRef } from 'react' +import Boxing from "./assets/Test_WeiHua_Segment_1.json" + +// 133, 3 +type PosePoints3D = [number, number, number][] + +// F, 133, 3 +type AnimePosePoints3D = PosePoints3D[] + +interface Skeleton { + "1": PosePoints3D[], + "2": PosePoints3D[], + "3": PosePoints3D[], +} + +// 扩展 Drei 的 TextProps 接口 +interface TextProps extends DreiTextProps { + position?: [number, number, number]; + fontSize?: number; + color?: string; + billboard?: boolean; +} + +// 创建类型安全的 Text 组件 +const Text = forwardRef((props, ref) => { + return ; +}); + +// const POSE_3D = POSE_3D_ as AnimePosePoints3D +// const POSE_3D_MANY = POSE_3D_MANY_ as AnimePosePoints3D[] // N F 133 3 +const BOXING = Boxing as Skeleton + +const THREE_ADDONS = { + FontLoader, + TextGeometry, +} as const + +// Create OpenCV to OpenGL conversion matrix +// OpenCV: X right, Y down, Z forward +// OpenGL: X right, Y up, Z backward +const CV_TO_GL_MAT = new THREE.Matrix4().set( + 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, -1, 0, + 0, 0, 0, 1 +) + +// Z-up to Y-up conversion matrix +// Rotate -90 degrees around X axis to convert from Z-up to Y-up +const Z_UP_TO_Y_UP = new THREE.Matrix4().set( + -1, 0, 0, 0, + 0, 0, -1, 0, + 0, -1, 0, 0, + 0, 0, 0, 1 +) + +const Z_UP_TO_Y_UP_PRIME = new THREE.Matrix4().set( + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 0, + 0, 0, 0, 1 +) + +// Color definitions for different body parts +const COLOR_SPINE = new THREE.Color(138 / 255, 201 / 255, 38 / 255) // green, spine & head +const COLOR_ARMS = new THREE.Color(255 / 255, 202 / 255, 58 / 255) // yellow, arms & shoulders +const COLOR_LEGS = new THREE.Color(25 / 255, 130 / 255, 196 / 255) // blue, legs & hips +const COLOR_FINGERS = new THREE.Color(255 / 255, 0, 0) // red, fingers +const COLOR_FACE = new THREE.Color(255 / 255, 200 / 255, 0) // yellow, face +const COLOR_FOOT = new THREE.Color(255 / 255, 128 / 255, 0) // orange, foot +const COLOR_HEAD = new THREE.Color(255 / 255, 0, 255 / 255) // purple, head + +// Body bone connections +const BODY_BONES = [ + // legs + [15, 13], [13, 11], [16, 14], [14, 12], [11, 12], // legs + [5, 11], [6, 12], [5, 6], // torso + [5, 7], [7, 9], [6, 8], [8, 10], // arms + [1, 2], [0, 1], [0, 2], [1, 3], [2, 4], // head + [15, 17], [15, 18], [15, 19], // left foot + [16, 20], [16, 21], [16, 22], // right foot +] as const + +// Body bone colors +const BODY_BONE_COLORS = [ + COLOR_LEGS, COLOR_LEGS, COLOR_LEGS, COLOR_LEGS, COLOR_LEGS, + COLOR_SPINE, COLOR_SPINE, COLOR_SPINE, + COLOR_ARMS, COLOR_ARMS, COLOR_ARMS, COLOR_ARMS, + COLOR_HEAD, COLOR_HEAD, COLOR_HEAD, COLOR_HEAD, COLOR_HEAD, + COLOR_FOOT, COLOR_FOOT, COLOR_FOOT, + COLOR_FOOT, COLOR_FOOT, COLOR_FOOT, +] as const + +// Hand bone connections (in pairs of [start, end] indices) +const HAND_BONES = [ + // right hand + [91, 92], [92, 93], [93, 94], [94, 95], // right thumb + [91, 96], [96, 97], [97, 98], [98, 99], // right index + [91, 100], [100, 101], [101, 102], [102, 103], // right middle + [91, 104], [104, 105], [105, 106], [106, 107], // right ring + [91, 108], [108, 109], [109, 110], [110, 111], // right pinky + // left hand + [112, 113], [113, 114], [114, 115], [115, 116], // left thumb + [112, 117], [117, 118], [118, 119], [119, 120], // left index + [112, 121], [121, 122], [122, 123], [123, 124], // left middle + [112, 125], [125, 126], [126, 127], [127, 128], // left ring + [112, 129], [129, 130], [130, 131], [131, 132] // left pinky +] as const + +const DEFAULT_TRANSFORMATION_MATRIX = [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, +] as const +const DEFAULT_NEAR = 0.05 +const DEFAULT_FAR = 1 +const CAMERA_EXTRINSIC_MATRIX_MAP: Record = { + "5602": [ + -0.76138617, 0.16195241, 0.62774399, -0.73832425, -0.40758532, + -0.87257285, -0.26924121, 0.55561231, 0.5041481, -0.46085577, + 0.73037432, 3.28663985, 0., 0., 0., + 1. + ] as const, + "5603": [ + -0.44966325, -0.02806161, -0.89275725, -0.24260155, 0.21789368, + -0.97275592, -0.07917234, 0.41197614, -0.8662132, -0.23012705, + 0.44352704, 4.37769458, 0., 0., 0., + 1. + ] as const, + "5604": [ + 0.9551736, -0.02735669, 0.29477958, 0.5908996, -0.06148677, + -0.99234061, 0.10714238, 0.87629594, 0.28959069, -0.12046462, + -0.94953963, 4.1617598, 0., 0., 0., + 1. + ] as const, + "5605": [ + 0.57140284, 0.03118154, -0.82007713, -0.22679438, 0.11021058, + -0.99314166, 0.0390292, 0.83904835, -0.81323577, -0.11268257, + -0.5709205, 4.17730229, 0., 0., 0., + 1. + ] as const, +} +const CAMERA_INTRINSIC_MATRIX_MAP: Record = { + "5602": [ + 2686.00393399, 0., 1470.49106388, 0., + 2699.92688641, 765.51266883, 0., 0., + 1. + ] as const, + "5603": [ + 2791.38378418, 0., 1258.11161208, 0., + 2790.67070205, 788.14860021, 0., 0., + 1. + ] as const, + "5604": [ + 2789.25680621, 0., 1231.13101573, 0., + 2787.08458106, 677.69385417, 0., 0., + 1. + ] as const, + "5605": [ + 2644.68145454, 0., 1285.34889127, 0., + 2644.9700955, 627.20808584, 0., 0., + 1. + ] as const, +} + +const IMAGE_WIDTH = 2560 +const IMAGE_HEIGHT = 1440 + +const intrinsicToFov = (intrinsic: number[], image_size: { width: number, height: number }) => { + console.assert(intrinsic.length === 9, "intrinsic must be a 3x3 matrix") + const fx = intrinsic[0] + const fy = intrinsic[4] + const cx = intrinsic[2] + const cy = intrinsic[5] + // in degrees + const fov_x = 2 * Math.atan(image_size.width / (2 * fx)) * (180 / Math.PI) + const fov_y = 2 * Math.atan(image_size.height / (2 * fy)) * (180 / Math.PI) + return { fov_x, fov_y } +} + +const calculaterCubeVersices = (position: number[], dimensions: number[]) => { + const [cx, cy, cz] = position + const [width, height, depth] = dimensions + const halfWidth = width / 2 + const halfHeight = height / 2 + const halfDepth = depth / 2 + + return [ + [cx - halfWidth, cy - halfHeight, cz - halfDepth], + [cx + halfWidth, cy - halfHeight, cz - halfDepth], + [cx + halfWidth, cy + halfHeight, cz - halfDepth], + [cx - halfWidth, cy + halfHeight, cz - halfDepth], + [cx - halfWidth, cy - halfHeight, cz + halfDepth], + [cx + halfWidth, cy - halfHeight, cz + halfDepth], + [cx + halfWidth, cy + halfHeight, cz + halfDepth], + [cx - halfWidth, cy + halfHeight, cz + halfDepth] + ] +} + + +const Scene = () => { + + // 定义立方体绘制组件 + const Cube = () => { + + const vertices = calculaterCubeVersices([0, -0.205 + 0.9, 0.90], [0.5, 1.8, 0.5]) + + return ( + <> + {/** 原点位置,相对于六面体中心偏移 */} + {/** 边长,宽:0.5米,高:1.8米,深度:0.5米 */} + + + + {/* 顶点标记(带坐标系和文本) */} + {vertices.map(([x, y, z], index) => ( + + {/* 顶点处的小标记点 */} + + + + + + {/* 坐标轴(X:红, Y:绿, Z:蓝) */} + + + {/* 沙袋上的坐标文本(始终面向相机) */} + {/* + {`P${index}: (${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)})`} + */} + + ))} + + + ) + } + + function Floor() { + return ( + + + + + ) + } + const Axes = () => { + return + } + interface CameraViewFromExtrinsicProps { + extrinsic: number[] | THREE.Matrix4 + aspect?: number + name?: string + near?: number + far?: number + fov?: number + textSize?: number + } + // https://threejs.org/docs/#examples/en/loaders/FontLoader + // https://www.ilyameerovich.com/simple-3d-text-meshes-in-three-js/ + + const CameraViewFromExtrinsic = ({ extrinsic, name, near, far, fov, textSize, aspect }: CameraViewFromExtrinsicProps) => { + let Rt: THREE.Matrix4 + if (extrinsic instanceof THREE.Matrix4) { + Rt = extrinsic + } else if (Array.isArray(extrinsic)) { + console.assert(extrinsic.length === 16, "extrinsic must be a 4x4 matrix") + Rt = new THREE.Matrix4() + // @ts-expect-error 16 elements + Rt.set(...extrinsic) + } else { + throw new Error("extrinsic must be a 4x4 matrix or an array of 16 elements") + } + const font = new FontLoader().parse(HelvetikerRegular) + const camera = new THREE.PerspectiveCamera(fov ?? 60, aspect ?? 4 / 3, near ?? DEFAULT_NEAR, far ?? DEFAULT_FAR) + const helper = + camera.applyMatrix4(Rt) + + const textRef = useRef(null) + const { camera: viewCamera } = useThree() + + useFrame(() => { + if (textRef.current) { + textRef.current.lookAt(viewCamera.position) + } + }) + + let text: JSX.Element | null = null + if (name) { + const geo = new THREE_ADDONS.TextGeometry(name ?? "", { font, size: textSize ?? 0.1, depth: 0.001 }) + const position = new THREE.Vector3() + position.setFromMatrixPosition(Rt) + + text = ( + + + + + ) + } + return ( + + {text} + + {helper} + + ) + } + + const preProcessExtrinsic = (extrinsic: number[] | THREE.Matrix4) => { + let Rt: THREE.Matrix4 + if (extrinsic instanceof THREE.Matrix4) { + Rt = extrinsic + } else if (Array.isArray(extrinsic)) { + console.assert(extrinsic.length === 16, "extrinsic must be a 4x4 matrix") + Rt = new THREE.Matrix4() + // @ts-expect-error 16 elements + Rt.set(...extrinsic) + } else { + throw new Error("extrinsic must be a 4x4 matrix or an array of 16 elements") + } + + + // Then handle OpenCV to OpenGL camera convention + const cameraCvt = CV_TO_GL_MAT.clone() + + // Convert from Z-up to Y-up first (this affects world coordinates) + // const worldCvt = Z_UP_TO_Y_UP.clone() + + // Final transformation: + // 1. Convert world from Z-up to Y-up + // 2. Apply the camera transform + // 3. Convert camera coordinates from OpenCV to OpenGL + const final = new THREE.Matrix4() + final + .multiply(cameraCvt) + .multiply(Rt) + // .multiply(worldCvt) + + // Invert to get the camera-to-world transform + final.invert() + return final + } + + interface Human3DSkeletonProps { + skeleton: AnimePosePoints3D + startFrame?: number + jointRadius?: number + boneRadius?: number + showJoints?: boolean + showBones?: boolean + frameRate?: number + } + + /** + * 3D人体骨架动画组件 + * @param skeleton 动画帧序列,每帧为133个关节点的三维坐标 + * @param startFrame 起始帧 + * @param jointRadius 关节点球体半径 + * @param boneRadius 骨骼圆柱体半径 + * @param showJoints 是否显示关节点 + * @param showBones 是否显示骨骼 + * @param frameRate 动画帧率 + */ + const Human3DSkeleton = ({ + skeleton, + startFrame = 0, + jointRadius = 0.01, + boneRadius = 0.005, + showJoints = true, + showBones = true, + frameRate = 24 + }: Human3DSkeletonProps) => { + // 当前动画帧索引 + const [frameIndex, setFrameIndex] = useState(startFrame) + const totalFrames = skeleton.length + // 动画帧推进函数,按frameRate自动推进 + const onFrame: RenderCallback = (totalFrames === 0) ? (state, delta) => { } : (state: RootState, delta: number) => { + // 根据帧率和delta推进帧索引,实现动画 + setFrameIndex(prevFrame => { + const nextFrame = prevFrame + frameRate * delta + // 到末尾自动循环 + return nextFrame >= totalFrames ? 0 : nextFrame + }) + return null + } + + // 注册动画帧推进 + useFrame(onFrame) + + // 当前帧的133个关节点坐标 + const currentFrame = Math.floor(frameIndex) % totalFrames + const joints = skeleton[currentFrame] + + /** + * 右腕(关键点10)速度计算 + * 速度 = (当前帧位置 - 上一帧位置) / (1 / frameRate) + * 单位:与坐标单位一致/秒 + */ + let wristSpeed: number | null = null; + if (currentFrame > 0 && joints && joints.length > 16) { + const prevJoints = skeleton[(currentFrame - 1 + totalFrames) % totalFrames]; + if (prevJoints && prevJoints.length > 16) { + const wNow = joints[10]; + const wPrev = prevJoints[10]; + if (wNow && wPrev) { + const dx = wNow[0] - wPrev[0]; + const dy = wNow[1] - wPrev[1]; + const dz = wNow[2] - wPrev[2]; + const dist = Math.sqrt(dx * dx + dy * dy + dz * dz); + wristSpeed = dist * frameRate; // 单位/秒 + } + } + } + + /** + * 右手大臂(右肩-右肘)和小臂(右肘-右腕)夹角计算 + * COCO/BlazePose: 右肩=6, 右肘=8, 右腕=10 + * 夹角为两向量的夹角,单位度 + */ + let rightUpperArmAngle: number | null = null; + if (joints && joints.length > 16) { + const shoulder = joints[6]; // 右肩 + const elbow = joints[8]; // 右肘 + const wrist = joints[10]; // 右腕 + if (shoulder && elbow && wrist) { + // 大臂向量(肩->肘) + const v1 = new THREE.Vector3( + shoulder[0] - elbow[0], + shoulder[1] - elbow[1], + shoulder[2] - elbow[2] + ); + // 小臂向量(腕->肘) + const v2 = new THREE.Vector3( + wrist[0] - elbow[0], + wrist[1] - elbow[1], + wrist[2] - elbow[2] + ); + // 计算夹角 + const dot = v1.dot(v2); + const len1 = v1.length(); + const len2 = v2.length(); + if (len1 > 1e-6 && len2 > 1e-6) { + const angle = Math.acos(Math.max(-1, Math.min(1, dot / (len1 * len2)))); + rightUpperArmAngle = angle * 180 / Math.PI; + } + } + } + + /** + * 获取关节点颜色 + * @param idx 关节点索引 + */ + const getJointColor = (idx: number) => { + // 脸部 + if (idx >= 23 && idx <= 90) return COLOR_FACE + // 手指 + if (idx >= 91 && idx <= 132) return COLOR_FINGERS + // 脚 + if (idx >= 17 && idx <= 22) return COLOR_FOOT + // 头部 + if (idx <= 4) return COLOR_HEAD + // 手臂 + if (idx >= 5 && idx <= 10) return COLOR_ARMS + // 腿 + if (idx >= 11 && idx <= 16) return COLOR_LEGS + // 躯干 + return COLOR_SPINE + } + + /** + * 关节点坐标转换(如需坐标系变换可在此处理) + */ + const transformJointPosition = (j: [number, number, number]) => { + const [x, y, z] = j + const V = new THREE.Vector3(x, y, z) + return V + } + + // 生成关节点球体 + const jointMeshes = showJoints ? joints.map((j, idx) => { + const position = transformJointPosition(j) + const color = getJointColor(idx) + return ( + + + + + ) + }) : null + + // 生成骨骼圆柱体 + const boneMeshes = showBones ? ( + <> + {/* 身体主骨骼 */} + {BODY_BONES.map((bone, idx) => { + const [startIdx, endIdx] = bone + if (startIdx >= joints.length || endIdx >= joints.length) return null + // 起止点 + const startPos = transformJointPosition(joints[startIdx]) + const endPos = transformJointPosition(joints[endIdx]) + const color = BODY_BONE_COLORS[idx] + // 骨骼中点和长度 + const midpoint = new THREE.Vector3().addVectors(startPos, endPos).multiplyScalar(0.5) + const length = startPos.distanceTo(endPos) + // 旋转对齐 + const direction = new THREE.Vector3().subVectors(endPos, startPos).normalize() + const quaternion = new THREE.Quaternion() + const up = new THREE.Vector3(0, 1, 0) + quaternion.setFromUnitVectors(up, direction) + return ( + + + + + ) + })} + {/* 手部骨骼 */} + {HAND_BONES.map((bone, idx) => { + const [startIdx, endIdx] = bone + if (startIdx >= joints.length || endIdx >= joints.length) return null + const startPos = transformJointPosition(joints[startIdx]) + const endPos = transformJointPosition(joints[endIdx]) + const midpoint = new THREE.Vector3().addVectors(startPos, endPos).multiplyScalar(0.5) + const length = startPos.distanceTo(endPos) + const direction = new THREE.Vector3().subVectors(endPos, startPos).normalize() + const quaternion = new THREE.Quaternion() + const up = new THREE.Vector3(0, 1, 0) + quaternion.setFromUnitVectors(up, direction) + return ( + + + + + ) + })} + + ) : null + + // 渲染骨架、关节点、骨骼、右臂夹角和右腕速度文本 + return ( + + {jointMeshes} + {boneMeshes} + {/* 实时显示右手大臂和小臂夹角,文本位于右肘上方 */} + {rightUpperArmAngle !== null && joints[8] && ( + + {`右臂夹角: ${rightUpperArmAngle.toFixed(1)}°`} + + )} + {/* 实时显示右腕速度,文本位于右腕上方 */} + {wristSpeed !== null && joints[10] && ( + + {`右腕速度: ${wristSpeed.toFixed(3)}`} + + )} + + ) + } + + // 调小关节点和骨骼的半径 + const skeletons = [ + , + ] + + const cameras = Object.entries(CAMERA_EXTRINSIC_MATRIX_MAP).map(([key, value]) => { + const intrinsic = CAMERA_INTRINSIC_MATRIX_MAP[key] + const { fov_x, fov_y } = intrinsicToFov(intrinsic, { width: IMAGE_WIDTH, height: IMAGE_HEIGHT }) + // make the far reverse proportional to the fov + const far = (1 / fov_x) * 20 + return + }) + + // 在场景中添加立方体 + const scene = ( + {/* */} + + + {/* */} + { } + + {/** 新增立方体 */} + {cameras} + {skeletons} + ) + return ( + // Note that we don't need to import anything, All three.js objects will be treated + // as native JSX elements, just like you can just write
or in + // regular ReactDOM. The general rule is that Fiber components are available under + // the camel-case version of their name in three.js. + <> + + + {scene} + + ) +} + +function App() { + return ( + + + + ) +} + +export default App diff --git a/src/Yeu_305.tsx b/src/Yeu_305.tsx new file mode 100644 index 0000000..60afb79 --- /dev/null +++ b/src/Yeu_305.tsx @@ -0,0 +1,538 @@ +import { Grid, useBVH, useGLTF, CameraControls, AccumulativeShadows, OrbitControls, Stats } from '@react-three/drei' +import { Camera, Canvas, useFrame, useThree, useLoader, RenderCallback, RootState } from '@react-three/fiber' +import { Text as DreiText, TextProps as DreiTextProps } from '@react-three/drei'; +import * as THREE from 'three' +import { FontLoader } from 'three/addons/loaders/FontLoader.js' +import { TextGeometry } from 'three/addons/geometries/TextGeometry.js' +import HelvetikerRegular from "three/examples/fonts/helvetiker_regular.typeface.json" +import { useEffect, useRef, useState, JSX, forwardRef } from 'react' +import OBJECT from "/home/admin/Code/CVTH3PE/samples/Test_YEU.json" + + +// 133, 3 +type PosePoints3D = [number, number, number][] + +// F, 133, 3 +type AnimePosePoints3D = PosePoints3D[] + +interface Skeleton { + 1: PosePoints3D[], + 2: PosePoints3D[], +} + +// 扩展 Drei 的 TextProps 接口 +interface TextProps extends DreiTextProps { + position?: [number, number, number]; + fontSize?: number; + color?: string; + billboard?: boolean; +} + +// 创建类型安全的 Text 组件 +const Text = forwardRef((props, ref) => { + return ; +}); + +// const POSE_3D = POSE_3D_ as AnimePosePoints3D +// const POSE_3D_MANY = POSE_3D_MANY_ as AnimePosePoints3D[] // N F 133 3 +const yeu_object = OBJECT as Skeleton + +const THREE_ADDONS = { + FontLoader, + TextGeometry, +} as const + +// Create OpenCV to OpenGL conversion matrix +// OpenCV: X right, Y down, Z forward +// OpenGL: X right, Y up, Z backward +const CV_TO_GL_MAT = new THREE.Matrix4().set( + 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, -1, 0, + 0, 0, 0, 1 +) + +// Z-up to Y-up conversion matrix +// Rotate -90 degrees around X axis to convert from Z-up to Y-up +const Z_UP_TO_Y_UP = new THREE.Matrix4().set( + -1, 0, 0, 0, + 0, 0, -1, 0, + 0, -1, 0, 0, + 0, 0, 0, 1 +) + +const Z_UP_TO_Y_UP_PRIME = new THREE.Matrix4().set( + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 0, + 0, 0, 0, 1 +) + +// Color definitions for different body parts +const COLOR_SPINE = new THREE.Color(138 / 255, 201 / 255, 38 / 255) // green, spine & head +const COLOR_ARMS = new THREE.Color(255 / 255, 202 / 255, 58 / 255) // yellow, arms & shoulders +const COLOR_LEGS = new THREE.Color(25 / 255, 130 / 255, 196 / 255) // blue, legs & hips +const COLOR_FINGERS = new THREE.Color(255 / 255, 0, 0) // red, fingers +const COLOR_FACE = new THREE.Color(255 / 255, 200 / 255, 0) // yellow, face +const COLOR_FOOT = new THREE.Color(255 / 255, 128 / 255, 0) // orange, foot +const COLOR_HEAD = new THREE.Color(255 / 255, 0, 255 / 255) // purple, head + +// Body bone connections +const BODY_BONES = [ + // legs + [15, 13], [13, 11], [16, 14], [14, 12], [11, 12], // legs + [5, 11], [6, 12], [5, 6], // torso + [5, 7], [7, 9], [6, 8], [8, 10], // arms + [1, 2], [0, 1], [0, 2], [1, 3], [2, 4], // head + [15, 17], [15, 18], [15, 19], // left foot + [16, 20], [16, 21], [16, 22], // right foot +] as const + +// Body bone colors +const BODY_BONE_COLORS = [ + COLOR_LEGS, COLOR_LEGS, COLOR_LEGS, COLOR_LEGS, COLOR_LEGS, + COLOR_SPINE, COLOR_SPINE, COLOR_SPINE, + COLOR_ARMS, COLOR_ARMS, COLOR_ARMS, COLOR_ARMS, + COLOR_HEAD, COLOR_HEAD, COLOR_HEAD, COLOR_HEAD, COLOR_HEAD, + COLOR_FOOT, COLOR_FOOT, COLOR_FOOT, + COLOR_FOOT, COLOR_FOOT, COLOR_FOOT, +] as const + +// Hand bone connections (in pairs of [start, end] indices) +const HAND_BONES = [ + // right hand + [91, 92], [92, 93], [93, 94], [94, 95], // right thumb + [91, 96], [96, 97], [97, 98], [98, 99], // right index + [91, 100], [100, 101], [101, 102], [102, 103], // right middle + [91, 104], [104, 105], [105, 106], [106, 107], // right ring + [91, 108], [108, 109], [109, 110], [110, 111], // right pinky + // left hand + [112, 113], [113, 114], [114, 115], [115, 116], // left thumb + [112, 117], [117, 118], [118, 119], [119, 120], // left index + [112, 121], [121, 122], [122, 123], [123, 124], // left middle + [112, 125], [125, 126], [126, 127], [127, 128], // left ring + [112, 129], [129, 130], [130, 131], [131, 132] // left pinky +] as const + +const DEFAULT_TRANSFORMATION_MATRIX = [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, +] as const +const DEFAULT_NEAR = 0.05 +const DEFAULT_FAR = 1 +const CAMERA_EXTRINSIC_MATRIX_MAP: Record = { + "5602": [ + -0.76138617, 0.16195241, 0.62774399, -0.73832425, -0.40758532, + -0.87257285, -0.26924121, 0.55561231, 0.5041481, -0.46085577, + 0.73037432, 3.28663985, 0., 0., 0., + 1. + ] as const, + "5603": [ + -0.44966325, -0.02806161, -0.89275725, -0.24260155, 0.21789368, + -0.97275592, -0.07917234, 0.41197614, -0.8662132, -0.23012705, + 0.44352704, 4.37769458, 0., 0., 0., + 1. + ] as const, + "5604": [ + 0.9551736, -0.02735669, 0.29477958, 0.5908996, -0.06148677, + -0.99234061, 0.10714238, 0.87629594, 0.28959069, -0.12046462, + -0.94953963, 4.1617598, 0., 0., 0., + 1. + ] as const, + "5605": [ + 0.57140284, 0.03118154, -0.82007713, -0.22679438, 0.11021058, + -0.99314166, 0.0390292, 0.83904835, -0.81323577, -0.11268257, + -0.5709205, 4.17730229, 0., 0., 0., + 1. + ] as const, +} +const CAMERA_INTRINSIC_MATRIX_MAP: Record = { + "5602": [ + 2686.00393399, 0., 1470.49106388, 0., + 2699.92688641, 765.51266883, 0., 0., + 1. + ] as const, + "5603": [ + 2791.38378418, 0., 1258.11161208, 0., + 2790.67070205, 788.14860021, 0., 0., + 1. + ] as const, + "5604": [ + 2789.25680621, 0., 1231.13101573, 0., + 2787.08458106, 677.69385417, 0., 0., + 1. + ] as const, + "5605": [ + 2644.68145454, 0., 1285.34889127, 0., + 2644.9700955, 627.20808584, 0., 0., + 1. + ] as const, +} + +const IMAGE_WIDTH = 2560 +const IMAGE_HEIGHT = 1440 + +const intrinsicToFov = (intrinsic: number[], image_size: { width: number, height: number }) => { + console.assert(intrinsic.length === 9, "intrinsic must be a 3x3 matrix") + const fx = intrinsic[0] + const fy = intrinsic[4] + const cx = intrinsic[2] + const cy = intrinsic[5] + // in degrees + const fov_x = 2 * Math.atan(image_size.width / (2 * fx)) * (180 / Math.PI) + const fov_y = 2 * Math.atan(image_size.height / (2 * fy)) * (180 / Math.PI) + return { fov_x, fov_y } +} + +const Scene = () => { + const Axes = () => { + return + } + interface CameraViewFromExtrinsicProps { + extrinsic: number[] | THREE.Matrix4 + aspect?: number + name?: string + near?: number + far?: number + fov?: number + textSize?: number + } + // https://threejs.org/docs/#examples/en/loaders/FontLoader + // https://www.ilyameerovich.com/simple-3d-text-meshes-in-three-js/ + + const CameraViewFromExtrinsic = ({ extrinsic, name, near, far, fov, textSize, aspect }: CameraViewFromExtrinsicProps) => { + let Rt: THREE.Matrix4 + if (extrinsic instanceof THREE.Matrix4) { + Rt = extrinsic + } else if (Array.isArray(extrinsic)) { + console.assert(extrinsic.length === 16, "extrinsic must be a 4x4 matrix") + Rt = new THREE.Matrix4() + // @ts-expect-error 16 elements + Rt.set(...extrinsic) + } else { + throw new Error("extrinsic must be a 4x4 matrix or an array of 16 elements") + } + const font = new FontLoader().parse(HelvetikerRegular) + const camera = new THREE.PerspectiveCamera(fov ?? 60, aspect ?? 4 / 3, near ?? DEFAULT_NEAR, far ?? DEFAULT_FAR) + const helper = + camera.applyMatrix4(Rt) + + const textRef = useRef(null) + const { camera: viewCamera } = useThree() + + useFrame(() => { + if (textRef.current) { + textRef.current.lookAt(viewCamera.position) + } + }) + + let text: JSX.Element | null = null + if (name) { + const geo = new THREE_ADDONS.TextGeometry(name ?? "", { font, size: textSize ?? 0.1, depth: 0.001 }) + const position = new THREE.Vector3() + position.setFromMatrixPosition(Rt) + + text = ( + + + + + ) + } + return ( + + {text} + + {helper} + + ) + } + + const preProcessExtrinsic = (extrinsic: number[] | THREE.Matrix4) => { + let Rt: THREE.Matrix4 + if (extrinsic instanceof THREE.Matrix4) { + Rt = extrinsic + } else if (Array.isArray(extrinsic)) { + console.assert(extrinsic.length === 16, "extrinsic must be a 4x4 matrix") + Rt = new THREE.Matrix4() + // @ts-expect-error 16 elements + Rt.set(...extrinsic) + } else { + throw new Error("extrinsic must be a 4x4 matrix or an array of 16 elements") + } + + + // Then handle OpenCV to OpenGL camera convention + const cameraCvt = CV_TO_GL_MAT.clone() + + // Convert from Z-up to Y-up first (this affects world coordinates) + // const worldCvt = Z_UP_TO_Y_UP.clone() + + // Final transformation: + // 1. Convert world from Z-up to Y-up + // 2. Apply the camera transform + // 3. Convert camera coordinates from OpenCV to OpenGL + const final = new THREE.Matrix4() + final + .multiply(cameraCvt) + .multiply(Rt) + // .multiply(worldCvt) + + // Invert to get the camera-to-world transform + final.invert() + return final + } + + interface Human3DSkeletonProps { + skeleton: AnimePosePoints3D + startFrame?: number + jointRadius?: number + boneRadius?: number + showJoints?: boolean + showBones?: boolean + frameRate?: number + } + + /** + * 3D人体骨架动画组件 + * @param skeleton 动画帧序列,每帧为133个关节点的三维坐标 + * @param startFrame 起始帧 + * @param jointRadius 关节点球体半径 + * @param boneRadius 骨骼圆柱体半径 + * @param showJoints 是否显示关节点 + * @param showBones 是否显示骨骼 + * @param frameRate 动画帧率 + */ + const Human3DSkeleton = ({ + skeleton, + startFrame = 0, + jointRadius = 0.01, + boneRadius = 0.005, + showJoints = true, + showBones = true, + frameRate = 24 + }: Human3DSkeletonProps) => { + // 当前动画帧索引 + const [frameIndex, setFrameIndex] = useState(startFrame) + const totalFrames = skeleton.length + // 动画帧推进函数,按frameRate自动推进 + const onFrame: RenderCallback = (totalFrames === 0) ? (state, delta) => { } : (state: RootState, delta: number) => { + // 根据帧率和delta推进帧索引,实现动画 + setFrameIndex(prevFrame => { + const nextFrame = prevFrame + frameRate * delta + // 到末尾自动循环 + return nextFrame >= totalFrames ? 0 : nextFrame + }) + return null + } + + // 注册动画帧推进 + useFrame(onFrame) + + // 当前帧的133个关节点坐标 + const currentFrame = Math.floor(frameIndex) % totalFrames + const joints = skeleton[currentFrame] + + /** + * 右腕(关键点10)速度计算 + * 速度 = (当前帧位置 - 上一帧位置) / (1 / frameRate) + * 单位:与坐标单位一致/秒 + */ + let wristSpeed: number | null = null; + if (currentFrame > 0 && joints && joints.length > 16) { + const prevJoints = skeleton[(currentFrame - 1 + totalFrames) % totalFrames]; + if (prevJoints && prevJoints.length > 16) { + const wNow = joints[10]; + const wPrev = prevJoints[10]; + if (wNow && wPrev) { + const dx = wNow[0] - wPrev[0]; + const dy = wNow[1] - wPrev[1]; + const dz = wNow[2] - wPrev[2]; + const dist = Math.sqrt(dx * dx + dy * dy + dz * dz); + wristSpeed = dist * frameRate; // 单位/秒 + } + } + } + + /** + * 右手大臂(右肩-右肘)和小臂(右肘-右腕)夹角计算 + * COCO/BlazePose: 右肩=6, 右肘=8, 右腕=10 + * 夹角为两向量的夹角,单位度 + */ + let rightUpperArmAngle: number | null = null; + if (joints && joints.length > 16) { + const shoulder = joints[6]; // 右肩 + const elbow = joints[8]; // 右肘 + const wrist = joints[10]; // 右腕 + if (shoulder && elbow && wrist) { + // 大臂向量(肩->肘) + const v1 = new THREE.Vector3( + shoulder[0] - elbow[0], + shoulder[1] - elbow[1], + shoulder[2] - elbow[2] + ); + // 小臂向量(腕->肘) + const v2 = new THREE.Vector3( + wrist[0] - elbow[0], + wrist[1] - elbow[1], + wrist[2] - elbow[2] + ); + // 计算夹角 + const dot = v1.dot(v2); + const len1 = v1.length(); + const len2 = v2.length(); + if (len1 > 1e-6 && len2 > 1e-6) { + const angle = Math.acos(Math.max(-1, Math.min(1, dot / (len1 * len2)))); + rightUpperArmAngle = angle * 180 / Math.PI; + } + } + } + + /** + * 获取关节点颜色 + * @param idx 关节点索引 + */ + const getJointColor = (idx: number) => { + // 脸部 + if (idx >= 23 && idx <= 90) return COLOR_FACE + // 手指 + if (idx >= 91 && idx <= 132) return COLOR_FINGERS + // 脚 + if (idx >= 17 && idx <= 22) return COLOR_FOOT + // 头部 + if (idx <= 4) return COLOR_HEAD + // 手臂 + if (idx >= 5 && idx <= 10) return COLOR_ARMS + // 腿 + if (idx >= 11 && idx <= 16) return COLOR_LEGS + // 躯干 + return COLOR_SPINE + } + + /** + * 关节点坐标转换(如需坐标系变换可在此处理) + */ + const transformJointPosition = (j: [number, number, number]) => { + const [x, y, z] = j + const V = new THREE.Vector3(x, y, z) + return V + } + + // 生成关节点球体 + const jointMeshes = showJoints ? joints.map((j, idx) => { + const position = transformJointPosition(j) + const color = getJointColor(idx) + return ( + + + + + ) + }) : null + + // 生成骨骼圆柱体 + const boneMeshes = showBones ? ( + <> + {/* 身体主骨骼 */} + {BODY_BONES.map((bone, idx) => { + const [startIdx, endIdx] = bone + if (startIdx >= joints.length || endIdx >= joints.length) return null + // 起止点 + const startPos = transformJointPosition(joints[startIdx]) + const endPos = transformJointPosition(joints[endIdx]) + const color = BODY_BONE_COLORS[idx] + // 骨骼中点和长度 + const midpoint = new THREE.Vector3().addVectors(startPos, endPos).multiplyScalar(0.5) + const length = startPos.distanceTo(endPos) + // 旋转对齐 + const direction = new THREE.Vector3().subVectors(endPos, startPos).normalize() + const quaternion = new THREE.Quaternion() + const up = new THREE.Vector3(0, 1, 0) + quaternion.setFromUnitVectors(up, direction) + return ( + + + + + ) + })} + {/* 手部骨骼 */} + {HAND_BONES.map((bone, idx) => { + const [startIdx, endIdx] = bone + if (startIdx >= joints.length || endIdx >= joints.length) return null + const startPos = transformJointPosition(joints[startIdx]) + const endPos = transformJointPosition(joints[endIdx]) + const midpoint = new THREE.Vector3().addVectors(startPos, endPos).multiplyScalar(0.5) + const length = startPos.distanceTo(endPos) + const direction = new THREE.Vector3().subVectors(endPos, startPos).normalize() + const quaternion = new THREE.Quaternion() + const up = new THREE.Vector3(0, 1, 0) + quaternion.setFromUnitVectors(up, direction) + return ( + + + + + ) + })} + + ) : null + + // 渲染骨架、关节点、骨骼、右臂夹角和右腕速度文本 + return ( + + {jointMeshes} + {boneMeshes} + + ) + } + + // 调小关节点和骨骼的半径 + const skeletons = [ + , + , + ] + + const cameras = Object.entries(CAMERA_EXTRINSIC_MATRIX_MAP).map(([key, value]) => { + const intrinsic = CAMERA_INTRINSIC_MATRIX_MAP[key] + const { fov_x, fov_y } = intrinsicToFov(intrinsic, { width: IMAGE_WIDTH, height: IMAGE_HEIGHT }) + // make the far reverse proportional to the fov + const far = (1 / fov_x) * 20 + return + }) + + // 在场景中添加立方体 + const scene = ( + {/* */} + + + {/* */} + { } + + {cameras} + {skeletons} + ) + return ( + // Note that we don't need to import anything, All three.js objects will be treated + // as native JSX elements, just like you can just write
or in + // regular ReactDOM. The general rule is that Fiber components are available under + // the camel-case version of their name in three.js. + <> + + + {scene} + + ) +} + +function App() { + return ( + + + + ) +} + +export default App diff --git a/src/main.tsx b/src/main.tsx index 8b4667f..8975da5 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -3,10 +3,14 @@ import { createRoot } from 'react-dom/client' import './index.css' import App from './App.tsx' import { Leva } from 'leva' +import Boxing from './Boxing.tsx' +import Yeu_305 from './Yeu_305.tsx' createRoot(document.getElementById('root')!).render( - + {/* */} + + {/* */} , ) diff --git a/src/test.cpp b/src/test.cpp new file mode 100644 index 0000000..84c561b --- /dev/null +++ b/src/test.cpp @@ -0,0 +1,23 @@ +#include + +int main(){ + auto a = 10; + auto b = 20; + + + struct capture_object_t { + int a; + int b; + int sum(){ + return a+b; + } + }; // object + + auto sum = [a, b]() mutable { + return a+b; + }; // closure + + auto sum_obj = capture_object_t{.a = a, .b = b}; + std::print("a={},b={},sum={},obj={}; a={}",a,b,sum(),sum_obj.sum(), a); + return 0; +} \ No newline at end of file diff --git a/test b/test new file mode 100755 index 0000000000000000000000000000000000000000..4c5d2f71ec63ae49c9c97d1cf2c395450c3d89a7 GIT binary patch literal 238152 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

F>=t>M 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 OAHHVq&IJg;W%M6zsSDfy literal 0 HcmV?d00001 diff --git a/test.py b/test.py new file mode 100644 index 0000000..cb4fa3c --- /dev/null +++ b/test.py @@ -0,0 +1,19 @@ +from typing import Optional + + +def main(): + # privitive + # int, bool, float, string + a = 10 + b = 20 + + def sum(): + nonlocal a + a = 1 + return a + b + + print("a={};sum={};a={}".format(a, sum(), a)) + + +if __name__ == "__main__": + main()