T-Junction angled: angle-aware Extension + Miter fuer flache T-Oberseite
Bei angled T-Junctions (nicht-90°) ragten Column-Ecken ueber Through- Wand-Face raus (= "Stummel"). Fix in zwei Pfaden: Cluster-Union (solid walls, _build_cluster_union_brep): - Extension entlang my_tan = n_half / |cross(my_tan, n_tan)|. Fuer 90° bleibt = n_half, fuer angled wird laenger damit mitered End-Face auf Through-Body-Far-Face landet. - Miter mit Nachbar-Tangent als miter_dir clippt L+R Curves an flache horizontale Linie aligned mit Through-Wand-Richtung. - _make_volume_geometry erhaelt miter_start/_end. Phase 2 (layered walls, _regenerate_element_body): - Gleiches angle-aware Extension-Factor fuer _backbone_axis_ext. - _layer_rect_2d erhaelt miter_start/_end Parameter. - Backbone-col bekommt Miter am extended-end-Position. - Non-backbone Spalten bekommen Std-Miter am Snap-Position. - Beide aligned mit through-tan (b_tan) Richtung → T-Oberseite flach. Diagnostic-Prints (bb-ext-calc, PRE-CARVE, CARVE cb, DO/SKIP carve) bleiben drin fuer kommende Edge-Cases.
This commit is contained in:
+97
-12
@@ -2563,7 +2563,14 @@ def _build_cluster_union_brep(doc, cluster_ids, uk, ok):
|
|||||||
breps = []
|
breps = []
|
||||||
for wid, (ax, wm, g) in walls.items():
|
for wid, (ax, wm, g) in walls.items():
|
||||||
ext_start = 0.0; ext_end = 0.0
|
ext_start = 0.0; ext_end = 0.0
|
||||||
|
miter_start = None; miter_end = None
|
||||||
my_start = g.PointAtStart; my_end = g.PointAtEnd
|
my_start = g.PointAtStart; my_end = g.PointAtEnd
|
||||||
|
try:
|
||||||
|
_myt = my_end - my_start
|
||||||
|
_ml = _myt.Length
|
||||||
|
my_tan = (_myt / _ml) if _ml > 1e-9 else rg.Vector3d(1, 0, 0)
|
||||||
|
except Exception:
|
||||||
|
my_tan = rg.Vector3d(1, 0, 0)
|
||||||
for nid, (n_ax, n_meta, n_g) in walls.items():
|
for nid, (n_ax, n_meta, n_g) in walls.items():
|
||||||
if nid == wid: continue
|
if nid == wid: continue
|
||||||
# Extension um NACHBAR-dicke/2 (= bis zur Mitte bzw. Far-Face der
|
# Extension um NACHBAR-dicke/2 (= bis zur Mitte bzw. Far-Face der
|
||||||
@@ -2571,6 +2578,17 @@ def _build_cluster_union_brep(doc, cluster_ids, uk, ok):
|
|||||||
# Seite als kleiner Stummel rausragen.
|
# Seite als kleiner Stummel rausragen.
|
||||||
n_half = float(n_meta.get("dicke", 0.0)) * 0.5
|
n_half = float(n_meta.get("dicke", 0.0)) * 0.5
|
||||||
n_start = n_g.PointAtStart; n_end = n_g.PointAtEnd
|
n_start = n_g.PointAtStart; n_end = n_g.PointAtEnd
|
||||||
|
try:
|
||||||
|
_nt = n_end - n_start
|
||||||
|
_nl = _nt.Length
|
||||||
|
n_tan = (_nt / _nl) if _nl > 1e-9 else rg.Vector3d(1, 0, 0)
|
||||||
|
except Exception:
|
||||||
|
n_tan = rg.Vector3d(1, 0, 0)
|
||||||
|
# Angle-aware Extension fuer T-Junction: ext entlang my_tan
|
||||||
|
# damit mitered End-Face exakt auf Nachbar-Body-Far-Face landet.
|
||||||
|
# ext = n_half / |my_tan · through_perp|. Fuer 90° = n_half.
|
||||||
|
_proj = abs(my_tan.X * n_tan.Y - my_tan.Y * n_tan.X)
|
||||||
|
n_ext = n_half / _proj if _proj > 1e-6 else n_half
|
||||||
# Endpunkt-Share: mein Endpunkt = ihr Endpunkt
|
# Endpunkt-Share: mein Endpunkt = ihr Endpunkt
|
||||||
if (my_start.DistanceTo(n_start) < eps or
|
if (my_start.DistanceTo(n_start) < eps or
|
||||||
my_start.DistanceTo(n_end) < eps):
|
my_start.DistanceTo(n_end) < eps):
|
||||||
@@ -2587,14 +2605,23 @@ def _build_cluster_union_brep(doc, cluster_ids, uk, ok):
|
|||||||
if (n_start.DistanceTo(my_ep) < eps or
|
if (n_start.DistanceTo(my_ep) < eps or
|
||||||
n_end.DistanceTo(my_ep) < eps):
|
n_end.DistanceTo(my_ep) < eps):
|
||||||
continue
|
continue
|
||||||
# Verlaengere mein Brep bis zur Far-Face der Nachbar-Wand
|
# Angle-aware Extension + Miter aligned mit Nachbar-Tangent
|
||||||
|
# damit angled T-Junctions clean clippen (T-Oberseite flach).
|
||||||
if is_start:
|
if is_start:
|
||||||
ext_start = max(ext_start, n_half)
|
ext_start = max(ext_start, n_ext)
|
||||||
|
_mpt = rg.Point3d(my_ep.X - my_tan.X * n_ext,
|
||||||
|
my_ep.Y - my_tan.Y * n_ext, 0)
|
||||||
|
miter_start = (_mpt, n_tan)
|
||||||
else:
|
else:
|
||||||
ext_end = max(ext_end, n_half)
|
ext_end = max(ext_end, n_ext)
|
||||||
|
_mpt = rg.Point3d(my_ep.X + my_tan.X * n_ext,
|
||||||
|
my_ep.Y + my_tan.Y * n_ext, 0)
|
||||||
|
miter_end = (_mpt, n_tan)
|
||||||
ext_g = _extend_axis_curve(g, ext_start, ext_end)
|
ext_g = _extend_axis_curve(g, ext_start, ext_end)
|
||||||
b = _make_volume_geometry(ext_g, wm["dicke"], uk, ok,
|
b = _make_volume_geometry(ext_g, wm["dicke"], uk, ok,
|
||||||
wm.get("referenz", "mid"))
|
wm.get("referenz", "mid"),
|
||||||
|
miter_start=miter_start,
|
||||||
|
miter_end=miter_end)
|
||||||
if b is not None:
|
if b is not None:
|
||||||
breps.append(b)
|
breps.append(b)
|
||||||
if not breps: return None
|
if not breps: return None
|
||||||
@@ -3377,12 +3404,17 @@ def _make_wall_layer_brep(axis_curve, d_left, d_right, uk, ok,
|
|||||||
return extrusion.ToBrep()
|
return extrusion.ToBrep()
|
||||||
|
|
||||||
|
|
||||||
def _layer_rect_2d(axis_curve, d_left, d_right):
|
def _layer_rect_2d(axis_curve, d_left, d_right,
|
||||||
|
miter_start=None, miter_end=None):
|
||||||
"""Liefert geschlossene XY-Rect-Curve fuer eine Schicht (Achse +
|
"""Liefert geschlossene XY-Rect-Curve fuer eine Schicht (Achse +
|
||||||
perp Offsets). Genutzt fuer 2D-Polylinen-Union beim T-Junction.
|
perp Offsets). Genutzt fuer 2D-Polylinen-Union beim T-Junction.
|
||||||
Nutzt _offset_curve wie _make_wall_layer_brep — wichtig fuer
|
Nutzt _offset_curve wie _make_wall_layer_brep — wichtig fuer
|
||||||
Boolean-Compatibility (PolyCurve statt PolylineCurve).
|
Boolean-Compatibility (PolyCurve statt PolylineCurve).
|
||||||
Forced z=0 + CCW winding (= ClosedCurveOrientation check)."""
|
Forced z=0 + CCW winding (= ClosedCurveOrientation check).
|
||||||
|
|
||||||
|
miter_start/_end: optional (miter_pt, miter_dir) — clip L+R am Ende
|
||||||
|
auf die Miter-Linie. Fuer angled T-Junctions damit Column-Ecken
|
||||||
|
nicht ueber Through-Face rausragen."""
|
||||||
if not isinstance(axis_curve, rg.Curve): return None
|
if not isinstance(axis_curve, rg.Curve): return None
|
||||||
d_l = float(d_left); d_r = float(d_right)
|
d_l = float(d_left); d_r = float(d_right)
|
||||||
if abs(d_l - d_r) < 1e-9: return None
|
if abs(d_l - d_r) < 1e-9: return None
|
||||||
@@ -3400,6 +3432,15 @@ def _layer_rect_2d(axis_curve, d_left, d_right):
|
|||||||
right = _offset_curve(ax_xy, plane, d_r, tol)
|
right = _offset_curve(ax_xy, plane, d_r, tol)
|
||||||
if not left or not right: return None
|
if not left or not right: return None
|
||||||
L = left[0]; R = right[0]
|
L = left[0]; R = right[0]
|
||||||
|
_max_ext = abs(d_l - d_r) * 5.0
|
||||||
|
if miter_start is not None:
|
||||||
|
_mpt, _mdir = miter_start
|
||||||
|
L = _apply_miter(L, "start", _mpt, _mdir, _max_ext)
|
||||||
|
R = _apply_miter(R, "start", _mpt, _mdir, _max_ext)
|
||||||
|
if miter_end is not None:
|
||||||
|
_mpt, _mdir = miter_end
|
||||||
|
L = _apply_miter(L, "end", _mpt, _mdir, _max_ext)
|
||||||
|
R = _apply_miter(R, "end", _mpt, _mdir, _max_ext)
|
||||||
R.Reverse()
|
R.Reverse()
|
||||||
cap_s = rg.LineCurve(L.PointAtEnd, R.PointAtStart)
|
cap_s = rg.LineCurve(L.PointAtEnd, R.PointAtStart)
|
||||||
cap_e = rg.LineCurve(R.PointAtEnd, L.PointAtStart)
|
cap_e = rg.LineCurve(R.PointAtEnd, L.PointAtStart)
|
||||||
@@ -8824,18 +8865,50 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
|||||||
_ext_arr[_li_x] or 0)
|
_ext_arr[_li_x] or 0)
|
||||||
break
|
break
|
||||||
if not _backbone_mat: continue
|
if not _backbone_mat: continue
|
||||||
|
# T-stem tangent + n_tan fuer angle-aware extension + miter
|
||||||
|
try:
|
||||||
|
_gst = geom.PointAtStart
|
||||||
|
_get = geom.PointAtEnd
|
||||||
|
_vec = _get - _gst
|
||||||
|
_vl = _vec.Length
|
||||||
|
_mtan = ((_vec / _vl) if _vl > 1e-9
|
||||||
|
else rg.Vector3d(0, 1, 0))
|
||||||
|
except Exception:
|
||||||
|
_mtan = rg.Vector3d(0, 1, 0)
|
||||||
|
_ntan = _tj_info[1] # through-wall tangent
|
||||||
|
# Cross product magnitude = |sin(angle)| zwischen tan-Vektoren
|
||||||
|
# = cos(deviation from 90°). Fuer 90° T = 1.
|
||||||
|
_proj_ang = abs(_mtan.X * _ntan.Y - _mtan.Y * _ntan.X)
|
||||||
|
_ext_factor = 1.0 / _proj_ang if _proj_ang > 1e-6 else 1.0
|
||||||
# Backbone-axis = extended bis Through-Far-Face (drill).
|
# Backbone-axis = extended bis Through-Far-Face (drill).
|
||||||
# Non-backbone columns nutzen geom (= stoppen am Snap).
|
# Angle-aware: ext entlang my_tan damit End-Face auf
|
||||||
if _backbone_ext_val > 0:
|
# through-perp-Linie landet (= flach in n_tan-Richtung).
|
||||||
|
_bb_ext_eff = _backbone_ext_val * _ext_factor
|
||||||
|
if _bb_ext_eff > 1e-9:
|
||||||
if _is_end:
|
if _is_end:
|
||||||
_backbone_axis_ext = _extend_axis_curve(
|
_backbone_axis_ext = _extend_axis_curve(
|
||||||
geom, 0, _backbone_ext_val)
|
geom, 0, _bb_ext_eff)
|
||||||
else:
|
else:
|
||||||
_backbone_axis_ext = _extend_axis_curve(
|
_backbone_axis_ext = _extend_axis_curve(
|
||||||
geom, _backbone_ext_val, 0)
|
geom, _bb_ext_eff, 0)
|
||||||
else:
|
else:
|
||||||
_backbone_axis_ext = geom
|
_backbone_axis_ext = geom
|
||||||
_my_axis_ext = geom
|
_my_axis_ext = geom
|
||||||
|
# Miter fuer Phase 2 columns: aligned mit through-tan
|
||||||
|
# damit angled T-junction-Spalten flache End-Faces haben.
|
||||||
|
# Backbone-end miter am extended position. Non-backbone
|
||||||
|
# miter am Snap-Position (= geom-Ende).
|
||||||
|
_snap = _tj_info[3]
|
||||||
|
_bb_end_pt = rg.Point3d(
|
||||||
|
_snap.X + (_mtan.X * _bb_ext_eff
|
||||||
|
if _is_end
|
||||||
|
else -_mtan.X * _bb_ext_eff),
|
||||||
|
_snap.Y + (_mtan.Y * _bb_ext_eff
|
||||||
|
if _is_end
|
||||||
|
else -_mtan.Y * _bb_ext_eff),
|
||||||
|
0)
|
||||||
|
_bb_miter = (_bb_end_pt, _ntan)
|
||||||
|
_std_miter = (rg.Point3d(_snap.X, _snap.Y, 0), _ntan)
|
||||||
try:
|
try:
|
||||||
_ps = _backbone_axis_ext.PointAtStart
|
_ps = _backbone_axis_ext.PointAtStart
|
||||||
_pe = _backbone_axis_ext.PointAtEnd
|
_pe = _backbone_axis_ext.PointAtEnd
|
||||||
@@ -8892,8 +8965,13 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
|||||||
_bbones = _my_info_by_mat[_backbone_mat]
|
_bbones = _my_info_by_mat[_backbone_mat]
|
||||||
if _bbones:
|
if _bbones:
|
||||||
_bd_l, _bd_r, _ = _bbones[0]
|
_bd_l, _bd_r, _ = _bbones[0]
|
||||||
|
_bb_miter_args = (
|
||||||
|
{"miter_end": _bb_miter}
|
||||||
|
if _is_end
|
||||||
|
else {"miter_start": _bb_miter})
|
||||||
_backbone_col_rect = _layer_rect_2d(
|
_backbone_col_rect = _layer_rect_2d(
|
||||||
_backbone_axis_ext, _bd_l, _bd_r)
|
_backbone_axis_ext, _bd_l, _bd_r,
|
||||||
|
**_bb_miter_args)
|
||||||
# 3D Version fuer Carve
|
# 3D Version fuer Carve
|
||||||
if _backbone_col_rect is not None:
|
if _backbone_col_rect is not None:
|
||||||
try:
|
try:
|
||||||
@@ -8959,10 +9037,17 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
|||||||
_column_x_ranges = []
|
_column_x_ranges = []
|
||||||
_ax_for_col = (_backbone_axis_ext if _is_backbone
|
_ax_for_col = (_backbone_axis_ext if _is_backbone
|
||||||
else _my_axis_ext)
|
else _my_axis_ext)
|
||||||
|
_col_miter = (_bb_miter if _is_backbone
|
||||||
|
else _std_miter)
|
||||||
|
_col_miter_args = (
|
||||||
|
{"miter_end": _col_miter}
|
||||||
|
if _is_end
|
||||||
|
else {"miter_start": _col_miter})
|
||||||
_my_cols_iter = (_my_info_by_mat[_mat]
|
_my_cols_iter = (_my_info_by_mat[_mat]
|
||||||
if _has_my_cols else [])
|
if _has_my_cols else [])
|
||||||
for (_dl, _dr, _layer_i) in _my_cols_iter:
|
for (_dl, _dr, _layer_i) in _my_cols_iter:
|
||||||
_r = _layer_rect_2d(_ax_for_col, _dl, _dr)
|
_r = _layer_rect_2d(_ax_for_col, _dl, _dr,
|
||||||
|
**_col_miter_args)
|
||||||
if _r is None: continue
|
if _r is None: continue
|
||||||
try:
|
try:
|
||||||
_profile = _r.DuplicateCurve()
|
_profile = _r.DuplicateCurve()
|
||||||
|
|||||||
Reference in New Issue
Block a user