Snap-Bar + Drag-Reorder + Schnittperspektive + Top-View Z-Guard + Multi-Geschoss-Clipping

Snap-Bar (Oberleiste):
- 4x2 Icon-Grid mit architektonischen Osnap-Modi (End/Mid/Int/Perp/Cen/Near)
- Master-O Toggle + Grid-Sichtbarkeit
- Symbol-Wahl angelehnt an Rhinos eigene Snap-Marker
- Ortho + Grid-Snap raus (in Rhino-Footer)
- Backend: _osnap_flag_map + _get/_set_osnap_modes + _set_grid_visible

Drag-to-Reorder (GeschossManager):
- HTML5-Drag auf jeder Zeile
- Drop-Indikator (accent-Border oben/unten je nach Cursor-Position)
- Gedraggte Row faded auf opacity 0.4
- Array-Reorder + onChange triggert recalcOkff -> OKFFs konsistent

Schnittperspektive:
- projection: 'parallel' | 'perspective' im Schnitt-Settings-Dialog
- Augenhoehe (cameraHeight) nur bei Perspektive sichtbar
- activate_schnitt mit ChangeToPerspectiveProjection(50 FOV)
- skip_view=True bei Grip-Drag-Re-Activate damit View nicht ploetzlich
  in Section springt

Top-View Z-Guard:
- _is_active_view_top_like + _suppress_z_drift_if_top_view in
  _on_object_replaced — bei Plan-View wird Z-Drift einer source-curve
  automatisch zurueckgerollt (gegen ungewolltes Snappen auf z!=0 oder
  Gumball-Z)

Multi-Geschoss-Clipping-UX:
- Klick auf cut-Icon einer nicht-aktiven Geschoss-Zeile aktiviert das
  Geschoss mit + toggelt Clipping → Plane erscheint sofort

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-23 23:44:12 +02:00
parent 736325fba1
commit ee01c7ebdc
7 changed files with 426 additions and 17 deletions
+96 -10
View File
@@ -722,17 +722,95 @@ def _set_display_mode(name):
# --- Snap / Ortho via ModelAidSettings --------------------------------------
# Architekten-relevante Osnap-Modes — UI-Key → OsnapModes-Flag.
# Vollstaendige Liste in Rhino: End, Near, Focus, Center, Vertex, Knot,
# Quadrant, Midpoint, Intersection, Perpendicular, Tangent, Point.
# Architektur-Workflow nutzt v.a. die ersten 6.
def _osnap_flag_map():
try:
OM = Rhino.ApplicationSettings.OsnapModes
return {
"end": OM.End,
"mid": OM.Midpoint,
"int": OM.Intersection,
"perp": OM.Perpendicular,
"cen": OM.Center,
"near": OM.Near,
}
except Exception:
return {}
def _get_osnap_modes_dict():
flags = _osnap_flag_map()
if not flags: return {}
try:
cur = int(Rhino.ApplicationSettings.ModelAidSettings.OsnapModes)
return {k: bool(cur & int(f)) for k, f in flags.items()}
except Exception:
return {k: False for k in flags}
def _set_osnap_mode(key, enabled):
flags = _osnap_flag_map()
flag = flags.get(key)
if flag is None: return
try:
s = Rhino.ApplicationSettings.ModelAidSettings
cur = int(s.OsnapModes)
flag_i = int(flag)
new = (cur | flag_i) if enabled else (cur & ~flag_i)
s.OsnapModes = Rhino.ApplicationSettings.OsnapModes(new)
except Exception as ex:
print("[OBERLEISTE] _set_osnap_mode:", ex)
def _is_grid_visible():
try:
doc = Rhino.RhinoDoc.ActiveDoc
if doc is None: return True
view = doc.Views.ActiveView
if view is None: return True
vp = view.ActiveViewport
try: return bool(vp.ConstructionGridVisible)
except Exception:
try: return bool(vp.ShowConstructionGrid)
except Exception: return True
except Exception:
return True
def _set_grid_visible(visible):
"""Schaltet Konstruktions-Grid in ALLEN Modell-Viewports an/aus."""
doc = Rhino.RhinoDoc.ActiveDoc
if doc is None: return
v_in = bool(visible)
for v in doc.Views:
try:
vp = v.ActiveViewport
try: vp.ConstructionGridVisible = v_in
except Exception:
try: vp.ShowConstructionGrid = v_in
except Exception: pass
except Exception: pass
try: doc.Views.Redraw()
except Exception: pass
def _get_snap_state():
try:
s = Rhino.ApplicationSettings.ModelAidSettings
return {
"ortho": bool(s.Ortho),
"gridSnap": bool(s.GridSnap),
"osnap": bool(s.UseHorizontalDialog) if False else bool(getattr(s, "Osnap", False)) or False,
"planar": bool(getattr(s, "ProjectOsnapsToCPlane", False)),
"ortho": bool(s.Ortho),
"gridSnap": bool(s.GridSnap),
"osnap": bool(getattr(s, "Osnap", False)) or bool(getattr(s, "OsnapEnabled", False)),
"planar": bool(getattr(s, "ProjectOsnapsToCPlane", False)),
"gridVisible": _is_grid_visible(),
"osnapModes": _get_osnap_modes_dict(),
}
except Exception:
return {"ortho": False, "gridSnap": False, "osnap": False, "planar": False}
return {"ortho": False, "gridSnap": False, "osnap": False,
"planar": False, "gridVisible": True, "osnapModes": {}}
def _set_ortho(v):
@@ -753,11 +831,11 @@ def _set_osnap_master(v):
"""Master-Toggle fuer Object-Snap (alle aktiven Snaps)."""
try:
s = Rhino.ApplicationSettings.ModelAidSettings
if hasattr(s, "Osnap"):
s.Osnap = bool(v)
elif hasattr(s, "UsePoints"):
# Fallback: einzelne Modi durch
pass
# Verschiedene Rhino-Versionen — beide Properties probieren
for attr in ("Osnap", "OsnapEnabled"):
if hasattr(s, attr):
setattr(s, attr, bool(v))
return
except Exception as ex:
print("[OBERLEISTE] _set_osnap_master:", ex)
@@ -1018,6 +1096,12 @@ class OberleisteBridge(panel_base.BaseBridge):
elif t == "TOGGLE_OSNAP":
_set_osnap_master(bool(p.get("enabled")))
self._send_state(force=True)
elif t == "SET_OSNAP_MODE":
_set_osnap_mode(p.get("key") or "", bool(p.get("enabled")))
self._send_state(force=True)
elif t == "TOGGLE_GRID_VISIBLE":
_set_grid_visible(bool(p.get("visible")))
self._send_state(force=True)
# --- Graphical Overrides ----------------------------------------
elif t == "TOGGLE_OVERRIDES":
@@ -1464,6 +1548,8 @@ class OberleisteBridge(panel_base.BaseBridge):
info.get("viewMode"),
info.get("displayMode"),
info.get("ortho"), info.get("gridSnap"), info.get("osnap"),
info.get("gridVisible"),
tuple(sorted((info.get("osnapModes") or {}).items())),
info.get("showLineweights"),
info["overridesEnabled"], info["overridesCount"],
info.get("overridesActivePreset"),