# eckrad.py # ILS Eckrad - PyRx-Implementierung # Ersetzt c:ILS_Eckrad und ils-eckrad-insert aus KreiselInsert.lsp # Setzt voraus: PyRx (cad-pyrx) in BricsCAD/AutoCAD geladen # ============================================ import math import PyRx as Rx import PyGe as Ge import PyDb as Db import PyEd as Ed import wx from elemente.utils import ( get_block_path, component_next_number, merge_attribs, read_block_attribs, ensure_block_from_dwg, create_attrib_defs, insert_block_with_attribs, ) # ============================================================ # Konstanten (aus KreiselInsert.lsp) # ============================================================ KREISEL_DURCHMESSER = 800.0 ECKRAD_DEFAULT_HOEHE = 2000.0 ECKRAD_ATTRIB_DEFS = [ ("NAME", ""), ("DREHRICHTUNG", "UZS"), ("DREHUNG", "0"), ("NUMMER", "0"), ("HOEHE", "0"), ] # ============================================================ # Eckrad-spezifische Funktionen # ============================================================ def ensure_eckrad_block(db, bname, block_path): """AN8.dwg als Basis-Block laden und als ECKRAD_n Block definieren, falls noch nicht vorhanden.""" bt = Db.BlockTable(db.blockTableId(), Db.OpenMode.kForRead) if bt.has(bname): print(f"\n-> Verwende bestehende Blockdefinition: {bname}") return True if not ensure_block_from_dwg(db, "AN8.dwg", block_path): return False # Neue Blockdefinition ECKRAD_n erzeugen bt.upgradeOpen() new_btr = Db.BlockTableRecord() new_btr.setName(bname) bt.add(new_btr) # AN8-Blockdefinition als BlockReference einfuegen an8_id = bt.getAt("AN8") bref = Db.BlockReference(Ge.Point3d(0, 0, 0), an8_id) new_btr.appendAcDbEntity(bref) # Unsichtbare Attribut-Definitionen erzeugen create_attrib_defs(new_btr, ECKRAD_ATTRIB_DEFS) print(f"\n-> Neue Blockdefinition: {bname}") return True def insert_eckrad(db, insert_point, rotation_deg, attribs=None): """Eckrad als Block-Referenz mit Attributen einfuegen. Parameter: db - aktuelle Datenbank insert_point - Ge.Point3d (x, y, z) rotation_deg - Drehwinkel in Grad attribs - dict mit Attribut-Ueberschreibungen (optional) Rueckgabe: ObjectId der eingefuegten BlockReference """ block_path = get_block_path() # Attribute vorbereiten merged = merge_attribs(attribs, ECKRAD_ATTRIB_DEFS) merged["DREHUNG"] = f"{rotation_deg:.1f}" # Hoehe aus Z-Koordinate oder Attribut z_hoehe = insert_point.z if z_hoehe > 0: merged["HOEHE"] = f"{z_hoehe:.0f}" else: try: z_hoehe = float(merged.get("HOEHE", "0")) except ValueError: z_hoehe = 0.0 # Nummer vergeben nummer = component_next_number(db, "ECKRAD_") merged["NUMMER"] = str(nummer) merged["NAME"] = f"ECKRAD_{nummer}" # Blockname bname = f"ECKRAD_{nummer}" # Block-Definition sicherstellen if not ensure_eckrad_block(db, bname, block_path): return None # Block einfuegen mit Attributen pt = Ge.Point3d(insert_point.x, insert_point.y, z_hoehe) obj_id = insert_block_with_attribs(db, bname, pt, rotation_deg, merged) print(f"\n-> Eckrad #{nummer} eingefuegt: {bname}" f" bei {insert_point.x:.1f},{insert_point.y:.1f}" f" Rotation={rotation_deg:.1f} Hoehe={z_hoehe:.0f}") return obj_id # ============================================================ # CAD-Befehl: ILS_Eckrad # ============================================================ @Rx.command("ILS_Eckrad") def ils_eckrad(): """Befehl ILS_Eckrad - fragt Eingaben ab und fuegt Eckrad ein. Ablauf: 1. Beruehrpunkt anklicken (wo der Kreis tangential anliegen soll) 2. Richtungspunkt anklicken (bestimmt Versatzrichtung zum Kreismittelpunkt) 3. Kreismittelpunkt = Beruehrpunkt + Radius in Richtung des 2. Klicks 4. Hoehe aus Z-Koordinate oder manuell """ ed = Ed.Editor() db = Db.HostApplicationServices().workingDatabase() # 1. Beruehrpunkt (Tangentenpunkt) res_tangent = ed.getPoint("\nBeruehrpunkt Eckrad (Tangente): ") if res_tangent[0] != Ed.PromptStatus.eOk: return pt_tangent = res_tangent[1] # 2. Richtungspunkt (Gummiband vom Beruehrpunkt) res_dir = ed.getPoint("\nRichtung zum Kreismittelpunkt: ", pt_tangent) if res_dir[0] != Ed.PromptStatus.eOk: return pt_dir = res_dir[1] # 3. Winkel und Versatz berechnen dx = pt_dir.x - pt_tangent.x dy = pt_dir.y - pt_tangent.y dist = math.sqrt(dx * dx + dy * dy) if dist <= 0.001: print("\nFehler: Beruehr- und Richtungspunkt sind identisch!") return ux = dx / dist uy = dy / dist radius = KREISEL_DURCHMESSER / 2.0 center_x = pt_tangent.x + radius * ux center_y = pt_tangent.y + radius * uy rotation = math.degrees(math.atan2(dy, dx)) # 4. Hoehe bestimmen if pt_tangent.z > 0: hoehe = pt_tangent.z print(f"\n-> Hoehe aus 3D-Punkt uebernommen: {hoehe:.0f}") else: res_hoehe = ed.getDouble(f"\nHoehe (Z-Koordinate) <{ECKRAD_DEFAULT_HOEHE:.0f}>: ") if res_hoehe[0] == Ed.PromptStatus.eOk: hoehe = res_hoehe[1] else: hoehe = ECKRAD_DEFAULT_HOEHE # 5. Einfuegen insert_pt = Ge.Point3d(center_x, center_y, hoehe) insert_eckrad(db, insert_pt, rotation) # ============================================================ # Eckrad bearbeiten (wx-Dialog) # ============================================================ class EckradEditDialog(wx.Dialog): """Dialog zum Bearbeiten eines bestehenden Eckrads.""" def __init__(self, parent, attribs): super().__init__(parent, title="Eckrad bearbeiten", size=(320, 220), style=wx.DEFAULT_DIALOG_STYLE) panel = wx.Panel(self) sizer = wx.BoxSizer(wx.VERTICAL) # Name (readonly) row_name = wx.BoxSizer(wx.HORIZONTAL) row_name.Add(wx.StaticText(panel, label="Name:"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5) self.txt_name = wx.TextCtrl(panel, value=attribs.get("NAME", ""), size=(180, -1), style=wx.TE_READONLY) row_name.Add(self.txt_name, 1) sizer.Add(row_name, 0, wx.EXPAND | wx.ALL, 5) # Drehrichtung row_dreh = wx.BoxSizer(wx.HORIZONTAL) row_dreh.Add(wx.StaticText(panel, label="Drehrichtung:"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5) self.cb_drehrichtung = wx.Choice(panel, choices=["UZS", "GUZ"]) self.cb_drehrichtung.SetSelection( 1 if attribs.get("DREHRICHTUNG") == "GUZ" else 0) row_dreh.Add(self.cb_drehrichtung, 0) sizer.Add(row_dreh, 0, wx.EXPAND | wx.ALL, 5) # Hoehe row_hoehe = wx.BoxSizer(wx.HORIZONTAL) row_hoehe.Add(wx.StaticText(panel, label="Hoehe (mm):"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5) self.txt_hoehe = wx.TextCtrl(panel, value=attribs.get("HOEHE", "0"), size=(80, -1)) row_hoehe.Add(self.txt_hoehe, 0) sizer.Add(row_hoehe, 0, wx.EXPAND | wx.ALL, 5) # OK / Cancel btn_sizer = self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL) sizer.Add(btn_sizer, 0, wx.EXPAND | wx.ALL, 10) panel.SetSizer(sizer) self.Fit() self.CenterOnScreen() def get_values(self): """Dialog-Werte als dict zurueckgeben.""" return { "drehrichtung": "GUZ" if self.cb_drehrichtung.GetSelection() == 1 else "UZS", "hoehe": self.txt_hoehe.GetValue(), } def eckrad_edit_entity(db, ent_id): """Eckrad per wxPython-Dialog bearbeiten (fuer eine bestimmte Entity-ID). Wird von EckradEdit und SSG_BLOCKEDIT aufgerufen. """ bref = Db.BlockReference(ent_id, Db.OpenMode.kForRead) attribs = read_block_attribs(bref) base_point = bref.position() rotation_deg = math.degrees(bref.rotation()) # HOEHE aus Z-Koordinate if base_point.z > 0: attribs["HOEHE"] = f"{base_point.z:.0f}" print(f"\n-> Bestehend: Name={attribs.get('NAME', '?')}" f" Drehrichtung={attribs.get('DREHRICHTUNG', '?')}" f" Hoehe={attribs.get('HOEHE', '?')}") dlg = EckradEditDialog(None, attribs) result = dlg.ShowModal() if result == wx.ID_OK: new_values = dlg.get_values() dlg.Destroy() attribs["DREHRICHTUNG"] = new_values["drehrichtung"] attribs["HOEHE"] = new_values["hoehe"] new_hoehe = float(new_values["hoehe"]) # Alten Block loeschen bref.upgradeOpen() bref.erase() # Neu einfuegen insert_pt = Ge.Point3d(base_point.x, base_point.y, new_hoehe) insert_eckrad(db, insert_pt, rotation_deg, attribs) print(f"\nEckrad aktualisiert: Drehrichtung={new_values['drehrichtung']}" f" Hoehe={new_values['hoehe']}") else: dlg.Destroy() print("\nAbgebrochen.") @Rx.command("EckradEdit") def cmd_eckrad_edit(): """EckradEdit: Bestehenden Eckrad per wxPython-Dialog bearbeiten.""" ed = Ed.Editor() db = Db.HostApplicationServices().workingDatabase() res = ed.entSel("\nEckrad-Block auswaehlen: ") if res[0] != Ed.PromptStatus.eOk: return ent_id = res[1] ent = Db.Entity(ent_id, Db.OpenMode.kForRead) if not ent.isKindOf(Db.BlockReference.desc()): print("\nKein Block ausgewaehlt!") return eckrad_edit_entity(db, ent_id)