306 lines
9.5 KiB
Python
306 lines
9.5 KiB
Python
# 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)
|