2026-05-07-18-11-12 增加模型库和可视化工具栏
This commit is contained in:
@@ -32,6 +32,7 @@ interface ModuleStyle {
|
||||
visible: boolean;
|
||||
color: string;
|
||||
opacity: number;
|
||||
partId: number;
|
||||
}
|
||||
|
||||
interface ModelPose {
|
||||
@@ -430,7 +431,7 @@ function NativeStlViewer({
|
||||
})
|
||||
.then((payload) => ({
|
||||
payload,
|
||||
style: styles[fileName] ?? { color: '#3b82f6', opacity: 0.72, visible: true },
|
||||
style: styles[fileName] ?? { color: '#3b82f6', opacity: 0.72, visible: true, partId: 1 },
|
||||
})),
|
||||
),
|
||||
).then((results) => {
|
||||
@@ -492,7 +493,7 @@ function NativeStlViewer({
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(payload.vertices, 3));
|
||||
geometry.computeVertexNormals();
|
||||
const style = styles[fileName] ?? { color: '#3b82f6', opacity: 0.72, visible: true };
|
||||
const style = styles[fileName] ?? { color: '#3b82f6', opacity: 0.72, visible: true, partId: 1 };
|
||||
const materialOpacity = solidMode ? Math.max(style.opacity, 0.94) : style.opacity;
|
||||
const mesh = new THREE.Mesh(
|
||||
geometry,
|
||||
@@ -629,12 +630,18 @@ function NativeStlViewer({
|
||||
);
|
||||
}
|
||||
|
||||
export default function ProjectLibrary({ onReverse }: { onReverse: (projId: string) => void }) {
|
||||
export default function ProjectLibrary({
|
||||
onReverse,
|
||||
initialViewMode = 'dicom',
|
||||
}: {
|
||||
onReverse: (projId: string) => void;
|
||||
initialViewMode?: 'dicom' | 'model' | 'mask';
|
||||
}) {
|
||||
const [search, setSearch] = useState('');
|
||||
const [projects, setProjects] = useState<Project[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [selectedProject, setSelectedProject] = useState<Project | null>(null);
|
||||
const [viewMode, setViewMode] = useState<'dicom' | 'model' | 'mask'>('dicom');
|
||||
const [viewMode, setViewMode] = useState<'dicom' | 'model' | 'mask'>(initialViewMode);
|
||||
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
|
||||
const [sliceIndex, setSliceIndex] = useState(0);
|
||||
const [plane, setPlane] = useState<Plane>('axial');
|
||||
@@ -701,6 +708,10 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri
|
||||
const sliceTotal = dicomPreview?.total ?? selectedProject?.dicomCount ?? 0;
|
||||
const selectedSolidity = solidityOptions.find((option) => option.id === solidityLevel) ?? solidityOptions[0];
|
||||
|
||||
useEffect(() => {
|
||||
setViewMode(initialViewMode);
|
||||
}, [initialViewMode]);
|
||||
|
||||
useEffect(() => {
|
||||
const next: Record<string, ModuleStyle> = {};
|
||||
stlFiles.forEach((fileName, index) => {
|
||||
@@ -708,6 +719,7 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri
|
||||
visible: true,
|
||||
color: defaultModuleColors[index % defaultModuleColors.length],
|
||||
opacity: 0.72,
|
||||
partId: index + 1,
|
||||
};
|
||||
});
|
||||
setModuleStyles(next);
|
||||
@@ -767,12 +779,18 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri
|
||||
visible: true,
|
||||
color: '#3b82f6',
|
||||
opacity: 0.72,
|
||||
partId: 1,
|
||||
...(prev[fileName] ?? {}),
|
||||
...partial,
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
const updateModulePartId = (fileName: string, value: number) => {
|
||||
const nextId = Math.max(1, Math.min(255, Math.round(Number.isFinite(value) ? value : 1)));
|
||||
updateModuleStyle(fileName, { partId: nextId });
|
||||
};
|
||||
|
||||
const toggleAllModules = () => {
|
||||
const nextVisible = !allModulesVisible;
|
||||
setModuleStyles(prev => {
|
||||
@@ -782,6 +800,7 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri
|
||||
visible: nextVisible,
|
||||
color: next[fileName]?.color ?? defaultModuleColors[index % defaultModuleColors.length],
|
||||
opacity: next[fileName]?.opacity ?? 0.72,
|
||||
partId: next[fileName]?.partId ?? index + 1,
|
||||
};
|
||||
});
|
||||
return next;
|
||||
@@ -1340,7 +1359,7 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri
|
||||
<div className="flex-1 overflow-y-auto space-y-2 pr-1 scrollbar-hide">
|
||||
{stlFiles.map((fileName, i) => {
|
||||
const name = fileName.replace(/\.stl$/i, '');
|
||||
const style = moduleStyles[fileName] ?? { visible: true, color: defaultModuleColors[i % defaultModuleColors.length], opacity: 0.72 };
|
||||
const style = moduleStyles[fileName] ?? { visible: true, color: defaultModuleColors[i % defaultModuleColors.length], opacity: 0.72, partId: i + 1 };
|
||||
return (
|
||||
<div
|
||||
key={fileName}
|
||||
@@ -1355,7 +1374,21 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-[11px] font-bold text-slate-700 truncate">{name}</p>
|
||||
<p className="text-[9px] text-slate-400 truncate">STL | {fileName}</p>
|
||||
<div className="mt-0.5 flex items-center gap-2">
|
||||
<p className="min-w-0 flex-1 text-[9px] text-slate-400 truncate">STL | {fileName}</p>
|
||||
<label className="flex shrink-0 items-center gap-1 text-[9px] font-bold text-slate-400">
|
||||
ID
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
max="255"
|
||||
value={style.partId}
|
||||
onChange={(event) => updateModulePartId(fileName, Number(event.target.value))}
|
||||
onBlur={(event) => updateModulePartId(fileName, Number(event.target.value))}
|
||||
className="h-5 w-12 rounded border border-slate-200 bg-white px-1 text-[9px] font-mono text-slate-600 outline-none focus:border-blue-400"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className="mt-2 flex items-center gap-2">
|
||||
<span className="text-[9px] text-slate-400 shrink-0">透明度</span>
|
||||
<input
|
||||
|
||||
Reference in New Issue
Block a user