Fix DICOM UPP model pivot

This commit is contained in:
Codex
2026-05-29 03:44:24 +08:00
parent 54bae18a21
commit 69a5926e3c

View File

@@ -94,6 +94,8 @@ const state = {
modelMeshes: [], modelMeshes: [],
stlSignature: "", stlSignature: "",
baseModelScale: 1, baseModelScale: 1,
modelBoundsFrame: null,
modelLocalBounds: null,
dicomSceneBox: null, dicomSceneBox: null,
mappingViewport: { scale: 1, offsetX: 0, offsetY: 0, rotation: 0 }, mappingViewport: { scale: 1, offsetX: 0, offsetY: 0, rotation: 0 },
mappingDrag: null, mappingDrag: null,
@@ -1018,6 +1020,8 @@ function clearFusion() {
state.dicomPlane = null; state.dicomPlane = null;
state.dicomPoints = null; state.dicomPoints = null;
state.dicomSlicePlanes = []; state.dicomSlicePlanes = [];
state.modelBoundsFrame = null;
state.modelLocalBounds = null;
$("fusionMeta").textContent = "DICOM - · STL -"; $("fusionMeta").textContent = "DICOM - · STL -";
$("fusionStatus").textContent = "等待选择 DICOM 与 STL"; $("fusionStatus").textContent = "等待选择 DICOM 与 STL";
updateSliceControl(); updateSliceControl();
@@ -1449,6 +1453,7 @@ async function buildDicomVolume(volume) {
const centerIndex = Number(volume.center || 0); const centerIndex = Number(volume.center || 0);
const total = Math.max(1, Number(volume.total || 1)); const total = Math.max(1, Number(volume.total || 1));
const indices = volume.indices || []; const indices = volume.indices || [];
const frames = volume.frames || [];
const centerPosition = indices.length ? Math.floor(indices.length / 2) : 0; const centerPosition = indices.length ? Math.floor(indices.length / 2) : 0;
state.volumeScene = { state.volumeScene = {
width, width,
@@ -1477,7 +1482,7 @@ async function buildDicomVolume(volume) {
const rangeDepth = Math.max(0.015, Math.abs(sliceToSceneZ(localEnd) - sliceToSceneZ(localStart))); const rangeDepth = Math.max(0.015, Math.abs(sliceToSceneZ(localEnd) - sliceToSceneZ(localStart)));
const rangeBox = new THREE.LineSegments( const rangeBox = new THREE.LineSegments(
new THREE.EdgesGeometry(new THREE.BoxGeometry(width * 0.94, height * 0.94, rangeDepth)), 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; rangeBox.position.z = (sliceToSceneZ(localStart) + sliceToSceneZ(localEnd)) / 2;
state.dicomGroup.add(rangeBox); state.dicomGroup.add(rangeBox);
@@ -1527,7 +1532,6 @@ async function buildDicomVolume(volume) {
const pointPositions = []; const pointPositions = [];
const pointColors = []; const pointColors = [];
const frames = volume.frames || [];
for (const [index, frame] of frames.entries()) { for (const [index, frame] of frames.entries()) {
const image = await loadImageData(frame, 180); const image = await loadImageData(frame, 180);
const stride = Math.max(2, Math.round(Math.max(image.width, image.height) / fusionDetail.pointGrid)); 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); clearGroup(state.rawModelGroup);
state.modelMeshes = []; state.modelMeshes = [];
state.modelBoundsFrame = null;
state.modelLocalBounds = null;
state.stlSignature = signature; state.stlSignature = signature;
const files = state.stlFiles.filter((file) => state.selectedStlIds.has(Number(file.id))); const files = state.stlFiles.filter((file) => state.selectedStlIds.has(Number(file.id)));
if (!files.length) { if (!files.length) {
@@ -1618,8 +1624,33 @@ async function buildStlModels(volume, onProgress = null) {
} }
const bbox = new THREE.Box3().setFromObject(state.rawModelGroup); const bbox = new THREE.Box3().setFromObject(state.rawModelGroup);
const center = new THREE.Vector3(); const center = new THREE.Vector3();
const size = new THREE.Vector3();
bbox.getCenter(center); 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(); applyPose();
scheduleMappingDraw(); scheduleMappingDraw();
onProgress?.(100); onProgress?.(100);