Files
DOSSIER/rhino/aliases/cmd/smart_join.py
T
karim 18d6d98e07 DOSSIER Multi-Phase: C#-Plugin + Yak + Wandstile + UX-Polish
- 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
2026-05-30 12:46:53 +02:00

158 lines
5.6 KiB
Python

#! python3
# -*- coding: utf-8 -*-
# Smart-Join: bei geschlossenen Curves → BooleanUnion (innere Linien weg),
# bei offenen Curves → normales _Join (Endpunkt-Verbindung).
# Sicherheits-Filter:
# A) Group by Layer + Object-Overrides (Color/Linetype/PlotWeight) + Fill —
# nur Curves mit IDENTISCHEN visuellen Attributen werden gemerged.
# C) Pre-Check Overlap — BooleanUnion liefert genauso viele Outputs wie
# Inputs wenn nichts overlapt → dann KEINE Aktion, Curves bleiben.
# Kombinierter Effekt: nur visuell zusammengehoerige UND tatsaechlich
# ueberlappende Curves werden zu einer Outline vereint.
import scriptcontext as sc
import Rhino
import Rhino.Geometry as rg
import Rhino.DocObjects as rdoc
def _attr_key(obj):
"""Tuple das definiert ob 2 Curves visuell identisch sind. Layer +
Per-Object-Overrides (alles was ByObject nicht ByLayer ist) + Fill-
State (Hatch-ID + No-Fill-Flag)."""
a = obj.Attributes
layer_idx = a.LayerIndex
# Color: nur Object-Override unterscheidend, ByLayer ist gleich.
col_key = ("layer",)
try:
if a.ColorSource == rdoc.ObjectColorSource.ColorFromObject:
col_key = ("obj", a.ObjectColor.ToArgb())
except Exception: pass
# Linetype
lt_key = ("layer",)
try:
if a.LinetypeSource == rdoc.ObjectLinetypeSource.LinetypeFromObject:
lt_key = ("obj", a.LinetypeIndex)
except Exception: pass
# PlotWeight
pw_key = ("layer",)
try:
if a.PlotWeightSource == rdoc.ObjectPlotWeightSource.PlotWeightFromObject:
pw_key = ("obj", float(a.PlotWeight))
except Exception: pass
# Fill / Hatch via gestaltung-UserStrings
fill_hatch = ""
fill_source = ""
no_fill = ""
try:
fill_hatch = a.GetUserString("ebenen_fill_hatch_id") or ""
fill_source = a.GetUserString("ebenen_fill_source") or ""
no_fill = a.GetUserString("ebenen_no_fill") or ""
except Exception: pass
# Fuer Gruppierung zaehlt: "hatte Fill ja/nein" + Quelle + No-Fill-Flag.
fill_key = (bool(fill_hatch), fill_source, no_fill)
return (layer_idx, col_key, lt_key, pw_key, fill_key)
def _run():
doc = Rhino.RhinoDoc.ActiveDoc
if doc is None: return
sel = list(doc.Objects.GetSelectedObjects(False, False))
if not sel:
Rhino.RhinoApp.RunScript("_Join", False); return
# Curves nach Closed/Open trennen
closed_objs = []
has_non_closed = False
for obj in sel:
g = obj.Geometry
if isinstance(g, rg.Curve) and g.IsClosed:
closed_objs.append(obj)
else:
has_non_closed = True
# Wenn nicht ALLE closed sind → einfach Standard-Join
if has_non_closed or len(closed_objs) < 2:
Rhino.RhinoApp.RunScript("_Join", False); return
# Gruppieren nach (Layer + Attrs + Fill)
groups = {} # key → [obj, obj, ...]
for obj in closed_objs:
try:
k = _attr_key(obj)
except Exception:
k = ("ungroup", id(obj))
groups.setdefault(k, []).append(obj)
# gestaltung fuer Fill-Re-Apply
_g = None
try:
import gestaltung as _gmod; _g = _gmod
except Exception as iex:
print("[SMART-JOIN] gestaltung import:", iex)
tol = doc.ModelAbsoluteTolerance
ur = doc.BeginUndoRecord("DOSSIER Smart-Join (gruppiert)")
n_merged_total = 0
n_groups_ops = 0
try:
for key, objs in groups.items():
if len(objs) < 2: continue # einzelne Curve → nichts zu mergen
try:
curves = [o.Geometry for o in objs]
result = rg.Curve.CreateBooleanUnion(curves, tol)
except Exception as ex:
print("[SMART-JOIN] BooleanUnion in Gruppe fehlgeschlagen:", ex)
continue
if not result: continue
# C) Pre-Check Overlap: wenn result-Anzahl gleich input-Anzahl
# ist, gab's keinen tatsaechlichen Overlap → Gruppe nicht
# anfassen.
if len(result) >= len(objs):
continue
# Tatsaechlich gemerged → replace
attrs_template = objs[0].Attributes.Duplicate()
# Fill-Key clearen damit _apply_ebene_fill nicht "schon gefuellt"
# zurueckgibt
try:
attrs_template.SetUserString("ebenen_fill_hatch_id", "")
except Exception: pass
any_had_fill = bool(key[4][0]) # fill_key[0] = had-fill bool
new_ids = []
for crv in result:
nid = doc.Objects.AddCurve(crv, attrs_template)
if nid: new_ids.append(nid)
for o in objs:
try: doc.Objects.Delete(o.Id, True)
except Exception: pass
# Fill nachziehen wenn Inputs welche hatten
if any_had_fill and _g is not None:
for nid in new_ids:
try:
nobj = doc.Objects.FindId(nid)
if nobj is not None:
_g._apply_ebene_fill(doc, nobj)
except Exception as fex:
print("[SMART-JOIN] fill-apply:", fex)
n_merged_total += (len(objs) - len(result))
n_groups_ops += 1
finally:
doc.EndUndoRecord(ur)
if n_groups_ops == 0:
print("[SMART-JOIN] Nichts zu mergen — keine Curves overlappen "
"(oder verschiedene Attribute/Layer)")
else:
doc.Views.Redraw()
print("[SMART-JOIN] {} Gruppe(n) bearbeitet, {} Curve(s) zu Union vereint"
.format(n_groups_ops, n_merged_total))
_run()