diff --git a/lib/dxf2lib.py b/lib/dxf2lib.py index 4927eaf..cccfb14 100644 --- a/lib/dxf2lib.py +++ b/lib/dxf2lib.py @@ -1,6 +1,7 @@ import os import ezdxf from ezdxf.math import Vec2 +import math import argparse import sys import shutil @@ -38,7 +39,47 @@ def get_bbox(entities): for x, y, *_ in e.get_points("xy"): min_x, min_y = min(min_x, x), min(min_y, y) max_x, max_y = max(max_x, x), max(max_y, y) + elif e.dxftype() == "ARC": + # Handle ARC entities: consider endpoints and cardinal extrema + try: + cx, cy = e.dxf.center.x, e.dxf.center.y + except Exception: + cx, cy = e.dxf.center + r = float(e.dxf.radius) + start_deg = float(e.dxf.start_angle) + end_deg = float(e.dxf.end_angle) + start = math.radians(start_deg % 360) + end = math.radians(end_deg % 360) + + def in_sweep(a: float, s: float, e_: float) -> bool: + # CCW sweep from s to e_ with wrap handling + if e_ >= s: + return s <= a <= e_ + return a >= s or a <= e_ + + candidates = [] + # Endpoints + candidates.append((cx + r * math.cos(start), cy + r * math.sin(start))) + candidates.append((cx + r * math.cos(end), cy + r * math.sin(end))) + # Cardinal angles 0, 90, 180, 270 deg + for ang in (0.0, math.pi/2, math.pi, 3*math.pi/2): + if in_sweep(ang, start, end): + candidates.append((cx + r * math.cos(ang), cy + r * math.sin(ang))) + for px, py in candidates: + min_x, min_y = min(min_x, px), min(min_y, py) + max_x, max_y = max(max_x, px), max(max_y, py) + elif e.dxftype() == "LINE": + # Handle simple line entities by their start/end points + try: + sx, sy = e.dxf.start.x, e.dxf.start.y + ex, ey = e.dxf.end.x, e.dxf.end.y + except Exception: + # Some ezdxf versions provide tuples + (sx, sy), (ex, ey) = e.dxf.start, e.dxf.end + min_x, min_y = min(min_x, sx), min(min_y, sy) + max_x, max_y = max(max_x, ex), max(max_y, ey) else: + # Für andere Entity-Typen: Standard bbox() verwenden box = e.bbox() if box: @@ -50,9 +91,9 @@ def get_bbox(entities): continue if min_x == float('inf'): - return None + return None, None - return Vec2((min_x + max_x) / 2, (min_y + max_y) / 2) + return Vec2((min_x + max_x) / 2, (min_y + max_y) / 2), (max_x -min_x, max_y -min_y) def create_block_library(input_dir, output_file, config, logger=None): @@ -109,7 +150,7 @@ def create_block_library(input_dir, output_file, config, logger=None): error_files.append((filename, error_msg)) continue - center = get_bbox(entities) + center, ausdehnung = get_bbox(entities) if center is None: error_msg = f"Keine gültige Geometrie in {filename}" if logger: @@ -148,9 +189,13 @@ def create_block_library(input_dir, output_file, config, logger=None): max_len = get_cfg_value(section, "max_len", DEFAULTS["max_len"]) text_height = get_cfg_value(section, "text_height", DEFAULTS["text_height"]) line_spacing = get_cfg_value(section, "line_spacing", DEFAULTS["line_spacing"]) - block_spacing_x = get_cfg_value(section, "block_spacing_x", DEFAULTS["block_spacing_x"]) + # block_spacing_x = get_cfg_value(section, "block_spacing_x", DEFAULTS["block_spacing_x"]) + extra_block_space_x = get_cfg_value(section, "extra_block_space_x", DEFAULTS["extra_block_space_x"]) + block_spacing_x = ausdehnung[0] + extra_block_space_x blocks_per_row = get_cfg_value(section, "blocks_per_row", DEFAULTS["blocks_per_row"]) - block_spacing_y = get_cfg_value(section, "block_spacing_y", DEFAULTS["block_spacing_y"]) + # block_spacing_y = get_cfg_value(section, "block_spacing_y", DEFAULTS["block_spacing_y"]) + block_spacing_y = ausdehnung[1] + if len(name) > max_len: first_line = name[:max_len] @@ -203,10 +248,9 @@ DEFAULTS = { "max_len": 9, # Maximale Zeichenlänge pro Textzeile (bei Überschreitung wird umgebrochen) "text_height": 20, # Schriftgröße des Texts (in DXF-Einheiten) "line_spacing": 22, # Abstand zwischen zwei Textzeilen bei Umbrüchen (in DXF-Einheiten) - "block_spacing_x": 350, # Horizontaler Abstand zwischen Blöcken in einer Reihe (in DXF-Einheiten) "blocks_per_row": 20, # Anzahl Blöcke pro Zeile im Raster - "block_spacing_y": 500, # Vertikaler Abstand zwischen Zeilen im Raster (in DXF-Einheiten) -} + "extra_block_space_x" : 50 # Extra Platz damit sich Blöcke nicht überlappen +} def get_cfg_value(section, key, fallback): try: @@ -219,8 +263,7 @@ if __name__ == "__main__": 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)') - parser.add_argument('-k', '--keep', action="store_true", help='Behalte converted_dxf Ordner. Sonst Löschen nach erfolgreicher Bibliothekserstellung') - + if len(sys.argv) == 2 and sys.argv[1] in ("-h", "--help"): parser.print_help() sys.exit(0) @@ -238,7 +281,7 @@ if __name__ == "__main__": INPUT_DIR = Path(args.input) print(f"Verwende Input-Verzeichnis: {INPUT_DIR} \n") else: - INPUT_DIR = check_environment_var("PROJECT_WORK") / "converted_dxfs" + INPUT_DIR = check_environment_var("PROJECT_DATA") / "dxf" print(f"Kein Input-Verzeichnis angegeben, verwende Standard: {INPUT_DIR} \n") OUTPUT_FILE = check_environment_var("PROJECT_DATA") / "block_libaries" / f"{args.name}.dxf" @@ -265,13 +308,5 @@ if __name__ == "__main__": logger.info(f"Erstelle Block-Bibliothek aus {INPUT_DIR}...") create_block_library(INPUT_DIR, OUTPUT_FILE, config, logger) - if not args.keep: - logger.info(f"Keep-Argument nicht gesetzt, lösche Input-Verzeichnis: {INPUT_DIR}") - try: - if INPUT_DIR.exists(): - shutil.rmtree(INPUT_DIR) - logger.info(f"Verzeichnis '{INPUT_DIR}' wurde gelöscht.") - except Exception as err: - logger.warning(f"Konnte '{INPUT_DIR}' nicht löschen: {err}") logger.info("=== DXF2LIB Verarbeitung abgeschlossen ===")