diff --git a/DICOM_and_UPP配准/static/app.js b/DICOM_and_UPP配准/static/app.js index 8658e17..225caa5 100644 --- a/DICOM_and_UPP配准/static/app.js +++ b/DICOM_and_UPP配准/static/app.js @@ -65,6 +65,12 @@ const AUTO_PROGRESS_STEPS = [ { id: "score", label: "评分" }, { id: "apply", label: "应用" }, ]; +const STL_DISPLAY_MODES = ["hidden", "solid", "translucent"]; +const STL_DISPLAY_LABELS = { + hidden: "取消显示", + solid: "实体显示", + translucent: "半透明", +}; const state = { token: localStorage.getItem("dicom_upp_registration_token") || "", @@ -92,6 +98,8 @@ const state = { registrationSeriesUid: "", stlFiles: [], selectedStlIds: new Set(), + stlDisplayModes: {}, + collapsedStlFamilies: new Set(), algorithmModel: "", registrationStatus: "unregistered", pose: { ...DEFAULT_POSE }, @@ -109,6 +117,7 @@ const state = { autoDeltaBaseline: null, autoFineRunning: false, poseControlsLocked: false, + collapsedPoseSections: { manual: false, auto: false }, dirty: false, dirtyReason: "", savedPoseSignature: "", @@ -533,7 +542,7 @@ function stlSelectionSignature() { } function visibleModelRecords() { - return state.modelMeshes.filter((record) => state.selectedStlIds.has(Number(record.file?.id)) && record.mesh?.visible !== false); + return state.modelMeshes.filter((record) => stlMode(record.file) !== "hidden" && record.mesh?.visible !== false); } function modelPoseMatrix() { @@ -609,6 +618,8 @@ function clearDetail() { state.series = []; state.stlFiles = []; state.selectedStlIds = new Set(); + state.stlDisplayModes = {}; + state.collapsedStlFamilies = new Set(); state.selectedSeriesUid = ""; state.registrationSeriesUid = ""; $("activeTitle").textContent = "未选择 CT"; @@ -662,6 +673,8 @@ async function selectCase(ctNumber, algorithmModel = "未指定模型") { const savedIds = new Set((registration.selected_stl_files || []).map((item) => Number(item.id)).filter(Number.isFinite)); state.selectedStlIds = savedIds.size ? savedIds : pickDefaultStlIds(state.stlFiles); + state.stlDisplayModes = initStlDisplayModes(state.stlFiles, state.selectedStlIds, registration.module_styles || {}); + state.collapsedStlFamilies = new Set(); const defaultSeries = pickDefaultSeries(state.series); const savedSeries = state.series.find((item) => item.series_uid === registration.series_instance_uid); const shouldUseSavedSeries = registration.registration_status === "registered" && savedSeries; @@ -728,6 +741,101 @@ function pickDefaultStlIds(files) { return new Set(ranked.slice(0, Math.min(6, ranked.length)).map((file) => Number(file.id))); } +function normalizedStlId(fileOrId) { + const id = typeof fileOrId === "object" ? Number(fileOrId?.id) : Number(fileOrId); + return Number.isFinite(id) ? id : null; +} + +function validStlDisplayMode(mode) { + return STL_DISPLAY_MODES.includes(mode) ? mode : ""; +} + +function stlMode(fileOrId) { + const id = normalizedStlId(fileOrId); + if (id == null || !state.selectedStlIds.has(id)) return "hidden"; + return validStlDisplayMode(state.stlDisplayModes[id]) || "translucent"; +} + +function stlFamilyLabel(file) { + return String(file.family || "未分类"); +} + +function stlFileIndex(file) { + const id = normalizedStlId(file); + const index = state.stlFiles.findIndex((item) => normalizedStlId(item) === id); + return index >= 0 ? index : 0; +} + +function stlColor(file, fallbackIndex = 0) { + const index = stlFileIndex(file); + return cssColor(index >= 0 ? index : fallbackIndex); +} + +function syncStlDisplayModesForSelection(defaultMode = "translucent") { + const next = {}; + state.stlFiles.forEach((file) => { + const id = normalizedStlId(file); + if (id == null || !state.selectedStlIds.has(id)) return; + next[id] = validStlDisplayMode(state.stlDisplayModes[id]) || defaultMode; + }); + state.stlDisplayModes = next; +} + +function initStlDisplayModes(files, selectedIds, moduleStyles = {}) { + const modes = {}; + files.forEach((file) => { + const id = normalizedStlId(file); + if (id == null || !selectedIds.has(id)) return; + const style = moduleStyles?.[file.file_name] || moduleStyles?.[file.segment_name] || {}; + const savedMode = validStlDisplayMode(style.mode); + if (savedMode && savedMode !== "hidden") { + modes[id] = savedMode; + return; + } + modes[id] = Number(style.opacity) >= 0.95 ? "solid" : "translucent"; + }); + return modes; +} + +async function setStlMode(fileOrId, mode) { + const id = normalizedStlId(fileOrId); + const nextMode = validStlDisplayMode(mode); + if (id == null || !nextMode) return; + if (nextMode === "hidden") { + state.selectedStlIds.delete(id); + delete state.stlDisplayModes[id]; + } else { + state.selectedStlIds.add(id); + state.stlDisplayModes[id] = nextMode; + } + await applyStlSelectionChange(); +} + +async function cycleStlMode(file) { + const next = { + hidden: "solid", + solid: "translucent", + translucent: "hidden", + }[stlMode(file)] || "translucent"; + await setStlMode(file, next); +} + +async function setAllStlMode(mode) { + const nextMode = validStlDisplayMode(mode); + if (!state.stlFiles.length || !nextMode) return; + if (nextMode === "hidden") { + state.selectedStlIds = new Set(); + state.stlDisplayModes = {}; + } else { + state.selectedStlIds = new Set(state.stlFiles.map((file) => normalizedStlId(file)).filter((id) => id != null)); + state.stlDisplayModes = {}; + state.selectedStlIds.forEach((id) => { + state.stlDisplayModes[id] = nextMode; + }); + } + await applyStlSelectionChange(); +} + function currentSeries() { return state.series.find((item) => item.series_uid === state.selectedSeriesUid) || null; } @@ -829,56 +937,91 @@ async function selectSeries(uid, options = {}) { } function renderStl() { - $("stlCount").textContent = state.stlFiles.length; + $("stlCount").textContent = state.stlFiles.length ? `${state.selectedStlIds.size}/${state.stlFiles.length}` : "0"; const models = [...new Set(state.stlFiles.map((item) => item.algorithm_model || state.algorithmModel || "未指定模型"))]; $("modelRail").innerHTML = models.map((model) => `${escapeHtml(model)}`).join(""); - $("stlList").innerHTML = state.stlFiles.length - ? state.stlFiles - .map((file, index) => { - const checked = state.selectedStlIds.has(Number(file.id)); - return ` - - `; - }) - .join("") - : `