""" placeblocks.py Erzeugt DXF-Elemente aus einer RuleDesigner-CSV. """ import os import sys import csv import json import re import argparse import configparser import ezdxf from ezdxf import units from ezdxf.entities import Line from ezdxf.math import Matrix44 from pathlib import Path import math from utils import check_environment_var, setup_logger # --------------------------------------------------------- CFG-Leser für shapes.cfg def get_shape_cfg(teileart, cfg_path, logger=None): parser = configparser.ConfigParser() try: with open(cfg_path, encoding='utf-8') as f: parser.read_file(f) except Exception as e: msg = f"Fehler beim Lesen der Config-Datei {cfg_path}: {e}" if logger: logger.error(msg) else: print(msg) return [] section = teileart if section not in parser: return [] # Blöcke items = parser.get(section, "items", fallback="").replace('"', '').split(",") blocks = [item.strip() for item in items if item.strip()] symbols = [] for i, name in enumerate(blocks): # Offset offset_key = f"offset_symb{i+1}" offset_str = parser.get(section, offset_key, fallback="0,0") try: ox, oy = [float(x) for x in offset_str.split(",")] except Exception: ox, oy = 0.0, 0.0 # Rotation rot_key = f"rot_symb{i+1}" rot_str = parser.get(section, rot_key, fallback="0.0") try: rot = float(rot_str) except Exception: rot = 0.0 symbols.append({ "name": name, "offset": (ox, oy), "rotation": rot }) return symbols # --------------------------------------------------------- Konstante Parameter ATTR_TAG = "TeileId" # Attributtag im Block RADIUS = 400 # Radius der Kreiselkreise (mm) # --------------------------------------------------------- Hilfsfunktionen def extract_coords(planquadrat: str) -> tuple[float, float]: """Extrahiert X/Y Koordinaten aus PlanquadratString.""" m = re.search(r"X:(\d+[\.,]?\d*)\s+Y:(\d+[\.,]?\d*)", planquadrat) if not m: raise ValueError(f"Koordinaten nicht gefunden in: '{planquadrat}'") x, y = m.groups() return float(x.replace(",", ".")), float(y.replace(",", ".")) def parse_merkmale(merkmale_str: str) -> dict: """Parst Merkmale-JSON-String in dict; bei Fehler → leeres Dict.""" try: return json.loads(merkmale_str) except json.JSONDecodeError: return {} def import_block(block_name: str, from_doc, to_doc) -> None: """Importiert Blockdefinition block_name von from_doc nach to_doc. - Kopiert alle Entities des Blocks - Stellt sicher, dass benutzte Layer im Ziel existieren (mit Eigenschaften) - Übernimmt Basis­punkt und Block-Layer, falls vorhanden """ if block_name in to_doc.blocks: return if block_name not in from_doc.blocks: raise ValueError(f"Block '{block_name}' nicht in Bibliothek gefunden.") src = from_doc.blocks[block_name] # Sicherstellen, dass alle verwendeten Layer existieren try: used_layer_names = {e.dxf.layer for e in src if hasattr(e.dxf, "layer")} for layer_name in used_layer_names: if layer_name and layer_name not in to_doc.layers: try: src_layer = from_doc.layers.get(layer_name) to_doc.layers.add( name=layer_name, color=getattr(src_layer.dxf, "color", None), linetype=getattr(src_layer.dxf, "linetype", None), lineweight=getattr(src_layer.dxf, "lineweight", None), ) except Exception: # Fallback: Layer mit Standardwerten anlegen to_doc.layers.add(name=layer_name) except Exception: pass tgt = to_doc.blocks.new(name=block_name) # Basis­punkt/Layer des Blocks übernehmen, wenn vorhanden try: tgt.block.dxf.base_point = src.block.dxf.base_point except Exception: pass try: tgt.block.dxf.layer = src.block.dxf.layer except Exception: pass for ent in src: if ent.dxftype() == "INSERT": import_block(ent.dxf.name,from_doc, to_doc) tgt.add_entity(ent.copy()) def berechne_hoehe(csv_path, logger=None): y_werte = [] try: with csv_path.open(newline="", encoding="utf-8") as fh: reader = csv.DictReader(fh, delimiter=';') for row in reader: planquadrat = row.get("Planquadrat", "") try: _, y = extract_coords(planquadrat) y_werte.append(y) except Exception as e: if logger: logger.warning(f"Fehler beim Extrahieren der Koordinate aus '{planquadrat}': {e}") except Exception as e: if logger: logger.error(f"Fehler beim Lesen der CSV-Datei {csv_path}: {e}") else: print(f"Fehler beim Lesen der CSV-Datei {csv_path}: {e}") return 0 if not y_werte: msg = "Keine Y-Koordinaten in der CSV gefunden!" if logger: logger.error(msg) else: print(msg) raise ValueError(msg) return max(y_werte) def transform_coords(x: float, y: float, height: float) -> tuple[float, float]: """Transformiert Bildschirmkoordinaten (0,0 oben links) ins DXF-KoSy (0,0 unten links).""" return x, y def handle_ils_2_0_kreisel(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols,gefaellestrecken_nachbarn,config): abstand_m = merkmale.get( "Abstand (Kreiselachse A - Kreiselachse) in Meter", "20" ).replace(",", ".") try: abstand = float(abstand_m) * 1000 # Meter → mm except ValueError: abstand = 10000 # Fallback 10 m # Drehung (Winkel in Grad, Standard 0) aus Merkmale try: winkel = float(merkmale.get("Drehung", 90)) except (ValueError, TypeError): winkel = 0.0 winkel_rad = math.radians(winkel) # Die Koordinaten (x, y) sind die Mitte zwischen den beiden Blöcken (bereits transformiert) halbabstand = abstand / 2 dx = halbabstand * math.cos(winkel_rad) dy = halbabstand * math.sin(winkel_rad) pos1 = (x - dx, y - dy,float(merkmale.get("Höhe in m"))* 1000) pos2 = (x + dx, y + dy, float(merkmale.get("Höhe in m"))* 1000) positions = [pos1, pos2] for i, sym in enumerate(symbols): blockname = sym["name"] offset = sym["offset"] rotation = sym["rotation"] if i < len(positions): pos = (positions[i][0] + offset[0], positions[i][1] + offset[1],float(merkmale.get("Höhe in m"))*1000) import_block(blockname, lib_doc, doc) blockref_layer = get_layer(doc, lib_doc, blockname) bref = msp.add_blockref(blockname, pos, dxfattribs={"rotation": merkmale.get("Drehung"), "layer" : blockref_layer}) bref.add_auto_attribs({ATTR_TAG: teileid}) if verbose: print(f"[INFO] Block '{blockname}' (Kreisel) → {teileid} " f"({pos[0]:.1f}, {pos[1]:.1f}), rot={rotation}") # Linien zeichnen draw_kreisel_lines(msp, pos1, pos2) draw_kreisel_drehrichtung_markierung(msp, pos1, pos2, merkmale, lib_doc, doc, verbose) def draw_kreisel_lines(msp, pos1, pos2): """Zeichnet tangentiale Linien zwischen zwei Kreiselblöcken, unabhängig vom Winkel.""" x1, y1, z1 = pos1 x2, y2, z1 = pos2 # Verbindungsvektor dx = x2 - x1 dy = y2 - y1 # Länge length = math.hypot(dx, dy) if length == 0: return # keine Linie bei identischen Punkten # Normalenvektor (senkrecht, normiert, Länge = RADIUS) nx = -dy / length * RADIUS ny = dx / length * RADIUS # Tangentialpunkte p1a = (x1 + nx, y1 + ny,z1) p1b = (x1 - nx, y1 - ny,z1) p2a = (x2 + nx, y2 + ny,z1) p2b = (x2 - nx, y2 - ny,z1) # Linien zeichnen msp.add_line(p1a, p2a) msp.add_line(p1b, p2b) def draw_kreisel_drehrichtung_markierung(msp, pos1, pos2, merkmale, lib_doc, doc, verbose): drehrichtung = merkmale.get("Drehrichtung", "").upper() if drehrichtung not in ("UZS", "GUZS"): return x1, y1,z1= pos1 x2, y2,z2 = pos2 dx = x2 - x1 dy = y2 - y1 length = math.hypot(dx, dy) if length == 0: return # Normalenvektor (senkrecht, normiert, Länge = RADIUS) nx = -dy / length * RADIUS ny = dx / length * RADIUS # Obere Linie p1_oben = (x1 + nx, y1 + ny) p2_oben = (x2 + nx, y2 + ny) # Untere Linie p1_unten = (x1 - nx, y1 - ny) p2_unten = (x2 - nx, y2 - ny) # S-LP auf oberer Linie (Drehrichtung wie angegeben) for i in range(1, 4): t = i / 4 # 1/4, 2/4, 3/4 px = p1_oben[0] + t * (p2_oben[0] - p1_oben[0]) py = p1_oben[1] + t * (p2_oben[1] - p1_oben[1]) rotation = math.degrees(math.atan2(p2_oben[1] - p1_oben[1], p2_oben[0] - p1_oben[0])) if drehrichtung == "GUZS": rotation += 180 import_block("S-LP", lib_doc, doc) blockref_layer = get_layer(doc, lib_doc, "S-LP") bref = msp.add_blockref("S-LP", (px, py,z1), dxfattribs={"rotation": rotation,"layer": blockref_layer}) if verbose: print(f"[INFO] Drehrichtung '{drehrichtung}': S-LP oben bei ({px:.1f}, {py:.1f}), rot={rotation:.1f}") # S-LP auf unterer Linie (Drehrichtung invertiert) for i in range(1, 4): t = i / 4 px = p1_unten[0] + t * (p2_unten[0] - p1_unten[0]) py = p1_unten[1] + t * (p2_unten[1] - p1_unten[1]) rotation = math.degrees(math.atan2(p2_unten[1] - p1_unten[1], p2_unten[0] - p1_unten[0])) if drehrichtung == "UZS": rotation += 180 import_block("S-LP", lib_doc, doc) blockref_layer = get_layer(doc, lib_doc, "S-LP") bref = msp.add_blockref("S-LP", (px, py, z1), dxfattribs={"rotation": rotation , "layer": blockref_layer}) if verbose: print(f"[INFO] Drehrichtung '{drehrichtung}': S-LP unten bei ({px:.1f}, {py:.1f}), rot={rotation:.1f}") def handle_standard(msp, blocknames, teileid, x, y, lib_doc, doc, verbose): for blockname in blocknames: import_block(blockname, lib_doc, doc) blockref_layer = get_layer(doc, lib_doc, blockname) bref = msp.add_blockref(blockname, (x, y), dxfattribs={"layer": blockref_layer}) bref.add_auto_attribs({ATTR_TAG: teileid}) if verbose: print(f"[INFO] Block '{blockname}' (Standard) → {teileid} " f"({x:.1f}, {y:.1f})") def handle_ils_2_0_gefaellestrecke(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, gefaellestrecken_nachbarn,config): #Vorbereitung der attributen if "6-SP" not in doc.layers: doc.layers.add(name="6-SP", color=3) verbunden_am_einen = False am_kreisel = 0 richtung2 ="DEFAULT" unterschiedlich = False asoffset = float(config.get("ILS 2.0 Gefällestrecke", "asoffset")) esoffset = float(config.get("ILS 2.0 Gefällestrecke", "esoffset")) upper_hoehe_gefaehlle= float(merkmale.get("Höhe oben")) *1000 lower_hoehe_gefaehlle = float(merkmale.get("Höhe unten")) * 1000 hoehe_gefaehlle = (upper_hoehe_gefaehlle + lower_hoehe_gefaehlle)/2 for nachbarn in gefaellestrecken_nachbarn: if teileid == nachbarn.get("Id"): gefaellestrecke_nachbarn = nachbarn break if "Drehung0" in gefaellestrecke_nachbarn and "Drehung1" not in gefaellestrecke_nachbarn: drehung0 =gefaellestrecke_nachbarn.get("Drehung0") x0_kreisel = float(gefaellestrecke_nachbarn.get("x0")) y0_kreisel = float(gefaellestrecke_nachbarn.get("y0")) hoehe0 = float(gefaellestrecke_nachbarn.get("Hoehe0")) richtung0 = float(gefaellestrecke_nachbarn.get("rotation0")) richtung_rad0= math.radians(richtung0) abstand0 = float(gefaellestrecke_nachbarn.get("abstand0")) * 1000 laenge_m = merkmale.get("Länge in Meter", "10").replace(",", ".") try: laenge = float(laenge_m) * 1000 # Meter → mm except ValueError: laenge = 10000 halbabstand0 = abstand0 / 2 dx0 = halbabstand0 * math.cos(richtung_rad0) dy0 = halbabstand0 * math.sin(richtung_rad0) pos0_0_floor= (math.floor(x0_kreisel + dx0),math.floor( y0_kreisel + dy0),hoehe0) pos0_1_floor = (math.floor(x0_kreisel- dx0),math.floor( y0_kreisel- dy0), hoehe0) pos0_0_round= (round(x0_kreisel + dx0),round( y0_kreisel + dy0),hoehe0) pos0_1_round = (round(x0_kreisel- dx0),round( y0_kreisel- dy0), hoehe0) halbe_laenge = laenge / 2 winkel = math.radians(float(merkmale.get("Drehung"))) dx = halbe_laenge *math.sin(winkel * -1) dy = halbe_laenge * math.cos(winkel) if upper_hoehe_gefaehlle < lower_hoehe_gefaehlle: hoehe2 = upper_hoehe_gefaehlle upper_hoehe_gefaehlle = lower_hoehe_gefaehlle lower_hoehe_gefaehlle = hoehe2 if upper_hoehe_gefaehlle == hoehe0: hight = "higher" if richtung0 == 90.0 or richtung0 ==270: richtung0= "Vertikal" if x0_kreisel < x: start_floor = (math.floor(x +dx ), math.floor(y + dy ),lower_hoehe_gefaehlle) ende_floor = (math.floor(x -dx), math.floor(y - dy ) ,upper_hoehe_gefaehlle) start_round = (round(x +dx ), round(y + dy ),lower_hoehe_gefaehlle) ende_round= (round(x -dx), round(y - dy ) ,upper_hoehe_gefaehlle) gefaelle = "links" else: start_floor = (math.floor(x +dx ), math.floor(y + dy ),upper_hoehe_gefaehlle) ende_floor = (math.floor(x -dx), math.floor(y - dy ) ,lower_hoehe_gefaehlle) start_round = (round(x +dx ), round(y + dy ),upper_hoehe_gefaehlle) ende_round= (round(x -dx), round(y - dy ) ,lower_hoehe_gefaehlle) gefaelle = "rechts" else: richtung0 = "Horinzontal" if y0_kreisel < y: start_floor = (math.floor(x +dx ), math.floor(y + dy ),lower_hoehe_gefaehlle) ende_floor = (math.floor(x -dx), math.floor(y - dy ) ,upper_hoehe_gefaehlle) start_round = (round(x +dx ), round(y + dy ),lower_hoehe_gefaehlle) ende_round= (round(x -dx), round(y - dy ) ,upper_hoehe_gefaehlle) gefaelle = "links" gefaelle = "unten" else: start_floor = (math.floor(x +dx ), math.floor(y + dy ),upper_hoehe_gefaehlle) ende_floor = (math.floor(x -dx), math.floor(y - dy ) ,lower_hoehe_gefaehlle) start_round = (round(x +dx ), round(y + dy ),upper_hoehe_gefaehlle) ende_round= (round(x -dx), round(y - dy ) ,lower_hoehe_gefaehlle) gefaelle = "links" gefaelle = "oben" else: hight = "lower" if richtung0 == 90.0 or richtung0 ==270: richtung0= "Vertikal" if x0_kreisel < x: start_floor = (math.floor(x +dx ), math.floor(y + dy ),upper_hoehe_gefaehlle) ende_floor = (math.floor(x -dx), math.floor(y - dy ) ,lower_hoehe_gefaehlle) start_round = (round(x +dx ), round(y + dy ),upper_hoehe_gefaehlle) ende_round= (round(x -dx), round(y - dy ) ,lower_hoehe_gefaehlle) gefaelle = "rechts" else: start_floor = (math.floor(x +dx ), math.floor(y + dy ),lower_hoehe_gefaehlle) ende_floor = (math.floor(x -dx), math.floor(y - dy ) ,upper_hoehe_gefaehlle) start_round = (round(x +dx ), round(y + dy ),lower_hoehe_gefaehlle) ende_round= (round(x -dx), round(y - dy ) ,upper_hoehe_gefaehlle) gefaelle = "links" else: richtung0 = "Horinzontal" if y0_kreisel < y: start_floor = (math.floor(x +dx ), math.floor(y + dy ),upper_hoehe_gefaehlle) ende_floor = (math.floor(x -dx), math.floor(y - dy ) ,lower_hoehe_gefaehlle) start_round = (round(x +dx ), round(y + dy ),upper_hoehe_gefaehlle) ende_round= (round(x -dx), round(y - dy ) ,lower_hoehe_gefaehlle) gefaelle = "links" gefaelle = "oben" else: start_floor = (math.floor(x +dx ), math.floor(y + dy ),lower_hoehe_gefaehlle) ende_floor = (math.floor(x -dx), math.floor(y - dy ) ,upper_hoehe_gefaehlle) start_round = (round(x +dx ), round(y + dy ),lower_hoehe_gefaehlle) ende_round= (round(x -dx), round(y - dy ) ,upper_hoehe_gefaehlle) gefaelle = "links" gefaelle = "unten" abstand_fuer_kreis_start_x_floor = start_floor[0] +RADIUS, start_floor[1] abstand_gegen_kreis_start_x_floor = start_floor[0] -RADIUS, start_floor[1] abstand_fuer_kreis_start_y_floor = start_floor[0], start_floor[1] + RADIUS abstand_gegen_kreis_start_y_floor = start_floor[0], start_floor[1] - RADIUS abstand_fuer_kreis_ende_x_floor= ende_floor[0] + RADIUS, ende_floor[1] abstand_gegen_kreis_ende_x_floor= ende_floor[0] - RADIUS, ende_floor[1] abstand_fuer_kreis_ende_y_floor = ende_floor[0] , ende_floor[1] + RADIUS abstand_gegen_kreis_ende_y_floor = ende_floor[0] , ende_floor[1] - RADIUS abstand_fuer_kreis_start_x_round= start_round[0] +RADIUS, start_round[1] abstand_gegen_kreis_start_x_round = start_round[0] -RADIUS, start_round[1] abstand_fuer_kreis_start_y_round = start_round[0], start_round[1] + RADIUS abstand_gegen_kreis_start_y_round = start_round[0], start_round[1] - RADIUS abstand_fuer_kreis_ende_x_round= ende_round[0] + RADIUS, ende_round[1] abstand_gegen_kreis_ende_x_round= ende_round[0] - RADIUS, ende_round[1] abstand_fuer_kreis_ende_y_round = ende_round[0] , ende_round[1] + RADIUS abstand_gegen_kreis_ende_y_round = ende_round[0] , ende_round[1] - RADIUS if( (abstand_fuer_kreis_start_x_floor == pos0_0_floor )or ( abstand_fuer_kreis_start_x_floor == pos0_1_floor) or ( abstand_gegen_kreis_start_x_floor == pos0_0_floor) or ( abstand_gegen_kreis_start_x_floor == pos0_1_floor) or (abstand_fuer_kreis_ende_x_floor == pos0_0_floor )or ( abstand_fuer_kreis_ende_x_floor == pos0_1_floor) or ( abstand_gegen_kreis_ende_x_floor == pos0_0_floor) or ( abstand_gegen_kreis_ende_x_floor == pos0_1_floor) or (abstand_fuer_kreis_start_y_floor == pos0_0_floor )or ( abstand_fuer_kreis_start_y_floor == pos0_1_floor) or ( abstand_gegen_kreis_start_y_floor == pos0_0_floor) or ( abstand_gegen_kreis_start_y_floor == pos0_1_floor) or (abstand_fuer_kreis_ende_y_floor == pos0_0_floor )or ( abstand_fuer_kreis_ende_y_floor == pos0_1_floor) or ( abstand_gegen_kreis_ende_y_floor == pos0_0_floor) or ( abstand_gegen_kreis_ende_y_floor == pos0_1_floor) or (abstand_fuer_kreis_start_x_round == pos0_0_round )or ( abstand_fuer_kreis_start_x_round == pos0_1_round) or ( abstand_gegen_kreis_start_x_round == pos0_0_round) or ( abstand_gegen_kreis_start_x_round == pos0_1_round) or (abstand_fuer_kreis_ende_x_round == pos0_0_round)or ( abstand_fuer_kreis_ende_x_round== pos0_1_round) or ( abstand_gegen_kreis_ende_x_round == pos0_0_round) or ( abstand_gegen_kreis_ende_x_round == pos0_1_round) or (abstand_fuer_kreis_start_y_round == pos0_0_round)or (abstand_fuer_kreis_start_y_round == pos0_1_round) or (abstand_gegen_kreis_start_y_round == pos0_0_round) or (abstand_gegen_kreis_start_y_round == pos0_1_round) or (abstand_fuer_kreis_ende_y_round == pos0_0_round)or ( abstand_fuer_kreis_ende_y_round == pos0_1_round) or ( abstand_gegen_kreis_ende_y_round == pos0_0_round) or ( abstand_gegen_kreis_ende_y_round == pos0_1_round) ): kreisel_verbunden = kreisel_verbunden +1 # setzung auf kreisel 1 für spätere ausrechnung der geraden form am_kreisel = 1 start = x +dx, y + dy,start_floor[2] ende = x -dx, y - dy,ende_floor[2] line =msp.add_line(start,ende) line.dxf.layer = "6-SP" return verbunden_am_einen = True blockname = f"Ils_2.0_Gefaellestrecke_{laenge}_{drehung0}_{gefaelle}_{verbunden_am_einen}_{hight}" if gefaelle == "oben": rotation = 0 elif gefaelle == "unten" : rotation = 180 elif gefaelle == "links" : rotation = 90 elif gefaelle == "rechts" : rotation = 270 erstellung_gefaelle_block_verbunenden_am_einen(x, y, doc, lib_doc, asoffset, esoffset, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, hoehe_gefaehlle, drehung0, laenge, hight, blockname) blockref_layer = get_layer(doc, lib_doc, blockname) bref =msp.add_blockref(blockname,(x,y,hoehe_gefaehlle),dxfattribs={"rotation": rotation, "layer": blockref_layer}) a =bref.add_attrib( tag= "NAME", text= merkmale.get("bezeichner"), insert = (x,y) ) a.is_invisible = True elif "Drehung0" in gefaellestrecke_nachbarn and "Drehung1" in gefaellestrecke_nachbarn: drehung0 =gefaellestrecke_nachbarn.get("Drehung0") drehung1 = gefaellestrecke_nachbarn.get("Drehung1") hoehe0 = gefaellestrecke_nachbarn.get("Hoehe0") hoehe1 = gefaellestrecke_nachbarn.get("Hoehe1") x0_kreisel = float(gefaellestrecke_nachbarn.get("x0")) y0_kreisel = float(gefaellestrecke_nachbarn.get("y0")) x1_kreisel = float(gefaellestrecke_nachbarn.get("x1")) y1_kreisel = float(gefaellestrecke_nachbarn.get("y1")) richtung0 = float(gefaellestrecke_nachbarn.get("rotation0")) richtung1 = float(gefaellestrecke_nachbarn.get("rotation1")) richtung2 ="DEFAULT" richtung_rad0= math.radians(richtung0) richtung_rad1 = math.radians(richtung1) abstand0 = float(gefaellestrecke_nachbarn.get("abstand0")) * 1000 abstand1 = float(gefaellestrecke_nachbarn.get("abstand1")) * 1000 kreisel_verbunden = 0 laenge_m = merkmale.get("Länge in Meter", "10").replace(",", ".") try: laenge = float(laenge_m) * 1000 # Meter → mm except ValueError: laenge = 10000 #ausrechnung position kreisel halbabstand0 = abstand0 / 2 dx0 = halbabstand0 * math.cos(richtung_rad0) dy0 = halbabstand0 * math.sin(richtung_rad0) pos0_0_floor= (math.floor(x0_kreisel + dx0),math.floor( y0_kreisel + dy0)) pos0_1_floor = (math.floor(x0_kreisel- dx0),math.floor( y0_kreisel- dy0)) pos0_0_round= (round(x0_kreisel + dx0),round( y0_kreisel + dy0)) pos0_1_round = (round(x0_kreisel- dx0),round( y0_kreisel- dy0)) halbabstand1 = abstand1 / 2 dx1 = halbabstand1 * math.cos(richtung_rad1 ) dy1 = halbabstand1 * math.sin(richtung_rad1) pos1_0_floor = (math.floor(x1_kreisel + dx1), math.floor(y1_kreisel + dy1)) pos1_1_floor = (math.floor(x1_kreisel - dx1), math.floor(y1_kreisel - dy1)) pos1_0_round = (round(x1_kreisel + dx1), round(y1_kreisel + dy1)) pos1_1_round = (round(x1_kreisel - dx1), round(y1_kreisel - dy1)) winkel = math.radians(float(merkmale.get("Drehung"))) # berechnung der geraden einmal abgerunden und einmal gerunden zur negirung des offsets halbe_laenge = laenge / 2 dx = halbe_laenge *math.sin(winkel * -1) dy = halbe_laenge * math.cos(winkel) start_floor = (math.floor(x +dx ), math.floor(y + dy ),upper_hoehe_gefaehlle) ende_floor = (math.floor(x -dx), math.floor(y - dy ) ,lower_hoehe_gefaehlle) start_round = (round(x +dx ), round(y + dy ),upper_hoehe_gefaehlle) ende_round= (round(x -dx), round(y - dy ) ,lower_hoehe_gefaehlle) # verschiebund der endpunkte der gerade in richtung von dem potentiellen Kreisel für den vergleich wieder mit floor und rundung abstand_fuer_kreis_start_x_floor = start_floor[0] +RADIUS, start_floor[1] abstand_gegen_kreis_start_x_floor = start_floor[0] -RADIUS, start_floor[1] abstand_fuer_kreis_start_y_floor = start_floor[0], start_floor[1] + RADIUS abstand_gegen_kreis_start_y_floor = start_floor[0], start_floor[1] - RADIUS abstand_fuer_kreis_ende_x_floor= ende_floor[0] + RADIUS, ende_floor[1] abstand_gegen_kreis_ende_x_floor= ende_floor[0] - RADIUS, ende_floor[1] abstand_fuer_kreis_ende_y_floor = ende_floor[0] , ende_floor[1] + RADIUS abstand_gegen_kreis_ende_y_floor = ende_floor[0] , ende_floor[1] - RADIUS abstand_fuer_kreis_start_x_round= start_round[0] +RADIUS, start_round[1] abstand_gegen_kreis_start_x_round = start_round[0] -RADIUS, start_round[1] abstand_fuer_kreis_start_y_round = start_round[0], start_round[1] + RADIUS abstand_gegen_kreis_start_y_round = start_round[0], start_round[1] - RADIUS abstand_fuer_kreis_ende_x_round= ende_round[0] + RADIUS, ende_round[1] abstand_gegen_kreis_ende_x_round= ende_round[0] - RADIUS, ende_round[1] abstand_fuer_kreis_ende_y_round = ende_round[0] , ende_round[1] + RADIUS abstand_gegen_kreis_ende_y_round = ende_round[0] , ende_round[1] - RADIUS # vergleich mit einem kreisel von zwei if( (abstand_fuer_kreis_start_x_floor == pos0_0_floor )or ( abstand_fuer_kreis_start_x_floor == pos0_1_floor) or ( abstand_gegen_kreis_start_x_floor == pos0_0_floor) or ( abstand_gegen_kreis_start_x_floor == pos0_1_floor) or (abstand_fuer_kreis_ende_x_floor == pos0_0_floor )or ( abstand_fuer_kreis_ende_x_floor == pos0_1_floor) or ( abstand_gegen_kreis_ende_x_floor == pos0_0_floor) or ( abstand_gegen_kreis_ende_x_floor == pos0_1_floor) or (abstand_fuer_kreis_start_y_floor == pos0_0_floor )or ( abstand_fuer_kreis_start_y_floor == pos0_1_floor) or ( abstand_gegen_kreis_start_y_floor == pos0_0_floor) or ( abstand_gegen_kreis_start_y_floor == pos0_1_floor) or (abstand_fuer_kreis_ende_y_floor == pos0_0_floor )or ( abstand_fuer_kreis_ende_y_floor == pos0_1_floor) or ( abstand_gegen_kreis_ende_y_floor == pos0_0_floor) or ( abstand_gegen_kreis_ende_y_floor == pos0_1_floor) or (abstand_fuer_kreis_start_x_round == pos0_0_round )or ( abstand_fuer_kreis_start_x_round == pos0_1_round) or ( abstand_gegen_kreis_start_x_round == pos0_0_round) or ( abstand_gegen_kreis_start_x_round == pos0_1_round) or (abstand_fuer_kreis_ende_x_round == pos0_0_round)or ( abstand_fuer_kreis_ende_x_round== pos0_1_round) or ( abstand_gegen_kreis_ende_x_round == pos0_0_round) or ( abstand_gegen_kreis_ende_x_round == pos0_1_round) or (abstand_fuer_kreis_start_y_round == pos0_0_round)or (abstand_fuer_kreis_start_y_round == pos0_1_round) or (abstand_gegen_kreis_start_y_round == pos0_0_round) or (abstand_gegen_kreis_start_y_round == pos0_1_round) or (abstand_fuer_kreis_ende_y_round == pos0_0_round)or ( abstand_fuer_kreis_ende_y_round == pos0_1_round) or ( abstand_gegen_kreis_ende_y_round == pos0_0_round) or ( abstand_gegen_kreis_ende_y_round == pos0_1_round) ): kreisel_verbunden = kreisel_verbunden +1 # setzung auf kreisel 1 für spätere ausrechnung der geraden form am_kreisel = 1 if( (abstand_fuer_kreis_start_x_floor == pos1_0_floor )or ( abstand_fuer_kreis_start_x_floor == pos1_1_floor) or ( abstand_gegen_kreis_start_x_floor == pos1_0_floor) or ( abstand_gegen_kreis_start_x_floor == pos1_1_floor) or (abstand_fuer_kreis_ende_x_floor == pos1_0_floor )or ( abstand_fuer_kreis_ende_x_floor == pos1_1_floor) or ( abstand_gegen_kreis_ende_x_floor == pos1_0_floor) or ( abstand_gegen_kreis_ende_x_floor == pos1_1_floor) or (abstand_fuer_kreis_start_y_floor == pos1_0_floor )or ( abstand_fuer_kreis_start_y_floor == pos1_1_floor) or ( abstand_gegen_kreis_start_y_floor == pos1_0_floor) or ( abstand_gegen_kreis_start_y_floor == pos1_1_floor) or (abstand_fuer_kreis_ende_y_floor == pos1_0_floor )or ( abstand_fuer_kreis_ende_y_floor == pos1_1_floor) or ( abstand_gegen_kreis_ende_y_floor == pos1_0_floor) or ( abstand_gegen_kreis_ende_y_floor == pos1_1_floor) or (abstand_fuer_kreis_start_x_round == pos1_0_round )or ( abstand_fuer_kreis_start_x_round == pos1_1_round) or ( abstand_gegen_kreis_start_x_round == pos1_0_round) or ( abstand_gegen_kreis_start_x_round == pos1_1_round) or (abstand_fuer_kreis_ende_x_round == pos1_0_round)or ( abstand_fuer_kreis_ende_x_round== pos1_1_round) or ( abstand_gegen_kreis_ende_x_round == pos1_0_round) or ( abstand_gegen_kreis_ende_x_round == pos1_1_round) or (abstand_fuer_kreis_start_y_round == pos1_0_round)or (abstand_fuer_kreis_start_y_round == pos1_1_round) or (abstand_gegen_kreis_start_y_round == pos1_0_round) or (abstand_gegen_kreis_start_y_round == pos1_1_round) or (abstand_fuer_kreis_ende_y_round == pos1_0_round)or ( abstand_fuer_kreis_ende_y_round == pos1_1_round) or ( abstand_gegen_kreis_ende_y_round == pos1_0_round) or ( abstand_gegen_kreis_ende_y_round == pos1_1_round) ): kreisel_verbunden = kreisel_verbunden +1 am_kreisel = 2 # falls gefälle strecke mit zwei kreiseln verbunden zeichne eine einfache linie (später braucht man hier auch eine logik sobald man die richtigen es und as elemente hat) if kreisel_verbunden == 2: start = x +dx, y + dy,upper_hoehe_gefaehlle ende = x -dx, y - dy,lower_hoehe_gefaehlle line = msp.add_line(start,ende) line.dxf.layer = "6-SP" return #umbezeichnung von den geraden zu strings ob es Vertikal oder Horizontal ist if richtung0 == 90.0 or richtung0 ==270: richtung0= "Vertikal" else: richtung0 = "Horinzontal" if richtung1 == 90.0 or richtung1 ==270: richtung1= "Vertikal" else: richtung1 = "Horinzontal" # setzung von richtung2 auf vertikal oder horizontal je nach dem welche orientierung der kreisel hat mit dem man am kreisel verbunden ist if (richtung0 != richtung1 and kreisel_verbunden ==1): if( (abstand_fuer_kreis_start_y_floor== pos0_0_floor)or (abstand_fuer_kreis_start_y_floor == pos0_1_floor) or (abstand_gegen_kreis_start_y_floor == pos0_0_floor) or (abstand_gegen_kreis_start_y_floor == pos0_1_floor) or (abstand_fuer_kreis_ende_y_floor == pos0_0_floor)or (abstand_fuer_kreis_ende_y_floor== pos0_1_floor) or (abstand_gegen_kreis_ende_y_floor == pos0_0_floor) or (abstand_gegen_kreis_ende_y_floor== pos0_1_floor) or (abstand_fuer_kreis_start_y_round == pos0_0_round)or (abstand_fuer_kreis_start_y_round == pos0_1_round) or (abstand_gegen_kreis_start_y_round == pos0_0_round) or (abstand_gegen_kreis_start_y_round== pos0_1_round) or (abstand_fuer_kreis_ende_y_round == pos0_0_round)or (abstand_fuer_kreis_ende_y_round== pos0_1_round) or (abstand_gegen_kreis_ende_y_round== pos0_0_round) or (abstand_gegen_kreis_ende_y_round == pos0_1_round) or (abstand_fuer_kreis_start_y_floor == pos1_0_floor )or ( abstand_fuer_kreis_start_y_floor == pos1_1_floor) or ( abstand_gegen_kreis_start_y_floor == pos1_0_floor) or ( abstand_gegen_kreis_start_y_floor == pos1_1_floor) or (abstand_fuer_kreis_ende_y_floor == pos1_0_floor )or ( abstand_fuer_kreis_ende_y_floor== pos1_1_floor) or ( abstand_gegen_kreis_ende_y_floor == pos1_0_floor) or ( abstand_gegen_kreis_ende_y_floor == pos1_1_floor) or (abstand_fuer_kreis_start_y_round == pos1_0_round)or ( abstand_fuer_kreis_start_y_round == pos1_1_round) or ( abstand_gegen_kreis_start_y_round == pos1_0_round) or ( abstand_gegen_kreis_start_y_round== pos1_1_round) or (abstand_fuer_kreis_ende_y_round == pos1_0_round)or ( abstand_fuer_kreis_ende_y_round == pos1_1_round) or ( abstand_gegen_kreis_ende_y_round == pos1_0_round) or ( abstand_gegen_kreis_ende_y_round == pos1_1_round) ): richtung2= "Vertikal" else: richtung2 = "Horinzontal" unterschiedlich = True #Berechnung des Gefälles if hoehe0 > hoehe1: hight_position = "higher" else: hight_position = "lower" if richtung2 == "DEFAULT": if richtung0 == "Vertikal": if x0_kreisel < x1_kreisel: position = hight_position + "_links" else: position = hight_position + "_rechts" else: if y0_kreisel > y1_kreisel: position = hight_position + "_higher" else: position = hight_position + "_lower" if richtung0 == "Vertikal": if position == "lower_rechts" or position == "higher_links": gefaelle = "links" else: gefaelle = "rechts" elif richtung0 == "Horinzontal": if position == "lower_lower" or position == "higher_higher": gefaelle = "oben" else: gefaelle = "unten" # vertausch der drehung und der höhe für die namens gebung des blockes if (position == "higher_rechts" or position == "lower_rechts" or position=="higher_lower" or position== "lower_lower") and drehung0 != drehung1 and am_kreisel == 0: drehung_2 = drehung0 drehung0 = drehung1 drehung1= drehung_2 if hight_position == "higher": hight_position = "lower" else: hight_position = "higher" # austausch der werte damit immer davon ausgehen dass der 1 kreisel in unserer Liste am Kreisel verbuden ist if kreisel_verbunden == 1 and am_kreisel ==2: am_kreisel == 1 drehung_2 = drehung0 drehung0 = drehung1 drehung1= drehung_2 if hight_position == "higher": hight_position = "lower" else: hight_position = "higher" else: if richtung2 == "Vertikal": if x0_kreisel < x1_kreisel: position = hight_position + "_links" else: position = hight_position + "_rechts" else: if y0_kreisel > y1_kreisel: position = hight_position + "_higher" else: position = hight_position + "_lower" if richtung2 == "Vertikal": if position == "lower_rechts" or position == "higher_links": gefaelle = "links" else: gefaelle = "rechts" elif richtung2 == "Horinzontal": if position == "lower_lower" or position == "higher_higher": gefaelle = "oben" else: gefaelle = "unten" # austausch der werte damit immer davon ausgehen dass der 1 kreisel in unserer Liste am Kreisel verbuden ist if am_kreisel == 2: am_kreisel = 1 drehung_2 = drehung0 drehung0 = drehung1 drehung1= drehung_2 if hight_position == "higher": hight_position = "lower" else: hight_position = "higher" if gefaelle == "oben": rotation = 0 elif gefaelle == "unten" : rotation = 180 elif gefaelle == "links" : rotation = 90 elif gefaelle == "rechts" : rotation = 270 #geben der richtung2 eines wertes außer default wenn beide kreisel die gleiche richung haben if (kreisel_verbunden == 1 and richtung2 =="DEFAULT"): richtung2 = richtung0 if richtung2 == "DEFAULT": blockname = f"Ils_2.0_Gefaellestrecke_{laenge}_{hoehe_gefaehlle}_{drehung0}_{drehung1}_{hight_position}_{verbunden_am_einen}" else: blockname = f"Ils_2.0_Gefaellestrecke_{laenge}_{hoehe_gefaehlle}_{drehung0}_{drehung1}_{hight_position}_{unterschiedlich}_{richtung2}_{verbunden_am_einen}" gefaellegerade_erstellung(x, y, doc, lib_doc, asoffset, esoffset, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, hoehe_gefaehlle,richtung2,drehung0, drehung1, laenge, hight_position,blockname) blockref_layer = get_layer(doc, lib_doc, blockname) bref =msp.add_blockref(blockname,(x,y,hoehe_gefaehlle),dxfattribs={"rotation": rotation, "layer": blockref_layer }) a = bref.add_attrib( tag= "NAME", text= merkmale.get("bezeichner"), insert = (x,y) ) a.is_invisible = True #falls eine gefällestrecke nicht mit einem kreisel verbunden ist else: laenge_m = merkmale.get("Länge in Meter", "10").replace(",", ".") try: laenge = float(laenge_m) * 1000 # Meter → mm except ValueError: laenge = 10000 winkel = math.radians(float(merkmale.get("Drehung"))) halbe_laenge = laenge / 2 dx = halbe_laenge *math.sin(winkel * -1) dy = halbe_laenge * math.cos(winkel) start = x +dx, y + dy,upper_hoehe_gefaehlle ende = x -dx, y - dy,lower_hoehe_gefaehlle line = msp.add_line(start,ende) line.dxf.layer = "6-SP" def erstellung_gefaelle_block_verbunenden_am_einen(x, y, doc, lib_doc, asoffset, esoffset, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, hoehe_gefaehlle, drehung0, laenge, hight, blockname): if blockname not in doc.blocks and hight == "higher" and drehung0 == "GUZS": block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy - asoffset,upper_hoehe_gefaehlle) ende = (x , y - dy ,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x,-y,-hoehe_gefaehlle) block.add_entity(copy) block_as = "200000241_AS-Element_90_rechts" import_block(block_as,lib_doc,doc) block.add_blockref(block_as,(start[0]-x ,start[1]+asoffset -y,start[2] -hoehe_gefaehlle)) elif blockname not in doc.blocks and hight == "lower" and drehung0 == "GUZS": block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy,upper_hoehe_gefaehlle) ende = (x , y - dy + esoffset,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x,-y,-hoehe_gefaehlle) block.add_entity(copy) block_es = "200000146_ES-Element_90_rechts" import_block(block_es,lib_doc,doc) block.add_blockref(block_es, (ende[0]-x ,ende[1]-esoffset-y ,ende[2] -hoehe_gefaehlle)) elif blockname not in doc.blocks and hight == "higher" and drehung0 == "UZS": block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy - asoffset,upper_hoehe_gefaehlle) ende = (x , y - dy ,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x ,-y,-hoehe_gefaehlle) block.add_entity(copy) block_as = "200000217_AS-Element_90_links" import_block(block_as,lib_doc,doc) block.add_blockref(block_as,(start[0]-x ,start[1]+asoffset -y,start[2] - hoehe_gefaehlle),dxfattribs={"rotation": 180}) elif blockname not in doc.blocks and hight == "lower" and drehung0 == "UZS": block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy ,upper_hoehe_gefaehlle) ende = (x , y - dy + esoffset,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x ,-y,-hoehe_gefaehlle) block.add_entity(copy) block_es = "400102632_ES-Element_90_links" import_block(block_es,lib_doc,doc) block.add_blockref(block_es, (ende[0]-x ,ende[1]-esoffset -y,ende[2] - hoehe_gefaehlle),dxfattribs={"rotation": 180}) def gefaellegerade_erstellung(x, y, doc, lib_doc, asoffset, esoffset, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, hoehe_gefaehlle,richtung2, drehung0, drehung1, laenge, hight_position, blockname): #erstellung des blockes falls die gefaelle strecke mit einem kreisel verbunden ist, mehrere variation von selben weil diese nötig sind sobald man die richtigen inserts bekommen if upper_hoehe_gefaehlle < lower_hoehe_gefaehlle: middle_hoehe = upper_hoehe_gefaehlle upper_hoehe_gefaehlle = lower_hoehe_gefaehlle lower_hoehe_gefaehlle = middle_hoehe if richtung2!= "DEFAULT": if blockname not in doc.blocks and drehung0 == "GUZS" and drehung1 == "GUZS" and hight_position == "higher" : block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy,upper_hoehe_gefaehlle) ende = (x , y - dy + esoffset,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x,-y,-hoehe_gefaehlle) block.add_entity(copy) block_es = "200000146_ES-Element_90_rechts" import_block(block_es,lib_doc,doc) block.add_blockref(block_es, (ende[0]-x ,ende[1]-esoffset-y ,ende[2] -hoehe_gefaehlle)) elif blockname not in doc.blocks and drehung0 == "GUZS" and drehung1 == "GUZS": block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy - asoffset,upper_hoehe_gefaehlle) ende = (x , y - dy ,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x,-y,-hoehe_gefaehlle) block.add_entity(copy) block_as = "200000241_AS-Element_90_rechts" import_block(block_as,lib_doc,doc) block.add_blockref(block_as,(start[0]-x ,start[1]+asoffset -y,start[2] -hoehe_gefaehlle)) elif blockname not in doc.blocks and ((drehung0 == "GUZS" and drehung1 == "UZS" and hight_position == "higher")): block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy ,upper_hoehe_gefaehlle) ende = (x , y - dy + esoffset,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x,-y,-hoehe_gefaehlle) block.add_entity(copy) block_es = "400102632_ES-Element_90_links" import_block(block_es,lib_doc,doc) block.add_blockref(block_es,(ende[0]-x ,ende[1]-esoffset -y,ende[2] - hoehe_gefaehlle), dxfattribs={"rotation": 180}) elif blockname not in doc.blocks and (drehung0 == "UZS" and drehung1 == "GUZS" and hight_position == "lower"): block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy - asoffset,upper_hoehe_gefaehlle) ende = (x , y - dy ,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x,-y,-hoehe_gefaehlle) block.add_entity(copy) block_as = "200000241_AS-Element_90_rechts" import_block(block_as,lib_doc,doc) block.add_blockref(block_as,(start[0]-x ,start[1]+asoffset -y,start[2] - hoehe_gefaehlle)) elif blockname not in doc.blocks and drehung0 == "UZS" and drehung1 == "UZS" and hight_position == "higher": block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy ,upper_hoehe_gefaehlle) ende = (x , y - dy + esoffset,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x ,-y,-hoehe_gefaehlle) block.add_entity(copy) block_es = "400102632_ES-Element_90_links" import_block(block_es,lib_doc,doc) block.add_blockref(block_es, (ende[0]-x ,ende[1]-esoffset -y,ende[2] - hoehe_gefaehlle),dxfattribs={"rotation": 180}) elif blockname not in doc.blocks and drehung0 == "UZS" and drehung1 == "UZS": block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy - asoffset,upper_hoehe_gefaehlle) ende = (x , y - dy ,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x ,-y,-hoehe_gefaehlle) block.add_entity(copy) block_as = "200000217_AS-Element_90_links" import_block(block_as,lib_doc,doc) block.add_blockref(block_as,(start[0]-x ,start[1]+asoffset -y,start[2] - hoehe_gefaehlle),dxfattribs={"rotation": 180}) elif blockname not in doc.blocks and ((drehung0 == "UZS" and drehung1 == "GUZS" and hight_position== "higher")): block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy,upper_hoehe_gefaehlle) ende = (x , y - dy + esoffset,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x ,-y,-hoehe_gefaehlle) block.add_entity(copy) block_es = "200000146_ES-Element_90_rechts" import_block(block_es,lib_doc,doc) block.add_blockref(block_es, (ende[0]-x ,ende[1]-esoffset -y,ende[2] - hoehe_gefaehlle)) elif blockname not in doc.blocks and (drehung0 == "GUZS" and drehung1 == "UZS" and hight_position == "lower"): block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy - asoffset,upper_hoehe_gefaehlle) ende = (x , y - dy ,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x ,-y,-hoehe_gefaehlle) block.add_entity(copy) block_as = "200000217_AS-Element_90_links" import_block(block_as,lib_doc,doc) block.add_blockref(block_as,(start[0]-x ,start[1]+asoffset -y,start[2] - hoehe_gefaehlle),dxfattribs={"rotation": 180}) #erstellung von den normalen gefällestrecken else: if blockname not in doc.blocks and drehung0 == "GUZS" and drehung1 == "GUZS" : block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy - asoffset,upper_hoehe_gefaehlle) ende = (x , y - dy + esoffset,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x,-y,-hoehe_gefaehlle) block.add_entity(copy) block_as = "200000241_AS-Element_90_rechts" import_block(block_as,lib_doc,doc) block_es = "200000146_ES-Element_90_rechts" import_block(block_es,lib_doc,doc) block.add_blockref(block_as,(start[0]-x ,start[1]+asoffset -y,start[2] -hoehe_gefaehlle)) block.add_blockref(block_es, (ende[0]-x ,ende[1]-esoffset-y ,ende[2] -hoehe_gefaehlle)) elif blockname not in doc.blocks and ((drehung0 == "GUZS" and drehung1 == "UZS" and hight_position == "higher")or (drehung0 == "UZS" and drehung1 == "GUZS" and hight_position == "lower")): block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy - asoffset,upper_hoehe_gefaehlle) ende = (x , y - dy + esoffset,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x,-y,-hoehe_gefaehlle) block.add_entity(copy) block_as = "200000241_AS-Element_90_rechts" import_block(block_as,lib_doc,doc) block_es = "400102632_ES-Element_90_links" import_block(block_es,lib_doc,doc) block.add_blockref(block_as,(start[0]-x ,start[1]+asoffset -y,start[2] - hoehe_gefaehlle)) block.add_blockref(block_es, (ende[0]-x ,ende[1]- esoffset-y,ende[2] - hoehe_gefaehlle),dxfattribs={"rotation": 180}) elif blockname not in doc.blocks and drehung0 == "UZS" and drehung1 == "UZS": block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy - asoffset,upper_hoehe_gefaehlle) ende = (x , y - dy + esoffset,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x ,-y,-hoehe_gefaehlle) block.add_entity(copy) block_as = "200000217_AS-Element_90_links" import_block(block_as,lib_doc,doc) block_es = "400102632_ES-Element_90_links" import_block(block_es,lib_doc,doc) block.add_blockref(block_as,(start[0]-x ,start[1]+asoffset -y,start[2] - hoehe_gefaehlle),dxfattribs={"rotation": 180}) block.add_blockref(block_es, (ende[0]-x ,ende[1]-esoffset -y,ende[2] - hoehe_gefaehlle) ,dxfattribs={"rotation": 180}) elif blockname not in doc.blocks and ((drehung0 == "UZS" and drehung1 == "GUZS" and hight_position== "higher")or (drehung0 == "GUZS" and drehung1 == "UZS" and hight_position == "lower")): block = doc.blocks.new(name=blockname, base_point=(0,0,0)) halbe_laenge = laenge / 2 dy = halbe_laenge * math.cos(0) start = (x , y + dy - asoffset,upper_hoehe_gefaehlle) ende = (x , y - dy + esoffset,lower_hoehe_gefaehlle) line = Line.new(dxfattribs={"start": start, "end": ende}) line.dxf.layer = "6-SP" copy= line.copy() copy.translate(-x ,-y,-hoehe_gefaehlle) block.add_entity(copy) block_as = "200000217_AS-Element_90_links" import_block(block_as,lib_doc,doc) block_es = "200000146_ES-Element_90_rechts" import_block(block_es,lib_doc,doc) block.add_blockref(block_as,(start[0]-x ,start[1]+asoffset -y,start[2] - hoehe_gefaehlle),dxfattribs={"rotation": 180}) block.add_blockref(block_es, (ende[0]-x ,ende[1]-esoffset -y,ende[2] - hoehe_gefaehlle)) def handle_omniflo(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, gefaellestrecken_nachbarn,config): """ Für Omniflo Gerade: zeichnet eine Linie (Mitte = Koordinate, Länge und Winkel aus Merkmale). Für alle anderen Omniflo-Typen: Block mit SivasNummer an den Koordinaten. """ # Prüfen, ob es sich um eine Gerade handelt if merkmale.get("Länge in Meter") is not None and merkmale.get("Winkel") is not None: try: laenge = float(merkmale.get("Länge in Meter", "0").replace(",", ".")) * 1000 # Meter → mm except Exception: laenge = 0 try: winkel = float(merkmale.get("Drehung")) except Exception: winkel = 0.0 winkel_rad = math.radians(winkel) halbe_laenge = laenge / 2 # Man muss bei sin -1 machen wegen des links koordinaten system dx = halbe_laenge * math.sin(winkel_rad * -1) dy = halbe_laenge * math.cos(winkel_rad) start = (x + dx, y + dy, float(merkmale.get("Höhe oben")) ) ende = (x - dx, y - dy, float(merkmale.get("Höhe unten"))) if "A-2" not in doc.layers: doc.layers.add(name="A-2", color=2) linie=msp.add_line(start, ende) linie.dxf.layer = "A-2" if verbose: print(f"[INFO] Omniflo Gerade → {teileid} Linie von ({start[0]:.1f}, {start[1]:.1f}) nach ({ende[0]:.1f}, {ende[1]:.1f})") return # Sonst wie gehabt: Block mit SivasNummer if not lib_doc: print("[WARN] lib_doc nicht verfügbar, Block wird nicht eingefügt.") return blockname = merkmale.get("SivasNummer") if not blockname: print(f"[WARN] Keine SivasNummer für {teileid}, überspringe.") return if blockname not in lib_doc.blocks: print(f"[WARN] Omniflo-Block '{blockname}' nicht in Bibliothek {lib_doc.filename}. Überspringe {teileid}.") return import_block(blockname, lib_doc, doc) blockref_layer = get_layer(doc, lib_doc, blockname) drehung = merkmale.get("Drehung") bref = msp.add_blockref(blockname, (x, y,float(merkmale.get("Höhe"))), dxfattribs={"xscale": 1.0, "yscale":1.0,"rotation": drehung, "layer": blockref_layer}) a =bref.add_attrib( tag= "NAME", text= merkmale.get("bezeichner"), insert = (x,y) ) a.is_invisible = True bref.add_auto_attribs({ATTR_TAG: teileid}) if verbose: print(f"[INFO] Block '{blockname}' (Omniflo) → {teileid} ({x:.1f}, {y:.1f})") def get_layer(doc, lib_doc, blockname): if blockname in lib_doc.blocks: src = lib_doc.blocks[blockname] else: src = doc.blocks[blockname] try: used_layer_names = {e.dxf.layer for e in src if hasattr(e.dxf, "layer")} for layer_name in used_layer_names: if layer_name and layer_name not in doc.layers: try: src_layer = lib_doc.layers.get(layer_name) doc.layers.add( name=layer_name, color=getattr(src_layer.dxf, "color", None), linetype=getattr(src_layer.dxf, "linetype", None), lineweight=getattr(src_layer.dxf, "lineweight", None), ) except Exception: # Fallback: Layer mit Standardwerten anlegen doc.layers.add(name=layer_name) except Exception: pass layer_counts = {} for e in src: ln = getattr(e.dxf, "layer", None) if not ln: continue if ln != "BOUNDING_BOX": layer_counts[ln] = layer_counts.get(ln, 0) + 1 if layer_counts: blockref_layer = max(layer_counts.items(), key=lambda kv: kv[1])[0] return blockref_layer def normalize_func_name(name): return ( name.replace('ä', 'ae') .replace('ö', 'oe') .replace('ü', 'ue') .replace('ß', 'ss') .replace(' ', '_') .replace('.', '_') .lower() ) def get_libfile_cfg(teileart, cfg_path): """Liest den Bibliotheksdateinamen für eine TeileArt aus der allgemein.cfg.""" parser = configparser.ConfigParser() with open(cfg_path, encoding='utf-8') as f: parser.read_file(f) # Teileart kann z.B. "ILS 2.0 Kreisel" sein, wir nehmen den ersten Teil vor erstem Leerzeichen oder Punkt # oder suchen iterativ nach Sektionen, die im Teileart-Namen vorkommen for section in parser.sections(): if section in teileart: return parser.get(section, "libfile", fallback=None) return None def get_rotations_of_gefaellestrecke(csv_path:Path) -> dict: gefaehlleStrecken = [] kreisel =[] gefaellestrecken_nachbarn = [] anweisungen = 0 """Gib für jede gefällestrecke zurück welche Drehrichtung die benachbarten Kreisel haben """ with csv_path.open(newline="", encoding="utf-8") as fh: reader = csv.DictReader(fh, delimiter=';') for row in reader: bezeichner = row["TeileArt"].strip() if bezeichner == "ILS 2.0 Gefällestrecke": Id = row["TeileId"].strip() NachbarIds = row["NachbarIds"].strip() gefaehlleStrecken.append({"Id": Id, "NachbarIds": NachbarIds}) if bezeichner == "ILS 2.0 Kreisel": Id = row["TeileId"].strip() planquadrat = row["Planquadrat"] x, y = extract_coords(planquadrat) merkmale = parse_merkmale(row.get("Merkmale", "")) drehung = merkmale.get("Drehrichtung") rotation = merkmale.get("Drehung") hight = float(merkmale.get("Höhe in m")) *1000 abstand_m = merkmale.get( "Abstand (Kreiselachse A - Kreiselachse) in Meter", "20" ).replace(",", ".") kreisel.append({"Id":Id, "drehung":drehung, "höhe":hight,"x": x, "y": y, "rotation": rotation,"abstand":abstand_m}) for gefaelle in gefaehlleStrecken: anweisungen = 0 eintrag = {"Id": gefaelle["Id"]} for kreis in kreisel: if kreis["Id"] in gefaelle["NachbarIds"]: if anweisungen == 0: eintrag["Drehung0"] = kreis.get("drehung") eintrag["Hoehe0"] = kreis.get("höhe") eintrag["x0"] = kreis.get("x") eintrag["y0"] = kreis.get("y") eintrag["rotation0"] = kreis.get("rotation") eintrag["abstand0"] = kreis.get("abstand") anweisungen = 1 elif anweisungen == 1: eintrag["Drehung1"] = kreis.get("drehung") eintrag["Hoehe1"] = kreis.get("höhe") eintrag["x1"] = kreis.get("x") eintrag["y1"] = kreis.get("y") eintrag["rotation1"] = kreis.get("rotation") eintrag["abstand1"] = kreis.get("abstand") break gefaellestrecken_nachbarn.append(eintrag) return gefaellestrecken_nachbarn # --------------------------------------------------------- Hauptfunktion def main(csv_path: Path, lib_path: Path, cfg_path: Path, output_path: Path, verbose=False, logger=None): # Bibliothek nur laden, wenn Datei existiert check_dxflibrary_path(lib_path, verbose, logger) parser = configparser.ConfigParser() try: with open(cfg_path, encoding='utf-8') as f: parser.read_file(f) except Exception as e: msg = f"Fehler beim Lesen der Config-Datei {cfg_path}: {e}" # Neue Ziel­zeichnung (DXF R2018) config =parser doc = ezdxf.new(dxfversion="R2018") doc.units = units.M doc.header['$INSUNITS'] = 4 # Millimeter msp = doc.modelspace() # Höhe bestimmen für Koordinaten-Transformation try: height = berechne_hoehe(csv_path, logger=logger) except Exception as e: msg = f"Fehler bei der Höhenberechnung: {e}" if logger: logger.error(msg) else: print(msg) sys.exit(1) blocklib_dir = data_dir / "block_libraries" lib_docs = dict() gefaellestrecken_nachbarn = get_rotations_of_gefaellestrecke(csv_path) # gibt zu jeder ShapeId einer Gefällestrecke zurück, ob sich der jeweilige Kreisel im UZ oder GUZ dreht # rot_of_gf["shape_3ae53a7b-efb8-f66b-eadc-20b99f949ef1"] = ('UZ', 'GUZ') # Verarbeitung der Blöcke with csv_path.open(newline="", encoding="utf-8") as fh: reader = csv.DictReader(fh, delimiter=';') for row in reader: bezeichner = row["Bezeichnung"].strip() teileart = row["TeileArt"].strip() teileid = row["TeileId"].strip() planquadrat = row["Planquadrat"] merkmale = parse_merkmale(row.get("Merkmale", "")) merkmale["bezeichner"] = bezeichner try: x_screen, y_screen = extract_coords(planquadrat) x, y = transform_coords(x_screen, y_screen, height) except Exception as e: msg = f"[WARN] {teileid}: {e}" if logger: logger.warning(msg) else: print(msg) continue # Bibliotheksdatei bestimmen libfile = get_libfile_cfg(teileart, allgemein_cfg_path) if libfile: lib_path = blocklib_dir / libfile else: lib_path = default_lib_path # Bibliothek laden (mit Cache) lib_doc = None if lib_path in lib_docs: lib_doc = lib_docs[lib_path] elif lib_path.exists(): try: lib_doc = ezdxf.readfile(lib_path) lib_docs[lib_path] = lib_doc if verbose: print(f"[INFO] Bibliothek geladen: {lib_path}") except Exception as e: print(f"[WARN] Fehler beim Lesen der Bibliothek '{lib_path}': {e}") else: print(f"[INFO] Keine Bibliothek gefunden unter {lib_path}. Komplexe Formen werden übersprungen.") # Funktions-Dispatch: handle_ (mit _ statt Leerzeichen und Punkten, alles klein) func_name = f'handle_{normalize_func_name(teileart)}' handler = globals().get(func_name) symbols = get_shape_cfg(teileart, cfg_path, logger=logger) # Mapping für Omniflo-Typen if func_name.startswith('handle_omniflo'): handler = globals().get('handle_omniflo') if handler: handler(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, gefaellestrecken_nachbarn, config) else: msg = f"[WARN] Keine Routine für TeileArt '{teileart}'. Überspringe '{teileid}'." if logger: logger.warning(msg) else: print(msg) continue # DXF speichern doc.saveas(output_path) if logger: logger.info(f"[DONE] DXF gespeichert unter: {output_path}") else: print(f"[DONE] DXF gespeichert unter: {output_path}") def check_dxflibrary_path(lib_path, verbose, logger): lib_doc = None if lib_path.exists(): try: lib_doc = ezdxf.readfile(lib_path) if verbose: logger.info(f"[INFO] Bibliothek geladen: {lib_path}") if logger else print(f"[INFO] Bibliothek geladen: {lib_path}") except Exception as e: msg = f"Fehler beim Lesen der Bibliothek '{lib_path}': {e}" if logger: logger.error(msg) else: print(msg) sys.exit(1) else: msg = f"[INFO] Keine Bibliothek gefunden unter {lib_path}." if logger: logger.error(msg) else: print(msg) sys.exit(1) if __name__ == "__main__": parser = argparse.ArgumentParser( description="Plaziert Anlagenkomponenten aus RuleDesigner CSV.") parser.add_argument("-f", "--file", required=True, help="CSV-Datei (Name oder Pfad)", metavar="input.csv") parser.add_argument("-c", "--config", help="CFG mit einfachen Formen", metavar="shapes.cfg") parser.add_argument("-l", "--lib", help="DXF-Bibliothek mit Blöcken", metavar="bibliothek.dxf") parser.add_argument("-o", "--output", help="Ziel-DXF (Standard: PROJECT_WORK/anlage.dxf)", metavar="anlage.dxf") parser.add_argument("-v", "--verbose", action="store_true", help="mehr Ausgaben anzeigen") args = parser.parse_args() # Verzeichnisse aus Umgebungs­variablen log_dir = check_environment_var("PROJECT_LOG") data_dir = check_environment_var("PROJECT_DATA") work_dir = check_environment_var("PROJECT_WORK") config_dir = check_environment_var("PROJECT_CFG") logger = setup_logger(log_dir, name='plant2dxf') logger.info("=== plant2dxf Verarbeitung gestartet ===") # CSV‑Pfad: nur Dateiname → im WORK‑Ordner suchen if os.sep not in args.file and "/" not in args.file: csv_path = work_dir / args.file else: csv_path = Path(args.file) cfg_path = Path(args.config) if args.config else config_dir / "shapes.cfg" allgemein_cfg_path = config_dir / "allgemein.cfg" default_lib_path = Path(args.lib) if args.lib else data_dir / "blocks.dxf" output_path = Path(args.output) if args.output else (work_dir / f"{csv_path.stem}.dxf") main(csv_path, default_lib_path, cfg_path, output_path, verbose=args.verbose, logger=logger) logger.info("=== plant2dxf Verarbeitung abgeschlossen ===")