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:
+139
-64
@@ -99,117 +99,192 @@ def _find_linetype_index(doc, name):
|
||||
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):
|
||||
"""Setzt einen Custom-SectionStyle auf den Layer aus dem Dossier-section-dict.
|
||||
|
||||
Nutzt Rhino-8's Python-3-API (Rhino.DocObjects.SectionStyle +
|
||||
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):
|
||||
return
|
||||
has_setter = hasattr(layer, "SetCustomSectionStyle")
|
||||
has_remover = hasattr(layer, "RemoveCustomSectionStyle")
|
||||
if not has_setter:
|
||||
# IPy-2.7-Pfad: API nicht da, leise raus.
|
||||
return
|
||||
return # IPy-2.7 — keine API
|
||||
|
||||
try:
|
||||
SS = Rhino.DocObjects.SectionStyle
|
||||
except Exception as ex:
|
||||
print("[EBENEN] SectionStyle-Klasse nicht da:", ex); return
|
||||
|
||||
# Wenn alles "leer/Default": Custom-Style abschalten
|
||||
pat = (section_cfg.get("hatchPattern") or "None").strip()
|
||||
show = section_cfg.get("boundaryShow", True)
|
||||
if pat == "None" and not show and has_remover:
|
||||
try: layer.RemoveCustomSectionStyle()
|
||||
except Exception: pass
|
||||
show = bool(section_cfg.get("boundaryShow", True))
|
||||
diag = "[SS:{}]".format(layer.Name if layer else "?")
|
||||
|
||||
# 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
|
||||
|
||||
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 ---
|
||||
if pat and pat != "None":
|
||||
hp_idx = _find_hatch_pattern_index(doc, pat)
|
||||
if hp_idx >= 0:
|
||||
# Property-Name probieren — Rhino-8 hat HatchIndex
|
||||
for prop in ("HatchIndex", "HatchPatternIndex"):
|
||||
if hasattr(style, prop):
|
||||
try: setattr(style, prop, hp_idx); break
|
||||
except Exception: pass
|
||||
# Hatch-Scale
|
||||
for prop in ("HatchScale", "HatchPatternScale"):
|
||||
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)
|
||||
set_to = _try_set(style, ("HatchIndex", "HatchPatternIndex"), hp_idx)
|
||||
print(diag, "HatchIndex={} via {}".format(hp_idx, set_to))
|
||||
else:
|
||||
print(diag, "Hatch '{}' nicht in HatchPatterns gefunden".format(pat))
|
||||
|
||||
scale_v = float(section_cfg.get("hatchScale") or 1.0)
|
||||
_try_set(style, ("HatchScale", "HatchPatternScale"), scale_v)
|
||||
|
||||
import math
|
||||
rot_deg = float(section_cfg.get("hatchRotation") or 0)
|
||||
for prop in ("HatchRotation", "HatchAngle"):
|
||||
if hasattr(style, prop):
|
||||
try:
|
||||
setattr(style, prop, math.radians(rot_deg))
|
||||
break
|
||||
except Exception: pass
|
||||
# Hatch-Color (null = ByObject = nicht setzen)
|
||||
_try_set(style, ("HatchRotation", "HatchAngle"), math.radians(rot_deg))
|
||||
|
||||
# Hatch-Color: explizit ColorFromObject setzen damit der eigene Wert greift
|
||||
hatch_color = section_cfg.get("hatchColor")
|
||||
if hatch_color:
|
||||
for prop in ("HatchColor", "FillColor"):
|
||||
if hasattr(style, prop):
|
||||
try: setattr(style, prop, _color(hatch_color)); break
|
||||
except Exception: pass
|
||||
# Background
|
||||
col = _color(hatch_color)
|
||||
set_color = _try_set(style, ("HatchColor", "FillColor"), col)
|
||||
# Source auf "FromObject" — sonst nutzt Rhino den Layer-Color
|
||||
src_from_object = _enum_int(
|
||||
(("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")
|
||||
if bg in ("object", "byObject"):
|
||||
for prop in ("BackgroundColorUsage", "FillBackground"):
|
||||
if hasattr(style, prop):
|
||||
# Enum-Werte sind versioniert; wir versuchen via int
|
||||
try: setattr(style, prop, 1); break
|
||||
except Exception: pass
|
||||
# Versuche Enum-Konstanten zu finden
|
||||
for prop_names, en_paths in (
|
||||
(("BackgroundFillMode", "BackgroundColorUsage", "FillBackground"),
|
||||
(("DocObjects", "SectionBackgroundFillMode"), "SolidColor")),
|
||||
):
|
||||
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 ---
|
||||
if hasattr(style, "BoundaryVisible"):
|
||||
try: style.BoundaryVisible = bool(show)
|
||||
except Exception: pass
|
||||
elif hasattr(style, "ShowBoundary"):
|
||||
try: style.ShowBoundary = bool(show)
|
||||
except Exception: pass
|
||||
set_show = _try_set(style, ("BoundaryVisible", "ShowBoundary"), show)
|
||||
print(diag, "BoundaryVisible={} via {}".format(show, set_show))
|
||||
|
||||
if show:
|
||||
# Boundary color
|
||||
# Boundary-Color: setze Color + Source auf FromObject
|
||||
bc = section_cfg.get("boundaryColor")
|
||||
if bc:
|
||||
for prop in ("BoundaryColor", "OutlineColor", "EdgeColor"):
|
||||
if hasattr(style, prop):
|
||||
try: setattr(style, prop, _color(bc)); break
|
||||
except Exception: pass
|
||||
# Boundary width scale
|
||||
col = _color(bc)
|
||||
set_to = _try_set(style,
|
||||
("BoundaryColor", "OutlineColor", "EdgeColor"), col)
|
||||
src_from_object = _enum_int(
|
||||
(("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)
|
||||
for prop in ("BoundaryWidthScale", "EdgeWidthScale", "OutlineWidthScale"):
|
||||
if hasattr(style, prop):
|
||||
try: setattr(style, prop, ws); break
|
||||
except Exception: pass
|
||||
# Linetype
|
||||
set_to = _try_set(style,
|
||||
("BoundaryWidthScale", "EdgeWidthScale", "OutlineWidthScale",
|
||||
"PlotWeightScale"), ws)
|
||||
if not set_to:
|
||||
# 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")
|
||||
if lt and lt not in ("byLayer", "ByLayer"):
|
||||
lt_idx = _find_linetype_index(doc, lt)
|
||||
for prop in ("BoundaryLinetypeIndex", "EdgeLinetypeIndex"):
|
||||
if hasattr(style, prop):
|
||||
try: setattr(style, prop, lt_idx); break
|
||||
except Exception: pass
|
||||
set_to = _try_set(style,
|
||||
("BoundaryLinetypeIndex", "EdgeLinetypeIndex"), lt_idx)
|
||||
lt_src = _enum_int(
|
||||
(("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))
|
||||
for prop in ("SectionOpenObjects", "ClipOpenObjects"):
|
||||
if hasattr(style, prop):
|
||||
try: setattr(style, prop, soo); break
|
||||
except Exception: pass
|
||||
_try_set(style, ("SectionOpenObjects", "ClipOpenObjects",
|
||||
"SectionCutsOpenObjects"), soo)
|
||||
|
||||
# Style auf Layer setzen
|
||||
# Style auf Layer setzen + explizit Modify damit Mac-Rhino den Layer
|
||||
# persistiert (sonst greift's nicht immer)
|
||||
try:
|
||||
layer.SetCustomSectionStyle(style)
|
||||
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):
|
||||
|
||||
Reference in New Issue
Block a user