fix(contour): fix intersect and subtract operations when one segment is fully inside another#2739
Conversation
…t intersection The intersectPolylinesSets function returned empty results when one polyline was entirely contained inside the other (no edge crossings). - When A is fully inside B (isContourHole), the intersection is A - When B is fully inside A, the intersection is B
…lly enclosed in minuend Subtracting a shape that was fully enclosed by another (e.g. small sphere inside big sphere) had no effect and returned the original shape unchanged. The subtract operation now correctly handles all spatial relationships and preserves holes through chained operations. Changes: - polylineInfoTypes: add optional holePolylines field to both world and canvas polyline info types - polylineSubtract: handle B-inside-A by cutting a hole, A-inside-B by removing entirely, and skip adding a hole when B is already inside an existing hole (no-op). Propagate existing holes through edge-crossing subtractions - logicalOperators: load hole polylines from child annotations when reading a segment, propagate them through canvas/world space conversions, and carry them back to world space in the operation result - addPolylinesToSegmentation: save hole polylines as proper child annotations with correct winding direction
Added an isTargetInsideSource flag to checkIntersection so that both containment directions (source inside target, target inside source) are detected in one place, rather than scattered containsPoints calls across each caller.
| !lineSegmentsIntersect && | ||
| math.polyline.containsPoints(targetPolyline, sourcePolyline); | ||
|
|
||
| // Target's points are all inside source -> target is a hole inside source |
There was a problem hiding this comment.
The comment is a little confusing to me. In this case target is not necessarily a hold inside source right? Would a better comment be // Target is fully contained inside source (no edge crossings)?
There was a problem hiding this comment.
Yes, comment updated.
|
|
||
| const DEFAULT_CONTOUR_SEG_TOOLNAME = 'PlanarFreehandContourSegmentationTool'; | ||
|
|
||
| function createContourSegmentationAnnotation( |
There was a problem hiding this comment.
Since addPolylinesToSegmentation is the main, exported function, let's move this one below it. It makes for a better diff in the PR too.
wayfarer3130
left a comment
There was a problem hiding this comment.
At a minimum, with the current performance, a progress bar is going to be required since I had assumed it had crashed on some examples I ran, but figured out if I left it a minute or two it eventually completed.
The performance generally need to be improved to make this workable.
There are also various bugs identified by running /review on claude
The explanation of how to use it in the UI would also really help - it was not obvious that it didn't remove the old annotation, so I really didn't know exactly what it was doing. I'd suggest have a remove annotation button.
Probably doing it only on the current view, or every slice would also be helpful.
|
I had it just disappear the new annotation that was created - I had a sample that had multiple holes/overlaps in various ways and it just went away after clicking merge - took over a minute to go away. |
|
Claude review: Code Review — cornerstone3D PR #2739Title: fix(contour): fix intersect and subtract operations when one segment is fully inside another OverviewFixes OHIF/Viewers#5648:
The diagnosis and overall direction look correct. A few concrete issues below. Issues & Suggestions1. Hole winding direction is not enforced (likely renders incorrectly)In updateContourPolyline(
holeAnnotation,
{ points: holePolylineCanvas, closed: true }, // no targetWindingDirection
viewport
);Compare with the established 2. Existing holes in the minuend are ignored when polygons clipIn 3.
|
|
I suspect bug #1 above it the cause of disappearing segments, but it is so slow I can't really be sure. Suggest trying this with clipper2-ts - I asked claude to try that, will report on how it works. |
|
I would also look as part of this at fixing the general add/remove sections of existing segments - I could get very odd behaviour as to whether it xored, merged, subtracted or did something else when I drew another overlapping item - I hadn't known exactly what to expect on the previous operation, and it took so long that I thought it was getting mixed up with the operation after I hit the merge. |
…t' into fix/contour-combine-empty-segment

Context
OHIF_REF: fix/5648-contour-combine
This PR fixes an issue in ohif OHIF/Viewers#5648
When performing contour logical operations between two segments where one segment is fully contained inside the other — meaning their edges do not cross — both the intersect and subtract operations produced incorrect results:
Intersect
When intersecting two segments where one segment is fully inside the other, the operation returned an empty segment instead of returning the smaller contained segment.
Subtract
When subtracting two contour segments where one segment is fully inside the other, for example subtracting a small sphere from a large sphere, the operation returned the larger sphere unchanged instead of producing the larger sphere with an internal hole.
Changes & Results
Intersection logic (
polylineIntersect.ts):Subtract logic (
polylineSubtract.ts):Pipeline (
logicalOperators.ts,addPolylinesToSegmentation.ts,polylineInfoTypes.ts):holePolylinesfield toPolylineInfoWorldandPolylineInfoCanvasTesting
Perform the following operations:
a. Large box intersecting ANY of the other (non large box) segments produces the inner geometry
b. Big sphere intersect the small sphere produces the small sphere.
c. Big sphere subtract the small sphere produces the big sphere with a hole the size of the small sphere.
Or you can do manual testing by drawing segments and perform Combine Contour operations on them (Intersect and subtract)
Checklist
PR
semantic-release format and guidelines.
Code
etc.)
Public Documentation Updates
additions or removals.
Tested Environment