T-Junction Phase 2 + Layer-Smart-Join: Backbone-Drill bis Stahl-Layer-Far-Face

Phase-2-Fixes:
- Backbone-Ext berechnet bis MATCHING-Layer (Stahl) far-face, nicht bis
  Body-far-face. Beton drillt durch Stahl-Band, nicht durch Daemm/Putz.
- Case A/B detection via dot(out_dir, RhinoPerp). Rhino Curve.Offset
  benutzt (tan × +z) = (b_tan.Y, -b_tan.X), nicht (-b_tan.Y, b_tan.X).
- Backbone-axis-ext separat von _my_axis_ext: nur Backbone-Column
  extended, non-backbone columns stoppen am Snap.
- Through-only Mats (z.B. Daemm wo T-stem keinen hat) werden auch
  durchlaufen damit hoehere-prio backbone diese carven kann.
- Post-Carve Union: gleiche-Material-Pieces mergen (Backbone-Beton-Col
  + Through-Stahl-Band → T-Shape).
- BBox-overlap strict-Filter vor BoolDiff: touching coplanar faces
  ueberspringen, vermeidet Rhino BoolDiff "punch-through" Artefakte.
- _has_my_cols guard: KeyError beim consume von T-stem-Layern fuer
  through-only mats verhindert.

Layer-Smart-Join (smart_join.py):
- Neue _layer_join_attempt: 2 selektierte wand_volume Breps gleicher
  Material via BoolUnion mergen. Manueller Override fuer Edge-Cases
  wo auto Phase 2 nicht reicht.

Cluster-Volume Select-Handler (elemente.py):
- Alt-Click bypassed Cluster-Swap → User kann einzelne Layer-Breps
  direkt anwaehlen (= fuer Layer-Smart-Join).
This commit is contained in:
2026-06-01 21:54:02 +02:00
parent df56a54b66
commit 118bc51cc5
2 changed files with 185 additions and 45 deletions
+68
View File
@@ -296,12 +296,80 @@ def _l_join_attempt(doc, sel):
doc.EndUndoRecord(ur)
def _layer_join_attempt(doc, wand_volumes):
"""Layer-Level Join: Versuch BoolUnion zwischen 2 wand_volume Breps.
Wenn beide gleiches Material haben + sich beruehren oder ueberlappen
→ mergen zu einem Brep. Behaelt UserStrings (= wand_id, material,
layer_idx) vom ersten Brep."""
if len(wand_volumes) != 2: return False
br0 = wand_volumes[0].Geometry
br1 = wand_volumes[1].Geometry
if not isinstance(br0, rg.Brep) or not isinstance(br1, rg.Brep):
return False
# Material-Check via UserStrings (= dossier_element_material)
mat0 = wand_volumes[0].Attributes.GetUserString(
"dossier_layer_material") or ""
mat1 = wand_volumes[1].Attributes.GetUserString(
"dossier_layer_material") or ""
if mat0 and mat1 and mat0 != mat1:
print("[SMART-JOIN] Layer-Join: Materialien unterschiedlich "
"({} vs {}), skip.".format(mat0, mat1))
return False
try:
union = rg.Brep.CreateBooleanUnion([br0, br1], 0.01)
except Exception as ex:
print("[SMART-JOIN] Layer-Join BoolUnion exc:", ex)
return False
if not union or len(union) == 0:
print("[SMART-JOIN] Layer-Join: Breps overlappen nicht "
"(BoolUnion returned None/empty)")
return False
if len(union) > 1:
print("[SMART-JOIN] Layer-Join: Breps overlappen nicht (BoolUnion"
" returned {} pieces, brauche 1)".format(len(union)))
return False
# 1 merged Brep
merged = union[0]
if not merged.IsValid:
print("[SMART-JOIN] Layer-Join: merged Brep invalid")
return False
try: merged.MergeCoplanarFaces(0.01)
except Exception: pass
ur = doc.BeginUndoRecord("DOSSIER Layer-Join")
try:
ok = doc.Objects.Replace(wand_volumes[0].Id, merged)
if ok:
doc.Objects.Delete(wand_volumes[1].Id, True)
return True
return False
finally:
doc.EndUndoRecord(ur)
def _run():
doc = Rhino.RhinoDoc.ActiveDoc
if doc is None: return
sel = list(doc.Objects.GetSelectedObjects(False, False))
if not sel:
Rhino.RhinoApp.RunScript("_Join", False); return
# Layer-Level Join: wenn GENAU 2 wand_volume Breps selektiert + keine
# wand_axis → versuche Layer-Join (= BoolUnion der zwei Layer-Breps).
_wand_vols = [o for o in sel
if (o.Attributes.GetUserString("dossier_element_type")
or "") == "wand_volume"]
_wand_axes = [o for o in sel
if (o.Attributes.GetUserString("dossier_element_type")
or "") == "wand_axis"]
if (len(_wand_vols) == 2 and len(_wand_axes) == 0
and len(sel) == 2):
print("[SMART-JOIN] Layer-Join attempt: 2 wand_volume Breps")
try:
if _layer_join_attempt(doc, _wand_vols):
doc.Views.Redraw()
print("[SMART-JOIN] Layer-Join: Breps zu einem gemerged")
return
except Exception as ex:
print("[SMART-JOIN] Layer-Join exc:", ex)
# Info-Hint (T-Join unterstuetzt 1-Wand-Modus, L-Join braucht 2)
n_wand_axes = sum(1 for o in sel
if (o.Attributes.GetUserString("dossier_element_type")