point_near_polyline impl. um Symbole entlang einer Polylinie zu nummerieren
This commit is contained in:
@@ -1,3 +1,8 @@
|
|||||||
[Layers]
|
[Layers]
|
||||||
ILS_RENAMER
|
ILS_RENAMER
|
||||||
Omniflo_RENAMER
|
Omniflo_RENAMER
|
||||||
|
|
||||||
|
[Parameters]
|
||||||
|
# Maximale Distanz (in DXF-Einheiten) eines Symbols zur Polylinie bei POLYLINE_PATH
|
||||||
|
# Symbole die weiter entfernt sind, werden nicht zur Nummerierung berücksichtigt
|
||||||
|
polyline_max_distance = 500.0
|
||||||
|
|||||||
+227
-7
@@ -83,6 +83,50 @@ def read_config_layers(config_path: Path) -> list:
|
|||||||
return layers
|
return layers
|
||||||
|
|
||||||
|
|
||||||
|
def read_config_parameters(config_path: Path) -> dict:
|
||||||
|
"""
|
||||||
|
Liest die enumerate.cfg und gibt die Parameter zurück.
|
||||||
|
Die Konfiguration hat Key-Value-Paare unter [Parameters]:
|
||||||
|
[Parameters]
|
||||||
|
polyline_max_distance = 500.0
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary mit Parametern und ihren Werten
|
||||||
|
"""
|
||||||
|
parameters = {
|
||||||
|
'polyline_max_distance': 500.0 # Default-Wert
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(config_path, 'r', encoding='utf-8') as f:
|
||||||
|
in_parameters_section = False
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
# Überspringe leere Zeilen und Kommentare
|
||||||
|
if not line or line.startswith('#') or line.startswith(';'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Prüfe ob wir in der [Parameters] Sektion sind
|
||||||
|
if line.startswith('['):
|
||||||
|
in_parameters_section = line.lower() == '[parameters]'
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Wenn in [Parameters], parse Key-Value-Paare
|
||||||
|
if in_parameters_section and '=' in line:
|
||||||
|
key, value = line.split('=', 1)
|
||||||
|
key = key.strip()
|
||||||
|
value = value.strip()
|
||||||
|
|
||||||
|
# Versuche den Wert als Float zu parsen
|
||||||
|
try:
|
||||||
|
parameters[key] = float(value)
|
||||||
|
except ValueError:
|
||||||
|
# Wenn nicht als Float parsbar, speichere als String
|
||||||
|
parameters[key] = value
|
||||||
|
|
||||||
|
return parameters
|
||||||
|
|
||||||
|
|
||||||
def is_rectangle_polyline(points):
|
def is_rectangle_polyline(points):
|
||||||
"""
|
"""
|
||||||
Prüft, ob eine Liste von Punkten ein Rechteck darstellt.
|
Prüft, ob eine Liste von Punkten ein Rechteck darstellt.
|
||||||
@@ -194,9 +238,63 @@ def point_in_polygon(point, polygon):
|
|||||||
return inside
|
return inside
|
||||||
|
|
||||||
|
|
||||||
def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes, error_collector=None):
|
def point_near_polyline(point, polyline_points, max_distance=500.0):
|
||||||
|
"""
|
||||||
|
Prüft ob ein Punkt in der Nähe einer Polylinie liegt.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
point: (x, y) Punkt
|
||||||
|
polyline_points: Liste von (x, y) Punkten der Polylinie
|
||||||
|
max_distance: Maximale Distanz zum nächsten Punkt auf der Polylinie
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True wenn Punkt innerhalb von max_distance zur Polylinie liegt
|
||||||
|
"""
|
||||||
|
if not polyline_points or len(polyline_points) < 2:
|
||||||
|
return False
|
||||||
|
|
||||||
|
min_dist_sq = float('inf')
|
||||||
|
|
||||||
|
# Prüfe Distanz zu jedem Segment der Polylinie
|
||||||
|
for i in range(len(polyline_points) - 1):
|
||||||
|
p1 = polyline_points[i]
|
||||||
|
p2 = polyline_points[i + 1]
|
||||||
|
|
||||||
|
# Berechne die Projektion des Punktes auf das Segment
|
||||||
|
dx = p2[0] - p1[0]
|
||||||
|
dy = p2[1] - p1[1]
|
||||||
|
segment_length_sq = dx * dx + dy * dy
|
||||||
|
|
||||||
|
if segment_length_sq == 0:
|
||||||
|
# Segment ist ein Punkt
|
||||||
|
dist_sq = (point[0] - p1[0])**2 + (point[1] - p1[1])**2
|
||||||
|
min_dist_sq = min(min_dist_sq, dist_sq)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Parameter t für die Projektion (0 = p1, 1 = p2)
|
||||||
|
t = ((point[0] - p1[0]) * dx + (point[1] - p1[1]) * dy) / segment_length_sq
|
||||||
|
t = max(0.0, min(1.0, t)) # Clampe auf [0, 1]
|
||||||
|
|
||||||
|
# Nächster Punkt auf dem Segment
|
||||||
|
proj_x = p1[0] + t * dx
|
||||||
|
proj_y = p1[1] + t * dy
|
||||||
|
|
||||||
|
# Distanz vom Punkt zur Projektion
|
||||||
|
dist_sq = (point[0] - proj_x)**2 + (point[1] - proj_y)**2
|
||||||
|
min_dist_sq = min(min_dist_sq, dist_sq)
|
||||||
|
|
||||||
|
return min_dist_sq <= (max_distance * max_distance)
|
||||||
|
|
||||||
|
|
||||||
|
def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes, error_collector=None, polyline_max_distance=500.0):
|
||||||
"""
|
"""
|
||||||
Findet alle Symbole (INSERT-Blöcke) innerhalb des angegebenen Bereichs auf den Ziel-Layern.
|
Findet alle Symbole (INSERT-Blöcke) innerhalb des angegebenen Bereichs auf den Ziel-Layern.
|
||||||
|
|
||||||
|
Für POLYLINE_PATH: Symbole in der Nähe der Polylinie
|
||||||
|
Für Rechtecke: Symbole innerhalb des Polygons
|
||||||
|
|
||||||
|
Args:
|
||||||
|
polyline_max_distance: Maximale Distanz zur Polylinie (aus Config)
|
||||||
"""
|
"""
|
||||||
symbols = []
|
symbols = []
|
||||||
|
|
||||||
@@ -211,6 +309,10 @@ def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes, erro
|
|||||||
if "LAYER_NAME" in attributes and attributes["LAYER_NAME"]:
|
if "LAYER_NAME" in attributes and attributes["LAYER_NAME"]:
|
||||||
search_layers.append(attributes["LAYER_NAME"])
|
search_layers.append(attributes["LAYER_NAME"])
|
||||||
|
|
||||||
|
# Prüfe ob es POLYLINE_PATH ist
|
||||||
|
direction = attributes.get("DIRECTION", "")
|
||||||
|
is_polyline_path = (direction == "POLYLINE_PATH")
|
||||||
|
|
||||||
# Durchsuche alle INSERT-Blöcke
|
# Durchsuche alle INSERT-Blöcke
|
||||||
for entity in msp.query('INSERT'):
|
for entity in msp.query('INSERT'):
|
||||||
if entity.dxf.layer not in search_layers:
|
if entity.dxf.layer not in search_layers:
|
||||||
@@ -228,7 +330,15 @@ def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes, erro
|
|||||||
point = (pos[0], pos[1])
|
point = (pos[0], pos[1])
|
||||||
|
|
||||||
# Prüfe ob innerhalb des Bereichs
|
# Prüfe ob innerhalb des Bereichs
|
||||||
if point_in_polygon(point, boundary):
|
is_in_boundary = False
|
||||||
|
if is_polyline_path:
|
||||||
|
# Für POLYLINE_PATH: Prüfe Nähe zur Polylinie
|
||||||
|
is_in_boundary = point_near_polyline(point, boundary, max_distance=polyline_max_distance)
|
||||||
|
else:
|
||||||
|
# Für Rechtecke: Prüfe ob innerhalb des Polygons
|
||||||
|
is_in_boundary = point_in_polygon(point, boundary)
|
||||||
|
|
||||||
|
if is_in_boundary:
|
||||||
# Prüfe ob es ein Template ist (enthält @)
|
# Prüfe ob es ein Template ist (enthält @)
|
||||||
has_template = False
|
has_template = False
|
||||||
for value in symbol_attribs.values():
|
for value in symbol_attribs.values():
|
||||||
@@ -247,7 +357,77 @@ def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes, erro
|
|||||||
return symbols
|
return symbols
|
||||||
|
|
||||||
|
|
||||||
def sort_symbols_by_direction(symbols, direction):
|
def calculate_position_along_polyline(symbol_pos, polyline_points):
|
||||||
|
"""
|
||||||
|
Berechnet die Position eines Symbols entlang einer Polylinie.
|
||||||
|
|
||||||
|
Die Position wird als kumulative Distanz vom Start der Polylinie bis zum
|
||||||
|
nächstgelegenen Punkt auf der Polylinie zum Symbol berechnet.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol_pos: (x, y) Position des Symbols
|
||||||
|
polyline_points: Liste von (x, y) Punkten der Polylinie
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Float: Kumulative Distanz entlang der Polylinie
|
||||||
|
"""
|
||||||
|
if not polyline_points or len(polyline_points) < 2:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
min_dist = float('inf')
|
||||||
|
best_segment_idx = 0
|
||||||
|
best_proj_dist = 0.0
|
||||||
|
|
||||||
|
# Finde das nächste Segment und die Projektion auf diesem Segment
|
||||||
|
for i in range(len(polyline_points) - 1):
|
||||||
|
p1 = polyline_points[i]
|
||||||
|
p2 = polyline_points[i + 1]
|
||||||
|
|
||||||
|
# Berechne die Projektion des Symbols auf das Segment
|
||||||
|
# Vektor von p1 zu p2
|
||||||
|
dx = p2[0] - p1[0]
|
||||||
|
dy = p2[1] - p1[1]
|
||||||
|
segment_length_sq = dx * dx + dy * dy
|
||||||
|
|
||||||
|
if segment_length_sq == 0:
|
||||||
|
# Segment ist ein Punkt
|
||||||
|
dist_sq = (symbol_pos[0] - p1[0])**2 + (symbol_pos[1] - p1[1])**2
|
||||||
|
if dist_sq < min_dist:
|
||||||
|
min_dist = dist_sq
|
||||||
|
best_segment_idx = i
|
||||||
|
best_proj_dist = 0.0
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Parameter t für die Projektion (0 = p1, 1 = p2)
|
||||||
|
t = ((symbol_pos[0] - p1[0]) * dx + (symbol_pos[1] - p1[1]) * dy) / segment_length_sq
|
||||||
|
t = max(0.0, min(1.0, t)) # Clampe auf [0, 1]
|
||||||
|
|
||||||
|
# Nächster Punkt auf dem Segment
|
||||||
|
proj_x = p1[0] + t * dx
|
||||||
|
proj_y = p1[1] + t * dy
|
||||||
|
|
||||||
|
# Distanz vom Symbol zur Projektion
|
||||||
|
dist_sq = (symbol_pos[0] - proj_x)**2 + (symbol_pos[1] - proj_y)**2
|
||||||
|
|
||||||
|
if dist_sq < min_dist:
|
||||||
|
min_dist = dist_sq
|
||||||
|
best_segment_idx = i
|
||||||
|
best_proj_dist = t * (segment_length_sq ** 0.5)
|
||||||
|
|
||||||
|
# Berechne kumulative Distanz bis zum besten Segment + Projektion auf dem Segment
|
||||||
|
cumulative_dist = 0.0
|
||||||
|
for i in range(best_segment_idx):
|
||||||
|
p1 = polyline_points[i]
|
||||||
|
p2 = polyline_points[i + 1]
|
||||||
|
segment_length = ((p2[0] - p1[0])**2 + (p2[1] - p1[1])**2) ** 0.5
|
||||||
|
cumulative_dist += segment_length
|
||||||
|
|
||||||
|
cumulative_dist += best_proj_dist
|
||||||
|
|
||||||
|
return cumulative_dist
|
||||||
|
|
||||||
|
|
||||||
|
def sort_symbols_by_direction(symbols, direction, polyline_points=None):
|
||||||
"""
|
"""
|
||||||
Sortiert die Symbole nach der angegebenen Richtung.
|
Sortiert die Symbole nach der angegebenen Richtung.
|
||||||
|
|
||||||
@@ -268,13 +448,33 @@ def sort_symbols_by_direction(symbols, direction):
|
|||||||
LEFT_RIGHT: Spaltenweise von links nach rechts (sortiert nach X, dann Y)
|
LEFT_RIGHT: Spaltenweise von links nach rechts (sortiert nach X, dann Y)
|
||||||
RIGHT_LEFT: Spaltenweise von rechts nach links (sortiert nach -X, dann Y)
|
RIGHT_LEFT: Spaltenweise von rechts nach links (sortiert nach -X, dann Y)
|
||||||
|
|
||||||
|
Spezielle Richtungen:
|
||||||
|
POLYLINE_PATH: Nummerierung entlang einer Polylinie in Reihenfolge der Polylinien-Punkte
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
symbols: Liste von Symbol-Dictionaries mit 'position' (x, y)
|
symbols: Liste von Symbol-Dictionaries mit 'position' (x, y)
|
||||||
direction: DIRECTION-Attribut (z.B. "LEFT_RIGHT", "TOP_BOTTOM/LEFT_RIGHT", "LEFT_RIGHT/TOP_BOTTOM")
|
direction: DIRECTION-Attribut (z.B. "LEFT_RIGHT", "TOP_BOTTOM/LEFT_RIGHT", "POLYLINE_PATH")
|
||||||
|
polyline_points: Optional - Liste von (x, y) Punkten für POLYLINE_PATH Richtung
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Sortierte Liste von Symbolen
|
Sortierte Liste von Symbolen
|
||||||
"""
|
"""
|
||||||
|
# POLYLINE_PATH: Sortiere entlang der Polylinie
|
||||||
|
if direction == "POLYLINE_PATH":
|
||||||
|
if not polyline_points or len(polyline_points) < 2:
|
||||||
|
print(" WARNUNG: POLYLINE_PATH ohne gültige Polylinie - verwende Fallback-Sortierung")
|
||||||
|
return sorted(symbols, key=lambda s: (-s['position'][1], s['position'][0]))
|
||||||
|
|
||||||
|
# Berechne Position entlang der Polylinie für jedes Symbol
|
||||||
|
symbols_with_dist = []
|
||||||
|
for symbol in symbols:
|
||||||
|
dist = calculate_position_along_polyline(symbol['position'], polyline_points)
|
||||||
|
symbols_with_dist.append((dist, symbol))
|
||||||
|
|
||||||
|
# Sortiere nach Distanz entlang der Polylinie
|
||||||
|
symbols_with_dist.sort(key=lambda x: x[0])
|
||||||
|
return [symbol for dist, symbol in symbols_with_dist]
|
||||||
|
|
||||||
# Kombinierte Richtungen (vollständig spezifiziert) - Y/X Reihenfolge
|
# Kombinierte Richtungen (vollständig spezifiziert) - Y/X Reihenfolge
|
||||||
if direction == "TOP_BOTTOM/LEFT_RIGHT" or direction == "TOP_BOTTOM":
|
if direction == "TOP_BOTTOM/LEFT_RIGHT" or direction == "TOP_BOTTOM":
|
||||||
# Von oben nach unten (-Y), in jeder Zeile von links nach rechts (X)
|
# Von oben nach unten (-Y), in jeder Zeile von links nach rechts (X)
|
||||||
@@ -711,8 +911,19 @@ def process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_colle
|
|||||||
error_collector.add_errors({"no_symbols_found": error_msg})
|
error_collector.add_errors({"no_symbols_found": error_msg})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Für POLYLINE_PATH: Hole die Polylinie vom ersten Block
|
||||||
|
polyline_points = None
|
||||||
|
if direction == "POLYLINE_PATH":
|
||||||
|
if group_blocks:
|
||||||
|
insert, boundary, attributes, is_rectangle = group_blocks[0]
|
||||||
|
if boundary and not is_rectangle:
|
||||||
|
polyline_points = boundary
|
||||||
|
print(f" POLYLINE_PATH: Verwende Polylinie mit {len(polyline_points)} Punkten")
|
||||||
|
else:
|
||||||
|
print(f" WARNUNG: POLYLINE_PATH angegeben, aber keine Polylinie gefunden (is_rectangle={is_rectangle})")
|
||||||
|
|
||||||
# Sortiere alle Symbole zusammen nach Richtung
|
# Sortiere alle Symbole zusammen nach Richtung
|
||||||
sorted_symbols = sort_symbols_by_direction(all_symbols, direction)
|
sorted_symbols = sort_symbols_by_direction(all_symbols, direction, polyline_points)
|
||||||
|
|
||||||
# Nummeriere alle Symbole als eine Sequenz
|
# Nummeriere alle Symbole als eine Sequenz
|
||||||
renamed = enumerate_symbols(sorted_symbols, reference_attributes)
|
renamed = enumerate_symbols(sorted_symbols, reference_attributes)
|
||||||
@@ -772,8 +983,17 @@ def check_symbol_in_boundary(symbol, boundary, attributes, renamer_pos):
|
|||||||
- wrong_layer_info: Dictionary mit Fehlerinformationen wenn Layer falsch ist, sonst None
|
- wrong_layer_info: Dictionary mit Fehlerinformationen wenn Layer falsch ist, sonst None
|
||||||
"""
|
"""
|
||||||
# Prüfe ob Symbol geometrisch innerhalb des Bereichs liegt
|
# Prüfe ob Symbol geometrisch innerhalb des Bereichs liegt
|
||||||
if not point_in_polygon(symbol['position'], boundary):
|
direction = attributes.get("DIRECTION", "")
|
||||||
return False, None
|
is_polyline_path = (direction == "POLYLINE_PATH")
|
||||||
|
|
||||||
|
if is_polyline_path:
|
||||||
|
# Für POLYLINE_PATH: Prüfe Nähe zur Polylinie
|
||||||
|
if not point_near_polyline(symbol['position'], boundary, max_distance=500.0):
|
||||||
|
return False, None
|
||||||
|
else:
|
||||||
|
# Für Rechtecke: Prüfe ob innerhalb des Polygons
|
||||||
|
if not point_in_polygon(symbol['position'], boundary):
|
||||||
|
return False, None
|
||||||
|
|
||||||
# Symbol liegt geometrisch im Bereich - prüfe Layer
|
# Symbol liegt geometrisch im Bereich - prüfe Layer
|
||||||
search_layers = get_layer_names_from_attributes(attributes)
|
search_layers = get_layer_names_from_attributes(attributes)
|
||||||
|
|||||||
Reference in New Issue
Block a user