diff --git a/lib/create_dxf_symbols.py b/lib/create_dxf_symbols.py new file mode 100644 index 0000000..51f8d7e --- /dev/null +++ b/lib/create_dxf_symbols.py @@ -0,0 +1,295 @@ +import ezdxf +from ezdxf import colors +from ezdxf.enums import TextEntityAlignment +import math + +def create_grounding_symbol(filename="erdung.dxf"): + """Erstellt ein Schutzerde-Symbol nach DIN EN 60617""" + doc = ezdxf.new('R2010') + msp = doc.modelspace() + + # Block erstellen + block = doc.blocks.new(name='ERDUNG') + + # Mittelpunkt (relativ zum Block) + cx, cy = 0, 0 + + # Vertikale Linie (Erdungsleiter) + block.add_line((cx, cy + 60), (cx, cy - 20)) + + # Drei horizontale Linien (Erdsymbol) + # Obere Linie (längste) + block.add_line((cx - 40, cy - 20), (cx + 40, cy - 20)) + # Mittlere Linie + block.add_line((cx - 25, cy - 35), (cx + 25, cy - 35)) + # Untere Linie (kürzeste) + block.add_line((cx - 15, cy - 50), (cx + 15, cy - 50)) + + # Beschriftung PE + block.add_text("PE", dxfattribs={ + 'height': 12, + 'insert': (cx, cy + 70), + 'halign': TextEntityAlignment.CENTER + }) + + # Attribute definieren + block.add_attdef(tag='REALE_POSITION', insert=(cx, cy - 70), dxfattribs={ + 'height': 8, + 'prompt': 'Reale Position:', + 'default': 'x', + 'halign': TextEntityAlignment.CENTER, + 'invisible': 1 # Unsichtbar, nur für Daten + }) + + block.add_attdef(tag='NAME', insert=(cx, cy - 85), dxfattribs={ + 'height': 10, + 'prompt': 'Name:', + 'default': '', + 'halign': TextEntityAlignment.CENTER + }) + + block.add_attdef(tag='KENNZEICHNUNG', insert=(cx, cy - 100), dxfattribs={ + 'height': 8, + 'prompt': 'Kennzeichnung:', + 'default': '=A01+UH00-X01', + 'halign': TextEntityAlignment.CENTER, + 'invisible': 1 # Unsichtbar, nur für Daten + }) + + # Block-Instanz einfügen + msp.add_blockref('ERDUNG', (125, 125)) + + doc.saveas(filename) + print(f"✓ {filename} erstellt") + +def create_switchboard_symbol(filename="schaltschrank.dxf"): + """Erstellt ein schematisches Schaltschrank-Symbol für Lagepläne""" + doc = ezdxf.new('R2010') + msp = doc.modelspace() + + # Block erstellen + block = doc.blocks.new(name='SCHALTSCHRANK') + + # Mittelpunkt (relativ zum Block) + cx, cy = 0, 0 + + # Rechteck für Schaltschrank (60x80mm) + width, height = 60, 80 + x1, y1 = cx - width/2, cy - height/2 + x2, y2 = cx + width/2, cy + height/2 + + # Außenrahmen + block.add_lwpolyline([ + (x1, y1), (x2, y1), (x2, y2), (x1, y2), (x1, y1) + ], dxfattribs={'closed': True}) + + # Innere Unterteilung (Fächer) + block.add_line((x1, cy - 15), (x2, cy - 15)) + block.add_line((x1, cy + 15), (x2, cy + 15)) + + # Türgriff-Symbol (vertikale Linie links) + block.add_line((x1 + 5, cy - 25), (x1 + 5, cy + 25)) + + # Beschriftung + block.add_text("Schaltschrank", dxfattribs={ + 'height': 10, + 'insert': (cx, y2 + 15), + 'halign': TextEntityAlignment.CENTER + }) + + # Attribute definieren + block.add_attdef(tag='REALE_POSITION', insert=(cx, y1 - 15), dxfattribs={ + 'height': 8, + 'prompt': 'Reale Position:', + 'default': 'x', + 'halign': TextEntityAlignment.CENTER, + 'invisible': 1 + }) + + block.add_attdef(tag='NAME', insert=(cx, y1 - 30), dxfattribs={ + 'height': 10, + 'prompt': 'Name:', + 'default': '', + 'halign': TextEntityAlignment.CENTER + }) + + block.add_attdef(tag='KENNZEICHNUNG', insert=(cx, y1 - 45), dxfattribs={ + 'height': 8, + 'prompt': 'Kennzeichnung:', + 'default': '=A01+UH00-X01', + 'halign': TextEntityAlignment.CENTER, + 'invisible': 1 + }) + + # Block-Instanz einfügen + msp.add_blockref('SCHALTSCHRANK', (125, 125)) + + doc.saveas(filename) + print(f"✓ {filename} erstellt") + +def create_subdistribution_symbol(filename="unterverteiler.dxf"): + """Erstellt ein Unterverteiler-Symbol (ähnlich Schaltschrank, kleiner)""" + doc = ezdxf.new('R2010') + msp = doc.modelspace() + + # Block erstellen + block = doc.blocks.new(name='UNTERVERTEILER') + + # Mittelpunkt (relativ zum Block) + cx, cy = 0, 0 + + # Rechteck für Unterverteiler (45x60mm - kleiner als Schaltschrank) + width, height = 45, 60 + x1, y1 = cx - width/2, cy - height/2 + x2, y2 = cx + width/2, cy + height/2 + + # Außenrahmen + block.add_lwpolyline([ + (x1, y1), (x2, y1), (x2, y2), (x1, y2), (x1, y1) + ], dxfattribs={'closed': True}) + + # Innere Unterteilung (weniger Fächer) + block.add_line((x1, cy), (x2, cy)) + + # Türgriff-Symbol + block.add_line((x1 + 4, cy - 15), (x1 + 4, cy + 15)) + + # Beschriftung UV (Unterverteiler) + block.add_text("UV", dxfattribs={ + 'height': 12, + 'insert': (cx, cy), + 'halign': TextEntityAlignment.CENTER, + 'valign': TextEntityAlignment.MIDDLE + }) + + # Zusätzliche Beschriftung + block.add_text("Unterverteiler", dxfattribs={ + 'height': 10, + 'insert': (cx, y2 + 15), + 'halign': TextEntityAlignment.CENTER + }) + + # Attribute definieren + block.add_attdef(tag='REALE_POSITION', insert=(cx, y1 - 15), dxfattribs={ + 'height': 8, + 'prompt': 'Reale Position:', + 'default': 'x', + 'halign': TextEntityAlignment.CENTER, + 'invisible': 1 + }) + + block.add_attdef(tag='NAME', insert=(cx, y1 - 30), dxfattribs={ + 'height': 10, + 'prompt': 'Name:', + 'default': '', + 'halign': TextEntityAlignment.CENTER + }) + + block.add_attdef(tag='KENNZEICHNUNG', insert=(cx, y1 - 45), dxfattribs={ + 'height': 8, + 'prompt': 'Kennzeichnung:', + 'default': '=A01+UH00-X01', + 'halign': TextEntityAlignment.CENTER, + 'invisible': 1 + }) + + # Block-Instanz einfügen + msp.add_blockref('UNTERVERTEILER', (125, 125)) + + doc.saveas(filename) + print(f"✓ {filename} erstellt") + +def create_motor_symbol(filename="motor.dxf"): + """Erstellt ein allgemeines Motor-Symbol (M im Kreis) nach DIN EN 60617""" + doc = ezdxf.new('R2010') + msp = doc.modelspace() + + # Block erstellen + block = doc.blocks.new(name='MOTOR') + + # Mittelpunkt (relativ zum Block) + cx, cy = 0, 0 + radius = 30 + + # Kreis + block.add_circle((cx, cy), radius) + + # Buchstabe "M" in der Mitte + block.add_text("M", dxfattribs={ + 'height': 25, + 'insert': (cx, cy), + 'halign': TextEntityAlignment.CENTER, + 'valign': TextEntityAlignment.MIDDLE + }) + + # Anschlussklemmen (drei Linien für Drehstrom - optional) + # Oben + block.add_line((cx, cy + radius), (cx, cy + radius + 20)) + # Links unten + angle1 = math.radians(210) + x1 = cx + radius * math.cos(angle1) + y1 = cy + radius * math.sin(angle1) + block.add_line((x1, y1), (x1 - 15, y1 - 10)) + # Rechts unten + angle2 = math.radians(330) + x2 = cx + radius * math.cos(angle2) + y2 = cy + radius * math.sin(angle2) + block.add_line((x2, y2), (x2 + 15, y2 - 10)) + + # Beschriftung + block.add_text("Motor", dxfattribs={ + 'height': 10, + 'insert': (cx, cy - radius - 25), + 'halign': TextEntityAlignment.CENTER + }) + + # Attribute definieren + block.add_attdef(tag='REALE_POSITION', insert=(cx, cy - radius - 40), dxfattribs={ + 'height': 8, + 'prompt': 'Reale Position:', + 'default': 'x', + 'halign': TextEntityAlignment.CENTER, + 'invisible': 1 + }) + + block.add_attdef(tag='NAME', insert=(cx, cy - radius - 55), dxfattribs={ + 'height': 10, + 'prompt': 'Name:', + 'default': '', + 'halign': TextEntityAlignment.CENTER + }) + + block.add_attdef(tag='KENNZEICHNUNG', insert=(cx, cy - radius - 70), dxfattribs={ + 'height': 8, + 'prompt': 'Kennzeichnung:', + 'default': '=A01+UH00-X01', + 'halign': TextEntityAlignment.CENTER, + 'invisible': 1 + }) + + # Block-Instanz einfügen + msp.add_blockref('MOTOR', (125, 125)) + + doc.saveas(filename) + print(f"✓ {filename} erstellt") + +# Hauptprogramm +if __name__ == "__main__": + print("Erstelle DXF Elektrosymbole nach DIN EN 60617 mit Attributen...\n") + + create_grounding_symbol() + create_switchboard_symbol() + create_subdistribution_symbol() + create_motor_symbol() + + print("\n✓ Alle Symbole wurden erfolgreich erstellt!") + print("\nDateien:") + print(" - erdung.dxf") + print(" - schaltschrank.dxf") + print(" - unterverteiler.dxf") + print(" - motor.dxf") + print("\nJedes Symbol ist als Block mit folgenden Attributen definiert:") + print(" - REALE_POSITION (Standard: 'x', unsichtbar)") + print(" - NAME (Standard: leer, sichtbar unter dem Symbol)") + print(" - KENNZEICHNUNG (Standard: '=A01+UH00-X01', unsichtbar)") + print("\nDie Attribute können in CAD-Programmen bearbeitet werden.") diff --git a/lib/create_example.py b/lib/create_example.py index 06a8d03..8c2df1f 100644 --- a/lib/create_example.py +++ b/lib/create_example.py @@ -14,9 +14,11 @@ import json from pathlib import Path import ezdxf +from symbol_frames import draw_symbol_frames + # Umgebungsvariablen PROJECT = os.getenv('PROJECT', Path(__file__).parent.parent.absolute()) -PROJECT_TEST = os.getenv('PROJECT_TEST', os.path.join(PROJECT, 'testdata')) +PROJECT_WORK = os.getenv('PROJECT_WORK', os.path.join(PROJECT, 'work')) PROJECT_CFG = os.getenv('PROJECT_CFG', os.path.join(PROJECT, 'cfg')) @@ -284,13 +286,38 @@ class TestDataGenerator: def save(self, output_dir=None): """Speichert das DXF-Dokument""" if output_dir is None: - output_dir = PROJECT_TEST + output_dir = PROJECT_WORK output_path = os.path.join(output_dir, self.filename) self.doc.saveas(output_path) print(f"Test-DXF erstellt: {output_path}") return output_path + def draw_frames_for_generated_symbols(self): + """ + Zeichnet Rahmen um alle generierten Symbole zur Visualisierung. + Konvertiert das interne Format in das von draw_symbol_frames erwartete Format. + """ + if not self.generated_symbols: + print("Keine generierten Symbole zum Zeichnen von Rahmen gefunden") + return 0 + + # Konvertiere das interne Format in das erwartete Format + symbols_for_frames = [] + for sym in self.generated_symbols: + # generated_symbols enthält 'x', 'y' (Einfügepunkt = linke untere Ecke bei left_bottom_corner) + # draw_symbol_frames erwartet Position = linke untere Ecke des Symbols + # Wir können die gespeicherten Koordinaten direkt verwenden + + symbols_for_frames.append({ + 'position': (sym['x'], sym['y']), # Position = linke untere Ecke + 'attributes': {'IO': sym['io']}, # IO-Wert für Breitenberechnung + 'layer': sym.get('layer', 'ILS_MOTOR') + }) + + # Zeichne die Rahmen + return draw_symbol_frames(self.doc, self.msp, symbols_for_frames) + def load_block_from_file(self, source_file, block_name): """ Lädt einen Block aus einer anderen DXF-Datei @@ -608,15 +635,15 @@ class TestDataGenerator: return offset_map.get(insert_point, (0, 0)) # Default: left_top_corner - def _generate_ma_group(self, group, ma_defaults, width_per_char, fixed_height, spacing_factor, y_offsets_list, insert_point='left_top_corner'): + def _generate_ma_group(self, group, ma_defaults, symbol_width, symbol_height, spacing_factor, y_offsets_list, insert_point='left_top_corner'): """ Generiert eine Gruppe von MA-Symbolen Args: group: Dict mit Gruppen-Config (name, count, layout_type, base_x, base_y, spacing) ma_defaults: Dict mit MA-Default-Attributen - width_per_char: Breite pro Zeichen - fixed_height: Fixe Symbol-Höhe + symbol_width: Feste Breite des Symbols + symbol_height: Feste Höhe des Symbols spacing_factor: Spacing-Faktor zwischen Symbolen y_offsets_list: Liste von Y-Offsets für horizontal_offset Layout insert_point: Einfügepunkt des Blocks (z.B. "left_top_corner", "center") @@ -630,9 +657,6 @@ class TestDataGenerator: # Extrahiere IO-Pattern aus Name (z.B. "MA-1@@_top" -> "MA-1@@") io_pattern = name.split('_')[0] - # Berechne Symbol-Ausdehnung - symbol_width = len(io_pattern) * width_per_char - # Berechne Spacing if layout_type in ['horizontal', 'horizontal_offset']: actual_spacing = symbol_width * spacing_factor @@ -644,7 +668,7 @@ class TestDataGenerator: layer = ma_defaults.get('layer', 'ILS_MOTOR') # Berechne Offset basierend auf Einfügepunkt - offset_x, offset_y = self._calculate_insert_offset(insert_point, symbol_width, fixed_height) + offset_x, offset_y = self._calculate_insert_offset(insert_point, symbol_width, symbol_height) # Generiere Symbole basierend auf Layout-Typ for i in range(count): @@ -669,7 +693,7 @@ class TestDataGenerator: # (da der io-Block bei (0,0) startet und nach oben geht) blockref = self.msp.add_blockref( 'io', - insert=(left_top_x, left_top_y - fixed_height), + insert=(left_top_x, left_top_y - symbol_height), dxfattribs={'layer': layer} ) @@ -681,10 +705,12 @@ class TestDataGenerator: blockref.add_auto_attribs(attrib_values) # Speichere Symbol-Position für spätere Validierung - # Speichere immer die linke obere Ecke + # Speichere den tatsächlichen Einfügepunkt (linke untere Ecke bei left_bottom_corner) + actual_insert_x = left_top_x + actual_insert_y = left_top_y - symbol_height self.generated_symbols.append({ - 'x': left_top_x, - 'y': left_top_y, + 'x': actual_insert_x, + 'y': actual_insert_y, 'io': io_pattern, 'group_name': name, 'layer': layer @@ -701,10 +727,10 @@ class TestDataGenerator: ma_defaults = self.config.get('ma_defaults', {}) general = self.config.get('general', {}) - # Hole Dimensions aus Config + # Hole Dimensions aus Config (feste Werte) dimensions = ma_defaults.get('dimensions', {}) - width_per_char = dimensions.get('width_per_char', 201.49) - fixed_height = dimensions.get('fixed_height', 380.94) + symbol_width = dimensions.get('width', 1210) + symbol_height = dimensions.get('height', 381) # Hole Layout-Einstellungen layout = general.get('layout', {}) @@ -717,7 +743,7 @@ class TestDataGenerator: # Generiere MA-Gruppen aus Config for group in scene.get('ma_groups', []): - self._generate_ma_group(group, ma_defaults, width_per_char, fixed_height, spacing_factor, y_offsets_list, insert_point) + self._generate_ma_group(group, ma_defaults, symbol_width, symbol_height, spacing_factor, y_offsets_list, insert_point) # Generiere Renamer-Rahmen aus Config for frame_config in scene.get('renaming_frames', []): @@ -916,10 +942,15 @@ class TestDataGenerator: print("Prüfe ob alle generierten Symbole in Renamer-Frames liegen") print("="*60) - # Hole Symbol-Dimensionen aus Config + # Hole Symbol-Dimensionen aus Config (feste Werte) dimensions = self.config.get('ma_defaults', {}).get('dimensions', {}) - width_per_char = dimensions.get('width_per_char', 201.49) - fixed_height = dimensions.get('fixed_height', 380.94) + ma_symbol_width = dimensions.get('width', 1210) + ma_symbol_height = dimensions.get('height', 381) + + # Hole Multi-Frame Dimensionen + multi_dimensions = self.config.get('general', {}).get('multi_frame', {}).get('dimensions', {}) + multi_symbol_width = multi_dimensions.get('width', 1410) + multi_symbol_height = multi_dimensions.get('height', 381) # Sammle alle Frame-Boundaries frame_boundaries = [] @@ -969,15 +1000,20 @@ class TestDataGenerator: # Prüfe jedes generierte Symbol symbols_outside = [] for symbol in self.generated_symbols: - # Berechne Symbol-Dimensionen + # Berechne Symbol-Dimensionen basierend auf IO-Text Länge io_text = symbol['io'] - symbol_width = len(io_text) * width_per_char - symbol_height = fixed_height + char_count = len(io_text) + if char_count >= 7: + symbol_width = multi_symbol_width # Multi-Frame (BG/MB/POT) + symbol_height = multi_symbol_height + else: + symbol_width = ma_symbol_width # MA-Frame + symbol_height = ma_symbol_height # Berechne Mittelpunkt des Symbols - # symbol['x'], symbol['y'] ist die linke obere Ecke + # symbol['x'], symbol['y'] ist die linke untere Ecke symbol_center_x = symbol['x'] + symbol_width / 2 - symbol_center_y = symbol['y'] - symbol_height / 2 + symbol_center_y = symbol['y'] + symbol_height / 2 symbol_point = (symbol_center_x, symbol_center_y) # Radius für Kreis-Test @@ -1041,7 +1077,8 @@ class TestDataGenerator: symbol_with_type['symbol_radius'] = symbol_radius symbol_with_type['shift_x'] = best_shift[0] symbol_with_type['shift_y'] = best_shift[1] - symbol_with_type['closest_frame'] = closest_frame.get('name', 'UNKNOWN') if closest_frame else 'UNKNOWN' + symbol_with_type['closest_frame_name'] = closest_frame.get('name', 'UNKNOWN') if closest_frame else 'UNKNOWN' + symbol_with_type['closest_frame_dict'] = closest_frame # Speichere das gesamte Dictionary symbols_outside.append(symbol_with_type) # Gib Warnungen aus und zeichne Fehlerkreise @@ -1059,18 +1096,31 @@ class TestDataGenerator: else: shift_x = symbol.get('shift_x', 0) shift_y = symbol.get('shift_y', 0) - closest_frame = symbol.get('closest_frame', 'UNKNOWN') + closest_frame_name = symbol.get('closest_frame_name', 'UNKNOWN') if shift_x != 0 or shift_y != 0: print(f"{base_msg}: " - f"Symbol liegt nicht innerhalb des Renamer-Rahmens '{closest_frame}'. " + f"Symbol liegt nicht innerhalb des Renamer-Rahmens '{closest_frame_name}'. " f"Verschieben Sie um dx={shift_x:.1f}, dy={shift_y:.1f}") else: print(f"{base_msg}: " f"Symbol liegt nicht innerhalb des Renamer-Rahmens") # Zeichne Fehlerkreis um das Symbol - self._draw_error_circle(symbol['x'], symbol['y'], symbol['symbol_radius']) + # Berechne Symbol-Zentrum (symbol['x'], symbol['y'] = linke untere Ecke) + io_text = symbol['io'] + char_count = len(io_text) + if char_count >= 7: + sym_width = multi_symbol_width + sym_height = multi_symbol_height + else: + sym_width = ma_symbol_width + sym_height = ma_symbol_height + + symbol_center_x = symbol['x'] + sym_width / 2 + symbol_center_y = symbol['y'] + sym_height / 2 + + self._draw_error_circle(symbol_center_x, symbol_center_y, symbol['symbol_radius']) else: print(f"\nOK: Alle {len(self.generated_symbols)} generierten Symbole erfüllen die Frame-Kriterien") @@ -1186,6 +1236,12 @@ def main(): help=f'JSON-Konfigurationsdatei (Standard: {default_config})' ) + parser.add_argument( + '--show_symbol_frames', + action='store_true', + help='Zeichne Rahmen um jedes Symbol, um die Grenze des Symbols zu visualisieren' + ) + args = parser.parse_args() # Bestimme Filename: entweder angegeben oder aus scene ableiten @@ -1202,6 +1258,10 @@ def main(): generator = TestDataGenerator(filename, args.scene, config_file=args.config) if generator.generate(): + # Zeichne Symbol-Rahmen wenn gewünscht + if args.show_symbol_frames: + generator.draw_frames_for_generated_symbols() + generator.save(args.output_dir) return 0 else: diff --git a/lib/create_numbers.py b/lib/create_numbers.py index ae44638..944cb94 100644 --- a/lib/create_numbers.py +++ b/lib/create_numbers.py @@ -4,6 +4,7 @@ from pathlib import Path from error_collector import ErrorCollector, write_json_file from utils import check_environment_var, check_file_in_work, dxf_is_binary, get_dxf_file +from symbol_frames import draw_symbol_frames """ @@ -491,6 +492,7 @@ def collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector): continue print(f" Boundary gefunden mit {len(boundary)} Punkten (Rechteck: {is_rectangle})") + print(f" Eckpunkte: {', '.join([f'({p[0]:.2f}, {p[1]:.2f})' for p in boundary])}") # Validiere DIRECTION gegen Geometrie direction = attributes.get("DIRECTION", "LEFT_RIGHT") @@ -679,17 +681,59 @@ def process_renamer_blocks(doc, msp, renamer_layers, error_collector): Verarbeitet alle Renamer-Blöcke auf den angegebenen Layern. Gruppiert Blöcke mit identischer Konfiguration und nummeriert alle Symbole aus allen Blöcken einer Gruppe gemeinsam. + + Returns: + Tuple (all_renamed, renamer_groups): + - all_renamed: Liste der umbenannten Symbole + - renamer_groups: Dictionary der gruppierten Renamer-Blöcke """ # Erster Pass: Sammle und gruppiere alle Renamer-Blöcke renamer_groups = collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector) - + # Zweiter Pass: Verarbeite jede Gruppe all_renamed = process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_collector) - + # Dritter Pass: Prüfe ob alle Symbole mit "@" im IO in einem Renamer-Bereich liegen check_symbols_in_boundaries(doc, msp, renamer_groups, error_collector) - - return all_renamed + + return all_renamed, renamer_groups + + +def collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, error_collector): + """ + Sammelt alle Symbole aus allen Renamer-Gruppen für Visualisierungszwecke. + + Args: + doc: DXF-Dokument + msp: Modelspace + renamer_groups: Dictionary mit gruppierten Renamer-Blöcken + renamer_layers: Liste der Layer mit Renamer-Blöcken + error_collector: ErrorCollector für Fehlerbehandlung + + Returns: + Liste aller gefundenen Symbole mit 'entity', 'position', 'attributes', 'layer' + """ + all_symbols = [] + seen_entities = set() # Verhindere Duplikate + + for config_key, group_blocks in renamer_groups.items(): + if not group_blocks: + continue + + reference_attributes = group_blocks[0][2] + + for insert, boundary, attributes, is_rectangle in group_blocks: + # Finde Symbole innerhalb dieses Bereichs + symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes, error_collector) + + # Füge Symbole hinzu, aber nur wenn das Entity noch nicht gesehen wurde + for symbol in symbols: + entity_id = id(symbol['entity']) + if entity_id not in seen_entities: + seen_entities.add(entity_id) + all_symbols.append(symbol) + + return all_symbols if __name__ == '__main__': @@ -726,6 +770,7 @@ if __name__ == '__main__': parser.add_argument('-e', '--errorfile', action='store', required=False, help='JSON-Datei für Fehler und Warnungen', metavar='errors.json') parser.add_argument('-w', '--write', action='store', help='Schreibe Ergebnisse der Nummerierung in eine JSON-Datei') parser.add_argument('-d', '--dryrun', action='store_true', help='Symbole nicht in der DXF-Datei überschreiben, nur Ausgabe auf Konsole') + parser.add_argument('--show_symbol_frames', action='store_true', help='Zeichne Rahmen um jedes Symbol, um die Grenze des Symbols zu visualisieren') args = parser.parse_args() @@ -770,7 +815,7 @@ if __name__ == '__main__': print("Starte Verarbeitung der Renamer-Blöcke") print("="*60 + "\n") - renamed_symbols = process_renamer_blocks(doc, msp, renamer_layers, error_collector) + renamed_symbols, renamer_groups = process_renamer_blocks(doc, msp, renamer_layers, error_collector) print("\n" + "="*60) print(f"Verarbeitung abgeschlossen: {len(renamed_symbols)} Symbole nummeriert") @@ -782,6 +827,14 @@ if __name__ == '__main__': for item in renamed_symbols: print(f" {item['old_value']} -> {item['new_value']} (Layer: {item['layer']}, Pos: {item['position']})") + # Zeichne Symbol-Rahmen wenn gewünscht + if args.show_symbol_frames: + all_symbols = collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, error_collector) + if all_symbols: + draw_symbol_frames(doc, msp, all_symbols) + else: + print("\nKeine Symbole für Rahmen-Zeichnung gefunden") + # Speichere DXF-Datei wenn nicht dry-run if not args.dryrun: output_path = dxf_path.parent / f"{dxf_path.stem}_numbered{dxf_path.suffix}"