@cornerstonejs/tools 1.51.4 → 1.52.0

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 (128) hide show
  1. package/dist/cjs/drawingSvg/drawPolyline.js +1 -1
  2. package/dist/cjs/drawingSvg/drawPolyline.js.map +1 -1
  3. package/dist/cjs/enums/ChangeTypes.d.ts +2 -1
  4. package/dist/cjs/enums/ChangeTypes.js +1 -0
  5. package/dist/cjs/enums/ChangeTypes.js.map +1 -1
  6. package/dist/cjs/eventDispatchers/keyboardEventHandlers/keyDown.js +1 -1
  7. package/dist/cjs/eventDispatchers/keyboardEventHandlers/keyDown.js.map +1 -1
  8. package/dist/cjs/stateManagement/annotation/helpers/state.d.ts +2 -1
  9. package/dist/cjs/stateManagement/annotation/helpers/state.js +2 -1
  10. package/dist/cjs/stateManagement/annotation/helpers/state.js.map +1 -1
  11. package/dist/cjs/tools/annotation/LivewireContourSegmentationTool.d.ts +4 -0
  12. package/dist/cjs/tools/annotation/LivewireContourSegmentationTool.js +82 -0
  13. package/dist/cjs/tools/annotation/LivewireContourSegmentationTool.js.map +1 -1
  14. package/dist/cjs/tools/annotation/LivewireContourTool.d.ts +10 -9
  15. package/dist/cjs/tools/annotation/LivewireContourTool.js +56 -43
  16. package/dist/cjs/tools/annotation/LivewireContourTool.js.map +1 -1
  17. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js +2 -1
  18. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  19. package/dist/cjs/tools/annotation/SplineROITool.js +3 -3
  20. package/dist/cjs/tools/annotation/SplineROITool.js.map +1 -1
  21. package/dist/cjs/types/ContourSegmentationAnnotation.d.ts +8 -0
  22. package/dist/cjs/types/InterpolationTypes.d.ts +2 -0
  23. package/dist/cjs/types/ToolSpecificAnnotationTypes.d.ts +2 -5
  24. package/dist/cjs/utilities/contours/interpolation/acceptAutogeneratedInterpolations.js.map +1 -1
  25. package/dist/cjs/utilities/contours/interpolation/createPolylineToolData.js +2 -1
  26. package/dist/cjs/utilities/contours/interpolation/createPolylineToolData.js.map +1 -1
  27. package/dist/cjs/utilities/contours/interpolation/findAnnotationForInterpolation.js +2 -1
  28. package/dist/cjs/utilities/contours/interpolation/findAnnotationForInterpolation.js.map +1 -1
  29. package/dist/cjs/utilities/contours/interpolation/getInterpolationData.js +3 -1
  30. package/dist/cjs/utilities/contours/interpolation/getInterpolationData.js.map +1 -1
  31. package/dist/cjs/utilities/contours/interpolation/interpolate.d.ts +8 -0
  32. package/dist/cjs/utilities/contours/interpolation/interpolate.js +56 -42
  33. package/dist/cjs/utilities/contours/interpolation/interpolate.js.map +1 -1
  34. package/dist/cjs/utilities/contours/interpolation/selectHandles.d.ts +4 -0
  35. package/dist/cjs/utilities/contours/interpolation/selectHandles.js +170 -0
  36. package/dist/cjs/utilities/contours/interpolation/selectHandles.js.map +1 -0
  37. package/dist/cjs/utilities/livewire/LivewireScissors.d.ts +2 -1
  38. package/dist/cjs/utilities/livewire/LivewireScissors.js +46 -24
  39. package/dist/cjs/utilities/livewire/LivewireScissors.js.map +1 -1
  40. package/dist/cjs/utilities/segmentation/InterpolationManager/InterpolationManager.js +13 -3
  41. package/dist/cjs/utilities/segmentation/InterpolationManager/InterpolationManager.js.map +1 -1
  42. package/dist/esm/drawingSvg/drawPolyline.js +1 -1
  43. package/dist/esm/drawingSvg/drawPolyline.js.map +1 -1
  44. package/dist/esm/enums/ChangeTypes.js +1 -0
  45. package/dist/esm/enums/ChangeTypes.js.map +1 -1
  46. package/dist/esm/eventDispatchers/keyboardEventHandlers/keyDown.js +1 -1
  47. package/dist/esm/eventDispatchers/keyboardEventHandlers/keyDown.js.map +1 -1
  48. package/dist/esm/stateManagement/annotation/helpers/state.js +3 -2
  49. package/dist/esm/stateManagement/annotation/helpers/state.js.map +1 -1
  50. package/dist/esm/tools/annotation/LivewireContourSegmentationTool.js +81 -0
  51. package/dist/esm/tools/annotation/LivewireContourSegmentationTool.js.map +1 -1
  52. package/dist/esm/tools/annotation/LivewireContourTool.js +57 -44
  53. package/dist/esm/tools/annotation/LivewireContourTool.js.map +1 -1
  54. package/dist/esm/tools/annotation/PlanarFreehandROITool.js +2 -1
  55. package/dist/esm/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  56. package/dist/esm/tools/annotation/SplineROITool.js +3 -3
  57. package/dist/esm/tools/annotation/SplineROITool.js.map +1 -1
  58. package/dist/esm/utilities/contours/interpolation/acceptAutogeneratedInterpolations.js.map +1 -1
  59. package/dist/esm/utilities/contours/interpolation/createPolylineToolData.js +2 -1
  60. package/dist/esm/utilities/contours/interpolation/createPolylineToolData.js.map +1 -1
  61. package/dist/esm/utilities/contours/interpolation/findAnnotationForInterpolation.js +2 -1
  62. package/dist/esm/utilities/contours/interpolation/findAnnotationForInterpolation.js.map +1 -1
  63. package/dist/esm/utilities/contours/interpolation/getInterpolationData.js +3 -1
  64. package/dist/esm/utilities/contours/interpolation/getInterpolationData.js.map +1 -1
  65. package/dist/esm/utilities/contours/interpolation/interpolate.js +57 -43
  66. package/dist/esm/utilities/contours/interpolation/interpolate.js.map +1 -1
  67. package/dist/esm/utilities/contours/interpolation/selectHandles.js +164 -0
  68. package/dist/esm/utilities/contours/interpolation/selectHandles.js.map +1 -0
  69. package/dist/esm/utilities/livewire/LivewireScissors.js +46 -24
  70. package/dist/esm/utilities/livewire/LivewireScissors.js.map +1 -1
  71. package/dist/esm/utilities/segmentation/InterpolationManager/InterpolationManager.js +13 -3
  72. package/dist/esm/utilities/segmentation/InterpolationManager/InterpolationManager.js.map +1 -1
  73. package/dist/types/enums/ChangeTypes.d.ts +2 -1
  74. package/dist/types/enums/ChangeTypes.d.ts.map +1 -1
  75. package/dist/types/stateManagement/annotation/helpers/state.d.ts +2 -1
  76. package/dist/types/stateManagement/annotation/helpers/state.d.ts.map +1 -1
  77. package/dist/types/tools/annotation/LivewireContourSegmentationTool.d.ts +4 -0
  78. package/dist/types/tools/annotation/LivewireContourSegmentationTool.d.ts.map +1 -1
  79. package/dist/types/tools/annotation/LivewireContourTool.d.ts +10 -9
  80. package/dist/types/tools/annotation/LivewireContourTool.d.ts.map +1 -1
  81. package/dist/types/tools/annotation/PlanarFreehandROITool.d.ts.map +1 -1
  82. package/dist/types/types/ContourSegmentationAnnotation.d.ts +8 -0
  83. package/dist/types/types/ContourSegmentationAnnotation.d.ts.map +1 -1
  84. package/dist/types/types/InterpolationTypes.d.ts +2 -0
  85. package/dist/types/types/InterpolationTypes.d.ts.map +1 -1
  86. package/dist/types/types/ToolSpecificAnnotationTypes.d.ts +2 -5
  87. package/dist/types/types/ToolSpecificAnnotationTypes.d.ts.map +1 -1
  88. package/dist/types/utilities/contours/interpolation/acceptAutogeneratedInterpolations.d.ts.map +1 -1
  89. package/dist/types/utilities/contours/interpolation/createPolylineToolData.d.ts.map +1 -1
  90. package/dist/types/utilities/contours/interpolation/findAnnotationForInterpolation.d.ts.map +1 -1
  91. package/dist/types/utilities/contours/interpolation/getInterpolationData.d.ts.map +1 -1
  92. package/dist/types/utilities/contours/interpolation/interpolate.d.ts +8 -0
  93. package/dist/types/utilities/contours/interpolation/interpolate.d.ts.map +1 -1
  94. package/dist/types/utilities/contours/interpolation/selectHandles.d.ts +5 -0
  95. package/dist/types/utilities/contours/interpolation/selectHandles.d.ts.map +1 -0
  96. package/dist/types/utilities/livewire/LivewireScissors.d.ts +2 -1
  97. package/dist/types/utilities/livewire/LivewireScissors.d.ts.map +1 -1
  98. package/dist/types/utilities/segmentation/InterpolationManager/InterpolationManager.d.ts.map +1 -1
  99. package/dist/umd/index.js +1 -1
  100. package/dist/umd/index.js.map +1 -1
  101. package/package.json +3 -3
  102. package/src/drawingSvg/drawPolyline.ts +1 -1
  103. package/src/enums/ChangeTypes.ts +4 -0
  104. package/src/eventDispatchers/keyboardEventHandlers/keyDown.ts +1 -1
  105. package/src/stateManagement/annotation/helpers/state.ts +4 -2
  106. package/src/tools/annotation/LivewireContourSegmentationTool.ts +151 -0
  107. package/src/tools/annotation/LivewireContourTool.ts +113 -82
  108. package/src/tools/annotation/PlanarFreehandROITool.ts +8 -3
  109. package/src/tools/annotation/SplineROITool.ts +3 -3
  110. package/src/types/ContourSegmentationAnnotation.ts +38 -0
  111. package/src/types/InterpolationTypes.ts +6 -0
  112. package/src/types/ToolSpecificAnnotationTypes.ts +7 -5
  113. package/src/utilities/contours/interpolation/acceptAutogeneratedInterpolations.ts +3 -1
  114. package/src/utilities/contours/interpolation/createPolylineToolData.ts +7 -1
  115. package/src/utilities/contours/interpolation/findAnnotationForInterpolation.ts +2 -1
  116. package/src/utilities/contours/interpolation/getInterpolationData.ts +3 -1
  117. package/src/utilities/contours/interpolation/interpolate.ts +94 -75
  118. package/src/utilities/contours/interpolation/selectHandles.ts +240 -0
  119. package/src/utilities/livewire/LivewireScissors.ts +65 -53
  120. package/src/utilities/segmentation/InterpolationManager/InterpolationManager.ts +39 -4
  121. package/dist/cjs/utilities/contours/PointsArray.d.ts +0 -29
  122. package/dist/cjs/utilities/contours/PointsArray.js +0 -104
  123. package/dist/cjs/utilities/contours/PointsArray.js.map +0 -1
  124. package/dist/esm/utilities/contours/PointsArray.js +0 -98
  125. package/dist/esm/utilities/contours/PointsArray.js.map +0 -1
  126. package/dist/types/utilities/contours/PointsArray.d.ts +0 -30
  127. package/dist/types/utilities/contours/PointsArray.d.ts.map +0 -1
  128. package/src/utilities/contours/PointsArray.ts +0 -165
