From 530aa0ca02819758e9dbda7fc22bdda72a4def8b Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Tue, 12 May 2026 12:26:23 +0200 Subject: [PATCH] =?UTF-8?q?Doku=20zum=20Programm=20zum=20setzen=20des=20Ei?= =?UTF-8?q?nf=C3=BCgepunktes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/tools.md | 116 ++++++++++ lib/set_einfuegepkt.py | 510 ++++++++++++++++++++++++++++++++++------- 2 files changed, 545 insertions(+), 81 deletions(-) create mode 100644 doc/tools.md diff --git a/doc/tools.md b/doc/tools.md new file mode 100644 index 0000000..3aed0ad --- /dev/null +++ b/doc/tools.md @@ -0,0 +1,116 @@ +# set_einfuegepunkt + +Setzt den Einfuegepunkt (`$INSBASE`) von Omniflo DXF-Dateien anhand geometrischer Kriterien. + +## Aufruf + +``` +bin\set_einfuegepunkt.bat [--schalter] [--number SIVASNR] +``` + +Das Batch-Skript ruft `setenv.bat` auf und startet `lib/set_einfuegepkt.py` mit den uebergebenen Argumenten. + +## Datenquellen + +| Pfad | Inhalt | +|---|---| +| `data/json/omniflo_boegen.json` | Liste aller Boegen mit Sivasnr, ProfilTyp, Radius, KurvenWinkel etc. | +| `data/json/omniflo_weichen.json` | Liste aller Weichen mit Sivasnr, ProfilTyp, WeichenTyp, KurvenWinkel etc. | +| `data/omniflo/*.dxf` | Die DXF-Quelldateien (je Sivasnr eine Datei) | + +Die JSON-Dateien liefern die IDs (Sivasnr) und Metadaten zur Filterung. Die DXF-Dateien enthalten die Geometrie (Linien, Boegen), aus der der Einfuegepunkt berechnet wird. + +## Ablauf + +1. JSON-Datei einlesen und nach Schalter-Kriterien filtern +2. Fuer jede gefilterte Sivasnr die DXF-Datei aus `data/omniflo/` laden +3. Einfuegepunkt gemaess Typ-Kriterium geometrisch berechnen +4. `$INSBASE` im DXF-Header setzen und Datei nach `results/` speichern + +## Schalter + +### --boegen + +Alle Eintraege aus `omniflo_boegen.json` (27 Items, KurvenWinkel 22.5/45/67.5/90/180). + +**Kriterium:** Schnittpunkt der Tangenten am Anfangs- und Endpunkt der Bogenkurve. Bei 180-Grad-Boegen wird der Mittelpunkt zwischen Start und Ende verwendet. + +### --weichen45 + +Einzel-, Doppel- und Dreiwegeweichen mit KurvenWinkel 45 (32 Items). + +**Kriterium:** Schnittpunkt der laengsten vertikalen Linie mit der Tangente am oberen Bogenpunkt. Bei mehreren Boegen wird der mit dem hoechsten Y-Wert gewaehlt. + +### --weichen90 + +Einzel-, Doppel- und Dreiwegeweichen mit KurvenWinkel 90 (24 Items). + +**Kriterium:** Wie `--weichen45`. + +### --weichenkoerper + +Alle Weichen mit KurvenWinkel 22.5 (8 Items: Einzel-/Doppel-/Dreiwegeweiche). + +**Kriterium:** Schnittpunkt der laengsten Diagonale mit der laengsten vertikalen Linie (Konvergenzpunkt aller Linien). + +### --weichen-parallel + +Einzel-, Doppel- und Dreiwegeweichen mit KurvenWinkel 0, ohne Sternweiche (8 Items). + +**Kriterium:** Wie `--weichenkoerper`. + +### --delta + +Deltaweichen (1 Item). + +**Kriterium:** Mittelpunkt der laengsten horizontalen Linie. + +### --dreifachweiche + +Dreifachweichen (6 Items). + +**Kriterium:** Mittelpunkt der laengsten horizontalen Linie. + +### --sternweiche + +Sternweiche (1 Item). + +**Kriterium:** Zentrum, berechnet als Mittelpunkt der laengsten horizontalen Linie (entspricht dem Schnittpunkt der horizontalen und vertikalen Mittelachse). + +### --number SIVASNR + +Optionaler Filter: nur die angegebene 9-stellige Sivasnr verarbeiten. Kombinierbar mit jedem Typ-Schalter. + +### --show-omniflo + +Erzeugt eine Uebersichts-DXF (`results/omniflo_uebersicht.dxf`) mit allen Omniflo-Elementen. Die Elemente werden in Reihen nach Typ gruppiert angezeigt. Jedes Element erhaelt: +- Den Dateinamen als Beschriftung oberhalb +- Ein rotes Kreuz am Einfuegepunkt +- Eine blaue Linie vom Einfuegepunkt zum tiefsten Punkt des Elements + +## Abdeckung + +Alle 107 Items (27 Boegen + 80 Weichen) sind exklusiv genau einem Schalter zugeordnet. Keine Ueberlappungen, keine Luecken. + +## Uebersicht aller Schalter + +| Schalter | Items | WeichenTyp | KurvenWinkel | Einfuegepunkt-Kriterium | +|---|---|---|---|---| +| `--boegen` | 27 | - | 22.5, 45, 67.5, 90, 180 | Tangentenschnittpunkt am Bogen (180: Mittelpunkt) | +| `--weichen45` | 32 | Einzel-, Doppel-, Dreiwegeweiche | 45 | Tangente oberer Bogenpunkt x laengste Vertikale | +| `--weichen90` | 24 | Einzel-, Doppel-, Dreiwegeweiche | 90 | Tangente oberer Bogenpunkt x laengste Vertikale | +| `--weichenkoerper` | 8 | Einzel-, Doppel-, Dreiwegeweiche | 22.5 | Laengste Diagonale x laengste Vertikale | +| `--weichen-parallel` | 8 | Einzel-, Doppel-, Dreiwegeweiche | 0 | Laengste Diagonale x laengste Vertikale | +| `--dreifachweiche` | 6 | Dreifachweiche | 90 | Mittelpunkt laengste Horizontale | +| `--delta` | 1 | Deltaweiche | 90 | Mittelpunkt laengste Horizontale | +| `--sternweiche` | 1 | Sternweiche | 0 | Zentrum (Mittelpunkt laengste Horizontale) | +| **Gesamt** | **107** | | | | + +## Umgebungsvariablen + +Werden durch `bin/setenv.bat` gesetzt: + +| Variable | Verwendung | +|---|---| +| `DXFM_DATA` | Pfad zu `data/` (JSON + DXF Quelldateien) | +| `DXFM_RESULTS` | Pfad zu `results/` (Ausgabe) | diff --git a/lib/set_einfuegepkt.py b/lib/set_einfuegepkt.py index 6b9889d..8ed4147 100644 --- a/lib/set_einfuegepkt.py +++ b/lib/set_einfuegepkt.py @@ -1,11 +1,26 @@ """ Setzt den Einfuegepunkt ($INSBASE) von Omniflo DXF-Dateien. -Mit --boegen: Einfuegepunkt auf den Schnittpunkt der Tangenten -am Anfangs- und Endpunkt der Bogenkurve. +Kriterien je Typ: -Mit --weichen45 / --weichen90: Einfuegepunkt auf den Schnittpunkt -der laengsten vertikalen Linie und des Endpunktes der Bogenkurve. + --boegen Schnittpunkt der Tangenten am Anfangs- und Endpunkt + der Bogenkurve (bei 180-Grad: Mittelpunkt). + + --weichen45 Schnittpunkt der laengsten vertikalen Linie mit einer + --weichen90 Horizontalen durch den Endpunkt der Bogenkurve. + + --weichenkoerper Schnittpunkt der laengsten Diagonale mit der laengsten + vertikalen Linie (Konvergenzpunkt aller Linien). + + --weichen-parallel Wie --weichenkoerper: Schnittpunkt der laengsten + Diagonale mit der laengsten vertikalen Linie. + + --delta Mittelpunkt der laengsten horizontalen Linie. + --dreifachweiche Mittelpunkt der laengsten horizontalen Linie. + --sternweiche Zentrum: Mittelpunkt der laengsten horizontalen Linie. + + --number SIVASNR Nur diese eine 9-stellige Sivasnr verarbeiten. + --show-omniflo Uebersichts-DXF mit allen Elementen erzeugen. """ import argparse @@ -15,8 +30,14 @@ import os import sys import ezdxf +from ezdxf import bbox as ezdxf_bbox +from ezdxf.addons.importer import Importer +# --------------------------------------------------------------------------- +# Geometrie-Hilfsfunktionen +# --------------------------------------------------------------------------- + def find_arc(doc): """Findet die erste ARC-Entity im Modelspace.""" msp = doc.modelspace() @@ -35,23 +56,19 @@ def tangent_intersection(arc): a_start = math.radians(arc.dxf.start_angle) a_end = math.radians(arc.dxf.end_angle) - # Start- und Endpunkt p1x = cx + r * math.cos(a_start) p1y = cy + r * math.sin(a_start) p2x = cx + r * math.cos(a_end) p2y = cy + r * math.sin(a_end) - # Tangentenrichtungen (senkrecht zum Radius) d1x = -math.sin(a_start) d1y = math.cos(a_start) d2x = -math.sin(a_end) d2y = math.cos(a_end) - # Determinante det = d1x * (-d2y) - (-d2x) * d1y if abs(det) < 1e-9: - # Tangenten parallel (z.B. 180-Grad-Bogen) -> Mittelpunkt return (p1x + p2x) / 2, (p1y + p2y) / 2 dx = p2x - p1x @@ -64,7 +81,7 @@ def tangent_intersection(arc): def find_longest_vertical_line(doc): - """Findet die laengste vertikale Linie im Modelspace und gibt deren x-Koordinate zurueck.""" + """Findet die laengste vertikale Linie im Modelspace.""" msp = doc.modelspace() longest = None longest_len = 0 @@ -83,6 +100,26 @@ def find_longest_vertical_line(doc): return longest +def find_longest_horizontal_line(doc): + """Findet die laengste horizontale Linie im Modelspace.""" + msp = doc.modelspace() + longest = None + longest_len = 0 + for entity in msp: + if entity.dxftype() != "LINE": + continue + start = entity.dxf.start + end = entity.dxf.end + dy = abs(end[1] - start[1]) + if dy > 0.01: + continue + length = abs(end[0] - start[0]) + if length > longest_len: + longest_len = length + longest = entity + return longest + + def arc_endpoint(arc): """Gibt den Endpunkt (end_angle) des Bogens zurueck.""" cx, cy, _ = arc.dxf.center @@ -91,36 +128,179 @@ def arc_endpoint(arc): return cx + r * math.cos(a_end), cy + r * math.sin(a_end) -def weichen_insertion_point(doc): - """Berechnet den Einfuegepunkt fuer eine Weiche: - Schnittpunkt der laengsten vertikalen Linie mit einer Horizontalen - durch den Endpunkt der Bogenkurve. - """ - vline = find_longest_vertical_line(doc) - if vline is None: - return None +# --------------------------------------------------------------------------- +# Einfuegepunkt-Berechnungen (je Typ) +# --------------------------------------------------------------------------- + +def boegen_insertion_point(doc): + """Tangentenschnittpunkt der Bogenkurve.""" arc = find_arc(doc) if arc is None: return None + return tangent_intersection(arc) + + +def weichen_insertion_point(doc): + """Schnittpunkt der laengsten vertikalen Linie mit der Tangente am oberen Arc-Endpunkt.""" + vline = find_longest_vertical_line(doc) + if vline is None: + return None vx = vline.dxf.start[0] - _, ey = arc_endpoint(arc) - return vx, ey + + # Alle Arcs sammeln, oberen Punkt (max Y aus Start/Ende) waehlen + best_angle = None + best_y = -float('inf') + best_arc = None + for entity in doc.modelspace(): + if entity.dxftype() != "ARC": + continue + cx, cy, _ = entity.dxf.center + r = entity.dxf.radius + for angle_deg in (entity.dxf.start_angle, entity.dxf.end_angle): + a = math.radians(angle_deg) + py = cy + r * math.sin(a) + if py > best_y: + best_y = py + best_angle = a + best_arc = entity + + if best_arc is None: + return None + + cx, cy, _ = best_arc.dxf.center + r = best_arc.dxf.radius + + ex = cx + r * math.cos(best_angle) + ey = cy + r * math.sin(best_angle) + tdx = -math.sin(best_angle) + tdy = math.cos(best_angle) + + if abs(tdx) < 1e-9: + return vx, ey + t = (vx - ex) / tdx + return vx, ey + t * tdy -def process_weichen(data_dir, json_path, results_dir, kurven_winkel): - """Verarbeitet Weichen-DXF-Dateien fuer den angegebenen KurvenWinkel.""" +def weichenkoerper_insertion_point(doc): + """Schnittpunkt der laengsten Diagonale mit der vertikalen Linie.""" + vline = find_longest_vertical_line(doc) + if vline is None: + return None + vx = vline.dxf.start[0] + + best_point = None + best_len = 0 + for entity in doc.modelspace(): + if entity.dxftype() != "LINE": + continue + s = entity.dxf.start + e = entity.dxf.end + dx = abs(e[0] - s[0]) + dy = abs(e[1] - s[1]) + if dx < 0.01 or dy < 0.01: + continue + length = (dx ** 2 + dy ** 2) ** 0.5 + if abs(s[0] - vx) < 0.01 and length > best_len: + best_point = (s[0], s[1]) + best_len = length + if abs(e[0] - vx) < 0.01 and length > best_len: + best_point = (e[0], e[1]) + best_len = length + return best_point + + +def horizontal_midpoint_insertion(doc): + """Mittelpunkt der laengsten horizontalen Linie.""" + hline = find_longest_horizontal_line(doc) + if hline is None: + return None + sx = hline.dxf.start[0] + ex = hline.dxf.end[0] + y = hline.dxf.start[1] + return (sx + ex) / 2, y + + +# --------------------------------------------------------------------------- +# Schalter-Definitionen: (Label, JSON-Datei, Filter, Einfuegepunkt-Funktion) +# --------------------------------------------------------------------------- + +SWITCH_DEFS = { + "boegen": ( + "Boegen", + "omniflo_boegen.json", + lambda item: True, + boegen_insertion_point, + ), + "weichen45": ( + "Weichen 45 Grad", + "omniflo_weichen.json", + lambda item: item["KurvenWinkel"] == 45 + and item["WeichenTyp"] not in ("Dreifachweiche", "Deltaweiche"), + weichen_insertion_point, + ), + "weichen90": ( + "Weichen 90 Grad", + "omniflo_weichen.json", + lambda item: item["KurvenWinkel"] == 90 + and item["WeichenTyp"] not in ("Dreifachweiche", "Deltaweiche"), + weichen_insertion_point, + ), + "weichenkoerper": ( + "Weichenkoerper", + "omniflo_weichen.json", + lambda item: item["KurvenWinkel"] == 22.5, + weichenkoerper_insertion_point, + ), + "weichen_parallel": ( + "Weichen Parallel", + "omniflo_weichen.json", + lambda item: item["KurvenWinkel"] == 0 and item["WeichenTyp"] != "Sternweiche", + weichenkoerper_insertion_point, + ), + "sternweiche": ( + "Sternweiche", + "omniflo_weichen.json", + lambda item: item["WeichenTyp"] == "Sternweiche", + horizontal_midpoint_insertion, + ), + "delta": ( + "Deltaweichen", + "omniflo_weichen.json", + lambda item: item["WeichenTyp"] == "Deltaweiche", + horizontal_midpoint_insertion, + ), + "dreifachweiche": ( + "Dreifachweichen", + "omniflo_weichen.json", + lambda item: item["WeichenTyp"] == "Dreifachweiche", + horizontal_midpoint_insertion, + ), +} + + +def process_dxf(data_dir, label, json_file, filter_func, insertion_func, + results_dir, number=None): + """Generische Verarbeitung: filtert IDs aus JSON, berechnet Einfuegepunkt, speichert.""" + json_path = os.path.join(data_dir, "json", json_file) + if not os.path.exists(json_path): + print(f"FEHLER: {json_path} nicht gefunden.") + sys.exit(1) + with open(json_path, "r", encoding="utf-8") as f: - weichen = json.load(f) + items = json.load(f) omniflo_dir = os.path.join(data_dir, "omniflo") - filtered = [w for w in weichen if w["KurvenWinkel"] == kurven_winkel] + filtered = [item for item in items if filter_func(item)] + if number: + filtered = [item for item in filtered if str(item["Sivasnr"]) == str(number)] if not filtered: - print(f"Keine Weichen mit KurvenWinkel={kurven_winkel} gefunden.") + print(f"Keine Elemente fuer '{label}' gefunden.") return - for weiche in filtered: - sivasnr = str(weiche["Sivasnr"]) + print(f"=== {label} ===") + for item in filtered: + sivasnr = str(item["Sivasnr"]) dxf_path = os.path.join(omniflo_dir, f"{sivasnr}.dxf") if not os.path.exists(dxf_path): @@ -128,10 +308,10 @@ def process_weichen(data_dir, json_path, results_dir, kurven_winkel): continue doc = ezdxf.readfile(dxf_path) - result = weichen_insertion_point(doc) + result = insertion_func(doc) if result is None: - print(f"WARNUNG: Kein ARC oder keine vertikale Linie in {sivasnr}.dxf, ueberspringe.") + print(f"WARNUNG: Kein Einfuegepunkt in {sivasnr}.dxf bestimmbar, ueberspringe.") continue ix, iy = result @@ -143,72 +323,250 @@ def process_weichen(data_dir, json_path, results_dir, kurven_winkel): print( f"{sivasnr}: INSBASE ({old_base[0]:.2f}, {old_base[1]:.2f}) " - f"-> ({ix:.2f}, {iy:.2f}) [{weiche['ProfilTyp']}]" + f"-> ({ix:.2f}, {iy:.2f}) [{item['ProfilTyp']}]" ) print(f"\nErgebnisse in: {results_dir}") -def process_boegen(data_dir, json_path, results_dir): - """Verarbeitet alle Boegen-DXF-Dateien.""" - with open(json_path, "r", encoding="utf-8") as f: - boegen = json.load(f) +# --------------------------------------------------------------------------- +# show-omniflo +# --------------------------------------------------------------------------- +def find_min_y_point(doc): + """Findet den Punkt mit dem geringsten Y-Wert im Modelspace.""" + min_y = float('inf') + min_point = None + for e in doc.modelspace(): + points = [] + if e.dxftype() == 'LINE': + points = [(e.dxf.start[0], e.dxf.start[1]), + (e.dxf.end[0], e.dxf.end[1])] + elif e.dxftype() == 'ARC': + cx, cy = e.dxf.center[0], e.dxf.center[1] + r = e.dxf.radius + a_s = math.radians(e.dxf.start_angle) + a_e = math.radians(e.dxf.end_angle) + points = [ + (cx + r * math.cos(a_s), cy + r * math.sin(a_s)), + (cx + r * math.cos(a_e), cy + r * math.sin(a_e)), + ] + for p in points: + if p[1] < min_y: + min_y = p[1] + min_point = p + return min_point + + +ROW_GROUPS = [ + ("Boegen 22.5", "boegen", + lambda b: b["KurvenWinkel"] == 22.5), + ("Boegen 45", "boegen", + lambda b: b["KurvenWinkel"] == 45), + ("Boegen 67.5", "boegen", + lambda b: b["KurvenWinkel"] == 67.5), + ("Boegen 90", "boegen", + lambda b: b["KurvenWinkel"] == 90), + ("Boegen 180", "boegen", + lambda b: b["KurvenWinkel"] == 180), + ("Weichenkoerper Einzel", "weichen", + lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 22.5), + ("Weichenkoerper Doppel", "weichen", + lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 22.5), + ("Weichenkoerper Dreiwege", "weichen", + lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 22.5), + ("Einzelweiche 45", "weichen", + lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 45), + ("Einzelweiche 90", "weichen", + lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 90), + ("Einzelweiche Parallel", "weichen", + lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 0), + ("Doppelweiche 45", "weichen", + lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 45), + ("Doppelweiche 90", "weichen", + lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 90), + ("Doppelweiche Parallel", "weichen", + lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 0), + ("Dreiwegeweiche 45", "weichen", + lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 45), + ("Dreiwegeweiche 90", "weichen", + lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 90), + ("Dreiwegeweiche Parallel", "weichen", + lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 0), + ("Dreifachweiche", "weichen", + lambda w: w["WeichenTyp"] == "Dreifachweiche"), + ("Deltaweiche", "weichen", + lambda w: w["WeichenTyp"] == "Deltaweiche"), + ("Sternweiche", "weichen", + lambda w: w["WeichenTyp"] == "Sternweiche"), +] + +PADDING_X = 200 +PADDING_Y = 400 +TEXT_HEIGHT = 30 +CROSS_SIZE = 40 +TEXT_MARGIN = 20 +ROW_LABEL_WIDTH = 600 + + +def show_omniflo(data_dir, results_dir): + """Erzeugt eine Uebersichts-DXF mit allen Omniflo-Elementen in Reihen.""" + boegen_path = os.path.join(data_dir, "json", "omniflo_boegen.json") + weichen_path = os.path.join(data_dir, "json", "omniflo_weichen.json") + + with open(boegen_path, "r", encoding="utf-8") as f: + boegen_data = json.load(f) + with open(weichen_path, "r", encoding="utf-8") as f: + weichen_data = json.load(f) + + sources = {"boegen": boegen_data, "weichen": weichen_data} omniflo_dir = os.path.join(data_dir, "omniflo") - for bogen in boegen: - sivasnr = str(bogen["Sivasnr"]) - dxf_path = os.path.join(omniflo_dir, f"{sivasnr}.dxf") + target = ezdxf.new(dxfversion='R2010') + target_msp = target.modelspace() + target.layers.add('ANNOTATION', color=7) + target.layers.add('INSPOINT', color=1) + target.layers.add('INSLINE', color=5) + target.layers.add('ROW_LABEL', color=3) - if not os.path.exists(dxf_path): - print(f"WARNUNG: {dxf_path} nicht gefunden, ueberspringe.") + cursor_y = 0.0 + block_counter = 0 + + for label, source_key, filter_func in ROW_GROUPS: + items = [item for item in sources[source_key] if filter_func(item)] + if not items: continue - doc = ezdxf.readfile(dxf_path) - arc = find_arc(doc) + row_elements = [] + for item in items: + sivasnr = str(item["Sivasnr"]) + dxf_path_result = os.path.join(results_dir, f"{sivasnr}.dxf") + dxf_path_orig = os.path.join(omniflo_dir, f"{sivasnr}.dxf") + dxf_path = dxf_path_result if os.path.exists(dxf_path_result) else dxf_path_orig - if arc is None: - print(f"WARNUNG: Kein ARC in {sivasnr}.dxf gefunden, ueberspringe.") + if not os.path.exists(dxf_path): + print(f" WARNUNG: {sivasnr}.dxf nicht gefunden, ueberspringe.") + continue + + source_doc = ezdxf.readfile(dxf_path) + bb = ezdxf_bbox.extents(source_doc.modelspace()) + if not bb.has_data: + continue + + min_y_pt = find_min_y_point(source_doc) + insbase = source_doc.header.get("$INSBASE", (0, 0, 0)) + + row_elements.append({ + 'sivasnr': sivasnr, + 'source': source_doc, + 'extmin': bb.extmin, + 'extmax': bb.extmax, + 'width': bb.extmax[0] - bb.extmin[0], + 'height': bb.extmax[1] - bb.extmin[1], + 'insbase': insbase, + 'min_y_point': min_y_pt, + }) + + if not row_elements: continue - ix, iy = tangent_intersection(arc) + max_height = max(e['height'] for e in row_elements) - old_base = doc.header.get("$INSBASE", (0, 0, 0)) - doc.header["$INSBASE"] = (ix, iy, 0) + label_y = cursor_y + max_height / 2 + target_msp.add_mtext( + label, + dxfattribs={ + 'layer': 'ROW_LABEL', + 'char_height': TEXT_HEIGHT * 1.2, + } + ).set_location(insert=(-ROW_LABEL_WIDTH, label_y)) - out_path = os.path.join(results_dir, f"{sivasnr}.dxf") - doc.saveas(out_path) + cursor_x = 0.0 + for elem in row_elements: + block_name = f"BLK_{block_counter}" + block_counter += 1 - print( - f"{sivasnr}: INSBASE ({old_base[0]:.2f}, {old_base[1]:.2f}) " - f"-> ({ix:.2f}, {iy:.2f}) [{bogen['ProfilTyp']}]" - ) + importer = Importer(elem['source'], target) + importer.import_tables() + blk = target.blocks.new(name=block_name) + for entity in elem['source'].modelspace(): + importer.import_entity(entity, blk) + importer.finalize() - print(f"\nErgebnisse in: {results_dir}") + offset_x = cursor_x - elem['extmin'][0] + offset_y = cursor_y - elem['extmin'][1] + target_msp.add_blockref(block_name, insert=(offset_x, offset_y)) + ipx = elem['insbase'][0] + offset_x + ipy = elem['insbase'][1] + offset_y + + target_msp.add_line( + (ipx - CROSS_SIZE, ipy), + (ipx + CROSS_SIZE, ipy), + dxfattribs={'layer': 'INSPOINT', 'color': 1} + ) + target_msp.add_line( + (ipx, ipy - CROSS_SIZE), + (ipx, ipy + CROSS_SIZE), + dxfattribs={'layer': 'INSPOINT', 'color': 1} + ) + + if elem['min_y_point']: + low_x = elem['min_y_point'][0] + offset_x + low_y = elem['min_y_point'][1] + offset_y + target_msp.add_line( + (ipx, ipy), + (low_x, low_y), + dxfattribs={'layer': 'INSLINE', 'color': 5} + ) + + text_x = cursor_x + text_y = cursor_y + elem['height'] + TEXT_MARGIN + target_msp.add_mtext( + elem['sivasnr'], + dxfattribs={ + 'layer': 'ANNOTATION', + 'char_height': TEXT_HEIGHT, + } + ).set_location(insert=(text_x, text_y)) + + cursor_x += elem['width'] + PADDING_X + + cursor_y -= max_height + TEXT_HEIGHT + TEXT_MARGIN * 2 + PADDING_Y + + out_path = os.path.join(results_dir, "omniflo_uebersicht.dxf") + target.saveas(out_path) + print(f"Uebersicht gespeichert: {out_path}") + print(f" {block_counter} Elemente in {sum(1 for l, s, f in ROW_GROUPS if any(f(i) for i in sources[s]))} Reihen") + + +# --------------------------------------------------------------------------- +# main +# --------------------------------------------------------------------------- def main(): parser = argparse.ArgumentParser( description="Setzt Einfuegepunkte fuer Omniflo DXF-Dateien" ) + for key, (label, _, _, _) in SWITCH_DEFS.items(): + arg_name = f"--{key.replace('_', '-')}" + parser.add_argument(arg_name, action="store_true", help=f"Einfuegepunkt fuer {label} setzen") parser.add_argument( - "--boegen", - action="store_true", - help="Einfuegepunkt auf Tangentenschnittpunkt der Bogenkurve setzen", + "--number", type=int, + help="Nur diese 9-stellige Sivasnr verarbeiten", ) parser.add_argument( - "--weichen45", - action="store_true", - help="Einfuegepunkt fuer 45-Grad-Weichen setzen", - ) - parser.add_argument( - "--weichen90", - action="store_true", - help="Einfuegepunkt fuer 90-Grad-Weichen setzen", + "--show-omniflo", action="store_true", + help="Uebersichts-DXF mit allen Omniflo-Elementen erzeugen", ) args = parser.parse_args() - if not args.boegen and not args.weichen45 and not args.weichen90: + if args.number and len(str(args.number)) != 9: + print("FEHLER: --number muss eine 9-stellige Ganzzahl sein.") + sys.exit(1) + + active = [key for key in SWITCH_DEFS if getattr(args, key)] + if not active and not args.show_omniflo: parser.print_help() sys.exit(1) @@ -222,24 +580,14 @@ def main(): os.makedirs(results_dir, exist_ok=True) - if args.boegen: - json_path = os.path.join(data_dir, "json", "omniflo_boegen.json") - if not os.path.exists(json_path): - print(f"FEHLER: {json_path} nicht gefunden.") - sys.exit(1) - process_boegen(data_dir, json_path, results_dir) + for key in active: + label, json_file, filter_func, insertion_func = SWITCH_DEFS[key] + process_dxf(data_dir, label, json_file, filter_func, insertion_func, + results_dir, args.number) - if args.weichen45 or args.weichen90: - json_path = os.path.join(data_dir, "json", "omniflo_weichen.json") - if not os.path.exists(json_path): - print(f"FEHLER: {json_path} nicht gefunden.") - sys.exit(1) - if args.weichen45: - print("=== Weichen 45 Grad ===") - process_weichen(data_dir, json_path, results_dir, 45) - if args.weichen90: - print("=== Weichen 90 Grad ===") - process_weichen(data_dir, json_path, results_dir, 90) + if args.show_omniflo: + print("=== Omniflo Uebersicht ===") + show_omniflo(data_dir, results_dir) if __name__ == "__main__":