flo-mat 4.1.4 → 5.0.1

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 (379) hide show
  1. package/__tests__/closest-boundary-point/get-endpoint-info.spec.ts +140 -0
  2. package/__tests__/closest-boundary-point/root-interval-to-distance-squared-interval.spec.ts +87 -0
  3. package/__tests__/cp-node/circle-identity.spec.ts +56 -0
  4. package/__tests__/cp-node/fs/get-all-on-loop-by-loop.spec.ts +55 -0
  5. package/__tests__/cp-node/fs/get-angle-between-mat-curves.spec.ts +39 -3
  6. package/__tests__/cp-node/fs/get-boundary-bezier-parts-to-next.spec.ts +87 -3
  7. package/__tests__/cp-node/fs/get-boundary-beziers-between.spec.ts +68 -3
  8. package/__tests__/cp-node/fs/get-boundary-beziers-to-next.spec.ts +41 -3
  9. package/__tests__/cp-node/fs/get-boundary-distance-to-next.spec.ts +20 -3
  10. package/__tests__/cp-node/fs/get-branch-beziers.spec.ts +42 -3
  11. package/__tests__/cp-node/fs/get-branch.spec.ts +60 -3
  12. package/__tests__/cp-node/fs/get-cp-node-ordering.spec.ts +19 -3
  13. package/__tests__/cp-node/fs/get-distance-to-next.spec.ts +37 -4
  14. package/__tests__/cp-node/fs/get-edge-direction.spec.ts +50 -3
  15. package/__tests__/cp-node/fs/get-first-exit.spec.ts +78 -3
  16. package/__tests__/cp-node/fs/get-hole-closers.spec.ts +32 -3
  17. package/__tests__/cp-node/fs/get-implied-boundary-bezier-between.spec.ts +48 -3
  18. package/__tests__/cp-node/fs/get-mat-curve-between.spec.ts +77 -3
  19. package/__tests__/cp-node/fs/get-mat-curve-to-next.spec.ts +17 -4
  20. package/__tests__/cp-node/fs/get-mat-curves-between.spec.ts +38 -3
  21. package/__tests__/cp-node/fs/get-salience.spec.ts +68 -3
  22. package/__tests__/cp-node/fs/get-vertex-forward-children.spec.ts +40 -3
  23. package/__tests__/cp-node/fs/insert-cp-node.spec.ts +87 -3
  24. package/__tests__/cp-node/fs/is-special.spec.ts +1 -1
  25. package/__tests__/cp-node/fs/remove-cp-node.spec.ts +42 -3
  26. package/__tests__/find-2-prong/calc-seperation-tolerance.spec.ts +46 -0
  27. package/__tests__/find-2-prong/get-medial/dd-get-medial-point-coeffs-same-curve.spec.ts +7 -5
  28. package/__tests__/find-2-prong/get-medial/dd-get-medial-point-coeffs.spec.ts +4 -2
  29. package/__tests__/find-2-prong/get-medial/get-closest-points.spec.ts +1 -1
  30. package/__tests__/floyd-warshall/get-graph.spec.ts +100 -0
  31. package/__tests__/get-mats-with-hole.ts +28 -0
  32. package/__tests__/get-mats1.spec.ts +61 -0
  33. package/__tests__/get-mats1.ts +75 -67
  34. package/__tests__/helpers/cp-node-by-id.ts +27 -0
  35. package/__tests__/helpers/expect-point-close.ts +42 -0
  36. package/__tests__/mat/hausdorff-distance-one-sided-exceeds.spec.ts +129 -0
  37. package/__tests__/mat/simplify-mat.spec.ts +85 -0
  38. package/__tests__/reconstruct-from-mat/reconstruct-from-mat.spec.ts +39 -0
  39. package/__tests__/specific-cases/test-svgs/achispado.svg +14 -0
  40. package/__tests__/utils/get-beziers-from-tangents.spec.ts +142 -0
  41. package/browser/index.js +17882 -13800
  42. package/browser/index.min.js +1 -1
  43. package/node/add-n-prong.ts/join-spokes.js +3 -2
  44. package/node/add-n-prong.ts/join-spokes.js.map +1 -1
  45. package/node/closest-boundary-point/cull-curve-pieces.d.ts +21 -0
  46. package/node/closest-boundary-point/cull-curve-pieces.js +40 -0
  47. package/node/closest-boundary-point/cull-curve-pieces.js.map +1 -0
  48. package/node/closest-boundary-point/get-close-boundary-points-certified.js +5 -2
  49. package/node/closest-boundary-point/get-close-boundary-points-certified.js.map +1 -1
  50. package/node/closest-boundary-point/get-endpoint-info.d.ts +39 -0
  51. package/node/closest-boundary-point/get-endpoint-info.js +78 -0
  52. package/node/closest-boundary-point/get-endpoint-info.js.map +1 -0
  53. package/node/closest-boundary-point/get-potential-closest-points-on-curve-certified.js +5 -19
  54. package/node/closest-boundary-point/get-potential-closest-points-on-curve-certified.js.map +1 -1
  55. package/node/closest-boundary-point/root-interval-to-distance-squared-interval.d.ts +17 -5
  56. package/node/closest-boundary-point/root-interval-to-distance-squared-interval.js +37 -50
  57. package/node/closest-boundary-point/root-interval-to-distance-squared-interval.js.map +1 -1
  58. package/node/corner/get-corner.d.ts +18 -1
  59. package/node/corner/get-corner.js +8 -1
  60. package/node/corner/get-corner.js.map +1 -1
  61. package/node/corner/get-interface-ccw.js.map +1 -1
  62. package/node/cp-node/cp-node.d.ts +4 -3
  63. package/node/cp-node/fs/enhance-cp-node.d.ts +9 -9
  64. package/node/cp-node/fs/get-all-on-loop-by-loop.js +0 -4
  65. package/node/cp-node/fs/get-all-on-loop-by-loop.js.map +1 -1
  66. package/node/cp-node/fs/get-angle-between-mat-curves.d.ts +1 -1
  67. package/node/cp-node/fs/get-angle-between-mat-curves.js +7 -4
  68. package/node/cp-node/fs/get-angle-between-mat-curves.js.map +1 -1
  69. package/node/cp-node/fs/get-boundary-beziers-between.d.ts +6 -4
  70. package/node/cp-node/fs/get-boundary-beziers-between.js +6 -4
  71. package/node/cp-node/fs/get-boundary-beziers-between.js.map +1 -1
  72. package/node/cp-node/fs/get-branch-beziers.d.ts +2 -1
  73. package/node/cp-node/fs/get-branch-beziers.js +3 -2
  74. package/node/cp-node/fs/get-branch-beziers.js.map +1 -1
  75. package/node/cp-node/fs/get-branch.js +1 -2
  76. package/node/cp-node/fs/get-branch.js.map +1 -1
  77. package/node/cp-node/fs/get-distance-to-next.js +4 -2
  78. package/node/cp-node/fs/get-distance-to-next.js.map +1 -1
  79. package/node/cp-node/fs/get-edge-direction.js +3 -2
  80. package/node/cp-node/fs/get-edge-direction.js.map +1 -1
  81. package/node/cp-node/fs/get-first-exit.d.ts +7 -3
  82. package/node/cp-node/fs/get-first-exit.js +13 -11
  83. package/node/cp-node/fs/get-first-exit.js.map +1 -1
  84. package/node/cp-node/fs/get-implied-boundary-bezier-between.d.ts +10 -2
  85. package/node/cp-node/fs/get-implied-boundary-bezier-between.js +11 -54
  86. package/node/cp-node/fs/get-implied-boundary-bezier-between.js.map +1 -1
  87. package/node/cp-node/fs/get-mat-curve-between.d.ts +5 -3
  88. package/node/cp-node/fs/get-mat-curve-between.js +6 -49
  89. package/node/cp-node/fs/get-mat-curve-between.js.map +1 -1
  90. package/node/cp-node/fs/get-mat-curve-to-next.d.ts +1 -1
  91. package/node/cp-node/fs/get-mat-curves-between.d.ts +8 -0
  92. package/node/cp-node/fs/get-mat-curves-between.js +9 -2
  93. package/node/cp-node/fs/get-mat-curves-between.js.map +1 -1
  94. package/node/cp-node/fs/get-real-prong-count.d.ts +2 -1
  95. package/node/cp-node/fs/get-real-prong-count.js +11 -5
  96. package/node/cp-node/fs/get-real-prong-count.js.map +1 -1
  97. package/node/cp-node/fs/get-salience.d.ts +10 -0
  98. package/node/cp-node/fs/get-salience.js +17 -1
  99. package/node/cp-node/fs/get-salience.js.map +1 -1
  100. package/node/cp-node/fs/is-fully-terminating.d.ts +4 -2
  101. package/node/cp-node/fs/is-fully-terminating.js +6 -17
  102. package/node/cp-node/fs/is-fully-terminating.js.map +1 -1
  103. package/node/cp-node/fs/is-on-same-circle.d.ts +3 -0
  104. package/node/cp-node/fs/is-on-same-circle.js +4 -4
  105. package/node/cp-node/fs/is-on-same-circle.js.map +1 -1
  106. package/node/cp-node/fs/is-on-same-loop.d.ts +1 -1
  107. package/node/cp-node/fs/is-special.d.ts +7 -0
  108. package/node/cp-node/fs/is-special.js +9 -2
  109. package/node/cp-node/fs/is-special.js.map +1 -1
  110. package/node/cp-node/fs/is-vertex.d.ts +1 -1
  111. package/node/cp-node/fs/traverse-edges.d.ts +7 -3
  112. package/node/cp-node/fs/traverse-edges.js +7 -3
  113. package/node/cp-node/fs/traverse-edges.js.map +1 -1
  114. package/node/cp-node/stringification/from-stringifyable.d.ts +2 -2
  115. package/node/cp-node/stringification/from-stringifyable.js +17 -1
  116. package/node/cp-node/stringification/from-stringifyable.js.map +1 -1
  117. package/node/cp-node/stringification/to-stringifyable.d.ts +3 -3
  118. package/node/cp-node/stringification/to-stringifyable.js +3 -3
  119. package/node/debug/debug.d.ts +26 -3
  120. package/node/debug/debug.js +16 -5
  121. package/node/debug/debug.js.map +1 -1
  122. package/node/find-2-prong/add-2-prong.js +7 -9
  123. package/node/find-2-prong/add-2-prong.js.map +1 -1
  124. package/node/find-2-prong/calc-seperation-tolerance.d.ts +28 -5
  125. package/node/find-2-prong/calc-seperation-tolerance.js +32 -89
  126. package/node/find-2-prong/calc-seperation-tolerance.js.map +1 -1
  127. package/node/find-2-prong/find-2-prong.js +8 -10
  128. package/node/find-2-prong/find-2-prong.js.map +1 -1
  129. package/node/find-2-prong/find-and-add-2-prongs.js +2 -2
  130. package/node/find-2-prong/find-and-add-2-prongs.js.map +1 -1
  131. package/node/find-2-prong/get-medial/get-antipodal-points.d.ts +1 -2
  132. package/node/find-2-prong/get-medial/get-antipodal-points.js +2 -3
  133. package/node/find-2-prong/get-medial/get-antipodal-points.js.map +1 -1
  134. package/node/find-2-prong/get-medial/get-closest-points.d.ts +1 -8
  135. package/node/find-2-prong/get-medial/get-closest-points.js +4 -40
  136. package/node/find-2-prong/get-medial/get-closest-points.js.map +1 -1
  137. package/node/find-2-prong/reduce-radius.d.ts +1 -1
  138. package/node/find-2-prong/reduce-radius.js +6 -3
  139. package/node/find-2-prong/reduce-radius.js.map +1 -1
  140. package/node/find-3-prong/calc-better-x.js +1 -1
  141. package/node/find-3-prong/calc-better-x.js.map +1 -1
  142. package/node/find-3-prong/find-3-prong-for-delta3s.d.ts +3 -2
  143. package/node/find-3-prong/find-3-prong-for-delta3s.js +36 -42
  144. package/node/find-3-prong/find-3-prong-for-delta3s.js.map +1 -1
  145. package/node/find-3-prong/find-3-prong.js +3 -1
  146. package/node/find-3-prong/find-3-prong.js.map +1 -1
  147. package/node/find-3-prong/find-and-add-3-prongs.js +16 -0
  148. package/node/find-3-prong/find-and-add-3-prongs.js.map +1 -1
  149. package/node/find-mat/create-initial-cp-tree.js +2 -2
  150. package/node/find-mat/create-initial-cp-tree.js.map +1 -1
  151. package/node/find-mat/find-mat.js +3 -4
  152. package/node/find-mat/find-mat.js.map +1 -1
  153. package/node/find-mat/find-mats.d.ts +6 -12
  154. package/node/find-mat/find-mats.js +10 -27
  155. package/node/find-mat/find-mats.js.map +1 -1
  156. package/node/find-mat/get-dull-corners-on-loop.js +2 -2
  157. package/node/find-mat/get-dull-corners-on-loop.js.map +1 -1
  158. package/node/find-mat/get-for-1-prongs-on-loop.js +36 -8
  159. package/node/find-mat/get-for-1-prongs-on-loop.js.map +1 -1
  160. package/node/find-mat/get-meta.d.ts +14 -4
  161. package/node/find-mat/get-meta.js +30 -24
  162. package/node/find-mat/get-meta.js.map +1 -1
  163. package/node/find-mat/get-sharp-corners.js +2 -2
  164. package/node/find-mat/get-sharp-corners.js.map +1 -1
  165. package/node/find-mat/mat-options.d.ts +2 -2
  166. package/node/floyd-warshall/get-graph.d.ts +12 -1
  167. package/node/floyd-warshall/get-graph.js +41 -38
  168. package/node/floyd-warshall/get-graph.js.map +1 -1
  169. package/node/geometry/get-bounding-box-.d.ts +16 -2
  170. package/node/geometry/get-bounding-box-.js +5 -2
  171. package/node/geometry/get-bounding-box-.js.map +1 -1
  172. package/node/geometry/get-closest-square-distance-to-rect.d.ts +3 -0
  173. package/node/geometry/get-closest-square-distance-to-rect.js +7 -5
  174. package/node/geometry/get-closest-square-distance-to-rect.js.map +1 -1
  175. package/node/geometry/get-closest-squared-distance-to-rotated-rect.d.ts +4 -0
  176. package/node/geometry/get-closest-squared-distance-to-rotated-rect.js +12 -5
  177. package/node/geometry/get-closest-squared-distance-to-rotated-rect.js.map +1 -1
  178. package/node/get-medial-points/double-double/dd-get-medial-point-coeffs-bez3.js +1 -2
  179. package/node/get-medial-points/double-double/dd-get-medial-point-coeffs-bez3.js.map +1 -1
  180. package/node/index.d.ts +3 -2
  181. package/node/index.js +2 -1
  182. package/node/index.js.map +1 -1
  183. package/node/mat/clone-mat.d.ts +9 -0
  184. package/node/mat/clone-mat.js +17 -0
  185. package/node/mat/clone-mat.js.map +1 -0
  186. package/node/mat/clone-meta.d.ts +12 -0
  187. package/node/mat/clone-meta.js +24 -0
  188. package/node/mat/clone-meta.js.map +1 -0
  189. package/node/mat/get-boundary-piece-beziers.d.ts +5 -0
  190. package/node/mat/get-boundary-piece-beziers.js +7 -93
  191. package/node/mat/get-boundary-piece-beziers.js.map +1 -1
  192. package/node/mat/get-closeby-cp-if-exist.js +2 -1
  193. package/node/mat/get-closeby-cp-if-exist.js.map +1 -1
  194. package/node/mat/get-largest-3-prong.d.ts +6 -0
  195. package/node/mat/get-largest-3-prong.js +8 -5
  196. package/node/mat/get-largest-3-prong.js.map +1 -1
  197. package/node/mat/get-largest-vertex.d.ts +15 -1
  198. package/node/mat/get-largest-vertex.js +20 -7
  199. package/node/mat/get-largest-vertex.js.map +1 -1
  200. package/node/mat/hausdorff-distance-one-sided-exceeds.d.ts +79 -0
  201. package/node/mat/hausdorff-distance-one-sided-exceeds.js +251 -0
  202. package/node/mat/hausdorff-distance-one-sided-exceeds.js.map +1 -0
  203. package/node/mat/mat-meta.d.ts +0 -6
  204. package/node/mat/simplify-mat.d.ts +25 -8
  205. package/node/mat/simplify-mat.js +182 -53
  206. package/node/mat/simplify-mat.js.map +1 -1
  207. package/node/mat/trim-mat.d.ts +4 -2
  208. package/node/mat/trim-mat.js +3 -1
  209. package/node/mat/trim-mat.js.map +1 -1
  210. package/node/point-on-shape/get-boundary-tangent-line.d.ts +19 -0
  211. package/node/point-on-shape/get-boundary-tangent-line.js +33 -0
  212. package/node/point-on-shape/get-boundary-tangent-line.js.map +1 -0
  213. package/node/point-on-shape/get-osculating-circle.d.ts +6 -4
  214. package/node/point-on-shape/get-osculating-circle.js +6 -4
  215. package/node/point-on-shape/get-osculating-circle.js.map +1 -1
  216. package/node/point-on-shape/get-pos-corner.js +2 -2
  217. package/node/point-on-shape/get-pos-corner.js.map +1 -1
  218. package/node/point-on-shape/point-on-shape.d.ts +5 -0
  219. package/node/reconstruct-from-mat/reconstruct-from-mat.js +20 -80
  220. package/node/reconstruct-from-mat/reconstruct-from-mat.js.map +1 -1
  221. package/node/reconstruct-from-mat/remove-sat-induced-trivial-3prongs.d.ts +4 -0
  222. package/node/reconstruct-from-mat/remove-sat-induced-trivial-3prongs.js +4 -0
  223. package/node/reconstruct-from-mat/remove-sat-induced-trivial-3prongs.js.map +1 -1
  224. package/node/sat/cull.d.ts +7 -6
  225. package/node/sat/cull.js +13 -16
  226. package/node/sat/cull.js.map +1 -1
  227. package/node/sat/get-sat-culls.d.ts +12 -2
  228. package/node/sat/get-sat-culls.js +21 -16
  229. package/node/sat/get-sat-culls.js.map +1 -1
  230. package/node/svg/get-shape-bounds.d.ts +1 -12
  231. package/node/svg/get-shape-bounds.js +2 -3
  232. package/node/svg/get-shape-bounds.js.map +1 -1
  233. package/node/sweep-line/sweep-line.js.map +1 -1
  234. package/node/utils/get-beziers-from-tangents.d.ts +28 -0
  235. package/node/utils/get-beziers-from-tangents.js +92 -0
  236. package/node/utils/get-beziers-from-tangents.js.map +1 -0
  237. package/node/vertex/remove-vertex.d.ts +5 -3
  238. package/node/vertex/remove-vertex.js +5 -3
  239. package/node/vertex/remove-vertex.js.map +1 -1
  240. package/package.json +16 -16
  241. package/qv-infinite-loop.md +120 -0
  242. package/src/add-n-prong.ts/join-spokes.ts +4 -2
  243. package/src/closest-boundary-point/cull-curve-pieces.ts +49 -0
  244. package/src/closest-boundary-point/get-close-boundary-points-certified.ts +5 -2
  245. package/src/closest-boundary-point/get-endpoint-info.ts +96 -0
  246. package/src/closest-boundary-point/get-potential-closest-points-on-curve-certified.ts +5 -23
  247. package/src/closest-boundary-point/root-interval-to-distance-squared-interval.ts +42 -60
  248. package/src/corner/get-corner.ts +10 -1
  249. package/src/corner/get-interface-ccw.ts +2 -2
  250. package/src/cp-node/cp-node.ts +4 -3
  251. package/src/cp-node/fs/get-all-on-loop-by-loop.ts +0 -4
  252. package/src/cp-node/fs/get-angle-between-mat-curves.ts +9 -5
  253. package/src/cp-node/fs/get-boundary-beziers-between.ts +10 -8
  254. package/src/cp-node/fs/get-branch-beziers.ts +3 -2
  255. package/src/cp-node/fs/get-branch.ts +1 -2
  256. package/src/cp-node/fs/get-distance-to-next.ts +4 -2
  257. package/src/cp-node/fs/get-edge-direction.ts +4 -2
  258. package/src/cp-node/fs/get-first-exit.ts +15 -14
  259. package/src/cp-node/fs/get-implied-boundary-bezier-between.ts +18 -70
  260. package/src/cp-node/fs/get-mat-curve-between.ts +10 -67
  261. package/src/cp-node/fs/get-mat-curves-between.ts +9 -2
  262. package/src/cp-node/fs/get-real-prong-count.ts +11 -7
  263. package/src/cp-node/fs/get-salience.ts +15 -2
  264. package/src/cp-node/fs/is-fully-terminating.ts +6 -22
  265. package/src/cp-node/fs/is-on-same-circle.ts +6 -7
  266. package/src/cp-node/fs/is-on-same-loop.ts +2 -2
  267. package/src/cp-node/fs/is-special.ts +11 -6
  268. package/src/cp-node/fs/is-vertex.ts +1 -1
  269. package/src/cp-node/fs/traverse-edges.ts +8 -4
  270. package/src/cp-node/stringification/from-stringifyable.ts +75 -58
  271. package/src/cp-node/stringification/to-stringifyable.ts +3 -3
  272. package/src/debug/debug.ts +44 -8
  273. package/src/find-2-prong/add-2-prong.ts +9 -11
  274. package/src/find-2-prong/calc-seperation-tolerance.ts +34 -115
  275. package/src/find-2-prong/find-2-prong.ts +9 -11
  276. package/src/find-2-prong/find-and-add-2-prongs.ts +2 -2
  277. package/src/find-2-prong/get-medial/get-antipodal-points.ts +1 -3
  278. package/src/find-2-prong/get-medial/get-closest-points.ts +6 -51
  279. package/src/find-2-prong/reduce-radius.ts +6 -4
  280. package/src/find-3-prong/calc-better-x.ts +2 -2
  281. package/src/find-3-prong/find-3-prong-for-delta3s.ts +197 -214
  282. package/src/find-3-prong/find-3-prong.ts +54 -51
  283. package/src/find-3-prong/find-and-add-3-prongs.ts +20 -1
  284. package/src/find-mat/create-initial-cp-tree.ts +2 -2
  285. package/src/find-mat/find-mat.ts +4 -6
  286. package/src/find-mat/find-mats.ts +83 -105
  287. package/src/find-mat/get-dull-corners-on-loop.ts +2 -2
  288. package/src/find-mat/get-for-1-prongs-on-loop.ts +40 -8
  289. package/src/find-mat/get-meta.ts +34 -39
  290. package/src/find-mat/get-sharp-corners.ts +2 -2
  291. package/src/find-mat/mat-options.ts +2 -2
  292. package/src/floyd-warshall/get-graph.ts +46 -47
  293. package/src/geometry/get-bounding-box-.ts +5 -2
  294. package/src/geometry/get-closest-square-distance-to-rect.ts +7 -7
  295. package/src/geometry/get-closest-squared-distance-to-rotated-rect.ts +15 -13
  296. package/src/get-medial-points/double-double/dd-get-medial-point-coeffs-bez3.ts +1 -2
  297. package/src/index.ts +3 -2
  298. package/src/mat/clone-mat.ts +9 -3
  299. package/src/mat/clone-meta.ts +8 -0
  300. package/src/mat/get-boundary-piece-beziers.ts +16 -117
  301. package/src/mat/get-closeby-cp-if-exist.ts +3 -1
  302. package/src/mat/get-largest-3-prong.ts +9 -6
  303. package/src/mat/get-largest-vertex.ts +25 -9
  304. package/src/mat/hausdorff-distance-one-sided-exceeds.ts +314 -0
  305. package/src/mat/mat-meta.ts +0 -9
  306. package/src/mat/simplify-mat.ts +254 -116
  307. package/src/mat/trim-mat.ts +6 -4
  308. package/src/point-on-shape/get-boundary-tangent-line.ts +36 -0
  309. package/src/point-on-shape/get-osculating-circle.ts +7 -5
  310. package/src/point-on-shape/get-pos-corner.ts +2 -2
  311. package/src/point-on-shape/point-on-shape.ts +5 -0
  312. package/src/reconstruct-from-mat/reconstruct-from-mat.ts +22 -111
  313. package/src/reconstruct-from-mat/remove-sat-induced-trivial-3prongs.ts +4 -0
  314. package/src/sat/cull.ts +16 -20
  315. package/src/sat/get-sat-culls.ts +28 -23
  316. package/src/svg/get-shape-bounds.ts +2 -3
  317. package/src/sweep-line/sweep-line.ts +1 -1
  318. package/src/utils/deg-to-rad.ts +4 -2
  319. package/src/utils/get-beziers-from-tangents.ts +110 -0
  320. package/src/utils/map-map.ts +1 -1
  321. package/src/utils/rad-to-deg.ts +4 -1
  322. package/src/vertex/remove-vertex.ts +5 -3
  323. package/webpack.config.cjs +7 -2
  324. package/__tests__/cp-node/fs/get-distance-between.spec.ts +0 -7
  325. package/__tests__/cp-node/fs/get-terminating-on-circle.spec.ts +0 -24
  326. package/__tests__/cp-node/fs/is-single-3-prong.spec.ts +0 -17
  327. package/__tests__/cp-node/fs/iterate-all-on-loop.spec.ts +0 -17
  328. package/__tests__/cp-node/fs/traverse-all-forward.spec.ts +0 -14
  329. package/node/closest-boundary-point/cull-bezier-pieces-1.d.ts +0 -9
  330. package/node/closest-boundary-point/cull-bezier-pieces-1.js +0 -17
  331. package/node/closest-boundary-point/cull-bezier-pieces-1.js.map +0 -1
  332. package/node/closest-boundary-point/cull-by-loose-bounding-box.d.ts +0 -13
  333. package/node/closest-boundary-point/cull-by-loose-bounding-box.js +0 -27
  334. package/node/closest-boundary-point/cull-by-loose-bounding-box.js.map +0 -1
  335. package/node/closest-boundary-point/cull-by-tight-boundary-box.d.ts +0 -11
  336. package/node/closest-boundary-point/cull-by-tight-boundary-box.js +0 -27
  337. package/node/closest-boundary-point/cull-by-tight-boundary-box.js.map +0 -1
  338. package/node/cp-node/fs/find-first.d.ts +0 -10
  339. package/node/cp-node/fs/find-first.js +0 -19
  340. package/node/cp-node/fs/find-first.js.map +0 -1
  341. package/node/debug/functions/check-ordering.d.ts +0 -4
  342. package/node/debug/functions/check-ordering.js +0 -43
  343. package/node/debug/functions/check-ordering.js.map +0 -1
  344. package/node/find-2-prong/cull-bezier-pieces.d.ts +0 -13
  345. package/node/find-2-prong/cull-bezier-pieces.js +0 -43
  346. package/node/find-2-prong/cull-bezier-pieces.js.map +0 -1
  347. package/node/floyd-warshall/get-hole-closer-pairs.d.ts +0 -8
  348. package/node/floyd-warshall/get-hole-closer-pairs.js +0 -27
  349. package/node/floyd-warshall/get-hole-closer-pairs.js.map +0 -1
  350. package/node/utils/dual-set.d.ts +0 -27
  351. package/node/utils/dual-set.js +0 -55
  352. package/node/utils/dual-set.js.map +0 -1
  353. package/node/utils/tri-map.d.ts +0 -27
  354. package/node/utils/tri-map.js +0 -49
  355. package/node/utils/tri-map.js.map +0 -1
  356. package/src/closest-boundary-point/cull-bezier-pieces-1.ts +0 -36
  357. package/src/closest-boundary-point/cull-by-loose-bounding-box.ts +0 -42
  358. package/src/closest-boundary-point/cull-by-tight-boundary-box.ts +0 -43
  359. package/src/closest-boundary-point/get-p-from-box.ts +0 -14
  360. package/src/cp-node/fs/add-distance-from.ts +0 -49
  361. package/src/cp-node/fs/get-distance-between.ts +0 -16
  362. package/src/cp-node/fs/get-terminating-on-circle.ts +0 -24
  363. package/src/cp-node/fs/is-single-3-prong.ts +0 -15
  364. package/src/cp-node/fs/iterate-all-on-loop.ts +0 -15
  365. package/src/cp-node/fs/traverse-all-forward.ts +0 -24
  366. package/src/debug/functions/draw/draw-beziers-as-single-path.ts +0 -40
  367. package/src/debug/functions/draw/draw-disk.ts +0 -34
  368. package/src/debug/functions/draw/xmlns.ts +0 -5
  369. package/src/find-2-prong/cull-bezier-pieces.ts +0 -61
  370. package/src/find-2-prong/find-and-add-2-prong.ts +0 -30
  371. package/src/find-2-prong/get-medial/get-closest-points-d.ts +0 -213
  372. package/src/find-2-prong/x-for-debugging.ts +0 -13
  373. package/src/floyd-warshall/get-hole-closer-pairs.ts +0 -35
  374. package/src/mat/get-two-prong-type.ts +0 -13
  375. package/src/point-on-shape/is-pos-dull-corner.ts +0 -18
  376. package/src/point-on-shape/is-pos-quite-dull-corner.ts +0 -18
  377. package/src/point-on-shape/is-pos-quite-sharp-corner.ts +0 -18
  378. package/src/point-on-shape/pos-to-human-string.ts +0 -18
  379. package/src/sweep-line/are-boxes-intersecting.ts +0 -37
