Improve connected schematic label placement (Mims-style)
- Add lead stub wires (0.75 unit) from collector and emitter pins for clearance between transistor and first component - Transistor label placed right of body, all chain labels on left - Offset parallel paths (RE/CE) label on right, facing outward - Wider parallel path spacing (2.5 units) for label breathing room - Down-turning components label outward based on path direction - Parameterized label_loc in _draw_vert_chain for context-aware placement
This commit is contained in:
parent
4bc68a58bd
commit
c120a179c8
@ -775,8 +775,14 @@ def _label_multiterminal(d, placed, comp: SpiceComponent) -> None:
|
|||||||
# ── Connected Layout Renderer ────────────────────────────────
|
# ── Connected Layout Renderer ────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
def _draw_vert_chain(d, parsed, start, components, going_up, end_type, end_node):
|
def _draw_vert_chain(
|
||||||
"""Draw a chain of components vertically, terminated by Vdd or Ground."""
|
d, parsed, start, components, going_up, end_type, end_node, label_loc="right"
|
||||||
|
):
|
||||||
|
"""Draw a chain of components vertically, terminated by Vdd or Ground.
|
||||||
|
|
||||||
|
label_loc controls which side labels appear on ("left" or "right").
|
||||||
|
Classic drafting rule: labels face outward from the circuit center.
|
||||||
|
"""
|
||||||
import schemdraw.elements as elm
|
import schemdraw.elements as elm
|
||||||
|
|
||||||
direction = "up" if going_up else "down"
|
direction = "up" if going_up else "down"
|
||||||
@ -785,7 +791,7 @@ def _draw_vert_chain(d, parsed, start, components, going_up, end_type, end_node)
|
|||||||
if i == 0 and start is not None:
|
if i == 0 and start is not None:
|
||||||
elem = elem.at(start)
|
elem = elem.at(start)
|
||||||
elem = getattr(elem, direction)()
|
elem = getattr(elem, direction)()
|
||||||
elem = elem.label(_component_label(comp), loc="left")
|
elem = elem.label(_component_label(comp), loc=label_loc)
|
||||||
d.add(elem)
|
d.add(elem)
|
||||||
|
|
||||||
if end_type == "ground":
|
if end_type == "ground":
|
||||||
@ -810,9 +816,10 @@ def _draw_horiz_then_down(d, parsed, start, path, going_right):
|
|||||||
|
|
||||||
is_last = i == len(comps) - 1
|
is_last = i == len(comps) - 1
|
||||||
if is_last and len(comps) > 1:
|
if is_last and len(comps) > 1:
|
||||||
# Turn downward at the bend
|
# Turn downward at the bend — label faces outward
|
||||||
d.push()
|
d.push()
|
||||||
d.add(elem.down().label(_component_label(comp), loc="right"))
|
down_label = "right" if going_right else "left"
|
||||||
|
d.add(elem.down().label(_component_label(comp), loc=down_label))
|
||||||
if path.end_type == "ground":
|
if path.end_type == "ground":
|
||||||
d.add(elm.Ground())
|
d.add(elm.Ground())
|
||||||
elif path.end_type == "supply":
|
elif path.end_type == "supply":
|
||||||
@ -856,10 +863,11 @@ def _render_connected(parsed: ParsedNetlist, layout: ActiveLayout) -> str:
|
|||||||
signal_names = {s.name for s in layout.signal_sources}
|
signal_names = {s.name for s in layout.signal_sources}
|
||||||
is_inverted = layout.device_type in ("bjt_pnp", "pfet")
|
is_inverted = layout.device_type in ("bjt_pnp", "pfet")
|
||||||
|
|
||||||
# Place active device at the center
|
# Place active device at the center — label goes right of the body
|
||||||
|
# (Mims convention: transistor type label beside the symbol)
|
||||||
if layout.device_type.startswith("bjt"):
|
if layout.device_type.startswith("bjt"):
|
||||||
dev_elem = elm.BjtPnp() if is_inverted else elm.BjtNpn()
|
dev_elem = elm.BjtPnp() if is_inverted else elm.BjtNpn()
|
||||||
q = d.add(dev_elem.label(_component_label(layout.device)))
|
q = d.add(dev_elem.label(_component_label(layout.device), loc="right"))
|
||||||
anchors = {
|
anchors = {
|
||||||
"collector": q.collector,
|
"collector": q.collector,
|
||||||
"base": q.base,
|
"base": q.base,
|
||||||
@ -869,7 +877,7 @@ def _render_connected(parsed: ParsedNetlist, layout: ActiveLayout) -> str:
|
|||||||
input_term = "base"
|
input_term = "base"
|
||||||
else:
|
else:
|
||||||
dev_elem = elm.PFet() if is_inverted else elm.NFet()
|
dev_elem = elm.PFet() if is_inverted else elm.NFet()
|
||||||
q = d.add(dev_elem.label(_component_label(layout.device)))
|
q = d.add(dev_elem.label(_component_label(layout.device), loc="right"))
|
||||||
anchors = {
|
anchors = {
|
||||||
"drain": q.drain,
|
"drain": q.drain,
|
||||||
"gate": q.gate,
|
"gate": q.gate,
|
||||||
@ -905,38 +913,61 @@ def _render_connected(parsed: ParsedNetlist, layout: ActiveLayout) -> str:
|
|||||||
|
|
||||||
total = len(up_paths) + len(down_paths) + len(input_paths) + len(output_paths)
|
total = len(up_paths) + len(down_paths) + len(input_paths) + len(output_paths)
|
||||||
|
|
||||||
# Junction wire for input terminal when paths branch
|
# Mims-style label placement: vertical labels go LEFT,
|
||||||
|
# keeping clear of the VCC/Ground terminators above/below.
|
||||||
|
# The transistor label sits on the RIGHT, so left is open.
|
||||||
|
# Offset parallel paths (i > 0) flip to RIGHT to face outward.
|
||||||
|
vert_label = "left"
|
||||||
|
|
||||||
|
# Lead stub wires from collector/emitter create clearance from Q
|
||||||
|
# (Mims always drew short leads from transistor pins)
|
||||||
if term_name == input_term and total > 1:
|
if term_name == input_term and total > 1:
|
||||||
|
# Base/gate: junction wire left for bias branching
|
||||||
junc = d.add(elm.Line().at(anchor).left(1))
|
junc = d.add(elm.Line().at(anchor).left(1))
|
||||||
draw_from = junc.end
|
draw_from = junc.end
|
||||||
|
elif term_name == supply_term and (up_paths or output_paths):
|
||||||
|
# Collector/drain: short stub up for clearance
|
||||||
|
lead = d.add(elm.Line().at(anchor).up().length(0.75))
|
||||||
|
draw_from = lead.end
|
||||||
|
elif term_name not in (supply_term, input_term) and (down_paths):
|
||||||
|
# Emitter/source: short stub down for clearance
|
||||||
|
lead = d.add(elm.Line().at(anchor).down().length(0.75))
|
||||||
|
draw_from = lead.end
|
||||||
else:
|
else:
|
||||||
draw_from = anchor
|
draw_from = anchor
|
||||||
|
|
||||||
# Vertical-up paths (toward supply rail)
|
# Vertical-up paths (toward supply rail)
|
||||||
for i, p in enumerate(up_paths):
|
for i, p in enumerate(up_paths):
|
||||||
d.push()
|
d.push()
|
||||||
|
# Offset parallel paths flip label side to avoid overlap
|
||||||
|
loc = "right" if i > 0 else vert_label
|
||||||
if i > 0:
|
if i > 0:
|
||||||
d.add(elm.Line().at(draw_from).right(1.5 * i))
|
d.add(elm.Line().at(draw_from).right(2.5 * i))
|
||||||
_draw_vert_chain(
|
_draw_vert_chain(
|
||||||
d, parsed, None, p.components, True, p.end_type, p.end_node
|
d, parsed, None, p.components, True, p.end_type, p.end_node,
|
||||||
|
label_loc=loc,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
_draw_vert_chain(
|
_draw_vert_chain(
|
||||||
d, parsed, draw_from, p.components, True, p.end_type, p.end_node
|
d, parsed, draw_from, p.components, True, p.end_type, p.end_node,
|
||||||
|
label_loc=loc,
|
||||||
)
|
)
|
||||||
d.pop()
|
d.pop()
|
||||||
|
|
||||||
# Vertical-down paths (toward ground)
|
# Vertical-down paths (toward ground)
|
||||||
for i, p in enumerate(down_paths):
|
for i, p in enumerate(down_paths):
|
||||||
d.push()
|
d.push()
|
||||||
|
loc = "right" if i > 0 else vert_label
|
||||||
if i > 0:
|
if i > 0:
|
||||||
d.add(elm.Line().at(draw_from).right(1.5 * i))
|
d.add(elm.Line().at(draw_from).right(2.5 * i))
|
||||||
_draw_vert_chain(
|
_draw_vert_chain(
|
||||||
d, parsed, None, p.components, False, p.end_type, p.end_node
|
d, parsed, None, p.components, False, p.end_type, p.end_node,
|
||||||
|
label_loc=loc,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
_draw_vert_chain(
|
_draw_vert_chain(
|
||||||
d, parsed, draw_from, p.components, False, p.end_type, p.end_node
|
d, parsed, draw_from, p.components, False, p.end_type, p.end_node,
|
||||||
|
label_loc=loc,
|
||||||
)
|
)
|
||||||
d.pop()
|
d.pop()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user