18d6d98e07
- C#-Plugin "DOSSIER" mit 23 nativen Commands (dWall, dDoor, ..., dSection)
- Native Command-Namen + Autocomplete + saubere History
- Idle-Defer + RhinoCode-API → kein _-RunPythonScript-Echo
- Yak-Paket via build.sh, Install in ~/Library/.../packages/8.0/
- Launcher (Tauri):
- dossier_init Tauri-Command + Setup-Tab in Settings
- Yak-Install + StartupCommands-XML + Window-Layout in einem Schritt
- clean-rhino.sh fuer reproduzierbare Resets
- check_dossier_initialized triggert Auto-Open-Setup beim ersten Start
- Wand-Architektur:
- Chain-Logik DEAKTIVIERT → jede Wand baut eigenes Volume (individuell
anwaehlbar, einzeln loeschbar)
- Polyline-Wand: jedes Segment = eigene Wand
- Smart-Split fuer wand_axis/decke/dach/raum/aussparung/traeger
- Auto-Group axis+volume → kein ChooseOne-Dialog, Delete loescht beides
- Stale-Mitre-Fix: Joint-Cache wird vor jedem Wand-Regen invalidiert
- T-Junction-Tolerance auf 1mm (war 1cm, lieferte falsche T-Mitres)
- Wand-Stile:
- Schema in dossier_project_settings.wand_styles (Material + Prio +
Default-Dicke + Referenz, oder Layered mit Schichten)
- dWall-Command Stil-Picker
- ProjectSettingsDialog: Sidebar-Layout (Pill-Selection) +
Wandstile-Tab mit Liste/Editor
- _wand_chain_compat benutzt style_id
- Prio-Dominanz: hoehere Prio gewinnt Eckverbindung, niedrigere wird
T-mitered (siehe _resolve_corner_miter)
- Cmd+G fuer Group (Geschoss-Up auf Alias 'gu')
- Welcome + Cheatsheet borderless mit X/Back-Buttons
- BeginCommand-Hook fuer Gestaltung-Panel-Auto-Open
- panel_base: Python.NET-Enum-Fix fuer Material-Render
119 lines
4.5 KiB
Python
119 lines
4.5 KiB
Python
#! 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 aktiv")
|
|
except Exception as ex:
|
|
print("[TREPPE_GRIPS] install:", ex)
|