Skip to content

feat: add results for different viewports on the fusion view for rectangle/circle ROI#2557

Merged
wayfarer3130 merged 28 commits into
mainfrom
feat/add-multiple-results
Jul 15, 2026
Merged

feat: add results for different viewports on the fusion view for rectangle/circle ROI#2557
wayfarer3130 merged 28 commits into
mainfrom
feat/add-multiple-results

Conversation

@wayfarer3130

@wayfarer3130 wayfarer3130 commented Jan 13, 2026

Copy link
Copy Markdown
Collaborator

Context

This PR adds display of measurements from multiple volumes onto a fused display.

Note this currently only works for acquisition orientation on volume viewports because the restrictions for display of the annotations are such that they don't work on the secondary viewports for non-acquisition. That is being fixed as a separate PR/issue.

Changes & Results

Add a generic method for get text lines
Add support for displaying stats from different volumes or multiple volumes
Add a getMeasurementTargets to allow getting a list of volumes to use for this.

Testing

Run tmtv mode before/after the change
Create a rectangle roi on each of the three orientations.
In the old version, only one measurement from both volumes would appear, whereas now both of them appear.

Note: Area measurement is unchanged - it is incorrect on both old/new.

Try again with circular measurement.

For annotations drawn on the center row, you may need to navigate the top row some as the distance between annotations and thus the distance that a single annotation might be shown changes depending on the differing slice thickness.

Annotations must have been displayed in both top and second row at some point in order to allow displaying measurements in the bottom row.

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:
  • [] "Node version:
  • [] "Browser:

Summary by CodeRabbit

  • New Features
    • Added dynamic TMTV layouts, including fusion and oblique arrangements, with layout-aware synchronization.
    • Added modality- and volume-based measurement targeting for multi-volume ROI statistics.
    • ROI measurements now display statistics across multiple fused volumes.
    • Added Generic Dynamic Volume and Generic Multi-Volume Measurements examples.
  • Bug Fixes
    • Improved volume-specific image data resolution and annotation reference handling.
    • Improved ROI statistics caching and boundary detection.
  • Documentation
    • Added guidance for configuring measurement targets and multi-volume statistics.

