# Interactive segmentation and the 3D view MITK-style manual and semi-automatic segmentation, entirely in Rust and CPU-side, plus a 3D surface view. Neural-network auto-segmentation is [auto-segmentation.md](auto-segmentation.md), the patient outline [body-contour.md](body-contour.md) - both land as the editable masks described here - and combining structures is [structure-algebra.md](structure-algebra.md). Drawing and editing an RT structure *as contours* - polygon, spline, freehand, nudge, interpolation and the tidying tools - is [contours.md](contours.md); the two representations meet by the rule stated there. ## Segmentation masks A segmentation is a per-voxel label mask (one byte per voxel, same index order as the volume), with a name, display color, visibility flag, voxel count / volume readout and a per-stroke undo journal. Masks are grouped into **segmentation series** - what a DICOM SEG file is and exports as. A series lives in the study, not the view state, and names the image series it is drawn on, so painted work survives a series switch, travels with tree copy/move, and can be re-pointed at a different image series later. Its masks keep the lattice they were made on and are resampled onto the displayed volume when their own image series is shown; a series belonging to another image series stays intact and simply reports that it is not editable here. The sidebar *Segmentations* section shows the series as tree nodes and, below the active one, its segments: visibility, color, active selection, volume in cm³, per-stroke undo (**Ctrl+Z**), delete, and conversion to RTSTRUCT (**→RS**, below). *All* / *None* tick every segment or none, Shift-click extends a range, and the tick doubles as the selection for the actions beneath - copy, move, remove, and *💾* to export just those segments as a DICOM SEG file. See [viewer.md](viewer.md#structures-and-segmentations-in-the-tree). ## Tools The toolbar's **✏ Draw structure** button unfolds the drawing tools as one row of glyphs, followed by the options of the tool in hand and the segmentation or structure being drawn into (with a **+** to start another). A tool takes over the left mouse button in the MPR views: * **🎨 Paint / ⊖ Erase** - a spherical, spacing-aware **3D brush** (radius in mm, set on the toolbar, `Shift+wheel` or `[` `]`) paints in any of the three views; a **3D** toggle switches to a flat 2D circle confined to the displayed slice. Strokes are swept as capsules between pointer samples, so fast drags stay gap-free; `Alt` temporarily erases while painting. * **✨ Grow** - interactive **organ-wise** segmentation by geodesic fast marching, not a plain threshold: a Dijkstra front expands from the seed, the cost of each step rising exponentially with the voxel's intensity deviation from robust seed statistics (median/MAD of the local neighborhood) **and** with the intensity jump of the crossing itself - organ boundaries, fat planes and edges act as barriers, so the organ under the cursor is suggested first instead of all similar-intensity tissue. Press to seed, drag up/down to extend/shrink the geodesic reach with a live yellow preview; the front expands *incrementally* (drag up continues the same priority queue, drag down truncates the accepted prefix), never recomputing from scratch. Release commits - enclosed holes (vessels, calcifications) are filled slice-wise so the organ comes out solid - and `Esc` cancels. An *inside* box under the tool names a **limiting structure**: the front may not leave it, whatever the grey levels do. That is a different kind of statement from the geodesic cost, which is why it is a hard stop rather than an expensive step - bone inside the body, contrast inside the liver, a target inside a box made for the purpose with *Insert structure*, which is the limiting box by another name. A seed placed outside the limit says so and grows without it, because a limit that yields a single voxel reads as a broken tool. | Input (tool active) | Action | |---|---| | Left drag | Paint / erase - or, with ✨, seed and drag ↑↓ to grow/shrink | | Alt | Erase while painting | | Shift + wheel, `[`, `]` | Brush radius | | Ctrl + Z | Undo the last stroke | | Esc | Cancel the running region grow | Masks appear instantly in **all three MPR views** (crisp nearest-neighbor colorwash over the grayscale). ## The 3D structure view The **3D A / 3D B** toolbar buttons open a floating window with a 3D surface rendering of the active structure set **and** all segmentation masks. Surfaces are built on a background thread - scanline rasterization of contours into a binary volume, a surface-nets mesher, Laplacian smoothing, area-weighted vertex normals, `rayon`-parallel per ROI - and drawn in the display colors with headlight shading; EXTERNAL/body ROIs are translucent so internal anatomy stays visible. The colours are read from the structure set and the segmentation series **at draw time**, not baked into the meshes: changing a structure's or a segment's colour in the tree recolours the scene on the next frame - in the window and in a row's 3D pane alike - and re-meshes nothing, and the meshes cached per phase of a 4D group come back in whatever colour the set has now. Every mask edit re-meshes that segmentation in the background (bounding-box-cropped surface nets, automatic striding for huge masks), so the 3D surface follows the brush in essentially real time; meshes are cached per structure set, so reopening the window is instant. Drag rotates, the wheel zooms, middle-drag pans, and a slider sets global opacity; the same four buttons the 3D pane carries do it without a mouse to spare - **➕ / ➖** zoom a step, **✋** turns a left drag into a move, and **⟲** restores the default camera. ## Editing a segment as a whole The voxel tools paint; the Structure editor's *Edit* section acts on the selected segment as one object - tidy it (keep the largest piece, fill the holes), grow or shrink it by millimetres, empty it, delete it, or convert it to an RT structure and carry on with the contour tools. It is the same section the editor shows for a structure, with the mask's own verbs in it: [contours.md](contours.md#editing-a-segmentation). ## Mask → RTSTRUCT (→RS) The **→RS** button converts a mask to RTSTRUCT closed planar contours: per axial slice, the outline of the voxels themselves, traced along their edges, with collinear points merged and the points mapped to patient coordinates. The outline encloses exactly the voxels, so the ROI's planimetric volume is the mask's voxel volume, and rasterizing it gives the same mask back. This replaced a marching-squares outline, which looks smoother but cuts every corner. That costs an eighth of a voxel per outer corner and half of a lone voxel. A large organ does not notice, but a target carried onto a 1.2 mm lattice as a cloud of single voxels lost a tenth of its planimetric volume that way. Voxels that touch only at a corner come out as separate contours. Its surface-based volume is smaller than the voxels by the end caps - see [volumes.md](volumes.md). The new ROI joins the active structure set - or a new in-memory set ("Segmentations") when the study has no RTSTRUCT - and renders like any ROI, participates in the 3D view, and rides the existing DICOM export ([export-and-tools.md](export-and-tools.md)). ## Verification `tests/segmentation.rs` covers the invariants: the brush sphere respects anisotropic spacing, 2D strokes stay slice-confined, undo restores voxel counts exactly, the geodesic grow respects organ boundaries under a 20× reach increase (no-leak test), hole filling, `mask → RTSTRUCT` contour round-trips, and the mask/contour → mesh pipeline on an analytic sphere.