Fenster/Tueren LoD + Stile + Phase-3-Ausschnitt-Darstellung + UI-Konsistenz
Fenster/Tueren: - 3-stufige SIA-400-Darstellung pro Element: einfach (1:100, flache Scheibe ohne Tiefe in Wand-Mittelebene), standard (1:50, Rahmen + Glas + Sims), detail (1:20, Doppelverglasung). - Aussenseite-Flag mit Auto-Detection aus der Click-Richtung beim Setzen — Sim sitzt automatisch aussen. Im Panel als Umkehren-Toggle. - Tueren-Rahmen-Typ Zarge|Block — Blockrahmen ragt seitlich raus. - Rahmen-Offset (m von Wand-Innenseite) ersetzt das 3-Preset Lage- Feld. Wirkt auch in der einfachen Darstellung (Pane sitzt auf der Rahmen-Mittelebene, nicht in Wand-Mitte). - Sims nur AUSSEN. Innen entfaellt — der Sim ist gleichzeitig der visuelle Indikator fuer die Aussenseite. - Oeffnungs-Stile: list/save/delete-API mit 6 Default-Presets (Fenster Standard/Gross/Bandlage, Tuer Innen/Eingang/Verglast). Style-ID per UserString am Objekt persistiert. Im Panel BarCombo mit "Aktuelle als Stil speichern…". Beim Rhino-Command "Stil"- Option zum Picken vor dem Klick. Ausschnitt-Darstellung (Phase 3): - Doc-Level Override dossier_aktive_darstellung gewinnt vor per- Object-Setting. Wechsel triggert Regen aller Oeffnungen via neuer regenerate_all_oeffnungen-API. - Ausschnitt-Capture speichert die Darstellung mit, Restore wendet sie an und regeneriert. - Oberleiste-Quick-Switch BarCombo mit 4 Optionen. - AusschnittSettings-Dialog: Darstellungs-Dropdown. Gestaltung (SectionStyle Phase 2): - _set_section_style schreibt per-Object SectionHatchIndex/Scale/ Rotation/Color mit Multi-Fallback (Property-Namen varieren je Rhino-Build). _selection_summary liest die selben zurueck. - HatchEditor als shared Component fuer Fill + Section. - geometryKind ignoriert DOSSIER-Source-Curves damit Wand-Selektion (Axis + Volume) als 3D klassifiziert wird. UI-Konsistenz Panels: - Ebenenkombi zurueck als eigene Section oben im Ebenen-Panel, Modelldarstellung-Dropdown an die freigewordene Position in der Oberleiste (Row 1 Col 2 im 2x2-Preset-Block). - BarCombo erweitert: stretch-Prop (Pill waechst auf Container- Breite), onSecond/secondIcon/secondTitle fuer 2. Trailing-Button, gearIcon-Prop. Plus-Slot immer ganz aussen rechts, Settings-Slot direkt nach dem Caret. - Ebenen + Zeichnungsebenen visuell kohaerent: identisches Padding (1px 12px 1px 0), Chevron/Spacer-Slot 12px, Master-Row mit Eye 16x16 + Lock 14x14, gleiche Border + Borderfarbe. Eye-Icons in beiden Panels untereinander ausgerichtet. - Properties-Container ohne Border (war zuvor accent-gruen, dann border — User wollte gar nichts mehr). - ElementList raus aus dem Elemente-Panel (Uebersicht via Tree- Window erreichbar). NeuesElement bleibt voll sichtbar bei Selektion (kein Collapse), Properties oben. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+35
-6
@@ -327,6 +327,8 @@ class AusschnittBridge(panel_base.BaseBridge):
|
||||
elif t == "DELETE": self._delete(p.get("id"))
|
||||
elif t == "SET_FOLDER": self._set_field(p.get("id"), "folder", p.get("folder") or "")
|
||||
elif t == "SET_SCALE": self._set_field(p.get("id"), "scale", p.get("scale") or "")
|
||||
elif t == "SET_DARSTELLUNG": self._set_field(p.get("id"), "darstellung",
|
||||
p.get("darstellung") or "")
|
||||
elif t == "DUPLICATE": self._duplicate(p.get("id"))
|
||||
elif t == "ADD_FOLDER": self._add_folder(p.get("name"))
|
||||
elif t == "REMOVE_FOLDER": self._remove_folder(p.get("name"))
|
||||
@@ -494,13 +496,21 @@ class AusschnittBridge(panel_base.BaseBridge):
|
||||
print("[AUSSCHNITTE] Live-Skala (Fallback):", ex)
|
||||
if not scale_str and prior_scale:
|
||||
scale_str = prior_scale # Perspective -> alten Wert nicht ueberschreiben
|
||||
# Darstellungs-Override aus dem aktuellen Doc-Setting uebernehmen.
|
||||
# Leer = "kein Override, per-Object respektieren".
|
||||
darst = ""
|
||||
try:
|
||||
import elemente
|
||||
darst = elemente.get_aktive_darstellung(doc) or ""
|
||||
except Exception: pass
|
||||
return {
|
||||
"id": existing_id or "snap_" + uuid.uuid4().hex[:8],
|
||||
"name": name,
|
||||
"scale": scale_str,
|
||||
"camera": _capture_camera(vp),
|
||||
"layers": _capture_layers(doc),
|
||||
"dossier": _capture_dossier_state(doc),
|
||||
"id": existing_id or "snap_" + uuid.uuid4().hex[:8],
|
||||
"name": name,
|
||||
"scale": scale_str,
|
||||
"camera": _capture_camera(vp),
|
||||
"layers": _capture_layers(doc),
|
||||
"dossier": _capture_dossier_state(doc),
|
||||
"darstellung": darst,
|
||||
}
|
||||
|
||||
def _save(self, name):
|
||||
@@ -558,6 +568,21 @@ class AusschnittBridge(panel_base.BaseBridge):
|
||||
rhinopanel._notify_oberleiste_combs()
|
||||
except Exception: pass
|
||||
_apply_dossier_state(doc, snap.get("dossier") or snap.get("pause") or {})
|
||||
# Darstellung anwenden + Oeffnungen regenerieren
|
||||
try:
|
||||
import elemente
|
||||
new_darst = snap.get("darstellung") or ""
|
||||
cur_darst = elemente.get_aktive_darstellung(doc) or ""
|
||||
if new_darst != cur_darst:
|
||||
elemente.set_aktive_darstellung(doc, new_darst)
|
||||
elemente.regenerate_all_oeffnungen(doc)
|
||||
# Oberleiste-Topbar muss neuen Wert spiegeln
|
||||
try:
|
||||
b = sc.sticky.get("oberleiste_bridge")
|
||||
if b is not None: b._send_state(force=True)
|
||||
except Exception: pass
|
||||
except Exception as ex:
|
||||
print("[AUSSCHNITTE] darstellung apply:", ex)
|
||||
# Overrides: nur anwenden wenn das Snap "applyOverrides" gesetzt hat.
|
||||
# Sonst bleibt der aktuelle User-Override-State unangetastet.
|
||||
if snap.get("applyOverrides"):
|
||||
@@ -786,6 +811,7 @@ class AusschnittBridge(panel_base.BaseBridge):
|
||||
"overridesEnabled": bool(sn.get("overridesEnabled", False)),
|
||||
"overridesPreset": sn.get("overridesPreset") or "",
|
||||
"layerCombination": sn.get("layerCombination") or "",
|
||||
"darstellung": sn.get("darstellung") or "",
|
||||
},
|
||||
"displayModes": display_modes,
|
||||
"overridesPresets": overrides_presets,
|
||||
@@ -815,6 +841,9 @@ class AusschnittBridge(panel_base.BaseBridge):
|
||||
target["overridesPreset"] = (settings.get("overridesPreset") or "").strip()
|
||||
# Ebenenkombi
|
||||
target["layerCombination"] = (settings.get("layerCombination") or "").strip()
|
||||
# Darstellung (SIA-400 LoD Override fuer diesen Ausschnitt)
|
||||
darst = (settings.get("darstellung") or "").strip()
|
||||
target["darstellung"] = darst if darst in ("einfach", "standard", "detail") else ""
|
||||
outer._store(d, snaps)
|
||||
outer._send_list()
|
||||
print("[AUSSCHNITTE] Settings fuer '{}' aktualisiert".format(target.get("name")))
|
||||
|
||||
+504
-54
@@ -59,6 +59,182 @@ _KEY_OEFF_SIMS_AUS = "dossier_oeff_sims_aus" # Style: "ohne"|"schmal"|"
|
||||
_KEY_OEFF_SIMS_IN = "dossier_oeff_sims_in" # Style: "ohne"|"schmal"|"standard"|"breit"
|
||||
_KEY_OEFF_GLAS = "dossier_oeff_glas" # "1"|"0" — sichtbare Glas-Scheibe
|
||||
_KEY_OEFF_REFERENZ = "dossier_oeff_referenz" # "mid" | "links" | "rechts" — Lage des Klick-Punkts in der Oeffnung
|
||||
_KEY_OEFF_DARSTELLUNG = "dossier_oeff_darstellung" # "einfach" | "standard" | "detail" — SIA-400 LoD
|
||||
_KEY_OEFF_AUSSENSEITE = "dossier_oeff_aussenseite" # "links" | "rechts" — welche Wand-Seite ist aussen
|
||||
_KEY_OEFF_TUER_RAHMEN = "dossier_oeff_tuer_rahmen" # "zarge" | "block" — Tueren-Rahmen-Typ
|
||||
_KEY_OEFF_RAHMEN_OFFSET = "dossier_oeff_rahmen_offset" # float (m): Abstand Rahmen-Innenkante von Wand-Innenseite
|
||||
_KEY_AKTIVE_DARSTELLUNG = "dossier_aktive_darstellung" # doc-level global override: einfach|standard|detail|"" (= per-object)
|
||||
_KEY_OEFF_STYLE_ID = "dossier_oeff_style_id" # per-Object: referenziert einen Style aus dossier_oeff_styles
|
||||
_KEY_OEFF_STYLES = "dossier_oeff_styles" # JSON-Liste aller Fenster/Tueren-Styles
|
||||
_KEY_OEFF_STYLE_ACTIVE = "dossier_oeff_style_active" # zuletzt benutzte Style-ID (pro typ)
|
||||
|
||||
_OEFF_DARSTELLUNGEN = ("einfach", "standard", "detail")
|
||||
|
||||
|
||||
def get_aktive_darstellung(doc):
|
||||
"""Liest die doc-level Darstellungs-Override. Leer → per-object."""
|
||||
if doc is None: return ""
|
||||
try:
|
||||
v = doc.Strings.GetValue(_KEY_AKTIVE_DARSTELLUNG) or ""
|
||||
except Exception:
|
||||
v = ""
|
||||
return v if v in _OEFF_DARSTELLUNGEN else ""
|
||||
|
||||
|
||||
def set_aktive_darstellung(doc, value):
|
||||
"""Setzt die doc-level Darstellungs-Override. Leer → clear."""
|
||||
if doc is None: return
|
||||
try:
|
||||
if value and value in _OEFF_DARSTELLUNGEN:
|
||||
doc.Strings.SetString(_KEY_AKTIVE_DARSTELLUNG, value)
|
||||
else:
|
||||
doc.Strings.Delete(_KEY_AKTIVE_DARSTELLUNG)
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] set_aktive_darstellung:", ex)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fenster/Tueren-Styles (Presets) — analog text_create.list_styles
|
||||
|
||||
_OEFF_STYLE_FIELDS = (
|
||||
"typ", "breite", "hoehe", "brueest",
|
||||
"rahmenB", "rahmenTiefe", "rahmenOffset",
|
||||
"fluegel", "simsAus", "glas",
|
||||
"darstellung", "tuerRahmen",
|
||||
)
|
||||
|
||||
_OEFF_DEFAULT_STYLES = [
|
||||
{"name": "Fenster Standard", "typ": "fenster",
|
||||
"breite": 1.20, "hoehe": 1.40, "brueest": 0.90,
|
||||
"rahmenB": 0.06, "rahmenTiefe": 0.08, "rahmenOffset": 0.05,
|
||||
"fluegel": 2, "simsAus": "standard", "glas": True,
|
||||
"darstellung": "standard"},
|
||||
{"name": "Fenster Gross", "typ": "fenster",
|
||||
"breite": 2.00, "hoehe": 1.80, "brueest": 0.40,
|
||||
"rahmenB": 0.08, "rahmenTiefe": 0.10, "rahmenOffset": 0.05,
|
||||
"fluegel": 3, "simsAus": "breit", "glas": True,
|
||||
"darstellung": "standard"},
|
||||
{"name": "Fenster Bandlage (boden)", "typ": "fenster",
|
||||
"breite": 3.00, "hoehe": 0.60, "brueest": 0.00,
|
||||
"rahmenB": 0.06, "rahmenTiefe": 0.08, "rahmenOffset": 0.05,
|
||||
"fluegel": 3, "simsAus": "schmal", "glas": True,
|
||||
"darstellung": "standard"},
|
||||
{"name": "Tuer Innen", "typ": "tuer",
|
||||
"breite": 0.90, "hoehe": 2.10, "brueest": 0.00,
|
||||
"rahmenB": 0.05, "rahmenTiefe": 0.08, "rahmenOffset": 0.05,
|
||||
"fluegel": 1, "simsAus": "ohne", "glas": False,
|
||||
"darstellung": "standard", "tuerRahmen": "zarge"},
|
||||
{"name": "Tuer Eingang", "typ": "tuer",
|
||||
"breite": 1.00, "hoehe": 2.20, "brueest": 0.00,
|
||||
"rahmenB": 0.08, "rahmenTiefe": 0.10, "rahmenOffset": 0.05,
|
||||
"fluegel": 1, "simsAus": "ohne", "glas": False,
|
||||
"darstellung": "standard", "tuerRahmen": "block"},
|
||||
{"name": "Tuer Verglast", "typ": "tuer",
|
||||
"breite": 0.90, "hoehe": 2.10, "brueest": 0.00,
|
||||
"rahmenB": 0.06, "rahmenTiefe": 0.08, "rahmenOffset": 0.05,
|
||||
"fluegel": 1, "simsAus": "ohne", "glas": True,
|
||||
"darstellung": "standard", "tuerRahmen": "zarge"},
|
||||
]
|
||||
|
||||
|
||||
def _normalize_oeff_style(s):
|
||||
"""Filtert auf erlaubte Felder + setzt sinnvolle Defaults."""
|
||||
out = {}
|
||||
for k in _OEFF_STYLE_FIELDS:
|
||||
if k in s: out[k] = s[k]
|
||||
return out
|
||||
|
||||
|
||||
def list_oeff_styles(doc, typ=None):
|
||||
"""Liste aller Window/Tuer-Styles. typ='fenster'|'tuer' filtert.
|
||||
Seedet Defaults beim ersten Zugriff."""
|
||||
if doc is None: return []
|
||||
import json, uuid
|
||||
try:
|
||||
raw = doc.Strings.GetValue(_KEY_OEFF_STYLES)
|
||||
if not raw:
|
||||
seeded = []
|
||||
for i, s in enumerate(_OEFF_DEFAULT_STYLES):
|
||||
norm = _normalize_oeff_style(s)
|
||||
norm["id"] = "os_default_" + str(i)
|
||||
norm["name"] = s["name"]
|
||||
seeded.append(norm)
|
||||
try: doc.Strings.SetString(_KEY_OEFF_STYLES, json.dumps(seeded))
|
||||
except Exception: pass
|
||||
items = seeded
|
||||
else:
|
||||
parsed = json.loads(raw)
|
||||
items = parsed if isinstance(parsed, list) else []
|
||||
out = []
|
||||
for it in items:
|
||||
if not isinstance(it, dict): continue
|
||||
n = _normalize_oeff_style(it)
|
||||
n["id"] = it.get("id") or ("os_" + uuid.uuid4().hex[:8])
|
||||
n["name"] = it.get("name") or "Stil"
|
||||
out.append(n)
|
||||
if typ in ("fenster", "tuer"):
|
||||
out = [s for s in out if s.get("typ") == typ]
|
||||
return out
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] list_oeff_styles:", ex)
|
||||
return []
|
||||
|
||||
|
||||
def save_oeff_style(doc, name, settings):
|
||||
"""Speichert (oder updated) einen Style unter `name`. Returns ID."""
|
||||
if doc is None or not name: return None
|
||||
import json, uuid
|
||||
items_all = []
|
||||
try:
|
||||
raw = doc.Strings.GetValue(_KEY_OEFF_STYLES)
|
||||
if raw:
|
||||
parsed = json.loads(raw)
|
||||
if isinstance(parsed, list):
|
||||
items_all = [it for it in parsed if isinstance(it, dict)]
|
||||
except Exception: pass
|
||||
sid = None
|
||||
for it in items_all:
|
||||
if it.get("name") == name:
|
||||
sid = it.get("id"); break
|
||||
norm = _normalize_oeff_style(settings or {})
|
||||
norm["id"] = sid or ("os_" + uuid.uuid4().hex[:8])
|
||||
norm["name"] = name
|
||||
if sid:
|
||||
items_all = [norm if it.get("id") == sid else it for it in items_all]
|
||||
else:
|
||||
items_all.append(norm)
|
||||
try: doc.Strings.SetString(_KEY_OEFF_STYLES, json.dumps(items_all))
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] save_oeff_style:", ex)
|
||||
return norm["id"]
|
||||
|
||||
|
||||
def delete_oeff_style(doc, sid):
|
||||
if doc is None or not sid: return
|
||||
import json
|
||||
try:
|
||||
items = list_oeff_styles(doc)
|
||||
items = [it for it in items if it.get("id") != sid]
|
||||
doc.Strings.SetString(_KEY_OEFF_STYLES, json.dumps(items))
|
||||
except Exception: pass
|
||||
|
||||
|
||||
def get_active_oeff_style_id(doc, typ):
|
||||
if doc is None: return None
|
||||
try:
|
||||
raw = doc.Strings.GetValue(_KEY_OEFF_STYLE_ACTIVE + "_" + typ)
|
||||
return raw or None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def set_active_oeff_style_id(doc, typ, sid):
|
||||
if doc is None or typ not in ("fenster", "tuer"): return
|
||||
try:
|
||||
doc.Strings.SetString(_KEY_OEFF_STYLE_ACTIVE + "_" + typ, sid or "")
|
||||
except Exception: pass
|
||||
_OEFF_AUSSENSEITEN = ("links", "rechts")
|
||||
_OEFF_TUER_RAHMEN = ("zarge", "block")
|
||||
|
||||
_OEFF_REFERENZ_OPTIONS = ("mid", "links", "rechts")
|
||||
|
||||
@@ -1705,7 +1881,9 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over,
|
||||
oeff_rahmen_b=None, oeff_rahmen_tiefe=None, oeff_rahmen_pos=None,
|
||||
oeff_fluegel=None,
|
||||
oeff_sims_aus=None, oeff_sims_in=None, oeff_glas=None,
|
||||
oeff_referenz=None,
|
||||
oeff_referenz=None, oeff_darstellung=None,
|
||||
oeff_aussenseite=None, oeff_tuer_rahmen=None,
|
||||
oeff_rahmen_offset=None, oeff_style_id=None,
|
||||
geschoss_end=None, treppe_breite=None,
|
||||
treppe_n=None, treppe_referenz=None,
|
||||
treppe_modus=None, treppe_lauf_d=None, treppe_art=None,
|
||||
@@ -1775,6 +1953,20 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over,
|
||||
obj_attrs.SetUserString(_KEY_OEFF_GLAS, "1" if bool(oeff_glas) else "0")
|
||||
if oeff_referenz is not None and oeff_referenz in _OEFF_REFERENZ_OPTIONS:
|
||||
obj_attrs.SetUserString(_KEY_OEFF_REFERENZ, oeff_referenz)
|
||||
if oeff_darstellung is not None and oeff_darstellung in _OEFF_DARSTELLUNGEN:
|
||||
obj_attrs.SetUserString(_KEY_OEFF_DARSTELLUNG, oeff_darstellung)
|
||||
if oeff_aussenseite is not None and oeff_aussenseite in _OEFF_AUSSENSEITEN:
|
||||
obj_attrs.SetUserString(_KEY_OEFF_AUSSENSEITE, oeff_aussenseite)
|
||||
if oeff_tuer_rahmen is not None and oeff_tuer_rahmen in _OEFF_TUER_RAHMEN:
|
||||
obj_attrs.SetUserString(_KEY_OEFF_TUER_RAHMEN, oeff_tuer_rahmen)
|
||||
if oeff_rahmen_offset is not None:
|
||||
try:
|
||||
v = max(0.0, float(oeff_rahmen_offset))
|
||||
obj_attrs.SetUserString(_KEY_OEFF_RAHMEN_OFFSET, "{:.4f}".format(v))
|
||||
except Exception: pass
|
||||
if oeff_style_id is not None:
|
||||
try: obj_attrs.SetUserString(_KEY_OEFF_STYLE_ID, str(oeff_style_id) or "")
|
||||
except Exception: pass
|
||||
# --- Treppen-Felder ---
|
||||
if geschoss_end is not None:
|
||||
obj_attrs.SetUserString(_KEY_GESCHOSS_END, geschoss_end or "")
|
||||
@@ -1925,6 +2117,19 @@ def _read_meta(obj):
|
||||
ogl = (og_raw == "1") if og_raw in ("0", "1") else is_fenster
|
||||
oref = a.GetUserString(_KEY_OEFF_REFERENZ) or "mid"
|
||||
if oref not in _OEFF_REFERENZ_OPTIONS: oref = "mid"
|
||||
odarst = a.GetUserString(_KEY_OEFF_DARSTELLUNG) or "standard"
|
||||
if odarst not in _OEFF_DARSTELLUNGEN: odarst = "standard"
|
||||
oauss = a.GetUserString(_KEY_OEFF_AUSSENSEITE) or "rechts"
|
||||
if oauss not in _OEFF_AUSSENSEITEN: oauss = "rechts"
|
||||
otrah = a.GetUserString(_KEY_OEFF_TUER_RAHMEN) or "zarge"
|
||||
if otrah not in _OEFF_TUER_RAHMEN: otrah = "zarge"
|
||||
# Rahmen-Offset (m, von Wand-Innenseite). Default 5cm. Wenn Legacy-
|
||||
# Wert (rahmen_pos) gesetzt aber kein offset, benutzt build-Logik
|
||||
# weiterhin den Preset.
|
||||
try: oro = float(a.GetUserString(_KEY_OEFF_RAHMEN_OFFSET) or "0.05")
|
||||
except Exception: oro = 0.05
|
||||
if oro < 0: oro = 0.0
|
||||
ostyle = a.GetUserString(_KEY_OEFF_STYLE_ID) or ""
|
||||
# Treppen-Felder
|
||||
gend = a.GetUserString(_KEY_GESCHOSS_END) or ""
|
||||
try: tb = float(a.GetUserString(_KEY_TREPPE_BREITE) or "1.0")
|
||||
@@ -2041,6 +2246,11 @@ def _read_meta(obj):
|
||||
"oeff_sims_in": osi,
|
||||
"oeff_glas": ogl,
|
||||
"oeff_referenz": oref,
|
||||
"oeff_darstellung": odarst,
|
||||
"oeff_aussenseite": oauss,
|
||||
"oeff_tuer_rahmen": otrah,
|
||||
"oeff_rahmen_offset": oro,
|
||||
"oeff_style_id": ostyle,
|
||||
"geschoss_end": gend,
|
||||
"treppe_breite": tb,
|
||||
"treppe_n": tn,
|
||||
@@ -2281,6 +2491,26 @@ def _make_oeffnung_pieces(axis_curve, point_on_axis, wall_dicke, oeff_meta, base
|
||||
sims_in_style = oeff_meta.get("oeff_sims_in", "ohne")
|
||||
has_glas = bool(oeff_meta.get("oeff_glas", False))
|
||||
is_tuer = (oeff_meta.get("oeff_typ") == "tuer")
|
||||
# Doc-level Darstellungs-Override gewinnt vor per-Object-Setting —
|
||||
# damit Ausschnitt-Wechsel / Oberleiste-Quick-Switch alle Oeffnungen
|
||||
# konsistent zwingen koennen.
|
||||
_doc = Rhino.RhinoDoc.ActiveDoc
|
||||
global_darst = get_aktive_darstellung(_doc) if _doc is not None else ""
|
||||
darstellung = global_darst or oeff_meta.get("oeff_darstellung", "standard")
|
||||
if darstellung not in _OEFF_DARSTELLUNGEN:
|
||||
darstellung = "standard"
|
||||
# Aussenseite: +1 = aussen ist +Plane.ZAxis (default), -1 = aussen ist
|
||||
# -Plane.ZAxis (Wand "umgedreht"). Wird verwendet um Sims aus/in
|
||||
# konsistent zu platzieren unabhaengig von der Wand-Achsen-Richtung.
|
||||
aussenseite = oeff_meta.get("oeff_aussenseite", "rechts")
|
||||
aus_sign = +1 if aussenseite == "rechts" else -1
|
||||
# Tueren-Rahmen-Typ: 'zarge' (klassisch, sitzt IM Wandquerschnitt) oder
|
||||
# 'block' (Blockrahmen, sitzt UM die Oeffnung und ragt seitlich raus).
|
||||
tuer_rahmen = oeff_meta.get("oeff_tuer_rahmen", "zarge")
|
||||
# Rahmen-Offset (m): Abstand der Rahmen-Innenkante von der Wand-
|
||||
# Innenseite. 0 = bündig innen, wand_dicke-rahmen_t = bündig aussen.
|
||||
try: rahmen_offset = max(0.0, float(oeff_meta.get("oeff_rahmen_offset", 0.05)))
|
||||
except Exception: rahmen_offset = 0.05
|
||||
|
||||
half_b = breite * 0.5
|
||||
half_d = float(wall_dicke) * 0.5
|
||||
@@ -2301,14 +2531,60 @@ def _make_oeffnung_pieces(axis_curve, point_on_axis, wall_dicke, oeff_meta, base
|
||||
if inner_l >= inner_r - 1e-6 or payload_z_lo >= payload_z_hi - 1e-6:
|
||||
return [] # Rahmen-Profil zu dick fuer Oeffnung
|
||||
|
||||
frame_perp_lo, frame_perp_hi = _resolve_rahmen_perp_range(
|
||||
half_d, rahmen_t, rahmen_pos)
|
||||
# Rahmen-Position aus Offset bestimmen. aus_sign=+1 -> Innen ist
|
||||
# -perp-Seite. Inside-Edge = -half_d + offset. Outside-Edge = inside
|
||||
# + rahmen_t. Maximaler Offset = wand_dicke - rahmen_t (sonst ragt
|
||||
# der Rahmen raus). Clamping mit 1mm Inset gegen Z-Fight.
|
||||
rt = max(0.01, float(rahmen_t))
|
||||
rt = min(rt, 2.0 * half_d - 0.002)
|
||||
max_offset = 2.0 * half_d - rt - 0.001
|
||||
eff_offset = min(max(0.0, rahmen_offset), max(0.0, max_offset))
|
||||
if aus_sign > 0:
|
||||
frame_perp_lo = -half_d + eff_offset
|
||||
frame_perp_hi = frame_perp_lo + rt
|
||||
else:
|
||||
frame_perp_hi = +half_d - eff_offset
|
||||
frame_perp_lo = frame_perp_hi - rt
|
||||
|
||||
# --- EINFACH (1:100): nur eine flache Scheibe OHNE Tiefe in der
|
||||
# Rahmen-Mittelebene (= dort wo das Glas im Standard sitzt). Im
|
||||
# 2D-Plan ergibt das eine einzelne Linie quer durch die Oeffnung,
|
||||
# auf dem korrekten Tiefen-Offset.
|
||||
if darstellung == "einfach":
|
||||
try:
|
||||
pane_perp = (frame_perp_lo + frame_perp_hi) * 0.5
|
||||
# Plane.Origin ans Wand-Achsenpunkt verschoben um pane_perp
|
||||
# entlang der Plane-Normale (=tan x Z = (0,0,1) cross tan).
|
||||
normal = rg.Vector3d.CrossProduct(tan, rg.Vector3d(0, 0, 1))
|
||||
try: normal.Unitize()
|
||||
except Exception: pass
|
||||
origin = rg.Point3d(pt.X + normal.X * pane_perp,
|
||||
pt.Y + normal.Y * pane_perp,
|
||||
0)
|
||||
plane = rg.Plane(origin, tan, rg.Vector3d(0, 0, 1))
|
||||
surf = rg.PlaneSurface(plane,
|
||||
rg.Interval(-half_b, +half_b),
|
||||
rg.Interval(z_lo, z_hi))
|
||||
brep = surf.ToBrep()
|
||||
return [brep] if brep is not None else []
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] einfach pane:", ex)
|
||||
return []
|
||||
|
||||
pieces = []
|
||||
|
||||
# --- RAHMEN: outer box - inner box, sauberer single-Brep
|
||||
# --- RAHMEN: outer box - inner box, sauberer single-Brep.
|
||||
# Tueren mit 'block'-Rahmen: outer box ist breiter+hoeher als die
|
||||
# Oeffnung (Blockrahmen sitzt UM den Wanddurchbruch). Sonst klassische
|
||||
# Zarge: outer = Oeffnungsmasse.
|
||||
is_block = (is_tuer and tuer_rahmen == "block")
|
||||
block_overlap = 0.05 # 5cm seitlich + oben
|
||||
out_l = (-half_b - block_overlap) if is_block else -half_b
|
||||
out_r = (+half_b + block_overlap) if is_block else +half_b
|
||||
out_z_hi = (z_hi + block_overlap) if is_block else z_hi
|
||||
out_z_lo = z_lo # unten immer bei z_lo (Boden / Bruestung)
|
||||
try:
|
||||
outer_box = _make_oeff_box(pt, tan, -half_b, +half_b, z_lo, z_hi,
|
||||
outer_box = _make_oeff_box(pt, tan, out_l, out_r, out_z_lo, out_z_hi,
|
||||
frame_perp_lo, frame_perp_hi)
|
||||
# Inner box leicht laenger in perp Richtung damit der Diff sauber
|
||||
# durchschneidet (keine Hauchschicht uebrig).
|
||||
@@ -2348,45 +2624,52 @@ def _make_oeffnung_pieces(axis_curve, point_on_axis, wall_dicke, oeff_meta, base
|
||||
|
||||
if fill_t > 0:
|
||||
fill_mid = (frame_perp_lo + frame_perp_hi) * 0.5
|
||||
fill_lo = fill_mid - fill_t * 0.5
|
||||
fill_hi = fill_mid + fill_t * 0.5
|
||||
if fluegel > 1:
|
||||
span = inner_r - inner_l
|
||||
for i in range(fluegel):
|
||||
fx_lo = inner_l + span * (float(i) / fluegel)
|
||||
fx_hi = inner_l + span * (float(i + 1) / fluegel)
|
||||
if i > 0: fx_lo += rahmen_b * 0.5
|
||||
if i < fluegel - 1: fx_hi -= rahmen_b * 0.5
|
||||
fp = _make_oeff_box(pt, tan, fx_lo, fx_hi,
|
||||
payload_z_lo, payload_z_hi,
|
||||
fill_lo, fill_hi)
|
||||
if fp is not None: pieces.append(fp)
|
||||
# DETAIL (1:20): Doppelverglasung — 2 Scheiben a 6mm, 16mm SZR
|
||||
is_double_glas = (darstellung == "detail" and has_glas and not is_tuer)
|
||||
if is_double_glas:
|
||||
single_t = 0.006
|
||||
szr = 0.016
|
||||
total = single_t * 2 + szr
|
||||
outer_lo = fill_mid - total * 0.5
|
||||
pane_specs = [
|
||||
(outer_lo, outer_lo + single_t),
|
||||
(outer_lo + single_t + szr,
|
||||
outer_lo + single_t + szr + single_t),
|
||||
]
|
||||
else:
|
||||
fp = _make_oeff_box(pt, tan, inner_l, inner_r,
|
||||
payload_z_lo, payload_z_hi,
|
||||
fill_lo, fill_hi)
|
||||
if fp is not None: pieces.append(fp)
|
||||
pane_specs = [(fill_mid - fill_t * 0.5, fill_mid + fill_t * 0.5)]
|
||||
for (fl, fh) in pane_specs:
|
||||
if fluegel > 1:
|
||||
span = inner_r - inner_l
|
||||
for i in range(fluegel):
|
||||
fx_lo = inner_l + span * (float(i) / fluegel)
|
||||
fx_hi = inner_l + span * (float(i + 1) / fluegel)
|
||||
if i > 0: fx_lo += rahmen_b * 0.5
|
||||
if i < fluegel - 1: fx_hi -= rahmen_b * 0.5
|
||||
fp = _make_oeff_box(pt, tan, fx_lo, fx_hi,
|
||||
payload_z_lo, payload_z_hi,
|
||||
fl, fh)
|
||||
if fp is not None: pieces.append(fp)
|
||||
else:
|
||||
fp = _make_oeff_box(pt, tan, inner_l, inner_r,
|
||||
payload_z_lo, payload_z_hi,
|
||||
fl, fh)
|
||||
if fp is not None: pieces.append(fp)
|
||||
|
||||
# --- SIMS AUSSEN (+Plane.ZAxis-Seite) — Platte unter der Oeffnung
|
||||
# --- SIMS — nur AUSSEN. aus_sign=+1 -> sims auf +perp, =-1 -> auf
|
||||
# -perp. Drinnen nie. Sim ist gleichzeitig der visuelle Indikator
|
||||
# fuer die Aussenseite (Test: aussenseite togglen → Sim wechselt).
|
||||
sa = _OEFF_SIMS_STYLES.get(sims_aus_style)
|
||||
if sa is not None:
|
||||
s_t = sa["dicke"]; s_pr = sa["aus"]; s_oh = sa["ueberhang"]
|
||||
s_lo = z_lo - s_t
|
||||
if aus_sign > 0:
|
||||
p_lo, p_hi = +half_d, +half_d + s_pr
|
||||
else:
|
||||
p_lo, p_hi = -half_d - s_pr, -half_d
|
||||
sb = _make_oeff_box(pt, tan,
|
||||
-half_b - s_oh, +half_b + s_oh,
|
||||
s_lo, z_lo,
|
||||
+half_d, +half_d + s_pr)
|
||||
if sb is not None: pieces.append(sb)
|
||||
|
||||
# --- SIMS INNEN (-Plane.ZAxis-Seite) — Platte unter der Oeffnung
|
||||
si = _OEFF_SIMS_STYLES.get(sims_in_style)
|
||||
if si is not None:
|
||||
s_t = si["dicke"]; s_pr = si["aus"]; s_oh = si["ueberhang"]
|
||||
s_lo = z_lo - s_t
|
||||
sb = _make_oeff_box(pt, tan,
|
||||
-half_b - s_oh, +half_b + s_oh,
|
||||
s_lo, z_lo,
|
||||
-half_d - s_pr, -half_d)
|
||||
s_lo, z_lo, p_lo, p_hi)
|
||||
if sb is not None: pieces.append(sb)
|
||||
|
||||
return pieces
|
||||
@@ -4206,7 +4489,12 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
||||
oeff_sims_aus=op_meta.get("oeff_sims_aus"),
|
||||
oeff_sims_in=op_meta.get("oeff_sims_in"),
|
||||
oeff_glas=op_meta.get("oeff_glas"),
|
||||
oeff_referenz=op_meta.get("oeff_referenz"))
|
||||
oeff_referenz=op_meta.get("oeff_referenz"),
|
||||
oeff_darstellung=op_meta.get("oeff_darstellung"),
|
||||
oeff_aussenseite=op_meta.get("oeff_aussenseite"),
|
||||
oeff_tuer_rahmen=op_meta.get("oeff_tuer_rahmen"),
|
||||
oeff_rahmen_offset=op_meta.get("oeff_rahmen_offset"),
|
||||
oeff_style_id=op_meta.get("oeff_style_id"))
|
||||
doc.Objects.AddBrep(pbrep, op_attrs)
|
||||
|
||||
# Source-Layer migrieren + Volumen-Layer-Index ermitteln
|
||||
@@ -4684,6 +4972,18 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
elif t == "DELETE_WALL": self._delete_wall(p.get("id"))
|
||||
elif t == "DELETE_ELEMENT": self._delete_wall(p.get("id"))
|
||||
elif t == "REGENERATE_ALL": self._regenerate_all()
|
||||
elif t == "SAVE_OEFF_STYLE":
|
||||
try:
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
save_oeff_style(doc, p.get("name") or "Stil", p.get("settings") or {})
|
||||
except Exception as ex: print("[ELEMENTE] save oeff style:", ex)
|
||||
self._send_state()
|
||||
elif t == "DELETE_OEFF_STYLE":
|
||||
try:
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
delete_oeff_style(doc, p.get("id"))
|
||||
except Exception as ex: print("[ELEMENTE] del oeff style:", ex)
|
||||
self._send_state()
|
||||
elif t == "OPEN_ELEMENTE_UEBERSICHT":
|
||||
try:
|
||||
import elemente_uebersicht
|
||||
@@ -4802,6 +5102,11 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
"simsIn": meta.get("oeff_sims_in", "ohne"),
|
||||
"glas": bool(meta.get("oeff_glas", False)),
|
||||
"oeffReferenz": meta.get("oeff_referenz", "mid"),
|
||||
"darstellung": meta.get("oeff_darstellung", "standard"),
|
||||
"aussenseite": meta.get("oeff_aussenseite", "rechts"),
|
||||
"tuerRahmen": meta.get("oeff_tuer_rahmen", "zarge"),
|
||||
"rahmenOffset": meta.get("oeff_rahmen_offset", 0.05),
|
||||
"styleId": meta.get("oeff_style_id", ""),
|
||||
})
|
||||
elif meta["type"] == "treppe_axis":
|
||||
gs = _geschoss_by_id(doc, meta["geschoss"])
|
||||
@@ -4911,6 +5216,7 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
{"name": n, "color": m["color"],
|
||||
"hatch": m.get("hatch", ""), "scale": m.get("scale", 1.0)}
|
||||
for n, m in _MATERIAL_LIBRARY.items()],
|
||||
"oeffStyles": list_oeff_styles(doc),
|
||||
}
|
||||
self.send("STATE", payload)
|
||||
# An Properties-Satellite-Window forwarden falls offen
|
||||
@@ -5691,6 +5997,37 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
try: preview_base_z = float(axis_curve.PointAtStart.Z)
|
||||
except Exception: preview_base_z = 0.0
|
||||
|
||||
# Entwurfs-Defaults pro Typ — VOR der Loop damit der Stil-Picker
|
||||
# sie ueberschreiben kann.
|
||||
is_fenster = (typ == "fenster")
|
||||
rahmen_b_def = _last("oeff_rahmen_b", 0.06)
|
||||
rahmen_t_def = _last("oeff_rahmen_tiefe", 0.08)
|
||||
rahmen_offset_def = _last("oeff_rahmen_offset", 0.05)
|
||||
fluegel_def = _last("{}_fluegel".format(typ), 2 if is_fenster else 1)
|
||||
simsa_def = "standard" if is_fenster else "ohne"
|
||||
glas_def = is_fenster
|
||||
referenz_def = _last("oeff_referenz", "mid")
|
||||
darst_def = "standard"
|
||||
tuer_rahmen_def = "zarge"
|
||||
# Pending-Style-ID aus sticky (von Stil-Picker gesetzt). Falls noch
|
||||
# kein Style gepickt, nutzen wir den zuletzt-aktiven fuer diesen typ
|
||||
# als Default-Source.
|
||||
active_sid = get_active_oeff_style_id(doc, typ)
|
||||
if active_sid:
|
||||
for s in list_oeff_styles(doc, typ):
|
||||
if s.get("id") == active_sid:
|
||||
if "rahmenB" in s: rahmen_b_def = float(s["rahmenB"])
|
||||
if "rahmenTiefe" in s: rahmen_t_def = float(s["rahmenTiefe"])
|
||||
if "rahmenOffset" in s: rahmen_offset_def = float(s["rahmenOffset"])
|
||||
if "fluegel" in s: fluegel_def = int(s["fluegel"])
|
||||
if "simsAus" in s: simsa_def = s["simsAus"]
|
||||
if "glas" in s: glas_def = bool(s["glas"])
|
||||
if "darstellung" in s: darst_def = s["darstellung"]
|
||||
if typ == "tuer" and "tuerRahmen" in s:
|
||||
tuer_rahmen_def = s["tuerRahmen"]
|
||||
break
|
||||
pending_sid = active_sid or ""
|
||||
|
||||
# 2) Punkt auf der Achse — constrained an die Wand-Achse
|
||||
try:
|
||||
while True:
|
||||
@@ -5701,8 +6038,12 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
prompt += ", Br={:.2f}".format(brueest)
|
||||
prompt += "]"
|
||||
gp.SetCommandPrompt(prompt)
|
||||
try: gp.Constrain(axis_curve, False)
|
||||
except Exception: pass
|
||||
# KEINE Constrain mehr — der User soll perpendicular zur
|
||||
# Achse klicken koennen damit wir die Aussenseite ableiten
|
||||
# koennen. Position wird via ClosestPoint auf die Achse
|
||||
# projiziert. Das DynamicDraw-Preview projiziert intern
|
||||
# auch, der Quader sitzt also weiterhin sauber auf der
|
||||
# Achse — nur die Cursor-Position bleibt frei.
|
||||
# Live-Preview: gruener Oeffnungs-Quader mit Glas-Diagonalen,
|
||||
# Brueest-Marker und Mass-Label oberhalb des Sturzes
|
||||
try:
|
||||
@@ -5717,9 +6058,47 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
opt_b = gp.AddOption("Breite")
|
||||
opt_h = gp.AddOption("Hoehe")
|
||||
opt_br = gp.AddOption("Bruestung") if typ == "fenster" else None
|
||||
# Stil-Picker: zeigt verfuegbare Styles als Sub-Optionen
|
||||
opt_st = gp.AddOption("Stil")
|
||||
rp = gp.Get()
|
||||
if rp == GetResult.Option:
|
||||
idx = gp.OptionIndex()
|
||||
if idx == opt_st:
|
||||
styles = list_oeff_styles(doc, typ)
|
||||
if not styles:
|
||||
print("[ELEMENTE] Keine Styles fuer {}".format(typ))
|
||||
else:
|
||||
try:
|
||||
go = ric.GetOption()
|
||||
go.SetCommandPrompt("Stil waehlen")
|
||||
opt_map = []
|
||||
for s in styles:
|
||||
safe = s["name"].replace(" ", "_")
|
||||
opt_map.append((go.AddOption(safe), s))
|
||||
if go.Get() == GetResult.Option:
|
||||
oi = go.OptionIndex()
|
||||
chosen = next((s for (i, s) in opt_map
|
||||
if i == oi), None)
|
||||
if chosen is not None:
|
||||
if "breite" in chosen: breite = float(chosen["breite"])
|
||||
if "hoehe" in chosen: hoehe = float(chosen["hoehe"])
|
||||
if typ == "fenster" and "brueest" in chosen:
|
||||
brueest = float(chosen["brueest"])
|
||||
if "rahmenB" in chosen: rahmen_b_def = float(chosen["rahmenB"])
|
||||
if "rahmenTiefe" in chosen: rahmen_t_def = float(chosen["rahmenTiefe"])
|
||||
if "rahmenOffset" in chosen: rahmen_offset_def = float(chosen["rahmenOffset"])
|
||||
if "fluegel" in chosen: fluegel_def = int(chosen["fluegel"])
|
||||
if "simsAus" in chosen: simsa_def = chosen["simsAus"]
|
||||
if "glas" in chosen: glas_def = bool(chosen["glas"])
|
||||
if "darstellung" in chosen: darst_def = chosen["darstellung"]
|
||||
if typ == "tuer" and "tuerRahmen" in chosen:
|
||||
tuer_rahmen_def = chosen["tuerRahmen"]
|
||||
pending_sid = chosen["id"]
|
||||
set_active_oeff_style_id(doc, typ, chosen["id"])
|
||||
print("[ELEMENTE] Stil '{}' geladen".format(chosen["name"]))
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] Stil-Picker:", ex)
|
||||
continue
|
||||
if idx == opt_b:
|
||||
gn = ric.GetNumber()
|
||||
gn.SetCommandPrompt("Breite")
|
||||
@@ -5753,6 +6132,22 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] ClosestPoint:", ex); return
|
||||
|
||||
# Aussenseite aus Click-Richtung ableiten: Vektor on_axis→click_pt
|
||||
# mit der Perp-Richtung der Achse vergleichen. Vorzeichen entscheidet
|
||||
# ob aussen=+perp (=rechts) oder aussen=-perp (=links). Bei Klick
|
||||
# direkt auf der Achse: Default 'rechts'.
|
||||
detected_aussen = "rechts"
|
||||
try:
|
||||
tan_at = axis_curve.TangentAt(t)
|
||||
perp = rg.Vector3d.CrossProduct(tan_at, rg.Vector3d(0, 0, 1))
|
||||
dx = click_pt.X - on_axis.X
|
||||
dy = click_pt.Y - on_axis.Y
|
||||
side = perp.X * dx + perp.Y * dy
|
||||
if side < -1e-6: detected_aussen = "links"
|
||||
elif side > 1e-6: detected_aussen = "rechts"
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] aussenseite detect:", ex)
|
||||
|
||||
# Point-Objekt mit Metadaten anlegen
|
||||
prefix = "fenster_" if typ == "fenster" else "tuer_"
|
||||
oeff_id = prefix + uuid.uuid4().hex[:10]
|
||||
@@ -5761,17 +6156,6 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
geschoss_name = g.get("name", "EG") if g else "EG"
|
||||
layer = _ensure_layer(doc, _layer_path_axis(doc, geschoss_name))
|
||||
|
||||
# Entwurfs-Defaults pro Typ
|
||||
is_fenster = (typ == "fenster")
|
||||
rahmen_b_def = _last("oeff_rahmen_b", 0.06)
|
||||
rahmen_t_def = _last("oeff_rahmen_tiefe", 0.08)
|
||||
rahmen_p_def = _last("oeff_rahmen_pos", "mid")
|
||||
fluegel_def = _last("{}_fluegel".format(typ), 2 if is_fenster else 1)
|
||||
simsa_def = "standard" if is_fenster else "ohne"
|
||||
simsi_def = "standard" if is_fenster else "ohne"
|
||||
glas_def = is_fenster
|
||||
referenz_def = _last("oeff_referenz", "mid")
|
||||
|
||||
attrs = Rhino.DocObjects.ObjectAttributes()
|
||||
attrs.LayerIndex = layer
|
||||
_attach_meta(attrs, oeff_id, "oeffnung_point", geschoss,
|
||||
@@ -5781,12 +6165,16 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
oeff_brueest=brueest,
|
||||
oeff_rahmen_b=rahmen_b_def,
|
||||
oeff_rahmen_tiefe=rahmen_t_def,
|
||||
oeff_rahmen_pos=rahmen_p_def,
|
||||
oeff_rahmen_offset=rahmen_offset_def,
|
||||
oeff_fluegel=fluegel_def,
|
||||
oeff_sims_aus=simsa_def,
|
||||
oeff_sims_in=simsi_def,
|
||||
oeff_sims_in="ohne",
|
||||
oeff_glas=glas_def,
|
||||
oeff_referenz=referenz_def)
|
||||
oeff_referenz=referenz_def,
|
||||
oeff_aussenseite=detected_aussen,
|
||||
oeff_darstellung=darst_def,
|
||||
oeff_tuer_rahmen=tuer_rahmen_def,
|
||||
oeff_style_id=pending_sid)
|
||||
# Oeffnungs-Punkt auf UK+Brueestung-Hoehe platzieren (= visuell auf
|
||||
# Unterkante Oeffnung). Constraint vergleicht spaeter pt.Z mit
|
||||
# UK+brueest — wenn der Punkt am axis.Z=0 saesse, wuerde der erste
|
||||
@@ -8210,6 +8598,41 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
glas = bool(p.get("glas", old_meta.get("oeff_glas", otyp == "fenster")))
|
||||
oref = p.get("oeffReferenz", old_meta.get("oeff_referenz", "mid"))
|
||||
if oref not in _OEFF_REFERENZ_OPTIONS: oref = "mid"
|
||||
odarst = p.get("darstellung", old_meta.get("oeff_darstellung", "standard"))
|
||||
if odarst not in _OEFF_DARSTELLUNGEN: odarst = "standard"
|
||||
oauss = p.get("aussenseite", old_meta.get("oeff_aussenseite", "rechts"))
|
||||
if oauss not in _OEFF_AUSSENSEITEN: oauss = "rechts"
|
||||
otrah = p.get("tuerRahmen", old_meta.get("oeff_tuer_rahmen", "zarge"))
|
||||
if otrah not in _OEFF_TUER_RAHMEN: otrah = "zarge"
|
||||
try: oro = float(p.get("rahmenOffset",
|
||||
old_meta.get("oeff_rahmen_offset", 0.05)))
|
||||
except Exception: oro = 0.05
|
||||
if oro < 0: oro = 0.0
|
||||
# Style-Apply: wenn ein styleId im Patch ist, alle Felder
|
||||
# aus dem Style ueberschreiben (User-Patch in derselben
|
||||
# Operation gewinnt aber ueber Style — sonst koennte ein
|
||||
# Stil das eben gemachte Field-Edit wegmaecken).
|
||||
o_style_id = p.get("styleId",
|
||||
old_meta.get("oeff_style_id", "")) or ""
|
||||
if p.get("styleId") and p.get("styleId") != old_meta.get("oeff_style_id"):
|
||||
# Style wurde NEU gewaehlt → seine Werte applizieren
|
||||
styles = list_oeff_styles(doc)
|
||||
stl = next((s for s in styles if s.get("id") == p["styleId"]), None)
|
||||
if stl is not None and stl.get("typ") == otyp:
|
||||
if "breite" in stl: breite = float(stl["breite"])
|
||||
if "hoehe" in stl: hoehe = float(stl["hoehe"])
|
||||
if otyp == "fenster" and "brueest" in stl:
|
||||
brueest = float(stl["brueest"])
|
||||
if "rahmenB" in stl: rahmen_b = float(stl["rahmenB"])
|
||||
if "rahmenTiefe" in stl: rahmen_t = float(stl["rahmenTiefe"])
|
||||
if "rahmenOffset" in stl: oro = float(stl["rahmenOffset"])
|
||||
if "fluegel" in stl: fluegel = int(stl["fluegel"])
|
||||
if "simsAus" in stl: simsa = stl["simsAus"]
|
||||
if "glas" in stl: glas = bool(stl["glas"])
|
||||
if "darstellung" in stl: odarst = stl["darstellung"]
|
||||
if otyp == "tuer" and "tuerRahmen" in stl:
|
||||
otrah = stl["tuerRahmen"]
|
||||
set_active_oeff_style_id(doc, otyp, p["styleId"])
|
||||
attrs = axis_obj.Attributes
|
||||
_attach_meta(attrs, wall_id, "oeffnung_point",
|
||||
old_meta["geschoss"], old_meta["dicke"],
|
||||
@@ -8224,7 +8647,12 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
oeff_fluegel=fluegel,
|
||||
oeff_sims_aus=simsa, oeff_sims_in=simsi,
|
||||
oeff_glas=glas,
|
||||
oeff_referenz=oref)
|
||||
oeff_referenz=oref,
|
||||
oeff_darstellung=odarst,
|
||||
oeff_aussenseite=oauss,
|
||||
oeff_tuer_rahmen=otrah,
|
||||
oeff_rahmen_offset=oro,
|
||||
oeff_style_id=o_style_id)
|
||||
axis_obj.Attributes = attrs
|
||||
axis_obj.CommitChanges()
|
||||
parent_id = old_meta.get("oeff_parent", "")
|
||||
@@ -8345,6 +8773,28 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
self._send_state()
|
||||
|
||||
|
||||
def regenerate_all_oeffnungen(doc):
|
||||
"""Modul-API: regen aller Oeffnungen + ihrer Parent-Waende. Wird vom
|
||||
Ausschnitt-Restore / Oberleiste-Darstellungs-Switch gerufen damit
|
||||
der doc-level Darstellungs-Override sofort wirkt."""
|
||||
if doc is None: return 0
|
||||
seen_walls = set()
|
||||
n = 0
|
||||
for obj in list(doc.Objects):
|
||||
meta = _read_meta(obj)
|
||||
if meta is None: continue
|
||||
if meta.get("type") != "oeffnung_point": continue
|
||||
parent = meta.get("oeff_parent") or ""
|
||||
if parent and parent not in seen_walls:
|
||||
seen_walls.add(parent)
|
||||
_regenerate_element(doc, parent)
|
||||
n += 1
|
||||
try: doc.Views.Redraw()
|
||||
except Exception: pass
|
||||
print("[ELEMENTE] regen all oeffnungen via {} Waende".format(n))
|
||||
return n
|
||||
|
||||
|
||||
# --- Event-Listener ---------------------------------------------------------
|
||||
|
||||
# Re-Entry-Guard: wenn _regenerate_volume die Brep ersetzt, feuert das
|
||||
|
||||
@@ -981,6 +981,18 @@ class OberleisteBridge(panel_base.BaseBridge):
|
||||
except Exception as ex:
|
||||
print("[OBERLEISTE] open masse:", ex)
|
||||
|
||||
# --- Darstellung (SIA-400 LoD globaler Override) -----------------
|
||||
elif t == "SET_DARSTELLUNG":
|
||||
try:
|
||||
import elemente
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
new_v = p.get("darstellung") or ""
|
||||
elemente.set_aktive_darstellung(doc, new_v)
|
||||
elemente.regenerate_all_oeffnungen(doc)
|
||||
except Exception as ex:
|
||||
print("[OBERLEISTE] set darstellung:", ex)
|
||||
self._send_state(force=True)
|
||||
|
||||
# --- Display-Mode -----------------------------------------------
|
||||
elif t == "SET_DISPLAY_MODE":
|
||||
n = p.get("name")
|
||||
@@ -1226,6 +1238,12 @@ class OberleisteBridge(panel_base.BaseBridge):
|
||||
info["textFonts"] = []
|
||||
info["textStyles"] = []
|
||||
info["textStyleActiveId"] = None
|
||||
# Aktive Darstellung (SIA-400 LoD globaler Override)
|
||||
try:
|
||||
import elemente
|
||||
info["aktiveDarstellung"] = elemente.get_aktive_darstellung(doc) or ""
|
||||
except Exception:
|
||||
info["aktiveDarstellung"] = ""
|
||||
# Norden-Rotation fuer N/O/S/W-Buttons
|
||||
try:
|
||||
import kamera
|
||||
|
||||
@@ -98,6 +98,8 @@ def _broadcast_state(doc=None, hatch_patterns=None):
|
||||
"projectZeroMum": zero_mum,
|
||||
"hatchPatterns": hatch_patterns if hatch_patterns is not None
|
||||
else _hatch_pattern_names(doc),
|
||||
"layerCombinations": list_layer_preset_names(doc),
|
||||
"layerCombinationActive": get_active_comb_name(doc),
|
||||
}
|
||||
except Exception as ex:
|
||||
print("[EBENEN] broadcast prepare:", ex)
|
||||
@@ -470,6 +472,31 @@ class EbenenBridge(panel_base.BaseBridge):
|
||||
p.get("hatchPatterns") or [])
|
||||
elif t == "OPEN_GESCHOSS_DIALOG":
|
||||
self._open_geschoss_dialog(p.get("zeichnungsebenen") or [])
|
||||
elif t == "PICK_LAYER_COMBINATION":
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
name = (p.get("name") or "").strip()
|
||||
if name:
|
||||
apply_layer_preset_by_name(doc, name)
|
||||
else:
|
||||
set_active_comb_name(doc, None)
|
||||
_broadcast_state(doc)
|
||||
_notify_oberleiste_combs()
|
||||
elif t == "SAVE_LAYER_COMBINATION":
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
name = (p.get("name") or "").strip()
|
||||
if name:
|
||||
save_current_as_layer_preset(doc, name)
|
||||
_broadcast_state(doc)
|
||||
_notify_oberleiste_combs()
|
||||
elif t == "DELETE_LAYER_COMBINATION":
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
delete_layer_preset(doc, p.get("name") or "")
|
||||
_broadcast_state(doc)
|
||||
_notify_oberleiste_combs()
|
||||
elif t == "OPEN_LAYER_COMBINATIONS_DIALOG":
|
||||
try: open_layer_combinations_window()
|
||||
except Exception as ex:
|
||||
print("[EBENEN] open layer-combinations:", ex)
|
||||
|
||||
# ---- Helpers ----
|
||||
|
||||
|
||||
Reference in New Issue
Block a user