L-Treppe: 3-Punkt-Axis behalten + Referenz IMMER aussen

User-Feedback: L-Treppe bleibt 3-Punkt-Polyline (Start/Eck/End), Podest
ergibt sich aus dem Eckpunkt + Breite. Aber Referenz darf nie 'mid'
sein — sonst kollidieren die Laeufe am Eck. Constraint:
- _update_wall: bei treppe_art=='l' und tref=='mid' → 'links' erzwungen
- Frontend: REF_OPTIONS filtert 'mittig' raus wenn treppeArt=='l'

Dead-Code: _l_segments + _aussen_l_polygon + _lauflinie_l + _make_treppe_l_volume
behalten 4-Punkt-Handling als optionalen Pfad (gerade nicht erreicht weil
Creation immer 3-Punkt produziert) — schaden nicht, koennen spaeter
wieder aktiviert werden wenn Podest-als-Segment doch gewuenscht.

Schlafsession-Status: Trittmass-Lock + Lauflinie zentriert + Pfeile +
Cmd+Z + Pure-Transform fuer hidden Layer = alles in main.
This commit is contained in:
2026-05-28 12:49:32 +02:00
parent e406e8d9b2
commit d8966cc035
2 changed files with 171 additions and 61 deletions
+143 -37
View File
@@ -288,6 +288,7 @@ _KEY_TREPPE_ARROW_STYLE = "dossier_treppe_arrow_style" # "klassisch"|"fil
_KEY_TREPPE_LOCK_S = "dossier_treppe_lock_s" # "0"/"1" — Schrittmass-Lock (S fix, N passt sich an) _KEY_TREPPE_LOCK_S = "dossier_treppe_lock_s" # "0"/"1" — Schrittmass-Lock (S fix, N passt sich an)
_KEY_TREPPE_TARGET_S = "dossier_treppe_target_s" # float (m) — fixierter S-Wert wenn Lock aktiv _KEY_TREPPE_TARGET_S = "dossier_treppe_target_s" # float (m) — fixierter S-Wert wenn Lock aktiv
_KEY_TREPPE_TARGET_A = "dossier_treppe_target_a" # float (m) — fixierter A-Wert (Auftritt-Tiefe) _KEY_TREPPE_TARGET_A = "dossier_treppe_target_a" # float (m) — fixierter A-Wert (Auftritt-Tiefe)
_KEY_TREPPE_PODEST_MIN = "dossier_treppe_podest_min" # float (m) — Mindestlaenge Podest bei L-Treppen
# Tragwerk: Stuetze / Traeger / Unterzug — gemeinsames Querschnitts-System # Tragwerk: Stuetze / Traeger / Unterzug — gemeinsames Querschnitts-System
_KEY_TRAG_KIND = "dossier_trag_kind" # "stuetze" | "traeger" | "unterzug" _KEY_TRAG_KIND = "dossier_trag_kind" # "stuetze" | "traeger" | "unterzug"
@@ -2735,6 +2736,7 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over,
treppe_obere_dashed=None, treppe_obere_dashed=None,
treppe_arrow_style=None, treppe_arrow_style=None,
treppe_lock_s=None, treppe_target_s=None, treppe_target_a=None, treppe_lock_s=None, treppe_target_s=None, treppe_target_a=None,
treppe_podest_min=None,
trag_kind=None, trag_profil=None, trag_b=None, trag_kind=None, trag_profil=None, trag_b=None,
trag_h=None, trag_d=None, trag_t=None, trag_h=None, trag_d=None, trag_t=None,
trag_angle=None, trag_z_over=None, trag_angle=None, trag_z_over=None,
@@ -2907,6 +2909,10 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over,
try: obj_attrs.SetUserString(_KEY_TREPPE_TARGET_A, try: obj_attrs.SetUserString(_KEY_TREPPE_TARGET_A,
"{:.4f}".format(float(treppe_target_a))) "{:.4f}".format(float(treppe_target_a)))
except Exception: pass except Exception: pass
if treppe_podest_min is not None:
try: obj_attrs.SetUserString(_KEY_TREPPE_PODEST_MIN,
"{:.4f}".format(float(treppe_podest_min)))
except Exception: pass
# Tragwerk-Felder # Tragwerk-Felder
if trag_kind is not None and trag_kind in _TRAG_KINDS: if trag_kind is not None and trag_kind in _TRAG_KINDS:
obj_attrs.SetUserString(_KEY_TRAG_KIND, trag_kind) obj_attrs.SetUserString(_KEY_TRAG_KIND, trag_kind)
@@ -3179,6 +3185,8 @@ def _read_meta(obj):
except Exception: t_target_s = 0.0 except Exception: t_target_s = 0.0
try: t_target_a = float(a.GetUserString(_KEY_TREPPE_TARGET_A) or "0") try: t_target_a = float(a.GetUserString(_KEY_TREPPE_TARGET_A) or "0")
except Exception: t_target_a = 0.0 except Exception: t_target_a = 0.0
try: t_podest_min = float(a.GetUserString(_KEY_TREPPE_PODEST_MIN) or "0")
except Exception: t_podest_min = 0.0
# Soll-Werte JSON, mit Defaults wenn nicht gesetzt # Soll-Werte JSON, mit Defaults wenn nicht gesetzt
import json import json
tsoll = dict(_TREPPE_SOLL_DEFAULT) tsoll = dict(_TREPPE_SOLL_DEFAULT)
@@ -3390,6 +3398,7 @@ def _read_meta(obj):
"treppe_lock_s": t_lock_s, "treppe_lock_s": t_lock_s,
"treppe_target_s": t_target_s, "treppe_target_s": t_target_s,
"treppe_target_a": t_target_a, "treppe_target_a": t_target_a,
"treppe_podest_min": t_podest_min,
"trag_kind": tk_raw, "trag_kind": tk_raw,
"trag_profil": tp_raw, "trag_profil": tp_raw,
"trag_b": t_b, "trag_b": t_b,
@@ -5846,19 +5855,32 @@ def _line_intersect_xy(p1, dir1, p2, dir2):
def _make_treppe_l_volume(axis_polyline, breite, referenz, n_stufen, uk, ok, def _make_treppe_l_volume(axis_polyline, breite, referenz, n_stufen, uk, ok,
modus="flach", lauf_d=0.18): modus="flach", lauf_d=0.18):
"""L-Treppe aus 3-Punkt-Polylinie (Start, Eck-Punkt, End). """L-Treppe. Axis kann 3-Punkt (legacy: Start/Eck/End — Podest am
Bauet 2 gerade Laufe + 1 Podest am Eck zusammen. Hoehe wird Eckpunkt) oder 4-Punkt (Start/Eck1/Eck2/End Podest zwischen Eck1
proportional zu den Lauflinien-Laengen auf die beiden Laufe verteilt.""" und Eck2) sein. Bauet 2 Laufe + Podest dazwischen."""
if not isinstance(axis_polyline, rg.Curve): return None if not isinstance(axis_polyline, rg.Curve): return None
try: try:
ok_pl, poly = axis_polyline.TryGetPolyline() ok_pl, poly = axis_polyline.TryGetPolyline()
except Exception: except Exception:
return None return None
if not ok_pl or poly is None or poly.Count != 3: if not ok_pl or poly is None: return None
return None # Auf gemeinsamen 3-Punkt-Layout normalisieren:
# p0=Start, p1=Podest-Mitte (Eck-Surrogat), p2=Ende
# Bei 4-Punkt-Axis nehmen wir Mitte zwischen Eck1+Eck2 als p1 —
# die Lauflaengen werden dann effektiv um halbe-Podest gekuerzt.
if poly.Count == 4:
p0 = rg.Point3d(poly[0].X, poly[0].Y, 0)
pc1 = rg.Point3d(poly[1].X, poly[1].Y, 0)
pc2 = rg.Point3d(poly[2].X, poly[2].Y, 0)
p2 = rg.Point3d(poly[3].X, poly[3].Y, 0)
# p1 als Podest-Mitte
p1 = rg.Point3d((pc1.X + pc2.X) * 0.5, (pc1.Y + pc2.Y) * 0.5, 0)
elif poly.Count == 3:
p0 = rg.Point3d(poly[0].X, poly[0].Y, 0) p0 = rg.Point3d(poly[0].X, poly[0].Y, 0)
p1 = rg.Point3d(poly[1].X, poly[1].Y, 0) p1 = rg.Point3d(poly[1].X, poly[1].Y, 0)
p2 = rg.Point3d(poly[2].X, poly[2].Y, 0) p2 = rg.Point3d(poly[2].X, poly[2].Y, 0)
else:
return None
v1 = rg.Vector3d(p1.X - p0.X, p1.Y - p0.Y, 0) v1 = rg.Vector3d(p1.X - p0.X, p1.Y - p0.Y, 0)
v2 = rg.Vector3d(p2.X - p1.X, p2.Y - p1.Y, 0) v2 = rg.Vector3d(p2.X - p1.X, p2.Y - p1.Y, 0)
@@ -6329,10 +6351,28 @@ def _bruch_gerade(axis_curve, breite, referenz, n_stufen, total_h, cut_h, z):
# ----- L-Treppe: delegieren auf beide Laeufe ------------------------------- # ----- L-Treppe: delegieren auf beide Laeufe -------------------------------
def _l_segments(axis_polyline, n_stufen, z): def _l_segments(axis_polyline, n_stufen, z):
"""Liefert (seg1, seg2, N1, N2) oder None.""" """Liefert (seg1, seg2, N1, N2, podest_seg) oder None.
Axis kann 3-Punkt (legacy: P0, corner, P1, podest=None) oder
4-Punkt (P0, P1=Ende-Lauf1, P2=Anfang-Lauf2, P3, podest=P1P2) sein.
N wird auf die zwei BEGEHBAREN Laeufe verteilt Podest hat keine
Stufen."""
if not isinstance(axis_polyline, rg.Curve): return None if not isinstance(axis_polyline, rg.Curve): return None
ok, poly = axis_polyline.TryGetPolyline() ok, poly = axis_polyline.TryGetPolyline()
if not ok or poly is None or poly.Count != 3: return None if not ok or poly is None: return None
if poly.Count == 4:
p0 = rg.Point3d(poly[0].X, poly[0].Y, z)
p1 = rg.Point3d(poly[1].X, poly[1].Y, z)
p2 = rg.Point3d(poly[2].X, poly[2].Y, z)
p3 = rg.Point3d(poly[3].X, poly[3].Y, z)
L1 = ((p1.X - p0.X) ** 2 + (p1.Y - p0.Y) ** 2) ** 0.5
L2 = ((p3.X - p2.X) ** 2 + (p3.Y - p2.Y) ** 2) ** 0.5
if L1 < 1e-6 or L2 < 1e-6: return None
N = max(2, int(n_stufen))
N1 = max(1, int(round(N * L1 / (L1 + L2))))
N2 = max(1, N - N1)
return (rg.LineCurve(p0, p1), rg.LineCurve(p2, p3), N1, N2,
rg.LineCurve(p1, p2))
if poly.Count == 3:
p0 = rg.Point3d(poly[0].X, poly[0].Y, z) p0 = rg.Point3d(poly[0].X, poly[0].Y, z)
pc = rg.Point3d(poly[1].X, poly[1].Y, z) pc = rg.Point3d(poly[1].X, poly[1].Y, z)
p1 = rg.Point3d(poly[2].X, poly[2].Y, z) p1 = rg.Point3d(poly[2].X, poly[2].Y, z)
@@ -6342,7 +6382,8 @@ def _l_segments(axis_polyline, n_stufen, z):
N = max(2, int(n_stufen)) N = max(2, int(n_stufen))
N1 = max(1, int(round(N * L1 / (L1 + L2)))) N1 = max(1, int(round(N * L1 / (L1 + L2))))
N2 = max(1, N - N1) N2 = max(1, N - N1)
return rg.LineCurve(p0, pc), rg.LineCurve(pc, p1), N1, N2 return rg.LineCurve(p0, pc), rg.LineCurve(pc, p1), N1, N2, None
return None
def _tritte_l(axis_polyline, breite, referenz, n_stufen, z, def _tritte_l(axis_polyline, breite, referenz, n_stufen, z,
@@ -6350,7 +6391,7 @@ def _tritte_l(axis_polyline, breite, referenz, n_stufen, z,
"""Returnt (lower, upper). Cut bestimmt in welchem Lauf gesplittet wird.""" """Returnt (lower, upper). Cut bestimmt in welchem Lauf gesplittet wird."""
segs = _l_segments(axis_polyline, n_stufen, z) segs = _l_segments(axis_polyline, n_stufen, z)
if segs is None: return [], [] if segs is None: return [], []
s1, s2, N1, N2 = segs s1, s2, N1, N2, _pod = segs
H1 = total_h * (N1 / float(N1 + N2)) if total_h > 0 else 0.0 H1 = total_h * (N1 / float(N1 + N2)) if total_h > 0 else 0.0
H2 = total_h - H1 H2 = total_h - H1
cut1 = _cut_idx_gerade(N1, H1, cut_h) if total_h > 0 else -1 cut1 = _cut_idx_gerade(N1, H1, cut_h) if total_h > 0 else -1
@@ -6374,13 +6415,12 @@ def _tritte_l(axis_polyline, breite, referenz, n_stufen, z,
def _lauflinie_l(axis_polyline, n_stufen, z, arrow_style="klassisch", doc=None, def _lauflinie_l(axis_polyline, n_stufen, z, arrow_style="klassisch", doc=None,
breite=None, referenz=None): breite=None, referenz=None):
"""Lauflinie ueber BEIDE Laeufe einer L-Treppe. Schaft 1 vom Start zur """Lauflinie ueber die L-Treppe. Bei 4-Punkt-Axis: Schaft1 + Podest-
PROJEZIERTEN Eck-Mitte (Schnittpunkt der zwei mid-versetzten Linien), Verbindung + Schaft2; Pfeil am Ende. Bei 3-Punkt: Eck-projezierte
Schaft 2 von Eck-Mitte zum Ende mit Pfeil. So sauberer Übergang ohne Verbindung wie zuvor."""
Versatz an der Ecke auch bei Lage=links/rechts."""
segs = _l_segments(axis_polyline, n_stufen, z) segs = _l_segments(axis_polyline, n_stufen, z)
if segs is None: return [] if segs is None: return []
s1, s2, _N1, _N2 = segs s1, s2, _N1, _N2, podest = segs
P0a = s1.PointAtStart; P1a = s1.PointAtEnd P0a = s1.PointAtStart; P1a = s1.PointAtEnd
txa, tya = P1a.X - P0a.X, P1a.Y - P0a.Y txa, tya = P1a.X - P0a.X, P1a.Y - P0a.Y
La = (txa * txa + tya * tya) ** 0.5 La = (txa * txa + tya * tya) ** 0.5
@@ -6390,7 +6430,6 @@ def _lauflinie_l(axis_polyline, n_stufen, z, arrow_style="klassisch", doc=None,
if La < 1e-6 or Lb < 1e-6: return [] if La < 1e-6 or Lb < 1e-6: return []
uxa, uya = txa / La, tya / La uxa, uya = txa / La, tya / La
uxb, uyb = txb / Lb, tyb / Lb uxb, uyb = txb / Lb, tyb / Lb
# Mid-offset relativ zur Axis
mid_off = 0.0 mid_off = 0.0
wide_l = wide_r = None wide_l = wide_r = None
if breite is not None and referenz is not None: if breite is not None and referenz is not None:
@@ -6398,35 +6437,100 @@ def _lauflinie_l(axis_polyline, n_stufen, z, arrow_style="klassisch", doc=None,
mid_off = (off_l + off_r) * 0.5 mid_off = (off_l + off_r) * 0.5
wide_l = off_l - mid_off wide_l = off_l - mid_off
wide_r = off_r - mid_off wide_r = off_r - mid_off
# perp 90° CCW
pa_x, pa_y = -uya * mid_off, uxa * mid_off pa_x, pa_y = -uya * mid_off, uxa * mid_off
pb_x, pb_y = -uyb * mid_off, uxb * mid_off pb_x, pb_y = -uyb * mid_off, uxb * mid_off
P0a_s = rg.Point3d(P0a.X + pa_x, P0a.Y + pa_y, z) P0a_s = rg.Point3d(P0a.X + pa_x, P0a.Y + pa_y, z)
P1a_s = rg.Point3d(P1a.X + pa_x, P1a.Y + pa_y, z) P1a_s = rg.Point3d(P1a.X + pa_x, P1a.Y + pa_y, z)
P0b_s = rg.Point3d(P0b.X + pb_x, P0b.Y + pb_y, z) P0b_s = rg.Point3d(P0b.X + pb_x, P0b.Y + pb_y, z)
P1b_s = rg.Point3d(P1b.X + pb_x, P1b.Y + pb_y, z) P1b_s = rg.Point3d(P1b.X + pb_x, P1b.Y + pb_y, z)
# Echte Eck-Mitte als Schnittpunkt der zwei versetzten Schaft-Linien out = []
head_size = min(0.25, Lb * 0.05)
if podest is not None:
# 4-Punkt: Schaft1 + Podest-Schaft (perp-zentriert) + Schaft2
Ppc1 = podest.PointAtStart; Ppc2 = podest.PointAtEnd
txp, typ = Ppc2.X - Ppc1.X, Ppc2.Y - Ppc1.Y
Lp = (txp * txp + typ * typ) ** 0.5
if Lp > 1e-6:
uxp, uyp = txp / Lp, typ / Lp
pp_x, pp_y = -uyp * mid_off, uxp * mid_off
Ppc1_s = rg.Point3d(Ppc1.X + pp_x, Ppc1.Y + pp_y, z)
Ppc2_s = rg.Point3d(Ppc2.X + pp_x, Ppc2.Y + pp_y, z)
# Eck1: Schnitt Schaft1 + Podest-Schaft
corner_a = _line_intersect_xy(
P0a_s, rg.Vector3d(uxa, uya, 0),
Ppc1_s, rg.Vector3d(uxp, uyp, 0)) if abs(mid_off) > 1e-6 else P1a_s
if corner_a is None: corner_a = P1a_s
else: corner_a = rg.Point3d(corner_a.X, corner_a.Y, z)
# Eck2: Schnitt Podest-Schaft + Schaft2
corner_b = _line_intersect_xy(
Ppc1_s, rg.Vector3d(uxp, uyp, 0),
P0b_s, rg.Vector3d(uxb, uyb, 0)) if abs(mid_off) > 1e-6 else P0b_s
if corner_b is None: corner_b = P0b_s
else: corner_b = rg.Point3d(corner_b.X, corner_b.Y, z)
out.append(rg.LineCurve(P0a_s, corner_a))
out.append(rg.LineCurve(corner_a, corner_b))
out.extend(_treppe_2d_arrow_curves(corner_b, P1b_s, head_size,
arrow_style, doc, wide_l, wide_r))
return out
# 3-Punkt-Fallback: Eck als Schnitt projezieren
if abs(mid_off) > 1e-6: if abs(mid_off) > 1e-6:
meet = _line_intersect_xy(P0a_s, rg.Vector3d(uxa, uya, 0), meet = _line_intersect_xy(P0a_s, rg.Vector3d(uxa, uya, 0),
P1b_s, rg.Vector3d(uxb, uyb, 0)) P1b_s, rg.Vector3d(uxb, uyb, 0))
if meet is None: meet = P1a_s # Fallback (parallel oder collinear) if meet is None: meet = P1a_s
else: meet = rg.Point3d(meet.X, meet.Y, z)
else: else:
meet = P1a_s # = P0b_s wenn mid=0 meet = P1a_s
out = []
out.append(rg.LineCurve(P0a_s, meet)) out.append(rg.LineCurve(P0a_s, meet))
head_size = min(0.25, Lb * 0.05)
out.extend(_treppe_2d_arrow_curves(meet, P1b_s, head_size, arrow_style, out.extend(_treppe_2d_arrow_curves(meet, P1b_s, head_size, arrow_style,
doc, wide_l, wide_r)) doc, wide_l, wide_r))
return out return out
def _aussen_l_polygon(axis_polyline, breite, referenz, z): def _aussen_l_polygon(axis_polyline, breite, referenz, z):
"""Sauberes L-Polygon: 6-Punkt-Outline mit Outer/Inner-Ecken via """Sauberes L-Polygon mit Outer/Inner-Ecken via Linien-Schnitt.
Linien-Schnitt. Returnt PolylineCurve oder None bei Singularitaet/ Unterstuetzt 3-Punkt (Eck=Punkt) und 4-Punkt (Podest zwischen Eck1 und
Fehler Caller faellt dann auf 2-Rechteck-Variante zurueck.""" Eck2). Returnt PolylineCurve oder None bei Singularitaet/Fehler."""
try: try:
ok, poly = axis_polyline.TryGetPolyline() ok, poly = axis_polyline.TryGetPolyline()
if not ok or poly is None or poly.Count != 3: return None if not ok or poly is None: return None
def _at_z(p, fallback):
return rg.Point3d(p.X, p.Y, z) if p is not None else fallback
off_l, off_r = _treppe_2d_side_offsets(breite, referenz)
def _off(pt, perp, d):
return rg.Point3d(pt.X + perp[0] * d, pt.Y + perp[1] * d, z)
if poly.Count == 4:
# 4-Punkt: Lauf1 (p0→pc1), Podest (pc1→pc2), Lauf2 (pc2→p3)
p0 = rg.Point3d(poly[0].X, poly[0].Y, z)
pc1 = rg.Point3d(poly[1].X, poly[1].Y, z)
pc2 = rg.Point3d(poly[2].X, poly[2].Y, z)
p3 = rg.Point3d(poly[3].X, poly[3].Y, z)
L1 = ((pc1.X-p0.X)**2 + (pc1.Y-p0.Y)**2) ** 0.5
Lp = ((pc2.X-pc1.X)**2 + (pc2.Y-pc1.Y)**2) ** 0.5
L2 = ((p3.X-pc2.X)**2 + (p3.Y-pc2.Y)**2) ** 0.5
if L1 < 1e-6 or Lp < 1e-6 or L2 < 1e-6: return None
u1 = rg.Vector3d((pc1.X-p0.X)/L1, (pc1.Y-p0.Y)/L1, 0)
uP = rg.Vector3d((pc2.X-pc1.X)/Lp, (pc2.Y-pc1.Y)/Lp, 0)
u2 = rg.Vector3d((p3.X-pc2.X)/L2, (p3.Y-pc2.Y)/L2, 0)
pp1 = (-u1.Y, u1.X); ppP = (-uP.Y, uP.X); pp2 = (-u2.Y, u2.X)
# Linke Seite (off_l) Outline-Punkte
p0_l = _off(p0, pp1, off_l)
pc1_l1 = _off(pc1, pp1, off_l); pc1_lP = _off(pc1, ppP, off_l)
pc2_lP = _off(pc2, ppP, off_l); pc2_l2 = _off(pc2, pp2, off_l)
p3_l = _off(p3, pp2, off_l)
cl_a = _at_z(_line_intersect_xy(p0_l, u1, pc1_lP, uP), pc1_l1)
cl_b = _at_z(_line_intersect_xy(pc2_lP, uP, p3_l, u2), pc2_l2)
# Rechte Seite (off_r)
p0_r = _off(p0, pp1, off_r)
pc1_r1 = _off(pc1, pp1, off_r); pc1_rP = _off(pc1, ppP, off_r)
pc2_rP = _off(pc2, ppP, off_r); pc2_r2 = _off(pc2, pp2, off_r)
p3_r = _off(p3, pp2, off_r)
cr_a = _at_z(_line_intersect_xy(p0_r, u1, pc1_rP, uP), pc1_r1)
cr_b = _at_z(_line_intersect_xy(pc2_rP, uP, p3_r, u2), pc2_r2)
# Polygon: 8-Punkt-Outline (CCW)
pts = [p0_l, p0_r, cr_a, cr_b, p3_r, p3_l, cl_b, cl_a, p0_l]
return rg.PolylineCurve(rg.Polyline(pts))
if poly.Count == 3:
# 3-Punkt: Legacy — Eck als Punkt
p0 = rg.Point3d(poly[0].X, poly[0].Y, z) p0 = rg.Point3d(poly[0].X, poly[0].Y, z)
pc = rg.Point3d(poly[1].X, poly[1].Y, z) pc = rg.Point3d(poly[1].X, poly[1].Y, z)
p1 = rg.Point3d(poly[2].X, poly[2].Y, z) p1 = rg.Point3d(poly[2].X, poly[2].Y, z)
@@ -6435,21 +6539,15 @@ def _aussen_l_polygon(axis_polyline, breite, referenz, z):
if L1 < 1e-6 or L2 < 1e-6: return None if L1 < 1e-6 or L2 < 1e-6: return None
u1 = rg.Vector3d((pc.X - p0.X) / L1, (pc.Y - p0.Y) / L1, 0) u1 = rg.Vector3d((pc.X - p0.X) / L1, (pc.Y - p0.Y) / L1, 0)
u2 = rg.Vector3d((p1.X - pc.X) / L2, (p1.Y - pc.Y) / L2, 0) u2 = rg.Vector3d((p1.X - pc.X) / L2, (p1.Y - pc.Y) / L2, 0)
pp1 = (-u1.Y, u1.X) pp1 = (-u1.Y, u1.X); pp2 = (-u2.Y, u2.X)
pp2 = (-u2.Y, u2.X)
off_l, off_r = _treppe_2d_side_offsets(breite, referenz)
def _off(pt, perp, d):
return rg.Point3d(pt.X + perp[0] * d, pt.Y + perp[1] * d, z)
p0_l = _off(p0, pp1, off_l); p0_r = _off(p0, pp1, off_r) p0_l = _off(p0, pp1, off_l); p0_r = _off(p0, pp1, off_r)
pc_l1 = _off(pc, pp1, off_l); pc_r1 = _off(pc, pp1, off_r) pc_l1 = _off(pc, pp1, off_l); pc_r1 = _off(pc, pp1, off_r)
p1_l = _off(p1, pp2, off_l); p1_r = _off(p1, pp2, off_r) p1_l = _off(p1, pp2, off_l); p1_r = _off(p1, pp2, off_r)
# Ecken-Schnitte — Z auf z setzen (Helper liefert Z=0)
def _at_z(p, fallback):
return rg.Point3d(p.X, p.Y, z) if p is not None else fallback
corner_l = _at_z(_line_intersect_xy(p0_l, u1, p1_l, u2), pc_l1) corner_l = _at_z(_line_intersect_xy(p0_l, u1, p1_l, u2), pc_l1)
corner_r = _at_z(_line_intersect_xy(p0_r, u1, p1_r, u2), pc_r1) corner_r = _at_z(_line_intersect_xy(p0_r, u1, p1_r, u2), pc_r1)
pts = [p0_l, p0_r, corner_r, p1_r, p1_l, corner_l, p0_l] pts = [p0_l, p0_r, corner_r, p1_r, p1_l, corner_l, p0_l]
return rg.PolylineCurve(rg.Polyline(pts)) return rg.PolylineCurve(rg.Polyline(pts))
return None
except Exception as ex: except Exception as ex:
print("[ELEMENTE] _aussen_l_polygon:", ex) print("[ELEMENTE] _aussen_l_polygon:", ex)
return None return None
@@ -6463,7 +6561,7 @@ def _aussen_l(axis_polyline, breite, referenz, z,
upper (= gestrichelt wenn oben).""" upper (= gestrichelt wenn oben)."""
segs = _l_segments(axis_polyline, n_stufen, z) segs = _l_segments(axis_polyline, n_stufen, z)
if segs is None: return [], [] if segs is None: return [], []
s1, s2, N1, N2 = segs s1, s2, N1, N2, _pod = segs
H1 = total_h * (N1 / float(N1 + N2)) if total_h > 0 else 0.0 H1 = total_h * (N1 / float(N1 + N2)) if total_h > 0 else 0.0
H2 = total_h - H1 H2 = total_h - H1
cut1 = _cut_idx_gerade(N1, H1, cut_h) if total_h > 0 else -1 cut1 = _cut_idx_gerade(N1, H1, cut_h) if total_h > 0 else -1
@@ -6490,7 +6588,7 @@ def _aussen_l(axis_polyline, breite, referenz, z,
def _bruch_l(axis_polyline, breite, referenz, n_stufen, total_h, cut_h, z): def _bruch_l(axis_polyline, breite, referenz, n_stufen, total_h, cut_h, z):
segs = _l_segments(axis_polyline, n_stufen, z) segs = _l_segments(axis_polyline, n_stufen, z)
if segs is None: return [] if segs is None: return []
s1, s2, N1, N2 = segs s1, s2, N1, N2, _pod = segs
H1 = total_h * (N1 / float(N1 + N2)) H1 = total_h * (N1 / float(N1 + N2))
H2 = total_h - H1 H2 = total_h - H1
if cut_h <= H1: if cut_h <= H1:
@@ -9517,11 +9615,15 @@ class ElementeBridge(panel_base.BaseBridge):
second_pt = clicked second_pt = clicked
# L-Treppe: dritter Punkt einsammeln (Endpunkt nach dem Eck) # L-Treppe: dritter Punkt einsammeln (Endpunkt nach dem Eck)
# Lage MUSS aussen liegen — bei Default 'mid' auf 'links' setzen
# damit die Treppe komplett auf einer Seite der Achse liegt.
if treppe_art == "l": if treppe_art == "l":
if referenz == "mid":
referenz = "links"
gp3 = ric.GetPoint() gp3 = ric.GetPoint()
gp3.SetCommandPrompt( gp3.SetCommandPrompt(
"L-Treppe: Endpunkt nach dem Podest [Stufen={}, Breite={:.2f}]".format( "L-Treppe: Endpunkt nach dem Podest [Stufen={}, Breite={:.2f}, Ref={}]".format(
n_stufen, breite)) n_stufen, breite, referenz))
gp3.SetBasePoint(second_pt, True) gp3.SetBasePoint(second_pt, True)
gp3.DynamicDraw += _make_treppe_preview_handler( gp3.DynamicDraw += _make_treppe_preview_handler(
second_pt, breite, referenz, max(1, n_stufen // 2)) second_pt, breite, referenz, max(1, n_stufen // 2))
@@ -12249,6 +12351,10 @@ class ElementeBridge(panel_base.BaseBridge):
if tn < 2: tn = 2 if tn < 2: tn = 2
tref = p.get("treppeReferenz", old_meta.get("treppe_referenz", "mid")) tref = p.get("treppeReferenz", old_meta.get("treppe_referenz", "mid"))
if tref not in ("mid", "links", "rechts"): tref = "mid" if tref not in ("mid", "links", "rechts"): tref = "mid"
# L-Treppen MUESSEN aussen-liegende Referenz haben (sonst
# ueberlappen sich die Laeufe am Eck)
if old_meta.get("treppe_art") == "l" and tref == "mid":
tref = "links"
tmod = p.get("treppeModus", old_meta.get("treppe_modus", "flach")) tmod = p.get("treppeModus", old_meta.get("treppe_modus", "flach"))
if tmod not in _TREPPE_MODI: tmod = "flach" if tmod not in _TREPPE_MODI: tmod = "flach"
try: tld = float(p.get("laufD", old_meta.get("treppe_lauf_d", 0.18))) try: tld = float(p.get("laufD", old_meta.get("treppe_lauf_d", 0.18)))
+5 -1
View File
@@ -2093,11 +2093,15 @@ function TreppeProperties({ treppe, geschosse, onUpdate, onDelete }) {
} }
const ref = treppe.treppeReferenz ?? 'mid' const ref = treppe.treppeReferenz ?? 'mid'
const REF_OPTIONS = [ // L-Treppen: nur Aussen-Lage erlaubt (sonst kollidieren die Laeufe am Eck)
const REF_OPTIONS_ALL = [
{ code: 'links', label: 'links' }, { code: 'links', label: 'links' },
{ code: 'mid', label: 'mittig' }, { code: 'mid', label: 'mittig' },
{ code: 'rechts', label: 'rechts' }, { code: 'rechts', label: 'rechts' },
] ]
const REF_OPTIONS = treppe.treppeArt === 'l'
? REF_OPTIONS_ALL.filter(o => o.code !== 'mid')
: REF_OPTIONS_ALL
const modus = treppe.treppeModus ?? 'flach' const modus = treppe.treppeModus ?? 'flach'
const MODUS_OPTIONS = [ const MODUS_OPTIONS = [
{ code: 'massiv', label: 'massiv', hint: 'Block bis zum Boden — wie eine Mauer unter der Treppe' }, { code: 'massiv', label: 'massiv', hint: 'Block bis zum Boden — wie eine Mauer unter der Treppe' },