diff --git a/cfg/create_tests.json b/cfg/create_tests.json index 444c5a0..97e1d57 100644 --- a/cfg/create_tests.json +++ b/cfg/create_tests.json @@ -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 diff --git a/lib/create_example.py b/lib/create_example.py index 9bf2996..1c9f9e9 100644 --- a/lib/create_example.py +++ b/lib/create_example.py @@ -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"""