@@ -1,7 +1,46 @@
1
1
  import { expect, test } from '@jest/globals';
2
2
  import { getBranchBeziers } from '../../../src/cp-node/fs/get-branch-beziers.js';
3
+ import { getBranch } from '../../../src/cp-node/fs/get-branch.js';
4
+ import { getMatCurveToNext } from '../../../src/cp-node/fs/get-mat-curve-to-next.js';
5
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
6
+ import { getMats1 } from '../../get-mats1.js';
7
+ import { cpNodesByIdOn } from '../../helpers/cp-node-by-id.js';
8
+ import { expectBeziersConnected, expectPointClose } from '../../helpers/expect-point-close.js';
3
9
 
4
- test.skip('getBranchBeziers [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
10
+
11
+ test('getBranchBeziers', function() {
12
+ const { cpNode } = getMats1()[0];
13
+ const byId = cpNodesByIdOn(cpNode);
14
+
15
+ {
16
+ // the branch [143, 11]: from the 3-prong center to the 2-prong center
17
+ // to the sharp corner [24,8]
18
+ const pss = getBranchBeziers(byId(1));
19
+ expect(pss).toEqual([
20
+ ...getMatCurveToNext(byId(143)),
21
+ ...getMatCurveToNext(byId(11))
22
+ ]);
23
+ expectPointClose(pss[0][0], [81.5, -12.641417453935567]);
24
+ const last = pss[pss.length-1];
25
+ expectPointClose(last[last.length-1], [24, 8]);
26
+ }
27
+
28
+ // For every node: the (flattened) beziers are the edges of the branch in
29
+ // order, joined end to start, from the first vertex center to the last.
30
+ for (const cp of getAllOnLoop(cpNode)) {
31
+ const branch = getBranch(cp);
32
+ const pss = getBranchBeziers(cp);
33
+
34
+ expect(pss).toEqual(branch.flatMap(getMatCurveToNext));
35
+ expect(pss.length).toBeGreaterThanOrEqual(branch.length);
36
+ expectBeziersConnected(pss);
37
+ expectPointClose(pss[0][0], branch[0].pointOnShape.circle.center);
38
+ const last = pss[pss.length-1];
39
+ expectPointClose(
40
+ last[last.length-1],
41
+ branch[branch.length-1].next.pointOnShape.circle.center
42
+ );
43
+ // lines and quadratics only
44
+ for (const ps of pss) { expect([2,3]).toContain(ps.length); }
45
+ }
7
46
  });
@@ -1,7 +1,64 @@
1
1
  import { expect, test } from '@jest/globals';
2
2
  import { getBranch } from '../../../src/cp-node/fs/get-branch.js';
3
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
4
+ import { getRealProngCount } from '../../../src/cp-node/fs/get-real-prong-count.js';
5
+ import { isTerminating } from '../../../src/cp-node/fs/is-terminating.js';
6
+ import { getMats1 } from '../../get-mats1.js';
7
+ import { cpNodesByIdOn } from '../../helpers/cp-node-by-id.js';
3
8
 
4
- test.skip('getBranch [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
9
+
10
+ test('getBranch', function() {
11
+ const { cpNode } = getMats1()[0];
12
+ const byId = cpNodesByIdOn(cpNode);
13
+ const ids = (cps: { id: number }[]) => cps.map(cp => cp.id);
14
+
15
+ // The branch from the sharp corner [24,8] via the 2-prong {10,11} to the
16
+ // 3-prong {142,143,144}. `getBranch` walks forward to the first vertex
17
+ // that is not a 2-prong and then returns the branch *back* from there
18
+ // (without the final node), so the result depends on the direction of
19
+ // the given node.
20
+ expect(ids(getBranch(byId(1)))).toEqual([143, 11]);
21
+ expect(ids(getBranch(byId(10)))).toEqual([143, 11]);
22
+ expect(ids(getBranch(byId(11)))).toEqual([1, 10]);
23
+ expect(ids(getBranch(byId(143)))).toEqual([1, 10]);
24
+ // a terminating node first steps to its non-terminating twin
25
+ expect(ids(getBranch(byId(0)))).toEqual([143, 11]);
26
+
27
+ // single-edge branches between non-2-prong vertices
28
+ expect(ids(getBranch(byId(144)))).toEqual([146]); // 3-prong -> 3-prong
29
+ expect(ids(getBranch(byId(147)))).toEqual([3]); // 3-prong -> leaf
30
+
31
+ // Structural invariants for every node in the fixture (none are trivial)
32
+ for (const cp of getAllOnLoop(cpNode)) {
33
+ const branch = getBranch(cp);
34
+ expect(branch.length).toBeGreaterThan(0);
35
+
36
+ // consecutive along the boundary
37
+ for (let i=1; i<branch.length; i++) {
38
+ expect(branch[i]).toBe(branch[i-1].next);
39
+ }
40
+ // it starts at a vertex that is not a 2-prong, passes only 2-prongs
41
+ // and the (excluded) final node is again not a 2-prong
42
+ expect(getRealProngCount(branch[0])).not.toBe(2);
43
+ for (const cp of branch.slice(1)) {
44
+ expect(getRealProngCount(cp)).toBe(2);
45
+ }
46
+ const final = branch[branch.length-1].next;
47
+ expect(getRealProngCount(final)).not.toBe(2);
48
+ // every node of the branch represents a real (non-terminating) edge
49
+ for (const cp of branch) { expect(isTerminating(cp)).toBe(false); }
50
+ }
51
+
52
+ // The fixture MAT has 5 leaves, 3 3-prongs and 2 2-prongs, so there are
53
+ // 7 distinct branches (up to direction).
54
+ const branchKeys = new Set<string>();
55
+ for (const cp of getAllOnLoop(cpNode)) {
56
+ const branch = getBranch(cp);
57
+ // key by the unordered pair of end vertices (via their circles)
58
+ const a = branch[0].pointOnShape.circle;
59
+ const b = branch[branch.length-1].next.pointOnShape.circle;
60
+ const key = [a, b].map(c => `${c.center[0]},${c.center[1]},${c.radius}`).sort().join('|');
61
+ branchKeys.add(key);
62
+ }
63
+ expect(branchKeys.size).toBe(7);
7
64
  });
@@ -1,7 +1,23 @@
1
1
  import { expect, test } from '@jest/globals';
2
2
  import { getCpNodeOrdering } from '../../../src/cp-node/fs/get-cp-node-ordering.js';
3
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
4
+ import { getMats1 } from '../../get-mats1.js';
5
+ import { cpNodesByIdOn } from '../../helpers/cp-node-by-id.js';
3
6
 
4
- test.skip('getCpNodeOrdering [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
7
+
8
+ test('getCpNodeOrdering', function() {
9
+ const { cpNode } = getMats1()[0];
10
+ const byId = cpNodesByIdOn(cpNode);
11
+
12
+ expect(getCpNodeOrdering(byId(147))).toEqual({
13
+ idx: 2, t: 0.2282608695652173, order: 0, order2: 0
14
+ });
15
+ // the two nodes at the same corner point differ in `order` only
16
+ expect(getCpNodeOrdering(byId(0))).toEqual({ idx: 0, t: 1, order: -1, order2: 0 });
17
+ expect(getCpNodeOrdering(byId(1))).toEqual({ idx: 0, t: 1, order: 1, order2: 0 });
18
+
19
+ for (const cp of getAllOnLoop(cpNode)) {
20
+ const { curve, t, order, order2 } = cp.pointOnShape;
21
+ expect(getCpNodeOrdering(cp)).toEqual({ idx: curve.idx, t, order, order2 });
22
+ }
7
23
  });
@@ -1,7 +1,40 @@
1
1
  import { expect, test } from '@jest/globals';
2
- import { getMatDistanceToNext } from '../../../src/cp-node/fs/get-distance-to-next.js';
2
+ import { distanceBetween } from 'flo-vector2d';
3
+ import { getMatDistanceToNext, getMatDistanceToNext$ } from '../../../src/cp-node/fs/get-distance-to-next.js';
4
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
5
+ import { isTerminating } from '../../../src/cp-node/fs/is-terminating.js';
6
+ import { getMats1 } from '../../get-mats1.js';
7
+ import { cpNodesByIdOn } from '../../helpers/cp-node-by-id.js';
3
8
 
4
- test.skip('getMatDistanceToNext [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
9
+
10
+ test('getMatDistanceToNext', function() {
11
+ const { cpNode } = getMats1()[0];
12
+ const byId = cpNodesByIdOn(cpNode);
13
+ const chord = (cp: typeof cpNode) => distanceBetween(
14
+ cp.pointOnShape.circle.center,
15
+ cp.next.pointOnShape.circle.center
16
+ );
17
+
18
+ // straight MAT edges: the arc length is the distance between the centers
19
+ // 3-prong center [81.5,18.5] -> sharp corner [46,54]
20
+ expect(getMatDistanceToNext(byId(147))).toBeCloseTo(35.5*Math.SQRT2, 9);
21
+ // sharp corner [24,8] -> 2-prong center [46,-5.21425847941623] (along the
22
+ // bisector of the corner)
23
+ expect(getMatDistanceToNext(byId(1))).toBeCloseTo(Math.hypot(22, 8 + 5.21425847941623), 9);
24
+
25
+ // a curved MAT edge (equidistant from a corner point and a line, i.e. a
26
+ // parabola) is strictly longer than its chord
27
+ expect(getMatDistanceToNext(byId(10))).toBeGreaterThan(chord(byId(10)) + 0.01);
28
+
29
+ // the two nodes at a 1-prong share a center
30
+ expect(getMatDistanceToNext(byId(0))).toBe(0);
31
+
32
+ for (const cp of getAllOnLoop(cpNode)) {
33
+ const d = getMatDistanceToNext(cp);
34
+ // never shorter than the chord
35
+ expect(d).toBeGreaterThanOrEqual(chord(cp) - 1e-9);
36
+ if (isTerminating(cp)) { expect(d).toBe(0); }
37
+ // the memoized version agrees
38
+ expect(getMatDistanceToNext$(cp)).toBe(d);
39
+ }
7
40
  });
@@ -1,7 +1,54 @@
1
1
  import { expect, test } from '@jest/globals';
2
+ import { distanceBetween, dot, fromTo, toUnitVector } from 'flo-vector2d';
2
3
  import { getEdgeDirection } from '../../../src/cp-node/fs/get-edge-direction.js';
4
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
5
+ import { isTerminating } from '../../../src/cp-node/fs/is-terminating.js';
6
+ import { getMats1 } from '../../get-mats1.js';
7
+ import { cpNodesByIdOn } from '../../helpers/cp-node-by-id.js';
8
+ import { expectPointClose } from '../../helpers/expect-point-close.js';
3
9
 
4
- test.skip('getEdgeDirection [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
10
+
11
+ test('getEdgeDirection', function() {
12
+ const { cpNode } = getMats1()[0];
13
+ const byId = cpNodesByIdOn(cpNode);
14
+ const dir = (id: number) => {
15
+ const [c, v] = getEdgeDirection(byId(id));
16
+ return fromTo(c, v);
17
+ };
18
+
19
+ // 3-prong with center [81.5,18.5] and contact points [46,18.5] (id 147),
20
+ // [81.5,54] (id 145) and [117,18.5] (id 146): each edge leaves along the
21
+ // bisector of the contact point of the node and the next one on the circle.
22
+ const s = Math.SQRT1_2;
23
+ expectPointClose(dir(147), [-s, s]);
24
+ expectPointClose(dir(145), [ s, s]);
25
+ expectPointClose(dir(146), [ 0,-1]);
26
+
27
+ // 1-prong at the sharp corner [24,8] (ids 0 and 1): the edge leaves along
28
+ // the bisector of the corner (both nodes sit at the same point).
29
+ const bisector = toUnitVector([
30
+ 1 + 58/Math.hypot(58, 109), // unit([46,8]-[24,8]) + unit([82,-101]-[24,8])
31
+ 0 - 109/Math.hypot(58, 109)
32
+ ]);
33
+ expectPointClose(dir(0), bisector);
34
+ expectPointClose(dir(1), bisector);
35
+
36
+ // 2-prong {10,11} on the line [24,8]->[46,8] and the other edge of the
37
+ // corner: its direction continues the corner bisector (the MAT is smooth
38
+ // there) - and the opposite way for the node pointing back to the corner.
39
+ expectPointClose(dir(10), bisector);
40
+ expectPointClose(dir(11), [-bisector[0], -bisector[1]]);
41
+
42
+ for (const cp of getAllOnLoop(cpNode)) {
43
+ const [c, v] = getEdgeDirection(cp);
44
+ // starts at the circle center, unit length
45
+ expect(c).toBe(cp.pointOnShape.circle.center);
46
+ expect(distanceBetween(c, v)).toBeCloseTo(1, 12);
47
+
48
+ // points towards the next vertex (never backwards)
49
+ if (!isTerminating(cp)) {
50
+ const toNext = fromTo(c, cp.next.pointOnShape.circle.center);
51
+ expect(dot(fromTo(c, v), toNext)).toBeGreaterThan(0);
52
+ }
53
+ }
7
54
  });
@@ -1,7 +1,82 @@
1
1
  import { expect, test } from '@jest/globals';
2
+ import type { CpNode } from '../../../src/cp-node/cp-node.js';
2
3
  import { getFirstExit } from '../../../src/cp-node/fs/get-first-exit.js';
4
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
5
+ import { getAllOnCircle } from '../../../src/cp-node/fs/get-all-on-circle.js';
6
+ import { getProngCount } from '../../../src/cp-node/fs/get-prong-count.js';
7
+ import { isTerminating } from '../../../src/cp-node/fs/is-terminating.js';
8
+ import { getMats1 } from '../../get-mats1.js';
9
+ import { getMatsWithHole } from '../../get-mats-with-hole.js';
10
+ import { cpNodesByIdOn } from '../../helpers/cp-node-by-id.js';
3
11
 
4
- test.skip('getFirstExit [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
12
+
13
+ /** The invariants `getFirstExit` must satisfy for every node of a MAT. */
14
+ function checkFirstExits(cpNode: CpNode) {
15
+ for (const cp of getAllOnLoop(cpNode)) {
16
+ const exit = getFirstExit(cp);
17
+
18
+ // the expected result: the first non-terminating node strictly after
19
+ // `cp` going round the circle, if any
20
+ const others = getAllOnCircle(cp, true);
21
+ const expected = others.find(c => !isTerminating(c));
22
+
23
+ expect(exit).toBe(expected);
24
+ // never the node itself
25
+ expect(exit).not.toBe(cp);
26
+ if (exit !== undefined) {
27
+ expect(getAllOnCircle(cp).includes(exit)).toBe(true);
28
+ expect(isTerminating(exit)).toBe(false);
29
+ }
30
+ }
31
+ }
32
+
33
+
34
+ test('getFirstExit', function() {
35
+ const { cpNode } = getMats1()[0];
36
+ const byId = cpNodesByIdOn(cpNode);
37
+
38
+ // 1-prong at the corner [24,8]: from the terminating node 0 the exit is
39
+ // node 1; from node 1 there is no *other* exit.
40
+ expect(getFirstExit(byId(0))).toBe(byId(1));
41
+ expect(getFirstExit(byId(1))).toBeUndefined();
42
+
43
+ // 2-prong {10,11}: each is the exit after the other
44
+ expect(getFirstExit(byId(10))).toBe(byId(11));
45
+ expect(getFirstExit(byId(11))).toBe(byId(10));
46
+
47
+ // 3-prong {144,142,143} (all non-terminating): the next one on the circle
48
+ expect(getFirstExit(byId(144))).toBe(byId(142));
49
+ expect(getFirstExit(byId(142))).toBe(byId(143));
50
+ expect(getFirstExit(byId(143))).toBe(byId(144));
51
+
52
+ checkFirstExits(cpNode);
53
+ });
54
+
55
+
56
+ test('getFirstExit - terminating nodes on 3-prong rings', function() {
57
+ const { cpNode } = getMatsWithHole()[0];
58
+ const all = getAllOnLoop(cpNode);
59
+
60
+ // The test is only meaningful if there are terminating nodes on rings of
61
+ // 3 or more (where `next === prevOnCircle` no longer coincides with
62
+ // `isTerminating`, which is what used to trip this function up).
63
+ const terminatingOnBigRings = all.filter(cp =>
64
+ isTerminating(cp) && getProngCount(cp) >= 3
65
+ );
66
+ expect(terminatingOnBigRings.length).toBeGreaterThan(0);
67
+
68
+ checkFirstExits(cpNode);
69
+ });
70
+
71
+
72
+ test('getFirstExit - MAT consisting of a single point', function() {
73
+ // Two nodes at the same point forming their own circle and loop (a
74
+ // degenerate MAT); neither exits the circle.
75
+ const a = { id: 0 } as unknown as CpNode;
76
+ const b = { id: 1 } as unknown as CpNode;
77
+ Object.assign(a, { next: b, prev: b, nextOnCircle: b, prevOnCircle: b });
78
+ Object.assign(b, { next: a, prev: a, nextOnCircle: a, prevOnCircle: a });
79
+
80
+ expect(getFirstExit(a)).toBeUndefined();
81
+ expect(getFirstExit(b)).toBeUndefined();
7
82
  });
@@ -1,7 +1,36 @@
1
1
  import { expect, test } from '@jest/globals';
2
2
  import { getHoleClosers } from '../../../src/cp-node/fs/get-hole-closers.js';
3
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
4
+ import { getMats1 } from '../../get-mats1.js';
5
+ import { getMatsWithHole } from '../../get-mats-with-hole.js';
3
6
 
4
- test.skip('getHoleClosers [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
7
+
8
+ test('getHoleClosers', function() {
9
+ {
10
+ // a single loop has no holes to close
11
+ const { cpNode } = getMats1()[0];
12
+ expect(getHoleClosers(cpNode)).toEqual([]);
13
+ }
14
+ {
15
+ const { cpNode } = getMatsWithHole()[0];
16
+ const holeClosers = getHoleClosers(cpNode);
17
+
18
+ // the full set of hole closers on the loop, in loop order (compared
19
+ // by identity - `CpNode`s are circular structures)
20
+ const expected = getAllOnLoop(cpNode).filter(cp => cp.isHoleClosing);
21
+ expect(holeClosers.length).toBe(expected.length);
22
+ expect(holeClosers.every((cp, i) => cp === expected[i])).toBe(true);
23
+ expect(holeClosers.length).toBeGreaterThan(0);
24
+
25
+ // hole closers come in twins at the same point - one on each loop
26
+ expect(holeClosers.length % 2).toBe(0);
27
+ for (const cp of holeClosers) {
28
+ const twin = cp.holeCloserTwin!;
29
+ expect(twin).toBeDefined();
30
+ expect(twin.isHoleClosing).toBe(true);
31
+ expect(twin.holeCloserTwin).toBe(cp);
32
+ expect(twin.pointOnShape.p).toEqual(cp.pointOnShape.p);
33
+ expect(holeClosers.includes(twin)).toBe(true);
34
+ }
35
+ }
7
36
  });
@@ -1,7 +1,52 @@
1
1
  import { expect, test } from '@jest/globals';
2
2
  import { getImpliedBoundaryBezierBetween } from '../../../src/cp-node/fs/get-implied-boundary-bezier-between.js';
3
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
4
+ import { getMats1 } from '../../get-mats1.js';
5
+ import { cpNodesByIdOn } from '../../helpers/cp-node-by-id.js';
6
+ import { expectBezierClose, expectBeziersConnected, expectPointClose } from '../../helpers/expect-point-close.js';
3
7
 
4
- test.skip('getImpliedBoundaryBezierBetween [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
8
+
9
+ test('getImpliedBoundaryBezierBetween', function() {
10
+ const { cpNode } = getMats1()[0];
11
+ const byId = cpNodesByIdOn(cpNode);
12
+
13
+ // Both points on the same straight boundary line: a single line.
14
+ {
15
+ const pss = getImpliedBoundaryBezierBetween(byId(147), byId(2));
16
+ expect(pss.length).toBe(1);
17
+ expectBezierClose(pss[0], [[46,18.5],[46,54]]);
18
+ }
19
+ {
20
+ // the tangent at the corner [24,8] is taken from the *outgoing* curve
21
+ const pss = getImpliedBoundaryBezierBetween(byId(1), byId(10));
22
+ expect(pss.length).toBe(1);
23
+ expectBezierClose(pss[0], [[24,8],[46,8]]);
24
+ }
25
+ {
26
+ // ...and at [46,8] (id 10, end of curve 1) likewise, so [46,8] ->
27
+ // [46,18.5] follows curve 2 (vertical)
28
+ const pss = getImpliedBoundaryBezierBetween(byId(10), byId(147));
29
+ expect(pss.length).toBe(1);
30
+ expectBezierClose(pss[0], [[46,8],[46,18.5]]);
31
+ }
32
+ {
33
+ // Tangents (1,0) at [24,8] and (0,1) at [46,18.5] meet at the (cut)
34
+ // corner [46,8]: a single quadratic with that control point.
35
+ const pss = getImpliedBoundaryBezierBetween(byId(1), byId(147));
36
+ expect(pss.length).toBe(1);
37
+ expectBezierClose(pss[0], [[24,8],[46,8],[46,18.5]]);
38
+ }
39
+
40
+ // For every pair of consecutive nodes the implied boundary runs from the
41
+ // one point to the other.
42
+ for (const cp of getAllOnLoop(cpNode)) {
43
+ const pss = getImpliedBoundaryBezierBetween(cp, cp.next);
44
+ expect(pss.length).toBeGreaterThan(0);
45
+ expect(pss.length).toBeLessThanOrEqual(2);
46
+ expectPointClose(pss[0][0], cp.pointOnShape.p);
47
+ const last = pss[pss.length-1];
48
+ expectPointClose(last[last.length-1], cp.next.pointOnShape.p);
49
+ expectBeziersConnected(pss);
50
+ for (const ps of pss) { expect([2,3]).toContain(ps.length); }
51
+ }
7
52
  });
@@ -1,7 +1,81 @@
1
1
  import { expect, test } from '@jest/globals';
2
+ import { cross, fromTo, toUnitVector } from 'flo-vector2d';
2
3
  import { getMatCurveBetween } from '../../../src/cp-node/fs/get-mat-curve-between.js';
4
+ import { getEdgeDirection } from '../../../src/cp-node/fs/get-edge-direction.js';
5
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
6
+ import { isTerminating } from '../../../src/cp-node/fs/is-terminating.js';
7
+ import { getMats1 } from '../../get-mats1.js';
8
+ import { cpNodesByIdOn } from '../../helpers/cp-node-by-id.js';
9
+ import { expectBezierClose, expectBeziersConnected, expectPointClose } from '../../helpers/expect-point-close.js';
3
10
 
4
- test.skip('getMatCurveBetween [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
11
+
12
+ /** unit direction of the given line `[p, q]` */
13
+ const lineDir = (l: number[][]) => toUnitVector(fromTo(l[0], l[1]));
14
+ /** unit tangent at the start / end of a bezier */
15
+ const startTan = (ps: number[][]) => toUnitVector(fromTo(ps[0], ps[1]));
16
+ const endTan = (ps: number[][]) => toUnitVector(fromTo(ps[ps.length-2], ps[ps.length-1]));
17
+
18
+
19
+ test('getMatCurveBetween', function() {
20
+ const { cpNode } = getMats1()[0];
21
+ const byId = cpNodesByIdOn(cpNode);
22
+
23
+ {
24
+ // 3-prong center [81.5,18.5] to the sharp corner [46,54]: the edge
25
+ // direction and the corner bisector are collinear - a straight line
26
+ const pss = getMatCurveBetween(byId(147), byId(2));
27
+ expect(pss.length).toBe(1);
28
+ expectBezierClose(pss[0], [[81.5,18.5],[46,54]]);
29
+ }
30
+ {
31
+ // sharp corner [24,8] to the 2-prong on its bisector - a straight line
32
+ const pss = getMatCurveBetween(byId(1), byId(10));
33
+ expect(pss.length).toBe(1);
34
+ expectBezierClose(pss[0], [[24,8],[46,-5.21425847941623]]);
35
+ }
36
+ {
37
+ // 2-prong {10,11} to the 3-prong {142,143,144}: the medial axis is a
38
+ // parabola here (equidistant from the corner point [46,8] and a line)
39
+ // - a single quadratic whose control point is the intersection of the
40
+ // two edge directions.
41
+ const pss = getMatCurveBetween(byId(10), byId(144));
42
+ expect(pss.length).toBe(1);
43
+ const ps = pss[0];
44
+ expect(ps.length).toBe(3);
45
+ const l0 = getEdgeDirection(byId(10));
46
+ const l1 = getEdgeDirection(byId(143)); // = 144.prevOnCircle
47
+ expect(cross(fromTo(l0[0], ps[1]), lineDir(l0))).toBeCloseTo(0, 9);
48
+ expect(cross(fromTo(l1[0], ps[1]), lineDir(l1))).toBeCloseTo(0, 9);
49
+ }
50
+
51
+ // For every MAT edge: 1 or 2 pieces (lines or quadratics), from the
52
+ // circle center of this node to that of the next, joined smoothly,
53
+ // leaving along the edge direction at the start and arriving along the
54
+ // edge direction at the end.
55
+ for (const cp of getAllOnLoop(cpNode)) {
56
+ const pss = getMatCurveBetween(cp, cp.next);
57
+ expect([1,2]).toContain(pss.length);
58
+ for (const ps of pss) { expect([2,3]).toContain(ps.length); }
59
+
60
+ const c0 = cp.pointOnShape.circle.center;
61
+ const c1 = cp.next.pointOnShape.circle.center;
62
+ expectPointClose(pss[0][0], c0);
63
+ const last = pss[pss.length-1];
64
+ expectPointClose(last[last.length-1], c1);
65
+ expectBeziersConnected(pss);
66
+
67
+ if (isTerminating(cp)) {
68
+ // both nodes share a center: a degenerate curve
69
+ for (const ps of pss) { for (const p of ps) { expectPointClose(p, c0); } }
70
+ continue;
71
+ }
72
+
73
+ const d0 = lineDir(getEdgeDirection(cp));
74
+ const d1 = lineDir(getEdgeDirection(cp.next.prevOnCircle));
75
+ expect(cross(startTan(pss[0]), d0)).toBeCloseTo(0, 4);
76
+ expect(cross(endTan(last), d1)).toBeCloseTo(0, 4);
77
+ if (pss.length === 2) {
78
+ expect(cross(endTan(pss[0]), startTan(pss[1]))).toBeCloseTo(0, 9);
79
+ }
80
+ }
7
81
  });
@@ -1,7 +1,20 @@
1
1
  import { expect, test } from '@jest/globals';
2
- import { getMatCurveToNext } from '../../../src/cp-node/fs/get-mat-curve-to-next.js';
2
+ import { getMatCurveToNext, getMatCurveToNext$ } from '../../../src/cp-node/fs/get-mat-curve-to-next.js';
3
+ import { getMatCurveBetween } from '../../../src/cp-node/fs/get-mat-curve-between.js';
4
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
5
+ import { getMats1 } from '../../get-mats1.js';
3
6
 
4
- test.skip('getMatCurveToNext [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
7
+
8
+ test('getMatCurveToNext', function() {
9
+ const { cpNode } = getMats1()[0];
10
+
11
+ for (const cp of getAllOnLoop(cpNode)) {
12
+ const pss = getMatCurveToNext(cp);
13
+ expect(pss).toEqual(getMatCurveBetween(cp, cp.next));
14
+
15
+ // the memoized version returns the identical result, once computed
16
+ const pss$ = getMatCurveToNext$(cp);
17
+ expect(pss$).toEqual(pss);
18
+ expect(getMatCurveToNext$(cp)).toBe(pss$);
19
+ }
7
20
  });
@@ -1,7 +1,42 @@
1
1
  import { expect, test } from '@jest/globals';
2
2
  import { getMatCurvesBetween } from '../../../src/cp-node/fs/get-mat-curves-between.js';
3
+ import { getMatCurveToNext } from '../../../src/cp-node/fs/get-mat-curve-to-next.js';
4
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
5
+ import { getMats1 } from '../../get-mats1.js';
6
+ import { cpNodesByIdOn } from '../../helpers/cp-node-by-id.js';
7
+ import { expectBeziersConnected, expectPointClose } from '../../helpers/expect-point-close.js';
3
8
 
4
- test.skip('getMatCurvesBetween [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
9
+
10
+ test('getMatCurvesBetween', function() {
11
+ const { cpNode } = getMats1()[0];
12
+ const byId = cpNodesByIdOn(cpNode);
13
+
14
+ // nothing between a node and itself
15
+ expect(getMatCurvesBetween(cpNode, cpNode)).toEqual([]);
16
+
17
+ // the flattened edges 1 -> 10 -> 144 -> 147 (the edge leaving the end
18
+ // node excluded)
19
+ const pss = getMatCurvesBetween(byId(1), byId(147));
20
+ expect(pss).toEqual([
21
+ ...getMatCurveToNext(byId(1)),
22
+ ...getMatCurveToNext(byId(10)),
23
+ ...getMatCurveToNext(byId(144)),
24
+ ]);
25
+ expectBeziersConnected(pss);
26
+ expectPointClose(pss[0][0], [24, 8]);
27
+ const last = pss[pss.length-1];
28
+ expectPointClose(last[last.length-1], [81.5, 18.5]);
29
+
30
+ for (const cp of getAllOnLoop(cpNode)) {
31
+ expect(getMatCurvesBetween(cp, cp.next)).toEqual(getMatCurveToNext(cp));
32
+ expect(getMatCurvesBetween(cp, cp.next.next)).toEqual([
33
+ ...getMatCurveToNext(cp), ...getMatCurveToNext(cp.next)
34
+ ]);
35
+ }
36
+
37
+ // all the way round (to the predecessor) covers every edge but one
38
+ const all = getAllOnLoop(cpNode);
39
+ expect(getMatCurvesBetween(cpNode, cpNode.prev)).toEqual(
40
+ all.slice(0, -1).flatMap(getMatCurveToNext)
41
+ );
7
42
  });