@cornerstonejs/tools 1.54.1 → 1.55.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 (86) hide show
  1. package/dist/cjs/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.js +72 -74
  2. package/dist/cjs/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.js.map +1 -1
  3. package/dist/cjs/tools/SegmentationIntersectionTool.js +2 -1
  4. package/dist/cjs/tools/SegmentationIntersectionTool.js.map +1 -1
  5. package/dist/cjs/tools/annotation/LivewireContourTool.js +5 -2
  6. package/dist/cjs/tools/annotation/LivewireContourTool.js.map +1 -1
  7. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js +4 -0
  8. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  9. package/dist/cjs/tools/annotation/SplineROITool.js +5 -2
  10. package/dist/cjs/tools/annotation/SplineROITool.js.map +1 -1
  11. package/dist/cjs/tools/annotation/planarFreehandROITool/drawLoop.js +2 -3
  12. package/dist/cjs/tools/annotation/planarFreehandROITool/drawLoop.js.map +1 -1
  13. package/dist/cjs/tools/base/ContourBaseTool.d.ts +8 -0
  14. package/dist/cjs/tools/base/ContourBaseTool.js +13 -2
  15. package/dist/cjs/tools/base/ContourBaseTool.js.map +1 -1
  16. package/dist/cjs/tools/displayTools/Surface/addOrUpdateSurfaceToElement.js +4 -3
  17. package/dist/cjs/tools/displayTools/Surface/addOrUpdateSurfaceToElement.js.map +1 -1
  18. package/dist/cjs/tools/displayTools/Surface/surfaceDisplay.js +4 -1
  19. package/dist/cjs/tools/displayTools/Surface/surfaceDisplay.js.map +1 -1
  20. package/dist/cjs/utilities/contours/updateContourPolyline.d.ts +5 -0
  21. package/dist/cjs/utilities/contours/updateContourPolyline.js +7 -2
  22. package/dist/cjs/utilities/contours/updateContourPolyline.js.map +1 -1
  23. package/dist/cjs/utilities/math/line/distanceToPointSquaredInfo.js.map +1 -1
  24. package/dist/cjs/utilities/math/polyline/decimate.d.ts +2 -0
  25. package/dist/cjs/utilities/math/polyline/decimate.js +73 -0
  26. package/dist/cjs/utilities/math/polyline/decimate.js.map +1 -0
  27. package/dist/cjs/utilities/math/polyline/index.d.ts +2 -1
  28. package/dist/cjs/utilities/math/polyline/index.js +3 -1
  29. package/dist/cjs/utilities/math/polyline/index.js.map +1 -1
  30. package/dist/esm/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.js +1 -1
  31. package/dist/esm/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.js.map +1 -1
  32. package/dist/esm/tools/SegmentationIntersectionTool.js +3 -2
  33. package/dist/esm/tools/SegmentationIntersectionTool.js.map +1 -1
  34. package/dist/esm/tools/annotation/LivewireContourTool.js +5 -2
  35. package/dist/esm/tools/annotation/LivewireContourTool.js.map +1 -1
  36. package/dist/esm/tools/annotation/PlanarFreehandROITool.js +4 -0
  37. package/dist/esm/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  38. package/dist/esm/tools/annotation/SplineROITool.js +5 -2
  39. package/dist/esm/tools/annotation/SplineROITool.js.map +1 -1
  40. package/dist/esm/tools/annotation/planarFreehandROITool/drawLoop.js +2 -3
  41. package/dist/esm/tools/annotation/planarFreehandROITool/drawLoop.js.map +1 -1
  42. package/dist/esm/tools/base/ContourBaseTool.js +10 -0
  43. package/dist/esm/tools/base/ContourBaseTool.js.map +1 -1
  44. package/dist/esm/tools/displayTools/Surface/addOrUpdateSurfaceToElement.js +4 -3
  45. package/dist/esm/tools/displayTools/Surface/addOrUpdateSurfaceToElement.js.map +1 -1
  46. package/dist/esm/tools/displayTools/Surface/surfaceDisplay.js +5 -2
  47. package/dist/esm/tools/displayTools/Surface/surfaceDisplay.js.map +1 -1
  48. package/dist/esm/utilities/contours/updateContourPolyline.js +6 -2
  49. package/dist/esm/utilities/contours/updateContourPolyline.js.map +1 -1
  50. package/dist/esm/utilities/math/line/distanceToPointSquaredInfo.js.map +1 -1
  51. package/dist/esm/utilities/math/polyline/decimate.js +47 -0
  52. package/dist/esm/utilities/math/polyline/decimate.js.map +1 -0
  53. package/dist/esm/utilities/math/polyline/index.js +2 -1
  54. package/dist/esm/utilities/math/polyline/index.js.map +1 -1
  55. package/dist/types/tools/SegmentationIntersectionTool.d.ts.map +1 -1
  56. package/dist/types/tools/annotation/LivewireContourTool.d.ts.map +1 -1
  57. package/dist/types/tools/annotation/PlanarFreehandROITool.d.ts.map +1 -1
  58. package/dist/types/tools/annotation/SplineROITool.d.ts.map +1 -1
  59. package/dist/types/tools/annotation/planarFreehandROITool/drawLoop.d.ts.map +1 -1
  60. package/dist/types/tools/base/ContourBaseTool.d.ts +8 -0
  61. package/dist/types/tools/base/ContourBaseTool.d.ts.map +1 -1
  62. package/dist/types/tools/displayTools/Surface/addOrUpdateSurfaceToElement.d.ts.map +1 -1
  63. package/dist/types/tools/displayTools/Surface/surfaceDisplay.d.ts.map +1 -1
  64. package/dist/types/utilities/contours/updateContourPolyline.d.ts +5 -0
  65. package/dist/types/utilities/contours/updateContourPolyline.d.ts.map +1 -1
  66. package/dist/types/utilities/math/line/distanceToPointSquaredInfo.d.ts.map +1 -1
  67. package/dist/types/utilities/math/polyline/decimate.d.ts +3 -0
  68. package/dist/types/utilities/math/polyline/decimate.d.ts.map +1 -0
  69. package/dist/types/utilities/math/polyline/index.d.ts +2 -1
  70. package/dist/types/utilities/math/polyline/index.d.ts.map +1 -1
  71. package/dist/umd/index.js +1 -1
  72. package/dist/umd/index.js.map +1 -1
  73. package/package.json +3 -3
  74. package/src/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.ts +1 -1
  75. package/src/tools/SegmentationIntersectionTool.ts +3 -2
  76. package/src/tools/annotation/LivewireContourTool.ts +15 -2
  77. package/src/tools/annotation/PlanarFreehandROITool.ts +12 -0
  78. package/src/tools/annotation/SplineROITool.ts +13 -2
  79. package/src/tools/annotation/planarFreehandROITool/drawLoop.ts +2 -3
  80. package/src/tools/base/ContourBaseTool.ts +23 -0
  81. package/src/tools/displayTools/Surface/addOrUpdateSurfaceToElement.ts +6 -4
  82. package/src/tools/displayTools/Surface/surfaceDisplay.ts +10 -3
  83. package/src/utilities/contours/updateContourPolyline.ts +23 -1
  84. package/src/utilities/math/line/distanceToPointSquaredInfo.ts +2 -1
  85. package/src/utilities/math/polyline/decimate.ts +105 -0
  86. package/src/utilities/math/polyline/index.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/tools",
