Materialfluss: Ein-/Ausgabestationen im Chain-Graph zeichnen
BTMT Beladung/Entladung bzw. die generischen Eingang/Ausgang-Symbole waren bislang von --chain-graph ausgenommen und wurden als Kantenziel einfach uebersprungen. Die Anlage laeuft als Kreislauf, diese Stationen sind ihre einzigen Materialquellen/-senken und gehoeren als eigene Knoten in jeden Graphtyp (coarsebox/compound/subgraph). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
0477f05180
commit
df6d56090e
+56
-10
@@ -2551,11 +2551,19 @@ def render_chain_svg(
|
||||
"""
|
||||
Massstabsgetreuer Chain-/Lageplan der groben Objekte (--chain-graph): jeder
|
||||
Kreisel als seine beiden Bahnen L/R (_kreisel_lane_rects(), Zuordnung aus
|
||||
allen Elementen fuer den Mehrheitsentscheid - wie --show-bbs) und jede
|
||||
allen Elementen fuer den Mehrheitsentscheid - wie --show-bbs), jede
|
||||
Strecke/Gefaellestrecke als ein Rechteck an ihrer echten Position/
|
||||
Boundingbox. Kanten kommen direkt aus dem schon aufgeloesten groben
|
||||
Boundingbox, und die BTMT-/generischen Ein-/Ausgabestationen (Beladung/
|
||||
Entladung/Eingang/Ausgang) ebenso - die Anlage laeuft als Kreislauf, die
|
||||
Stationen sind seine einzigen Materialquellen/-senken und gehoeren mit in
|
||||
jeden Chain-Graph. Gezeichnet werden nur Stationen, die auch echte Knoten
|
||||
im groben Materialfluss-Graphen sind (graph.nodes) - ein durch eine
|
||||
deckungsgleiche BTMT-Station verschlucktes generisches Eingang/Ausgang-
|
||||
Symbol (_merge_station_symbols(), siehe build_graph()) taucht folgerichtig
|
||||
nicht doppelt auf. Kanten kommen direkt aus dem schon aufgeloesten groben
|
||||
Materialfluss-Graphen (graph.edges) - eine Kante, deren Knoten hier nicht
|
||||
gezeichnet wird (z. B. eine BTMT-Station), wird schlicht uebersprungen.
|
||||
gezeichnet wird (z. B. eine Station ohne Position/Boundingbox), wird
|
||||
schlicht uebersprungen.
|
||||
|
||||
track nur fuer --graphtype compound (siehe render_chain_compound_svg): mit
|
||||
angegebenem TrackGraph werden zusaetzlich die Anbauteile (Separator,
|
||||
@@ -2570,6 +2578,7 @@ def render_chain_svg(
|
||||
rects: dict[str, tuple[float, float, float, float]] = {}
|
||||
lane_info: dict[str, tuple[Element, str]] = {}
|
||||
strecke_labels: dict[str, Element] = {}
|
||||
station_labels: dict[str, Element] = {}
|
||||
|
||||
for element in elements:
|
||||
if element.position is None or element.bbox is None:
|
||||
@@ -2586,6 +2595,16 @@ def render_chain_svg(
|
||||
)
|
||||
strecke_labels[element.teile_id] = element
|
||||
|
||||
for node in graph.nodes.values():
|
||||
element = node.element
|
||||
if not element.is_station or element.position is None or element.bbox is None:
|
||||
continue
|
||||
rects[node.node_id] = (
|
||||
element.x - element.bbox_x / 2, element.y - element.bbox_y / 2,
|
||||
element.x + element.bbox_x / 2, element.y + element.bbox_y / 2,
|
||||
)
|
||||
station_labels[node.node_id] = element
|
||||
|
||||
if not rects:
|
||||
raise ValueError("keine Kreisel/Strecken mit Position/Boundingbox gefunden")
|
||||
|
||||
@@ -2666,10 +2685,15 @@ def render_chain_svg(
|
||||
kreisel, lane = lane_info[node_id]
|
||||
style = NODE_STYLES.get(kreisel.kind, DEFAULT_NODE_STYLE)
|
||||
label = f"{kreisel.name or kreisel.teile_id} Bahn {lane}"
|
||||
else:
|
||||
elif node_id in strecke_labels:
|
||||
strecke = strecke_labels[node_id]
|
||||
style = NODE_STYLES.get(strecke.kind, DEFAULT_NODE_STYLE)
|
||||
label = f"{strecke.name or node_id} [{node_id}]"
|
||||
else:
|
||||
station = station_labels[node_id]
|
||||
style = NODE_STYLES.get(station.kind, DEFAULT_NODE_STYLE)
|
||||
role = "Materialeingang" if station.is_entry else "Materialausgang"
|
||||
label = f"{station.name or node_id} [{node_id}] - {role}"
|
||||
box_svg.append(
|
||||
f'<rect x="{sx(x0):.1f}" y="{sy(y1):.1f}" width="{(x1 - x0) * scale:.1f}" '
|
||||
f'height="{(y1 - y0) * scale:.1f}" fill="{style["fillcolor"]}" fill-opacity="0.3" '
|
||||
@@ -2700,12 +2724,14 @@ def render_chain_svg(
|
||||
el_lane = _lane_of_point((el.x, el.y), lane_rects)
|
||||
if el_lane == lane:
|
||||
children.append(el)
|
||||
else:
|
||||
elif node_id in strecke_labels:
|
||||
strecke = strecke_labels[node_id]
|
||||
children = [
|
||||
el for el in _container_children(strecke, by_id)
|
||||
if el.x is not None and el.y is not None
|
||||
]
|
||||
else:
|
||||
continue # Station - kein Container, keine Anbauteile zum Verschachteln
|
||||
if not children:
|
||||
continue
|
||||
|
||||
@@ -2804,8 +2830,11 @@ def render_chain_subgraph_dot(
|
||||
--graphtype subgraph: dieselbe Containment wie render_chain_compound_svg(),
|
||||
aber als verschachtelte Graphviz-Cluster (Kreisel-Cluster mit einem
|
||||
Bahn-L/R-Cluster je Bahn darin, Strecke/Gefaellestrecke-Cluster) statt
|
||||
massstabsgetreuer SVG. Rendern mit dot -Tsvg (siehe dot_to_svg()) - NICHT
|
||||
lagerichtig, das Layout kommt von Graphviz.
|
||||
massstabsgetreuer SVG, plus die Ein-/Ausgabestationen (Beladung/Entladung/
|
||||
Eingang/Ausgang) als eigene, nicht verschachtelte Knoten - die Anlage
|
||||
laeuft als Kreislauf, sie sind seine einzigen Materialquellen/-senken.
|
||||
Rendern mit dot -Tsvg (siehe dot_to_svg()) - NICHT lagerichtig, das Layout
|
||||
kommt von Graphviz.
|
||||
"""
|
||||
by_id = {e.teile_id: e for e in elements}
|
||||
|
||||
@@ -2884,6 +2913,21 @@ def render_chain_subgraph_dot(
|
||||
add(" }")
|
||||
add("")
|
||||
|
||||
# Ein-/Ausgabestationen (Beladung/Entladung/Eingang/Ausgang): die Anlage
|
||||
# laeuft als Kreislauf, sie sind seine einzigen Materialquellen/-senken -
|
||||
# eigene (nicht verschachtelte) Knoten, damit ihre schon aufgeloesten
|
||||
# Kanten unten nicht wie bisher uebersprungen werden.
|
||||
for node in sorted(graph.nodes.values(), key=lambda n: n.node_id):
|
||||
if not node.element.is_station:
|
||||
continue
|
||||
top_ids.add(node.node_id)
|
||||
style = NODE_STYLES.get(node.element.kind, DEFAULT_NODE_STYLE)
|
||||
attrs = ", ".join(f'{key}="{value}"' for key, value in style.items())
|
||||
role = "Materialeingang" if node.element.is_entry else "Materialausgang"
|
||||
label = _dot_label([node.element.name or node.node_id, f"{node.element.kind} - {role}"])
|
||||
add(f' "{node.node_id}" [label="{label}", {attrs}];')
|
||||
add("")
|
||||
|
||||
for edge in graph.edges:
|
||||
if edge.src not in top_ids or edge.dst not in top_ids:
|
||||
continue
|
||||
@@ -3336,9 +3380,11 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
dest="chain_graph",
|
||||
action="store_true",
|
||||
help="Chain-/Lageplan der groben Objekte (Kreisel je Bahn L/R, Strecke/"
|
||||
"Gefaellestrecke) nach %%SKEL_RESULTS%% schreiben, verbunden durch "
|
||||
"die bereits aufgeloesten Materialfluss-Kanten. Noch ohne Ein-/"
|
||||
"Ausgabestationen. Methode/Ausgabedatei je --graphtype.",
|
||||
"Gefaellestrecke, plus Beladung/Entladung/Eingang/Ausgang - die "
|
||||
"Anlage laeuft als Kreislauf, sie sind seine einzigen Material-"
|
||||
"quellen/-senken) nach %%SKEL_RESULTS%% schreiben, verbunden durch "
|
||||
"die bereits aufgeloesten Materialfluss-Kanten. Methode/Ausgabedatei "
|
||||
"je --graphtype.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--graphtype",
|
||||
|
||||
Reference in New Issue
Block a user