[Skeletonization+ SOFA] Liver resection simulation: segment mapping + candidate-cut analysis - #3
Merged
Merged
Conversation
… file that has the needed data
… file that has the needed data
Co-authored-by: erik pernod <erik.pernod@gmail.com>
…nfinyTech3D/SofaSkeletonPlugin into dev_kamelia_new_comp_couinaud_segments addition of scene files
…p_couinaud_segments
epernod
approved these changes
Aug 25, 2026
epernod
left a comment
There was a problem hiding this comment.
ok works on my computer. Many things I would like to update but let's do that in next PR
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1. Description
Adds a full liver-resection simulation pipeline on top of the existing skeletonization plugin:
This closes the loop from "we have a vessel skeleton" to "here's what a given cut would devascularize, shown on screen."
2. Features
a. New component logic and class diagram
Class diagram
Algorithm core (
SkeletonGraph, unchanged SOFA/CGAL-free design):assignSegmentLabels(rawNodeLabels): per-node segment ids, smoothed by majority vote over maximal tree branches so boundary noise doesn't fragment a branch across segments.simulateResection(cutNodeIds): BFS reachability from the root over the raw connectivity graph (m_adjacency), skipping every cut node. Returns every node no longer reachable :Affected(C) = C ∪ (V \ Perfused(C)). Deliberately distinct from a plainsubtree()walk : a collateral/loop edge elsewhere in the graph can keep a node reachable even after its "obvious" path is severed.affectedSegments(affectedNodeIds): distinct segments touched by an affected-node set.SkeletonSegmentMapper(newDataEngine): links aSkeletonReaderplus aMultiLinkof 8 segmentMeshLoaders. For each skeleton node, tests point-in-mesh (CGAL::Side_of_triangle_mesh) against every segment mesh, falls back to nearest surface if none contain it, then hands the raw labels toassignSegmentLabels().SkeletonResectionSimulator(newDataEngine): links aSkeletonSegmentMapperplus, optionally, the actual segmentOglModels (l_segmentVisualModels). Key pieces:-
cutNodeIds/activationDelayinputs : the candidate cut, and how many seconds of sim time before the visual result actually shows.-
updateSegmentColors(): pollsgetContext()->getTime()everydraw()call (no event listener needed) to decide perfused vs. affected color, then pushes it two ways: as asegmentColorsoutputData, and directly into each linkedOglModelviafindData("material"): reading the object's current material string, patching just theDiffuse <flag> r g b atokens, and writing it back. This round-trip avoids hard-coding theMaterialstruct's exact serialization format.-
draw()renders the skeleton itself as colored points (green/red/black), same time-gating.b. SOFA scene structure
flowchart TB subgraph MeshNode["Node: Mesh"] loader["MeshOBJLoader loader"] skel["MeshSkeletonization skel"] ogl["OglModel (vein mesh)"] reader["SkeletonReader reader"] end subgraph SegNode["Node: LiverSegments"] segLoaders["MeshOBJLoader x8 segII..segVIII"] oglSegs["OglModel x8 oglII..oglVIII"] mapper["SkeletonSegmentMapper segmentMapper"] sim["SkeletonResectionSimulator resectionSim<br/>cutNodeIds, activationDelay"] end loader --> skel loader --> ogl loader -->|inputVertices| reader skel -->|writes skeleton.txt| readerr reader -->|skeletonReader link| mapper segLoaders -->|segmentMeshes link| mapper mapper -->|segmentMapper link| sim sim -->|"segmentVisualModels link:<br/>findData(material).read(...) every draw()"| oglSegsScene-level requirements this PR depends on:
<RequiredPlugin pluginName="MeshSkeletonizationPlugin"/>, andshowBehaviorModelsinVisualStyle'sdisplayFlags(otherwisedraw()never fires).3. Results
Verified against Patient 08's venous skeleton (986 nodes, root at node 574):
Confirmed single-segment resection points, one per segment:
(941,942)rescues 123)The segment II case is the concrete evidence that the reachability formula (not plain subtree) is load-bearing here, a real collateral in the data changes the answer by two orders of magnitude.
Skeleton results
Segment results