Library: Format-Konvertierung beim Import (.dwg/.obj/.fbx/.dae/.stl/...)
User kann jetzt im Symbole-Tab Dateien in vielen Formaten waehlen — Rhino konvertiert automatisch nach .3dm via _-Import + File3dm.Write. library.py — convert_to_3dm_via_import(): - Snapshot der existierenden Object-IDs im aktiven Doc - User-Selection sichern - _-Import scripted ausfuehren → neue Objekte in Doc - Diff -> neue Objekte sammeln - BoundingBox.Min als Origin → in File3dm packen - f3.Write nach library/assets/<name>.3dm - Neue Objekte aus Doc wieder loeschen + User-Selection restoren - sticky 'dossier_library_import_busy' + 'dossier_swisstopo_busy' damit unsere Listener nichts cascaden waehrend Doc kurzzeitig die Importe haelt rhinopanel.py — _add_library_file(): - .3dm: copy_to_assets (wie bisher) - .dwg/.dxf/.obj/.fbx/.dae/.stl/.3ds/.skp/.iges/.step/.ply: konvertieren - Sonst LIBRARY_ERROR Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -210,6 +210,137 @@ def add_item(item):
|
|||||||
return ok, load_manifest()
|
return ok, load_manifest()
|
||||||
|
|
||||||
|
|
||||||
|
def convert_to_3dm_via_import(src_path, target_name):
|
||||||
|
"""Konvertiert eine beliebige CAD-Datei (.dwg/.obj/.fbx/.dae/.stl/...)
|
||||||
|
nach .3dm. Strategie: Rhinos _-Import in den aktiven Doc, dann die
|
||||||
|
NEU hinzugekommenen Objekte als File3dm in library/assets/<name>.3dm
|
||||||
|
speichern + aus dem Doc wieder loeschen. Returns relativer Pfad oder
|
||||||
|
None.
|
||||||
|
|
||||||
|
WARNUNG: User-Doc wird kurz veraendert (Objects in/out) — wir
|
||||||
|
delete'n alles wieder am Ende. Setzt einen sticky-Flag damit unsere
|
||||||
|
eigenen Listener nichts cascaden."""
|
||||||
|
if not src_path or not os.path.isfile(src_path): return None
|
||||||
|
if Rhino is None: return None
|
||||||
|
import scriptcontext as sc
|
||||||
|
doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
|
if doc is None: return None
|
||||||
|
ensure_library()
|
||||||
|
assets_dir = os.path.join(library_root(), "assets")
|
||||||
|
if not os.path.isdir(assets_dir):
|
||||||
|
try: os.makedirs(assets_dir)
|
||||||
|
except Exception: pass
|
||||||
|
safe = "".join(c if (c.isalnum() or c in "-_") else "_" for c in target_name)
|
||||||
|
if not safe.endswith(".3dm"): safe += ".3dm"
|
||||||
|
target = os.path.join(assets_dir, safe)
|
||||||
|
if os.path.isfile(target):
|
||||||
|
stem, ext = os.path.splitext(safe)
|
||||||
|
n = 2
|
||||||
|
while os.path.isfile(os.path.join(assets_dir, "{}_{}{}".format(stem, n, ext))):
|
||||||
|
n += 1
|
||||||
|
target = os.path.join(assets_dir, "{}_{}{}".format(stem, n, ext))
|
||||||
|
# Listener stilllegen: unsere Add-/Delete-Cascade soll bei diesem
|
||||||
|
# temporaeren Import nicht greifen (Objekte haben keine DOSSIER-
|
||||||
|
# UserStrings, kommen aber trotzdem durch unsere Schnellfilter).
|
||||||
|
sc.sticky["dossier_library_import_busy"] = True
|
||||||
|
sc.sticky["dossier_swisstopo_busy"] = True # blockt schon viele Listener
|
||||||
|
# Snapshot der existierenden Object-IDs
|
||||||
|
before_ids = set()
|
||||||
|
try:
|
||||||
|
for o in doc.Objects:
|
||||||
|
try:
|
||||||
|
if not o.IsDeleted: before_ids.add(str(o.Id))
|
||||||
|
except Exception: pass
|
||||||
|
except Exception: pass
|
||||||
|
# User-Selection sichern damit wir sie am Ende restoren
|
||||||
|
sel_before_ids = []
|
||||||
|
try:
|
||||||
|
for o in doc.Objects.GetSelectedObjects(False, False):
|
||||||
|
sel_before_ids.append(o.Id)
|
||||||
|
except Exception: pass
|
||||||
|
new_objs = []
|
||||||
|
try:
|
||||||
|
try: doc.Objects.UnselectAll()
|
||||||
|
except Exception: pass
|
||||||
|
# _-Import dash-prefix = scripted, kein UI-Dialog. Pfad in Quotes
|
||||||
|
# damit Spaces nicht splitten. _Enter beendet die Optionen.
|
||||||
|
cmd = '_-Import "' + src_path + '" _Enter _Enter'
|
||||||
|
try:
|
||||||
|
Rhino.RhinoApp.RunScript(cmd, False)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[LIBRARY] convert_to_3dm RunScript:", ex)
|
||||||
|
# Sammle die NEU hinzugekommenen Objekte
|
||||||
|
try:
|
||||||
|
for o in doc.Objects:
|
||||||
|
try:
|
||||||
|
if o.IsDeleted: continue
|
||||||
|
if str(o.Id) not in before_ids:
|
||||||
|
new_objs.append(o)
|
||||||
|
except Exception: pass
|
||||||
|
except Exception: pass
|
||||||
|
if not new_objs:
|
||||||
|
print("[LIBRARY] convert_to_3dm: Import lieferte keine Objekte")
|
||||||
|
return None
|
||||||
|
# In File3dm packen
|
||||||
|
try:
|
||||||
|
from Rhino.FileIO import File3dm
|
||||||
|
import Rhino.Geometry as rg
|
||||||
|
bbox = rg.BoundingBox.Empty
|
||||||
|
geoms_attrs = []
|
||||||
|
for o in new_objs:
|
||||||
|
g = o.Geometry
|
||||||
|
if g is None: continue
|
||||||
|
try:
|
||||||
|
bb = g.GetBoundingBox(True)
|
||||||
|
if bb.IsValid: bbox.Union(bb)
|
||||||
|
except Exception: pass
|
||||||
|
geoms_attrs.append((g, o.Attributes))
|
||||||
|
if bbox.IsValid:
|
||||||
|
offset = rg.Transform.Translation(
|
||||||
|
-bbox.Min.X, -bbox.Min.Y, -bbox.Min.Z)
|
||||||
|
else:
|
||||||
|
offset = rg.Transform.Identity
|
||||||
|
f3 = File3dm()
|
||||||
|
for g, a in geoms_attrs:
|
||||||
|
try:
|
||||||
|
g2 = g.Duplicate()
|
||||||
|
try: g2.Transform(offset)
|
||||||
|
except Exception: pass
|
||||||
|
try: f3.Objects.Add(g2, a)
|
||||||
|
except Exception:
|
||||||
|
if isinstance(g2, rg.Brep): f3.Objects.AddBrep(g2)
|
||||||
|
elif isinstance(g2, rg.Curve): f3.Objects.AddCurve(g2)
|
||||||
|
elif isinstance(g2, rg.Mesh): f3.Objects.AddMesh(g2)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[LIBRARY] convert add geom:", ex)
|
||||||
|
try: f3.Write(target, 8)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[LIBRARY] convert_to_3dm Write:", ex)
|
||||||
|
return None
|
||||||
|
except Exception as ex:
|
||||||
|
print("[LIBRARY] convert_to_3dm File3dm:", ex)
|
||||||
|
return None
|
||||||
|
finally:
|
||||||
|
# Cleanup: importierte Objekte wieder loeschen
|
||||||
|
for o in new_objs:
|
||||||
|
try: doc.Objects.Delete(o.Id, True)
|
||||||
|
except Exception: pass
|
||||||
|
# Restore Selection
|
||||||
|
try:
|
||||||
|
for gid in sel_before_ids:
|
||||||
|
try: doc.Objects.Select(gid, True)
|
||||||
|
except Exception: pass
|
||||||
|
except Exception: pass
|
||||||
|
rel = os.path.relpath(target, library_root())
|
||||||
|
print("[LIBRARY] convert_to_3dm OK: {} → {}".format(src_path, rel))
|
||||||
|
return rel
|
||||||
|
finally:
|
||||||
|
sc.sticky["dossier_library_import_busy"] = False
|
||||||
|
sc.sticky["dossier_swisstopo_busy"] = False
|
||||||
|
try: doc.Views.Redraw()
|
||||||
|
except Exception: pass
|
||||||
|
|
||||||
|
|
||||||
def save_selection_to_asset(doc, target_name):
|
def save_selection_to_asset(doc, target_name):
|
||||||
"""Speichert die aktuelle Selection aus dem Doc als eigene .3dm-Datei
|
"""Speichert die aktuelle Selection aus dem Doc als eigene .3dm-Datei
|
||||||
in library/assets/<target_name>.3dm. Returns relativer Pfad oder None.
|
in library/assets/<target_name>.3dm. Returns relativer Pfad oder None.
|
||||||
|
|||||||
+19
-14
@@ -1224,20 +1224,28 @@ class EbenenBridge(panel_base.BaseBridge):
|
|||||||
path = dlg.FileName or ""
|
path = dlg.FileName or ""
|
||||||
if not path: return
|
if not path: return
|
||||||
ext = (path.split(".")[-1] if "." in path else "").lower()
|
ext = (path.split(".")[-1] if "." in path else "").lower()
|
||||||
if ext != "3dm":
|
import os
|
||||||
# TODO: konvertieren via temporaerer RhinoDoc-Import
|
base = os.path.basename(path)
|
||||||
# Phase 1: nur .3dm direkt unterstuetzt
|
stem = os.path.splitext(base)[0]
|
||||||
print("[PROJECT-SETTINGS] Format '.{}' wird in dieser "
|
rel = None
|
||||||
"Version noch nicht konvertiert — bitte in Rhino "
|
if ext == "3dm":
|
||||||
"oeffnen + als .3dm speichern".format(ext))
|
# Direkt kopieren
|
||||||
|
rel = library.copy_to_assets(path)
|
||||||
|
elif ext in ("dwg", "dxf", "obj", "fbx", "dae",
|
||||||
|
"stl", "3ds", "skp", "iges", "igs",
|
||||||
|
"step", "stp", "ply"):
|
||||||
|
# Konvertieren via Rhino-Import
|
||||||
|
rel = library.convert_to_3dm_via_import(
|
||||||
|
path, stem + "_" + variant)
|
||||||
|
else:
|
||||||
self.send("LIBRARY_ERROR", {
|
self.send("LIBRARY_ERROR", {
|
||||||
"msg": "Format .{} noch nicht unterstuetzt. "
|
"msg": "Format .{} wird nicht unterstuetzt.".format(ext),
|
||||||
"Konvertiere in Rhino zu .3dm.".format(ext),
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
rel = library.copy_to_assets(path)
|
|
||||||
if not rel:
|
if not rel:
|
||||||
print("[PROJECT-SETTINGS] copy_to_assets failed")
|
print("[PROJECT-SETTINGS] add file failed")
|
||||||
|
self.send("LIBRARY_ERROR", {
|
||||||
|
"msg": "Konnte Datei nicht importieren — siehe Log."})
|
||||||
return
|
return
|
||||||
if target_id:
|
if target_id:
|
||||||
# Bestehendes Item updaten
|
# Bestehendes Item updaten
|
||||||
@@ -1250,10 +1258,7 @@ class EbenenBridge(panel_base.BaseBridge):
|
|||||||
break
|
break
|
||||||
library.save_manifest(m)
|
library.save_manifest(m)
|
||||||
else:
|
else:
|
||||||
# Neues Item
|
# Neues Item — stem aus dem Pfad oben bereits berechnet
|
||||||
import os
|
|
||||||
base = os.path.basename(path)
|
|
||||||
stem = os.path.splitext(base)[0]
|
|
||||||
import uuid as _uuid
|
import uuid as _uuid
|
||||||
new_id = "obj-" + _uuid.uuid4().hex[:10]
|
new_id = "obj-" + _uuid.uuid4().hex[:10]
|
||||||
item = {
|
item = {
|
||||||
|
|||||||
Reference in New Issue
Block a user