Return the set of snapped grid points where a junction dot is needed. Assumes through-wires have already been split at T-positions. Unified rule: a junction is required when the total number of connections at a grid point is >= 3, where each wire endpoint and each component pin
(wires, components)
| 115 | |
| 116 | |
| 117 | def _find_junction_points(wires, components) -> set[tuple[int, int]]: |
| 118 | """ |
| 119 | Return the set of snapped grid points where a junction dot is needed. |
| 120 | Assumes through-wires have already been split at T-positions. |
| 121 | |
| 122 | Unified rule: a junction is required when the total number of connections |
| 123 | at a grid point is >= 3, where each wire endpoint and each component pin |
| 124 | counts as one connection. |
| 125 | |
| 126 | Examples (from the schematic wiring rules picture): |
| 127 | - R1+R2 series (2 pins, 0 wire endpoints) → 2 connections → no junction |
| 128 | - Wire+GND (1 wire endpoint, 1 pin) → 2 connections → no junction |
| 129 | - R4+R5+R6 (3 pins, 0 wire endpoints) → 3 connections → junction ✓ |
| 130 | - R11+R12+wire (2 pins, 1 wire endpoint) → 3 connections → junction ✓ |
| 131 | - T-connection (1 endpoint + wire through) → safety net rule → junction ✓ |
| 132 | """ |
| 133 | # Count wire endpoints per grid point |
| 134 | endpoints: dict[tuple, int] = {} |
| 135 | for wire in wires: |
| 136 | if len(wire.points) < 2: |
| 137 | continue |
| 138 | for pt in (wire.points[0], wire.points[-1]): |
| 139 | k = _pt_key(pt) |
| 140 | endpoints[k] = endpoints.get(k, 0) + 1 |
| 141 | |
| 142 | # Count component pins per grid point |
| 143 | pin_counts: dict[tuple, int] = {} |
| 144 | for comp in components: |
| 145 | for pin_pt in comp.pin_scene_pos(): |
| 146 | k = _pt_key(pin_pt) |
| 147 | pin_counts[k] = pin_counts.get(k, 0) + 1 |
| 148 | |
| 149 | junctions: set[tuple[int, int]] = set() |
| 150 | |
| 151 | # Safety net: wire endpoint landing on the interior of another wire |
| 152 | for wire in wires: |
| 153 | if len(wire.points) < 2: |
| 154 | continue |
| 155 | for pt in (wire.points[0], wire.points[-1]): |
| 156 | k = _pt_key(pt) |
| 157 | for other in wires: |
| 158 | if other is not wire and _on_wire_interior(k, other): |
| 159 | junctions.add(k) |
| 160 | break |
| 161 | |
| 162 | # Unified rule: junction when wire_endpoints + component_pins >= 3 |
| 163 | all_points = set(endpoints.keys()) | set(pin_counts.keys()) |
| 164 | for k in all_points: |
| 165 | if endpoints.get(k, 0) + pin_counts.get(k, 0) >= 3: |
| 166 | junctions.add(k) |
| 167 | |
| 168 | return junctions |
| 169 | |
| 170 | |
| 171 | # ── scene ───────────────────────────────────────────────────────────────────── |
no test coverage detected