Skip to content

Commit

Permalink
improvements to Assembly i/o #1202
Browse files Browse the repository at this point in the history
  • Loading branch information
marcomusy committed Dec 2, 2024
1 parent bb4414c commit 27b8c59
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 43 deletions.
2 changes: 2 additions & 0 deletions docs/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
- fix purging of nan in pyplot.plot()
- fix line trace to skip first point
- adjust volume transfer function for transparency @Poisoned
- fixing axes type 10 by @Poisoned
- improvements to input/output functionality for Assembly @ttsesm



Expand Down
64 changes: 39 additions & 25 deletions vedo/assembly.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from weakref import ref as weak_ref_to
from typing import List, Union, Any
from typing import List, Union, Any, Self
import numpy as np

import vedo.file_io
import vedo.vtkclasses as vtki # a wrapper for lazy imports

import vedo
Expand Down Expand Up @@ -237,18 +238,46 @@ def __init__(self, *meshs):
Group many objects and treat them as a single new object,
keeping track of internal transformations.
File can be loaded by passing its name as a string. Format must be `npy`.
Examples:
- [gyroscope1.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/gyroscope1.py)
![](https://vedo.embl.es/images/simulations/39766016-85c1c1d6-52e3-11e8-8575-d167b7ce5217.gif)
"""
super().__init__()

self.actor = self
self.actor.retrieve_object = weak_ref_to(self)

self.name = "Assembly"
self.filename = ""
self.rendered_at = set()
self.scalarbar = None
self.info = {}
self.time = 0

self.transform = LinearTransform()

# Init by filename
if len(meshs) == 1 and isinstance(meshs[0], str):
filename = vedo.file_io.download(meshs[0], verbose=False)
filename = vedo.file_io.download(meshs[0], verbose=False)
data = np.load(filename, allow_pickle=True)
meshs = [vedo.file_io._from_numpy(dd) for dd in data]
try:
# old format with a single object
meshs = [vedo.file_io.from_numpy(dd) for dd in data]
except TypeError:
# new format with a dictionary
data = data.item()
meshs = []
for ad in data["objects"][0]["parts"]:
obb = vedo.file_io.from_numpy(ad)
meshs.append(obb)
self.transform = LinearTransform(data["objects"][0]["transform"])
self.actor.SetPosition(self.transform.T.GetPosition())
self.actor.SetOrientation(self.transform.T.GetOrientation())
self.actor.SetScale(self.transform.T.GetScale())

# Name and load from dictionary
if len(meshs) == 1 and isinstance(meshs[0], dict):
meshs = meshs[0]
Expand All @@ -261,18 +290,6 @@ def __init__(self, *meshs):
else:
meshs = vedo.utils.flatten(meshs)

self.actor = self
self.actor.retrieve_object = weak_ref_to(self)

self.name = "Assembly"
self.filename = ""
self.rendered_at = set()
self.scalarbar = None
self.info = {}
self.time = 0

self.transform = LinearTransform()

self.objects = [m for m in meshs if m]
self.actors = [m.actor for m in self.objects]

Expand Down Expand Up @@ -471,6 +488,13 @@ def __len__(self):
"""Return nr. of objects in the assembly."""
return len(self.objects)

def write(self, filename="assembly.npy") -> Self:
"""
Write the object to file in `numpy` format (npy).
"""
vedo.file_io.write(self, filename)
return self

# TODO ####
# def propagate_transform(self):
# """Propagate the transformation to all parts."""
Expand Down Expand Up @@ -660,13 +684,3 @@ def copy(self) -> "Assembly":
"""Return a copy of the object. Alias of `clone()`."""
return self.clone()

# def write(self, filename="assembly.npy") -> "Assembly":
# """
# Write the object to file in `numpy` format.
# """
# objs = []
# for ob in self.unpack():
# d = vedo.file_io._to_numpy(ob)
# objs.append(d)
# np.save(filename, objs)
# return self
41 changes: 36 additions & 5 deletions vedo/file_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,20 @@ def _load_file(filename, unpack):
acts.append(vedo.UnstructuredGrid(b))
return acts
return mb

######################################################### assembly:
elif fl.endswith(".npy"):
data = np.load(filename, allow_pickle=True)
try:
# old format with a single object
meshs = [from_numpy(dd) for dd in data]
except TypeError:
data = data.item()
meshs = []
for ad in data["objects"][0]["parts"]:
obb = from_numpy(ad)
meshs.append(obb)
return Assembly(meshs)

###########################################################
elif fl.endswith(".geojson"):
Expand Down Expand Up @@ -881,7 +895,7 @@ def loadPCD(filename: Union[str, os.PathLike]) -> Points:
return Points(poly).point_size(4)

#########################################################################
def _from_numpy(d: dict) -> Mesh:
def from_numpy(d: dict) -> Mesh:
# recreate a mesh from numpy arrays
keys = d.keys()

Expand Down Expand Up @@ -1062,13 +1076,13 @@ def _import_npy(fileinput: Union[str, os.PathLike]) -> "vedo.Plotter":
for d in data["objects"]:
### Mesh
if d['type'].lower() == 'mesh':
obj = _from_numpy(d)
obj = from_numpy(d)

### Assembly
elif d['type'].lower() == 'assembly':
assacts = []
for ad in d["actors"]:
assacts.append(_from_numpy(ad))
assacts.append(from_numpy(ad))
obj = Assembly(assacts)
obj.SetScale(d["scale"])
obj.SetPosition(d["position"])
Expand Down Expand Up @@ -1171,6 +1185,15 @@ def write(objct: Any, fileoutput: Union[str, os.PathLike], binary=True) -> Any:
- `vtk, vti, ply, obj, stl, byu, vtp, vti, mhd, xyz, xml, tif, png, bmp`
"""
fileoutput = str(fileoutput)

###############################
if isinstance(objct, Assembly):
dd = to_numpy(objct)
sdict = {"objects": [dd]}
np.save(fileoutput, sdict)
return objct

###############################
obj = objct.dataset

try:
Expand Down Expand Up @@ -1424,7 +1447,7 @@ def export_window(fileoutput: Union[str, os.PathLike], binary=False, plt=None) -
return plt

#########################################################################
def _to_numpy(act: Any) -> dict:
def to_numpy(act: Any) -> dict:
"""Encode a vedo object to numpy format."""

########################################################
Expand Down Expand Up @@ -1603,6 +1626,14 @@ def _fillcommon(obj, adict):
adict["bgcol"] = obj.properties.GetBackgroundColor()
adict["alpha"] = obj.properties.GetBackgroundOpacity()
adict["frame"] = obj.properties.GetFrame()

######################################################## Assembly
elif isinstance(obj, Assembly):
adict["type"] = "Assembly"
_fillcommon(obj, adict)
adict["parts"] = []
for a in obj.unpack():
adict["parts"].append(to_numpy(a))

else:
# vedo.logger.warning(f"to_numpy: cannot export object of type {type(obj)}")
Expand Down Expand Up @@ -1681,7 +1712,7 @@ def _export_npy(plt, fileoutput="scene.npz") -> None:
for a in allobjs:
# print("to_numpy(): dumping", [a], a.name)
# try:
npobj = _to_numpy(a)
npobj = to_numpy(a)
sdict["objects"].append(npobj)
# except AttributeError:
# vedo.logger.warning(f"Cannot export object of type {type(a)}")
Expand Down
13 changes: 0 additions & 13 deletions vedo/pointcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
"Point",
"CellCenters",
"merge",
"delaunay2d", # deprecated, use .generate_delaunay2d()
"fit_line",
"fit_circle",
"fit_plane",
Expand Down Expand Up @@ -93,18 +92,6 @@ def merge(*meshs, flag=False) -> Union["vedo.Mesh", "vedo.Points", None]:
return msh


def delaunay2d(plist, **kwargs) -> Self:
"""delaunay2d() is deprecated, use Points().generate_delaunay2d() instead."""
if isinstance(plist, Points):
plist = plist.vertices
else:
plist = np.ascontiguousarray(plist)
plist = utils.make3d(plist)
pp = Points(plist).generate_delaunay2d(**kwargs)
print("WARNING: delaunay2d() is deprecated, use Points().generate_delaunay2d() instead")
return pp


def _rotate_points(points, n0=None, n1=(0, 0, 1)) -> Union[np.ndarray, tuple]:
# Rotate a set of 3D points from direction n0 to direction n1.
# Return the rotated points and the normal to the fitting plane (if n0 is None).
Expand Down

0 comments on commit 27b8c59

Please sign in to comment.