Ausserhalb der Bereiche liegende Symbole werden im dxf von einem Kreis umgeben, um neben der Consoleausgabe auch ein graphisches Feedback zu bekommen. Angabe des Einfügepunktes am Symbol in der Config möglich. Der MA Block kann im Zentrum, an der linken oberen Ecke etc. positioniert werden. Nummerierung2 wurde vergrössert, damit die Symbole sich nicht überschneiden.
This commit is contained in:
+28
-26
@@ -71,70 +71,70 @@
|
||||
},
|
||||
|
||||
"Nummerierung2": {
|
||||
"description": "Test-Szene mit komplexem Layout - 8 MA-1@@ Symbole in verschiedenen Anordnungen",
|
||||
"description": "Test-Szene mit zirkulärer Nummerierung des Layouts - 8 MA-1@@ Symbole entlang einer Polyline nummerieren",
|
||||
"ma_groups": [
|
||||
{
|
||||
"name": "MA-1@@_top_left",
|
||||
"count": 1,
|
||||
"layout_type": "single",
|
||||
"base_x": 200,
|
||||
"base_y": 1400,
|
||||
"base_x": 400,
|
||||
"base_y": 2800,
|
||||
"spacing": 0
|
||||
},
|
||||
{
|
||||
"name": "MA-1@@_top_right",
|
||||
"count": 1,
|
||||
"layout_type": "single",
|
||||
"base_x": 240000,
|
||||
"base_y": 1400,
|
||||
"base_x": 4800,
|
||||
"base_y": 2800,
|
||||
"spacing": 0
|
||||
},
|
||||
{
|
||||
"name": "MA-1@@_middle_left",
|
||||
"count": 1,
|
||||
"layout_type": "single",
|
||||
"base_x": 600,
|
||||
"base_y": 900,
|
||||
"base_x": 1200,
|
||||
"base_y": 1800,
|
||||
"spacing": 0
|
||||
},
|
||||
{
|
||||
"name": "MA-1@@_middle_right",
|
||||
"count": 1,
|
||||
"layout_type": "single",
|
||||
"base_x": 1700,
|
||||
"base_y": 900,
|
||||
"base_x": 3400,
|
||||
"base_y": 1800,
|
||||
"spacing": 0
|
||||
},
|
||||
{
|
||||
"name": "MA-1@@_bottom_center",
|
||||
"count": 1,
|
||||
"layout_type": "single",
|
||||
"base_x": 1240,
|
||||
"base_y": 400,
|
||||
"base_x": 2480,
|
||||
"base_y": 800,
|
||||
"spacing": 0
|
||||
},
|
||||
{
|
||||
"name": "MA-1@@_right_top",
|
||||
"count": 1,
|
||||
"layout_type": "single",
|
||||
"base_x": 3200,
|
||||
"base_y": 1200,
|
||||
"base_x": 6400,
|
||||
"base_y": 2400,
|
||||
"spacing": 0
|
||||
},
|
||||
{
|
||||
"name": "MA-1@@_right_middle",
|
||||
"count": 1,
|
||||
"layout_type": "single",
|
||||
"base_x": 3200,
|
||||
"base_y": 700,
|
||||
"base_x": 6400,
|
||||
"base_y": 1400,
|
||||
"spacing": 0
|
||||
},
|
||||
{
|
||||
"name": "MA-1@@_right_bottom",
|
||||
"count": 1,
|
||||
"layout_type": "single",
|
||||
"base_x": 3040,
|
||||
"base_y": 400,
|
||||
"base_x": 6080,
|
||||
"base_y": 800,
|
||||
"spacing": 0
|
||||
}
|
||||
],
|
||||
@@ -147,15 +147,15 @@
|
||||
"layer_name": "ILS_MOTOR",
|
||||
"direction": "POLYLINE_PATH",
|
||||
"path_points": [
|
||||
{"x": 200, "y": 1400},
|
||||
{"x": 2400, "y": 1400},
|
||||
{"x": 3200, "y": 1200},
|
||||
{"x": 3200, "y": 700},
|
||||
{"x": 3040, "y": 400},
|
||||
{"x": 1240, "y": 400},
|
||||
{"x": 600, "y": 900},
|
||||
{"x": 1700, "y": 900},
|
||||
{"x": 200, "y": 1400}
|
||||
{"x": 400, "y": 2800},
|
||||
{"x": 4800, "y": 2800},
|
||||
{"x": 6400, "y": 2400},
|
||||
{"x": 6400, "y": 1400},
|
||||
{"x": 6080, "y": 800},
|
||||
{"x": 2480, "y": 800},
|
||||
{"x": 3400, "y": 1800},
|
||||
{"x": 1200, "y": 1800},
|
||||
{"x": 400, "y": 2800}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -194,6 +194,8 @@
|
||||
"_description": "MA Frame-Typ Attribute (einfache Frames für MA, MG, etc.)",
|
||||
"layer": "ILS_RENAMER",
|
||||
"color": 1,
|
||||
"insert_point": "center",
|
||||
"_insert_point_description": "Einfügepunkt für MA-Blöcke. Mögliche Werte: left_top_corner, left_bottom_corner, right_top_corner, right_bottom_corner, center, top_center, bottom_center",
|
||||
"attdef_positions": {
|
||||
"_description": "ATTDEF Positionen im inneren Block RENAMER_ATTRIB_MA",
|
||||
"NAME": {
|
||||
|
||||
+307
-44
@@ -88,6 +88,7 @@ class TestDataGenerator:
|
||||
{'name': '0', 'color': 7}, # Standard-Layer
|
||||
{'name': 'ILS_MOTOR', 'color': 7},
|
||||
{'name': 'ILS_RENAMER', 'color': 1}, # Rot
|
||||
{'name': 'VALIDATION_ERROR', 'color': 1}, # Rot für Fehlerkreise
|
||||
{'name': 'TEXT-D', 'color': -7}, # Ausgeschaltet (negativ)
|
||||
{'name': 'TEXT-E', 'color': 7},
|
||||
{'name': 'TEXT-ES', 'color': 7},
|
||||
@@ -573,7 +574,31 @@ class TestDataGenerator:
|
||||
else:
|
||||
print("Fehler in Config ... terminating")
|
||||
|
||||
def _generate_ma_group(self, group, ma_defaults, width_per_char, fixed_height, spacing_factor, y_offsets_list):
|
||||
def _calculate_insert_offset(self, insert_point, symbol_width, symbol_height):
|
||||
"""
|
||||
Berechnet den Offset vom Einfügepunkt zur linken oberen Ecke
|
||||
|
||||
Args:
|
||||
insert_point: String mit dem Einfügepunkt (z.B. "left_top_corner", "center")
|
||||
symbol_width: Breite des Symbols
|
||||
symbol_height: Höhe des Symbols
|
||||
|
||||
Returns:
|
||||
(offset_x, offset_y) - Offset vom Einfügepunkt zur linken oberen Ecke
|
||||
"""
|
||||
offset_map = {
|
||||
'left_top_corner': (0, 0),
|
||||
'left_bottom_corner': (0, symbol_height),
|
||||
'right_top_corner': (-symbol_width, 0),
|
||||
'right_bottom_corner': (-symbol_width, symbol_height),
|
||||
'center': (-symbol_width / 2, symbol_height / 2),
|
||||
'top_center': (-symbol_width / 2, 0),
|
||||
'bottom_center': (-symbol_width / 2, symbol_height)
|
||||
}
|
||||
|
||||
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'):
|
||||
"""
|
||||
Generiert eine Gruppe von MA-Symbolen
|
||||
|
||||
@@ -584,6 +609,7 @@ class TestDataGenerator:
|
||||
fixed_height: Fixe Symbol-Höhe
|
||||
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")
|
||||
"""
|
||||
name = group['name']
|
||||
count = group['count']
|
||||
@@ -607,6 +633,9 @@ class TestDataGenerator:
|
||||
attributes = ma_defaults.get('attributes', {})
|
||||
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)
|
||||
|
||||
# Generiere Symbole basierend auf Layout-Typ
|
||||
for i in range(count):
|
||||
if layout_type == 'single':
|
||||
@@ -622,12 +651,15 @@ class TestDataGenerator:
|
||||
print(f"Unbekannter Layout-Typ: {layout_type}")
|
||||
continue
|
||||
|
||||
# Berechne die linke obere Ecke basierend auf dem Einfügepunkt
|
||||
left_top_x = x + offset_x
|
||||
left_top_y = y + offset_y
|
||||
|
||||
|
||||
# DXF-Block wird an der linken unteren Ecke eingefügt
|
||||
# (da der io-Block bei (0,0) startet und nach oben geht)
|
||||
blockref = self.msp.add_blockref(
|
||||
'io',
|
||||
#insert=(block_insert_x, block_insert_y),
|
||||
insert=(x, y),
|
||||
insert=(left_top_x, left_top_y - fixed_height),
|
||||
dxfattribs={'layer': layer}
|
||||
)
|
||||
|
||||
@@ -637,11 +669,12 @@ class TestDataGenerator:
|
||||
attrib_values['BEZEICHNUNG'] = f"Motor {io_pattern}"
|
||||
|
||||
blockref.add_auto_attribs(attrib_values)
|
||||
|
||||
|
||||
# Speichere Symbol-Position für spätere Validierung
|
||||
# Speichere immer die linke obere Ecke
|
||||
self.generated_symbols.append({
|
||||
'x': x,
|
||||
'y': y,
|
||||
'x': left_top_x,
|
||||
'y': left_top_y,
|
||||
'io': io_pattern,
|
||||
'group_name': name,
|
||||
'layer': layer
|
||||
@@ -668,9 +701,13 @@ class TestDataGenerator:
|
||||
spacing_factor = layout.get('symbol_spacing_factor', 1.2)
|
||||
y_offsets_list = layout.get('horizontal_offset_y_offsets', [0, -50, 50])
|
||||
|
||||
# Hole Einfügepunkt aus Config (default: left_top_corner)
|
||||
ma_frame = general.get('ma_frame', {})
|
||||
insert_point = ma_frame.get('insert_point', 'left_top_corner')
|
||||
|
||||
# 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)
|
||||
self._generate_ma_group(group, ma_defaults, width_per_char, fixed_height, spacing_factor, y_offsets_list, insert_point)
|
||||
|
||||
# Generiere Renamer-Rahmen aus Config
|
||||
for frame_config in scene.get('renaming_frames', []):
|
||||
@@ -704,16 +741,46 @@ class TestDataGenerator:
|
||||
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
|
||||
return (rect_x <= x <= rect_x + rect_width and
|
||||
rect_y <= y <= rect_y + rect_height)
|
||||
|
||||
def _calculate_shift_to_rectangle(self, point, rect_x, rect_y, rect_width, rect_height):
|
||||
"""
|
||||
Berechnet den minimalen Verschiebungsvektor, um einen Punkt in ein Rechteck zu verschieben.
|
||||
|
||||
Args:
|
||||
point: (x, y) Tupel
|
||||
rect_x, rect_y: Linke untere Ecke des Rechtecks
|
||||
rect_width, rect_height: Breite und Höhe des Rechtecks
|
||||
|
||||
Returns:
|
||||
(delta_x, delta_y) - Verschiebungsvektor
|
||||
"""
|
||||
x, y = point
|
||||
delta_x = 0
|
||||
delta_y = 0
|
||||
|
||||
# Berechne Verschiebung in x-Richtung
|
||||
if x < rect_x:
|
||||
delta_x = rect_x - x
|
||||
elif x > rect_x + rect_width:
|
||||
delta_x = (rect_x + rect_width) - x
|
||||
|
||||
# Berechne Verschiebung in y-Richtung
|
||||
if y < rect_y:
|
||||
delta_y = rect_y - y
|
||||
elif y > rect_y + rect_height:
|
||||
delta_y = (rect_y + rect_height) - y
|
||||
|
||||
return (delta_x, delta_y)
|
||||
|
||||
def _point_in_polygon(self, point, polygon):
|
||||
"""
|
||||
Prüft, ob ein Punkt innerhalb eines Polygons liegt (Ray-casting Algorithmus).
|
||||
@@ -736,34 +803,109 @@ class TestDataGenerator:
|
||||
|
||||
return inside
|
||||
|
||||
def _circle_intersects_polyline(self, center, radius, polyline):
|
||||
"""
|
||||
Prüft ob ein Kreis mit Mittelpunkt 'center' und Radius 'radius'
|
||||
irgendein Segment der Polylinie schneidet.
|
||||
|
||||
Args:
|
||||
center: (x, y) Mittelpunkt des Kreises
|
||||
radius: Radius des Kreises
|
||||
polyline: Liste von (x, y) Punkten der Polylinie
|
||||
|
||||
Returns:
|
||||
True wenn der Kreis die Polylinie schneidet
|
||||
"""
|
||||
cx, cy = center
|
||||
|
||||
# Prüfe jedes Segment der Polylinie
|
||||
for i in range(len(polyline) - 1):
|
||||
p1 = polyline[i]
|
||||
p2 = polyline[i + 1]
|
||||
|
||||
# Berechne den kürzesten Abstand vom Kreis-Mittelpunkt zum Linien-Segment
|
||||
dist = self._distance_point_to_segment(center, p1, p2)
|
||||
|
||||
# Wenn Abstand <= Radius, dann gibt es eine Schnittmenge
|
||||
if dist <= radius:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _distance_point_to_segment(self, point, seg_start, seg_end):
|
||||
"""
|
||||
Berechnet den kürzesten Abstand von einem Punkt zu einem Linien-Segment.
|
||||
|
||||
Args:
|
||||
point: (x, y) Punkt
|
||||
seg_start: (x, y) Start des Segments
|
||||
seg_end: (x, y) Ende des Segments
|
||||
|
||||
Returns:
|
||||
Kürzester Abstand
|
||||
"""
|
||||
px, py = point
|
||||
x1, y1 = seg_start
|
||||
x2, y2 = seg_end
|
||||
|
||||
# Vektor von seg_start zu seg_end
|
||||
dx = x2 - x1
|
||||
dy = y2 - y1
|
||||
|
||||
# Spezialfall: Segment ist ein Punkt
|
||||
if dx == 0 and dy == 0:
|
||||
return ((px - x1)**2 + (py - y1)**2)**0.5
|
||||
|
||||
# Parameter t für die Projektion des Punktes auf die Linie
|
||||
# t = 0 bedeutet seg_start, t = 1 bedeutet seg_end
|
||||
t = max(0, min(1, ((px - x1) * dx + (py - y1) * dy) / (dx * dx + dy * dy)))
|
||||
|
||||
# Nächster Punkt auf dem Segment
|
||||
closest_x = x1 + t * dx
|
||||
closest_y = y1 + t * dy
|
||||
|
||||
# Abstand vom Punkt zum nächsten Punkt auf dem Segment
|
||||
return ((px - closest_x)**2 + (py - closest_y)**2)**0.5
|
||||
|
||||
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.
|
||||
|
||||
Für POLYLINE_PATH Frames: Prüft ob die Polylinie einen Kreis um das Symbol schneidet
|
||||
Für andere Frames: Prüft ob das Symbol innerhalb des Frames liegt
|
||||
|
||||
Gibt Warnungen aus für Symbole, die nicht die Kriterien erfüllen.
|
||||
"""
|
||||
if not self.generated_symbols:
|
||||
return
|
||||
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("Prüfe ob alle generierten Symbole in Renamer-Frames liegen")
|
||||
print("="*60)
|
||||
|
||||
|
||||
# Hole Symbol-Dimensionen aus Config
|
||||
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)
|
||||
|
||||
# Sammle alle Frame-Boundaries
|
||||
frame_boundaries = []
|
||||
for frame_config in scene.get('renaming_frames', []):
|
||||
frame_type = frame_config.get('type', 'rectangle')
|
||||
direction = frame_config.get('direction', '')
|
||||
layer_name = frame_config.get('layer_name', '')
|
||||
|
||||
if frame_type == 'polyline_path':
|
||||
|
||||
if frame_type == 'polyline_path' or direction == '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]
|
||||
polyline = [(p['x'], p['y']) for p in path_points]
|
||||
frame_boundaries.append({
|
||||
'type': 'polygon',
|
||||
'boundary': polygon,
|
||||
'validation_type': 'polyline_path',
|
||||
'polyline': polyline,
|
||||
'name': frame_config.get('name', 'UNKNOWN'),
|
||||
'layer_name': layer_name
|
||||
'layer_name': layer_name,
|
||||
'direction': direction
|
||||
})
|
||||
else:
|
||||
# Rechteck: berechne Eckpunkte
|
||||
@@ -771,7 +913,7 @@ class TestDataGenerator:
|
||||
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),
|
||||
@@ -780,29 +922,60 @@ class TestDataGenerator:
|
||||
(rect_x, rect_y + rect_height)
|
||||
]
|
||||
frame_boundaries.append({
|
||||
'type': 'rectangle',
|
||||
'validation_type': 'containment',
|
||||
'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
|
||||
'layer_name': layer_name,
|
||||
'direction': direction
|
||||
})
|
||||
|
||||
|
||||
# Prüfe jedes generierte Symbol
|
||||
symbols_outside = []
|
||||
for symbol in self.generated_symbols:
|
||||
symbol_point = (symbol['x'], symbol['y'])
|
||||
# Berechne Symbol-Dimensionen
|
||||
io_text = symbol['io']
|
||||
symbol_width = len(io_text) * width_per_char
|
||||
symbol_height = fixed_height
|
||||
|
||||
# Berechne Mittelpunkt des Symbols
|
||||
# symbol['x'], symbol['y'] ist die linke obere Ecke
|
||||
symbol_center_x = symbol['x'] + symbol_width / 2
|
||||
symbol_center_y = symbol['y'] - symbol_height / 2
|
||||
symbol_point = (symbol_center_x, symbol_center_y)
|
||||
|
||||
# Radius für Kreis-Test
|
||||
symbol_radius = max(symbol_width, symbol_height) / 2
|
||||
|
||||
in_any_frame = False
|
||||
|
||||
validation_type_used = None
|
||||
|
||||
closest_frame = None
|
||||
min_shift_distance = float('inf')
|
||||
best_shift = (0, 0)
|
||||
|
||||
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':
|
||||
|
||||
# Speichere den Validierungstyp für Fehlermeldung
|
||||
if validation_type_used is None:
|
||||
validation_type_used = frame['validation_type']
|
||||
|
||||
# Wähle Validierungsmethode basierend auf Frame-Typ
|
||||
if frame['validation_type'] == 'polyline_path':
|
||||
# POLYLINE_PATH: Prüfe ob Polylinie den Kreis um das Symbol schneidet
|
||||
if self._circle_intersects_polyline(symbol_point, symbol_radius, frame['polyline']):
|
||||
in_any_frame = True
|
||||
break
|
||||
# Für Polyline-Pfade: Berechne minimalen Abstand zur Polylinie
|
||||
# (vereinfacht: nicht implementiert, da komplex)
|
||||
else:
|
||||
# Containment: Prüfe ob Symbol innerhalb des Frames liegt
|
||||
if self._point_in_rectangle(
|
||||
symbol_point,
|
||||
frame['rect_x'],
|
||||
@@ -812,23 +985,79 @@ class TestDataGenerator:
|
||||
):
|
||||
in_any_frame = True
|
||||
break
|
||||
else: # polygon
|
||||
if self._point_in_polygon(symbol_point, frame['boundary']):
|
||||
in_any_frame = True
|
||||
break
|
||||
|
||||
|
||||
# Berechne Verschiebung für diesen Frame
|
||||
shift_x, shift_y = self._calculate_shift_to_rectangle(
|
||||
symbol_point,
|
||||
frame['rect_x'],
|
||||
frame['rect_y'],
|
||||
frame['rect_width'],
|
||||
frame['rect_height']
|
||||
)
|
||||
shift_distance = (shift_x**2 + shift_y**2)**0.5
|
||||
|
||||
# Speichere den nächsten Frame
|
||||
if shift_distance < min_shift_distance:
|
||||
min_shift_distance = shift_distance
|
||||
best_shift = (shift_x, shift_y)
|
||||
closest_frame = frame
|
||||
|
||||
if not in_any_frame:
|
||||
symbols_outside.append(symbol)
|
||||
|
||||
# Gib Warnungen aus
|
||||
symbol_with_type = symbol.copy()
|
||||
symbol_with_type['validation_type'] = validation_type_used
|
||||
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'
|
||||
symbols_outside.append(symbol_with_type)
|
||||
|
||||
# Gib Warnungen aus und zeichne Fehlerkreise
|
||||
if symbols_outside:
|
||||
print(f"\nWARNUNG: {len(symbols_outside)} Symbol(e) liegen außerhalb aller Renamer-Frames:")
|
||||
print(f"\nWARNUNG: {len(symbols_outside)} Symbol(e) erfüllen nicht die Frame-Kriterien:")
|
||||
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")
|
||||
base_msg = (f" Symbol IO='{symbol['io']}' aus Gruppe '{symbol['group_name']}' "
|
||||
f"an Position ({symbol['x']:.1f}, {symbol['y']:.1f}) "
|
||||
f"auf Layer '{symbol['layer']}'")
|
||||
|
||||
if symbol.get('validation_type') == 'polyline_path':
|
||||
print(f"{base_msg}: "
|
||||
f"Die Polylinie schneidet nicht den Umkreis des Symbols "
|
||||
f"(Radius: {symbol['symbol_radius']:.1f})")
|
||||
else:
|
||||
shift_x = symbol.get('shift_x', 0)
|
||||
shift_y = symbol.get('shift_y', 0)
|
||||
closest_frame = symbol.get('closest_frame', 'UNKNOWN')
|
||||
|
||||
if shift_x != 0 or shift_y != 0:
|
||||
print(f"{base_msg}: "
|
||||
f"Symbol liegt nicht innerhalb des Renamer-Rahmens '{closest_frame}'. "
|
||||
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'])
|
||||
else:
|
||||
print(f"\n✓ Alle {len(self.generated_symbols)} generierten Symbole liegen in Renamer-Frames")
|
||||
print(f"\nOK: Alle {len(self.generated_symbols)} generierten Symbole erfüllen die Frame-Kriterien")
|
||||
|
||||
def _draw_error_circle(self, center_x, center_y, radius):
|
||||
"""
|
||||
Zeichnet einen roten Kreis um ein fehlerhaftes Symbol in die DXF-Datei.
|
||||
|
||||
Args:
|
||||
center_x: X-Koordinate des Kreismittelpunkts
|
||||
center_y: Y-Koordinate des Kreismittelpunkts
|
||||
radius: Radius des Kreises
|
||||
"""
|
||||
self.msp.add_circle(
|
||||
center=(center_x, center_y),
|
||||
radius=radius,
|
||||
dxfattribs={
|
||||
'layer': 'VALIDATION_ERROR',
|
||||
'color': 1 # Rot
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -858,8 +1087,42 @@ class TestDataGenerator:
|
||||
|
||||
def main():
|
||||
"""Hauptfunktion mit Argparse"""
|
||||
# Versuche Config zu lesen für --help Ausgabe
|
||||
default_config = 'create_tests.json'
|
||||
config_path = Path(PROJECT_CFG) / default_config
|
||||
available_scenes = []
|
||||
scene_descriptions = {}
|
||||
|
||||
if config_path.exists():
|
||||
try:
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
config = json.load(f)
|
||||
if 'test_scenes' in config:
|
||||
for scene_name, scene_data in config['test_scenes'].items():
|
||||
available_scenes.append(scene_name)
|
||||
if 'description' in scene_data:
|
||||
scene_descriptions[scene_name] = scene_data['description']
|
||||
except Exception:
|
||||
pass # Ignoriere Fehler beim Lesen für --help
|
||||
|
||||
# Erstelle Epilog mit verfügbaren Szenen
|
||||
epilog_text = "\nVerfügbare Test-Szenen:\n"
|
||||
if available_scenes:
|
||||
for scene in sorted(available_scenes):
|
||||
desc = scene_descriptions.get(scene, '')
|
||||
if desc:
|
||||
epilog_text += f" {scene}: {desc}\n"
|
||||
else:
|
||||
epilog_text += f" {scene}\n"
|
||||
epilog_text += f"\n(aus Konfigurationsdatei: {config_path})"
|
||||
else:
|
||||
epilog_text += " (keine Szenen konfiguriert oder Config-Datei nicht gefunden)\n"
|
||||
epilog_text += f" Standard-Pfad: {config_path}"
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Erzeugt Test-DXF-Dateien für verschiedene Testszenarien'
|
||||
description='Erzeugt Test-DXF-Dateien für verschiedene Testszenarien',
|
||||
epilog=epilog_text,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -886,8 +1149,8 @@ def main():
|
||||
parser.add_argument(
|
||||
'--config',
|
||||
type=str,
|
||||
default='create_tests.json',
|
||||
help='JSON-Konfigurationsdatei (Standard: create_tests.json)'
|
||||
default=default_config,
|
||||
help=f'JSON-Konfigurationsdatei (Standard: {default_config})'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
+30
-7
@@ -693,7 +693,35 @@ def process_renamer_blocks(doc, msp, renamer_layers, error_collector):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='Nummeriert Symbole in DXF-Dateien basierend auf Renamer-Blöcken', prog='create_numbers')
|
||||
# Lese Umgebungsvariablen vor argparse, um Config-Info in Hilfe anzuzeigen
|
||||
out_dir = check_environment_var('PROJECT_DATA')
|
||||
work_dir = check_environment_var('PROJECT_WORK')
|
||||
config_dir = check_environment_var("PROJECT_CFG")
|
||||
|
||||
# Versuche Config zu lesen für --help Ausgabe
|
||||
config_path = Path(config_dir) / "enumerate.cfg"
|
||||
available_layers = []
|
||||
if config_path.exists():
|
||||
try:
|
||||
available_layers = read_config_layers(config_path)
|
||||
except Exception:
|
||||
pass # Ignoriere Fehler beim Lesen für --help
|
||||
|
||||
# Erstelle Epilog mit verfügbaren Layern
|
||||
epilog_text = "\nKonfigurierte Renamer-Layer:\n"
|
||||
if available_layers:
|
||||
epilog_text += " " + ", ".join(available_layers) + "\n"
|
||||
epilog_text += f"\n(aus Konfigurationsdatei: {config_path})"
|
||||
else:
|
||||
epilog_text += " (keine Layer konfiguriert oder Config-Datei nicht gefunden)\n"
|
||||
epilog_text += f" Standard-Pfad: {config_path}"
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Nummeriert Symbole in DXF-Dateien basierend auf Renamer-Blöcken',
|
||||
prog='create_numbers',
|
||||
epilog=epilog_text,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
parser.add_argument('-f', '--filename', action='store', required=True, help='DXF-Datei die verarbeitet werden soll', metavar='myfile.dxf')
|
||||
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')
|
||||
@@ -701,10 +729,6 @@ if __name__ == '__main__':
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
out_dir = check_environment_var('PROJECT_DATA')
|
||||
work_dir = check_environment_var('PROJECT_WORK')
|
||||
config_dir = check_environment_var("PROJECT_CFG")
|
||||
|
||||
# Initialisiere ErrorCollector
|
||||
error_collector = ErrorCollector()
|
||||
|
||||
@@ -719,8 +743,7 @@ if __name__ == '__main__':
|
||||
parser.print_help()
|
||||
exit(1)
|
||||
|
||||
# Lese Konfiguration
|
||||
config_path = Path(config_dir) / "enumerate.cfg"
|
||||
# Config-Datei existiert bereits (wurde für --help geprüft)
|
||||
if not config_path.exists():
|
||||
print(f"Konfigurationsdatei nicht gefunden: {config_path}")
|
||||
exit(1)
|
||||
|
||||
Reference in New Issue
Block a user