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