L-Treppe: Schrittmass-Clamp bei Setzen + 3D-Podest Lage-aware

Bisher griff der Schrittmass-Clamp nur bei treppe_art=='gerade'.
Fuer L-Treppen jetzt:
- gp2 (Eck-Klick): clampt erste Lauf-Laenge auf [1-Stufe-min, (n-1)-Stufen-max]
- gp3 (End-Klick): schaetzt N1 aus erster Lauf-Laenge, clampt zweiten Lauf
  auf den S/A-konformen Bereich (mit cut_back kompensiert)

Volume-Fix _make_treppe_l_volume:
- cut_back am Eckpunkt war hardcoded half_b (= passt nur fuer Lage=mid).
  Fuer Lage=links/rechts wird die FULL breite cut-back gebraucht — sonst
  ueberlappen die Lauf-Volumen am Eckpunkt mit dem Podest falsch.
  Fix: cut_back = half_b if mid else breite.
This commit is contained in:
2026-05-28 12:57:37 +02:00
parent d8966cc035
commit 7930705d01
+59 -17
View File
@@ -5887,7 +5887,11 @@ def _make_treppe_l_volume(axis_polyline, breite, referenz, n_stufen, uk, ok,
L1 = v1.Length L1 = v1.Length
L2 = v2.Length L2 = v2.Length
half_b = float(breite) * 0.5 half_b = float(breite) * 0.5
if L1 < half_b + 0.05 or L2 < half_b + 0.05: # Cut-back am Eckpunkt: Lage=mid → half_b (treppe extends ±b/2 perp,
# halbe Ueberlappung pro Lauf). Lage=links/rechts → FULL b (treppe
# extends 0..b auf einer Seite, ganze Ueberlappung pro Lauf).
cut_back = half_b if referenz == "mid" else float(breite)
if L1 < cut_back + 0.05 or L2 < cut_back + 0.05:
print("[ELEMENTE] L-Treppe: Lauflinien zu kurz fuer Podest") print("[ELEMENTE] L-Treppe: Lauflinien zu kurz fuer Podest")
return None return None
@@ -5896,11 +5900,9 @@ def _make_treppe_l_volume(axis_polyline, breite, referenz, n_stufen, uk, ok,
N = max(2, int(n_stufen)) N = max(2, int(n_stufen))
S = H / N S = H / N
# Stufen-Verteilung: N1 wird aus L1 mit dem optimalen A bestimmt, # Stufen-Verteilung: N1 wird aus eff_L1 mit dem optimalen A bestimmt.
# damit die Klick-Position des Users direkt N1 (Stufen vor Podest) eff_L1 = L1 - cut_back
# entspricht — genauso wie's der Live-Preview anzeigt. eff_L2 = L2 - cut_back
eff_L1 = L1 - half_b
eff_L2 = L2 - half_b
if eff_L1 + eff_L2 <= 0: return None if eff_L1 + eff_L2 <= 0: return None
A_opt = 0.63 - 2.0 * S A_opt = 0.63 - 2.0 * S
if A_opt < 0.21: A_opt = 0.21 if A_opt < 0.21: A_opt = 0.21
@@ -5913,15 +5915,15 @@ def _make_treppe_l_volume(axis_polyline, breite, referenz, n_stufen, uk, ok,
v1u = rg.Vector3d(v1); v1u.Unitize() v1u = rg.Vector3d(v1); v1u.Unitize()
v2u = rg.Vector3d(v2); v2u.Unitize() v2u = rg.Vector3d(v2); v2u.Unitize()
# Run 1: von p0 bis p1 - v1u*half_b # Run 1: von p0 bis p1 - v1u*cut_back
run1_end = rg.Point3d(p1.X - v1u.X * half_b, p1.Y - v1u.Y * half_b, 0) run1_end = rg.Point3d(p1.X - v1u.X * cut_back, p1.Y - v1u.Y * cut_back, 0)
line1 = rg.LineCurve(p0, run1_end) line1 = rg.LineCurve(p0, run1_end)
z_podest = float(uk) + N1 * S z_podest = float(uk) + N1 * S
brep1 = _make_treppe_volume(line1, breite, referenz, N1, brep1 = _make_treppe_volume(line1, breite, referenz, N1,
float(uk), z_podest, modus, lauf_d) float(uk), z_podest, modus, lauf_d)
# Run 2: von p1 + v2u*half_b bis p2 # Run 2: von p1 + v2u*cut_back bis p2
run2_start = rg.Point3d(p1.X + v2u.X * half_b, p1.Y + v2u.Y * half_b, 0) run2_start = rg.Point3d(p1.X + v2u.X * cut_back, p1.Y + v2u.Y * cut_back, 0)
line2 = rg.LineCurve(run2_start, p2) line2 = rg.LineCurve(run2_start, p2)
brep2 = _make_treppe_volume(line2, breite, referenz, N2, brep2 = _make_treppe_volume(line2, breite, referenz, N2,
z_podest, float(ok), modus, lauf_d) z_podest, float(ok), modus, lauf_d)
@@ -9595,15 +9597,23 @@ class ElementeBridge(panel_base.BaseBridge):
first_pt, breite, referenz, n_stufen) first_pt, breite, referenz, n_stufen)
if gp2.Get() != GetResult.Point: return if gp2.Get() != GetResult.Point: return
clicked = gp2.Point() clicked = gp2.Point()
if regel_mode == "regel" and treppe_art == "gerade": # Schrittmass-Clamp im "regel"-Modus — fuer gerade Treppen mit
# voller n_stufen. Fuer L-Treppen: lockerer Clamp (1..n-1 Stufen
# in diesem Lauf, da der zweite noch kommt).
if regel_mode == "regel" and treppe_art in ("gerade", "l"):
dx = clicked.X - first_pt.X dx = clicked.X - first_pt.X
dy = clicked.Y - first_pt.Y dy = clicked.Y - first_pt.Y
dist = (dx * dx + dy * dy) ** 0.5 dist = (dx * dx + dy * dy) ** 0.5
if dist < 1e-4: if dist < 1e-4:
print("[ELEMENTE] Keine Richtung gewaehlt"); return print("[ELEMENTE] Keine Richtung gewaehlt"); return
L_min2, L_max2 = _l_range(n_stufen, H) if treppe_art == "gerade":
# Clamp Mauspos-Distanz in die Range (oder reskaliere auf fix L_min2, L_max2 = _l_range(n_stufen, H)
# wenn Range gleich null). else:
# L: clamp auf [1 Stufe × A_min, (n-1) Stufen × A_max]
L1_min, L1_max = _l_range(1, H)
L_minF, L_maxF = _l_range(max(2, n_stufen - 1), H)
L_min2 = L1_min
L_max2 = L_maxF
if abs(L_max2 - L_min2) < 1e-4: if abs(L_max2 - L_min2) < 1e-4:
final_L = L_min2 final_L = L_min2
else: else:
@@ -9620,15 +9630,47 @@ class ElementeBridge(panel_base.BaseBridge):
if treppe_art == "l": if treppe_art == "l":
if referenz == "mid": if referenz == "mid":
referenz = "links" referenz = "links"
# Erste Lauf-Laenge (bereits via cut_back im Volume reduziert,
# hier nur fuer Stufen-Verteilung)
d1x = second_pt.X - first_pt.X
d1y = second_pt.Y - first_pt.Y
L1_cur = (d1x * d1x + d1y * d1y) ** 0.5
gp3 = ric.GetPoint() gp3 = ric.GetPoint()
gp3.SetCommandPrompt( gp3.SetCommandPrompt(
"L-Treppe: Endpunkt nach dem Podest [Stufen={}, Breite={:.2f}, Ref={}]".format( "L-Treppe: Endpunkt nach Eck [Stufen={}, Breite={:.2f}, Ref={}, Modus={}]".format(
n_stufen, breite, referenz)) n_stufen, breite, referenz, regel_mode))
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))
if gp3.Get() != GetResult.Point: return if gp3.Get() != GetResult.Point: return
third_pt = gp3.Point() third_pt_raw = gp3.Point()
# Schrittmass-Clamp fuer Lauf 2: verbleibende Stufen = n - N1_est
# Mit cut_back-Aware Stufen-Schaetzung: N1_est aus eff_L1 = L1 - cut_back
if regel_mode == "regel":
d2x = third_pt_raw.X - second_pt.X
d2y = third_pt_raw.Y - second_pt.Y
d2 = (d2x * d2x + d2y * d2y) ** 0.5
if d2 < 1e-4:
print("[ELEMENTE] Keine Richtung gewaehlt"); return
S_cur = H / max(2, int(n_stufen))
cb = (breite * 0.5) if referenz == "mid" else float(breite)
eff_L1 = max(0.01, L1_cur - cb)
A_opt = max(0.21, min(0.35, 0.63 - 2.0 * S_cur))
N1_est = max(1, min(n_stufen - 1, int(round(eff_L1 / A_opt))))
N2_est = max(1, n_stufen - N1_est)
# Range fuer Lauf 2 (mit cut_back-Anteil dazu)
L2_min, L2_max = _l_range(N2_est, H * N2_est / n_stufen)
L2_min += cb
L2_max += cb
if abs(L2_max - L2_min) < 1e-4:
final_L2 = L2_min
else:
final_L2 = max(L2_min, min(L2_max, d2))
third_pt = rg.Point3d(second_pt.X + d2x / d2 * final_L2,
second_pt.Y + d2y / d2 * final_L2,
second_pt.Z)
else:
third_pt = third_pt_raw
p_first = rg.Point3d(first_pt.X, first_pt.Y, 0) p_first = rg.Point3d(first_pt.X, first_pt.Y, 0)
p_corner = rg.Point3d(second_pt.X, second_pt.Y, 0) p_corner = rg.Point3d(second_pt.X, second_pt.Y, 0)
p_end = rg.Point3d(third_pt.X, third_pt.Y, 0) p_end = rg.Point3d(third_pt.X, third_pt.Y, 0)