material_flow: --show-bbs [flow|all] mit Legende und Tooltips
- neuer Wert fuer --show-bbs: 'flow' (Default, auch ohne Wert) blendet die VF/GF-Einzelbausteine (Bogen, Streckenelement, Motor-/Umlenkstation) aus, 'all' zeichnet jede CSV-Zeile - eigene Farben fuer die Einzelbausteine, Legende mit Anzahl je Objektart, Tooltip je Rechteck (Zeile, TeileId, TeileArt, Bezeichnung) - genutzt von dxfmakros/bin/show_exportcsv.bat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0477f05180
commit
a1bba2b20d
+76
-5
@@ -2279,9 +2279,27 @@ BBS_STYLES: dict[str, dict[str, str]] = {
|
||||
"Einschleus": dict(fill="#cfe2f3", stroke="#3d85c6", text="#1155cc"),
|
||||
"Ausschleus": dict(fill="#f9cb9c", stroke="#e69138", text="#b45f06"),
|
||||
"Weiche": dict(fill="#ead1dc", stroke="#a64d79", text="#741b47"),
|
||||
# Einzelbausteine einer VF/GF-Kette (nur mit --show-bbs all, siehe
|
||||
# BBS_DETAIL_KINDS) - sie liegen innerhalb ihrer Strecke/Gefaellestrecke
|
||||
# und brauchen deshalb kraeftigere Farben, um darauf sichtbar zu bleiben.
|
||||
"Bogen": dict(fill="#b6d7a8", stroke="#38761d", text="#274e13"),
|
||||
"Streckenelement": dict(fill="#a2c4c9", stroke="#134f5c", text="#0c343d"),
|
||||
"Motorstation": dict(fill="#ea9999", stroke="#990000", text="#660000"),
|
||||
"Umlenkstation": dict(fill="#b4a7d6", stroke="#351c75", text="#20124d"),
|
||||
}
|
||||
BBS_DEFAULT_STYLE = dict(fill="#f2f2f2", stroke="#808080", text="#404040")
|
||||
|
||||
# Einzelbausteine der VF_n/GF_n-Ketten, die dxfmakros/export_csv.py seit
|
||||
# Sept. 2026 als eigene Proxy-Zeilen exportiert. Sie ueberlagern die Box ihrer
|
||||
# Strecke/Gefaellestrecke vollstaendig; --show-bbs (= flow) blendet sie
|
||||
# deshalb aus, --show-bbs all zeichnet sie mit.
|
||||
BBS_DETAIL_KINDS = ("Bogen", "Streckenelement", "Motorstation", "Umlenkstation")
|
||||
BBS_MODES = ("flow", "all")
|
||||
|
||||
# Legende (--show-bbs): Kaestchen- und Schriftgroesse in mm.
|
||||
BBS_LEGEND_BOX_MM = 250.0
|
||||
BBS_LEGEND_FONT_MM = 180.0
|
||||
|
||||
# Rand um die Anlage herum und minimale Schriftgroesse, jeweils in mm.
|
||||
BBS_MARGIN_MM = 500.0
|
||||
BBS_MIN_FONT_MM = 40.0
|
||||
@@ -2312,6 +2330,7 @@ def render_bbs_svg(
|
||||
source: str,
|
||||
warnings: list[str],
|
||||
show_kx: bool = False,
|
||||
mode: str = "flow",
|
||||
) -> str:
|
||||
"""
|
||||
2D-Draufsicht der ganzen Szene: ein achsparalleles Rechteck je CSV-Zeile,
|
||||
@@ -2339,9 +2358,19 @@ def render_bbs_svg(
|
||||
entscheid fuer L/R), siehe _kreisel_lane_rects().
|
||||
Die K1-K4-Anschlusspunkte (real am Kreisel als Ganzem haengend) werden
|
||||
trotzdem nur einmal gezeichnet, nicht je Bahn doppelt.
|
||||
|
||||
mode 'flow' (Default) laesst die Einzelbausteine der VF/GF-Ketten
|
||||
(BBS_DETAIL_KINDS) weg, 'all' zeichnet wirklich jede Zeile.
|
||||
Rechts oben steht in beiden Faellen eine Legende (Farbe, Objektart,
|
||||
Anzahl); jedes Rechteck traegt einen Tooltip mit Zeile/TeileId/
|
||||
TeileArt.
|
||||
"""
|
||||
if mode not in BBS_MODES:
|
||||
raise ValueError(f"unbekannter Modus '{mode}' (erlaubt: {', '.join(BBS_MODES)})")
|
||||
items: list[tuple[Element, float, float, float, float, str | None]] = []
|
||||
for element in elements:
|
||||
if mode == "flow" and element.kind in BBS_DETAIL_KINDS:
|
||||
continue
|
||||
if element.position is None or element.bbox is None:
|
||||
warnings.append(
|
||||
f"{element.describe()}: keine Position/Boundingbox - in --show-bbs nicht gezeichnet"
|
||||
@@ -2367,7 +2396,24 @@ def render_bbs_svg(
|
||||
max_x = max(item[1] + item[3] / 2 for item in items) + BBS_MARGIN_MM
|
||||
min_y = min(item[2] - item[4] / 2 for item in items) - BBS_MARGIN_MM
|
||||
max_y = max(item[2] + item[4] / 2 for item in items) + BBS_MARGIN_MM
|
||||
width, height = max_x - min_x, max_y - min_y
|
||||
plant_w = max_x - min_x
|
||||
|
||||
# Legende als eigene Spalte rechts neben der Anlage (Objektart + Anzahl der
|
||||
# gezeichneten Zeilen; ein Kreisel zaehlt einmal, nicht je Bahn).
|
||||
counts: dict[str, int] = {}
|
||||
counted: set[str] = set()
|
||||
for element, *_rest in items:
|
||||
if element.teile_id not in counted:
|
||||
counted.add(element.teile_id)
|
||||
counts[element.kind] = counts.get(element.kind, 0) + 1
|
||||
legend = [(kind, f"{kind} ({count})") for kind, count in sorted(counts.items())]
|
||||
legend_title = f"{len(counted)} Zeilen ({mode})"
|
||||
row_h = BBS_LEGEND_BOX_MM * 1.5
|
||||
text_w = max(len(text) for _, text in legend + [("", legend_title)]) * BBS_LEGEND_FONT_MM * 0.6
|
||||
legend_w = BBS_LEGEND_BOX_MM * 1.5 + text_w + BBS_MARGIN_MM
|
||||
legend_h = (len(legend) + 1) * row_h + BBS_MARGIN_MM
|
||||
width = plant_w + legend_w
|
||||
height = max(max_y - min_y, legend_h)
|
||||
|
||||
# Groesste Objekte zuerst zeichnen, damit kleine Anbauteile (Separator,
|
||||
# Scanner, ...) sichtbar obenauf liegen statt unter einer Strecke/einem
|
||||
@@ -2391,8 +2437,10 @@ def render_bbs_svg(
|
||||
y = max_y - (py + bd / 2)
|
||||
name = f"{element.name or element.teile_id} Bahn {lane}" if lane else (element.name or element.teile_id)
|
||||
font_size = max(BBS_MIN_FONT_MM, min(bw, bd) * 0.28)
|
||||
tooltip = _xml_escape(f"Zeile {element.row} | {element.teile_id} | {element.teile_art} | {name}")
|
||||
add(f' <rect x="{x:.2f}" y="{y:.2f}" width="{bw:.2f}" height="{bd:.2f}" '
|
||||
f'fill="{style["fill"]}" fill-opacity="0.85" stroke="{style["stroke"]}" stroke-width="6"/>')
|
||||
f'fill="{style["fill"]}" fill-opacity="0.85" stroke="{style["stroke"]}" stroke-width="6">'
|
||||
f'<title>{tooltip}</title></rect>')
|
||||
add(f' <text x="{x + bw / 2:.2f}" y="{y + bd / 2:.2f}" '
|
||||
f'font-family="Segoe UI, sans-serif" font-size="{font_size:.1f}" fill="{style["text"]}" '
|
||||
f'text-anchor="middle" dominant-baseline="middle">{_xml_escape(name)}</text>')
|
||||
@@ -2422,6 +2470,22 @@ def render_bbs_svg(
|
||||
f'font-family="Segoe UI, sans-serif" font-size="{KX_FONT_MM:.1f}" '
|
||||
f'fill="{KX_CROSS_COLOR}" dominant-baseline="middle">{label}</text>')
|
||||
|
||||
# Legende zuletzt, rechts neben der Anlage.
|
||||
lx = plant_w + BBS_MARGIN_MM / 2
|
||||
ly = BBS_MARGIN_MM / 2
|
||||
add(f' <text x="{lx:.2f}" y="{ly + BBS_LEGEND_BOX_MM / 2:.2f}" font-family="Segoe UI, sans-serif" '
|
||||
f'font-size="{BBS_LEGEND_FONT_MM:.1f}" font-weight="bold" fill="#000000" '
|
||||
f'dominant-baseline="middle">{_xml_escape(legend_title)}</text>')
|
||||
for index, (kind, text) in enumerate(legend, start=1):
|
||||
style = BBS_STYLES.get(kind, BBS_DEFAULT_STYLE)
|
||||
ry = ly + index * row_h
|
||||
add(f' <rect x="{lx:.2f}" y="{ry:.2f}" width="{BBS_LEGEND_BOX_MM:.2f}" '
|
||||
f'height="{BBS_LEGEND_BOX_MM:.2f}" fill="{style["fill"]}" stroke="{style["stroke"]}" '
|
||||
f'stroke-width="6"/>')
|
||||
add(f' <text x="{lx + BBS_LEGEND_BOX_MM * 1.5:.2f}" y="{ry + BBS_LEGEND_BOX_MM / 2:.2f}" '
|
||||
f'font-family="Segoe UI, sans-serif" font-size="{BBS_LEGEND_FONT_MM:.1f}" '
|
||||
f'fill="#000000" dominant-baseline="middle">{_xml_escape(text)}</text>')
|
||||
|
||||
add("</svg>")
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
@@ -3306,11 +3370,17 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
parser.add_argument(
|
||||
"--show-bbs",
|
||||
dest="show_bbs",
|
||||
action="store_true",
|
||||
nargs="?",
|
||||
const="flow",
|
||||
default=None,
|
||||
choices=BBS_MODES,
|
||||
help="2D-Draufsicht der ganzen Szene als <name>_bbs.svg nach %%SKEL_RESULTS%% "
|
||||
"schreiben: ein achsparalleles Rechteck je CSV-Zeile aus 'Position' "
|
||||
"(Mittelpunkt) und 'Boundingbox' (Breite/Tiefe), mit der Bezeichnung "
|
||||
"im Zentrum, skaliert auf DIN A3. Braucht kein Graphviz.",
|
||||
"im Zentrum, Legende und Tooltip je Rechteck, skaliert auf DIN A3. "
|
||||
"Braucht kein Graphviz. 'flow' (Default, auch ohne Wert) laesst die "
|
||||
"Einzelbausteine der VF/GF-Ketten (Bogen, Streckenelement, Motor-/"
|
||||
"Umlenkstation) weg, 'all' zeichnet jede Zeile.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--show-kx",
|
||||
@@ -3402,7 +3472,8 @@ def main(argv: list[str] | None = None) -> int:
|
||||
bbs_target = results / f"{stem}_bbs.svg"
|
||||
try:
|
||||
bbs_target.write_text(
|
||||
render_bbs_svg(elements, csv_file.name, warnings, show_kx=args.show_kx),
|
||||
render_bbs_svg(elements, csv_file.name, warnings, show_kx=args.show_kx,
|
||||
mode=args.show_bbs),
|
||||
encoding="utf-8",
|
||||
)
|
||||
except ValueError as exc:
|
||||
|
||||
Reference in New Issue
Block a user