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