point_near_polyline impl. um Symbole entlang einer Polylinie zu nummerieren
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
[Layers]
|
||||
ILS_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
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
Prüft, ob eine Liste von Punkten ein Rechteck darstellt.
|
||||
@@ -194,9 +238,63 @@ def point_in_polygon(point, polygon):
|
||||
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.
|
||||
|
||||
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 = []
|
||||
|
||||
@@ -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"]:
|
||||
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
|
||||
for entity in msp.query('INSERT'):
|
||||
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])
|
||||
|
||||
# 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 @)
|
||||
has_template = False
|
||||
for value in symbol_attribs.values():
|
||||
@@ -247,7 +357,77 @@ def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes, erro
|
||||
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.
|
||||
|
||||
@@ -268,13 +448,33 @@ def sort_symbols_by_direction(symbols, direction):
|
||||
LEFT_RIGHT: Spaltenweise von links nach rechts (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:
|
||||
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:
|
||||
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
|
||||
if direction == "TOP_BOTTOM/LEFT_RIGHT" or direction == "TOP_BOTTOM":
|
||||
# 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})
|
||||
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
|
||||
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
|
||||
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
|
||||
"""
|
||||
# Prüfe ob Symbol geometrisch innerhalb des Bereichs liegt
|
||||
if not point_in_polygon(symbol['position'], boundary):
|
||||
return False, None
|
||||
direction = attributes.get("DIRECTION", "")
|
||||
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
|
||||
search_layers = get_layer_names_from_attributes(attributes)
|
||||
|
||||
Reference in New Issue
Block a user