""" Gemeinsame Hilfsfunktionen fuer DXF-Makros. Enthaelt die Reihen-Gruppierung fuer Omniflo-Elemente und Layout-Funktionen fuer die Uebersichts-DXF Erzeugung. """ import json import os import ezdxf from ezdxf import bbox as ezdxf_bbox from ezdxf.addons.importer import Importer # --------------------------------------------------------------------------- # Reihen-Gruppen: (Label, Quell-Key, Filter-Funktion) # --------------------------------------------------------------------------- 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"), ] # --------------------------------------------------------------------------- # Schalter-Filter: (Label, JSON-Datei, Filter-Funktion) # Gemeinsam genutzt von set_einfuegepkt.py und set_koords.py. # --------------------------------------------------------------------------- SWITCH_FILTERS = { "boegen": ( "Boegen", "omniflo_boegen.json", lambda item: True, ), "weichen45": ( "Weichen 45 Grad", "omniflo_weichen.json", lambda item: item["KurvenWinkel"] == 45 and item["WeichenTyp"] not in ("Dreifachweiche", "Deltaweiche"), ), "weichen90": ( "Weichen 90 Grad", "omniflo_weichen.json", lambda item: item["KurvenWinkel"] == 90 and item["WeichenTyp"] not in ("Dreifachweiche", "Deltaweiche"), ), "weichenkoerper": ( "Weichenkoerper", "omniflo_weichen.json", lambda item: item["KurvenWinkel"] == 22.5, ), "weichen_parallel": ( "Weichen Parallel", "omniflo_weichen.json", lambda item: item["KurvenWinkel"] == 0 and item["WeichenTyp"] != "Sternweiche", ), "sternweiche": ( "Sternweiche", "omniflo_weichen.json", lambda item: item["WeichenTyp"] == "Sternweiche", ), "delta": ( "Deltaweichen", "omniflo_weichen.json", lambda item: item["WeichenTyp"] == "Deltaweiche", ), "dreifachweiche": ( "Dreifachweichen", "omniflo_weichen.json", lambda item: item["WeichenTyp"] == "Dreifachweiche", ), } PADDING_X = 200 PADDING_Y = 400 TEXT_HEIGHT = 30 CROSS_SIZE = 40 TEXT_MARGIN = 20 ROW_LABEL_WIDTH = 600 def load_omniflo_data(data_dir): """Laedt Boegen- und Weichen-JSON und gibt ein Dict {boegen, weichen} zurueck.""" 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) return {"boegen": boegen_data, "weichen": weichen_data} def is_bogen(sivasnr, sources): """Prueft ob eine Sivasnr ein Bogen ist.""" return any(str(b["Sivasnr"]) == str(sivasnr) for b in sources["boegen"]) def build_row_layout(data_dir, results_dir): """ Berechnet das Layout aller Omniflo-Elemente in Reihen. Returns: (sources, rows) wobei rows eine Liste von Dicts ist: [{ 'label': str, 'source_key': str, 'elements': [{'sivasnr', 'source', 'extmin', 'extmax', 'width', 'height', 'offset_x', 'offset_y'}], 'cursor_y': float, 'max_height': float, }] """ sources = load_omniflo_data(data_dir) omniflo_dir = os.environ.get("DXFM_OMNIFLO") or os.path.join(data_dir, "omniflo") rows = [] cursor_y = 0.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 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 not os.path.exists(dxf_path): continue source_doc = ezdxf.readfile(dxf_path) bb = ezdxf_bbox.extents(source_doc.modelspace()) if not bb.has_data: continue 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], }) if not row_elements: continue max_height = max(e['height'] for e in row_elements) cursor_x = 0.0 for elem in row_elements: elem['offset_x'] = cursor_x - elem['extmin'][0] elem['offset_y'] = cursor_y - elem['extmin'][1] cursor_x += elem['width'] + PADDING_X rows.append({ 'label': label, 'source_key': source_key, 'elements': row_elements, 'cursor_y': cursor_y, 'max_height': max_height, }) cursor_y -= max_height + TEXT_HEIGHT + TEXT_MARGIN * 2 + PADDING_Y return sources, rows def import_element_as_block(source_doc, target_doc, block_name): """ Importiert ein Quell-Dokument als Block in target_doc. Wenn das Quell-Dokument eine Blockdefinition mit dem Namen block_name enthaelt (erzeugt durch set_attributs.py), wird diese direkt importiert. Andernfalls werden alle Modelspace-Entities als Block importiert. """ importer = Importer(source_doc, target_doc) importer.import_tables() blk = target_doc.blocks.new(name=block_name) # if block_name in source_doc.blocks: # source_layout = source_doc.blocks[block_name] # else: # source_layout = source_doc.modelspace() # for entity in source_layout: for entity in source_doc.modelspace(): importer.import_entity(entity, blk) importer.finalize() return block_name def draw_cross(msp, x, y, size, color, layer): """Zeichnet ein Kreuz an (x, y).""" msp.add_line((x - size, y), (x + size, y), dxfattribs={'layer': layer, 'color': color}) msp.add_line((x, y - size), (x, y + size), dxfattribs={'layer': layer, 'color': color})