Menu

#705 GTK3V2MAIN utility.c: ClipLine 4-branch geometry dispatch -- bugprone-branch-clone, intentionally not merged

Next Release
needs-review
None
V5.3.1GA
Windows
5
2026-09-02
2026-08-21
No

Found triaging clang-tidy's bugprone-branch-clone Phase D (see .claude/phase-d-branch-clone-remaining-plan.md and the original SF #699/commit 18a1d00e first slice, which named this same site). app/bin/utility.c:681 and :684 -- inside ClipLine(), two of its 4 else-if branches clang-tidy flags as clone candidates.

Deliberately NOT merged: the 4 branches each swap which point is treated as the 'inside' vs 'outside' endpoint; reordering changes which side's IntersectBox call wins at a box corner. This ticket exists purely to track the decision (per project policy: leave-alone findings get a ticket) so it doesn't keep resurfacing as 'new'. No fix planned. A geometry unit test exercising a clip line that hits a box exactly at a corner (both x and y bounds crossed simultaneously) would give this real coverage -- currently believed untested; CMocka would be a natural home for this one since ClipLine is pure geometry, not UI.

Discussion

  • Martin Fischer

    Martin Fischer - 2026-08-26
    • Description has changed:

    Diff:

    
    
    • status: --> open
    • assigned_to: Martin Fischer
    • XTrackCAD Version: --> V5.3.1GA
    • OS Level: -->
    • Operating System: --> Windows
    • Priority: --> 5
    • Milestone: --> Next Release
     
  • Martin Fischer

    Martin Fischer - 2026-08-26

    Investigated. The sector classification in ClipLine() (utility.c:627-638) already resolves "which point is inside" unambiguously — strict </> comparisons with an outward
    EPSILON margin mean a point exactly on a box corner always classifies as inside on both axes. That part isn't actually ambiguous.

    The real defect is in IntersectBox() (utility.c:588-603): for a line that exits exactly through a corner (x1!=0 && y1!=0), the code short-circuits and evaluates only one
    of two algebraically different edge-intersection formulas, chosen by axis order rather than which is more precise. Both formulas independently solve for the same
    physical point, but floating-point rounding can give slightly different coordinates depending on which one runs — so the same corner can resolve to sub-EPSILON-different
    positions depending on the approach direction of the calling line. This is a plausible source of hairline gaps/overlaps at rectangle corners in grid lines, ruler ticks,
    and general clip-drawn output (csnap.c, drawruler.c, ddrawprim.c/cprint.c are the callers).

    Fix: in the corner-sector case, snap the result to the exact box-boundary coordinate (x1==-1?0:size.x, y1==-1?0:size.y) rather than trusting either formula's raw
    floating-point output, so the outcome is independent of approach direction.

     
  • Martin Fischer

    Martin Fischer - 2026-09-02
    • status: open --> needs-review
     
  • Martin Fischer

    Martin Fischer - 2026-09-02

    Root cause (confirmed)

    IntersectBox() (utility.c:585) resolves a box-boundary intersection by trying two algebraically-equivalent edge formulas in a short-circuit ||, picked by axis order. For
    a line exiting exactly through a box corner, both formulas produce an in-bounds result, but FP rounding makes them differ by ~1e-14 — so the same corner resolves to a
    slightly different point depending on the calling line's approach direction. This is the hairline-gap/overlap source in clip-drawn output (grid lines, ruler ticks,
    dimension lines via csnap.c, drawruler.c, ddrawprim.c, cprint.c).

    Fix — app/bin/utility.c

    Split the || into two if blocks and added a guarded corner snap: after a successful edge intersection, when p1 is in a diagonal sector (x1!=0 && y1!=0) and the solved
    coordinate lands within EPSILON of the shared corner, snap it to the exact boundary value. Both code paths then converge on a bit-identical corner point regardless of
    approach direction.

    Deviation from your proposal: you suggested snapping unconditionally in the corner-sector case. I made it conditional on fabs(solved - boundary) < EPSILON, because a
    diagonal-sector point does not imply a corner exit — e.g. p0=(5,5), p1=(30,12) in a 10×10 box: p1 is in sector (1,1) but the line exits cleanly through the right edge at
    (10, 6.4). Unconditional snapping to (10,10) would be a real error there.

    Test — app/bin/unittest/utilitytest.c

    Added a corner-exit case to ClipLineTests that clips the same geometry forwards and reversed, asserting bit-exact landing on (10,10). Verified it fails without the fix
    (10.00000000000001) and passes with it.

     

Anonymous
Anonymous

Add attachments
Cancel