vedo.assembly
Submodule for managing groups of vedo objects
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3import numpy as np 4 5try: 6 import vedo.vtkclasses as vtk 7except ImportError: 8 import vtkmodules.all as vtk 9 10import vedo 11 12__docformat__ = "google" 13 14__doc__ = """ 15Submodule for managing groups of vedo objects 16 17![](https://vedo.embl.es/images/basic/align4.png) 18""" 19 20__all__ = ["Group", "Assembly", "procrustes_alignment"] 21 22 23################################################# 24def procrustes_alignment(sources, rigid=False): 25 """ 26 Return an ``Assembly`` of aligned source meshes with the `Procrustes` algorithm. 27 The output ``Assembly`` is normalized in size. 28 29 The `Procrustes` algorithm takes N set of points and aligns them in a least-squares sense 30 to their mutual mean. The algorithm is iterated until convergence, 31 as the mean must be recomputed after each alignment. 32 33 The set of average points generated by the algorithm can be accessed with 34 ``algoutput.info['mean']`` as a numpy array. 35 36 Arguments: 37 rigid : bool 38 if `True` scaling is disabled. 39 40 Examples: 41 - [align4.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/align4.py) 42 43 ![](https://vedo.embl.es/images/basic/align4.png) 44 """ 45 46 group = vtk.vtkMultiBlockDataGroupFilter() 47 for source in sources: 48 if sources[0].npoints != source.npoints: 49 vedo.logger.error("sources have different nr of points") 50 raise RuntimeError() 51 group.AddInputData(source.polydata()) 52 procrustes = vtk.vtkProcrustesAlignmentFilter() 53 procrustes.StartFromCentroidOn() 54 procrustes.SetInputConnection(group.GetOutputPort()) 55 if rigid: 56 procrustes.GetLandmarkTransform().SetModeToRigidBody() 57 procrustes.Update() 58 59 acts = [] 60 for i, s in enumerate(sources): 61 poly = procrustes.GetOutput().GetBlock(i) 62 mesh = vedo.mesh.Mesh(poly) 63 mesh.SetProperty(s.GetProperty()) 64 if hasattr(s, "name"): 65 mesh.name = s.name 66 acts.append(mesh) 67 assem = Assembly(acts) 68 assem.transform = procrustes.GetLandmarkTransform() 69 assem.info["mean"] = vedo.utils.vtk2numpy(procrustes.GetMeanPoints().GetData()) 70 return assem 71 72 73################################################# 74class Group(vtk.vtkPropAssembly): 75 """Form groups of generic objects (not necessarily meshes).""" 76 77 def __init__(self, objects=()): 78 """Form groups of generic objects (not necessarily meshes).""" 79 80 vtk.vtkPropAssembly.__init__(self) 81 82 self.name = "" 83 self.created = "" 84 self.trail = None 85 self.trail_points = [] 86 self.trail_segment_size = 0 87 self.trail_offset = None 88 self.shadows = [] 89 self.info = {} 90 self.rendered_at = set() 91 self.transform = None 92 self.scalarbar = None 93 94 for a in vedo.utils.flatten(objects): 95 if a: 96 self.AddPart(a) 97 98 self.PickableOff() 99 100 def __iadd__(self, obj): 101 """ 102 Add an object to the group 103 """ 104 if not vedo.utils.is_sequence(obj): 105 obj = [obj] 106 for a in obj: 107 if a: 108 self.AddPart(a) 109 return self 110 111 def unpack(self): 112 """Unpack the group into its elements""" 113 elements = [] 114 self.InitPathTraversal() 115 parts = self.GetParts() 116 parts.InitTraversal() 117 for i in range(parts.GetNumberOfItems()): 118 ele = parts.GetItemAsObject(i) 119 elements.append(ele) 120 121 # gr.InitPathTraversal() 122 # for _ in range(gr.GetNumberOfPaths()): 123 # path = gr.GetNextPath() 124 # print([path]) 125 # path.InitTraversal() 126 # for i in range(path.GetNumberOfItems()): 127 # a = path.GetItemAsObject(i).GetViewProp() 128 # print([a]) 129 130 return elements 131 132 def clear(self): 133 """Remove all parts""" 134 for a in self.unpack(): 135 self.RemovePart(a) 136 return self 137 138 def on(self): 139 """Switch on visibility""" 140 self.VisibilityOn() 141 return self 142 143 def off(self): 144 """Switch off visibility""" 145 self.VisibilityOff() 146 return self 147 148 def pickable(self, value=None): 149 """Set/get the pickability property of an object.""" 150 if value is None: 151 return self.GetPickable() 152 self.SetPickable(value) 153 return self 154 155 def draggable(self, value=None): 156 """Set/get the draggability property of an object.""" 157 if value is None: 158 return self.GetDragable() 159 self.SetDragable(value) 160 return self 161 162 def pos(self, x=None, y=None): 163 """Set/Get object position.""" 164 if x is None: # get functionality 165 return np.array(self.GetPosition()) 166 167 if y is None: # assume x is of the form (x,y) 168 x, y = x 169 self.SetPosition(x, y) 170 return self 171 172 def shift(self, ds): 173 """Add a shift to the current object position.""" 174 p = np.array(self.GetPosition()) 175 176 self.SetPosition(p + ds) 177 return self 178 179 def bounds(self): 180 """ 181 Get the object bounds. 182 Returns a list in format [xmin,xmax, ymin,ymax]. 183 """ 184 return self.GetBounds() 185 186 def diagonal_size(self): 187 """Get the length of the diagonal""" 188 b = self.GetBounds() 189 return np.sqrt((b[1] - b[0]) ** 2 + (b[3] - b[2]) ** 2) 190 191 def show(self, **options): 192 """ 193 Create on the fly an instance of class ``Plotter`` or use the last existing one to 194 show one single object. 195 196 This method is meant as a shortcut. If more than one object needs to be visualised 197 please use the syntax `show(mesh1, mesh2, volume, ..., options)`. 198 199 Returns the ``Plotter`` class instance. 200 """ 201 return vedo.plotter.show(self, **options) 202 203 204################################################# 205class Assembly(vedo.base.Base3DProp, vtk.vtkAssembly): 206 """ 207 Group many objects and treat them as a single new object. 208 """ 209 210 def __init__(self, *meshs): 211 """ 212 Group many objects and treat them as a single new object, 213 keeping track of internal transformations. 214 215 Examples: 216 - [gyroscope1.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/gyroscope1.py) 217 218 ![](https://vedo.embl.es/images/simulations/39766016-85c1c1d6-52e3-11e8-8575-d167b7ce5217.gif) 219 """ 220 vtk.vtkAssembly.__init__(self) 221 vedo.base.Base3DProp.__init__(self) 222 223 if len(meshs) == 1: 224 meshs = meshs[0] 225 else: 226 meshs = vedo.utils.flatten(meshs) 227 228 self.actors = meshs 229 230 if meshs and hasattr(meshs[0], "top"): 231 self.base = meshs[0].base 232 self.top = meshs[0].top 233 else: 234 self.base = None 235 self.top = None 236 237 scalarbars = [] 238 for a in meshs: 239 if isinstance(a, vtk.vtkProp3D): # and a.GetNumberOfPoints(): 240 self.AddPart(a) 241 if hasattr(a, "scalarbar") and a.scalarbar is not None: 242 scalarbars.append(a.scalarbar) 243 244 if len(scalarbars) > 1: 245 self.scalarbar = Group(scalarbars) 246 elif len(scalarbars) == 1: 247 self.scalarbar = scalarbars[0] 248 249 self.pipeline = vedo.utils.OperationNode( 250 "Assembly", parents=meshs, comment=f"#meshes {len(meshs)}", c="#f08080" 251 ) 252 ################################################################### 253 254 def _repr_html_(self): 255 """ 256 HTML representation of the Assembly object for Jupyter Notebooks. 257 258 Returns: 259 HTML text with the image and some properties. 260 """ 261 import io 262 import base64 263 from PIL import Image 264 265 library_name = "vedo.assembly.Assembly" 266 help_url = "https://vedo.embl.es/docs/vedo/assembly.html" 267 268 arr = self.thumbnail(zoom=1.1, elevation=-60) 269 270 im = Image.fromarray(arr) 271 buffered = io.BytesIO() 272 im.save(buffered, format="PNG", quality=100) 273 encoded = base64.b64encode(buffered.getvalue()).decode("utf-8") 274 url = "data:image/png;base64," + encoded 275 image = f"<img src='{url}'></img>" 276 277 # statisitics 278 bounds = "<br/>".join( 279 [ 280 vedo.utils.precision(min_x, 4) + " ... " + vedo.utils.precision(max_x, 4) 281 for min_x, max_x in zip(self.bounds()[::2], self.bounds()[1::2]) 282 ] 283 ) 284 285 help_text = "" 286 if self.name: 287 help_text += f"<b> {self.name}:   </b>" 288 help_text += '<b><a href="' + help_url + '" target="_blank">' + library_name + "</a></b>" 289 if self.filename: 290 dots = "" 291 if len(self.filename) > 30: 292 dots = "..." 293 help_text += f"<br/><code><i>({dots}{self.filename[-30:]})</i></code>" 294 295 allt = [ 296 "<table>", 297 "<tr>", 298 "<td>", 299 image, 300 "</td>", 301 "<td style='text-align: center; vertical-align: center;'><br/>", 302 help_text, 303 "<table>", 304 "<tr><td><b> nr. of objects </b></td><td>" 305 + str(self.GetNumberOfPaths()) 306 + "</td></tr>", 307 "<tr><td><b> position </b></td><td>" + str(self.GetPosition()) + "</td></tr>", 308 "<tr><td><b> diagonal size </b></td><td>" 309 + vedo.utils.precision(self.diagonal_size(), 5) 310 + "</td></tr>", 311 "<tr><td><b> bounds </b> <br/> (x/y/z) </td><td>" + str(bounds) + "</td></tr>", 312 "</table>", 313 "</table>", 314 ] 315 return "\n".join(allt) 316 317 def __add__(self, obj): 318 """ 319 Add an object to the assembly 320 """ 321 if isinstance(obj, vtk.vtkProp3D): 322 self.AddPart(obj) 323 324 self.actors.append(obj) 325 326 if hasattr(obj, "scalarbar") and obj.scalarbar is not None: 327 if self.scalarbar is None: 328 self.scalarbar = obj.scalarbar 329 return self 330 331 def unpack_group(scalarbar): 332 if isinstance(scalarbar, Group): 333 return scalarbar.unpack() 334 else: 335 return scalarbar 336 337 if isinstance(self.scalarbar, Group): 338 self.scalarbar += unpack_group(obj.scalarbar) 339 else: 340 self.scalarbar = Group([unpack_group(self.scalarbar), unpack_group(obj.scalarbar)]) 341 self.pipeline = vedo.utils.OperationNode("add mesh", parents=[self, obj], c="#f08080") 342 return self 343 344 def __contains__(self, obj): 345 """Allows to use ``in`` to check if an object is in the Assembly.""" 346 return obj in self.actors 347 348 def clone(self): 349 """Make a clone copy of the object.""" 350 newlist = [] 351 for a in self.actors: 352 newlist.append(a.clone()) 353 return Assembly(newlist) 354 355 def unpack(self, i=None, transformed=False): 356 """Unpack the list of objects from a ``Assembly``. 357 358 If `i` is given, get `i-th` object from a ``Assembly``. 359 Input can be a string, in this case returns the first object 360 whose name contains the given string. 361 362 Examples: 363 - [custom_axes4.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes4.py) 364 """ 365 if transformed: 366 actors = [] 367 for a in self.actors: 368 actors.append(a.clone(transformed=True)) 369 else: 370 actors = self.actors 371 372 if i is None: 373 return actors 374 elif isinstance(i, int): 375 return actors[i] 376 elif isinstance(i, str): 377 for m in actors: 378 if i in m.name: 379 return m 380 381 def recursive_unpack(self): 382 """Flatten out an Assembly.""" 383 384 def _genflatten(lst): 385 if not lst: 386 return [] 387 ## 388 if isinstance(lst[0], Assembly): 389 lst = lst[0].unpack() 390 ## 391 for elem in lst: 392 if isinstance(elem, Assembly): 393 apos = elem.GetPosition() 394 asum = np.sum(apos) 395 for x in elem.unpack(): 396 if asum: 397 yield x.clone().shift(apos) 398 else: 399 yield x 400 else: 401 yield elem 402 403 return list(_genflatten([self]))
75class Group(vtk.vtkPropAssembly): 76 """Form groups of generic objects (not necessarily meshes).""" 77 78 def __init__(self, objects=()): 79 """Form groups of generic objects (not necessarily meshes).""" 80 81 vtk.vtkPropAssembly.__init__(self) 82 83 self.name = "" 84 self.created = "" 85 self.trail = None 86 self.trail_points = [] 87 self.trail_segment_size = 0 88 self.trail_offset = None 89 self.shadows = [] 90 self.info = {} 91 self.rendered_at = set() 92 self.transform = None 93 self.scalarbar = None 94 95 for a in vedo.utils.flatten(objects): 96 if a: 97 self.AddPart(a) 98 99 self.PickableOff() 100 101 def __iadd__(self, obj): 102 """ 103 Add an object to the group 104 """ 105 if not vedo.utils.is_sequence(obj): 106 obj = [obj] 107 for a in obj: 108 if a: 109 self.AddPart(a) 110 return self 111 112 def unpack(self): 113 """Unpack the group into its elements""" 114 elements = [] 115 self.InitPathTraversal() 116 parts = self.GetParts() 117 parts.InitTraversal() 118 for i in range(parts.GetNumberOfItems()): 119 ele = parts.GetItemAsObject(i) 120 elements.append(ele) 121 122 # gr.InitPathTraversal() 123 # for _ in range(gr.GetNumberOfPaths()): 124 # path = gr.GetNextPath() 125 # print([path]) 126 # path.InitTraversal() 127 # for i in range(path.GetNumberOfItems()): 128 # a = path.GetItemAsObject(i).GetViewProp() 129 # print([a]) 130 131 return elements 132 133 def clear(self): 134 """Remove all parts""" 135 for a in self.unpack(): 136 self.RemovePart(a) 137 return self 138 139 def on(self): 140 """Switch on visibility""" 141 self.VisibilityOn() 142 return self 143 144 def off(self): 145 """Switch off visibility""" 146 self.VisibilityOff() 147 return self 148 149 def pickable(self, value=None): 150 """Set/get the pickability property of an object.""" 151 if value is None: 152 return self.GetPickable() 153 self.SetPickable(value) 154 return self 155 156 def draggable(self, value=None): 157 """Set/get the draggability property of an object.""" 158 if value is None: 159 return self.GetDragable() 160 self.SetDragable(value) 161 return self 162 163 def pos(self, x=None, y=None): 164 """Set/Get object position.""" 165 if x is None: # get functionality 166 return np.array(self.GetPosition()) 167 168 if y is None: # assume x is of the form (x,y) 169 x, y = x 170 self.SetPosition(x, y) 171 return self 172 173 def shift(self, ds): 174 """Add a shift to the current object position.""" 175 p = np.array(self.GetPosition()) 176 177 self.SetPosition(p + ds) 178 return self 179 180 def bounds(self): 181 """ 182 Get the object bounds. 183 Returns a list in format [xmin,xmax, ymin,ymax]. 184 """ 185 return self.GetBounds() 186 187 def diagonal_size(self): 188 """Get the length of the diagonal""" 189 b = self.GetBounds() 190 return np.sqrt((b[1] - b[0]) ** 2 + (b[3] - b[2]) ** 2) 191 192 def show(self, **options): 193 """ 194 Create on the fly an instance of class ``Plotter`` or use the last existing one to 195 show one single object. 196 197 This method is meant as a shortcut. If more than one object needs to be visualised 198 please use the syntax `show(mesh1, mesh2, volume, ..., options)`. 199 200 Returns the ``Plotter`` class instance. 201 """ 202 return vedo.plotter.show(self, **options)
Form groups of generic objects (not necessarily meshes).
78 def __init__(self, objects=()): 79 """Form groups of generic objects (not necessarily meshes).""" 80 81 vtk.vtkPropAssembly.__init__(self) 82 83 self.name = "" 84 self.created = "" 85 self.trail = None 86 self.trail_points = [] 87 self.trail_segment_size = 0 88 self.trail_offset = None 89 self.shadows = [] 90 self.info = {} 91 self.rendered_at = set() 92 self.transform = None 93 self.scalarbar = None 94 95 for a in vedo.utils.flatten(objects): 96 if a: 97 self.AddPart(a) 98 99 self.PickableOff()
Form groups of generic objects (not necessarily meshes).
112 def unpack(self): 113 """Unpack the group into its elements""" 114 elements = [] 115 self.InitPathTraversal() 116 parts = self.GetParts() 117 parts.InitTraversal() 118 for i in range(parts.GetNumberOfItems()): 119 ele = parts.GetItemAsObject(i) 120 elements.append(ele) 121 122 # gr.InitPathTraversal() 123 # for _ in range(gr.GetNumberOfPaths()): 124 # path = gr.GetNextPath() 125 # print([path]) 126 # path.InitTraversal() 127 # for i in range(path.GetNumberOfItems()): 128 # a = path.GetItemAsObject(i).GetViewProp() 129 # print([a]) 130 131 return elements
Unpack the group into its elements
133 def clear(self): 134 """Remove all parts""" 135 for a in self.unpack(): 136 self.RemovePart(a) 137 return self
Remove all parts
149 def pickable(self, value=None): 150 """Set/get the pickability property of an object.""" 151 if value is None: 152 return self.GetPickable() 153 self.SetPickable(value) 154 return self
Set/get the pickability property of an object.
156 def draggable(self, value=None): 157 """Set/get the draggability property of an object.""" 158 if value is None: 159 return self.GetDragable() 160 self.SetDragable(value) 161 return self
Set/get the draggability property of an object.
163 def pos(self, x=None, y=None): 164 """Set/Get object position.""" 165 if x is None: # get functionality 166 return np.array(self.GetPosition()) 167 168 if y is None: # assume x is of the form (x,y) 169 x, y = x 170 self.SetPosition(x, y) 171 return self
Set/Get object position.
173 def shift(self, ds): 174 """Add a shift to the current object position.""" 175 p = np.array(self.GetPosition()) 176 177 self.SetPosition(p + ds) 178 return self
Add a shift to the current object position.
180 def bounds(self): 181 """ 182 Get the object bounds. 183 Returns a list in format [xmin,xmax, ymin,ymax]. 184 """ 185 return self.GetBounds()
Get the object bounds. Returns a list in format [xmin,xmax, ymin,ymax].
187 def diagonal_size(self): 188 """Get the length of the diagonal""" 189 b = self.GetBounds() 190 return np.sqrt((b[1] - b[0]) ** 2 + (b[3] - b[2]) ** 2)
Get the length of the diagonal
192 def show(self, **options): 193 """ 194 Create on the fly an instance of class ``Plotter`` or use the last existing one to 195 show one single object. 196 197 This method is meant as a shortcut. If more than one object needs to be visualised 198 please use the syntax `show(mesh1, mesh2, volume, ..., options)`. 199 200 Returns the ``Plotter`` class instance. 201 """ 202 return vedo.plotter.show(self, **options)
Create on the fly an instance of class Plotter
or use the last existing one to
show one single object.
This method is meant as a shortcut. If more than one object needs to be visualised
please use the syntax show(mesh1, mesh2, volume, ..., options)
.
Returns the Plotter
class instance.
206class Assembly(vedo.base.Base3DProp, vtk.vtkAssembly): 207 """ 208 Group many objects and treat them as a single new object. 209 """ 210 211 def __init__(self, *meshs): 212 """ 213 Group many objects and treat them as a single new object, 214 keeping track of internal transformations. 215 216 Examples: 217 - [gyroscope1.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/gyroscope1.py) 218 219 ![](https://vedo.embl.es/images/simulations/39766016-85c1c1d6-52e3-11e8-8575-d167b7ce5217.gif) 220 """ 221 vtk.vtkAssembly.__init__(self) 222 vedo.base.Base3DProp.__init__(self) 223 224 if len(meshs) == 1: 225 meshs = meshs[0] 226 else: 227 meshs = vedo.utils.flatten(meshs) 228 229 self.actors = meshs 230 231 if meshs and hasattr(meshs[0], "top"): 232 self.base = meshs[0].base 233 self.top = meshs[0].top 234 else: 235 self.base = None 236 self.top = None 237 238 scalarbars = [] 239 for a in meshs: 240 if isinstance(a, vtk.vtkProp3D): # and a.GetNumberOfPoints(): 241 self.AddPart(a) 242 if hasattr(a, "scalarbar") and a.scalarbar is not None: 243 scalarbars.append(a.scalarbar) 244 245 if len(scalarbars) > 1: 246 self.scalarbar = Group(scalarbars) 247 elif len(scalarbars) == 1: 248 self.scalarbar = scalarbars[0] 249 250 self.pipeline = vedo.utils.OperationNode( 251 "Assembly", parents=meshs, comment=f"#meshes {len(meshs)}", c="#f08080" 252 ) 253 ################################################################### 254 255 def _repr_html_(self): 256 """ 257 HTML representation of the Assembly object for Jupyter Notebooks. 258 259 Returns: 260 HTML text with the image and some properties. 261 """ 262 import io 263 import base64 264 from PIL import Image 265 266 library_name = "vedo.assembly.Assembly" 267 help_url = "https://vedo.embl.es/docs/vedo/assembly.html" 268 269 arr = self.thumbnail(zoom=1.1, elevation=-60) 270 271 im = Image.fromarray(arr) 272 buffered = io.BytesIO() 273 im.save(buffered, format="PNG", quality=100) 274 encoded = base64.b64encode(buffered.getvalue()).decode("utf-8") 275 url = "data:image/png;base64," + encoded 276 image = f"<img src='{url}'></img>" 277 278 # statisitics 279 bounds = "<br/>".join( 280 [ 281 vedo.utils.precision(min_x, 4) + " ... " + vedo.utils.precision(max_x, 4) 282 for min_x, max_x in zip(self.bounds()[::2], self.bounds()[1::2]) 283 ] 284 ) 285 286 help_text = "" 287 if self.name: 288 help_text += f"<b> {self.name}:   </b>" 289 help_text += '<b><a href="' + help_url + '" target="_blank">' + library_name + "</a></b>" 290 if self.filename: 291 dots = "" 292 if len(self.filename) > 30: 293 dots = "..." 294 help_text += f"<br/><code><i>({dots}{self.filename[-30:]})</i></code>" 295 296 allt = [ 297 "<table>", 298 "<tr>", 299 "<td>", 300 image, 301 "</td>", 302 "<td style='text-align: center; vertical-align: center;'><br/>", 303 help_text, 304 "<table>", 305 "<tr><td><b> nr. of objects </b></td><td>" 306 + str(self.GetNumberOfPaths()) 307 + "</td></tr>", 308 "<tr><td><b> position </b></td><td>" + str(self.GetPosition()) + "</td></tr>", 309 "<tr><td><b> diagonal size </b></td><td>" 310 + vedo.utils.precision(self.diagonal_size(), 5) 311 + "</td></tr>", 312 "<tr><td><b> bounds </b> <br/> (x/y/z) </td><td>" + str(bounds) + "</td></tr>", 313 "</table>", 314 "</table>", 315 ] 316 return "\n".join(allt) 317 318 def __add__(self, obj): 319 """ 320 Add an object to the assembly 321 """ 322 if isinstance(obj, vtk.vtkProp3D): 323 self.AddPart(obj) 324 325 self.actors.append(obj) 326 327 if hasattr(obj, "scalarbar") and obj.scalarbar is not None: 328 if self.scalarbar is None: 329 self.scalarbar = obj.scalarbar 330 return self 331 332 def unpack_group(scalarbar): 333 if isinstance(scalarbar, Group): 334 return scalarbar.unpack() 335 else: 336 return scalarbar 337 338 if isinstance(self.scalarbar, Group): 339 self.scalarbar += unpack_group(obj.scalarbar) 340 else: 341 self.scalarbar = Group([unpack_group(self.scalarbar), unpack_group(obj.scalarbar)]) 342 self.pipeline = vedo.utils.OperationNode("add mesh", parents=[self, obj], c="#f08080") 343 return self 344 345 def __contains__(self, obj): 346 """Allows to use ``in`` to check if an object is in the Assembly.""" 347 return obj in self.actors 348 349 def clone(self): 350 """Make a clone copy of the object.""" 351 newlist = [] 352 for a in self.actors: 353 newlist.append(a.clone()) 354 return Assembly(newlist) 355 356 def unpack(self, i=None, transformed=False): 357 """Unpack the list of objects from a ``Assembly``. 358 359 If `i` is given, get `i-th` object from a ``Assembly``. 360 Input can be a string, in this case returns the first object 361 whose name contains the given string. 362 363 Examples: 364 - [custom_axes4.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes4.py) 365 """ 366 if transformed: 367 actors = [] 368 for a in self.actors: 369 actors.append(a.clone(transformed=True)) 370 else: 371 actors = self.actors 372 373 if i is None: 374 return actors 375 elif isinstance(i, int): 376 return actors[i] 377 elif isinstance(i, str): 378 for m in actors: 379 if i in m.name: 380 return m 381 382 def recursive_unpack(self): 383 """Flatten out an Assembly.""" 384 385 def _genflatten(lst): 386 if not lst: 387 return [] 388 ## 389 if isinstance(lst[0], Assembly): 390 lst = lst[0].unpack() 391 ## 392 for elem in lst: 393 if isinstance(elem, Assembly): 394 apos = elem.GetPosition() 395 asum = np.sum(apos) 396 for x in elem.unpack(): 397 if asum: 398 yield x.clone().shift(apos) 399 else: 400 yield x 401 else: 402 yield elem 403 404 return list(_genflatten([self]))
Group many objects and treat them as a single new object.
211 def __init__(self, *meshs): 212 """ 213 Group many objects and treat them as a single new object, 214 keeping track of internal transformations. 215 216 Examples: 217 - [gyroscope1.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/gyroscope1.py) 218 219 ![](https://vedo.embl.es/images/simulations/39766016-85c1c1d6-52e3-11e8-8575-d167b7ce5217.gif) 220 """ 221 vtk.vtkAssembly.__init__(self) 222 vedo.base.Base3DProp.__init__(self) 223 224 if len(meshs) == 1: 225 meshs = meshs[0] 226 else: 227 meshs = vedo.utils.flatten(meshs) 228 229 self.actors = meshs 230 231 if meshs and hasattr(meshs[0], "top"): 232 self.base = meshs[0].base 233 self.top = meshs[0].top 234 else: 235 self.base = None 236 self.top = None 237 238 scalarbars = [] 239 for a in meshs: 240 if isinstance(a, vtk.vtkProp3D): # and a.GetNumberOfPoints(): 241 self.AddPart(a) 242 if hasattr(a, "scalarbar") and a.scalarbar is not None: 243 scalarbars.append(a.scalarbar) 244 245 if len(scalarbars) > 1: 246 self.scalarbar = Group(scalarbars) 247 elif len(scalarbars) == 1: 248 self.scalarbar = scalarbars[0] 249 250 self.pipeline = vedo.utils.OperationNode( 251 "Assembly", parents=meshs, comment=f"#meshes {len(meshs)}", c="#f08080" 252 ) 253 ###################################################################
Group many objects and treat them as a single new object, keeping track of internal transformations.
Examples:
349 def clone(self): 350 """Make a clone copy of the object.""" 351 newlist = [] 352 for a in self.actors: 353 newlist.append(a.clone()) 354 return Assembly(newlist)
Make a clone copy of the object.
356 def unpack(self, i=None, transformed=False): 357 """Unpack the list of objects from a ``Assembly``. 358 359 If `i` is given, get `i-th` object from a ``Assembly``. 360 Input can be a string, in this case returns the first object 361 whose name contains the given string. 362 363 Examples: 364 - [custom_axes4.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes4.py) 365 """ 366 if transformed: 367 actors = [] 368 for a in self.actors: 369 actors.append(a.clone(transformed=True)) 370 else: 371 actors = self.actors 372 373 if i is None: 374 return actors 375 elif isinstance(i, int): 376 return actors[i] 377 elif isinstance(i, str): 378 for m in actors: 379 if i in m.name: 380 return m
382 def recursive_unpack(self): 383 """Flatten out an Assembly.""" 384 385 def _genflatten(lst): 386 if not lst: 387 return [] 388 ## 389 if isinstance(lst[0], Assembly): 390 lst = lst[0].unpack() 391 ## 392 for elem in lst: 393 if isinstance(elem, Assembly): 394 apos = elem.GetPosition() 395 asum = np.sum(apos) 396 for x in elem.unpack(): 397 if asum: 398 yield x.clone().shift(apos) 399 else: 400 yield x 401 else: 402 yield elem 403 404 return list(_genflatten([self]))
Flatten out an Assembly.
Inherited Members
25def procrustes_alignment(sources, rigid=False): 26 """ 27 Return an ``Assembly`` of aligned source meshes with the `Procrustes` algorithm. 28 The output ``Assembly`` is normalized in size. 29 30 The `Procrustes` algorithm takes N set of points and aligns them in a least-squares sense 31 to their mutual mean. The algorithm is iterated until convergence, 32 as the mean must be recomputed after each alignment. 33 34 The set of average points generated by the algorithm can be accessed with 35 ``algoutput.info['mean']`` as a numpy array. 36 37 Arguments: 38 rigid : bool 39 if `True` scaling is disabled. 40 41 Examples: 42 - [align4.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/align4.py) 43 44 ![](https://vedo.embl.es/images/basic/align4.png) 45 """ 46 47 group = vtk.vtkMultiBlockDataGroupFilter() 48 for source in sources: 49 if sources[0].npoints != source.npoints: 50 vedo.logger.error("sources have different nr of points") 51 raise RuntimeError() 52 group.AddInputData(source.polydata()) 53 procrustes = vtk.vtkProcrustesAlignmentFilter() 54 procrustes.StartFromCentroidOn() 55 procrustes.SetInputConnection(group.GetOutputPort()) 56 if rigid: 57 procrustes.GetLandmarkTransform().SetModeToRigidBody() 58 procrustes.Update() 59 60 acts = [] 61 for i, s in enumerate(sources): 62 poly = procrustes.GetOutput().GetBlock(i) 63 mesh = vedo.mesh.Mesh(poly) 64 mesh.SetProperty(s.GetProperty()) 65 if hasattr(s, "name"): 66 mesh.name = s.name 67 acts.append(mesh) 68 assem = Assembly(acts) 69 assem.transform = procrustes.GetLandmarkTransform() 70 assem.info["mean"] = vedo.utils.vtk2numpy(procrustes.GetMeanPoints().GetData()) 71 return assem
Return an Assembly
of aligned source meshes with the Procrustes
algorithm.
The output Assembly
is normalized in size.
The Procrustes
algorithm takes N set of points and aligns them in a least-squares sense
to their mutual mean. The algorithm is iterated until convergence,
as the mean must be recomputed after each alignment.
The set of average points generated by the algorithm can be accessed with
algoutput.info['mean']
as a numpy array.
Arguments:
- rigid : bool
if
True
scaling is disabled.