Tueren-Schwung: Live-Update + Hinge-Position auf Wand-Innenkante
- Schwung-Curves-Block VOR dem Volume-Replace-Pfad — sonst greifen openAngle/hingeSide/swingInvert-Aenderungen nicht (Volume-Anzahl bleibt gleich → continue → Swing-Skip). - Hinge-Punkt sitzt nicht mehr auf der Wand-Achse sondern auf der Wand-Innenkante (half_d * inside-Richtung). Tuerblatt + Bogen beginnen damit an der Wandflucht statt in der Wandmitte. - Rotation-Vorzeichen korrigiert (rad = open_angle * sweep_sign * aus_sign) damit Tuere wirklich ins Innere schwingt. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+216
-5
@@ -67,6 +67,13 @@ _KEY_AKTIVE_DARSTELLUNG = "dossier_aktive_darstellung" # doc-level global overr
|
|||||||
_KEY_OEFF_STYLE_ID = "dossier_oeff_style_id" # per-Object: referenziert einen Style aus dossier_oeff_styles
|
_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_STYLES = "dossier_oeff_styles" # JSON-Liste aller Fenster/Tueren-Styles
|
||||||
_KEY_OEFF_STYLE_ACTIVE = "dossier_oeff_style_active" # zuletzt benutzte Style-ID (pro typ)
|
_KEY_OEFF_STYLE_ACTIVE = "dossier_oeff_style_active" # zuletzt benutzte Style-ID (pro typ)
|
||||||
|
_KEY_OEFF_TUER_TYP = "dossier_oeff_tuer_typ" # "normal" | "wandoeffnung"
|
||||||
|
_KEY_OEFF_HINGE_SIDE = "dossier_oeff_hinge_side" # "links" | "rechts" — Bandseite (welche Tuerflueg-Seite)
|
||||||
|
_KEY_OEFF_OPEN_ANGLE = "dossier_oeff_open_angle" # float Grad 0–180 (Plan-Oeffnungswinkel)
|
||||||
|
_KEY_OEFF_SWING_INVERT = "dossier_oeff_swing_invert" # "1"/"0" — flippt die Schwung-Richtung
|
||||||
|
|
||||||
|
_OEFF_TUER_TYPEN = ("normal", "wandoeffnung")
|
||||||
|
_OEFF_HINGE_SIDES = ("links", "rechts")
|
||||||
|
|
||||||
_OEFF_DARSTELLUNGEN = ("einfach", "standard", "detail")
|
_OEFF_DARSTELLUNGEN = ("einfach", "standard", "detail")
|
||||||
|
|
||||||
@@ -101,6 +108,7 @@ _OEFF_STYLE_FIELDS = (
|
|||||||
"rahmenB", "rahmenTiefe", "rahmenOffset",
|
"rahmenB", "rahmenTiefe", "rahmenOffset",
|
||||||
"fluegel", "simsAus", "glas",
|
"fluegel", "simsAus", "glas",
|
||||||
"darstellung", "tuerRahmen",
|
"darstellung", "tuerRahmen",
|
||||||
|
"tuerTyp", "hingeSide", "openAngle",
|
||||||
)
|
)
|
||||||
|
|
||||||
_OEFF_DEFAULT_STYLES = [
|
_OEFF_DEFAULT_STYLES = [
|
||||||
@@ -133,7 +141,14 @@ _OEFF_DEFAULT_STYLES = [
|
|||||||
"breite": 0.90, "hoehe": 2.10, "brueest": 0.00,
|
"breite": 0.90, "hoehe": 2.10, "brueest": 0.00,
|
||||||
"rahmenB": 0.06, "rahmenTiefe": 0.08, "rahmenOffset": 0.05,
|
"rahmenB": 0.06, "rahmenTiefe": 0.08, "rahmenOffset": 0.05,
|
||||||
"fluegel": 1, "simsAus": "ohne", "glas": True,
|
"fluegel": 1, "simsAus": "ohne", "glas": True,
|
||||||
"darstellung": "standard", "tuerRahmen": "zarge"},
|
"darstellung": "standard", "tuerRahmen": "zarge",
|
||||||
|
"tuerTyp": "normal", "hingeSide": "links", "openAngle": 90.0},
|
||||||
|
{"name": "Wandöffnung", "typ": "tuer",
|
||||||
|
"breite": 1.20, "hoehe": 2.10, "brueest": 0.00,
|
||||||
|
"rahmenB": 0.04, "rahmenTiefe": 0.04, "rahmenOffset": 0.05,
|
||||||
|
"fluegel": 1, "simsAus": "ohne", "glas": False,
|
||||||
|
"darstellung": "standard", "tuerRahmen": "zarge",
|
||||||
|
"tuerTyp": "wandoeffnung", "hingeSide": "links", "openAngle": 0.0},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -599,6 +614,15 @@ def _layer_path_volume(doc, geschoss_name):
|
|||||||
return _layer_path_axis(doc, geschoss_name)
|
return _layer_path_axis(doc, geschoss_name)
|
||||||
|
|
||||||
|
|
||||||
|
def _layer_path_oeff_swing(doc, geschoss_name):
|
||||||
|
"""Türschwung-Linien — eigener Sublayer (Code 23) damit User die
|
||||||
|
Schwung-Bögen unabhängig von den Türen-Volumes ausblenden kann."""
|
||||||
|
sub = _find_ebene_sublayer_name(doc, ["schwung", "tuerschwung"],
|
||||||
|
"23", "Türschwung",
|
||||||
|
default_color="#888888", default_lw=0.13)
|
||||||
|
return "{}::{}".format(geschoss_name, sub)
|
||||||
|
|
||||||
|
|
||||||
def _layer_path_decke(doc, geschoss_name):
|
def _layer_path_decke(doc, geschoss_name):
|
||||||
"""Decken-Outline + Volumen — Sublayer 'DECKEN' (Code 30)."""
|
"""Decken-Outline + Volumen — Sublayer 'DECKEN' (Code 30)."""
|
||||||
sub = _find_ebene_sublayer_name(doc, ["decke"], "30", "DECKEN",
|
sub = _find_ebene_sublayer_name(doc, ["decke"], "30", "DECKEN",
|
||||||
@@ -1884,6 +1908,8 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over,
|
|||||||
oeff_referenz=None, oeff_darstellung=None,
|
oeff_referenz=None, oeff_darstellung=None,
|
||||||
oeff_aussenseite=None, oeff_tuer_rahmen=None,
|
oeff_aussenseite=None, oeff_tuer_rahmen=None,
|
||||||
oeff_rahmen_offset=None, oeff_style_id=None,
|
oeff_rahmen_offset=None, oeff_style_id=None,
|
||||||
|
oeff_tuer_typ=None, oeff_hinge_side=None, oeff_open_angle=None,
|
||||||
|
oeff_swing_invert=None,
|
||||||
geschoss_end=None, treppe_breite=None,
|
geschoss_end=None, treppe_breite=None,
|
||||||
treppe_n=None, treppe_referenz=None,
|
treppe_n=None, treppe_referenz=None,
|
||||||
treppe_modus=None, treppe_lauf_d=None, treppe_art=None,
|
treppe_modus=None, treppe_lauf_d=None, treppe_art=None,
|
||||||
@@ -1967,6 +1993,19 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over,
|
|||||||
if oeff_style_id is not None:
|
if oeff_style_id is not None:
|
||||||
try: obj_attrs.SetUserString(_KEY_OEFF_STYLE_ID, str(oeff_style_id) or "")
|
try: obj_attrs.SetUserString(_KEY_OEFF_STYLE_ID, str(oeff_style_id) or "")
|
||||||
except Exception: pass
|
except Exception: pass
|
||||||
|
if oeff_tuer_typ is not None and oeff_tuer_typ in _OEFF_TUER_TYPEN:
|
||||||
|
obj_attrs.SetUserString(_KEY_OEFF_TUER_TYP, oeff_tuer_typ)
|
||||||
|
if oeff_hinge_side is not None and oeff_hinge_side in _OEFF_HINGE_SIDES:
|
||||||
|
obj_attrs.SetUserString(_KEY_OEFF_HINGE_SIDE, oeff_hinge_side)
|
||||||
|
if oeff_open_angle is not None:
|
||||||
|
try:
|
||||||
|
v = max(0.0, min(180.0, float(oeff_open_angle)))
|
||||||
|
obj_attrs.SetUserString(_KEY_OEFF_OPEN_ANGLE, "{:.2f}".format(v))
|
||||||
|
except Exception: pass
|
||||||
|
if oeff_swing_invert is not None:
|
||||||
|
try: obj_attrs.SetUserString(_KEY_OEFF_SWING_INVERT,
|
||||||
|
"1" if bool(oeff_swing_invert) else "0")
|
||||||
|
except Exception: pass
|
||||||
# --- Treppen-Felder ---
|
# --- Treppen-Felder ---
|
||||||
if geschoss_end is not None:
|
if geschoss_end is not None:
|
||||||
obj_attrs.SetUserString(_KEY_GESCHOSS_END, geschoss_end or "")
|
obj_attrs.SetUserString(_KEY_GESCHOSS_END, geschoss_end or "")
|
||||||
@@ -2130,6 +2169,15 @@ def _read_meta(obj):
|
|||||||
except Exception: oro = 0.05
|
except Exception: oro = 0.05
|
||||||
if oro < 0: oro = 0.0
|
if oro < 0: oro = 0.0
|
||||||
ostyle = a.GetUserString(_KEY_OEFF_STYLE_ID) or ""
|
ostyle = a.GetUserString(_KEY_OEFF_STYLE_ID) or ""
|
||||||
|
ottyp = a.GetUserString(_KEY_OEFF_TUER_TYP) or "normal"
|
||||||
|
if ottyp not in _OEFF_TUER_TYPEN: ottyp = "normal"
|
||||||
|
ohinge = a.GetUserString(_KEY_OEFF_HINGE_SIDE) or "links"
|
||||||
|
if ohinge not in _OEFF_HINGE_SIDES: ohinge = "links"
|
||||||
|
try: oangle = float(a.GetUserString(_KEY_OEFF_OPEN_ANGLE) or "90")
|
||||||
|
except Exception: oangle = 90.0
|
||||||
|
if oangle < 0: oangle = 0.0
|
||||||
|
elif oangle > 180: oangle = 180.0
|
||||||
|
oswinv = (a.GetUserString(_KEY_OEFF_SWING_INVERT) == "1")
|
||||||
# Treppen-Felder
|
# Treppen-Felder
|
||||||
gend = a.GetUserString(_KEY_GESCHOSS_END) or ""
|
gend = a.GetUserString(_KEY_GESCHOSS_END) or ""
|
||||||
try: tb = float(a.GetUserString(_KEY_TREPPE_BREITE) or "1.0")
|
try: tb = float(a.GetUserString(_KEY_TREPPE_BREITE) or "1.0")
|
||||||
@@ -2251,6 +2299,10 @@ def _read_meta(obj):
|
|||||||
"oeff_tuer_rahmen": otrah,
|
"oeff_tuer_rahmen": otrah,
|
||||||
"oeff_rahmen_offset": oro,
|
"oeff_rahmen_offset": oro,
|
||||||
"oeff_style_id": ostyle,
|
"oeff_style_id": ostyle,
|
||||||
|
"oeff_tuer_typ": ottyp,
|
||||||
|
"oeff_hinge_side": ohinge,
|
||||||
|
"oeff_open_angle": oangle,
|
||||||
|
"oeff_swing_invert": oswinv,
|
||||||
"geschoss_end": gend,
|
"geschoss_end": gend,
|
||||||
"treppe_breite": tb,
|
"treppe_breite": tb,
|
||||||
"treppe_n": tn,
|
"treppe_n": tn,
|
||||||
@@ -2471,6 +2523,109 @@ def _resolve_rahmen_perp_range(half_d, rahmen_tiefe, rahmen_pos):
|
|||||||
return lo, hi
|
return lo, hi
|
||||||
|
|
||||||
|
|
||||||
|
def _make_tuer_swing_curves(axis_curve, point_on_axis, wall_dicke,
|
||||||
|
oeff_meta, base_z):
|
||||||
|
"""Generiert die 2D-Plan-Schwung-Linien einer Tuer:
|
||||||
|
- 1 Linie: Tuerblatt im geoeffneten Zustand (vom Scharnier um
|
||||||
|
open_angle gedreht)
|
||||||
|
- 1 Arc: Schwung-Bogen vom geschlossenen Endpunkt zum offenen
|
||||||
|
Endpunkt
|
||||||
|
Alle Kurven liegen auf Floor-Level (z = base_z).
|
||||||
|
|
||||||
|
Returns [Curve] oder leere Liste wenn Tuer-Typ='wandoeffnung'
|
||||||
|
oder Oeffnung keine Tuer ist."""
|
||||||
|
import math
|
||||||
|
if oeff_meta.get("oeff_typ") != "tuer": return []
|
||||||
|
if oeff_meta.get("oeff_tuer_typ", "normal") != "normal": return []
|
||||||
|
frame = _oeff_axis_frame(axis_curve, point_on_axis)
|
||||||
|
if frame is None: return []
|
||||||
|
pt, tan, perp = frame
|
||||||
|
breite = float(oeff_meta.get("oeff_breite", 0.9))
|
||||||
|
rahmen_b = float(oeff_meta.get("oeff_rahmen_b", 0.06))
|
||||||
|
darstellung = oeff_meta.get("oeff_darstellung", "standard")
|
||||||
|
aussenseite = oeff_meta.get("oeff_aussenseite", "rechts")
|
||||||
|
aus_sign = +1 if aussenseite == "rechts" else -1
|
||||||
|
hinge_side = oeff_meta.get("oeff_hinge_side", "links")
|
||||||
|
try: open_angle = float(oeff_meta.get("oeff_open_angle", 90))
|
||||||
|
except Exception: open_angle = 90.0
|
||||||
|
if open_angle <= 0: return [] # tuer geschlossen — keine Schwung-Curves
|
||||||
|
swing_invert = bool(oeff_meta.get("oeff_swing_invert", False))
|
||||||
|
|
||||||
|
half_b = breite * 0.5
|
||||||
|
# Lichte-Breite je nach Darstellung: einfach = volle Breite,
|
||||||
|
# standard/detail = Innenmass abzueglich Rahmen
|
||||||
|
if darstellung == "einfach":
|
||||||
|
leaf_t_lo, leaf_t_hi = -half_b, +half_b
|
||||||
|
else:
|
||||||
|
leaf_t_lo, leaf_t_hi = -half_b + rahmen_b, +half_b - rahmen_b
|
||||||
|
leaf_len = leaf_t_hi - leaf_t_lo
|
||||||
|
if leaf_len <= 1e-6: return []
|
||||||
|
|
||||||
|
# Scharnier-Tangentenkoordinate (along axis)
|
||||||
|
if hinge_side == "links":
|
||||||
|
hinge_t = leaf_t_lo
|
||||||
|
# Schwung-Richtung: vom Scharnier nach aussen → Aussenseite-Vorzeichen
|
||||||
|
# Linke Bandseite: Tuer schwingt entlang +tan-Richtung im geschlossenen
|
||||||
|
# Zustand, perpendikulaer ins Innere (= -aus_sign perp) im offenen.
|
||||||
|
sweep_sign = -1 # gegen Uhrzeigersinn um Scharnier wenn aus +Z gesehen
|
||||||
|
else:
|
||||||
|
hinge_t = leaf_t_hi
|
||||||
|
sweep_sign = +1
|
||||||
|
|
||||||
|
# Hinge-Punkt: sitzt auf der WAND-INNENKANTE (nicht auf der Achse).
|
||||||
|
# Innen-Richtung: aussenseite='rechts' (aus_sign=+1) bedeutet aussen
|
||||||
|
# liegt in box-perp-Richtung (tan.Y, -tan.X). Innen ist dann das
|
||||||
|
# Gegenstueck (-tan.Y, +tan.X). aus_sign=-1 flippt das.
|
||||||
|
half_d = float(wall_dicke) * 0.5
|
||||||
|
inside_x = aus_sign * (-tan.Y)
|
||||||
|
inside_y = aus_sign * tan.X
|
||||||
|
z0 = float(base_z)
|
||||||
|
hinge_pt = rg.Point3d(
|
||||||
|
pt.X + hinge_t * tan.X + half_d * inside_x,
|
||||||
|
pt.Y + hinge_t * tan.Y + half_d * inside_y,
|
||||||
|
z0)
|
||||||
|
# Geschlossener Endpunkt (Tuer-Blatt in Wand-Flucht, parallel zur Achse):
|
||||||
|
closed_pt = rg.Point3d(
|
||||||
|
pt.X + (hinge_t + sweep_sign * leaf_len) * tan.X + half_d * inside_x,
|
||||||
|
pt.Y + (hinge_t + sweep_sign * leaf_len) * tan.Y + half_d * inside_y,
|
||||||
|
z0)
|
||||||
|
# Offener Endpunkt: hinge + leaf_len * direction(sweep_angle)
|
||||||
|
# Direction startet bei +sweep_sign * tan (geschlossen) und rotiert
|
||||||
|
# um Z-Achse um sweep_sign * (-aus_sign) * open_angle Grad — Tuer
|
||||||
|
# schwingt in den Innenraum (-aus_sign * perp).
|
||||||
|
rad = math.radians(open_angle) * sweep_sign * aus_sign
|
||||||
|
if swing_invert: rad = -rad
|
||||||
|
cos_r, sin_r = math.cos(rad), math.sin(rad)
|
||||||
|
base_dx = sweep_sign * tan.X
|
||||||
|
base_dy = sweep_sign * tan.Y
|
||||||
|
open_dx = base_dx * cos_r - base_dy * sin_r
|
||||||
|
open_dy = base_dx * sin_r + base_dy * cos_r
|
||||||
|
open_pt = rg.Point3d(hinge_pt.X + open_dx * leaf_len,
|
||||||
|
hinge_pt.Y + open_dy * leaf_len, z0)
|
||||||
|
|
||||||
|
curves = []
|
||||||
|
# 1) Tuerblatt-Linie (Hinge → Open)
|
||||||
|
try:
|
||||||
|
leaf_line = rg.LineCurve(hinge_pt, open_pt)
|
||||||
|
curves.append(leaf_line)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[ELEMENTE] swing leaf:", ex)
|
||||||
|
# 2) Schwung-Arc — Mittelpunkt = Scharnier, Radius = Tuerblatt-Laenge.
|
||||||
|
# Plane mit X→closed_pt + Y→open_pt; Arc(plane, radius, angle) startet
|
||||||
|
# auf der X-Achse und sweept CCW um angle Radian. Damit liegt der
|
||||||
|
# Bogen genau auf einem Kreis um das Scharnier.
|
||||||
|
try:
|
||||||
|
dir_closed = closed_pt - hinge_pt
|
||||||
|
dir_open = open_pt - hinge_pt
|
||||||
|
arc_plane = rg.Plane(hinge_pt, dir_closed, dir_open)
|
||||||
|
arc = rg.Arc(arc_plane, leaf_len, math.radians(open_angle))
|
||||||
|
if arc.IsValid:
|
||||||
|
curves.append(rg.ArcCurve(arc))
|
||||||
|
except Exception as ex:
|
||||||
|
print("[ELEMENTE] swing arc:", ex)
|
||||||
|
return curves
|
||||||
|
|
||||||
|
|
||||||
def _make_oeffnung_pieces(axis_curve, point_on_axis, wall_dicke, oeff_meta, base_z):
|
def _make_oeffnung_pieces(axis_curve, point_on_axis, wall_dicke, oeff_meta, base_z):
|
||||||
"""Baut die einzelnen Brep-Pieces der Oeffnung — Rahmen (single Brep
|
"""Baut die einzelnen Brep-Pieces der Oeffnung — Rahmen (single Brep
|
||||||
via Boolean-Differenz), Mittelpfosten (pro Fluegel), Glas, Sims aussen,
|
via Boolean-Differenz), Mittelpfosten (pro Fluegel), Glas, Sims aussen,
|
||||||
@@ -2680,7 +2835,7 @@ SOURCE_TYPES = ("wand_axis", "decke_outline", "dach_outline",
|
|||||||
"stuetze_point", "traeger_axis",
|
"stuetze_point", "traeger_axis",
|
||||||
"raum_outline", "decke_aussparung_outline")
|
"raum_outline", "decke_aussparung_outline")
|
||||||
VOLUME_TYPES = ("wand_volume", "decke_volume", "dach_volume",
|
VOLUME_TYPES = ("wand_volume", "decke_volume", "dach_volume",
|
||||||
"oeffnung_volume", "treppe_volume",
|
"oeffnung_volume", "oeffnung_swing", "treppe_volume",
|
||||||
"stuetze_volume", "traeger_volume",
|
"stuetze_volume", "traeger_volume",
|
||||||
"raum_stamp", "raum_fill")
|
"raum_stamp", "raum_fill")
|
||||||
# Oeffnungs-Cutout: Boolean-Difference aus Wand. Zusaetzlich kriegt die
|
# Oeffnungs-Cutout: Boolean-Difference aus Wand. Zusaetzlich kriegt die
|
||||||
@@ -4458,6 +4613,30 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
|||||||
# Aenderung (z.B. Fluegel-Wechsel) Fallback auf Delete+Add.
|
# Aenderung (z.B. Fluegel-Wechsel) Fallback auf Delete+Add.
|
||||||
op_layer = _ensure_layer(doc, _layer_path_volume(doc, geschoss_name))
|
op_layer = _ensure_layer(doc, _layer_path_volume(doc, geschoss_name))
|
||||||
for op_meta, pt_loc, op_uk in opening_jobs:
|
for op_meta, pt_loc, op_uk in opening_jobs:
|
||||||
|
# Schwung-Curves IMMER zuerst regenerieren — unabhaengig davon
|
||||||
|
# ob die Volume-Pieces sich geaendert haben. Andernfalls greifen
|
||||||
|
# Aenderungen an openAngle/hingeSide/swingInvert nicht weil der
|
||||||
|
# Replace-Pfad weiter unten via `continue` rausspringt.
|
||||||
|
if op_meta.get("oeff_typ") == "tuer":
|
||||||
|
old_sw = list(_find_objects_by_wall_id(doc, op_meta["id"],
|
||||||
|
"oeffnung_swing"))
|
||||||
|
for o, _m in old_sw:
|
||||||
|
try: doc.Objects.Delete(o.Id, True)
|
||||||
|
except Exception: pass
|
||||||
|
swings = _make_tuer_swing_curves(geom, pt_loc, meta["dicke"],
|
||||||
|
op_meta, op_uk)
|
||||||
|
if swings:
|
||||||
|
sw_layer_idx = _ensure_layer(doc,
|
||||||
|
_layer_path_oeff_swing(doc, geschoss_name))
|
||||||
|
for crv in swings:
|
||||||
|
sw_attrs = Rhino.DocObjects.ObjectAttributes()
|
||||||
|
sw_attrs.LayerIndex = sw_layer_idx
|
||||||
|
_attach_meta(sw_attrs, op_meta["id"], "oeffnung_swing",
|
||||||
|
op_meta["geschoss"], meta["dicke"], "", "",
|
||||||
|
oeff_typ="tuer",
|
||||||
|
oeff_parent=op_meta.get("oeff_parent"))
|
||||||
|
doc.Objects.AddCurve(crv, sw_attrs)
|
||||||
|
|
||||||
old_objs = list(_find_objects_by_wall_id(doc, op_meta["id"],
|
old_objs = list(_find_objects_by_wall_id(doc, op_meta["id"],
|
||||||
"oeffnung_volume"))
|
"oeffnung_volume"))
|
||||||
pieces = _make_oeffnung_pieces(geom, pt_loc, meta["dicke"],
|
pieces = _make_oeffnung_pieces(geom, pt_loc, meta["dicke"],
|
||||||
@@ -4494,7 +4673,11 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
|||||||
oeff_aussenseite=op_meta.get("oeff_aussenseite"),
|
oeff_aussenseite=op_meta.get("oeff_aussenseite"),
|
||||||
oeff_tuer_rahmen=op_meta.get("oeff_tuer_rahmen"),
|
oeff_tuer_rahmen=op_meta.get("oeff_tuer_rahmen"),
|
||||||
oeff_rahmen_offset=op_meta.get("oeff_rahmen_offset"),
|
oeff_rahmen_offset=op_meta.get("oeff_rahmen_offset"),
|
||||||
oeff_style_id=op_meta.get("oeff_style_id"))
|
oeff_style_id=op_meta.get("oeff_style_id"),
|
||||||
|
oeff_tuer_typ=op_meta.get("oeff_tuer_typ"),
|
||||||
|
oeff_hinge_side=op_meta.get("oeff_hinge_side"),
|
||||||
|
oeff_open_angle=op_meta.get("oeff_open_angle"),
|
||||||
|
oeff_swing_invert=op_meta.get("oeff_swing_invert"))
|
||||||
doc.Objects.AddBrep(pbrep, op_attrs)
|
doc.Objects.AddBrep(pbrep, op_attrs)
|
||||||
|
|
||||||
# Source-Layer migrieren + Volumen-Layer-Index ermitteln
|
# Source-Layer migrieren + Volumen-Layer-Index ermitteln
|
||||||
@@ -5107,6 +5290,10 @@ class ElementeBridge(panel_base.BaseBridge):
|
|||||||
"tuerRahmen": meta.get("oeff_tuer_rahmen", "zarge"),
|
"tuerRahmen": meta.get("oeff_tuer_rahmen", "zarge"),
|
||||||
"rahmenOffset": meta.get("oeff_rahmen_offset", 0.05),
|
"rahmenOffset": meta.get("oeff_rahmen_offset", 0.05),
|
||||||
"styleId": meta.get("oeff_style_id", ""),
|
"styleId": meta.get("oeff_style_id", ""),
|
||||||
|
"tuerTyp": meta.get("oeff_tuer_typ", "normal"),
|
||||||
|
"hingeSide": meta.get("oeff_hinge_side", "links"),
|
||||||
|
"openAngle": meta.get("oeff_open_angle", 90.0),
|
||||||
|
"swingInvert": bool(meta.get("oeff_swing_invert", False)),
|
||||||
})
|
})
|
||||||
elif meta["type"] == "treppe_axis":
|
elif meta["type"] == "treppe_axis":
|
||||||
gs = _geschoss_by_id(doc, meta["geschoss"])
|
gs = _geschoss_by_id(doc, meta["geschoss"])
|
||||||
@@ -6174,7 +6361,10 @@ class ElementeBridge(panel_base.BaseBridge):
|
|||||||
oeff_aussenseite=detected_aussen,
|
oeff_aussenseite=detected_aussen,
|
||||||
oeff_darstellung=darst_def,
|
oeff_darstellung=darst_def,
|
||||||
oeff_tuer_rahmen=tuer_rahmen_def,
|
oeff_tuer_rahmen=tuer_rahmen_def,
|
||||||
oeff_style_id=pending_sid)
|
oeff_style_id=pending_sid,
|
||||||
|
oeff_tuer_typ="normal",
|
||||||
|
oeff_hinge_side="links",
|
||||||
|
oeff_open_angle=90.0)
|
||||||
# Oeffnungs-Punkt auf UK+Brueestung-Hoehe platzieren (= visuell auf
|
# Oeffnungs-Punkt auf UK+Brueestung-Hoehe platzieren (= visuell auf
|
||||||
# Unterkante Oeffnung). Constraint vergleicht spaeter pt.Z mit
|
# Unterkante Oeffnung). Constraint vergleicht spaeter pt.Z mit
|
||||||
# UK+brueest — wenn der Punkt am axis.Z=0 saesse, wuerde der erste
|
# UK+brueest — wenn der Punkt am axis.Z=0 saesse, wuerde der erste
|
||||||
@@ -8614,6 +8804,17 @@ class ElementeBridge(panel_base.BaseBridge):
|
|||||||
# Stil das eben gemachte Field-Edit wegmaecken).
|
# Stil das eben gemachte Field-Edit wegmaecken).
|
||||||
o_style_id = p.get("styleId",
|
o_style_id = p.get("styleId",
|
||||||
old_meta.get("oeff_style_id", "")) or ""
|
old_meta.get("oeff_style_id", "")) or ""
|
||||||
|
# Tueren-Schwung-Felder
|
||||||
|
o_tuer_typ = p.get("tuerTyp", old_meta.get("oeff_tuer_typ", "normal"))
|
||||||
|
if o_tuer_typ not in _OEFF_TUER_TYPEN: o_tuer_typ = "normal"
|
||||||
|
o_hinge = p.get("hingeSide", old_meta.get("oeff_hinge_side", "links"))
|
||||||
|
if o_hinge not in _OEFF_HINGE_SIDES: o_hinge = "links"
|
||||||
|
try: o_angle = float(p.get("openAngle",
|
||||||
|
old_meta.get("oeff_open_angle", 90)))
|
||||||
|
except Exception: o_angle = 90.0
|
||||||
|
o_angle = max(0.0, min(180.0, o_angle))
|
||||||
|
o_swinv = bool(p.get("swingInvert",
|
||||||
|
old_meta.get("oeff_swing_invert", False)))
|
||||||
if p.get("styleId") and p.get("styleId") != old_meta.get("oeff_style_id"):
|
if p.get("styleId") and p.get("styleId") != old_meta.get("oeff_style_id"):
|
||||||
# Style wurde NEU gewaehlt → seine Werte applizieren
|
# Style wurde NEU gewaehlt → seine Werte applizieren
|
||||||
styles = list_oeff_styles(doc)
|
styles = list_oeff_styles(doc)
|
||||||
@@ -8632,6 +8833,12 @@ class ElementeBridge(panel_base.BaseBridge):
|
|||||||
if "darstellung" in stl: odarst = stl["darstellung"]
|
if "darstellung" in stl: odarst = stl["darstellung"]
|
||||||
if otyp == "tuer" and "tuerRahmen" in stl:
|
if otyp == "tuer" and "tuerRahmen" in stl:
|
||||||
otrah = stl["tuerRahmen"]
|
otrah = stl["tuerRahmen"]
|
||||||
|
if otyp == "tuer" and "tuerTyp" in stl:
|
||||||
|
o_tuer_typ = stl["tuerTyp"]
|
||||||
|
if otyp == "tuer" and "hingeSide" in stl:
|
||||||
|
o_hinge = stl["hingeSide"]
|
||||||
|
if otyp == "tuer" and "openAngle" in stl:
|
||||||
|
o_angle = float(stl["openAngle"])
|
||||||
set_active_oeff_style_id(doc, otyp, p["styleId"])
|
set_active_oeff_style_id(doc, otyp, p["styleId"])
|
||||||
attrs = axis_obj.Attributes
|
attrs = axis_obj.Attributes
|
||||||
_attach_meta(attrs, wall_id, "oeffnung_point",
|
_attach_meta(attrs, wall_id, "oeffnung_point",
|
||||||
@@ -8652,7 +8859,11 @@ class ElementeBridge(panel_base.BaseBridge):
|
|||||||
oeff_aussenseite=oauss,
|
oeff_aussenseite=oauss,
|
||||||
oeff_tuer_rahmen=otrah,
|
oeff_tuer_rahmen=otrah,
|
||||||
oeff_rahmen_offset=oro,
|
oeff_rahmen_offset=oro,
|
||||||
oeff_style_id=o_style_id)
|
oeff_style_id=o_style_id,
|
||||||
|
oeff_tuer_typ=o_tuer_typ,
|
||||||
|
oeff_hinge_side=o_hinge,
|
||||||
|
oeff_open_angle=o_angle,
|
||||||
|
oeff_swing_invert=o_swinv)
|
||||||
axis_obj.Attributes = attrs
|
axis_obj.Attributes = attrs
|
||||||
axis_obj.CommitChanges()
|
axis_obj.CommitChanges()
|
||||||
parent_id = old_meta.get("oeff_parent", "")
|
parent_id = old_meta.get("oeff_parent", "")
|
||||||
|
|||||||
@@ -1746,6 +1746,8 @@ function OeffnungProperties({ oeff, onUpdate, onDelete, oeffStyles = [] }) {
|
|||||||
fluegel: oeff.fluegel, simsAus: oeff.simsAus,
|
fluegel: oeff.fluegel, simsAus: oeff.simsAus,
|
||||||
glas: oeff.glas, darstellung: oeff.darstellung,
|
glas: oeff.glas, darstellung: oeff.darstellung,
|
||||||
tuerRahmen: oeff.tuerRahmen,
|
tuerRahmen: oeff.tuerRahmen,
|
||||||
|
tuerTyp: oeff.tuerTyp, hingeSide: oeff.hingeSide,
|
||||||
|
openAngle: oeff.openAngle,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1799,6 +1801,63 @@ function OeffnungProperties({ oeff, onUpdate, onDelete, oeffStyles = [] }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!isFenster && (
|
{!isFenster && (
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
|
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 50 }}
|
||||||
|
title="Tueren-Typ. Wandoeffnung = nur Cutout ohne Schwung-Blatt">
|
||||||
|
Typ
|
||||||
|
</span>
|
||||||
|
<div style={{ flex: 1, display: 'flex', gap: 3 }}>
|
||||||
|
<BarToggle label="Normal"
|
||||||
|
active={(oeff.tuerTyp || 'normal') === 'normal'}
|
||||||
|
onClick={() => onUpdate({ tuerTyp: 'normal' })}
|
||||||
|
title="Tuere mit Tuerblatt + Schwung-Bogen" />
|
||||||
|
<BarToggle label="Wandöffnung"
|
||||||
|
active={(oeff.tuerTyp || 'normal') === 'wandoeffnung'}
|
||||||
|
onClick={() => onUpdate({ tuerTyp: 'wandoeffnung' })}
|
||||||
|
title="Nur Wand-Cutout ohne Schwung" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isFenster && (oeff.tuerTyp || 'normal') === 'normal' && (
|
||||||
|
<>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
|
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 50 }}
|
||||||
|
title="Bandseite — welche Tuerflueg-Seite. Im Plan = Scharnier-Position">
|
||||||
|
Band
|
||||||
|
</span>
|
||||||
|
<div style={{ flex: 1, display: 'flex', gap: 3 }}>
|
||||||
|
<BarToggle label="Links"
|
||||||
|
active={(oeff.hingeSide || 'links') === 'links'}
|
||||||
|
onClick={() => onUpdate({ hingeSide: 'links' })} />
|
||||||
|
<BarToggle label="Rechts"
|
||||||
|
active={(oeff.hingeSide || 'links') === 'rechts'}
|
||||||
|
onClick={() => onUpdate({ hingeSide: 'rechts' })} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
|
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 50 }}
|
||||||
|
title="Oeffnungswinkel im Plan (Grad, 0-180)">
|
||||||
|
Öffn.
|
||||||
|
</span>
|
||||||
|
<input type="text"
|
||||||
|
value={String(oeff.openAngle ?? 90)}
|
||||||
|
onChange={(e) => {
|
||||||
|
const v = parseFloat(e.target.value.replace(',', '.'))
|
||||||
|
if (!Number.isNaN(v) && v >= 0 && v <= 180) onUpdate({ openAngle: v })
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
||||||
|
style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
|
||||||
|
<span style={{ fontSize: 10, color: 'var(--text-muted)' }}>°</span>
|
||||||
|
<BarToggle label="Umkehren"
|
||||||
|
onClick={() => onUpdate({ swingInvert: !oeff.swingInvert })}
|
||||||
|
title="Schwung-Richtung umkehren (nach aussen statt innen)" />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isFenster && (oeff.tuerTyp || 'normal') === 'normal' && (
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 50 }}
|
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 50 }}
|
||||||
title="Tueren-Rahmen-Typ. Zarge sitzt in der Oeffnung, Blockrahmen sitzt aussen herum">
|
title="Tueren-Rahmen-Typ. Zarge sitzt in der Oeffnung, Blockrahmen sitzt aussen herum">
|
||||||
|
|||||||
Reference in New Issue
Block a user