From 5eac830c5b379957ac91f8c357dc5ff6bb472ed3 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Sat, 10 Aug 2024 23:09:39 -0300 Subject: [PATCH] fix(splineSegROI): fix for CCW splines --- .../splineContourSegmentationTools/index.ts | 14 ++++++- .../src/tools/annotation/SplineROITool.ts | 3 +- .../tools/src/tools/base/ContourBaseTool.ts | 4 ++ .../contours/updateContourPolyline.ts | 40 ++++++++++--------- 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/packages/tools/examples/splineContourSegmentationTools/index.ts b/packages/tools/examples/splineContourSegmentationTools/index.ts index c09376410b..4c7b43c998 100644 --- a/packages/tools/examples/splineContourSegmentationTools/index.ts +++ b/packages/tools/examples/splineContourSegmentationTools/index.ts @@ -86,7 +86,19 @@ createInfoSection(content, { ordered: true }) 'Notice that each segment index has a different color assigned to it' ) .addInstruction('Change the style for the segmentation') - .addInstruction('Confirm the style is applied properly'); + .addInstruction('Confirm the style is applied properly') + .addInstruction('You can draw overlapping splines to:') + .openNestedSection() + .addInstruction( + 'Merge both splines when the first point of the second spline is inside the first spline' + ) + .addInstruction( + 'Subctract the second spline from the first spline when the first point of the second spline is outside the first spline' + ) + .closeNestedSection() + .addInstruction( + 'The resulting contour is converted to freehand ROI when splines are merged/subtracted' + ); function updateInputsForCurrentSegmentation() { // We can use any toolGroupId because they are all configured in the same way diff --git a/packages/tools/src/tools/annotation/SplineROITool.ts b/packages/tools/src/tools/annotation/SplineROITool.ts index 57dfbb33f4..9243de966e 100644 --- a/packages/tools/src/tools/annotation/SplineROITool.ts +++ b/packages/tools/src/tools/annotation/SplineROITool.ts @@ -720,7 +720,8 @@ class SplineROITool extends ContourSegmentationBaseTool { closed: data.contour.closed, targetWindingDirection: ContourWindingDirection.Clockwise, }, - viewport + viewport, + { updateWindingDirection: data.contour.closed } ); }); diff --git a/packages/tools/src/tools/base/ContourBaseTool.ts b/packages/tools/src/tools/base/ContourBaseTool.ts index a7de8aac24..5752022b77 100644 --- a/packages/tools/src/tools/base/ContourBaseTool.ts +++ b/packages/tools/src/tools/base/ContourBaseTool.ts @@ -220,6 +220,9 @@ abstract class ContourBaseTool extends AnnotationTool { transforms: { canvasToWorld: (point: Types.Point2) => Types.Point3; worldToCanvas: (point: Types.Point3) => Types.Point2; + }, + options?: { + updateWindingDirection?: boolean; } ) { const decimateConfig = this.configuration?.decimate || {}; @@ -229,6 +232,7 @@ abstract class ContourBaseTool extends AnnotationTool { enabled: !!decimateConfig.enabled, epsilon: decimateConfig.epsilon, }, + updateWindingDirection: options?.updateWindingDirection, }); } diff --git a/packages/tools/src/utilities/contours/updateContourPolyline.ts b/packages/tools/src/utilities/contours/updateContourPolyline.ts index ddb6d1a148..e66fbe8b0d 100644 --- a/packages/tools/src/utilities/contours/updateContourPolyline.ts +++ b/packages/tools/src/utilities/contours/updateContourPolyline.ts @@ -33,6 +33,7 @@ export default function updateContourPolyline( worldToCanvas: (point: Types.Point3) => Types.Point2; }, options?: { + updateWindingDirection?: boolean; decimate?: { enabled?: boolean; epsilon?: number; @@ -55,8 +56,7 @@ export default function updateContourPolyline( let { closed } = polylineData; const numPoints = polyline.length; const polylineWorldPoints = new Array(numPoints); - const currentPolylineWindingDirection = - math.polyline.getWindingDirection(polyline); + let windingDirection = math.polyline.getWindingDirection(polyline); const parentAnnotation = getParentAnnotation(annotation) as ContourAnnotation; if (closed === undefined) { @@ -75,28 +75,32 @@ export default function updateContourPolyline( closed = currentClosedState; } - // It must be in the opposite direction if it is a child annotation (hole) - let windingDirection = parentAnnotation - ? parentAnnotation.data.contour.windingDirection * -1 - : targetWindingDirection; + if (options?.updateWindingDirection !== false) { + // It must be in the opposite direction if it is a child annotation (hole) + let updatedWindingDirection = parentAnnotation + ? parentAnnotation.data.contour.windingDirection * -1 + : targetWindingDirection; - if (windingDirection === undefined) { - windingDirection = currentPolylineWindingDirection; - } + if (updatedWindingDirection === undefined) { + updatedWindingDirection = windingDirection; + } - if (windingDirection !== currentPolylineWindingDirection) { - polyline.reverse(); - } + if (updatedWindingDirection !== windingDirection) { + polyline.reverse(); + } - const handlePoints = data.handles.points.map((p) => worldToCanvas(p)); + const handlePoints = data.handles.points.map((p) => worldToCanvas(p)); - if (handlePoints.length > 2) { - const currentHandlesWindingDirection = - math.polyline.getWindingDirection(handlePoints); + if (handlePoints.length > 2) { + const currentHandlesWindingDirection = + math.polyline.getWindingDirection(handlePoints); - if (currentHandlesWindingDirection !== windingDirection) { - data.handles.points.reverse(); + if (currentHandlesWindingDirection !== updatedWindingDirection) { + data.handles.points.reverse(); + } } + + windingDirection = updatedWindingDirection; } for (let i = 0; i < numPoints; i++) {