Treppe L+Wendel: Lauflinie, Aussen-Polygon, Pfeil-voll, Lock + Cmd+Z
Cmd+Z: - _update_wall wrapped in BeginUndoRecord/EndUndoRecord — sodass Property-Patches + Regen-Delete/Add als ein Undo-Schritt rueckgaengig L-Treppe Aussenlinie: - _aussen_l_polygon: sauberes 6-Punkt L-Polygon mit korrekt projizierten Ecken (Outer + Inner via Linien-Schnitt der mid-versetzten Seiten) - _aussen_l: nutzt Polygon wenn kein Cut, faellt sonst zurueck auf per-Lauf Rechtecke mit Diagonal-Cut (wie bisher) L-Treppe Lauflinie: - ueber BEIDE Laeufe, mid-perp versetzt, Pfeil am Treppen-Ende - Eck-Mitte als Linien-Schnitt der zwei versetzten Schaft-Linien → sauberer Übergang auch bei Lage=links/rechts (kein Versatz an der Ecke) - 'voll'-Pfeil-Style mit wide-Offsets relativ zur Lauflinie Wendel-Treppe: - _lauflinie_wendel: 'voll'-Pfeil-Style mit r_inner/r_outer-Spitzen relativ zu r_mid (Radial-Offsets) Trittmass-Lock auf alle Treppen-Arten: - L: Beide Laeufe proportional skalieren (ratio = N*target_A / (L1+L2)) - Wendel: Sweep-Winkel anpassen (new_delta = sign × N*target_A/r_mid) - Axis-Geometrie wird in-place via Replace ausgetauscht — Source moves fliessen in regulären Regen-Pfad ein
This commit is contained in:
+186
-21
@@ -6374,36 +6374,115 @@ 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
|
||||||
|
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."""
|
||||||
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 = segs
|
||||||
lo, _up = _lauflinie_gerade(s1, N1, z, -1, False, arrow_style, doc,
|
P0a = s1.PointAtStart; P1a = s1.PointAtEnd
|
||||||
breite, referenz)
|
txa, tya = P1a.X - P0a.X, P1a.Y - P0a.Y
|
||||||
return lo
|
La = (txa * txa + tya * tya) ** 0.5
|
||||||
|
P0b = s2.PointAtStart; P1b = s2.PointAtEnd
|
||||||
|
txb, tyb = P1b.X - P0b.X, P1b.Y - P0b.Y
|
||||||
|
Lb = (txb * txb + tyb * tyb) ** 0.5
|
||||||
|
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:
|
||||||
|
off_l, off_r = _treppe_2d_side_offsets(breite, referenz)
|
||||||
|
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
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
meet = P1a_s # = P0b_s wenn mid=0
|
||||||
|
out = []
|
||||||
|
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, die beiden Laeufe schliessen
|
||||||
|
sich an der Ecke an (Outer/Inner via Linien-Schnitt). Returnt eine
|
||||||
|
Polyline ODER None bei Singularitaet (parallele Schenkel)."""
|
||||||
|
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)
|
||||||
|
# perp 90° CCW
|
||||||
|
pp1 = (-u1.Y, u1.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)
|
||||||
|
# Lauf1 Seiten (start + corner)
|
||||||
|
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)
|
||||||
|
# Lauf2 Seiten (corner + end)
|
||||||
|
pc_l2 = _off(pc, pp2, off_l); pc_r2 = _off(pc, pp2, off_r)
|
||||||
|
p1_l = _off(p1, pp2, off_l); p1_r = _off(p1, pp2, off_r)
|
||||||
|
# Ecken-Schnitte: linker Schenkel intersect, rechter Schenkel intersect
|
||||||
|
corner_l = _line_intersect_xy(p0_l, u1, p1_l, u2) or pc_l1
|
||||||
|
corner_r = _line_intersect_xy(p0_r, u1, p1_r, u2) or pc_r1
|
||||||
|
pts = [p0_l, p0_r, corner_r, p1_r, p1_l, corner_l, p0_l]
|
||||||
|
return rg.PolylineCurve(rg.Polyline(pts))
|
||||||
|
|
||||||
|
|
||||||
def _aussen_l(axis_polyline, breite, referenz, z,
|
def _aussen_l(axis_polyline, breite, referenz, z,
|
||||||
total_h=0.0, cut_h=0.0, n_stufen=15):
|
total_h=0.0, cut_h=0.0, n_stufen=15):
|
||||||
"""Returnt (lower, upper). Bei Cut: betroffener Lauf wird gesplittet,
|
"""Returnt (lower, upper). Ohne Cut: sauberes L-Polygon (eine Outline).
|
||||||
anderer Lauf komplett lower bzw. upper."""
|
Mit Cut: faellt zurueck auf Per-Lauf-Rechtecke mit Diagonal-Cut auf der
|
||||||
|
betroffenen Seite — der jeweils andere Lauf wird komplett lower bzw.
|
||||||
|
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 = 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
|
||||||
|
cut2 = _cut_idx_gerade(N2, H2, cut_h - H1) if total_h > 0 else -1
|
||||||
|
# Ohne Cut: zeichne sauberes L-Polygon
|
||||||
|
if cut1 < 0 and cut2 < 0:
|
||||||
|
poly = _aussen_l_polygon(axis_polyline, breite, referenz, z)
|
||||||
|
if poly is not None:
|
||||||
|
return [poly], []
|
||||||
|
# Fallback: zwei Rechtecke
|
||||||
|
l1, _ = _aussen_gerade(s1, breite, referenz, z, -1, None)
|
||||||
|
l2, _ = _aussen_gerade(s2, breite, referenz, z, -1, None)
|
||||||
|
return l1 + l2, []
|
||||||
|
# Mit Cut: per-Lauf, betroffener Lauf wird diagonal gesplittet
|
||||||
if cut1 >= 0:
|
if cut1 >= 0:
|
||||||
l1, u1 = _aussen_gerade(s1, breite, referenz, z, cut1, N1)
|
l1, u1 = _aussen_gerade(s1, breite, referenz, z, cut1, N1)
|
||||||
l2, _ = _aussen_gerade(s2, breite, referenz, z, -1, None)
|
l2, _ = _aussen_gerade(s2, breite, referenz, z, -1, None)
|
||||||
return l1, u1 + l2
|
return l1, u1 + l2
|
||||||
cut2 = _cut_idx_gerade(N2, H2, cut_h - H1) if total_h > 0 else -1
|
|
||||||
if cut2 >= 0:
|
|
||||||
l1, _ = _aussen_gerade(s1, breite, referenz, z, -1, None)
|
|
||||||
l2, u2 = _aussen_gerade(s2, breite, referenz, z, cut2, N2)
|
|
||||||
return l1 + l2, u2
|
|
||||||
l1, _ = _aussen_gerade(s1, breite, referenz, z, -1, None)
|
l1, _ = _aussen_gerade(s1, breite, referenz, z, -1, None)
|
||||||
l2, _ = _aussen_gerade(s2, breite, referenz, z, -1, None)
|
l2, u2 = _aussen_gerade(s2, breite, referenz, z, cut2, N2)
|
||||||
return l1 + l2, []
|
return l1 + l2, u2
|
||||||
|
|
||||||
|
|
||||||
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):
|
||||||
@@ -6487,7 +6566,13 @@ def _lauflinie_wendel(axis_polyline, breite, referenz, n_stufen, z,
|
|||||||
head_len = min(0.25, abs(r_mid * da) * 0.6)
|
head_len = min(0.25, abs(r_mid * da) * 0.6)
|
||||||
tail = rg.Point3d(arc_end.X - tang_x * head_len * 0.3,
|
tail = rg.Point3d(arc_end.X - tang_x * head_len * 0.3,
|
||||||
arc_end.Y - tang_y * head_len * 0.3, z)
|
arc_end.Y - tang_y * head_len * 0.3, z)
|
||||||
out.extend(_treppe_2d_arrow_curves(tail, arc_end, head_len, arrow_style, doc))
|
# 'voll'-Style: Spitzen-Offsets relativ zum r_mid (Tangenten-perp =
|
||||||
|
# radial). Innen → negativ, Aussen → positiv (in perp-Richtung
|
||||||
|
# CCW vom Tangenten-Vektor — entspricht der Radial-Richtung).
|
||||||
|
wide_in = r_inner - r_mid # negativ
|
||||||
|
wide_out = r_outer - r_mid # positiv
|
||||||
|
out.extend(_treppe_2d_arrow_curves(tail, arc_end, head_len, arrow_style,
|
||||||
|
doc, wide_in, wide_out))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[ELEMENTE] _lauflinie_wendel:", ex)
|
print("[ELEMENTE] _lauflinie_wendel:", ex)
|
||||||
return out
|
return out
|
||||||
@@ -11907,12 +11992,30 @@ class ElementeBridge(panel_base.BaseBridge):
|
|||||||
|
|
||||||
def _update_wall(self, p):
|
def _update_wall(self, p):
|
||||||
"""Properties eines Elements aendern (Wand/Decke/Dach/Oeffnung).
|
"""Properties eines Elements aendern (Wand/Decke/Dach/Oeffnung).
|
||||||
Volumen wird anschliessend regeneriert."""
|
Volumen wird anschliessend regeneriert.
|
||||||
|
Wird in EINEM Undo-Record zusammengefasst, sodass Cmd+Z die ganze
|
||||||
|
Patch-Operation inkl. Regen rueckgaengig macht (statt nur den
|
||||||
|
letzten AddBrep)."""
|
||||||
doc = Rhino.RhinoDoc.ActiveDoc
|
doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
wall_id = p.get("id")
|
wall_id = p.get("id")
|
||||||
if not wall_id: return
|
if not wall_id: return
|
||||||
axis_obj, old_meta = _find_source(doc, wall_id)
|
axis_obj, old_meta = _find_source(doc, wall_id)
|
||||||
if axis_obj is None or old_meta is None: return
|
if axis_obj is None or old_meta is None: return
|
||||||
|
# Undo-Record umschliesst Source-Mods + Regen-Delete/Add
|
||||||
|
_ur_serial = None
|
||||||
|
try:
|
||||||
|
if doc is not None:
|
||||||
|
_ur_serial = doc.BeginUndoRecord("Dossier: Element-Update")
|
||||||
|
except Exception: _ur_serial = None
|
||||||
|
try:
|
||||||
|
return self._update_wall_body(doc, p, wall_id, axis_obj, old_meta)
|
||||||
|
finally:
|
||||||
|
if _ur_serial is not None:
|
||||||
|
try: doc.EndUndoRecord(_ur_serial)
|
||||||
|
except Exception: pass
|
||||||
|
|
||||||
|
def _update_wall_body(self, doc, p, wall_id, axis_obj, old_meta):
|
||||||
|
"""Eigentliche Patch-Logik. Sieh _update_wall fuer Undo-Wrapping."""
|
||||||
# Tragwerk: Stuetze (Punkt) oder Traeger/Unterzug (Achse)
|
# Tragwerk: Stuetze (Punkt) oder Traeger/Unterzug (Achse)
|
||||||
if old_meta["type"] in ("stuetze_point", "traeger_axis"):
|
if old_meta["type"] in ("stuetze_point", "traeger_axis"):
|
||||||
kind = p.get("kind", old_meta.get("trag_kind", "stuetze"))
|
kind = p.get("kind", old_meta.get("trag_kind", "stuetze"))
|
||||||
@@ -12234,26 +12337,88 @@ class ElementeBridge(panel_base.BaseBridge):
|
|||||||
if H_now > 0.1:
|
if H_now > 0.1:
|
||||||
tn = max(2, int(round(H_now / ttarget_s)))
|
tn = max(2, int(round(H_now / ttarget_s)))
|
||||||
# Axis-Laenge so anpassen dass A = target_A bleibt
|
# Axis-Laenge so anpassen dass A = target_A bleibt
|
||||||
# (nur fuer gerade Treppe = LineCurve)
|
|
||||||
if ttarget_a > 0.05:
|
if ttarget_a > 0.05:
|
||||||
new_L = tn * ttarget_a
|
new_total_L = tn * ttarget_a
|
||||||
|
tart_cur = old_meta.get("treppe_art", "gerade")
|
||||||
try:
|
try:
|
||||||
_g = axis_obj.Geometry
|
_g = axis_obj.Geometry
|
||||||
|
# ---- GERADE: LineCurve P1 verschieben
|
||||||
if (isinstance(_g, rg.LineCurve)
|
if (isinstance(_g, rg.LineCurve)
|
||||||
and abs(cur_axis_L - new_L) > 1e-4):
|
and abs(cur_axis_L - new_total_L) > 1e-4):
|
||||||
P0 = _g.PointAtStart
|
P0 = _g.PointAtStart
|
||||||
P1 = _g.PointAtEnd
|
P1 = _g.PointAtEnd
|
||||||
dx = P1.X - P0.X; dy = P1.Y - P0.Y
|
dx = P1.X - P0.X; dy = P1.Y - P0.Y
|
||||||
if cur_axis_L > 1e-6:
|
if cur_axis_L > 1e-6:
|
||||||
ux = dx / cur_axis_L
|
ux = dx / cur_axis_L
|
||||||
uy = dy / cur_axis_L
|
uy = dy / cur_axis_L
|
||||||
P1n = rg.Point3d(P0.X + ux * new_L,
|
P1n = rg.Point3d(P0.X + ux * new_total_L,
|
||||||
P0.Y + uy * new_L, P0.Z)
|
P0.Y + uy * new_total_L, P0.Z)
|
||||||
new_line = rg.LineCurve(P0, P1n)
|
new_line = rg.LineCurve(P0, P1n)
|
||||||
doc.Objects.Replace(axis_obj.Id, new_line)
|
doc.Objects.Replace(axis_obj.Id, new_line)
|
||||||
# axis_obj-Reference neu holen
|
|
||||||
axis_obj = doc.Objects.Find(axis_obj.Id)
|
axis_obj = doc.Objects.Find(axis_obj.Id)
|
||||||
attrs = axis_obj.Attributes
|
attrs = axis_obj.Attributes
|
||||||
|
# ---- L: PolylineCurve mit 3 Punkten,
|
||||||
|
# beide Laeufe proportional skalieren
|
||||||
|
elif (isinstance(_g, rg.PolylineCurve) and tart_cur == "l"):
|
||||||
|
ok_pl, poly = _g.TryGetPolyline()
|
||||||
|
if ok_pl and poly is not None and poly.Count == 3:
|
||||||
|
p0 = poly[0]; pc = poly[1]; p1 = poly[2]
|
||||||
|
L1c = ((pc.X-p0.X)**2 + (pc.Y-p0.Y)**2) ** 0.5
|
||||||
|
L2c = ((p1.X-pc.X)**2 + (p1.Y-pc.Y)**2) ** 0.5
|
||||||
|
cur_total = L1c + L2c
|
||||||
|
if cur_total > 1e-6:
|
||||||
|
ratio = new_total_L / cur_total
|
||||||
|
if abs(ratio - 1.0) > 1e-4:
|
||||||
|
# Lauf 1 skalieren — pc neu setzen
|
||||||
|
u1x = (pc.X-p0.X)/L1c
|
||||||
|
u1y = (pc.Y-p0.Y)/L1c
|
||||||
|
new_L1 = L1c * ratio
|
||||||
|
pc_n = rg.Point3d(
|
||||||
|
p0.X + u1x*new_L1,
|
||||||
|
p0.Y + u1y*new_L1, p0.Z)
|
||||||
|
# Lauf 2 skalieren — p1 neu setzen
|
||||||
|
u2x = (p1.X-pc.X)/L2c
|
||||||
|
u2y = (p1.Y-pc.Y)/L2c
|
||||||
|
new_L2 = L2c * ratio
|
||||||
|
p1_n = rg.Point3d(
|
||||||
|
pc_n.X + u2x*new_L2,
|
||||||
|
pc_n.Y + u2y*new_L2, p1.Z)
|
||||||
|
new_poly = rg.PolylineCurve(
|
||||||
|
rg.Polyline([p0, pc_n, p1_n]))
|
||||||
|
doc.Objects.Replace(axis_obj.Id, new_poly)
|
||||||
|
axis_obj = doc.Objects.Find(axis_obj.Id)
|
||||||
|
attrs = axis_obj.Attributes
|
||||||
|
# ---- WENDEL: PolylineCurve [center, start, end]
|
||||||
|
# → Sweep-Winkel anpassen damit Bogenlaenge =
|
||||||
|
# new_total_L (Bogenlaenge = r_mid * |delta|)
|
||||||
|
elif (isinstance(_g, rg.PolylineCurve) and tart_cur == "wendel"):
|
||||||
|
import math as _m
|
||||||
|
ok_pl, poly = _g.TryGetPolyline()
|
||||||
|
if ok_pl and poly is not None and poly.Count == 3:
|
||||||
|
center = poly[0]; pstart = poly[1]; pend = poly[2]
|
||||||
|
r_click = _m.hypot(pstart.X-center.X,
|
||||||
|
pstart.Y-center.Y)
|
||||||
|
if r_click > 0.2:
|
||||||
|
r_inner_l, r_outer_l = _wendel_radii(
|
||||||
|
r_click, tb, tref)
|
||||||
|
r_mid = (r_inner_l + r_outer_l) * 0.5
|
||||||
|
a_start_cur, delta_cur = _wendel_sweep(
|
||||||
|
center, pstart, pend)
|
||||||
|
if abs(delta_cur) > 0.01 and r_mid > 1e-6:
|
||||||
|
cur_arc = abs(r_mid * delta_cur)
|
||||||
|
if abs(cur_arc - new_total_L) > 1e-4:
|
||||||
|
sign = 1.0 if delta_cur > 0 else -1.0
|
||||||
|
new_delta = sign * (new_total_L / r_mid)
|
||||||
|
a_end_new = a_start_cur + new_delta
|
||||||
|
pend_n = rg.Point3d(
|
||||||
|
center.X + r_click * _m.cos(a_end_new),
|
||||||
|
center.Y + r_click * _m.sin(a_end_new),
|
||||||
|
pend.Z)
|
||||||
|
new_poly = rg.PolylineCurve(
|
||||||
|
rg.Polyline([center, pstart, pend_n]))
|
||||||
|
doc.Objects.Replace(axis_obj.Id, new_poly)
|
||||||
|
axis_obj = doc.Objects.Find(axis_obj.Id)
|
||||||
|
attrs = axis_obj.Attributes
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[ELEMENTE] lock axis-len adjust:", ex)
|
print("[ELEMENTE] lock axis-len adjust:", ex)
|
||||||
print("[ELEMENTE] Lock: H={:.3f}/S={:.3f}→n={}, A={:.3f}".format(
|
print("[ELEMENTE] Lock: H={:.3f}/S={:.3f}→n={}, A={:.3f}".format(
|
||||||
|
|||||||
Reference in New Issue
Block a user