Treppen UX-Polish: Start-Z, Trittmass-Lock, Pfeil-Stile, Grips
Properties-Panel: - Konsistentes 50px/1fr/14px Grid fuer alle Treppen-Rows - Lage + Unten als Dropdown (lowercase Labels) - Versatz: Dropdown (Geschoss-OKFF) oder eigenes Z mit Input + x-Button - Ziel: gleich (Geschoss-Liste oder eigene Hoehe), Geschosse-Filter excludes das Start-Geschoss - Start-Dropdown filtert auf okff < Ziel-Z (kein hoeheres Geschoss als Start waehlbar, beachtet auch eigene-Hoehe-Ziel) - Stufen: Dropdown 2-40 (statt freie Eingabe), mit Lock nur S-konforme Werte - Dropdowns nutzen System-Font (statt mono) - Ausgrenzung 'Aussenlinie'-Toggle (Aussenlinie immer an) - Pfeil-Style-Dropdown unter Lauflinie-Checkbox: klassisch / gefuellt (Solid-Hatch) / breit / voll (Spitzen bis Treppen-Aussenkanten) Backend Treppe: - Start-Z-Override via treppe_uk_over (m Offset relativ zu Geschoss-OKFF) - 2D-Symbol bleibt auf OKFF (egal ob Versatz) — Symbol klebt am Boden - Lauflinie-Schaft auf visuellen Treppen-Mittelpunkt versetzt (bei Lage=links/rechts), nicht mehr auf der Referenz-Achse - Trittmass-Lock: treppe_lock_s + target_S/A. Beim Aktivieren werden S+A als Ziel gespeichert. Bei H-Change wird N=round(H/target_S) recomputed + Axis-Laenge auf N*target_A angepasst (gerade Treppen) - Bruchsymbol-Toggle aus: ganze Treppe ungesplittet zeichnen (eff_cut_h=0 → kein Lower/Upper-Split) - Treppen-Endpunkt-Marker (treppe_grips.py) — gruene Punkte an Start/ Ende der Lauflinie, beachtet treppe_art (Wendel: poly[1]/poly[2]) Verdoppelungs-Fix: - _find_target_volume skipt treppe_2d_symbol explicit (sind 2D-Curves, kein Volume). Vorher konnte Replace(curve, brep) fehlschlagen → das echte Treppen-Brep blieb stehen + neues kam dazu → Duplikat - _find_objects_by_wall_id mit HiddenObjects+LockedObjects-Iterator, findet auch Objs auf hidden 3D-Layer - Anti-Dup-Cleanup in _regenerate_element: bei mehreren treppe_volume mit gleicher element_id → alle ausser dem ersten loeschen State-Pipeline: - geschosse-Liste enthaelt jetzt okff+hoehe (fuer Frontend-Constraints) - Treppe-State neu: ukOver, arrowStyle, lockS, targetS, targetA - Hidden-Source-Fallback in _send_state findet auch Treppen wenn der 3D-Layer aus ist (sodass Properties-Panel angezeigt wird) Dimensionen-Panel: - on_select + on_idle skippen waehrend Partnership-Cascade oder User-Transform — kein Flicker mehr beim Drag Andere: - Wand-Polyline-Vertex-Grips (alle Vertices, nicht nur Enden) - PopupMenu unterstuetzt _divider + checked-Items - TREPPEN/RAEUME Layer-Migration auf Capital-Case - selection-partnership tolerant: hidden Source wird trotzdem in die Selection genommen (sonst kann Drag nicht durch Pure-Transform)
This commit is contained in:
+356
-69
@@ -273,6 +273,7 @@ _KEY_TREPPE_MODUS = "dossier_treppe_modus" # "massiv"|"flach"|"platten
|
||||
_KEY_TREPPE_LAUF_D = "dossier_treppe_lauf_d" # Lauf-Plattendicke (m)
|
||||
_KEY_TREPPE_ART = "dossier_treppe_art" # "gerade"|"l"|"wendel"
|
||||
_KEY_TREPPE_H_OVER = "dossier_treppe_h_over" # eigene Hoehe (m); leer = Geschoss
|
||||
_KEY_TREPPE_UK_OVER = "dossier_treppe_uk_over" # Start-Z-Offset relativ zu Geschoss-OKFF (m); leer = 0
|
||||
_KEY_TREPPE_SOLL = "dossier_treppe_soll" # JSON {s:[lo,hi,on], a:[lo,hi,on], sa:[lo,hi,on]}
|
||||
_KEY_TREPPE_2D_SHOW = "dossier_treppe_2d_show" # doc-Setting "1"/"0" — Plansymbol an/aus
|
||||
|
||||
@@ -283,6 +284,10 @@ _KEY_TREPPE_SHOW_LAUFLINIE = "dossier_treppe_show_lauflinie"
|
||||
_KEY_TREPPE_SHOW_AUSSEN = "dossier_treppe_show_aussen"
|
||||
_KEY_TREPPE_SHOW_BRUCH = "dossier_treppe_show_bruch"
|
||||
_KEY_TREPPE_OBERE_DASHED = "dossier_treppe_obere_dashed" # "0"/"1" — Obere Stufen+Aussen gestrichelt
|
||||
_KEY_TREPPE_ARROW_STYLE = "dossier_treppe_arrow_style" # "klassisch"|"filled"|"doppelt"|"strich"
|
||||
_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)
|
||||
|
||||
# Tragwerk: Stuetze / Traeger / Unterzug — gemeinsames Querschnitts-System
|
||||
_KEY_TRAG_KIND = "dossier_trag_kind" # "stuetze" | "traeger" | "unterzug"
|
||||
@@ -2724,10 +2729,12 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over,
|
||||
geschoss_end=None, treppe_breite=None,
|
||||
treppe_n=None, treppe_referenz=None,
|
||||
treppe_modus=None, treppe_lauf_d=None, treppe_art=None,
|
||||
treppe_h_over=None, treppe_soll=None,
|
||||
treppe_h_over=None, treppe_uk_over=None, treppe_soll=None,
|
||||
treppe_show_tritte=None, treppe_show_lauflinie=None,
|
||||
treppe_show_aussen=None, treppe_show_bruch=None,
|
||||
treppe_obere_dashed=None,
|
||||
treppe_arrow_style=None,
|
||||
treppe_lock_s=None, treppe_target_s=None, treppe_target_a=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,
|
||||
@@ -2864,6 +2871,14 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over,
|
||||
obj_attrs.SetUserString(_KEY_TREPPE_H_OVER,
|
||||
"{:.4f}".format(float(treppe_h_over)))
|
||||
except Exception: pass
|
||||
if treppe_uk_over is not None:
|
||||
if treppe_uk_over == "" or treppe_uk_over is None:
|
||||
obj_attrs.SetUserString(_KEY_TREPPE_UK_OVER, "")
|
||||
else:
|
||||
try:
|
||||
obj_attrs.SetUserString(_KEY_TREPPE_UK_OVER,
|
||||
"{:.4f}".format(float(treppe_uk_over)))
|
||||
except Exception: pass
|
||||
if treppe_soll is not None:
|
||||
try:
|
||||
import json
|
||||
@@ -2879,6 +2894,19 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over,
|
||||
):
|
||||
if _val is not None:
|
||||
obj_attrs.SetUserString(_key, "1" if bool(_val) else "0")
|
||||
if (treppe_arrow_style is not None
|
||||
and treppe_arrow_style in ("klassisch", "filled", "breit", "voll")):
|
||||
obj_attrs.SetUserString(_KEY_TREPPE_ARROW_STYLE, treppe_arrow_style)
|
||||
if treppe_lock_s is not None:
|
||||
obj_attrs.SetUserString(_KEY_TREPPE_LOCK_S, "1" if bool(treppe_lock_s) else "0")
|
||||
if treppe_target_s is not None:
|
||||
try: obj_attrs.SetUserString(_KEY_TREPPE_TARGET_S,
|
||||
"{:.4f}".format(float(treppe_target_s)))
|
||||
except Exception: pass
|
||||
if treppe_target_a is not None:
|
||||
try: obj_attrs.SetUserString(_KEY_TREPPE_TARGET_A,
|
||||
"{:.4f}".format(float(treppe_target_a)))
|
||||
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)
|
||||
@@ -3133,6 +3161,7 @@ def _read_meta(obj):
|
||||
tart = a.GetUserString(_KEY_TREPPE_ART) or "gerade"
|
||||
if tart not in _TREPPE_ARTEN: tart = "gerade"
|
||||
thov = a.GetUserString(_KEY_TREPPE_H_OVER) or ""
|
||||
tukov = a.GetUserString(_KEY_TREPPE_UK_OVER) or ""
|
||||
# 2D-Plansymbol-Flags — default True wenn nicht gesetzt
|
||||
def _flag_on(key):
|
||||
v = a.GetUserString(key)
|
||||
@@ -3142,6 +3171,14 @@ def _read_meta(obj):
|
||||
t_show_aus = _flag_on(_KEY_TREPPE_SHOW_AUSSEN)
|
||||
t_show_bru = _flag_on(_KEY_TREPPE_SHOW_BRUCH)
|
||||
t_obere_dash = (a.GetUserString(_KEY_TREPPE_OBERE_DASHED) == "1")
|
||||
t_arrow_style = a.GetUserString(_KEY_TREPPE_ARROW_STYLE) or "klassisch"
|
||||
if t_arrow_style not in ("klassisch", "filled", "breit", "voll"):
|
||||
t_arrow_style = "klassisch"
|
||||
t_lock_s = (a.GetUserString(_KEY_TREPPE_LOCK_S) == "1")
|
||||
try: t_target_s = float(a.GetUserString(_KEY_TREPPE_TARGET_S) or "0")
|
||||
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
|
||||
# Soll-Werte JSON, mit Defaults wenn nicht gesetzt
|
||||
import json
|
||||
tsoll = dict(_TREPPE_SOLL_DEFAULT)
|
||||
@@ -3342,12 +3379,17 @@ def _read_meta(obj):
|
||||
"treppe_lauf_d": tld,
|
||||
"treppe_art": tart,
|
||||
"treppe_h_over": thov,
|
||||
"treppe_uk_over": tukov,
|
||||
"treppe_soll": tsoll,
|
||||
"treppe_show_tritte": t_show_tri,
|
||||
"treppe_show_lauflinie": t_show_lau,
|
||||
"treppe_show_aussen": t_show_aus,
|
||||
"treppe_show_bruch": t_show_bru,
|
||||
"treppe_obere_dashed": t_obere_dash,
|
||||
"treppe_arrow_style": t_arrow_style,
|
||||
"treppe_lock_s": t_lock_s,
|
||||
"treppe_target_s": t_target_s,
|
||||
"treppe_target_a": t_target_a,
|
||||
"trag_kind": tk_raw,
|
||||
"trag_profil": tp_raw,
|
||||
"trag_b": t_b,
|
||||
@@ -3408,9 +3450,18 @@ def _read_meta(obj):
|
||||
|
||||
|
||||
def _find_objects_by_wall_id(doc, wall_id, type_filter=None):
|
||||
"""Findet alle Rhino-Objekte mit der gegebenen wall_id."""
|
||||
"""Findet alle Rhino-Objekte mit der gegebenen wall_id. Hidden+Locked
|
||||
werden mit-iteriert — sonst werden Treppen-Volumes auf hidden 3D-Layer
|
||||
nicht gefunden + Regen-Replace schlaegt fehl → Duplikate."""
|
||||
out = []
|
||||
for obj in doc.Objects:
|
||||
try:
|
||||
_s = Rhino.DocObjects.ObjectEnumeratorSettings()
|
||||
_s.HiddenObjects = True; _s.LockedObjects = True
|
||||
_s.IncludeLights = False; _s.IncludeGrips = False
|
||||
_iter = doc.Objects.GetObjectList(_s)
|
||||
except Exception:
|
||||
_iter = doc.Objects
|
||||
for obj in _iter:
|
||||
meta = _read_meta(obj)
|
||||
if meta and meta["id"] == wall_id:
|
||||
if type_filter is None or meta["type"] == type_filter:
|
||||
@@ -4089,9 +4140,13 @@ def _find_source(doc, element_id):
|
||||
|
||||
|
||||
def _find_target_volume(doc, element_id):
|
||||
"""Volumen-Objekt (Brep)."""
|
||||
"""Volumen-Objekt (Brep). Skipt explicit treppe_2d_symbol (das sind 2D-
|
||||
Plansymbol-Curves, kein Volume) — sonst kann Replace einen 2D-Curve mit
|
||||
einem Brep zu ueberschreiben versuchen und fehlschlagen → Duplikat des
|
||||
eigentlichen Treppe-Volumens."""
|
||||
for obj, meta in _find_objects_by_wall_id(doc, element_id):
|
||||
if meta["type"] in VOLUME_TYPES:
|
||||
t = meta["type"]
|
||||
if t in VOLUME_TYPES and t != "treppe_2d_symbol":
|
||||
return obj
|
||||
return None
|
||||
|
||||
@@ -6008,33 +6063,83 @@ def _treppe_2d_enabled(doc):
|
||||
return True
|
||||
|
||||
|
||||
def _treppe_2d_arrow_curves(p_tail, p_head, head_size):
|
||||
"""Liefert Liste von Curves fuer einen 2D-Pfeil von p_tail nach p_head:
|
||||
Schaft (Line) + zwei kurze Linien fuer die Spitze (V-Form, jeweils ~30°
|
||||
vom Schaft, Laenge head_size). p_tail/p_head sind Point3d auf gleicher Z."""
|
||||
def _treppe_2d_arrow_curves(p_tail, p_head, head_size, style="klassisch",
|
||||
doc=None, wide_off_l=None, wide_off_r=None):
|
||||
"""Liefert Liste von Items (Curves + ggf. Hatch) fuer einen 2D-Pfeil.
|
||||
Schaft (Line) + Spitze gemaess style:
|
||||
- 'klassisch': offene V-Spitze (zwei Linien, 30° gespreizt)
|
||||
- 'filled': Dreieck-Outline + Solid-Hatch (wirklich ausgefuellt)
|
||||
- 'breit': V-Spitze 60° + laenger
|
||||
- 'voll': Pfeilspitzen reichen bis zu den Treppen-Aussenkanten
|
||||
(braucht wide_off_l + wide_off_r vom Caller)
|
||||
Caller-Loop muss zwischen Curve und Hatch unterscheiden beim Add.
|
||||
"""
|
||||
import math
|
||||
out = []
|
||||
try:
|
||||
out.append(rg.LineCurve(p_tail, p_head))
|
||||
out.append(rg.LineCurve(p_tail, p_head)) # Schaft
|
||||
dx = p_head.X - p_tail.X
|
||||
dy = p_head.Y - p_tail.Y
|
||||
L = math.hypot(dx, dy)
|
||||
if L < 1e-9 or head_size <= 0:
|
||||
return out
|
||||
ux, uy = dx / L, dy / L # vom tail zum head
|
||||
# Spitzen zeigen ZURUECK vom head, um ~30° gespreizt
|
||||
ang = math.radians(30.0)
|
||||
ux, uy = dx / L, dy / L
|
||||
px, py = -uy, ux # perpendikular zum Schaft
|
||||
z = p_head.Z
|
||||
|
||||
def _v_at(head_x, head_y, size, deg=30.0):
|
||||
ang = math.radians(deg)
|
||||
ca, sa = math.cos(ang), math.sin(ang)
|
||||
# Rotiere -u um +/- ang
|
||||
bx = -ux * ca - (-uy) * sa
|
||||
by = -ux * sa + (-uy) * ca
|
||||
cx = -ux * ca + (-uy) * sa
|
||||
cy = ux * sa + (-uy) * ca
|
||||
z = p_head.Z
|
||||
out.append(rg.LineCurve(p_head,
|
||||
rg.Point3d(p_head.X + bx * head_size, p_head.Y + by * head_size, z)))
|
||||
out.append(rg.LineCurve(p_head,
|
||||
rg.Point3d(p_head.X + cx * head_size, p_head.Y + cy * head_size, z)))
|
||||
cx_ = -ux * ca + (-uy) * sa
|
||||
cy_ = ux * sa + (-uy) * ca
|
||||
h = rg.Point3d(head_x, head_y, z)
|
||||
return [
|
||||
rg.LineCurve(h, rg.Point3d(head_x + bx * size, head_y + by * size, z)),
|
||||
rg.LineCurve(h, rg.Point3d(head_x + cx_ * size, head_y + cy_ * size, z)),
|
||||
]
|
||||
|
||||
if style == "filled":
|
||||
ang = math.radians(20.0)
|
||||
ca, sa = math.cos(ang), math.sin(ang)
|
||||
bx = -ux * ca - (-uy) * sa
|
||||
by = -ux * sa + (-uy) * ca
|
||||
cx_ = -ux * ca + (-uy) * sa
|
||||
cy_ = ux * sa + (-uy) * ca
|
||||
p_b = rg.Point3d(p_head.X + bx * head_size, p_head.Y + by * head_size, z)
|
||||
p_c = rg.Point3d(p_head.X + cx_ * head_size, p_head.Y + cy_ * head_size, z)
|
||||
tri = rg.PolylineCurve(rg.Polyline([p_head, p_b, p_c, p_head]))
|
||||
out.append(tri)
|
||||
if doc is not None:
|
||||
try:
|
||||
pidx = doc.HatchPatterns.Find("Solid", True)
|
||||
if pidx < 0:
|
||||
pidx = doc.HatchPatterns.CurrentHatchPatternIndex
|
||||
if pidx >= 0:
|
||||
hatches = rg.Hatch.Create(tri, pidx, 0.0, 1.0, 0.001)
|
||||
if hatches and len(hatches) > 0:
|
||||
out.append(hatches[0])
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] arrow hatch:", ex)
|
||||
elif style == "breit":
|
||||
out.extend(_v_at(p_head.X, p_head.Y, head_size * 1.2, deg=55.0))
|
||||
elif style == "voll" and wide_off_l is not None and wide_off_r is not None:
|
||||
# V-Spitze die seitlich bis zu den Treppen-Aussenkanten reicht.
|
||||
# Tip am head, Beine zurueckziehen + nach off_l bzw. off_r perp.
|
||||
# "back" = wie weit die Spitzen-Beine entgegen Auf-Richtung
|
||||
# zurueckgehen. Wir nehmen 0.6 * |max(|off_l|,|off_r|)| als
|
||||
# Recess damit der V-Winkel ~50-60° wird.
|
||||
mx = max(abs(wide_off_l), abs(wide_off_r))
|
||||
back = max(0.05, mx * 0.6)
|
||||
tip_l = rg.Point3d(p_head.X - ux * back + px * wide_off_l,
|
||||
p_head.Y - uy * back + py * wide_off_l, z)
|
||||
tip_r = rg.Point3d(p_head.X - ux * back + px * wide_off_r,
|
||||
p_head.Y - uy * back + py * wide_off_r, z)
|
||||
out.append(rg.LineCurve(p_head, tip_l))
|
||||
out.append(rg.LineCurve(p_head, tip_r))
|
||||
else: # klassisch (Default + Fallback bei 'voll' ohne breite)
|
||||
out.extend(_v_at(p_head.X, p_head.Y, head_size))
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] _treppe_2d_arrow_curves:", ex)
|
||||
return out
|
||||
@@ -6086,33 +6191,54 @@ def _tritte_gerade(axis_curve, breite, referenz, n_stufen, z, cut_idx=-1):
|
||||
return lower, upper
|
||||
|
||||
|
||||
def _lauflinie_gerade(axis_curve, n_stufen, z, cut_idx=-1, with_bruch=False):
|
||||
"""Returnt (lower_curves, upper_curves).
|
||||
Ohne Bruch: eine durchgehende Lauflinie P0→P1 mit Pfeil am Ende, alles
|
||||
in lower.
|
||||
Mit Bruch: gesplittet in zwei Segmente. Lower-Segment endet mit Pfeil-
|
||||
spitze direkt VOR dem Bruchbereich (am Front-Rand der Bruchzone), Upper-
|
||||
Segment startet hinter dem Bruchbereich und endet mit Pfeil am Treppen-
|
||||
Ende. So kann upper bei obereDashed=True gestrichelt gerendert werden."""
|
||||
def _lauflinie_gerade(axis_curve, n_stufen, z, cut_idx=-1, with_bruch=False,
|
||||
arrow_style="klassisch", doc=None,
|
||||
breite=None, referenz=None):
|
||||
"""Returnt (lower_curves, upper_curves). Wenn breite + referenz gegeben,
|
||||
wird der Lauflinien-SCHAFT auf den visuellen Mittelpunkt der Treppe
|
||||
versetzt (= zwischen Aussenkanten), sodass der Pfeil im Bild zentriert
|
||||
in der Treppe sitzt — nicht auf der Referenzlinie wenn Lage = links/
|
||||
rechts. Style 'voll' braucht breite+referenz fuer die Spitzen-Breite."""
|
||||
if not isinstance(axis_curve, rg.Curve): return [], []
|
||||
P0 = axis_curve.PointAtStart; P1 = axis_curve.PointAtEnd
|
||||
tx, ty = P1.X - P0.X, P1.Y - P0.Y
|
||||
L = (tx * tx + ty * ty) ** 0.5
|
||||
if L < 1e-6: return [], []
|
||||
ux, uy = tx / L, ty / L
|
||||
px, py = -uy, ux # Perpendikular CCW
|
||||
head_size = min(0.25, L * 0.05)
|
||||
|
||||
# Perp-Offset der Lauflinie: Mittelpunkt zwischen Treppen-Aussenkanten.
|
||||
# Damit folgt der Pfeil visuell der Treppen-Mitte (statt auf der
|
||||
# Referenz-Achse zu kleben, was bei Lage=links/rechts seitlich aussieht).
|
||||
mid_off = 0.0
|
||||
wide_off_l = wide_off_r = None
|
||||
if breite is not None and referenz is not None:
|
||||
off_l, off_r = _treppe_2d_side_offsets(breite, referenz)
|
||||
mid_off = (off_l + off_r) * 0.5
|
||||
# Fuer 'voll' Style: Spitzen-Offset relativ zur Lauflinie (= mid_off)
|
||||
wide_off_l = off_l - mid_off
|
||||
wide_off_r = off_r - mid_off
|
||||
|
||||
def _shift(p):
|
||||
return rg.Point3d(p.X + px * mid_off, p.Y + py * mid_off, z)
|
||||
|
||||
P0s = _shift(P0); P1s = _shift(P1)
|
||||
|
||||
if cut_idx < 0 or not with_bruch:
|
||||
return _treppe_2d_arrow_curves(P0, P1, head_size), []
|
||||
# Splitt-Positionen entlang Lauflinie (siehe _bruch_diag_params)
|
||||
return _treppe_2d_arrow_curves(P0s, P1s, head_size, arrow_style, doc,
|
||||
wide_off_l, wide_off_r), []
|
||||
pos, _d_diag, gap = _bruch_diag_params(L, n_stufen, cut_idx)
|
||||
lower_end_t = pos - gap * 0.5 # Front-Rand des Bruchbereichs (lower-Seite)
|
||||
lower_end_t = pos - gap * 0.5
|
||||
upper_start_t = pos + gap * 0.5
|
||||
lower_head = rg.Point3d(P0.X + ux * lower_end_t,
|
||||
P0.Y + uy * lower_end_t, z)
|
||||
upper_tail = rg.Point3d(P0.X + ux * upper_start_t,
|
||||
P0.Y + uy * upper_start_t, z)
|
||||
lower = _treppe_2d_arrow_curves(P0, lower_head, head_size)
|
||||
upper = _treppe_2d_arrow_curves(upper_tail, P1, head_size)
|
||||
lower_head = rg.Point3d(P0s.X + ux * lower_end_t,
|
||||
P0s.Y + uy * lower_end_t, z)
|
||||
upper_tail = rg.Point3d(P0s.X + ux * upper_start_t,
|
||||
P0s.Y + uy * upper_start_t, z)
|
||||
lower = _treppe_2d_arrow_curves(P0s, lower_head, head_size, arrow_style,
|
||||
doc, wide_off_l, wide_off_r)
|
||||
upper = _treppe_2d_arrow_curves(upper_tail, P1s, head_size, arrow_style,
|
||||
doc, wide_off_l, wide_off_r)
|
||||
return lower, upper
|
||||
|
||||
|
||||
@@ -6246,11 +6372,14 @@ def _tritte_l(axis_polyline, breite, referenz, n_stufen, z,
|
||||
return l1 + l2, []
|
||||
|
||||
|
||||
def _lauflinie_l(axis_polyline, n_stufen, z):
|
||||
def _lauflinie_l(axis_polyline, n_stufen, z, arrow_style="klassisch", doc=None,
|
||||
breite=None, referenz=None):
|
||||
segs = _l_segments(axis_polyline, n_stufen, z)
|
||||
if segs is None: return []
|
||||
s1, _s2, N1, _N2 = segs
|
||||
return _lauflinie_gerade(s1, N1, z)
|
||||
lo, _up = _lauflinie_gerade(s1, N1, z, -1, False, arrow_style, doc,
|
||||
breite, referenz)
|
||||
return lo
|
||||
|
||||
|
||||
def _aussen_l(axis_polyline, breite, referenz, z,
|
||||
@@ -6331,7 +6460,8 @@ def _tritte_wendel(axis_polyline, breite, referenz, n_stufen, z,
|
||||
return lower, upper
|
||||
|
||||
|
||||
def _lauflinie_wendel(axis_polyline, breite, referenz, n_stufen, z):
|
||||
def _lauflinie_wendel(axis_polyline, breite, referenz, n_stufen, z,
|
||||
arrow_style="klassisch", doc=None):
|
||||
import math
|
||||
p = _wendel_params(axis_polyline, breite, referenz, n_stufen, z)
|
||||
if p is None: return []
|
||||
@@ -6357,7 +6487,7 @@ def _lauflinie_wendel(axis_polyline, breite, referenz, n_stufen, z):
|
||||
head_len = min(0.25, abs(r_mid * da) * 0.6)
|
||||
tail = rg.Point3d(arc_end.X - tang_x * head_len * 0.3,
|
||||
arc_end.Y - tang_y * head_len * 0.3, z)
|
||||
out.extend(_treppe_2d_arrow_curves(tail, arc_end, head_len))
|
||||
out.extend(_treppe_2d_arrow_curves(tail, arc_end, head_len, arrow_style, doc))
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] _lauflinie_wendel:", ex)
|
||||
return out
|
||||
@@ -6431,7 +6561,7 @@ def _bruch_wendel(axis_polyline, breite, referenz, n_stufen, total_h, cut_h, z):
|
||||
return out
|
||||
|
||||
|
||||
def _make_treppe_2d_symbol(meta, geom, z, total_h, cut_h):
|
||||
def _make_treppe_2d_symbol(meta, geom, z, total_h, cut_h, doc=None):
|
||||
"""Komponiert per per-treppe-Flags. Returnt (solid, dashed):
|
||||
- Tritte/Aussenlinie unterhalb der Schnittebene → solid
|
||||
- Tritte/Aussenlinie oberhalb der Schnittebene → dashed (wenn
|
||||
@@ -6444,45 +6574,47 @@ def _make_treppe_2d_symbol(meta, geom, z, total_h, cut_h):
|
||||
n = meta.get("treppe_n", 15)
|
||||
show_tri = meta.get("treppe_show_tritte", True)
|
||||
show_lau = meta.get("treppe_show_lauflinie", True)
|
||||
show_aus = meta.get("treppe_show_aussen", True)
|
||||
show_bru = meta.get("treppe_show_bruch", True)
|
||||
obe_dash = meta.get("treppe_obere_dashed", True)
|
||||
arrow_style = meta.get("treppe_arrow_style", "klassisch")
|
||||
|
||||
solid, dashed = [], []
|
||||
upper_target = dashed if obe_dash else solid
|
||||
# Wenn Bruchsymbol AUS → ganze Treppe ungesplittet zeichnen (cut_h=0
|
||||
# signalisiert den Sub-Generatoren keinen Cut)
|
||||
eff_cut_h = cut_h if show_bru else 0.0
|
||||
try:
|
||||
if art == "l":
|
||||
if show_tri:
|
||||
lo, up = _tritte_l(geom, breite, ref, n, z, total_h, cut_h)
|
||||
lo, up = _tritte_l(geom, breite, ref, n, z, total_h, eff_cut_h)
|
||||
solid.extend(lo); upper_target.extend(up)
|
||||
if show_lau:
|
||||
solid.extend(_lauflinie_l(geom, n, z))
|
||||
if show_aus:
|
||||
lo, up = _aussen_l(geom, breite, ref, z, total_h, cut_h, n)
|
||||
solid.extend(_lauflinie_l(geom, n, z, arrow_style, doc,
|
||||
breite, ref))
|
||||
lo, up = _aussen_l(geom, breite, ref, z, total_h, eff_cut_h, n)
|
||||
solid.extend(lo); upper_target.extend(up)
|
||||
if show_bru:
|
||||
solid.extend(_bruch_l(geom, breite, ref, n, total_h, cut_h, z))
|
||||
elif art == "wendel":
|
||||
if show_tri:
|
||||
lo, up = _tritte_wendel(geom, breite, ref, n, z, total_h, cut_h)
|
||||
lo, up = _tritte_wendel(geom, breite, ref, n, z, total_h, eff_cut_h)
|
||||
solid.extend(lo); upper_target.extend(up)
|
||||
if show_lau:
|
||||
solid.extend(_lauflinie_wendel(geom, breite, ref, n, z))
|
||||
if show_aus:
|
||||
lo, up = _aussen_wendel(geom, breite, ref, z, total_h, cut_h, n)
|
||||
solid.extend(_lauflinie_wendel(geom, breite, ref, n, z, arrow_style, doc))
|
||||
lo, up = _aussen_wendel(geom, breite, ref, z, total_h, eff_cut_h, n)
|
||||
solid.extend(lo); upper_target.extend(up)
|
||||
if show_bru:
|
||||
solid.extend(_bruch_wendel(geom, breite, ref, n,
|
||||
total_h, cut_h, z))
|
||||
else: # gerade
|
||||
cut_idx = _cut_idx_gerade(n, total_h, cut_h)
|
||||
cut_idx = _cut_idx_gerade(n, total_h, eff_cut_h)
|
||||
if show_tri:
|
||||
lo, up = _tritte_gerade(geom, breite, ref, n, z, cut_idx)
|
||||
solid.extend(lo); upper_target.extend(up)
|
||||
if show_lau:
|
||||
lo, up = _lauflinie_gerade(geom, n, z, cut_idx, show_bru)
|
||||
lo, up = _lauflinie_gerade(geom, n, z, cut_idx, show_bru,
|
||||
arrow_style, doc, breite, ref)
|
||||
solid.extend(lo); upper_target.extend(up)
|
||||
if show_aus:
|
||||
lo, up = _aussen_gerade(geom, breite, ref, z, cut_idx, n)
|
||||
solid.extend(lo); upper_target.extend(up)
|
||||
if show_bru:
|
||||
@@ -7008,6 +7140,10 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
||||
if g_start is None:
|
||||
return False
|
||||
uk = float(g_start.get("okff", 0.0))
|
||||
uk_over = meta.get("treppe_uk_over", "")
|
||||
if uk_over:
|
||||
try: uk = uk + float(uk_over)
|
||||
except Exception: pass
|
||||
h_over = meta.get("treppe_h_over", "")
|
||||
if h_over:
|
||||
try:
|
||||
@@ -7364,6 +7500,24 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
||||
|
||||
if brep is None: return False
|
||||
vol_obj = _find_target_volume(doc, element_id)
|
||||
# Anti-Duplikat-Cleanup: wenn aus frueheren Bugs mehrere Volumes
|
||||
# mit derselben element_id existieren (gleicher Typ) → alle ausser
|
||||
# dem ersten loeschen. Sonst akkumulieren sich Verdoppelungen ueber
|
||||
# Sessions. Aktuell nur fuer Treppen relevant (Wand-Chains haben
|
||||
# eigene Logik).
|
||||
if meta["type"] == "treppe_axis":
|
||||
try:
|
||||
extras = _find_objects_by_wall_id(doc, element_id, "treppe_volume")
|
||||
if vol_obj is not None and len(extras) > 1:
|
||||
first_id = str(vol_obj.Id)
|
||||
for ex_obj, _ex_meta in extras:
|
||||
if str(ex_obj.Id) != first_id:
|
||||
try: doc.Objects.Delete(ex_obj.Id, True)
|
||||
except Exception: pass
|
||||
print("[ELEMENTE] Treppen-Volumen-Duplikat-Cleanup: "
|
||||
"{} extra entfernt".format(len(extras) - 1))
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] dup-cleanup treppe:", ex)
|
||||
attrs = Rhino.DocObjects.ObjectAttributes()
|
||||
attrs.LayerIndex = layer
|
||||
_attach_meta(attrs, element_id, vol_type, meta["geschoss"],
|
||||
@@ -7383,12 +7537,17 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
||||
treppe_lauf_d=meta.get("treppe_lauf_d"),
|
||||
treppe_art=meta.get("treppe_art"),
|
||||
treppe_h_over=meta.get("treppe_h_over"),
|
||||
treppe_uk_over=meta.get("treppe_uk_over"),
|
||||
treppe_soll=meta.get("treppe_soll"),
|
||||
treppe_show_tritte=meta.get("treppe_show_tritte"),
|
||||
treppe_show_lauflinie=meta.get("treppe_show_lauflinie"),
|
||||
treppe_show_aussen=meta.get("treppe_show_aussen"),
|
||||
treppe_show_bruch=meta.get("treppe_show_bruch"),
|
||||
treppe_obere_dashed=meta.get("treppe_obere_dashed"),
|
||||
treppe_arrow_style=meta.get("treppe_arrow_style"),
|
||||
treppe_lock_s=meta.get("treppe_lock_s"),
|
||||
treppe_target_s=meta.get("treppe_target_s"),
|
||||
treppe_target_a=meta.get("treppe_target_a"),
|
||||
trag_kind=meta.get("trag_kind"),
|
||||
trag_profil=meta.get("trag_profil"),
|
||||
trag_b=meta.get("trag_b"),
|
||||
@@ -7436,7 +7595,16 @@ def _regen_treppe_2d_symbol(doc, element_id, meta, geom, g_start, geschoss_name,
|
||||
Geschosses. Curves landen auf eigenem 2D-Layer (41_Treppen_2D).
|
||||
total_h = Gesamthoehe der Treppe (ok - uk), wird fuer Bruchsymbol-
|
||||
Positionierung gebraucht."""
|
||||
for obj in list(doc.Objects):
|
||||
# Hidden-inclusive Iterator damit alte Curves auch auf hidden
|
||||
# 41_Treppen_2D-Layer geloescht werden
|
||||
try:
|
||||
_s = Rhino.DocObjects.ObjectEnumeratorSettings()
|
||||
_s.HiddenObjects = True; _s.LockedObjects = True
|
||||
_s.IncludeLights = False; _s.IncludeGrips = False
|
||||
_del_iter = list(doc.Objects.GetObjectList(_s))
|
||||
except Exception:
|
||||
_del_iter = list(doc.Objects)
|
||||
for obj in _del_iter:
|
||||
a = obj.Attributes
|
||||
if a.GetUserString(_KEY_ID) != element_id: continue
|
||||
if a.GetUserString(_KEY_TYPE) != "treppe_2d_symbol": continue
|
||||
@@ -7449,11 +7617,11 @@ def _regen_treppe_2d_symbol(doc, element_id, meta, geom, g_start, geschoss_name,
|
||||
sh = float(g_start.get("schnitthoehe", 1.0))
|
||||
except Exception:
|
||||
okff, sh = 0.0, 1.0
|
||||
# 2D-Plan-Symbol auf OKFF (= Boden des Geschosses) damit beim Zeichnen
|
||||
# alles auf einer Z-Ebene liegt. sh wird weiterhin fuer den Lower/Upper-
|
||||
# Split gebraucht (= welche Stufen unter/ueber Schnittebene liegen).
|
||||
# 2D-Plan-Symbol IMMER auf Geschoss-OKFF — egal ob die Treppe via
|
||||
# uk_over angehoben ist. Das Plansymbol soll auf der Plan-Schnitt-
|
||||
# ebene/Geschoss-Boden liegen damit's beim Top-View sichtbar bleibt.
|
||||
z = okff
|
||||
solid, dashed = _make_treppe_2d_symbol(meta, geom, z, total_h, sh)
|
||||
solid, dashed = _make_treppe_2d_symbol(meta, geom, z, total_h, sh, doc)
|
||||
if not solid and not dashed: return
|
||||
layer_2d = _ensure_layer(doc, _layer_path_treppe_2d(doc, geschoss_name))
|
||||
|
||||
@@ -7469,18 +7637,24 @@ def _regen_treppe_2d_symbol(doc, element_id, meta, geom, g_start, geschoss_name,
|
||||
a.LinetypeIndex = linetype_idx
|
||||
return a
|
||||
|
||||
def _add(item, attrs):
|
||||
# Item kann rg.Curve, rg.Hatch oder rg.PolylineCurve sein.
|
||||
try:
|
||||
if isinstance(item, rg.Hatch):
|
||||
doc.Objects.AddHatch(item, attrs)
|
||||
else:
|
||||
doc.Objects.AddCurve(item, attrs)
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] add 2d item:", ex)
|
||||
|
||||
attrs_solid = _make_attrs()
|
||||
for c in solid:
|
||||
try: doc.Objects.AddCurve(c, attrs_solid)
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] add 2d curve:", ex)
|
||||
_add(c, attrs_solid)
|
||||
if dashed:
|
||||
dashed_idx = _ensure_linetype_dashed(doc)
|
||||
attrs_dashed = _make_attrs(dashed_idx)
|
||||
for c in dashed:
|
||||
try: doc.Objects.AddCurve(c, attrs_dashed)
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] add 2d dashed curve:", ex)
|
||||
_add(c, attrs_dashed)
|
||||
|
||||
|
||||
def _ensure_linetype_dashed(doc):
|
||||
@@ -7793,12 +7967,17 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
"uk": uk,
|
||||
"ok": ok,
|
||||
"hOver": meta.get("treppe_h_over", ""),
|
||||
"ukOver": meta.get("treppe_uk_over", ""),
|
||||
"soll": meta.get("treppe_soll", _TREPPE_SOLL_DEFAULT),
|
||||
"showTritte": bool(meta.get("treppe_show_tritte", True)),
|
||||
"showLauflinie": bool(meta.get("treppe_show_lauflinie", True)),
|
||||
"showAussen": bool(meta.get("treppe_show_aussen", True)),
|
||||
"showBruch": bool(meta.get("treppe_show_bruch", True)),
|
||||
"obereDashed": bool(meta.get("treppe_obere_dashed", False)),
|
||||
"arrowStyle": meta.get("treppe_arrow_style", "klassisch"),
|
||||
"lockS": bool(meta.get("treppe_lock_s", False)),
|
||||
"targetS": float(meta.get("treppe_target_s", 0.0)),
|
||||
"targetA": float(meta.get("treppe_target_a", 0.0)),
|
||||
})
|
||||
elif meta["type"] == "raum_outline":
|
||||
# Raum: Flaeche + Umfang aus der Outline-Curve
|
||||
@@ -7957,6 +8136,10 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
g_end = _geschoss_by_id(doc, src_meta.get("geschoss_end", ""))
|
||||
if g_start is not None:
|
||||
uk = float(g_start.get("okff", 0.0))
|
||||
uk_over = src_meta.get("treppe_uk_over", "")
|
||||
if uk_over:
|
||||
try: uk = uk + float(uk_over)
|
||||
except Exception: pass
|
||||
h_over = src_meta.get("treppe_h_over", "")
|
||||
if h_over:
|
||||
try: ok_v = uk + float(h_over)
|
||||
@@ -7988,11 +8171,16 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
"uk": uk,
|
||||
"ok": ok_v,
|
||||
"hOver": src_meta.get("treppe_h_over", ""),
|
||||
"ukOver": src_meta.get("treppe_uk_over", ""),
|
||||
"soll": src_meta.get("treppe_soll", _TREPPE_SOLL_DEFAULT),
|
||||
"showLauflinie": bool(src_meta.get("treppe_show_lauflinie", True)),
|
||||
"showAussen": bool(src_meta.get("treppe_show_aussen", True)),
|
||||
"showBruch": bool(src_meta.get("treppe_show_bruch", True)),
|
||||
"obereDashed": bool(src_meta.get("treppe_obere_dashed", True)),
|
||||
"arrowStyle": src_meta.get("treppe_arrow_style", "klassisch"),
|
||||
"lockS": bool(src_meta.get("treppe_lock_s", False)),
|
||||
"targetS": float(src_meta.get("treppe_target_s", 0.0)),
|
||||
"targetA": float(src_meta.get("treppe_target_a", 0.0)),
|
||||
})
|
||||
elements.append(base)
|
||||
except Exception as ex:
|
||||
@@ -8000,7 +8188,9 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
sel_id = next((e["id"] for e in elements if e["selected"]), None)
|
||||
payload = {
|
||||
"elements": elements,
|
||||
"geschosse": [{"id": g.get("id"), "name": g.get("name")}
|
||||
"geschosse": [{"id": g.get("id"), "name": g.get("name"),
|
||||
"okff": g.get("okff", 0),
|
||||
"hoehe": g.get("hoehe", 0)}
|
||||
for g in geschosse if isinstance(g, dict)],
|
||||
"selection": sel_id,
|
||||
"activeGeschoss": _active_geschoss_id(doc),
|
||||
@@ -11965,10 +12155,13 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
gs = _geschoss_by_id(doc, gstart)
|
||||
gn = gs.get("name", "EG") if gs else "EG"
|
||||
attrs.LayerIndex = _ensure_layer(doc, _layer_path_treppe(doc, gn))
|
||||
# Custom H + Soll-Werte
|
||||
# Custom H + UK + Soll-Werte
|
||||
h_over = p.get("hOver", old_meta.get("treppe_h_over", ""))
|
||||
if h_over is None: h_over = ""
|
||||
if isinstance(h_over, (int, float)): h_over = "{:.4f}".format(float(h_over))
|
||||
uk_over = p.get("ukOver", old_meta.get("treppe_uk_over", ""))
|
||||
if uk_over is None: uk_over = ""
|
||||
if isinstance(uk_over, (int, float)): uk_over = "{:.4f}".format(float(uk_over))
|
||||
soll_in = p.get("soll", old_meta.get("treppe_soll", _TREPPE_SOLL_DEFAULT))
|
||||
# Auf Form normieren
|
||||
if not isinstance(soll_in, dict): soll_in = dict(_TREPPE_SOLL_DEFAULT)
|
||||
@@ -11989,6 +12182,88 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
tshow_aus = _flag("showAussen", "treppe_show_aussen", True)
|
||||
tshow_bru = _flag("showBruch", "treppe_show_bruch", True)
|
||||
tobe_dsh = _flag("obereDashed", "treppe_obere_dashed", False)
|
||||
# Schrittmass-Lock: wenn aktiv, recompute n_stufen damit S
|
||||
# konstant bleibt. Beim Aktivieren via Patch wird targetS auf
|
||||
# aktuelles S gesetzt (sofern Patch keinen Wert mitgibt).
|
||||
tlock_s = _flag("lockS", "treppe_lock_s", False)
|
||||
old_target_s = float(old_meta.get("treppe_target_s", 0.0) or 0.0)
|
||||
old_target_a = float(old_meta.get("treppe_target_a", 0.0) or 0.0)
|
||||
ttarget_s = old_target_s
|
||||
ttarget_a = old_target_a
|
||||
# Aktuelle Axis-Laenge (fuer target_A wenn Lock gerade aktiviert)
|
||||
try:
|
||||
_g = axis_obj.Geometry
|
||||
cur_axis_L = float(_g.GetLength()) if isinstance(_g, rg.Curve) else 0.0
|
||||
except Exception: cur_axis_L = 0.0
|
||||
if "lockS" in p and bool(p.get("lockS")) and old_target_s <= 1e-6:
|
||||
gs_now = _geschoss_by_id(doc, gstart)
|
||||
uk_now = float(gs_now.get("okff", 0.0)) if gs_now else 0.0
|
||||
cur_uk = float(old_meta.get("treppe_uk_over") or 0)
|
||||
if cur_uk: uk_now += cur_uk
|
||||
ge_now = _geschoss_by_id(doc, gend)
|
||||
old_h_over = (old_meta.get("treppe_h_over") or "")
|
||||
if old_h_over:
|
||||
try: H_cur = float(old_h_over)
|
||||
except Exception: H_cur = 3.0
|
||||
elif ge_now is not None:
|
||||
H_cur = float(ge_now.get("okff", uk_now + 3.0)) - uk_now
|
||||
else:
|
||||
H_cur = float(gs_now.get("hoehe", 3.0)) if gs_now else 3.0
|
||||
cur_n = max(2, int(old_meta.get("treppe_n", 15)))
|
||||
ttarget_s = max(0.10, H_cur / cur_n)
|
||||
ttarget_a = max(0.10, cur_axis_L / cur_n) if cur_axis_L > 0.1 else 0.27
|
||||
elif "targetS" in p:
|
||||
try: ttarget_s = float(p["targetS"])
|
||||
except Exception: ttarget_s = old_target_s
|
||||
# Wenn Lock aktiv + targetS gueltig → n_stufen + Axis-Laenge anpassen
|
||||
if tlock_s and ttarget_s > 0.05:
|
||||
try:
|
||||
gs_now = _geschoss_by_id(doc, gstart)
|
||||
uk_now = float(gs_now.get("okff", 0.0)) if gs_now else 0.0
|
||||
if uk_over:
|
||||
try: uk_now += float(uk_over)
|
||||
except Exception: pass
|
||||
ge_now = _geschoss_by_id(doc, gend)
|
||||
if h_over:
|
||||
try: H_now = float(h_over)
|
||||
except Exception: H_now = 3.0
|
||||
elif ge_now is not None:
|
||||
H_now = float(ge_now.get("okff", uk_now + 3.0)) - uk_now
|
||||
else:
|
||||
H_now = float(gs_now.get("hoehe", 3.0)) if gs_now else 3.0
|
||||
if H_now > 0.1:
|
||||
tn = max(2, int(round(H_now / ttarget_s)))
|
||||
# Axis-Laenge so anpassen dass A = target_A bleibt
|
||||
# (nur fuer gerade Treppe = LineCurve)
|
||||
if ttarget_a > 0.05:
|
||||
new_L = tn * ttarget_a
|
||||
try:
|
||||
_g = axis_obj.Geometry
|
||||
if (isinstance(_g, rg.LineCurve)
|
||||
and abs(cur_axis_L - new_L) > 1e-4):
|
||||
P0 = _g.PointAtStart
|
||||
P1 = _g.PointAtEnd
|
||||
dx = P1.X - P0.X; dy = P1.Y - P0.Y
|
||||
if cur_axis_L > 1e-6:
|
||||
ux = dx / cur_axis_L
|
||||
uy = dy / cur_axis_L
|
||||
P1n = rg.Point3d(P0.X + ux * new_L,
|
||||
P0.Y + uy * new_L, P0.Z)
|
||||
new_line = rg.LineCurve(P0, P1n)
|
||||
doc.Objects.Replace(axis_obj.Id, new_line)
|
||||
# axis_obj-Reference neu holen
|
||||
axis_obj = doc.Objects.Find(axis_obj.Id)
|
||||
attrs = axis_obj.Attributes
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] lock axis-len adjust:", ex)
|
||||
print("[ELEMENTE] Lock: H={:.3f}/S={:.3f}→n={}, A={:.3f}".format(
|
||||
H_now, ttarget_s, tn, ttarget_a))
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] lock recompute:", ex)
|
||||
tarr = p.get("arrowStyle",
|
||||
old_meta.get("treppe_arrow_style", "klassisch"))
|
||||
if tarr not in ("klassisch", "filled", "breit", "voll"):
|
||||
tarr = "klassisch"
|
||||
_attach_meta(attrs, wall_id, "treppe_axis",
|
||||
gstart, tb, "", "", "mid",
|
||||
geschoss_end=gend,
|
||||
@@ -11999,12 +12274,17 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
treppe_lauf_d=tld,
|
||||
treppe_art=old_meta.get("treppe_art", "gerade"),
|
||||
treppe_h_over=h_over,
|
||||
treppe_uk_over=uk_over,
|
||||
treppe_soll=soll_norm,
|
||||
treppe_show_tritte=tshow_tri,
|
||||
treppe_show_lauflinie=tshow_lau,
|
||||
treppe_show_aussen=tshow_aus,
|
||||
treppe_show_bruch=tshow_bru,
|
||||
treppe_obere_dashed=tobe_dsh)
|
||||
treppe_obere_dashed=tobe_dsh,
|
||||
treppe_arrow_style=tarr,
|
||||
treppe_lock_s=tlock_s,
|
||||
treppe_target_s=ttarget_s,
|
||||
treppe_target_a=ttarget_a)
|
||||
# Persistenz fuer Creation Default
|
||||
try:
|
||||
import json
|
||||
@@ -12729,6 +13009,7 @@ def _on_object_replaced_body(sender, e):
|
||||
treppe_lauf_d=meta.get("treppe_lauf_d"),
|
||||
treppe_art=meta.get("treppe_art"),
|
||||
treppe_h_over=meta.get("treppe_h_over"),
|
||||
treppe_uk_over=meta.get("treppe_uk_over"),
|
||||
treppe_soll=meta.get("treppe_soll"),
|
||||
trag_kind=meta.get("trag_kind") or None,
|
||||
trag_profil=meta.get("trag_profil") or None,
|
||||
@@ -15127,6 +15408,12 @@ def _install_listeners(bridge):
|
||||
wand_grips.install_handlers()
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] wand_grips install:", ex)
|
||||
# Treppen-Endpoint-Marker (visuell only — Drag laeuft via Partnership)
|
||||
try:
|
||||
import treppe_grips
|
||||
treppe_grips.install_handlers()
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] treppe_grips install:", ex)
|
||||
# Schnittsymbol-Endpoint-Grips — analoges Overlay an den P1/P2 der
|
||||
# Schnittlinie. Ziehen updated linePts + regeneriert das Symbol +
|
||||
# re-aktiviert den Schnitt wenn aktiv.
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
#! python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (C) 2026 Karim Gabriele Varano
|
||||
"""
|
||||
treppe_grips.py
|
||||
Display-Conduit fuer gruene Endpunkt-Marker an Treppen-Achsen. Visuelle
|
||||
Indikation wie bei Waenden, aber keine eigene Drag-Logik — der normale
|
||||
Partnership-Cascade (elemente._on_select_objects) + Pure-Transform-Pfad
|
||||
verschieben die Treppe bereits sauber.
|
||||
|
||||
Endpunkt-Logik pro Treppen-Art:
|
||||
- gerade : PointAtStart, PointAtEnd der Linie
|
||||
- L : poly[0] (Start), poly[2] (Ende) — poly[1] ist der Eck-Punkt
|
||||
- Wendel : poly[1] (Start), poly[2] (Ende) — poly[0] ist Rotations-
|
||||
zentrum, nicht der Treppen-Anfang
|
||||
"""
|
||||
import Rhino
|
||||
import Rhino.Display as rd
|
||||
import Rhino.Geometry as rg
|
||||
import scriptcontext as sc
|
||||
import System.Drawing as SD
|
||||
|
||||
|
||||
_MARKER_RADIUS_PX = 7
|
||||
_MARKER_FILL = SD.Color.FromArgb(220, 95, 168, 150) # petrol-gruen, gleich wie wand_grips
|
||||
_MARKER_BORDER = SD.Color.FromArgb(255, 47, 93, 84)
|
||||
|
||||
|
||||
def _treppe_endpoints(axis_obj):
|
||||
"""Liefert Liste von Point3d fuer Treppen-Start + -Ende. Beachtet
|
||||
treppe_art (Wendel hat anderes Polyline-Schema)."""
|
||||
if axis_obj is None or axis_obj.IsDeleted: return []
|
||||
a = axis_obj.Attributes
|
||||
if a.GetUserString("dossier_element_type") != "treppe_axis": return []
|
||||
geom = axis_obj.Geometry
|
||||
if not isinstance(geom, rg.Curve): return []
|
||||
art = a.GetUserString("dossier_treppe_art") or "gerade"
|
||||
try:
|
||||
if art == "wendel":
|
||||
ok, poly = geom.TryGetPolyline()
|
||||
if not ok or poly is None or poly.Count != 3: return []
|
||||
return [poly[1], poly[2]]
|
||||
# gerade + L → Start- und End-Punkt der Curve sind die Treppen-Enden
|
||||
return [geom.PointAtStart, geom.PointAtEnd]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
class _TreppeEndpointConduit(rd.DisplayConduit):
|
||||
"""Zeichnet gruene Endpunkt-Marker an allen selektierten Treppen-Achsen."""
|
||||
|
||||
def DrawForeground(self, e):
|
||||
try:
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
if doc is None: return
|
||||
sel = list(doc.Objects.GetSelectedObjects(False, False))
|
||||
seen = set()
|
||||
for obj in sel:
|
||||
a = obj.Attributes
|
||||
eid = a.GetUserString("dossier_element_id") or ""
|
||||
if not eid or eid in seen: continue
|
||||
# Source-Axis via element_id finden (kann anderer Obj sein
|
||||
# wenn User nur Volume oder 2D-Symbol selektiert hat)
|
||||
axis = None
|
||||
for o in doc.Objects:
|
||||
if o is None or o.IsDeleted: continue
|
||||
try:
|
||||
a2 = o.Attributes
|
||||
if a2.GetUserString("dossier_element_id") == eid and \
|
||||
a2.GetUserString("dossier_element_type") == "treppe_axis":
|
||||
axis = o; break
|
||||
except Exception: continue
|
||||
if axis is None: continue
|
||||
seen.add(eid)
|
||||
for pt in _treppe_endpoints(axis):
|
||||
try:
|
||||
e.Display.DrawPoint(pt,
|
||||
rd.PointStyle.RoundControlPoint,
|
||||
_MARKER_RADIUS_PX, _MARKER_FILL)
|
||||
except Exception:
|
||||
try: e.Display.DrawDot(pt, "●", _MARKER_FILL, _MARKER_BORDER)
|
||||
except Exception: pass
|
||||
except Exception as ex:
|
||||
print("[TREPPE_GRIPS] DrawForeground:", ex)
|
||||
|
||||
|
||||
_STICKY_CONDUIT = "_dossier_treppe_grips_conduit"
|
||||
|
||||
|
||||
def install_handlers():
|
||||
"""Idempotente Registrierung. Bei Modul-Reload alten Conduit zuerst
|
||||
disablen, dann neuen anhaengen."""
|
||||
try:
|
||||
old = sc.sticky.get(_STICKY_CONDUIT)
|
||||
if old is not None:
|
||||
try: old.Enabled = False
|
||||
except Exception: pass
|
||||
conduit = _TreppeEndpointConduit()
|
||||
conduit.Enabled = True
|
||||
sc.sticky[_STICKY_CONDUIT] = conduit
|
||||
print("[TREPPE_GRIPS] Endpoint-Conduit aktiv")
|
||||
except Exception as ex:
|
||||
print("[TREPPE_GRIPS] install:", ex)
|
||||
+209
-72
@@ -2035,6 +2035,7 @@ function TreppeProperties({ treppe, geschosse, onUpdate, onDelete }) {
|
||||
const [nStufen, setNStufen] = useState(String(treppe.nStufen ?? 15))
|
||||
const [laufD, setLaufD] = useState(String(treppe.laufD ?? 0.18))
|
||||
const [hStr, setHStr] = useState('')
|
||||
const [ukStr, setUkStr] = useState('')
|
||||
useEffect(() => {
|
||||
setBreite(String(treppe.breite ?? 1.0))
|
||||
setNStufen(String(treppe.nStufen ?? 15))
|
||||
@@ -2049,9 +2050,27 @@ function TreppeProperties({ treppe, geschosse, onUpdate, onDelete }) {
|
||||
const sa = 2 * S + A
|
||||
const soll = treppe.soll || DEFAULT_TREPPE_SOLL
|
||||
const hasHOver = treppe.hOver != null && treppe.hOver !== ''
|
||||
const hasUkOver = treppe.ukOver != null && treppe.ukOver !== ''
|
||||
useEffect(() => {
|
||||
setHStr(hasHOver ? String(treppe.hOver) : fmtNum(H))
|
||||
}, [treppe.id, treppe.hOver, H, hasHOver])
|
||||
useEffect(() => {
|
||||
setUkStr(hasUkOver ? String(treppe.ukOver) : '')
|
||||
}, [treppe.id, treppe.ukOver, hasUkOver])
|
||||
const onCommitUk = () => {
|
||||
const trimmed = (ukStr || '').trim()
|
||||
if (trimmed === '') {
|
||||
if (hasUkOver) onUpdate({ ukOver: '' })
|
||||
return
|
||||
}
|
||||
const v = parseFloat(trimmed)
|
||||
if (Number.isNaN(v)) { setUkStr(hasUkOver ? String(treppe.ukOver) : ''); return }
|
||||
if (Math.abs(v) < 1e-6) {
|
||||
if (hasUkOver) onUpdate({ ukOver: '' })
|
||||
} else if (Math.abs(v - (parseFloat(treppe.ukOver) || 0)) > 1e-5) {
|
||||
onUpdate({ ukOver: v })
|
||||
}
|
||||
}
|
||||
|
||||
const allOK = (
|
||||
(!soll.s[2] || (S >= soll.s[0] && S <= soll.s[1])) &&
|
||||
@@ -2075,17 +2094,36 @@ function TreppeProperties({ treppe, geschosse, onUpdate, onDelete }) {
|
||||
|
||||
const ref = treppe.treppeReferenz ?? 'mid'
|
||||
const REF_OPTIONS = [
|
||||
{ code: 'links', label: 'Links' },
|
||||
{ code: 'mid', label: 'Mittig' },
|
||||
{ code: 'rechts', label: 'Rechts' },
|
||||
{ code: 'links', label: 'links' },
|
||||
{ code: 'mid', label: 'mittig' },
|
||||
{ code: 'rechts', label: 'rechts' },
|
||||
]
|
||||
const modus = treppe.treppeModus ?? 'flach'
|
||||
const MODUS_OPTIONS = [
|
||||
{ code: 'massiv', label: 'massiv', hint: 'Block bis zum Boden — wie eine Mauer unter der Treppe' },
|
||||
{ code: 'flach', label: 'flach', hint: 'Schräge Plattenunterseite parallel zum Treppenlauf (realistisch)' },
|
||||
{ code: 'flach', label: 'flach', hint: 'Schräge Plattenunterseite parallel zum Treppenlauf' },
|
||||
{ code: 'plattenrand', label: 'gestuft', hint: 'Plattenunterseite folgt den Stufen, vertikal versetzt' },
|
||||
]
|
||||
|
||||
// Konsistentes Grid: label(50) | control(1fr) | unit(14)
|
||||
const rowStyle = {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '50px 1fr 14px',
|
||||
alignItems: 'center', gap: 6,
|
||||
}
|
||||
const labelStyle = {
|
||||
fontSize: 10, color: 'var(--text-secondary)',
|
||||
}
|
||||
const unitStyle = {
|
||||
fontSize: 10, color: 'var(--text-muted)', textAlign: 'left',
|
||||
}
|
||||
const inputStyle = {
|
||||
fontSize: 11, fontFamily: 'DM Mono, monospace', width: '100%',
|
||||
}
|
||||
const selectStyle = {
|
||||
fontSize: 11, width: '100%', // Dropdowns nutzen System-Font (lesbar bei Worten)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', gap: 8,
|
||||
@@ -2103,38 +2141,105 @@ function TreppeProperties({ treppe, geschosse, onUpdate, onDelete }) {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 50 }}>Start</span>
|
||||
<div style={rowStyle}>
|
||||
<span style={labelStyle}>Start</span>
|
||||
<select value={treppe.geschoss}
|
||||
onChange={(e) => onUpdate({ geschoss: e.target.value })}
|
||||
style={{ flex: 1, fontSize: 11 }}>
|
||||
{geschosse.map(g => <option key={g.id} value={g.id}>{g.name}</option>)}
|
||||
style={selectStyle}
|
||||
title="Start-Geschoss — kann nicht hoeher als das Ziel-Geschoss sein">
|
||||
{(() => {
|
||||
// Ziel-Z bestimmen: aus Ziel-Geschoss oder aus hOver+startOkff+ukOver
|
||||
let zielZ = null
|
||||
if (treppe.geschossEnd) {
|
||||
const g = geschosse.find(x => x.id === treppe.geschossEnd)
|
||||
if (g) zielZ = Number(g.okff || 0)
|
||||
} else if (treppe.hOver) {
|
||||
const startG = geschosse.find(x => x.id === treppe.geschoss)
|
||||
const startOkff = startG ? Number(startG.okff || 0) : 0
|
||||
const ukO = Number(treppe.ukOver || 0)
|
||||
zielZ = startOkff + ukO + Number(treppe.hOver)
|
||||
}
|
||||
return geschosse
|
||||
.filter(g => zielZ === null || Number(g.okff || 0) < zielZ)
|
||||
.map(g => <option key={g.id} value={g.id}>{g.name}</option>)
|
||||
})()}
|
||||
</select>
|
||||
<span style={unitStyle}></span>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 50 }}>Ziel</span>
|
||||
<div style={rowStyle}
|
||||
title="Vertikaler Versatz des Treppen-Anfangs (relativ zum Geschoss-OKFF)">
|
||||
<span style={labelStyle}>Versatz</span>
|
||||
{hasUkOver ? (
|
||||
<div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
|
||||
<input type="text" value={ukStr}
|
||||
placeholder="0.00"
|
||||
onChange={(e) => setUkStr(e.target.value)}
|
||||
onBlur={onCommitUk}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
||||
title="Versatz relativ zum Geschoss-OKFF"
|
||||
style={{ ...inputStyle, flex: 1,
|
||||
border: '1px solid var(--accent)' }} />
|
||||
<button onClick={() => onUpdate({ ukOver: '' })}
|
||||
title="Zurueck zu Geschoss-OKFF"
|
||||
style={{ fontSize: 11, padding: '0 6px',
|
||||
background: 'transparent', border: 'none',
|
||||
color: 'var(--text-muted)', cursor: 'pointer' }}>×</button>
|
||||
</div>
|
||||
) : (
|
||||
<select value=""
|
||||
onChange={(e) => {
|
||||
if (e.target.value === '__custom__') onUpdate({ ukOver: 0 })
|
||||
}}
|
||||
style={selectStyle}>
|
||||
<option value="">(Geschoss-OKFF)</option>
|
||||
<option value="__custom__">eigenes Z…</option>
|
||||
</select>
|
||||
)}
|
||||
<span style={unitStyle}>{hasUkOver ? 'm' : ''}</span>
|
||||
</div>
|
||||
|
||||
<div style={rowStyle}>
|
||||
<span style={labelStyle}>Ziel</span>
|
||||
{hasHOver ? (
|
||||
<div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
|
||||
<input type="text" value={hStr}
|
||||
placeholder="1.50"
|
||||
onChange={(e) => setHStr(e.target.value)}
|
||||
onBlur={onCommitH}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
||||
title="Treppen-Höhe (Delta Start → Ende)"
|
||||
style={{ ...inputStyle, flex: 1,
|
||||
border: '1px solid var(--accent)' }} />
|
||||
<button onClick={() => onUpdate({ hOver: '' })}
|
||||
title="Zurueck zu Geschoss-Verknuepfung"
|
||||
style={{ fontSize: 11, padding: '0 6px',
|
||||
background: 'transparent', border: 'none',
|
||||
color: 'var(--text-muted)', cursor: 'pointer' }}>×</button>
|
||||
</div>
|
||||
) : (
|
||||
<select
|
||||
value={hasHOver ? '__custom__' : (treppe.geschossEnd || '')}
|
||||
value={treppe.geschossEnd || ''}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
if (v === '__custom__') {
|
||||
// Eigene Hoehe — falls noch nicht gesetzt, mit aktuellem H starten
|
||||
onUpdate({ hOver: H, geschossEnd: '' })
|
||||
} else {
|
||||
onUpdate({ geschossEnd: v, hOver: '' })
|
||||
}
|
||||
}}
|
||||
style={{ flex: 1, fontSize: 11 }}>
|
||||
style={selectStyle}>
|
||||
<option value="">(auto: Start + Höhe)</option>
|
||||
{geschosse.filter(g => g.id !== treppe.geschoss)
|
||||
.map(g => <option key={g.id} value={g.id}>{g.name}</option>)}
|
||||
<option value="__custom__">eigene Höhe</option>
|
||||
<option value="__custom__">eigene Höhe…</option>
|
||||
</select>
|
||||
)}
|
||||
<span style={unitStyle}>{hasHOver ? 'm' : ''}</span>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 50 }}>Breite</span>
|
||||
<div style={rowStyle}>
|
||||
<span style={labelStyle}>Breite</span>
|
||||
<input type="text" value={breite}
|
||||
onChange={(e) => setBreite(e.target.value)}
|
||||
onBlur={() => {
|
||||
@@ -2143,62 +2248,68 @@ function TreppeProperties({ treppe, geschosse, onUpdate, onDelete }) {
|
||||
else setBreite(String(treppe.breite))
|
||||
}}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
||||
style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
|
||||
<span style={{ fontSize: 10, color: 'var(--text-muted)' }}>m</span>
|
||||
style={inputStyle} />
|
||||
<span style={unitStyle}>m</span>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 50 }}>Stufen</span>
|
||||
<input type="text" value={nStufen}
|
||||
onChange={(e) => setNStufen(e.target.value)}
|
||||
onBlur={() => {
|
||||
const v = parseInt(nStufen, 10)
|
||||
if (Number.isFinite(v) && v >= 2 && v <= 40) onUpdate({ nStufen: v })
|
||||
else setNStufen(String(treppe.nStufen))
|
||||
}}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
||||
style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
|
||||
<span style={{ fontSize: 10, color: 'var(--text-muted)' }}>×</span>
|
||||
<div style={rowStyle} title={treppe.lockS
|
||||
? 'Mit Trittmaß-Lock: nur Anzahlen die ein S-Werte nahe der Sollhöhe ergeben'
|
||||
: 'Anzahl Tritte (2-40)'}>
|
||||
<span style={labelStyle}>Stufen</span>
|
||||
<select value={treppe.nStufen}
|
||||
onChange={(e) => onUpdate({ nStufen: parseInt(e.target.value, 10) })}
|
||||
style={selectStyle}>
|
||||
{(() => {
|
||||
// Mit Lock: filtere die N-Werte deren resultierendes S nahe an
|
||||
// target_S liegt (±10%). Sonst 2-40.
|
||||
const range = []
|
||||
for (let i = 2; i <= 40; i++) range.push(i)
|
||||
if (treppe.lockS && treppe.targetS > 0.05 && H > 0.1) {
|
||||
const tgt = Number(treppe.targetS)
|
||||
const tol = tgt * 0.10
|
||||
return range
|
||||
.filter(n => Math.abs(H / n - tgt) <= tol)
|
||||
.map(n => (
|
||||
<option key={n} value={n}>
|
||||
{n} (S={(H / n).toFixed(3)} m)
|
||||
</option>
|
||||
))
|
||||
}
|
||||
return range.map(n => <option key={n} value={n}>{n}</option>)
|
||||
})()}
|
||||
</select>
|
||||
<span style={unitStyle}>×</span>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 50 }}>Lage</span>
|
||||
<div style={{ flex: 1, display: 'flex', gap: 3 }}>
|
||||
<div style={rowStyle}>
|
||||
<span style={labelStyle}>Lage</span>
|
||||
<select value={ref}
|
||||
onChange={(e) => onUpdate({ treppeReferenz: e.target.value })}
|
||||
style={selectStyle}>
|
||||
{REF_OPTIONS.map(o => (
|
||||
<BarToggle key={o.code}
|
||||
label={o.label}
|
||||
active={ref === o.code}
|
||||
onClick={() => onUpdate({ treppeReferenz: o.code })} />
|
||||
<option key={o.code} value={o.code}>{o.label}</option>
|
||||
))}
|
||||
</div>
|
||||
</select>
|
||||
<span style={unitStyle}></span>
|
||||
</div>
|
||||
|
||||
<div style={{ height: 1, background: 'var(--border-light)', margin: '2px 0' }} />
|
||||
|
||||
{/* Unterseite-Modus */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 50 }}
|
||||
title="Form der Treppen-Unterseite">
|
||||
Unten
|
||||
</span>
|
||||
<div style={{ flex: 1, display: 'flex', gap: 3 }}>
|
||||
<div style={rowStyle} title="Form der Treppen-Unterseite">
|
||||
<span style={labelStyle}>Unten</span>
|
||||
<select value={modus}
|
||||
onChange={(e) => onUpdate({ treppeModus: e.target.value })}
|
||||
style={selectStyle}>
|
||||
{MODUS_OPTIONS.map(o => (
|
||||
<BarToggle key={o.code}
|
||||
label={o.label}
|
||||
active={modus === o.code}
|
||||
onClick={() => onUpdate({ treppeModus: o.code })}
|
||||
title={o.hint} />
|
||||
<option key={o.code} value={o.code} title={o.hint}>{o.label}</option>
|
||||
))}
|
||||
</div>
|
||||
</select>
|
||||
<span style={unitStyle}></span>
|
||||
</div>
|
||||
|
||||
{/* Lauf-Plattendicke (nur fuer flach + plattenrand relevant) */}
|
||||
{modus !== 'massiv' && (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 50 }}
|
||||
title="Dicke der Lauf-Platte (Materialdicke unter den Stufen)">
|
||||
Platte
|
||||
</span>
|
||||
<div style={rowStyle} title="Dicke der Lauf-Platte (Materialdicke unter den Stufen)">
|
||||
<span style={labelStyle}>Platte</span>
|
||||
<input type="text" value={laufD}
|
||||
onChange={(e) => setLaufD(e.target.value)}
|
||||
onBlur={() => {
|
||||
@@ -2207,8 +2318,8 @@ function TreppeProperties({ treppe, geschosse, onUpdate, onDelete }) {
|
||||
else setLaufD(String(treppe.laufD))
|
||||
}}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
||||
style={{ flex: 1, fontSize: 11, fontFamily: 'DM Mono, monospace' }} />
|
||||
<span style={{ fontSize: 10, color: 'var(--text-muted)' }}>m</span>
|
||||
style={inputStyle} />
|
||||
<span style={unitStyle}>m</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -2219,24 +2330,41 @@ function TreppeProperties({ treppe, geschosse, onUpdate, onDelete }) {
|
||||
letterSpacing: '0.06em', textTransform: 'uppercase' }}>
|
||||
2D-Plansymbol
|
||||
</span>
|
||||
{[
|
||||
['showLauflinie', 'Lauflinie'],
|
||||
['showAussen', 'Außenlinie'],
|
||||
['showBruch', 'Bruchsymbol'],
|
||||
].map(([key, label]) => {
|
||||
const on = treppe[key] !== false
|
||||
return (
|
||||
<label key={key} style={{
|
||||
<label style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
fontSize: 11, cursor: 'pointer',
|
||||
}}>
|
||||
<input type="checkbox" checked={on}
|
||||
onChange={(e) => onUpdate({ [key]: e.target.checked })}
|
||||
<input type="checkbox" checked={treppe.showLauflinie !== false}
|
||||
onChange={(e) => onUpdate({ showLauflinie: e.target.checked })}
|
||||
style={{ accentColor: 'var(--accent)' }} />
|
||||
<span>{label}</span>
|
||||
<span>Lauflinie</span>
|
||||
</label>
|
||||
{treppe.showLauflinie !== false && (
|
||||
<div style={{
|
||||
display: 'grid', gridTemplateColumns: '50px 1fr 14px',
|
||||
alignItems: 'center', gap: 6, marginLeft: 18,
|
||||
}}>
|
||||
<span style={labelStyle}>Pfeil</span>
|
||||
<select value={treppe.arrowStyle || 'klassisch'}
|
||||
onChange={(e) => onUpdate({ arrowStyle: e.target.value })}
|
||||
style={selectStyle}>
|
||||
<option value="klassisch">klassisch</option>
|
||||
<option value="filled">gefüllt</option>
|
||||
<option value="breit">breit</option>
|
||||
<option value="voll">voll (bis zu den Seiten)</option>
|
||||
</select>
|
||||
<span style={unitStyle}></span>
|
||||
</div>
|
||||
)}
|
||||
<label style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
fontSize: 11, cursor: 'pointer',
|
||||
}}>
|
||||
<input type="checkbox" checked={treppe.showBruch !== false}
|
||||
onChange={(e) => onUpdate({ showBruch: e.target.checked })}
|
||||
style={{ accentColor: 'var(--accent)' }} />
|
||||
<span>Bruchsymbol</span>
|
||||
</label>
|
||||
)
|
||||
})}
|
||||
<label style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
fontSize: 10, cursor: 'pointer',
|
||||
@@ -2283,6 +2411,15 @@ function TreppeProperties({ treppe, geschosse, onUpdate, onDelete }) {
|
||||
}}>auto</button>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}
|
||||
title="Wenn an: Beim Aendern der Hoehe (oder Versatz/Ziel) wird die Anzahl Stufen automatisch nachgerechnet, damit S konstant bleibt.">
|
||||
<input type="checkbox" checked={!!treppe.lockS}
|
||||
onChange={(e) => onUpdate({ lockS: e.target.checked })}
|
||||
style={{ accentColor: 'var(--accent)', width: 12, height: 12 }} />
|
||||
<span style={{ fontSize: 10, color: 'var(--text-muted)' }}>
|
||||
Trittmaß fixiert (S{treppe.targetS ? ' = ' + Number(treppe.targetS).toFixed(3) + ' m' : ''})
|
||||
</span>
|
||||
</div>
|
||||
<SollRow label="S" value={S} unit="m" soll={soll} sollKey="s"
|
||||
onUpdateSoll={onUpdateSoll} />
|
||||
<SollRow label="A" value={A} unit="m" soll={soll} sollKey="a"
|
||||
|
||||
Reference in New Issue
Block a user