From 5a9d09a5bf420241257befc29faf109b15efa037 Mon Sep 17 00:00:00 2001 From: admin <572701190@qq.com> Date: Tue, 23 Jun 2026 13:54:15 +0800 Subject: [PATCH] =?UTF-8?q?2026-06-23-13-38-48=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E9=80=86=E5=90=91=E5=B7=A5=E4=BD=9C=E5=8C=BA=E5=8D=95=E8=BD=B4?= =?UTF-8?q?=E6=8B=89=E4=BC=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebSite/server.ts | 31 +++- WebSite/src/components/AutoMatchWorkspace.tsx | 15 ++ WebSite/src/components/ProjectLibrary.tsx | 39 ++++- WebSite/src/components/ReverseWorkspace.tsx | 141 ++++++++++++++---- WebSite/src/types.ts | 3 + 工程分析/实现方案-2026-06-23-13-38-48.md | 71 +++++++++ 工程分析/测试方案-2026-06-23-13-38-48.md | 70 +++++++++ 工程分析/经验记录.md | 18 +++ 工程分析/需求分析-2026-06-23-13-38-48.md | 55 +++++++ 9 files changed, 401 insertions(+), 42 deletions(-) create mode 100644 工程分析/实现方案-2026-06-23-13-38-48.md create mode 100644 工程分析/测试方案-2026-06-23-13-38-48.md create mode 100644 工程分析/需求分析-2026-06-23-13-38-48.md diff --git a/WebSite/server.ts b/WebSite/server.ts index 9b37c28..36ce1c6 100644 --- a/WebSite/server.ts +++ b/WebSite/server.ts @@ -33,6 +33,9 @@ interface ModelPoseValue { translateY: number; translateZ: number; scale: number; + scaleX: number; + scaleY: number; + scaleZ: number; flipX: boolean; flipY: boolean; flipZ: boolean; @@ -179,6 +182,9 @@ const defaultModelPose: ModelPoseValue = { translateY: 0, translateZ: 0, scale: 1, + scaleX: 1, + scaleY: 1, + scaleZ: 1, flipX: false, flipY: false, flipZ: false, @@ -188,6 +194,9 @@ const modelPoseValuePrecision: Partial> = { translateY: 3, translateZ: 3, scale: 3, + scaleX: 3, + scaleY: 3, + scaleZ: 3, }; interface DicomAttributes { @@ -494,6 +503,9 @@ function normalizeModelPoseValue(value: Partial | undefined): Mo translateY: read('translateY', defaultModelPose.translateY, -2, 2), translateZ: read('translateZ', defaultModelPose.translateZ, -2, 2), scale: read('scale', defaultModelPose.scale, 0.5, 3), + scaleX: read('scaleX', defaultModelPose.scaleX, 0.2, 8), + scaleY: read('scaleY', defaultModelPose.scaleY, 0.2, 8), + scaleZ: read('scaleZ', defaultModelPose.scaleZ, 0.2, 8), flipX: readBoolean('flipX', defaultModelPose.flipX), flipY: readBoolean('flipY', defaultModelPose.flipY), flipZ: readBoolean('flipZ', defaultModelPose.flipZ), @@ -1030,9 +1042,9 @@ function transformPointForExportPose(x: number, y: number, z: number, metrics: E y: afterX.y, z: -afterX.x * sinY + afterX.z * cosY, }; - px = afterY.x * cosZ - afterY.y * sinZ; - py = afterY.x * sinZ + afterY.y * cosZ; - pz = afterY.z; + px = (afterY.x * cosZ - afterY.y * sinZ) * pose.scaleX; + py = (afterY.x * sinZ + afterY.y * cosZ) * pose.scaleY; + pz = afterY.z * pose.scaleZ; return { x: px + pose.translateX, @@ -2437,6 +2449,10 @@ function normalizePipelinePose(metadata: Record) { const translateY = readNumberOption(translation.y ?? translation.translateY ?? metadata.translateY); const translateZ = readNumberOption(translation.z ?? translation.translateZ ?? metadata.translateZ); const scale = readNumberOption(metadata.scale ?? poseSource.scale); + const axisScale = readNestedObject(metadata.axisScale ?? metadata.axisScales ?? metadata.stretchScale); + const scaleX = readNumberOption(axisScale.x ?? axisScale.scaleX ?? metadata.scaleX); + const scaleY = readNumberOption(axisScale.y ?? axisScale.scaleY ?? metadata.scaleY); + const scaleZ = readNumberOption(axisScale.z ?? axisScale.scaleZ ?? metadata.scaleZ); if (rotationX !== undefined) pose.rotateX = rotationX; if (rotationY !== undefined) pose.rotateY = rotationY; @@ -2445,6 +2461,9 @@ function normalizePipelinePose(metadata: Record) { if (translateY !== undefined) pose.translateY = translateY; if (translateZ !== undefined) pose.translateZ = translateZ; if (scale !== undefined) pose.scale = scale; + if (scaleX !== undefined) pose.scaleX = scaleX; + if (scaleY !== undefined) pose.scaleY = scaleY; + if (scaleZ !== undefined) pose.scaleZ = scaleZ; (['X', 'Y', 'Z'] as const).forEach((axis) => { const lowerAxis = axis.toLowerCase(); @@ -2518,9 +2537,9 @@ function rotatedModelSizeForPose(bounds: ModelBoundsRecord, pose: ModelPoseValue y: afterX.y, z: -afterX.x * sinY + afterX.z * cosY, }; - px = afterY.x * cosZ - afterY.y * sinZ; - py = afterY.x * sinZ + afterY.y * cosZ; - pz = afterY.z; + px = (afterY.x * cosZ - afterY.y * sinZ) * pose.scaleX; + py = (afterY.x * sinZ + afterY.y * cosZ) * pose.scaleY; + pz = afterY.z * pose.scaleZ; rotatedBounds.min.x = Math.min(rotatedBounds.min.x, px); rotatedBounds.min.y = Math.min(rotatedBounds.min.y, py); rotatedBounds.min.z = Math.min(rotatedBounds.min.z, pz); diff --git a/WebSite/src/components/AutoMatchWorkspace.tsx b/WebSite/src/components/AutoMatchWorkspace.tsx index 82d249e..838681d 100644 --- a/WebSite/src/components/AutoMatchWorkspace.tsx +++ b/WebSite/src/components/AutoMatchWorkspace.tsx @@ -31,6 +31,9 @@ const defaultModelPose: ModelPose = { translateY: 0, translateZ: 0, scale: 1, + scaleX: 1, + scaleY: 1, + scaleZ: 1, flipX: false, flipY: false, flipZ: false, @@ -583,6 +586,18 @@ export default function AutoMatchWorkspace({ projectId, initialPose, onOpenRever ))} +
+ {[ + { label: '轴拉伸 X', value: comparePose.scaleX }, + { label: '轴拉伸 Y', value: comparePose.scaleY }, + { label: '轴拉伸 Z', value: comparePose.scaleZ }, + ].map((item) => ( +
+ {item.label} + {formatPoseNumber(item.value)} +
+ ))} +
{result && !resultHasPoseChange && (
diff --git a/WebSite/src/components/ProjectLibrary.tsx b/WebSite/src/components/ProjectLibrary.tsx index 3e1aeb6..61d77cd 100644 --- a/WebSite/src/components/ProjectLibrary.tsx +++ b/WebSite/src/components/ProjectLibrary.tsx @@ -53,6 +53,9 @@ interface ModelPose { translateY: number; translateZ: number; scale: number; + scaleX: number; + scaleY: number; + scaleZ: number; flipX: boolean; flipY: boolean; flipZ: boolean; @@ -101,6 +104,9 @@ const defaultModelPose: ModelPose = { translateY: 0, translateZ: 0, scale: 1, + scaleX: 1, + scaleY: 1, + scaleZ: 1, flipX: false, flipY: false, flipZ: false, @@ -124,12 +130,18 @@ const modelPoseLimits: Record = { translateY: { min: -2, max: 2 }, translateZ: { min: -2, max: 2 }, scale: { min: 0.5, max: 2.5 }, + scaleX: { min: 0.2, max: 8 }, + scaleY: { min: 0.2, max: 8 }, + scaleZ: { min: 0.2, max: 8 }, }; const modelPoseStepPrecision: Partial> = { translateX: 3, translateY: 3, translateZ: 3, scale: 3, + scaleX: 3, + scaleY: 3, + scaleZ: 3, }; function clampModelPoseValue(key: ModelPoseKey, value: number) { @@ -161,6 +173,9 @@ function clampModelPose(next: ModelPose): ModelPose { translateY: clampModelPoseValue('translateY', next.translateY), translateZ: clampModelPoseValue('translateZ', next.translateZ), scale: clampModelPoseValue('scale', next.scale), + scaleX: clampModelPoseValue('scaleX', next.scaleX), + scaleY: clampModelPoseValue('scaleY', next.scaleY), + scaleZ: clampModelPoseValue('scaleZ', next.scaleZ), flipX: Boolean(next.flipX), flipY: Boolean(next.flipY), flipZ: Boolean(next.flipZ), @@ -699,8 +714,12 @@ function NativeStlViewer({ scene.add(fillLight); const poseGroup = new THREE.Group(); + const axisScaleGroup = new THREE.Group(); + const rotationScaleGroup = new THREE.Group(); const pivotGroup = new THREE.Group(); - poseGroup.add(pivotGroup); + rotationScaleGroup.add(pivotGroup); + axisScaleGroup.add(rotationScaleGroup); + poseGroup.add(axisScaleGroup); let baseScale = 1; scene.add(poseGroup); let loaded = 0; @@ -774,7 +793,7 @@ function NativeStlViewer({ pivotGroup.position.set(0, 0, 0); baseScale = 4.2 / maxSize; const initialPoseScale = baseScale * poseRef.current.scale; - pivotGroup.scale.set( + rotationScaleGroup.scale.set( poseRef.current.flipX ? -initialPoseScale : initialPoseScale, poseRef.current.flipY ? -initialPoseScale : initialPoseScale, poseRef.current.flipZ ? -initialPoseScale : initialPoseScale, @@ -803,13 +822,14 @@ function NativeStlViewer({ if (disposed) return; const currentPose = poseRef.current; poseGroup.position.set(currentPose.translateX, currentPose.translateY, currentPose.translateZ); - pivotGroup.rotation.set( + axisScaleGroup.scale.set(currentPose.scaleX, currentPose.scaleY, currentPose.scaleZ); + rotationScaleGroup.rotation.set( THREE.MathUtils.degToRad(currentPose.rotateX), THREE.MathUtils.degToRad(currentPose.rotateY), THREE.MathUtils.degToRad(currentPose.rotateZ), ); const poseScale = baseScale * currentPose.scale; - pivotGroup.scale.set( + rotationScaleGroup.scale.set( currentPose.flipX ? -poseScale : poseScale, currentPose.flipY ? -poseScale : poseScale, currentPose.flipZ ? -poseScale : poseScale, @@ -1353,6 +1373,9 @@ export default function ProjectLibrary({ translateY: 0, translateZ: 0, scale: 1, + scaleX: 1, + scaleY: 1, + scaleZ: 1, })); }; @@ -2143,8 +2166,11 @@ export default function ProjectLibrary({ { key: 'translateY' as const, label: '平移 Y', min: -2, max: 2, step: 0.001, value: modelPose.translateY, minus: '-0.001', plus: '+0.001', delta: 0.001 }, { key: 'translateZ' as const, label: '平移 Z', min: -2, max: 2, step: 0.001, value: modelPose.translateZ, minus: '-0.001', plus: '+0.001', delta: 0.001 }, { key: 'scale' as const, label: '缩放', min: 0.5, max: 2.5, step: 0.001, value: modelPose.scale, minus: '-0.001', plus: '+0.001', delta: 0.001 }, + { key: 'scaleX' as const, label: '轴拉伸 X', min: 0.2, max: 8, step: 0.001, value: modelPose.scaleX, minus: '-0.001', plus: '+0.001', delta: 0.001 }, + { key: 'scaleY' as const, label: '轴拉伸 Y', min: 0.2, max: 8, step: 0.001, value: modelPose.scaleY, minus: '-0.001', plus: '+0.001', delta: 0.001 }, + { key: 'scaleZ' as const, label: '轴拉伸 Z', min: 0.2, max: 8, step: 0.001, value: modelPose.scaleZ, minus: '-0.001', plus: '+0.001', delta: 0.001 }, ].map((item) => ( -
+
{item.label} + ))} + + 单轴 + {(['x', 'y', 'z'] as AxisKey[]).map((axis) => ( + ))}
@@ -4186,8 +4262,11 @@ export default function ReverseWorkspace({ { key: 'translateY' as const, label: '平移 Y', value: modelPose.translateY }, { key: 'translateZ' as const, label: '平移 Z', value: modelPose.translateZ }, { key: 'scale' as const, label: '缩放', value: modelPose.scale }, + { key: 'scaleX' as const, label: '轴拉伸 X', value: modelPose.scaleX }, + { key: 'scaleY' as const, label: '轴拉伸 Y', value: modelPose.scaleY }, + { key: 'scaleZ' as const, label: '轴拉伸 Z', value: modelPose.scaleZ }, ].map((item) => ( -
+
{item.label}