vedo.plotter
This module defines the main class Plotter to manage objects and 3D rendering.
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3import os.path 4import sys 5import time 6from typing import MutableSequence, Callable, Any, Union 7import numpy as np 8 9import vedo.vtkclasses as vtki # a wrapper for lazy imports 10 11import vedo 12from vedo import transformations 13from vedo import utils 14from vedo import backends 15from vedo import addons 16 17 18__docformat__ = "google" 19 20__doc__ = """ 21This module defines the main class Plotter to manage objects and 3D rendering. 22 23![](https://vedo.embl.es/images/basic/multirenderers.png) 24""" 25 26__all__ = ["Plotter", "show", "close"] 27 28######################################################################################## 29class Event: 30 """ 31 This class holds the info from an event in the window, works as dictionary too. 32 """ 33 34 __slots__ = [ 35 "name", 36 "title", 37 "id", 38 "timerid", 39 "time", 40 "priority", 41 "at", 42 "object", 43 "actor", 44 "picked3d", 45 "keypress", 46 "picked2d", 47 "delta2d", 48 "angle2d", 49 "speed2d", 50 "delta3d", 51 "speed3d", 52 "isPoints", 53 "isMesh", 54 "isAssembly", 55 "isVolume", 56 "isImage", 57 "isActor2D", 58 ] 59 60 def __init__(self): 61 self.name = "event" 62 self.title = "" 63 self.id = 0 64 self.timerid = 0 65 self.time = 0 66 self.priority = 0 67 self.at = 0 68 self.object = None 69 self.actor = None 70 self.picked3d = () 71 self.keypress = "" 72 self.picked2d = () 73 self.delta2d = () 74 self.angle2d = 0 75 self.speed2d = () 76 self.delta3d = () 77 self.speed3d = 0 78 self.isPoints = False 79 self.isMesh = False 80 self.isAssembly = False 81 self.isVolume = False 82 self.isImage = False 83 self.isActor2D = False 84 85 def __getitem__(self, key): 86 return getattr(self, key) 87 88 def __setitem__(self, key, value): 89 setattr(self, key, value) 90 91 def __str__(self): 92 module = self.__class__.__module__ 93 name = self.__class__.__name__ 94 out = vedo.printc( 95 f"{module}.{name} at ({hex(id(self))})".ljust(75), 96 bold=True, invert=True, return_string=True, 97 ) 98 out += "\x1b[0m" 99 for n in self.__slots__: 100 if n == "actor": 101 continue 102 out += f"{n}".ljust(11) + ": " 103 val = str(self[n]).replace("\n", "")[:65].rstrip() 104 if val == "True": 105 out += "\x1b[32;1m" 106 elif val == "False": 107 out += "\x1b[31;1m" 108 out += val + "\x1b[0m\n" 109 return out.rstrip() 110 111 def keys(self): 112 return self.__slots__ 113 114 115############################################################################################## 116def show( 117 *objects, 118 at=None, 119 shape=(1, 1), 120 N=None, 121 pos=(0, 0), 122 size="auto", 123 screensize="auto", 124 title="vedo", 125 bg="white", 126 bg2=None, 127 axes=None, 128 interactive=None, 129 offscreen=False, 130 sharecam=True, 131 resetcam=True, 132 zoom=None, 133 viewup="", 134 azimuth=0.0, 135 elevation=0.0, 136 roll=0.0, 137 camera=None, 138 mode=None, 139 screenshot="", 140 new=False, 141) -> Union["Plotter", None]: 142 """ 143 Create on the fly an instance of class Plotter and show the object(s) provided. 144 145 Arguments: 146 at : (int) 147 number of the renderer to plot to, in case of more than one exists 148 shape : (list, str) 149 Number of sub-render windows inside of the main window. E.g.: 150 specify two across with shape=(2,1) and a two by two grid 151 with shape=(2, 2). By default there is only one renderer. 152 153 Can also accept a shape as string descriptor. E.g.: 154 - shape="3|1" means 3 plots on the left and 1 on the right, 155 - shape="4/2" means 4 plots on top of 2 at bottom. 156 N : (int) 157 number of desired sub-render windows arranged automatically in a grid 158 pos : (list) 159 position coordinates of the top-left corner of the rendering window 160 on the screen 161 size : (list) 162 size of the rendering window 163 screensize : (list) 164 physical size of the monitor screen 165 title : (str) 166 window title 167 bg : (color) 168 background color or specify jpg image file name with path 169 bg2 : (color) 170 background color of a gradient towards the top 171 axes : (int) 172 set the type of axes to be shown: 173 - 0, no axes 174 - 1, draw three gray grid walls 175 - 2, show cartesian axes from (0,0,0) 176 - 3, show positive range of cartesian axes from (0,0,0) 177 - 4, show a triad at bottom left 178 - 5, show a cube at bottom left 179 - 6, mark the corners of the bounding box 180 - 7, draw a 3D ruler at each side of the cartesian axes 181 - 8, show the `vtkCubeAxesActor` object 182 - 9, show the bounding box outLine 183 - 10, show three circles representing the maximum bounding box 184 - 11, show a large grid on the x-y plane 185 - 12, show polar axes 186 - 13, draw a simple ruler at the bottom of the window 187 - 14: draw a `CameraOrientationWidget` 188 189 Axis type-1 can be fully customized by passing a dictionary. 190 Check `vedo.addons.Axes()` for the full list of options. 191 azimuth/elevation/roll : (float) 192 move camera accordingly the specified value 193 viewup : (str, list) 194 either `['x', 'y', 'z']` or a vector to set vertical direction 195 resetcam : (bool) 196 re-adjust camera position to fit objects 197 camera : (dict, vtkCamera) 198 camera parameters can further be specified with a dictionary 199 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 200 - **pos** (list), the position of the camera in world coordinates 201 - **focal_point** (list), the focal point of the camera in world coordinates 202 - **viewup** (list), the view up direction for the camera 203 - **distance** (float), set the focal point to the specified distance from the camera position. 204 - **clipping_range** (float), distance of the near and far clipping planes along the direction of projection. 205 - **parallel_scale** (float), 206 scaling used for a parallel projection, i.e. the height of the viewport 207 in world-coordinate distances. The default is 1. Note that the "scale" parameter works as 208 an "inverse scale", larger numbers produce smaller images. 209 This method has no effect in perspective projection mode. 210 - **thickness** (float), 211 set the distance between clipping planes. This method adjusts the far clipping 212 plane to be set a distance 'thickness' beyond the near clipping plane. 213 - **view_angle** (float), 214 the camera view angle, which is the angular height of the camera view 215 measured in degrees. The default angle is 30 degrees. 216 This method has no effect in parallel projection mode. 217 The formula for setting the angle up for perfect perspective viewing is: 218 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 219 (measured by holding a ruler up to your screen) and d is the distance 220 from your eyes to the screen. 221 interactive : (bool) 222 pause and interact with window (True) or continue execution (False) 223 rate : (float) 224 maximum rate of `show()` in Hertz 225 mode : (int, str) 226 set the type of interaction: 227 - 0 = TrackballCamera [default] 228 - 1 = TrackballActor 229 - 2 = JoystickCamera 230 - 3 = JoystickActor 231 - 4 = Flight 232 - 5 = RubberBand2D 233 - 6 = RubberBand3D 234 - 7 = RubberBandZoom 235 - 8 = Terrain 236 - 9 = Unicam 237 - 10 = Image 238 new : (bool) 239 if set to `True`, a call to show will instantiate 240 a new Plotter object (a new window) instead of reusing the first created. 241 If new is `True`, but the existing plotter was instantiated with a different 242 argument for `offscreen`, `new` is ignored and a new Plotter is created anyway. 243 """ 244 if len(objects) == 0: 245 objects = None 246 elif len(objects) == 1: 247 objects = objects[0] 248 else: 249 objects = utils.flatten(objects) 250 251 # If a plotter instance is already present, check if the offscreen argument 252 # is the same as the one requested by the user. If not, create a new 253 # plotter instance (see https://github.com/marcomusy/vedo/issues/1026) 254 if vedo.plotter_instance and vedo.plotter_instance.offscreen != offscreen: 255 new = True 256 257 if vedo.plotter_instance and not new: # Plotter exists 258 plt = vedo.plotter_instance 259 260 else: # Plotter must be created 261 262 if utils.is_sequence(at): # user passed a sequence for "at" 263 264 if not utils.is_sequence(objects): 265 vedo.logger.error("in show() input must be a list.") 266 raise RuntimeError() 267 if len(at) != len(objects): 268 vedo.logger.error("in show() lists 'input' and 'at' must have equal lengths") 269 raise RuntimeError() 270 if shape == (1, 1) and N is None: 271 N = max(at) + 1 272 273 elif at is None and (N or shape != (1, 1)): 274 275 if not utils.is_sequence(objects): 276 e = "in show(), N or shape is set, but input is not a sequence\n" 277 e += " you may need to specify e.g. at=0" 278 vedo.logger.error(e) 279 raise RuntimeError() 280 at = list(range(len(objects))) 281 282 plt = Plotter( 283 shape=shape, 284 N=N, 285 pos=pos, 286 size=size, 287 screensize=screensize, 288 title=title, 289 axes=axes, 290 sharecam=sharecam, 291 resetcam=resetcam, 292 interactive=interactive, 293 offscreen=offscreen, 294 bg=bg, 295 bg2=bg2, 296 ) 297 298 if vedo.settings.dry_run_mode >= 2: 299 return plt 300 301 # use _plt_to_return because plt.show() can return a k3d plot 302 _plt_to_return = None 303 304 if utils.is_sequence(at): 305 306 for i, act in enumerate(objects): 307 _plt_to_return = plt.show( 308 act, 309 at=i, 310 zoom=zoom, 311 resetcam=resetcam, 312 viewup=viewup, 313 azimuth=azimuth, 314 elevation=elevation, 315 roll=roll, 316 camera=camera, 317 interactive=False, 318 mode=mode, 319 screenshot=screenshot, 320 bg=bg, 321 bg2=bg2, 322 axes=axes, 323 ) 324 325 if ( 326 interactive 327 or len(at) == N 328 or (isinstance(shape[0], int) and len(at) == shape[0] * shape[1]) 329 ): 330 # note that shape can be a string 331 if plt.interactor and not offscreen and (interactive is None or interactive): 332 plt.interactor.Start() 333 if plt._must_close_now: 334 plt.interactor.GetRenderWindow().Finalize() 335 plt.interactor.TerminateApp() 336 plt.interactor = None 337 plt.window = None 338 plt.renderer = None 339 plt.renderers = [] 340 plt.camera = None 341 342 else: 343 344 _plt_to_return = plt.show( 345 objects, 346 at=at, 347 zoom=zoom, 348 resetcam=resetcam, 349 viewup=viewup, 350 azimuth=azimuth, 351 elevation=elevation, 352 roll=roll, 353 camera=camera, 354 interactive=interactive, 355 mode=mode, 356 screenshot=screenshot, 357 bg=bg, 358 bg2=bg2, 359 axes=axes, 360 ) 361 362 return _plt_to_return 363 364 365def close() -> None: 366 """Close the last created Plotter instance if it exists.""" 367 if not vedo.plotter_instance: 368 return 369 vedo.plotter_instance.close() 370 return 371 372 373######################################################################## 374class Plotter: 375 """Main class to manage objects.""" 376 377 def __init__( 378 self, 379 shape=(1, 1), 380 N=None, 381 pos=(0, 0), 382 size="auto", 383 screensize="auto", 384 title="vedo", 385 bg="white", 386 bg2=None, 387 axes=None, 388 sharecam=True, 389 resetcam=True, 390 interactive=None, 391 offscreen=False, 392 qt_widget=None, 393 wx_widget=None, 394 ): 395 """ 396 Arguments: 397 shape : (str, list) 398 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 399 N : (int) 400 number of desired renderers arranged in a grid automatically. 401 pos : (list) 402 (x,y) position in pixels of top-left corner of the rendering window on the screen 403 size : (str, list) 404 size of the rendering window. If 'auto', guess it based on screensize. 405 screensize : (list) 406 physical size of the monitor screen in pixels 407 bg : (color, str) 408 background color or specify jpg image file name with path 409 bg2 : (color) 410 background color of a gradient towards the top 411 title : (str) 412 window title 413 414 axes : (int) 415 416 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 417 Check out `vedo.addons.Axes()` for the available options. 418 419 - 0, no axes 420 - 1, draw three gray grid walls 421 - 2, show cartesian axes from (0,0,0) 422 - 3, show positive range of cartesian axes from (0,0,0) 423 - 4, show a triad at bottom left 424 - 5, show a cube at bottom left 425 - 6, mark the corners of the bounding box 426 - 7, draw a 3D ruler at each side of the cartesian axes 427 - 8, show the VTK CubeAxesActor object 428 - 9, show the bounding box outLine 429 - 10, show three circles representing the maximum bounding box 430 - 11, show a large grid on the x-y plane (use with zoom=8) 431 - 12, show polar axes 432 - 13, draw a simple ruler at the bottom of the window 433 - 14: draw a camera orientation widget 434 435 sharecam : (bool) 436 if False each renderer will have an independent camera 437 interactive : (bool) 438 if True will stop after show() to allow interaction with the 3d scene 439 offscreen : (bool) 440 if True will not show the rendering window 441 qt_widget : (QVTKRenderWindowInteractor) 442 render in a Qt-Widget using an QVTKRenderWindowInteractor. 443 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 444 """ 445 vedo.plotter_instance = self 446 447 if interactive is None: 448 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 449 self._interactive = interactive 450 # print("interactive", interactive, N, shape) 451 452 self.objects = [] # list of objects to be shown 453 self.clicked_object = None # holds the object that has been clicked 454 self.clicked_actor = None # holds the actor that has been clicked 455 456 self.shape = shape # nr. of subwindows in grid 457 self.axes = axes # show axes type nr. 458 self.title = title # window title 459 self.size = size # window size 460 self.backgrcol = bg # used also by backend notebooks 461 462 self.offscreen= offscreen 463 self.resetcam = resetcam 464 self.sharecam = sharecam # share the same camera if multiple renderers 465 self.pos = pos # used by vedo.file_io 466 467 self.picker = None # hold the vtkPicker object 468 self.picked2d = None # 2d coords of a clicked point on the rendering window 469 self.picked3d = None # 3d coords of a clicked point on an actor 470 471 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 472 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 473 self.interactor = None 474 self.window = None 475 self.renderer = None 476 self.renderers = [] # list of renderers 477 478 # mostly internal stuff: 479 self.hover_legends = [] 480 self.justremoved = None 481 self.axes_instances = [] 482 self.clock = 0 483 self.sliders = [] 484 self.buttons = [] 485 self.widgets = [] 486 self.cutter_widget = None 487 self.hint_widget = None 488 self.background_renderer = None 489 self.last_event = None 490 self.skybox = None 491 self._icol = 0 492 self._clockt0 = time.time() 493 self._extralight = None 494 self._cocoa_initialized = False 495 self._cocoa_process_events = True # make one call in show() 496 self._must_close_now = False 497 498 ##################################################################### 499 if vedo.settings.default_backend == "2d": 500 self.offscreen = True 501 if self.size == "auto": 502 self.size = (800, 600) 503 504 elif vedo.settings.default_backend == "k3d": 505 if self.size == "auto": 506 self.size = (1000, 1000) 507 #################################### 508 return ############################ 509 #################################### 510 511 ############################################################# 512 if vedo.settings.default_backend in ["vtk", "2d", "trame"]: 513 514 if screensize == "auto": 515 screensize = (2160, 1440) # TODO: get actual screen size 516 517 # build the rendering window: 518 self.window = vtki.vtkRenderWindow() 519 520 self.window.GlobalWarningDisplayOff() 521 522 if self.title == "vedo": # check if dev version 523 if "dev" in vedo.__version__: 524 self.title = f"vedo ({vedo.__version__})" 525 self.window.SetWindowName(self.title) 526 527 # more vedo.settings 528 if vedo.settings.use_depth_peeling: 529 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 530 self.window.SetMultiSamples(vedo.settings.multi_samples) 531 532 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 533 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 534 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 535 536 ############################################################# 537 if N: # N = number of renderers. Find out the best 538 539 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 540 vedo.logger.warning("having set N, shape is ignored.") 541 542 x, y = screensize 543 nx = int(np.sqrt(int(N * y / x) + 1)) 544 ny = int(np.sqrt(int(N * x / y) + 1)) 545 lm = [ 546 (nx, ny), 547 (nx, ny + 1), 548 (nx - 1, ny), 549 (nx + 1, ny), 550 (nx, ny - 1), 551 (nx - 1, ny + 1), 552 (nx + 1, ny - 1), 553 (nx + 1, ny + 1), 554 (nx - 1, ny - 1), 555 ] 556 ind, minl = 0, 1000 557 for i, m in enumerate(lm): 558 l = m[0] * m[1] 559 if N <= l < minl: 560 ind = i 561 minl = l 562 shape = lm[ind] 563 564 ################################################## 565 if isinstance(shape, str): 566 567 if "|" in shape: 568 if self.size == "auto": 569 self.size = (800, 1200) 570 n = int(shape.split("|")[0]) 571 m = int(shape.split("|")[1]) 572 rangen = reversed(range(n)) 573 rangem = reversed(range(m)) 574 else: 575 if self.size == "auto": 576 self.size = (1200, 800) 577 m = int(shape.split("/")[0]) 578 n = int(shape.split("/")[1]) 579 rangen = range(n) 580 rangem = range(m) 581 582 if n >= m: 583 xsplit = m / (n + m) 584 else: 585 xsplit = 1 - n / (n + m) 586 if vedo.settings.window_splitting_position: 587 xsplit = vedo.settings.window_splitting_position 588 589 for i in rangen: 590 arenderer = vtki.vtkRenderer() 591 if "|" in shape: 592 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 593 else: 594 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 595 self.renderers.append(arenderer) 596 597 for i in rangem: 598 arenderer = vtki.vtkRenderer() 599 600 if "|" in shape: 601 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 602 else: 603 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 604 self.renderers.append(arenderer) 605 606 for r in self.renderers: 607 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 608 609 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 610 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 611 if vedo.settings.use_depth_peeling: 612 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 613 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 614 r.SetUseFXAA(vedo.settings.use_fxaa) 615 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 616 617 r.SetBackground(vedo.get_color(self.backgrcol)) 618 619 self.axes_instances.append(None) 620 621 self.shape = (n + m,) 622 623 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 624 # passing a sequence of dicts for renderers specifications 625 626 if self.size == "auto": 627 self.size = (1000, 800) 628 629 for rd in shape: 630 x0, y0 = rd["bottomleft"] 631 x1, y1 = rd["topright"] 632 bg_ = rd.pop("bg", "white") 633 bg2_ = rd.pop("bg2", None) 634 635 arenderer = vtki.vtkRenderer() 636 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 637 638 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 639 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 640 if vedo.settings.use_depth_peeling: 641 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 642 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 643 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 644 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 645 646 arenderer.SetViewport(x0, y0, x1, y1) 647 arenderer.SetBackground(vedo.get_color(bg_)) 648 if bg2_: 649 arenderer.GradientBackgroundOn() 650 arenderer.SetBackground2(vedo.get_color(bg2_)) 651 652 self.renderers.append(arenderer) 653 self.axes_instances.append(None) 654 655 self.shape = (len(shape),) 656 657 else: 658 659 if isinstance(self.size, str) and self.size == "auto": 660 # figure out a reasonable window size 661 f = 1.5 662 x, y = screensize 663 xs = y / f * shape[1] # because y<x 664 ys = y / f * shape[0] 665 if xs > x / f: # shrink 666 xs = x / f 667 ys = xs / shape[1] * shape[0] 668 if ys > y / f: 669 ys = y / f 670 xs = ys / shape[0] * shape[1] 671 self.size = (int(xs), int(ys)) 672 if shape == (1, 1): 673 self.size = (int(y / f), int(y / f)) # because y<x 674 else: 675 self.size = (self.size[0], self.size[1]) 676 677 try: 678 image_actor = None 679 bgname = str(self.backgrcol).lower() 680 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 681 self.window.SetNumberOfLayers(2) 682 self.background_renderer = vtki.vtkRenderer() 683 self.background_renderer.SetLayer(0) 684 self.background_renderer.InteractiveOff() 685 self.background_renderer.SetBackground(vedo.get_color(bg2)) 686 image_actor = vedo.Image(self.backgrcol).actor 687 self.window.AddRenderer(self.background_renderer) 688 self.background_renderer.AddActor(image_actor) 689 except AttributeError: 690 pass 691 692 for i in reversed(range(shape[0])): 693 for j in range(shape[1]): 694 arenderer = vtki.vtkRenderer() 695 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 696 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 697 698 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 699 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 700 if vedo.settings.use_depth_peeling: 701 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 702 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 703 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 704 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 705 706 if image_actor: 707 arenderer.SetLayer(1) 708 709 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 710 if bg2: 711 arenderer.GradientBackgroundOn() 712 arenderer.SetBackground2(vedo.get_color(bg2)) 713 714 x0 = i / shape[0] 715 y0 = j / shape[1] 716 x1 = (i + 1) / shape[0] 717 y1 = (j + 1) / shape[1] 718 arenderer.SetViewport(y0, x0, y1, x1) 719 self.renderers.append(arenderer) 720 self.axes_instances.append(None) 721 self.shape = shape 722 723 if self.renderers: 724 self.renderer = self.renderers[0] 725 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 726 727 ######################################################### 728 if self.qt_widget or self.wx_widget: 729 if self.qt_widget: 730 self.window = self.qt_widget.GetRenderWindow() # overwrite 731 else: 732 self.window = self.wx_widget.GetRenderWindow() 733 self.interactor = self.window.GetInteractor() 734 735 ######################################################### 736 for r in self.renderers: 737 self.window.AddRenderer(r) 738 # set the background gradient if any 739 if vedo.settings.background_gradient_orientation > 0: 740 try: 741 modes = [ 742 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 743 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 744 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 745 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 746 ] 747 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 748 r.GradientBackgroundOn() 749 except AttributeError: 750 pass 751 752 ######################################################### 753 if self.qt_widget or self.wx_widget: 754 # self.window.SetSize(int(self.size[0]), int(self.size[1])) 755 self.interactor.SetRenderWindow(self.window) 756 # vsty = vtki.new("InteractorStyleTrackballCamera") 757 # self.interactor.SetInteractorStyle(vsty) 758 if vedo.settings.enable_default_keyboard_callbacks: 759 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 760 if vedo.settings.enable_default_mouse_callbacks: 761 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 762 return ################ 763 ######################## 764 765 if self.size[0] == "f": # full screen 766 self.size = "fullscreen" 767 self.window.SetFullScreen(True) 768 self.window.BordersOn() 769 else: 770 self.window.SetSize(int(self.size[0]), int(self.size[1])) 771 772 if self.offscreen: 773 if self.axes in (4, 5, 8, 12, 14): 774 self.axes = 0 # does not work with those 775 self.window.SetOffScreenRendering(True) 776 self.interactor = None 777 self._interactive = False 778 return ################ 779 ######################## 780 781 self.window.SetPosition(pos) 782 783 ######################################################### 784 self.interactor = vtki.vtkRenderWindowInteractor() 785 786 self.interactor.SetRenderWindow(self.window) 787 vsty = vtki.new("InteractorStyleTrackballCamera") 788 self.interactor.SetInteractorStyle(vsty) 789 self.interactor.RemoveObservers("CharEvent") 790 791 if vedo.settings.enable_default_keyboard_callbacks: 792 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 793 if vedo.settings.enable_default_mouse_callbacks: 794 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 795 796 ##################################################################### ..init ends here. 797 798 def __str__(self): 799 """Return Plotter info.""" 800 axtype = { 801 0: "(no axes)", 802 1: "(default customizable grid walls)", 803 2: "(cartesian axes from origin", 804 3: "(positive range of cartesian axes from origin", 805 4: "(axes triad at bottom left)", 806 5: "(oriented cube at bottom left)", 807 6: "(mark the corners of the bounding box)", 808 7: "(3D ruler at each side of the cartesian axes)", 809 8: "(the vtkCubeAxesActor object)", 810 9: "(the bounding box outline)", 811 10: "(circles of maximum bounding box range)", 812 11: "(show a large grid on the x-y plane)", 813 12: "(show polar axes)", 814 13: "(simple ruler at the bottom of the window)", 815 14: "(the vtkCameraOrientationWidget object)", 816 } 817 818 module = self.__class__.__module__ 819 name = self.__class__.__name__ 820 out = vedo.printc( 821 f"{module}.{name} at ({hex(id(self))})".ljust(75), 822 bold=True, invert=True, return_string=True, 823 ) 824 out += "\x1b[0m" 825 if self.interactor: 826 out += "window title".ljust(14) + ": " + self.title + "\n" 827 out += "window size".ljust(14) + f": {self.window.GetSize()}" 828 out += f", full_screen={self.window.GetScreenSize()}\n" 829 out += "activ renderer".ljust(14) + ": nr." + str(self.renderers.index(self.renderer)) 830 out += f" (out of {len(self.renderers)} renderers)\n" 831 832 bns, totpt = [], 0 833 for a in self.objects: 834 try: 835 b = a.bounds() 836 bns.append(b) 837 except AttributeError: 838 pass 839 try: 840 totpt += a.npoints 841 except AttributeError: 842 pass 843 out += "n. of objects".ljust(14) + f": {len(self.objects)}" 844 out += f" ({totpt} vertices)\n" if totpt else "\n" 845 846 if len(bns) > 0: 847 min_bns = np.min(bns, axis=0) 848 max_bns = np.max(bns, axis=0) 849 bx1, bx2 = utils.precision(min_bns[0], 3), utils.precision(max_bns[1], 3) 850 by1, by2 = utils.precision(min_bns[2], 3), utils.precision(max_bns[3], 3) 851 bz1, bz2 = utils.precision(min_bns[4], 3), utils.precision(max_bns[5], 3) 852 out += "bounds".ljust(14) + ":" 853 out += " x=(" + bx1 + ", " + bx2 + ")," 854 out += " y=(" + by1 + ", " + by2 + ")," 855 out += " z=(" + bz1 + ", " + bz2 + ")\n" 856 857 if utils.is_integer(self.axes): 858 out += "axes style".ljust(14) + f": {self.axes} {axtype[self.axes]}\n" 859 elif isinstance(self.axes, dict): 860 out += "axes style".ljust(14) + f": 1 {axtype[1]}\n" 861 else: 862 out += "axes style".ljust(14) + f": {[self.axes]}\n" 863 return out.rstrip() + "\x1b[0m" 864 865 def print(self): 866 """Print information about the current instance.""" 867 print(self.__str__()) 868 return self 869 870 def __iadd__(self, objects): 871 self.add(objects) 872 return self 873 874 def __isub__(self, objects): 875 self.remove(objects) 876 return self 877 878 def __enter__(self): 879 # context manager like in "with Plotter() as plt:" 880 return self 881 882 def __exit__(self, *args, **kwargs): 883 # context manager like in "with Plotter() as plt:" 884 self.close() 885 886 def initialize_interactor(self) -> "Plotter": 887 """Initialize the interactor if not already initialized.""" 888 if self.offscreen: 889 return self 890 if self.interactor: 891 if not self.interactor.GetInitialized(): 892 self.interactor.Initialize() 893 self.interactor.RemoveObservers("CharEvent") 894 return self 895 896 def process_events(self) -> "Plotter": 897 """Process all pending events.""" 898 self.initialize_interactor() 899 if self.interactor: 900 try: 901 self.interactor.ProcessEvents() 902 except AttributeError: 903 pass 904 return self 905 906 def at(self, nren: int, yren=None) -> "Plotter": 907 """ 908 Select the current renderer number as an int. 909 Can also use the `[nx, ny]` format. 910 """ 911 if utils.is_sequence(nren): 912 if len(nren) == 2: 913 nren, yren = nren 914 else: 915 vedo.logger.error("at() argument must be a single number or a list of two numbers") 916 raise RuntimeError 917 918 if yren is not None: 919 a, b = self.shape 920 x, y = nren, yren 921 nren = x * b + y 922 # print("at (", x, y, ") -> ren", nren) 923 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 924 vedo.logger.error(f"at({nren, yren}) is malformed!") 925 raise RuntimeError 926 927 self.renderer = self.renderers[nren] 928 return self 929 930 def add(self, *objs, at=None) -> "Plotter": 931 """ 932 Append the input objects to the internal list of objects to be shown. 933 934 Arguments: 935 at : (int) 936 add the object at the specified renderer 937 """ 938 if at is not None: 939 ren = self.renderers[at] 940 else: 941 ren = self.renderer 942 943 objs = utils.flatten(objs) 944 for ob in objs: 945 if ob and ob not in self.objects: 946 self.objects.append(ob) 947 948 acts = self._scan_input_return_acts(objs) 949 950 for a in acts: 951 952 if ren: 953 954 if isinstance(a, vedo.addons.BaseCutter): 955 a.add_to(self) # from cutters 956 continue 957 958 try: 959 ren.AddActor(a) 960 except TypeError: 961 ren.AddActor(a.actor) 962 963 if hasattr(a, "rendered_at"): 964 ir = self.renderers.index(ren) 965 a.rendered_at.add(ir) 966 if isinstance(a, vtki.vtkFollower): 967 a.SetCamera(self.camera) 968 if isinstance(a, vedo.visual.LightKit): 969 a.lightkit.AddLightsToRenderer(ren) 970 971 return self 972 973 def remove(self, *objs, at=None) -> "Plotter": 974 """ 975 Remove input object to the internal list of objects to be shown. 976 977 Objects to be removed can be referenced by their assigned name, 978 979 Arguments: 980 at : (int) 981 remove the object at the specified renderer 982 """ 983 # TODO and you can also use wildcards like `*` and `?`. 984 if at is not None: 985 ren = self.renderers[at] 986 else: 987 ren = self.renderer 988 989 objs = [ob for ob in utils.flatten(objs) if ob] 990 991 has_str = False 992 for ob in objs: 993 if isinstance(ob, str): 994 has_str = True 995 break 996 997 has_actor = False 998 for ob in objs: 999 if hasattr(ob, "actor") and ob.actor: 1000 has_actor = True 1001 break 1002 1003 if has_str or has_actor: 1004 # need to get the actors to search for 1005 for a in self.get_actors(include_non_pickables=True): 1006 # print("PARSING", [a]) 1007 try: 1008 if (a.name and a.name in objs) or a in objs: 1009 objs.append(a) 1010 # if a.name: 1011 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1012 # if any(bools) or a in objs: 1013 # objs.append(a) 1014 # print('a.name',a.name, objs,any(bools)) 1015 except AttributeError: # no .name 1016 # passing the actor so get back the object with .retrieve_object() 1017 try: 1018 vobj = a.retrieve_object() 1019 if (vobj.name and vobj.name in objs) or vobj in objs: 1020 # print('vobj.name', vobj.name) 1021 objs.append(vobj) 1022 except AttributeError: 1023 pass 1024 1025 ir = self.renderers.index(ren) 1026 1027 ids = [] 1028 for ob in set(objs): 1029 1030 # will remove it from internal list if possible 1031 try: 1032 idx = self.objects.index(ob) 1033 ids.append(idx) 1034 except ValueError: 1035 pass 1036 1037 if ren: ### remove it from the renderer 1038 1039 if isinstance(ob, vedo.addons.BaseCutter): 1040 ob.remove_from(self) # from cutters 1041 continue 1042 1043 try: 1044 ren.RemoveActor(ob) 1045 except TypeError: 1046 try: 1047 ren.RemoveActor(ob.actor) 1048 except AttributeError: 1049 pass 1050 1051 if hasattr(ob, "rendered_at"): 1052 ob.rendered_at.discard(ir) 1053 1054 if hasattr(ob, "scalarbar") and ob.scalarbar: 1055 ren.RemoveActor(ob.scalarbar) 1056 if hasattr(ob, "_caption") and ob._caption: 1057 ren.RemoveActor(ob._caption) 1058 if hasattr(ob, "shadows") and ob.shadows: 1059 for sha in ob.shadows: 1060 ren.RemoveActor(sha.actor) 1061 if hasattr(ob, "trail") and ob.trail: 1062 ren.RemoveActor(ob.trail.actor) 1063 ob.trail_points = [] 1064 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1065 for sha in ob.trail.shadows: 1066 ren.RemoveActor(sha.actor) 1067 1068 elif isinstance(ob, vedo.visual.LightKit): 1069 ob.lightkit.RemoveLightsFromRenderer(ren) 1070 1071 # for i in ids: # WRONG way of doing it! 1072 # del self.objects[i] 1073 # instead we do: 1074 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1075 return self 1076 1077 @property 1078 def actors(self): 1079 """Return the list of actors.""" 1080 return [ob.actor for ob in self.objects if hasattr(ob, "actor")] 1081 1082 def remove_lights(self) -> "Plotter": 1083 """Remove all the present lights in the current renderer.""" 1084 if self.renderer: 1085 self.renderer.RemoveAllLights() 1086 return self 1087 1088 def pop(self, at=None) -> "Plotter": 1089 """ 1090 Remove the last added object from the rendering window. 1091 This method is typically used in loops or callback functions. 1092 """ 1093 if at is not None and not isinstance(at, int): 1094 # wrong usage pitfall 1095 vedo.logger.error("argument of pop() must be an integer") 1096 raise RuntimeError() 1097 1098 if self.objects: 1099 self.remove(self.objects[-1], at) 1100 return self 1101 1102 def render(self, resetcam=False) -> "Plotter": 1103 """Render the scene. This method is typically used in loops or callback functions.""" 1104 1105 if vedo.settings.dry_run_mode >= 2: 1106 return self 1107 1108 if not self.window: 1109 return self 1110 1111 self.initialize_interactor() 1112 1113 if resetcam: 1114 self.renderer.ResetCamera() 1115 1116 self.window.Render() 1117 1118 if self._cocoa_process_events and self.interactor.GetInitialized(): 1119 if "Darwin" in vedo.sys_platform and not self.offscreen: 1120 self.interactor.ProcessEvents() 1121 self._cocoa_process_events = False 1122 return self 1123 1124 def interactive(self) -> "Plotter": 1125 """ 1126 Start window interaction. 1127 Analogous to `show(..., interactive=True)`. 1128 """ 1129 if vedo.settings.dry_run_mode >= 1: 1130 return self 1131 self.initialize_interactor() 1132 if self.interactor: 1133 # print("self.interactor.Start()") 1134 self.interactor.Start() 1135 # print("self.interactor.Start() done") 1136 if self._must_close_now: 1137 # print("self.interactor.TerminateApp()") 1138 self.interactor.GetRenderWindow().Finalize() 1139 self.interactor.TerminateApp() 1140 self.interactor = None 1141 self.window = None 1142 self.renderer = None 1143 self.renderers = [] 1144 self.camera = None 1145 return self 1146 1147 def use_depth_peeling(self, at=None, value=True) -> "Plotter": 1148 """ 1149 Specify whether use depth peeling algorithm at this specific renderer 1150 Call this method before the first rendering. 1151 """ 1152 if at is None: 1153 ren = self.renderer 1154 else: 1155 ren = self.renderers[at] 1156 ren.SetUseDepthPeeling(value) 1157 return self 1158 1159 def background(self, c1=None, c2=None, at=None, mode=0) -> Union["Plotter", "np.ndarray"]: 1160 """Set the color of the background for the current renderer. 1161 A different renderer index can be specified by keyword `at`. 1162 1163 Arguments: 1164 c1 : (list) 1165 background main color. 1166 c2 : (list) 1167 background color for the upper part of the window. 1168 at : (int) 1169 renderer index. 1170 mode : (int) 1171 background mode (needs vtk version >= 9.3) 1172 0 = vertical, 1173 1 = horizontal, 1174 2 = radial farthest side, 1175 3 = radia farthest corner. 1176 """ 1177 if not self.renderers: 1178 return self 1179 if at is None: 1180 r = self.renderer 1181 else: 1182 r = self.renderers[at] 1183 1184 if c1 is None and c2 is None: 1185 return np.array(r.GetBackground()) 1186 1187 if r: 1188 if c1 is not None: 1189 r.SetBackground(vedo.get_color(c1)) 1190 if c2 is not None: 1191 r.GradientBackgroundOn() 1192 r.SetBackground2(vedo.get_color(c2)) 1193 if mode: 1194 try: # only works with vtk>=9.3 1195 modes = [ 1196 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1197 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1198 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1199 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1200 ] 1201 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 1202 except AttributeError: 1203 pass 1204 1205 else: 1206 r.GradientBackgroundOff() 1207 return self 1208 1209 ################################################################## 1210 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1211 """ 1212 Return a list of Meshes from the specified renderer. 1213 1214 Arguments: 1215 at : (int) 1216 specify which renderer to look at. 1217 include_non_pickables : (bool) 1218 include non-pickable objects 1219 unpack_assemblies : (bool) 1220 unpack assemblies into their components 1221 """ 1222 if at is None: 1223 renderer = self.renderer 1224 at = self.renderers.index(renderer) 1225 elif isinstance(at, int): 1226 renderer = self.renderers[at] 1227 1228 has_global_axes = False 1229 if isinstance(self.axes_instances[at], vedo.Assembly): 1230 has_global_axes = True 1231 1232 if unpack_assemblies: 1233 acs = renderer.GetActors() 1234 else: 1235 acs = renderer.GetViewProps() 1236 1237 objs = [] 1238 acs.InitTraversal() 1239 for _ in range(acs.GetNumberOfItems()): 1240 1241 if unpack_assemblies: 1242 a = acs.GetNextItem() 1243 else: 1244 a = acs.GetNextProp() 1245 1246 if isinstance(a, vtki.vtkVolume): 1247 continue 1248 1249 if include_non_pickables or a.GetPickable(): 1250 if a == self.axes_instances[at]: 1251 continue 1252 if has_global_axes and a in self.axes_instances[at].actors: 1253 continue 1254 try: 1255 objs.append(a.retrieve_object()) 1256 except AttributeError: 1257 pass 1258 return objs 1259 1260 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1261 """ 1262 Return a list of Volumes from the specified renderer. 1263 1264 Arguments: 1265 at : (int) 1266 specify which renderer to look at 1267 include_non_pickables : (bool) 1268 include non-pickable objects 1269 """ 1270 if at is None: 1271 renderer = self.renderer 1272 at = self.renderers.index(renderer) 1273 elif isinstance(at, int): 1274 renderer = self.renderers[at] 1275 1276 vols = [] 1277 acs = renderer.GetVolumes() 1278 acs.InitTraversal() 1279 for _ in range(acs.GetNumberOfItems()): 1280 a = acs.GetNextItem() 1281 if include_non_pickables or a.GetPickable(): 1282 try: 1283 vols.append(a.retrieve_object()) 1284 except AttributeError: 1285 pass 1286 return vols 1287 1288 def get_actors(self, at=None, include_non_pickables=False) -> list: 1289 """ 1290 Return a list of Volumes from the specified renderer. 1291 1292 Arguments: 1293 at : (int) 1294 specify which renderer to look at 1295 include_non_pickables : (bool) 1296 include non-pickable objects 1297 """ 1298 if at is None: 1299 renderer = self.renderer 1300 at = self.renderers.index(renderer) 1301 elif isinstance(at, int): 1302 renderer = self.renderers[at] 1303 1304 acts = [] 1305 acs = renderer.GetViewProps() 1306 acs.InitTraversal() 1307 for _ in range(acs.GetNumberOfItems()): 1308 a = acs.GetNextProp() 1309 if include_non_pickables or a.GetPickable(): 1310 acts.append(a) 1311 return acts 1312 1313 def check_actors_trasform(self, at=None) -> "Plotter": 1314 """ 1315 Reset the transformation matrix of all actors at specified renderer. 1316 This is only useful when actors have been moved/rotated/scaled manually 1317 in an already rendered scene using interactors like 1318 'TrackballActor' or 'JoystickActor'. 1319 """ 1320 # see issue https://github.com/marcomusy/vedo/issues/1046 1321 for a in self.get_actors(at=at, include_non_pickables=True): 1322 try: 1323 M = a.GetMatrix() 1324 except AttributeError: 1325 continue 1326 if M and not M.IsIdentity(): 1327 try: 1328 a.retrieve_object().apply_transform_from_actor() 1329 # vedo.logger.info( 1330 # f"object '{a.retrieve_object().name}' " 1331 # "was manually moved. Updated to its current position." 1332 # ) 1333 except AttributeError: 1334 pass 1335 return self 1336 1337 def reset_camera(self, tight=None) -> "Plotter": 1338 """ 1339 Reset the camera position and zooming. 1340 If tight (float) is specified the zooming reserves a padding space 1341 in the xy-plane expressed in percent of the average size. 1342 """ 1343 if tight is None: 1344 self.renderer.ResetCamera() 1345 else: 1346 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1347 1348 cam = self.renderer.GetActiveCamera() 1349 1350 self.renderer.ComputeAspect() 1351 aspect = self.renderer.GetAspect() 1352 angle = np.pi * cam.GetViewAngle() / 180.0 1353 dx, dy = (x1 - x0) * 0.999, (y1 - y0) * 0.999 1354 dist = max(dx / aspect[0], dy) / np.sin(angle / 2) / 2 1355 1356 cam.SetViewUp(0, 1, 0) 1357 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1358 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1359 if cam.GetParallelProjection(): 1360 ps = max(dx / aspect[0], dy) / 2 1361 cam.SetParallelScale(ps * (1 + tight)) 1362 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1363 return self 1364 1365 def reset_viewup(self, smooth=True) -> "Plotter": 1366 """ 1367 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1368 """ 1369 vbb = addons.compute_visible_bounds()[0] 1370 x0, x1, y0, y1, z0, z1 = vbb 1371 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1372 d = self.camera.GetDistance() 1373 1374 viewups = np.array( 1375 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1376 ) 1377 positions = np.array( 1378 [ 1379 (mx, my, mz + d), 1380 (mx, my, mz - d), 1381 (mx, my + d, mz), 1382 (mx, my - d, mz), 1383 (mx + d, my, mz), 1384 (mx - d, my, mz), 1385 ] 1386 ) 1387 1388 vu = np.array(self.camera.GetViewUp()) 1389 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1390 1391 poc = np.array(self.camera.GetPosition()) 1392 foc = np.array(self.camera.GetFocalPoint()) 1393 a = poc - foc 1394 b = positions - foc 1395 a = a / np.linalg.norm(a) 1396 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1397 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1398 1399 if smooth: 1400 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1401 for t in outtimes: 1402 vv = vu * (1 - t) + viewups[vui] * t 1403 pp = poc * (1 - t) + positions[pui] * t 1404 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1405 self.camera.SetViewUp(vv) 1406 self.camera.SetPosition(pp) 1407 self.camera.SetFocalPoint(ff) 1408 self.render() 1409 1410 # interpolator does not respect parallel view...: 1411 # cam1 = dict( 1412 # pos=poc, 1413 # viewup=vu, 1414 # focal_point=(mx,my,mz), 1415 # clipping_range=self.camera.GetClippingRange() 1416 # ) 1417 # # cam1 = self.camera 1418 # cam2 = dict( 1419 # pos=positions[pui], 1420 # viewup=viewups[vui], 1421 # focal_point=(mx,my,mz), 1422 # clipping_range=self.camera.GetClippingRange() 1423 # ) 1424 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1425 # for c in vcams: 1426 # self.renderer.SetActiveCamera(c) 1427 # self.render() 1428 else: 1429 1430 self.camera.SetViewUp(viewups[vui]) 1431 self.camera.SetPosition(positions[pui]) 1432 self.camera.SetFocalPoint(mx, my, mz) 1433 1434 self.renderer.ResetCameraClippingRange() 1435 1436 # vbb, _, _, _ = addons.compute_visible_bounds() 1437 # x0,x1, y0,y1, z0,z1 = vbb 1438 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1439 self.render() 1440 return self 1441 1442 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1443 """ 1444 Takes as input two cameras set camera at an interpolated position: 1445 1446 Cameras can be vtkCamera or dictionaries in format: 1447 1448 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1449 1450 Press `shift-C` key in interactive mode to dump a python snipplet 1451 of parameters for the current camera view. 1452 """ 1453 nc = len(cameras) 1454 if len(times) == 0: 1455 times = np.linspace(0, 1, num=nc, endpoint=True) 1456 1457 assert len(times) == nc 1458 1459 cin = vtki.new("CameraInterpolator") 1460 1461 # cin.SetInterpolationTypeToLinear() # buggy? 1462 if nc > 2 and smooth: 1463 cin.SetInterpolationTypeToSpline() 1464 1465 for i, cam in enumerate(cameras): 1466 vcam = cam 1467 if isinstance(cam, dict): 1468 vcam = utils.camera_from_dict(cam) 1469 cin.AddCamera(times[i], vcam) 1470 1471 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1472 rng = maxt - mint 1473 1474 if len(output_times) == 0: 1475 cin.InterpolateCamera(t * rng, self.camera) 1476 self.renderer.SetActiveCamera(self.camera) 1477 return [self.camera] 1478 else: 1479 vcams = [] 1480 for tt in output_times: 1481 c = vtki.vtkCamera() 1482 cin.InterpolateCamera(tt * rng, c) 1483 vcams.append(c) 1484 return vcams 1485 1486 def fly_to(self, point) -> "Plotter": 1487 """ 1488 Fly camera to the specified point. 1489 1490 Arguments: 1491 point : (list) 1492 point in space to place camera. 1493 1494 Example: 1495 ```python 1496 from vedo import * 1497 cone = Cone() 1498 plt = Plotter(axes=1) 1499 plt.show(cone) 1500 plt.fly_to([1,0,0]) 1501 plt.interactive().close() 1502 ``` 1503 """ 1504 if self.interactor: 1505 self.resetcam = False 1506 self.interactor.FlyTo(self.renderer, point) 1507 return self 1508 1509 def look_at(self, plane="xy") -> "Plotter": 1510 """Move the camera so that it looks at the specified cartesian plane""" 1511 cam = self.renderer.GetActiveCamera() 1512 fp = np.array(cam.GetFocalPoint()) 1513 p = np.array(cam.GetPosition()) 1514 dist = np.linalg.norm(fp - p) 1515 plane = plane.lower() 1516 if "x" in plane and "y" in plane: 1517 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1518 cam.SetViewUp(0.0, 1.0, 0.0) 1519 elif "x" in plane and "z" in plane: 1520 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1521 cam.SetViewUp(0.0, 0.0, 1.0) 1522 elif "y" in plane and "z" in plane: 1523 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1524 cam.SetViewUp(0.0, 0.0, 1.0) 1525 else: 1526 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1527 return self 1528 1529 def record(self, filename="") -> str: 1530 """ 1531 Record camera, mouse, keystrokes and all other events. 1532 Recording can be toggled on/off by pressing key "R". 1533 1534 Arguments: 1535 filename : (str) 1536 ascii file to store events. 1537 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1538 1539 Returns: 1540 a string descriptor of events. 1541 1542 Examples: 1543 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1544 """ 1545 if vedo.settings.dry_run_mode >= 1: 1546 return "" 1547 if not self.interactor: 1548 vedo.logger.warning("Cannot record events, no interactor defined.") 1549 return "" 1550 erec = vtki.new("InteractorEventRecorder") 1551 erec.SetInteractor(self.interactor) 1552 if not filename: 1553 if not os.path.exists(vedo.settings.cache_directory): 1554 os.makedirs(vedo.settings.cache_directory) 1555 home_dir = os.path.expanduser("~") 1556 filename = os.path.join( 1557 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1558 print("Events will be recorded in", filename) 1559 erec.SetFileName(filename) 1560 erec.SetKeyPressActivationValue("R") 1561 erec.EnabledOn() 1562 erec.Record() 1563 self.interactor.Start() 1564 erec.Stop() 1565 erec.EnabledOff() 1566 with open(filename, "r", encoding="UTF-8") as fl: 1567 events = fl.read() 1568 erec = None 1569 return events 1570 1571 def play(self, recorded_events="", repeats=0) -> "Plotter": 1572 """ 1573 Play camera, mouse, keystrokes and all other events. 1574 1575 Arguments: 1576 events : (str) 1577 file o string of events. 1578 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1579 repeats : (int) 1580 number of extra repeats of the same events. The default is 0. 1581 1582 Examples: 1583 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1584 """ 1585 if vedo.settings.dry_run_mode >= 1: 1586 return self 1587 if not self.interactor: 1588 vedo.logger.warning("Cannot play events, no interactor defined.") 1589 return self 1590 1591 erec = vtki.new("InteractorEventRecorder") 1592 erec.SetInteractor(self.interactor) 1593 1594 if not recorded_events: 1595 home_dir = os.path.expanduser("~") 1596 recorded_events = os.path.join( 1597 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1598 1599 if recorded_events.endswith(".log"): 1600 erec.ReadFromInputStringOff() 1601 erec.SetFileName(recorded_events) 1602 else: 1603 erec.ReadFromInputStringOn() 1604 erec.SetInputString(recorded_events) 1605 1606 erec.Play() 1607 for _ in range(repeats): 1608 erec.Rewind() 1609 erec.Play() 1610 erec.EnabledOff() 1611 erec = None 1612 return self 1613 1614 def parallel_projection(self, value=True, at=None) -> "Plotter": 1615 """ 1616 Use parallel projection `at` a specified renderer. 1617 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1618 An input value equal to -1 will toggle it on/off. 1619 """ 1620 if at is not None: 1621 r = self.renderers[at] 1622 else: 1623 r = self.renderer 1624 if value == -1: 1625 val = r.GetActiveCamera().GetParallelProjection() 1626 value = not val 1627 r.GetActiveCamera().SetParallelProjection(value) 1628 r.Modified() 1629 return self 1630 1631 def render_hidden_lines(self, value=True) -> "Plotter": 1632 """Remove hidden lines when in wireframe mode.""" 1633 self.renderer.SetUseHiddenLineRemoval(not value) 1634 return self 1635 1636 def fov(self, angle: float) -> "Plotter": 1637 """ 1638 Set the field of view angle for the camera. 1639 This is the angle of the camera frustum in the horizontal direction. 1640 High values will result in a wide-angle lens (fish-eye effect), 1641 and low values will result in a telephoto lens. 1642 1643 Default value is 30 degrees. 1644 """ 1645 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1646 self.renderer.GetActiveCamera().SetViewAngle(angle) 1647 return self 1648 1649 def zoom(self, zoom: float) -> "Plotter": 1650 """Apply a zooming factor for the current camera view""" 1651 self.renderer.GetActiveCamera().Zoom(zoom) 1652 return self 1653 1654 def azimuth(self, angle: float) -> "Plotter": 1655 """Rotate camera around the view up vector.""" 1656 self.renderer.GetActiveCamera().Azimuth(angle) 1657 return self 1658 1659 def elevation(self, angle: float) -> "Plotter": 1660 """Rotate the camera around the cross product of the negative 1661 of the direction of projection and the view up vector.""" 1662 self.renderer.GetActiveCamera().Elevation(angle) 1663 return self 1664 1665 def roll(self, angle: float) -> "Plotter": 1666 """Roll the camera about the direction of projection.""" 1667 self.renderer.GetActiveCamera().Roll(angle) 1668 return self 1669 1670 def dolly(self, value: float) -> "Plotter": 1671 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1672 self.renderer.GetActiveCamera().Dolly(value) 1673 return self 1674 1675 ################################################################## 1676 def add_slider( 1677 self, 1678 sliderfunc, 1679 xmin, 1680 xmax, 1681 value=None, 1682 pos=4, 1683 title="", 1684 font="Calco", 1685 title_size=1, 1686 c=None, 1687 alpha=1, 1688 show_value=True, 1689 delayed=False, 1690 **options, 1691 ) -> "vedo.addons.Slider2D": 1692 """ 1693 Add a `vedo.addons.Slider2D` which can call an external custom function. 1694 1695 Arguments: 1696 sliderfunc : (Callable) 1697 external function to be called by the widget 1698 xmin : (float) 1699 lower value of the slider 1700 xmax : (float) 1701 upper value 1702 value : (float) 1703 current value 1704 pos : (list, str) 1705 position corner number: horizontal [1-5] or vertical [11-15] 1706 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1707 and also by a string descriptor (eg. "bottom-left") 1708 title : (str) 1709 title text 1710 font : (str) 1711 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1712 title_size : (float) 1713 title text scale [1.0] 1714 show_value : (bool) 1715 if True current value is shown 1716 delayed : (bool) 1717 if True the callback is delayed until when the mouse button is released 1718 alpha : (float) 1719 opacity of the scalar bar texts 1720 slider_length : (float) 1721 slider length 1722 slider_width : (float) 1723 slider width 1724 end_cap_length : (float) 1725 length of the end cap 1726 end_cap_width : (float) 1727 width of the end cap 1728 tube_width : (float) 1729 width of the tube 1730 title_height : (float) 1731 width of the title 1732 tformat : (str) 1733 format of the title 1734 1735 Examples: 1736 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1737 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1738 1739 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1740 """ 1741 if c is None: # automatic black or white 1742 c = (0.8, 0.8, 0.8) 1743 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1744 c = (0.2, 0.2, 0.2) 1745 else: 1746 c = vedo.get_color(c) 1747 1748 slider2d = addons.Slider2D( 1749 sliderfunc, 1750 xmin, 1751 xmax, 1752 value, 1753 pos, 1754 title, 1755 font, 1756 title_size, 1757 c, 1758 alpha, 1759 show_value, 1760 delayed, 1761 **options, 1762 ) 1763 1764 if self.renderer: 1765 slider2d.renderer = self.renderer 1766 if self.interactor: 1767 slider2d.interactor = self.interactor 1768 slider2d.on() 1769 self.sliders.append([slider2d, sliderfunc]) 1770 return slider2d 1771 1772 def add_slider3d( 1773 self, 1774 sliderfunc, 1775 pos1, 1776 pos2, 1777 xmin, 1778 xmax, 1779 value=None, 1780 s=0.03, 1781 t=1, 1782 title="", 1783 rotation=0.0, 1784 c=None, 1785 show_value=True, 1786 ) -> "vedo.addons.Slider3D": 1787 """ 1788 Add a 3D slider widget which can call an external custom function. 1789 1790 Arguments: 1791 sliderfunc : (function) 1792 external function to be called by the widget 1793 pos1 : (list) 1794 first position 3D coordinates 1795 pos2 : (list) 1796 second position coordinates 1797 xmin : (float) 1798 lower value 1799 xmax : (float) 1800 upper value 1801 value : (float) 1802 initial value 1803 s : (float) 1804 label scaling factor 1805 t : (float) 1806 tube scaling factor 1807 title : (str) 1808 title text 1809 c : (color) 1810 slider color 1811 rotation : (float) 1812 title rotation around slider axis 1813 show_value : (bool) 1814 if True current value is shown 1815 1816 Examples: 1817 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1818 1819 ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png) 1820 """ 1821 if c is None: # automatic black or white 1822 c = (0.8, 0.8, 0.8) 1823 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1824 c = (0.2, 0.2, 0.2) 1825 else: 1826 c = vedo.get_color(c) 1827 1828 slider3d = addons.Slider3D( 1829 sliderfunc, 1830 pos1, 1831 pos2, 1832 xmin, 1833 xmax, 1834 value, 1835 s, 1836 t, 1837 title, 1838 rotation, 1839 c, 1840 show_value, 1841 ) 1842 slider3d.renderer = self.renderer 1843 slider3d.interactor = self.interactor 1844 slider3d.on() 1845 self.sliders.append([slider3d, sliderfunc]) 1846 return slider3d 1847 1848 def add_button( 1849 self, 1850 fnc=None, 1851 states=("On", "Off"), 1852 c=("w", "w"), 1853 bc=("green4", "red4"), 1854 pos=(0.7, 0.1), 1855 size=24, 1856 font="Courier", 1857 bold=True, 1858 italic=False, 1859 alpha=1, 1860 angle=0, 1861 ) -> Union["vedo.addons.Button", None]: 1862 """ 1863 Add a button to the renderer window. 1864 1865 Arguments: 1866 states : (list) 1867 a list of possible states, e.g. ['On', 'Off'] 1868 c : (list) 1869 a list of colors for each state 1870 bc : (list) 1871 a list of background colors for each state 1872 pos : (list) 1873 2D position from left-bottom corner 1874 size : (float) 1875 size of button font 1876 font : (str) 1877 font type. Check [available fonts here](https://vedo.embl.es/fonts). 1878 bold : (bool) 1879 bold font face (False) 1880 italic : (bool) 1881 italic font face (False) 1882 alpha : (float) 1883 opacity level 1884 angle : (float) 1885 anticlockwise rotation in degrees 1886 1887 Returns: 1888 `vedo.addons.Button` object. 1889 1890 Examples: 1891 - [buttons1.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons1.py) 1892 - [buttons2.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons2.py) 1893 1894 ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg) 1895 """ 1896 if self.interactor: 1897 bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle) 1898 self.renderer.AddActor2D(bu) 1899 self.buttons.append(bu) 1900 # bu.function_id = self.add_callback("LeftButtonPress", bu.function) # not good 1901 bu.function_id = bu.add_observer("pick", bu.function, priority=10) 1902 return bu 1903 return None 1904 1905 def add_spline_tool( 1906 self, points, pc="k", ps=8, lc="r4", ac="g5", 1907 lw=2, alpha=1, closed=False, ontop=True, can_add_nodes=True, 1908 ) -> "vedo.addons.SplineTool": 1909 """ 1910 Add a spline tool to the current plotter. 1911 Nodes of the spline can be dragged in space with the mouse. 1912 Clicking on the line itself adds an extra point. 1913 Selecting a point and pressing del removes it. 1914 1915 Arguments: 1916 points : (Mesh, Points, array) 1917 the set of vertices forming the spline nodes. 1918 pc : (str) 1919 point color. The default is 'k'. 1920 ps : (str) 1921 point size. The default is 8. 1922 lc : (str) 1923 line color. The default is 'r4'. 1924 ac : (str) 1925 active point marker color. The default is 'g5'. 1926 lw : (int) 1927 line width. The default is 2. 1928 alpha : (float) 1929 line transparency. 1930 closed : (bool) 1931 spline is meant to be closed. The default is False. 1932 1933 Returns: 1934 a `SplineTool` object. 1935 1936 Examples: 1937 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1938 1939 ![](https://vedo.embl.es/images/basic/spline_tool.png) 1940 """ 1941 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1942 sw.interactor = self.interactor 1943 sw.on() 1944 sw.Initialize(sw.points.dataset) 1945 sw.representation.SetRenderer(self.renderer) 1946 sw.representation.SetClosedLoop(closed) 1947 sw.representation.BuildRepresentation() 1948 self.widgets.append(sw) 1949 return sw 1950 1951 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1952 """Add an inset icon mesh into the same renderer. 1953 1954 Arguments: 1955 pos : (int, list) 1956 icon position in the range [1-4] indicating one of the 4 corners, 1957 or it can be a tuple (x,y) as a fraction of the renderer size. 1958 size : (float) 1959 size of the square inset. 1960 1961 Examples: 1962 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1963 """ 1964 iconw = addons.Icon(icon, pos, size) 1965 1966 iconw.SetInteractor(self.interactor) 1967 iconw.EnabledOn() 1968 iconw.InteractiveOff() 1969 self.widgets.append(iconw) 1970 return iconw 1971 1972 def add_global_axes(self, axtype=None, c=None) -> "Plotter": 1973 """Draw axes on scene. Available axes types: 1974 1975 Arguments: 1976 axtype : (int) 1977 - 0, no axes, 1978 - 1, draw three gray grid walls 1979 - 2, show cartesian axes from (0,0,0) 1980 - 3, show positive range of cartesian axes from (0,0,0) 1981 - 4, show a triad at bottom left 1982 - 5, show a cube at bottom left 1983 - 6, mark the corners of the bounding box 1984 - 7, draw a 3D ruler at each side of the cartesian axes 1985 - 8, show the vtkCubeAxesActor object 1986 - 9, show the bounding box outLine 1987 - 10, show three circles representing the maximum bounding box 1988 - 11, show a large grid on the x-y plane 1989 - 12, show polar axes 1990 - 13, draw a simple ruler at the bottom of the window 1991 1992 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 1993 1994 Example: 1995 ```python 1996 from vedo import Box, show 1997 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 1998 show( 1999 b, 2000 axes={ 2001 "xtitle": "Some long variable [a.u.]", 2002 "number_of_divisions": 4, 2003 # ... 2004 }, 2005 ) 2006 ``` 2007 2008 Examples: 2009 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2010 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2011 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2012 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2013 2014 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2015 """ 2016 addons.add_global_axes(axtype, c) 2017 return self 2018 2019 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2020 """Add a legend to the top right. 2021 2022 Examples: 2023 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 2024 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 2025 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 2026 """ 2027 acts = self.get_meshes() 2028 lb = addons.LegendBox(acts, **kwargs) 2029 self.add(lb) 2030 return lb 2031 2032 def add_hint( 2033 self, 2034 obj, 2035 text="", 2036 c="k", 2037 bg="yellow9", 2038 font="Calco", 2039 size=18, 2040 justify=0, 2041 angle=0, 2042 delay=250, 2043 ) -> Union[vtki.vtkBalloonWidget, None]: 2044 """ 2045 Create a pop-up hint style message when hovering an object. 2046 Use `add_hint(obj, False)` to disable a hinting a specific object. 2047 Use `add_hint(None)` to disable all hints. 2048 2049 Arguments: 2050 obj : (Mesh, Points) 2051 the object to associate the pop-up to 2052 text : (str) 2053 string description of the pop-up 2054 delay : (int) 2055 milliseconds to wait before pop-up occurs 2056 """ 2057 if self.offscreen or not self.interactor: 2058 return None 2059 2060 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2061 # Linux vtk9.0 is bugged 2062 vedo.logger.warning( 2063 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2064 ) 2065 return None 2066 2067 if obj is None: 2068 self.hint_widget.EnabledOff() 2069 self.hint_widget.SetInteractor(None) 2070 self.hint_widget = None 2071 return self.hint_widget 2072 2073 if text is False and self.hint_widget: 2074 self.hint_widget.RemoveBalloon(obj) 2075 return self.hint_widget 2076 2077 if text == "": 2078 if obj.name: 2079 text = obj.name 2080 elif obj.filename: 2081 text = obj.filename 2082 else: 2083 return None 2084 2085 if not self.hint_widget: 2086 self.hint_widget = vtki.vtkBalloonWidget() 2087 2088 rep = self.hint_widget.GetRepresentation() 2089 rep.SetBalloonLayoutToImageRight() 2090 2091 trep = rep.GetTextProperty() 2092 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2093 trep.SetFontFile(utils.get_font_path(font)) 2094 trep.SetFontSize(size) 2095 trep.SetColor(vedo.get_color(c)) 2096 trep.SetBackgroundColor(vedo.get_color(bg)) 2097 trep.SetShadow(0) 2098 trep.SetJustification(justify) 2099 trep.UseTightBoundingBoxOn() 2100 2101 self.hint_widget.ManagesCursorOff() 2102 self.hint_widget.SetTimerDuration(delay) 2103 self.hint_widget.SetInteractor(self.interactor) 2104 if angle: 2105 trep.SetOrientation(angle) 2106 trep.SetBackgroundOpacity(0) 2107 # else: 2108 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2109 self.hint_widget.SetRepresentation(rep) 2110 self.widgets.append(self.hint_widget) 2111 self.hint_widget.EnabledOn() 2112 2113 bst = self.hint_widget.GetBalloonString(obj.actor) 2114 if bst: 2115 self.hint_widget.UpdateBalloonString(obj.actor, text) 2116 else: 2117 self.hint_widget.AddBalloon(obj.actor, text) 2118 2119 return self.hint_widget 2120 2121 def add_shadows(self) -> "Plotter": 2122 """Add shadows at the current renderer.""" 2123 if self.renderer: 2124 shadows = vtki.new("ShadowMapPass") 2125 seq = vtki.new("SequencePass") 2126 passes = vtki.new("RenderPassCollection") 2127 passes.AddItem(shadows.GetShadowMapBakerPass()) 2128 passes.AddItem(shadows) 2129 seq.SetPasses(passes) 2130 camerapass = vtki.new("CameraPass") 2131 camerapass.SetDelegatePass(seq) 2132 self.renderer.SetPass(camerapass) 2133 return self 2134 2135 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> "Plotter": 2136 """ 2137 Screen Space Ambient Occlusion. 2138 2139 For every pixel on the screen, the pixel shader samples the depth values around 2140 the current pixel and tries to compute the amount of occlusion from each of the sampled 2141 points. 2142 2143 Arguments: 2144 radius : (float) 2145 radius of influence in absolute units 2146 bias : (float) 2147 bias of the normals 2148 blur : (bool) 2149 add a blurring to the sampled positions 2150 samples : (int) 2151 number of samples to probe 2152 2153 Examples: 2154 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2155 2156 ![](https://vedo.embl.es/images/basic/ssao.jpg) 2157 """ 2158 lights = vtki.new("LightsPass") 2159 2160 opaque = vtki.new("OpaquePass") 2161 2162 ssaoCam = vtki.new("CameraPass") 2163 ssaoCam.SetDelegatePass(opaque) 2164 2165 ssao = vtki.new("SSAOPass") 2166 ssao.SetRadius(radius) 2167 ssao.SetBias(bias) 2168 ssao.SetBlur(blur) 2169 ssao.SetKernelSize(samples) 2170 ssao.SetDelegatePass(ssaoCam) 2171 2172 translucent = vtki.new("TranslucentPass") 2173 2174 volpass = vtki.new("VolumetricPass") 2175 ddp = vtki.new("DualDepthPeelingPass") 2176 ddp.SetTranslucentPass(translucent) 2177 ddp.SetVolumetricPass(volpass) 2178 2179 over = vtki.new("OverlayPass") 2180 2181 collection = vtki.new("RenderPassCollection") 2182 collection.AddItem(lights) 2183 collection.AddItem(ssao) 2184 collection.AddItem(ddp) 2185 collection.AddItem(over) 2186 2187 sequence = vtki.new("SequencePass") 2188 sequence.SetPasses(collection) 2189 2190 cam = vtki.new("CameraPass") 2191 cam.SetDelegatePass(sequence) 2192 2193 self.renderer.SetPass(cam) 2194 return self 2195 2196 def add_depth_of_field(self, autofocus=True) -> "Plotter": 2197 """Add a depth of field effect in the scene.""" 2198 lights = vtki.new("LightsPass") 2199 2200 opaque = vtki.new("OpaquePass") 2201 2202 dofCam = vtki.new("CameraPass") 2203 dofCam.SetDelegatePass(opaque) 2204 2205 dof = vtki.new("DepthOfFieldPass") 2206 dof.SetAutomaticFocalDistance(autofocus) 2207 dof.SetDelegatePass(dofCam) 2208 2209 collection = vtki.new("RenderPassCollection") 2210 collection.AddItem(lights) 2211 collection.AddItem(dof) 2212 2213 sequence = vtki.new("SequencePass") 2214 sequence.SetPasses(collection) 2215 2216 cam = vtki.new("CameraPass") 2217 cam.SetDelegatePass(sequence) 2218 2219 self.renderer.SetPass(cam) 2220 return self 2221 2222 def _add_skybox(self, hdrfile: str) -> "Plotter": 2223 # many hdr files are at https://polyhaven.com/all 2224 2225 reader = vtki.new("HDRReader") 2226 # Check the image can be read. 2227 if not reader.CanReadFile(hdrfile): 2228 vedo.logger.error(f"Cannot read HDR file {hdrfile}") 2229 return self 2230 reader.SetFileName(hdrfile) 2231 reader.Update() 2232 2233 texture = vtki.vtkTexture() 2234 texture.SetColorModeToDirectScalars() 2235 texture.SetInputData(reader.GetOutput()) 2236 2237 # Convert to a cube map 2238 tcm = vtki.new("EquirectangularToCubeMapTexture") 2239 tcm.SetInputTexture(texture) 2240 # Enable mipmapping to handle HDR image 2241 tcm.MipmapOn() 2242 tcm.InterpolateOn() 2243 2244 self.renderer.SetEnvironmentTexture(tcm) 2245 self.renderer.UseImageBasedLightingOn() 2246 self.skybox = vtki.new("Skybox") 2247 self.skybox.SetTexture(tcm) 2248 self.renderer.AddActor(self.skybox) 2249 return self 2250 2251 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None) -> "vedo.addons.RendererFrame": 2252 """ 2253 Add a frame to the renderer subwindow. 2254 2255 Arguments: 2256 c : (color) 2257 color name or index 2258 alpha : (float) 2259 opacity level 2260 lw : (int) 2261 line width in pixels. 2262 padding : (float) 2263 padding space in pixels. 2264 """ 2265 if c is None: # automatic black or white 2266 c = (0.9, 0.9, 0.9) 2267 if self.renderer: 2268 if np.sum(self.renderer.GetBackground()) > 1.5: 2269 c = (0.1, 0.1, 0.1) 2270 renf = addons.RendererFrame(c, alpha, lw, padding) 2271 if renf: 2272 self.renderer.AddActor(renf) 2273 return renf 2274 2275 def add_hover_legend( 2276 self, 2277 at=None, 2278 c=None, 2279 pos="bottom-left", 2280 font="Calco", 2281 s=0.75, 2282 bg="auto", 2283 alpha=0.1, 2284 maxlength=24, 2285 use_info=False, 2286 ) -> int: 2287 """ 2288 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2289 2290 The created text object are stored in `plotter.hover_legends`. 2291 2292 Returns: 2293 the id of the callback function. 2294 2295 Arguments: 2296 c : (color) 2297 Text color. If None then black or white is chosen automatically 2298 pos : (str) 2299 text positioning 2300 font : (str) 2301 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2302 s : (float) 2303 text size scale 2304 bg : (color) 2305 background color of the 2D box containing the text 2306 alpha : (float) 2307 box transparency 2308 maxlength : (int) 2309 maximum number of characters per line 2310 use_info : (bool) 2311 visualize the content of the `obj.info` attribute 2312 2313 Examples: 2314 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2315 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2316 2317 ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg) 2318 """ 2319 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2320 2321 if at is None: 2322 at = self.renderers.index(self.renderer) 2323 2324 def _legfunc(evt): 2325 if not evt.object or not self.renderer or at != evt.at: 2326 if hoverlegend.mapper.GetInput(): # clear and return 2327 hoverlegend.mapper.SetInput("") 2328 self.render() 2329 return 2330 2331 if use_info: 2332 if hasattr(evt.object, "info"): 2333 t = str(evt.object.info) 2334 else: 2335 return 2336 else: 2337 t, tp = "", "" 2338 if evt.isMesh: 2339 tp = "Mesh " 2340 elif evt.isPoints: 2341 tp = "Points " 2342 elif evt.isVolume: 2343 tp = "Volume " 2344 elif evt.isImage: 2345 tp = "Image " 2346 elif evt.isAssembly: 2347 tp = "Assembly " 2348 else: 2349 return 2350 2351 if evt.isAssembly: 2352 if not evt.object.name: 2353 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2354 else: 2355 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2356 else: 2357 if evt.object.name: 2358 t += f"{tp}name" 2359 if evt.isPoints: 2360 t += " " 2361 if evt.isMesh: 2362 t += " " 2363 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2364 2365 if evt.object.filename: 2366 t += f"{tp}filename: " 2367 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2368 t += "\n" 2369 if not evt.object.file_size: 2370 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2371 if evt.object.file_size: 2372 t += " : " 2373 sz, created = evt.object.file_size, evt.object.created 2374 t += f"{created[4:-5]} ({sz})" + "\n" 2375 2376 if evt.isPoints: 2377 indata = evt.object.dataset 2378 if indata.GetNumberOfPoints(): 2379 t += ( 2380 f"#points/cells: {indata.GetNumberOfPoints()}" 2381 f" / {indata.GetNumberOfCells()}" 2382 ) 2383 pdata = indata.GetPointData() 2384 cdata = indata.GetCellData() 2385 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2386 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2387 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2388 t += " *" 2389 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2390 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2391 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2392 t += " *" 2393 2394 if evt.isImage: 2395 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2396 t += f"\nImage shape: {evt.object.shape}" 2397 pcol = self.color_picker(evt.picked2d) 2398 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2399 2400 # change box color if needed in 'auto' mode 2401 if evt.isPoints and "auto" in str(bg): 2402 actcol = evt.object.properties.GetColor() 2403 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2404 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2405 2406 # adapt to changes in bg color 2407 bgcol = self.renderers[at].GetBackground() 2408 _bgcol = c 2409 if _bgcol is None: # automatic black or white 2410 _bgcol = (0.9, 0.9, 0.9) 2411 if sum(bgcol) > 1.5: 2412 _bgcol = (0.1, 0.1, 0.1) 2413 if len(set(_bgcol).intersection(bgcol)) < 3: 2414 hoverlegend.color(_bgcol) 2415 2416 if hoverlegend.mapper.GetInput() != t: 2417 hoverlegend.mapper.SetInput(t) 2418 self.interactor.Render() 2419 2420 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2421 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2422 2423 self.add(hoverlegend, at=at) 2424 self.hover_legends.append(hoverlegend) 2425 idcall = self.add_callback("MouseMove", _legfunc) 2426 return idcall 2427 2428 def add_scale_indicator( 2429 self, 2430 pos=(0.7, 0.05), 2431 s=0.02, 2432 length=2, 2433 lw=4, 2434 c="k1", 2435 alpha=1, 2436 units="", 2437 gap=0.05, 2438 ) -> Union["vedo.visual.Actor2D", None]: 2439 """ 2440 Add a Scale Indicator. Only works in parallel mode (no perspective). 2441 2442 Arguments: 2443 pos : (list) 2444 fractional (x,y) position on the screen. 2445 s : (float) 2446 size of the text. 2447 length : (float) 2448 length of the line. 2449 units : (str) 2450 string to show units. 2451 gap : (float) 2452 separation of line and text. 2453 2454 Example: 2455 ```python 2456 from vedo import settings, Cube, Plotter 2457 settings.use_parallel_projection = True # or else it does not make sense! 2458 cube = Cube().alpha(0.2) 2459 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2460 plt.add_scale_indicator(units='um', c='blue4') 2461 plt.show(cube, "Scale indicator with units").close() 2462 ``` 2463 ![](https://vedo.embl.es/images/feats/scale_indicator.png) 2464 """ 2465 # Note that this cannot go in addons.py 2466 # because it needs callbacks and window size 2467 if not self.interactor: 2468 return None 2469 2470 ppoints = vtki.vtkPoints() # Generate the polyline 2471 psqr = [[0.0, gap], [length / 10, gap]] 2472 dd = psqr[1][0] - psqr[0][0] 2473 for i, pt in enumerate(psqr): 2474 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2475 lines = vtki.vtkCellArray() 2476 lines.InsertNextCell(len(psqr)) 2477 for i in range(len(psqr)): 2478 lines.InsertCellPoint(i) 2479 pd = vtki.vtkPolyData() 2480 pd.SetPoints(ppoints) 2481 pd.SetLines(lines) 2482 2483 wsx, wsy = self.window.GetSize() 2484 if not self.camera.GetParallelProjection(): 2485 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2486 return None 2487 2488 rlabel = vtki.new("VectorText") 2489 rlabel.SetText("scale") 2490 tf = vtki.new("TransformPolyDataFilter") 2491 tf.SetInputConnection(rlabel.GetOutputPort()) 2492 t = vtki.vtkTransform() 2493 t.Scale(s * wsy / wsx, s, 1) 2494 tf.SetTransform(t) 2495 2496 app = vtki.new("AppendPolyData") 2497 app.AddInputConnection(tf.GetOutputPort()) 2498 app.AddInputData(pd) 2499 2500 mapper = vtki.new("PolyDataMapper2D") 2501 mapper.SetInputConnection(app.GetOutputPort()) 2502 cs = vtki.vtkCoordinate() 2503 cs.SetCoordinateSystem(1) 2504 mapper.SetTransformCoordinate(cs) 2505 2506 fractor = vedo.visual.Actor2D() 2507 csys = fractor.GetPositionCoordinate() 2508 csys.SetCoordinateSystem(3) 2509 fractor.SetPosition(pos) 2510 fractor.SetMapper(mapper) 2511 fractor.GetProperty().SetColor(vedo.get_color(c)) 2512 fractor.GetProperty().SetOpacity(alpha) 2513 fractor.GetProperty().SetLineWidth(lw) 2514 fractor.GetProperty().SetDisplayLocationToForeground() 2515 2516 def sifunc(iren, ev): 2517 wsx, wsy = self.window.GetSize() 2518 ps = self.camera.GetParallelScale() 2519 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2520 if units: 2521 newtxt += " " + units 2522 if rlabel.GetText() != newtxt: 2523 rlabel.SetText(newtxt) 2524 2525 self.renderer.AddActor(fractor) 2526 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2527 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2528 self.interactor.AddObserver("InteractionEvent", sifunc) 2529 sifunc(0, 0) 2530 return fractor 2531 2532 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2533 """ 2534 Create an Event object with information of what was clicked. 2535 2536 If `enable_picking` is False, no picking will be performed. 2537 This can be useful to avoid double picking when using buttons. 2538 """ 2539 if not self.interactor: 2540 return Event() 2541 2542 if len(pos) > 0: 2543 x, y = pos 2544 self.interactor.SetEventPosition(pos) 2545 else: 2546 x, y = self.interactor.GetEventPosition() 2547 self.renderer = self.interactor.FindPokedRenderer(x, y) 2548 2549 self.picked2d = (x, y) 2550 2551 key = self.interactor.GetKeySym() 2552 2553 if key: 2554 if "_L" in key or "_R" in key: 2555 # skip things like Shift_R 2556 key = "" # better than None 2557 else: 2558 if self.interactor.GetShiftKey(): 2559 key = key.upper() 2560 2561 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2562 key = "underscore" 2563 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2564 key = "plus" 2565 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2566 key = "?" 2567 2568 if self.interactor.GetControlKey(): 2569 key = "Ctrl+" + key 2570 2571 if self.interactor.GetAltKey(): 2572 key = "Alt+" + key 2573 2574 if enable_picking: 2575 if not self.picker: 2576 self.picker = vtki.vtkPropPicker() 2577 2578 self.picker.PickProp(x, y, self.renderer) 2579 actor = self.picker.GetProp3D() 2580 # Note that GetProp3D already picks Assembly 2581 2582 xp, yp = self.interactor.GetLastEventPosition() 2583 dx, dy = x - xp, y - yp 2584 2585 delta3d = np.array([0, 0, 0]) 2586 2587 if actor: 2588 picked3d = np.array(self.picker.GetPickPosition()) 2589 2590 try: 2591 vobj = actor.retrieve_object() 2592 old_pt = np.asarray(vobj.picked3d) 2593 vobj.picked3d = picked3d 2594 delta3d = picked3d - old_pt 2595 except (AttributeError, TypeError): 2596 pass 2597 2598 else: 2599 picked3d = None 2600 2601 if not actor: # try 2D 2602 actor = self.picker.GetActor2D() 2603 2604 event = Event() 2605 event.name = ename 2606 event.title = self.title 2607 event.id = -1 # will be set by the timer wrapper function 2608 event.timerid = -1 # will be set by the timer wrapper function 2609 event.priority = -1 # will be set by the timer wrapper function 2610 event.time = time.time() 2611 event.at = self.renderers.index(self.renderer) 2612 event.keypress = key 2613 if enable_picking: 2614 try: 2615 event.object = actor.retrieve_object() 2616 except AttributeError: 2617 event.object = actor 2618 try: 2619 event.actor = actor.retrieve_object() # obsolete use object instead 2620 except AttributeError: 2621 event.actor = actor 2622 event.picked3d = picked3d 2623 event.picked2d = (x, y) 2624 event.delta2d = (dx, dy) 2625 event.angle2d = np.arctan2(dy, dx) 2626 event.speed2d = np.sqrt(dx * dx + dy * dy) 2627 event.delta3d = delta3d 2628 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2629 event.isPoints = isinstance(event.object, vedo.Points) 2630 event.isMesh = isinstance(event.object, vedo.Mesh) 2631 event.isAssembly = isinstance(event.object, vedo.Assembly) 2632 event.isVolume = isinstance(event.object, vedo.Volume) 2633 event.isImage = isinstance(event.object, vedo.Image) 2634 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2635 return event 2636 2637 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2638 """ 2639 Add a function to be executed while show() is active. 2640 2641 Return a unique id for the callback. 2642 2643 The callback function (see example below) exposes a dictionary 2644 with the following information: 2645 - `name`: event name, 2646 - `id`: event unique identifier, 2647 - `priority`: event priority (float), 2648 - `interactor`: the interactor object, 2649 - `at`: renderer nr. where the event occurred 2650 - `keypress`: key pressed as string 2651 - `actor`: object picked by the mouse 2652 - `picked3d`: point picked in world coordinates 2653 - `picked2d`: screen coords of the mouse pointer 2654 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2655 - `delta3d`: ...same but in 3D world coords 2656 - `angle2d`: angle of mouse movement on screen 2657 - `speed2d`: speed of mouse movement on screen 2658 - `speed3d`: speed of picked point in world coordinates 2659 - `isPoints`: True if of class 2660 - `isMesh`: True if of class 2661 - `isAssembly`: True if of class 2662 - `isVolume`: True if of class Volume 2663 - `isImage`: True if of class 2664 2665 If `enable_picking` is False, no picking will be performed. 2666 This can be useful to avoid double picking when using buttons. 2667 2668 Frequently used events are: 2669 - `KeyPress`, `KeyRelease`: listen to keyboard events 2670 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2671 - `MiddleButtonPress`, `MiddleButtonRelease` 2672 - `RightButtonPress`, `RightButtonRelease` 2673 - `MouseMove`: listen to mouse pointer changing position 2674 - `MouseWheelForward`, `MouseWheelBackward` 2675 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2676 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2677 - `ResetCamera`, `ResetCameraClippingRange` 2678 - `Error`, `Warning` 2679 - `Char` 2680 - `Timer` 2681 2682 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2683 2684 Example: 2685 ```python 2686 from vedo import * 2687 2688 def func(evt): 2689 # this function is called every time the mouse moves 2690 # (evt is a dotted dictionary) 2691 if not evt.object: 2692 return # no hit, return 2693 print("point coords =", evt.picked3d) 2694 # print(evt) # full event dump 2695 2696 elli = Ellipsoid() 2697 plt = Plotter(axes=1) 2698 plt.add_callback('mouse hovering', func) 2699 plt.show(elli).close() 2700 ``` 2701 2702 Examples: 2703 - [spline_draw.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw.py) 2704 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2705 2706 ![](https://vedo.embl.es/images/advanced/spline_draw.png) 2707 2708 - ..and many others! 2709 """ 2710 from vtkmodules.util.misc import calldata_type 2711 2712 if not self.interactor: 2713 return 0 2714 2715 if vedo.settings.dry_run_mode >= 1: 2716 return 0 2717 2718 ######################################### 2719 @calldata_type(vtki.VTK_INT) 2720 def _func_wrap(iren, ename, timerid=None): 2721 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2722 event.timerid = timerid 2723 event.id = cid 2724 event.priority = priority 2725 self.last_event = event 2726 func(event) 2727 2728 ######################################### 2729 2730 event_name = utils.get_vtk_name_event(event_name) 2731 2732 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2733 # print(f"Registering event: {event_name} with id={cid}") 2734 return cid 2735 2736 def remove_callback(self, cid: Union[int, str]) -> "Plotter": 2737 """ 2738 Remove a callback function by its id 2739 or a whole category of callbacks by their name. 2740 2741 Arguments: 2742 cid : (int, str) 2743 Unique id of the callback. 2744 If an event name is passed all callbacks of that type are removed. 2745 """ 2746 if self.interactor: 2747 if isinstance(cid, str): 2748 cid = utils.get_vtk_name_event(cid) 2749 self.interactor.RemoveObservers(cid) 2750 else: 2751 self.interactor.RemoveObserver(cid) 2752 return self 2753 2754 def remove_all_observers(self) -> "Plotter": 2755 """ 2756 Remove all observers. 2757 2758 Example: 2759 ```python 2760 from vedo import * 2761 2762 def kfunc(event): 2763 print("Key pressed:", event.keypress) 2764 if event.keypress == 'q': 2765 plt.close() 2766 2767 def rfunc(event): 2768 if event.isImage: 2769 printc("Right-clicked!", event) 2770 plt.render() 2771 2772 img = Image(dataurl+"images/embryo.jpg") 2773 2774 plt = Plotter(size=(1050, 600)) 2775 plt.parallel_projection(True) 2776 plt.remove_all_observers() 2777 plt.add_callback("key press", kfunc) 2778 plt.add_callback("mouse right click", rfunc) 2779 plt.show("Right-Click Me! Press q to exit.", img) 2780 plt.close() 2781 ``` 2782 """ 2783 if self.interactor: 2784 self.interactor.RemoveAllObservers() 2785 return self 2786 2787 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2788 """ 2789 Start or stop an existing timer. 2790 2791 Arguments: 2792 action : (str) 2793 Either "create"/"start" or "destroy"/"stop" 2794 timer_id : (int) 2795 When stopping the timer, the ID of the timer as returned when created 2796 dt : (int) 2797 time in milliseconds between each repeated call 2798 one_shot : (bool) 2799 create a one shot timer of prescribed duration instead of a repeating one 2800 2801 Examples: 2802 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2803 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2804 2805 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 2806 """ 2807 if action in ("create", "start"): 2808 if timer_id is not None: 2809 vedo.logger.warning("you set a timer_id but it will be ignored.") 2810 if one_shot: 2811 timer_id = self.interactor.CreateOneShotTimer(dt) 2812 else: 2813 timer_id = self.interactor.CreateRepeatingTimer(dt) 2814 return timer_id 2815 2816 elif action in ("destroy", "stop"): 2817 if timer_id is not None: 2818 self.interactor.DestroyTimer(timer_id) 2819 else: 2820 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2821 else: 2822 e = f"in timer_callback(). Cannot understand action: {action}\n" 2823 e += " allowed actions are: ['start', 'stop']. Skipped." 2824 vedo.logger.error(e) 2825 return timer_id 2826 2827 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2828 """ 2829 Add a callback function that will be called when an event occurs. 2830 Consider using `add_callback()` instead. 2831 """ 2832 if not self.interactor: 2833 return -1 2834 event_name = utils.get_vtk_name_event(event_name) 2835 idd = self.interactor.AddObserver(event_name, func, priority) 2836 return idd 2837 2838 def compute_world_coordinate( 2839 self, 2840 pos2d: MutableSequence[float], 2841 at=None, 2842 objs=(), 2843 bounds=(), 2844 offset=None, 2845 pixeltol=None, 2846 worldtol=None, 2847 ) -> np.ndarray: 2848 """ 2849 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2850 If a set of meshes is passed then points are placed onto these. 2851 2852 Arguments: 2853 pos2d : (list) 2854 2D screen coordinates point. 2855 at : (int) 2856 renderer number. 2857 objs : (list) 2858 list of Mesh objects to project the point onto. 2859 bounds : (list) 2860 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2861 offset : (float) 2862 specify an offset value. 2863 pixeltol : (int) 2864 screen tolerance in pixels. 2865 worldtol : (float) 2866 world coordinates tolerance. 2867 2868 Returns: 2869 numpy array, the point in 3D world coordinates. 2870 2871 Examples: 2872 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2873 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2874 2875 ![](https://vedo.embl.es/images/basic/mousehover3.jpg) 2876 """ 2877 if at is not None: 2878 renderer = self.renderers[at] 2879 else: 2880 renderer = self.renderer 2881 2882 if not objs: 2883 pp = vtki.vtkFocalPlanePointPlacer() 2884 else: 2885 pps = vtki.vtkPolygonalSurfacePointPlacer() 2886 for ob in objs: 2887 pps.AddProp(ob.actor) 2888 pp = pps # type: ignore 2889 2890 if len(bounds) == 6: 2891 pp.SetPointBounds(bounds) 2892 if pixeltol: 2893 pp.SetPixelTolerance(pixeltol) 2894 if worldtol: 2895 pp.SetWorldTolerance(worldtol) 2896 if offset: 2897 pp.SetOffset(offset) 2898 2899 worldPos: MutableSequence[float] = [0, 0, 0] 2900 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2901 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2902 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2903 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2904 return np.array(worldPos) 2905 2906 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2907 """ 2908 Given a 3D points in the current renderer (or full window), 2909 find the screen pixel coordinates. 2910 2911 Example: 2912 ```python 2913 from vedo import * 2914 2915 elli = Ellipsoid().point_size(5) 2916 2917 plt = Plotter() 2918 plt.show(elli, "Press q to continue and print the info") 2919 2920 xyscreen = plt.compute_screen_coordinates(elli) 2921 print('xyscreen coords:', xyscreen) 2922 2923 # simulate an event happening at one point 2924 event = plt.fill_event(pos=xyscreen[123]) 2925 print(event) 2926 ``` 2927 """ 2928 try: 2929 obj = obj.vertices 2930 except AttributeError: 2931 pass 2932 2933 if utils.is_sequence(obj): 2934 pts = obj 2935 p2d = [] 2936 cs = vtki.vtkCoordinate() 2937 cs.SetCoordinateSystemToWorld() 2938 cs.SetViewport(self.renderer) 2939 for p in pts: 2940 cs.SetValue(p) 2941 if full_window: 2942 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2943 else: 2944 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2945 return np.array(p2d, dtype=int) 2946 2947 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2948 """ 2949 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2950 2951 Returns a frustum Mesh that contains the visible field of view. 2952 This can be used to select objects in a scene or select vertices. 2953 2954 Example: 2955 ```python 2956 from vedo import * 2957 2958 settings.enable_default_mouse_callbacks = False 2959 2960 def mode_select(objs): 2961 print("Selected objects:", objs) 2962 d0 = mode.start_x, mode.start_y # display coords 2963 d1 = mode.end_x, mode.end_y 2964 2965 frustum = plt.pick_area(d0, d1) 2966 col = np.random.randint(0, 10) 2967 infru = frustum.inside_points(mesh) 2968 infru.point_size(10).color(col) 2969 plt.add(frustum, infru).render() 2970 2971 mesh = Mesh(dataurl+"cow.vtk") 2972 mesh.color("k5").linewidth(1) 2973 2974 mode = interactor_modes.BlenderStyle() 2975 mode.callback_select = mode_select 2976 2977 plt = Plotter().user_mode(mode) 2978 plt.show(mesh, axes=1) 2979 ``` 2980 """ 2981 if at is not None: 2982 ren = self.renderers[at] 2983 else: 2984 ren = self.renderer 2985 area_picker = vtki.vtkAreaPicker() 2986 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 2987 planes = area_picker.GetFrustum() 2988 2989 fru = vtki.new("FrustumSource") 2990 fru.SetPlanes(planes) 2991 fru.ShowLinesOff() 2992 fru.Update() 2993 2994 afru = vedo.Mesh(fru.GetOutput()) 2995 afru.alpha(0.1).lw(1).pickable(False) 2996 afru.name = "Frustum" 2997 return afru 2998 2999 def _scan_input_return_acts(self, objs) -> Any: 3000 # scan the input and return a list of actors 3001 if not utils.is_sequence(objs): 3002 objs = [objs] 3003 3004 ################# 3005 wannabe_acts2 = [] 3006 for a in objs: 3007 3008 try: 3009 wannabe_acts2.append(a.actor) 3010 except AttributeError: 3011 wannabe_acts2.append(a) # already actor 3012 3013 try: 3014 wannabe_acts2.append(a.scalarbar) 3015 except AttributeError: 3016 pass 3017 3018 try: 3019 for sh in a.shadows: 3020 wannabe_acts2.append(sh.actor) 3021 except AttributeError: 3022 pass 3023 3024 try: 3025 wannabe_acts2.append(a.trail.actor) 3026 if a.trail.shadows: # trails may also have shadows 3027 for sh in a.trail.shadows: 3028 wannabe_acts2.append(sh.actor) 3029 except AttributeError: 3030 pass 3031 3032 ################# 3033 scanned_acts = [] 3034 for a in wannabe_acts2: # scan content of list 3035 3036 if a is None: 3037 pass 3038 3039 elif isinstance(a, (vtki.vtkActor, vtki.vtkActor2D)): 3040 scanned_acts.append(a) 3041 3042 elif isinstance(a, str): 3043 # assume a 2D comment was given 3044 changed = False # check if one already exists so to just update text 3045 if self.renderer: # might be jupyter 3046 acs = self.renderer.GetActors2D() 3047 acs.InitTraversal() 3048 for i in range(acs.GetNumberOfItems()): 3049 act = acs.GetNextItem() 3050 if isinstance(act, vedo.shapes.Text2D): 3051 aposx, aposy = act.GetPosition() 3052 if aposx < 0.01 and aposy > 0.99: # "top-left" 3053 act.text(a) # update content! no appending nada 3054 changed = True 3055 break 3056 if not changed: 3057 out = vedo.shapes.Text2D(a) # append a new one 3058 scanned_acts.append(out) 3059 # scanned_acts.append(vedo.shapes.Text2D(a)) # naive version 3060 3061 elif isinstance(a, vtki.vtkPolyData): 3062 scanned_acts.append(vedo.Mesh(a).actor) 3063 3064 elif isinstance(a, vtki.vtkImageData): 3065 scanned_acts.append(vedo.Volume(a).actor) 3066 3067 elif isinstance(a, vedo.RectilinearGrid): 3068 scanned_acts.append(a.actor) 3069 3070 elif isinstance(a, vedo.StructuredGrid): 3071 scanned_acts.append(a.actor) 3072 3073 elif isinstance(a, vtki.vtkLight): 3074 self.renderer.AddLight(a) 3075 3076 elif isinstance(a, vedo.visual.LightKit): 3077 a.lightkit.AddLightsToRenderer(self.renderer) 3078 3079 elif isinstance(a, vtki.get_class("MultiBlockDataSet")): 3080 for i in range(a.GetNumberOfBlocks()): 3081 b = a.GetBlock(i) 3082 if isinstance(b, vtki.vtkPolyData): 3083 scanned_acts.append(vedo.Mesh(b).actor) 3084 elif isinstance(b, vtki.vtkImageData): 3085 scanned_acts.append(vedo.Volume(b).actor) 3086 3087 elif isinstance(a, (vtki.vtkProp, vtki.vtkInteractorObserver)): 3088 scanned_acts.append(a) 3089 3090 elif "trimesh" in str(type(a)): 3091 scanned_acts.append(utils.trimesh2vedo(a)) 3092 3093 elif "meshlab" in str(type(a)): 3094 if "MeshSet" in str(type(a)): 3095 for i in range(a.number_meshes()): 3096 if a.mesh_id_exists(i): 3097 scanned_acts.append(utils.meshlab2vedo(a.mesh(i))) 3098 else: 3099 scanned_acts.append(utils.meshlab2vedo(a)) 3100 3101 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 3102 import vedo.dolfin as vdlf 3103 3104 scanned_acts.append(vdlf.IMesh(a).actor) 3105 3106 elif "madcad" in str(type(a)): 3107 scanned_acts.append(utils.madcad2vedo(a).actor) 3108 3109 elif "TetgenIO" in str(type(a)): 3110 scanned_acts.append(vedo.TetMesh(a).shrink(0.9).c("pink7").actor) 3111 3112 elif "matplotlib.figure.Figure" in str(type(a)): 3113 scanned_acts.append(vedo.Image(a).clone2d("top-right", 0.6)) 3114 3115 else: 3116 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 3117 3118 return scanned_acts 3119 3120 def show( 3121 self, 3122 *objects, 3123 at=None, 3124 axes=None, 3125 resetcam=None, 3126 zoom=False, 3127 interactive=None, 3128 viewup="", 3129 azimuth=0.0, 3130 elevation=0.0, 3131 roll=0.0, 3132 camera=None, 3133 mode=None, 3134 rate=None, 3135 bg=None, 3136 bg2=None, 3137 size=None, 3138 title=None, 3139 screenshot="", 3140 ) -> Any: 3141 """ 3142 Render a list of objects. 3143 3144 Arguments: 3145 at : (int) 3146 number of the renderer to plot to, in case of more than one exists 3147 3148 axes : (int) 3149 axis type-1 can be fully customized by passing a dictionary. 3150 Check `addons.Axes()` for the full list of options. 3151 set the type of axes to be shown: 3152 - 0, no axes 3153 - 1, draw three gray grid walls 3154 - 2, show cartesian axes from (0,0,0) 3155 - 3, show positive range of cartesian axes from (0,0,0) 3156 - 4, show a triad at bottom left 3157 - 5, show a cube at bottom left 3158 - 6, mark the corners of the bounding box 3159 - 7, draw a 3D ruler at each side of the cartesian axes 3160 - 8, show the `vtkCubeAxesActor` object 3161 - 9, show the bounding box outLine 3162 - 10, show three circles representing the maximum bounding box 3163 - 11, show a large grid on the x-y plane 3164 - 12, show polar axes 3165 - 13, draw a simple ruler at the bottom of the window 3166 3167 azimuth/elevation/roll : (float) 3168 move camera accordingly the specified value 3169 3170 viewup: str, list 3171 either `['x', 'y', 'z']` or a vector to set vertical direction 3172 3173 resetcam : (bool) 3174 re-adjust camera position to fit objects 3175 3176 camera : (dict, vtkCamera) 3177 camera parameters can further be specified with a dictionary 3178 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3179 - pos, `(list)`, the position of the camera in world coordinates 3180 - focal_point `(list)`, the focal point of the camera in world coordinates 3181 - viewup `(list)`, the view up direction for the camera 3182 - distance `(float)`, set the focal point to the specified distance from the camera position. 3183 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3184 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3185 in world-coordinate distances. The default is 1. 3186 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3187 This method has no effect in perspective projection mode. 3188 3189 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3190 plane to be set a distance 'thickness' beyond the near clipping plane. 3191 3192 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3193 measured in degrees. The default angle is 30 degrees. 3194 This method has no effect in parallel projection mode. 3195 The formula for setting the angle up for perfect perspective viewing is: 3196 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3197 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3198 3199 interactive : (bool) 3200 pause and interact with window (True) or continue execution (False) 3201 3202 rate : (float) 3203 maximum rate of `show()` in Hertz 3204 3205 mode : (int, str) 3206 set the type of interaction: 3207 - 0 = TrackballCamera [default] 3208 - 1 = TrackballActor 3209 - 2 = JoystickCamera 3210 - 3 = JoystickActor 3211 - 4 = Flight 3212 - 5 = RubberBand2D 3213 - 6 = RubberBand3D 3214 - 7 = RubberBandZoom 3215 - 8 = Terrain 3216 - 9 = Unicam 3217 - 10 = Image 3218 - Check out `vedo.interaction_modes` for more options. 3219 3220 bg : (str, list) 3221 background color in RGB format, or string name 3222 3223 bg2 : (str, list) 3224 second background color to create a gradient background 3225 3226 size : (str, list) 3227 size of the window, e.g. size="fullscreen", or size=[600,400] 3228 3229 title : (str) 3230 window title text 3231 3232 screenshot : (str) 3233 save a screenshot of the window to file 3234 """ 3235 3236 if vedo.settings.dry_run_mode >= 2: 3237 return self 3238 3239 if self.wx_widget: 3240 return self 3241 3242 if self.renderers: # in case of notebooks 3243 3244 if at is None: 3245 at = self.renderers.index(self.renderer) 3246 3247 else: 3248 3249 if at >= len(self.renderers): 3250 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3251 vedo.logger.error(t) 3252 return self 3253 3254 self.renderer = self.renderers[at] 3255 3256 if title is not None: 3257 self.title = title 3258 3259 if size is not None: 3260 self.size = size 3261 if self.size[0] == "f": # full screen 3262 self.size = "fullscreen" 3263 self.window.SetFullScreen(True) 3264 self.window.BordersOn() 3265 else: 3266 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3267 3268 if vedo.settings.default_backend == "vtk": 3269 if str(bg).endswith(".hdr"): 3270 self._add_skybox(bg) 3271 else: 3272 if bg is not None: 3273 self.backgrcol = vedo.get_color(bg) 3274 self.renderer.SetBackground(self.backgrcol) 3275 if bg2 is not None: 3276 self.renderer.GradientBackgroundOn() 3277 self.renderer.SetBackground2(vedo.get_color(bg2)) 3278 3279 if axes is not None: 3280 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3281 objects = list(objects) 3282 objects.append(axes) # move it into the list of normal things to show 3283 axes = 0 3284 self.axes = axes 3285 3286 if interactive is not None: 3287 self._interactive = interactive 3288 if self.offscreen: 3289 self._interactive = False 3290 3291 # camera stuff 3292 if resetcam is not None: 3293 self.resetcam = resetcam 3294 3295 if camera is not None: 3296 self.resetcam = False 3297 viewup = "" 3298 if isinstance(camera, vtki.vtkCamera): 3299 cameracopy = vtki.vtkCamera() 3300 cameracopy.DeepCopy(camera) 3301 self.camera = cameracopy 3302 else: 3303 self.camera = utils.camera_from_dict(camera) 3304 3305 self.add(objects) 3306 3307 # Backend ############################################################### 3308 if vedo.settings.default_backend in ["k3d"]: 3309 return backends.get_notebook_backend(self.objects) 3310 ######################################################################### 3311 3312 for ia in utils.flatten(objects): 3313 try: 3314 # fix gray color labels and title to white or black 3315 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3316 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3317 c = (0.9, 0.9, 0.9) 3318 if np.sum(self.renderer.GetBackground()) > 1.5: 3319 c = (0.1, 0.1, 0.1) 3320 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3321 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3322 except AttributeError: 3323 pass 3324 3325 if self.sharecam: 3326 for r in self.renderers: 3327 r.SetActiveCamera(self.camera) 3328 3329 if self.axes is not None: 3330 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3331 bns = self.renderer.ComputeVisiblePropBounds() 3332 addons.add_global_axes(self.axes, bounds=bns) 3333 3334 # Backend ############################################################### 3335 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3336 return backends.get_notebook_backend() 3337 ######################################################################### 3338 3339 if self.resetcam: 3340 self.renderer.ResetCamera() 3341 3342 if len(self.renderers) > 1: 3343 self.add_renderer_frame() 3344 3345 if vedo.settings.default_backend == "2d" and not zoom: 3346 zoom = "tightest" 3347 3348 if zoom: 3349 if zoom == "tight": 3350 self.reset_camera(tight=0.04) 3351 elif zoom == "tightest": 3352 self.reset_camera(tight=0.0001) 3353 else: 3354 self.camera.Zoom(zoom) 3355 if elevation: 3356 self.camera.Elevation(elevation) 3357 if azimuth: 3358 self.camera.Azimuth(azimuth) 3359 if roll: 3360 self.camera.Roll(roll) 3361 3362 if len(viewup) > 0: 3363 b = self.renderer.ComputeVisiblePropBounds() 3364 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3365 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3366 if viewup == "x": 3367 sz = np.linalg.norm(sz) 3368 self.camera.SetViewUp([1, 0, 0]) 3369 self.camera.SetPosition(cm + sz) 3370 elif viewup == "y": 3371 sz = np.linalg.norm(sz) 3372 self.camera.SetViewUp([0, 1, 0]) 3373 self.camera.SetPosition(cm + sz) 3374 elif viewup == "z": 3375 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3376 self.camera.SetViewUp([0, 0, 1]) 3377 self.camera.SetPosition(cm + 2 * sz) 3378 elif utils.is_sequence(viewup): 3379 sz = np.linalg.norm(sz) 3380 self.camera.SetViewUp(viewup) 3381 cpos = np.cross([0, 1, 0], viewup) 3382 self.camera.SetPosition(cm - 2 * sz * cpos) 3383 3384 self.renderer.ResetCameraClippingRange() 3385 3386 self.initialize_interactor() 3387 3388 if vedo.settings.immediate_rendering: 3389 self.window.Render() ##################### <-------------- Render 3390 3391 if self.interactor: # can be offscreen or not the vtk backend.. 3392 3393 self.window.SetWindowName(self.title) 3394 3395 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3396 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3397 # print(pic.dataset)# Array 0 name PNGImage 3398 # self.window.SetIcon(pic.dataset) 3399 3400 try: 3401 # Needs "pip install pyobjc" on Mac OSX 3402 if ( 3403 self._cocoa_initialized is False 3404 and "Darwin" in vedo.sys_platform 3405 and not self.offscreen 3406 ): 3407 self._cocoa_initialized = True 3408 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3409 pid = os.getpid() 3410 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3411 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3412 except: 3413 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3414 pass 3415 3416 # Set the interaction style 3417 if mode is not None: 3418 self.user_mode(mode) 3419 if self.qt_widget and mode is None: 3420 self.user_mode(0) 3421 3422 if screenshot: 3423 self.screenshot(screenshot) 3424 3425 if self._interactive: 3426 self.interactor.Start() 3427 if self._must_close_now: 3428 self.interactor.GetRenderWindow().Finalize() 3429 self.interactor.TerminateApp() 3430 self.camera = None 3431 self.renderer = None 3432 self.renderers = [] 3433 self.window = None 3434 self.interactor = None 3435 return self 3436 3437 if rate: 3438 if self.clock is None: # set clock and limit rate 3439 self._clockt0 = time.time() 3440 self.clock = 0.0 3441 else: 3442 t = time.time() - self._clockt0 3443 elapsed = t - self.clock 3444 mint = 1.0 / rate 3445 if elapsed < mint: 3446 time.sleep(mint - elapsed) 3447 self.clock = time.time() - self._clockt0 3448 3449 # 2d #################################################################### 3450 if vedo.settings.default_backend == "2d": 3451 return backends.get_notebook_backend() 3452 ######################################################################### 3453 3454 return self 3455 3456 3457 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3458 """Add a draggable inset space into a renderer. 3459 3460 Arguments: 3461 at : (int) 3462 specify the renderer number 3463 pos : (list) 3464 icon position in the range [1-4] indicating one of the 4 corners, 3465 or it can be a tuple (x,y) as a fraction of the renderer size. 3466 size : (float) 3467 size of the square inset 3468 draggable : (bool) 3469 if True the subrenderer space can be dragged around 3470 c : (color) 3471 color of the inset frame when dragged 3472 3473 Examples: 3474 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3475 3476 ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg) 3477 """ 3478 if not self.interactor: 3479 return None 3480 3481 if not self.renderer: 3482 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3483 return None 3484 3485 options = dict(options) 3486 pos = options.pop("pos", 0) 3487 size = options.pop("size", 0.1) 3488 c = options.pop("c", "lb") 3489 at = options.pop("at", None) 3490 draggable = options.pop("draggable", True) 3491 3492 widget = vtki.vtkOrientationMarkerWidget() 3493 r, g, b = vedo.get_color(c) 3494 widget.SetOutlineColor(r, g, b) 3495 if len(objects) == 1: 3496 widget.SetOrientationMarker(objects[0].actor) 3497 else: 3498 widget.SetOrientationMarker(vedo.Assembly(objects)) 3499 3500 widget.SetInteractor(self.interactor) 3501 3502 if utils.is_sequence(pos): 3503 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3504 else: 3505 if pos < 2: 3506 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3507 elif pos == 2: 3508 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3509 elif pos == 3: 3510 widget.SetViewport(0, 0, size * 2, size * 2) 3511 elif pos == 4: 3512 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3513 widget.EnabledOn() 3514 widget.SetInteractive(draggable) 3515 if at is not None and at < len(self.renderers): 3516 widget.SetCurrentRenderer(self.renderers[at]) 3517 else: 3518 widget.SetCurrentRenderer(self.renderer) 3519 self.widgets.append(widget) 3520 return widget 3521 3522 def clear(self, at=None, deep=False) -> "Plotter": 3523 """Clear the scene from all meshes and volumes.""" 3524 if at is not None: 3525 renderer = self.renderers[at] 3526 else: 3527 renderer = self.renderer 3528 if not renderer: 3529 return self 3530 3531 if deep: 3532 renderer.RemoveAllViewProps() 3533 else: 3534 for ob in set( 3535 self.get_meshes() 3536 + self.get_volumes() 3537 + self.objects 3538 + self.axes_instances 3539 ): 3540 if isinstance(ob, vedo.shapes.Text2D): 3541 continue 3542 self.remove(ob) 3543 try: 3544 if ob.scalarbar: 3545 self.remove(ob.scalarbar) 3546 except AttributeError: 3547 pass 3548 return self 3549 3550 def break_interaction(self) -> "Plotter": 3551 """Break window interaction and return to the python execution flow""" 3552 if self.interactor: 3553 self.check_actors_trasform() 3554 self.interactor.ExitCallback() 3555 return self 3556 3557 def user_mode(self, mode) -> Union["Plotter", None]: 3558 """ 3559 Modify the user interaction mode. 3560 3561 Examples: 3562 ```python 3563 from vedo import * 3564 mode = interactor_modes.MousePan() 3565 mesh = Mesh(dataurl+"cow.vtk") 3566 plt = Plotter().user_mode(mode) 3567 plt.show(mesh, axes=1) 3568 ``` 3569 See also: 3570 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3571 """ 3572 if not self.interactor: 3573 return None 3574 3575 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3576 # print("Current style:", curr_style) 3577 if curr_style.endswith("Actor"): 3578 self.check_actors_trasform() 3579 3580 if isinstance(mode, (str, int)): 3581 # Set the style of interaction 3582 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3583 if mode in (0, "TrackballCamera"): 3584 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3585 self.interactor.RemoveObservers("CharEvent") 3586 elif mode in (1, "TrackballActor"): 3587 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3588 elif mode in (2, "JoystickCamera"): 3589 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3590 elif mode in (3, "JoystickActor"): 3591 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3592 elif mode in (4, "Flight"): 3593 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3594 elif mode in (5, "RubberBand2D"): 3595 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3596 elif mode in (6, "RubberBand3D"): 3597 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3598 elif mode in (7, "RubberBandZoom"): 3599 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3600 elif mode in (8, "Terrain"): 3601 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3602 elif mode in (9, "Unicam"): 3603 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3604 elif mode in (10, "Image", "image", "2d"): 3605 astyle = vtki.new("InteractorStyleImage") 3606 astyle.SetInteractionModeToImage3D() 3607 self.interactor.SetInteractorStyle(astyle) 3608 else: 3609 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3610 3611 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3612 # set a custom interactor style 3613 if hasattr(mode, "interactor"): 3614 mode.interactor = self.interactor 3615 mode.renderer = self.renderer # type: ignore 3616 mode.SetInteractor(self.interactor) 3617 mode.SetDefaultRenderer(self.renderer) 3618 self.interactor.SetInteractorStyle(mode) 3619 3620 return self 3621 3622 def close(self) -> "Plotter": 3623 """Close the plotter.""" 3624 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3625 vedo.last_figure = None 3626 self.last_event = None 3627 self.sliders = [] 3628 self.buttons = [] 3629 self.widgets = [] 3630 self.hover_legends = [] 3631 self.background_renderer = None 3632 self._extralight = None 3633 3634 self.hint_widget = None 3635 self.cutter_widget = None 3636 3637 if vedo.settings.dry_run_mode >= 2: 3638 return self 3639 3640 if not hasattr(self, "window"): 3641 return self 3642 if not self.window: 3643 return self 3644 if not hasattr(self, "interactor"): 3645 return self 3646 if not self.interactor: 3647 return self 3648 3649 ################################################### 3650 try: 3651 if "Darwin" in vedo.sys_platform: 3652 self.interactor.ProcessEvents() 3653 except: 3654 pass 3655 3656 self._must_close_now = True 3657 3658 if vedo.plotter_instance == self: 3659 vedo.plotter_instance = None 3660 3661 if self.interactor and self._interactive: 3662 self.break_interaction() 3663 elif self._must_close_now: 3664 # dont call ExitCallback here 3665 self.interactor.GetRenderWindow().Finalize() 3666 self.interactor.TerminateApp() 3667 self.camera = None 3668 self.renderer = None 3669 self.renderers = [] 3670 self.window = None 3671 self.interactor = None 3672 return self 3673 3674 @property 3675 def camera(self): 3676 """Return the current active camera.""" 3677 if self.renderer: 3678 return self.renderer.GetActiveCamera() 3679 3680 @camera.setter 3681 def camera(self, cam): 3682 if self.renderer: 3683 if isinstance(cam, dict): 3684 cam = utils.camera_from_dict(cam) 3685 self.renderer.SetActiveCamera(cam) 3686 3687 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3688 """ 3689 Take a screenshot of the Plotter window. 3690 3691 Arguments: 3692 scale : (int) 3693 set image magnification as an integer multiplicating factor 3694 asarray : (bool) 3695 return a numpy array of the image instead of writing a file 3696 3697 Warning: 3698 If you get black screenshots try to set `interactive=False` in `show()` 3699 then call `screenshot()` and `plt.interactive()` afterwards. 3700 3701 Example: 3702 ```py 3703 from vedo import * 3704 sphere = Sphere().linewidth(1) 3705 plt = show(sphere, interactive=False) 3706 plt.screenshot('image.png') 3707 plt.interactive() 3708 plt.close() 3709 ``` 3710 3711 Example: 3712 ```py 3713 from vedo import * 3714 sphere = Sphere().linewidth(1) 3715 plt = show(sphere, interactive=False) 3716 plt.screenshot('anotherimage.png') 3717 plt.interactive() 3718 plt.close() 3719 ``` 3720 """ 3721 return vedo.file_io.screenshot(filename, scale, asarray) 3722 3723 def toimage(self, scale=1) -> "vedo.image.Image": 3724 """ 3725 Generate a `Image` object from the current rendering window. 3726 3727 Arguments: 3728 scale : (int) 3729 set image magnification as an integer multiplicating factor 3730 """ 3731 if vedo.settings.screeshot_large_image: 3732 w2if = vtki.new("RenderLargeImage") 3733 w2if.SetInput(self.renderer) 3734 w2if.SetMagnification(scale) 3735 else: 3736 w2if = vtki.new("WindowToImageFilter") 3737 w2if.SetInput(self.window) 3738 if hasattr(w2if, "SetScale"): 3739 w2if.SetScale(scale, scale) 3740 if vedo.settings.screenshot_transparent_background: 3741 w2if.SetInputBufferTypeToRGBA() 3742 w2if.ReadFrontBufferOff() # read from the back buffer 3743 w2if.Update() 3744 return vedo.image.Image(w2if.GetOutput()) 3745 3746 def export(self, filename="scene.npz", binary=False) -> "Plotter": 3747 """ 3748 Export scene to file to HTML, X3D or Numpy file. 3749 3750 Examples: 3751 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3752 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3753 """ 3754 vedo.file_io.export_window(filename, binary=binary) 3755 return self 3756 3757 def color_picker(self, xy, verbose=False): 3758 """Pick color of specific (x,y) pixel on the screen.""" 3759 w2if = vtki.new("WindowToImageFilter") 3760 w2if.SetInput(self.window) 3761 w2if.ReadFrontBufferOff() 3762 w2if.Update() 3763 nx, ny = self.window.GetSize() 3764 varr = w2if.GetOutput().GetPointData().GetScalars() 3765 3766 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3767 x, y = int(xy[0]), int(xy[1]) 3768 if y < ny and x < nx: 3769 3770 rgb = arr[y, x] 3771 3772 if verbose: 3773 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3774 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3775 vedo.printc("█", c=[0, rgb[1], 0], end="") 3776 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3777 vedo.printc("] = ", end="") 3778 cnm = vedo.get_color_name(rgb) 3779 if np.sum(rgb) < 150: 3780 vedo.printc( 3781 rgb.tolist(), 3782 vedo.colors.rgb2hex(np.array(rgb) / 255), 3783 c="w", 3784 bc=rgb, 3785 invert=1, 3786 end="", 3787 ) 3788 vedo.printc(" -> " + cnm, invert=1, c="w") 3789 else: 3790 vedo.printc( 3791 rgb.tolist(), 3792 vedo.colors.rgb2hex(np.array(rgb) / 255), 3793 c=rgb, 3794 end="", 3795 ) 3796 vedo.printc(" -> " + cnm, c=cnm) 3797 3798 return rgb 3799 3800 return None 3801 3802 ####################################################################### 3803 def _default_mouseleftclick(self, iren, event) -> None: 3804 x, y = iren.GetEventPosition() 3805 renderer = iren.FindPokedRenderer(x, y) 3806 picker = vtki.vtkPropPicker() 3807 picker.PickProp(x, y, renderer) 3808 3809 self.renderer = renderer 3810 3811 clicked_actor = picker.GetActor() 3812 # clicked_actor2D = picker.GetActor2D() 3813 3814 # print('_default_mouseleftclick mouse at', x, y) 3815 # print("picked Volume:", [picker.GetVolume()]) 3816 # print("picked Actor2D:", [picker.GetActor2D()]) 3817 # print("picked Assembly:", [picker.GetAssembly()]) 3818 # print("picked Prop3D:", [picker.GetProp3D()]) 3819 3820 if not clicked_actor: 3821 clicked_actor = picker.GetAssembly() 3822 3823 if not clicked_actor: 3824 clicked_actor = picker.GetProp3D() 3825 3826 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3827 return 3828 3829 self.picked3d = picker.GetPickPosition() 3830 self.picked2d = np.array([x, y]) 3831 3832 if not clicked_actor: 3833 return 3834 3835 self.justremoved = None 3836 self.clicked_actor = clicked_actor 3837 3838 try: # might not be a vedo obj 3839 self.clicked_object = clicked_actor.retrieve_object() 3840 # save this info in the object itself 3841 self.clicked_object.picked3d = self.picked3d 3842 self.clicked_object.picked2d = self.picked2d 3843 except AttributeError: 3844 pass 3845 3846 # ----------- 3847 # if "Histogram1D" in picker.GetAssembly().__class__.__name__: 3848 # histo = picker.GetAssembly() 3849 # if histo.verbose: 3850 # x = self.picked3d[0] 3851 # idx = np.digitize(x, histo.edges) - 1 3852 # f = histo.frequencies[idx] 3853 # cn = histo.centers[idx] 3854 # vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3855 3856 ####################################################################### 3857 def _default_keypress(self, iren, event) -> None: 3858 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3859 3860 key = iren.GetKeySym() 3861 3862 if "_L" in key or "_R" in key: 3863 return 3864 3865 if iren.GetShiftKey(): 3866 key = key.upper() 3867 3868 if iren.GetControlKey(): 3869 key = "Ctrl+" + key 3870 3871 if iren.GetAltKey(): 3872 key = "Alt+" + key 3873 3874 ####################################################### 3875 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3876 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3877 # iren.GetKeyCode(), iren.GetRepeatCount()) 3878 ####################################################### 3879 3880 x, y = iren.GetEventPosition() 3881 renderer = iren.FindPokedRenderer(x, y) 3882 3883 if key in ["q", "Return"]: 3884 self.break_interaction() 3885 return 3886 3887 elif key in ["Ctrl+q", "Ctrl+w", "Escape"]: 3888 self.close() 3889 return 3890 3891 elif key == "F1": 3892 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3893 self.break_interaction() 3894 sys.exit(0) 3895 3896 elif key == "Down": 3897 if self.clicked_object and self.clicked_object in self.get_meshes(): 3898 self.clicked_object.alpha(0.02) 3899 if hasattr(self.clicked_object, "properties_backface"): 3900 bfp = self.clicked_actor.GetBackfaceProperty() 3901 self.clicked_object.properties_backface = bfp # save it 3902 self.clicked_actor.SetBackfaceProperty(None) 3903 else: 3904 for obj in self.get_meshes(): 3905 if obj: 3906 obj.alpha(0.02) 3907 bfp = obj.actor.GetBackfaceProperty() 3908 if bfp and hasattr(obj, "properties_backface"): 3909 obj.properties_backface = bfp 3910 obj.actor.SetBackfaceProperty(None) 3911 3912 elif key == "Left": 3913 if self.clicked_object and self.clicked_object in self.get_meshes(): 3914 ap = self.clicked_object.properties 3915 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3916 ap.SetOpacity(aal) 3917 bfp = self.clicked_actor.GetBackfaceProperty() 3918 if bfp and hasattr(self.clicked_object, "properties_backface"): 3919 self.clicked_object.properties_backface = bfp 3920 self.clicked_actor.SetBackfaceProperty(None) 3921 else: 3922 for a in self.get_meshes(): 3923 if a: 3924 ap = a.properties 3925 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3926 ap.SetOpacity(aal) 3927 bfp = a.actor.GetBackfaceProperty() 3928 if bfp and hasattr(a, "properties_backface"): 3929 a.properties_backface = bfp 3930 a.actor.SetBackfaceProperty(None) 3931 3932 elif key == "Right": 3933 if self.clicked_object and self.clicked_object in self.get_meshes(): 3934 ap = self.clicked_object.properties 3935 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3936 ap.SetOpacity(aal) 3937 if ( 3938 aal == 1 3939 and hasattr(self.clicked_object, "properties_backface") 3940 and self.clicked_object.properties_backface 3941 ): 3942 # put back 3943 self.clicked_actor.SetBackfaceProperty( 3944 self.clicked_object.properties_backface) 3945 else: 3946 for a in self.get_meshes(): 3947 if a: 3948 ap = a.properties 3949 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3950 ap.SetOpacity(aal) 3951 if aal == 1 and hasattr(a, "properties_backface") and a.properties_backface: 3952 a.actor.SetBackfaceProperty(a.properties_backface) 3953 3954 elif key == "Up": 3955 if self.clicked_object and self.clicked_object in self.get_meshes(): 3956 self.clicked_object.properties.SetOpacity(1) 3957 if hasattr(self.clicked_object, "properties_backface") and self.clicked_object.properties_backface: 3958 self.clicked_object.actor.SetBackfaceProperty(self.clicked_object.properties_backface) 3959 else: 3960 for a in self.get_meshes(): 3961 if a: 3962 a.properties.SetOpacity(1) 3963 if hasattr(a, "properties_backface") and a.properties_backface: 3964 a.actor.SetBackfaceProperty(a.properties_backface) 3965 3966 elif key == "P": 3967 if self.clicked_object and self.clicked_object in self.get_meshes(): 3968 objs = [self.clicked_object] 3969 else: 3970 objs = self.get_meshes() 3971 for ia in objs: 3972 try: 3973 ps = ia.properties.GetPointSize() 3974 if ps > 1: 3975 ia.properties.SetPointSize(ps - 1) 3976 ia.properties.SetRepresentationToPoints() 3977 except AttributeError: 3978 pass 3979 3980 elif key == "p": 3981 if self.clicked_object and self.clicked_object in self.get_meshes(): 3982 objs = [self.clicked_object] 3983 else: 3984 objs = self.get_meshes() 3985 for ia in objs: 3986 try: 3987 ps = ia.properties.GetPointSize() 3988 ia.properties.SetPointSize(ps + 2) 3989 ia.properties.SetRepresentationToPoints() 3990 except AttributeError: 3991 pass 3992 3993 elif key == "U": 3994 pval = renderer.GetActiveCamera().GetParallelProjection() 3995 renderer.GetActiveCamera().SetParallelProjection(not pval) 3996 if pval: 3997 renderer.ResetCamera() 3998 3999 elif key == "r": 4000 renderer.ResetCamera() 4001 4002 elif key == "h": 4003 msg = f" vedo {vedo.__version__}" 4004 msg += f" | vtk {vtki.vtkVersion().GetVTKVersion()}" 4005 msg += f" | numpy {np.__version__}" 4006 msg += f" | python {sys.version_info[0]}.{sys.version_info[1]}, press: " 4007 vedo.printc(msg.ljust(75), invert=True) 4008 msg = ( 4009 " i print info about the last clicked object \n" 4010 " I print color of the pixel under the mouse \n" 4011 " Y show the pipeline for this object as a graph \n" 4012 " <- -> use arrows to reduce/increase opacity \n" 4013 " x toggle mesh visibility \n" 4014 " w toggle wireframe/surface style \n" 4015 " l toggle surface edges visibility \n" 4016 " p/P hide surface faces and show only points \n" 4017 " 1-3 cycle surface color (2=light, 3=dark) \n" 4018 " 4 cycle color map (press shift-4 to go back) \n" 4019 " 5-6 cycle point-cell arrays (shift to go back) \n" 4020 " 7-8 cycle background and gradient color \n" 4021 " 09+- cycle axes styles (on keypad, or press +/-) \n" 4022 " k cycle available lighting styles \n" 4023 " K toggle shading as flat or phong \n" 4024 " A toggle anti-aliasing \n" 4025 " D toggle depth-peeling (for transparencies) \n" 4026 " U toggle perspective/parallel projection \n" 4027 " o/O toggle extra light to scene and rotate it \n" 4028 " a toggle interaction to Actor Mode \n" 4029 " n toggle surface normals \n" 4030 " r reset camera position \n" 4031 " R reset camera to the closest orthogonal view \n" 4032 " . fly camera to the last clicked point \n" 4033 " C print the current camera parameters state \n" 4034 " X invoke a cutter widget tool \n" 4035 " S save a screenshot of the current scene \n" 4036 " E/F export 3D scene to numpy file or X3D \n" 4037 " q return control to python script \n" 4038 " Esc abort execution and exit python kernel " 4039 ) 4040 vedo.printc(msg, dim=True, italic=True, bold=True) 4041 vedo.printc( 4042 " Check out the documentation at: https://vedo.embl.es ".ljust(75), 4043 invert=True, 4044 bold=True, 4045 ) 4046 return 4047 4048 elif key == "a": 4049 cur = iren.GetInteractorStyle() 4050 if isinstance(cur, vtki.get_class("InteractorStyleTrackballCamera")): 4051 msg = "Interactor style changed to TrackballActor\n" 4052 msg += " you can now move and rotate individual meshes:\n" 4053 msg += " press X twice to save the repositioned mesh\n" 4054 msg += " press 'a' to go back to normal style" 4055 vedo.printc(msg) 4056 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 4057 else: 4058 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 4059 return 4060 4061 elif key == "A": # toggle antialiasing 4062 msam = self.window.GetMultiSamples() 4063 if not msam: 4064 self.window.SetMultiSamples(16) 4065 else: 4066 self.window.SetMultiSamples(0) 4067 msam = self.window.GetMultiSamples() 4068 if msam: 4069 vedo.printc(f"Antialiasing set to {msam} samples", c=bool(msam)) 4070 else: 4071 vedo.printc("Antialiasing disabled", c=bool(msam)) 4072 4073 elif key == "D": # toggle depthpeeling 4074 udp = not renderer.GetUseDepthPeeling() 4075 renderer.SetUseDepthPeeling(udp) 4076 # self.renderer.SetUseDepthPeelingForVolumes(udp) 4077 if udp: 4078 self.window.SetAlphaBitPlanes(1) 4079 renderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 4080 renderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 4081 self.interactor.Render() 4082 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 4083 rnr = self.renderers.index(renderer) 4084 vedo.printc(f"Depth peeling set to {udp} for renderer nr.{rnr}", c=udp) 4085 if not wasUsed and udp: 4086 vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True) 4087 return 4088 4089 elif key == "period": 4090 if self.picked3d: 4091 self.fly_to(self.picked3d) 4092 return 4093 4094 elif key == "S": 4095 vedo.file_io.screenshot("screenshot.png") 4096 vedo.printc(r":camera: Saved rendering window to 'screenshot.png'", c="b") 4097 return 4098 4099 elif key == "C": 4100 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 4101 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 4102 # This could happen for plotting geological models with UTM coordinate systems 4103 cam = renderer.GetActiveCamera() 4104 vedo.printc("\n###################################################", c="y") 4105 vedo.printc("## Template python code to position this camera: ##", c="y") 4106 vedo.printc("cam = dict(", c="y") 4107 vedo.printc(" position=" + utils.precision(cam.GetPosition(), 6) + ",", c="y") 4108 vedo.printc(" focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y") 4109 vedo.printc(" viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y") 4110 vedo.printc(" roll=" + utils.precision(cam.GetRoll(), 6) + ",", c="y") 4111 if cam.GetParallelProjection(): 4112 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 4113 else: 4114 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 4115 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 4116 vedo.printc(')', c='y') 4117 vedo.printc('show(mymeshes, camera=cam)', c='y') 4118 vedo.printc('###################################################', c='y') 4119 return 4120 4121 elif key == "R": 4122 self.reset_viewup() 4123 4124 elif key == "w": 4125 try: 4126 if self.clicked_object.properties.GetRepresentation() == 1: # toggle 4127 self.clicked_object.properties.SetRepresentationToSurface() 4128 else: 4129 self.clicked_object.properties.SetRepresentationToWireframe() 4130 except AttributeError: 4131 pass 4132 4133 elif key == "1": 4134 try: 4135 self._icol += 1 4136 self.clicked_object.mapper.ScalarVisibilityOff() 4137 pal = vedo.colors.palettes[vedo.settings.palette % len(vedo.colors.palettes)] 4138 self.clicked_object.c(pal[(self._icol) % 10]) 4139 self.remove(self.clicked_object.scalarbar) 4140 except AttributeError: 4141 pass 4142 4143 elif key == "2": # dark colors 4144 try: 4145 bsc = ["k1", "k2", "k3", "k4", 4146 "b1", "b2", "b3", "b4", 4147 "p1", "p2", "p3", "p4", 4148 "g1", "g2", "g3", "g4", 4149 "r1", "r2", "r3", "r4", 4150 "o1", "o2", "o3", "o4", 4151 "y1", "y2", "y3", "y4"] 4152 self._icol += 1 4153 if self.clicked_object: 4154 self.clicked_object.mapper.ScalarVisibilityOff() 4155 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4156 self.clicked_object.c(newcol) 4157 self.remove(self.clicked_object.scalarbar) 4158 except AttributeError: 4159 pass 4160 4161 elif key == "3": # light colors 4162 try: 4163 bsc = ["k6", "k7", "k8", "k9", 4164 "b6", "b7", "b8", "b9", 4165 "p6", "p7", "p8", "p9", 4166 "g6", "g7", "g8", "g9", 4167 "r6", "r7", "r8", "r9", 4168 "o6", "o7", "o8", "o9", 4169 "y6", "y7", "y8", "y9"] 4170 self._icol += 1 4171 if self.clicked_object: 4172 self.clicked_object.mapper.ScalarVisibilityOff() 4173 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4174 self.clicked_object.c(newcol) 4175 self.remove(self.clicked_object.scalarbar) 4176 except AttributeError: 4177 pass 4178 4179 elif key == "4": # cmap name cycle 4180 ob = self.clicked_object 4181 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4182 return 4183 if not ob.mapper.GetScalarVisibility(): 4184 return 4185 onwhat = ob.mapper.GetScalarModeAsString() # UsePointData/UseCellData 4186 4187 cmap_names = [ 4188 "Accent", "Paired", 4189 "rainbow", "rainbow_r", 4190 "Spectral", "Spectral_r", 4191 "gist_ncar", "gist_ncar_r", 4192 "viridis", "viridis_r", 4193 "hot", "hot_r", 4194 "terrain", "ocean", 4195 "coolwarm", "seismic", "PuOr", "RdYlGn", 4196 ] 4197 try: 4198 i = cmap_names.index(ob._cmap_name) 4199 if iren.GetShiftKey(): 4200 i -= 1 4201 else: 4202 i += 1 4203 if i >= len(cmap_names): 4204 i = 0 4205 if i < 0: 4206 i = len(cmap_names) - 1 4207 except ValueError: 4208 i = 0 4209 4210 ob._cmap_name = cmap_names[i] 4211 ob.cmap(ob._cmap_name, on=onwhat) 4212 if ob.scalarbar: 4213 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4214 self.remove(ob.scalarbar) 4215 title = ob.scalarbar.GetTitle() 4216 ob.add_scalarbar(title=title) 4217 self.add(ob.scalarbar).render() 4218 elif isinstance(ob.scalarbar, vedo.Assembly): 4219 self.remove(ob.scalarbar) 4220 ob.add_scalarbar3d(title=ob._cmap_name) 4221 self.add(ob.scalarbar) 4222 4223 vedo.printc( 4224 f"Name:'{ob.name}'," if ob.name else "", 4225 f"range:{utils.precision(ob.mapper.GetScalarRange(),3)},", 4226 f"colormap:'{ob._cmap_name}'", c="g", bold=False, 4227 ) 4228 4229 elif key == "5": # cycle pointdata array 4230 ob = self.clicked_object 4231 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4232 return 4233 4234 arrnames = ob.pointdata.keys() 4235 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4236 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4237 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4238 if len(arrnames) == 0: 4239 return 4240 ob.mapper.SetScalarVisibility(1) 4241 4242 if not ob._cmap_name: 4243 ob._cmap_name = "rainbow" 4244 4245 try: 4246 curr_name = ob.dataset.GetPointData().GetScalars().GetName() 4247 i = arrnames.index(curr_name) 4248 if "normals" in curr_name.lower(): 4249 return 4250 if iren.GetShiftKey(): 4251 i -= 1 4252 else: 4253 i += 1 4254 if i >= len(arrnames): 4255 i = 0 4256 if i < 0: 4257 i = len(arrnames) - 1 4258 except (ValueError, AttributeError): 4259 i = 0 4260 4261 ob.cmap(ob._cmap_name, arrnames[i], on="points") 4262 if ob.scalarbar: 4263 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4264 self.remove(ob.scalarbar) 4265 title = ob.scalarbar.GetTitle() 4266 ob.scalarbar = None 4267 ob.add_scalarbar(title=arrnames[i]) 4268 self.add(ob.scalarbar) 4269 elif isinstance(ob.scalarbar, vedo.Assembly): 4270 self.remove(ob.scalarbar) 4271 ob.scalarbar = None 4272 ob.add_scalarbar3d(title=arrnames[i]) 4273 self.add(ob.scalarbar) 4274 else: 4275 vedo.printc( 4276 f"Name:'{ob.name}'," if ob.name else "", 4277 f"active pointdata array: '{arrnames[i]}'", 4278 c="g", bold=False, 4279 ) 4280 4281 elif key == "6": # cycle celldata array 4282 ob = self.clicked_object 4283 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4284 return 4285 4286 arrnames = ob.celldata.keys() 4287 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4288 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4289 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4290 if len(arrnames) == 0: 4291 return 4292 ob.mapper.SetScalarVisibility(1) 4293 4294 if not ob._cmap_name: 4295 ob._cmap_name = "rainbow" 4296 4297 try: 4298 curr_name = ob.dataset.GetCellData().GetScalars().GetName() 4299 i = arrnames.index(curr_name) 4300 if "normals" in curr_name.lower(): 4301 return 4302 if iren.GetShiftKey(): 4303 i -= 1 4304 else: 4305 i += 1 4306 if i >= len(arrnames): 4307 i = 0 4308 if i < 0: 4309 i = len(arrnames) - 1 4310 except (ValueError, AttributeError): 4311 i = 0 4312 4313 ob.cmap(ob._cmap_name, arrnames[i], on="cells") 4314 if ob.scalarbar: 4315 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4316 self.remove(ob.scalarbar) 4317 title = ob.scalarbar.GetTitle() 4318 ob.scalarbar = None 4319 ob.add_scalarbar(title=arrnames[i]) 4320 self.add(ob.scalarbar) 4321 elif isinstance(ob.scalarbar, vedo.Assembly): 4322 self.remove(ob.scalarbar) 4323 ob.scalarbar = None 4324 ob.add_scalarbar3d(title=arrnames[i]) 4325 self.add(ob.scalarbar) 4326 else: 4327 vedo.printc( 4328 f"Name:'{ob.name}'," if ob.name else "", 4329 f"active celldata array: '{arrnames[i]}'", 4330 c="g", bold=False, 4331 ) 4332 4333 elif key == "7": 4334 bgc = np.array(renderer.GetBackground()).sum() / 3 4335 if bgc <= 0: 4336 bgc = 0.223 4337 elif 0 < bgc < 1: 4338 bgc = 1 4339 else: 4340 bgc = 0 4341 renderer.SetBackground(bgc, bgc, bgc) 4342 4343 elif key == "8": 4344 bg2cols = [ 4345 "lightyellow", 4346 "darkseagreen", 4347 "palegreen", 4348 "steelblue", 4349 "lightblue", 4350 "cadetblue", 4351 "lavender", 4352 "white", 4353 "blackboard", 4354 "black", 4355 ] 4356 bg2name = vedo.get_color_name(renderer.GetBackground2()) 4357 if bg2name in bg2cols: 4358 idx = bg2cols.index(bg2name) 4359 else: 4360 idx = 4 4361 if idx is not None: 4362 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 4363 if not bg2name_next: 4364 renderer.GradientBackgroundOff() 4365 else: 4366 renderer.GradientBackgroundOn() 4367 renderer.SetBackground2(vedo.get_color(bg2name_next)) 4368 4369 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 4370 i = self.renderers.index(renderer) 4371 try: 4372 self.axes_instances[i].EnabledOff() 4373 self.axes_instances[i].SetInteractor(None) 4374 except AttributeError: 4375 # print("Cannot remove widget", [self.axes_instances[i]]) 4376 try: 4377 self.remove(self.axes_instances[i]) 4378 except: 4379 print("Cannot remove axes", [self.axes_instances[i]]) 4380 return 4381 self.axes_instances[i] = None 4382 4383 if not self.axes: 4384 self.axes = 0 4385 if isinstance(self.axes, dict): 4386 self.axes = 1 4387 4388 if key in ["minus", "KP_Subtract"]: 4389 if not self.camera.GetParallelProjection() and self.axes == 0: 4390 self.axes -= 1 # jump ruler doesnt make sense in perspective mode 4391 bns = self.renderer.ComputeVisiblePropBounds() 4392 addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None, bounds=bns) 4393 else: 4394 if not self.camera.GetParallelProjection() and self.axes == 12: 4395 self.axes += 1 # jump ruler doesnt make sense in perspective mode 4396 bns = self.renderer.ComputeVisiblePropBounds() 4397 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns) 4398 self.render() 4399 4400 elif "KP_" in key or key in [ 4401 "Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior" 4402 ]: 4403 asso = { # change axes style 4404 "KP_Insert": 0, "KP_0": 0, "Insert": 0, 4405 "KP_End": 1, "KP_1": 1, "End": 1, 4406 "KP_Down": 2, "KP_2": 2, "Down": 2, 4407 "KP_Next": 3, "KP_3": 3, "Next": 3, 4408 "KP_Left": 4, "KP_4": 4, "Left": 4, 4409 "KP_Begin": 5, "KP_5": 5, "Begin": 5, 4410 "KP_Right": 6, "KP_6": 6, "Right": 6, 4411 "KP_Home": 7, "KP_7": 7, "Home": 7, 4412 "KP_Up": 8, "KP_8": 8, "Up": 8, 4413 "Prior": 9, # on windows OS 4414 } 4415 clickedr = self.renderers.index(renderer) 4416 if key in asso: 4417 if self.axes_instances[clickedr]: 4418 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 4419 self.axes_instances[clickedr].EnabledOff() 4420 else: 4421 try: 4422 renderer.RemoveActor(self.axes_instances[clickedr]) 4423 except: 4424 pass 4425 self.axes_instances[clickedr] = None 4426 bounds = renderer.ComputeVisiblePropBounds() 4427 addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds) 4428 self.interactor.Render() 4429 4430 if key == "O": 4431 renderer.RemoveLight(self._extralight) 4432 self._extralight = None 4433 4434 elif key == "o": 4435 vbb, sizes, _, _ = addons.compute_visible_bounds() 4436 cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2) 4437 if not self._extralight: 4438 vup = renderer.GetActiveCamera().GetViewUp() 4439 pos = cm + utils.vector(vup) * utils.mag(sizes) 4440 self._extralight = addons.Light(pos, focal_point=cm, intensity=0.4) 4441 renderer.AddLight(self._extralight) 4442 vedo.printc("Press 'o' again to rotate light source, or 'O' to remove it.", c='y') 4443 else: 4444 cpos = utils.vector(self._extralight.GetPosition()) 4445 x, y, z = self._extralight.GetPosition() - cm 4446 r, th, ph = transformations.cart2spher(x, y, z) 4447 th += 0.2 4448 if th > np.pi: 4449 th = np.random.random() * np.pi / 2 4450 ph += 0.3 4451 cpos = transformations.spher2cart(r, th, ph).T + cm 4452 self._extralight.SetPosition(cpos) 4453 4454 elif key == "l": 4455 if self.clicked_object in self.get_meshes(): 4456 objs = [self.clicked_object] 4457 else: 4458 objs = self.get_meshes() 4459 for ia in objs: 4460 try: 4461 ev = ia.properties.GetEdgeVisibility() 4462 ia.properties.SetEdgeVisibility(not ev) 4463 ia.properties.SetRepresentationToSurface() 4464 ia.properties.SetLineWidth(0.1) 4465 except AttributeError: 4466 pass 4467 4468 elif key == "k": # lightings 4469 if self.clicked_object in self.get_meshes(): 4470 objs = [self.clicked_object] 4471 else: 4472 objs = self.get_meshes() 4473 shds = ("default", "metallic", "plastic", "shiny", "glossy", "off") 4474 for ia in objs: 4475 try: 4476 lnr = (ia._ligthingnr + 1) % 6 4477 ia.lighting(shds[lnr]) 4478 ia._ligthingnr = lnr 4479 except AttributeError: 4480 pass 4481 4482 elif key == "K": # shading 4483 if self.clicked_object in self.get_meshes(): 4484 objs = [self.clicked_object] 4485 else: 4486 objs = self.get_meshes() 4487 for ia in objs: 4488 if isinstance(ia, vedo.Mesh): 4489 ia.compute_normals(cells=False) 4490 intrp = ia.properties.GetInterpolation() 4491 if intrp > 0: 4492 ia.properties.SetInterpolation(0) # flat 4493 else: 4494 ia.properties.SetInterpolation(2) # phong 4495 4496 elif key == "n": # show normals to an actor 4497 self.remove("added_auto_normals") 4498 if self.clicked_object in self.get_meshes(): 4499 if self.clicked_actor.GetPickable(): 4500 norml = vedo.shapes.NormalLines(self.clicked_object) 4501 norml.name = "added_auto_normals" 4502 self.add(norml) 4503 4504 elif key == "x": 4505 if self.justremoved is None: 4506 if self.clicked_object in self.get_meshes() or isinstance( 4507 self.clicked_object, vtki.vtkAssembly 4508 ): 4509 self.justremoved = self.clicked_actor 4510 self.renderer.RemoveActor(self.clicked_actor) 4511 else: 4512 self.renderer.AddActor(self.justremoved) 4513 self.justremoved = None 4514 4515 elif key == "X": 4516 if self.clicked_object: 4517 if not self.cutter_widget: 4518 self.cutter_widget = addons.BoxCutter(self.clicked_object) 4519 self.add(self.cutter_widget) 4520 vedo.printc("Press i to toggle the cutter on/off", c='g', dim=1) 4521 vedo.printc(" u to flip selection", c='g', dim=1) 4522 vedo.printc(" r to reset cutting planes", c='g', dim=1) 4523 vedo.printc(" Shift+X to close the cutter box widget", c='g', dim=1) 4524 vedo.printc(" Ctrl+S to save the cut section to file.", c='g', dim=1) 4525 else: 4526 self.remove(self.cutter_widget) 4527 self.cutter_widget = None 4528 vedo.printc("Click object and press X to open the cutter box widget.", c='g') 4529 4530 elif key == "E": 4531 vedo.printc(r":camera: Exporting 3D window to file scene.npz", c="b", end="") 4532 vedo.file_io.export_window("scene.npz") 4533 vedo.printc(", try:\n> vedo scene.npz # (this is experimental!)", c="b") 4534 return 4535 4536 elif key == "F": 4537 vedo.file_io.export_window("scene.x3d") 4538 vedo.printc(r":camera: Exporting 3D window to file", c="b", end="") 4539 vedo.file_io.export_window("scene.npz") 4540 vedo.printc(". Try:\n> firefox scene.html", c="b") 4541 4542 # elif key == "G": # not working with last version of k3d 4543 # vedo.file_io.export_window("scene.html") 4544 # vedo.printc(r":camera: Exporting K3D window to file", c="b", end="") 4545 # vedo.file_io.export_window("scene.html") 4546 # vedo.printc(". Try:\n> firefox scene.html", c="b") 4547 4548 elif key == "i": # print info 4549 if self.clicked_object: 4550 print(self.clicked_object) 4551 else: 4552 print(self) 4553 4554 elif key == "I": # print color under the mouse 4555 x, y = iren.GetEventPosition() 4556 self.color_picker([x, y], verbose=True) 4557 4558 elif key == "Y": 4559 if self.clicked_object and self.clicked_object.pipeline: 4560 self.clicked_object.pipeline.show() 4561 4562 if iren: 4563 iren.Render()
375class Plotter: 376 """Main class to manage objects.""" 377 378 def __init__( 379 self, 380 shape=(1, 1), 381 N=None, 382 pos=(0, 0), 383 size="auto", 384 screensize="auto", 385 title="vedo", 386 bg="white", 387 bg2=None, 388 axes=None, 389 sharecam=True, 390 resetcam=True, 391 interactive=None, 392 offscreen=False, 393 qt_widget=None, 394 wx_widget=None, 395 ): 396 """ 397 Arguments: 398 shape : (str, list) 399 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 400 N : (int) 401 number of desired renderers arranged in a grid automatically. 402 pos : (list) 403 (x,y) position in pixels of top-left corner of the rendering window on the screen 404 size : (str, list) 405 size of the rendering window. If 'auto', guess it based on screensize. 406 screensize : (list) 407 physical size of the monitor screen in pixels 408 bg : (color, str) 409 background color or specify jpg image file name with path 410 bg2 : (color) 411 background color of a gradient towards the top 412 title : (str) 413 window title 414 415 axes : (int) 416 417 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 418 Check out `vedo.addons.Axes()` for the available options. 419 420 - 0, no axes 421 - 1, draw three gray grid walls 422 - 2, show cartesian axes from (0,0,0) 423 - 3, show positive range of cartesian axes from (0,0,0) 424 - 4, show a triad at bottom left 425 - 5, show a cube at bottom left 426 - 6, mark the corners of the bounding box 427 - 7, draw a 3D ruler at each side of the cartesian axes 428 - 8, show the VTK CubeAxesActor object 429 - 9, show the bounding box outLine 430 - 10, show three circles representing the maximum bounding box 431 - 11, show a large grid on the x-y plane (use with zoom=8) 432 - 12, show polar axes 433 - 13, draw a simple ruler at the bottom of the window 434 - 14: draw a camera orientation widget 435 436 sharecam : (bool) 437 if False each renderer will have an independent camera 438 interactive : (bool) 439 if True will stop after show() to allow interaction with the 3d scene 440 offscreen : (bool) 441 if True will not show the rendering window 442 qt_widget : (QVTKRenderWindowInteractor) 443 render in a Qt-Widget using an QVTKRenderWindowInteractor. 444 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 445 """ 446 vedo.plotter_instance = self 447 448 if interactive is None: 449 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 450 self._interactive = interactive 451 # print("interactive", interactive, N, shape) 452 453 self.objects = [] # list of objects to be shown 454 self.clicked_object = None # holds the object that has been clicked 455 self.clicked_actor = None # holds the actor that has been clicked 456 457 self.shape = shape # nr. of subwindows in grid 458 self.axes = axes # show axes type nr. 459 self.title = title # window title 460 self.size = size # window size 461 self.backgrcol = bg # used also by backend notebooks 462 463 self.offscreen= offscreen 464 self.resetcam = resetcam 465 self.sharecam = sharecam # share the same camera if multiple renderers 466 self.pos = pos # used by vedo.file_io 467 468 self.picker = None # hold the vtkPicker object 469 self.picked2d = None # 2d coords of a clicked point on the rendering window 470 self.picked3d = None # 3d coords of a clicked point on an actor 471 472 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 473 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 474 self.interactor = None 475 self.window = None 476 self.renderer = None 477 self.renderers = [] # list of renderers 478 479 # mostly internal stuff: 480 self.hover_legends = [] 481 self.justremoved = None 482 self.axes_instances = [] 483 self.clock = 0 484 self.sliders = [] 485 self.buttons = [] 486 self.widgets = [] 487 self.cutter_widget = None 488 self.hint_widget = None 489 self.background_renderer = None 490 self.last_event = None 491 self.skybox = None 492 self._icol = 0 493 self._clockt0 = time.time() 494 self._extralight = None 495 self._cocoa_initialized = False 496 self._cocoa_process_events = True # make one call in show() 497 self._must_close_now = False 498 499 ##################################################################### 500 if vedo.settings.default_backend == "2d": 501 self.offscreen = True 502 if self.size == "auto": 503 self.size = (800, 600) 504 505 elif vedo.settings.default_backend == "k3d": 506 if self.size == "auto": 507 self.size = (1000, 1000) 508 #################################### 509 return ############################ 510 #################################### 511 512 ############################################################# 513 if vedo.settings.default_backend in ["vtk", "2d", "trame"]: 514 515 if screensize == "auto": 516 screensize = (2160, 1440) # TODO: get actual screen size 517 518 # build the rendering window: 519 self.window = vtki.vtkRenderWindow() 520 521 self.window.GlobalWarningDisplayOff() 522 523 if self.title == "vedo": # check if dev version 524 if "dev" in vedo.__version__: 525 self.title = f"vedo ({vedo.__version__})" 526 self.window.SetWindowName(self.title) 527 528 # more vedo.settings 529 if vedo.settings.use_depth_peeling: 530 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 531 self.window.SetMultiSamples(vedo.settings.multi_samples) 532 533 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 534 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 535 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 536 537 ############################################################# 538 if N: # N = number of renderers. Find out the best 539 540 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 541 vedo.logger.warning("having set N, shape is ignored.") 542 543 x, y = screensize 544 nx = int(np.sqrt(int(N * y / x) + 1)) 545 ny = int(np.sqrt(int(N * x / y) + 1)) 546 lm = [ 547 (nx, ny), 548 (nx, ny + 1), 549 (nx - 1, ny), 550 (nx + 1, ny), 551 (nx, ny - 1), 552 (nx - 1, ny + 1), 553 (nx + 1, ny - 1), 554 (nx + 1, ny + 1), 555 (nx - 1, ny - 1), 556 ] 557 ind, minl = 0, 1000 558 for i, m in enumerate(lm): 559 l = m[0] * m[1] 560 if N <= l < minl: 561 ind = i 562 minl = l 563 shape = lm[ind] 564 565 ################################################## 566 if isinstance(shape, str): 567 568 if "|" in shape: 569 if self.size == "auto": 570 self.size = (800, 1200) 571 n = int(shape.split("|")[0]) 572 m = int(shape.split("|")[1]) 573 rangen = reversed(range(n)) 574 rangem = reversed(range(m)) 575 else: 576 if self.size == "auto": 577 self.size = (1200, 800) 578 m = int(shape.split("/")[0]) 579 n = int(shape.split("/")[1]) 580 rangen = range(n) 581 rangem = range(m) 582 583 if n >= m: 584 xsplit = m / (n + m) 585 else: 586 xsplit = 1 - n / (n + m) 587 if vedo.settings.window_splitting_position: 588 xsplit = vedo.settings.window_splitting_position 589 590 for i in rangen: 591 arenderer = vtki.vtkRenderer() 592 if "|" in shape: 593 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 594 else: 595 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 596 self.renderers.append(arenderer) 597 598 for i in rangem: 599 arenderer = vtki.vtkRenderer() 600 601 if "|" in shape: 602 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 603 else: 604 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 605 self.renderers.append(arenderer) 606 607 for r in self.renderers: 608 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 609 610 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 611 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 612 if vedo.settings.use_depth_peeling: 613 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 614 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 615 r.SetUseFXAA(vedo.settings.use_fxaa) 616 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 617 618 r.SetBackground(vedo.get_color(self.backgrcol)) 619 620 self.axes_instances.append(None) 621 622 self.shape = (n + m,) 623 624 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 625 # passing a sequence of dicts for renderers specifications 626 627 if self.size == "auto": 628 self.size = (1000, 800) 629 630 for rd in shape: 631 x0, y0 = rd["bottomleft"] 632 x1, y1 = rd["topright"] 633 bg_ = rd.pop("bg", "white") 634 bg2_ = rd.pop("bg2", None) 635 636 arenderer = vtki.vtkRenderer() 637 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 638 639 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 640 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 641 if vedo.settings.use_depth_peeling: 642 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 643 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 644 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 645 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 646 647 arenderer.SetViewport(x0, y0, x1, y1) 648 arenderer.SetBackground(vedo.get_color(bg_)) 649 if bg2_: 650 arenderer.GradientBackgroundOn() 651 arenderer.SetBackground2(vedo.get_color(bg2_)) 652 653 self.renderers.append(arenderer) 654 self.axes_instances.append(None) 655 656 self.shape = (len(shape),) 657 658 else: 659 660 if isinstance(self.size, str) and self.size == "auto": 661 # figure out a reasonable window size 662 f = 1.5 663 x, y = screensize 664 xs = y / f * shape[1] # because y<x 665 ys = y / f * shape[0] 666 if xs > x / f: # shrink 667 xs = x / f 668 ys = xs / shape[1] * shape[0] 669 if ys > y / f: 670 ys = y / f 671 xs = ys / shape[0] * shape[1] 672 self.size = (int(xs), int(ys)) 673 if shape == (1, 1): 674 self.size = (int(y / f), int(y / f)) # because y<x 675 else: 676 self.size = (self.size[0], self.size[1]) 677 678 try: 679 image_actor = None 680 bgname = str(self.backgrcol).lower() 681 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 682 self.window.SetNumberOfLayers(2) 683 self.background_renderer = vtki.vtkRenderer() 684 self.background_renderer.SetLayer(0) 685 self.background_renderer.InteractiveOff() 686 self.background_renderer.SetBackground(vedo.get_color(bg2)) 687 image_actor = vedo.Image(self.backgrcol).actor 688 self.window.AddRenderer(self.background_renderer) 689 self.background_renderer.AddActor(image_actor) 690 except AttributeError: 691 pass 692 693 for i in reversed(range(shape[0])): 694 for j in range(shape[1]): 695 arenderer = vtki.vtkRenderer() 696 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 697 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 698 699 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 700 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 701 if vedo.settings.use_depth_peeling: 702 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 703 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 704 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 705 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 706 707 if image_actor: 708 arenderer.SetLayer(1) 709 710 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 711 if bg2: 712 arenderer.GradientBackgroundOn() 713 arenderer.SetBackground2(vedo.get_color(bg2)) 714 715 x0 = i / shape[0] 716 y0 = j / shape[1] 717 x1 = (i + 1) / shape[0] 718 y1 = (j + 1) / shape[1] 719 arenderer.SetViewport(y0, x0, y1, x1) 720 self.renderers.append(arenderer) 721 self.axes_instances.append(None) 722 self.shape = shape 723 724 if self.renderers: 725 self.renderer = self.renderers[0] 726 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 727 728 ######################################################### 729 if self.qt_widget or self.wx_widget: 730 if self.qt_widget: 731 self.window = self.qt_widget.GetRenderWindow() # overwrite 732 else: 733 self.window = self.wx_widget.GetRenderWindow() 734 self.interactor = self.window.GetInteractor() 735 736 ######################################################### 737 for r in self.renderers: 738 self.window.AddRenderer(r) 739 # set the background gradient if any 740 if vedo.settings.background_gradient_orientation > 0: 741 try: 742 modes = [ 743 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 744 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 745 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 746 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 747 ] 748 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 749 r.GradientBackgroundOn() 750 except AttributeError: 751 pass 752 753 ######################################################### 754 if self.qt_widget or self.wx_widget: 755 # self.window.SetSize(int(self.size[0]), int(self.size[1])) 756 self.interactor.SetRenderWindow(self.window) 757 # vsty = vtki.new("InteractorStyleTrackballCamera") 758 # self.interactor.SetInteractorStyle(vsty) 759 if vedo.settings.enable_default_keyboard_callbacks: 760 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 761 if vedo.settings.enable_default_mouse_callbacks: 762 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 763 return ################ 764 ######################## 765 766 if self.size[0] == "f": # full screen 767 self.size = "fullscreen" 768 self.window.SetFullScreen(True) 769 self.window.BordersOn() 770 else: 771 self.window.SetSize(int(self.size[0]), int(self.size[1])) 772 773 if self.offscreen: 774 if self.axes in (4, 5, 8, 12, 14): 775 self.axes = 0 # does not work with those 776 self.window.SetOffScreenRendering(True) 777 self.interactor = None 778 self._interactive = False 779 return ################ 780 ######################## 781 782 self.window.SetPosition(pos) 783 784 ######################################################### 785 self.interactor = vtki.vtkRenderWindowInteractor() 786 787 self.interactor.SetRenderWindow(self.window) 788 vsty = vtki.new("InteractorStyleTrackballCamera") 789 self.interactor.SetInteractorStyle(vsty) 790 self.interactor.RemoveObservers("CharEvent") 791 792 if vedo.settings.enable_default_keyboard_callbacks: 793 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 794 if vedo.settings.enable_default_mouse_callbacks: 795 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 796 797 ##################################################################### ..init ends here. 798 799 def __str__(self): 800 """Return Plotter info.""" 801 axtype = { 802 0: "(no axes)", 803 1: "(default customizable grid walls)", 804 2: "(cartesian axes from origin", 805 3: "(positive range of cartesian axes from origin", 806 4: "(axes triad at bottom left)", 807 5: "(oriented cube at bottom left)", 808 6: "(mark the corners of the bounding box)", 809 7: "(3D ruler at each side of the cartesian axes)", 810 8: "(the vtkCubeAxesActor object)", 811 9: "(the bounding box outline)", 812 10: "(circles of maximum bounding box range)", 813 11: "(show a large grid on the x-y plane)", 814 12: "(show polar axes)", 815 13: "(simple ruler at the bottom of the window)", 816 14: "(the vtkCameraOrientationWidget object)", 817 } 818 819 module = self.__class__.__module__ 820 name = self.__class__.__name__ 821 out = vedo.printc( 822 f"{module}.{name} at ({hex(id(self))})".ljust(75), 823 bold=True, invert=True, return_string=True, 824 ) 825 out += "\x1b[0m" 826 if self.interactor: 827 out += "window title".ljust(14) + ": " + self.title + "\n" 828 out += "window size".ljust(14) + f": {self.window.GetSize()}" 829 out += f", full_screen={self.window.GetScreenSize()}\n" 830 out += "activ renderer".ljust(14) + ": nr." + str(self.renderers.index(self.renderer)) 831 out += f" (out of {len(self.renderers)} renderers)\n" 832 833 bns, totpt = [], 0 834 for a in self.objects: 835 try: 836 b = a.bounds() 837 bns.append(b) 838 except AttributeError: 839 pass 840 try: 841 totpt += a.npoints 842 except AttributeError: 843 pass 844 out += "n. of objects".ljust(14) + f": {len(self.objects)}" 845 out += f" ({totpt} vertices)\n" if totpt else "\n" 846 847 if len(bns) > 0: 848 min_bns = np.min(bns, axis=0) 849 max_bns = np.max(bns, axis=0) 850 bx1, bx2 = utils.precision(min_bns[0], 3), utils.precision(max_bns[1], 3) 851 by1, by2 = utils.precision(min_bns[2], 3), utils.precision(max_bns[3], 3) 852 bz1, bz2 = utils.precision(min_bns[4], 3), utils.precision(max_bns[5], 3) 853 out += "bounds".ljust(14) + ":" 854 out += " x=(" + bx1 + ", " + bx2 + ")," 855 out += " y=(" + by1 + ", " + by2 + ")," 856 out += " z=(" + bz1 + ", " + bz2 + ")\n" 857 858 if utils.is_integer(self.axes): 859 out += "axes style".ljust(14) + f": {self.axes} {axtype[self.axes]}\n" 860 elif isinstance(self.axes, dict): 861 out += "axes style".ljust(14) + f": 1 {axtype[1]}\n" 862 else: 863 out += "axes style".ljust(14) + f": {[self.axes]}\n" 864 return out.rstrip() + "\x1b[0m" 865 866 def print(self): 867 """Print information about the current instance.""" 868 print(self.__str__()) 869 return self 870 871 def __iadd__(self, objects): 872 self.add(objects) 873 return self 874 875 def __isub__(self, objects): 876 self.remove(objects) 877 return self 878 879 def __enter__(self): 880 # context manager like in "with Plotter() as plt:" 881 return self 882 883 def __exit__(self, *args, **kwargs): 884 # context manager like in "with Plotter() as plt:" 885 self.close() 886 887 def initialize_interactor(self) -> "Plotter": 888 """Initialize the interactor if not already initialized.""" 889 if self.offscreen: 890 return self 891 if self.interactor: 892 if not self.interactor.GetInitialized(): 893 self.interactor.Initialize() 894 self.interactor.RemoveObservers("CharEvent") 895 return self 896 897 def process_events(self) -> "Plotter": 898 """Process all pending events.""" 899 self.initialize_interactor() 900 if self.interactor: 901 try: 902 self.interactor.ProcessEvents() 903 except AttributeError: 904 pass 905 return self 906 907 def at(self, nren: int, yren=None) -> "Plotter": 908 """ 909 Select the current renderer number as an int. 910 Can also use the `[nx, ny]` format. 911 """ 912 if utils.is_sequence(nren): 913 if len(nren) == 2: 914 nren, yren = nren 915 else: 916 vedo.logger.error("at() argument must be a single number or a list of two numbers") 917 raise RuntimeError 918 919 if yren is not None: 920 a, b = self.shape 921 x, y = nren, yren 922 nren = x * b + y 923 # print("at (", x, y, ") -> ren", nren) 924 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 925 vedo.logger.error(f"at({nren, yren}) is malformed!") 926 raise RuntimeError 927 928 self.renderer = self.renderers[nren] 929 return self 930 931 def add(self, *objs, at=None) -> "Plotter": 932 """ 933 Append the input objects to the internal list of objects to be shown. 934 935 Arguments: 936 at : (int) 937 add the object at the specified renderer 938 """ 939 if at is not None: 940 ren = self.renderers[at] 941 else: 942 ren = self.renderer 943 944 objs = utils.flatten(objs) 945 for ob in objs: 946 if ob and ob not in self.objects: 947 self.objects.append(ob) 948 949 acts = self._scan_input_return_acts(objs) 950 951 for a in acts: 952 953 if ren: 954 955 if isinstance(a, vedo.addons.BaseCutter): 956 a.add_to(self) # from cutters 957 continue 958 959 try: 960 ren.AddActor(a) 961 except TypeError: 962 ren.AddActor(a.actor) 963 964 if hasattr(a, "rendered_at"): 965 ir = self.renderers.index(ren) 966 a.rendered_at.add(ir) 967 if isinstance(a, vtki.vtkFollower): 968 a.SetCamera(self.camera) 969 if isinstance(a, vedo.visual.LightKit): 970 a.lightkit.AddLightsToRenderer(ren) 971 972 return self 973 974 def remove(self, *objs, at=None) -> "Plotter": 975 """ 976 Remove input object to the internal list of objects to be shown. 977 978 Objects to be removed can be referenced by their assigned name, 979 980 Arguments: 981 at : (int) 982 remove the object at the specified renderer 983 """ 984 # TODO and you can also use wildcards like `*` and `?`. 985 if at is not None: 986 ren = self.renderers[at] 987 else: 988 ren = self.renderer 989 990 objs = [ob for ob in utils.flatten(objs) if ob] 991 992 has_str = False 993 for ob in objs: 994 if isinstance(ob, str): 995 has_str = True 996 break 997 998 has_actor = False 999 for ob in objs: 1000 if hasattr(ob, "actor") and ob.actor: 1001 has_actor = True 1002 break 1003 1004 if has_str or has_actor: 1005 # need to get the actors to search for 1006 for a in self.get_actors(include_non_pickables=True): 1007 # print("PARSING", [a]) 1008 try: 1009 if (a.name and a.name in objs) or a in objs: 1010 objs.append(a) 1011 # if a.name: 1012 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1013 # if any(bools) or a in objs: 1014 # objs.append(a) 1015 # print('a.name',a.name, objs,any(bools)) 1016 except AttributeError: # no .name 1017 # passing the actor so get back the object with .retrieve_object() 1018 try: 1019 vobj = a.retrieve_object() 1020 if (vobj.name and vobj.name in objs) or vobj in objs: 1021 # print('vobj.name', vobj.name) 1022 objs.append(vobj) 1023 except AttributeError: 1024 pass 1025 1026 ir = self.renderers.index(ren) 1027 1028 ids = [] 1029 for ob in set(objs): 1030 1031 # will remove it from internal list if possible 1032 try: 1033 idx = self.objects.index(ob) 1034 ids.append(idx) 1035 except ValueError: 1036 pass 1037 1038 if ren: ### remove it from the renderer 1039 1040 if isinstance(ob, vedo.addons.BaseCutter): 1041 ob.remove_from(self) # from cutters 1042 continue 1043 1044 try: 1045 ren.RemoveActor(ob) 1046 except TypeError: 1047 try: 1048 ren.RemoveActor(ob.actor) 1049 except AttributeError: 1050 pass 1051 1052 if hasattr(ob, "rendered_at"): 1053 ob.rendered_at.discard(ir) 1054 1055 if hasattr(ob, "scalarbar") and ob.scalarbar: 1056 ren.RemoveActor(ob.scalarbar) 1057 if hasattr(ob, "_caption") and ob._caption: 1058 ren.RemoveActor(ob._caption) 1059 if hasattr(ob, "shadows") and ob.shadows: 1060 for sha in ob.shadows: 1061 ren.RemoveActor(sha.actor) 1062 if hasattr(ob, "trail") and ob.trail: 1063 ren.RemoveActor(ob.trail.actor) 1064 ob.trail_points = [] 1065 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1066 for sha in ob.trail.shadows: 1067 ren.RemoveActor(sha.actor) 1068 1069 elif isinstance(ob, vedo.visual.LightKit): 1070 ob.lightkit.RemoveLightsFromRenderer(ren) 1071 1072 # for i in ids: # WRONG way of doing it! 1073 # del self.objects[i] 1074 # instead we do: 1075 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1076 return self 1077 1078 @property 1079 def actors(self): 1080 """Return the list of actors.""" 1081 return [ob.actor for ob in self.objects if hasattr(ob, "actor")] 1082 1083 def remove_lights(self) -> "Plotter": 1084 """Remove all the present lights in the current renderer.""" 1085 if self.renderer: 1086 self.renderer.RemoveAllLights() 1087 return self 1088 1089 def pop(self, at=None) -> "Plotter": 1090 """ 1091 Remove the last added object from the rendering window. 1092 This method is typically used in loops or callback functions. 1093 """ 1094 if at is not None and not isinstance(at, int): 1095 # wrong usage pitfall 1096 vedo.logger.error("argument of pop() must be an integer") 1097 raise RuntimeError() 1098 1099 if self.objects: 1100 self.remove(self.objects[-1], at) 1101 return self 1102 1103 def render(self, resetcam=False) -> "Plotter": 1104 """Render the scene. This method is typically used in loops or callback functions.""" 1105 1106 if vedo.settings.dry_run_mode >= 2: 1107 return self 1108 1109 if not self.window: 1110 return self 1111 1112 self.initialize_interactor() 1113 1114 if resetcam: 1115 self.renderer.ResetCamera() 1116 1117 self.window.Render() 1118 1119 if self._cocoa_process_events and self.interactor.GetInitialized(): 1120 if "Darwin" in vedo.sys_platform and not self.offscreen: 1121 self.interactor.ProcessEvents() 1122 self._cocoa_process_events = False 1123 return self 1124 1125 def interactive(self) -> "Plotter": 1126 """ 1127 Start window interaction. 1128 Analogous to `show(..., interactive=True)`. 1129 """ 1130 if vedo.settings.dry_run_mode >= 1: 1131 return self 1132 self.initialize_interactor() 1133 if self.interactor: 1134 # print("self.interactor.Start()") 1135 self.interactor.Start() 1136 # print("self.interactor.Start() done") 1137 if self._must_close_now: 1138 # print("self.interactor.TerminateApp()") 1139 self.interactor.GetRenderWindow().Finalize() 1140 self.interactor.TerminateApp() 1141 self.interactor = None 1142 self.window = None 1143 self.renderer = None 1144 self.renderers = [] 1145 self.camera = None 1146 return self 1147 1148 def use_depth_peeling(self, at=None, value=True) -> "Plotter": 1149 """ 1150 Specify whether use depth peeling algorithm at this specific renderer 1151 Call this method before the first rendering. 1152 """ 1153 if at is None: 1154 ren = self.renderer 1155 else: 1156 ren = self.renderers[at] 1157 ren.SetUseDepthPeeling(value) 1158 return self 1159 1160 def background(self, c1=None, c2=None, at=None, mode=0) -> Union["Plotter", "np.ndarray"]: 1161 """Set the color of the background for the current renderer. 1162 A different renderer index can be specified by keyword `at`. 1163 1164 Arguments: 1165 c1 : (list) 1166 background main color. 1167 c2 : (list) 1168 background color for the upper part of the window. 1169 at : (int) 1170 renderer index. 1171 mode : (int) 1172 background mode (needs vtk version >= 9.3) 1173 0 = vertical, 1174 1 = horizontal, 1175 2 = radial farthest side, 1176 3 = radia farthest corner. 1177 """ 1178 if not self.renderers: 1179 return self 1180 if at is None: 1181 r = self.renderer 1182 else: 1183 r = self.renderers[at] 1184 1185 if c1 is None and c2 is None: 1186 return np.array(r.GetBackground()) 1187 1188 if r: 1189 if c1 is not None: 1190 r.SetBackground(vedo.get_color(c1)) 1191 if c2 is not None: 1192 r.GradientBackgroundOn() 1193 r.SetBackground2(vedo.get_color(c2)) 1194 if mode: 1195 try: # only works with vtk>=9.3 1196 modes = [ 1197 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1198 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1199 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1200 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1201 ] 1202 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 1203 except AttributeError: 1204 pass 1205 1206 else: 1207 r.GradientBackgroundOff() 1208 return self 1209 1210 ################################################################## 1211 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1212 """ 1213 Return a list of Meshes from the specified renderer. 1214 1215 Arguments: 1216 at : (int) 1217 specify which renderer to look at. 1218 include_non_pickables : (bool) 1219 include non-pickable objects 1220 unpack_assemblies : (bool) 1221 unpack assemblies into their components 1222 """ 1223 if at is None: 1224 renderer = self.renderer 1225 at = self.renderers.index(renderer) 1226 elif isinstance(at, int): 1227 renderer = self.renderers[at] 1228 1229 has_global_axes = False 1230 if isinstance(self.axes_instances[at], vedo.Assembly): 1231 has_global_axes = True 1232 1233 if unpack_assemblies: 1234 acs = renderer.GetActors() 1235 else: 1236 acs = renderer.GetViewProps() 1237 1238 objs = [] 1239 acs.InitTraversal() 1240 for _ in range(acs.GetNumberOfItems()): 1241 1242 if unpack_assemblies: 1243 a = acs.GetNextItem() 1244 else: 1245 a = acs.GetNextProp() 1246 1247 if isinstance(a, vtki.vtkVolume): 1248 continue 1249 1250 if include_non_pickables or a.GetPickable(): 1251 if a == self.axes_instances[at]: 1252 continue 1253 if has_global_axes and a in self.axes_instances[at].actors: 1254 continue 1255 try: 1256 objs.append(a.retrieve_object()) 1257 except AttributeError: 1258 pass 1259 return objs 1260 1261 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1262 """ 1263 Return a list of Volumes from the specified renderer. 1264 1265 Arguments: 1266 at : (int) 1267 specify which renderer to look at 1268 include_non_pickables : (bool) 1269 include non-pickable objects 1270 """ 1271 if at is None: 1272 renderer = self.renderer 1273 at = self.renderers.index(renderer) 1274 elif isinstance(at, int): 1275 renderer = self.renderers[at] 1276 1277 vols = [] 1278 acs = renderer.GetVolumes() 1279 acs.InitTraversal() 1280 for _ in range(acs.GetNumberOfItems()): 1281 a = acs.GetNextItem() 1282 if include_non_pickables or a.GetPickable(): 1283 try: 1284 vols.append(a.retrieve_object()) 1285 except AttributeError: 1286 pass 1287 return vols 1288 1289 def get_actors(self, at=None, include_non_pickables=False) -> list: 1290 """ 1291 Return a list of Volumes from the specified renderer. 1292 1293 Arguments: 1294 at : (int) 1295 specify which renderer to look at 1296 include_non_pickables : (bool) 1297 include non-pickable objects 1298 """ 1299 if at is None: 1300 renderer = self.renderer 1301 at = self.renderers.index(renderer) 1302 elif isinstance(at, int): 1303 renderer = self.renderers[at] 1304 1305 acts = [] 1306 acs = renderer.GetViewProps() 1307 acs.InitTraversal() 1308 for _ in range(acs.GetNumberOfItems()): 1309 a = acs.GetNextProp() 1310 if include_non_pickables or a.GetPickable(): 1311 acts.append(a) 1312 return acts 1313 1314 def check_actors_trasform(self, at=None) -> "Plotter": 1315 """ 1316 Reset the transformation matrix of all actors at specified renderer. 1317 This is only useful when actors have been moved/rotated/scaled manually 1318 in an already rendered scene using interactors like 1319 'TrackballActor' or 'JoystickActor'. 1320 """ 1321 # see issue https://github.com/marcomusy/vedo/issues/1046 1322 for a in self.get_actors(at=at, include_non_pickables=True): 1323 try: 1324 M = a.GetMatrix() 1325 except AttributeError: 1326 continue 1327 if M and not M.IsIdentity(): 1328 try: 1329 a.retrieve_object().apply_transform_from_actor() 1330 # vedo.logger.info( 1331 # f"object '{a.retrieve_object().name}' " 1332 # "was manually moved. Updated to its current position." 1333 # ) 1334 except AttributeError: 1335 pass 1336 return self 1337 1338 def reset_camera(self, tight=None) -> "Plotter": 1339 """ 1340 Reset the camera position and zooming. 1341 If tight (float) is specified the zooming reserves a padding space 1342 in the xy-plane expressed in percent of the average size. 1343 """ 1344 if tight is None: 1345 self.renderer.ResetCamera() 1346 else: 1347 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1348 1349 cam = self.renderer.GetActiveCamera() 1350 1351 self.renderer.ComputeAspect() 1352 aspect = self.renderer.GetAspect() 1353 angle = np.pi * cam.GetViewAngle() / 180.0 1354 dx, dy = (x1 - x0) * 0.999, (y1 - y0) * 0.999 1355 dist = max(dx / aspect[0], dy) / np.sin(angle / 2) / 2 1356 1357 cam.SetViewUp(0, 1, 0) 1358 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1359 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1360 if cam.GetParallelProjection(): 1361 ps = max(dx / aspect[0], dy) / 2 1362 cam.SetParallelScale(ps * (1 + tight)) 1363 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1364 return self 1365 1366 def reset_viewup(self, smooth=True) -> "Plotter": 1367 """ 1368 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1369 """ 1370 vbb = addons.compute_visible_bounds()[0] 1371 x0, x1, y0, y1, z0, z1 = vbb 1372 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1373 d = self.camera.GetDistance() 1374 1375 viewups = np.array( 1376 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1377 ) 1378 positions = np.array( 1379 [ 1380 (mx, my, mz + d), 1381 (mx, my, mz - d), 1382 (mx, my + d, mz), 1383 (mx, my - d, mz), 1384 (mx + d, my, mz), 1385 (mx - d, my, mz), 1386 ] 1387 ) 1388 1389 vu = np.array(self.camera.GetViewUp()) 1390 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1391 1392 poc = np.array(self.camera.GetPosition()) 1393 foc = np.array(self.camera.GetFocalPoint()) 1394 a = poc - foc 1395 b = positions - foc 1396 a = a / np.linalg.norm(a) 1397 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1398 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1399 1400 if smooth: 1401 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1402 for t in outtimes: 1403 vv = vu * (1 - t) + viewups[vui] * t 1404 pp = poc * (1 - t) + positions[pui] * t 1405 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1406 self.camera.SetViewUp(vv) 1407 self.camera.SetPosition(pp) 1408 self.camera.SetFocalPoint(ff) 1409 self.render() 1410 1411 # interpolator does not respect parallel view...: 1412 # cam1 = dict( 1413 # pos=poc, 1414 # viewup=vu, 1415 # focal_point=(mx,my,mz), 1416 # clipping_range=self.camera.GetClippingRange() 1417 # ) 1418 # # cam1 = self.camera 1419 # cam2 = dict( 1420 # pos=positions[pui], 1421 # viewup=viewups[vui], 1422 # focal_point=(mx,my,mz), 1423 # clipping_range=self.camera.GetClippingRange() 1424 # ) 1425 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1426 # for c in vcams: 1427 # self.renderer.SetActiveCamera(c) 1428 # self.render() 1429 else: 1430 1431 self.camera.SetViewUp(viewups[vui]) 1432 self.camera.SetPosition(positions[pui]) 1433 self.camera.SetFocalPoint(mx, my, mz) 1434 1435 self.renderer.ResetCameraClippingRange() 1436 1437 # vbb, _, _, _ = addons.compute_visible_bounds() 1438 # x0,x1, y0,y1, z0,z1 = vbb 1439 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1440 self.render() 1441 return self 1442 1443 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1444 """ 1445 Takes as input two cameras set camera at an interpolated position: 1446 1447 Cameras can be vtkCamera or dictionaries in format: 1448 1449 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1450 1451 Press `shift-C` key in interactive mode to dump a python snipplet 1452 of parameters for the current camera view. 1453 """ 1454 nc = len(cameras) 1455 if len(times) == 0: 1456 times = np.linspace(0, 1, num=nc, endpoint=True) 1457 1458 assert len(times) == nc 1459 1460 cin = vtki.new("CameraInterpolator") 1461 1462 # cin.SetInterpolationTypeToLinear() # buggy? 1463 if nc > 2 and smooth: 1464 cin.SetInterpolationTypeToSpline() 1465 1466 for i, cam in enumerate(cameras): 1467 vcam = cam 1468 if isinstance(cam, dict): 1469 vcam = utils.camera_from_dict(cam) 1470 cin.AddCamera(times[i], vcam) 1471 1472 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1473 rng = maxt - mint 1474 1475 if len(output_times) == 0: 1476 cin.InterpolateCamera(t * rng, self.camera) 1477 self.renderer.SetActiveCamera(self.camera) 1478 return [self.camera] 1479 else: 1480 vcams = [] 1481 for tt in output_times: 1482 c = vtki.vtkCamera() 1483 cin.InterpolateCamera(tt * rng, c) 1484 vcams.append(c) 1485 return vcams 1486 1487 def fly_to(self, point) -> "Plotter": 1488 """ 1489 Fly camera to the specified point. 1490 1491 Arguments: 1492 point : (list) 1493 point in space to place camera. 1494 1495 Example: 1496 ```python 1497 from vedo import * 1498 cone = Cone() 1499 plt = Plotter(axes=1) 1500 plt.show(cone) 1501 plt.fly_to([1,0,0]) 1502 plt.interactive().close() 1503 ``` 1504 """ 1505 if self.interactor: 1506 self.resetcam = False 1507 self.interactor.FlyTo(self.renderer, point) 1508 return self 1509 1510 def look_at(self, plane="xy") -> "Plotter": 1511 """Move the camera so that it looks at the specified cartesian plane""" 1512 cam = self.renderer.GetActiveCamera() 1513 fp = np.array(cam.GetFocalPoint()) 1514 p = np.array(cam.GetPosition()) 1515 dist = np.linalg.norm(fp - p) 1516 plane = plane.lower() 1517 if "x" in plane and "y" in plane: 1518 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1519 cam.SetViewUp(0.0, 1.0, 0.0) 1520 elif "x" in plane and "z" in plane: 1521 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1522 cam.SetViewUp(0.0, 0.0, 1.0) 1523 elif "y" in plane and "z" in plane: 1524 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1525 cam.SetViewUp(0.0, 0.0, 1.0) 1526 else: 1527 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1528 return self 1529 1530 def record(self, filename="") -> str: 1531 """ 1532 Record camera, mouse, keystrokes and all other events. 1533 Recording can be toggled on/off by pressing key "R". 1534 1535 Arguments: 1536 filename : (str) 1537 ascii file to store events. 1538 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1539 1540 Returns: 1541 a string descriptor of events. 1542 1543 Examples: 1544 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1545 """ 1546 if vedo.settings.dry_run_mode >= 1: 1547 return "" 1548 if not self.interactor: 1549 vedo.logger.warning("Cannot record events, no interactor defined.") 1550 return "" 1551 erec = vtki.new("InteractorEventRecorder") 1552 erec.SetInteractor(self.interactor) 1553 if not filename: 1554 if not os.path.exists(vedo.settings.cache_directory): 1555 os.makedirs(vedo.settings.cache_directory) 1556 home_dir = os.path.expanduser("~") 1557 filename = os.path.join( 1558 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1559 print("Events will be recorded in", filename) 1560 erec.SetFileName(filename) 1561 erec.SetKeyPressActivationValue("R") 1562 erec.EnabledOn() 1563 erec.Record() 1564 self.interactor.Start() 1565 erec.Stop() 1566 erec.EnabledOff() 1567 with open(filename, "r", encoding="UTF-8") as fl: 1568 events = fl.read() 1569 erec = None 1570 return events 1571 1572 def play(self, recorded_events="", repeats=0) -> "Plotter": 1573 """ 1574 Play camera, mouse, keystrokes and all other events. 1575 1576 Arguments: 1577 events : (str) 1578 file o string of events. 1579 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1580 repeats : (int) 1581 number of extra repeats of the same events. The default is 0. 1582 1583 Examples: 1584 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1585 """ 1586 if vedo.settings.dry_run_mode >= 1: 1587 return self 1588 if not self.interactor: 1589 vedo.logger.warning("Cannot play events, no interactor defined.") 1590 return self 1591 1592 erec = vtki.new("InteractorEventRecorder") 1593 erec.SetInteractor(self.interactor) 1594 1595 if not recorded_events: 1596 home_dir = os.path.expanduser("~") 1597 recorded_events = os.path.join( 1598 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1599 1600 if recorded_events.endswith(".log"): 1601 erec.ReadFromInputStringOff() 1602 erec.SetFileName(recorded_events) 1603 else: 1604 erec.ReadFromInputStringOn() 1605 erec.SetInputString(recorded_events) 1606 1607 erec.Play() 1608 for _ in range(repeats): 1609 erec.Rewind() 1610 erec.Play() 1611 erec.EnabledOff() 1612 erec = None 1613 return self 1614 1615 def parallel_projection(self, value=True, at=None) -> "Plotter": 1616 """ 1617 Use parallel projection `at` a specified renderer. 1618 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1619 An input value equal to -1 will toggle it on/off. 1620 """ 1621 if at is not None: 1622 r = self.renderers[at] 1623 else: 1624 r = self.renderer 1625 if value == -1: 1626 val = r.GetActiveCamera().GetParallelProjection() 1627 value = not val 1628 r.GetActiveCamera().SetParallelProjection(value) 1629 r.Modified() 1630 return self 1631 1632 def render_hidden_lines(self, value=True) -> "Plotter": 1633 """Remove hidden lines when in wireframe mode.""" 1634 self.renderer.SetUseHiddenLineRemoval(not value) 1635 return self 1636 1637 def fov(self, angle: float) -> "Plotter": 1638 """ 1639 Set the field of view angle for the camera. 1640 This is the angle of the camera frustum in the horizontal direction. 1641 High values will result in a wide-angle lens (fish-eye effect), 1642 and low values will result in a telephoto lens. 1643 1644 Default value is 30 degrees. 1645 """ 1646 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1647 self.renderer.GetActiveCamera().SetViewAngle(angle) 1648 return self 1649 1650 def zoom(self, zoom: float) -> "Plotter": 1651 """Apply a zooming factor for the current camera view""" 1652 self.renderer.GetActiveCamera().Zoom(zoom) 1653 return self 1654 1655 def azimuth(self, angle: float) -> "Plotter": 1656 """Rotate camera around the view up vector.""" 1657 self.renderer.GetActiveCamera().Azimuth(angle) 1658 return self 1659 1660 def elevation(self, angle: float) -> "Plotter": 1661 """Rotate the camera around the cross product of the negative 1662 of the direction of projection and the view up vector.""" 1663 self.renderer.GetActiveCamera().Elevation(angle) 1664 return self 1665 1666 def roll(self, angle: float) -> "Plotter": 1667 """Roll the camera about the direction of projection.""" 1668 self.renderer.GetActiveCamera().Roll(angle) 1669 return self 1670 1671 def dolly(self, value: float) -> "Plotter": 1672 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1673 self.renderer.GetActiveCamera().Dolly(value) 1674 return self 1675 1676 ################################################################## 1677 def add_slider( 1678 self, 1679 sliderfunc, 1680 xmin, 1681 xmax, 1682 value=None, 1683 pos=4, 1684 title="", 1685 font="Calco", 1686 title_size=1, 1687 c=None, 1688 alpha=1, 1689 show_value=True, 1690 delayed=False, 1691 **options, 1692 ) -> "vedo.addons.Slider2D": 1693 """ 1694 Add a `vedo.addons.Slider2D` which can call an external custom function. 1695 1696 Arguments: 1697 sliderfunc : (Callable) 1698 external function to be called by the widget 1699 xmin : (float) 1700 lower value of the slider 1701 xmax : (float) 1702 upper value 1703 value : (float) 1704 current value 1705 pos : (list, str) 1706 position corner number: horizontal [1-5] or vertical [11-15] 1707 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1708 and also by a string descriptor (eg. "bottom-left") 1709 title : (str) 1710 title text 1711 font : (str) 1712 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1713 title_size : (float) 1714 title text scale [1.0] 1715 show_value : (bool) 1716 if True current value is shown 1717 delayed : (bool) 1718 if True the callback is delayed until when the mouse button is released 1719 alpha : (float) 1720 opacity of the scalar bar texts 1721 slider_length : (float) 1722 slider length 1723 slider_width : (float) 1724 slider width 1725 end_cap_length : (float) 1726 length of the end cap 1727 end_cap_width : (float) 1728 width of the end cap 1729 tube_width : (float) 1730 width of the tube 1731 title_height : (float) 1732 width of the title 1733 tformat : (str) 1734 format of the title 1735 1736 Examples: 1737 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1738 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1739 1740 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1741 """ 1742 if c is None: # automatic black or white 1743 c = (0.8, 0.8, 0.8) 1744 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1745 c = (0.2, 0.2, 0.2) 1746 else: 1747 c = vedo.get_color(c) 1748 1749 slider2d = addons.Slider2D( 1750 sliderfunc, 1751 xmin, 1752 xmax, 1753 value, 1754 pos, 1755 title, 1756 font, 1757 title_size, 1758 c, 1759 alpha, 1760 show_value, 1761 delayed, 1762 **options, 1763 ) 1764 1765 if self.renderer: 1766 slider2d.renderer = self.renderer 1767 if self.interactor: 1768 slider2d.interactor = self.interactor 1769 slider2d.on() 1770 self.sliders.append([slider2d, sliderfunc]) 1771 return slider2d 1772 1773 def add_slider3d( 1774 self, 1775 sliderfunc, 1776 pos1, 1777 pos2, 1778 xmin, 1779 xmax, 1780 value=None, 1781 s=0.03, 1782 t=1, 1783 title="", 1784 rotation=0.0, 1785 c=None, 1786 show_value=True, 1787 ) -> "vedo.addons.Slider3D": 1788 """ 1789 Add a 3D slider widget which can call an external custom function. 1790 1791 Arguments: 1792 sliderfunc : (function) 1793 external function to be called by the widget 1794 pos1 : (list) 1795 first position 3D coordinates 1796 pos2 : (list) 1797 second position coordinates 1798 xmin : (float) 1799 lower value 1800 xmax : (float) 1801 upper value 1802 value : (float) 1803 initial value 1804 s : (float) 1805 label scaling factor 1806 t : (float) 1807 tube scaling factor 1808 title : (str) 1809 title text 1810 c : (color) 1811 slider color 1812 rotation : (float) 1813 title rotation around slider axis 1814 show_value : (bool) 1815 if True current value is shown 1816 1817 Examples: 1818 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1819 1820 ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png) 1821 """ 1822 if c is None: # automatic black or white 1823 c = (0.8, 0.8, 0.8) 1824 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1825 c = (0.2, 0.2, 0.2) 1826 else: 1827 c = vedo.get_color(c) 1828 1829 slider3d = addons.Slider3D( 1830 sliderfunc, 1831 pos1, 1832 pos2, 1833 xmin, 1834 xmax, 1835 value, 1836 s, 1837 t, 1838 title, 1839 rotation, 1840 c, 1841 show_value, 1842 ) 1843 slider3d.renderer = self.renderer 1844 slider3d.interactor = self.interactor 1845 slider3d.on() 1846 self.sliders.append([slider3d, sliderfunc]) 1847 return slider3d 1848 1849 def add_button( 1850 self, 1851 fnc=None, 1852 states=("On", "Off"), 1853 c=("w", "w"), 1854 bc=("green4", "red4"), 1855 pos=(0.7, 0.1), 1856 size=24, 1857 font="Courier", 1858 bold=True, 1859 italic=False, 1860 alpha=1, 1861 angle=0, 1862 ) -> Union["vedo.addons.Button", None]: 1863 """ 1864 Add a button to the renderer window. 1865 1866 Arguments: 1867 states : (list) 1868 a list of possible states, e.g. ['On', 'Off'] 1869 c : (list) 1870 a list of colors for each state 1871 bc : (list) 1872 a list of background colors for each state 1873 pos : (list) 1874 2D position from left-bottom corner 1875 size : (float) 1876 size of button font 1877 font : (str) 1878 font type. Check [available fonts here](https://vedo.embl.es/fonts). 1879 bold : (bool) 1880 bold font face (False) 1881 italic : (bool) 1882 italic font face (False) 1883 alpha : (float) 1884 opacity level 1885 angle : (float) 1886 anticlockwise rotation in degrees 1887 1888 Returns: 1889 `vedo.addons.Button` object. 1890 1891 Examples: 1892 - [buttons1.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons1.py) 1893 - [buttons2.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons2.py) 1894 1895 ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg) 1896 """ 1897 if self.interactor: 1898 bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle) 1899 self.renderer.AddActor2D(bu) 1900 self.buttons.append(bu) 1901 # bu.function_id = self.add_callback("LeftButtonPress", bu.function) # not good 1902 bu.function_id = bu.add_observer("pick", bu.function, priority=10) 1903 return bu 1904 return None 1905 1906 def add_spline_tool( 1907 self, points, pc="k", ps=8, lc="r4", ac="g5", 1908 lw=2, alpha=1, closed=False, ontop=True, can_add_nodes=True, 1909 ) -> "vedo.addons.SplineTool": 1910 """ 1911 Add a spline tool to the current plotter. 1912 Nodes of the spline can be dragged in space with the mouse. 1913 Clicking on the line itself adds an extra point. 1914 Selecting a point and pressing del removes it. 1915 1916 Arguments: 1917 points : (Mesh, Points, array) 1918 the set of vertices forming the spline nodes. 1919 pc : (str) 1920 point color. The default is 'k'. 1921 ps : (str) 1922 point size. The default is 8. 1923 lc : (str) 1924 line color. The default is 'r4'. 1925 ac : (str) 1926 active point marker color. The default is 'g5'. 1927 lw : (int) 1928 line width. The default is 2. 1929 alpha : (float) 1930 line transparency. 1931 closed : (bool) 1932 spline is meant to be closed. The default is False. 1933 1934 Returns: 1935 a `SplineTool` object. 1936 1937 Examples: 1938 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1939 1940 ![](https://vedo.embl.es/images/basic/spline_tool.png) 1941 """ 1942 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1943 sw.interactor = self.interactor 1944 sw.on() 1945 sw.Initialize(sw.points.dataset) 1946 sw.representation.SetRenderer(self.renderer) 1947 sw.representation.SetClosedLoop(closed) 1948 sw.representation.BuildRepresentation() 1949 self.widgets.append(sw) 1950 return sw 1951 1952 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1953 """Add an inset icon mesh into the same renderer. 1954 1955 Arguments: 1956 pos : (int, list) 1957 icon position in the range [1-4] indicating one of the 4 corners, 1958 or it can be a tuple (x,y) as a fraction of the renderer size. 1959 size : (float) 1960 size of the square inset. 1961 1962 Examples: 1963 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1964 """ 1965 iconw = addons.Icon(icon, pos, size) 1966 1967 iconw.SetInteractor(self.interactor) 1968 iconw.EnabledOn() 1969 iconw.InteractiveOff() 1970 self.widgets.append(iconw) 1971 return iconw 1972 1973 def add_global_axes(self, axtype=None, c=None) -> "Plotter": 1974 """Draw axes on scene. Available axes types: 1975 1976 Arguments: 1977 axtype : (int) 1978 - 0, no axes, 1979 - 1, draw three gray grid walls 1980 - 2, show cartesian axes from (0,0,0) 1981 - 3, show positive range of cartesian axes from (0,0,0) 1982 - 4, show a triad at bottom left 1983 - 5, show a cube at bottom left 1984 - 6, mark the corners of the bounding box 1985 - 7, draw a 3D ruler at each side of the cartesian axes 1986 - 8, show the vtkCubeAxesActor object 1987 - 9, show the bounding box outLine 1988 - 10, show three circles representing the maximum bounding box 1989 - 11, show a large grid on the x-y plane 1990 - 12, show polar axes 1991 - 13, draw a simple ruler at the bottom of the window 1992 1993 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 1994 1995 Example: 1996 ```python 1997 from vedo import Box, show 1998 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 1999 show( 2000 b, 2001 axes={ 2002 "xtitle": "Some long variable [a.u.]", 2003 "number_of_divisions": 4, 2004 # ... 2005 }, 2006 ) 2007 ``` 2008 2009 Examples: 2010 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2011 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2012 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2013 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2014 2015 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2016 """ 2017 addons.add_global_axes(axtype, c) 2018 return self 2019 2020 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2021 """Add a legend to the top right. 2022 2023 Examples: 2024 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 2025 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 2026 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 2027 """ 2028 acts = self.get_meshes() 2029 lb = addons.LegendBox(acts, **kwargs) 2030 self.add(lb) 2031 return lb 2032 2033 def add_hint( 2034 self, 2035 obj, 2036 text="", 2037 c="k", 2038 bg="yellow9", 2039 font="Calco", 2040 size=18, 2041 justify=0, 2042 angle=0, 2043 delay=250, 2044 ) -> Union[vtki.vtkBalloonWidget, None]: 2045 """ 2046 Create a pop-up hint style message when hovering an object. 2047 Use `add_hint(obj, False)` to disable a hinting a specific object. 2048 Use `add_hint(None)` to disable all hints. 2049 2050 Arguments: 2051 obj : (Mesh, Points) 2052 the object to associate the pop-up to 2053 text : (str) 2054 string description of the pop-up 2055 delay : (int) 2056 milliseconds to wait before pop-up occurs 2057 """ 2058 if self.offscreen or not self.interactor: 2059 return None 2060 2061 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2062 # Linux vtk9.0 is bugged 2063 vedo.logger.warning( 2064 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2065 ) 2066 return None 2067 2068 if obj is None: 2069 self.hint_widget.EnabledOff() 2070 self.hint_widget.SetInteractor(None) 2071 self.hint_widget = None 2072 return self.hint_widget 2073 2074 if text is False and self.hint_widget: 2075 self.hint_widget.RemoveBalloon(obj) 2076 return self.hint_widget 2077 2078 if text == "": 2079 if obj.name: 2080 text = obj.name 2081 elif obj.filename: 2082 text = obj.filename 2083 else: 2084 return None 2085 2086 if not self.hint_widget: 2087 self.hint_widget = vtki.vtkBalloonWidget() 2088 2089 rep = self.hint_widget.GetRepresentation() 2090 rep.SetBalloonLayoutToImageRight() 2091 2092 trep = rep.GetTextProperty() 2093 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2094 trep.SetFontFile(utils.get_font_path(font)) 2095 trep.SetFontSize(size) 2096 trep.SetColor(vedo.get_color(c)) 2097 trep.SetBackgroundColor(vedo.get_color(bg)) 2098 trep.SetShadow(0) 2099 trep.SetJustification(justify) 2100 trep.UseTightBoundingBoxOn() 2101 2102 self.hint_widget.ManagesCursorOff() 2103 self.hint_widget.SetTimerDuration(delay) 2104 self.hint_widget.SetInteractor(self.interactor) 2105 if angle: 2106 trep.SetOrientation(angle) 2107 trep.SetBackgroundOpacity(0) 2108 # else: 2109 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2110 self.hint_widget.SetRepresentation(rep) 2111 self.widgets.append(self.hint_widget) 2112 self.hint_widget.EnabledOn() 2113 2114 bst = self.hint_widget.GetBalloonString(obj.actor) 2115 if bst: 2116 self.hint_widget.UpdateBalloonString(obj.actor, text) 2117 else: 2118 self.hint_widget.AddBalloon(obj.actor, text) 2119 2120 return self.hint_widget 2121 2122 def add_shadows(self) -> "Plotter": 2123 """Add shadows at the current renderer.""" 2124 if self.renderer: 2125 shadows = vtki.new("ShadowMapPass") 2126 seq = vtki.new("SequencePass") 2127 passes = vtki.new("RenderPassCollection") 2128 passes.AddItem(shadows.GetShadowMapBakerPass()) 2129 passes.AddItem(shadows) 2130 seq.SetPasses(passes) 2131 camerapass = vtki.new("CameraPass") 2132 camerapass.SetDelegatePass(seq) 2133 self.renderer.SetPass(camerapass) 2134 return self 2135 2136 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> "Plotter": 2137 """ 2138 Screen Space Ambient Occlusion. 2139 2140 For every pixel on the screen, the pixel shader samples the depth values around 2141 the current pixel and tries to compute the amount of occlusion from each of the sampled 2142 points. 2143 2144 Arguments: 2145 radius : (float) 2146 radius of influence in absolute units 2147 bias : (float) 2148 bias of the normals 2149 blur : (bool) 2150 add a blurring to the sampled positions 2151 samples : (int) 2152 number of samples to probe 2153 2154 Examples: 2155 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2156 2157 ![](https://vedo.embl.es/images/basic/ssao.jpg) 2158 """ 2159 lights = vtki.new("LightsPass") 2160 2161 opaque = vtki.new("OpaquePass") 2162 2163 ssaoCam = vtki.new("CameraPass") 2164 ssaoCam.SetDelegatePass(opaque) 2165 2166 ssao = vtki.new("SSAOPass") 2167 ssao.SetRadius(radius) 2168 ssao.SetBias(bias) 2169 ssao.SetBlur(blur) 2170 ssao.SetKernelSize(samples) 2171 ssao.SetDelegatePass(ssaoCam) 2172 2173 translucent = vtki.new("TranslucentPass") 2174 2175 volpass = vtki.new("VolumetricPass") 2176 ddp = vtki.new("DualDepthPeelingPass") 2177 ddp.SetTranslucentPass(translucent) 2178 ddp.SetVolumetricPass(volpass) 2179 2180 over = vtki.new("OverlayPass") 2181 2182 collection = vtki.new("RenderPassCollection") 2183 collection.AddItem(lights) 2184 collection.AddItem(ssao) 2185 collection.AddItem(ddp) 2186 collection.AddItem(over) 2187 2188 sequence = vtki.new("SequencePass") 2189 sequence.SetPasses(collection) 2190 2191 cam = vtki.new("CameraPass") 2192 cam.SetDelegatePass(sequence) 2193 2194 self.renderer.SetPass(cam) 2195 return self 2196 2197 def add_depth_of_field(self, autofocus=True) -> "Plotter": 2198 """Add a depth of field effect in the scene.""" 2199 lights = vtki.new("LightsPass") 2200 2201 opaque = vtki.new("OpaquePass") 2202 2203 dofCam = vtki.new("CameraPass") 2204 dofCam.SetDelegatePass(opaque) 2205 2206 dof = vtki.new("DepthOfFieldPass") 2207 dof.SetAutomaticFocalDistance(autofocus) 2208 dof.SetDelegatePass(dofCam) 2209 2210 collection = vtki.new("RenderPassCollection") 2211 collection.AddItem(lights) 2212 collection.AddItem(dof) 2213 2214 sequence = vtki.new("SequencePass") 2215 sequence.SetPasses(collection) 2216 2217 cam = vtki.new("CameraPass") 2218 cam.SetDelegatePass(sequence) 2219 2220 self.renderer.SetPass(cam) 2221 return self 2222 2223 def _add_skybox(self, hdrfile: str) -> "Plotter": 2224 # many hdr files are at https://polyhaven.com/all 2225 2226 reader = vtki.new("HDRReader") 2227 # Check the image can be read. 2228 if not reader.CanReadFile(hdrfile): 2229 vedo.logger.error(f"Cannot read HDR file {hdrfile}") 2230 return self 2231 reader.SetFileName(hdrfile) 2232 reader.Update() 2233 2234 texture = vtki.vtkTexture() 2235 texture.SetColorModeToDirectScalars() 2236 texture.SetInputData(reader.GetOutput()) 2237 2238 # Convert to a cube map 2239 tcm = vtki.new("EquirectangularToCubeMapTexture") 2240 tcm.SetInputTexture(texture) 2241 # Enable mipmapping to handle HDR image 2242 tcm.MipmapOn() 2243 tcm.InterpolateOn() 2244 2245 self.renderer.SetEnvironmentTexture(tcm) 2246 self.renderer.UseImageBasedLightingOn() 2247 self.skybox = vtki.new("Skybox") 2248 self.skybox.SetTexture(tcm) 2249 self.renderer.AddActor(self.skybox) 2250 return self 2251 2252 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None) -> "vedo.addons.RendererFrame": 2253 """ 2254 Add a frame to the renderer subwindow. 2255 2256 Arguments: 2257 c : (color) 2258 color name or index 2259 alpha : (float) 2260 opacity level 2261 lw : (int) 2262 line width in pixels. 2263 padding : (float) 2264 padding space in pixels. 2265 """ 2266 if c is None: # automatic black or white 2267 c = (0.9, 0.9, 0.9) 2268 if self.renderer: 2269 if np.sum(self.renderer.GetBackground()) > 1.5: 2270 c = (0.1, 0.1, 0.1) 2271 renf = addons.RendererFrame(c, alpha, lw, padding) 2272 if renf: 2273 self.renderer.AddActor(renf) 2274 return renf 2275 2276 def add_hover_legend( 2277 self, 2278 at=None, 2279 c=None, 2280 pos="bottom-left", 2281 font="Calco", 2282 s=0.75, 2283 bg="auto", 2284 alpha=0.1, 2285 maxlength=24, 2286 use_info=False, 2287 ) -> int: 2288 """ 2289 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2290 2291 The created text object are stored in `plotter.hover_legends`. 2292 2293 Returns: 2294 the id of the callback function. 2295 2296 Arguments: 2297 c : (color) 2298 Text color. If None then black or white is chosen automatically 2299 pos : (str) 2300 text positioning 2301 font : (str) 2302 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2303 s : (float) 2304 text size scale 2305 bg : (color) 2306 background color of the 2D box containing the text 2307 alpha : (float) 2308 box transparency 2309 maxlength : (int) 2310 maximum number of characters per line 2311 use_info : (bool) 2312 visualize the content of the `obj.info` attribute 2313 2314 Examples: 2315 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2316 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2317 2318 ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg) 2319 """ 2320 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2321 2322 if at is None: 2323 at = self.renderers.index(self.renderer) 2324 2325 def _legfunc(evt): 2326 if not evt.object or not self.renderer or at != evt.at: 2327 if hoverlegend.mapper.GetInput(): # clear and return 2328 hoverlegend.mapper.SetInput("") 2329 self.render() 2330 return 2331 2332 if use_info: 2333 if hasattr(evt.object, "info"): 2334 t = str(evt.object.info) 2335 else: 2336 return 2337 else: 2338 t, tp = "", "" 2339 if evt.isMesh: 2340 tp = "Mesh " 2341 elif evt.isPoints: 2342 tp = "Points " 2343 elif evt.isVolume: 2344 tp = "Volume " 2345 elif evt.isImage: 2346 tp = "Image " 2347 elif evt.isAssembly: 2348 tp = "Assembly " 2349 else: 2350 return 2351 2352 if evt.isAssembly: 2353 if not evt.object.name: 2354 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2355 else: 2356 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2357 else: 2358 if evt.object.name: 2359 t += f"{tp}name" 2360 if evt.isPoints: 2361 t += " " 2362 if evt.isMesh: 2363 t += " " 2364 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2365 2366 if evt.object.filename: 2367 t += f"{tp}filename: " 2368 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2369 t += "\n" 2370 if not evt.object.file_size: 2371 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2372 if evt.object.file_size: 2373 t += " : " 2374 sz, created = evt.object.file_size, evt.object.created 2375 t += f"{created[4:-5]} ({sz})" + "\n" 2376 2377 if evt.isPoints: 2378 indata = evt.object.dataset 2379 if indata.GetNumberOfPoints(): 2380 t += ( 2381 f"#points/cells: {indata.GetNumberOfPoints()}" 2382 f" / {indata.GetNumberOfCells()}" 2383 ) 2384 pdata = indata.GetPointData() 2385 cdata = indata.GetCellData() 2386 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2387 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2388 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2389 t += " *" 2390 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2391 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2392 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2393 t += " *" 2394 2395 if evt.isImage: 2396 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2397 t += f"\nImage shape: {evt.object.shape}" 2398 pcol = self.color_picker(evt.picked2d) 2399 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2400 2401 # change box color if needed in 'auto' mode 2402 if evt.isPoints and "auto" in str(bg): 2403 actcol = evt.object.properties.GetColor() 2404 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2405 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2406 2407 # adapt to changes in bg color 2408 bgcol = self.renderers[at].GetBackground() 2409 _bgcol = c 2410 if _bgcol is None: # automatic black or white 2411 _bgcol = (0.9, 0.9, 0.9) 2412 if sum(bgcol) > 1.5: 2413 _bgcol = (0.1, 0.1, 0.1) 2414 if len(set(_bgcol).intersection(bgcol)) < 3: 2415 hoverlegend.color(_bgcol) 2416 2417 if hoverlegend.mapper.GetInput() != t: 2418 hoverlegend.mapper.SetInput(t) 2419 self.interactor.Render() 2420 2421 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2422 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2423 2424 self.add(hoverlegend, at=at) 2425 self.hover_legends.append(hoverlegend) 2426 idcall = self.add_callback("MouseMove", _legfunc) 2427 return idcall 2428 2429 def add_scale_indicator( 2430 self, 2431 pos=(0.7, 0.05), 2432 s=0.02, 2433 length=2, 2434 lw=4, 2435 c="k1", 2436 alpha=1, 2437 units="", 2438 gap=0.05, 2439 ) -> Union["vedo.visual.Actor2D", None]: 2440 """ 2441 Add a Scale Indicator. Only works in parallel mode (no perspective). 2442 2443 Arguments: 2444 pos : (list) 2445 fractional (x,y) position on the screen. 2446 s : (float) 2447 size of the text. 2448 length : (float) 2449 length of the line. 2450 units : (str) 2451 string to show units. 2452 gap : (float) 2453 separation of line and text. 2454 2455 Example: 2456 ```python 2457 from vedo import settings, Cube, Plotter 2458 settings.use_parallel_projection = True # or else it does not make sense! 2459 cube = Cube().alpha(0.2) 2460 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2461 plt.add_scale_indicator(units='um', c='blue4') 2462 plt.show(cube, "Scale indicator with units").close() 2463 ``` 2464 ![](https://vedo.embl.es/images/feats/scale_indicator.png) 2465 """ 2466 # Note that this cannot go in addons.py 2467 # because it needs callbacks and window size 2468 if not self.interactor: 2469 return None 2470 2471 ppoints = vtki.vtkPoints() # Generate the polyline 2472 psqr = [[0.0, gap], [length / 10, gap]] 2473 dd = psqr[1][0] - psqr[0][0] 2474 for i, pt in enumerate(psqr): 2475 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2476 lines = vtki.vtkCellArray() 2477 lines.InsertNextCell(len(psqr)) 2478 for i in range(len(psqr)): 2479 lines.InsertCellPoint(i) 2480 pd = vtki.vtkPolyData() 2481 pd.SetPoints(ppoints) 2482 pd.SetLines(lines) 2483 2484 wsx, wsy = self.window.GetSize() 2485 if not self.camera.GetParallelProjection(): 2486 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2487 return None 2488 2489 rlabel = vtki.new("VectorText") 2490 rlabel.SetText("scale") 2491 tf = vtki.new("TransformPolyDataFilter") 2492 tf.SetInputConnection(rlabel.GetOutputPort()) 2493 t = vtki.vtkTransform() 2494 t.Scale(s * wsy / wsx, s, 1) 2495 tf.SetTransform(t) 2496 2497 app = vtki.new("AppendPolyData") 2498 app.AddInputConnection(tf.GetOutputPort()) 2499 app.AddInputData(pd) 2500 2501 mapper = vtki.new("PolyDataMapper2D") 2502 mapper.SetInputConnection(app.GetOutputPort()) 2503 cs = vtki.vtkCoordinate() 2504 cs.SetCoordinateSystem(1) 2505 mapper.SetTransformCoordinate(cs) 2506 2507 fractor = vedo.visual.Actor2D() 2508 csys = fractor.GetPositionCoordinate() 2509 csys.SetCoordinateSystem(3) 2510 fractor.SetPosition(pos) 2511 fractor.SetMapper(mapper) 2512 fractor.GetProperty().SetColor(vedo.get_color(c)) 2513 fractor.GetProperty().SetOpacity(alpha) 2514 fractor.GetProperty().SetLineWidth(lw) 2515 fractor.GetProperty().SetDisplayLocationToForeground() 2516 2517 def sifunc(iren, ev): 2518 wsx, wsy = self.window.GetSize() 2519 ps = self.camera.GetParallelScale() 2520 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2521 if units: 2522 newtxt += " " + units 2523 if rlabel.GetText() != newtxt: 2524 rlabel.SetText(newtxt) 2525 2526 self.renderer.AddActor(fractor) 2527 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2528 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2529 self.interactor.AddObserver("InteractionEvent", sifunc) 2530 sifunc(0, 0) 2531 return fractor 2532 2533 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2534 """ 2535 Create an Event object with information of what was clicked. 2536 2537 If `enable_picking` is False, no picking will be performed. 2538 This can be useful to avoid double picking when using buttons. 2539 """ 2540 if not self.interactor: 2541 return Event() 2542 2543 if len(pos) > 0: 2544 x, y = pos 2545 self.interactor.SetEventPosition(pos) 2546 else: 2547 x, y = self.interactor.GetEventPosition() 2548 self.renderer = self.interactor.FindPokedRenderer(x, y) 2549 2550 self.picked2d = (x, y) 2551 2552 key = self.interactor.GetKeySym() 2553 2554 if key: 2555 if "_L" in key or "_R" in key: 2556 # skip things like Shift_R 2557 key = "" # better than None 2558 else: 2559 if self.interactor.GetShiftKey(): 2560 key = key.upper() 2561 2562 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2563 key = "underscore" 2564 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2565 key = "plus" 2566 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2567 key = "?" 2568 2569 if self.interactor.GetControlKey(): 2570 key = "Ctrl+" + key 2571 2572 if self.interactor.GetAltKey(): 2573 key = "Alt+" + key 2574 2575 if enable_picking: 2576 if not self.picker: 2577 self.picker = vtki.vtkPropPicker() 2578 2579 self.picker.PickProp(x, y, self.renderer) 2580 actor = self.picker.GetProp3D() 2581 # Note that GetProp3D already picks Assembly 2582 2583 xp, yp = self.interactor.GetLastEventPosition() 2584 dx, dy = x - xp, y - yp 2585 2586 delta3d = np.array([0, 0, 0]) 2587 2588 if actor: 2589 picked3d = np.array(self.picker.GetPickPosition()) 2590 2591 try: 2592 vobj = actor.retrieve_object() 2593 old_pt = np.asarray(vobj.picked3d) 2594 vobj.picked3d = picked3d 2595 delta3d = picked3d - old_pt 2596 except (AttributeError, TypeError): 2597 pass 2598 2599 else: 2600 picked3d = None 2601 2602 if not actor: # try 2D 2603 actor = self.picker.GetActor2D() 2604 2605 event = Event() 2606 event.name = ename 2607 event.title = self.title 2608 event.id = -1 # will be set by the timer wrapper function 2609 event.timerid = -1 # will be set by the timer wrapper function 2610 event.priority = -1 # will be set by the timer wrapper function 2611 event.time = time.time() 2612 event.at = self.renderers.index(self.renderer) 2613 event.keypress = key 2614 if enable_picking: 2615 try: 2616 event.object = actor.retrieve_object() 2617 except AttributeError: 2618 event.object = actor 2619 try: 2620 event.actor = actor.retrieve_object() # obsolete use object instead 2621 except AttributeError: 2622 event.actor = actor 2623 event.picked3d = picked3d 2624 event.picked2d = (x, y) 2625 event.delta2d = (dx, dy) 2626 event.angle2d = np.arctan2(dy, dx) 2627 event.speed2d = np.sqrt(dx * dx + dy * dy) 2628 event.delta3d = delta3d 2629 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2630 event.isPoints = isinstance(event.object, vedo.Points) 2631 event.isMesh = isinstance(event.object, vedo.Mesh) 2632 event.isAssembly = isinstance(event.object, vedo.Assembly) 2633 event.isVolume = isinstance(event.object, vedo.Volume) 2634 event.isImage = isinstance(event.object, vedo.Image) 2635 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2636 return event 2637 2638 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2639 """ 2640 Add a function to be executed while show() is active. 2641 2642 Return a unique id for the callback. 2643 2644 The callback function (see example below) exposes a dictionary 2645 with the following information: 2646 - `name`: event name, 2647 - `id`: event unique identifier, 2648 - `priority`: event priority (float), 2649 - `interactor`: the interactor object, 2650 - `at`: renderer nr. where the event occurred 2651 - `keypress`: key pressed as string 2652 - `actor`: object picked by the mouse 2653 - `picked3d`: point picked in world coordinates 2654 - `picked2d`: screen coords of the mouse pointer 2655 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2656 - `delta3d`: ...same but in 3D world coords 2657 - `angle2d`: angle of mouse movement on screen 2658 - `speed2d`: speed of mouse movement on screen 2659 - `speed3d`: speed of picked point in world coordinates 2660 - `isPoints`: True if of class 2661 - `isMesh`: True if of class 2662 - `isAssembly`: True if of class 2663 - `isVolume`: True if of class Volume 2664 - `isImage`: True if of class 2665 2666 If `enable_picking` is False, no picking will be performed. 2667 This can be useful to avoid double picking when using buttons. 2668 2669 Frequently used events are: 2670 - `KeyPress`, `KeyRelease`: listen to keyboard events 2671 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2672 - `MiddleButtonPress`, `MiddleButtonRelease` 2673 - `RightButtonPress`, `RightButtonRelease` 2674 - `MouseMove`: listen to mouse pointer changing position 2675 - `MouseWheelForward`, `MouseWheelBackward` 2676 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2677 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2678 - `ResetCamera`, `ResetCameraClippingRange` 2679 - `Error`, `Warning` 2680 - `Char` 2681 - `Timer` 2682 2683 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2684 2685 Example: 2686 ```python 2687 from vedo import * 2688 2689 def func(evt): 2690 # this function is called every time the mouse moves 2691 # (evt is a dotted dictionary) 2692 if not evt.object: 2693 return # no hit, return 2694 print("point coords =", evt.picked3d) 2695 # print(evt) # full event dump 2696 2697 elli = Ellipsoid() 2698 plt = Plotter(axes=1) 2699 plt.add_callback('mouse hovering', func) 2700 plt.show(elli).close() 2701 ``` 2702 2703 Examples: 2704 - [spline_draw.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw.py) 2705 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2706 2707 ![](https://vedo.embl.es/images/advanced/spline_draw.png) 2708 2709 - ..and many others! 2710 """ 2711 from vtkmodules.util.misc import calldata_type 2712 2713 if not self.interactor: 2714 return 0 2715 2716 if vedo.settings.dry_run_mode >= 1: 2717 return 0 2718 2719 ######################################### 2720 @calldata_type(vtki.VTK_INT) 2721 def _func_wrap(iren, ename, timerid=None): 2722 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2723 event.timerid = timerid 2724 event.id = cid 2725 event.priority = priority 2726 self.last_event = event 2727 func(event) 2728 2729 ######################################### 2730 2731 event_name = utils.get_vtk_name_event(event_name) 2732 2733 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2734 # print(f"Registering event: {event_name} with id={cid}") 2735 return cid 2736 2737 def remove_callback(self, cid: Union[int, str]) -> "Plotter": 2738 """ 2739 Remove a callback function by its id 2740 or a whole category of callbacks by their name. 2741 2742 Arguments: 2743 cid : (int, str) 2744 Unique id of the callback. 2745 If an event name is passed all callbacks of that type are removed. 2746 """ 2747 if self.interactor: 2748 if isinstance(cid, str): 2749 cid = utils.get_vtk_name_event(cid) 2750 self.interactor.RemoveObservers(cid) 2751 else: 2752 self.interactor.RemoveObserver(cid) 2753 return self 2754 2755 def remove_all_observers(self) -> "Plotter": 2756 """ 2757 Remove all observers. 2758 2759 Example: 2760 ```python 2761 from vedo import * 2762 2763 def kfunc(event): 2764 print("Key pressed:", event.keypress) 2765 if event.keypress == 'q': 2766 plt.close() 2767 2768 def rfunc(event): 2769 if event.isImage: 2770 printc("Right-clicked!", event) 2771 plt.render() 2772 2773 img = Image(dataurl+"images/embryo.jpg") 2774 2775 plt = Plotter(size=(1050, 600)) 2776 plt.parallel_projection(True) 2777 plt.remove_all_observers() 2778 plt.add_callback("key press", kfunc) 2779 plt.add_callback("mouse right click", rfunc) 2780 plt.show("Right-Click Me! Press q to exit.", img) 2781 plt.close() 2782 ``` 2783 """ 2784 if self.interactor: 2785 self.interactor.RemoveAllObservers() 2786 return self 2787 2788 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2789 """ 2790 Start or stop an existing timer. 2791 2792 Arguments: 2793 action : (str) 2794 Either "create"/"start" or "destroy"/"stop" 2795 timer_id : (int) 2796 When stopping the timer, the ID of the timer as returned when created 2797 dt : (int) 2798 time in milliseconds between each repeated call 2799 one_shot : (bool) 2800 create a one shot timer of prescribed duration instead of a repeating one 2801 2802 Examples: 2803 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2804 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2805 2806 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 2807 """ 2808 if action in ("create", "start"): 2809 if timer_id is not None: 2810 vedo.logger.warning("you set a timer_id but it will be ignored.") 2811 if one_shot: 2812 timer_id = self.interactor.CreateOneShotTimer(dt) 2813 else: 2814 timer_id = self.interactor.CreateRepeatingTimer(dt) 2815 return timer_id 2816 2817 elif action in ("destroy", "stop"): 2818 if timer_id is not None: 2819 self.interactor.DestroyTimer(timer_id) 2820 else: 2821 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2822 else: 2823 e = f"in timer_callback(). Cannot understand action: {action}\n" 2824 e += " allowed actions are: ['start', 'stop']. Skipped." 2825 vedo.logger.error(e) 2826 return timer_id 2827 2828 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2829 """ 2830 Add a callback function that will be called when an event occurs. 2831 Consider using `add_callback()` instead. 2832 """ 2833 if not self.interactor: 2834 return -1 2835 event_name = utils.get_vtk_name_event(event_name) 2836 idd = self.interactor.AddObserver(event_name, func, priority) 2837 return idd 2838 2839 def compute_world_coordinate( 2840 self, 2841 pos2d: MutableSequence[float], 2842 at=None, 2843 objs=(), 2844 bounds=(), 2845 offset=None, 2846 pixeltol=None, 2847 worldtol=None, 2848 ) -> np.ndarray: 2849 """ 2850 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2851 If a set of meshes is passed then points are placed onto these. 2852 2853 Arguments: 2854 pos2d : (list) 2855 2D screen coordinates point. 2856 at : (int) 2857 renderer number. 2858 objs : (list) 2859 list of Mesh objects to project the point onto. 2860 bounds : (list) 2861 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2862 offset : (float) 2863 specify an offset value. 2864 pixeltol : (int) 2865 screen tolerance in pixels. 2866 worldtol : (float) 2867 world coordinates tolerance. 2868 2869 Returns: 2870 numpy array, the point in 3D world coordinates. 2871 2872 Examples: 2873 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2874 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2875 2876 ![](https://vedo.embl.es/images/basic/mousehover3.jpg) 2877 """ 2878 if at is not None: 2879 renderer = self.renderers[at] 2880 else: 2881 renderer = self.renderer 2882 2883 if not objs: 2884 pp = vtki.vtkFocalPlanePointPlacer() 2885 else: 2886 pps = vtki.vtkPolygonalSurfacePointPlacer() 2887 for ob in objs: 2888 pps.AddProp(ob.actor) 2889 pp = pps # type: ignore 2890 2891 if len(bounds) == 6: 2892 pp.SetPointBounds(bounds) 2893 if pixeltol: 2894 pp.SetPixelTolerance(pixeltol) 2895 if worldtol: 2896 pp.SetWorldTolerance(worldtol) 2897 if offset: 2898 pp.SetOffset(offset) 2899 2900 worldPos: MutableSequence[float] = [0, 0, 0] 2901 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2902 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2903 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2904 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2905 return np.array(worldPos) 2906 2907 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2908 """ 2909 Given a 3D points in the current renderer (or full window), 2910 find the screen pixel coordinates. 2911 2912 Example: 2913 ```python 2914 from vedo import * 2915 2916 elli = Ellipsoid().point_size(5) 2917 2918 plt = Plotter() 2919 plt.show(elli, "Press q to continue and print the info") 2920 2921 xyscreen = plt.compute_screen_coordinates(elli) 2922 print('xyscreen coords:', xyscreen) 2923 2924 # simulate an event happening at one point 2925 event = plt.fill_event(pos=xyscreen[123]) 2926 print(event) 2927 ``` 2928 """ 2929 try: 2930 obj = obj.vertices 2931 except AttributeError: 2932 pass 2933 2934 if utils.is_sequence(obj): 2935 pts = obj 2936 p2d = [] 2937 cs = vtki.vtkCoordinate() 2938 cs.SetCoordinateSystemToWorld() 2939 cs.SetViewport(self.renderer) 2940 for p in pts: 2941 cs.SetValue(p) 2942 if full_window: 2943 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2944 else: 2945 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2946 return np.array(p2d, dtype=int) 2947 2948 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2949 """ 2950 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2951 2952 Returns a frustum Mesh that contains the visible field of view. 2953 This can be used to select objects in a scene or select vertices. 2954 2955 Example: 2956 ```python 2957 from vedo import * 2958 2959 settings.enable_default_mouse_callbacks = False 2960 2961 def mode_select(objs): 2962 print("Selected objects:", objs) 2963 d0 = mode.start_x, mode.start_y # display coords 2964 d1 = mode.end_x, mode.end_y 2965 2966 frustum = plt.pick_area(d0, d1) 2967 col = np.random.randint(0, 10) 2968 infru = frustum.inside_points(mesh) 2969 infru.point_size(10).color(col) 2970 plt.add(frustum, infru).render() 2971 2972 mesh = Mesh(dataurl+"cow.vtk") 2973 mesh.color("k5").linewidth(1) 2974 2975 mode = interactor_modes.BlenderStyle() 2976 mode.callback_select = mode_select 2977 2978 plt = Plotter().user_mode(mode) 2979 plt.show(mesh, axes=1) 2980 ``` 2981 """ 2982 if at is not None: 2983 ren = self.renderers[at] 2984 else: 2985 ren = self.renderer 2986 area_picker = vtki.vtkAreaPicker() 2987 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 2988 planes = area_picker.GetFrustum() 2989 2990 fru = vtki.new("FrustumSource") 2991 fru.SetPlanes(planes) 2992 fru.ShowLinesOff() 2993 fru.Update() 2994 2995 afru = vedo.Mesh(fru.GetOutput()) 2996 afru.alpha(0.1).lw(1).pickable(False) 2997 afru.name = "Frustum" 2998 return afru 2999 3000 def _scan_input_return_acts(self, objs) -> Any: 3001 # scan the input and return a list of actors 3002 if not utils.is_sequence(objs): 3003 objs = [objs] 3004 3005 ################# 3006 wannabe_acts2 = [] 3007 for a in objs: 3008 3009 try: 3010 wannabe_acts2.append(a.actor) 3011 except AttributeError: 3012 wannabe_acts2.append(a) # already actor 3013 3014 try: 3015 wannabe_acts2.append(a.scalarbar) 3016 except AttributeError: 3017 pass 3018 3019 try: 3020 for sh in a.shadows: 3021 wannabe_acts2.append(sh.actor) 3022 except AttributeError: 3023 pass 3024 3025 try: 3026 wannabe_acts2.append(a.trail.actor) 3027 if a.trail.shadows: # trails may also have shadows 3028 for sh in a.trail.shadows: 3029 wannabe_acts2.append(sh.actor) 3030 except AttributeError: 3031 pass 3032 3033 ################# 3034 scanned_acts = [] 3035 for a in wannabe_acts2: # scan content of list 3036 3037 if a is None: 3038 pass 3039 3040 elif isinstance(a, (vtki.vtkActor, vtki.vtkActor2D)): 3041 scanned_acts.append(a) 3042 3043 elif isinstance(a, str): 3044 # assume a 2D comment was given 3045 changed = False # check if one already exists so to just update text 3046 if self.renderer: # might be jupyter 3047 acs = self.renderer.GetActors2D() 3048 acs.InitTraversal() 3049 for i in range(acs.GetNumberOfItems()): 3050 act = acs.GetNextItem() 3051 if isinstance(act, vedo.shapes.Text2D): 3052 aposx, aposy = act.GetPosition() 3053 if aposx < 0.01 and aposy > 0.99: # "top-left" 3054 act.text(a) # update content! no appending nada 3055 changed = True 3056 break 3057 if not changed: 3058 out = vedo.shapes.Text2D(a) # append a new one 3059 scanned_acts.append(out) 3060 # scanned_acts.append(vedo.shapes.Text2D(a)) # naive version 3061 3062 elif isinstance(a, vtki.vtkPolyData): 3063 scanned_acts.append(vedo.Mesh(a).actor) 3064 3065 elif isinstance(a, vtki.vtkImageData): 3066 scanned_acts.append(vedo.Volume(a).actor) 3067 3068 elif isinstance(a, vedo.RectilinearGrid): 3069 scanned_acts.append(a.actor) 3070 3071 elif isinstance(a, vedo.StructuredGrid): 3072 scanned_acts.append(a.actor) 3073 3074 elif isinstance(a, vtki.vtkLight): 3075 self.renderer.AddLight(a) 3076 3077 elif isinstance(a, vedo.visual.LightKit): 3078 a.lightkit.AddLightsToRenderer(self.renderer) 3079 3080 elif isinstance(a, vtki.get_class("MultiBlockDataSet")): 3081 for i in range(a.GetNumberOfBlocks()): 3082 b = a.GetBlock(i) 3083 if isinstance(b, vtki.vtkPolyData): 3084 scanned_acts.append(vedo.Mesh(b).actor) 3085 elif isinstance(b, vtki.vtkImageData): 3086 scanned_acts.append(vedo.Volume(b).actor) 3087 3088 elif isinstance(a, (vtki.vtkProp, vtki.vtkInteractorObserver)): 3089 scanned_acts.append(a) 3090 3091 elif "trimesh" in str(type(a)): 3092 scanned_acts.append(utils.trimesh2vedo(a)) 3093 3094 elif "meshlab" in str(type(a)): 3095 if "MeshSet" in str(type(a)): 3096 for i in range(a.number_meshes()): 3097 if a.mesh_id_exists(i): 3098 scanned_acts.append(utils.meshlab2vedo(a.mesh(i))) 3099 else: 3100 scanned_acts.append(utils.meshlab2vedo(a)) 3101 3102 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 3103 import vedo.dolfin as vdlf 3104 3105 scanned_acts.append(vdlf.IMesh(a).actor) 3106 3107 elif "madcad" in str(type(a)): 3108 scanned_acts.append(utils.madcad2vedo(a).actor) 3109 3110 elif "TetgenIO" in str(type(a)): 3111 scanned_acts.append(vedo.TetMesh(a).shrink(0.9).c("pink7").actor) 3112 3113 elif "matplotlib.figure.Figure" in str(type(a)): 3114 scanned_acts.append(vedo.Image(a).clone2d("top-right", 0.6)) 3115 3116 else: 3117 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 3118 3119 return scanned_acts 3120 3121 def show( 3122 self, 3123 *objects, 3124 at=None, 3125 axes=None, 3126 resetcam=None, 3127 zoom=False, 3128 interactive=None, 3129 viewup="", 3130 azimuth=0.0, 3131 elevation=0.0, 3132 roll=0.0, 3133 camera=None, 3134 mode=None, 3135 rate=None, 3136 bg=None, 3137 bg2=None, 3138 size=None, 3139 title=None, 3140 screenshot="", 3141 ) -> Any: 3142 """ 3143 Render a list of objects. 3144 3145 Arguments: 3146 at : (int) 3147 number of the renderer to plot to, in case of more than one exists 3148 3149 axes : (int) 3150 axis type-1 can be fully customized by passing a dictionary. 3151 Check `addons.Axes()` for the full list of options. 3152 set the type of axes to be shown: 3153 - 0, no axes 3154 - 1, draw three gray grid walls 3155 - 2, show cartesian axes from (0,0,0) 3156 - 3, show positive range of cartesian axes from (0,0,0) 3157 - 4, show a triad at bottom left 3158 - 5, show a cube at bottom left 3159 - 6, mark the corners of the bounding box 3160 - 7, draw a 3D ruler at each side of the cartesian axes 3161 - 8, show the `vtkCubeAxesActor` object 3162 - 9, show the bounding box outLine 3163 - 10, show three circles representing the maximum bounding box 3164 - 11, show a large grid on the x-y plane 3165 - 12, show polar axes 3166 - 13, draw a simple ruler at the bottom of the window 3167 3168 azimuth/elevation/roll : (float) 3169 move camera accordingly the specified value 3170 3171 viewup: str, list 3172 either `['x', 'y', 'z']` or a vector to set vertical direction 3173 3174 resetcam : (bool) 3175 re-adjust camera position to fit objects 3176 3177 camera : (dict, vtkCamera) 3178 camera parameters can further be specified with a dictionary 3179 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3180 - pos, `(list)`, the position of the camera in world coordinates 3181 - focal_point `(list)`, the focal point of the camera in world coordinates 3182 - viewup `(list)`, the view up direction for the camera 3183 - distance `(float)`, set the focal point to the specified distance from the camera position. 3184 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3185 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3186 in world-coordinate distances. The default is 1. 3187 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3188 This method has no effect in perspective projection mode. 3189 3190 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3191 plane to be set a distance 'thickness' beyond the near clipping plane. 3192 3193 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3194 measured in degrees. The default angle is 30 degrees. 3195 This method has no effect in parallel projection mode. 3196 The formula for setting the angle up for perfect perspective viewing is: 3197 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3198 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3199 3200 interactive : (bool) 3201 pause and interact with window (True) or continue execution (False) 3202 3203 rate : (float) 3204 maximum rate of `show()` in Hertz 3205 3206 mode : (int, str) 3207 set the type of interaction: 3208 - 0 = TrackballCamera [default] 3209 - 1 = TrackballActor 3210 - 2 = JoystickCamera 3211 - 3 = JoystickActor 3212 - 4 = Flight 3213 - 5 = RubberBand2D 3214 - 6 = RubberBand3D 3215 - 7 = RubberBandZoom 3216 - 8 = Terrain 3217 - 9 = Unicam 3218 - 10 = Image 3219 - Check out `vedo.interaction_modes` for more options. 3220 3221 bg : (str, list) 3222 background color in RGB format, or string name 3223 3224 bg2 : (str, list) 3225 second background color to create a gradient background 3226 3227 size : (str, list) 3228 size of the window, e.g. size="fullscreen", or size=[600,400] 3229 3230 title : (str) 3231 window title text 3232 3233 screenshot : (str) 3234 save a screenshot of the window to file 3235 """ 3236 3237 if vedo.settings.dry_run_mode >= 2: 3238 return self 3239 3240 if self.wx_widget: 3241 return self 3242 3243 if self.renderers: # in case of notebooks 3244 3245 if at is None: 3246 at = self.renderers.index(self.renderer) 3247 3248 else: 3249 3250 if at >= len(self.renderers): 3251 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3252 vedo.logger.error(t) 3253 return self 3254 3255 self.renderer = self.renderers[at] 3256 3257 if title is not None: 3258 self.title = title 3259 3260 if size is not None: 3261 self.size = size 3262 if self.size[0] == "f": # full screen 3263 self.size = "fullscreen" 3264 self.window.SetFullScreen(True) 3265 self.window.BordersOn() 3266 else: 3267 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3268 3269 if vedo.settings.default_backend == "vtk": 3270 if str(bg).endswith(".hdr"): 3271 self._add_skybox(bg) 3272 else: 3273 if bg is not None: 3274 self.backgrcol = vedo.get_color(bg) 3275 self.renderer.SetBackground(self.backgrcol) 3276 if bg2 is not None: 3277 self.renderer.GradientBackgroundOn() 3278 self.renderer.SetBackground2(vedo.get_color(bg2)) 3279 3280 if axes is not None: 3281 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3282 objects = list(objects) 3283 objects.append(axes) # move it into the list of normal things to show 3284 axes = 0 3285 self.axes = axes 3286 3287 if interactive is not None: 3288 self._interactive = interactive 3289 if self.offscreen: 3290 self._interactive = False 3291 3292 # camera stuff 3293 if resetcam is not None: 3294 self.resetcam = resetcam 3295 3296 if camera is not None: 3297 self.resetcam = False 3298 viewup = "" 3299 if isinstance(camera, vtki.vtkCamera): 3300 cameracopy = vtki.vtkCamera() 3301 cameracopy.DeepCopy(camera) 3302 self.camera = cameracopy 3303 else: 3304 self.camera = utils.camera_from_dict(camera) 3305 3306 self.add(objects) 3307 3308 # Backend ############################################################### 3309 if vedo.settings.default_backend in ["k3d"]: 3310 return backends.get_notebook_backend(self.objects) 3311 ######################################################################### 3312 3313 for ia in utils.flatten(objects): 3314 try: 3315 # fix gray color labels and title to white or black 3316 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3317 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3318 c = (0.9, 0.9, 0.9) 3319 if np.sum(self.renderer.GetBackground()) > 1.5: 3320 c = (0.1, 0.1, 0.1) 3321 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3322 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3323 except AttributeError: 3324 pass 3325 3326 if self.sharecam: 3327 for r in self.renderers: 3328 r.SetActiveCamera(self.camera) 3329 3330 if self.axes is not None: 3331 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3332 bns = self.renderer.ComputeVisiblePropBounds() 3333 addons.add_global_axes(self.axes, bounds=bns) 3334 3335 # Backend ############################################################### 3336 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3337 return backends.get_notebook_backend() 3338 ######################################################################### 3339 3340 if self.resetcam: 3341 self.renderer.ResetCamera() 3342 3343 if len(self.renderers) > 1: 3344 self.add_renderer_frame() 3345 3346 if vedo.settings.default_backend == "2d" and not zoom: 3347 zoom = "tightest" 3348 3349 if zoom: 3350 if zoom == "tight": 3351 self.reset_camera(tight=0.04) 3352 elif zoom == "tightest": 3353 self.reset_camera(tight=0.0001) 3354 else: 3355 self.camera.Zoom(zoom) 3356 if elevation: 3357 self.camera.Elevation(elevation) 3358 if azimuth: 3359 self.camera.Azimuth(azimuth) 3360 if roll: 3361 self.camera.Roll(roll) 3362 3363 if len(viewup) > 0: 3364 b = self.renderer.ComputeVisiblePropBounds() 3365 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3366 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3367 if viewup == "x": 3368 sz = np.linalg.norm(sz) 3369 self.camera.SetViewUp([1, 0, 0]) 3370 self.camera.SetPosition(cm + sz) 3371 elif viewup == "y": 3372 sz = np.linalg.norm(sz) 3373 self.camera.SetViewUp([0, 1, 0]) 3374 self.camera.SetPosition(cm + sz) 3375 elif viewup == "z": 3376 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3377 self.camera.SetViewUp([0, 0, 1]) 3378 self.camera.SetPosition(cm + 2 * sz) 3379 elif utils.is_sequence(viewup): 3380 sz = np.linalg.norm(sz) 3381 self.camera.SetViewUp(viewup) 3382 cpos = np.cross([0, 1, 0], viewup) 3383 self.camera.SetPosition(cm - 2 * sz * cpos) 3384 3385 self.renderer.ResetCameraClippingRange() 3386 3387 self.initialize_interactor() 3388 3389 if vedo.settings.immediate_rendering: 3390 self.window.Render() ##################### <-------------- Render 3391 3392 if self.interactor: # can be offscreen or not the vtk backend.. 3393 3394 self.window.SetWindowName(self.title) 3395 3396 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3397 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3398 # print(pic.dataset)# Array 0 name PNGImage 3399 # self.window.SetIcon(pic.dataset) 3400 3401 try: 3402 # Needs "pip install pyobjc" on Mac OSX 3403 if ( 3404 self._cocoa_initialized is False 3405 and "Darwin" in vedo.sys_platform 3406 and not self.offscreen 3407 ): 3408 self._cocoa_initialized = True 3409 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3410 pid = os.getpid() 3411 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3412 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3413 except: 3414 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3415 pass 3416 3417 # Set the interaction style 3418 if mode is not None: 3419 self.user_mode(mode) 3420 if self.qt_widget and mode is None: 3421 self.user_mode(0) 3422 3423 if screenshot: 3424 self.screenshot(screenshot) 3425 3426 if self._interactive: 3427 self.interactor.Start() 3428 if self._must_close_now: 3429 self.interactor.GetRenderWindow().Finalize() 3430 self.interactor.TerminateApp() 3431 self.camera = None 3432 self.renderer = None 3433 self.renderers = [] 3434 self.window = None 3435 self.interactor = None 3436 return self 3437 3438 if rate: 3439 if self.clock is None: # set clock and limit rate 3440 self._clockt0 = time.time() 3441 self.clock = 0.0 3442 else: 3443 t = time.time() - self._clockt0 3444 elapsed = t - self.clock 3445 mint = 1.0 / rate 3446 if elapsed < mint: 3447 time.sleep(mint - elapsed) 3448 self.clock = time.time() - self._clockt0 3449 3450 # 2d #################################################################### 3451 if vedo.settings.default_backend == "2d": 3452 return backends.get_notebook_backend() 3453 ######################################################################### 3454 3455 return self 3456 3457 3458 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3459 """Add a draggable inset space into a renderer. 3460 3461 Arguments: 3462 at : (int) 3463 specify the renderer number 3464 pos : (list) 3465 icon position in the range [1-4] indicating one of the 4 corners, 3466 or it can be a tuple (x,y) as a fraction of the renderer size. 3467 size : (float) 3468 size of the square inset 3469 draggable : (bool) 3470 if True the subrenderer space can be dragged around 3471 c : (color) 3472 color of the inset frame when dragged 3473 3474 Examples: 3475 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3476 3477 ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg) 3478 """ 3479 if not self.interactor: 3480 return None 3481 3482 if not self.renderer: 3483 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3484 return None 3485 3486 options = dict(options) 3487 pos = options.pop("pos", 0) 3488 size = options.pop("size", 0.1) 3489 c = options.pop("c", "lb") 3490 at = options.pop("at", None) 3491 draggable = options.pop("draggable", True) 3492 3493 widget = vtki.vtkOrientationMarkerWidget() 3494 r, g, b = vedo.get_color(c) 3495 widget.SetOutlineColor(r, g, b) 3496 if len(objects) == 1: 3497 widget.SetOrientationMarker(objects[0].actor) 3498 else: 3499 widget.SetOrientationMarker(vedo.Assembly(objects)) 3500 3501 widget.SetInteractor(self.interactor) 3502 3503 if utils.is_sequence(pos): 3504 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3505 else: 3506 if pos < 2: 3507 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3508 elif pos == 2: 3509 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3510 elif pos == 3: 3511 widget.SetViewport(0, 0, size * 2, size * 2) 3512 elif pos == 4: 3513 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3514 widget.EnabledOn() 3515 widget.SetInteractive(draggable) 3516 if at is not None and at < len(self.renderers): 3517 widget.SetCurrentRenderer(self.renderers[at]) 3518 else: 3519 widget.SetCurrentRenderer(self.renderer) 3520 self.widgets.append(widget) 3521 return widget 3522 3523 def clear(self, at=None, deep=False) -> "Plotter": 3524 """Clear the scene from all meshes and volumes.""" 3525 if at is not None: 3526 renderer = self.renderers[at] 3527 else: 3528 renderer = self.renderer 3529 if not renderer: 3530 return self 3531 3532 if deep: 3533 renderer.RemoveAllViewProps() 3534 else: 3535 for ob in set( 3536 self.get_meshes() 3537 + self.get_volumes() 3538 + self.objects 3539 + self.axes_instances 3540 ): 3541 if isinstance(ob, vedo.shapes.Text2D): 3542 continue 3543 self.remove(ob) 3544 try: 3545 if ob.scalarbar: 3546 self.remove(ob.scalarbar) 3547 except AttributeError: 3548 pass 3549 return self 3550 3551 def break_interaction(self) -> "Plotter": 3552 """Break window interaction and return to the python execution flow""" 3553 if self.interactor: 3554 self.check_actors_trasform() 3555 self.interactor.ExitCallback() 3556 return self 3557 3558 def user_mode(self, mode) -> Union["Plotter", None]: 3559 """ 3560 Modify the user interaction mode. 3561 3562 Examples: 3563 ```python 3564 from vedo import * 3565 mode = interactor_modes.MousePan() 3566 mesh = Mesh(dataurl+"cow.vtk") 3567 plt = Plotter().user_mode(mode) 3568 plt.show(mesh, axes=1) 3569 ``` 3570 See also: 3571 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3572 """ 3573 if not self.interactor: 3574 return None 3575 3576 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3577 # print("Current style:", curr_style) 3578 if curr_style.endswith("Actor"): 3579 self.check_actors_trasform() 3580 3581 if isinstance(mode, (str, int)): 3582 # Set the style of interaction 3583 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3584 if mode in (0, "TrackballCamera"): 3585 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3586 self.interactor.RemoveObservers("CharEvent") 3587 elif mode in (1, "TrackballActor"): 3588 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3589 elif mode in (2, "JoystickCamera"): 3590 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3591 elif mode in (3, "JoystickActor"): 3592 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3593 elif mode in (4, "Flight"): 3594 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3595 elif mode in (5, "RubberBand2D"): 3596 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3597 elif mode in (6, "RubberBand3D"): 3598 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3599 elif mode in (7, "RubberBandZoom"): 3600 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3601 elif mode in (8, "Terrain"): 3602 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3603 elif mode in (9, "Unicam"): 3604 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3605 elif mode in (10, "Image", "image", "2d"): 3606 astyle = vtki.new("InteractorStyleImage") 3607 astyle.SetInteractionModeToImage3D() 3608 self.interactor.SetInteractorStyle(astyle) 3609 else: 3610 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3611 3612 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3613 # set a custom interactor style 3614 if hasattr(mode, "interactor"): 3615 mode.interactor = self.interactor 3616 mode.renderer = self.renderer # type: ignore 3617 mode.SetInteractor(self.interactor) 3618 mode.SetDefaultRenderer(self.renderer) 3619 self.interactor.SetInteractorStyle(mode) 3620 3621 return self 3622 3623 def close(self) -> "Plotter": 3624 """Close the plotter.""" 3625 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3626 vedo.last_figure = None 3627 self.last_event = None 3628 self.sliders = [] 3629 self.buttons = [] 3630 self.widgets = [] 3631 self.hover_legends = [] 3632 self.background_renderer = None 3633 self._extralight = None 3634 3635 self.hint_widget = None 3636 self.cutter_widget = None 3637 3638 if vedo.settings.dry_run_mode >= 2: 3639 return self 3640 3641 if not hasattr(self, "window"): 3642 return self 3643 if not self.window: 3644 return self 3645 if not hasattr(self, "interactor"): 3646 return self 3647 if not self.interactor: 3648 return self 3649 3650 ################################################### 3651 try: 3652 if "Darwin" in vedo.sys_platform: 3653 self.interactor.ProcessEvents() 3654 except: 3655 pass 3656 3657 self._must_close_now = True 3658 3659 if vedo.plotter_instance == self: 3660 vedo.plotter_instance = None 3661 3662 if self.interactor and self._interactive: 3663 self.break_interaction() 3664 elif self._must_close_now: 3665 # dont call ExitCallback here 3666 self.interactor.GetRenderWindow().Finalize() 3667 self.interactor.TerminateApp() 3668 self.camera = None 3669 self.renderer = None 3670 self.renderers = [] 3671 self.window = None 3672 self.interactor = None 3673 return self 3674 3675 @property 3676 def camera(self): 3677 """Return the current active camera.""" 3678 if self.renderer: 3679 return self.renderer.GetActiveCamera() 3680 3681 @camera.setter 3682 def camera(self, cam): 3683 if self.renderer: 3684 if isinstance(cam, dict): 3685 cam = utils.camera_from_dict(cam) 3686 self.renderer.SetActiveCamera(cam) 3687 3688 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3689 """ 3690 Take a screenshot of the Plotter window. 3691 3692 Arguments: 3693 scale : (int) 3694 set image magnification as an integer multiplicating factor 3695 asarray : (bool) 3696 return a numpy array of the image instead of writing a file 3697 3698 Warning: 3699 If you get black screenshots try to set `interactive=False` in `show()` 3700 then call `screenshot()` and `plt.interactive()` afterwards. 3701 3702 Example: 3703 ```py 3704 from vedo import * 3705 sphere = Sphere().linewidth(1) 3706 plt = show(sphere, interactive=False) 3707 plt.screenshot('image.png') 3708 plt.interactive() 3709 plt.close() 3710 ``` 3711 3712 Example: 3713 ```py 3714 from vedo import * 3715 sphere = Sphere().linewidth(1) 3716 plt = show(sphere, interactive=False) 3717 plt.screenshot('anotherimage.png') 3718 plt.interactive() 3719 plt.close() 3720 ``` 3721 """ 3722 return vedo.file_io.screenshot(filename, scale, asarray) 3723 3724 def toimage(self, scale=1) -> "vedo.image.Image": 3725 """ 3726 Generate a `Image` object from the current rendering window. 3727 3728 Arguments: 3729 scale : (int) 3730 set image magnification as an integer multiplicating factor 3731 """ 3732 if vedo.settings.screeshot_large_image: 3733 w2if = vtki.new("RenderLargeImage") 3734 w2if.SetInput(self.renderer) 3735 w2if.SetMagnification(scale) 3736 else: 3737 w2if = vtki.new("WindowToImageFilter") 3738 w2if.SetInput(self.window) 3739 if hasattr(w2if, "SetScale"): 3740 w2if.SetScale(scale, scale) 3741 if vedo.settings.screenshot_transparent_background: 3742 w2if.SetInputBufferTypeToRGBA() 3743 w2if.ReadFrontBufferOff() # read from the back buffer 3744 w2if.Update() 3745 return vedo.image.Image(w2if.GetOutput()) 3746 3747 def export(self, filename="scene.npz", binary=False) -> "Plotter": 3748 """ 3749 Export scene to file to HTML, X3D or Numpy file. 3750 3751 Examples: 3752 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3753 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3754 """ 3755 vedo.file_io.export_window(filename, binary=binary) 3756 return self 3757 3758 def color_picker(self, xy, verbose=False): 3759 """Pick color of specific (x,y) pixel on the screen.""" 3760 w2if = vtki.new("WindowToImageFilter") 3761 w2if.SetInput(self.window) 3762 w2if.ReadFrontBufferOff() 3763 w2if.Update() 3764 nx, ny = self.window.GetSize() 3765 varr = w2if.GetOutput().GetPointData().GetScalars() 3766 3767 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3768 x, y = int(xy[0]), int(xy[1]) 3769 if y < ny and x < nx: 3770 3771 rgb = arr[y, x] 3772 3773 if verbose: 3774 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3775 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3776 vedo.printc("█", c=[0, rgb[1], 0], end="") 3777 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3778 vedo.printc("] = ", end="") 3779 cnm = vedo.get_color_name(rgb) 3780 if np.sum(rgb) < 150: 3781 vedo.printc( 3782 rgb.tolist(), 3783 vedo.colors.rgb2hex(np.array(rgb) / 255), 3784 c="w", 3785 bc=rgb, 3786 invert=1, 3787 end="", 3788 ) 3789 vedo.printc(" -> " + cnm, invert=1, c="w") 3790 else: 3791 vedo.printc( 3792 rgb.tolist(), 3793 vedo.colors.rgb2hex(np.array(rgb) / 255), 3794 c=rgb, 3795 end="", 3796 ) 3797 vedo.printc(" -> " + cnm, c=cnm) 3798 3799 return rgb 3800 3801 return None 3802 3803 ####################################################################### 3804 def _default_mouseleftclick(self, iren, event) -> None: 3805 x, y = iren.GetEventPosition() 3806 renderer = iren.FindPokedRenderer(x, y) 3807 picker = vtki.vtkPropPicker() 3808 picker.PickProp(x, y, renderer) 3809 3810 self.renderer = renderer 3811 3812 clicked_actor = picker.GetActor() 3813 # clicked_actor2D = picker.GetActor2D() 3814 3815 # print('_default_mouseleftclick mouse at', x, y) 3816 # print("picked Volume:", [picker.GetVolume()]) 3817 # print("picked Actor2D:", [picker.GetActor2D()]) 3818 # print("picked Assembly:", [picker.GetAssembly()]) 3819 # print("picked Prop3D:", [picker.GetProp3D()]) 3820 3821 if not clicked_actor: 3822 clicked_actor = picker.GetAssembly() 3823 3824 if not clicked_actor: 3825 clicked_actor = picker.GetProp3D() 3826 3827 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3828 return 3829 3830 self.picked3d = picker.GetPickPosition() 3831 self.picked2d = np.array([x, y]) 3832 3833 if not clicked_actor: 3834 return 3835 3836 self.justremoved = None 3837 self.clicked_actor = clicked_actor 3838 3839 try: # might not be a vedo obj 3840 self.clicked_object = clicked_actor.retrieve_object() 3841 # save this info in the object itself 3842 self.clicked_object.picked3d = self.picked3d 3843 self.clicked_object.picked2d = self.picked2d 3844 except AttributeError: 3845 pass 3846 3847 # ----------- 3848 # if "Histogram1D" in picker.GetAssembly().__class__.__name__: 3849 # histo = picker.GetAssembly() 3850 # if histo.verbose: 3851 # x = self.picked3d[0] 3852 # idx = np.digitize(x, histo.edges) - 1 3853 # f = histo.frequencies[idx] 3854 # cn = histo.centers[idx] 3855 # vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3856 3857 ####################################################################### 3858 def _default_keypress(self, iren, event) -> None: 3859 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3860 3861 key = iren.GetKeySym() 3862 3863 if "_L" in key or "_R" in key: 3864 return 3865 3866 if iren.GetShiftKey(): 3867 key = key.upper() 3868 3869 if iren.GetControlKey(): 3870 key = "Ctrl+" + key 3871 3872 if iren.GetAltKey(): 3873 key = "Alt+" + key 3874 3875 ####################################################### 3876 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3877 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3878 # iren.GetKeyCode(), iren.GetRepeatCount()) 3879 ####################################################### 3880 3881 x, y = iren.GetEventPosition() 3882 renderer = iren.FindPokedRenderer(x, y) 3883 3884 if key in ["q", "Return"]: 3885 self.break_interaction() 3886 return 3887 3888 elif key in ["Ctrl+q", "Ctrl+w", "Escape"]: 3889 self.close() 3890 return 3891 3892 elif key == "F1": 3893 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3894 self.break_interaction() 3895 sys.exit(0) 3896 3897 elif key == "Down": 3898 if self.clicked_object and self.clicked_object in self.get_meshes(): 3899 self.clicked_object.alpha(0.02) 3900 if hasattr(self.clicked_object, "properties_backface"): 3901 bfp = self.clicked_actor.GetBackfaceProperty() 3902 self.clicked_object.properties_backface = bfp # save it 3903 self.clicked_actor.SetBackfaceProperty(None) 3904 else: 3905 for obj in self.get_meshes(): 3906 if obj: 3907 obj.alpha(0.02) 3908 bfp = obj.actor.GetBackfaceProperty() 3909 if bfp and hasattr(obj, "properties_backface"): 3910 obj.properties_backface = bfp 3911 obj.actor.SetBackfaceProperty(None) 3912 3913 elif key == "Left": 3914 if self.clicked_object and self.clicked_object in self.get_meshes(): 3915 ap = self.clicked_object.properties 3916 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3917 ap.SetOpacity(aal) 3918 bfp = self.clicked_actor.GetBackfaceProperty() 3919 if bfp and hasattr(self.clicked_object, "properties_backface"): 3920 self.clicked_object.properties_backface = bfp 3921 self.clicked_actor.SetBackfaceProperty(None) 3922 else: 3923 for a in self.get_meshes(): 3924 if a: 3925 ap = a.properties 3926 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3927 ap.SetOpacity(aal) 3928 bfp = a.actor.GetBackfaceProperty() 3929 if bfp and hasattr(a, "properties_backface"): 3930 a.properties_backface = bfp 3931 a.actor.SetBackfaceProperty(None) 3932 3933 elif key == "Right": 3934 if self.clicked_object and self.clicked_object in self.get_meshes(): 3935 ap = self.clicked_object.properties 3936 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3937 ap.SetOpacity(aal) 3938 if ( 3939 aal == 1 3940 and hasattr(self.clicked_object, "properties_backface") 3941 and self.clicked_object.properties_backface 3942 ): 3943 # put back 3944 self.clicked_actor.SetBackfaceProperty( 3945 self.clicked_object.properties_backface) 3946 else: 3947 for a in self.get_meshes(): 3948 if a: 3949 ap = a.properties 3950 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3951 ap.SetOpacity(aal) 3952 if aal == 1 and hasattr(a, "properties_backface") and a.properties_backface: 3953 a.actor.SetBackfaceProperty(a.properties_backface) 3954 3955 elif key == "Up": 3956 if self.clicked_object and self.clicked_object in self.get_meshes(): 3957 self.clicked_object.properties.SetOpacity(1) 3958 if hasattr(self.clicked_object, "properties_backface") and self.clicked_object.properties_backface: 3959 self.clicked_object.actor.SetBackfaceProperty(self.clicked_object.properties_backface) 3960 else: 3961 for a in self.get_meshes(): 3962 if a: 3963 a.properties.SetOpacity(1) 3964 if hasattr(a, "properties_backface") and a.properties_backface: 3965 a.actor.SetBackfaceProperty(a.properties_backface) 3966 3967 elif key == "P": 3968 if self.clicked_object and self.clicked_object in self.get_meshes(): 3969 objs = [self.clicked_object] 3970 else: 3971 objs = self.get_meshes() 3972 for ia in objs: 3973 try: 3974 ps = ia.properties.GetPointSize() 3975 if ps > 1: 3976 ia.properties.SetPointSize(ps - 1) 3977 ia.properties.SetRepresentationToPoints() 3978 except AttributeError: 3979 pass 3980 3981 elif key == "p": 3982 if self.clicked_object and self.clicked_object in self.get_meshes(): 3983 objs = [self.clicked_object] 3984 else: 3985 objs = self.get_meshes() 3986 for ia in objs: 3987 try: 3988 ps = ia.properties.GetPointSize() 3989 ia.properties.SetPointSize(ps + 2) 3990 ia.properties.SetRepresentationToPoints() 3991 except AttributeError: 3992 pass 3993 3994 elif key == "U": 3995 pval = renderer.GetActiveCamera().GetParallelProjection() 3996 renderer.GetActiveCamera().SetParallelProjection(not pval) 3997 if pval: 3998 renderer.ResetCamera() 3999 4000 elif key == "r": 4001 renderer.ResetCamera() 4002 4003 elif key == "h": 4004 msg = f" vedo {vedo.__version__}" 4005 msg += f" | vtk {vtki.vtkVersion().GetVTKVersion()}" 4006 msg += f" | numpy {np.__version__}" 4007 msg += f" | python {sys.version_info[0]}.{sys.version_info[1]}, press: " 4008 vedo.printc(msg.ljust(75), invert=True) 4009 msg = ( 4010 " i print info about the last clicked object \n" 4011 " I print color of the pixel under the mouse \n" 4012 " Y show the pipeline for this object as a graph \n" 4013 " <- -> use arrows to reduce/increase opacity \n" 4014 " x toggle mesh visibility \n" 4015 " w toggle wireframe/surface style \n" 4016 " l toggle surface edges visibility \n" 4017 " p/P hide surface faces and show only points \n" 4018 " 1-3 cycle surface color (2=light, 3=dark) \n" 4019 " 4 cycle color map (press shift-4 to go back) \n" 4020 " 5-6 cycle point-cell arrays (shift to go back) \n" 4021 " 7-8 cycle background and gradient color \n" 4022 " 09+- cycle axes styles (on keypad, or press +/-) \n" 4023 " k cycle available lighting styles \n" 4024 " K toggle shading as flat or phong \n" 4025 " A toggle anti-aliasing \n" 4026 " D toggle depth-peeling (for transparencies) \n" 4027 " U toggle perspective/parallel projection \n" 4028 " o/O toggle extra light to scene and rotate it \n" 4029 " a toggle interaction to Actor Mode \n" 4030 " n toggle surface normals \n" 4031 " r reset camera position \n" 4032 " R reset camera to the closest orthogonal view \n" 4033 " . fly camera to the last clicked point \n" 4034 " C print the current camera parameters state \n" 4035 " X invoke a cutter widget tool \n" 4036 " S save a screenshot of the current scene \n" 4037 " E/F export 3D scene to numpy file or X3D \n" 4038 " q return control to python script \n" 4039 " Esc abort execution and exit python kernel " 4040 ) 4041 vedo.printc(msg, dim=True, italic=True, bold=True) 4042 vedo.printc( 4043 " Check out the documentation at: https://vedo.embl.es ".ljust(75), 4044 invert=True, 4045 bold=True, 4046 ) 4047 return 4048 4049 elif key == "a": 4050 cur = iren.GetInteractorStyle() 4051 if isinstance(cur, vtki.get_class("InteractorStyleTrackballCamera")): 4052 msg = "Interactor style changed to TrackballActor\n" 4053 msg += " you can now move and rotate individual meshes:\n" 4054 msg += " press X twice to save the repositioned mesh\n" 4055 msg += " press 'a' to go back to normal style" 4056 vedo.printc(msg) 4057 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 4058 else: 4059 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 4060 return 4061 4062 elif key == "A": # toggle antialiasing 4063 msam = self.window.GetMultiSamples() 4064 if not msam: 4065 self.window.SetMultiSamples(16) 4066 else: 4067 self.window.SetMultiSamples(0) 4068 msam = self.window.GetMultiSamples() 4069 if msam: 4070 vedo.printc(f"Antialiasing set to {msam} samples", c=bool(msam)) 4071 else: 4072 vedo.printc("Antialiasing disabled", c=bool(msam)) 4073 4074 elif key == "D": # toggle depthpeeling 4075 udp = not renderer.GetUseDepthPeeling() 4076 renderer.SetUseDepthPeeling(udp) 4077 # self.renderer.SetUseDepthPeelingForVolumes(udp) 4078 if udp: 4079 self.window.SetAlphaBitPlanes(1) 4080 renderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 4081 renderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 4082 self.interactor.Render() 4083 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 4084 rnr = self.renderers.index(renderer) 4085 vedo.printc(f"Depth peeling set to {udp} for renderer nr.{rnr}", c=udp) 4086 if not wasUsed and udp: 4087 vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True) 4088 return 4089 4090 elif key == "period": 4091 if self.picked3d: 4092 self.fly_to(self.picked3d) 4093 return 4094 4095 elif key == "S": 4096 vedo.file_io.screenshot("screenshot.png") 4097 vedo.printc(r":camera: Saved rendering window to 'screenshot.png'", c="b") 4098 return 4099 4100 elif key == "C": 4101 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 4102 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 4103 # This could happen for plotting geological models with UTM coordinate systems 4104 cam = renderer.GetActiveCamera() 4105 vedo.printc("\n###################################################", c="y") 4106 vedo.printc("## Template python code to position this camera: ##", c="y") 4107 vedo.printc("cam = dict(", c="y") 4108 vedo.printc(" position=" + utils.precision(cam.GetPosition(), 6) + ",", c="y") 4109 vedo.printc(" focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y") 4110 vedo.printc(" viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y") 4111 vedo.printc(" roll=" + utils.precision(cam.GetRoll(), 6) + ",", c="y") 4112 if cam.GetParallelProjection(): 4113 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 4114 else: 4115 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 4116 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 4117 vedo.printc(')', c='y') 4118 vedo.printc('show(mymeshes, camera=cam)', c='y') 4119 vedo.printc('###################################################', c='y') 4120 return 4121 4122 elif key == "R": 4123 self.reset_viewup() 4124 4125 elif key == "w": 4126 try: 4127 if self.clicked_object.properties.GetRepresentation() == 1: # toggle 4128 self.clicked_object.properties.SetRepresentationToSurface() 4129 else: 4130 self.clicked_object.properties.SetRepresentationToWireframe() 4131 except AttributeError: 4132 pass 4133 4134 elif key == "1": 4135 try: 4136 self._icol += 1 4137 self.clicked_object.mapper.ScalarVisibilityOff() 4138 pal = vedo.colors.palettes[vedo.settings.palette % len(vedo.colors.palettes)] 4139 self.clicked_object.c(pal[(self._icol) % 10]) 4140 self.remove(self.clicked_object.scalarbar) 4141 except AttributeError: 4142 pass 4143 4144 elif key == "2": # dark colors 4145 try: 4146 bsc = ["k1", "k2", "k3", "k4", 4147 "b1", "b2", "b3", "b4", 4148 "p1", "p2", "p3", "p4", 4149 "g1", "g2", "g3", "g4", 4150 "r1", "r2", "r3", "r4", 4151 "o1", "o2", "o3", "o4", 4152 "y1", "y2", "y3", "y4"] 4153 self._icol += 1 4154 if self.clicked_object: 4155 self.clicked_object.mapper.ScalarVisibilityOff() 4156 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4157 self.clicked_object.c(newcol) 4158 self.remove(self.clicked_object.scalarbar) 4159 except AttributeError: 4160 pass 4161 4162 elif key == "3": # light colors 4163 try: 4164 bsc = ["k6", "k7", "k8", "k9", 4165 "b6", "b7", "b8", "b9", 4166 "p6", "p7", "p8", "p9", 4167 "g6", "g7", "g8", "g9", 4168 "r6", "r7", "r8", "r9", 4169 "o6", "o7", "o8", "o9", 4170 "y6", "y7", "y8", "y9"] 4171 self._icol += 1 4172 if self.clicked_object: 4173 self.clicked_object.mapper.ScalarVisibilityOff() 4174 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4175 self.clicked_object.c(newcol) 4176 self.remove(self.clicked_object.scalarbar) 4177 except AttributeError: 4178 pass 4179 4180 elif key == "4": # cmap name cycle 4181 ob = self.clicked_object 4182 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4183 return 4184 if not ob.mapper.GetScalarVisibility(): 4185 return 4186 onwhat = ob.mapper.GetScalarModeAsString() # UsePointData/UseCellData 4187 4188 cmap_names = [ 4189 "Accent", "Paired", 4190 "rainbow", "rainbow_r", 4191 "Spectral", "Spectral_r", 4192 "gist_ncar", "gist_ncar_r", 4193 "viridis", "viridis_r", 4194 "hot", "hot_r", 4195 "terrain", "ocean", 4196 "coolwarm", "seismic", "PuOr", "RdYlGn", 4197 ] 4198 try: 4199 i = cmap_names.index(ob._cmap_name) 4200 if iren.GetShiftKey(): 4201 i -= 1 4202 else: 4203 i += 1 4204 if i >= len(cmap_names): 4205 i = 0 4206 if i < 0: 4207 i = len(cmap_names) - 1 4208 except ValueError: 4209 i = 0 4210 4211 ob._cmap_name = cmap_names[i] 4212 ob.cmap(ob._cmap_name, on=onwhat) 4213 if ob.scalarbar: 4214 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4215 self.remove(ob.scalarbar) 4216 title = ob.scalarbar.GetTitle() 4217 ob.add_scalarbar(title=title) 4218 self.add(ob.scalarbar).render() 4219 elif isinstance(ob.scalarbar, vedo.Assembly): 4220 self.remove(ob.scalarbar) 4221 ob.add_scalarbar3d(title=ob._cmap_name) 4222 self.add(ob.scalarbar) 4223 4224 vedo.printc( 4225 f"Name:'{ob.name}'," if ob.name else "", 4226 f"range:{utils.precision(ob.mapper.GetScalarRange(),3)},", 4227 f"colormap:'{ob._cmap_name}'", c="g", bold=False, 4228 ) 4229 4230 elif key == "5": # cycle pointdata array 4231 ob = self.clicked_object 4232 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4233 return 4234 4235 arrnames = ob.pointdata.keys() 4236 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4237 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4238 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4239 if len(arrnames) == 0: 4240 return 4241 ob.mapper.SetScalarVisibility(1) 4242 4243 if not ob._cmap_name: 4244 ob._cmap_name = "rainbow" 4245 4246 try: 4247 curr_name = ob.dataset.GetPointData().GetScalars().GetName() 4248 i = arrnames.index(curr_name) 4249 if "normals" in curr_name.lower(): 4250 return 4251 if iren.GetShiftKey(): 4252 i -= 1 4253 else: 4254 i += 1 4255 if i >= len(arrnames): 4256 i = 0 4257 if i < 0: 4258 i = len(arrnames) - 1 4259 except (ValueError, AttributeError): 4260 i = 0 4261 4262 ob.cmap(ob._cmap_name, arrnames[i], on="points") 4263 if ob.scalarbar: 4264 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4265 self.remove(ob.scalarbar) 4266 title = ob.scalarbar.GetTitle() 4267 ob.scalarbar = None 4268 ob.add_scalarbar(title=arrnames[i]) 4269 self.add(ob.scalarbar) 4270 elif isinstance(ob.scalarbar, vedo.Assembly): 4271 self.remove(ob.scalarbar) 4272 ob.scalarbar = None 4273 ob.add_scalarbar3d(title=arrnames[i]) 4274 self.add(ob.scalarbar) 4275 else: 4276 vedo.printc( 4277 f"Name:'{ob.name}'," if ob.name else "", 4278 f"active pointdata array: '{arrnames[i]}'", 4279 c="g", bold=False, 4280 ) 4281 4282 elif key == "6": # cycle celldata array 4283 ob = self.clicked_object 4284 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4285 return 4286 4287 arrnames = ob.celldata.keys() 4288 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4289 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4290 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4291 if len(arrnames) == 0: 4292 return 4293 ob.mapper.SetScalarVisibility(1) 4294 4295 if not ob._cmap_name: 4296 ob._cmap_name = "rainbow" 4297 4298 try: 4299 curr_name = ob.dataset.GetCellData().GetScalars().GetName() 4300 i = arrnames.index(curr_name) 4301 if "normals" in curr_name.lower(): 4302 return 4303 if iren.GetShiftKey(): 4304 i -= 1 4305 else: 4306 i += 1 4307 if i >= len(arrnames): 4308 i = 0 4309 if i < 0: 4310 i = len(arrnames) - 1 4311 except (ValueError, AttributeError): 4312 i = 0 4313 4314 ob.cmap(ob._cmap_name, arrnames[i], on="cells") 4315 if ob.scalarbar: 4316 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4317 self.remove(ob.scalarbar) 4318 title = ob.scalarbar.GetTitle() 4319 ob.scalarbar = None 4320 ob.add_scalarbar(title=arrnames[i]) 4321 self.add(ob.scalarbar) 4322 elif isinstance(ob.scalarbar, vedo.Assembly): 4323 self.remove(ob.scalarbar) 4324 ob.scalarbar = None 4325 ob.add_scalarbar3d(title=arrnames[i]) 4326 self.add(ob.scalarbar) 4327 else: 4328 vedo.printc( 4329 f"Name:'{ob.name}'," if ob.name else "", 4330 f"active celldata array: '{arrnames[i]}'", 4331 c="g", bold=False, 4332 ) 4333 4334 elif key == "7": 4335 bgc = np.array(renderer.GetBackground()).sum() / 3 4336 if bgc <= 0: 4337 bgc = 0.223 4338 elif 0 < bgc < 1: 4339 bgc = 1 4340 else: 4341 bgc = 0 4342 renderer.SetBackground(bgc, bgc, bgc) 4343 4344 elif key == "8": 4345 bg2cols = [ 4346 "lightyellow", 4347 "darkseagreen", 4348 "palegreen", 4349 "steelblue", 4350 "lightblue", 4351 "cadetblue", 4352 "lavender", 4353 "white", 4354 "blackboard", 4355 "black", 4356 ] 4357 bg2name = vedo.get_color_name(renderer.GetBackground2()) 4358 if bg2name in bg2cols: 4359 idx = bg2cols.index(bg2name) 4360 else: 4361 idx = 4 4362 if idx is not None: 4363 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 4364 if not bg2name_next: 4365 renderer.GradientBackgroundOff() 4366 else: 4367 renderer.GradientBackgroundOn() 4368 renderer.SetBackground2(vedo.get_color(bg2name_next)) 4369 4370 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 4371 i = self.renderers.index(renderer) 4372 try: 4373 self.axes_instances[i].EnabledOff() 4374 self.axes_instances[i].SetInteractor(None) 4375 except AttributeError: 4376 # print("Cannot remove widget", [self.axes_instances[i]]) 4377 try: 4378 self.remove(self.axes_instances[i]) 4379 except: 4380 print("Cannot remove axes", [self.axes_instances[i]]) 4381 return 4382 self.axes_instances[i] = None 4383 4384 if not self.axes: 4385 self.axes = 0 4386 if isinstance(self.axes, dict): 4387 self.axes = 1 4388 4389 if key in ["minus", "KP_Subtract"]: 4390 if not self.camera.GetParallelProjection() and self.axes == 0: 4391 self.axes -= 1 # jump ruler doesnt make sense in perspective mode 4392 bns = self.renderer.ComputeVisiblePropBounds() 4393 addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None, bounds=bns) 4394 else: 4395 if not self.camera.GetParallelProjection() and self.axes == 12: 4396 self.axes += 1 # jump ruler doesnt make sense in perspective mode 4397 bns = self.renderer.ComputeVisiblePropBounds() 4398 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns) 4399 self.render() 4400 4401 elif "KP_" in key or key in [ 4402 "Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior" 4403 ]: 4404 asso = { # change axes style 4405 "KP_Insert": 0, "KP_0": 0, "Insert": 0, 4406 "KP_End": 1, "KP_1": 1, "End": 1, 4407 "KP_Down": 2, "KP_2": 2, "Down": 2, 4408 "KP_Next": 3, "KP_3": 3, "Next": 3, 4409 "KP_Left": 4, "KP_4": 4, "Left": 4, 4410 "KP_Begin": 5, "KP_5": 5, "Begin": 5, 4411 "KP_Right": 6, "KP_6": 6, "Right": 6, 4412 "KP_Home": 7, "KP_7": 7, "Home": 7, 4413 "KP_Up": 8, "KP_8": 8, "Up": 8, 4414 "Prior": 9, # on windows OS 4415 } 4416 clickedr = self.renderers.index(renderer) 4417 if key in asso: 4418 if self.axes_instances[clickedr]: 4419 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 4420 self.axes_instances[clickedr].EnabledOff() 4421 else: 4422 try: 4423 renderer.RemoveActor(self.axes_instances[clickedr]) 4424 except: 4425 pass 4426 self.axes_instances[clickedr] = None 4427 bounds = renderer.ComputeVisiblePropBounds() 4428 addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds) 4429 self.interactor.Render() 4430 4431 if key == "O": 4432 renderer.RemoveLight(self._extralight) 4433 self._extralight = None 4434 4435 elif key == "o": 4436 vbb, sizes, _, _ = addons.compute_visible_bounds() 4437 cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2) 4438 if not self._extralight: 4439 vup = renderer.GetActiveCamera().GetViewUp() 4440 pos = cm + utils.vector(vup) * utils.mag(sizes) 4441 self._extralight = addons.Light(pos, focal_point=cm, intensity=0.4) 4442 renderer.AddLight(self._extralight) 4443 vedo.printc("Press 'o' again to rotate light source, or 'O' to remove it.", c='y') 4444 else: 4445 cpos = utils.vector(self._extralight.GetPosition()) 4446 x, y, z = self._extralight.GetPosition() - cm 4447 r, th, ph = transformations.cart2spher(x, y, z) 4448 th += 0.2 4449 if th > np.pi: 4450 th = np.random.random() * np.pi / 2 4451 ph += 0.3 4452 cpos = transformations.spher2cart(r, th, ph).T + cm 4453 self._extralight.SetPosition(cpos) 4454 4455 elif key == "l": 4456 if self.clicked_object in self.get_meshes(): 4457 objs = [self.clicked_object] 4458 else: 4459 objs = self.get_meshes() 4460 for ia in objs: 4461 try: 4462 ev = ia.properties.GetEdgeVisibility() 4463 ia.properties.SetEdgeVisibility(not ev) 4464 ia.properties.SetRepresentationToSurface() 4465 ia.properties.SetLineWidth(0.1) 4466 except AttributeError: 4467 pass 4468 4469 elif key == "k": # lightings 4470 if self.clicked_object in self.get_meshes(): 4471 objs = [self.clicked_object] 4472 else: 4473 objs = self.get_meshes() 4474 shds = ("default", "metallic", "plastic", "shiny", "glossy", "off") 4475 for ia in objs: 4476 try: 4477 lnr = (ia._ligthingnr + 1) % 6 4478 ia.lighting(shds[lnr]) 4479 ia._ligthingnr = lnr 4480 except AttributeError: 4481 pass 4482 4483 elif key == "K": # shading 4484 if self.clicked_object in self.get_meshes(): 4485 objs = [self.clicked_object] 4486 else: 4487 objs = self.get_meshes() 4488 for ia in objs: 4489 if isinstance(ia, vedo.Mesh): 4490 ia.compute_normals(cells=False) 4491 intrp = ia.properties.GetInterpolation() 4492 if intrp > 0: 4493 ia.properties.SetInterpolation(0) # flat 4494 else: 4495 ia.properties.SetInterpolation(2) # phong 4496 4497 elif key == "n": # show normals to an actor 4498 self.remove("added_auto_normals") 4499 if self.clicked_object in self.get_meshes(): 4500 if self.clicked_actor.GetPickable(): 4501 norml = vedo.shapes.NormalLines(self.clicked_object) 4502 norml.name = "added_auto_normals" 4503 self.add(norml) 4504 4505 elif key == "x": 4506 if self.justremoved is None: 4507 if self.clicked_object in self.get_meshes() or isinstance( 4508 self.clicked_object, vtki.vtkAssembly 4509 ): 4510 self.justremoved = self.clicked_actor 4511 self.renderer.RemoveActor(self.clicked_actor) 4512 else: 4513 self.renderer.AddActor(self.justremoved) 4514 self.justremoved = None 4515 4516 elif key == "X": 4517 if self.clicked_object: 4518 if not self.cutter_widget: 4519 self.cutter_widget = addons.BoxCutter(self.clicked_object) 4520 self.add(self.cutter_widget) 4521 vedo.printc("Press i to toggle the cutter on/off", c='g', dim=1) 4522 vedo.printc(" u to flip selection", c='g', dim=1) 4523 vedo.printc(" r to reset cutting planes", c='g', dim=1) 4524 vedo.printc(" Shift+X to close the cutter box widget", c='g', dim=1) 4525 vedo.printc(" Ctrl+S to save the cut section to file.", c='g', dim=1) 4526 else: 4527 self.remove(self.cutter_widget) 4528 self.cutter_widget = None 4529 vedo.printc("Click object and press X to open the cutter box widget.", c='g') 4530 4531 elif key == "E": 4532 vedo.printc(r":camera: Exporting 3D window to file scene.npz", c="b", end="") 4533 vedo.file_io.export_window("scene.npz") 4534 vedo.printc(", try:\n> vedo scene.npz # (this is experimental!)", c="b") 4535 return 4536 4537 elif key == "F": 4538 vedo.file_io.export_window("scene.x3d") 4539 vedo.printc(r":camera: Exporting 3D window to file", c="b", end="") 4540 vedo.file_io.export_window("scene.npz") 4541 vedo.printc(". Try:\n> firefox scene.html", c="b") 4542 4543 # elif key == "G": # not working with last version of k3d 4544 # vedo.file_io.export_window("scene.html") 4545 # vedo.printc(r":camera: Exporting K3D window to file", c="b", end="") 4546 # vedo.file_io.export_window("scene.html") 4547 # vedo.printc(". Try:\n> firefox scene.html", c="b") 4548 4549 elif key == "i": # print info 4550 if self.clicked_object: 4551 print(self.clicked_object) 4552 else: 4553 print(self) 4554 4555 elif key == "I": # print color under the mouse 4556 x, y = iren.GetEventPosition() 4557 self.color_picker([x, y], verbose=True) 4558 4559 elif key == "Y": 4560 if self.clicked_object and self.clicked_object.pipeline: 4561 self.clicked_object.pipeline.show() 4562 4563 if iren: 4564 iren.Render()
Main class to manage objects.
378 def __init__( 379 self, 380 shape=(1, 1), 381 N=None, 382 pos=(0, 0), 383 size="auto", 384 screensize="auto", 385 title="vedo", 386 bg="white", 387 bg2=None, 388 axes=None, 389 sharecam=True, 390 resetcam=True, 391 interactive=None, 392 offscreen=False, 393 qt_widget=None, 394 wx_widget=None, 395 ): 396 """ 397 Arguments: 398 shape : (str, list) 399 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 400 N : (int) 401 number of desired renderers arranged in a grid automatically. 402 pos : (list) 403 (x,y) position in pixels of top-left corner of the rendering window on the screen 404 size : (str, list) 405 size of the rendering window. If 'auto', guess it based on screensize. 406 screensize : (list) 407 physical size of the monitor screen in pixels 408 bg : (color, str) 409 background color or specify jpg image file name with path 410 bg2 : (color) 411 background color of a gradient towards the top 412 title : (str) 413 window title 414 415 axes : (int) 416 417 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 418 Check out `vedo.addons.Axes()` for the available options. 419 420 - 0, no axes 421 - 1, draw three gray grid walls 422 - 2, show cartesian axes from (0,0,0) 423 - 3, show positive range of cartesian axes from (0,0,0) 424 - 4, show a triad at bottom left 425 - 5, show a cube at bottom left 426 - 6, mark the corners of the bounding box 427 - 7, draw a 3D ruler at each side of the cartesian axes 428 - 8, show the VTK CubeAxesActor object 429 - 9, show the bounding box outLine 430 - 10, show three circles representing the maximum bounding box 431 - 11, show a large grid on the x-y plane (use with zoom=8) 432 - 12, show polar axes 433 - 13, draw a simple ruler at the bottom of the window 434 - 14: draw a camera orientation widget 435 436 sharecam : (bool) 437 if False each renderer will have an independent camera 438 interactive : (bool) 439 if True will stop after show() to allow interaction with the 3d scene 440 offscreen : (bool) 441 if True will not show the rendering window 442 qt_widget : (QVTKRenderWindowInteractor) 443 render in a Qt-Widget using an QVTKRenderWindowInteractor. 444 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 445 """ 446 vedo.plotter_instance = self 447 448 if interactive is None: 449 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 450 self._interactive = interactive 451 # print("interactive", interactive, N, shape) 452 453 self.objects = [] # list of objects to be shown 454 self.clicked_object = None # holds the object that has been clicked 455 self.clicked_actor = None # holds the actor that has been clicked 456 457 self.shape = shape # nr. of subwindows in grid 458 self.axes = axes # show axes type nr. 459 self.title = title # window title 460 self.size = size # window size 461 self.backgrcol = bg # used also by backend notebooks 462 463 self.offscreen= offscreen 464 self.resetcam = resetcam 465 self.sharecam = sharecam # share the same camera if multiple renderers 466 self.pos = pos # used by vedo.file_io 467 468 self.picker = None # hold the vtkPicker object 469 self.picked2d = None # 2d coords of a clicked point on the rendering window 470 self.picked3d = None # 3d coords of a clicked point on an actor 471 472 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 473 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 474 self.interactor = None 475 self.window = None 476 self.renderer = None 477 self.renderers = [] # list of renderers 478 479 # mostly internal stuff: 480 self.hover_legends = [] 481 self.justremoved = None 482 self.axes_instances = [] 483 self.clock = 0 484 self.sliders = [] 485 self.buttons = [] 486 self.widgets = [] 487 self.cutter_widget = None 488 self.hint_widget = None 489 self.background_renderer = None 490 self.last_event = None 491 self.skybox = None 492 self._icol = 0 493 self._clockt0 = time.time() 494 self._extralight = None 495 self._cocoa_initialized = False 496 self._cocoa_process_events = True # make one call in show() 497 self._must_close_now = False 498 499 ##################################################################### 500 if vedo.settings.default_backend == "2d": 501 self.offscreen = True 502 if self.size == "auto": 503 self.size = (800, 600) 504 505 elif vedo.settings.default_backend == "k3d": 506 if self.size == "auto": 507 self.size = (1000, 1000) 508 #################################### 509 return ############################ 510 #################################### 511 512 ############################################################# 513 if vedo.settings.default_backend in ["vtk", "2d", "trame"]: 514 515 if screensize == "auto": 516 screensize = (2160, 1440) # TODO: get actual screen size 517 518 # build the rendering window: 519 self.window = vtki.vtkRenderWindow() 520 521 self.window.GlobalWarningDisplayOff() 522 523 if self.title == "vedo": # check if dev version 524 if "dev" in vedo.__version__: 525 self.title = f"vedo ({vedo.__version__})" 526 self.window.SetWindowName(self.title) 527 528 # more vedo.settings 529 if vedo.settings.use_depth_peeling: 530 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 531 self.window.SetMultiSamples(vedo.settings.multi_samples) 532 533 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 534 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 535 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 536 537 ############################################################# 538 if N: # N = number of renderers. Find out the best 539 540 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 541 vedo.logger.warning("having set N, shape is ignored.") 542 543 x, y = screensize 544 nx = int(np.sqrt(int(N * y / x) + 1)) 545 ny = int(np.sqrt(int(N * x / y) + 1)) 546 lm = [ 547 (nx, ny), 548 (nx, ny + 1), 549 (nx - 1, ny), 550 (nx + 1, ny), 551 (nx, ny - 1), 552 (nx - 1, ny + 1), 553 (nx + 1, ny - 1), 554 (nx + 1, ny + 1), 555 (nx - 1, ny - 1), 556 ] 557 ind, minl = 0, 1000 558 for i, m in enumerate(lm): 559 l = m[0] * m[1] 560 if N <= l < minl: 561 ind = i 562 minl = l 563 shape = lm[ind] 564 565 ################################################## 566 if isinstance(shape, str): 567 568 if "|" in shape: 569 if self.size == "auto": 570 self.size = (800, 1200) 571 n = int(shape.split("|")[0]) 572 m = int(shape.split("|")[1]) 573 rangen = reversed(range(n)) 574 rangem = reversed(range(m)) 575 else: 576 if self.size == "auto": 577 self.size = (1200, 800) 578 m = int(shape.split("/")[0]) 579 n = int(shape.split("/")[1]) 580 rangen = range(n) 581 rangem = range(m) 582 583 if n >= m: 584 xsplit = m / (n + m) 585 else: 586 xsplit = 1 - n / (n + m) 587 if vedo.settings.window_splitting_position: 588 xsplit = vedo.settings.window_splitting_position 589 590 for i in rangen: 591 arenderer = vtki.vtkRenderer() 592 if "|" in shape: 593 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 594 else: 595 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 596 self.renderers.append(arenderer) 597 598 for i in rangem: 599 arenderer = vtki.vtkRenderer() 600 601 if "|" in shape: 602 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 603 else: 604 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 605 self.renderers.append(arenderer) 606 607 for r in self.renderers: 608 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 609 610 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 611 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 612 if vedo.settings.use_depth_peeling: 613 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 614 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 615 r.SetUseFXAA(vedo.settings.use_fxaa) 616 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 617 618 r.SetBackground(vedo.get_color(self.backgrcol)) 619 620 self.axes_instances.append(None) 621 622 self.shape = (n + m,) 623 624 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 625 # passing a sequence of dicts for renderers specifications 626 627 if self.size == "auto": 628 self.size = (1000, 800) 629 630 for rd in shape: 631 x0, y0 = rd["bottomleft"] 632 x1, y1 = rd["topright"] 633 bg_ = rd.pop("bg", "white") 634 bg2_ = rd.pop("bg2", None) 635 636 arenderer = vtki.vtkRenderer() 637 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 638 639 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 640 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 641 if vedo.settings.use_depth_peeling: 642 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 643 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 644 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 645 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 646 647 arenderer.SetViewport(x0, y0, x1, y1) 648 arenderer.SetBackground(vedo.get_color(bg_)) 649 if bg2_: 650 arenderer.GradientBackgroundOn() 651 arenderer.SetBackground2(vedo.get_color(bg2_)) 652 653 self.renderers.append(arenderer) 654 self.axes_instances.append(None) 655 656 self.shape = (len(shape),) 657 658 else: 659 660 if isinstance(self.size, str) and self.size == "auto": 661 # figure out a reasonable window size 662 f = 1.5 663 x, y = screensize 664 xs = y / f * shape[1] # because y<x 665 ys = y / f * shape[0] 666 if xs > x / f: # shrink 667 xs = x / f 668 ys = xs / shape[1] * shape[0] 669 if ys > y / f: 670 ys = y / f 671 xs = ys / shape[0] * shape[1] 672 self.size = (int(xs), int(ys)) 673 if shape == (1, 1): 674 self.size = (int(y / f), int(y / f)) # because y<x 675 else: 676 self.size = (self.size[0], self.size[1]) 677 678 try: 679 image_actor = None 680 bgname = str(self.backgrcol).lower() 681 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 682 self.window.SetNumberOfLayers(2) 683 self.background_renderer = vtki.vtkRenderer() 684 self.background_renderer.SetLayer(0) 685 self.background_renderer.InteractiveOff() 686 self.background_renderer.SetBackground(vedo.get_color(bg2)) 687 image_actor = vedo.Image(self.backgrcol).actor 688 self.window.AddRenderer(self.background_renderer) 689 self.background_renderer.AddActor(image_actor) 690 except AttributeError: 691 pass 692 693 for i in reversed(range(shape[0])): 694 for j in range(shape[1]): 695 arenderer = vtki.vtkRenderer() 696 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 697 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 698 699 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 700 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 701 if vedo.settings.use_depth_peeling: 702 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 703 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 704 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 705 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 706 707 if image_actor: 708 arenderer.SetLayer(1) 709 710 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 711 if bg2: 712 arenderer.GradientBackgroundOn() 713 arenderer.SetBackground2(vedo.get_color(bg2)) 714 715 x0 = i / shape[0] 716 y0 = j / shape[1] 717 x1 = (i + 1) / shape[0] 718 y1 = (j + 1) / shape[1] 719 arenderer.SetViewport(y0, x0, y1, x1) 720 self.renderers.append(arenderer) 721 self.axes_instances.append(None) 722 self.shape = shape 723 724 if self.renderers: 725 self.renderer = self.renderers[0] 726 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 727 728 ######################################################### 729 if self.qt_widget or self.wx_widget: 730 if self.qt_widget: 731 self.window = self.qt_widget.GetRenderWindow() # overwrite 732 else: 733 self.window = self.wx_widget.GetRenderWindow() 734 self.interactor = self.window.GetInteractor() 735 736 ######################################################### 737 for r in self.renderers: 738 self.window.AddRenderer(r) 739 # set the background gradient if any 740 if vedo.settings.background_gradient_orientation > 0: 741 try: 742 modes = [ 743 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 744 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 745 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 746 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 747 ] 748 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 749 r.GradientBackgroundOn() 750 except AttributeError: 751 pass 752 753 ######################################################### 754 if self.qt_widget or self.wx_widget: 755 # self.window.SetSize(int(self.size[0]), int(self.size[1])) 756 self.interactor.SetRenderWindow(self.window) 757 # vsty = vtki.new("InteractorStyleTrackballCamera") 758 # self.interactor.SetInteractorStyle(vsty) 759 if vedo.settings.enable_default_keyboard_callbacks: 760 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 761 if vedo.settings.enable_default_mouse_callbacks: 762 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 763 return ################ 764 ######################## 765 766 if self.size[0] == "f": # full screen 767 self.size = "fullscreen" 768 self.window.SetFullScreen(True) 769 self.window.BordersOn() 770 else: 771 self.window.SetSize(int(self.size[0]), int(self.size[1])) 772 773 if self.offscreen: 774 if self.axes in (4, 5, 8, 12, 14): 775 self.axes = 0 # does not work with those 776 self.window.SetOffScreenRendering(True) 777 self.interactor = None 778 self._interactive = False 779 return ################ 780 ######################## 781 782 self.window.SetPosition(pos) 783 784 ######################################################### 785 self.interactor = vtki.vtkRenderWindowInteractor() 786 787 self.interactor.SetRenderWindow(self.window) 788 vsty = vtki.new("InteractorStyleTrackballCamera") 789 self.interactor.SetInteractorStyle(vsty) 790 self.interactor.RemoveObservers("CharEvent") 791 792 if vedo.settings.enable_default_keyboard_callbacks: 793 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 794 if vedo.settings.enable_default_mouse_callbacks: 795 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick)
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
- title : (str) window title
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
- 14: draw a camera orientation widget
- sharecam : (bool) if False each renderer will have an independent camera
- interactive : (bool) if True will stop after show() to allow interaction with the 3d scene
- offscreen : (bool) if True will not show the rendering window
- qt_widget : (QVTKRenderWindowInteractor)
render in a Qt-Widget using an QVTKRenderWindowInteractor.
See examples
qt_windows[1,2,3].py
andqt_cutter.py
.
866 def print(self): 867 """Print information about the current instance.""" 868 print(self.__str__()) 869 return self
Print information about the current instance.
887 def initialize_interactor(self) -> "Plotter": 888 """Initialize the interactor if not already initialized.""" 889 if self.offscreen: 890 return self 891 if self.interactor: 892 if not self.interactor.GetInitialized(): 893 self.interactor.Initialize() 894 self.interactor.RemoveObservers("CharEvent") 895 return self
Initialize the interactor if not already initialized.
897 def process_events(self) -> "Plotter": 898 """Process all pending events.""" 899 self.initialize_interactor() 900 if self.interactor: 901 try: 902 self.interactor.ProcessEvents() 903 except AttributeError: 904 pass 905 return self
Process all pending events.
907 def at(self, nren: int, yren=None) -> "Plotter": 908 """ 909 Select the current renderer number as an int. 910 Can also use the `[nx, ny]` format. 911 """ 912 if utils.is_sequence(nren): 913 if len(nren) == 2: 914 nren, yren = nren 915 else: 916 vedo.logger.error("at() argument must be a single number or a list of two numbers") 917 raise RuntimeError 918 919 if yren is not None: 920 a, b = self.shape 921 x, y = nren, yren 922 nren = x * b + y 923 # print("at (", x, y, ") -> ren", nren) 924 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 925 vedo.logger.error(f"at({nren, yren}) is malformed!") 926 raise RuntimeError 927 928 self.renderer = self.renderers[nren] 929 return self
Select the current renderer number as an int.
Can also use the [nx, ny]
format.
931 def add(self, *objs, at=None) -> "Plotter": 932 """ 933 Append the input objects to the internal list of objects to be shown. 934 935 Arguments: 936 at : (int) 937 add the object at the specified renderer 938 """ 939 if at is not None: 940 ren = self.renderers[at] 941 else: 942 ren = self.renderer 943 944 objs = utils.flatten(objs) 945 for ob in objs: 946 if ob and ob not in self.objects: 947 self.objects.append(ob) 948 949 acts = self._scan_input_return_acts(objs) 950 951 for a in acts: 952 953 if ren: 954 955 if isinstance(a, vedo.addons.BaseCutter): 956 a.add_to(self) # from cutters 957 continue 958 959 try: 960 ren.AddActor(a) 961 except TypeError: 962 ren.AddActor(a.actor) 963 964 if hasattr(a, "rendered_at"): 965 ir = self.renderers.index(ren) 966 a.rendered_at.add(ir) 967 if isinstance(a, vtki.vtkFollower): 968 a.SetCamera(self.camera) 969 if isinstance(a, vedo.visual.LightKit): 970 a.lightkit.AddLightsToRenderer(ren) 971 972 return self
Append the input objects to the internal list of objects to be shown.
Arguments:
- at : (int) add the object at the specified renderer
974 def remove(self, *objs, at=None) -> "Plotter": 975 """ 976 Remove input object to the internal list of objects to be shown. 977 978 Objects to be removed can be referenced by their assigned name, 979 980 Arguments: 981 at : (int) 982 remove the object at the specified renderer 983 """ 984 # TODO and you can also use wildcards like `*` and `?`. 985 if at is not None: 986 ren = self.renderers[at] 987 else: 988 ren = self.renderer 989 990 objs = [ob for ob in utils.flatten(objs) if ob] 991 992 has_str = False 993 for ob in objs: 994 if isinstance(ob, str): 995 has_str = True 996 break 997 998 has_actor = False 999 for ob in objs: 1000 if hasattr(ob, "actor") and ob.actor: 1001 has_actor = True 1002 break 1003 1004 if has_str or has_actor: 1005 # need to get the actors to search for 1006 for a in self.get_actors(include_non_pickables=True): 1007 # print("PARSING", [a]) 1008 try: 1009 if (a.name and a.name in objs) or a in objs: 1010 objs.append(a) 1011 # if a.name: 1012 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1013 # if any(bools) or a in objs: 1014 # objs.append(a) 1015 # print('a.name',a.name, objs,any(bools)) 1016 except AttributeError: # no .name 1017 # passing the actor so get back the object with .retrieve_object() 1018 try: 1019 vobj = a.retrieve_object() 1020 if (vobj.name and vobj.name in objs) or vobj in objs: 1021 # print('vobj.name', vobj.name) 1022 objs.append(vobj) 1023 except AttributeError: 1024 pass 1025 1026 ir = self.renderers.index(ren) 1027 1028 ids = [] 1029 for ob in set(objs): 1030 1031 # will remove it from internal list if possible 1032 try: 1033 idx = self.objects.index(ob) 1034 ids.append(idx) 1035 except ValueError: 1036 pass 1037 1038 if ren: ### remove it from the renderer 1039 1040 if isinstance(ob, vedo.addons.BaseCutter): 1041 ob.remove_from(self) # from cutters 1042 continue 1043 1044 try: 1045 ren.RemoveActor(ob) 1046 except TypeError: 1047 try: 1048 ren.RemoveActor(ob.actor) 1049 except AttributeError: 1050 pass 1051 1052 if hasattr(ob, "rendered_at"): 1053 ob.rendered_at.discard(ir) 1054 1055 if hasattr(ob, "scalarbar") and ob.scalarbar: 1056 ren.RemoveActor(ob.scalarbar) 1057 if hasattr(ob, "_caption") and ob._caption: 1058 ren.RemoveActor(ob._caption) 1059 if hasattr(ob, "shadows") and ob.shadows: 1060 for sha in ob.shadows: 1061 ren.RemoveActor(sha.actor) 1062 if hasattr(ob, "trail") and ob.trail: 1063 ren.RemoveActor(ob.trail.actor) 1064 ob.trail_points = [] 1065 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1066 for sha in ob.trail.shadows: 1067 ren.RemoveActor(sha.actor) 1068 1069 elif isinstance(ob, vedo.visual.LightKit): 1070 ob.lightkit.RemoveLightsFromRenderer(ren) 1071 1072 # for i in ids: # WRONG way of doing it! 1073 # del self.objects[i] 1074 # instead we do: 1075 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1076 return self
Remove input object to the internal list of objects to be shown.
Objects to be removed can be referenced by their assigned name,
Arguments:
- at : (int) remove the object at the specified renderer
1078 @property 1079 def actors(self): 1080 """Return the list of actors.""" 1081 return [ob.actor for ob in self.objects if hasattr(ob, "actor")]
Return the list of actors.
1083 def remove_lights(self) -> "Plotter": 1084 """Remove all the present lights in the current renderer.""" 1085 if self.renderer: 1086 self.renderer.RemoveAllLights() 1087 return self
Remove all the present lights in the current renderer.
1089 def pop(self, at=None) -> "Plotter": 1090 """ 1091 Remove the last added object from the rendering window. 1092 This method is typically used in loops or callback functions. 1093 """ 1094 if at is not None and not isinstance(at, int): 1095 # wrong usage pitfall 1096 vedo.logger.error("argument of pop() must be an integer") 1097 raise RuntimeError() 1098 1099 if self.objects: 1100 self.remove(self.objects[-1], at) 1101 return self
Remove the last added object from the rendering window. This method is typically used in loops or callback functions.
1103 def render(self, resetcam=False) -> "Plotter": 1104 """Render the scene. This method is typically used in loops or callback functions.""" 1105 1106 if vedo.settings.dry_run_mode >= 2: 1107 return self 1108 1109 if not self.window: 1110 return self 1111 1112 self.initialize_interactor() 1113 1114 if resetcam: 1115 self.renderer.ResetCamera() 1116 1117 self.window.Render() 1118 1119 if self._cocoa_process_events and self.interactor.GetInitialized(): 1120 if "Darwin" in vedo.sys_platform and not self.offscreen: 1121 self.interactor.ProcessEvents() 1122 self._cocoa_process_events = False 1123 return self
Render the scene. This method is typically used in loops or callback functions.
1125 def interactive(self) -> "Plotter": 1126 """ 1127 Start window interaction. 1128 Analogous to `show(..., interactive=True)`. 1129 """ 1130 if vedo.settings.dry_run_mode >= 1: 1131 return self 1132 self.initialize_interactor() 1133 if self.interactor: 1134 # print("self.interactor.Start()") 1135 self.interactor.Start() 1136 # print("self.interactor.Start() done") 1137 if self._must_close_now: 1138 # print("self.interactor.TerminateApp()") 1139 self.interactor.GetRenderWindow().Finalize() 1140 self.interactor.TerminateApp() 1141 self.interactor = None 1142 self.window = None 1143 self.renderer = None 1144 self.renderers = [] 1145 self.camera = None 1146 return self
Start window interaction.
Analogous to show(..., interactive=True)
.
1148 def use_depth_peeling(self, at=None, value=True) -> "Plotter": 1149 """ 1150 Specify whether use depth peeling algorithm at this specific renderer 1151 Call this method before the first rendering. 1152 """ 1153 if at is None: 1154 ren = self.renderer 1155 else: 1156 ren = self.renderers[at] 1157 ren.SetUseDepthPeeling(value) 1158 return self
Specify whether use depth peeling algorithm at this specific renderer Call this method before the first rendering.
1160 def background(self, c1=None, c2=None, at=None, mode=0) -> Union["Plotter", "np.ndarray"]: 1161 """Set the color of the background for the current renderer. 1162 A different renderer index can be specified by keyword `at`. 1163 1164 Arguments: 1165 c1 : (list) 1166 background main color. 1167 c2 : (list) 1168 background color for the upper part of the window. 1169 at : (int) 1170 renderer index. 1171 mode : (int) 1172 background mode (needs vtk version >= 9.3) 1173 0 = vertical, 1174 1 = horizontal, 1175 2 = radial farthest side, 1176 3 = radia farthest corner. 1177 """ 1178 if not self.renderers: 1179 return self 1180 if at is None: 1181 r = self.renderer 1182 else: 1183 r = self.renderers[at] 1184 1185 if c1 is None and c2 is None: 1186 return np.array(r.GetBackground()) 1187 1188 if r: 1189 if c1 is not None: 1190 r.SetBackground(vedo.get_color(c1)) 1191 if c2 is not None: 1192 r.GradientBackgroundOn() 1193 r.SetBackground2(vedo.get_color(c2)) 1194 if mode: 1195 try: # only works with vtk>=9.3 1196 modes = [ 1197 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1198 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1199 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1200 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1201 ] 1202 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 1203 except AttributeError: 1204 pass 1205 1206 else: 1207 r.GradientBackgroundOff() 1208 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.
- mode : (int) background mode (needs vtk version >= 9.3) 0 = vertical, 1 = horizontal, 2 = radial farthest side, 3 = radia farthest corner.
1211 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1212 """ 1213 Return a list of Meshes from the specified renderer. 1214 1215 Arguments: 1216 at : (int) 1217 specify which renderer to look at. 1218 include_non_pickables : (bool) 1219 include non-pickable objects 1220 unpack_assemblies : (bool) 1221 unpack assemblies into their components 1222 """ 1223 if at is None: 1224 renderer = self.renderer 1225 at = self.renderers.index(renderer) 1226 elif isinstance(at, int): 1227 renderer = self.renderers[at] 1228 1229 has_global_axes = False 1230 if isinstance(self.axes_instances[at], vedo.Assembly): 1231 has_global_axes = True 1232 1233 if unpack_assemblies: 1234 acs = renderer.GetActors() 1235 else: 1236 acs = renderer.GetViewProps() 1237 1238 objs = [] 1239 acs.InitTraversal() 1240 for _ in range(acs.GetNumberOfItems()): 1241 1242 if unpack_assemblies: 1243 a = acs.GetNextItem() 1244 else: 1245 a = acs.GetNextProp() 1246 1247 if isinstance(a, vtki.vtkVolume): 1248 continue 1249 1250 if include_non_pickables or a.GetPickable(): 1251 if a == self.axes_instances[at]: 1252 continue 1253 if has_global_axes and a in self.axes_instances[at].actors: 1254 continue 1255 try: 1256 objs.append(a.retrieve_object()) 1257 except AttributeError: 1258 pass 1259 return objs
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
1261 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1262 """ 1263 Return a list of Volumes from the specified renderer. 1264 1265 Arguments: 1266 at : (int) 1267 specify which renderer to look at 1268 include_non_pickables : (bool) 1269 include non-pickable objects 1270 """ 1271 if at is None: 1272 renderer = self.renderer 1273 at = self.renderers.index(renderer) 1274 elif isinstance(at, int): 1275 renderer = self.renderers[at] 1276 1277 vols = [] 1278 acs = renderer.GetVolumes() 1279 acs.InitTraversal() 1280 for _ in range(acs.GetNumberOfItems()): 1281 a = acs.GetNextItem() 1282 if include_non_pickables or a.GetPickable(): 1283 try: 1284 vols.append(a.retrieve_object()) 1285 except AttributeError: 1286 pass 1287 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
1289 def get_actors(self, at=None, include_non_pickables=False) -> list: 1290 """ 1291 Return a list of Volumes from the specified renderer. 1292 1293 Arguments: 1294 at : (int) 1295 specify which renderer to look at 1296 include_non_pickables : (bool) 1297 include non-pickable objects 1298 """ 1299 if at is None: 1300 renderer = self.renderer 1301 at = self.renderers.index(renderer) 1302 elif isinstance(at, int): 1303 renderer = self.renderers[at] 1304 1305 acts = [] 1306 acs = renderer.GetViewProps() 1307 acs.InitTraversal() 1308 for _ in range(acs.GetNumberOfItems()): 1309 a = acs.GetNextProp() 1310 if include_non_pickables or a.GetPickable(): 1311 acts.append(a) 1312 return acts
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
1314 def check_actors_trasform(self, at=None) -> "Plotter": 1315 """ 1316 Reset the transformation matrix of all actors at specified renderer. 1317 This is only useful when actors have been moved/rotated/scaled manually 1318 in an already rendered scene using interactors like 1319 'TrackballActor' or 'JoystickActor'. 1320 """ 1321 # see issue https://github.com/marcomusy/vedo/issues/1046 1322 for a in self.get_actors(at=at, include_non_pickables=True): 1323 try: 1324 M = a.GetMatrix() 1325 except AttributeError: 1326 continue 1327 if M and not M.IsIdentity(): 1328 try: 1329 a.retrieve_object().apply_transform_from_actor() 1330 # vedo.logger.info( 1331 # f"object '{a.retrieve_object().name}' " 1332 # "was manually moved. Updated to its current position." 1333 # ) 1334 except AttributeError: 1335 pass 1336 return self
Reset the transformation matrix of all actors at specified renderer. This is only useful when actors have been moved/rotated/scaled manually in an already rendered scene using interactors like 'TrackballActor' or 'JoystickActor'.
1338 def reset_camera(self, tight=None) -> "Plotter": 1339 """ 1340 Reset the camera position and zooming. 1341 If tight (float) is specified the zooming reserves a padding space 1342 in the xy-plane expressed in percent of the average size. 1343 """ 1344 if tight is None: 1345 self.renderer.ResetCamera() 1346 else: 1347 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1348 1349 cam = self.renderer.GetActiveCamera() 1350 1351 self.renderer.ComputeAspect() 1352 aspect = self.renderer.GetAspect() 1353 angle = np.pi * cam.GetViewAngle() / 180.0 1354 dx, dy = (x1 - x0) * 0.999, (y1 - y0) * 0.999 1355 dist = max(dx / aspect[0], dy) / np.sin(angle / 2) / 2 1356 1357 cam.SetViewUp(0, 1, 0) 1358 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1359 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1360 if cam.GetParallelProjection(): 1361 ps = max(dx / aspect[0], dy) / 2 1362 cam.SetParallelScale(ps * (1 + tight)) 1363 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1364 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.
1366 def reset_viewup(self, smooth=True) -> "Plotter": 1367 """ 1368 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1369 """ 1370 vbb = addons.compute_visible_bounds()[0] 1371 x0, x1, y0, y1, z0, z1 = vbb 1372 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1373 d = self.camera.GetDistance() 1374 1375 viewups = np.array( 1376 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1377 ) 1378 positions = np.array( 1379 [ 1380 (mx, my, mz + d), 1381 (mx, my, mz - d), 1382 (mx, my + d, mz), 1383 (mx, my - d, mz), 1384 (mx + d, my, mz), 1385 (mx - d, my, mz), 1386 ] 1387 ) 1388 1389 vu = np.array(self.camera.GetViewUp()) 1390 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1391 1392 poc = np.array(self.camera.GetPosition()) 1393 foc = np.array(self.camera.GetFocalPoint()) 1394 a = poc - foc 1395 b = positions - foc 1396 a = a / np.linalg.norm(a) 1397 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1398 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1399 1400 if smooth: 1401 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1402 for t in outtimes: 1403 vv = vu * (1 - t) + viewups[vui] * t 1404 pp = poc * (1 - t) + positions[pui] * t 1405 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1406 self.camera.SetViewUp(vv) 1407 self.camera.SetPosition(pp) 1408 self.camera.SetFocalPoint(ff) 1409 self.render() 1410 1411 # interpolator does not respect parallel view...: 1412 # cam1 = dict( 1413 # pos=poc, 1414 # viewup=vu, 1415 # focal_point=(mx,my,mz), 1416 # clipping_range=self.camera.GetClippingRange() 1417 # ) 1418 # # cam1 = self.camera 1419 # cam2 = dict( 1420 # pos=positions[pui], 1421 # viewup=viewups[vui], 1422 # focal_point=(mx,my,mz), 1423 # clipping_range=self.camera.GetClippingRange() 1424 # ) 1425 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1426 # for c in vcams: 1427 # self.renderer.SetActiveCamera(c) 1428 # self.render() 1429 else: 1430 1431 self.camera.SetViewUp(viewups[vui]) 1432 self.camera.SetPosition(positions[pui]) 1433 self.camera.SetFocalPoint(mx, my, mz) 1434 1435 self.renderer.ResetCameraClippingRange() 1436 1437 # vbb, _, _, _ = addons.compute_visible_bounds() 1438 # x0,x1, y0,y1, z0,z1 = vbb 1439 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1440 self.render() 1441 return self
Reset the orientation of the camera to the closest orthogonal direction and view-up.
1443 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1444 """ 1445 Takes as input two cameras set camera at an interpolated position: 1446 1447 Cameras can be vtkCamera or dictionaries in format: 1448 1449 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1450 1451 Press `shift-C` key in interactive mode to dump a python snipplet 1452 of parameters for the current camera view. 1453 """ 1454 nc = len(cameras) 1455 if len(times) == 0: 1456 times = np.linspace(0, 1, num=nc, endpoint=True) 1457 1458 assert len(times) == nc 1459 1460 cin = vtki.new("CameraInterpolator") 1461 1462 # cin.SetInterpolationTypeToLinear() # buggy? 1463 if nc > 2 and smooth: 1464 cin.SetInterpolationTypeToSpline() 1465 1466 for i, cam in enumerate(cameras): 1467 vcam = cam 1468 if isinstance(cam, dict): 1469 vcam = utils.camera_from_dict(cam) 1470 cin.AddCamera(times[i], vcam) 1471 1472 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1473 rng = maxt - mint 1474 1475 if len(output_times) == 0: 1476 cin.InterpolateCamera(t * rng, self.camera) 1477 self.renderer.SetActiveCamera(self.camera) 1478 return [self.camera] 1479 else: 1480 vcams = [] 1481 for tt in output_times: 1482 c = vtki.vtkCamera() 1483 cin.InterpolateCamera(tt * rng, c) 1484 vcams.append(c) 1485 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.
1487 def fly_to(self, point) -> "Plotter": 1488 """ 1489 Fly camera to the specified point. 1490 1491 Arguments: 1492 point : (list) 1493 point in space to place camera. 1494 1495 Example: 1496 ```python 1497 from vedo import * 1498 cone = Cone() 1499 plt = Plotter(axes=1) 1500 plt.show(cone) 1501 plt.fly_to([1,0,0]) 1502 plt.interactive().close() 1503 ``` 1504 """ 1505 if self.interactor: 1506 self.resetcam = False 1507 self.interactor.FlyTo(self.renderer, point) 1508 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()
1510 def look_at(self, plane="xy") -> "Plotter": 1511 """Move the camera so that it looks at the specified cartesian plane""" 1512 cam = self.renderer.GetActiveCamera() 1513 fp = np.array(cam.GetFocalPoint()) 1514 p = np.array(cam.GetPosition()) 1515 dist = np.linalg.norm(fp - p) 1516 plane = plane.lower() 1517 if "x" in plane and "y" in plane: 1518 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1519 cam.SetViewUp(0.0, 1.0, 0.0) 1520 elif "x" in plane and "z" in plane: 1521 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1522 cam.SetViewUp(0.0, 0.0, 1.0) 1523 elif "y" in plane and "z" in plane: 1524 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1525 cam.SetViewUp(0.0, 0.0, 1.0) 1526 else: 1527 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1528 return self
Move the camera so that it looks at the specified cartesian plane
1530 def record(self, filename="") -> str: 1531 """ 1532 Record camera, mouse, keystrokes and all other events. 1533 Recording can be toggled on/off by pressing key "R". 1534 1535 Arguments: 1536 filename : (str) 1537 ascii file to store events. 1538 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1539 1540 Returns: 1541 a string descriptor of events. 1542 1543 Examples: 1544 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1545 """ 1546 if vedo.settings.dry_run_mode >= 1: 1547 return "" 1548 if not self.interactor: 1549 vedo.logger.warning("Cannot record events, no interactor defined.") 1550 return "" 1551 erec = vtki.new("InteractorEventRecorder") 1552 erec.SetInteractor(self.interactor) 1553 if not filename: 1554 if not os.path.exists(vedo.settings.cache_directory): 1555 os.makedirs(vedo.settings.cache_directory) 1556 home_dir = os.path.expanduser("~") 1557 filename = os.path.join( 1558 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1559 print("Events will be recorded in", filename) 1560 erec.SetFileName(filename) 1561 erec.SetKeyPressActivationValue("R") 1562 erec.EnabledOn() 1563 erec.Record() 1564 self.interactor.Start() 1565 erec.Stop() 1566 erec.EnabledOff() 1567 with open(filename, "r", encoding="UTF-8") as fl: 1568 events = fl.read() 1569 erec = None 1570 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.settings.cache_directory+"vedo/recorded_events.log"
.
Returns:
a string descriptor of events.
Examples:
1572 def play(self, recorded_events="", repeats=0) -> "Plotter": 1573 """ 1574 Play camera, mouse, keystrokes and all other events. 1575 1576 Arguments: 1577 events : (str) 1578 file o string of events. 1579 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1580 repeats : (int) 1581 number of extra repeats of the same events. The default is 0. 1582 1583 Examples: 1584 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1585 """ 1586 if vedo.settings.dry_run_mode >= 1: 1587 return self 1588 if not self.interactor: 1589 vedo.logger.warning("Cannot play events, no interactor defined.") 1590 return self 1591 1592 erec = vtki.new("InteractorEventRecorder") 1593 erec.SetInteractor(self.interactor) 1594 1595 if not recorded_events: 1596 home_dir = os.path.expanduser("~") 1597 recorded_events = os.path.join( 1598 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1599 1600 if recorded_events.endswith(".log"): 1601 erec.ReadFromInputStringOff() 1602 erec.SetFileName(recorded_events) 1603 else: 1604 erec.ReadFromInputStringOn() 1605 erec.SetInputString(recorded_events) 1606 1607 erec.Play() 1608 for _ in range(repeats): 1609 erec.Rewind() 1610 erec.Play() 1611 erec.EnabledOff() 1612 erec = None 1613 return self
Play camera, mouse, keystrokes and all other events.
Arguments:
- events : (str)
file o string of events.
The default is
vedo.settings.cache_directory+"vedo/recorded_events.log"
. - repeats : (int) number of extra repeats of the same events. The default is 0.
Examples:
1615 def parallel_projection(self, value=True, at=None) -> "Plotter": 1616 """ 1617 Use parallel projection `at` a specified renderer. 1618 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1619 An input value equal to -1 will toggle it on/off. 1620 """ 1621 if at is not None: 1622 r = self.renderers[at] 1623 else: 1624 r = self.renderer 1625 if value == -1: 1626 val = r.GetActiveCamera().GetParallelProjection() 1627 value = not val 1628 r.GetActiveCamera().SetParallelProjection(value) 1629 r.Modified() 1630 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.
1637 def fov(self, angle: float) -> "Plotter": 1638 """ 1639 Set the field of view angle for the camera. 1640 This is the angle of the camera frustum in the horizontal direction. 1641 High values will result in a wide-angle lens (fish-eye effect), 1642 and low values will result in a telephoto lens. 1643 1644 Default value is 30 degrees. 1645 """ 1646 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1647 self.renderer.GetActiveCamera().SetViewAngle(angle) 1648 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.
1650 def zoom(self, zoom: float) -> "Plotter": 1651 """Apply a zooming factor for the current camera view""" 1652 self.renderer.GetActiveCamera().Zoom(zoom) 1653 return self
Apply a zooming factor for the current camera view
1655 def azimuth(self, angle: float) -> "Plotter": 1656 """Rotate camera around the view up vector.""" 1657 self.renderer.GetActiveCamera().Azimuth(angle) 1658 return self
Rotate camera around the view up vector.
1660 def elevation(self, angle: float) -> "Plotter": 1661 """Rotate the camera around the cross product of the negative 1662 of the direction of projection and the view up vector.""" 1663 self.renderer.GetActiveCamera().Elevation(angle) 1664 return self
Rotate the camera around the cross product of the negative of the direction of projection and the view up vector.
1666 def roll(self, angle: float) -> "Plotter": 1667 """Roll the camera about the direction of projection.""" 1668 self.renderer.GetActiveCamera().Roll(angle) 1669 return self
Roll the camera about the direction of projection.
1671 def dolly(self, value: float) -> "Plotter": 1672 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1673 self.renderer.GetActiveCamera().Dolly(value) 1674 return self
Move the camera towards (value>0) or away from (value<0) the focal point.
1677 def add_slider( 1678 self, 1679 sliderfunc, 1680 xmin, 1681 xmax, 1682 value=None, 1683 pos=4, 1684 title="", 1685 font="Calco", 1686 title_size=1, 1687 c=None, 1688 alpha=1, 1689 show_value=True, 1690 delayed=False, 1691 **options, 1692 ) -> "vedo.addons.Slider2D": 1693 """ 1694 Add a `vedo.addons.Slider2D` which can call an external custom function. 1695 1696 Arguments: 1697 sliderfunc : (Callable) 1698 external function to be called by the widget 1699 xmin : (float) 1700 lower value of the slider 1701 xmax : (float) 1702 upper value 1703 value : (float) 1704 current value 1705 pos : (list, str) 1706 position corner number: horizontal [1-5] or vertical [11-15] 1707 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1708 and also by a string descriptor (eg. "bottom-left") 1709 title : (str) 1710 title text 1711 font : (str) 1712 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1713 title_size : (float) 1714 title text scale [1.0] 1715 show_value : (bool) 1716 if True current value is shown 1717 delayed : (bool) 1718 if True the callback is delayed until when the mouse button is released 1719 alpha : (float) 1720 opacity of the scalar bar texts 1721 slider_length : (float) 1722 slider length 1723 slider_width : (float) 1724 slider width 1725 end_cap_length : (float) 1726 length of the end cap 1727 end_cap_width : (float) 1728 width of the end cap 1729 tube_width : (float) 1730 width of the tube 1731 title_height : (float) 1732 width of the title 1733 tformat : (str) 1734 format of the title 1735 1736 Examples: 1737 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1738 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1739 1740 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1741 """ 1742 if c is None: # automatic black or white 1743 c = (0.8, 0.8, 0.8) 1744 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1745 c = (0.2, 0.2, 0.2) 1746 else: 1747 c = vedo.get_color(c) 1748 1749 slider2d = addons.Slider2D( 1750 sliderfunc, 1751 xmin, 1752 xmax, 1753 value, 1754 pos, 1755 title, 1756 font, 1757 title_size, 1758 c, 1759 alpha, 1760 show_value, 1761 delayed, 1762 **options, 1763 ) 1764 1765 if self.renderer: 1766 slider2d.renderer = self.renderer 1767 if self.interactor: 1768 slider2d.interactor = self.interactor 1769 slider2d.on() 1770 self.sliders.append([slider2d, sliderfunc]) 1771 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:
1773 def add_slider3d( 1774 self, 1775 sliderfunc, 1776 pos1, 1777 pos2, 1778 xmin, 1779 xmax, 1780 value=None, 1781 s=0.03, 1782 t=1, 1783 title="", 1784 rotation=0.0, 1785 c=None, 1786 show_value=True, 1787 ) -> "vedo.addons.Slider3D": 1788 """ 1789 Add a 3D slider widget which can call an external custom function. 1790 1791 Arguments: 1792 sliderfunc : (function) 1793 external function to be called by the widget 1794 pos1 : (list) 1795 first position 3D coordinates 1796 pos2 : (list) 1797 second position coordinates 1798 xmin : (float) 1799 lower value 1800 xmax : (float) 1801 upper value 1802 value : (float) 1803 initial value 1804 s : (float) 1805 label scaling factor 1806 t : (float) 1807 tube scaling factor 1808 title : (str) 1809 title text 1810 c : (color) 1811 slider color 1812 rotation : (float) 1813 title rotation around slider axis 1814 show_value : (bool) 1815 if True current value is shown 1816 1817 Examples: 1818 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1819 1820 ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png) 1821 """ 1822 if c is None: # automatic black or white 1823 c = (0.8, 0.8, 0.8) 1824 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1825 c = (0.2, 0.2, 0.2) 1826 else: 1827 c = vedo.get_color(c) 1828 1829 slider3d = addons.Slider3D( 1830 sliderfunc, 1831 pos1, 1832 pos2, 1833 xmin, 1834 xmax, 1835 value, 1836 s, 1837 t, 1838 title, 1839 rotation, 1840 c, 1841 show_value, 1842 ) 1843 slider3d.renderer = self.renderer 1844 slider3d.interactor = self.interactor 1845 slider3d.on() 1846 self.sliders.append([slider3d, sliderfunc]) 1847 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:
1906 def add_spline_tool( 1907 self, points, pc="k", ps=8, lc="r4", ac="g5", 1908 lw=2, alpha=1, closed=False, ontop=True, can_add_nodes=True, 1909 ) -> "vedo.addons.SplineTool": 1910 """ 1911 Add a spline tool to the current plotter. 1912 Nodes of the spline can be dragged in space with the mouse. 1913 Clicking on the line itself adds an extra point. 1914 Selecting a point and pressing del removes it. 1915 1916 Arguments: 1917 points : (Mesh, Points, array) 1918 the set of vertices forming the spline nodes. 1919 pc : (str) 1920 point color. The default is 'k'. 1921 ps : (str) 1922 point size. The default is 8. 1923 lc : (str) 1924 line color. The default is 'r4'. 1925 ac : (str) 1926 active point marker color. The default is 'g5'. 1927 lw : (int) 1928 line width. The default is 2. 1929 alpha : (float) 1930 line transparency. 1931 closed : (bool) 1932 spline is meant to be closed. The default is False. 1933 1934 Returns: 1935 a `SplineTool` object. 1936 1937 Examples: 1938 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1939 1940 ![](https://vedo.embl.es/images/basic/spline_tool.png) 1941 """ 1942 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1943 sw.interactor = self.interactor 1944 sw.on() 1945 sw.Initialize(sw.points.dataset) 1946 sw.representation.SetRenderer(self.renderer) 1947 sw.representation.SetClosedLoop(closed) 1948 sw.representation.BuildRepresentation() 1949 self.widgets.append(sw) 1950 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.
- alpha : (float) line transparency.
- closed : (bool) spline is meant to be closed. The default is False.
Returns:
a
SplineTool
object.
Examples:
1952 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1953 """Add an inset icon mesh into the same renderer. 1954 1955 Arguments: 1956 pos : (int, list) 1957 icon position in the range [1-4] indicating one of the 4 corners, 1958 or it can be a tuple (x,y) as a fraction of the renderer size. 1959 size : (float) 1960 size of the square inset. 1961 1962 Examples: 1963 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1964 """ 1965 iconw = addons.Icon(icon, pos, size) 1966 1967 iconw.SetInteractor(self.interactor) 1968 iconw.EnabledOn() 1969 iconw.InteractiveOff() 1970 self.widgets.append(iconw) 1971 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:
1973 def add_global_axes(self, axtype=None, c=None) -> "Plotter": 1974 """Draw axes on scene. Available axes types: 1975 1976 Arguments: 1977 axtype : (int) 1978 - 0, no axes, 1979 - 1, draw three gray grid walls 1980 - 2, show cartesian axes from (0,0,0) 1981 - 3, show positive range of cartesian axes from (0,0,0) 1982 - 4, show a triad at bottom left 1983 - 5, show a cube at bottom left 1984 - 6, mark the corners of the bounding box 1985 - 7, draw a 3D ruler at each side of the cartesian axes 1986 - 8, show the vtkCubeAxesActor object 1987 - 9, show the bounding box outLine 1988 - 10, show three circles representing the maximum bounding box 1989 - 11, show a large grid on the x-y plane 1990 - 12, show polar axes 1991 - 13, draw a simple ruler at the bottom of the window 1992 1993 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 1994 1995 Example: 1996 ```python 1997 from vedo import Box, show 1998 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 1999 show( 2000 b, 2001 axes={ 2002 "xtitle": "Some long variable [a.u.]", 2003 "number_of_divisions": 4, 2004 # ... 2005 }, 2006 ) 2007 ``` 2008 2009 Examples: 2010 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2011 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2012 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2013 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2014 2015 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2016 """ 2017 addons.add_global_axes(axtype, c) 2018 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:
2020 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2021 """Add a legend to the top right. 2022 2023 Examples: 2024 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 2025 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 2026 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 2027 """ 2028 acts = self.get_meshes() 2029 lb = addons.LegendBox(acts, **kwargs) 2030 self.add(lb) 2031 return lb
2033 def add_hint( 2034 self, 2035 obj, 2036 text="", 2037 c="k", 2038 bg="yellow9", 2039 font="Calco", 2040 size=18, 2041 justify=0, 2042 angle=0, 2043 delay=250, 2044 ) -> Union[vtki.vtkBalloonWidget, None]: 2045 """ 2046 Create a pop-up hint style message when hovering an object. 2047 Use `add_hint(obj, False)` to disable a hinting a specific object. 2048 Use `add_hint(None)` to disable all hints. 2049 2050 Arguments: 2051 obj : (Mesh, Points) 2052 the object to associate the pop-up to 2053 text : (str) 2054 string description of the pop-up 2055 delay : (int) 2056 milliseconds to wait before pop-up occurs 2057 """ 2058 if self.offscreen or not self.interactor: 2059 return None 2060 2061 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2062 # Linux vtk9.0 is bugged 2063 vedo.logger.warning( 2064 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2065 ) 2066 return None 2067 2068 if obj is None: 2069 self.hint_widget.EnabledOff() 2070 self.hint_widget.SetInteractor(None) 2071 self.hint_widget = None 2072 return self.hint_widget 2073 2074 if text is False and self.hint_widget: 2075 self.hint_widget.RemoveBalloon(obj) 2076 return self.hint_widget 2077 2078 if text == "": 2079 if obj.name: 2080 text = obj.name 2081 elif obj.filename: 2082 text = obj.filename 2083 else: 2084 return None 2085 2086 if not self.hint_widget: 2087 self.hint_widget = vtki.vtkBalloonWidget() 2088 2089 rep = self.hint_widget.GetRepresentation() 2090 rep.SetBalloonLayoutToImageRight() 2091 2092 trep = rep.GetTextProperty() 2093 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2094 trep.SetFontFile(utils.get_font_path(font)) 2095 trep.SetFontSize(size) 2096 trep.SetColor(vedo.get_color(c)) 2097 trep.SetBackgroundColor(vedo.get_color(bg)) 2098 trep.SetShadow(0) 2099 trep.SetJustification(justify) 2100 trep.UseTightBoundingBoxOn() 2101 2102 self.hint_widget.ManagesCursorOff() 2103 self.hint_widget.SetTimerDuration(delay) 2104 self.hint_widget.SetInteractor(self.interactor) 2105 if angle: 2106 trep.SetOrientation(angle) 2107 trep.SetBackgroundOpacity(0) 2108 # else: 2109 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2110 self.hint_widget.SetRepresentation(rep) 2111 self.widgets.append(self.hint_widget) 2112 self.hint_widget.EnabledOn() 2113 2114 bst = self.hint_widget.GetBalloonString(obj.actor) 2115 if bst: 2116 self.hint_widget.UpdateBalloonString(obj.actor, text) 2117 else: 2118 self.hint_widget.AddBalloon(obj.actor, text) 2119 2120 return self.hint_widget
Create a pop-up hint style message when hovering an object.
Use add_hint(obj, False)
to disable a hinting a specific object.
Use add_hint(None)
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
2122 def add_shadows(self) -> "Plotter": 2123 """Add shadows at the current renderer.""" 2124 if self.renderer: 2125 shadows = vtki.new("ShadowMapPass") 2126 seq = vtki.new("SequencePass") 2127 passes = vtki.new("RenderPassCollection") 2128 passes.AddItem(shadows.GetShadowMapBakerPass()) 2129 passes.AddItem(shadows) 2130 seq.SetPasses(passes) 2131 camerapass = vtki.new("CameraPass") 2132 camerapass.SetDelegatePass(seq) 2133 self.renderer.SetPass(camerapass) 2134 return self
Add shadows at the current renderer.
2136 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> "Plotter": 2137 """ 2138 Screen Space Ambient Occlusion. 2139 2140 For every pixel on the screen, the pixel shader samples the depth values around 2141 the current pixel and tries to compute the amount of occlusion from each of the sampled 2142 points. 2143 2144 Arguments: 2145 radius : (float) 2146 radius of influence in absolute units 2147 bias : (float) 2148 bias of the normals 2149 blur : (bool) 2150 add a blurring to the sampled positions 2151 samples : (int) 2152 number of samples to probe 2153 2154 Examples: 2155 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2156 2157 ![](https://vedo.embl.es/images/basic/ssao.jpg) 2158 """ 2159 lights = vtki.new("LightsPass") 2160 2161 opaque = vtki.new("OpaquePass") 2162 2163 ssaoCam = vtki.new("CameraPass") 2164 ssaoCam.SetDelegatePass(opaque) 2165 2166 ssao = vtki.new("SSAOPass") 2167 ssao.SetRadius(radius) 2168 ssao.SetBias(bias) 2169 ssao.SetBlur(blur) 2170 ssao.SetKernelSize(samples) 2171 ssao.SetDelegatePass(ssaoCam) 2172 2173 translucent = vtki.new("TranslucentPass") 2174 2175 volpass = vtki.new("VolumetricPass") 2176 ddp = vtki.new("DualDepthPeelingPass") 2177 ddp.SetTranslucentPass(translucent) 2178 ddp.SetVolumetricPass(volpass) 2179 2180 over = vtki.new("OverlayPass") 2181 2182 collection = vtki.new("RenderPassCollection") 2183 collection.AddItem(lights) 2184 collection.AddItem(ssao) 2185 collection.AddItem(ddp) 2186 collection.AddItem(over) 2187 2188 sequence = vtki.new("SequencePass") 2189 sequence.SetPasses(collection) 2190 2191 cam = vtki.new("CameraPass") 2192 cam.SetDelegatePass(sequence) 2193 2194 self.renderer.SetPass(cam) 2195 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:
2197 def add_depth_of_field(self, autofocus=True) -> "Plotter": 2198 """Add a depth of field effect in the scene.""" 2199 lights = vtki.new("LightsPass") 2200 2201 opaque = vtki.new("OpaquePass") 2202 2203 dofCam = vtki.new("CameraPass") 2204 dofCam.SetDelegatePass(opaque) 2205 2206 dof = vtki.new("DepthOfFieldPass") 2207 dof.SetAutomaticFocalDistance(autofocus) 2208 dof.SetDelegatePass(dofCam) 2209 2210 collection = vtki.new("RenderPassCollection") 2211 collection.AddItem(lights) 2212 collection.AddItem(dof) 2213 2214 sequence = vtki.new("SequencePass") 2215 sequence.SetPasses(collection) 2216 2217 cam = vtki.new("CameraPass") 2218 cam.SetDelegatePass(sequence) 2219 2220 self.renderer.SetPass(cam) 2221 return self
Add a depth of field effect in the scene.
2252 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None) -> "vedo.addons.RendererFrame": 2253 """ 2254 Add a frame to the renderer subwindow. 2255 2256 Arguments: 2257 c : (color) 2258 color name or index 2259 alpha : (float) 2260 opacity level 2261 lw : (int) 2262 line width in pixels. 2263 padding : (float) 2264 padding space in pixels. 2265 """ 2266 if c is None: # automatic black or white 2267 c = (0.9, 0.9, 0.9) 2268 if self.renderer: 2269 if np.sum(self.renderer.GetBackground()) > 1.5: 2270 c = (0.1, 0.1, 0.1) 2271 renf = addons.RendererFrame(c, alpha, lw, padding) 2272 if renf: 2273 self.renderer.AddActor(renf) 2274 return renf
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.
2276 def add_hover_legend( 2277 self, 2278 at=None, 2279 c=None, 2280 pos="bottom-left", 2281 font="Calco", 2282 s=0.75, 2283 bg="auto", 2284 alpha=0.1, 2285 maxlength=24, 2286 use_info=False, 2287 ) -> int: 2288 """ 2289 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2290 2291 The created text object are stored in `plotter.hover_legends`. 2292 2293 Returns: 2294 the id of the callback function. 2295 2296 Arguments: 2297 c : (color) 2298 Text color. If None then black or white is chosen automatically 2299 pos : (str) 2300 text positioning 2301 font : (str) 2302 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2303 s : (float) 2304 text size scale 2305 bg : (color) 2306 background color of the 2D box containing the text 2307 alpha : (float) 2308 box transparency 2309 maxlength : (int) 2310 maximum number of characters per line 2311 use_info : (bool) 2312 visualize the content of the `obj.info` attribute 2313 2314 Examples: 2315 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2316 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2317 2318 ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg) 2319 """ 2320 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2321 2322 if at is None: 2323 at = self.renderers.index(self.renderer) 2324 2325 def _legfunc(evt): 2326 if not evt.object or not self.renderer or at != evt.at: 2327 if hoverlegend.mapper.GetInput(): # clear and return 2328 hoverlegend.mapper.SetInput("") 2329 self.render() 2330 return 2331 2332 if use_info: 2333 if hasattr(evt.object, "info"): 2334 t = str(evt.object.info) 2335 else: 2336 return 2337 else: 2338 t, tp = "", "" 2339 if evt.isMesh: 2340 tp = "Mesh " 2341 elif evt.isPoints: 2342 tp = "Points " 2343 elif evt.isVolume: 2344 tp = "Volume " 2345 elif evt.isImage: 2346 tp = "Image " 2347 elif evt.isAssembly: 2348 tp = "Assembly " 2349 else: 2350 return 2351 2352 if evt.isAssembly: 2353 if not evt.object.name: 2354 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2355 else: 2356 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2357 else: 2358 if evt.object.name: 2359 t += f"{tp}name" 2360 if evt.isPoints: 2361 t += " " 2362 if evt.isMesh: 2363 t += " " 2364 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2365 2366 if evt.object.filename: 2367 t += f"{tp}filename: " 2368 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2369 t += "\n" 2370 if not evt.object.file_size: 2371 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2372 if evt.object.file_size: 2373 t += " : " 2374 sz, created = evt.object.file_size, evt.object.created 2375 t += f"{created[4:-5]} ({sz})" + "\n" 2376 2377 if evt.isPoints: 2378 indata = evt.object.dataset 2379 if indata.GetNumberOfPoints(): 2380 t += ( 2381 f"#points/cells: {indata.GetNumberOfPoints()}" 2382 f" / {indata.GetNumberOfCells()}" 2383 ) 2384 pdata = indata.GetPointData() 2385 cdata = indata.GetCellData() 2386 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2387 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2388 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2389 t += " *" 2390 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2391 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2392 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2393 t += " *" 2394 2395 if evt.isImage: 2396 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2397 t += f"\nImage shape: {evt.object.shape}" 2398 pcol = self.color_picker(evt.picked2d) 2399 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2400 2401 # change box color if needed in 'auto' mode 2402 if evt.isPoints and "auto" in str(bg): 2403 actcol = evt.object.properties.GetColor() 2404 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2405 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2406 2407 # adapt to changes in bg color 2408 bgcol = self.renderers[at].GetBackground() 2409 _bgcol = c 2410 if _bgcol is None: # automatic black or white 2411 _bgcol = (0.9, 0.9, 0.9) 2412 if sum(bgcol) > 1.5: 2413 _bgcol = (0.1, 0.1, 0.1) 2414 if len(set(_bgcol).intersection(bgcol)) < 3: 2415 hoverlegend.color(_bgcol) 2416 2417 if hoverlegend.mapper.GetInput() != t: 2418 hoverlegend.mapper.SetInput(t) 2419 self.interactor.Render() 2420 2421 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2422 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2423 2424 self.add(hoverlegend, at=at) 2425 self.hover_legends.append(hoverlegend) 2426 idcall = self.add_callback("MouseMove", _legfunc) 2427 return idcall
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
.
Returns:
the id of the callback function.
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:
2429 def add_scale_indicator( 2430 self, 2431 pos=(0.7, 0.05), 2432 s=0.02, 2433 length=2, 2434 lw=4, 2435 c="k1", 2436 alpha=1, 2437 units="", 2438 gap=0.05, 2439 ) -> Union["vedo.visual.Actor2D", None]: 2440 """ 2441 Add a Scale Indicator. Only works in parallel mode (no perspective). 2442 2443 Arguments: 2444 pos : (list) 2445 fractional (x,y) position on the screen. 2446 s : (float) 2447 size of the text. 2448 length : (float) 2449 length of the line. 2450 units : (str) 2451 string to show units. 2452 gap : (float) 2453 separation of line and text. 2454 2455 Example: 2456 ```python 2457 from vedo import settings, Cube, Plotter 2458 settings.use_parallel_projection = True # or else it does not make sense! 2459 cube = Cube().alpha(0.2) 2460 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2461 plt.add_scale_indicator(units='um', c='blue4') 2462 plt.show(cube, "Scale indicator with units").close() 2463 ``` 2464 ![](https://vedo.embl.es/images/feats/scale_indicator.png) 2465 """ 2466 # Note that this cannot go in addons.py 2467 # because it needs callbacks and window size 2468 if not self.interactor: 2469 return None 2470 2471 ppoints = vtki.vtkPoints() # Generate the polyline 2472 psqr = [[0.0, gap], [length / 10, gap]] 2473 dd = psqr[1][0] - psqr[0][0] 2474 for i, pt in enumerate(psqr): 2475 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2476 lines = vtki.vtkCellArray() 2477 lines.InsertNextCell(len(psqr)) 2478 for i in range(len(psqr)): 2479 lines.InsertCellPoint(i) 2480 pd = vtki.vtkPolyData() 2481 pd.SetPoints(ppoints) 2482 pd.SetLines(lines) 2483 2484 wsx, wsy = self.window.GetSize() 2485 if not self.camera.GetParallelProjection(): 2486 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2487 return None 2488 2489 rlabel = vtki.new("VectorText") 2490 rlabel.SetText("scale") 2491 tf = vtki.new("TransformPolyDataFilter") 2492 tf.SetInputConnection(rlabel.GetOutputPort()) 2493 t = vtki.vtkTransform() 2494 t.Scale(s * wsy / wsx, s, 1) 2495 tf.SetTransform(t) 2496 2497 app = vtki.new("AppendPolyData") 2498 app.AddInputConnection(tf.GetOutputPort()) 2499 app.AddInputData(pd) 2500 2501 mapper = vtki.new("PolyDataMapper2D") 2502 mapper.SetInputConnection(app.GetOutputPort()) 2503 cs = vtki.vtkCoordinate() 2504 cs.SetCoordinateSystem(1) 2505 mapper.SetTransformCoordinate(cs) 2506 2507 fractor = vedo.visual.Actor2D() 2508 csys = fractor.GetPositionCoordinate() 2509 csys.SetCoordinateSystem(3) 2510 fractor.SetPosition(pos) 2511 fractor.SetMapper(mapper) 2512 fractor.GetProperty().SetColor(vedo.get_color(c)) 2513 fractor.GetProperty().SetOpacity(alpha) 2514 fractor.GetProperty().SetLineWidth(lw) 2515 fractor.GetProperty().SetDisplayLocationToForeground() 2516 2517 def sifunc(iren, ev): 2518 wsx, wsy = self.window.GetSize() 2519 ps = self.camera.GetParallelScale() 2520 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2521 if units: 2522 newtxt += " " + units 2523 if rlabel.GetText() != newtxt: 2524 rlabel.SetText(newtxt) 2525 2526 self.renderer.AddActor(fractor) 2527 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2528 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2529 self.interactor.AddObserver("InteractionEvent", sifunc) 2530 sifunc(0, 0) 2531 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()
2533 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2534 """ 2535 Create an Event object with information of what was clicked. 2536 2537 If `enable_picking` is False, no picking will be performed. 2538 This can be useful to avoid double picking when using buttons. 2539 """ 2540 if not self.interactor: 2541 return Event() 2542 2543 if len(pos) > 0: 2544 x, y = pos 2545 self.interactor.SetEventPosition(pos) 2546 else: 2547 x, y = self.interactor.GetEventPosition() 2548 self.renderer = self.interactor.FindPokedRenderer(x, y) 2549 2550 self.picked2d = (x, y) 2551 2552 key = self.interactor.GetKeySym() 2553 2554 if key: 2555 if "_L" in key or "_R" in key: 2556 # skip things like Shift_R 2557 key = "" # better than None 2558 else: 2559 if self.interactor.GetShiftKey(): 2560 key = key.upper() 2561 2562 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2563 key = "underscore" 2564 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2565 key = "plus" 2566 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2567 key = "?" 2568 2569 if self.interactor.GetControlKey(): 2570 key = "Ctrl+" + key 2571 2572 if self.interactor.GetAltKey(): 2573 key = "Alt+" + key 2574 2575 if enable_picking: 2576 if not self.picker: 2577 self.picker = vtki.vtkPropPicker() 2578 2579 self.picker.PickProp(x, y, self.renderer) 2580 actor = self.picker.GetProp3D() 2581 # Note that GetProp3D already picks Assembly 2582 2583 xp, yp = self.interactor.GetLastEventPosition() 2584 dx, dy = x - xp, y - yp 2585 2586 delta3d = np.array([0, 0, 0]) 2587 2588 if actor: 2589 picked3d = np.array(self.picker.GetPickPosition()) 2590 2591 try: 2592 vobj = actor.retrieve_object() 2593 old_pt = np.asarray(vobj.picked3d) 2594 vobj.picked3d = picked3d 2595 delta3d = picked3d - old_pt 2596 except (AttributeError, TypeError): 2597 pass 2598 2599 else: 2600 picked3d = None 2601 2602 if not actor: # try 2D 2603 actor = self.picker.GetActor2D() 2604 2605 event = Event() 2606 event.name = ename 2607 event.title = self.title 2608 event.id = -1 # will be set by the timer wrapper function 2609 event.timerid = -1 # will be set by the timer wrapper function 2610 event.priority = -1 # will be set by the timer wrapper function 2611 event.time = time.time() 2612 event.at = self.renderers.index(self.renderer) 2613 event.keypress = key 2614 if enable_picking: 2615 try: 2616 event.object = actor.retrieve_object() 2617 except AttributeError: 2618 event.object = actor 2619 try: 2620 event.actor = actor.retrieve_object() # obsolete use object instead 2621 except AttributeError: 2622 event.actor = actor 2623 event.picked3d = picked3d 2624 event.picked2d = (x, y) 2625 event.delta2d = (dx, dy) 2626 event.angle2d = np.arctan2(dy, dx) 2627 event.speed2d = np.sqrt(dx * dx + dy * dy) 2628 event.delta3d = delta3d 2629 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2630 event.isPoints = isinstance(event.object, vedo.Points) 2631 event.isMesh = isinstance(event.object, vedo.Mesh) 2632 event.isAssembly = isinstance(event.object, vedo.Assembly) 2633 event.isVolume = isinstance(event.object, vedo.Volume) 2634 event.isImage = isinstance(event.object, vedo.Image) 2635 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2636 return event
Create an Event object with information of what was clicked.
If enable_picking
is False, no picking will be performed.
This can be useful to avoid double picking when using buttons.
2638 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2639 """ 2640 Add a function to be executed while show() is active. 2641 2642 Return a unique id for the callback. 2643 2644 The callback function (see example below) exposes a dictionary 2645 with the following information: 2646 - `name`: event name, 2647 - `id`: event unique identifier, 2648 - `priority`: event priority (float), 2649 - `interactor`: the interactor object, 2650 - `at`: renderer nr. where the event occurred 2651 - `keypress`: key pressed as string 2652 - `actor`: object picked by the mouse 2653 - `picked3d`: point picked in world coordinates 2654 - `picked2d`: screen coords of the mouse pointer 2655 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2656 - `delta3d`: ...same but in 3D world coords 2657 - `angle2d`: angle of mouse movement on screen 2658 - `speed2d`: speed of mouse movement on screen 2659 - `speed3d`: speed of picked point in world coordinates 2660 - `isPoints`: True if of class 2661 - `isMesh`: True if of class 2662 - `isAssembly`: True if of class 2663 - `isVolume`: True if of class Volume 2664 - `isImage`: True if of class 2665 2666 If `enable_picking` is False, no picking will be performed. 2667 This can be useful to avoid double picking when using buttons. 2668 2669 Frequently used events are: 2670 - `KeyPress`, `KeyRelease`: listen to keyboard events 2671 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2672 - `MiddleButtonPress`, `MiddleButtonRelease` 2673 - `RightButtonPress`, `RightButtonRelease` 2674 - `MouseMove`: listen to mouse pointer changing position 2675 - `MouseWheelForward`, `MouseWheelBackward` 2676 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2677 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2678 - `ResetCamera`, `ResetCameraClippingRange` 2679 - `Error`, `Warning` 2680 - `Char` 2681 - `Timer` 2682 2683 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2684 2685 Example: 2686 ```python 2687 from vedo import * 2688 2689 def func(evt): 2690 # this function is called every time the mouse moves 2691 # (evt is a dotted dictionary) 2692 if not evt.object: 2693 return # no hit, return 2694 print("point coords =", evt.picked3d) 2695 # print(evt) # full event dump 2696 2697 elli = Ellipsoid() 2698 plt = Plotter(axes=1) 2699 plt.add_callback('mouse hovering', func) 2700 plt.show(elli).close() 2701 ``` 2702 2703 Examples: 2704 - [spline_draw.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw.py) 2705 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2706 2707 ![](https://vedo.embl.es/images/advanced/spline_draw.png) 2708 2709 - ..and many others! 2710 """ 2711 from vtkmodules.util.misc import calldata_type 2712 2713 if not self.interactor: 2714 return 0 2715 2716 if vedo.settings.dry_run_mode >= 1: 2717 return 0 2718 2719 ######################################### 2720 @calldata_type(vtki.VTK_INT) 2721 def _func_wrap(iren, ename, timerid=None): 2722 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2723 event.timerid = timerid 2724 event.id = cid 2725 event.priority = priority 2726 self.last_event = event 2727 func(event) 2728 2729 ######################################### 2730 2731 event_name = utils.get_vtk_name_event(event_name) 2732 2733 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2734 # print(f"Registering event: {event_name} with id={cid}") 2735 return cid
Add a function to be executed while show() is active.
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 occurredkeypress
: key pressed as stringactor
: object picked by the mousepicked3d
: point picked in world coordinatespicked2d
: 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 VolumeisImage
: True if of class
If enable_picking
is False, no picking will be performed.
This can be useful to avoid double picking when using buttons.
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.object: return # no hit, return print("point coords =", evt.picked3d) # print(evt) # full event dump elli = Ellipsoid() plt = Plotter(axes=1) plt.add_callback('mouse hovering', func) plt.show(elli).close()
Examples:
- spline_draw.py
..and many others!
2737 def remove_callback(self, cid: Union[int, str]) -> "Plotter": 2738 """ 2739 Remove a callback function by its id 2740 or a whole category of callbacks by their name. 2741 2742 Arguments: 2743 cid : (int, str) 2744 Unique id of the callback. 2745 If an event name is passed all callbacks of that type are removed. 2746 """ 2747 if self.interactor: 2748 if isinstance(cid, str): 2749 cid = utils.get_vtk_name_event(cid) 2750 self.interactor.RemoveObservers(cid) 2751 else: 2752 self.interactor.RemoveObserver(cid) 2753 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.
2755 def remove_all_observers(self) -> "Plotter": 2756 """ 2757 Remove all observers. 2758 2759 Example: 2760 ```python 2761 from vedo import * 2762 2763 def kfunc(event): 2764 print("Key pressed:", event.keypress) 2765 if event.keypress == 'q': 2766 plt.close() 2767 2768 def rfunc(event): 2769 if event.isImage: 2770 printc("Right-clicked!", event) 2771 plt.render() 2772 2773 img = Image(dataurl+"images/embryo.jpg") 2774 2775 plt = Plotter(size=(1050, 600)) 2776 plt.parallel_projection(True) 2777 plt.remove_all_observers() 2778 plt.add_callback("key press", kfunc) 2779 plt.add_callback("mouse right click", rfunc) 2780 plt.show("Right-Click Me! Press q to exit.", img) 2781 plt.close() 2782 ``` 2783 """ 2784 if self.interactor: 2785 self.interactor.RemoveAllObservers() 2786 return self
Remove all observers.
Example:
from vedo import *
def kfunc(event):
print("Key pressed:", event.keypress)
if event.keypress == 'q':
plt.close()
def rfunc(event):
if event.isImage:
printc("Right-clicked!", event)
plt.render()
img = Image(dataurl+"images/embryo.jpg")
plt = Plotter(size=(1050, 600))
plt.parallel_projection(True)
plt.remove_all_observers()
plt.add_callback("key press", kfunc)
plt.add_callback("mouse right click", rfunc)
plt.show("Right-Click Me! Press q to exit.", img)
plt.close()
2788 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2789 """ 2790 Start or stop an existing timer. 2791 2792 Arguments: 2793 action : (str) 2794 Either "create"/"start" or "destroy"/"stop" 2795 timer_id : (int) 2796 When stopping the timer, the ID of the timer as returned when created 2797 dt : (int) 2798 time in milliseconds between each repeated call 2799 one_shot : (bool) 2800 create a one shot timer of prescribed duration instead of a repeating one 2801 2802 Examples: 2803 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2804 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2805 2806 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 2807 """ 2808 if action in ("create", "start"): 2809 if timer_id is not None: 2810 vedo.logger.warning("you set a timer_id but it will be ignored.") 2811 if one_shot: 2812 timer_id = self.interactor.CreateOneShotTimer(dt) 2813 else: 2814 timer_id = self.interactor.CreateRepeatingTimer(dt) 2815 return timer_id 2816 2817 elif action in ("destroy", "stop"): 2818 if timer_id is not None: 2819 self.interactor.DestroyTimer(timer_id) 2820 else: 2821 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2822 else: 2823 e = f"in timer_callback(). Cannot understand action: {action}\n" 2824 e += " allowed actions are: ['start', 'stop']. Skipped." 2825 vedo.logger.error(e) 2826 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:
2828 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2829 """ 2830 Add a callback function that will be called when an event occurs. 2831 Consider using `add_callback()` instead. 2832 """ 2833 if not self.interactor: 2834 return -1 2835 event_name = utils.get_vtk_name_event(event_name) 2836 idd = self.interactor.AddObserver(event_name, func, priority) 2837 return idd
Add a callback function that will be called when an event occurs.
Consider using add_callback()
instead.
2839 def compute_world_coordinate( 2840 self, 2841 pos2d: MutableSequence[float], 2842 at=None, 2843 objs=(), 2844 bounds=(), 2845 offset=None, 2846 pixeltol=None, 2847 worldtol=None, 2848 ) -> np.ndarray: 2849 """ 2850 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2851 If a set of meshes is passed then points are placed onto these. 2852 2853 Arguments: 2854 pos2d : (list) 2855 2D screen coordinates point. 2856 at : (int) 2857 renderer number. 2858 objs : (list) 2859 list of Mesh objects to project the point onto. 2860 bounds : (list) 2861 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2862 offset : (float) 2863 specify an offset value. 2864 pixeltol : (int) 2865 screen tolerance in pixels. 2866 worldtol : (float) 2867 world coordinates tolerance. 2868 2869 Returns: 2870 numpy array, the point in 3D world coordinates. 2871 2872 Examples: 2873 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2874 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2875 2876 ![](https://vedo.embl.es/images/basic/mousehover3.jpg) 2877 """ 2878 if at is not None: 2879 renderer = self.renderers[at] 2880 else: 2881 renderer = self.renderer 2882 2883 if not objs: 2884 pp = vtki.vtkFocalPlanePointPlacer() 2885 else: 2886 pps = vtki.vtkPolygonalSurfacePointPlacer() 2887 for ob in objs: 2888 pps.AddProp(ob.actor) 2889 pp = pps # type: ignore 2890 2891 if len(bounds) == 6: 2892 pp.SetPointBounds(bounds) 2893 if pixeltol: 2894 pp.SetPixelTolerance(pixeltol) 2895 if worldtol: 2896 pp.SetWorldTolerance(worldtol) 2897 if offset: 2898 pp.SetOffset(offset) 2899 2900 worldPos: MutableSequence[float] = [0, 0, 0] 2901 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2902 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2903 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2904 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2905 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:
2907 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2908 """ 2909 Given a 3D points in the current renderer (or full window), 2910 find the screen pixel coordinates. 2911 2912 Example: 2913 ```python 2914 from vedo import * 2915 2916 elli = Ellipsoid().point_size(5) 2917 2918 plt = Plotter() 2919 plt.show(elli, "Press q to continue and print the info") 2920 2921 xyscreen = plt.compute_screen_coordinates(elli) 2922 print('xyscreen coords:', xyscreen) 2923 2924 # simulate an event happening at one point 2925 event = plt.fill_event(pos=xyscreen[123]) 2926 print(event) 2927 ``` 2928 """ 2929 try: 2930 obj = obj.vertices 2931 except AttributeError: 2932 pass 2933 2934 if utils.is_sequence(obj): 2935 pts = obj 2936 p2d = [] 2937 cs = vtki.vtkCoordinate() 2938 cs.SetCoordinateSystemToWorld() 2939 cs.SetViewport(self.renderer) 2940 for p in pts: 2941 cs.SetValue(p) 2942 if full_window: 2943 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2944 else: 2945 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2946 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().point_size(5) plt = Plotter() plt.show(elli, "Press q to continue and print the info") xyscreen = plt.compute_screen_coordinates(elli) print('xyscreen coords:', xyscreen) # simulate an event happening at one point event = plt.fill_event(pos=xyscreen[123]) print(event)
2948 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2949 """ 2950 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2951 2952 Returns a frustum Mesh that contains the visible field of view. 2953 This can be used to select objects in a scene or select vertices. 2954 2955 Example: 2956 ```python 2957 from vedo import * 2958 2959 settings.enable_default_mouse_callbacks = False 2960 2961 def mode_select(objs): 2962 print("Selected objects:", objs) 2963 d0 = mode.start_x, mode.start_y # display coords 2964 d1 = mode.end_x, mode.end_y 2965 2966 frustum = plt.pick_area(d0, d1) 2967 col = np.random.randint(0, 10) 2968 infru = frustum.inside_points(mesh) 2969 infru.point_size(10).color(col) 2970 plt.add(frustum, infru).render() 2971 2972 mesh = Mesh(dataurl+"cow.vtk") 2973 mesh.color("k5").linewidth(1) 2974 2975 mode = interactor_modes.BlenderStyle() 2976 mode.callback_select = mode_select 2977 2978 plt = Plotter().user_mode(mode) 2979 plt.show(mesh, axes=1) 2980 ``` 2981 """ 2982 if at is not None: 2983 ren = self.renderers[at] 2984 else: 2985 ren = self.renderer 2986 area_picker = vtki.vtkAreaPicker() 2987 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 2988 planes = area_picker.GetFrustum() 2989 2990 fru = vtki.new("FrustumSource") 2991 fru.SetPlanes(planes) 2992 fru.ShowLinesOff() 2993 fru.Update() 2994 2995 afru = vedo.Mesh(fru.GetOutput()) 2996 afru.alpha(0.1).lw(1).pickable(False) 2997 afru.name = "Frustum" 2998 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) col = np.random.randint(0, 10) infru = frustum.inside_points(mesh) infru.point_size(10).color(col) plt.add(frustum, infru).render() mesh = Mesh(dataurl+"cow.vtk") mesh.color("k5").linewidth(1) mode = interactor_modes.BlenderStyle() mode.callback_select = mode_select plt = Plotter().user_mode(mode) plt.show(mesh, axes=1)
3121 def show( 3122 self, 3123 *objects, 3124 at=None, 3125 axes=None, 3126 resetcam=None, 3127 zoom=False, 3128 interactive=None, 3129 viewup="", 3130 azimuth=0.0, 3131 elevation=0.0, 3132 roll=0.0, 3133 camera=None, 3134 mode=None, 3135 rate=None, 3136 bg=None, 3137 bg2=None, 3138 size=None, 3139 title=None, 3140 screenshot="", 3141 ) -> Any: 3142 """ 3143 Render a list of objects. 3144 3145 Arguments: 3146 at : (int) 3147 number of the renderer to plot to, in case of more than one exists 3148 3149 axes : (int) 3150 axis type-1 can be fully customized by passing a dictionary. 3151 Check `addons.Axes()` for the full list of options. 3152 set the type of axes to be shown: 3153 - 0, no axes 3154 - 1, draw three gray grid walls 3155 - 2, show cartesian axes from (0,0,0) 3156 - 3, show positive range of cartesian axes from (0,0,0) 3157 - 4, show a triad at bottom left 3158 - 5, show a cube at bottom left 3159 - 6, mark the corners of the bounding box 3160 - 7, draw a 3D ruler at each side of the cartesian axes 3161 - 8, show the `vtkCubeAxesActor` object 3162 - 9, show the bounding box outLine 3163 - 10, show three circles representing the maximum bounding box 3164 - 11, show a large grid on the x-y plane 3165 - 12, show polar axes 3166 - 13, draw a simple ruler at the bottom of the window 3167 3168 azimuth/elevation/roll : (float) 3169 move camera accordingly the specified value 3170 3171 viewup: str, list 3172 either `['x', 'y', 'z']` or a vector to set vertical direction 3173 3174 resetcam : (bool) 3175 re-adjust camera position to fit objects 3176 3177 camera : (dict, vtkCamera) 3178 camera parameters can further be specified with a dictionary 3179 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3180 - pos, `(list)`, the position of the camera in world coordinates 3181 - focal_point `(list)`, the focal point of the camera in world coordinates 3182 - viewup `(list)`, the view up direction for the camera 3183 - distance `(float)`, set the focal point to the specified distance from the camera position. 3184 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3185 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3186 in world-coordinate distances. The default is 1. 3187 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3188 This method has no effect in perspective projection mode. 3189 3190 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3191 plane to be set a distance 'thickness' beyond the near clipping plane. 3192 3193 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3194 measured in degrees. The default angle is 30 degrees. 3195 This method has no effect in parallel projection mode. 3196 The formula for setting the angle up for perfect perspective viewing is: 3197 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3198 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3199 3200 interactive : (bool) 3201 pause and interact with window (True) or continue execution (False) 3202 3203 rate : (float) 3204 maximum rate of `show()` in Hertz 3205 3206 mode : (int, str) 3207 set the type of interaction: 3208 - 0 = TrackballCamera [default] 3209 - 1 = TrackballActor 3210 - 2 = JoystickCamera 3211 - 3 = JoystickActor 3212 - 4 = Flight 3213 - 5 = RubberBand2D 3214 - 6 = RubberBand3D 3215 - 7 = RubberBandZoom 3216 - 8 = Terrain 3217 - 9 = Unicam 3218 - 10 = Image 3219 - Check out `vedo.interaction_modes` for more options. 3220 3221 bg : (str, list) 3222 background color in RGB format, or string name 3223 3224 bg2 : (str, list) 3225 second background color to create a gradient background 3226 3227 size : (str, list) 3228 size of the window, e.g. size="fullscreen", or size=[600,400] 3229 3230 title : (str) 3231 window title text 3232 3233 screenshot : (str) 3234 save a screenshot of the window to file 3235 """ 3236 3237 if vedo.settings.dry_run_mode >= 2: 3238 return self 3239 3240 if self.wx_widget: 3241 return self 3242 3243 if self.renderers: # in case of notebooks 3244 3245 if at is None: 3246 at = self.renderers.index(self.renderer) 3247 3248 else: 3249 3250 if at >= len(self.renderers): 3251 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3252 vedo.logger.error(t) 3253 return self 3254 3255 self.renderer = self.renderers[at] 3256 3257 if title is not None: 3258 self.title = title 3259 3260 if size is not None: 3261 self.size = size 3262 if self.size[0] == "f": # full screen 3263 self.size = "fullscreen" 3264 self.window.SetFullScreen(True) 3265 self.window.BordersOn() 3266 else: 3267 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3268 3269 if vedo.settings.default_backend == "vtk": 3270 if str(bg).endswith(".hdr"): 3271 self._add_skybox(bg) 3272 else: 3273 if bg is not None: 3274 self.backgrcol = vedo.get_color(bg) 3275 self.renderer.SetBackground(self.backgrcol) 3276 if bg2 is not None: 3277 self.renderer.GradientBackgroundOn() 3278 self.renderer.SetBackground2(vedo.get_color(bg2)) 3279 3280 if axes is not None: 3281 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3282 objects = list(objects) 3283 objects.append(axes) # move it into the list of normal things to show 3284 axes = 0 3285 self.axes = axes 3286 3287 if interactive is not None: 3288 self._interactive = interactive 3289 if self.offscreen: 3290 self._interactive = False 3291 3292 # camera stuff 3293 if resetcam is not None: 3294 self.resetcam = resetcam 3295 3296 if camera is not None: 3297 self.resetcam = False 3298 viewup = "" 3299 if isinstance(camera, vtki.vtkCamera): 3300 cameracopy = vtki.vtkCamera() 3301 cameracopy.DeepCopy(camera) 3302 self.camera = cameracopy 3303 else: 3304 self.camera = utils.camera_from_dict(camera) 3305 3306 self.add(objects) 3307 3308 # Backend ############################################################### 3309 if vedo.settings.default_backend in ["k3d"]: 3310 return backends.get_notebook_backend(self.objects) 3311 ######################################################################### 3312 3313 for ia in utils.flatten(objects): 3314 try: 3315 # fix gray color labels and title to white or black 3316 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3317 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3318 c = (0.9, 0.9, 0.9) 3319 if np.sum(self.renderer.GetBackground()) > 1.5: 3320 c = (0.1, 0.1, 0.1) 3321 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3322 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3323 except AttributeError: 3324 pass 3325 3326 if self.sharecam: 3327 for r in self.renderers: 3328 r.SetActiveCamera(self.camera) 3329 3330 if self.axes is not None: 3331 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3332 bns = self.renderer.ComputeVisiblePropBounds() 3333 addons.add_global_axes(self.axes, bounds=bns) 3334 3335 # Backend ############################################################### 3336 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3337 return backends.get_notebook_backend() 3338 ######################################################################### 3339 3340 if self.resetcam: 3341 self.renderer.ResetCamera() 3342 3343 if len(self.renderers) > 1: 3344 self.add_renderer_frame() 3345 3346 if vedo.settings.default_backend == "2d" and not zoom: 3347 zoom = "tightest" 3348 3349 if zoom: 3350 if zoom == "tight": 3351 self.reset_camera(tight=0.04) 3352 elif zoom == "tightest": 3353 self.reset_camera(tight=0.0001) 3354 else: 3355 self.camera.Zoom(zoom) 3356 if elevation: 3357 self.camera.Elevation(elevation) 3358 if azimuth: 3359 self.camera.Azimuth(azimuth) 3360 if roll: 3361 self.camera.Roll(roll) 3362 3363 if len(viewup) > 0: 3364 b = self.renderer.ComputeVisiblePropBounds() 3365 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3366 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3367 if viewup == "x": 3368 sz = np.linalg.norm(sz) 3369 self.camera.SetViewUp([1, 0, 0]) 3370 self.camera.SetPosition(cm + sz) 3371 elif viewup == "y": 3372 sz = np.linalg.norm(sz) 3373 self.camera.SetViewUp([0, 1, 0]) 3374 self.camera.SetPosition(cm + sz) 3375 elif viewup == "z": 3376 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3377 self.camera.SetViewUp([0, 0, 1]) 3378 self.camera.SetPosition(cm + 2 * sz) 3379 elif utils.is_sequence(viewup): 3380 sz = np.linalg.norm(sz) 3381 self.camera.SetViewUp(viewup) 3382 cpos = np.cross([0, 1, 0], viewup) 3383 self.camera.SetPosition(cm - 2 * sz * cpos) 3384 3385 self.renderer.ResetCameraClippingRange() 3386 3387 self.initialize_interactor() 3388 3389 if vedo.settings.immediate_rendering: 3390 self.window.Render() ##################### <-------------- Render 3391 3392 if self.interactor: # can be offscreen or not the vtk backend.. 3393 3394 self.window.SetWindowName(self.title) 3395 3396 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3397 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3398 # print(pic.dataset)# Array 0 name PNGImage 3399 # self.window.SetIcon(pic.dataset) 3400 3401 try: 3402 # Needs "pip install pyobjc" on Mac OSX 3403 if ( 3404 self._cocoa_initialized is False 3405 and "Darwin" in vedo.sys_platform 3406 and not self.offscreen 3407 ): 3408 self._cocoa_initialized = True 3409 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3410 pid = os.getpid() 3411 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3412 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3413 except: 3414 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3415 pass 3416 3417 # Set the interaction style 3418 if mode is not None: 3419 self.user_mode(mode) 3420 if self.qt_widget and mode is None: 3421 self.user_mode(0) 3422 3423 if screenshot: 3424 self.screenshot(screenshot) 3425 3426 if self._interactive: 3427 self.interactor.Start() 3428 if self._must_close_now: 3429 self.interactor.GetRenderWindow().Finalize() 3430 self.interactor.TerminateApp() 3431 self.camera = None 3432 self.renderer = None 3433 self.renderers = [] 3434 self.window = None 3435 self.interactor = None 3436 return self 3437 3438 if rate: 3439 if self.clock is None: # set clock and limit rate 3440 self._clockt0 = time.time() 3441 self.clock = 0.0 3442 else: 3443 t = time.time() - self._clockt0 3444 elapsed = t - self.clock 3445 mint = 1.0 / rate 3446 if elapsed < mint: 3447 time.sleep(mint - elapsed) 3448 self.clock = time.time() - self._clockt0 3449 3450 # 2d #################################################################### 3451 if vedo.settings.default_backend == "2d": 3452 return backends.get_notebook_backend() 3453 ######################################################################### 3454 3455 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.
- bg : (str, list) background color in RGB format, or string name
- bg2 : (str, list) second background color to create a gradient background
- size : (str, list) size of the window, e.g. size="fullscreen", or size=[600,400]
- title : (str) window title text
- screenshot : (str) save a screenshot of the window to file
3458 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3459 """Add a draggable inset space into a renderer. 3460 3461 Arguments: 3462 at : (int) 3463 specify the renderer number 3464 pos : (list) 3465 icon position in the range [1-4] indicating one of the 4 corners, 3466 or it can be a tuple (x,y) as a fraction of the renderer size. 3467 size : (float) 3468 size of the square inset 3469 draggable : (bool) 3470 if True the subrenderer space can be dragged around 3471 c : (color) 3472 color of the inset frame when dragged 3473 3474 Examples: 3475 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3476 3477 ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg) 3478 """ 3479 if not self.interactor: 3480 return None 3481 3482 if not self.renderer: 3483 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3484 return None 3485 3486 options = dict(options) 3487 pos = options.pop("pos", 0) 3488 size = options.pop("size", 0.1) 3489 c = options.pop("c", "lb") 3490 at = options.pop("at", None) 3491 draggable = options.pop("draggable", True) 3492 3493 widget = vtki.vtkOrientationMarkerWidget() 3494 r, g, b = vedo.get_color(c) 3495 widget.SetOutlineColor(r, g, b) 3496 if len(objects) == 1: 3497 widget.SetOrientationMarker(objects[0].actor) 3498 else: 3499 widget.SetOrientationMarker(vedo.Assembly(objects)) 3500 3501 widget.SetInteractor(self.interactor) 3502 3503 if utils.is_sequence(pos): 3504 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3505 else: 3506 if pos < 2: 3507 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3508 elif pos == 2: 3509 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3510 elif pos == 3: 3511 widget.SetViewport(0, 0, size * 2, size * 2) 3512 elif pos == 4: 3513 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3514 widget.EnabledOn() 3515 widget.SetInteractive(draggable) 3516 if at is not None and at < len(self.renderers): 3517 widget.SetCurrentRenderer(self.renderers[at]) 3518 else: 3519 widget.SetCurrentRenderer(self.renderer) 3520 self.widgets.append(widget) 3521 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:
3523 def clear(self, at=None, deep=False) -> "Plotter": 3524 """Clear the scene from all meshes and volumes.""" 3525 if at is not None: 3526 renderer = self.renderers[at] 3527 else: 3528 renderer = self.renderer 3529 if not renderer: 3530 return self 3531 3532 if deep: 3533 renderer.RemoveAllViewProps() 3534 else: 3535 for ob in set( 3536 self.get_meshes() 3537 + self.get_volumes() 3538 + self.objects 3539 + self.axes_instances 3540 ): 3541 if isinstance(ob, vedo.shapes.Text2D): 3542 continue 3543 self.remove(ob) 3544 try: 3545 if ob.scalarbar: 3546 self.remove(ob.scalarbar) 3547 except AttributeError: 3548 pass 3549 return self
Clear the scene from all meshes and volumes.
3551 def break_interaction(self) -> "Plotter": 3552 """Break window interaction and return to the python execution flow""" 3553 if self.interactor: 3554 self.check_actors_trasform() 3555 self.interactor.ExitCallback() 3556 return self
Break window interaction and return to the python execution flow
3558 def user_mode(self, mode) -> Union["Plotter", None]: 3559 """ 3560 Modify the user interaction mode. 3561 3562 Examples: 3563 ```python 3564 from vedo import * 3565 mode = interactor_modes.MousePan() 3566 mesh = Mesh(dataurl+"cow.vtk") 3567 plt = Plotter().user_mode(mode) 3568 plt.show(mesh, axes=1) 3569 ``` 3570 See also: 3571 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3572 """ 3573 if not self.interactor: 3574 return None 3575 3576 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3577 # print("Current style:", curr_style) 3578 if curr_style.endswith("Actor"): 3579 self.check_actors_trasform() 3580 3581 if isinstance(mode, (str, int)): 3582 # Set the style of interaction 3583 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3584 if mode in (0, "TrackballCamera"): 3585 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3586 self.interactor.RemoveObservers("CharEvent") 3587 elif mode in (1, "TrackballActor"): 3588 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3589 elif mode in (2, "JoystickCamera"): 3590 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3591 elif mode in (3, "JoystickActor"): 3592 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3593 elif mode in (4, "Flight"): 3594 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3595 elif mode in (5, "RubberBand2D"): 3596 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3597 elif mode in (6, "RubberBand3D"): 3598 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3599 elif mode in (7, "RubberBandZoom"): 3600 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3601 elif mode in (8, "Terrain"): 3602 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3603 elif mode in (9, "Unicam"): 3604 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3605 elif mode in (10, "Image", "image", "2d"): 3606 astyle = vtki.new("InteractorStyleImage") 3607 astyle.SetInteractionModeToImage3D() 3608 self.interactor.SetInteractorStyle(astyle) 3609 else: 3610 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3611 3612 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3613 # set a custom interactor style 3614 if hasattr(mode, "interactor"): 3615 mode.interactor = self.interactor 3616 mode.renderer = self.renderer # type: ignore 3617 mode.SetInteractor(self.interactor) 3618 mode.SetDefaultRenderer(self.renderer) 3619 self.interactor.SetInteractorStyle(mode) 3620 3621 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: VTK interactor styles
3623 def close(self) -> "Plotter": 3624 """Close the plotter.""" 3625 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3626 vedo.last_figure = None 3627 self.last_event = None 3628 self.sliders = [] 3629 self.buttons = [] 3630 self.widgets = [] 3631 self.hover_legends = [] 3632 self.background_renderer = None 3633 self._extralight = None 3634 3635 self.hint_widget = None 3636 self.cutter_widget = None 3637 3638 if vedo.settings.dry_run_mode >= 2: 3639 return self 3640 3641 if not hasattr(self, "window"): 3642 return self 3643 if not self.window: 3644 return self 3645 if not hasattr(self, "interactor"): 3646 return self 3647 if not self.interactor: 3648 return self 3649 3650 ################################################### 3651 try: 3652 if "Darwin" in vedo.sys_platform: 3653 self.interactor.ProcessEvents() 3654 except: 3655 pass 3656 3657 self._must_close_now = True 3658 3659 if vedo.plotter_instance == self: 3660 vedo.plotter_instance = None 3661 3662 if self.interactor and self._interactive: 3663 self.break_interaction() 3664 elif self._must_close_now: 3665 # dont call ExitCallback here 3666 self.interactor.GetRenderWindow().Finalize() 3667 self.interactor.TerminateApp() 3668 self.camera = None 3669 self.renderer = None 3670 self.renderers = [] 3671 self.window = None 3672 self.interactor = None 3673 return self
Close the plotter.
3675 @property 3676 def camera(self): 3677 """Return the current active camera.""" 3678 if self.renderer: 3679 return self.renderer.GetActiveCamera()
Return the current active camera.
3688 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3689 """ 3690 Take a screenshot of the Plotter window. 3691 3692 Arguments: 3693 scale : (int) 3694 set image magnification as an integer multiplicating factor 3695 asarray : (bool) 3696 return a numpy array of the image instead of writing a file 3697 3698 Warning: 3699 If you get black screenshots try to set `interactive=False` in `show()` 3700 then call `screenshot()` and `plt.interactive()` afterwards. 3701 3702 Example: 3703 ```py 3704 from vedo import * 3705 sphere = Sphere().linewidth(1) 3706 plt = show(sphere, interactive=False) 3707 plt.screenshot('image.png') 3708 plt.interactive() 3709 plt.close() 3710 ``` 3711 3712 Example: 3713 ```py 3714 from vedo import * 3715 sphere = Sphere().linewidth(1) 3716 plt = show(sphere, interactive=False) 3717 plt.screenshot('anotherimage.png') 3718 plt.interactive() 3719 plt.close() 3720 ``` 3721 """ 3722 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
Warning:
If you get black screenshots try to set
interactive=False
inshow()
then callscreenshot()
andplt.interactive()
afterwards.
Example:
from vedo import * sphere = Sphere().linewidth(1) plt = show(sphere, interactive=False) plt.screenshot('image.png') plt.interactive() plt.close()
Example:
from vedo import * sphere = Sphere().linewidth(1) plt = show(sphere, interactive=False) plt.screenshot('anotherimage.png') plt.interactive() plt.close()
3724 def toimage(self, scale=1) -> "vedo.image.Image": 3725 """ 3726 Generate a `Image` object from the current rendering window. 3727 3728 Arguments: 3729 scale : (int) 3730 set image magnification as an integer multiplicating factor 3731 """ 3732 if vedo.settings.screeshot_large_image: 3733 w2if = vtki.new("RenderLargeImage") 3734 w2if.SetInput(self.renderer) 3735 w2if.SetMagnification(scale) 3736 else: 3737 w2if = vtki.new("WindowToImageFilter") 3738 w2if.SetInput(self.window) 3739 if hasattr(w2if, "SetScale"): 3740 w2if.SetScale(scale, scale) 3741 if vedo.settings.screenshot_transparent_background: 3742 w2if.SetInputBufferTypeToRGBA() 3743 w2if.ReadFrontBufferOff() # read from the back buffer 3744 w2if.Update() 3745 return vedo.image.Image(w2if.GetOutput())
Generate a Image
object from the current rendering window.
Arguments:
- scale : (int) set image magnification as an integer multiplicating factor
3747 def export(self, filename="scene.npz", binary=False) -> "Plotter": 3748 """ 3749 Export scene to file to HTML, X3D or Numpy file. 3750 3751 Examples: 3752 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3753 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3754 """ 3755 vedo.file_io.export_window(filename, binary=binary) 3756 return self
3758 def color_picker(self, xy, verbose=False): 3759 """Pick color of specific (x,y) pixel on the screen.""" 3760 w2if = vtki.new("WindowToImageFilter") 3761 w2if.SetInput(self.window) 3762 w2if.ReadFrontBufferOff() 3763 w2if.Update() 3764 nx, ny = self.window.GetSize() 3765 varr = w2if.GetOutput().GetPointData().GetScalars() 3766 3767 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3768 x, y = int(xy[0]), int(xy[1]) 3769 if y < ny and x < nx: 3770 3771 rgb = arr[y, x] 3772 3773 if verbose: 3774 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3775 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3776 vedo.printc("█", c=[0, rgb[1], 0], end="") 3777 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3778 vedo.printc("] = ", end="") 3779 cnm = vedo.get_color_name(rgb) 3780 if np.sum(rgb) < 150: 3781 vedo.printc( 3782 rgb.tolist(), 3783 vedo.colors.rgb2hex(np.array(rgb) / 255), 3784 c="w", 3785 bc=rgb, 3786 invert=1, 3787 end="", 3788 ) 3789 vedo.printc(" -> " + cnm, invert=1, c="w") 3790 else: 3791 vedo.printc( 3792 rgb.tolist(), 3793 vedo.colors.rgb2hex(np.array(rgb) / 255), 3794 c=rgb, 3795 end="", 3796 ) 3797 vedo.printc(" -> " + cnm, c=cnm) 3798 3799 return rgb 3800 3801 return None
Pick color of specific (x,y) pixel on the screen.
117def show( 118 *objects, 119 at=None, 120 shape=(1, 1), 121 N=None, 122 pos=(0, 0), 123 size="auto", 124 screensize="auto", 125 title="vedo", 126 bg="white", 127 bg2=None, 128 axes=None, 129 interactive=None, 130 offscreen=False, 131 sharecam=True, 132 resetcam=True, 133 zoom=None, 134 viewup="", 135 azimuth=0.0, 136 elevation=0.0, 137 roll=0.0, 138 camera=None, 139 mode=None, 140 screenshot="", 141 new=False, 142) -> Union["Plotter", None]: 143 """ 144 Create on the fly an instance of class Plotter and show the object(s) provided. 145 146 Arguments: 147 at : (int) 148 number of the renderer to plot to, in case of more than one exists 149 shape : (list, str) 150 Number of sub-render windows inside of the main window. E.g.: 151 specify two across with shape=(2,1) and a two by two grid 152 with shape=(2, 2). By default there is only one renderer. 153 154 Can also accept a shape as string descriptor. E.g.: 155 - shape="3|1" means 3 plots on the left and 1 on the right, 156 - shape="4/2" means 4 plots on top of 2 at bottom. 157 N : (int) 158 number of desired sub-render windows arranged automatically in a grid 159 pos : (list) 160 position coordinates of the top-left corner of the rendering window 161 on the screen 162 size : (list) 163 size of the rendering window 164 screensize : (list) 165 physical size of the monitor screen 166 title : (str) 167 window title 168 bg : (color) 169 background color or specify jpg image file name with path 170 bg2 : (color) 171 background color of a gradient towards the top 172 axes : (int) 173 set the type of axes to be shown: 174 - 0, no axes 175 - 1, draw three gray grid walls 176 - 2, show cartesian axes from (0,0,0) 177 - 3, show positive range of cartesian axes from (0,0,0) 178 - 4, show a triad at bottom left 179 - 5, show a cube at bottom left 180 - 6, mark the corners of the bounding box 181 - 7, draw a 3D ruler at each side of the cartesian axes 182 - 8, show the `vtkCubeAxesActor` object 183 - 9, show the bounding box outLine 184 - 10, show three circles representing the maximum bounding box 185 - 11, show a large grid on the x-y plane 186 - 12, show polar axes 187 - 13, draw a simple ruler at the bottom of the window 188 - 14: draw a `CameraOrientationWidget` 189 190 Axis type-1 can be fully customized by passing a dictionary. 191 Check `vedo.addons.Axes()` for the full list of options. 192 azimuth/elevation/roll : (float) 193 move camera accordingly the specified value 194 viewup : (str, list) 195 either `['x', 'y', 'z']` or a vector to set vertical direction 196 resetcam : (bool) 197 re-adjust camera position to fit objects 198 camera : (dict, vtkCamera) 199 camera parameters can further be specified with a dictionary 200 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 201 - **pos** (list), the position of the camera in world coordinates 202 - **focal_point** (list), the focal point of the camera in world coordinates 203 - **viewup** (list), the view up direction for the camera 204 - **distance** (float), set the focal point to the specified distance from the camera position. 205 - **clipping_range** (float), distance of the near and far clipping planes along the direction of projection. 206 - **parallel_scale** (float), 207 scaling used for a parallel projection, i.e. the height of the viewport 208 in world-coordinate distances. The default is 1. Note that the "scale" parameter works as 209 an "inverse scale", larger numbers produce smaller images. 210 This method has no effect in perspective projection mode. 211 - **thickness** (float), 212 set the distance between clipping planes. This method adjusts the far clipping 213 plane to be set a distance 'thickness' beyond the near clipping plane. 214 - **view_angle** (float), 215 the camera view angle, which is the angular height of the camera view 216 measured in degrees. The default angle is 30 degrees. 217 This method has no effect in parallel projection mode. 218 The formula for setting the angle up for perfect perspective viewing is: 219 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 220 (measured by holding a ruler up to your screen) and d is the distance 221 from your eyes to the screen. 222 interactive : (bool) 223 pause and interact with window (True) or continue execution (False) 224 rate : (float) 225 maximum rate of `show()` in Hertz 226 mode : (int, str) 227 set the type of interaction: 228 - 0 = TrackballCamera [default] 229 - 1 = TrackballActor 230 - 2 = JoystickCamera 231 - 3 = JoystickActor 232 - 4 = Flight 233 - 5 = RubberBand2D 234 - 6 = RubberBand3D 235 - 7 = RubberBandZoom 236 - 8 = Terrain 237 - 9 = Unicam 238 - 10 = Image 239 new : (bool) 240 if set to `True`, a call to show will instantiate 241 a new Plotter object (a new window) instead of reusing the first created. 242 If new is `True`, but the existing plotter was instantiated with a different 243 argument for `offscreen`, `new` is ignored and a new Plotter is created anyway. 244 """ 245 if len(objects) == 0: 246 objects = None 247 elif len(objects) == 1: 248 objects = objects[0] 249 else: 250 objects = utils.flatten(objects) 251 252 # If a plotter instance is already present, check if the offscreen argument 253 # is the same as the one requested by the user. If not, create a new 254 # plotter instance (see https://github.com/marcomusy/vedo/issues/1026) 255 if vedo.plotter_instance and vedo.plotter_instance.offscreen != offscreen: 256 new = True 257 258 if vedo.plotter_instance and not new: # Plotter exists 259 plt = vedo.plotter_instance 260 261 else: # Plotter must be created 262 263 if utils.is_sequence(at): # user passed a sequence for "at" 264 265 if not utils.is_sequence(objects): 266 vedo.logger.error("in show() input must be a list.") 267 raise RuntimeError() 268 if len(at) != len(objects): 269 vedo.logger.error("in show() lists 'input' and 'at' must have equal lengths") 270 raise RuntimeError() 271 if shape == (1, 1) and N is None: 272 N = max(at) + 1 273 274 elif at is None and (N or shape != (1, 1)): 275 276 if not utils.is_sequence(objects): 277 e = "in show(), N or shape is set, but input is not a sequence\n" 278 e += " you may need to specify e.g. at=0" 279 vedo.logger.error(e) 280 raise RuntimeError() 281 at = list(range(len(objects))) 282 283 plt = Plotter( 284 shape=shape, 285 N=N, 286 pos=pos, 287 size=size, 288 screensize=screensize, 289 title=title, 290 axes=axes, 291 sharecam=sharecam, 292 resetcam=resetcam, 293 interactive=interactive, 294 offscreen=offscreen, 295 bg=bg, 296 bg2=bg2, 297 ) 298 299 if vedo.settings.dry_run_mode >= 2: 300 return plt 301 302 # use _plt_to_return because plt.show() can return a k3d plot 303 _plt_to_return = None 304 305 if utils.is_sequence(at): 306 307 for i, act in enumerate(objects): 308 _plt_to_return = plt.show( 309 act, 310 at=i, 311 zoom=zoom, 312 resetcam=resetcam, 313 viewup=viewup, 314 azimuth=azimuth, 315 elevation=elevation, 316 roll=roll, 317 camera=camera, 318 interactive=False, 319 mode=mode, 320 screenshot=screenshot, 321 bg=bg, 322 bg2=bg2, 323 axes=axes, 324 ) 325 326 if ( 327 interactive 328 or len(at) == N 329 or (isinstance(shape[0], int) and len(at) == shape[0] * shape[1]) 330 ): 331 # note that shape can be a string 332 if plt.interactor and not offscreen and (interactive is None or interactive): 333 plt.interactor.Start() 334 if plt._must_close_now: 335 plt.interactor.GetRenderWindow().Finalize() 336 plt.interactor.TerminateApp() 337 plt.interactor = None 338 plt.window = None 339 plt.renderer = None 340 plt.renderers = [] 341 plt.camera = None 342 343 else: 344 345 _plt_to_return = plt.show( 346 objects, 347 at=at, 348 zoom=zoom, 349 resetcam=resetcam, 350 viewup=viewup, 351 azimuth=azimuth, 352 elevation=elevation, 353 roll=roll, 354 camera=camera, 355 interactive=interactive, 356 mode=mode, 357 screenshot=screenshot, 358 bg=bg, 359 bg2=bg2, 360 axes=axes, 361 ) 362 363 return _plt_to_return
Create on the fly an instance of class Plotter and show the object(s) provided.
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.
- N : (int) number of desired sub-render windows arranged automatically in a grid
- pos : (list) position coordinates of the top-left corner of the rendering window on the screen
- size : (list) size of the rendering window
- screensize : (list) physical size of the monitor screen
- title : (str) window title
- bg : (color) background color or specify jpg image file name with path
- bg2 : (color) background color of a gradient towards the top
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
- 14: draw a
CameraOrientationWidget
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. If new isTrue
, but the existing plotter was instantiated with a different argument foroffscreen
,new
is ignored and a new Plotter is created anyway.
366def close() -> None: 367 """Close the last created Plotter instance if it exists.""" 368 if not vedo.plotter_instance: 369 return 370 vedo.plotter_instance.close() 371 return
Close the last created Plotter instance if it exists.