Skip to content

Conversation

@olhalit
Copy link
Contributor

@olhalit olhalit commented Nov 7, 2025

Context

When adding or editing a contour segmentation using any of the SplineROI based tools (e.g., CatmullRomSplineROI, LinearSplineROI, BSplineROI), an infinite loop of SEGMENTATION_DATA_MODIFIED events was triggered.

This issue was caused within the renderAnnotationInstance method of the SplineROITool. During every render pass, the tool would recalculate the spline's polyline and call updateContourPolyline. This process unconditionally marked the annotation as invalidated, which in turn triggered a new SEGMENTATION_DATA_MODIFIED event and a subsequent re-render, leading to a feedback loop.

Changes & Results

Modified SplineROITool.ts:

  • A new private method, _arePolylinesEqual, has been added. This method performs a comparison of two polyline point arrays in canvas space to check if they are geometrically identical within a small tolerance.
  • The logic in renderAnnotationInstance has been updated. It now uses _arePolylinesEqual to compare the newly generated spline polyline with the one currently stored in the annotation's data.
  • The call to updateContourPolyline is now conditional and only executes if the polyline's geometry has actually changed.

Effect of the change:

  • Before: Any render pass involving a SplineROI annotation would unconditionally trigger a data modification event (SEGMENTATION_DATA_MODIFIED), leading to an infinite re-render loop.
  • After: The modification event is now correctly triggered only when the spline's control points are moved or its geometry is altered. The infinite loop is resolved, and the application remains responsive during spline manipulation.

Testing

This fix was tested locally by monitoring the SEGMENTATION_DATA_MODIFIED event to ensure it no longer fires in a loop.

Checklist

PR

  • My Pull Request title is descriptive, accurate and follows the
    semantic-release format and guidelines.

Code

  • My code has been well-documented (function documentation, inline comments,
    etc.)

Public Documentation Updates

  • [] The documentation page has been updated as necessary for any public API
    additions or removals.

Tested Environment

  • "OS: Ubuntu 24.04.2
  • "Node version: v20.19.5
  • "Browser: Chrome 139.0.7258.66

@olhalit
Copy link
Contributor Author

olhalit commented Nov 17, 2025

Hi team! 👋
Just wanted to kindly follow up on this PR.

The fix resolves an infinite SEGMENTATION_DATA_MODIFIED loop in SplineROI-based tools and significantly improves app stability during contour editing.

Please let me know if there is anything I can improve or adjust to help move the review forward.

Thanks a lot for your time and for maintaining this great project!

@sedghi
Copy link
Member

sedghi commented Nov 17, 2025

Thanks for the PR, we will look soon

@sedghi sedghi requested a review from wayfarer3130 November 17, 2025 14:28
};

// Compare polylines in canvas space with a small tolerance
private _arePolylinesEqual = (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a utility in the tools module

if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (
Math.abs(a[i][0] - b[i][0]) > eps ||
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use isEqual(a[i],b[i])?

);
// Compare with existing contour polyline (converted to canvas)
const existingPolylineCanvas =
annotation.data.contour.polyline?.map(worldToCanvas) || [];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need the || [] at the end - the compare function works without it.

{
points: splinePolylineCanvas,
closed: isClosed,
targetWindingDirection: ContourWindingDirection.Clockwise,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that the target winding direction will update/smooth the contour, resulting in infinite loop anyways?

!this._arePolylinesEqual(existingPolylineCanvas, splinePolylineCanvas)
) {
const isClosed = data.contour.closed;
this.updateContourPolyline(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider moving the check for is same into the updateContourPolyline itself, perhaps under a flag.
Also, the isSamePolyline with the changes I suggested works fine for either 2d or 3d point data, so you might not need to convert the old one to canvas points if the new one is being converted anyways, and might have new changes inside updateContourPolyline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants