Fix PNP wire-through-body routing and inductor coil fill

The is_inverted flag incorrectly reversed stub wire directions for
PNP/PFET devices, causing wires to route through the transistor body
instead of away from it.  SchemDraw's BjtPnp already orients the
emitter (supply) terminal at the top, so stubs should always go UP
for supply and DOWN for ground regardless of polarity.

- Remove is_inverted direction flipping from _path_style and stubs
- Use output_term (always collector/drain) for output path detection
  instead of supply_term which varies by polarity
- Add .fill(_GRID_BG) to Inductor2 elements so the coil body has a
  solid background fill, visually breaking the through-wire
This commit is contained in:
Ryan Malloy 2026-02-24 13:28:59 -07:00
parent faebe1cee4
commit 8c66b10448

View File

@ -531,8 +531,7 @@ def _path_style(
term_name: str,
path: TerminalPath,
has_signal: bool,
is_inverted: bool,
supply_term: str,
output_term: str,
input_term: str,
) -> str:
"""Classify a path's drawing style: up, down, input, input_up, or output.
@ -540,18 +539,25 @@ def _path_style(
``input_up`` routes input-terminal bias paths (base/gate VCC) left then
up with a local Vdd symbol, keeping them on the input side of the
schematic and avoiding wire crossings with collector/drain vertical paths.
Direction classification is polarity-independent: supply up, ground
down. SchemDraw's BJT/FET symbols already orient supply terminals at the
top and ground terminals at the bottom for both N- and P-type devices.
``output_term`` is always collector (BJT) or drain (FET), regardless of
polarity the output coupling path originates from this terminal.
"""
if path.end_type == "supply":
if term_name == input_term:
return "input_up"
return "up" if not is_inverted else "down"
return "up"
if term_name == input_term and has_signal and len(path.components) > 1:
return "input"
if term_name == supply_term and path.end_type == "ground" and len(path.components) > 1:
if term_name == output_term and path.end_type == "ground" and len(path.components) > 1:
return "output"
if path.end_type == "ground":
return "down" if not is_inverted else "up"
return "down" if not is_inverted else "up"
return "down"
return "down"
# ── Value Formatting ───────────────────────────────────────────
@ -606,7 +612,7 @@ def _get_element(comp: SpiceComponent, models: dict[str, str]):
elif prefix == "C":
return elm.Capacitor()
elif prefix == "L":
return elm.Inductor2()
return elm.Inductor2().fill(_GRID_BG)
elif prefix == "V":
return elm.SourceV()
elif prefix == "I":
@ -1296,6 +1302,7 @@ def _render_connected(parsed: ParsedNetlist, layout: ActiveLayout) -> str:
"emitter": q.emitter,
}
supply_term = "emitter" if is_inverted else "collector"
output_term = "collector"
input_term = "base"
else:
dev_elem = elm.PFet() if is_inverted else elm.NFet()
@ -1309,6 +1316,7 @@ def _render_connected(parsed: ParsedNetlist, layout: ActiveLayout) -> str:
"source": q.source,
}
supply_term = "source" if is_inverted else "drain"
output_term = "drain"
input_term = "gate"
for term_name, term_paths in layout.paths.items():
@ -1326,7 +1334,7 @@ def _render_connected(parsed: ParsedNetlist, layout: ActiveLayout) -> str:
for p in term_paths:
has_sig = any(c.name in signal_names for c in p.components)
style = _path_style(
term_name, p, has_sig, is_inverted, supply_term, input_term
term_name, p, has_sig, output_term, input_term
)
if style == "up":
up_paths.append(p)
@ -1356,22 +1364,16 @@ def _render_connected(parsed: ParsedNetlist, layout: ActiveLayout) -> str:
junc = d.add(elm.Line().at(anchor).left(_INPUT_JUNCTION_LENGTH))
draw_from = junc.end
elif term_name == supply_term and (up_paths or down_paths or output_paths):
# Supply terminal stub: direction matches polarity.
# NPN/NFET collector→up; PNP/PFET emitter→down.
# Longer when output paths also branch so labels clear the wire.
# Supply terminal stub: always route UP (away from body toward VCC).
# SchemDraw's BjtPnp/PFet already orient the supply terminal at
# the top of the symbol, so UP moves away from the body for both
# NPN and PNP. Longer when output paths also branch.
stub = _LEAD_STUB_LENGTH * (1.5 if output_paths else 1.0)
if is_inverted:
lead = d.add(elm.Line().at(anchor).down().length(stub))
else:
lead = d.add(elm.Line().at(anchor).up().length(stub))
lead = d.add(elm.Line().at(anchor).up().length(stub))
draw_from = lead.end
elif term_name not in (supply_term, input_term) and (up_paths or down_paths):
# Ground terminal stub: opposite direction from supply.
# NPN/NFET emitter→down; PNP/PFET collector→up.
if is_inverted:
lead = d.add(elm.Line().at(anchor).up().length(_LEAD_STUB_LENGTH))
else:
lead = d.add(elm.Line().at(anchor).down().length(_LEAD_STUB_LENGTH))
# Ground terminal stub: always route DOWN (away from body toward GND).
lead = d.add(elm.Line().at(anchor).down().length(_LEAD_STUB_LENGTH))
draw_from = lead.end
else:
draw_from = anchor