3
- "version": "1.54.1",
3
+ "version": "1.55.0",
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.54.1",
32
+ "@cornerstonejs/core": "^1.55.0",
33
33
  "@icr/polyseg-wasm": "0.4.0",
34
34
  "comlink": "^4.4.1",
35
35
  "lodash.clonedeep": "4.5.0",
@@ -55,5 +55,5 @@
55
55
  "type": "individual",
56
56
  "url": "https://ohif.org/donate"
57
57
  },
58
- "gitHead": "3cea03f5216a14ff0e24802a393fab97ba7e0b7c"
58
+ "gitHead": "78d3c2fd9e2cd07d1ec5b84201f9f909ff6ce7cf"
59
59
  }
@@ -242,7 +242,7 @@ function getContourHolesData(
242
242
  });
243
243
  }
244
244
 
245
- async function combinePolylines(
245
+ function combinePolylines(
246
246
  viewport: Types.IViewport,
247
247
  targetAnnotation: ContourSegmentationAnnotation,
248
248
  targetPolyline: Types.Point2[],
@@ -5,7 +5,7 @@ import {
5
5
  addAnnotation,
6
6
  getAnnotations,
7
7
  } from '../stateManagement/annotation/annotationState';
8
- import { drawPolyline } from '../drawingSvg';
8
+ import { drawPath } from '../drawingSvg';
9
9
  import { getToolGroup } from '../store/ToolGroupManager';
10
10
  import triggerAnnotationRenderForViewportIds from '../utilities/triggerAnnotationRenderForViewportIds';
11
11
  import { PublicToolProps, ToolProps, SVGDrawingHelper } from '../types';
@@ -147,10 +147,11 @@ class SegmentationIntersectionTool extends AnnotationDisplayTool {
147
147
  fillColor: color,
148
148
  fillOpacity: this.configuration.opacity,
149
149
  closePath: true,
150
+ lineWidth: 2,
150
151
  };
151
152
 
152
153
  const polyLineUID = actorEntry.uid + '#' + polyLineIdx;
153
- drawPolyline(
154
+ drawPath(
154
155
  svgDrawingHelper,
155
156
  annotationUID,
156
157
  polyLineUID,
@@ -32,7 +32,6 @@ import { LivewireScissors } from '../../utilities/livewire/LivewireScissors';
32
32
  import { LivewirePath } from '../../utilities/livewire/LiveWirePath';
33
33
  import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
34
34
  import ContourSegmentationBaseTool from '../base/ContourSegmentationBaseTool';
35
- import updateContourPolyline from '../../utilities/contours/updateContourPolyline';
36
35
 
37
36
  const CLICK_CLOSE_CURVE_SQR_DIST = 10 ** 2; // px
38
37
 
@@ -108,6 +107,20 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
108
107
  */
109
108
  showInterpolationPolyline: false,
110
109
  },
110
+
111
+ /**
112
+ * The polyline may get processed in order to reduce the number of points
113
+ * for better performance and storage.
114
+ */
115
+ decimate: {
116
+ enabled: false,
117
+ /** A maximum given distance 'epsilon' to decide if a point should or
118
+ * shouldn't be added the resulting polyline which will have a lower
119
+ * number of points for higher `epsilon` values.
120
+ */
121
+ epsilon: 0.1,
122
+ },
123
+
111
124
  actions: {
112
125
  undo: {
113
126
  method: 'undo',
@@ -922,7 +935,7 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
922
935
  imagePoints = [...imagePoints, imagePoints[0]];
923
936
  }
924
937
 
925
- updateContourPolyline(
938
+ this.updateContourPolyline(
926
939
  annotation,
927
940
  {
928
941
  points: imagePoints,
@@ -231,6 +231,18 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
231
231
  // interpolation is complete.
232
232
  onInterpolationComplete: null,
233
233
  },
234
+ /**
235
+ * The polyline may get processed in order to reduce the number of points
236
+ * for better performance and storage.
237
+ */
238
+ decimate: {
239
+ enabled: false,
240
+ /** A maximum given distance 'epsilon' to decide if a point should or
241
+ * shouldn't be added the resulting polyline which will have a lower
242
+ * number of points for higher `epsilon` values.
243
+ */
244
+ epsilon: 0.1,
245
+ },
234
246
  calculateStats: false,
235
247
  getTextLines: defaultGetTextLines,
236
248
  statsCalculator: BasicStatsCalculator,
@@ -56,7 +56,6 @@ import { LinearSpline } from './splines/LinearSpline';
56
56
  import { CatmullRomSpline } from './splines/CatmullRomSpline';
57
57
  import { BSpline } from './splines/BSpline';
58
58
  import ContourSegmentationBaseTool from '../base/ContourSegmentationBaseTool';
59
- import updateContourPolyline from '../../utilities/contours/updateContourPolyline';
60
59
 
61
60
  const SPLINE_MIN_POINTS = 3;
62
61
  const SPLINE_CLICK_CLOSE_CURVE_DIST = 10;
@@ -121,6 +120,18 @@ class SplineROITool extends ContourSegmentationBaseTool {
121
120
  * modifier must be pressed when the first point of a new contour is added.
122
121
  */
123
122
  contourHoleAdditionModifierKey: KeyboardBindings.Shift,
123
+ /**
124
+ * The polyline may get processed in order to reduce the number of points
125
+ * for better performance and storage.
126
+ */
127
+ decimate: {
128
+ enabled: false,
129
+ /** A maximum given distance 'epsilon' to decide if a point should or
130
+ * shouldn't be added the resulting polyline which will have a lower
131
+ * number of points for higher `epsilon` values.
132
+ */
133
+ epsilon: 0.1,
134
+ },
124
135
  spline: {
125
136
  configuration: {
126
137
  [SplineTypesEnum.Cardinal]: {
@@ -693,7 +704,7 @@ class SplineROITool extends ContourSegmentationBaseTool {
693
704
  const spline = this._updateSplineInstance(element, annotation);
694
705
  const splinePolylineCanvas = spline.getPolylinePoints();
695
706
 
696
- updateContourPolyline(
707
+ this.updateContourPolyline(
697
708
  annotation,
698
709
  {
699
710
  points: splinePolylineCanvas,
@@ -18,7 +18,6 @@ import { PlanarFreehandROIAnnotation } from '../../../types/ToolSpecificAnnotati
18
18
  import findOpenUShapedContourVectorToPeak from './findOpenUShapedContourVectorToPeak';
19
19
  import { polyline } from '../../../utilities/math';
20
20
  import { removeAnnotation } from '../../../stateManagement/annotation/annotationState';
21
- import { updateContourPolyline } from '../../../utilities/contours/';
22
21
  import reverseIfAntiClockwise from '../../../utilities/contours/reverseIfAntiClockwise';
23
22
 
24
23
  const {
@@ -238,7 +237,7 @@ function completeDrawClosedContour(
238
237
  // contours. A future optimization if we use this for segmentation is to re-do
239
238
  // this rendering with the GPU rather than SVG.
240
239
 
241
- updateContourPolyline(
240
+ this.updateContourPolyline(
242
241
  annotation,
243
242
  {
244
243
  points: updatedPoints,
@@ -315,7 +314,7 @@ function completeDrawOpenContour(
315
314
  // contours. A future optimisation if we use this for segmentation is to re-do
316
315
  // this rendering with the GPU rather than SVG.
317
316
 
318
- updateContourPolyline(
317
+ this.updateContourPolyline(
319
318
  annotation,
320
319
  {
321
320
  points: updatedPoints,
@@ -17,7 +17,9 @@ import type {
17
17
  import { drawPath as drawPathSvg } from '../../drawingSvg';
18
18
  import { StyleSpecifier } from '../../types/AnnotationStyle';
19
19
  import AnnotationTool from './AnnotationTool';
20
+ import { updateContourPolyline } from '../../utilities/contours/';
20
21
  import { getContourHolesDataCanvas } from '../../utilities/contours';
22
+ import { ContourWindingDirection } from '../../types/ContourAnnotation';
21
23
 
22
24
  /**
23
25
  * A contour base class responsible for rendering contour instances such as
@@ -210,6 +212,27 @@ abstract class ContourBaseTool extends AnnotationTool {
210
212
  );
211
213
  }
212
214
 
215
+ protected updateContourPolyline(
216
+ annotation: ContourAnnotation,
217
+ polylineData: {
218
+ points: Types.Point2[];
219
+ closed?: boolean;
220
+ targetWindingDirection?: ContourWindingDirection;
221
+ },
222
+ transforms: {
223
+ canvasToWorld: (point: Types.Point2) => Types.Point3;
224
+ }
225
+ ) {
226
+ const decimateConfig = this.configuration?.decimate || {};
227
+
228
+ updateContourPolyline(annotation, polylineData, transforms, {
229
+ decimate: {
230
+ enabled: !!decimateConfig.enabled,
231
+ epsilon: decimateConfig.epsilon,
232
+ },
233
+ });
234
+ }
235
+
213
236
  /**
214
237
  * Get polyline points in world space.
215
238
  * Just to give a chance for child classes to override it.
@@ -105,6 +105,9 @@ function addOrUpdateSurfaceToElement(
105
105
  const filteredData = clippingFilter.getOutputData();
106
106
  mapper.setInputData(filteredData);
107
107
 
108
+ // @ts-ignore
109
+ const viewportPlanes = viewport.getClippingPlanesForActor?.();
110
+
108
111
  const evt = {
109
112
  detail: {
110
113
  actorEntry: {
@@ -114,9 +117,7 @@ function addOrUpdateSurfaceToElement(
114
117
  clippingFilter,
115
118
  uid: actorUID,
116
119
  },
117
- // @ts-ignore
118
- vtkPlanes: viewport.getClippingPlanesForActor?.(),
119
- viewport,
120
+ vtkPlanes: viewportPlanes,
120
121
  },
121
122
  };
122
123
 
@@ -147,6 +148,7 @@ function addOrUpdateSurfaceToElement(
147
148
  });
148
149
 
149
150
  viewport.resetCamera();
151
+ viewport.render();
150
152
 
151
153
  setTimeout(() => {
152
154
  viewport.getRenderer().resetCameraClippingRange();
@@ -158,7 +160,7 @@ function addOrUpdateSurfaceToElement(
158
160
  */
159
161
  function updateSurfacePlanes(evt) {
160
162
  const { actorEntry, vtkPlanes, viewport } = evt.detail;
161
- if (!actorEntry?.clippingFilter) {
163
+ if (!actorEntry?.clippingFilter || vtkPlanes.length === 0) {
162
164
  return;
163
165
  }
164
166
  const sliceIndex = viewport.getSliceIndex();
@@ -5,11 +5,11 @@ import {
5
5
  getEnabledElementByIds,
6
6
  Types,
7
7
  Enums,
8
- VolumeViewport,
9
8
  getWebWorkerManager,
10
9
  eventTarget,
11
10
  triggerEvent,
12
11
  utilities,
12
+ VolumeViewport3D,
13
13
  } from '@cornerstonejs/core';
14
14
 
15
15
  import * as SegmentationState from '../../../stateManagement/segmentation/segmentationState';
@@ -166,7 +166,7 @@ async function render(
166
166
  surfaces.push(surface);
167
167
  });
168
168
 
169
- if (viewport instanceof VolumeViewport) {
169
+ if (!(viewport instanceof VolumeViewport3D)) {
170
170
  // const { viewPlaneNormal } = viewport.getCamera();
171
171
  // currentViewportNormal.set(surface.id, structuredClone(viewPlaneNormal));
172
172
  // if the viewport is not 3D means we should calculate
@@ -208,7 +208,7 @@ function _removeSurfaceFromToolGroupViewports(
208
208
 
209
209
  async function generateAndCacheClippedSurfaces(
210
210
  surfaces: Types.ISurface[],
211
- viewport: Types.IVolumeViewport,
211
+ viewport: Types.IVolumeViewport | Types.IStackViewport,
212
212
  segmentationRepresentationUID: string
213
213
  ) {
214
214
  registerDisplayToolsWorker();
@@ -220,6 +220,13 @@ async function generateAndCacheClippedSurfaces(
220
220
  // @ts-ignore
221
221
  const planesInfo = viewport.getSlicesClippingPlanes?.();
222
222
 
223
+ if (!planesInfo) {
224
+ // this means it is probably the stack viewport not being ready
225
+ // in terms of planes which we should wait for the first render to
226
+ // get the planes
227
+ return;
228
+ }
229
+
223
230
  // @ts-ignore
224
231
  const currentSliceIndex = viewport.getSliceIndex();
225
232
 
@@ -14,6 +14,12 @@ import {
14
14
  * @param viewport - Viewport
15
15
  * @param polylineData - Polyline data (points, winding direction and closed)
16
16
  * @param transforms - Methods to convert points to/from canvas and world spaces
17
+ * @param options - Options
18
+ * - decimate: allow to set some parameters to decimate the polyline reducing
19
+ * the amount of points stored which also affects how fast it will draw the
20
+ * annotation in a viewport, compute the winding direction, append/remove
21
+ * contours and create holes. A higher `epsilon` value results in a polyline
22
+ * with less points.
17
23
  */
18
24
  export default function updateContourPolyline(
19
25
  annotation: ContourAnnotation,
@@ -24,11 +30,27 @@ export default function updateContourPolyline(
24
30
  },
25
31
  transforms: {
26
32
  canvasToWorld: (point: Types.Point2) => Types.Point3;
33
+ },
34
+ options?: {
35
+ decimate?: {
36
+ enabled?: boolean;
37
+ epsilon?: number;
38
+ };
27
39
  }
28
40
  ) {
29
41
  const { canvasToWorld } = transforms;
30
42
  const { data } = annotation;
31
- const { points: polyline, targetWindingDirection } = polylineData;
43
+ const { targetWindingDirection } = polylineData;
44
+ let { points: polyline } = polylineData;
45
+
46
+ // Decimate the polyline to reduce tha amount of points
47
+ if (options?.decimate?.enabled) {
48
+ polyline = math.polyline.decimate(
49
+ polylineData.points,
50
+ options?.decimate?.epsilon
51
+ );
52
+ }
53
+
32
54
  let { closed } = polylineData;
33
55
  const numPoints = polyline.length;
34
56
  const polylineWorldPoints = new Array(numPoints);
@@ -24,7 +24,8 @@ export default function distanceToPointSquaredInfo(
24
24
  let closestPoint: Types.Point2;
25
25
  const distanceSquared = math.point.distanceToPointSquared(lineStart, lineEnd);
26
26
 
27
- // Check if lineStart is the same as lineEnd which means
27
+ // Check if lineStart equal to the lineEnd which means the closest point
28
+ // is any of these two points
28
29
  if (lineStart[0] === lineEnd[0] && lineStart[1] === lineEnd[1]) {
29
30
  closestPoint = lineStart;
30
31
  }
@@ -0,0 +1,105 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+ import * as mathLine from '../line';
3
+
4
+ const DEFAULT_EPSILON = 0.1;
5
+
6
+ /**
7
+ * Ramer–Douglas–Peucker algorithm implementation to decimate a polyline
8
+ * to a similar polyline with fewer points
9
+ *
10
+ * https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
11
+ * https://rosettacode.org/wiki/Ramer-Douglas-Peucker_line_simplification
12
+ * https://karthaus.nl/rdp/
13
+ *
14
+ * @param polyline - Polyline to decimate
15
+ * @param epsilon - A maximum given distance 'epsilon' to decide if a point
16
+ * should or shouldn't be added the decimated polyline version. In each
17
+ * iteration the polyline is split into two polylines and the distance of each
18
+ * point from those new polylines are checked against the line that connects
19
+ * the first and last points.
20
+ * @returns Decimated polyline
21
+ */
22
+ export default function decimate(
23
+ polyline: Types.Point2[],
24
+ epsilon = DEFAULT_EPSILON
25
+ ) {
26
+ const numPoints = polyline.length;
27
+
28
+ // The polyline must have at least a start and end points
29
+ if (numPoints < 3) {
30
+ return polyline;
31
+ }
32
+
33
+ const epsilonSquared = epsilon * epsilon;
34
+ const partitionQueue = [[0, numPoints - 1]];
35
+
36
+ // Used a boolean array to set each point that will be in the decimated polyline
37
+ // because pre-allocated arrays are 3-4x faster than thousands of push() calls
38
+ // to add all points to a new array.
39
+ const polylinePointFlags = new Array(numPoints).fill(false);
40
+
41
+ // Start and end points are always added to the decimated polyline
42
+ let numDecimatedPoints = 2;
43
+
44
+ // Add start and end points to the decimated polyline
45
+ polylinePointFlags[0] = true;
46
+ polylinePointFlags[numPoints - 1] = true;
47
+
48
+ // Iterative approach using a queue instead of recursion to reduce the number
49
+ // of function calls (performance)
50
+ while (partitionQueue.length) {
51
+ const [startIndex, endIndex] = partitionQueue.pop();
52
+
53
+ // Return if there is no point between the start and end points
54
+ if (endIndex - startIndex === 1) {
55
+ continue;
56
+ }
57
+
58
+ const startPoint = polyline[startIndex];
59
+ const endPoint = polyline[endIndex];
60
+ let maxDistSquared = -Infinity;
61
+ let maxDistIndex = -1;
62
+
63
+ // Search for the furthest point
64
+ for (let i = startIndex + 1; i < endIndex; i++) {
65
+ const currentPoint = polyline[i];
66
+ const distSquared = mathLine.distanceToPointSquared(
67
+ startPoint,
68
+ endPoint,
69
+ currentPoint
70
+ );
71
+
72
+ if (distSquared > maxDistSquared) {
73
+ maxDistSquared = distSquared;
74
+ maxDistIndex = i;
75
+ }
76
+ }
77
+
78
+ // Do not add any of the points because the fursthest one is very close to
79
+ // the line based on the epsilon value
80
+ if (maxDistSquared < epsilonSquared) {
81
+ continue;
82
+ }
83
+
84
+ // Update the flag for the furthest point because it will be added to the
85
+ // decimated polyline
86
+ polylinePointFlags[maxDistIndex] = true;
87
+ numDecimatedPoints++;
88
+
89
+ // Partition the points into two parts using maxDistIndex as the pivot point
90
+ // and process both sides
91
+ partitionQueue.push([maxDistIndex, endIndex]);
92
+ partitionQueue.push([startIndex, maxDistIndex]);
93
+ }
94
+
95
+ // A pre-allocated array is 3-4x faster then multiple push() calls
96
+ const decimatedPolyline: Types.Point2[] = new Array(numDecimatedPoints);
97
+
98
+ for (let srcIndex = 0, dstIndex = 0; srcIndex < numPoints; srcIndex++) {
99
+ if (polylinePointFlags[srcIndex]) {
100
+ decimatedPolyline[dstIndex++] = polyline[srcIndex];
101
+ }
102
+ }
103
+
104
+ return decimatedPolyline;
105
+ }
@@ -9,6 +9,7 @@ import getNormal3 from './getNormal3';
9
9
  import getNormal2 from './getNormal2';
10
10
  import { mergePolylines, subtractPolylines } from './combinePolyline';
11
11
  import intersectPolyline from './intersectPolyline';
12
+ import decimate from './decimate';
12
13
  import getFirstLineSegmentIntersectionIndexes from './getFirstLineSegmentIntersectionIndexes';
13
14
  import getLineSegmentIntersectionsIndexes from './getLineSegmentIntersectionsIndexes';
14
15
  import getLineSegmentIntersectionsCoordinates from './getLineSegmentIntersectionsCoordinates';
@@ -30,6 +31,7 @@ export {
30
31
  getNormal3,
31
32
  getNormal2,
32
33
  intersectPolyline,
34
+ decimate,
33
35
  getFirstLineSegmentIntersectionIndexes,
34
36
  getLineSegmentIntersectionsIndexes,
35
37
  getLineSegmentIntersectionsCoordinates,