From bc87ae1acc637ba582088c15bf7cba36c42d0973 Mon Sep 17 00:00:00 2001 From: karim Date: Sun, 31 May 2026 08:04:17 +0200 Subject: [PATCH] Per-Wand Joint-Rolle: explizit waehlen wer am T-Stoss durchgeht MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neues UserString-Feld 'dossier_wand_joint_rolle' per wand_axis: - 'auto' (default): bisherige Logik. Bei beidseitig auto entscheidet die Style-Prio (hoehere = durchgehend) - 'durchgehend': diese Wand ueberschreibt → ich gehe durch, T-Miter wird NICHT auf mich angewendet - 'anstossend': diese Wand stoppt immer am Joint, auch wenn ich Prio-haerter waere T-Junction-Detection in regen ruft jetzt _wand_should_apply_t_miter: - my.rolle entscheidet zuerst - bei my=auto: through.rolle entscheidet - bei beide=auto: Prio-Vergleich (hoehere Prio = durchgehend) - Default: T-Miter applied (= ich stoppe am Through) Frontend: neue Dropdown 'Joint' in WallProperties zwischen Stil und Aufbau: - Auto (Prio entscheidet) - Durchgehend - Anstossend Backend-Pipeline: _attach_meta + _read_meta um wand_joint_rolle erweitert, state-JSON sendet 'jointRolle', _update_wall_body handhabt jointRolle-Patch. --- rhino/elemente.py | 56 +++++++++++++++++++++++++++++++++++++++++---- src/ElementeApp.jsx | 15 ++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/rhino/elemente.py b/rhino/elemente.py index d4dc39b..fe25ff4 100644 --- a/rhino/elemente.py +++ b/rhino/elemente.py @@ -41,6 +41,7 @@ _KEY_WAND_LAYERS = "dossier_wand_layers" # JSON-Liste [{name, dicke, color}] _KEY_WAND_LAYER_IDX = "dossier_wand_layer_idx" # Layer-Index am Volume-Brep _KEY_WAND_CHAIN_MEMBERS = "dossier_wand_chain_members" # JSON-Liste wand_ids einer Polyline-Chain (nur auf wand_volume) _KEY_WAND_STYLE_ID = "dossier_wand_style_id" # Verweis auf Project-Settings wand_styles[].id +_KEY_WAND_JOINT_ROLLE = "dossier_wand_joint_rolle" # "auto"|"durchgehend"|"anstossend" — per-Wand Override am T-Joint _KEY_DACH_NEIGUNG = "dossier_dach_neigung" # Grad als string ("30") _KEY_DACH_EAVE = "dossier_dach_eave" # Index der Traufkante (string) _KEY_DACH_TYP = "dossier_dach_typ" # "pult"|"sattel"|"walm"|"mansarde" @@ -3571,6 +3572,34 @@ def _wand_meta_prio(doc, meta): except Exception: return 500 +def _wand_should_apply_t_miter(doc, my_meta, through_wid): + """Entscheidet ob ich (T-Stem-Kandidat) am T-Junction zur Through-Wand + angeschmiegt werden soll (= T-mitered). Per-Wand Joint-Rolle hat Vorrang + vor Prio: + - my.rolle='durchgehend': skip miter (ich bin Through, gehe drueber) + - my.rolle='anstossend': apply miter (ich stoppe) + - my.rolle='auto' + through.rolle='anstossend': skip (through stoppt) + - my.rolle='auto' + through.rolle='durchgehend': apply + - beide 'auto': Prio entscheidet (hoehere Prio = Through)""" + if not my_meta: return True + my_rolle = (my_meta.get("wand_joint_rolle") or "auto").lower() + if my_rolle == "durchgehend": return False + if my_rolle == "anstossend": return True + # auto: schau auf through-wand + th_meta = _wand_meta_by_id(doc, through_wid) if through_wid else None + if th_meta: + th_rolle = (th_meta.get("wand_joint_rolle") or "auto").lower() + if th_rolle == "durchgehend": return True # through ist explizit through + if th_rolle == "anstossend": return False # through stoppt → ich Through + # beide auto → Prio + try: + if int(_wand_meta_prio(doc, my_meta)) > int( + _wand_meta_prio(doc, th_meta)): + return False # ich hoehere Prio = Through + except Exception: pass + return True # Default: T-mitere mich (T-Stem stoppt am Through) + + def _wand_meta_by_id(doc, wall_id): """Kuerzel: liefert das meta-Dict fuer eine wall_id ueber _find_axis.""" obj = _find_axis(doc, wall_id) @@ -3884,6 +3913,7 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over, wand_layered=None, wand_layers=None, wand_layer_idx=None, wand_chain_members=None, wand_style_id=None, + wand_joint_rolle=None, aussp_parent=None): """User-Strings auf die Object-Attributes setzen.""" obj_attrs.SetUserString(_KEY_ID, wall_id) @@ -4186,6 +4216,12 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over, obj_attrs.SetUserString(_KEY_WAND_STYLE_ID, str(wand_style_id or "")) except Exception: pass + if wand_joint_rolle is not None: + try: + rv = str(wand_joint_rolle or "auto").lower() + if rv not in ("auto", "durchgehend", "anstossend"): rv = "auto" + obj_attrs.SetUserString(_KEY_WAND_JOINT_ROLLE, rv) + except Exception: pass if wand_chain_members is not None: try: import json as _json @@ -4580,6 +4616,7 @@ def _read_meta(obj): "wand_layer_idx": w_layer_idx, "wand_chain_members": w_chain_members, "wand_style_id": a.GetUserString(_KEY_WAND_STYLE_ID) or "", + "wand_joint_rolle": (a.GetUserString(_KEY_WAND_JOINT_ROLLE) or "auto").lower(), "aussp_parent": aussp_parent_raw, } except Exception: @@ -8363,8 +8400,9 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name exclude_ids=chain_set) if tj is not None: _oid, b_tan, b_dicke = tj - tm = _t_junction_miter(p_s, out_s, b_tan, b_dicke) - if tm is not None: miter_start = tm + if _wand_should_apply_t_miter(doc, meta, _oid): + tm = _t_junction_miter(p_s, out_s, b_tan, b_dicke) + if tm is not None: miter_start = tm else: # 3+ Joint: ich bin T-Stem wenn meine Tangente NICHT # collinear mit zwei collinearen Partner-Tangenten ist. @@ -8393,8 +8431,9 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name exclude_ids=chain_set) if tj is not None: _oid, b_tan, b_dicke = tj - tm = _t_junction_miter(p_e, out_e, b_tan, b_dicke) - if tm is not None: miter_end = tm + if _wand_should_apply_t_miter(doc, meta, _oid): + tm = _t_junction_miter(p_e, out_e, b_tan, b_dicke) + if tm is not None: miter_end = tm else: # 3+ Joint: T-Stem-Erkennung (analog start) _my_t = geom.TangentAtEnd @@ -9498,6 +9537,7 @@ class ElementeBridge(panel_base.BaseBridge): "layered": bool(meta.get("wand_layered", False)), "layers": meta.get("wand_layers", []), "styleId": meta.get("wand_style_id", "") or "", + "jointRolle": meta.get("wand_joint_rolle", "auto") or "auto", }) elif meta["type"] == "decke_outline": uk, ok = _resolve_decke_z(doc, meta["geschoss"], meta["dicke"], @@ -14543,6 +14583,11 @@ class ElementeBridge(panel_base.BaseBridge): try: old_chain_members = _find_wall_chain(doc, wall_id) except Exception: pass + # Joint-Rolle: explizit per-Wand "auto"/"durchgehend"/"anstossend" + if old_meta["type"] == "wand_axis" and "jointRolle" in p: + new_joint_rolle = p.get("jointRolle") or "auto" + else: + new_joint_rolle = old_meta.get("wand_joint_rolle", "auto") _attach_meta(attrs, wall_id, old_meta["type"], geschoss, dicke, uk_over, ok_over, referenz, neigung=neigung, eave_idx=eave_idx, dach_typ=dach_typ, @@ -14550,7 +14595,8 @@ class ElementeBridge(panel_base.BaseBridge): dach_variante=dach_variante, wand_layered=wand_layered, wand_layers=wand_layers if wand_layered else [], - wand_style_id=new_style_id if old_meta["type"] == "wand_axis" else None) + wand_style_id=new_style_id if old_meta["type"] == "wand_axis" else None, + wand_joint_rolle=new_joint_rolle if old_meta["type"] == "wand_axis" else None) axis_obj.Attributes = attrs axis_obj.CommitChanges() # Volumen regenerieren (Layer ggf. anpassen) diff --git a/src/ElementeApp.jsx b/src/ElementeApp.jsx index 87d9c49..a56bfb6 100644 --- a/src/ElementeApp.jsx +++ b/src/ElementeApp.jsx @@ -1528,6 +1528,21 @@ function WallProperties({ wall, geschosse, materials, wandStyles, onUpdate, onDe )} +
+ + Joint + + +
+
Aufbau