Vorschau-Quader fuer Fenster/Tuer + Launcher reinweiss + SectionStyle-Apply gehaertet

Elemente:
- _make_oeffnung_preview baut jetzt einen vollen 3D-Quader (12 Kanten) mit
  Wand-Dicke statt nur einer 2D-Flaeche. Glas-Diagonalen auf Vorder- und
  Hinterflaeche, Brueest-Linie (gepunktet) auf der Vorderflaeche, Achs-
  Marker auf der Wand-Achse, dazu ein 3D-Mass-Label "B x H Br" ueberm
  Sturz (zentriert, an der Wand "geheftet"). Aktualisiert live bei
  Option-Aenderungen.

Launcher:
- Paper-Theme zu reinem Weiss umgestellt (User-Feedback "zu warm"):
  --bg #ffffff, --dark #f0f0f0, neutrale Greys statt Sand-Tones,
  Schatten ohne warmen Braun-Stich. Petrol-Radial-Gradient oben raus.
- latest.json aus dem neuen Release-Build.

SectionStyle (Bug-Hunt — Hatch/Boundary haben nicht gegriffen):
- *Source-Properties (HatchColorSource, BoundaryColorSource,
  BoundaryLinetypeSource) jetzt explizit auf ColorFromObject /
  LinetypeFromObject — sonst hat Rhino die eigenen Color/Linetype-
  Werte ignoriert (Default ist ByLayer).
- doc.Layers.Modify nach SetCustomSectionStyle, sonst persistiert Mac
  Rhino den Custom-Style nicht zuverlaessig.
- Helper _try_set + _enum_int eliminieren das 8x duplizierte
  Property-Probe-Pattern.
- Property-Inventar wird einmal pro Session gedumpt (verfuegbare
  SectionStyle-Felder) damit API-Mismatches sichtbar werden.
- Per-Layer Apply-Logs zeigen welche Properties via welchem Namen
  gesetzt wurden — leicht debuggbar im Mismatch-Fall.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-19 12:15:02 +02:00
