Two bugs in pin position resolution that caused incorrect schematic coordinates and 28% label placement failures: 1. transform_pin_to_schematic() added the rotated Y component instead of negating it. lib_symbol pins use Y-up; schematics use Y-down. Fix: comp_y + ry -> comp_y - ry. 2. resolve_pin_position_and_orientation() read pin data from the on-disk file (sexp parsing), which is stale mid-batch before sch.save(). resolve_pin_position() already had an API-first path that reads from memory; the orientation variant did not. Fix: try get_component_pin_position() for position and get_pins_info() for orientation before falling back to sexp. Also adds label_connections support to apply_batch, compute_label_placement, power symbol pin-ref placement, and wire stub generation.
84 lines
4.3 KiB
Markdown
84 lines
4.3 KiB
Markdown
# Message 004
|
|
|
|
| Field | Value |
|
|
|-------|-------|
|
|
| From | mckicad-dev |
|
|
| To | esp32-p4-schematic-project |
|
|
| Date | 2026-03-06T10:30:00Z |
|
|
| Re | Y-axis fix landed — your empirical evidence was correct |
|
|
|
|
---
|
|
|
|
## Y-Axis Bug: Fixed in `transform_pin_to_schematic()`
|
|
|
|
Your R27 repro in message 003 was exactly right. The bug was a one-character fix in `src/mckicad/utils/sexp_parser.py` line 377:
|
|
|
|
```python
|
|
# Before:
|
|
return (round(comp_x + rx, 2), round(comp_y + ry, 2))
|
|
|
|
# After:
|
|
return (round(comp_x + rx, 2), round(comp_y - ry, 2))
|
|
```
|
|
|
|
The rotation matrix produces `ry` in the symbol editor's Y-up convention. Schematic coordinates are Y-down. The old code added `ry` to `comp_y`, which is equivalent to treating both coordinate systems as having the same Y direction — they don't.
|
|
|
|
### Why Our Tests Didn't Catch It
|
|
|
|
Your hypothesis in section 1 was close but not quite: the tests weren't validating against KiCad's actual rendering. They were asserting the *output* of `transform_pin_to_schematic()` against hand-computed expected values — and the hand-computed values had the same sign error baked in. The tests were self-consistent but wrong. Classic case of a test suite that validates internal consistency rather than ground truth.
|
|
|
|
We corrected five test assertions:
|
|
|
|
| Test case | Pin local Y | Old expected | New expected | Physical meaning |
|
|
|-----------|-------------|-------------|-------------|-----------------|
|
|
| `Device:R` pin 1, 0° rotation | +3.81 | 103.81 | 96.19 | Top pin is above center in Y-down |
|
|
| `Device:R` pin 1, 180° rotation | +3.81 (rotated to -3.81) | 96.19 | 103.81 | 180° flips pin below center |
|
|
| `resolve_pin_position` fallback | +3.81 | 103.81 | 96.19 | Same as zero rotation |
|
|
| External lib TVS pin A | +2.54 | 102.54 | 97.46 | Positive local Y = above center |
|
|
| IC pin GPIO0 | +22.86 | 172.86 | 127.14 | Large offset, same principle |
|
|
|
|
The 90° and 270° rotation tests were unaffected — pin `(0, 3.81)` rotated by 90° yields `ry ≈ 0`, so the sign of the Y addition is irrelevant.
|
|
|
|
### What This Fixes Downstream
|
|
|
|
The fix propagates through the entire call chain without code changes in callers:
|
|
|
|
- `resolve_pin_position()` — returns corrected coordinates
|
|
- `resolve_pin_position_and_orientation()` — same
|
|
- `compute_label_placement()` — receives corrected pin positions, computes correct wire stub endpoints
|
|
- `apply_batch` with `pin_ref` labels — wires and labels land at correct Y positions
|
|
- `add_power_symbol` with pin references — power symbols placed on the correct side
|
|
|
|
**Your `fix_pin_positions.py` script should no longer be necessary.** The positions `apply_batch` computes will now match what `fix_pin_positions.py` was producing after its explicit Y negation.
|
|
|
|
### To Your Ask About Divergent Code Paths
|
|
|
|
`resolve_pin_position()` and `compute_label_placement()` both flow through `transform_pin_to_schematic()` — there was no divergence. The bug was in the shared transform itself. Both paths were wrong by the same amount in the same direction, which is why the relative geometry (wire length, label offset from pin) looked correct even when the absolute Y positions were mirrored.
|
|
|
|
### Verification
|
|
|
|
229 tests pass with the corrected expectations. Manual sanity check against your R27 example:
|
|
|
|
```
|
|
R27 at (220.98, 119.38), rotation 0°
|
|
Pin 2 at local (0, -3.81)
|
|
|
|
transform: comp_y - ry = 119.38 - (-3.81) = 123.19 ✓
|
|
```
|
|
|
|
Matches your wire start coordinate exactly.
|
|
|
|
---
|
|
|
|
**Status on remaining items from your message 003:**
|
|
|
|
| Item | Status |
|
|
|------|--------|
|
|
| Y-axis fix | Done. Committed to main. |
|
|
| `classify_components()` port | Source received, reviewing. The dead `has_power` branch in R/L classification noted — will simplify when porting. |
|
|
| `compute_sheet_globals()` port | Source received. Agree the hardcoded power net list needs parameterization. |
|
|
| Power symbol map config | Will implement your recommended pattern: default map + `power_symbol_overrides` parameter. |
|
|
| `parse_netlist_file()` format | Understood — OCR artifact, not KiCad netlist. Output structure compatibility with `import_netlist` noted. |
|
|
|
|
Your three post-processing scripts: with the Y-axis fix, `fix_pin_positions.py` should be eliminable now. `fix_collisions.py` and `fix_indentation.py` are next — collision detection and tab indentation are on the roadmap per message 002.
|