@cornerstonejs/tools 1.33.0 → 1.35.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 (338) hide show
  1. package/dist/cjs/eventDispatchers/mouseEventHandlers/mouseDown.js +4 -4
  2. package/dist/cjs/eventDispatchers/mouseEventHandlers/mouseDown.js.map +1 -1
  3. package/dist/cjs/eventDispatchers/shared/getToolsWithActionsForMouseEvent.js +3 -2
  4. package/dist/cjs/eventDispatchers/shared/getToolsWithActionsForMouseEvent.js.map +1 -1
  5. package/dist/cjs/index.d.ts +2 -2
  6. package/dist/cjs/index.js +3 -2
  7. package/dist/cjs/index.js.map +1 -1
  8. package/dist/cjs/store/SynchronizerManager/Synchronizer.d.ts +1 -0
  9. package/dist/cjs/store/SynchronizerManager/Synchronizer.js +17 -1
  10. package/dist/cjs/store/SynchronizerManager/Synchronizer.js.map +1 -1
  11. package/dist/cjs/synchronizers/callbacks/areViewportsCoplanar .d.ts +1 -1
  12. package/dist/cjs/synchronizers/callbacks/areViewportsCoplanar .js.map +1 -1
  13. package/dist/cjs/synchronizers/callbacks/{stackImageSyncCallback.d.ts → imageSliceSyncCallback.d.ts} +1 -1
  14. package/dist/cjs/synchronizers/callbacks/{stackImageSyncCallback.js → imageSliceSyncCallback.js} +8 -4
  15. package/dist/cjs/synchronizers/callbacks/{stackImageSyncCallback.js.map → imageSliceSyncCallback.js.map} +1 -1
  16. package/dist/cjs/synchronizers/index.d.ts +3 -2
  17. package/dist/cjs/synchronizers/index.js +5 -3
  18. package/dist/cjs/synchronizers/index.js.map +1 -1
  19. package/dist/cjs/synchronizers/synchronizers/{createStackImageSynchronizer.d.ts → createImageSliceSynchronizer.d.ts} +1 -1
  20. package/dist/cjs/synchronizers/synchronizers/createImageSliceSynchronizer.js +17 -0
  21. package/dist/cjs/synchronizers/synchronizers/createImageSliceSynchronizer.js.map +1 -0
  22. package/dist/cjs/synchronizers/synchronizers/index.d.ts +3 -2
  23. package/dist/cjs/synchronizers/synchronizers/index.js +5 -3
  24. package/dist/cjs/synchronizers/synchronizers/index.js.map +1 -1
  25. package/dist/cjs/tools/AdvancedMagnifyTool.d.ts +4 -0
  26. package/dist/cjs/tools/AdvancedMagnifyTool.js +8 -3
  27. package/dist/cjs/tools/AdvancedMagnifyTool.js.map +1 -1
  28. package/dist/cjs/tools/annotation/SplineROITool.d.ts +59 -0
  29. package/dist/cjs/tools/annotation/SplineROITool.js +709 -0
  30. package/dist/cjs/tools/annotation/SplineROITool.js.map +1 -0
  31. package/dist/cjs/tools/annotation/splines/BSpline.d.ts +5 -0
  32. package/dist/cjs/tools/annotation/splines/BSpline.js +14 -0
  33. package/dist/cjs/tools/annotation/splines/BSpline.js.map +1 -0
  34. package/dist/cjs/tools/annotation/splines/CardinalSpline.d.ts +12 -0
  35. package/dist/cjs/tools/annotation/splines/CardinalSpline.js +38 -0
  36. package/dist/cjs/tools/annotation/splines/CardinalSpline.js.map +1 -0
  37. package/dist/cjs/tools/annotation/splines/CatmullRomSpline.d.ts +5 -0
  38. package/dist/cjs/tools/annotation/splines/CatmullRomSpline.js +12 -0
  39. package/dist/cjs/tools/annotation/splines/CatmullRomSpline.js.map +1 -0
  40. package/dist/cjs/tools/annotation/splines/CubicSpline.d.ts +13 -0
  41. package/dist/cjs/tools/annotation/splines/CubicSpline.js +192 -0
  42. package/dist/cjs/tools/annotation/splines/CubicSpline.js.map +1 -0
  43. package/dist/cjs/tools/annotation/splines/LinearSpline.d.ts +5 -0
  44. package/dist/cjs/tools/annotation/splines/LinearSpline.js +12 -0
  45. package/dist/cjs/tools/annotation/splines/LinearSpline.js.map +1 -0
  46. package/dist/cjs/tools/annotation/splines/QuadraticBezier.d.ts +6 -0
  47. package/dist/cjs/tools/annotation/splines/QuadraticBezier.js +20 -0
  48. package/dist/cjs/tools/annotation/splines/QuadraticBezier.js.map +1 -0
  49. package/dist/cjs/tools/annotation/splines/QuadraticSpline.d.ts +9 -0
  50. package/dist/cjs/tools/annotation/splines/QuadraticSpline.js +18 -0
  51. package/dist/cjs/tools/annotation/splines/QuadraticSpline.js.map +1 -0
  52. package/dist/cjs/tools/annotation/splines/Spline.d.ts +49 -0
  53. package/dist/cjs/tools/annotation/splines/Spline.js +420 -0
  54. package/dist/cjs/tools/annotation/splines/Spline.js.map +1 -0
  55. package/dist/cjs/tools/index.d.ts +2 -1
  56. package/dist/cjs/tools/index.js +3 -1
  57. package/dist/cjs/tools/index.js.map +1 -1
  58. package/dist/cjs/types/CardinalSplineProps.d.ts +5 -0
  59. package/dist/cjs/types/CardinalSplineProps.js +3 -0
  60. package/dist/cjs/types/CardinalSplineProps.js.map +1 -0
  61. package/dist/cjs/types/ClosestControlPoint.d.ts +4 -0
  62. package/dist/cjs/types/ClosestControlPoint.js +3 -0
  63. package/dist/cjs/types/ClosestControlPoint.js.map +1 -0
  64. package/dist/cjs/types/ClosestPoint.d.ts +5 -0
  65. package/dist/cjs/types/ClosestPoint.js +3 -0
  66. package/dist/cjs/types/ClosestPoint.js.map +1 -0
  67. package/dist/cjs/types/ClosestSplinePoint.d.ts +4 -0
  68. package/dist/cjs/types/ClosestSplinePoint.js +3 -0
  69. package/dist/cjs/types/ClosestSplinePoint.js.map +1 -0
  70. package/dist/cjs/types/ControlPointInfo.d.ts +5 -0
  71. package/dist/cjs/types/ControlPointInfo.js +3 -0
  72. package/dist/cjs/types/ControlPointInfo.js.map +1 -0
  73. package/dist/cjs/types/ISpline.d.ts +29 -0
  74. package/dist/cjs/types/ISpline.js +3 -0
  75. package/dist/cjs/types/ISpline.js.map +1 -0
  76. package/dist/cjs/types/SplineCurveSegment.d.ts +14 -0
  77. package/dist/cjs/types/SplineCurveSegment.js +3 -0
  78. package/dist/cjs/types/SplineCurveSegment.js.map +1 -0
  79. package/dist/cjs/types/SplineLineSegment.d.ts +10 -0
  80. package/dist/cjs/types/SplineLineSegment.js +3 -0
  81. package/dist/cjs/types/SplineLineSegment.js.map +1 -0
  82. package/dist/cjs/types/SplineProps.d.ts +4 -0
  83. package/dist/cjs/types/SplineProps.js +3 -0
  84. package/dist/cjs/types/SplineProps.js.map +1 -0
  85. package/dist/cjs/types/ToolSpecificAnnotationTypes.d.ts +34 -0
  86. package/dist/cjs/types/index.d.ts +10 -1
  87. package/dist/cjs/utilities/index.d.ts +2 -3
  88. package/dist/cjs/utilities/index.js +5 -5
  89. package/dist/cjs/utilities/index.js.map +1 -1
  90. package/dist/cjs/utilities/math/aabb/distanceToPoint.d.ts +2 -0
  91. package/dist/cjs/utilities/math/aabb/distanceToPoint.js +11 -0
  92. package/dist/cjs/utilities/math/aabb/distanceToPoint.js.map +1 -0
  93. package/dist/cjs/utilities/math/aabb/distanceToPointSquared.d.ts +2 -0
  94. package/dist/cjs/utilities/math/aabb/distanceToPointSquared.js +24 -0
  95. package/dist/cjs/utilities/math/aabb/distanceToPointSquared.js.map +1 -0
  96. package/dist/cjs/utilities/math/aabb/index.d.ts +2 -0
  97. package/dist/cjs/utilities/math/aabb/index.js +11 -0
  98. package/dist/cjs/utilities/math/aabb/index.js.map +1 -0
  99. package/dist/cjs/utilities/math/index.d.ts +6 -5
  100. package/dist/cjs/utilities/math/index.js +11 -9
  101. package/dist/cjs/utilities/math/index.js.map +1 -1
  102. package/dist/cjs/utilities/math/line/distanceToPointSquared.js +5 -21
  103. package/dist/cjs/utilities/math/line/distanceToPointSquared.js.map +1 -1
  104. package/dist/cjs/utilities/math/line/distanceToPointSquaredInfo.d.ts +5 -0
  105. package/dist/cjs/utilities/math/line/distanceToPointSquaredInfo.js +56 -0
  106. package/dist/cjs/utilities/math/line/distanceToPointSquaredInfo.js.map +1 -0
  107. package/dist/cjs/utilities/math/line/index.d.ts +2 -1
  108. package/dist/cjs/utilities/math/line/index.js +3 -1
  109. package/dist/cjs/utilities/math/line/index.js.map +1 -1
  110. package/dist/cjs/utilities/math/point/distanceToPoint.js +5 -6
  111. package/dist/cjs/utilities/math/point/distanceToPoint.js.map +1 -1
  112. package/dist/cjs/utilities/math/point/distanceToPointSquared.d.ts +4 -0
  113. package/dist/cjs/utilities/math/point/distanceToPointSquared.js +12 -0
  114. package/dist/cjs/utilities/math/point/distanceToPointSquared.js.map +1 -0
  115. package/dist/cjs/utilities/math/point/index.d.ts +3 -2
  116. package/dist/cjs/utilities/math/point/index.js +7 -3
  117. package/dist/cjs/utilities/math/point/index.js.map +1 -1
  118. package/dist/cjs/utilities/math/point/mirror.d.ts +2 -0
  119. package/dist/cjs/utilities/math/point/mirror.js +11 -0
  120. package/dist/cjs/utilities/math/point/mirror.js.map +1 -0
  121. package/dist/esm/eventDispatchers/mouseEventHandlers/mouseDown.js +4 -4
  122. package/dist/esm/eventDispatchers/mouseEventHandlers/mouseDown.js.map +1 -1
  123. package/dist/esm/eventDispatchers/shared/getToolsWithActionsForMouseEvent.js +2 -1
  124. package/dist/esm/eventDispatchers/shared/getToolsWithActionsForMouseEvent.js.map +1 -1
  125. package/dist/esm/index.js +2 -2
  126. package/dist/esm/index.js.map +1 -1
  127. package/dist/esm/store/SynchronizerManager/Synchronizer.js +17 -1
  128. package/dist/esm/store/SynchronizerManager/Synchronizer.js.map +1 -1
  129. package/dist/esm/synchronizers/callbacks/areViewportsCoplanar .js.map +1 -1
  130. package/dist/esm/synchronizers/callbacks/{stackImageSyncCallback.js → imageSliceSyncCallback.js} +8 -4
  131. package/dist/esm/synchronizers/callbacks/imageSliceSyncCallback.js.map +1 -0
  132. package/dist/esm/synchronizers/index.js +3 -2
  133. package/dist/esm/synchronizers/index.js.map +1 -1
  134. package/dist/esm/synchronizers/synchronizers/createImageSliceSynchronizer.js +11 -0
  135. package/dist/esm/synchronizers/synchronizers/createImageSliceSynchronizer.js.map +1 -0
  136. package/dist/esm/synchronizers/synchronizers/index.js +3 -2
  137. package/dist/esm/synchronizers/synchronizers/index.js.map +1 -1
  138. package/dist/esm/tools/AdvancedMagnifyTool.js +8 -3
  139. package/dist/esm/tools/AdvancedMagnifyTool.js.map +1 -1
  140. package/dist/esm/tools/annotation/SplineROITool.js +703 -0
  141. package/dist/esm/tools/annotation/SplineROITool.js.map +1 -0
  142. package/dist/esm/tools/annotation/splines/BSpline.js +10 -0
  143. package/dist/esm/tools/annotation/splines/BSpline.js.map +1 -0
  144. package/dist/esm/tools/annotation/splines/CardinalSpline.js +33 -0
  145. package/dist/esm/tools/annotation/splines/CardinalSpline.js.map +1 -0
  146. package/dist/esm/tools/annotation/splines/CatmullRomSpline.js +8 -0
  147. package/dist/esm/tools/annotation/splines/CatmullRomSpline.js.map +1 -0
  148. package/dist/esm/tools/annotation/splines/CubicSpline.js +165 -0
  149. package/dist/esm/tools/annotation/splines/CubicSpline.js.map +1 -0
  150. package/dist/esm/tools/annotation/splines/LinearSpline.js +8 -0
  151. package/dist/esm/tools/annotation/splines/LinearSpline.js.map +1 -0
  152. package/dist/esm/tools/annotation/splines/QuadraticBezier.js +16 -0
  153. package/dist/esm/tools/annotation/splines/QuadraticBezier.js.map +1 -0
  154. package/dist/esm/tools/annotation/splines/QuadraticSpline.js +14 -0
  155. package/dist/esm/tools/annotation/splines/QuadraticSpline.js.map +1 -0
  156. package/dist/esm/tools/annotation/splines/Spline.js +392 -0
  157. package/dist/esm/tools/annotation/splines/Spline.js.map +1 -0
  158. package/dist/esm/tools/index.js +2 -1
  159. package/dist/esm/tools/index.js.map +1 -1
  160. package/dist/esm/types/CardinalSplineProps.js +2 -0
  161. package/dist/esm/types/CardinalSplineProps.js.map +1 -0
  162. package/dist/esm/types/ClosestControlPoint.js +2 -0
  163. package/dist/esm/types/ClosestControlPoint.js.map +1 -0
  164. package/dist/esm/types/ClosestPoint.js +2 -0
  165. package/dist/esm/types/ClosestPoint.js.map +1 -0
  166. package/dist/esm/types/ClosestSplinePoint.js +2 -0
  167. package/dist/esm/types/ClosestSplinePoint.js.map +1 -0
  168. package/dist/esm/types/ControlPointInfo.js +2 -0
  169. package/dist/esm/types/ControlPointInfo.js.map +1 -0
  170. package/dist/esm/types/ISpline.js +2 -0
  171. package/dist/esm/types/ISpline.js.map +1 -0
  172. package/dist/esm/types/SplineCurveSegment.js +2 -0
  173. package/dist/esm/types/SplineCurveSegment.js.map +1 -0
  174. package/dist/esm/types/SplineLineSegment.js +2 -0
  175. package/dist/esm/types/SplineLineSegment.js.map +1 -0
  176. package/dist/esm/types/SplineProps.js +2 -0
  177. package/dist/esm/types/SplineProps.js.map +1 -0
  178. package/dist/esm/utilities/index.js +2 -3
  179. package/dist/esm/utilities/index.js.map +1 -1
  180. package/dist/esm/utilities/math/aabb/distanceToPoint.js +5 -0
  181. package/dist/esm/utilities/math/aabb/distanceToPoint.js.map +1 -0
  182. package/dist/esm/utilities/math/aabb/distanceToPointSquared.js +21 -0
  183. package/dist/esm/utilities/math/aabb/distanceToPointSquared.js.map +1 -0
  184. package/dist/esm/utilities/math/aabb/index.js +3 -0
  185. package/dist/esm/utilities/math/aabb/index.js.map +1 -0
  186. package/dist/esm/utilities/math/index.js +6 -5
  187. package/dist/esm/utilities/math/index.js.map +1 -1
  188. package/dist/esm/utilities/math/line/distanceToPointSquared.js +2 -21
  189. package/dist/esm/utilities/math/line/distanceToPointSquared.js.map +1 -1
  190. package/dist/esm/utilities/math/line/distanceToPointSquaredInfo.js +30 -0
  191. package/dist/esm/utilities/math/line/distanceToPointSquaredInfo.js.map +1 -0
  192. package/dist/esm/utilities/math/line/index.js +2 -1
  193. package/dist/esm/utilities/math/line/index.js.map +1 -1
  194. package/dist/esm/utilities/math/point/distanceToPoint.js +2 -6
  195. package/dist/esm/utilities/math/point/distanceToPoint.js.map +1 -1
  196. package/dist/esm/utilities/math/point/distanceToPointSquared.js +9 -0
  197. package/dist/esm/utilities/math/point/distanceToPointSquared.js.map +1 -0
  198. package/dist/esm/utilities/math/point/index.js +3 -2
  199. package/dist/esm/utilities/math/point/index.js.map +1 -1
  200. package/dist/esm/utilities/math/point/mirror.js +8 -0
  201. package/dist/esm/utilities/math/point/mirror.js.map +1 -0
  202. package/dist/types/eventDispatchers/mouseEventHandlers/mouseDown.d.ts.map +1 -1
  203. package/dist/types/eventDispatchers/shared/getToolsWithActionsForMouseEvent.d.ts.map +1 -1
  204. package/dist/types/index.d.ts +2 -2
  205. package/dist/types/index.d.ts.map +1 -1
  206. package/dist/types/store/SynchronizerManager/Synchronizer.d.ts +1 -0
  207. package/dist/types/store/SynchronizerManager/Synchronizer.d.ts.map +1 -1
  208. package/dist/types/synchronizers/callbacks/areViewportsCoplanar .d.ts +1 -1
  209. package/dist/types/synchronizers/callbacks/areViewportsCoplanar .d.ts.map +1 -1
  210. package/dist/types/synchronizers/callbacks/{stackImageSyncCallback.d.ts → imageSliceSyncCallback.d.ts} +2 -2
  211. package/dist/types/synchronizers/callbacks/imageSliceSyncCallback.d.ts.map +1 -0
  212. package/dist/types/synchronizers/index.d.ts +3 -2
  213. package/dist/types/synchronizers/index.d.ts.map +1 -1
  214. package/dist/types/synchronizers/synchronizers/createImageSliceSynchronizer.d.ts +3 -0
  215. package/dist/types/synchronizers/synchronizers/createImageSliceSynchronizer.d.ts.map +1 -0
  216. package/dist/types/synchronizers/synchronizers/index.d.ts +3 -2
  217. package/dist/types/synchronizers/synchronizers/index.d.ts.map +1 -1
  218. package/dist/types/tools/AdvancedMagnifyTool.d.ts +4 -0
  219. package/dist/types/tools/AdvancedMagnifyTool.d.ts.map +1 -1
  220. package/dist/types/tools/annotation/SplineROITool.d.ts +60 -0
  221. package/dist/types/tools/annotation/SplineROITool.d.ts.map +1 -0
  222. package/dist/types/tools/annotation/splines/BSpline.d.ts +6 -0
  223. package/dist/types/tools/annotation/splines/BSpline.d.ts.map +1 -0
  224. package/dist/types/tools/annotation/splines/CardinalSpline.d.ts +13 -0
  225. package/dist/types/tools/annotation/splines/CardinalSpline.d.ts.map +1 -0
  226. package/dist/types/tools/annotation/splines/CatmullRomSpline.d.ts +6 -0
  227. package/dist/types/tools/annotation/splines/CatmullRomSpline.d.ts.map +1 -0
  228. package/dist/types/tools/annotation/splines/CubicSpline.d.ts +14 -0
  229. package/dist/types/tools/annotation/splines/CubicSpline.d.ts.map +1 -0
  230. package/dist/types/tools/annotation/splines/LinearSpline.d.ts +6 -0
  231. package/dist/types/tools/annotation/splines/LinearSpline.d.ts.map +1 -0
  232. package/dist/types/tools/annotation/splines/QuadraticBezier.d.ts +7 -0
  233. package/dist/types/tools/annotation/splines/QuadraticBezier.d.ts.map +1 -0
  234. package/dist/types/tools/annotation/splines/QuadraticSpline.d.ts +10 -0
  235. package/dist/types/tools/annotation/splines/QuadraticSpline.d.ts.map +1 -0
  236. package/dist/types/tools/annotation/splines/Spline.d.ts +50 -0
  237. package/dist/types/tools/annotation/splines/Spline.d.ts.map +1 -0
  238. package/dist/types/tools/index.d.ts +2 -1
  239. package/dist/types/tools/index.d.ts.map +1 -1
  240. package/dist/types/types/CardinalSplineProps.d.ts +6 -0
  241. package/dist/types/types/CardinalSplineProps.d.ts.map +1 -0
  242. package/dist/types/types/ClosestControlPoint.d.ts +5 -0
  243. package/dist/types/types/ClosestControlPoint.d.ts.map +1 -0
  244. package/dist/types/types/ClosestPoint.d.ts +6 -0
  245. package/dist/types/types/ClosestPoint.d.ts.map +1 -0
  246. package/dist/types/types/ClosestSplinePoint.d.ts +5 -0
  247. package/dist/types/types/ClosestSplinePoint.d.ts.map +1 -0
  248. package/dist/types/types/ControlPointInfo.d.ts +6 -0
  249. package/dist/types/types/ControlPointInfo.d.ts.map +1 -0
  250. package/dist/types/types/ISpline.d.ts +30 -0
  251. package/dist/types/types/ISpline.d.ts.map +1 -0
  252. package/dist/types/types/SplineCurveSegment.d.ts +15 -0
  253. package/dist/types/types/SplineCurveSegment.d.ts.map +1 -0
  254. package/dist/types/types/SplineLineSegment.d.ts +11 -0
  255. package/dist/types/types/SplineLineSegment.d.ts.map +1 -0
  256. package/dist/types/types/SplineProps.d.ts +5 -0
  257. package/dist/types/types/SplineProps.d.ts.map +1 -0
  258. package/dist/types/types/ToolSpecificAnnotationTypes.d.ts +34 -0
  259. package/dist/types/types/ToolSpecificAnnotationTypes.d.ts.map +1 -1
  260. package/dist/types/types/index.d.ts +10 -1
  261. package/dist/types/types/index.d.ts.map +1 -1
  262. package/dist/types/utilities/index.d.ts +2 -3
  263. package/dist/types/utilities/index.d.ts.map +1 -1
  264. package/dist/types/utilities/math/aabb/distanceToPoint.d.ts +3 -0
  265. package/dist/types/utilities/math/aabb/distanceToPoint.d.ts.map +1 -0
  266. package/dist/types/utilities/math/aabb/distanceToPointSquared.d.ts +3 -0
  267. package/dist/types/utilities/math/aabb/distanceToPointSquared.d.ts.map +1 -0
  268. package/dist/types/utilities/math/aabb/index.d.ts +3 -0
  269. package/dist/types/utilities/math/aabb/index.d.ts.map +1 -0
  270. package/dist/types/utilities/math/index.d.ts +6 -5
  271. package/dist/types/utilities/math/index.d.ts.map +1 -1
  272. package/dist/types/utilities/math/line/distanceToPointSquared.d.ts.map +1 -1
  273. package/dist/types/utilities/math/line/distanceToPointSquaredInfo.d.ts +6 -0
  274. package/dist/types/utilities/math/line/distanceToPointSquaredInfo.d.ts.map +1 -0
  275. package/dist/types/utilities/math/line/index.d.ts +2 -1
  276. package/dist/types/utilities/math/line/index.d.ts.map +1 -1
  277. package/dist/types/utilities/math/point/distanceToPoint.d.ts.map +1 -1
  278. package/dist/types/utilities/math/point/distanceToPointSquared.d.ts +5 -0
  279. package/dist/types/utilities/math/point/distanceToPointSquared.d.ts.map +1 -0
  280. package/dist/types/utilities/math/point/index.d.ts +3 -2
  281. package/dist/types/utilities/math/point/index.d.ts.map +1 -1
  282. package/dist/types/utilities/math/point/mirror.d.ts +3 -0
  283. package/dist/types/utilities/math/point/mirror.d.ts.map +1 -0
  284. package/dist/umd/index.js +2 -1
  285. package/dist/umd/index.js.LICENSE.txt +6 -0
  286. package/dist/umd/index.js.map +1 -1
  287. package/package.json +3 -3
  288. package/src/eventDispatchers/mouseEventHandlers/mouseDown.ts +8 -6
  289. package/src/eventDispatchers/shared/getToolsWithActionsForMouseEvent.ts +3 -2
  290. package/src/index.ts +2 -0
  291. package/src/store/SynchronizerManager/Synchronizer.ts +28 -3
  292. package/src/synchronizers/callbacks/areViewportsCoplanar .ts +2 -2
  293. package/src/synchronizers/callbacks/{stackImageSyncCallback.ts → imageSliceSyncCallback.ts} +16 -8
  294. package/src/synchronizers/index.ts +5 -1
  295. package/src/synchronizers/synchronizers/{createStackImageSynchronizer.ts → createImageSliceSynchronizer.ts} +8 -5
  296. package/src/synchronizers/synchronizers/index.ts +4 -1
  297. package/src/tools/AdvancedMagnifyTool.ts +8 -3
  298. package/src/tools/annotation/SplineROITool.ts +1151 -0
  299. package/src/tools/annotation/splines/BSpline.ts +22 -0
  300. package/src/tools/annotation/splines/CardinalSpline.ts +45 -0
  301. package/src/tools/annotation/splines/CatmullRomSpline.ts +19 -0
  302. package/src/tools/annotation/splines/CubicSpline.ts +288 -0
  303. package/src/tools/annotation/splines/LinearSpline.ts +20 -0
  304. package/src/tools/annotation/splines/QuadraticBezier.ts +20 -0
  305. package/src/tools/annotation/splines/QuadraticSpline.ts +25 -0
  306. package/src/tools/annotation/splines/Spline.ts +729 -0
  307. package/src/tools/index.ts +2 -0
  308. package/src/types/CardinalSplineProps.ts +11 -0
  309. package/src/types/ClosestControlPoint.ts +6 -0
  310. package/src/types/ClosestPoint.ts +8 -0
  311. package/src/types/ClosestSplinePoint.ts +6 -0
  312. package/src/types/ControlPointInfo.ts +8 -0
  313. package/src/types/ISpline.ts +164 -0
  314. package/src/types/SplineCurveSegment.ts +28 -0
  315. package/src/types/SplineLineSegment.ts +20 -0
  316. package/src/types/SplineProps.ts +15 -0
  317. package/src/types/ToolSpecificAnnotationTypes.ts +35 -0
  318. package/src/types/index.ts +21 -0
  319. package/src/utilities/index.ts +6 -2
  320. package/src/utilities/math/aabb/distanceToPoint.ts +20 -0
  321. package/src/utilities/math/aabb/distanceToPointSquared.ts +47 -0
  322. package/src/utilities/math/aabb/index.ts +2 -0
  323. package/src/utilities/math/index.ts +10 -8
  324. package/src/utilities/math/line/distanceToPointSquared.ts +3 -29
  325. package/src/utilities/math/line/distanceToPointSquaredInfo.ts +54 -0
  326. package/src/utilities/math/line/index.ts +7 -1
  327. package/src/utilities/math/point/distanceToPoint.ts +2 -10
  328. package/src/utilities/math/point/distanceToPointSquared.ts +21 -0
  329. package/src/utilities/math/point/index.ts +3 -3
  330. package/src/utilities/math/point/mirror.ts +21 -0
  331. package/dist/cjs/synchronizers/synchronizers/createStackImageSynchronizer.js +0 -15
  332. package/dist/cjs/synchronizers/synchronizers/createStackImageSynchronizer.js.map +0 -1
  333. package/dist/esm/synchronizers/callbacks/stackImageSyncCallback.js.map +0 -1
  334. package/dist/esm/synchronizers/synchronizers/createStackImageSynchronizer.js +0 -9
  335. package/dist/esm/synchronizers/synchronizers/createStackImageSynchronizer.js.map +0 -1
  336. package/dist/types/synchronizers/callbacks/stackImageSyncCallback.d.ts.map +0 -1
  337. package/dist/types/synchronizers/synchronizers/createStackImageSynchronizer.d.ts +0 -3
  338. package/dist/types/synchronizers/synchronizers/createStackImageSynchronizer.d.ts.map +0 -1
