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 7from typing_extensions import Self 8import numpy as np 9 10import vedo.vtkclasses as vtki # a wrapper for lazy imports 11 12import vedo 13from vedo import transformations 14from vedo import utils 15from vedo import backends 16from vedo import addons 17 18 19__docformat__ = "google" 20 21__doc__ = """ 22This module defines the main class Plotter to manage objects and 3D rendering. 23 24![](https://vedo.embl.es/images/basic/multirenderers.png) 25""" 26 27__all__ = ["Plotter", "show", "close"] 28 29######################################################################################## 30class Event: 31 """ 32 This class holds the info from an event in the window, works as dictionary too. 33 """ 34 35 __slots__ = [ 36 "name", 37 "title", 38 "id", 39 "timerid", 40 "time", 41 "priority", 42 "at", 43 "object", 44 "actor", 45 "picked3d", 46 "keypress", 47 "picked2d", 48 "delta2d", 49 "angle2d", 50 "speed2d", 51 "delta3d", 52 "speed3d", 53 "isPoints", 54 "isMesh", 55 "isAssembly", 56 "isVolume", 57 "isImage", 58 "isActor2D", 59 ] 60 61 def __init__(self): 62 self.name = "event" 63 self.title = "" 64 self.id = 0 65 self.timerid = 0 66 self.time = 0 67 self.priority = 0 68 self.at = 0 69 self.object = None 70 self.actor = None 71 self.picked3d = () 72 self.keypress = "" 73 self.picked2d = () 74 self.delta2d = () 75 self.angle2d = 0 76 self.speed2d = () 77 self.delta3d = () 78 self.speed3d = 0 79 self.isPoints = False 80 self.isMesh = False 81 self.isAssembly = False 82 self.isVolume = False 83 self.isImage = False 84 self.isActor2D = False 85 86 def __getitem__(self, key): 87 return getattr(self, key) 88 89 def __setitem__(self, key, value): 90 setattr(self, key, value) 91 92 def __str__(self): 93 module = self.__class__.__module__ 94 name = self.__class__.__name__ 95 out = vedo.printc( 96 f"{module}.{name} at ({hex(id(self))})".ljust(75), 97 bold=True, invert=True, return_string=True, 98 ) 99 out += "\x1b[0m" 100 for n in self.__slots__: 101 if n == "actor": 102 continue 103 out += f"{n}".ljust(11) + ": " 104 val = str(self[n]).replace("\n", "")[:65].rstrip() 105 if val == "True": 106 out += "\x1b[32;1m" 107 elif val == "False": 108 out += "\x1b[31;1m" 109 out += val + "\x1b[0m\n" 110 return out.rstrip() 111 112 def keys(self): 113 return self.__slots__ 114 115 116############################################################################################## 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[Self, 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 364 365 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 372 373 374######################################################################## 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) -> Self: 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) -> Self: 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) -> Self: 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) -> Self: 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 if isinstance(a, vedo.addons.BaseCutter): 955 a.add_to(self) # from cutters 956 continue 957 958 if isinstance(a, vtki.vtkLight): 959 ren.AddLight(a) 960 continue 961 962 try: 963 ren.AddActor(a) 964 except TypeError: 965 ren.AddActor(a.actor) 966 967 try: 968 ir = self.renderers.index(ren) 969 a.rendered_at.add(ir) # might not have rendered_at 970 except (AttributeError, ValueError): 971 pass 972 973 if isinstance(a, vtki.vtkFollower): 974 a.SetCamera(self.camera) 975 elif isinstance(a, vedo.visual.LightKit): 976 a.lightkit.AddLightsToRenderer(ren) 977 978 return self 979 980 def remove(self, *objs, at=None) -> Self: 981 """ 982 Remove input object to the internal list of objects to be shown. 983 984 Objects to be removed can be referenced by their assigned name, 985 986 Arguments: 987 at : (int) 988 remove the object at the specified renderer 989 """ 990 # TODO and you can also use wildcards like `*` and `?`. 991 if at is not None: 992 ren = self.renderers[at] 993 else: 994 ren = self.renderer 995 996 objs = [ob for ob in utils.flatten(objs) if ob] 997 998 has_str = False 999 for ob in objs: 1000 if isinstance(ob, str): 1001 has_str = True 1002 break 1003 1004 has_actor = False 1005 for ob in objs: 1006 if hasattr(ob, "actor") and ob.actor: 1007 has_actor = True 1008 break 1009 1010 if has_str or has_actor: 1011 # need to get the actors to search for 1012 for a in self.get_actors(include_non_pickables=True): 1013 # print("PARSING", [a]) 1014 try: 1015 if (a.name and a.name in objs) or a in objs: 1016 objs.append(a) 1017 # if a.name: 1018 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1019 # if any(bools) or a in objs: 1020 # objs.append(a) 1021 # print('a.name',a.name, objs,any(bools)) 1022 except AttributeError: # no .name 1023 # passing the actor so get back the object with .retrieve_object() 1024 try: 1025 vobj = a.retrieve_object() 1026 if (vobj.name and vobj.name in objs) or vobj in objs: 1027 # print('vobj.name', vobj.name) 1028 objs.append(vobj) 1029 except AttributeError: 1030 pass 1031 1032 ir = self.renderers.index(ren) 1033 1034 ids = [] 1035 for ob in set(objs): 1036 1037 # will remove it from internal list if possible 1038 try: 1039 idx = self.objects.index(ob) 1040 ids.append(idx) 1041 except ValueError: 1042 pass 1043 1044 if ren: ### remove it from the renderer 1045 1046 if isinstance(ob, vedo.addons.BaseCutter): 1047 ob.remove_from(self) # from cutters 1048 continue 1049 1050 try: 1051 ren.RemoveActor(ob) 1052 except TypeError: 1053 try: 1054 ren.RemoveActor(ob.actor) 1055 except AttributeError: 1056 pass 1057 1058 if hasattr(ob, "rendered_at"): 1059 ob.rendered_at.discard(ir) 1060 1061 if hasattr(ob, "scalarbar") and ob.scalarbar: 1062 ren.RemoveActor(ob.scalarbar) 1063 if hasattr(ob, "_caption") and ob._caption: 1064 ren.RemoveActor(ob._caption) 1065 if hasattr(ob, "shadows") and ob.shadows: 1066 for sha in ob.shadows: 1067 ren.RemoveActor(sha.actor) 1068 if hasattr(ob, "trail") and ob.trail: 1069 ren.RemoveActor(ob.trail.actor) 1070 ob.trail_points = [] 1071 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1072 for sha in ob.trail.shadows: 1073 ren.RemoveActor(sha.actor) 1074 1075 elif isinstance(ob, vedo.visual.LightKit): 1076 ob.lightkit.RemoveLightsFromRenderer(ren) 1077 1078 # for i in ids: # WRONG way of doing it! 1079 # del self.objects[i] 1080 # instead we do: 1081 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1082 return self 1083 1084 @property 1085 def actors(self): 1086 """Return the list of actors.""" 1087 return [ob.actor for ob in self.objects if hasattr(ob, "actor")] 1088 1089 def remove_lights(self) -> Self: 1090 """Remove all the present lights in the current renderer.""" 1091 if self.renderer: 1092 self.renderer.RemoveAllLights() 1093 return self 1094 1095 def pop(self, at=None) -> Self: 1096 """ 1097 Remove the last added object from the rendering window. 1098 This method is typically used in loops or callback functions. 1099 """ 1100 if at is not None and not isinstance(at, int): 1101 # wrong usage pitfall 1102 vedo.logger.error("argument of pop() must be an integer") 1103 raise RuntimeError() 1104 1105 if self.objects: 1106 self.remove(self.objects[-1], at) 1107 return self 1108 1109 def render(self, resetcam=False) -> Self: 1110 """Render the scene. This method is typically used in loops or callback functions.""" 1111 1112 if vedo.settings.dry_run_mode >= 2: 1113 return self 1114 1115 if not self.window: 1116 return self 1117 1118 self.initialize_interactor() 1119 1120 if resetcam: 1121 self.renderer.ResetCamera() 1122 1123 self.window.Render() 1124 1125 if self._cocoa_process_events and self.interactor.GetInitialized(): 1126 if "Darwin" in vedo.sys_platform and not self.offscreen: 1127 self.interactor.ProcessEvents() 1128 self._cocoa_process_events = False 1129 return self 1130 1131 def interactive(self) -> Self: 1132 """ 1133 Start window interaction. 1134 Analogous to `show(..., interactive=True)`. 1135 """ 1136 if vedo.settings.dry_run_mode >= 1: 1137 return self 1138 self.initialize_interactor() 1139 if self.interactor: 1140 # print("self.interactor.Start()") 1141 self.interactor.Start() 1142 # print("self.interactor.Start() done") 1143 if self._must_close_now: 1144 # print("self.interactor.TerminateApp()") 1145 if self.interactor: 1146 self.interactor.GetRenderWindow().Finalize() 1147 self.interactor.TerminateApp() 1148 self.interactor = None 1149 self.window = None 1150 self.renderer = None 1151 self.renderers = [] 1152 self.camera = None 1153 return self 1154 1155 def use_depth_peeling(self, at=None, value=True) -> Self: 1156 """ 1157 Specify whether use depth peeling algorithm at this specific renderer 1158 Call this method before the first rendering. 1159 """ 1160 if at is None: 1161 ren = self.renderer 1162 else: 1163 ren = self.renderers[at] 1164 ren.SetUseDepthPeeling(value) 1165 return self 1166 1167 def background(self, c1=None, c2=None, at=None, mode=0) -> Union[Self, "np.ndarray"]: 1168 """Set the color of the background for the current renderer. 1169 A different renderer index can be specified by keyword `at`. 1170 1171 Arguments: 1172 c1 : (list) 1173 background main color. 1174 c2 : (list) 1175 background color for the upper part of the window. 1176 at : (int) 1177 renderer index. 1178 mode : (int) 1179 background mode (needs vtk version >= 9.3) 1180 0 = vertical, 1181 1 = horizontal, 1182 2 = radial farthest side, 1183 3 = radia farthest corner. 1184 """ 1185 if not self.renderers: 1186 return self 1187 if at is None: 1188 r = self.renderer 1189 else: 1190 r = self.renderers[at] 1191 1192 if c1 is None and c2 is None: 1193 return np.array(r.GetBackground()) 1194 1195 if r: 1196 if c1 is not None: 1197 r.SetBackground(vedo.get_color(c1)) 1198 if c2 is not None: 1199 r.GradientBackgroundOn() 1200 r.SetBackground2(vedo.get_color(c2)) 1201 if mode: 1202 try: # only works with vtk>=9.3 1203 modes = [ 1204 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1205 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1206 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1207 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1208 ] 1209 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 1210 except AttributeError: 1211 pass 1212 1213 else: 1214 r.GradientBackgroundOff() 1215 return self 1216 1217 ################################################################## 1218 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1219 """ 1220 Return a list of Meshes from the specified renderer. 1221 1222 Arguments: 1223 at : (int) 1224 specify which renderer to look at. 1225 include_non_pickables : (bool) 1226 include non-pickable objects 1227 unpack_assemblies : (bool) 1228 unpack assemblies into their components 1229 """ 1230 if at is None: 1231 renderer = self.renderer 1232 at = self.renderers.index(renderer) 1233 elif isinstance(at, int): 1234 renderer = self.renderers[at] 1235 1236 has_global_axes = False 1237 if isinstance(self.axes_instances[at], vedo.Assembly): 1238 has_global_axes = True 1239 1240 if unpack_assemblies: 1241 acs = renderer.GetActors() 1242 else: 1243 acs = renderer.GetViewProps() 1244 1245 objs = [] 1246 acs.InitTraversal() 1247 for _ in range(acs.GetNumberOfItems()): 1248 1249 if unpack_assemblies: 1250 a = acs.GetNextItem() 1251 else: 1252 a = acs.GetNextProp() 1253 1254 if isinstance(a, vtki.vtkVolume): 1255 continue 1256 1257 if include_non_pickables or a.GetPickable(): 1258 if a == self.axes_instances[at]: 1259 continue 1260 if has_global_axes and a in self.axes_instances[at].actors: 1261 continue 1262 try: 1263 objs.append(a.retrieve_object()) 1264 except AttributeError: 1265 pass 1266 return objs 1267 1268 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1269 """ 1270 Return a list of Volumes from the specified renderer. 1271 1272 Arguments: 1273 at : (int) 1274 specify which renderer to look at 1275 include_non_pickables : (bool) 1276 include non-pickable objects 1277 """ 1278 if at is None: 1279 renderer = self.renderer 1280 at = self.renderers.index(renderer) 1281 elif isinstance(at, int): 1282 renderer = self.renderers[at] 1283 1284 vols = [] 1285 acs = renderer.GetVolumes() 1286 acs.InitTraversal() 1287 for _ in range(acs.GetNumberOfItems()): 1288 a = acs.GetNextItem() 1289 if include_non_pickables or a.GetPickable(): 1290 try: 1291 vols.append(a.retrieve_object()) 1292 except AttributeError: 1293 pass 1294 return vols 1295 1296 def get_actors(self, at=None, include_non_pickables=False) -> list: 1297 """ 1298 Return a list of Volumes from the specified renderer. 1299 1300 Arguments: 1301 at : (int) 1302 specify which renderer to look at 1303 include_non_pickables : (bool) 1304 include non-pickable objects 1305 """ 1306 if at is None: 1307 renderer = self.renderer 1308 at = self.renderers.index(renderer) 1309 elif isinstance(at, int): 1310 renderer = self.renderers[at] 1311 1312 acts = [] 1313 acs = renderer.GetViewProps() 1314 acs.InitTraversal() 1315 for _ in range(acs.GetNumberOfItems()): 1316 a = acs.GetNextProp() 1317 if include_non_pickables or a.GetPickable(): 1318 acts.append(a) 1319 return acts 1320 1321 def check_actors_trasform(self, at=None) -> Self: 1322 """ 1323 Reset the transformation matrix of all actors at specified renderer. 1324 This is only useful when actors have been moved/rotated/scaled manually 1325 in an already rendered scene using interactors like 1326 'TrackballActor' or 'JoystickActor'. 1327 """ 1328 # see issue https://github.com/marcomusy/vedo/issues/1046 1329 for a in self.get_actors(at=at, include_non_pickables=True): 1330 try: 1331 M = a.GetMatrix() 1332 except AttributeError: 1333 continue 1334 if M and not M.IsIdentity(): 1335 try: 1336 a.retrieve_object().apply_transform_from_actor() 1337 # vedo.logger.info( 1338 # f"object '{a.retrieve_object().name}' " 1339 # "was manually moved. Updated to its current position." 1340 # ) 1341 except AttributeError: 1342 pass 1343 return self 1344 1345 def reset_camera(self, tight=None) -> Self: 1346 """ 1347 Reset the camera position and zooming. 1348 If tight (float) is specified the zooming reserves a padding space 1349 in the xy-plane expressed in percent of the average size. 1350 """ 1351 if tight is None: 1352 self.renderer.ResetCamera() 1353 else: 1354 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1355 cam = self.camera 1356 1357 self.renderer.ComputeAspect() 1358 aspect = self.renderer.GetAspect() 1359 angle = np.pi * cam.GetViewAngle() / 180.0 1360 dx = x1 - x0 1361 dy = y1 - y0 1362 dist = max(dx / aspect[0], dy) / np.tan(angle / 2) / 2 1363 1364 cam.SetViewUp(0, 1, 0) 1365 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1366 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1367 if cam.GetParallelProjection(): 1368 ps = max(dx / aspect[0], dy) / 2 1369 cam.SetParallelScale(ps * (1 + tight)) 1370 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1371 return self 1372 1373 def reset_viewup(self, smooth=True) -> Self: 1374 """ 1375 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1376 """ 1377 vbb = addons.compute_visible_bounds()[0] 1378 x0, x1, y0, y1, z0, z1 = vbb 1379 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1380 d = self.camera.GetDistance() 1381 1382 viewups = np.array( 1383 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1384 ) 1385 positions = np.array( 1386 [ 1387 (mx, my, mz + d), 1388 (mx, my, mz - d), 1389 (mx, my + d, mz), 1390 (mx, my - d, mz), 1391 (mx + d, my, mz), 1392 (mx - d, my, mz), 1393 ] 1394 ) 1395 1396 vu = np.array(self.camera.GetViewUp()) 1397 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1398 1399 poc = np.array(self.camera.GetPosition()) 1400 foc = np.array(self.camera.GetFocalPoint()) 1401 a = poc - foc 1402 b = positions - foc 1403 a = a / np.linalg.norm(a) 1404 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1405 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1406 1407 if smooth: 1408 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1409 for t in outtimes: 1410 vv = vu * (1 - t) + viewups[vui] * t 1411 pp = poc * (1 - t) + positions[pui] * t 1412 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1413 self.camera.SetViewUp(vv) 1414 self.camera.SetPosition(pp) 1415 self.camera.SetFocalPoint(ff) 1416 self.render() 1417 1418 # interpolator does not respect parallel view...: 1419 # cam1 = dict( 1420 # pos=poc, 1421 # viewup=vu, 1422 # focal_point=(mx,my,mz), 1423 # clipping_range=self.camera.GetClippingRange() 1424 # ) 1425 # # cam1 = self.camera 1426 # cam2 = dict( 1427 # pos=positions[pui], 1428 # viewup=viewups[vui], 1429 # focal_point=(mx,my,mz), 1430 # clipping_range=self.camera.GetClippingRange() 1431 # ) 1432 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1433 # for c in vcams: 1434 # self.renderer.SetActiveCamera(c) 1435 # self.render() 1436 else: 1437 1438 self.camera.SetViewUp(viewups[vui]) 1439 self.camera.SetPosition(positions[pui]) 1440 self.camera.SetFocalPoint(mx, my, mz) 1441 1442 self.renderer.ResetCameraClippingRange() 1443 1444 # vbb, _, _, _ = addons.compute_visible_bounds() 1445 # x0,x1, y0,y1, z0,z1 = vbb 1446 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1447 self.render() 1448 return self 1449 1450 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1451 """ 1452 Takes as input two cameras set camera at an interpolated position: 1453 1454 Cameras can be vtkCamera or dictionaries in format: 1455 1456 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1457 1458 Press `shift-C` key in interactive mode to dump a python snipplet 1459 of parameters for the current camera view. 1460 """ 1461 nc = len(cameras) 1462 if len(times) == 0: 1463 times = np.linspace(0, 1, num=nc, endpoint=True) 1464 1465 assert len(times) == nc 1466 1467 cin = vtki.new("CameraInterpolator") 1468 1469 # cin.SetInterpolationTypeToLinear() # buggy? 1470 if nc > 2 and smooth: 1471 cin.SetInterpolationTypeToSpline() 1472 1473 for i, cam in enumerate(cameras): 1474 vcam = cam 1475 if isinstance(cam, dict): 1476 vcam = utils.camera_from_dict(cam) 1477 cin.AddCamera(times[i], vcam) 1478 1479 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1480 rng = maxt - mint 1481 1482 if len(output_times) == 0: 1483 cin.InterpolateCamera(t * rng, self.camera) 1484 self.renderer.SetActiveCamera(self.camera) 1485 return [self.camera] 1486 else: 1487 vcams = [] 1488 for tt in output_times: 1489 c = vtki.vtkCamera() 1490 cin.InterpolateCamera(tt * rng, c) 1491 vcams.append(c) 1492 return vcams 1493 1494 def fly_to(self, point) -> Self: 1495 """ 1496 Fly camera to the specified point. 1497 1498 Arguments: 1499 point : (list) 1500 point in space to place camera. 1501 1502 Example: 1503 ```python 1504 from vedo import * 1505 cone = Cone() 1506 plt = Plotter(axes=1) 1507 plt.show(cone) 1508 plt.fly_to([1,0,0]) 1509 plt.interactive().close() 1510 ``` 1511 """ 1512 if self.interactor: 1513 self.resetcam = False 1514 self.interactor.FlyTo(self.renderer, point) 1515 return self 1516 1517 def look_at(self, plane="xy") -> Self: 1518 """Move the camera so that it looks at the specified cartesian plane""" 1519 cam = self.renderer.GetActiveCamera() 1520 fp = np.array(cam.GetFocalPoint()) 1521 p = np.array(cam.GetPosition()) 1522 dist = np.linalg.norm(fp - p) 1523 plane = plane.lower() 1524 if "x" in plane and "y" in plane: 1525 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1526 cam.SetViewUp(0.0, 1.0, 0.0) 1527 elif "x" in plane and "z" in plane: 1528 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1529 cam.SetViewUp(0.0, 0.0, 1.0) 1530 elif "y" in plane and "z" in plane: 1531 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1532 cam.SetViewUp(0.0, 0.0, 1.0) 1533 else: 1534 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1535 return self 1536 1537 def record(self, filename="") -> str: 1538 """ 1539 Record camera, mouse, keystrokes and all other events. 1540 Recording can be toggled on/off by pressing key "R". 1541 1542 Arguments: 1543 filename : (str) 1544 ascii file to store events. 1545 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1546 1547 Returns: 1548 a string descriptor of events. 1549 1550 Examples: 1551 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1552 """ 1553 if vedo.settings.dry_run_mode >= 1: 1554 return "" 1555 if not self.interactor: 1556 vedo.logger.warning("Cannot record events, no interactor defined.") 1557 return "" 1558 erec = vtki.new("InteractorEventRecorder") 1559 erec.SetInteractor(self.interactor) 1560 if not filename: 1561 if not os.path.exists(vedo.settings.cache_directory): 1562 os.makedirs(vedo.settings.cache_directory) 1563 home_dir = os.path.expanduser("~") 1564 filename = os.path.join( 1565 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1566 print("Events will be recorded in", filename) 1567 erec.SetFileName(filename) 1568 erec.SetKeyPressActivationValue("R") 1569 erec.EnabledOn() 1570 erec.Record() 1571 self.interactor.Start() 1572 erec.Stop() 1573 erec.EnabledOff() 1574 with open(filename, "r", encoding="UTF-8") as fl: 1575 events = fl.read() 1576 erec = None 1577 return events 1578 1579 def play(self, recorded_events="", repeats=0) -> Self: 1580 """ 1581 Play camera, mouse, keystrokes and all other events. 1582 1583 Arguments: 1584 events : (str) 1585 file o string of events. 1586 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1587 repeats : (int) 1588 number of extra repeats of the same events. The default is 0. 1589 1590 Examples: 1591 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1592 """ 1593 if vedo.settings.dry_run_mode >= 1: 1594 return self 1595 if not self.interactor: 1596 vedo.logger.warning("Cannot play events, no interactor defined.") 1597 return self 1598 1599 erec = vtki.new("InteractorEventRecorder") 1600 erec.SetInteractor(self.interactor) 1601 1602 if not recorded_events: 1603 home_dir = os.path.expanduser("~") 1604 recorded_events = os.path.join( 1605 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1606 1607 if recorded_events.endswith(".log"): 1608 erec.ReadFromInputStringOff() 1609 erec.SetFileName(recorded_events) 1610 else: 1611 erec.ReadFromInputStringOn() 1612 erec.SetInputString(recorded_events) 1613 1614 erec.Play() 1615 for _ in range(repeats): 1616 erec.Rewind() 1617 erec.Play() 1618 erec.EnabledOff() 1619 erec = None 1620 return self 1621 1622 def parallel_projection(self, value=True, at=None) -> Self: 1623 """ 1624 Use parallel projection `at` a specified renderer. 1625 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1626 An input value equal to -1 will toggle it on/off. 1627 """ 1628 if at is not None: 1629 r = self.renderers[at] 1630 else: 1631 r = self.renderer 1632 if value == -1: 1633 val = r.GetActiveCamera().GetParallelProjection() 1634 value = not val 1635 r.GetActiveCamera().SetParallelProjection(value) 1636 r.Modified() 1637 return self 1638 1639 def render_hidden_lines(self, value=True) -> Self: 1640 """Remove hidden lines when in wireframe mode.""" 1641 self.renderer.SetUseHiddenLineRemoval(not value) 1642 return self 1643 1644 def fov(self, angle: float) -> Self: 1645 """ 1646 Set the field of view angle for the camera. 1647 This is the angle of the camera frustum in the horizontal direction. 1648 High values will result in a wide-angle lens (fish-eye effect), 1649 and low values will result in a telephoto lens. 1650 1651 Default value is 30 degrees. 1652 """ 1653 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1654 self.renderer.GetActiveCamera().SetViewAngle(angle) 1655 return self 1656 1657 def zoom(self, zoom: float) -> Self: 1658 """Apply a zooming factor for the current camera view""" 1659 self.renderer.GetActiveCamera().Zoom(zoom) 1660 return self 1661 1662 def azimuth(self, angle: float) -> Self: 1663 """Rotate camera around the view up vector.""" 1664 self.renderer.GetActiveCamera().Azimuth(angle) 1665 return self 1666 1667 def elevation(self, angle: float) -> Self: 1668 """Rotate the camera around the cross product of the negative 1669 of the direction of projection and the view up vector.""" 1670 self.renderer.GetActiveCamera().Elevation(angle) 1671 return self 1672 1673 def roll(self, angle: float) -> Self: 1674 """Roll the camera about the direction of projection.""" 1675 self.renderer.GetActiveCamera().Roll(angle) 1676 return self 1677 1678 def dolly(self, value: float) -> Self: 1679 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1680 self.renderer.GetActiveCamera().Dolly(value) 1681 return self 1682 1683 ################################################################## 1684 def add_slider( 1685 self, 1686 sliderfunc, 1687 xmin, 1688 xmax, 1689 value=None, 1690 pos=4, 1691 title="", 1692 font="Calco", 1693 title_size=1, 1694 c=None, 1695 alpha=1, 1696 show_value=True, 1697 delayed=False, 1698 **options, 1699 ) -> "vedo.addons.Slider2D": 1700 """ 1701 Add a `vedo.addons.Slider2D` which can call an external custom function. 1702 1703 Arguments: 1704 sliderfunc : (Callable) 1705 external function to be called by the widget 1706 xmin : (float) 1707 lower value of the slider 1708 xmax : (float) 1709 upper value 1710 value : (float) 1711 current value 1712 pos : (list, str) 1713 position corner number: horizontal [1-5] or vertical [11-15] 1714 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1715 and also by a string descriptor (eg. "bottom-left") 1716 title : (str) 1717 title text 1718 font : (str) 1719 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1720 title_size : (float) 1721 title text scale [1.0] 1722 show_value : (bool) 1723 if True current value is shown 1724 delayed : (bool) 1725 if True the callback is delayed until when the mouse button is released 1726 alpha : (float) 1727 opacity of the scalar bar texts 1728 slider_length : (float) 1729 slider length 1730 slider_width : (float) 1731 slider width 1732 end_cap_length : (float) 1733 length of the end cap 1734 end_cap_width : (float) 1735 width of the end cap 1736 tube_width : (float) 1737 width of the tube 1738 title_height : (float) 1739 width of the title 1740 tformat : (str) 1741 format of the title 1742 1743 Examples: 1744 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1745 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1746 1747 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1748 """ 1749 if c is None: # automatic black or white 1750 c = (0.8, 0.8, 0.8) 1751 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1752 c = (0.2, 0.2, 0.2) 1753 else: 1754 c = vedo.get_color(c) 1755 1756 slider2d = addons.Slider2D( 1757 sliderfunc, 1758 xmin, 1759 xmax, 1760 value, 1761 pos, 1762 title, 1763 font, 1764 title_size, 1765 c, 1766 alpha, 1767 show_value, 1768 delayed, 1769 **options, 1770 ) 1771 1772 if self.renderer: 1773 slider2d.renderer = self.renderer 1774 if self.interactor: 1775 slider2d.interactor = self.interactor 1776 slider2d.on() 1777 self.sliders.append([slider2d, sliderfunc]) 1778 return slider2d 1779 1780 def add_slider3d( 1781 self, 1782 sliderfunc, 1783 pos1, 1784 pos2, 1785 xmin, 1786 xmax, 1787 value=None, 1788 s=0.03, 1789 t=1, 1790 title="", 1791 rotation=0.0, 1792 c=None, 1793 show_value=True, 1794 ) -> "vedo.addons.Slider3D": 1795 """ 1796 Add a 3D slider widget which can call an external custom function. 1797 1798 Arguments: 1799 sliderfunc : (function) 1800 external function to be called by the widget 1801 pos1 : (list) 1802 first position 3D coordinates 1803 pos2 : (list) 1804 second position coordinates 1805 xmin : (float) 1806 lower value 1807 xmax : (float) 1808 upper value 1809 value : (float) 1810 initial value 1811 s : (float) 1812 label scaling factor 1813 t : (float) 1814 tube scaling factor 1815 title : (str) 1816 title text 1817 c : (color) 1818 slider color 1819 rotation : (float) 1820 title rotation around slider axis 1821 show_value : (bool) 1822 if True current value is shown 1823 1824 Examples: 1825 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1826 1827 ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png) 1828 """ 1829 if c is None: # automatic black or white 1830 c = (0.8, 0.8, 0.8) 1831 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1832 c = (0.2, 0.2, 0.2) 1833 else: 1834 c = vedo.get_color(c) 1835 1836 slider3d = addons.Slider3D( 1837 sliderfunc, 1838 pos1, 1839 pos2, 1840 xmin, 1841 xmax, 1842 value, 1843 s, 1844 t, 1845 title, 1846 rotation, 1847 c, 1848 show_value, 1849 ) 1850 slider3d.renderer = self.renderer 1851 slider3d.interactor = self.interactor 1852 slider3d.on() 1853 self.sliders.append([slider3d, sliderfunc]) 1854 return slider3d 1855 1856 def add_button( 1857 self, 1858 fnc=None, 1859 states=("On", "Off"), 1860 c=("w", "w"), 1861 bc=("green4", "red4"), 1862 pos=(0.7, 0.1), 1863 size=24, 1864 font="Courier", 1865 bold=True, 1866 italic=False, 1867 alpha=1, 1868 angle=0, 1869 ) -> Union["vedo.addons.Button", None]: 1870 """ 1871 Add a button to the renderer window. 1872 1873 Arguments: 1874 states : (list) 1875 a list of possible states, e.g. ['On', 'Off'] 1876 c : (list) 1877 a list of colors for each state 1878 bc : (list) 1879 a list of background colors for each state 1880 pos : (list) 1881 2D position from left-bottom corner 1882 size : (float) 1883 size of button font 1884 font : (str) 1885 font type. Check [available fonts here](https://vedo.embl.es/fonts). 1886 bold : (bool) 1887 bold font face (False) 1888 italic : (bool) 1889 italic font face (False) 1890 alpha : (float) 1891 opacity level 1892 angle : (float) 1893 anticlockwise rotation in degrees 1894 1895 Returns: 1896 `vedo.addons.Button` object. 1897 1898 Examples: 1899 - [buttons1.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons1.py) 1900 - [buttons2.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons2.py) 1901 1902 ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg) 1903 """ 1904 if self.interactor: 1905 bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle) 1906 self.renderer.AddActor2D(bu) 1907 self.buttons.append(bu) 1908 # bu.function_id = self.add_callback("LeftButtonPress", bu.function) # not good 1909 bu.function_id = bu.add_observer("pick", bu.function, priority=10) 1910 return bu 1911 return None 1912 1913 def add_spline_tool( 1914 self, points, pc="k", ps=8, lc="r4", ac="g5", 1915 lw=2, alpha=1, closed=False, ontop=True, can_add_nodes=True, 1916 ) -> "vedo.addons.SplineTool": 1917 """ 1918 Add a spline tool to the current plotter. 1919 Nodes of the spline can be dragged in space with the mouse. 1920 Clicking on the line itself adds an extra point. 1921 Selecting a point and pressing del removes it. 1922 1923 Arguments: 1924 points : (Mesh, Points, array) 1925 the set of vertices forming the spline nodes. 1926 pc : (str) 1927 point color. The default is 'k'. 1928 ps : (str) 1929 point size. The default is 8. 1930 lc : (str) 1931 line color. The default is 'r4'. 1932 ac : (str) 1933 active point marker color. The default is 'g5'. 1934 lw : (int) 1935 line width. The default is 2. 1936 alpha : (float) 1937 line transparency. 1938 closed : (bool) 1939 spline is meant to be closed. The default is False. 1940 1941 Returns: 1942 a `SplineTool` object. 1943 1944 Examples: 1945 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1946 1947 ![](https://vedo.embl.es/images/basic/spline_tool.png) 1948 """ 1949 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1950 sw.interactor = self.interactor 1951 sw.on() 1952 sw.Initialize(sw.points.dataset) 1953 sw.representation.SetRenderer(self.renderer) 1954 sw.representation.SetClosedLoop(closed) 1955 sw.representation.BuildRepresentation() 1956 self.widgets.append(sw) 1957 return sw 1958 1959 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1960 """Add an inset icon mesh into the same renderer. 1961 1962 Arguments: 1963 pos : (int, list) 1964 icon position in the range [1-4] indicating one of the 4 corners, 1965 or it can be a tuple (x,y) as a fraction of the renderer size. 1966 size : (float) 1967 size of the square inset. 1968 1969 Examples: 1970 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1971 """ 1972 iconw = addons.Icon(icon, pos, size) 1973 1974 iconw.SetInteractor(self.interactor) 1975 iconw.EnabledOn() 1976 iconw.InteractiveOff() 1977 self.widgets.append(iconw) 1978 return iconw 1979 1980 def add_global_axes(self, axtype=None, c=None) -> Self: 1981 """Draw axes on scene. Available axes types: 1982 1983 Arguments: 1984 axtype : (int) 1985 - 0, no axes, 1986 - 1, draw three gray grid walls 1987 - 2, show cartesian axes from (0,0,0) 1988 - 3, show positive range of cartesian axes from (0,0,0) 1989 - 4, show a triad at bottom left 1990 - 5, show a cube at bottom left 1991 - 6, mark the corners of the bounding box 1992 - 7, draw a 3D ruler at each side of the cartesian axes 1993 - 8, show the vtkCubeAxesActor object 1994 - 9, show the bounding box outLine 1995 - 10, show three circles representing the maximum bounding box 1996 - 11, show a large grid on the x-y plane 1997 - 12, show polar axes 1998 - 13, draw a simple ruler at the bottom of the window 1999 2000 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 2001 2002 Example: 2003 ```python 2004 from vedo import Box, show 2005 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 2006 show( 2007 b, 2008 axes={ 2009 "xtitle": "Some long variable [a.u.]", 2010 "number_of_divisions": 4, 2011 # ... 2012 }, 2013 ) 2014 ``` 2015 2016 Examples: 2017 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2018 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2019 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2020 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2021 2022 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2023 """ 2024 addons.add_global_axes(axtype, c) 2025 return self 2026 2027 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2028 """Add a legend to the top right. 2029 2030 Examples: 2031 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 2032 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 2033 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 2034 """ 2035 acts = self.get_meshes() 2036 lb = addons.LegendBox(acts, **kwargs) 2037 self.add(lb) 2038 return lb 2039 2040 def add_hint( 2041 self, 2042 obj, 2043 text="", 2044 c="k", 2045 bg="yellow9", 2046 font="Calco", 2047 size=18, 2048 justify=0, 2049 angle=0, 2050 delay=250, 2051 ) -> Union[vtki.vtkBalloonWidget, None]: 2052 """ 2053 Create a pop-up hint style message when hovering an object. 2054 Use `add_hint(obj, False)` to disable a hinting a specific object. 2055 Use `add_hint(None)` to disable all hints. 2056 2057 Arguments: 2058 obj : (Mesh, Points) 2059 the object to associate the pop-up to 2060 text : (str) 2061 string description of the pop-up 2062 delay : (int) 2063 milliseconds to wait before pop-up occurs 2064 """ 2065 if self.offscreen or not self.interactor: 2066 return None 2067 2068 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2069 # Linux vtk9.0 is bugged 2070 vedo.logger.warning( 2071 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2072 ) 2073 return None 2074 2075 if obj is None: 2076 self.hint_widget.EnabledOff() 2077 self.hint_widget.SetInteractor(None) 2078 self.hint_widget = None 2079 return self.hint_widget 2080 2081 if text is False and self.hint_widget: 2082 self.hint_widget.RemoveBalloon(obj) 2083 return self.hint_widget 2084 2085 if text == "": 2086 if obj.name: 2087 text = obj.name 2088 elif obj.filename: 2089 text = obj.filename 2090 else: 2091 return None 2092 2093 if not self.hint_widget: 2094 self.hint_widget = vtki.vtkBalloonWidget() 2095 2096 rep = self.hint_widget.GetRepresentation() 2097 rep.SetBalloonLayoutToImageRight() 2098 2099 trep = rep.GetTextProperty() 2100 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2101 trep.SetFontFile(utils.get_font_path(font)) 2102 trep.SetFontSize(size) 2103 trep.SetColor(vedo.get_color(c)) 2104 trep.SetBackgroundColor(vedo.get_color(bg)) 2105 trep.SetShadow(0) 2106 trep.SetJustification(justify) 2107 trep.UseTightBoundingBoxOn() 2108 2109 self.hint_widget.ManagesCursorOff() 2110 self.hint_widget.SetTimerDuration(delay) 2111 self.hint_widget.SetInteractor(self.interactor) 2112 if angle: 2113 trep.SetOrientation(angle) 2114 trep.SetBackgroundOpacity(0) 2115 # else: 2116 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2117 self.hint_widget.SetRepresentation(rep) 2118 self.widgets.append(self.hint_widget) 2119 self.hint_widget.EnabledOn() 2120 2121 bst = self.hint_widget.GetBalloonString(obj.actor) 2122 if bst: 2123 self.hint_widget.UpdateBalloonString(obj.actor, text) 2124 else: 2125 self.hint_widget.AddBalloon(obj.actor, text) 2126 2127 return self.hint_widget 2128 2129 def add_shadows(self) -> Self: 2130 """Add shadows at the current renderer.""" 2131 if self.renderer: 2132 shadows = vtki.new("ShadowMapPass") 2133 seq = vtki.new("SequencePass") 2134 passes = vtki.new("RenderPassCollection") 2135 passes.AddItem(shadows.GetShadowMapBakerPass()) 2136 passes.AddItem(shadows) 2137 seq.SetPasses(passes) 2138 camerapass = vtki.new("CameraPass") 2139 camerapass.SetDelegatePass(seq) 2140 self.renderer.SetPass(camerapass) 2141 return self 2142 2143 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> Self: 2144 """ 2145 Screen Space Ambient Occlusion. 2146 2147 For every pixel on the screen, the pixel shader samples the depth values around 2148 the current pixel and tries to compute the amount of occlusion from each of the sampled 2149 points. 2150 2151 Arguments: 2152 radius : (float) 2153 radius of influence in absolute units 2154 bias : (float) 2155 bias of the normals 2156 blur : (bool) 2157 add a blurring to the sampled positions 2158 samples : (int) 2159 number of samples to probe 2160 2161 Examples: 2162 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2163 2164 ![](https://vedo.embl.es/images/basic/ssao.jpg) 2165 """ 2166 lights = vtki.new("LightsPass") 2167 2168 opaque = vtki.new("OpaquePass") 2169 2170 ssaoCam = vtki.new("CameraPass") 2171 ssaoCam.SetDelegatePass(opaque) 2172 2173 ssao = vtki.new("SSAOPass") 2174 ssao.SetRadius(radius) 2175 ssao.SetBias(bias) 2176 ssao.SetBlur(blur) 2177 ssao.SetKernelSize(samples) 2178 ssao.SetDelegatePass(ssaoCam) 2179 2180 translucent = vtki.new("TranslucentPass") 2181 2182 volpass = vtki.new("VolumetricPass") 2183 ddp = vtki.new("DualDepthPeelingPass") 2184 ddp.SetTranslucentPass(translucent) 2185 ddp.SetVolumetricPass(volpass) 2186 2187 over = vtki.new("OverlayPass") 2188 2189 collection = vtki.new("RenderPassCollection") 2190 collection.AddItem(lights) 2191 collection.AddItem(ssao) 2192 collection.AddItem(ddp) 2193 collection.AddItem(over) 2194 2195 sequence = vtki.new("SequencePass") 2196 sequence.SetPasses(collection) 2197 2198 cam = vtki.new("CameraPass") 2199 cam.SetDelegatePass(sequence) 2200 2201 self.renderer.SetPass(cam) 2202 return self 2203 2204 def add_depth_of_field(self, autofocus=True) -> Self: 2205 """Add a depth of field effect in the scene.""" 2206 lights = vtki.new("LightsPass") 2207 2208 opaque = vtki.new("OpaquePass") 2209 2210 dofCam = vtki.new("CameraPass") 2211 dofCam.SetDelegatePass(opaque) 2212 2213 dof = vtki.new("DepthOfFieldPass") 2214 dof.SetAutomaticFocalDistance(autofocus) 2215 dof.SetDelegatePass(dofCam) 2216 2217 collection = vtki.new("RenderPassCollection") 2218 collection.AddItem(lights) 2219 collection.AddItem(dof) 2220 2221 sequence = vtki.new("SequencePass") 2222 sequence.SetPasses(collection) 2223 2224 cam = vtki.new("CameraPass") 2225 cam.SetDelegatePass(sequence) 2226 2227 self.renderer.SetPass(cam) 2228 return self 2229 2230 def _add_skybox(self, hdrfile: str) -> Self: 2231 # many hdr files are at https://polyhaven.com/all 2232 2233 reader = vtki.new("HDRReader") 2234 # Check the image can be read. 2235 if not reader.CanReadFile(hdrfile): 2236 vedo.logger.error(f"Cannot read HDR file {hdrfile}") 2237 return self 2238 reader.SetFileName(hdrfile) 2239 reader.Update() 2240 2241 texture = vtki.vtkTexture() 2242 texture.SetColorModeToDirectScalars() 2243 texture.SetInputData(reader.GetOutput()) 2244 2245 # Convert to a cube map 2246 tcm = vtki.new("EquirectangularToCubeMapTexture") 2247 tcm.SetInputTexture(texture) 2248 # Enable mipmapping to handle HDR image 2249 tcm.MipmapOn() 2250 tcm.InterpolateOn() 2251 2252 self.renderer.SetEnvironmentTexture(tcm) 2253 self.renderer.UseImageBasedLightingOn() 2254 self.skybox = vtki.new("Skybox") 2255 self.skybox.SetTexture(tcm) 2256 self.renderer.AddActor(self.skybox) 2257 return self 2258 2259 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None) -> "vedo.addons.RendererFrame": 2260 """ 2261 Add a frame to the renderer subwindow. 2262 2263 Arguments: 2264 c : (color) 2265 color name or index 2266 alpha : (float) 2267 opacity level 2268 lw : (int) 2269 line width in pixels. 2270 padding : (float) 2271 padding space in pixels. 2272 """ 2273 if c is None: # automatic black or white 2274 c = (0.9, 0.9, 0.9) 2275 if self.renderer: 2276 if np.sum(self.renderer.GetBackground()) > 1.5: 2277 c = (0.1, 0.1, 0.1) 2278 renf = addons.RendererFrame(c, alpha, lw, padding) 2279 if renf: 2280 self.renderer.AddActor(renf) 2281 return renf 2282 2283 def add_hover_legend( 2284 self, 2285 at=None, 2286 c=None, 2287 pos="bottom-left", 2288 font="Calco", 2289 s=0.75, 2290 bg="auto", 2291 alpha=0.1, 2292 maxlength=24, 2293 use_info=False, 2294 ) -> int: 2295 """ 2296 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2297 2298 The created text object are stored in `plotter.hover_legends`. 2299 2300 Returns: 2301 the id of the callback function. 2302 2303 Arguments: 2304 c : (color) 2305 Text color. If None then black or white is chosen automatically 2306 pos : (str) 2307 text positioning 2308 font : (str) 2309 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2310 s : (float) 2311 text size scale 2312 bg : (color) 2313 background color of the 2D box containing the text 2314 alpha : (float) 2315 box transparency 2316 maxlength : (int) 2317 maximum number of characters per line 2318 use_info : (bool) 2319 visualize the content of the `obj.info` attribute 2320 2321 Examples: 2322 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2323 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2324 2325 ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg) 2326 """ 2327 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2328 2329 if at is None: 2330 at = self.renderers.index(self.renderer) 2331 2332 def _legfunc(evt): 2333 if not evt.object or not self.renderer or at != evt.at: 2334 if hoverlegend.mapper.GetInput(): # clear and return 2335 hoverlegend.mapper.SetInput("") 2336 self.render() 2337 return 2338 2339 if use_info: 2340 if hasattr(evt.object, "info"): 2341 t = str(evt.object.info) 2342 else: 2343 return 2344 else: 2345 t, tp = "", "" 2346 if evt.isMesh: 2347 tp = "Mesh " 2348 elif evt.isPoints: 2349 tp = "Points " 2350 elif evt.isVolume: 2351 tp = "Volume " 2352 elif evt.isImage: 2353 tp = "Image " 2354 elif evt.isAssembly: 2355 tp = "Assembly " 2356 else: 2357 return 2358 2359 if evt.isAssembly: 2360 if not evt.object.name: 2361 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2362 else: 2363 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2364 else: 2365 if evt.object.name: 2366 t += f"{tp}name" 2367 if evt.isPoints: 2368 t += " " 2369 if evt.isMesh: 2370 t += " " 2371 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2372 2373 if evt.object.filename: 2374 t += f"{tp}filename: " 2375 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2376 t += "\n" 2377 if not evt.object.file_size: 2378 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2379 if evt.object.file_size: 2380 t += " : " 2381 sz, created = evt.object.file_size, evt.object.created 2382 t += f"{created[4:-5]} ({sz})" + "\n" 2383 2384 if evt.isPoints: 2385 indata = evt.object.dataset 2386 if indata.GetNumberOfPoints(): 2387 t += ( 2388 f"#points/cells: {indata.GetNumberOfPoints()}" 2389 f" / {indata.GetNumberOfCells()}" 2390 ) 2391 pdata = indata.GetPointData() 2392 cdata = indata.GetCellData() 2393 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2394 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2395 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2396 t += " *" 2397 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2398 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2399 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2400 t += " *" 2401 2402 if evt.isImage: 2403 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2404 t += f"\nImage shape: {evt.object.shape}" 2405 pcol = self.color_picker(evt.picked2d) 2406 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2407 2408 # change box color if needed in 'auto' mode 2409 if evt.isPoints and "auto" in str(bg): 2410 actcol = evt.object.properties.GetColor() 2411 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2412 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2413 2414 # adapt to changes in bg color 2415 bgcol = self.renderers[at].GetBackground() 2416 _bgcol = c 2417 if _bgcol is None: # automatic black or white 2418 _bgcol = (0.9, 0.9, 0.9) 2419 if sum(bgcol) > 1.5: 2420 _bgcol = (0.1, 0.1, 0.1) 2421 if len(set(_bgcol).intersection(bgcol)) < 3: 2422 hoverlegend.color(_bgcol) 2423 2424 if hoverlegend.mapper.GetInput() != t: 2425 hoverlegend.mapper.SetInput(t) 2426 self.interactor.Render() 2427 2428 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2429 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2430 2431 self.add(hoverlegend, at=at) 2432 self.hover_legends.append(hoverlegend) 2433 idcall = self.add_callback("MouseMove", _legfunc) 2434 return idcall 2435 2436 def add_scale_indicator( 2437 self, 2438 pos=(0.7, 0.05), 2439 s=0.02, 2440 length=2, 2441 lw=4, 2442 c="k1", 2443 alpha=1, 2444 units="", 2445 gap=0.05, 2446 ) -> Union["vedo.visual.Actor2D", None]: 2447 """ 2448 Add a Scale Indicator. Only works in parallel mode (no perspective). 2449 2450 Arguments: 2451 pos : (list) 2452 fractional (x,y) position on the screen. 2453 s : (float) 2454 size of the text. 2455 length : (float) 2456 length of the line. 2457 units : (str) 2458 string to show units. 2459 gap : (float) 2460 separation of line and text. 2461 2462 Example: 2463 ```python 2464 from vedo import settings, Cube, Plotter 2465 settings.use_parallel_projection = True # or else it does not make sense! 2466 cube = Cube().alpha(0.2) 2467 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2468 plt.add_scale_indicator(units='um', c='blue4') 2469 plt.show(cube, "Scale indicator with units").close() 2470 ``` 2471 ![](https://vedo.embl.es/images/feats/scale_indicator.png) 2472 """ 2473 # Note that this cannot go in addons.py 2474 # because it needs callbacks and window size 2475 if not self.interactor: 2476 return None 2477 2478 ppoints = vtki.vtkPoints() # Generate the polyline 2479 psqr = [[0.0, gap], [length / 10, gap]] 2480 dd = psqr[1][0] - psqr[0][0] 2481 for i, pt in enumerate(psqr): 2482 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2483 lines = vtki.vtkCellArray() 2484 lines.InsertNextCell(len(psqr)) 2485 for i in range(len(psqr)): 2486 lines.InsertCellPoint(i) 2487 pd = vtki.vtkPolyData() 2488 pd.SetPoints(ppoints) 2489 pd.SetLines(lines) 2490 2491 wsx, wsy = self.window.GetSize() 2492 if not self.camera.GetParallelProjection(): 2493 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2494 return None 2495 2496 rlabel = vtki.new("VectorText") 2497 rlabel.SetText("scale") 2498 tf = vtki.new("TransformPolyDataFilter") 2499 tf.SetInputConnection(rlabel.GetOutputPort()) 2500 t = vtki.vtkTransform() 2501 t.Scale(s * wsy / wsx, s, 1) 2502 tf.SetTransform(t) 2503 2504 app = vtki.new("AppendPolyData") 2505 app.AddInputConnection(tf.GetOutputPort()) 2506 app.AddInputData(pd) 2507 2508 mapper = vtki.new("PolyDataMapper2D") 2509 mapper.SetInputConnection(app.GetOutputPort()) 2510 cs = vtki.vtkCoordinate() 2511 cs.SetCoordinateSystem(1) 2512 mapper.SetTransformCoordinate(cs) 2513 2514 fractor = vedo.visual.Actor2D() 2515 csys = fractor.GetPositionCoordinate() 2516 csys.SetCoordinateSystem(3) 2517 fractor.SetPosition(pos) 2518 fractor.SetMapper(mapper) 2519 fractor.GetProperty().SetColor(vedo.get_color(c)) 2520 fractor.GetProperty().SetOpacity(alpha) 2521 fractor.GetProperty().SetLineWidth(lw) 2522 fractor.GetProperty().SetDisplayLocationToForeground() 2523 2524 def sifunc(iren, ev): 2525 wsx, wsy = self.window.GetSize() 2526 ps = self.camera.GetParallelScale() 2527 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2528 if units: 2529 newtxt += " " + units 2530 if rlabel.GetText() != newtxt: 2531 rlabel.SetText(newtxt) 2532 2533 self.renderer.AddActor(fractor) 2534 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2535 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2536 self.interactor.AddObserver("InteractionEvent", sifunc) 2537 sifunc(0, 0) 2538 return fractor 2539 2540 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2541 """ 2542 Create an Event object with information of what was clicked. 2543 2544 If `enable_picking` is False, no picking will be performed. 2545 This can be useful to avoid double picking when using buttons. 2546 """ 2547 if not self.interactor: 2548 return Event() 2549 2550 if len(pos) > 0: 2551 x, y = pos 2552 self.interactor.SetEventPosition(pos) 2553 else: 2554 x, y = self.interactor.GetEventPosition() 2555 self.renderer = self.interactor.FindPokedRenderer(x, y) 2556 2557 self.picked2d = (x, y) 2558 2559 key = self.interactor.GetKeySym() 2560 2561 if key: 2562 if "_L" in key or "_R" in key: 2563 # skip things like Shift_R 2564 key = "" # better than None 2565 else: 2566 if self.interactor.GetShiftKey(): 2567 key = key.upper() 2568 2569 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2570 key = "underscore" 2571 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2572 key = "plus" 2573 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2574 key = "?" 2575 2576 if self.interactor.GetControlKey(): 2577 key = "Ctrl+" + key 2578 2579 if self.interactor.GetAltKey(): 2580 key = "Alt+" + key 2581 2582 if enable_picking: 2583 if not self.picker: 2584 self.picker = vtki.vtkPropPicker() 2585 2586 self.picker.PickProp(x, y, self.renderer) 2587 actor = self.picker.GetProp3D() 2588 # Note that GetProp3D already picks Assembly 2589 2590 xp, yp = self.interactor.GetLastEventPosition() 2591 dx, dy = x - xp, y - yp 2592 2593 delta3d = np.array([0, 0, 0]) 2594 2595 if actor: 2596 picked3d = np.array(self.picker.GetPickPosition()) 2597 2598 try: 2599 vobj = actor.retrieve_object() 2600 old_pt = np.asarray(vobj.picked3d) 2601 vobj.picked3d = picked3d 2602 delta3d = picked3d - old_pt 2603 except (AttributeError, TypeError): 2604 pass 2605 2606 else: 2607 picked3d = None 2608 2609 if not actor: # try 2D 2610 actor = self.picker.GetActor2D() 2611 2612 event = Event() 2613 event.name = ename 2614 event.title = self.title 2615 event.id = -1 # will be set by the timer wrapper function 2616 event.timerid = -1 # will be set by the timer wrapper function 2617 event.priority = -1 # will be set by the timer wrapper function 2618 event.time = time.time() 2619 event.at = self.renderers.index(self.renderer) 2620 event.keypress = key 2621 if enable_picking: 2622 try: 2623 event.object = actor.retrieve_object() 2624 except AttributeError: 2625 event.object = actor 2626 try: 2627 event.actor = actor.retrieve_object() # obsolete use object instead 2628 except AttributeError: 2629 event.actor = actor 2630 event.picked3d = picked3d 2631 event.picked2d = (x, y) 2632 event.delta2d = (dx, dy) 2633 event.angle2d = np.arctan2(dy, dx) 2634 event.speed2d = np.sqrt(dx * dx + dy * dy) 2635 event.delta3d = delta3d 2636 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2637 event.isPoints = isinstance(event.object, vedo.Points) 2638 event.isMesh = isinstance(event.object, vedo.Mesh) 2639 event.isAssembly = isinstance(event.object, vedo.Assembly) 2640 event.isVolume = isinstance(event.object, vedo.Volume) 2641 event.isImage = isinstance(event.object, vedo.Image) 2642 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2643 return event 2644 2645 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2646 """ 2647 Add a function to be executed while show() is active. 2648 2649 Return a unique id for the callback. 2650 2651 The callback function (see example below) exposes a dictionary 2652 with the following information: 2653 - `name`: event name, 2654 - `id`: event unique identifier, 2655 - `priority`: event priority (float), 2656 - `interactor`: the interactor object, 2657 - `at`: renderer nr. where the event occurred 2658 - `keypress`: key pressed as string 2659 - `actor`: object picked by the mouse 2660 - `picked3d`: point picked in world coordinates 2661 - `picked2d`: screen coords of the mouse pointer 2662 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2663 - `delta3d`: ...same but in 3D world coords 2664 - `angle2d`: angle of mouse movement on screen 2665 - `speed2d`: speed of mouse movement on screen 2666 - `speed3d`: speed of picked point in world coordinates 2667 - `isPoints`: True if of class 2668 - `isMesh`: True if of class 2669 - `isAssembly`: True if of class 2670 - `isVolume`: True if of class Volume 2671 - `isImage`: True if of class 2672 2673 If `enable_picking` is False, no picking will be performed. 2674 This can be useful to avoid double picking when using buttons. 2675 2676 Frequently used events are: 2677 - `KeyPress`, `KeyRelease`: listen to keyboard events 2678 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2679 - `MiddleButtonPress`, `MiddleButtonRelease` 2680 - `RightButtonPress`, `RightButtonRelease` 2681 - `MouseMove`: listen to mouse pointer changing position 2682 - `MouseWheelForward`, `MouseWheelBackward` 2683 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2684 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2685 - `ResetCamera`, `ResetCameraClippingRange` 2686 - `Error`, `Warning` 2687 - `Char` 2688 - `Timer` 2689 2690 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2691 2692 Example: 2693 ```python 2694 from vedo import * 2695 2696 def func(evt): 2697 # this function is called every time the mouse moves 2698 # (evt is a dotted dictionary) 2699 if not evt.object: 2700 return # no hit, return 2701 print("point coords =", evt.picked3d) 2702 # print(evt) # full event dump 2703 2704 elli = Ellipsoid() 2705 plt = Plotter(axes=1) 2706 plt.add_callback('mouse hovering', func) 2707 plt.show(elli).close() 2708 ``` 2709 2710 Examples: 2711 - [spline_draw.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw.py) 2712 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2713 2714 ![](https://vedo.embl.es/images/advanced/spline_draw.png) 2715 2716 - ..and many others! 2717 """ 2718 from vtkmodules.util.misc import calldata_type 2719 2720 if not self.interactor: 2721 return 0 2722 2723 if vedo.settings.dry_run_mode >= 1: 2724 return 0 2725 2726 ######################################### 2727 @calldata_type(vtki.VTK_INT) 2728 def _func_wrap(iren, ename, timerid=None): 2729 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2730 event.timerid = timerid 2731 event.id = cid 2732 event.priority = priority 2733 self.last_event = event 2734 func(event) 2735 2736 ######################################### 2737 2738 event_name = utils.get_vtk_name_event(event_name) 2739 2740 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2741 # print(f"Registering event: {event_name} with id={cid}") 2742 return cid 2743 2744 def remove_callback(self, cid: Union[int, str]) -> Self: 2745 """ 2746 Remove a callback function by its id 2747 or a whole category of callbacks by their name. 2748 2749 Arguments: 2750 cid : (int, str) 2751 Unique id of the callback. 2752 If an event name is passed all callbacks of that type are removed. 2753 """ 2754 if self.interactor: 2755 if isinstance(cid, str): 2756 cid = utils.get_vtk_name_event(cid) 2757 self.interactor.RemoveObservers(cid) 2758 else: 2759 self.interactor.RemoveObserver(cid) 2760 return self 2761 2762 def remove_all_observers(self) -> Self: 2763 """ 2764 Remove all observers. 2765 2766 Example: 2767 ```python 2768 from vedo import * 2769 2770 def kfunc(event): 2771 print("Key pressed:", event.keypress) 2772 if event.keypress == 'q': 2773 plt.close() 2774 2775 def rfunc(event): 2776 if event.isImage: 2777 printc("Right-clicked!", event) 2778 plt.render() 2779 2780 img = Image(dataurl+"images/embryo.jpg") 2781 2782 plt = Plotter(size=(1050, 600)) 2783 plt.parallel_projection(True) 2784 plt.remove_all_observers() 2785 plt.add_callback("key press", kfunc) 2786 plt.add_callback("mouse right click", rfunc) 2787 plt.show("Right-Click Me! Press q to exit.", img) 2788 plt.close() 2789 ``` 2790 """ 2791 if self.interactor: 2792 self.interactor.RemoveAllObservers() 2793 return self 2794 2795 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2796 """ 2797 Start or stop an existing timer. 2798 2799 Arguments: 2800 action : (str) 2801 Either "create"/"start" or "destroy"/"stop" 2802 timer_id : (int) 2803 When stopping the timer, the ID of the timer as returned when created 2804 dt : (int) 2805 time in milliseconds between each repeated call 2806 one_shot : (bool) 2807 create a one shot timer of prescribed duration instead of a repeating one 2808 2809 Examples: 2810 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2811 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2812 2813 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 2814 """ 2815 if action in ("create", "start"): 2816 if timer_id is not None: 2817 vedo.logger.warning("you set a timer_id but it will be ignored.") 2818 if one_shot: 2819 timer_id = self.interactor.CreateOneShotTimer(dt) 2820 else: 2821 timer_id = self.interactor.CreateRepeatingTimer(dt) 2822 return timer_id 2823 2824 elif action in ("destroy", "stop"): 2825 if timer_id is not None: 2826 self.interactor.DestroyTimer(timer_id) 2827 else: 2828 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2829 else: 2830 e = f"in timer_callback(). Cannot understand action: {action}\n" 2831 e += " allowed actions are: ['start', 'stop']. Skipped." 2832 vedo.logger.error(e) 2833 return timer_id 2834 2835 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2836 """ 2837 Add a callback function that will be called when an event occurs. 2838 Consider using `add_callback()` instead. 2839 """ 2840 if not self.interactor: 2841 return -1 2842 event_name = utils.get_vtk_name_event(event_name) 2843 idd = self.interactor.AddObserver(event_name, func, priority) 2844 return idd 2845 2846 def compute_world_coordinate( 2847 self, 2848 pos2d: MutableSequence[float], 2849 at=None, 2850 objs=(), 2851 bounds=(), 2852 offset=None, 2853 pixeltol=None, 2854 worldtol=None, 2855 ) -> np.ndarray: 2856 """ 2857 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2858 If a set of meshes is passed then points are placed onto these. 2859 2860 Arguments: 2861 pos2d : (list) 2862 2D screen coordinates point. 2863 at : (int) 2864 renderer number. 2865 objs : (list) 2866 list of Mesh objects to project the point onto. 2867 bounds : (list) 2868 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2869 offset : (float) 2870 specify an offset value. 2871 pixeltol : (int) 2872 screen tolerance in pixels. 2873 worldtol : (float) 2874 world coordinates tolerance. 2875 2876 Returns: 2877 numpy array, the point in 3D world coordinates. 2878 2879 Examples: 2880 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2881 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2882 2883 ![](https://vedo.embl.es/images/basic/mousehover3.jpg) 2884 """ 2885 if at is not None: 2886 renderer = self.renderers[at] 2887 else: 2888 renderer = self.renderer 2889 2890 if not objs: 2891 pp = vtki.vtkFocalPlanePointPlacer() 2892 else: 2893 pps = vtki.vtkPolygonalSurfacePointPlacer() 2894 for ob in objs: 2895 pps.AddProp(ob.actor) 2896 pp = pps # type: ignore 2897 2898 if len(bounds) == 6: 2899 pp.SetPointBounds(bounds) 2900 if pixeltol: 2901 pp.SetPixelTolerance(pixeltol) 2902 if worldtol: 2903 pp.SetWorldTolerance(worldtol) 2904 if offset: 2905 pp.SetOffset(offset) 2906 2907 worldPos: MutableSequence[float] = [0, 0, 0] 2908 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2909 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2910 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2911 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2912 return np.array(worldPos) 2913 2914 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2915 """ 2916 Given a 3D points in the current renderer (or full window), 2917 find the screen pixel coordinates. 2918 2919 Example: 2920 ```python 2921 from vedo import * 2922 2923 elli = Ellipsoid().point_size(5) 2924 2925 plt = Plotter() 2926 plt.show(elli, "Press q to continue and print the info") 2927 2928 xyscreen = plt.compute_screen_coordinates(elli) 2929 print('xyscreen coords:', xyscreen) 2930 2931 # simulate an event happening at one point 2932 event = plt.fill_event(pos=xyscreen[123]) 2933 print(event) 2934 ``` 2935 """ 2936 try: 2937 obj = obj.vertices 2938 except AttributeError: 2939 pass 2940 2941 if utils.is_sequence(obj): 2942 pts = obj 2943 p2d = [] 2944 cs = vtki.vtkCoordinate() 2945 cs.SetCoordinateSystemToWorld() 2946 cs.SetViewport(self.renderer) 2947 for p in pts: 2948 cs.SetValue(p) 2949 if full_window: 2950 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2951 else: 2952 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2953 return np.array(p2d, dtype=int) 2954 2955 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2956 """ 2957 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2958 2959 Returns a frustum Mesh that contains the visible field of view. 2960 This can be used to select objects in a scene or select vertices. 2961 2962 Example: 2963 ```python 2964 from vedo import * 2965 2966 settings.enable_default_mouse_callbacks = False 2967 2968 def mode_select(objs): 2969 print("Selected objects:", objs) 2970 d0 = mode.start_x, mode.start_y # display coords 2971 d1 = mode.end_x, mode.end_y 2972 2973 frustum = plt.pick_area(d0, d1) 2974 col = np.random.randint(0, 10) 2975 infru = frustum.inside_points(mesh) 2976 infru.point_size(10).color(col) 2977 plt.add(frustum, infru).render() 2978 2979 mesh = Mesh(dataurl+"cow.vtk") 2980 mesh.color("k5").linewidth(1) 2981 2982 mode = interactor_modes.BlenderStyle() 2983 mode.callback_select = mode_select 2984 2985 plt = Plotter().user_mode(mode) 2986 plt.show(mesh, axes=1) 2987 ``` 2988 """ 2989 if at is not None: 2990 ren = self.renderers[at] 2991 else: 2992 ren = self.renderer 2993 area_picker = vtki.vtkAreaPicker() 2994 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 2995 planes = area_picker.GetFrustum() 2996 2997 fru = vtki.new("FrustumSource") 2998 fru.SetPlanes(planes) 2999 fru.ShowLinesOff() 3000 fru.Update() 3001 3002 afru = vedo.Mesh(fru.GetOutput()) 3003 afru.alpha(0.1).lw(1).pickable(False) 3004 afru.name = "Frustum" 3005 return afru 3006 3007 def _scan_input_return_acts(self, objs) -> Any: 3008 # scan the input and return a list of actors 3009 if not utils.is_sequence(objs): 3010 objs = [objs] 3011 3012 ################# 3013 wannabe_acts = [] 3014 for a in objs: 3015 3016 try: 3017 wannabe_acts.append(a.actor) 3018 except AttributeError: 3019 wannabe_acts.append(a) # already actor 3020 3021 try: 3022 wannabe_acts.append(a.scalarbar) 3023 except AttributeError: 3024 pass 3025 3026 try: 3027 for sh in a.shadows: 3028 wannabe_acts.append(sh.actor) 3029 except AttributeError: 3030 pass 3031 3032 try: 3033 wannabe_acts.append(a.trail.actor) 3034 if a.trail.shadows: # trails may also have shadows 3035 for sh in a.trail.shadows: 3036 wannabe_acts.append(sh.actor) 3037 except AttributeError: 3038 pass 3039 3040 ################# 3041 scanned_acts = [] 3042 for a in wannabe_acts: # scan content of list 3043 3044 if a is None: 3045 pass 3046 3047 elif isinstance(a, (vtki.vtkActor, vtki.vtkActor2D)): 3048 scanned_acts.append(a) 3049 3050 elif isinstance(a, str): 3051 # assume a 2D comment was given 3052 changed = False # check if one already exists so to just update text 3053 if self.renderer: # might be jupyter 3054 acs = self.renderer.GetActors2D() 3055 acs.InitTraversal() 3056 for i in range(acs.GetNumberOfItems()): 3057 act = acs.GetNextItem() 3058 if isinstance(act, vedo.shapes.Text2D): 3059 aposx, aposy = act.GetPosition() 3060 if aposx < 0.01 and aposy > 0.99: # "top-left" 3061 act.text(a) # update content! no appending nada 3062 changed = True 3063 break 3064 if not changed: 3065 out = vedo.shapes.Text2D(a) # append a new one 3066 scanned_acts.append(out) 3067 # scanned_acts.append(vedo.shapes.Text2D(a)) # naive version 3068 3069 elif isinstance(a, vtki.vtkPolyData): 3070 scanned_acts.append(vedo.Mesh(a).actor) 3071 3072 elif isinstance(a, vtki.vtkImageData): 3073 scanned_acts.append(vedo.Volume(a).actor) 3074 3075 elif isinstance(a, vedo.RectilinearGrid): 3076 scanned_acts.append(a.actor) 3077 3078 elif isinstance(a, vedo.StructuredGrid): 3079 scanned_acts.append(a.actor) 3080 3081 elif isinstance(a, vtki.vtkLight): 3082 scanned_acts.append(a) 3083 3084 elif isinstance(a, vedo.visual.LightKit): 3085 a.lightkit.AddLightsToRenderer(self.renderer) 3086 3087 elif isinstance(a, vtki.get_class("MultiBlockDataSet")): 3088 for i in range(a.GetNumberOfBlocks()): 3089 b = a.GetBlock(i) 3090 if isinstance(b, vtki.vtkPolyData): 3091 scanned_acts.append(vedo.Mesh(b).actor) 3092 elif isinstance(b, vtki.vtkImageData): 3093 scanned_acts.append(vedo.Volume(b).actor) 3094 3095 elif isinstance(a, (vtki.vtkProp, vtki.vtkInteractorObserver)): 3096 scanned_acts.append(a) 3097 3098 elif "trimesh" in str(type(a)): 3099 scanned_acts.append(utils.trimesh2vedo(a)) 3100 3101 elif "meshlab" in str(type(a)): 3102 if "MeshSet" in str(type(a)): 3103 for i in range(a.number_meshes()): 3104 if a.mesh_id_exists(i): 3105 scanned_acts.append(utils.meshlab2vedo(a.mesh(i))) 3106 else: 3107 scanned_acts.append(utils.meshlab2vedo(a)) 3108 3109 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 3110 import vedo.dolfin as vdlf 3111 3112 scanned_acts.append(vdlf.IMesh(a).actor) 3113 3114 elif "madcad" in str(type(a)): 3115 scanned_acts.append(utils.madcad2vedo(a).actor) 3116 3117 elif "TetgenIO" in str(type(a)): 3118 scanned_acts.append(vedo.TetMesh(a).shrink(0.9).c("pink7").actor) 3119 3120 elif "matplotlib.figure.Figure" in str(type(a)): 3121 scanned_acts.append(vedo.Image(a).clone2d("top-right", 0.6)) 3122 3123 else: 3124 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 3125 3126 return scanned_acts 3127 3128 def show( 3129 self, 3130 *objects, 3131 at=None, 3132 axes=None, 3133 resetcam=None, 3134 zoom=False, 3135 interactive=None, 3136 viewup="", 3137 azimuth=0.0, 3138 elevation=0.0, 3139 roll=0.0, 3140 camera=None, 3141 mode=None, 3142 rate=None, 3143 bg=None, 3144 bg2=None, 3145 size=None, 3146 title=None, 3147 screenshot="", 3148 ) -> Any: 3149 """ 3150 Render a list of objects. 3151 3152 Arguments: 3153 at : (int) 3154 number of the renderer to plot to, in case of more than one exists 3155 3156 axes : (int) 3157 axis type-1 can be fully customized by passing a dictionary. 3158 Check `addons.Axes()` for the full list of options. 3159 set the type of axes to be shown: 3160 - 0, no axes 3161 - 1, draw three gray grid walls 3162 - 2, show cartesian axes from (0,0,0) 3163 - 3, show positive range of cartesian axes from (0,0,0) 3164 - 4, show a triad at bottom left 3165 - 5, show a cube at bottom left 3166 - 6, mark the corners of the bounding box 3167 - 7, draw a 3D ruler at each side of the cartesian axes 3168 - 8, show the `vtkCubeAxesActor` object 3169 - 9, show the bounding box outLine 3170 - 10, show three circles representing the maximum bounding box 3171 - 11, show a large grid on the x-y plane 3172 - 12, show polar axes 3173 - 13, draw a simple ruler at the bottom of the window 3174 3175 azimuth/elevation/roll : (float) 3176 move camera accordingly the specified value 3177 3178 viewup: str, list 3179 either `['x', 'y', 'z']` or a vector to set vertical direction 3180 3181 resetcam : (bool) 3182 re-adjust camera position to fit objects 3183 3184 camera : (dict, vtkCamera) 3185 camera parameters can further be specified with a dictionary 3186 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3187 - pos, `(list)`, the position of the camera in world coordinates 3188 - focal_point `(list)`, the focal point of the camera in world coordinates 3189 - viewup `(list)`, the view up direction for the camera 3190 - distance `(float)`, set the focal point to the specified distance from the camera position. 3191 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3192 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3193 in world-coordinate distances. The default is 1. 3194 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3195 This method has no effect in perspective projection mode. 3196 3197 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3198 plane to be set a distance 'thickness' beyond the near clipping plane. 3199 3200 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3201 measured in degrees. The default angle is 30 degrees. 3202 This method has no effect in parallel projection mode. 3203 The formula for setting the angle up for perfect perspective viewing is: 3204 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3205 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3206 3207 interactive : (bool) 3208 pause and interact with window (True) or continue execution (False) 3209 3210 rate : (float) 3211 maximum rate of `show()` in Hertz 3212 3213 mode : (int, str) 3214 set the type of interaction: 3215 - 0 = TrackballCamera [default] 3216 - 1 = TrackballActor 3217 - 2 = JoystickCamera 3218 - 3 = JoystickActor 3219 - 4 = Flight 3220 - 5 = RubberBand2D 3221 - 6 = RubberBand3D 3222 - 7 = RubberBandZoom 3223 - 8 = Terrain 3224 - 9 = Unicam 3225 - 10 = Image 3226 - Check out `vedo.interaction_modes` for more options. 3227 3228 bg : (str, list) 3229 background color in RGB format, or string name 3230 3231 bg2 : (str, list) 3232 second background color to create a gradient background 3233 3234 size : (str, list) 3235 size of the window, e.g. size="fullscreen", or size=[600,400] 3236 3237 title : (str) 3238 window title text 3239 3240 screenshot : (str) 3241 save a screenshot of the window to file 3242 """ 3243 3244 if vedo.settings.dry_run_mode >= 2: 3245 return self 3246 3247 if self.wx_widget: 3248 return self 3249 3250 if self.renderers: # in case of notebooks 3251 3252 if at is None: 3253 at = self.renderers.index(self.renderer) 3254 3255 else: 3256 3257 if at >= len(self.renderers): 3258 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3259 vedo.logger.error(t) 3260 return self 3261 3262 self.renderer = self.renderers[at] 3263 3264 if title is not None: 3265 self.title = title 3266 3267 if size is not None: 3268 self.size = size 3269 if self.size[0] == "f": # full screen 3270 self.size = "fullscreen" 3271 self.window.SetFullScreen(True) 3272 self.window.BordersOn() 3273 else: 3274 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3275 3276 if vedo.settings.default_backend == "vtk": 3277 if str(bg).endswith(".hdr"): 3278 self._add_skybox(bg) 3279 else: 3280 if bg is not None: 3281 self.backgrcol = vedo.get_color(bg) 3282 self.renderer.SetBackground(self.backgrcol) 3283 if bg2 is not None: 3284 self.renderer.GradientBackgroundOn() 3285 self.renderer.SetBackground2(vedo.get_color(bg2)) 3286 3287 if axes is not None: 3288 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3289 objects = list(objects) 3290 objects.append(axes) # move it into the list of normal things to show 3291 axes = 0 3292 self.axes = axes 3293 3294 if interactive is not None: 3295 self._interactive = interactive 3296 if self.offscreen: 3297 self._interactive = False 3298 3299 # camera stuff 3300 if resetcam is not None: 3301 self.resetcam = resetcam 3302 3303 if camera is not None: 3304 self.resetcam = False 3305 viewup = "" 3306 if isinstance(camera, vtki.vtkCamera): 3307 cameracopy = vtki.vtkCamera() 3308 cameracopy.DeepCopy(camera) 3309 self.camera = cameracopy 3310 else: 3311 self.camera = utils.camera_from_dict(camera) 3312 3313 self.add(objects) 3314 3315 # Backend ############################################################### 3316 if vedo.settings.default_backend in ["k3d"]: 3317 return backends.get_notebook_backend(self.objects) 3318 ######################################################################### 3319 3320 for ia in utils.flatten(objects): 3321 try: 3322 # fix gray color labels and title to white or black 3323 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3324 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3325 c = (0.9, 0.9, 0.9) 3326 if np.sum(self.renderer.GetBackground()) > 1.5: 3327 c = (0.1, 0.1, 0.1) 3328 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3329 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3330 except AttributeError: 3331 pass 3332 3333 if self.sharecam: 3334 for r in self.renderers: 3335 r.SetActiveCamera(self.camera) 3336 3337 if self.axes is not None: 3338 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3339 bns = self.renderer.ComputeVisiblePropBounds() 3340 addons.add_global_axes(self.axes, bounds=bns) 3341 3342 # Backend ############################################################### 3343 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3344 return backends.get_notebook_backend() 3345 ######################################################################### 3346 3347 if self.resetcam: 3348 self.renderer.ResetCamera() 3349 3350 if len(self.renderers) > 1: 3351 self.add_renderer_frame() 3352 3353 if vedo.settings.default_backend == "2d" and not zoom: 3354 zoom = "tightest" 3355 3356 if zoom: 3357 if zoom == "tight": 3358 self.reset_camera(tight=0.04) 3359 elif zoom == "tightest": 3360 self.reset_camera(tight=0.0001) 3361 else: 3362 self.camera.Zoom(zoom) 3363 if elevation: 3364 self.camera.Elevation(elevation) 3365 if azimuth: 3366 self.camera.Azimuth(azimuth) 3367 if roll: 3368 self.camera.Roll(roll) 3369 3370 if len(viewup) > 0: 3371 b = self.renderer.ComputeVisiblePropBounds() 3372 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3373 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3374 if viewup == "x": 3375 sz = np.linalg.norm(sz) 3376 self.camera.SetViewUp([1, 0, 0]) 3377 self.camera.SetPosition(cm + sz) 3378 elif viewup == "y": 3379 sz = np.linalg.norm(sz) 3380 self.camera.SetViewUp([0, 1, 0]) 3381 self.camera.SetPosition(cm + sz) 3382 elif viewup == "z": 3383 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3384 self.camera.SetViewUp([0, 0, 1]) 3385 self.camera.SetPosition(cm + 2 * sz) 3386 elif utils.is_sequence(viewup): 3387 sz = np.linalg.norm(sz) 3388 self.camera.SetViewUp(viewup) 3389 cpos = np.cross([0, 1, 0], viewup) 3390 self.camera.SetPosition(cm - 2 * sz * cpos) 3391 3392 self.renderer.ResetCameraClippingRange() 3393 3394 self.initialize_interactor() 3395 3396 if vedo.settings.immediate_rendering: 3397 self.window.Render() ##################### <-------------- Render 3398 3399 if self.interactor: # can be offscreen or not the vtk backend.. 3400 3401 self.window.SetWindowName(self.title) 3402 3403 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3404 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3405 # print(pic.dataset)# Array 0 name PNGImage 3406 # self.window.SetIcon(pic.dataset) 3407 3408 try: 3409 # Needs "pip install pyobjc" on Mac OSX 3410 if ( 3411 self._cocoa_initialized is False 3412 and "Darwin" in vedo.sys_platform 3413 and not self.offscreen 3414 ): 3415 self._cocoa_initialized = True 3416 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3417 pid = os.getpid() 3418 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3419 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3420 except: 3421 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3422 pass 3423 3424 # Set the interaction style 3425 if mode is not None: 3426 self.user_mode(mode) 3427 if self.qt_widget and mode is None: 3428 self.user_mode(0) 3429 3430 if screenshot: 3431 self.screenshot(screenshot) 3432 3433 if self._interactive: 3434 self.interactor.Start() 3435 if self._must_close_now: 3436 self.interactor.GetRenderWindow().Finalize() 3437 self.interactor.TerminateApp() 3438 self.camera = None 3439 self.renderer = None 3440 self.renderers = [] 3441 self.window = None 3442 self.interactor = None 3443 return self 3444 3445 if rate: 3446 if self.clock is None: # set clock and limit rate 3447 self._clockt0 = time.time() 3448 self.clock = 0.0 3449 else: 3450 t = time.time() - self._clockt0 3451 elapsed = t - self.clock 3452 mint = 1.0 / rate 3453 if elapsed < mint: 3454 time.sleep(mint - elapsed) 3455 self.clock = time.time() - self._clockt0 3456 3457 # 2d #################################################################### 3458 if vedo.settings.default_backend == "2d": 3459 return backends.get_notebook_backend() 3460 ######################################################################### 3461 3462 return self 3463 3464 3465 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3466 """Add a draggable inset space into a renderer. 3467 3468 Arguments: 3469 at : (int) 3470 specify the renderer number 3471 pos : (list) 3472 icon position in the range [1-4] indicating one of the 4 corners, 3473 or it can be a tuple (x,y) as a fraction of the renderer size. 3474 size : (float) 3475 size of the square inset 3476 draggable : (bool) 3477 if True the subrenderer space can be dragged around 3478 c : (color) 3479 color of the inset frame when dragged 3480 3481 Examples: 3482 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3483 3484 ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg) 3485 """ 3486 if not self.interactor: 3487 return None 3488 3489 if not self.renderer: 3490 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3491 return None 3492 3493 options = dict(options) 3494 pos = options.pop("pos", 0) 3495 size = options.pop("size", 0.1) 3496 c = options.pop("c", "lb") 3497 at = options.pop("at", None) 3498 draggable = options.pop("draggable", True) 3499 3500 r, g, b = vedo.get_color(c) 3501 widget = vtki.vtkOrientationMarkerWidget() 3502 widget.SetOutlineColor(r, g, b) 3503 if len(objects) == 1: 3504 widget.SetOrientationMarker(objects[0].actor) 3505 else: 3506 widget.SetOrientationMarker(vedo.Assembly(objects)) 3507 3508 widget.SetInteractor(self.interactor) 3509 3510 if utils.is_sequence(pos): 3511 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3512 else: 3513 if pos < 2: 3514 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3515 elif pos == 2: 3516 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3517 elif pos == 3: 3518 widget.SetViewport(0, 0, size * 2, size * 2) 3519 elif pos == 4: 3520 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3521 widget.EnabledOn() 3522 widget.SetInteractive(draggable) 3523 if at is not None and at < len(self.renderers): 3524 widget.SetCurrentRenderer(self.renderers[at]) 3525 else: 3526 widget.SetCurrentRenderer(self.renderer) 3527 self.widgets.append(widget) 3528 return widget 3529 3530 def clear(self, at=None, deep=False) -> Self: 3531 """Clear the scene from all meshes and volumes.""" 3532 if at is not None: 3533 renderer = self.renderers[at] 3534 else: 3535 renderer = self.renderer 3536 if not renderer: 3537 return self 3538 3539 if deep: 3540 renderer.RemoveAllViewProps() 3541 else: 3542 for ob in set( 3543 self.get_meshes() 3544 + self.get_volumes() 3545 + self.objects 3546 + self.axes_instances 3547 ): 3548 if isinstance(ob, vedo.shapes.Text2D): 3549 continue 3550 self.remove(ob) 3551 try: 3552 if ob.scalarbar: 3553 self.remove(ob.scalarbar) 3554 except AttributeError: 3555 pass 3556 return self 3557 3558 def break_interaction(self) -> Self: 3559 """Break window interaction and return to the python execution flow""" 3560 if self.interactor: 3561 self.check_actors_trasform() 3562 self.interactor.ExitCallback() 3563 return self 3564 3565 def freeze(self, value=True) -> Self: 3566 """Freeze the current renderer. Use this with `sharecam=False`.""" 3567 if not self.interactor: 3568 return self 3569 if not self.renderer: 3570 return self 3571 self.renderer.SetInteractive(not value) 3572 return self 3573 3574 def user_mode(self, mode) -> Self: 3575 """ 3576 Modify the user interaction mode. 3577 3578 Examples: 3579 ```python 3580 from vedo import * 3581 mode = interactor_modes.MousePan() 3582 mesh = Mesh(dataurl+"cow.vtk") 3583 plt = Plotter().user_mode(mode) 3584 plt.show(mesh, axes=1) 3585 ``` 3586 See also: 3587 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3588 """ 3589 if not self.interactor: 3590 return self 3591 3592 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3593 # print("Current style:", curr_style) 3594 if curr_style.endswith("Actor"): 3595 self.check_actors_trasform() 3596 3597 if isinstance(mode, (str, int)): 3598 # Set the style of interaction 3599 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3600 if mode in (0, "TrackballCamera"): 3601 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3602 self.interactor.RemoveObservers("CharEvent") 3603 elif mode in (1, "TrackballActor"): 3604 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3605 elif mode in (2, "JoystickCamera"): 3606 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3607 elif mode in (3, "JoystickActor"): 3608 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3609 elif mode in (4, "Flight"): 3610 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3611 elif mode in (5, "RubberBand2D"): 3612 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3613 elif mode in (6, "RubberBand3D"): 3614 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3615 elif mode in (7, "RubberBandZoom"): 3616 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3617 elif mode in (8, "Terrain"): 3618 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3619 elif mode in (9, "Unicam"): 3620 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3621 elif mode in (10, "Image", "image", "2d"): 3622 astyle = vtki.new("InteractorStyleImage") 3623 astyle.SetInteractionModeToImage3D() 3624 self.interactor.SetInteractorStyle(astyle) 3625 else: 3626 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3627 3628 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3629 # set a custom interactor style 3630 if hasattr(mode, "interactor"): 3631 mode.interactor = self.interactor 3632 mode.renderer = self.renderer # type: ignore 3633 mode.SetInteractor(self.interactor) 3634 mode.SetDefaultRenderer(self.renderer) 3635 self.interactor.SetInteractorStyle(mode) 3636 3637 return self 3638 3639 def close(self) -> Self: 3640 """Close the plotter.""" 3641 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3642 vedo.last_figure = None 3643 self.last_event = None 3644 self.sliders = [] 3645 self.buttons = [] 3646 self.widgets = [] 3647 self.hover_legends = [] 3648 self.background_renderer = None 3649 self._extralight = None 3650 3651 self.hint_widget = None 3652 self.cutter_widget = None 3653 3654 if vedo.settings.dry_run_mode >= 2: 3655 return self 3656 3657 if not hasattr(self, "window"): 3658 return self 3659 if not self.window: 3660 return self 3661 if not hasattr(self, "interactor"): 3662 return self 3663 if not self.interactor: 3664 return self 3665 3666 ################################################### 3667 try: 3668 if "Darwin" in vedo.sys_platform: 3669 self.interactor.ProcessEvents() 3670 except: 3671 pass 3672 3673 self._must_close_now = True 3674 3675 if vedo.plotter_instance == self: 3676 vedo.plotter_instance = None 3677 3678 if self.interactor and self._interactive: 3679 self.break_interaction() 3680 elif self._must_close_now: 3681 # dont call ExitCallback here 3682 if self.interactor: 3683 self.break_interaction() 3684 self.interactor.GetRenderWindow().Finalize() 3685 self.interactor.TerminateApp() 3686 self.camera = None 3687 self.renderer = None 3688 self.renderers = [] 3689 self.window = None 3690 self.interactor = None 3691 return self 3692 3693 @property 3694 def camera(self): 3695 """Return the current active camera.""" 3696 if self.renderer: 3697 return self.renderer.GetActiveCamera() 3698 3699 @camera.setter 3700 def camera(self, cam): 3701 if self.renderer: 3702 if isinstance(cam, dict): 3703 cam = utils.camera_from_dict(cam) 3704 self.renderer.SetActiveCamera(cam) 3705 3706 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3707 """ 3708 Take a screenshot of the Plotter window. 3709 3710 Arguments: 3711 scale : (int) 3712 set image magnification as an integer multiplicating factor 3713 asarray : (bool) 3714 return a numpy array of the image instead of writing a file 3715 3716 Warning: 3717 If you get black screenshots try to set `interactive=False` in `show()` 3718 then call `screenshot()` and `plt.interactive()` afterwards. 3719 3720 Example: 3721 ```py 3722 from vedo import * 3723 sphere = Sphere().linewidth(1) 3724 plt = show(sphere, interactive=False) 3725 plt.screenshot('image.png') 3726 plt.interactive() 3727 plt.close() 3728 ``` 3729 3730 Example: 3731 ```py 3732 from vedo import * 3733 sphere = Sphere().linewidth(1) 3734 plt = show(sphere, interactive=False) 3735 plt.screenshot('anotherimage.png') 3736 plt.interactive() 3737 plt.close() 3738 ``` 3739 """ 3740 return vedo.file_io.screenshot(filename, scale, asarray) 3741 3742 def toimage(self, scale=1) -> "vedo.image.Image": 3743 """ 3744 Generate a `Image` object from the current rendering window. 3745 3746 Arguments: 3747 scale : (int) 3748 set image magnification as an integer multiplicating factor 3749 """ 3750 if vedo.settings.screeshot_large_image: 3751 w2if = vtki.new("RenderLargeImage") 3752 w2if.SetInput(self.renderer) 3753 w2if.SetMagnification(scale) 3754 else: 3755 w2if = vtki.new("WindowToImageFilter") 3756 w2if.SetInput(self.window) 3757 if hasattr(w2if, "SetScale"): 3758 w2if.SetScale(scale, scale) 3759 if vedo.settings.screenshot_transparent_background: 3760 w2if.SetInputBufferTypeToRGBA() 3761 w2if.ReadFrontBufferOff() # read from the back buffer 3762 w2if.Update() 3763 return vedo.image.Image(w2if.GetOutput()) 3764 3765 def export(self, filename="scene.npz", binary=False) -> Self: 3766 """ 3767 Export scene to file to HTML, X3D or Numpy file. 3768 3769 Examples: 3770 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3771 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3772 """ 3773 vedo.file_io.export_window(filename, binary=binary) 3774 return self 3775 3776 def color_picker(self, xy, verbose=False): 3777 """Pick color of specific (x,y) pixel on the screen.""" 3778 w2if = vtki.new("WindowToImageFilter") 3779 w2if.SetInput(self.window) 3780 w2if.ReadFrontBufferOff() 3781 w2if.Update() 3782 nx, ny = self.window.GetSize() 3783 varr = w2if.GetOutput().GetPointData().GetScalars() 3784 3785 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3786 x, y = int(xy[0]), int(xy[1]) 3787 if y < ny and x < nx: 3788 3789 rgb = arr[y, x] 3790 3791 if verbose: 3792 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3793 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3794 vedo.printc("█", c=[0, rgb[1], 0], end="") 3795 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3796 vedo.printc("] = ", end="") 3797 cnm = vedo.get_color_name(rgb) 3798 if np.sum(rgb) < 150: 3799 vedo.printc( 3800 rgb.tolist(), 3801 vedo.colors.rgb2hex(np.array(rgb) / 255), 3802 c="w", 3803 bc=rgb, 3804 invert=1, 3805 end="", 3806 ) 3807 vedo.printc(" -> " + cnm, invert=1, c="w") 3808 else: 3809 vedo.printc( 3810 rgb.tolist(), 3811 vedo.colors.rgb2hex(np.array(rgb) / 255), 3812 c=rgb, 3813 end="", 3814 ) 3815 vedo.printc(" -> " + cnm, c=cnm) 3816 3817 return rgb 3818 3819 return None 3820 3821 ####################################################################### 3822 def _default_mouseleftclick(self, iren, event) -> None: 3823 x, y = iren.GetEventPosition() 3824 renderer = iren.FindPokedRenderer(x, y) 3825 picker = vtki.vtkPropPicker() 3826 picker.PickProp(x, y, renderer) 3827 3828 self.renderer = renderer 3829 3830 clicked_actor = picker.GetActor() 3831 # clicked_actor2D = picker.GetActor2D() 3832 3833 # print('_default_mouseleftclick mouse at', x, y) 3834 # print("picked Volume:", [picker.GetVolume()]) 3835 # print("picked Actor2D:", [picker.GetActor2D()]) 3836 # print("picked Assembly:", [picker.GetAssembly()]) 3837 # print("picked Prop3D:", [picker.GetProp3D()]) 3838 3839 if not clicked_actor: 3840 clicked_actor = picker.GetAssembly() 3841 3842 if not clicked_actor: 3843 clicked_actor = picker.GetProp3D() 3844 3845 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3846 return 3847 3848 self.picked3d = picker.GetPickPosition() 3849 self.picked2d = np.array([x, y]) 3850 3851 if not clicked_actor: 3852 return 3853 3854 self.justremoved = None 3855 self.clicked_actor = clicked_actor 3856 3857 try: # might not be a vedo obj 3858 self.clicked_object = clicked_actor.retrieve_object() 3859 # save this info in the object itself 3860 self.clicked_object.picked3d = self.picked3d 3861 self.clicked_object.picked2d = self.picked2d 3862 except AttributeError: 3863 pass 3864 3865 # ----------- 3866 # if "Histogram1D" in picker.GetAssembly().__class__.__name__: 3867 # histo = picker.GetAssembly() 3868 # if histo.verbose: 3869 # x = self.picked3d[0] 3870 # idx = np.digitize(x, histo.edges) - 1 3871 # f = histo.frequencies[idx] 3872 # cn = histo.centers[idx] 3873 # vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3874 3875 ####################################################################### 3876 def _default_keypress(self, iren, event) -> None: 3877 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3878 3879 key = iren.GetKeySym() 3880 3881 if "_L" in key or "_R" in key: 3882 return 3883 3884 if iren.GetShiftKey(): 3885 key = key.upper() 3886 3887 if iren.GetControlKey(): 3888 key = "Ctrl+" + key 3889 3890 if iren.GetAltKey(): 3891 key = "Alt+" + key 3892 3893 ####################################################### 3894 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3895 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3896 # iren.GetKeyCode(), iren.GetRepeatCount()) 3897 ####################################################### 3898 3899 x, y = iren.GetEventPosition() 3900 renderer = iren.FindPokedRenderer(x, y) 3901 3902 if key in ["q", "Return"]: 3903 self.break_interaction() 3904 return 3905 3906 elif key in ["Ctrl+q", "Ctrl+w", "Escape"]: 3907 self.close() 3908 return 3909 3910 elif key == "F1": 3911 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3912 self.break_interaction() 3913 sys.exit(0) 3914 3915 elif key == "Down": 3916 if self.clicked_object and self.clicked_object in self.get_meshes(): 3917 self.clicked_object.alpha(0.02) 3918 if hasattr(self.clicked_object, "properties_backface"): 3919 bfp = self.clicked_actor.GetBackfaceProperty() 3920 self.clicked_object.properties_backface = bfp # save it 3921 self.clicked_actor.SetBackfaceProperty(None) 3922 else: 3923 for obj in self.get_meshes(): 3924 if obj: 3925 obj.alpha(0.02) 3926 bfp = obj.actor.GetBackfaceProperty() 3927 if bfp and hasattr(obj, "properties_backface"): 3928 obj.properties_backface = bfp 3929 obj.actor.SetBackfaceProperty(None) 3930 3931 elif key == "Left": 3932 if self.clicked_object and self.clicked_object in self.get_meshes(): 3933 ap = self.clicked_object.properties 3934 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3935 ap.SetOpacity(aal) 3936 bfp = self.clicked_actor.GetBackfaceProperty() 3937 if bfp and hasattr(self.clicked_object, "properties_backface"): 3938 self.clicked_object.properties_backface = bfp 3939 self.clicked_actor.SetBackfaceProperty(None) 3940 else: 3941 for a in self.get_meshes(): 3942 if a: 3943 ap = a.properties 3944 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3945 ap.SetOpacity(aal) 3946 bfp = a.actor.GetBackfaceProperty() 3947 if bfp and hasattr(a, "properties_backface"): 3948 a.properties_backface = bfp 3949 a.actor.SetBackfaceProperty(None) 3950 3951 elif key == "Right": 3952 if self.clicked_object and self.clicked_object in self.get_meshes(): 3953 ap = self.clicked_object.properties 3954 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3955 ap.SetOpacity(aal) 3956 if ( 3957 aal == 1 3958 and hasattr(self.clicked_object, "properties_backface") 3959 and self.clicked_object.properties_backface 3960 ): 3961 # put back 3962 self.clicked_actor.SetBackfaceProperty( 3963 self.clicked_object.properties_backface) 3964 else: 3965 for a in self.get_meshes(): 3966 if a: 3967 ap = a.properties 3968 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3969 ap.SetOpacity(aal) 3970 if aal == 1 and hasattr(a, "properties_backface") and a.properties_backface: 3971 a.actor.SetBackfaceProperty(a.properties_backface) 3972 3973 elif key == "Up": 3974 if self.clicked_object and self.clicked_object in self.get_meshes(): 3975 self.clicked_object.properties.SetOpacity(1) 3976 if hasattr(self.clicked_object, "properties_backface") and self.clicked_object.properties_backface: 3977 self.clicked_object.actor.SetBackfaceProperty(self.clicked_object.properties_backface) 3978 else: 3979 for a in self.get_meshes(): 3980 if a: 3981 a.properties.SetOpacity(1) 3982 if hasattr(a, "properties_backface") and a.properties_backface: 3983 a.actor.SetBackfaceProperty(a.properties_backface) 3984 3985 elif key == "P": 3986 if self.clicked_object and self.clicked_object in self.get_meshes(): 3987 objs = [self.clicked_object] 3988 else: 3989 objs = self.get_meshes() 3990 for ia in objs: 3991 try: 3992 ps = ia.properties.GetPointSize() 3993 if ps > 1: 3994 ia.properties.SetPointSize(ps - 1) 3995 ia.properties.SetRepresentationToPoints() 3996 except AttributeError: 3997 pass 3998 3999 elif key == "p": 4000 if self.clicked_object and self.clicked_object in self.get_meshes(): 4001 objs = [self.clicked_object] 4002 else: 4003 objs = self.get_meshes() 4004 for ia in objs: 4005 try: 4006 ps = ia.properties.GetPointSize() 4007 ia.properties.SetPointSize(ps + 2) 4008 ia.properties.SetRepresentationToPoints() 4009 except AttributeError: 4010 pass 4011 4012 elif key == "U": 4013 pval = renderer.GetActiveCamera().GetParallelProjection() 4014 renderer.GetActiveCamera().SetParallelProjection(not pval) 4015 if pval: 4016 renderer.ResetCamera() 4017 4018 elif key == "r": 4019 renderer.ResetCamera() 4020 4021 elif key == "h": 4022 msg = f" vedo {vedo.__version__}" 4023 msg += f" | vtk {vtki.vtkVersion().GetVTKVersion()}" 4024 msg += f" | numpy {np.__version__}" 4025 msg += f" | python {sys.version_info[0]}.{sys.version_info[1]}, press: " 4026 vedo.printc(msg.ljust(75), invert=True) 4027 msg = ( 4028 " i print info about the last clicked object \n" 4029 " I print color of the pixel under the mouse \n" 4030 " Y show the pipeline for this object as a graph \n" 4031 " <- -> use arrows to reduce/increase opacity \n" 4032 " x toggle mesh visibility \n" 4033 " w toggle wireframe/surface style \n" 4034 " l toggle surface edges visibility \n" 4035 " p/P hide surface faces and show only points \n" 4036 " 1-3 cycle surface color (2=light, 3=dark) \n" 4037 " 4 cycle color map (press shift-4 to go back) \n" 4038 " 5-6 cycle point-cell arrays (shift to go back) \n" 4039 " 7-8 cycle background and gradient color \n" 4040 " 09+- cycle axes styles (on keypad, or press +/-) \n" 4041 " k cycle available lighting styles \n" 4042 " K toggle shading as flat or phong \n" 4043 " A toggle anti-aliasing \n" 4044 " D toggle depth-peeling (for transparencies) \n" 4045 " U toggle perspective/parallel projection \n" 4046 " o/O toggle extra light to scene and rotate it \n" 4047 " a toggle interaction to Actor Mode \n" 4048 " n toggle surface normals \n" 4049 " r reset camera position \n" 4050 " R reset camera to the closest orthogonal view \n" 4051 " . fly camera to the last clicked point \n" 4052 " C print the current camera parameters state \n" 4053 " X invoke a cutter widget tool \n" 4054 " S save a screenshot of the current scene \n" 4055 " E/F export 3D scene to numpy file or X3D \n" 4056 " q return control to python script \n" 4057 " Esc abort execution and exit python kernel " 4058 ) 4059 vedo.printc(msg, dim=True, italic=True, bold=True) 4060 vedo.printc( 4061 " Check out the documentation at: https://vedo.embl.es ".ljust(75), 4062 invert=True, 4063 bold=True, 4064 ) 4065 return 4066 4067 elif key == "a": 4068 cur = iren.GetInteractorStyle() 4069 if isinstance(cur, vtki.get_class("InteractorStyleTrackballCamera")): 4070 msg = "Interactor style changed to TrackballActor\n" 4071 msg += " you can now move and rotate individual meshes:\n" 4072 msg += " press X twice to save the repositioned mesh\n" 4073 msg += " press 'a' to go back to normal style" 4074 vedo.printc(msg) 4075 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 4076 else: 4077 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 4078 return 4079 4080 elif key == "A": # toggle antialiasing 4081 msam = self.window.GetMultiSamples() 4082 if not msam: 4083 self.window.SetMultiSamples(16) 4084 else: 4085 self.window.SetMultiSamples(0) 4086 msam = self.window.GetMultiSamples() 4087 if msam: 4088 vedo.printc(f"Antialiasing set to {msam} samples", c=bool(msam)) 4089 else: 4090 vedo.printc("Antialiasing disabled", c=bool(msam)) 4091 4092 elif key == "D": # toggle depthpeeling 4093 udp = not renderer.GetUseDepthPeeling() 4094 renderer.SetUseDepthPeeling(udp) 4095 # self.renderer.SetUseDepthPeelingForVolumes(udp) 4096 if udp: 4097 self.window.SetAlphaBitPlanes(1) 4098 renderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 4099 renderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 4100 self.interactor.Render() 4101 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 4102 rnr = self.renderers.index(renderer) 4103 vedo.printc(f"Depth peeling set to {udp} for renderer nr.{rnr}", c=udp) 4104 if not wasUsed and udp: 4105 vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True) 4106 return 4107 4108 elif key == "period": 4109 if self.picked3d: 4110 self.fly_to(self.picked3d) 4111 return 4112 4113 elif key == "S": 4114 fname = "screenshot.png" 4115 i = 1 4116 while os.path.isfile(fname): 4117 fname = f"screenshot{i}.png" 4118 i += 1 4119 vedo.file_io.screenshot(fname) 4120 vedo.printc(rf":camera: Saved rendering window to {fname}", c="b") 4121 return 4122 4123 elif key == "C": 4124 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 4125 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 4126 # This could happen for plotting geological models with UTM coordinate systems 4127 cam = renderer.GetActiveCamera() 4128 vedo.printc("\n###################################################", c="y") 4129 vedo.printc("## Template python code to position this camera: ##", c="y") 4130 vedo.printc("cam = dict(", c="y") 4131 vedo.printc(" pos=" + utils.precision(cam.GetPosition(), 6) + ",", c="y") 4132 vedo.printc(" focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y") 4133 vedo.printc(" viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y") 4134 vedo.printc(" roll=" + utils.precision(cam.GetRoll(), 6) + ",", c="y") 4135 if cam.GetParallelProjection(): 4136 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 4137 else: 4138 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 4139 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 4140 vedo.printc(')', c='y') 4141 vedo.printc('show(mymeshes, camera=cam)', c='y') 4142 vedo.printc('###################################################', c='y') 4143 return 4144 4145 elif key == "R": 4146 self.reset_viewup() 4147 4148 elif key == "w": 4149 try: 4150 if self.clicked_object.properties.GetRepresentation() == 1: # toggle 4151 self.clicked_object.properties.SetRepresentationToSurface() 4152 else: 4153 self.clicked_object.properties.SetRepresentationToWireframe() 4154 except AttributeError: 4155 pass 4156 4157 elif key == "1": 4158 try: 4159 self._icol += 1 4160 self.clicked_object.mapper.ScalarVisibilityOff() 4161 pal = vedo.colors.palettes[vedo.settings.palette % len(vedo.colors.palettes)] 4162 self.clicked_object.c(pal[(self._icol) % 10]) 4163 self.remove(self.clicked_object.scalarbar) 4164 except AttributeError: 4165 pass 4166 4167 elif key == "2": # dark colors 4168 try: 4169 bsc = ["k1", "k2", "k3", "k4", 4170 "b1", "b2", "b3", "b4", 4171 "p1", "p2", "p3", "p4", 4172 "g1", "g2", "g3", "g4", 4173 "r1", "r2", "r3", "r4", 4174 "o1", "o2", "o3", "o4", 4175 "y1", "y2", "y3", "y4"] 4176 self._icol += 1 4177 if self.clicked_object: 4178 self.clicked_object.mapper.ScalarVisibilityOff() 4179 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4180 self.clicked_object.c(newcol) 4181 self.remove(self.clicked_object.scalarbar) 4182 except AttributeError: 4183 pass 4184 4185 elif key == "3": # light colors 4186 try: 4187 bsc = ["k6", "k7", "k8", "k9", 4188 "b6", "b7", "b8", "b9", 4189 "p6", "p7", "p8", "p9", 4190 "g6", "g7", "g8", "g9", 4191 "r6", "r7", "r8", "r9", 4192 "o6", "o7", "o8", "o9", 4193 "y6", "y7", "y8", "y9"] 4194 self._icol += 1 4195 if self.clicked_object: 4196 self.clicked_object.mapper.ScalarVisibilityOff() 4197 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4198 self.clicked_object.c(newcol) 4199 self.remove(self.clicked_object.scalarbar) 4200 except AttributeError: 4201 pass 4202 4203 elif key == "4": # cmap name cycle 4204 ob = self.clicked_object 4205 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4206 return 4207 if not ob.mapper.GetScalarVisibility(): 4208 return 4209 onwhat = ob.mapper.GetScalarModeAsString() # UsePointData/UseCellData 4210 4211 cmap_names = [ 4212 "Accent", "Paired", 4213 "rainbow", "rainbow_r", 4214 "Spectral", "Spectral_r", 4215 "gist_ncar", "gist_ncar_r", 4216 "viridis", "viridis_r", 4217 "hot", "hot_r", 4218 "terrain", "ocean", 4219 "coolwarm", "seismic", "PuOr", "RdYlGn", 4220 ] 4221 try: 4222 i = cmap_names.index(ob._cmap_name) 4223 if iren.GetShiftKey(): 4224 i -= 1 4225 else: 4226 i += 1 4227 if i >= len(cmap_names): 4228 i = 0 4229 if i < 0: 4230 i = len(cmap_names) - 1 4231 except ValueError: 4232 i = 0 4233 4234 ob._cmap_name = cmap_names[i] 4235 ob.cmap(ob._cmap_name, on=onwhat) 4236 if ob.scalarbar: 4237 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4238 self.remove(ob.scalarbar) 4239 title = ob.scalarbar.GetTitle() 4240 ob.add_scalarbar(title=title) 4241 self.add(ob.scalarbar).render() 4242 elif isinstance(ob.scalarbar, vedo.Assembly): 4243 self.remove(ob.scalarbar) 4244 ob.add_scalarbar3d(title=ob._cmap_name) 4245 self.add(ob.scalarbar) 4246 4247 vedo.printc( 4248 f"Name:'{ob.name}'," if ob.name else "", 4249 f"range:{utils.precision(ob.mapper.GetScalarRange(),3)},", 4250 f"colormap:'{ob._cmap_name}'", c="g", bold=False, 4251 ) 4252 4253 elif key == "5": # cycle pointdata array 4254 ob = self.clicked_object 4255 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4256 return 4257 4258 arrnames = ob.pointdata.keys() 4259 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4260 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4261 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4262 if len(arrnames) == 0: 4263 return 4264 ob.mapper.SetScalarVisibility(1) 4265 4266 if not ob._cmap_name: 4267 ob._cmap_name = "rainbow" 4268 4269 try: 4270 curr_name = ob.dataset.GetPointData().GetScalars().GetName() 4271 i = arrnames.index(curr_name) 4272 if "normals" in curr_name.lower(): 4273 return 4274 if iren.GetShiftKey(): 4275 i -= 1 4276 else: 4277 i += 1 4278 if i >= len(arrnames): 4279 i = 0 4280 if i < 0: 4281 i = len(arrnames) - 1 4282 except (ValueError, AttributeError): 4283 i = 0 4284 4285 ob.cmap(ob._cmap_name, arrnames[i], on="points") 4286 if ob.scalarbar: 4287 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4288 self.remove(ob.scalarbar) 4289 title = ob.scalarbar.GetTitle() 4290 ob.scalarbar = None 4291 ob.add_scalarbar(title=arrnames[i]) 4292 self.add(ob.scalarbar) 4293 elif isinstance(ob.scalarbar, vedo.Assembly): 4294 self.remove(ob.scalarbar) 4295 ob.scalarbar = None 4296 ob.add_scalarbar3d(title=arrnames[i]) 4297 self.add(ob.scalarbar) 4298 else: 4299 vedo.printc( 4300 f"Name:'{ob.name}'," if ob.name else "", 4301 f"active pointdata array: '{arrnames[i]}'", 4302 c="g", bold=False, 4303 ) 4304 4305 elif key == "6": # cycle celldata array 4306 ob = self.clicked_object 4307 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4308 return 4309 4310 arrnames = ob.celldata.keys() 4311 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4312 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4313 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4314 if len(arrnames) == 0: 4315 return 4316 ob.mapper.SetScalarVisibility(1) 4317 4318 if not ob._cmap_name: 4319 ob._cmap_name = "rainbow" 4320 4321 try: 4322 curr_name = ob.dataset.GetCellData().GetScalars().GetName() 4323 i = arrnames.index(curr_name) 4324 if "normals" in curr_name.lower(): 4325 return 4326 if iren.GetShiftKey(): 4327 i -= 1 4328 else: 4329 i += 1 4330 if i >= len(arrnames): 4331 i = 0 4332 if i < 0: 4333 i = len(arrnames) - 1 4334 except (ValueError, AttributeError): 4335 i = 0 4336 4337 ob.cmap(ob._cmap_name, arrnames[i], on="cells") 4338 if ob.scalarbar: 4339 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4340 self.remove(ob.scalarbar) 4341 title = ob.scalarbar.GetTitle() 4342 ob.scalarbar = None 4343 ob.add_scalarbar(title=arrnames[i]) 4344 self.add(ob.scalarbar) 4345 elif isinstance(ob.scalarbar, vedo.Assembly): 4346 self.remove(ob.scalarbar) 4347 ob.scalarbar = None 4348 ob.add_scalarbar3d(title=arrnames[i]) 4349 self.add(ob.scalarbar) 4350 else: 4351 vedo.printc( 4352 f"Name:'{ob.name}'," if ob.name else "", 4353 f"active celldata array: '{arrnames[i]}'", 4354 c="g", bold=False, 4355 ) 4356 4357 elif key == "7": 4358 bgc = np.array(renderer.GetBackground()).sum() / 3 4359 if bgc <= 0: 4360 bgc = 0.223 4361 elif 0 < bgc < 1: 4362 bgc = 1 4363 else: 4364 bgc = 0 4365 renderer.SetBackground(bgc, bgc, bgc) 4366 4367 elif key == "8": 4368 bg2cols = [ 4369 "lightyellow", 4370 "darkseagreen", 4371 "palegreen", 4372 "steelblue", 4373 "lightblue", 4374 "cadetblue", 4375 "lavender", 4376 "white", 4377 "blackboard", 4378 "black", 4379 ] 4380 bg2name = vedo.get_color_name(renderer.GetBackground2()) 4381 if bg2name in bg2cols: 4382 idx = bg2cols.index(bg2name) 4383 else: 4384 idx = 4 4385 if idx is not None: 4386 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 4387 if not bg2name_next: 4388 renderer.GradientBackgroundOff() 4389 else: 4390 renderer.GradientBackgroundOn() 4391 renderer.SetBackground2(vedo.get_color(bg2name_next)) 4392 4393 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 4394 i = self.renderers.index(renderer) 4395 try: 4396 self.axes_instances[i].EnabledOff() 4397 self.axes_instances[i].SetInteractor(None) 4398 except AttributeError: 4399 # print("Cannot remove widget", [self.axes_instances[i]]) 4400 try: 4401 self.remove(self.axes_instances[i]) 4402 except: 4403 print("Cannot remove axes", [self.axes_instances[i]]) 4404 return 4405 self.axes_instances[i] = None 4406 4407 if not self.axes: 4408 self.axes = 0 4409 if isinstance(self.axes, dict): 4410 self.axes = 1 4411 4412 if key in ["minus", "KP_Subtract"]: 4413 if not self.camera.GetParallelProjection() and self.axes == 0: 4414 self.axes -= 1 # jump ruler doesnt make sense in perspective mode 4415 bns = self.renderer.ComputeVisiblePropBounds() 4416 addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None, bounds=bns) 4417 else: 4418 if not self.camera.GetParallelProjection() and self.axes == 12: 4419 self.axes += 1 # jump ruler doesnt make sense in perspective mode 4420 bns = self.renderer.ComputeVisiblePropBounds() 4421 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns) 4422 self.render() 4423 4424 elif "KP_" in key or key in [ 4425 "Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior" 4426 ]: 4427 asso = { # change axes style 4428 "KP_Insert": 0, "KP_0": 0, "Insert": 0, 4429 "KP_End": 1, "KP_1": 1, "End": 1, 4430 "KP_Down": 2, "KP_2": 2, "Down": 2, 4431 "KP_Next": 3, "KP_3": 3, "Next": 3, 4432 "KP_Left": 4, "KP_4": 4, "Left": 4, 4433 "KP_Begin": 5, "KP_5": 5, "Begin": 5, 4434 "KP_Right": 6, "KP_6": 6, "Right": 6, 4435 "KP_Home": 7, "KP_7": 7, "Home": 7, 4436 "KP_Up": 8, "KP_8": 8, "Up": 8, 4437 "Prior": 9, # on windows OS 4438 } 4439 clickedr = self.renderers.index(renderer) 4440 if key in asso: 4441 if self.axes_instances[clickedr]: 4442 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 4443 self.axes_instances[clickedr].EnabledOff() 4444 else: 4445 try: 4446 renderer.RemoveActor(self.axes_instances[clickedr]) 4447 except: 4448 pass 4449 self.axes_instances[clickedr] = None 4450 bounds = renderer.ComputeVisiblePropBounds() 4451 addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds) 4452 self.interactor.Render() 4453 4454 if key == "O": 4455 renderer.RemoveLight(self._extralight) 4456 self._extralight = None 4457 4458 elif key == "o": 4459 vbb, sizes, _, _ = addons.compute_visible_bounds() 4460 cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2) 4461 if not self._extralight: 4462 vup = renderer.GetActiveCamera().GetViewUp() 4463 pos = cm + utils.vector(vup) * utils.mag(sizes) 4464 self._extralight = addons.Light(pos, focal_point=cm, intensity=0.4) 4465 renderer.AddLight(self._extralight) 4466 vedo.printc("Press 'o' again to rotate light source, or 'O' to remove it.", c='y') 4467 else: 4468 cpos = utils.vector(self._extralight.GetPosition()) 4469 x, y, z = self._extralight.GetPosition() - cm 4470 r, th, ph = transformations.cart2spher(x, y, z) 4471 th += 0.2 4472 if th > np.pi: 4473 th = np.random.random() * np.pi / 2 4474 ph += 0.3 4475 cpos = transformations.spher2cart(r, th, ph).T + cm 4476 self._extralight.SetPosition(cpos) 4477 4478 elif key == "l": 4479 if self.clicked_object in self.get_meshes(): 4480 objs = [self.clicked_object] 4481 else: 4482 objs = self.get_meshes() 4483 for ia in objs: 4484 try: 4485 ev = ia.properties.GetEdgeVisibility() 4486 ia.properties.SetEdgeVisibility(not ev) 4487 ia.properties.SetRepresentationToSurface() 4488 ia.properties.SetLineWidth(0.1) 4489 except AttributeError: 4490 pass 4491 4492 elif key == "k": # lightings 4493 if self.clicked_object in self.get_meshes(): 4494 objs = [self.clicked_object] 4495 else: 4496 objs = self.get_meshes() 4497 shds = ("default", "metallic", "plastic", "shiny", "glossy", "off") 4498 for ia in objs: 4499 try: 4500 lnr = (ia._ligthingnr + 1) % 6 4501 ia.lighting(shds[lnr]) 4502 ia._ligthingnr = lnr 4503 except AttributeError: 4504 pass 4505 4506 elif key == "K": # shading 4507 if self.clicked_object in self.get_meshes(): 4508 objs = [self.clicked_object] 4509 else: 4510 objs = self.get_meshes() 4511 for ia in objs: 4512 if isinstance(ia, vedo.Mesh): 4513 ia.compute_normals(cells=False) 4514 intrp = ia.properties.GetInterpolation() 4515 if intrp > 0: 4516 ia.properties.SetInterpolation(0) # flat 4517 else: 4518 ia.properties.SetInterpolation(2) # phong 4519 4520 elif key == "n": # show normals to an actor 4521 self.remove("added_auto_normals") 4522 if self.clicked_object in self.get_meshes(): 4523 if self.clicked_actor.GetPickable(): 4524 norml = vedo.shapes.NormalLines(self.clicked_object) 4525 norml.name = "added_auto_normals" 4526 self.add(norml) 4527 4528 elif key == "x": 4529 if self.justremoved is None: 4530 if self.clicked_object in self.get_meshes() or isinstance( 4531 self.clicked_object, vtki.vtkAssembly 4532 ): 4533 self.justremoved = self.clicked_actor 4534 self.renderer.RemoveActor(self.clicked_actor) 4535 else: 4536 self.renderer.AddActor(self.justremoved) 4537 self.justremoved = None 4538 4539 elif key == "X": 4540 if self.clicked_object: 4541 if not self.cutter_widget: 4542 self.cutter_widget = addons.BoxCutter(self.clicked_object) 4543 self.add(self.cutter_widget) 4544 vedo.printc("Press i to toggle the cutter on/off", c='g', dim=1) 4545 vedo.printc(" u to flip selection", c='g', dim=1) 4546 vedo.printc(" r to reset cutting planes", c='g', dim=1) 4547 vedo.printc(" Shift+X to close the cutter box widget", c='g', dim=1) 4548 vedo.printc(" Ctrl+S to save the cut section to file.", c='g', dim=1) 4549 else: 4550 self.remove(self.cutter_widget) 4551 self.cutter_widget = None 4552 vedo.printc("Click object and press X to open the cutter box widget.", c='g') 4553 4554 elif key == "E": 4555 vedo.printc(r":camera: Exporting 3D window to file scene.npz", c="b", end="") 4556 vedo.file_io.export_window("scene.npz") 4557 vedo.printc(", try:\n> vedo scene.npz # (this is experimental!)", c="b") 4558 return 4559 4560 elif key == "F": 4561 vedo.file_io.export_window("scene.x3d") 4562 vedo.printc(r":camera: Exporting 3D window to file", c="b", end="") 4563 vedo.file_io.export_window("scene.npz") 4564 vedo.printc(". Try:\n> firefox scene.html", c="b") 4565 4566 # elif key == "G": # not working with last version of k3d 4567 # vedo.file_io.export_window("scene.html") 4568 # vedo.printc(r":camera: Exporting K3D window to file", c="b", end="") 4569 # vedo.file_io.export_window("scene.html") 4570 # vedo.printc(". Try:\n> firefox scene.html", c="b") 4571 4572 elif key == "i": # print info 4573 if self.clicked_object: 4574 print(self.clicked_object) 4575 else: 4576 print(self) 4577 4578 elif key == "I": # print color under the mouse 4579 x, y = iren.GetEventPosition() 4580 self.color_picker([x, y], verbose=True) 4581 4582 elif key == "Y": 4583 if self.clicked_object and self.clicked_object.pipeline: 4584 self.clicked_object.pipeline.show() 4585 4586 if iren: 4587 iren.Render()
376class Plotter: 377 """Main class to manage objects.""" 378 379 def __init__( 380 self, 381 shape=(1, 1), 382 N=None, 383 pos=(0, 0), 384 size="auto", 385 screensize="auto", 386 title="vedo", 387 bg="white", 388 bg2=None, 389 axes=None, 390 sharecam=True, 391 resetcam=True, 392 interactive=None, 393 offscreen=False, 394 qt_widget=None, 395 wx_widget=None, 396 ): 397 """ 398 Arguments: 399 shape : (str, list) 400 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 401 N : (int) 402 number of desired renderers arranged in a grid automatically. 403 pos : (list) 404 (x,y) position in pixels of top-left corner of the rendering window on the screen 405 size : (str, list) 406 size of the rendering window. If 'auto', guess it based on screensize. 407 screensize : (list) 408 physical size of the monitor screen in pixels 409 bg : (color, str) 410 background color or specify jpg image file name with path 411 bg2 : (color) 412 background color of a gradient towards the top 413 title : (str) 414 window title 415 416 axes : (int) 417 418 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 419 Check out `vedo.addons.Axes()` for the available options. 420 421 - 0, no axes 422 - 1, draw three gray grid walls 423 - 2, show cartesian axes from (0,0,0) 424 - 3, show positive range of cartesian axes from (0,0,0) 425 - 4, show a triad at bottom left 426 - 5, show a cube at bottom left 427 - 6, mark the corners of the bounding box 428 - 7, draw a 3D ruler at each side of the cartesian axes 429 - 8, show the VTK CubeAxesActor object 430 - 9, show the bounding box outLine 431 - 10, show three circles representing the maximum bounding box 432 - 11, show a large grid on the x-y plane (use with zoom=8) 433 - 12, show polar axes 434 - 13, draw a simple ruler at the bottom of the window 435 - 14: draw a camera orientation widget 436 437 sharecam : (bool) 438 if False each renderer will have an independent camera 439 interactive : (bool) 440 if True will stop after show() to allow interaction with the 3d scene 441 offscreen : (bool) 442 if True will not show the rendering window 443 qt_widget : (QVTKRenderWindowInteractor) 444 render in a Qt-Widget using an QVTKRenderWindowInteractor. 445 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 446 """ 447 vedo.plotter_instance = self 448 449 if interactive is None: 450 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 451 self._interactive = interactive 452 # print("interactive", interactive, N, shape) 453 454 self.objects = [] # list of objects to be shown 455 self.clicked_object = None # holds the object that has been clicked 456 self.clicked_actor = None # holds the actor that has been clicked 457 458 self.shape = shape # nr. of subwindows in grid 459 self.axes = axes # show axes type nr. 460 self.title = title # window title 461 self.size = size # window size 462 self.backgrcol = bg # used also by backend notebooks 463 464 self.offscreen= offscreen 465 self.resetcam = resetcam 466 self.sharecam = sharecam # share the same camera if multiple renderers 467 self.pos = pos # used by vedo.file_io 468 469 self.picker = None # hold the vtkPicker object 470 self.picked2d = None # 2d coords of a clicked point on the rendering window 471 self.picked3d = None # 3d coords of a clicked point on an actor 472 473 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 474 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 475 self.interactor = None 476 self.window = None 477 self.renderer = None 478 self.renderers = [] # list of renderers 479 480 # mostly internal stuff: 481 self.hover_legends = [] 482 self.justremoved = None 483 self.axes_instances = [] 484 self.clock = 0 485 self.sliders = [] 486 self.buttons = [] 487 self.widgets = [] 488 self.cutter_widget = None 489 self.hint_widget = None 490 self.background_renderer = None 491 self.last_event = None 492 self.skybox = None 493 self._icol = 0 494 self._clockt0 = time.time() 495 self._extralight = None 496 self._cocoa_initialized = False 497 self._cocoa_process_events = True # make one call in show() 498 self._must_close_now = False 499 500 ##################################################################### 501 if vedo.settings.default_backend == "2d": 502 self.offscreen = True 503 if self.size == "auto": 504 self.size = (800, 600) 505 506 elif vedo.settings.default_backend == "k3d": 507 if self.size == "auto": 508 self.size = (1000, 1000) 509 #################################### 510 return ############################ 511 #################################### 512 513 ############################################################# 514 if vedo.settings.default_backend in ["vtk", "2d", "trame"]: 515 516 if screensize == "auto": 517 screensize = (2160, 1440) # TODO: get actual screen size 518 519 # build the rendering window: 520 self.window = vtki.vtkRenderWindow() 521 522 self.window.GlobalWarningDisplayOff() 523 524 if self.title == "vedo": # check if dev version 525 if "dev" in vedo.__version__: 526 self.title = f"vedo ({vedo.__version__})" 527 self.window.SetWindowName(self.title) 528 529 # more vedo.settings 530 if vedo.settings.use_depth_peeling: 531 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 532 self.window.SetMultiSamples(vedo.settings.multi_samples) 533 534 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 535 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 536 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 537 538 ############################################################# 539 if N: # N = number of renderers. Find out the best 540 541 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 542 vedo.logger.warning("having set N, shape is ignored.") 543 544 x, y = screensize 545 nx = int(np.sqrt(int(N * y / x) + 1)) 546 ny = int(np.sqrt(int(N * x / y) + 1)) 547 lm = [ 548 (nx, ny), 549 (nx, ny + 1), 550 (nx - 1, ny), 551 (nx + 1, ny), 552 (nx, ny - 1), 553 (nx - 1, ny + 1), 554 (nx + 1, ny - 1), 555 (nx + 1, ny + 1), 556 (nx - 1, ny - 1), 557 ] 558 ind, minl = 0, 1000 559 for i, m in enumerate(lm): 560 l = m[0] * m[1] 561 if N <= l < minl: 562 ind = i 563 minl = l 564 shape = lm[ind] 565 566 ################################################## 567 if isinstance(shape, str): 568 569 if "|" in shape: 570 if self.size == "auto": 571 self.size = (800, 1200) 572 n = int(shape.split("|")[0]) 573 m = int(shape.split("|")[1]) 574 rangen = reversed(range(n)) 575 rangem = reversed(range(m)) 576 else: 577 if self.size == "auto": 578 self.size = (1200, 800) 579 m = int(shape.split("/")[0]) 580 n = int(shape.split("/")[1]) 581 rangen = range(n) 582 rangem = range(m) 583 584 if n >= m: 585 xsplit = m / (n + m) 586 else: 587 xsplit = 1 - n / (n + m) 588 if vedo.settings.window_splitting_position: 589 xsplit = vedo.settings.window_splitting_position 590 591 for i in rangen: 592 arenderer = vtki.vtkRenderer() 593 if "|" in shape: 594 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 595 else: 596 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 597 self.renderers.append(arenderer) 598 599 for i in rangem: 600 arenderer = vtki.vtkRenderer() 601 602 if "|" in shape: 603 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 604 else: 605 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 606 self.renderers.append(arenderer) 607 608 for r in self.renderers: 609 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 610 611 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 612 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 613 if vedo.settings.use_depth_peeling: 614 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 615 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 616 r.SetUseFXAA(vedo.settings.use_fxaa) 617 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 618 619 r.SetBackground(vedo.get_color(self.backgrcol)) 620 621 self.axes_instances.append(None) 622 623 self.shape = (n + m,) 624 625 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 626 # passing a sequence of dicts for renderers specifications 627 628 if self.size == "auto": 629 self.size = (1000, 800) 630 631 for rd in shape: 632 x0, y0 = rd["bottomleft"] 633 x1, y1 = rd["topright"] 634 bg_ = rd.pop("bg", "white") 635 bg2_ = rd.pop("bg2", None) 636 637 arenderer = vtki.vtkRenderer() 638 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 639 640 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 641 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 642 if vedo.settings.use_depth_peeling: 643 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 644 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 645 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 646 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 647 648 arenderer.SetViewport(x0, y0, x1, y1) 649 arenderer.SetBackground(vedo.get_color(bg_)) 650 if bg2_: 651 arenderer.GradientBackgroundOn() 652 arenderer.SetBackground2(vedo.get_color(bg2_)) 653 654 self.renderers.append(arenderer) 655 self.axes_instances.append(None) 656 657 self.shape = (len(shape),) 658 659 else: 660 661 if isinstance(self.size, str) and self.size == "auto": 662 # figure out a reasonable window size 663 f = 1.5 664 x, y = screensize 665 xs = y / f * shape[1] # because y<x 666 ys = y / f * shape[0] 667 if xs > x / f: # shrink 668 xs = x / f 669 ys = xs / shape[1] * shape[0] 670 if ys > y / f: 671 ys = y / f 672 xs = ys / shape[0] * shape[1] 673 self.size = (int(xs), int(ys)) 674 if shape == (1, 1): 675 self.size = (int(y / f), int(y / f)) # because y<x 676 else: 677 self.size = (self.size[0], self.size[1]) 678 679 try: 680 image_actor = None 681 bgname = str(self.backgrcol).lower() 682 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 683 self.window.SetNumberOfLayers(2) 684 self.background_renderer = vtki.vtkRenderer() 685 self.background_renderer.SetLayer(0) 686 self.background_renderer.InteractiveOff() 687 self.background_renderer.SetBackground(vedo.get_color(bg2)) 688 image_actor = vedo.Image(self.backgrcol).actor 689 self.window.AddRenderer(self.background_renderer) 690 self.background_renderer.AddActor(image_actor) 691 except AttributeError: 692 pass 693 694 for i in reversed(range(shape[0])): 695 for j in range(shape[1]): 696 arenderer = vtki.vtkRenderer() 697 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 698 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 699 700 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 701 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 702 if vedo.settings.use_depth_peeling: 703 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 704 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 705 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 706 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 707 708 if image_actor: 709 arenderer.SetLayer(1) 710 711 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 712 if bg2: 713 arenderer.GradientBackgroundOn() 714 arenderer.SetBackground2(vedo.get_color(bg2)) 715 716 x0 = i / shape[0] 717 y0 = j / shape[1] 718 x1 = (i + 1) / shape[0] 719 y1 = (j + 1) / shape[1] 720 arenderer.SetViewport(y0, x0, y1, x1) 721 self.renderers.append(arenderer) 722 self.axes_instances.append(None) 723 self.shape = shape 724 725 if self.renderers: 726 self.renderer = self.renderers[0] 727 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 728 729 ######################################################### 730 if self.qt_widget or self.wx_widget: 731 if self.qt_widget: 732 self.window = self.qt_widget.GetRenderWindow() # overwrite 733 else: 734 self.window = self.wx_widget.GetRenderWindow() 735 self.interactor = self.window.GetInteractor() 736 737 ######################################################### 738 for r in self.renderers: 739 self.window.AddRenderer(r) 740 # set the background gradient if any 741 if vedo.settings.background_gradient_orientation > 0: 742 try: 743 modes = [ 744 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 745 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 746 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 747 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 748 ] 749 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 750 r.GradientBackgroundOn() 751 except AttributeError: 752 pass 753 754 ######################################################### 755 if self.qt_widget or self.wx_widget: 756 # self.window.SetSize(int(self.size[0]), int(self.size[1])) 757 self.interactor.SetRenderWindow(self.window) 758 # vsty = vtki.new("InteractorStyleTrackballCamera") 759 # self.interactor.SetInteractorStyle(vsty) 760 if vedo.settings.enable_default_keyboard_callbacks: 761 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 762 if vedo.settings.enable_default_mouse_callbacks: 763 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 764 return ################ 765 ######################## 766 767 if self.size[0] == "f": # full screen 768 self.size = "fullscreen" 769 self.window.SetFullScreen(True) 770 self.window.BordersOn() 771 else: 772 self.window.SetSize(int(self.size[0]), int(self.size[1])) 773 774 if self.offscreen: 775 if self.axes in (4, 5, 8, 12, 14): 776 self.axes = 0 # does not work with those 777 self.window.SetOffScreenRendering(True) 778 self.interactor = None 779 self._interactive = False 780 return ################ 781 ######################## 782 783 self.window.SetPosition(pos) 784 785 ######################################################### 786 self.interactor = vtki.vtkRenderWindowInteractor() 787 788 self.interactor.SetRenderWindow(self.window) 789 vsty = vtki.new("InteractorStyleTrackballCamera") 790 self.interactor.SetInteractorStyle(vsty) 791 self.interactor.RemoveObservers("CharEvent") 792 793 if vedo.settings.enable_default_keyboard_callbacks: 794 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 795 if vedo.settings.enable_default_mouse_callbacks: 796 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 797 798 ##################################################################### ..init ends here. 799 800 def __str__(self): 801 """Return Plotter info.""" 802 axtype = { 803 0: "(no axes)", 804 1: "(default customizable grid walls)", 805 2: "(cartesian axes from origin", 806 3: "(positive range of cartesian axes from origin", 807 4: "(axes triad at bottom left)", 808 5: "(oriented cube at bottom left)", 809 6: "(mark the corners of the bounding box)", 810 7: "(3D ruler at each side of the cartesian axes)", 811 8: "(the vtkCubeAxesActor object)", 812 9: "(the bounding box outline)", 813 10: "(circles of maximum bounding box range)", 814 11: "(show a large grid on the x-y plane)", 815 12: "(show polar axes)", 816 13: "(simple ruler at the bottom of the window)", 817 14: "(the vtkCameraOrientationWidget object)", 818 } 819 820 module = self.__class__.__module__ 821 name = self.__class__.__name__ 822 out = vedo.printc( 823 f"{module}.{name} at ({hex(id(self))})".ljust(75), 824 bold=True, invert=True, return_string=True, 825 ) 826 out += "\x1b[0m" 827 if self.interactor: 828 out += "window title".ljust(14) + ": " + self.title + "\n" 829 out += "window size".ljust(14) + f": {self.window.GetSize()}" 830 out += f", full_screen={self.window.GetScreenSize()}\n" 831 out += "activ renderer".ljust(14) + ": nr." + str(self.renderers.index(self.renderer)) 832 out += f" (out of {len(self.renderers)} renderers)\n" 833 834 bns, totpt = [], 0 835 for a in self.objects: 836 try: 837 b = a.bounds() 838 bns.append(b) 839 except AttributeError: 840 pass 841 try: 842 totpt += a.npoints 843 except AttributeError: 844 pass 845 out += "n. of objects".ljust(14) + f": {len(self.objects)}" 846 out += f" ({totpt} vertices)\n" if totpt else "\n" 847 848 if len(bns) > 0: 849 min_bns = np.min(bns, axis=0) 850 max_bns = np.max(bns, axis=0) 851 bx1, bx2 = utils.precision(min_bns[0], 3), utils.precision(max_bns[1], 3) 852 by1, by2 = utils.precision(min_bns[2], 3), utils.precision(max_bns[3], 3) 853 bz1, bz2 = utils.precision(min_bns[4], 3), utils.precision(max_bns[5], 3) 854 out += "bounds".ljust(14) + ":" 855 out += " x=(" + bx1 + ", " + bx2 + ")," 856 out += " y=(" + by1 + ", " + by2 + ")," 857 out += " z=(" + bz1 + ", " + bz2 + ")\n" 858 859 if utils.is_integer(self.axes): 860 out += "axes style".ljust(14) + f": {self.axes} {axtype[self.axes]}\n" 861 elif isinstance(self.axes, dict): 862 out += "axes style".ljust(14) + f": 1 {axtype[1]}\n" 863 else: 864 out += "axes style".ljust(14) + f": {[self.axes]}\n" 865 return out.rstrip() + "\x1b[0m" 866 867 def print(self): 868 """Print information about the current instance.""" 869 print(self.__str__()) 870 return self 871 872 def __iadd__(self, objects): 873 self.add(objects) 874 return self 875 876 def __isub__(self, objects): 877 self.remove(objects) 878 return self 879 880 def __enter__(self): 881 # context manager like in "with Plotter() as plt:" 882 return self 883 884 def __exit__(self, *args, **kwargs): 885 # context manager like in "with Plotter() as plt:" 886 self.close() 887 888 def initialize_interactor(self) -> Self: 889 """Initialize the interactor if not already initialized.""" 890 if self.offscreen: 891 return self 892 if self.interactor: 893 if not self.interactor.GetInitialized(): 894 self.interactor.Initialize() 895 self.interactor.RemoveObservers("CharEvent") 896 return self 897 898 def process_events(self) -> Self: 899 """Process all pending events.""" 900 self.initialize_interactor() 901 if self.interactor: 902 try: 903 self.interactor.ProcessEvents() 904 except AttributeError: 905 pass 906 return self 907 908 def at(self, nren: int, yren=None) -> Self: 909 """ 910 Select the current renderer number as an int. 911 Can also use the `[nx, ny]` format. 912 """ 913 if utils.is_sequence(nren): 914 if len(nren) == 2: 915 nren, yren = nren 916 else: 917 vedo.logger.error("at() argument must be a single number or a list of two numbers") 918 raise RuntimeError 919 920 if yren is not None: 921 a, b = self.shape 922 x, y = nren, yren 923 nren = x * b + y 924 # print("at (", x, y, ") -> ren", nren) 925 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 926 vedo.logger.error(f"at({nren, yren}) is malformed!") 927 raise RuntimeError 928 929 self.renderer = self.renderers[nren] 930 return self 931 932 def add(self, *objs, at=None) -> Self: 933 """ 934 Append the input objects to the internal list of objects to be shown. 935 936 Arguments: 937 at : (int) 938 add the object at the specified renderer 939 """ 940 if at is not None: 941 ren = self.renderers[at] 942 else: 943 ren = self.renderer 944 945 objs = utils.flatten(objs) 946 for ob in objs: 947 if ob and ob not in self.objects: 948 self.objects.append(ob) 949 950 acts = self._scan_input_return_acts(objs) 951 952 for a in acts: 953 954 if ren: 955 if isinstance(a, vedo.addons.BaseCutter): 956 a.add_to(self) # from cutters 957 continue 958 959 if isinstance(a, vtki.vtkLight): 960 ren.AddLight(a) 961 continue 962 963 try: 964 ren.AddActor(a) 965 except TypeError: 966 ren.AddActor(a.actor) 967 968 try: 969 ir = self.renderers.index(ren) 970 a.rendered_at.add(ir) # might not have rendered_at 971 except (AttributeError, ValueError): 972 pass 973 974 if isinstance(a, vtki.vtkFollower): 975 a.SetCamera(self.camera) 976 elif isinstance(a, vedo.visual.LightKit): 977 a.lightkit.AddLightsToRenderer(ren) 978 979 return self 980 981 def remove(self, *objs, at=None) -> Self: 982 """ 983 Remove input object to the internal list of objects to be shown. 984 985 Objects to be removed can be referenced by their assigned name, 986 987 Arguments: 988 at : (int) 989 remove the object at the specified renderer 990 """ 991 # TODO and you can also use wildcards like `*` and `?`. 992 if at is not None: 993 ren = self.renderers[at] 994 else: 995 ren = self.renderer 996 997 objs = [ob for ob in utils.flatten(objs) if ob] 998 999 has_str = False 1000 for ob in objs: 1001 if isinstance(ob, str): 1002 has_str = True 1003 break 1004 1005 has_actor = False 1006 for ob in objs: 1007 if hasattr(ob, "actor") and ob.actor: 1008 has_actor = True 1009 break 1010 1011 if has_str or has_actor: 1012 # need to get the actors to search for 1013 for a in self.get_actors(include_non_pickables=True): 1014 # print("PARSING", [a]) 1015 try: 1016 if (a.name and a.name in objs) or a in objs: 1017 objs.append(a) 1018 # if a.name: 1019 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1020 # if any(bools) or a in objs: 1021 # objs.append(a) 1022 # print('a.name',a.name, objs,any(bools)) 1023 except AttributeError: # no .name 1024 # passing the actor so get back the object with .retrieve_object() 1025 try: 1026 vobj = a.retrieve_object() 1027 if (vobj.name and vobj.name in objs) or vobj in objs: 1028 # print('vobj.name', vobj.name) 1029 objs.append(vobj) 1030 except AttributeError: 1031 pass 1032 1033 ir = self.renderers.index(ren) 1034 1035 ids = [] 1036 for ob in set(objs): 1037 1038 # will remove it from internal list if possible 1039 try: 1040 idx = self.objects.index(ob) 1041 ids.append(idx) 1042 except ValueError: 1043 pass 1044 1045 if ren: ### remove it from the renderer 1046 1047 if isinstance(ob, vedo.addons.BaseCutter): 1048 ob.remove_from(self) # from cutters 1049 continue 1050 1051 try: 1052 ren.RemoveActor(ob) 1053 except TypeError: 1054 try: 1055 ren.RemoveActor(ob.actor) 1056 except AttributeError: 1057 pass 1058 1059 if hasattr(ob, "rendered_at"): 1060 ob.rendered_at.discard(ir) 1061 1062 if hasattr(ob, "scalarbar") and ob.scalarbar: 1063 ren.RemoveActor(ob.scalarbar) 1064 if hasattr(ob, "_caption") and ob._caption: 1065 ren.RemoveActor(ob._caption) 1066 if hasattr(ob, "shadows") and ob.shadows: 1067 for sha in ob.shadows: 1068 ren.RemoveActor(sha.actor) 1069 if hasattr(ob, "trail") and ob.trail: 1070 ren.RemoveActor(ob.trail.actor) 1071 ob.trail_points = [] 1072 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1073 for sha in ob.trail.shadows: 1074 ren.RemoveActor(sha.actor) 1075 1076 elif isinstance(ob, vedo.visual.LightKit): 1077 ob.lightkit.RemoveLightsFromRenderer(ren) 1078 1079 # for i in ids: # WRONG way of doing it! 1080 # del self.objects[i] 1081 # instead we do: 1082 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1083 return self 1084 1085 @property 1086 def actors(self): 1087 """Return the list of actors.""" 1088 return [ob.actor for ob in self.objects if hasattr(ob, "actor")] 1089 1090 def remove_lights(self) -> Self: 1091 """Remove all the present lights in the current renderer.""" 1092 if self.renderer: 1093 self.renderer.RemoveAllLights() 1094 return self 1095 1096 def pop(self, at=None) -> Self: 1097 """ 1098 Remove the last added object from the rendering window. 1099 This method is typically used in loops or callback functions. 1100 """ 1101 if at is not None and not isinstance(at, int): 1102 # wrong usage pitfall 1103 vedo.logger.error("argument of pop() must be an integer") 1104 raise RuntimeError() 1105 1106 if self.objects: 1107 self.remove(self.objects[-1], at) 1108 return self 1109 1110 def render(self, resetcam=False) -> Self: 1111 """Render the scene. This method is typically used in loops or callback functions.""" 1112 1113 if vedo.settings.dry_run_mode >= 2: 1114 return self 1115 1116 if not self.window: 1117 return self 1118 1119 self.initialize_interactor() 1120 1121 if resetcam: 1122 self.renderer.ResetCamera() 1123 1124 self.window.Render() 1125 1126 if self._cocoa_process_events and self.interactor.GetInitialized(): 1127 if "Darwin" in vedo.sys_platform and not self.offscreen: 1128 self.interactor.ProcessEvents() 1129 self._cocoa_process_events = False 1130 return self 1131 1132 def interactive(self) -> Self: 1133 """ 1134 Start window interaction. 1135 Analogous to `show(..., interactive=True)`. 1136 """ 1137 if vedo.settings.dry_run_mode >= 1: 1138 return self 1139 self.initialize_interactor() 1140 if self.interactor: 1141 # print("self.interactor.Start()") 1142 self.interactor.Start() 1143 # print("self.interactor.Start() done") 1144 if self._must_close_now: 1145 # print("self.interactor.TerminateApp()") 1146 if self.interactor: 1147 self.interactor.GetRenderWindow().Finalize() 1148 self.interactor.TerminateApp() 1149 self.interactor = None 1150 self.window = None 1151 self.renderer = None 1152 self.renderers = [] 1153 self.camera = None 1154 return self 1155 1156 def use_depth_peeling(self, at=None, value=True) -> Self: 1157 """ 1158 Specify whether use depth peeling algorithm at this specific renderer 1159 Call this method before the first rendering. 1160 """ 1161 if at is None: 1162 ren = self.renderer 1163 else: 1164 ren = self.renderers[at] 1165 ren.SetUseDepthPeeling(value) 1166 return self 1167 1168 def background(self, c1=None, c2=None, at=None, mode=0) -> Union[Self, "np.ndarray"]: 1169 """Set the color of the background for the current renderer. 1170 A different renderer index can be specified by keyword `at`. 1171 1172 Arguments: 1173 c1 : (list) 1174 background main color. 1175 c2 : (list) 1176 background color for the upper part of the window. 1177 at : (int) 1178 renderer index. 1179 mode : (int) 1180 background mode (needs vtk version >= 9.3) 1181 0 = vertical, 1182 1 = horizontal, 1183 2 = radial farthest side, 1184 3 = radia farthest corner. 1185 """ 1186 if not self.renderers: 1187 return self 1188 if at is None: 1189 r = self.renderer 1190 else: 1191 r = self.renderers[at] 1192 1193 if c1 is None and c2 is None: 1194 return np.array(r.GetBackground()) 1195 1196 if r: 1197 if c1 is not None: 1198 r.SetBackground(vedo.get_color(c1)) 1199 if c2 is not None: 1200 r.GradientBackgroundOn() 1201 r.SetBackground2(vedo.get_color(c2)) 1202 if mode: 1203 try: # only works with vtk>=9.3 1204 modes = [ 1205 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1206 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1207 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1208 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1209 ] 1210 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 1211 except AttributeError: 1212 pass 1213 1214 else: 1215 r.GradientBackgroundOff() 1216 return self 1217 1218 ################################################################## 1219 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1220 """ 1221 Return a list of Meshes from the specified renderer. 1222 1223 Arguments: 1224 at : (int) 1225 specify which renderer to look at. 1226 include_non_pickables : (bool) 1227 include non-pickable objects 1228 unpack_assemblies : (bool) 1229 unpack assemblies into their components 1230 """ 1231 if at is None: 1232 renderer = self.renderer 1233 at = self.renderers.index(renderer) 1234 elif isinstance(at, int): 1235 renderer = self.renderers[at] 1236 1237 has_global_axes = False 1238 if isinstance(self.axes_instances[at], vedo.Assembly): 1239 has_global_axes = True 1240 1241 if unpack_assemblies: 1242 acs = renderer.GetActors() 1243 else: 1244 acs = renderer.GetViewProps() 1245 1246 objs = [] 1247 acs.InitTraversal() 1248 for _ in range(acs.GetNumberOfItems()): 1249 1250 if unpack_assemblies: 1251 a = acs.GetNextItem() 1252 else: 1253 a = acs.GetNextProp() 1254 1255 if isinstance(a, vtki.vtkVolume): 1256 continue 1257 1258 if include_non_pickables or a.GetPickable(): 1259 if a == self.axes_instances[at]: 1260 continue 1261 if has_global_axes and a in self.axes_instances[at].actors: 1262 continue 1263 try: 1264 objs.append(a.retrieve_object()) 1265 except AttributeError: 1266 pass 1267 return objs 1268 1269 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1270 """ 1271 Return a list of Volumes from the specified renderer. 1272 1273 Arguments: 1274 at : (int) 1275 specify which renderer to look at 1276 include_non_pickables : (bool) 1277 include non-pickable objects 1278 """ 1279 if at is None: 1280 renderer = self.renderer 1281 at = self.renderers.index(renderer) 1282 elif isinstance(at, int): 1283 renderer = self.renderers[at] 1284 1285 vols = [] 1286 acs = renderer.GetVolumes() 1287 acs.InitTraversal() 1288 for _ in range(acs.GetNumberOfItems()): 1289 a = acs.GetNextItem() 1290 if include_non_pickables or a.GetPickable(): 1291 try: 1292 vols.append(a.retrieve_object()) 1293 except AttributeError: 1294 pass 1295 return vols 1296 1297 def get_actors(self, at=None, include_non_pickables=False) -> list: 1298 """ 1299 Return a list of Volumes from the specified renderer. 1300 1301 Arguments: 1302 at : (int) 1303 specify which renderer to look at 1304 include_non_pickables : (bool) 1305 include non-pickable objects 1306 """ 1307 if at is None: 1308 renderer = self.renderer 1309 at = self.renderers.index(renderer) 1310 elif isinstance(at, int): 1311 renderer = self.renderers[at] 1312 1313 acts = [] 1314 acs = renderer.GetViewProps() 1315 acs.InitTraversal() 1316 for _ in range(acs.GetNumberOfItems()): 1317 a = acs.GetNextProp() 1318 if include_non_pickables or a.GetPickable(): 1319 acts.append(a) 1320 return acts 1321 1322 def check_actors_trasform(self, at=None) -> Self: 1323 """ 1324 Reset the transformation matrix of all actors at specified renderer. 1325 This is only useful when actors have been moved/rotated/scaled manually 1326 in an already rendered scene using interactors like 1327 'TrackballActor' or 'JoystickActor'. 1328 """ 1329 # see issue https://github.com/marcomusy/vedo/issues/1046 1330 for a in self.get_actors(at=at, include_non_pickables=True): 1331 try: 1332 M = a.GetMatrix() 1333 except AttributeError: 1334 continue 1335 if M and not M.IsIdentity(): 1336 try: 1337 a.retrieve_object().apply_transform_from_actor() 1338 # vedo.logger.info( 1339 # f"object '{a.retrieve_object().name}' " 1340 # "was manually moved. Updated to its current position." 1341 # ) 1342 except AttributeError: 1343 pass 1344 return self 1345 1346 def reset_camera(self, tight=None) -> Self: 1347 """ 1348 Reset the camera position and zooming. 1349 If tight (float) is specified the zooming reserves a padding space 1350 in the xy-plane expressed in percent of the average size. 1351 """ 1352 if tight is None: 1353 self.renderer.ResetCamera() 1354 else: 1355 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1356 cam = self.camera 1357 1358 self.renderer.ComputeAspect() 1359 aspect = self.renderer.GetAspect() 1360 angle = np.pi * cam.GetViewAngle() / 180.0 1361 dx = x1 - x0 1362 dy = y1 - y0 1363 dist = max(dx / aspect[0], dy) / np.tan(angle / 2) / 2 1364 1365 cam.SetViewUp(0, 1, 0) 1366 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1367 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1368 if cam.GetParallelProjection(): 1369 ps = max(dx / aspect[0], dy) / 2 1370 cam.SetParallelScale(ps * (1 + tight)) 1371 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1372 return self 1373 1374 def reset_viewup(self, smooth=True) -> Self: 1375 """ 1376 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1377 """ 1378 vbb = addons.compute_visible_bounds()[0] 1379 x0, x1, y0, y1, z0, z1 = vbb 1380 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1381 d = self.camera.GetDistance() 1382 1383 viewups = np.array( 1384 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1385 ) 1386 positions = np.array( 1387 [ 1388 (mx, my, mz + d), 1389 (mx, my, mz - d), 1390 (mx, my + d, mz), 1391 (mx, my - d, mz), 1392 (mx + d, my, mz), 1393 (mx - d, my, mz), 1394 ] 1395 ) 1396 1397 vu = np.array(self.camera.GetViewUp()) 1398 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1399 1400 poc = np.array(self.camera.GetPosition()) 1401 foc = np.array(self.camera.GetFocalPoint()) 1402 a = poc - foc 1403 b = positions - foc 1404 a = a / np.linalg.norm(a) 1405 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1406 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1407 1408 if smooth: 1409 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1410 for t in outtimes: 1411 vv = vu * (1 - t) + viewups[vui] * t 1412 pp = poc * (1 - t) + positions[pui] * t 1413 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1414 self.camera.SetViewUp(vv) 1415 self.camera.SetPosition(pp) 1416 self.camera.SetFocalPoint(ff) 1417 self.render() 1418 1419 # interpolator does not respect parallel view...: 1420 # cam1 = dict( 1421 # pos=poc, 1422 # viewup=vu, 1423 # focal_point=(mx,my,mz), 1424 # clipping_range=self.camera.GetClippingRange() 1425 # ) 1426 # # cam1 = self.camera 1427 # cam2 = dict( 1428 # pos=positions[pui], 1429 # viewup=viewups[vui], 1430 # focal_point=(mx,my,mz), 1431 # clipping_range=self.camera.GetClippingRange() 1432 # ) 1433 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1434 # for c in vcams: 1435 # self.renderer.SetActiveCamera(c) 1436 # self.render() 1437 else: 1438 1439 self.camera.SetViewUp(viewups[vui]) 1440 self.camera.SetPosition(positions[pui]) 1441 self.camera.SetFocalPoint(mx, my, mz) 1442 1443 self.renderer.ResetCameraClippingRange() 1444 1445 # vbb, _, _, _ = addons.compute_visible_bounds() 1446 # x0,x1, y0,y1, z0,z1 = vbb 1447 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1448 self.render() 1449 return self 1450 1451 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1452 """ 1453 Takes as input two cameras set camera at an interpolated position: 1454 1455 Cameras can be vtkCamera or dictionaries in format: 1456 1457 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1458 1459 Press `shift-C` key in interactive mode to dump a python snipplet 1460 of parameters for the current camera view. 1461 """ 1462 nc = len(cameras) 1463 if len(times) == 0: 1464 times = np.linspace(0, 1, num=nc, endpoint=True) 1465 1466 assert len(times) == nc 1467 1468 cin = vtki.new("CameraInterpolator") 1469 1470 # cin.SetInterpolationTypeToLinear() # buggy? 1471 if nc > 2 and smooth: 1472 cin.SetInterpolationTypeToSpline() 1473 1474 for i, cam in enumerate(cameras): 1475 vcam = cam 1476 if isinstance(cam, dict): 1477 vcam = utils.camera_from_dict(cam) 1478 cin.AddCamera(times[i], vcam) 1479 1480 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1481 rng = maxt - mint 1482 1483 if len(output_times) == 0: 1484 cin.InterpolateCamera(t * rng, self.camera) 1485 self.renderer.SetActiveCamera(self.camera) 1486 return [self.camera] 1487 else: 1488 vcams = [] 1489 for tt in output_times: 1490 c = vtki.vtkCamera() 1491 cin.InterpolateCamera(tt * rng, c) 1492 vcams.append(c) 1493 return vcams 1494 1495 def fly_to(self, point) -> Self: 1496 """ 1497 Fly camera to the specified point. 1498 1499 Arguments: 1500 point : (list) 1501 point in space to place camera. 1502 1503 Example: 1504 ```python 1505 from vedo import * 1506 cone = Cone() 1507 plt = Plotter(axes=1) 1508 plt.show(cone) 1509 plt.fly_to([1,0,0]) 1510 plt.interactive().close() 1511 ``` 1512 """ 1513 if self.interactor: 1514 self.resetcam = False 1515 self.interactor.FlyTo(self.renderer, point) 1516 return self 1517 1518 def look_at(self, plane="xy") -> Self: 1519 """Move the camera so that it looks at the specified cartesian plane""" 1520 cam = self.renderer.GetActiveCamera() 1521 fp = np.array(cam.GetFocalPoint()) 1522 p = np.array(cam.GetPosition()) 1523 dist = np.linalg.norm(fp - p) 1524 plane = plane.lower() 1525 if "x" in plane and "y" in plane: 1526 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1527 cam.SetViewUp(0.0, 1.0, 0.0) 1528 elif "x" in plane and "z" in plane: 1529 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1530 cam.SetViewUp(0.0, 0.0, 1.0) 1531 elif "y" in plane and "z" in plane: 1532 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1533 cam.SetViewUp(0.0, 0.0, 1.0) 1534 else: 1535 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1536 return self 1537 1538 def record(self, filename="") -> str: 1539 """ 1540 Record camera, mouse, keystrokes and all other events. 1541 Recording can be toggled on/off by pressing key "R". 1542 1543 Arguments: 1544 filename : (str) 1545 ascii file to store events. 1546 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1547 1548 Returns: 1549 a string descriptor of events. 1550 1551 Examples: 1552 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1553 """ 1554 if vedo.settings.dry_run_mode >= 1: 1555 return "" 1556 if not self.interactor: 1557 vedo.logger.warning("Cannot record events, no interactor defined.") 1558 return "" 1559 erec = vtki.new("InteractorEventRecorder") 1560 erec.SetInteractor(self.interactor) 1561 if not filename: 1562 if not os.path.exists(vedo.settings.cache_directory): 1563 os.makedirs(vedo.settings.cache_directory) 1564 home_dir = os.path.expanduser("~") 1565 filename = os.path.join( 1566 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1567 print("Events will be recorded in", filename) 1568 erec.SetFileName(filename) 1569 erec.SetKeyPressActivationValue("R") 1570 erec.EnabledOn() 1571 erec.Record() 1572 self.interactor.Start() 1573 erec.Stop() 1574 erec.EnabledOff() 1575 with open(filename, "r", encoding="UTF-8") as fl: 1576 events = fl.read() 1577 erec = None 1578 return events 1579 1580 def play(self, recorded_events="", repeats=0) -> Self: 1581 """ 1582 Play camera, mouse, keystrokes and all other events. 1583 1584 Arguments: 1585 events : (str) 1586 file o string of events. 1587 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1588 repeats : (int) 1589 number of extra repeats of the same events. The default is 0. 1590 1591 Examples: 1592 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1593 """ 1594 if vedo.settings.dry_run_mode >= 1: 1595 return self 1596 if not self.interactor: 1597 vedo.logger.warning("Cannot play events, no interactor defined.") 1598 return self 1599 1600 erec = vtki.new("InteractorEventRecorder") 1601 erec.SetInteractor(self.interactor) 1602 1603 if not recorded_events: 1604 home_dir = os.path.expanduser("~") 1605 recorded_events = os.path.join( 1606 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1607 1608 if recorded_events.endswith(".log"): 1609 erec.ReadFromInputStringOff() 1610 erec.SetFileName(recorded_events) 1611 else: 1612 erec.ReadFromInputStringOn() 1613 erec.SetInputString(recorded_events) 1614 1615 erec.Play() 1616 for _ in range(repeats): 1617 erec.Rewind() 1618 erec.Play() 1619 erec.EnabledOff() 1620 erec = None 1621 return self 1622 1623 def parallel_projection(self, value=True, at=None) -> Self: 1624 """ 1625 Use parallel projection `at` a specified renderer. 1626 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1627 An input value equal to -1 will toggle it on/off. 1628 """ 1629 if at is not None: 1630 r = self.renderers[at] 1631 else: 1632 r = self.renderer 1633 if value == -1: 1634 val = r.GetActiveCamera().GetParallelProjection() 1635 value = not val 1636 r.GetActiveCamera().SetParallelProjection(value) 1637 r.Modified() 1638 return self 1639 1640 def render_hidden_lines(self, value=True) -> Self: 1641 """Remove hidden lines when in wireframe mode.""" 1642 self.renderer.SetUseHiddenLineRemoval(not value) 1643 return self 1644 1645 def fov(self, angle: float) -> Self: 1646 """ 1647 Set the field of view angle for the camera. 1648 This is the angle of the camera frustum in the horizontal direction. 1649 High values will result in a wide-angle lens (fish-eye effect), 1650 and low values will result in a telephoto lens. 1651 1652 Default value is 30 degrees. 1653 """ 1654 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1655 self.renderer.GetActiveCamera().SetViewAngle(angle) 1656 return self 1657 1658 def zoom(self, zoom: float) -> Self: 1659 """Apply a zooming factor for the current camera view""" 1660 self.renderer.GetActiveCamera().Zoom(zoom) 1661 return self 1662 1663 def azimuth(self, angle: float) -> Self: 1664 """Rotate camera around the view up vector.""" 1665 self.renderer.GetActiveCamera().Azimuth(angle) 1666 return self 1667 1668 def elevation(self, angle: float) -> Self: 1669 """Rotate the camera around the cross product of the negative 1670 of the direction of projection and the view up vector.""" 1671 self.renderer.GetActiveCamera().Elevation(angle) 1672 return self 1673 1674 def roll(self, angle: float) -> Self: 1675 """Roll the camera about the direction of projection.""" 1676 self.renderer.GetActiveCamera().Roll(angle) 1677 return self 1678 1679 def dolly(self, value: float) -> Self: 1680 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1681 self.renderer.GetActiveCamera().Dolly(value) 1682 return self 1683 1684 ################################################################## 1685 def add_slider( 1686 self, 1687 sliderfunc, 1688 xmin, 1689 xmax, 1690 value=None, 1691 pos=4, 1692 title="", 1693 font="Calco", 1694 title_size=1, 1695 c=None, 1696 alpha=1, 1697 show_value=True, 1698 delayed=False, 1699 **options, 1700 ) -> "vedo.addons.Slider2D": 1701 """ 1702 Add a `vedo.addons.Slider2D` which can call an external custom function. 1703 1704 Arguments: 1705 sliderfunc : (Callable) 1706 external function to be called by the widget 1707 xmin : (float) 1708 lower value of the slider 1709 xmax : (float) 1710 upper value 1711 value : (float) 1712 current value 1713 pos : (list, str) 1714 position corner number: horizontal [1-5] or vertical [11-15] 1715 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1716 and also by a string descriptor (eg. "bottom-left") 1717 title : (str) 1718 title text 1719 font : (str) 1720 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1721 title_size : (float) 1722 title text scale [1.0] 1723 show_value : (bool) 1724 if True current value is shown 1725 delayed : (bool) 1726 if True the callback is delayed until when the mouse button is released 1727 alpha : (float) 1728 opacity of the scalar bar texts 1729 slider_length : (float) 1730 slider length 1731 slider_width : (float) 1732 slider width 1733 end_cap_length : (float) 1734 length of the end cap 1735 end_cap_width : (float) 1736 width of the end cap 1737 tube_width : (float) 1738 width of the tube 1739 title_height : (float) 1740 width of the title 1741 tformat : (str) 1742 format of the title 1743 1744 Examples: 1745 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1746 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1747 1748 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1749 """ 1750 if c is None: # automatic black or white 1751 c = (0.8, 0.8, 0.8) 1752 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1753 c = (0.2, 0.2, 0.2) 1754 else: 1755 c = vedo.get_color(c) 1756 1757 slider2d = addons.Slider2D( 1758 sliderfunc, 1759 xmin, 1760 xmax, 1761 value, 1762 pos, 1763 title, 1764 font, 1765 title_size, 1766 c, 1767 alpha, 1768 show_value, 1769 delayed, 1770 **options, 1771 ) 1772 1773 if self.renderer: 1774 slider2d.renderer = self.renderer 1775 if self.interactor: 1776 slider2d.interactor = self.interactor 1777 slider2d.on() 1778 self.sliders.append([slider2d, sliderfunc]) 1779 return slider2d 1780 1781 def add_slider3d( 1782 self, 1783 sliderfunc, 1784 pos1, 1785 pos2, 1786 xmin, 1787 xmax, 1788 value=None, 1789 s=0.03, 1790 t=1, 1791 title="", 1792 rotation=0.0, 1793 c=None, 1794 show_value=True, 1795 ) -> "vedo.addons.Slider3D": 1796 """ 1797 Add a 3D slider widget which can call an external custom function. 1798 1799 Arguments: 1800 sliderfunc : (function) 1801 external function to be called by the widget 1802 pos1 : (list) 1803 first position 3D coordinates 1804 pos2 : (list) 1805 second position coordinates 1806 xmin : (float) 1807 lower value 1808 xmax : (float) 1809 upper value 1810 value : (float) 1811 initial value 1812 s : (float) 1813 label scaling factor 1814 t : (float) 1815 tube scaling factor 1816 title : (str) 1817 title text 1818 c : (color) 1819 slider color 1820 rotation : (float) 1821 title rotation around slider axis 1822 show_value : (bool) 1823 if True current value is shown 1824 1825 Examples: 1826 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1827 1828 ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png) 1829 """ 1830 if c is None: # automatic black or white 1831 c = (0.8, 0.8, 0.8) 1832 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1833 c = (0.2, 0.2, 0.2) 1834 else: 1835 c = vedo.get_color(c) 1836 1837 slider3d = addons.Slider3D( 1838 sliderfunc, 1839 pos1, 1840 pos2, 1841 xmin, 1842 xmax, 1843 value, 1844 s, 1845 t, 1846 title, 1847 rotation, 1848 c, 1849 show_value, 1850 ) 1851 slider3d.renderer = self.renderer 1852 slider3d.interactor = self.interactor 1853 slider3d.on() 1854 self.sliders.append([slider3d, sliderfunc]) 1855 return slider3d 1856 1857 def add_button( 1858 self, 1859 fnc=None, 1860 states=("On", "Off"), 1861 c=("w", "w"), 1862 bc=("green4", "red4"), 1863 pos=(0.7, 0.1), 1864 size=24, 1865 font="Courier", 1866 bold=True, 1867 italic=False, 1868 alpha=1, 1869 angle=0, 1870 ) -> Union["vedo.addons.Button", None]: 1871 """ 1872 Add a button to the renderer window. 1873 1874 Arguments: 1875 states : (list) 1876 a list of possible states, e.g. ['On', 'Off'] 1877 c : (list) 1878 a list of colors for each state 1879 bc : (list) 1880 a list of background colors for each state 1881 pos : (list) 1882 2D position from left-bottom corner 1883 size : (float) 1884 size of button font 1885 font : (str) 1886 font type. Check [available fonts here](https://vedo.embl.es/fonts). 1887 bold : (bool) 1888 bold font face (False) 1889 italic : (bool) 1890 italic font face (False) 1891 alpha : (float) 1892 opacity level 1893 angle : (float) 1894 anticlockwise rotation in degrees 1895 1896 Returns: 1897 `vedo.addons.Button` object. 1898 1899 Examples: 1900 - [buttons1.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons1.py) 1901 - [buttons2.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons2.py) 1902 1903 ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg) 1904 """ 1905 if self.interactor: 1906 bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle) 1907 self.renderer.AddActor2D(bu) 1908 self.buttons.append(bu) 1909 # bu.function_id = self.add_callback("LeftButtonPress", bu.function) # not good 1910 bu.function_id = bu.add_observer("pick", bu.function, priority=10) 1911 return bu 1912 return None 1913 1914 def add_spline_tool( 1915 self, points, pc="k", ps=8, lc="r4", ac="g5", 1916 lw=2, alpha=1, closed=False, ontop=True, can_add_nodes=True, 1917 ) -> "vedo.addons.SplineTool": 1918 """ 1919 Add a spline tool to the current plotter. 1920 Nodes of the spline can be dragged in space with the mouse. 1921 Clicking on the line itself adds an extra point. 1922 Selecting a point and pressing del removes it. 1923 1924 Arguments: 1925 points : (Mesh, Points, array) 1926 the set of vertices forming the spline nodes. 1927 pc : (str) 1928 point color. The default is 'k'. 1929 ps : (str) 1930 point size. The default is 8. 1931 lc : (str) 1932 line color. The default is 'r4'. 1933 ac : (str) 1934 active point marker color. The default is 'g5'. 1935 lw : (int) 1936 line width. The default is 2. 1937 alpha : (float) 1938 line transparency. 1939 closed : (bool) 1940 spline is meant to be closed. The default is False. 1941 1942 Returns: 1943 a `SplineTool` object. 1944 1945 Examples: 1946 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1947 1948 ![](https://vedo.embl.es/images/basic/spline_tool.png) 1949 """ 1950 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1951 sw.interactor = self.interactor 1952 sw.on() 1953 sw.Initialize(sw.points.dataset) 1954 sw.representation.SetRenderer(self.renderer) 1955 sw.representation.SetClosedLoop(closed) 1956 sw.representation.BuildRepresentation() 1957 self.widgets.append(sw) 1958 return sw 1959 1960 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1961 """Add an inset icon mesh into the same renderer. 1962 1963 Arguments: 1964 pos : (int, list) 1965 icon position in the range [1-4] indicating one of the 4 corners, 1966 or it can be a tuple (x,y) as a fraction of the renderer size. 1967 size : (float) 1968 size of the square inset. 1969 1970 Examples: 1971 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1972 """ 1973 iconw = addons.Icon(icon, pos, size) 1974 1975 iconw.SetInteractor(self.interactor) 1976 iconw.EnabledOn() 1977 iconw.InteractiveOff() 1978 self.widgets.append(iconw) 1979 return iconw 1980 1981 def add_global_axes(self, axtype=None, c=None) -> Self: 1982 """Draw axes on scene. Available axes types: 1983 1984 Arguments: 1985 axtype : (int) 1986 - 0, no axes, 1987 - 1, draw three gray grid walls 1988 - 2, show cartesian axes from (0,0,0) 1989 - 3, show positive range of cartesian axes from (0,0,0) 1990 - 4, show a triad at bottom left 1991 - 5, show a cube at bottom left 1992 - 6, mark the corners of the bounding box 1993 - 7, draw a 3D ruler at each side of the cartesian axes 1994 - 8, show the vtkCubeAxesActor object 1995 - 9, show the bounding box outLine 1996 - 10, show three circles representing the maximum bounding box 1997 - 11, show a large grid on the x-y plane 1998 - 12, show polar axes 1999 - 13, draw a simple ruler at the bottom of the window 2000 2001 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 2002 2003 Example: 2004 ```python 2005 from vedo import Box, show 2006 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 2007 show( 2008 b, 2009 axes={ 2010 "xtitle": "Some long variable [a.u.]", 2011 "number_of_divisions": 4, 2012 # ... 2013 }, 2014 ) 2015 ``` 2016 2017 Examples: 2018 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2019 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2020 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2021 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2022 2023 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2024 """ 2025 addons.add_global_axes(axtype, c) 2026 return self 2027 2028 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2029 """Add a legend to the top right. 2030 2031 Examples: 2032 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 2033 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 2034 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 2035 """ 2036 acts = self.get_meshes() 2037 lb = addons.LegendBox(acts, **kwargs) 2038 self.add(lb) 2039 return lb 2040 2041 def add_hint( 2042 self, 2043 obj, 2044 text="", 2045 c="k", 2046 bg="yellow9", 2047 font="Calco", 2048 size=18, 2049 justify=0, 2050 angle=0, 2051 delay=250, 2052 ) -> Union[vtki.vtkBalloonWidget, None]: 2053 """ 2054 Create a pop-up hint style message when hovering an object. 2055 Use `add_hint(obj, False)` to disable a hinting a specific object. 2056 Use `add_hint(None)` to disable all hints. 2057 2058 Arguments: 2059 obj : (Mesh, Points) 2060 the object to associate the pop-up to 2061 text : (str) 2062 string description of the pop-up 2063 delay : (int) 2064 milliseconds to wait before pop-up occurs 2065 """ 2066 if self.offscreen or not self.interactor: 2067 return None 2068 2069 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2070 # Linux vtk9.0 is bugged 2071 vedo.logger.warning( 2072 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2073 ) 2074 return None 2075 2076 if obj is None: 2077 self.hint_widget.EnabledOff() 2078 self.hint_widget.SetInteractor(None) 2079 self.hint_widget = None 2080 return self.hint_widget 2081 2082 if text is False and self.hint_widget: 2083 self.hint_widget.RemoveBalloon(obj) 2084 return self.hint_widget 2085 2086 if text == "": 2087 if obj.name: 2088 text = obj.name 2089 elif obj.filename: 2090 text = obj.filename 2091 else: 2092 return None 2093 2094 if not self.hint_widget: 2095 self.hint_widget = vtki.vtkBalloonWidget() 2096 2097 rep = self.hint_widget.GetRepresentation() 2098 rep.SetBalloonLayoutToImageRight() 2099 2100 trep = rep.GetTextProperty() 2101 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2102 trep.SetFontFile(utils.get_font_path(font)) 2103 trep.SetFontSize(size) 2104 trep.SetColor(vedo.get_color(c)) 2105 trep.SetBackgroundColor(vedo.get_color(bg)) 2106 trep.SetShadow(0) 2107 trep.SetJustification(justify) 2108 trep.UseTightBoundingBoxOn() 2109 2110 self.hint_widget.ManagesCursorOff() 2111 self.hint_widget.SetTimerDuration(delay) 2112 self.hint_widget.SetInteractor(self.interactor) 2113 if angle: 2114 trep.SetOrientation(angle) 2115 trep.SetBackgroundOpacity(0) 2116 # else: 2117 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2118 self.hint_widget.SetRepresentation(rep) 2119 self.widgets.append(self.hint_widget) 2120 self.hint_widget.EnabledOn() 2121 2122 bst = self.hint_widget.GetBalloonString(obj.actor) 2123 if bst: 2124 self.hint_widget.UpdateBalloonString(obj.actor, text) 2125 else: 2126 self.hint_widget.AddBalloon(obj.actor, text) 2127 2128 return self.hint_widget 2129 2130 def add_shadows(self) -> Self: 2131 """Add shadows at the current renderer.""" 2132 if self.renderer: 2133 shadows = vtki.new("ShadowMapPass") 2134 seq = vtki.new("SequencePass") 2135 passes = vtki.new("RenderPassCollection") 2136 passes.AddItem(shadows.GetShadowMapBakerPass()) 2137 passes.AddItem(shadows) 2138 seq.SetPasses(passes) 2139 camerapass = vtki.new("CameraPass") 2140 camerapass.SetDelegatePass(seq) 2141 self.renderer.SetPass(camerapass) 2142 return self 2143 2144 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> Self: 2145 """ 2146 Screen Space Ambient Occlusion. 2147 2148 For every pixel on the screen, the pixel shader samples the depth values around 2149 the current pixel and tries to compute the amount of occlusion from each of the sampled 2150 points. 2151 2152 Arguments: 2153 radius : (float) 2154 radius of influence in absolute units 2155 bias : (float) 2156 bias of the normals 2157 blur : (bool) 2158 add a blurring to the sampled positions 2159 samples : (int) 2160 number of samples to probe 2161 2162 Examples: 2163 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2164 2165 ![](https://vedo.embl.es/images/basic/ssao.jpg) 2166 """ 2167 lights = vtki.new("LightsPass") 2168 2169 opaque = vtki.new("OpaquePass") 2170 2171 ssaoCam = vtki.new("CameraPass") 2172 ssaoCam.SetDelegatePass(opaque) 2173 2174 ssao = vtki.new("SSAOPass") 2175 ssao.SetRadius(radius) 2176 ssao.SetBias(bias) 2177 ssao.SetBlur(blur) 2178 ssao.SetKernelSize(samples) 2179 ssao.SetDelegatePass(ssaoCam) 2180 2181 translucent = vtki.new("TranslucentPass") 2182 2183 volpass = vtki.new("VolumetricPass") 2184 ddp = vtki.new("DualDepthPeelingPass") 2185 ddp.SetTranslucentPass(translucent) 2186 ddp.SetVolumetricPass(volpass) 2187 2188 over = vtki.new("OverlayPass") 2189 2190 collection = vtki.new("RenderPassCollection") 2191 collection.AddItem(lights) 2192 collection.AddItem(ssao) 2193 collection.AddItem(ddp) 2194 collection.AddItem(over) 2195 2196 sequence = vtki.new("SequencePass") 2197 sequence.SetPasses(collection) 2198 2199 cam = vtki.new("CameraPass") 2200 cam.SetDelegatePass(sequence) 2201 2202 self.renderer.SetPass(cam) 2203 return self 2204 2205 def add_depth_of_field(self, autofocus=True) -> Self: 2206 """Add a depth of field effect in the scene.""" 2207 lights = vtki.new("LightsPass") 2208 2209 opaque = vtki.new("OpaquePass") 2210 2211 dofCam = vtki.new("CameraPass") 2212 dofCam.SetDelegatePass(opaque) 2213 2214 dof = vtki.new("DepthOfFieldPass") 2215 dof.SetAutomaticFocalDistance(autofocus) 2216 dof.SetDelegatePass(dofCam) 2217 2218 collection = vtki.new("RenderPassCollection") 2219 collection.AddItem(lights) 2220 collection.AddItem(dof) 2221 2222 sequence = vtki.new("SequencePass") 2223 sequence.SetPasses(collection) 2224 2225 cam = vtki.new("CameraPass") 2226 cam.SetDelegatePass(sequence) 2227 2228 self.renderer.SetPass(cam) 2229 return self 2230 2231 def _add_skybox(self, hdrfile: str) -> Self: 2232 # many hdr files are at https://polyhaven.com/all 2233 2234 reader = vtki.new("HDRReader") 2235 # Check the image can be read. 2236 if not reader.CanReadFile(hdrfile): 2237 vedo.logger.error(f"Cannot read HDR file {hdrfile}") 2238 return self 2239 reader.SetFileName(hdrfile) 2240 reader.Update() 2241 2242 texture = vtki.vtkTexture() 2243 texture.SetColorModeToDirectScalars() 2244 texture.SetInputData(reader.GetOutput()) 2245 2246 # Convert to a cube map 2247 tcm = vtki.new("EquirectangularToCubeMapTexture") 2248 tcm.SetInputTexture(texture) 2249 # Enable mipmapping to handle HDR image 2250 tcm.MipmapOn() 2251 tcm.InterpolateOn() 2252 2253 self.renderer.SetEnvironmentTexture(tcm) 2254 self.renderer.UseImageBasedLightingOn() 2255 self.skybox = vtki.new("Skybox") 2256 self.skybox.SetTexture(tcm) 2257 self.renderer.AddActor(self.skybox) 2258 return self 2259 2260 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None) -> "vedo.addons.RendererFrame": 2261 """ 2262 Add a frame to the renderer subwindow. 2263 2264 Arguments: 2265 c : (color) 2266 color name or index 2267 alpha : (float) 2268 opacity level 2269 lw : (int) 2270 line width in pixels. 2271 padding : (float) 2272 padding space in pixels. 2273 """ 2274 if c is None: # automatic black or white 2275 c = (0.9, 0.9, 0.9) 2276 if self.renderer: 2277 if np.sum(self.renderer.GetBackground()) > 1.5: 2278 c = (0.1, 0.1, 0.1) 2279 renf = addons.RendererFrame(c, alpha, lw, padding) 2280 if renf: 2281 self.renderer.AddActor(renf) 2282 return renf 2283 2284 def add_hover_legend( 2285 self, 2286 at=None, 2287 c=None, 2288 pos="bottom-left", 2289 font="Calco", 2290 s=0.75, 2291 bg="auto", 2292 alpha=0.1, 2293 maxlength=24, 2294 use_info=False, 2295 ) -> int: 2296 """ 2297 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2298 2299 The created text object are stored in `plotter.hover_legends`. 2300 2301 Returns: 2302 the id of the callback function. 2303 2304 Arguments: 2305 c : (color) 2306 Text color. If None then black or white is chosen automatically 2307 pos : (str) 2308 text positioning 2309 font : (str) 2310 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2311 s : (float) 2312 text size scale 2313 bg : (color) 2314 background color of the 2D box containing the text 2315 alpha : (float) 2316 box transparency 2317 maxlength : (int) 2318 maximum number of characters per line 2319 use_info : (bool) 2320 visualize the content of the `obj.info` attribute 2321 2322 Examples: 2323 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2324 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2325 2326 ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg) 2327 """ 2328 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2329 2330 if at is None: 2331 at = self.renderers.index(self.renderer) 2332 2333 def _legfunc(evt): 2334 if not evt.object or not self.renderer or at != evt.at: 2335 if hoverlegend.mapper.GetInput(): # clear and return 2336 hoverlegend.mapper.SetInput("") 2337 self.render() 2338 return 2339 2340 if use_info: 2341 if hasattr(evt.object, "info"): 2342 t = str(evt.object.info) 2343 else: 2344 return 2345 else: 2346 t, tp = "", "" 2347 if evt.isMesh: 2348 tp = "Mesh " 2349 elif evt.isPoints: 2350 tp = "Points " 2351 elif evt.isVolume: 2352 tp = "Volume " 2353 elif evt.isImage: 2354 tp = "Image " 2355 elif evt.isAssembly: 2356 tp = "Assembly " 2357 else: 2358 return 2359 2360 if evt.isAssembly: 2361 if not evt.object.name: 2362 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2363 else: 2364 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2365 else: 2366 if evt.object.name: 2367 t += f"{tp}name" 2368 if evt.isPoints: 2369 t += " " 2370 if evt.isMesh: 2371 t += " " 2372 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2373 2374 if evt.object.filename: 2375 t += f"{tp}filename: " 2376 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2377 t += "\n" 2378 if not evt.object.file_size: 2379 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2380 if evt.object.file_size: 2381 t += " : " 2382 sz, created = evt.object.file_size, evt.object.created 2383 t += f"{created[4:-5]} ({sz})" + "\n" 2384 2385 if evt.isPoints: 2386 indata = evt.object.dataset 2387 if indata.GetNumberOfPoints(): 2388 t += ( 2389 f"#points/cells: {indata.GetNumberOfPoints()}" 2390 f" / {indata.GetNumberOfCells()}" 2391 ) 2392 pdata = indata.GetPointData() 2393 cdata = indata.GetCellData() 2394 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2395 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2396 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2397 t += " *" 2398 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2399 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2400 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2401 t += " *" 2402 2403 if evt.isImage: 2404 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2405 t += f"\nImage shape: {evt.object.shape}" 2406 pcol = self.color_picker(evt.picked2d) 2407 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2408 2409 # change box color if needed in 'auto' mode 2410 if evt.isPoints and "auto" in str(bg): 2411 actcol = evt.object.properties.GetColor() 2412 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2413 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2414 2415 # adapt to changes in bg color 2416 bgcol = self.renderers[at].GetBackground() 2417 _bgcol = c 2418 if _bgcol is None: # automatic black or white 2419 _bgcol = (0.9, 0.9, 0.9) 2420 if sum(bgcol) > 1.5: 2421 _bgcol = (0.1, 0.1, 0.1) 2422 if len(set(_bgcol).intersection(bgcol)) < 3: 2423 hoverlegend.color(_bgcol) 2424 2425 if hoverlegend.mapper.GetInput() != t: 2426 hoverlegend.mapper.SetInput(t) 2427 self.interactor.Render() 2428 2429 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2430 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2431 2432 self.add(hoverlegend, at=at) 2433 self.hover_legends.append(hoverlegend) 2434 idcall = self.add_callback("MouseMove", _legfunc) 2435 return idcall 2436 2437 def add_scale_indicator( 2438 self, 2439 pos=(0.7, 0.05), 2440 s=0.02, 2441 length=2, 2442 lw=4, 2443 c="k1", 2444 alpha=1, 2445 units="", 2446 gap=0.05, 2447 ) -> Union["vedo.visual.Actor2D", None]: 2448 """ 2449 Add a Scale Indicator. Only works in parallel mode (no perspective). 2450 2451 Arguments: 2452 pos : (list) 2453 fractional (x,y) position on the screen. 2454 s : (float) 2455 size of the text. 2456 length : (float) 2457 length of the line. 2458 units : (str) 2459 string to show units. 2460 gap : (float) 2461 separation of line and text. 2462 2463 Example: 2464 ```python 2465 from vedo import settings, Cube, Plotter 2466 settings.use_parallel_projection = True # or else it does not make sense! 2467 cube = Cube().alpha(0.2) 2468 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2469 plt.add_scale_indicator(units='um', c='blue4') 2470 plt.show(cube, "Scale indicator with units").close() 2471 ``` 2472 ![](https://vedo.embl.es/images/feats/scale_indicator.png) 2473 """ 2474 # Note that this cannot go in addons.py 2475 # because it needs callbacks and window size 2476 if not self.interactor: 2477 return None 2478 2479 ppoints = vtki.vtkPoints() # Generate the polyline 2480 psqr = [[0.0, gap], [length / 10, gap]] 2481 dd = psqr[1][0] - psqr[0][0] 2482 for i, pt in enumerate(psqr): 2483 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2484 lines = vtki.vtkCellArray() 2485 lines.InsertNextCell(len(psqr)) 2486 for i in range(len(psqr)): 2487 lines.InsertCellPoint(i) 2488 pd = vtki.vtkPolyData() 2489 pd.SetPoints(ppoints) 2490 pd.SetLines(lines) 2491 2492 wsx, wsy = self.window.GetSize() 2493 if not self.camera.GetParallelProjection(): 2494 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2495 return None 2496 2497 rlabel = vtki.new("VectorText") 2498 rlabel.SetText("scale") 2499 tf = vtki.new("TransformPolyDataFilter") 2500 tf.SetInputConnection(rlabel.GetOutputPort()) 2501 t = vtki.vtkTransform() 2502 t.Scale(s * wsy / wsx, s, 1) 2503 tf.SetTransform(t) 2504 2505 app = vtki.new("AppendPolyData") 2506 app.AddInputConnection(tf.GetOutputPort()) 2507 app.AddInputData(pd) 2508 2509 mapper = vtki.new("PolyDataMapper2D") 2510 mapper.SetInputConnection(app.GetOutputPort()) 2511 cs = vtki.vtkCoordinate() 2512 cs.SetCoordinateSystem(1) 2513 mapper.SetTransformCoordinate(cs) 2514 2515 fractor = vedo.visual.Actor2D() 2516 csys = fractor.GetPositionCoordinate() 2517 csys.SetCoordinateSystem(3) 2518 fractor.SetPosition(pos) 2519 fractor.SetMapper(mapper) 2520 fractor.GetProperty().SetColor(vedo.get_color(c)) 2521 fractor.GetProperty().SetOpacity(alpha) 2522 fractor.GetProperty().SetLineWidth(lw) 2523 fractor.GetProperty().SetDisplayLocationToForeground() 2524 2525 def sifunc(iren, ev): 2526 wsx, wsy = self.window.GetSize() 2527 ps = self.camera.GetParallelScale() 2528 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2529 if units: 2530 newtxt += " " + units 2531 if rlabel.GetText() != newtxt: 2532 rlabel.SetText(newtxt) 2533 2534 self.renderer.AddActor(fractor) 2535 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2536 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2537 self.interactor.AddObserver("InteractionEvent", sifunc) 2538 sifunc(0, 0) 2539 return fractor 2540 2541 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2542 """ 2543 Create an Event object with information of what was clicked. 2544 2545 If `enable_picking` is False, no picking will be performed. 2546 This can be useful to avoid double picking when using buttons. 2547 """ 2548 if not self.interactor: 2549 return Event() 2550 2551 if len(pos) > 0: 2552 x, y = pos 2553 self.interactor.SetEventPosition(pos) 2554 else: 2555 x, y = self.interactor.GetEventPosition() 2556 self.renderer = self.interactor.FindPokedRenderer(x, y) 2557 2558 self.picked2d = (x, y) 2559 2560 key = self.interactor.GetKeySym() 2561 2562 if key: 2563 if "_L" in key or "_R" in key: 2564 # skip things like Shift_R 2565 key = "" # better than None 2566 else: 2567 if self.interactor.GetShiftKey(): 2568 key = key.upper() 2569 2570 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2571 key = "underscore" 2572 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2573 key = "plus" 2574 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2575 key = "?" 2576 2577 if self.interactor.GetControlKey(): 2578 key = "Ctrl+" + key 2579 2580 if self.interactor.GetAltKey(): 2581 key = "Alt+" + key 2582 2583 if enable_picking: 2584 if not self.picker: 2585 self.picker = vtki.vtkPropPicker() 2586 2587 self.picker.PickProp(x, y, self.renderer) 2588 actor = self.picker.GetProp3D() 2589 # Note that GetProp3D already picks Assembly 2590 2591 xp, yp = self.interactor.GetLastEventPosition() 2592 dx, dy = x - xp, y - yp 2593 2594 delta3d = np.array([0, 0, 0]) 2595 2596 if actor: 2597 picked3d = np.array(self.picker.GetPickPosition()) 2598 2599 try: 2600 vobj = actor.retrieve_object() 2601 old_pt = np.asarray(vobj.picked3d) 2602 vobj.picked3d = picked3d 2603 delta3d = picked3d - old_pt 2604 except (AttributeError, TypeError): 2605 pass 2606 2607 else: 2608 picked3d = None 2609 2610 if not actor: # try 2D 2611 actor = self.picker.GetActor2D() 2612 2613 event = Event() 2614 event.name = ename 2615 event.title = self.title 2616 event.id = -1 # will be set by the timer wrapper function 2617 event.timerid = -1 # will be set by the timer wrapper function 2618 event.priority = -1 # will be set by the timer wrapper function 2619 event.time = time.time() 2620 event.at = self.renderers.index(self.renderer) 2621 event.keypress = key 2622 if enable_picking: 2623 try: 2624 event.object = actor.retrieve_object() 2625 except AttributeError: 2626 event.object = actor 2627 try: 2628 event.actor = actor.retrieve_object() # obsolete use object instead 2629 except AttributeError: 2630 event.actor = actor 2631 event.picked3d = picked3d 2632 event.picked2d = (x, y) 2633 event.delta2d = (dx, dy) 2634 event.angle2d = np.arctan2(dy, dx) 2635 event.speed2d = np.sqrt(dx * dx + dy * dy) 2636 event.delta3d = delta3d 2637 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2638 event.isPoints = isinstance(event.object, vedo.Points) 2639 event.isMesh = isinstance(event.object, vedo.Mesh) 2640 event.isAssembly = isinstance(event.object, vedo.Assembly) 2641 event.isVolume = isinstance(event.object, vedo.Volume) 2642 event.isImage = isinstance(event.object, vedo.Image) 2643 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2644 return event 2645 2646 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2647 """ 2648 Add a function to be executed while show() is active. 2649 2650 Return a unique id for the callback. 2651 2652 The callback function (see example below) exposes a dictionary 2653 with the following information: 2654 - `name`: event name, 2655 - `id`: event unique identifier, 2656 - `priority`: event priority (float), 2657 - `interactor`: the interactor object, 2658 - `at`: renderer nr. where the event occurred 2659 - `keypress`: key pressed as string 2660 - `actor`: object picked by the mouse 2661 - `picked3d`: point picked in world coordinates 2662 - `picked2d`: screen coords of the mouse pointer 2663 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2664 - `delta3d`: ...same but in 3D world coords 2665 - `angle2d`: angle of mouse movement on screen 2666 - `speed2d`: speed of mouse movement on screen 2667 - `speed3d`: speed of picked point in world coordinates 2668 - `isPoints`: True if of class 2669 - `isMesh`: True if of class 2670 - `isAssembly`: True if of class 2671 - `isVolume`: True if of class Volume 2672 - `isImage`: True if of class 2673 2674 If `enable_picking` is False, no picking will be performed. 2675 This can be useful to avoid double picking when using buttons. 2676 2677 Frequently used events are: 2678 - `KeyPress`, `KeyRelease`: listen to keyboard events 2679 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2680 - `MiddleButtonPress`, `MiddleButtonRelease` 2681 - `RightButtonPress`, `RightButtonRelease` 2682 - `MouseMove`: listen to mouse pointer changing position 2683 - `MouseWheelForward`, `MouseWheelBackward` 2684 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2685 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2686 - `ResetCamera`, `ResetCameraClippingRange` 2687 - `Error`, `Warning` 2688 - `Char` 2689 - `Timer` 2690 2691 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2692 2693 Example: 2694 ```python 2695 from vedo import * 2696 2697 def func(evt): 2698 # this function is called every time the mouse moves 2699 # (evt is a dotted dictionary) 2700 if not evt.object: 2701 return # no hit, return 2702 print("point coords =", evt.picked3d) 2703 # print(evt) # full event dump 2704 2705 elli = Ellipsoid() 2706 plt = Plotter(axes=1) 2707 plt.add_callback('mouse hovering', func) 2708 plt.show(elli).close() 2709 ``` 2710 2711 Examples: 2712 - [spline_draw.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw.py) 2713 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2714 2715 ![](https://vedo.embl.es/images/advanced/spline_draw.png) 2716 2717 - ..and many others! 2718 """ 2719 from vtkmodules.util.misc import calldata_type 2720 2721 if not self.interactor: 2722 return 0 2723 2724 if vedo.settings.dry_run_mode >= 1: 2725 return 0 2726 2727 ######################################### 2728 @calldata_type(vtki.VTK_INT) 2729 def _func_wrap(iren, ename, timerid=None): 2730 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2731 event.timerid = timerid 2732 event.id = cid 2733 event.priority = priority 2734 self.last_event = event 2735 func(event) 2736 2737 ######################################### 2738 2739 event_name = utils.get_vtk_name_event(event_name) 2740 2741 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2742 # print(f"Registering event: {event_name} with id={cid}") 2743 return cid 2744 2745 def remove_callback(self, cid: Union[int, str]) -> Self: 2746 """ 2747 Remove a callback function by its id 2748 or a whole category of callbacks by their name. 2749 2750 Arguments: 2751 cid : (int, str) 2752 Unique id of the callback. 2753 If an event name is passed all callbacks of that type are removed. 2754 """ 2755 if self.interactor: 2756 if isinstance(cid, str): 2757 cid = utils.get_vtk_name_event(cid) 2758 self.interactor.RemoveObservers(cid) 2759 else: 2760 self.interactor.RemoveObserver(cid) 2761 return self 2762 2763 def remove_all_observers(self) -> Self: 2764 """ 2765 Remove all observers. 2766 2767 Example: 2768 ```python 2769 from vedo import * 2770 2771 def kfunc(event): 2772 print("Key pressed:", event.keypress) 2773 if event.keypress == 'q': 2774 plt.close() 2775 2776 def rfunc(event): 2777 if event.isImage: 2778 printc("Right-clicked!", event) 2779 plt.render() 2780 2781 img = Image(dataurl+"images/embryo.jpg") 2782 2783 plt = Plotter(size=(1050, 600)) 2784 plt.parallel_projection(True) 2785 plt.remove_all_observers() 2786 plt.add_callback("key press", kfunc) 2787 plt.add_callback("mouse right click", rfunc) 2788 plt.show("Right-Click Me! Press q to exit.", img) 2789 plt.close() 2790 ``` 2791 """ 2792 if self.interactor: 2793 self.interactor.RemoveAllObservers() 2794 return self 2795 2796 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2797 """ 2798 Start or stop an existing timer. 2799 2800 Arguments: 2801 action : (str) 2802 Either "create"/"start" or "destroy"/"stop" 2803 timer_id : (int) 2804 When stopping the timer, the ID of the timer as returned when created 2805 dt : (int) 2806 time in milliseconds between each repeated call 2807 one_shot : (bool) 2808 create a one shot timer of prescribed duration instead of a repeating one 2809 2810 Examples: 2811 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2812 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2813 2814 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 2815 """ 2816 if action in ("create", "start"): 2817 if timer_id is not None: 2818 vedo.logger.warning("you set a timer_id but it will be ignored.") 2819 if one_shot: 2820 timer_id = self.interactor.CreateOneShotTimer(dt) 2821 else: 2822 timer_id = self.interactor.CreateRepeatingTimer(dt) 2823 return timer_id 2824 2825 elif action in ("destroy", "stop"): 2826 if timer_id is not None: 2827 self.interactor.DestroyTimer(timer_id) 2828 else: 2829 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2830 else: 2831 e = f"in timer_callback(). Cannot understand action: {action}\n" 2832 e += " allowed actions are: ['start', 'stop']. Skipped." 2833 vedo.logger.error(e) 2834 return timer_id 2835 2836 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2837 """ 2838 Add a callback function that will be called when an event occurs. 2839 Consider using `add_callback()` instead. 2840 """ 2841 if not self.interactor: 2842 return -1 2843 event_name = utils.get_vtk_name_event(event_name) 2844 idd = self.interactor.AddObserver(event_name, func, priority) 2845 return idd 2846 2847 def compute_world_coordinate( 2848 self, 2849 pos2d: MutableSequence[float], 2850 at=None, 2851 objs=(), 2852 bounds=(), 2853 offset=None, 2854 pixeltol=None, 2855 worldtol=None, 2856 ) -> np.ndarray: 2857 """ 2858 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2859 If a set of meshes is passed then points are placed onto these. 2860 2861 Arguments: 2862 pos2d : (list) 2863 2D screen coordinates point. 2864 at : (int) 2865 renderer number. 2866 objs : (list) 2867 list of Mesh objects to project the point onto. 2868 bounds : (list) 2869 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2870 offset : (float) 2871 specify an offset value. 2872 pixeltol : (int) 2873 screen tolerance in pixels. 2874 worldtol : (float) 2875 world coordinates tolerance. 2876 2877 Returns: 2878 numpy array, the point in 3D world coordinates. 2879 2880 Examples: 2881 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2882 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2883 2884 ![](https://vedo.embl.es/images/basic/mousehover3.jpg) 2885 """ 2886 if at is not None: 2887 renderer = self.renderers[at] 2888 else: 2889 renderer = self.renderer 2890 2891 if not objs: 2892 pp = vtki.vtkFocalPlanePointPlacer() 2893 else: 2894 pps = vtki.vtkPolygonalSurfacePointPlacer() 2895 for ob in objs: 2896 pps.AddProp(ob.actor) 2897 pp = pps # type: ignore 2898 2899 if len(bounds) == 6: 2900 pp.SetPointBounds(bounds) 2901 if pixeltol: 2902 pp.SetPixelTolerance(pixeltol) 2903 if worldtol: 2904 pp.SetWorldTolerance(worldtol) 2905 if offset: 2906 pp.SetOffset(offset) 2907 2908 worldPos: MutableSequence[float] = [0, 0, 0] 2909 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2910 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2911 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2912 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2913 return np.array(worldPos) 2914 2915 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2916 """ 2917 Given a 3D points in the current renderer (or full window), 2918 find the screen pixel coordinates. 2919 2920 Example: 2921 ```python 2922 from vedo import * 2923 2924 elli = Ellipsoid().point_size(5) 2925 2926 plt = Plotter() 2927 plt.show(elli, "Press q to continue and print the info") 2928 2929 xyscreen = plt.compute_screen_coordinates(elli) 2930 print('xyscreen coords:', xyscreen) 2931 2932 # simulate an event happening at one point 2933 event = plt.fill_event(pos=xyscreen[123]) 2934 print(event) 2935 ``` 2936 """ 2937 try: 2938 obj = obj.vertices 2939 except AttributeError: 2940 pass 2941 2942 if utils.is_sequence(obj): 2943 pts = obj 2944 p2d = [] 2945 cs = vtki.vtkCoordinate() 2946 cs.SetCoordinateSystemToWorld() 2947 cs.SetViewport(self.renderer) 2948 for p in pts: 2949 cs.SetValue(p) 2950 if full_window: 2951 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2952 else: 2953 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2954 return np.array(p2d, dtype=int) 2955 2956 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2957 """ 2958 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2959 2960 Returns a frustum Mesh that contains the visible field of view. 2961 This can be used to select objects in a scene or select vertices. 2962 2963 Example: 2964 ```python 2965 from vedo import * 2966 2967 settings.enable_default_mouse_callbacks = False 2968 2969 def mode_select(objs): 2970 print("Selected objects:", objs) 2971 d0 = mode.start_x, mode.start_y # display coords 2972 d1 = mode.end_x, mode.end_y 2973 2974 frustum = plt.pick_area(d0, d1) 2975 col = np.random.randint(0, 10) 2976 infru = frustum.inside_points(mesh) 2977 infru.point_size(10).color(col) 2978 plt.add(frustum, infru).render() 2979 2980 mesh = Mesh(dataurl+"cow.vtk") 2981 mesh.color("k5").linewidth(1) 2982 2983 mode = interactor_modes.BlenderStyle() 2984 mode.callback_select = mode_select 2985 2986 plt = Plotter().user_mode(mode) 2987 plt.show(mesh, axes=1) 2988 ``` 2989 """ 2990 if at is not None: 2991 ren = self.renderers[at] 2992 else: 2993 ren = self.renderer 2994 area_picker = vtki.vtkAreaPicker() 2995 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 2996 planes = area_picker.GetFrustum() 2997 2998 fru = vtki.new("FrustumSource") 2999 fru.SetPlanes(planes) 3000 fru.ShowLinesOff() 3001 fru.Update() 3002 3003 afru = vedo.Mesh(fru.GetOutput()) 3004 afru.alpha(0.1).lw(1).pickable(False) 3005 afru.name = "Frustum" 3006 return afru 3007 3008 def _scan_input_return_acts(self, objs) -> Any: 3009 # scan the input and return a list of actors 3010 if not utils.is_sequence(objs): 3011 objs = [objs] 3012 3013 ################# 3014 wannabe_acts = [] 3015 for a in objs: 3016 3017 try: 3018 wannabe_acts.append(a.actor) 3019 except AttributeError: 3020 wannabe_acts.append(a) # already actor 3021 3022 try: 3023 wannabe_acts.append(a.scalarbar) 3024 except AttributeError: 3025 pass 3026 3027 try: 3028 for sh in a.shadows: 3029 wannabe_acts.append(sh.actor) 3030 except AttributeError: 3031 pass 3032 3033 try: 3034 wannabe_acts.append(a.trail.actor) 3035 if a.trail.shadows: # trails may also have shadows 3036 for sh in a.trail.shadows: 3037 wannabe_acts.append(sh.actor) 3038 except AttributeError: 3039 pass 3040 3041 ################# 3042 scanned_acts = [] 3043 for a in wannabe_acts: # scan content of list 3044 3045 if a is None: 3046 pass 3047 3048 elif isinstance(a, (vtki.vtkActor, vtki.vtkActor2D)): 3049 scanned_acts.append(a) 3050 3051 elif isinstance(a, str): 3052 # assume a 2D comment was given 3053 changed = False # check if one already exists so to just update text 3054 if self.renderer: # might be jupyter 3055 acs = self.renderer.GetActors2D() 3056 acs.InitTraversal() 3057 for i in range(acs.GetNumberOfItems()): 3058 act = acs.GetNextItem() 3059 if isinstance(act, vedo.shapes.Text2D): 3060 aposx, aposy = act.GetPosition() 3061 if aposx < 0.01 and aposy > 0.99: # "top-left" 3062 act.text(a) # update content! no appending nada 3063 changed = True 3064 break 3065 if not changed: 3066 out = vedo.shapes.Text2D(a) # append a new one 3067 scanned_acts.append(out) 3068 # scanned_acts.append(vedo.shapes.Text2D(a)) # naive version 3069 3070 elif isinstance(a, vtki.vtkPolyData): 3071 scanned_acts.append(vedo.Mesh(a).actor) 3072 3073 elif isinstance(a, vtki.vtkImageData): 3074 scanned_acts.append(vedo.Volume(a).actor) 3075 3076 elif isinstance(a, vedo.RectilinearGrid): 3077 scanned_acts.append(a.actor) 3078 3079 elif isinstance(a, vedo.StructuredGrid): 3080 scanned_acts.append(a.actor) 3081 3082 elif isinstance(a, vtki.vtkLight): 3083 scanned_acts.append(a) 3084 3085 elif isinstance(a, vedo.visual.LightKit): 3086 a.lightkit.AddLightsToRenderer(self.renderer) 3087 3088 elif isinstance(a, vtki.get_class("MultiBlockDataSet")): 3089 for i in range(a.GetNumberOfBlocks()): 3090 b = a.GetBlock(i) 3091 if isinstance(b, vtki.vtkPolyData): 3092 scanned_acts.append(vedo.Mesh(b).actor) 3093 elif isinstance(b, vtki.vtkImageData): 3094 scanned_acts.append(vedo.Volume(b).actor) 3095 3096 elif isinstance(a, (vtki.vtkProp, vtki.vtkInteractorObserver)): 3097 scanned_acts.append(a) 3098 3099 elif "trimesh" in str(type(a)): 3100 scanned_acts.append(utils.trimesh2vedo(a)) 3101 3102 elif "meshlab" in str(type(a)): 3103 if "MeshSet" in str(type(a)): 3104 for i in range(a.number_meshes()): 3105 if a.mesh_id_exists(i): 3106 scanned_acts.append(utils.meshlab2vedo(a.mesh(i))) 3107 else: 3108 scanned_acts.append(utils.meshlab2vedo(a)) 3109 3110 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 3111 import vedo.dolfin as vdlf 3112 3113 scanned_acts.append(vdlf.IMesh(a).actor) 3114 3115 elif "madcad" in str(type(a)): 3116 scanned_acts.append(utils.madcad2vedo(a).actor) 3117 3118 elif "TetgenIO" in str(type(a)): 3119 scanned_acts.append(vedo.TetMesh(a).shrink(0.9).c("pink7").actor) 3120 3121 elif "matplotlib.figure.Figure" in str(type(a)): 3122 scanned_acts.append(vedo.Image(a).clone2d("top-right", 0.6)) 3123 3124 else: 3125 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 3126 3127 return scanned_acts 3128 3129 def show( 3130 self, 3131 *objects, 3132 at=None, 3133 axes=None, 3134 resetcam=None, 3135 zoom=False, 3136 interactive=None, 3137 viewup="", 3138 azimuth=0.0, 3139 elevation=0.0, 3140 roll=0.0, 3141 camera=None, 3142 mode=None, 3143 rate=None, 3144 bg=None, 3145 bg2=None, 3146 size=None, 3147 title=None, 3148 screenshot="", 3149 ) -> Any: 3150 """ 3151 Render a list of objects. 3152 3153 Arguments: 3154 at : (int) 3155 number of the renderer to plot to, in case of more than one exists 3156 3157 axes : (int) 3158 axis type-1 can be fully customized by passing a dictionary. 3159 Check `addons.Axes()` for the full list of options. 3160 set the type of axes to be shown: 3161 - 0, no axes 3162 - 1, draw three gray grid walls 3163 - 2, show cartesian axes from (0,0,0) 3164 - 3, show positive range of cartesian axes from (0,0,0) 3165 - 4, show a triad at bottom left 3166 - 5, show a cube at bottom left 3167 - 6, mark the corners of the bounding box 3168 - 7, draw a 3D ruler at each side of the cartesian axes 3169 - 8, show the `vtkCubeAxesActor` object 3170 - 9, show the bounding box outLine 3171 - 10, show three circles representing the maximum bounding box 3172 - 11, show a large grid on the x-y plane 3173 - 12, show polar axes 3174 - 13, draw a simple ruler at the bottom of the window 3175 3176 azimuth/elevation/roll : (float) 3177 move camera accordingly the specified value 3178 3179 viewup: str, list 3180 either `['x', 'y', 'z']` or a vector to set vertical direction 3181 3182 resetcam : (bool) 3183 re-adjust camera position to fit objects 3184 3185 camera : (dict, vtkCamera) 3186 camera parameters can further be specified with a dictionary 3187 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3188 - pos, `(list)`, the position of the camera in world coordinates 3189 - focal_point `(list)`, the focal point of the camera in world coordinates 3190 - viewup `(list)`, the view up direction for the camera 3191 - distance `(float)`, set the focal point to the specified distance from the camera position. 3192 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3193 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3194 in world-coordinate distances. The default is 1. 3195 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3196 This method has no effect in perspective projection mode. 3197 3198 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3199 plane to be set a distance 'thickness' beyond the near clipping plane. 3200 3201 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3202 measured in degrees. The default angle is 30 degrees. 3203 This method has no effect in parallel projection mode. 3204 The formula for setting the angle up for perfect perspective viewing is: 3205 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3206 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3207 3208 interactive : (bool) 3209 pause and interact with window (True) or continue execution (False) 3210 3211 rate : (float) 3212 maximum rate of `show()` in Hertz 3213 3214 mode : (int, str) 3215 set the type of interaction: 3216 - 0 = TrackballCamera [default] 3217 - 1 = TrackballActor 3218 - 2 = JoystickCamera 3219 - 3 = JoystickActor 3220 - 4 = Flight 3221 - 5 = RubberBand2D 3222 - 6 = RubberBand3D 3223 - 7 = RubberBandZoom 3224 - 8 = Terrain 3225 - 9 = Unicam 3226 - 10 = Image 3227 - Check out `vedo.interaction_modes` for more options. 3228 3229 bg : (str, list) 3230 background color in RGB format, or string name 3231 3232 bg2 : (str, list) 3233 second background color to create a gradient background 3234 3235 size : (str, list) 3236 size of the window, e.g. size="fullscreen", or size=[600,400] 3237 3238 title : (str) 3239 window title text 3240 3241 screenshot : (str) 3242 save a screenshot of the window to file 3243 """ 3244 3245 if vedo.settings.dry_run_mode >= 2: 3246 return self 3247 3248 if self.wx_widget: 3249 return self 3250 3251 if self.renderers: # in case of notebooks 3252 3253 if at is None: 3254 at = self.renderers.index(self.renderer) 3255 3256 else: 3257 3258 if at >= len(self.renderers): 3259 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3260 vedo.logger.error(t) 3261 return self 3262 3263 self.renderer = self.renderers[at] 3264 3265 if title is not None: 3266 self.title = title 3267 3268 if size is not None: 3269 self.size = size 3270 if self.size[0] == "f": # full screen 3271 self.size = "fullscreen" 3272 self.window.SetFullScreen(True) 3273 self.window.BordersOn() 3274 else: 3275 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3276 3277 if vedo.settings.default_backend == "vtk": 3278 if str(bg).endswith(".hdr"): 3279 self._add_skybox(bg) 3280 else: 3281 if bg is not None: 3282 self.backgrcol = vedo.get_color(bg) 3283 self.renderer.SetBackground(self.backgrcol) 3284 if bg2 is not None: 3285 self.renderer.GradientBackgroundOn() 3286 self.renderer.SetBackground2(vedo.get_color(bg2)) 3287 3288 if axes is not None: 3289 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3290 objects = list(objects) 3291 objects.append(axes) # move it into the list of normal things to show 3292 axes = 0 3293 self.axes = axes 3294 3295 if interactive is not None: 3296 self._interactive = interactive 3297 if self.offscreen: 3298 self._interactive = False 3299 3300 # camera stuff 3301 if resetcam is not None: 3302 self.resetcam = resetcam 3303 3304 if camera is not None: 3305 self.resetcam = False 3306 viewup = "" 3307 if isinstance(camera, vtki.vtkCamera): 3308 cameracopy = vtki.vtkCamera() 3309 cameracopy.DeepCopy(camera) 3310 self.camera = cameracopy 3311 else: 3312 self.camera = utils.camera_from_dict(camera) 3313 3314 self.add(objects) 3315 3316 # Backend ############################################################### 3317 if vedo.settings.default_backend in ["k3d"]: 3318 return backends.get_notebook_backend(self.objects) 3319 ######################################################################### 3320 3321 for ia in utils.flatten(objects): 3322 try: 3323 # fix gray color labels and title to white or black 3324 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3325 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3326 c = (0.9, 0.9, 0.9) 3327 if np.sum(self.renderer.GetBackground()) > 1.5: 3328 c = (0.1, 0.1, 0.1) 3329 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3330 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3331 except AttributeError: 3332 pass 3333 3334 if self.sharecam: 3335 for r in self.renderers: 3336 r.SetActiveCamera(self.camera) 3337 3338 if self.axes is not None: 3339 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3340 bns = self.renderer.ComputeVisiblePropBounds() 3341 addons.add_global_axes(self.axes, bounds=bns) 3342 3343 # Backend ############################################################### 3344 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3345 return backends.get_notebook_backend() 3346 ######################################################################### 3347 3348 if self.resetcam: 3349 self.renderer.ResetCamera() 3350 3351 if len(self.renderers) > 1: 3352 self.add_renderer_frame() 3353 3354 if vedo.settings.default_backend == "2d" and not zoom: 3355 zoom = "tightest" 3356 3357 if zoom: 3358 if zoom == "tight": 3359 self.reset_camera(tight=0.04) 3360 elif zoom == "tightest": 3361 self.reset_camera(tight=0.0001) 3362 else: 3363 self.camera.Zoom(zoom) 3364 if elevation: 3365 self.camera.Elevation(elevation) 3366 if azimuth: 3367 self.camera.Azimuth(azimuth) 3368 if roll: 3369 self.camera.Roll(roll) 3370 3371 if len(viewup) > 0: 3372 b = self.renderer.ComputeVisiblePropBounds() 3373 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3374 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3375 if viewup == "x": 3376 sz = np.linalg.norm(sz) 3377 self.camera.SetViewUp([1, 0, 0]) 3378 self.camera.SetPosition(cm + sz) 3379 elif viewup == "y": 3380 sz = np.linalg.norm(sz) 3381 self.camera.SetViewUp([0, 1, 0]) 3382 self.camera.SetPosition(cm + sz) 3383 elif viewup == "z": 3384 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3385 self.camera.SetViewUp([0, 0, 1]) 3386 self.camera.SetPosition(cm + 2 * sz) 3387 elif utils.is_sequence(viewup): 3388 sz = np.linalg.norm(sz) 3389 self.camera.SetViewUp(viewup) 3390 cpos = np.cross([0, 1, 0], viewup) 3391 self.camera.SetPosition(cm - 2 * sz * cpos) 3392 3393 self.renderer.ResetCameraClippingRange() 3394 3395 self.initialize_interactor() 3396 3397 if vedo.settings.immediate_rendering: 3398 self.window.Render() ##################### <-------------- Render 3399 3400 if self.interactor: # can be offscreen or not the vtk backend.. 3401 3402 self.window.SetWindowName(self.title) 3403 3404 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3405 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3406 # print(pic.dataset)# Array 0 name PNGImage 3407 # self.window.SetIcon(pic.dataset) 3408 3409 try: 3410 # Needs "pip install pyobjc" on Mac OSX 3411 if ( 3412 self._cocoa_initialized is False 3413 and "Darwin" in vedo.sys_platform 3414 and not self.offscreen 3415 ): 3416 self._cocoa_initialized = True 3417 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3418 pid = os.getpid() 3419 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3420 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3421 except: 3422 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3423 pass 3424 3425 # Set the interaction style 3426 if mode is not None: 3427 self.user_mode(mode) 3428 if self.qt_widget and mode is None: 3429 self.user_mode(0) 3430 3431 if screenshot: 3432 self.screenshot(screenshot) 3433 3434 if self._interactive: 3435 self.interactor.Start() 3436 if self._must_close_now: 3437 self.interactor.GetRenderWindow().Finalize() 3438 self.interactor.TerminateApp() 3439 self.camera = None 3440 self.renderer = None 3441 self.renderers = [] 3442 self.window = None 3443 self.interactor = None 3444 return self 3445 3446 if rate: 3447 if self.clock is None: # set clock and limit rate 3448 self._clockt0 = time.time() 3449 self.clock = 0.0 3450 else: 3451 t = time.time() - self._clockt0 3452 elapsed = t - self.clock 3453 mint = 1.0 / rate 3454 if elapsed < mint: 3455 time.sleep(mint - elapsed) 3456 self.clock = time.time() - self._clockt0 3457 3458 # 2d #################################################################### 3459 if vedo.settings.default_backend == "2d": 3460 return backends.get_notebook_backend() 3461 ######################################################################### 3462 3463 return self 3464 3465 3466 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3467 """Add a draggable inset space into a renderer. 3468 3469 Arguments: 3470 at : (int) 3471 specify the renderer number 3472 pos : (list) 3473 icon position in the range [1-4] indicating one of the 4 corners, 3474 or it can be a tuple (x,y) as a fraction of the renderer size. 3475 size : (float) 3476 size of the square inset 3477 draggable : (bool) 3478 if True the subrenderer space can be dragged around 3479 c : (color) 3480 color of the inset frame when dragged 3481 3482 Examples: 3483 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3484 3485 ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg) 3486 """ 3487 if not self.interactor: 3488 return None 3489 3490 if not self.renderer: 3491 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3492 return None 3493 3494 options = dict(options) 3495 pos = options.pop("pos", 0) 3496 size = options.pop("size", 0.1) 3497 c = options.pop("c", "lb") 3498 at = options.pop("at", None) 3499 draggable = options.pop("draggable", True) 3500 3501 r, g, b = vedo.get_color(c) 3502 widget = vtki.vtkOrientationMarkerWidget() 3503 widget.SetOutlineColor(r, g, b) 3504 if len(objects) == 1: 3505 widget.SetOrientationMarker(objects[0].actor) 3506 else: 3507 widget.SetOrientationMarker(vedo.Assembly(objects)) 3508 3509 widget.SetInteractor(self.interactor) 3510 3511 if utils.is_sequence(pos): 3512 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3513 else: 3514 if pos < 2: 3515 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3516 elif pos == 2: 3517 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3518 elif pos == 3: 3519 widget.SetViewport(0, 0, size * 2, size * 2) 3520 elif pos == 4: 3521 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3522 widget.EnabledOn() 3523 widget.SetInteractive(draggable) 3524 if at is not None and at < len(self.renderers): 3525 widget.SetCurrentRenderer(self.renderers[at]) 3526 else: 3527 widget.SetCurrentRenderer(self.renderer) 3528 self.widgets.append(widget) 3529 return widget 3530 3531 def clear(self, at=None, deep=False) -> Self: 3532 """Clear the scene from all meshes and volumes.""" 3533 if at is not None: 3534 renderer = self.renderers[at] 3535 else: 3536 renderer = self.renderer 3537 if not renderer: 3538 return self 3539 3540 if deep: 3541 renderer.RemoveAllViewProps() 3542 else: 3543 for ob in set( 3544 self.get_meshes() 3545 + self.get_volumes() 3546 + self.objects 3547 + self.axes_instances 3548 ): 3549 if isinstance(ob, vedo.shapes.Text2D): 3550 continue 3551 self.remove(ob) 3552 try: 3553 if ob.scalarbar: 3554 self.remove(ob.scalarbar) 3555 except AttributeError: 3556 pass 3557 return self 3558 3559 def break_interaction(self) -> Self: 3560 """Break window interaction and return to the python execution flow""" 3561 if self.interactor: 3562 self.check_actors_trasform() 3563 self.interactor.ExitCallback() 3564 return self 3565 3566 def freeze(self, value=True) -> Self: 3567 """Freeze the current renderer. Use this with `sharecam=False`.""" 3568 if not self.interactor: 3569 return self 3570 if not self.renderer: 3571 return self 3572 self.renderer.SetInteractive(not value) 3573 return self 3574 3575 def user_mode(self, mode) -> Self: 3576 """ 3577 Modify the user interaction mode. 3578 3579 Examples: 3580 ```python 3581 from vedo import * 3582 mode = interactor_modes.MousePan() 3583 mesh = Mesh(dataurl+"cow.vtk") 3584 plt = Plotter().user_mode(mode) 3585 plt.show(mesh, axes=1) 3586 ``` 3587 See also: 3588 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3589 """ 3590 if not self.interactor: 3591 return self 3592 3593 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3594 # print("Current style:", curr_style) 3595 if curr_style.endswith("Actor"): 3596 self.check_actors_trasform() 3597 3598 if isinstance(mode, (str, int)): 3599 # Set the style of interaction 3600 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3601 if mode in (0, "TrackballCamera"): 3602 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3603 self.interactor.RemoveObservers("CharEvent") 3604 elif mode in (1, "TrackballActor"): 3605 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3606 elif mode in (2, "JoystickCamera"): 3607 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3608 elif mode in (3, "JoystickActor"): 3609 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3610 elif mode in (4, "Flight"): 3611 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3612 elif mode in (5, "RubberBand2D"): 3613 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3614 elif mode in (6, "RubberBand3D"): 3615 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3616 elif mode in (7, "RubberBandZoom"): 3617 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3618 elif mode in (8, "Terrain"): 3619 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3620 elif mode in (9, "Unicam"): 3621 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3622 elif mode in (10, "Image", "image", "2d"): 3623 astyle = vtki.new("InteractorStyleImage") 3624 astyle.SetInteractionModeToImage3D() 3625 self.interactor.SetInteractorStyle(astyle) 3626 else: 3627 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3628 3629 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3630 # set a custom interactor style 3631 if hasattr(mode, "interactor"): 3632 mode.interactor = self.interactor 3633 mode.renderer = self.renderer # type: ignore 3634 mode.SetInteractor(self.interactor) 3635 mode.SetDefaultRenderer(self.renderer) 3636 self.interactor.SetInteractorStyle(mode) 3637 3638 return self 3639 3640 def close(self) -> Self: 3641 """Close the plotter.""" 3642 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3643 vedo.last_figure = None 3644 self.last_event = None 3645 self.sliders = [] 3646 self.buttons = [] 3647 self.widgets = [] 3648 self.hover_legends = [] 3649 self.background_renderer = None 3650 self._extralight = None 3651 3652 self.hint_widget = None 3653 self.cutter_widget = None 3654 3655 if vedo.settings.dry_run_mode >= 2: 3656 return self 3657 3658 if not hasattr(self, "window"): 3659 return self 3660 if not self.window: 3661 return self 3662 if not hasattr(self, "interactor"): 3663 return self 3664 if not self.interactor: 3665 return self 3666 3667 ################################################### 3668 try: 3669 if "Darwin" in vedo.sys_platform: 3670 self.interactor.ProcessEvents() 3671 except: 3672 pass 3673 3674 self._must_close_now = True 3675 3676 if vedo.plotter_instance == self: 3677 vedo.plotter_instance = None 3678 3679 if self.interactor and self._interactive: 3680 self.break_interaction() 3681 elif self._must_close_now: 3682 # dont call ExitCallback here 3683 if self.interactor: 3684 self.break_interaction() 3685 self.interactor.GetRenderWindow().Finalize() 3686 self.interactor.TerminateApp() 3687 self.camera = None 3688 self.renderer = None 3689 self.renderers = [] 3690 self.window = None 3691 self.interactor = None 3692 return self 3693 3694 @property 3695 def camera(self): 3696 """Return the current active camera.""" 3697 if self.renderer: 3698 return self.renderer.GetActiveCamera() 3699 3700 @camera.setter 3701 def camera(self, cam): 3702 if self.renderer: 3703 if isinstance(cam, dict): 3704 cam = utils.camera_from_dict(cam) 3705 self.renderer.SetActiveCamera(cam) 3706 3707 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3708 """ 3709 Take a screenshot of the Plotter window. 3710 3711 Arguments: 3712 scale : (int) 3713 set image magnification as an integer multiplicating factor 3714 asarray : (bool) 3715 return a numpy array of the image instead of writing a file 3716 3717 Warning: 3718 If you get black screenshots try to set `interactive=False` in `show()` 3719 then call `screenshot()` and `plt.interactive()` afterwards. 3720 3721 Example: 3722 ```py 3723 from vedo import * 3724 sphere = Sphere().linewidth(1) 3725 plt = show(sphere, interactive=False) 3726 plt.screenshot('image.png') 3727 plt.interactive() 3728 plt.close() 3729 ``` 3730 3731 Example: 3732 ```py 3733 from vedo import * 3734 sphere = Sphere().linewidth(1) 3735 plt = show(sphere, interactive=False) 3736 plt.screenshot('anotherimage.png') 3737 plt.interactive() 3738 plt.close() 3739 ``` 3740 """ 3741 return vedo.file_io.screenshot(filename, scale, asarray) 3742 3743 def toimage(self, scale=1) -> "vedo.image.Image": 3744 """ 3745 Generate a `Image` object from the current rendering window. 3746 3747 Arguments: 3748 scale : (int) 3749 set image magnification as an integer multiplicating factor 3750 """ 3751 if vedo.settings.screeshot_large_image: 3752 w2if = vtki.new("RenderLargeImage") 3753 w2if.SetInput(self.renderer) 3754 w2if.SetMagnification(scale) 3755 else: 3756 w2if = vtki.new("WindowToImageFilter") 3757 w2if.SetInput(self.window) 3758 if hasattr(w2if, "SetScale"): 3759 w2if.SetScale(scale, scale) 3760 if vedo.settings.screenshot_transparent_background: 3761 w2if.SetInputBufferTypeToRGBA() 3762 w2if.ReadFrontBufferOff() # read from the back buffer 3763 w2if.Update() 3764 return vedo.image.Image(w2if.GetOutput()) 3765 3766 def export(self, filename="scene.npz", binary=False) -> Self: 3767 """ 3768 Export scene to file to HTML, X3D or Numpy file. 3769 3770 Examples: 3771 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3772 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3773 """ 3774 vedo.file_io.export_window(filename, binary=binary) 3775 return self 3776 3777 def color_picker(self, xy, verbose=False): 3778 """Pick color of specific (x,y) pixel on the screen.""" 3779 w2if = vtki.new("WindowToImageFilter") 3780 w2if.SetInput(self.window) 3781 w2if.ReadFrontBufferOff() 3782 w2if.Update() 3783 nx, ny = self.window.GetSize() 3784 varr = w2if.GetOutput().GetPointData().GetScalars() 3785 3786 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3787 x, y = int(xy[0]), int(xy[1]) 3788 if y < ny and x < nx: 3789 3790 rgb = arr[y, x] 3791 3792 if verbose: 3793 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3794 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3795 vedo.printc("█", c=[0, rgb[1], 0], end="") 3796 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3797 vedo.printc("] = ", end="") 3798 cnm = vedo.get_color_name(rgb) 3799 if np.sum(rgb) < 150: 3800 vedo.printc( 3801 rgb.tolist(), 3802 vedo.colors.rgb2hex(np.array(rgb) / 255), 3803 c="w", 3804 bc=rgb, 3805 invert=1, 3806 end="", 3807 ) 3808 vedo.printc(" -> " + cnm, invert=1, c="w") 3809 else: 3810 vedo.printc( 3811 rgb.tolist(), 3812 vedo.colors.rgb2hex(np.array(rgb) / 255), 3813 c=rgb, 3814 end="", 3815 ) 3816 vedo.printc(" -> " + cnm, c=cnm) 3817 3818 return rgb 3819 3820 return None 3821 3822 ####################################################################### 3823 def _default_mouseleftclick(self, iren, event) -> None: 3824 x, y = iren.GetEventPosition() 3825 renderer = iren.FindPokedRenderer(x, y) 3826 picker = vtki.vtkPropPicker() 3827 picker.PickProp(x, y, renderer) 3828 3829 self.renderer = renderer 3830 3831 clicked_actor = picker.GetActor() 3832 # clicked_actor2D = picker.GetActor2D() 3833 3834 # print('_default_mouseleftclick mouse at', x, y) 3835 # print("picked Volume:", [picker.GetVolume()]) 3836 # print("picked Actor2D:", [picker.GetActor2D()]) 3837 # print("picked Assembly:", [picker.GetAssembly()]) 3838 # print("picked Prop3D:", [picker.GetProp3D()]) 3839 3840 if not clicked_actor: 3841 clicked_actor = picker.GetAssembly() 3842 3843 if not clicked_actor: 3844 clicked_actor = picker.GetProp3D() 3845 3846 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3847 return 3848 3849 self.picked3d = picker.GetPickPosition() 3850 self.picked2d = np.array([x, y]) 3851 3852 if not clicked_actor: 3853 return 3854 3855 self.justremoved = None 3856 self.clicked_actor = clicked_actor 3857 3858 try: # might not be a vedo obj 3859 self.clicked_object = clicked_actor.retrieve_object() 3860 # save this info in the object itself 3861 self.clicked_object.picked3d = self.picked3d 3862 self.clicked_object.picked2d = self.picked2d 3863 except AttributeError: 3864 pass 3865 3866 # ----------- 3867 # if "Histogram1D" in picker.GetAssembly().__class__.__name__: 3868 # histo = picker.GetAssembly() 3869 # if histo.verbose: 3870 # x = self.picked3d[0] 3871 # idx = np.digitize(x, histo.edges) - 1 3872 # f = histo.frequencies[idx] 3873 # cn = histo.centers[idx] 3874 # vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3875 3876 ####################################################################### 3877 def _default_keypress(self, iren, event) -> None: 3878 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3879 3880 key = iren.GetKeySym() 3881 3882 if "_L" in key or "_R" in key: 3883 return 3884 3885 if iren.GetShiftKey(): 3886 key = key.upper() 3887 3888 if iren.GetControlKey(): 3889 key = "Ctrl+" + key 3890 3891 if iren.GetAltKey(): 3892 key = "Alt+" + key 3893 3894 ####################################################### 3895 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3896 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3897 # iren.GetKeyCode(), iren.GetRepeatCount()) 3898 ####################################################### 3899 3900 x, y = iren.GetEventPosition() 3901 renderer = iren.FindPokedRenderer(x, y) 3902 3903 if key in ["q", "Return"]: 3904 self.break_interaction() 3905 return 3906 3907 elif key in ["Ctrl+q", "Ctrl+w", "Escape"]: 3908 self.close() 3909 return 3910 3911 elif key == "F1": 3912 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3913 self.break_interaction() 3914 sys.exit(0) 3915 3916 elif key == "Down": 3917 if self.clicked_object and self.clicked_object in self.get_meshes(): 3918 self.clicked_object.alpha(0.02) 3919 if hasattr(self.clicked_object, "properties_backface"): 3920 bfp = self.clicked_actor.GetBackfaceProperty() 3921 self.clicked_object.properties_backface = bfp # save it 3922 self.clicked_actor.SetBackfaceProperty(None) 3923 else: 3924 for obj in self.get_meshes(): 3925 if obj: 3926 obj.alpha(0.02) 3927 bfp = obj.actor.GetBackfaceProperty() 3928 if bfp and hasattr(obj, "properties_backface"): 3929 obj.properties_backface = bfp 3930 obj.actor.SetBackfaceProperty(None) 3931 3932 elif key == "Left": 3933 if self.clicked_object and self.clicked_object in self.get_meshes(): 3934 ap = self.clicked_object.properties 3935 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3936 ap.SetOpacity(aal) 3937 bfp = self.clicked_actor.GetBackfaceProperty() 3938 if bfp and hasattr(self.clicked_object, "properties_backface"): 3939 self.clicked_object.properties_backface = bfp 3940 self.clicked_actor.SetBackfaceProperty(None) 3941 else: 3942 for a in self.get_meshes(): 3943 if a: 3944 ap = a.properties 3945 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3946 ap.SetOpacity(aal) 3947 bfp = a.actor.GetBackfaceProperty() 3948 if bfp and hasattr(a, "properties_backface"): 3949 a.properties_backface = bfp 3950 a.actor.SetBackfaceProperty(None) 3951 3952 elif key == "Right": 3953 if self.clicked_object and self.clicked_object in self.get_meshes(): 3954 ap = self.clicked_object.properties 3955 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3956 ap.SetOpacity(aal) 3957 if ( 3958 aal == 1 3959 and hasattr(self.clicked_object, "properties_backface") 3960 and self.clicked_object.properties_backface 3961 ): 3962 # put back 3963 self.clicked_actor.SetBackfaceProperty( 3964 self.clicked_object.properties_backface) 3965 else: 3966 for a in self.get_meshes(): 3967 if a: 3968 ap = a.properties 3969 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3970 ap.SetOpacity(aal) 3971 if aal == 1 and hasattr(a, "properties_backface") and a.properties_backface: 3972 a.actor.SetBackfaceProperty(a.properties_backface) 3973 3974 elif key == "Up": 3975 if self.clicked_object and self.clicked_object in self.get_meshes(): 3976 self.clicked_object.properties.SetOpacity(1) 3977 if hasattr(self.clicked_object, "properties_backface") and self.clicked_object.properties_backface: 3978 self.clicked_object.actor.SetBackfaceProperty(self.clicked_object.properties_backface) 3979 else: 3980 for a in self.get_meshes(): 3981 if a: 3982 a.properties.SetOpacity(1) 3983 if hasattr(a, "properties_backface") and a.properties_backface: 3984 a.actor.SetBackfaceProperty(a.properties_backface) 3985 3986 elif key == "P": 3987 if self.clicked_object and self.clicked_object in self.get_meshes(): 3988 objs = [self.clicked_object] 3989 else: 3990 objs = self.get_meshes() 3991 for ia in objs: 3992 try: 3993 ps = ia.properties.GetPointSize() 3994 if ps > 1: 3995 ia.properties.SetPointSize(ps - 1) 3996 ia.properties.SetRepresentationToPoints() 3997 except AttributeError: 3998 pass 3999 4000 elif key == "p": 4001 if self.clicked_object and self.clicked_object in self.get_meshes(): 4002 objs = [self.clicked_object] 4003 else: 4004 objs = self.get_meshes() 4005 for ia in objs: 4006 try: 4007 ps = ia.properties.GetPointSize() 4008 ia.properties.SetPointSize(ps + 2) 4009 ia.properties.SetRepresentationToPoints() 4010 except AttributeError: 4011 pass 4012 4013 elif key == "U": 4014 pval = renderer.GetActiveCamera().GetParallelProjection() 4015 renderer.GetActiveCamera().SetParallelProjection(not pval) 4016 if pval: 4017 renderer.ResetCamera() 4018 4019 elif key == "r": 4020 renderer.ResetCamera() 4021 4022 elif key == "h": 4023 msg = f" vedo {vedo.__version__}" 4024 msg += f" | vtk {vtki.vtkVersion().GetVTKVersion()}" 4025 msg += f" | numpy {np.__version__}" 4026 msg += f" | python {sys.version_info[0]}.{sys.version_info[1]}, press: " 4027 vedo.printc(msg.ljust(75), invert=True) 4028 msg = ( 4029 " i print info about the last clicked object \n" 4030 " I print color of the pixel under the mouse \n" 4031 " Y show the pipeline for this object as a graph \n" 4032 " <- -> use arrows to reduce/increase opacity \n" 4033 " x toggle mesh visibility \n" 4034 " w toggle wireframe/surface style \n" 4035 " l toggle surface edges visibility \n" 4036 " p/P hide surface faces and show only points \n" 4037 " 1-3 cycle surface color (2=light, 3=dark) \n" 4038 " 4 cycle color map (press shift-4 to go back) \n" 4039 " 5-6 cycle point-cell arrays (shift to go back) \n" 4040 " 7-8 cycle background and gradient color \n" 4041 " 09+- cycle axes styles (on keypad, or press +/-) \n" 4042 " k cycle available lighting styles \n" 4043 " K toggle shading as flat or phong \n" 4044 " A toggle anti-aliasing \n" 4045 " D toggle depth-peeling (for transparencies) \n" 4046 " U toggle perspective/parallel projection \n" 4047 " o/O toggle extra light to scene and rotate it \n" 4048 " a toggle interaction to Actor Mode \n" 4049 " n toggle surface normals \n" 4050 " r reset camera position \n" 4051 " R reset camera to the closest orthogonal view \n" 4052 " . fly camera to the last clicked point \n" 4053 " C print the current camera parameters state \n" 4054 " X invoke a cutter widget tool \n" 4055 " S save a screenshot of the current scene \n" 4056 " E/F export 3D scene to numpy file or X3D \n" 4057 " q return control to python script \n" 4058 " Esc abort execution and exit python kernel " 4059 ) 4060 vedo.printc(msg, dim=True, italic=True, bold=True) 4061 vedo.printc( 4062 " Check out the documentation at: https://vedo.embl.es ".ljust(75), 4063 invert=True, 4064 bold=True, 4065 ) 4066 return 4067 4068 elif key == "a": 4069 cur = iren.GetInteractorStyle() 4070 if isinstance(cur, vtki.get_class("InteractorStyleTrackballCamera")): 4071 msg = "Interactor style changed to TrackballActor\n" 4072 msg += " you can now move and rotate individual meshes:\n" 4073 msg += " press X twice to save the repositioned mesh\n" 4074 msg += " press 'a' to go back to normal style" 4075 vedo.printc(msg) 4076 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 4077 else: 4078 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 4079 return 4080 4081 elif key == "A": # toggle antialiasing 4082 msam = self.window.GetMultiSamples() 4083 if not msam: 4084 self.window.SetMultiSamples(16) 4085 else: 4086 self.window.SetMultiSamples(0) 4087 msam = self.window.GetMultiSamples() 4088 if msam: 4089 vedo.printc(f"Antialiasing set to {msam} samples", c=bool(msam)) 4090 else: 4091 vedo.printc("Antialiasing disabled", c=bool(msam)) 4092 4093 elif key == "D": # toggle depthpeeling 4094 udp = not renderer.GetUseDepthPeeling() 4095 renderer.SetUseDepthPeeling(udp) 4096 # self.renderer.SetUseDepthPeelingForVolumes(udp) 4097 if udp: 4098 self.window.SetAlphaBitPlanes(1) 4099 renderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 4100 renderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 4101 self.interactor.Render() 4102 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 4103 rnr = self.renderers.index(renderer) 4104 vedo.printc(f"Depth peeling set to {udp} for renderer nr.{rnr}", c=udp) 4105 if not wasUsed and udp: 4106 vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True) 4107 return 4108 4109 elif key == "period": 4110 if self.picked3d: 4111 self.fly_to(self.picked3d) 4112 return 4113 4114 elif key == "S": 4115 fname = "screenshot.png" 4116 i = 1 4117 while os.path.isfile(fname): 4118 fname = f"screenshot{i}.png" 4119 i += 1 4120 vedo.file_io.screenshot(fname) 4121 vedo.printc(rf":camera: Saved rendering window to {fname}", c="b") 4122 return 4123 4124 elif key == "C": 4125 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 4126 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 4127 # This could happen for plotting geological models with UTM coordinate systems 4128 cam = renderer.GetActiveCamera() 4129 vedo.printc("\n###################################################", c="y") 4130 vedo.printc("## Template python code to position this camera: ##", c="y") 4131 vedo.printc("cam = dict(", c="y") 4132 vedo.printc(" pos=" + utils.precision(cam.GetPosition(), 6) + ",", c="y") 4133 vedo.printc(" focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y") 4134 vedo.printc(" viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y") 4135 vedo.printc(" roll=" + utils.precision(cam.GetRoll(), 6) + ",", c="y") 4136 if cam.GetParallelProjection(): 4137 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 4138 else: 4139 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 4140 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 4141 vedo.printc(')', c='y') 4142 vedo.printc('show(mymeshes, camera=cam)', c='y') 4143 vedo.printc('###################################################', c='y') 4144 return 4145 4146 elif key == "R": 4147 self.reset_viewup() 4148 4149 elif key == "w": 4150 try: 4151 if self.clicked_object.properties.GetRepresentation() == 1: # toggle 4152 self.clicked_object.properties.SetRepresentationToSurface() 4153 else: 4154 self.clicked_object.properties.SetRepresentationToWireframe() 4155 except AttributeError: 4156 pass 4157 4158 elif key == "1": 4159 try: 4160 self._icol += 1 4161 self.clicked_object.mapper.ScalarVisibilityOff() 4162 pal = vedo.colors.palettes[vedo.settings.palette % len(vedo.colors.palettes)] 4163 self.clicked_object.c(pal[(self._icol) % 10]) 4164 self.remove(self.clicked_object.scalarbar) 4165 except AttributeError: 4166 pass 4167 4168 elif key == "2": # dark colors 4169 try: 4170 bsc = ["k1", "k2", "k3", "k4", 4171 "b1", "b2", "b3", "b4", 4172 "p1", "p2", "p3", "p4", 4173 "g1", "g2", "g3", "g4", 4174 "r1", "r2", "r3", "r4", 4175 "o1", "o2", "o3", "o4", 4176 "y1", "y2", "y3", "y4"] 4177 self._icol += 1 4178 if self.clicked_object: 4179 self.clicked_object.mapper.ScalarVisibilityOff() 4180 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4181 self.clicked_object.c(newcol) 4182 self.remove(self.clicked_object.scalarbar) 4183 except AttributeError: 4184 pass 4185 4186 elif key == "3": # light colors 4187 try: 4188 bsc = ["k6", "k7", "k8", "k9", 4189 "b6", "b7", "b8", "b9", 4190 "p6", "p7", "p8", "p9", 4191 "g6", "g7", "g8", "g9", 4192 "r6", "r7", "r8", "r9", 4193 "o6", "o7", "o8", "o9", 4194 "y6", "y7", "y8", "y9"] 4195 self._icol += 1 4196 if self.clicked_object: 4197 self.clicked_object.mapper.ScalarVisibilityOff() 4198 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4199 self.clicked_object.c(newcol) 4200 self.remove(self.clicked_object.scalarbar) 4201 except AttributeError: 4202 pass 4203 4204 elif key == "4": # cmap name cycle 4205 ob = self.clicked_object 4206 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4207 return 4208 if not ob.mapper.GetScalarVisibility(): 4209 return 4210 onwhat = ob.mapper.GetScalarModeAsString() # UsePointData/UseCellData 4211 4212 cmap_names = [ 4213 "Accent", "Paired", 4214 "rainbow", "rainbow_r", 4215 "Spectral", "Spectral_r", 4216 "gist_ncar", "gist_ncar_r", 4217 "viridis", "viridis_r", 4218 "hot", "hot_r", 4219 "terrain", "ocean", 4220 "coolwarm", "seismic", "PuOr", "RdYlGn", 4221 ] 4222 try: 4223 i = cmap_names.index(ob._cmap_name) 4224 if iren.GetShiftKey(): 4225 i -= 1 4226 else: 4227 i += 1 4228 if i >= len(cmap_names): 4229 i = 0 4230 if i < 0: 4231 i = len(cmap_names) - 1 4232 except ValueError: 4233 i = 0 4234 4235 ob._cmap_name = cmap_names[i] 4236 ob.cmap(ob._cmap_name, on=onwhat) 4237 if ob.scalarbar: 4238 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4239 self.remove(ob.scalarbar) 4240 title = ob.scalarbar.GetTitle() 4241 ob.add_scalarbar(title=title) 4242 self.add(ob.scalarbar).render() 4243 elif isinstance(ob.scalarbar, vedo.Assembly): 4244 self.remove(ob.scalarbar) 4245 ob.add_scalarbar3d(title=ob._cmap_name) 4246 self.add(ob.scalarbar) 4247 4248 vedo.printc( 4249 f"Name:'{ob.name}'," if ob.name else "", 4250 f"range:{utils.precision(ob.mapper.GetScalarRange(),3)},", 4251 f"colormap:'{ob._cmap_name}'", c="g", bold=False, 4252 ) 4253 4254 elif key == "5": # cycle pointdata array 4255 ob = self.clicked_object 4256 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4257 return 4258 4259 arrnames = ob.pointdata.keys() 4260 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4261 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4262 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4263 if len(arrnames) == 0: 4264 return 4265 ob.mapper.SetScalarVisibility(1) 4266 4267 if not ob._cmap_name: 4268 ob._cmap_name = "rainbow" 4269 4270 try: 4271 curr_name = ob.dataset.GetPointData().GetScalars().GetName() 4272 i = arrnames.index(curr_name) 4273 if "normals" in curr_name.lower(): 4274 return 4275 if iren.GetShiftKey(): 4276 i -= 1 4277 else: 4278 i += 1 4279 if i >= len(arrnames): 4280 i = 0 4281 if i < 0: 4282 i = len(arrnames) - 1 4283 except (ValueError, AttributeError): 4284 i = 0 4285 4286 ob.cmap(ob._cmap_name, arrnames[i], on="points") 4287 if ob.scalarbar: 4288 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4289 self.remove(ob.scalarbar) 4290 title = ob.scalarbar.GetTitle() 4291 ob.scalarbar = None 4292 ob.add_scalarbar(title=arrnames[i]) 4293 self.add(ob.scalarbar) 4294 elif isinstance(ob.scalarbar, vedo.Assembly): 4295 self.remove(ob.scalarbar) 4296 ob.scalarbar = None 4297 ob.add_scalarbar3d(title=arrnames[i]) 4298 self.add(ob.scalarbar) 4299 else: 4300 vedo.printc( 4301 f"Name:'{ob.name}'," if ob.name else "", 4302 f"active pointdata array: '{arrnames[i]}'", 4303 c="g", bold=False, 4304 ) 4305 4306 elif key == "6": # cycle celldata array 4307 ob = self.clicked_object 4308 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4309 return 4310 4311 arrnames = ob.celldata.keys() 4312 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4313 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4314 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4315 if len(arrnames) == 0: 4316 return 4317 ob.mapper.SetScalarVisibility(1) 4318 4319 if not ob._cmap_name: 4320 ob._cmap_name = "rainbow" 4321 4322 try: 4323 curr_name = ob.dataset.GetCellData().GetScalars().GetName() 4324 i = arrnames.index(curr_name) 4325 if "normals" in curr_name.lower(): 4326 return 4327 if iren.GetShiftKey(): 4328 i -= 1 4329 else: 4330 i += 1 4331 if i >= len(arrnames): 4332 i = 0 4333 if i < 0: 4334 i = len(arrnames) - 1 4335 except (ValueError, AttributeError): 4336 i = 0 4337 4338 ob.cmap(ob._cmap_name, arrnames[i], on="cells") 4339 if ob.scalarbar: 4340 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4341 self.remove(ob.scalarbar) 4342 title = ob.scalarbar.GetTitle() 4343 ob.scalarbar = None 4344 ob.add_scalarbar(title=arrnames[i]) 4345 self.add(ob.scalarbar) 4346 elif isinstance(ob.scalarbar, vedo.Assembly): 4347 self.remove(ob.scalarbar) 4348 ob.scalarbar = None 4349 ob.add_scalarbar3d(title=arrnames[i]) 4350 self.add(ob.scalarbar) 4351 else: 4352 vedo.printc( 4353 f"Name:'{ob.name}'," if ob.name else "", 4354 f"active celldata array: '{arrnames[i]}'", 4355 c="g", bold=False, 4356 ) 4357 4358 elif key == "7": 4359 bgc = np.array(renderer.GetBackground()).sum() / 3 4360 if bgc <= 0: 4361 bgc = 0.223 4362 elif 0 < bgc < 1: 4363 bgc = 1 4364 else: 4365 bgc = 0 4366 renderer.SetBackground(bgc, bgc, bgc) 4367 4368 elif key == "8": 4369 bg2cols = [ 4370 "lightyellow", 4371 "darkseagreen", 4372 "palegreen", 4373 "steelblue", 4374 "lightblue", 4375 "cadetblue", 4376 "lavender", 4377 "white", 4378 "blackboard", 4379 "black", 4380 ] 4381 bg2name = vedo.get_color_name(renderer.GetBackground2()) 4382 if bg2name in bg2cols: 4383 idx = bg2cols.index(bg2name) 4384 else: 4385 idx = 4 4386 if idx is not None: 4387 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 4388 if not bg2name_next: 4389 renderer.GradientBackgroundOff() 4390 else: 4391 renderer.GradientBackgroundOn() 4392 renderer.SetBackground2(vedo.get_color(bg2name_next)) 4393 4394 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 4395 i = self.renderers.index(renderer) 4396 try: 4397 self.axes_instances[i].EnabledOff() 4398 self.axes_instances[i].SetInteractor(None) 4399 except AttributeError: 4400 # print("Cannot remove widget", [self.axes_instances[i]]) 4401 try: 4402 self.remove(self.axes_instances[i]) 4403 except: 4404 print("Cannot remove axes", [self.axes_instances[i]]) 4405 return 4406 self.axes_instances[i] = None 4407 4408 if not self.axes: 4409 self.axes = 0 4410 if isinstance(self.axes, dict): 4411 self.axes = 1 4412 4413 if key in ["minus", "KP_Subtract"]: 4414 if not self.camera.GetParallelProjection() and self.axes == 0: 4415 self.axes -= 1 # jump ruler doesnt make sense in perspective mode 4416 bns = self.renderer.ComputeVisiblePropBounds() 4417 addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None, bounds=bns) 4418 else: 4419 if not self.camera.GetParallelProjection() and self.axes == 12: 4420 self.axes += 1 # jump ruler doesnt make sense in perspective mode 4421 bns = self.renderer.ComputeVisiblePropBounds() 4422 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns) 4423 self.render() 4424 4425 elif "KP_" in key or key in [ 4426 "Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior" 4427 ]: 4428 asso = { # change axes style 4429 "KP_Insert": 0, "KP_0": 0, "Insert": 0, 4430 "KP_End": 1, "KP_1": 1, "End": 1, 4431 "KP_Down": 2, "KP_2": 2, "Down": 2, 4432 "KP_Next": 3, "KP_3": 3, "Next": 3, 4433 "KP_Left": 4, "KP_4": 4, "Left": 4, 4434 "KP_Begin": 5, "KP_5": 5, "Begin": 5, 4435 "KP_Right": 6, "KP_6": 6, "Right": 6, 4436 "KP_Home": 7, "KP_7": 7, "Home": 7, 4437 "KP_Up": 8, "KP_8": 8, "Up": 8, 4438 "Prior": 9, # on windows OS 4439 } 4440 clickedr = self.renderers.index(renderer) 4441 if key in asso: 4442 if self.axes_instances[clickedr]: 4443 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 4444 self.axes_instances[clickedr].EnabledOff() 4445 else: 4446 try: 4447 renderer.RemoveActor(self.axes_instances[clickedr]) 4448 except: 4449 pass 4450 self.axes_instances[clickedr] = None 4451 bounds = renderer.ComputeVisiblePropBounds() 4452 addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds) 4453 self.interactor.Render() 4454 4455 if key == "O": 4456 renderer.RemoveLight(self._extralight) 4457 self._extralight = None 4458 4459 elif key == "o": 4460 vbb, sizes, _, _ = addons.compute_visible_bounds() 4461 cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2) 4462 if not self._extralight: 4463 vup = renderer.GetActiveCamera().GetViewUp() 4464 pos = cm + utils.vector(vup) * utils.mag(sizes) 4465 self._extralight = addons.Light(pos, focal_point=cm, intensity=0.4) 4466 renderer.AddLight(self._extralight) 4467 vedo.printc("Press 'o' again to rotate light source, or 'O' to remove it.", c='y') 4468 else: 4469 cpos = utils.vector(self._extralight.GetPosition()) 4470 x, y, z = self._extralight.GetPosition() - cm 4471 r, th, ph = transformations.cart2spher(x, y, z) 4472 th += 0.2 4473 if th > np.pi: 4474 th = np.random.random() * np.pi / 2 4475 ph += 0.3 4476 cpos = transformations.spher2cart(r, th, ph).T + cm 4477 self._extralight.SetPosition(cpos) 4478 4479 elif key == "l": 4480 if self.clicked_object in self.get_meshes(): 4481 objs = [self.clicked_object] 4482 else: 4483 objs = self.get_meshes() 4484 for ia in objs: 4485 try: 4486 ev = ia.properties.GetEdgeVisibility() 4487 ia.properties.SetEdgeVisibility(not ev) 4488 ia.properties.SetRepresentationToSurface() 4489 ia.properties.SetLineWidth(0.1) 4490 except AttributeError: 4491 pass 4492 4493 elif key == "k": # lightings 4494 if self.clicked_object in self.get_meshes(): 4495 objs = [self.clicked_object] 4496 else: 4497 objs = self.get_meshes() 4498 shds = ("default", "metallic", "plastic", "shiny", "glossy", "off") 4499 for ia in objs: 4500 try: 4501 lnr = (ia._ligthingnr + 1) % 6 4502 ia.lighting(shds[lnr]) 4503 ia._ligthingnr = lnr 4504 except AttributeError: 4505 pass 4506 4507 elif key == "K": # shading 4508 if self.clicked_object in self.get_meshes(): 4509 objs = [self.clicked_object] 4510 else: 4511 objs = self.get_meshes() 4512 for ia in objs: 4513 if isinstance(ia, vedo.Mesh): 4514 ia.compute_normals(cells=False) 4515 intrp = ia.properties.GetInterpolation() 4516 if intrp > 0: 4517 ia.properties.SetInterpolation(0) # flat 4518 else: 4519 ia.properties.SetInterpolation(2) # phong 4520 4521 elif key == "n": # show normals to an actor 4522 self.remove("added_auto_normals") 4523 if self.clicked_object in self.get_meshes(): 4524 if self.clicked_actor.GetPickable(): 4525 norml = vedo.shapes.NormalLines(self.clicked_object) 4526 norml.name = "added_auto_normals" 4527 self.add(norml) 4528 4529 elif key == "x": 4530 if self.justremoved is None: 4531 if self.clicked_object in self.get_meshes() or isinstance( 4532 self.clicked_object, vtki.vtkAssembly 4533 ): 4534 self.justremoved = self.clicked_actor 4535 self.renderer.RemoveActor(self.clicked_actor) 4536 else: 4537 self.renderer.AddActor(self.justremoved) 4538 self.justremoved = None 4539 4540 elif key == "X": 4541 if self.clicked_object: 4542 if not self.cutter_widget: 4543 self.cutter_widget = addons.BoxCutter(self.clicked_object) 4544 self.add(self.cutter_widget) 4545 vedo.printc("Press i to toggle the cutter on/off", c='g', dim=1) 4546 vedo.printc(" u to flip selection", c='g', dim=1) 4547 vedo.printc(" r to reset cutting planes", c='g', dim=1) 4548 vedo.printc(" Shift+X to close the cutter box widget", c='g', dim=1) 4549 vedo.printc(" Ctrl+S to save the cut section to file.", c='g', dim=1) 4550 else: 4551 self.remove(self.cutter_widget) 4552 self.cutter_widget = None 4553 vedo.printc("Click object and press X to open the cutter box widget.", c='g') 4554 4555 elif key == "E": 4556 vedo.printc(r":camera: Exporting 3D window to file scene.npz", c="b", end="") 4557 vedo.file_io.export_window("scene.npz") 4558 vedo.printc(", try:\n> vedo scene.npz # (this is experimental!)", c="b") 4559 return 4560 4561 elif key == "F": 4562 vedo.file_io.export_window("scene.x3d") 4563 vedo.printc(r":camera: Exporting 3D window to file", c="b", end="") 4564 vedo.file_io.export_window("scene.npz") 4565 vedo.printc(". Try:\n> firefox scene.html", c="b") 4566 4567 # elif key == "G": # not working with last version of k3d 4568 # vedo.file_io.export_window("scene.html") 4569 # vedo.printc(r":camera: Exporting K3D window to file", c="b", end="") 4570 # vedo.file_io.export_window("scene.html") 4571 # vedo.printc(". Try:\n> firefox scene.html", c="b") 4572 4573 elif key == "i": # print info 4574 if self.clicked_object: 4575 print(self.clicked_object) 4576 else: 4577 print(self) 4578 4579 elif key == "I": # print color under the mouse 4580 x, y = iren.GetEventPosition() 4581 self.color_picker([x, y], verbose=True) 4582 4583 elif key == "Y": 4584 if self.clicked_object and self.clicked_object.pipeline: 4585 self.clicked_object.pipeline.show() 4586 4587 if iren: 4588 iren.Render()
Main class to manage objects.
379 def __init__( 380 self, 381 shape=(1, 1), 382 N=None, 383 pos=(0, 0), 384 size="auto", 385 screensize="auto", 386 title="vedo", 387 bg="white", 388 bg2=None, 389 axes=None, 390 sharecam=True, 391 resetcam=True, 392 interactive=None, 393 offscreen=False, 394 qt_widget=None, 395 wx_widget=None, 396 ): 397 """ 398 Arguments: 399 shape : (str, list) 400 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 401 N : (int) 402 number of desired renderers arranged in a grid automatically. 403 pos : (list) 404 (x,y) position in pixels of top-left corner of the rendering window on the screen 405 size : (str, list) 406 size of the rendering window. If 'auto', guess it based on screensize. 407 screensize : (list) 408 physical size of the monitor screen in pixels 409 bg : (color, str) 410 background color or specify jpg image file name with path 411 bg2 : (color) 412 background color of a gradient towards the top 413 title : (str) 414 window title 415 416 axes : (int) 417 418 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 419 Check out `vedo.addons.Axes()` for the available options. 420 421 - 0, no axes 422 - 1, draw three gray grid walls 423 - 2, show cartesian axes from (0,0,0) 424 - 3, show positive range of cartesian axes from (0,0,0) 425 - 4, show a triad at bottom left 426 - 5, show a cube at bottom left 427 - 6, mark the corners of the bounding box 428 - 7, draw a 3D ruler at each side of the cartesian axes 429 - 8, show the VTK CubeAxesActor object 430 - 9, show the bounding box outLine 431 - 10, show three circles representing the maximum bounding box 432 - 11, show a large grid on the x-y plane (use with zoom=8) 433 - 12, show polar axes 434 - 13, draw a simple ruler at the bottom of the window 435 - 14: draw a camera orientation widget 436 437 sharecam : (bool) 438 if False each renderer will have an independent camera 439 interactive : (bool) 440 if True will stop after show() to allow interaction with the 3d scene 441 offscreen : (bool) 442 if True will not show the rendering window 443 qt_widget : (QVTKRenderWindowInteractor) 444 render in a Qt-Widget using an QVTKRenderWindowInteractor. 445 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 446 """ 447 vedo.plotter_instance = self 448 449 if interactive is None: 450 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 451 self._interactive = interactive 452 # print("interactive", interactive, N, shape) 453 454 self.objects = [] # list of objects to be shown 455 self.clicked_object = None # holds the object that has been clicked 456 self.clicked_actor = None # holds the actor that has been clicked 457 458 self.shape = shape # nr. of subwindows in grid 459 self.axes = axes # show axes type nr. 460 self.title = title # window title 461 self.size = size # window size 462 self.backgrcol = bg # used also by backend notebooks 463 464 self.offscreen= offscreen 465 self.resetcam = resetcam 466 self.sharecam = sharecam # share the same camera if multiple renderers 467 self.pos = pos # used by vedo.file_io 468 469 self.picker = None # hold the vtkPicker object 470 self.picked2d = None # 2d coords of a clicked point on the rendering window 471 self.picked3d = None # 3d coords of a clicked point on an actor 472 473 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 474 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 475 self.interactor = None 476 self.window = None 477 self.renderer = None 478 self.renderers = [] # list of renderers 479 480 # mostly internal stuff: 481 self.hover_legends = [] 482 self.justremoved = None 483 self.axes_instances = [] 484 self.clock = 0 485 self.sliders = [] 486 self.buttons = [] 487 self.widgets = [] 488 self.cutter_widget = None 489 self.hint_widget = None 490 self.background_renderer = None 491 self.last_event = None 492 self.skybox = None 493 self._icol = 0 494 self._clockt0 = time.time() 495 self._extralight = None 496 self._cocoa_initialized = False 497 self._cocoa_process_events = True # make one call in show() 498 self._must_close_now = False 499 500 ##################################################################### 501 if vedo.settings.default_backend == "2d": 502 self.offscreen = True 503 if self.size == "auto": 504 self.size = (800, 600) 505 506 elif vedo.settings.default_backend == "k3d": 507 if self.size == "auto": 508 self.size = (1000, 1000) 509 #################################### 510 return ############################ 511 #################################### 512 513 ############################################################# 514 if vedo.settings.default_backend in ["vtk", "2d", "trame"]: 515 516 if screensize == "auto": 517 screensize = (2160, 1440) # TODO: get actual screen size 518 519 # build the rendering window: 520 self.window = vtki.vtkRenderWindow() 521 522 self.window.GlobalWarningDisplayOff() 523 524 if self.title == "vedo": # check if dev version 525 if "dev" in vedo.__version__: 526 self.title = f"vedo ({vedo.__version__})" 527 self.window.SetWindowName(self.title) 528 529 # more vedo.settings 530 if vedo.settings.use_depth_peeling: 531 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 532 self.window.SetMultiSamples(vedo.settings.multi_samples) 533 534 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 535 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 536 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 537 538 ############################################################# 539 if N: # N = number of renderers. Find out the best 540 541 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 542 vedo.logger.warning("having set N, shape is ignored.") 543 544 x, y = screensize 545 nx = int(np.sqrt(int(N * y / x) + 1)) 546 ny = int(np.sqrt(int(N * x / y) + 1)) 547 lm = [ 548 (nx, ny), 549 (nx, ny + 1), 550 (nx - 1, ny), 551 (nx + 1, ny), 552 (nx, ny - 1), 553 (nx - 1, ny + 1), 554 (nx + 1, ny - 1), 555 (nx + 1, ny + 1), 556 (nx - 1, ny - 1), 557 ] 558 ind, minl = 0, 1000 559 for i, m in enumerate(lm): 560 l = m[0] * m[1] 561 if N <= l < minl: 562 ind = i 563 minl = l 564 shape = lm[ind] 565 566 ################################################## 567 if isinstance(shape, str): 568 569 if "|" in shape: 570 if self.size == "auto": 571 self.size = (800, 1200) 572 n = int(shape.split("|")[0]) 573 m = int(shape.split("|")[1]) 574 rangen = reversed(range(n)) 575 rangem = reversed(range(m)) 576 else: 577 if self.size == "auto": 578 self.size = (1200, 800) 579 m = int(shape.split("/")[0]) 580 n = int(shape.split("/")[1]) 581 rangen = range(n) 582 rangem = range(m) 583 584 if n >= m: 585 xsplit = m / (n + m) 586 else: 587 xsplit = 1 - n / (n + m) 588 if vedo.settings.window_splitting_position: 589 xsplit = vedo.settings.window_splitting_position 590 591 for i in rangen: 592 arenderer = vtki.vtkRenderer() 593 if "|" in shape: 594 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 595 else: 596 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 597 self.renderers.append(arenderer) 598 599 for i in rangem: 600 arenderer = vtki.vtkRenderer() 601 602 if "|" in shape: 603 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 604 else: 605 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 606 self.renderers.append(arenderer) 607 608 for r in self.renderers: 609 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 610 611 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 612 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 613 if vedo.settings.use_depth_peeling: 614 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 615 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 616 r.SetUseFXAA(vedo.settings.use_fxaa) 617 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 618 619 r.SetBackground(vedo.get_color(self.backgrcol)) 620 621 self.axes_instances.append(None) 622 623 self.shape = (n + m,) 624 625 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 626 # passing a sequence of dicts for renderers specifications 627 628 if self.size == "auto": 629 self.size = (1000, 800) 630 631 for rd in shape: 632 x0, y0 = rd["bottomleft"] 633 x1, y1 = rd["topright"] 634 bg_ = rd.pop("bg", "white") 635 bg2_ = rd.pop("bg2", None) 636 637 arenderer = vtki.vtkRenderer() 638 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 639 640 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 641 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 642 if vedo.settings.use_depth_peeling: 643 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 644 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 645 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 646 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 647 648 arenderer.SetViewport(x0, y0, x1, y1) 649 arenderer.SetBackground(vedo.get_color(bg_)) 650 if bg2_: 651 arenderer.GradientBackgroundOn() 652 arenderer.SetBackground2(vedo.get_color(bg2_)) 653 654 self.renderers.append(arenderer) 655 self.axes_instances.append(None) 656 657 self.shape = (len(shape),) 658 659 else: 660 661 if isinstance(self.size, str) and self.size == "auto": 662 # figure out a reasonable window size 663 f = 1.5 664 x, y = screensize 665 xs = y / f * shape[1] # because y<x 666 ys = y / f * shape[0] 667 if xs > x / f: # shrink 668 xs = x / f 669 ys = xs / shape[1] * shape[0] 670 if ys > y / f: 671 ys = y / f 672 xs = ys / shape[0] * shape[1] 673 self.size = (int(xs), int(ys)) 674 if shape == (1, 1): 675 self.size = (int(y / f), int(y / f)) # because y<x 676 else: 677 self.size = (self.size[0], self.size[1]) 678 679 try: 680 image_actor = None 681 bgname = str(self.backgrcol).lower() 682 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 683 self.window.SetNumberOfLayers(2) 684 self.background_renderer = vtki.vtkRenderer() 685 self.background_renderer.SetLayer(0) 686 self.background_renderer.InteractiveOff() 687 self.background_renderer.SetBackground(vedo.get_color(bg2)) 688 image_actor = vedo.Image(self.backgrcol).actor 689 self.window.AddRenderer(self.background_renderer) 690 self.background_renderer.AddActor(image_actor) 691 except AttributeError: 692 pass 693 694 for i in reversed(range(shape[0])): 695 for j in range(shape[1]): 696 arenderer = vtki.vtkRenderer() 697 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 698 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 699 700 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 701 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 702 if vedo.settings.use_depth_peeling: 703 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 704 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 705 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 706 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 707 708 if image_actor: 709 arenderer.SetLayer(1) 710 711 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 712 if bg2: 713 arenderer.GradientBackgroundOn() 714 arenderer.SetBackground2(vedo.get_color(bg2)) 715 716 x0 = i / shape[0] 717 y0 = j / shape[1] 718 x1 = (i + 1) / shape[0] 719 y1 = (j + 1) / shape[1] 720 arenderer.SetViewport(y0, x0, y1, x1) 721 self.renderers.append(arenderer) 722 self.axes_instances.append(None) 723 self.shape = shape 724 725 if self.renderers: 726 self.renderer = self.renderers[0] 727 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 728 729 ######################################################### 730 if self.qt_widget or self.wx_widget: 731 if self.qt_widget: 732 self.window = self.qt_widget.GetRenderWindow() # overwrite 733 else: 734 self.window = self.wx_widget.GetRenderWindow() 735 self.interactor = self.window.GetInteractor() 736 737 ######################################################### 738 for r in self.renderers: 739 self.window.AddRenderer(r) 740 # set the background gradient if any 741 if vedo.settings.background_gradient_orientation > 0: 742 try: 743 modes = [ 744 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 745 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 746 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 747 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 748 ] 749 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 750 r.GradientBackgroundOn() 751 except AttributeError: 752 pass 753 754 ######################################################### 755 if self.qt_widget or self.wx_widget: 756 # self.window.SetSize(int(self.size[0]), int(self.size[1])) 757 self.interactor.SetRenderWindow(self.window) 758 # vsty = vtki.new("InteractorStyleTrackballCamera") 759 # self.interactor.SetInteractorStyle(vsty) 760 if vedo.settings.enable_default_keyboard_callbacks: 761 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 762 if vedo.settings.enable_default_mouse_callbacks: 763 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 764 return ################ 765 ######################## 766 767 if self.size[0] == "f": # full screen 768 self.size = "fullscreen" 769 self.window.SetFullScreen(True) 770 self.window.BordersOn() 771 else: 772 self.window.SetSize(int(self.size[0]), int(self.size[1])) 773 774 if self.offscreen: 775 if self.axes in (4, 5, 8, 12, 14): 776 self.axes = 0 # does not work with those 777 self.window.SetOffScreenRendering(True) 778 self.interactor = None 779 self._interactive = False 780 return ################ 781 ######################## 782 783 self.window.SetPosition(pos) 784 785 ######################################################### 786 self.interactor = vtki.vtkRenderWindowInteractor() 787 788 self.interactor.SetRenderWindow(self.window) 789 vsty = vtki.new("InteractorStyleTrackballCamera") 790 self.interactor.SetInteractorStyle(vsty) 791 self.interactor.RemoveObservers("CharEvent") 792 793 if vedo.settings.enable_default_keyboard_callbacks: 794 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 795 if vedo.settings.enable_default_mouse_callbacks: 796 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
.
867 def print(self): 868 """Print information about the current instance.""" 869 print(self.__str__()) 870 return self
Print information about the current instance.
888 def initialize_interactor(self) -> Self: 889 """Initialize the interactor if not already initialized.""" 890 if self.offscreen: 891 return self 892 if self.interactor: 893 if not self.interactor.GetInitialized(): 894 self.interactor.Initialize() 895 self.interactor.RemoveObservers("CharEvent") 896 return self
Initialize the interactor if not already initialized.
898 def process_events(self) -> Self: 899 """Process all pending events.""" 900 self.initialize_interactor() 901 if self.interactor: 902 try: 903 self.interactor.ProcessEvents() 904 except AttributeError: 905 pass 906 return self
Process all pending events.
908 def at(self, nren: int, yren=None) -> Self: 909 """ 910 Select the current renderer number as an int. 911 Can also use the `[nx, ny]` format. 912 """ 913 if utils.is_sequence(nren): 914 if len(nren) == 2: 915 nren, yren = nren 916 else: 917 vedo.logger.error("at() argument must be a single number or a list of two numbers") 918 raise RuntimeError 919 920 if yren is not None: 921 a, b = self.shape 922 x, y = nren, yren 923 nren = x * b + y 924 # print("at (", x, y, ") -> ren", nren) 925 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 926 vedo.logger.error(f"at({nren, yren}) is malformed!") 927 raise RuntimeError 928 929 self.renderer = self.renderers[nren] 930 return self
Select the current renderer number as an int.
Can also use the [nx, ny]
format.
932 def add(self, *objs, at=None) -> Self: 933 """ 934 Append the input objects to the internal list of objects to be shown. 935 936 Arguments: 937 at : (int) 938 add the object at the specified renderer 939 """ 940 if at is not None: 941 ren = self.renderers[at] 942 else: 943 ren = self.renderer 944 945 objs = utils.flatten(objs) 946 for ob in objs: 947 if ob and ob not in self.objects: 948 self.objects.append(ob) 949 950 acts = self._scan_input_return_acts(objs) 951 952 for a in acts: 953 954 if ren: 955 if isinstance(a, vedo.addons.BaseCutter): 956 a.add_to(self) # from cutters 957 continue 958 959 if isinstance(a, vtki.vtkLight): 960 ren.AddLight(a) 961 continue 962 963 try: 964 ren.AddActor(a) 965 except TypeError: 966 ren.AddActor(a.actor) 967 968 try: 969 ir = self.renderers.index(ren) 970 a.rendered_at.add(ir) # might not have rendered_at 971 except (AttributeError, ValueError): 972 pass 973 974 if isinstance(a, vtki.vtkFollower): 975 a.SetCamera(self.camera) 976 elif isinstance(a, vedo.visual.LightKit): 977 a.lightkit.AddLightsToRenderer(ren) 978 979 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
981 def remove(self, *objs, at=None) -> Self: 982 """ 983 Remove input object to the internal list of objects to be shown. 984 985 Objects to be removed can be referenced by their assigned name, 986 987 Arguments: 988 at : (int) 989 remove the object at the specified renderer 990 """ 991 # TODO and you can also use wildcards like `*` and `?`. 992 if at is not None: 993 ren = self.renderers[at] 994 else: 995 ren = self.renderer 996 997 objs = [ob for ob in utils.flatten(objs) if ob] 998 999 has_str = False 1000 for ob in objs: 1001 if isinstance(ob, str): 1002 has_str = True 1003 break 1004 1005 has_actor = False 1006 for ob in objs: 1007 if hasattr(ob, "actor") and ob.actor: 1008 has_actor = True 1009 break 1010 1011 if has_str or has_actor: 1012 # need to get the actors to search for 1013 for a in self.get_actors(include_non_pickables=True): 1014 # print("PARSING", [a]) 1015 try: 1016 if (a.name and a.name in objs) or a in objs: 1017 objs.append(a) 1018 # if a.name: 1019 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1020 # if any(bools) or a in objs: 1021 # objs.append(a) 1022 # print('a.name',a.name, objs,any(bools)) 1023 except AttributeError: # no .name 1024 # passing the actor so get back the object with .retrieve_object() 1025 try: 1026 vobj = a.retrieve_object() 1027 if (vobj.name and vobj.name in objs) or vobj in objs: 1028 # print('vobj.name', vobj.name) 1029 objs.append(vobj) 1030 except AttributeError: 1031 pass 1032 1033 ir = self.renderers.index(ren) 1034 1035 ids = [] 1036 for ob in set(objs): 1037 1038 # will remove it from internal list if possible 1039 try: 1040 idx = self.objects.index(ob) 1041 ids.append(idx) 1042 except ValueError: 1043 pass 1044 1045 if ren: ### remove it from the renderer 1046 1047 if isinstance(ob, vedo.addons.BaseCutter): 1048 ob.remove_from(self) # from cutters 1049 continue 1050 1051 try: 1052 ren.RemoveActor(ob) 1053 except TypeError: 1054 try: 1055 ren.RemoveActor(ob.actor) 1056 except AttributeError: 1057 pass 1058 1059 if hasattr(ob, "rendered_at"): 1060 ob.rendered_at.discard(ir) 1061 1062 if hasattr(ob, "scalarbar") and ob.scalarbar: 1063 ren.RemoveActor(ob.scalarbar) 1064 if hasattr(ob, "_caption") and ob._caption: 1065 ren.RemoveActor(ob._caption) 1066 if hasattr(ob, "shadows") and ob.shadows: 1067 for sha in ob.shadows: 1068 ren.RemoveActor(sha.actor) 1069 if hasattr(ob, "trail") and ob.trail: 1070 ren.RemoveActor(ob.trail.actor) 1071 ob.trail_points = [] 1072 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1073 for sha in ob.trail.shadows: 1074 ren.RemoveActor(sha.actor) 1075 1076 elif isinstance(ob, vedo.visual.LightKit): 1077 ob.lightkit.RemoveLightsFromRenderer(ren) 1078 1079 # for i in ids: # WRONG way of doing it! 1080 # del self.objects[i] 1081 # instead we do: 1082 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1083 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
1085 @property 1086 def actors(self): 1087 """Return the list of actors.""" 1088 return [ob.actor for ob in self.objects if hasattr(ob, "actor")]
Return the list of actors.
1090 def remove_lights(self) -> Self: 1091 """Remove all the present lights in the current renderer.""" 1092 if self.renderer: 1093 self.renderer.RemoveAllLights() 1094 return self
Remove all the present lights in the current renderer.
1096 def pop(self, at=None) -> Self: 1097 """ 1098 Remove the last added object from the rendering window. 1099 This method is typically used in loops or callback functions. 1100 """ 1101 if at is not None and not isinstance(at, int): 1102 # wrong usage pitfall 1103 vedo.logger.error("argument of pop() must be an integer") 1104 raise RuntimeError() 1105 1106 if self.objects: 1107 self.remove(self.objects[-1], at) 1108 return self
Remove the last added object from the rendering window. This method is typically used in loops or callback functions.
1110 def render(self, resetcam=False) -> Self: 1111 """Render the scene. This method is typically used in loops or callback functions.""" 1112 1113 if vedo.settings.dry_run_mode >= 2: 1114 return self 1115 1116 if not self.window: 1117 return self 1118 1119 self.initialize_interactor() 1120 1121 if resetcam: 1122 self.renderer.ResetCamera() 1123 1124 self.window.Render() 1125 1126 if self._cocoa_process_events and self.interactor.GetInitialized(): 1127 if "Darwin" in vedo.sys_platform and not self.offscreen: 1128 self.interactor.ProcessEvents() 1129 self._cocoa_process_events = False 1130 return self
Render the scene. This method is typically used in loops or callback functions.
1132 def interactive(self) -> Self: 1133 """ 1134 Start window interaction. 1135 Analogous to `show(..., interactive=True)`. 1136 """ 1137 if vedo.settings.dry_run_mode >= 1: 1138 return self 1139 self.initialize_interactor() 1140 if self.interactor: 1141 # print("self.interactor.Start()") 1142 self.interactor.Start() 1143 # print("self.interactor.Start() done") 1144 if self._must_close_now: 1145 # print("self.interactor.TerminateApp()") 1146 if self.interactor: 1147 self.interactor.GetRenderWindow().Finalize() 1148 self.interactor.TerminateApp() 1149 self.interactor = None 1150 self.window = None 1151 self.renderer = None 1152 self.renderers = [] 1153 self.camera = None 1154 return self
Start window interaction.
Analogous to show(..., interactive=True)
.
1156 def use_depth_peeling(self, at=None, value=True) -> Self: 1157 """ 1158 Specify whether use depth peeling algorithm at this specific renderer 1159 Call this method before the first rendering. 1160 """ 1161 if at is None: 1162 ren = self.renderer 1163 else: 1164 ren = self.renderers[at] 1165 ren.SetUseDepthPeeling(value) 1166 return self
Specify whether use depth peeling algorithm at this specific renderer Call this method before the first rendering.
1168 def background(self, c1=None, c2=None, at=None, mode=0) -> Union[Self, "np.ndarray"]: 1169 """Set the color of the background for the current renderer. 1170 A different renderer index can be specified by keyword `at`. 1171 1172 Arguments: 1173 c1 : (list) 1174 background main color. 1175 c2 : (list) 1176 background color for the upper part of the window. 1177 at : (int) 1178 renderer index. 1179 mode : (int) 1180 background mode (needs vtk version >= 9.3) 1181 0 = vertical, 1182 1 = horizontal, 1183 2 = radial farthest side, 1184 3 = radia farthest corner. 1185 """ 1186 if not self.renderers: 1187 return self 1188 if at is None: 1189 r = self.renderer 1190 else: 1191 r = self.renderers[at] 1192 1193 if c1 is None and c2 is None: 1194 return np.array(r.GetBackground()) 1195 1196 if r: 1197 if c1 is not None: 1198 r.SetBackground(vedo.get_color(c1)) 1199 if c2 is not None: 1200 r.GradientBackgroundOn() 1201 r.SetBackground2(vedo.get_color(c2)) 1202 if mode: 1203 try: # only works with vtk>=9.3 1204 modes = [ 1205 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1206 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1207 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1208 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1209 ] 1210 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 1211 except AttributeError: 1212 pass 1213 1214 else: 1215 r.GradientBackgroundOff() 1216 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.
1219 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1220 """ 1221 Return a list of Meshes from the specified renderer. 1222 1223 Arguments: 1224 at : (int) 1225 specify which renderer to look at. 1226 include_non_pickables : (bool) 1227 include non-pickable objects 1228 unpack_assemblies : (bool) 1229 unpack assemblies into their components 1230 """ 1231 if at is None: 1232 renderer = self.renderer 1233 at = self.renderers.index(renderer) 1234 elif isinstance(at, int): 1235 renderer = self.renderers[at] 1236 1237 has_global_axes = False 1238 if isinstance(self.axes_instances[at], vedo.Assembly): 1239 has_global_axes = True 1240 1241 if unpack_assemblies: 1242 acs = renderer.GetActors() 1243 else: 1244 acs = renderer.GetViewProps() 1245 1246 objs = [] 1247 acs.InitTraversal() 1248 for _ in range(acs.GetNumberOfItems()): 1249 1250 if unpack_assemblies: 1251 a = acs.GetNextItem() 1252 else: 1253 a = acs.GetNextProp() 1254 1255 if isinstance(a, vtki.vtkVolume): 1256 continue 1257 1258 if include_non_pickables or a.GetPickable(): 1259 if a == self.axes_instances[at]: 1260 continue 1261 if has_global_axes and a in self.axes_instances[at].actors: 1262 continue 1263 try: 1264 objs.append(a.retrieve_object()) 1265 except AttributeError: 1266 pass 1267 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
1269 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1270 """ 1271 Return a list of Volumes from the specified renderer. 1272 1273 Arguments: 1274 at : (int) 1275 specify which renderer to look at 1276 include_non_pickables : (bool) 1277 include non-pickable objects 1278 """ 1279 if at is None: 1280 renderer = self.renderer 1281 at = self.renderers.index(renderer) 1282 elif isinstance(at, int): 1283 renderer = self.renderers[at] 1284 1285 vols = [] 1286 acs = renderer.GetVolumes() 1287 acs.InitTraversal() 1288 for _ in range(acs.GetNumberOfItems()): 1289 a = acs.GetNextItem() 1290 if include_non_pickables or a.GetPickable(): 1291 try: 1292 vols.append(a.retrieve_object()) 1293 except AttributeError: 1294 pass 1295 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
1297 def get_actors(self, at=None, include_non_pickables=False) -> list: 1298 """ 1299 Return a list of Volumes from the specified renderer. 1300 1301 Arguments: 1302 at : (int) 1303 specify which renderer to look at 1304 include_non_pickables : (bool) 1305 include non-pickable objects 1306 """ 1307 if at is None: 1308 renderer = self.renderer 1309 at = self.renderers.index(renderer) 1310 elif isinstance(at, int): 1311 renderer = self.renderers[at] 1312 1313 acts = [] 1314 acs = renderer.GetViewProps() 1315 acs.InitTraversal() 1316 for _ in range(acs.GetNumberOfItems()): 1317 a = acs.GetNextProp() 1318 if include_non_pickables or a.GetPickable(): 1319 acts.append(a) 1320 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
1322 def check_actors_trasform(self, at=None) -> Self: 1323 """ 1324 Reset the transformation matrix of all actors at specified renderer. 1325 This is only useful when actors have been moved/rotated/scaled manually 1326 in an already rendered scene using interactors like 1327 'TrackballActor' or 'JoystickActor'. 1328 """ 1329 # see issue https://github.com/marcomusy/vedo/issues/1046 1330 for a in self.get_actors(at=at, include_non_pickables=True): 1331 try: 1332 M = a.GetMatrix() 1333 except AttributeError: 1334 continue 1335 if M and not M.IsIdentity(): 1336 try: 1337 a.retrieve_object().apply_transform_from_actor() 1338 # vedo.logger.info( 1339 # f"object '{a.retrieve_object().name}' " 1340 # "was manually moved. Updated to its current position." 1341 # ) 1342 except AttributeError: 1343 pass 1344 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'.
1346 def reset_camera(self, tight=None) -> Self: 1347 """ 1348 Reset the camera position and zooming. 1349 If tight (float) is specified the zooming reserves a padding space 1350 in the xy-plane expressed in percent of the average size. 1351 """ 1352 if tight is None: 1353 self.renderer.ResetCamera() 1354 else: 1355 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1356 cam = self.camera 1357 1358 self.renderer.ComputeAspect() 1359 aspect = self.renderer.GetAspect() 1360 angle = np.pi * cam.GetViewAngle() / 180.0 1361 dx = x1 - x0 1362 dy = y1 - y0 1363 dist = max(dx / aspect[0], dy) / np.tan(angle / 2) / 2 1364 1365 cam.SetViewUp(0, 1, 0) 1366 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1367 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1368 if cam.GetParallelProjection(): 1369 ps = max(dx / aspect[0], dy) / 2 1370 cam.SetParallelScale(ps * (1 + tight)) 1371 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1372 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.
1374 def reset_viewup(self, smooth=True) -> Self: 1375 """ 1376 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1377 """ 1378 vbb = addons.compute_visible_bounds()[0] 1379 x0, x1, y0, y1, z0, z1 = vbb 1380 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1381 d = self.camera.GetDistance() 1382 1383 viewups = np.array( 1384 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1385 ) 1386 positions = np.array( 1387 [ 1388 (mx, my, mz + d), 1389 (mx, my, mz - d), 1390 (mx, my + d, mz), 1391 (mx, my - d, mz), 1392 (mx + d, my, mz), 1393 (mx - d, my, mz), 1394 ] 1395 ) 1396 1397 vu = np.array(self.camera.GetViewUp()) 1398 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1399 1400 poc = np.array(self.camera.GetPosition()) 1401 foc = np.array(self.camera.GetFocalPoint()) 1402 a = poc - foc 1403 b = positions - foc 1404 a = a / np.linalg.norm(a) 1405 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1406 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1407 1408 if smooth: 1409 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1410 for t in outtimes: 1411 vv = vu * (1 - t) + viewups[vui] * t 1412 pp = poc * (1 - t) + positions[pui] * t 1413 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1414 self.camera.SetViewUp(vv) 1415 self.camera.SetPosition(pp) 1416 self.camera.SetFocalPoint(ff) 1417 self.render() 1418 1419 # interpolator does not respect parallel view...: 1420 # cam1 = dict( 1421 # pos=poc, 1422 # viewup=vu, 1423 # focal_point=(mx,my,mz), 1424 # clipping_range=self.camera.GetClippingRange() 1425 # ) 1426 # # cam1 = self.camera 1427 # cam2 = dict( 1428 # pos=positions[pui], 1429 # viewup=viewups[vui], 1430 # focal_point=(mx,my,mz), 1431 # clipping_range=self.camera.GetClippingRange() 1432 # ) 1433 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1434 # for c in vcams: 1435 # self.renderer.SetActiveCamera(c) 1436 # self.render() 1437 else: 1438 1439 self.camera.SetViewUp(viewups[vui]) 1440 self.camera.SetPosition(positions[pui]) 1441 self.camera.SetFocalPoint(mx, my, mz) 1442 1443 self.renderer.ResetCameraClippingRange() 1444 1445 # vbb, _, _, _ = addons.compute_visible_bounds() 1446 # x0,x1, y0,y1, z0,z1 = vbb 1447 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1448 self.render() 1449 return self
Reset the orientation of the camera to the closest orthogonal direction and view-up.
1451 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1452 """ 1453 Takes as input two cameras set camera at an interpolated position: 1454 1455 Cameras can be vtkCamera or dictionaries in format: 1456 1457 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1458 1459 Press `shift-C` key in interactive mode to dump a python snipplet 1460 of parameters for the current camera view. 1461 """ 1462 nc = len(cameras) 1463 if len(times) == 0: 1464 times = np.linspace(0, 1, num=nc, endpoint=True) 1465 1466 assert len(times) == nc 1467 1468 cin = vtki.new("CameraInterpolator") 1469 1470 # cin.SetInterpolationTypeToLinear() # buggy? 1471 if nc > 2 and smooth: 1472 cin.SetInterpolationTypeToSpline() 1473 1474 for i, cam in enumerate(cameras): 1475 vcam = cam 1476 if isinstance(cam, dict): 1477 vcam = utils.camera_from_dict(cam) 1478 cin.AddCamera(times[i], vcam) 1479 1480 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1481 rng = maxt - mint 1482 1483 if len(output_times) == 0: 1484 cin.InterpolateCamera(t * rng, self.camera) 1485 self.renderer.SetActiveCamera(self.camera) 1486 return [self.camera] 1487 else: 1488 vcams = [] 1489 for tt in output_times: 1490 c = vtki.vtkCamera() 1491 cin.InterpolateCamera(tt * rng, c) 1492 vcams.append(c) 1493 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.
1495 def fly_to(self, point) -> Self: 1496 """ 1497 Fly camera to the specified point. 1498 1499 Arguments: 1500 point : (list) 1501 point in space to place camera. 1502 1503 Example: 1504 ```python 1505 from vedo import * 1506 cone = Cone() 1507 plt = Plotter(axes=1) 1508 plt.show(cone) 1509 plt.fly_to([1,0,0]) 1510 plt.interactive().close() 1511 ``` 1512 """ 1513 if self.interactor: 1514 self.resetcam = False 1515 self.interactor.FlyTo(self.renderer, point) 1516 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()
1518 def look_at(self, plane="xy") -> Self: 1519 """Move the camera so that it looks at the specified cartesian plane""" 1520 cam = self.renderer.GetActiveCamera() 1521 fp = np.array(cam.GetFocalPoint()) 1522 p = np.array(cam.GetPosition()) 1523 dist = np.linalg.norm(fp - p) 1524 plane = plane.lower() 1525 if "x" in plane and "y" in plane: 1526 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1527 cam.SetViewUp(0.0, 1.0, 0.0) 1528 elif "x" in plane and "z" in plane: 1529 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1530 cam.SetViewUp(0.0, 0.0, 1.0) 1531 elif "y" in plane and "z" in plane: 1532 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1533 cam.SetViewUp(0.0, 0.0, 1.0) 1534 else: 1535 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1536 return self
Move the camera so that it looks at the specified cartesian plane
1538 def record(self, filename="") -> str: 1539 """ 1540 Record camera, mouse, keystrokes and all other events. 1541 Recording can be toggled on/off by pressing key "R". 1542 1543 Arguments: 1544 filename : (str) 1545 ascii file to store events. 1546 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1547 1548 Returns: 1549 a string descriptor of events. 1550 1551 Examples: 1552 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1553 """ 1554 if vedo.settings.dry_run_mode >= 1: 1555 return "" 1556 if not self.interactor: 1557 vedo.logger.warning("Cannot record events, no interactor defined.") 1558 return "" 1559 erec = vtki.new("InteractorEventRecorder") 1560 erec.SetInteractor(self.interactor) 1561 if not filename: 1562 if not os.path.exists(vedo.settings.cache_directory): 1563 os.makedirs(vedo.settings.cache_directory) 1564 home_dir = os.path.expanduser("~") 1565 filename = os.path.join( 1566 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1567 print("Events will be recorded in", filename) 1568 erec.SetFileName(filename) 1569 erec.SetKeyPressActivationValue("R") 1570 erec.EnabledOn() 1571 erec.Record() 1572 self.interactor.Start() 1573 erec.Stop() 1574 erec.EnabledOff() 1575 with open(filename, "r", encoding="UTF-8") as fl: 1576 events = fl.read() 1577 erec = None 1578 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:
1580 def play(self, recorded_events="", repeats=0) -> Self: 1581 """ 1582 Play camera, mouse, keystrokes and all other events. 1583 1584 Arguments: 1585 events : (str) 1586 file o string of events. 1587 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1588 repeats : (int) 1589 number of extra repeats of the same events. The default is 0. 1590 1591 Examples: 1592 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1593 """ 1594 if vedo.settings.dry_run_mode >= 1: 1595 return self 1596 if not self.interactor: 1597 vedo.logger.warning("Cannot play events, no interactor defined.") 1598 return self 1599 1600 erec = vtki.new("InteractorEventRecorder") 1601 erec.SetInteractor(self.interactor) 1602 1603 if not recorded_events: 1604 home_dir = os.path.expanduser("~") 1605 recorded_events = os.path.join( 1606 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1607 1608 if recorded_events.endswith(".log"): 1609 erec.ReadFromInputStringOff() 1610 erec.SetFileName(recorded_events) 1611 else: 1612 erec.ReadFromInputStringOn() 1613 erec.SetInputString(recorded_events) 1614 1615 erec.Play() 1616 for _ in range(repeats): 1617 erec.Rewind() 1618 erec.Play() 1619 erec.EnabledOff() 1620 erec = None 1621 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:
1623 def parallel_projection(self, value=True, at=None) -> Self: 1624 """ 1625 Use parallel projection `at` a specified renderer. 1626 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1627 An input value equal to -1 will toggle it on/off. 1628 """ 1629 if at is not None: 1630 r = self.renderers[at] 1631 else: 1632 r = self.renderer 1633 if value == -1: 1634 val = r.GetActiveCamera().GetParallelProjection() 1635 value = not val 1636 r.GetActiveCamera().SetParallelProjection(value) 1637 r.Modified() 1638 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.
1645 def fov(self, angle: float) -> Self: 1646 """ 1647 Set the field of view angle for the camera. 1648 This is the angle of the camera frustum in the horizontal direction. 1649 High values will result in a wide-angle lens (fish-eye effect), 1650 and low values will result in a telephoto lens. 1651 1652 Default value is 30 degrees. 1653 """ 1654 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1655 self.renderer.GetActiveCamera().SetViewAngle(angle) 1656 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.
1658 def zoom(self, zoom: float) -> Self: 1659 """Apply a zooming factor for the current camera view""" 1660 self.renderer.GetActiveCamera().Zoom(zoom) 1661 return self
Apply a zooming factor for the current camera view
1663 def azimuth(self, angle: float) -> Self: 1664 """Rotate camera around the view up vector.""" 1665 self.renderer.GetActiveCamera().Azimuth(angle) 1666 return self
Rotate camera around the view up vector.
1668 def elevation(self, angle: float) -> Self: 1669 """Rotate the camera around the cross product of the negative 1670 of the direction of projection and the view up vector.""" 1671 self.renderer.GetActiveCamera().Elevation(angle) 1672 return self
Rotate the camera around the cross product of the negative of the direction of projection and the view up vector.
1674 def roll(self, angle: float) -> Self: 1675 """Roll the camera about the direction of projection.""" 1676 self.renderer.GetActiveCamera().Roll(angle) 1677 return self
Roll the camera about the direction of projection.
1679 def dolly(self, value: float) -> Self: 1680 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1681 self.renderer.GetActiveCamera().Dolly(value) 1682 return self
Move the camera towards (value>0) or away from (value<0) the focal point.
1685 def add_slider( 1686 self, 1687 sliderfunc, 1688 xmin, 1689 xmax, 1690 value=None, 1691 pos=4, 1692 title="", 1693 font="Calco", 1694 title_size=1, 1695 c=None, 1696 alpha=1, 1697 show_value=True, 1698 delayed=False, 1699 **options, 1700 ) -> "vedo.addons.Slider2D": 1701 """ 1702 Add a `vedo.addons.Slider2D` which can call an external custom function. 1703 1704 Arguments: 1705 sliderfunc : (Callable) 1706 external function to be called by the widget 1707 xmin : (float) 1708 lower value of the slider 1709 xmax : (float) 1710 upper value 1711 value : (float) 1712 current value 1713 pos : (list, str) 1714 position corner number: horizontal [1-5] or vertical [11-15] 1715 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1716 and also by a string descriptor (eg. "bottom-left") 1717 title : (str) 1718 title text 1719 font : (str) 1720 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1721 title_size : (float) 1722 title text scale [1.0] 1723 show_value : (bool) 1724 if True current value is shown 1725 delayed : (bool) 1726 if True the callback is delayed until when the mouse button is released 1727 alpha : (float) 1728 opacity of the scalar bar texts 1729 slider_length : (float) 1730 slider length 1731 slider_width : (float) 1732 slider width 1733 end_cap_length : (float) 1734 length of the end cap 1735 end_cap_width : (float) 1736 width of the end cap 1737 tube_width : (float) 1738 width of the tube 1739 title_height : (float) 1740 width of the title 1741 tformat : (str) 1742 format of the title 1743 1744 Examples: 1745 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1746 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1747 1748 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1749 """ 1750 if c is None: # automatic black or white 1751 c = (0.8, 0.8, 0.8) 1752 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1753 c = (0.2, 0.2, 0.2) 1754 else: 1755 c = vedo.get_color(c) 1756 1757 slider2d = addons.Slider2D( 1758 sliderfunc, 1759 xmin, 1760 xmax, 1761 value, 1762 pos, 1763 title, 1764 font, 1765 title_size, 1766 c, 1767 alpha, 1768 show_value, 1769 delayed, 1770 **options, 1771 ) 1772 1773 if self.renderer: 1774 slider2d.renderer = self.renderer 1775 if self.interactor: 1776 slider2d.interactor = self.interactor 1777 slider2d.on() 1778 self.sliders.append([slider2d, sliderfunc]) 1779 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:
1781 def add_slider3d( 1782 self, 1783 sliderfunc, 1784 pos1, 1785 pos2, 1786 xmin, 1787 xmax, 1788 value=None, 1789 s=0.03, 1790 t=1, 1791 title="", 1792 rotation=0.0, 1793 c=None, 1794 show_value=True, 1795 ) -> "vedo.addons.Slider3D": 1796 """ 1797 Add a 3D slider widget which can call an external custom function. 1798 1799 Arguments: 1800 sliderfunc : (function) 1801 external function to be called by the widget 1802 pos1 : (list) 1803 first position 3D coordinates 1804 pos2 : (list) 1805 second position coordinates 1806 xmin : (float) 1807 lower value 1808 xmax : (float) 1809 upper value 1810 value : (float) 1811 initial value 1812 s : (float) 1813 label scaling factor 1814 t : (float) 1815 tube scaling factor 1816 title : (str) 1817 title text 1818 c : (color) 1819 slider color 1820 rotation : (float) 1821 title rotation around slider axis 1822 show_value : (bool) 1823 if True current value is shown 1824 1825 Examples: 1826 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1827 1828 ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png) 1829 """ 1830 if c is None: # automatic black or white 1831 c = (0.8, 0.8, 0.8) 1832 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1833 c = (0.2, 0.2, 0.2) 1834 else: 1835 c = vedo.get_color(c) 1836 1837 slider3d = addons.Slider3D( 1838 sliderfunc, 1839 pos1, 1840 pos2, 1841 xmin, 1842 xmax, 1843 value, 1844 s, 1845 t, 1846 title, 1847 rotation, 1848 c, 1849 show_value, 1850 ) 1851 slider3d.renderer = self.renderer 1852 slider3d.interactor = self.interactor 1853 slider3d.on() 1854 self.sliders.append([slider3d, sliderfunc]) 1855 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:
1914 def add_spline_tool( 1915 self, points, pc="k", ps=8, lc="r4", ac="g5", 1916 lw=2, alpha=1, closed=False, ontop=True, can_add_nodes=True, 1917 ) -> "vedo.addons.SplineTool": 1918 """ 1919 Add a spline tool to the current plotter. 1920 Nodes of the spline can be dragged in space with the mouse. 1921 Clicking on the line itself adds an extra point. 1922 Selecting a point and pressing del removes it. 1923 1924 Arguments: 1925 points : (Mesh, Points, array) 1926 the set of vertices forming the spline nodes. 1927 pc : (str) 1928 point color. The default is 'k'. 1929 ps : (str) 1930 point size. The default is 8. 1931 lc : (str) 1932 line color. The default is 'r4'. 1933 ac : (str) 1934 active point marker color. The default is 'g5'. 1935 lw : (int) 1936 line width. The default is 2. 1937 alpha : (float) 1938 line transparency. 1939 closed : (bool) 1940 spline is meant to be closed. The default is False. 1941 1942 Returns: 1943 a `SplineTool` object. 1944 1945 Examples: 1946 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1947 1948 ![](https://vedo.embl.es/images/basic/spline_tool.png) 1949 """ 1950 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1951 sw.interactor = self.interactor 1952 sw.on() 1953 sw.Initialize(sw.points.dataset) 1954 sw.representation.SetRenderer(self.renderer) 1955 sw.representation.SetClosedLoop(closed) 1956 sw.representation.BuildRepresentation() 1957 self.widgets.append(sw) 1958 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:
1960 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1961 """Add an inset icon mesh into the same renderer. 1962 1963 Arguments: 1964 pos : (int, list) 1965 icon position in the range [1-4] indicating one of the 4 corners, 1966 or it can be a tuple (x,y) as a fraction of the renderer size. 1967 size : (float) 1968 size of the square inset. 1969 1970 Examples: 1971 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1972 """ 1973 iconw = addons.Icon(icon, pos, size) 1974 1975 iconw.SetInteractor(self.interactor) 1976 iconw.EnabledOn() 1977 iconw.InteractiveOff() 1978 self.widgets.append(iconw) 1979 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:
1981 def add_global_axes(self, axtype=None, c=None) -> Self: 1982 """Draw axes on scene. Available axes types: 1983 1984 Arguments: 1985 axtype : (int) 1986 - 0, no axes, 1987 - 1, draw three gray grid walls 1988 - 2, show cartesian axes from (0,0,0) 1989 - 3, show positive range of cartesian axes from (0,0,0) 1990 - 4, show a triad at bottom left 1991 - 5, show a cube at bottom left 1992 - 6, mark the corners of the bounding box 1993 - 7, draw a 3D ruler at each side of the cartesian axes 1994 - 8, show the vtkCubeAxesActor object 1995 - 9, show the bounding box outLine 1996 - 10, show three circles representing the maximum bounding box 1997 - 11, show a large grid on the x-y plane 1998 - 12, show polar axes 1999 - 13, draw a simple ruler at the bottom of the window 2000 2001 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 2002 2003 Example: 2004 ```python 2005 from vedo import Box, show 2006 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 2007 show( 2008 b, 2009 axes={ 2010 "xtitle": "Some long variable [a.u.]", 2011 "number_of_divisions": 4, 2012 # ... 2013 }, 2014 ) 2015 ``` 2016 2017 Examples: 2018 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2019 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2020 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2021 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2022 2023 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2024 """ 2025 addons.add_global_axes(axtype, c) 2026 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:
2028 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2029 """Add a legend to the top right. 2030 2031 Examples: 2032 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 2033 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 2034 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 2035 """ 2036 acts = self.get_meshes() 2037 lb = addons.LegendBox(acts, **kwargs) 2038 self.add(lb) 2039 return lb
2041 def add_hint( 2042 self, 2043 obj, 2044 text="", 2045 c="k", 2046 bg="yellow9", 2047 font="Calco", 2048 size=18, 2049 justify=0, 2050 angle=0, 2051 delay=250, 2052 ) -> Union[vtki.vtkBalloonWidget, None]: 2053 """ 2054 Create a pop-up hint style message when hovering an object. 2055 Use `add_hint(obj, False)` to disable a hinting a specific object. 2056 Use `add_hint(None)` to disable all hints. 2057 2058 Arguments: 2059 obj : (Mesh, Points) 2060 the object to associate the pop-up to 2061 text : (str) 2062 string description of the pop-up 2063 delay : (int) 2064 milliseconds to wait before pop-up occurs 2065 """ 2066 if self.offscreen or not self.interactor: 2067 return None 2068 2069 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2070 # Linux vtk9.0 is bugged 2071 vedo.logger.warning( 2072 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2073 ) 2074 return None 2075 2076 if obj is None: 2077 self.hint_widget.EnabledOff() 2078 self.hint_widget.SetInteractor(None) 2079 self.hint_widget = None 2080 return self.hint_widget 2081 2082 if text is False and self.hint_widget: 2083 self.hint_widget.RemoveBalloon(obj) 2084 return self.hint_widget 2085 2086 if text == "": 2087 if obj.name: 2088 text = obj.name 2089 elif obj.filename: 2090 text = obj.filename 2091 else: 2092 return None 2093 2094 if not self.hint_widget: 2095 self.hint_widget = vtki.vtkBalloonWidget() 2096 2097 rep = self.hint_widget.GetRepresentation() 2098 rep.SetBalloonLayoutToImageRight() 2099 2100 trep = rep.GetTextProperty() 2101 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2102 trep.SetFontFile(utils.get_font_path(font)) 2103 trep.SetFontSize(size) 2104 trep.SetColor(vedo.get_color(c)) 2105 trep.SetBackgroundColor(vedo.get_color(bg)) 2106 trep.SetShadow(0) 2107 trep.SetJustification(justify) 2108 trep.UseTightBoundingBoxOn() 2109 2110 self.hint_widget.ManagesCursorOff() 2111 self.hint_widget.SetTimerDuration(delay) 2112 self.hint_widget.SetInteractor(self.interactor) 2113 if angle: 2114 trep.SetOrientation(angle) 2115 trep.SetBackgroundOpacity(0) 2116 # else: 2117 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2118 self.hint_widget.SetRepresentation(rep) 2119 self.widgets.append(self.hint_widget) 2120 self.hint_widget.EnabledOn() 2121 2122 bst = self.hint_widget.GetBalloonString(obj.actor) 2123 if bst: 2124 self.hint_widget.UpdateBalloonString(obj.actor, text) 2125 else: 2126 self.hint_widget.AddBalloon(obj.actor, text) 2127 2128 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
2130 def add_shadows(self) -> Self: 2131 """Add shadows at the current renderer.""" 2132 if self.renderer: 2133 shadows = vtki.new("ShadowMapPass") 2134 seq = vtki.new("SequencePass") 2135 passes = vtki.new("RenderPassCollection") 2136 passes.AddItem(shadows.GetShadowMapBakerPass()) 2137 passes.AddItem(shadows) 2138 seq.SetPasses(passes) 2139 camerapass = vtki.new("CameraPass") 2140 camerapass.SetDelegatePass(seq) 2141 self.renderer.SetPass(camerapass) 2142 return self
Add shadows at the current renderer.
2144 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> Self: 2145 """ 2146 Screen Space Ambient Occlusion. 2147 2148 For every pixel on the screen, the pixel shader samples the depth values around 2149 the current pixel and tries to compute the amount of occlusion from each of the sampled 2150 points. 2151 2152 Arguments: 2153 radius : (float) 2154 radius of influence in absolute units 2155 bias : (float) 2156 bias of the normals 2157 blur : (bool) 2158 add a blurring to the sampled positions 2159 samples : (int) 2160 number of samples to probe 2161 2162 Examples: 2163 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2164 2165 ![](https://vedo.embl.es/images/basic/ssao.jpg) 2166 """ 2167 lights = vtki.new("LightsPass") 2168 2169 opaque = vtki.new("OpaquePass") 2170 2171 ssaoCam = vtki.new("CameraPass") 2172 ssaoCam.SetDelegatePass(opaque) 2173 2174 ssao = vtki.new("SSAOPass") 2175 ssao.SetRadius(radius) 2176 ssao.SetBias(bias) 2177 ssao.SetBlur(blur) 2178 ssao.SetKernelSize(samples) 2179 ssao.SetDelegatePass(ssaoCam) 2180 2181 translucent = vtki.new("TranslucentPass") 2182 2183 volpass = vtki.new("VolumetricPass") 2184 ddp = vtki.new("DualDepthPeelingPass") 2185 ddp.SetTranslucentPass(translucent) 2186 ddp.SetVolumetricPass(volpass) 2187 2188 over = vtki.new("OverlayPass") 2189 2190 collection = vtki.new("RenderPassCollection") 2191 collection.AddItem(lights) 2192 collection.AddItem(ssao) 2193 collection.AddItem(ddp) 2194 collection.AddItem(over) 2195 2196 sequence = vtki.new("SequencePass") 2197 sequence.SetPasses(collection) 2198 2199 cam = vtki.new("CameraPass") 2200 cam.SetDelegatePass(sequence) 2201 2202 self.renderer.SetPass(cam) 2203 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:
2205 def add_depth_of_field(self, autofocus=True) -> Self: 2206 """Add a depth of field effect in the scene.""" 2207 lights = vtki.new("LightsPass") 2208 2209 opaque = vtki.new("OpaquePass") 2210 2211 dofCam = vtki.new("CameraPass") 2212 dofCam.SetDelegatePass(opaque) 2213 2214 dof = vtki.new("DepthOfFieldPass") 2215 dof.SetAutomaticFocalDistance(autofocus) 2216 dof.SetDelegatePass(dofCam) 2217 2218 collection = vtki.new("RenderPassCollection") 2219 collection.AddItem(lights) 2220 collection.AddItem(dof) 2221 2222 sequence = vtki.new("SequencePass") 2223 sequence.SetPasses(collection) 2224 2225 cam = vtki.new("CameraPass") 2226 cam.SetDelegatePass(sequence) 2227 2228 self.renderer.SetPass(cam) 2229 return self
Add a depth of field effect in the scene.
2260 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None) -> "vedo.addons.RendererFrame": 2261 """ 2262 Add a frame to the renderer subwindow. 2263 2264 Arguments: 2265 c : (color) 2266 color name or index 2267 alpha : (float) 2268 opacity level 2269 lw : (int) 2270 line width in pixels. 2271 padding : (float) 2272 padding space in pixels. 2273 """ 2274 if c is None: # automatic black or white 2275 c = (0.9, 0.9, 0.9) 2276 if self.renderer: 2277 if np.sum(self.renderer.GetBackground()) > 1.5: 2278 c = (0.1, 0.1, 0.1) 2279 renf = addons.RendererFrame(c, alpha, lw, padding) 2280 if renf: 2281 self.renderer.AddActor(renf) 2282 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.
2284 def add_hover_legend( 2285 self, 2286 at=None, 2287 c=None, 2288 pos="bottom-left", 2289 font="Calco", 2290 s=0.75, 2291 bg="auto", 2292 alpha=0.1, 2293 maxlength=24, 2294 use_info=False, 2295 ) -> int: 2296 """ 2297 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2298 2299 The created text object are stored in `plotter.hover_legends`. 2300 2301 Returns: 2302 the id of the callback function. 2303 2304 Arguments: 2305 c : (color) 2306 Text color. If None then black or white is chosen automatically 2307 pos : (str) 2308 text positioning 2309 font : (str) 2310 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2311 s : (float) 2312 text size scale 2313 bg : (color) 2314 background color of the 2D box containing the text 2315 alpha : (float) 2316 box transparency 2317 maxlength : (int) 2318 maximum number of characters per line 2319 use_info : (bool) 2320 visualize the content of the `obj.info` attribute 2321 2322 Examples: 2323 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2324 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2325 2326 ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg) 2327 """ 2328 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2329 2330 if at is None: 2331 at = self.renderers.index(self.renderer) 2332 2333 def _legfunc(evt): 2334 if not evt.object or not self.renderer or at != evt.at: 2335 if hoverlegend.mapper.GetInput(): # clear and return 2336 hoverlegend.mapper.SetInput("") 2337 self.render() 2338 return 2339 2340 if use_info: 2341 if hasattr(evt.object, "info"): 2342 t = str(evt.object.info) 2343 else: 2344 return 2345 else: 2346 t, tp = "", "" 2347 if evt.isMesh: 2348 tp = "Mesh " 2349 elif evt.isPoints: 2350 tp = "Points " 2351 elif evt.isVolume: 2352 tp = "Volume " 2353 elif evt.isImage: 2354 tp = "Image " 2355 elif evt.isAssembly: 2356 tp = "Assembly " 2357 else: 2358 return 2359 2360 if evt.isAssembly: 2361 if not evt.object.name: 2362 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2363 else: 2364 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2365 else: 2366 if evt.object.name: 2367 t += f"{tp}name" 2368 if evt.isPoints: 2369 t += " " 2370 if evt.isMesh: 2371 t += " " 2372 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2373 2374 if evt.object.filename: 2375 t += f"{tp}filename: " 2376 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2377 t += "\n" 2378 if not evt.object.file_size: 2379 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2380 if evt.object.file_size: 2381 t += " : " 2382 sz, created = evt.object.file_size, evt.object.created 2383 t += f"{created[4:-5]} ({sz})" + "\n" 2384 2385 if evt.isPoints: 2386 indata = evt.object.dataset 2387 if indata.GetNumberOfPoints(): 2388 t += ( 2389 f"#points/cells: {indata.GetNumberOfPoints()}" 2390 f" / {indata.GetNumberOfCells()}" 2391 ) 2392 pdata = indata.GetPointData() 2393 cdata = indata.GetCellData() 2394 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2395 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2396 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2397 t += " *" 2398 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2399 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2400 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2401 t += " *" 2402 2403 if evt.isImage: 2404 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2405 t += f"\nImage shape: {evt.object.shape}" 2406 pcol = self.color_picker(evt.picked2d) 2407 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2408 2409 # change box color if needed in 'auto' mode 2410 if evt.isPoints and "auto" in str(bg): 2411 actcol = evt.object.properties.GetColor() 2412 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2413 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2414 2415 # adapt to changes in bg color 2416 bgcol = self.renderers[at].GetBackground() 2417 _bgcol = c 2418 if _bgcol is None: # automatic black or white 2419 _bgcol = (0.9, 0.9, 0.9) 2420 if sum(bgcol) > 1.5: 2421 _bgcol = (0.1, 0.1, 0.1) 2422 if len(set(_bgcol).intersection(bgcol)) < 3: 2423 hoverlegend.color(_bgcol) 2424 2425 if hoverlegend.mapper.GetInput() != t: 2426 hoverlegend.mapper.SetInput(t) 2427 self.interactor.Render() 2428 2429 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2430 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2431 2432 self.add(hoverlegend, at=at) 2433 self.hover_legends.append(hoverlegend) 2434 idcall = self.add_callback("MouseMove", _legfunc) 2435 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:
2437 def add_scale_indicator( 2438 self, 2439 pos=(0.7, 0.05), 2440 s=0.02, 2441 length=2, 2442 lw=4, 2443 c="k1", 2444 alpha=1, 2445 units="", 2446 gap=0.05, 2447 ) -> Union["vedo.visual.Actor2D", None]: 2448 """ 2449 Add a Scale Indicator. Only works in parallel mode (no perspective). 2450 2451 Arguments: 2452 pos : (list) 2453 fractional (x,y) position on the screen. 2454 s : (float) 2455 size of the text. 2456 length : (float) 2457 length of the line. 2458 units : (str) 2459 string to show units. 2460 gap : (float) 2461 separation of line and text. 2462 2463 Example: 2464 ```python 2465 from vedo import settings, Cube, Plotter 2466 settings.use_parallel_projection = True # or else it does not make sense! 2467 cube = Cube().alpha(0.2) 2468 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2469 plt.add_scale_indicator(units='um', c='blue4') 2470 plt.show(cube, "Scale indicator with units").close() 2471 ``` 2472 ![](https://vedo.embl.es/images/feats/scale_indicator.png) 2473 """ 2474 # Note that this cannot go in addons.py 2475 # because it needs callbacks and window size 2476 if not self.interactor: 2477 return None 2478 2479 ppoints = vtki.vtkPoints() # Generate the polyline 2480 psqr = [[0.0, gap], [length / 10, gap]] 2481 dd = psqr[1][0] - psqr[0][0] 2482 for i, pt in enumerate(psqr): 2483 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2484 lines = vtki.vtkCellArray() 2485 lines.InsertNextCell(len(psqr)) 2486 for i in range(len(psqr)): 2487 lines.InsertCellPoint(i) 2488 pd = vtki.vtkPolyData() 2489 pd.SetPoints(ppoints) 2490 pd.SetLines(lines) 2491 2492 wsx, wsy = self.window.GetSize() 2493 if not self.camera.GetParallelProjection(): 2494 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2495 return None 2496 2497 rlabel = vtki.new("VectorText") 2498 rlabel.SetText("scale") 2499 tf = vtki.new("TransformPolyDataFilter") 2500 tf.SetInputConnection(rlabel.GetOutputPort()) 2501 t = vtki.vtkTransform() 2502 t.Scale(s * wsy / wsx, s, 1) 2503 tf.SetTransform(t) 2504 2505 app = vtki.new("AppendPolyData") 2506 app.AddInputConnection(tf.GetOutputPort()) 2507 app.AddInputData(pd) 2508 2509 mapper = vtki.new("PolyDataMapper2D") 2510 mapper.SetInputConnection(app.GetOutputPort()) 2511 cs = vtki.vtkCoordinate() 2512 cs.SetCoordinateSystem(1) 2513 mapper.SetTransformCoordinate(cs) 2514 2515 fractor = vedo.visual.Actor2D() 2516 csys = fractor.GetPositionCoordinate() 2517 csys.SetCoordinateSystem(3) 2518 fractor.SetPosition(pos) 2519 fractor.SetMapper(mapper) 2520 fractor.GetProperty().SetColor(vedo.get_color(c)) 2521 fractor.GetProperty().SetOpacity(alpha) 2522 fractor.GetProperty().SetLineWidth(lw) 2523 fractor.GetProperty().SetDisplayLocationToForeground() 2524 2525 def sifunc(iren, ev): 2526 wsx, wsy = self.window.GetSize() 2527 ps = self.camera.GetParallelScale() 2528 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2529 if units: 2530 newtxt += " " + units 2531 if rlabel.GetText() != newtxt: 2532 rlabel.SetText(newtxt) 2533 2534 self.renderer.AddActor(fractor) 2535 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2536 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2537 self.interactor.AddObserver("InteractionEvent", sifunc) 2538 sifunc(0, 0) 2539 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()
2541 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2542 """ 2543 Create an Event object with information of what was clicked. 2544 2545 If `enable_picking` is False, no picking will be performed. 2546 This can be useful to avoid double picking when using buttons. 2547 """ 2548 if not self.interactor: 2549 return Event() 2550 2551 if len(pos) > 0: 2552 x, y = pos 2553 self.interactor.SetEventPosition(pos) 2554 else: 2555 x, y = self.interactor.GetEventPosition() 2556 self.renderer = self.interactor.FindPokedRenderer(x, y) 2557 2558 self.picked2d = (x, y) 2559 2560 key = self.interactor.GetKeySym() 2561 2562 if key: 2563 if "_L" in key or "_R" in key: 2564 # skip things like Shift_R 2565 key = "" # better than None 2566 else: 2567 if self.interactor.GetShiftKey(): 2568 key = key.upper() 2569 2570 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2571 key = "underscore" 2572 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2573 key = "plus" 2574 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2575 key = "?" 2576 2577 if self.interactor.GetControlKey(): 2578 key = "Ctrl+" + key 2579 2580 if self.interactor.GetAltKey(): 2581 key = "Alt+" + key 2582 2583 if enable_picking: 2584 if not self.picker: 2585 self.picker = vtki.vtkPropPicker() 2586 2587 self.picker.PickProp(x, y, self.renderer) 2588 actor = self.picker.GetProp3D() 2589 # Note that GetProp3D already picks Assembly 2590 2591 xp, yp = self.interactor.GetLastEventPosition() 2592 dx, dy = x - xp, y - yp 2593 2594 delta3d = np.array([0, 0, 0]) 2595 2596 if actor: 2597 picked3d = np.array(self.picker.GetPickPosition()) 2598 2599 try: 2600 vobj = actor.retrieve_object() 2601 old_pt = np.asarray(vobj.picked3d) 2602 vobj.picked3d = picked3d 2603 delta3d = picked3d - old_pt 2604 except (AttributeError, TypeError): 2605 pass 2606 2607 else: 2608 picked3d = None 2609 2610 if not actor: # try 2D 2611 actor = self.picker.GetActor2D() 2612 2613 event = Event() 2614 event.name = ename 2615 event.title = self.title 2616 event.id = -1 # will be set by the timer wrapper function 2617 event.timerid = -1 # will be set by the timer wrapper function 2618 event.priority = -1 # will be set by the timer wrapper function 2619 event.time = time.time() 2620 event.at = self.renderers.index(self.renderer) 2621 event.keypress = key 2622 if enable_picking: 2623 try: 2624 event.object = actor.retrieve_object() 2625 except AttributeError: 2626 event.object = actor 2627 try: 2628 event.actor = actor.retrieve_object() # obsolete use object instead 2629 except AttributeError: 2630 event.actor = actor 2631 event.picked3d = picked3d 2632 event.picked2d = (x, y) 2633 event.delta2d = (dx, dy) 2634 event.angle2d = np.arctan2(dy, dx) 2635 event.speed2d = np.sqrt(dx * dx + dy * dy) 2636 event.delta3d = delta3d 2637 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2638 event.isPoints = isinstance(event.object, vedo.Points) 2639 event.isMesh = isinstance(event.object, vedo.Mesh) 2640 event.isAssembly = isinstance(event.object, vedo.Assembly) 2641 event.isVolume = isinstance(event.object, vedo.Volume) 2642 event.isImage = isinstance(event.object, vedo.Image) 2643 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2644 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.
2646 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2647 """ 2648 Add a function to be executed while show() is active. 2649 2650 Return a unique id for the callback. 2651 2652 The callback function (see example below) exposes a dictionary 2653 with the following information: 2654 - `name`: event name, 2655 - `id`: event unique identifier, 2656 - `priority`: event priority (float), 2657 - `interactor`: the interactor object, 2658 - `at`: renderer nr. where the event occurred 2659 - `keypress`: key pressed as string 2660 - `actor`: object picked by the mouse 2661 - `picked3d`: point picked in world coordinates 2662 - `picked2d`: screen coords of the mouse pointer 2663 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2664 - `delta3d`: ...same but in 3D world coords 2665 - `angle2d`: angle of mouse movement on screen 2666 - `speed2d`: speed of mouse movement on screen 2667 - `speed3d`: speed of picked point in world coordinates 2668 - `isPoints`: True if of class 2669 - `isMesh`: True if of class 2670 - `isAssembly`: True if of class 2671 - `isVolume`: True if of class Volume 2672 - `isImage`: True if of class 2673 2674 If `enable_picking` is False, no picking will be performed. 2675 This can be useful to avoid double picking when using buttons. 2676 2677 Frequently used events are: 2678 - `KeyPress`, `KeyRelease`: listen to keyboard events 2679 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2680 - `MiddleButtonPress`, `MiddleButtonRelease` 2681 - `RightButtonPress`, `RightButtonRelease` 2682 - `MouseMove`: listen to mouse pointer changing position 2683 - `MouseWheelForward`, `MouseWheelBackward` 2684 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2685 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2686 - `ResetCamera`, `ResetCameraClippingRange` 2687 - `Error`, `Warning` 2688 - `Char` 2689 - `Timer` 2690 2691 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2692 2693 Example: 2694 ```python 2695 from vedo import * 2696 2697 def func(evt): 2698 # this function is called every time the mouse moves 2699 # (evt is a dotted dictionary) 2700 if not evt.object: 2701 return # no hit, return 2702 print("point coords =", evt.picked3d) 2703 # print(evt) # full event dump 2704 2705 elli = Ellipsoid() 2706 plt = Plotter(axes=1) 2707 plt.add_callback('mouse hovering', func) 2708 plt.show(elli).close() 2709 ``` 2710 2711 Examples: 2712 - [spline_draw.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw.py) 2713 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2714 2715 ![](https://vedo.embl.es/images/advanced/spline_draw.png) 2716 2717 - ..and many others! 2718 """ 2719 from vtkmodules.util.misc import calldata_type 2720 2721 if not self.interactor: 2722 return 0 2723 2724 if vedo.settings.dry_run_mode >= 1: 2725 return 0 2726 2727 ######################################### 2728 @calldata_type(vtki.VTK_INT) 2729 def _func_wrap(iren, ename, timerid=None): 2730 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2731 event.timerid = timerid 2732 event.id = cid 2733 event.priority = priority 2734 self.last_event = event 2735 func(event) 2736 2737 ######################################### 2738 2739 event_name = utils.get_vtk_name_event(event_name) 2740 2741 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2742 # print(f"Registering event: {event_name} with id={cid}") 2743 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!
2745 def remove_callback(self, cid: Union[int, str]) -> Self: 2746 """ 2747 Remove a callback function by its id 2748 or a whole category of callbacks by their name. 2749 2750 Arguments: 2751 cid : (int, str) 2752 Unique id of the callback. 2753 If an event name is passed all callbacks of that type are removed. 2754 """ 2755 if self.interactor: 2756 if isinstance(cid, str): 2757 cid = utils.get_vtk_name_event(cid) 2758 self.interactor.RemoveObservers(cid) 2759 else: 2760 self.interactor.RemoveObserver(cid) 2761 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.
2763 def remove_all_observers(self) -> Self: 2764 """ 2765 Remove all observers. 2766 2767 Example: 2768 ```python 2769 from vedo import * 2770 2771 def kfunc(event): 2772 print("Key pressed:", event.keypress) 2773 if event.keypress == 'q': 2774 plt.close() 2775 2776 def rfunc(event): 2777 if event.isImage: 2778 printc("Right-clicked!", event) 2779 plt.render() 2780 2781 img = Image(dataurl+"images/embryo.jpg") 2782 2783 plt = Plotter(size=(1050, 600)) 2784 plt.parallel_projection(True) 2785 plt.remove_all_observers() 2786 plt.add_callback("key press", kfunc) 2787 plt.add_callback("mouse right click", rfunc) 2788 plt.show("Right-Click Me! Press q to exit.", img) 2789 plt.close() 2790 ``` 2791 """ 2792 if self.interactor: 2793 self.interactor.RemoveAllObservers() 2794 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()
2796 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2797 """ 2798 Start or stop an existing timer. 2799 2800 Arguments: 2801 action : (str) 2802 Either "create"/"start" or "destroy"/"stop" 2803 timer_id : (int) 2804 When stopping the timer, the ID of the timer as returned when created 2805 dt : (int) 2806 time in milliseconds between each repeated call 2807 one_shot : (bool) 2808 create a one shot timer of prescribed duration instead of a repeating one 2809 2810 Examples: 2811 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2812 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2813 2814 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 2815 """ 2816 if action in ("create", "start"): 2817 if timer_id is not None: 2818 vedo.logger.warning("you set a timer_id but it will be ignored.") 2819 if one_shot: 2820 timer_id = self.interactor.CreateOneShotTimer(dt) 2821 else: 2822 timer_id = self.interactor.CreateRepeatingTimer(dt) 2823 return timer_id 2824 2825 elif action in ("destroy", "stop"): 2826 if timer_id is not None: 2827 self.interactor.DestroyTimer(timer_id) 2828 else: 2829 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2830 else: 2831 e = f"in timer_callback(). Cannot understand action: {action}\n" 2832 e += " allowed actions are: ['start', 'stop']. Skipped." 2833 vedo.logger.error(e) 2834 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:
2836 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2837 """ 2838 Add a callback function that will be called when an event occurs. 2839 Consider using `add_callback()` instead. 2840 """ 2841 if not self.interactor: 2842 return -1 2843 event_name = utils.get_vtk_name_event(event_name) 2844 idd = self.interactor.AddObserver(event_name, func, priority) 2845 return idd
Add a callback function that will be called when an event occurs.
Consider using add_callback()
instead.
2847 def compute_world_coordinate( 2848 self, 2849 pos2d: MutableSequence[float], 2850 at=None, 2851 objs=(), 2852 bounds=(), 2853 offset=None, 2854 pixeltol=None, 2855 worldtol=None, 2856 ) -> np.ndarray: 2857 """ 2858 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2859 If a set of meshes is passed then points are placed onto these. 2860 2861 Arguments: 2862 pos2d : (list) 2863 2D screen coordinates point. 2864 at : (int) 2865 renderer number. 2866 objs : (list) 2867 list of Mesh objects to project the point onto. 2868 bounds : (list) 2869 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2870 offset : (float) 2871 specify an offset value. 2872 pixeltol : (int) 2873 screen tolerance in pixels. 2874 worldtol : (float) 2875 world coordinates tolerance. 2876 2877 Returns: 2878 numpy array, the point in 3D world coordinates. 2879 2880 Examples: 2881 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2882 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2883 2884 ![](https://vedo.embl.es/images/basic/mousehover3.jpg) 2885 """ 2886 if at is not None: 2887 renderer = self.renderers[at] 2888 else: 2889 renderer = self.renderer 2890 2891 if not objs: 2892 pp = vtki.vtkFocalPlanePointPlacer() 2893 else: 2894 pps = vtki.vtkPolygonalSurfacePointPlacer() 2895 for ob in objs: 2896 pps.AddProp(ob.actor) 2897 pp = pps # type: ignore 2898 2899 if len(bounds) == 6: 2900 pp.SetPointBounds(bounds) 2901 if pixeltol: 2902 pp.SetPixelTolerance(pixeltol) 2903 if worldtol: 2904 pp.SetWorldTolerance(worldtol) 2905 if offset: 2906 pp.SetOffset(offset) 2907 2908 worldPos: MutableSequence[float] = [0, 0, 0] 2909 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2910 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2911 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2912 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2913 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:
2915 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2916 """ 2917 Given a 3D points in the current renderer (or full window), 2918 find the screen pixel coordinates. 2919 2920 Example: 2921 ```python 2922 from vedo import * 2923 2924 elli = Ellipsoid().point_size(5) 2925 2926 plt = Plotter() 2927 plt.show(elli, "Press q to continue and print the info") 2928 2929 xyscreen = plt.compute_screen_coordinates(elli) 2930 print('xyscreen coords:', xyscreen) 2931 2932 # simulate an event happening at one point 2933 event = plt.fill_event(pos=xyscreen[123]) 2934 print(event) 2935 ``` 2936 """ 2937 try: 2938 obj = obj.vertices 2939 except AttributeError: 2940 pass 2941 2942 if utils.is_sequence(obj): 2943 pts = obj 2944 p2d = [] 2945 cs = vtki.vtkCoordinate() 2946 cs.SetCoordinateSystemToWorld() 2947 cs.SetViewport(self.renderer) 2948 for p in pts: 2949 cs.SetValue(p) 2950 if full_window: 2951 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2952 else: 2953 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2954 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)
2956 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2957 """ 2958 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2959 2960 Returns a frustum Mesh that contains the visible field of view. 2961 This can be used to select objects in a scene or select vertices. 2962 2963 Example: 2964 ```python 2965 from vedo import * 2966 2967 settings.enable_default_mouse_callbacks = False 2968 2969 def mode_select(objs): 2970 print("Selected objects:", objs) 2971 d0 = mode.start_x, mode.start_y # display coords 2972 d1 = mode.end_x, mode.end_y 2973 2974 frustum = plt.pick_area(d0, d1) 2975 col = np.random.randint(0, 10) 2976 infru = frustum.inside_points(mesh) 2977 infru.point_size(10).color(col) 2978 plt.add(frustum, infru).render() 2979 2980 mesh = Mesh(dataurl+"cow.vtk") 2981 mesh.color("k5").linewidth(1) 2982 2983 mode = interactor_modes.BlenderStyle() 2984 mode.callback_select = mode_select 2985 2986 plt = Plotter().user_mode(mode) 2987 plt.show(mesh, axes=1) 2988 ``` 2989 """ 2990 if at is not None: 2991 ren = self.renderers[at] 2992 else: 2993 ren = self.renderer 2994 area_picker = vtki.vtkAreaPicker() 2995 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 2996 planes = area_picker.GetFrustum() 2997 2998 fru = vtki.new("FrustumSource") 2999 fru.SetPlanes(planes) 3000 fru.ShowLinesOff() 3001 fru.Update() 3002 3003 afru = vedo.Mesh(fru.GetOutput()) 3004 afru.alpha(0.1).lw(1).pickable(False) 3005 afru.name = "Frustum" 3006 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)
3129 def show( 3130 self, 3131 *objects, 3132 at=None, 3133 axes=None, 3134 resetcam=None, 3135 zoom=False, 3136 interactive=None, 3137 viewup="", 3138 azimuth=0.0, 3139 elevation=0.0, 3140 roll=0.0, 3141 camera=None, 3142 mode=None, 3143 rate=None, 3144 bg=None, 3145 bg2=None, 3146 size=None, 3147 title=None, 3148 screenshot="", 3149 ) -> Any: 3150 """ 3151 Render a list of objects. 3152 3153 Arguments: 3154 at : (int) 3155 number of the renderer to plot to, in case of more than one exists 3156 3157 axes : (int) 3158 axis type-1 can be fully customized by passing a dictionary. 3159 Check `addons.Axes()` for the full list of options. 3160 set the type of axes to be shown: 3161 - 0, no axes 3162 - 1, draw three gray grid walls 3163 - 2, show cartesian axes from (0,0,0) 3164 - 3, show positive range of cartesian axes from (0,0,0) 3165 - 4, show a triad at bottom left 3166 - 5, show a cube at bottom left 3167 - 6, mark the corners of the bounding box 3168 - 7, draw a 3D ruler at each side of the cartesian axes 3169 - 8, show the `vtkCubeAxesActor` object 3170 - 9, show the bounding box outLine 3171 - 10, show three circles representing the maximum bounding box 3172 - 11, show a large grid on the x-y plane 3173 - 12, show polar axes 3174 - 13, draw a simple ruler at the bottom of the window 3175 3176 azimuth/elevation/roll : (float) 3177 move camera accordingly the specified value 3178 3179 viewup: str, list 3180 either `['x', 'y', 'z']` or a vector to set vertical direction 3181 3182 resetcam : (bool) 3183 re-adjust camera position to fit objects 3184 3185 camera : (dict, vtkCamera) 3186 camera parameters can further be specified with a dictionary 3187 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3188 - pos, `(list)`, the position of the camera in world coordinates 3189 - focal_point `(list)`, the focal point of the camera in world coordinates 3190 - viewup `(list)`, the view up direction for the camera 3191 - distance `(float)`, set the focal point to the specified distance from the camera position. 3192 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3193 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3194 in world-coordinate distances. The default is 1. 3195 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3196 This method has no effect in perspective projection mode. 3197 3198 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3199 plane to be set a distance 'thickness' beyond the near clipping plane. 3200 3201 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3202 measured in degrees. The default angle is 30 degrees. 3203 This method has no effect in parallel projection mode. 3204 The formula for setting the angle up for perfect perspective viewing is: 3205 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3206 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3207 3208 interactive : (bool) 3209 pause and interact with window (True) or continue execution (False) 3210 3211 rate : (float) 3212 maximum rate of `show()` in Hertz 3213 3214 mode : (int, str) 3215 set the type of interaction: 3216 - 0 = TrackballCamera [default] 3217 - 1 = TrackballActor 3218 - 2 = JoystickCamera 3219 - 3 = JoystickActor 3220 - 4 = Flight 3221 - 5 = RubberBand2D 3222 - 6 = RubberBand3D 3223 - 7 = RubberBandZoom 3224 - 8 = Terrain 3225 - 9 = Unicam 3226 - 10 = Image 3227 - Check out `vedo.interaction_modes` for more options. 3228 3229 bg : (str, list) 3230 background color in RGB format, or string name 3231 3232 bg2 : (str, list) 3233 second background color to create a gradient background 3234 3235 size : (str, list) 3236 size of the window, e.g. size="fullscreen", or size=[600,400] 3237 3238 title : (str) 3239 window title text 3240 3241 screenshot : (str) 3242 save a screenshot of the window to file 3243 """ 3244 3245 if vedo.settings.dry_run_mode >= 2: 3246 return self 3247 3248 if self.wx_widget: 3249 return self 3250 3251 if self.renderers: # in case of notebooks 3252 3253 if at is None: 3254 at = self.renderers.index(self.renderer) 3255 3256 else: 3257 3258 if at >= len(self.renderers): 3259 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3260 vedo.logger.error(t) 3261 return self 3262 3263 self.renderer = self.renderers[at] 3264 3265 if title is not None: 3266 self.title = title 3267 3268 if size is not None: 3269 self.size = size 3270 if self.size[0] == "f": # full screen 3271 self.size = "fullscreen" 3272 self.window.SetFullScreen(True) 3273 self.window.BordersOn() 3274 else: 3275 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3276 3277 if vedo.settings.default_backend == "vtk": 3278 if str(bg).endswith(".hdr"): 3279 self._add_skybox(bg) 3280 else: 3281 if bg is not None: 3282 self.backgrcol = vedo.get_color(bg) 3283 self.renderer.SetBackground(self.backgrcol) 3284 if bg2 is not None: 3285 self.renderer.GradientBackgroundOn() 3286 self.renderer.SetBackground2(vedo.get_color(bg2)) 3287 3288 if axes is not None: 3289 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3290 objects = list(objects) 3291 objects.append(axes) # move it into the list of normal things to show 3292 axes = 0 3293 self.axes = axes 3294 3295 if interactive is not None: 3296 self._interactive = interactive 3297 if self.offscreen: 3298 self._interactive = False 3299 3300 # camera stuff 3301 if resetcam is not None: 3302 self.resetcam = resetcam 3303 3304 if camera is not None: 3305 self.resetcam = False 3306 viewup = "" 3307 if isinstance(camera, vtki.vtkCamera): 3308 cameracopy = vtki.vtkCamera() 3309 cameracopy.DeepCopy(camera) 3310 self.camera = cameracopy 3311 else: 3312 self.camera = utils.camera_from_dict(camera) 3313 3314 self.add(objects) 3315 3316 # Backend ############################################################### 3317 if vedo.settings.default_backend in ["k3d"]: 3318 return backends.get_notebook_backend(self.objects) 3319 ######################################################################### 3320 3321 for ia in utils.flatten(objects): 3322 try: 3323 # fix gray color labels and title to white or black 3324 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3325 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3326 c = (0.9, 0.9, 0.9) 3327 if np.sum(self.renderer.GetBackground()) > 1.5: 3328 c = (0.1, 0.1, 0.1) 3329 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3330 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3331 except AttributeError: 3332 pass 3333 3334 if self.sharecam: 3335 for r in self.renderers: 3336 r.SetActiveCamera(self.camera) 3337 3338 if self.axes is not None: 3339 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3340 bns = self.renderer.ComputeVisiblePropBounds() 3341 addons.add_global_axes(self.axes, bounds=bns) 3342 3343 # Backend ############################################################### 3344 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3345 return backends.get_notebook_backend() 3346 ######################################################################### 3347 3348 if self.resetcam: 3349 self.renderer.ResetCamera() 3350 3351 if len(self.renderers) > 1: 3352 self.add_renderer_frame() 3353 3354 if vedo.settings.default_backend == "2d" and not zoom: 3355 zoom = "tightest" 3356 3357 if zoom: 3358 if zoom == "tight": 3359 self.reset_camera(tight=0.04) 3360 elif zoom == "tightest": 3361 self.reset_camera(tight=0.0001) 3362 else: 3363 self.camera.Zoom(zoom) 3364 if elevation: 3365 self.camera.Elevation(elevation) 3366 if azimuth: 3367 self.camera.Azimuth(azimuth) 3368 if roll: 3369 self.camera.Roll(roll) 3370 3371 if len(viewup) > 0: 3372 b = self.renderer.ComputeVisiblePropBounds() 3373 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3374 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3375 if viewup == "x": 3376 sz = np.linalg.norm(sz) 3377 self.camera.SetViewUp([1, 0, 0]) 3378 self.camera.SetPosition(cm + sz) 3379 elif viewup == "y": 3380 sz = np.linalg.norm(sz) 3381 self.camera.SetViewUp([0, 1, 0]) 3382 self.camera.SetPosition(cm + sz) 3383 elif viewup == "z": 3384 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3385 self.camera.SetViewUp([0, 0, 1]) 3386 self.camera.SetPosition(cm + 2 * sz) 3387 elif utils.is_sequence(viewup): 3388 sz = np.linalg.norm(sz) 3389 self.camera.SetViewUp(viewup) 3390 cpos = np.cross([0, 1, 0], viewup) 3391 self.camera.SetPosition(cm - 2 * sz * cpos) 3392 3393 self.renderer.ResetCameraClippingRange() 3394 3395 self.initialize_interactor() 3396 3397 if vedo.settings.immediate_rendering: 3398 self.window.Render() ##################### <-------------- Render 3399 3400 if self.interactor: # can be offscreen or not the vtk backend.. 3401 3402 self.window.SetWindowName(self.title) 3403 3404 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3405 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3406 # print(pic.dataset)# Array 0 name PNGImage 3407 # self.window.SetIcon(pic.dataset) 3408 3409 try: 3410 # Needs "pip install pyobjc" on Mac OSX 3411 if ( 3412 self._cocoa_initialized is False 3413 and "Darwin" in vedo.sys_platform 3414 and not self.offscreen 3415 ): 3416 self._cocoa_initialized = True 3417 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3418 pid = os.getpid() 3419 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3420 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3421 except: 3422 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3423 pass 3424 3425 # Set the interaction style 3426 if mode is not None: 3427 self.user_mode(mode) 3428 if self.qt_widget and mode is None: 3429 self.user_mode(0) 3430 3431 if screenshot: 3432 self.screenshot(screenshot) 3433 3434 if self._interactive: 3435 self.interactor.Start() 3436 if self._must_close_now: 3437 self.interactor.GetRenderWindow().Finalize() 3438 self.interactor.TerminateApp() 3439 self.camera = None 3440 self.renderer = None 3441 self.renderers = [] 3442 self.window = None 3443 self.interactor = None 3444 return self 3445 3446 if rate: 3447 if self.clock is None: # set clock and limit rate 3448 self._clockt0 = time.time() 3449 self.clock = 0.0 3450 else: 3451 t = time.time() - self._clockt0 3452 elapsed = t - self.clock 3453 mint = 1.0 / rate 3454 if elapsed < mint: 3455 time.sleep(mint - elapsed) 3456 self.clock = time.time() - self._clockt0 3457 3458 # 2d #################################################################### 3459 if vedo.settings.default_backend == "2d": 3460 return backends.get_notebook_backend() 3461 ######################################################################### 3462 3463 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
3466 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3467 """Add a draggable inset space into a renderer. 3468 3469 Arguments: 3470 at : (int) 3471 specify the renderer number 3472 pos : (list) 3473 icon position in the range [1-4] indicating one of the 4 corners, 3474 or it can be a tuple (x,y) as a fraction of the renderer size. 3475 size : (float) 3476 size of the square inset 3477 draggable : (bool) 3478 if True the subrenderer space can be dragged around 3479 c : (color) 3480 color of the inset frame when dragged 3481 3482 Examples: 3483 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3484 3485 ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg) 3486 """ 3487 if not self.interactor: 3488 return None 3489 3490 if not self.renderer: 3491 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3492 return None 3493 3494 options = dict(options) 3495 pos = options.pop("pos", 0) 3496 size = options.pop("size", 0.1) 3497 c = options.pop("c", "lb") 3498 at = options.pop("at", None) 3499 draggable = options.pop("draggable", True) 3500 3501 r, g, b = vedo.get_color(c) 3502 widget = vtki.vtkOrientationMarkerWidget() 3503 widget.SetOutlineColor(r, g, b) 3504 if len(objects) == 1: 3505 widget.SetOrientationMarker(objects[0].actor) 3506 else: 3507 widget.SetOrientationMarker(vedo.Assembly(objects)) 3508 3509 widget.SetInteractor(self.interactor) 3510 3511 if utils.is_sequence(pos): 3512 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3513 else: 3514 if pos < 2: 3515 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3516 elif pos == 2: 3517 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3518 elif pos == 3: 3519 widget.SetViewport(0, 0, size * 2, size * 2) 3520 elif pos == 4: 3521 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3522 widget.EnabledOn() 3523 widget.SetInteractive(draggable) 3524 if at is not None and at < len(self.renderers): 3525 widget.SetCurrentRenderer(self.renderers[at]) 3526 else: 3527 widget.SetCurrentRenderer(self.renderer) 3528 self.widgets.append(widget) 3529 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:
3531 def clear(self, at=None, deep=False) -> Self: 3532 """Clear the scene from all meshes and volumes.""" 3533 if at is not None: 3534 renderer = self.renderers[at] 3535 else: 3536 renderer = self.renderer 3537 if not renderer: 3538 return self 3539 3540 if deep: 3541 renderer.RemoveAllViewProps() 3542 else: 3543 for ob in set( 3544 self.get_meshes() 3545 + self.get_volumes() 3546 + self.objects 3547 + self.axes_instances 3548 ): 3549 if isinstance(ob, vedo.shapes.Text2D): 3550 continue 3551 self.remove(ob) 3552 try: 3553 if ob.scalarbar: 3554 self.remove(ob.scalarbar) 3555 except AttributeError: 3556 pass 3557 return self
Clear the scene from all meshes and volumes.
3559 def break_interaction(self) -> Self: 3560 """Break window interaction and return to the python execution flow""" 3561 if self.interactor: 3562 self.check_actors_trasform() 3563 self.interactor.ExitCallback() 3564 return self
Break window interaction and return to the python execution flow
3566 def freeze(self, value=True) -> Self: 3567 """Freeze the current renderer. Use this with `sharecam=False`.""" 3568 if not self.interactor: 3569 return self 3570 if not self.renderer: 3571 return self 3572 self.renderer.SetInteractive(not value) 3573 return self
Freeze the current renderer. Use this with sharecam=False
.
3575 def user_mode(self, mode) -> Self: 3576 """ 3577 Modify the user interaction mode. 3578 3579 Examples: 3580 ```python 3581 from vedo import * 3582 mode = interactor_modes.MousePan() 3583 mesh = Mesh(dataurl+"cow.vtk") 3584 plt = Plotter().user_mode(mode) 3585 plt.show(mesh, axes=1) 3586 ``` 3587 See also: 3588 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3589 """ 3590 if not self.interactor: 3591 return self 3592 3593 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3594 # print("Current style:", curr_style) 3595 if curr_style.endswith("Actor"): 3596 self.check_actors_trasform() 3597 3598 if isinstance(mode, (str, int)): 3599 # Set the style of interaction 3600 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3601 if mode in (0, "TrackballCamera"): 3602 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3603 self.interactor.RemoveObservers("CharEvent") 3604 elif mode in (1, "TrackballActor"): 3605 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3606 elif mode in (2, "JoystickCamera"): 3607 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3608 elif mode in (3, "JoystickActor"): 3609 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3610 elif mode in (4, "Flight"): 3611 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3612 elif mode in (5, "RubberBand2D"): 3613 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3614 elif mode in (6, "RubberBand3D"): 3615 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3616 elif mode in (7, "RubberBandZoom"): 3617 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3618 elif mode in (8, "Terrain"): 3619 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3620 elif mode in (9, "Unicam"): 3621 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3622 elif mode in (10, "Image", "image", "2d"): 3623 astyle = vtki.new("InteractorStyleImage") 3624 astyle.SetInteractionModeToImage3D() 3625 self.interactor.SetInteractorStyle(astyle) 3626 else: 3627 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3628 3629 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3630 # set a custom interactor style 3631 if hasattr(mode, "interactor"): 3632 mode.interactor = self.interactor 3633 mode.renderer = self.renderer # type: ignore 3634 mode.SetInteractor(self.interactor) 3635 mode.SetDefaultRenderer(self.renderer) 3636 self.interactor.SetInteractorStyle(mode) 3637 3638 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
3640 def close(self) -> Self: 3641 """Close the plotter.""" 3642 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3643 vedo.last_figure = None 3644 self.last_event = None 3645 self.sliders = [] 3646 self.buttons = [] 3647 self.widgets = [] 3648 self.hover_legends = [] 3649 self.background_renderer = None 3650 self._extralight = None 3651 3652 self.hint_widget = None 3653 self.cutter_widget = None 3654 3655 if vedo.settings.dry_run_mode >= 2: 3656 return self 3657 3658 if not hasattr(self, "window"): 3659 return self 3660 if not self.window: 3661 return self 3662 if not hasattr(self, "interactor"): 3663 return self 3664 if not self.interactor: 3665 return self 3666 3667 ################################################### 3668 try: 3669 if "Darwin" in vedo.sys_platform: 3670 self.interactor.ProcessEvents() 3671 except: 3672 pass 3673 3674 self._must_close_now = True 3675 3676 if vedo.plotter_instance == self: 3677 vedo.plotter_instance = None 3678 3679 if self.interactor and self._interactive: 3680 self.break_interaction() 3681 elif self._must_close_now: 3682 # dont call ExitCallback here 3683 if self.interactor: 3684 self.break_interaction() 3685 self.interactor.GetRenderWindow().Finalize() 3686 self.interactor.TerminateApp() 3687 self.camera = None 3688 self.renderer = None 3689 self.renderers = [] 3690 self.window = None 3691 self.interactor = None 3692 return self
Close the plotter.
3694 @property 3695 def camera(self): 3696 """Return the current active camera.""" 3697 if self.renderer: 3698 return self.renderer.GetActiveCamera()
Return the current active camera.
3707 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3708 """ 3709 Take a screenshot of the Plotter window. 3710 3711 Arguments: 3712 scale : (int) 3713 set image magnification as an integer multiplicating factor 3714 asarray : (bool) 3715 return a numpy array of the image instead of writing a file 3716 3717 Warning: 3718 If you get black screenshots try to set `interactive=False` in `show()` 3719 then call `screenshot()` and `plt.interactive()` afterwards. 3720 3721 Example: 3722 ```py 3723 from vedo import * 3724 sphere = Sphere().linewidth(1) 3725 plt = show(sphere, interactive=False) 3726 plt.screenshot('image.png') 3727 plt.interactive() 3728 plt.close() 3729 ``` 3730 3731 Example: 3732 ```py 3733 from vedo import * 3734 sphere = Sphere().linewidth(1) 3735 plt = show(sphere, interactive=False) 3736 plt.screenshot('anotherimage.png') 3737 plt.interactive() 3738 plt.close() 3739 ``` 3740 """ 3741 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()
3743 def toimage(self, scale=1) -> "vedo.image.Image": 3744 """ 3745 Generate a `Image` object from the current rendering window. 3746 3747 Arguments: 3748 scale : (int) 3749 set image magnification as an integer multiplicating factor 3750 """ 3751 if vedo.settings.screeshot_large_image: 3752 w2if = vtki.new("RenderLargeImage") 3753 w2if.SetInput(self.renderer) 3754 w2if.SetMagnification(scale) 3755 else: 3756 w2if = vtki.new("WindowToImageFilter") 3757 w2if.SetInput(self.window) 3758 if hasattr(w2if, "SetScale"): 3759 w2if.SetScale(scale, scale) 3760 if vedo.settings.screenshot_transparent_background: 3761 w2if.SetInputBufferTypeToRGBA() 3762 w2if.ReadFrontBufferOff() # read from the back buffer 3763 w2if.Update() 3764 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
3766 def export(self, filename="scene.npz", binary=False) -> Self: 3767 """ 3768 Export scene to file to HTML, X3D or Numpy file. 3769 3770 Examples: 3771 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3772 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3773 """ 3774 vedo.file_io.export_window(filename, binary=binary) 3775 return self
3777 def color_picker(self, xy, verbose=False): 3778 """Pick color of specific (x,y) pixel on the screen.""" 3779 w2if = vtki.new("WindowToImageFilter") 3780 w2if.SetInput(self.window) 3781 w2if.ReadFrontBufferOff() 3782 w2if.Update() 3783 nx, ny = self.window.GetSize() 3784 varr = w2if.GetOutput().GetPointData().GetScalars() 3785 3786 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3787 x, y = int(xy[0]), int(xy[1]) 3788 if y < ny and x < nx: 3789 3790 rgb = arr[y, x] 3791 3792 if verbose: 3793 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3794 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3795 vedo.printc("█", c=[0, rgb[1], 0], end="") 3796 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3797 vedo.printc("] = ", end="") 3798 cnm = vedo.get_color_name(rgb) 3799 if np.sum(rgb) < 150: 3800 vedo.printc( 3801 rgb.tolist(), 3802 vedo.colors.rgb2hex(np.array(rgb) / 255), 3803 c="w", 3804 bc=rgb, 3805 invert=1, 3806 end="", 3807 ) 3808 vedo.printc(" -> " + cnm, invert=1, c="w") 3809 else: 3810 vedo.printc( 3811 rgb.tolist(), 3812 vedo.colors.rgb2hex(np.array(rgb) / 255), 3813 c=rgb, 3814 end="", 3815 ) 3816 vedo.printc(" -> " + cnm, c=cnm) 3817 3818 return rgb 3819 3820 return None
Pick color of specific (x,y) pixel on the screen.
118def show( 119 *objects, 120 at=None, 121 shape=(1, 1), 122 N=None, 123 pos=(0, 0), 124 size="auto", 125 screensize="auto", 126 title="vedo", 127 bg="white", 128 bg2=None, 129 axes=None, 130 interactive=None, 131 offscreen=False, 132 sharecam=True, 133 resetcam=True, 134 zoom=None, 135 viewup="", 136 azimuth=0.0, 137 elevation=0.0, 138 roll=0.0, 139 camera=None, 140 mode=None, 141 screenshot="", 142 new=False, 143) -> Union[Self, None]: 144 """ 145 Create on the fly an instance of class Plotter and show the object(s) provided. 146 147 Arguments: 148 at : (int) 149 number of the renderer to plot to, in case of more than one exists 150 shape : (list, str) 151 Number of sub-render windows inside of the main window. E.g.: 152 specify two across with shape=(2,1) and a two by two grid 153 with shape=(2, 2). By default there is only one renderer. 154 155 Can also accept a shape as string descriptor. E.g.: 156 - shape="3|1" means 3 plots on the left and 1 on the right, 157 - shape="4/2" means 4 plots on top of 2 at bottom. 158 N : (int) 159 number of desired sub-render windows arranged automatically in a grid 160 pos : (list) 161 position coordinates of the top-left corner of the rendering window 162 on the screen 163 size : (list) 164 size of the rendering window 165 screensize : (list) 166 physical size of the monitor screen 167 title : (str) 168 window title 169 bg : (color) 170 background color or specify jpg image file name with path 171 bg2 : (color) 172 background color of a gradient towards the top 173 axes : (int) 174 set the type of axes to be shown: 175 - 0, no axes 176 - 1, draw three gray grid walls 177 - 2, show cartesian axes from (0,0,0) 178 - 3, show positive range of cartesian axes from (0,0,0) 179 - 4, show a triad at bottom left 180 - 5, show a cube at bottom left 181 - 6, mark the corners of the bounding box 182 - 7, draw a 3D ruler at each side of the cartesian axes 183 - 8, show the `vtkCubeAxesActor` object 184 - 9, show the bounding box outLine 185 - 10, show three circles representing the maximum bounding box 186 - 11, show a large grid on the x-y plane 187 - 12, show polar axes 188 - 13, draw a simple ruler at the bottom of the window 189 - 14: draw a `CameraOrientationWidget` 190 191 Axis type-1 can be fully customized by passing a dictionary. 192 Check `vedo.addons.Axes()` for the full list of options. 193 azimuth/elevation/roll : (float) 194 move camera accordingly the specified value 195 viewup : (str, list) 196 either `['x', 'y', 'z']` or a vector to set vertical direction 197 resetcam : (bool) 198 re-adjust camera position to fit objects 199 camera : (dict, vtkCamera) 200 camera parameters can further be specified with a dictionary 201 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 202 - **pos** (list), the position of the camera in world coordinates 203 - **focal_point** (list), the focal point of the camera in world coordinates 204 - **viewup** (list), the view up direction for the camera 205 - **distance** (float), set the focal point to the specified distance from the camera position. 206 - **clipping_range** (float), distance of the near and far clipping planes along the direction of projection. 207 - **parallel_scale** (float), 208 scaling used for a parallel projection, i.e. the height of the viewport 209 in world-coordinate distances. The default is 1. Note that the "scale" parameter works as 210 an "inverse scale", larger numbers produce smaller images. 211 This method has no effect in perspective projection mode. 212 - **thickness** (float), 213 set the distance between clipping planes. This method adjusts the far clipping 214 plane to be set a distance 'thickness' beyond the near clipping plane. 215 - **view_angle** (float), 216 the camera view angle, which is the angular height of the camera view 217 measured in degrees. The default angle is 30 degrees. 218 This method has no effect in parallel projection mode. 219 The formula for setting the angle up for perfect perspective viewing is: 220 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 221 (measured by holding a ruler up to your screen) and d is the distance 222 from your eyes to the screen. 223 interactive : (bool) 224 pause and interact with window (True) or continue execution (False) 225 rate : (float) 226 maximum rate of `show()` in Hertz 227 mode : (int, str) 228 set the type of interaction: 229 - 0 = TrackballCamera [default] 230 - 1 = TrackballActor 231 - 2 = JoystickCamera 232 - 3 = JoystickActor 233 - 4 = Flight 234 - 5 = RubberBand2D 235 - 6 = RubberBand3D 236 - 7 = RubberBandZoom 237 - 8 = Terrain 238 - 9 = Unicam 239 - 10 = Image 240 new : (bool) 241 if set to `True`, a call to show will instantiate 242 a new Plotter object (a new window) instead of reusing the first created. 243 If new is `True`, but the existing plotter was instantiated with a different 244 argument for `offscreen`, `new` is ignored and a new Plotter is created anyway. 245 """ 246 if len(objects) == 0: 247 objects = None 248 elif len(objects) == 1: 249 objects = objects[0] 250 else: 251 objects = utils.flatten(objects) 252 253 # If a plotter instance is already present, check if the offscreen argument 254 # is the same as the one requested by the user. If not, create a new 255 # plotter instance (see https://github.com/marcomusy/vedo/issues/1026) 256 if vedo.plotter_instance and vedo.plotter_instance.offscreen != offscreen: 257 new = True 258 259 if vedo.plotter_instance and not new: # Plotter exists 260 plt = vedo.plotter_instance 261 262 else: # Plotter must be created 263 264 if utils.is_sequence(at): # user passed a sequence for "at" 265 266 if not utils.is_sequence(objects): 267 vedo.logger.error("in show() input must be a list.") 268 raise RuntimeError() 269 if len(at) != len(objects): 270 vedo.logger.error("in show() lists 'input' and 'at' must have equal lengths") 271 raise RuntimeError() 272 if shape == (1, 1) and N is None: 273 N = max(at) + 1 274 275 elif at is None and (N or shape != (1, 1)): 276 277 if not utils.is_sequence(objects): 278 e = "in show(), N or shape is set, but input is not a sequence\n" 279 e += " you may need to specify e.g. at=0" 280 vedo.logger.error(e) 281 raise RuntimeError() 282 at = list(range(len(objects))) 283 284 plt = Plotter( 285 shape=shape, 286 N=N, 287 pos=pos, 288 size=size, 289 screensize=screensize, 290 title=title, 291 axes=axes, 292 sharecam=sharecam, 293 resetcam=resetcam, 294 interactive=interactive, 295 offscreen=offscreen, 296 bg=bg, 297 bg2=bg2, 298 ) 299 300 if vedo.settings.dry_run_mode >= 2: 301 return plt 302 303 # use _plt_to_return because plt.show() can return a k3d plot 304 _plt_to_return = None 305 306 if utils.is_sequence(at): 307 308 for i, act in enumerate(objects): 309 _plt_to_return = plt.show( 310 act, 311 at=i, 312 zoom=zoom, 313 resetcam=resetcam, 314 viewup=viewup, 315 azimuth=azimuth, 316 elevation=elevation, 317 roll=roll, 318 camera=camera, 319 interactive=False, 320 mode=mode, 321 screenshot=screenshot, 322 bg=bg, 323 bg2=bg2, 324 axes=axes, 325 ) 326 327 if ( 328 interactive 329 or len(at) == N 330 or (isinstance(shape[0], int) and len(at) == shape[0] * shape[1]) 331 ): 332 # note that shape can be a string 333 if plt.interactor and not offscreen and (interactive is None or interactive): 334 plt.interactor.Start() 335 if plt._must_close_now: 336 plt.interactor.GetRenderWindow().Finalize() 337 plt.interactor.TerminateApp() 338 plt.interactor = None 339 plt.window = None 340 plt.renderer = None 341 plt.renderers = [] 342 plt.camera = None 343 344 else: 345 346 _plt_to_return = plt.show( 347 objects, 348 at=at, 349 zoom=zoom, 350 resetcam=resetcam, 351 viewup=viewup, 352 azimuth=azimuth, 353 elevation=elevation, 354 roll=roll, 355 camera=camera, 356 interactive=interactive, 357 mode=mode, 358 screenshot=screenshot, 359 bg=bg, 360 bg2=bg2, 361 axes=axes, 362 ) 363 364 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.
367def close() -> None: 368 """Close the last created Plotter instance if it exists.""" 369 if not vedo.plotter_instance: 370 return 371 vedo.plotter_instance.close() 372 return
Close the last created Plotter instance if it exists.