Schalter --show_symbol_frames dazugemacht.
This commit is contained in:
@@ -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.")
|
||||
+89
-29
@@ -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:
|
||||
|
||||
+58
-5
@@ -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}"
|
||||
|
||||
Reference in New Issue
Block a user