From 69a5926e3c837403be700133c4442cd98054a6ae Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 29 May 2026 03:44:24 +0800 Subject: [PATCH] Fix DICOM UPP model pivot --- DICOM_and_UPP配准/static/app.js | 37 ++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/DICOM_and_UPP配准/static/app.js b/DICOM_and_UPP配准/static/app.js index a2eff80..3653c30 100644 --- a/DICOM_and_UPP配准/static/app.js +++ b/DICOM_and_UPP配准/static/app.js @@ -94,6 +94,8 @@ const state = { modelMeshes: [], stlSignature: "", baseModelScale: 1, + modelBoundsFrame: null, + modelLocalBounds: null, dicomSceneBox: null, mappingViewport: { scale: 1, offsetX: 0, offsetY: 0, rotation: 0 }, mappingDrag: null, @@ -1018,6 +1020,8 @@ function clearFusion() { state.dicomPlane = null; state.dicomPoints = null; state.dicomSlicePlanes = []; + state.modelBoundsFrame = null; + state.modelLocalBounds = null; $("fusionMeta").textContent = "DICOM - · STL -"; $("fusionStatus").textContent = "等待选择 DICOM 与 STL"; updateSliceControl(); @@ -1449,6 +1453,7 @@ async function buildDicomVolume(volume) { const centerIndex = Number(volume.center || 0); const total = Math.max(1, Number(volume.total || 1)); const indices = volume.indices || []; + const frames = volume.frames || []; const centerPosition = indices.length ? Math.floor(indices.length / 2) : 0; state.volumeScene = { width, @@ -1477,7 +1482,7 @@ async function buildDicomVolume(volume) { const rangeDepth = Math.max(0.015, Math.abs(sliceToSceneZ(localEnd) - sliceToSceneZ(localStart))); const rangeBox = new THREE.LineSegments( new THREE.EdgesGeometry(new THREE.BoxGeometry(width * 0.94, height * 0.94, rangeDepth)), - new THREE.LineBasicMaterial({ color: 0xfacc15, transparent: true, opacity: 0.56 }), + new THREE.LineBasicMaterial({ color: 0x38bdf8, transparent: true, opacity: 0.24 }), ); rangeBox.position.z = (sliceToSceneZ(localStart) + sliceToSceneZ(localEnd)) / 2; state.dicomGroup.add(rangeBox); @@ -1527,7 +1532,6 @@ async function buildDicomVolume(volume) { const pointPositions = []; const pointColors = []; - const frames = volume.frames || []; for (const [index, frame] of frames.entries()) { const image = await loadImageData(frame, 180); const stride = Math.max(2, Math.round(Math.max(image.width, image.height) / fusionDetail.pointGrid)); @@ -1578,6 +1582,8 @@ async function buildStlModels(volume, onProgress = null) { } clearGroup(state.rawModelGroup); state.modelMeshes = []; + state.modelBoundsFrame = null; + state.modelLocalBounds = null; state.stlSignature = signature; const files = state.stlFiles.filter((file) => state.selectedStlIds.has(Number(file.id))); if (!files.length) { @@ -1618,8 +1624,33 @@ async function buildStlModels(volume, onProgress = null) { } const bbox = new THREE.Box3().setFromObject(state.rawModelGroup); const center = new THREE.Vector3(); + const size = new THREE.Vector3(); bbox.getCenter(center); - state.rawModelGroup.position.set(-center.x, -center.y, -center.z); + bbox.getSize(size); + state.rawModelGroup.position.set(0, 0, 0); + state.rawModelGroup.traverse((object) => { + if (!object.geometry) return; + object.geometry.translate(-center.x, -center.y, -center.z); + object.geometry.computeBoundingBox?.(); + object.geometry.computeBoundingSphere?.(); + object.geometry.computeVertexNormals?.(); + }); + const maxModelSize = Math.max(size.x, size.y, size.z, 1); + const volumeSize = state.volumeScene + ? Math.max(state.volumeScene.width, state.volumeScene.height, state.volumeScene.depth, 1) + : 4.8; + state.baseModelScale = (volumeSize / maxModelSize) * 0.92; + state.modelLocalBounds = { + center: { x: center.x, y: center.y, z: center.z }, + size: { x: size.x, y: size.y, z: size.z }, + }; + const modelBounds = new THREE.LineSegments( + new THREE.EdgesGeometry(new THREE.BoxGeometry(size.x, size.y, size.z)), + new THREE.LineBasicMaterial({ color: 0xfacc15, transparent: true, opacity: 0.78 }), + ); + modelBounds.name = "model-bounds"; + state.rawModelGroup.add(modelBounds); + state.modelBoundsFrame = modelBounds; applyPose(); scheduleMappingDraw(); onProgress?.(100);