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,72 @@
1
1
  import { expect, test } from '@jest/globals';
2
+ import { distanceBetween } from 'flo-vector2d';
2
3
  import { getSalience } from '../../../src/cp-node/fs/get-salience.js';
4
+ import { getBoundaryBeziersBetween } from '../../../src/cp-node/fs/get-boundary-beziers-between.js';
5
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
6
+ import { getAllOnLoop_ByLoop } from '../../../src/cp-node/fs/get-all-on-loop-by-loop.js';
7
+ import { getRealProngCount } from '../../../src/cp-node/fs/get-real-prong-count.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('getSalience [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
12
+
13
+ test('getSalience', function() {
14
+ const { cpNode } = getMats1()[0];
15
+ const byId = cpNodesByIdOn(cpNode);
16
+
17
+ // Salience = (boundary length from this contact point forward to the next
18
+ // contact point on the circle) / (straight distance between them).
19
+
20
+ {
21
+ // 2-prong {10,11} touching [46,8] and a point on the other edge of the
22
+ // corner [24,8]: from id 11 the boundary runs to the corner and on to
23
+ // [46,8] - two equal tangent lengths of 22.
24
+ const cp = byId(11);
25
+ const d = distanceBetween(cp.pointOnShape.p, cp.nextOnCircle.pointOnShape.p);
26
+ expect(getSalience(cp)).toBeCloseTo(44/d, 9);
27
+ }
28
+ {
29
+ // 3-prong {142,143,144}: from [46,8] (id 144) forward to [117,8]
30
+ // (id 142) the boundary goes round the top of the rectangle:
31
+ // 46 + 71 + 46 = 163, against a chord of 71.
32
+ expect(getSalience(byId(144))).toBeCloseTo(163/71, 9);
33
+ }
34
+
35
+ // the boundary is never shorter than the chord
36
+ for (const cp of getAllOnLoop(cpNode)) {
37
+ if (getRealProngCount(cp) < 2) { continue; } // 1-prongs: see below
38
+ const s = getSalience(cp);
39
+ expect(Number.isFinite(s)).toBe(true);
40
+ expect(s).toBeGreaterThanOrEqual(1);
41
+ }
42
+
43
+ // 1-prongs (two nodes at the same point): the terminating node has no
44
+ // boundary between itself and its twin - salience 0 - while the twin has
45
+ // the entire boundary - salience Infinity. Ids 0 and 1 at the corner
46
+ // [24,8] are such a pair.
47
+ expect(getSalience(byId(0))).toBe(0);
48
+ expect(getSalience(byId(1))).toBe(Infinity);
49
+ for (const cp of getAllOnLoop(cpNode)) {
50
+ if (getRealProngCount(cp) >= 2) { continue; }
51
+ const s = getSalience(cp);
52
+ expect(Number.isNaN(s)).toBe(false);
53
+ expect(s === 0 || s === Infinity).toBe(true);
54
+ }
55
+ });
56
+
57
+
58
+ test('getSalience - hole closers', function() {
59
+ const { cpNode } = getMatsWithHole()[0];
60
+
61
+ // a stretch of boundary passing a hole closer has infinite salience
62
+ const cpNodes = getAllOnLoop_ByLoop(cpNode).flat();
63
+ let infinite = 0;
64
+ for (const cp of cpNodes) {
65
+ const { hasHoleCloser } = getBoundaryBeziersBetween(cp, cp.nextOnCircle);
66
+ if (hasHoleCloser) {
67
+ infinite++;
68
+ expect(getSalience(cp)).toBe(Infinity);
69
+ }
70
+ }
71
+ expect(infinite).toBeGreaterThan(0);
7
72
  });
@@ -1,7 +1,44 @@
1
1
  import { expect, test } from '@jest/globals';
2
2
  import { getVertexForwardChildren } from '../../../src/cp-node/fs/get-vertex-forward-children.js';
3
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
4
+ import { getAllOnCircle } from '../../../src/cp-node/fs/get-all-on-circle.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('getVertexForwardChildren [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
9
+
10
+ test('getVertexForwardChildren', function() {
11
+ const { cpNode } = getMats1()[0];
12
+ const byId = cpNodesByIdOn(cpNode);
13
+ const childIds = (id: number) => getVertexForwardChildren(byId(id)).map(cp => cp.id);
14
+
15
+ // Seen as a vertex entered via the given node (its `prevOnCircle` being
16
+ // the edge back to the parent), the forward children are the far ends of
17
+ // the other edges, in on-circle order.
18
+ expect(childIds(144)).toEqual([147, 148]); // 3-prong: two children
19
+ expect(childIds(142)).toEqual([148, 11]); // same 3-prong, entered differently
20
+ expect(childIds(147)).toEqual([2, 4]); // 3-prong towards two leaves
21
+ expect(childIds(10)).toEqual([144]); // 2-prong: one child
22
+ expect(childIds(1)).toEqual([10]); // leaf, leaving
23
+ expect(childIds(0)).toEqual([]); // leaf, entered - no children
24
+
25
+ for (const cp of getAllOnLoop(cpNode)) {
26
+ const children = getVertexForwardChildren(cp);
27
+
28
+ // one child per non-terminating node on the circle other than the one
29
+ // leading back
30
+ const ring = getAllOnCircle(cp);
31
+ const expected = ring
32
+ .filter(c => c !== cp.prevOnCircle && !isTerminating(c))
33
+ .map(c => c.next);
34
+ expect(children.length).toBe(expected.length);
35
+ expect(children.every((c, i) => c === expected[i])).toBe(true);
36
+
37
+ for (const child of children) {
38
+ // a child is reached by an edge from this vertex...
39
+ expect(child.prev.pointOnShape.circle).toBe(cp.pointOnShape.circle);
40
+ // ...to a different vertex
41
+ expect(child.pointOnShape.circle).not.toBe(cp.pointOnShape.circle);
42
+ }
43
+ }
7
44
  });
@@ -1,7 +1,91 @@
1
1
  import { expect, test } from '@jest/globals';
2
+ import { RbTree } from 'flo-ll-rb-tree';
3
+ import type { CpNode } from '../../../src/cp-node/cp-node.js';
4
+ import type { PointOnShape } from '../../../src/point-on-shape/point-on-shape.js';
2
5
  import { insertCpNode } from '../../../src/cp-node/fs/insert-cp-node.js';
6
+ import { cpNodeComparator } from '../../../src/cp-node/fs/cp-node-comparator.js';
7
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
8
+ import { getMats1 } from '../../get-mats1.js';
9
+ import { cpNodesByIdOn } from '../../helpers/cp-node-by-id.js';
3
10
 
4
- test.skip('insertCpNode [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
11
+
12
+ /** A point on the shape (on the curve of the given node at `t`) with a zero disk. */
13
+ function posOnCurveOf(cpNode: CpNode, t: number, p: number[]): PointOnShape {
14
+ return {
15
+ curve: cpNode.pointOnShape.curve, t, p,
16
+ pDd: [[0,p[0]],[0,p[1]]], isSource: true,
17
+ circle: { center: p, radius: 0 }, order: 0, order2: 0
18
+ };
19
+ }
20
+
21
+
22
+ test('insertCpNode', function() {
23
+ const { cpNode } = getMats1()[0];
24
+ const byId = cpNodesByIdOn(cpNode);
25
+ const all = getAllOnLoop(cpNode);
26
+ const cpTree = new RbTree<CpNode>(cpNodeComparator);
27
+ for (const cp of all) { cpTree.insert(cp); }
28
+ const lastInsertId = { id: 1000 };
29
+
30
+ // insert between [24,8] (id 1, end of curve 0) and [46,8] (id 10, end of
31
+ // curve 1) a point halfway along curve 1
32
+ const pred = byId(1);
33
+ const succ = byId(10);
34
+ const pos = posOnCurveOf(succ, 0.5, [35, 8]);
35
+ const inserted = insertCpNode(false, false, false, cpTree, pos, pred, succ, lastInsertId)!;
36
+
37
+ expect(inserted).toBeDefined();
38
+ expect(inserted.id).toBe(1000);
39
+ expect(lastInsertId.id).toBe(1001);
40
+ expect(inserted.pointOnShape).toBe(pos);
41
+ expect(inserted.isHoleClosing).toBe(false);
42
+ expect(inserted.isIntersection).toBe(false);
43
+ // linked into the loop...
44
+ expect(inserted.prev).toBe(pred);
45
+ expect(inserted.next).toBe(succ);
46
+ expect(pred.next).toBe(inserted);
47
+ expect(succ.prev).toBe(inserted);
48
+ expect(getAllOnLoop(cpNode).length).toBe(all.length + 1);
49
+ // ...and into the tree, in order
50
+ expect(cpTree.find(inserted)?.datum).toBe(inserted);
51
+ expect(cpTree.size).toBe(all.length + 1);
52
+ const inOrder = cpTree.toArr();
53
+ expect(inOrder.length).toBe(all.length + 1);
54
+ expect(inOrder[inOrder.indexOf(pred) + 1]).toBe(inserted);
55
+ expect(inOrder[inOrder.indexOf(inserted) + 1]).toBe(succ);
56
+ // the on-circle edges are left for the caller to set
57
+ expect(inserted.nextOnCircle).toBeUndefined();
58
+ expect(inserted.prevOnCircle).toBeUndefined();
59
+
60
+ {
61
+ // A point on curve 3 does not belong between ids 1 and 10 - refused
62
+ // unless told to insert regardless. (At t=0.25 - not t=0.5 where id
63
+ // 145 already sits, which the tree would treat as a duplicate.)
64
+ const wrongPos = posOnCurveOf(byId(145), 0.25, [63.75, 54]);
65
+ const r = insertCpNode(false, false, false, cpTree, wrongPos, pred, inserted, lastInsertId);
66
+ expect(r).toBeUndefined();
67
+ expect(pred.next).toBe(inserted);
68
+ expect(cpTree.size).toBe(all.length + 1);
69
+ expect(lastInsertId.id).toBe(1001);
70
+
71
+ const forced = insertCpNode(true, true, true, cpTree, wrongPos, pred, inserted, lastInsertId)!;
72
+ expect(forced).toBeDefined();
73
+ expect(forced.id).toBe(1001);
74
+ expect(forced.isHoleClosing).toBe(true);
75
+ expect(forced.isIntersection).toBe(true);
76
+ expect(pred.next).toBe(forced);
77
+ expect(forced.next).toBe(inserted);
78
+ expect(inserted.prev).toBe(forced);
79
+ expect(cpTree.size).toBe(all.length + 2);
80
+ }
81
+
82
+ {
83
+ // without a predecessor the node starts a loop of its own
84
+ const emptyTree = new RbTree<CpNode>(cpNodeComparator);
85
+ const lone = insertCpNode(false, false, false, emptyTree, pos, undefined, undefined, lastInsertId)!;
86
+ expect(lone.next).toBe(lone);
87
+ expect(lone.prev).toBe(lone);
88
+ expect(emptyTree.size).toBe(1);
89
+ expect(emptyTree.toArr()[0]).toBe(lone);
90
+ }
7
91
  });
@@ -10,7 +10,7 @@ test('isSpecial', function() {
10
10
  const allOnLoop = getAllOnLoop(cpNode);
11
11
 
12
12
  for (const n of allOnLoop) {
13
- const expected = getRealProngCount(n) !== 2 && !n.isHoleClosing;
13
+ const expected = getRealProngCount(n) !== 2 || n.isHoleClosing;
14
14
  expect(isSpecial(n)).toBe(expected);
15
15
  }
16
16
  });
@@ -1,7 +1,46 @@
1
1
  import { expect, test } from '@jest/globals';
2
+ import { RbTree } from 'flo-ll-rb-tree';
3
+ import type { CpNode } from '../../../src/cp-node/cp-node.js';
4
+ import type { MatMeta } from '../../../src/mat/mat-meta.js';
2
5
  import { removeCpNode } from '../../../src/cp-node/fs/remove-cp-node.js';
6
+ import { cpNodeComparator } from '../../../src/cp-node/fs/cp-node-comparator.js';
7
+ import { getAllOnLoop } from '../../../src/cp-node/fs/get-all-on-loop.js';
8
+ import { getMats1 } from '../../get-mats1.js';
9
+ import { cpNodesByIdOn } from '../../helpers/cp-node-by-id.js';
3
10
 
4
- test.skip('removeCpNode [STUB]', () => {
5
- // TODO: implement test
6
- expect(true).toBe(true);
11
+
12
+ test('removeCpNode', function() {
13
+ const { cpNode } = getMats1()[0];
14
+ const byId = cpNodesByIdOn(cpNode);
15
+ const all = getAllOnLoop(cpNode);
16
+
17
+ const loop = cpNode.pointOnShape.curve.loop;
18
+ const cpTree = new RbTree<CpNode>(cpNodeComparator);
19
+ for (const cp of all) { cpTree.insert(cp); }
20
+ const meta = { cpTrees: new Map([[loop, cpTree]]) } as unknown as MatMeta;
21
+
22
+ const cp = byId(10);
23
+ const prev = cp.prev; // id 1
24
+ const next = cp.next; // id 144
25
+ expect(cpTree.find(cp)?.datum).toBe(cp);
26
+
27
+ removeCpNode(cp, meta);
28
+
29
+ // (compare `CpNode`s by identity / id only - they are circular structures
30
+ // that must not be deep-compared)
31
+
32
+ // unlinked from the loop...
33
+ expect(prev.next).toBe(next);
34
+ expect(next.prev).toBe(prev);
35
+ const remaining = getAllOnLoop(cpNode);
36
+ expect(remaining.length).toBe(all.length - 1);
37
+ expect(remaining.includes(cp)).toBe(false);
38
+ // ...and from the tree, with everything else still there
39
+ expect(cpTree.find(cp)).toBeUndefined();
40
+ expect(cpTree.size).toBe(all.length - 1);
41
+ const expectedIds = all.filter(c => c !== cp).sort(cpNodeComparator).map(c => c.id);
42
+ expect(cpTree.toArr().map(c => c.id)).toEqual(expectedIds);
43
+ // the removed node itself is left untouched
44
+ expect(cp.prev).toBe(prev);
45
+ expect(cp.next).toBe(next);
7
46
  });
@@ -0,0 +1,46 @@
1
+ import { expect, test } from '@jest/globals';
2
+ import { calcSquaredSeperationTolerance } from '../../src/find-2-prong/calc-seperation-tolerance.js';
3
+
4
+
5
+ /**
6
+ * Reference: the squared chord `(2r·sin(θ/2))²` with `θ` from the law of
7
+ * cosines formulation, computed via `1 - cos θ` to avoid cancellation.
8
+ */
9
+ function refSquaredChord(R: number, r: number, δ: number) {
10
+ const oneMinusCos = (R*δ + δ*δ/2) / ((R - r)*(r + δ));
11
+ const θ = 2*Math.asin(Math.sqrt(oneMinusCos/2));
12
+ return (2*r*Math.sin(θ/2))**2;
13
+ }
14
+
15
+
16
+ test('calcSquaredSeperationTolerance', function() {
17
+ // a spread of osculating radii, MAT radii and (tiny) distance tolerances
18
+ const cases: [number, number, number][] = [
19
+ [100, 50, 2**-34], [100, 99, 2**-34], [1000, 0.5, 2**-30],
20
+ [100, 99.999, 2**-34], [37.3, 12.1, 2**-40], [2**20, 1000, 2**-24],
21
+ ];
22
+ for (const [R, r, δ] of cases) {
23
+ const d2 = calcSquaredSeperationTolerance(R, r, δ);
24
+ expect(Number.isFinite(d2)).toBe(true);
25
+ expect(d2).toBeGreaterThan(0);
26
+ // agrees with the trigonometric reference
27
+ expect(d2 / refSquaredChord(R, r, δ)).toBeCloseTo(1, 12);
28
+ }
29
+
30
+ // the closer the MAT radius gets to the osculating radius (a near
31
+ // 1-prong) the larger the separation required
32
+ const grow = [10, 50, 90, 99, 99.9].map(r => calcSquaredSeperationTolerance(100, r, 2**-34));
33
+ for (let i=1; i<grow.length; i++) { expect(grow[i]).toBeGreaterThan(grow[i-1]); }
34
+
35
+ // ...and a larger error tolerance requires more separation too
36
+ expect(calcSquaredSeperationTolerance(100, 50, 2**-30))
37
+ .toBeGreaterThan(calcSquaredSeperationTolerance(100, 50, 2**-34));
38
+
39
+ // the enlarged MAT circle reaching the osculating circle: reject everything
40
+ expect(calcSquaredSeperationTolerance(100, 100, 2**-34)).toBe(Infinity);
41
+ expect(calcSquaredSeperationTolerance(100, 100 - 2**-40, 2**-34)).toBe(Infinity);
42
+ expect(calcSquaredSeperationTolerance(100, 120, 2**-34)).toBe(Infinity);
43
+
44
+ // sanity: a concrete value (R=100, r=50, δ=2**-34), chord ~ 1.079e-4
45
+ expect(Math.sqrt(calcSquaredSeperationTolerance(100, 50, 2**-34))).toBeCloseTo(1.078959e-4, 9);
46
+ });
@@ -5,6 +5,8 @@ import { ddGetMedialPointCoeffsBez2_SameCurve } from '../../../src/get-medial-po
5
5
  import { ddGetMedialPointCoeffsBez3_SameCurve } from '../../../src/get-medial-points/double-double/dd-get-medial-point-coeffs-bez3-same-curve.js';
6
6
  import { ddNormal } from 'flo-bezier3';
7
7
 
8
+ const { abs, floor } = Math;
9
+
8
10
 
9
11
  function toDdVec(v: number[]): number[][] {
10
12
  return v.map(x => [0, x]);
@@ -17,7 +19,7 @@ function ddPolyToDouble(poly: number[][]): number[] {
17
19
  function normalizedPoly(poly: number[]): number[] {
18
20
  let m = 0;
19
21
  for (const x of poly) {
20
- const ax = Math.abs(x);
22
+ const ax = abs(x);
21
23
  if (ax > m) { m = ax; }
22
24
  }
23
25
 
@@ -34,8 +36,8 @@ function randomNumberIn01(seed: number): number {
34
36
 
35
37
  function randomSignedValue(seed: number): number {
36
38
  const sign = randomNumberIn01(seed) < 0.5 ? -1 : 1;
37
- const exponent = Math.floor(randomNumberIn01(seed + 1) * 61) - 30;
38
- const mantissa = 1 + Math.floor(randomNumberIn01(seed + 2) * 2**20);
39
+ const exponent = floor(randomNumberIn01(seed + 1) * 61) - 30;
40
+ const mantissa = 1 + floor(randomNumberIn01(seed + 2) * 2**20);
39
41
 
40
42
  return sign * mantissa * 2**exponent;
41
43
  }
@@ -67,7 +69,7 @@ test.skip('dd same-curve coefficients match flo-bezier3 double coefficients', fu
67
69
 
68
70
  const n2Dd = ddNormal(ps2, t);
69
71
  let v2 = [n2Dd[0][1], n2Dd[1][1]];
70
- if (Math.abs(v2[0]) + Math.abs(v2[1]) < 2**-20) {
72
+ if (abs(v2[0]) + abs(v2[1]) < 2**-20) {
71
73
  v2 = [v2[0] + 1, v2[1] - 1];
72
74
  }
73
75
  const v2Dd = toDdVec(v2);
@@ -89,7 +91,7 @@ test.skip('dd same-curve coefficients match flo-bezier3 double coefficients', fu
89
91
 
90
92
  const n3Dd = ddNormal(ps3, t);
91
93
  let v3 = [n3Dd[0][1], n3Dd[1][1]];
92
- if (Math.abs(v3[0]) + Math.abs(v3[1]) < 2**-20) {
94
+ if (abs(v3[0]) + abs(v3[1]) < 2**-20) {
93
95
  v3 = [v3[0] + 1, v3[1] - 1];
94
96
  }
95
97
  const v3Dd = toDdVec(v3);
@@ -3,6 +3,8 @@ import { getMedialPointCoeffs } from 'flo-bezier3';
3
3
  import { ddGetMedialPointCoeffs } from '../../../src/get-medial-points/double-double/dd-get-medial-point-coeffs.js';
4
4
  import { squares } from 'squares-rng';
5
5
 
6
+ const { floor, abs } = Math;
7
+
6
8
 
7
9
  function toDdVec(v: number[]): number[][] {
8
10
  return v.map(x => [0, x]);
@@ -19,7 +21,7 @@ function randomNumberIn01(seed: number): number {
19
21
 
20
22
  function randomSignedValue(seed: number): number {
21
23
  const sign = randomNumberIn01(seed) < 0.5 ? -1 : 1;
22
- const exponent = Math.floor(randomNumberIn01(seed + 1) * 81) - 40;
24
+ const exponent = floor(randomNumberIn01(seed + 1) * 81) - 40;
23
25
  const mantissa = 1 + randomNumberIn01(seed + 2);
24
26
 
25
27
  return sign * mantissa * 2**exponent;
@@ -46,7 +48,7 @@ test('ddGetMedialPointCoeffs matches flo-bezier3 double coefficients', function(
46
48
  let v = randomPoint(seed); seed += 11;
47
49
 
48
50
  // Keep the direction away from near-zero vectors.
49
- if (Math.abs(v[0]) + Math.abs(v[1]) < 2**-20) {
51
+ if (abs(v[0]) + abs(v[1]) < 2**-20) {
50
52
  v = [v[0] + 1, v[1] - 1];
51
53
  }
52
54
 
@@ -44,7 +44,7 @@ test('getClosestPoints', function() {
44
44
  const def = deflate(H,t);//?
45
45
  // def = [47279295, -157379071.5, 100304568, -23598664.03125, 1904993.75390625]
46
46
 
47
- const r = getClosestPoint(maxPowerOf2, nnorm, nnormDd, yPos, for1Prong, angle, curvePiece);
47
+ const r = getClosestPoint(maxPowerOf2, nnormDd, yPos, for1Prong, angle, curvePiece);
48
48
 
49
49
  r;//?
50
50
 
@@ -0,0 +1,100 @@
1
+ import { expect, test } from '@jest/globals';
2
+ import type { CpNode } from '../../src/cp-node/cp-node.js';
3
+ import { getGraph } from '../../src/floyd-warshall/get-graph.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 { isVertexSpecial } from '../../src/cp-node/fs/is-vertex-special.js';
7
+ import { getPathsFromStr } from 'flo-boolean';
8
+ import { findMats } from '../../src/find-mat/find-mats.js';
9
+ import { getMats1 } from '../get-mats1.js';
10
+
11
+
12
+ /** One `CpNode` per special vertex (leaves, 3-prongs, hole closers). */
13
+ function getSpecialVertices(cpNode: CpNode): CpNode[] {
14
+ const seen = new Set<CpNode>();
15
+ const vertices: CpNode[] = [];
16
+ for (const cp of getAllOnLoop(cpNode)) {
17
+ if (seen.has(cp)) { continue; }
18
+ for (const c of getAllOnCircle(cp)) { seen.add(c); }
19
+ if (isVertexSpecial(cp)) { vertices.push(cp); }
20
+ }
21
+ return vertices;
22
+ }
23
+
24
+
25
+ function checkGraph(graph: number[][], V: number, symmetricLengths: boolean) {
26
+ expect(graph.length).toBe(V);
27
+ for (let i=0; i<V; i++) {
28
+ expect(graph[i].length).toBe(V);
29
+ expect(graph[i][i]).toBe(0);
30
+ let adjacent = 0;
31
+ for (let j=0; j<V; j++) {
32
+ const d = graph[i][j];
33
+ // symmetric adjacency...
34
+ expect(Number.isFinite(graph[j][i])).toBe(Number.isFinite(d));
35
+ // ...and symmetric lengths (up to the summation order of the edge
36
+ // lengths) - except across hole closers, where the two directions
37
+ // currently accumulate slightly different lengths (pre-existing)
38
+ if (symmetricLengths && Number.isFinite(d)) {
39
+ expect(d).toBeCloseTo(graph[j][i], 9);
40
+ }
41
+ expect(d >= 0).toBe(true);
42
+ if (i !== j && Number.isFinite(d)) { adjacent++; }
43
+ }
44
+ // the MAT is connected: every special vertex has a neighbour
45
+ expect(adjacent).toBeGreaterThan(0);
46
+ }
47
+ }
48
+
49
+
50
+ test('getGraph - single loop fixture', function() {
51
+ const { cpNode } = getMats1()[0];
52
+ const vertices = getSpecialVertices(cpNode);
53
+ // 5 leaves + 3 3-prongs
54
+ expect(vertices.length).toBe(8);
55
+
56
+ const graph = getGraph(vertices);
57
+ checkGraph(graph, vertices.length, true);
58
+
59
+ // the 3-prong {142,143,144} (center [81.5,-12.64]) is adjacent to the
60
+ // 3-prong at [81.5,18.5] by a straight vertical edge of length 31.14...
61
+ const i = vertices.findIndex(v => v.pointOnShape.circle.center[1] === 18.5);
62
+ const j = vertices.findIndex(v => v.pointOnShape.circle.center[1] === -12.641417453935567);
63
+ expect(i).toBeGreaterThanOrEqual(0);
64
+ expect(j).toBeGreaterThanOrEqual(0);
65
+ expect(graph[i][j]).toBeCloseTo(18.5 + 12.641417453935567, 9);
66
+ });
67
+
68
+
69
+ test('getGraph - shape with a hole (hole closers are skipped via their twins)', function() {
70
+ // (not the SAT-processed fixture: the SAT culls all the spurs of a square
71
+ // frame, leaving only a cycle of 2-prongs and thus no special vertices)
72
+ const loops = getPathsFromStr(
73
+ 'M0 0 L100 0 L100 100 L0 100 Z ' +
74
+ 'M30 30 L30 70 L70 70 L70 30 Z'
75
+ );
76
+ const { cpNode } = findMats(loops, { applySat: false, simplify: false })[0];
77
+ const vertices = getSpecialVertices(cpNode).filter(v => !v.isHoleClosing);
78
+ // 4 outer-corner leaves + 4 3-prongs where the spurs meet the ring
79
+ expect(vertices.length).toBe(8);
80
+
81
+ // must terminate (the walk over a hole closer continues on the other loop)
82
+ const graph = getGraph(vertices);
83
+ checkGraph(graph, vertices.length, false);
84
+
85
+ // the 4 leaves are each adjacent to exactly one 3-prong (their spur)...
86
+ const leaves = vertices.filter(v => v.pointOnShape.circle.radius === 0);
87
+ expect(leaves.length).toBe(4);
88
+ for (const leaf of leaves) {
89
+ const i = vertices.indexOf(leaf);
90
+ const finite = graph[i].filter((d, j) => j !== i && Number.isFinite(d));
91
+ expect(finite.length).toBe(1);
92
+ }
93
+ // ...and each 3-prong to its spur leaf and its two ring neighbours
94
+ for (const v of vertices) {
95
+ if (v.pointOnShape.circle.radius === 0) { continue; }
96
+ const i = vertices.indexOf(v);
97
+ const finite = graph[i].filter((d, j) => j !== i && Number.isFinite(d));
98
+ expect(finite.length).toBe(3);
99
+ }
100
+ });
@@ -0,0 +1,28 @@
1
+ import type { Mat } from "../src/mat/mat.js";
2
+ import { getPathsFromStr } from 'flo-boolean';
3
+ import { findMats } from "../src/find-mat/find-mats.js";
4
+
5
+
6
+ // Cache - the tests using this fixture must not mutate it.
7
+ let mats: Mat[] | undefined = undefined;
8
+
9
+ /**
10
+ * A square with a square hole - forces hole-closing 2-prongs (which `getMats1`
11
+ * does not have). Unlike `getMats1` this is calculated and thus susceptible to
12
+ * small perturbations in the algorithm, so only test *structural* properties
13
+ * against it.
14
+ */
15
+ function getMatsWithHole(): Mat[] {
16
+ if (mats) { return mats; }
17
+
18
+ const loops = getPathsFromStr(
19
+ 'M0 0 L100 0 L100 100 L0 100 Z ' +
20
+ 'M30 30 L30 70 L70 70 L70 30 Z'
21
+ );
22
+ mats = findMats(loops, {});
23
+
24
+ return mats;
25
+ }
26
+
27
+
28
+ export { getMatsWithHole }
@@ -0,0 +1,61 @@
1
+ import { expect, test } from '@jest/globals';
2
+ import type { Circle } from '../src/geometry/circle.js';
3
+ import { getMats1, cpNodeStringifyable1 } from './get-mats1.js';
4
+ import { getAllOnCircle } from '../src/cp-node/fs/get-all-on-circle.js';
5
+ import { getAllOnLoop_ByLoop } from '../src/cp-node/fs/get-all-on-loop-by-loop.js';
6
+
7
+
8
+ test('`getMats1` fixture: one `Circle` object per on-circle ring', function() {
9
+ {
10
+ // The stringifyable literal itself (before `fromStringifyable`
11
+ // normalizes anything) must already share one `Circle` object per
12
+ // on-circle ring and no `Circle` object across rings.
13
+ const { cpNodes } = cpNodeStringifyable1;
14
+ const byId = new Map(cpNodes.map(cp => [cp.id, cp]));
15
+
16
+ const seen = new Set<number>();
17
+ const ringOfCircle = new Map<Circle, number[]>();
18
+ for (const cpNode of cpNodes) {
19
+ if (seen.has(cpNode.id)) { continue; }
20
+
21
+ // walk the ring via the `nextOnCircle` ids
22
+ const ring: number[] = [];
23
+ let cp = cpNode;
24
+ do {
25
+ ring.push(cp.id);
26
+ seen.add(cp.id);
27
+ cp = byId.get(cp.nextOnCircle as number)!;
28
+ } while (cp !== cpNode);
29
+
30
+ const { circle } = cpNode.pointOnShape;
31
+ for (const id of ring) {
32
+ expect(byId.get(id)!.pointOnShape.circle).toBe(circle);
33
+ }
34
+ expect(ringOfCircle.has(circle)).toBe(false);
35
+ ringOfCircle.set(circle, ring);
36
+ }
37
+
38
+ expect(ringOfCircle.size).toBe(10);
39
+ }
40
+ {
41
+ // ...and so does the `CpNode` graph built from it
42
+ const { cpNode } = getMats1()[0];
43
+ const cpNodes = getAllOnLoop_ByLoop(cpNode).flat();
44
+
45
+ const seen = new Set<typeof cpNode>();
46
+ const circles = new Set<Circle>();
47
+ for (const cp of cpNodes) {
48
+ if (seen.has(cp)) { continue; }
49
+
50
+ const { circle } = cp.pointOnShape;
51
+ for (const cp_ of getAllOnCircle(cp)) {
52
+ seen.add(cp_);
53
+ expect(cp_.pointOnShape.circle).toBe(circle);
54
+ }
55
+ expect(circles.has(circle)).toBe(false);
56
+ circles.add(circle);
57
+ }
58
+
59
+ expect(circles.size).toBe(10);
60
+ }
61
+ });