vedo.base
Base classes. Do not instantiate.
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3import time 4import numpy as np 5from deprecated import deprecated 6 7try: 8 import vedo.vtkclasses as vtk 9except ImportError: 10 import vtkmodules.all as vtk 11 12import vedo 13from vedo import colors 14from vedo import utils 15 16__docformat__ = "google" 17 18__doc__ = "Base classes. Do not instantiate." 19 20__all__ = [ 21 "Base3DProp", 22 "BaseActor", 23 "BaseActor2D", 24 "BaseGrid", 25 "probe_points", 26 "probe_line", 27 "probe_plane", 28] 29 30 31############################################################################### 32class _DataArrayHelper: 33 # Helper class to manage data associated to either 34 # points (or vertices) and cells (or faces). 35 # Internal use only. 36 def __init__(self, actor, association): 37 self.actor = actor 38 self.association = association 39 40 def __getitem__(self, key): 41 42 if self.association == 0: 43 data = self.actor.inputdata().GetPointData() 44 45 elif self.association == 1: 46 data = self.actor.inputdata().GetCellData() 47 48 elif self.association == 2: 49 data = self.actor.inputdata().GetFieldData() 50 51 varr = data.GetAbstractArray(key) 52 if isinstance(varr, vtk.vtkStringArray): 53 if isinstance(key, int): 54 key = data.GetArrayName(key) 55 n = varr.GetNumberOfValues() 56 narr = [varr.GetValue(i) for i in range(n)] 57 return narr 58 ########### 59 60 else: 61 raise RuntimeError() 62 63 if isinstance(key, int): 64 key = data.GetArrayName(key) 65 66 arr = data.GetArray(key) 67 if not arr: 68 return None 69 return utils.vtk2numpy(arr) 70 71 def __setitem__(self, key, input_array): 72 73 if self.association == 0: 74 data = self.actor.inputdata().GetPointData() 75 n = self.actor.inputdata().GetNumberOfPoints() 76 self.actor._mapper.SetScalarModeToUsePointData() 77 78 elif self.association == 1: 79 data = self.actor.inputdata().GetCellData() 80 n = self.actor.inputdata().GetNumberOfCells() 81 self.actor._mapper.SetScalarModeToUseCellData() 82 83 elif self.association == 2: 84 data = self.actor.inputdata().GetFieldData() 85 if not utils.is_sequence(input_array): 86 input_array = [input_array] 87 88 if isinstance(input_array[0], str): 89 varr = vtk.vtkStringArray() 90 varr.SetName(key) 91 varr.SetNumberOfComponents(1) 92 varr.SetNumberOfTuples(len(input_array)) 93 for i, iarr in enumerate(input_array): 94 if isinstance(iarr, np.ndarray): 95 iarr = iarr.tolist() # better format 96 # Note: a string k can be converted to numpy with 97 # import json; k = np.array(json.loads(k)) 98 varr.InsertValue(i, str(iarr)) 99 else: 100 try: 101 varr = utils.numpy2vtk(input_array, name=key) 102 except TypeError as e: 103 vedo.logger.error( 104 f"cannot create metadata with input object:\n" 105 f"{input_array}" 106 f"\n\nAllowed content examples are:\n" 107 f"- flat list of strings ['a','b', 1, [1,2,3], ...]" 108 f" (first item must be a string in this case)\n" 109 f" hint: use k = np.array(json.loads(k)) to convert strings\n" 110 f"- numpy arrays of any shape" 111 ) 112 raise e 113 114 data.AddArray(varr) 115 return ############ 116 117 else: 118 raise RuntimeError() 119 120 if len(input_array) != n: 121 vedo.logger.error( 122 f"Error in point/cell data: length of input {len(input_array)}" 123 f" != {n} nr. of elements" 124 ) 125 raise RuntimeError() 126 127 input_array = np.asarray(input_array) 128 varr = utils.numpy2vtk(input_array, name=key) 129 data.AddArray(varr) 130 131 if len(input_array.shape) == 1: # scalars 132 data.SetActiveScalars(key) 133 elif len(input_array.shape) == 2 and input_array.shape[1] == 3: # vectors 134 if key.lower() == "normals": 135 data.SetActiveNormals(key) 136 else: 137 data.SetActiveVectors(key) 138 139 def keys(self): 140 """Return the list of available data array names""" 141 if self.association == 0: 142 data = self.actor.inputdata().GetPointData() 143 elif self.association == 1: 144 data = self.actor.inputdata().GetCellData() 145 elif self.association == 2: 146 data = self.actor.inputdata().GetFieldData() 147 arrnames = [] 148 for i in range(data.GetNumberOfArrays()): 149 name = data.GetArray(i).GetName() 150 if name: 151 arrnames.append(name) 152 return arrnames 153 154 def remove(self, key): 155 """Remove a data array by name or number""" 156 if self.association == 0: 157 self.actor.inputdata().GetPointData().RemoveArray(key) 158 elif self.association == 1: 159 self.actor.inputdata().GetCellData().RemoveArray(key) 160 elif self.association == 2: 161 self.actor.inputdata().GetFieldData().RemoveArray(key) 162 163 def rename(self, oldname, newname): 164 """Rename an array""" 165 if self.association == 0: 166 varr = self.actor.inputdata().GetPointData().GetArray(oldname) 167 elif self.association == 1: 168 varr = self.actor.inputdata().GetCellData().GetArray(oldname) 169 elif self.association == 2: 170 varr = self.actor.inputdata().GetFieldData().GetArray(oldname) 171 if varr: 172 varr.SetName(newname) 173 else: 174 vedo.logger.warning( 175 f"Cannot rename non existing array {oldname} to {newname}" 176 ) 177 178 def select(self, key): 179 """Select one specific array by its name to make it the `active` one.""" 180 if self.association == 0: 181 data = self.actor.inputdata().GetPointData() 182 self.actor.mapper().SetScalarModeToUsePointData() 183 else: 184 data = self.actor.inputdata().GetCellData() 185 self.actor.mapper().SetScalarModeToUseCellData() 186 187 if isinstance(key, int): 188 key = data.GetArrayName(key) 189 data.SetActiveScalars(key) 190 191 if hasattr(self.actor.mapper(), "SetArrayName"): 192 self.actor.mapper().SetArrayName(key) 193 194 if hasattr(self.actor.mapper(), "ScalarVisibilityOn"): # could be volume mapper 195 self.actor.mapper().ScalarVisibilityOn() 196 197 def print(self, **kwargs): 198 """Print the array names available to terminal""" 199 colors.printc(self.keys(), **kwargs) 200 201 202############################################################################### 203class Base3DProp: 204 """ 205 Base class to manage positioning and size of the objects in space and other properties. 206 207 .. warning:: Do not use this class to instanciate objects 208 """ 209 def __init__(self): 210 """ 211 Base class to manage positioning and size of the objects in space and other properties. 212 """ 213 self.filename = "" 214 self.name = "" 215 self.file_size = "" 216 self.created = "" 217 self.trail = None 218 self.trail_points = [] 219 self.trail_segment_size = 0 220 self.trail_offset = None 221 self.shadows = [] 222 self.axes = None 223 self.picked3d = None 224 self.units = None 225 self.top = np.array([0, 0, 1]) 226 self.base = np.array([0, 0, 0]) 227 self.info = {} 228 self.time = time.time() 229 self.rendered_at = set() 230 self.transform = None 231 self._isfollower = False # set by mesh.follow_camera() 232 233 self.point_locator = None 234 self.cell_locator = None 235 236 self.scalarbar = None 237 # self.scalarbars = dict() #TODO 238 self.pipeline = None 239 240 241 def address(self): 242 """ 243 Return a unique memory address integer which may serve as the ID of the 244 object, or passed to c++ code. 245 """ 246 # https://www.linkedin.com/pulse/speedup-your-code-accessing-python-vtk-objects-from-c-pletzer/ 247 # https://github.com/tfmoraes/polydata_connectivity 248 return int(self.inputdata().GetAddressAsString("")[5:], 16) 249 250 def pickable(self, value=None): 251 """Set/get the pickability property of an object.""" 252 if value is None: 253 return self.GetPickable() 254 self.SetPickable(value) 255 return self 256 257 def draggable(self, value=None): # NOT FUNCTIONAL? 258 """Set/get the draggability property of an object.""" 259 if value is None: 260 return self.GetDragable() 261 self.SetDragable(value) 262 return self 263 264 def origin(self, x=None, y=None, z=None): 265 """ 266 Set/get object's origin. 267 268 Relevant to control the scaling with `scale()` and rotations. 269 Has no effect on position. 270 """ 271 if x is None: 272 return np.array(self.GetOrigin()) + self.GetPosition() 273 274 if z is None and y is None: # assume x is of the form (x,y,z) 275 if len(x) == 3: 276 x, y, z = x 277 else: 278 x, y = x 279 z = 0 280 elif z is None: # assume x,y is of the form x, y 281 z = 0 282 self.SetOrigin([x, y, z] - np.array(self.GetPosition())) 283 return self 284 285 def pos(self, x=None, y=None, z=None): 286 """Set/Get object position.""" 287 if x is None: # get functionality 288 return np.array(self.GetPosition()) 289 290 if z is None and y is None: # assume x is of the form (x,y,z) 291 if len(x) == 3: 292 x, y, z = x 293 else: 294 x, y = x 295 z = 0 296 elif z is None: # assume x,y is of the form x, y 297 z = 0 298 self.SetPosition(x, y, z) 299 300 self.point_locator = None 301 self.cell_locator = None 302 return self # return itself to concatenate methods 303 304 def shift(self, dx=0, dy=0, dz=0): 305 """Add a vector to the current object position.""" 306 p = np.array(self.GetPosition()) 307 308 if utils.is_sequence(dx): 309 if len(dx) == 2: 310 self.SetPosition(p + [dx[0], dx[1], 0]) 311 else: 312 self.SetPosition(p + dx) 313 else: 314 self.SetPosition(p + [dx, dy, dz]) 315 316 self.point_locator = None 317 self.cell_locator = None 318 return self 319 320 def x(self, val=None): 321 """Set/Get object position along x axis.""" 322 p = self.GetPosition() 323 if val is None: 324 return p[0] 325 self.pos(val, p[1], p[2]) 326 return self 327 328 def y(self, val=None): 329 """Set/Get object position along y axis.""" 330 p = self.GetPosition() 331 if val is None: 332 return p[1] 333 self.pos(p[0], val, p[2]) 334 return self 335 336 def z(self, val=None): 337 """Set/Get object position along z axis.""" 338 p = self.GetPosition() 339 if val is None: 340 return p[2] 341 self.pos(p[0], p[1], val) 342 return self 343 344 def rotate(self, angle, axis=(1, 0, 0), point=(0, 0, 0), rad=False): 345 """ 346 Rotate around an arbitrary `axis` passing through `point`. 347 348 Example: 349 ```python 350 from vedo import * 351 c1 = Cube() 352 c2 = c1.clone().c('violet').alpha(0.5) # copy of c1 353 v = vector(0.2,1,0) 354 p = vector(1,0,0) # axis passes through this point 355 c2.rotate(90, axis=v, point=p) 356 l = Line(-v+p, v+p).lw(3).c('red') 357 show(c1, l, c2, axes=1).close() 358 ``` 359  360 """ 361 if rad: 362 anglerad = angle 363 else: 364 anglerad = np.deg2rad(angle) 365 axis = utils.versor(axis) 366 a = np.cos(anglerad / 2) 367 b, c, d = -axis * np.sin(anglerad / 2) 368 aa, bb, cc, dd = a * a, b * b, c * c, d * d 369 bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d 370 R = np.array( 371 [ 372 [aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)], 373 [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)], 374 [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc], 375 ] 376 ) 377 rv = np.dot(R, self.GetPosition() - np.asarray(point)) + point 378 379 if rad: 380 angle *= 180.0 / np.pi 381 # this vtk method only rotates in the origin of the object: 382 self.RotateWXYZ(angle, axis[0], axis[1], axis[2]) 383 self.pos(rv) 384 return self 385 386 def _rotatexyz(self, a, angle, rad, around): 387 if rad: 388 angle *= 180 / np.pi 389 390 T = vtk.vtkTransform() 391 T.SetMatrix(self.GetMatrix()) 392 T.PostMultiply() 393 394 rot = dict(x=T.RotateX, y=T.RotateY, z=T.RotateZ) 395 396 if around is None: 397 # rotate around its origin 398 rot[a](angle) 399 else: 400 if around == "itself": 401 around = self.GetPosition() 402 # displacement needed to bring it back to the origin 403 # and disregard origin 404 disp = around - np.array(self.GetOrigin()) 405 T.Translate(-disp) 406 rot[a](angle) 407 T.Translate(disp) 408 409 self.SetOrientation(T.GetOrientation()) 410 self.SetPosition(T.GetPosition()) 411 412 self.point_locator = None 413 self.cell_locator = None 414 return self 415 416 @deprecated(reason=vedo.colors.red + "Please use rotate_x()" + vedo.colors.reset) 417 def rotateX(self, *a, **b): 418 """Deprecated. Please use `rotate_x()`.""" 419 return self.rotate_x(*a, **b) 420 @deprecated(reason=vedo.colors.red + "Please use rotate_y()" + vedo.colors.reset) 421 def rotateY(self, *a, **b): 422 """Deprecated. Please use `rotate_y()`.""" 423 return self.rotate_y(*a, **b) 424 @deprecated(reason=vedo.colors.red + "Please use rotate_z()" + vedo.colors.reset) 425 def rotateZ(self, *a, **b): 426 """Deprecated. Please use `rotate_z()`.""" 427 return self.rotate_z(*a, **b) 428 429 def rotate_x(self, angle, rad=False, around=None): 430 """ 431 Rotate around x-axis. If angle is in radians set `rad=True`. 432 433 Use `around` to define a pivoting point. 434 """ 435 return self._rotatexyz("x", angle, rad, around) 436 437 def rotate_y(self, angle, rad=False, around=None): 438 """ 439 Rotate around y-axis. If angle is in radians set `rad=True`. 440 441 Use `around` to define a pivoting point. 442 """ 443 return self._rotatexyz("y", angle, rad, around) 444 445 def rotate_z(self, angle, rad=False, around=None): 446 """ 447 Rotate around z-axis. If angle is in radians set `rad=True`. 448 449 Use `around` to define a pivoting point. 450 """ 451 return self._rotatexyz("z", angle, rad, around) 452 453 def orientation(self, newaxis=None, rotation=0, concatenate=False, xyplane=False, rad=False): 454 """ 455 Set/Get object orientation. 456 457 Arguments: 458 rotation : (float) 459 rotate object around newaxis. 460 concatenate : (bool) 461 concatenate the orietation operation with the previous existing transform (if any) 462 xyplane : (bool) 463 make an extra rotation to keep the object aligned to the xy-plane 464 rad : (bool) 465 set to True if angle is expressed in radians. 466 467 Example: 468 ```python 469 from vedo import * 470 center = np.array([581/2,723/2,0]) 471 objs = [] 472 for a in np.linspace(0, 6.28, 7): 473 v = vector(cos(a), sin(a), 0)*1000 474 pic = Picture(dataurl+"images/dog.jpg").rotate_z(10) 475 pic.orientation(v, xyplane=True) 476 pic.origin(center) 477 pic.pos(v - center) 478 objs += [pic, Arrow(v, v+v)] 479 show(objs, Point(), axes=1).close() 480 ``` 481  482 483 Examples: 484 - [gyroscope2.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/gyroscope2.py) 485 486  487 """ 488 if self.top is None or self.base is None: 489 initaxis = (0, 0, 1) 490 else: 491 initaxis = utils.versor(self.top - self.base) 492 493 newaxis = utils.versor(newaxis) 494 p = np.array(self.GetPosition()) 495 crossvec = np.cross(initaxis, newaxis) 496 497 angleth = np.arccos(np.dot(initaxis, newaxis)) 498 499 T = vtk.vtkTransform() 500 if concatenate: 501 try: 502 M = self.GetMatrix() 503 T.SetMatrix(M) 504 except: 505 pass 506 T.PostMultiply() 507 T.Translate(-p) 508 if rotation: 509 if rad: 510 rotation *= 180.0 / np.pi 511 T.RotateWXYZ(rotation, initaxis) 512 if xyplane: 513 angleph = np.arctan2(newaxis[1], newaxis[0]) 514 T.RotateWXYZ(np.rad2deg(angleph + angleth), initaxis) # compensation 515 T.RotateWXYZ(np.rad2deg(angleth), crossvec) 516 T.Translate(p) 517 518 self.SetOrientation(T.GetOrientation()) 519 520 self.point_locator = None 521 self.cell_locator = None 522 return self 523 524 # newaxis = utils.versor(newaxis) 525 # pos = np.array(self.GetPosition()) 526 # crossvec = np.cross(initaxis, newaxis) 527 # angle = np.arccos(np.dot(initaxis, newaxis)) 528 # T = vtk.vtkTransform() 529 # T.PostMultiply() 530 # T.Translate(-pos) 531 # if rotation: 532 # T.RotateWXYZ(rotation, initaxis) 533 # T.RotateWXYZ(np.rad2deg(angle), crossvec) 534 # T.Translate(pos) 535 # self.SetUserTransform(T) 536 # self.transform = T 537 538 539 def scale(self, s=None, reset=False): 540 """ 541 Set/get object's scaling factor. 542 543 Arguments: 544 s : (list, float) 545 scaling factor(s). 546 reset : (bool) 547 if True previous scaling factors are ignored. 548 549 Note: 550 use `s=(sx,sy,sz)` to scale differently in the three coordinates. 551 """ 552 if s is None: 553 return np.array(self.GetScale()) 554 555 # assert s[0] != 0 556 # assert s[1] != 0 557 # assert s[2] != 0 558 559 if reset: 560 self.SetScale(s) 561 else: 562 self.SetScale(np.multiply(self.GetScale(), s)) 563 564 self.point_locator = None 565 self.cell_locator = None 566 return self 567 568 def get_transform(self, invert=False): 569 """ 570 Check if `object.transform` exists and returns a `vtkTransform`. 571 Otherwise return current user transformation (where the object is currently placed). 572 573 Use `invert` to return the inverse of the current transformation 574 575 Example: 576 ```python 577 from vedo import * 578 579 c1 = Cube() 580 c2 = c1.clone().c('violet').alpha(0.5) # copy of c1 581 v = vector(0.2,1,0) 582 p = vector(1,0,0) # axis passes through this point 583 c2.rotate(90, axis=v, point=p) 584 585 # get the inverse of the current transformation 586 T = c2.get_transform(invert=True) 587 c2.apply_transform(T) # put back c2 in place 588 589 l = Line(p-v, p+v).lw(3).c('red') 590 show(c1.wireframe().lw(3), l, c2, axes=1).close() 591 ``` 592  593 """ 594 if self.transform: 595 tr = self.transform 596 if invert: 597 tr = tr.GetInverse() 598 return tr 599 600 T = self.GetMatrix() 601 tr = vtk.vtkTransform() 602 tr.SetMatrix(T) 603 if invert: 604 tr = tr.GetInverse() 605 return tr 606 607 608 @deprecated(reason=vedo.colors.red + "Please use apply_transform()" + vedo.colors.reset) 609 def applyTransform(self, T, reset=False, concatenate=False): 610 """Deprecated. Please use `apply_transform()`""" 611 return self.apply_transform(T,reset,concatenate) 612 613 def apply_transform(self, T, reset=False, concatenate=False): 614 """ 615 Transform object position and orientation. 616 617 Arguments: 618 reset : (bool) 619 no effect, this is superseded by `pointcloud.apply_transform()` 620 concatenate : (bool) 621 no effect, this is superseded by `pointcloud.apply_transform()` 622 """ 623 if isinstance(T, vtk.vtkMatrix4x4): 624 self.SetUserMatrix(T) 625 elif utils.is_sequence(T): 626 vm = vtk.vtkMatrix4x4() 627 for i in [0, 1, 2, 3]: 628 for j in [0, 1, 2, 3]: 629 vm.SetElement(i, j, T[i][j]) 630 self.SetUserMatrix(vm) 631 else: 632 self.SetUserTransform(T) 633 self.transform = T 634 635 self.point_locator = None 636 self.cell_locator = None 637 return self 638 639 def align_to_bounding_box(self, msh, rigid=False): 640 """ 641 Align the current object's bounding box to the bounding box 642 of the input object. 643 644 Use `rigid` to disable scaling. 645 646 Examples: 647 - [align6.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/align6.py) 648 """ 649 lmt = vtk.vtkLandmarkTransform() 650 ss = vtk.vtkPoints() 651 xss0, xss1, yss0, yss1, zss0, zss1 = self.bounds() 652 for p in [ 653 [xss0, yss0, zss0], 654 [xss1, yss0, zss0], 655 [xss1, yss1, zss0], 656 [xss0, yss1, zss0], 657 [xss0, yss0, zss1], 658 [xss1, yss0, zss1], 659 [xss1, yss1, zss1], 660 [xss0, yss1, zss1], 661 ]: 662 ss.InsertNextPoint(p) 663 st = vtk.vtkPoints() 664 xst0, xst1, yst0, yst1, zst0, zst1 = msh.bounds() 665 for p in [ 666 [xst0, yst0, zst0], 667 [xst1, yst0, zst0], 668 [xst1, yst1, zst0], 669 [xst0, yst1, zst0], 670 [xst0, yst0, zst1], 671 [xst1, yst0, zst1], 672 [xst1, yst1, zst1], 673 [xst0, yst1, zst1], 674 ]: 675 st.InsertNextPoint(p) 676 677 lmt.SetSourceLandmarks(ss) 678 lmt.SetTargetLandmarks(st) 679 lmt.SetModeToAffine() 680 if rigid: 681 lmt.SetModeToRigidBody() 682 lmt.Update() 683 self.apply_transform(lmt) 684 self.transform = lmt 685 686 self.point_locator = None 687 self.cell_locator = None 688 return self 689 690 def on(self): 691 """Switch on object visibility. Object is not removed.""" 692 self.VisibilityOn() 693 try: 694 self.scalarbar.VisibilityOn() 695 except AttributeError: 696 pass 697 try: 698 self.trail.VisibilityOn() 699 except AttributeError: 700 pass 701 try: 702 for sh in self.shadows: 703 sh.VisibilityOn() 704 except AttributeError: 705 pass 706 return self 707 708 def off(self): 709 """Switch off object visibility. Object is not removed.""" 710 self.VisibilityOff() 711 try: 712 self.scalarbar.VisibilityOff() 713 except AttributeError: 714 pass 715 try: 716 self.trail.VisibilityOff() 717 except AttributeError: 718 pass 719 try: 720 for sh in self.shadows: 721 sh.VisibilityOff() 722 except AttributeError: 723 pass 724 return self 725 726 def toggle(self): 727 """Toggle object visibility on/off.""" 728 v = self.GetVisibility() 729 if v: 730 self.off() 731 else: 732 self.on() 733 return self 734 735 def box(self, scale=1, padding=0, fill=False): 736 """ 737 Return the bounding box as a new `Mesh`. 738 739 Arguments: 740 scale : (float) 741 box size can be scaled by a factor 742 padding : (float, list) 743 a constant padding can be added (can be a list [padx,pady,padz]) 744 745 Examples: 746 - [latex.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/latex.py) 747 """ 748 b = self.bounds() 749 if not utils.is_sequence(padding): 750 padding = [padding, padding, padding] 751 length, width, height = b[1] - b[0], b[3] - b[2], b[5] - b[4] 752 tol = (length + width + height) / 30000 # useful for boxing 2D text 753 pos = [(b[0] + b[1]) / 2, (b[3] + b[2]) / 2, (b[5] + b[4]) / 2 - tol] 754 bx = vedo.shapes.Box( 755 pos, 756 length * scale + padding[0], 757 width * scale + padding[1], 758 height * scale + padding[2], 759 c="gray", 760 ) 761 if hasattr(self, "GetProperty"): # could be Assembly 762 if isinstance(self.GetProperty(), vtk.vtkProperty): # could be volume 763 pr = vtk.vtkProperty() 764 pr.DeepCopy(self.GetProperty()) 765 bx.SetProperty(pr) 766 bx.property = pr 767 bx.wireframe(not fill) 768 bx.flat().lighting("off") 769 return bx 770 771 def use_bounds(self, ub=True): 772 """ 773 Instruct the current camera to either take into account or ignore 774 the object bounds when resetting. 775 """ 776 self.SetUseBounds(ub) 777 return self 778 779 def bounds(self): 780 """ 781 Get the object bounds. 782 Returns a list in format `[xmin,xmax, ymin,ymax, zmin,zmax]`. 783 """ 784 try: 785 pts = self.points() 786 xmin, ymin, zmin = np.min(pts, axis=0) 787 xmax, ymax, zmax = np.max(pts, axis=0) 788 return [xmin,xmax, ymin,ymax, zmin,zmax] 789 except (AttributeError, ValueError): 790 return self.GetBounds() 791 792 def xbounds(self, i=None): 793 """Get the bounds `[xmin,xmax]`. Can specify upper or lower with i (0,1).""" 794 b = self.bounds() 795 if i is not None: 796 return b[i] 797 return (b[0], b[1]) 798 799 def ybounds(self, i=None): 800 """Get the bounds `[ymin,ymax]`. Can specify upper or lower with i (0,1).""" 801 b = self.bounds() 802 if i == 0: 803 return b[2] 804 if i == 1: 805 return b[3] 806 return (b[2], b[3]) 807 808 def zbounds(self, i=None): 809 """Get the bounds `[zmin,zmax]`. Can specify upper or lower with i (0,1).""" 810 b = self.bounds() 811 if i == 0: return b[4] 812 if i == 1: return b[5] 813 return (b[4], b[5]) 814 815 @deprecated(reason=vedo.colors.red + "Please use diagonal_size()" + vedo.colors.reset) 816 def diagonalSize(self): 817 """Deprecated. Please use `diagonal_size()`.""" 818 return self.diagonal_size() 819 820 def diagonal_size(self): 821 """Get the length of the diagonal of mesh bounding box.""" 822 b = self.bounds() 823 return np.sqrt((b[1] - b[0]) ** 2 + (b[3] - b[2]) ** 2 + (b[5] - b[4]) ** 2) 824 # return self.GetLength() # ???different??? 825 826 827 def copy_data_from(self, obj): 828 """Copy all data (point and cell data) from this input object""" 829 self._data.GetPointData().PassData(obj._data.GetPointData()) 830 self._data.GetCellData().PassData(obj._data.GetCellData()) 831 self.pipeline = utils.OperationNode( 832 f"copy_data_from\n{obj.__class__.__name__}", 833 parents=[self, obj], 834 shape='note', 835 c="#ccc5b9", 836 ) 837 return self 838 839 def print(self): 840 """Print information about an object.""" 841 utils.print_info(self) 842 return self 843 844 def show(self, **options): 845 """ 846 Create on the fly an instance of class `Plotter` or use the last existing one to 847 show one single object. 848 849 This method is meant as a shortcut. If more than one object needs to be visualised 850 please use the syntax `show(mesh1, mesh2, volume, ..., options)`. 851 852 Returns the `Plotter` class instance. 853 """ 854 return vedo.plotter.show(self, **options) 855 856 def thumbnail( 857 self, 858 zoom=1.25, 859 size=(200, 200), 860 bg="white", 861 azimuth=0, 862 elevation=0, 863 axes=False, 864 ): 865 """Build a thumbnail of the object and return it as an array.""" 866 # speed is about 20Hz for size=[200,200] 867 ren = vtk.vtkRenderer() 868 ren.AddActor(self) 869 if axes: 870 axes = vedo.addons.Axes(self) 871 ren.AddActor(axes) 872 ren.ResetCamera() 873 cam = ren.GetActiveCamera() 874 cam.Zoom(zoom) 875 cam.Elevation(elevation) 876 cam.Azimuth(azimuth) 877 878 ren_win = vtk.vtkRenderWindow() 879 ren_win.SetOffScreenRendering(True) 880 ren_win.SetSize(size) 881 ren.SetBackground(colors.get_color(bg)) 882 ren_win.AddRenderer(ren) 883 ren_win.Render() 884 885 nx, ny = ren_win.GetSize() 886 arr = vtk.vtkUnsignedCharArray() 887 ren_win.GetRGBACharPixelData(0, 0, nx-1, ny-1, 0, arr) 888 narr = utils.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3]) 889 narr = np.ascontiguousarray(np.flip(narr, axis=0)) 890 891 ren.RemoveActor(self) 892 if axes: 893 ren.RemoveActor(axes) 894 ren_win.Finalize() 895 del ren_win 896 return narr 897 898 899######################################################################################## 900class BaseActor2D(vtk.vtkActor2D): 901 """ 902 Base class. 903 904 .. warning:: Do not use this class to instanciate objects. 905 """ 906 def __init__(self): 907 """Manage 2D objects.""" 908 super().__init__() 909 self._mapper = None 910 self.property = self.GetProperty() 911 self.filename = "" 912 913 def layer(self, value=None): 914 """Set/Get the layer number in the overlay planes into which to render.""" 915 if value is None: 916 return self.GetLayerNumber() 917 self.SetLayerNumber(value) 918 return self 919 920 def pos(self, px=None, py=None): 921 """Set/Get the screen-coordinate position.""" 922 if isinstance(px, str): 923 vedo.logger.error("Use string descriptors only inside the contructor") 924 return self 925 if px is None: 926 return np.array(self.GetPosition(), dtype=int) 927 if py is not None: 928 p = [px,py] 929 else: 930 p = px 931 assert len(p) == 2, "Error: len(pos) must be 2 for BaseActor2D" 932 self.SetPosition(p) 933 return self 934 935 def coordinate_system(self, value=None): 936 """ 937 Set/get the coordinate system which this coordinate is defined in. 938 939 The options are: 940 0. Display 941 1. Normalized Display 942 2. Viewport 943 3. Normalized Viewport 944 4. View 945 5. Pose 946 6. World 947 """ 948 coor = self.GetPositionCoordinate() 949 if value is None: 950 return coor.GetCoordinateSystem() 951 coor.SetCoordinateSystem(value) 952 return self 953 954 def on(self): 955 """Set object visibility.""" 956 self.VisibilityOn() 957 return self 958 959 def off(self): 960 """Set object visibility.""" 961 self.VisibilityOn() 962 return self 963 964 def toggle(self): 965 """Toggle object visibility.""" 966 self.SetVisibility(not(self.GetVisibility())) 967 return self 968 969 def pickable(self, value=True): 970 self.SetPickable(value) 971 return self 972 973 def alpha(self, value=None): 974 """Set/Get the object opacity.""" 975 if value is None: 976 return self.GetProperty().GetOpacity() 977 self.GetProperty().SetOpacity(value) 978 return self 979 980 def ps(self, point_size=None): 981 if point_size is None: 982 return self.GetProperty().GetPointSize() 983 self.GetProperty().SetPointSize(point_size) 984 return self 985 986 def ontop(self, value=True): 987 """Keep the object always on top of everithing else.""" 988 if value: 989 self.GetProperty().SetDisplayLocationToForeground() 990 else: 991 self.GetProperty().SetDisplayLocationToBackground() 992 return self 993 994 995######################################################################################## 996class BaseActor(Base3DProp): 997 """ 998 Base class. 999 1000 .. warning:: Do not use this class to instanciate objects, use one the above instead. 1001 """ 1002 def __init__(self): 1003 """ 1004 Base class to add operative and data 1005 functionality to `Mesh`, `Assembly`, `Volume` and `Picture` objects. 1006 """ 1007 1008 super().__init__() 1009 1010 self._mapper = None 1011 self._caption = None 1012 self.property = None 1013 1014 1015 def mapper(self, new_mapper=None): 1016 """Return the `vtkMapper` data object, or update it with a new one.""" 1017 if new_mapper: 1018 self.SetMapper(new_mapper) 1019 if self._mapper: 1020 iptdata = self._mapper.GetInput() 1021 if iptdata: 1022 new_mapper.SetInputData(self._mapper.GetInput()) 1023 self._mapper = new_mapper 1024 self._mapper.Modified() 1025 return self._mapper 1026 1027 def inputdata(self): 1028 """Return the VTK input data object.""" 1029 if self._mapper: 1030 return self._mapper.GetInput() 1031 return self.GetMapper().GetInput() 1032 1033 def modified(self): 1034 """Use in conjunction with `tonumpy()` 1035 to update any modifications to the volume array""" 1036 sc = self.inputdata().GetPointData().GetScalars() 1037 if sc: 1038 sc.Modified() 1039 self.inputdata().GetPointData().Modified() 1040 return self 1041 1042 @deprecated(reason=vedo.colors.red + "Please use property object.npoints" + vedo.colors.reset) 1043 def N(self): 1044 """Deprecated. Please use property object.npoints""" 1045 return self.inputdata().GetNumberOfPoints() 1046 1047 @deprecated(reason=vedo.colors.red + "Please use property object.npoints" + vedo.colors.reset) 1048 def NPoints(self): 1049 """Deprecated. Please use property object.npoints""" 1050 return self.inputdata().GetNumberOfPoints() 1051 1052 @deprecated(reason=vedo.colors.red + "Please use property object.ncells" + vedo.colors.reset) 1053 def NCells(self): 1054 """Deprecated. Please use property object.ncells""" 1055 return self.inputdata().GetNumberOfCells() 1056 1057 @property 1058 def npoints(self): 1059 """Retrieve the number of points.""" 1060 return self.inputdata().GetNumberOfPoints() 1061 1062 @property 1063 def ncells(self): 1064 """Retrieve the number of cells.""" 1065 return self.inputdata().GetNumberOfCells() 1066 1067 1068 def points(self, pts=None, transformed=True): 1069 """ 1070 Set/Get the vertex coordinates of a mesh or point cloud. 1071 Argument can be an index, a set of indices 1072 or a complete new set of points to update the mesh. 1073 1074 Set `transformed=False` to ignore any previous transformation applied to the mesh. 1075 """ 1076 if pts is None: ### getter 1077 1078 if isinstance(self, vedo.Points): 1079 vpts = self.polydata(transformed).GetPoints() 1080 elif isinstance(self, vedo.BaseVolume): 1081 v2p = vtk.vtkImageToPoints() 1082 v2p.SetInputData(self.imagedata()) 1083 v2p.Update() 1084 vpts = v2p.GetOutput().GetPoints() 1085 else: # tetmesh et al 1086 vpts = self.inputdata().GetPoints() 1087 1088 if vpts: 1089 return utils.vtk2numpy(vpts.GetData()) 1090 return np.array([], dtype=np.float32) 1091 1092 else: ### setter 1093 1094 if len(pts) == 3 and len(pts[0]) != 3: 1095 # assume plist is in the format [all_x, all_y, all_z] 1096 pts = np.stack((pts[0], pts[1], pts[2]), axis=1) 1097 pts = np.asarray(pts, dtype=np.float32) 1098 if pts.shape[1] == 2: 1099 pts = np.c_[pts, np.zeros(pts.shape[0], dtype=np.float32)] 1100 vpts = self.inputdata().GetPoints() 1101 arr = utils.numpy2vtk(pts, dtype=np.float32) 1102 vpts.SetData(arr) 1103 vpts.Modified() 1104 # reset mesh to identity matrix position/rotation: 1105 self.PokeMatrix(vtk.vtkMatrix4x4()) 1106 self.point_locator = None 1107 self.cell_locator = None 1108 return self 1109 1110 @deprecated(reason=vedo.colors.red + "Please use cell_centers()" + vedo.colors.reset) 1111 def cellCenters(self): 1112 """Deprecated. Please use `cell_centers()`""" 1113 return self.cell_centers() 1114 1115 def cell_centers(self): 1116 """ 1117 Get the coordinates of the cell centers. 1118 1119 Examples: 1120 - [delaunay2d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/delaunay2d.py) 1121 """ 1122 vcen = vtk.vtkCellCenters() 1123 if hasattr(self, "polydata"): 1124 vcen.SetInputData(self.polydata()) 1125 else: 1126 vcen.SetInputData(self.inputdata()) 1127 vcen.Update() 1128 return utils.vtk2numpy(vcen.GetOutput().GetPoints().GetData()) 1129 1130 def delete_cells(self, ids): 1131 """ 1132 Remove cells from the mesh object by their ID. 1133 Points (vertices) are not removed (you may use `.clean()` to remove those). 1134 """ 1135 data = self.inputdata() 1136 data.BuildLinks() 1137 for cid in ids: 1138 data.DeleteCell(cid) 1139 data.RemoveDeletedCells() 1140 data.Modified() 1141 self._mapper.Modified() 1142 self.pipeline = utils.OperationNode( 1143 "delete_cells", parents=[self], 1144 comment=f"#cells {self._data.GetNumberOfCells()}", 1145 ) 1146 return self 1147 1148 def mark_boundaries(self): 1149 """Mark cells and vertices of the mesh if they lie on a boundary.""" 1150 mb = vtk.vtkMarkBoundaryFilter() 1151 mb.SetInputData(self._data) 1152 mb.Update() 1153 out = self._update(mb.GetOutput()) 1154 out.pipeline = utils.OperationNode("mark_boundaries", parents=[self]) 1155 return out 1156 1157 def find_cells_in(self, xbounds=(), ybounds=(), zbounds=()): 1158 """ 1159 Find cells that are within the specified bounds. 1160 Setting a color will add a vtk array to colorize these cells. 1161 """ 1162 if len(xbounds) == 6: 1163 bnds = xbounds 1164 else: 1165 bnds = list(self.bounds()) 1166 if len(xbounds) == 2: 1167 bnds[0] = xbounds[0] 1168 bnds[1] = xbounds[1] 1169 if len(ybounds) == 2: 1170 bnds[2] = ybounds[0] 1171 bnds[3] = ybounds[1] 1172 if len(zbounds) == 2: 1173 bnds[4] = zbounds[0] 1174 bnds[5] = zbounds[1] 1175 1176 cellIds = vtk.vtkIdList() 1177 self.cell_locator = vtk.vtkCellTreeLocator() 1178 self.cell_locator.SetDataSet(self.polydata()) 1179 self.cell_locator.BuildLocator() 1180 self.cell_locator.FindCellsWithinBounds(bnds, cellIds) 1181 1182 cids = [] 1183 for i in range(cellIds.GetNumberOfIds()): 1184 cid = cellIds.GetId(i) 1185 cids.append(cid) 1186 1187 return np.array(cids) 1188 1189 def count_vertices(self): 1190 """Count the number of vertices each cell has and return it as a numpy array""" 1191 vc = vtk.vtkCountVertices() 1192 vc.SetInputData(self._data) 1193 vc.SetOutputArrayName("VertexCount") 1194 vc.Update() 1195 varr = vc.GetOutput().GetCellData().GetArray("VertexCount") 1196 return utils.vtk2numpy(varr) 1197 1198 def lighting( 1199 self, 1200 style="", 1201 ambient=None, 1202 diffuse=None, 1203 specular=None, 1204 specular_power=None, 1205 specular_color=None, 1206 metallicity=None, 1207 roughness=None, 1208 ): 1209 """ 1210 Set the ambient, diffuse, specular and specular_power lighting constants. 1211 1212 Arguments: 1213 style : (str) 1214 preset style, options are `[metallic, plastic, shiny, glossy, ambient, off]` 1215 ambient : (float) 1216 ambient fraction of emission [0-1] 1217 diffuse : (float) 1218 emission of diffused light in fraction [0-1] 1219 specular : (float) 1220 fraction of reflected light [0-1] 1221 specular_power : (float) 1222 precision of reflection [1-100] 1223 specular_color : (color) 1224 color that is being reflected by the surface 1225 1226 <img src="https://upload.wikimedia.org/wikipedia/commons/6/6b/Phong_components_version_4.png" alt="", width=700px> 1227 1228 Examples: 1229 - [specular.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/specular.py) 1230 """ 1231 pr = self.GetProperty() 1232 1233 if style: 1234 1235 if isinstance(pr, vtk.vtkVolumeProperty): 1236 self.shade(True) 1237 if style == "off": 1238 self.shade(False) 1239 elif style == "ambient": 1240 style = "default" 1241 self.shade(False) 1242 else: 1243 if style != "off": 1244 pr.LightingOn() 1245 1246 if style == "off": 1247 pr.SetInterpolationToFlat() 1248 pr.LightingOff() 1249 return self ############## 1250 1251 if hasattr(pr, "GetColor"): # could be Volume 1252 c = pr.GetColor() 1253 else: 1254 c = (1, 1, 0.99) 1255 mpr = self._mapper 1256 if hasattr(mpr, 'GetScalarVisibility') and mpr.GetScalarVisibility(): 1257 c = (1,1,0.99) 1258 if style=='metallic': pars = [0.1, 0.3, 1.0, 10, c] 1259 elif style=='plastic' : pars = [0.3, 0.4, 0.3, 5, c] 1260 elif style=='shiny' : pars = [0.2, 0.6, 0.8, 50, c] 1261 elif style=='glossy' : pars = [0.1, 0.7, 0.9, 90, (1,1,0.99)] 1262 elif style=='ambient' : pars = [0.8, 0.1, 0.0, 1, (1,1,1)] 1263 elif style=='default' : pars = [0.1, 1.0, 0.05, 5, c] 1264 else: 1265 vedo.logger.error("in lighting(): Available styles are") 1266 vedo.logger.error( 1267 "[default, metallic, plastic, shiny, glossy, ambient, off]" 1268 ) 1269 raise RuntimeError() 1270 pr.SetAmbient(pars[0]) 1271 pr.SetDiffuse(pars[1]) 1272 pr.SetSpecular(pars[2]) 1273 pr.SetSpecularPower(pars[3]) 1274 if hasattr(pr, "GetColor"): 1275 pr.SetSpecularColor(pars[4]) 1276 1277 if ambient is not None: pr.SetAmbient(ambient) 1278 if diffuse is not None: pr.SetDiffuse(diffuse) 1279 if specular is not None: pr.SetSpecular(specular) 1280 if specular_power is not None: pr.SetSpecularPower(specular_power) 1281 if specular_color is not None: pr.SetSpecularColor(colors.get_color(specular_color)) 1282 if utils.vtk_version_at_least(9): 1283 if metallicity is not None: 1284 pr.SetInterpolationToPBR() 1285 pr.SetMetallic(metallicity) 1286 if roughness is not None: 1287 pr.SetInterpolationToPBR() 1288 pr.SetRoughness(roughness) 1289 1290 return self 1291 1292 def print_histogram( 1293 self, 1294 bins=10, 1295 height=10, 1296 logscale=False, 1297 minbin=0, 1298 horizontal=False, 1299 char="\U00002589", 1300 c=None, 1301 bold=True, 1302 title="Histogram", 1303 ): 1304 """ 1305 Ascii histogram printing on terminal. 1306 Input can be `Volume` or `Mesh` (will grab the active point array). 1307 1308 Arguments: 1309 bins : (int) 1310 number of histogram bins 1311 height : (int) 1312 height of the histogram in character units 1313 logscale : (bool) 1314 use logscale for frequencies 1315 minbin : (int) 1316 ignore bins before minbin 1317 horizontal : (bool) 1318 show histogram horizontally 1319 char : (str) 1320 character to be used as marker 1321 c : (color) 1322 ascii color 1323 bold : (bool) 1324 use boldface 1325 title : (str) 1326 histogram title 1327 1328  1329 """ 1330 utils.print_histogram( 1331 self, bins, height, logscale, minbin, horizontal, char, c, bold, title 1332 ) 1333 return self 1334 1335 def c(self, color=False, alpha=None): 1336 """ 1337 Shortcut for `color()`. 1338 If None is passed as input, will use colors from current active scalars. 1339 """ 1340 return self.color(color, alpha) 1341 1342 @property 1343 def pointdata(self): 1344 """ 1345 Create and/or return a `numpy.array` associated to points (vertices). 1346 A data array can be indexed either as a string or by an integer number. 1347 E.g.: `myobj.pointdata["arrayname"]` 1348 1349 Usage: 1350 1351 `myobj.pointdata.keys()` to return the available data array names 1352 1353 `myobj.pointdata.select(name)` to make this array the active one 1354 1355 `myobj.pointdata.remove(name)` to remove this array 1356 """ 1357 return _DataArrayHelper(self, 0) 1358 1359 @property 1360 def celldata(self): 1361 """ 1362 Create and/or return a `numpy.array` associated to cells (faces). 1363 A data array can be indexed either as a string or by an integer number. 1364 E.g.: `myobj.celldata["arrayname"]` 1365 1366 Usage: 1367 1368 `myobj.celldata.keys()` to return the available data array names 1369 1370 `myobj.celldata.select(name)` to make this array the active one 1371 1372 `myobj.celldata.remove(name)` to remove this array 1373 """ 1374 return _DataArrayHelper(self, 1) 1375 1376 @property 1377 def metadata(self): 1378 """ 1379 Create and/or return a `numpy.array` associated to neither cells nor faces. 1380 A data array can be indexed either as a string or by an integer number. 1381 E.g.: `myobj.metadata["arrayname"]` 1382 1383 Usage: 1384 1385 `myobj.metadata.keys()` to return the available data array names 1386 1387 `myobj.metadata.select(name)` to make this array the active one 1388 1389 `myobj.metadata.remove(name)` to remove this array 1390 """ 1391 return _DataArrayHelper(self, 2) 1392 1393 1394 def map_cells_to_points(self, arrays=(), move=False): 1395 """ 1396 Interpolate cell data (i.e., data specified per cell or face) 1397 into point data (i.e., data specified at each vertex). 1398 The method of transformation is based on averaging the data values 1399 of all cells using a particular point. 1400 1401 A custom list of arrays to be mapped can be passed in input. 1402 1403 Set `move=True` to delete the original `celldata` array. 1404 """ 1405 c2p = vtk.vtkCellDataToPointData() 1406 c2p.SetInputData(self.inputdata()) 1407 if not move: 1408 c2p.PassCellDataOn() 1409 if arrays: 1410 c2p.ClearCellDataArrays() 1411 c2p.ProcessAllArraysOff() 1412 for arr in arrays: 1413 c2p.AddCellDataArray(arr) 1414 else: 1415 c2p.ProcessAllArraysOn() 1416 c2p.Update() 1417 self._mapper.SetScalarModeToUsePointData() 1418 out = self._update(c2p.GetOutput()) 1419 out.pipeline = utils.OperationNode("map cell\nto point data", parents=[self]) 1420 return out 1421 1422 def map_points_to_cells(self, arrays=(), move=False): 1423 """ 1424 Interpolate point data (i.e., data specified per point or vertex) 1425 into cell data (i.e., data specified per cell). 1426 The method of transformation is based on averaging the data values 1427 of all points defining a particular cell. 1428 1429 A custom list of arrays to be mapped can be passed in input. 1430 1431 Set `move=True` to delete the original `pointdata` array. 1432 1433 Examples: 1434 - [mesh_map2cell.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_map2cell.py) 1435 """ 1436 p2c = vtk.vtkPointDataToCellData() 1437 p2c.SetInputData(self.inputdata()) 1438 if not move: 1439 p2c.PassPointDataOn() 1440 if arrays: 1441 p2c.ClearPointDataArrays() 1442 p2c.ProcessAllArraysOff() 1443 for arr in arrays: 1444 p2c.AddPointDataArray(arr) 1445 else: 1446 p2c.ProcessAllArraysOn() 1447 p2c.Update() 1448 self._mapper.SetScalarModeToUseCellData() 1449 out = self._update(p2c.GetOutput()) 1450 out.pipeline = utils.OperationNode("map point\nto cell data", parents=[self]) 1451 return out 1452 1453 def resample_data_from(self, source, tol=None, categorical=False): 1454 """ 1455 Resample point and cell data from another dataset. 1456 The output has the same structure but its point data have 1457 the resampled values from target. 1458 1459 Use `tol` to set the tolerance used to compute whether 1460 a point in the source is in a cell of the current object. 1461 Points without resampled values, and their cells, are marked as blank. 1462 If the data is categorical, then the resulting data will be determined 1463 by a nearest neighbor interpolation scheme. 1464 1465 Example: 1466 ```python 1467 from vedo import * 1468 m1 = Mesh(dataurl+'bunny.obj')#.add_gaussian_noise(0.1) 1469 pts = m1.points() 1470 ces = m1.cell_centers() 1471 m1.pointdata["xvalues"] = np.power(pts[:,0], 3) 1472 m1.celldata["yvalues"] = np.power(ces[:,1], 3) 1473 m2 = Mesh(dataurl+'bunny.obj') 1474 m2.resample_arrays_from(m1) 1475 # print(m2.pointdata["xvalues"]) 1476 show(m1, m2 , N=2, axes=1) 1477 ``` 1478 """ 1479 rs = vtk.vtkResampleWithDataSet() 1480 rs.SetInputData(self.inputdata()) 1481 rs.SetSourceData(source.inputdata()) 1482 1483 rs.SetPassPointArrays(True) 1484 rs.SetPassCellArrays(True) 1485 rs.SetPassFieldArrays(True) 1486 rs.SetCategoricalData(categorical) 1487 1488 rs.SetComputeTolerance(True) 1489 if tol: 1490 rs.SetComputeTolerance(False) 1491 rs.SetTolerance(tol) 1492 rs.Update() 1493 self._update(rs.GetOutput()) 1494 self.pipeline = utils.OperationNode( 1495 f"resample_data_from\n{source.__class__.__name__}", 1496 parents=[self, source]) 1497 return self 1498 1499 def add_ids(self): 1500 """Generate point and cell ids arrays.""" 1501 ids = vtk.vtkIdFilter() 1502 ids.SetInputData(self.inputdata()) 1503 ids.PointIdsOn() 1504 ids.CellIdsOn() 1505 ids.FieldDataOff() 1506 ids.SetPointIdsArrayName("PointID") 1507 ids.SetCellIdsArrayName("CellID") 1508 ids.Update() 1509 self._update(ids.GetOutput()) 1510 self.pipeline = utils.OperationNode("add_ids", parents=[self]) 1511 return self 1512 1513 def gradient(self, input_array=None, on="points", fast=False): 1514 """ 1515 Compute and return the gradiend of the active scalar field as a numpy array. 1516 1517 Arguments: 1518 input_array : (str) 1519 array of the scalars to compute the gradient, 1520 if None the current active array is selected 1521 on : (str) 1522 compute either on 'points' or 'cells' data 1523 fast : (bool) 1524 if True, will use a less accurate algorithm 1525 that performs fewer derivative calculations (and is therefore faster). 1526 1527 Examples: 1528 - [isolines.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/isolines.py) 1529 1530  1531 """ 1532 gra = vtk.vtkGradientFilter() 1533 if on.startswith("p"): 1534 varr = self.inputdata().GetPointData() 1535 tp = vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS 1536 else: 1537 varr = self.inputdata().GetCellData() 1538 tp = vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS 1539 1540 if input_array is None: 1541 if varr.GetScalars(): 1542 input_array = varr.GetScalars().GetName() 1543 else: 1544 vedo.logger.error(f"in gradient: no scalars found for {on}") 1545 raise RuntimeError 1546 1547 gra.SetInputData(self.inputdata()) 1548 gra.SetInputScalars(tp, input_array) 1549 gra.SetResultArrayName("Gradient") 1550 gra.SetFasterApproximation(fast) 1551 gra.ComputeDivergenceOff() 1552 gra.ComputeVorticityOff() 1553 gra.ComputeGradientOn() 1554 gra.Update() 1555 if on.startswith("p"): 1556 gvecs = utils.vtk2numpy(gra.GetOutput().GetPointData().GetArray("Gradient")) 1557 else: 1558 gvecs = utils.vtk2numpy(gra.GetOutput().GetCellData().GetArray("Gradient")) 1559 return gvecs 1560 1561 def divergence(self, array_name=None, on="points", fast=False): 1562 """ 1563 Compute and return the divergence of a vector field as a numpy array. 1564 1565 Arguments: 1566 array_name : (str) 1567 name of the array of vectors to compute the divergence, 1568 if None the current active array is selected 1569 on : (str) 1570 compute either on 'points' or 'cells' data 1571 fast : (bool) 1572 if True, will use a less accurate algorithm 1573 that performs fewer derivative calculations (and is therefore faster). 1574 """ 1575 div = vtk.vtkGradientFilter() 1576 if on.startswith("p"): 1577 varr = self.inputdata().GetPointData() 1578 tp = vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS 1579 else: 1580 varr = self.inputdata().GetCellData() 1581 tp = vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS 1582 1583 if array_name is None: 1584 if varr.GetVectors(): 1585 array_name = varr.GetVectors().GetName() 1586 else: 1587 vedo.logger.error(f"in divergence(): no vectors found for {on}") 1588 raise RuntimeError 1589 1590 div.SetInputData(self.inputdata()) 1591 div.SetInputScalars(tp, array_name) 1592 div.ComputeDivergenceOn() 1593 div.ComputeGradientOff() 1594 div.ComputeVorticityOff() 1595 div.SetDivergenceArrayName("Divergence") 1596 div.SetFasterApproximation(fast) 1597 div.Update() 1598 if on.startswith('p'): 1599 dvecs = utils.vtk2numpy(div.GetOutput().GetPointData().GetArray('Divergence')) 1600 else: 1601 dvecs = utils.vtk2numpy(div.GetOutput().GetCellData().GetArray('Divergence')) 1602 return dvecs 1603 1604 def vorticity(self, array_name=None, on="points", fast=False): 1605 """ 1606 Compute and return the vorticity of a vector field as a numpy array. 1607 1608 Arguments: 1609 array_name : (str) 1610 name of the array to compute the vorticity, 1611 if None the current active array is selected 1612 on : (str) 1613 compute either on 'points' or 'cells' data 1614 fast : (bool) 1615 if True, will use a less accurate algorithm 1616 that performs fewer derivative calculations (and is therefore faster). 1617 """ 1618 vort = vtk.vtkGradientFilter() 1619 if on.startswith("p"): 1620 varr = self.inputdata().GetPointData() 1621 tp = vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS 1622 else: 1623 varr = self.inputdata().GetCellData() 1624 tp = vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS 1625 1626 if array_name is None: 1627 if varr.GetVectors(): 1628 array_name = varr.GetVectors().GetName() 1629 else: 1630 vedo.logger.error(f"in vorticity(): no vectors found for {on}") 1631 raise RuntimeError 1632 1633 vort.SetInputData(self.inputdata()) 1634 vort.SetInputScalars(tp, array_name) 1635 vort.ComputeDivergenceOff() 1636 vort.ComputeGradientOff() 1637 vort.ComputeVorticityOn() 1638 vort.SetVorticityArrayName("Vorticity") 1639 vort.SetFasterApproximation(fast) 1640 vort.Update() 1641 if on.startswith('p'): 1642 vvecs = utils.vtk2numpy(vort.GetOutput().GetPointData().GetArray('Vorticity')) 1643 else: 1644 vvecs = utils.vtk2numpy(vort.GetOutput().GetCellData().GetArray('Vorticity')) 1645 return vvecs 1646 1647 1648 @deprecated(reason=vedo.colors.red + "Please use method add_scalarbar()" + vedo.colors.reset) 1649 def addScalarBar(self, *a, **b): 1650 """Deprecated. Please use method `add_scalarbar()`""" 1651 return self.add_scalarbar(*a, **b) 1652 1653 def add_scalarbar( 1654 self, 1655 title="", 1656 pos=(0.8,0.05), 1657 title_yoffset=15, 1658 font_size=12, 1659 size=(None,None), 1660 nlabels=None, 1661 c=None, 1662 horizontal=False, 1663 use_alpha=True, 1664 label_format=':6.3g', 1665 ): 1666 """ 1667 Add a 2D scalar bar for the specified obj. 1668 1669 Examples: 1670 - [mesh_coloring.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_coloring.py) 1671 - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py) 1672 """ 1673 plt = vedo.plotter_instance 1674 1675 if plt and plt.renderer: 1676 c = (0.9, 0.9, 0.9) 1677 if np.sum(plt.renderer.GetBackground()) > 1.5: 1678 c = (0.1, 0.1, 0.1) 1679 if isinstance(self.scalarbar, vtk.vtkActor): 1680 plt.renderer.RemoveActor(self.scalarbar) 1681 elif isinstance(self.scalarbar, vedo.Assembly): 1682 for a in self.scalarbar.unpack(): 1683 plt.renderer.RemoveActor(a) 1684 if c is None: 1685 c = "gray" 1686 1687 sb = vedo.addons.ScalarBar( 1688 self, 1689 title, 1690 pos, 1691 title_yoffset, 1692 font_size, 1693 size, 1694 nlabels, 1695 c, 1696 horizontal, 1697 use_alpha, 1698 label_format, 1699 ) 1700 self.scalarbar = sb 1701 return self 1702 1703 @deprecated(reason=vedo.colors.red + "Please use method add_scalarbar3d()" + vedo.colors.reset) 1704 def addScalarBar3D(self, *a, **b): 1705 """Deprecated. Please use method `add_scalarbar3d()`""" 1706 return self.add_scalarbar3d(*a, **b) 1707 1708 def add_scalarbar3d( 1709 self, 1710 title="", 1711 pos=None, 1712 s=(None, None), 1713 title_font="", 1714 title_xoffset=-1.5, 1715 title_yoffset=0.0, 1716 title_size=1.5, 1717 title_rotation=0.0, 1718 nlabels=9, 1719 label_font="", 1720 label_size=1, 1721 label_offset=0.375, 1722 label_rotation=0, 1723 label_format="", 1724 italic=0, 1725 c=None, 1726 draw_box=True, 1727 above_text=None, 1728 below_text=None, 1729 nan_text="NaN", 1730 categories=None, 1731 ): 1732 """ 1733 Associate a 3D scalar bar to the object and add it to the scene. 1734 The new scalarbar object (Assembly) will be accessible as obj.scalarbar 1735 1736 Arguments: 1737 s : (list) 1738 (thickness, length) of scalarbar 1739 title : (str) 1740 scalar bar title 1741 title_xoffset : (float) 1742 horizontal space btw title and color scalarbar 1743 title_yoffset : (float) 1744 vertical space offset 1745 title_size : (float) 1746 size of title wrt numeric labels 1747 title_rotation : (float) 1748 title rotation in degrees 1749 nlabels : (int) 1750 number of numeric labels 1751 label_font : (str) 1752 font type for labels 1753 label_size : (float) 1754 label scale factor 1755 label_offset : (float) 1756 space btw numeric labels and scale 1757 label_rotation : (float) 1758 label rotation in degrees 1759 label_format : (str) 1760 label format for floats and integers (e.g. `':.2f'`) 1761 draw_box : (bool) 1762 draw a box around the colorbar 1763 categories : (list) 1764 make a categorical scalarbar, 1765 the input list will have the format `[value, color, alpha, textlabel]` 1766 1767 Examples: 1768 - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py) 1769 """ 1770 plt = vedo.plotter_instance 1771 if plt and c is None: # automatic black or white 1772 c = (0.9, 0.9, 0.9) 1773 if np.sum(vedo.get_color(plt.backgrcol)) > 1.5: 1774 c = (0.1, 0.1, 0.1) 1775 if c is None: 1776 c = (0, 0, 0) 1777 c = vedo.get_color(c) 1778 1779 self.scalarbar = vedo.addons.ScalarBar3D( 1780 self, 1781 title, 1782 pos, 1783 s, 1784 title_font, 1785 title_xoffset, 1786 title_yoffset, 1787 title_size, 1788 title_rotation, 1789 nlabels, 1790 label_font, 1791 label_size, 1792 label_offset, 1793 label_rotation, 1794 label_format, 1795 italic, 1796 c, 1797 draw_box, 1798 above_text, 1799 below_text, 1800 nan_text, 1801 categories, 1802 ) 1803 return self 1804 1805 ################################################################################### 1806 def write(self, filename, binary=True): 1807 """Write object to file.""" 1808 out = vedo.io.write(self, filename, binary) 1809 out.pipeline = utils.OperationNode( 1810 "write", parents=[self], 1811 comment=filename[:15], 1812 shape="folder", 1813 c="#8a817c", 1814 ) 1815 return out 1816 1817 1818######################################################################################## 1819class BaseGrid(BaseActor): 1820 """ 1821 Base class for grid datasets. 1822 1823 .. warning:: Do not use this class to instanciate objects. 1824 """ 1825 def __init__(self): 1826 """Base class for grid datasets.""" 1827 1828 super().__init__() 1829 1830 self._data = None 1831 self.useCells = True 1832 self._color = None 1833 self._alpha = [0,1] 1834 1835 # ----------------------------------------------------------- 1836 1837 def _update(self, data): 1838 self._data = data 1839 self._mapper.SetInputData(self.tomesh().polydata()) 1840 self._mapper.Modified() 1841 return self 1842 1843 def tomesh(self, fill=True, shrink=1.0): 1844 """ 1845 Build a polygonal Mesh from the current Grid object. 1846 1847 If `fill=True`, the interior faces of all the cells are created. 1848 (setting a `shrink` value slightly smaller than the default 1.0 1849 can avoid flickering due to internal adjacent faces). 1850 1851 If `fill=False`, only the boundary faces will be generated. 1852 """ 1853 gf = vtk.vtkGeometryFilter() 1854 if fill: 1855 sf = vtk.vtkShrinkFilter() 1856 sf.SetInputData(self._data) 1857 sf.SetShrinkFactor(shrink) 1858 sf.Update() 1859 gf.SetInputData(sf.GetOutput()) 1860 gf.Update() 1861 poly = gf.GetOutput() 1862 if shrink == 1.0: 1863 cleanPolyData = vtk.vtkCleanPolyData() 1864 cleanPolyData.PointMergingOn() 1865 cleanPolyData.ConvertLinesToPointsOn() 1866 cleanPolyData.ConvertPolysToLinesOn() 1867 cleanPolyData.ConvertStripsToPolysOn() 1868 cleanPolyData.SetInputData(poly) 1869 cleanPolyData.Update() 1870 poly = cleanPolyData.GetOutput() 1871 else: 1872 gf.SetInputData(self._data) 1873 gf.Update() 1874 poly = gf.GetOutput() 1875 1876 msh = vedo.mesh.Mesh(poly).flat() 1877 msh.scalarbar = self.scalarbar 1878 lut = utils.ctf2lut(self) 1879 if lut: 1880 msh.mapper().SetLookupTable(lut) 1881 if self.useCells: 1882 msh.mapper().SetScalarModeToUseCellData() 1883 else: 1884 msh.mapper().SetScalarModeToUsePointData() 1885 1886 msh.pipeline = utils.OperationNode( 1887 "tomesh", parents=[self], 1888 comment=f"fill={fill}", 1889 c="#9e2a2b:#e9c46a", 1890 ) 1891 return msh 1892 1893 def cells(self): 1894 """ 1895 Get the cells connectivity ids as a numpy array. 1896 1897 The output format is: `[[id0 ... idn], [id0 ... idm], etc]`. 1898 """ 1899 arr1d = utils.vtk2numpy(self._data.GetCells().GetData()) 1900 if arr1d is None: 1901 return [] 1902 1903 # Get cell connettivity ids as a 1D array. vtk format is: 1904 # [nids1, id0 ... idn, niids2, id0 ... idm, etc]. 1905 i = 0 1906 conn = [] 1907 n = len(arr1d) 1908 if n: 1909 while True: 1910 cell = [arr1d[i + k] for k in range(1, arr1d[i] + 1)] 1911 conn.append(cell) 1912 i += arr1d[i] + 1 1913 if i >= n: 1914 break 1915 return conn 1916 1917 def color(self, col, alpha=None, vmin=None, vmax=None): 1918 """ 1919 Assign a color or a set of colors along the range of the scalar value. 1920 A single constant color can also be assigned. 1921 Any matplotlib color map name is also accepted, e.g. `volume.color('jet')`. 1922 1923 E.g.: say that your cells scalar runs from -3 to 6, 1924 and you want -3 to show red and 1.5 violet and 6 green, then just set: 1925 1926 `volume.color(['red', 'violet', 'green'])` 1927 1928 You can also assign a specific color to a aspecific value with eg.: 1929 1930 `volume.color([(0,'red', (0.5,'violet'), (1,'green')])` 1931 1932 Arguments: 1933 alpha : (list) 1934 use a list to specify transparencies along the scalar range 1935 vmin : (float) 1936 force the min of the scalar range to be this value 1937 vmax : (float) 1938 force the max of the scalar range to be this value 1939 """ 1940 # supersedes method in Points, Mesh 1941 if vmin is None: 1942 vmin, _ = self._data.GetScalarRange() 1943 if vmax is None: 1944 _, vmax = self._data.GetScalarRange() 1945 ctf = self.GetProperty().GetRGBTransferFunction() 1946 ctf.RemoveAllPoints() 1947 self._color = col 1948 1949 if utils.is_sequence(col): 1950 if utils.is_sequence(col[0]) and len(col[0]) == 2: 1951 # user passing [(value1, color1), ...] 1952 for x, ci in col: 1953 r, g, b = colors.get_color(ci) 1954 ctf.AddRGBPoint(x, r, g, b) 1955 # colors.printc('color at', round(x, 1), 1956 # 'set to', colors.get_color_name((r, g, b)), 1957 # c='w', bold=0) 1958 else: 1959 # user passing [color1, color2, ..] 1960 for i, ci in enumerate(col): 1961 r, g, b = colors.get_color(ci) 1962 x = vmin + (vmax - vmin) * i / (len(col) - 1) 1963 ctf.AddRGBPoint(x, r, g, b) 1964 elif isinstance(col, str): 1965 if col in colors.colors.keys() or col in colors.color_nicks.keys(): 1966 r, g, b = colors.get_color(col) 1967 ctf.AddRGBPoint(vmin, r, g, b) # constant color 1968 ctf.AddRGBPoint(vmax, r, g, b) 1969 else: # assume it's a colormap 1970 for x in np.linspace(vmin, vmax, num=64, endpoint=True): 1971 r, g, b = colors.color_map(x, name=col, vmin=vmin, vmax=vmax) 1972 ctf.AddRGBPoint(x, r, g, b) 1973 elif isinstance(col, int): 1974 r, g, b = colors.get_color(col) 1975 ctf.AddRGBPoint(vmin, r, g, b) # constant color 1976 ctf.AddRGBPoint(vmax, r, g, b) 1977 else: 1978 vedo.logger.warning(f"in color() unknown input type {type(col)}") 1979 1980 if alpha is not None: 1981 self.alpha(alpha, vmin=vmin, vmax=vmax) 1982 return self 1983 1984 def alpha(self, alpha, vmin=None, vmax=None): 1985 """ 1986 Assign a set of tranparencies along the range of the scalar value. 1987 A single constant value can also be assigned. 1988 1989 E.g.: say `alpha=(0.0, 0.3, 0.9, 1)` and the scalar range goes from -10 to 150. 1990 Then all cells with a value close to -10 will be completely transparent, cells at 1/4 1991 of the range will get an alpha equal to 0.3 and voxels with value close to 150 1992 will be completely opaque. 1993 1994 As a second option one can set explicit (x, alpha_x) pairs to define the transfer function. 1995 1996 E.g.: say `alpha=[(-5, 0), (35, 0.4) (123,0.9)]` and the scalar range goes from -10 to 150. 1997 Then all cells below -5 will be completely transparent, cells with a scalar value of 35 1998 will get an opacity of 40% and above 123 alpha is set to 90%. 1999 """ 2000 if vmin is None: 2001 vmin, _ = self._data.GetScalarRange() 2002 if vmax is None: 2003 _, vmax = self._data.GetScalarRange() 2004 otf = self.GetProperty().GetScalarOpacity() 2005 otf.RemoveAllPoints() 2006 self._alpha = alpha 2007 2008 if utils.is_sequence(alpha): 2009 alpha = np.array(alpha) 2010 if len(alpha.shape)==1: # user passing a flat list e.g. (0.0, 0.3, 0.9, 1) 2011 for i, al in enumerate(alpha): 2012 xalpha = vmin + (vmax - vmin) * i / (len(alpha) - 1) 2013 # Create transfer mapping scalar value to opacity 2014 otf.AddPoint(xalpha, al) 2015 # colors.printc("alpha at", round(xalpha, 1), "\tset to", al) 2016 elif len(alpha.shape) == 2: # user passing [(x0,alpha0), ...] 2017 otf.AddPoint(vmin, alpha[0][1]) 2018 for xalpha, al in alpha: 2019 # Create transfer mapping scalar value to opacity 2020 otf.AddPoint(xalpha, al) 2021 otf.AddPoint(vmax, alpha[-1][1]) 2022 2023 else: 2024 2025 otf.AddPoint(vmin, alpha) # constant alpha 2026 otf.AddPoint(vmax, alpha) 2027 2028 return self 2029 2030 def alpha_unit(self, u=None): 2031 """ 2032 Defines light attenuation per unit length. Default is 1. 2033 The larger the unit length, the further light has to travel to attenuate the same amount. 2034 2035 E.g., if you set the unit distance to 0, you will get full opacity. 2036 It means that when light travels 0 distance it's already attenuated a finite amount. 2037 Thus, any finite distance should attenuate all light. 2038 The larger you make the unit distance, the more transparent the rendering becomes. 2039 """ 2040 if u is None: 2041 return self.GetProperty().GetScalarOpacityUnitDistance() 2042 self.GetProperty().SetScalarOpacityUnitDistance(u) 2043 return self 2044 2045 def shrink(self, fraction=0.8): 2046 """ 2047 Shrink the individual cells to improve visibility. 2048 2049  2050 """ 2051 sf = vtk.vtkShrinkFilter() 2052 sf.SetInputData(self.inputdata()) 2053 sf.SetShrinkFactor(fraction) 2054 sf.Update() 2055 self._update(sf.GetOutput()) 2056 self.pipeline = utils.OperationNode( 2057 "shrink", comment=f"by {fraction}", parents=[self], c='#9e2a2b') 2058 return self 2059 2060 def isosurface(self, value=None, flying_edges=True): 2061 """ 2062 Return an `Mesh` isosurface extracted from the `Volume` object. 2063 2064 Set `value` as single float or list of values to draw the isosurface(s). 2065 Use flying_edges for faster results (but sometimes can interfere with `smooth()`). 2066 2067 Examples: 2068 - [isosurfaces.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/isosurfaces.py) 2069 2070  2071 """ 2072 scrange = self._data.GetScalarRange() 2073 2074 if flying_edges: 2075 cf = vtk.vtkFlyingEdges3D() 2076 cf.InterpolateAttributesOn() 2077 else: 2078 cf = vtk.vtkContourFilter() 2079 cf.UseScalarTreeOn() 2080 2081 cf.SetInputData(self._data) 2082 cf.ComputeNormalsOn() 2083 2084 if utils.is_sequence(value): 2085 cf.SetNumberOfContours(len(value)) 2086 for i, t in enumerate(value): 2087 cf.SetValue(i, t) 2088 else: 2089 if value is None: 2090 value = (2 * scrange[0] + scrange[1]) / 3.0 2091 # print("automatic isosurface value =", value) 2092 cf.SetValue(0, value) 2093 2094 cf.Update() 2095 poly = cf.GetOutput() 2096 2097 out = vedo.mesh.Mesh(poly, c=None).phong() 2098 out.mapper().SetScalarRange(scrange[0], scrange[1]) 2099 2100 out.pipeline = utils.OperationNode( 2101 "isosurface", parents=[self], 2102 comment=f"#pts {out._data.GetNumberOfPoints()}", 2103 c="#4cc9f0:#e9c46a", 2104 ) 2105 return out 2106 2107 def legosurface( 2108 self, vmin=None, vmax=None, 2109 invert=False, boundary=False, 2110 array_name='input_scalars', 2111 ): 2112 """ 2113 Represent an object - typically a `Volume` - as lego blocks (voxels). 2114 By default colors correspond to the volume's scalar. 2115 Returns an `Mesh` object. 2116 2117 Arguments: 2118 vmin : (float) 2119 the lower threshold, voxels below this value are not shown. 2120 vmax : (float) 2121 the upper threshold, voxels above this value are not shown. 2122 boundary : (bool) 2123 controls whether to include cells that are partially inside 2124 array_name : (int, str) 2125 name or index of the scalar array to be considered 2126 2127 Examples: 2128 - [legosurface.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/legosurface.py) 2129 2130  2131 """ 2132 dataset = vtk.vtkImplicitDataSet() 2133 dataset.SetDataSet(self._data) 2134 window = vtk.vtkImplicitWindowFunction() 2135 window.SetImplicitFunction(dataset) 2136 2137 srng = list(self._data.GetScalarRange()) 2138 if vmin is not None: 2139 srng[0] = vmin 2140 if vmax is not None: 2141 srng[1] = vmax 2142 tol = 0.00001 * (srng[1] - srng[0]) 2143 srng[0] -= tol 2144 srng[1] += tol 2145 window.SetWindowRange(srng) 2146 2147 extract = vtk.vtkExtractGeometry() 2148 extract.SetInputData(self._data) 2149 extract.SetImplicitFunction(window) 2150 extract.SetExtractInside(invert) 2151 extract.SetExtractBoundaryCells(boundary) 2152 extract.Update() 2153 2154 gf = vtk.vtkGeometryFilter() 2155 gf.SetInputData(extract.GetOutput()) 2156 gf.Update() 2157 2158 m = vedo.mesh.Mesh(gf.GetOutput()).lw(0.1).flat() 2159 m.map_points_to_cells() 2160 m.celldata.select(array_name) 2161 2162 m.pipeline = utils.OperationNode( 2163 "legosurface", parents=[self], 2164 comment=f"array: {array_name}", 2165 c="#4cc9f0:#e9c46a", 2166 ) 2167 return m 2168 2169 @deprecated(reason=vedo.colors.red + "Please use cut_with_plane()" + vedo.colors.reset) 2170 def cutWithPlane(self, origin=(0, 0, 0), normal=(1, 0, 0)): 2171 """Deprecated. Please use cut_with_plane()""" 2172 return self.cut_with_plane(origin, normal) 2173 2174 def cut_with_plane(self, origin=(0, 0, 0), normal="x"): 2175 """ 2176 Cut the object with the plane defined by a point and a normal. 2177 2178 Arguments: 2179 origin : (list) 2180 the cutting plane goes through this point 2181 normal : (list, str) 2182 normal vector to the cutting plane 2183 """ 2184 strn = str(normal) 2185 if strn == "x": normal = (1, 0, 0) 2186 elif strn == "y": normal = (0, 1, 0) 2187 elif strn == "z": normal = (0, 0, 1) 2188 elif strn == "-x": normal = (-1, 0, 0) 2189 elif strn == "-y": normal = (0, -1, 0) 2190 elif strn == "-z": normal = (0, 0, -1) 2191 plane = vtk.vtkPlane() 2192 plane.SetOrigin(origin) 2193 plane.SetNormal(normal) 2194 clipper = vtk.vtkClipDataSet() 2195 clipper.SetInputData(self._data) 2196 clipper.SetClipFunction(plane) 2197 clipper.GenerateClipScalarsOff() 2198 clipper.GenerateClippedOutputOff() 2199 clipper.SetValue(0) 2200 clipper.Update() 2201 cout = clipper.GetOutput() 2202 self._update(cout) 2203 self.pipeline = utils.OperationNode( 2204 "cut_with_plane", parents=[self], c='#9e2a2b') 2205 return self 2206 2207 def cut_with_box(self, box): 2208 """ 2209 Cut the grid with the specified bounding box. 2210 2211 Parameter box has format [xmin, xmax, ymin, ymax, zmin, zmax]. 2212 If an object is passed, its bounding box are used. 2213 2214 Example: 2215 ```python 2216 from vedo import * 2217 tetmesh = TetMesh(dataurl+'limb_ugrid.vtk') 2218 tetmesh.color('rainbow') 2219 cu = Cube(side=500).x(500) # any Mesh works 2220 tetmesh.cut_with_box(cu).show(axes=1) 2221 ``` 2222  2223 """ 2224 bc = vtk.vtkBoxClipDataSet() 2225 bc.SetInputData(self._data) 2226 if isinstance(box, vtk.vtkProp): 2227 boxb = box.GetBounds() 2228 else: 2229 boxb = box 2230 bc.SetBoxClip(*boxb) 2231 bc.Update() 2232 self._update(bc.GetOutput()) 2233 self.pipeline = utils.OperationNode( 2234 "cut_with_box", parents=[self, box], c='#9e2a2b') 2235 return self 2236 2237 def cut_with_mesh(self, mesh, invert=False, whole_cells=False, only_boundary=False): 2238 """ 2239 Cut a UGrid, TetMesh or Volume with a Mesh. 2240 2241 Use `invert` to return cut off part of the input object. 2242 """ 2243 polymesh = mesh.polydata() 2244 ug = self._data 2245 2246 ippd = vtk.vtkImplicitPolyDataDistance() 2247 ippd.SetInput(polymesh) 2248 2249 if whole_cells or only_boundary: 2250 clipper = vtk.vtkExtractGeometry() 2251 clipper.SetInputData(ug) 2252 clipper.SetImplicitFunction(ippd) 2253 clipper.SetExtractInside(not invert) 2254 clipper.SetExtractBoundaryCells(False) 2255 if only_boundary: 2256 clipper.SetExtractBoundaryCells(True) 2257 clipper.SetExtractOnlyBoundaryCells(True) 2258 else: 2259 signedDistances = vtk.vtkFloatArray() 2260 signedDistances.SetNumberOfComponents(1) 2261 signedDistances.SetName("SignedDistances") 2262 for pointId in range(ug.GetNumberOfPoints()): 2263 p = ug.GetPoint(pointId) 2264 signedDistance = ippd.EvaluateFunction(p) 2265 signedDistances.InsertNextValue(signedDistance) 2266 ug.GetPointData().AddArray(signedDistances) 2267 ug.GetPointData().SetActiveScalars("SignedDistances") 2268 clipper = vtk.vtkClipDataSet() 2269 clipper.SetInputData(ug) 2270 clipper.SetInsideOut(not invert) 2271 clipper.SetValue(0.0) 2272 2273 clipper.Update() 2274 cug = clipper.GetOutput() 2275 2276 if ug.GetCellData().GetScalars(): # not working 2277 scalname = ug.GetCellData().GetScalars().GetName() 2278 if scalname: # not working 2279 if self.useCells: 2280 self.celldata.select(scalname) 2281 else: 2282 self.pointdata.select(scalname) 2283 2284 self._update(cug) 2285 self.pipeline = utils.OperationNode( 2286 "cut_with_mesh", parents=[self, mesh], c='#9e2a2b') 2287 return self 2288 2289 def extract_cells_on_plane(self, origin, normal): 2290 """ 2291 Extract cells that are lying of the specified surface. 2292 """ 2293 bf = vtk.vtk3DLinearGridCrinkleExtractor() 2294 bf.SetInputData(self._data) 2295 bf.CopyPointDataOn() 2296 bf.CopyCellDataOn() 2297 bf.RemoveUnusedPointsOff() 2298 2299 plane = vtk.vtkPlane() 2300 plane.SetOrigin(origin) 2301 plane.SetNormal(normal) 2302 bf.SetImplicitFunction(plane) 2303 bf.Update() 2304 2305 self._update(bf.GetOutput()) 2306 self.pipeline = utils.OperationNode( 2307 "extract_cells_on_plane", parents=[self], 2308 comment=f"#cells {self._data.GetNumberOfCells()}", c='#9e2a2b', 2309 ) 2310 return self 2311 2312 def extract_cells_on_sphere(self, center, radius): 2313 """ 2314 Extract cells that are lying of the specified surface. 2315 """ 2316 bf = vtk.vtk3DLinearGridCrinkleExtractor() 2317 bf.SetInputData(self._data) 2318 bf.CopyPointDataOn() 2319 bf.CopyCellDataOn() 2320 bf.RemoveUnusedPointsOff() 2321 2322 sph = vtk.vtkSphere() 2323 sph.SetRadius(radius) 2324 sph.SetCenter(center) 2325 bf.SetImplicitFunction(sph) 2326 bf.Update() 2327 2328 self._update(bf.GetOutput()) 2329 self.pipeline = utils.OperationNode( 2330 "extract_cells_on_sphere", parents=[self], 2331 comment=f"#cells {self._data.GetNumberOfCells()}", c='#9e2a2b', 2332 ) 2333 return self 2334 2335 def extract_cells_on_cylinder(self, center, axis, radius): 2336 """ 2337 Extract cells that are lying of the specified surface. 2338 """ 2339 bf = vtk.vtk3DLinearGridCrinkleExtractor() 2340 bf.SetInputData(self._data) 2341 bf.CopyPointDataOn() 2342 bf.CopyCellDataOn() 2343 bf.RemoveUnusedPointsOff() 2344 2345 cyl = vtk.vtkCylinder() 2346 cyl.SetRadius(radius) 2347 cyl.SetCenter(center) 2348 cyl.SetAxis(axis) 2349 bf.SetImplicitFunction(cyl) 2350 bf.Update() 2351 2352 self.pipeline = utils.OperationNode( 2353 "extract_cells_on_cylinder", parents=[self], 2354 comment=f"#cells {self._data.GetNumberOfCells()}", c='#9e2a2b', 2355 ) 2356 self._update(bf.GetOutput()) 2357 return self 2358 2359 def clean(self): 2360 """ 2361 Cleanup unused points and empty cells 2362 """ 2363 cl = vtk.vtkStaticCleanUnstructuredGrid() 2364 cl.SetInputData(self._data) 2365 cl.RemoveUnusedPointsOn() 2366 cl.ProduceMergeMapOff() 2367 cl.AveragePointDataOff() 2368 cl.Update() 2369 2370 self._update(cl.GetOutput()) 2371 self.pipeline = utils.OperationNode( 2372 "clean", parents=[self], 2373 comment=f"#cells {self._data.GetNumberOfCells()}", 2374 c='#9e2a2b', 2375 ) 2376 return self 2377 2378 2379 def find_cell(self, p): 2380 """Locate the cell that contains a point and return the cell ID.""" 2381 cell = vtk.vtkTetra() 2382 cellId = vtk.mutable(0) 2383 tol2 = vtk.mutable(0) 2384 subId = vtk.mutable(0) 2385 pcoords = [0,0,0] 2386 weights = [0,0,0] 2387 cid = self._data.FindCell(p, cell, cellId, tol2, subId, pcoords, weights) 2388 return cid 2389 2390 2391 def extract_cells_by_id(self, idlist, use_point_ids=False): 2392 """Return a new UGrid composed of the specified subset of indices.""" 2393 selectionNode = vtk.vtkSelectionNode() 2394 if use_point_ids: 2395 selectionNode.SetFieldType(vtk.vtkSelectionNode.POINT) 2396 contcells = vtk.vtkSelectionNode.CONTAINING_CELLS() 2397 selectionNode.GetProperties().Set(contcells, 1) 2398 else: 2399 selectionNode.SetFieldType(vtk.vtkSelectionNode.CELL) 2400 selectionNode.SetContentType(vtk.vtkSelectionNode.INDICES) 2401 vidlist = utils.numpy2vtk(idlist, dtype="id") 2402 selectionNode.SetSelectionList(vidlist) 2403 selection = vtk.vtkSelection() 2404 selection.AddNode(selectionNode) 2405 es = vtk.vtkExtractSelection() 2406 es.SetInputData(0, self._data) 2407 es.SetInputData(1, selection) 2408 es.Update() 2409 2410 ug = vedo.ugrid.UGrid(es.GetOutput()) 2411 pr = vtk.vtkProperty() 2412 pr.DeepCopy(self.GetProperty()) 2413 ug.SetProperty(pr) 2414 ug.property = pr 2415 2416 # assign the same transformation to the copy 2417 ug.SetOrigin(self.GetOrigin()) 2418 ug.SetScale(self.GetScale()) 2419 ug.SetOrientation(self.GetOrientation()) 2420 ug.SetPosition(self.GetPosition()) 2421 ug.mapper().SetLookupTable(utils.ctf2lut(self)) 2422 ug.pipeline = utils.OperationNode( 2423 "extract_cells_by_id", parents=[self], 2424 comment=f"#cells {self._data.GetNumberOfCells()}", 2425 c="#9e2a2b", 2426 ) 2427 return ug 2428 2429 2430############################################################################### funcs 2431def _getinput(obj): 2432 if isinstance(obj, (vtk.vtkVolume, vtk.vtkActor)): 2433 return obj.GetMapper().GetInput() 2434 return obj 2435 2436 2437def probe_points(dataset, pts): 2438 """ 2439 Takes a `Volume` (or any other vtk data set) 2440 and probes its scalars at the specified points in space. 2441 2442 Note that a mask is also output with valid/invalid points which can be accessed 2443 with `mesh.pointdata['vtkValidPointMask']`. 2444 2445 Examples: 2446 - [probe_points.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/probe_points.py) 2447 2448  2449 """ 2450 if isinstance(pts, vedo.pointcloud.Points): 2451 pts = pts.points() 2452 2453 def _readpoints(): 2454 output = src.GetPolyDataOutput() 2455 points = vtk.vtkPoints() 2456 for p in pts: 2457 x, y, z = p 2458 points.InsertNextPoint(x, y, z) 2459 output.SetPoints(points) 2460 2461 cells = vtk.vtkCellArray() 2462 cells.InsertNextCell(len(pts)) 2463 for i in range(len(pts)): 2464 cells.InsertCellPoint(i) 2465 output.SetVerts(cells) 2466 2467 src = vtk.vtkProgrammableSource() 2468 src.SetExecuteMethod(_readpoints) 2469 src.Update() 2470 img = _getinput(dataset) 2471 probeFilter = vtk.vtkProbeFilter() 2472 probeFilter.SetSourceData(img) 2473 probeFilter.SetInputConnection(src.GetOutputPort()) 2474 probeFilter.Update() 2475 poly = probeFilter.GetOutput() 2476 pm = vedo.mesh.Mesh(poly) 2477 pm.name = "ProbePoints" 2478 pm.pipeline = utils.OperationNode("probe_points", parents=[dataset]) 2479 return pm 2480 2481 2482def probe_line(dataset, p1, p2, res=100): 2483 """ 2484 Takes a `Volume` (or any other vtk data set) 2485 and probes its scalars along a line defined by 2 points `p1` and `p2`. 2486 2487 Note that a mask is also output with valid/invalid points which can be accessed 2488 with `mesh.pointdata['vtkValidPointMask']`. 2489 2490 Use `res` to set the nr of points along the line 2491 2492 Examples: 2493 - [probe_line1.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/probe_line1.py) 2494 - [probe_line2.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/probe_line2.py) 2495 2496  2497 """ 2498 line = vtk.vtkLineSource() 2499 line.SetResolution(res) 2500 line.SetPoint1(p1) 2501 line.SetPoint2(p2) 2502 img = _getinput(dataset) 2503 probeFilter = vtk.vtkProbeFilter() 2504 probeFilter.SetSourceData(img) 2505 probeFilter.SetInputConnection(line.GetOutputPort()) 2506 probeFilter.Update() 2507 poly = probeFilter.GetOutput() 2508 lnn = vedo.mesh.Mesh(poly) 2509 lnn.name = "ProbeLine" 2510 lnn.pipeline = utils.OperationNode("probe_line", parents=[dataset]) 2511 return lnn 2512 2513 2514def probe_plane(dataset, origin=(0, 0, 0), normal=(1, 0, 0)): 2515 """ 2516 Takes a `Volume` (or any other vtk data set) 2517 and probes its scalars on a plane defined by a point and a normal. 2518 2519 Examples: 2520 - [slicePlane1.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/slicePlane1.py) 2521 - [slicePlane2.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/slicePlane2.py) 2522 2523  2524 """ 2525 img = _getinput(dataset) 2526 plane = vtk.vtkPlane() 2527 plane.SetOrigin(origin) 2528 plane.SetNormal(normal) 2529 planeCut = vtk.vtkCutter() 2530 planeCut.SetInputData(img) 2531 planeCut.SetCutFunction(plane) 2532 planeCut.Update() 2533 poly = planeCut.GetOutput() 2534 cutmesh = vedo.mesh.Mesh(poly) 2535 cutmesh.name = "ProbePlane" 2536 cutmesh.pipeline = utils.OperationNode("probe_plane", parents=[dataset]) 2537 return cutmesh
204class Base3DProp: 205 """ 206 Base class to manage positioning and size of the objects in space and other properties. 207 208 .. warning:: Do not use this class to instanciate objects 209 """ 210 def __init__(self): 211 """ 212 Base class to manage positioning and size of the objects in space and other properties. 213 """ 214 self.filename = "" 215 self.name = "" 216 self.file_size = "" 217 self.created = "" 218 self.trail = None 219 self.trail_points = [] 220 self.trail_segment_size = 0 221 self.trail_offset = None 222 self.shadows = [] 223 self.axes = None 224 self.picked3d = None 225 self.units = None 226 self.top = np.array([0, 0, 1]) 227 self.base = np.array([0, 0, 0]) 228 self.info = {} 229 self.time = time.time() 230 self.rendered_at = set() 231 self.transform = None 232 self._isfollower = False # set by mesh.follow_camera() 233 234 self.point_locator = None 235 self.cell_locator = None 236 237 self.scalarbar = None 238 # self.scalarbars = dict() #TODO 239 self.pipeline = None 240 241 242 def address(self): 243 """ 244 Return a unique memory address integer which may serve as the ID of the 245 object, or passed to c++ code. 246 """ 247 # https://www.linkedin.com/pulse/speedup-your-code-accessing-python-vtk-objects-from-c-pletzer/ 248 # https://github.com/tfmoraes/polydata_connectivity 249 return int(self.inputdata().GetAddressAsString("")[5:], 16) 250 251 def pickable(self, value=None): 252 """Set/get the pickability property of an object.""" 253 if value is None: 254 return self.GetPickable() 255 self.SetPickable(value) 256 return self 257 258 def draggable(self, value=None): # NOT FUNCTIONAL? 259 """Set/get the draggability property of an object.""" 260 if value is None: 261 return self.GetDragable() 262 self.SetDragable(value) 263 return self 264 265 def origin(self, x=None, y=None, z=None): 266 """ 267 Set/get object's origin. 268 269 Relevant to control the scaling with `scale()` and rotations. 270 Has no effect on position. 271 """ 272 if x is None: 273 return np.array(self.GetOrigin()) + self.GetPosition() 274 275 if z is None and y is None: # assume x is of the form (x,y,z) 276 if len(x) == 3: 277 x, y, z = x 278 else: 279 x, y = x 280 z = 0 281 elif z is None: # assume x,y is of the form x, y 282 z = 0 283 self.SetOrigin([x, y, z] - np.array(self.GetPosition())) 284 return self 285 286 def pos(self, x=None, y=None, z=None): 287 """Set/Get object position.""" 288 if x is None: # get functionality 289 return np.array(self.GetPosition()) 290 291 if z is None and y is None: # assume x is of the form (x,y,z) 292 if len(x) == 3: 293 x, y, z = x 294 else: 295 x, y = x 296 z = 0 297 elif z is None: # assume x,y is of the form x, y 298 z = 0 299 self.SetPosition(x, y, z) 300 301 self.point_locator = None 302 self.cell_locator = None 303 return self # return itself to concatenate methods 304 305 def shift(self, dx=0, dy=0, dz=0): 306 """Add a vector to the current object position.""" 307 p = np.array(self.GetPosition()) 308 309 if utils.is_sequence(dx): 310 if len(dx) == 2: 311 self.SetPosition(p + [dx[0], dx[1], 0]) 312 else: 313 self.SetPosition(p + dx) 314 else: 315 self.SetPosition(p + [dx, dy, dz]) 316 317 self.point_locator = None 318 self.cell_locator = None 319 return self 320 321 def x(self, val=None): 322 """Set/Get object position along x axis.""" 323 p = self.GetPosition() 324 if val is None: 325 return p[0] 326 self.pos(val, p[1], p[2]) 327 return self 328 329 def y(self, val=None): 330 """Set/Get object position along y axis.""" 331 p = self.GetPosition() 332 if val is None: 333 return p[1] 334 self.pos(p[0], val, p[2]) 335 return self 336 337 def z(self, val=None): 338 """Set/Get object position along z axis.""" 339 p = self.GetPosition() 340 if val is None: 341 return p[2] 342 self.pos(p[0], p[1], val) 343 return self 344 345 def rotate(self, angle, axis=(1, 0, 0), point=(0, 0, 0), rad=False): 346 """ 347 Rotate around an arbitrary `axis` passing through `point`. 348 349 Example: 350 ```python 351 from vedo import * 352 c1 = Cube() 353 c2 = c1.clone().c('violet').alpha(0.5) # copy of c1 354 v = vector(0.2,1,0) 355 p = vector(1,0,0) # axis passes through this point 356 c2.rotate(90, axis=v, point=p) 357 l = Line(-v+p, v+p).lw(3).c('red') 358 show(c1, l, c2, axes=1).close() 359 ``` 360  361 """ 362 if rad: 363 anglerad = angle 364 else: 365 anglerad = np.deg2rad(angle) 366 axis = utils.versor(axis) 367 a = np.cos(anglerad / 2) 368 b, c, d = -axis * np.sin(anglerad / 2) 369 aa, bb, cc, dd = a * a, b * b, c * c, d * d 370 bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d 371 R = np.array( 372 [ 373 [aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)], 374 [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)], 375 [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc], 376 ] 377 ) 378 rv = np.dot(R, self.GetPosition() - np.asarray(point)) + point 379 380 if rad: 381 angle *= 180.0 / np.pi 382 # this vtk method only rotates in the origin of the object: 383 self.RotateWXYZ(angle, axis[0], axis[1], axis[2]) 384 self.pos(rv) 385 return self 386 387 def _rotatexyz(self, a, angle, rad, around): 388 if rad: 389 angle *= 180 / np.pi 390 391 T = vtk.vtkTransform() 392 T.SetMatrix(self.GetMatrix()) 393 T.PostMultiply() 394 395 rot = dict(x=T.RotateX, y=T.RotateY, z=T.RotateZ) 396 397 if around is None: 398 # rotate around its origin 399 rot[a](angle) 400 else: 401 if around == "itself": 402 around = self.GetPosition() 403 # displacement needed to bring it back to the origin 404 # and disregard origin 405 disp = around - np.array(self.GetOrigin()) 406 T.Translate(-disp) 407 rot[a](angle) 408 T.Translate(disp) 409 410 self.SetOrientation(T.GetOrientation()) 411 self.SetPosition(T.GetPosition()) 412 413 self.point_locator = None 414 self.cell_locator = None 415 return self 416 417 @deprecated(reason=vedo.colors.red + "Please use rotate_x()" + vedo.colors.reset) 418 def rotateX(self, *a, **b): 419 """Deprecated. Please use `rotate_x()`.""" 420 return self.rotate_x(*a, **b) 421 @deprecated(reason=vedo.colors.red + "Please use rotate_y()" + vedo.colors.reset) 422 def rotateY(self, *a, **b): 423 """Deprecated. Please use `rotate_y()`.""" 424 return self.rotate_y(*a, **b) 425 @deprecated(reason=vedo.colors.red + "Please use rotate_z()" + vedo.colors.reset) 426 def rotateZ(self, *a, **b): 427 """Deprecated. Please use `rotate_z()`.""" 428 return self.rotate_z(*a, **b) 429 430 def rotate_x(self, angle, rad=False, around=None): 431 """ 432 Rotate around x-axis. If angle is in radians set `rad=True`. 433 434 Use `around` to define a pivoting point. 435 """ 436 return self._rotatexyz("x", angle, rad, around) 437 438 def rotate_y(self, angle, rad=False, around=None): 439 """ 440 Rotate around y-axis. If angle is in radians set `rad=True`. 441 442 Use `around` to define a pivoting point. 443 """ 444 return self._rotatexyz("y", angle, rad, around) 445 446 def rotate_z(self, angle, rad=False, around=None): 447 """ 448 Rotate around z-axis. If angle is in radians set `rad=True`. 449 450 Use `around` to define a pivoting point. 451 """ 452 return self._rotatexyz("z", angle, rad, around) 453 454 def orientation(self, newaxis=None, rotation=0, concatenate=False, xyplane=False, rad=False): 455 """ 456 Set/Get object orientation. 457 458 Arguments: 459 rotation : (float) 460 rotate object around newaxis. 461 concatenate : (bool) 462 concatenate the orietation operation with the previous existing transform (if any) 463 xyplane : (bool) 464 make an extra rotation to keep the object aligned to the xy-plane 465 rad : (bool) 466 set to True if angle is expressed in radians. 467 468 Example: 469 ```python 470 from vedo import * 471 center = np.array([581/2,723/2,0]) 472 objs = [] 473 for a in np.linspace(0, 6.28, 7): 474 v = vector(cos(a), sin(a), 0)*1000 475 pic = Picture(dataurl+"images/dog.jpg").rotate_z(10) 476 pic.orientation(v, xyplane=True) 477 pic.origin(center) 478 pic.pos(v - center) 479 objs += [pic, Arrow(v, v+v)] 480 show(objs, Point(), axes=1).close() 481 ``` 482  483 484 Examples: 485 - [gyroscope2.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/gyroscope2.py) 486 487  488 """ 489 if self.top is None or self.base is None: 490 initaxis = (0, 0, 1) 491 else: 492 initaxis = utils.versor(self.top - self.base) 493 494 newaxis = utils.versor(newaxis) 495 p = np.array(self.GetPosition()) 496 crossvec = np.cross(initaxis, newaxis) 497 498 angleth = np.arccos(np.dot(initaxis, newaxis)) 499 500 T = vtk.vtkTransform() 501 if concatenate: 502 try: 503 M = self.GetMatrix() 504 T.SetMatrix(M) 505 except: 506 pass 507 T.PostMultiply() 508 T.Translate(-p) 509 if rotation: 510 if rad: 511 rotation *= 180.0 / np.pi 512 T.RotateWXYZ(rotation, initaxis) 513 if xyplane: 514 angleph = np.arctan2(newaxis[1], newaxis[0]) 515 T.RotateWXYZ(np.rad2deg(angleph + angleth), initaxis) # compensation 516 T.RotateWXYZ(np.rad2deg(angleth), crossvec) 517 T.Translate(p) 518 519 self.SetOrientation(T.GetOrientation()) 520 521 self.point_locator = None 522 self.cell_locator = None 523 return self 524 525 # newaxis = utils.versor(newaxis) 526 # pos = np.array(self.GetPosition()) 527 # crossvec = np.cross(initaxis, newaxis) 528 # angle = np.arccos(np.dot(initaxis, newaxis)) 529 # T = vtk.vtkTransform() 530 # T.PostMultiply() 531 # T.Translate(-pos) 532 # if rotation: 533 # T.RotateWXYZ(rotation, initaxis) 534 # T.RotateWXYZ(np.rad2deg(angle), crossvec) 535 # T.Translate(pos) 536 # self.SetUserTransform(T) 537 # self.transform = T 538 539 540 def scale(self, s=None, reset=False): 541 """ 542 Set/get object's scaling factor. 543 544 Arguments: 545 s : (list, float) 546 scaling factor(s). 547 reset : (bool) 548 if True previous scaling factors are ignored. 549 550 Note: 551 use `s=(sx,sy,sz)` to scale differently in the three coordinates. 552 """ 553 if s is None: 554 return np.array(self.GetScale()) 555 556 # assert s[0] != 0 557 # assert s[1] != 0 558 # assert s[2] != 0 559 560 if reset: 561 self.SetScale(s) 562 else: 563 self.SetScale(np.multiply(self.GetScale(), s)) 564 565 self.point_locator = None 566 self.cell_locator = None 567 return self 568 569 def get_transform(self, invert=False): 570 """ 571 Check if `object.transform` exists and returns a `vtkTransform`. 572 Otherwise return current user transformation (where the object is currently placed). 573 574 Use `invert` to return the inverse of the current transformation 575 576 Example: 577 ```python 578 from vedo import * 579 580 c1 = Cube() 581 c2 = c1.clone().c('violet').alpha(0.5) # copy of c1 582 v = vector(0.2,1,0) 583 p = vector(1,0,0) # axis passes through this point 584 c2.rotate(90, axis=v, point=p) 585 586 # get the inverse of the current transformation 587 T = c2.get_transform(invert=True) 588 c2.apply_transform(T) # put back c2 in place 589 590 l = Line(p-v, p+v).lw(3).c('red') 591 show(c1.wireframe().lw(3), l, c2, axes=1).close() 592 ``` 593  594 """ 595 if self.transform: 596 tr = self.transform 597 if invert: 598 tr = tr.GetInverse() 599 return tr 600 601 T = self.GetMatrix() 602 tr = vtk.vtkTransform() 603 tr.SetMatrix(T) 604 if invert: 605 tr = tr.GetInverse() 606 return tr 607 608 609 @deprecated(reason=vedo.colors.red + "Please use apply_transform()" + vedo.colors.reset) 610 def applyTransform(self, T, reset=False, concatenate=False): 611 """Deprecated. Please use `apply_transform()`""" 612 return self.apply_transform(T,reset,concatenate) 613 614 def apply_transform(self, T, reset=False, concatenate=False): 615 """ 616 Transform object position and orientation. 617 618 Arguments: 619 reset : (bool) 620 no effect, this is superseded by `pointcloud.apply_transform()` 621 concatenate : (bool) 622 no effect, this is superseded by `pointcloud.apply_transform()` 623 """ 624 if isinstance(T, vtk.vtkMatrix4x4): 625 self.SetUserMatrix(T) 626 elif utils.is_sequence(T): 627 vm = vtk.vtkMatrix4x4() 628 for i in [0, 1, 2, 3]: 629 for j in [0, 1, 2, 3]: 630 vm.SetElement(i, j, T[i][j]) 631 self.SetUserMatrix(vm) 632 else: 633 self.SetUserTransform(T) 634 self.transform = T 635 636 self.point_locator = None 637 self.cell_locator = None 638 return self 639 640 def align_to_bounding_box(self, msh, rigid=False): 641 """ 642 Align the current object's bounding box to the bounding box 643 of the input object. 644 645 Use `rigid` to disable scaling. 646 647 Examples: 648 - [align6.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/align6.py) 649 """ 650 lmt = vtk.vtkLandmarkTransform() 651 ss = vtk.vtkPoints() 652 xss0, xss1, yss0, yss1, zss0, zss1 = self.bounds() 653 for p in [ 654 [xss0, yss0, zss0], 655 [xss1, yss0, zss0], 656 [xss1, yss1, zss0], 657 [xss0, yss1, zss0], 658 [xss0, yss0, zss1], 659 [xss1, yss0, zss1], 660 [xss1, yss1, zss1], 661 [xss0, yss1, zss1], 662 ]: 663 ss.InsertNextPoint(p) 664 st = vtk.vtkPoints() 665 xst0, xst1, yst0, yst1, zst0, zst1 = msh.bounds() 666 for p in [ 667 [xst0, yst0, zst0], 668 [xst1, yst0, zst0], 669 [xst1, yst1, zst0], 670 [xst0, yst1, zst0], 671 [xst0, yst0, zst1], 672 [xst1, yst0, zst1], 673 [xst1, yst1, zst1], 674 [xst0, yst1, zst1], 675 ]: 676 st.InsertNextPoint(p) 677 678 lmt.SetSourceLandmarks(ss) 679 lmt.SetTargetLandmarks(st) 680 lmt.SetModeToAffine() 681 if rigid: 682 lmt.SetModeToRigidBody() 683 lmt.Update() 684 self.apply_transform(lmt) 685 self.transform = lmt 686 687 self.point_locator = None 688 self.cell_locator = None 689 return self 690 691 def on(self): 692 """Switch on object visibility. Object is not removed.""" 693 self.VisibilityOn() 694 try: 695 self.scalarbar.VisibilityOn() 696 except AttributeError: 697 pass 698 try: 699 self.trail.VisibilityOn() 700 except AttributeError: 701 pass 702 try: 703 for sh in self.shadows: 704 sh.VisibilityOn() 705 except AttributeError: 706 pass 707 return self 708 709 def off(self): 710 """Switch off object visibility. Object is not removed.""" 711 self.VisibilityOff() 712 try: 713 self.scalarbar.VisibilityOff() 714 except AttributeError: 715 pass 716 try: 717 self.trail.VisibilityOff() 718 except AttributeError: 719 pass 720 try: 721 for sh in self.shadows: 722 sh.VisibilityOff() 723 except AttributeError: 724 pass 725 return self 726 727 def toggle(self): 728 """Toggle object visibility on/off.""" 729 v = self.GetVisibility() 730 if v: 731 self.off() 732 else: 733 self.on() 734 return self 735 736 def box(self, scale=1, padding=0, fill=False): 737 """ 738 Return the bounding box as a new `Mesh`. 739 740 Arguments: 741 scale : (float) 742 box size can be scaled by a factor 743 padding : (float, list) 744 a constant padding can be added (can be a list [padx,pady,padz]) 745 746 Examples: 747 - [latex.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/latex.py) 748 """ 749 b = self.bounds() 750 if not utils.is_sequence(padding): 751 padding = [padding, padding, padding] 752 length, width, height = b[1] - b[0], b[3] - b[2], b[5] - b[4] 753 tol = (length + width + height) / 30000 # useful for boxing 2D text 754 pos = [(b[0] + b[1]) / 2, (b[3] + b[2]) / 2, (b[5] + b[4]) / 2 - tol] 755 bx = vedo.shapes.Box( 756 pos, 757 length * scale + padding[0], 758 width * scale + padding[1], 759 height * scale + padding[2], 760 c="gray", 761 ) 762 if hasattr(self, "GetProperty"): # could be Assembly 763 if isinstance(self.GetProperty(), vtk.vtkProperty): # could be volume 764 pr = vtk.vtkProperty() 765 pr.DeepCopy(self.GetProperty()) 766 bx.SetProperty(pr) 767 bx.property = pr 768 bx.wireframe(not fill) 769 bx.flat().lighting("off") 770 return bx 771 772 def use_bounds(self, ub=True): 773 """ 774 Instruct the current camera to either take into account or ignore 775 the object bounds when resetting. 776 """ 777 self.SetUseBounds(ub) 778 return self 779 780 def bounds(self): 781 """ 782 Get the object bounds. 783 Returns a list in format `[xmin,xmax, ymin,ymax, zmin,zmax]`. 784 """ 785 try: 786 pts = self.points() 787 xmin, ymin, zmin = np.min(pts, axis=0) 788 xmax, ymax, zmax = np.max(pts, axis=0) 789 return [xmin,xmax, ymin,ymax, zmin,zmax] 790 except (AttributeError, ValueError): 791 return self.GetBounds() 792 793 def xbounds(self, i=None): 794 """Get the bounds `[xmin,xmax]`. Can specify upper or lower with i (0,1).""" 795 b = self.bounds() 796 if i is not None: 797 return b[i] 798 return (b[0], b[1]) 799 800 def ybounds(self, i=None): 801 """Get the bounds `[ymin,ymax]`. Can specify upper or lower with i (0,1).""" 802 b = self.bounds() 803 if i == 0: 804 return b[2] 805 if i == 1: 806 return b[3] 807 return (b[2], b[3]) 808 809 def zbounds(self, i=None): 810 """Get the bounds `[zmin,zmax]`. Can specify upper or lower with i (0,1).""" 811 b = self.bounds() 812 if i == 0: return b[4] 813 if i == 1: return b[5] 814 return (b[4], b[5]) 815 816 @deprecated(reason=vedo.colors.red + "Please use diagonal_size()" + vedo.colors.reset) 817 def diagonalSize(self): 818 """Deprecated. Please use `diagonal_size()`.""" 819 return self.diagonal_size() 820 821 def diagonal_size(self): 822 """Get the length of the diagonal of mesh bounding box.""" 823 b = self.bounds() 824 return np.sqrt((b[1] - b[0]) ** 2 + (b[3] - b[2]) ** 2 + (b[5] - b[4]) ** 2) 825 # return self.GetLength() # ???different??? 826 827 828 def copy_data_from(self, obj): 829 """Copy all data (point and cell data) from this input object""" 830 self._data.GetPointData().PassData(obj._data.GetPointData()) 831 self._data.GetCellData().PassData(obj._data.GetCellData()) 832 self.pipeline = utils.OperationNode( 833 f"copy_data_from\n{obj.__class__.__name__}", 834 parents=[self, obj], 835 shape='note', 836 c="#ccc5b9", 837 ) 838 return self 839 840 def print(self): 841 """Print information about an object.""" 842 utils.print_info(self) 843 return self 844 845 def show(self, **options): 846 """ 847 Create on the fly an instance of class `Plotter` or use the last existing one to 848 show one single object. 849 850 This method is meant as a shortcut. If more than one object needs to be visualised 851 please use the syntax `show(mesh1, mesh2, volume, ..., options)`. 852 853 Returns the `Plotter` class instance. 854 """ 855 return vedo.plotter.show(self, **options) 856 857 def thumbnail( 858 self, 859 zoom=1.25, 860 size=(200, 200), 861 bg="white", 862 azimuth=0, 863 elevation=0, 864 axes=False, 865 ): 866 """Build a thumbnail of the object and return it as an array.""" 867 # speed is about 20Hz for size=[200,200] 868 ren = vtk.vtkRenderer() 869 ren.AddActor(self) 870 if axes: 871 axes = vedo.addons.Axes(self) 872 ren.AddActor(axes) 873 ren.ResetCamera() 874 cam = ren.GetActiveCamera() 875 cam.Zoom(zoom) 876 cam.Elevation(elevation) 877 cam.Azimuth(azimuth) 878 879 ren_win = vtk.vtkRenderWindow() 880 ren_win.SetOffScreenRendering(True) 881 ren_win.SetSize(size) 882 ren.SetBackground(colors.get_color(bg)) 883 ren_win.AddRenderer(ren) 884 ren_win.Render() 885 886 nx, ny = ren_win.GetSize() 887 arr = vtk.vtkUnsignedCharArray() 888 ren_win.GetRGBACharPixelData(0, 0, nx-1, ny-1, 0, arr) 889 narr = utils.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3]) 890 narr = np.ascontiguousarray(np.flip(narr, axis=0)) 891 892 ren.RemoveActor(self) 893 if axes: 894 ren.RemoveActor(axes) 895 ren_win.Finalize() 896 del ren_win 897 return narr
Base class to manage positioning and size of the objects in space and other properties.
Do not use this class to instanciate objects
210 def __init__(self): 211 """ 212 Base class to manage positioning and size of the objects in space and other properties. 213 """ 214 self.filename = "" 215 self.name = "" 216 self.file_size = "" 217 self.created = "" 218 self.trail = None 219 self.trail_points = [] 220 self.trail_segment_size = 0 221 self.trail_offset = None 222 self.shadows = [] 223 self.axes = None 224 self.picked3d = None 225 self.units = None 226 self.top = np.array([0, 0, 1]) 227 self.base = np.array([0, 0, 0]) 228 self.info = {} 229 self.time = time.time() 230 self.rendered_at = set() 231 self.transform = None 232 self._isfollower = False # set by mesh.follow_camera() 233 234 self.point_locator = None 235 self.cell_locator = None 236 237 self.scalarbar = None 238 # self.scalarbars = dict() #TODO 239 self.pipeline = None
Base class to manage positioning and size of the objects in space and other properties.
242 def address(self): 243 """ 244 Return a unique memory address integer which may serve as the ID of the 245 object, or passed to c++ code. 246 """ 247 # https://www.linkedin.com/pulse/speedup-your-code-accessing-python-vtk-objects-from-c-pletzer/ 248 # https://github.com/tfmoraes/polydata_connectivity 249 return int(self.inputdata().GetAddressAsString("")[5:], 16)
Return a unique memory address integer which may serve as the ID of the object, or passed to c++ code.
251 def pickable(self, value=None): 252 """Set/get the pickability property of an object.""" 253 if value is None: 254 return self.GetPickable() 255 self.SetPickable(value) 256 return self
Set/get the pickability property of an object.
258 def draggable(self, value=None): # NOT FUNCTIONAL? 259 """Set/get the draggability property of an object.""" 260 if value is None: 261 return self.GetDragable() 262 self.SetDragable(value) 263 return self
Set/get the draggability property of an object.
265 def origin(self, x=None, y=None, z=None): 266 """ 267 Set/get object's origin. 268 269 Relevant to control the scaling with `scale()` and rotations. 270 Has no effect on position. 271 """ 272 if x is None: 273 return np.array(self.GetOrigin()) + self.GetPosition() 274 275 if z is None and y is None: # assume x is of the form (x,y,z) 276 if len(x) == 3: 277 x, y, z = x 278 else: 279 x, y = x 280 z = 0 281 elif z is None: # assume x,y is of the form x, y 282 z = 0 283 self.SetOrigin([x, y, z] - np.array(self.GetPosition())) 284 return self
Set/get object's origin.
Relevant to control the scaling with scale()
and rotations.
Has no effect on position.
286 def pos(self, x=None, y=None, z=None): 287 """Set/Get object position.""" 288 if x is None: # get functionality 289 return np.array(self.GetPosition()) 290 291 if z is None and y is None: # assume x is of the form (x,y,z) 292 if len(x) == 3: 293 x, y, z = x 294 else: 295 x, y = x 296 z = 0 297 elif z is None: # assume x,y is of the form x, y 298 z = 0 299 self.SetPosition(x, y, z) 300 301 self.point_locator = None 302 self.cell_locator = None 303 return self # return itself to concatenate methods
Set/Get object position.
305 def shift(self, dx=0, dy=0, dz=0): 306 """Add a vector to the current object position.""" 307 p = np.array(self.GetPosition()) 308 309 if utils.is_sequence(dx): 310 if len(dx) == 2: 311 self.SetPosition(p + [dx[0], dx[1], 0]) 312 else: 313 self.SetPosition(p + dx) 314 else: 315 self.SetPosition(p + [dx, dy, dz]) 316 317 self.point_locator = None 318 self.cell_locator = None 319 return self
Add a vector to the current object position.
321 def x(self, val=None): 322 """Set/Get object position along x axis.""" 323 p = self.GetPosition() 324 if val is None: 325 return p[0] 326 self.pos(val, p[1], p[2]) 327 return self
Set/Get object position along x axis.
329 def y(self, val=None): 330 """Set/Get object position along y axis.""" 331 p = self.GetPosition() 332 if val is None: 333 return p[1] 334 self.pos(p[0], val, p[2]) 335 return self
Set/Get object position along y axis.
337 def z(self, val=None): 338 """Set/Get object position along z axis.""" 339 p = self.GetPosition() 340 if val is None: 341 return p[2] 342 self.pos(p[0], p[1], val) 343 return self
Set/Get object position along z axis.
345 def rotate(self, angle, axis=(1, 0, 0), point=(0, 0, 0), rad=False): 346 """ 347 Rotate around an arbitrary `axis` passing through `point`. 348 349 Example: 350 ```python 351 from vedo import * 352 c1 = Cube() 353 c2 = c1.clone().c('violet').alpha(0.5) # copy of c1 354 v = vector(0.2,1,0) 355 p = vector(1,0,0) # axis passes through this point 356 c2.rotate(90, axis=v, point=p) 357 l = Line(-v+p, v+p).lw(3).c('red') 358 show(c1, l, c2, axes=1).close() 359 ``` 360  361 """ 362 if rad: 363 anglerad = angle 364 else: 365 anglerad = np.deg2rad(angle) 366 axis = utils.versor(axis) 367 a = np.cos(anglerad / 2) 368 b, c, d = -axis * np.sin(anglerad / 2) 369 aa, bb, cc, dd = a * a, b * b, c * c, d * d 370 bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d 371 R = np.array( 372 [ 373 [aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)], 374 [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)], 375 [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc], 376 ] 377 ) 378 rv = np.dot(R, self.GetPosition() - np.asarray(point)) + point 379 380 if rad: 381 angle *= 180.0 / np.pi 382 # this vtk method only rotates in the origin of the object: 383 self.RotateWXYZ(angle, axis[0], axis[1], axis[2]) 384 self.pos(rv) 385 return self
Rotate around an arbitrary axis
passing through point
.
Example:
from vedo import * c1 = Cube() c2 = c1.clone().c('violet').alpha(0.5) # copy of c1 v = vector(0.2,1,0) p = vector(1,0,0) # axis passes through this point c2.rotate(90, axis=v, point=p) l = Line(-v+p, v+p).lw(3).c('red') show(c1, l, c2, axes=1).close()
417 @deprecated(reason=vedo.colors.red + "Please use rotate_x()" + vedo.colors.reset) 418 def rotateX(self, *a, **b): 419 """Deprecated. Please use `rotate_x()`.""" 420 return self.rotate_x(*a, **b)
Deprecated. Please use rotate_x()
.
421 @deprecated(reason=vedo.colors.red + "Please use rotate_y()" + vedo.colors.reset) 422 def rotateY(self, *a, **b): 423 """Deprecated. Please use `rotate_y()`.""" 424 return self.rotate_y(*a, **b)
Deprecated. Please use rotate_y()
.
425 @deprecated(reason=vedo.colors.red + "Please use rotate_z()" + vedo.colors.reset) 426 def rotateZ(self, *a, **b): 427 """Deprecated. Please use `rotate_z()`.""" 428 return self.rotate_z(*a, **b)
Deprecated. Please use rotate_z()
.
430 def rotate_x(self, angle, rad=False, around=None): 431 """ 432 Rotate around x-axis. If angle is in radians set `rad=True`. 433 434 Use `around` to define a pivoting point. 435 """ 436 return self._rotatexyz("x", angle, rad, around)
Rotate around x-axis. If angle is in radians set rad=True
.
Use around
to define a pivoting point.
438 def rotate_y(self, angle, rad=False, around=None): 439 """ 440 Rotate around y-axis. If angle is in radians set `rad=True`. 441 442 Use `around` to define a pivoting point. 443 """ 444 return self._rotatexyz("y", angle, rad, around)
Rotate around y-axis. If angle is in radians set rad=True
.
Use around
to define a pivoting point.
446 def rotate_z(self, angle, rad=False, around=None): 447 """ 448 Rotate around z-axis. If angle is in radians set `rad=True`. 449 450 Use `around` to define a pivoting point. 451 """ 452 return self._rotatexyz("z", angle, rad, around)
Rotate around z-axis. If angle is in radians set rad=True
.
Use around
to define a pivoting point.
454 def orientation(self, newaxis=None, rotation=0, concatenate=False, xyplane=False, rad=False): 455 """ 456 Set/Get object orientation. 457 458 Arguments: 459 rotation : (float) 460 rotate object around newaxis. 461 concatenate : (bool) 462 concatenate the orietation operation with the previous existing transform (if any) 463 xyplane : (bool) 464 make an extra rotation to keep the object aligned to the xy-plane 465 rad : (bool) 466 set to True if angle is expressed in radians. 467 468 Example: 469 ```python 470 from vedo import * 471 center = np.array([581/2,723/2,0]) 472 objs = [] 473 for a in np.linspace(0, 6.28, 7): 474 v = vector(cos(a), sin(a), 0)*1000 475 pic = Picture(dataurl+"images/dog.jpg").rotate_z(10) 476 pic.orientation(v, xyplane=True) 477 pic.origin(center) 478 pic.pos(v - center) 479 objs += [pic, Arrow(v, v+v)] 480 show(objs, Point(), axes=1).close() 481 ``` 482  483 484 Examples: 485 - [gyroscope2.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/gyroscope2.py) 486 487  488 """ 489 if self.top is None or self.base is None: 490 initaxis = (0, 0, 1) 491 else: 492 initaxis = utils.versor(self.top - self.base) 493 494 newaxis = utils.versor(newaxis) 495 p = np.array(self.GetPosition()) 496 crossvec = np.cross(initaxis, newaxis) 497 498 angleth = np.arccos(np.dot(initaxis, newaxis)) 499 500 T = vtk.vtkTransform() 501 if concatenate: 502 try: 503 M = self.GetMatrix() 504 T.SetMatrix(M) 505 except: 506 pass 507 T.PostMultiply() 508 T.Translate(-p) 509 if rotation: 510 if rad: 511 rotation *= 180.0 / np.pi 512 T.RotateWXYZ(rotation, initaxis) 513 if xyplane: 514 angleph = np.arctan2(newaxis[1], newaxis[0]) 515 T.RotateWXYZ(np.rad2deg(angleph + angleth), initaxis) # compensation 516 T.RotateWXYZ(np.rad2deg(angleth), crossvec) 517 T.Translate(p) 518 519 self.SetOrientation(T.GetOrientation()) 520 521 self.point_locator = None 522 self.cell_locator = None 523 return self 524 525 # newaxis = utils.versor(newaxis) 526 # pos = np.array(self.GetPosition()) 527 # crossvec = np.cross(initaxis, newaxis) 528 # angle = np.arccos(np.dot(initaxis, newaxis)) 529 # T = vtk.vtkTransform() 530 # T.PostMultiply() 531 # T.Translate(-pos) 532 # if rotation: 533 # T.RotateWXYZ(rotation, initaxis) 534 # T.RotateWXYZ(np.rad2deg(angle), crossvec) 535 # T.Translate(pos) 536 # self.SetUserTransform(T) 537 # self.transform = T
Set/Get object orientation.
Arguments:
- rotation : (float) rotate object around newaxis.
- concatenate : (bool) concatenate the orietation operation with the previous existing transform (if any)
- xyplane : (bool) make an extra rotation to keep the object aligned to the xy-plane
- rad : (bool) set to True if angle is expressed in radians.
Example:
from vedo import * center = np.array([581/2,723/2,0]) objs = [] for a in np.linspace(0, 6.28, 7): v = vector(cos(a), sin(a), 0)*1000 pic = Picture(dataurl+"images/dog.jpg").rotate_z(10) pic.orientation(v, xyplane=True) pic.origin(center) pic.pos(v - center) objs += [pic, Arrow(v, v+v)] show(objs, Point(), axes=1).close()
Examples:
540 def scale(self, s=None, reset=False): 541 """ 542 Set/get object's scaling factor. 543 544 Arguments: 545 s : (list, float) 546 scaling factor(s). 547 reset : (bool) 548 if True previous scaling factors are ignored. 549 550 Note: 551 use `s=(sx,sy,sz)` to scale differently in the three coordinates. 552 """ 553 if s is None: 554 return np.array(self.GetScale()) 555 556 # assert s[0] != 0 557 # assert s[1] != 0 558 # assert s[2] != 0 559 560 if reset: 561 self.SetScale(s) 562 else: 563 self.SetScale(np.multiply(self.GetScale(), s)) 564 565 self.point_locator = None 566 self.cell_locator = None 567 return self
Set/get object's scaling factor.
Arguments:
- s : (list, float) scaling factor(s).
- reset : (bool) if True previous scaling factors are ignored.
Note:
use
s=(sx,sy,sz)
to scale differently in the three coordinates.
569 def get_transform(self, invert=False): 570 """ 571 Check if `object.transform` exists and returns a `vtkTransform`. 572 Otherwise return current user transformation (where the object is currently placed). 573 574 Use `invert` to return the inverse of the current transformation 575 576 Example: 577 ```python 578 from vedo import * 579 580 c1 = Cube() 581 c2 = c1.clone().c('violet').alpha(0.5) # copy of c1 582 v = vector(0.2,1,0) 583 p = vector(1,0,0) # axis passes through this point 584 c2.rotate(90, axis=v, point=p) 585 586 # get the inverse of the current transformation 587 T = c2.get_transform(invert=True) 588 c2.apply_transform(T) # put back c2 in place 589 590 l = Line(p-v, p+v).lw(3).c('red') 591 show(c1.wireframe().lw(3), l, c2, axes=1).close() 592 ``` 593  594 """ 595 if self.transform: 596 tr = self.transform 597 if invert: 598 tr = tr.GetInverse() 599 return tr 600 601 T = self.GetMatrix() 602 tr = vtk.vtkTransform() 603 tr.SetMatrix(T) 604 if invert: 605 tr = tr.GetInverse() 606 return tr
Check if object.transform
exists and returns a vtkTransform
.
Otherwise return current user transformation (where the object is currently placed).
Use invert
to return the inverse of the current transformation
Example:
from vedo import * c1 = Cube() c2 = c1.clone().c('violet').alpha(0.5) # copy of c1 v = vector(0.2,1,0) p = vector(1,0,0) # axis passes through this point c2.rotate(90, axis=v, point=p) # get the inverse of the current transformation T = c2.get_transform(invert=True) c2.apply_transform(T) # put back c2 in place l = Line(p-v, p+v).lw(3).c('red') show(c1.wireframe().lw(3), l, c2, axes=1).close()
609 @deprecated(reason=vedo.colors.red + "Please use apply_transform()" + vedo.colors.reset) 610 def applyTransform(self, T, reset=False, concatenate=False): 611 """Deprecated. Please use `apply_transform()`""" 612 return self.apply_transform(T,reset,concatenate)
Deprecated. Please use apply_transform()
614 def apply_transform(self, T, reset=False, concatenate=False): 615 """ 616 Transform object position and orientation. 617 618 Arguments: 619 reset : (bool) 620 no effect, this is superseded by `pointcloud.apply_transform()` 621 concatenate : (bool) 622 no effect, this is superseded by `pointcloud.apply_transform()` 623 """ 624 if isinstance(T, vtk.vtkMatrix4x4): 625 self.SetUserMatrix(T) 626 elif utils.is_sequence(T): 627 vm = vtk.vtkMatrix4x4() 628 for i in [0, 1, 2, 3]: 629 for j in [0, 1, 2, 3]: 630 vm.SetElement(i, j, T[i][j]) 631 self.SetUserMatrix(vm) 632 else: 633 self.SetUserTransform(T) 634 self.transform = T 635 636 self.point_locator = None 637 self.cell_locator = None 638 return self
Transform object position and orientation.
Arguments:
- reset : (bool)
no effect, this is superseded by
pointcloud.apply_transform()
- concatenate : (bool)
no effect, this is superseded by
pointcloud.apply_transform()
640 def align_to_bounding_box(self, msh, rigid=False): 641 """ 642 Align the current object's bounding box to the bounding box 643 of the input object. 644 645 Use `rigid` to disable scaling. 646 647 Examples: 648 - [align6.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/align6.py) 649 """ 650 lmt = vtk.vtkLandmarkTransform() 651 ss = vtk.vtkPoints() 652 xss0, xss1, yss0, yss1, zss0, zss1 = self.bounds() 653 for p in [ 654 [xss0, yss0, zss0], 655 [xss1, yss0, zss0], 656 [xss1, yss1, zss0], 657 [xss0, yss1, zss0], 658 [xss0, yss0, zss1], 659 [xss1, yss0, zss1], 660 [xss1, yss1, zss1], 661 [xss0, yss1, zss1], 662 ]: 663 ss.InsertNextPoint(p) 664 st = vtk.vtkPoints() 665 xst0, xst1, yst0, yst1, zst0, zst1 = msh.bounds() 666 for p in [ 667 [xst0, yst0, zst0], 668 [xst1, yst0, zst0], 669 [xst1, yst1, zst0], 670 [xst0, yst1, zst0], 671 [xst0, yst0, zst1], 672 [xst1, yst0, zst1], 673 [xst1, yst1, zst1], 674 [xst0, yst1, zst1], 675 ]: 676 st.InsertNextPoint(p) 677 678 lmt.SetSourceLandmarks(ss) 679 lmt.SetTargetLandmarks(st) 680 lmt.SetModeToAffine() 681 if rigid: 682 lmt.SetModeToRigidBody() 683 lmt.Update() 684 self.apply_transform(lmt) 685 self.transform = lmt 686 687 self.point_locator = None 688 self.cell_locator = None 689 return self
Align the current object's bounding box to the bounding box of the input object.
Use rigid
to disable scaling.
Examples:
691 def on(self): 692 """Switch on object visibility. Object is not removed.""" 693 self.VisibilityOn() 694 try: 695 self.scalarbar.VisibilityOn() 696 except AttributeError: 697 pass 698 try: 699 self.trail.VisibilityOn() 700 except AttributeError: 701 pass 702 try: 703 for sh in self.shadows: 704 sh.VisibilityOn() 705 except AttributeError: 706 pass 707 return self
Switch on object visibility. Object is not removed.
709 def off(self): 710 """Switch off object visibility. Object is not removed.""" 711 self.VisibilityOff() 712 try: 713 self.scalarbar.VisibilityOff() 714 except AttributeError: 715 pass 716 try: 717 self.trail.VisibilityOff() 718 except AttributeError: 719 pass 720 try: 721 for sh in self.shadows: 722 sh.VisibilityOff() 723 except AttributeError: 724 pass 725 return self
Switch off object visibility. Object is not removed.
727 def toggle(self): 728 """Toggle object visibility on/off.""" 729 v = self.GetVisibility() 730 if v: 731 self.off() 732 else: 733 self.on() 734 return self
Toggle object visibility on/off.
736 def box(self, scale=1, padding=0, fill=False): 737 """ 738 Return the bounding box as a new `Mesh`. 739 740 Arguments: 741 scale : (float) 742 box size can be scaled by a factor 743 padding : (float, list) 744 a constant padding can be added (can be a list [padx,pady,padz]) 745 746 Examples: 747 - [latex.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/latex.py) 748 """ 749 b = self.bounds() 750 if not utils.is_sequence(padding): 751 padding = [padding, padding, padding] 752 length, width, height = b[1] - b[0], b[3] - b[2], b[5] - b[4] 753 tol = (length + width + height) / 30000 # useful for boxing 2D text 754 pos = [(b[0] + b[1]) / 2, (b[3] + b[2]) / 2, (b[5] + b[4]) / 2 - tol] 755 bx = vedo.shapes.Box( 756 pos, 757 length * scale + padding[0], 758 width * scale + padding[1], 759 height * scale + padding[2], 760 c="gray", 761 ) 762 if hasattr(self, "GetProperty"): # could be Assembly 763 if isinstance(self.GetProperty(), vtk.vtkProperty): # could be volume 764 pr = vtk.vtkProperty() 765 pr.DeepCopy(self.GetProperty()) 766 bx.SetProperty(pr) 767 bx.property = pr 768 bx.wireframe(not fill) 769 bx.flat().lighting("off") 770 return bx
Return the bounding box as a new Mesh
.
Arguments:
- scale : (float) box size can be scaled by a factor
- padding : (float, list) a constant padding can be added (can be a list [padx,pady,padz])
Examples:
772 def use_bounds(self, ub=True): 773 """ 774 Instruct the current camera to either take into account or ignore 775 the object bounds when resetting. 776 """ 777 self.SetUseBounds(ub) 778 return self
Instruct the current camera to either take into account or ignore the object bounds when resetting.
780 def bounds(self): 781 """ 782 Get the object bounds. 783 Returns a list in format `[xmin,xmax, ymin,ymax, zmin,zmax]`. 784 """ 785 try: 786 pts = self.points() 787 xmin, ymin, zmin = np.min(pts, axis=0) 788 xmax, ymax, zmax = np.max(pts, axis=0) 789 return [xmin,xmax, ymin,ymax, zmin,zmax] 790 except (AttributeError, ValueError): 791 return self.GetBounds()
Get the object bounds.
Returns a list in format [xmin,xmax, ymin,ymax, zmin,zmax]
.
793 def xbounds(self, i=None): 794 """Get the bounds `[xmin,xmax]`. Can specify upper or lower with i (0,1).""" 795 b = self.bounds() 796 if i is not None: 797 return b[i] 798 return (b[0], b[1])
Get the bounds [xmin,xmax]
. Can specify upper or lower with i (0,1).
800 def ybounds(self, i=None): 801 """Get the bounds `[ymin,ymax]`. Can specify upper or lower with i (0,1).""" 802 b = self.bounds() 803 if i == 0: 804 return b[2] 805 if i == 1: 806 return b[3] 807 return (b[2], b[3])
Get the bounds [ymin,ymax]
. Can specify upper or lower with i (0,1).
809 def zbounds(self, i=None): 810 """Get the bounds `[zmin,zmax]`. Can specify upper or lower with i (0,1).""" 811 b = self.bounds() 812 if i == 0: return b[4] 813 if i == 1: return b[5] 814 return (b[4], b[5])
Get the bounds [zmin,zmax]
. Can specify upper or lower with i (0,1).
816 @deprecated(reason=vedo.colors.red + "Please use diagonal_size()" + vedo.colors.reset) 817 def diagonalSize(self): 818 """Deprecated. Please use `diagonal_size()`.""" 819 return self.diagonal_size()
Deprecated. Please use diagonal_size()
.
821 def diagonal_size(self): 822 """Get the length of the diagonal of mesh bounding box.""" 823 b = self.bounds() 824 return np.sqrt((b[1] - b[0]) ** 2 + (b[3] - b[2]) ** 2 + (b[5] - b[4]) ** 2) 825 # return self.GetLength() # ???different???
Get the length of the diagonal of mesh bounding box.
828 def copy_data_from(self, obj): 829 """Copy all data (point and cell data) from this input object""" 830 self._data.GetPointData().PassData(obj._data.GetPointData()) 831 self._data.GetCellData().PassData(obj._data.GetCellData()) 832 self.pipeline = utils.OperationNode( 833 f"copy_data_from\n{obj.__class__.__name__}", 834 parents=[self, obj], 835 shape='note', 836 c="#ccc5b9", 837 ) 838 return self
Copy all data (point and cell data) from this input object
840 def print(self): 841 """Print information about an object.""" 842 utils.print_info(self) 843 return self
Print information about an object.
845 def show(self, **options): 846 """ 847 Create on the fly an instance of class `Plotter` or use the last existing one to 848 show one single object. 849 850 This method is meant as a shortcut. If more than one object needs to be visualised 851 please use the syntax `show(mesh1, mesh2, volume, ..., options)`. 852 853 Returns the `Plotter` class instance. 854 """ 855 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.
857 def thumbnail( 858 self, 859 zoom=1.25, 860 size=(200, 200), 861 bg="white", 862 azimuth=0, 863 elevation=0, 864 axes=False, 865 ): 866 """Build a thumbnail of the object and return it as an array.""" 867 # speed is about 20Hz for size=[200,200] 868 ren = vtk.vtkRenderer() 869 ren.AddActor(self) 870 if axes: 871 axes = vedo.addons.Axes(self) 872 ren.AddActor(axes) 873 ren.ResetCamera() 874 cam = ren.GetActiveCamera() 875 cam.Zoom(zoom) 876 cam.Elevation(elevation) 877 cam.Azimuth(azimuth) 878 879 ren_win = vtk.vtkRenderWindow() 880 ren_win.SetOffScreenRendering(True) 881 ren_win.SetSize(size) 882 ren.SetBackground(colors.get_color(bg)) 883 ren_win.AddRenderer(ren) 884 ren_win.Render() 885 886 nx, ny = ren_win.GetSize() 887 arr = vtk.vtkUnsignedCharArray() 888 ren_win.GetRGBACharPixelData(0, 0, nx-1, ny-1, 0, arr) 889 narr = utils.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3]) 890 narr = np.ascontiguousarray(np.flip(narr, axis=0)) 891 892 ren.RemoveActor(self) 893 if axes: 894 ren.RemoveActor(axes) 895 ren_win.Finalize() 896 del ren_win 897 return narr
Build a thumbnail of the object and return it as an array.
997class BaseActor(Base3DProp): 998 """ 999 Base class. 1000 1001 .. warning:: Do not use this class to instanciate objects, use one the above instead. 1002 """ 1003 def __init__(self): 1004 """ 1005 Base class to add operative and data 1006 functionality to `Mesh`, `Assembly`, `Volume` and `Picture` objects. 1007 """ 1008 1009 super().__init__() 1010 1011 self._mapper = None 1012 self._caption = None 1013 self.property = None 1014 1015 1016 def mapper(self, new_mapper=None): 1017 """Return the `vtkMapper` data object, or update it with a new one.""" 1018 if new_mapper: 1019 self.SetMapper(new_mapper) 1020 if self._mapper: 1021 iptdata = self._mapper.GetInput() 1022 if iptdata: 1023 new_mapper.SetInputData(self._mapper.GetInput()) 1024 self._mapper = new_mapper 1025 self._mapper.Modified() 1026 return self._mapper 1027 1028 def inputdata(self): 1029 """Return the VTK input data object.""" 1030 if self._mapper: 1031 return self._mapper.GetInput() 1032 return self.GetMapper().GetInput() 1033 1034 def modified(self): 1035 """Use in conjunction with `tonumpy()` 1036 to update any modifications to the volume array""" 1037 sc = self.inputdata().GetPointData().GetScalars() 1038 if sc: 1039 sc.Modified() 1040 self.inputdata().GetPointData().Modified() 1041 return self 1042 1043 @deprecated(reason=vedo.colors.red + "Please use property object.npoints" + vedo.colors.reset) 1044 def N(self): 1045 """Deprecated. Please use property object.npoints""" 1046 return self.inputdata().GetNumberOfPoints() 1047 1048 @deprecated(reason=vedo.colors.red + "Please use property object.npoints" + vedo.colors.reset) 1049 def NPoints(self): 1050 """Deprecated. Please use property object.npoints""" 1051 return self.inputdata().GetNumberOfPoints() 1052 1053 @deprecated(reason=vedo.colors.red + "Please use property object.ncells" + vedo.colors.reset) 1054 def NCells(self): 1055 """Deprecated. Please use property object.ncells""" 1056 return self.inputdata().GetNumberOfCells() 1057 1058 @property 1059 def npoints(self): 1060 """Retrieve the number of points.""" 1061 return self.inputdata().GetNumberOfPoints() 1062 1063 @property 1064 def ncells(self): 1065 """Retrieve the number of cells.""" 1066 return self.inputdata().GetNumberOfCells() 1067 1068 1069 def points(self, pts=None, transformed=True): 1070 """ 1071 Set/Get the vertex coordinates of a mesh or point cloud. 1072 Argument can be an index, a set of indices 1073 or a complete new set of points to update the mesh. 1074 1075 Set `transformed=False` to ignore any previous transformation applied to the mesh. 1076 """ 1077 if pts is None: ### getter 1078 1079 if isinstance(self, vedo.Points): 1080 vpts = self.polydata(transformed).GetPoints() 1081 elif isinstance(self, vedo.BaseVolume): 1082 v2p = vtk.vtkImageToPoints() 1083 v2p.SetInputData(self.imagedata()) 1084 v2p.Update() 1085 vpts = v2p.GetOutput().GetPoints() 1086 else: # tetmesh et al 1087 vpts = self.inputdata().GetPoints() 1088 1089 if vpts: 1090 return utils.vtk2numpy(vpts.GetData()) 1091 return np.array([], dtype=np.float32) 1092 1093 else: ### setter 1094 1095 if len(pts) == 3 and len(pts[0]) != 3: 1096 # assume plist is in the format [all_x, all_y, all_z] 1097 pts = np.stack((pts[0], pts[1], pts[2]), axis=1) 1098 pts = np.asarray(pts, dtype=np.float32) 1099 if pts.shape[1] == 2: 1100 pts = np.c_[pts, np.zeros(pts.shape[0], dtype=np.float32)] 1101 vpts = self.inputdata().GetPoints() 1102 arr = utils.numpy2vtk(pts, dtype=np.float32) 1103 vpts.SetData(arr) 1104 vpts.Modified() 1105 # reset mesh to identity matrix position/rotation: 1106 self.PokeMatrix(vtk.vtkMatrix4x4()) 1107 self.point_locator = None 1108 self.cell_locator = None 1109 return self 1110 1111 @deprecated(reason=vedo.colors.red + "Please use cell_centers()" + vedo.colors.reset) 1112 def cellCenters(self): 1113 """Deprecated. Please use `cell_centers()`""" 1114 return self.cell_centers() 1115 1116 def cell_centers(self): 1117 """ 1118 Get the coordinates of the cell centers. 1119 1120 Examples: 1121 - [delaunay2d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/delaunay2d.py) 1122 """ 1123 vcen = vtk.vtkCellCenters() 1124 if hasattr(self, "polydata"): 1125 vcen.SetInputData(self.polydata()) 1126 else: 1127 vcen.SetInputData(self.inputdata()) 1128 vcen.Update() 1129 return utils.vtk2numpy(vcen.GetOutput().GetPoints().GetData()) 1130 1131 def delete_cells(self, ids): 1132 """ 1133 Remove cells from the mesh object by their ID. 1134 Points (vertices) are not removed (you may use `.clean()` to remove those). 1135 """ 1136 data = self.inputdata() 1137 data.BuildLinks() 1138 for cid in ids: 1139 data.DeleteCell(cid) 1140 data.RemoveDeletedCells() 1141 data.Modified() 1142 self._mapper.Modified() 1143 self.pipeline = utils.OperationNode( 1144 "delete_cells", parents=[self], 1145 comment=f"#cells {self._data.GetNumberOfCells()}", 1146 ) 1147 return self 1148 1149 def mark_boundaries(self): 1150 """Mark cells and vertices of the mesh if they lie on a boundary.""" 1151 mb = vtk.vtkMarkBoundaryFilter() 1152 mb.SetInputData(self._data) 1153 mb.Update() 1154 out = self._update(mb.GetOutput()) 1155 out.pipeline = utils.OperationNode("mark_boundaries", parents=[self]) 1156 return out 1157 1158 def find_cells_in(self, xbounds=(), ybounds=(), zbounds=()): 1159 """ 1160 Find cells that are within the specified bounds. 1161 Setting a color will add a vtk array to colorize these cells. 1162 """ 1163 if len(xbounds) == 6: 1164 bnds = xbounds 1165 else: 1166 bnds = list(self.bounds()) 1167 if len(xbounds) == 2: 1168 bnds[0] = xbounds[0] 1169 bnds[1] = xbounds[1] 1170 if len(ybounds) == 2: 1171 bnds[2] = ybounds[0] 1172 bnds[3] = ybounds[1] 1173 if len(zbounds) == 2: 1174 bnds[4] = zbounds[0] 1175 bnds[5] = zbounds[1] 1176 1177 cellIds = vtk.vtkIdList() 1178 self.cell_locator = vtk.vtkCellTreeLocator() 1179 self.cell_locator.SetDataSet(self.polydata()) 1180 self.cell_locator.BuildLocator() 1181 self.cell_locator.FindCellsWithinBounds(bnds, cellIds) 1182 1183 cids = [] 1184 for i in range(cellIds.GetNumberOfIds()): 1185 cid = cellIds.GetId(i) 1186 cids.append(cid) 1187 1188 return np.array(cids) 1189 1190 def count_vertices(self): 1191 """Count the number of vertices each cell has and return it as a numpy array""" 1192 vc = vtk.vtkCountVertices() 1193 vc.SetInputData(self._data) 1194 vc.SetOutputArrayName("VertexCount") 1195 vc.Update() 1196 varr = vc.GetOutput().GetCellData().GetArray("VertexCount") 1197 return utils.vtk2numpy(varr) 1198 1199 def lighting( 1200 self, 1201 style="", 1202 ambient=None, 1203 diffuse=None, 1204 specular=None, 1205 specular_power=None, 1206 specular_color=None, 1207 metallicity=None, 1208 roughness=None, 1209 ): 1210 """ 1211 Set the ambient, diffuse, specular and specular_power lighting constants. 1212 1213 Arguments: 1214 style : (str) 1215 preset style, options are `[metallic, plastic, shiny, glossy, ambient, off]` 1216 ambient : (float) 1217 ambient fraction of emission [0-1] 1218 diffuse : (float) 1219 emission of diffused light in fraction [0-1] 1220 specular : (float) 1221 fraction of reflected light [0-1] 1222 specular_power : (float) 1223 precision of reflection [1-100] 1224 specular_color : (color) 1225 color that is being reflected by the surface 1226 1227 <img src="https://upload.wikimedia.org/wikipedia/commons/6/6b/Phong_components_version_4.png" alt="", width=700px> 1228 1229 Examples: 1230 - [specular.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/specular.py) 1231 """ 1232 pr = self.GetProperty() 1233 1234 if style: 1235 1236 if isinstance(pr, vtk.vtkVolumeProperty): 1237 self.shade(True) 1238 if style == "off": 1239 self.