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
@@ -0,0 +1,92 @@
1
+ import { fromTo, dot, cross, lineLineIntersection } from 'flo-vector2d';
2
+ const { abs, sqrt } = Math;
3
+ /**
4
+ * The maximum sine of the angle between the chord and either tangent for
5
+ * which a 'twisted' (inflected) curve is approximated by a straight line -
6
+ * the line then deviates from the true curve by only about a millionth of the
7
+ * chord length.
8
+ *
9
+ * * a relative (angle) tolerance and thus independent of the scale of the
10
+ * shape and of the magnitude of the given tangents
11
+ *
12
+ * @internal
13
+ */
14
+ const SIN_TOLERANCE_USE_LINE = 2 ** -16;
15
+ /**
16
+ * Returns an array of 1 or 2 bezier curves (each a line or a quadratic) that
17
+ * together form a smooth curve from the start point of `l0` to the start point
18
+ * of `l1` such that the curve is tangent to `l0` at its start and tangent to
19
+ * `l1` at its end.
20
+ *
21
+ * * `l0` and `l1` are given as `[p, p + dir]` where `p` is the curve endpoint
22
+ * and `dir` is the tangent direction (of any magnitude; its orientation does
23
+ * not matter)
24
+ * * if the tangent lines are not 'twisted' (they meet strictly between the
25
+ * endpoints along the chord) a single quadratic is returned
26
+ * * if the tangent lines are 'twisted' (the curve must have an inflection)
27
+ * 2 quadratics joined smoothly at the inflection are returned as an
28
+ * approximation - or, if both tangents deviate from the chord by less than
29
+ * `SIN_TOLERANCE_USE_LINE`, a single line
30
+ * * if the endpoints coincide (or a tangent is degenerate) a (degenerate) line
31
+ * is returned
32
+ *
33
+ * FUTURE: a strongly twisted MAT edge indicates that an additional 2-prong
34
+ * should be added so that the edge is approximated by two less twisted ones.
35
+ *
36
+ * @param l0 the tangent line at the start of the curve
37
+ * @param l1 the tangent line at the end of the curve
38
+ *
39
+ * @internal
40
+ */
41
+ function getBeziersFromTangents(l0, l1) {
42
+ const p0 = l0[0];
43
+ const p1 = l1[0];
44
+ const c = fromTo(p0, p1);
45
+ if (c[0] === 0 && c[1] === 0) {
46
+ return [[p0, p1]]; // coinciding endpoints - a degenerate line
47
+ }
48
+ const w0 = fromTo(l0[0], l0[1]);
49
+ const w1 = fromTo(l1[0], l1[1]);
50
+ const lc = sqrt(dot(c, c));
51
+ const lw0 = sqrt(dot(w0, w0));
52
+ const lw1 = sqrt(dot(w1, w1));
53
+ if (lw0 === 0 || lw1 === 0) {
54
+ return [[p0, p1]]; // a degenerate tangent - fall back to a line
55
+ }
56
+ const mid = lineLineIntersection(l0, l1);
57
+ let twisted;
58
+ if (!mid) {
59
+ twisted = true;
60
+ }
61
+ else {
62
+ const a = fromTo(p0, mid);
63
+ const b = fromTo(p1, mid);
64
+ // (`<= 0` / `>= 0`: an intersection *at* an endpoint, which happens for
65
+ // collinear tangents, would give a degenerate quadratic with a zero
66
+ // end tangent - treat it as twisted so a line results instead)
67
+ twisted = dot(a, c) <= 0 || dot(b, c) >= 0;
68
+ }
69
+ if (!twisted) {
70
+ return [[p0, mid, p1]];
71
+ }
72
+ // the sines of the angles between the chord and the tangents
73
+ const sin0 = abs(cross(c, w0)) / (lc * lw0);
74
+ const sin1 = abs(cross(c, w1)) / (lc * lw1);
75
+ if (sin0 <= SIN_TOLERANCE_USE_LINE && sin1 <= SIN_TOLERANCE_USE_LINE) {
76
+ return [[p0, p1]]; // within tolerance - a straight line
77
+ }
78
+ // Approximate with 2 quadratic beziers joined smoothly at the inflection.
79
+ // The control points lie on the tangent lines a quarter of the chord
80
+ // length away from the endpoints, on the side facing the other endpoint
81
+ // (for small angles this is the midpoint degree reduction of the cubic
82
+ // having control points at 1/3 and 2/3 along the chord; unlike placing
83
+ // them at 1/4 and 3/4 *along* the chord it stays bounded for any angle).
84
+ const q0 = (dot(w0, c) < 0 ? -1 : 1) * lc / (4 * lw0);
85
+ const q1 = (dot(w1, c) < 0 ? 1 : -1) * lc / (4 * lw1);
86
+ const c0 = [p0[0] + q0 * w0[0], p0[1] + q0 * w0[1]];
87
+ const c1 = [p1[0] + q1 * w1[0], p1[1] + q1 * w1[1]];
88
+ const j = [(c0[0] + c1[0]) / 2, (c0[1] + c1[1]) / 2];
89
+ return [[p0, c0, j], [j, c1, p1]];
90
+ }
91
+ export { getBeziersFromTangents };
92
+ //# sourceMappingURL=get-beziers-from-tangents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-beziers-from-tangents.js","sourceRoot":"","sources":["../../src/utils/get-beziers-from-tangents.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,12 @@
1
1
  import type { CpNode } from "../cp-node/cp-node.js";
