From 9999f3d0ad449880aab931df482f27ed709c6605 Mon Sep 17 00:00:00 2001 From: karim Date: Sun, 31 May 2026 12:07:32 +0200 Subject: [PATCH] Schichtdurchdringung am T-Junction: pro Schicht material-basiertes Verbinden MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bisher: bei T-Junction stoppen ALLE Layers des T-Stems uniform am Near-Face der Through-Wand (uniformer T-Miter pro Wand). Neu: per-Schicht Logik im _make_wand_layer_breps: - T-Stem-Schicht mit Material das in Through-Wand auch vorkommt → Layer-Axis extends um through_dicke/2 → Layer durchstoesst Through-Wand bis zur Far-Face → visuell verbunden, kein sichtbarer Joint - Schicht ohne Material-Match in Through-Wand → standard T-Miter, stoppt am Near-Face Beispiel "Aussenwand 30cm" (Beton/Daemmung/Putz) T auf gleicher Aussenwand: alle 3 Schichten matchen → durchgehend gemerged. Beispiel "Aussenwand 30cm" T auf "Beton solid 15cm": - T-Stem Beton matched (Through-Solid hat Beton via Style) → durchstoesst - T-Stem Daemmung/Putz: kein Match → stoppen am Through-Beton-Aussenkante Implementation: - _make_wand_layer_breps: per_layer_ext_start/end + per_layer_miter_start/end - _t_junction_layer_overrides(doc, my_meta, through_meta, ...): baut die per-Layer Overrides via Material-Set-Lookup. Solid through wird via _wand_solid_material auf Style-Material aufgeloest. - Regen-Pfad: t_junction_start/end speichern (oid+tan+dicke+ep+out_dir), vor _make_wand_layer_breps die Overrides bauen. --- rhino/elemente.py | 119 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 112 insertions(+), 7 deletions(-) diff --git a/rhino/elemente.py b/rhino/elemente.py index 43f25f6..0338344 100644 --- a/rhino/elemente.py +++ b/rhino/elemente.py @@ -3600,6 +3600,53 @@ def _wand_meta_prio(doc, meta): except Exception: return 500 +def _t_junction_layer_overrides(doc, my_meta, through_meta, ep_pt, out_dir, + b_tan, b_dicke): + """Per-Layer Schichtdurchdringung bei T-Junction zwischen layered Waenden. + + Pro T-Stem-Schicht wird in der Through-Wand nach einer Schicht mit + GLEICHEM MATERIAL gesucht: + - Match: T-Stem-Layer extends durch die Through-Wand (Axis-Extension um + b_dicke/2 = bis Far-Face) → visuell verbunden + - No Match: T-Stem-Layer stoppt am Near-Face (Standard T-Miter) + + Returns (per_layer_ext, per_layer_miter) — Listen pro T-Stem-Layer. + Wenn my keine Layers hat: (None, None) (Caller faellt auf uniform Miter + zurueck).""" + if not my_meta or not through_meta: return None, None + my_layers = my_meta.get("wand_layers") or [] + if not my_layers: return None, None + th_layers = through_meta.get("wand_layers") or [] + # Through-Material-Set sammeln + th_materials = set() + if through_meta.get("wand_layered") and th_layers: + for l in th_layers: + mat = (l.get("material") or "").strip() + if mat: th_materials.add(mat) + else: + # Through ist solid → 1 Material aus Style + try: + sm = _wand_solid_material(doc, through_meta) if doc else "" + if sm: th_materials.add(sm) + except Exception: pass + + standard_miter = _t_junction_miter(ep_pt, out_dir, b_tan, b_dicke) + extension = float(b_dicke) * 0.5 # bis Far-Face + per_ext = [] + per_miter = [] + for layer in my_layers: + mat = (layer.get("material") or "").strip() + if mat and mat in th_materials: + # Match → durchstossen + per_ext.append(extension) + per_miter.append(None) + else: + # Kein Match → an Near-Face stoppen + per_ext.append(0.0) + per_miter.append(standard_miter) + return per_ext, per_miter + + 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 @@ -3867,25 +3914,56 @@ def _ensure_pbr_material(doc, mat_dict): def _make_wand_layer_breps(axis_curve, layers, dicke, referenz, uk, ok, - miter_start=None, miter_end=None): + miter_start=None, miter_end=None, + per_layer_ext_start=None, per_layer_ext_end=None, + per_layer_miter_start=None, per_layer_miter_end=None): """Baut eine Liste (brep, color_hex, name) pro Schicht. Schicht-Reihen- folge: von der +perp-Seite zur -perp-Seite (links→rechts entlang der - Wand-Achse). layers = Liste von dicts mit Keys 'dicke', 'color', 'name'.""" + Wand-Achse). layers = Liste von dicts mit Keys 'dicke', 'color', 'name'. + + Per-Layer Overrides (alle optional, jede Liste len(layers)): + - per_layer_ext_start/end: zusaetzliche Axis-Verlaengerung pro Layer am + jeweiligen Ende (fuer Schichtdurchdringung bei T-Junction) + - per_layer_miter_start/end: per-Layer Miter (= clip am Through-Face) + → ueberschreibt die globalen miter_start/_end args fuer diesen Layer""" out = [] if not layers: return out start_off, d_total = _wall_offsets_from_referenz(dicke, referenz) cur = start_off max_ext = float(d_total) * 5.0 - for layer in layers: + for layer_idx, layer in enumerate(layers): try: d = float(layer.get("dicke", 0)) except Exception: d = 0.0 if d <= 0: continue d_left = cur d_right = cur - d - brep = _make_wall_layer_brep(axis_curve, d_left, d_right, uk, ok, - miter_start=miter_start, - miter_end=miter_end, + # Per-Layer Overrides ziehen + m_start = miter_start + m_end = miter_end + if (per_layer_miter_start is not None + and layer_idx < len(per_layer_miter_start)): + m_start = per_layer_miter_start[layer_idx] + if (per_layer_miter_end is not None + and layer_idx < len(per_layer_miter_end)): + m_end = per_layer_miter_end[layer_idx] + ext_s = 0.0; ext_e = 0.0 + if (per_layer_ext_start is not None + and layer_idx < len(per_layer_ext_start)): + try: ext_s = float(per_layer_ext_start[layer_idx] or 0) + except Exception: ext_s = 0.0 + if (per_layer_ext_end is not None + and layer_idx < len(per_layer_ext_end)): + try: ext_e = float(per_layer_ext_end[layer_idx] or 0) + except Exception: ext_e = 0.0 + # Axis ggf. extenden (fuer Schichtdurchdringung) + if ext_s > 1e-9 or ext_e > 1e-9: + axis_for_layer = _extend_axis_curve(axis_curve, ext_s, ext_e) + else: + axis_for_layer = axis_curve + brep = _make_wall_layer_brep(axis_for_layer, d_left, d_right, uk, ok, + miter_start=m_start, + miter_end=m_end, max_miter_extend=max_ext) out.append((brep, layer.get("color", ""), layer.get("name", ""))) cur = d_right @@ -8406,6 +8484,8 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name # liegen ja schon in der gejointen Polyline drin. miter_start = None miter_end = None + t_junction_start = None # (through_wid, b_tan, b_dicke, ep_pt, out_dir) bei T-Junction + t_junction_end = None chain_set = set(chain_ids) if (chain_ids and len(chain_ids) > 1) else {element_id} try: joints = _collect_wall_joints(doc, meta["geschoss"]) @@ -8431,6 +8511,9 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name 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 + # Through-Meta merken fuer per-Layer Schicht- + # durchdringung (s. unten bei layer_breps build) + t_junction_start = (_oid, b_tan, b_dicke, p_s, out_s) else: # 3+ Joint: ich bin T-Stem wenn meine Tangente NICHT # collinear mit zwei collinearen Partner-Tangenten ist. @@ -8462,6 +8545,7 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name 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 + t_junction_end = (_oid, b_tan, b_dicke, p_e, out_e) else: # 3+ Joint: T-Stem-Erkennung (analog start) _my_t = geom.TangentAtEnd @@ -8480,10 +8564,31 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name layers_def = meta.get("wand_layers") or [] is_layered = bool(meta.get("wand_layered")) and len(layers_def) > 0 if is_layered: + # Per-Layer Schichtdurchdringung bei T-Junctions: + # Pro Schicht des T-Stems suchen wir in der Through-Wand eine + # Schicht mit gleichem Material. Match → durchstossen (Layer- + # Axis extension). Kein Match → standard T-Miter. + pl_ext_s = None; pl_ext_e = None + pl_miter_s = None; pl_miter_e = None + if t_junction_start is not None: + _toid, _btan, _bdk, _ep, _od = t_junction_start + _tm = _wand_meta_by_id(doc, _toid) + if _tm is not None: + pl_ext_s, pl_miter_s = _t_junction_layer_overrides( + doc, meta, _tm, _ep, _od, _btan, _bdk) + if t_junction_end is not None: + _toid, _btan, _bdk, _ep, _od = t_junction_end + _tm = _wand_meta_by_id(doc, _toid) + if _tm is not None: + pl_ext_e, pl_miter_e = _t_junction_layer_overrides( + doc, meta, _tm, _ep, _od, _btan, _bdk) layer_breps = _make_wand_layer_breps( geom, layers_def, meta["dicke"], meta.get("referenz", "mid"), uk, ok, - miter_start=miter_start, miter_end=miter_end) + miter_start=miter_start, miter_end=miter_end, + per_layer_ext_start=pl_ext_s, per_layer_ext_end=pl_ext_e, + per_layer_miter_start=pl_miter_s, + per_layer_miter_end=pl_miter_e) else: single_brep = _make_volume_geometry( geom, meta["dicke"], uk, ok,