Schichtdurchdringung am T-Junction: pro Schicht material-basiertes Verbinden

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.
This commit is contained in:
2026-05-31 12:07:32 +02:00
parent 3609236da9
commit 9999f3d0ad
+112 -7
View File
@@ -3600,6 +3600,53 @@ def _wand_meta_prio(doc, meta):
except Exception: return 500 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): def _wand_should_apply_t_miter(doc, my_meta, through_wid):
"""Entscheidet ob ich (T-Stem-Kandidat) am T-Junction zur Through-Wand """Entscheidet ob ich (T-Stem-Kandidat) am T-Junction zur Through-Wand
angeschmiegt werden soll (= T-mitered). Per-Wand Joint-Rolle hat Vorrang 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, 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- """Baut eine Liste (brep, color_hex, name) pro Schicht. Schicht-Reihen-
folge: von der +perp-Seite zur -perp-Seite (linksrechts entlang der folge: von der +perp-Seite zur -perp-Seite (linksrechts 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 = [] out = []
if not layers: if not layers:
return out return out
start_off, d_total = _wall_offsets_from_referenz(dicke, referenz) start_off, d_total = _wall_offsets_from_referenz(dicke, referenz)
cur = start_off cur = start_off
max_ext = float(d_total) * 5.0 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)) try: d = float(layer.get("dicke", 0))
except Exception: d = 0.0 except Exception: d = 0.0
if d <= 0: continue if d <= 0: continue
d_left = cur d_left = cur
d_right = cur - d d_right = cur - d
brep = _make_wall_layer_brep(axis_curve, d_left, d_right, uk, ok, # Per-Layer Overrides ziehen
miter_start=miter_start, m_start = miter_start
miter_end=miter_end, 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) max_miter_extend=max_ext)
out.append((brep, layer.get("color", ""), layer.get("name", ""))) out.append((brep, layer.get("color", ""), layer.get("name", "")))
cur = d_right 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. # liegen ja schon in der gejointen Polyline drin.
miter_start = None miter_start = None
miter_end = 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} chain_set = set(chain_ids) if (chain_ids and len(chain_ids) > 1) else {element_id}
try: try:
joints = _collect_wall_joints(doc, meta["geschoss"]) 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): if _wand_should_apply_t_miter(doc, meta, _oid):
tm = _t_junction_miter(p_s, out_s, b_tan, b_dicke) tm = _t_junction_miter(p_s, out_s, b_tan, b_dicke)
if tm is not None: miter_start = tm 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: else:
# 3+ Joint: ich bin T-Stem wenn meine Tangente NICHT # 3+ Joint: ich bin T-Stem wenn meine Tangente NICHT
# collinear mit zwei collinearen Partner-Tangenten ist. # 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): if _wand_should_apply_t_miter(doc, meta, _oid):
tm = _t_junction_miter(p_e, out_e, b_tan, b_dicke) tm = _t_junction_miter(p_e, out_e, b_tan, b_dicke)
if tm is not None: miter_end = tm if tm is not None: miter_end = tm
t_junction_end = (_oid, b_tan, b_dicke, p_e, out_e)
else: else:
# 3+ Joint: T-Stem-Erkennung (analog start) # 3+ Joint: T-Stem-Erkennung (analog start)
_my_t = geom.TangentAtEnd _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 [] layers_def = meta.get("wand_layers") or []
is_layered = bool(meta.get("wand_layered")) and len(layers_def) > 0 is_layered = bool(meta.get("wand_layered")) and len(layers_def) > 0
if is_layered: 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( layer_breps = _make_wand_layer_breps(
geom, layers_def, meta["dicke"], geom, layers_def, meta["dicke"],
meta.get("referenz", "mid"), uk, ok, 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: else:
single_brep = _make_volume_geometry( single_brep = _make_volume_geometry(
geom, meta["dicke"], uk, ok, geom, meta["dicke"], uk, ok,