Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(EllipticalROITool) Issue 1294 - Ellipse tool spamming ANNOTATION_MODIFIED events when it is outside volume #1295

Merged
merged 7 commits into from
Jun 5, 2024
155 changes: 75 additions & 80 deletions packages/tools/src/tools/annotation/EllipticalROITool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,6 @@ class EllipticalROITool extends AnnotationTool {
);

const { centerPointRadius } = this.configuration;

// If cachedStats does not exist, or the unit is missing (as part of import/hydration etc.),
// force to recalculate the stats from the points
if (
Expand Down Expand Up @@ -1012,99 +1011,95 @@ class EllipticalROITool extends AnnotationTool {
// Check if one of the indexes are inside the volume, this then gives us
// Some area to do stats over.

if (this._isInsideVolume(pos1Index, post2Index, dimensions)) {
this.isHandleOutsideImage = false;
this.isHandleOutsideImage = !this._isInsideVolume(
pos1Index,
post2Index,
dimensions
);
kom482 marked this conversation as resolved.
Show resolved Hide resolved

const iMin = Math.min(pos1Index[0], post2Index[0]);
const iMax = Math.max(pos1Index[0], post2Index[0]);
const iMin = Math.min(pos1Index[0], post2Index[0]);
const iMax = Math.max(pos1Index[0], post2Index[0]);

const jMin = Math.min(pos1Index[1], post2Index[1]);
const jMax = Math.max(pos1Index[1], post2Index[1]);
const jMin = Math.min(pos1Index[1], post2Index[1]);
const jMax = Math.max(pos1Index[1], post2Index[1]);

const kMin = Math.min(pos1Index[2], post2Index[2]);
const kMax = Math.max(pos1Index[2], post2Index[2]);
const kMin = Math.min(pos1Index[2], post2Index[2]);
const kMax = Math.max(pos1Index[2], post2Index[2]);

const boundsIJK = [
[iMin, iMax],
[jMin, jMax],
[kMin, kMax],
] as [Types.Point2, Types.Point2, Types.Point2];
const boundsIJK = [
[iMin, iMax],
[jMin, jMax],
[kMin, kMax],
] as [Types.Point2, Types.Point2, Types.Point2];

const center = [
(topLeftWorld[0] + bottomRightWorld[0]) / 2,
(topLeftWorld[1] + bottomRightWorld[1]) / 2,
(topLeftWorld[2] + bottomRightWorld[2]) / 2,
] as Types.Point3;
const center = [
(topLeftWorld[0] + bottomRightWorld[0]) / 2,
(topLeftWorld[1] + bottomRightWorld[1]) / 2,
(topLeftWorld[2] + bottomRightWorld[2]) / 2,
] as Types.Point3;

const ellipseObj = {
center,
xRadius: Math.abs(topLeftWorld[0] - bottomRightWorld[0]) / 2,
yRadius: Math.abs(topLeftWorld[1] - bottomRightWorld[1]) / 2,
zRadius: Math.abs(topLeftWorld[2] - bottomRightWorld[2]) / 2,
};

const { worldWidth, worldHeight } = getWorldWidthAndHeightFromTwoPoints(
viewPlaneNormal,
viewUp,
worldPos1,
worldPos2
);
const isEmptyArea = worldWidth === 0 && worldHeight === 0;
const ellipseObj = {
center,
xRadius: Math.abs(topLeftWorld[0] - bottomRightWorld[0]) / 2,
yRadius: Math.abs(topLeftWorld[1] - bottomRightWorld[1]) / 2,
zRadius: Math.abs(topLeftWorld[2] - bottomRightWorld[2]) / 2,
};

const handles = [pos1Index, post2Index];
const { scale, areaUnits } = getCalibratedLengthUnitsAndScale(
image,
handles
);
const { worldWidth, worldHeight } = getWorldWidthAndHeightFromTwoPoints(
viewPlaneNormal,
viewUp,
worldPos1,
worldPos2
);
const isEmptyArea = worldWidth === 0 && worldHeight === 0;

const area =
Math.abs(Math.PI * (worldWidth / 2) * (worldHeight / 2)) /
scale /
scale;
const handles = [pos1Index, post2Index];
const { scale, areaUnits } = getCalibratedLengthUnitsAndScale(
image,
handles
);

const modalityUnitOptions = {
isPreScaled: isViewportPreScaled(viewport, targetId),
const area =
Math.abs(Math.PI * (worldWidth / 2) * (worldHeight / 2)) /
scale /
scale;

isSuvScaled: this.isSuvScaled(
viewport,
targetId,
annotation.metadata.referencedImageId
),
};
const modalityUnitOptions = {
isPreScaled: isViewportPreScaled(viewport, targetId),

const modalityUnit = getModalityUnit(
metadata.Modality,
annotation.metadata.referencedImageId,
modalityUnitOptions
);
isSuvScaled: this.isSuvScaled(
viewport,
targetId,
annotation.metadata.referencedImageId
),
};

const pointsInShape = pointInShapeCallback(
imageData,
(pointLPS) => pointInEllipse(ellipseObj, pointLPS, { fast: true }),
this.configuration.statsCalculator.statsCallback,
boundsIJK
);
const modalityUnit = getModalityUnit(
metadata.Modality,
annotation.metadata.referencedImageId,
modalityUnitOptions
);

const stats = this.configuration.statsCalculator.getStatistics();
cachedStats[targetId] = {
Modality: metadata.Modality,
area,
mean: stats.mean?.value,
max: stats.max?.value,
stdDev: stats.stdDev?.value,
statsArray: stats.array,
pointsInShape,
isEmptyArea,
areaUnit: areaUnits,
modalityUnit,
};
} else {
this.isHandleOutsideImage = true;
const pointsInShape = pointInShapeCallback(
imageData,
(pointLPS) => pointInEllipse(ellipseObj, pointLPS, { fast: true }),
this.configuration.statsCalculator.statsCallback,
boundsIJK
);

cachedStats[targetId] = {
Modality: metadata.Modality,
};
}
const stats = this.configuration.statsCalculator.getStatistics();
cachedStats[targetId] = {
Modality: metadata.Modality,
area,
mean: stats.mean?.value,
max: stats.max?.value,
stdDev: stats.stdDev?.value,
statsArray: stats.array,
pointsInShape,
isEmptyArea,
areaUnit: areaUnits,
modalityUnit,
};
}

annotation.invalidated = false;
Expand Down