alles neu mit black formatiert. handler_context.py impl. um weniger Übergabeparameter zu haben

This commit is contained in:
2026-01-27 16:09:52 +01:00
parent 4d09aee02b
commit 19282888ee
16 changed files with 4228 additions and 1682 deletions
+261 -187
View File
@@ -4,27 +4,27 @@ from ezdxf.math import Matrix44, Vec3, BoundingBox, Vec2
import math
import argparse
import sys
import shutil
import configparser
from utils import check_environment_var, setup_logger
from pathlib import Path
import logging
def create_block_library(input_dir, output_file, config, logger=None):
"""
Erstellt eine DXF-Block-Bibliothek aus einzelnen DXF-Dateien.
Diese Funktion liest alle DXF-Dateien aus einem Verzeichnis und erstellt
daraus eine Bibliothek mit Blöcken. Die Blöcke werden in einem Raster
angeordnet und mit Beschriftungen versehen. Fehlerhafte Dateien werden
protokolliert.
Args:
input_dir (str): Verzeichnis mit den zu verarbeitenden DXF-Dateien
output_file (str): Pfad zur zu erstellenden Bibliotheks-DXF-Datei
config: ConfigParser-Objekt mit den Konfigurationswerten
logger: Optionaler Logger für Logging-Ausgaben
Note:
- Unterstützte Entity-Typen: LINE, LWPOLYLINE, POLYLINE, SPLINE, CIRCLE, ARC, INSERT, TEXT, MTEXT, REGION, …
- Blöcke werden zentriert und in einem 20x20 Raster angeordnet
@@ -37,12 +37,12 @@ def create_block_library(input_dir, output_file, config, logger=None):
x_offset = 0
y_offset = 0
blocks_in_row = 0
error_files = []
processed_files = []
max_blockspacing_x = 0
max_blockspacing_y = 0
for filename in os.listdir(input_dir):
if not filename.lower().endswith(".dxf"):
@@ -79,22 +79,21 @@ def create_block_library(input_dir, output_file, config, logger=None):
"REGION",
}
filtered_entities = []
att_def= {}
att_def = {}
for insert in src_msp.query("INSERT"):
for attrib in insert.attribs:
att_def[attrib.dxf.tag] = attrib.dxf.text
for e in entities:
if e.dxftype() in allowed_types:
if e.dxftype() in allowed_types:
filtered_entities.append(e)
else:
logger.info(f"{e.dxftype()} is nicht in der erlaubten Liste. Diese befindet sich in {filename}")
logger.info(
f"{e.dxftype()} is nicht in der erlaubten Liste. Diese befindet sich in {filename}"
)
entities = filtered_entities
except Exception as e:
error_msg = f"Fehler beim Lesen von {filename}: {e}"
if logger:
@@ -106,7 +105,9 @@ def create_block_library(input_dir, output_file, config, logger=None):
# Sicherstellen, dass die im Quell-DXF verwendeten Layer im Zieldokument existieren
try:
used_layer_names = {e.dxf.layer for e in entities if hasattr(e.dxf, "layer")}
used_layer_names = {
e.dxf.layer for e in entities if hasattr(e.dxf, "layer")
}
for layer_name in used_layer_names:
if layer_name and layer_name not in doc.layers:
try:
@@ -122,8 +123,10 @@ def create_block_library(input_dir, output_file, config, logger=None):
doc.layers.add(name=layer_name)
except Exception:
pass
boundingbox, ausdehnung, center = calculate_block_bounding_box(filtered_entities, doc,src_doc, filename,config)
boundingbox, ausdehnung, center = calculate_block_bounding_box(
filtered_entities, doc, src_doc, filename, config
)
if center is None:
error_msg = f"Keine gültige Geometrie in {filename}"
if logger:
@@ -136,7 +139,7 @@ def create_block_library(input_dir, output_file, config, logger=None):
if name in doc.blocks:
doc.blocks.delete_block(name)
blk = doc.blocks.new(name=name, base_point=(0,0))
blk = doc.blocks.new(name=name, base_point=(0, 0))
for e in entities:
cp = copy_entity(logger, error_files, filename, e, center)
@@ -146,11 +149,10 @@ def create_block_library(input_dir, output_file, config, logger=None):
# Platzierung in Reihen und Spalten
# Attribut-Definition (ATTDEF) hinzufügen
# Blockreferenz-Layer bestimmen
# Blockreferenz-Layer bestimmen
blockref_layer = None
try:
#Konfiguration erlaubt expliziten Layernamen
# Konfiguration erlaubt expliziten Layernamen
cfg_layer = None
try:
cfg_layer = config.get("dxf2lib", "blockref_layer")
@@ -186,42 +188,59 @@ def create_block_library(input_dir, output_file, config, logger=None):
blockref_layer = None
section = "dxf2lib"
text_height = get_cfg_value(section, "text_height", DEFAULTS["text_height"])
extra_block_space_x = get_cfg_value(section, "extra_block_space_x", DEFAULTS["extra_block_space_x"])
blocks_per_row = get_cfg_value(section, "blocks_per_row", DEFAULTS["blocks_per_row"])
extra_text_space_y = get_cfg_value(section, "extra_text_space_y", DEFAULTS["extra_text_space_y"])
extra_block_space_x = get_cfg_value(
section, "extra_block_space_x", DEFAULTS["extra_block_space_x"]
)
blocks_per_row = get_cfg_value(
section, "blocks_per_row", DEFAULTS["blocks_per_row"]
)
extra_text_space_y = get_cfg_value(
section, "extra_text_space_y", DEFAULTS["extra_text_space_y"]
)
ausdehnung_x, ausdehung_y = ausdehnung[0], ausdehnung[1]
block_spacing_y = ausdehung_y + 400
block_spacing_x = ausdehnung_x + extra_block_space_x
max_blockspacing_x = max(max_blockspacing_x, block_spacing_x)
max_blockspacing_y = max(max_blockspacing_y, block_spacing_y)
x_offset += max_blockspacing_x
max_blockspacing_x = max(max_blockspacing_x, block_spacing_x)
max_blockspacing_y = max(max_blockspacing_y, block_spacing_y)
x_offset += max_blockspacing_x
# Blockreferenz mit optionalem Layer einfügen (Entity-Layer bleiben erhalten)
if blockref_layer:
test =msp.add_blockref(name, insert=(x_offset, y_offset), dxfattribs={"layer": blockref_layer})
test = msp.add_blockref(
name, insert=(x_offset, y_offset), dxfattribs={"layer": blockref_layer}
)
else:
test =msp.add_blockref(name, insert=(x_offset, y_offset))
test = msp.add_blockref(name, insert=(x_offset, y_offset))
# Text mit Blocknamen über dem Block
if name == "a" or name == "b":
for tag, value in att_def.items():
tag = tag
value = value
a =test.add_attrib(
a = test.add_attrib(
tag=tag,
text=value,
)
a.is_invisible = True
# Werte aus Config holen (Block: [dxf2lib])
msp.add_text(name, dxfattribs={'height': text_height, 'insert': (x_offset -ausdehnung_x/2 , y_offset + ausdehung_y/2 + extra_text_space_y)})
msp.add_text(
name,
dxfattribs={
"height": text_height,
"insert": (
x_offset - ausdehnung_x / 2,
y_offset + ausdehung_y / 2 + extra_text_space_y,
),
},
)
processed_files.append(filename)
blocks_in_row += 1
# Abstand zwischen Blöcken in einer Reihe
# Abstand zwischen Blöcken in einer Reihe
if blocks_in_row == blocks_per_row:
blocks_in_row = 0
x_offset = 0
y_offset -= max_blockspacing_y # Neue Zeile, nach unten versetzt
@@ -232,9 +251,11 @@ def create_block_library(input_dir, output_file, config, logger=None):
logger.info(f"Bibliotheks-DXF gespeichert: {output_file}")
logger.info(f"Verarbeitete Dateien: {len(processed_files)}")
logger.info(f"Fehlerhafte Dateien: {len(error_files)}")
if error_files:
logger.warning(f"{len(error_files)} Dateien konnten nicht verarbeitet werden:")
logger.warning(
f"{len(error_files)} Dateien konnten nicht verarbeitet werden:"
)
for filename, error_msg in error_files:
logger.error(f"{filename}: {error_msg}")
else:
@@ -243,9 +264,11 @@ def create_block_library(input_dir, output_file, config, logger=None):
print(f"Bibliotheks-DXF gespeichert: {output_file}")
print(f"Verarbeitete Dateien: {len(processed_files)}")
print(f"Fehlerhafte Dateien: {len(error_files)}")
if error_files:
print(f"Warnung: {len(error_files)} Dateien konnten nicht verarbeitet werden.")
print(
f"Warnung: {len(error_files)} Dateien konnten nicht verarbeitet werden."
)
output_dir = output_file.parent
if not output_dir.exists():
@@ -267,24 +290,26 @@ def copy_entity(logger, error_files, filename, e, center):
return cp
except Exception as err:
error_msg = f"Fehler beim Verarbeiten von Entity {e.dxftype()} in {filename}: {err}"
error_msg = (
f"Fehler beim Verarbeiten von Entity {e.dxftype()} in {filename}: {err}"
)
if logger:
logger.error(error_msg)
else:
print(error_msg)
error_files.append((filename, error_msg))
return None
# Standardwerte (falls nicht in der Config)
DEFAULTS = {
"text_height": 20, # Schriftgröße des Texts (in DXF-Einheiten)
"blocks_per_row": 20, # Anzahl Blöcke pro Zeile im Raster
"extra_block_space_x" : 50, # Extra Platz damit sich Blöcke nicht überlappen
"extra_text_space_y" : 50 # Abstand der Überschrift über dem Symbol
"text_height": 20, # Schriftgröße des Texts (in DXF-Einheiten)
"blocks_per_row": 20, # Anzahl Blöcke pro Zeile im Raster
"extra_block_space_x": 50, # Extra Platz damit sich Blöcke nicht überlappen
"extra_text_space_y": 50, # Abstand der Überschrift über dem Symbol
}
def get_cfg_value(section, key, fallback):
try:
return int(config.get(section, key))
@@ -292,9 +317,9 @@ def get_cfg_value(section, key, fallback):
return fallback
def convert_dxf_to_block_with_inserts(input_filename, output_filename, block_name="CONVERTED_BLOCK"):
def convert_dxf_to_block_with_inserts(
input_filename, output_filename, block_name="CONVERTED_BLOCK"
):
"""
Konvertiert alle Entities einer DXF-Datei in einen neuen Block
INSERTs werden als Referenzen beibehalten (nicht explodiert)
@@ -303,57 +328,57 @@ def convert_dxf_to_block_with_inserts(input_filename, output_filename, block_nam
# Eingabe-DXF laden
input_doc = ezdxf.readfile(input_filename)
print(f"Lade DXF-Datei: {input_filename}")
# Neue Ausgabe-DXF erstellen
output_doc = ezdxf.new('R2010')
output_doc.header['$INSUNITS'] = 4 # Millimeter
output_doc = ezdxf.new("R2010")
output_doc.header["$INSUNITS"] = 4 # Millimeter
# Zuerst alle Block-Definitionen kopieren
copied_blocks = copy_block_definitions(input_doc, output_doc)
print(f"Block-Definitionen kopiert: {len(copied_blocks)}")
# Neuen Hauptblock erstellen
new_block = output_doc.blocks.new(name=block_name)
print(f"Erstelle neuen Block: {block_name}")
# Alle Entities aus dem Modelspace kopieren
msp = input_doc.modelspace()
entity_count = 0
insert_count = 0
for entity in msp:
if entity.dxftype() == 'INSERT':
if entity.dxftype() == "INSERT":
# INSERT direkt kopieren (nicht explodieren)
copy_entity_to_block(entity, new_block)
insert_count += 1
else:
# Normale Entity kopieren
copy_entity_to_block(entity, new_block)
entity_count += 1
# Bounding Box berechnen
bbox = calculate_block_bounding_box(new_block, output_doc)
# Block im Modelspace der neuen Datei platzieren
output_msp = output_doc.modelspace()
output_msp.add_blockref(block_name, insert=(0, 0))
# Bounding Box als Hilfslinien hinzufügen (optional)
if bbox.has_data:
add_bounding_box_to_modelspace(output_msp, bbox)
# Speichern
output_doc.saveas(output_filename)
print(f"Konvertierung abgeschlossen:")
print(f" - {entity_count} Entities übertragen")
print(f" - {insert_count} INSERTs als Referenzen beibehalten")
print(f" - Bounding Box: {format_bounding_box(bbox)}")
print(f" - Ausgabe: {output_filename}")
return bbox
except FileNotFoundError:
print(f"Fehler: Datei {input_filename} nicht gefunden")
return None
@@ -370,48 +395,53 @@ def copy_block_definitions(source_doc, target_doc):
Kopiert alle Block-Definitionen vom Quell- zum Ziel-Dokument
"""
copied_blocks = []
for block_name in source_doc.blocks:
# Standard-Blöcke (MODEL_SPACE, PAPER_SPACE) überspringen
if block_name.startswith('*'):
if block_name.startswith("*"):
continue
source_block = source_doc.blocks[block_name]
# Prüfen ob Block bereits existiert
if block_name in target_doc.blocks:
print(f"Warnung: Block '{block_name}' existiert bereits, wird übersprungen")
continue
# Neuen Block in Ziel-Dokument erstellen
target_block = target_doc.blocks.new(name=block_name)
# Alle Entities des Quell-Blocks kopieren
for entity in source_block:
copy_entity_to_block(entity, target_block)
copied_blocks.append(block_name)
return copied_blocks
def calculate_block_bounding_box(block, doc, src_doc, filename,config):
def calculate_block_bounding_box(block, doc, src_doc, filename, config):
"""
Berechnet die Bounding Box eines Blocks inklusive aller INSERTs
"""
bbox = BoundingBox()
for entity in block:
entity_bbox = get_entity_bounding_box(entity, doc,src_doc,filename,config)
entity_bbox = get_entity_bounding_box(entity, doc, src_doc, filename, config)
if entity_bbox and entity_bbox.has_data:
bbox.extend(entity_bbox)
return bbox, (bbox.extmax.x - bbox.extmin.x, bbox.extmax.y - bbox.extmin.y), bbox.center
return (
bbox,
(bbox.extmax.x - bbox.extmin.x, bbox.extmax.y - bbox.extmin.y),
bbox.center,
)
def _bbox_from_virtual_entities(entity, doc, src_doc, filename, config, transform_matrix=None):
def _bbox_from_virtual_entities(
entity, doc, src_doc, filename, config, transform_matrix=None
):
"""
Nutzt virtual_entities(), um komplexe Objekte (z.B. SURFACE) in
auswertbare Geometrie zu zerlegen und darauf eine Bounding Box zu
@@ -432,15 +462,15 @@ def _bbox_from_virtual_entities(entity, doc, src_doc, filename, config, transfor
return v_bbox
def get_entity_bounding_box(e, doc,src_doc,filename,config, transform_matrix=None):
def get_entity_bounding_box(e, doc, src_doc, filename, config, transform_matrix=None):
"""
Berechnet die Bounding Box einer einzelnen Entity
Berücksichtigt INSERTs mit ihren Block-Inhalten
"""
bbox = BoundingBox()
bbox = BoundingBox()
try:
if e.dxftype() == 'LINE':
if e.dxftype() == "LINE":
start = Vec3(e.dxf.start)
end = Vec3(e.dxf.end)
if transform_matrix:
@@ -448,8 +478,8 @@ def get_entity_bounding_box(e, doc,src_doc,filename,config, transform_matrix=Non
end = transform_matrix.transform(end)
bbox.extend([start])
bbox.extend([end])
elif e.dxftype() == 'CIRCLE':
elif e.dxftype() == "CIRCLE":
# Kreis durch Punktabtastung (robust bei Transformationen)
center = Vec3(e.dxf.center)
radius = float(e.dxf.radius)
@@ -464,8 +494,8 @@ def get_entity_bounding_box(e, doc,src_doc,filename,config, transform_matrix=Non
points.append(p)
if points:
bbox.extend(points)
elif e.dxftype() == 'ARC':
elif e.dxftype() == "ARC":
# Bogen durch Punktabtastung zwischen Start- und Endwinkel
center = Vec3(e.dxf.center)
radius = float(e.dxf.radius)
@@ -495,16 +525,16 @@ def get_entity_bounding_box(e, doc,src_doc,filename,config, transform_matrix=Non
points.append(p)
if points:
bbox.extend(points)
elif e.dxftype() == 'LWPOLYLINE':
elif e.dxftype() == "LWPOLYLINE":
# Nutze virtuelle Entities (Linien/Bögen), inkl. Bulge-Unterstützung
ve_bbox = _bbox_from_virtual_entities(
e, doc, src_doc, filename, config, transform_matrix
)
if ve_bbox.has_data:
bbox.extend(ve_bbox)
elif e.dxftype() == 'POLYLINE':
elif e.dxftype() == "POLYLINE":
# 2D/3D Polylines ebenfalls über virtuelle Entities
ve_bbox = _bbox_from_virtual_entities(
e, doc, src_doc, filename, config, transform_matrix
@@ -512,7 +542,7 @@ def get_entity_bounding_box(e, doc,src_doc,filename,config, transform_matrix=Non
if ve_bbox.has_data:
bbox.extend(ve_bbox)
elif e.dxftype() == '3DFACE':
elif e.dxftype() == "3DFACE":
# 3DFace: direkte Eckpunkte verwenden
pts = []
try:
@@ -527,15 +557,15 @@ def get_entity_bounding_box(e, doc,src_doc,filename,config, transform_matrix=Non
if pts:
bbox.extend(pts)
elif e.dxftype() in {'MESH', 'POLYFACE', 'POLYFACEMESH'}:
elif e.dxftype() in {"MESH", "POLYFACE", "POLYFACEMESH"}:
# Mesh/Polyface über virtuelle Geometrie auswerten
ve_bbox = _bbox_from_virtual_entities(
e, doc, src_doc, filename, config, transform_matrix
)
if ve_bbox.has_data:
bbox.extend(ve_bbox)
elif e.dxftype() == 'SPLINE':
elif e.dxftype() == "SPLINE":
# Approximation der Spline-Kurve
try:
pts = list(e.approximate(60))
@@ -556,19 +586,21 @@ def get_entity_bounding_box(e, doc,src_doc,filename,config, transform_matrix=Non
sampled.append(v)
if sampled:
bbox.extend(sampled)
elif e.dxftype() == 'ATTDEF':
elif e.dxftype() == "ATTDEF":
insert_point = Vec3(e.dxf.insert)
if transform_matrix:
insert_point = transform_matrix.transform(insert_point)
elif e.dxftype() == 'TEXT':
elif e.dxftype() == "TEXT":
insert_point = Vec3(e.dxf.insert)
height = float(getattr(e.dxf, "height", 1.0))
width_factor = float(getattr(e.dxf, "width", 1.0))
rotation = math.radians(getattr(e.dxf, "rotation", 0.0))
text_content = getattr(e.dxf, "text", "") or ""
est_width = max(len(text_content) * height * 0.6 * width_factor, height * 0.5)
est_width = max(
len(text_content) * height * 0.6 * width_factor, height * 0.5
)
corners = [
insert_point,
insert_point + Vec3(est_width, 0, 0),
@@ -590,7 +622,7 @@ def get_entity_bounding_box(e, doc,src_doc,filename,config, transform_matrix=Non
pt = transform_matrix.transform(pt)
bbox.extend([pt])
elif e.dxftype() == 'MTEXT':
elif e.dxftype() == "MTEXT":
insert_point = Vec3(e.dxf.insert)
char_height = float(getattr(e.dxf, "char_height", 1.0))
width = float(getattr(e.dxf, "width", 0.0))
@@ -610,86 +642,99 @@ def get_entity_bounding_box(e, doc,src_doc,filename,config, transform_matrix=Non
pt = transform_matrix.transform(pt)
bbox.extend([pt])
elif e.dxftype() == 'REGION':
elif e.dxftype() == "REGION":
# Region: Begrenzungsgeometrie über virtual_entities()
ve_bbox = _bbox_from_virtual_entities(
e, doc, src_doc, filename, config, transform_matrix
)
if ve_bbox.has_data:
bbox.extend(ve_bbox)
elif e.dxftype().endswith('SURFACE'):
elif e.dxftype().endswith("SURFACE"):
# Viele Surface-Typen liefern ihre Proxy-Geometrie über virtual_entities()
ve_bbox = _bbox_from_virtual_entities(
e, doc, src_doc, filename, config, transform_matrix
)
if ve_bbox.has_data:
bbox.extend(ve_bbox)
elif e.dxftype() == 'INSERT':
elif e.dxftype() == "INSERT":
# INSERT: Block-Inhalt mit Transformation berücksichtigen
insert_bbox = calculate_insert_bounding_box(e, doc,src_doc,filename,config, transform_matrix)
if insert_bbox and insert_bbox.has_data and e.dxf.layer not in config.get("dxf2lib","automation_layer"):
insert_bbox = calculate_insert_bounding_box(
e, doc, src_doc, filename, config, transform_matrix
)
if (
insert_bbox
and insert_bbox.has_data
and e.dxf.layer not in config.get("dxf2lib", "automation_layer")
):
bbox.extend(insert_bbox)
except Exception as e:
print(f"Fehler bei Bounding Box Berechnung für {e.dxftype()}: {e}")
return bbox
def extract_scaling(matrix):
sx = math.sqrt(matrix[0, 0]**2 + matrix[0, 1]**2 + matrix[0, 2]**2)
sy = math.sqrt(matrix[1, 0]**2 + matrix[1, 1]**2 + matrix[1, 2]**2)
return sx, sy,
def calculate_insert_bounding_box(insert_entity, doc,src_doc,filename,config,parent_transform=None):
def extract_scaling(matrix):
sx = math.sqrt(matrix[0, 0] ** 2 + matrix[0, 1] ** 2 + matrix[0, 2] ** 2)
sy = math.sqrt(matrix[1, 0] ** 2 + matrix[1, 1] ** 2 + matrix[1, 2] ** 2)
return (
sx,
sy,
)
def calculate_insert_bounding_box(
insert_entity, doc, src_doc, filename, config, parent_transform=None
):
"""
Berechnet die Bounding Box eines INSERTs inklusive Block-Inhalt
"""
try:
# Block-Definition finden
# Block-Definition finden
block_name = insert_entity.dxf.name
src_blk = src_doc.blocks[block_name]
if block_name in doc.blocks:
dst_blk = doc.blocks[block_name]
new_insert = False
else:
dst_blk = doc.blocks.new(name=block_name)
new_insert = True
if block_name not in src_doc.blocks:
print(f"Warnung: Block '{block_name}' ")
return BoundingBox()
block_def = src_blk
# Transformation der INSERT-Entity berechnen
insert_transform = get_insert_transform_matrix(insert_entity)
# Mit übergeordneter Transformation kombinieren
if parent_transform:
combined_transform = parent_transform * insert_transform
else:
combined_transform = insert_transform
# Bounding Box aller Entities im Block berechnen
block_bbox = BoundingBox()
for block_entity in block_def:
entity_bbox= get_entity_bounding_box(block_entity, doc,src_doc,filename, config,combined_transform)
if entity_bbox and entity_bbox.has_data:
if new_insert:
dst_blk.add_entity(block_entity.copy())
block_bbox.extend(entity_bbox)
entity_bbox = get_entity_bounding_box(
block_entity, doc, src_doc, filename, config, combined_transform
)
if entity_bbox and entity_bbox.has_data:
if new_insert:
dst_blk.add_entity(block_entity.copy())
block_bbox.extend(entity_bbox)
return block_bbox
except Exception as e:
print(f"Fehler bei INSERT Bounding Box: {e},{filename}")
return BoundingBox()
@@ -701,22 +746,22 @@ def get_insert_transform_matrix(insert_entity):
"""
# Position
insert_point = Vec3(insert_entity.dxf.insert)
# Skalierung
xscale = getattr(insert_entity.dxf, 'xscale', 1.0)
yscale = getattr(insert_entity.dxf, 'yscale', 1.0)
zscale = getattr(insert_entity.dxf, 'zscale', 1.0)
xscale = getattr(insert_entity.dxf, "xscale", 1.0)
yscale = getattr(insert_entity.dxf, "yscale", 1.0)
zscale = getattr(insert_entity.dxf, "zscale", 1.0)
# Rotation (in Radiant umwandeln)
rotation = math.radians(getattr(insert_entity.dxf, 'rotation', 0.0))
rotation = math.radians(getattr(insert_entity.dxf, "rotation", 0.0))
# Transformationsmatrix erstellen
matrix = Matrix44.chain(
Matrix44.scale(xscale, yscale, zscale),
Matrix44.z_rotate(rotation),
Matrix44.translate(insert_point.x, insert_point.y, insert_point.z)
Matrix44.translate(insert_point.x, insert_point.y, insert_point.z),
)
return matrix
@@ -728,7 +773,7 @@ def copy_entity_to_block(entity, target_block):
# Entity kopieren und zum Block hinzufügen
entity_copy = entity.copy()
target_block.add_entity(entity_copy)
except Exception as e:
print(f"Fehler beim Kopieren von {entity.dxftype()}: {e}")
@@ -741,13 +786,13 @@ def add_bounding_box_to_modelspace(msp, bbox, centered=False):
"""
if not bbox.has_data:
return
# Bounding Box Rechteck zeichnen
min_pt = bbox.extmin
max_pt = bbox.extmax
width = max_pt.x - min_pt.x
height = max_pt.y - min_pt.y
if centered:
# Rechteck um (0,0) zentriert
left = -width / 2.0
@@ -759,11 +804,14 @@ def add_bounding_box_to_modelspace(msp, bbox, centered=False):
(right, bottom),
(right, top),
(left, top),
(left, bottom)
(left, bottom),
]
# Roter Punkt in der Mitte
msp.add_circle(center=(0.0, 0.0), radius=max(0.5, min(width, height) * 0.01),
dxfattribs={"layer": "BOUNDING_BOX", "color": 1})
msp.add_circle(
center=(0.0, 0.0),
radius=max(0.5, min(width, height) * 0.01),
dxfattribs={"layer": "BOUNDING_BOX", "color": 1},
)
else:
# Ursprüngliche, nicht-zentrierte Bounding Box
bbox_points = [
@@ -771,21 +819,31 @@ def add_bounding_box_to_modelspace(msp, bbox, centered=False):
(max_pt.x, min_pt.y),
(max_pt.x, max_pt.y),
(min_pt.x, max_pt.y),
(min_pt.x, min_pt.y)
(min_pt.x, min_pt.y),
]
bbox_poly = msp.add_lwpolyline(bbox_points)
bbox_poly.dxf.layer = "BOUNDING_BOX"
bbox_poly.dxf.color = 1 # Rot
# Text mit Abmessungen
text_pos = Vec3(bbox_points[0][0], (bbox_points[2][1] if centered else max_pt.y) + 5, 0)
msp.add_text(f"Breite: {width:.2f} mm", height=3,
dxfattribs={'insert': text_pos, 'layer': "BOUNDING_BOX", 'color': 1})
text_pos2 = Vec3(bbox_points[0][0], (bbox_points[2][1] if centered else max_pt.y) + 10, 0)
msp.add_text(f"Höhe: {height:.2f} mm", height=3,
dxfattribs={'insert': text_pos2, 'layer': "BOUNDING_BOX", 'color': 1})
text_pos = Vec3(
bbox_points[0][0], (bbox_points[2][1] if centered else max_pt.y) + 5, 0
)
msp.add_text(
f"Breite: {width:.2f} mm",
height=3,
dxfattribs={"insert": text_pos, "layer": "BOUNDING_BOX", "color": 1},
)
text_pos2 = Vec3(
bbox_points[0][0], (bbox_points[2][1] if centered else max_pt.y) + 10, 0
)
msp.add_text(
f"Höhe: {height:.2f} mm",
height=3,
dxfattribs={"insert": text_pos2, "layer": "BOUNDING_BOX", "color": 1},
)
def format_bounding_box(bbox):
@@ -794,16 +852,18 @@ def format_bounding_box(bbox):
"""
if not bbox.has_data:
return "Keine gültigen Geometriedaten gefunden"
min_pt = bbox.extmin
max_pt = bbox.extmax
width = max_pt.x - min_pt.x
height = max_pt.y - min_pt.y
depth = max_pt.z - min_pt.z
return (f"Min: ({min_pt.x:.2f}, {min_pt.y:.2f}, {min_pt.z:.2f}) "
f"Max: ({max_pt.x:.2f}, {max_pt.y:.2f}, {max_pt.z:.2f}) "
f"Größe: {width:.2f} × {height:.2f} × {depth:.2f} mm")
return (
f"Min: ({min_pt.x:.2f}, {min_pt.y:.2f}, {min_pt.z:.2f}) "
f"Max: ({max_pt.x:.2f}, {max_pt.y:.2f}, {max_pt.z:.2f}) "
f"Größe: {width:.2f} × {height:.2f} × {depth:.2f} mm"
)
def analyze_source_dxf_with_blocks(filename):
@@ -813,76 +873,88 @@ def analyze_source_dxf_with_blocks(filename):
try:
doc = ezdxf.readfile(filename)
msp = doc.modelspace()
entity_types = {}
layer_count = {}
insert_blocks = {}
block_definitions = {}
# Modelspace analysieren
for entity in msp:
entity_type = entity.dxftype()
entity_types[entity_type] = entity_types.get(entity_type, 0) + 1
layer = getattr(entity.dxf, 'layer', '0')
layer = getattr(entity.dxf, "layer", "0")
layer_count[layer] = layer_count.get(layer, 0) + 1
if entity_type == 'INSERT':
if entity_type == "INSERT":
block_name = entity.dxf.name
insert_blocks[block_name] = insert_blocks.get(block_name, 0) + 1
# Block-Definitionen analysieren
for block_name in doc.blocks:
if not block_name.startswith('*'): # Keine Standard-Blöcke
if not block_name.startswith("*"): # Keine Standard-Blöcke
block_def = doc.blocks[block_name]
entity_count = len(list(block_def))
block_definitions[block_name] = entity_count
print(f"\nAnalyse von {filename}:")
print("=" * 50)
print("Entity-Typen im Modelspace:")
for etype, count in sorted(entity_types.items()):
print(f" {etype}: {count}")
print(f"\nLayer ({len(layer_count)}):")
for layer, count in sorted(layer_count.items()):
print(f" {layer}: {count} entities")
if insert_blocks:
print(f"\nINSERT-Verwendungen ({sum(insert_blocks.values())} total):")
for block, count in sorted(insert_blocks.items()):
print(f" {block}: {count}× verwendet")
if block_definitions:
print(f"\nBlock-Definitionen ({len(block_definitions)}):")
for block, count in sorted(block_definitions.items()):
print(f" {block}: {count} entities")
return entity_types, layer_count, insert_blocks, block_definitions
except Exception as e:
print(f"Fehler bei der Analyse: {e}")
return {}, {}, {}, {}
if __name__ == "__main__":
# Argumentparser für Kommandozeilenoptionen
parser = argparse.ArgumentParser(description="SVG/XML zu DXF Konverter")
parser.add_argument('-i', '--input', type=str, help='Input-Verzeichnis mit SVG/XML-Dateien')
parser.add_argument('-n', '--name', required=False, type=str, help='Name der zu erzeugenden Bibliothek (optional, wird sonst abgefragt)', default="test")
parser.add_argument(
"-i", "--input", type=str, help="Input-Verzeichnis mit SVG/XML-Dateien"
)
parser.add_argument(
"-n",
"--name",
required=False,
type=str,
help="Name der zu erzeugenden Bibliothek (optional, wird sonst abgefragt)",
default="test",
)
if len(sys.argv) == 2 and sys.argv[1] in ("-h", "--help"):
parser.print_help()
sys.exit(0)
args = parser.parse_args()
if not args.name:
args.name = input("Bitte Namen der zu erzeugenden Bibliothek eingeben: ").strip()
args.name = input(
"Bitte Namen der zu erzeugenden Bibliothek eingeben: "
).strip()
if not args.name:
print("Fehler: Kein Name angegeben. Beende.")
sys.exit(1)
# Verzeichnisse über Umgebungsvariablen oder Fallback
# Verzeichnisse über Umgebungsvariablen oder Fallback
if args.input:
INPUT_DIR = Path(args.input)
print(f"Verwende Input-Verzeichnis: {INPUT_DIR} \n")
@@ -890,7 +962,9 @@ if __name__ == "__main__":
INPUT_DIR = check_environment_var("PROJECT_DATA") / "omniflo"
print(f"Kein Input-Verzeichnis angegeben, verwende Standard: {INPUT_DIR} \n")
OUTPUT_FILE = check_environment_var("PROJECT_DATA") / "block_libraries" / f"{args.name}.dxf"
OUTPUT_FILE = (
check_environment_var("PROJECT_DATA") / "block_libraries" / f"{args.name}.dxf"
)
# Prüfe und erstelle log-Verzeichnis falls nötig
log_dir = check_environment_var("PROJECT_LOG")
@@ -899,9 +973,9 @@ if __name__ == "__main__":
print(f"Log-Verzeichnis erstellt: {log_dir}")
# Logger Setup
log_file = Path(os.environ['PROJECT_LOG']) / 'dxf2lib.log'
file_handler = logging.FileHandler(str(log_file), 'a', 'utf-8')
logger = setup_logger(log_dir, name='dxf2lib')
log_file = Path(os.environ["PROJECT_LOG"]) / "dxf2lib.log"
file_handler = logging.FileHandler(str(log_file), "a", "utf-8")
logger = setup_logger(log_dir, name="dxf2lib")
logger.info("=== DXF2LIB Verarbeitung gestartet ===")
logger.info(f"Input-Verzeichnis: {INPUT_DIR}")
logger.info(f"Output-Datei: {OUTPUT_FILE}")