@@ -0,0 +1,1151 @@
1
+ import { AnnotationTool } from '../base';
2
+
3
+ import {
4
+ getEnabledElement,
5
+ eventTarget,
6
+ triggerEvent,
7
+ } from '@cornerstonejs/core';
8
+ import type { Types } from '@cornerstonejs/core';
9
+ import { vec3 } from 'gl-matrix';
10
+ import {
11
+ addAnnotation,
12
+ getAnnotations,
13
+ removeAnnotation,
14
+ } from '../../stateManagement/annotation/annotationState';
15
+ import { isAnnotationLocked } from '../../stateManagement/annotation/annotationLocking';
16
+ import { isAnnotationVisible } from '../../stateManagement/annotation/annotationVisibility';
17
+ import {
18
+ drawHandles as drawHandlesSvg,
19
+ drawPolyline as drawPolylineSvg,
20
+ drawLinkedTextBox as drawLinkedTextBoxSvg,
21
+ } from '../../drawingSvg';
22
+ import { state } from '../../store';
23
+ import { Events, MouseBindings, KeyboardBindings } from '../../enums';
24
+ import { resetElementCursor } from '../../cursors/elementCursor';
25
+ import {
26
+ EventTypes,
27
+ ToolHandle,
28
+ TextBoxHandle,
29
+ PublicToolProps,
30
+ ToolProps,
31
+ SVGDrawingHelper,
32
+ } from '../../types';
33
+ import {
34
+ math,
35
+ viewportFilters,
36
+ drawing,
37
+ throttle,
38
+ roundNumber,
39
+ triggerAnnotationRenderForViewportIds,
40
+ getCalibratedScale,
41
+ getCalibratedAreaUnits,
42
+ } from '../../utilities';
43
+ import { SplineROIAnnotation } from '../../types/ToolSpecificAnnotationTypes';
44
+ import {
45
+ AnnotationCompletedEventDetail,
46
+ AnnotationModifiedEventDetail,
47
+ } from '../../types/EventTypes';
48
+ import { StyleSpecifier } from '../../types/AnnotationStyle';
49
+ import { ISpline } from '../../types/ISpline';
50
+ import { CardinalSpline } from './splines/CardinalSpline';
51
+ import { LinearSpline } from './splines/LinearSpline';
52
+ import { CatmullRomSpline } from './splines/CatmullRomSpline';
53
+ import { BSpline } from './splines/BSpline';
54
+
55
+ const { getViewportIdsWithToolToRender } = viewportFilters;
56
+ const { getTextBoxCoordsCanvas } = drawing;
57
+
58
+ const SPLINE_MIN_POINTS = 3;
59
+ const SPLINE_CLICK_CLOSE_CURVE_DIST = 10;
60
+
61
+ const DEFAULT_SPLINE_CONFIG = {
62
+ resolution: 20,
63
+ controlPointAdditionDistance: 6,
64
+ controlPointDeletionDistance: 6,
65
+ showControlPointsConnectors: false,
66
+ controlPointAdditionEnabled: true,
67
+ controlPointDeletionEnabled: true,
68
+ };
69
+
70
+ enum SplineTypesEnum {
71
+ Cardinal = 'CARDINAL',
72
+ Linear = 'LINEAR',
73
+ CatmullRom = 'CATMULLROM',
74
+ BSpline = 'BSPLINE',
75
+ }
76
+
77
+ enum SplineToolActions {
78
+ AddControlPoint = 'addControlPoint',
79
+ DeleteControlPoint = 'deleteControlPoint',
80
+ }
81
+
82
+ class SplineROITool extends AnnotationTool {
83
+ static toolName;
84
+ static SplineTypes = SplineTypesEnum;
85
+ static Actions = SplineToolActions;
86
+
87
+ touchDragCallback: any;
88
+ mouseDragCallback: any;
89
+ _throttledCalculateCachedStats: any;
90
+ editData: {
91
+ annotation: SplineROIAnnotation;
92
+ viewportIdsToRender: Array<string>;
93
+ handleIndex?: number;
94
+ movingTextBox?: boolean;
95
+ newAnnotation?: boolean;
96
+ hasMoved?: boolean;
97
+ lastCanvasPoint?: Types.Point2;
98
+ } | null;
99
+ isDrawing: boolean;
100
+ isHandleOutsideImage = false;
101
+
102
+ constructor(
103
+ toolProps: PublicToolProps = {},
104
+ defaultToolProps: ToolProps = {
105
+ supportedInteractionTypes: ['Mouse', 'Touch'],
106
+ configuration: {
107
+ preventHandleOutsideImage: false,
108
+ calculateStats: true,
109
+ getTextLines: defaultGetTextLines,
110
+ spline: {
111
+ configuration: {
112
+ [SplineTypesEnum.Cardinal]: {
113
+ Class: CardinalSpline,
114
+ scale: 0.5,
115
+ },
116
+ [SplineTypesEnum.CatmullRom]: {
117
+ Class: CatmullRomSpline,
118
+ },
119
+ [SplineTypesEnum.Linear]: {
120
+ Class: LinearSpline,
121
+ },
122
+ [SplineTypesEnum.BSpline]: {
123
+ Class: BSpline,
124
+ controlPointAdditionEnabled: false,
125
+ controlPointDeletionEnabled: false,
126
+ showControlPointsConnectors: true,
127
+ },
128
+ },
129
+ type: SplineTypesEnum.CatmullRom,
130
+ drawPreviewEnabled: true,
131
+ lastControlPointDeletionKeys: ['Backspace', 'Delete'],
132
+ },
133
+ actions: {
134
+ [SplineToolActions.AddControlPoint]: {
135
+ method: 'addControlPointCallback',
136
+ bindings: [
137
+ {
138
+ mouseButton: MouseBindings.Primary,
139
+ modifierKey: KeyboardBindings.Shift,
140
+ },
141
+ ],
142
+ },
143
+ [SplineToolActions.DeleteControlPoint]: {
144
+ method: 'deleteControlPointCallback',
145
+ bindings: [
146
+ {
147
+ mouseButton: MouseBindings.Primary,
148
+ modifierKey: KeyboardBindings.Ctrl,
149
+ },
150
+ ],
151
+ },
152
+ },
153
+ },
154
+ }
155
+ ) {
156
+ super(toolProps, defaultToolProps);
157
+
158
+ this._throttledCalculateCachedStats = throttle(
159
+ this._calculateCachedStats,
160
+ 100,
161
+ { trailing: true }
162
+ );
163
+ }
164
+
165
+ /**
166
+ * Based on the current position of the mouse and the current imageId to create
167
+ * a CircleROI Annotation and stores it in the annotationManager
168
+ *
169
+ * @param evt - EventTypes.NormalizedMouseEventType
170
+ * @returns The annotation object.
171
+ *
172
+ */
173
+ addNewAnnotation = (
174
+ evt: EventTypes.InteractionEventType
175
+ ): SplineROIAnnotation => {
176
+ const eventDetail = evt.detail;
177
+ const { currentPoints, element } = eventDetail;
178
+ const { world: worldPos, canvas: canvasPos } = currentPoints;
179
+
180
+ const enabledElement = getEnabledElement(element);
181
+ const { viewport, renderingEngine } = enabledElement;
182
+
183
+ this.isDrawing = true;
184
+
185
+ const camera = viewport.getCamera();
186
+ const { viewPlaneNormal, viewUp } = camera;
187
+
188
+ const { type: splineType } = this.configuration.spline;
189
+ const splineConfig = this._getSplineConfig(splineType);
190
+ const spline = new splineConfig.Class();
191
+
192
+ const referencedImageId = this.getReferencedImageId(
193
+ viewport,
194
+ worldPos,
195
+ viewPlaneNormal,
196
+ viewUp
197
+ );
198
+
199
+ const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
200
+
201
+ const annotation: SplineROIAnnotation = {
202
+ highlighted: true,
203
+ invalidated: true,
204
+ metadata: {
205
+ toolName: this.getToolName(),
206
+ viewPlaneNormal: <Types.Point3>[...viewPlaneNormal],
207
+ viewUp: <Types.Point3>[...viewUp],
208
+ FrameOfReferenceUID,
209
+ referencedImageId,
210
+ },
211
+ data: {
212
+ handles: {
213
+ textBox: {
214
+ hasMoved: false,
215
+ worldPosition: <Types.Point3>[0, 0, 0],
216
+ worldBoundingBox: {
217
+ topLeft: <Types.Point3>[0, 0, 0],
218
+ topRight: <Types.Point3>[0, 0, 0],
219
+ bottomLeft: <Types.Point3>[0, 0, 0],
220
+ bottomRight: <Types.Point3>[0, 0, 0],
221
+ },
222
+ },
223
+ points: [[...worldPos]],
224
+ activeHandleIndex: null,
225
+ },
226
+ spline: {
227
+ type: splineConfig.type,
228
+ instance: spline,
229
+ resolution: splineConfig.resolution,
230
+ closed: false,
231
+ polyline: [],
232
+ },
233
+ cachedStats: {},
234
+ },
235
+ };
236
+
237
+ addAnnotation(annotation, element);
238
+
239
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
240
+ element,
241
+ this.getToolName()
242
+ );
243
+
244
+ this.editData = {
245
+ annotation,
246
+ viewportIdsToRender,
247
+ movingTextBox: false,
248
+ newAnnotation: true,
249
+ hasMoved: false,
250
+ lastCanvasPoint: canvasPos,
251
+ };
252
+
253
+ this._activateDraw(element);
254
+ evt.preventDefault();
255
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
256
+
257
+ return annotation;
258
+ };
259
+
260
+ /**
261
+ * It returns if the canvas point is near the provided annotation in the provided
262
+ * element or not. A proximity is passed to the function to determine the
263
+ * proximity of the point to the annotation in number of pixels.
264
+ *
265
+ * @param element - HTML Element
266
+ * @param annotation - Annotation
267
+ * @param canvasCoords - Canvas coordinates
268
+ * @param proximity - Proximity to tool to consider
269
+ * @returns Boolean, whether the canvas point is near tool
270
+ */
271
+ isPointNearTool = (
272
+ element: HTMLDivElement,
273
+ annotation: SplineROIAnnotation,
274
+ canvasCoords: Types.Point2,
275
+ proximity: number
276
+ ): boolean => {
277
+ const { instance: spline } = annotation.data.spline;
278
+
279
+ return spline.isPointNearCurve(canvasCoords, proximity);
280
+ };
281
+
282
+ toolSelectedCallback = (
283
+ evt: EventTypes.InteractionEventType,
284
+ annotation: SplineROIAnnotation
285
+ ): void => {
286
+ const eventDetail = evt.detail;
287
+ const { element } = eventDetail;
288
+
289
+ annotation.highlighted = true;
290
+
291
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
292
+ element,
293
+ this.getToolName()
294
+ );
295
+
296
+ this.editData = {
297
+ annotation,
298
+ viewportIdsToRender,
299
+ movingTextBox: false,
300
+ };
301
+
302
+ const enabledElement = getEnabledElement(element);
303
+ const { renderingEngine } = enabledElement;
304
+
305
+ this._activateModify(element);
306
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
307
+ evt.preventDefault();
308
+ };
309
+
310
+ handleSelectedCallback = (
311
+ evt: EventTypes.InteractionEventType,
312
+ annotation: SplineROIAnnotation,
313
+ handle: ToolHandle
314
+ ): void => {
315
+ const eventDetail = evt.detail;
316
+ const { element } = eventDetail;
317
+ const { data } = annotation;
318
+
319
+ annotation.highlighted = true;
320
+
321
+ let movingTextBox = false;
322
+ let handleIndex;
323
+
324
+ if ((handle as TextBoxHandle).worldPosition) {
325
+ movingTextBox = true;
326
+ } else {
327
+ const { points } = data.handles;
328
+
329
+ handleIndex = points.findIndex((p) => p === handle);
330
+ }
331
+
332
+ // Find viewports to render on drag.
333
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
334
+ element,
335
+ this.getToolName()
336
+ );
337
+
338
+ this.editData = {
339
+ annotation,
340
+ viewportIdsToRender,
341
+ handleIndex,
342
+ movingTextBox,
343
+ };
344
+ this._activateModify(element);
345
+
346
+ const enabledElement = getEnabledElement(element);
347
+ const { renderingEngine } = enabledElement;
348
+
349
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
350
+
351
+ evt.preventDefault();
352
+ };
353
+
354
+ _endCallback = (evt: EventTypes.InteractionEventType): void => {
355
+ const eventDetail = evt.detail;
356
+ const { element } = eventDetail;
357
+
358
+ const { annotation, viewportIdsToRender, newAnnotation } = this.editData;
359
+ const { data } = annotation;
360
+
361
+ data.handles.activeHandleIndex = null;
362
+
363
+ this._deactivateModify(element);
364
+ this._deactivateDraw(element);
365
+ resetElementCursor(element);
366
+
367
+ const enabledElement = getEnabledElement(element);
368
+ const { renderingEngine } = enabledElement;
369
+
370
+ if (
371
+ this.isHandleOutsideImage &&
372
+ this.configuration.preventHandleOutsideImage
373
+ ) {
374
+ removeAnnotation(annotation.annotationUID);
375
+ }
376
+
377
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
378
+
379
+ if (newAnnotation) {
380
+ const eventType = Events.ANNOTATION_COMPLETED;
381
+ const eventDetail: AnnotationCompletedEventDetail = {
382
+ annotation,
383
+ };
384
+
385
+ triggerEvent(eventTarget, eventType, eventDetail);
386
+ }
387
+
388
+ this.editData = null;
389
+ this.isDrawing = false;
390
+ };
391
+
392
+ private _keyDownCallback = (evt: EventTypes.KeyDownEventType) => {
393
+ const eventDetail = evt.detail;
394
+ const { element } = eventDetail;
395
+ const key = eventDetail.key ?? '';
396
+ const { lastControlPointDeletionKeys } = this.configuration.spline;
397
+ const deleteLastPoint = lastControlPointDeletionKeys.includes(key);
398
+
399
+ if (!deleteLastPoint) {
400
+ return;
401
+ }
402
+
403
+ const { annotation } = this.editData;
404
+ const { data } = annotation;
405
+
406
+ if (data.handles.points.length === SPLINE_MIN_POINTS) {
407
+ this.cancel(element);
408
+ return;
409
+ } else {
410
+ const controlPointIndex = data.handles.points.length - 1;
411
+ this._deleteControlPointByIndex(element, annotation, controlPointIndex);
412
+ }
413
+
414
+ evt.preventDefault();
415
+ };
416
+
417
+ private _mouseMoveCallback = (evt: EventTypes.InteractionEventType): void => {
418
+ const { drawPreviewEnabled } = this.configuration.spline;
419
+
420
+ // Does not force a re-render if preview is not enabled
421
+ if (!drawPreviewEnabled) {
422
+ return;
423
+ }
424
+
425
+ const { element } = evt.detail;
426
+ const { renderingEngine } = getEnabledElement(element);
427
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
428
+ element,
429
+ this.getToolName()
430
+ );
431
+
432
+ this.editData.lastCanvasPoint = evt.detail.currentPoints.canvas;
433
+
434
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
435
+ evt.preventDefault();
436
+ };
437
+
438
+ private _mouseDownCallback = (evt: EventTypes.InteractionEventType): void => {
439
+ const doubleClick = evt.type === Events.MOUSE_DOUBLE_CLICK;
440
+ const { annotation, viewportIdsToRender } = this.editData;
441
+ const { data } = annotation;
442
+
443
+ if (data.spline.closed) {
444
+ return;
445
+ }
446
+
447
+ const eventDetail = evt.detail;
448
+ const { element } = eventDetail;
449
+ const { currentPoints } = eventDetail;
450
+ const { canvas: canvasPoint, world: worldPoint } = currentPoints;
451
+ const enabledElement = getEnabledElement(element);
452
+ const { renderingEngine } = enabledElement;
453
+ let closeSpline = data.handles.points.length >= 2 && doubleClick;
454
+ let addNewPoint = true;
455
+
456
+ // Check if user clicked on the first point to close the curve
457
+ if (data.handles.points.length >= 3) {
458
+ const { instance: spline } = data.spline;
459
+ const closestControlPoint = spline.getClosestControlPointWithinDistance(
460
+ canvasPoint,
461
+ SPLINE_CLICK_CLOSE_CURVE_DIST
462
+ );
463
+
464
+ if (closestControlPoint?.index === 0) {
465
+ addNewPoint = false;
466
+ closeSpline = true;
467
+ }
468
+ }
469
+
470
+ if (addNewPoint) {
471
+ data.handles.points.push(worldPoint);
472
+ }
473
+
474
+ data.spline.closed = data.spline.closed || closeSpline;
475
+ annotation.invalidated = true;
476
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
477
+
478
+ if (data.spline.closed) {
479
+ this._endCallback(evt);
480
+ }
481
+
482
+ evt.preventDefault();
483
+ };
484
+
485
+ private _dragCallback = (evt: EventTypes.InteractionEventType): void => {
486
+ this.isDrawing = true;
487
+ const eventDetail = evt.detail;
488
+ const { element } = eventDetail;
489
+
490
+ const { annotation, viewportIdsToRender, handleIndex, movingTextBox } =
491
+ this.editData;
492
+ const { data } = annotation;
493
+
494
+ if (movingTextBox) {
495
+ // Drag mode - moving text box
496
+ const { deltaPoints } = eventDetail as EventTypes.MouseDragEventDetail;
497
+ const worldPosDelta = deltaPoints.world;
498
+
499
+ const { textBox } = data.handles;
500
+ const { worldPosition } = textBox;
501
+
502
+ worldPosition[0] += worldPosDelta[0];
503
+ worldPosition[1] += worldPosDelta[1];
504
+ worldPosition[2] += worldPosDelta[2];
505
+
506
+ textBox.hasMoved = true;
507
+ } else if (handleIndex === undefined) {
508
+ // Drag mode - moving handle
509
+ const { deltaPoints } = eventDetail as EventTypes.MouseDragEventDetail;
510
+ const worldPosDelta = deltaPoints.world;
511
+
512
+ const points = data.handles.points;
513
+
514
+ points.forEach((point) => {
515
+ point[0] += worldPosDelta[0];
516
+ point[1] += worldPosDelta[1];
517
+ point[2] += worldPosDelta[2];
518
+ });
519
+ annotation.invalidated = true;
520
+ } else {
521
+ // Move mode - after double click, and mouse move to draw
522
+ const { currentPoints } = eventDetail;
523
+ const worldPos = currentPoints.world;
524
+
525
+ data.handles.points[handleIndex] = [...worldPos];
526
+ annotation.invalidated = true;
527
+ }
528
+
529
+ this.editData.hasMoved = true;
530
+
531
+ const enabledElement = getEnabledElement(element);
532
+ const { renderingEngine } = enabledElement;
533
+
534
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
535
+ };
536
+
537
+ cancel = (element: HTMLDivElement) => {
538
+ // If it is not in mid-draw or mid-modify
539
+ if (!this.isDrawing) {
540
+ return;
541
+ }
542
+
543
+ this.isDrawing = false;
544
+ this._deactivateDraw(element);
545
+ this._deactivateModify(element);
546
+ resetElementCursor(element);
547
+
548
+ const { annotation, viewportIdsToRender, newAnnotation } = this.editData;
549
+
550
+ if (newAnnotation) {
551
+ removeAnnotation(annotation.annotationUID);
552
+ }
553
+
554
+ const enabledElement = getEnabledElement(element);
555
+ const { renderingEngine } = enabledElement;
556
+
557
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
558
+
559
+ this.editData = null;
560
+ return annotation.annotationUID;
561
+ };
562
+
563
+ /**
564
+ * Triggers an annotation modified event.
565
+ */
566
+ triggerAnnotationModified = (
567
+ annotation: SplineROIAnnotation,
568
+ enabledElement: Types.IEnabledElement
569
+ ): void => {
570
+ const { viewportId, renderingEngineId } = enabledElement;
571
+ const eventType = Events.ANNOTATION_MODIFIED;
572
+
573
+ const eventDetail: AnnotationModifiedEventDetail = {
574
+ annotation,
575
+ viewportId,
576
+ renderingEngineId,
577
+ };
578
+
579
+ triggerEvent(eventTarget, eventType, eventDetail);
580
+ };
581
+
582
+ private _activateModify = (element) => {
583
+ state.isInteractingWithTool = true;
584
+
585
+ element.addEventListener(Events.MOUSE_UP, this._endCallback);
586
+ element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
587
+ element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
588
+
589
+ element.addEventListener(Events.TOUCH_END, this._endCallback);
590
+ element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
591
+ element.addEventListener(Events.TOUCH_TAP, this._endCallback);
592
+ };
593
+
594
+ private _deactivateModify = (element) => {
595
+ state.isInteractingWithTool = false;
596
+
597
+ element.removeEventListener(Events.MOUSE_UP, this._endCallback);
598
+ element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
599
+ element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
600
+
601
+ element.removeEventListener(Events.TOUCH_END, this._endCallback);
602
+ element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
603
+ element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
604
+ };
605
+
606
+ private _activateDraw = (element) => {
607
+ state.isInteractingWithTool = true;
608
+
609
+ element.addEventListener(Events.KEY_DOWN, this._keyDownCallback);
610
+ element.addEventListener(Events.MOUSE_MOVE, this._mouseMoveCallback);
611
+ element.addEventListener(Events.MOUSE_DOWN, this._mouseDownCallback);
612
+ element.addEventListener(
613
+ Events.MOUSE_DOUBLE_CLICK,
614
+ this._mouseDownCallback
615
+ );
616
+
617
+ element.addEventListener(Events.TOUCH_TAP, this._mouseDownCallback);
618
+ };
619
+
620
+ private _deactivateDraw = (element) => {
621
+ state.isInteractingWithTool = false;
622
+
623
+ element.removeEventListener(Events.KEY_DOWN, this._keyDownCallback);
624
+ element.removeEventListener(Events.MOUSE_MOVE, this._mouseMoveCallback);
625
+ element.removeEventListener(Events.MOUSE_DOWN, this._mouseDownCallback);
626
+ element.removeEventListener(
627
+ Events.MOUSE_DOUBLE_CLICK,
628
+ this._mouseDownCallback
629
+ );
630
+
631
+ element.removeEventListener(Events.TOUCH_TAP, this._mouseDownCallback);
632
+ };
633
+
634
+ /**
635
+ * it is used to draw the circleROI annotation in each
636
+ * request animation frame. It calculates the updated cached statistics if
637
+ * data is invalidated and cache it.
638
+ *
639
+ * @param enabledElement - The Cornerstone's enabledElement.
640
+ * @param svgDrawingHelper - The svgDrawingHelper providing the context for drawing.
641
+ */
642
+ renderAnnotation = (
643
+ enabledElement: Types.IEnabledElement,
644
+ svgDrawingHelper: SVGDrawingHelper
645
+ ): boolean => {
646
+ let renderStatus = false;
647
+ const { viewport } = enabledElement;
648
+ const { worldToCanvas } = viewport;
649
+ const { element } = viewport;
650
+
651
+ // If rendering engine has been destroyed while rendering
652
+ if (!viewport.getRenderingEngine()) {
653
+ console.warn('Rendering Engine has been destroyed');
654
+ return renderStatus;
655
+ }
656
+
657
+ let annotations = getAnnotations(this.getToolName(), element);
658
+
659
+ if (!annotations?.length) {
660
+ return renderStatus;
661
+ }
662
+
663
+ annotations = this.filterInteractableAnnotationsForElement(
664
+ element,
665
+ annotations
666
+ );
667
+
668
+ if (!annotations?.length) {
669
+ return renderStatus;
670
+ }
671
+
672
+ const targetId = this.getTargetId(viewport);
673
+ const newAnnotation = this.editData?.newAnnotation;
674
+ const styleSpecifier: StyleSpecifier = {
675
+ toolGroupId: this.toolGroupId,
676
+ toolName: this.getToolName(),
677
+ viewportId: enabledElement.viewport.id,
678
+ };
679
+
680
+ for (let i = 0; i < annotations.length; i++) {
681
+ const annotation = annotations[i] as SplineROIAnnotation;
682
+ const { annotationUID, data, highlighted } = annotation;
683
+ const { handles } = data;
684
+ const { points: controlPoints, activeHandleIndex } = handles;
685
+
686
+ styleSpecifier.annotationUID = annotationUID;
687
+
688
+ const lineWidth = this.getStyle(
689
+ 'lineWidth',
690
+ styleSpecifier,
691
+ annotation
692
+ ) as number;
693
+ const lineDash = this.getStyle(
694
+ 'lineDash',
695
+ styleSpecifier,
696
+ annotation
697
+ ) as string;
698
+ const color = this.getStyle(
699
+ 'color',
700
+ styleSpecifier,
701
+ annotation
702
+ ) as string;
703
+
704
+ const canvasCoordinates = controlPoints.map((p) =>
705
+ worldToCanvas(p)
706
+ ) as Types.Point2[];
707
+
708
+ const { drawPreviewEnabled } = this.configuration.spline;
709
+ const splineType = annotation.data.spline.type;
710
+ const splineConfig = this._getSplineConfig(splineType);
711
+ const spline = this._updateSplineInstance(element, annotation);
712
+ const splinePolylineCanvas = spline.getPolylinePoints();
713
+ const splinePolylineWorld = [];
714
+
715
+ for (let i = 0, len = splinePolylineCanvas.length; i < len; i++) {
716
+ splinePolylineWorld.push(
717
+ viewport.canvasToWorld(splinePolylineCanvas[i])
718
+ );
719
+ }
720
+
721
+ data.spline.polyline = splinePolylineWorld;
722
+
723
+ // If cachedStats does not exist, or the areaUnit is missing (as part of
724
+ // import/hydration etc.), force to recalculate the stats from the points
725
+ if (
726
+ !data.cachedStats[targetId] ||
727
+ data.cachedStats[targetId].areaUnit == null
728
+ ) {
729
+ data.cachedStats[targetId] = {
730
+ Modality: null,
731
+ area: null,
732
+ areaUnit: null,
733
+ };
734
+
735
+ this._calculateCachedStats(annotation, element);
736
+ } else if (annotation.invalidated) {
737
+ this._throttledCalculateCachedStats(annotation, element);
738
+ }
739
+
740
+ let activeHandleCanvasCoords;
741
+
742
+ if (!isAnnotationVisible(annotationUID)) {
743
+ continue;
744
+ }
745
+
746
+ if (
747
+ !isAnnotationLocked(annotation) &&
748
+ !this.editData &&
749
+ activeHandleIndex !== null
750
+ ) {
751
+ // Not locked or creating and hovering over handle, so render handle.
752
+ activeHandleCanvasCoords = [canvasCoordinates[activeHandleIndex]];
753
+ }
754
+
755
+ if (activeHandleCanvasCoords || newAnnotation || highlighted) {
756
+ const handleGroupUID = '0';
757
+ drawHandlesSvg(
758
+ svgDrawingHelper,
759
+ annotationUID,
760
+ handleGroupUID,
761
+ canvasCoordinates,
762
+ {
763
+ color,
764
+ lineDash,
765
+ lineWidth,
766
+ handleRadius: '3',
767
+ }
768
+ );
769
+ }
770
+
771
+ if (
772
+ drawPreviewEnabled &&
773
+ spline.numControlPoints > 1 &&
774
+ this.editData?.lastCanvasPoint &&
775
+ !spline.closed
776
+ ) {
777
+ const { lastCanvasPoint } = this.editData;
778
+ const previewPolylinePoints = spline.getPreviewPolylinePoints(
779
+ lastCanvasPoint,
780
+ SPLINE_CLICK_CLOSE_CURVE_DIST
781
+ );
782
+
783
+ drawPolylineSvg(
784
+ svgDrawingHelper,
785
+ annotationUID,
786
+ 'previewSplineChange',
787
+ previewPolylinePoints,
788
+ {
789
+ color: '#9EA0CA',
790
+ lineDash,
791
+ lineWidth,
792
+ }
793
+ );
794
+ }
795
+
796
+ if (splineConfig.showControlPointsConnectors) {
797
+ const controlPointsConnectors = [...canvasCoordinates];
798
+
799
+ // Connect the last point to the first one when the spline is closed
800
+ if (spline.closed) {
801
+ controlPointsConnectors.push(canvasCoordinates[0]);
802
+ }
803
+
804
+ drawPolylineSvg(
805
+ svgDrawingHelper,
806
+ annotationUID,
807
+ 'controlPointsConnectors',
808
+ controlPointsConnectors,
809
+ {
810
+ color: 'rgba(255, 255, 255, 0.5)',
811
+ lineDash,
812
+ lineWidth,
813
+ }
814
+ );
815
+ }
816
+
817
+ drawPolylineSvg(
818
+ svgDrawingHelper,
819
+ annotationUID,
820
+ 'lineSegments',
821
+ splinePolylineCanvas,
822
+ {
823
+ color,
824
+ lineDash,
825
+ lineWidth,
826
+ }
827
+ );
828
+
829
+ this._renderStats(annotation, viewport, enabledElement, svgDrawingHelper);
830
+
831
+ renderStatus = true;
832
+ annotation.invalidated = false;
833
+ }
834
+
835
+ return renderStatus;
836
+ };
837
+
838
+ _renderStats = (annotation, viewport, enabledElement, svgDrawingHelper) => {
839
+ const data = annotation.data;
840
+ const targetId = this.getTargetId(viewport);
841
+
842
+ if (!data.spline.closed) {
843
+ return;
844
+ }
845
+
846
+ const styleSpecifier: StyleSpecifier = {
847
+ toolGroupId: this.toolGroupId,
848
+ toolName: this.getToolName(),
849
+ viewportId: enabledElement.viewport.id,
850
+ };
851
+
852
+ const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
853
+ if (!options.visibility) {
854
+ return;
855
+ }
856
+
857
+ const textLines = this.configuration.getTextLines(data, targetId);
858
+ if (!textLines || textLines.length === 0) {
859
+ return;
860
+ }
861
+
862
+ const canvasCoordinates = data.handles.points.map((p) =>
863
+ viewport.worldToCanvas(p)
864
+ );
865
+ if (!data.handles.textBox.hasMoved) {
866
+ const canvasTextBoxCoords = getTextBoxCoordsCanvas(canvasCoordinates);
867
+
868
+ data.handles.textBox.worldPosition =
869
+ viewport.canvasToWorld(canvasTextBoxCoords);
870
+ }
871
+
872
+ const textBoxPosition = viewport.worldToCanvas(
873
+ data.handles.textBox.worldPosition
874
+ );
875
+
876
+ const textBoxUID = 'textBox';
877
+ const boundingBox = drawLinkedTextBoxSvg(
878
+ svgDrawingHelper,
879
+ annotation.annotationUID ?? '',
880
+ textBoxUID,
881
+ textLines,
882
+ textBoxPosition,
883
+ canvasCoordinates,
884
+ {},
885
+ options
886
+ );
887
+
888
+ const { x: left, y: top, width, height } = boundingBox;
889
+
890
+ data.handles.textBox.worldBoundingBox = {
891
+ topLeft: viewport.canvasToWorld([left, top]),
892
+ topRight: viewport.canvasToWorld([left + width, top]),
893
+ bottomLeft: viewport.canvasToWorld([left, top + height]),
894
+ bottomRight: viewport.canvasToWorld([left + width, top + height]),
895
+ };
896
+ };
897
+
898
+ addControlPointCallback = (
899
+ evt: EventTypes.InteractionEventType,
900
+ annotation: SplineROIAnnotation
901
+ ) => {
902
+ const { data } = annotation;
903
+ const splineType = data.spline.type;
904
+ const splineConfig = this._getSplineConfig(splineType);
905
+ const maxDist = splineConfig.controlPointAdditionDistance;
906
+
907
+ if (splineConfig.controlPointAdditionEnabled === false) {
908
+ return;
909
+ }
910
+
911
+ const eventDetail = evt.detail;
912
+ const { element } = eventDetail;
913
+
914
+ const enabledElement = getEnabledElement(element);
915
+ const { renderingEngine, viewport } = enabledElement;
916
+ const { canvasToWorld } = viewport;
917
+
918
+ const { instance: spline } = data.spline;
919
+ const canvasPos = evt.detail.currentPoints.canvas;
920
+ const closestPointInfo = spline.getClosestPoint(canvasPos);
921
+
922
+ if (closestPointInfo.distance > maxDist) {
923
+ return;
924
+ }
925
+
926
+ // Add a point at the `u` position from Parameter Space
927
+ const { index, point: canvasPoint } = spline.addControlPointAtU(
928
+ closestPointInfo.uValue
929
+ );
930
+
931
+ data.handles.points.splice(index, 0, canvasToWorld(canvasPoint));
932
+ annotation.invalidated = true;
933
+
934
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
935
+ element,
936
+ this.getToolName()
937
+ );
938
+
939
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
940
+ };
941
+
942
+ private _deleteControlPointByIndex(
943
+ element: HTMLDivElement,
944
+ annotation: SplineROIAnnotation,
945
+ controlPointIndex: number
946
+ ) {
947
+ const enabledElement = getEnabledElement(element);
948
+ const { points: controlPoints } = annotation.data.handles;
949
+
950
+ // There is no curve with only 2 points
951
+ if (controlPoints.length === 3) {
952
+ removeAnnotation(annotation.annotationUID);
953
+ } else {
954
+ controlPoints.splice(controlPointIndex, 1);
955
+ }
956
+
957
+ const { renderingEngine } = enabledElement;
958
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
959
+ element,
960
+ this.getToolName()
961
+ );
962
+
963
+ annotation.invalidated = true;
964
+
965
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
966
+ }
967
+
968
+ deleteControlPointCallback = (
969
+ evt: EventTypes.InteractionEventType,
970
+ annotation: SplineROIAnnotation
971
+ ) => {
972
+ const splineType = annotation.data.spline.type;
973
+ const splineConfig = this._getSplineConfig(splineType);
974
+ const maxDist = splineConfig.controlPointDeletionDistance;
975
+
976
+ if (splineConfig.controlPointDeletionEnabled === false) {
977
+ return;
978
+ }
979
+
980
+ const eventDetail = evt.detail;
981
+ const { element, currentPoints } = eventDetail;
982
+ const { canvas: canvasPos } = currentPoints;
983
+ const { instance: spline } = annotation.data.spline;
984
+ const closestControlPoint = spline.getClosestControlPointWithinDistance(
985
+ canvasPos,
986
+ maxDist
987
+ );
988
+
989
+ if (!closestControlPoint) {
990
+ return;
991
+ }
992
+
993
+ this._deleteControlPointByIndex(
994
+ element,
995
+ annotation,
996
+ closestControlPoint.index
997
+ );
998
+ };
999
+
1000
+ /**
1001
+ * Get a spline config merged with the default settings.
1002
+ * @param type - Spline type (CARDINAL, CATMULLROM, LINEAR or BSPLINE)
1003
+ * @returns Spline configuration
1004
+ */
1005
+ private _getSplineConfig(type: string) {
1006
+ const { configuration: config } = this;
1007
+ const splineConfigs = config.spline.configuration;
1008
+
1009
+ return Object.assign({ type }, DEFAULT_SPLINE_CONFIG, splineConfigs[type]);
1010
+ }
1011
+
1012
+ private _updateSplineScale(spline: ISpline, annotation: SplineROIAnnotation) {
1013
+ const splineType = annotation.data.spline.type;
1014
+ const splineConfig = this._getSplineConfig(splineType);
1015
+
1016
+ if (
1017
+ !(spline instanceof CardinalSpline) ||
1018
+ spline.fixedScale ||
1019
+ splineConfig.scale === undefined ||
1020
+ spline.scale === splineConfig.scale
1021
+ ) {
1022
+ return;
1023
+ }
1024
+
1025
+ spline.scale = splineConfig.scale;
1026
+ annotation.invalidated = true;
1027
+ }
1028
+
1029
+ private _updateSplineInstance(
1030
+ element: HTMLDivElement,
1031
+ annotation: SplineROIAnnotation
1032
+ ): ISpline {
1033
+ const enabledElement = getEnabledElement(element);
1034
+ const { viewport } = enabledElement;
1035
+ const { worldToCanvas } = viewport;
1036
+ const { data } = annotation;
1037
+ const { type: splineType, instance: spline } = annotation.data.spline;
1038
+ const splineConfig = this._getSplineConfig(splineType);
1039
+ const worldPoints = data.handles.points;
1040
+ const canvasPoints = worldPoints.map(worldToCanvas);
1041
+
1042
+ spline.setControlPoints(canvasPoints);
1043
+ spline.closed = !!data.spline?.closed;
1044
+
1045
+ // Update spline resolution in case it has changed
1046
+ if (spline.resolution !== splineConfig.resolution) {
1047
+ spline.resolution = parseInt(splineConfig.resolution);
1048
+ annotation.invalidated = true;
1049
+ }
1050
+
1051
+ // Update Cardinal spline scale in case it has changed
1052
+ if (
1053
+ spline instanceof CardinalSpline &&
1054
+ !spline.fixedScale &&
1055
+ splineConfig.scale !== undefined &&
1056
+ spline.scale !== splineConfig.scale
1057
+ ) {
1058
+ spline.scale = splineConfig.scale;
1059
+ annotation.invalidated = true;
1060
+ }
1061
+
1062
+ return spline;
1063
+ }
1064
+
1065
+ private _calculateCachedStats = (
1066
+ annotation: SplineROIAnnotation,
1067
+ element: HTMLDivElement
1068
+ ) => {
1069
+ if (!this.configuration.calculateStats) {
1070
+ return;
1071
+ }
1072
+ const data = annotation.data;
1073
+
1074
+ if (!data.spline.closed) {
1075
+ return;
1076
+ }
1077
+
1078
+ const enabledElement = getEnabledElement(element);
1079
+ const { viewport, renderingEngine } = enabledElement;
1080
+ const { cachedStats } = data;
1081
+ const { polyline: points } = data.spline;
1082
+ const targetIds = Object.keys(cachedStats);
1083
+
1084
+ for (let i = 0; i < targetIds.length; i++) {
1085
+ const targetId = targetIds[i];
1086
+ const image = this.getTargetIdImage(targetId, renderingEngine);
1087
+
1088
+ // If image does not exists for the targetId, skip. This can be due
1089
+ // to various reasons such as if the target was a volumeViewport, and
1090
+ // the volumeViewport has been decached in the meantime.
1091
+ if (!image) {
1092
+ continue;
1093
+ }
1094
+
1095
+ const { metadata } = image;
1096
+ const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));
1097
+
1098
+ // Using an arbitrary start point (canvasPoint), calculate the
1099
+ // mm spacing for the canvas in the X and Y directions.
1100
+ const canvasPoint = canvasCoordinates[0];
1101
+ const originalWorldPoint = viewport.canvasToWorld(canvasPoint);
1102
+ const deltaXPoint = viewport.canvasToWorld([
1103
+ canvasPoint[0] + 1,
1104
+ canvasPoint[1],
1105
+ ]);
1106
+ const deltaYPoint = viewport.canvasToWorld([
1107
+ canvasPoint[0],
1108
+ canvasPoint[1] + 1,
1109
+ ]);
1110
+
1111
+ const deltaInX = vec3.distance(originalWorldPoint, deltaXPoint);
1112
+ const deltaInY = vec3.distance(originalWorldPoint, deltaYPoint);
1113
+
1114
+ const scale = getCalibratedScale(image);
1115
+ let area =
1116
+ math.polyline.calculateAreaOfPoints(canvasCoordinates) / scale / scale;
1117
+
1118
+ // Convert from canvas_pixels ^2 to mm^2
1119
+ area *= deltaInX * deltaInY;
1120
+
1121
+ cachedStats[targetId] = {
1122
+ Modality: metadata.Modality,
1123
+ area,
1124
+ areaUnit: getCalibratedAreaUnits(null, image),
1125
+ };
1126
+ }
1127
+
1128
+ this.triggerAnnotationModified(annotation, enabledElement);
1129
+
1130
+ return cachedStats;
1131
+ };
1132
+ }
1133
+
1134
+ function defaultGetTextLines(data, targetId): string[] {
1135
+ const cachedVolumeStats = data.cachedStats[targetId];
1136
+ const { area, isEmptyArea, areaUnit } = cachedVolumeStats;
1137
+ const textLines: string[] = [];
1138
+
1139
+ if (area) {
1140
+ const areaLine = isEmptyArea
1141
+ ? `Area: Oblique not supported`
1142
+ : `Area: ${roundNumber(area)} ${areaUnit}`;
1143
+
1144
+ textLines.push(areaLine);
1145
+ }
1146
+
1147
+ return textLines;
1148
+ }
1149
+
1150
+ SplineROITool.toolName = 'SplineROI';
1151
+ export default SplineROITool;