Treat a line as tangent to a circle when its closest point is on the circle - #71
Merged
Merged
Conversation
…circle compute_line_circle_intersections used to collapse its two computed intersections into one only when they were equal. At a tangency, they are a double root, and the rounding error of the coordinates alone is enough to separate them by several times the point-equality tolerance once the radius exceeds ~30 (e.g. ~2e-6 for a radius of ~46 at coordinates of ~100): a tangency was then reported as two proper intersections a few micro-units apart. Boolean operations split both elements there, which made them share a sub-tolerance edge instead of touching at a point, and compute_union failed with "face area is not positive" or recursed until the stack overflowed (fontanf/packingsolver#595). Each branch now first computes the foot of the perpendicular from the circle's center to the line, the midpoint of the two intersections whenever there are two. If it is on the circle (point_on_circle), the line stays within the tolerance of the circle everywhere between the two intersections, and the foot is returned as the single tangency point. This also covers a tangent line whose rounding makes it miss the circle by a sub-tolerance amount. As a consequence, two genuine crossings between which the line stays within the tolerance of the circle are reported as a tangency at the foot, e.g. the crossing 0.0023 from the shared endpoint of a radius-4 arc and a line in fontanf/packingsolver#574: the expected outputs of that intersection test and of union 033.json are updated accordingly (the new union output agrees with the previous one within the tolerance). New regression tests: two line/arc tangencies at an endpoint of the line segment and one at an interior point, and the unions 034.json to 037.json.
This was referenced Sep 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the remaining cause of fontanf/packingsolver#595 ("face area is not positive").
Problem
compute_line_circle_intersectionscollapsed its two computed intersections into one only when they wereequal.At a tangency, they are a double root. The rounding error of the coordinates alone (a few
epsilontimes their magnitude) moves the line by enough to separate them by about2 * sqrt(2 * radius * delta). That is several times the point-equality tolerance once the radius exceeds ~30: e.g. ~2e-6 for a radius of ~46 at coordinates of ~100. A tangency was then reported as two proper intersections a few micro-units apart.Boolean operations split both elements at these two points. Between them, the two pieces differ by ~1e-14, so they are merged, and the arc and the line end up sharing a sub-tolerance edge instead of touching at a point. For a union of two shapes touching at a point, this merges them into a single shape joined through a sub-tolerance neck. The final
fix_self_intersectionspass then fails on it:036.json: the outer loop goes around the rectangle the wrong way, and the true outer boundary is left over as a face of area -(total area));fix_self_intersectionsandcompute_unionuntil the stack overflows (union037.json, see Fix fix_self_intersections/compute_union infinite mutual recursion #68).In packingsolver#595, the tangent segments are those of the circular arc extras of
approximate_by_line_segments's fallback union. With #69 and #70 merged, that fallback is no longer used for the issue's shape, but it is still reachable, and tangencies at an interior point of a segment were mishandled in 36-44% of random cases.Fix
Each branch of
compute_line_circle_intersectionsnow first computes the foot of the perpendicular from the circle's center to the line: the midpoint of the two intersections, whenever there are two. If it is on the circle (point_on_circle, i.e. at the library's own tolerance), the line stays within that tolerance of the circle everywhere between the two intersections, and the foot is returned as the single tangency point. This also covers a tangent line that rounding makes miss the circle by a sub-tolerance amount. The finalequal-based collapse is removed.Behavior change (fontanf/packingsolver#574)
Two genuine crossings between which the line stays within the tolerance of the circle are now reported as a tangency at the foot.
In #574's first intersection test, a line and a radius-4 arc share an endpoint and cross again 0.0023 further on, never more than ~1.6e-7 apart in between. The crossing is now reported as a tangency at the foot, 0.0011 from the shared endpoint, besides the endpoint itself. The expected outputs of that test and of union
033.jsonare updated. The new union output is simple, non-self-intersecting, and agrees with the previous one within the tolerance (same area up to ~1e-4 out of ~3.57e6).Reporting only the shared endpoint instead (treating the foot as the same contact as the endpoint) was also tried: it makes
033.jsonthrow "outline area is not positive" again, as it originally did in #574.Testing
034.jsonand035.json: the issue's item with its arc's tangent extras;036.jsonand037.json: a disk and a rectangle touching at an interior point of one of its edges.main;037.jsoncrashes it (stack overflow).main.