parent ee4d4876dd
commit 41b6f8ac51
4 changed files with 235 additions and 106 deletions
+3 -3
View File
@@ -1,10 +1,10 @@
{ {
"version": "0.6.3", "version": "0.6.3",
"notes": "Silent Plugin Auto-Load via _-RunPythonScript + korrigierter Shebang #! python 3 — kein ScriptEditor mehr", "notes": "Dossier 0.6.3",
"pub_date": "2026-05-17T14:26:39Z", "pub_date": "2026-05-19T10:11:20Z",
"platforms": { "platforms": {
"darwin-aarch64": { "darwin-aarch64": {
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVRNFYzbUN3TE44QnNuNmhvcmJZcTdFbklSYllMcmQvYTI0NDhVRFlPWGN0YXFmSWxXSW9XSE1zbmJTWEpRdTZSc0xlcFg1VVZ5bGUvaEhSTGlhL2NjNi96NzVQZ0R0bWc4PQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzc5MDI3OTk5CWZpbGU6RG9zc2llci5hcHAudGFyLmd6CjkzQXZKMDlBbndqeTlHa2hBY2NwUGJNckJXZXNCbWhNRkZ2bW9VeDlSZ0JiK1lVRzBVTHdqK0V5T0NXNFlBMWdJRXRHRStKWnVtNG1WcWx2T1pkMUNnPT0K", "signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVRNFYzbUN3TE44QnR0RlRXY3c2MGloMTY4L0hja3ZaOGVYbHc4VHY5MHU3V0NqQ2o5aTlyWFJwaWc0RHV4OXNYRGsrZWN4eGdnSzhpTENQRy95NzZONGFiV2cwNWVpdnd3PQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzc5MTg1NDgwCWZpbGU6RG9zc2llci5hcHAudGFyLmd6CmRKZlBWWTZMSUI5L1VmUmh4QnZoUkxXTU5zQ0pMWUpyUk4yQUZ2SGtzaUF2WTl2ZU8xM21saElqT1ppeldxTmpGUUhGZkJtN0t3RFIyakwvMFByK0F3PT0K",
"url": "https://git.kgva.ch/karim/DOSSIER/releases/download/0.6.3/Dossier.app.tar.gz" "url": "https://git.kgva.ch/karim/DOSSIER/releases/download/0.6.3/Dossier.app.tar.gz"
} }
} }
+13 -16
View File
@@ -1,9 +1,9 @@
:root { :root {
/* Paper-Theme — warm-weisser Hintergrund, schwarze Schrift, Petrol-Akzent */ /* Light-Theme — neutrales Weiss, schwarze Schrift, Petrol-Akzent */
--bg: #f4f0e6; /* warmes Papier */ --bg: #ffffff; /* pures Weiss */
--surface: #ffffff; /* Cards */ --surface: #ffffff; /* Cards */
--surface2: #faf6ec; /* leichter Hover / zweite Ebene */ --surface2: #f5f5f5; /* leichter Hover / zweite Ebene */
--dark: #ebe5d4; /* "vertieft" — Inputs, Code, dropdowns */ --dark: #f0f0f0; /* "vertieft" — Inputs, Code, dropdowns */
--accent: #5fa896; --accent: #5fa896;
--accent-soft: #4a8a7c; --accent-soft: #4a8a7c;
@@ -11,11 +11,11 @@
--text: #1a1a1a; /* fast schwarz, Lesefluss */ --text: #1a1a1a; /* fast schwarz, Lesefluss */
--text2: #404040; /* sekundaer */ --text2: #404040; /* sekundaer */
--text3: #6e6a62; /* tertiaer / meta */ --text3: #6e6e6e; /* tertiaer / meta */
--text4: #a09a8e; /* Hint / Placeholder */ --text4: #a0a0a0; /* Hint / Placeholder */
--border: #d8d0bc; /* warme Papier-Linie */ --border: #e2e2e2; /* neutrale, kuehle Linie */
--border2: #b9b09a; /* staerker fuer Hover */ --border2: #c8c8c8; /* staerker fuer Hover */
--danger: #b54e30; --danger: #b54e30;
@@ -24,10 +24,10 @@
--font-mono: 'DM Mono', 'Menlo', monospace; --font-mono: 'DM Mono', 'Menlo', monospace;
--font-ui: 'DM Mono', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; --font-ui: 'DM Mono', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
/* Sanftere Schatten auf hellem Grund — warm-braeunlicher Ton statt schwarz */ /* Neutrale, sanfte Schatten — kein warmer Stich mehr */
--sh-sm: 0 1px 3px rgba(70,55,30,0.08), 0 1px 2px rgba(70,55,30,0.04); --sh-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
--sh-md: 0 6px 16px rgba(70,55,30,0.10), 0 2px 4px rgba(70,55,30,0.06); --sh-md: 0 6px 16px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.05);
--sh-lg: 0 16px 40px rgba(70,55,30,0.14), 0 4px 12px rgba(70,55,30,0.08); --sh-lg: 0 16px 40px rgba(0,0,0,0.12), 0 4px 12px rgba(0,0,0,0.06);
} }
* { box-sizing: border-box; } * { box-sizing: border-box; }
@@ -129,10 +129,7 @@ input::placeholder { color: var(--text4); }
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100vh; height: 100vh;
/* Subtiler Petrol-Schein oben — Papier mit leichtem Sonnen-Touch */ background: var(--bg);
background:
radial-gradient(ellipse at top, rgba(95,168,150,0.08) 0%, transparent 55%),
var(--bg);
} }
/* --- Topbar ----------------------------------------------------------- */ /* --- Topbar ----------------------------------------------------------- */
+80 -23
View File
@@ -503,13 +503,19 @@ def _make_circle_preview(center):
return handler return handler
def _make_oeffnung_preview(axis_curve, breite, hoehe, brueest, base_z): def _make_oeffnung_preview(axis_curve, wall_dicke, breite, hoehe, brueest, base_z, typ):
"""Preview fuer Fenster/Tuer-Platzierung. Zeigt das vertikale Rechteck """Preview fuer Fenster/Tuer-Platzierung. Zeigt:
der Oeffnungsflaeche auf Hoehe der Achse (entlang Tangente x Hoehe), - Voller 3D-Quader des Oeffnungs-Cutouts (mit Wand-Dicke)
zusaetzlich eine Marker-Linie auf der Achse die die Breite andeutet.""" - Glas-Diagonalen auf Vorder- und Hinterflaeche
- Brueestungs-Markierung (gepunktet) bei Fenster mit Brueest > 0
- Achs-Marker (Strich auf der Wand-Achse)
- Mass-Label oberhalb des Sturzes (B x H, ggf. Brueest)
Aktualisiert sich live wenn User Optionen aendert."""
import System.Drawing as SD import System.Drawing as SD
color = SD.Color.FromArgb(255, 95, 200, 180) # Accent gruen color_main = SD.Color.FromArgb(255, 95, 200, 180) # Accent gruen, voll
color_axis = SD.Color.FromArgb(180, 95, 200, 180) # halbtransparent color_soft = SD.Color.FromArgb(160, 95, 200, 180) # halbtransparent
color_dotted = SD.Color.FromArgb(210, 95, 200, 180) # Brueest-/Achs-Marker
hd = wall_dicke * 0.5
def handler(sender, e): def handler(sender, e):
try: try:
cur = e.CurrentPoint cur = e.CurrentPoint
@@ -520,25 +526,70 @@ def _make_oeffnung_preview(axis_curve, breite, hoehe, brueest, base_z):
tlen = (tan.X * tan.X + tan.Y * tan.Y) ** 0.5 tlen = (tan.X * tan.X + tan.Y * tan.Y) ** 0.5
if tlen < 1e-9: return if tlen < 1e-9: return
tx = tan.X / tlen; ty = tan.Y / tlen tx = tan.X / tlen; ty = tan.Y / tlen
# Normale zur Tangente in XY (90deg gegen Uhrzeigersinn)
nx, ny = -ty, tx
hb = breite * 0.5 hb = breite * 0.5
z_bot = base_z + brueest z_bot = base_z + brueest
z_top = z_bot + hoehe z_top = z_bot + hoehe
cx, cy = on_axis.X, on_axis.Y cx, cy = on_axis.X, on_axis.Y
# Breiten-Marker auf der Achse
left_xy = rg.Point3d(cx - hb * tx, cy - hb * ty, on_axis.Z) # 8 Ecken des Quaders. side_t = ±1 (links/rechts entlang Tangente),
right_xy = rg.Point3d(cx + hb * tx, cy + hb * ty, on_axis.Z) # side_n = ±1 (vorne/hinten entlang Normale)
e.Display.DrawLine(left_xy, right_xy, color_axis, 1) def pt(side_t, side_n, z):
# 4 Kanten der vertikalen Oeffnungs-Flaeche return rg.Point3d(
p_lb = rg.Point3d(cx - hb * tx, cy - hb * ty, z_bot) cx + side_t * hb * tx + side_n * hd * nx,
p_rb = rg.Point3d(cx + hb * tx, cy + hb * ty, z_bot) cy + side_t * hb * ty + side_n * hd * ny,
p_rt = rg.Point3d(cx + hb * tx, cy + hb * ty, z_top) z)
p_lt = rg.Point3d(cx - hb * tx, cy - hb * ty, z_top)
for a, b in ((p_lb, p_rb), (p_rb, p_rt), lbf = pt(-1, +1, z_bot); rbf = pt(+1, +1, z_bot)
(p_rt, p_lt), (p_lt, p_lb)): rtf = pt(+1, +1, z_top); ltf = pt(-1, +1, z_top)
e.Display.DrawLine(a, b, color, 2) lbb = pt(-1, -1, z_bot); rbb = pt(+1, -1, z_bot)
# Diagonalen — Andeutung der Glasflaeche rtb = pt(+1, -1, z_top); ltb = pt(-1, -1, z_top)
e.Display.DrawLine(p_lb, p_rt, color_axis, 1)
e.Display.DrawLine(p_lt, p_rb, color_axis, 1) # 12 Quader-Kanten
for a, b in (
(lbf, rbf), (rbf, rtf), (rtf, ltf), (ltf, lbf), # vorne
(lbb, rbb), (rbb, rtb), (rtb, ltb), (ltb, lbb), # hinten
(lbf, lbb), (rbf, rbb), (rtf, rtb), (ltf, ltb), # Tiefen-Kanten
):
e.Display.DrawLine(a, b, color_main, 2)
# Glas-Diagonalen (vorne + hinten)
e.Display.DrawLine(lbf, rtf, color_soft, 1)
e.Display.DrawLine(ltf, rbf, color_soft, 1)
e.Display.DrawLine(lbb, rtb, color_soft, 1)
e.Display.DrawLine(ltb, rbb, color_soft, 1)
# Achs-Marker (durchgestrichen wo das Loch sitzt)
ax_l = rg.Point3d(cx - hb * tx, cy - hb * ty, on_axis.Z)
ax_r = rg.Point3d(cx + hb * tx, cy + hb * ty, on_axis.Z)
try: e.Display.DrawDottedLine(ax_l, ax_r, color_dotted)
except Exception: e.Display.DrawLine(ax_l, ax_r, color_dotted, 1)
# Brueestungs-Linie (gepunktet, nur Fenster mit Brueest > 0)
if typ == "fenster" and brueest > 1e-4:
# quer ueber die Vorderflaeche
bL = rg.Point3d(cx - hb * tx + hd * nx, cy - hb * ty + hd * ny, z_bot)
bR = rg.Point3d(cx + hb * tx + hd * nx, cy + hb * ty + hd * ny, z_bot)
try: e.Display.DrawDottedLine(bL, bR, color_dotted)
except Exception: pass
# Mass-Label ueberm Sturz
try:
if typ == "fenster":
label = "{:.2f} x {:.2f} Br {:.2f}".format(breite, hoehe, brueest)
else:
label = "{:.2f} x {:.2f}".format(breite, hoehe)
anchor = rg.Point3d(cx, cy, z_top + 0.12)
# Plane: X = Tangente (horizontal an Wand), Y = vertikal
text_plane = rg.Plane(anchor,
rg.Vector3d(tx, ty, 0),
rg.Vector3d(0, 0, 1))
t3d = Rhino.Display.Text3d(label, text_plane, 0.10)
try: t3d.HorizontalAlignment = Rhino.DocObjects.TextHorizontalAlignment.Center
except Exception: pass
e.Display.Draw3dText(t3d, color_main)
except Exception: pass
except Exception: pass except Exception: pass
return handler return handler
@@ -5578,10 +5629,16 @@ class ElementeBridge(panel_base.BaseBridge):
gp.SetCommandPrompt(prompt) gp.SetCommandPrompt(prompt)
try: gp.Constrain(axis_curve, False) try: gp.Constrain(axis_curve, False)
except Exception: pass except Exception: pass
# Live-Preview: gruenes Oeffnungs-Rechteck auf der Wand # Live-Preview: gruener Oeffnungs-Quader mit Glas-Diagonalen,
# Brueest-Marker und Mass-Label oberhalb des Sturzes
try:
wall_dicke = float(wall_meta.get("dicke", 0.15))
except Exception:
wall_dicke = 0.15
try: try:
gp.DynamicDraw += _make_oeffnung_preview( gp.DynamicDraw += _make_oeffnung_preview(
axis_curve, breite, hoehe, brueest, preview_base_z) axis_curve, wall_dicke, breite, hoehe, brueest,
preview_base_z, typ)
except Exception: pass except Exception: pass
opt_b = gp.AddOption("Breite") opt_b = gp.AddOption("Breite")
opt_h = gp.AddOption("Hoehe") opt_h = gp.AddOption("Hoehe")
+139 -64
View File
@@ -99,117 +99,192 @@ def _find_linetype_index(doc, name):
return -1 return -1
def _try_set(obj, prop_names, value):
"""Versucht den Wert auf das erste vorhandene Property zu setzen.
Liefert den Property-Namen bei Erfolg, sonst None."""
if isinstance(prop_names, str):
prop_names = (prop_names,)
for prop in prop_names:
if hasattr(obj, prop):
try:
setattr(obj, prop, value)
return prop
except Exception as ex:
# Property da, aber Wert nicht akzeptiert (z.B. enum-conversion)
# — anderen Namen probieren statt aufgeben
continue
return None
def _enum_int(*candidates):
"""Liefert den ersten Enum-Wert aus den Kandidaten der existiert.
candidates = list of (module-path-list, value-name). Bei keiner Match
liefert None."""
for path, val_name in candidates:
try:
obj = Rhino
for p in path:
obj = getattr(obj, p)
return getattr(obj, val_name)
except Exception:
continue
return None
def _apply_section_style(doc, layer, section_cfg, layer_color): def _apply_section_style(doc, layer, section_cfg, layer_color):
"""Setzt einen Custom-SectionStyle auf den Layer aus dem Dossier-section-dict. """Setzt einen Custom-SectionStyle auf den Layer aus dem Dossier-section-dict.
Nutzt Rhino-8's Python-3-API (Rhino.DocObjects.SectionStyle + Nutzt Rhino-8's Python-3-API (Rhino.DocObjects.SectionStyle +
Layer.SetCustomSectionStyle / RemoveCustomSectionStyle). In IPy 2.7 Layer.SetCustomSectionStyle / RemoveCustomSectionStyle). In IPy 2.7
sind diese Methoden nicht exponiert — dort no-op (mit Print-Warnung). sind diese Methoden nicht exponiert — dort no-op.
Wichtig: viele Farb-/Linetype-Properties greifen nur wenn der
zugehoerige "*Source"-Wert auf ColorFromObject / LinetypeFromObject
steht. Das setzen wir explizit.
""" """
if not section_cfg or not isinstance(section_cfg, dict): if not section_cfg or not isinstance(section_cfg, dict):
return return
has_setter = hasattr(layer, "SetCustomSectionStyle") has_setter = hasattr(layer, "SetCustomSectionStyle")
has_remover = hasattr(layer, "RemoveCustomSectionStyle") has_remover = hasattr(layer, "RemoveCustomSectionStyle")
if not has_setter: if not has_setter:
# IPy-2.7-Pfad: API nicht da, leise raus. return # IPy-2.7 — keine API
return
try: try:
SS = Rhino.DocObjects.SectionStyle SS = Rhino.DocObjects.SectionStyle
except Exception as ex: except Exception as ex:
print("[EBENEN] SectionStyle-Klasse nicht da:", ex); return print("[EBENEN] SectionStyle-Klasse nicht da:", ex); return
# Wenn alles "leer/Default": Custom-Style abschalten
pat = (section_cfg.get("hatchPattern") or "None").strip() pat = (section_cfg.get("hatchPattern") or "None").strip()
show = section_cfg.get("boundaryShow", True) show = bool(section_cfg.get("boundaryShow", True))
if pat == "None" and not show and has_remover: diag = "[SS:{}]".format(layer.Name if layer else "?")
try: layer.RemoveCustomSectionStyle()
except Exception: pass # Wenn weder Hatch noch Boundary → Custom-Style entfernen
if pat == "None" and not show:
if has_remover:
try:
layer.RemoveCustomSectionStyle()
print(diag, "removed (kein Hatch + kein Boundary)")
except Exception as ex:
print(diag, "remove fehlgeschlagen:", ex)
return return
style = SS() style = SS()
# Property-Inventar einmal beim ersten Aufruf loggen — hilft bei
# API-Verschiebungen zwischen Rhino-Versionen sofort den Mismatch zu sehen.
if not getattr(_apply_section_style, "_props_logged", False):
props = [n for n in dir(style)
if not n.startswith("_") and not callable(getattr(style, n, None))]
print("[SS] verfuegbare Properties:", ", ".join(sorted(props)))
_apply_section_style._props_logged = True
# --- Hatch --- # --- Hatch ---
if pat and pat != "None": if pat and pat != "None":
hp_idx = _find_hatch_pattern_index(doc, pat) hp_idx = _find_hatch_pattern_index(doc, pat)
if hp_idx >= 0: if hp_idx >= 0:
# Property-Name probieren — Rhino-8 hat HatchIndex set_to = _try_set(style, ("HatchIndex", "HatchPatternIndex"), hp_idx)
for prop in ("HatchIndex", "HatchPatternIndex"): print(diag, "HatchIndex={} via {}".format(hp_idx, set_to))
if hasattr(style, prop): else:
try: setattr(style, prop, hp_idx); break print(diag, "Hatch '{}' nicht in HatchPatterns gefunden".format(pat))
except Exception: pass
# Hatch-Scale scale_v = float(section_cfg.get("hatchScale") or 1.0)
for prop in ("HatchScale", "HatchPatternScale"): _try_set(style, ("HatchScale", "HatchPatternScale"), scale_v)
if hasattr(style, prop):
try: setattr(style, prop, float(section_cfg.get("hatchScale") or 1.0)); break
except Exception: pass
# Hatch-Rotation (Rhino erwartet Radians — wir bekommen Grad)
import math import math
rot_deg = float(section_cfg.get("hatchRotation") or 0) rot_deg = float(section_cfg.get("hatchRotation") or 0)
for prop in ("HatchRotation", "HatchAngle"): _try_set(style, ("HatchRotation", "HatchAngle"), math.radians(rot_deg))
if hasattr(style, prop):
try: # Hatch-Color: explizit ColorFromObject setzen damit der eigene Wert greift
setattr(style, prop, math.radians(rot_deg))
break
except Exception: pass
# Hatch-Color (null = ByObject = nicht setzen)
hatch_color = section_cfg.get("hatchColor") hatch_color = section_cfg.get("hatchColor")
if hatch_color: if hatch_color:
for prop in ("HatchColor", "FillColor"): col = _color(hatch_color)
if hasattr(style, prop): set_color = _try_set(style, ("HatchColor", "FillColor"), col)
try: setattr(style, prop, _color(hatch_color)); break # Source auf "FromObject" — sonst nutzt Rhino den Layer-Color
except Exception: pass src_from_object = _enum_int(
# Background (("DocObjects", "ObjectColorSource"), "ColorFromObject"))
if src_from_object is not None:
_try_set(style, ("HatchColorSource", "FillColorSource"), src_from_object)
print(diag, "HatchColor via {}".format(set_color))
# Background (viewport=0/transparent vs object=1)
bg = section_cfg.get("background") bg = section_cfg.get("background")
if bg in ("object", "byObject"): if bg in ("object", "byObject"):
for prop in ("BackgroundColorUsage", "FillBackground"): # Versuche Enum-Konstanten zu finden
if hasattr(style, prop): for prop_names, en_paths in (
# Enum-Werte sind versioniert; wir versuchen via int (("BackgroundFillMode", "BackgroundColorUsage", "FillBackground"),
try: setattr(style, prop, 1); break (("DocObjects", "SectionBackgroundFillMode"), "SolidColor")),
except Exception: pass ):
en_val = _enum_int(en_paths)
if en_val is not None and _try_set(style, prop_names, en_val):
break
# Fallback: bool/int = 1
else:
_try_set(style, ("FillBackground",), True)
# --- Boundary --- # --- Boundary ---
if hasattr(style, "BoundaryVisible"): set_show = _try_set(style, ("BoundaryVisible", "ShowBoundary"), show)
try: style.BoundaryVisible = bool(show) print(diag, "BoundaryVisible={} via {}".format(show, set_show))
except Exception: pass
elif hasattr(style, "ShowBoundary"):
try: style.ShowBoundary = bool(show)
except Exception: pass
if show: if show:
# Boundary color # Boundary-Color: setze Color + Source auf FromObject
bc = section_cfg.get("boundaryColor") bc = section_cfg.get("boundaryColor")
if bc: if bc:
for prop in ("BoundaryColor", "OutlineColor", "EdgeColor"): col = _color(bc)
if hasattr(style, prop): set_to = _try_set(style,
try: setattr(style, prop, _color(bc)); break ("BoundaryColor", "OutlineColor", "EdgeColor"), col)
except Exception: pass src_from_object = _enum_int(
# Boundary width scale (("DocObjects", "ObjectColorSource"), "ColorFromObject"))
if src_from_object is not None:
_try_set(style,
("BoundaryColorSource", "OutlineColorSource",
"EdgeColorSource"),
src_from_object)
print(diag, "BoundaryColor={} via {}".format(bc, set_to))
# Width-Scale auf PlotWeight uebertragen (RW8 hat keine WidthScale direkt;
# alternative Property-Namen probieren)
ws = float(section_cfg.get("boundaryWidthScale") or 1.0) ws = float(section_cfg.get("boundaryWidthScale") or 1.0)
for prop in ("BoundaryWidthScale", "EdgeWidthScale", "OutlineWidthScale"): set_to = _try_set(style,
if hasattr(style, prop): ("BoundaryWidthScale", "EdgeWidthScale", "OutlineWidthScale",
try: setattr(style, prop, ws); break "PlotWeightScale"), ws)
except Exception: pass if not set_to:
# Linetype # Direkte PlotWeight setzen wenn Layer-PlotWeight bekannt
try:
base_lw = float(getattr(layer, "PlotWeight", 0.25) or 0.25)
except Exception:
base_lw = 0.25
_try_set(style, ("BoundaryPlotWeight", "PlotWeight"), base_lw * ws)
# Linetype: Index + Source auf LinetypeFromObject
lt = section_cfg.get("boundaryLinetype") lt = section_cfg.get("boundaryLinetype")
if lt and lt not in ("byLayer", "ByLayer"): if lt and lt not in ("byLayer", "ByLayer"):
lt_idx = _find_linetype_index(doc, lt) lt_idx = _find_linetype_index(doc, lt)
for prop in ("BoundaryLinetypeIndex", "EdgeLinetypeIndex"): set_to = _try_set(style,
if hasattr(style, prop): ("BoundaryLinetypeIndex", "EdgeLinetypeIndex"), lt_idx)
try: setattr(style, prop, lt_idx); break lt_src = _enum_int(
except Exception: pass (("DocObjects", "ObjectLinetypeSource"), "LinetypeFromObject"))
if lt_src is not None:
_try_set(style,
("BoundaryLinetypeSource", "EdgeLinetypeSource"),
lt_src)
print(diag, "BoundaryLinetype={} idx={} via {}".format(lt, lt_idx, set_to))
# Section open objects # SectionOpenObjects: bei nicht-geschlossener Geometrie auch schneiden
soo = bool(section_cfg.get("sectionOpenObjects", True)) soo = bool(section_cfg.get("sectionOpenObjects", True))
for prop in ("SectionOpenObjects", "ClipOpenObjects"): _try_set(style, ("SectionOpenObjects", "ClipOpenObjects",
if hasattr(style, prop): "SectionCutsOpenObjects"), soo)
try: setattr(style, prop, soo); break
except Exception: pass
# Style auf Layer setzen # Style auf Layer setzen + explizit Modify damit Mac-Rhino den Layer
# persistiert (sonst greift's nicht immer)
try: try:
layer.SetCustomSectionStyle(style) layer.SetCustomSectionStyle(style)
except Exception as ex: except Exception as ex:
print("[EBENEN] SetCustomSectionStyle({}): {}".format(layer.Name, ex)) print(diag, "SetCustomSectionStyle FAIL:", ex)
return
try:
doc.Layers.Modify(layer, layer.LayerIndex, True)
except Exception: pass
print(diag, "OK applied")
def build_layers(doc, zeichnungsebenen, ebenen): def build_layers(doc, zeichnungsebenen, ebenen):