diff --git a/bin/set_einfuegepunkt.bat b/bin/set_einfuegepunkt.bat new file mode 100644 index 0000000..6fed057 --- /dev/null +++ b/bin/set_einfuegepunkt.bat @@ -0,0 +1,8 @@ +@echo off +REM ================================================================ +REM Setzt Einfuegepunkte fuer Omniflo DXF-Dateien +REM ================================================================ + +call "%~dp0setenv.bat" + +python "%DXFM_LIB%\set_einfuegepkt.py" %* diff --git a/lib/set_einfuegepkt.py b/lib/set_einfuegepkt.py new file mode 100644 index 0000000..6b9889d --- /dev/null +++ b/lib/set_einfuegepkt.py @@ -0,0 +1,246 @@ +""" +Setzt den Einfuegepunkt ($INSBASE) von Omniflo DXF-Dateien. + +Mit --boegen: Einfuegepunkt auf den Schnittpunkt der Tangenten +am Anfangs- und Endpunkt der Bogenkurve. + +Mit --weichen45 / --weichen90: Einfuegepunkt auf den Schnittpunkt +der laengsten vertikalen Linie und des Endpunktes der Bogenkurve. +""" + +import argparse +import json +import math +import os +import sys + +import ezdxf + + +def find_arc(doc): + """Findet die erste ARC-Entity im Modelspace.""" + msp = doc.modelspace() + for entity in msp: + if entity.dxftype() == "ARC": + return entity + return None + + +def tangent_intersection(arc): + """Berechnet den Schnittpunkt der Tangenten am Start- und Endpunkt des Bogens. + Bei 180-Grad-Boegen wird der Mittelpunkt zwischen Start und Ende verwendet. + """ + cx, cy, _ = arc.dxf.center + r = arc.dxf.radius + 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 + dy = p2y - p1y + t = (-dx * d2y + dy * d2x) / det + + ix = p1x + t * d1x + iy = p1y + t * d1y + return ix, iy + + +def find_longest_vertical_line(doc): + """Findet die laengste vertikale Linie im Modelspace und gibt deren x-Koordinate zurueck.""" + 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 + dx = abs(end[0] - start[0]) + if dx > 0.01: + continue + length = abs(end[1] - start[1]) + 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 + r = arc.dxf.radius + a_end = math.radians(arc.dxf.end_angle) + 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 + arc = find_arc(doc) + if arc is None: + return None + vx = vline.dxf.start[0] + _, ey = arc_endpoint(arc) + return vx, ey + + +def process_weichen(data_dir, json_path, results_dir, kurven_winkel): + """Verarbeitet Weichen-DXF-Dateien fuer den angegebenen KurvenWinkel.""" + with open(json_path, "r", encoding="utf-8") as f: + weichen = json.load(f) + + omniflo_dir = os.path.join(data_dir, "omniflo") + filtered = [w for w in weichen if w["KurvenWinkel"] == kurven_winkel] + + if not filtered: + print(f"Keine Weichen mit KurvenWinkel={kurven_winkel} gefunden.") + return + + for weiche in filtered: + sivasnr = str(weiche["Sivasnr"]) + dxf_path = os.path.join(omniflo_dir, f"{sivasnr}.dxf") + + if not os.path.exists(dxf_path): + print(f"WARNUNG: {dxf_path} nicht gefunden, ueberspringe.") + continue + + doc = ezdxf.readfile(dxf_path) + result = weichen_insertion_point(doc) + + if result is None: + print(f"WARNUNG: Kein ARC oder keine vertikale Linie in {sivasnr}.dxf, ueberspringe.") + continue + + ix, iy = result + old_base = doc.header.get("$INSBASE", (0, 0, 0)) + doc.header["$INSBASE"] = (ix, iy, 0) + + out_path = os.path.join(results_dir, f"{sivasnr}.dxf") + doc.saveas(out_path) + + print( + f"{sivasnr}: INSBASE ({old_base[0]:.2f}, {old_base[1]:.2f}) " + f"-> ({ix:.2f}, {iy:.2f}) [{weiche['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) + + 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") + + if not os.path.exists(dxf_path): + print(f"WARNUNG: {dxf_path} nicht gefunden, ueberspringe.") + continue + + doc = ezdxf.readfile(dxf_path) + arc = find_arc(doc) + + if arc is None: + print(f"WARNUNG: Kein ARC in {sivasnr}.dxf gefunden, ueberspringe.") + continue + + ix, iy = tangent_intersection(arc) + + old_base = doc.header.get("$INSBASE", (0, 0, 0)) + doc.header["$INSBASE"] = (ix, iy, 0) + + out_path = os.path.join(results_dir, f"{sivasnr}.dxf") + doc.saveas(out_path) + + print( + f"{sivasnr}: INSBASE ({old_base[0]:.2f}, {old_base[1]:.2f}) " + f"-> ({ix:.2f}, {iy:.2f}) [{bogen['ProfilTyp']}]" + ) + + print(f"\nErgebnisse in: {results_dir}") + + +def main(): + parser = argparse.ArgumentParser( + description="Setzt Einfuegepunkte fuer Omniflo DXF-Dateien" + ) + parser.add_argument( + "--boegen", + action="store_true", + help="Einfuegepunkt auf Tangentenschnittpunkt der Bogenkurve setzen", + ) + 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", + ) + args = parser.parse_args() + + if not args.boegen and not args.weichen45 and not args.weichen90: + parser.print_help() + sys.exit(1) + + data_dir = os.environ.get("DXFM_DATA") + results_dir = os.environ.get("DXFM_RESULTS") + + if not data_dir or not results_dir: + print("FEHLER: Umgebungsvariablen DXFM_DATA und DXFM_RESULTS muessen gesetzt sein.") + print("Bitte zuerst setenv.bat ausfuehren.") + sys.exit(1) + + 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) + + 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 __name__ == "__main__": + main()