2
2
  import type { MatMeta } from '../mat/mat-meta.js';
3
3
  /**
4
- * Removes a cpNode from the MAT.
4
+ * Removes the (2-prong) vertex represented by the given `CpNode` from the MAT,
5
+ * i.e. both `CpNode`s on its circle are unlinked from the boundary and removed
6
+ * from `meta.cpTrees`.
5
7
  *
6
- * @param cpTree The tree graph holding the `CpNodes` of the MAT.
7
- * @param cpNode The `CpNode` to remove.
8
+ * @param cpNode a `CpNode` on the vertex to remove
9
+ * @param meta the `MatMeta` whose `cpTrees` hold the `CpNode`s of the MAT
8
10
  */
9
11
  declare function removeVertex(cpNode: CpNode, meta: MatMeta): void;
10
12
  export { removeVertex };
@@ -2,10 +2,12 @@ import { getAllOnCircle } from '../cp-node/fs/get-all-on-circle.js';
2
2
  import { getProngCount } from '../cp-node/fs/get-prong-count.js';
3
3
  import { removeCpNode } from '../cp-node/fs/remove-cp-node.js';
4
4
  /**
5
- * Removes a cpNode from the MAT.
5
+ * Removes the (2-prong) vertex represented by the given `CpNode` from the MAT,
6
+ * i.e. both `CpNode`s on its circle are unlinked from the boundary and removed
7
+ * from `meta.cpTrees`.
6
8
  *
7
- * @param cpTree The tree graph holding the `CpNodes` of the MAT.
8
- * @param cpNode The `CpNode` to remove.
9
+ * @param cpNode a `CpNode` on the vertex to remove
10
+ * @param meta the `MatMeta` whose `cpTrees` hold the `CpNode`s of the MAT
9
11
  */
