375487c10c
i18n: - src/i18n/de.json + en.json: 200+ keys covering all main panels - src/i18n/index.js: t(key, vars) reads window.DOSSIER_LANG - panel_base.py: injects window.DOSSIER_LANG from dossier_settings.json - EbenenManager, GeschossManager, AusschnitteApp, LayoutsApp: all context menus and main labels use t() DossierSettings panel: - DossierSettingsApp.jsx: language toggle (DE/EN pill) + launcher status - toolbar.py: OPEN_SETTINGS opens new Rhino-hosted satellite window, SAVE_LANG writes lang to dossier_settings.json + reloads all panels File renames (JSX → English): - ZeichnungsebenenApp → DrawingLevelsApp - GeschossManager/Dialog/Settings → Floor* - AusschnitteApp/Settings → Viewports* - EbenenManager/Settings → Layer* - GestaltungApp → StylesApp, OberleisteApp → ToolbarApp - WerkzeugeApp → ToolsApp, DimensionenApp → DimensionsApp - MassstabApp → ScaleApp, KameraApp → CameraApp - MasseSettingsApp → UnitsSettingsApp - ConfirmDeleteEbene → ConfirmDeleteLayer - AusschnittLayerDialog → ViewportLayerDialog Python module renames: - rhinopanel.py → layers_panel.py - oberleiste.py → toolbar.py - gestaltung.py → styles.py - werkzeuge.py → tools.py - dimensionen.py → dimensions.py - startup.py _MODULE_TO_PY updated, all cross-imports fixed
268 lines
10 KiB
Python
268 lines
10 KiB
Python
#! 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()
|