@@ -0,0 +1,240 @@
1
+ import { vec3 } from 'gl-matrix';
2
+ import { utilities } from '@cornerstonejs/core';
3
+ import type { PointsArray3 } from './interpolate';
4
+ import { add } from '@kitware/vtk.js/Common/Core/Math';
5
+
6
+ const { PointsManager } = utilities;
7
+
8
+ /**
9
+ * Selects handles by looking for local maximums in the angle that the local
10
+ * vector makes
11
+ *
12
+ * @param polyline - an array of points, usually the polyline for the contour to
13
+ * select handles from.
14
+ * @param handleCount - a guideline for how many handles to create
15
+ */
16
+ export default function selectHandles(
17
+ polyline: PointsArray3,
18
+ handleCount = 8
19
+ ): PointsArray3 {
20
+ const handles = PointsManager.create3(handleCount) as PointsArray3;
21
+ handles.sources = [];
22
+ const { sources: destPoints } = handles;
23
+ const { length, sources: sourcePoints = [] } = polyline;
24
+ const distance = 6;
25
+ if (length < distance * 3) {
26
+ console.log('Adding subselect handles', handleCount, length);
27
+ return polyline.subselect(handleCount);
28
+ }
29
+ const interval = Math.min(30, Math.floor(length / 3));
30
+ sourcePoints.forEach(() =>
31
+ destPoints.push(PointsManager.create3(handleCount))
32
+ );
33
+
34
+ const dotValues = createDotValues(polyline, distance);
35
+
36
+ const minimumRegions = findMinimumRegions(dotValues, handleCount);
37
+ const indices = [];
38
+ if (minimumRegions?.length > 2) {
39
+ let lastHandle = -1;
40
+ const thirdInterval = interval / 3;
41
+ minimumRegions.forEach((region) => {
42
+ const [start, , end] = region;
43
+ const midIndex = Math.ceil((start + end) / 2);
44
+ if (end - lastHandle < thirdInterval) {
45
+ return;
46
+ }
47
+ if (midIndex - start > 2 * thirdInterval) {
48
+ addInterval(indices, lastHandle, start, interval, length);
49
+ lastHandle = addInterval(indices, start, midIndex, interval, length);
50
+ } else {
51
+ lastHandle = addInterval(
52
+ indices,
53
+ lastHandle,
54
+ midIndex,
55
+ interval,
56
+ length
57
+ );
58
+ }
59
+ if (end - lastHandle > thirdInterval) {
60
+ lastHandle = addInterval(indices, lastHandle, end, interval, length);
61
+ }
62
+ });
63
+ const firstHandle = indices[0];
64
+ const lastDistance = indexValue(firstHandle + length - lastHandle, length);
65
+ // Check that there is enough space between the last and first handle to
66
+ // need an extra handle.
67
+ if (lastDistance > 2 * thirdInterval) {
68
+ addInterval(
69
+ indices,
70
+ lastHandle,
71
+ // Don't add a point too close to the first handle
72
+ firstHandle - thirdInterval,
73
+ interval,
74
+ length
75
+ );
76
+ }
77
+ } else {
78
+ const interval = Math.floor(length / handleCount);
79
+ addInterval(indices, -1, length - interval, interval, length);
80
+ }
81
+
82
+ indices.forEach((index) => {
83
+ const point = polyline.getPointArray(index);
84
+ handles.push(point);
85
+ sourcePoints.forEach((source, destSourceIndex) =>
86
+ destPoints[destSourceIndex].push(source.getPoint(index))
87
+ );
88
+ });
89
+ return handles;
90
+ }
91
+
92
+ /**
93
+ * Creates an array of the dot products between each point in the points array
94
+ * and a point +/- distance from that point, unitized to vector length 1.
95
+ * That is, this is a measure of the angle at the given point, where 1 is a
96
+ * straight line, and -1 is a 180 degree angle change.
97
+ *
98
+ * @param polyline - the array of Point3 values
99
+ * @param distance - previous/next distance to use for the vectors for the dot product
100
+ * @returns - Float32Array of dot products, one per point in the source array.
101
+ */
102
+ export function createDotValues(
103
+ polyline: PointsArray3,
104
+ distance = 6
105
+ ): Float32Array {
106
+ const { length } = polyline;
107
+ const prevVec3 = vec3.create();
108
+ const nextVec3 = vec3.create();
109
+ const dotValues = new Float32Array(length);
110
+
111
+ for (let i = 0; i < length; i++) {
112
+ const point = polyline.getPoint(i);
113
+ const prevPoint = polyline.getPoint(i - distance);
114
+ const nextPoint = polyline.getPoint((i + distance) % length);
115
+ vec3.sub(prevVec3, point, prevPoint);
116
+ vec3.sub(nextVec3, nextPoint, point);
117
+ const dot =
118
+ vec3.dot(prevVec3, nextVec3) / (vec3.len(prevVec3) * vec3.len(nextVec3));
119
+ dotValues[i] = dot;
120
+ }
121
+
122
+ return dotValues;
123
+ }
124
+
125
+ /**
126
+ * Finds minimum regions in the dot products. These are detected as
127
+ * center points of the dot values regions having a minimum value - that is,
128
+ * where the direction of the line is changing fastest.
129
+ */
130
+ function findMinimumRegions(dotValues, handleCount) {
131
+ const { max, deviation } = getStats(dotValues);
132
+ const { length } = dotValues;
133
+ // Fallback for very uniform ojects.
134
+ if (deviation < 0.01 || length < handleCount * 3) {
135
+ // TODO - create handleCount evenly spaced handles
136
+ return [0, Math.floor(length / 3), Math.floor((length * 2) / 3)];
137
+ }
138
+
139
+ const inflection = [];
140
+ let pair = null;
141
+ let minValue;
142
+ let minIndex = 0;
143
+
144
+ for (let i = 0; i < length; i++) {
145
+ const dot = dotValues[i];
146
+ if (dot < max - deviation) {
147
+ if (pair) {
148
+ pair[2] = i;
149
+ if (dot < minValue) {
150
+ minValue = dot;
151
+ minIndex = i;
152
+ }
153
+ pair[1] = minIndex;
154
+ } else {
155
+ minValue = dot;
156
+ minIndex = i;
157
+ pair = [i, i, i];
158
+ }
159
+ } else {
160
+ if (pair) {
161
+ inflection.push(pair);
162
+ pair = null;
163
+ }
164
+ }
165
+ }
166
+ if (pair) {
167
+ if (inflection[0][0] === 0) {
168
+ inflection[0][0] = pair[0];
169
+ } else {
170
+ pair[1] = minIndex;
171
+ pair[2] = length - 1;
172
+ inflection.push(pair);
173
+ }
174
+ }
175
+
176
+ return inflection;
177
+ }
178
+
179
+ /**
180
+ * Adds points in between the start and finish.
181
+ * This is currently just the center point for short values and the start/center/end
182
+ * for ranges where the distance between these is at least the increment.
183
+ */
184
+ export function addInterval(indices, start, finish, interval, length) {
185
+ if (finish < start) {
186
+ // Always want a positive distance even if the long way round
187
+ finish += length;
188
+ }
189
+ const distance = finish - start;
190
+ const count = Math.ceil(distance / interval);
191
+ if (count <= 0) {
192
+ if (indices[indices.length - 1] !== finish) {
193
+ indices.push(indexValue(finish, length));
194
+ }
195
+ return finish;
196
+ }
197
+ // Don't add the start index, and always add the end index
198
+ for (let i = 1; i <= count; i++) {
199
+ const index = indexValue(start + (i * distance) / count, length);
200
+ indices.push(index);
201
+ }
202
+ return indices[indices.length - 1];
203
+ }
204
+
205
+ /**
206
+ * Gets the index value of a closed polyline, rounding the value and
207
+ * doing the module operation as required.
208
+ */
209
+ function indexValue(v, length) {
210
+ return (Math.round(v) + length) % length;
211
+ }
212
+
213
+ /**
214
+ * Gets statistics on the provided array numbers.
215
+ */
216
+ function getStats(dotValues) {
217
+ const { length } = dotValues;
218
+ let sum = 0;
219
+ let min = Infinity;
220
+ let max = -Infinity;
221
+ let sumSq = 0;
222
+ for (let i = 0; i < length; i++) {
223
+ const dot = dotValues[i];
224
+ sum += dot;
225
+ min = Math.min(min, dot);
226
+ max = Math.max(max, dot);
227
+ }
228
+ const mean = sum / length;
229
+ for (let i = 0; i < length; i++) {
230
+ const valueDiff = dotValues[i] - mean;
231
+ sumSq += valueDiff * valueDiff;
232
+ }
233
+ return {
234
+ mean,
235
+ max,
236
+ min,
237
+ sumSq,
238
+ deviation: Math.sqrt(sumSq / length),
239
+ };
240
+ }
@@ -105,45 +105,47 @@ export class LivewireScissors {
105
105
  }
