/** * Geometry editing toolbar for interactive vessel modification * Provides tools for drawing stents, adding stenosis/aneurysm */ import { useState, useCallback } from "react"; import type { Point2D, StenosisParams, AneurysmParams, StentParams } from "../lib/types"; /** Available geometry editing tools */ export type GeometryTool = "select" | "stent" | "stenosis" | "aneurysm" | "pan"; /** Tool state for stenosis placement */ export interface StenosisToolState { active: boolean; position: Point2D | null; diameterRatio: number; length: number; } /** Tool state for aneurysm placement */ export interface AneurysmToolState { active: boolean; position: Point2D | null; diameterRatio: number; sacRadius: number; } /** Tool state for stent drawing */ export interface StentToolState { active: boolean; path: Point2D[]; radius: number; porosity: number; isDrawing: boolean; } /** Combined geometry editing state */ export interface GeometryEditorState { currentTool: GeometryTool; stenosis: StenosisToolState; aneurysm: AneurysmToolState; stent: StentToolState; previewVisible: boolean; } /** Initial state for geometry editor */ export const initialEditorState: GeometryEditorState = { currentTool: "select", stenosis: { active: false, position: null, diameterRatio: 0.5, length: 0.01, }, aneurysm: { active: false, position: null, diameterRatio: 1.5, sacRadius: 0.003, }, stent: { active: false, path: [], radius: 0.0005, porosity: 0.8, isDrawing: false, }, previewVisible: true, }; interface GeometryToolsProps { editorState: GeometryEditorState; onStateChange: (state: GeometryEditorState) => void; onApplyStenosis: (params: StenosisParams) => void; onApplyAneurysm: (params: AneurysmParams) => void; onApplyStent: (params: StentParams) => void; onResetGeometry: () => void; isDisabled: boolean; vesselLength: number; vesselRadius: number; } /** * Toolbar component for geometry editing tools */ export function GeometryTools({ editorState, onStateChange, onApplyStenosis, onApplyAneurysm, onApplyStent, onResetGeometry, isDisabled, vesselLength, vesselRadius, }: GeometryToolsProps) { const [showHelp, setShowHelp] = useState(false); const selectTool = useCallback( (tool: GeometryTool) => { onStateChange({ ...editorState, currentTool: tool, stenosis: { ...editorState.stenosis, active: tool === "stenosis" }, aneurysm: { ...editorState.aneurysm, active: tool === "aneurysm" }, stent: { ...editorState.stent, active: tool === "stent", isDrawing: false, path: tool === "stent" ? [] : editorState.stent.path, }, }); }, [editorState, onStateChange] ); const handleApplyStenosis = useCallback(() => { if (editorState.stenosis.position) { onApplyStenosis({ diameter_ratio: editorState.stenosis.diameterRatio, length: editorState.stenosis.length, center_x: editorState.stenosis.position[0], }); // Reset stenosis tool state after application onStateChange({ ...editorState, stenosis: { ...editorState.stenosis, position: null }, }); } }, [editorState, onApplyStenosis, onStateChange]); const handleApplyAneurysm = useCallback(() => { if (editorState.aneurysm.position) { onApplyAneurysm({ diameter_ratio: editorState.aneurysm.diameterRatio, sac_radius: editorState.aneurysm.sacRadius, center_x: editorState.aneurysm.position[0], center_y: editorState.aneurysm.position[1], }); // Reset aneurysm tool state after application onStateChange({ ...editorState, aneurysm: { ...editorState.aneurysm, position: null }, }); } }, [editorState, onApplyAneurysm, onStateChange]); const handleApplyStent = useCallback(() => { if (editorState.stent.path.length >= 2) { onApplyStent({ path: editorState.stent.path, radius: editorState.stent.radius, porosity: editorState.stent.porosity, }); // Reset stent tool state after application onStateChange({ ...editorState, stent: { ...editorState.stent, path: [], isDrawing: false }, }); } }, [editorState, onApplyStent, onStateChange]); const handleClearStentPath = useCallback(() => { onStateChange({ ...editorState, stent: { ...editorState.stent, path: [], isDrawing: false }, }); }, [editorState, onStateChange]); const updateStenosisParams = useCallback( (updates: Partial) => { onStateChange({ ...editorState, stenosis: { ...editorState.stenosis, ...updates }, }); }, [editorState, onStateChange] ); const updateAneurysmParams = useCallback( (updates: Partial) => { onStateChange({ ...editorState, aneurysm: { ...editorState.aneurysm, ...updates }, }); }, [editorState, onStateChange] ); const updateStentParams = useCallback( (updates: Partial) => { onStateChange({ ...editorState, stent: { ...editorState.stent, ...updates }, }); }, [editorState, onStateChange] ); const togglePreview = useCallback(() => { onStateChange({ ...editorState, previewVisible: !editorState.previewVisible, }); }, [editorState, onStateChange]); return (

Geometry Tools

{/* Tool selection buttons */}
{/* Tool-specific controls */} {editorState.currentTool === "stenosis" && (

Stenosis Parameters

updateStenosisParams({ diameterRatio: parseFloat(e.target.value) }) } min={0.1} max={0.9} step={0.05} disabled={isDisabled} /> {(editorState.stenosis.diameterRatio * 100).toFixed(0)}%
updateStenosisParams({ length: parseFloat(e.target.value) }) } min={0.002} max={vesselLength * 0.5} step={0.001} disabled={isDisabled} /> {(editorState.stenosis.length * 1000).toFixed(1)} mm
{editorState.stenosis.position && (
Position: ({(editorState.stenosis.position[0] * 1000).toFixed(1)} mm, 0)
)}
)} {editorState.currentTool === "aneurysm" && (

Aneurysm Parameters

updateAneurysmParams({ diameterRatio: parseFloat(e.target.value) }) } min={1.1} max={3.0} step={0.1} disabled={isDisabled} /> {editorState.aneurysm.diameterRatio.toFixed(1)}x
updateAneurysmParams({ sacRadius: parseFloat(e.target.value) }) } min={vesselRadius * 0.2} max={vesselRadius * 2} step={0.0005} disabled={isDisabled} /> {(editorState.aneurysm.sacRadius * 1000).toFixed(2)} mm
{editorState.aneurysm.position && (
Position: ({(editorState.aneurysm.position[0] * 1000).toFixed(1)} mm,{" "} {(editorState.aneurysm.position[1] * 1000).toFixed(1)} mm)
)}
)} {editorState.currentTool === "stent" && (

Stent Parameters

updateStentParams({ radius: parseFloat(e.target.value) }) } min={0.0002} max={vesselRadius * 0.5} step={0.0001} disabled={isDisabled} /> {(editorState.stent.radius * 1000).toFixed(2)} mm
updateStentParams({ porosity: parseFloat(e.target.value) }) } min={0.1} max={0.95} step={0.05} disabled={isDisabled} /> {(editorState.stent.porosity * 100).toFixed(0)}%
Path points: {editorState.stent.path.length} {editorState.stent.isDrawing && ( (Drawing...) )}
)} {/* Common actions */}
{/* Help button and panel */} {showHelp && (

How to Use

  • Select: Click on the vessel to inspect field values
  • Pan: Drag to move the view
  • Stenosis: Click on vessel axis to place narrowing
  • Aneurysm: Click near vessel wall to place bulge
  • Stent: Click to start, drag to draw path, click to end

After placing a modification, click "Apply" to update the simulation.

)}
); } export default GeometryTools;