@cornerstonejs/tools 1.58.1 → 1.58.2

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.
Files changed (88) hide show
  1. package/dist/cjs/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.js +6 -1
  2. package/dist/cjs/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.js.map +1 -1
  3. package/dist/cjs/types/ContourSegmentationAnnotation.d.ts +5 -0
  4. package/dist/cjs/types/IToolGroup.d.ts +3 -1
  5. package/dist/cjs/types/ToolSpecificAnnotationTypes.d.ts +2 -1
  6. package/dist/cjs/utilities/contours/interpolation/getInterpolationData.js +15 -2
  7. package/dist/cjs/utilities/contours/interpolation/getInterpolationData.js.map +1 -1
  8. package/dist/cjs/utilities/contours/interpolation/interpolate.js +1 -4
  9. package/dist/cjs/utilities/contours/interpolation/interpolate.js.map +1 -1
  10. package/dist/cjs/utilities/contours/interpolation/selectHandles.js +2 -3
  11. package/dist/cjs/utilities/contours/interpolation/selectHandles.js.map +1 -1
  12. package/dist/cjs/utilities/math/line/index.d.ts +2 -1
  13. package/dist/cjs/utilities/math/line/index.js +3 -1
  14. package/dist/cjs/utilities/math/line/index.js.map +1 -1
  15. package/dist/cjs/utilities/math/line/isPointOnLineSegment.d.ts +2 -0
  16. package/dist/cjs/utilities/math/line/isPointOnLineSegment.js +22 -0
  17. package/dist/cjs/utilities/math/line/isPointOnLineSegment.js.map +1 -0
  18. package/dist/cjs/utilities/math/polyline/combinePolyline.js +2 -2
  19. package/dist/cjs/utilities/math/polyline/combinePolyline.js.map +1 -1
  20. package/dist/cjs/utilities/math/polyline/getLineSegmentIntersectionsCoordinates.js +2 -2
  21. package/dist/cjs/utilities/math/polyline/getLineSegmentIntersectionsCoordinates.js.map +1 -1
  22. package/dist/cjs/utilities/math/polyline/getLinesIntersection.d.ts +2 -0
  23. package/dist/cjs/utilities/math/polyline/getLinesIntersection.js +78 -0
  24. package/dist/cjs/utilities/math/polyline/getLinesIntersection.js.map +1 -0
  25. package/dist/cjs/utilities/segmentation/InterpolationManager/InterpolationManager.js +30 -12
  26. package/dist/cjs/utilities/segmentation/InterpolationManager/InterpolationManager.js.map +1 -1
  27. package/dist/esm/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.js +6 -0
  28. package/dist/esm/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.js.map +1 -1
  29. package/dist/esm/utilities/contours/interpolation/getInterpolationData.js +15 -2
  30. package/dist/esm/utilities/contours/interpolation/getInterpolationData.js.map +1 -1
  31. package/dist/esm/utilities/contours/interpolation/interpolate.js +1 -4
  32. package/dist/esm/utilities/contours/interpolation/interpolate.js.map +1 -1
  33. package/dist/esm/utilities/contours/interpolation/selectHandles.js +2 -3
  34. package/dist/esm/utilities/contours/interpolation/selectHandles.js.map +1 -1
  35. package/dist/esm/utilities/math/line/index.js +2 -1
  36. package/dist/esm/utilities/math/line/index.js.map +1 -1
  37. package/dist/esm/utilities/math/line/isPointOnLineSegment.js +19 -0
  38. package/dist/esm/utilities/math/line/isPointOnLineSegment.js.map +1 -0
  39. package/dist/esm/utilities/math/polyline/combinePolyline.js +2 -2
  40. package/dist/esm/utilities/math/polyline/combinePolyline.js.map +1 -1
  41. package/dist/esm/utilities/math/polyline/getLineSegmentIntersectionsCoordinates.js +2 -2
  42. package/dist/esm/utilities/math/polyline/getLineSegmentIntersectionsCoordinates.js.map +1 -1
  43. package/dist/esm/utilities/math/polyline/getLinesIntersection.js +52 -0
  44. package/dist/esm/utilities/math/polyline/getLinesIntersection.js.map +1 -0
  45. package/dist/esm/utilities/segmentation/InterpolationManager/InterpolationManager.js +30 -12
  46. package/dist/esm/utilities/segmentation/InterpolationManager/InterpolationManager.js.map +1 -1
  47. package/dist/types/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.d.ts.map +1 -1
  48. package/dist/types/types/ContourSegmentationAnnotation.d.ts +5 -0
  49. package/dist/types/types/ContourSegmentationAnnotation.d.ts.map +1 -1
  50. package/dist/types/types/IToolGroup.d.ts +3 -1
  51. package/dist/types/types/IToolGroup.d.ts.map +1 -1
  52. package/dist/types/types/ToolSpecificAnnotationTypes.d.ts +2 -1
  53. package/dist/types/types/ToolSpecificAnnotationTypes.d.ts.map +1 -1
  54. package/dist/types/utilities/contours/interpolation/getInterpolationData.d.ts.map +1 -1
  55. package/dist/types/utilities/contours/interpolation/interpolate.d.ts.map +1 -1
  56. package/dist/types/utilities/contours/interpolation/selectHandles.d.ts.map +1 -1
  57. package/dist/types/utilities/math/line/index.d.ts +2 -1
  58. package/dist/types/utilities/math/line/index.d.ts.map +1 -1
  59. package/dist/types/utilities/math/line/isPointOnLineSegment.d.ts +3 -0
  60. package/dist/types/utilities/math/line/isPointOnLineSegment.d.ts.map +1 -0
  61. package/dist/types/utilities/math/polyline/combinePolyline.d.ts.map +1 -1
  62. package/dist/types/utilities/math/polyline/getLinesIntersection.d.ts +3 -0
  63. package/dist/types/utilities/math/polyline/getLinesIntersection.d.ts.map +1 -0
  64. package/dist/types/utilities/segmentation/InterpolationManager/InterpolationManager.d.ts.map +1 -1
  65. package/dist/umd/index.js +1 -1
  66. package/dist/umd/index.js.map +1 -1
  67. package/package.json +3 -3
  68. package/src/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.ts +8 -0
  69. package/src/types/ContourSegmentationAnnotation.ts +6 -0
  70. package/src/types/IToolGroup.ts +1 -1
  71. package/src/types/ToolSpecificAnnotationTypes.ts +18 -11
  72. package/src/utilities/contours/interpolation/getInterpolationData.ts +24 -5
  73. package/src/utilities/contours/interpolation/interpolate.ts +1 -5
  74. package/src/utilities/contours/interpolation/selectHandles.ts +9 -4
  75. package/src/utilities/math/line/index.ts +2 -0
  76. package/src/utilities/math/line/isPointOnLineSegment.ts +44 -0
  77. package/src/utilities/math/polyline/combinePolyline.ts +5 -7
  78. package/src/utilities/math/polyline/getLineSegmentIntersectionsCoordinates.ts +2 -2
  79. package/src/utilities/math/polyline/getLinesIntersection.ts +94 -0
  80. package/src/utilities/segmentation/InterpolationManager/InterpolationManager.ts +52 -14
  81. package/dist/cjs/utilities/math/polyline/getLineSegmentsIntersection.d.ts +0 -2
  82. package/dist/cjs/utilities/math/polyline/getLineSegmentsIntersection.js +0 -21
  83. package/dist/cjs/utilities/math/polyline/getLineSegmentsIntersection.js.map +0 -1
  84. package/dist/esm/utilities/math/polyline/getLineSegmentsIntersection.js +0 -18
  85. package/dist/esm/utilities/math/polyline/getLineSegmentsIntersection.js.map +0 -1
  86. package/dist/types/utilities/math/polyline/getLineSegmentsIntersection.d.ts +0 -3
  87. package/dist/types/utilities/math/polyline/getLineSegmentsIntersection.d.ts.map +0 -1
  88. package/src/utilities/math/polyline/getLineSegmentsIntersection.ts +0 -47
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/tools",
3
- "version": "1.58.1",
3
+ "version": "1.58.2",
4
4
  "description": "Cornerstone3D Tools",
