--smooth: no 45-degree leg shorter than two track widths
A short diagonal at a wire's end is a kink beside its pad, and the pour refilled round it left a copper sliver (KiCad copper_sliver) on the TerraHawk lights node. Small offsets now stay square jogs; chamfers get the same floor.
This commit is contained in:
@@ -39,6 +39,9 @@ from .pipeline import _dsn_units_per_um, _mm_to_board, _rule_clearance_dsn, _via
|
|||||||
|
|
||||||
#: how many points ahead a staircase collapse looks from each point
|
#: how many points ahead a staircase collapse looks from each point
|
||||||
LOOKAHEAD = 64
|
LOOKAHEAD = 64
|
||||||
|
#: a 45-degree leg at least this many track widths long: a shorter one is a
|
||||||
|
#: kink beside its end's pad, where a refilled pour leaves a copper sliver
|
||||||
|
MIN_DIAGONAL_WIDTHS = 2
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -121,6 +124,13 @@ def _candidates(a: IntPoint, b: IntPoint) -> list[list[IntPoint]]:
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _too_short(a: IntPoint, b: IntPoint, half: int) -> bool:
|
||||||
|
"""A slanted leg shorter than MIN_DIAGONAL_WIDTHS track widths."""
|
||||||
|
if a.x == b.x or a.y == b.y:
|
||||||
|
return False
|
||||||
|
return (b.x - a.x) ** 2 + (b.y - a.y) ** 2 < (MIN_DIAGONAL_WIDTHS * 2 * half) ** 2
|
||||||
|
|
||||||
|
|
||||||
def _dedupe(pts: list[IntPoint]) -> list[IntPoint]:
|
def _dedupe(pts: list[IntPoint]) -> list[IntPoint]:
|
||||||
out: list[IntPoint] = []
|
out: list[IntPoint] = []
|
||||||
for p in pts:
|
for p in pts:
|
||||||
@@ -211,6 +221,8 @@ class _Smoother:
|
|||||||
|
|
||||||
def path_clear(self, path: list[IntPoint], half: int, net: int, layer: int) -> bool:
|
def path_clear(self, path: list[IntPoint], half: int, net: int, layer: int) -> bool:
|
||||||
"""New legs and new corners of ``path`` (its ends are kept points)."""
|
"""New legs and new corners of ``path`` (its ends are kept points)."""
|
||||||
|
if any(_too_short(p, q, half) for p, q in zip(path, path[1:], strict=False)):
|
||||||
|
return False
|
||||||
return all(
|
return all(
|
||||||
self.leg_clear(p, q, half, net, layer) for p, q in zip(path, path[1:], strict=False)
|
self.leg_clear(p, q, half, net, layer) for p, q in zip(path, path[1:], strict=False)
|
||||||
) and all(self.corner_clear(c, half, net, layer) for c in path[1:-1])
|
) and all(self.corner_clear(c, half, net, layer) for c in path[1:-1])
|
||||||
@@ -262,7 +274,8 @@ class _Smoother:
|
|||||||
cap_out = len_out if (b.x, b.y) in keep or i == len(pts) - 2 else len_out // 2
|
cap_out = len_out if (b.x, b.y) in keep or i == len(pts) - 2 else len_out // 2
|
||||||
d = min(cap_in, cap_out)
|
d = min(cap_in, cap_out)
|
||||||
done = False
|
done = False
|
||||||
while d >= 2 * half:
|
# the chamfer's leg is d * sqrt(2) long
|
||||||
|
while d * d * 2 >= (MIN_DIAGONAL_WIDTHS * 2 * half) ** 2:
|
||||||
p = IntPoint(c.x - ux * d, c.y - uy * d)
|
p = IntPoint(c.x - ux * d, c.y - uy * d)
|
||||||
q = IntPoint(c.x + vx * d, c.y + vy * d)
|
q = IntPoint(c.x + vx * d, c.y + vy * d)
|
||||||
corners = [x for x in (p, q) if (x.x, x.y) not in ((a.x, a.y), (b.x, b.y))]
|
corners = [x for x in (p, q) if (x.x, x.y) not in ((a.x, a.y), (b.x, b.y))]
|
||||||
|
|||||||
@@ -146,3 +146,17 @@ def test_a_detour_with_nothing_in_its_way_is_straightened():
|
|||||||
assert _points(result.nets[0].wires[0]) == [(22000, -2000), (22000, -22000)]
|
assert _points(result.nets[0].wires[0]) == [(22000, -2000), (22000, -22000)]
|
||||||
assert (report.before.right_angles, report.after.right_angles) == (2, 0)
|
assert (report.before.right_angles, report.after.right_angles) == (2, 0)
|
||||||
assert verify_result(dsn, result).clean
|
assert verify_result(dsn, result).clean
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_jog_too_small_for_a_long_diagonal_stays_square():
|
||||||
|
# a 0.1 mm jog halfway: its 45-degree leg would be shorter than two track
|
||||||
|
# widths, a kink beside the copper that leaves pour slivers
|
||||||
|
dsn = parse_dsn(_board(extra_places="(place A3 2100 -22000 front 0)").replace(
|
||||||
|
"(pins A1-1 A2-1)", "(pins A1-1 A3-1)"))
|
||||||
|
wire = [(2000, -2000), (2000, -12000), (2100, -12000), (2100, -22000)]
|
||||||
|
result = _result(_wire(wire))
|
||||||
|
smooth(dsn, result)
|
||||||
|
pts = _points(result.nets[0].wires[0])
|
||||||
|
for a, b in zip(pts, pts[1:], strict=False):
|
||||||
|
if a[0] != b[0] and a[1] != b[1]:
|
||||||
|
assert (b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2 >= (2 * 200) ** 2
|
||||||
|
|||||||
Reference in New Issue
Block a user