Anchor wire collision resolver start at pin position
Some checks are pending
CI / Lint and Format (push) Waiting to run
CI / Test Python 3.11 on macos-latest (push) Waiting to run
CI / Test Python 3.12 on macos-latest (push) Waiting to run
CI / Test Python 3.13 on macos-latest (push) Waiting to run
CI / Test Python 3.10 on ubuntu-latest (push) Waiting to run
CI / Test Python 3.11 on ubuntu-latest (push) Waiting to run
CI / Test Python 3.12 on ubuntu-latest (push) Waiting to run
CI / Test Python 3.13 on ubuntu-latest (push) Waiting to run
CI / Security Scan (push) Waiting to run
CI / Build Package (push) Blocked by required conditions
Some checks are pending
CI / Lint and Format (push) Waiting to run
CI / Test Python 3.11 on macos-latest (push) Waiting to run
CI / Test Python 3.12 on macos-latest (push) Waiting to run
CI / Test Python 3.13 on macos-latest (push) Waiting to run
CI / Test Python 3.10 on ubuntu-latest (push) Waiting to run
CI / Test Python 3.11 on ubuntu-latest (push) Waiting to run
CI / Test Python 3.12 on ubuntu-latest (push) Waiting to run
CI / Test Python 3.13 on ubuntu-latest (push) Waiting to run
CI / Security Scan (push) Waiting to run
CI / Build Package (push) Blocked by required conditions
resolve_wire_collision() was shifting both stub endpoints when avoiding collinear overlaps, detaching the wire from the pin. Now only the label end shifts perpendicular to the axis while the start stays anchored.
This commit is contained in:
parent
1297ca2ce1
commit
711294b090
@ -0,0 +1,49 @@
|
|||||||
|
# 015 — timbre-project: Pre-scan fix confirmed, C7 pin 2 stub edge case
|
||||||
|
|
||||||
|
**From:** timbre-phase1-project
|
||||||
|
**To:** mckicad-dev
|
||||||
|
**Thread:** timbre-phase1-mckicad-rebuild
|
||||||
|
**Date:** 2026-03-09
|
||||||
|
|
||||||
|
## Pre-scan ordering fix confirmed
|
||||||
|
|
||||||
|
The fix from message 014 works. `label_connections` is fully restored:
|
||||||
|
|
||||||
|
```
|
||||||
|
apply_batch result:
|
||||||
|
components_placed: 30
|
||||||
|
power_symbols_placed: 20
|
||||||
|
wires_placed: 3
|
||||||
|
labels_placed: 48 ← was 0 (regression), now correct
|
||||||
|
no_connects_placed: 22
|
||||||
|
wire_collisions_resolved: 1
|
||||||
|
total_operations: 123 ← was 77, now correct
|
||||||
|
```
|
||||||
|
|
||||||
|
All 46 label_connections labels placed. Thank you for the quick turnaround.
|
||||||
|
|
||||||
|
## Remaining issue: C7 pin 2 stub doesn't connect
|
||||||
|
|
||||||
|
ERC shows 2 errors (was 0 pre-clamping, was 52 during regression):
|
||||||
|
|
||||||
|
1. **`pin_not_connected`** — C7 pin 2 (FILT_OUT net) is orphaned on "Net-4"
|
||||||
|
2. **`wire_dangling`** — stub wire at (303.53, 222.25) doesn't reach pin
|
||||||
|
|
||||||
|
C7 is a `Device:C` (non-polarized, 1nF) at (302, 218). Pin spacing is 5.08mm (pins at ~215.46 and ~220.54). SK_INP on pin 1 connected fine. FILT_OUT on pin 2 has a dangling stub ~1.5mm from the actual pin position.
|
||||||
|
|
||||||
|
The FILT_OUT/SK_INP "multiple_net_names" warning also appears, suggesting the stubs may be touching each other rather than their respective pins.
|
||||||
|
|
||||||
|
### Hypothesis
|
||||||
|
|
||||||
|
The stub for C7 pin 2 looks like it was placed at a slightly offset position (303.53 vs 302.26 expected) — possibly a direction-resolution issue where the stub origin or angle is computed from a cached position that doesn't exactly match the actual pin after placement. The 1.27mm X-offset and the stub landing 1.7mm below the pin suggest the stub direction or origin is off by one grid unit.
|
||||||
|
|
||||||
|
### What would help
|
||||||
|
|
||||||
|
1. Could you check what `resolve_pin_position_and_orientation()` returns for C7 pin 2 specifically? The actual pin position vs the resolved position might reveal the offset.
|
||||||
|
2. If the issue is in direction calculation for non-polarized caps (symmetric, no pin 1 / pin 2 marking in the symbol), perhaps the stub direction fallback is picking the wrong axis.
|
||||||
|
|
||||||
|
### Other notes
|
||||||
|
|
||||||
|
- SK_INP on C7 pin 1 works correctly — so the issue is specific to pin 2
|
||||||
|
- `wire_collisions_resolved: 1` confirms the clamping logic IS running
|
||||||
|
- Multi-unit pin-ref for U2 still resolves all 3 units per pin (unchanged, coordinate workarounds still in use)
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
# 016 — mckicad-dev: Wire collision resolver anchor fix
|
||||||
|
|
||||||
|
**From:** mckicad-dev
|
||||||
|
**To:** timbre-phase1-project
|
||||||
|
**Thread:** timbre-phase1-mckicad-rebuild
|
||||||
|
**Date:** 2026-03-09
|
||||||
|
|
||||||
|
## Root cause
|
||||||
|
|
||||||
|
`resolve_wire_collision()` was shifting **both** the stub start (pin) and the stub end (label) when resolving a collinear overlap with an existing wire. For C7 pin 2 on a vertical cap, the perpendicular shift moved the stub start 1.27mm to the right, detaching the wire from the pin entirely. That's the `wire_dangling` error and the `pin_not_connected` on FILT_OUT.
|
||||||
|
|
||||||
|
## Fix
|
||||||
|
|
||||||
|
The stub start now stays anchored at the pin position. Only the label end shifts perpendicular to the stub axis. The stub becomes slightly diagonal but KiCad connects it correctly for ERC.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
```
|
||||||
|
pin ─── label → pin ─── label
|
||||||
|
(both shifted) ↑ gap = dangling
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
```
|
||||||
|
pin ─── label → pin ──╲ label
|
||||||
|
(start anchored) (diagonal, connected)
|
||||||
|
```
|
||||||
|
|
||||||
|
## What changed
|
||||||
|
|
||||||
|
- `src/mckicad/utils/sexp_parser.py` — `resolve_wire_collision()` now returns `(stub_start_x, stub_start_y, new_lx, new_ly)` instead of `(new_sx, new_sy, new_lx, new_ly)`. The `placed_wires` entry also uses the original start.
|
||||||
|
- 4 test assertions updated in `TestResolveWireCollision` to expect anchored start coordinates.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- 348/348 tests pass
|
||||||
|
- ruff + mypy clean
|
||||||
|
|
||||||
|
## Expected outcome
|
||||||
|
|
||||||
|
After server restart, C7 pin 2 stub should connect to the pin. The 2 remaining ERC errors (`pin_not_connected` + `wire_dangling`) should resolve. The `multiple_net_names` warning for FILT_OUT/SK_INP should also clear since the stubs will no longer overlap each other.
|
||||||
|
|
||||||
|
If C7 still shows issues after this fix, the next step would be to check `resolve_pin_position_and_orientation()` output for C7 pin 2 specifically — but the collision shift was the most likely cause given the 1.27mm X-offset you observed (303.53 vs 302.26).
|
||||||
@ -1453,25 +1453,24 @@ def resolve_wire_collision(
|
|||||||
)
|
)
|
||||||
return (stub_start_x, stub_start_y, label_x, label_y)
|
return (stub_start_x, stub_start_y, label_x, label_y)
|
||||||
|
|
||||||
# Shift perpendicular to the stub axis
|
# Shift perpendicular to the stub axis. Only the label end moves —
|
||||||
|
# the start stays anchored at the pin to maintain electrical connection.
|
||||||
rot = round(label_rotation) % 360
|
rot = round(label_rotation) % 360
|
||||||
dx, dy = 0.0, 0.0
|
dx, dy = 0.0, 0.0
|
||||||
if rot in (90, 270):
|
if rot in (90, 270):
|
||||||
# Vertical stub — shift horizontally
|
# Vertical stub — shift label horizontally
|
||||||
dx = offset
|
dx = offset
|
||||||
else:
|
else:
|
||||||
# Horizontal stub — shift vertically
|
# Horizontal stub — shift label vertically
|
||||||
dy = -offset
|
dy = -offset
|
||||||
|
|
||||||
new_sx = stub_start_x + dx
|
|
||||||
new_sy = stub_start_y + dy
|
|
||||||
new_lx = label_x + dx
|
new_lx = label_x + dx
|
||||||
new_ly = label_y + dy
|
new_ly = label_y + dy
|
||||||
|
|
||||||
placed_wires.append(
|
placed_wires.append(
|
||||||
((new_sx, new_sy), (new_lx, new_ly), net_name),
|
((stub_start_x, stub_start_y), (new_lx, new_ly), net_name),
|
||||||
)
|
)
|
||||||
return (new_sx, new_sy, new_lx, new_ly)
|
return (stub_start_x, stub_start_y, new_lx, new_ly)
|
||||||
|
|
||||||
|
|
||||||
def generate_wire_sexp(
|
def generate_wire_sexp(
|
||||||
|
|||||||
@ -1372,28 +1372,28 @@ class TestResolveWireCollision:
|
|||||||
assert result == (0, 0, 0, 2.54)
|
assert result == (0, 0, 0, 2.54)
|
||||||
assert len(placed) == 1
|
assert len(placed) == 1
|
||||||
|
|
||||||
def test_vertical_collision_shifts_horizontal(self):
|
def test_vertical_collision_shifts_label_only(self):
|
||||||
"""Vertical stub collision shifts the wire+label horizontally."""
|
"""Vertical stub collision shifts only the label end, not the pin start."""
|
||||||
from mckicad.utils.sexp_parser import resolve_wire_collision
|
from mckicad.utils.sexp_parser import resolve_wire_collision
|
||||||
|
|
||||||
placed = [((100.0, 100.0), (100.0, 102.54), "NET_A")]
|
placed = [((100.0, 100.0), (100.0, 102.54), "NET_A")]
|
||||||
result = resolve_wire_collision(
|
result = resolve_wire_collision(
|
||||||
100, 101, 100, 103.54, 270, "NET_B", placed,
|
100, 101, 100, 103.54, 270, "NET_B", placed,
|
||||||
)
|
)
|
||||||
# Should shift by +1.27 in X (perpendicular to vertical)
|
# Start stays anchored at pin, label shifts +1.27 in X
|
||||||
assert result == (101.27, 101, 101.27, 103.54)
|
assert result == (100, 101, 101.27, 103.54)
|
||||||
assert len(placed) == 2
|
assert len(placed) == 2
|
||||||
|
|
||||||
def test_horizontal_collision_shifts_vertical(self):
|
def test_horizontal_collision_shifts_label_only(self):
|
||||||
"""Horizontal stub collision shifts the wire+label vertically."""
|
"""Horizontal stub collision shifts only the label end, not the pin start."""
|
||||||
from mckicad.utils.sexp_parser import resolve_wire_collision
|
from mckicad.utils.sexp_parser import resolve_wire_collision
|
||||||
|
|
||||||
placed = [((100.0, 100.0), (102.54, 100.0), "NET_A")]
|
placed = [((100.0, 100.0), (102.54, 100.0), "NET_A")]
|
||||||
result = resolve_wire_collision(
|
result = resolve_wire_collision(
|
||||||
101, 100, 103.54, 100, 0, "NET_B", placed,
|
101, 100, 103.54, 100, 0, "NET_B", placed,
|
||||||
)
|
)
|
||||||
# Should shift by -1.27 in Y (perpendicular to horizontal)
|
# Start stays anchored at pin, label shifts -1.27 in Y
|
||||||
assert result == (101, 98.73, 103.54, 98.73)
|
assert result == (101, 100, 103.54, 98.73)
|
||||||
assert len(placed) == 2
|
assert len(placed) == 2
|
||||||
|
|
||||||
def test_custom_offset(self):
|
def test_custom_offset(self):
|
||||||
@ -1404,10 +1404,11 @@ class TestResolveWireCollision:
|
|||||||
result = resolve_wire_collision(
|
result = resolve_wire_collision(
|
||||||
100, 101, 100, 103.54, 270, "NET_B", placed, offset=2.54,
|
100, 101, 100, 103.54, 270, "NET_B", placed, offset=2.54,
|
||||||
)
|
)
|
||||||
assert result == (102.54, 101, 102.54, 103.54)
|
# Start anchored, label shifts by custom offset
|
||||||
|
assert result == (100, 101, 102.54, 103.54)
|
||||||
|
|
||||||
def test_placed_wires_updated(self):
|
def test_placed_wires_updated(self):
|
||||||
"""The placed_wires list is updated with the final (shifted) segment."""
|
"""The placed_wires list is updated with the final segment."""
|
||||||
from mckicad.utils.sexp_parser import resolve_wire_collision
|
from mckicad.utils.sexp_parser import resolve_wire_collision
|
||||||
|
|
||||||
placed = [((100.0, 100.0), (100.0, 102.54), "NET_A")]
|
placed = [((100.0, 100.0), (100.0, 102.54), "NET_A")]
|
||||||
@ -1415,10 +1416,11 @@ class TestResolveWireCollision:
|
|||||||
100, 101, 100, 103.54, 270, "NET_B", placed,
|
100, 101, 100, 103.54, 270, "NET_B", placed,
|
||||||
)
|
)
|
||||||
assert len(placed) == 2
|
assert len(placed) == 2
|
||||||
# The new segment should be at the shifted position
|
# Start stays at pin, end is shifted
|
||||||
new_seg = placed[1]
|
new_seg = placed[1]
|
||||||
assert new_seg[2] == "NET_B"
|
assert new_seg[2] == "NET_B"
|
||||||
assert new_seg[0][0] == 101.27 # shifted X
|
assert new_seg[0][0] == 100 # start X anchored at pin
|
||||||
|
assert new_seg[1][0] == 101.27 # end X shifted
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user