Open bug report

laser-etch: rect_contained samples 5 columns, so a raised feature between them is invisible and the mark is placed across it

Ray · 1mo ago ·edited

rect_contained in api/laser_fit.py decides whether a fitted part-number block is clear of the face edge, the pin-1 keep-out and any raised feature by sampling fifteen points: five columns along the text (at −1, −½, 0, ½, 1 of the half-width) by three rows across it. A raised feature that falls between two columns is invisible, so the mark is placed straight across it and reported clear.

Found while reviewing the 1.3.16 placement change; it is pre-existing and independent of that change.

Reproducer

A 12 × 3 mm top face with a 0.4 mm strip standing 0.05 mm proud in the middle of it, at x ∈ [1.0, 1.4]:

part = solids(box(-6,-1.5,0, 6,1.5,1.0), box(1.0,-1.5,1.0, 1.4,1.5,1.05))
best, _, _ = lf.fit_text_block(part, 4.0, 0.5, None, angles=(0,))
# scale 2.580, w 10.32, cx 0.0  ->  text spans [-5.16, +5.16], straddling the strip

Calling the check directly on that placement:

occluders seen above the face: 5          (the strip is one of them)
rect_contained(centre 0, w=10.32) -> True
x columns actually sampled: [-5.16, -2.58, 0.0, 2.58, 5.16]   any inside [1.0, 1.4]: False

The occluder is known — occluders_above() returned it — and the containment check still passes, because no sample lands on it. Full-scale text is then lasered across a raised feature.

When it bites

Any feature narrower than a quarter of the text width that sits away from the ends of the text. Column spacing is w/4; at the 86% cap on a 12 mm face that is 2.58 mm, so anything under ~2.5 mm wide mid-face is unseen. A cathode stripe or pin-1 bar at the end of a body is caught, because the outermost column reaches it — which is why denser sampling changed nothing on the SOD-123 that motivated 1.3.16: on that part there was genuinely no overlap to find. Mid-face features are the gap: moulded bars, dividers, a raised logo, a second-row bump.

The fix is exact, not denser

The occluders are axis-aligned bounding boxes in plan, and at angles 0 and 90 the text block is an axis-aligned rectangle. Overlap between two axis-aligned rectangles is a four-comparison intersection test — exact, and cheaper than fifteen point samples. rect_contained should test every occluder's plan bbox against the text rect analytically (with the same margin free_center already uses), keeping the sampled _inside(face) test for the face boundary, which is not a rectangle.

Denser sampling is the wrong direction: it makes the miss rarer without removing it, and costs more per candidate.

A refinement from project-e0, who has been staring at the occluder data, and it should shape the fix: the analytic test must not be the arbiter, only the fast-fail. Occluder bboxes overapproximate badly for non-rectangular raised faces — a fillet face that wraps a corner spans a whole body edge in bbox terms — so a raw rect-vs-bbox overlap would veto text that is genuinely clear and over-reject on exactly the parts with rounded features. So: analytic overlap against the bbox first; when it reports no overlap, the pad is clear and no sampling is needed (this is what closes the mid-face hole, since the strip's bbox is exact); when it reports overlap, fall back to the point sampling as the arbiter of whether the real face geometry is hit. That keeps the cheapness of the analytic test and the truth of the sampled one, and it means a part whose marks are right today does not move.

Why this is not bundled into a review

It changes containment semantics for every part. Marks that today pass because a feature slipped between columns would move or shrink, so it is a corpus re-etch consideration, not a code-only one. Ray's call on timing, alongside the 1.3.16 re-etch already pending.

Regression test to ship with it: the fixture above must place the text in one of the two free intervals (the 86% cap will not fit either side, so the fit should shrink to fit the larger one) rather than across the strip; and an end-strip part must be byte-identical to 1.3.16, so the exact test does not move marks that were already right.

0 Replies

Log in to reply.