Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions packages/tools/src/tools/annotation/PlanarFreehandROITool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { BasicStatsCalculator } from '../../utilities/math/basic';
import ContourSegmentationBaseTool from '../base/ContourSegmentationBaseTool';
import { KeyboardBindings, ChangeTypes, MeasurementType } from '../../enums';
import { getPixelValueUnits } from '../../utilities/getPixelValueUnits';
import snapIndexBounds from '../../utilities/boundingBox/snapIndexBounds';

const { pointCanProjectOnLine } = polyline;
const { EPSILON } = CONSTANTS;
Expand Down Expand Up @@ -784,7 +785,7 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
continue;
}

const { imageData, metadata } = image;
const { imageData, metadata, voxelManager } = image;
const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));

const modalityUnitOptions = {
Expand Down Expand Up @@ -860,6 +861,7 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
points,
imageData,
metadata,
voxelManager,
cachedStats,
modalityUnit,
calibratedScale,
Expand Down Expand Up @@ -893,6 +895,7 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
points,
imageData,
metadata,
voxelManager,
cachedStats,
targetId,
modalityUnit,
Expand All @@ -903,8 +906,6 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
}) {
const { areaUnit, unit } = calibratedScale;

const { voxelManager } = viewport.getImageData();

const indexPoints = points.map((point) => imageData.worldToIndex(point));

let iMin = Number.MAX_SAFE_INTEGER;
Expand All @@ -915,7 +916,8 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
let kMax = Number.MIN_SAFE_INTEGER;

for (let j = 0; j < points.length; j++) {
const worldPosIndex = indexPoints[j].map(Math.floor);
const worldPosIndex = indexPoints[j];

iMin = Math.min(iMin, worldPosIndex[0]);
iMax = Math.max(iMax, worldPosIndex[0]);

Expand All @@ -926,6 +928,10 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
kMax = Math.max(kMax, worldPosIndex[2]);
}

[iMin, iMax] = snapIndexBounds(iMin, iMax);
[jMin, jMax] = snapIndexBounds(jMin, jMax);
[kMin, kMax] = snapIndexBounds(kMin, kMax);

// Convert from canvas_pixels ^2 to mm^2
const area = polyline.getArea(canvasCoordinates) * deltaInX * deltaInY;

Expand Down
3 changes: 1 addition & 2 deletions packages/tools/src/tools/segmentation/SegmentLabelTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { getSegmentIndexAtWorldPoint } from '../../utilities/segmentation';
import { state } from '../../store/state';
import type { Segmentation } from '../../types/SegmentationStateTypes';
import { drawTextBox as drawTextBoxSvg } from '../../drawingSvg';
import type { Point2 } from 'packages/core/dist/esm/types';

/**
* Represents a tool used for segment selection. It is used to select a segment
Expand Down Expand Up @@ -199,7 +198,7 @@ class SegmentLabelTool extends BaseTool {
const textBoxPosition = [
canvasCoordinates[0] + offset,
canvasCoordinates[1] + offset,
] as Point2;
] as Types.Point2;

const boundingBox = drawTextBoxSvg(
svgDrawingHelper,
Expand Down
2 changes: 2 additions & 0 deletions packages/tools/src/utilities/boundingBox/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import {
getBoundingBoxAroundShapeIJK,
getBoundingBoxAroundShapeWorld,
} from './getBoundingBoxAroundShape';
import snapIndexBounds from './snapIndexBounds';

export {
extend2DBoundingBoxInViewAxis,
getBoundingBoxAroundShapeIJK,
getBoundingBoxAroundShapeWorld,
snapIndexBounds,
// backwards compatibility
getBoundingBoxAroundShapeIJK as getBoundingBoxAroundShape,
};
24 changes: 24 additions & 0 deletions packages/tools/src/utilities/boundingBox/snapIndexBounds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Types } from '@cornerstonejs/core';

/**
* Converts floating-point min/max index bounds to integer voxel indices.
*
* For planar annotations (delta ≤ 1), floating-point drift from world-to-index
* conversion can map to adjacent slices. Collapsing to a single rounded index
* keeps planar ROIs on the intended slice across viewport types.
*
* For bounds spanning multiple voxels (delta > 1), floor/ceil preserves coverage.
*/
function snapIndexBounds(min: number, max: number): Types.Point2 {
const delta = max - min;

if (delta <= 1) {
const index = Math.round((min + max) / 2);

return [index, index];
}

return [Math.floor(min), Math.ceil(max)];
}

export default snapIndexBounds;
Loading