10
12
  function removeVertex(cpNode, meta) {
11
13
  const prongCount = getProngCount(cpNode);
@@ -1 +1 @@
1
- {"version":3,"file":"remove-vertex.js","sourceRoot":"","sources":["../../src/vertex/remove-vertex.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAG/D;;;;;GAKG;AACH,SAAS,YAAY,CACb,MAAc,EACd,IAAa;IAEjB,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,+BAA+B,UAAU,QAAQ,CAAC,CAAA;IACtE,CAAC;IAED,cAAc,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;AACzE,CAAC;AAGD,OAAO,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"remove-vertex.js","sourceRoot":"","sources":["../../src/vertex/remove-vertex.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAG/D;;;;;;;GAOG;AACH,SAAS,YAAY,CACb,MAAc,EACd,IAAa;IAEjB,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,+BAA+B,UAAU,QAAQ,CAAC,CAAA;IACtE,CAAC;IAED,cAAc,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;AACzE,CAAC;AAGD,OAAO,EAAE,YAAY,EAAE,CAAA"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "flo-mat",
3
3
  "sideEffects": false,
4
4
  "description": "Medial / Scale Axis Transform (MAT/SAT) Library.",
5
- "version": "4.1.4",
5
+ "version": "5.0.1",
6
6
  "author": {
7
7
  "name": "Floris Steenkamp"
8
8
  },
@@ -32,32 +32,32 @@
32
32
  ],
33
33
  "dependencies": {
34
34
  "big-float-ts": "^4.0.4",
35
- "double-double": "^3.0.4",
36
- "flo-bezier3": "^8.1.1",
37
- "flo-boolean": "^6.0.2",
35
+ "double-double": "^4.0.0",
36
+ "flo-bezier3": "^9.0.0",
37
+ "flo-boolean": "^6.0.3",
38
38
  "flo-gauss-quadrature": "^2.1.4",
39
39
  "flo-ll-rb-tree": "^1.0.4",
40
40
  "flo-memoize": "^2.0.1",
41
- "flo-poly": "^8.1.4",
41
+ "flo-poly": "^9.0.0",
42
42
  "flo-vector2d": "^4.0.6"
43
43
  },
44
44
  "devDependencies": {
45
- "squares-rng": "^2.0.4",
46
- "@jest/globals": "^30.5.1",
47
- "@swc/core": "^1.16.2",
45
+ "@jest/globals": "^30.5.2",
46
+ "@swc/core": "^1.16.18",
48
47
  "@swc/jest": "^0.2.39",
49
48
  "@types/jest": "^30.0.0",
50
- "jest": "^30.5.1",
51
- "jest-ts-webcompat-resolver": "^1.0.1",
52
- "ts-node": "^10.9.2",
53
- "@types/node": "^26.6.0",
49
+ "@types/node": "^26.6.5",
54
50
  "c8": "^12.0.0",
55
51
  "cheerio": "^1.2.0",
56
52
  "circular-dependency-plugin": "^5.2.2",
53
+ "jest": "^30.5.2",
54
+ "jest-ts-webcompat-resolver": "^1.0.1",
57
55
  "rimraf": "^6.1.3",
58
- "ts-loader": "^9.6.2",
59
- "typescript": "^6.0.3",
60
- "webpack": "^5.111.0",
56
+ "squares-rng": "^2.0.4",
57
+ "swc-loader": "^0.2.7",
58
+ "ts-node": "^10.9.2",
59
+ "typescript": "^7.0.2",
60
+ "webpack": "^5.111.1",
61
61
  "webpack-cli": "^7.2.3"
62
62
  },
63
63
  "scripts": {
@@ -70,6 +70,6 @@
70
70
  "allowScripts": {
71
71
  "unrs-resolver@1.12.2": true,
72
72
  "@parcel/watcher@2.6.0": true,
73
- "@swc/core@1.16.2": true
73
+ "@swc/core@1.16.18": true
74
74
  }
75
75
  }
@@ -0,0 +1,120 @@
1
+ # Q_V infinite loop - investigation handoff
2
+
3
+ Status: root cause found, **nothing fixed yet**. All diagnostic instrumentation
4
+ has been removed from `src/`.
5
+
6
+ ## Symptom
7
+
8
+ `findMats` never returns for the `Q_V` glyph of `__tests__/specific-cases/test-svgs/achispado.svg`
9
+ **once the 1-prong pass is enabled**, i.e. once line 39 of
10
+ `src/find-mat/get-for-1-prongs-on-loop.ts` reads
11
+
12
+ ```ts
13
+ .filter(pos => pos.t > 2**-20 && pos.t < 1 - 2**-20);
14
+ ```
15
+
16
+ (The historical form `pos.t < 2**-20` is impossible, so the 1-prong pass added
17
+ nothing and the problem was invisible. Line 39 is correct as written above; the
18
+ bug is elsewhere.)
19
+
20
+ Reproduce (the loop is synchronous, so only a process-level time limit stops it;
21
+ use Git Bash for `timeout`):
22
+
23
+ ```
24
+ timeout 120 npx jest __tests__/specific-cases # with "Q_V" in the glyph list
25
+ ```
26
+
27
+ ## Where it loops
28
+
29
+ `findAndAdd3Prongs` (`src/find-3-prong/find-and-add-3-prongs.ts`, the
30
+ `do ... while (true)`), started from CpNode #90. Each iteration:
31
+
32
+ 1. `traverseCp(#90)` visits the same 7-vertex face.
33
+ 2. `find3Prong` finds the same 3-prong: radius 23.18, contacts at the dull
34
+ corner at the end of curve 10, the dull corner at the end of curve 14, and
35
+ curve 14 at t = 0.726.
36
+ 3. `findAndAdd3Prong`'s close-by check (`getCloseByCpIfExist`) *does* find the
37
+ copy inserted in the previous iteration, but `if (prongCount <= 2 && ...)`
38
+ ignores anything that is already a 3-prong, so a duplicate is inserted.
39
+ 4. The face does not shrink (the new nodes interleave with existing ones at the
40
+ two corners) - repeat forever.
41
+
42
+ ## Why the face is wrong
43
+
44
+ `traverseCp` from #90 returns to its own vertex through the *other* node #91 and
45
+ continues, so vertex {#90, #91} appears twice on one face. That 2-prong is
46
+ geometrically invalid: center [567.20, 736.36], radius 14.99; its contacts
47
+ (curve 13 at t = 0.9999926 and the far dull corner of curve 14, t = 1)
48
+ interleave along the boundary with those of the existing 3-prong A
49
+ (curve 13 at 0.58, curve 14 at 0.7265), and A's contact point on curve 14
50
+ ([559.44, 748.45]) is 14.37 from the disk's center - inside the disk. The disk
51
+ cuts through curve 14.
52
+
53
+ ## Root cause: a degenerate bezier end in the font data
54
+
55
+ Curve 13 (raw path bezier 21) is a tiny cubic whose last two control points are
56
+ identical in the SVG itself:
57
+
58
+ ```
59
+ [[551.49,738.38],[552.36,738.88],[552.7243186433545,740.2428659207691],[552.7243186433545,740.2428659207691]]
60
+ ```
61
+
62
+ Its speed is therefore 0 at t = 1 (|tangent| ~ 2e-10). Three things break:
63
+
64
+ 1. **Spurious curvature maximum.** `getCurvatureExtremaDd` reports a maximum at
65
+ t = 0.9999925600310483 - the curvature blowing up toward the zero-speed end
66
+ (2.6e3 there, -4e19 at t = 1), not a feature of the shape. It passes the
67
+ line-39 filter: 7e-6 in parameter from the end, ~1e-10 in distance. The
68
+ candidate *is* the c13/c14 junction.
69
+ 2. **Garbage normal.** `ddNormal` at that t is a vector of length 6e-5 whose
70
+ direction is ~11 degrees off the true inward direction at the junction. The
71
+ 2-prong ray search runs along it (curve 14 *was* fully in the searched
72
+ pieces), finds no interior tangency on that ray, and accepts the far corner
73
+ of curve 14 at distance 14.99. `getOsculatingCircle` clamps rO to the maximum
74
+ (1346.7) because the curvature sign is wrong there.
75
+ 3. **Corner not detected.** The c13/c14 junction is a real corner of ~8 degrees,
76
+ but `getInterfaceCcw(c13.ps, c14.ps)` takes curve 13's end tangent from the
77
+ 3e-11-long derivative; the unit tangent comes out as (0.447, 0.894) - a 1:2
78
+ rounding residue - giving a 3.5 degree turn, under the 4 degree `DEGREE_LIMIT`.
79
+ Result: `isDull = true`, `isQuiteDull = false`, so the corner gets no corner
80
+ handling and no corner contact points at all.
81
+
82
+ So the spurious contact sits exactly at a corner position but is ordered as an
83
+ interior point of curve 13 (order 0), outside all corner-ordering logic, while
84
+ the corner itself is never registered - hence the "ordering" smell.
85
+
86
+ ## Suggested fixes (not implemented)
87
+
88
+ Most fundamental first:
89
+
90
+ - Handle near-zero end derivatives: in `getInterfaceCcw` take the end tangent
91
+ from the last *distinct* control point when control points coincide; reject
92
+ curvature-extrema 1-prong candidates where the speed is below a scaled
93
+ threshold (or where the osculating radius comes back clamped to the maximum).
94
+ - Make `findAndAdd3Prong` treat an existing 3-prong at the same contact points
95
+ as a duplicate (drop or rethink the `prongCount <= 2` filter) so a repeat is
96
+ detected instead of re-inserted.
97
+ - Put an iteration cap on the `do ... while (true)` in `findAndAdd3Prongs` that
98
+ throws with context, so this class of bug fails loudly.
99
+ - Consider making `traverseCp` terminate on reaching the start *vertex* (any
100
+ node on the start circle), not the start *node*, which would have exposed the
101
+ inconsistency immediately.
102
+
103
+ ## Useful diagnostics (how it was found, for re-use)
104
+
105
+ - `node --prof node_modules/jest/bin/jest.js --runInBand <spec>` under
106
+ `timeout`, then `node --prof-process isolate-*.log`: the V8 log is appended
107
+ while running, so it survives the kill and shows the hot functions.
108
+ - A per-call iteration counter in `findAndAdd3Prongs` that logs `traverseCp`'s
109
+ visited nodes (id, curve idx, t, order, radius, ring members, prev/next) once
110
+ a call exceeds ~3 iterations, plus logging of `getCloseByCpIfExist` results
111
+ and `insertCpNode` pred/succ in `addToCpTree`, found the duplicate cycle.
112
+ - Logging `nnorm`, `rO`, the searched curve pieces and the antipodes in
113
+ `find2Prong` for the source at curve 13, t > 0.9999 exposed the bad ray.
114
+
115
+ ## Working-tree state to be aware of
116
+
117
+ - Line 39 of `get-for-1-prongs-on-loop.ts` is currently back in the impossible
118
+ `pos.t < 2**-20` form (1-prong pass disabled again).
119
+ - `__tests__/specific-cases/specific-cases.spec.ts` currently lists only `"Q_V"`;
120
+ the full glyph list from HEAD must be restored before committing.
@@ -2,6 +2,8 @@ import type { CpNode } from "../cp-node/cp-node.js";
2
2
  import type { Circle } from "../geometry/circle.js";
3
3
  import type { Mutable } from "../utils/mutable.js";
4
4
 
5
+ const { atan2 } = Math;
6
+
5
7
 
6
8
  function joinSpokes(
7
9
  circle: Circle,
@@ -30,8 +32,8 @@ function byAngle(circle: Circle) {
30
32
  a = [a[0] - c[0], a[1] - c[1]];
31
33
  b = [b[0] - c[0], b[1] - c[1]];
32
34
 
33
- const a_ = Math.atan2(a[1], a[0]);
34
- const b_ = Math.atan2(b[1], b[0]);
35
+ const a_ = atan2(a[1], a[0]);
36
+ const b_ = atan2(b[1], b[0]);
35
37
 
36
38
  return b_ - a_;
37
39
  }
@@ -0,0 +1,49 @@
1
+ import type { CurvePiece } from '../mat/curve-piece.js';
2
+ import { getBoundingBox$, getBoundingBoxTight$ } from '../geometry/get-bounding-box-.js';
3
+ import { getClosestSquareDistanceToRect } from '../geometry/get-closest-square-distance-to-rect.js';
4
+ import { getClosestSquaredDistanceToRotatedRect } from '../geometry/get-closest-squared-distance-to-rotated-rect.js';
5
+
6
+
7
+ /**
8
+ * Returns the given curve pieces with those culled whose closest possible
9
+ * point to `p` is further than `sqrt(maxSquaredDistance)` away.
10
+ *
11
+ * * a piece is kept only if both its (cheap, axis-aligned) loose bounding box
12
+ * and then its (rotated) tight bounding box come within the distance
13
+ * * the bounding boxes are those of the entire curves (not just the pieces)
14
+ * and are memoized per curve
15
+ * * the given array is not modified (callers may reuse it for other points)
16
+ * * any tolerance for floating point error is the responsibility of the
17
+ * caller (i.e. `maxSquaredDistance` should already include it)
18
+ *
19
+ * @param curvePieces the curve pieces to cull
20
+ * @param p the point
21
+ * @param maxSquaredDistance the squared distance beyond which to cull
22
+ *
23
+ * @internal
24
+ */
25
+ function cullCurvePieces(
26
+ curvePieces: CurvePiece[],
27
+ p: number[],
28
+ maxSquaredDistance: number): CurvePiece[] {
29
+
30
+ const culled: CurvePiece[] = [];
31
+
32
+ for (let i=0; i<curvePieces.length; i++) {
33
+ const curvePiece = curvePieces[i];
34
+ const { ps } = curvePiece.curve;
35
+
36
+ const d = getClosestSquareDistanceToRect(getBoundingBox$(ps), p);
37
+ if (d > maxSquaredDistance) { continue; }
38
+
39
+ const d2 = getClosestSquaredDistanceToRotatedRect(getBoundingBoxTight$(ps), p);
40
+ if (d2 > maxSquaredDistance) { continue; }
41
+
42
+ culled.push(curvePiece);
43
+ }
44
+
45
+ return culled;
46
+ }
47
+
48
+
49
+ export { cullCurvePieces }
@@ -2,7 +2,8 @@ import type { CurvePiece } from '../mat/curve-piece.js';
2
2
  import type { FootAndEndpointInfo } from './foot-and-endpoint-info.js';
3
3
  import type { PrePointOnShape } from '../point-on-shape/point-on-shape.js';
4
4
  import { getPotentialClosestPointsOnCurveCertified } from './get-potential-closest-points-on-curve-certified.js';
5
- import { cullCurvePieces1 } from './cull-bezier-pieces-1.js';
5
+ import { cullCurvePieces } from './cull-curve-pieces.js';
6
+ import { getBestDistanceSquared } from './get-best-distance-squared.js';
6
7
  import { toP } from '../point-on-shape/to-p.js';
7
8
 
8
9
 
@@ -20,7 +21,9 @@ function getCloseBoundaryPointsCertified(
20
21
  curvePieces: CurvePiece[],
21
22
  x: number[]): PrePointOnShape[] {
22
23
 
23
- curvePieces = cullCurvePieces1(curvePieces, x);
24
+ // cull the pieces that cannot contain the closest point (the best
25
+ // squared distance already includes a floating point tolerance)
26
+ curvePieces = cullCurvePieces(curvePieces, x, getBestDistanceSquared(curvePieces, x));
24
27
 
25
28
  const pInfos: FootAndEndpointInfo[] = [];
26
29
  for (let i=0; i<curvePieces.length; i++) {
@@ -0,0 +1,96 @@
1
+ import type { Curve } from "flo-boolean";
2
+ import type { FootAndEndpointInfo } from './foot-and-endpoint-info.js';
3
+ import { evalDeCasteljauWithErrDd } from 'flo-bezier3';
4
+ import { ddDiffDouble } from 'double-double';
5
+ import { eps } from '../error-analysis/gamma.js';
6
+
7
+ const { abs, max } = Math;
8
+
9
+ /**
10
+ * Relative widening of the interval bounds - see the error analysis in
11
+ * `getEndpointInfoCertified`: the exact first order requirement is `6u = 3eps`
12
+ * (`u = eps/2` the unit roundoff); `4eps = 2**-50` (exactly representable)
13
+ * leaves room for the `O(u²)` terms.
14
+ */
15
+ const WIDEN = 4*eps;
16
+ /**
17
+ * Absolute slack (relative to `ax² + ay²`) for the lower bound - see the error
18
+ * analysis: the exact first order requirement is `2u² = eps²/2`.
19
+ */
20
+ const SLACK = eps*eps;
21
+
22
+
23
+ /**
24
+ * Returns the `FootAndEndpointInfo` of the endpoint at parameter `t` of the
25
+ * given curve, i.e. the certified interval `dSquaredI` guaranteed to contain
26
+ * the squared distance from `x` to the point on the curve at `t` (`t` taken as
27
+ * exact).
28
+ *
29
+ * * the point is kept in double-double precision (including its evaluation
30
+ * error bound) until the differences with `x` have been taken, so the only
31
+ * rounding left is that of forming the interval in double precision, which
32
+ * is accounted for exactly below
33
+ *
34
+ * Error analysis (`u = eps/2` the unit roundoff, `fl(a∘b) = (a∘b)(1+δ)`,
35
+ * `|δ| <= u`): let `a` be the exact magnitude of the double-double difference
36
+ * `dx` and `Δx` the true (exact) difference. Then
37
+ * * `|Δx| ∈ [a(1-ρ) - ex, a(1+ρ) + ex]` with `ρ = 2u²` (the relative error
38
+ * bound of the double-double minus double subtraction) and `ex` the
39
+ * evaluation error bound on the coordinate
40
+ * * `ax = |hi(dx)|` satisfies `|ax - a| <= u·ax` (the low part of a
41
+ * normalized double-double is at most half an ulp of the high part)
42
+ * * the upper bound `(ax+ex)² + (ay+ey)²` is then computed with 3 roundings
43
+ * (addition, square, sum) and widened with 1 more; it must dominate
44
+ * `(a(1+ρ)+ex)² + ...` which requires `(1-u)⁴(1+WIDEN) >= (1+u+ρ)²`, i.e.
45
+ * `WIDEN >= 6u + O(u²)`
46
+ * * the lower bound `(ax-ex)₊² + (ay-ey)₊²` likewise requires
47
+ * `(1+u)⁴(1-WIDEN) <= (1-u-ρ)²`, i.e. `WIDEN >= 6u + O(u²)`, *except* that
48
+ * when the true term is `0` (`a <= ex`) rounding may still leave
49
+ * `ax - ex <= ax·u` positive, giving a spurious `(ax-ex)² <= 2u²·ax²`; this
50
+ * is covered by subtracting `SLACK·(ax² + ay²)` with `SLACK >= 2u²`
51
+ *
52
+ * @param curve the curve
53
+ * @param x the point from which to check
54
+ * @param t the parameter value of the endpoint
55
+ *
56
+ * @internal
57
+ */
58
+ function getEndpointInfoCertified(
59
+ curve: Curve,
60
+ x: number[],
61
+ t: number): FootAndEndpointInfo {
62
+
63
+ const { ps } = curve;
64
+
65
+ // the point in double-double precision with coordinate-wise absolute
66
+ // error bounds `pE`
67
+ const { p: pDd, pE } = evalDeCasteljauWithErrDd(ps, [0,t]);
68
+
69
+ // the coordinate differences in double-double precision
70
+ const dx = ddDiffDouble(pDd[0], x[0]);
71
+ const dy = ddDiffDouble(pDd[1], x[1]);
72
+
73
+ const ax = abs(dx[1]);
74
+ const ay = abs(dy[1]);
75
+ const [ex, ey] = pE;
76
+
77
+ const lo = max(ax - ex, 0)**2 + max(ay - ey, 0)**2;
78
+ const hi = (ax + ex)**2 + (ay + ey)**2;
79
+
80
+ const dSquaredI = [
81
+ max(lo*(1 - WIDEN) - SLACK*(ax*ax + ay*ay), 0),
82
+ hi*(1 + WIDEN)
83
+ ];
84
+
85
+ const t_ = t === 0 ? 1 : t;
86
+ const curve_ = t === 0 ? curve.prev : curve;
87
+
88
+ return {
89
+ curve: curve_,
90
+ t: t_,
91
+ dSquaredI
92
+ }
93
+ }
94
+
95
+
96
+ export { getEndpointInfoCertified }
@@ -1,10 +1,9 @@
1
1
  import type { Curve } from "flo-boolean";
2
2
  import type { FootAndEndpointInfo } from './foot-and-endpoint-info.js';
3
3
  import { roots } from 'flo-poly';
4
- import { evalDeCasteljauDd, getFootPointsOnBezierPolysCertified, getIntervalBox } from 'flo-bezier3';
4
+ import { getFootPointsOnBezierPolysCertified, getIntervalBox } from 'flo-bezier3';
5
5
  import { rootIntervalToDistanceSquaredInterval } from './root-interval-to-distance-squared-interval.js';
6
- import { squaredDistanceBetweenDd } from "../find-2-prong/squared-distance-between-dd.js";
7
- import { eps } from '../error-analysis/gamma.js';
6
+ import { getEndpointInfoCertified } from './get-endpoint-info.js';
8
7
 
9
8
 
10
9
  /**
@@ -50,27 +49,10 @@ function getPotentialClosestPointsOnCurveCertified(
50
49
  }));
51
50
  }
52
51
 
53
- const ts = tRange.slice();
54
- if (tS === tE) {
55
- ts.push(tS);
56
- }
57
-
58
- infos.push(...ts.map(t => {
59
- const p = evalDeCasteljauDd(ps, [0,t]).map(v => v[0] + v[1]);
60
-
61
- const d = squaredDistanceBetweenDd(p, x);
62
-
63
- const dSquaredI = [d*(1 - eps), d*(1 + eps)];
64
-
65
- const t_ = t === 0 ? 1 : t;
66
- const curve_ = t === 0 ? curve.prev : curve;
52
+ // the endpoints of the range (just the one if the range is a single point)
53
+ const ts = tS === tE ? [tS] : [tS, tE];
67
54
 
68
- return {
69
- curve: curve_,
70
- t: t_,
71
- dSquaredI
72
- }
73
- }));
55
+ infos.push(...ts.map(t => getEndpointInfoCertified(curve, x, t)));
74
56
 
75
57
  return infos;
76
58
  }