create_example prüft die Sinnhaftigkeit der position der frames

This commit is contained in:
2026-01-23 20:00:46 +01:00
parent 56e4e8ca85
commit 657d0d99f7
2 changed files with 146 additions and 171 deletions
+5 -5
View File
@@ -10,7 +10,7 @@
"count": 3,
"layout_type": "horizontal",
"base_x": 1500,
"base_y": 1000,
"base_y": 1300,
"spacing": 1450
},
{
@@ -25,7 +25,7 @@
"name": "MA-2@@_bottom",
"count": 3,
"layout_type": "horizontal_offset",
"base_x": 1500,
"base_x": 2000,
"base_y": 400,
"spacing": 1450
}
@@ -37,9 +37,9 @@
"kennzeichnung": "A01+UH00",
"layer_name": "ILS_MOTOR",
"direction": "LEFT_RIGHT",
"x": 700,
"x": 800,
"y": 800,
"width": 3600,
"width": 4000,
"height": 450,
"use_polyline": false
},
@@ -50,7 +50,7 @@
"layer_name": "ILS_MOTOR",
"direction": "LEFT_RIGHT",
"x": 450,
"y": 150,
"y": 100,
"width": 1400,
"height": 480,
"use_polyline": true
+141 -166
View File
@@ -29,6 +29,7 @@ class TestDataGenerator:
self.doc = None
self.msp = None
self.config = None
self.generated_symbols = [] # Liste von (x, y, io_pattern, group_name) für Validierung
# Lade Config-Datei (muss immer angegeben werden)
if config_file:
@@ -570,8 +571,7 @@ class TestDataGenerator:
print(f"Verwende JSON-Config für {scene_name}...")
self._generate_nummerierung_from_config(scene_name)
else:
print("Verwende Hard-coded Defaults für Nummerierung...")
self._generate_nummerierung_hardcoded()
print("Fehler in Config ... terminating")
def _generate_ma_group(self, group, ma_defaults, width_per_char, fixed_height, spacing_factor, y_offsets_list):
"""
@@ -622,19 +622,12 @@ class TestDataGenerator:
print(f"Unbekannter Layout-Typ: {layout_type}")
continue
# Erstelle Block-Referenz
# Der Insert-Punkt soll an der oberen linken Ecke des TEXT-D liegen
# TEXT-D ist bei (772.87, 110.44) relativ zum Block, Höhe 63.75
# Die obere linke Ecke von TEXT-D ist bei (772.87, 110.44 + 63.75) = (772.87, 174.19) relativ zum Block
# Daher muss der Block um diese Werte verschoben werden
text_d_x_offset = 772.87
text_d_y_offset = 110.44 + 63.75 # Basis + Höhe = obere linke Ecke
block_insert_x = x - text_d_x_offset
block_insert_y = y - text_d_y_offset
blockref = self.msp.add_blockref(
'io',
insert=(block_insert_x, block_insert_y),
#insert=(block_insert_x, block_insert_y),
insert=(x, y),
dxfattribs={'layer': layer}
)
@@ -644,6 +637,15 @@ class TestDataGenerator:
attrib_values['BEZEICHNUNG'] = f"Motor {io_pattern}"
blockref.add_auto_attribs(attrib_values)
# Speichere Symbol-Position für spätere Validierung
self.generated_symbols.append({
'x': x,
'y': y,
'io': io_pattern,
'group_name': name,
'layer': layer
})
def _generate_nummerierung_from_config(self, scene_name='Nummerierung1'):
"""
@@ -695,167 +697,140 @@ class TestDataGenerator:
direction=frame_config['direction'],
use_polyline=frame_config.get('use_polyline', False)
)
# Prüfe ob alle generierten Symbole in einem Renamer-Frame liegen
self._validate_symbols_in_frames(scene)
def _generate_nummerierung_hardcoded(self):
"""Generiert Nummerierung-Testdaten mit Hard-coded Werten (Fallback)"""
def _point_in_rectangle(self, point, rect_x, rect_y, rect_width, rect_height):
"""
Prüft ob ein Punkt innerhalb eines Rechtecks liegt.
Args:
point: (x, y) Tupel
rect_x, rect_y: Linke obere Ecke des Rechtecks
rect_width, rect_height: Breite und Höhe des Rechtecks
"""
x, y = point
return (rect_x <= x <= rect_x + rect_width and
rect_y <= y <= rect_y + rect_height)
# Berechne Symbol-Ausdehnung für korrekte Positionierung
ma1_width, ma1_height = self.calculate_symbol_extent('MA-1@@')
ma2_width, ma2_height = self.calculate_symbol_extent('MA-2@@')
def _point_in_polygon(self, point, polygon):
"""
Prüft, ob ein Punkt innerhalb eines Polygons liegt (Ray-casting Algorithmus).
"""
x, y = point
n = len(polygon)
inside = False
# Abstand zwischen Symbolen (20% der Breite als Puffer)
spacing = ma1_width * 1.2
p1x, p1y = polygon[0]
for i in range(1, n + 1):
p2x, p2y = polygon[i % n]
if y > min(p1y, p2y):
if y <= max(p1y, p2y):
if x <= max(p1x, p2x):
if p1y != p2y:
xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
if p1x == p2x or x <= xinters:
inside = not inside
p1x, p1y = p2x, p2y
# Definiere Positionen
# Rechts oben: 3x MA-1@@ nebeneinander
top_right_y = 1000
top_right_x_start = 1500
return inside
# Der Insert-Punkt soll an der oberen linken Ecke des TEXT-D liegen
text_d_x_offset = 772.87
text_d_y_offset = 110.44 + 63.75 # Basis + Höhe = obere linke Ecke
def _validate_symbols_in_frames(self, scene):
"""
Prüft ob alle generierten Symbole aus ma_groups in mindestens einem Renamer-Frame liegen.
Gibt Warnungen aus für Symbole, die außerhalb liegen.
"""
if not self.generated_symbols:
return
print("\n" + "="*60)
print("Prüfe ob alle generierten Symbole in Renamer-Frames liegen")
print("="*60)
# Sammle alle Frame-Boundaries
frame_boundaries = []
for frame_config in scene.get('renaming_frames', []):
frame_type = frame_config.get('type', 'rectangle')
layer_name = frame_config.get('layer_name', '')
if frame_type == 'polyline_path':
# Polyline-Pfad: verwende path_points direkt
path_points = frame_config.get('path_points', [])
if path_points:
polygon = [(p['x'], p['y']) for p in path_points]
frame_boundaries.append({
'type': 'polygon',
'boundary': polygon,
'name': frame_config.get('name', 'UNKNOWN'),
'layer_name': layer_name
})
else:
# Rechteck: berechne Eckpunkte
rect_x = frame_config.get('x', 0)
rect_y = frame_config.get('y', 0)
rect_width = frame_config.get('width', 0)
rect_height = frame_config.get('height', 0)
# Erstelle Rechteck als Polygon (4 Eckpunkte)
polygon = [
(rect_x, rect_y),
(rect_x + rect_width, rect_y),
(rect_x + rect_width, rect_y + rect_height),
(rect_x, rect_y + rect_height)
]
frame_boundaries.append({
'type': 'rectangle',
'boundary': polygon,
'rect_x': rect_x,
'rect_y': rect_y,
'rect_width': rect_width,
'rect_height': rect_height,
'name': frame_config.get('name', 'UNKNOWN'),
'layer_name': layer_name
})
# Prüfe jedes generierte Symbol
symbols_outside = []
for symbol in self.generated_symbols:
symbol_point = (symbol['x'], symbol['y'])
in_any_frame = False
for frame in frame_boundaries:
# Prüfe ob Symbol auf dem richtigen Layer ist
if frame['layer_name'] and symbol['layer'] != frame['layer_name']:
continue
# Prüfe ob Symbol innerhalb des Frames liegt
if frame['type'] == 'rectangle':
if self._point_in_rectangle(
symbol_point,
frame['rect_x'],
frame['rect_y'],
frame['rect_width'],
frame['rect_height']
):
in_any_frame = True
break
else: # polygon
if self._point_in_polygon(symbol_point, frame['boundary']):
in_any_frame = True
break
if not in_any_frame:
symbols_outside.append(symbol)
# Gib Warnungen aus
if symbols_outside:
print(f"\nWARNUNG: {len(symbols_outside)} Symbol(e) liegen außerhalb aller Renamer-Frames:")
for symbol in symbols_outside:
print(f" Symbol IO='{symbol['io']}' aus Gruppe '{symbol['group_name']}' "
f"an Position ({symbol['x']:.1f}, {symbol['y']:.1f}) "
f"auf Layer '{symbol['layer']}' liegt in keinem Renamer-Frame")
else:
print(f"\n✓ Alle {len(self.generated_symbols)} generierten Symbole liegen in Renamer-Frames")
for i in range(3):
x = top_right_x_start + i * spacing
block_insert_x = x - text_d_x_offset
block_insert_y = top_right_y - text_d_y_offset
blockref = self.msp.add_blockref(
'io',
# insert=(block_insert_x, block_insert_y),
insert=(x, y),
dxfattribs={'layer': 'ILS_MOTOR'}
)
# Erzeuge Attribut-Instanzen aus ATTDEFs mit Werten
blockref.add_auto_attribs({
'SPS': '1',
'IO': 'MA-1@@',
'VERW': 'Motor',
'BEZEICHNUNG': 'Motor MA-1@@',
'ARTIKELNR': '790902001',
'ARTIKELBEZEICHN': 'E-Teile für SEW Motor ASE1-HAN10ES-BG',
'KENNZEICHNUNG': '=A01+UH00',
'TEXT-D': '',
'TEXT-E': '',
'TEXT-ES': '',
'TEXT-F': '',
'TEXT-I': '',
'ID': '',
'REALE_POSITION': 'x',
})
# Links unten: 1x MA-1@@
left_bottom_x = 500
left_bottom_y = 200
# Der Insert-Punkt soll an der oberen linken Ecke des TEXT-D liegen
text_d_x_offset = 772.87
text_d_y_offset = 110.44 + 63.75 # Basis + Höhe = obere linke Ecke
block_insert_x = left_bottom_x - text_d_x_offset
block_insert_y = left_bottom_y - text_d_y_offset
blockref = self.msp.add_blockref(
'io',
#insert=(block_insert_x, block_insert_y),
insert=(left_bottom_x, left_bottom_y),
dxfattribs={'layer': 'ILS_MOTOR'}
)
blockref.add_auto_attribs({
'SPS': '1',
'IO': 'MA-1@@',
'VERW': 'Motor',
'BEZEICHNUNG': 'Motor MA-1@@',
'ARTIKELNR': '790902001',
'ARTIKELBEZEICHN': 'E-Teile für SEW Motor ASE1-HAN10ES-BG',
'KENNZEICHNUNG': '=A01+UH00',
'TEXT-D': '',
'TEXT-E': '',
'TEXT-F': '',
'TEXT-ES': '',
'ID': '',
'REALE_POSITION': 'x'
})
# Rechts unten: 3x MA-2@@ leicht versetzt
bottom_right_y_start = 400
bottom_right_x_start = top_right_x_start
y_offsets = [0, -50, 50] # Leichte vertikale Versetzung (angepasst an Symbol-Höhe)
# Der Insert-Punkt soll an der oberen linken Ecke des TEXT-D liegen
text_d_x_offset = 772.87
text_d_y_offset = 110.44 + 63.75 # Basis + Höhe = obere linke Ecke
for i in range(3):
x = bottom_right_x_start + i * spacing
y = bottom_right_y_start + y_offsets[i]
block_insert_x = x - text_d_x_offset
block_insert_y = y - text_d_y_offset
blockref = self.msp.add_blockref(
'io',
#insert=(block_insert_x, block_insert_y),
insert=(x, y),
dxfattribs={'layer': 'ILS_MOTOR'}
)
blockref.add_auto_attribs({
'SPS': '1',
'IO': 'MA-2@@',
'VERW': 'Motor',
'BEZEICHNUNG': 'Motor MA-2@@',
'ARTIKELNR': '790902001',
'ARTIKELBEZEICHN': 'E-Teile für SEW Motor ASE1-HAN10ES-BG',
'KENNZEICHNUNG': '=A01+UH00',
'TEXT-D': '',
'TEXT-ES': '',
'TEXT-F': '',
'ID': '',
'REALE_POSITION': 'x'
})
# Renamer-Rahmen erstellen (mit angepassten Größen basierend auf Symbol-Ausdehnung)
# Puffer für Rahmen
frame_padding = 100
# Rahmen 1: Rechts oben (Rechteck) - umschließt die 3 MA-1@@
frame1_width = 3 * spacing + ma1_width * 0.2
frame1_height = ma1_height + 2 * frame_padding
self.create_renamer_frame(
insert_point=(top_right_x_start - frame_padding, top_right_y + frame_padding),
width=frame1_width,
height=frame1_height,
name='MA-1@@',
kennzeichnung='A01+UH00',
layer_name='ILS_MOTOR',
direction='LEFT_RIGHT',
use_polyline=False
)
# Rahmen 2: Links unten (Polylinie) - umschließt das einzelne MA-1@@
# Schmaler machen - nur 15% breiter als das Symbol
frame2_padding = 50 # Kleinerer Puffer für schmalen Rahmen
frame2_width = ma1_width * 1.15
frame2_height = ma1_height + 2 * frame2_padding
self.create_renamer_frame(
insert_point=(left_bottom_x - frame2_padding, left_bottom_y - frame2_padding),
width=frame2_width,
height=frame2_height,
name='MA-1@@',
kennzeichnung='A01+UH00',
layer_name='ILS_MOTOR',
direction='TOP_BOTTOM/LEFT_RIGHT',
use_polyline=True
)
# Rahmen 3: Rechts unten (Rechteck) - umschließt die 3 MA-2@@
# Berücksichtige die vertikalen Offsets (max offset = 50)
frame3_width = 3 * spacing + ma2_width * 0.2
frame3_height = ma2_height + 2 * frame_padding + 100 # Extra für y_offsets
self.create_renamer_frame(
insert_point=(bottom_right_x_start - frame_padding, bottom_right_y_start - frame_padding - 50),
width=frame3_width,
height=frame3_height,
name='MA-2@@',
kennzeichnung='A01+UH00',
layer_name='ILS_MOTOR',
direction='TOP_BOTTOM',
use_polyline=False
)
def generate(self):
"""Generiert die Testdaten basierend auf dem Test-Typ"""