#! python3 # -*- coding: utf-8 -*- # Smart-Split: User zeichnet eine Splitlinie/Polylinie waehrend des Befehls # (mehrere Klicks, Enter beendet die Eingabe). Alle Curves die die Linie # schneidet werden gesplittet. # - Offene Curves: bei den Schnittpunkten in offene Segmente. # - GESCHLOSSENE Curves: in mehrere CLOSED Sub-Regionen via # Curve.CreateBooleanRegions (funktioniert auch bei multi-segment # Polylinien-Cuttern). Per-Object-Hatch wird auf alle Regionen repliziert. # DOSSIER-Source-Typen (Wand-Achse etc.) bleiben geschuetzt. import scriptcontext as sc import Rhino import Rhino.Input.Custom as ric import Rhino.Geometry as rg import Rhino.DocObjects as rdoc from Rhino.Input import GetResult # Was Smart-Split NIE anfasst: # - oeffnung_point / stuetze_point: Punkte, nicht teilbar # - schnitt_axis: Schnitt-Linien sollen bleiben, sonst kaputte Schnitte # - treppe_axis: Treppen-State (Lauflinie, Schrittmass-Lock, Wendel-Sweep) # waere bei einem Split inkonsistent # Alles andere (wand/traeger/decke/dach/raum/aussparung) DARF gesplittet werden: # der Add-Listener in elemente.py erkennt die Duplikat-IDs der neuen Stuecke # und vergibt jedem Stueck ein frisches Element-ID + Regen → BIM-Volumen # baut sich pro neuem Stueck neu auf. _PROTECTED_TYPES = { "treppe_axis", "oeffnung_point", "stuetze_point", "schnitt_axis", } def _capture_hatch_props(doc, src_obj): try: sa = src_obj.Attributes fill_hid = sa.GetUserString("ebenen_fill_hatch_id") or "" if not fill_hid: return None import System hid = System.Guid(fill_hid) hobj = doc.Objects.FindId(hid) if hobj is None or hobj.IsDeleted: return None hg = hobj.Geometry ha = hobj.Attributes if not hasattr(hg, "PatternIndex"): return None return { "pattern_idx": int(hg.PatternIndex), "scale": float(hg.PatternScale), "rotation": float(hg.PatternRotation), "layer_idx": int(ha.LayerIndex), "color_source": int(ha.ColorSource), "color_argb": int(ha.ObjectColor.ToArgb()), "plot_color_source": int(ha.PlotColorSource), "plot_color_argb": int(ha.PlotColor.ToArgb()), "linetype_source": int(ha.LinetypeSource), "linetype_idx": int(ha.LinetypeIndex), "fill_source": sa.GetUserString("ebenen_fill_source") or "object", } except Exception as ex: print("[SMART-SPLIT] capture-hatch:", ex) return None def _replicate_hatch(doc, new_obj, hp): if hp is None: return import System try: crv = new_obj.Geometry if not isinstance(crv, rg.Curve) or not crv.IsClosed: return tol = doc.ModelAbsoluteTolerance hatches = rg.Hatch.Create(crv, hp["pattern_idx"], hp["rotation"], hp["scale"], tol) if not hatches or len(hatches) == 0: return ha = rdoc.ObjectAttributes() ha.LayerIndex = hp["layer_idx"] ha.ColorSource = rdoc.ObjectColorSource(hp["color_source"]) ha.ObjectColor = System.Drawing.Color.FromArgb(hp["color_argb"]) try: ha.PlotColorSource = rdoc.ObjectPlotColorSource(hp["plot_color_source"]) ha.PlotColor = System.Drawing.Color.FromArgb(hp["plot_color_argb"]) except Exception: pass if hp["linetype_source"] == int(rdoc.ObjectLinetypeSource.LinetypeFromObject): ha.LinetypeSource = rdoc.ObjectLinetypeSource.LinetypeFromObject ha.LinetypeIndex = hp["linetype_idx"] ha.SetUserString("ebenen_fill_source", hp.get("fill_source", "object")) ha.SetUserString("ebenen_fill_owner", str(new_obj.Id)) new_hid = doc.Objects.AddHatch(hatches[0], ha) if new_hid and new_hid != System.Guid.Empty: ca = new_obj.Attributes.Duplicate() ca.SetUserString("ebenen_fill_hatch_id", str(new_hid)) ca.SetUserString("ebenen_fill_source", hp.get("fill_source", "object")) doc.Objects.ModifyAttributes(new_obj, ca, True) except Exception as ex: print("[SMART-SPLIT] hatch-replicate:", ex) def _collect_polyline_cutter(prompt_first, prompt_more): """Sammelt n Punkte fuer den Cutter. Enter beendet (min. 2 Punkte). ESC bricht ab. Returnt Polyline oder None.""" pts = [] while True: gp = ric.GetPoint() if not pts: gp.SetCommandPrompt(prompt_first) else: gp.SetCommandPrompt(prompt_more + " (Enter zum Splitten, ESC = abbrechen)") gp.SetBasePoint(pts[-1], True) gp.DrawLineFromPoint(pts[-1], True) gp.AcceptNothing(True) res = gp.Get() if res == GetResult.Nothing: # Enter gedrueckt if len(pts) >= 2: return rg.Polyline(pts) print("[SMART-SPLIT] Mindestens 2 Punkte noetig"); return None if res != GetResult.Point: return None pts.append(gp.Point()) def _split_closed_with_cutter(closed_crv, cutter_crv, doc): """Splittet closed curve mit beliebigem cutter (Linie oder Polylinie) in closed Sub-Regionen via Curve.CreateBooleanRegions.""" tol = doc.ModelAbsoluteTolerance try: # WorldXY-Plane als Default (DOSSIER ist 2D Plan-Workflow) plane = rg.Plane.WorldXY regions = rg.Curve.CreateBooleanRegions( [closed_crv, cutter_crv], plane, False, tol) if regions is None or regions.RegionCount == 0: return None out = [] for i in range(regions.RegionCount): rcurves = list(regions.RegionCurves(i)) if not rcurves: continue if len(rcurves) == 1: if rcurves[0].IsClosed: out.append(rcurves[0]) else: # einzelne offene curve — sollte nicht passieren bei # Boolean-Regions, aber defensiv joined = rg.Curve.JoinCurves([rcurves[0]], tol) if joined and len(joined) > 0 and joined[0].IsClosed: out.append(joined[0]) else: joined = rg.Curve.JoinCurves(rcurves, tol) if joined: for j in joined: if j.IsClosed: out.append(j) return out if out else None except Exception as ex: print("[SMART-SPLIT] closed-split:", ex) return None def _run(): doc = Rhino.RhinoDoc.ActiveDoc if doc is None: return # Polylinie als Cutter sammeln poly = _collect_polyline_cutter( "Splitlinie Startpunkt", "Naechster Punkt") if poly is None or poly.Count < 2: return cutter = rg.PolylineCurve(poly) tol = doc.ModelAbsoluteTolerance pre_sel = [o for o in doc.Objects.GetSelectedObjects(False, False) if o is not None and not o.IsDeleted] if pre_sel: source = pre_sel mode_label = "selektierte ({})".format(len(pre_sel)) else: s = rdoc.ObjectEnumeratorSettings() s.HiddenObjects = False; s.LockedObjects = False source = list(doc.Objects.GetObjectList(s)) mode_label = "alle sichtbaren" candidates_open = [] candidates_closed = [] for obj in source: if obj is None or obj.IsDeleted: continue try: t = obj.Attributes.GetUserString("dossier_element_type") or "" if t in _PROTECTED_TYPES: continue except Exception: pass g = obj.Geometry if not isinstance(g, rg.Curve): continue try: ints = rg.Intersect.Intersection.CurveCurve(cutter, g, tol, tol) except Exception: continue if not ints or ints.Count == 0: continue if g.IsClosed: candidates_closed.append((obj, g)) else: params = [] for i in range(ints.Count): ev = ints[i] if ev.IsPoint: params.append(ev.ParameterB) else: params.append(ev.ParameterB); params.append(ev.ParameterB2) if params: params = sorted(set(round(p, 6) for p in params)) candidates_open.append((obj, g, params)) if not candidates_open and not candidates_closed: print("[SMART-SPLIT] Cutter schneidet nichts ({})".format(mode_label)) return ur = doc.BeginUndoRecord("DOSSIER Smart-Split") n_open = 0; n_closed = 0 try: # Closed: Boolean-Regions → CLOSED Sub-Regionen + Fill replicate for obj, crv in candidates_closed: try: regions = _split_closed_with_cutter(crv, cutter, doc) if not regions or len(regions) <= 1: continue hatch_props = _capture_hatch_props(doc, obj) attrs = obj.Attributes.Duplicate() try: attrs.SetUserString("ebenen_fill_hatch_id", "") except Exception: pass new_ids = [] for r in regions: nid = doc.Objects.AddCurve(r, attrs) if nid: new_ids.append(nid) doc.Objects.Delete(obj.Id, True) if hatch_props is not None: for nid in new_ids: nobj = doc.Objects.FindId(nid) if nobj is not None: _replicate_hatch(doc, nobj, hatch_props) else: try: import styles as _gmod for nid in new_ids: nobj = doc.Objects.FindId(nid) if nobj is not None: _gmod._apply_ebene_fill(doc, nobj) except Exception: pass n_closed += 1 except Exception as ex: print("[SMART-SPLIT] closed-fail:", ex) # Open: split bei Params for obj, crv, params in candidates_open: try: pieces = crv.Split(params) if not pieces or len(pieces) <= 1: continue attrs = obj.Attributes.Duplicate() for p in pieces: doc.Objects.AddCurve(p, attrs) doc.Objects.Delete(obj.Id, True) n_open += 1 except Exception as ex: print("[SMART-SPLIT] open-fail:", ex) finally: doc.EndUndoRecord(ur) doc.Views.Redraw() print("[SMART-SPLIT] {} closed-Regionen + {} offene Curves gesplittet " "({} Cutter-Punkte, {})" .format(n_closed, n_open, poly.Count, mode_label)) _run()