#! 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 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. Marker-Logik pro Treppen-Art: - gerade : PointAtStart, PointAtEnd der Linie - L (3-Pt): poly[0] (Start), poly[1] (Eck), poly[2] (Ende) — alle 3 damit das Eck einzeln gegriffen werden kann - L (4-Pt): alle 4 Punkte (Start, Lauf1-Ende, Lauf2-Anfang, Ende) - 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.DocObjects as rdoc 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. Beachtet treppe_art + Polyline-Punktzahl.""" 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]] if art == "l": ok, poly = geom.TryGetPolyline() if not ok or poly is None: return [] return [poly[i] for i in range(poly.Count)] return [geom.PointAtStart, geom.PointAtEnd] except Exception: return [] def _enumerator_all(): """Iterator-Settings die hidden + locked Objekte mit einschliessen — Mac-Default skipt sonst hidden-Layer-Objekte.""" s = rdoc.ObjectEnumeratorSettings() s.HiddenObjects = True s.LockedObjects = True return s class _TreppeEndpointConduit(rd.DisplayConduit): """Zeichnet gruene 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 — auch wenn auf hidden # Layer (User hat z.B. nur 2D-Plansymbol selektiert). axis = None for o in doc.Objects.GetObjectList(_enumerator_all()): 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 active") except Exception as ex: print("[TREPPE_GRIPS] install:", ex)