@cornerstonejs/tools 4.12.3 → 4.12.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/enums/MeasurementType.d.ts +6 -0
- package/dist/esm/enums/MeasurementType.js +7 -0
- package/dist/esm/enums/index.d.ts +1 -0
- package/dist/esm/enums/index.js +1 -0
- package/dist/esm/tools/SculptorTool/CircleSculptCursor.d.ts +8 -10
- package/dist/esm/tools/SculptorTool/CircleSculptCursor.js +33 -133
- package/dist/esm/tools/SculptorTool.d.ts +20 -5
- package/dist/esm/tools/SculptorTool.js +243 -52
- package/dist/esm/tools/annotation/BidirectionalTool.d.ts +0 -2
- package/dist/esm/tools/annotation/BidirectionalTool.js +24 -28
- package/dist/esm/tools/annotation/CircleROITool.d.ts +1 -2
- package/dist/esm/tools/annotation/CircleROITool.js +51 -44
- package/dist/esm/tools/annotation/EllipticalROITool.js +1 -1
- package/dist/esm/tools/annotation/LengthTool.d.ts +0 -2
- package/dist/esm/tools/annotation/LengthTool.js +13 -25
- package/dist/esm/tools/annotation/PlanarFreehandROITool.d.ts +2 -1
- package/dist/esm/tools/annotation/PlanarFreehandROITool.js +70 -68
- package/dist/esm/tools/base/BaseTool.d.ts +4 -2
- package/dist/esm/tools/base/BaseTool.js +38 -11
- package/dist/esm/tools/segmentation/BrushTool.js +9 -0
- package/dist/esm/tools/segmentation/CircleROIStartEndThresholdTool.js +4 -1
- package/dist/esm/types/CalculatorTypes.d.ts +4 -3
- package/dist/esm/types/ISculptToolShape.d.ts +6 -4
- package/dist/esm/utilities/contours/index.d.ts +1 -2
- package/dist/esm/utilities/contours/index.js +1 -2
- package/dist/esm/utilities/getCalibratedUnits.d.ts +2 -0
- package/dist/esm/utilities/getCalibratedUnits.js +32 -63
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +3 -3
- package/dist/esm/utilities/contours/calculatePerimeter.d.ts +0 -2
- package/dist/esm/utilities/contours/calculatePerimeter.js +0 -16
|
@@ -568,7 +568,7 @@ class EllipticalROITool extends AnnotationTool {
|
|
|
568
568
|
const { worldWidth, worldHeight } = getWorldWidthAndHeightFromTwoPoints(viewPlaneNormal, viewUp, worldPos1, worldPos2);
|
|
569
569
|
const isEmptyArea = worldWidth === 0 && worldHeight === 0;
|
|
570
570
|
const handles = [pos1Index, pos2Index];
|
|
571
|
-
const { scale,
|
|
571
|
+
const { scale, areaUnit } = getCalibratedLengthUnitsAndScale(image, handles);
|
|
572
572
|
const aspect = getCalibratedAspect(image);
|
|
573
573
|
const area = Math.abs(Math.PI *
|
|
574
574
|
(worldWidth / scale / 2) *
|
|
@@ -35,8 +35,6 @@ declare class LengthTool extends AnnotationTool {
|
|
|
35
35
|
_activateDraw: (element: HTMLDivElement) => void;
|
|
36
36
|
_deactivateDraw: (element: HTMLDivElement) => void;
|
|
37
37
|
renderAnnotation: (enabledElement: Types.IEnabledElement, svgDrawingHelper: SVGDrawingHelper) => boolean;
|
|
38
|
-
_calculateLength(pos1: any, pos2: any): number;
|
|
39
38
|
_calculateCachedStats(annotation: any, renderingEngine: any, enabledElement: any): any;
|
|
40
|
-
_isInsideVolume(index1: any, index2: any, dimensions: any): boolean;
|
|
41
39
|
}
|
|
42
40
|
export default LengthTool;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Events, ChangeTypes } from '../../enums';
|
|
1
|
+
import { Events, ChangeTypes, MeasurementType } from '../../enums';
|
|
2
2
|
import { getEnabledElement, utilities as csUtils, utilities, getEnabledElementByViewportId, } from '@cornerstonejs/core';
|
|
3
3
|
import { getCalibratedLengthUnitsAndScale } from '../../utilities/getCalibratedUnits';
|
|
4
4
|
import { AnnotationTool } from '../base';
|
|
@@ -15,7 +15,6 @@ import { getTextBoxCoordsCanvas } from '../../utilities/drawing';
|
|
|
15
15
|
import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
|
|
16
16
|
import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
|
|
17
17
|
import { getStyleProperty } from '../../stateManagement/annotation/config/helpers';
|
|
18
|
-
const { transformWorldToIndex } = csUtils;
|
|
19
18
|
class LengthTool extends AnnotationTool {
|
|
20
19
|
static { this.toolName = 'Length'; }
|
|
21
20
|
constructor(toolProps = {}, defaultToolProps = {
|
|
@@ -384,17 +383,9 @@ class LengthTool extends AnnotationTool {
|
|
|
384
383
|
triggerAnnotationRenderForViewportIds(viewportIdsToRender);
|
|
385
384
|
evt.preventDefault();
|
|
386
385
|
}
|
|
387
|
-
_calculateLength(pos1, pos2) {
|
|
388
|
-
const dx = pos1[0] - pos2[0];
|
|
389
|
-
const dy = pos1[1] - pos2[1];
|
|
390
|
-
const dz = pos1[2] - pos2[2];
|
|
391
|
-
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
392
|
-
}
|
|
393
386
|
_calculateCachedStats(annotation, renderingEngine, enabledElement) {
|
|
394
387
|
const data = annotation.data;
|
|
395
388
|
const { element } = enabledElement.viewport;
|
|
396
|
-
const worldPos1 = data.handles.points[0];
|
|
397
|
-
const worldPos2 = data.handles.points[1];
|
|
398
389
|
const { cachedStats } = data;
|
|
399
390
|
const targetIds = Object.keys(cachedStats);
|
|
400
391
|
for (let i = 0; i < targetIds.length; i++) {
|
|
@@ -404,20 +395,21 @@ class LengthTool extends AnnotationTool {
|
|
|
404
395
|
continue;
|
|
405
396
|
}
|
|
406
397
|
const { imageData, dimensions } = image;
|
|
407
|
-
const
|
|
408
|
-
const
|
|
409
|
-
const
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
398
|
+
const handles = data.handles.points.map((point) => imageData.worldToIndex(point));
|
|
399
|
+
const calibrate = getCalibratedLengthUnitsAndScale(image, handles);
|
|
400
|
+
const { unit } = calibrate;
|
|
401
|
+
const length = LengthTool.calculateLengthInIndex(calibrate, handles);
|
|
402
|
+
this.isHandleOutsideImage = !LengthTool.isInsideVolume(dimensions, handles);
|
|
403
|
+
const namedLength = {
|
|
404
|
+
name: 'length',
|
|
405
|
+
value: length,
|
|
406
|
+
unit,
|
|
407
|
+
type: MeasurementType.Linear,
|
|
408
|
+
};
|
|
418
409
|
cachedStats[targetId] = {
|
|
419
410
|
length,
|
|
420
411
|
unit,
|
|
412
|
+
statsArray: [namedLength],
|
|
421
413
|
};
|
|
422
414
|
}
|
|
423
415
|
const invalidated = annotation.invalidated;
|
|
@@ -427,10 +419,6 @@ class LengthTool extends AnnotationTool {
|
|
|
427
419
|
}
|
|
428
420
|
return cachedStats;
|
|
429
421
|
}
|
|
430
|
-
_isInsideVolume(index1, index2, dimensions) {
|
|
431
|
-
return (csUtils.indexWithinDimensions(index1, dimensions) &&
|
|
432
|
-
csUtils.indexWithinDimensions(index2, dimensions));
|
|
433
|
-
}
|
|
434
422
|
}
|
|
435
423
|
function defaultGetTextLines(data, targetId) {
|
|
436
424
|
const cachedVolumeStats = data.cachedStats[targetId];
|
|
@@ -48,12 +48,13 @@ declare class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
48
48
|
deltaInX: any;
|
|
49
49
|
deltaInY: any;
|
|
50
50
|
}): void;
|
|
51
|
-
protected updateOpenCachedStats({ targetId, metadata, cachedStats, modalityUnit, calibratedScale, points, }: {
|
|
51
|
+
protected updateOpenCachedStats({ targetId, metadata, cachedStats, modalityUnit, calibratedScale, imageData, points, }: {
|
|
52
52
|
targetId: any;
|
|
53
53
|
metadata: any;
|
|
54
54
|
cachedStats: any;
|
|
55
55
|
modalityUnit: any;
|
|
56
56
|
calibratedScale: any;
|
|
57
|
+
imageData: any;
|
|
57
58
|
points: any;
|
|
58
59
|
}): void;
|
|
59
60
|
private _renderStats;
|
|
@@ -19,9 +19,8 @@ import { getTextBoxCoordsCanvas } from '../../utilities/drawing';
|
|
|
19
19
|
import { getLineSegmentIntersectionsCoordinates } from '../../utilities/math/polyline';
|
|
20
20
|
import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScaled';
|
|
21
21
|
import { BasicStatsCalculator } from '../../utilities/math/basic';
|
|
22
|
-
import calculatePerimeter from '../../utilities/contours/calculatePerimeter';
|
|
23
22
|
import ContourSegmentationBaseTool from '../base/ContourSegmentationBaseTool';
|
|
24
|
-
import { KeyboardBindings, ChangeTypes } from '../../enums';
|
|
23
|
+
import { KeyboardBindings, ChangeTypes, MeasurementType } from '../../enums';
|
|
25
24
|
import { getPixelValueUnits } from '../../utilities/getPixelValueUnits';
|
|
26
25
|
const { pointCanProjectOnLine } = polyline;
|
|
27
26
|
const { EPSILON } = CONSTANTS;
|
|
@@ -150,23 +149,22 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
150
149
|
isSuvScaled: this.isSuvScaled(viewport, targetId, annotation.metadata.referencedImageId),
|
|
151
150
|
};
|
|
152
151
|
const modalityUnit = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, modalityUnitOptions);
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
});
|
|
152
|
+
const polyline = data.contour.polyline;
|
|
153
|
+
const numPoints = polyline.length;
|
|
154
|
+
const projectedPolyline = new Array(numPoints);
|
|
155
|
+
for (let i = 0; i < numPoints; i++) {
|
|
156
|
+
projectedPolyline[i] = viewport.worldToCanvas(polyline[i]);
|
|
157
|
+
}
|
|
158
|
+
const { maxX: canvasMaxX, maxY: canvasMaxY, minX: canvasMinX, minY: canvasMinY, } = math.polyline.getAABB(projectedPolyline);
|
|
159
|
+
const topLeftBBWorld = viewport.canvasToWorld([canvasMinX, canvasMinY]);
|
|
160
|
+
const topLeftBBIndex = csUtils.transformWorldToIndex(imageData, topLeftBBWorld);
|
|
161
|
+
const bottomRightBBWorld = viewport.canvasToWorld([
|
|
162
|
+
canvasMaxX,
|
|
163
|
+
canvasMaxY,
|
|
164
|
+
]);
|
|
165
|
+
const bottomRightBBIndex = csUtils.transformWorldToIndex(imageData, bottomRightBBWorld);
|
|
166
|
+
const handles = [topLeftBBIndex, bottomRightBBIndex];
|
|
167
|
+
const calibratedScale = getCalibratedLengthUnitsAndScale(image, handles);
|
|
170
168
|
const canvasPoint = canvasCoordinates[0];
|
|
171
169
|
const originalWorldPoint = viewport.canvasToWorld(canvasPoint);
|
|
172
170
|
const deltaXPoint = viewport.canvasToWorld([
|
|
@@ -179,30 +177,24 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
179
177
|
]);
|
|
180
178
|
const deltaInX = vec3.distance(originalWorldPoint, deltaXPoint);
|
|
181
179
|
const deltaInY = vec3.distance(originalWorldPoint, deltaYPoint);
|
|
180
|
+
const statsArgs = {
|
|
181
|
+
targetId,
|
|
182
|
+
viewport,
|
|
183
|
+
canvasCoordinates,
|
|
184
|
+
points,
|
|
185
|
+
imageData,
|
|
186
|
+
metadata,
|
|
187
|
+
cachedStats,
|
|
188
|
+
modalityUnit,
|
|
189
|
+
calibratedScale,
|
|
190
|
+
deltaInX,
|
|
191
|
+
deltaInY,
|
|
192
|
+
};
|
|
182
193
|
if (closed) {
|
|
183
|
-
this.updateClosedCachedStats(
|
|
184
|
-
targetId,
|
|
185
|
-
viewport,
|
|
186
|
-
canvasCoordinates,
|
|
187
|
-
points,
|
|
188
|
-
imageData,
|
|
189
|
-
metadata,
|
|
190
|
-
cachedStats,
|
|
191
|
-
modalityUnit,
|
|
192
|
-
calibratedScale,
|
|
193
|
-
deltaInX,
|
|
194
|
-
deltaInY,
|
|
195
|
-
});
|
|
194
|
+
this.updateClosedCachedStats(statsArgs);
|
|
196
195
|
}
|
|
197
196
|
else {
|
|
198
|
-
this.updateOpenCachedStats(
|
|
199
|
-
metadata,
|
|
200
|
-
targetId,
|
|
201
|
-
cachedStats,
|
|
202
|
-
modalityUnit,
|
|
203
|
-
calibratedScale,
|
|
204
|
-
points,
|
|
205
|
-
});
|
|
197
|
+
this.updateOpenCachedStats(statsArgs);
|
|
206
198
|
}
|
|
207
199
|
}
|
|
208
200
|
const invalidated = annotation.invalidated;
|
|
@@ -255,12 +247,12 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
255
247
|
this._throttledCalculateCachedStats = throttle(this._calculateCachedStats, 100, { trailing: true });
|
|
256
248
|
}
|
|
257
249
|
filterInteractableAnnotationsForElement(element, annotations) {
|
|
258
|
-
if (!annotations
|
|
259
|
-
return;
|
|
250
|
+
if (!annotations?.length) {
|
|
251
|
+
return [];
|
|
260
252
|
}
|
|
261
253
|
const baseFilteredAnnotations = super.filterInteractableAnnotationsForElement(element, annotations);
|
|
262
|
-
if (!baseFilteredAnnotations
|
|
263
|
-
return;
|
|
254
|
+
if (!baseFilteredAnnotations?.length) {
|
|
255
|
+
return [];
|
|
264
256
|
}
|
|
265
257
|
const enabledElement = getEnabledElement(element);
|
|
266
258
|
const { viewport } = enabledElement;
|
|
@@ -432,21 +424,15 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
432
424
|
updateClosedCachedStats({ viewport, points, imageData, metadata, cachedStats, targetId, modalityUnit, canvasCoordinates, calibratedScale, deltaInX, deltaInY, }) {
|
|
433
425
|
const { scale, areaUnit, unit } = calibratedScale;
|
|
434
426
|
const { voxelManager } = viewport.getImageData();
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
let
|
|
440
|
-
let
|
|
441
|
-
let
|
|
442
|
-
let
|
|
443
|
-
|
|
444
|
-
let kMax = worldPosIndex[2];
|
|
445
|
-
for (let j = 1; j < points.length; j++) {
|
|
446
|
-
const worldPosIndex = csUtils.transformWorldToIndex(imageData, points[j]);
|
|
447
|
-
worldPosIndex[0] = Math.floor(worldPosIndex[0]);
|
|
448
|
-
worldPosIndex[1] = Math.floor(worldPosIndex[1]);
|
|
449
|
-
worldPosIndex[2] = Math.floor(worldPosIndex[2]);
|
|
427
|
+
const indexPoints = points.map((point) => imageData.worldToIndex(point));
|
|
428
|
+
let iMin = Number.MAX_SAFE_INTEGER;
|
|
429
|
+
let iMax = Number.MIN_SAFE_INTEGER;
|
|
430
|
+
let jMin = Number.MAX_SAFE_INTEGER;
|
|
431
|
+
let jMax = Number.MIN_SAFE_INTEGER;
|
|
432
|
+
let kMin = Number.MAX_SAFE_INTEGER;
|
|
433
|
+
let kMax = Number.MIN_SAFE_INTEGER;
|
|
434
|
+
for (let j = 0; j < points.length; j++) {
|
|
435
|
+
const worldPosIndex = indexPoints[j].map(Math.floor);
|
|
450
436
|
iMin = Math.min(iMin, worldPosIndex[0]);
|
|
451
437
|
iMax = Math.max(iMax, worldPosIndex[0]);
|
|
452
438
|
jMin = Math.min(jMin, worldPosIndex[1]);
|
|
@@ -454,13 +440,9 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
454
440
|
kMin = Math.min(kMin, worldPosIndex[2]);
|
|
455
441
|
kMax = Math.max(kMax, worldPosIndex[2]);
|
|
456
442
|
}
|
|
457
|
-
const worldPosIndex2 = csUtils.transformWorldToIndex(imageData, points[1]);
|
|
458
|
-
worldPosIndex2[0] = Math.floor(worldPosIndex2[0]);
|
|
459
|
-
worldPosIndex2[1] = Math.floor(worldPosIndex2[1]);
|
|
460
|
-
worldPosIndex2[2] = Math.floor(worldPosIndex2[2]);
|
|
461
443
|
let area = polyline.getArea(canvasCoordinates) / scale / scale;
|
|
462
444
|
area *= deltaInX * deltaInY;
|
|
463
|
-
const perimeter =
|
|
445
|
+
const perimeter = PlanarFreehandROITool.calculateLengthInIndex(calibratedScale, indexPoints, closed);
|
|
464
446
|
const iDelta = 0.01 * (iMax - iMin);
|
|
465
447
|
const jDelta = 0.01 * (jMax - jMin);
|
|
466
448
|
const kDelta = 0.01 * (kMax - kMin);
|
|
@@ -515,6 +497,18 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
515
497
|
});
|
|
516
498
|
}
|
|
517
499
|
const stats = this.configuration.statsCalculator.getStatistics();
|
|
500
|
+
const namedArea = {
|
|
501
|
+
name: 'area',
|
|
502
|
+
value: area,
|
|
503
|
+
unit: areaUnit,
|
|
504
|
+
type: MeasurementType.Area,
|
|
505
|
+
};
|
|
506
|
+
const namedPerimeter = {
|
|
507
|
+
name: 'perimeter',
|
|
508
|
+
value: perimeter,
|
|
509
|
+
unit,
|
|
510
|
+
type: MeasurementType.Linear,
|
|
511
|
+
};
|
|
518
512
|
cachedStats[targetId] = {
|
|
519
513
|
Modality: metadata.Modality,
|
|
520
514
|
area,
|
|
@@ -523,21 +517,29 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
523
517
|
max: stats.max?.value,
|
|
524
518
|
min: stats.min?.value,
|
|
525
519
|
stdDev: stats.stdDev?.value,
|
|
526
|
-
statsArray: stats.array,
|
|
520
|
+
statsArray: [namedArea, namedPerimeter, ...stats.array],
|
|
527
521
|
pointsInShape: pointsInShape,
|
|
528
522
|
areaUnit,
|
|
529
523
|
modalityUnit,
|
|
530
524
|
unit,
|
|
531
525
|
};
|
|
532
526
|
}
|
|
533
|
-
updateOpenCachedStats({ targetId, metadata, cachedStats, modalityUnit, calibratedScale, points, }) {
|
|
534
|
-
const {
|
|
535
|
-
const
|
|
527
|
+
updateOpenCachedStats({ targetId, metadata, cachedStats, modalityUnit, calibratedScale, imageData, points, }) {
|
|
528
|
+
const { unit } = calibratedScale;
|
|
529
|
+
const indexPoints = points.map((point) => imageData.worldToIndex(point));
|
|
530
|
+
const length = PlanarFreehandROITool.calculateLengthInIndex(calibratedScale, indexPoints);
|
|
531
|
+
const namedLength = {
|
|
532
|
+
name: 'length',
|
|
533
|
+
value: length,
|
|
534
|
+
unit,
|
|
535
|
+
type: MeasurementType.Linear,
|
|
536
|
+
};
|
|
536
537
|
cachedStats[targetId] = {
|
|
537
538
|
Modality: metadata.Modality,
|
|
538
539
|
length,
|
|
539
540
|
modalityUnit,
|
|
540
541
|
unit,
|
|
542
|
+
statArray: [namedLength],
|
|
541
543
|
};
|
|
542
544
|
}
|
|
543
545
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { utilities } from '@cornerstonejs/core';
|
|
1
|
+
import { utilities as csUtils } from '@cornerstonejs/core';
|
|
2
2
|
import type { Types } from '@cornerstonejs/core';
|
|
3
3
|
import ToolModes from '../../enums/ToolModes';
|
|
4
4
|
import type StrategyCallbacks from '../../enums/StrategyCallbacks';
|
|
@@ -9,7 +9,7 @@ declare abstract class BaseTool {
|
|
|
9
9
|
configuration: Record<string, any>;
|
|
10
10
|
toolGroupId: string;
|
|
11
11
|
mode: ToolModes;
|
|
12
|
-
protected memo:
|
|
12
|
+
protected memo: csUtils.HistoryMemo.Memo;
|
|
13
13
|
static defaults: {
|
|
14
14
|
configuration: {
|
|
15
15
|
strategies: {};
|
|
@@ -36,5 +36,7 @@ declare abstract class BaseTool {
|
|
|
36
36
|
doneEditMemo(): void;
|
|
37
37
|
static startGroupRecording(): void;
|
|
38
38
|
static endGroupRecording(): void;
|
|
39
|
+
static calculateLengthInIndex(calibrate: any, indexPoints: any, closed?: boolean): number;
|
|
40
|
+
static isInsideVolume(dimensions: any, indexPoints: any): boolean;
|
|
39
41
|
}
|
|
40
42
|
export default BaseTool;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { utilities } from '@cornerstonejs/core';
|
|
1
|
+
import { utilities as csUtils } from '@cornerstonejs/core';
|
|
2
2
|
import ToolModes from '../../enums/ToolModes';
|
|
3
|
-
const { DefaultHistoryMemo } =
|
|
3
|
+
const { DefaultHistoryMemo } = csUtils.HistoryMemo;
|
|
4
4
|
class BaseTool {
|
|
5
5
|
static { this.defaults = {
|
|
6
6
|
configuration: {
|
|
@@ -12,7 +12,7 @@ class BaseTool {
|
|
|
12
12
|
}; }
|
|
13
13
|
constructor(toolProps, defaultToolProps) {
|
|
14
14
|
const mergedDefaults = BaseTool.mergeDefaultProps(BaseTool.defaults, defaultToolProps);
|
|
15
|
-
const initialProps =
|
|
15
|
+
const initialProps = csUtils.deepMerge(mergedDefaults, toolProps);
|
|
16
16
|
const { configuration = {}, supportedInteractionTypes, toolGroupId, } = initialProps;
|
|
17
17
|
this.toolGroupId = toolGroupId;
|
|
18
18
|
this.supportedInteractionTypes = supportedInteractionTypes || [];
|
|
@@ -23,7 +23,7 @@ class BaseTool {
|
|
|
23
23
|
if (!additionalProps) {
|
|
24
24
|
return defaultProps;
|
|
25
25
|
}
|
|
26
|
-
return
|
|
26
|
+
return csUtils.deepMerge(defaultProps, additionalProps);
|
|
27
27
|
}
|
|
28
28
|
get toolName() {
|
|
29
29
|
return this.getToolName();
|
|
@@ -43,7 +43,7 @@ class BaseTool {
|
|
|
43
43
|
return strategies[activeStrategy][callbackType]?.call(this, enabledElement, operationData, ...extraArgs);
|
|
44
44
|
}
|
|
45
45
|
setConfiguration(newConfiguration) {
|
|
46
|
-
this.configuration =
|
|
46
|
+
this.configuration = csUtils.deepMerge(this.configuration, newConfiguration);
|
|
47
47
|
}
|
|
48
48
|
setActiveStrategy(strategyName) {
|
|
49
49
|
this.setConfiguration({ activeStrategy: strategyName });
|
|
@@ -51,8 +51,8 @@ class BaseTool {
|
|
|
51
51
|
getTargetImageData(targetId) {
|
|
52
52
|
if (targetId.startsWith('imageId:')) {
|
|
53
53
|
const imageId = targetId.split('imageId:')[1];
|
|
54
|
-
const imageURI =
|
|
55
|
-
let viewports =
|
|
54
|
+
const imageURI = csUtils.imageIdToURI(imageId);
|
|
55
|
+
let viewports = csUtils.getViewportsWithImageURI(imageURI);
|
|
56
56
|
if (!viewports || !viewports.length) {
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
@@ -65,16 +65,16 @@ class BaseTool {
|
|
|
65
65
|
return viewports[0].getImageData();
|
|
66
66
|
}
|
|
67
67
|
else if (targetId.startsWith('volumeId:')) {
|
|
68
|
-
const volumeId =
|
|
69
|
-
const viewports =
|
|
68
|
+
const volumeId = csUtils.getVolumeId(targetId);
|
|
69
|
+
const viewports = csUtils.getViewportsWithVolumeId(volumeId);
|
|
70
70
|
if (!viewports || !viewports.length) {
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
73
|
return viewports[0].getImageData();
|
|
74
74
|
}
|
|
75
75
|
else if (targetId.startsWith('videoId:')) {
|
|
76
|
-
const imageURI =
|
|
77
|
-
const viewports =
|
|
76
|
+
const imageURI = csUtils.imageIdToURI(targetId);
|
|
77
|
+
const viewports = csUtils.getViewportsWithImageURI(imageURI);
|
|
78
78
|
if (!viewports || !viewports.length) {
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
@@ -129,6 +129,33 @@ class BaseTool {
|
|
|
129
129
|
static endGroupRecording() {
|
|
130
130
|
DefaultHistoryMemo.endGroupRecording();
|
|
131
131
|
}
|
|
132
|
+
static calculateLengthInIndex(calibrate, indexPoints, closed = false) {
|
|
133
|
+
const scale = calibrate?.scale || 1;
|
|
134
|
+
const scaleY = calibrate?.scaleY || scale;
|
|
135
|
+
const scaleZ = calibrate?.scaleZ || scale;
|
|
136
|
+
let length = 0;
|
|
137
|
+
const count = indexPoints.length;
|
|
138
|
+
const start = closed ? 0 : 1;
|
|
139
|
+
let lastPoint = closed ? indexPoints[count - 1] : indexPoints[0];
|
|
140
|
+
for (let i = start; i < count; i++) {
|
|
141
|
+
const point = indexPoints[i];
|
|
142
|
+
const dx = (point[0] - lastPoint[0]) / scale;
|
|
143
|
+
const dy = (point[1] - lastPoint[1]) / scaleY;
|
|
144
|
+
const dz = (point[2] - lastPoint[2]) / scaleZ;
|
|
145
|
+
length += Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
146
|
+
lastPoint = point;
|
|
147
|
+
}
|
|
148
|
+
return length;
|
|
149
|
+
}
|
|
150
|
+
static isInsideVolume(dimensions, indexPoints) {
|
|
151
|
+
const { length: count } = indexPoints;
|
|
152
|
+
for (let i = 0; i < count; i++) {
|
|
153
|
+
if (!csUtils.indexWithinDimensions(indexPoints[i], dimensions)) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
132
159
|
}
|
|
133
160
|
BaseTool.toolName = 'BaseTool';
|
|
134
161
|
export default BaseTool;
|
|
@@ -113,6 +113,9 @@ class BrushTool extends LabelmapBaseTool {
|
|
|
113
113
|
const hoverData = this._hoverData || this.createHoverData(element);
|
|
114
114
|
triggerAnnotationRenderForViewportUIDs(hoverData.viewportIdsToRender);
|
|
115
115
|
const operationData = this.getOperationData(element);
|
|
116
|
+
if (!operationData) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
116
119
|
this.applyActiveStrategyCallback(enabledElement, operationData, StrategyCallbacks.OnInteractionStart);
|
|
117
120
|
return true;
|
|
118
121
|
};
|
|
@@ -218,6 +221,9 @@ class BrushTool extends LabelmapBaseTool {
|
|
|
218
221
|
this._hoverData = this.createHoverData(element, currentCanvas);
|
|
219
222
|
this._calculateCursor(element, currentCanvas);
|
|
220
223
|
const operationData = this.getOperationData(element);
|
|
224
|
+
if (!operationData) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
221
227
|
operationData.strokePointsWorld = [
|
|
222
228
|
vec3.clone(this._lastDragInfo.world),
|
|
223
229
|
vec3.clone(currentWorld),
|
|
@@ -238,6 +244,9 @@ class BrushTool extends LabelmapBaseTool {
|
|
|
238
244
|
const { element } = eventData;
|
|
239
245
|
const enabledElement = getEnabledElement(element);
|
|
240
246
|
const operationData = this.getOperationData(element);
|
|
247
|
+
if (!operationData) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
241
250
|
if (!this._previewData.preview && !this._previewData.isDrag) {
|
|
242
251
|
this.applyActiveStrategy(enabledElement, operationData);
|
|
243
252
|
}
|
|
@@ -418,7 +418,10 @@ class CircleROIStartEndThresholdTool extends CircleROITool {
|
|
|
418
418
|
worldPos2Index[2] = Math.floor(worldPos2Index[2]);
|
|
419
419
|
worldPos2Index[indexOfProjection] =
|
|
420
420
|
worldProjectionPointIndex[indexOfProjection];
|
|
421
|
-
if (
|
|
421
|
+
if (CircleROITool.isInsideVolume(dimensions, [
|
|
422
|
+
worldPos1Index,
|
|
423
|
+
worldPos2Index,
|
|
424
|
+
])) {
|
|
422
425
|
const iMin = Math.min(worldPos1Index[0], worldPos2Index[0]);
|
|
423
426
|
const iMax = Math.max(worldPos1Index[0], worldPos2Index[0]);
|
|
424
427
|
const jMin = Math.min(worldPos1Index[1], worldPos2Index[1]);
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { Types } from '@cornerstonejs/core';
|
|
2
|
-
type
|
|
2
|
+
import type { MeasurementType } from '../enums/MeasurementType';
|
|
3
|
+
export type Statistics = {
|
|
3
4
|
name: string;
|
|
4
5
|
label?: string;
|
|
5
6
|
value: number | number[];
|
|
6
7
|
unit: null | string;
|
|
7
8
|
pointIJK?: Types.Point3;
|
|
8
9
|
pointLPS?: Types.Point3;
|
|
10
|
+
type?: MeasurementType;
|
|
9
11
|
};
|
|
10
|
-
type NamedStatistics = {
|
|
12
|
+
export type NamedStatistics = {
|
|
11
13
|
mean: Statistics & {
|
|
12
14
|
name: 'mean';
|
|
13
15
|
};
|
|
@@ -60,4 +62,3 @@ type NamedStatistics = {
|
|
|
60
62
|
}>;
|
|
61
63
|
array: Statistics[];
|
|
62
64
|
};
|
|
63
|
-
export type { Statistics, NamedStatistics };
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { Types } from '@cornerstonejs/core';
|
|
2
2
|
import type { SVGDrawingHelper, EventTypes, ContourAnnotation } from '.';
|
|
3
|
-
import type { PushedHandles } from '../tools/SculptorTool/CircleSculptCursor';
|
|
4
|
-
import type { SculptData } from '../tools/SculptorTool';
|
|
5
3
|
export interface ISculptToolShape {
|
|
6
4
|
renderShape(svgDrawingHelper: SVGDrawingHelper, canvasLocation: Types.Point2, options: any): void;
|
|
7
|
-
pushHandles(viewport: Types.IViewport, sculptData: SculptData): PushedHandles;
|
|
8
5
|
configureToolSize(evt: EventTypes.InteractionEventType): void;
|
|
6
|
+
interpolatePoint(viewport: Types.IViewport, angle: number, center: Types.Point2): Types.Point2;
|
|
7
|
+
getEdge(viewport: Types.IViewport, p1: Types.Point3, p2: Types.Point3, mouseCanvas: Types.Point2): {
|
|
8
|
+
point: Types.Point3;
|
|
9
|
+
angle: number;
|
|
10
|
+
canvasPoint: Types.Point2;
|
|
11
|
+
};
|
|
9
12
|
updateToolSize(canvasCoords: Types.Point2, viewport: Types.IViewport, activeAnnotation: ContourAnnotation): void;
|
|
10
13
|
getMaxSpacing(minSpacing: number): number;
|
|
11
|
-
getInsertPosition(previousIndex: number, nextIndex: number, sculptData: SculptData): Types.Point3;
|
|
12
14
|
}
|
|
@@ -10,6 +10,5 @@ import getContourHolesDataCanvas from './getContourHolesDataCanvas';
|
|
|
10
10
|
import updateContourPolyline from './updateContourPolyline';
|
|
11
11
|
import acceptAutogeneratedInterpolations from './interpolation/acceptAutogeneratedInterpolations';
|
|
12
12
|
import findHandlePolylineIndex from './findHandlePolylineIndex';
|
|
13
|
-
import calculatePerimeter from './calculatePerimeter';
|
|
14
13
|
import findIslands from './findIslands';
|
|
15
|
-
export { areCoplanarContours, contourFinder, getDeduplicatedVTKPolyDataPoints, detectContourHoles, findContourHoles, generateContourSetsFromLabelmap, AnnotationToPointData, getContourHolesDataWorld, getContourHolesDataCanvas, updateContourPolyline, acceptAutogeneratedInterpolations, findHandlePolylineIndex,
|
|
14
|
+
export { areCoplanarContours, contourFinder, getDeduplicatedVTKPolyDataPoints, detectContourHoles, findContourHoles, generateContourSetsFromLabelmap, AnnotationToPointData, getContourHolesDataWorld, getContourHolesDataCanvas, updateContourPolyline, acceptAutogeneratedInterpolations, findHandlePolylineIndex, findIslands, };
|
|
@@ -10,6 +10,5 @@ import getContourHolesDataCanvas from './getContourHolesDataCanvas';
|
|
|
10
10
|
import updateContourPolyline from './updateContourPolyline';
|
|
11
11
|
import acceptAutogeneratedInterpolations from './interpolation/acceptAutogeneratedInterpolations';
|
|
12
12
|
import findHandlePolylineIndex from './findHandlePolylineIndex';
|
|
13
|
-
import calculatePerimeter from './calculatePerimeter';
|
|
14
13
|
import findIslands from './findIslands';
|
|
15
|
-
export { areCoplanarContours, contourFinder, getDeduplicatedVTKPolyDataPoints, detectContourHoles, findContourHoles, generateContourSetsFromLabelmap, AnnotationToPointData, getContourHolesDataWorld, getContourHolesDataCanvas, updateContourPolyline, acceptAutogeneratedInterpolations, findHandlePolylineIndex,
|
|
14
|
+
export { areCoplanarContours, contourFinder, getDeduplicatedVTKPolyDataPoints, detectContourHoles, findContourHoles, generateContourSetsFromLabelmap, AnnotationToPointData, getContourHolesDataWorld, getContourHolesDataCanvas, updateContourPolyline, acceptAutogeneratedInterpolations, findHandlePolylineIndex, findIslands, };
|
|
@@ -2,6 +2,8 @@ declare const getCalibratedLengthUnitsAndScale: (image: any, handles: any) => {
|
|
|
2
2
|
unit: string;
|
|
3
3
|
areaUnit: string;
|
|
4
4
|
scale: number;
|
|
5
|
+
scaleY: number;
|
|
6
|
+
scaleZ: number;
|
|
5
7
|
volumeUnit: string;
|
|
6
8
|
};
|
|
7
9
|
declare const getCalibratedProbeUnitsAndValue: (image: any, handles: any) => {
|