diff --git a/rhino/elemente.py b/rhino/elemente.py index b03684a..ee220c6 100644 --- a/rhino/elemente.py +++ b/rhino/elemente.py @@ -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_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_PODEST_MIN = "dossier_treppe_podest_min" # float (m) — Mindestlaenge Podest bei L-Treppen # Tragwerk: Stuetze / Traeger / Unterzug — gemeinsames Querschnitts-System _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_arrow_style=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_h=None, trag_d=None, trag_t=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, "{:.4f}".format(float(treppe_target_a))) 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 if trag_kind is not None and trag_kind in _TRAG_KINDS: obj_attrs.SetUserString(_KEY_TRAG_KIND, trag_kind) @@ -3179,6 +3185,8 @@ def _read_meta(obj): except Exception: t_target_s = 0.0 try: t_target_a = float(a.GetUserString(_KEY_TREPPE_TARGET_A) or "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 import json tsoll = dict(_TREPPE_SOLL_DEFAULT) @@ -3390,6 +3398,7 @@ def _read_meta(obj): "treppe_lock_s": t_lock_s, "treppe_target_s": t_target_s, "treppe_target_a": t_target_a, + "treppe_podest_min": t_podest_min, "trag_kind": tk_raw, "trag_profil": tp_raw, "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, modus="flach", lauf_d=0.18): - """L-Treppe aus 3-Punkt-Polylinie (Start, Eck-Punkt, End). - Bauet 2 gerade Laufe + 1 Podest am Eck zusammen. Hoehe wird - proportional zu den Lauflinien-Laengen auf die beiden Laufe verteilt.""" + """L-Treppe. Axis kann 3-Punkt (legacy: Start/Eck/End — Podest am + Eckpunkt) oder 4-Punkt (Start/Eck1/Eck2/End — Podest zwischen Eck1 + und Eck2) sein. Bauet 2 Laufe + Podest dazwischen.""" if not isinstance(axis_polyline, rg.Curve): return None try: ok_pl, poly = axis_polyline.TryGetPolyline() except Exception: return None - if not ok_pl or poly is None or poly.Count != 3: + if not ok_pl or poly is 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) + p1 = rg.Point3d(poly[1].X, poly[1].Y, 0) + p2 = rg.Point3d(poly[2].X, poly[2].Y, 0) + else: return None - p0 = rg.Point3d(poly[0].X, poly[0].Y, 0) - p1 = rg.Point3d(poly[1].X, poly[1].Y, 0) - p2 = rg.Point3d(poly[2].X, poly[2].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) @@ -6329,20 +6351,39 @@ def _bruch_gerade(axis_curve, breite, referenz, n_stufen, total_h, cut_h, z): # ----- L-Treppe: delegieren auf beide Laeufe ------------------------------- 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=P1→P2) sein. + N wird auf die zwei BEGEHBAREN Laeufe verteilt — Podest hat keine + Stufen.""" if not isinstance(axis_polyline, rg.Curve): return None ok, poly = axis_polyline.TryGetPolyline() - if not ok or poly is None or poly.Count != 3: return None - p0 = rg.Point3d(poly[0].X, poly[0].Y, z) - pc = rg.Point3d(poly[1].X, poly[1].Y, z) - p1 = rg.Point3d(poly[2].X, poly[2].Y, z) - L1 = ((pc.X - p0.X) ** 2 + (pc.Y - p0.Y) ** 2) ** 0.5 - L2 = ((p1.X - pc.X) ** 2 + (p1.Y - pc.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, pc), rg.LineCurve(pc, p1), N1, N2 + 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) + pc = rg.Point3d(poly[1].X, poly[1].Y, z) + p1 = rg.Point3d(poly[2].X, poly[2].Y, z) + L1 = ((pc.X - p0.X) ** 2 + (pc.Y - p0.Y) ** 2) ** 0.5 + L2 = ((p1.X - pc.X) ** 2 + (p1.Y - pc.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, pc), rg.LineCurve(pc, p1), N1, N2, None + return None 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.""" segs = _l_segments(axis_polyline, n_stufen, z) 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 H2 = total_h - H1 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, breite=None, referenz=None): - """Lauflinie ueber BEIDE Laeufe einer L-Treppe. Schaft 1 vom Start zur - PROJEZIERTEN Eck-Mitte (Schnittpunkt der zwei mid-versetzten Linien), - Schaft 2 von Eck-Mitte zum Ende mit Pfeil. So sauberer Übergang ohne - Versatz an der Ecke — auch bei Lage=links/rechts.""" + """Lauflinie ueber die L-Treppe. Bei 4-Punkt-Axis: Schaft1 + Podest- + Verbindung + Schaft2; Pfeil am Ende. Bei 3-Punkt: Eck-projezierte + Verbindung wie zuvor.""" segs = _l_segments(axis_polyline, n_stufen, z) if segs is None: return [] - s1, s2, _N1, _N2 = segs + s1, s2, _N1, _N2, podest = segs P0a = s1.PointAtStart; P1a = s1.PointAtEnd txa, tya = P1a.X - P0a.X, P1a.Y - P0a.Y 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 [] uxa, uya = txa / La, tya / La uxb, uyb = txb / Lb, tyb / Lb - # Mid-offset relativ zur Axis mid_off = 0.0 wide_l = wide_r = None if breite is not None and referenz is not None: @@ -6398,58 +6437,117 @@ def _lauflinie_l(axis_polyline, n_stufen, z, arrow_style="klassisch", doc=None, mid_off = (off_l + off_r) * 0.5 wide_l = off_l - mid_off wide_r = off_r - mid_off - # perp 90° CCW pa_x, pa_y = -uya * mid_off, uxa * 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) 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) 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: meet = _line_intersect_xy(P0a_s, rg.Vector3d(uxa, uya, 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: - meet = P1a_s # = P0b_s wenn mid=0 - out = [] + meet = P1a_s 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, doc, wide_l, wide_r)) return out def _aussen_l_polygon(axis_polyline, breite, referenz, z): - """Sauberes L-Polygon: 6-Punkt-Outline mit Outer/Inner-Ecken via - Linien-Schnitt. Returnt PolylineCurve oder None bei Singularitaet/ - Fehler — Caller faellt dann auf 2-Rechteck-Variante zurueck.""" + """Sauberes L-Polygon mit Outer/Inner-Ecken via Linien-Schnitt. + Unterstuetzt 3-Punkt (Eck=Punkt) und 4-Punkt (Podest zwischen Eck1 und + Eck2). Returnt PolylineCurve oder None bei Singularitaet/Fehler.""" try: ok, poly = axis_polyline.TryGetPolyline() - if not ok or poly is None or poly.Count != 3: return None - p0 = rg.Point3d(poly[0].X, poly[0].Y, z) - pc = rg.Point3d(poly[1].X, poly[1].Y, z) - p1 = rg.Point3d(poly[2].X, poly[2].Y, z) - L1 = ((pc.X - p0.X) ** 2 + (pc.Y - p0.Y) ** 2) ** 0.5 - L2 = ((p1.X - pc.X) ** 2 + (p1.Y - pc.Y) ** 2) ** 0.5 - if L1 < 1e-6 or L2 < 1e-6: return None - 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) - pp1 = (-u1.Y, u1.X) - pp2 = (-u2.Y, u2.X) + 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) - 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) - 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_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] - return rg.PolylineCurve(rg.Polyline(pts)) + 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) + pc = rg.Point3d(poly[1].X, poly[1].Y, z) + p1 = rg.Point3d(poly[2].X, poly[2].Y, z) + L1 = ((pc.X - p0.X) ** 2 + (pc.Y - p0.Y) ** 2) ** 0.5 + L2 = ((p1.X - pc.X) ** 2 + (p1.Y - pc.Y) ** 2) ** 0.5 + if L1 < 1e-6 or L2 < 1e-6: return None + 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) + pp1 = (-u1.Y, u1.X); pp2 = (-u2.Y, u2.X) + 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) + p1_l = _off(p1, pp2, off_l); p1_r = _off(p1, pp2, off_r) + 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) + pts = [p0_l, p0_r, corner_r, p1_r, p1_l, corner_l, p0_l] + return rg.PolylineCurve(rg.Polyline(pts)) + return None except Exception as ex: print("[ELEMENTE] _aussen_l_polygon:", ex) return None @@ -6463,7 +6561,7 @@ def _aussen_l(axis_polyline, breite, referenz, z, upper (= gestrichelt wenn oben).""" segs = _l_segments(axis_polyline, n_stufen, z) 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 H2 = total_h - H1 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): segs = _l_segments(axis_polyline, n_stufen, z) if segs is None: return [] - s1, s2, N1, N2 = segs + s1, s2, N1, N2, _pod = segs H1 = total_h * (N1 / float(N1 + N2)) H2 = total_h - H1 if cut_h <= H1: @@ -9517,11 +9615,15 @@ class ElementeBridge(panel_base.BaseBridge): second_pt = clicked # 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 referenz == "mid": + referenz = "links" gp3 = ric.GetPoint() gp3.SetCommandPrompt( - "L-Treppe: Endpunkt nach dem Podest [Stufen={}, Breite={:.2f}]".format( - n_stufen, breite)) + "L-Treppe: Endpunkt nach dem Podest [Stufen={}, Breite={:.2f}, Ref={}]".format( + n_stufen, breite, referenz)) gp3.SetBasePoint(second_pt, True) gp3.DynamicDraw += _make_treppe_preview_handler( second_pt, breite, referenz, max(1, n_stufen // 2)) @@ -12249,6 +12351,10 @@ class ElementeBridge(panel_base.BaseBridge): if tn < 2: tn = 2 tref = p.get("treppeReferenz", old_meta.get("treppe_referenz", "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")) if tmod not in _TREPPE_MODI: tmod = "flach" try: tld = float(p.get("laufD", old_meta.get("treppe_lauf_d", 0.18))) diff --git a/src/ElementeApp.jsx b/src/ElementeApp.jsx index 9003699..7a93124 100644 --- a/src/ElementeApp.jsx +++ b/src/ElementeApp.jsx @@ -2093,11 +2093,15 @@ function TreppeProperties({ treppe, geschosse, onUpdate, onDelete }) { } 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: 'mid', label: 'mittig' }, { 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_OPTIONS = [ { code: 'massiv', label: 'massiv', hint: 'Block bis zum Boden — wie eine Mauer unter der Treppe' },