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:
@@ -433,6 +433,40 @@ class EbenenBridge(panel_base.BaseBridge):
|
||||
self._update_ebene_field(p["code"], "lw", p["lw"])
|
||||
elif t == "SET_ACTIVE":
|
||||
self._set_active_zeichnungsebene(p)
|
||||
elif t == "CREATE_SCHNITT":
|
||||
# Interaktiver Pick: 2 Punkte fuer Schnittlinie + Klick fuer
|
||||
# Blickrichtung. Defaults aus payload (vom UI vorbelegt).
|
||||
try:
|
||||
import schnitte
|
||||
sid = schnitte.pick_schnitt_interactive(doc, defaults={
|
||||
"depthBack": float(p.get("depthBack", 8.0)),
|
||||
"heightMin": float(p.get("heightMin", -1.0)),
|
||||
"heightMax": float(p.get("heightMax", 12.0)),
|
||||
"cutAtLine": bool(p.get("cutAtLine", True)),
|
||||
"namePrefix": p.get("namePrefix", "S"),
|
||||
})
|
||||
if sid:
|
||||
_broadcast_state(doc)
|
||||
# Auto-aktivieren nach Erstellung
|
||||
try:
|
||||
zraw = doc.Strings.GetValue("dossier_zeichnungsebenen")
|
||||
z_list = json.loads(zraw) if zraw else []
|
||||
new_z = next((x for x in z_list
|
||||
if isinstance(x, dict) and x.get("id") == sid),
|
||||
None)
|
||||
if new_z is not None:
|
||||
self._set_active_zeichnungsebene(new_z)
|
||||
except Exception as ex:
|
||||
print("[SCHNITT] auto-activate:", ex)
|
||||
except Exception as ex:
|
||||
print("[SCHNITT] CREATE_SCHNITT:", ex)
|
||||
elif t == "DELETE_SCHNITT":
|
||||
try:
|
||||
import schnitte
|
||||
if schnitte.delete_schnitt_entry(doc, p.get("id") or ""):
|
||||
_broadcast_state(doc)
|
||||
except Exception as ex:
|
||||
print("[SCHNITT] DELETE_SCHNITT:", ex)
|
||||
elif t == "SET_ACTIVE_LAYER":
|
||||
code = p.get("code", "")
|
||||
if code:
|
||||
@@ -551,6 +585,18 @@ class EbenenBridge(panel_base.BaseBridge):
|
||||
try: e_list = json.loads(e_raw) if e_raw else []
|
||||
except Exception: e_list = []
|
||||
self._apply(z_list, e_list, save_z=True, save_e=False)
|
||||
# Schnitt-Refresh: wenn der geaenderte Eintrag ein Schnitt ist
|
||||
# UND aktuell aktiv ist, Clipping-Planes + View neu aufbauen
|
||||
# damit die neuen Werte (depthBack, heightRange, cutAtLine etc.)
|
||||
# sofort wirken.
|
||||
try:
|
||||
if updated.get("type") == "schnitt":
|
||||
active_id = doc.Strings.GetValue("dossier_active_id") or ""
|
||||
if active_id == updated.get("id"):
|
||||
import schnitte
|
||||
schnitte.activate_schnitt(doc, updated)
|
||||
except Exception as ex:
|
||||
print("[SCHNITT] post-save reactivate:", ex)
|
||||
panel_base.open_satellite_window(
|
||||
"geschoss_settings",
|
||||
params=params,
|
||||
@@ -709,6 +755,22 @@ class EbenenBridge(panel_base.BaseBridge):
|
||||
new_sig = _fill_signature(ebenen)
|
||||
fill_changed = (old_sig != new_sig)
|
||||
|
||||
# Schnitt-Cleanup-Detection: alt vs neu Schnitt-Ids vergleichen.
|
||||
# Wenn ein Schnitt entfernt wurde (via normalem Delete-Menue), die
|
||||
# 2D-Plan-Symbole + ggf. Clipping-Planes aufraeumen. Sonst bleiben
|
||||
# Waisen im Doc.
|
||||
schnitte_removed = set()
|
||||
if save_z:
|
||||
try:
|
||||
import schnitte as _schn
|
||||
old_z_raw = doc.Strings.GetValue("dossier_zeichnungsebenen")
|
||||
old_z = json.loads(old_z_raw) if old_z_raw else []
|
||||
old_ids = _schn.schnitt_ids_in_list(old_z)
|
||||
new_ids = _schn.schnitt_ids_in_list(zeichnungsebenen)
|
||||
schnitte_removed = old_ids - new_ids
|
||||
except Exception as ex:
|
||||
print("[SCHNITT] cleanup detection:", ex)
|
||||
|
||||
_set_processing(True)
|
||||
try:
|
||||
print("[EBENEN] _apply: build_layers ...")
|
||||
@@ -724,6 +786,21 @@ class EbenenBridge(panel_base.BaseBridge):
|
||||
doc.Strings.SetString("dossier_zeichnungsebenen", z_json)
|
||||
if save_e:
|
||||
doc.Strings.SetString("dossier_ebenen", e_json)
|
||||
# Cleanup geloeschter Schnitte: 2D-Symbole + ggf. Clipping-Planes.
|
||||
# Muss NACH dem SetString passieren damit dossier_active_id-Check
|
||||
# in cleanup_schnitt_artifacts den korrekten Stand sieht.
|
||||
if schnitte_removed:
|
||||
try:
|
||||
import schnitte as _schn
|
||||
active_id = doc.Strings.GetValue("dossier_active_id") or ""
|
||||
n_total = 0
|
||||
for sid in schnitte_removed:
|
||||
n_total += _schn.cleanup_schnitt_artifacts(
|
||||
doc, sid, active_id=active_id)
|
||||
print("[SCHNITT] {} Schnitt(e) geloescht, {} Symbol-Curves entfernt".format(
|
||||
len(schnitte_removed), n_total))
|
||||
except Exception as ex:
|
||||
print("[SCHNITT] artifact cleanup:", ex)
|
||||
# Smart-Elemente (Waende) regenerieren — Geschoss-Hoehen/OKFF
|
||||
# haben sich evtl. geaendert, gebundene Waende muessen neu
|
||||
# extrudiert werden. Best-effort, faengt jeden Fehler ab.
|
||||
@@ -943,6 +1020,61 @@ class EbenenBridge(panel_base.BaseBridge):
|
||||
# Vorigen Stand merken um redundante teure Operationen zu sparen
|
||||
prev_active_id = doc.Strings.GetValue("dossier_active_id") or ""
|
||||
doc.Strings.SetString("dossier_active_id", z_id)
|
||||
# Schnitt-Typ: Spezial-Pfad. Vertikale Clipping-Planes + Parallel-
|
||||
# View statt der ueblichen horizontalen Geschoss-Clipping-Logik.
|
||||
# Den vollen Record aus doc.Strings holen (z-Payload aus React ist
|
||||
# minimal, hat type/linePts/etc nicht zwingend dabei).
|
||||
z_full = z
|
||||
try:
|
||||
zraw = doc.Strings.GetValue("dossier_zeichnungsebenen")
|
||||
if zraw:
|
||||
for cand in json.loads(zraw):
|
||||
if isinstance(cand, dict) and cand.get("id") == z_id:
|
||||
z_full = cand; break
|
||||
except Exception: pass
|
||||
# Vorheriger Eintrag ein Schnitt? Brauchen wir fuer View-Snapshot-
|
||||
# Logik: Geschoss → Schnitt snapshot, Schnitt → Geschoss restore.
|
||||
prev_was_schnitt = False
|
||||
try:
|
||||
import schnitte as _schn_check
|
||||
prev_was_schnitt = _schn_check.is_schnitt_id(doc, prev_active_id)
|
||||
except Exception: pass
|
||||
|
||||
if isinstance(z_full, dict) and z_full.get("type") == "schnitt":
|
||||
try:
|
||||
import schnitte
|
||||
# Pre-Schnitt-View snapshotten — aber NUR beim Wechsel von
|
||||
# einem Nicht-Schnitt. Schnitt→Schnitt-Wechsel soll den
|
||||
# urspruenglichen Plan-View nicht ueberschreiben.
|
||||
if not prev_was_schnitt:
|
||||
schnitte.save_pre_schnitt_view(doc)
|
||||
# Horizontale Geschoss-Clipping aufraeumen falls aktiv —
|
||||
# die existiert parallel zur Schnitt-Clipping und wuerde
|
||||
# die Sicht doppelt schneiden.
|
||||
try:
|
||||
existing_geschoss = layer_builder._find_clipping_plane(doc)
|
||||
if existing_geschoss is not None:
|
||||
doc.Objects.Delete(existing_geschoss.Id, True)
|
||||
except Exception: pass
|
||||
schnitte.activate_schnitt(doc, z_full)
|
||||
_broadcast_state(doc)
|
||||
# Elemente-Panel auch informieren
|
||||
try:
|
||||
eb = sc.sticky.get("elemente_bridge")
|
||||
if eb is not None: eb._notify_active_geschoss()
|
||||
except Exception: pass
|
||||
except Exception as ex:
|
||||
print("[SCHNITT] activate fehler:", ex)
|
||||
return
|
||||
# Geschoss-Pfad (default): falls vorher ein Schnitt aktiv war,
|
||||
# dessen Clipping-Planes aufraeumen + Pre-Schnitt-View restoren.
|
||||
try:
|
||||
import schnitte
|
||||
schnitte.clear_schnitt_clipping(doc)
|
||||
if prev_was_schnitt:
|
||||
schnitte.restore_pre_schnitt_view(doc)
|
||||
except Exception as ex:
|
||||
print("[SCHNITT] cleanup beim Wechsel auf Geschoss:", ex)
|
||||
# Aktiven Sublayer auf die GLEICHE Ebene unter dem neuen Geschoss
|
||||
# umschalten — wenn User auf "20 Wände" steht und das Geschoss
|
||||
# wechselt, soll Rhino's aktiver Layer "1OG::20_Wände" werden statt
|
||||
|
||||
Reference in New Issue
Block a user