TRO-Fluss: ILS Weiche als echte Weiche, 2Sep2Swi am Kreisel-Beruehrpunkt, lesbare lagerichtige Darstellung
- _find_weiche_separators: ILS Weiche (TeileArt) ist ein echtes Weichen- Bauteil -> 1Sep1Swi; steuernder Separator = CSV-Nachbar mit Scanner (Mubea 0125 -> 0036), mehrdeutig -> Finding statt Raten - _find_switch_separators: Separator mit Zuordnung auf eine Strecke, der in der TrackIds-Kette eines Kreisels steht, gilt als Kreisel-Separator (Mubea 0005 vor Ausschleuselement 0057) - derive_tros 3d: Ausschleuselement-Weiche und ILS Weiche direkt hintereinander in der TrackIds-Kette -> ein 2Sep2Swi (Mubea TRO10: Sep 0005/0036, Scan 0006/0041); FB_ILS_MTRO_2Sep2Swi fehlt noch in der ILSLib - --use-cords: feste Symbolgroesse je Form, nur TRO-Nummer im Symbol, Typ/Separator als xlabel, Details als Tooltip, Massstab 0.1 statt 0.25 pt/mm, Legende rechts mit nur den vorkommenden Typen, blasse Linien fuer gleiche Bahn, Systemgrenzen dicht an einem TRO werden in der Zeichnung beiseitegeschoben - HundM05 unveraendert (27 Verbindungen), Mubea 18 TROs / 23 Verbindungen Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
02db9a95b0
commit
43543915ca
+248
-28
@@ -166,9 +166,16 @@ GEFAELLE_DROP_MM = 30.0
|
||||
GENERATED_MARKER = "<!-- erzeugt von lib/tro_flow.py -->"
|
||||
|
||||
# Zeichnungsmassstab fuer --use-cords: Graphviz rechnet in Punkt, die Anlage in
|
||||
# Millimeter. 0.25 pt/mm entspricht 1:4 - bei ~11 x 20 m Anlage ergibt das eine
|
||||
# Zeichnung von etwa 2800 x 5000 pt, in der sich die TRO-Kaesten nicht ueberdecken.
|
||||
COORD_SCALE = 0.25
|
||||
# Millimeter. Seit die Symbole eine feste Groesse haben (COORD_SYMBOL_SIZE)
|
||||
# statt mit ihrem Label zu wachsen, reichen 0.1 pt/mm: ~11 x 20 m Anlage ergeben
|
||||
# etwa 1100 x 2000 pt - bildschirmfuellend lesbar. Vorher 0.25 (riesige
|
||||
# Zeichnung, in der Symbole und Schrift verschwanden; Review 2026-10-01).
|
||||
COORD_SCALE = 0.1
|
||||
|
||||
# Mindestabstand (mm) einer Systemgrenze zu einem TRO in der lagerichtigen
|
||||
# Zeichnung - eine BTMT-Station sitzt oft fast auf ihrem Separator (Mubea SYS1
|
||||
# 186 mm neben TRO04); sie wird dann nur in der Darstellung beiseitegeschoben.
|
||||
COORD_MIN_ABSTAND = 600
|
||||
|
||||
# Abstand der Legende zum Anlagenrand in mm
|
||||
LEGEND_MARGIN = 3000
|
||||
@@ -374,13 +381,21 @@ def _find_switch_separators(
|
||||
for sep in separators:
|
||||
sep_kreisel_id = sep.assignment.split("-", 1)[0] if sep.assignment else ""
|
||||
kreisel = kreisel_by_id.get(sep_kreisel_id)
|
||||
if kreisel is None:
|
||||
# Zuordnung zeigt auf die Strecke statt auf den Kreisel, der Separator
|
||||
# steht aber in der TrackIds-Kette eines Kreisels (Mubea 0005: Zuordnung
|
||||
# VF_2, liegt auf Kreisel2 Bahn R direkt vor Ausschleuselement 0057) -
|
||||
# dann gilt dieser Kreisel. Die Gegenprobe "Ausschleuselement in
|
||||
# derselben Kette" (Kriterium 2) bleibt unveraendert.
|
||||
bahn_kreisel = [k for k in kreisel_by_id.values() if sep.teile_id in k.track_ids]
|
||||
if kreisel is not None:
|
||||
bahn_kreisel = [kreisel]
|
||||
if not bahn_kreisel:
|
||||
continue
|
||||
for base, _lane in sep.neighbours:
|
||||
transfer = transfers.get(base)
|
||||
if transfer is None:
|
||||
continue
|
||||
if transfer.teile_id not in kreisel.track_ids:
|
||||
if not any(transfer.teile_id in k.track_ids for k in bahn_kreisel):
|
||||
continue
|
||||
d = (
|
||||
math.hypot(transfer.x - sep.x, transfer.y - sep.y)
|
||||
@@ -393,6 +408,46 @@ def _find_switch_separators(
|
||||
return result
|
||||
|
||||
|
||||
def _find_weiche_separators(
|
||||
separators: list[Element], elements: list[Element], findings: list[str]
|
||||
) -> dict[str, Element]:
|
||||
"""Separator-Id -> ILS Weiche, die er steuert.
|
||||
|
||||
Eine "ILS Weiche" (TeileArt, Element.kind "Weiche") ist ein ECHTES
|
||||
Weichen-Bauteil - anders als das Ausschleuselement in
|
||||
_find_switch_separators, das die Weichenrolle nur uebernimmt. dxfmakros
|
||||
erzeugt sie z. B. am Beruehrpunkt zweier Kreisel (Merkmal "Erzeugt aus":
|
||||
"Kreisel-Beruehrpunkt"; Mubea 0125 zwischen Kreisel2 und Kreisel1).
|
||||
|
||||
Steuernder Separator ist der Separator unter den CSV-Nachbarn der Weiche,
|
||||
der einen Scanner traegt (Scanner-Merkmal "Naechster Separator"). Mubea
|
||||
0125: Nachbarn 0040 und 0036, nur 0036 hat einen Scanner (0041) - vor Ort
|
||||
bestaetigt (Review 2026-08-12 und 2026-10-01). Ist nur ein Separator
|
||||
benachbart, gilt dieser. Bleibt es mehrdeutig, wird nichts geraten,
|
||||
sondern ein Finding geschrieben.
|
||||
"""
|
||||
by_id = {s.teile_id: s for s in separators}
|
||||
mit_scanner = {
|
||||
str(_merkmal(e.merkmale, "Naechster Separator") or "").strip()
|
||||
for e in elements if e.kind == "Scanner"
|
||||
}
|
||||
result: dict[str, Element] = {}
|
||||
for weiche in (e for e in elements if e.kind == "Weiche"):
|
||||
kandidaten = sorted({base for base, _lane in weiche.neighbours if base in by_id})
|
||||
gewaehlt = [s for s in kandidaten if s in mit_scanner]
|
||||
if len(gewaehlt) != 1:
|
||||
gewaehlt = kandidaten if len(kandidaten) == 1 else []
|
||||
if not gewaehlt:
|
||||
findings.append(
|
||||
f"ILS Weiche {weiche.teile_id}: steuernder Separator nicht "
|
||||
f"eindeutig (benachbart: {', '.join(kandidaten) or '-'}) - "
|
||||
f"keinem TRO zugeordnet"
|
||||
)
|
||||
continue
|
||||
result[gewaehlt[0]] = weiche
|
||||
return result
|
||||
|
||||
|
||||
def _incoming_drop(
|
||||
host: Element, sep_id: str, elements_by_id: dict[str, Element]
|
||||
) -> tuple[str | None, float | None]:
|
||||
@@ -543,11 +598,17 @@ def derive_tros(
|
||||
# als gewoehnlicher Separator ihres Zuordnung-Hosts (Kreisel-Bahn oder
|
||||
# Strecke - beides kommt vor) mitgezaehlt werden.
|
||||
switch_hits = _find_switch_separators(switch_candidates, elements)
|
||||
# Separatoren an einer echten ILS Weiche (siehe _find_weiche_separators) -
|
||||
# wie switch_hits vor der Host-Aufloesung herausgenommen, Schritt 3c.
|
||||
weiche_hits = _find_weiche_separators(
|
||||
[s for s in switch_candidates if s.teile_id not in switch_hits],
|
||||
elements, findings,
|
||||
)
|
||||
|
||||
# Separator -> Host-Knoten
|
||||
sep_host: dict[str, str] = {}
|
||||
for sep in separators:
|
||||
if sep.teile_id in switch_hits:
|
||||
if sep.teile_id in switch_hits or sep.teile_id in weiche_hits:
|
||||
continue
|
||||
host = resolve_host(sep, findings)
|
||||
if host is not None:
|
||||
@@ -558,7 +619,7 @@ def derive_tros(
|
||||
sep_scanners: dict[str, list[str]] = {}
|
||||
for scanner in scanners:
|
||||
target = str(_merkmal(scanner.merkmale, "Naechster Separator") or "").strip()
|
||||
if target in sep_host or target in switch_hits:
|
||||
if target in sep_host or target in switch_hits or target in weiche_hits:
|
||||
sep_scanners.setdefault(target, []).append(scanner.teile_id)
|
||||
mounted = scanner.assignment
|
||||
target_host = sep_host.get(target)
|
||||
@@ -804,12 +865,13 @@ def derive_tros(
|
||||
# vor), und die Strecke hat bereits reale, gerichtete Kanten zum Kreisel
|
||||
# (_apply_transfers) - darueber haengt sich die Verknuepfung (link_tros)
|
||||
# von selbst ein, ohne einen weiteren synthetischen Host zu erfinden.
|
||||
as_switch_tro: dict[str, Tro] = {} # Ausschleuselement-Id -> 1Sep1Swi
|
||||
for sep_id, (transfer, distance) in sorted(switch_hits.items()):
|
||||
strecke_id = str(_merkmal(transfer.merkmale, "Strecke", "Förderer") or "")
|
||||
ende = str(_merkmal(transfer.merkmale, "Streckenende") or "")
|
||||
own_scanners = sorted(sep_scanners.get(sep_id, []))
|
||||
art = "Ausschleuselement"
|
||||
tros.append(
|
||||
as_switch_tro[transfer.teile_id] = tro = (
|
||||
Tro(
|
||||
tro_id="",
|
||||
type_name="1Sep1Swi",
|
||||
@@ -826,6 +888,64 @@ def derive_tros(
|
||||
confidence="mittel",
|
||||
)
|
||||
)
|
||||
tros.append(tro)
|
||||
|
||||
# 3c) Separatoren an einer echten ILS Weiche -> 1Sep1Swi (siehe
|
||||
# _find_weiche_separators). Host wie bei 3b: der aufgeloeste Zuordnung-Host
|
||||
# des Separators, sonst die Weiche selbst.
|
||||
weiche_tro: dict[str, Tro] = {} # Weichen-Id -> 1Sep1Swi
|
||||
for sep_id, weiche in sorted(weiche_hits.items()):
|
||||
host = resolve_host(elements_by_id[sep_id], findings)
|
||||
weiche_tro[weiche.teile_id] = tro = Tro(
|
||||
tro_id="",
|
||||
type_name="1Sep1Swi",
|
||||
label=f"Separator {sep_id} an ILS Weiche {weiche.teile_id}",
|
||||
hosts=[host] if host is not None else [weiche.teile_id],
|
||||
separators=[sep_id],
|
||||
scanners=sorted(sep_scanners.get(sep_id, [])),
|
||||
reason=f"steuert die ILS Weiche {weiche.teile_id} (echtes Weichen-Bauteil)",
|
||||
confidence="hoch",
|
||||
)
|
||||
tros.append(tro)
|
||||
|
||||
# 3d) Ausschleuselement-Weiche und ILS Weiche direkt hintereinander in der
|
||||
# TrackIds-Kette eines Kreisels -> EIN 2Sep2Swi (2 Separatoren, 2 Weichen,
|
||||
# 2 Scanner). Mubea: Kreisel2 ... 0005 -> 0057 (AS VF_2) -> 0125 (ILS
|
||||
# Weiche zu Kreisel1) -> 0012 ...; Separator 0005 steuert das Ausschleusen,
|
||||
# 0036 die Weiche - steuerungstechnisch eine Uebergabestelle (Review
|
||||
# 2026-08-12, im Code seit 2026-10-01). Host ist der des AS-Teils (die
|
||||
# Strecke), damit link_tros die Weichenkante in deren ersten Vario-TRO zieht.
|
||||
for kreisel in (e for e in elements if e.kind == "Kreisel"):
|
||||
chain = list(kreisel.track_ids)
|
||||
if len(chain) < 2:
|
||||
continue
|
||||
for a, b in zip(chain, chain[1:] + chain[:1]):
|
||||
for as_id, w_id in ((a, b), (b, a)):
|
||||
as_tro, w_tro = as_switch_tro.get(as_id), weiche_tro.get(w_id)
|
||||
if not (as_tro and w_tro and as_tro in tros and w_tro in tros):
|
||||
continue
|
||||
tros.remove(as_tro)
|
||||
tros.remove(w_tro)
|
||||
tros.append(Tro(
|
||||
tro_id="",
|
||||
type_name="2Sep2Swi",
|
||||
label=(f"Uebergabestelle {kreisel.name}: Ausschleuselement "
|
||||
f"{as_id} + ILS Weiche {w_id}"),
|
||||
hosts=list(as_tro.hosts),
|
||||
separators=as_tro.separators + w_tro.separators,
|
||||
scanners=sorted(as_tro.scanners + w_tro.scanners),
|
||||
reason=(f"Ausschleuselement {as_id} (Separator "
|
||||
f"{', '.join(as_tro.separators)}) und ILS Weiche {w_id} "
|
||||
f"(Separator {', '.join(w_tro.separators)}) liegen in "
|
||||
f"der TrackIds-Kette von {kreisel.name} direkt "
|
||||
f"hintereinander - eine Uebergabestelle"),
|
||||
confidence="hoch",
|
||||
))
|
||||
findings.append(
|
||||
f"{kreisel.name}: Ausschleuselement {as_id} und ILS Weiche "
|
||||
f"{w_id} benachbart - als 2Sep2Swi zusammengefasst "
|
||||
f"(FB_ILS_MTRO_2Sep2Swi fehlt noch in der ILSLib)"
|
||||
)
|
||||
|
||||
# 4) Weichen-Aufwertung: 2 bzw. 3 abgehende Flusskanten
|
||||
for tro in tros:
|
||||
@@ -1502,14 +1622,91 @@ def missing_positions(analysis: Analysis) -> list[str]:
|
||||
return [t.tro_id for t in analysis.tros + analysis.boundaries if not t.position]
|
||||
|
||||
|
||||
def _legend_pos(analysis: Analysis) -> tuple[float, float]:
|
||||
"""Legende links unterhalb der Anlage platzieren (mm)."""
|
||||
# Lagerichtige Darstellung (--use-cords): feste Symbolgroesse je Form in Zoll
|
||||
# (Breite, Hoehe). Ohne fixedsize waechst jede Form mit ihrem Label - Dreiecke
|
||||
# und Doppel-Dreiecke wurden dadurch um ein Vielfaches groesser als die Kreise
|
||||
# (Review 2026-10-01: "manche Symbole sind riesig").
|
||||
COORD_SYMBOL_SIZE: dict[str, tuple[float, float]] = {
|
||||
"circle": (0.45, 0.45),
|
||||
"triangle": (0.62, 0.52),
|
||||
"invtriangle": (0.62, 0.52),
|
||||
"box": (0.7, 0.34),
|
||||
"square": (0.55, 0.55),
|
||||
"diamond": (0.62, 0.5),
|
||||
"pentagon": (0.55, 0.52),
|
||||
"hexagon": (0.65, 0.45),
|
||||
"rarrow": (0.7, 0.36),
|
||||
"ellipse": (0.7, 0.34),
|
||||
}
|
||||
|
||||
|
||||
def _anzeige_position(
|
||||
boundary: Tro, tros: list[Tro]
|
||||
) -> tuple[float, float, float] | None:
|
||||
"""Position einer Systemgrenze fuer die Zeichnung: liegt sie naeher als
|
||||
COORD_MIN_ABSTAND an einem TRO, wird sie radial von ihm weggeschoben."""
|
||||
if not boundary.position:
|
||||
return None
|
||||
x, y, z = boundary.position
|
||||
for tro in tros:
|
||||
if not tro.position:
|
||||
continue
|
||||
dx, dy = x - tro.position[0], y - tro.position[1]
|
||||
d = math.hypot(dx, dy)
|
||||
if d >= COORD_MIN_ABSTAND:
|
||||
continue
|
||||
if d == 0:
|
||||
dx, dy, d = 0.0, -1.0, 1.0
|
||||
x = tro.position[0] + dx / d * COORD_MIN_ABSTAND
|
||||
y = tro.position[1] + dy / d * COORD_MIN_ABSTAND
|
||||
return (x, y, z)
|
||||
|
||||
|
||||
def _tro_kurzinfo(tro: Tro) -> str:
|
||||
"""Zweizeilige Beschriftung neben dem Symbol: Typ und Bauteile."""
|
||||
if tro.type_name in BOUNDARY_TYPES:
|
||||
return _dot_label([tro.type_name, tro.label])
|
||||
if len(tro.separators) > 3:
|
||||
teile = f"{len(tro.separators)} Separatoren"
|
||||
elif tro.separators:
|
||||
teile = "Sep " + ", ".join(tro.separators)
|
||||
else:
|
||||
teile = tro.label
|
||||
return _dot_label([tro.type_name, teile])
|
||||
|
||||
|
||||
def _tro_tooltip(tro: Tro) -> str:
|
||||
"""Vollstaendige Angaben als SVG-Tooltip (Zeilenumbruch per )."""
|
||||
return _tro_label(tro).replace("\\n", " ")
|
||||
|
||||
|
||||
def _coord_node_attrs(tro: Tro, base: dict[str, str]) -> str:
|
||||
"""Knotenattribute fuer --use-cords: feste Groesse, kurzes Label im Symbol,
|
||||
Typ/Bauteile als xlabel daneben, alle Details als Tooltip."""
|
||||
attrs = dict(base)
|
||||
shape = attrs.get("shape", "box")
|
||||
width, height = COORD_SYMBOL_SIZE.get(shape, (0.7, 0.5))
|
||||
attrs.update({
|
||||
"fixedsize": "true",
|
||||
"width": f"{width}",
|
||||
"height": f"{height}",
|
||||
"fontsize": "8",
|
||||
"label": tro.tro_id,
|
||||
"xlabel": _tro_kurzinfo(tro),
|
||||
"tooltip": _tro_tooltip(tro),
|
||||
})
|
||||
return ", ".join(f'{k}="{v}"' for k, v in attrs.items())
|
||||
|
||||
|
||||
def _legend_pos_rechts(analysis: Analysis) -> tuple[float, float]:
|
||||
"""Legende rechts neben der Anlage, obere Kante etwa auf Anlagenoberkante
|
||||
(mm; pos ist bei neato -n der Knotenmittelpunkt)."""
|
||||
all_nodes = analysis.tros + analysis.boundaries
|
||||
xs = [t.position[0] for t in all_nodes if t.position]
|
||||
ys = [t.position[1] for t in all_nodes if t.position]
|
||||
if not xs:
|
||||
return (0.0, 0.0)
|
||||
return (min(xs) - LEGEND_MARGIN, min(ys) - LEGEND_MARGIN)
|
||||
return (max(xs) + LEGEND_MARGIN + 1500, max(ys) - 1500)
|
||||
|
||||
|
||||
# Kantenstil je Art (an material_flow.py angelehnt: Umlauf hellblau gestrichelt,
|
||||
@@ -1546,17 +1743,23 @@ def render_dot(analysis: Analysis, source: str, use_coords: bool = False) -> str
|
||||
add("digraph TROFluss {")
|
||||
if use_coords:
|
||||
# Bei -n bestimmen die pos-Attribute die Lage; rankdir/ranksep entfallen.
|
||||
add(" graph [splines=true, outputorder=edgesfirst,")
|
||||
add(' fontname="Segoe UI", fontsize=11, labelloc="t",')
|
||||
add(f' label="TRO-Fluss lagerichtig ({COORD_SCALE} pt/mm) - '
|
||||
f'{_dot_escape(source)}"];')
|
||||
add(" graph [splines=true, outputorder=edgesfirst, forcelabels=true,")
|
||||
add(' bgcolor="white", pad=0.6,')
|
||||
add(' fontname="Segoe UI", fontsize=16, labelloc="t",')
|
||||
add(f' label="TRO-Fluss - {_dot_escape(source)}"];')
|
||||
else:
|
||||
add(" graph [rankdir=LR, splines=spline, nodesep=0.35, ranksep=1.3,")
|
||||
add(' fontname="Segoe UI", fontsize=11, labelloc="t",')
|
||||
add(f' label="TRO-Fluss - {_dot_escape(source)}"];')
|
||||
add(' node [shape=box, style="rounded,filled", fontname="Segoe UI", fontsize=9];')
|
||||
add(' edge [fontname="Segoe UI", fontsize=8, color="#2f5597", penwidth=1.2,'
|
||||
" arrowsize=0.8];")
|
||||
if use_coords:
|
||||
add(' node [shape=box, style="filled", penwidth=1.6, fontname="Segoe UI Semibold",'
|
||||
' fontsize=9];')
|
||||
add(' edge [fontname="Segoe UI", fontsize=8, color="#2f5597", penwidth=1.3,'
|
||||
" arrowsize=0.7];")
|
||||
else:
|
||||
add(' node [shape=box, style="rounded,filled", fontname="Segoe UI", fontsize=9];')
|
||||
add(' edge [fontname="Segoe UI", fontsize=8, color="#2f5597", penwidth=1.2,'
|
||||
" arrowsize=0.8];")
|
||||
add("")
|
||||
|
||||
for tro in analysis.tros:
|
||||
@@ -1572,7 +1775,10 @@ def render_dot(analysis: Analysis, source: str, use_coords: bool = False) -> str
|
||||
scale = COORD_SCALE if use_coords else 0.01
|
||||
pos = (f', pos="{tro.position[0] * scale:.2f},'
|
||||
f'{tro.position[1] * scale:.2f}"')
|
||||
add(f' "{tro.tro_id}" [label="{_tro_label(tro)}", {rendered}{pos}];')
|
||||
if use_coords:
|
||||
add(f' "{tro.tro_id}" [{_coord_node_attrs(tro, attrs)}{pos}];')
|
||||
else:
|
||||
add(f' "{tro.tro_id}" [label="{_tro_label(tro)}", {rendered}{pos}];')
|
||||
add("")
|
||||
|
||||
# Systemgrenzen (BTMT Be-/Entladestationen): kein Katalogtyp, eigene
|
||||
@@ -1581,12 +1787,18 @@ def render_dot(analysis: Analysis, source: str, use_coords: bool = False) -> str
|
||||
for boundary in analysis.boundaries:
|
||||
rendered = ", ".join(f'{k}="{v}"' for k, v in STYLE_EXT.dot_attrs().items())
|
||||
pos = ""
|
||||
if boundary.position:
|
||||
position = (_anzeige_position(boundary, analysis.tros) if use_coords
|
||||
else boundary.position)
|
||||
if position:
|
||||
scale = COORD_SCALE if use_coords else 0.01
|
||||
pos = (f', pos="{boundary.position[0] * scale:.2f},'
|
||||
f'{boundary.position[1] * scale:.2f}"')
|
||||
add(f' "{boundary.tro_id}" [label="{_tro_label(boundary)}", '
|
||||
f'shape=ellipse, style=filled, {rendered}{pos}];')
|
||||
pos = (f', pos="{position[0] * scale:.2f},'
|
||||
f'{position[1] * scale:.2f}"')
|
||||
if use_coords:
|
||||
attrs = dict(STYLE_EXT.dot_attrs(), shape="ellipse")
|
||||
add(f' "{boundary.tro_id}" [{_coord_node_attrs(boundary, attrs)}{pos}];')
|
||||
else:
|
||||
add(f' "{boundary.tro_id}" [label="{_tro_label(boundary)}", '
|
||||
f'shape=ellipse, style=filled, {rendered}{pos}];')
|
||||
add("")
|
||||
|
||||
for src, dst in analysis.edges:
|
||||
@@ -1599,18 +1811,26 @@ def render_dot(analysis: Analysis, source: str, use_coords: bool = False) -> str
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
# Lagerichtig liegen gleiche Bahn/Strecke ohnehin sichtbar
|
||||
# nebeneinander - dort nur noch als blasser Hinweis.
|
||||
farbe = '"#c8c8c8", penwidth=0.7' if use_coords else '"#7f7f7f"'
|
||||
add(f' "{tro.tro_id}" -> "{other}" [dir=none, style=dotted, '
|
||||
f'color="#7f7f7f", constraint=false, tooltip="gleiche Bahn"];')
|
||||
f'color={farbe}, constraint=false, tooltip="gleiche Bahn"];')
|
||||
add("")
|
||||
|
||||
# Legende: eine Zeile je TRO-Typ (Farbe der Gruppe + eigene Form, siehe
|
||||
# tro_catalog.TRO_CATALOG), plus eine Zeile fuer die Systemgrenzen und die
|
||||
# Kantenarten. Bei -n braucht auch sie ein pos, sonst landet sie auf 0,0
|
||||
# mitten in der Anlage - daher unterhalb links vom Anlagenrand.
|
||||
# Lagerichtig nur die tatsaechlich vorkommenden Typen - die volle
|
||||
# Katalogliste macht die Legende dort laenger als die Anlage breit ist.
|
||||
used_types = {t.type_name for t in analysis.tros}
|
||||
legend_types = [d for d in TRO_CATALOG
|
||||
if not use_coords or d.get_name() in used_types]
|
||||
rows = "".join(
|
||||
f'<tr><td bgcolor="{d.get_fill()}" color="{d.get_style().stroke}" align="left"> {d.get_name()} </td>'
|
||||
f'<td align="left">{d.describe_symbol()}</td></tr>'
|
||||
for d in TRO_CATALOG
|
||||
for d in legend_types
|
||||
)
|
||||
rows += (f'<tr><td bgcolor="{STYLE_EXT.fill}" align="left"> Systemgrenze </td>'
|
||||
f'<td align="left">Ellipse (Materialein-/ausgang, kein TRO)</td></tr>')
|
||||
@@ -1626,8 +1846,8 @@ def render_dot(analysis: Analysis, source: str, use_coords: bool = False) -> str
|
||||
'<td align="left">gleiche Bahn / Strecke</td></tr>'
|
||||
"</table>>")
|
||||
if use_coords:
|
||||
lx, ly = _legend_pos(analysis)
|
||||
add(f' legende [shape=plaintext, style="", fillcolor="none", '
|
||||
lx, ly = _legend_pos_rechts(analysis)
|
||||
add(f' legende [shape=plaintext, style="", fillcolor="none", fontsize=11, '
|
||||
f'label={legend_label}, pos="{lx * COORD_SCALE:.2f},'
|
||||
f'{ly * COORD_SCALE:.2f}"];')
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user