vedo.plotter
This module defines the main class Plotter to manage actors and 3D rendering.
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3import os.path 4import sys 5import time 6import numpy as np 7from deprecated import deprecated 8 9try: 10 import vedo.vtkclasses as vtk 11except ImportError: 12 import vtkmodules.all as vtk 13 14import vedo 15from vedo import settings 16from vedo import utils 17from vedo import backends 18from vedo import addons 19 20 21__docformat__ = "google" 22 23__doc__ = """ 24This module defines the main class Plotter to manage actors and 3D rendering. 25 26 27""" 28 29__all__ = [ 30 "Plotter", 31 "show", 32 "close", 33] 34 35######################################################################################## 36class Event: 37 """ 38 This class holds the info from an event in the window, works as dictionary too 39 """ 40 __slots__ = [ 41 "name", 42 "title", 43 "id", 44 "time", 45 "priority", 46 "at", 47 "actor", 48 "picked3d", 49 "keyPressed", # obsolete, will disappear. Use "keypress" 50 "keypress", 51 "picked2d", 52 "delta2d", 53 "angle2d", 54 "speed2d", 55 "delta3d", 56 "speed3d", 57 "isPoints", 58 "isMesh", 59 "isAssembly", 60 "isVolume", 61 "isPicture", 62 "isActor2D", 63 ] 64 65 def __init__(self): 66 return 67 68 def __getitem__(self, key): 69 """Make the class work like a dictionary too""" 70 return getattr(self, key) 71 72 def __setitem__(self, key, value): 73 """Make the class work like a dictionary too""" 74 setattr(self, key, value) 75 76 def __repr__(self): 77 f = "---------- <vedo.plotter.Event object> ----------\n" 78 for n in self.__slots__: 79 if n == "actor" and self.actor and self.actor.name: 80 f += f"event.{n} = {self.actor.name} ({self.actor.npoints} points)\n" 81 else: 82 f += f"event.{n} = " + str(self[n]).replace('\n','')[:60] + "\n" 83 return f 84 85 def keys(self): 86 return self.__slots__ 87 88 89############################################################################################## 90def show( 91 *actors, 92 at=None, 93 shape=(1, 1), 94 N=None, 95 pos=(0, 0), 96 size="auto", 97 screensize="auto", 98 title="vedo", 99 bg="white", 100 bg2=None, 101 axes=None, 102 interactive=None, 103 offscreen=False, 104 sharecam=True, 105 resetcam=True, 106 zoom=None, 107 viewup="", 108 azimuth=0, 109 elevation=0, 110 roll=0, 111 camera=None, 112 mode=0, 113 q=False, 114 new=False, 115 backend="", # DEPRECATED 116 ): 117 """ 118 Create on the fly an instance of class Plotter and show the object(s) provided. 119 120 Allowed input objects types are: 121 ``str, Mesh, Volume, Picture, Assembly 122 vtkPolyData, vtkActor, vtkActor2D, vtkImageActor, 123 vtkAssembly or vtkVolume`` 124 125 Arguments: 126 at : (int) 127 number of the renderer to plot to, in case of more than one exists 128 shape : (list, str) 129 Number of sub-render windows inside of the main window. E.g.: 130 specify two across with shape=(2,1) and a two by two grid 131 with shape=(2, 2). By default there is only one renderer. 132 133 Can also accept a shape as string descriptor. E.g.: 134 - shape="3|1" means 3 plots on the left and 1 on the right, 135 - shape="4/2" means 4 plots on top of 2 at bottom. 136 137 axes : (int) 138 set the type of axes to be shown: 139 - 0, no axes 140 - 1, draw three gray grid walls 141 - 2, show cartesian axes from (0,0,0) 142 - 3, show positive range of cartesian axes from (0,0,0) 143 - 4, show a triad at bottom left 144 - 5, show a cube at bottom left 145 - 6, mark the corners of the bounding box 146 - 7, draw a 3D ruler at each side of the cartesian axes 147 - 8, show the `vtkCubeAxesActor` object 148 - 9, show the bounding box outLine 149 - 10, show three circles representing the maximum bounding box 150 - 11, show a large grid on the x-y plane 151 - 12, show polar axes 152 - 13, draw a simple ruler at the bottom of the window 153 154 Axis type-1 can be fully customized by passing a dictionary. 155 Check `vedo.addons.Axes()` for the full list of options. 156 azimuth/elevation/roll : (float) 157 move camera accordingly the specified value 158 viewup : (str, list) 159 either `['x', 'y', 'z']` or a vector to set vertical direction 160 resetcam : (bool) 161 re-adjust camera position to fit objects 162 camera : (dict, vtkCamera) 163 camera parameters can further be specified with a dictionary 164 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 165 - **pos** (list), the position of the camera in world coordinates 166 - **focal_point** (list), the focal point of the camera in world coordinates 167 - **viewup** (list), the view up direction for the camera 168 - **distance** (float), set the focal point to the specified distance from the camera position. 169 - **clipping_range** (float), distance of the near and far clipping planes along the direction of projection. 170 - **parallel_scale** (float), 171 scaling used for a parallel projection, i.e. the height of the viewport 172 in world-coordinate distances. The default is 1. Note that the "scale" parameter works as 173 an "inverse scale", larger numbers produce smaller images. 174 This method has no effect in perspective projection mode. 175 - **thickness** (float), 176 set the distance between clipping planes. This method adjusts the far clipping 177 plane to be set a distance 'thickness' beyond the near clipping plane. 178 - **view_angle** (float), 179 the camera view angle, which is the angular height of the camera view 180 measured in degrees. The default angle is 30 degrees. 181 This method has no effect in parallel projection mode. 182 The formula for setting the angle up for perfect perspective viewing is: 183 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 184 (measured by holding a ruler up to your screen) and d is the distance 185 from your eyes to the screen. 186 interactive : (bool) 187 pause and interact with window (True) or continue execution (False) 188 rate : (float) 189 maximum rate of `show()` in Hertz 190 mode : (int, str) 191 set the type of interaction: 192 - 0 = TrackballCamera [default] 193 - 1 = TrackballActor 194 - 2 = JoystickCamera 195 - 3 = JoystickActor 196 - 4 = Flight 197 - 5 = RubberBand2D 198 - 6 = RubberBand3D 199 - 7 = RubberBandZoom 200 - 8 = Context 201 - 9 = 3D 202 - 10 = Terrain 203 - 11 = Unicam 204 new : (bool) 205 if set to `True`, a call to show will instantiate 206 a new Plotter object (a new window) instead of reusing the first created. 207 """ 208 if len(actors) == 0: 209 actors = None 210 elif len(actors) == 1: 211 actors = actors[0] 212 else: 213 actors = utils.flatten(actors) 214 215 if vedo.plotter_instance and not new: # Plotter exists 216 plt = vedo.plotter_instance 217 218 else: # Plotter must be created 219 220 if utils.is_sequence(at): # user passed a sequence for "at" 221 222 if not utils.is_sequence(actors): 223 vedo.logger.error("in show() input must be a list.") 224 raise RuntimeError() 225 if len(at) != len(actors): 226 vedo.logger.error("in show() lists 'input' and 'at' must have equal lengths") 227 raise RuntimeError() 228 if shape == (1, 1) and N is None: 229 N = max(at) + 1 230 231 elif at is None and (N or shape != (1, 1)): 232 233 if not utils.is_sequence(actors): 234 e = "in show(), N or shape is set, but input is not a sequence\n" 235 e += " you may need to specify e.g. at=0" 236 vedo.logger.error(e) 237 raise RuntimeError() 238 at = list(range(len(actors))) 239 240 plt = Plotter( 241 shape=shape, 242 N=N, 243 pos=pos, 244 size=size, 245 screensize=screensize, 246 title=title, 247 axes=axes, 248 sharecam=sharecam, 249 resetcam=resetcam, 250 interactive=interactive, 251 offscreen=offscreen, 252 bg=bg, 253 bg2=bg2, 254 backend=backend, # DEPRECATED 255 ) 256 257 # use _plt_to_return because plt.show() can return a k3d plot 258 _plt_to_return = None 259 260 if utils.is_sequence(at): 261 262 for i, act in enumerate(actors): 263 _plt_to_return = plt.show( 264 act, 265 at=i, 266 zoom=zoom, 267 resetcam=resetcam, 268 viewup=viewup, 269 azimuth=azimuth, 270 elevation=elevation, 271 roll=roll, 272 camera=camera, 273 interactive=False, 274 mode=mode, 275 bg=bg, 276 bg2=bg2, 277 axes=axes, 278 q=q, 279 ) 280 281 if ( 282 interactive 283 or len(at) == N 284 or (isinstance(shape[0], int) and len(at) == shape[0] * shape[1]) 285 ): 286 # note that shape can be a string 287 if plt.interactor and not offscreen and (interactive is None or interactive): 288 plt.interactor.Start() 289 if vedo.vtk_version == (9,2,2): plt.interactor.GetRenderWindow().SetDisplayId("_0_p_void") ##HACK 290 291 else: 292 293 _plt_to_return = plt.show( 294 actors, 295 at=at, 296 zoom=zoom, 297 resetcam=resetcam, 298 viewup=viewup, 299 azimuth=azimuth, 300 elevation=elevation, 301 roll=roll, 302 camera=camera, 303 interactive=interactive, 304 mode=mode, 305 bg=bg, 306 bg2=bg2, 307 axes=axes, 308 q=q, 309 ) 310 311 return _plt_to_return 312 313 314def close(): 315 """Close the last created Plotter instance if it exists.""" 316 if not vedo.plotter_instance: 317 return 318 vedo.plotter_instance.close() 319 return 320 321 322######################################################################## 323class Plotter: 324 """Main class to manage actors.""" 325 def __init__( 326 self, 327 shape=(1, 1), 328 N=None, 329 pos=(0, 0), 330 size="auto", 331 screensize="auto", 332 title="vedo", 333 bg="white", 334 bg2=None, 335 axes=None, 336 sharecam=True, 337 resetcam=True, 338 interactive=None, 339 offscreen=False, 340 qt_widget=None, 341 wx_widget=None, 342 backend="", # DEPRECATED 343 ): 344 """ 345 Arguments: 346 shape : (str, list) 347 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 348 N : (int) 349 number of desired renderers arranged in a grid automatically. 350 pos : (list) 351 (x,y) position in pixels of top-left corner of the rendering window on the screen 352 size : (str, list) 353 size of the rendering window. If 'auto', guess it based on screensize. 354 screensize : (list) 355 physical size of the monitor screen in pixels 356 bg : (color, str) 357 background color or specify jpg image file name with path 358 bg2 : (color) 359 background color of a gradient towards the top 360 axes : (int) 361 362 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 363 Check out `vedo.addons.Axes()` for the available options. 364 - 0, no axes 365 - 1, draw three gray grid walls 366 - 2, show cartesian axes from (0,0,0) 367 - 3, show positive range of cartesian axes from (0,0,0) 368 - 4, show a triad at bottom left 369 - 5, show a cube at bottom left 370 - 6, mark the corners of the bounding box 371 - 7, draw a 3D ruler at each side of the cartesian axes 372 - 8, show the VTK CubeAxesActor object 373 - 9, show the bounding box outLine, 374 - 10, show three circles representing the maximum bounding box, 375 - 11, show a large grid on the x-y plane (use with zoom=8) 376 - 12, show polar axes. 377 - 13, draw a simple ruler at the bottom of the window 378 379 sharecam : (bool) 380 if False each renderer will have an independent vtkCamera 381 interactive : (bool) 382 if True will stop after show() to allow interaction w/ window 383 offscreen : (bool) 384 if True will not show the rendering window 385 qt_widget : (QVTKRenderWindowInteractor) 386 render in a Qt-Widget using an QVTKRenderWindowInteractor. 387 Overrides offscreen to True. 388 Overrides interactive to False. 389 See examples `qt_windows1.py` and `qt_windows2.py` 390 """ 391 392 if backend != "": 393 vedo.logger.error(f"obsolete 'backend' keyword, use settings.default_backend = '{backend}'") 394 settings.default_backend = backend 395 396 vedo.plotter_instance = self 397 398 if qt_widget is not None: 399 # overrides the interactive and offscreen properties 400 interactive = False 401 offscreen = True 402 403 if wx_widget is not None: 404 # overrides the interactive property 405 interactive = False 406 407 if interactive is None: 408 if N == 1: 409 interactive = True 410 elif N or shape != (1, 1): 411 interactive = False 412 else: 413 interactive = True 414 415 self.actors = [] # list of actors to be shown 416 self.clicked_actor = None # holds the actor that has been clicked 417 self.renderer = None # current renderer 418 self.renderers = [] # list of renderers 419 self.shape = shape # don't remove this line 420 self._interactive = interactive # allows to interact with renderer 421 self.axes = axes # show axes type nr. 422 self.title = title # window title 423 self.sharecam = sharecam # share the same camera if multiple renderers 424 self.picker = None # the vtkPicker object 425 self.picked2d = None # 2d coords of a clicked point on the rendering window 426 self.picked3d = None # 3d coords of a clicked point on an actor 427 self.offscreen = offscreen 428 self.resetcam = resetcam 429 self.last_event = None 430 431 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 432 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 433 434 self.skybox = None 435 436 # mostly internal stuff: 437 self.hover_legends = [] 438 self.backgrcol = bg 439 self.pos = pos # used by vedo.io 440 self.justremoved = None 441 self.axes_instances = [] 442 self.clock = 0 443 self.sliders = [] 444 self.buttons = [] 445 self.widgets = [] 446 self.cutter_widget = None 447 self.hint_widget = None 448 self.background_renderer = None 449 self.size = size 450 self.interactor = None 451 self.camera = None 452 453 self._icol = 0 454 self._clockt0 = time.time() 455 self._first_viewup = True 456 self._extralight = None 457 self._cocoa_initialized = False 458 459 ##################################################################### 460 if settings.default_backend != 'vtk': 461 if settings.default_backend == "2d": 462 self.offscreen = True 463 if self.size == "auto": 464 self.size = (800, 600) 465 466 elif settings.default_backend == "k3d": 467 self._interactive = False 468 self.interactor = None 469 self.window = None 470 self.camera = None # let the backend choose 471 if self.size == "auto": 472 self.size = (1000, 1000) 473 ############################################################# 474 return ###################################################### 475 ############################################################# 476 ##################################################################### 477 478 # build the rendering window: 479 self.window = vtk.vtkRenderWindow() 480 481 self.window.GlobalWarningDisplayOff() 482 self.window.SetWindowName(self.title) 483 484 # more settings 485 if settings.use_depth_peeling: 486 self.window.SetAlphaBitPlanes(settings.alpha_bit_planes) 487 self.window.SetMultiSamples(settings.multi_samples) 488 489 self.window.SetPolygonSmoothing(settings.polygon_smoothing) 490 self.window.SetLineSmoothing(settings.line_smoothing) 491 self.window.SetPointSmoothing(settings.point_smoothing) 492 493 # sort out screen size 494 if screensize == "auto": 495 screensize = (2160, 1440) # might go wrong, use a default 1.5 ratio 496 497 ### BUG in GetScreenSize in VTK 9.1.0 498 ### https://discourse.vtk.org/t/vtk9-1-0-problems/7094/3 499 if settings.hack_call_screen_size: # True 500 501 vtkvers = vedo.vtk_version 502 if not self.offscreen and (vtkvers[0]<9 or vtkvers[0]==9 and vtkvers[1]==0): 503 aus = self.window.GetScreenSize() 504 if aus and len(aus) == 2 and aus[0] > 100 and aus[1] > 100: # seems ok 505 if aus[0] / aus[1] > 2: # looks like there are 2 or more screens 506 screensize = (int(aus[0] / 2), aus[1]) 507 else: 508 screensize = aus 509 510 x, y = screensize 511 512 if N: # N = number of renderers. Find out the best 513 514 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 515 vedo.logger.warning("having set N, shape is ignored.") 516 517 nx = int(np.sqrt(int(N * y / x) + 1)) 518 ny = int(np.sqrt(int(N * x / y) + 1)) 519 lm = [ 520 (nx, ny), 521 (nx, ny + 1), 522 (nx - 1, ny), 523 (nx + 1, ny), 524 (nx, ny - 1), 525 (nx - 1, ny + 1), 526 (nx + 1, ny - 1), 527 (nx + 1, ny + 1), 528 (nx - 1, ny - 1), 529 ] 530 ind, minl = 0, 1000 531 for i, m in enumerate(lm): 532 l = m[0] * m[1] 533 if N <= l < minl: 534 ind = i 535 minl = l 536 shape = lm[ind] 537 538 ################################################## 539 if isinstance(shape, str): 540 541 if "|" in shape: 542 if self.size == "auto": 543 self.size = (800, 1200) 544 n = int(shape.split("|")[0]) 545 m = int(shape.split("|")[1]) 546 rangen = reversed(range(n)) 547 rangem = reversed(range(m)) 548 else: 549 if self.size == "auto": 550 self.size = (1200, 800) 551 m = int(shape.split("/")[0]) 552 n = int(shape.split("/")[1]) 553 rangen = range(n) 554 rangem = range(m) 555 556 if n >= m: 557 xsplit = m / (n + m) 558 else: 559 xsplit = 1 - n / (n + m) 560 if settings.window_splitting_position: 561 xsplit = settings.window_splitting_position 562 563 for i in rangen: 564 arenderer = vtk.vtkRenderer() 565 if "|" in shape: 566 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 567 else: 568 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 569 self.renderers.append(arenderer) 570 571 for i in rangem: 572 arenderer = vtk.vtkRenderer() 573 574 if "|" in shape: 575 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 576 else: 577 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 578 self.renderers.append(arenderer) 579 580 for r in self.renderers: 581 r.SetUseHiddenLineRemoval(settings.hidden_line_removal) 582 r.SetLightFollowCamera(settings.light_follows_camera) 583 584 r.SetUseDepthPeeling(settings.use_depth_peeling) 585 # r.SetUseDepthPeelingForVolumes(settings.use_depth_peeling) 586 if settings.use_depth_peeling: 587 r.SetMaximumNumberOfPeels(settings.max_number_of_peels) 588 r.SetOcclusionRatio(settings.occlusion_ratio) 589 r.SetUseFXAA(settings.use_fxaa) 590 r.SetPreserveDepthBuffer(settings.preserve_depth_buffer) 591 592 r.SetBackground(vedo.get_color(self.backgrcol)) 593 594 self.axes_instances.append(None) 595 596 self.shape = (n + m,) 597 598 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 599 # passing a sequence of dicts for renderers specifications 600 601 if self.size == "auto": 602 self.size = (1200, 900) 603 604 for rd in shape: 605 x0, y0 = rd["bottomleft"] 606 x1, y1 = rd["topright"] 607 bg_ = rd.pop("bg", "white") 608 bg2_ = rd.pop("bg2", None) 609 610 arenderer = vtk.vtkRenderer() 611 arenderer.SetUseHiddenLineRemoval(settings.hidden_line_removal) 612 arenderer.SetLightFollowCamera(settings.light_follows_camera) 613 614 arenderer.SetUseDepthPeeling(settings.use_depth_peeling) 615 # arenderer.SetUseDepthPeelingForVolumes(settings.use_depth_peeling) 616 if settings.use_depth_peeling: 617 arenderer.SetMaximumNumberOfPeels(settings.max_number_of_peels) 618 arenderer.SetOcclusionRatio(settings.occlusion_ratio) 619 arenderer.SetUseFXAA(settings.use_fxaa) 620 arenderer.SetPreserveDepthBuffer(settings.preserve_depth_buffer) 621 622 arenderer.SetViewport(x0, y0, x1, y1) 623 arenderer.SetBackground(vedo.get_color(bg_)) 624 if bg2_: 625 arenderer.GradientBackgroundOn() 626 arenderer.SetBackground2(vedo.get_color(bg2_)) 627 628 self.renderers.append(arenderer) 629 self.axes_instances.append(None) 630 631 self.shape = (len(shape),) 632 633 else: 634 635 if isinstance(self.size, str) and self.size == "auto": 636 # figure out a reasonable window size 637 f = 1.5 638 xs = y / f * shape[1] # because y<x 639 ys = y / f * shape[0] 640 if xs > x / f: # shrink 641 xs = x / f 642 ys = xs / shape[1] * shape[0] 643 if ys > y / f: 644 ys = y / f 645 xs = ys / shape[0] * shape[1] 646 self.size = (int(xs), int(ys)) 647 if shape == (1, 1): 648 self.size = (int(y / f), int(y / f)) # because y<x 649 else: 650 self.size = (self.size[0], self.size[1]) 651 652 image_actor = None 653 bgname = str(self.backgrcol).lower() 654 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 655 self.window.SetNumberOfLayers(2) 656 self.background_renderer = vtk.vtkRenderer() 657 self.background_renderer.SetLayer(0) 658 self.background_renderer.InteractiveOff() 659 self.background_renderer.SetBackground(vedo.get_color(bg2)) 660 image_actor = vedo.Picture(self.backgrcol) 661 self.window.AddRenderer(self.background_renderer) 662 self.background_renderer.AddActor(image_actor) 663 664 for i in reversed(range(shape[0])): 665 for j in range(shape[1]): 666 arenderer = vtk.vtkRenderer() 667 arenderer.SetUseHiddenLineRemoval(settings.hidden_line_removal) 668 arenderer.SetLightFollowCamera(settings.light_follows_camera) 669 arenderer.SetTwoSidedLighting(settings.two_sided_lighting) 670 671 arenderer.SetUseDepthPeeling(settings.use_depth_peeling) 672 # arenderer.SetUseDepthPeelingForVolumes(settings.use_depth_peeling) 673 if settings.use_depth_peeling: 674 arenderer.SetMaximumNumberOfPeels(settings.max_number_of_peels) 675 arenderer.SetOcclusionRatio(settings.occlusion_ratio) 676 arenderer.SetUseFXAA(settings.use_fxaa) 677 arenderer.SetPreserveDepthBuffer(settings.preserve_depth_buffer) 678 679 if image_actor: 680 arenderer.SetLayer(1) 681 682 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 683 if bg2: 684 arenderer.GradientBackgroundOn() 685 arenderer.SetBackground2(vedo.get_color(bg2)) 686 687 x0 = i / shape[0] 688 y0 = j / shape[1] 689 x1 = (i + 1) / shape[0] 690 y1 = (j + 1) / shape[1] 691 arenderer.SetViewport(y0, x0, y1, x1) 692 self.renderers.append(arenderer) 693 self.axes_instances.append(None) 694 self.shape = shape 695 696 if self.renderers: 697 self.renderer = self.renderers[0] 698 self.camera = self.renderer.GetActiveCamera() 699 self.camera.SetParallelProjection(settings.use_parallel_projection) 700 701 if self.size[0] == "f": # full screen 702 self.size = "fullscreen" 703 self.window.SetFullScreen(True) 704 self.window.BordersOn() 705 else: 706 self.window.SetSize(int(self.size[0]), int(self.size[1])) 707 708 if self.wx_widget is not None: 709 settings.immediate_rendering = False # override 710 self.window = self.wx_widget.GetRenderWindow() # overwrite 711 self.interactor = self.window.GetInteractor() 712 for r in self.renderers: 713 self.window.AddRenderer(r) 714 self.wx_widget.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera()) 715 self.camera = self.renderer.GetActiveCamera() 716 ######################## 717 return ################# 718 ######################## 719 720 if self.qt_widget is not None: 721 self.interactor = self.qt_widget.GetRenderWindow().GetInteractor() 722 self.window = self.qt_widget.GetRenderWindow() # overwrite 723 ######################## 724 return ################# 725 ######################## 726 727 self.window.SetPosition(pos) 728 729 for r in self.renderers: 730 self.window.AddRenderer(r) 731 732 if self.offscreen: 733 if self.axes in (4, 5): 734 self.axes = 0 # doesn't work with those 735 self.window.SetOffScreenRendering(True) 736 self._interactive = False 737 self.interactor = None 738 ######################## 739 return ################# 740 ######################## 741 742 self.interactor = vtk.vtkRenderWindowInteractor() 743 744 self.interactor.SetRenderWindow(self.window) 745 vsty = vtk.vtkInteractorStyleTrackballCamera() 746 self.interactor.SetInteractorStyle(vsty) 747 748 if settings.enable_default_mouse_callbacks: 749 self.interactor.AddObserver("LeftButtonPressEvent", self._mouseleftclick) 750 751 if settings.enable_default_keyboard_callbacks: 752 self.interactor.AddObserver("KeyPressEvent", self._keypress) 753 754 # self._timer_event_id = None 755 # if settings.allow_interaction: 756 # def win_interact(iren, event): # flushing interactor events 757 # if event == "TimerEvent": 758 # iren.ExitCallback() 759 # self._timer_event_id = self.interactor.AddObserver("TimerEvent", win_interact) 760 761 ##################################################################### ..init ends here. 762 763 764 # def allow_interaction(self): 765 # """Call this method from inside a loop to allow mouse and keyboard interaction.""" 766 # if ( 767 # self.interactor 768 # and self._timer_event_id is not None 769 # and settings.immediate_rendering 770 # ): 771 # self._repeatingtimer_id = self.interactor.CreateRepeatingTimer(1) 772 # self.interactor.Start() 773 # if vedo.vtk_version == (9,2,2): 774 # self.interactor.GetRenderWindow().SetDisplayId("_0_p_void") ##HACK 775 # if self.interactor: 776 # self.interactor.DestroyTimer(self._repeatingtimer_id) 777 # self._repeatingtimer_id = None 778 # return self 779 780 def __iadd__(self, actors): 781 self.add(actors, render=False) 782 return self 783 784 def __isub__(self, actors): 785 self.remove(actors, render=False) 786 return self 787 788 def __enter__(self): 789 # context manager like in "with Plotter() as plt:" 790 return self 791 792 def __exit__(self, *args, **kwargs): 793 # context manager like in "with Plotter() as plt:" 794 self.close() 795 796 def process_events(self): 797 if self.interactor: 798 try: 799 self.interactor.ProcessEvents() 800 except AttributeError: 801 pass 802 return self 803 804 def at(self, nren, yren=None): 805 """ 806 Select the current renderer number as an int. 807 Can also use the [nx, ny] format. 808 """ 809 if yren is not None: 810 nren = (yren) * self.shape[1] + (nren) 811 if nren < 0 or nren > len(self.renderers): 812 vedo.logger.error(f"at({nren, yren}) is malformed!") 813 raise RuntimeError 814 815 self.renderer = self.renderers[nren] 816 self.camera = self.renderer.GetActiveCamera() 817 return self 818 819 def add(self, *actors, at=None, render=True, resetcam=False): 820 """ 821 Append the input objects to the internal list of actors to be shown. 822 This method is typically used in loops or callback functions. 823 824 Arguments: 825 at : (int) 826 add the object at the specified renderer 827 render : (bool) 828 render the scene after adding the objects (True by default) 829 """ 830 if at is not None: 831 ren = self.renderers[at] 832 else: 833 ren = self.renderer 834 835 actors = utils.flatten(actors) 836 actors = self._scan_input(actors) 837 838 for a in actors: 839 if a not in self.actors: 840 self.actors.append(a) 841 if ren: 842 ren.AddActor(a) 843 if hasattr(a, "scalarbar") and a.scalarbar: 844 ren.AddActor(a.scalarbar) 845 if render: 846 if self.interactor: 847 if not self.interactor.GetInitialized(): 848 # vedo.logger.warning("call to add() but Plotter was not initialized with show()") 849 pass 850 else: 851 self.render(resetcam=resetcam) 852 return self 853 854 def remove(self, *actors, at=None, render=False, resetcam=False): 855 """ 856 Remove input object to the internal list of actors to be shown. 857 This method is typically used in loops or callback functions. 858 Objects to be removed can be referenced by their assigned name. 859 860 Arguments: 861 at : (int) 862 remove the object at the specified renderer 863 render : (bool) 864 render the scene after removing the objects (False by default). 865 """ 866 if at is not None: 867 ren = self.renderers[at] 868 else: 869 ren = self.renderer 870 871 actors = utils.flatten(actors) 872 # actors += self.get_meshes(include_non_pickables=True) 873 actors_r = [] 874 for i, a in enumerate(actors): 875 if isinstance(a, str): 876 for b in self.actors: 877 if hasattr(b, "name") and a in b.name: 878 actors_r.append(b) 879 else: 880 actors_r.append(a) 881 882 for a in set(actors_r): 883 if ren: 884 ren.RemoveActor(a) 885 if hasattr(a, "rendered_at"): 886 ir = self.renderers.index(ren) 887 a.rendered_at.discard(ir) 888 if hasattr(a, "scalarbar") and a.scalarbar: 889 ren.RemoveActor(a.scalarbar) 890 if hasattr(a, "_caption") and a._caption: 891 ren.RemoveActor(a._caption) 892 if hasattr(a, "trail") and a.trail: 893 ren.RemoveActor(a.trail) 894 a.trail_points = [] 895 if a in self.actors: 896 i = self.actors.index(a) 897 del self.actors[i] 898 899 if render: 900 if not self.interactor.GetInitialized(): 901 vedo.logger.warning("call to remove(resetcam=True) but Plotter was not initialized with show()") 902 else: 903 self.render(resetcam=resetcam) 904 return self 905 906 def pop(self, at=None): 907 """ 908 Remove the last added object from the rendering window. 909 This method is typically used in loops or callback functions. 910 """ 911 if at is not None and not isinstance(at, int): 912 # wrong usage pitfall 913 vedo.logger.error("argument of pop() must be an integer") 914 raise RuntimeError() 915 916 if self.actors: 917 self.remove(self.actors[-1], at) 918 return self 919 920 def render(self, resetcam=False): 921 """Render the scene. This method is typically used in loops or callback functions.""" 922 if not self.window: 923 return self 924 925 if self.wx_widget: 926 if resetcam: 927 self.renderer.ResetCamera() 928 self.wx_widget.Render() 929 return self 930 931 if self.qt_widget: 932 if resetcam: 933 self.renderer.ResetCamera() 934 self.qt_widget.Render() 935 return self 936 937 if self.interactor: 938 if not self.interactor.GetInitialized(): 939 self.interactor.Initialize() 940 941 self.camera = self.renderer.GetActiveCamera() 942 if resetcam: 943 self.renderer.ResetCamera() 944 945 self.window.Render() 946 return self 947 948 def interactive(self): 949 """ 950 Start window interaction. 951 Analogous to `show(..., interactive=True)`. 952 """ 953 if self.interactor: 954 self.interactor.Start() 955 if vedo.vtk_version == (9,2,2): 956 self.interactor.GetRenderWindow().SetDisplayId("_0_p_void") ##HACK 957 return self 958 959 def use_depth_peeling(self, at=None, value=True): 960 """ 961 Specify whether use depth peeling algorithm at this specific renderer 962 Call this method before the first rendering. 963 """ 964 if at is None: 965 ren = self.renderer 966 else: 967 ren = self.renderers[at] 968 ren.SetUseDepthPeeling(value) 969 return self 970 971 def background(self, c1=None, c2=None, at=None): 972 """Set the color of the background for the current renderer. 973 A different renderer index can be specified by keyword ``at``. 974 975 Arguments: 976 c1 : (list) 977 background main color. 978 c2 : (list) 979 background color for the upper part of the window. 980 at : (int) 981 renderer index. 982 """ 983 if not self.renderers: 984 return self 985 if at is None: 986 r = self.renderer 987 else: 988 r = self.renderers[at] 989 if r: 990 if c1 is not None: 991 r.SetBackground(vedo.get_color(c1)) 992 if c2 is not None: 993 r.GradientBackgroundOn() 994 r.SetBackground2(vedo.get_color(c2)) 995 else: 996 r.GradientBackgroundOff() 997 return self 998 999 #################################################### 1000 @deprecated(reason=vedo.colors.red + "Please use get_meshes()" + vedo.colors.reset) 1001 def getMeshes(self, *a, **b): 1002 """Deprecated, use get_meshes()""" 1003 return self.get_meshes(*a, **b) 1004 1005 def get_meshes(self, at=None, include_non_pickables=False): 1006 """ 1007 Return a list of Meshes from the specified renderer. 1008 1009 Arguments: 1010 at : (int) 1011 specify which renderer to look at. 1012 include_non_pickables : (bool) 1013 include non-pickable objects 1014 """ 1015 if at is None: 1016 renderer = self.renderer 1017 at = self.renderers.index(renderer) 1018 elif isinstance(at, int): 1019 renderer = self.renderers[at] 1020 1021 has_global_axes = False 1022 if isinstance(self.axes_instances[at], vedo.Assembly): 1023 has_global_axes = True 1024 1025 actors = [] 1026 acs = renderer.GetActors() 1027 acs.InitTraversal() 1028 for _ in range(acs.GetNumberOfItems()): 1029 a = acs.GetNextItem() 1030 if isinstance(a, vtk.vtkVolume): 1031 continue 1032 if include_non_pickables or a.GetPickable(): 1033 if a == self.axes_instances[at]: 1034 continue 1035 if has_global_axes and a in self.axes_instances[at].actors: 1036 continue 1037 actors.append(a) 1038 return actors 1039 1040 def get_volumes(self, at=None, include_non_pickables=False): 1041 """ 1042 Return a list of Volumes from the specified renderer. 1043 1044 Arguments: 1045 at : (int) 1046 specify which renderer to look at 1047 include_non_pickables : (bool) 1048 include non-pickable objects 1049 """ 1050 if at is None: 1051 renderer = self.renderer 1052 at = self.renderers.index(renderer) 1053 elif isinstance(at, int): 1054 renderer = self.renderers[at] 1055 1056 vols = [] 1057 acs = renderer.GetVolumes() 1058 acs.InitTraversal() 1059 for _ in range(acs.GetNumberOfItems()): 1060 a = acs.GetNextItem() 1061 if include_non_pickables or a.GetPickable(): 1062 vols.append(a) 1063 return vols 1064 1065 @deprecated(reason=vedo.colors.red + "Please use reset_camera()" + vedo.colors.reset) 1066 def resetCamera(self, tight=None): 1067 "Deprecated, please use reset_camera()" 1068 return self.reset_camera(tight) 1069 1070 def reset_camera(self, tight=None): 1071 """ 1072 Reset the camera position and zooming. 1073 If tight (float) is specified the zooming reserves a padding space in the xy-plane 1074 expressed in percent of the average size. 1075 """ 1076 if tight is None: 1077 self.renderer.ResetCamera() 1078 else: 1079 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1080 1081 cam = self.renderer.GetActiveCamera() 1082 1083 self.renderer.ComputeAspect() 1084 aspect = self.renderer.GetAspect() 1085 angle = np.pi * cam.GetViewAngle() / 180.0 1086 dx, dy = (x1 - x0) * 0.999, (y1 - y0) * 0.999 1087 dist = max(dx / aspect[0], dy) / np.sin(angle / 2) / 2 1088 1089 cam.SetViewUp(0, 1, 0) 1090 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1091 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1092 if cam.GetParallelProjection(): 1093 ps = max(dx / aspect[0], dy) / 2 1094 cam.SetParallelScale(ps * (1 + tight)) 1095 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1096 return self 1097 1098 def reset_viewup(self, smooth=True): 1099 """ 1100 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1101 """ 1102 vbb, sizes, _, _ = addons.compute_visible_bounds() 1103 x0,x1, y0,y1, z0,z1 = vbb 1104 mx, my, mz = (x0+x1)/2, (y0+y1)/2, (z0+z1)/2 1105 d = self.camera.GetDistance() 1106 1107 viewups = np.array([ 1108 (0, 1, 0), ( 0, -1, 0), 1109 (0, 0, 1), ( 0, 0, -1), 1110 (1, 0, 0), (-1, 0, 0), 1111 ]) 1112 positions = np.array([ 1113 (mx, my, mz+d), (mx, my, mz-d), 1114 (mx, my+d, mz), (mx, my-d, mz), 1115 (mx+d, my, mz), (mx-d, my, mz), 1116 ]) 1117 1118 vu = np.array(self.camera.GetViewUp()) 1119 vui = np.argmin(np.linalg.norm(viewups-vu, axis=1)) 1120 1121 poc = np.array(self.camera.GetPosition()) 1122 foc = np.array(self.camera.GetFocalPoint()) 1123 a = poc - foc 1124 b = positions - foc 1125 a = a / np.linalg.norm(a) 1126 b = b.T * (1/np.linalg.norm(b, axis=1)) 1127 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1128 1129 if smooth: 1130 outtimes = np.linspace(0,1, num=11, endpoint=True) 1131 for t in outtimes: 1132 vv = vu * (1-t) + viewups[vui] * t 1133 pp = poc * (1-t) + positions[pui] * t 1134 ff = foc * (1-t) + np.array([mx,my,mz]) * t 1135 self.camera.SetViewUp(vv) 1136 self.camera.SetPosition(pp) 1137 self.camera.SetFocalPoint(ff) 1138 self.render() 1139 1140 # interpolator does not respect parallel view...: 1141 # cam1 = dict( 1142 # pos=poc, 1143 # viewup=vu, 1144 # focal_point=(mx,my,mz), 1145 # clipping_range=self.camera.GetClippingRange() 1146 # ) 1147 # # cam1 = self.camera 1148 # cam2 = dict( 1149 # pos=positions[pui], 1150 # viewup=viewups[vui], 1151 # focal_point=(mx,my,mz), 1152 # clipping_range=self.camera.GetClippingRange() 1153 # ) 1154 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1155 # for c in vcams: 1156 # self.renderer.SetActiveCamera(c) 1157 # self.render() 1158 else: 1159 1160 self.camera.SetViewUp(viewups[vui]) 1161 self.camera.SetPosition(positions[pui]) 1162 self.camera.SetFocalPoint(mx,my,mz) 1163 self.render() 1164 1165 self.renderer.ResetCameraClippingRange() 1166 self.render() 1167 # vbb, _, _, _ = addons.compute_visible_bounds() 1168 # x0,x1, y0,y1, z0,z1 = vbb 1169 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1170 # self.render() 1171 1172 return self 1173 1174 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()): 1175 """ 1176 Takes as input two cameras set camera at an interpolated position: 1177 1178 Cameras can be vtkCamera or dictionaries in format: 1179 1180 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1181 1182 Press `shift-C` key in interactive mode to dump a python snipplet 1183 of parameters for the current camera view. 1184 """ 1185 nc = len(cameras) 1186 if len(times) == 0: 1187 times = np.linspace(0, 1, num=nc, endpoint=True) 1188 1189 assert len(times) == nc 1190 1191 cin = vtk.vtkCameraInterpolator() 1192 1193 # cin.SetInterpolationTypeToLinear() # bugged? 1194 if nc > 2 and smooth: 1195 cin.SetInterpolationTypeToSpline() 1196 1197 for i, cam in enumerate(cameras): 1198 vcam = cam 1199 if isinstance(cam, dict): 1200 vcam = utils.camera_from_dict(cam) 1201 cin.AddCamera(times[i], vcam) 1202 1203 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1204 rng = maxt - mint 1205 1206 if len(output_times) == 0: 1207 cin.InterpolateCamera(t * rng, self.camera) 1208 self.renderer.SetActiveCamera(self.camera) 1209 return [self.camera] 1210 else: 1211 vcams = [] 1212 for t in output_times: 1213 c = vtk.vtkCamera() 1214 cin.InterpolateCamera(t * rng, c) 1215 vcams.append(c) 1216 return vcams 1217 1218 def fly_to(self, point): 1219 """ 1220 Fly camera to the specified point. 1221 1222 Arguments: 1223 point : (list) 1224 point in space to place camera. 1225 1226 Example: 1227 ```python 1228 from vedo import * 1229 cone = Cone() 1230 plt = Plotter(axes=1) 1231 plt.show(cone) 1232 plt.fly_to([1,0,0]) 1233 plt.interactive().close() 1234 ``` 1235 """ 1236 if self.interactor: 1237 self.resetcam = False 1238 self.interactor.FlyTo(self.renderer, point) 1239 self.camera = self.renderer.GetActiveCamera() 1240 return self 1241 1242 def look_at(self, plane="xy"): 1243 """Move the camera so that it looks at the specified cartesian plane""" 1244 cam = self.renderer.GetActiveCamera() 1245 fp = np.array(cam.GetFocalPoint()) 1246 p = np.array(cam.GetPosition()) 1247 dist = np.linalg.norm(fp - p) 1248 plane = plane.lower() 1249 if "x" in plane and "y" in plane: 1250 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1251 cam.SetViewUp(0.0, 1.0, 0.0) 1252 elif "x" in plane and "z" in plane: 1253 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1254 cam.SetViewUp(0.0, 0.0, 1.0) 1255 elif "y" in plane and "z" in plane: 1256 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1257 cam.SetViewUp(0.0, 0.0, 1.0) 1258 else: 1259 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1260 return self 1261 1262 def record(self, filename=".vedo_recorded_events.log"): 1263 """ 1264 Record camera, mouse, keystrokes and all other events. 1265 Recording can be toggled on/off by pressing key "R". 1266 1267 Arguments: 1268 filename : (str) 1269 ascii file to store events. The default is '.vedo_recorded_events.log'. 1270 1271 Returns: 1272 a string descriptor of events. 1273 1274 Examples: 1275 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1276 """ 1277 erec = vtk.vtkInteractorEventRecorder() 1278 erec.SetInteractor(self.interactor) 1279 erec.SetFileName(filename) 1280 erec.SetKeyPressActivationValue("R") 1281 erec.EnabledOn() 1282 erec.Record() 1283 self.interactor.Start() 1284 if vedo.vtk_version == (9,2,2): self.interactor.GetRenderWindow().SetDisplayId("_0_p_void") ##HACK 1285 erec.Stop() 1286 erec.EnabledOff() 1287 with open(filename, "r", encoding='UTF-8') as fl: 1288 events = fl.read() 1289 erec = None 1290 return events 1291 1292 def play(self, events=".vedo_recorded_events.log", repeats=0): 1293 """ 1294 Play camera, mouse, keystrokes and all other events. 1295 1296 Arguments: 1297 events : (str) 1298 file o string of events. The default is '.vedo_recorded_events.log'. 1299 repeats : (int) 1300 number of extra repeats of the same events. The default is 0. 1301 1302 Examples: 1303 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1304 """ 1305 erec = vtk.vtkInteractorEventRecorder() 1306 erec.SetInteractor(self.interactor) 1307 1308 if events.endswith(".log"): 1309 erec.ReadFromInputStringOff() 1310 erec.SetFileName(events) 1311 else: 1312 erec.ReadFromInputStringOn() 1313 erec.SetInputString(events) 1314 1315 erec.Play() 1316 for _i in range(repeats): 1317 erec.Rewind() 1318 erec.Play() 1319 erec.EnabledOff() 1320 erec = None 1321 return self 1322 1323 def parallel_projection(self, value=True, at=None): 1324 """ 1325 Use parallel projection `at` a specified renderer. 1326 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1327 An input value equal to -1 will toggle it on/off. 1328 """ 1329 if at is not None: 1330 r = self.renderers[at] 1331 else: 1332 r = self.renderer 1333 if value == -1: 1334 val = r.GetActiveCamera().GetParallelProjection() 1335 value = not val 1336 r.GetActiveCamera().SetParallelProjection(value) 1337 r.Modified() 1338 return self 1339 1340 ################################################################## 1341 @deprecated(reason=vedo.colors.red + "Please use add_slider()" + vedo.colors.reset) 1342 def addSlider2D(self, *a, **b): 1343 return self.add_slider(*a, **b) 1344 1345 def add_slider( 1346 self, 1347 sliderfunc, 1348 xmin, 1349 xmax, 1350 value=None, 1351 pos=4, 1352 title="", 1353 font="", 1354 title_size=1, 1355 c=None, 1356 alpha=1, 1357 show_value=True, 1358 delayed=False, 1359 **options, 1360 ): 1361 """ 1362 Add a `vedo.addons.Slider2D` which can call an external custom function. 1363 1364 Arguments: 1365 sliderfunc : (function) 1366 external function to be called by the widget 1367 xmin : (float) 1368 lower value of the slider 1369 xmax : (float) 1370 upper value 1371 value : (float) 1372 current value 1373 pos : (list, str) 1374 position corner number: horizontal [1-5] or vertical [11-15] 1375 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1376 and also by a string descriptor (eg. "bottom-left") 1377 title : (str) 1378 title text 1379 font : (str) 1380 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1381 title_size : (float) 1382 title text scale [1.0] 1383 show_value : (bool) 1384 if True current value is shown 1385 delayed : (bool) 1386 if True the callback is delayed until when the mouse button is released 1387 alpha : (float) 1388 opacity of the scalar bar texts 1389 slider_length : (float) 1390 slider length 1391 slider_width : (float) 1392 slider width 1393 end_cap_length : (float) 1394 length of the end cap 1395 end_cap_width : (float) 1396 width of the end cap 1397 tube_width : (float) 1398 width of the tube 1399 title_height : (float) 1400 width of the title 1401 tformat : (str) 1402 format of the title 1403 1404 Examples: 1405 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1406 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1407 1408  1409 """ 1410 if c is None: # automatic black or white 1411 c = (0.8, 0.8, 0.8) 1412 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1413 c = (0.2, 0.2, 0.2) 1414 else: 1415 c = vedo.get_color(c) 1416 1417 slider2d = addons.Slider2D( 1418 sliderfunc, 1419 xmin, 1420 xmax, 1421 value, 1422 pos, 1423 title, 1424 font, 1425 title_size, 1426 c, 1427 alpha, 1428 show_value, 1429 delayed, 1430 **options, 1431 ) 1432 1433 if self.renderer: 1434 slider2d.renderer = self.renderer 1435 if self.interactor: 1436 slider2d.interactor = self.interactor 1437 slider2d.on() 1438 self.sliders.append([slider2d, sliderfunc]) 1439 return slider2d 1440 1441 1442 def add_slider3d( 1443 self, 1444 sliderfunc, 1445 pos1, 1446 pos2, 1447 xmin, 1448 xmax, 1449 value=None, 1450 s=0.03, 1451 t=1, 1452 title="", 1453 rotation=0, 1454 c=None, 1455 show_value=True, 1456 ): 1457 """ 1458 Add a 3D slider widget which can call an external custom function. 1459 1460 Arguments: 1461 sliderfunc : (function) 1462 external function to be called by the widget 1463 pos1 : (list) 1464 first position 3D coordinates 1465 pos2 : (list) 1466 second position coordinates 1467 xmin : (float) 1468 lower value 1469 xmax : (float) 1470 upper value 1471 value : (float) 1472 initial value 1473 s : (float) 1474 label scaling factor 1475 t : (float) 1476 tube scaling factor 1477 title : (str) 1478 title text 1479 c : (color) 1480 slider color 1481 rotation : (float) 1482 title rotation around slider axis 1483 show_value : (bool) 1484 if True current value is shown 1485 1486 Examples: 1487 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1488 1489  1490 """ 1491 if c is None: # automatic black or white 1492 c = (0.8, 0.8, 0.8) 1493 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1494 c = (0.2, 0.2, 0.2) 1495 else: 1496 c = vedo.get_color(c) 1497 1498 slider3d = addons.Slider3D(sliderfunc, pos1, pos2, xmin, xmax, value, s, t, title, rotation, c, show_value) 1499 slider3d.renderer = self.renderer 1500 slider3d.interactor = self.interactor 1501 slider3d.on() 1502 self.sliders.append([slider3d, sliderfunc]) 1503 return slider3d 1504 1505 1506 @deprecated(reason=vedo.colors.red + "Please use add_button()" + vedo.colors.reset) 1507 def addButton(self, *a, **b): 1508 return self.add_button(*a, **b) 1509 1510 def add_button( 1511 self, 1512 fnc, 1513 states=("On", "Off"), 1514 c=("w", "w"), 1515 bc=("dg", "dr"), 1516 pos=(0.7, 0.05), 1517 size=24, 1518 font=None, 1519 bold=False, 1520 italic=False, 1521 alpha=1, 1522 angle=0, 1523 ): 1524 """ 1525 Add a button to the renderer window. 1526 1527 Arguments: 1528 states : (list) 1529 a list of possible states, e.g. ['On', 'Off'] 1530 c : (list) 1531 a list of colors for each state 1532 bc : (list) 1533 a list of background colors for each state 1534 pos : (list) 1535 2D position in pixels from left-bottom corner 1536 size : (float) 1537 size of button font 1538 font : (str) 1539 font type. Check [available fonts here](https://vedo.embl.es/fonts). 1540 bold : (bool) 1541 bold font face (False) 1542 italic : (bool) 1543 italic font face (False) 1544 alpha : (float) 1545 opacity level 1546 angle : (float) 1547 anticlockwise rotation in degrees 1548 1549 Returns: 1550 `vedo.addons.Button` object. 1551 1552 Examples: 1553 - [buttons.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons.py) 1554 1555  1556 """ 1557 bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle) 1558 self.renderer.AddActor2D(bu.actor) 1559 self.buttons.append(bu) 1560 return bu 1561 1562 1563 @deprecated(reason=vedo.colors.red + "Please use add_spline_tool()" + vedo.colors.reset) 1564 def addSplineTool(self, *a, **b): 1565 """Deprecated. Use `add_spline_tool()`""" 1566 return self.add_spline_tool(*a, **b) 1567 1568 def add_spline_tool(self, points, pc='k', ps=8, lc='r4', ac='g5', lw=2, closed=False, interactive=False): 1569 """ 1570 Add a spline tool to the current plotter. 1571 Nodes of the spline can be dragged in space with the mouse. 1572 Clicking on the line itself adds an extra point. 1573 Selecting a point and pressing del removes it. 1574 1575 Arguments: 1576 points : (Mesh, Points, array) 1577 the set of vertices forming the spline nodes. 1578 pc : (str) 1579 point color. The default is 'k'. 1580 ps : (str) 1581 point size. The default is 8. 1582 lc : (str) 1583 line color. The default is 'r4'. 1584 ac : (str) 1585 active point marker color. The default is 'g5'. 1586 lw : (int) 1587 line width. The default is 2. 1588 closed : (bool) 1589 spline is meant to be closed. The default is False. 1590 1591 Returns: 1592 a `SplineTool` object. 1593 1594 Examples: 1595 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1596 1597  1598 """ 1599 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, closed) 1600 if self.interactor: 1601 sw.SetInteractor(self.interactor) 1602 else: 1603 vedo.logger.error("in add_spline_tool(), No interactor found.") 1604 raise RuntimeError 1605 sw.On() 1606 sw.Initialize(sw.points.polydata()) 1607 if sw.closed: 1608 sw.representation.ClosedLoopOn() 1609 sw.representation.SetRenderer(self.renderer) 1610 sw.representation.BuildRepresentation() 1611 sw.Render() 1612 if interactive: 1613 self.interactor.Start() 1614 if vedo.vtk_version == (9,2,2): self.interactor.GetRenderWindow().SetDisplayId("_0_p_void") ##HACK 1615 else: 1616 self.interactor.Render() 1617 return sw 1618 1619 def add_cutter_tool(self, obj=None, mode="box", invert=False): 1620 """Create an interactive tool to cut away parts of a mesh or volume. 1621 1622 Arguments: 1623 mode : (str) 1624 either "box", "plane" or "sphere" 1625 invert : (bool) 1626 invert selection (inside-out) 1627 1628 Examples: 1629 - [cutter.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/cutter.py) 1630 1631  1632 """ 1633 return addons.add_cutter_tool(obj, mode, invert) 1634 1635 def add_icon(self, icon, pos=3, size=0.08): 1636 """Add an inset icon mesh into the same renderer. 1637 1638 Arguments: 1639 pos : (int, list) 1640 icon position in the range [1-4] indicating one of the 4 corners, 1641 or it can be a tuple (x,y) as a fraction of the renderer size. 1642 size : (float) 1643 size of the square inset. 1644 1645 Examples: 1646 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1647 """ 1648 iconw = addons.Icon(icon, pos, size) 1649 1650 iconw.SetInteractor(self.interactor) 1651 iconw.EnabledOn() 1652 iconw.InteractiveOff() 1653 self.widgets.append(iconw) 1654 return iconw 1655 1656 1657 def add_global_axes(self, axtype=None, c=None): 1658 """Draw axes on scene. Available axes types: 1659 1660 Arguments: 1661 axtype : (int) 1662 - 0, no axes, 1663 - 1, draw three gray grid walls 1664 - 2, show cartesian axes from (0,0,0) 1665 - 3, show positive range of cartesian axes from (0,0,0) 1666 - 4, show a triad at bottom left 1667 - 5, show a cube at bottom left 1668 - 6, mark the corners of the bounding box 1669 - 7, draw a 3D ruler at each side of the cartesian axes 1670 - 8, show the vtkCubeAxesActor object 1671 - 9, show the bounding box outLine 1672 - 10, show three circles representing the maximum bounding box 1673 - 11, show a large grid on the x-y plane 1674 - 12, show polar axes 1675 - 13, draw a simple ruler at the bottom of the window 1676 1677 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 1678 1679 Example: 1680 ```python 1681 from vedo import Box, show 1682 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 1683 show( 1684 b, 1685 axes={ 1686 "xtitle": "Some long variable [a.u.]", 1687 "number_of_divisions": 4, 1688 # ... 1689 }, 1690 ) 1691 ``` 1692 1693 Examples: 1694 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 1695 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 1696 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 1697 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 1698 1699 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 1700 """ 1701 addons.add_global_axes(axtype, c) 1702 return self 1703 1704 def add_legend_box(self, **kwargs): 1705 """Add a legend to the top right. 1706 1707 Examples: 1708 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 1709 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 1710 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 1711 """ 1712 acts = self.get_meshes() 1713 lb = addons.LegendBox(acts, **kwargs) 1714 self.add(lb) 1715 return self 1716 1717 def add_hint( 1718 self, obj, text="", c="k", bc="yellow8", 1719 font="Calco", size=18, justify=0, angle=0, delay=100, 1720 ): 1721 """ 1722 Create a pop-up hint style message when hovering an object. 1723 Use add_hint(False) to disable all hints. 1724 1725 Arguments: 1726 obj : (Mesh, Points) 1727 the object to associate the pop-up to 1728 text : (str) 1729 string description of the pop-up 1730 delay : (int) 1731 milliseconds to wait before pop-up occurs 1732 """ 1733 if self.offscreen: 1734 return self 1735 1736 if vedo.vtk_version[0] == 9 and "Linux" in vedo.sys_platform: # Linux vtk9 is bugged 1737 vedo.logger.warning("add_hint() is not available on Linux platforms.") 1738 return self 1739 1740 if obj is False: 1741 self.hint_widget.EnabledOff() 1742 self.hint_widget = None 1743 return self 1744 1745 if text is False and self.hint_widget: 1746 self.hint_widget.RemoveBalloon(obj) 1747 return self 1748 1749 if text == "": 1750 if obj.name: 1751 text = obj.name 1752 elif obj.filename: 1753 text = obj.filename 1754 else: 1755 return self 1756 1757 if not self.hint_widget: 1758 self.hint_widget = vtk.vtkBalloonWidget() 1759 1760 rep = vtk.vtkBalloonRepresentation() 1761 rep.SetBalloonLayoutToImageRight() 1762 1763 trep = rep.GetTextProperty() 1764 trep.SetFontFamily(vtk.VTK_FONT_FILE) 1765 trep.SetFontFile(utils.get_font_path(font)) 1766 trep.SetFontSize(size) 1767 trep.SetColor(vedo.get_color(c)) 1768 trep.SetBackgroundColor(vedo.get_color(bc)) 1769 trep.SetShadow(False) 1770 trep.SetJustification(justify) 1771 trep.UseTightBoundingBoxOn() 1772 1773 self.hint_widget.ManagesCursorOff() 1774 self.hint_widget.SetTimerDuration(delay) 1775 self.hint_widget.SetInteractor(self.interactor) 1776 if angle: 1777 rep.SetOrientation(angle) 1778 rep.SetBackgroundOpacity(0) 1779 self.hint_widget.SetRepresentation(rep) 1780 self.widgets.append(self.hint_widget) 1781 if self.interactor.GetInitialized(): 1782 self.hint_widget.EnabledOn() 1783 else: 1784 vedo.logger.warning("add_hint() must be called after show(). Skip.") 1785 return self 1786 1787 bst = self.hint_widget.GetBalloonString(obj) 1788 if bst: 1789 self.hint_widget.UpdateBalloonString(obj, text) 1790 else: 1791 self.hint_widget.AddBalloon(obj, text) 1792 1793 return self 1794 1795 1796 def add_shadows(self): 1797 """Add shadows at the current renderer.""" 1798 shadows = vtk.vtkShadowMapPass() 1799 seq = vtk.vtkSequencePass() 1800 passes = vtk.vtkRenderPassCollection() 1801 passes.AddItem(shadows.GetShadowMapBakerPass()) 1802 passes.AddItem(shadows) 1803 seq.SetPasses(passes) 1804 camerapass = vtk.vtkCameraPass() 1805 camerapass.SetDelegatePass(seq) 1806 self.renderer.SetPass(camerapass) 1807 return self 1808 1809 def add_ambient_occlusion(self, radius, bias=0.01, blur=True, samples=100): 1810 """ 1811 Screen Space Ambient Occlusion. 1812 1813 For every pixel on the screen, the pixel shader samples the depth values around 1814 the current pixel and tries to compute the amount of occlusion from each of the sampled 1815 points. 1816 1817 Arguments: 1818 radius : (float) 1819 radius of influence in absolute units 1820 bias : (float) 1821 bias of the normals 1822 blur : (bool) 1823 add a blurring to the sampled positions 1824 samples : (int) 1825 number of samples to probe 1826 1827 Examples: 1828 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 1829 1830  1831 """ 1832 lights = vtk.vtkLightsPass() 1833 1834 opaque = vtk.vtkOpaquePass() 1835 1836 ssaoCam = vtk.vtkCameraPass() 1837 ssaoCam.SetDelegatePass(opaque) 1838 1839 ssao = vtk.vtkSSAOPass() 1840 ssao.SetRadius(radius) 1841 ssao.SetBias(bias) 1842 ssao.SetBlur(blur) 1843 ssao.SetKernelSize(samples) 1844 ssao.SetDelegatePass(ssaoCam) 1845 1846 translucent = vtk.vtkTranslucentPass() 1847 1848 volpass = vtk.vtkVolumetricPass() 1849 ddp = vtk.vtkDualDepthPeelingPass() 1850 ddp.SetTranslucentPass(translucent) 1851 ddp.SetVolumetricPass(volpass) 1852 1853 over = vtk.vtkOverlayPass() 1854 1855 collection = vtk.vtkRenderPassCollection() 1856 collection.AddItem(lights) 1857 collection.AddItem(ssao) 1858 collection.AddItem(ddp) 1859 collection.AddItem(over) 1860 1861 sequence = vtk.vtkSequencePass() 1862 sequence.SetPasses(collection) 1863 1864 cam = vtk.vtkCameraPass() 1865 cam.SetDelegatePass(sequence) 1866 1867 self.renderer.SetPass(cam) 1868 return self 1869 1870 def add_depth_of_field(self, autofocus=True): 1871 """Add a depth of field effect in the scene.""" 1872 lights = vtk.vtkLightsPass() 1873 1874 opaque = vtk.vtkOpaquePass() 1875 1876 dofCam = vtk.vtkCameraPass() 1877 dofCam.SetDelegatePass(opaque) 1878 1879 dof = vtk.vtkDepthOfFieldPass() 1880 dof.SetAutomaticFocalDistance(autofocus) 1881 dof.SetDelegatePass(dofCam) 1882 1883 collection = vtk.vtkRenderPassCollection() 1884 collection.AddItem(lights) 1885 collection.AddItem(dof) 1886 1887 sequence = vtk.vtkSequencePass() 1888 sequence.SetPasses(collection) 1889 1890 cam = vtk.vtkCameraPass() 1891 cam.SetDelegatePass(sequence) 1892 1893 self.renderer.SetPass(cam) 1894 return self 1895 1896 def _add_skybox(self, hdrfile): 1897 # many hdr files are at https://polyhaven.com/all 1898 1899 if utils.vtk_version_at_least(9): 1900 reader = vtk.vtkHDRReader() 1901 # Check the image can be read. 1902 if not reader.CanReadFile(hdrfile): 1903 vedo.logger.error(f"Cannot read HDR file {hdrfile}") 1904 return self 1905 reader.SetFileName(hdrfile) 1906 reader.Update() 1907 1908 texture = vtk.vtkTexture() 1909 texture.SetColorModeToDirectScalars() 1910 texture.SetInputData(reader.GetOutput()) 1911 1912 # Convert to a cube map 1913 tcm = vtk.vtkEquirectangularToCubeMapTexture() 1914 tcm.SetInputTexture(texture) 1915 # Enable mipmapping to handle HDR image 1916 tcm.MipmapOn() 1917 tcm.InterpolateOn() 1918 1919 self.renderer.SetEnvironmentTexture(tcm) 1920 self.renderer.UseImageBasedLightingOn() 1921 self.skybox = vtk.vtkSkybox() 1922 self.skybox.SetTexture(tcm) 1923 self.renderer.AddActor(self.skybox) 1924 1925 else: 1926 vedo.logger.error("add_skybox not supported in this VTK version. Skip.") 1927 1928 return self 1929 1930 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None): 1931 """ 1932 Add a frame to the renderer subwindow. 1933 1934 Arguments: 1935 c : (color) 1936 color name or index 1937 alpha : (float) 1938 opacity level 1939 lw : (int) 1940 line width in pixels. 1941 padding : (float) 1942 padding space in pixels. 1943 """ 1944 if c is None: # automatic black or white 1945 c = (0.9, 0.9, 0.9) 1946 if np.sum(vedo.plotter_instance.renderer.GetBackground()) > 1.5: 1947 c = (0.1, 0.1, 0.1) 1948 renf = addons.RendererFrame(c, alpha, lw, padding) 1949 self.renderer.AddActor(renf) 1950 return self 1951 1952 def add_hover_legend( 1953 self, 1954 at=None, 1955 c=None, 1956 pos="bottom-left", 1957 font="Calco", 1958 s=0.75, 1959 bg="auto", 1960 alpha=0.1, 1961 maxlength=24, 1962 use_info=False, 1963 ): 1964 """ 1965 Add a legend with 2D text which is triggered by hovering the mouse on an object. 1966 1967 The created text object are stored in plotter.hover_legends 1968 1969 Arguments: 1970 c : (color) 1971 Text color. If None then black or white is chosen automatically 1972 pos : (str) 1973 text positioning 1974 font : (str) 1975 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 1976 s : (float) 1977 text size scale 1978 bg : (color) 1979 background color of the 2D box containing the text 1980 alpha : (float) 1981 box transparency 1982 maxlength : (int) 1983 maximum number of characters per line 1984 use_info : (bool) 1985 visualize the content of the `obj.info` attribute 1986 1987 Examples: 1988 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 1989 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 1990 1991  1992 """ 1993 hoverlegend = vedo.shapes.Text2D('', pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 1994 1995 if at is None: 1996 at = self.renderers.index(self.renderer) 1997 1998 def _legfunc(evt): 1999 # helper function (png not pickable because of alpha channel in vtk9 ??) 2000 if not evt.actor or not self.renderer or at != evt.at: 2001 if hoverlegend._mapper.GetInput(): # clear and return 2002 hoverlegend._mapper.SetInput("") 2003 self.interactor.Render() 2004 return 2005 2006 if use_info: 2007 if hasattr(evt.actor, "info"): 2008 t = str(evt.actor.info) 2009 else: 2010 return 2011 else: 2012 t, tp = "", "" 2013 if evt.isMesh: 2014 tp = "Mesh " 2015 elif evt.isPoints: 2016 tp = "Points " 2017 # elif evt.isVolume: 2018 # tp = "Volume " 2019 elif evt.isPicture: 2020 tp = "Pict " 2021 elif evt.isAssembly: 2022 tp = "Assembly " 2023 else: 2024 return 2025 2026 if evt.isAssembly: 2027 if not evt.actor.name: 2028 t += f"Assembly object of {len(evt.actor.unpack())} parts\n" 2029 else: 2030 t += f"Assembly name: {evt.actor.name} ({len(evt.actor.unpack())} parts)\n" 2031 else: 2032 if evt.actor.name: 2033 t += f"{tp}name" 2034 if evt.isPoints: t += ' ' 2035 if evt.isMesh: t += ' ' 2036 t += f": {evt.actor.name[:maxlength]}".ljust(maxlength) + "\n" 2037 2038 if evt.actor.filename: 2039 t += f"{tp}filename: " 2040 t += f"{os.path.basename(evt.actor.filename[-maxlength:])}".ljust(maxlength) 2041 t += '\n' 2042 if not evt.actor.file_size: 2043 evt.actor.file_size, evt.actor.created = vedo.io.fileInfo(evt.actor.filename) 2044 if evt.actor.file_size: 2045 t += " : " 2046 sz, created = evt.actor.file_size, evt.actor.created 2047 t += f"{created[4:-5]} ({sz})" + "\n" 2048 2049 if evt.isPoints: 2050 indata = evt.actor.polydata(False) 2051 if indata.GetNumberOfPoints(): 2052 t += f"#points/cells: {indata.GetNumberOfPoints()}"\ 2053 f" / {indata.GetNumberOfCells()}" 2054 pdata = indata.GetPointData() 2055 cdata = indata.GetCellData() 2056 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2057 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2058 if pdata.GetScalars().GetName() == evt.actor.mapper().GetArrayName(): 2059 t += " *" 2060 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2061 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2062 if cdata.GetScalars().GetName() == evt.actor.mapper().GetArrayName(): 2063 t += " *" 2064 2065 if evt.isPicture: 2066 t = f"{os.path.basename(evt.actor.filename[:maxlength+10])}".ljust(maxlength+10) 2067 t += f"\nImage shape: {evt.actor.shape}" 2068 pcol = self.color_picker(evt.picked2d) 2069 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2070 2071 # change box color if needed in 'auto' mode 2072 if evt.isPoints and "auto" in str(bg): 2073 actcol = evt.actor.GetProperty().GetColor() 2074 if hoverlegend._mapper.GetTextProperty().GetBackgroundColor() != actcol: 2075 hoverlegend._mapper.GetTextProperty().SetBackgroundColor(actcol) 2076 2077 # adapt to changes in bg color 2078 bgcol = self.renderers[at].GetBackground() 2079 _bgcol = c 2080 if _bgcol is None: # automatic black or white 2081 _bgcol = (0.9, 0.9, 0.9) 2082 if sum(bgcol) > 1.5: 2083 _bgcol = (0.1, 0.1, 0.1) 2084 if len(set(_bgcol).intersection(bgcol)) < 3: 2085 hoverlegend.color(_bgcol) 2086 2087 if hoverlegend._mapper.GetInput() != t: 2088 hoverlegend._mapper.SetInput(t) 2089 self.interactor.Render() 2090 2091 self.add(hoverlegend, render=False, at=at) 2092 self.hover_legends.append(hoverlegend) 2093 self.add_callback("MouseMove", _legfunc) 2094 return self 2095 2096 ##################################################################### 2097 def add_scale_indicator( 2098 self, 2099 pos=(0.7, 0.05), 2100 s=0.02, 2101 length=2, 2102 lw=4, 2103 c="k1", 2104 alpha=1, 2105 units="", 2106 gap=0.05, 2107 ): 2108 """ 2109 Add a Scale Indicator. Only works in parallel mode (no perspective). 2110 2111 Arguments: 2112 pos : (list) 2113 fractional (x,y) position on the screen. 2114 s : (float) 2115 size of the text. 2116 length : (float) 2117 length of the line. 2118 units : (str) 2119 string to show units. 2120 gap : (float) 2121 separation of line and text. 2122 2123 Example: 2124 ```python 2125 from vedo import settings, Cube, Plotter 2126 settings.use_parallel_projection = True # or else it doesnt make sense! 2127 cube = Cube().alpha(0.2) 2128 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2129 plt.add_scale_indicator(units='um', c='blue4') 2130 plt.show(cube, "Scale indicator with units").close() 2131 ``` 2132  2133 """ 2134 ppoints = vtk.vtkPoints() # Generate the polyline 2135 psqr = [[0.0, gap], [length / 10, gap]] 2136 dd = psqr[1][0] - psqr[0][0] 2137 for i, pt in enumerate(psqr): 2138 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2139 lines = vtk.vtkCellArray() 2140 lines.InsertNextCell(len(psqr)) 2141 for i in range(len(psqr)): 2142 lines.InsertCellPoint(i) 2143 pd = vtk.vtkPolyData() 2144 pd.SetPoints(ppoints) 2145 pd.SetLines(lines) 2146 2147 wsx, wsy = self.window.GetSize() 2148 if not settings.use_parallel_projection: 2149 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2150 return None 2151 2152 rlabel = vtk.vtkVectorText() 2153 rlabel.SetText("scale") 2154 tf = vtk.vtkTransformPolyDataFilter() 2155 tf.SetInputConnection(rlabel.GetOutputPort()) 2156 t = vtk.vtkTransform() 2157 t.Scale(s * wsy / wsx, s, 1) 2158 tf.SetTransform(t) 2159 2160 app = vtk.vtkAppendPolyData() 2161 app.AddInputConnection(tf.GetOutputPort()) 2162 app.AddInputData(pd) 2163 2164 mapper = vtk.vtkPolyDataMapper2D() 2165 mapper.SetInputConnection(app.GetOutputPort()) 2166 cs = vtk.vtkCoordinate() 2167 cs.SetCoordinateSystem(1) 2168 mapper.SetTransformCoordinate(cs) 2169 2170 fractor = vtk.vtkActor2D() 2171 csys = fractor.GetPositionCoordinate() 2172 csys.SetCoordinateSystem(3) 2173 fractor.SetPosition(pos) 2174 fractor.SetMapper(mapper) 2175 fractor.GetProperty().SetColor(vedo.get_color(c)) 2176 fractor.GetProperty().SetOpacity(alpha) 2177 fractor.GetProperty().SetLineWidth(lw) 2178 fractor.GetProperty().SetDisplayLocationToForeground() 2179 2180 def sifunc(iren, ev): 2181 wsx, wsy = self.window.GetSize() 2182 ps = self.camera.GetParallelScale() 2183 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2184 if units: 2185 newtxt += " " + units 2186 if rlabel.GetText() != newtxt: 2187 rlabel.SetText(newtxt) 2188 2189 self.renderer.AddActor(fractor) 2190 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2191 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2192 self.interactor.AddObserver("InteractionEvent", sifunc) 2193 sifunc(0, 0) 2194 return fractor 2195 2196 def fill_event(self, ename="", pos=(), cid=0, priority=0): 2197 """ 2198 Create an Event object. 2199 2200 A 2D screen-position can be provided to be picked. 2201 """ 2202 if not self.interactor: 2203 return Event() 2204 2205 if len(pos): 2206 x, y = pos 2207 self.interactor.SetEventPosition(pos) 2208 else: 2209 x, y = self.interactor.GetEventPosition() 2210 self.renderer = self.interactor.FindPokedRenderer(x, y) 2211 if not self.picker: 2212 self.picker = vtk.vtkPropPicker() 2213 self.picked2d = (x, y) 2214 self.picker.PickProp(x, y, self.renderer) 2215 xp, yp = self.interactor.GetLastEventPosition() 2216 actor = self.picker.GetProp3D() 2217 delta3d = np.array([0, 0, 0]) 2218 if actor: 2219 picked3d = np.array(self.picker.GetPickPosition()) 2220 if isinstance(actor, vedo.base.Base3DProp): # needed! 2221 if actor.picked3d is not None: 2222 delta3d = picked3d - actor.picked3d 2223 actor.picked3d = picked3d 2224 else: 2225 picked3d = None 2226 2227 if not actor: # try 2D 2228 actor = self.picker.GetActor2D() 2229 2230 dx, dy = x - xp, y - yp 2231 2232 key = self.interactor.GetKeySym() 2233 2234 if key: 2235 if "_L" in key or "_R" in key: 2236 # skip things like Shift_R 2237 key = "" # better than None 2238 else: 2239 if self.interactor.GetShiftKey(): 2240 key = key.upper() 2241 2242 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2243 key = "underscore" 2244 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2245 key = "plus" 2246 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2247 key = "?" 2248 2249 if self.interactor.GetControlKey(): 2250 key = "Ctrl+" + key 2251 2252 if self.interactor.GetAltKey(): 2253 key = "Alt+" + key 2254 2255 event = Event() 2256 event.name = ename 2257 event.title = self.title 2258 event.id = cid 2259 event.priority = priority 2260 event.time = time.time() 2261 event.at = self.renderers.index(self.renderer) 2262 event.actor = actor 2263 event.picked3d = picked3d 2264 event.keyPressed = key # obsolete, will disappear. Use "keypress" 2265 event.keypress = key 2266 event.picked2d = (x, y) 2267 event.delta2d = (dx, dy) 2268 event.angle2d = np.arctan2(dy, dx) 2269 event.speed2d = np.sqrt(dx * dx + dy * dy) 2270 event.delta3d = delta3d 2271 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2272 event.isPoints = isinstance(actor, vedo.Points) 2273 event.isMesh = isinstance(actor, vedo.Mesh) 2274 event.isAssembly = isinstance(actor, vedo.Assembly) 2275 event.isVolume = isinstance(actor, vedo.Volume) 2276 event.isPicture = isinstance(actor, vedo.Picture) 2277 event.isActor2D = isinstance(actor, vtk.vtkActor2D) 2278 return event 2279 2280 @deprecated(reason=vedo.colors.red + "Please use add_callback()" + vedo.colors.reset) 2281 def addCallback(self, *a, **b): 2282 """Deprecated, use `add_callback()`""" 2283 return self.add_callback(*a, **b) 2284 2285 def add_callback(self, event_name, func, priority=0.0): 2286 """ 2287 Add a function to be executed while show() is active. 2288 Information about the event can be acquired with method getEvent(). 2289 2290 Return a unique id for the callback. 2291 2292 The callback function (see example below) exposes a dictionary 2293 with the following information: 2294 - `name`: event name, 2295 - `id`: event unique identifier, 2296 - `priority`: event priority (float), 2297 - `interactor`: the interactor object, 2298 - `at`: renderer nr. where the event occurred 2299 - `actor`: object picked by the mouse 2300 - `picked3d`: point picked in world coordinates 2301 - `keypress`: key pressed as string 2302 - `picked2d`: screen coords of the mouse pointer 2303 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2304 - `delta3d`: ...same but in 3D world coords 2305 - `angle2d`: angle of mouse movement on screen 2306 - `speed2d`: speed of mouse movement on screen 2307 - `speed3d`: speed of picked point in world coordinates 2308 - `isPoints`: True if of class 2309 - `isMesh`: True if of class 2310 - `isAssembly`: True if of class 2311 - `isVolume`: True if of class Volume 2312 - `isPicture`: True if of class 2313 2314 Frequently used events are: 2315 - `KeyPress`, `KeyRelease`: listen to keyboard events 2316 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2317 - `MiddleButtonPress`, `MiddleButtonRelease` 2318 - `RightButtonPress`, `RightButtonRelease` 2319 - `MouseMove`: listen to mouse pointer changing position 2320 - `MouseWheelForward`, `MouseWheelBackward` 2321 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2322 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2323 - `ResetCamera`, `ResetCameraClippingRange` 2324 - `Error`, `Warning` 2325 - `Char` 2326 - `Timer` 2327 2328 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2329 2330 Example: 2331 ```python 2332 from vedo import * 2333 2334 def func(evt): 2335 # this function is called every time the mouse moves 2336 # (evt is a dotted dictionary) 2337 if not evt.actor: 2338 return # no hit, return 2339 print("point coords =", evt.picked3d) 2340 # print("full event dump:", evt) 2341 2342 elli = Ellipsoid() 2343 plt = Plotter(axes=1) 2344 plt.add_callback('mouse hovering', func) 2345 plt.show(elli).close() 2346 ``` 2347 2348 Examples: 2349 - [spline_draw.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw.py) 2350 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2351 2352  2353 2354 - ..and many others! 2355 """ 2356 if not self.interactor: 2357 return None 2358 2359 # as vtk names are ugly and difficult to remember: 2360 ln = event_name.lower() 2361 if "click" in ln or "button" in ln: 2362 event_name = "LeftButtonPress" 2363 if "right" in ln: 2364 event_name = "RightButtonPress" 2365 elif "mid" in ln: 2366 event_name = "MiddleButtonPress" 2367 if "release" in ln: 2368 # event_name = event_name.replace("Press","Release") # vtk bug 2369 event_name = "EndInteraction" 2370 else: 2371 if "key" in ln: 2372 if "release" in ln: 2373 event_name = "KeyRelease" 2374 else: 2375 event_name = "KeyPress" 2376 2377 if ("mouse" in ln and "mov" in ln) or "over" in ln: 2378 event_name = "MouseMove" 2379 if "timer" in ln: 2380 event_name = "Timer" 2381 2382 if not event_name.endswith("Event"): 2383 event_name += "Event" 2384 2385 def _func_wrap(iren, ename): 2386 event = self.fill_event(ename=ename, priority=priority, cid=cid) 2387 self.last_event = event 2388 func(event) 2389 return ## _func_wrap 2390 2391 # Not compatible with ProcessEvents() 2392 if "MouseMove" in event_name or "Timer" in event_name: 2393 settings.allow_interaction = False 2394 2395 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2396 vedo.logger.debug(f"registering event: {event_name} with id={cid}") 2397 return cid 2398 2399 def remove_callback(self, cid): 2400 """ 2401 Remove a callback function by its id 2402 or a whole category of callbacks by their name. 2403 2404 Arguments: 2405 cid : (int, str) 2406 Unique id of the callback. 2407 If an event name is passed all callbacks of that type are removed. 2408 """ 2409 if self.interactor: 2410 if isinstance(cid, str): 2411 # as vtk names are ugly and difficult to remember: 2412 ln = cid.lower() 2413 if "click" in ln or "button" in ln: 2414 cid = "LeftButtonPress" 2415 if "right" in ln: 2416 cid = "RightButtonPress" 2417 elif "mid" in ln: 2418 cid = "MiddleButtonPress" 2419 if "release" in ln: 2420 cid.replace("Press", "Release") 2421 else: 2422 if "key" in ln: 2423 if "release" in ln: 2424 cid = "KeyRelease" 2425 else: 2426 cid = "KeyPress" 2427 if ("mouse" in ln and "mov" in ln) or "over" in ln: 2428 cid = "MouseMove" 2429 if "timer" in ln: 2430 cid = "Timer" 2431 if not cid.endswith("Event"): 2432 cid += "Event" 2433 self.interactor.RemoveObservers(cid) 2434 else: 2435 self.interactor.RemoveObserver(cid) 2436 return self 2437 2438 def timer_callback(self, action, timer_id=None, dt=1, one_shot=False): 2439 """ 2440 Start or stop an existing timer. 2441 2442 Arguments: 2443 action : (str) 2444 Either "create"/"start" or "destroy"/"stop" 2445 timer_id : (int) 2446 When stopping the timer, the ID of the timer as returned when created 2447 dt : (int) 2448 time in milliseconds between each repeated call 2449 one_shot : (bool) 2450 create a one shot timer of prescribed duration instead of a repeating one 2451 2452 Examples: 2453 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2454 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2455 2456  2457 """ 2458 if action in ("create", "start") : 2459 if one_shot: 2460 timer_id = self.interactor.CreateOneShotTimer(dt) 2461 else: 2462 timer_id = self.interactor.CreateRepeatingTimer(dt) 2463 return timer_id 2464 2465 elif action in ("destroy", "stop"): 2466 if timer_id is not None: 2467 self.interactor.DestroyTimer(timer_id) 2468 else: 2469 e = f"in plotter.timer_callback(). Cannot understand action: {action}\n" 2470 e += " allowed actions are: ['start', 'stop']. Skipped." 2471 vedo.logger.error(e) 2472 return self 2473 2474 def compute_world_coordinate( 2475 self, 2476 pos2d, 2477 at=None, 2478 objs=(), 2479 bounds=(), 2480 offset=None, 2481 pixeltol=None, 2482 worldtol=None, 2483 ): 2484 """ 2485 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2486 If a set of meshes is passed then points are placed onto these. 2487 2488 Arguments: 2489 pos2d : (list) 2490 2D screen coordinates point. 2491 at : (int) 2492 renderer number. 2493 objs : (list) 2494 list of Mesh objects to project the point onto. 2495 bounds : (list) 2496 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2497 offset : (float) 2498 specify an offset value. 2499 pixeltol : (int) 2500 screen tolerance in pixels. 2501 worldtol : (float) 2502 world coordinates tolerance. 2503 2504 Returns: 2505 numpy array, the point in 3D world coordinates. 2506 2507 Examples: 2508 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2509 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2510 2511  2512 """ 2513 if at is not None: 2514 renderer = self.renderers[at] 2515 else: 2516 renderer = self.renderer 2517 2518 if not objs: 2519 pp = vtk.vtkFocalPlanePointPlacer() 2520 else: 2521 pp = vtk.vtkPolygonalSurfacePointPlacer() 2522 for ob in objs: 2523 pp.AddProp(ob) 2524 2525 if len(bounds) == 6: 2526 pp.SetPointBounds(bounds) 2527 if pixeltol: 2528 pp.SetPixelTolerance(pixeltol) 2529 if worldtol: 2530 pp.SetWorldTolerance(worldtol) 2531 if offset: 2532 pp.SetOffset(offset) 2533 2534 worldPos = [0, 0, 0] 2535 worldOrient = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2536 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2537 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2538 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2539 return np.array(worldPos) 2540 2541 def compute_screen_coordinates(self, obj, full_window=False): 2542 """ 2543 Given a 3D points in the current renderer (or full window), 2544 find the screen pixel coordinates. 2545 2546 Example: 2547 ```python 2548 from vedo import * 2549 2550 elli = Ellipsoid().rotate_y(30) 2551 2552 plt = Plotter() 2553 plt.show(elli) 2554 2555 xyscreen = plt.compute_screen_positions(elli) 2556 print('xyscreen coords:', xyscreen) 2557 2558 # simulate an event happening at one point 2559 event = plt.fill_event(pos=xyscreen[123]) 2560 print(event) 2561 ``` 2562 """ 2563 if isinstance(obj, vedo.base.Base3DProp): 2564 pts = obj.points() 2565 elif utils.is_sequence(obj): 2566 pts = obj 2567 p2d = [] 2568 cs = vtk.vtkCoordinate() 2569 cs.SetCoordinateSystemToWorld() 2570 cs.SetViewport(self.renderer) 2571 for p in pts: 2572 cs.SetValue(p) 2573 if full_window: 2574 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2575 else: 2576 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2577 return np.array(p2d, dtype=int) 2578 2579 def _scan_input(self, wannabeacts): 2580 # scan the input of show 2581 if not utils.is_sequence(wannabeacts): 2582 wannabeacts = [wannabeacts] 2583 2584 scannedacts = [] 2585 for a in wannabeacts: # scan content of list 2586 2587 if a is None: 2588 pass 2589 2590 elif isinstance(a, vtk.vtkActor): 2591 2592 scannedacts.append(a) 2593 2594 if isinstance(a, vedo.base.BaseActor): 2595 if a.shadows: 2596 a._update_shadows() 2597 scannedacts.extend(a.shadows) 2598 2599 if a.trail and a.trail not in self.actors: 2600 a._update_trail() 2601 scannedacts.append(a.trail) 2602 # trails may also have shadows: 2603 if a.trail.shadows: 2604 a.trail._update_shadows() 2605 scannedacts.extend(a.trail.shadows) 2606 2607 if a._caption and a._caption not in self.actors: 2608 scannedacts.append(a._caption) 2609 2610 elif isinstance(a, vtk.vtkActor2D): 2611 scannedacts.append(a) 2612 2613 elif isinstance(a, vtk.vtkAssembly): 2614 scannedacts.append(a) 2615 if a.trail and a.trail not in self.actors: 2616 scannedacts.append(a.trail) 2617 2618 elif isinstance(a, (vedo.Volume, vedo.VolumeSlice)): 2619 scannedacts.append(a) 2620 2621 elif isinstance(a, vtk.vtkImageData): 2622 scannedacts.append(vedo.Volume(a)) 2623 2624 elif isinstance(a, vedo.TetMesh): 2625 # check ugrid is all made of tets 2626 ugrid = a.inputdata() 2627 uarr = ugrid.GetCellTypesArray() 2628 celltypes = np.unique(utils.vtk2numpy(uarr)) 2629 ncelltypes = len(celltypes) 2630 if ncelltypes > 1 or (ncelltypes == 1 and celltypes[0] != 10): 2631 scannedacts.append(a.tomesh()) 2632 else: 2633 if not ugrid.GetPointData().GetScalars(): 2634 if not ugrid.GetCellData().GetScalars(): 2635 # add dummy array for vtkProjectedTetrahedraMapper to work: 2636 a.celldata["DummyOneArray"] = np.ones(a.ncells) 2637 scannedacts.append(a) 2638 2639 elif isinstance(a, vedo.UGrid): 2640 scannedacts.append(a.tomesh()) 2641 2642 elif isinstance(a, vtk.vtkVolume): # order matters! dont move above TetMesh 2643 vvol = vedo.Volume(a.GetMapper().GetInput()) 2644 vprop = vtk.vtkVolumeProperty() 2645 vprop.DeepCopy(a.GetProperty()) 2646 vvol.SetProperty(vprop) 2647 scannedacts.append(vvol) 2648 2649 elif isinstance(a, str): 2650 # assume a 2D comment was given 2651 changed = False # check if one already exists so to just update text 2652 if self.renderer: # might be jupyter 2653 acs = self.renderer.GetActors2D() 2654 acs.InitTraversal() 2655 for i in range(acs.GetNumberOfItems()): 2656 act = acs.GetNextItem() 2657 if isinstance(act, vedo.shapes.Text2D): 2658 aposx, aposy = act.GetPosition() 2659 if aposx < 0.01 and aposy > 0.99: # "top-left" 2660 act.text(a) # update content! no appending nada 2661 changed = True 2662 break 2663 if not changed: 2664 out = vedo.shapes.Text2D(a) # append a new one 2665 scannedacts.append(out) 2666 2667 elif isinstance(a, vtk.vtkImageActor): 2668 scannedacts.append(a) 2669 2670 elif isinstance(a, vtk.vtkBillboardTextActor3D): 2671 scannedacts.append(a) 2672 2673 elif isinstance(a, vtk.vtkLight): 2674 self.renderer.AddLight(a) 2675 2676 elif isinstance(a, vtk.vtkMultiBlockDataSet): 2677 for i in range(a.GetNumberOfBlocks()): 2678 b = a.GetBlock(i) 2679 if isinstance(b, vtk.vtkPolyData): 2680 scannedacts.append(vedo.Mesh(b)) 2681 elif isinstance(b, vtk.vtkImageData): 2682 scannedacts.append(vedo.Volume(b)) 2683 2684 elif "PolyData" in str(type(a)): # assume pyvista or vtkPolydata 2685 scannedacts.append(vedo.Mesh(a)) 2686 2687 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 2688 import vedo.dolfin as dlf 2689 scannedacts.append(dlf.MeshActor(a)) 2690 2691 elif "trimesh" in str(type(a)): 2692 scannedacts.append(utils.trimesh2vedo(a)) 2693 2694 elif "meshlab" in str(type(a)): 2695 if "MeshSet" in str(type(a)): 2696 for i in range(a.number_meshes()): 2697 if a.mesh_id_exists(i): 2698 scannedacts.append(vedo.Mesh(utils.meshlab2vedo(a.mesh(i)))) 2699 else: 2700 scannedacts.append(vedo.Mesh(utils.meshlab2vedo(a))) 2701 2702 elif isinstance(a, (vtk.vtkProp, )): 2703 scannedacts.append(a) 2704 2705 else: 2706 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 2707 return scannedacts 2708 2709 2710 def show( 2711 self, 2712 *actors, 2713 at=None, 2714 axes=None, 2715 resetcam=None, 2716 zoom=False, 2717 interactive=None, 2718 viewup="", 2719 azimuth=0, 2720 elevation=0, 2721 roll=0, 2722 camera=None, 2723 mode=0, 2724 rate=None, 2725 bg=None, 2726 bg2=None, 2727 size=None, 2728 title=None, 2729 q=False, 2730 ): 2731 """ 2732 Render a list of objects. 2733 2734 Arguments: 2735 at : (int) 2736 number of the renderer to plot to, in case of more than one exists 2737 2738 axes : (int) 2739 axis type-1 can be fully customized by passing a dictionary. 2740 Check `addons.Axes()` for the full list of options. 2741 set the type of axes to be shown: 2742 - 0, no axes 2743 - 1, draw three gray grid walls 2744 - 2, show cartesian axes from (0,0,0) 2745 - 3, show positive range of cartesian axes from (0,0,0) 2746 - 4, show a triad at bottom left 2747 - 5, show a cube at bottom left 2748 - 6, mark the corners of the bounding box 2749 - 7, draw a 3D ruler at each side of the cartesian axes 2750 - 8, show the `vtkCubeAxesActor` object 2751 - 9, show the bounding box outLine 2752 - 10, show three circles representing the maximum bounding box 2753 - 11, show a large grid on the x-y plane 2754 - 12, show polar axes 2755 - 13, draw a simple ruler at the bottom of the window 2756 2757 azimuth/elevation/roll : (float) 2758 move camera accordingly the specified value 2759 2760 viewup: str, list 2761 either `['x', 'y', 'z']` or a vector to set vertical direction 2762 2763 resetcam : (bool) 2764 re-adjust camera position to fit objects 2765 2766 camera : (dict, vtkCamera) 2767 camera parameters can further be specified with a dictionary 2768 assigned to the ``camera`` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 2769 - pos, `(list)`, the position of the camera in world coordinates 2770 - focal_point `(list)`, the focal point of the camera in world coordinates 2771 - viewup `(list)`, the view up direction for the camera 2772 - distance `(float)`, set the focal point to the specified distance from the camera position. 2773 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 2774 - parallel_scale `(float)`, 2775 scaling used for a parallel projection, i.e. the height of the viewport 2776 in world-coordinate distances. The default is 1. Note that the "scale" parameter works as 2777 an "inverse scale", larger numbers produce smaller images. 2778 This method has no effect in perspective projection mode. 2779 2780 - thickness `(float)`, 2781 set the distance between clipping planes. This method adjusts the far clipping 2782 plane to be set a distance 'thickness' beyond the near clipping plane. 2783 2784 - view_angle `(float)`, 2785 the camera view angle, which is the angular height of the camera view 2786 measured in degrees. The default angle is 30 degrees. 2787 This method has no effect in parallel projection mode. 2788 The formula for setting the angle up for perfect perspective viewing is: 2789 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 2790 (measured by holding a ruler up to your screen) and d is the distance 2791 from your eyes to the screen. 2792 2793 interactive : (bool) 2794 pause and interact with window (True) or continue execution (False) 2795 2796 rate : (float) 2797 maximum rate of `show()` in Hertz 2798 2799 mode : (int, str) 2800 set the type of interaction: 2801 - 0 = TrackballCamera [default] 2802 - 1 = TrackballActor 2803 - 2 = JoystickCamera 2804 - 3 = JoystickActor 2805 - 4 = Flight 2806 - 5 = RubberBand2D 2807 - 6 = RubberBand3D 2808 - 7 = RubberBandZoom 2809 - 8 = Context 2810 - 9 = 3D 2811 - 10 = Terrain 2812 - 11 = Unicam 2813 """ 2814 2815 if self.wx_widget: 2816 return self 2817 2818 if self.renderers: # in case of notebooks 2819 2820 if at is None: 2821 at = self.renderers.index(self.renderer) 2822 2823 else: 2824 2825 if at >= len(self.renderers): 2826 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 2827 vedo.logger.error(t) 2828 return self 2829 2830 self.renderer = self.renderers[at] 2831 2832 if title is not None: 2833 self.title = title 2834 2835 if size is not None: 2836 self.size = size 2837 if self.size[0] == "f": # full screen 2838 self.size = "fullscreen" 2839 self.window.SetFullScreen(True) 2840 self.window.BordersOn() 2841 else: 2842 self.window.SetSize(int(self.size[0]), int(self.size[1])) 2843 2844 if settings.default_backend == 'vtk': 2845 if str(bg).endswith(".hdr"): 2846 self._add_skybox(bg) 2847 else: 2848 if bg is not None: 2849 self.backgrcol = vedo.get_color(bg) 2850 self.renderer.SetBackground(self.backgrcol) 2851 if bg2 is not None: 2852 self.renderer.GradientBackgroundOn() 2853 self.renderer.SetBackground2(vedo.get_color(bg2)) 2854 2855 if axes is not None: 2856 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 2857 actors = list(actors) 2858 actors.append(axes) # move it into the list of normal things to show 2859 axes = 0 2860 self.axes = axes 2861 2862 if self.offscreen: 2863 interactive = False 2864 self._interactive = False 2865 2866 # camera stuff 2867 if resetcam is not None: 2868 self.resetcam = resetcam 2869 2870 if camera is not None: 2871 self.resetcam = False 2872 if isinstance(camera, vtk.vtkCamera): 2873 self.camera = camera 2874 else: 2875 self.camera = utils.camera_from_dict(camera) 2876 if self.renderer: 2877 self.renderer.SetActiveCamera(self.camera) 2878 2879 if self.renderer: 2880 self.camera = self.renderer.GetActiveCamera() 2881 2882 # if user passes a list of actors forget about everything and show those 2883 if len(actors) == 0: 2884 actors = None 2885 elif len(actors) == 1: 2886 actors = actors[0] 2887 else: 2888 actors = utils.flatten(actors) 2889 2890 actors2show = [] 2891 if actors is not None: 2892 self.actors = [] 2893 actors2show = self._scan_input(actors) 2894 for a in actors2show: 2895 if a not in self.actors: 2896 self.actors.append(a) 2897 2898 # update all trailing lines 2899 for a in self.actors: 2900 try: 2901 if a.trail: 2902 a._update_trail() 2903 except AttributeError: 2904 pass 2905 2906 # Backend ############################################################### 2907 if settings.default_backend != 'vtk': 2908 if settings.default_backend in ["k3d"]: 2909 return backends.get_notebook_backend(self.actors) 2910 ######################################################################### 2911 2912 # remove all old shadows from the scene 2913 shad_acs = self.renderer.GetActors() 2914 shad_acs.InitTraversal() 2915 for _ in range(shad_acs.GetNumberOfItems()): 2916 a = shad_acs.GetNextItem() 2917 try: 2918 if a.name == "Shadow": 2919 self.renderer.RemoveActor(a) 2920 except AttributeError: 2921 pass 2922 2923 # rendering 2924 for ia in actors2show: 2925 2926 if not ia: 2927 continue 2928 2929 # if hasattr(ia, "shadows"): 2930 # for ias in ia.shadows: 2931 # # print([ias.name]) 2932 # self.renderer.RemoveActor(ias) 2933 2934 self.renderer.AddActor(ia) 2935 2936 if isinstance(ia, vedo.base.Base3DProp): 2937 2938 if ia._isfollower: # set by mesh.follow_camera() 2939 ia.SetCamera(self.camera) 2940 2941 ia.rendered_at.add(at) # set.add() 2942 2943 if ia.scalarbar: 2944 self.renderer.AddActor(ia.scalarbar) 2945 # fix gray color labels and title to white or black 2946 if isinstance(ia.scalarbar, vtk.vtkScalarBarActor): 2947 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 2948 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 2949 c = (0.9, 0.9, 0.9) 2950 if np.sum(self.renderer.GetBackground()) > 1.5: 2951 c = (0.1, 0.1, 0.1) 2952 ia.scalarbar.GetLabelTextProperty().SetColor(c) 2953 ia.scalarbar.GetTitleTextProperty().SetColor(c) 2954 2955 if self.sharecam: 2956 for r in self.renderers: 2957 r.SetActiveCamera(self.camera) 2958 2959 if self.qt_widget is not None: 2960 self.qt_widget.GetRenderWindow().AddRenderer(self.renderer) 2961 2962 if self.axes is not None: 2963 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 2964 addons.add_global_axes(self.axes) 2965 2966 ######################################################################### 2967 if settings.default_backend in ["ipyvtk", "trame"]: 2968 return backends.get_notebook_backend() 2969 ######################################################################### 2970 2971 if self.resetcam: 2972 self.renderer.ResetCamera() 2973 2974 if len(self.renderers) > 1: 2975 self.add_renderer_frame() 2976 2977 if settings.default_backend == '2d' and not zoom: 2978 zoom = "tightest" 2979 2980 if zoom: 2981 if zoom == "tight": 2982 self.reset_camera(tight=0.04) 2983 elif zoom == "tightest": 2984 self.reset_camera(tight=0.0001) 2985 else: 2986 self.camera.Zoom(zoom) 2987 if elevation: 2988 self.camera.Elevation(elevation) 2989 if azimuth: 2990 self.camera.Azimuth(azimuth) 2991 if roll: 2992 self.camera.Roll(roll) 2993 2994 if self._first_viewup and len(viewup)>0: 2995 self._first_viewup = False # gets executed only once 2996 b = self.renderer.ComputeVisiblePropBounds() 2997 cm = np.array([(b[1]+b[0])/2, (b[3]+b[2])/2, (b[5]+b[4])/2]) 2998 sz = np.array([(b[1]-b[0]), (b[3]-b[2]), (b[5]-b[4])]) 2999 if viewup == "x": 3000 sz = np.linalg.norm(sz) 3001 self.camera.SetViewUp([1, 0, 0]) 3002 self.camera.SetPosition(cm + sz) 3003 elif viewup == "y": 3004 sz = np.linalg.norm(sz) 3005 self.camera.SetViewUp([0, 1, 0]) 3006 self.camera.SetPosition(cm + sz) 3007 elif viewup == "z": 3008 sz = np.array([ 3009 (b[1] - b[0]) * 0.7, 3010 -(b[3] - b[2]) * 1.0, 3011 (b[5] - b[4]) * 1.2, 3012 ]) 3013 self.camera.SetViewUp([0, 0, 1]) 3014 self.camera.SetPosition(cm + 2 * sz) 3015 elif utils.is_sequence(viewup): 3016 sz = np.linalg.norm(sz) 3017 self.camera.SetViewUp(viewup) 3018 cpos = np.cross([0,1,0], viewup) 3019 self.camera.SetPosition(cm - 2 * sz * cpos) 3020 elif viewup == "2d": 3021 mode = 12 3022 3023 self.renderer.ResetCameraClippingRange() 3024 3025 if self.interactor and not self.interactor.GetInitialized(): 3026 self.interactor.Initialize() 3027 self.interactor.RemoveObservers("CharEvent") 3028 3029 if settings.immediate_rendering: 3030 self.window.Render() ##################### <-------------- Render 3031 3032 # 2d #################################################################### 3033 if settings.default_backend == "2d": 3034 return backends.get_notebook_backend() 3035 ######################################################################### 3036 3037 self.window.SetWindowName(self.title) 3038 3039 try: 3040 # Needs pip install pyobjc 3041 if (self._cocoa_initialized is False 3042 and "Darwin" in vedo.sys_platform 3043 and not self.offscreen 3044 ): 3045 self._cocoa_initialized = True 3046 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps 3047 pid = os.getpid() 3048 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3049 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3050 except: 3051 vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3052 3053 if self.interactor: # can be offscreen.. 3054 3055 if interactive is not None: 3056 self._interactive = interactive 3057 3058 # Set the style of interaction 3059 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3060 if mode in (0, 'TrackballCamera'): 3061 # csty = self.interactor.GetInteractorStyle().GetCurrentStyle().GetClassName() 3062 # if "TrackballCamera" not in csty: 3063 # this causes problems (when pressing 3 eg) : 3064 if self.qt_widget: 3065 self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera()) 3066 elif mode in (1, 'TrackballActor'): 3067 self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleTrackballActor()) 3068 elif mode in (2, 'JoystickCamera'): 3069 self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleJoystickCamera()) 3070 elif mode in (3, 'JoystickActor'): 3071 self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleJoystickActor()) 3072 elif mode in (4, 'Flight'): 3073 self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleFlight()) 3074 elif mode in (5, 'RubberBand2D'): 3075 self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleRubberBand2D()) 3076 elif mode in (6, 'RubberBand3D'): 3077 self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleRubberBand3D()) 3078 elif mode in (7, 'RubberBandZoom'): 3079 self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleRubberBandZoom()) 3080 elif mode in (8, 'Context'): 3081 self.interactor.SetInteractorStyle(vtk.vtkContextInteractorStyle()) 3082 elif mode in (9, '3D'): 3083 self.interactor.SetInteractorStyle(vtk.vtkInteractorStyle3D()) 3084 elif mode in (10, 'Terrain'): 3085 self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleTerrain()) 3086 elif mode in (11, 'Unicam'): 3087 self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleUnicam()) 3088 elif mode in (12, 'Image', 'image'): 3089 astyle = vtk.vtkInteractorStyleImage() 3090 astyle.SetInteractionModeToImage3D() 3091 self.interactor.SetInteractorStyle(astyle) 3092 3093 if self._interactive: 3094 self.interactor.Start() 3095 # vtk BUG: 3096 if vedo.vtk_version == (9,2,2): 3097 self.interactor.GetRenderWindow().SetDisplayId("_0_p_void") ##HACK 3098 elif ( 3099 "Darwin" in vedo.sys_platform 3100 and vedo.vtk_version == (9,2,5) 3101 and settings.allow_interaction 3102 and len(self.renderers) == 1 3103 ): 3104 # this causes focus problems with boolean.py and others 3105 # when multirendering is present, but makes the window pop up 3106 self.process_events() 3107 3108 if rate: 3109 if self.clock is None: # set clock and limit rate 3110 self._clockt0 = time.time() 3111 self.clock = 0.0 3112 else: 3113 t = time.time() - self._clockt0 3114 elapsed = t - self.clock 3115 mint = 1.0 / rate 3116 if elapsed < mint: 3117 time.sleep(mint - elapsed) 3118 self.clock = time.time() - self._clockt0 3119 3120 return self 3121 3122 3123 def add_inset(self, *actors, **options): 3124 """Add a draggable inset space into a renderer. 3125 3126 Arguments: 3127 at : (int) 3128 specify the renderer number 3129 pos : (list) 3130 icon position in the range [1-4] indicating one of the 4 corners, 3131 or it can be a tuple (x,y) as a fraction of the renderer size. 3132 size : (float) 3133 size of the square inset 3134 draggable : (bool) 3135 if True the subrenderer space can be dragged around 3136 c : (color) 3137 color of the inset frame when dragged 3138 3139 Examples: 3140 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3141 3142  3143 """ 3144 if not self.interactor: 3145 return None 3146 pos = options.pop("pos", 0) 3147 size = options.pop("size", 0.1) 3148 c = options.pop("c", "lb") 3149 at = options.pop("at", None) 3150 draggable = options.pop("draggable", True) 3151 3152 if not self.renderer: 3153 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3154 save_int = self._interactive 3155 self.show(interactive=0) 3156 self._interactive = save_int 3157 widget = vtk.vtkOrientationMarkerWidget() 3158 r, g, b = vedo.get_color(c) 3159 widget.SetOutlineColor(r, g, b) 3160 if len(actors) == 1: 3161 widget.SetOrientationMarker(actors[0]) 3162 else: 3163 widget.SetOrientationMarker(vedo.Assembly(actors)) 3164 3165 widget.SetInteractor(self.interactor) 3166 3167 if utils.is_sequence(pos): 3168 widget.SetViewport(pos[0]-size, pos[1]-size, pos[0]+size, pos[1]+size) 3169 else: 3170 if pos < 2: 3171 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3172 elif pos == 2: 3173 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3174 elif pos == 3: 3175 widget.SetViewport(0, 0, size * 2, size * 2) 3176 elif pos == 4: 3177 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3178 widget.EnabledOn() 3179 widget.SetInteractive(draggable) 3180 if at is not None and at < len(self.renderers): 3181 widget.SetCurrentRenderer(self.renderers[at]) 3182 self.widgets.append(widget) 3183 return widget 3184 3185 def clear(self, at=None, deep=False, render=False): 3186 """Clear the scene from all meshes and volumes.""" 3187 if at is not None: 3188 renderer = self.renderers[at] 3189 else: 3190 renderer = self.renderer 3191 if not renderer: 3192 return self 3193 3194 if deep: 3195 renderer.RemoveAllViewProps() 3196 else: 3197 for a in set(self.get_meshes() + self.get_volumes() + self.actors + self.axes_instances): 3198 if isinstance(a, vedo.shapes.Text2D): 3199 continue 3200 self.remove(a) 3201 try: 3202 if a.scalarbar: 3203 self.remove(a.scalarbar) 3204 except AttributeError: 3205 pass 3206 self.actors = [] 3207 3208 if render: 3209 self.render() 3210 return self 3211 3212 def break_interaction(self): 3213 """Break window interaction and return to the python execution flow""" 3214 if self.interactor: 3215 self.interactor.ExitCallback() 3216 return self 3217 3218 def close_window(self): 3219 """Close the current or the input rendering window. 3220 3221 Examples: 3222 - [closewindow.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/closewindow.py) 3223 """ 3224 vedo.last_figure = None 3225 self.sliders = [] 3226 self.buttons = [] 3227 self.widgets = [] 3228 self.hover_legends = [] 3229 self.background_renderer = None 3230 self._first_viewup = True 3231 self._extralight = None 3232 3233 self.hint_widget = None 3234 self.cutter_widget = None 3235 3236 for r in self.renderers: 3237 r.RemoveAllObservers() 3238 if hasattr(self, "window") and self.window: 3239 if hasattr(self, "interactor") and self.interactor: 3240 self.interactor.ExitCallback() 3241 try: 3242 self.interactor.SetDone(True) 3243 except AttributeError: 3244 pass 3245 self.interactor.TerminateApp() 3246 3247 # self.interactor = None 3248 self.window.Finalize() # this must be done here 3249 3250 if hasattr(self, "interactor") and self.interactor: 3251 if "Darwin" in vedo.sys_platform: 3252 try: 3253 self.interactor.ProcessEvents() 3254 except: 3255 pass 3256 self.interactor = None 3257 3258 self.window = None 3259 3260 self.renderer = None # current renderer 3261 self.renderers = [] 3262 self.camera = None 3263 self.skybox = None 3264 return self 3265 3266 def close(self): 3267 """Close the Plotter instance and release resources.""" 3268 self.close_window() 3269 self.actors = [] 3270 if vedo.plotter_instance == self: 3271 vedo.plotter_instance = None 3272 3273 def screenshot(self, filename="screenshot.png", scale=None, asarray=False): 3274 """ 3275 Take a screenshot of the Plotter window. 3276 3277 Arguments: 3278 scale : (int) 3279 set image magnification as an integer multiplicating factor 3280 asarray : (bool) 3281 return a numpy array of the image instead of writing a file 3282 """ 3283 return vedo.io.screenshot(filename, scale, asarray) 3284 3285 def topicture(self, scale=None): 3286 """ 3287 Generate a Picture object from the current rendering window. 3288 3289 Arguments: 3290 scale : (int) 3291 set image magnification as an integer multiplicating factor 3292 """ 3293 if scale is None: 3294 scale = settings.screeshot_scale 3295 3296 if settings.screeshot_large_image: 3297 w2if = vtk.vtkRenderLargeImage() 3298 w2if.SetInput(self.renderer) 3299 w2if.SetMagnification(scale) 3300 else: 3301 w2if = vtk.vtkWindowToImageFilter() 3302 w2if.SetInput(self.window) 3303 if hasattr(w2if, "SetScale"): 3304 w2if.SetScale(scale, scale) 3305 if settings.screenshot_transparent_background: 3306 w2if.SetInputBufferTypeToRGBA() 3307 w2if.ReadFrontBufferOff() # read from the back buffer 3308 w2if.Update() 3309 return vedo.picture.Picture(w2if.GetOutput()) 3310 3311 def export(self, filename="scene.npz", binary=False): 3312 """ 3313 Export scene to file to HTML, X3D or Numpy file. 3314 3315 Examples: 3316 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3317 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3318 """ 3319 vedo.io.export_window(filename, binary=binary) 3320 return self 3321 3322 def color_picker(self, xy, verbose=False): 3323 """Pick color of specific (x,y) pixel on the screen.""" 3324 w2if = vtk.vtkWindowToImageFilter() 3325 w2if.SetInput(self.window) 3326 w2if.ReadFrontBufferOff() 3327 w2if.Update() 3328 nx, ny = self.window.GetSize() 3329 varr = w2if.GetOutput().GetPointData().GetScalars() 3330 3331 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3332 x, y = int(xy[0]), int(xy[1]) 3333 if y < ny and x < nx: 3334 3335 rgb = arr[y, x] 3336 3337 if verbose: 3338 vedo.printc("Pixel", [x, y], "has RGB[", end="") 3339 vedo.printc("â–ˆ", c=[rgb[0], 0, 0], end="") 3340 vedo.printc("â–ˆ", c=[0, rgb[1], 0], end="") 3341 vedo.printc("â–ˆ", c=[0, 0, rgb[2]], end="") 3342 vedo.printc("] = ", end="") 3343 cnm = vedo.get_color_name(rgb) 3344 if np.sum(rgb) < 150: 3345 vedo.printc(rgb.tolist(), vedo.colors.rgb2hex(np.array(rgb)/255), c='w', 3346 bc=rgb, invert=1, end='') 3347 vedo.printc(' -> '+cnm, invert=1, c='w') 3348 else: 3349 vedo.printc(rgb.tolist(), vedo.colors.rgb2hex(np.array(rgb)/255), c=rgb, end='') 3350 vedo.printc(' -> '+cnm, c=cnm) 3351 3352 return rgb 3353 3354 return None 3355 3356 ####################################################################### 3357 def _mouseleftclick(self, iren, event): 3358 3359 x, y = iren.GetEventPosition() 3360 3361 renderer = iren.FindPokedRenderer(x, y) 3362 picker = vtk.vtkPropPicker() 3363 picker.PickProp(x, y, renderer) 3364 3365 self.renderer = renderer 3366 3367 clicked_actor = picker.GetActor() 3368 3369 # print('_mouseleftclick mouse at', x, y) 3370 # print("picked Volume:", [picker.GetVolume()]) 3371 # print("picked Actor2D:", [picker.GetActor2D()]) 3372 # print("picked Assembly:", [picker.GetAssembly()]) 3373 # print("picked Prop3D:", [picker.GetProp3D()]) 3374 3375 # check if any button objects are clicked 3376 clicked_actor2D = picker.GetActor2D() 3377 if clicked_actor2D: 3378 for bt in self.buttons: 3379 if clicked_actor2D == bt.actor: 3380 bt.function() 3381 break 3382 3383 if not clicked_actor: 3384 clicked_actor = picker.GetAssembly() 3385 3386 if not clicked_actor: 3387 clicked_actor = picker.GetProp3D() 3388 3389 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3390 return 3391 3392 self.picked3d = picker.GetPickPosition() 3393 self.picked2d = np.array([x, y]) 3394 3395 if not clicked_actor: 3396 return 3397 3398 self.justremoved = None 3399 3400 self.clicked_actor = clicked_actor 3401 if hasattr(clicked_actor, "picked3d"): # might be not a vedo obj 3402 clicked_actor.picked3d = picker.GetPickPosition() 3403 x, y = iren.GetEventPosition() 3404 3405 # ----------- 3406 if "Histogram1D" in str(type(picker.GetAssembly())): 3407 histo = picker.GetAssembly() 3408 x = self.picked3d[0] 3409 idx = np.digitize(x, histo.edges) - 1 3410 f = histo.frequencies[idx] 3411 cn = histo.centers[idx] 3412 vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3413 3414 3415 ####################################################################### 3416 def _keypress(self, iren, event): 3417 3418 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3419 3420 key = iren.GetKeySym() 3421 3422 if "_L" in key or "_R" in key: 3423 return 3424 3425 if iren.GetShiftKey(): 3426 key = key.upper() 3427 3428 if iren.GetControlKey(): 3429 key = "Ctrl+" + key 3430 3431 if iren.GetAltKey(): 3432 key = "Alt+" + key 3433 3434 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3435 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3436 # iren.GetKeyCode(), iren.GetRepeatCount()) 3437 # iren.ExitCallback() 3438 # return 3439 3440 x, y = iren.GetEventPosition() 3441 renderer = iren.FindPokedRenderer(x, y) 3442 3443 if key in ["q", "Ctrl+q", "Ctrl+w", "space", "Return", "Escape"]: 3444 iren.ExitCallback() 3445 return 3446 3447 elif key == "F1": 3448 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3449 iren.ExitCallback() 3450 sys.exit(0) 3451 3452 elif key == "Down": 3453 if self.clicked_actor in self.get_meshes(): 3454 self.clicked_actor.GetProperty().SetOpacity(0.02) 3455 bfp = self.clicked_actor.GetBackfaceProperty() 3456 if bfp and hasattr(self.clicked_actor, "_bfprop"): 3457 self.clicked_actor._bfprop = bfp # save it 3458 self.clicked_actor.SetBackfaceProperty(None) 3459 else: 3460 for a in self.get_meshes(): 3461 if a.GetPickable(): 3462 a.GetProperty().SetOpacity(0.02) 3463 bfp = a.GetBackfaceProperty() 3464 if bfp and hasattr(a, "_bfprop"): 3465 a._bfprop = bfp 3466 a.SetBackfaceProperty(None) 3467 3468 elif key == "Left": 3469 if self.clicked_actor in self.get_meshes(): 3470 ap = self.clicked_actor.GetProperty() 3471 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3472 ap.SetOpacity(aal) 3473 bfp = self.clicked_actor.GetBackfaceProperty() 3474 if bfp and hasattr(self.clicked_actor, "_bfprop"): 3475 self.clicked_actor._bfprop = bfp 3476 self.clicked_actor.SetBackfaceProperty(None) 3477 else: 3478 for a in self.get_meshes(): 3479 if a.GetPickable(): 3480 ap = a.GetProperty() 3481 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3482 ap.SetOpacity(aal) 3483 bfp = a.GetBackfaceProperty() 3484 if bfp and hasattr(a, "_bfprop"): 3485 a._bfprop = bfp 3486 a.SetBackfaceProperty(None) 3487 3488 elif key == "Right": 3489 if self.clicked_actor in self.get_meshes(): 3490 ap = self.clicked_actor.GetProperty() 3491 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3492 ap.SetOpacity(aal) 3493 if aal == 1 and hasattr(self.clicked_actor, "_bfprop") \ 3494 and self.clicked_actor._bfprop: 3495 # put back 3496 self.clicked_actor.SetBackfaceProperty(self.clicked_actor._bfprop) 3497 else: 3498 for a in self.get_meshes(): 3499 if a.GetPickable(): 3500 ap = a.GetProperty() 3501 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3502 ap.SetOpacity(aal) 3503 if aal == 1 and hasattr(a, "_bfprop") and a._bfprop: 3504 a.SetBackfaceProperty(a._bfprop) 3505 3506 elif key in ('slash', 'Up'): 3507 if self.clicked_actor in self.get_meshes(): 3508 self.clicked_actor.GetProperty().SetOpacity(1) 3509 if hasattr(self.clicked_actor, "_bfprop") and self.clicked_actor._bfprop: 3510 self.clicked_actor.SetBackfaceProperty(self.clicked_actor._bfprop) 3511 else: 3512 for a in self.get_meshes(): 3513 if a.GetPickable(): 3514 a.GetProperty().SetOpacity(1) 3515 if hasattr(a, "_bfprop") and a._bfprop: 3516 a.SetBackfaceProperty(a._bfprop) 3517 3518 elif key == "P": 3519 if self.clicked_actor in self.get_meshes(): 3520 acts = [self.clicked_actor] 3521 else: 3522 acts = self.get_meshes() 3523 for ia in acts: 3524 if ia.GetPickable(): 3525 try: 3526 ps = ia.GetProperty().GetPointSize() 3527 if ps > 1: 3528 ia.GetProperty().SetPointSize(ps - 1) 3529 ia.GetProperty().SetRepresentationToPoints() 3530 except AttributeError: 3531 pass 3532 3533 elif key == "u": 3534 pval = renderer.GetActiveCamera().GetParallelProjection() 3535 renderer.GetActiveCamera().SetParallelProjection(not pval) 3536 if pval: 3537 renderer.ResetCamera() 3538 3539 elif key == "p": 3540 if self.clicked_actor in self.get_meshes(): 3541 acts = [self.clicked_actor] 3542 else: 3543 acts = self.get_meshes() 3544 for ia in acts: 3545 if ia.GetPickable(): 3546 try: 3547 ps = ia.GetProperty().GetPointSize() 3548 ia.GetProperty().SetPointSize(ps + 2) 3549 ia.GetProperty().SetRepresentationToPoints() 3550 except AttributeError: 3551 pass 3552 3553 elif key == "w": 3554 if self.clicked_actor and self.clicked_actor in self.get_meshes(): 3555 self.clicked_actor.GetProperty().SetRepresentationToWireframe() 3556 else: 3557 for a in self.get_meshes(): 3558 if a and a.GetPickable(): 3559 if a.GetProperty().GetRepresentation() == 1: # toggle 3560 a.GetProperty().SetRepresentationToSurface() 3561 else: 3562 a.GetProperty().SetRepresentationToWireframe() 3563 3564 elif key == "r": 3565 renderer.ResetCamera() 3566 3567 elif key == "h": 3568 msg = ( 3569 " ============================================================\n" 3570 " | Press: i print info about selected object |\n" 3571 " | I print the RGB color under the mouse |\n" 3572 " | y show the pipeline for this object as a graph |\n" 3573 " | <--> use arrows to reduce/increase opacity |\n" 3574 " | w/s toggle wireframe/surface style |\n" 3575 " | p/P change point size of vertices |\n" 3576 " | l toggle edges visibility |\n" 3577 " | x toggle mesh visibility |\n" 3578 " | X invoke a cutter widget tool |\n" 3579 " | 1-3 change mesh color |\n" 3580 " | 4 use data array as colors, if present |\n" 3581 " | 5-6 change background color(s) |\n" 3582 " | 09+- (on keypad) or +/- to cycle axes style |\n" 3583 " | k cycle available lighting styles |\n" 3584 " | K cycle available shading styles |\n" 3585 " | A toggle anti-aliasing |\n" 3586 " | D toggle depth-peeling (for transparencies) |\n" 3587 " | o/O add/remove light to scene and rotate it |\n" 3588 " | n show surface mesh normals |\n" 3589 " | a toggle interaction to Actor Mode |\n" 3590 " | j toggle interaction to Joystick Mode |\n" 3591 " | u toggle perspective/parallel projection |\n" 3592 " | r reset camera position |\n" 3593 " | R reset camera orientation to orthogonal view |\n" 3594 " | . fly camera towards last clicked point |\n" 3595 " | C print current camera settings |\n" 3596 " | S save a screenshot |\n" 3597 " | E/F export 3D scene to numpy file or X3D |\n" 3598 " | q return control to python script |\n" 3599 " | Esc abort execution and exit python kernel |\n" 3600 " |------------------------------------------------------------|\n" 3601 " | Mouse: Left-click rotate scene / pick actors |\n" 3602 " | Middle-click pan scene |\n" 3603 " | Right-click zoom scene in or out |\n" 3604 " | Cntrl-click rotate scene |\n" 3605 " |------------------------------------------------------------|\n" 3606 " | Check out the documentation at: https://vedo.embl.es |\n" 3607 " ============================================================" 3608 ) 3609 vedo.printc(msg, dim=True) 3610 3611 msg = " vedo " + vedo.__version__ + " " 3612 vedo.printc(msg, invert=True, dim=True, end="") 3613 vtkVers = vtk.vtkVersion().GetVTKVersion() 3614 msg = "| vtk " + str(vtkVers) 3615 msg += " | numpy " + str(np.__version__) 3616 msg += " | python " + str(sys.version_info[0]) + "." + str(sys.version_info[1]) 3617 vedo.printc(msg, invert=False, dim=True) 3618 return 3619 3620 elif key == "a": 3621 iren.ExitCallback() 3622 cur = iren.GetInteractorStyle() 3623 if isinstance(cur, vtk.vtkInteractorStyleTrackballCamera): 3624 msg = "\nInteractor style changed to TrackballActor\n" 3625 msg += " you can now move and rotate individual meshes:\n" 3626 msg += " press X twice to save the repositioned mesh\n" 3627 msg += " press 'a' to go back to normal style" 3628 vedo.printc(msg) 3629 iren.SetInteractorStyle(vtk.vtkInteractorStyleTrackballActor()) 3630 else: 3631 iren.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera()) 3632 iren.Start() 3633 if vedo.vtk_version == (9,2,2): iren.GetRenderWindow().SetDisplayId("_0_p_void") ##HACK 3634 return 3635 3636 elif key == "A": # toggle antialiasing 3637 msam = self.window.GetMultiSamples() 3638 if not msam: 3639 self.window.SetMultiSamples(8) 3640 else: 3641 self.window.SetMultiSamples(0) 3642 msam = self.window.GetMultiSamples() 3643 if msam: 3644 vedo.printc(f"Antialiasing is now set to {msam} samples", c=bool(msam)) 3645 else: 3646 vedo.printc("Antialiasing is now disabled", c=bool(msam)) 3647 3648 elif key == "D": # toggle depthpeeling 3649 udp = not renderer.GetUseDepthPeeling() 3650 renderer.SetUseDepthPeeling(udp) 3651 # self.renderer.SetUseDepthPeelingForVolumes(udp) 3652 # print(self.window.GetAlphaBitPlanes()) 3653 if udp: 3654 self.window.SetAlphaBitPlanes(1) 3655 renderer.SetMaximumNumberOfPeels(settings.max_number_of_peels) 3656 renderer.SetOcclusionRatio(settings.occlusion_ratio) 3657 self.interactor.Render() 3658 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 3659 rnr = self.renderers.index(renderer) 3660 vedo.printc(f'Depth peeling is now set to {udp} for renderer nr.{rnr}', c=udp) 3661 if not wasUsed and udp: 3662 vedo.printc('\t...but last rendering did not actually used it!', c=udp, invert=True) 3663 return 3664 3665 elif key == "j": 3666 iren.ExitCallback() 3667 cur = iren.GetInteractorStyle() 3668 if isinstance(cur, vtk.vtkInteractorStyleJoystickCamera): 3669 iren.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera()) 3670 else: 3671 vedo.printc("\nInteractor style changed to Joystick,", end="") 3672 vedo.printc(" press j to go back to normal.") 3673 iren.SetInteractorStyle(vtk.vtkInteractorStyleJoystickCamera()) 3674 iren.Start() 3675 if vedo.vtk_version == (9,2,2): iren.GetRenderWindow().SetDisplayId("_0_p_void") ##HACK 3676 return 3677 3678 elif key == "period": 3679 if self.picked3d: 3680 self.fly_to(self.picked3d) 3681 return 3682 3683 elif key == "S": 3684 vedo.io.screenshot("screenshot.png") 3685 vedo.printc(r"\camera Saved rendering window as screenshot.png", c="blue") 3686 return 3687 3688 elif key == "C": 3689 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 3690 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 3691 # This could happen for plotting geological models with UTM coordinate systems 3692 cam = renderer.GetActiveCamera() 3693 vedo.printc('\n###################################################', c='y') 3694 vedo.printc('## Template python code to position this camera: ##', c='y') 3695 vedo.printc('cam = dict(', c='y') 3696 vedo.printc(' position=' +utils.precision(cam.GetPosition(),6)+',', c='y') 3697 vedo.printc(' focal_point=' +utils.precision(cam.GetFocalPoint(),6)+',', c='y') 3698 vedo.printc(' viewup=' +utils.precision(cam.GetViewUp(),6)+',', c='y') 3699 if settings.use_parallel_projection: 3700 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 3701 else: 3702 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 3703 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 3704 vedo.printc(')', c='y') 3705 vedo.printc('show(mymeshes, camera=cam)', c='y') 3706 vedo.printc('###################################################', c='y') 3707 return 3708 3709 elif key == "R": 3710 self.reset_viewup() 3711 3712 elif key == "s": 3713 if self.clicked_actor and self.clicked_actor in self.get_meshes(): 3714 self.clicked_actor.GetProperty().SetRepresentationToSurface() 3715 else: 3716 for a in self.get_meshes(): 3717 if a and a.GetPickable(): 3718 a.GetProperty().SetRepresentationToSurface() 3719 3720 elif key == "1": 3721 self._icol += 1 3722 if isinstance(self.clicked_actor, vedo.Points): 3723 self.clicked_actor.GetMapper().ScalarVisibilityOff() 3724 pal = vedo.colors.palettes[settings.palette % len(vedo.colors.palettes)] 3725 self.clicked_actor.GetProperty().SetColor(pal[(self._icol) % 10]) 3726 3727 elif key == "2": 3728 self._icol += 1 3729 settings.palette += 1 3730 settings.palette = settings.palette % len(vedo.colors.palettes) 3731 if isinstance(self.clicked_actor, vedo.Points): 3732 self.clicked_actor.GetMapper().ScalarVisibilityOff() 3733 pal = vedo.colors.palettes[settings.palette % len(vedo.colors.palettes)] 3734 self.clicked_actor.GetProperty().SetColor(pal[(self._icol) % 10]) 3735 3736 elif key == "3": 3737 bsc = ["b5", "cyan5", "g4", "o5", "p5", "r4", "teal4", "yellow4"] 3738 self._icol += 1 3739 if isinstance(self.clicked_actor, vedo.Points): 3740 self.clicked_actor.GetMapper().ScalarVisibilityOff() 3741 self.clicked_actor.GetProperty().SetColor(vedo.get_color(bsc[(self._icol) % len(bsc)])) 3742 3743 elif key == "4": 3744 if self.clicked_actor: 3745 acts = [self.clicked_actor] 3746 else: 3747 acts = self.get_meshes() 3748 for ia in acts: 3749 if not hasattr(ia, "_cmap_name"): 3750 continue 3751 cmap_name = ia._cmap_name 3752 if not cmap_name: 3753 cmap_name = "rainbow" 3754 if isinstance(ia, vedo.pointcloud.Points): 3755 arnames = ia.pointdata.keys() 3756 if len(arnames): 3757 arnam = arnames[ia._scals_idx] 3758 if arnam and ("normals" not in arnam.lower()): # exclude normals 3759 ia.cmap(cmap_name, arnam, on="points") 3760 vedo.printc("..active point data set to:", arnam, c='g', bold=0) 3761 ia._scals_idx += 1 3762 if ia._scals_idx >= len(arnames): 3763 ia._scals_idx = 0 3764 else: 3765 arnames = ia.celldata.keys() 3766 if len(arnames): 3767 arnam = arnames[ia._scals_idx] 3768 if arnam and ("normals" not in arnam.lower()): # exclude normals 3769 ia.cmap(cmap_name, arnam, on="cells") 3770 vedo.printc("..active cell array set to:", arnam, c='g', bold=0) 3771 ia._scals_idx += 1 3772 if ia._scals_idx >= len(arnames): 3773 ia._scals_idx = 0 3774 3775 elif key == "5": 3776 bgc = np.array(renderer.GetBackground()).sum() / 3 3777 if bgc <= 0: 3778 bgc = 0.223 3779 elif 0 < bgc < 1: 3780 bgc = 1 3781 else: 3782 bgc = 0 3783 renderer.SetBackground(bgc, bgc, bgc) 3784 3785 elif key == "6": 3786 bg2cols = [ 3787 "lightyellow", 3788 "darkseagreen", 3789 "palegreen", 3790 "steelblue", 3791 "lightblue", 3792 "cadetblue", 3793 "lavender", 3794 "white", 3795 "blackboard", 3796 "black", 3797 ] 3798 bg2name = vedo.get_color_name(renderer.GetBackground2()) 3799 if bg2name in bg2cols: 3800 idx = bg2cols.index(bg2name) 3801 else: 3802 idx = 4 3803 if idx is not None: 3804 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 3805 if not bg2name_next: 3806 renderer.GradientBackgroundOff() 3807 else: 3808 renderer.GradientBackgroundOn() 3809 renderer.SetBackground2(vedo.get_color(bg2name_next)) 3810 3811 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 3812 clickedr = self.renderers.index(renderer) 3813 if self.axes_instances[clickedr]: 3814 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 3815 self.axes_instances[clickedr].EnabledOff() 3816 else: 3817 try: 3818 renderer.RemoveActor(self.axes_instances[clickedr]) 3819 except: 3820 pass 3821 self.axes_instances[clickedr] = None 3822 if not self.axes: 3823 self.axes = 0 3824 if isinstance(self.axes, dict): 3825 self.axes = 1 3826 if key in ["minus", "KP_Subtract"]: 3827 if not settings.use_parallel_projection and self.axes == 0: 3828 self.axes -= 1 # jump ruler doesnt make sense in perspective mode 3829 addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None) 3830 else: 3831 if not settings.use_parallel_projection and self.axes == 12: 3832 self.axes += 1 # jump ruler doesnt make sense in perspective mode 3833 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None) 3834 self.interactor.Render() 3835 3836 elif "KP_" in key or key in ["Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior"]: 3837 # change axes style 3838 asso = { 3839 "KP_Insert":0, "KP_0":0, 3840 "KP_End":1, "KP_1":1, 3841 "KP_Down":2, "KP_2":2, 3842 "KP_Next":3, "KP_3":3, 3843 "KP_Left":4, "KP_4":4, 3844 "KP_Begin":5, "KP_5":5, 3845 "KP_Right":6, "KP_6":6, 3846 "KP_Home":7, "KP_7":7, 3847 "KP_Up":8, "KP_8":8, 3848 "Prior":9, # on windows OS 3849 "Insert":0, 3850 "End":1, 3851 "Down":2, 3852 "Next":3, 3853 "Left":4, 3854 "Begin":5, 3855 "Right":6, 3856 "Home":7, 3857 "Up":8, 3858 "Prior":9, 3859 } 3860 clickedr = self.renderers.index(renderer) 3861 if key in asso: 3862 if self.axes_instances[clickedr]: 3863 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 3864 self.axes_instances[clickedr].EnabledOff() 3865 else: 3866 try: 3867 renderer.RemoveActor(self.axes_instances[clickedr]) 3868 except: 3869 pass 3870 self.axes_instances[clickedr] = None 3871 addons.add_global_axes(axtype=asso[key], c=None) 3872 self.interactor.Render() 3873 3874 if key == "O": 3875 renderer.RemoveLight(self._extralight) 3876 self._extralight = None 3877 3878 elif key == "o": 3879 vbb, sizes, _, _ = addons.compute_visible_bounds() 3880 cm = utils.vector( 3881 (vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2 3882 ) 3883 if not self._extralight: 3884 vup = renderer.GetActiveCamera().GetViewUp() 3885 pos = cm + utils.vector(vup) * utils.mag(sizes) 3886 self._extralight = addons.Light(pos, focal_point=cm) 3887 renderer.AddLight(self._extralight) 3888 print("Press again o to rotate light source, or O to remove it.") 3889 else: 3890 cpos = utils.vector(self._extralight.GetPosition()) 3891 x, y, z = self._extralight.GetPosition() - cm 3892 r, th, ph = utils.cart2spher(x, y, z) 3893 th += 0.2 3894 if th > np.pi: 3895 th = np.random.random() * np.pi / 2 3896 ph += 0.3 3897 cpos = utils.spher2cart(r, th, ph) + cm 3898 self._extralight.SetPosition(cpos) 3899 3900 self.window.Render() 3901 3902 elif key == "l": 3903 if self.clicked_actor in self.get_meshes(): 3904 acts = [self.clicked_actor] 3905 else: 3906 acts = self.get_meshes() 3907 for ia in acts: 3908 if not ia.GetPickable(): 3909 continue 3910 try: 3911 ev = ia.GetProperty().GetEdgeVisibility() 3912 ia.GetProperty().SetEdgeVisibility(not ev) 3913 ia.GetProperty().SetRepresentationToSurface() 3914 ia.GetProperty().SetLineWidth(0.1) 3915 except AttributeError: 3916 pass 3917 3918 elif key == "k": # lightings 3919 if self.clicked_actor in self.get_meshes(): 3920 acts = [self.clicked_actor] 3921 else: 3922 acts = self.get_meshes() 3923 shds = ('default', 3924 'metallic', 3925 'plastic', 3926 'shiny', 3927 'glossy', 3928 'off') 3929 for ia in acts: 3930 if ia.GetPickable(): 3931 try: 3932 lnr = (ia._ligthingnr + 1) % 6 3933 ia.lighting(shds[lnr]) 3934 ia._ligthingnr = lnr 3935 # vedo.printc('-> lighting set to:', shds[lnr], c='g', bold=0) 3936 except AttributeError: 3937 pass 3938 3939 elif key == "K": # shading 3940 if self.clicked_actor in self.get_meshes(): 3941 acts = [self.clicked_actor] 3942 else: 3943 acts = self.get_meshes() 3944 for ia in acts: 3945 if ia.GetPickable() and isinstance(ia, vedo.Mesh): 3946 ia.compute_normals(cells=False) 3947 intrp = ia.GetProperty().GetInterpolation() 3948 # print(intrp, ia.GetProperty().GetInterpolationAsString()) 3949 if intrp > 0: 3950 ia.GetProperty().SetInterpolation(0) # flat 3951 else: 3952 ia.GetProperty().SetInterpolation(2) # phong 3953 3954 elif key == "n": # show normals to an actor 3955 if self.clicked_actor in self.get_meshes(): 3956 if self.clicked_actor.GetPickable(): 3957 self.renderer.AddActor(vedo.shapes.NormalLines(self.clicked_actor)) 3958 iren.Render() 3959 else: 3960 print("Click an actor and press n to add normals.") 3961 3962 elif key == "x": 3963 if self.justremoved is None: 3964 if self.clicked_actor in self.get_meshes() or isinstance( 3965 self.clicked_actor, vtk.vtkAssembly 3966 ): 3967 self.justremoved = self.clicked_actor 3968 self.renderer.RemoveActor(self.clicked_actor) 3969 else: 3970 self.renderer.AddActor(self.justremoved) 3971 self.renderer.Render() 3972 self.justremoved = None 3973 3974 elif key == "X": 3975 if self.clicked_actor: 3976 if not self.cutter_widget: 3977 addons.add_cutter_tool(self.clicked_actor) 3978 else: 3979 if isinstance(self.clicked_actor, vtk.vtkActor): 3980 fname = "clipped.vtk" 3981 w = vtk.vtkPolyDataWriter() 3982 w.SetInputData(self.clicked_actor.polydata()) 3983 w.SetFileName(fname) 3984 w.Write() 3985 vedo.printc(r"\save Saved file:", fname, c="m") 3986 self.cutter_widget.Off() 3987 self.cutter_widget = None 3988 else: 3989 for a in self.actors: 3990 if isinstance(a, vtk.vtkVolume): 3991 addons.add_cutter_tool(a) 3992 return 3993 3994 vedo.printc("Click object and press X to open the cutter box widget.", c=4) 3995 3996 elif key == "E": 3997 vedo.printc(r"\camera Exporting 3D window to file", c="blue", end="") 3998 vedo.io.export_window("scene.npz") 3999 vedo.printc(". Try:\n> vedo scene.npz", c="blue") 4000 4001 elif key == "F": 4002 vedo.io.export_window("scene.x3d") 4003 vedo.printc("Try: firefox scene.html", c="blue") 4004 4005 elif key == "i": # print info 4006 if self.clicked_actor: 4007 utils.print_info(self.clicked_actor) 4008 else: 4009 utils.print_info(self) 4010 4011 elif key == "I": # print color under the mouse 4012 x, y = iren.GetEventPosition() 4013 self.color_picker([x, y], verbose=True) 4014 4015 elif key == "y": 4016 if self.clicked_actor and self.clicked_actor.pipeline: 4017 # self.clicked_actor.pipeline = utils.OperationNode( 4018 # "show", parents=[self.clicked_actor], 4019 # shape="circle", 4020 # ) 4021 self.clicked_actor.pipeline.show() 4022 4023 if iren: 4024 iren.Render()
class
Plotter:
324class Plotter: 325 """Main class to manage actors.""" 326 def __init__( 327 self, 328 shape=(1, 1), 329 N=None, 330 pos=(0, 0), 331 size="auto", 332 screensize="auto", 333 title="vedo", 334 bg="white", 335 bg2=None, 336 axes=None, 337 sharecam=True, 338 resetcam=True, 339 interactive=None, 340 offscreen=False, 341 qt_widget=None, 342 wx_widget=None, 343 backend="", # DEPRECATED 344 ): 345 """ 346 Arguments: 347 shape : (str, list) 348 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 349 N : (int) 350 number of desired renderers arranged in a grid automatically. 351 pos : (list) 352 (x,y) position in pixels of top-left corner of the rendering window on the screen 353 size : (str, list) 354 size of the rendering window. If 'auto', guess it based on screensize. 355 screensize : (list) 356 physical size of the monitor screen in pixels 357 bg : (color, str) 358 background color or specify jpg image file name with path 359 bg2 : (color) 360 background color of a gradient towards the top 361 axes : (int) 362 363 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 364 Check out `vedo.addons.Axes()` for the available options. 365 - 0, no axes 366 - 1, draw three gray grid walls 367 - 2, show cartesian axes from (0,0,0) 368 - 3, show positive range of cartesian axes from (0,0,0) 369 - 4, show a triad at bottom left 370 - 5, show a cube at bottom left 371 - 6, mark the corners of the bounding box 372 - 7, draw a 3D ruler at each side of the cartesian axes 373 - 8, show the VTK CubeAxesActor object 374 - 9, show the bounding box outLine, 375 - 10, show three circles representing the maximum bounding box, 376 - 11, show a large grid on the x-y plane (use with zoom=8) 377 - 12, show polar axes. 378 - 13, draw a simple ruler at the bottom of the window 379 380 sharecam : (bool) 381 if False each renderer will have an independent vtkCamera 382 interactive : (bool) 383 if True will stop after show() to allow interaction w/ window 384 offscreen : (bool) 385 if True will not show the rendering window 386 qt_widget : (QVTKRenderWindowInteractor) 387 render in a Qt-Widget using an QVTKRenderWindowInteractor. 388 Overrides offscreen to True. 389 Overrides interactive to False. 390 See examples `qt_windows1.py` and `qt_windows2.py` 391 """ 392 393 if backend != "": 394 vedo.logger.error(f"obsolete 'backend' keyword, use settings.default_backend = '{backend}'") 395 settings.default_backend = backend 396 397 vedo.plotter_instance = self 398 399 if qt_widget is not None: 400 # overrides the interactive and offscreen properties 401 interactive = False 402 offscreen = True 403 404 if wx_widget is not None: 405 # overrides the interactive property 406 interactive = False 407 408 if interactive is None: 409 if N == 1: 410 interactive = True 411 elif N or shape != (1, 1): 412 interactive = False 413 else: 414 interactive = True 415 416 self.actors = [] # list of actors to be shown 417 self.clicked_actor = None # holds the actor that has been clicked 418 self.renderer = None # current renderer 419 self.renderers = [] # list of renderers 420 self.shape = shape # don't remove this line 421 self._interactive = interactive # allows to interact with renderer 422 self.axes = axes # show axes type nr. 423 self.title = title # window title 424 self.sharecam = sharecam # share the same camera if multiple renderers 425 self.picker = None # the vtkPicker object 426 self.picked2d = None # 2d coords of a clicked point on the rendering window 427 self.picked3d = None # 3d coords of a clicked point on an actor 428 self.offscreen = offscreen 429 self.resetcam = resetcam 430 self.last_event = None 431 432 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 433 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 434 435 self.skybox = None 436 437 # mostly internal stuff: 438 self.hover_legends = [] 439 self.backgrcol = bg 440 self.pos = pos # used by vedo.io 441 self.justremoved = None 442 self.axes_instances = [] 443 self.clock = 0 444 self.sliders = [] 445 self.buttons = [] 446 self.widgets = [] 447 self.cutter_widget = None 448 self.hint_widget = None 449 self.background_renderer = None 450 self.size = size 451 self.interactor = None 452 self.camera = None 453 454 self._icol = 0 455 self._clockt0 = time.time() 456 self._first_viewup = True 457 self._extralight = None 458 self._cocoa_initialized = False 459 460 ##################################################################### 461 if settings.default_backend != 'vtk': 462 if settings.default_backend == "2d": 463 self.offscreen = True 464 if self.size == "auto": 465 self.size = (800, 600) 466 467 elif settings.default_backend == "k3d": 468 self._interactive = False 469 self.interactor = None 470 self.window = None 471 self.camera = None # let the backend choose 472 if self.size == "auto": 473 self.size = (1000, 1000) 474 ############################################################# 475 return ###################################################### 476 ############################################################# 477 ##################################################################### 478 479 # build the rendering window: 480 self.window = vtk.vtkRenderWindow() 481 482 self.window.GlobalWarningDisplayOff() 483 self.window.SetWindowName(self.title) 484 485 # more settings 486 if settings.use_depth_peeling: 487 self.window.SetAlphaBitPlanes(settings.alpha_bit_planes) 488 self.window.SetMultiSamples(settings.multi_samples) 489 490 self.window.SetPolygonSmoothing(settings.polygon_smoothing) 491 self.window.SetLineSmoothing(settings.line_smoothing) 492 self.window.SetPointSmoothing(settings.point_smoothing) 493 494 # sort out screen size 495 if screensize == "auto": 496 screensize = (2160, 1440) # might go wrong, use a default 1.5 ratio 497 498 ### BUG in GetScreenSize in VTK 9.1.0 499 ### https://discourse.vtk.org/t/vtk9-1-0-problems/7094/3 500 if settings.hack_call_screen_size: # True 501 502 vtkvers = vedo.vtk_version 503 if not self.offscreen and (vtkvers[0]<9 or vtkvers[0]==9 and vtkvers[1]==0): 504 aus = self.window.GetScreenSize() 505 if aus and len(aus) == 2 and aus[0] > 100 and aus[1] > 100: # seems ok 506 if aus[0] / aus[1] > 2: # looks like there are 2 or more screens 507 screensize = (int(aus[0] / 2), aus[1]) 508 else: 509 screensize = aus 510 511 x, y = screensize 512 513 if N: # N = number of renderers. Find out the best 514 515 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 516 vedo.logger.warning("having set N, shape is ignored.") 517 518 nx = int(np.sqrt(int(N * y / x) + 1)) 519 ny = int(np.sqrt(int(N * x / y) + 1)) 520 lm = [ 521 (nx, ny), 522 (nx, ny + 1), 523 (nx - 1, ny), 524 (nx + 1, ny), 525 (nx, ny - 1), 526 (nx - 1, ny + 1), 527 (nx + 1, ny - 1), 528 (nx + 1, ny + 1), 529 (nx - 1, ny - 1), 530 ] 531 ind, minl = 0, 1000 532 for i, m in enumerate(lm): 533 l = m[0] * m[1] 534 if N <= l < minl: 535 ind = i 536 minl = l 537 shape = lm[ind] 538 539 ################################################## 540 if isinstance(shape, str): 541 542 if "|" in shape: 543 if self.size == "auto": 544 self.size = (800, 1200) 545 n = int(shape.split("|")[0]) 546 m = int(shape.split("|")[1]) 547 rangen = reversed(range(n)) 548 rangem = reversed(range(m)) 549 else: 550 if self.size == "auto": 551 self.size = (1200, 800) 552 m = int(shape.split("/")[0]) 553 n = int(shape.split("/")[1]) 554 rangen = range(n) 555 rangem = range(m) 556 557 if n >= m: 558 xsplit = m / (n + m) 559 else: 560 xsplit = 1 - n / (n + m) 561 if settings.window_splitting_position: 562 xsplit = settings.window_splitting_position 563 564 for i in rangen: 565 arenderer = vtk.vtkRenderer() 566 if "|" in shape: 567 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 568 else: 569 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 570 self.renderers.append(arenderer) 571 572 for i in rangem: 573 arenderer = vtk.vtkRenderer() 574 575 if "|" in shape: 576 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 577 else: 578 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 579 self.renderers.append(arenderer) 580 581 for r in self.renderers: 582 r.SetUseHiddenLineRemoval(settings.hidden_line_removal) 583 r.SetLightFollowCamera(settings.light_follows_camera) 584 585 r.SetUseDepthPeeling(settings.use_depth_peeling) 586 # r.SetUseDepthPeelingForVolumes(settings.use_depth_peeling) 587 if settings.use_depth_peeling: 588 r.SetMaximumNumberOfPeels(settings.max_number_of_peels) 589 r.SetOcclusionRatio(settings.occlusion_ratio) 590 r.SetUseFXAA(settings.use_fxaa) 591 r.SetPreserveDepthBuffer(settings.preserve_depth_buffer) 592 593 r.SetBackground(vedo.get_color(self.backgrcol)) 594 595 self.axes_instances.append(None) 596 597 self.shape = (n + m,) 598 599 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 600 # passing a sequence of dicts for renderers specifications 601 602 if self.size == "auto": 603 self.size = (1200, 900) 604 605 for rd in shape: 606 x0, y0 = rd["bottomleft"] 607 x1, y1 = rd["topright"] 608 bg_ = rd.pop("bg", "white") 609 bg2_ = rd.pop("bg2", None) 610 611 arenderer = vtk.vtkRenderer() 612 arenderer.SetUseHiddenLineRemoval(settings.hidden_line_removal) 613 arenderer.SetLightFollowCamera(settings.light_follows_camera) 614 615 arenderer.SetUseDepthPeeling(settings.use_depth_peeling) 616 # arenderer.SetUseDepthPeelingForVolumes(settings.use_depth_peeling) 617 if settings.use_depth_peeling: 618 arenderer.SetMaximumNumberOfPeels(settings.max_number_of_peels) 619 arenderer.SetOcclusionRatio(settings.occlusion_ratio) 620 arenderer.SetUseFXAA(settings.use_fxaa) 621 arenderer.SetPreserveDepthBuffer(settings.preserve_depth_buffer) 622 623 arenderer.SetViewport(x0, y0, x1, y1) 624 arenderer.SetBackground(vedo.get_color(bg_)) 625 if bg2_: 626 arenderer.GradientBackgroundOn() 627 arenderer.SetBackground2(vedo.get_color(bg2_)) 628 629 self.renderers.append(arenderer) 630 self.axes_instances.append(None) 631 632 self.shape = (len(shape),) 633 634 else: 635 636 if isinstance(self.size, str) and self.size == "auto": 637 # figure out a reasonable window size 638 f = 1.5 639 xs = y / f * shape[1] # because y<x 640 ys = y / f * shape[0] 641 if xs > x / f: # shrink 642 xs = x / f 643 ys = xs / shape[1] * shape[0] 644 if ys > y / f: 645 ys = y / f 646 xs = ys / shape[0] * shape[1] 647 self.size = (int(xs), int(ys)) 648 if shape == (1, 1): 649 self.size = (int(y / f), int(y / f)) # because y<x 650 else: 651 self.size = (self.size[0], self.size[1]) 652 653 image_actor = None 654 bgname = str(self.backgrcol).lower() 655 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 656 self.window.SetNumberOfLayers(2) 657 self.background_renderer = vtk.vtkRenderer() 658 self.background_renderer.SetLayer(0) 659 self.background_renderer.InteractiveOff() 660 self.background_renderer.SetBackground(vedo.get_color(bg2)) 661 image_actor = vedo.Picture(self.backgrcol) 662 self.window.AddRenderer(self.background_renderer) 663 self.background_renderer.AddActor(image_actor) 664 665 for i in reversed(range(shape[0])): 666 for j in range(shape[1]): 667 arenderer = vtk.vtkRenderer() 668 arenderer.SetUseHiddenLineRemoval(settings.hidden_line_removal) 669 arenderer.SetLightFollowCamera(settings.light_follows_camera) 670 arenderer.SetTwoSidedLighting(settings.two_sided_lighting) 671 672 arenderer.SetUseDepthPeeling(settings.use_depth_peeling) 673 # arenderer.SetUseDepthPeelingForVolumes(settings.use_depth_peeling) 674 if settings.use_depth_peeling: 675 arenderer.SetMaximumNumberOfPeels(settings.max_number_of_peels) 676 arenderer.SetOcclusionRatio(settings.occlusion_ratio) 677 arenderer.SetUseFXAA(settings.use_fxaa) 678 arenderer.SetPreserveDepthBuffer(settings.preserve_depth_buffer) 679 680 if image_actor: 681 arenderer.SetLayer(1) 682 683 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 684 if bg2: 685 arenderer.GradientBackgroundOn() 686 arenderer.SetBackground2(vedo.get_color(bg2)) 687