Schnitt/Ansicht-Feature + Terrain-Volumen + Geschoss-Add-Dialog
Schnitt-Feature V1+V2: - Neues rhino/schnitte.py mit Pick-Workflow, Activation (Clipping-Planes + Parallel-View), 2D-Plan-Symbol auf 18_Schnittlinien-Sublayer - Doppelklick auf Symbol aktiviert den Schnitt - Schnitt-Settings (cutAtLine/Tiefe/Höhen/Blickrichtung) im GeschossSettingsDialog - View-Snapshot + Restore beim Wechsel Schnitt → Geschoss - Symbol-Cleanup bei Delete via normalem Ebenen-Menü Terrain als Volumen: - swisstopo.volumize_terrain_object: Skirt + Bottom-Cap auf Mesh/Brep damit Clipping-Planes gefuellte Querschnitte erzeugen - UI im SwisstopoApp mit Nachbearbeitung-Section + Tiefen-Eingabe Geschoss-Add mit Dialog: - + im GeschossManager oeffnet 3-Optionen-Picker (Geschoss/Schnitt/Zeichnung) - Geschoss-Dialog mit Anker-Dropdown, Position über/unter, Auto-Name, Höhen-Prefill aus Anker Fix: _send_state fallback — Element gilt als selektiert wenn Source ODER Volume in der Selection ist (robust gegen Layer-Visibility wenn Referenz- linien-Layer im aktuellen Mode versteckt ist) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -909,6 +909,133 @@ def generate_patch_from_contours(doc, contour_curves, progress=None):
|
||||
return None
|
||||
|
||||
|
||||
def volumize_terrain_object(doc, top_obj, depth_doc, progress=None):
|
||||
"""Wandelt ein offenes Terrain (Mesh ODER Brep) in ein geschlossenes
|
||||
Mesh-Volumen um: Skirt um den Boundary + planarer Boden bei
|
||||
(min_z - depth_doc). Resultat hat eine Section beim Schneiden mit
|
||||
einer Clipping-Plane.
|
||||
|
||||
Strategie:
|
||||
1. Mesh-Source ermitteln (Brep → Mesh.CreateFromBrep, Mesh → direkt)
|
||||
2. GetNakedEdges() liefert die Boundary-Loop(s) als Polylines
|
||||
3. Pro Loop: Skirt-Quads zwischen Top-Edge und Bottom-Vertices
|
||||
4. Pro Loop: Bottom-Cap via Mesh.CreateFromClosedPolyline (Rhino
|
||||
triangliert auch nicht-konvexe Boundaries sauber)
|
||||
5. CombineIdentical schweisst Top + Skirt-Top zusammen
|
||||
|
||||
Ersetzt das Original im Doc (Delete+Add mit gleichen Attributes).
|
||||
Liefert das neue RhinoObject oder None bei Fehler."""
|
||||
import System
|
||||
if top_obj is None or top_obj.IsDeleted: return None
|
||||
geom = top_obj.Geometry
|
||||
if geom is None: return None
|
||||
# 1) Top-Mesh ermitteln (Brep meshen wenn noetig)
|
||||
top_mesh = None
|
||||
if isinstance(geom, rg.Mesh):
|
||||
top_mesh = geom.Duplicate()
|
||||
elif isinstance(geom, rg.Brep):
|
||||
try:
|
||||
mp = rg.MeshingParameters.Default
|
||||
meshes = rg.Mesh.CreateFromBrep(geom, mp)
|
||||
if meshes and len(meshes) > 0:
|
||||
joined = rg.Mesh()
|
||||
for m in meshes: joined.Append(m)
|
||||
top_mesh = joined
|
||||
except Exception as ex:
|
||||
if progress: progress("Volumize: Brep-Meshing-Fehler: {}".format(ex))
|
||||
return None
|
||||
elif isinstance(geom, rg.Extrusion):
|
||||
try:
|
||||
brep = geom.ToBrep(False)
|
||||
mp = rg.MeshingParameters.Default
|
||||
meshes = rg.Mesh.CreateFromBrep(brep, mp)
|
||||
if meshes and len(meshes) > 0:
|
||||
joined = rg.Mesh()
|
||||
for m in meshes: joined.Append(m)
|
||||
top_mesh = joined
|
||||
except Exception: pass
|
||||
if top_mesh is None or top_mesh.Vertices.Count < 3:
|
||||
if progress: progress("Volumize: kein Mesh-Top")
|
||||
return None
|
||||
# 2) Boundary-Loops
|
||||
naked = top_mesh.GetNakedEdges()
|
||||
if naked is None or len(naked) == 0:
|
||||
if progress: progress("Volumize: keine Boundary — Terrain schon geschlossen")
|
||||
return None
|
||||
# 3) Bottom-Z = min_z des Top - depth
|
||||
bb = top_mesh.GetBoundingBox(True)
|
||||
if not bb.IsValid:
|
||||
if progress: progress("Volumize: ungueltige BoundingBox")
|
||||
return None
|
||||
bottom_z = bb.Min.Z - float(depth_doc)
|
||||
if progress:
|
||||
progress("Volumize: {} Boundary-Loop(s), Boden bei Z={:.3f}".format(
|
||||
len(naked), bottom_z))
|
||||
# 4) Volumen-Mesh aufbauen: top + Skirt + Bottom-Cap
|
||||
vol = top_mesh.Duplicate()
|
||||
for loop in naked:
|
||||
try:
|
||||
if loop is None or loop.Count < 3: continue
|
||||
# Polyline-Punkte (offene Form — closing point ggf. entfernen)
|
||||
pts = [rg.Point3d(p) for p in loop]
|
||||
if len(pts) > 1 and pts[0].DistanceTo(pts[-1]) < 1e-6:
|
||||
pts = pts[:-1]
|
||||
n = len(pts)
|
||||
if n < 3: continue
|
||||
# Top + Bottom Vertices anfuegen
|
||||
top_idx = []
|
||||
bot_idx = []
|
||||
for p in pts:
|
||||
top_idx.append(vol.Vertices.Add(p.X, p.Y, p.Z))
|
||||
bot_idx.append(vol.Vertices.Add(p.X, p.Y, bottom_z))
|
||||
# Skirt: Quads zwischen aufeinanderfolgenden Top/Bottom-Paaren.
|
||||
# Faces sind "innen orientiert" — bei Bedarf normals
|
||||
# umdrehen via ComputeNormals + RebuildNormals.
|
||||
for i in range(n):
|
||||
j = (i + 1) % n
|
||||
vol.Faces.AddFace(top_idx[i], top_idx[j],
|
||||
bot_idx[j], bot_idx[i])
|
||||
# Bottom-Cap via planar Polyline → Mesh
|
||||
bot_pts = [rg.Point3d(p.X, p.Y, bottom_z) for p in pts]
|
||||
bot_pts.append(bot_pts[0]) # schliessen
|
||||
bot_poly = rg.Polyline(bot_pts)
|
||||
cap = rg.Mesh.CreateFromClosedPolyline(bot_poly)
|
||||
if cap is not None and cap.Vertices.Count >= 3:
|
||||
vol.Append(cap)
|
||||
except Exception as ex:
|
||||
if progress: progress("Volumize: Loop-Fehler: {}".format(ex))
|
||||
# 5) Cleanup
|
||||
try: vol.Vertices.CombineIdentical(True, True)
|
||||
except Exception: pass
|
||||
try: vol.Compact()
|
||||
except Exception: pass
|
||||
try:
|
||||
vol.Normals.ComputeNormals()
|
||||
vol.FaceNormals.ComputeFaceNormals()
|
||||
# Topologie pruefen + Naked-Edges-Anzahl loggen
|
||||
post_naked = vol.GetNakedEdges()
|
||||
if progress:
|
||||
n_naked = len(post_naked) if post_naked else 0
|
||||
progress("Volumize: Resultat {} naked-edge-loops (0 = closed)".format(
|
||||
n_naked))
|
||||
except Exception: pass
|
||||
# 6) Original ersetzen — Attributes + LayerIndex behalten
|
||||
try:
|
||||
attrs = top_obj.Attributes.Duplicate()
|
||||
old_id = top_obj.Id
|
||||
new_gid = doc.Objects.AddMesh(vol, attrs)
|
||||
if new_gid is None or new_gid == System.Guid.Empty:
|
||||
if progress: progress("Volumize: AddMesh fehlgeschlagen")
|
||||
return None
|
||||
doc.Objects.Delete(old_id, True)
|
||||
new_obj = doc.Objects.Find(new_gid)
|
||||
if progress: progress("→ Terrain-Volumen erzeugt")
|
||||
return new_obj
|
||||
except Exception as ex:
|
||||
if progress: progress("Volumize: Replace-Fehler: {}".format(ex))
|
||||
return None
|
||||
|
||||
|
||||
def generate_contour_curves(grid, shift_lv95, m_to_unit, interval=2.0,
|
||||
progress=None):
|
||||
"""Generiert Hoehenlinien (Contour-Curves) aus dem Terrain-Grid via
|
||||
|
||||
Reference in New Issue
Block a user