Closed bug report

gate: a ring courtyard (outer + inner loop) is treated as filled, so parts in its hole read as overlapping

John Lauer · ·closed by John Lauer

What happens

adom-aiflow gate (0.2.6, still the same code in 0.2.7) reports a courtyard overlap between parts that do not touch, whenever one footprint's courtyard is a ring: an outer loop with an inner loop as its hole.

The board was a rotary encoder sensor. Its electrode rings and guard are generated copper-only footprints, and each courtyard is an annulus drawn as two fp_circles on F.CrtYd. Parts placed inside the ring's hole (and the other rings nested inside it) were all reported as overlapping it. KiCad's own DRC, which uses the exact shapes, reported no courtyard problem.

Cause

crates/adom-aiflow/src/mechanical.rs, around line 193 in 0.2.7:

let inside = |s: &[Vec<Pt>], x: f64, y: f64| s.iter().any(|p| geom::point_in_poly(x, y, p));

Every loop of a courtyard is treated as filled, so a point in the hole of a ring counts as inside it.

Fix we used (works on the board above)

Even-odd counting, so a loop inside another is a hole:

// even-odd: a loop inside another is a hole (an annular courtyard is an outer and an inner circle)
let inside = |s: &[Vec<Pt>], x: f64, y: f64| s.iter().filter(|p| geom::point_in_poly(x, y, p)).count() % 2 == 1;

With it, the gate passed on 41 footprints (courtyards clear, 36 part bodies clear) and matched KiCad's DRC.

Patch

The full local patch against 0.2.6 (this fix, the placement verdict in a sibling issue, and the outline reader already fixed in 0.2.7 via #77) is aiflow/tool-local/aiflow-0.2.6-capencoder.patch on john/capacitive-encoder (private page; the hunk above is the whole fix): https://wiki.adom.inc/john/capacitive-encoder

Found by a Claude Opus 5.5 thread running the KiCad lane on 2026-10-20.

1 Reply

John Lauer ·

Fixed in adom-aiflow 0.2.8. Your diagnosis was right.

  • Even-odd rule: a point is inside a courtyard only when it is inside an odd number of that footprint's loops, so an annulus drawn as two fp_circles has a real hole. The gate's courtyard and body overlap checks both use it.
  • Overlap test: two courtyards overlap only where some point lies inside both, which covers crossing edges and one courtyard inside the other. Courtyards that only touch still pass.
  • Overlap depth: the reported overlap depth is now capped by the overlap's real thickness. Two rings sharing a 1 mm band used to read as their whole bounding box, and now read about 1 mm.
  • Tests:
    • a part fully inside a ring's hole is clear
    • a part across the band overlaps
    • a part reaching from the hole into the band overlaps
    • nested rings are clear
    • nested rings whose bands overlap are refused

Not changed: place pack and place check still pack courtyards as boxes, so a ring's hole is not offered as a free spot. Placing parts inside a ring is still yours to do; the gate judges it correctly now.

Upgrade: adom-wiki pkg install adom/adom-aiflow, then adom-aiflow --version says 0.2.8.

John Lauer closed this ·

Log in to reply.