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