5
5
  "main": "src/index.ts",
6
6
  "types": "dist/types/index.d.ts",
@@ -29,7 +29,7 @@
29
29
  "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js"
30
30
  },
31
31
  "dependencies": {
32
- "@cornerstonejs/core": "^1.58.1",
32
+ "@cornerstonejs/core": "^1.58.2",
33
33
  "@icr/polyseg-wasm": "0.4.0",
34
34
  "@types/offscreencanvas": "2019.7.3",
35
35
  "comlink": "^4.4.1",
@@ -59,5 +59,5 @@
59
59
  "type": "individual",
60
60
  "url": "https://ohif.org/donate"
61
61
  },
62
- "gitHead": "6125a8f89f37b975a49786ba2fd75f682f537402"
62
+ "gitHead": "808abe92daab401511e2fbad6328281195a8e297"
63
63
  }
@@ -29,6 +29,7 @@ import { PlanarFreehandContourSegmentationTool } from '../../../tools';
29
29
  import type { Annotation } from '../../../types';
30
30
  import type { ContourAnnotation } from '../../../types/ContourAnnotation';
31
31
  import { ContourWindingDirection } from '../../../types/ContourAnnotation';
32
+ import { triggerAnnotationModified } from '../../../stateManagement/annotation/helpers/state';
32
33
 
33
34
  const DEFAULT_CONTOUR_SEG_TOOLNAME = 'PlanarFreehandContourSegmentationTool';
34
35
 
