T-Junction Phase 2 mit 3D Brep Union + Material-Prio-Carve
Asymmetric L-merge fuer Schichtdurchdringung: - Backbone (= hoechste Material-Prio in beiden Waenden) bildet T-form - Non-backbone Layer werden gecarved mit backbone-Column + through-Bands hoeherer Prio - ext=0 fuer T-Stem-Axis (= column endet am snap, kein "drueber") - 3D Brep Union via Brep.CreateBooleanUnion mit cross-junction safety (= aktueller through-Brep statt von Meta neu zu bauen) - Cleanup: MergeCoplanarFaces Plus: - Innenwand Beton 20cm Style (Putz + Beton + Putz, ref-mid) - curve_vertex_dots.py: gruene Vertex-Punkte fuer Polylinen/Curves - Cluster-Volume Select Handler: Shift-Modifier fuer Multi-Select - startup.py: Top-View maximieren on Doc-Open Known limitation: Putz-Schicht kann in bestimmten Konfigurationen visuell suedlich des Daemm-Band-Top weiter sichtbar sein (= edge case fuer asymmetric layers). Naechster Schritt: manuelle 2D-Polygon-Konstruktion statt 3D Boolean.
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
#! python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (C) 2026 Karim Gabriele Varano
|
||||
"""
|
||||
curve_vertex_dots.py
|
||||
Display-only Vertex-Dots fuer GENERISCHE Curves (Polylinen, Linien,
|
||||
Rectangles, NurbsCurves etc). Zeigt gruene Punkte an allen Vertices
|
||||
selektierter Curves — hilft beim Visuell-Finden von Grip-Positionen
|
||||
wenn die Curve eine Fuellung (Hatch) hat und schwer per Klick auf
|
||||
einen einzelnen Vertex zu treffen ist.
|
||||
|
||||
Display-only — kein eigener Drag-Handler. User editiert Vertices via
|
||||
Rhino's native _Grips (Punkte sichtbar machen + Standard-Drag) oder
|
||||
direktes Object-Snapping waehrend Drag.
|
||||
|
||||
Skipt dossier-managed Curves (wand_axis, treppe_axis, schnitt_axis,
|
||||
wand_outline, wand_centerline, raum_polylinie etc) — die haben ihre
|
||||
eigenen Conduits oder duerfen nicht via Vertex editiert werden.
|
||||
"""
|
||||
import Rhino
|
||||
import Rhino.Display as rd
|
||||
import Rhino.Geometry as rg
|
||||
import scriptcontext as sc
|
||||
import System.Drawing as SD
|
||||
|
||||
|
||||
# --- Konstanten ------------------------------------------------------------
|
||||
|
||||
_MARKER_RADIUS_PX = 6
|
||||
_MARKER_FILL = SD.Color.FromArgb(200, 95, 168, 150) # accent-gruen
|
||||
_MARKER_BORDER = SD.Color.FromArgb(255, 47, 93, 84)
|
||||
|
||||
# Dossier-managed Element-Types die NICHT mit generic dots versehen werden
|
||||
# (= haben eigene Conduits oder sind nicht editierbar via Vertex-Click).
|
||||
_SKIP_TYPES = {
|
||||
"wand_axis", "wand_centerline", "wand_outline", "wand_volume",
|
||||
"treppe_axis", "treppe_outline", "treppe_volume",
|
||||
"schnitt_axis", "schnitt_outline",
|
||||
"raum_polylinie", "raum_stempel",
|
||||
"ausschnitt_polylinie",
|
||||
"decke_polylinie", "decke_volume",
|
||||
"dach_polylinie", "dach_volume",
|
||||
}
|
||||
|
||||
|
||||
# --- Helpers --------------------------------------------------------------
|
||||
|
||||
def _is_dossier_managed(obj):
|
||||
"""True wenn obj ein dossier-managed Element ist (= Skip)."""
|
||||
if obj is None or obj.IsDeleted: return True
|
||||
try:
|
||||
t = obj.Attributes.GetUserString("dossier_element_type") or ""
|
||||
return t in _SKIP_TYPES
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _curve_vertices(curve):
|
||||
"""Liefert Liste von rg.Point3d fuer alle relevanten Vertices der
|
||||
Curve. Verschiedene Curve-Types haben verschiedene Vertices:
|
||||
- LineCurve: 2 Endpunkte
|
||||
- PolylineCurve: alle Polyline-Punkte (deduplizert wenn closed)
|
||||
- PolyCurve: rekursiv Segmente
|
||||
- NurbsCurve/sonst: Start + End (control points nicht — zu viele)"""
|
||||
pts = []
|
||||
if curve is None: return pts
|
||||
try:
|
||||
if isinstance(curve, rg.PolylineCurve):
|
||||
ok, pline = curve.TryGetPolyline()
|
||||
if ok and pline is not None:
|
||||
n = pline.Count
|
||||
# Deduplizieren wenn closed (letzter Punkt = erster)
|
||||
last = n
|
||||
try:
|
||||
if (n >= 2
|
||||
and pline[0].DistanceTo(pline[n - 1]) < 1e-6):
|
||||
last = n - 1
|
||||
except Exception: pass
|
||||
for i in range(last):
|
||||
pts.append(rg.Point3d(pline[i]))
|
||||
return pts
|
||||
if isinstance(curve, rg.LineCurve):
|
||||
pts.append(curve.PointAtStart)
|
||||
pts.append(curve.PointAtEnd)
|
||||
return pts
|
||||
if isinstance(curve, rg.PolyCurve):
|
||||
for i in range(curve.SegmentCount):
|
||||
seg = curve.SegmentCurve(i)
|
||||
if seg is None: continue
|
||||
# Nur Start jedes Segments (End ist Start des naechsten)
|
||||
pts.append(seg.PointAtStart)
|
||||
# Letztes Segment-End anhaengen
|
||||
try:
|
||||
pts.append(curve.PointAtEnd)
|
||||
except Exception: pass
|
||||
return pts
|
||||
# Generic Curve: nur Start + End
|
||||
try:
|
||||
pts.append(curve.PointAtStart)
|
||||
pts.append(curve.PointAtEnd)
|
||||
except Exception: pass
|
||||
except Exception:
|
||||
pass
|
||||
return pts
|
||||
|
||||
|
||||
# --- Conduit -------------------------------------------------------------
|
||||
|
||||
class _VertexDotConduit(rd.DisplayConduit):
|
||||
"""Zeichnet bei jeder selektierten generischen Curve gruene Punkte
|
||||
an allen Vertices."""
|
||||
|
||||
def DrawForeground(self, e):
|
||||
try:
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
if doc is None: return
|
||||
try:
|
||||
sel = list(doc.Objects.GetSelectedObjects(False, False))
|
||||
except Exception: return
|
||||
seen_curve_ids = set()
|
||||
for obj in sel:
|
||||
if _is_dossier_managed(obj): continue
|
||||
try:
|
||||
cid = str(obj.Id)
|
||||
except Exception: continue
|
||||
if cid in seen_curve_ids: continue
|
||||
seen_curve_ids.add(cid)
|
||||
geom = obj.Geometry
|
||||
if not isinstance(geom, rg.Curve): continue
|
||||
for pt in _curve_vertices(geom):
|
||||
try:
|
||||
e.Display.DrawPoint(
|
||||
pt, rd.PointStyle.RoundControlPoint,
|
||||
_MARKER_RADIUS_PX, _MARKER_FILL)
|
||||
except Exception:
|
||||
try: e.Display.DrawDot(
|
||||
pt, "·", _MARKER_FILL, _MARKER_BORDER)
|
||||
except Exception: pass
|
||||
except Exception as ex:
|
||||
print("[CURVE_DOTS] DrawForeground:", ex)
|
||||
|
||||
|
||||
# --- Install -------------------------------------------------------------
|
||||
|
||||
_STICKY_CONDUIT = "_dossier_curve_vertex_dots_conduit"
|
||||
|
||||
|
||||
def install_curve_vertex_dots():
|
||||
"""Idempotent: alten Conduit disable, neuen installieren."""
|
||||
try:
|
||||
old = sc.sticky.get(_STICKY_CONDUIT)
|
||||
if old is not None:
|
||||
try: old.Enabled = False
|
||||
except Exception: pass
|
||||
conduit = _VertexDotConduit()
|
||||
conduit.Enabled = True
|
||||
sc.sticky[_STICKY_CONDUIT] = conduit
|
||||
print("[CURVE_DOTS] Vertex-Dot-Conduit aktiv")
|
||||
except Exception as ex:
|
||||
print("[CURVE_DOTS] install:", ex)
|
||||
+534
-51
@@ -2214,7 +2214,8 @@ def _detect_t_junction(doc, geschoss_id, wall_id, endpoint,
|
||||
tan = geom.TangentAt(t)
|
||||
return (meta["id"],
|
||||
rg.Vector3d(tan.X, tan.Y, 0),
|
||||
float(meta["dicke"]))
|
||||
float(meta["dicke"]),
|
||||
meta.get("referenz", "mid"))
|
||||
except Exception: continue
|
||||
return None
|
||||
|
||||
@@ -2299,32 +2300,30 @@ def _detect_through_wall_at(doc, partners_at_joint, exclude_self_tan):
|
||||
# Gefunden: ti/tj formen die Through-Wand
|
||||
mi = _wand_meta_by_id(doc, wi)
|
||||
b_dicke = float((mi or {}).get("dicke", 0.25))
|
||||
return (rg.Vector3d(ti.X, ti.Y, 0), b_dicke)
|
||||
b_ref = (mi or {}).get("referenz", "mid")
|
||||
return (rg.Vector3d(ti.X, ti.Y, 0), b_dicke, b_ref)
|
||||
return None
|
||||
|
||||
|
||||
def _t_junction_miter(endpoint, out_dir, b_tan, b_dicke):
|
||||
def _t_junction_miter(endpoint, out_dir, b_tan, b_dicke, b_referenz="mid"):
|
||||
"""Berechnet (miter_pt, miter_dir) fuer einen T-Stoss.
|
||||
miter_dir = Tangente der Durchgangs-Wand (Linie laeuft parallel zu B's Achse).
|
||||
miter_pt = endpoint verschoben um d_B/2 in Approach-Richtung — also auf
|
||||
der NAHEN Aussenflaeche von B (der Seite an der A ankommt).
|
||||
|
||||
A (= T-Stem, das zweite Wand-Stueck) wird an dieser Stelle abgeschnitten;
|
||||
B (= Durchgangswand) bleibt unveraendert. Das ist das BIM-Standard-
|
||||
Verhalten: 'erste Wand bleibt, zweite haengt sich dran'."""
|
||||
miter_pt = endpoint verschoben bis zur NAHEN Aussenflaeche von B.
|
||||
Beruecksichtigt B's referenz: bei mid ist Axis Centerline (Aussen-
|
||||
flaechen ±d/2), bei left/right ist Axis auf einer Aussenkante."""
|
||||
perp_b = rg.Vector3d(-b_tan.Y, b_tan.X, 0)
|
||||
try: perp_b.Unitize()
|
||||
except Exception: return None
|
||||
# A's Body liegt auf der Seite -out_dir. Approach-Seite (perp_b
|
||||
# ausgerichtet zur Approach) = sign(dot(-out_dir, perp_b)).
|
||||
s = -(out_dir.X * perp_b.X + out_dir.Y * perp_b.Y)
|
||||
if abs(s) < 1e-6:
|
||||
# A parallel zu B — kein sauberer T-Stoss
|
||||
return None
|
||||
if abs(s) < 1e-6: return None
|
||||
side = 1.0 if s > 0 else -1.0
|
||||
off = float(b_dicke) * 0.5 * side
|
||||
mpt = rg.Point3d(endpoint.X + perp_b.X * off,
|
||||
endpoint.Y + perp_b.Y * off, 0)
|
||||
# B's Aussenflaechen-Offsets entlang perp_b
|
||||
start_off, d_total = _wall_offsets_from_referenz(b_dicke, b_referenz)
|
||||
off_a = start_off
|
||||
off_b = start_off - d_total
|
||||
# Nahe Aussenflaeche = die in approach-Richtung naehere (= side-Richtung)
|
||||
near_off = max(off_a, off_b) if side > 0 else min(off_a, off_b)
|
||||
mpt = rg.Point3d(endpoint.X + perp_b.X * near_off,
|
||||
endpoint.Y + perp_b.Y * near_off, 0)
|
||||
mdir = rg.Vector3d(b_tan.X, b_tan.Y, 0)
|
||||
try: mdir.Unitize()
|
||||
except Exception: pass
|
||||
@@ -2867,11 +2866,17 @@ class _ClusterVolumeSelectHandler(Rhino.UI.MouseCallback):
|
||||
best_d2 = d2; best_axis = c_ax
|
||||
except Exception: continue
|
||||
if best_axis is None: return
|
||||
# Shift-Modifier: ADD zur Selektion (kein UnselectAll)
|
||||
try:
|
||||
_shift = bool(e.ShiftKeyDown)
|
||||
except Exception:
|
||||
_shift = False
|
||||
try: e.Cancel = True
|
||||
except Exception: pass
|
||||
self._busy = True
|
||||
try:
|
||||
doc.Objects.UnselectAll()
|
||||
if not _shift:
|
||||
doc.Objects.UnselectAll()
|
||||
doc.Objects.Select(best_axis.Id, True)
|
||||
try: view.Redraw()
|
||||
except Exception: pass
|
||||
@@ -3367,6 +3372,53 @@ def _make_wall_layer_brep(axis_curve, d_left, d_right, uk, ok,
|
||||
return extrusion.ToBrep()
|
||||
|
||||
|
||||
def _layer_rect_2d(axis_curve, d_left, d_right):
|
||||
"""Liefert geschlossene XY-Rect-Curve fuer eine Schicht (Achse +
|
||||
perp Offsets). Genutzt fuer 2D-Polylinen-Union beim T-Junction.
|
||||
Nutzt _offset_curve wie _make_wall_layer_brep — wichtig fuer
|
||||
Boolean-Compatibility (PolyCurve statt PolylineCurve).
|
||||
Forced z=0 + CCW winding (= ClosedCurveOrientation check)."""
|
||||
if not isinstance(axis_curve, rg.Curve): return None
|
||||
d_l = float(d_left); d_r = float(d_right)
|
||||
if abs(d_l - d_r) < 1e-9: return None
|
||||
try:
|
||||
# Force axis to z=0 plane
|
||||
ax_xy = axis_curve.DuplicateCurve()
|
||||
if abs(ax_xy.PointAtStart.Z) > 1e-9 or abs(ax_xy.PointAtEnd.Z) > 1e-9:
|
||||
ax_xy.Transform(rg.Transform.PlaneToPlane(
|
||||
rg.Plane(rg.Point3d(0,0,ax_xy.PointAtStart.Z),
|
||||
rg.Vector3d.ZAxis),
|
||||
rg.Plane.WorldXY))
|
||||
plane = rg.Plane.WorldXY
|
||||
tol = 0.001
|
||||
left = _offset_curve(ax_xy, plane, d_l, tol)
|
||||
right = _offset_curve(ax_xy, plane, d_r, tol)
|
||||
if not left or not right: return None
|
||||
L = left[0]; R = right[0]
|
||||
R.Reverse()
|
||||
cap_s = rg.LineCurve(L.PointAtEnd, R.PointAtStart)
|
||||
cap_e = rg.LineCurve(R.PointAtEnd, L.PointAtStart)
|
||||
joined = rg.Curve.JoinCurves([L, cap_s, R, cap_e], tol)
|
||||
if not joined or len(joined) == 0 or not joined[0].IsClosed:
|
||||
return None
|
||||
out = joined[0]
|
||||
# Force z=0 (offset_curve might preserve original z)
|
||||
try:
|
||||
if (abs(out.PointAtStart.Z) > 1e-9):
|
||||
xform = rg.Transform.Translation(0, 0, -out.PointAtStart.Z)
|
||||
out.Transform(xform)
|
||||
except Exception: pass
|
||||
# Force CCW orientation (= positive area in WorldXY)
|
||||
try:
|
||||
ori = out.ClosedCurveOrientation(rg.Plane.WorldXY)
|
||||
if ori == rg.CurveOrientation.Clockwise:
|
||||
out.Reverse()
|
||||
except Exception: pass
|
||||
return out
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _wall_offsets_from_referenz(dicke, referenz):
|
||||
"""Liefert (start_offset, d_total) — start_offset ist der Wert von 'links'
|
||||
relativ zur Achse, d_total ist die Summe der Wand-Dicke (immer positiv)."""
|
||||
@@ -3600,19 +3652,38 @@ def _wand_meta_prio(doc, meta):
|
||||
except Exception: return 500
|
||||
|
||||
|
||||
# Material-Prio fuer T-Junction Schichtdurchdringung: an einem T-Stoss
|
||||
# zwischen zwei layered Waenden geht NUR die hoechste gemeinsame
|
||||
# Material-Prio als 'Backbone' durch + uniont (= T-Form). Alle anderen
|
||||
# T-Stem-Layer T-mitern am Near-Face → bei symmetrisch geschichteten
|
||||
# Waenden ergibt das automatisch L-Stoesse mit gleichfarbigen Aussenlagen.
|
||||
_MATERIAL_PRIO = {
|
||||
"stahlbeton": 800,
|
||||
"beton": 800,
|
||||
"mauerwerk": 600,
|
||||
"kalksandstein": 600,
|
||||
"ziegel": 550,
|
||||
"holzstaender": 400,
|
||||
"holz": 400,
|
||||
"daemmung": 200,
|
||||
"putz": 100,
|
||||
}
|
||||
|
||||
|
||||
def _material_prio(mat):
|
||||
if not mat: return 0
|
||||
return _MATERIAL_PRIO.get(mat.strip().lower(), 300)
|
||||
|
||||
|
||||
def _t_junction_layer_overrides(doc, my_meta, through_meta, ep_pt, out_dir,
|
||||
b_tan, b_dicke):
|
||||
"""Per-Layer Schichtdurchdringung bei T-Junction zwischen layered Waenden.
|
||||
|
||||
Pro T-Stem-Schicht wird in der Through-Wand nach einer Schicht mit
|
||||
GLEICHEM MATERIAL gesucht:
|
||||
- Match: T-Stem-Layer extends durch die Through-Wand (Axis-Extension um
|
||||
b_dicke/2 = bis Far-Face) → visuell verbunden
|
||||
- No Match: T-Stem-Layer stoppt am Near-Face (Standard T-Miter)
|
||||
NEU: nur das BACKBONE-Material extends + uniont. Backbone = das mit
|
||||
hoechster Material-Prio das in BEIDEN Waenden vorkommt. Andere Layer
|
||||
stoppen am Near-Face (Standard T-Miter).
|
||||
|
||||
Returns (per_layer_ext, per_layer_miter) — Listen pro T-Stem-Layer.
|
||||
Wenn my keine Layers hat: (None, None) (Caller faellt auf uniform Miter
|
||||
zurueck)."""
|
||||
Returns (per_layer_ext, per_layer_miter) — Listen pro T-Stem-Layer."""
|
||||
if not my_meta or not through_meta: return None, None
|
||||
my_layers = my_meta.get("wand_layers") or []
|
||||
if not my_layers: return None, None
|
||||
@@ -3624,26 +3695,101 @@ def _t_junction_layer_overrides(doc, my_meta, through_meta, ep_pt, out_dir,
|
||||
mat = (l.get("material") or "").strip()
|
||||
if mat: th_materials.add(mat)
|
||||
else:
|
||||
# Through ist solid → 1 Material aus Style
|
||||
try:
|
||||
sm = _wand_solid_material(doc, through_meta) if doc else ""
|
||||
if sm: th_materials.add(sm)
|
||||
except Exception: pass
|
||||
|
||||
standard_miter = _t_junction_miter(ep_pt, out_dir, b_tan, b_dicke)
|
||||
extension = float(b_dicke) * 0.5 # bis Far-Face
|
||||
# Backbone = hoechste Material-Prio in BEIDEN Waenden
|
||||
my_materials = set()
|
||||
for layer in my_layers:
|
||||
m = (layer.get("material") or "").strip()
|
||||
if m: my_materials.add(m)
|
||||
common = my_materials & th_materials
|
||||
backbone = None
|
||||
backbone_prio = -1
|
||||
for m in common:
|
||||
p = _material_prio(m)
|
||||
if p > backbone_prio:
|
||||
backbone = m
|
||||
backbone_prio = p
|
||||
|
||||
# Backbone-Schicht-Position in der Through-Wand finden (perp-Offsets)
|
||||
backbone_dL = None
|
||||
backbone_dR = None
|
||||
if backbone and through_meta.get("wand_layered") and th_layers:
|
||||
th_dicke = float(through_meta.get("dicke", 0) or 0)
|
||||
th_ref = through_meta.get("referenz", "mid")
|
||||
start_off, _ = _wall_offsets_from_referenz(th_dicke, th_ref)
|
||||
cur = start_off
|
||||
for tl in th_layers:
|
||||
d = float(tl.get("dicke", 0) or 0)
|
||||
if d <= 0: continue
|
||||
if (tl.get("material") or "").strip() == backbone:
|
||||
backbone_dL = cur
|
||||
backbone_dR = cur - d
|
||||
break
|
||||
cur -= d
|
||||
|
||||
# Backbone-Extension: IMMER 0 = column endet exakt am Through-axis,
|
||||
# geht nie ueber outer face hinaus. Asymmetric Merge ist Resultat
|
||||
# der natuerlichen Geometrie:
|
||||
# - Wenn T-Stem-Body auf gleicher Seite wie Through-Wand-Body
|
||||
# (= column passes durch wall body): alle Layer mergen via 3D
|
||||
# Union + Carve (L-merge auf T-Stem-Layer-Seite, west_piece
|
||||
# getrennt).
|
||||
# - Wenn T-Stem-Body auf gegenueberliegender Seite (= column endet
|
||||
# am Through-Aussenrand, betritt wall body nicht): kein Merge,
|
||||
# visually 2 separate Walls die sich am axis touchieren.
|
||||
backbone_ext = 0.0
|
||||
if backbone_ext is None:
|
||||
backbone_ext = float(b_dicke) * 0.5
|
||||
|
||||
standard_miter = _t_junction_miter(ep_pt, out_dir, b_tan, b_dicke,
|
||||
through_meta.get("referenz", "mid"))
|
||||
# Through-Layer pro Material indexieren (Liste von (d_l, d_r))
|
||||
through_by_mat_pos = {}
|
||||
if through_meta.get("wand_layered") and th_layers:
|
||||
th_dicke2 = float(through_meta.get("dicke", 0) or 0)
|
||||
th_ref2 = through_meta.get("referenz", "mid")
|
||||
start_off2, _ = _wall_offsets_from_referenz(th_dicke2, th_ref2)
|
||||
cur2 = start_off2
|
||||
for tl in th_layers:
|
||||
d2 = float(tl.get("dicke", 0) or 0)
|
||||
if d2 <= 0: continue
|
||||
m2 = (tl.get("material") or "").strip()
|
||||
if m2:
|
||||
through_by_mat_pos.setdefault(m2, []).append(
|
||||
(cur2, cur2 - d2))
|
||||
cur2 -= d2
|
||||
# perp_b und Approach-Richtung
|
||||
perp_bx = rg.Vector3d(-b_tan.Y, b_tan.X, 0)
|
||||
try: perp_bx.Unitize()
|
||||
except Exception: pass
|
||||
dot_opx = out_dir.X * perp_bx.X + out_dir.Y * perp_bx.Y
|
||||
b_tan_u = rg.Vector3d(b_tan.X, b_tan.Y, 0)
|
||||
try: b_tan_u.Unitize()
|
||||
except Exception: pass
|
||||
per_ext = []
|
||||
per_miter = []
|
||||
match_log = []
|
||||
for layer in my_layers:
|
||||
mat = (layer.get("material") or "").strip()
|
||||
if mat and mat in th_materials:
|
||||
# Match → durchstossen
|
||||
per_ext.append(extension)
|
||||
if backbone and mat == backbone:
|
||||
per_ext.append(backbone_ext)
|
||||
per_miter.append(None)
|
||||
match_log.append("{}=BACKBONE(+{:.3f})".format(mat, backbone_ext))
|
||||
else:
|
||||
# Kein Match → an Near-Face stoppen
|
||||
# Non-backbone: Standard T-Miter an Through-Aussenkante.
|
||||
# T-Stem-Layer + Through-Putz-Band touchieren am L-Korner
|
||||
# mit gleichem Material (Same-color visual L, seam moeglich).
|
||||
per_ext.append(0.0)
|
||||
per_miter.append(standard_miter)
|
||||
match_log.append("{}=stop".format(mat or "?"))
|
||||
print("[ELEMENTE] T-Junction Schichtdurchdringung: backbone={} "
|
||||
"(prio={}, ext={:.3f}), through-mats={}, layers: {}".format(
|
||||
backbone or "none", backbone_prio, backbone_ext,
|
||||
sorted(th_materials), ", ".join(match_log)))
|
||||
return per_ext, per_miter
|
||||
|
||||
|
||||
@@ -8507,24 +8653,21 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
||||
element_id, p_s,
|
||||
exclude_ids=chain_set)
|
||||
if tj is not None:
|
||||
_oid, b_tan, b_dicke = tj
|
||||
_oid, b_tan, b_dicke, b_ref = tj
|
||||
if _wand_should_apply_t_miter(doc, meta, _oid):
|
||||
tm = _t_junction_miter(p_s, out_s, b_tan, b_dicke)
|
||||
tm = _t_junction_miter(p_s, out_s, b_tan,
|
||||
b_dicke, b_ref)
|
||||
if tm is not None: miter_start = tm
|
||||
# Through-Meta merken fuer per-Layer Schicht-
|
||||
# durchdringung (s. unten bei layer_breps build)
|
||||
t_junction_start = (_oid, b_tan, b_dicke, p_s, out_s)
|
||||
else:
|
||||
# 3+ Joint: ich bin T-Stem wenn meine Tangente NICHT
|
||||
# collinear mit zwei collinearen Partner-Tangenten ist.
|
||||
# Eigene Tangente (gerichtet aus Body raus) am Start = -TangentAtStart
|
||||
_my_t = geom.TangentAtStart
|
||||
_my_out_tan = rg.Vector3d(-_my_t.X, -_my_t.Y, 0)
|
||||
through = _detect_through_wall_at(
|
||||
doc, joints.get(key_s, []), _my_out_tan)
|
||||
if through is not None:
|
||||
b_tan, b_dicke = through
|
||||
tm = _t_junction_miter(p_s, out_s, b_tan, b_dicke)
|
||||
b_tan, b_dicke, b_ref = through
|
||||
tm = _t_junction_miter(p_s, out_s, b_tan,
|
||||
b_dicke, b_ref)
|
||||
if tm is not None: miter_start = tm
|
||||
if out_e is not None:
|
||||
key_e = _pt_key(p_e)
|
||||
@@ -8541,20 +8684,21 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
||||
element_id, p_e,
|
||||
exclude_ids=chain_set)
|
||||
if tj is not None:
|
||||
_oid, b_tan, b_dicke = tj
|
||||
_oid, b_tan, b_dicke, b_ref = tj
|
||||
if _wand_should_apply_t_miter(doc, meta, _oid):
|
||||
tm = _t_junction_miter(p_e, out_e, b_tan, b_dicke)
|
||||
tm = _t_junction_miter(p_e, out_e, b_tan,
|
||||
b_dicke, b_ref)
|
||||
if tm is not None: miter_end = tm
|
||||
t_junction_end = (_oid, b_tan, b_dicke, p_e, out_e)
|
||||
else:
|
||||
# 3+ Joint: T-Stem-Erkennung (analog start)
|
||||
_my_t = geom.TangentAtEnd
|
||||
_my_out_tan = rg.Vector3d(_my_t.X, _my_t.Y, 0)
|
||||
through = _detect_through_wall_at(
|
||||
doc, joints.get(key_e, []), _my_out_tan)
|
||||
if through is not None:
|
||||
b_tan, b_dicke = through
|
||||
tm = _t_junction_miter(p_e, out_e, b_tan, b_dicke)
|
||||
b_tan, b_dicke, b_ref = through
|
||||
tm = _t_junction_miter(p_e, out_e, b_tan,
|
||||
b_dicke, b_ref)
|
||||
if tm is not None: miter_end = tm
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] wall joints:", ex)
|
||||
@@ -8589,14 +8733,346 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
||||
per_layer_ext_start=pl_ext_s, per_layer_ext_end=pl_ext_e,
|
||||
per_layer_miter_start=pl_miter_s,
|
||||
per_layer_miter_end=pl_miter_e)
|
||||
# Diagnostic: layer build status — User sieht oft "solid" obwohl
|
||||
# walls layered sind, kontrollieren obs an Brep-Build oder Display liegt
|
||||
# Diagnostic: layer build status
|
||||
_n_ok = sum(1 for (b, _c, _n) in layer_breps if b is not None)
|
||||
_n_fail = len(layer_breps) - _n_ok
|
||||
print("[ELEMENTE] layered build {} (chain={}): {}/{} layers built"
|
||||
" (def={} layers)".format(
|
||||
element_id, len(chain_ids) if chain_ids else 1,
|
||||
_n_ok, len(layer_breps), len(layers_def)))
|
||||
# Phase 2 Schichtdurchdringung (2D-Polylinen-Approach):
|
||||
# Pro Material 2D-Rechtecke (Through-Bands + T-Stem-Spalten)
|
||||
# bauen, in 2D unionen, fuer non-backbone Material mit
|
||||
# Backbone-Column subtrahieren (= 2D-Carve), dann extrudieren.
|
||||
# Robuster als 3D-BoolUnion mit touching breps.
|
||||
try:
|
||||
import json as _j
|
||||
for _tj_info, _is_end in ((t_junction_start, False),
|
||||
(t_junction_end, True)):
|
||||
if _tj_info is None: continue
|
||||
_toid = _tj_info[0]
|
||||
_mit_arr = pl_miter_e if _is_end else pl_miter_s
|
||||
_ext_arr = pl_ext_e if _is_end else pl_ext_s
|
||||
if _mit_arr is None: continue
|
||||
# Through axis + meta
|
||||
_th_axis = _find_axis(doc, _toid)
|
||||
if _th_axis is None: continue
|
||||
_th_meta = _read_meta(_th_axis)
|
||||
if not _th_meta or not _th_meta.get("wand_layered"): continue
|
||||
_th_geom = _th_axis.Geometry
|
||||
if not isinstance(_th_geom, rg.Curve): continue
|
||||
_th_layers = _th_meta.get("wand_layers") or []
|
||||
_th_ref = _th_meta.get("referenz", "mid")
|
||||
_th_dicke = float(_th_meta.get("dicke", 0) or 0)
|
||||
# Through-Layer-Offsets per Material
|
||||
_th_start, _ = _wall_offsets_from_referenz(
|
||||
_th_dicke, _th_ref)
|
||||
_th_off_by_mat = {}
|
||||
_cur = _th_start
|
||||
for _tl in _th_layers:
|
||||
_d = float(_tl.get("dicke", 0) or 0)
|
||||
if _d <= 0: continue
|
||||
_m = (_tl.get("material") or "").strip()
|
||||
if _m:
|
||||
_th_off_by_mat.setdefault(_m, []).append(
|
||||
(_cur, _cur - _d))
|
||||
_cur -= _d
|
||||
# T-Stem-Layer-Offsets + layer-idx per Material
|
||||
_my_layers = meta.get("wand_layers") or []
|
||||
_my_dicke = float(meta.get("dicke", 0) or 0)
|
||||
_my_ref = meta.get("referenz", "mid")
|
||||
_my_start, _ = _wall_offsets_from_referenz(
|
||||
_my_dicke, _my_ref)
|
||||
_my_info_by_mat = {}
|
||||
_cur = _my_start
|
||||
for _i, _ml in enumerate(_my_layers):
|
||||
_d = float(_ml.get("dicke", 0) or 0)
|
||||
if _d <= 0: continue
|
||||
_m = (_ml.get("material") or "").strip()
|
||||
if _m:
|
||||
_my_info_by_mat.setdefault(_m, []).append(
|
||||
(_cur, _cur - _d, _i))
|
||||
_cur -= _d
|
||||
# Backbone-Material + Ext-Wert
|
||||
_backbone_mat = None
|
||||
_backbone_ext_val = 0.0
|
||||
for _li_x in range(len(layers_def)):
|
||||
if (_li_x < len(_mit_arr)
|
||||
and _mit_arr[_li_x] is None):
|
||||
_backbone_mat = (layers_def[_li_x].get(
|
||||
"material") or "").strip()
|
||||
if _ext_arr and _li_x < len(_ext_arr):
|
||||
_backbone_ext_val = float(
|
||||
_ext_arr[_li_x] or 0)
|
||||
break
|
||||
if not _backbone_mat: continue
|
||||
# T-Stem axis (extended for backbone-side)
|
||||
if _backbone_ext_val > 0:
|
||||
if _is_end:
|
||||
_my_axis_ext = _extend_axis_curve(
|
||||
geom, 0, _backbone_ext_val)
|
||||
else:
|
||||
_my_axis_ext = _extend_axis_curve(
|
||||
geom, _backbone_ext_val, 0)
|
||||
else:
|
||||
_my_axis_ext = geom
|
||||
try:
|
||||
_ps = _my_axis_ext.PointAtStart
|
||||
_pe = _my_axis_ext.PointAtEnd
|
||||
_gs = geom.PointAtStart
|
||||
_ge = geom.PointAtEnd
|
||||
print(("[ELEMENTE] axis-ext: geom y=[{:.3f}{:.3f}]"
|
||||
" → ext y=[{:.3f}{:.3f}] (ext_val={:.3f},"
|
||||
" is_end={})").format(
|
||||
_gs.Y, _ge.Y, _ps.Y, _pe.Y,
|
||||
_backbone_ext_val, _is_end))
|
||||
except Exception: pass
|
||||
# Backbone-Near-Face in T-Stem-Axis-Richtung (=
|
||||
# wo Backbone-Layer auf Approach-Seite anfaengt).
|
||||
# Non-backbone matching columns sollen DORT stoppen
|
||||
# (Backbone hat hoehere Prio, Putz/etc weicht).
|
||||
_btan_v = _tj_info[1]
|
||||
_od_v = _tj_info[4]
|
||||
_perp_b = rg.Vector3d(-_btan_v.Y, _btan_v.X, 0)
|
||||
try: _perp_b.Unitize()
|
||||
except Exception: pass
|
||||
_dot_op = _od_v.X * _perp_b.X + _od_v.Y * _perp_b.Y
|
||||
_backbone_near_dist = None
|
||||
if _backbone_mat in _th_off_by_mat:
|
||||
_bb_through = _th_off_by_mat[_backbone_mat]
|
||||
if _bb_through:
|
||||
_bb_dl, _bb_dr = _bb_through[0]
|
||||
if _dot_op > 0:
|
||||
_bn_perp = min(_bb_dl, _bb_dr)
|
||||
else:
|
||||
_bn_perp = max(_bb_dl, _bb_dr)
|
||||
_backbone_near_dist = _bn_perp * (
|
||||
1 if _dot_op > 0 else -1)
|
||||
# Helper: T-Stem axis clipped an gegebener axial-distance
|
||||
# vom Endpoint (negativ = retract, positiv = extend)
|
||||
def _ax_clipped(_dist):
|
||||
try:
|
||||
_p1 = geom.PointAtStart
|
||||
_p2 = geom.PointAtEnd
|
||||
_tan = _p2 - _p1
|
||||
if _tan.Length < 1e-9: return geom
|
||||
_tan.Unitize()
|
||||
if _is_end:
|
||||
_new_end = _p2 + _tan * _dist
|
||||
return rg.LineCurve(_p1, _new_end)
|
||||
else:
|
||||
_new_start = _p1 - _tan * _dist
|
||||
return rg.LineCurve(_new_start, _p2)
|
||||
except Exception:
|
||||
return geom
|
||||
# Backbone-Column 2D Rect + 3D Brep (fuer Carve)
|
||||
_backbone_col_rect = None
|
||||
_backbone_col_brep_3d = None
|
||||
if _backbone_mat in _my_info_by_mat:
|
||||
_bbones = _my_info_by_mat[_backbone_mat]
|
||||
if _bbones:
|
||||
_bd_l, _bd_r, _ = _bbones[0]
|
||||
_backbone_col_rect = _layer_rect_2d(
|
||||
_my_axis_ext, _bd_l, _bd_r)
|
||||
# 3D Version fuer Carve
|
||||
if _backbone_col_rect is not None:
|
||||
try:
|
||||
_bbh = float(ok) - float(uk)
|
||||
_bbp = _backbone_col_rect.DuplicateCurve()
|
||||
if abs(uk) > 1e-9:
|
||||
_bbp.Transform(rg.Transform.Translation(
|
||||
0, 0, uk))
|
||||
_bbe = rg.Extrusion.Create(
|
||||
_bbp, _bbh, True)
|
||||
if _bbe is not None:
|
||||
_bbb = _bbe.ToBrep()
|
||||
if _bbb is not None and _bbb.IsValid:
|
||||
_backbone_col_brep_3d = _bbb
|
||||
except Exception: pass
|
||||
# Through wand_volume objects per material
|
||||
_th_objs_by_mat = {}
|
||||
for _o in doc.Objects:
|
||||
try:
|
||||
if (_o.Attributes.GetUserString(_KEY_TYPE)
|
||||
!= "wand_volume"): continue
|
||||
if (_o.Attributes.GetUserString(_KEY_ID)
|
||||
!= _toid): continue
|
||||
_idx_s = _o.Attributes.GetUserString(
|
||||
_KEY_WAND_LAYER_IDX) or ""
|
||||
_lj = _o.Attributes.GetUserString(
|
||||
_KEY_WAND_LAYERS) or ""
|
||||
if not _idx_s or not _lj: continue
|
||||
_tl = _j.loads(_lj)
|
||||
_i = int(_idx_s)
|
||||
if 0 <= _i < len(_tl):
|
||||
_m = (_tl[_i].get("material") or "").strip()
|
||||
if _m:
|
||||
_th_objs_by_mat.setdefault(
|
||||
_m, []).append(_o)
|
||||
except Exception: pass
|
||||
# 2D-Polylinen Union per Material:
|
||||
# ALLE matching T-Stem-Columns nutzen die GLEICHE
|
||||
# _my_axis_ext (= backbone-extended). Damit reichen
|
||||
# ALLE Schichten gleich hoch ins Through (nicht nur
|
||||
# Backbone) — Daemm + Putz visuell durchgehend.
|
||||
# Carve verhindert Material-Konflikte: non-backbone
|
||||
# Polygone werden gegen Backbone-Column geschnitten.
|
||||
_height = float(ok) - float(uk)
|
||||
for _mat in list(_th_off_by_mat.keys()):
|
||||
if _mat not in _my_info_by_mat: continue
|
||||
_is_backbone = (_mat == _backbone_mat)
|
||||
# Cross-junction safe: nutze CURRENT through-Brep(s)
|
||||
_layer_breps_3d = []
|
||||
for _o in _th_objs_by_mat.get(_mat, []):
|
||||
try:
|
||||
_br_cur = _o.Geometry
|
||||
if isinstance(_br_cur, rg.Brep) and _br_cur.IsValid:
|
||||
_layer_breps_3d.append(
|
||||
_br_cur.DuplicateBrep())
|
||||
except Exception: pass
|
||||
# Sammle T-Stem-Column Rects + Column-X-Range
|
||||
# (= fuer west-stub-filter spaeter)
|
||||
_column_breps = []
|
||||
_column_x_ranges = []
|
||||
for (_dl, _dr, _layer_i) in _my_info_by_mat[_mat]:
|
||||
_r = _layer_rect_2d(_my_axis_ext, _dl, _dr)
|
||||
if _r is None: continue
|
||||
try:
|
||||
_profile = _r.DuplicateCurve()
|
||||
if abs(uk) > 1e-9:
|
||||
_profile.Transform(
|
||||
rg.Transform.Translation(0, 0, uk))
|
||||
_extr = rg.Extrusion.Create(
|
||||
_profile, _height, True)
|
||||
if _extr is None: continue
|
||||
_br = _extr.ToBrep()
|
||||
if _br is None or not _br.IsValid: continue
|
||||
_column_breps.append(_br)
|
||||
# Column x range fuer filter
|
||||
_cbb = _br.GetBoundingBox(True)
|
||||
_column_x_ranges.append(
|
||||
(_cbb.Min.X, _cbb.Max.X))
|
||||
_layer_breps_3d.append(_br)
|
||||
except Exception as _ex:
|
||||
print("[ELEMENTE] Column-extr exc ({}):"
|
||||
.format(_mat), _ex)
|
||||
if not _layer_breps_3d: continue
|
||||
# 3D Union all
|
||||
if len(_layer_breps_3d) == 1:
|
||||
_result_breps = _layer_breps_3d
|
||||
else:
|
||||
try:
|
||||
_u3d = rg.Brep.CreateBooleanUnion(
|
||||
_layer_breps_3d, 0.01)
|
||||
if _u3d and len(_u3d) > 0:
|
||||
_result_breps = list(_u3d)
|
||||
else:
|
||||
_result_breps = _layer_breps_3d
|
||||
except Exception as _ex:
|
||||
print("[ELEMENTE] {} Union exc:"
|
||||
.format(_mat), _ex)
|
||||
_result_breps = _layer_breps_3d
|
||||
# Carve mit backbone-col + hoehere-prio through-bands
|
||||
if not _is_backbone:
|
||||
_carve_breps = []
|
||||
if _backbone_col_brep_3d is not None:
|
||||
_carve_breps.append(_backbone_col_brep_3d)
|
||||
_my_prio = _material_prio(_mat)
|
||||
for _other_mat, _bands in _th_off_by_mat.items():
|
||||
if _other_mat == _mat: continue
|
||||
_other_prio = _material_prio(_other_mat)
|
||||
if _other_prio <= _my_prio: continue
|
||||
for (_obdl, _obdr) in _bands:
|
||||
_ob_rect = _layer_rect_2d(
|
||||
_th_geom, _obdl, _obdr)
|
||||
if _ob_rect is None: continue
|
||||
try:
|
||||
_ob_profile = _ob_rect.DuplicateCurve()
|
||||
if abs(uk) > 1e-9:
|
||||
_ob_profile.Transform(
|
||||
rg.Transform.Translation(
|
||||
0, 0, uk))
|
||||
_ob_extr = rg.Extrusion.Create(
|
||||
_ob_profile, _height, True)
|
||||
if _ob_extr is None: continue
|
||||
_ob_br = _ob_extr.ToBrep()
|
||||
if (_ob_br is not None
|
||||
and _ob_br.IsValid):
|
||||
_carve_breps.append(_ob_br)
|
||||
except Exception: pass
|
||||
if _carve_breps:
|
||||
_current = list(_result_breps)
|
||||
for _cb in _carve_breps:
|
||||
_next = []
|
||||
for _br in _current:
|
||||
try:
|
||||
_diff = rg.Brep.CreateBooleanDifference(
|
||||
_br, _cb, 0.001)
|
||||
except Exception:
|
||||
_next.append(_br); continue
|
||||
if (_diff is not None
|
||||
and len(_diff) > 0):
|
||||
for _db in _diff:
|
||||
if (_db is not None
|
||||
and _db.IsValid):
|
||||
_next.append(_db)
|
||||
else:
|
||||
_next.append(_br)
|
||||
_current = _next
|
||||
_result_breps = _current
|
||||
# (West-stub filter entfernt — war zu aggressiv,
|
||||
# discarded legitime through-wall west pieces)
|
||||
# 4. Validity + MergeCoplanarFaces
|
||||
_result_breps = [_br for _br in _result_breps
|
||||
if _br is not None and _br.IsValid]
|
||||
for _br in _result_breps:
|
||||
try: _br.MergeCoplanarFaces(0.01)
|
||||
except Exception: pass
|
||||
# Re-Extrude entfernt — war Bug-Quelle wo carve-
|
||||
# holes verloren gingen. Direkter carved Brep ist
|
||||
# OK (= 2 disconnected pieces nach BoolDifference).
|
||||
if not _result_breps: continue
|
||||
# Diagnostic: BBox y-range per result brep
|
||||
for _bri, _br_diag in enumerate(_result_breps):
|
||||
try:
|
||||
_bb_d = _br_diag.GetBoundingBox(True)
|
||||
print("[ELEMENTE] {} result[{}] y=[{:.3f}"
|
||||
",{:.3f}]".format(
|
||||
_mat, _bri, _bb_d.Min.Y, _bb_d.Max.Y))
|
||||
except Exception: pass
|
||||
# Replace through objects mit Extrude-Resultaten
|
||||
_existing = _th_objs_by_mat.get(_mat, [])
|
||||
for _ri in range(len(_result_breps)):
|
||||
if _ri < len(_existing):
|
||||
try:
|
||||
doc.Objects.Replace(
|
||||
_existing[_ri].Id, _result_breps[_ri])
|
||||
except Exception as _ex:
|
||||
print("[ELEMENTE] 2D Replace:", _ex)
|
||||
else:
|
||||
if _existing:
|
||||
try:
|
||||
_attrs = _existing[0].Attributes.Duplicate()
|
||||
doc.Objects.AddBrep(
|
||||
_result_breps[_ri], _attrs)
|
||||
except Exception as _ex:
|
||||
print("[ELEMENTE] 2D Add:", _ex)
|
||||
for _ei in range(len(_result_breps), len(_existing)):
|
||||
try: doc.Objects.Delete(
|
||||
_existing[_ei].Id, True)
|
||||
except Exception: pass
|
||||
# T-Stem-Layer fuer dieses Material consumen
|
||||
for (_dl, _dr, _layer_i) in _my_info_by_mat[_mat]:
|
||||
if _layer_i < len(layer_breps):
|
||||
_lb_old = layer_breps[_layer_i]
|
||||
layer_breps[_layer_i] = (
|
||||
None, _lb_old[1], _lb_old[2])
|
||||
print("[ELEMENTE] 2D-Phase2: {} ({}) → {} brep(s)"
|
||||
" aus {} cols".format(
|
||||
_mat,
|
||||
"backbone" if _is_backbone else "L-merge",
|
||||
len(_result_breps), len(_column_breps)))
|
||||
except Exception as _ex:
|
||||
print("[ELEMENTE] T-Junction Phase2 2D:", _ex)
|
||||
else:
|
||||
single_brep = _make_volume_geometry(
|
||||
geom, meta["dicke"], uk, ok,
|
||||
@@ -17843,6 +18319,13 @@ def _install_listeners(bridge):
|
||||
install_cluster_select_handler()
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] cluster-select install:", ex)
|
||||
# Generic Vertex-Dots fuer Curves (Polylinen, Rectangles etc) —
|
||||
# display-only, hilft beim visuellen Finden von Grip-Positionen.
|
||||
try:
|
||||
import curve_vertex_dots
|
||||
curve_vertex_dots.install_curve_vertex_dots()
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] curve_vertex_dots install:", ex)
|
||||
# Pre-Warm: native OpenNURBS-Libraries beim Plugin-Start laden um den
|
||||
# First-Call-Lag zu vermeiden (User-Meldung: beim ersten Wand-Verbinden
|
||||
# haengt das UI kurz, danach nicht mehr → native code wird lazy-loaded).
|
||||
|
||||
@@ -349,6 +349,22 @@ _PROJECT_SETTINGS_DEFAULTS = {
|
||||
{"material": "Daemmung", "dicke": 0.10},
|
||||
{"material": "Putz", "dicke": 0.02},
|
||||
]},
|
||||
{"id": "style_innen_holz", "name": "Innenwand Holzstaender 14 cm",
|
||||
"prio": 250, "dicke": 0.14, "referenz": "mid",
|
||||
"layered": True, "material": "",
|
||||
"layers": [
|
||||
{"material": "Putz", "dicke": 0.015},
|
||||
{"material": "Holzstaender", "dicke": 0.110},
|
||||
{"material": "Putz", "dicke": 0.015},
|
||||
]},
|
||||
{"id": "style_innen_beton", "name": "Innenwand Beton 20 cm",
|
||||
"prio": 700, "dicke": 0.23, "referenz": "mid",
|
||||
"layered": True, "material": "",
|
||||
"layers": [
|
||||
{"material": "Putz", "dicke": 0.015},
|
||||
{"material": "Stahlbeton", "dicke": 0.200},
|
||||
{"material": "Putz", "dicke": 0.015},
|
||||
]},
|
||||
],
|
||||
"project": {
|
||||
"name": "",
|
||||
|
||||
@@ -151,6 +151,45 @@ def _assign_default_display_modes(doc):
|
||||
print("[STARTUP] view-modes: {} Viewport(s) gesetzt".format(n_set))
|
||||
|
||||
|
||||
_DOC_FLAG_VIEW_MAXIMIZED = "dossier_top_view_maximized"
|
||||
|
||||
|
||||
def _maximize_top_view(doc):
|
||||
"""Maximiert den Top-Viewport (= einzige aktive View statt 4-Viewport-
|
||||
Default). Persistiert Flag in doc.Strings → laeuft nur EINMAL pro Doc.
|
||||
User-Overrides (manuelles Wechseln zu 4-View etc) bleiben erhalten."""
|
||||
if doc is None: return
|
||||
try:
|
||||
if doc.Strings.GetValue(_DOC_FLAG_VIEW_MAXIMIZED) == "1":
|
||||
return # schon initialisiert
|
||||
except Exception: pass
|
||||
try:
|
||||
top_view = None
|
||||
for view in doc.Views:
|
||||
try:
|
||||
vp = view.ActiveViewport
|
||||
if vp is None: continue
|
||||
if vp.Name == "Top":
|
||||
top_view = view
|
||||
break
|
||||
except Exception: pass
|
||||
if top_view is None:
|
||||
print("[STARTUP] view-max: kein Top-Viewport gefunden")
|
||||
return
|
||||
try:
|
||||
top_view.Maximized = True
|
||||
doc.Views.ActiveView = top_view
|
||||
doc.Views.Redraw()
|
||||
print("[STARTUP] view-max: Top-Viewport maximiert")
|
||||
except Exception as ex:
|
||||
print("[STARTUP] view-max set:", ex); return
|
||||
try:
|
||||
doc.Strings.SetString(_DOC_FLAG_VIEW_MAXIMIZED, "1")
|
||||
except Exception: pass
|
||||
except Exception as ex:
|
||||
print("[STARTUP] view-max:", ex)
|
||||
|
||||
|
||||
_DOC_FLAG_UNIT_CHECKED = "dossier_unit_checked"
|
||||
|
||||
|
||||
@@ -227,6 +266,7 @@ def _on_doc_opened(sender, e):
|
||||
import panel_base
|
||||
panel_base.migrate_to_dossier(doc)
|
||||
_assign_default_display_modes(doc)
|
||||
_maximize_top_view(doc)
|
||||
_check_doc_unit(doc)
|
||||
except Exception as ex:
|
||||
print("[STARTUP] _on_doc_opened:", ex)
|
||||
@@ -301,6 +341,11 @@ def _load_all(sender, e):
|
||||
_pb._t_mark("post_init", "view_modes", _t_vm)
|
||||
except Exception as ex:
|
||||
print("[STARTUP] view-modes assign:", ex)
|
||||
# Top-View maximieren (= einzige aktive View statt 4-View Default)
|
||||
try:
|
||||
_maximize_top_view(Rhino.RhinoDoc.ActiveDoc)
|
||||
except Exception as ex:
|
||||
print("[STARTUP] view-max:", ex)
|
||||
# Unit-Check fuer das beim Start aktive Doc — fragt einmal pro Doc
|
||||
# wenn doc.ModelUnitSystem != Project-Setting
|
||||
_t_uc = _t.time()
|
||||
|
||||
Reference in New Issue
Block a user