diff --git a/backend/src/spicebook/engine/schematic.py b/backend/src/spicebook/engine/schematic.py index 460e646..3acd216 100644 --- a/backend/src/spicebook/engine/schematic.py +++ b/backend/src/spicebook/engine/schematic.py @@ -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