@@ -357,6 +358,7 @@ function combinePolylines(
357
358
  metadata: {
358
359
  ...metadata,
359
360
  toolName: DEFAULT_CONTOUR_SEG_TOOLNAME,
361
+ originalToolName: targetAnnotation.metadata.toolName,
360
362
  },
361
363
  data: {
362
364
  cachedStats: {},
@@ -368,6 +370,7 @@ function combinePolylines(
368
370
  polyline: [],
369
371
  closed: true,
370
372
  },
373
+ spline: targetAnnotation.data.spline,
371
374
  segmentation: {
372
375
  ...segmentation,
373
376
  },
@@ -377,6 +380,10 @@ function combinePolylines(
377
380
  invalidated: true,
378
381
  isLocked: false,
379
382
  isVisible: undefined,
383
+ // Allow this object to be interpolated against the original interpolation
384
+ // data.
385
+ interpolationUID: targetAnnotation.interpolationUID,
386
+ interpolationCompleted: targetAnnotation.interpolationCompleted,
380
387
  };
381
388
 
382
389
  // Calling `updateContourPolyline` method instead of setting it locally
@@ -393,6 +400,7 @@ function combinePolylines(
393
400
 
394
401
  addAnnotation(newAnnotation, element);
395
402
  contourSegUtils.addContourSegmentationAnnotation(newAnnotation);
403
+ triggerAnnotationModified(newAnnotation, viewport.element);
396
404
 
397
405
  reassignedContourHolesMap
398
406
  .get(polyline)
@@ -4,6 +4,8 @@ import { ContourAnnotation } from './ContourAnnotation';
4
4
 
5
5
  export type ContourSegmentationAnnotationData = {
6
6
  autoGenerated?: boolean;
7
+ interpolationUID?: string;
8
+ interpolationCompleted?: boolean;
7
9
  data: {
8
10
  segmentation: {
9
11
  segmentationId: string;
@@ -16,6 +18,10 @@ export type ContourSegmentationAnnotationData = {
16
18
  originalPolyline?: Types.Point3[];
17
19
  };
18
20
  };
21
+ metadata?: {
22
+ /** The original name of the tool before adding/removing holes and contours */
23
+ originalToolName?: string;
24
+ };
19
25
  handles?: {
20
26
  /**
21
27
  * Segmentation contours can be interpolated between slices to produce
@@ -44,7 +44,7 @@ export default interface IToolGroup {
44
44
  };
45
45
  /** Setting the tool to be Passive by its name*/
46
46
  setToolPassive: {
47
- (toolName: string): void;
47
+ (toolName: string, options?: { removeAllBindings?: boolean }): void;
48
48
  };
49
49
  /** Setting the tool to be Enabled by its name*/
50
50
  setToolEnabled: {
@@ -260,18 +260,25 @@ export type PlanarFreehandROIAnnotation = ContourAnnotation & {
260
260
  export type PlanarFreehandContourSegmentationAnnotation =
261
261
  PlanarFreehandROIAnnotation & ContourSegmentationAnnotationData;
262
262
 
263
- export type InterpolationROIAnnotation = ContourAnnotation & {
264
- metadata: {
265
- annotationUID?: string;
263
+ export type InterpolationROIAnnotation = ContourAnnotation &
264
+ ContourSegmentationAnnotationData & {
265
+ metadata: {
266
+ annotationUID?: string;
267
+ };
268
+ /** The interpolationUID links contours which are interpolated together */
269
+ interpolationUID?: string;
270
+ /**
271
+ * The interpolation completed flag is used to mark interpolations as being done
272
+ * and no longer elligible for matching.
273
+ */
274
+ interpolationCompleted?: boolean;
275
+ /**
276
+ * A flag to track updates to annotations caused by things like
277
+ * spline or livewire regeenration of the data, and which should cause further
278
+ * updates to occur (or not as the tool decides).
279
+ */
280
+ isInterpolationUpdate?: boolean;
266
281
  };
267
- interpolationUID?: string;
268
- /**
269
- * A flag to track updates to annotations caused by things like
270
- * spline or livewire regeenration of the data, and which should cause further
271
- * updates to occur (or not as the tool decides).
272
- */
273
- isInterpolationUpdate?: boolean;
274
- };
275
282
 
276
283
  export interface ArrowAnnotation extends Annotation {
277
284
  data: {
@@ -1,6 +1,11 @@
1
- import type { InterpolationViewportData, Annotation } from '../../../types';
1
+ import type {
2
+ InterpolationViewportData,
3
+ Annotation,
4
+ ContourSegmentationAnnotation,
5
+ } from '../../../types';
2
6
  import { getAnnotations } from '../../../stateManagement/annotation/annotationState';
3
- import { InterpolationROIAnnotation } from '../../../types/ToolSpecificAnnotationTypes';
7
+
8
+ const DEFAULT_CONTOUR_SEG_TOOLNAME = 'PlanarFreehandContourSegmentationTool';
4
9
 
5
10
  export type FilterParam = {
6
11
  /**
@@ -38,12 +43,26 @@ export default function getInterpolationData(
38
43
  ): Map<number, Annotation[]> {
39
44
  const { viewport, sliceData, annotation } = viewportData;
40
45
  const interpolationDatas = new Map<number, Annotation[]>();
41
- const annotations = getAnnotations(
42
- annotation.metadata.toolName,
46
+ const { toolName, originalToolName } = annotation.metadata;
47
+ const testToolName = originalToolName || toolName;
48
+ const annotations = getAnnotations(testToolName, viewport.element) || [];
49
+ const modifiedAnnotations = getAnnotations(
50
+ DEFAULT_CONTOUR_SEG_TOOLNAME,
43
51
  viewport.element
44
52
  );
53
+ if (modifiedAnnotations?.length) {
54
+ modifiedAnnotations.forEach((annotation) => {
55
+ const { metadata } = annotation as ContourSegmentationAnnotation;
56
+ if (
57
+ metadata.originalToolName === testToolName &&
58
+ !annotations.find((it) => it === annotation)
59
+ ) {
60
+ annotations.push(annotation);
61
+ }
62
+ });
63
+ }
45
64
 
46
- if (!annotations) {
65
+ if (!annotations?.length) {
47
66
  return interpolationDatas;
48
67
  }
49
68
 
@@ -28,8 +28,6 @@ export type PointsArray3 = Types.PointsManager<Types.Point3> & {
28
28
 
29
29
  const dP = 0.2; // Aim for < 0.2mm between interpolated nodes when super-sampling.
30
30
 
31
- let interpolating = false;
32
-
33
31
  /**
34
32
  * interpolate - Interpolate missing contours in the ROIContours.
35
33
  * If input is tool data collection, it is expected to be sorted in the order
@@ -44,10 +42,9 @@ let interpolating = false;
44
42
  * @returns null
45
43
  */
46
44
  function interpolate(viewportData: InterpolationViewportData) {
47
- if (interpolating || !viewportData.annotation) {
45
+ if (!viewportData.annotation) {
48
46
  return;
49
47
  }
50
- interpolating = true;
51
48
  const { isInterpolationUpdate, annotation } = viewportData;
52
49
  queueMicrotask(() => {
53
50
  try {
@@ -59,7 +56,6 @@ function interpolate(viewportData: InterpolationViewportData) {
59
56
  }
60
57
  startInterpolation(viewportData);
61
58
  } finally {
62
- interpolating = false;
63
59
  if (isInterpolationUpdate) {
64
60
  // Reset the auto generated flag
65
61
  annotation.autoGenerated = true;
@@ -1,7 +1,6 @@
1
1
  import { vec3 } from 'gl-matrix';
2
2
  import { utilities } from '@cornerstonejs/core';
3
3
  import type { PointsArray3 } from './interpolate';
4
- import { add } from '@kitware/vtk.js/Common/Core/Math';
5
4
 
6
5
  const { PointsManager } = utilities;
7
6
 
@@ -15,18 +14,24 @@ const { PointsManager } = utilities;
15
14
  */
16
15
  export default function selectHandles(
17
16
  polyline: PointsArray3,
18
- handleCount = 8
17
+ handleCount = 12
19
18
  ): PointsArray3 {
20
19
  const handles = PointsManager.create3(handleCount) as PointsArray3;
21
20
  handles.sources = [];
22
21
  const { sources: destPoints } = handles;
23
22
  const { length, sources: sourcePoints = [] } = polyline;
23
+ // The distance used for figuring out the local angle of a line
24
24
  const distance = 6;
25
25
  if (length < distance * 3) {
26
- console.log('Adding subselect handles', handleCount, length);
27
26
  return polyline.subselect(handleCount);
28
27
  }
29
- const interval = Math.min(30, Math.floor(length / 3));
28
+ // Need to make the interval between handles long enough to allow for some
29
+ // variation between points in terms of the distance of a line angle, but
30
+ // also not too many handles either.
31
+ // On average, we get twice the interval between handles, so double the length here.
32
+ const interval = Math.floor(
33
+ Math.max((2 * length) / handleCount, distance * 5)
34
+ );
30
35
  sourcePoints.forEach(() =>
31
36
  destPoints.push(PointsManager.create3(handleCount))
32
37
  );
@@ -2,10 +2,12 @@ import distanceToPoint from './distanceToPoint';
2
2
  import distanceToPointSquared from './distanceToPointSquared';
3
3
  import distanceToPointSquaredInfo from './distanceToPointSquaredInfo';
4
4
  import intersectLine from './intersectLine';
5
+ import isPointOnLineSegment from './isPointOnLineSegment';
5
6
 
6
7
  export {
7
8
  distanceToPoint,
8
9
  distanceToPointSquared,
9
10
  distanceToPointSquaredInfo,
10
11
  intersectLine,
12
+ isPointOnLineSegment,
11
13
  };
@@ -0,0 +1,44 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+
3
+ const ORIENTATION_TOLERANCE = 1e-2;
4
+
5
+ /**
6
+ * Test if a point is on a line segment
7
+ * @param lineStart - Line segment start point
8
+ * @param lineEnd - Line segment end point
9
+ * @param point - Point to test
10
+ * @returns True if the point lies on the line segment or false otherwise
11
+ */
12
+ export default function isPointOnLineSegment(
13
+ lineStart: Types.Point2,
14
+ lineEnd: Types.Point2,
15
+ point: Types.Point2
16
+ ): boolean {
17
+ // The code below runs ~4x faster than calling `line.distanceToPointSquared()` (155 vs 598 ms)
18
+
19
+ // No Math.min/max call for better performance when testing thousands of points
20
+ const minX = lineStart[0] <= lineEnd[0] ? lineStart[0] : lineEnd[0];
21
+ const maxX = lineStart[0] >= lineEnd[0] ? lineStart[0] : lineEnd[0];
22
+ const minY = lineStart[1] <= lineEnd[1] ? lineStart[1] : lineEnd[1];
23
+ const maxY = lineStart[1] >= lineEnd[1] ? lineStart[1] : lineEnd[1];
24
+
25
+ // Checks if the point lies inside the AABB
26
+ const aabbContainsPoint =
27
+ point[0] >= minX - ORIENTATION_TOLERANCE &&
28
+ point[0] <= maxX + ORIENTATION_TOLERANCE &&
29
+ point[1] >= minY - ORIENTATION_TOLERANCE &&
30
+ point[1] <= maxY + ORIENTATION_TOLERANCE;
31
+
32
+ if (!aabbContainsPoint) {
33
+ return false;
34
+ }
35
+
36
+ // Now that we know the point is inside the AABB we check if it lies on the line segment
37
+ const orientation =
38
+ (lineEnd[1] - lineStart[1]) * (point[0] - lineEnd[0]) -
39
+ (lineEnd[0] - lineStart[0]) * (point[1] - lineEnd[1]);
40
+ const absOrientation = orientation >= 0 ? orientation : -orientation;
41
+
42
+ // The orientation must be zero for points that lies on the same line
43
+ return absOrientation <= ORIENTATION_TOLERANCE;
44
+ }
@@ -4,7 +4,7 @@ import getLineSegmentIntersectionsIndexes from './getLineSegmentIntersectionsInd
4
4
  import containsPoint from './containsPoint';
5
5
  import getNormal2 from './getNormal2';
6
6
  import { glMatrix, vec3 } from 'gl-matrix';
7
- import getLineSegmentsIntersection from './getLineSegmentsIntersection';
7
+ import getLinesIntersection from './getLinesIntersection';
8
8
 
9
9
  enum PolylinePointType {
10
10
  Vertex,
@@ -106,12 +106,10 @@ function getSourceAndTargetPointsList(
106
106
  const p2 = sourcePolyline[intersectedLineSegment[0]];
107
107
  const q2 = sourcePolyline[intersectedLineSegment[1]];
108
108
 
109
- // lineSegment.intersectLine returns `undefined` when the intersection
110
- // is at one of the line vertices.
111
- // Examples:
112
- // - [(0, 0), (1, 1)] x [(1, 1), (1, 2)]
113
- // - [(0, 1), (2, 1)] x [(1, 1), (1, 2)]
114
- const intersectionCoordinate = getLineSegmentsIntersection(
109
+ // lineSegment.intersectLine returns the midpoint of the four points
110
+ // when the lines are parallel or co-incident. Otherwise it will return
111
+ // an extension of the line.
112
+ const intersectionCoordinate = getLinesIntersection(
115
113
  p1,
116
114
  q1,
117
115
  p2,
@@ -1,6 +1,6 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
2
  import getLineSegmentIntersectionsIndexes from './getLineSegmentIntersectionsIndexes';
3
- import getLineSegmentsIntersection from './getLineSegmentsIntersection';
3
+ import getLinesIntersection from './getLinesIntersection';
4
4
 
5
5
  /**
6
6
  * Returns all intersections points between a line segment and a polyline
@@ -22,7 +22,7 @@ export default function getLineSegmentIntersectionsCoordinates(
22
22
  for (let i = 0; i < polylineIndexes.length; i++) {
23
23
  const p2 = points[polylineIndexes[i][0]];
24
24
  const q2 = points[polylineIndexes[i][1]];
25
- const intersection = getLineSegmentsIntersection(p1, q1, p2, q2);
25
+ const intersection = getLinesIntersection(p1, q1, p2, q2);
26
26
  result.push(intersection);
27
27
  }
28
28
 
@@ -0,0 +1,94 @@
1
+ import { Types } from '@cornerstonejs/core';
2
+ import * as mathLine from '../line';
3
+
4
+ // ATTENTION: this is an internal function and it should not be added to "polyline" namespace
5
+
6
+ // Tested with +1M random overlapping line segments and any tolerance below this
7
+ // one may return invalid results.
8
+ const PARALLEL_LINES_TOLERANCE = 1e-2;
9
+
10
+ /**
11
+ * It returns the intersection between two lines (not line segments) or a midpoint
12
+ * when the line segments overlap. This function calculates the intersection between
13
+ * lines because it considers that getFirstLineSegmentIntersectionIndexes,
14
+ * getLineSegmentIntersectionsCoordinates or getLineSegmentIntersectionsIndexes
15
+ * has already been called first which guarantees.
16
+ *
17
+ * @param p1 - Line segment 1 start
18
+ * @param q1 - Line segment 1 end
19
+ * @param p2 - Line segment 2 start
20
+ * @param q2 - Line segment 21 end
21
+ * @returns The intersection between two lines or a midpoint when they overlap
22
+ */
23
+ export default function getLinesIntersection(
24
+ p1: Types.Point2,
25
+ q1: Types.Point2,
26
+ p2: Types.Point2,
27
+ q2: Types.Point2
28
+ ) {
29
+ const diffQ1P1 = [q1[0] - p1[0], q1[1] - p1[1]];
30
+ const diffQ2P2 = [q2[0] - p2[0], q2[1] - p2[1]];
31
+ const denominator = diffQ2P2[1] * diffQ1P1[0] - diffQ2P2[0] * diffQ1P1[1];
32
+ const absDenominator = denominator >= 0 ? denominator : -denominator;
33
+
34
+ if (absDenominator < PARALLEL_LINES_TOLERANCE) {
35
+ // No Math.min/max calls for better performance.
36
+ const line1AABB = [
37
+ p1[0] < q1[0] ? p1[0] : q1[0], // 0: minX
38
+ p1[0] > q1[0] ? p1[0] : q1[0], // 1: maxX
39
+ p1[1] < q1[1] ? p1[1] : q1[1], // 2: minY
40
+ p1[1] > q1[1] ? p1[1] : q1[1], // 3: maxY
41
+ ];
42
+
43
+ // No Math.min/max calls for better performance.
44
+ const line2AABB = [
45
+ p2[0] < q2[0] ? p2[0] : q2[0], // 0: minX
46
+ p2[0] > q2[0] ? p2[0] : q2[0], // 1: maxX
47
+ p2[1] < q2[1] ? p2[1] : q2[1], // 2: minY
48
+ p2[1] > q2[1] ? p2[1] : q2[1], // 3: maxY
49
+ ];
50
+
51
+ const aabbIntersects =
52
+ line1AABB[0] <= line2AABB[1] && // minX1 <= maxX2
53
+ line1AABB[1] >= line2AABB[0] && // maxX1 >= minX2
54
+ line1AABB[2] <= line2AABB[3] && // minY1 <= maxY2
55
+ line1AABB[3] >= line2AABB[2]; // maxY1 >= minY2
56
+
57
+ if (!aabbIntersects) {
58
+ return;
59
+ }
60
+
61
+ // Three tests are enough to know if the lines overlap
62
+ const overlap =
63
+ mathLine.isPointOnLineSegment(p1, q1, p2) ||
64
+ mathLine.isPointOnLineSegment(p1, q1, q2) ||
65
+ mathLine.isPointOnLineSegment(p2, q2, p1);
66
+
67
+ if (!overlap) {
68
+ return;
69
+ }
70
+
71
+ // min/max seems to be inverted but that is correct because it is looking
72
+ // for the intersection range. No Math.min/max calls for better performance.
73
+ const minX = line1AABB[0] > line2AABB[0] ? line1AABB[0] : line2AABB[0];
74
+ const maxX = line1AABB[1] < line2AABB[1] ? line1AABB[1] : line2AABB[1];
75
+ const minY = line1AABB[2] > line2AABB[2] ? line1AABB[2] : line2AABB[2];
76
+ const maxY = line1AABB[3] < line2AABB[3] ? line1AABB[3] : line2AABB[3];
77
+ const midX = (minX + maxX) * 0.5;
78
+ const midY = (minY + maxY) * 0.5;
79
+
80
+ return [midX, midY];
81
+ }
82
+
83
+ let a = p1[1] - p2[1];
84
+ let b = p1[0] - p2[0];
85
+ const numerator1 = diffQ2P2[0] * a - diffQ2P2[1] * b;
86
+ const numerator2 = diffQ1P1[0] * a - diffQ1P1[1] * b;
87
+ a = numerator1 / denominator;
88
+ b = numerator2 / denominator;
89
+
90
+ const resultX = p1[0] + a * diffQ1P1[0];
91
+ const resultY = p1[1] + a * diffQ1P1[1];
92
+
93
+ return [resultX, resultY];
94
+ }
@@ -17,6 +17,7 @@ import deleteRelatedAnnotations from './deleteRelatedAnnotations';
17
17
  import { InterpolationROIAnnotation } from '../../../types/ToolSpecificAnnotationTypes';
18
18
  import ChangeTypes from '../../../enums/ChangeTypes';
19
19
  import getViewportForAnnotation from '../../getViewportForAnnotation';
20
+ import { addContourSegmentationAnnotation } from '../../contourSegmentation/addContourSegmentationAnnotation';
20
21
 
21
22
  const { uuidv4 } = csUtils;
22
23
 
@@ -69,12 +70,15 @@ export default class InterpolationManager {
69
70
  const annotations = annotationState.getAnnotations(
70
71
  toolName,
71
72
  annotationGroupSelector
72
- );
73
+ ) as InterpolationROIAnnotation[];
73
74
  if (!annotations?.length) {
74
75
  continue;
75
76
  }
76
77
  for (const annotation of annotations) {
77
- const { data, autoGenerated, metadata } = annotation;
78
+ const { interpolationUID, data, autoGenerated, metadata } = annotation;
79
+ if (interpolationUID) {
80
+ annotation.interpolationCompleted = true;
81
+ }
78
82
  if (!autoGenerated) {
79
83
  continue;
80
84
  }
@@ -94,21 +98,31 @@ export default class InterpolationManager {
94
98
  ) {
95
99
  continue;
96
100
  }
101
+ addContourSegmentationAnnotation(annotation);
97
102
  annotation.autoGenerated = false;
98
103
  }
99
104
  }
100
105
  }
101
106
 
107
+ /**
108
+ * When an annotation is completed, if the configuration includes interpolation,
109
+ * then find matching interpolations and interpolation between this segmentation
110
+ * and the other segmentations of the same type.
111
+ */
102
112
  static handleAnnotationCompleted = (evt: AnnotationCompletedEventType) => {
103
113
  const annotation = evt.detail.annotation as InterpolationROIAnnotation;
104
114
  if (!annotation?.metadata) {
105
115
  return;
106
116
  }
107
- const { toolName } = annotation.metadata;
117
+ const { toolName, originalToolName } = annotation.metadata;
108
118
 
109
- if (!this.toolNames.includes(toolName)) {
119
+ if (
120
+ !this.toolNames.includes(toolName) &&
121
+ !this.toolNames.includes(originalToolName)
122
+ ) {
110
123
  return;
111
124
  }
125
+ console.log('Interpolation annotation', annotation.annotationUID);
112
126
 
113
127
  const viewport = getViewportForAnnotation(annotation);
114
128
  if (!viewport) {
@@ -153,28 +167,45 @@ export default class InterpolationManager {
153
167
  viewportData,
154
168
  filterData
155
169
  );
156
- // Skip other type of annotations with same location
170
+ // Skip other type of annotation interpolationUID's that are co-located
171
+ const { sliceIndex } = annotation.metadata;
172
+ const skipUIDs = new Set<string>();
173
+ interpolationAnnotations.forEach((interpolationAnnotation) => {
174
+ if (
175
+ interpolationAnnotation.interpolationCompleted ||
176
+ interpolationAnnotation.metadata.sliceIndex === sliceIndex
177
+ ) {
178
+ const { interpolationUID } = interpolationAnnotation;
179
+ skipUIDs.add(interpolationUID);
180
+ }
181
+ });
157
182
  interpolationAnnotations = interpolationAnnotations.filter(
158
- (interpolationAnnotation) => interpolationAnnotation.interpolationUID
183
+ (interpolationAnnotation) =>
184
+ !skipUIDs.has(interpolationAnnotation.interpolationUID)
159
185
  );
160
- if (!annotation.interpolationUID) {
161
- annotation.interpolationUID =
162
- interpolationAnnotations[0]?.interpolationUID || uuidv4();
163
- viewportData.interpolationUID = annotation.interpolationUID;
164
- }
186
+
187
+ // Assign a new interpolationUID (this is checked above, so will be empty initially)
188
+ annotation.interpolationUID =
189
+ interpolationAnnotations[0]?.interpolationUID || uuidv4();
190
+ viewportData.interpolationUID = annotation.interpolationUID;
165
191
  interpolate(viewportData);
166
192
  };
167
193
 
194
+ /**
195
+ * This method gets called when an annotation changes. It will then trigger
196
+ * related already interpolated annotations to be updated with the modified data.
197
+ */
168
198
  static handleAnnotationUpdate = (evt: AnnotationModifiedEventType) => {
169
199
  const annotation = evt.detail.annotation as InterpolationROIAnnotation;
170
200
  const { changeType = ChangeTypes.HandlesUpdated } = evt.detail;
171
201
  if (!annotation?.metadata) {
172
202
  return;
173
203
  }
174
- const { toolName } = annotation.metadata;
204
+ const { toolName, originalToolName } = annotation.metadata;
175
205
 
176
206
  if (
177
- !this.toolNames.includes(toolName) ||
207
+ (!this.toolNames.includes(toolName) &&
208
+ !this.toolNames.includes(originalToolName)) ||
178
209
  !ChangeTypesForInterpolation.includes(changeType)
179
210
  ) {
180
211
  return;
@@ -188,7 +219,11 @@ export default class InterpolationManager {
188
219
  );
189
220
  return;
190
221
  }
191
- annotation.autoGenerated = false;
222
+ if (annotation.autoGenerated) {
223
+ // Dont fire the annotation changed events here, as that leads to recursion.
224
+ addContourSegmentationAnnotation(annotation);
225
+ annotation.autoGenerated = false;
226
+ }
192
227
 
193
228
  const sliceData: Types.ImageSliceData = getSliceData(viewport);
194
229
  const viewportData: InterpolationViewportData = {
@@ -201,6 +236,9 @@ export default class InterpolationManager {
201
236
  interpolate(viewportData);
202
237
  };
203
238
 
239
+ /**
240
+ * Delete interpolated annotations when their endpoints are deleted.
241
+ */
204
242
  static handleAnnotationDelete = (evt: AnnotationRemovedEventType) => {
205
243
  const annotation = evt.detail.annotation as InterpolationROIAnnotation;
206
244
  if (!annotation?.metadata) {
@@ -1,2 +0,0 @@
1
- import { Types } from '@cornerstonejs/core';
2
- export default function getLineSegmentsIntersection(p1: Types.Point2, q1: Types.Point2, p2: Types.Point2, q2: Types.Point2): Types.Point2;
@@ -1,21 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- function getLineSegmentsIntersection(p1, q1, p2, q2) {
4
- const diffQ1P1 = [q1[0] - p1[0], q1[1] - p1[1]];
5
- const diffQ2P2 = [q2[0] - p2[0], q2[1] - p2[1]];
6
- const denominator = diffQ2P2[1] * diffQ1P1[0] - diffQ2P2[0] * diffQ1P1[1];
7
- if (denominator == 0) {
8
- return;
9
- }
10
- let a = p1[1] - p2[1];
11
- let b = p1[0] - p2[0];
12
- const numerator1 = diffQ2P2[0] * a - diffQ2P2[1] * b;
13
- const numerator2 = diffQ1P1[0] * a - diffQ1P1[1] * b;
14
- a = numerator1 / denominator;
15
- b = numerator2 / denominator;
16
- const resultX = p1[0] + a * diffQ1P1[0];
17
- const resultY = p1[1] + a * diffQ1P1[1];
18
- return [resultX, resultY];
19
- }
20
- exports.default = getLineSegmentsIntersection;
21
- //# sourceMappingURL=getLineSegmentsIntersection.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getLineSegmentsIntersection.js","sourceRoot":"","sources":["../../../../../src/utilities/math/polyline/getLineSegmentsIntersection.ts"],"names":[],"mappings":";;AAqBA,SAAwB,2BAA2B,CACjD,EAAgB,EAChB,EAAgB,EAChB,EAAgB,EAChB,EAAgB;IAEhB,MAAM,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAE1E,IAAI,WAAW,IAAI,CAAC,EAAE;QACpB,OAAO;KACR;IAED,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC,GAAG,UAAU,GAAG,WAAW,CAAC;IAC7B,CAAC,GAAG,UAAU,GAAG,WAAW,CAAC;IAE7B,MAAM,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAExC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAzBD,8CAyBC"}
@@ -1,18 +0,0 @@
1
- export default function getLineSegmentsIntersection(p1, q1, p2, q2) {
2
- const diffQ1P1 = [q1[0] - p1[0], q1[1] - p1[1]];
3
- const diffQ2P2 = [q2[0] - p2[0], q2[1] - p2[1]];
4
- const denominator = diffQ2P2[1] * diffQ1P1[0] - diffQ2P2[0] * diffQ1P1[1];
5
- if (denominator == 0) {
6
- return;
7
- }
8
- let a = p1[1] - p2[1];
9
- let b = p1[0] - p2[0];
10
- const numerator1 = diffQ2P2[0] * a - diffQ2P2[1] * b;
11
- const numerator2 = diffQ1P1[0] * a - diffQ1P1[1] * b;
12
- a = numerator1 / denominator;
13
- b = numerator2 / denominator;
14
- const resultX = p1[0] + a * diffQ1P1[0];
15
- const resultY = p1[1] + a * diffQ1P1[1];
16
- return [resultX, resultY];
17
- }
18
- //# sourceMappingURL=getLineSegmentsIntersection.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getLineSegmentsIntersection.js","sourceRoot":"","sources":["../../../../../src/utilities/math/polyline/getLineSegmentsIntersection.ts"],"names":[],"mappings":"AAqBA,MAAM,CAAC,OAAO,UAAU,2BAA2B,CACjD,EAAgB,EAChB,EAAgB,EAChB,EAAgB,EAChB,EAAgB;IAEhB,MAAM,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAE1E,IAAI,WAAW,IAAI,CAAC,EAAE;QACpB,OAAO;KACR;IAED,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC,GAAG,UAAU,GAAG,WAAW,CAAC;IAC7B,CAAC,GAAG,UAAU,GAAG,WAAW,CAAC;IAE7B,MAAM,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAExC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC"}
@@ -1,3 +0,0 @@
1
- import { Types } from '@cornerstonejs/core';
2
- export default function getLineSegmentsIntersection(p1: Types.Point2, q1: Types.Point2, p2: Types.Point2, q2: Types.Point2): Types.Point2;
3
- //# sourceMappingURL=getLineSegmentsIntersection.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getLineSegmentsIntersection.d.ts","sourceRoot":"","sources":["../../../../../src/utilities/math/polyline/getLineSegmentsIntersection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAqB5C,MAAM,CAAC,OAAO,UAAU,2BAA2B,CACjD,EAAE,EAAE,KAAK,CAAC,MAAM,EAChB,EAAE,EAAE,KAAK,CAAC,MAAM,EAChB,EAAE,EAAE,KAAK,CAAC,MAAM,EAChB,EAAE,EAAE,KAAK,CAAC,MAAM,GACf,KAAK,CAAC,MAAM,CAoBd"}