106
106
 
107
107
  /**
108
- * Returns a smoothing path count for how many items to remove.
109
- * This will remove points, up to count which have a high gradient, up to
110
- * count of them where the clip value is larger than that provided.
108
+ * Finds a nearby point with a minimum cost nearby
111
109
  *
112
- * @returns Count of items to remove from the path.
110
+ * @param testPoint - to look nearby
111
+ * @param delta - how long a distance to look
112
+ * @returns A point having the minimum weighted distance from the testPoint
113
113
  */
114
- public smoothPathCount(
115
- pathPoints: Types.Point2[],
116
- lastPoint: Types.Point2,
117
- count = 5,
118
- clipValue = 0.85
119
- ) {
120
- const lastIndex =
121
- (lastPoint &&
122
- pathPoints.findIndex((point) => isEqual(point, lastPoint))) ||
123
- -1;
124
- if (pathPoints.length - lastIndex < count * 2) {
125
- // If a nearby point is clicked, just add it anyways, because that means
126
- // the user actually wants the given point
127
- return 0;
128
- }
129
- let removeCount = 0;
130
- for (
131
- let i = pathPoints.length - 1;
132
- i > pathPoints.length - count && i > 0;
133
- i--
134
- ) {
135
- const weighted = this._getWeightedDistance(
136
- pathPoints[i],
137
- pathPoints[i - 1]
138
- );
139
- if (weighted < clipValue) {
140
- return removeCount ? removeCount + 2 : 0;
114
+ public findMinNearby(testPoint: Types.Point2, delta = 2) {
115
+ const [x, y] = testPoint;
116
+ const { costs } = this;
117
+
118
+ const xRange = [
119
+ Math.max(0, x - delta),
120
+ Math.min(x + delta + 1, this.width),
121
+ ];
122
+ const yRange = [
123
+ Math.max(0, y - delta),
124
+ Math.min(y + delta + 1, this.height),
125
+ ];
126
+ let minValue = costs[this._getPointIndex(y, x)] * 0.8;
127
+
128
+ let minPoint = testPoint;
129
+ for (let xTest = xRange[0]; xTest < xRange[1]; xTest++) {
130
+ for (let yTest = yRange[0]; yTest < yRange[1]; yTest++) {
131
+ // Cost values are 0...1, with 1 being a poor choice for the
132
+ // livewire fitting - thus, we want to minimize our value, so the
133
+ // distance cost should be low for the center point.
134
+ const distanceCost =
135
+ 1 -
136
+ (Math.abs(xTest - testPoint[0]) + Math.abs(yTest - testPoint[1])) /
137
+ delta /
138
+ 2;
139
+ const weightCost = costs[this._getPointIndex(yTest, xTest)];
140
+
141
+ const weight = weightCost * 0.8 + distanceCost * 0.2;
142
+ if (weight < minValue) {
143
+ minPoint = [xTest, yTest];
144
+ minValue = weight;
145
+ }
141
146
  }
142
- removeCount++;
143
147
  }
144
- // Tried all of them, they were all too big, so assume they are really moving
145
- // along a high gradient.
146
- return 0;
148
+ return minPoint;
147
149
  }
148
150
 
149
151
  /**
@@ -281,7 +283,7 @@ export class LivewireScissors {
281
283
 
282
284
  // If it is at the end, back up one
283
285
  if (y + 1 === height) {
284
- index -= height;
286
+ index -= width;
285
287
  }
286
288
 
287
289
  return data[index] - data[index + width];
@@ -420,14 +422,9 @@ export class LivewireScissors {
420
422
  let pixelIndex = 0;
421
423
 
422
424
  for (let y = 0; y < height; y++) {
423
- for (let x = 0; x < width - 1; x++) {
425
+ for (let x = 0; x < width; x++) {
424
426
  gradX[pixelIndex++] = this._getDeltaX(x, y);
425
427
  }
426
-
427
- // Make the last column the same as the previous one because there is
428
- // no way to calculate `dx` since x+1 gets out of bounds
429
- gradX[pixelIndex] = gradX[pixelIndex - 1];
430
- pixelIndex++;
431
428
  }
432
429
 
433
430
  return gradX;
@@ -444,18 +441,12 @@ export class LivewireScissors {
444
441
  const gradY = new Float32Array(width * height);
445
442
  let pixelIndex = 0;
446
443
 
447
- for (let y = 0; y < height - 1; y++) {
444
+ for (let y = 0; y < height; y++) {
448
445
  for (let x = 0; x < width; x++) {
449
446
  gradY[pixelIndex++] = this._getDeltaY(x, y);
450
447
  }
451
448
  }
452
449
 
453
- // Make the last row the same as the previous one because there is
454
- // no way to calculate `dy` since y+1 gets out of bounds
455
- for (let len = gradY.length; pixelIndex < len; pixelIndex++) {
456
- gradY[pixelIndex] = gradY[pixelIndex - width];
457
- }
458
-
459
450
  return gradY;
460
451
  }
461
452
 
@@ -481,7 +472,7 @@ export class LivewireScissors {
481
472
  }
482
473
 
483
474
  /**
484
- * Compute the gradiant direction, in radians, between to points
475
+ * Compute the gradient direction, in radians, between two points
485
476
  *
486
477
  * @param px - Point `p` x-coordinate of point p.
487
478
  * @param py - Point `p` y-coordinate of point p.
@@ -506,23 +497,44 @@ export class LivewireScissors {
506
497
  dp = -dp;
507
498
  dq = -dq;
508
499
  }
509
-
510
500
  if (px !== qx && py !== qy) {
511
501
  // It's going diagonally between pixels
512
502
  dp *= Math.SQRT1_2;
513
503
  dq *= Math.SQRT1_2;
514
504
  }
505
+ dq = Math.min(Math.max(dq, -1), 1);
506
+
507
+ const direction =
508
+ TWO_THIRD_PI * (Math.acos(Math.min(dp, 1)) + Math.acos(dq));
509
+ if (isNaN(direction) || !isFinite(direction)) {
510
+ console.warn('Found non-direction:', px, py, qx, qy, dp, dq, direction);
511
+ return 1;
512
+ }
513
+ return direction;
514
+ }
515
515
 
516
- return TWO_THIRD_PI * (Math.acos(dp) + Math.acos(dq));
516
+ /** Gets the cost to go from A to B */
517
+ public getCost(pointA, pointB): number {
518
+ return this._getWeightedDistance(pointA, pointB);
517
519
  }
518
520
 
519
521
  /**
520
522
  * Return a weighted distance between two points
521
523
  */
522
524
  private _getWeightedDistance(pointA: Types.Point2, pointB: Types.Point2) {
523
- const { _getPointIndex: index } = this;
525
+ const { _getPointIndex: index, width, height } = this;
524
526
  const [aX, aY] = pointA;
525
527
  const [bX, bY] = pointB;
528
+ // Assign a cost of 1 to any points outside the image, prevents using invalid
529
+ // points
530
+ if (bX < 0 || bX >= width || bY < 0 || bY >= height) {
531
+ return 1;
532
+ }
533
+ // Use a cost of 0 if the point was outside and is now going inside
534
+ if (aX < 0 || aY < 0 || aX >= width || aY >= height) {
535
+ return 0;
536
+ }
537
+
526
538
  const bIndex = index(bY, bX);
527
539
 
528
540
  // Weighted distance function
@@ -20,6 +20,11 @@ import getViewportForAnnotation from '../../getViewportForAnnotation';
20
20
 
21
21
  const { uuidv4 } = csUtils;
22
22
 
23
+ const ChangeTypesForInterpolation = [
24
+ ChangeTypes.HandlesUpdated,
25
+ ChangeTypes.InterpolationUpdated,
26
+ ];
27
+
23
28
  export default class InterpolationManager {
24
29
  static toolNames = [];
25
30
 
@@ -31,13 +36,35 @@ export default class InterpolationManager {
31
36
 
32
37
  /**
33
38
  * Accepts the autogenerated interpolations, marking them as non-autogenerated.
34
- * Can provide a selector to choose which ones to accept
39
+ * Can provide a selector to choose which ones to accept.
40
+ *
41
+ * Rules for which items to select:
42
+ * 1. Only choose annotations having the same segment index and segmentationID
43
+ * 2. Exclude all contours having the same interpolation UID as any other contours
44
+ * on the same slice.
45
+ * 3. Exclude autogenerated annotations
46
+ * 4. Exclude any reset interpolationUIDs (this is a manual operation to allow
47
+ * creating a new interpolation)
48
+ * 5. Find the set of interpolationUID's remaining
49
+ * a. If the set is of size 0, assign a new interpolationUID
50
+ * b. If the set is of size 1, assign that interpolationUID
51
+ * c. Otherwise (optional, otherwise do b for size>1 randomly),
52
+ * for every remaining annotation, find the one whose center
53
+ * point is closest to the center point of the new annotation.
54
+ * Choose that interpolationUID
55
+ *
56
+ * To allow creating new interpolated groups, the idea is to just use a new
57
+ * segment index, then have an operation to update the segment index of an
58
+ * interpolation set. That way the user can easily draw/see the difference,
59
+ * and then merge them as required.
60
+ * However, the base rules allow creating two contours on a single image to
61
+ * create a separate set.
35
62
  */
36
63
  static acceptAutoGenerated(
37
64
  annotationGroupSelector: AnnotationGroupSelector,
38
65
  selector: AcceptInterpolationSelector = {}
39
66
  ) {
40
- const { toolNames, segmentationId, segmentIndex } = selector;
67
+ const { toolNames, segmentationId, segmentIndex, sliceIndex } = selector;
41
68
  for (const toolName of toolNames || InterpolationManager.toolNames) {
42
69
  const annotations = annotationState.getAnnotations(
43
70
  toolName,
@@ -47,13 +74,20 @@ export default class InterpolationManager {
47
74
  continue;
48
75
  }
49
76
  for (const annotation of annotations) {
50
- const { data, autoGenerated } = annotation;
77
+ const { data, autoGenerated, metadata } = annotation;
51
78
  if (!autoGenerated) {
52
79
  continue;
53
80
  }
54
81
  if (segmentIndex && segmentIndex !== data.segmentation.segmentIndex) {
55
82
  continue;
56
83
  }
84
+ if (
85
+ sliceIndex !== undefined &&
86
+ metadata &&
87
+ sliceIndex !== (metadata as any).referencedSliceIndex
88
+ ) {
89
+ continue;
90
+ }
57
91
  if (
58
92
  segmentationId &&
59
93
  segmentationId !== data.segmentation.segmentationId
@@ -141,7 +175,7 @@ export default class InterpolationManager {
141
175
 
142
176
  if (
143
177
  !this.toolNames.includes(toolName) ||
144
- changeType !== ChangeTypes.HandlesUpdated
178
+ !ChangeTypesForInterpolation.includes(changeType)
145
179
  ) {
146
180
  return;
147
181
  }
@@ -162,6 +196,7 @@ export default class InterpolationManager {
162
196
  sliceData,
163
197
  annotation,
164
198
  interpolationUID: annotation.interpolationUID,
199
+ isInterpolationUpdate: changeType === ChangeTypes.InterpolationUpdated,
165
200
  };
166
201
  interpolate(viewportData);
167
202
  };
@@ -1,29 +0,0 @@
1
- import type { Types } from '@cornerstonejs/core';
2
- export declare type PolyDataPointConfiguration = {
3
- dimensions?: 2 | 3;
4
- initialSize?: number;
5
- };
6
- export declare abstract class PointsArray<T> {
7
- data: Float32Array;
8
- dimensions: number;
9
- length: number;
10
- constructor(configuration?: PolyDataPointConfiguration);
11
- protected forEach(func: (value: T, index: number) => void, point: T): void;
12
- abstract getPoint(index: number, point: T): T;
13
- reverse(): void;
14
- protected map(f: (value: any, index: number) => T, factory: (index: number) => T): any[];
15
- static fromXYZ({ x, y, z }: Types.PointsXYZ): PointsArray3;
16
- }
17
- export declare class PointsArray2 extends PointsArray<Types.Point2> {
18
- constructor(configuration?: {});
19
- forEach(func: (value: Types.Point2, index: number) => void, point?: Types.Point2): void;
20
- getPoint(index: number, point?: Types.Point2): Types.Point2;
21
- get points(): any[];
22
- }
23
- export declare class PointsArray3 extends PointsArray<Types.Point3> {
24
- constructor(configuration?: {});
25
- forEach(func: (value: Types.Point3, index: number) => void, point?: Types.Point3): void;
26
- getPoint(index: number, point?: Types.Point3): Types.Point3;
27
- get points(): any[];
28
- getXYZ(): Types.PointsXYZ;
29
- }
@@ -1,104 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PointsArray3 = exports.PointsArray2 = exports.PointsArray = void 0;
4
- const gl_matrix_1 = require("gl-matrix");
5
- class PointsArray {
6
- constructor(configuration = {}) {
7
- this.dimensions = 3;
8
- this.length = 0;
9
- const { initialSize = 1024, dimensions = 3 } = configuration;
10
- this.data = new Float32Array(initialSize * dimensions);
11
- this.dimensions = dimensions;
12
- }
13
- forEach(func, point) {
14
- for (let i = 0; i < this.length; i++) {
15
- func(this.getPoint(i, point), i);
16
- }
17
- }
18
- reverse() {
19
- const midLength = Math.floor(this.length / 2);
20
- for (let i = 0; i < midLength; i++) {
21
- const indexStart = i * this.dimensions;
22
- const indexEnd = (this.length - 1 - i) * this.dimensions;
23
- for (let dimension = 0; dimension < this.dimensions; dimension++) {
24
- const valueStart = this.data[indexStart + dimension];
25
- this.data[indexStart + dimension] = this.data[indexEnd + dimension];
26
- this.data[indexEnd + dimension] = valueStart;
27
- }
28
- }
29
- }
30
- map(f, factory) {
31
- const mapData = [];
32
- for (let i = 0; i < this.length; i++) {
33
- mapData.push(f(this.getPoint(i, factory(i)), i));
34
- }
35
- return mapData;
36
- }
37
- static fromXYZ({ x, y, z }) {
38
- const array = new PointsArray3({ initialSize: x.length });
39
- let offset = 0;
40
- for (let i = 0; i < x.length; i++) {
41
- array.data[offset++] = x[i];
42
- array.data[offset++] = y[i];
43
- array.data[offset++] = z[i];
44
- }
45
- array.length = x.length;
46
- return array;
47
- }
48
- }
49
- exports.PointsArray = PointsArray;
50
- class PointsArray2 extends PointsArray {
51
- constructor(configuration = {}) {
52
- super(Object.assign(Object.assign({}, configuration), { dimensions: 2 }));
53
- }
54
- forEach(func, point = gl_matrix_1.vec2.create()) {
55
- super.forEach(func, point);
56
- }
57
- getPoint(index, point = gl_matrix_1.vec2.create()) {
58
- if (index >= this.length) {
59
- return;
60
- }
61
- const index2 = index * 2;
62
- point[0] = this.data[index2];
63
- point[1] = this.data[index2 + 1];
64
- return point;
65
- }
66
- get points() {
67
- return this.map((point) => point, () => gl_matrix_1.vec2.create());
68
- }
69
- }
70
- exports.PointsArray2 = PointsArray2;
71
- class PointsArray3 extends PointsArray {
72
- constructor(configuration = {}) {
73
- super(Object.assign(Object.assign({}, configuration), { dimensions: 3 }));
74
- }
75
- forEach(func, point = gl_matrix_1.vec3.create()) {
76
- super.forEach(func, point);
77
- }
78
- getPoint(index, point = gl_matrix_1.vec3.create()) {
79
- if (index >= this.length) {
80
- return;
81
- }
82
- const index2 = index * 3;
83
- point[0] = this.data[index2];
84
- point[1] = this.data[index2 + 1];
85
- point[2] = this.data[index2 + 2];
86
- return point;
87
- }
88
- get points() {
89
- return this.map((point) => point, () => gl_matrix_1.vec3.create());
90
- }
91
- getXYZ() {
92
- const x = [];
93
- const y = [];
94
- const z = [];
95
- this.forEach((point) => {
96
- x.push(point[0]);
97
- y.push(point[1]);
98
- z.push(point[2]);
99
- });
100
- return { x, y, z };
101
- }
102
- }
103
- exports.PointsArray3 = PointsArray3;
104
- //# sourceMappingURL=PointsArray.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PointsArray.js","sourceRoot":"","sources":["../../../../src/utilities/contours/PointsArray.ts"],"names":[],"mappings":";;;AACA,yCAAuC;AAevC,MAAsB,WAAW;IAK/B,YAAY,gBAA4C,EAAE;QAH1D,eAAU,GAAG,CAAC,CAAC;QACR,WAAM,GAAG,CAAC,CAAC;QAGhB,MAAM,EAAE,WAAW,GAAG,IAAI,EAAE,UAAU,GAAG,CAAC,EAAE,GAAG,aAAa,CAAC;QAC7D,IAAI,CAAC,IAAI,GAAG,IAAI,YAAY,CAAC,WAAW,GAAG,UAAU,CAAC,CAAC;QACvD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAES,OAAO,CAAC,IAAuC,EAAE,KAAQ;QACjE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACpC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;SAClC;IACH,CAAC;IAOM,OAAO;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAE9C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE;YAClC,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC;YACvC,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC;YACzD,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE;gBAChE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;gBACrD,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;gBACpE,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC,GAAG,UAAU,CAAC;aAC9C;SACF;IACH,CAAC;IAES,GAAG,CAAC,CAA8B,EAAE,OAA6B;QACzE,MAAM,OAAO,GAAG,EAAE,CAAC;QACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;SAClD;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAGM,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAmB;QAChD,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1D,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACjC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;SAC7B;QACD,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAxDD,kCAwDC;AAQD,MAAa,YAAa,SAAQ,WAAyB;IACzD,YAAY,aAAa,GAAG,EAAE;QAC5B,KAAK,iCAAM,aAAa,KAAE,UAAU,EAAE,CAAC,IAAG,CAAC;IAC7C,CAAC;IAEM,OAAO,CACZ,IAAkD,EAClD,QAAQ,gBAAI,CAAC,MAAM,EAAkB;QAErC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IAEM,QAAQ,CAAC,KAAa,EAAE,QAAQ,gBAAI,CAAC,MAAM,EAAkB;QAClE,IAAI,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE;YACxB,OAAO;SACR;QACD,MAAM,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC;QACzB,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7B,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,GAAG,CACb,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,EAChB,GAAG,EAAE,CAAC,gBAAI,CAAC,MAAM,EAAkB,CACpC,CAAC;IACJ,CAAC;CACF;AA5BD,oCA4BC;AAeD,MAAa,YAAa,SAAQ,WAAyB;IACzD,YAAY,aAAa,GAAG,EAAE;QAC5B,KAAK,iCAAM,aAAa,KAAE,UAAU,EAAE,CAAC,IAAG,CAAC;IAC7C,CAAC;IAEM,OAAO,CACZ,IAAkD,EAClD,QAAQ,gBAAI,CAAC,MAAM,EAAkB;QAErC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IAEM,QAAQ,CAAC,KAAa,EAAE,QAAQ,gBAAI,CAAC,MAAM,EAAkB;QAClE,IAAI,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE;YACxB,OAAO;SACR;QACD,MAAM,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC;QACzB,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7B,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,GAAG,CACb,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,EAChB,GAAG,EAAE,CAAC,gBAAI,CAAC,MAAM,EAAkB,CACpC,CAAC;IACJ,CAAC;IAEM,MAAM;QACX,MAAM,CAAC,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACrB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACjB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACjB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;QACH,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;IACrB,CAAC;CACF;AAzCD,oCAyCC"}