diff --git a/rhino/elemente.py b/rhino/elemente.py index ee220c6..cad7d50 100644 --- a/rhino/elemente.py +++ b/rhino/elemente.py @@ -5887,7 +5887,11 @@ def _make_treppe_l_volume(axis_polyline, breite, referenz, n_stufen, uk, ok, L1 = v1.Length L2 = v2.Length 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") 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)) S = H / N - # Stufen-Verteilung: N1 wird aus L1 mit dem optimalen A bestimmt, - # damit die Klick-Position des Users direkt N1 (Stufen vor Podest) - # entspricht — genauso wie's der Live-Preview anzeigt. - eff_L1 = L1 - half_b - eff_L2 = L2 - half_b + # Stufen-Verteilung: N1 wird aus eff_L1 mit dem optimalen A bestimmt. + eff_L1 = L1 - cut_back + eff_L2 = L2 - cut_back if eff_L1 + eff_L2 <= 0: return None A_opt = 0.63 - 2.0 * S 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() v2u = rg.Vector3d(v2); v2u.Unitize() - # Run 1: von p0 bis p1 - v1u*half_b - run1_end = rg.Point3d(p1.X - v1u.X * half_b, p1.Y - v1u.Y * half_b, 0) + # Run 1: von p0 bis p1 - v1u*cut_back + run1_end = rg.Point3d(p1.X - v1u.X * cut_back, p1.Y - v1u.Y * cut_back, 0) line1 = rg.LineCurve(p0, run1_end) z_podest = float(uk) + N1 * S brep1 = _make_treppe_volume(line1, breite, referenz, N1, float(uk), z_podest, modus, lauf_d) - # Run 2: von p1 + v2u*half_b bis p2 - run2_start = rg.Point3d(p1.X + v2u.X * half_b, p1.Y + v2u.Y * half_b, 0) + # Run 2: von p1 + v2u*cut_back bis p2 + run2_start = rg.Point3d(p1.X + v2u.X * cut_back, p1.Y + v2u.Y * cut_back, 0) line2 = rg.LineCurve(run2_start, p2) brep2 = _make_treppe_volume(line2, breite, referenz, N2, z_podest, float(ok), modus, lauf_d) @@ -9595,15 +9597,23 @@ class ElementeBridge(panel_base.BaseBridge): first_pt, breite, referenz, n_stufen) if gp2.Get() != GetResult.Point: return 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 dy = clicked.Y - first_pt.Y dist = (dx * dx + dy * dy) ** 0.5 if dist < 1e-4: print("[ELEMENTE] Keine Richtung gewaehlt"); return - L_min2, L_max2 = _l_range(n_stufen, H) - # Clamp Mauspos-Distanz in die Range (oder reskaliere auf fix - # wenn Range gleich null). + if treppe_art == "gerade": + L_min2, L_max2 = _l_range(n_stufen, H) + 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: final_L = L_min2 else: @@ -9620,15 +9630,47 @@ class ElementeBridge(panel_base.BaseBridge): if treppe_art == "l": if referenz == "mid": 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.SetCommandPrompt( - "L-Treppe: Endpunkt nach dem Podest [Stufen={}, Breite={:.2f}, Ref={}]".format( - n_stufen, breite, referenz)) + "L-Treppe: Endpunkt nach Eck [Stufen={}, Breite={:.2f}, Ref={}, Modus={}]".format( + n_stufen, breite, referenz, regel_mode)) gp3.SetBasePoint(second_pt, True) gp3.DynamicDraw += _make_treppe_preview_handler( second_pt, breite, referenz, max(1, n_stufen // 2)) 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_corner = rg.Point3d(second_pt.X, second_pt.Y, 0) p_end = rg.Point3d(third_pt.X, third_pt.Y, 0)