diff --git a/docs/changes.md b/docs/changes.md index e9b2bd07..9cb8a938 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -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 diff --git a/vedo/assembly.py b/vedo/assembly.py index 5d70132f..64d05f37 100644 --- a/vedo/assembly.py +++ b/vedo/assembly.py @@ -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 @@ -237,6 +238,8 @@ 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) @@ -244,11 +247,37 @@ def __init__(self, *meshs): """ 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] @@ -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] @@ -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.""" @@ -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 diff --git a/vedo/file_io.py b/vedo/file_io.py index 80b94355..5af34d99 100644 --- a/vedo/file_io.py +++ b/vedo/file_io.py @@ -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"): @@ -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() @@ -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"]) @@ -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: @@ -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.""" ######################################################## @@ -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)}") @@ -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)}") diff --git a/vedo/pointcloud.py b/vedo/pointcloud.py index 491a65f7..2fc566da 100644 --- a/vedo/pointcloud.py +++ b/vedo/pointcloud.py @@ -28,7 +28,6 @@ "Point", "CellCenters", "merge", - "delaunay2d", # deprecated, use .generate_delaunay2d() "fit_line", "fit_circle", "fit_plane", @@ -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).