Mehrere Regionen können über das Blatt verteilt sein. Sammle alle zusammengehörigen Items in Gruppen
This commit is contained in:
Vendored
+10
@@ -24,6 +24,16 @@
|
|||||||
"program": "${file}",
|
"program": "${file}",
|
||||||
"console": "integratedTerminal"
|
"console": "integratedTerminal"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "create_numbers with Nummerierung1.dxf",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${file}",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"args": [
|
||||||
|
"--filename",
|
||||||
|
"Nummerierung1.dxf" ]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getpositions with easy.dxf",
|
"name": "getpositions with easy.dxf",
|
||||||
"type": "debugpy",
|
"type": "debugpy",
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import os
|
|||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import ezdxf
|
import ezdxf
|
||||||
from ezdxf.math import Vec3
|
|
||||||
|
|
||||||
# Umgebungsvariablen
|
# Umgebungsvariablen
|
||||||
PROJECT = os.getenv('PROJECT', Path(__file__).parent.parent.absolute())
|
PROJECT = os.getenv('PROJECT', Path(__file__).parent.parent.absolute())
|
||||||
|
|||||||
+274
-61
@@ -1,14 +1,6 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import configparser
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import ezdxf
|
|
||||||
from ezdxf.addons import iterdxf
|
|
||||||
from ezdxf.lldxf.const import DXFStructureError
|
|
||||||
|
|
||||||
from error_collector import ErrorCollector, write_json_file
|
from error_collector import ErrorCollector, write_json_file
|
||||||
from utils import check_environment_var, check_file_in_work, dxf_is_binary, get_dxf_file
|
from utils import check_environment_var, check_file_in_work, dxf_is_binary, get_dxf_file
|
||||||
@@ -87,27 +79,90 @@ def read_config_layers(config_path: Path) -> list:
|
|||||||
return layers
|
return layers
|
||||||
|
|
||||||
|
|
||||||
def extract_block_attributes(insert) -> dict:
|
def extract_block_attributes(doc, insert) -> dict:
|
||||||
"""
|
"""
|
||||||
Extrahiert alle Attribute aus einem INSERT-Block.
|
Extrahiert alle Attribute aus einem INSERT-Block.
|
||||||
|
Unterstützt zweistufige Blockstruktur:
|
||||||
|
- Äußerer Block enthält LWPOLYLINE und INSERT zu Attribut-Block
|
||||||
|
- Innerer Attribut-Block enthält die eigentlichen Attribute
|
||||||
|
|
||||||
|
Args:
|
||||||
|
doc: DXF-Dokument
|
||||||
|
insert: INSERT-Entity des äußeren Blocks
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary mit allen gefundenen Attributen
|
||||||
"""
|
"""
|
||||||
attributes = {}
|
attributes = {}
|
||||||
if insert.dxftype() != 'INSERT':
|
if insert.dxftype() != 'INSERT':
|
||||||
return attributes
|
return attributes
|
||||||
|
|
||||||
if insert.has_attrib:
|
# Prüfe zuerst ob der Block direkt Attribute hat (alte Struktur)
|
||||||
for attrib in insert.attribs:
|
# Sammle alle direkten Attribute
|
||||||
|
direct_attribs = list(insert.attribs)
|
||||||
|
if direct_attribs:
|
||||||
|
for attrib in direct_attribs:
|
||||||
tag = attrib.dxf.tag
|
tag = attrib.dxf.tag
|
||||||
value = attrib.dxf.text
|
value = attrib.dxf.text
|
||||||
attributes[tag] = value
|
attributes[tag] = value
|
||||||
|
return attributes
|
||||||
|
|
||||||
|
# Neue zweistufige Struktur: Suche im Block nach einem INSERT mit Attributen
|
||||||
|
try:
|
||||||
|
block_layout = doc.blocks.get(insert.dxf.name)
|
||||||
|
for entity in block_layout:
|
||||||
|
if entity.dxftype() == 'INSERT':
|
||||||
|
inner_attribs = list(entity.attribs)
|
||||||
|
if inner_attribs:
|
||||||
|
# Gefunden: innerer Block mit Attributen
|
||||||
|
for attrib in inner_attribs:
|
||||||
|
tag = attrib.dxf.tag
|
||||||
|
value = attrib.dxf.text
|
||||||
|
attributes[tag] = value
|
||||||
|
break # Nur das erste INSERT mit Attributen verwenden
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Warnung: Fehler beim Extrahieren der Attribute aus Block: {e}")
|
||||||
|
|
||||||
return attributes
|
return attributes
|
||||||
|
|
||||||
|
|
||||||
|
def is_rectangle_polyline(points):
|
||||||
|
"""
|
||||||
|
Prüft, ob eine Liste von Punkten ein Rechteck darstellt.
|
||||||
|
Ein Rechteck hat genau 4 Eckpunkte (oder 5 wenn geschlossen mit identischem Start/End).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
points: Liste von Punkten (Tupel mit mindestens x, y Koordinaten)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True wenn es ein Rechteck ist, False sonst
|
||||||
|
"""
|
||||||
|
if len(points) < 4:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Prüfe ob erster und letzter Punkt identisch sind (geschlossene Polylinie)
|
||||||
|
first = (points[0][0], points[0][1])
|
||||||
|
last = (points[-1][0], points[-1][1])
|
||||||
|
|
||||||
|
if abs(first[0] - last[0]) < 0.001 and abs(first[1] - last[1]) < 0.001:
|
||||||
|
# Geschlossene Polylinie - sollte 5 Punkte haben (4 Ecken + 1 Schließung)
|
||||||
|
if len(points) == 5:
|
||||||
|
return True
|
||||||
|
# Wenn mehr als 5 Punkte, ist es kein einfaches Rechteck
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
# Offene Polylinie - sollte genau 4 Punkte haben
|
||||||
|
return len(points) == 4
|
||||||
|
|
||||||
|
|
||||||
def get_boundary_geometry(doc, insert):
|
def get_boundary_geometry(doc, insert):
|
||||||
"""
|
"""
|
||||||
Sucht im Block nach einem Rechteck oder einer geschlossenen Polylinie.
|
Sucht im Block nach einem Rechteck oder einer geschlossenen Polylinie.
|
||||||
|
Unterstützt zweistufige Blockstruktur (äußerer Block mit LWPOLYLINE + INSERT zu Attribut-Block).
|
||||||
Gibt die Eckpunkte zurück.
|
Gibt die Eckpunkte zurück.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tupel (transformed_points, is_rectangle) oder (None, None) wenn nichts gefunden
|
||||||
"""
|
"""
|
||||||
block_layout = doc.blocks.get(insert.dxf.name)
|
block_layout = doc.blocks.get(insert.dxf.name)
|
||||||
|
|
||||||
@@ -126,7 +181,7 @@ def get_boundary_geometry(doc, insert):
|
|||||||
if abs(first[0] - last[0]) < 0.001 and abs(first[1] - last[1]) < 0.001:
|
if abs(first[0] - last[0]) < 0.001 and abs(first[1] - last[1]) < 0.001:
|
||||||
is_closed = True
|
is_closed = True
|
||||||
|
|
||||||
if is_closed:
|
if is_closed or len(points) >= 4:
|
||||||
# Transform points relative to insert position
|
# Transform points relative to insert position
|
||||||
insert_point = insert.dxf.insert
|
insert_point = insert.dxf.insert
|
||||||
transformed_points = []
|
transformed_points = []
|
||||||
@@ -136,7 +191,10 @@ def get_boundary_geometry(doc, insert):
|
|||||||
insert_point[0] + p[0],
|
insert_point[0] + p[0],
|
||||||
insert_point[1] + p[1]
|
insert_point[1] + p[1]
|
||||||
))
|
))
|
||||||
return transformed_points
|
|
||||||
|
# Prüfe ob es ein Rechteck ist
|
||||||
|
is_rect = is_rectangle_polyline(points)
|
||||||
|
return transformed_points, is_rect
|
||||||
|
|
||||||
elif entity.dxftype() == 'POLYLINE':
|
elif entity.dxftype() == 'POLYLINE':
|
||||||
if entity.is_closed:
|
if entity.is_closed:
|
||||||
@@ -148,9 +206,12 @@ def get_boundary_geometry(doc, insert):
|
|||||||
insert_point[0] + p[0],
|
insert_point[0] + p[0],
|
||||||
insert_point[1] + p[1]
|
insert_point[1] + p[1]
|
||||||
))
|
))
|
||||||
return transformed_points
|
|
||||||
|
|
||||||
return None
|
# Prüfe ob es ein Rechteck ist
|
||||||
|
is_rect = is_rectangle_polyline(points)
|
||||||
|
return transformed_points, is_rect
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
def point_in_polygon(point, polygon):
|
def point_in_polygon(point, polygon):
|
||||||
@@ -198,8 +259,11 @@ def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes):
|
|||||||
if entity.dxf.layer not in search_layers:
|
if entity.dxf.layer not in search_layers:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Prüfe ob der Block Attribute hat
|
# Prüfe ob der Block Attribute hat (direkt oder in zweistufiger Struktur)
|
||||||
if not entity.has_attrib:
|
# Verwende extract_block_attributes um beides zu unterstützen
|
||||||
|
symbol_attribs = extract_block_attributes(doc, entity)
|
||||||
|
|
||||||
|
if not symbol_attribs:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Hole Position des Symbols
|
# Hole Position des Symbols
|
||||||
@@ -208,9 +272,6 @@ def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes):
|
|||||||
|
|
||||||
# Prüfe ob innerhalb des Bereichs
|
# Prüfe ob innerhalb des Bereichs
|
||||||
if point_in_polygon(point, boundary):
|
if point_in_polygon(point, boundary):
|
||||||
# Extrahiere Attribute des Symbols
|
|
||||||
symbol_attribs = extract_block_attributes(entity)
|
|
||||||
|
|
||||||
# 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():
|
||||||
@@ -232,19 +293,25 @@ def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes):
|
|||||||
def sort_symbols_by_direction(symbols, direction):
|
def sort_symbols_by_direction(symbols, direction):
|
||||||
"""
|
"""
|
||||||
Sortiert die Symbole nach der angegebenen Richtung.
|
Sortiert die Symbole nach der angegebenen Richtung.
|
||||||
TOP_BOTTOM: nach Y absteigend, dann X
|
TOP_BOTTOM/LEFT_RIGHT: nach Y absteigend, dann X aufsteigend
|
||||||
BOTTOM_TOP: nach Y aufsteigend, dann X
|
TOP_BOTTOM/RIGHT_LEFT: nach Y absteigend, dann X absteigend
|
||||||
LEFT_RIGHT: nach X aufsteigend, dann Y
|
BOTTOM_TOP/RIGHT_LEFT: nach Y aufsteigend, dann X absteigend
|
||||||
RIGHT_LEFT: nach X absteigend, dann Y
|
BOTTOM_TOP/LEFT_RIGHT: nach Y aufsteigend, dann X aufsteigend
|
||||||
|
|
||||||
|
default ist von links nach rechts wenn keine Angabe gemacht wird. Oder von oben nach unten
|
||||||
|
TOP_BOTTOM: TOP_BOTTOM/LEFT_RIGHT
|
||||||
|
BOTTOM_TOP: BOTTOM_TOP/LEFT_RIGHT
|
||||||
|
LEFT_RIGHT: TOP_BOTTOM/LEFT_RIGHT
|
||||||
|
RIGHT_LEFT: TOP_BOTTOM/RIGHT_LEFT
|
||||||
"""
|
"""
|
||||||
if direction == "TOP_BOTTOM":
|
if direction == "TOP_BOTTOM/LEFT_RIGHT" or direction == "TOP_BOTTOM" or direction == "LEFT_RIGHT":
|
||||||
return sorted(symbols, key=lambda s: (-s['position'][1], s['position'][0]))
|
return sorted(symbols, key=lambda s: (-s['position'][1], s['position'][0]))
|
||||||
elif direction == "BOTTOM_TOP":
|
elif direction == "TOP_BOTTOM/RIGHT_LEFT" or direction == "RIGHT_LEFT":
|
||||||
|
return sorted(symbols, key=lambda s: (-s['position'][1], s['position'][0]))
|
||||||
|
elif direction == "BOTTOM_TOP/RIGHT_LEFT":
|
||||||
|
return sorted(symbols, key=lambda s: (s['position'][1], s['position'][0]))
|
||||||
|
elif direction == "BOTTOM_TOP/LEFT_RIGHT"or direction == "BOTTOM_TOP":
|
||||||
return sorted(symbols, key=lambda s: (s['position'][1], s['position'][0]))
|
return sorted(symbols, key=lambda s: (s['position'][1], s['position'][0]))
|
||||||
elif direction == "LEFT_RIGHT":
|
|
||||||
return sorted(symbols, key=lambda s: (s['position'][0], s['position'][1]))
|
|
||||||
elif direction == "RIGHT_LEFT":
|
|
||||||
return sorted(symbols, key=lambda s: (-s['position'][0], s['position'][1]))
|
|
||||||
else:
|
else:
|
||||||
# Fallback: LEFT_RIGHT
|
# Fallback: LEFT_RIGHT
|
||||||
return sorted(symbols, key=lambda s: (s['position'][0], s['position'][1]))
|
return sorted(symbols, key=lambda s: (s['position'][0], s['position'][1]))
|
||||||
@@ -258,7 +325,6 @@ def enumerate_symbols(symbols, attributes):
|
|||||||
renamed = []
|
renamed = []
|
||||||
|
|
||||||
for symbol in symbols:
|
for symbol in symbols:
|
||||||
symbol_attribs = symbol['attributes']
|
|
||||||
layer = symbol['layer']
|
layer = symbol['layer']
|
||||||
|
|
||||||
# Finde das passende NAME-Template für diesen Layer
|
# Finde das passende NAME-Template für diesen Layer
|
||||||
@@ -283,7 +349,6 @@ def enumerate_symbols(symbols, attributes):
|
|||||||
# Zähle wie viele @ im Template sind
|
# Zähle wie viele @ im Template sind
|
||||||
at_count = name_template.count('@')
|
at_count = name_template.count('@')
|
||||||
number_str = str(counter).zfill(at_count)
|
number_str = str(counter).zfill(at_count)
|
||||||
new_name = name_template.replace('@' * at_count, number_str)
|
|
||||||
|
|
||||||
# Aktualisiere Attribute im Symbol
|
# Aktualisiere Attribute im Symbol
|
||||||
for attrib in symbol['entity'].attribs:
|
for attrib in symbol['entity'].attribs:
|
||||||
@@ -305,19 +370,100 @@ def enumerate_symbols(symbols, attributes):
|
|||||||
return renamed
|
return renamed
|
||||||
|
|
||||||
|
|
||||||
|
def get_renamer_config_key(attributes):
|
||||||
|
"""
|
||||||
|
Erstellt einen eindeutigen Schlüssel für die Renamer-Block-Konfiguration.
|
||||||
|
Blöcke mit demselben Schlüssel werden gruppiert und gemeinsam nummeriert.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
attributes: Dictionary mit Block-Attributen
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple mit (NAME-Patterns, LAYER_NAMEs, DIRECTION, KENNZEICHNUNG)
|
||||||
|
"""
|
||||||
|
# Sammle NAME-Patterns (normalisiert: NAME oder NAME1-3)
|
||||||
|
name_patterns = []
|
||||||
|
if "NAME" in attributes and attributes["NAME"]:
|
||||||
|
name_patterns.append(("NAME", attributes["NAME"]))
|
||||||
|
for i in range(1, 4):
|
||||||
|
name_key = f"NAME{i}"
|
||||||
|
if name_key in attributes and attributes[name_key]:
|
||||||
|
name_patterns.append((name_key, attributes[name_key]))
|
||||||
|
|
||||||
|
# Sammle LAYER_NAMEs (normalisiert: LAYER_NAME oder LAYER_NAME1-3)
|
||||||
|
layer_names = []
|
||||||
|
if "LAYER_NAME" in attributes and attributes["LAYER_NAME"]:
|
||||||
|
layer_names.append(("LAYER_NAME", attributes["LAYER_NAME"]))
|
||||||
|
for i in range(1, 4):
|
||||||
|
layer_key = f"LAYER_NAME{i}"
|
||||||
|
if layer_key in attributes and attributes[layer_key]:
|
||||||
|
layer_names.append((layer_key, attributes[layer_key]))
|
||||||
|
|
||||||
|
# DIRECTION und KENNZEICHNUNG
|
||||||
|
direction = attributes.get("DIRECTION", "")
|
||||||
|
kennzeichnung = attributes.get("KENNZEICHNUNG", "")
|
||||||
|
|
||||||
|
# Erstelle einen eindeutigen Schlüssel
|
||||||
|
# Sortiere die Listen für konsistente Keys
|
||||||
|
name_patterns.sort()
|
||||||
|
layer_names.sort()
|
||||||
|
|
||||||
|
return (tuple(name_patterns), tuple(layer_names), direction, kennzeichnung)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_direction_with_geometry(direction, is_rectangle):
|
||||||
|
"""
|
||||||
|
Validiert die DIRECTION-Angabe gegen die tatsächliche Geometrie.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
direction: DIRECTION-Attribut (z.B. "LEFT_RIGHT", "POLYLINE_PATH")
|
||||||
|
is_rectangle: True wenn die Polylinie ein Rechteck ist, False sonst
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tupel (is_valid, error_message):
|
||||||
|
- is_valid: True wenn Kombination gültig ist
|
||||||
|
- error_message: Fehlermeldung wenn ungültig, sonst None
|
||||||
|
"""
|
||||||
|
# POLYLINE_PATH-Richtungen erwarten komplexe Polylinien (keine Rechtecke)
|
||||||
|
polyline_path_directions = ["POLYLINE_PATH"]
|
||||||
|
|
||||||
|
# Rechteck/Standard-Richtungen erwarten einfache Rechtecke
|
||||||
|
rectangle_directions = ["TOP_BOTTOM", "BOTTOM_TOP", "LEFT_RIGHT", "RIGHT_LEFT",
|
||||||
|
"TOP_BOTTOM/LEFT_RIGHT", "TOP_BOTTOM/RIGHT_LEFT", "BOTTOM_TOP/RIGHT_LEFT", "BOTTOM_TOP/LEFT_RIGHT"]
|
||||||
|
|
||||||
|
if direction in polyline_path_directions:
|
||||||
|
if is_rectangle:
|
||||||
|
return False, f"DIRECTION '{direction}' erfordert eine komplexe Polylinie, aber ein Rechteck wurde gefunden"
|
||||||
|
return True, None
|
||||||
|
|
||||||
|
elif direction in rectangle_directions:
|
||||||
|
if not is_rectangle:
|
||||||
|
return False, f"DIRECTION '{direction}' erfordert ein Rechteck, aber eine komplexe Polylinie wurde gefunden"
|
||||||
|
return True, None
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Unbekannte DIRECTION - gebe Warnung, aber breche nicht ab
|
||||||
|
return True, f"Unbekannte DIRECTION '{direction}' - Verarbeitung wird fortgesetzt"
|
||||||
|
|
||||||
|
|
||||||
def process_renamer_blocks(doc, msp, renamer_layers, error_collector):
|
def process_renamer_blocks(doc, msp, renamer_layers, error_collector):
|
||||||
"""
|
"""
|
||||||
Verarbeitet alle Renamer-Blöcke auf den angegebenen Layern.
|
Verarbeitet alle Renamer-Blöcke auf den angegebenen Layern.
|
||||||
|
Gruppiert Blöcke mit identischer Konfiguration und nummeriert alle Symbole
|
||||||
|
aus allen Blöcken einer Gruppe gemeinsam.
|
||||||
"""
|
"""
|
||||||
all_renamed = []
|
all_renamed = []
|
||||||
|
|
||||||
|
# Erste Pass: Sammle und gruppiere alle Renamer-Blöcke
|
||||||
|
renamer_groups = {} # {config_key: [(insert, boundary, attributes, is_rectangle), ...]}
|
||||||
|
|
||||||
for layer in renamer_layers:
|
for layer in renamer_layers:
|
||||||
print(f"Durchsuche Layer: {layer}")
|
print(f"Durchsuche Layer: {layer}")
|
||||||
|
|
||||||
# Finde alle INSERT-Blöcke auf diesem Layer
|
# Finde alle INSERT-Blöcke auf diesem Layer
|
||||||
for insert in msp.query(f'INSERT[layer=="{layer}"]'):
|
for insert in msp.query(f'INSERT[layer=="{layer}"]'):
|
||||||
# Extrahiere Attribute
|
# Extrahiere Attribute (unterstützt zweistufige Blockstruktur)
|
||||||
attributes = extract_block_attributes(insert)
|
attributes = extract_block_attributes(doc, insert)
|
||||||
|
|
||||||
if not attributes:
|
if not attributes:
|
||||||
print(f" Block ohne Attribute gefunden an Position {insert.dxf.insert}")
|
print(f" Block ohne Attribute gefunden an Position {insert.dxf.insert}")
|
||||||
@@ -330,36 +476,95 @@ def process_renamer_blocks(doc, msp, renamer_layers, error_collector):
|
|||||||
if not (has_name and has_direction):
|
if not (has_name and has_direction):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print(f" Renamer-Block gefunden: {attributes.get('NAME', attributes.get('NAME1', 'UNKNOWN'))}")
|
block_name = attributes.get('NAME', attributes.get('NAME1', 'UNKNOWN'))
|
||||||
|
print(f" Renamer-Block gefunden: {block_name}")
|
||||||
print(f" Direction: {attributes.get('DIRECTION', 'UNKNOWN')}")
|
print(f" Direction: {attributes.get('DIRECTION', 'UNKNOWN')}")
|
||||||
print(f" Kennzeichnung: {attributes.get('KENNZEICHNUNG', 'N/A')}")
|
print(f" Kennzeichnung: {attributes.get('KENNZEICHNUNG', 'N/A')}")
|
||||||
|
|
||||||
# Finde Boundary (Rechteck oder Polylinie)
|
# Finde Boundary (Rechteck oder Polylinie)
|
||||||
boundary = get_boundary_geometry(doc, insert)
|
boundary, is_rectangle = get_boundary_geometry(doc, insert)
|
||||||
if not boundary:
|
if boundary is None:
|
||||||
error_msg = f"Keine Polylinie/Rechteck im Renamer-Block an Position {insert.dxf.insert} gefunden"
|
error_msg = f"Keine Polylinie/Rechteck im Renamer-Block '{block_name}' an Position {insert.dxf.insert} gefunden"
|
||||||
print(f" WARNUNG: {error_msg}")
|
print(f" FEHLER: {error_msg}")
|
||||||
error_collector.add_warnings({"missing_boundary": error_msg})
|
error_collector.add_errors({"missing_boundary": error_msg})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print(f" Boundary gefunden mit {len(boundary)} Punkten")
|
print(f" Boundary gefunden mit {len(boundary)} Punkten (Rechteck: {is_rectangle})")
|
||||||
|
|
||||||
# Finde Symbole innerhalb des Bereichs
|
# Validiere DIRECTION gegen Geometrie
|
||||||
symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes)
|
|
||||||
print(f" {len(symbols)} Template-Symbole gefunden")
|
|
||||||
|
|
||||||
if not symbols:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Sortiere nach Richtung
|
|
||||||
direction = attributes.get("DIRECTION", "LEFT_RIGHT")
|
direction = attributes.get("DIRECTION", "LEFT_RIGHT")
|
||||||
sorted_symbols = sort_symbols_by_direction(symbols, direction)
|
is_valid, validation_error = validate_direction_with_geometry(direction, is_rectangle)
|
||||||
|
|
||||||
# Nummeriere durch
|
if not is_valid:
|
||||||
renamed = enumerate_symbols(sorted_symbols, attributes)
|
error_msg = f"Renamer-Block '{block_name}' an Position {insert.dxf.insert}: {validation_error}"
|
||||||
all_renamed.extend(renamed)
|
print(f" FEHLER: {error_msg}")
|
||||||
|
error_collector.add_errors({"direction_geometry_mismatch": error_msg})
|
||||||
|
continue
|
||||||
|
elif validation_error:
|
||||||
|
# Warnung bei unbekannter DIRECTION, aber fortfahren
|
||||||
|
print(f" WARNUNG: {validation_error}")
|
||||||
|
error_collector.add_warnings({"unknown_direction": validation_error})
|
||||||
|
|
||||||
print(f" {len(renamed)} Symbole nummeriert")
|
# Erstelle Konfigurationsschlüssel und gruppiere
|
||||||
|
config_key = get_renamer_config_key(attributes)
|
||||||
|
if config_key not in renamer_groups:
|
||||||
|
renamer_groups[config_key] = []
|
||||||
|
renamer_groups[config_key].append((insert, boundary, attributes, is_rectangle))
|
||||||
|
|
||||||
|
# Zweite Pass: Verarbeite jede Gruppe
|
||||||
|
for config_key, group_blocks in renamer_groups.items():
|
||||||
|
if not group_blocks:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Verwende die Attribute des ersten Blocks als Referenz (alle in der Gruppe haben identische Attribute)
|
||||||
|
reference_attributes = group_blocks[0][2]
|
||||||
|
direction = reference_attributes.get("DIRECTION", "LEFT_RIGHT")
|
||||||
|
block_name = reference_attributes.get('NAME', reference_attributes.get('NAME1', 'UNKNOWN'))
|
||||||
|
|
||||||
|
print(f"\n Verarbeite Gruppe: {block_name} ({len(group_blocks)} Renamer-Blöcke)")
|
||||||
|
|
||||||
|
# Sammle Symbole aus ALLEN Boundaries dieser Gruppe
|
||||||
|
all_symbols = []
|
||||||
|
seen_entities = {} # {entity_id: (symbol, [list of renamer block positions])}
|
||||||
|
|
||||||
|
for insert, boundary, attributes, is_rectangle in group_blocks:
|
||||||
|
# Finde Symbole innerhalb dieses Bereichs
|
||||||
|
symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes)
|
||||||
|
|
||||||
|
# Füge Symbole hinzu, aber nur wenn das Entity noch nicht gesehen wurde
|
||||||
|
for symbol in symbols:
|
||||||
|
entity_id = id(symbol['entity']) # Eindeutige ID des Entity-Objekts
|
||||||
|
renamer_pos = insert.dxf.insert
|
||||||
|
|
||||||
|
if entity_id not in seen_entities:
|
||||||
|
seen_entities[entity_id] = (symbol, [renamer_pos])
|
||||||
|
all_symbols.append(symbol)
|
||||||
|
else:
|
||||||
|
# Symbol wurde bereits in einem anderen Bereich gefunden - Warnung
|
||||||
|
existing_symbol, existing_positions = seen_entities[entity_id]
|
||||||
|
existing_positions.append(renamer_pos)
|
||||||
|
symbol_pos = symbol['position']
|
||||||
|
warning_msg = (
|
||||||
|
f"Symbol an Position ({symbol_pos[0]:.1f}, {symbol_pos[1]:.1f}) "
|
||||||
|
f"liegt in mehreren Renamer-Bereichen der Gruppe '{block_name}'. "
|
||||||
|
f"Gefunden in Renamer-Blöcken an Positionen: "
|
||||||
|
f"{', '.join([f'({p[0]:.1f}, {p[1]:.1f})' for p in existing_positions])}"
|
||||||
|
)
|
||||||
|
error_collector.add_warnings({"symbol_in_multiple_boundaries": warning_msg})
|
||||||
|
|
||||||
|
print(f" Gesamt {len(all_symbols)} eindeutige Template-Symbole aus {len(group_blocks)} Blöcken")
|
||||||
|
|
||||||
|
if not all_symbols:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Sortiere alle Symbole zusammen nach Richtung
|
||||||
|
sorted_symbols = sort_symbols_by_direction(all_symbols, direction)
|
||||||
|
|
||||||
|
# Nummeriere alle Symbole als eine Sequenz
|
||||||
|
renamed = enumerate_symbols(sorted_symbols, reference_attributes)
|
||||||
|
all_renamed.extend(renamed)
|
||||||
|
|
||||||
|
print(f" {len(renamed)} Symbole nummeriert")
|
||||||
|
|
||||||
return all_renamed
|
return all_renamed
|
||||||
|
|
||||||
@@ -449,12 +654,21 @@ if __name__ == '__main__':
|
|||||||
write_json_file(output_data, output_path.parent, output_path.name)
|
write_json_file(output_data, output_path.parent, output_path.name)
|
||||||
print(f"Ergebnisse in JSON gespeichert: {args.write}")
|
print(f"Ergebnisse in JSON gespeichert: {args.write}")
|
||||||
|
|
||||||
# Schreibe Fehler-Datei
|
# Schreibe Fehler-Datei nur wenn Fehler oder Warnungen vorhanden sind
|
||||||
if args.errorfile:
|
if error_collector.has_errors_or_warnings():
|
||||||
error_data = error_collector.get_all_issues()
|
if args.errorfile:
|
||||||
error_path = Path(args.errorfile)
|
error_data = error_collector.get_all_issues()
|
||||||
write_json_file(error_data, error_path.parent, error_path.name)
|
error_path = Path(args.errorfile)
|
||||||
print(f"Fehler/Warnungen gespeichert: {args.errorfile}")
|
write_json_file(error_data, error_path.parent, error_path.name)
|
||||||
|
print(f"Fehler/Warnungen gespeichert: {args.errorfile}")
|
||||||
|
else:
|
||||||
|
# Keine explizite Fehlerdatei angegeben, generiere eine basierend auf Input-Datei
|
||||||
|
error_filename = f"{dxf_path.stem}_errors.json"
|
||||||
|
error_data = error_collector.get_all_issues()
|
||||||
|
write_json_file(error_data, dxf_path.parent, error_filename)
|
||||||
|
print(f"Fehler/Warnungen gespeichert: {dxf_path.parent / error_filename}")
|
||||||
|
else:
|
||||||
|
print("\nKeine Fehler oder Warnungen - keine Fehlerdatei wird geschrieben")
|
||||||
|
|
||||||
# Exit-Code basierend auf Fehlern
|
# Exit-Code basierend auf Fehlern
|
||||||
if error_collector.has_errors():
|
if error_collector.has_errors():
|
||||||
@@ -464,5 +678,4 @@ if __name__ == '__main__':
|
|||||||
print("\n(Warnungen vorhanden, aber keine kritischen Fehler)")
|
print("\n(Warnungen vorhanden, aber keine kritischen Fehler)")
|
||||||
exit(0)
|
exit(0)
|
||||||
else:
|
else:
|
||||||
print("\nKeine Fehler oder Warnungen")
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|||||||
Reference in New Issue
Block a user