vedo.visual
Base classes to manage visualization and apperance of objects and their properties"
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3import os 4import numpy as np 5from weakref import ref as weak_ref_to 6 7import vedo.vtkclasses as vtk 8 9import vedo 10from vedo import colors 11from vedo import utils 12 13 14__docformat__ = "google" 15 16__doc__ = """ 17Base classes to manage visualization and apperance of objects and their properties" 18""" 19 20__all__ = [ 21 "CommonVisual", 22 "PointsVisual", 23 "VolumeVisual", 24 "MeshVisual", 25 "ImageVisual", 26 "Actor2D", 27] 28 29 30################################################### 31class CommonVisual: 32 """Class to manage the visual aspects common to all objects.""" 33 34 def __init__(self): 35 # print("init CommonVisual") 36 self.mapper = None 37 self.properties = None 38 self.actor = None 39 self.scalarbar = None 40 self.trail = None 41 self.shadows = [] 42 43 def print(self): 44 """Print object info.""" 45 print(self.__str__()) 46 return self 47 48 @property 49 def LUT(self): 50 """Return the lookup table of the object as a numpy object.""" 51 try: 52 _lut = self.mapper.GetLookupTable() 53 54 values = [] 55 for i in range(_lut.GetTable().GetNumberOfTuples()): 56 # print("LUT i =", i, "value =", _lut.GetTableValue(i)) 57 values.append(_lut.GetTableValue(i)) 58 return np.array(values) 59 except AttributeError: 60 pass 61 62 @LUT.setter 63 def LUT(self, arr): 64 """ 65 Set the lookup table of the object from a numpy object. 66 Consider using `cmap()` or `build_lut()` instead as it allows 67 to set the range of the LUT and to use a string name for the color map. 68 """ 69 _newlut = vtk.vtkLookupTable() 70 _newlut.SetNumberOfTableValues(len(arr)) 71 if len(arr[0]) == 3: 72 arr = np.insert(arr, 3, 1, axis=1) 73 for i, v in enumerate(arr): 74 _newlut.SetTableValue(i, v) 75 _newlut.SetRange(self.mapper.GetScalarRange()) 76 _newlut.Build() 77 self.mapper.SetLookupTable(_newlut) 78 self.mapper.ScalarVisibilityOn() 79 80 def add_observer(self, event_name, func, priority=0): 81 """Add a callback function that will be called when an event occurs.""" 82 event_name = utils.get_vtk_name_event(event_name) 83 idd = self.actor.AddObserver(event_name, func, priority) 84 return idd 85 86 def show(self, **options): 87 """ 88 Create on the fly an instance of class `Plotter` or use the last existing one to 89 show one single object. 90 91 This method is meant as a shortcut. If more than one object needs to be visualised 92 please use the syntax `show(mesh1, mesh2, volume, ..., options)`. 93 94 Returns the `Plotter` class instance. 95 """ 96 return vedo.plotter.show(self, **options) 97 98 def thumbnail(self, zoom=1.25, size=(200, 200), bg="white", azimuth=0, elevation=0, axes=False): 99 """Build a thumbnail of the object and return it as an array.""" 100 # speed is about 20Hz for size=[200,200] 101 ren = vtk.vtkRenderer() 102 103 actor = self.actor 104 if isinstance(self, vedo.UnstructuredGrid): 105 geo = vtk.new("GeometryFilter") 106 geo.SetInputData(self.dataset) 107 geo.Update() 108 actor = vedo.Mesh(geo.GetOutput()).cmap("rainbow").actor 109 110 ren.AddActor(actor) 111 if axes: 112 axes = vedo.addons.Axes(self) 113 ren.AddActor(axes.actor) 114 ren.ResetCamera() 115 cam = ren.GetActiveCamera() 116 cam.Zoom(zoom) 117 cam.Elevation(elevation) 118 cam.Azimuth(azimuth) 119 120 ren_win = vtk.vtkRenderWindow() 121 ren_win.SetOffScreenRendering(True) 122 ren_win.SetSize(size) 123 ren.SetBackground(colors.get_color(bg)) 124 ren_win.AddRenderer(ren) 125 ren_win.Render() 126 127 nx, ny = ren_win.GetSize() 128 arr = vtk.vtkUnsignedCharArray() 129 ren_win.GetRGBACharPixelData(0, 0, nx - 1, ny - 1, 0, arr) 130 narr = utils.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3]) 131 narr = np.ascontiguousarray(np.flip(narr, axis=0)) 132 133 ren.RemoveActor(actor) 134 if axes: 135 ren.RemoveActor(axes.actor) 136 ren_win.Finalize() 137 del ren_win 138 return narr 139 140 def pickable(self, value=None): 141 """Set/get the pickability property of an object.""" 142 if value is None: 143 return self.actor.GetPickable() 144 self.actor.SetPickable(value) 145 return self 146 147 def use_bounds(self, value=True): 148 """ 149 Instruct the current camera to either take into account or ignore 150 the object bounds when resetting. 151 """ 152 self.actor.SetUseBounds(value) 153 return self 154 155 def draggable(self, value=None): # NOT FUNCTIONAL? 156 """Set/get the draggability property of an object.""" 157 if value is None: 158 return self.actor.GetDragable() 159 self.actor.SetDragable(value) 160 return self 161 162 def on(self): 163 """Switch on object visibility. Object is not removed.""" 164 self.actor.VisibilityOn() 165 try: 166 self.scalarbar.actor.VisibilityOn() 167 except AttributeError: 168 pass 169 try: 170 self.trail.actor.VisibilityOn() 171 except AttributeError: 172 pass 173 try: 174 for sh in self.shadows: 175 sh.actor.VisibilityOn() 176 except AttributeError: 177 pass 178 return self 179 180 def off(self): 181 """Switch off object visibility. Object is not removed.""" 182 self.actor.VisibilityOff() 183 try: 184 self.scalarbar.actor.VisibilityOff() 185 except AttributeError: 186 pass 187 try: 188 self.trail.actor.VisibilityOff() 189 except AttributeError: 190 pass 191 try: 192 for sh in self.shadows: 193 sh.actor.VisibilityOff() 194 except AttributeError: 195 pass 196 return self 197 198 def toggle(self): 199 """Toggle object visibility on/off.""" 200 v = self.actor.GetVisibility() 201 if v: 202 self.off() 203 else: 204 self.on() 205 return self 206 207 def add_scalarbar( 208 self, 209 title="", 210 pos=(0.775, 0.05), 211 title_yoffset=15, 212 font_size=12, 213 size=(60, 350), 214 nlabels=None, 215 c=None, 216 horizontal=False, 217 use_alpha=True, 218 label_format=":6.3g", 219 ): 220 """ 221 Add a 2D scalar bar for the specified obj. 222 223 Arguments: 224 title : (str) 225 scalar bar title 226 pos : (float,float) 227 position coordinates of the bottom left corner 228 title_yoffset : (float) 229 vertical space offset between title and color scalarbar 230 font_size : (float) 231 size of font for title and numeric labels 232 size : (float,float) 233 size of the scalarbar in number of pixels (width, height) 234 nlabels : (int) 235 number of numeric labels 236 c : (list) 237 color of the scalar bar text 238 horizontal : (bool) 239 lay the scalarbar horizontally 240 use_alpha : (bool) 241 render transparency in the color bar itself 242 label_format : (str) 243 c-style format string for numeric labels 244 245 Examples: 246 - [mesh_coloring.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_coloring.py) 247 - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py) 248 """ 249 plt = vedo.plotter_instance 250 251 if plt and plt.renderer: 252 c = (0.9, 0.9, 0.9) 253 if np.sum(plt.renderer.GetBackground()) > 1.5: 254 c = (0.1, 0.1, 0.1) 255 if isinstance(self.scalarbar, vtk.vtkActor): 256 plt.renderer.RemoveActor(self.scalarbar) 257 elif isinstance(self.scalarbar, vedo.Assembly): 258 for a in self.scalarbar.unpack(): 259 plt.renderer.RemoveActor(a) 260 if c is None: 261 c = "gray" 262 263 sb = vedo.addons.ScalarBar( 264 self, 265 title, 266 pos, 267 title_yoffset, 268 font_size, 269 size, 270 nlabels, 271 c, 272 horizontal, 273 use_alpha, 274 label_format, 275 ) 276 self.scalarbar = sb 277 return self 278 279 def add_scalarbar3d( 280 self, 281 title="", 282 pos=None, 283 size=(0, 0), 284 title_font="", 285 title_xoffset=-1.2, 286 title_yoffset=0.0, 287 title_size=1.5, 288 title_rotation=0.0, 289 nlabels=9, 290 label_font="", 291 label_size=1, 292 label_offset=0.375, 293 label_rotation=0, 294 label_format="", 295 italic=0, 296 c=None, 297 draw_box=True, 298 above_text=None, 299 below_text=None, 300 nan_text="NaN", 301 categories=None, 302 ): 303 """ 304 Associate a 3D scalar bar to the object and add it to the scene. 305 The new scalarbar object (Assembly) will be accessible as obj.scalarbar 306 307 Arguments: 308 size : (list) 309 (thickness, length) of scalarbar 310 title : (str) 311 scalar bar title 312 title_xoffset : (float) 313 horizontal space btw title and color scalarbar 314 title_yoffset : (float) 315 vertical space offset 316 title_size : (float) 317 size of title wrt numeric labels 318 title_rotation : (float) 319 title rotation in degrees 320 nlabels : (int) 321 number of numeric labels 322 label_font : (str) 323 font type for labels 324 label_size : (float) 325 label scale factor 326 label_offset : (float) 327 space btw numeric labels and scale 328 label_rotation : (float) 329 label rotation in degrees 330 label_format : (str) 331 label format for floats and integers (e.g. `':.2f'`) 332 draw_box : (bool) 333 draw a box around the colorbar 334 categories : (list) 335 make a categorical scalarbar, 336 the input list will have the format `[value, color, alpha, textlabel]` 337 338 Examples: 339 - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py) 340 """ 341 plt = vedo.plotter_instance 342 if plt and c is None: # automatic black or white 343 c = (0.9, 0.9, 0.9) 344 if np.sum(vedo.get_color(plt.backgrcol)) > 1.5: 345 c = (0.1, 0.1, 0.1) 346 if c is None: 347 c = (0, 0, 0) 348 c = vedo.get_color(c) 349 350 self.scalarbar = vedo.addons.ScalarBar3D( 351 self, 352 title, 353 pos, 354 size, 355 title_font, 356 title_xoffset, 357 title_yoffset, 358 title_size, 359 title_rotation, 360 nlabels, 361 label_font, 362 label_size, 363 label_offset, 364 label_rotation, 365 label_format, 366 italic, 367 c, 368 draw_box, 369 above_text, 370 below_text, 371 nan_text, 372 categories, 373 ) 374 return self 375 376 def color(self, col, alpha=None, vmin=None, vmax=None): 377 """ 378 Assign a color or a set of colors along the range of the scalar value. 379 A single constant color can also be assigned. 380 Any matplotlib color map name is also accepted, e.g. `volume.color('jet')`. 381 382 E.g.: say that your cells scalar runs from -3 to 6, 383 and you want -3 to show red and 1.5 violet and 6 green, then just set: 384 385 `volume.color(['red', 'violet', 'green'])` 386 387 You can also assign a specific color to a aspecific value with eg.: 388 389 `volume.color([(0,'red'), (0.5,'violet'), (1,'green')])` 390 391 Arguments: 392 alpha : (list) 393 use a list to specify transparencies along the scalar range 394 vmin : (float) 395 force the min of the scalar range to be this value 396 vmax : (float) 397 force the max of the scalar range to be this value 398 """ 399 # supersedes method in Points, Mesh 400 401 if col is None: 402 return self 403 404 if vmin is None: 405 vmin, _ = self.dataset.GetScalarRange() 406 if vmax is None: 407 _, vmax = self.dataset.GetScalarRange() 408 ctf = self.properties.GetRGBTransferFunction() 409 ctf.RemoveAllPoints() 410 411 if utils.is_sequence(col): 412 if utils.is_sequence(col[0]) and len(col[0]) == 2: 413 # user passing [(value1, color1), ...] 414 for x, ci in col: 415 r, g, b = colors.get_color(ci) 416 ctf.AddRGBPoint(x, r, g, b) 417 # colors.printc('color at', round(x, 1), 418 # 'set to', colors.get_color_name((r, g, b)), bold=0) 419 else: 420 # user passing [color1, color2, ..] 421 for i, ci in enumerate(col): 422 r, g, b = colors.get_color(ci) 423 x = vmin + (vmax - vmin) * i / (len(col) - 1) 424 ctf.AddRGBPoint(x, r, g, b) 425 elif isinstance(col, str): 426 if col in colors.colors.keys() or col in colors.color_nicks.keys(): 427 r, g, b = colors.get_color(col) 428 ctf.AddRGBPoint(vmin, r, g, b) # constant color 429 ctf.AddRGBPoint(vmax, r, g, b) 430 else: # assume it's a colormap 431 for x in np.linspace(vmin, vmax, num=64, endpoint=True): 432 r, g, b = colors.color_map(x, name=col, vmin=vmin, vmax=vmax) 433 ctf.AddRGBPoint(x, r, g, b) 434 elif isinstance(col, int): 435 r, g, b = colors.get_color(col) 436 ctf.AddRGBPoint(vmin, r, g, b) # constant color 437 ctf.AddRGBPoint(vmax, r, g, b) 438 else: 439 vedo.logger.warning(f"in color() unknown input type {type(col)}") 440 441 if alpha is not None: 442 self.alpha(alpha, vmin=vmin, vmax=vmax) 443 return self 444 445 def alpha(self, alpha, vmin=None, vmax=None): 446 """ 447 Assign a set of tranparencies along the range of the scalar value. 448 A single constant value can also be assigned. 449 450 E.g.: say `alpha=(0.0, 0.3, 0.9, 1)` and the scalar range goes from -10 to 150. 451 Then all cells with a value close to -10 will be completely transparent, cells at 1/4 452 of the range will get an alpha equal to 0.3 and voxels with value close to 150 453 will be completely opaque. 454 455 As a second option one can set explicit (x, alpha_x) pairs to define the transfer function. 456 457 E.g.: say `alpha=[(-5, 0), (35, 0.4) (123,0.9)]` and the scalar range goes from -10 to 150. 458 Then all cells below -5 will be completely transparent, cells with a scalar value of 35 459 will get an opacity of 40% and above 123 alpha is set to 90%. 460 """ 461 if vmin is None: 462 vmin, _ = self.dataset.GetScalarRange() 463 if vmax is None: 464 _, vmax = self.dataset.GetScalarRange() 465 otf = self.properties.GetScalarOpacity() 466 otf.RemoveAllPoints() 467 468 if utils.is_sequence(alpha): 469 alpha = np.array(alpha) 470 if len(alpha.shape) == 1: # user passing a flat list e.g. (0.0, 0.3, 0.9, 1) 471 for i, al in enumerate(alpha): 472 xalpha = vmin + (vmax - vmin) * i / (len(alpha) - 1) 473 # Create transfer mapping scalar value to opacity 474 otf.AddPoint(xalpha, al) 475 # print("alpha at", round(xalpha, 1), "\tset to", al) 476 elif len(alpha.shape) == 2: # user passing [(x0,alpha0), ...] 477 otf.AddPoint(vmin, alpha[0][1]) 478 for xalpha, al in alpha: 479 # Create transfer mapping scalar value to opacity 480 otf.AddPoint(xalpha, al) 481 otf.AddPoint(vmax, alpha[-1][1]) 482 483 else: 484 485 otf.AddPoint(vmin, alpha) # constant alpha 486 otf.AddPoint(vmax, alpha) 487 488 return self 489 490 491######################################################################################## 492class Actor2D(vtk.vtkActor2D): 493 """Wrapping of `vtkActor2D`.""" 494 495 def __init__(self): 496 """Manage 2D objects.""" 497 super().__init__() 498 499 self.mapper = self.GetMapper() 500 self.properties = self.GetProperty() 501 self.filename = "" 502 self.shape = [] 503 504 def layer(self, value=None): 505 """Set/Get the layer number in the overlay planes into which to render.""" 506 if value is None: 507 return self.GetLayerNumber() 508 self.SetLayerNumber(value) 509 return self 510 511 def pos(self, px=None, py=None): 512 """Set/Get the screen-coordinate position.""" 513 if isinstance(px, str): 514 vedo.logger.error("Use string descriptors only inside the constructor") 515 return self 516 if px is None: 517 return np.array(self.GetPosition(), dtype=int) 518 if py is not None: 519 p = [px, py] 520 else: 521 p = px 522 assert len(p) == 2, "Error: len(pos) must be 2 for Actor2D" 523 self.SetPosition(p) 524 return self 525 526 def coordinate_system(self, value=None): 527 """ 528 Set/get the coordinate system which this coordinate is defined in. 529 530 The options are: 531 0. Display 532 1. Normalized Display 533 2. Viewport 534 3. Normalized Viewport 535 4. View 536 5. Pose 537 6. World 538 """ 539 coor = self.GetPositionCoordinate() 540 if value is None: 541 return coor.GetCoordinateSystem() 542 coor.SetCoordinateSystem(value) 543 return self 544 545 def on(self): 546 """Set object visibility.""" 547 self.VisibilityOn() 548 return self 549 550 def off(self): 551 """Set object visibility.""" 552 self.VisibilityOn() 553 return self 554 555 def toggle(self): 556 """Toggle object visibility.""" 557 self.SetVisibility(not self.GetVisibility()) 558 return self 559 560 def pickable(self, value=True): 561 """Set object pickability.""" 562 self.SetPickable(value) 563 return self 564 565 def color(self, value=None): 566 """Set/Get the object color.""" 567 if value is None: 568 return self.properties.GetColor() 569 self.properties.SetColor(colors.get_color(value)) 570 return self 571 572 def c(self, value=None): 573 """Set/Get the object color.""" 574 return self.color(value) 575 576 def alpha(self, value=None): 577 """Set/Get the object opacity.""" 578 if value is None: 579 return self.properties.GetOpacity() 580 self.properties.SetOpacity(value) 581 return self 582 583 def ps(self, point_size=None): 584 if point_size is None: 585 return self.properties.GetPointSize() 586 self.properties.SetPointSize(point_size) 587 return self 588 589 def ontop(self, value=True): 590 """Keep the object always on top of everything else.""" 591 if value: 592 self.properties.SetDisplayLocationToForeground() 593 else: 594 self.properties.SetDisplayLocationToBackground() 595 return self 596 597 def add_observer(self, event_name, func, priority=0): 598 """Add a callback function that will be called when an event occurs.""" 599 event_name = utils.get_vtk_name_event(event_name) 600 idd = self.AddObserver(event_name, func, priority) 601 return idd 602 603######################################################################################## 604class Actor3DHelper: 605 606 def apply_transform(self, LT, concatenate=True): 607 """Apply a linear transformation to the actor.""" 608 if concatenate: 609 self.transform.concatenate(LT) 610 self.actor.SetPosition(self.transform.T.GetPosition()) 611 self.actor.SetOrientation(self.transform.T.GetOrientation()) 612 self.actor.SetScale(self.transform.T.GetScale()) 613 return self 614 615 def pos(self, x=None, y=None, z=None): 616 """Set/Get object position.""" 617 if x is None: # get functionality 618 return self.transform.position 619 620 if z is None and y is None: # assume x is of the form (x,y,z) 621 if len(x) == 3: 622 x, y, z = x 623 else: 624 x, y = x 625 z = 0 626 elif z is None: # assume x,y is of the form x, y 627 z = 0 628 629 q = self.transform.position 630 LT = vedo.LinearTransform().translate([x,y,z]-q) 631 return self.apply_transform(LT) 632 633 def shift(self, dx, dy=0, dz=0): 634 """Add a vector to the current object position.""" 635 if vedo.utils.is_sequence(dx): 636 vedo.utils.make3d(dx) 637 dx, dy, dz = dx 638 LT = vedo.LinearTransform().translate([dx, dy, dz]) 639 return self.apply_transform(LT) 640 641 def origin(self, point=None): 642 """ 643 Set/get origin of object. 644 Useful for defining pivoting point when rotating and/or scaling. 645 """ 646 if point is None: 647 return np.array(self.actor.GetOrigin()) 648 self.actor.SetOrigin(point) 649 return self 650 651 def scale(self, s): 652 """Multiply object size by `s` factor.""" 653 LT = vedo.LinearTransform().scale(s) 654 return self.apply_transform(LT) 655 656 def x(self, val=None): 657 """Set/Get object position along x axis.""" 658 p = self.transform.position 659 if val is None: 660 return p[0] 661 self.pos(val, p[1], p[2]) 662 return self 663 664 def y(self, val=None): 665 """Set/Get object position along y axis.""" 666 p = self.transform.position 667 if val is None: 668 return p[1] 669 self.pos(p[0], val, p[2]) 670 return self 671 672 def z(self, val=None): 673 """Set/Get object position along z axis.""" 674 p = self.transform.position 675 if val is None: 676 return p[2] 677 self.pos(p[0], p[1], val) 678 return self 679 680 def rotate_x(self, angle): 681 """Rotate object around x axis.""" 682 LT = vedo.LinearTransform().rotate_x(angle) 683 return self.apply_transform(LT) 684 685 def rotate_y(self, angle): 686 """Rotate object around y axis.""" 687 LT = vedo.LinearTransform().rotate_y(angle) 688 return self.apply_transform(LT) 689 690 def rotate_z(self, angle): 691 """Rotate object around z axis.""" 692 LT = vedo.LinearTransform().rotate_z(angle) 693 return self.apply_transform(LT) 694 695 def reorient(self, old_axis, new_axis, rotation=0, rad=False): 696 """Rotate object to a new orientation.""" 697 if rad: 698 rotation *= 180 / np.pi 699 axis = old_axis / np.linalg.norm(old_axis) 700 direction = new_axis / np.linalg.norm(new_axis) 701 angle = np.arccos(np.dot(axis, direction)) * 180 / np.pi 702 self.actor.RotateZ(rotation) 703 a,b,c = np.cross(axis, direction) 704 self.actor.RotateWXYZ(angle, c,b,a) 705 return self 706 707 def bounds(self): 708 """ 709 Get the object bounds. 710 Returns a list in format `[xmin,xmax, ymin,ymax, zmin,zmax]`. 711 """ 712 return self.actor.GetBounds() 713 714 def xbounds(self, i=None): 715 """Get the bounds `[xmin,xmax]`. Can specify upper or lower with i (0,1).""" 716 b = self.bounds() 717 if i is not None: 718 return b[i] 719 return (b[0], b[1]) 720 721 def ybounds(self, i=None): 722 """Get the bounds `[ymin,ymax]`. Can specify upper or lower with i (0,1).""" 723 b = self.bounds() 724 if i == 0: 725 return b[2] 726 if i == 1: 727 return b[3] 728 return (b[2], b[3]) 729 730 def zbounds(self, i=None): 731 """Get the bounds `[zmin,zmax]`. Can specify upper or lower with i (0,1).""" 732 b = self.bounds() 733 if i == 0: 734 return b[4] 735 if i == 1: 736 return b[5] 737 return (b[4], b[5]) 738 739 def diagonal_size(self): 740 """Get the diagonal size of the bounding box.""" 741 b = self.bounds() 742 return np.sqrt((b[1]-b[0])**2 + (b[3]-b[2])**2 + (b[5]-b[4])**2) 743 744################################################### 745class PointsVisual(CommonVisual): 746 """Class to manage the visual aspects of a ``Points`` object.""" 747 748 def __init__(self): 749 # print("init PointsVisual") 750 super().__init__() 751 752 def clone2d(self, scale=None): 753 """ 754 Copy a 3D Mesh into a flat 2D image. 755 Returns a `Actor2D`. 756 757 Examples: 758 - [clone2d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/clone2d.py) 759 760  761 """ 762 if scale is None: 763 # work out a reasonable scale 764 msiz = self.diagonal_size() 765 if vedo.plotter_instance and vedo.plotter_instance.window: 766 sz = vedo.plotter_instance.window.GetSize() 767 dsiz = utils.mag(sz) 768 scale = dsiz / msiz / 10 769 else: 770 scale = 350 / msiz 771 772 cmsh = self.clone() 773 poly = cmsh.pos([0, 0, 0]).scale(scale).dataset 774 775 cm = self.mapper.GetColorMode() 776 lut = self.mapper.GetLookupTable() 777 sv = self.mapper.GetScalarVisibility() 778 use_lut = self.mapper.GetUseLookupTableScalarRange() 779 vrange = self.mapper.GetScalarRange() 780 sm = self.mapper.GetScalarMode() 781 782 mapper2d = vtk.new("PolyDataMapper2D") 783 mapper2d.ShallowCopy(self.mapper) 784 mapper2d.SetInputData(poly) 785 mapper2d.SetColorMode(cm) 786 mapper2d.SetLookupTable(lut) 787 mapper2d.SetScalarVisibility(sv) 788 mapper2d.SetUseLookupTableScalarRange(use_lut) 789 mapper2d.SetScalarRange(vrange) 790 mapper2d.SetScalarMode(sm) 791 792 act2d = Actor2D() 793 act2d.properties = act2d.GetProperty() 794 act2d.mapper = mapper2d 795 act2d.dataset = poly 796 797 act2d.SetMapper(mapper2d) 798 csys = act2d.GetPositionCoordinate() 799 csys.SetCoordinateSystem(4) 800 act2d.properties.SetColor(cmsh.color()) 801 act2d.properties.SetOpacity(cmsh.alpha()) 802 return act2d 803 804 ################################################## 805 def copy_properties_from(self, source, deep=True, actor_related=True): 806 """ 807 Copy properties from another ``Points`` object. 808 """ 809 pr = vtk.vtkProperty() 810 try: 811 sp = source.properties 812 mp = source.mapper 813 sa = source.actor 814 except AttributeError: 815 sp = source.GetProperty() 816 mp = source.GetMapper() 817 sa = source 818 819 if deep: 820 pr.DeepCopy(sp) 821 else: 822 pr.ShallowCopy(sp) 823 self.actor.SetProperty(pr) 824 self.properties = pr 825 826 if self.actor.GetBackfaceProperty(): 827 bfpr = vtk.vtkProperty() 828 bfpr.DeepCopy(sa.GetBackfaceProperty()) 829 self.actor.SetBackfaceProperty(bfpr) 830 self.properties_backface = bfpr 831 832 if not actor_related: 833 return self 834 835 # mapper related: 836 self.mapper.SetScalarVisibility(mp.GetScalarVisibility()) 837 self.mapper.SetScalarMode(mp.GetScalarMode()) 838 self.mapper.SetScalarRange(mp.GetScalarRange()) 839 self.mapper.SetLookupTable(mp.GetLookupTable()) 840 self.mapper.SetColorMode(mp.GetColorMode()) 841 self.mapper.SetInterpolateScalarsBeforeMapping( 842 mp.GetInterpolateScalarsBeforeMapping() 843 ) 844 self.mapper.SetUseLookupTableScalarRange( 845 mp.GetUseLookupTableScalarRange() 846 ) 847 848 self.actor.SetPickable(sa.GetPickable()) 849 self.actor.SetDragable(sa.GetDragable()) 850 self.actor.SetTexture(sa.GetTexture()) 851 self.actor.SetVisibility(sa.GetVisibility()) 852 return self 853 854 def color(self, c=False, alpha=None): 855 """ 856 Set/get mesh's color. 857 If None is passed as input, will use colors from active scalars. 858 Same as `mesh.c()`. 859 """ 860 if c is False: 861 return np.array(self.properties.GetColor()) 862 if c is None: 863 self.mapper.ScalarVisibilityOn() 864 return self 865 self.mapper.ScalarVisibilityOff() 866 cc = colors.get_color(c) 867 self.properties.SetColor(cc) 868 if self.trail: 869 self.trail.properties.SetColor(cc) 870 if alpha is not None: 871 self.alpha(alpha) 872 return self 873 874 def c(self, color=False, alpha=None): 875 """ 876 Shortcut for `color()`. 877 If None is passed as input, will use colors from current active scalars. 878 """ 879 return self.color(color, alpha) 880 881 def alpha(self, opacity=None): 882 """Set/get mesh's transparency. Same as `mesh.opacity()`.""" 883 if opacity is None: 884 return self.properties.GetOpacity() 885 886 self.properties.SetOpacity(opacity) 887 bfp = self.actor.GetBackfaceProperty() 888 if bfp: 889 if opacity < 1: 890 self.properties_backface = bfp 891 self.actor.SetBackfaceProperty(None) 892 else: 893 self.actor.SetBackfaceProperty(self.properties_backface) 894 return self 895 896 def lut_color_at(self, value): 897 """ 898 Return the color of the lookup table at value. 899 """ 900 lut = self.mapper.GetLookupTable() 901 if not lut: 902 return None 903 rgb = [0,0,0] 904 lut.GetColor(value, rgb) 905 alpha = lut.GetOpacity(value) 906 return np.array(rgb + [alpha]) 907 908 def opacity(self, alpha=None): 909 """Set/get mesh's transparency. Same as `mesh.alpha()`.""" 910 return self.alpha(alpha) 911 912 def force_opaque(self, value=True): 913 """ Force the Mesh, Line or point cloud to be treated as opaque""" 914 ## force the opaque pass, fixes picking in vtk9 915 # but causes other bad troubles with lines.. 916 self.actor.SetForceOpaque(value) 917 return self 918 919 def force_translucent(self, value=True): 920 """ Force the Mesh, Line or point cloud to be treated as translucent""" 921 self.actor.SetForceTranslucent(value) 922 return self 923 924 def point_size(self, value=None): 925 """Set/get mesh's point size of vertices. Same as `mesh.ps()`""" 926 if value is None: 927 return self.properties.GetPointSize() 928 # self.properties.SetRepresentationToSurface() 929 else: 930 self.properties.SetRepresentationToPoints() 931 self.properties.SetPointSize(value) 932 return self 933 934 def ps(self, pointsize=None): 935 """Set/get mesh's point size of vertices. Same as `mesh.point_size()`""" 936 return self.point_size(pointsize) 937 938 def render_points_as_spheres(self, value=True): 939 """Make points look spheric or else make them look as squares.""" 940 self.properties.SetRenderPointsAsSpheres(value) 941 return self 942 943 def lighting( 944 self, 945 style="", 946 ambient=None, 947 diffuse=None, 948 specular=None, 949 specular_power=None, 950 specular_color=None, 951 metallicity=None, 952 roughness=None, 953 ): 954 """ 955 Set the ambient, diffuse, specular and specular_power lighting constants. 956 957 Arguments: 958 style : (str) 959 preset style, options are `[metallic, plastic, shiny, glossy, ambient, off]` 960 ambient : (float) 961 ambient fraction of emission [0-1] 962 diffuse : (float) 963 emission of diffused light in fraction [0-1] 964 specular : (float) 965 fraction of reflected light [0-1] 966 specular_power : (float) 967 precision of reflection [1-100] 968 specular_color : (color) 969 color that is being reflected by the surface 970 971 <img src="https://upload.wikimedia.org/wikipedia/commons/6/6b/Phong_components_version_4.png" alt="", width=700px> 972 973 Examples: 974 - [specular.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/specular.py) 975 """ 976 pr = self.properties 977 978 if style: 979 980 if style != "off": 981 pr.LightingOn() 982 983 if style == "off": 984 pr.SetInterpolationToFlat() 985 pr.LightingOff() 986 return self ############## 987 988 if hasattr(pr, "GetColor"): # could be Volume 989 c = pr.GetColor() 990 else: 991 c = (1, 1, 0.99) 992 mpr = self.mapper 993 if hasattr(mpr, 'GetScalarVisibility') and mpr.GetScalarVisibility(): 994 c = (1,1,0.99) 995 if style=='metallic': pars = [0.1, 0.3, 1.0, 10, c] 996 elif style=='plastic' : pars = [0.3, 0.4, 0.3, 5, c] 997 elif style=='shiny' : pars = [0.2, 0.6, 0.8, 50, c] 998 elif style=='glossy' : pars = [0.1, 0.7, 0.9, 90, (1,1,0.99)] 999 elif style=='ambient' : pars = [0.8, 0.1, 0.0, 1, (1,1,1)] 1000 elif style=='default' : pars = [0.1, 1.0, 0.05, 5, c] 1001 else: 1002 vedo.logger.error("in lighting(): Available styles are") 1003 vedo.logger.error("[default, metallic, plastic, shiny, glossy, ambient, off]") 1004 raise RuntimeError() 1005 pr.SetAmbient(pars[0]) 1006 pr.SetDiffuse(pars[1]) 1007 pr.SetSpecular(pars[2]) 1008 pr.SetSpecularPower(pars[3]) 1009 if hasattr(pr, "GetColor"): 1010 pr.SetSpecularColor(pars[4]) 1011 1012 if ambient is not None: pr.SetAmbient(ambient) 1013 if diffuse is not None: pr.SetDiffuse(diffuse) 1014 if specular is not None: pr.SetSpecular(specular) 1015 if specular_power is not None: pr.SetSpecularPower(specular_power) 1016 if specular_color is not None: pr.SetSpecularColor(colors.get_color(specular_color)) 1017 if utils.vtk_version_at_least(9): 1018 if metallicity is not None: 1019 pr.SetInterpolationToPBR() 1020 pr.SetMetallic(metallicity) 1021 if roughness is not None: 1022 pr.SetInterpolationToPBR() 1023 pr.SetRoughness(roughness) 1024 1025 return self 1026 1027 def point_blurring(self, r=1, emissive=False): 1028 """Set point blurring. 1029 Apply a gaussian convolution filter to the points. 1030 In this case the radius `r` is in absolute units of the mesh coordinates. 1031 With emissive set, the halo of point becomes light-emissive. 1032 """ 1033 self.properties.SetRepresentationToPoints() 1034 if emissive: 1035 self.mapper.SetEmissive(bool(emissive)) 1036 self.mapper.SetScaleFactor(r * 1.4142) 1037 1038 # https://kitware.github.io/vtk-examples/site/Python/Meshes/PointInterpolator/ 1039 if alpha < 1: 1040 self.mapper.SetSplatShaderCode( 1041 "//VTK::Color::Impl\n" 1042 "float dist = dot(offsetVCVSOutput.xy,offsetVCVSOutput.xy);\n" 1043 "if (dist > 1.0) {\n" 1044 " discard;\n" 1045 "} else {\n" 1046 f" float scale = ({alpha} - dist);\n" 1047 " ambientColor *= scale;\n" 1048 " diffuseColor *= scale;\n" 1049 "}\n" 1050 ) 1051 alpha = 1 1052 1053 self.mapper.Modified() 1054 self.actor.Modified() 1055 self.properties.SetOpacity(alpha) 1056 self.actor.SetMapper(self.mapper) 1057 return self 1058 1059 @property 1060 def cellcolors(self): 1061 """ 1062 Colorize each cell (face) of a mesh by passing 1063 a 1-to-1 list of colors in format [R,G,B] or [R,G,B,A]. 1064 Colors levels and opacities must be in the range [0,255]. 1065 1066 A single constant color can also be passed as string or RGBA. 1067 1068 A cell array named "CellsRGBA" is automatically created. 1069 1070 Examples: 1071 - [color_mesh_cells1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/color_mesh_cells1.py) 1072 - [color_mesh_cells2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/color_mesh_cells2.py) 1073 1074  1075 """ 1076 if "CellsRGBA" not in self.celldata.keys(): 1077 lut = self.mapper.GetLookupTable() 1078 vscalars = self.dataset.GetCellData().GetScalars() 1079 if vscalars is None or lut is None: 1080 arr = np.zeros([self.ncells, 4], dtype=np.uint8) 1081 col = np.array(self.properties.GetColor()) 1082 col = np.round(col * 255).astype(np.uint8) 1083 alf = self.properties.GetOpacity() 1084 alf = np.round(alf * 255).astype(np.uint8) 1085 arr[:, (0, 1, 2)] = col 1086 arr[:, 3] = alf 1087 else: 1088 cols = lut.MapScalars(vscalars, 0, 0) 1089 arr = utils.vtk2numpy(cols) 1090 self.celldata["CellsRGBA"] = arr 1091 self.celldata.select("CellsRGBA") 1092 return self.celldata["CellsRGBA"] 1093 1094 @cellcolors.setter 1095 def cellcolors(self, value): 1096 if isinstance(value, str): 1097 c = colors.get_color(value) 1098 value = np.array([*c, 1]) * 255 1099 value = np.round(value) 1100 1101 value = np.asarray(value) 1102 n = self.ncells 1103 1104 if value.ndim == 1: 1105 value = np.repeat([value], n, axis=0) 1106 1107 if value.shape[1] == 3: 1108 z = np.zeros((n, 1), dtype=np.uint8) 1109 value = np.append(value, z + 255, axis=1) 1110 1111 assert n == value.shape[0] 1112 1113 self.celldata["CellsRGBA"] = value.astype(np.uint8) 1114 # self.mapper.SetColorModeToDirectScalars() # done in select() 1115 self.celldata.select("CellsRGBA") 1116 1117 @property 1118 def pointcolors(self): 1119 """ 1120 Colorize each point (or vertex of a mesh) by passing 1121 a 1-to-1 list of colors in format [R,G,B] or [R,G,B,A]. 1122 Colors levels and opacities must be in the range [0,255]. 1123 1124 A single constant color can also be passed as string or RGBA. 1125 1126 A point array named "PointsRGBA" is automatically created. 1127 """ 1128 if "PointsRGBA" not in self.pointdata.keys(): 1129 lut = self.mapper.GetLookupTable() 1130 vscalars = self.dataset.GetPointData().GetScalars() 1131 if vscalars is None or lut is None: 1132 # create a constant array 1133 arr = np.zeros([self.npoints, 4], dtype=np.uint8) 1134 col = np.array(self.properties.GetColor()) 1135 col = np.round(col * 255).astype(np.uint8) 1136 alf = self.properties.GetOpacity() 1137 alf = np.round(alf * 255).astype(np.uint8) 1138 arr[:, (0, 1, 2)] = col 1139 arr[:, 3] = alf 1140 else: 1141 cols = lut.MapScalars(vscalars, 0, 0) 1142 arr = utils.vtk2numpy(cols) 1143 self.pointdata["PointsRGBA"] = arr 1144 self.pointdata.select("PointsRGBA") 1145 return self.pointdata["PointsRGBA"] 1146 1147 @pointcolors.setter 1148 def pointcolors(self, value): 1149 if isinstance(value, str): 1150 c = colors.get_color(value) 1151 value = np.array([*c, 1]) * 255 1152 value = np.round(value) 1153 1154 value = np.asarray(value) 1155 n = self.npoints 1156 1157 if value.ndim == 1: 1158 value = np.repeat([value], n, axis=0) 1159 1160 if value.shape[1] == 3: 1161 z = np.zeros((n, 1), dtype=np.uint8) 1162 value = np.append(value, z + 255, axis=1) 1163 1164 assert n == value.shape[0] 1165 1166 self.pointdata["PointsRGBA"] = value.astype(np.uint8) 1167 # self.mapper.SetColorModeToDirectScalars() # done in select() 1168 self.pointdata.select("PointsRGBA") 1169 1170 ##################################################################################### 1171 def cmap( 1172 self, 1173 input_cmap, 1174 input_array=None, 1175 on="", 1176 name="Scalars", 1177 vmin=None, 1178 vmax=None, 1179 n_colors=256, 1180 alpha=1.0, 1181 logscale=False, 1182 ): 1183 """ 1184 Set individual point/cell colors by providing a list of scalar values and a color map. 1185 1186 Arguments: 1187 input_cmap : (str, list, vtkLookupTable, matplotlib.colors.LinearSegmentedColormap) 1188 color map scheme to transform a real number into a color. 1189 input_array : (str, list, vtkArray) 1190 can be the string name of an existing array, a new array or a `vtkArray`. 1191 on : (str) 1192 either 'points' or 'cells' or blank (automatic). 1193 Apply the color map to data which is defined on either points or cells. 1194 name : (str) 1195 give a name to the provided array (if input_array is an array) 1196 vmin : (float) 1197 clip scalars to this minimum value 1198 vmax : (float) 1199 clip scalars to this maximum value 1200 n_colors : (int) 1201 number of distinct colors to be used in colormap table. 1202 alpha : (float, list) 1203 Mesh transparency. Can be a `list` of values one for each vertex. 1204 logscale : (bool) 1205 Use logscale 1206 1207 Examples: 1208 - [mesh_coloring.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_coloring.py) 1209 - [mesh_alphas.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_alphas.py) 1210 - [mesh_custom.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_custom.py) 1211 - (and many others) 1212 1213  1214 """ 1215 self._cmap_name = input_cmap 1216 1217 if on == "": 1218 try: 1219 on = self.mapper.GetScalarModeAsString().replace("Use", "") 1220 if on not in ["PointData", "CellData"]: # can be "Default" 1221 on = "points" 1222 self.mapper.SetScalarModeToUsePointData() 1223 except AttributeError: 1224 on = "points" 1225 elif on == "Default": 1226 on = "points" 1227 self.mapper.SetScalarModeToUsePointData() 1228 1229 if input_array is None: 1230 if not self.pointdata.keys() and self.celldata.keys(): 1231 on = "cells" 1232 if not self.dataset.GetCellData().GetScalars(): 1233 input_array = 0 # pick the first at hand 1234 1235 if "point" in on.lower(): 1236 data = self.dataset.GetPointData() 1237 n = self.dataset.GetNumberOfPoints() 1238 elif "cell" in on.lower(): 1239 data = self.dataset.GetCellData() 1240 n = self.dataset.GetNumberOfCells() 1241 else: 1242 vedo.logger.error( 1243 f"Must specify in cmap(on=...) to either 'cells' or 'points', not {on}") 1244 raise RuntimeError() 1245 1246 if input_array is None: # if None try to fetch the active scalars 1247 arr = data.GetScalars() 1248 if not arr: 1249 vedo.logger.error(f"in cmap(), cannot find any {on} active array ...skip coloring.") 1250 return self 1251 1252 if not arr.GetName(): # sometimes arrays dont have a name.. 1253 arr.SetName(name) 1254 1255 elif isinstance(input_array, str): # if a string is passed 1256 arr = data.GetArray(input_array) 1257 if not arr: 1258 vedo.logger.error(f"in cmap(), cannot find {on} array {input_array} ...skip coloring.") 1259 return self 1260 1261 elif isinstance(input_array, int): # if an int is passed 1262 if input_array < data.GetNumberOfArrays(): 1263 arr = data.GetArray(input_array) 1264 else: 1265 vedo.logger.error(f"in cmap(), cannot find {on} array at {input_array} ...skip coloring.") 1266 return self 1267 1268 elif utils.is_sequence(input_array): # if a numpy array is passed 1269 npts = len(input_array) 1270 if npts != n: 1271 vedo.logger.error(f"in cmap(), nr. of input {on} scalars {npts} != {n} ...skip coloring.") 1272 return self 1273 arr = utils.numpy2vtk(input_array, name=name, dtype=float) 1274 data.AddArray(arr) 1275 data.Modified() 1276 1277 elif isinstance(input_array, vtk.vtkArray): # if a vtkArray is passed 1278 arr = input_array 1279 data.AddArray(arr) 1280 data.Modified() 1281 1282 else: 1283 vedo.logger.error(f"in cmap(), cannot understand input type {type(input_array)}") 1284 raise RuntimeError() 1285 1286 # Now we have array "arr" 1287 array_name = arr.GetName() 1288 1289 if arr.GetNumberOfComponents() == 1: 1290 if vmin is None: 1291 vmin = arr.GetRange()[0] 1292 if vmax is None: 1293 vmax = arr.GetRange()[1] 1294 else: 1295 if vmin is None or vmax is None: 1296 vn = utils.mag(utils.vtk2numpy(arr)) 1297 if vmin is None: 1298 vmin = vn.min() 1299 if vmax is None: 1300 vmax = vn.max() 1301 1302 # interpolate alphas if they are not constant 1303 if not utils.is_sequence(alpha): 1304 alpha = [alpha] * n_colors 1305 else: 1306 v = np.linspace(0, 1, n_colors, endpoint=True) 1307 xp = np.linspace(0, 1, len(alpha), endpoint=True) 1308 alpha = np.interp(v, xp, alpha) 1309 1310 ########################### build the look-up table 1311 if isinstance(input_cmap, vtk.vtkLookupTable): # vtkLookupTable 1312 lut = input_cmap 1313 1314 elif utils.is_sequence(input_cmap): # manual sequence of colors 1315 lut = vtk.vtkLookupTable() 1316 if logscale: 1317 lut.SetScaleToLog10() 1318 lut.SetRange(vmin, vmax) 1319 ncols = len(input_cmap) 1320 lut.SetNumberOfTableValues(ncols) 1321 1322 for i, c in enumerate(input_cmap): 1323 r, g, b = colors.get_color(c) 1324 lut.SetTableValue(i, r, g, b, alpha[i]) 1325 lut.Build() 1326 1327 else: 1328 # assume string cmap name OR matplotlib.colors.LinearSegmentedColormap 1329 lut = vtk.vtkLookupTable() 1330 if logscale: 1331 lut.SetScaleToLog10() 1332 lut.SetVectorModeToMagnitude() 1333 lut.SetRange(vmin, vmax) 1334 lut.SetNumberOfTableValues(n_colors) 1335 mycols = colors.color_map(range(n_colors), input_cmap, 0, n_colors) 1336 for i, c in enumerate(mycols): 1337 r, g, b = c 1338 lut.SetTableValue(i, r, g, b, alpha[i]) 1339 lut.Build() 1340 1341 # TEST NEW WAY 1342 self.mapper.SetLookupTable(lut) 1343 self.mapper.ScalarVisibilityOn() 1344 self.mapper.SetColorModeToMapScalars() 1345 self.mapper.SetScalarRange(lut.GetRange()) 1346 if "point" in on.lower(): 1347 self.pointdata.select(array_name) 1348 else: 1349 self.celldata.select(array_name) 1350 return self 1351 1352 # # TEST this is the old way: 1353 # # arr.SetLookupTable(lut) # wrong! causes weird instabilities with LUT 1354 # # if data.GetScalars(): 1355 # # data.GetScalars().SetLookupTable(lut) 1356 # # data.GetScalars().Modified() 1357 1358 # data.SetActiveScalars(array_name) 1359 # # data.SetScalars(arr) # wrong! it deletes array in position 0, never use SetScalars 1360 # # data.SetActiveAttribute(array_name, 0) # boh! 1361 1362 # self.mapper.SetLookupTable(lut) 1363 # self.mapper.SetColorModeToMapScalars() # so we dont need to convert uint8 scalars 1364 1365 # self.mapper.ScalarVisibilityOn() 1366 # self.mapper.SetScalarRange(lut.GetRange()) 1367 1368 # if on.startswith("point"): 1369 # self.mapper.SetScalarModeToUsePointData() 1370 # else: 1371 # self.mapper.SetScalarModeToUseCellData() 1372 # if hasattr(self.mapper, "SetArrayName"): 1373 # self.mapper.SetArrayName(array_name) 1374 # return self 1375 1376 def add_trail(self, offset=(0, 0, 0), n=50, c=None, alpha=1.0, lw=2): 1377 """ 1378 Add a trailing line to mesh. 1379 This new mesh is accessible through `mesh.trail`. 1380 1381 Arguments: 1382 offset : (float) 1383 set an offset vector from the object center. 1384 n : (int) 1385 number of segments 1386 lw : (float) 1387 line width of the trail 1388 1389 Examples: 1390 - [trail.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/trail.py) 1391 1392  1393 1394 - [airplane1.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/airplane1.py) 1395 - [airplane2.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/airplane2.py) 1396 """ 1397 if self.trail is None: 1398 pos = self.pos() 1399 self.trail_offset = np.asarray(offset) 1400 self.trail_points = [pos] * n 1401 1402 if c is None: 1403 col = self.properties.GetColor() 1404 else: 1405 col = colors.get_color(c) 1406 1407 tline = vedo.shapes.Line(pos, pos, res=n, c=col, alpha=alpha, lw=lw) 1408 self.trail = tline # holds the Line 1409 return self 1410 1411 def update_trail(self): 1412 """ 1413 Update the trailing line of a moving object. 1414 """ 1415 currentpos = self.pos() 1416 self.trail_points.append(currentpos) # cycle 1417 self.trail_points.pop(0) 1418 data = np.array(self.trail_points) + self.trail_offset 1419 tpoly = self.trail.dataset 1420 tpoly.GetPoints().SetData(utils.numpy2vtk(data, dtype=np.float32)) 1421 return self 1422 1423 def _compute_shadow(self, plane, point, direction): 1424 shad = self.clone() 1425 shad.name = "Shadow" 1426 1427 tarr = shad.dataset.GetPointData().GetTCoords() 1428 if tarr: # remove any texture coords 1429 tname = tarr.GetName() 1430 shad.pointdata.remove(tname) 1431 shad.dataset.GetPointData().SetTCoords(None) 1432 shad.actor.SetTexture(None) 1433 1434 pts = shad.vertices 1435 if plane == "x": 1436 # shad = shad.project_on_plane('x') 1437 # instead do it manually so in case of alpha<1 1438 # we dont see glitches due to coplanar points 1439 # we leave a small tolerance of 0.1% in thickness 1440 x0, x1 = self.xbounds() 1441 pts[:, 0] = (pts[:, 0] - (x0 + x1) / 2) / 1000 + self.actor.GetOrigin()[0] 1442 shad.vertices = pts 1443 shad.x(point) 1444 elif plane == "y": 1445 x0, x1 = self.ybounds() 1446 pts[:, 1] = (pts[:, 1] - (x0 + x1) / 2) / 1000 + self.actor.GetOrigin()[1] 1447 shad.vertices = pts 1448 shad.y(point) 1449 elif plane == "z": 1450 x0, x1 = self.zbounds() 1451 pts[:, 2] = (pts[:, 2] - (x0 + x1) / 2) / 1000 + self.actor.GetOrigin()[2] 1452 shad.vertices = pts 1453 shad.z(point) 1454 else: 1455 shad = shad.project_on_plane(plane, point, direction) 1456 return shad 1457 1458 def add_shadow(self, plane, point, direction=None, c=(0.6, 0.6, 0.6), alpha=1, culling=0): 1459 """ 1460 Generate a shadow out of an `Mesh` on one of the three Cartesian planes. 1461 The output is a new `Mesh` representing the shadow. 1462 This new mesh is accessible through `mesh.shadow`. 1463 By default the shadow mesh is placed on the bottom wall of the bounding box. 1464 1465 See also `pointcloud.project_on_plane()`. 1466 1467 Arguments: 1468 plane : (str, Plane) 1469 if plane is `str`, plane can be one of `['x', 'y', 'z']`, 1470 represents x-plane, y-plane and z-plane, respectively. 1471 Otherwise, plane should be an instance of `vedo.shapes.Plane` 1472 point : (float, array) 1473 if plane is `str`, point should be a float represents the intercept. 1474 Otherwise, point is the camera point of perspective projection 1475 direction : (list) 1476 direction of oblique projection 1477 culling : (int) 1478 choose between front [1] or backface [-1] culling or None. 1479 1480 Examples: 1481 - [shadow1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/shadow1.py) 1482 - [airplane1.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/airplane1.py) 1483 - [airplane2.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/airplane2.py) 1484 1485  1486 """ 1487 shad = self._compute_shadow(plane, point, direction) 1488 shad.c(c).alpha(alpha) 1489 1490 try: 1491 # Points dont have these methods 1492 shad.flat() 1493 if culling in (1, True): 1494 shad.frontface_culling() 1495 elif culling == -1: 1496 shad.backface_culling() 1497 except AttributeError: 1498 pass 1499 1500 shad.properties.LightingOff() 1501 shad.actor.SetPickable(False) 1502 shad.actor.SetUseBounds(True) 1503 1504 if shad not in self.shadows: 1505 self.shadows.append(shad) 1506 shad.info = dict(plane=plane, point=point, direction=direction) 1507 # shad.metadata["plane"] = plane 1508 # shad.metadata["point"] = point 1509 # print("AAAA", direction, plane, point) 1510 # if direction is None: 1511 # direction = [0,0,0] 1512 # shad.metadata["direction"] = direction 1513 return self 1514 1515 def update_shadows(self): 1516 """Update the shadows of a moving object.""" 1517 for sha in self.shadows: 1518 plane = sha.info["plane"] 1519 point = sha.info["point"] 1520 direction = sha.info["direction"] 1521 # print("update_shadows direction", direction,plane,point ) 1522 # plane = sha.metadata["plane"] 1523 # point = sha.metadata["point"] 1524 # direction = sha.metadata["direction"] 1525 # if direction[0]==0 and direction[1]==0 and direction[2]==0: 1526 # direction = None 1527 # print("BBBB", sha.metadata["direction"], 1528 # sha.metadata["plane"], sha.metadata["point"]) 1529 new_sha = self._compute_shadow(plane, point, direction) 1530 sha._update(new_sha.dataset) 1531 return self 1532 1533 def labels( 1534 self, 1535 content=None, 1536 on="points", 1537 scale=None, 1538 xrot=0.0, 1539 yrot=0.0, 1540 zrot=0.0, 1541 ratio=1, 1542 precision=None, 1543 italic=False, 1544 font="", 1545 justify="", 1546 c="black", 1547 alpha=1.0, 1548 ): 1549 """ 1550 Generate value or ID labels for mesh cells or points. 1551 For large nr. of labels use `font="VTK"` which is much faster. 1552 1553 See also: 1554 `labels2d()`, `flagpole()`, `caption()` and `legend()`. 1555 1556 Arguments: 1557 content : (list,int,str) 1558 either 'id', 'cellid', array name or array number. 1559 A array can also be passed (must match the nr. of points or cells). 1560 on : (str) 1561 generate labels for "cells" instead of "points" 1562 scale : (float) 1563 absolute size of labels, if left as None it is automatic 1564 zrot : (float) 1565 local rotation angle of label in degrees 1566 ratio : (int) 1567 skipping ratio, to reduce nr of labels for large meshes 1568 precision : (int) 1569 numeric precision of labels 1570 1571 ```python 1572 from vedo import * 1573 s = Sphere(res=10).linewidth(1).c("orange").compute_normals() 1574 point_ids = s.labels('id', on="points").c('green') 1575 cell_ids = s.labels('id', on="cells" ).c('black') 1576 show(s, point_ids, cell_ids) 1577 ``` 1578  1579 1580 Examples: 1581 - [boundaries.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/boundaries.py) 1582 1583  1584 """ 1585 1586 cells = False 1587 if "cell" in on or "face" in on: 1588 cells = True 1589 justify = "centered" if justify == "" else justify 1590 1591 if isinstance(content, str): 1592 if content in ("pointid", "pointsid"): 1593 cells = False 1594 content = "id" 1595 justify = "bottom-left" if justify == "" else justify 1596 if content in ("cellid", "cellsid"): 1597 cells = True 1598 content = "id" 1599 justify = "centered" if justify == "" else justify 1600 1601 try: 1602 if cells: 1603 elems = self.cell_centers 1604 norms = self.cell_normals 1605 ns = np.sqrt(self.ncells) 1606 justify = "centered" if justify == "" else justify 1607 else: 1608 elems = self.vertices 1609 norms = self.vertex_normals 1610 ns = np.sqrt(self.npoints) 1611 except AttributeError: 1612 norms = [] 1613 1614 if not justify: 1615 justify = "bottom-left" 1616 1617 hasnorms = False 1618 if len(norms) > 0: 1619 hasnorms = True 1620 1621 if scale is None: 1622 if not ns: 1623 ns = 100 1624 scale = self.diagonal_size() / ns / 10 1625 1626 arr = None 1627 mode = 0 1628 if content is None: 1629 mode = 0 1630 if cells: 1631 if self.dataset.GetCellData().GetScalars(): 1632 name = self.dataset.GetCellData().GetScalars().GetName() 1633 arr = self.celldata[name] 1634 else: 1635 if self.dataset.GetPointData().GetScalars(): 1636 name = self.dataset.GetPointData().GetScalars().GetName() 1637 arr = self.pointdata[name] 1638 elif isinstance(content, (str, int)): 1639 if content == "id": 1640 mode = 1 1641 elif cells: 1642 mode = 0 1643 arr = self.celldata[content] 1644 else: 1645 mode = 0 1646 arr = self.pointdata[content] 1647 elif utils.is_sequence(content): 1648 mode = 0 1649 arr = content 1650 1651 if arr is None and mode == 0: 1652 vedo.logger.error("in labels(), array not found in point or cell data") 1653 return None 1654 1655 ratio = int(ratio+0.5) 1656 tapp = vtk.new("AppendPolyData") 1657 has_inputs = False 1658 1659 for i, e in enumerate(elems): 1660 if i % ratio: 1661 continue 1662 1663 if mode == 1: 1664 txt_lab = str(i) 1665 else: 1666 if precision: 1667 txt_lab = utils.precision(arr[i], precision) 1668 else: 1669 txt_lab = str(arr[i]) 1670 1671 if not txt_lab: 1672 continue 1673 1674 if font == "VTK": 1675 tx = vtk.new("VectorText") 1676 tx.SetText(txt_lab) 1677 tx.Update() 1678 tx_poly = tx.GetOutput() 1679 else: 1680 tx_poly = vedo.shapes.Text3D(txt_lab, font=font, justify=justify).dataset 1681 1682 if tx_poly.GetNumberOfPoints() == 0: 1683 continue ###################### 1684 1685 T = vtk.vtkTransform() 1686 T.PostMultiply() 1687 if italic: 1688 T.Concatenate([1, 0.2, 0, 0, 1689 0, 1 , 0, 0, 1690 0, 0 , 1, 0, 1691 0, 0 , 0, 1]) 1692 if hasnorms: 1693 ni = norms[i] 1694 if cells and font=="VTK": # center-justify 1695 bb = tx_poly.GetBounds() 1696 dx, dy = (bb[1] - bb[0]) / 2, (bb[3] - bb[2]) / 2 1697 T.Translate(-dx, -dy, 0) 1698 if xrot: T.RotateX(xrot) 1699 if yrot: T.RotateY(yrot) 1700 if zrot: T.RotateZ(zrot) 1701 crossvec = np.cross([0, 0, 1], ni) 1702 angle = np.arccos(np.dot([0, 0, 1], ni)) * 57.3 1703 T.RotateWXYZ(angle, crossvec) 1704 T.Translate(ni / 100) 1705 else: 1706 if xrot: T.RotateX(xrot) 1707 if yrot: T.RotateY(yrot) 1708 if zrot: T.RotateZ(zrot) 1709 T.Scale(scale, scale, scale) 1710 T.Translate(e) 1711 tf = vtk.new("TransformPolyDataFilter") 1712 tf.SetInputData(tx_poly) 1713 tf.SetTransform(T) 1714 tf.Update() 1715 tapp.AddInputData(tf.GetOutput()) 1716 has_inputs = True 1717 1718 if has_inputs: 1719 tapp.Update() 1720 lpoly = tapp.GetOutput() 1721 else: 1722 lpoly = vtk.vtkPolyData() 1723 ids = vedo.mesh.Mesh(lpoly, c=c, alpha=alpha) 1724 ids.properties.LightingOff() 1725 ids.actor.PickableOff() 1726 ids.actor.SetUseBounds(False) 1727 ids.name = "Labels" 1728 return ids 1729 1730 def labels2d( 1731 self, 1732 content="id", 1733 on="points", 1734 scale=1.0, 1735 precision=4, 1736 font="Calco", 1737 justify="bottom-left", 1738 angle=0.0, 1739 frame=False, 1740 c="black", 1741 bc=None, 1742 alpha=1.0, 1743 ): 1744 """ 1745 Generate value or ID bi-dimensional labels for mesh cells or points. 1746 1747 See also: `labels()`, `flagpole()`, `caption()` and `legend()`. 1748 1749 Arguments: 1750 content : (str) 1751 either 'id', 'cellid', or array name 1752 on : (str) 1753 generate labels for "cells" instead of "points" (the default) 1754 scale : (float) 1755 size scaling of labels 1756 precision : (int) 1757 precision of numeric labels 1758 angle : (float) 1759 local rotation angle of label in degrees 1760 frame : (bool) 1761 draw a frame around the label 1762 bc : (str) 1763 background color of the label 1764 1765 ```python 1766 from vedo import Sphere, show 1767 sph = Sphere(quads=True, res=4).compute_normals().wireframe() 1768 sph.celldata["zvals"] = sph.cell_centers[:,2] 1769 l2d = sph.labels("zvals", on="cells", precision=2).backcolor('orange9') 1770 show(sph, l2d, axes=1).close() 1771 ``` 1772  1773 """ 1774 cells = False 1775 if "cell" in on: 1776 cells = True 1777 1778 if isinstance(content, str): 1779 if content in ("id", "pointid", "pointsid"): 1780 cells = False 1781 content = "id" 1782 if content in ("cellid", "cellsid"): 1783 cells = True 1784 content = "id" 1785 1786 if cells: 1787 if content != "id" and content not in self.celldata.keys(): 1788 vedo.logger.error(f"In labels2d: cell array {content} does not exist.") 1789 return None 1790 cellcloud = vedo.Points(self.cell_centers) 1791 arr = self.dataset.GetCellData().GetScalars() 1792 poly = cellcloud.dataset 1793 poly.GetPointData().SetScalars(arr) 1794 else: 1795 poly = self.dataset 1796 if content != "id" and content not in self.pointdata.keys(): 1797 vedo.logger.error(f"In labels2d: point array {content} does not exist.") 1798 return None 1799 1800 mp = vtk.new("LabeledDataMapper") 1801 1802 if content == "id": 1803 mp.SetLabelModeToLabelIds() 1804 else: 1805 mp.SetLabelModeToLabelScalars() 1806 if precision is not None: 1807 mp.SetLabelFormat(f"%-#.{precision}g") 1808 1809 pr = mp.GetLabelTextProperty() 1810 c = colors.get_color(c) 1811 pr.SetColor(c) 1812 pr.SetOpacity(alpha) 1813 pr.SetFrame(frame) 1814 pr.SetFrameColor(c) 1815 pr.SetItalic(False) 1816 pr.BoldOff() 1817 pr.ShadowOff() 1818 pr.UseTightBoundingBoxOn() 1819 pr.SetOrientation(angle) 1820 pr.SetFontFamily(vtk.VTK_FONT_FILE) 1821 fl = utils.get_font_path(font) 1822 pr.SetFontFile(fl) 1823 pr.SetFontSize(int(20 * scale)) 1824 1825 if "cent" in justify or "mid" in justify: 1826 pr.SetJustificationToCentered() 1827 elif "rig" in justify: 1828 pr.SetJustificationToRight() 1829 elif "left" in justify: 1830 pr.SetJustificationToLeft() 1831 # ------ 1832 if "top" in justify: 1833 pr.SetVerticalJustificationToTop() 1834 else: 1835 pr.SetVerticalJustificationToBottom() 1836 1837 if bc is not None: 1838 bc = colors.get_color(bc) 1839 pr.SetBackgroundColor(bc) 1840 pr.SetBackgroundOpacity(alpha) 1841 1842 mp.SetInputData(poly) 1843 a2d = vtk.vtkActor2D() 1844 a2d.PickableOff() 1845 a2d.SetMapper(mp) 1846 return a2d 1847 1848 def legend(self, txt): 1849 """Book a legend text.""" 1850 self.info["legend"] = txt 1851 # self.metadata["legend"] = txt 1852 return self 1853 1854 def flagpole( 1855 self, 1856 txt=None, 1857 point=None, 1858 offset=None, 1859 s=None, 1860 font="Calco", 1861 rounded=True, 1862 c=None, 1863 alpha=1.0, 1864 lw=2, 1865 italic=0.0, 1866 padding=0.1, 1867 ): 1868 """ 1869 Generate a flag pole style element to describe an object. 1870 Returns a `Mesh` object. 1871 1872 Use flagpole.follow_camera() to make it face the camera in the scene. 1873 1874 Consider using `settings.use_parallel_projection = True` 1875 to avoid perspective distortions. 1876 1877 See also `flagpost()`. 1878 1879 Arguments: 1880 txt : (str) 1881 Text to display. The default is the filename or the object name. 1882 point : (list) 1883 position of the flagpole pointer. 1884 offset : (list) 1885 text offset wrt the application point. 1886 s : (float) 1887 size of the flagpole. 1888 font : (str) 1889 font face. Check [available fonts here](https://vedo.embl.es/fonts). 1890 rounded : (bool) 1891 draw a rounded or squared box around the text. 1892 c : (list) 1893 text and box color. 1894 alpha : (float) 1895 opacity of text and box. 1896 lw : (float) 1897 line with of box frame. 1898 italic : (float) 1899 italicness of text. 1900 1901 Examples: 1902 - [intersect2d.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/intersect2d.py) 1903 1904  1905 1906 - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py) 1907 - [flag_labels1.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels1.py) 1908 - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels2.py) 1909 """ 1910 objs = [] 1911 1912 if txt is None: 1913 if self.filename: 1914 txt = self.filename.split("/")[-1] 1915 elif self.name: 1916 txt = self.name 1917 else: 1918 return None 1919 1920 x0, x1, y0, y1, z0, z1 = self.bounds() 1921 d = self.diagonal_size() 1922 if point is None: 1923 if d: 1924 point = self.closest_point([(x0 + x1) / 2, (y0 + y1) / 2, z1]) 1925 # point = self.closest_point([x1, y0, z1]) 1926 else: # it's a Point 1927 point = self.transform.position 1928 1929 pt = utils.make3d(point) 1930 1931 if offset is None: 1932 offset = [(x1 - x0) / 1.75, (y1 - y0) / 5, 0] 1933 offset = utils.make3d(offset) 1934 1935 if s is None: 1936 s = d / 20 1937 1938 sph = None 1939 if d and (z1 - z0) / d > 0.1: 1940 sph = vedo.shapes.Sphere(pt, r=s * 0.4, res=6) 1941 1942 if c is None: 1943 c = np.array(self.color()) / 1.4 1944 1945 lab = vedo.shapes.Text3D( 1946 txt, pos=pt + offset, s=s, font=font, italic=italic, justify="center" 1947 ) 1948 objs.append(lab) 1949 1950 if d and not sph: 1951 sph = vedo.shapes.Circle(pt, r=s / 3, res=16) 1952 objs.append(sph) 1953 1954 x0, x1, y0, y1, z0, z1 = lab.bounds() 1955 aline = [(x0,y0,z0), (x1,y0,z0), (x1,y1,z0), (x0,y1,z0)] 1956 if rounded: 1957 box = vedo.shapes.KSpline(aline, closed=True) 1958 else: 1959 box = vedo.shapes.Line(aline, closed=True) 1960 1961 cnt = [(x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2] 1962 1963 # box.actor.SetOrigin(cnt) 1964 box.scale([1 + padding, 1 + 2 * padding, 1], origin=cnt) 1965 objs.append(box) 1966 1967 x0, x1, y0, y1, z0, z1 = box.bounds() 1968 if x0 < pt[0] < x1: 1969 c0 = box.closest_point(pt) 1970 c1 = [c0[0], c0[1] + (pt[1] - y0) / 4, pt[2]] 1971 elif (pt[0] - x0) < (x1 - pt[0]): 1972 c0 = [x0, (y0 + y1) / 2, pt[2]] 1973 c1 = [x0 + (pt[0] - x0) / 4, (y0 + y1) / 2, pt[2]] 1974 else: 1975 c0 = [x1, (y0 + y1) / 2, pt[2]] 1976 c1 = [x1 + (pt[0] - x1) / 4, (y0 + y1) / 2, pt[2]] 1977 1978 con = vedo.shapes.Line([c0, c1, pt]) 1979 objs.append(con) 1980 1981 mobjs = vedo.merge(objs).c(c).alpha(alpha) 1982 mobjs.name = "FlagPole" 1983 mobjs.bc("tomato").pickable(False) 1984 mobjs.properties.LightingOff() 1985 mobjs.properties.SetLineWidth(lw) 1986 mobjs.actor.UseBoundsOff() 1987 mobjs.actor.SetPosition([0,0,0]) 1988 mobjs.actor.SetOrigin(pt) 1989 # print(pt) 1990 return mobjs 1991 1992 # mobjs = vedo.Assembly(objs)#.c(c).alpha(alpha) 1993 # mobjs.name = "FlagPole" 1994 # # mobjs.bc("tomato").pickable(False) 1995 # # mobjs.properties.LightingOff() 1996 # # mobjs.properties.SetLineWidth(lw) 1997 # # mobjs.actor.UseBoundsOff() 1998 # # mobjs.actor.SetPosition([0,0,0]) 1999 # # mobjs.actor.SetOrigin(pt) 2000 # # print(pt) 2001 # return mobjs 2002 2003 def flagpost( 2004 self, 2005 txt=None, 2006 point=None, 2007 offset=None, 2008 s=1.0, 2009 c="k9", 2010 bc="k1", 2011 alpha=1, 2012 lw=0, 2013 font="Calco", 2014 justify="center-left", 2015 vspacing=1.0, 2016 ): 2017 """ 2018 Generate a flag post style element to describe an object. 2019 2020 Arguments: 2021 txt : (str) 2022 Text to display. The default is the filename or the object name. 2023 point : (list) 2024 position of the flag anchor point. The default is None. 2025 offset : (list) 2026 a 3D displacement or offset. The default is None. 2027 s : (float) 2028 size of the text to be shown 2029 c : (list) 2030 color of text and line 2031 bc : (list) 2032 color of the flag background 2033 alpha : (float) 2034 opacity of text and box. 2035 lw : (int) 2036 line with of box frame. The default is 0. 2037 font : (str) 2038 font name. Use a monospace font for better rendering. The default is "Calco". 2039 Type `vedo -r fonts` for a font demo. 2040 Check [available fonts here](https://vedo.embl.es/fonts). 2041 justify : (str) 2042 internal text justification. The default is "center-left". 2043 vspacing : (float) 2044 vertical spacing between lines. 2045 2046 Examples: 2047 - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/examples/other/flag_labels2.py) 2048 2049  2050 """ 2051 if txt is None: 2052 if self.filename: 2053 txt = self.filename.split("/")[-1] 2054 elif self.name: 2055 txt = self.name 2056 else: 2057 return None 2058 2059 x0, x1, y0, y1, z0, z1 = self.bounds() 2060 d = self.diagonal_size() 2061 if point is None: 2062 if d: 2063 point = self.closest_point([(x0 + x1) / 2, (y0 + y1) / 2, z1]) 2064 else: # it's a Point 2065 point = self.transform.position 2066 2067 point = utils.make3d(point) 2068 2069 if offset is None: 2070 offset = [0, 0, (z1 - z0) / 2] 2071 offset = utils.make3d(offset) 2072 2073 fpost = vedo.addons.Flagpost( 2074 txt, point, point + offset, s, c, bc, alpha, lw, font, justify, vspacing 2075 ) 2076 self._caption = fpost 2077 return fpost 2078 2079 def caption( 2080 self, 2081 txt=None, 2082 point=None, 2083 size=(0.30, 0.15), 2084 padding=5, 2085 font="Calco", 2086 justify="center-right", 2087 vspacing=1.0, 2088 c=None, 2089 alpha=1.0, 2090 lw=1, 2091 ontop=True, 2092 ): 2093 """ 2094 Create a 2D caption to an object which follows the camera movements. 2095 Latex is not supported. Returns the same input object for concatenation. 2096 2097 See also `flagpole()`, `flagpost()`, `labels()` and `legend()` 2098 with similar functionality. 2099 2100 Arguments: 2101 txt : (str) 2102 text to be rendered. The default is the file name. 2103 point : (list) 2104 anchoring point. The default is None. 2105 size : (list) 2106 (width, height) of the caption box. The default is (0.30, 0.15). 2107 padding : (float) 2108 padding space of the caption box in pixels. The default is 5. 2109 font : (str) 2110 font name. Use a monospace font for better rendering. The default is "VictorMono". 2111 Type `vedo -r fonts` for a font demo. 2112 Check [available fonts here](https://vedo.embl.es/fonts). 2113 justify : (str) 2114 internal text justification. The default is "center-right". 2115 vspacing : (float) 2116 vertical spacing between lines. The default is 1. 2117 c : (str) 2118 text and box color. The default is 'lb'. 2119 alpha : (float) 2120 text and box transparency. The default is 1. 2121 lw : (int) 2122 line width in pixels. The default is 1. 2123 ontop : (bool) 2124 keep the 2d caption always on top. The default is True. 2125 2126 Examples: 2127 - [caption.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/caption.py) 2128 2129  2130 2131 - [flag_labels1.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels1.py) 2132 - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels2.py) 2133 """ 2134 if txt is None: 2135 if self.filename: 2136 txt = self.filename.split("/")[-1] 2137 elif self.name: 2138 txt = self.name 2139 2140 if not txt: # disable it 2141 self._caption = None 2142 return self 2143 2144 for r in vedo.shapes._reps: 2145 txt = txt.replace(r[0], r[1]) 2146 2147 if c is None: 2148 c = np.array(self.properties.GetColor()) / 2 2149 else: 2150 c = colors.get_color(c) 2151 2152 if point is None: 2153 x0, x1, y0, y1, _, z1 = self.dataset.GetBounds() 2154 pt = [(x0 + x1) / 2, (y0 + y1) / 2, z1] 2155 point = self.closest_point(pt) 2156 2157 capt = vtk.vtkCaptionActor2D() 2158 capt.SetAttachmentPoint(point) 2159 capt.SetBorder(True) 2160 capt.SetLeader(True) 2161 sph = vtk.new("SphereSource") 2162 sph.Update() 2163 capt.SetLeaderGlyphData(sph.GetOutput()) 2164 capt.SetMaximumLeaderGlyphSize(5) 2165 capt.SetPadding(int(padding)) 2166 capt.SetCaption(txt) 2167 capt.SetWidth(size[0]) 2168 capt.SetHeight(size[1]) 2169 capt.SetThreeDimensionalLeader(not ontop) 2170 2171 pra = capt.GetProperty() 2172 pra.SetColor(c) 2173 pra.SetOpacity(alpha) 2174 pra.SetLineWidth(lw) 2175 2176 pr = capt.GetCaptionTextProperty() 2177 pr.SetFontFamily(vtk.VTK_FONT_FILE) 2178 fl = utils.get_font_path(font) 2179 pr.SetFontFile(fl) 2180 pr.ShadowOff() 2181 pr.BoldOff() 2182 pr.FrameOff() 2183 pr.SetColor(c) 2184 pr.SetOpacity(alpha) 2185 pr.SetJustificationToLeft() 2186 if "top" in justify: 2187 pr.SetVerticalJustificationToTop() 2188 if "bottom" in justify: 2189 pr.SetVerticalJustificationToBottom() 2190 if "cent" in justify: 2191 pr.SetVerticalJustificationToCentered() 2192 pr.SetJustificationToCentered() 2193 if "left" in justify: 2194 pr.SetJustificationToLeft() 2195 if "right" in justify: 2196 pr.SetJustificationToRight() 2197 pr.SetLineSpacing(vspacing) 2198 return capt 2199 2200 2201##################################################################### 2202class MeshVisual(PointsVisual): 2203 """Class to manage the visual aspects of a ``Maesh`` object.""" 2204 2205 def __init__(self) -> None: 2206 # print("INIT MeshVisual", super()) 2207 super().__init__() 2208 2209 def follow_camera(self, camera=None, origin=None): 2210 """ 2211 Return an object that will follow camera movements and stay locked to it. 2212 Use `mesh.follow_camera(False)` to disable it. 2213 2214 A `vtkCamera` object can also be passed. 2215 """ 2216 if camera is False: 2217 try: 2218 self.SetCamera(None) 2219 return self 2220 except AttributeError: 2221 return self 2222 2223 factor = vtk.vtkFollower() 2224 factor.SetMapper(self.mapper) 2225 factor.SetProperty(self.properties) 2226 factor.SetBackfaceProperty(self.actor.GetBackfaceProperty()) 2227 factor.SetTexture(self.actor.GetTexture()) 2228 factor.SetScale(self.actor.GetScale()) 2229 # factor.SetOrientation(self.actor.GetOrientation()) 2230 factor.SetPosition(self.actor.GetPosition()) 2231 factor.SetUseBounds(self.actor.GetUseBounds()) 2232 2233 if origin is None: 2234 factor.SetOrigin(self.actor.GetOrigin()) 2235 else: 2236 factor.SetOrigin(origin) 2237 2238 factor.PickableOff() 2239 2240 if isinstance(camera, vtk.vtkCamera): 2241 factor.SetCamera(camera) 2242 else: 2243 plt = vedo.plotter_instance 2244 if plt and plt.renderer and plt.renderer.GetActiveCamera(): 2245 factor.SetCamera(plt.renderer.GetActiveCamera()) 2246 2247 self.actor = None 2248 factor.retrieve_object = weak_ref_to(self) 2249 self.actor = factor 2250 return self 2251 2252 def wireframe(self, value=True): 2253 """Set mesh's representation as wireframe or solid surface.""" 2254 if value: 2255 self.properties.SetRepresentationToWireframe() 2256 else: 2257 self.properties.SetRepresentationToSurface() 2258 return self 2259 2260 def flat(self): 2261 """Set surface interpolation to flat. 2262 2263 <img src="https://upload.wikimedia.org/wikipedia/commons/6/6b/Phong_components_version_4.png" width="700"> 2264 """ 2265 self.properties.SetInterpolationToFlat() 2266 return self 2267 2268 def phong(self): 2269 """Set surface interpolation to "phong".""" 2270 self.properties.SetInterpolationToPhong() 2271 return self 2272 2273 def backface_culling(self, value=True): 2274 """Set culling of polygons based on orientation of normal with respect to camera.""" 2275 self.properties.SetBackfaceCulling(value) 2276 return self 2277 2278 def render_lines_as_tubes(self, value=True): 2279 """Wrap a fake tube around a simple line for visualization""" 2280 self.properties.SetRenderLinesAsTubes(value) 2281 return self 2282 2283 def frontface_culling(self, value=True): 2284 """Set culling of polygons based on orientation of normal with respect to camera.""" 2285 self.properties.SetFrontfaceCulling(value) 2286 return self 2287 2288 def backcolor(self, bc=None): 2289 """ 2290 Set/get mesh's backface color. 2291 """ 2292 back_prop = self.actor.GetBackfaceProperty() 2293 2294 if bc is None: 2295 if back_prop: 2296 return back_prop.GetDiffuseColor() 2297 return self 2298 2299 if self.properties.GetOpacity() < 1: 2300 return self 2301 2302 if not back_prop: 2303 back_prop = vtk.vtkProperty() 2304 2305 back_prop.SetDiffuseColor(colors.get_color(bc)) 2306 back_prop.SetOpacity(self.properties.GetOpacity()) 2307 self.actor.SetBackfaceProperty(back_prop) 2308 self.mapper.ScalarVisibilityOff() 2309 return self 2310 2311 def bc(self, backcolor=False): 2312 """Shortcut for `mesh.backcolor()`.""" 2313 return self.backcolor(backcolor) 2314 2315 def linewidth(self, lw=None): 2316 """Set/get width of mesh edges. Same as `lw()`.""" 2317 if lw is not None: 2318 if lw == 0: 2319 self.properties.EdgeVisibilityOff() 2320 self.properties.SetRepresentationToSurface() 2321 return self 2322 self.properties.EdgeVisibilityOn() 2323 self.properties.SetLineWidth(lw) 2324 else: 2325 return self.properties.GetLineWidth() 2326 return self 2327 2328 def lw(self, linewidth=None): 2329 """Set/get width of mesh edges. Same as `linewidth()`.""" 2330 return self.linewidth(linewidth) 2331 2332 def linecolor(self, lc=None): 2333 """Set/get color of mesh edges. Same as `lc()`.""" 2334 if lc is None: 2335 return self.properties.GetEdgeColor() 2336 self.properties.EdgeVisibilityOn() 2337 self.properties.SetEdgeColor(colors.get_color(lc)) 2338 return self 2339 2340 def lc(self, linecolor=None): 2341 """Set/get color of mesh edges. Same as `linecolor()`.""" 2342 return self.linecolor(linecolor) 2343 2344 def texture( 2345 self, 2346 tname, 2347 tcoords=None, 2348 interpolate=True, 2349 repeat=True, 2350 edge_clamp=False, 2351 scale=None, 2352 ushift=None, 2353 vshift=None, 2354 seam_threshold=None, 2355 ): 2356 """ 2357 Assign a texture to mesh from image file or predefined texture `tname`. 2358 If tname is set to `None` texture is disabled. 2359 Input tname can also be an array or a `vtkTexture`. 2360 2361 Arguments: 2362 tname : (numpy.array, str, Image, vtkTexture, None) 2363 the input texture to be applied. Can be a numpy array, a path to an image file, 2364 a vedo Image. The None value disables texture. 2365 tcoords : (numpy.array, str) 2366 this is the (u,v) texture coordinate array. Can also be a string of an existing array 2367 in the mesh. 2368 interpolate : (bool) 2369 turn on/off linear interpolation of the texture map when rendering. 2370 repeat : (bool) 2371 repeat of the texture when tcoords extend beyond the [0,1] range. 2372 edge_clamp : (bool) 2373 turn on/off the clamping of the texture map when 2374 the texture coords extend beyond the [0,1] range. 2375 Only used when repeat is False, and edge clamping is supported by the graphics card. 2376 scale : (bool) 2377 scale the texture image by this factor 2378 ushift : (bool) 2379 shift u-coordinates of texture by this amount 2380 vshift : (bool) 2381 shift v-coordinates of texture by this amount 2382 seam_threshold : (float) 2383 try to seal seams in texture by collapsing triangles 2384 (test values around 1.0, lower values = stronger collapse) 2385 2386 Examples: 2387 - [texturecubes.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/texturecubes.py) 2388 2389  2390 """ 2391 pd = self.dataset 2392 out_img = None 2393 2394 if tname is None: # disable texture 2395 pd.GetPointData().SetTCoords(None) 2396 pd.GetPointData().Modified() 2397 return self ###################################### 2398 2399 if isinstance(tname, vtk.vtkTexture): 2400 tu = tname 2401 2402 elif isinstance(tname, vedo.Image): 2403 tu = vtk.vtkTexture() 2404 out_img = tname.dataset 2405 2406 elif utils.is_sequence(tname): 2407 tu = vtk.vtkTexture() 2408 out_img = vedo.image._get_img(tname) 2409 2410 elif isinstance(tname, str): 2411 tu = vtk.vtkTexture() 2412 2413 if "https://" in tname: 2414 try: 2415 tname = vedo.file_io.download(tname, verbose=False) 2416 except: 2417 vedo.logger.error(f"texture {tname} could not be downloaded") 2418 return self 2419 2420 fn = tname + ".jpg" 2421 if os.path.exists(tname): 2422 fn = tname 2423 else: 2424 vedo.logger.error(f"texture file {tname} does not exist") 2425 return self 2426 2427 fnl = fn.lower() 2428 if ".jpg" in fnl or ".jpeg" in fnl: 2429 reader = vtk.new("JPEGReader") 2430 elif ".png" in fnl: 2431 reader = vtk.new("PNGReader") 2432 elif ".bmp" in fnl: 2433 reader = vtk.new("BMPReader") 2434 else: 2435 vedo.logger.error("in texture() supported files are only PNG, BMP or JPG") 2436 return self 2437 reader.SetFileName(fn) 2438 reader.Update() 2439 out_img = reader.GetOutput() 2440 2441 else: 2442 vedo.logger.error(f"in texture() cannot understand input {type(tname)}") 2443 return self 2444 2445 if tcoords is not None: 2446 2447 if isinstance(tcoords, str): 2448 vtarr = pd.GetPointData().GetArray(tcoords) 2449 2450 else: 2451 tcoords = np.asarray(tcoords) 2452 if tcoords.ndim != 2: 2453 vedo.logger.error("tcoords must be a 2-dimensional array") 2454 return self 2455 if tcoords.shape[0] != pd.GetNumberOfPoints(): 2456 vedo.logger.error("nr of texture coords must match nr of points") 2457 return self 2458 if tcoords.shape[1] != 2: 2459 vedo.logger.error("tcoords texture vector must have 2 components") 2460 vtarr = utils.numpy2vtk(tcoords) 2461 vtarr.SetName("TCoordinates") 2462 2463 pd.GetPointData().SetTCoords(vtarr) 2464 pd.GetPointData().Modified() 2465 2466 elif not pd.GetPointData().GetTCoords(): 2467 2468 # TCoords still void.. 2469 # check that there are no texture-like arrays: 2470 names = self.pointdata.keys() 2471 candidate_arr = "" 2472 for name in names: 2473 vtarr = pd.GetPointData().GetArray(name) 2474 if vtarr.GetNumberOfComponents() != 2: 2475 continue 2476 t0, t1 = vtarr.GetRange() 2477 if t0 >= 0 and t1 <= 1: 2478 candidate_arr = name 2479 2480 if candidate_arr: 2481 2482 vtarr = pd.GetPointData().GetArray(candidate_arr) 2483 pd.GetPointData().SetTCoords(vtarr) 2484 pd.GetPointData().Modified() 2485 2486 else: 2487 # last resource is automatic mapping 2488 tmapper = vtk.new("TextureMapToPlane") 2489 tmapper.AutomaticPlaneGenerationOn() 2490 tmapper.SetInputData(pd) 2491 tmapper.Update() 2492 tc = tmapper.GetOutput().GetPointData().GetTCoords() 2493 if scale or ushift or vshift: 2494 ntc = utils.vtk2numpy(tc) 2495 if scale: 2496 ntc *= scale 2497 if ushift: 2498 ntc[:, 0] += ushift 2499 if vshift: 2500 ntc[:, 1] += vshift 2501 tc = utils.numpy2vtk(tc) 2502 pd.GetPointData().SetTCoords(tc) 2503 pd.GetPointData().Modified() 2504 2505 if out_img: 2506 tu.SetInputData(out_img) 2507 tu.SetInterpolate(interpolate) 2508 tu.SetRepeat(repeat) 2509 tu.SetEdgeClamp(edge_clamp) 2510 2511 self.properties.SetColor(1, 1, 1) 2512 self.mapper.ScalarVisibilityOff() 2513 self.actor.SetTexture(tu) 2514 2515 if seam_threshold is not None: 2516 tname = self.dataset.GetPointData().GetTCoords().GetName() 2517 grad = self.gradient(tname) 2518 ugrad, vgrad = np.split(grad, 2, axis=1) 2519 ugradm, vgradm = utils.mag2(ugrad), utils.mag2(vgrad) 2520 gradm = np.log(ugradm + vgradm) 2521 largegrad_ids = np.arange(len(grad))[gradm > seam_threshold * 4] 2522 uvmap = self.pointdata[tname] 2523 # collapse triangles that have large gradient 2524 new_points = self.vertices.copy() 2525 for f in self.cells: 2526 if np.isin(f, largegrad_ids).all(): 2527 id1, id2, id3 = f 2528 uv1, uv2, uv3 = uvmap[f] 2529 d12 = utils.mag2(uv1 - uv2) 2530 d23 = utils.mag2(uv2 - uv3) 2531 d31 = utils.mag2(uv3 - uv1) 2532 idm = np.argmin([d12, d23, d31]) 2533 if idm == 0: 2534 new_points[id1] = new_points[id3] 2535 new_points[id2] = new_points[id3] 2536 elif idm == 1: 2537 new_points[id2] = new_points[id1] 2538 new_points[id3] = new_points[id1] 2539 self.vertices = new_points 2540 2541 self.dataset.Modified() 2542 return self 2543 2544######################################################################################## 2545class VolumeVisual(CommonVisual): 2546 """Class to manage the visual aspects of a ``Volume`` object.""" 2547 2548 def __init__(self) -> None: 2549 # print("INIT VolumeVisual") 2550 super().__init__() 2551 2552 def alpha_unit(self, u=None): 2553 """ 2554 Defines light attenuation per unit length. Default is 1. 2555 The larger the unit length, the further light has to travel to attenuate the same amount. 2556 2557 E.g., if you set the unit distance to 0, you will get full opacity. 2558 It means that when light travels 0 distance it's already attenuated a finite amount. 2559 Thus, any finite distance should attenuate all light. 2560 The larger you make the unit distance, the more transparent the rendering becomes. 2561 """ 2562 if u is None: 2563 return self.properties.GetScalarOpacityUnitDistance() 2564 self.properties.SetScalarOpacityUnitDistance(u) 2565 return self 2566 2567 def alpha_gradient(self, alpha_grad, vmin=None, vmax=None): 2568 """ 2569 Assign a set of tranparencies to a volume's gradient 2570 along the range of the scalar value. 2571 A single constant value can also be assigned. 2572 The gradient function is used to decrease the opacity 2573 in the "flat" regions of the volume while maintaining the opacity 2574 at the boundaries between material types. The gradient is measured 2575 as the amount by which the intensity changes over unit distance. 2576 2577 The format for alpha_grad is the same as for method `volume.alpha()`. 2578 """ 2579 if vmin is None: 2580 vmin, _ = self.dataset.GetScalarRange() 2581 if vmax is None: 2582 _, vmax = self.dataset.GetScalarRange() 2583 2584 if alpha_grad is None: 2585 self.properties.DisableGradientOpacityOn() 2586 return self 2587 2588 self.properties.DisableGradientOpacityOff() 2589 2590 gotf = self.properties.GetGradientOpacity() 2591 if utils.is_sequence(alpha_grad): 2592 alpha_grad = np.array(alpha_grad) 2593 if len(alpha_grad.shape) == 1: # user passing a flat list e.g. (0.0, 0.3, 0.9, 1) 2594 for i, al in enumerate(alpha_grad): 2595 xalpha = vmin + (vmax - vmin) * i / (len(alpha_grad) - 1) 2596 # Create transfer mapping scalar value to gradient opacity 2597 gotf.AddPoint(xalpha, al) 2598 elif len(alpha_grad.shape) == 2: # user passing [(x0,alpha0), ...] 2599 gotf.AddPoint(vmin, alpha_grad[0][1]) 2600 for xalpha, al in alpha_grad: 2601 # Create transfer mapping scalar value to opacity 2602 gotf.AddPoint(xalpha, al) 2603 gotf.AddPoint(vmax, alpha_grad[-1][1]) 2604 # print("alpha_grad at", round(xalpha, 1), "\tset to", al) 2605 else: 2606 gotf.AddPoint(vmin, alpha_grad) # constant alpha_grad 2607 gotf.AddPoint(vmax, alpha_grad) 2608 return self 2609 2610 def cmap(self, c, alpha=None, vmin=None, vmax=None): 2611 """Same as `color()`. 2612 2613 Arguments: 2614 alpha : (list) 2615 use a list to specify transparencies along the scalar range 2616 vmin : (float) 2617 force the min of the scalar range to be this value 2618 vmax : (float) 2619 force the max of the scalar range to be this value 2620 """ 2621 return self.color(c, alpha, vmin, vmax) 2622 2623 def jittering(self, status=None): 2624 """ 2625 If `True`, each ray traversal direction will be perturbed slightly 2626 using a noise-texture to get rid of wood-grain effects. 2627 """ 2628 if hasattr(self.mapper, "SetUseJittering"): # tetmesh doesnt have it 2629 if status is None: 2630 return self.mapper.GetUseJittering() 2631 self.mapper.SetUseJittering(status) 2632 return self 2633 2634 def hide_voxels(self, ids): 2635 """ 2636 Hide voxels (cells) from visualization. 2637 2638 Example: 2639 ```python 2640 from vedo import * 2641 embryo = Volume(dataurl+'embryo.tif') 2642 embryo.hide_voxels(list(range(10000))) 2643 show(embryo, axes=1).close() 2644 ``` 2645 2646 See also: 2647 `volume.mask()` 2648 """ 2649 ghost_mask = np.zeros(self.ncells, dtype=np.uint8) 2650 ghost_mask[ids] = vtk.vtkDataSetAttributes.HIDDENCELL 2651 name = vtk.vtkDataSetAttributes.GhostArrayName() 2652 garr = utils.numpy2vtk(ghost_mask, name=name, dtype=np.uint8) 2653 self.dataset.GetCellData().AddArray(garr) 2654 self.dataset.GetCellData().Modified() 2655 return self 2656 2657 2658 def mode(self, mode=None): 2659 """ 2660 Define the volumetric rendering mode following this: 2661 - 0, composite rendering 2662 - 1, maximum projection rendering 2663 - 2, minimum projection rendering 2664 - 3, average projection rendering 2665 - 4, additive mode 2666 2667 The default mode is "composite" where the scalar values are sampled through 2668 the volume and composited in a front-to-back scheme through alpha blending. 2669 The final color and opacity is determined using the color and opacity transfer 2670 functions specified in alpha keyword. 2671 2672 Maximum and minimum intensity blend modes use the maximum and minimum 2673 scalar values, respectively, along the sampling ray. 2674 The final color and opacity is determined by passing the resultant value 2675 through the color and opacity transfer functions. 2676 2677 Additive blend mode accumulates scalar values by passing each value 2678 through the opacity transfer function and then adding up the product 2679 of the value and its opacity. In other words, the scalar values are scaled 2680 using the opacity transfer function and summed to derive the final color. 2681 Note that the resulting image is always grayscale i.e. aggregated values 2682 are not passed through the color transfer function. 2683 This is because the final value is a derived value and not a real data value 2684 along the sampling ray. 2685 2686 Average intensity blend mode works similar to the additive blend mode where 2687 the scalar values are multiplied by opacity calculated from the opacity 2688 transfer function and then added. 2689 The additional step here is to divide the sum by the number of samples 2690 taken through the volume. 2691 As is the case with the additive intensity projection, the final image will 2692 always be grayscale i.e. the aggregated values are not passed through the 2693 color transfer function. 2694 """ 2695 if mode is None: 2696 return self.mapper.GetBlendMode() 2697 2698 if isinstance(mode, str): 2699 if "comp" in mode: 2700 mode = 0 2701 elif "proj" in mode: 2702 if "max" in mode: 2703 mode = 1 2704 elif "min" in mode: 2705 mode = 2 2706 elif "ave" in mode: 2707 mode = 3 2708 else: 2709 vedo.logger.warning(f"unknown mode {mode}") 2710 mode = 0 2711 elif "add" in mode: 2712 mode = 4 2713 else: 2714 vedo.logger.warning(f"unknown mode {mode}") 2715 mode = 0 2716 2717 self.mapper.SetBlendMode(mode) 2718 return self 2719 2720 def shade(self, status=None): 2721 """ 2722 Set/Get the shading of a Volume. 2723 Shading can be further controlled with `volume.lighting()` method. 2724 2725 If shading is turned on, the mapper may perform shading calculations. 2726 In some cases shading does not apply 2727 (for example, in maximum intensity projection mode). 2728 """ 2729 if status is None: 2730 return self.properties.GetShade() 2731 self.properties.SetShade(status) 2732 return self 2733 2734 2735 def mask(self, data): 2736 """ 2737 Mask a volume visualization with a binary value. 2738 Needs to specify keyword mapper='gpu'. 2739 2740 Example: 2741 ```python 2742 from vedo import np, Volume, show 2743 data_matrix = np.zeros([75, 75, 75], dtype=np.uint8) 2744 # all voxels have value zero except: 2745 data_matrix[0:35, 0:35, 0:35] = 1 2746 data_matrix[35:55, 35:55, 35:55] = 2 2747 data_matrix[55:74, 55:74, 55:74] = 3 2748 vol = Volume(data_matrix, c=['white','b','g','r'], mapper='gpu') 2749 data_mask = np.zeros_like(data_matrix) 2750 data_mask[10:65, 10:45, 20:75] = 1 2751 vol.mask(data_mask) 2752 show(vol, axes=1).close() 2753 ``` 2754 See also: 2755 `volume.hide_voxels()` 2756 """ 2757 mask = vedo.Volume(data.astype(np.uint8)) 2758 try: 2759 self.mapper.SetMaskTypeToBinary() 2760 self.mapper.SetMaskInput(mask.dataset) 2761 except AttributeError: 2762 vedo.logger.error("volume.mask() must create the volume with Volume(..., mapper='gpu')") 2763 return self 2764 2765 def interpolation(self, itype): 2766 """ 2767 Set interpolation type. 2768 2769 0=nearest neighbour, 1=linear 2770 """ 2771 self.properties.SetInterpolationType(itype) 2772 return self 2773 2774 2775######################################################################################## 2776class ImageVisual(CommonVisual, Actor3DHelper): 2777 2778 def __init__(self) -> None: 2779 # print("init ImageVisual") 2780 super().__init__() 2781 2782 def memory_size(self): 2783 """ 2784 Return the size in bytes of the object in memory. 2785 """ 2786 return self.dataset.GetActualMemorySize() 2787 2788 def scalar_range(self): 2789 """ 2790 Return the scalar range of the image. 2791 """ 2792 return self.dataset.GetScalarRange() 2793 2794 def alpha(self, a=None): 2795 """Set/get image's transparency in the rendering scene.""" 2796 if a is not None: 2797 self.properties.SetOpacity(a) 2798 return self 2799 return self.properties.GetOpacity() 2800 2801 def level(self, value=None): 2802 """Get/Set the image color level (brightness) in the rendering scene.""" 2803 if value is None: 2804 return self.properties.GetColorLevel() 2805 self.properties.SetColorLevel(value) 2806 return self 2807 2808 def window(self, value=None): 2809 """Get/Set the image color window (contrast) in the rendering scene.""" 2810 if value is None: 2811 return self.properties.GetColorWindow() 2812 self.properties.SetColorWindow(value) 2813 return self
32class CommonVisual: 33 """Class to manage the visual aspects common to all objects.""" 34 35 def __init__(self): 36 # print("init CommonVisual") 37 self.mapper = None 38 self.properties = None 39 self.actor = None 40 self.scalarbar = None 41 self.trail = None 42 self.shadows = [] 43 44 def print(self): 45 """Print object info.""" 46 print(self.__str__()) 47 return self 48 49 @property 50 def LUT(self): 51 """Return the lookup table of the object as a numpy object.""" 52 try: 53 _lut = self.mapper.GetLookupTable() 54 55 values = [] 56 for i in range(_lut.GetTable().GetNumberOfTuples()): 57 # print("LUT i =", i, "value =", _lut.GetTableValue(i)) 58 values.append(_lut.GetTableValue(i)) 59 return np.array(values) 60 except AttributeError: 61 pass 62 63 @LUT.setter 64 def LUT(self, arr): 65 """ 66 Set the lookup table of the object from a numpy object. 67 Consider using `cmap()` or `build_lut()` instead as it allows 68 to set the range of the LUT and to use a string name for the color map. 69 """ 70 _newlut = vtk.vtkLookupTable() 71 _newlut.SetNumberOfTableValues(len(arr)) 72 if len(arr[0]) == 3: 73 arr = np.insert(arr, 3, 1, axis=1) 74 for i, v in enumerate(arr): 75 _newlut.SetTableValue(i, v) 76 _newlut.SetRange(self.mapper.GetScalarRange()) 77 _newlut.Build() 78 self.mapper.SetLookupTable(_newlut) 79 self.mapper.ScalarVisibilityOn() 80 81 def add_observer(self, event_name, func, priority=0): 82 """Add a callback function that will be called when an event occurs.""" 83 event_name = utils.get_vtk_name_event(event_name) 84 idd = self.actor.AddObserver(event_name, func, priority) 85 return idd 86 87 def show(self, **options): 88 """ 89 Create on the fly an instance of class `Plotter` or use the last existing one to 90 show one single object. 91 92 This method is meant as a shortcut. If more than one object needs to be visualised 93 please use the syntax `show(mesh1, mesh2, volume, ..., options)`. 94 95 Returns the `Plotter` class instance. 96 """ 97 return vedo.plotter.show(self, **options) 98 99 def thumbnail(self, zoom=1.25, size=(200, 200), bg="white", azimuth=0, elevation=0, axes=False): 100 """Build a thumbnail of the object and return it as an array.""" 101 # speed is about 20Hz for size=[200,200] 102 ren = vtk.vtkRenderer() 103 104 actor = self.actor 105 if isinstance(self, vedo.UnstructuredGrid): 106 geo = vtk.new("GeometryFilter") 107 geo.SetInputData(self.dataset) 108 geo.Update() 109 actor = vedo.Mesh(geo.GetOutput()).cmap("rainbow").actor 110 111 ren.AddActor(actor) 112 if axes: 113 axes = vedo.addons.Axes(self) 114 ren.AddActor(axes.actor) 115 ren.ResetCamera() 116 cam = ren.GetActiveCamera() 117 cam.Zoom(zoom) 118 cam.Elevation(elevation) 119 cam.Azimuth(azimuth) 120 121 ren_win = vtk.vtkRenderWindow() 122 ren_win.SetOffScreenRendering(True) 123 ren_win.SetSize(size) 124 ren.SetBackground(colors.get_color(bg)) 125 ren_win.AddRenderer(ren) 126 ren_win.Render() 127 128 nx, ny = ren_win.GetSize() 129 arr = vtk.vtkUnsignedCharArray() 130 ren_win.GetRGBACharPixelData(0, 0, nx - 1, ny - 1, 0, arr) 131 narr = utils.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3]) 132 narr = np.ascontiguousarray(np.flip(narr, axis=0)) 133 134 ren.RemoveActor(actor) 135 if axes: 136 ren.RemoveActor(axes.actor) 137 ren_win.Finalize() 138 del ren_win 139 return narr 140 141 def pickable(self, value=None): 142 """Set/get the pickability property of an object.""" 143 if value is None: 144 return self.actor.GetPickable() 145 self.actor.SetPickable(value) 146 return self 147 148 def use_bounds(self, value=True): 149 """ 150 Instruct the current camera to either take into account or ignore 151 the object bounds when resetting. 152 """ 153 self.actor.SetUseBounds(value) 154 return self 155 156 def draggable(self, value=None): # NOT FUNCTIONAL? 157 """Set/get the draggability property of an object.""" 158 if value is None: 159 return self.actor.GetDragable() 160 self.actor.SetDragable(value) 161 return self 162 163 def on(self): 164 """Switch on object visibility. Object is not removed.""" 165 self.actor.VisibilityOn() 166 try: 167 self.scalarbar.actor.VisibilityOn() 168 except AttributeError: 169 pass 170 try: 171 self.trail.actor.VisibilityOn() 172 except AttributeError: 173 pass 174 try: 175 for sh in self.shadows: 176 sh.actor.VisibilityOn() 177 except AttributeError: 178 pass 179 return self 180 181 def off(self): 182 """Switch off object visibility. Object is not removed.""" 183 self.actor.VisibilityOff() 184 try: 185 self.scalarbar.actor.VisibilityOff() 186 except AttributeError: 187 pass 188 try: 189 self.trail.actor.VisibilityOff() 190 except AttributeError: 191 pass 192 try: 193 for sh in self.shadows: 194 sh.actor.VisibilityOff() 195 except AttributeError: 196 pass 197 return self 198 199 def toggle(self): 200 """Toggle object visibility on/off.""" 201 v = self.actor.GetVisibility() 202 if v: 203 self.off() 204 else: 205 self.on() 206 return self 207 208 def add_scalarbar( 209 self, 210 title="", 211 pos=(0.775, 0.05), 212 title_yoffset=15, 213 font_size=12, 214 size=(60, 350), 215 nlabels=None, 216 c=None, 217 horizontal=False, 218 use_alpha=True, 219 label_format=":6.3g", 220 ): 221 """ 222 Add a 2D scalar bar for the specified obj. 223 224 Arguments: 225 title : (str) 226 scalar bar title 227 pos : (float,float) 228 position coordinates of the bottom left corner 229 title_yoffset : (float) 230 vertical space offset between title and color scalarbar 231 font_size : (float) 232 size of font for title and numeric labels 233 size : (float,float) 234 size of the scalarbar in number of pixels (width, height) 235 nlabels : (int) 236 number of numeric labels 237 c : (list) 238 color of the scalar bar text 239 horizontal : (bool) 240 lay the scalarbar horizontally 241 use_alpha : (bool) 242 render transparency in the color bar itself 243 label_format : (str) 244 c-style format string for numeric labels 245 246 Examples: 247 - [mesh_coloring.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_coloring.py) 248 - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py) 249 """ 250 plt = vedo.plotter_instance 251 252 if plt and plt.renderer: 253 c = (0.9, 0.9, 0.9) 254 if np.sum(plt.renderer.GetBackground()) > 1.5: 255 c = (0.1, 0.1, 0.1) 256 if isinstance(self.scalarbar, vtk.vtkActor): 257 plt.renderer.RemoveActor(self.scalarbar) 258 elif isinstance(self.scalarbar, vedo.Assembly): 259 for a in self.scalarbar.unpack(): 260 plt.renderer.RemoveActor(a) 261 if c is None: 262 c = "gray" 263 264 sb = vedo.addons.ScalarBar( 265 self, 266 title, 267 pos, 268 title_yoffset, 269 font_size, 270 size, 271 nlabels, 272 c, 273 horizontal, 274 use_alpha, 275 label_format, 276 ) 277 self.scalarbar = sb 278 return self 279 280 def add_scalarbar3d( 281 self, 282 title="", 283 pos=None, 284 size=(0, 0), 285 title_font="", 286 title_xoffset=-1.2, 287 title_yoffset=0.0, 288 title_size=1.5, 289 title_rotation=0.0, 290 nlabels=9, 291 label_font="", 292 label_size=1, 293 label_offset=0.375, 294 label_rotation=0, 295 label_format="", 296 italic=0, 297 c=None, 298 draw_box=True, 299 above_text=None, 300 below_text=None, 301 nan_text="NaN", 302 categories=None, 303 ): 304 """ 305 Associate a 3D scalar bar to the object and add it to the scene. 306 The new scalarbar object (Assembly) will be accessible as obj.scalarbar 307 308 Arguments: 309 size : (list) 310 (thickness, length) of scalarbar 311 title : (str) 312 scalar bar title 313 title_xoffset : (float) 314 horizontal space btw title and color scalarbar 315 title_yoffset : (float) 316 vertical space offset 317 title_size : (float) 318 size of title wrt numeric labels 319 title_rotation : (float) 320 title rotation in degrees 321 nlabels : (int) 322 number of numeric labels 323 label_font : (str) 324 font type for labels 325 label_size : (float) 326 label scale factor 327 label_offset : (float) 328 space btw numeric labels and scale 329 label_rotation : (float) 330 label rotation in degrees 331 label_format : (str) 332 label format for floats and integers (e.g. `':.2f'`) 333 draw_box : (bool) 334 draw a box around the colorbar 335 categories : (list) 336 make a categorical scalarbar, 337 the input list will have the format `[value, color, alpha, textlabel]` 338 339 Examples: 340 - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py) 341 """ 342 plt = vedo.plotter_instance 343 if plt and c is None: # automatic black or white 344 c = (0.9, 0.9, 0.9) 345 if np.sum(vedo.get_color(plt.backgrcol)) > 1.5: 346 c = (0.1, 0.1, 0.1) 347 if c is None: 348 c = (0, 0, 0) 349 c = vedo.get_color(c) 350 351 self.scalarbar = vedo.addons.ScalarBar3D( 352 self, 353 title, 354 pos, 355 size, 356 title_font, 357 title_xoffset, 358 title_yoffset, 359 title_size, 360 title_rotation, 361 nlabels, 362 label_font, 363 label_size, 364 label_offset, 365 label_rotation, 366 label_format, 367 italic, 368 c, 369 draw_box, 370 above_text, 371 below_text, 372 nan_text, 373 categories, 374 ) 375 return self 376 377 def color(self, col, alpha=None, vmin=None, vmax=None): 378 """ 379 Assign a color or a set of colors along the range of the scalar value. 380 A single constant color can also be assigned. 381 Any matplotlib color map name is also accepted, e.g. `volume.color('jet')`. 382 383 E.g.: say that your cells scalar runs from -3 to 6, 384 and you want -3 to show red and 1.5 violet and 6 green, then just set: 385 386 `volume.color(['red', 'violet', 'green'])` 387 388 You can also assign a specific color to a aspecific value with eg.: 389 390 `volume.color([(0,'red'), (0.5,'violet'), (1,'green')])` 391 392 Arguments: 393 alpha : (list) 394 use a list to specify transparencies along the scalar range 395 vmin : (float) 396 force the min of the scalar range to be this value 397 vmax : (float) 398 force the max of the scalar range to be this value 399 """ 400 # supersedes method in Points, Mesh 401 402 if col is None: 403 return self 404 405 if vmin is None: 406 vmin, _ = self.dataset.GetScalarRange() 407 if vmax is None: 408 _, vmax = self.dataset.GetScalarRange() 409 ctf = self.properties.GetRGBTransferFunction() 410 ctf.RemoveAllPoints() 411 412 if utils.is_sequence(col): 413 if utils.is_sequence(col[0]) and len(col[0]) == 2: 414 # user passing [(value1, color1), ...] 415 for x, ci in col: 416 r, g, b = colors.get_color(ci) 417 ctf.AddRGBPoint(x, r, g, b) 418 # colors.printc('color at', round(x, 1), 419 # 'set to', colors.get_color_name((r, g, b)), bold=0) 420 else: 421 # user passing [color1, color2, ..] 422 for i, ci in enumerate(col): 423 r, g, b = colors.get_color(ci) 424 x = vmin + (vmax - vmin) * i / (len(col) - 1) 425 ctf.AddRGBPoint(x, r, g, b) 426 elif isinstance(col, str): 427 if col in colors.colors.keys() or col in colors.color_nicks.keys(): 428 r, g, b = colors.get_color(col) 429 ctf.AddRGBPoint(vmin, r, g, b) # constant color 430 ctf.AddRGBPoint(vmax, r, g, b) 431 else: # assume it's a colormap 432 for x in np.linspace(vmin, vmax, num=64, endpoint=True): 433 r, g, b = colors.color_map(x, name=col, vmin=vmin, vmax=vmax) 434 ctf.AddRGBPoint(x, r, g, b) 435 elif isinstance(col, int): 436 r, g, b = colors.get_color(col) 437 ctf.AddRGBPoint(vmin, r, g, b) # constant color 438 ctf.AddRGBPoint(vmax, r, g, b) 439 else: 440 vedo.logger.warning(f"in color() unknown input type {type(col)}") 441 442 if alpha is not None: 443 self.alpha(alpha, vmin=vmin, vmax=vmax) 444 return self 445 446 def alpha(self, alpha, vmin=None, vmax=None): 447 """ 448 Assign a set of tranparencies along the range of the scalar value. 449 A single constant value can also be assigned. 450 451 E.g.: say `alpha=(0.0, 0.3, 0.9, 1)` and the scalar range goes from -10 to 150. 452 Then all cells with a value close to -10 will be completely transparent, cells at 1/4 453 of the range will get an alpha equal to 0.3 and voxels with value close to 150 454 will be completely opaque. 455 456 As a second option one can set explicit (x, alpha_x) pairs to define the transfer function. 457 458 E.g.: say `alpha=[(-5, 0), (35, 0.4) (123,0.9)]` and the scalar range goes from -10 to 150. 459 Then all cells below -5 will be completely transparent, cells with a scalar value of 35 460 will get an opacity of 40% and above 123 alpha is set to 90%. 461 """ 462 if vmin is None: 463 vmin, _ = self.dataset.GetScalarRange() 464 if vmax is None: 465 _, vmax = self.dataset.GetScalarRange() 466 otf = self.properties.GetScalarOpacity() 467 otf.RemoveAllPoints() 468 469 if utils.is_sequence(alpha): 470 alpha = np.array(alpha) 471 if len(alpha.shape) == 1: # user passing a flat list e.g. (0.0, 0.3, 0.9, 1) 472 for i, al in enumerate(alpha): 473 xalpha = vmin + (vmax - vmin) * i / (len(alpha) - 1) 474 # Create transfer mapping scalar value to opacity 475 otf.AddPoint(xalpha, al) 476 # print("alpha at", round(xalpha, 1), "\tset to", al) 477 elif len(alpha.shape) == 2: # user passing [(x0,alpha0), ...] 478 otf.AddPoint(vmin, alpha[0][1]) 479 for xalpha, al in alpha: 480 # Create transfer mapping scalar value to opacity 481 otf.AddPoint(xalpha, al) 482 otf.AddPoint(vmax, alpha[-1][1]) 483 484 else: 485 486 otf.AddPoint(vmin, alpha) # constant alpha 487 otf.AddPoint(vmax, alpha) 488 489 return self
Class to manage the visual aspects common to all objects.
81 def add_observer(self, event_name, func, priority=0): 82 """Add a callback function that will be called when an event occurs.""" 83 event_name = utils.get_vtk_name_event(event_name) 84 idd = self.actor.AddObserver(event_name, func, priority) 85 return idd
Add a callback function that will be called when an event occurs.
87 def show(self, **options): 88 """ 89 Create on the fly an instance of class `Plotter` or use the last existing one to 90 show one single object. 91 92 This method is meant as a shortcut. If more than one object needs to be visualised 93 please use the syntax `show(mesh1, mesh2, volume, ..., options)`. 94 95 Returns the `Plotter` class instance. 96 """ 97 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.
99 def thumbnail(self, zoom=1.25, size=(200, 200), bg="white", azimuth=0, elevation=0, axes=False): 100 """Build a thumbnail of the object and return it as an array.""" 101 # speed is about 20Hz for size=[200,200] 102 ren = vtk.vtkRenderer() 103 104 actor = self.actor 105 if isinstance(self, vedo.UnstructuredGrid): 106 geo = vtk.new("GeometryFilter") 107 geo.SetInputData(self.dataset) 108 geo.Update() 109 actor = vedo.Mesh(geo.GetOutput()).cmap("rainbow").actor 110 111 ren.AddActor(actor) 112 if axes: 113 axes = vedo.addons.Axes(self) 114 ren.AddActor(axes.actor) 115 ren.ResetCamera() 116 cam = ren.GetActiveCamera() 117 cam.Zoom(zoom) 118 cam.Elevation(elevation) 119 cam.Azimuth(azimuth) 120 121 ren_win = vtk.vtkRenderWindow() 122 ren_win.SetOffScreenRendering(True) 123 ren_win.SetSize(size) 124 ren.SetBackground(colors.get_color(bg)) 125 ren_win.AddRenderer(ren) 126 ren_win.Render() 127 128 nx, ny = ren_win.GetSize() 129 arr = vtk.vtkUnsignedCharArray() 130 ren_win.GetRGBACharPixelData(0, 0, nx - 1, ny - 1, 0, arr) 131 narr = utils.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3]) 132 narr = np.ascontiguousarray(np.flip(narr, axis=0)) 133 134 ren.RemoveActor(actor) 135 if axes: 136 ren.RemoveActor(axes.actor) 137 ren_win.Finalize() 138 del ren_win 139 return narr
Build a thumbnail of the object and return it as an array.
141 def pickable(self, value=None): 142 """Set/get the pickability property of an object.""" 143 if value is None: 144 return self.actor.GetPickable() 145 self.actor.SetPickable(value) 146 return self
Set/get the pickability property of an object.
148 def use_bounds(self, value=True): 149 """ 150 Instruct the current camera to either take into account or ignore 151 the object bounds when resetting. 152 """ 153 self.actor.SetUseBounds(value) 154 return self
Instruct the current camera to either take into account or ignore the object bounds when resetting.
156 def draggable(self, value=None): # NOT FUNCTIONAL? 157 """Set/get the draggability property of an object.""" 158 if value is None: 159 return self.actor.GetDragable() 160 self.actor.SetDragable(value) 161 return self
Set/get the draggability property of an object.
163 def on(self): 164 """Switch on object visibility. Object is not removed.""" 165 self.actor.VisibilityOn() 166 try: 167 self.scalarbar.actor.VisibilityOn() 168 except AttributeError: 169 pass 170 try: 171 self.trail.actor.VisibilityOn() 172 except AttributeError: 173 pass 174 try: 175 for sh in self.shadows: 176 sh.actor.VisibilityOn() 177 except AttributeError: 178 pass 179 return self
Switch on object visibility. Object is not removed.
181 def off(self): 182 """Switch off object visibility. Object is not removed.""" 183 self.actor.VisibilityOff() 184 try: 185 self.scalarbar.actor.VisibilityOff() 186 except AttributeError: 187 pass 188 try: 189 self.trail.actor.VisibilityOff() 190 except AttributeError: 191 pass 192 try: 193 for sh in self.shadows: 194 sh.actor.VisibilityOff() 195 except AttributeError: 196 pass 197 return self
Switch off object visibility. Object is not removed.
199 def toggle(self): 200 """Toggle object visibility on/off.""" 201 v = self.actor.GetVisibility() 202 if v: 203 self.off() 204 else: 205 self.on() 206 return self
Toggle object visibility on/off.
208 def add_scalarbar( 209 self, 210 title="", 211 pos=(0.775, 0.05), 212 title_yoffset=15, 213 font_size=12, 214 size=(60, 350), 215 nlabels=None, 216 c=None, 217 horizontal=False, 218 use_alpha=True, 219 label_format=":6.3g", 220 ): 221 """ 222 Add a 2D scalar bar for the specified obj. 223 224 Arguments: 225 title : (str) 226 scalar bar title 227 pos : (float,float) 228 position coordinates of the bottom left corner 229 title_yoffset : (float) 230 vertical space offset between title and color scalarbar 231 font_size : (float) 232 size of font for title and numeric labels 233 size : (float,float) 234 size of the scalarbar in number of pixels (width, height) 235 nlabels : (int) 236 number of numeric labels 237 c : (list) 238 color of the scalar bar text 239 horizontal : (bool) 240 lay the scalarbar horizontally 241 use_alpha : (bool) 242 render transparency in the color bar itself 243 label_format : (str) 244 c-style format string for numeric labels 245 246 Examples: 247 - [mesh_coloring.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_coloring.py) 248 - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py) 249 """ 250 plt = vedo.plotter_instance 251 252 if plt and plt.renderer: 253 c = (0.9, 0.9, 0.9) 254 if np.sum(plt.renderer.GetBackground()) > 1.5: 255 c = (0.1, 0.1, 0.1) 256 if isinstance(self.scalarbar, vtk.vtkActor): 257 plt.renderer.RemoveActor(self.scalarbar) 258 elif isinstance(self.scalarbar, vedo.Assembly): 259 for a in self.scalarbar.unpack(): 260 plt.renderer.RemoveActor(a) 261 if c is None: 262 c = "gray" 263 264 sb = vedo.addons.ScalarBar( 265 self, 266 title, 267 pos, 268 title_yoffset, 269 font_size, 270 size, 271 nlabels, 272 c, 273 horizontal, 274 use_alpha, 275 label_format, 276 ) 277 self.scalarbar = sb 278 return self
Add a 2D scalar bar for the specified obj.
Arguments:
- title : (str) scalar bar title
- pos : (float,float) position coordinates of the bottom left corner
- title_yoffset : (float) vertical space offset between title and color scalarbar
- font_size : (float) size of font for title and numeric labels
- size : (float,float) size of the scalarbar in number of pixels (width, height)
- nlabels : (int) number of numeric labels
- c : (list) color of the scalar bar text
- horizontal : (bool) lay the scalarbar horizontally
- use_alpha : (bool) render transparency in the color bar itself
- label_format : (str) c-style format string for numeric labels
Examples:
280 def add_scalarbar3d( 281 self, 282 title="", 283 pos=None, 284 size=(0, 0), 285 title_font="", 286 title_xoffset=-1.2, 287 title_yoffset=0.0, 288 title_size=1.5, 289 title_rotation=0.0, 290 nlabels=9, 291 label_font="", 292 label_size=1, 293 label_offset=0.375, 294 label_rotation=0, 295 label_format="", 296 italic=0, 297 c=None, 298 draw_box=True, 299 above_text=None, 300 below_text=None, 301 nan_text="NaN", 302 categories=None, 303 ): 304 """ 305 Associate a 3D scalar bar to the object and add it to the scene. 306 The new scalarbar object (Assembly) will be accessible as obj.scalarbar 307 308 Arguments: 309 size : (list) 310 (thickness, length) of scalarbar 311 title : (str) 312 scalar bar title 313 title_xoffset : (float) 314 horizontal space btw title and color scalarbar 315 title_yoffset : (float) 316 vertical space offset 317 title_size : (float) 318 size of title wrt numeric labels 319 title_rotation : (float) 320 title rotation in degrees 321 nlabels : (int) 322 number of numeric labels 323 label_font : (str) 324 font type for labels 325 label_size : (float) 326 label scale factor 327 label_offset : (float) 328 space btw numeric labels and scale 329 label_rotation : (float) 330 label rotation in degrees 331 label_format : (str) 332 label format for floats and integers (e.g. `':.2f'`) 333 draw_box : (bool) 334 draw a box around the colorbar 335 categories : (list) 336 make a categorical scalarbar, 337 the input list will have the format `[value, color, alpha, textlabel]` 338 339 Examples: 340 - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py) 341 """ 342 plt = vedo.plotter_instance 343 if plt and c is None: # automatic black or white 344 c = (0.9, 0.9, 0.9) 345 if np.sum(vedo.get_color(plt.backgrcol)) > 1.5: 346 c = (0.1, 0.1, 0.1) 347 if c is None: 348 c = (0, 0, 0) 349 c = vedo.get_color(c) 350 351 self.scalarbar = vedo.addons.ScalarBar3D( 352 self, 353 title, 354 pos, 355 size, 356 title_font, 357 title_xoffset, 358 title_yoffset, 359 title_size, 360 title_rotation, 361 nlabels, 362 label_font, 363 label_size, 364 label_offset, 365 label_rotation, 366 label_format, 367 italic, 368 c, 369 draw_box, 370 above_text, 371 below_text, 372 nan_text, 373 categories, 374 ) 375 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:
377 def color(self, col, alpha=None, vmin=None, vmax=None): 378 """ 379 Assign a color or a set of colors along the range of the scalar value. 380 A single constant color can also be assigned. 381 Any matplotlib color map name is also accepted, e.g. `volume.color('jet')`. 382 383 E.g.: say that your cells scalar runs from -3 to 6, 384 and you want -3 to show red and 1.5 violet and 6 green, then just set: 385 386 `volume.color(['red', 'violet', 'green'])` 387 388 You can also assign a specific color to a aspecific value with eg.: 389 390 `volume.color([(0,'red'), (0.5,'violet'), (1,'green')])` 391 392 Arguments: 393 alpha : (list) 394 use a list to specify transparencies along the scalar range 395 vmin : (float) 396 force the min of the scalar range to be this value 397 vmax : (float) 398 force the max of the scalar range to be this value 399 """ 400 # supersedes method in Points, Mesh 401 402 if col is None: 403 return self 404 405 if vmin is None: 406 vmin, _ = self.dataset.GetScalarRange() 407 if vmax is None: 408 _, vmax = self.dataset.GetScalarRange() 409 ctf = self.properties.GetRGBTransferFunction() 410 ctf.RemoveAllPoints() 411 412 if utils.is_sequence(col): 413 if utils.is_sequence(col[0]) and len(col[0]) == 2: 414 # user passing [(value1, color1), ...] 415 for x, ci in col: 416 r, g, b = colors.get_color(ci) 417 ctf.AddRGBPoint(x, r, g, b) 418 # colors.printc('color at', round(x, 1), 419 # 'set to', colors.get_color_name((r, g, b)), bold=0) 420 else: 421 # user passing [color1, color2, ..] 422 for i, ci in enumerate(col): 423 r, g, b = colors.get_color(ci) 424 x = vmin + (vmax - vmin) * i / (len(col) - 1) 425 ctf.AddRGBPoint(x, r, g, b) 426 elif isinstance(col, str): 427 if col in colors.colors.keys() or col in colors.color_nicks.keys(): 428 r, g, b = colors.get_color(col) 429 ctf.AddRGBPoint(vmin, r, g, b) # constant color 430 ctf.AddRGBPoint(vmax, r, g, b) 431 else: # assume it's a colormap 432 for x in np.linspace(vmin, vmax, num=64, endpoint=True): 433 r, g, b = colors.color_map(x, name=col, vmin=vmin, vmax=vmax) 434 ctf.AddRGBPoint(x, r, g, b) 435 elif isinstance(col, int): 436 r, g, b = colors.get_color(col) 437 ctf.AddRGBPoint(vmin, r, g, b) # constant color 438 ctf.AddRGBPoint(vmax, r, g, b) 439 else: 440 vedo.logger.warning(f"in color() unknown input type {type(col)}") 441 442 if alpha is not None: 443 self.alpha(alpha, vmin=vmin, vmax=vmax) 444 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
446 def alpha(self, alpha, vmin=None, vmax=None): 447 """ 448 Assign a set of tranparencies along the range of the scalar value. 449 A single constant value can also be assigned. 450 451 E.g.: say `alpha=(0.0, 0.3, 0.9, 1)` and the scalar range goes from -10 to 150. 452 Then all cells with a value close to -10 will be completely transparent, cells at 1/4 453 of the range will get an alpha equal to 0.3 and voxels with value close to 150 454 will be completely opaque. 455 456 As a second option one can set explicit (x, alpha_x) pairs to define the transfer function. 457 458 E.g.: say `alpha=[(-5, 0), (35, 0.4) (123,0.9)]` and the scalar range goes from -10 to 150. 459 Then all cells below -5 will be completely transparent, cells with a scalar value of 35 460 will get an opacity of 40% and above 123 alpha is set to 90%. 461 """ 462 if vmin is None: 463 vmin, _ = self.dataset.GetScalarRange() 464 if vmax is None: 465 _, vmax = self.dataset.GetScalarRange() 466 otf = self.properties.GetScalarOpacity() 467 otf.RemoveAllPoints() 468 469 if utils.is_sequence(alpha): 470 alpha = np.array(alpha) 471 if len(alpha.shape) == 1: # user passing a flat list e.g. (0.0, 0.3, 0.9, 1) 472 for i, al in enumerate(alpha): 473 xalpha = vmin + (vmax - vmin) * i / (len(alpha) - 1) 474 # Create transfer mapping scalar value to opacity 475 otf.AddPoint(xalpha, al) 476 # print("alpha at", round(xalpha, 1), "\tset to", al) 477 elif len(alpha.shape) == 2: # user passing [(x0,alpha0), ...] 478 otf.AddPoint(vmin, alpha[0][1]) 479 for xalpha, al in alpha: 480 # Create transfer mapping scalar value to opacity 481 otf.AddPoint(xalpha, al) 482 otf.AddPoint(vmax, alpha[-1][1]) 483 484 else: 485 486 otf.AddPoint(vmin, alpha) # constant alpha 487 otf.AddPoint(vmax, alpha) 488 489 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%.
746class PointsVisual(CommonVisual): 747 """Class to manage the visual aspects of a ``Points`` object.""" 748 749 def __init__(self): 750 # print("init PointsVisual") 751 super().__init__() 752 753 def clone2d(self, scale=None): 754 """ 755 Copy a 3D Mesh into a flat 2D image. 756 Returns a `Actor2D`. 757 758 Examples: 759 - [clone2d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/clone2d.py) 760 761  762 """ 763 if scale is None: 764 # work out a reasonable scale 765 msiz = self.diagonal_size() 766 if vedo.plotter_instance and vedo.plotter_instance.window: 767 sz = vedo.plotter_instance.window.GetSize() 768 dsiz = utils.mag(sz) 769 scale = dsiz / msiz / 10 770 else: 771 scale = 350 / msiz 772 773 cmsh = self.clone() 774 poly = cmsh.pos([0, 0, 0]).scale(scale).dataset 775 776 cm = self.mapper.GetColorMode() 777 lut = self.mapper.GetLookupTable() 778 sv = self.mapper.GetScalarVisibility() 779 use_lut = self.mapper.GetUseLookupTableScalarRange() 780 vrange = self.mapper.GetScalarRange() 781 sm = self.mapper.GetScalarMode() 782 783 mapper2d = vtk.new("PolyDataMapper2D") 784 mapper2d.ShallowCopy(self.mapper) 785 mapper2d.SetInputData(poly) 786 mapper2d.SetColorMode(cm) 787 mapper2d.SetLookupTable(lut) 788 mapper2d.SetScalarVisibility(sv) 789 mapper2d.SetUseLookupTableScalarRange(use_lut) 790 mapper2d.SetScalarRange(vrange) 791 mapper2d.SetScalarMode(sm) 792 793 act2d = Actor2D() 794 act2d.properties = act2d.GetProperty() 795 act2d.mapper = mapper2d 796 act2d.dataset = poly 797 798 act2d.SetMapper(mapper2d) 799 csys = act2d.GetPositionCoordinate() 800 csys.SetCoordinateSystem(4) 801 act2d.properties.SetColor(cmsh.color()) 802 act2d.properties.SetOpacity(cmsh.alpha()) 803 return act2d 804 805 ################################################## 806 def copy_properties_from(self, source, deep=True, actor_related=True): 807 """ 808 Copy properties from another ``Points`` object. 809 """ 810 pr = vtk.vtkProperty() 811 try: 812 sp = source.properties 813 mp = source.mapper 814 sa = source.actor 815 except AttributeError: 816 sp = source.GetProperty() 817 mp = source.GetMapper() 818 sa = source 819 820 if deep: 821 pr.DeepCopy(sp) 822 else: 823 pr.ShallowCopy(sp) 824 self.actor.SetProperty(pr) 825 self.properties = pr 826 827 if self.actor.GetBackfaceProperty(): 828 bfpr = vtk.vtkProperty() 829 bfpr.DeepCopy(sa.GetBackfaceProperty()) 830 self.actor.SetBackfaceProperty(bfpr) 831 self.properties_backface = bfpr 832 833 if not actor_related: 834 return self 835 836 # mapper related: 837 self.mapper.SetScalarVisibility(mp.GetScalarVisibility()) 838 self.mapper.SetScalarMode(mp.GetScalarMode()) 839 self.mapper.SetScalarRange(mp.GetScalarRange()) 840 self.mapper.SetLookupTable(mp.GetLookupTable()) 841 self.mapper.SetColorMode(mp.GetColorMode()) 842 self.mapper.SetInterpolateScalarsBeforeMapping( 843 mp.GetInterpolateScalarsBeforeMapping() 844 ) 845 self.mapper.SetUseLookupTableScalarRange( 846 mp.GetUseLookupTableScalarRange() 847 ) 848 849 self.actor.SetPickable(sa.GetPickable()) 850 self.actor.SetDragable(sa.GetDragable()) 851 self.actor.SetTexture(sa.GetTexture()) 852 self.actor.SetVisibility(sa.GetVisibility()) 853 return self 854 855 def color(self, c=False, alpha=None): 856 """ 857 Set/get mesh's color. 858 If None is passed as input, will use colors from active scalars. 859 Same as `mesh.c()`. 860 """ 861 if c is False: 862 return np.array(self.properties.GetColor()) 863 if c is None: 864 self.mapper.ScalarVisibilityOn() 865 return self 866 self.mapper.ScalarVisibilityOff() 867 cc = colors.get_color(c) 868 self.properties.SetColor(cc) 869 if self.trail: 870 self.trail.properties.SetColor(cc) 871 if alpha is not None: 872 self.alpha(alpha) 873 return self 874 875 def c(self, color=False, alpha=None): 876 """ 877 Shortcut for `color()`. 878 If None is passed as input, will use colors from current active scalars. 879 """ 880 return self.color(color, alpha) 881 882 def alpha(self, opacity=None): 883 """Set/get mesh's transparency. Same as `mesh.opacity()`.""" 884 if opacity is None: 885 return self.properties.GetOpacity() 886 887 self.properties.SetOpacity(opacity) 888 bfp = self.actor.GetBackfaceProperty() 889 if bfp: 890 if opacity < 1: 891 self.properties_backface = bfp 892 self.actor.SetBackfaceProperty(None) 893 else: 894 self.actor.SetBackfaceProperty(self.properties_backface) 895 return self 896 897 def lut_color_at(self, value): 898 """ 899 Return the color of the lookup table at value. 900 """ 901 lut = self.mapper.GetLookupTable() 902 if not lut: 903 return None 904 rgb = [0,0,0] 905 lut.GetColor(value, rgb) 906 alpha = lut.GetOpacity(value) 907 return np.array(rgb + [alpha]) 908 909 def opacity(self, alpha=None): 910 """Set/get mesh's transparency. Same as `mesh.alpha()`.""" 911 return self.alpha(alpha) 912 913 def force_opaque(self, value=True): 914 """ Force the Mesh, Line or point cloud to be treated as opaque""" 915 ## force the opaque pass, fixes picking in vtk9 916 # but causes other bad troubles with lines.. 917 self.actor.SetForceOpaque(value) 918 return self 919 920 def force_translucent(self, value=True): 921 """ Force the Mesh, Line or point cloud to be treated as translucent""" 922 self.actor.SetForceTranslucent(value) 923 return self 924 925 def point_size(self, value=None): 926 """Set/get mesh's point size of vertices. Same as `mesh.ps()`""" 927 if value is None: 928 return self.properties.GetPointSize() 929 # self.properties.SetRepresentationToSurface() 930 else: 931 self.properties.SetRepresentationToPoints() 932 self.properties.SetPointSize(value) 933 return self 934 935 def ps(self, pointsize=None): 936 """Set/get mesh's point size of vertices. Same as `mesh.point_size()`""" 937 return self.point_size(pointsize) 938 939 def render_points_as_spheres(self, value=True): 940 """Make points look spheric or else make them look as squares.""" 941 self.properties.SetRenderPointsAsSpheres(value) 942 return self 943 944 def lighting( 945 self, 946 style="", 947 ambient=None, 948 diffuse=None, 949 specular=None, 950 specular_power=None, 951 specular_color=None, 952 metallicity=None, 953 roughness=None, 954 ): 955 """ 956 Set the ambient, diffuse, specular and specular_power lighting constants. 957 958 Arguments: 959 style : (str) 960 preset style, options are `[metallic, plastic, shiny, glossy, ambient, off]` 961 ambient : (float) 962 ambient fraction of emission [0-1] 963 diffuse : (float) 964 emission of diffused light in fraction [0-1] 965 specular : (float) 966 fraction of reflected light [0-1] 967 specular_power : (float) 968 precision of reflection [1-100] 969 specular_color : (color) 970 color that is being reflected by the surface 971 972 <img src="https://upload.wikimedia.org/wikipedia/commons/6/6b/Phong_components_version_4.png" alt="", width=700px> 973 974 Examples: 975 - [specular.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/specular.py) 976 """ 977 pr = self.properties 978 979 if style: 980 981 if style != "off": 982 pr.LightingOn() 983 984 if style == "off": 985 pr.SetInterpolationToFlat() 986 pr.LightingOff() 987 return self ############## 988 989 if hasattr(pr, "GetColor"): # could be Volume 990 c = pr.GetColor() 991 else: 992 c = (1, 1, 0.99) 993 mpr = self.mapper 994 if hasattr(mpr, 'GetScalarVisibility') and mpr.GetScalarVisibility(): 995 c = (1,1,0.99) 996 if style=='metallic': pars = [0.1, 0.3, 1.0, 10, c] 997 elif style=='plastic' : pars = [0.3, 0.4, 0.3, 5, c] 998 elif style=='shiny' : pars = [0.2, 0.6, 0.8, 50, c] 999 elif style=='glossy' : pars = [0.1, 0.7, 0.9, 90, (1,1,0.99)] 1000 elif style=='ambient' : pars = [0.8, 0.1, 0.0, 1, (1,1,1)] 1001 elif style=='default' : pars = [0.1, 1.0, 0.05, 5, c] 1002 else: 1003 vedo.logger.error("in lighting(): Available styles are") 1004 vedo.logger.error("[default, metallic, plastic, shiny, glossy, ambient, off]") 1005 raise RuntimeError() 1006 pr.SetAmbient(pars[0]) 1007 pr.SetDiffuse(pars[1]) 1008 pr.SetSpecular(pars[2]) 1009 pr.SetSpecularPower(pars[3]) 1010 if hasattr(pr, "GetColor"): 1011 pr.SetSpecularColor(pars[4]) 1012 1013 if ambient is not None: pr.SetAmbient(ambient) 1014 if diffuse is not None: pr.SetDiffuse(diffuse) 1015 if specular is not None: pr.SetSpecular(specular) 1016 if specular_power is not None: pr.SetSpecularPower(specular_power) 1017 if specular_color is not None: pr.SetSpecularColor(colors.get_color(specular_color)) 1018 if utils.vtk_version_at_least(9): 1019 if metallicity is not None: 1020 pr.SetInterpolationToPBR() 1021 pr.SetMetallic(metallicity) 1022 if roughness is not None: 1023 pr.SetInterpolationToPBR() 1024 pr.SetRoughness(roughness) 1025 1026 return self 1027 1028 def point_blurring(self, r=1, emissive=False): 1029 """Set point blurring. 1030 Apply a gaussian convolution filter to the points. 1031 In this case the radius `r` is in absolute units of the mesh coordinates. 1032 With emissive set, the halo of point becomes light-emissive. 1033 """ 1034 self.properties.SetRepresentationToPoints() 1035 if emissive: 1036 self.mapper.SetEmissive(bool(emissive)) 1037 self.mapper.SetScaleFactor(r * 1.4142) 1038 1039 # https://kitware.github.io/vtk-examples/site/Python/Meshes/PointInterpolator/ 1040 if alpha < 1: 1041 self.mapper.SetSplatShaderCode( 1042 "//VTK::Color::Impl\n" 1043 "float dist = dot(offsetVCVSOutput.xy,offsetVCVSOutput.xy);\n" 1044 "if (dist > 1.0) {\n" 1045 " discard;\n" 1046 "} else {\n" 1047 f" float scale = ({alpha} - dist);\n" 1048 " ambientColor *= scale;\n" 1049 " diffuseColor *= scale;\n" 1050 "}\n" 1051 ) 1052 alpha = 1 1053 1054 self.mapper.Modified() 1055 self.actor.Modified() 1056 self.properties.SetOpacity(alpha) 1057 self.actor.SetMapper(self.mapper) 1058 return self 1059 1060 @property 1061 def cellcolors(self): 1062 """ 1063 Colorize each cell (face) of a mesh by passing 1064 a 1-to-1 list of colors in format [R,G,B] or [R,G,B,A]. 1065 Colors levels and opacities must be in the range [0,255]. 1066 1067 A single constant color can also be passed as string or RGBA. 1068 1069 A cell array named "CellsRGBA" is automatically created. 1070 1071 Examples: 1072 - [color_mesh_cells1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/color_mesh_cells1.py) 1073 - [color_mesh_cells2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/color_mesh_cells2.py) 1074 1075  1076 """ 1077 if "CellsRGBA" not in self.celldata.keys(): 1078 lut = self.mapper.GetLookupTable() 1079 vscalars = self.dataset.GetCellData().GetScalars() 1080 if vscalars is None or lut is None: 1081 arr = np.zeros([self.ncells, 4], dtype=np.uint8) 1082 col = np.array(self.properties.GetColor()) 1083 col = np.round(col * 255).astype(np.uint8) 1084 alf = self.properties.GetOpacity() 1085 alf = np.round(alf * 255).astype(np.uint8) 1086 arr[:, (0, 1, 2)] = col 1087 arr[:, 3] = alf 1088 else: 1089 cols = lut.MapScalars(vscalars, 0, 0) 1090 arr = utils.vtk2numpy(cols) 1091 self.celldata["CellsRGBA"] = arr 1092 self.celldata.select("CellsRGBA") 1093 return self.celldata["CellsRGBA"] 1094 1095 @cellcolors.setter 1096 def cellcolors(self, value): 1097 if isinstance(value, str): 1098 c = colors.get_color(value) 1099 value = np.array([*c, 1]) * 255 1100 value = np.round(value) 1101 1102 value = np.asarray(value) 1103 n = self.ncells 1104 1105 if value.ndim == 1: 1106 value = np.repeat([value], n, axis=0) 1107 1108 if value.shape[1] == 3: 1109 z = np.zeros((n, 1), dtype=np.uint8) 1110 value = np.append(value, z + 255, axis=1) 1111 1112 assert n == value.shape[0] 1113 1114 self.celldata["CellsRGBA"] = value.astype(np.uint8) 1115 # self.mapper.SetColorModeToDirectScalars() # done in select() 1116 self.celldata.select("CellsRGBA") 1117 1118 @property 1119 def pointcolors(self): 1120 """ 1121 Colorize each point (or vertex of a mesh) by passing 1122 a 1-to-1 list of colors in format [R,G,B] or [R,G,B,A]. 1123 Colors levels and opacities must be in the range [0,255]. 1124 1125 A single constant color can also be passed as string or RGBA. 1126 1127 A point array named "PointsRGBA" is automatically created. 1128 """ 1129 if "PointsRGBA" not in self.pointdata.keys(): 1130 lut = self.mapper.GetLookupTable() 1131 vscalars = self.dataset.GetPointData().GetScalars() 1132 if vscalars is None or lut is None: 1133 # create a constant array 1134 arr = np.zeros([self.npoints, 4], dtype=np.uint8) 1135 col = np.array(self.properties.GetColor()) 1136 col = np.round(col * 255).astype(np.uint8) 1137 alf = self.properties.GetOpacity() 1138 alf = np.round(alf * 255).astype(np.uint8) 1139 arr[:, (0, 1, 2)] = col 1140 arr[:, 3] = alf 1141 else: 1142 cols = lut.MapScalars(vscalars, 0, 0) 1143 arr = utils.vtk2numpy(cols) 1144 self.pointdata["PointsRGBA"] = arr 1145 self.pointdata.select("PointsRGBA") 1146 return self.pointdata["PointsRGBA"] 1147 1148 @pointcolors.setter 1149 def pointcolors(self, value): 1150 if isinstance(value, str): 1151 c = colors.get_color(value) 1152 value = np.array([*c, 1]) * 255 1153 value = np.round(value) 1154 1155 value = np.asarray(value) 1156 n = self.npoints 1157 1158 if value.ndim == 1: 1159 value = np.repeat([value], n, axis=0) 1160 1161 if value.shape[1] == 3: 1162 z = np.zeros((n, 1), dtype=np.uint8) 1163 value = np.append(value, z + 255, axis=1) 1164 1165 assert n == value.shape[0] 1166 1167 self.pointdata["PointsRGBA"] = value.astype(np.uint8) 1168 # self.mapper.SetColorModeToDirectScalars() # done in select() 1169 self.pointdata.select("PointsRGBA") 1170 1171 ##################################################################################### 1172 def cmap( 1173 self, 1174 input_cmap, 1175 input_array=None, 1176 on="", 1177 name="Scalars", 1178 vmin=None, 1179 vmax=None, 1180 n_colors=256, 1181 alpha=1.0, 1182 logscale=False, 1183 ): 1184 """ 1185 Set individual point/cell colors by providing a list of scalar values and a color map. 1186 1187 Arguments: 1188 input_cmap : (str, list, vtkLookupTable, matplotlib.colors.LinearSegmentedColormap) 1189 color map scheme to transform a real number into a color. 1190 input_array : (str, list, vtkArray) 1191 can be the string name of an existing array, a new array or a `vtkArray`. 1192 on : (str) 1193 either 'points' or 'cells' or blank (automatic). 1194 Apply the color map to data which is defined on either points or cells. 1195 name : (str) 1196 give a name to the provided array (if input_array is an array) 1197 vmin : (float) 1198 clip scalars to this minimum value 1199 vmax : (float) 1200 clip scalars to this maximum value 1201 n_colors : (int) 1202 number of distinct colors to be used in colormap table. 1203 alpha : (float, list) 1204 Mesh transparency. Can be a `list` of values one for each vertex. 1205 logscale : (bool) 1206 Use logscale 1207 1208 Examples: 1209 - [mesh_coloring.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_coloring.py) 1210 - [mesh_alphas.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_alphas.py) 1211 - [mesh_custom.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_custom.py) 1212 - (and many others) 1213 1214  1215 """ 1216 self._cmap_name = input_cmap 1217 1218 if on == "": 1219 try: 1220 on = self.mapper.GetScalarModeAsString().replace("Use", "") 1221 if on not in ["PointData", "CellData"]: # can be "Default" 1222 on = "points" 1223 self.mapper.SetScalarModeToUsePointData() 1224 except AttributeError: 1225 on = "points" 1226 elif on == "Default": 1227 on = "points" 1228 self.mapper.SetScalarModeToUsePointData() 1229 1230 if input_array is None: 1231 if not self.pointdata.keys() and self.celldata.keys(): 1232 on = "cells" 1233 if not self.dataset.GetCellData().GetScalars(): 1234 input_array = 0 # pick the first at hand 1235 1236 if "point" in on.lower(): 1237 data = self.dataset.GetPointData() 1238 n = self.dataset.GetNumberOfPoints() 1239 elif "cell" in on.lower(): 1240 data = self.dataset.GetCellData() 1241 n = self.dataset.GetNumberOfCells() 1242 else: 1243 vedo.logger.error( 1244 f"Must specify in cmap(on=...) to either 'cells' or 'points', not {on}") 1245 raise RuntimeError() 1246 1247 if input_array is None: # if None try to fetch the active scalars 1248 arr = data.GetScalars() 1249 if not arr: 1250 vedo.logger.error(f"in cmap(), cannot find any {on} active array ...skip coloring.") 1251 return self 1252 1253 if not arr.GetName(): # sometimes arrays dont have a name.. 1254 arr.SetName(name) 1255 1256 elif isinstance(input_array, str): # if a string is passed 1257 arr = data.GetArray(input_array) 1258 if not arr: 1259 vedo.logger.error(f"in cmap(), cannot find {on} array {input_array} ...skip coloring.") 1260 return self 1261 1262 elif isinstance(input_array, int): # if an int is passed 1263 if input_array < data.GetNumberOfArrays(): 1264 arr = data.GetArray(input_array) 1265 else: 1266 vedo.logger.error(f"in cmap(), cannot find {on} array at {input_array} ...skip coloring.") 1267 return self 1268 1269 elif utils.is_sequence(input_array): # if a numpy array is passed 1270 npts = len(input_array) 1271 if npts != n: 1272 vedo.logger.error(f"in cmap(), nr. of input {on} scalars {npts} != {n} ...skip coloring.") 1273 return self 1274 arr = utils.numpy2vtk(input_array, name=name, dtype=float) 1275 data.AddArray(arr) 1276 data.Modified() 1277 1278 elif isinstance(input_array, vtk.vtkArray): # if a vtkArray is passed 1279 arr = input_array 1280 data.AddArray(arr) 1281 data.Modified() 1282 1283 else: 1284 vedo.logger.error(f"in cmap(), cannot understand input type {type(input_array)}") 1285 raise RuntimeError() 1286 1287 # Now we have array "arr" 1288 array_name = arr.GetName() 1289 1290 if arr.GetNumberOfComponents() == 1: 1291 if vmin is None: 1292 vmin = arr.GetRange()[0] 1293 if vmax is None: 1294 vmax = arr.GetRange()[1] 1295 else: 1296 if vmin is None or vmax is None: 1297 vn = utils.mag(utils.vtk2numpy(arr)) 1298 if vmin is None: 1299 vmin = vn.min() 1300 if vmax is None: 1301 vmax = vn.max() 1302 1303 # interpolate alphas if they are not constant 1304 if not utils.is_sequence(alpha): 1305 alpha = [alpha] * n_colors 1306 else: 1307 v = np.linspace(0, 1, n_colors, endpoint=True) 1308 xp = np.linspace(0, 1, len(alpha), endpoint=True) 1309 alpha = np.interp(v, xp, alpha) 1310 1311 ########################### build the look-up table 1312 if isinstance(input_cmap, vtk.vtkLookupTable): # vtkLookupTable 1313 lut = input_cmap 1314 1315 elif utils.is_sequence(input_cmap): # manual sequence of colors 1316 lut = vtk.vtkLookupTable() 1317 if logscale: 1318 lut.SetScaleToLog10() 1319 lut.SetRange(vmin, vmax) 1320 ncols = len(input_cmap) 1321 lut.SetNumberOfTableValues(ncols) 1322 1323 for i, c in enumerate(input_cmap): 1324 r, g, b = colors.get_color(c) 1325 lut.SetTableValue(i, r, g, b, alpha[i]) 1326 lut.Build() 1327 1328 else: 1329 # assume string cmap name OR matplotlib.colors.LinearSegmentedColormap 1330 lut = vtk.vtkLookupTable() 1331 if logscale: 1332 lut.SetScaleToLog10() 1333 lut.SetVectorModeToMagnitude() 1334 lut.SetRange(vmin, vmax) 1335 lut.SetNumberOfTableValues(n_colors) 1336 mycols = colors.color_map(range(n_colors), input_cmap, 0, n_colors) 1337 for i, c in enumerate(mycols): 1338 r, g, b = c 1339 lut.SetTableValue(i, r, g, b, alpha[i]) 1340 lut.Build() 1341 1342 # TEST NEW WAY 1343 self.mapper.