#! 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()