@wayfarer3130 wayfarer3130 requested a review from sedghi January 13, 2026 21:27
Comment thread packages/core/src/RenderingEngine/BaseVolumeViewport.ts
Comment thread packages/core/src/RenderingEngine/Viewport.ts Outdated
Comment thread packages/tools/src/tools/annotation/BidirectionalTool.ts
Comment thread packages/tools/src/utilities/planar/filterAnnotationsWithinSlice.ts
Comment thread utils/ExampleRunner/example-info.json Outdated
"name": "PET-CT Fusion + MIPLayout",
"description": "PT-CT fusion layout with Crosshairs, and synchronized cameras, CT W/L and PET threshold"
},
"tmtv": {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

don't we already have two ptct one? maybe do it in those examples? and we don't add one here?

Comment thread packages/core/src/RenderingEngine/BaseVolumeViewport.ts
Comment thread packages/core/src/RenderingEngine/VolumeViewport.ts
Comment thread packages/tools/src/tools/base/BaseTool.ts
…ults

# Conflicts:
#	packages/tools/src/tools/annotation/CircleROITool.ts
#	packages/tools/src/tools/annotation/RectangleROITool.ts
#	packages/tools/src/utilities/index.ts
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds multi-target ROI statistics and filtering, updates volume-aware viewport references and image-data lookup, refactors the TMTV demo around dynamic layouts, and adds dynamic-volume and multi-volume measurement examples with related documentation and tests.

Changes

Multi-target measurement statistics

Layer / File(s) Summary
Measurement and cached-stat contracts
packages/tools/src/types/*, packages/tools/src/tools/base/*, packages/tools/src/index.ts, packages/tools/src/tools/index.ts
Adds typed measurement-target filters, reusable modality/identifier filters, and unified cached-stat models across annotation types.
Measurement target resolution
packages/tools/src/tools/base/BaseTool.ts, packages/tools/src/tools/base/AnnotationTool.ts
Resolves display-set candidates and metadata, applies target filters, reuses cached target ids, and retrieves volume-specific image data and PET scaling.
Multi-target ROI statistics
packages/tools/src/tools/annotation/{CircleROI,RectangleROI}Tool.ts, packages/tools/src/utilities/defaultGetTextLines.ts
Computes and renders ROI statistics across multiple targets, preserves unavailable target stats, aggregates outside-image state, and centralizes metric formatting.
Measurement examples and validation
packages/tools/examples/petCt/*, packages/docs/docs/concepts/cornerstone-tools/annotation/*, packages/tools/test/*
Updates PET/CT filtering, documents measurement targets, and tests fused-volume statistics and related regressions.
Annotation typing cleanup
packages/tools/src/tools/annotation/{BidirectionalTool,DragProbeTool}.ts, packages/tools/src/tools/segmentation/CircleROIStartEndThresholdTool.ts
Removes unused values and redundant assertions while tightening local annotation types.

Viewport reference and image-data behavior

Layer / File(s) Summary
Volume-aware viewport APIs
packages/core/src/RenderingEngine/{BaseVolumeViewport,VolumeViewport}.ts, packages/core/src/RenderingEngine/GenericViewport/Planar/*, packages/core/src/types/IViewport.ts
Updates frame-of-reference volume-id semantics and supports explicit volume-id lookup for planar image data and volume bindings.
Viewport behavior validation
packages/core/test/{planarViewReference,planarViewportState}.jest.js
Verifies explicit frame-of-reference behavior, bound-volume lookup, unknown-volume handling, and source-image fallback.

Dynamic TMTV layouts

Layer / File(s) Summary
TMTV controls and tool groups
packages/core/examples/tmtv/index.ts
Adds oblique fusion support, layout selection, modality-specific ROI tools, and deferred viewport membership.
TMTV layout application
packages/core/examples/tmtv/index.ts
Rebuilds viewport arrangements and applies layout-specific volumes, segmentations, synchronization, and rendering.
TMTV synchronization lifecycle
packages/core/examples/tmtv/index.ts
Recreates camera and VOI synchronizers and aligns cameras only for viewports in the active layout.

Additional examples and repository support

Layer / File(s) Summary
Dynamic volume example
packages/core/examples/genericDynamicVolume/index.ts
Adds orientation, cine playback, dimension-group controls, and event-driven updates for dynamic volumes.
Generic multi-volume measurements
packages/tools/examples/genericMultiVolumeMeasurements/index.ts
Adds CT-only, PT-only, and fused planar viewports with shared ROI measurement tools and presentation settings.
Example registry
utils/ExampleRunner/example-info.json
Registers the TMTV example.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant LayoutDropdown
  participant TMTV
  participant RenderingEngine
  participant SynchronizerManager
  User->>LayoutDropdown: choose default, fusion, or oblique
  LayoutDropdown->>TMTV: call applyLayout(layoutKey)
  TMTV->>RenderingEngine: rebuild viewport grid and bindings
  TMTV->>SynchronizerManager: destroy and recreate layout synchronizers
  TMTV->>RenderingEngine: set layout volumes and render
Loading

Possibly related PRs

Suggested reviewers: sedghi

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The template structure is present, but the checklist items are unchecked and the tested environment details are missing. Mark the PR, code, docs, and testing checklist items as complete, and fill in the OS, Node, and browser versions in Tested Environment.
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is descriptive, accurate, and uses semantic-release style to summarize the fused-view ROI measurement update.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-multiple-results

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/tools/src/tools/segmentation/CircleROIStartEndThresholdTool.ts (1)

191-230: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Drop null for labelmapUID. VolumeStats.statistics is unknown, so statistics: [] is fine; the remaining mismatch is labelmapUID: null vs labelmapUID?: string, which should be undefined or broadened in the type.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tools/src/tools/segmentation/CircleROIStartEndThresholdTool.ts`
around lines 191 - 230, In CircleROIStartEndThresholdTool, the annotation
object’s data.labelmapUID is using null even though the type is optional string,
so update the CircleROIStartEndThresholdAnnotation construction to omit
labelmapUID or set it to undefined instead. Check the annotation literal in the
create flow alongside cachedStats and statistics so the shape matches the
defined data type.
packages/tools/src/tools/annotation/RectangleROITool.ts (1)

618-659: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Seed cached stats for all measurement targets
getMeasurementTargets() only feeds text-line rendering here; cache initialization and recomputation still start from a single targetId. When it returns multiple IDs, the extra entries never get seeded in this path, so multi-target annotations stay incomplete until another viewport populates cachedStats.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tools/src/tools/annotation/RectangleROITool.ts` around lines 618 -
659, Seed and recompute cached stats for every entry returned by
getMeasurementTargets() in RectangleROITool, not just the single targetId.
Update the cache-initialization path and the invalidated branch so each
measurement target gets its own cachedStats entry and calls into
_calculateCachedStats or _throttledCalculateCachedStats as needed. Keep the
existing getTargetId/getMeasurementTargets split, but make the stats setup loop
over all targetIds so multi-target annotations are fully populated in this
rendering path.
packages/tools/src/tools/annotation/CircleROITool.ts (1)

697-733: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Seed all measurement targets before building text lines.
getMeasurementTargets() only reuses IDs already present in data.cachedStats, but this path seeds just targetId. If this viewport is the first one rendering the annotation, secondary volumes never get added to the cache, so _calculateCachedStats() never computes them and their text lines stay hidden until another viewport fills data.cachedStats. packages/tools/src/tools/annotation/CircleROITool.ts:697-733, 877-880

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tools/src/tools/annotation/CircleROITool.ts` around lines 697 - 733,
Seed cached stats for every measurement target before text generation, not just
the current targetId. In CircleROITool’s rendering path, make sure all IDs
returned by getMeasurementTargets()/derived from getTargetId are initialized in
data.cachedStats before _calculateCachedStats() and the measurement text lines
are built, so secondary volumes are not skipped when this viewport renders
first.
🧹 Nitpick comments (5)
packages/tools/src/types/AnnotationTypes.ts (1)

104-107: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider using Omit for a cleaner intersection type.

AnnotationDataStatsByTargetId = AnnotationData & { cachedStats: Record<string, CachedStats> } produces an intersection where cachedStats becomes (Record<string, CachedStats> | VolumeStats) & Record<string, CachedStats>, which includes a VolumeStats & Record<string, CachedStats> arm that is never intended. Using Omit avoids this:

-export type AnnotationDataStatsByTargetId = AnnotationData & {
-  cachedStats: Record<string, CachedStats>;
-};
+export type AnnotationDataStatsByTargetId = Omit<AnnotationData, 'cachedStats'> & {
+  cachedStats: Record<string, CachedStats>;
+};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tools/src/types/AnnotationTypes.ts` around lines 104 - 107, The
AnnotationDataStatsByTargetId type currently uses an intersection with
AnnotationData, which leaves an unintended cachedStats union arm in the
resulting type. Update the type alias in AnnotationTypes.ts to use Omit on
AnnotationData for cachedStats before adding cachedStats: Record<string,
CachedStats>, so the resulting shape is clean and avoids the extra VolumeStats
intersection.
packages/tools/src/tools/base/BaseTool.ts (1)

327-370: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant if (ref) check and missing return type annotation.

The if (ref) on line 361 is always true since if (!ref) { continue; } on line 358 already handles the falsy case. Also, getMeasurementTargets lacks an explicit return type annotation — the inferred type is (string | undefined)[] because getTargetId returns string | undefined, but callers use the result as string[].

♻️ Proposed cleanup
-  protected getMeasurementTargets(
+  protected getMeasurementTargets(
     viewport: Types.IViewport,
     data?: AnnotationData
-  ) {
+  ): string[] {
     const { showAllTargets } = this.configurationTyped;
     const actors = viewport.getActors();
     if (showAllTargets === false || !actors?.length) {
-      return [this.getTargetId(viewport, data)];
+      return [this.getTargetId(viewport, data) ?? ''];
     }
     const references = [];
     const keys = data?.cachedStats ? Object.keys(data.cachedStats) : [];
     for (const actor of actors) {
       const volumeId = actor.referencedId;
       if (!volumeId) {
         continue;
       }
       const volumeIdQuery = volumeId + '?';
       const ref = keys.find((key) => key.indexOf(volumeIdQuery) !== -1);
-      if (!ref) {
-        continue;
-      }
-      if (ref) {
-        references.push(ref);
-      }
+      if (ref) {
+        references.push(ref);
+      }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tools/src/tools/base/BaseTool.ts` around lines 327 - 370, Remove the
redundant `if (ref)` check in `BaseTool.getMeasurementTargets` since the falsy
case is already skipped by the preceding `continue`, and add an explicit return
type for `getMeasurementTargets` so it matches the expected string array shape
used by callers. Update the method signature in `BaseTool` to make the return
type explicit, and ensure the fallback from `getTargetId` is handled
consistently with the rest of the method’s returned references.
packages/tools/src/utilities/defaultGetTextLines.ts (1)

30-32: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Filter hardcodes mean check, making createGetTextLines non-generic.

The filter it?.mean !== undefined && it?.mean !== null assumes all metric sets include mean. If createGetTextLines is used with metrics that don't compute mean (e.g., a custom metrics array with only area and perimeter), valid entries would be excluded and no text lines would be generated.

Consider filtering based on whether any of the configured metrics' attributes are present, or at minimum document that mean is required for all stat entries.

♻️ Proposed generic filter
     const cachedVolumeStats = targetIds
       .map((id) => data.cachedStats[id])
-      .filter((it) => it?.mean !== undefined && it?.mean !== null);
+      .filter((it) =>
+        metrics.some((m) => it?.[m.attribute] !== undefined && it?.[m.attribute] !== null)
+      );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tools/src/utilities/defaultGetTextLines.ts` around lines 30 - 32,
The cached stats filter in createGetTextLines/defaultGetTextLines is hardcoded
to require mean, which breaks use with metric sets that don’t define that field.
Update the filter on cachedVolumeStats to check for the presence of the
configured metric attributes instead of assuming mean, using the existing
targetIds/data.cachedStats flow. If mean is intentionally required, make that
contract explicit in createGetTextLines documentation and type expectations so
callers know the constraint.
packages/tools/src/tools/annotation/CircleROITool.ts (2)

877-880: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

getMeasurementTargets called on every annotation, every render frame.

This resolves targets fresh per annotation per animation frame instead of once per renderAnnotation invocation (viewport doesn't change across the loop). If the resolver iterates viewport actors, this repeats that work annotations.length times per frame.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tools/src/tools/annotation/CircleROITool.ts` around lines 877 - 880,
`getMeasurementTargets` is being recomputed for each annotation inside the
`CircleROITool` render loop, which repeats the same viewport lookup work every
frame. Move the `this.getMeasurementTargets(viewport, data)` call out of the
per-annotation path in `renderAnnotation` so it is resolved once per invocation
and then reused when calling `this.configuration.getTextLines`, keeping the
behavior the same while avoiding repeated actor iteration.

1157-1165: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the shared modality metrics. CIRCLE_ROI_METRICS duplicates the Mean/Max/Min/Std Dev entries from AREA_METRICS; split those four lines into a shared constant and compose AREA_METRICS/CIRCLE_ROI_METRICS from it. AREA_METRICS itself can’t be reused directly because it also includes Area.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tools/src/tools/annotation/CircleROITool.ts` around lines 1157 -
1165, The CircleROITool metric definitions duplicate the shared Mean/Max/Min/Std
Dev entries already present in AREA_METRICS. Extract those four common metric
objects into a shared constant in CircleROITool, then build both AREA_METRICS
and CIRCLE_ROI_METRICS from that shared list while keeping AREA_METRICS’ Area
entry separate. Use the existing AREA_METRICS and CIRCLE_ROI_METRICS symbols to
locate the change and preserve createGetTextLines usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/examples/tmtv/index.ts`:
- Line 173: The toolbar dropdown options in the example setup include
CircleROITool.toolName twice, creating a duplicate entry. Update the
optionsValues list in the tmtv example to remove the repeated
CircleROITool.toolName reference and keep only the single existing occurrence so
the dropdown shows each tool once.

In `@packages/tools/src/tools/base/BaseTool.ts`:
- Line 350: Guard the `Object.keys(data?.cachedStats)` call in `BaseTool` so it
never runs on `undefined`; the current `cachedStats` access can throw for
annotations without computed stats. Update the logic that builds the keys list
to safely fall back to an empty object/array when `data`, `data.cachedStats`, or
the `cachedStats` field on `AnnotationData`/`CircleROIAnnotation` is missing,
and keep the behavior in the surrounding `showAllTargets` path unchanged.

---

Outside diff comments:
In `@packages/tools/src/tools/annotation/CircleROITool.ts`:
- Around line 697-733: Seed cached stats for every measurement target before
text generation, not just the current targetId. In CircleROITool’s rendering
path, make sure all IDs returned by getMeasurementTargets()/derived from
getTargetId are initialized in data.cachedStats before _calculateCachedStats()
and the measurement text lines are built, so secondary volumes are not skipped
when this viewport renders first.

In `@packages/tools/src/tools/annotation/RectangleROITool.ts`:
- Around line 618-659: Seed and recompute cached stats for every entry returned
by getMeasurementTargets() in RectangleROITool, not just the single targetId.
Update the cache-initialization path and the invalidated branch so each
measurement target gets its own cachedStats entry and calls into
_calculateCachedStats or _throttledCalculateCachedStats as needed. Keep the
existing getTargetId/getMeasurementTargets split, but make the stats setup loop
over all targetIds so multi-target annotations are fully populated in this
rendering path.

In `@packages/tools/src/tools/segmentation/CircleROIStartEndThresholdTool.ts`:
- Around line 191-230: In CircleROIStartEndThresholdTool, the annotation
object’s data.labelmapUID is using null even though the type is optional string,
so update the CircleROIStartEndThresholdAnnotation construction to omit
labelmapUID or set it to undefined instead. Check the annotation literal in the
create flow alongside cachedStats and statistics so the shape matches the
defined data type.

---

Nitpick comments:
In `@packages/tools/src/tools/annotation/CircleROITool.ts`:
- Around line 877-880: `getMeasurementTargets` is being recomputed for each
annotation inside the `CircleROITool` render loop, which repeats the same
viewport lookup work every frame. Move the `this.getMeasurementTargets(viewport,
data)` call out of the per-annotation path in `renderAnnotation` so it is
resolved once per invocation and then reused when calling
`this.configuration.getTextLines`, keeping the behavior the same while avoiding
repeated actor iteration.
- Around line 1157-1165: The CircleROITool metric definitions duplicate the
shared Mean/Max/Min/Std Dev entries already present in AREA_METRICS. Extract
those four common metric objects into a shared constant in CircleROITool, then
build both AREA_METRICS and CIRCLE_ROI_METRICS from that shared list while
keeping AREA_METRICS’ Area entry separate. Use the existing AREA_METRICS and
CIRCLE_ROI_METRICS symbols to locate the change and preserve createGetTextLines
usage.

In `@packages/tools/src/tools/base/BaseTool.ts`:
- Around line 327-370: Remove the redundant `if (ref)` check in
`BaseTool.getMeasurementTargets` since the falsy case is already skipped by the
preceding `continue`, and add an explicit return type for
`getMeasurementTargets` so it matches the expected string array shape used by
callers. Update the method signature in `BaseTool` to make the return type
explicit, and ensure the fallback from `getTargetId` is handled consistently
with the rest of the method’s returned references.

In `@packages/tools/src/types/AnnotationTypes.ts`:
- Around line 104-107: The AnnotationDataStatsByTargetId type currently uses an
intersection with AnnotationData, which leaves an unintended cachedStats union
arm in the resulting type. Update the type alias in AnnotationTypes.ts to use
Omit on AnnotationData for cachedStats before adding cachedStats: Record<string,
CachedStats>, so the resulting shape is clean and avoids the extra VolumeStats
intersection.

In `@packages/tools/src/utilities/defaultGetTextLines.ts`:
- Around line 30-32: The cached stats filter in
createGetTextLines/defaultGetTextLines is hardcoded to require mean, which
breaks use with metric sets that don’t define that field. Update the filter on
cachedVolumeStats to check for the presence of the configured metric attributes
instead of assuming mean, using the existing targetIds/data.cachedStats flow. If
mean is intentionally required, make that contract explicit in
createGetTextLines documentation and type expectations so callers know the
constraint.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1747fd9a-6487-44d3-b334-ef4e7ce1526c

📥 Commits

Reviewing files that changed from the base of the PR and between 523becb and 92c671b.

📒 Files selected for processing (17)
  • packages/core/examples/tmtv/index.ts
  • packages/core/src/RenderingEngine/BaseVolumeViewport.ts
  • packages/core/src/RenderingEngine/VolumeViewport.ts
  • packages/core/src/RenderingEngine/WebGLContextPool.ts
  • packages/tools/src/tools/annotation/BidirectionalTool.ts
  • packages/tools/src/tools/annotation/CircleROITool.ts
  • packages/tools/src/tools/annotation/DragProbeTool.ts
  • packages/tools/src/tools/annotation/RectangleROITool.ts
  • packages/tools/src/tools/base/AnnotationTool.ts
  • packages/tools/src/tools/base/BaseTool.ts
  • packages/tools/src/tools/segmentation/CircleROIStartEndThresholdTool.ts
  • packages/tools/src/types/AnnotationTypes.ts
  • packages/tools/src/types/ToolSpecificAnnotationTypes.ts
  • packages/tools/src/utilities/defaultGetTextLines.ts
  • packages/tools/src/utilities/index.ts
  • packages/tools/src/utilities/planar/filterAnnotationsWithinSlice.ts
  • utils/ExampleRunner/example-info.json

Comment thread packages/core/examples/tmtv/index.ts Outdated
Comment thread packages/tools/src/tools/base/BaseTool.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewport.ts`:
- Around line 1224-1240: Update GenericViewport.getImageData so an explicit
volumeId request never falls back to the active binding: resolve the volume
through findDataIdByVolumeId and getBinding, return that binding’s image data
when available, and otherwise return undefined. Preserve the current-binding
fallback only when volumeId is omitted.

In
`@packages/docs/docs/concepts/cornerstone-tools/annotation/measurementTargets.md`:
- Around line 252-255: Update the note in measurementTargets.md to state that
multi-target selection is limited to acquisition-oriented volume viewports, and
that secondary or non-acquisition viewports may not display measurements,
alongside the existing on-screen and stack-fusion limitations.
- Around line 214-223: Clarify the documentation so the cachedStats reuse rule
explicitly applies only to volume candidates. State that volume target IDs use
the `volumeId:<volumeId>?sliceIndex=...&viewPlaneNormal=...` format, and
separately document that stack candidates use imageId-derived target IDs as
described earlier.

In `@packages/tools/src/tools/base/BaseTool.ts`:
- Around line 556-590: Set MeasurementTargetCandidate.index to the matching
display-set position rather than the actor-loop index. In the actor processing
logic, derive the index from the displaySets entry associated with referencedId
(or otherwise preserve its array position), and use that value when constructing
candidates; do not assign the loop variable index from viewport.getActors().

In `@packages/tools/test/CircleROI_test.js`:
- Around line 415-429: Add a rejection handler to the Promise chain in the
test’s volume-loading block by chaining .catch(done.fail) after the existing
.then callback. Keep the synchronous try/catch, and ensure asynchronous failures
from either createAndCacheVolume promise are passed to done.fail.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: eaa5f78e-57d6-411e-916d-feb10ad49295

📥 Commits

Reviewing files that changed from the base of the PR and between 92c671b and 1518778.

📒 Files selected for processing (11)
  • packages/core/examples/tmtv/index.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewport.ts
  • packages/docs/docs/concepts/cornerstone-tools/annotation/measurementTargets.md
  • packages/docs/sidebars.js
  • packages/tools/examples/petCt/index.ts
  • packages/tools/src/tools/annotation/CircleROITool.ts
  • packages/tools/src/tools/annotation/RectangleROITool.ts
  • packages/tools/src/tools/base/BaseTool.ts
  • packages/tools/src/types/IBaseTool.ts
  • packages/tools/src/types/index.ts
  • packages/tools/test/CircleROI_test.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/tools/src/tools/annotation/CircleROITool.ts
  • packages/tools/src/tools/annotation/RectangleROITool.ts

Comment thread packages/docs/docs/concepts/cornerstone-tools/annotation/measurementTargets.md Outdated
Comment thread packages/tools/src/tools/base/BaseTool.ts
Comment thread packages/tools/test/CircleROI_test.js
@wayfarer3130 wayfarer3130 requested a review from sedghi July 11, 2026 20:36

@sedghi sedghi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

d

@sedghi

sedghi commented Jul 12, 2026

Copy link
Copy Markdown
Member

The target-selection API has been reworked per the review feedback - see #2802 for the alternative implementation (targets this branch, so its diff is just the architecture change). targetsFilter is now a { key, options } spec resolved through the annotationTargetFilter metadata providers, and the static targetFilters helpers are removed from the tool classes.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/tools/src/tools/base/BaseTool.ts (1)

615-630: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard against undefined cachedStats in ensureCachedStatsTargets.

data.cachedStats is optional on AnnotationData, but line 621 casts it directly and line 623 indexes into it without a null check. If an annotation is created or hydrated without cachedStats (e.g., from external persistence), this throws a TypeError. The similar Object.keys(data?.cachedStats) issue was previously fixed in getMeasurementTargets; this method needs the same guard.

🛡️ Proposed fix
   protected ensureCachedStatsTargets(
     data: AnnotationData,
     targetIds: string[],
     needsUpdate?: (stats) => boolean
   ): boolean {
     let missing = false;
+    if (!data.cachedStats) {
+      data.cachedStats = {};
+    }
     const cachedStats = data.cachedStats as Record<string, unknown>;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tools/src/tools/base/BaseTool.ts` around lines 615 - 630, Update
ensureCachedStatsTargets in BaseTool to safely handle missing or undefined
data.cachedStats before indexing target IDs. Initialize or otherwise provide an
empty mutable stats record when cachedStats is absent, then preserve the
existing needsUpdate checks and missing-target behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/skills/cornerstone3d-dev/SKILL.md:
- Around line 67-71: Update the cross-session project memory guidance in
SKILL.md to use a generic, configured memory location rather than the
user-specific path. Remove the embedded personal project key and all
transcript/session IDs from the Evidence section, while preserving the
instruction to read and persist durable project memory.
- Around line 23-31: Update the type-refresh and typecheck commands in the build
workflow so they are cwd-independent and execute from the repository root
context, avoiding a persistent `cd packages/core`. Use subshells or equivalent
`pnpm --dir` invocations for `tsc` and the tools fallback, while preserving the
existing projects and declaration-only behavior.

In `@packages/core/examples/genericDynamicVolume/index.ts`:
- Around line 100-131: Guard both toolbar callbacks against incomplete
initialization in run: have the orientation handler verify getViewport() returns
a viewport before calling setOrientation or render, and have the Play/Stop
handler verify volume is initialized before starting or advancing the cine
interval. Preserve existing behavior once the viewport and volume are ready.

In
`@packages/docs/docs/concepts/cornerstone-tools/annotation/measurementTargets.md`:
- Around line 103-105: Update the measurement-target documentation to reference
NON_PIXEL_DATA_MODALITIES as its standalone export from
measurementTargetFilters.ts, not as a property of measurementTargetFilters.
Since it is not publicly re-exported by `@cornerstonejs/tools`, avoid presenting
it as a public API property and describe the non-pixel modalities directly or
use the supported public exports.

---

Outside diff comments:
In `@packages/tools/src/tools/base/BaseTool.ts`:
- Around line 615-630: Update ensureCachedStatsTargets in BaseTool to safely
handle missing or undefined data.cachedStats before indexing target IDs.
Initialize or otherwise provide an empty mutable stats record when cachedStats
is absent, then preserve the existing needsUpdate checks and missing-target
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 792b6fa9-3722-4d93-8b52-bbe6dd3c6b2c

📥 Commits

Reviewing files that changed from the base of the PR and between dc3c412 and 37eeea0.

📒 Files selected for processing (17)
  • .claude/skills/cornerstone3d-dev/SKILL.md
  • packages/core/examples/genericDynamicVolume/index.ts
  • packages/core/examples/tmtv/index.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewport.ts
  • packages/core/test/planarViewportState.jest.js
  • packages/docs/docs/concepts/cornerstone-tools/annotation/measurementTargets.md
  • packages/tools/examples/petCt/index.ts
  • packages/tools/src/index.ts
  • packages/tools/src/tools/annotation/CircleROITool.ts
  • packages/tools/src/tools/annotation/RectangleROITool.ts
  • packages/tools/src/tools/base/AnnotationTool.ts
  • packages/tools/src/tools/base/BaseTool.ts
  • packages/tools/src/tools/base/index.ts
  • packages/tools/src/tools/base/measurementTargetFilters.ts
  • packages/tools/src/tools/index.ts
  • packages/tools/src/types/IBaseTool.ts
  • packages/tools/test/measurementTargets.jest.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/tools/examples/petCt/index.ts
  • packages/tools/src/tools/annotation/CircleROITool.ts
  • packages/tools/src/types/IBaseTool.ts
  • packages/core/examples/tmtv/index.ts

Comment thread .claude/skills/cornerstone3d-dev/SKILL.md Outdated
Comment thread .claude/skills/cornerstone3d-dev/SKILL.md Outdated
Comment thread packages/core/examples/genericDynamicVolume/index.ts
Comment thread packages/docs/docs/concepts/cornerstone-tools/annotation/measurementTargets.md Outdated
sedghi and others added 6 commits July 13, 2026 10:24
targetsFilter now receives the viewport's candidate display sets and returns the subset to measure, instead of being called per candidate with a true/false/'useAndStop'/'stop' return. Drops the MeasurementTargetsFilterResult union and the per-candidate 'previous' field; the built-in filters become plain array transforms.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

Comment thread packages/docs/docs/concepts/cornerstone-tools/annotation/measurementTargets.md Outdated

@sedghi sedghi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i think docs should be updated though

@wayfarer3130 wayfarer3130 merged commit 183357e into main Jul 15, 2026
17 checks passed
@wayfarer3130 wayfarer3130 deleted the feat/add-multiple-results branch July 15, 2026 14:56
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.

2 participants