Zusätzliche Abfragen für Fehler durch überlappende Renaming Frames eingebaut
This commit is contained in:
+320
-21
@@ -883,7 +883,7 @@ class TestDataGenerator:
|
||||
|
||||
# Generiere Renamer-Rahmen aus Config
|
||||
for frame_config in scene.get('renaming_frames', []):
|
||||
frame_type = frame_config.get('type', 'rectangle')
|
||||
frame_type = frame_config.get('type', 'rectangle')
|
||||
|
||||
if frame_type == 'polyline_path':
|
||||
# Polyline-Pfad Frame
|
||||
@@ -933,6 +933,245 @@ class TestDataGenerator:
|
||||
# Prüfe ob alle generierten Symbole in einem Renamer-Frame liegen
|
||||
self._validate_symbols_in_frames(scene)
|
||||
|
||||
# Prüfe ob sich Renamer-Rechtecke überlappen (außer POLYLINE_PATH)
|
||||
self._check_overlapping_frames(scene)
|
||||
|
||||
def _rectangles_overlap(self, rect1, rect2):
|
||||
"""
|
||||
Prüft ob sich zwei Rechtecke überlappen.
|
||||
|
||||
Args:
|
||||
rect1: Dictionary mit 'x', 'y', 'width', 'height'
|
||||
rect2: Dictionary mit 'x', 'y', 'width', 'height'
|
||||
|
||||
Returns:
|
||||
True wenn sich die Rechtecke überlappen
|
||||
"""
|
||||
# Rechteck 1
|
||||
r1_left = rect1['x']
|
||||
r1_right = rect1['x'] + rect1['width']
|
||||
r1_bottom = rect1['y']
|
||||
r1_top = rect1['y'] + rect1['height']
|
||||
|
||||
# Rechteck 2
|
||||
r2_left = rect2['x']
|
||||
r2_right = rect2['x'] + rect2['width']
|
||||
r2_bottom = rect2['y']
|
||||
r2_top = rect2['y'] + rect2['height']
|
||||
|
||||
# Prüfe ob keine Überlappung (dann ist das Gegenteil eine Überlappung)
|
||||
no_overlap = (r1_right <= r2_left or # R1 ist links von R2
|
||||
r2_right <= r1_left or # R2 ist links von R1
|
||||
r1_top <= r2_bottom or # R1 ist unter R2
|
||||
r2_top <= r1_bottom) # R2 ist unter R1
|
||||
|
||||
return not no_overlap
|
||||
|
||||
def _calculate_overlap_area(self, rect1, rect2):
|
||||
"""
|
||||
Berechnet die Überlappungsfläche zweier Rechtecke.
|
||||
|
||||
Args:
|
||||
rect1: Dictionary mit 'x', 'y', 'width', 'height'
|
||||
rect2: Dictionary mit 'x', 'y', 'width', 'height'
|
||||
|
||||
Returns:
|
||||
Überlappungsfläche in Quadrateinheiten
|
||||
"""
|
||||
# Rechteck 1
|
||||
r1_left = rect1['x']
|
||||
r1_right = rect1['x'] + rect1['width']
|
||||
r1_bottom = rect1['y']
|
||||
r1_top = rect1['y'] + rect1['height']
|
||||
|
||||
# Rechteck 2
|
||||
r2_left = rect2['x']
|
||||
r2_right = rect2['x'] + rect2['width']
|
||||
r2_bottom = rect2['y']
|
||||
r2_top = rect2['y'] + rect2['height']
|
||||
|
||||
# Berechne überlappenden Bereich
|
||||
overlap_left = max(r1_left, r2_left)
|
||||
overlap_right = min(r1_right, r2_right)
|
||||
overlap_bottom = max(r1_bottom, r2_bottom)
|
||||
overlap_top = min(r1_top, r2_top)
|
||||
|
||||
# Wenn keine Überlappung, gib 0 zurück
|
||||
if overlap_right <= overlap_left or overlap_top <= overlap_bottom:
|
||||
return 0
|
||||
|
||||
# Berechne Fläche
|
||||
overlap_width = overlap_right - overlap_left
|
||||
overlap_height = overlap_top - overlap_bottom
|
||||
return overlap_width * overlap_height
|
||||
|
||||
def _calculate_overlap_recommendations(self, frame1, frame2):
|
||||
"""
|
||||
Berechnet Empfehlungen zur Behebung einer Überlappung zwischen zwei Frames.
|
||||
|
||||
Args:
|
||||
frame1: Dictionary mit 'x', 'y', 'width', 'height', 'name'
|
||||
frame2: Dictionary mit 'x', 'y', 'width', 'height', 'name'
|
||||
|
||||
Returns:
|
||||
Liste von Empfehlungs-Strings
|
||||
"""
|
||||
recommendations = []
|
||||
|
||||
# Berechne Kanten
|
||||
f1_left = frame1['x']
|
||||
f1_right = frame1['x'] + frame1['width']
|
||||
f1_bottom = frame1['y']
|
||||
f1_top = frame1['y'] + frame1['height']
|
||||
|
||||
f2_left = frame2['x']
|
||||
f2_right = frame2['x'] + frame2['width']
|
||||
f2_bottom = frame2['y']
|
||||
f2_top = frame2['y'] + frame2['height']
|
||||
|
||||
# Berechne Überlappungsbereiche
|
||||
overlap_left = max(f1_left, f2_left)
|
||||
overlap_right = min(f1_right, f2_right)
|
||||
overlap_bottom = max(f1_bottom, f2_bottom)
|
||||
overlap_top = min(f1_top, f2_top)
|
||||
|
||||
overlap_width = overlap_right - overlap_left
|
||||
overlap_height = overlap_top - overlap_bottom
|
||||
|
||||
# Horizontale Überlappung
|
||||
if overlap_width > 0:
|
||||
# Frame 1 ist links von Frame 2
|
||||
if f1_left < f2_left:
|
||||
# Option 1: Frame 1 width reduzieren
|
||||
new_width1 = f1_right - f1_left - overlap_width - 1
|
||||
recommendations.append(
|
||||
f"Reduziere '{frame1['name']}' width von {frame1['width']} auf {new_width1:.0f} "
|
||||
f"(um {overlap_width + 1:.0f} Einheiten)"
|
||||
)
|
||||
# Option 2: Frame 2 nach rechts verschieben
|
||||
new_x2 = f2_left + overlap_width + 1
|
||||
recommendations.append(
|
||||
f"Verschiebe '{frame2['name']}' x von {frame2['x']} auf {new_x2:.0f} "
|
||||
f"(um {overlap_width + 1:.0f} Einheiten nach rechts)"
|
||||
)
|
||||
# Frame 2 ist links von Frame 1
|
||||
else:
|
||||
# Option 1: Frame 2 width reduzieren
|
||||
new_width2 = f2_right - f2_left - overlap_width - 1
|
||||
recommendations.append(
|
||||
f"Reduziere '{frame2['name']}' width von {frame2['width']} auf {new_width2:.0f} "
|
||||
f"(um {overlap_width + 1:.0f} Einheiten)"
|
||||
)
|
||||
# Option 2: Frame 1 nach rechts verschieben
|
||||
new_x1 = f1_left + overlap_width + 1
|
||||
recommendations.append(
|
||||
f"Verschiebe '{frame1['name']}' x von {frame1['x']} auf {new_x1:.0f} "
|
||||
f"(um {overlap_width + 1:.0f} Einheiten nach rechts)"
|
||||
)
|
||||
|
||||
# Vertikale Überlappung
|
||||
if overlap_height > 0:
|
||||
# Frame 1 ist unter Frame 2
|
||||
if f1_bottom < f2_bottom:
|
||||
# Option 1: Frame 1 height reduzieren
|
||||
new_height1 = f1_top - f1_bottom - overlap_height - 1
|
||||
recommendations.append(
|
||||
f"Reduziere '{frame1['name']}' height von {frame1['height']} auf {new_height1:.0f} "
|
||||
f"(um {overlap_height + 1:.0f} Einheiten)"
|
||||
)
|
||||
# Option 2: Frame 2 nach oben verschieben
|
||||
new_y2 = f2_bottom + overlap_height + 1
|
||||
recommendations.append(
|
||||
f"Verschiebe '{frame2['name']}' y von {frame2['y']} auf {new_y2:.0f} "
|
||||
f"(um {overlap_height + 1:.0f} Einheiten nach oben)"
|
||||
)
|
||||
# Frame 2 ist unter Frame 1
|
||||
else:
|
||||
# Option 1: Frame 2 height reduzieren
|
||||
new_height2 = f2_top - f2_bottom - overlap_height - 1
|
||||
recommendations.append(
|
||||
f"Reduziere '{frame2['name']}' height von {frame2['height']} auf {new_height2:.0f} "
|
||||
f"(um {overlap_height + 1:.0f} Einheiten)"
|
||||
)
|
||||
# Option 2: Frame 1 nach oben verschieben
|
||||
new_y1 = f1_bottom + overlap_height + 1
|
||||
recommendations.append(
|
||||
f"Verschiebe '{frame1['name']}' y von {frame1['y']} auf {new_y1:.0f} "
|
||||
f"(um {overlap_height + 1:.0f} Einheiten nach oben)"
|
||||
)
|
||||
|
||||
return recommendations
|
||||
|
||||
def _check_overlapping_frames(self, scene):
|
||||
"""
|
||||
Prüft ob sich Renamer-Rechtecke überlappen.
|
||||
Polyline-Pfade (DIRECTION=POLYLINE_PATH) werden nicht geprüft.
|
||||
|
||||
Args:
|
||||
scene: Scene-Configuration Dictionary
|
||||
"""
|
||||
print("\n" + "="*60)
|
||||
print("Prüfe auf überlappende Renamer-Rechtecke")
|
||||
print("="*60)
|
||||
|
||||
# Sammle alle Rechteck-Frames (ohne POLYLINE_PATH)
|
||||
rectangle_frames = []
|
||||
for frame_config in scene.get('renaming_frames', []):
|
||||
frame_type = frame_config.get('type', 'rectangle')
|
||||
direction = frame_config.get('direction', '')
|
||||
|
||||
# Überspringe POLYLINE_PATH Frames
|
||||
if frame_type == 'polyline_path' or direction == 'POLYLINE_PATH':
|
||||
continue
|
||||
|
||||
# Überspringe Frames ohne Positionsangaben
|
||||
if 'x' not in frame_config or 'y' not in frame_config:
|
||||
continue
|
||||
|
||||
rectangle_frames.append({
|
||||
'name': frame_config.get('name', 'UNKNOWN'),
|
||||
'x': frame_config['x'],
|
||||
'y': frame_config['y'],
|
||||
'width': frame_config.get('width', 0),
|
||||
'height': frame_config.get('height', 0),
|
||||
'direction': direction
|
||||
})
|
||||
|
||||
# Prüfe alle Paare von Rechtecken auf Überlappung
|
||||
overlaps_found = []
|
||||
for i in range(len(rectangle_frames)):
|
||||
for j in range(i + 1, len(rectangle_frames)):
|
||||
frame1 = rectangle_frames[i]
|
||||
frame2 = rectangle_frames[j]
|
||||
|
||||
if self._rectangles_overlap(frame1, frame2):
|
||||
overlap_area = self._calculate_overlap_area(frame1, frame2)
|
||||
recommendations = self._calculate_overlap_recommendations(frame1, frame2)
|
||||
overlaps_found.append({
|
||||
'frame1': frame1,
|
||||
'frame2': frame2,
|
||||
'overlap_area': overlap_area,
|
||||
'recommendations': recommendations
|
||||
})
|
||||
|
||||
# Gib Warnungen aus
|
||||
if overlaps_found:
|
||||
print(f"\nWARNUNG: {len(overlaps_found)} Überlappung(en) zwischen Renamer-Rechtecken gefunden:")
|
||||
for overlap in overlaps_found:
|
||||
f1 = overlap['frame1']
|
||||
f2 = overlap['frame2']
|
||||
area = overlap['overlap_area']
|
||||
print(f" Frame '{f1['name']}' (Position: {f1['x']}, {f1['y']}, "
|
||||
f"Größe: {f1['width']}x{f1['height']}) "
|
||||
f"überlappt mit Frame '{f2['name']}' "
|
||||
f"(Position: {f2['x']}, {f2['y']}, Größe: {f2['width']}x{f2['height']})")
|
||||
print(f" Überlappungsfläche: {area:.1f} Quadrateinheiten")
|
||||
print(f" Empfohlene Änderungen (wähle eine):")
|
||||
for rec in overlap['recommendations']:
|
||||
print(f" - {rec}")
|
||||
else:
|
||||
print(f"\nOK: Keine Überlappungen zwischen {len(rectangle_frames)} Renamer-Rechtecken gefunden")
|
||||
|
||||
def _point_in_rectangle(self, point, rect_x, rect_y, rect_width, rect_height):
|
||||
"""
|
||||
Prüft ob ein Punkt innerhalb eines Rechtecks liegt.
|
||||
@@ -1347,15 +1586,21 @@ def main():
|
||||
parser.add_argument(
|
||||
'--scene',
|
||||
type=str,
|
||||
required=True,
|
||||
default=None,
|
||||
help='Name der Testszene (z.B. Nummerierung1, Nummerierung2). Muss in der Config existieren.'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--all',
|
||||
action='store_true',
|
||||
help='Erzeugt alle konfigurierten Testszenen'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--filename',
|
||||
type=str,
|
||||
default=None,
|
||||
help='Name der zu erzeugenden Testdatei (z.B. Nummerierung1.dxf). Falls nicht angegeben, wird scene + .dxf verwendet.'
|
||||
help='Name der zu erzeugenden Testdatei (z.B. Nummerierung1.dxf). Falls nicht angegeben, wird scene + .dxf verwendet. Wird bei --all ignoriert.'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -1380,29 +1625,83 @@ def main():
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Bestimme Filename: entweder angegeben oder aus scene ableiten
|
||||
if args.filename:
|
||||
filename = args.filename
|
||||
# Validierung: entweder --scene oder --all muss angegeben sein
|
||||
if not args.scene and not args.all:
|
||||
parser.error("Entweder --scene oder --all muss angegeben werden")
|
||||
|
||||
if args.scene and args.all:
|
||||
parser.error("--scene und --all können nicht zusammen verwendet werden")
|
||||
|
||||
# Bestimme welche Szenen zu erzeugen sind
|
||||
if args.all:
|
||||
# Lade Config, um alle Szenen zu bekommen
|
||||
config_path = Path(PROJECT_CFG) / args.config
|
||||
if not config_path.exists():
|
||||
print(f"FEHLER: Config-Datei nicht gefunden: {config_path}")
|
||||
return 1
|
||||
|
||||
try:
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
config = json.load(f)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"FEHLER beim Parsen der Config-Datei: {e}")
|
||||
return 1
|
||||
|
||||
if 'test_scenes' not in config:
|
||||
print("FEHLER: Config enthält keinen 'test_scenes' Abschnitt")
|
||||
return 1
|
||||
|
||||
scenes_to_generate = list(config['test_scenes'].keys())
|
||||
print(f"Erzeuge alle {len(scenes_to_generate)} konfigurierten Testszenen:")
|
||||
for scene in scenes_to_generate:
|
||||
print(f" - {scene}")
|
||||
print()
|
||||
else:
|
||||
filename = args.scene + '.dxf'
|
||||
# Einzelne Szene
|
||||
scenes_to_generate = [args.scene]
|
||||
|
||||
# Stelle sicher, dass Dateiname .dxf Endung hat
|
||||
if not filename.endswith('.dxf'):
|
||||
filename += '.dxf'
|
||||
# Generiere alle Szenen
|
||||
success_count = 0
|
||||
error_count = 0
|
||||
|
||||
# Generiere Testdaten
|
||||
generator = TestDataGenerator(filename, args.scene, config_file=args.config)
|
||||
for scene_name in scenes_to_generate:
|
||||
# Bestimme Filename: entweder angegeben (nur bei einzelner Szene) oder aus scene ableiten
|
||||
if args.filename and not args.all:
|
||||
filename = args.filename
|
||||
else:
|
||||
filename = scene_name + '.dxf'
|
||||
|
||||
if generator.generate():
|
||||
# Zeichne Symbol-Rahmen wenn gewünscht
|
||||
if args.show_symbol_frames:
|
||||
generator.draw_frames_for_generated_symbols()
|
||||
# Stelle sicher, dass Dateiname .dxf Endung hat
|
||||
if not filename.endswith('.dxf'):
|
||||
filename += '.dxf'
|
||||
|
||||
generator.save(args.output_dir)
|
||||
return 0
|
||||
else:
|
||||
print("Fehler bei der Generierung der Testdaten")
|
||||
return 1
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Erzeuge Testszene: {scene_name}")
|
||||
print(f"Datei: {filename}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
# Generiere Testdaten
|
||||
generator = TestDataGenerator(filename, scene_name, 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)
|
||||
success_count += 1
|
||||
else:
|
||||
print(f"Fehler bei der Generierung von {scene_name}")
|
||||
error_count += 1
|
||||
|
||||
# Zusammenfassung
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Zusammenfassung:")
|
||||
print(f" Erfolgreich: {success_count}")
|
||||
print(f" Fehler: {error_count}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
return 0 if error_count == 0 else 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user