vedo.plotter
Module containing the Plotter class for managing objects and 3D rendering.
The Plotter class is the main interface for creating visualization windows, rendering 3D scenes, handling interactions, managing renderers, cameras, and visualization settings. It provides methods for displaying meshes, creating screenshots, handling user events, and configuring the visualization pipeline.
Key functionality:
- Create and manage multiple render windows
Handle user interaction via mouse and keyboard events
Control camera position and settings
Configure lighting and shading
Add text labels, scalar bars and other annotations
Export visualizations to images and movies
1# -*- coding: utf-8 -*- 2import os.path 3import sys 4import time 5from typing import MutableSequence, Callable, Any, Union 6from typing_extensions import Self 7import numpy as np 8 9import vedo.vtkclasses as vtki # a wrapper for lazy imports 10 11import vedo 12from vedo import transformations 13from vedo import utils 14from vedo import backends 15from vedo import addons 16 17 18__doc__ = """ 19Module containing the Plotter class for managing objects and 3D rendering. 20 21The Plotter class is the main interface for creating visualization windows, 22rendering 3D scenes, handling interactions, managing renderers, cameras, 23and visualization settings. It provides methods for displaying meshes, creating 24screenshots, handling user events, and configuring the visualization pipeline. 25 26Key functionality: 27- Create and manage multiple render windows 28 29- Handle user interaction via mouse and keyboard events 30 31- Control camera position and settings 32 33- Configure lighting and shading 34 35- Add text labels, scalar bars and other annotations 36 37- Export visualizations to images and movies 38 39 40""" 41__docformat__ = "google" 42 43__all__ = ["Plotter", "show", "close"] 44 45######################################################################################## 46class Event: 47 """ 48 This class holds the info from an event in the window, works as dictionary too. 49 """ 50 51 __slots__ = [ 52 "name", 53 "title", 54 "id", 55 "timerid", 56 "time", 57 "priority", 58 "at", 59 "object", 60 "actor", 61 "picked3d", 62 "keypress", 63 "picked2d", 64 "delta2d", 65 "angle2d", 66 "speed2d", 67 "delta3d", 68 "speed3d", 69 "isPoints", 70 "isMesh", 71 "isAssembly", 72 "isVolume", 73 "isImage", 74 "isActor2D", 75 ] 76 77 def __init__(self): 78 self.name = "event" 79 self.title = "" 80 self.id = 0 81 self.timerid = 0 82 self.time = 0 83 self.priority = 0 84 self.at = 0 85 self.object = None 86 self.actor = None 87 self.picked3d = () 88 self.keypress = "" 89 self.picked2d = () 90 self.delta2d = () 91 self.angle2d = 0 92 self.speed2d = () 93 self.delta3d = () 94 self.speed3d = 0 95 self.isPoints = False 96 self.isMesh = False 97 self.isAssembly = False 98 self.isVolume = False 99 self.isImage = False 100 self.isActor2D = False 101 102 def __getitem__(self, key): 103 return getattr(self, key) 104 105 def __setitem__(self, key, value): 106 setattr(self, key, value) 107 108 def __str__(self): 109 module = self.__class__.__module__ 110 name = self.__class__.__name__ 111 out = vedo.printc( 112 f"{module}.{name} at ({hex(id(self))})".ljust(75), 113 bold=True, invert=True, return_string=True, 114 ) 115 out += "\x1b[0m" 116 for n in self.__slots__: 117 if n == "actor": 118 continue 119 out += f"{n}".ljust(11) + ": " 120 val = str(self[n]).replace("\n", "")[:65].rstrip() 121 if val == "True": 122 out += "\x1b[32;1m" 123 elif val == "False": 124 out += "\x1b[31;1m" 125 out += val + "\x1b[0m\n" 126 return out.rstrip() 127 128 def keys(self): 129 """Return the list of keys.""" 130 return self.__slots__ 131 132 133############################################################################################## 134def show( 135 *objects, 136 at=None, 137 shape=(1, 1), 138 N=None, 139 pos=(0, 0), 140 size="auto", 141 screensize="auto", 142 title="vedo", 143 bg="white", 144 bg2=None, 145 axes=None, 146 interactive=None, 147 offscreen=False, 148 sharecam=True, 149 resetcam=True, 150 zoom=None, 151 viewup="", 152 azimuth=0.0, 153 elevation=0.0, 154 roll=0.0, 155 camera=None, 156 mode=None, 157 screenshot="", 158 new=False, 159) -> Union[Self, None]: 160 """ 161 Create on the fly an instance of class Plotter and show the object(s) provided. 162 163 Arguments: 164 at : (int) 165 number of the renderer to plot to, in case of more than one exists 166 shape : (list, str) 167 Number of sub-render windows inside of the main window. E.g.: 168 specify two across with shape=(2,1) and a two by two grid 169 with shape=(2, 2). By default there is only one renderer. 170 171 Can also accept a shape as string descriptor. E.g.: 172 - shape="3|1" means 3 plots on the left and 1 on the right, 173 - shape="4/2" means 4 plots on top of 2 at bottom. 174 175 N : (int) 176 number of desired sub-render windows arranged automatically in a grid 177 pos : (list) 178 position coordinates of the top-left corner of the rendering window 179 on the screen 180 size : (list) 181 size of the rendering window 182 screensize : (list) 183 physical size of the monitor screen 184 title : (str) 185 window title 186 bg : (color) 187 background color or specify jpg image file name with path 188 bg2 : (color) 189 background color of a gradient towards the top 190 axes : (int) 191 set the type of axes to be shown: 192 - 0, no axes 193 - 1, draw three gray grid walls 194 - 2, show cartesian axes from (0,0,0) 195 - 3, show positive range of cartesian axes from (0,0,0) 196 - 4, show a triad at bottom left 197 - 5, show a cube at bottom left 198 - 6, mark the corners of the bounding box 199 - 7, draw a 3D ruler at each side of the cartesian axes 200 - 8, show the `vtkCubeAxesActor` object 201 - 9, show the bounding box outLine 202 - 10, show three circles representing the maximum bounding box 203 - 11, show a large grid on the x-y plane 204 - 12, show polar axes 205 - 13, draw a simple ruler at the bottom of the window 206 - 14: draw a `CameraOrientationWidget` 207 208 Axis type-1 can be fully customized by passing a dictionary. 209 Check `vedo.addons.Axes()` for the full list of options. 210 azimuth/elevation/roll : (float) 211 move camera accordingly the specified value 212 viewup : (str, list) 213 either `['x', 'y', 'z']` or a vector to set vertical direction 214 resetcam : (bool) 215 re-adjust camera position to fit objects 216 camera : (dict, vtkCamera) 217 camera parameters can further be specified with a dictionary 218 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 219 - **pos** (list), the position of the camera in world coordinates 220 - **focal_point** (list), the focal point of the camera in world coordinates 221 - **viewup** (list), the view up direction for the camera 222 - **distance** (float), set the focal point to the specified distance from the camera position. 223 - **clipping_range** (float), distance of the near and far clipping planes along the direction of projection. 224 - **parallel_scale** (float), 225 scaling used for a parallel projection, i.e. the height of the viewport 226 in world-coordinate distances. The default is 1. Note that the "scale" parameter works as 227 an "inverse scale", larger numbers produce smaller images. 228 This method has no effect in perspective projection mode. 229 - **thickness** (float), 230 set the distance between clipping planes. This method adjusts the far clipping 231 plane to be set a distance 'thickness' beyond the near clipping plane. 232 - **view_angle** (float), 233 the camera view angle, which is the angular height of the camera view 234 measured in degrees. The default angle is 30 degrees. 235 This method has no effect in parallel projection mode. 236 The formula for setting the angle up for perfect perspective viewing is: 237 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 238 (measured by holding a ruler up to your screen) and d is the distance 239 from your eyes to the screen. 240 interactive : (bool) 241 pause and interact with window (True) or continue execution (False) 242 rate : (float) 243 maximum rate of `show()` in Hertz 244 mode : (int, str) 245 set the type of interaction: 246 - 0 = TrackballCamera [default] 247 - 1 = TrackballActor 248 - 2 = JoystickCamera 249 - 3 = JoystickActor 250 - 4 = Flight 251 - 5 = RubberBand2D 252 - 6 = RubberBand3D 253 - 7 = RubberBandZoom 254 - 8 = Terrain 255 - 9 = Unicam 256 - 10 = Image 257 new : (bool) 258 if set to `True`, a call to show will instantiate 259 a new Plotter object (a new window) instead of reusing the first created. 260 If new is `True`, but the existing plotter was instantiated with a different 261 argument for `offscreen`, `new` is ignored and a new Plotter is created anyway. 262 """ 263 if len(objects) == 0: 264 objects = None 265 elif len(objects) == 1: 266 objects = objects[0] 267 else: 268 objects = utils.flatten(objects) 269 270 # If a plotter instance is already present, check if the offscreen argument 271 # is the same as the one requested by the user. If not, create a new 272 # plotter instance (see https://github.com/marcomusy/vedo/issues/1026) 273 if vedo.plotter_instance and vedo.plotter_instance.offscreen != offscreen: 274 new = True 275 276 if vedo.plotter_instance and not new: # Plotter exists 277 plt = vedo.plotter_instance 278 279 else: # Plotter must be created 280 281 if utils.is_sequence(at): # user passed a sequence for "at" 282 283 if not utils.is_sequence(objects): 284 vedo.logger.error("in show() input must be a list.") 285 raise RuntimeError() 286 if len(at) != len(objects): 287 vedo.logger.error("in show() lists 'input' and 'at' must have equal lengths") 288 raise RuntimeError() 289 if shape == (1, 1) and N is None: 290 N = max(at) + 1 291 292 elif at is None and (N or shape != (1, 1)): 293 294 if not utils.is_sequence(objects): 295 e = "in show(), N or shape is set, but input is not a sequence\n" 296 e += " you may need to specify e.g. at=0" 297 vedo.logger.error(e) 298 raise RuntimeError() 299 at = list(range(len(objects))) 300 301 plt = Plotter( 302 shape=shape, 303 N=N, 304 pos=pos, 305 size=size, 306 screensize=screensize, 307 title=title, 308 axes=axes, 309 sharecam=sharecam, 310 resetcam=resetcam, 311 interactive=interactive, 312 offscreen=offscreen, 313 bg=bg, 314 bg2=bg2, 315 ) 316 317 if vedo.settings.dry_run_mode >= 2: 318 return plt 319 320 # use _plt_to_return because plt.show() can return a k3d plot 321 _plt_to_return = None 322 323 if utils.is_sequence(at): 324 325 for i, act in enumerate(objects): 326 _plt_to_return = plt.show( 327 act, 328 at=i, 329 zoom=zoom, 330 resetcam=resetcam, 331 viewup=viewup, 332 azimuth=azimuth, 333 elevation=elevation, 334 roll=roll, 335 camera=camera, 336 interactive=False, 337 mode=mode, 338 screenshot=screenshot, 339 bg=bg, 340 bg2=bg2, 341 axes=axes, 342 ) 343 344 if ( 345 interactive 346 or len(at) == N 347 or (isinstance(shape[0], int) and len(at) == shape[0] * shape[1]) 348 ): 349 # note that shape can be a string 350 if plt.interactor and not offscreen and (interactive is None or interactive): 351 plt.interactor.Start() 352 if plt._must_close_now: 353 plt.interactor.GetRenderWindow().Finalize() 354 plt.interactor.TerminateApp() 355 plt.interactor = None 356 plt.window = None 357 plt.renderer = None 358 plt.renderers = [] 359 plt.camera = None 360 361 else: 362 363 _plt_to_return = plt.show( 364 objects, 365 at=at, 366 zoom=zoom, 367 resetcam=resetcam, 368 viewup=viewup, 369 azimuth=azimuth, 370 elevation=elevation, 371 roll=roll, 372 camera=camera, 373 interactive=interactive, 374 mode=mode, 375 screenshot=screenshot, 376 bg=bg, 377 bg2=bg2, 378 axes=axes, 379 ) 380 381 return _plt_to_return 382 383 384def close() -> None: 385 """Close the last created Plotter instance if it exists.""" 386 if not vedo.plotter_instance: 387 return 388 vedo.plotter_instance.close() 389 return 390 391 392######################################################################## 393class Plotter: 394 """Main class to manage objects.""" 395 396 def __init__( 397 self, 398 shape=(1, 1), 399 N=None, 400 pos=(0, 0), 401 size="auto", 402 screensize="auto", 403 title="vedo", 404 bg="white", 405 bg2=None, 406 axes=None, 407 sharecam=True, 408 resetcam=True, 409 interactive=None, 410 offscreen=False, 411 qt_widget=None, 412 wx_widget=None, 413 ): 414 """ 415 Arguments: 416 shape : (str, list) 417 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 418 N : (int) 419 number of desired renderers arranged in a grid automatically. 420 pos : (list) 421 (x,y) position in pixels of top-left corner of the rendering window on the screen 422 size : (str, list) 423 size of the rendering window. If 'auto', guess it based on screensize. 424 screensize : (list) 425 physical size of the monitor screen in pixels 426 bg : (color, str) 427 background color or specify jpg image file name with path 428 bg2 : (color) 429 background color of a gradient towards the top 430 title : (str) 431 window title 432 axes : (int) 433 axis type-1 can be fully customized by passing a dictionary. 434 Check `addons.Axes()` for the full list of options. 435 Set the type of axes to be shown: 436 - 0, no axes 437 - 1, draw three gray grid walls 438 - 2, show cartesian axes from (0,0,0) 439 - 3, show positive range of cartesian axes from (0,0,0) 440 - 4, show a triad at bottom left 441 - 5, show a cube at bottom left 442 - 6, mark the corners of the bounding box 443 - 7, draw a 3D ruler at each side of the cartesian axes 444 - 8, show the `vtkCubeAxesActor` object 445 - 9, show the bounding box outLine 446 - 10, show three circles representing the maximum bounding box 447 - 11, show a large grid on the x-y plane 448 - 12, show polar axes 449 - 13, draw a simple ruler at the bottom of the window 450 - 14: draw a camera orientation widget 451 452 sharecam : (bool) 453 if False each renderer will have an independent camera 454 interactive : (bool) 455 if True will stop after show() to allow interaction with the 3d scene 456 offscreen : (bool) 457 if True will not show the rendering window 458 qt_widget : (QVTKRenderWindowInteractor) 459 render in a Qt-Widget using an QVTKRenderWindowInteractor. 460 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 461 """ 462 vedo.plotter_instance = self 463 464 if interactive is None: 465 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 466 self._interactive = interactive 467 # print("interactive", interactive, N, shape) 468 469 self.objects = [] # list of objects to be shown 470 self.clicked_object = None # holds the object that has been clicked 471 self.clicked_actor = None # holds the actor that has been clicked 472 473 self.shape = shape # nr. of subwindows in grid 474 self.axes = axes # show axes type nr. 475 self.title = title # window title 476 self.size = size # window size 477 self.backgrcol = bg # used also by backend notebooks 478 479 self.offscreen= offscreen 480 self.resetcam = resetcam 481 self.sharecam = sharecam # share the same camera if multiple renderers 482 self.pos = pos # used by vedo.file_io 483 484 self.picker = None # hold the vtkPicker object 485 self.picked2d = None # 2d coords of a clicked point on the rendering window 486 self.picked3d = None # 3d coords of a clicked point on an actor 487 488 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 489 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 490 self.interactor = None 491 self.window = None 492 self.renderer = None 493 self.renderers = [] # list of renderers 494 495 # mostly internal stuff: 496 self.hover_legends = [] 497 self.justremoved = None 498 self.axes_instances = [] 499 self.clock = 0 500 self.sliders = [] 501 self.buttons = [] 502 self.widgets = [] 503 self.cutter_widget = None 504 self.hint_widget = None 505 self.background_renderer = None 506 self.last_event = None 507 self.skybox = None 508 self._icol = 0 509 self._clockt0 = time.time() 510 self._extralight = None 511 self._cocoa_initialized = False 512 self._cocoa_process_events = True # make one call in show() 513 self._must_close_now = False 514 515 ##################################################################### 516 if vedo.settings.default_backend == "2d": 517 self.offscreen = True 518 if self.size == "auto": 519 self.size = (800, 600) 520 521 elif vedo.settings.default_backend == "k3d": 522 if self.size == "auto": 523 self.size = (1000, 1000) 524 #################################### 525 return ############################ 526 #################################### 527 528 ############################################################# 529 if screensize == "auto": 530 screensize = (2160, 1440) # TODO: get actual screen size 531 532 # build the rendering window: 533 self.window = vtki.vtkRenderWindow() 534 535 self.window.GlobalWarningDisplayOff() 536 537 if self.title == "vedo": # check if dev version 538 if "dev" in vedo.__version__: 539 self.title = f"vedo ({vedo.__version__})" 540 self.window.SetWindowName(self.title) 541 542 # more vedo.settings 543 if vedo.settings.use_depth_peeling: 544 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 545 self.window.SetMultiSamples(vedo.settings.multi_samples) 546 547 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 548 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 549 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 550 551 ############################################################# 552 if N: # N = number of renderers. Find out the best 553 554 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 555 vedo.logger.warning("having set N, shape is ignored.") 556 557 x, y = screensize 558 nx = int(np.sqrt(int(N * y / x) + 1)) 559 ny = int(np.sqrt(int(N * x / y) + 1)) 560 lm = [ 561 (nx, ny), 562 (nx, ny + 1), 563 (nx - 1, ny), 564 (nx + 1, ny), 565 (nx, ny - 1), 566 (nx - 1, ny + 1), 567 (nx + 1, ny - 1), 568 (nx + 1, ny + 1), 569 (nx - 1, ny - 1), 570 ] 571 ind, minl = 0, 1000 572 for i, m in enumerate(lm): 573 l = m[0] * m[1] 574 if N <= l < minl: 575 ind = i 576 minl = l 577 shape = lm[ind] 578 579 ################################################## 580 if isinstance(shape, str): 581 582 if "|" in shape: 583 if self.size == "auto": 584 self.size = (800, 1200) 585 n = int(shape.split("|")[0]) 586 m = int(shape.split("|")[1]) 587 rangen = reversed(range(n)) 588 rangem = reversed(range(m)) 589 else: 590 if self.size == "auto": 591 self.size = (1200, 800) 592 m = int(shape.split("/")[0]) 593 n = int(shape.split("/")[1]) 594 rangen = range(n) 595 rangem = range(m) 596 597 if n >= m: 598 xsplit = m / (n + m) 599 else: 600 xsplit = 1 - n / (n + m) 601 if vedo.settings.window_splitting_position: 602 xsplit = vedo.settings.window_splitting_position 603 604 for i in rangen: 605 arenderer = vtki.vtkRenderer() 606 if "|" in shape: 607 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 608 else: 609 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 610 self.renderers.append(arenderer) 611 612 for i in rangem: 613 arenderer = vtki.vtkRenderer() 614 615 if "|" in shape: 616 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 617 else: 618 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 619 self.renderers.append(arenderer) 620 621 for r in self.renderers: 622 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 623 624 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 625 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 626 if vedo.settings.use_depth_peeling: 627 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 628 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 629 r.SetUseFXAA(vedo.settings.use_fxaa) 630 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 631 632 r.SetBackground(vedo.get_color(self.backgrcol)) 633 634 self.axes_instances.append(None) 635 636 self.shape = (n + m,) 637 638 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 639 # passing a sequence of dicts for renderers specifications 640 641 if self.size == "auto": 642 self.size = (1000, 800) 643 644 for rd in shape: 645 x0, y0 = rd["bottomleft"] 646 x1, y1 = rd["topright"] 647 bg_ = rd.pop("bg", "white") 648 bg2_ = rd.pop("bg2", None) 649 650 arenderer = vtki.vtkRenderer() 651 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 652 653 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 654 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 655 if vedo.settings.use_depth_peeling: 656 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 657 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 658 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 659 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 660 661 arenderer.SetViewport(x0, y0, x1, y1) 662 arenderer.SetBackground(vedo.get_color(bg_)) 663 if bg2_: 664 arenderer.GradientBackgroundOn() 665 arenderer.SetBackground2(vedo.get_color(bg2_)) 666 667 self.renderers.append(arenderer) 668 self.axes_instances.append(None) 669 670 self.shape = (len(shape),) 671 672 else: 673 674 if isinstance(self.size, str) and self.size == "auto": 675 # figure out a reasonable window size 676 f = 1.5 677 x, y = screensize 678 xs = y / f * shape[1] # because y<x 679 ys = y / f * shape[0] 680 if xs > x / f: # shrink 681 xs = x / f 682 ys = xs / shape[1] * shape[0] 683 if ys > y / f: 684 ys = y / f 685 xs = ys / shape[0] * shape[1] 686 self.size = (int(xs), int(ys)) 687 if shape == (1, 1): 688 self.size = (int(y / f), int(y / f)) # because y<x 689 else: 690 self.size = (self.size[0], self.size[1]) 691 692 try: 693 image_actor = None 694 bgname = str(self.backgrcol).lower() 695 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 696 self.window.SetNumberOfLayers(2) 697 self.background_renderer = vtki.vtkRenderer() 698 self.background_renderer.SetLayer(0) 699 self.background_renderer.InteractiveOff() 700 self.background_renderer.SetBackground(vedo.get_color(bg2)) 701 image_actor = vedo.Image(self.backgrcol).actor 702 self.window.AddRenderer(self.background_renderer) 703 self.background_renderer.AddActor(image_actor) 704 except AttributeError: 705 pass 706 707 for i in reversed(range(shape[0])): 708 for j in range(shape[1]): 709 arenderer = vtki.vtkRenderer() 710 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 711 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 712 713 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 714 if vedo.settings.use_depth_peeling: 715 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 716 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 717 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 718 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 719 720 if image_actor: 721 arenderer.SetLayer(1) 722 723 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 724 if bg2: 725 arenderer.GradientBackgroundOn() 726 arenderer.SetBackground2(vedo.get_color(bg2)) 727 728 x0 = i / shape[0] 729 y0 = j / shape[1] 730 x1 = (i + 1) / shape[0] 731 y1 = (j + 1) / shape[1] 732 arenderer.SetViewport(y0, x0, y1, x1) 733 self.renderers.append(arenderer) 734 self.axes_instances.append(None) 735 self.shape = shape 736 737 if self.renderers: 738 self.renderer = self.renderers[0] 739 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 740 741 ######################################################### 742 if self.qt_widget or self.wx_widget: 743 if self.qt_widget: 744 self.window = self.qt_widget.GetRenderWindow() # overwrite 745 else: 746 self.window = self.wx_widget.GetRenderWindow() 747 self.interactor = self.window.GetInteractor() 748 749 ######################################################### 750 for r in self.renderers: 751 self.window.AddRenderer(r) 752 # set the background gradient if any 753 if vedo.settings.background_gradient_orientation > 0: 754 try: 755 modes = [ 756 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 757 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 758 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 759 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 760 ] 761 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 762 r.GradientBackgroundOn() 763 except AttributeError: 764 pass 765 766 # Backend #################################################### 767 if vedo.settings.default_backend in ["panel", "trame", "k3d"]: 768 return ################ 769 ######################## 770 771 ######################################################### 772 if self.qt_widget or self.wx_widget: 773 self.interactor.SetRenderWindow(self.window) 774 if vedo.settings.enable_default_keyboard_callbacks: 775 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 776 if vedo.settings.enable_default_mouse_callbacks: 777 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 778 return ################ 779 ######################## 780 781 if self.size[0] == "f": # full screen 782 self.size = "fullscreen" 783 self.window.SetFullScreen(True) 784 self.window.BordersOn() 785 else: 786 self.window.SetSize(int(self.size[0]), int(self.size[1])) 787 788 if self.offscreen: 789 if self.axes in (4, 5, 8, 12, 14): 790 self.axes = 0 # does not work with those 791 self.window.SetOffScreenRendering(True) 792 self.interactor = None 793 self._interactive = False 794 return ################ 795 ######################## 796 797 self.window.SetPosition(pos) 798 799 ######################################################### 800 self.interactor = vtki.vtkRenderWindowInteractor() 801 802 self.interactor.SetRenderWindow(self.window) 803 vsty = vtki.new("InteractorStyleTrackballCamera") 804 self.interactor.SetInteractorStyle(vsty) 805 self.interactor.RemoveObservers("CharEvent") 806 807 if vedo.settings.enable_default_keyboard_callbacks: 808 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 809 if vedo.settings.enable_default_mouse_callbacks: 810 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 811 812 ##################################################################### ..init ends here. 813 814 def __str__(self): 815 """Return Plotter info.""" 816 axtype = { 817 0: "(no axes)", 818 1: "(default customizable grid walls)", 819 2: "(cartesian axes from origin", 820 3: "(positive range of cartesian axes from origin", 821 4: "(axes triad at bottom left)", 822 5: "(oriented cube at bottom left)", 823 6: "(mark the corners of the bounding box)", 824 7: "(3D ruler at each side of the cartesian axes)", 825 8: "(the vtkCubeAxesActor object)", 826 9: "(the bounding box outline)", 827 10: "(circles of maximum bounding box range)", 828 11: "(show a large grid on the x-y plane)", 829 12: "(show polar axes)", 830 13: "(simple ruler at the bottom of the window)", 831 14: "(the vtkCameraOrientationWidget object)", 832 } 833 834 module = self.__class__.__module__ 835 name = self.__class__.__name__ 836 out = vedo.printc( 837 f"{module}.{name} at ({hex(id(self))})".ljust(75), 838 bold=True, invert=True, return_string=True, 839 ) 840 out += "\x1b[0m" 841 if self.interactor: 842 out += "window title".ljust(14) + ": " + self.title + "\n" 843 out += "window size".ljust(14) + f": {self.window.GetSize()}" 844 out += f", full_screen={self.window.GetScreenSize()}\n" 845 out += "activ renderer".ljust(14) + ": nr." + str(self.renderers.index(self.renderer)) 846 out += f" (out of {len(self.renderers)} renderers)\n" 847 848 bns, totpt = [], 0 849 for a in self.objects: 850 try: 851 b = a.bounds() 852 bns.append(b) 853 except (AttributeError, TypeError): 854 pass 855 try: 856 totpt += a.npoints 857 except AttributeError: 858 pass 859 out += "n. of objects".ljust(14) + f": {len(self.objects)}" 860 out += f" ({totpt} vertices)\n" if totpt else "\n" 861 862 if len(bns) > 0: 863 min_bns = np.min(bns, axis=0) 864 max_bns = np.max(bns, axis=0) 865 bx1, bx2 = utils.precision(min_bns[0], 3), utils.precision(max_bns[1], 3) 866 by1, by2 = utils.precision(min_bns[2], 3), utils.precision(max_bns[3], 3) 867 bz1, bz2 = utils.precision(min_bns[4], 3), utils.precision(max_bns[5], 3) 868 out += "bounds".ljust(14) + ":" 869 out += " x=(" + bx1 + ", " + bx2 + ")," 870 out += " y=(" + by1 + ", " + by2 + ")," 871 out += " z=(" + bz1 + ", " + bz2 + ")\n" 872 873 if utils.is_integer(self.axes): 874 out += "axes style".ljust(14) + f": {self.axes} {axtype[self.axes]}\n" 875 elif isinstance(self.axes, dict): 876 out += "axes style".ljust(14) + f": 1 {axtype[1]}\n" 877 else: 878 out += "axes style".ljust(14) + f": {[self.axes]}\n" 879 return out.rstrip() + "\x1b[0m" 880 881 def print(self): 882 """Print information about the current instance.""" 883 print(self.__str__()) 884 return self 885 886 def __iadd__(self, objects): 887 self.add(objects) 888 return self 889 890 def __isub__(self, objects): 891 self.remove(objects) 892 return self 893 894 def __enter__(self): 895 # context manager like in "with Plotter() as plt:" 896 return self 897 898 def __exit__(self, *args, **kwargs): 899 # context manager like in "with Plotter() as plt:" 900 self.close() 901 902 def initialize_interactor(self) -> Self: 903 """Initialize the interactor if not already initialized.""" 904 if self.offscreen: 905 return self 906 if self.interactor: 907 if not self.interactor.GetInitialized(): 908 self.interactor.Initialize() 909 self.interactor.RemoveObservers("CharEvent") 910 return self 911 912 def process_events(self) -> Self: 913 """Process all pending events.""" 914 self.initialize_interactor() 915 if self.interactor: 916 try: 917 self.interactor.ProcessEvents() 918 except AttributeError: 919 pass 920 return self 921 922 def at(self, nren: int, yren=None) -> Self: 923 """ 924 Select the current renderer number as an int. 925 Can also use the `[nx, ny]` format. 926 """ 927 if utils.is_sequence(nren): 928 if len(nren) == 2: 929 nren, yren = nren 930 else: 931 vedo.logger.error("at() argument must be a single number or a list of two numbers") 932 raise TypeError 933 934 if yren is not None: 935 a, b = self.shape 936 x, y = nren, yren 937 nren_orig = nren 938 nren = x * b + y 939 # print("at (", x, y, ") -> ren", nren) 940 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 941 vedo.logger.error(f"at({nren_orig, yren}) is malformed!") 942 raise RuntimeError 943 944 self.renderer = self.renderers[nren] 945 return self 946 947 def add(self, *objs, at=None) -> Self: 948 """ 949 Append the input objects to the internal list of objects to be shown. 950 951 Arguments: 952 at : (int) 953 add the object at the specified renderer 954 """ 955 ren = self.renderer if at is None else self.renderers[at] 956 957 objs = utils.flatten(objs) 958 for ob in objs: 959 if ob and ob not in self.objects: 960 self.objects.append(ob) 961 962 acts = self._scan_input_return_acts(objs) 963 964 for a in acts: 965 966 if ren: 967 if isinstance(a, vedo.addons.BaseCutter): 968 a.add_to(self) # from cutters 969 continue 970 971 if isinstance(a, vtki.vtkLight): 972 ren.AddLight(a) 973 continue 974 975 try: 976 ren.AddActor(a) 977 except TypeError: 978 ren.AddActor(a.actor) 979 980 try: 981 ir = self.renderers.index(ren) 982 a.rendered_at.add(ir) # might not have rendered_at 983 except (AttributeError, ValueError): 984 pass 985 986 if isinstance(a, vtki.vtkFollower): 987 a.SetCamera(self.camera) 988 elif isinstance(a, vedo.visual.LightKit): 989 a.lightkit.AddLightsToRenderer(ren) 990 991 return self 992 993 def remove(self, *objs, at=None) -> Self: 994 """ 995 Remove input object to the internal list of objects to be shown. 996 997 Objects to be removed can be referenced by their assigned name, 998 999 Arguments: 1000 at : (int) 1001 remove the object at the specified renderer 1002 """ 1003 # TODO and you can also use wildcards like `*` and `?`. 1004 1005 ren = self.renderer if at is None else self.renderers[at] 1006 1007 objs = [ob for ob in utils.flatten(objs) if ob] 1008 1009 has_str = False 1010 for ob in objs: 1011 if isinstance(ob, str): 1012 has_str = True 1013 break 1014 1015 has_actor = False 1016 for ob in objs: 1017 if hasattr(ob, "actor") and ob.actor: 1018 has_actor = True 1019 break 1020 1021 if has_str or has_actor: 1022 # need to get the actors to search for 1023 for a in self.get_actors(include_non_pickables=True): 1024 # print("PARSING", [a]) 1025 try: 1026 if (a.name and a.name in objs) or a in objs: 1027 objs.append(a) 1028 # if a.name: 1029 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1030 # if any(bools) or a in objs: 1031 # objs.append(a) 1032 # print('a.name',a.name, objs,any(bools)) 1033 except AttributeError: # no .name 1034 # passing the actor so get back the object with .retrieve_object() 1035 try: 1036 vobj = a.retrieve_object() 1037 if (vobj.name and vobj.name in objs) or vobj in objs: 1038 # print('vobj.name', vobj.name) 1039 objs.append(vobj) 1040 except AttributeError: 1041 pass 1042 1043 if ren is None: 1044 return self 1045 ir = self.renderers.index(ren) 1046 1047 ids = [] 1048 for ob in set(objs): 1049 1050 # will remove it from internal list if possible 1051 try: 1052 idx = self.objects.index(ob) 1053 ids.append(idx) 1054 except ValueError: 1055 pass 1056 1057 if ren: ### remove it from the renderer 1058 1059 if isinstance(ob, vedo.addons.BaseCutter): 1060 ob.remove_from(self) # from cutters 1061 continue 1062 1063 try: 1064 ren.RemoveActor(ob) 1065 except TypeError: 1066 try: 1067 ren.RemoveActor(ob.actor) 1068 except AttributeError: 1069 pass 1070 1071 if hasattr(ob, "rendered_at"): 1072 ob.rendered_at.discard(ir) 1073 1074 if hasattr(ob, "scalarbar") and ob.scalarbar: 1075 ren.RemoveActor(ob.scalarbar) 1076 if hasattr(ob, "_caption") and ob._caption: 1077 ren.RemoveActor(ob._caption) 1078 if hasattr(ob, "shadows") and ob.shadows: 1079 for sha in ob.shadows: 1080 ren.RemoveActor(sha.actor) 1081 if hasattr(ob, "trail") and ob.trail: 1082 ren.RemoveActor(ob.trail.actor) 1083 ob.trail_points = [] 1084 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1085 for sha in ob.trail.shadows: 1086 ren.RemoveActor(sha.actor) 1087 1088 elif isinstance(ob, vedo.visual.LightKit): 1089 ob.lightkit.RemoveLightsFromRenderer(ren) 1090 1091 # for i in ids: # WRONG way of doing it! 1092 # del self.objects[i] 1093 # instead we do: 1094 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1095 return self 1096 1097 @property 1098 def actors(self): 1099 """Return the list of actors.""" 1100 return [ob.actor for ob in self.objects if hasattr(ob, "actor")] 1101 1102 def remove_lights(self) -> Self: 1103 """Remove all the present lights in the current renderer.""" 1104 if self.renderer: 1105 self.renderer.RemoveAllLights() 1106 return self 1107 1108 def pop(self, at=None) -> Self: 1109 """ 1110 Remove the last added object from the rendering window. 1111 This method is typically used in loops or callback functions. 1112 """ 1113 if at is not None and not isinstance(at, int): 1114 # wrong usage pitfall 1115 vedo.logger.error("argument of pop() must be an integer") 1116 raise RuntimeError() 1117 1118 if self.objects: 1119 self.remove(self.objects[-1], at) 1120 return self 1121 1122 def render(self, resetcam=False) -> Self: 1123 """Render the scene. This method is typically used in loops or callback functions.""" 1124 1125 if vedo.settings.dry_run_mode >= 2: 1126 return self 1127 1128 if not self.window: 1129 return self 1130 1131 self.initialize_interactor() 1132 1133 if resetcam: 1134 self.renderer.ResetCamera() 1135 1136 self.window.Render() 1137 1138 if self._cocoa_process_events and self.interactor and self.interactor.GetInitialized(): 1139 if "Darwin" in vedo.sys_platform and not self.offscreen: 1140 self.interactor.ProcessEvents() 1141 self._cocoa_process_events = False 1142 return self 1143 1144 def interactive(self) -> Self: 1145 """ 1146 Start window interaction. 1147 Analogous to `show(..., interactive=True)`. 1148 """ 1149 if vedo.settings.dry_run_mode >= 1: 1150 return self 1151 self.initialize_interactor() 1152 if self.interactor: 1153 # print("self.interactor.Start()") 1154 self.interactor.Start() 1155 # print("self.interactor.Start() done") 1156 if self._must_close_now: 1157 # print("self.interactor.TerminateApp()") 1158 if self.interactor: 1159 self.interactor.GetRenderWindow().Finalize() 1160 self.interactor.TerminateApp() 1161 self.interactor = None 1162 self.window = None 1163 self.renderer = None 1164 self.renderers = [] 1165 self.camera = None 1166 return self 1167 1168 def use_depth_peeling(self, at=None, value=True) -> Self: 1169 """ 1170 Specify whether use depth peeling algorithm at this specific renderer 1171 Call this method before the first rendering. 1172 """ 1173 ren = self.renderer if at is None else self.renderers[at] 1174 ren.SetUseDepthPeeling(value) 1175 return self 1176 1177 def background(self, c1=None, c2=None, at=None, mode=0) -> Union[Self, "np.ndarray"]: 1178 """Set the color of the background for the current renderer. 1179 A different renderer index can be specified by keyword `at`. 1180 1181 Arguments: 1182 c1 : (list) 1183 background main color. 1184 c2 : (list) 1185 background color for the upper part of the window. 1186 at : (int) 1187 renderer index. 1188 mode : (int) 1189 background mode (needs vtk version >= 9.3) 1190 0 = vertical, 1191 1 = horizontal, 1192 2 = radial farthest side, 1193 3 = radia farthest corner. 1194 """ 1195 if not self.renderers: 1196 return self 1197 r = self.renderer if at is None else self.renderers[at] 1198 1199 if c1 is None and c2 is None: 1200 return np.array(r.GetBackground()) 1201 1202 if r: 1203 if c1 is not None: 1204 r.SetBackground(vedo.get_color(c1)) 1205 if c2 is not None: 1206 r.GradientBackgroundOn() 1207 r.SetBackground2(vedo.get_color(c2)) 1208 if mode: 1209 try: # only works with vtk>=9.3 1210 modes = [ 1211 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1212 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1213 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1214 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1215 ] 1216 r.SetGradientMode(modes[mode]) 1217 except AttributeError: 1218 pass 1219 1220 else: 1221 r.GradientBackgroundOff() 1222 return self 1223 1224 ################################################################## 1225 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1226 """ 1227 Return a list of Meshes from the specified renderer. 1228 1229 Arguments: 1230 at : (int) 1231 specify which renderer to look at. 1232 include_non_pickables : (bool) 1233 include non-pickable objects 1234 unpack_assemblies : (bool) 1235 unpack assemblies into their components 1236 """ 1237 if at is None: 1238 renderer = self.renderer 1239 at = self.renderers.index(renderer) 1240 elif isinstance(at, int): 1241 renderer = self.renderers[at] 1242 1243 has_global_axes = False 1244 if isinstance(self.axes_instances[at], vedo.Assembly): 1245 has_global_axes = True 1246 1247 if unpack_assemblies: 1248 acs = renderer.GetActors() 1249 else: 1250 acs = renderer.GetViewProps() 1251 1252 objs = [] 1253 acs.InitTraversal() 1254 for _ in range(acs.GetNumberOfItems()): 1255 1256 if unpack_assemblies: 1257 a = acs.GetNextItem() 1258 else: 1259 a = acs.GetNextProp() 1260 1261 if isinstance(a, vtki.vtkVolume): 1262 continue 1263 1264 if include_non_pickables or a.GetPickable(): 1265 if a == self.axes_instances[at]: 1266 continue 1267 if has_global_axes and a in self.axes_instances[at].actors: 1268 continue 1269 try: 1270 objs.append(a.retrieve_object()) 1271 except AttributeError: 1272 pass 1273 return objs 1274 1275 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1276 """ 1277 Return a list of Volumes from the specified renderer. 1278 1279 Arguments: 1280 at : (int) 1281 specify which renderer to look at 1282 include_non_pickables : (bool) 1283 include non-pickable objects 1284 """ 1285 renderer = self.renderer if at is None else self.renderers[at] 1286 1287 vols = [] 1288 acs = renderer.GetVolumes() 1289 acs.InitTraversal() 1290 for _ in range(acs.GetNumberOfItems()): 1291 a = acs.GetNextItem() 1292 if include_non_pickables or a.GetPickable(): 1293 try: 1294 vols.append(a.retrieve_object()) 1295 except AttributeError: 1296 pass 1297 return vols 1298 1299 def get_actors(self, at=None, include_non_pickables=False) -> list: 1300 """ 1301 Return a list of Volumes from the specified renderer. 1302 1303 Arguments: 1304 at : (int) 1305 specify which renderer to look at 1306 include_non_pickables : (bool) 1307 include non-pickable objects 1308 """ 1309 renderer = self.renderer if at is None else self.renderers[at] 1310 if renderer is None: 1311 return [] 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_clipping_range(self, bounds=None) -> Self: 1375 """ 1376 Reset the camera clipping range to include all visible actors. 1377 If bounds is given, it will be used instead of computing it. 1378 """ 1379 if bounds is None: 1380 self.renderer.ResetCameraClippingRange() 1381 else: 1382 self.renderer.ResetCameraClippingRange(bounds) 1383 return self 1384 1385 def reset_viewup(self, smooth=True) -> Self: 1386 """ 1387 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1388 """ 1389 vbb = addons.compute_visible_bounds()[0] 1390 x0, x1, y0, y1, z0, z1 = vbb 1391 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1392 d = self.camera.GetDistance() 1393 1394 viewups = np.array( 1395 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1396 ) 1397 positions = np.array( 1398 [ 1399 (mx, my, mz + d), 1400 (mx, my, mz - d), 1401 (mx, my + d, mz), 1402 (mx, my - d, mz), 1403 (mx + d, my, mz), 1404 (mx - d, my, mz), 1405 ] 1406 ) 1407 1408 vu = np.array(self.camera.GetViewUp()) 1409 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1410 1411 poc = np.array(self.camera.GetPosition()) 1412 foc = np.array(self.camera.GetFocalPoint()) 1413 a = poc - foc 1414 b = positions - foc 1415 a = a / np.linalg.norm(a) 1416 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1417 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1418 1419 if smooth: 1420 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1421 for t in outtimes: 1422 vv = vu * (1 - t) + viewups[vui] * t 1423 pp = poc * (1 - t) + positions[pui] * t 1424 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1425 self.camera.SetViewUp(vv) 1426 self.camera.SetPosition(pp) 1427 self.camera.SetFocalPoint(ff) 1428 self.render() 1429 1430 # interpolator does not respect parallel view...: 1431 # cam1 = dict( 1432 # pos=poc, 1433 # viewup=vu, 1434 # focal_point=(mx,my,mz), 1435 # clipping_range=self.camera.GetClippingRange() 1436 # ) 1437 # # cam1 = self.camera 1438 # cam2 = dict( 1439 # pos=positions[pui], 1440 # viewup=viewups[vui], 1441 # focal_point=(mx,my,mz), 1442 # clipping_range=self.camera.GetClippingRange() 1443 # ) 1444 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1445 # for c in vcams: 1446 # self.renderer.SetActiveCamera(c) 1447 # self.render() 1448 else: 1449 1450 self.camera.SetViewUp(viewups[vui]) 1451 self.camera.SetPosition(positions[pui]) 1452 self.camera.SetFocalPoint(mx, my, mz) 1453 1454 self.renderer.ResetCameraClippingRange() 1455 1456 # vbb, _, _, _ = addons.compute_visible_bounds() 1457 # x0,x1, y0,y1, z0,z1 = vbb 1458 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1459 self.render() 1460 return self 1461 1462 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1463 """ 1464 Takes as input two cameras set camera at an interpolated position: 1465 1466 Cameras can be vtkCamera or dictionaries in format: 1467 1468 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1469 1470 Press `shift-C` key in interactive mode to dump a python snipplet 1471 of parameters for the current camera view. 1472 """ 1473 nc = len(cameras) 1474 if len(times) == 0: 1475 times = np.linspace(0, 1, num=nc, endpoint=True) 1476 1477 assert len(times) == nc 1478 1479 cin = vtki.new("CameraInterpolator") 1480 1481 # cin.SetInterpolationTypeToLinear() # buggy? 1482 if nc > 2 and smooth: 1483 cin.SetInterpolationTypeToSpline() 1484 1485 for i, cam in enumerate(cameras): 1486 vcam = cam 1487 if isinstance(cam, dict): 1488 vcam = utils.camera_from_dict(cam) 1489 cin.AddCamera(times[i], vcam) 1490 1491 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1492 rng = maxt - mint 1493 1494 if len(output_times) == 0: 1495 cin.InterpolateCamera(t * rng, self.camera) 1496 return [self.camera] 1497 else: 1498 vcams = [] 1499 for tt in output_times: 1500 c = vtki.vtkCamera() 1501 cin.InterpolateCamera(tt * rng, c) 1502 vcams.append(c) 1503 return vcams 1504 1505 def fly_to(self, point) -> Self: 1506 """ 1507 Fly camera to the specified point. 1508 1509 Arguments: 1510 point : (list) 1511 point in space to place camera. 1512 1513 Example: 1514 ```python 1515 from vedo import * 1516 cone = Cone() 1517 plt = Plotter(axes=1) 1518 plt.show(cone) 1519 plt.fly_to([1,0,0]) 1520 plt.interactive().close() 1521 ``` 1522 """ 1523 if self.interactor: 1524 self.resetcam = False 1525 self.interactor.FlyTo(self.renderer, point) 1526 return self 1527 1528 def look_at(self, plane="xy") -> Self: 1529 """Move the camera so that it looks at the specified cartesian plane""" 1530 cam = self.renderer.GetActiveCamera() 1531 fp = np.array(cam.GetFocalPoint()) 1532 p = np.array(cam.GetPosition()) 1533 dist = np.linalg.norm(fp - p) 1534 plane = plane.lower() 1535 if "x" in plane and "y" in plane: 1536 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1537 cam.SetViewUp(0.0, 1.0, 0.0) 1538 elif "x" in plane and "z" in plane: 1539 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1540 cam.SetViewUp(0.0, 0.0, 1.0) 1541 elif "y" in plane and "z" in plane: 1542 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1543 cam.SetViewUp(0.0, 0.0, 1.0) 1544 else: 1545 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1546 return self 1547 1548 def record(self, filename="") -> str: 1549 """ 1550 Record camera, mouse, keystrokes and all other events. 1551 Recording can be toggled on/off by pressing key "R". 1552 1553 Arguments: 1554 filename : (str) 1555 ascii file to store events. 1556 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1557 1558 Returns: 1559 a string descriptor of events. 1560 1561 Examples: 1562 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1563 """ 1564 if vedo.settings.dry_run_mode >= 1: 1565 return "" 1566 if not self.interactor: 1567 vedo.logger.warning("Cannot record events, no interactor defined.") 1568 return "" 1569 erec = vtki.new("InteractorEventRecorder") 1570 erec.SetInteractor(self.interactor) 1571 if not filename: 1572 if not os.path.exists(vedo.settings.cache_directory): 1573 os.makedirs(vedo.settings.cache_directory) 1574 home_dir = os.path.expanduser("~") 1575 filename = os.path.join( 1576 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1577 print("Events will be recorded in", filename) 1578 erec.SetFileName(filename) 1579 erec.SetKeyPressActivationValue("R") 1580 erec.EnabledOn() 1581 erec.Record() 1582 self.interactor.Start() 1583 erec.Stop() 1584 erec.EnabledOff() 1585 with open(filename, "r", encoding="UTF-8") as fl: 1586 events = fl.read() 1587 erec = None 1588 return events 1589 1590 def play(self, recorded_events="", repeats=0) -> Self: 1591 """ 1592 Play camera, mouse, keystrokes and all other events. 1593 1594 Arguments: 1595 events : (str) 1596 file o string of events. 1597 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1598 repeats : (int) 1599 number of extra repeats of the same events. The default is 0. 1600 1601 Examples: 1602 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1603 """ 1604 if vedo.settings.dry_run_mode >= 1: 1605 return self 1606 if not self.interactor: 1607 vedo.logger.warning("Cannot play events, no interactor defined.") 1608 return self 1609 1610 erec = vtki.new("InteractorEventRecorder") 1611 erec.SetInteractor(self.interactor) 1612 1613 if not recorded_events: 1614 home_dir = os.path.expanduser("~") 1615 recorded_events = os.path.join( 1616 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1617 1618 if recorded_events.endswith(".log"): 1619 erec.ReadFromInputStringOff() 1620 erec.SetFileName(recorded_events) 1621 else: 1622 erec.ReadFromInputStringOn() 1623 erec.SetInputString(recorded_events) 1624 1625 erec.Play() 1626 for _ in range(repeats): 1627 erec.Rewind() 1628 erec.Play() 1629 erec.EnabledOff() 1630 erec = None 1631 return self 1632 1633 def parallel_projection(self, value=True, at=None) -> Self: 1634 """ 1635 Use parallel projection `at` a specified renderer. 1636 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1637 An input value equal to -1 will toggle it on/off. 1638 """ 1639 r = self.renderer if at is None else self.renderers[at] 1640 1641 if value == -1: 1642 val = r.GetActiveCamera().GetParallelProjection() 1643 value = not val 1644 r.GetActiveCamera().SetParallelProjection(value) 1645 r.Modified() 1646 return self 1647 1648 def render_hidden_lines(self, value=True) -> Self: 1649 """Remove hidden lines when in wireframe mode.""" 1650 self.renderer.SetUseHiddenLineRemoval(not value) 1651 return self 1652 1653 def fov(self, angle: float) -> Self: 1654 """ 1655 Set the field of view angle for the camera. 1656 This is the angle of the camera frustum in the horizontal direction. 1657 High values will result in a wide-angle lens (fish-eye effect), 1658 and low values will result in a telephoto lens. 1659 1660 Default value is 30 degrees. 1661 """ 1662 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1663 self.renderer.GetActiveCamera().SetViewAngle(angle) 1664 return self 1665 1666 def zoom(self, zoom: float) -> Self: 1667 """Apply a zooming factor for the current camera view""" 1668 self.renderer.GetActiveCamera().Zoom(zoom) 1669 return self 1670 1671 def azimuth(self, angle: float) -> Self: 1672 """Rotate camera around the view up vector.""" 1673 self.renderer.GetActiveCamera().Azimuth(angle) 1674 return self 1675 1676 def elevation(self, angle: float) -> Self: 1677 """Rotate the camera around the cross product of the negative 1678 of the direction of projection and the view up vector.""" 1679 self.renderer.GetActiveCamera().Elevation(angle) 1680 return self 1681 1682 def roll(self, angle: float) -> Self: 1683 """Roll the camera about the direction of projection.""" 1684 self.renderer.GetActiveCamera().Roll(angle) 1685 return self 1686 1687 def dolly(self, value: float) -> Self: 1688 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1689 self.renderer.GetActiveCamera().Dolly(value) 1690 return self 1691 1692 ################################################################## 1693 def add_slider( 1694 self, 1695 sliderfunc, 1696 xmin, 1697 xmax, 1698 value=None, 1699 pos=4, 1700 title="", 1701 font="Calco", 1702 title_size=1, 1703 c=None, 1704 alpha=1, 1705 show_value=True, 1706 delayed=False, 1707 **options, 1708 ) -> "vedo.addons.Slider2D": 1709 """ 1710 Add a `vedo.addons.Slider2D` which can call an external custom function. 1711 1712 Arguments: 1713 sliderfunc : (Callable) 1714 external function to be called by the widget 1715 xmin : (float) 1716 lower value of the slider 1717 xmax : (float) 1718 upper value 1719 value : (float) 1720 current value 1721 pos : (list, str) 1722 position corner number: horizontal [1-5] or vertical [11-15] 1723 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1724 and also by a string descriptor (eg. "bottom-left") 1725 title : (str) 1726 title text 1727 font : (str) 1728 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1729 title_size : (float) 1730 title text scale [1.0] 1731 show_value : (bool) 1732 if True current value is shown 1733 delayed : (bool) 1734 if True the callback is delayed until when the mouse button is released 1735 alpha : (float) 1736 opacity of the scalar bar texts 1737 slider_length : (float) 1738 slider length 1739 slider_width : (float) 1740 slider width 1741 end_cap_length : (float) 1742 length of the end cap 1743 end_cap_width : (float) 1744 width of the end cap 1745 tube_width : (float) 1746 width of the tube 1747 title_height : (float) 1748 width of the title 1749 tformat : (str) 1750 format of the title 1751 1752 Examples: 1753 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1754 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1755 1756  1757 """ 1758 if c is None: # automatic black or white 1759 c = (0.8, 0.8, 0.8) 1760 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1761 c = (0.2, 0.2, 0.2) 1762 else: 1763 c = vedo.get_color(c) 1764 1765 slider2d = addons.Slider2D( 1766 sliderfunc, 1767 xmin, 1768 xmax, 1769 value, 1770 pos, 1771 title, 1772 font, 1773 title_size, 1774 c, 1775 alpha, 1776 show_value, 1777 delayed, 1778 **options, 1779 ) 1780 1781 if self.renderer: 1782 slider2d.renderer = self.renderer 1783 if self.interactor: 1784 slider2d.interactor = self.interactor 1785 slider2d.on() 1786 self.sliders.append([slider2d, sliderfunc]) 1787 return slider2d 1788 1789 def add_slider3d( 1790 self, 1791 sliderfunc, 1792 pos1, 1793 pos2, 1794 xmin, 1795 xmax, 1796 value=None, 1797 s=0.03, 1798 t=1, 1799 title="", 1800 rotation=0.0, 1801 c=None, 1802 show_value=True, 1803 ) -> "vedo.addons.Slider3D": 1804 """ 1805 Add a 3D slider widget which can call an external custom function. 1806 1807 Arguments: 1808 sliderfunc : (function) 1809 external function to be called by the widget 1810 pos1 : (list) 1811 first position 3D coordinates 1812 pos2 : (list) 1813 second position coordinates 1814 xmin : (float) 1815 lower value 1816 xmax : (float) 1817 upper value 1818 value : (float) 1819 initial value 1820 s : (float) 1821 label scaling factor 1822 t : (float) 1823 tube scaling factor 1824 title : (str) 1825 title text 1826 c : (color) 1827 slider color 1828 rotation : (float) 1829 title rotation around slider axis 1830 show_value : (bool) 1831 if True current value is shown 1832 1833 Examples: 1834 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1835 1836  1837 """ 1838 if c is None: # automatic black or white 1839 c = (0.8, 0.8, 0.8) 1840 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1841 c = (0.2, 0.2, 0.2) 1842 else: 1843 c = vedo.get_color(c) 1844 1845 slider3d = addons.Slider3D( 1846 sliderfunc, 1847 pos1, 1848 pos2, 1849 xmin, 1850 xmax, 1851 value, 1852 s, 1853 t, 1854 title, 1855 rotation, 1856 c, 1857 show_value, 1858 ) 1859 slider3d.renderer = self.renderer 1860 slider3d.interactor = self.interactor 1861 slider3d.on() 1862 self.sliders.append([slider3d, sliderfunc]) 1863 return slider3d 1864 1865 def add_button( 1866 self, 1867 fnc=None, 1868 states=("On", "Off"), 1869 c=("w", "w"), 1870 bc=("green4", "red4"), 1871 pos=(0.7, 0.1), 1872 size=24, 1873 font="Courier", 1874 bold=True, 1875 italic=False, 1876 alpha=1, 1877 angle=0, 1878 ) -> Union["vedo.addons.Button", None]: 1879 """ 1880 Add a button to the renderer window. 1881 1882 Arguments: 1883 states : (list) 1884 a list of possible states, e.g. ['On', 'Off'] 1885 c : (list) 1886 a list of colors for each state 1887 bc : (list) 1888 a list of background colors for each state 1889 pos : (list) 1890 2D position from left-bottom corner 1891 size : (float) 1892 size of button font 1893 font : (str) 1894 font type. Check [available fonts here](https://vedo.embl.es/fonts). 1895 bold : (bool) 1896 bold font face (False) 1897 italic : (bool) 1898 italic font face (False) 1899 alpha : (float) 1900 opacity level 1901 angle : (float) 1902 anticlockwise rotation in degrees 1903 1904 Returns: 1905 `vedo.addons.Button` object. 1906 1907 Examples: 1908 - [buttons1.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons1.py) 1909 - [buttons2.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons2.py) 1910 1911  1912 """ 1913 if self.interactor: 1914 bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle) 1915 self.renderer.AddActor2D(bu) 1916 self.buttons.append(bu) 1917 # bu.function_id = self.add_callback("LeftButtonPress", bu.function) # not good 1918 bu.function_id = bu.add_observer("pick", bu.function, priority=10) 1919 return bu 1920 return None 1921 1922 def add_spline_tool( 1923 self, 1924 points, 1925 pc="k", 1926 ps=8, 1927 lc="r4", 1928 ac="g5", 1929 lw=2, 1930 alpha=1, 1931 closed=False, 1932 ontop=True, 1933 can_add_nodes=True, 1934 ) -> "vedo.addons.SplineTool": 1935 """ 1936 Add a spline tool to the current plotter. 1937 Nodes of the spline can be dragged in space with the mouse. 1938 Clicking on the line itself adds an extra point. 1939 Selecting a point and pressing del removes it. 1940 1941 Arguments: 1942 points : (Mesh, Points, array) 1943 the set of coordinates forming the spline nodes. 1944 pc : (str) 1945 point color. The default is 'k'. 1946 ps : (str) 1947 point size. The default is 8. 1948 lc : (str) 1949 line color. The default is 'r4'. 1950 ac : (str) 1951 active point marker color. The default is 'g5'. 1952 lw : (int) 1953 line width. The default is 2. 1954 alpha : (float) 1955 line transparency. 1956 closed : (bool) 1957 spline is meant to be closed. The default is False. 1958 1959 Returns: 1960 a `SplineTool` object. 1961 1962 Examples: 1963 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1964 1965  1966 """ 1967 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1968 sw.interactor = self.interactor 1969 sw.on() 1970 sw.Initialize(sw.points.dataset) 1971 sw.representation.SetRenderer(self.renderer) 1972 sw.representation.SetClosedLoop(closed) 1973 sw.representation.BuildRepresentation() 1974 self.widgets.append(sw) 1975 return sw 1976 1977 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1978 """Add an inset icon mesh into the same renderer. 1979 1980 Arguments: 1981 pos : (int, list) 1982 icon position in the range [1-4] indicating one of the 4 corners, 1983 or it can be a tuple (x,y) as a fraction of the renderer size. 1984 size : (float) 1985 size of the square inset. 1986 1987 Examples: 1988 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1989 """ 1990 iconw = addons.Icon(icon, pos, size) 1991 1992 iconw.SetInteractor(self.interactor) 1993 iconw.EnabledOn() 1994 iconw.InteractiveOff() 1995 self.widgets.append(iconw) 1996 return iconw 1997 1998 def add_global_axes(self, axtype=None, c=None) -> Self: 1999 """Draw axes on scene. Available axes types: 2000 2001 Arguments: 2002 axtype : (int) 2003 - 0, no axes, 2004 - 1, draw three gray grid walls 2005 - 2, show cartesian axes from (0,0,0) 2006 - 3, show positive range of cartesian axes from (0,0,0) 2007 - 4, show a triad at bottom left 2008 - 5, show a cube at bottom left 2009 - 6, mark the corners of the bounding box 2010 - 7, draw a 3D ruler at each side of the cartesian axes 2011 - 8, show the vtkCubeAxesActor object 2012 - 9, show the bounding box outLine 2013 - 10, show three circles representing the maximum bounding box 2014 - 11, show a large grid on the x-y plane 2015 - 12, show polar axes 2016 - 13, draw a simple ruler at the bottom of the window 2017 2018 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 2019 2020 Example: 2021 ```python 2022 from vedo import Box, show 2023 b = Box(pos=(0, 0, 0), size=(80, 90, 70)).alpha(0.1) 2024 show( 2025 b, 2026 axes={ 2027 "xtitle": "Some long variable [a.u.]", 2028 "number_of_divisions": 4, 2029 # ... 2030 }, 2031 ) 2032 ``` 2033 2034 Examples: 2035 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2036 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2037 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2038 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2039 2040 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2041 """ 2042 addons.add_global_axes(axtype, c) 2043 return self 2044 2045 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2046 """Add a legend to the top right. 2047 2048 Examples: 2049 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/legendbox.py), 2050 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/other/flag_labels1.py) 2051 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/other/flag_labels2.py) 2052 """ 2053 acts = self.get_meshes() 2054 lb = addons.LegendBox(acts, **kwargs) 2055 self.add(lb) 2056 return lb 2057 2058 def add_hint( 2059 self, 2060 obj, 2061 text="", 2062 c="k", 2063 bg="yellow9", 2064 font="Calco", 2065 size=18, 2066 justify=0, 2067 angle=0, 2068 delay=250, 2069 ) -> Union[vtki.vtkBalloonWidget, None]: 2070 """ 2071 Create a pop-up hint style message when hovering an object. 2072 Use `add_hint(obj, False)` to disable a hinting a specific object. 2073 Use `add_hint(None)` to disable all hints. 2074 2075 Arguments: 2076 obj : (Mesh, Points) 2077 the object to associate the pop-up to 2078 text : (str) 2079 string description of the pop-up 2080 delay : (int) 2081 milliseconds to wait before pop-up occurs 2082 """ 2083 if self.offscreen or not self.interactor: 2084 return None 2085 2086 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2087 # Linux vtk9.0 is bugged 2088 vedo.logger.warning( 2089 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2090 ) 2091 return None 2092 2093 if obj is None: 2094 self.hint_widget.EnabledOff() 2095 self.hint_widget.SetInteractor(None) 2096 self.hint_widget = None 2097 return self.hint_widget 2098 2099 if text is False and self.hint_widget: 2100 self.hint_widget.RemoveBalloon(obj) 2101 return self.hint_widget 2102 2103 if text == "": 2104 if obj.name: 2105 text = obj.name 2106 elif obj.filename: 2107 text = obj.filename 2108 else: 2109 return None 2110 2111 if not self.hint_widget: 2112 self.hint_widget = vtki.vtkBalloonWidget() 2113 2114 rep = self.hint_widget.GetRepresentation() 2115 rep.SetBalloonLayoutToImageRight() 2116 2117 trep = rep.GetTextProperty() 2118 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2119 trep.SetFontFile(utils.get_font_path(font)) 2120 trep.SetFontSize(size) 2121 trep.SetColor(vedo.get_color(c)) 2122 trep.SetBackgroundColor(vedo.get_color(bg)) 2123 trep.SetShadow(0) 2124 trep.SetJustification(justify) 2125 trep.UseTightBoundingBoxOn() 2126 2127 self.hint_widget.ManagesCursorOff() 2128 self.hint_widget.SetTimerDuration(delay) 2129 self.hint_widget.SetInteractor(self.interactor) 2130 if angle: 2131 trep.SetOrientation(angle) 2132 trep.SetBackgroundOpacity(0) 2133 # else: 2134 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2135 self.hint_widget.SetRepresentation(rep) 2136 self.widgets.append(self.hint_widget) 2137 self.hint_widget.EnabledOn() 2138 2139 bst = self.hint_widget.GetBalloonString(obj.actor) 2140 if bst: 2141 self.hint_widget.UpdateBalloonString(obj.actor, text) 2142 else: 2143 self.hint_widget.AddBalloon(obj.actor, text) 2144 2145 return self.hint_widget 2146 2147 def add_shadows(self) -> Self: 2148 """Add shadows at the current renderer.""" 2149 if self.renderer: 2150 shadows = vtki.new("ShadowMapPass") 2151 seq = vtki.new("SequencePass") 2152 passes = vtki.new("RenderPassCollection") 2153 passes.AddItem(shadows.GetShadowMapBakerPass()) 2154 passes.AddItem(shadows) 2155 seq.SetPasses(passes) 2156 camerapass = vtki.new("CameraPass") 2157 camerapass.SetDelegatePass(seq) 2158 self.renderer.SetPass(camerapass) 2159 return self 2160 2161 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> Self: 2162 """ 2163 Screen Space Ambient Occlusion. 2164 2165 For every pixel on the screen, the pixel shader samples the depth values around 2166 the current pixel and tries to compute the amount of occlusion from each of the sampled 2167 points. 2168 2169 Arguments: 2170 radius : (float) 2171 radius of influence in absolute units 2172 bias : (float) 2173 bias of the normals 2174 blur : (bool) 2175 add a blurring to the sampled positions 2176 samples : (int) 2177 number of samples to probe 2178 2179 Examples: 2180 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2181 2182  2183 """ 2184 lights = vtki.new("LightsPass") 2185 2186 opaque = vtki.new("OpaquePass") 2187 2188 ssaoCam = vtki.new("CameraPass") 2189 ssaoCam.SetDelegatePass(opaque) 2190 2191 ssao = vtki.new("SSAOPass") 2192 ssao.SetRadius(radius) 2193 ssao.SetBias(bias) 2194 ssao.SetBlur(blur) 2195 ssao.SetKernelSize(samples) 2196 ssao.SetDelegatePass(ssaoCam) 2197 2198 translucent = vtki.new("TranslucentPass") 2199 2200 volpass = vtki.new("VolumetricPass") 2201 ddp = vtki.new("DualDepthPeelingPass") 2202 ddp.SetTranslucentPass(translucent) 2203 ddp.SetVolumetricPass(volpass) 2204 2205 over = vtki.new("OverlayPass") 2206 2207 collection = vtki.new("RenderPassCollection") 2208 collection.AddItem(lights) 2209 collection.AddItem(ssao) 2210 collection.AddItem(ddp) 2211 collection.AddItem(over) 2212 2213 sequence = vtki.new("SequencePass") 2214 sequence.SetPasses(collection) 2215 2216 cam = vtki.new("CameraPass") 2217 cam.SetDelegatePass(sequence) 2218 2219 self.renderer.SetPass(cam) 2220 return self 2221 2222 def add_depth_of_field(self, autofocus=True) -> Self: 2223 """Add a depth of field effect in the scene.""" 2224 lights = vtki.new("LightsPass") 2225 2226 opaque = vtki.new("OpaquePass") 2227 2228 dofCam = vtki.new("CameraPass") 2229 dofCam.SetDelegatePass(opaque) 2230 2231 dof = vtki.new("DepthOfFieldPass") 2232 dof.SetAutomaticFocalDistance(autofocus) 2233 dof.SetDelegatePass(dofCam) 2234 2235 collection = vtki.new("RenderPassCollection") 2236 collection.AddItem(lights) 2237 collection.AddItem(dof) 2238 2239 sequence = vtki.new("SequencePass") 2240 sequence.SetPasses(collection) 2241 2242 cam = vtki.new("CameraPass") 2243 cam.SetDelegatePass(sequence) 2244 2245 self.renderer.SetPass(cam) 2246 return self 2247 2248 def _add_skybox(self, hdrfile: str) -> Self: 2249 # many hdr files are at https://polyhaven.com/all 2250 2251 reader = vtki.new("HDRReader") 2252 # Check the image can be read. 2253 if not reader.CanReadFile(hdrfile): 2254 vedo.logger.error(f"Cannot read HDR file {hdrfile}") 2255 return self 2256 reader.SetFileName(hdrfile) 2257 reader.Update() 2258 2259 texture = vtki.vtkTexture() 2260 texture.SetColorModeToDirectScalars() 2261 texture.SetInputData(reader.GetOutput()) 2262 2263 # Convert to a cube map 2264 tcm = vtki.new("EquirectangularToCubeMapTexture") 2265 tcm.SetInputTexture(texture) 2266 # Enable mipmapping to handle HDR image 2267 tcm.MipmapOn() 2268 tcm.InterpolateOn() 2269 2270 self.renderer.SetEnvironmentTexture(tcm) 2271 self.renderer.UseImageBasedLightingOn() 2272 self.skybox = vtki.new("Skybox") 2273 self.skybox.SetTexture(tcm) 2274 self.renderer.AddActor(self.skybox) 2275 return self 2276 2277 def add_renderer_frame(self, 2278 c=None, alpha=None, lw=None, 2279 padding=None, pattern="brtl") -> "vedo.addons.RendererFrame": 2280 """ 2281 Add a frame to the renderer subwindow. 2282 2283 Arguments: 2284 c : (color) 2285 color name or index 2286 alpha : (float) 2287 opacity level 2288 lw : (int) 2289 line width in pixels. 2290 padding : (float) 2291 padding space in pixels. 2292 pattern : (str) 2293 a string made of characters 'b', 'r', 't', 'l' 2294 to show the frame line at the bottom, right, top, left. 2295 """ 2296 if c is None: # automatic black or white 2297 c = (0.9, 0.9, 0.9) 2298 if self.renderer: 2299 if np.sum(self.renderer.GetBackground()) > 1.5: 2300 c = (0.1, 0.1, 0.1) 2301 renf = addons.RendererFrame(c, alpha, lw, padding, pattern) 2302 if renf: 2303 self.renderer.AddActor(renf) 2304 return renf 2305 2306 def add_hover_legend( 2307 self, 2308 at=None, 2309 c=None, 2310 pos="bottom-left", 2311 font="Calco", 2312 s=0.75, 2313 bg="auto", 2314 alpha=0.1, 2315 maxlength=24, 2316 use_info=False, 2317 ) -> int: 2318 """ 2319 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2320 2321 The created text object are stored in `plotter.hover_legends`. 2322 2323 Returns: 2324 the id of the callback function. 2325 2326 Arguments: 2327 c : (color) 2328 Text color. If None then black or white is chosen automatically 2329 pos : (str) 2330 text positioning 2331 font : (str) 2332 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2333 s : (float) 2334 text size scale 2335 bg : (color) 2336 background color of the 2D box containing the text 2337 alpha : (float) 2338 box transparency 2339 maxlength : (int) 2340 maximum number of characters per line 2341 use_info : (bool) 2342 visualize the content of the `obj.info` attribute 2343 2344 Examples: 2345 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2346 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2347 2348  2349 """ 2350 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2351 2352 if at is None: 2353 at = self.renderers.index(self.renderer) 2354 2355 def _legfunc(evt): 2356 if not evt.object or not self.renderer or at != evt.at: 2357 if hoverlegend.mapper.GetInput(): # clear and return 2358 hoverlegend.mapper.SetInput("") 2359 self.render() 2360 return 2361 2362 if use_info: 2363 if hasattr(evt.object, "info"): 2364 t = str(evt.object.info) 2365 else: 2366 return 2367 else: 2368 t, tp = "", "" 2369 if evt.isMesh: 2370 tp = "Mesh " 2371 elif evt.isPoints: 2372 tp = "Points " 2373 elif evt.isVolume: 2374 tp = "Volume " 2375 elif evt.isImage: 2376 tp = "Image " 2377 elif evt.isAssembly: 2378 tp = "Assembly " 2379 else: 2380 return 2381 2382 if evt.isAssembly: 2383 if not evt.object.name: 2384 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2385 else: 2386 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2387 else: 2388 if evt.object.name: 2389 t += f"{tp}name" 2390 if evt.isPoints: 2391 t += " " 2392 if evt.isMesh: 2393 t += " " 2394 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2395 2396 if evt.object.filename: 2397 t += f"{tp}filename: " 2398 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2399 t += "\n" 2400 if not evt.object.file_size: 2401 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2402 if evt.object.file_size: 2403 t += " : " 2404 sz, created = evt.object.file_size, evt.object.created 2405 t += f"{created[4:-5]} ({sz})" + "\n" 2406 2407 if evt.isPoints: 2408 indata = evt.object.dataset 2409 if indata.GetNumberOfPoints(): 2410 t += ( 2411 f"#points/cells: {indata.GetNumberOfPoints()}" 2412 f" / {indata.GetNumberOfCells()}" 2413 ) 2414 pdata = indata.GetPointData() 2415 cdata = indata.GetCellData() 2416 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2417 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2418 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2419 t += " *" 2420 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2421 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2422 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2423 t += " *" 2424 2425 if evt.isImage: 2426 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2427 t += f"\nImage shape: {evt.object.shape}" 2428 pcol = self.color_picker(evt.picked2d) 2429 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2430 2431 # change box color if needed in 'auto' mode 2432 if evt.isPoints and "auto" in str(bg): 2433 actcol = evt.object.properties.GetColor() 2434 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2435 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2436 2437 # adapt to changes in bg color 2438 bgcol = self.renderers[at].GetBackground() 2439 _bgcol = c 2440 if _bgcol is None: # automatic black or white 2441 _bgcol = (0.9, 0.9, 0.9) 2442 if sum(bgcol) > 1.5: 2443 _bgcol = (0.1, 0.1, 0.1) 2444 if len(set(_bgcol).intersection(bgcol)) < 3: 2445 hoverlegend.color(_bgcol) 2446 2447 if hoverlegend.mapper.GetInput() != t: 2448 hoverlegend.mapper.SetInput(t) 2449 self.interactor.Render() 2450 2451 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2452 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2453 2454 self.add(hoverlegend, at=at) 2455 self.hover_legends.append(hoverlegend) 2456 idcall = self.add_callback("MouseMove", _legfunc) 2457 return idcall 2458 2459 def add_scale_indicator( 2460 self, 2461 pos=(0.7, 0.05), 2462 s=0.02, 2463 length=2, 2464 lw=4, 2465 c="k1", 2466 alpha=1, 2467 units="", 2468 gap=0.05, 2469 ) -> Union["vedo.visual.Actor2D", None]: 2470 """ 2471 Add a Scale Indicator. Only works in parallel mode (no perspective). 2472 2473 Arguments: 2474 pos : (list) 2475 fractional (x,y) position on the screen. 2476 s : (float) 2477 size of the text. 2478 length : (float) 2479 length of the line. 2480 units : (str) 2481 string to show units. 2482 gap : (float) 2483 separation of line and text. 2484 2485 Example: 2486 ```python 2487 from vedo import settings, Cube, Plotter 2488 settings.use_parallel_projection = True # or else it does not make sense! 2489 cube = Cube().alpha(0.2) 2490 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2491 plt.add_scale_indicator(units='um', c='blue4') 2492 plt.show(cube, "Scale indicator with units").close() 2493 ``` 2494  2495 """ 2496 # Note that this cannot go in addons.py 2497 # because it needs callbacks and window size 2498 if not self.interactor: 2499 return None 2500 2501 ppoints = vtki.vtkPoints() # Generate the polyline 2502 psqr = [[0.0, gap], [length / 10, gap]] 2503 dd = psqr[1][0] - psqr[0][0] 2504 for i, pt in enumerate(psqr): 2505 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2506 lines = vtki.vtkCellArray() 2507 lines.InsertNextCell(len(psqr)) 2508 for i in range(len(psqr)): 2509 lines.InsertCellPoint(i) 2510 pd = vtki.vtkPolyData() 2511 pd.SetPoints(ppoints) 2512 pd.SetLines(lines) 2513 2514 wsx, wsy = self.window.GetSize() 2515 if not self.camera.GetParallelProjection(): 2516 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2517 return None 2518 2519 rlabel = vtki.new("VectorText") 2520 rlabel.SetText("scale") 2521 tf = vtki.new("TransformPolyDataFilter") 2522 tf.SetInputConnection(rlabel.GetOutputPort()) 2523 t = vtki.vtkTransform() 2524 t.Scale(s * wsy / wsx, s, 1) 2525 tf.SetTransform(t) 2526 2527 app = vtki.new("AppendPolyData") 2528 app.AddInputConnection(tf.GetOutputPort()) 2529 app.AddInputData(pd) 2530 2531 mapper = vtki.new("PolyDataMapper2D") 2532 mapper.SetInputConnection(app.GetOutputPort()) 2533 cs = vtki.vtkCoordinate() 2534 cs.SetCoordinateSystem(1) 2535 mapper.SetTransformCoordinate(cs) 2536 2537 fractor = vedo.visual.Actor2D() 2538 csys = fractor.GetPositionCoordinate() 2539 csys.SetCoordinateSystem(3) 2540 fractor.SetPosition(pos) 2541 fractor.SetMapper(mapper) 2542 fractor.GetProperty().SetColor(vedo.get_color(c)) 2543 fractor.GetProperty().SetOpacity(alpha) 2544 fractor.GetProperty().SetLineWidth(lw) 2545 fractor.GetProperty().SetDisplayLocationToForeground() 2546 2547 def sifunc(iren, ev): 2548 wsx, wsy = self.window.GetSize() 2549 ps = self.camera.GetParallelScale() 2550 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2551 if units: 2552 newtxt += " " + units 2553 if rlabel.GetText() != newtxt: 2554 rlabel.SetText(newtxt) 2555 2556 self.renderer.AddActor(fractor) 2557 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2558 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2559 self.interactor.AddObserver("InteractionEvent", sifunc) 2560 sifunc(0, 0) 2561 return fractor 2562 2563 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2564 """ 2565 Create an Event object with information of what was clicked. 2566 2567 If `enable_picking` is False, no picking will be performed. 2568 This can be useful to avoid double picking when using buttons. 2569 """ 2570 if not self.interactor: 2571 return Event() 2572 2573 if len(pos) > 0: 2574 x, y = pos 2575 self.interactor.SetEventPosition(pos) 2576 else: 2577 x, y = self.interactor.GetEventPosition() 2578 self.renderer = self.interactor.FindPokedRenderer(x, y) 2579 2580 self.picked2d = (x, y) 2581 2582 key = self.interactor.GetKeySym() 2583 2584 if key: 2585 if "_L" in key or "_R" in key: 2586 # skip things like Shift_R 2587 key = "" # better than None 2588 else: 2589 if self.interactor.GetShiftKey(): 2590 key = key.upper() 2591 2592 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2593 key = "underscore" 2594 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2595 key = "plus" 2596 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2597 key = "?" 2598 2599 if self.interactor.GetControlKey(): 2600 key = "Ctrl+" + key 2601 2602 if self.interactor.GetAltKey(): 2603 key = "Alt+" + key 2604 2605 if enable_picking: 2606 if not self.picker: 2607 self.picker = vtki.vtkPropPicker() 2608 2609 self.picker.PickProp(x, y, self.renderer) 2610 actor = self.picker.GetProp3D() 2611 # Note that GetProp3D already picks Assembly 2612 2613 xp, yp = self.interactor.GetLastEventPosition() 2614 dx, dy = x - xp, y - yp 2615 2616 delta3d = np.array([0, 0, 0]) 2617 2618 if actor: 2619 picked3d = np.array(self.picker.GetPickPosition()) 2620 2621 try: 2622 vobj = actor.retrieve_object() 2623 old_pt = np.asarray(vobj.picked3d) 2624 vobj.picked3d = picked3d 2625 delta3d = picked3d - old_pt 2626 except (AttributeError, TypeError): 2627 pass 2628 2629 else: 2630 picked3d = None 2631 2632 if not actor: # try 2D 2633 actor = self.picker.GetActor2D() 2634 2635 event = Event() 2636 event.name = ename 2637 event.title = self.title 2638 event.id = -1 # will be set by the timer wrapper function 2639 event.timerid = -1 # will be set by the timer wrapper function 2640 event.priority = -1 # will be set by the timer wrapper function 2641 event.time = time.time() 2642 event.at = self.renderers.index(self.renderer) 2643 event.keypress = key 2644 if enable_picking: 2645 try: 2646 event.object = actor.retrieve_object() 2647 except AttributeError: 2648 event.object = actor 2649 try: 2650 event.actor = actor.retrieve_object() # obsolete use object instead 2651 except AttributeError: 2652 event.actor = actor 2653 event.picked3d = picked3d 2654 event.picked2d = (x, y) 2655 event.delta2d = (dx, dy) 2656 event.angle2d = np.arctan2(dy, dx) 2657 event.speed2d = np.sqrt(dx * dx + dy * dy) 2658 event.delta3d = delta3d 2659 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2660 event.isPoints = isinstance(event.object, vedo.Points) 2661 event.isMesh = isinstance(event.object, vedo.Mesh) 2662 event.isAssembly = isinstance(event.object, vedo.Assembly) 2663 event.isVolume = isinstance(event.object, vedo.Volume) 2664 event.isImage = isinstance(event.object, vedo.Image) 2665 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2666 return event 2667 2668 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2669 """ 2670 Add a function to be executed while show() is active. 2671 2672 Return a unique id for the callback. 2673 2674 The callback function (see example below) exposes a dictionary 2675 with the following information: 2676 - `name`: event name, 2677 - `id`: event unique identifier, 2678 - `priority`: event priority (float), 2679 - `interactor`: the interactor object, 2680 - `at`: renderer nr. where the event occurred 2681 - `keypress`: key pressed as string 2682 - `actor`: object picked by the mouse 2683 - `picked3d`: point picked in world coordinates 2684 - `picked2d`: screen coords of the mouse pointer 2685 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2686 - `delta3d`: ...same but in 3D world coords 2687 - `angle2d`: angle of mouse movement on screen 2688 - `speed2d`: speed of mouse movement on screen 2689 - `speed3d`: speed of picked point in world coordinates 2690 - `isPoints`: True if of class 2691 - `isMesh`: True if of class 2692 - `isAssembly`: True if of class 2693 - `isVolume`: True if of class Volume 2694 - `isImage`: True if of class 2695 2696 If `enable_picking` is False, no picking will be performed. 2697 This can be useful to avoid double picking when using buttons. 2698 2699 Frequently used events are: 2700 - `KeyPress`, `KeyRelease`: listen to keyboard events 2701 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2702 - `MiddleButtonPress`, `MiddleButtonRelease` 2703 - `RightButtonPress`, `RightButtonRelease` 2704 - `MouseMove`: listen to mouse pointer changing position 2705 - `MouseWheelForward`, `MouseWheelBackward` 2706 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2707 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2708 - `ResetCamera`, `ResetCameraClippingRange` 2709 - `Error`, `Warning` 2710 - `Char` 2711 - `Timer` 2712 2713 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2714 2715 Example: 2716 ```python 2717 from vedo import * 2718 2719 def func(evt): 2720 # this function is called every time the mouse moves 2721 # (evt is a dotted dictionary) 2722 if not evt.object: 2723 return # no hit, return 2724 print("point coords =", evt.picked3d) 2725 # print(evt) # full event dump 2726 2727 elli = Ellipsoid() 2728 plt = Plotter(axes=1) 2729 plt.add_callback('mouse hovering', func) 2730 plt.show(elli).close() 2731 ``` 2732 2733 Examples: 2734 - [spline_draw1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw1.py) 2735 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2736 2737  2738 2739 - ..and many others! 2740 """ 2741 from vtkmodules.util.misc import calldata_type # noqa 2742 2743 if not self.interactor: 2744 return 0 2745 2746 if vedo.settings.dry_run_mode >= 1: 2747 return 0 2748 2749 ######################################### 2750 @calldata_type(vtki.VTK_INT) 2751 def _func_wrap(iren, ename, timerid=None): 2752 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2753 event.timerid = timerid 2754 event.id = cid 2755 event.priority = priority 2756 self.last_event = event 2757 func(event) 2758 2759 ######################################### 2760 2761 event_name = utils.get_vtk_name_event(event_name) 2762 2763 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2764 # print(f"Registering event: {event_name} with id={cid}") 2765 return cid 2766 2767 def remove_callback(self, cid: Union[int, str]) -> Self: 2768 """ 2769 Remove a callback function by its id 2770 or a whole category of callbacks by their name. 2771 2772 Arguments: 2773 cid : (int, str) 2774 Unique id of the callback. 2775 If an event name is passed all callbacks of that type are removed. 2776 """ 2777 if self.interactor: 2778 if isinstance(cid, str): 2779 cid = utils.get_vtk_name_event(cid) 2780 self.interactor.RemoveObservers(cid) 2781 else: 2782 self.interactor.RemoveObserver(cid) 2783 return self 2784 2785 def remove_all_observers(self) -> Self: 2786 """ 2787 Remove all observers. 2788 2789 Example: 2790 ```python 2791 from vedo import * 2792 2793 def kfunc(event): 2794 print("Key pressed:", event.keypress) 2795 if event.keypress == 'q': 2796 plt.close() 2797 2798 def rfunc(event): 2799 if event.isImage: 2800 printc("Right-clicked!", event) 2801 plt.render() 2802 2803 img = Image(dataurl+"images/embryo.jpg") 2804 2805 plt = Plotter(size=(1050, 600)) 2806 plt.parallel_projection(True) 2807 plt.remove_all_observers() 2808 plt.add_callback("key press", kfunc) 2809 plt.add_callback("mouse right click", rfunc) 2810 plt.show("Right-Click Me! Press q to exit.", img) 2811 plt.close() 2812 ``` 2813 """ 2814 if self.interactor: 2815 self.interactor.RemoveAllObservers() 2816 return self 2817 2818 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2819 """ 2820 Start or stop an existing timer. 2821 2822 Arguments: 2823 action : (str) 2824 Either "create"/"start" or "destroy"/"stop" 2825 timer_id : (int) 2826 When stopping the timer, the ID of the timer as returned when created 2827 dt : (int) 2828 time in milliseconds between each repeated call 2829 one_shot : (bool) 2830 create a one shot timer of prescribed duration instead of a repeating one 2831 2832 Examples: 2833 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2834 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2835 2836  2837 """ 2838 if action in ("create", "start"): 2839 2840 if "Windows" in vedo.sys_platform: 2841 # otherwise on windows it gets stuck 2842 self.initialize_interactor() 2843 2844 if timer_id is not None: 2845 vedo.logger.warning("you set a timer_id but it will be ignored.") 2846 if one_shot: 2847 timer_id = self.interactor.CreateOneShotTimer(dt) 2848 else: 2849 timer_id = self.interactor.CreateRepeatingTimer(dt) 2850 return timer_id 2851 2852 elif action in ("destroy", "stop"): 2853 if timer_id is not None: 2854 self.interactor.DestroyTimer(timer_id) 2855 else: 2856 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2857 else: 2858 e = f"in timer_callback(). Cannot understand action: {action}\n" 2859 e += " allowed actions are: ['start', 'stop']. Skipped." 2860 vedo.logger.error(e) 2861 return timer_id 2862 2863 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2864 """ 2865 Add a callback function that will be called when an event occurs. 2866 Consider using `add_callback()` instead. 2867 """ 2868 if not self.interactor: 2869 return -1 2870 event_name = utils.get_vtk_name_event(event_name) 2871 idd = self.interactor.AddObserver(event_name, func, priority) 2872 return idd 2873 2874 def compute_world_coordinate( 2875 self, 2876 pos2d: MutableSequence[float], 2877 at=None, 2878 objs=(), 2879 bounds=(), 2880 offset=None, 2881 pixeltol=None, 2882 worldtol=None, 2883 ) -> np.ndarray: 2884 """ 2885 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2886 If a set of meshes is passed then points are placed onto these. 2887 2888 Arguments: 2889 pos2d : (list) 2890 2D screen coordinates point. 2891 at : (int) 2892 renderer number. 2893 objs : (list) 2894 list of Mesh objects to project the point onto. 2895 bounds : (list) 2896 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2897 offset : (float) 2898 specify an offset value. 2899 pixeltol : (int) 2900 screen tolerance in pixels. 2901 worldtol : (float) 2902 world coordinates tolerance. 2903 2904 Returns: 2905 numpy array, the point in 3D world coordinates. 2906 2907 Examples: 2908 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2909 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2910 2911  2912 """ 2913 renderer = self.renderer if at is None else self.renderers[at] 2914 2915 if not objs: 2916 pp = vtki.vtkFocalPlanePointPlacer() 2917 else: 2918 pps = vtki.vtkPolygonalSurfacePointPlacer() 2919 for ob in objs: 2920 pps.AddProp(ob.actor) 2921 pp = pps # type: ignore 2922 2923 if len(bounds) == 6: 2924 pp.SetPointBounds(bounds) 2925 if pixeltol: 2926 pp.SetPixelTolerance(pixeltol) 2927 if worldtol: 2928 pp.SetWorldTolerance(worldtol) 2929 if offset: 2930 pp.SetOffset(offset) 2931 2932 worldPos: MutableSequence[float] = [0, 0, 0] 2933 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2934 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2935 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2936 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2937 return np.array(worldPos) 2938 2939 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2940 """ 2941 Given a 3D points in the current renderer (or full window), 2942 find the screen pixel coordinates. 2943 2944 Example: 2945 ```python 2946 from vedo import * 2947 2948 elli = Ellipsoid().point_size(5) 2949 2950 plt = Plotter() 2951 plt.show(elli, "Press q to continue and print the info") 2952 2953 xyscreen = plt.compute_screen_coordinates(elli) 2954 print('xyscreen coords:', xyscreen) 2955 2956 # simulate an event happening at one point 2957 event = plt.fill_event(pos=xyscreen[123]) 2958 print(event) 2959 ``` 2960 """ 2961 try: 2962 obj = obj.coordinates 2963 except AttributeError: 2964 pass 2965 2966 if utils.is_sequence(obj): 2967 pts = obj 2968 p2d = [] 2969 cs = vtki.vtkCoordinate() 2970 cs.SetCoordinateSystemToWorld() 2971 cs.SetViewport(self.renderer) 2972 for p in pts: 2973 cs.SetValue(p) 2974 if full_window: 2975 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2976 else: 2977 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2978 return np.array(p2d, dtype=int) 2979 2980 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2981 """ 2982 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2983 2984 Returns a frustum Mesh that contains the visible field of view. 2985 This can be used to select objects in a scene or select vertices. 2986 2987 Example: 2988 ```python 2989 from vedo import * 2990 2991 settings.enable_default_mouse_callbacks = False 2992 2993 def mode_select(objs): 2994 print("Selected objects:", objs) 2995 d0 = mode.start_x, mode.start_y # display coords 2996 d1 = mode.end_x, mode.end_y 2997 2998 frustum = plt.pick_area(d0, d1) 2999 col = np.random.randint(0, 10) 3000 infru = frustum.inside_points(mesh) 3001 infru.point_size(10).color(col) 3002 plt.add(frustum, infru).render() 3003 3004 mesh = Mesh(dataurl+"cow.vtk") 3005 mesh.color("k5").linewidth(1) 3006 3007 mode = interactor_modes.BlenderStyle() 3008 mode.callback_select = mode_select 3009 3010 plt = Plotter().user_mode(mode) 3011 plt.show(mesh, axes=1) 3012 ``` 3013 """ 3014 ren = self.renderer if at is None else self.renderers[at] 3015 area_picker = vtki.vtkAreaPicker() 3016 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 3017 planes = area_picker.GetFrustum() 3018 3019 fru = vtki.new("FrustumSource") 3020 fru.SetPlanes(planes) 3021 fru.ShowLinesOff() 3022 fru.Update() 3023 3024 afru = vedo.Mesh(fru.GetOutput()) 3025 afru.alpha(0.1).lw(1).pickable(False) 3026 afru.name = "Frustum" 3027 return afru 3028 3029 def _scan_input_return_acts(self, objs) -> Any: 3030 # scan the input and return a list of actors 3031 if not utils.is_sequence(objs): 3032 objs = [objs] 3033 3034 ################# 3035 wannabe_acts = [] 3036 for a in objs: 3037 3038 try: 3039 wannabe_acts.append(a.actor) 3040 except AttributeError: 3041 wannabe_acts.append(a) # already actor 3042 3043 try: 3044 wannabe_acts.append(a.scalarbar) 3045 except AttributeError: 3046 pass 3047 3048 try: 3049 for sh in a.shadows: 3050 wannabe_acts.append(sh.actor) 3051 except AttributeError: 3052 pass 3053 3054 try: 3055 wannabe_acts.append(a.trail.actor) 3056 if a.trail.shadows: # trails may also have shadows 3057 for sh in a.trail.shadows: 3058 wannabe_acts.append(sh.actor) 3059 except AttributeError: 3060 pass 3061 3062 ################# 3063 scanned_acts = [] 3064 for a in wannabe_acts: # scan content of list 3065 3066 if a is None: 3067 pass 3068 3069 elif isinstance(a, (vtki.vtkActor, vtki.vtkActor2D)): 3070 scanned_acts.append(a) 3071 3072 elif isinstance(a, str): 3073 # assume a 2D comment was given 3074 changed = False # check if one already exists so to just update text 3075 if self.renderer: # might be jupyter 3076 acs = self.renderer.GetActors2D() 3077 acs.InitTraversal() 3078 for i in range(acs.GetNumberOfItems()): 3079 act = acs.GetNextItem() 3080 if isinstance(act, vedo.shapes.Text2D): 3081 aposx, aposy = act.GetPosition() 3082 if aposx < 0.01 and aposy > 0.99: # "top-left" 3083 act.text(a) # update content! no appending nada 3084 changed = True 3085 break 3086 if not changed: 3087 out = vedo.shapes.Text2D(a) # append a new one 3088 scanned_acts.append(out) 3089 # scanned_acts.append(vedo.shapes.Text2D(a)) # naive version 3090 3091 elif isinstance(a, vtki.vtkPolyData): 3092 scanned_acts.append(vedo.Mesh(a).actor) 3093 3094 elif isinstance(a, vtki.vtkImageData): 3095 scanned_acts.append(vedo.Volume(a).actor) 3096 3097 elif isinstance(a, vedo.RectilinearGrid): 3098 scanned_acts.append(a.actor) 3099 3100 elif isinstance(a, vedo.StructuredGrid): 3101 scanned_acts.append(a.actor) 3102 3103 elif isinstance(a, vtki.vtkLight): 3104 scanned_acts.append(a) 3105 3106 elif isinstance(a, vedo.visual.LightKit): 3107 a.lightkit.AddLightsToRenderer(self.renderer) 3108 3109 elif isinstance(a, vtki.get_class("MultiBlockDataSet")): 3110 for i in range(a.GetNumberOfBlocks()): 3111 b = a.GetBlock(i) 3112 if isinstance(b, vtki.vtkPolyData): 3113 scanned_acts.append(vedo.Mesh(b).actor) 3114 elif isinstance(b, vtki.vtkImageData): 3115 scanned_acts.append(vedo.Volume(b).actor) 3116 3117 elif isinstance(a, (vtki.vtkProp, vtki.vtkInteractorObserver)): 3118 scanned_acts.append(a) 3119 3120 elif "trimesh" in str(type(a)): 3121 scanned_acts.append(utils.trimesh2vedo(a)) 3122 3123 elif "meshlab" in str(type(a)): 3124 if "MeshSet" in str(type(a)): 3125 for i in range(a.number_meshes()): 3126 if a.mesh_id_exists(i): 3127 scanned_acts.append(utils.meshlab2vedo(a.mesh(i))) 3128 else: 3129 scanned_acts.append(utils.meshlab2vedo(a)) 3130 3131 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 3132 import vedo.dolfin as vdlf # type: ignore[import] 3133 3134 scanned_acts.append(vdlf.IMesh(a).actor) 3135 3136 elif "madcad" in str(type(a)): 3137 scanned_acts.append(utils.madcad2vedo(a).actor) 3138 3139 elif "TetgenIO" in str(type(a)): 3140 scanned_acts.append(vedo.TetMesh(a).shrink(0.9).c("pink7").actor) 3141 3142 elif "matplotlib.figure.Figure" in str(type(a)): 3143 scanned_acts.append(vedo.Image(a).clone2d("top-right", 0.6)) 3144 3145 else: 3146 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 3147 3148 return scanned_acts 3149 3150 def show( 3151 self, 3152 *objects, 3153 at=None, 3154 axes=None, 3155 resetcam=None, 3156 zoom=False, 3157 interactive=None, 3158 viewup="", 3159 azimuth=0.0, 3160 elevation=0.0, 3161 roll=0.0, 3162 camera=None, 3163 mode=None, 3164 rate=None, 3165 bg=None, 3166 bg2=None, 3167 size=None, 3168 title=None, 3169 screenshot="", 3170 ) -> Any: 3171 """ 3172 Render a list of objects. 3173 3174 Arguments: 3175 at : (int) 3176 number of the renderer to plot to, in case of more than one exists 3177 3178 axes : (int) 3179 axis type-1 can be fully customized by passing a dictionary. 3180 Check `addons.Axes()` for the full list of options. 3181 set the type of axes to be shown: 3182 - 0, no axes 3183 - 1, draw three gray grid walls 3184 - 2, show cartesian axes from (0,0,0) 3185 - 3, show positive range of cartesian axes from (0,0,0) 3186 - 4, show a triad at bottom left 3187 - 5, show a cube at bottom left 3188 - 6, mark the corners of the bounding box 3189 - 7, draw a 3D ruler at each side of the cartesian axes 3190 - 8, show the `vtkCubeAxesActor` object 3191 - 9, show the bounding box outLine 3192 - 10, show three circles representing the maximum bounding box 3193 - 11, show a large grid on the x-y plane 3194 - 12, show polar axes 3195 - 13, draw a simple ruler at the bottom of the window 3196 3197 azimuth/elevation/roll : (float) 3198 move camera accordingly the specified value 3199 3200 viewup: str, list 3201 either `['x', 'y', 'z']` or a vector to set vertical direction 3202 3203 resetcam : (bool) 3204 re-adjust camera position to fit objects 3205 3206 camera : (dict, vtkCamera) 3207 camera parameters can further be specified with a dictionary 3208 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3209 - pos, `(list)`, the position of the camera in world coordinates 3210 - focal_point `(list)`, the focal point of the camera in world coordinates 3211 - viewup `(list)`, the view up direction for the camera 3212 - distance `(float)`, set the focal point to the specified distance from the camera position. 3213 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3214 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3215 in world-coordinate distances. The default is 1. 3216 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3217 This method has no effect in perspective projection mode. 3218 3219 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3220 plane to be set a distance 'thickness' beyond the near clipping plane. 3221 3222 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3223 measured in degrees. The default angle is 30 degrees. 3224 This method has no effect in parallel projection mode. 3225 The formula for setting the angle up for perfect perspective viewing is: 3226 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3227 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3228 3229 interactive : (bool) 3230 pause and interact with window (True) or continue execution (False) 3231 3232 rate : (float) 3233 maximum rate of `show()` in Hertz 3234 3235 mode : (int, str) 3236 set the type of interaction: 3237 - 0 = TrackballCamera [default] 3238 - 1 = TrackballActor 3239 - 2 = JoystickCamera 3240 - 3 = JoystickActor 3241 - 4 = Flight 3242 - 5 = RubberBand2D 3243 - 6 = RubberBand3D 3244 - 7 = RubberBandZoom 3245 - 8 = Terrain 3246 - 9 = Unicam 3247 - 10 = Image 3248 - Check out `vedo.interaction_modes` for more options. 3249 3250 bg : (str, list) 3251 background color in RGB format, or string name 3252 3253 bg2 : (str, list) 3254 second background color to create a gradient background 3255 3256 size : (str, list) 3257 size of the window, e.g. size="fullscreen", or size=[600,400] 3258 3259 title : (str) 3260 window title text 3261 3262 screenshot : (str) 3263 save a screenshot of the window to file 3264 """ 3265 3266 if vedo.settings.dry_run_mode >= 2: 3267 return self 3268 3269 if self.wx_widget: 3270 return self 3271 3272 if self.renderers: # in case of notebooks 3273 3274 if at is None: 3275 at = self.renderers.index(self.renderer) 3276 3277 else: 3278 3279 if at >= len(self.renderers): 3280 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3281 vedo.logger.error(t) 3282 return self 3283 3284 self.renderer = self.renderers[at] 3285 3286 if title is not None: 3287 self.title = title 3288 3289 if size is not None: 3290 self.size = size 3291 if self.size[0] == "f": # full screen 3292 self.size = "fullscreen" 3293 self.window.SetFullScreen(True) 3294 self.window.BordersOn() 3295 else: 3296 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3297 3298 if vedo.settings.default_backend == "vtk": 3299 if str(bg).endswith(".hdr"): 3300 self._add_skybox(bg) 3301 else: 3302 if bg is not None: 3303 self.backgrcol = vedo.get_color(bg) 3304 self.renderer.SetBackground(self.backgrcol) 3305 if bg2 is not None: 3306 self.renderer.GradientBackgroundOn() 3307 self.renderer.SetBackground2(vedo.get_color(bg2)) 3308 3309 if axes is not None: 3310 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3311 objects = list(objects) 3312 objects.append(axes) # move it into the list of normal things to show 3313 axes = 0 3314 self.axes = axes 3315 3316 if interactive is not None: 3317 self._interactive = interactive 3318 if self.offscreen: 3319 self._interactive = False 3320 3321 # camera stuff 3322 if resetcam is not None: 3323 self.resetcam = resetcam 3324 3325 if camera is not None: 3326 self.resetcam = False 3327 viewup = "" 3328 if isinstance(camera, vtki.vtkCamera): 3329 cameracopy = vtki.vtkCamera() 3330 cameracopy.DeepCopy(camera) 3331 self.camera = cameracopy 3332 else: 3333 self.camera = utils.camera_from_dict(camera) 3334 3335 self.add(objects) 3336 3337 # Backend ############################################################### 3338 if vedo.settings.default_backend in ["k3d", "panel"]: 3339 return backends.get_notebook_backend(self.objects) 3340 ######################################################################### 3341 3342 for ia in utils.flatten(objects): 3343 try: 3344 # fix gray color labels and title to white or black 3345 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3346 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3347 c = (0.9, 0.9, 0.9) 3348 if np.sum(self.renderer.GetBackground()) > 1.5: 3349 c = (0.1, 0.1, 0.1) 3350 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3351 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3352 except AttributeError: 3353 pass 3354 3355 if self.sharecam: 3356 for r in self.renderers: 3357 r.SetActiveCamera(self.camera) 3358 3359 if self.axes is not None: 3360 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3361 bns = self.renderer.ComputeVisiblePropBounds() 3362 addons.add_global_axes(self.axes, bounds=bns) 3363 3364 # Backend ############################################################### 3365 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3366 return backends.get_notebook_backend() 3367 ######################################################################### 3368 3369 if self.resetcam and self.renderer: 3370 self.renderer.ResetCamera() 3371 3372 if len(self.renderers) > 1: 3373 self.add_renderer_frame() 3374 3375 if vedo.settings.default_backend == "2d" and not zoom: 3376 zoom = "tightest" 3377 3378 if zoom: 3379 if zoom == "tight": 3380 self.reset_camera(tight=0.04) 3381 elif zoom == "tightest": 3382 self.reset_camera(tight=0.0001) 3383 else: 3384 self.camera.Zoom(zoom) 3385 if elevation: 3386 self.camera.Elevation(elevation) 3387 if azimuth: 3388 self.camera.Azimuth(azimuth) 3389 if roll: 3390 self.camera.Roll(roll) 3391 3392 if len(viewup) > 0: 3393 b = self.renderer.ComputeVisiblePropBounds() 3394 cm = np.array([(b[1] + b[0]) / 2, (b[3] + b[2]) / 2, (b[5] + b[4]) / 2]) 3395 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3396 if viewup == "x": 3397 sz = np.linalg.norm(sz) 3398 self.camera.SetViewUp([1, 0, 0]) 3399 self.camera.SetPosition(cm + sz) 3400 elif viewup == "y": 3401 sz = np.linalg.norm(sz) 3402 self.camera.SetViewUp([0, 1, 0]) 3403 self.camera.SetPosition(cm + sz) 3404 elif viewup == "z": 3405 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3406 self.camera.SetViewUp([0, 0, 1]) 3407 self.camera.SetPosition(cm + 2 * sz) 3408 elif utils.is_sequence(viewup): 3409 sz = np.linalg.norm(sz) 3410 self.camera.SetViewUp(viewup) 3411 cpos = np.cross([0, 1, 0], viewup) 3412 self.camera.SetPosition(cm - 2 * sz * cpos) 3413 3414 self.renderer.ResetCameraClippingRange() 3415 3416 self.initialize_interactor() 3417 3418 if vedo.settings.immediate_rendering: 3419 self.window.Render() ##################### <-------------- Render 3420 3421 if self.interactor: # can be offscreen or not the vtk backend.. 3422 3423 self.window.SetWindowName(self.title) 3424 3425 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3426 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3427 # print(pic.dataset)# Array 0 name PNGImage 3428 # self.window.SetIcon(pic.dataset) 3429 3430 try: 3431 # Needs "pip install pyobjc" on Mac OSX 3432 if ( 3433 self._cocoa_initialized is False 3434 and "Darwin" in vedo.sys_platform 3435 and not self.offscreen 3436 ): 3437 self._cocoa_initialized = True 3438 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3439 pid = os.getpid() 3440 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3441 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3442 except: 3443 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3444 pass 3445 3446 # Set the interaction style 3447 if mode is not None: 3448 self.user_mode(mode) 3449 if self.qt_widget and mode is None: 3450 self.user_mode(0) 3451 3452 if screenshot: 3453 self.screenshot(screenshot) 3454 3455 if self._interactive and self.interactor: 3456 self.interactor.Start() 3457 if self._must_close_now and self.interactor: 3458 self.interactor.GetRenderWindow().Finalize() 3459 self.interactor.TerminateApp() 3460 self.camera = None 3461 self.renderer = None 3462 self.renderers = [] 3463 self.window = None 3464 self.interactor = None 3465 return self 3466 3467 if rate: 3468 if self.clock is None: # set clock and limit rate 3469 self._clockt0 = time.time() 3470 self.clock = 0.0 3471 else: 3472 t = time.time() - self._clockt0 3473 elapsed = t - self.clock 3474 mint = 1.0 / rate 3475 if elapsed < mint: 3476 time.sleep(mint - elapsed) 3477 self.clock = time.time() - self._clockt0 3478 3479 # 2d #################################################################### 3480 if vedo.settings.default_backend in ["2d"]: 3481 return backends.get_notebook_backend() 3482 ######################################################################### 3483 3484 return self 3485 3486 def add_inset( 3487 self, *objects, **options 3488 ) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3489 """Add a draggable inset space into a renderer. 3490 3491 Arguments: 3492 at : (int) 3493 specify the renderer number 3494 pos : (list) 3495 icon position in the range [1-4] indicating one of the 4 corners, 3496 or it can be a tuple (x,y) as a fraction of the renderer size. 3497 size : (float) 3498 size of the square inset 3499 draggable : (bool) 3500 if True the subrenderer space can be dragged around 3501 c : (color) 3502 color of the inset frame when dragged 3503 3504 Examples: 3505 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3506 3507  3508 """ 3509 if not self.interactor: 3510 return None 3511 3512 if not self.renderer: 3513 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3514 return None 3515 3516 options = dict(options) 3517 pos = options.pop("pos", 0) 3518 size = options.pop("size", 0.1) 3519 c = options.pop("c", "lb") 3520 at = options.pop("at", None) 3521 draggable = options.pop("draggable", True) 3522 3523 r, g, b = vedo.get_color(c) 3524 widget = vtki.vtkOrientationMarkerWidget() 3525 widget.SetOutlineColor(r, g, b) 3526 if len(objects) == 1: 3527 widget.SetOrientationMarker(objects[0].actor) 3528 else: 3529 widget.SetOrientationMarker(vedo.Assembly(objects)) 3530 3531 widget.SetInteractor(self.interactor) 3532 3533 if utils.is_sequence(pos): 3534 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3535 else: 3536 if pos < 2: 3537 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3538 elif pos == 2: 3539 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3540 elif pos == 3: 3541 widget.SetViewport(0, 0, size * 2, size * 2) 3542 elif pos == 4: 3543 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3544 widget.EnabledOn() 3545 widget.SetInteractive(draggable) 3546 if at is not None and at < len(self.renderers): 3547 widget.SetCurrentRenderer(self.renderers[at]) 3548 else: 3549 widget.SetCurrentRenderer(self.renderer) 3550 self.widgets.append(widget) 3551 return widget 3552 3553 def clear(self, at=None, deep=False) -> Self: 3554 """Clear the scene from all meshes and volumes.""" 3555 renderer = self.renderer if at is None else self.renderers[at] 3556 if not renderer: 3557 return self 3558 3559 if deep: 3560 renderer.RemoveAllViewProps() 3561 else: 3562 for ob in set( 3563 self.get_meshes() 3564 + self.get_volumes() 3565 + self.objects 3566 + self.axes_instances 3567 ): 3568 if isinstance(ob, vedo.shapes.Text2D): 3569 continue 3570 self.remove(ob) 3571 try: 3572 if ob.scalarbar: 3573 self.remove(ob.scalarbar) 3574 except AttributeError: 3575 pass 3576 return self 3577 3578 def break_interaction(self) -> Self: 3579 """Break window interaction and return to the python execution flow""" 3580 if self.interactor: 3581 self.check_actors_trasform() 3582 self.interactor.ExitCallback() 3583 return self 3584 3585 def freeze(self, value=True) -> Self: 3586 """Freeze the current renderer. Use this with `sharecam=False`.""" 3587 if not self.interactor: 3588 return self 3589 if not self.renderer: 3590 return self 3591 self.renderer.SetInteractive(not value) 3592 return self 3593 3594 def user_mode(self, mode) -> Self: 3595 """ 3596 Modify the user interaction mode. 3597 3598 Examples: 3599 ```python 3600 from vedo import * 3601 mode = interactor_modes.MousePan() 3602 mesh = Mesh(dataurl+"cow.vtk") 3603 plt = Plotter().user_mode(mode) 3604 plt.show(mesh, axes=1) 3605 ``` 3606 See also: 3607 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3608 """ 3609 if not self.interactor: 3610 return self 3611 3612 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3613 # print("Current style:", curr_style) 3614 if curr_style.endswith("Actor"): 3615 self.check_actors_trasform() 3616 3617 if isinstance(mode, (str, int)): 3618 # Set the style of interaction 3619 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3620 if mode in (0, "TrackballCamera"): 3621 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3622 self.interactor.RemoveObservers("CharEvent") 3623 elif mode in (1, "TrackballActor"): 3624 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3625 elif mode in (2, "JoystickCamera"): 3626 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3627 elif mode in (3, "JoystickActor"): 3628 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3629 elif mode in (4, "Flight"): 3630 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3631 elif mode in (5, "RubberBand2D"): 3632 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3633 elif mode in (6, "RubberBand3D"): 3634 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3635 elif mode in (7, "RubberBandZoom"): 3636 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3637 elif mode in (8, "Terrain"): 3638 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3639 elif mode in (9, "Unicam"): 3640 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3641 elif mode in (10, "Image", "image", "2d"): 3642 astyle = vtki.new("InteractorStyleImage") 3643 astyle.SetInteractionModeToImage3D() 3644 self.interactor.SetInteractorStyle(astyle) 3645 else: 3646 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3647 3648 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3649 # set a custom interactor style 3650 if hasattr(mode, "interactor"): 3651 mode.interactor = self.interactor 3652 mode.renderer = self.renderer # type: ignore 3653 mode.SetInteractor(self.interactor) 3654 mode.SetDefaultRenderer(self.renderer) 3655 self.interactor.SetInteractorStyle(mode) 3656 3657 return self 3658 3659 def close(self) -> Self: 3660 """Close the plotter.""" 3661 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3662 vedo.last_figure = None 3663 self.last_event = None 3664 self.sliders = [] 3665 self.buttons = [] 3666 self.widgets = [] 3667 self.hover_legends = [] 3668 self.background_renderer = None 3669 self._extralight = None 3670 3671 self.hint_widget = None 3672 self.cutter_widget = None 3673 3674 if vedo.settings.dry_run_mode >= 2: 3675 return self 3676 3677 if not hasattr(self, "window"): 3678 return self 3679 if not self.window: 3680 return self 3681 if not hasattr(self, "interactor"): 3682 return self 3683 if not self.interactor: 3684 return self 3685 3686 ################################################### 3687 3688 self._must_close_now = True 3689 3690 if self.interactor: 3691 if self._interactive: 3692 self.break_interaction() 3693 self.interactor.GetRenderWindow().Finalize() 3694 try: 3695 if "Darwin" in vedo.sys_platform: 3696 self.interactor.ProcessEvents() 3697 except: 3698 pass 3699 self.interactor.TerminateApp() 3700 self.camera = None 3701 self.renderer = None 3702 self.renderers = [] 3703 self.window = None 3704 self.interactor = None 3705 3706 if vedo.plotter_instance == self: 3707 vedo.plotter_instance = None 3708 return self # must return self for consistency 3709 3710 3711 @property 3712 def camera(self): 3713 """Return the current active camera.""" 3714 if self.renderer: 3715 return self.renderer.GetActiveCamera() 3716 3717 @camera.setter 3718 def camera(self, cam): 3719 if self.renderer: 3720 if isinstance(cam, dict): 3721 cam = utils.camera_from_dict(cam) 3722 self.renderer.SetActiveCamera(cam) 3723 3724 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3725 """ 3726 Take a screenshot of the Plotter window. 3727 3728 Arguments: 3729 scale : (int) 3730 set image magnification as an integer multiplicating factor 3731 asarray : (bool) 3732 return a numpy array of the image instead of writing a file 3733 3734 Warning: 3735 If you get black screenshots try to set `interactive=False` in `show()` 3736 then call `screenshot()` and `plt.interactive()` afterwards. 3737 3738 Example: 3739 ```py 3740 from vedo import * 3741 sphere = Sphere().linewidth(1) 3742 plt = show(sphere, interactive=False) 3743 plt.screenshot('image.png') 3744 plt.interactive() 3745 plt.close() 3746 ``` 3747 3748 Example: 3749 ```py 3750 from vedo import * 3751 sphere = Sphere().linewidth(1) 3752 plt = show(sphere, interactive=False) 3753 plt.screenshot('anotherimage.png') 3754 plt.interactive() 3755 plt.close() 3756 ``` 3757 """ 3758 return vedo.file_io.screenshot(filename, scale, asarray) 3759 3760 def toimage(self, scale=1) -> "vedo.image.Image": 3761 """ 3762 Generate a `Image` object from the current rendering window. 3763 3764 Arguments: 3765 scale : (int) 3766 set image magnification as an integer multiplicating factor 3767 """ 3768 if vedo.settings.screeshot_large_image: 3769 w2if = vtki.new("RenderLargeImage") 3770 w2if.SetInput(self.renderer) 3771 w2if.SetMagnification(scale) 3772 else: 3773 w2if = vtki.new("WindowToImageFilter") 3774 w2if.SetInput(self.window) 3775 if hasattr(w2if, "SetScale"): 3776 w2if.SetScale(scale, scale) 3777 if vedo.settings.screenshot_transparent_background: 3778 w2if.SetInputBufferTypeToRGBA() 3779 w2if.ReadFrontBufferOff() # read from the back buffer 3780 w2if.Update() 3781 return vedo.image.Image(w2if.GetOutput()) 3782 3783 def export(self, filename="scene.npz", binary=False) -> Self: 3784 """ 3785 Export scene to file to HTML, X3D or Numpy file. 3786 3787 Examples: 3788 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3789 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3790 """ 3791 vedo.file_io.export_window(filename, binary=binary) 3792 return self 3793 3794 def color_picker(self, xy, verbose=False): 3795 """Pick color of specific (x,y) pixel on the screen.""" 3796 w2if = vtki.new("WindowToImageFilter") 3797 w2if.SetInput(self.window) 3798 w2if.ReadFrontBufferOff() 3799 w2if.Update() 3800 nx, ny = self.window.GetSize() 3801 varr = w2if.GetOutput().GetPointData().GetScalars() 3802 3803 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3804 x, y = int(xy[0]), int(xy[1]) 3805 if y < ny and x < nx: 3806 3807 rgb = arr[y, x] 3808 3809 if verbose: 3810 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3811 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3812 vedo.printc("█", c=[0, rgb[1], 0], end="") 3813 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3814 vedo.printc("] = ", end="") 3815 cnm = vedo.get_color_name(rgb) 3816 if np.sum(rgb) < 150: 3817 vedo.printc( 3818 rgb.tolist(), 3819 vedo.colors.rgb2hex(np.array(rgb) / 255), 3820 c="w", 3821 bc=rgb, 3822 invert=1, 3823 end="", 3824 ) 3825 vedo.printc(" -> " + cnm, invert=1, c="w") 3826 else: 3827 vedo.printc( 3828 rgb.tolist(), 3829 vedo.colors.rgb2hex(np.array(rgb) / 255), 3830 c=rgb, 3831 end="", 3832 ) 3833 vedo.printc(" -> " + cnm, c=cnm) 3834 3835 return rgb 3836 3837 return None 3838 3839 ####################################################################### 3840 def _default_mouseleftclick(self, iren, event) -> None: 3841 x, y = iren.GetEventPosition() 3842 renderer = iren.FindPokedRenderer(x, y) 3843 picker = vtki.vtkPropPicker() 3844 picker.PickProp(x, y, renderer) 3845 3846 self.renderer = renderer 3847 3848 clicked_actor = picker.GetActor() 3849 # clicked_actor2D = picker.GetActor2D() 3850 3851 # print('_default_mouseleftclick mouse at', x, y) 3852 # print("picked Volume:", [picker.GetVolume()]) 3853 # print("picked Actor2D:", [picker.GetActor2D()]) 3854 # print("picked Assembly:", [picker.GetAssembly()]) 3855 # print("picked Prop3D:", [picker.GetProp3D()]) 3856 3857 if not clicked_actor: 3858 clicked_actor = picker.GetAssembly() 3859 3860 if not clicked_actor: 3861 clicked_actor = picker.GetProp3D() 3862 3863 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3864 return 3865 3866 self.picked3d = picker.GetPickPosition() 3867 self.picked2d = np.array([x, y]) 3868 3869 if not clicked_actor: 3870 return 3871 3872 self.justremoved = None 3873 self.clicked_actor = clicked_actor 3874 3875 try: # might not be a vedo obj 3876 self.clicked_object = clicked_actor.retrieve_object() 3877 # save this info in the object itself 3878 self.clicked_object.picked3d = self.picked3d 3879 self.clicked_object.picked2d = self.picked2d 3880 except AttributeError: 3881 pass 3882 3883 # ----------- 3884 # if "Histogram1D" in picker.GetAssembly().__class__.__name__: 3885 # histo = picker.GetAssembly() 3886 # if histo.verbose: 3887 # x = self.picked3d[0] 3888 # idx = np.digitize(x, histo.edges) - 1 3889 # f = histo.frequencies[idx] 3890 # cn = histo.centers[idx] 3891 # vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3892 3893 ####################################################################### 3894 def _default_keypress(self, iren, event) -> None: 3895 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3896 3897 key = iren.GetKeySym() 3898 3899 if "_L" in key or "_R" in key: 3900 return 3901 3902 if iren.GetShiftKey(): 3903 key = key.upper() 3904 3905 if iren.GetControlKey(): 3906 key = "Ctrl+" + key 3907 3908 if iren.GetAltKey(): 3909 key = "Alt+" + key 3910 3911 ####################################################### 3912 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3913 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3914 # iren.GetKeyCode(), iren.GetRepeatCount()) 3915 ####################################################### 3916 3917 x, y = iren.GetEventPosition() 3918 renderer = iren.FindPokedRenderer(x, y) 3919 3920 if key in ["q", "Return"]: 3921 self.break_interaction() 3922 return 3923 3924 elif key in ["Ctrl+q", "Ctrl+w", "Escape"]: 3925 self.close() 3926 return 3927 3928 elif key == "F1": 3929 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3930 self.break_interaction() 3931 sys.exit(0) 3932 3933 elif key == "Down": 3934 if self.clicked_object and self.clicked_object in self.get_meshes(): 3935 self.clicked_object.alpha(0.02) 3936 if hasattr(self.clicked_object, "properties_backface"): 3937 bfp = self.clicked_actor.GetBackfaceProperty() 3938 self.clicked_object.properties_backface = bfp # save it 3939 self.clicked_actor.SetBackfaceProperty(None) 3940 else: 3941 for obj in self.get_meshes(): 3942 if obj: 3943 obj.alpha(0.02) 3944 bfp = obj.actor.GetBackfaceProperty() 3945 if bfp and hasattr(obj, "properties_backface"): 3946 obj.properties_backface = bfp 3947 obj.actor.SetBackfaceProperty(None) 3948 3949 elif key == "Left": 3950 if self.clicked_object and self.clicked_object in self.get_meshes(): 3951 ap = self.clicked_object.properties 3952 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3953 ap.SetOpacity(aal) 3954 bfp = self.clicked_actor.GetBackfaceProperty() 3955 if bfp and hasattr(self.clicked_object, "properties_backface"): 3956 self.clicked_object.properties_backface = bfp 3957 self.clicked_actor.SetBackfaceProperty(None) 3958 else: 3959 for a in self.get_meshes(): 3960 if a: 3961 ap = a.properties 3962 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3963 ap.SetOpacity(aal) 3964 bfp = a.actor.GetBackfaceProperty() 3965 if bfp and hasattr(a, "properties_backface"): 3966 a.properties_backface = bfp 3967 a.actor.SetBackfaceProperty(None) 3968 3969 elif key == "Right": 3970 if self.clicked_object and self.clicked_object in self.get_meshes(): 3971 ap = self.clicked_object.properties 3972 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3973 ap.SetOpacity(aal) 3974 if ( 3975 aal == 1 3976 and hasattr(self.clicked_object, "properties_backface") 3977 and self.clicked_object.properties_backface 3978 ): 3979 # put back 3980 self.clicked_actor.SetBackfaceProperty( 3981 self.clicked_object.properties_backface) 3982 else: 3983 for a in self.get_meshes(): 3984 if a: 3985 ap = a.properties 3986 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3987 ap.SetOpacity(aal) 3988 if ( 3989 aal == 1 3990 and hasattr(a, "properties_backface") 3991 and a.properties_backface 3992 ): 3993 a.actor.SetBackfaceProperty(a.properties_backface) 3994 3995 elif key == "Up": 3996 if self.clicked_object and self.clicked_object in self.get_meshes(): 3997 self.clicked_object.properties.SetOpacity(1) 3998 if ( 3999 hasattr(self.clicked_object, "properties_backface") 4000 and self.clicked_object.properties_backface 4001 ): 4002 self.clicked_object.actor.SetBackfaceProperty( 4003 self.clicked_object.properties_backface 4004 ) 4005 else: 4006 for a in self.get_meshes(): 4007 if a: 4008 a.properties.SetOpacity(1) 4009 if hasattr(a, "properties_backface") and a.properties_backface: 4010 a.actor.SetBackfaceProperty(a.properties_backface) 4011 4012 elif key == "P": 4013 if self.clicked_object and self.clicked_object in self.get_meshes(): 4014 objs = [self.clicked_object] 4015 else: 4016 objs = self.get_meshes() 4017 for ia in objs: 4018 try: 4019 ps = ia.properties.GetPointSize() 4020 if ps > 1: 4021 ia.properties.SetPointSize(ps - 1) 4022 ia.properties.SetRepresentationToPoints() 4023 except AttributeError: 4024 pass 4025 4026 elif key == "p": 4027 if self.clicked_object and self.clicked_object in self.get_meshes(): 4028 objs = [self.clicked_object] 4029 else: 4030 objs = self.get_meshes() 4031 for ia in objs: 4032 try: 4033 ps = ia.properties.GetPointSize() 4034 ia.properties.SetPointSize(ps + 2) 4035 ia.properties.SetRepresentationToPoints() 4036 except AttributeError: 4037 pass 4038 4039 elif key == "U": 4040 pval = renderer.GetActiveCamera().GetParallelProjection() 4041 renderer.GetActiveCamera().SetParallelProjection(not pval) 4042 if pval: 4043 renderer.ResetCamera() 4044 4045 elif key == "r": 4046 renderer.ResetCamera() 4047 4048 elif key == "h": 4049 msg = f" vedo {vedo.__version__}" 4050 msg += f" | vtk {vtki.vtkVersion().GetVTKVersion()}" 4051 msg += f" | numpy {np.__version__}" 4052 msg += f" | python {sys.version_info[0]}.{sys.version_info[1]}, press: " 4053 vedo.printc(msg.ljust(75), invert=True) 4054 msg = ( 4055 " i print info about the last clicked object \n" 4056 " I print color of the pixel under the mouse \n" 4057 " Y show the pipeline for this object as a graph \n" 4058 " <- -> use arrows to reduce/increase opacity \n" 4059 " x toggle mesh visibility \n" 4060 " w toggle wireframe/surface style \n" 4061 " l toggle surface edges visibility \n" 4062 " p/P hide surface faces and show only points \n" 4063 " 1-3 cycle surface color (2=light, 3=dark) \n" 4064 " 4 cycle color map (press shift-4 to go back) \n" 4065 " 5-6 cycle point-cell arrays (shift to go back) \n" 4066 " 7-8 cycle background and gradient color \n" 4067 " 09+- cycle axes styles (on keypad, or press +/-) \n" 4068 " k cycle available lighting styles \n" 4069 " K toggle shading as flat or phong \n" 4070 " A toggle anti-aliasing \n" 4071 " D toggle depth-peeling (for transparencies) \n" 4072 " U toggle perspective/parallel projection \n" 4073 " o/O toggle extra light to scene and rotate it \n" 4074 " a toggle interaction to Actor Mode \n" 4075 " n toggle surface normals \n" 4076 " r reset camera position \n" 4077 " R reset camera to the closest orthogonal view \n" 4078 " . fly camera to the last clicked point \n" 4079 " C print the current camera parameters state \n" 4080 " X invoke a cutter widget tool \n" 4081 " S save a screenshot of the current scene \n" 4082 " E/F export 3D scene to numpy file or X3D \n" 4083 " q return control to python script \n" 4084 " Esc abort execution and exit python kernel " 4085 ) 4086 vedo.printc(msg, dim=True, italic=True, bold=True) 4087 vedo.printc( 4088 " Check out the documentation at: https://vedo.embl.es ".ljust(75), 4089 invert=True, 4090 bold=True, 4091 ) 4092 return 4093 4094 elif key == "a": 4095 cur = iren.GetInteractorStyle() 4096 if isinstance(cur, vtki.get_class("InteractorStyleTrackballCamera")): 4097 msg = "Interactor style changed to TrackballActor\n" 4098 msg += " you can now move and rotate individual meshes:\n" 4099 msg += " press X twice to save the repositioned mesh\n" 4100 msg += " press 'a' to go back to normal style" 4101 vedo.printc(msg) 4102 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 4103 else: 4104 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 4105 return 4106 4107 elif key == "A": # toggle antialiasing 4108 msam = self.window.GetMultiSamples() 4109 if not msam: 4110 self.window.SetMultiSamples(16) 4111 else: 4112 self.window.SetMultiSamples(0) 4113 msam = self.window.GetMultiSamples() 4114 if msam: 4115 vedo.printc(f"Antialiasing set to {msam} samples", c=bool(msam)) 4116 else: 4117 vedo.printc("Antialiasing disabled", c=bool(msam)) 4118 4119 elif key == "D": # toggle depthpeeling 4120 udp = not renderer.GetUseDepthPeeling() 4121 renderer.SetUseDepthPeeling(udp) 4122 # self.renderer.SetUseDepthPeelingForVolumes(udp) 4123 if udp: 4124 self.window.SetAlphaBitPlanes(1) 4125 renderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 4126 renderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 4127 self.interactor.Render() 4128 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 4129 rnr = self.renderers.index(renderer) 4130 vedo.printc(f"Depth peeling set to {udp} for renderer nr.{rnr}", c=udp) 4131 if not wasUsed and udp: 4132 vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True) 4133 return 4134 4135 elif key == "period": 4136 if self.picked3d: 4137 self.fly_to(self.picked3d) 4138 return 4139 4140 elif key == "S": 4141 fname = "screenshot.png" 4142 i = 1 4143 while os.path.isfile(fname): 4144 fname = f"screenshot{i}.png" 4145 i += 1 4146 vedo.file_io.screenshot(fname) 4147 vedo.printc(rf":camera: Saved rendering window to {fname}", c="b") 4148 return 4149 4150 elif key == "C": 4151 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 4152 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 4153 # This could happen for plotting geological models with UTM coordinate systems 4154 cam = renderer.GetActiveCamera() 4155 vedo.printc("\n###################################################", c="y") 4156 vedo.printc("## Template python code to position this camera: ##", c="y") 4157 vedo.printc("cam = dict(", c="y") 4158 vedo.printc(" pos=" + utils.precision(cam.GetPosition(), 6) + ",", c="y") 4159 vedo.printc(" focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y") 4160 vedo.printc(" viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y") 4161 vedo.printc(" roll=" + utils.precision(cam.GetRoll(), 6) + ",", c="y") 4162 if cam.GetParallelProjection(): 4163 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 4164 else: 4165 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 4166 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 4167 vedo.printc(')', c='y') 4168 vedo.printc('show(mymeshes, camera=cam)', c='y') 4169 vedo.printc('###################################################', c='y') 4170 return 4171 4172 elif key == "R": 4173 self.reset_viewup() 4174 4175 elif key == "w": 4176 try: 4177 if self.clicked_object.properties.GetRepresentation() == 1: # toggle 4178 self.clicked_object.properties.SetRepresentationToSurface() 4179 else: 4180 self.clicked_object.properties.SetRepresentationToWireframe() 4181 except AttributeError: 4182 pass 4183 4184 elif key == "1": 4185 try: 4186 self._icol += 1 4187 self.clicked_object.mapper.ScalarVisibilityOff() 4188 pal = vedo.colors.palettes[vedo.settings.palette % len(vedo.colors.palettes)] 4189 self.clicked_object.c(pal[(self._icol) % 10]) 4190 self.remove(self.clicked_object.scalarbar) 4191 except AttributeError: 4192 pass 4193 4194 elif key == "2": # dark colors 4195 try: 4196 bsc = ["k1", "k2", "k3", "k4", 4197 "b1", "b2", "b3", "b4", 4198 "p1", "p2", "p3", "p4", 4199 "g1", "g2", "g3", "g4", 4200 "r1", "r2", "r3", "r4", 4201 "o1", "o2", "o3", "o4", 4202 "y1", "y2", "y3", "y4"] 4203 self._icol += 1 4204 if self.clicked_object: 4205 self.clicked_object.mapper.ScalarVisibilityOff() 4206 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4207 self.clicked_object.c(newcol) 4208 self.remove(self.clicked_object.scalarbar) 4209 except AttributeError: 4210 pass 4211 4212 elif key == "3": # light colors 4213 try: 4214 bsc = ["k6", "k7", "k8", "k9", 4215 "b6", "b7", "b8", "b9", 4216 "p6", "p7", "p8", "p9", 4217 "g6", "g7", "g8", "g9", 4218 "r6", "r7", "r8", "r9", 4219 "o6", "o7", "o8", "o9", 4220 "y6", "y7", "y8", "y9"] 4221 self._icol += 1 4222 if self.clicked_object: 4223 self.clicked_object.mapper.ScalarVisibilityOff() 4224 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4225 self.clicked_object.c(newcol) 4226 self.remove(self.clicked_object.scalarbar) 4227 except AttributeError: 4228 pass 4229 4230 elif key == "4": # cmap name cycle 4231 ob = self.clicked_object 4232 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4233 return 4234 if not ob.mapper.GetScalarVisibility(): 4235 return 4236 onwhat = ob.mapper.GetScalarModeAsString() # UsePointData/UseCellData 4237 4238 cmap_names = [ 4239 "Accent", 4240 "Paired", 4241 "rainbow", 4242 "rainbow_r", 4243 "Spectral", 4244 "Spectral_r", 4245 "gist_ncar", 4246 "gist_ncar_r", 4247 "viridis", 4248 "viridis_r", 4249 "hot", 4250 "hot_r", 4251 "terrain", 4252 "ocean", 4253 "coolwarm", 4254 "seismic", 4255 "PuOr", 4256 "RdYlGn", 4257 ] 4258 try: 4259 i = cmap_names.index(ob._cmap_name) 4260 if iren.GetShiftKey(): 4261 i -= 1 4262 else: 4263 i += 1 4264 if i >= len(cmap_names): 4265 i = 0 4266 if i < 0: 4267 i = len(cmap_names) - 1 4268 except ValueError: 4269 i = 0 4270 4271 ob._cmap_name = cmap_names[i] 4272 ob.cmap(ob._cmap_name, on=onwhat) 4273 if ob.scalarbar: 4274 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4275 self.remove(ob.scalarbar) 4276 title = ob.scalarbar.GetTitle() 4277 ob.add_scalarbar(title=title) 4278 self.add(ob.scalarbar).render() 4279 elif isinstance(ob.scalarbar, vedo.Assembly): 4280 self.remove(ob.scalarbar) 4281 ob.add_scalarbar3d(title=ob._cmap_name) 4282 self.add(ob.scalarbar) 4283 4284 vedo.printc( 4285 f"Name:'{ob.name}'," if ob.name else "", 4286 f"range:{utils.precision(ob.mapper.GetScalarRange(),3)},", 4287 f"colormap:'{ob._cmap_name}'", c="g", bold=False, 4288 ) 4289 4290 elif key == "5": # cycle pointdata array 4291 ob = self.clicked_object 4292 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4293 return 4294 4295 arrnames = ob.pointdata.keys() 4296 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4297 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4298 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4299 if len(arrnames) == 0: 4300 return 4301 ob.mapper.SetScalarVisibility(1) 4302 4303 if not ob._cmap_name: 4304 ob._cmap_name = "rainbow" 4305 4306 try: 4307 curr_name = ob.dataset.GetPointData().GetScalars().GetName() 4308 i = arrnames.index(curr_name) 4309 if "normals" in curr_name.lower(): 4310 return 4311 if iren.GetShiftKey(): 4312 i -= 1 4313 else: 4314 i += 1 4315 if i >= len(arrnames): 4316 i = 0 4317 if i < 0: 4318 i = len(arrnames) - 1 4319 except (ValueError, AttributeError): 4320 i = 0 4321 4322 ob.cmap(ob._cmap_name, arrnames[i], on="points") 4323 if ob.scalarbar: 4324 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4325 self.remove(ob.scalarbar) 4326 title = ob.scalarbar.GetTitle() 4327 ob.scalarbar = None 4328 ob.add_scalarbar(title=arrnames[i]) 4329 self.add(ob.scalarbar) 4330 elif isinstance(ob.scalarbar, vedo.Assembly): 4331 self.remove(ob.scalarbar) 4332 ob.scalarbar = None 4333 ob.add_scalarbar3d(title=arrnames[i]) 4334 self.add(ob.scalarbar) 4335 else: 4336 vedo.printc( 4337 f"Name:'{ob.name}'," if ob.name else "", 4338 f"active pointdata array: '{arrnames[i]}'", 4339 c="g", 4340 bold=False, 4341 ) 4342 4343 elif key == "6": # cycle celldata array 4344 ob = self.clicked_object 4345 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4346 return 4347 4348 arrnames = ob.celldata.keys() 4349 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4350 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4351 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4352 if len(arrnames) == 0: 4353 return 4354 ob.mapper.SetScalarVisibility(1) 4355 4356 if not ob._cmap_name: 4357 ob._cmap_name = "rainbow" 4358 4359 try: 4360 curr_name = ob.dataset.GetCellData().GetScalars().GetName() 4361 i = arrnames.index(curr_name) 4362 if "normals" in curr_name.lower(): 4363 return 4364 if iren.GetShiftKey(): 4365 i -= 1 4366 else: 4367 i += 1 4368 if i >= len(arrnames): 4369 i = 0 4370 if i < 0: 4371 i = len(arrnames) - 1 4372 except (ValueError, AttributeError): 4373 i = 0 4374 4375 ob.cmap(ob._cmap_name, arrnames[i], on="cells") 4376 if ob.scalarbar: 4377 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4378 self.remove(ob.scalarbar) 4379 title = ob.scalarbar.GetTitle() 4380 ob.scalarbar = None 4381 ob.add_scalarbar(title=arrnames[i]) 4382 self.add(ob.scalarbar) 4383 elif isinstance(ob.scalarbar, vedo.Assembly): 4384 self.remove(ob.scalarbar) 4385 ob.scalarbar = None 4386 ob.add_scalarbar3d(title=arrnames[i]) 4387 self.add(ob.scalarbar) 4388 else: 4389 vedo.printc( 4390 f"Name:'{ob.name}'," if ob.name else "", 4391 f"active celldata array: '{arrnames[i]}'", 4392 c="g", bold=False, 4393 ) 4394 4395 elif key == "7": 4396 bgc = np.array(renderer.GetBackground()).sum() / 3 4397 if bgc <= 0: 4398 bgc = 0.223 4399 elif 0 < bgc < 1: 4400 bgc = 1 4401 else: 4402 bgc = 0 4403 renderer.SetBackground(bgc, bgc, bgc) 4404 4405 elif key == "8": 4406 bg2cols = [ 4407 "lightyellow", 4408 "darkseagreen", 4409 "palegreen", 4410 "steelblue", 4411 "lightblue", 4412 "cadetblue", 4413 "lavender", 4414 "white", 4415 "blackboard", 4416 "black", 4417 ] 4418 bg2name = vedo.get_color_name(renderer.GetBackground2()) 4419 if bg2name in bg2cols: 4420 idx = bg2cols.index(bg2name) 4421 else: 4422 idx = 4 4423 if idx is not None: 4424 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 4425 if not bg2name_next: 4426 renderer.GradientBackgroundOff() 4427 else: 4428 renderer.GradientBackgroundOn() 4429 renderer.SetBackground2(vedo.get_color(bg2name_next)) 4430 4431 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 4432 i = self.renderers.index(renderer) 4433 try: 4434 self.axes_instances[i].EnabledOff() 4435 self.axes_instances[i].SetInteractor(None) 4436 except AttributeError: 4437 # print("Cannot remove widget", [self.axes_instances[i]]) 4438 try: 4439 self.remove(self.axes_instances[i]) 4440 except: 4441 print("Cannot remove axes", [self.axes_instances[i]]) 4442 return 4443 self.axes_instances[i] = None 4444 4445 if not self.axes: 4446 self.axes = 0 4447 if isinstance(self.axes, dict): 4448 self.axes = 1 4449 4450 if key in ["minus", "KP_Subtract"]: 4451 if not self.camera.GetParallelProjection() and self.axes == 0: 4452 self.axes -= 1 # jump ruler doesnt make sense in perspective mode 4453 bns = self.renderer.ComputeVisiblePropBounds() 4454 addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None, bounds=bns) 4455 else: 4456 if not self.camera.GetParallelProjection() and self.axes == 12: 4457 self.axes += 1 # jump ruler doesnt make sense in perspective mode 4458 bns = self.renderer.ComputeVisiblePropBounds() 4459 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns) 4460 self.render() 4461 4462 elif "KP_" in key or key in [ 4463 "Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior" 4464 ]: 4465 asso = { # change axes style 4466 "KP_Insert": 0, "KP_0": 0, "Insert": 0, 4467 "KP_End": 1, "KP_1": 1, "End": 1, 4468 "KP_Down": 2, "KP_2": 2, "Down": 2, 4469 "KP_Next": 3, "KP_3": 3, "Next": 3, 4470 "KP_Left": 4, "KP_4": 4, "Left": 4, 4471 "KP_Begin": 5, "KP_5": 5, "Begin": 5, 4472 "KP_Right": 6, "KP_6": 6, "Right": 6, 4473 "KP_Home": 7, "KP_7": 7, "Home": 7, 4474 "KP_Up": 8, "KP_8": 8, "Up": 8, 4475 "Prior": 9, # on windows OS 4476 } 4477 clickedr = self.renderers.index(renderer) 4478 if key in asso: 4479 if self.axes_instances[clickedr]: 4480 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 4481 self.axes_instances[clickedr].EnabledOff() 4482 else: 4483 try: 4484 renderer.RemoveActor(self.axes_instances[clickedr]) 4485 except: 4486 pass 4487 self.axes_instances[clickedr] = None 4488 bounds = renderer.ComputeVisiblePropBounds() 4489 addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds) 4490 self.interactor.Render() 4491 4492 if key == "O": 4493 renderer.RemoveLight(self._extralight) 4494 self._extralight = None 4495 4496 elif key == "o": 4497 vbb, sizes, _, _ = addons.compute_visible_bounds() 4498 cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2) 4499 if not self._extralight: 4500 vup = renderer.GetActiveCamera().GetViewUp() 4501 pos = cm + utils.vector(vup) * utils.mag(sizes) 4502 self._extralight = addons.Light(pos, focal_point=cm, intensity=0.4) 4503 renderer.AddLight(self._extralight) 4504 vedo.printc("Press 'o' again to rotate light source, or 'O' to remove it.", c='y') 4505 else: 4506 cpos = utils.vector(self._extralight.GetPosition()) 4507 x, y, z = self._extralight.GetPosition() - cm 4508 r, th, ph = transformations.cart2spher(x, y, z) 4509 th += 0.2 4510 if th > np.pi: 4511 th = np.random.random() * np.pi / 2 4512 ph += 0.3 4513 cpos = transformations.spher2cart(r, th, ph).T + cm 4514 self._extralight.SetPosition(cpos) 4515 4516 elif key == "l": 4517 if self.clicked_object in self.get_meshes(): 4518 objs = [self.clicked_object] 4519 else: 4520 objs = self.get_meshes() 4521 for ia in objs: 4522 try: 4523 ev = ia.properties.GetEdgeVisibility() 4524 ia.properties.SetEdgeVisibility(not ev) 4525 ia.properties.SetRepresentationToSurface() 4526 ia.properties.SetLineWidth(0.1) 4527 except AttributeError: 4528 pass 4529 4530 elif key == "k": # lightings 4531 if self.clicked_object in self.get_meshes(): 4532 objs = [self.clicked_object] 4533 else: 4534 objs = self.get_meshes() 4535 shds = ("default", "metallic", "plastic", "shiny", "glossy", "off") 4536 for ia in objs: 4537 try: 4538 lnr = (ia._ligthingnr + 1) % 6 4539 ia.lighting(shds[lnr]) 4540 ia._ligthingnr = lnr 4541 except AttributeError: 4542 pass 4543 4544 elif key == "K": # shading 4545 if self.clicked_object in self.get_meshes(): 4546 objs = [self.clicked_object] 4547 else: 4548 objs = self.get_meshes() 4549 for ia in objs: 4550 if isinstance(ia, vedo.Mesh): 4551 ia.compute_normals(cells=False) 4552 intrp = ia.properties.GetInterpolation() 4553 if intrp > 0: 4554 ia.properties.SetInterpolation(0) # flat 4555 else: 4556 ia.properties.SetInterpolation(2) # phong 4557 4558 elif key == "n": # show normals to an actor 4559 self.remove("added_auto_normals") 4560 if self.clicked_object in self.get_meshes(): 4561 if self.clicked_actor.GetPickable(): 4562 norml = vedo.shapes.NormalLines(self.clicked_object) 4563 norml.name = "added_auto_normals" 4564 self.add(norml) 4565 4566 elif key == "x": 4567 if self.justremoved is None: 4568 if self.clicked_object in self.get_meshes() or isinstance( 4569 self.clicked_object, vtki.vtkAssembly 4570 ): 4571 self.justremoved = self.clicked_actor 4572 self.renderer.RemoveActor(self.clicked_actor) 4573 else: 4574 self.renderer.AddActor(self.justremoved) 4575 self.justremoved = None 4576 4577 elif key == "X": 4578 if self.clicked_object: 4579 if not self.cutter_widget: 4580 self.cutter_widget = addons.BoxCutter(self.clicked_object) 4581 self.add(self.cutter_widget) 4582 vedo.printc("Press i to toggle the cutter on/off", c='g', dim=1) 4583 vedo.printc(" u to flip selection", c='g', dim=1) 4584 vedo.printc(" r to reset cutting planes", c='g', dim=1) 4585 vedo.printc(" Shift+X to close the cutter box widget", c='g', dim=1) 4586 vedo.printc(" Ctrl+S to save the cut section to file.", c='g', dim=1) 4587 else: 4588 self.remove(self.cutter_widget) 4589 self.cutter_widget = None 4590 vedo.printc("Click object and press X to open the cutter box widget.", c='g') 4591 4592 elif key == "E": 4593 vedo.printc(r":camera: Exporting 3D window to file scene.npz", c="b", end="") 4594 vedo.file_io.export_window("scene.npz") 4595 vedo.printc(", try:\n> vedo scene.npz # (this is experimental!)", c="b") 4596 return 4597 4598 elif key == "F": 4599 vedo.file_io.export_window("scene.x3d") 4600 vedo.printc(r":camera: Exporting 3D window to file", c="b", end="") 4601 vedo.file_io.export_window("scene.npz") 4602 vedo.printc(". Try:\n> firefox scene.html", c="b") 4603 4604 # elif key == "G": # not working with last version of k3d 4605 # vedo.file_io.export_window("scene.html") 4606 # vedo.printc(r":camera: Exporting K3D window to file", c="b", end="") 4607 # vedo.file_io.export_window("scene.html") 4608 # vedo.printc(". Try:\n> firefox scene.html", c="b") 4609 4610 elif key == "i": # print info 4611 if self.clicked_object: 4612 print(self.clicked_object) 4613 else: 4614 print(self) 4615 4616 elif key == "I": # print color under the mouse 4617 x, y = iren.GetEventPosition() 4618 self.color_picker([x, y], verbose=True) 4619 4620 elif key == "Y": 4621 if self.clicked_object and self.clicked_object.pipeline: 4622 self.clicked_object.pipeline.show() 4623 4624 if iren: 4625 iren.Render()
394class Plotter: 395 """Main class to manage objects.""" 396 397 def __init__( 398 self, 399 shape=(1, 1), 400 N=None, 401 pos=(0, 0), 402 size="auto", 403 screensize="auto", 404 title="vedo", 405 bg="white", 406 bg2=None, 407 axes=None, 408 sharecam=True, 409 resetcam=True, 410 interactive=None, 411 offscreen=False, 412 qt_widget=None, 413 wx_widget=None, 414 ): 415 """ 416 Arguments: 417 shape : (str, list) 418 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 419 N : (int) 420 number of desired renderers arranged in a grid automatically. 421 pos : (list) 422 (x,y) position in pixels of top-left corner of the rendering window on the screen 423 size : (str, list) 424 size of the rendering window. If 'auto', guess it based on screensize. 425 screensize : (list) 426 physical size of the monitor screen in pixels 427 bg : (color, str) 428 background color or specify jpg image file name with path 429 bg2 : (color) 430 background color of a gradient towards the top 431 title : (str) 432 window title 433 axes : (int) 434 axis type-1 can be fully customized by passing a dictionary. 435 Check `addons.Axes()` for the full list of options. 436 Set the type of axes to be shown: 437 - 0, no axes 438 - 1, draw three gray grid walls 439 - 2, show cartesian axes from (0,0,0) 440 - 3, show positive range of cartesian axes from (0,0,0) 441 - 4, show a triad at bottom left 442 - 5, show a cube at bottom left 443 - 6, mark the corners of the bounding box 444 - 7, draw a 3D ruler at each side of the cartesian axes 445 - 8, show the `vtkCubeAxesActor` object 446 - 9, show the bounding box outLine 447 - 10, show three circles representing the maximum bounding box 448 - 11, show a large grid on the x-y plane 449 - 12, show polar axes 450 - 13, draw a simple ruler at the bottom of the window 451 - 14: draw a camera orientation widget 452 453 sharecam : (bool) 454 if False each renderer will have an independent camera 455 interactive : (bool) 456 if True will stop after show() to allow interaction with the 3d scene 457 offscreen : (bool) 458 if True will not show the rendering window 459 qt_widget : (QVTKRenderWindowInteractor) 460 render in a Qt-Widget using an QVTKRenderWindowInteractor. 461 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 462 """ 463 vedo.plotter_instance = self 464 465 if interactive is None: 466 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 467 self._interactive = interactive 468 # print("interactive", interactive, N, shape) 469 470 self.objects = [] # list of objects to be shown 471 self.clicked_object = None # holds the object that has been clicked 472 self.clicked_actor = None # holds the actor that has been clicked 473 474 self.shape = shape # nr. of subwindows in grid 475 self.axes = axes # show axes type nr. 476 self.title = title # window title 477 self.size = size # window size 478 self.backgrcol = bg # used also by backend notebooks 479 480 self.offscreen= offscreen 481 self.resetcam = resetcam 482 self.sharecam = sharecam # share the same camera if multiple renderers 483 self.pos = pos # used by vedo.file_io 484 485 self.picker = None # hold the vtkPicker object 486 self.picked2d = None # 2d coords of a clicked point on the rendering window 487 self.picked3d = None # 3d coords of a clicked point on an actor 488 489 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 490 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 491 self.interactor = None 492 self.window = None 493 self.renderer = None 494 self.renderers = [] # list of renderers 495 496 # mostly internal stuff: 497 self.hover_legends = [] 498 self.justremoved = None 499 self.axes_instances = [] 500 self.clock = 0 501 self.sliders = [] 502 self.buttons = [] 503 self.widgets = [] 504 self.cutter_widget = None 505 self.hint_widget = None 506 self.background_renderer = None 507 self.last_event = None 508 self.skybox = None 509 self._icol = 0 510 self._clockt0 = time.time() 511 self._extralight = None 512 self._cocoa_initialized = False 513 self._cocoa_process_events = True # make one call in show() 514 self._must_close_now = False 515 516 ##################################################################### 517 if vedo.settings.default_backend == "2d": 518 self.offscreen = True 519 if self.size == "auto": 520 self.size = (800, 600) 521 522 elif vedo.settings.default_backend == "k3d": 523 if self.size == "auto": 524 self.size = (1000, 1000) 525 #################################### 526 return ############################ 527 #################################### 528 529 ############################################################# 530 if screensize == "auto": 531 screensize = (2160, 1440) # TODO: get actual screen size 532 533 # build the rendering window: 534 self.window = vtki.vtkRenderWindow() 535 536 self.window.GlobalWarningDisplayOff() 537 538 if self.title == "vedo": # check if dev version 539 if "dev" in vedo.__version__: 540 self.title = f"vedo ({vedo.__version__})" 541 self.window.SetWindowName(self.title) 542 543 # more vedo.settings 544 if vedo.settings.use_depth_peeling: 545 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 546 self.window.SetMultiSamples(vedo.settings.multi_samples) 547 548 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 549 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 550 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 551 552 ############################################################# 553 if N: # N = number of renderers. Find out the best 554 555 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 556 vedo.logger.warning("having set N, shape is ignored.") 557 558 x, y = screensize 559 nx = int(np.sqrt(int(N * y / x) + 1)) 560 ny = int(np.sqrt(int(N * x / y) + 1)) 561 lm = [ 562 (nx, ny), 563 (nx, ny + 1), 564 (nx - 1, ny), 565 (nx + 1, ny), 566 (nx, ny - 1), 567 (nx - 1, ny + 1), 568 (nx + 1, ny - 1), 569 (nx + 1, ny + 1), 570 (nx - 1, ny - 1), 571 ] 572 ind, minl = 0, 1000 573 for i, m in enumerate(lm): 574 l = m[0] * m[1] 575 if N <= l < minl: 576 ind = i 577 minl = l 578 shape = lm[ind] 579 580 ################################################## 581 if isinstance(shape, str): 582 583 if "|" in shape: 584 if self.size == "auto": 585 self.size = (800, 1200) 586 n = int(shape.split("|")[0]) 587 m = int(shape.split("|")[1]) 588 rangen = reversed(range(n)) 589 rangem = reversed(range(m)) 590 else: 591 if self.size == "auto": 592 self.size = (1200, 800) 593 m = int(shape.split("/")[0]) 594 n = int(shape.split("/")[1]) 595 rangen = range(n) 596 rangem = range(m) 597 598 if n >= m: 599 xsplit = m / (n + m) 600 else: 601 xsplit = 1 - n / (n + m) 602 if vedo.settings.window_splitting_position: 603 xsplit = vedo.settings.window_splitting_position 604 605 for i in rangen: 606 arenderer = vtki.vtkRenderer() 607 if "|" in shape: 608 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 609 else: 610 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 611 self.renderers.append(arenderer) 612 613 for i in rangem: 614 arenderer = vtki.vtkRenderer() 615 616 if "|" in shape: 617 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 618 else: 619 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 620 self.renderers.append(arenderer) 621 622 for r in self.renderers: 623 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 624 625 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 626 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 627 if vedo.settings.use_depth_peeling: 628 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 629 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 630 r.SetUseFXAA(vedo.settings.use_fxaa) 631 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 632 633 r.SetBackground(vedo.get_color(self.backgrcol)) 634 635 self.axes_instances.append(None) 636 637 self.shape = (n + m,) 638 639 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 640 # passing a sequence of dicts for renderers specifications 641 642 if self.size == "auto": 643 self.size = (1000, 800) 644 645 for rd in shape: 646 x0, y0 = rd["bottomleft"] 647 x1, y1 = rd["topright"] 648 bg_ = rd.pop("bg", "white") 649 bg2_ = rd.pop("bg2", None) 650 651 arenderer = vtki.vtkRenderer() 652 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 653 654 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 655 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 656 if vedo.settings.use_depth_peeling: 657 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 658 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 659 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 660 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 661 662 arenderer.SetViewport(x0, y0, x1, y1) 663 arenderer.SetBackground(vedo.get_color(bg_)) 664 if bg2_: 665 arenderer.GradientBackgroundOn() 666 arenderer.SetBackground2(vedo.get_color(bg2_)) 667 668 self.renderers.append(arenderer) 669 self.axes_instances.append(None) 670 671 self.shape = (len(shape),) 672 673 else: 674 675 if isinstance(self.size, str) and self.size == "auto": 676 # figure out a reasonable window size 677 f = 1.5 678 x, y = screensize 679 xs = y / f * shape[1] # because y<x 680 ys = y / f * shape[0] 681 if xs > x / f: # shrink 682 xs = x / f 683 ys = xs / shape[1] * shape[0] 684 if ys > y / f: 685 ys = y / f 686 xs = ys / shape[0] * shape[1] 687 self.size = (int(xs), int(ys)) 688 if shape == (1, 1): 689 self.size = (int(y / f), int(y / f)) # because y<x 690 else: 691 self.size = (self.size[0], self.size[1]) 692 693 try: 694 image_actor = None 695 bgname = str(self.backgrcol).lower() 696 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 697 self.window.SetNumberOfLayers(2) 698 self.background_renderer = vtki.vtkRenderer() 699 self.background_renderer.SetLayer(0) 700 self.background_renderer.InteractiveOff() 701 self.background_renderer.SetBackground(vedo.get_color(bg2)) 702 image_actor = vedo.Image(self.backgrcol).actor 703 self.window.AddRenderer(self.background_renderer) 704 self.background_renderer.AddActor(image_actor) 705 except AttributeError: 706 pass 707 708 for i in reversed(range(shape[0])): 709 for j in range(shape[1]): 710 arenderer = vtki.vtkRenderer() 711 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 712 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 713 714 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 715 if vedo.settings.use_depth_peeling: 716 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 717 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 718 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 719 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 720 721 if image_actor: 722 arenderer.SetLayer(1) 723 724 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 725 if bg2: 726 arenderer.GradientBackgroundOn() 727 arenderer.SetBackground2(vedo.get_color(bg2)) 728 729 x0 = i / shape[0] 730 y0 = j / shape[1] 731 x1 = (i + 1) / shape[0] 732 y1 = (j + 1) / shape[1] 733 arenderer.SetViewport(y0, x0, y1, x1) 734 self.renderers.append(arenderer) 735 self.axes_instances.append(None) 736 self.shape = shape 737 738 if self.renderers: 739 self.renderer = self.renderers[0] 740 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 741 742 ######################################################### 743 if self.qt_widget or self.wx_widget: 744 if self.qt_widget: 745 self.window = self.qt_widget.GetRenderWindow() # overwrite 746 else: 747 self.window = self.wx_widget.GetRenderWindow() 748 self.interactor = self.window.GetInteractor() 749 750 ######################################################### 751 for r in self.renderers: 752 self.window.AddRenderer(r) 753 # set the background gradient if any 754 if vedo.settings.background_gradient_orientation > 0: 755 try: 756 modes = [ 757 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 758 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 759 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 760 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 761 ] 762 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 763 r.GradientBackgroundOn() 764 except AttributeError: 765 pass 766 767 # Backend #################################################### 768 if vedo.settings.default_backend in ["panel", "trame", "k3d"]: 769 return ################ 770 ######################## 771 772 ######################################################### 773 if self.qt_widget or self.wx_widget: 774 self.interactor.SetRenderWindow(self.window) 775 if vedo.settings.enable_default_keyboard_callbacks: 776 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 777 if vedo.settings.enable_default_mouse_callbacks: 778 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 779 return ################ 780 ######################## 781 782 if self.size[0] == "f": # full screen 783 self.size = "fullscreen" 784 self.window.SetFullScreen(True) 785 self.window.BordersOn() 786 else: 787 self.window.SetSize(int(self.size[0]), int(self.size[1])) 788 789 if self.offscreen: 790 if self.axes in (4, 5, 8, 12, 14): 791 self.axes = 0 # does not work with those 792 self.window.SetOffScreenRendering(True) 793 self.interactor = None 794 self._interactive = False 795 return ################ 796 ######################## 797 798 self.window.SetPosition(pos) 799 800 ######################################################### 801 self.interactor = vtki.vtkRenderWindowInteractor() 802 803 self.interactor.SetRenderWindow(self.window) 804 vsty = vtki.new("InteractorStyleTrackballCamera") 805 self.interactor.SetInteractorStyle(vsty) 806 self.interactor.RemoveObservers("CharEvent") 807 808 if vedo.settings.enable_default_keyboard_callbacks: 809 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 810 if vedo.settings.enable_default_mouse_callbacks: 811 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 812 813 ##################################################################### ..init ends here. 814 815 def __str__(self): 816 """Return Plotter info.""" 817 axtype = { 818 0: "(no axes)", 819 1: "(default customizable grid walls)", 820 2: "(cartesian axes from origin", 821 3: "(positive range of cartesian axes from origin", 822 4: "(axes triad at bottom left)", 823 5: "(oriented cube at bottom left)", 824 6: "(mark the corners of the bounding box)", 825 7: "(3D ruler at each side of the cartesian axes)", 826 8: "(the vtkCubeAxesActor object)", 827 9: "(the bounding box outline)", 828 10: "(circles of maximum bounding box range)", 829 11: "(show a large grid on the x-y plane)", 830 12: "(show polar axes)", 831 13: "(simple ruler at the bottom of the window)", 832 14: "(the vtkCameraOrientationWidget object)", 833 } 834 835 module = self.__class__.__module__ 836 name = self.__class__.__name__ 837 out = vedo.printc( 838 f"{module}.{name} at ({hex(id(self))})".ljust(75), 839 bold=True, invert=True, return_string=True, 840 ) 841 out += "\x1b[0m" 842 if self.interactor: 843 out += "window title".ljust(14) + ": " + self.title + "\n" 844 out += "window size".ljust(14) + f": {self.window.GetSize()}" 845 out += f", full_screen={self.window.GetScreenSize()}\n" 846 out += "activ renderer".ljust(14) + ": nr." + str(self.renderers.index(self.renderer)) 847 out += f" (out of {len(self.renderers)} renderers)\n" 848 849 bns, totpt = [], 0 850 for a in self.objects: 851 try: 852 b = a.bounds() 853 bns.append(b) 854 except (AttributeError, TypeError): 855 pass 856 try: 857 totpt += a.npoints 858 except AttributeError: 859 pass 860 out += "n. of objects".ljust(14) + f": {len(self.objects)}" 861 out += f" ({totpt} vertices)\n" if totpt else "\n" 862 863 if len(bns) > 0: 864 min_bns = np.min(bns, axis=0) 865 max_bns = np.max(bns, axis=0) 866 bx1, bx2 = utils.precision(min_bns[0], 3), utils.precision(max_bns[1], 3) 867 by1, by2 = utils.precision(min_bns[2], 3), utils.precision(max_bns[3], 3) 868 bz1, bz2 = utils.precision(min_bns[4], 3), utils.precision(max_bns[5], 3) 869 out += "bounds".ljust(14) + ":" 870 out += " x=(" + bx1 + ", " + bx2 + ")," 871 out += " y=(" + by1 + ", " + by2 + ")," 872 out += " z=(" + bz1 + ", " + bz2 + ")\n" 873 874 if utils.is_integer(self.axes): 875 out += "axes style".ljust(14) + f": {self.axes} {axtype[self.axes]}\n" 876 elif isinstance(self.axes, dict): 877 out += "axes style".ljust(14) + f": 1 {axtype[1]}\n" 878 else: 879 out += "axes style".ljust(14) + f": {[self.axes]}\n" 880 return out.rstrip() + "\x1b[0m" 881 882 def print(self): 883 """Print information about the current instance.""" 884 print(self.__str__()) 885 return self 886 887 def __iadd__(self, objects): 888 self.add(objects) 889 return self 890 891 def __isub__(self, objects): 892 self.remove(objects) 893 return self 894 895 def __enter__(self): 896 # context manager like in "with Plotter() as plt:" 897 return self 898 899 def __exit__(self, *args, **kwargs): 900 # context manager like in "with Plotter() as plt:" 901 self.close() 902 903 def initialize_interactor(self) -> Self: 904 """Initialize the interactor if not already initialized.""" 905 if self.offscreen: 906 return self 907 if self.interactor: 908 if not self.interactor.GetInitialized(): 909 self.interactor.Initialize() 910 self.interactor.RemoveObservers("CharEvent") 911 return self 912 913 def process_events(self) -> Self: 914 """Process all pending events.""" 915 self.initialize_interactor() 916 if self.interactor: 917 try: 918 self.interactor.ProcessEvents() 919 except AttributeError: 920 pass 921 return self 922 923 def at(self, nren: int, yren=None) -> Self: 924 """ 925 Select the current renderer number as an int. 926 Can also use the `[nx, ny]` format. 927 """ 928 if utils.is_sequence(nren): 929 if len(nren) == 2: 930 nren, yren = nren 931 else: 932 vedo.logger.error("at() argument must be a single number or a list of two numbers") 933 raise TypeError 934 935 if yren is not None: 936 a, b = self.shape 937 x, y = nren, yren 938 nren_orig = nren 939 nren = x * b + y 940 # print("at (", x, y, ") -> ren", nren) 941 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 942 vedo.logger.error(f"at({nren_orig, yren}) is malformed!") 943 raise RuntimeError 944 945 self.renderer = self.renderers[nren] 946 return self 947 948 def add(self, *objs, at=None) -> Self: 949 """ 950 Append the input objects to the internal list of objects to be shown. 951 952 Arguments: 953 at : (int) 954 add the object at the specified renderer 955 """ 956 ren = self.renderer if at is None else self.renderers[at] 957 958 objs = utils.flatten(objs) 959 for ob in objs: 960 if ob and ob not in self.objects: 961 self.objects.append(ob) 962 963 acts = self._scan_input_return_acts(objs) 964 965 for a in acts: 966 967 if ren: 968 if isinstance(a, vedo.addons.BaseCutter): 969 a.add_to(self) # from cutters 970 continue 971 972 if isinstance(a, vtki.vtkLight): 973 ren.AddLight(a) 974 continue 975 976 try: 977 ren.AddActor(a) 978 except TypeError: 979 ren.AddActor(a.actor) 980 981 try: 982 ir = self.renderers.index(ren) 983 a.rendered_at.add(ir) # might not have rendered_at 984 except (AttributeError, ValueError): 985 pass 986 987 if isinstance(a, vtki.vtkFollower): 988 a.SetCamera(self.camera) 989 elif isinstance(a, vedo.visual.LightKit): 990 a.lightkit.AddLightsToRenderer(ren) 991 992 return self 993 994 def remove(self, *objs, at=None) -> Self: 995 """ 996 Remove input object to the internal list of objects to be shown. 997 998 Objects to be removed can be referenced by their assigned name, 999 1000 Arguments: 1001 at : (int) 1002 remove the object at the specified renderer 1003 """ 1004 # TODO and you can also use wildcards like `*` and `?`. 1005 1006 ren = self.renderer if at is None else self.renderers[at] 1007 1008 objs = [ob for ob in utils.flatten(objs) if ob] 1009 1010 has_str = False 1011 for ob in objs: 1012 if isinstance(ob, str): 1013 has_str = True 1014 break 1015 1016 has_actor = False 1017 for ob in objs: 1018 if hasattr(ob, "actor") and ob.actor: 1019 has_actor = True 1020 break 1021 1022 if has_str or has_actor: 1023 # need to get the actors to search for 1024 for a in self.get_actors(include_non_pickables=True): 1025 # print("PARSING", [a]) 1026 try: 1027 if (a.name and a.name in objs) or a in objs: 1028 objs.append(a) 1029 # if a.name: 1030 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1031 # if any(bools) or a in objs: 1032 # objs.append(a) 1033 # print('a.name',a.name, objs,any(bools)) 1034 except AttributeError: # no .name 1035 # passing the actor so get back the object with .retrieve_object() 1036 try: 1037 vobj = a.retrieve_object() 1038 if (vobj.name and vobj.name in objs) or vobj in objs: 1039 # print('vobj.name', vobj.name) 1040 objs.append(vobj) 1041 except AttributeError: 1042 pass 1043 1044 if ren is None: 1045 return self 1046 ir = self.renderers.index(ren) 1047 1048 ids = [] 1049 for ob in set(objs): 1050 1051 # will remove it from internal list if possible 1052 try: 1053 idx = self.objects.index(ob) 1054 ids.append(idx) 1055 except ValueError: 1056 pass 1057 1058 if ren: ### remove it from the renderer 1059 1060 if isinstance(ob, vedo.addons.BaseCutter): 1061 ob.remove_from(self) # from cutters 1062 continue 1063 1064 try: 1065 ren.RemoveActor(ob) 1066 except TypeError: 1067 try: 1068 ren.RemoveActor(ob.actor) 1069 except AttributeError: 1070 pass 1071 1072 if hasattr(ob, "rendered_at"): 1073 ob.rendered_at.discard(ir) 1074 1075 if hasattr(ob, "scalarbar") and ob.scalarbar: 1076 ren.RemoveActor(ob.scalarbar) 1077 if hasattr(ob, "_caption") and ob._caption: 1078 ren.RemoveActor(ob._caption) 1079 if hasattr(ob, "shadows") and ob.shadows: 1080 for sha in ob.shadows: 1081 ren.RemoveActor(sha.actor) 1082 if hasattr(ob, "trail") and ob.trail: 1083 ren.RemoveActor(ob.trail.actor) 1084 ob.trail_points = [] 1085 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1086 for sha in ob.trail.shadows: 1087 ren.RemoveActor(sha.actor) 1088 1089 elif isinstance(ob, vedo.visual.LightKit): 1090 ob.lightkit.RemoveLightsFromRenderer(ren) 1091 1092 # for i in ids: # WRONG way of doing it! 1093 # del self.objects[i] 1094 # instead we do: 1095 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1096 return self 1097 1098 @property 1099 def actors(self): 1100 """Return the list of actors.""" 1101 return [ob.actor for ob in self.objects if hasattr(ob, "actor")] 1102 1103 def remove_lights(self) -> Self: 1104 """Remove all the present lights in the current renderer.""" 1105 if self.renderer: 1106 self.renderer.RemoveAllLights() 1107 return self 1108 1109 def pop(self, at=None) -> Self: 1110 """ 1111 Remove the last added object from the rendering window. 1112 This method is typically used in loops or callback functions. 1113 """ 1114 if at is not None and not isinstance(at, int): 1115 # wrong usage pitfall 1116 vedo.logger.error("argument of pop() must be an integer") 1117 raise RuntimeError() 1118 1119 if self.objects: 1120 self.remove(self.objects[-1], at) 1121 return self 1122 1123 def render(self, resetcam=False) -> Self: 1124 """Render the scene. This method is typically used in loops or callback functions.""" 1125 1126 if vedo.settings.dry_run_mode >= 2: 1127 return self 1128 1129 if not self.window: 1130 return self 1131 1132 self.initialize_interactor() 1133 1134 if resetcam: 1135 self.renderer.ResetCamera() 1136 1137 self.window.Render() 1138 1139 if self._cocoa_process_events and self.interactor and self.interactor.GetInitialized(): 1140 if "Darwin" in vedo.sys_platform and not self.offscreen: 1141 self.interactor.ProcessEvents() 1142 self._cocoa_process_events = False 1143 return self 1144 1145 def interactive(self) -> Self: 1146 """ 1147 Start window interaction. 1148 Analogous to `show(..., interactive=True)`. 1149 """ 1150 if vedo.settings.dry_run_mode >= 1: 1151 return self 1152 self.initialize_interactor() 1153 if self.interactor: 1154 # print("self.interactor.Start()") 1155 self.interactor.Start() 1156 # print("self.interactor.Start() done") 1157 if self._must_close_now: 1158 # print("self.interactor.TerminateApp()") 1159 if self.interactor: 1160 self.interactor.GetRenderWindow().Finalize() 1161 self.interactor.TerminateApp() 1162 self.interactor = None 1163 self.window = None 1164 self.renderer = None 1165 self.renderers = [] 1166 self.camera = None 1167 return self 1168 1169 def use_depth_peeling(self, at=None, value=True) -> Self: 1170 """ 1171 Specify whether use depth peeling algorithm at this specific renderer 1172 Call this method before the first rendering. 1173 """ 1174 ren = self.renderer if at is None else self.renderers[at] 1175 ren.SetUseDepthPeeling(value) 1176 return self 1177 1178 def background(self, c1=None, c2=None, at=None, mode=0) -> Union[Self, "np.ndarray"]: 1179 """Set the color of the background for the current renderer. 1180 A different renderer index can be specified by keyword `at`. 1181 1182 Arguments: 1183 c1 : (list) 1184 background main color. 1185 c2 : (list) 1186 background color for the upper part of the window. 1187 at : (int) 1188 renderer index. 1189 mode : (int) 1190 background mode (needs vtk version >= 9.3) 1191 0 = vertical, 1192 1 = horizontal, 1193 2 = radial farthest side, 1194 3 = radia farthest corner. 1195 """ 1196 if not self.renderers: 1197 return self 1198 r = self.renderer if at is None else self.renderers[at] 1199 1200 if c1 is None and c2 is None: 1201 return np.array(r.GetBackground()) 1202 1203 if r: 1204 if c1 is not None: 1205 r.SetBackground(vedo.get_color(c1)) 1206 if c2 is not None: 1207 r.GradientBackgroundOn() 1208 r.SetBackground2(vedo.get_color(c2)) 1209 if mode: 1210 try: # only works with vtk>=9.3 1211 modes = [ 1212 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1213 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1214 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1215 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1216 ] 1217 r.SetGradientMode(modes[mode]) 1218 except AttributeError: 1219 pass 1220 1221 else: 1222 r.GradientBackgroundOff() 1223 return self 1224 1225 ################################################################## 1226 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1227 """ 1228 Return a list of Meshes from the specified renderer. 1229 1230 Arguments: 1231 at : (int) 1232 specify which renderer to look at. 1233 include_non_pickables : (bool) 1234 include non-pickable objects 1235 unpack_assemblies : (bool) 1236 unpack assemblies into their components 1237 """ 1238 if at is None: 1239 renderer = self.renderer 1240 at = self.renderers.index(renderer) 1241 elif isinstance(at, int): 1242 renderer = self.renderers[at] 1243 1244 has_global_axes = False 1245 if isinstance(self.axes_instances[at], vedo.Assembly): 1246 has_global_axes = True 1247 1248 if unpack_assemblies: 1249 acs = renderer.GetActors() 1250 else: 1251 acs = renderer.GetViewProps() 1252 1253 objs = [] 1254 acs.InitTraversal() 1255 for _ in range(acs.GetNumberOfItems()): 1256 1257 if unpack_assemblies: 1258 a = acs.GetNextItem() 1259 else: 1260 a = acs.GetNextProp() 1261 1262 if isinstance(a, vtki.vtkVolume): 1263 continue 1264 1265 if include_non_pickables or a.GetPickable(): 1266 if a == self.axes_instances[at]: 1267 continue 1268 if has_global_axes and a in self.axes_instances[at].actors: 1269 continue 1270 try: 1271 objs.append(a.retrieve_object()) 1272 except AttributeError: 1273 pass 1274 return objs 1275 1276 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1277 """ 1278 Return a list of Volumes from the specified renderer. 1279 1280 Arguments: 1281 at : (int) 1282 specify which renderer to look at 1283 include_non_pickables : (bool) 1284 include non-pickable objects 1285 """ 1286 renderer = self.renderer if at is None else self.renderers[at] 1287 1288 vols = [] 1289 acs = renderer.GetVolumes() 1290 acs.InitTraversal() 1291 for _ in range(acs.GetNumberOfItems()): 1292 a = acs.GetNextItem() 1293 if include_non_pickables or a.GetPickable(): 1294 try: 1295 vols.append(a.retrieve_object()) 1296 except AttributeError: 1297 pass 1298 return vols 1299 1300 def get_actors(self, at=None, include_non_pickables=False) -> list: 1301 """ 1302 Return a list of Volumes from the specified renderer. 1303 1304 Arguments: 1305 at : (int) 1306 specify which renderer to look at 1307 include_non_pickables : (bool) 1308 include non-pickable objects 1309 """ 1310 renderer = self.renderer if at is None else self.renderers[at] 1311 if renderer is None: 1312 return [] 1313 1314 acts = [] 1315 acs = renderer.GetViewProps() 1316 acs.InitTraversal() 1317 for _ in range(acs.GetNumberOfItems()): 1318 a = acs.GetNextProp() 1319 if include_non_pickables or a.GetPickable(): 1320 acts.append(a) 1321 return acts 1322 1323 def check_actors_trasform(self, at=None) -> Self: 1324 """ 1325 Reset the transformation matrix of all actors at specified renderer. 1326 This is only useful when actors have been moved/rotated/scaled manually 1327 in an already rendered scene using interactors like 1328 'TrackballActor' or 'JoystickActor'. 1329 """ 1330 # see issue https://github.com/marcomusy/vedo/issues/1046 1331 for a in self.get_actors(at=at, include_non_pickables=True): 1332 try: 1333 M = a.GetMatrix() 1334 except AttributeError: 1335 continue 1336 if M and not M.IsIdentity(): 1337 try: 1338 a.retrieve_object().apply_transform_from_actor() 1339 # vedo.logger.info( 1340 # f"object '{a.retrieve_object().name}' " 1341 # "was manually moved. Updated to its current position." 1342 # ) 1343 except AttributeError: 1344 pass 1345 return self 1346 1347 def reset_camera(self, tight=None) -> Self: 1348 """ 1349 Reset the camera position and zooming. 1350 If tight (float) is specified the zooming reserves a padding space 1351 in the xy-plane expressed in percent of the average size. 1352 """ 1353 if tight is None: 1354 self.renderer.ResetCamera() 1355 else: 1356 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1357 cam = self.camera 1358 1359 self.renderer.ComputeAspect() 1360 aspect = self.renderer.GetAspect() 1361 angle = np.pi * cam.GetViewAngle() / 180.0 1362 dx = x1 - x0 1363 dy = y1 - y0 1364 dist = max(dx / aspect[0], dy) / np.tan(angle / 2) / 2 1365 1366 cam.SetViewUp(0, 1, 0) 1367 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1368 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1369 if cam.GetParallelProjection(): 1370 ps = max(dx / aspect[0], dy) / 2 1371 cam.SetParallelScale(ps * (1 + tight)) 1372 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1373 return self 1374 1375 def reset_clipping_range(self, bounds=None) -> Self: 1376 """ 1377 Reset the camera clipping range to include all visible actors. 1378 If bounds is given, it will be used instead of computing it. 1379 """ 1380 if bounds is None: 1381 self.renderer.ResetCameraClippingRange() 1382 else: 1383 self.renderer.ResetCameraClippingRange(bounds) 1384 return self 1385 1386 def reset_viewup(self, smooth=True) -> Self: 1387 """ 1388 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1389 """ 1390 vbb = addons.compute_visible_bounds()[0] 1391 x0, x1, y0, y1, z0, z1 = vbb 1392 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1393 d = self.camera.GetDistance() 1394 1395 viewups = np.array( 1396 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1397 ) 1398 positions = np.array( 1399 [ 1400 (mx, my, mz + d), 1401 (mx, my, mz - d), 1402 (mx, my + d, mz), 1403 (mx, my - d, mz), 1404 (mx + d, my, mz), 1405 (mx - d, my, mz), 1406 ] 1407 ) 1408 1409 vu = np.array(self.camera.GetViewUp()) 1410 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1411 1412 poc = np.array(self.camera.GetPosition()) 1413 foc = np.array(self.camera.GetFocalPoint()) 1414 a = poc - foc 1415 b = positions - foc 1416 a = a / np.linalg.norm(a) 1417 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1418 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1419 1420 if smooth: 1421 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1422 for t in outtimes: 1423 vv = vu * (1 - t) + viewups[vui] * t 1424 pp = poc * (1 - t) + positions[pui] * t 1425 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1426 self.camera.SetViewUp(vv) 1427 self.camera.SetPosition(pp) 1428 self.camera.SetFocalPoint(ff) 1429 self.render() 1430 1431 # interpolator does not respect parallel view...: 1432 # cam1 = dict( 1433 # pos=poc, 1434 # viewup=vu, 1435 # focal_point=(mx,my,mz), 1436 # clipping_range=self.camera.GetClippingRange() 1437 # ) 1438 # # cam1 = self.camera 1439 # cam2 = dict( 1440 # pos=positions[pui], 1441 # viewup=viewups[vui], 1442 # focal_point=(mx,my,mz), 1443 # clipping_range=self.camera.GetClippingRange() 1444 # ) 1445 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1446 # for c in vcams: 1447 # self.renderer.SetActiveCamera(c) 1448 # self.render() 1449 else: 1450 1451 self.camera.SetViewUp(viewups[vui]) 1452 self.camera.SetPosition(positions[pui]) 1453 self.camera.SetFocalPoint(mx, my, mz) 1454 1455 self.renderer.ResetCameraClippingRange() 1456 1457 # vbb, _, _, _ = addons.compute_visible_bounds() 1458 # x0,x1, y0,y1, z0,z1 = vbb 1459 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1460 self.render() 1461 return self 1462 1463 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1464 """ 1465 Takes as input two cameras set camera at an interpolated position: 1466 1467 Cameras can be vtkCamera or dictionaries in format: 1468 1469 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1470 1471 Press `shift-C` key in interactive mode to dump a python snipplet 1472 of parameters for the current camera view. 1473 """ 1474 nc = len(cameras) 1475 if len(times) == 0: 1476 times = np.linspace(0, 1, num=nc, endpoint=True) 1477 1478 assert len(times) == nc 1479 1480 cin = vtki.new("CameraInterpolator") 1481 1482 # cin.SetInterpolationTypeToLinear() # buggy? 1483 if nc > 2 and smooth: 1484 cin.SetInterpolationTypeToSpline() 1485 1486 for i, cam in enumerate(cameras): 1487 vcam = cam 1488 if isinstance(cam, dict): 1489 vcam = utils.camera_from_dict(cam) 1490 cin.AddCamera(times[i], vcam) 1491 1492 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1493 rng = maxt - mint 1494 1495 if len(output_times) == 0: 1496 cin.InterpolateCamera(t * rng, self.camera) 1497 return [self.camera] 1498 else: 1499 vcams = [] 1500 for tt in output_times: 1501 c = vtki.vtkCamera() 1502 cin.InterpolateCamera(tt * rng, c) 1503 vcams.append(c) 1504 return vcams 1505 1506 def fly_to(self, point) -> Self: 1507 """ 1508 Fly camera to the specified point. 1509 1510 Arguments: 1511 point : (list) 1512 point in space to place camera. 1513 1514 Example: 1515 ```python 1516 from vedo import * 1517 cone = Cone() 1518 plt = Plotter(axes=1) 1519 plt.show(cone) 1520 plt.fly_to([1,0,0]) 1521 plt.interactive().close() 1522 ``` 1523 """ 1524 if self.interactor: 1525 self.resetcam = False 1526 self.interactor.FlyTo(self.renderer, point) 1527 return self 1528 1529 def look_at(self, plane="xy") -> Self: 1530 """Move the camera so that it looks at the specified cartesian plane""" 1531 cam = self.renderer.GetActiveCamera() 1532 fp = np.array(cam.GetFocalPoint()) 1533 p = np.array(cam.GetPosition()) 1534 dist = np.linalg.norm(fp - p) 1535 plane = plane.lower() 1536 if "x" in plane and "y" in plane: 1537 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1538 cam.SetViewUp(0.0, 1.0, 0.0) 1539 elif "x" in plane and "z" in plane: 1540 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1541 cam.SetViewUp(0.0, 0.0, 1.0) 1542 elif "y" in plane and "z" in plane: 1543 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1544 cam.SetViewUp(0.0, 0.0, 1.0) 1545 else: 1546 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1547 return self 1548 1549 def record(self, filename="") -> str: 1550 """ 1551 Record camera, mouse, keystrokes and all other events. 1552 Recording can be toggled on/off by pressing key "R". 1553 1554 Arguments: 1555 filename : (str) 1556 ascii file to store events. 1557 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1558 1559 Returns: 1560 a string descriptor of events. 1561 1562 Examples: 1563 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1564 """ 1565 if vedo.settings.dry_run_mode >= 1: 1566 return "" 1567 if not self.interactor: 1568 vedo.logger.warning("Cannot record events, no interactor defined.") 1569 return "" 1570 erec = vtki.new("InteractorEventRecorder") 1571 erec.SetInteractor(self.interactor) 1572 if not filename: 1573 if not os.path.exists(vedo.settings.cache_directory): 1574 os.makedirs(vedo.settings.cache_directory) 1575 home_dir = os.path.expanduser("~") 1576 filename = os.path.join( 1577 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1578 print("Events will be recorded in", filename) 1579 erec.SetFileName(filename) 1580 erec.SetKeyPressActivationValue("R") 1581 erec.EnabledOn() 1582 erec.Record() 1583 self.interactor.Start() 1584 erec.Stop() 1585 erec.EnabledOff() 1586 with open(filename, "r", encoding="UTF-8") as fl: 1587 events = fl.read() 1588 erec = None 1589 return events 1590 1591 def play(self, recorded_events="", repeats=0) -> Self: 1592 """ 1593 Play camera, mouse, keystrokes and all other events. 1594 1595 Arguments: 1596 events : (str) 1597 file o string of events. 1598 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1599 repeats : (int) 1600 number of extra repeats of the same events. The default is 0. 1601 1602 Examples: 1603 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1604 """ 1605 if vedo.settings.dry_run_mode >= 1: 1606 return self 1607 if not self.interactor: 1608 vedo.logger.warning("Cannot play events, no interactor defined.") 1609 return self 1610 1611 erec = vtki.new("InteractorEventRecorder") 1612 erec.SetInteractor(self.interactor) 1613 1614 if not recorded_events: 1615 home_dir = os.path.expanduser("~") 1616 recorded_events = os.path.join( 1617 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1618 1619 if recorded_events.endswith(".log"): 1620 erec.ReadFromInputStringOff() 1621 erec.SetFileName(recorded_events) 1622 else: 1623 erec.ReadFromInputStringOn() 1624 erec.SetInputString(recorded_events) 1625 1626 erec.Play() 1627 for _ in range(repeats): 1628 erec.Rewind() 1629 erec.Play() 1630 erec.EnabledOff() 1631 erec = None 1632 return self 1633 1634 def parallel_projection(self, value=True, at=None) -> Self: 1635 """ 1636 Use parallel projection `at` a specified renderer. 1637 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1638 An input value equal to -1 will toggle it on/off. 1639 """ 1640 r = self.renderer if at is None else self.renderers[at] 1641 1642 if value == -1: 1643 val = r.GetActiveCamera().GetParallelProjection() 1644 value = not val 1645 r.GetActiveCamera().SetParallelProjection(value) 1646 r.Modified() 1647 return self 1648 1649 def render_hidden_lines(self, value=True) -> Self: 1650 """Remove hidden lines when in wireframe mode.""" 1651 self.renderer.SetUseHiddenLineRemoval(not value) 1652 return self 1653 1654 def fov(self, angle: float) -> Self: 1655 """ 1656 Set the field of view angle for the camera. 1657 This is the angle of the camera frustum in the horizontal direction. 1658 High values will result in a wide-angle lens (fish-eye effect), 1659 and low values will result in a telephoto lens. 1660 1661 Default value is 30 degrees. 1662 """ 1663 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1664 self.renderer.GetActiveCamera().SetViewAngle(angle) 1665 return self 1666 1667 def zoom(self, zoom: float) -> Self: 1668 """Apply a zooming factor for the current camera view""" 1669 self.renderer.GetActiveCamera().Zoom(zoom) 1670 return self 1671 1672 def azimuth(self, angle: float) -> Self: 1673 """Rotate camera around the view up vector.""" 1674 self.renderer.GetActiveCamera().Azimuth(angle) 1675 return self 1676 1677 def elevation(self, angle: float) -> Self: 1678 """Rotate the camera around the cross product of the negative 1679 of the direction of projection and the view up vector.""" 1680 self.renderer.GetActiveCamera().Elevation(angle) 1681 return self 1682 1683 def roll(self, angle: float) -> Self: 1684 """Roll the camera about the direction of projection.""" 1685 self.renderer.GetActiveCamera().Roll(angle) 1686 return self 1687 1688 def dolly(self, value: float) -> Self: 1689 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1690 self.renderer.GetActiveCamera().Dolly(value) 1691 return self 1692 1693 ################################################################## 1694 def add_slider( 1695 self, 1696 sliderfunc, 1697 xmin, 1698 xmax, 1699 value=None, 1700 pos=4, 1701 title="", 1702 font="Calco", 1703 title_size=1, 1704 c=None, 1705 alpha=1, 1706 show_value=True, 1707 delayed=False, 1708 **options, 1709 ) -> "vedo.addons.Slider2D": 1710 """ 1711 Add a `vedo.addons.Slider2D` which can call an external custom function. 1712 1713 Arguments: 1714 sliderfunc : (Callable) 1715 external function to be called by the widget 1716 xmin : (float) 1717 lower value of the slider 1718 xmax : (float) 1719 upper value 1720 value : (float) 1721 current value 1722 pos : (list, str) 1723 position corner number: horizontal [1-5] or vertical [11-15] 1724 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1725 and also by a string descriptor (eg. "bottom-left") 1726 title : (str) 1727 title text 1728 font : (str) 1729 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1730 title_size : (float) 1731 title text scale [1.0] 1732 show_value : (bool) 1733 if True current value is shown 1734 delayed : (bool) 1735 if True the callback is delayed until when the mouse button is released 1736 alpha : (float) 1737 opacity of the scalar bar texts 1738 slider_length : (float) 1739 slider length 1740 slider_width : (float) 1741 slider width 1742 end_cap_length : (float) 1743 length of the end cap 1744 end_cap_width : (float) 1745 width of the end cap 1746 tube_width : (float) 1747 width of the tube 1748 title_height : (float) 1749 width of the title 1750 tformat : (str) 1751 format of the title 1752 1753 Examples: 1754 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1755 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1756 1757  1758 """ 1759 if c is None: # automatic black or white 1760 c = (0.8, 0.8, 0.8) 1761 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1762 c = (0.2, 0.2, 0.2) 1763 else: 1764 c = vedo.get_color(c) 1765 1766 slider2d = addons.Slider2D( 1767 sliderfunc, 1768 xmin, 1769 xmax, 1770 value, 1771 pos, 1772 title, 1773 font, 1774 title_size, 1775 c, 1776 alpha, 1777 show_value, 1778 delayed, 1779 **options, 1780 ) 1781 1782 if self.renderer: 1783 slider2d.renderer = self.renderer 1784 if self.interactor: 1785 slider2d.interactor = self.interactor 1786 slider2d.on() 1787 self.sliders.append([slider2d, sliderfunc]) 1788 return slider2d 1789 1790 def add_slider3d( 1791 self, 1792 sliderfunc, 1793 pos1, 1794 pos2, 1795 xmin, 1796 xmax, 1797 value=None, 1798 s=0.03, 1799 t=1, 1800 title="", 1801 rotation=0.0, 1802 c=None, 1803 show_value=True, 1804 ) -> "vedo.addons.Slider3D": 1805 """ 1806 Add a 3D slider widget which can call an external custom function. 1807 1808 Arguments: 1809 sliderfunc : (function) 1810 external function to be called by the widget 1811 pos1 : (list) 1812 first position 3D coordinates 1813 pos2 : (list) 1814 second position coordinates 1815 xmin : (float) 1816 lower value 1817 xmax : (float) 1818 upper value 1819 value : (float) 1820 initial value 1821 s : (float) 1822 label scaling factor 1823 t : (float) 1824 tube scaling factor 1825 title : (str) 1826 title text 1827 c : (color) 1828 slider color 1829 rotation : (float) 1830 title rotation around slider axis 1831 show_value : (bool) 1832 if True current value is shown 1833 1834 Examples: 1835 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1836 1837  1838 """ 1839 if c is None: # automatic black or white 1840 c = (0.8, 0.8, 0.8) 1841 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1842 c = (0.2, 0.2, 0.2) 1843 else: 1844 c = vedo.get_color(c) 1845 1846 slider3d = addons.Slider3D( 1847 sliderfunc, 1848 pos1, 1849 pos2, 1850 xmin, 1851 xmax, 1852 value, 1853 s, 1854 t, 1855 title, 1856 rotation, 1857 c, 1858 show_value, 1859 ) 1860 slider3d.renderer = self.renderer 1861 slider3d.interactor = self.interactor 1862 slider3d.on() 1863 self.sliders.append([slider3d, sliderfunc]) 1864 return slider3d 1865 1866 def add_button( 1867 self, 1868 fnc=None, 1869 states=("On", "Off"), 1870 c=("w", "w"), 1871 bc=("green4", "red4"), 1872 pos=(0.7, 0.1), 1873 size=24, 1874 font="Courier", 1875 bold=True, 1876 italic=False, 1877 alpha=1, 1878 angle=0, 1879 ) -> Union["vedo.addons.Button", None]: 1880 """ 1881 Add a button to the renderer window. 1882 1883 Arguments: 1884 states : (list) 1885 a list of possible states, e.g. ['On', 'Off'] 1886 c : (list) 1887 a list of colors for each state 1888 bc : (list) 1889 a list of background colors for each state 1890 pos : (list) 1891 2D position from left-bottom corner 1892 size : (float) 1893 size of button font 1894 font : (str) 1895 font type. Check [available fonts here](https://vedo.embl.es/fonts). 1896 bold : (bool) 1897 bold font face (False) 1898 italic : (bool) 1899 italic font face (False) 1900 alpha : (float) 1901 opacity level 1902 angle : (float) 1903 anticlockwise rotation in degrees 1904 1905 Returns: 1906 `vedo.addons.Button` object. 1907 1908 Examples: 1909 - [buttons1.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons1.py) 1910 - [buttons2.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons2.py) 1911 1912  1913 """ 1914 if self.interactor: 1915 bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle) 1916 self.renderer.AddActor2D(bu) 1917 self.buttons.append(bu) 1918 # bu.function_id = self.add_callback("LeftButtonPress", bu.function) # not good 1919 bu.function_id = bu.add_observer("pick", bu.function, priority=10) 1920 return bu 1921 return None 1922 1923 def add_spline_tool( 1924 self, 1925 points, 1926 pc="k", 1927 ps=8, 1928 lc="r4", 1929 ac="g5", 1930 lw=2, 1931 alpha=1, 1932 closed=False, 1933 ontop=True, 1934 can_add_nodes=True, 1935 ) -> "vedo.addons.SplineTool": 1936 """ 1937 Add a spline tool to the current plotter. 1938 Nodes of the spline can be dragged in space with the mouse. 1939 Clicking on the line itself adds an extra point. 1940 Selecting a point and pressing del removes it. 1941 1942 Arguments: 1943 points : (Mesh, Points, array) 1944 the set of coordinates forming the spline nodes. 1945 pc : (str) 1946 point color. The default is 'k'. 1947 ps : (str) 1948 point size. The default is 8. 1949 lc : (str) 1950 line color. The default is 'r4'. 1951 ac : (str) 1952 active point marker color. The default is 'g5'. 1953 lw : (int) 1954 line width. The default is 2. 1955 alpha : (float) 1956 line transparency. 1957 closed : (bool) 1958 spline is meant to be closed. The default is False. 1959 1960 Returns: 1961 a `SplineTool` object. 1962 1963 Examples: 1964 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1965 1966  1967 """ 1968 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1969 sw.interactor = self.interactor 1970 sw.on() 1971 sw.Initialize(sw.points.dataset) 1972 sw.representation.SetRenderer(self.renderer) 1973 sw.representation.SetClosedLoop(closed) 1974 sw.representation.BuildRepresentation() 1975 self.widgets.append(sw) 1976 return sw 1977 1978 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1979 """Add an inset icon mesh into the same renderer. 1980 1981 Arguments: 1982 pos : (int, list) 1983 icon position in the range [1-4] indicating one of the 4 corners, 1984 or it can be a tuple (x,y) as a fraction of the renderer size. 1985 size : (float) 1986 size of the square inset. 1987 1988 Examples: 1989 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1990 """ 1991 iconw = addons.Icon(icon, pos, size) 1992 1993 iconw.SetInteractor(self.interactor) 1994 iconw.EnabledOn() 1995 iconw.InteractiveOff() 1996 self.widgets.append(iconw) 1997 return iconw 1998 1999 def add_global_axes(self, axtype=None, c=None) -> Self: 2000 """Draw axes on scene. Available axes types: 2001 2002 Arguments: 2003 axtype : (int) 2004 - 0, no axes, 2005 - 1, draw three gray grid walls 2006 - 2, show cartesian axes from (0,0,0) 2007 - 3, show positive range of cartesian axes from (0,0,0) 2008 - 4, show a triad at bottom left 2009 - 5, show a cube at bottom left 2010 - 6, mark the corners of the bounding box 2011 - 7, draw a 3D ruler at each side of the cartesian axes 2012 - 8, show the vtkCubeAxesActor object 2013 - 9, show the bounding box outLine 2014 - 10, show three circles representing the maximum bounding box 2015 - 11, show a large grid on the x-y plane 2016 - 12, show polar axes 2017 - 13, draw a simple ruler at the bottom of the window 2018 2019 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 2020 2021 Example: 2022 ```python 2023 from vedo import Box, show 2024 b = Box(pos=(0, 0, 0), size=(80, 90, 70)).alpha(0.1) 2025 show( 2026 b, 2027 axes={ 2028 "xtitle": "Some long variable [a.u.]", 2029 "number_of_divisions": 4, 2030 # ... 2031 }, 2032 ) 2033 ``` 2034 2035 Examples: 2036 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2037 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2038 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2039 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2040 2041 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2042 """ 2043 addons.add_global_axes(axtype, c) 2044 return self 2045 2046 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2047 """Add a legend to the top right. 2048 2049 Examples: 2050 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/legendbox.py), 2051 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/other/flag_labels1.py) 2052 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/other/flag_labels2.py) 2053 """ 2054 acts = self.get_meshes() 2055 lb = addons.LegendBox(acts, **kwargs) 2056 self.add(lb) 2057 return lb 2058 2059 def add_hint( 2060 self, 2061 obj, 2062 text="", 2063 c="k", 2064 bg="yellow9", 2065 font="Calco", 2066 size=18, 2067 justify=0, 2068 angle=0, 2069 delay=250, 2070 ) -> Union[vtki.vtkBalloonWidget, None]: 2071 """ 2072 Create a pop-up hint style message when hovering an object. 2073 Use `add_hint(obj, False)` to disable a hinting a specific object. 2074 Use `add_hint(None)` to disable all hints. 2075 2076 Arguments: 2077 obj : (Mesh, Points) 2078 the object to associate the pop-up to 2079 text : (str) 2080 string description of the pop-up 2081 delay : (int) 2082 milliseconds to wait before pop-up occurs 2083 """ 2084 if self.offscreen or not self.interactor: 2085 return None 2086 2087 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2088 # Linux vtk9.0 is bugged 2089 vedo.logger.warning( 2090 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2091 ) 2092 return None 2093 2094 if obj is None: 2095 self.hint_widget.EnabledOff() 2096 self.hint_widget.SetInteractor(None) 2097 self.hint_widget = None 2098 return self.hint_widget 2099 2100 if text is False and self.hint_widget: 2101 self.hint_widget.RemoveBalloon(obj) 2102 return self.hint_widget 2103 2104 if text == "": 2105 if obj.name: 2106 text = obj.name 2107 elif obj.filename: 2108 text = obj.filename 2109 else: 2110 return None 2111 2112 if not self.hint_widget: 2113 self.hint_widget = vtki.vtkBalloonWidget() 2114 2115 rep = self.hint_widget.GetRepresentation() 2116 rep.SetBalloonLayoutToImageRight() 2117 2118 trep = rep.GetTextProperty() 2119 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2120 trep.SetFontFile(utils.get_font_path(font)) 2121 trep.SetFontSize(size) 2122 trep.SetColor(vedo.get_color(c)) 2123 trep.SetBackgroundColor(vedo.get_color(bg)) 2124 trep.SetShadow(0) 2125 trep.SetJustification(justify) 2126 trep.UseTightBoundingBoxOn() 2127 2128 self.hint_widget.ManagesCursorOff() 2129 self.hint_widget.SetTimerDuration(delay) 2130 self.hint_widget.SetInteractor(self.interactor) 2131 if angle: 2132 trep.SetOrientation(angle) 2133 trep.SetBackgroundOpacity(0) 2134 # else: 2135 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2136 self.hint_widget.SetRepresentation(rep) 2137 self.widgets.append(self.hint_widget) 2138 self.hint_widget.EnabledOn() 2139 2140 bst = self.hint_widget.GetBalloonString(obj.actor) 2141 if bst: 2142 self.hint_widget.UpdateBalloonString(obj.actor, text) 2143 else: 2144 self.hint_widget.AddBalloon(obj.actor, text) 2145 2146 return self.hint_widget 2147 2148 def add_shadows(self) -> Self: 2149 """Add shadows at the current renderer.""" 2150 if self.renderer: 2151 shadows = vtki.new("ShadowMapPass") 2152 seq = vtki.new("SequencePass") 2153 passes = vtki.new("RenderPassCollection") 2154 passes.AddItem(shadows.GetShadowMapBakerPass()) 2155 passes.AddItem(shadows) 2156 seq.SetPasses(passes) 2157 camerapass = vtki.new("CameraPass") 2158 camerapass.SetDelegatePass(seq) 2159 self.renderer.SetPass(camerapass) 2160 return self 2161 2162 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> Self: 2163 """ 2164 Screen Space Ambient Occlusion. 2165 2166 For every pixel on the screen, the pixel shader samples the depth values around 2167 the current pixel and tries to compute the amount of occlusion from each of the sampled 2168 points. 2169 2170 Arguments: 2171 radius : (float) 2172 radius of influence in absolute units 2173 bias : (float) 2174 bias of the normals 2175 blur : (bool) 2176 add a blurring to the sampled positions 2177 samples : (int) 2178 number of samples to probe 2179 2180 Examples: 2181 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2182 2183  2184 """ 2185 lights = vtki.new("LightsPass") 2186 2187 opaque = vtki.new("OpaquePass") 2188 2189 ssaoCam = vtki.new("CameraPass") 2190 ssaoCam.SetDelegatePass(opaque) 2191 2192 ssao = vtki.new("SSAOPass") 2193 ssao.SetRadius(radius) 2194 ssao.SetBias(bias) 2195 ssao.SetBlur(blur) 2196 ssao.SetKernelSize(samples) 2197 ssao.SetDelegatePass(ssaoCam) 2198 2199 translucent = vtki.new("TranslucentPass") 2200 2201 volpass = vtki.new("VolumetricPass") 2202 ddp = vtki.new("DualDepthPeelingPass") 2203 ddp.SetTranslucentPass(translucent) 2204 ddp.SetVolumetricPass(volpass) 2205 2206 over = vtki.new("OverlayPass") 2207 2208 collection = vtki.new("RenderPassCollection") 2209 collection.AddItem(lights) 2210 collection.AddItem(ssao) 2211 collection.AddItem(ddp) 2212 collection.AddItem(over) 2213 2214 sequence = vtki.new("SequencePass") 2215 sequence.SetPasses(collection) 2216 2217 cam = vtki.new("CameraPass") 2218 cam.SetDelegatePass(sequence) 2219 2220 self.renderer.SetPass(cam) 2221 return self 2222 2223 def add_depth_of_field(self, autofocus=True) -> Self: 2224 """Add a depth of field effect in the scene.""" 2225 lights = vtki.new("LightsPass") 2226 2227 opaque = vtki.new("OpaquePass") 2228 2229 dofCam = vtki.new("CameraPass") 2230 dofCam.SetDelegatePass(opaque) 2231 2232 dof = vtki.new("DepthOfFieldPass") 2233 dof.SetAutomaticFocalDistance(autofocus) 2234 dof.SetDelegatePass(dofCam) 2235 2236 collection = vtki.new("RenderPassCollection") 2237 collection.AddItem(lights) 2238 collection.AddItem(dof) 2239 2240 sequence = vtki.new("SequencePass") 2241 sequence.SetPasses(collection) 2242 2243 cam = vtki.new("CameraPass") 2244 cam.SetDelegatePass(sequence) 2245 2246 self.renderer.SetPass(cam) 2247 return self 2248 2249 def _add_skybox(self, hdrfile: str) -> Self: 2250 # many hdr files are at https://polyhaven.com/all 2251 2252 reader = vtki.new("HDRReader") 2253 # Check the image can be read. 2254 if not reader.CanReadFile(hdrfile): 2255 vedo.logger.error(f"Cannot read HDR file {hdrfile}") 2256 return self 2257 reader.SetFileName(hdrfile) 2258 reader.Update() 2259 2260 texture = vtki.vtkTexture() 2261 texture.SetColorModeToDirectScalars() 2262 texture.SetInputData(reader.GetOutput()) 2263 2264 # Convert to a cube map 2265 tcm = vtki.new("EquirectangularToCubeMapTexture") 2266 tcm.SetInputTexture(texture) 2267 # Enable mipmapping to handle HDR image 2268 tcm.MipmapOn() 2269 tcm.InterpolateOn() 2270 2271 self.renderer.SetEnvironmentTexture(tcm) 2272 self.renderer.UseImageBasedLightingOn() 2273 self.skybox = vtki.new("Skybox") 2274 self.skybox.SetTexture(tcm) 2275 self.renderer.AddActor(self.skybox) 2276 return self 2277 2278 def add_renderer_frame(self, 2279 c=None, alpha=None, lw=None, 2280 padding=None, pattern="brtl") -> "vedo.addons.RendererFrame": 2281 """ 2282 Add a frame to the renderer subwindow. 2283 2284 Arguments: 2285 c : (color) 2286 color name or index 2287 alpha : (float) 2288 opacity level 2289 lw : (int) 2290 line width in pixels. 2291 padding : (float) 2292 padding space in pixels. 2293 pattern : (str) 2294 a string made of characters 'b', 'r', 't', 'l' 2295 to show the frame line at the bottom, right, top, left. 2296 """ 2297 if c is None: # automatic black or white 2298 c = (0.9, 0.9, 0.9) 2299 if self.renderer: 2300 if np.sum(self.renderer.GetBackground()) > 1.5: 2301 c = (0.1, 0.1, 0.1) 2302 renf = addons.RendererFrame(c, alpha, lw, padding, pattern) 2303 if renf: 2304 self.renderer.AddActor(renf) 2305 return renf 2306 2307 def add_hover_legend( 2308 self, 2309 at=None, 2310 c=None, 2311 pos="bottom-left", 2312 font="Calco", 2313 s=0.75, 2314 bg="auto", 2315 alpha=0.1, 2316 maxlength=24, 2317 use_info=False, 2318 ) -> int: 2319 """ 2320 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2321 2322 The created text object are stored in `plotter.hover_legends`. 2323 2324 Returns: 2325 the id of the callback function. 2326 2327 Arguments: 2328 c : (color) 2329 Text color. If None then black or white is chosen automatically 2330 pos : (str) 2331 text positioning 2332 font : (str) 2333 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2334 s : (float) 2335 text size scale 2336 bg : (color) 2337 background color of the 2D box containing the text 2338 alpha : (float) 2339 box transparency 2340 maxlength : (int) 2341 maximum number of characters per line 2342 use_info : (bool) 2343 visualize the content of the `obj.info` attribute 2344 2345 Examples: 2346 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2347 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2348 2349  2350 """ 2351 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2352 2353 if at is None: 2354 at = self.renderers.index(self.renderer) 2355 2356 def _legfunc(evt): 2357 if not evt.object or not self.renderer or at != evt.at: 2358 if hoverlegend.mapper.GetInput(): # clear and return 2359 hoverlegend.mapper.SetInput("") 2360 self.render() 2361 return 2362 2363 if use_info: 2364 if hasattr(evt.object, "info"): 2365 t = str(evt.object.info) 2366 else: 2367 return 2368 else: 2369 t, tp = "", "" 2370 if evt.isMesh: 2371 tp = "Mesh " 2372 elif evt.isPoints: 2373 tp = "Points " 2374 elif evt.isVolume: 2375 tp = "Volume " 2376 elif evt.isImage: 2377 tp = "Image " 2378 elif evt.isAssembly: 2379 tp = "Assembly " 2380 else: 2381 return 2382 2383 if evt.isAssembly: 2384 if not evt.object.name: 2385 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2386 else: 2387 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2388 else: 2389 if evt.object.name: 2390 t += f"{tp}name" 2391 if evt.isPoints: 2392 t += " " 2393 if evt.isMesh: 2394 t += " " 2395 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2396 2397 if evt.object.filename: 2398 t += f"{tp}filename: " 2399 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2400 t += "\n" 2401 if not evt.object.file_size: 2402 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2403 if evt.object.file_size: 2404 t += " : " 2405 sz, created = evt.object.file_size, evt.object.created 2406 t += f"{created[4:-5]} ({sz})" + "\n" 2407 2408 if evt.isPoints: 2409 indata = evt.object.dataset 2410 if indata.GetNumberOfPoints(): 2411 t += ( 2412 f"#points/cells: {indata.GetNumberOfPoints()}" 2413 f" / {indata.GetNumberOfCells()}" 2414 ) 2415 pdata = indata.GetPointData() 2416 cdata = indata.GetCellData() 2417 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2418 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2419 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2420 t += " *" 2421 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2422 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2423 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2424 t += " *" 2425 2426 if evt.isImage: 2427 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2428 t += f"\nImage shape: {evt.object.shape}" 2429 pcol = self.color_picker(evt.picked2d) 2430 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2431 2432 # change box color if needed in 'auto' mode 2433 if evt.isPoints and "auto" in str(bg): 2434 actcol = evt.object.properties.GetColor() 2435 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2436 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2437 2438 # adapt to changes in bg color 2439 bgcol = self.renderers[at].GetBackground() 2440 _bgcol = c 2441 if _bgcol is None: # automatic black or white 2442 _bgcol = (0.9, 0.9, 0.9) 2443 if sum(bgcol) > 1.5: 2444 _bgcol = (0.1, 0.1, 0.1) 2445 if len(set(_bgcol).intersection(bgcol)) < 3: 2446 hoverlegend.color(_bgcol) 2447 2448 if hoverlegend.mapper.GetInput() != t: 2449 hoverlegend.mapper.SetInput(t) 2450 self.interactor.Render() 2451 2452 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2453 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2454 2455 self.add(hoverlegend, at=at) 2456 self.hover_legends.append(hoverlegend) 2457 idcall = self.add_callback("MouseMove", _legfunc) 2458 return idcall 2459 2460 def add_scale_indicator( 2461 self, 2462 pos=(0.7, 0.05), 2463 s=0.02, 2464 length=2, 2465 lw=4, 2466 c="k1", 2467 alpha=1, 2468 units="", 2469 gap=0.05, 2470 ) -> Union["vedo.visual.Actor2D", None]: 2471 """ 2472 Add a Scale Indicator. Only works in parallel mode (no perspective). 2473 2474 Arguments: 2475 pos : (list) 2476 fractional (x,y) position on the screen. 2477 s : (float) 2478 size of the text. 2479 length : (float) 2480 length of the line. 2481 units : (str) 2482 string to show units. 2483 gap : (float) 2484 separation of line and text. 2485 2486 Example: 2487 ```python 2488 from vedo import settings, Cube, Plotter 2489 settings.use_parallel_projection = True # or else it does not make sense! 2490 cube = Cube().alpha(0.2) 2491 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2492 plt.add_scale_indicator(units='um', c='blue4') 2493 plt.show(cube, "Scale indicator with units").close() 2494 ``` 2495  2496 """ 2497 # Note that this cannot go in addons.py 2498 # because it needs callbacks and window size 2499 if not self.interactor: 2500 return None 2501 2502 ppoints = vtki.vtkPoints() # Generate the polyline 2503 psqr = [[0.0, gap], [length / 10, gap]] 2504 dd = psqr[1][0] - psqr[0][0] 2505 for i, pt in enumerate(psqr): 2506 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2507 lines = vtki.vtkCellArray() 2508 lines.InsertNextCell(len(psqr)) 2509 for i in range(len(psqr)): 2510 lines.InsertCellPoint(i) 2511 pd = vtki.vtkPolyData() 2512 pd.SetPoints(ppoints) 2513 pd.SetLines(lines) 2514 2515 wsx, wsy = self.window.GetSize() 2516 if not self.camera.GetParallelProjection(): 2517 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2518 return None 2519 2520 rlabel = vtki.new("VectorText") 2521 rlabel.SetText("scale") 2522 tf = vtki.new("TransformPolyDataFilter") 2523 tf.SetInputConnection(rlabel.GetOutputPort()) 2524 t = vtki.vtkTransform() 2525 t.Scale(s * wsy / wsx, s, 1) 2526 tf.SetTransform(t) 2527 2528 app = vtki.new("AppendPolyData") 2529 app.AddInputConnection(tf.GetOutputPort()) 2530 app.AddInputData(pd) 2531 2532 mapper = vtki.new("PolyDataMapper2D") 2533 mapper.SetInputConnection(app.GetOutputPort()) 2534 cs = vtki.vtkCoordinate() 2535 cs.SetCoordinateSystem(1) 2536 mapper.SetTransformCoordinate(cs) 2537 2538 fractor = vedo.visual.Actor2D() 2539 csys = fractor.GetPositionCoordinate() 2540 csys.SetCoordinateSystem(3) 2541 fractor.SetPosition(pos) 2542 fractor.SetMapper(mapper) 2543 fractor.GetProperty().SetColor(vedo.get_color(c)) 2544 fractor.GetProperty().SetOpacity(alpha) 2545 fractor.GetProperty().SetLineWidth(lw) 2546 fractor.GetProperty().SetDisplayLocationToForeground() 2547 2548 def sifunc(iren, ev): 2549 wsx, wsy = self.window.GetSize() 2550 ps = self.camera.GetParallelScale() 2551 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2552 if units: 2553 newtxt += " " + units 2554 if rlabel.GetText() != newtxt: 2555 rlabel.SetText(newtxt) 2556 2557 self.renderer.AddActor(fractor) 2558 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2559 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2560 self.interactor.AddObserver("InteractionEvent", sifunc) 2561 sifunc(0, 0) 2562 return fractor 2563 2564 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2565 """ 2566 Create an Event object with information of what was clicked. 2567 2568 If `enable_picking` is False, no picking will be performed. 2569 This can be useful to avoid double picking when using buttons. 2570 """ 2571 if not self.interactor: 2572 return Event() 2573 2574 if len(pos) > 0: 2575 x, y = pos 2576 self.interactor.SetEventPosition(pos) 2577 else: 2578 x, y = self.interactor.GetEventPosition() 2579 self.renderer = self.interactor.FindPokedRenderer(x, y) 2580 2581 self.picked2d = (x, y) 2582 2583 key = self.interactor.GetKeySym() 2584 2585 if key: 2586 if "_L" in key or "_R" in key: 2587 # skip things like Shift_R 2588 key = "" # better than None 2589 else: 2590 if self.interactor.GetShiftKey(): 2591 key = key.upper() 2592 2593 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2594 key = "underscore" 2595 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2596 key = "plus" 2597 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2598 key = "?" 2599 2600 if self.interactor.GetControlKey(): 2601 key = "Ctrl+" + key 2602 2603 if self.interactor.GetAltKey(): 2604 key = "Alt+" + key 2605 2606 if enable_picking: 2607 if not self.picker: 2608 self.picker = vtki.vtkPropPicker() 2609 2610 self.picker.PickProp(x, y, self.renderer) 2611 actor = self.picker.GetProp3D() 2612 # Note that GetProp3D already picks Assembly 2613 2614 xp, yp = self.interactor.GetLastEventPosition() 2615 dx, dy = x - xp, y - yp 2616 2617 delta3d = np.array([0, 0, 0]) 2618 2619 if actor: 2620 picked3d = np.array(self.picker.GetPickPosition()) 2621 2622 try: 2623 vobj = actor.retrieve_object() 2624 old_pt = np.asarray(vobj.picked3d) 2625 vobj.picked3d = picked3d 2626 delta3d = picked3d - old_pt 2627 except (AttributeError, TypeError): 2628 pass 2629 2630 else: 2631 picked3d = None 2632 2633 if not actor: # try 2D 2634 actor = self.picker.GetActor2D() 2635 2636 event = Event() 2637 event.name = ename 2638 event.title = self.title 2639 event.id = -1 # will be set by the timer wrapper function 2640 event.timerid = -1 # will be set by the timer wrapper function 2641 event.priority = -1 # will be set by the timer wrapper function 2642 event.time = time.time() 2643 event.at = self.renderers.index(self.renderer) 2644 event.keypress = key 2645 if enable_picking: 2646 try: 2647 event.object = actor.retrieve_object() 2648 except AttributeError: 2649 event.object = actor 2650 try: 2651 event.actor = actor.retrieve_object() # obsolete use object instead 2652 except AttributeError: 2653 event.actor = actor 2654 event.picked3d = picked3d 2655 event.picked2d = (x, y) 2656 event.delta2d = (dx, dy) 2657 event.angle2d = np.arctan2(dy, dx) 2658 event.speed2d = np.sqrt(dx * dx + dy * dy) 2659 event.delta3d = delta3d 2660 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2661 event.isPoints = isinstance(event.object, vedo.Points) 2662 event.isMesh = isinstance(event.object, vedo.Mesh) 2663 event.isAssembly = isinstance(event.object, vedo.Assembly) 2664 event.isVolume = isinstance(event.object, vedo.Volume) 2665 event.isImage = isinstance(event.object, vedo.Image) 2666 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2667 return event 2668 2669 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2670 """ 2671 Add a function to be executed while show() is active. 2672 2673 Return a unique id for the callback. 2674 2675 The callback function (see example below) exposes a dictionary 2676 with the following information: 2677 - `name`: event name, 2678 - `id`: event unique identifier, 2679 - `priority`: event priority (float), 2680 - `interactor`: the interactor object, 2681 - `at`: renderer nr. where the event occurred 2682 - `keypress`: key pressed as string 2683 - `actor`: object picked by the mouse 2684 - `picked3d`: point picked in world coordinates 2685 - `picked2d`: screen coords of the mouse pointer 2686 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2687 - `delta3d`: ...same but in 3D world coords 2688 - `angle2d`: angle of mouse movement on screen 2689 - `speed2d`: speed of mouse movement on screen 2690 - `speed3d`: speed of picked point in world coordinates 2691 - `isPoints`: True if of class 2692 - `isMesh`: True if of class 2693 - `isAssembly`: True if of class 2694 - `isVolume`: True if of class Volume 2695 - `isImage`: True if of class 2696 2697 If `enable_picking` is False, no picking will be performed. 2698 This can be useful to avoid double picking when using buttons. 2699 2700 Frequently used events are: 2701 - `KeyPress`, `KeyRelease`: listen to keyboard events 2702 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2703 - `MiddleButtonPress`, `MiddleButtonRelease` 2704 - `RightButtonPress`, `RightButtonRelease` 2705 - `MouseMove`: listen to mouse pointer changing position 2706 - `MouseWheelForward`, `MouseWheelBackward` 2707 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2708 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2709 - `ResetCamera`, `ResetCameraClippingRange` 2710 - `Error`, `Warning` 2711 - `Char` 2712 - `Timer` 2713 2714 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2715 2716 Example: 2717 ```python 2718 from vedo import * 2719 2720 def func(evt): 2721 # this function is called every time the mouse moves 2722 # (evt is a dotted dictionary) 2723 if not evt.object: 2724 return # no hit, return 2725 print("point coords =", evt.picked3d) 2726 # print(evt) # full event dump 2727 2728 elli = Ellipsoid() 2729 plt = Plotter(axes=1) 2730 plt.add_callback('mouse hovering', func) 2731 plt.show(elli).close() 2732 ``` 2733 2734 Examples: 2735 - [spline_draw1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw1.py) 2736 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2737 2738  2739 2740 - ..and many others! 2741 """ 2742 from vtkmodules.util.misc import calldata_type # noqa 2743 2744 if not self.interactor: 2745 return 0 2746 2747 if vedo.settings.dry_run_mode >= 1: 2748 return 0 2749 2750 ######################################### 2751 @calldata_type(vtki.VTK_INT) 2752 def _func_wrap(iren, ename, timerid=None): 2753 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2754 event.timerid = timerid 2755 event.id = cid 2756 event.priority = priority 2757 self.last_event = event 2758 func(event) 2759 2760 ######################################### 2761 2762 event_name = utils.get_vtk_name_event(event_name) 2763 2764 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2765 # print(f"Registering event: {event_name} with id={cid}") 2766 return cid 2767 2768 def remove_callback(self, cid: Union[int, str]) -> Self: 2769 """ 2770 Remove a callback function by its id 2771 or a whole category of callbacks by their name. 2772 2773 Arguments: 2774 cid : (int, str) 2775 Unique id of the callback. 2776 If an event name is passed all callbacks of that type are removed. 2777 """ 2778 if self.interactor: 2779 if isinstance(cid, str): 2780 cid = utils.get_vtk_name_event(cid) 2781 self.interactor.RemoveObservers(cid) 2782 else: 2783 self.interactor.RemoveObserver(cid) 2784 return self 2785 2786 def remove_all_observers(self) -> Self: 2787 """ 2788 Remove all observers. 2789 2790 Example: 2791 ```python 2792 from vedo import * 2793 2794 def kfunc(event): 2795 print("Key pressed:", event.keypress) 2796 if event.keypress == 'q': 2797 plt.close() 2798 2799 def rfunc(event): 2800 if event.isImage: 2801 printc("Right-clicked!", event) 2802 plt.render() 2803 2804 img = Image(dataurl+"images/embryo.jpg") 2805 2806 plt = Plotter(size=(1050, 600)) 2807 plt.parallel_projection(True) 2808 plt.remove_all_observers() 2809 plt.add_callback("key press", kfunc) 2810 plt.add_callback("mouse right click", rfunc) 2811 plt.show("Right-Click Me! Press q to exit.", img) 2812 plt.close() 2813 ``` 2814 """ 2815 if self.interactor: 2816 self.interactor.RemoveAllObservers() 2817 return self 2818 2819 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2820 """ 2821 Start or stop an existing timer. 2822 2823 Arguments: 2824 action : (str) 2825 Either "create"/"start" or "destroy"/"stop" 2826 timer_id : (int) 2827 When stopping the timer, the ID of the timer as returned when created 2828 dt : (int) 2829 time in milliseconds between each repeated call 2830 one_shot : (bool) 2831 create a one shot timer of prescribed duration instead of a repeating one 2832 2833 Examples: 2834 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2835 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2836 2837  2838 """ 2839 if action in ("create", "start"): 2840 2841 if "Windows" in vedo.sys_platform: 2842 # otherwise on windows it gets stuck 2843 self.initialize_interactor() 2844 2845 if timer_id is not None: 2846 vedo.logger.warning("you set a timer_id but it will be ignored.") 2847 if one_shot: 2848 timer_id = self.interactor.CreateOneShotTimer(dt) 2849 else: 2850 timer_id = self.interactor.CreateRepeatingTimer(dt) 2851 return timer_id 2852 2853 elif action in ("destroy", "stop"): 2854 if timer_id is not None: 2855 self.interactor.DestroyTimer(timer_id) 2856 else: 2857 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2858 else: 2859 e = f"in timer_callback(). Cannot understand action: {action}\n" 2860 e += " allowed actions are: ['start', 'stop']. Skipped." 2861 vedo.logger.error(e) 2862 return timer_id 2863 2864 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2865 """ 2866 Add a callback function that will be called when an event occurs. 2867 Consider using `add_callback()` instead. 2868 """ 2869 if not self.interactor: 2870 return -1 2871 event_name = utils.get_vtk_name_event(event_name) 2872 idd = self.interactor.AddObserver(event_name, func, priority) 2873 return idd 2874 2875 def compute_world_coordinate( 2876 self, 2877 pos2d: MutableSequence[float], 2878 at=None, 2879 objs=(), 2880 bounds=(), 2881 offset=None, 2882 pixeltol=None, 2883 worldtol=None, 2884 ) -> np.ndarray: 2885 """ 2886 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2887 If a set of meshes is passed then points are placed onto these. 2888 2889 Arguments: 2890 pos2d : (list) 2891 2D screen coordinates point. 2892 at : (int) 2893 renderer number. 2894 objs : (list) 2895 list of Mesh objects to project the point onto. 2896 bounds : (list) 2897 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2898 offset : (float) 2899 specify an offset value. 2900 pixeltol : (int) 2901 screen tolerance in pixels. 2902 worldtol : (float) 2903 world coordinates tolerance. 2904 2905 Returns: 2906 numpy array, the point in 3D world coordinates. 2907 2908 Examples: 2909 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2910 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2911 2912  2913 """ 2914 renderer = self.renderer if at is None else self.renderers[at] 2915 2916 if not objs: 2917 pp = vtki.vtkFocalPlanePointPlacer() 2918 else: 2919 pps = vtki.vtkPolygonalSurfacePointPlacer() 2920 for ob in objs: 2921 pps.AddProp(ob.actor) 2922 pp = pps # type: ignore 2923 2924 if len(bounds) == 6: 2925 pp.SetPointBounds(bounds) 2926 if pixeltol: 2927 pp.SetPixelTolerance(pixeltol) 2928 if worldtol: 2929 pp.SetWorldTolerance(worldtol) 2930 if offset: 2931 pp.SetOffset(offset) 2932 2933 worldPos: MutableSequence[float] = [0, 0, 0] 2934 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2935 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2936 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2937 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2938 return np.array(worldPos) 2939 2940 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2941 """ 2942 Given a 3D points in the current renderer (or full window), 2943 find the screen pixel coordinates. 2944 2945 Example: 2946 ```python 2947 from vedo import * 2948 2949 elli = Ellipsoid().point_size(5) 2950 2951 plt = Plotter() 2952 plt.show(elli, "Press q to continue and print the info") 2953 2954 xyscreen = plt.compute_screen_coordinates(elli) 2955 print('xyscreen coords:', xyscreen) 2956 2957 # simulate an event happening at one point 2958 event = plt.fill_event(pos=xyscreen[123]) 2959 print(event) 2960 ``` 2961 """ 2962 try: 2963 obj = obj.coordinates 2964 except AttributeError: 2965 pass 2966 2967 if utils.is_sequence(obj): 2968 pts = obj 2969 p2d = [] 2970 cs = vtki.vtkCoordinate() 2971 cs.SetCoordinateSystemToWorld() 2972 cs.SetViewport(self.renderer) 2973 for p in pts: 2974 cs.SetValue(p) 2975 if full_window: 2976 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2977 else: 2978 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2979 return np.array(p2d, dtype=int) 2980 2981 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2982 """ 2983 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2984 2985 Returns a frustum Mesh that contains the visible field of view. 2986 This can be used to select objects in a scene or select vertices. 2987 2988 Example: 2989 ```python 2990 from vedo import * 2991 2992 settings.enable_default_mouse_callbacks = False 2993 2994 def mode_select(objs): 2995 print("Selected objects:", objs) 2996 d0 = mode.start_x, mode.start_y # display coords 2997 d1 = mode.end_x, mode.end_y 2998 2999 frustum = plt.pick_area(d0, d1) 3000 col = np.random.randint(0, 10) 3001 infru = frustum.inside_points(mesh) 3002 infru.point_size(10).color(col) 3003 plt.add(frustum, infru).render() 3004 3005 mesh = Mesh(dataurl+"cow.vtk") 3006 mesh.color("k5").linewidth(1) 3007 3008 mode = interactor_modes.BlenderStyle() 3009 mode.callback_select = mode_select 3010 3011 plt = Plotter().user_mode(mode) 3012 plt.show(mesh, axes=1) 3013 ``` 3014 """ 3015 ren = self.renderer if at is None else self.renderers[at] 3016 area_picker = vtki.vtkAreaPicker() 3017 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 3018 planes = area_picker.GetFrustum() 3019 3020 fru = vtki.new("FrustumSource") 3021 fru.SetPlanes(planes) 3022 fru.ShowLinesOff() 3023 fru.Update() 3024 3025 afru = vedo.Mesh(fru.GetOutput()) 3026 afru.alpha(0.1).lw(1).pickable(False) 3027 afru.name = "Frustum" 3028 return afru 3029 3030 def _scan_input_return_acts(self, objs) -> Any: 3031 # scan the input and return a list of actors 3032 if not utils.is_sequence(objs): 3033 objs = [objs] 3034 3035 ################# 3036 wannabe_acts = [] 3037 for a in objs: 3038 3039 try: 3040 wannabe_acts.append(a.actor) 3041 except AttributeError: 3042 wannabe_acts.append(a) # already actor 3043 3044 try: 3045 wannabe_acts.append(a.scalarbar) 3046 except AttributeError: 3047 pass 3048 3049 try: 3050 for sh in a.shadows: 3051 wannabe_acts.append(sh.actor) 3052 except AttributeError: 3053 pass 3054 3055 try: 3056 wannabe_acts.append(a.trail.actor) 3057 if a.trail.shadows: # trails may also have shadows 3058 for sh in a.trail.shadows: 3059 wannabe_acts.append(sh.actor) 3060 except AttributeError: 3061 pass 3062 3063 ################# 3064 scanned_acts = [] 3065 for a in wannabe_acts: # scan content of list 3066 3067 if a is None: 3068 pass 3069 3070 elif isinstance(a, (vtki.vtkActor, vtki.vtkActor2D)): 3071 scanned_acts.append(a) 3072 3073 elif isinstance(a, str): 3074 # assume a 2D comment was given 3075 changed = False # check if one already exists so to just update text 3076 if self.renderer: # might be jupyter 3077 acs = self.renderer.GetActors2D() 3078 acs.InitTraversal() 3079 for i in range(acs.GetNumberOfItems()): 3080 act = acs.GetNextItem() 3081 if isinstance(act, vedo.shapes.Text2D): 3082 aposx, aposy = act.GetPosition() 3083 if aposx < 0.01 and aposy > 0.99: # "top-left" 3084 act.text(a) # update content! no appending nada 3085 changed = True 3086 break 3087 if not changed: 3088 out = vedo.shapes.Text2D(a) # append a new one 3089 scanned_acts.append(out) 3090 # scanned_acts.append(vedo.shapes.Text2D(a)) # naive version 3091 3092 elif isinstance(a, vtki.vtkPolyData): 3093 scanned_acts.append(vedo.Mesh(a).actor) 3094 3095 elif isinstance(a, vtki.vtkImageData): 3096 scanned_acts.append(vedo.Volume(a).actor) 3097 3098 elif isinstance(a, vedo.RectilinearGrid): 3099 scanned_acts.append(a.actor) 3100 3101 elif isinstance(a, vedo.StructuredGrid): 3102 scanned_acts.append(a.actor) 3103 3104 elif isinstance(a, vtki.vtkLight): 3105 scanned_acts.append(a) 3106 3107 elif isinstance(a, vedo.visual.LightKit): 3108 a.lightkit.AddLightsToRenderer(self.renderer) 3109 3110 elif isinstance(a, vtki.get_class("MultiBlockDataSet")): 3111 for i in range(a.GetNumberOfBlocks()): 3112 b = a.GetBlock(i) 3113 if isinstance(b, vtki.vtkPolyData): 3114 scanned_acts.append(vedo.Mesh(b).actor) 3115 elif isinstance(b, vtki.vtkImageData): 3116 scanned_acts.append(vedo.Volume(b).actor) 3117 3118 elif isinstance(a, (vtki.vtkProp, vtki.vtkInteractorObserver)): 3119 scanned_acts.append(a) 3120 3121 elif "trimesh" in str(type(a)): 3122 scanned_acts.append(utils.trimesh2vedo(a)) 3123 3124 elif "meshlab" in str(type(a)): 3125 if "MeshSet" in str(type(a)): 3126 for i in range(a.number_meshes()): 3127 if a.mesh_id_exists(i): 3128 scanned_acts.append(utils.meshlab2vedo(a.mesh(i))) 3129 else: 3130 scanned_acts.append(utils.meshlab2vedo(a)) 3131 3132 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 3133 import vedo.dolfin as vdlf # type: ignore[import] 3134 3135 scanned_acts.append(vdlf.IMesh(a).actor) 3136 3137 elif "madcad" in str(type(a)): 3138 scanned_acts.append(utils.madcad2vedo(a).actor) 3139 3140 elif "TetgenIO" in str(type(a)): 3141 scanned_acts.append(vedo.TetMesh(a).shrink(0.9).c("pink7").actor) 3142 3143 elif "matplotlib.figure.Figure" in str(type(a)): 3144 scanned_acts.append(vedo.Image(a).clone2d("top-right", 0.6)) 3145 3146 else: 3147 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 3148 3149 return scanned_acts 3150 3151 def show( 3152 self, 3153 *objects, 3154 at=None, 3155 axes=None, 3156 resetcam=None, 3157 zoom=False, 3158 interactive=None, 3159 viewup="", 3160 azimuth=0.0, 3161 elevation=0.0, 3162 roll=0.0, 3163 camera=None, 3164 mode=None, 3165 rate=None, 3166 bg=None, 3167 bg2=None, 3168 size=None, 3169 title=None, 3170 screenshot="", 3171 ) -> Any: 3172 """ 3173 Render a list of objects. 3174 3175 Arguments: 3176 at : (int) 3177 number of the renderer to plot to, in case of more than one exists 3178 3179 axes : (int) 3180 axis type-1 can be fully customized by passing a dictionary. 3181 Check `addons.Axes()` for the full list of options. 3182 set the type of axes to be shown: 3183 - 0, no axes 3184 - 1, draw three gray grid walls 3185 - 2, show cartesian axes from (0,0,0) 3186 - 3, show positive range of cartesian axes from (0,0,0) 3187 - 4, show a triad at bottom left 3188 - 5, show a cube at bottom left 3189 - 6, mark the corners of the bounding box 3190 - 7, draw a 3D ruler at each side of the cartesian axes 3191 - 8, show the `vtkCubeAxesActor` object 3192 - 9, show the bounding box outLine 3193 - 10, show three circles representing the maximum bounding box 3194 - 11, show a large grid on the x-y plane 3195 - 12, show polar axes 3196 - 13, draw a simple ruler at the bottom of the window 3197 3198 azimuth/elevation/roll : (float) 3199 move camera accordingly the specified value 3200 3201 viewup: str, list 3202 either `['x', 'y', 'z']` or a vector to set vertical direction 3203 3204 resetcam : (bool) 3205 re-adjust camera position to fit objects 3206 3207 camera : (dict, vtkCamera) 3208 camera parameters can further be specified with a dictionary 3209 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3210 - pos, `(list)`, the position of the camera in world coordinates 3211 - focal_point `(list)`, the focal point of the camera in world coordinates 3212 - viewup `(list)`, the view up direction for the camera 3213 - distance `(float)`, set the focal point to the specified distance from the camera position. 3214 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3215 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3216 in world-coordinate distances. The default is 1. 3217 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3218 This method has no effect in perspective projection mode. 3219 3220 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3221 plane to be set a distance 'thickness' beyond the near clipping plane. 3222 3223 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3224 measured in degrees. The default angle is 30 degrees. 3225 This method has no effect in parallel projection mode. 3226 The formula for setting the angle up for perfect perspective viewing is: 3227 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3228 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3229 3230 interactive : (bool) 3231 pause and interact with window (True) or continue execution (False) 3232 3233 rate : (float) 3234 maximum rate of `show()` in Hertz 3235 3236 mode : (int, str) 3237 set the type of interaction: 3238 - 0 = TrackballCamera [default] 3239 - 1 = TrackballActor 3240 - 2 = JoystickCamera 3241 - 3 = JoystickActor 3242 - 4 = Flight 3243 - 5 = RubberBand2D 3244 - 6 = RubberBand3D 3245 - 7 = RubberBandZoom 3246 - 8 = Terrain 3247 - 9 = Unicam 3248 - 10 = Image 3249 - Check out `vedo.interaction_modes` for more options. 3250 3251 bg : (str, list) 3252 background color in RGB format, or string name 3253 3254 bg2 : (str, list) 3255 second background color to create a gradient background 3256 3257 size : (str, list) 3258 size of the window, e.g. size="fullscreen", or size=[600,400] 3259 3260 title : (str) 3261 window title text 3262 3263 screenshot : (str) 3264 save a screenshot of the window to file 3265 """ 3266 3267 if vedo.settings.dry_run_mode >= 2: 3268 return self 3269 3270 if self.wx_widget: 3271 return self 3272 3273 if self.renderers: # in case of notebooks 3274 3275 if at is None: 3276 at = self.renderers.index(self.renderer) 3277 3278 else: 3279 3280 if at >= len(self.renderers): 3281 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3282 vedo.logger.error(t) 3283 return self 3284 3285 self.renderer = self.renderers[at] 3286 3287 if title is not None: 3288 self.title = title 3289 3290 if size is not None: 3291 self.size = size 3292 if self.size[0] == "f": # full screen 3293 self.size = "fullscreen" 3294 self.window.SetFullScreen(True) 3295 self.window.BordersOn() 3296 else: 3297 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3298 3299 if vedo.settings.default_backend == "vtk": 3300 if str(bg).endswith(".hdr"): 3301 self._add_skybox(bg) 3302 else: 3303 if bg is not None: 3304 self.backgrcol = vedo.get_color(bg) 3305 self.renderer.SetBackground(self.backgrcol) 3306 if bg2 is not None: 3307 self.renderer.GradientBackgroundOn() 3308 self.renderer.SetBackground2(vedo.get_color(bg2)) 3309 3310 if axes is not None: 3311 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3312 objects = list(objects) 3313 objects.append(axes) # move it into the list of normal things to show 3314 axes = 0 3315 self.axes = axes 3316 3317 if interactive is not None: 3318 self._interactive = interactive 3319 if self.offscreen: 3320 self._interactive = False 3321 3322 # camera stuff 3323 if resetcam is not None: 3324 self.resetcam = resetcam 3325 3326 if camera is not None: 3327 self.resetcam = False 3328 viewup = "" 3329 if isinstance(camera, vtki.vtkCamera): 3330 cameracopy = vtki.vtkCamera() 3331 cameracopy.DeepCopy(camera) 3332 self.camera = cameracopy 3333 else: 3334 self.camera = utils.camera_from_dict(camera) 3335 3336 self.add(objects) 3337 3338 # Backend ############################################################### 3339 if vedo.settings.default_backend in ["k3d", "panel"]: 3340 return backends.get_notebook_backend(self.objects) 3341 ######################################################################### 3342 3343 for ia in utils.flatten(objects): 3344 try: 3345 # fix gray color labels and title to white or black 3346 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3347 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3348 c = (0.9, 0.9, 0.9) 3349 if np.sum(self.renderer.GetBackground()) > 1.5: 3350 c = (0.1, 0.1, 0.1) 3351 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3352 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3353 except AttributeError: 3354 pass 3355 3356 if self.sharecam: 3357 for r in self.renderers: 3358 r.SetActiveCamera(self.camera) 3359 3360 if self.axes is not None: 3361 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3362 bns = self.renderer.ComputeVisiblePropBounds() 3363 addons.add_global_axes(self.axes, bounds=bns) 3364 3365 # Backend ############################################################### 3366 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3367 return backends.get_notebook_backend() 3368 ######################################################################### 3369 3370 if self.resetcam and self.renderer: 3371 self.renderer.ResetCamera() 3372 3373 if len(self.renderers) > 1: 3374 self.add_renderer_frame() 3375 3376 if vedo.settings.default_backend == "2d" and not zoom: 3377 zoom = "tightest" 3378 3379 if zoom: 3380 if zoom == "tight": 3381 self.reset_camera(tight=0.04) 3382 elif zoom == "tightest": 3383 self.reset_camera(tight=0.0001) 3384 else: 3385 self.camera.Zoom(zoom) 3386 if elevation: 3387 self.camera.Elevation(elevation) 3388 if azimuth: 3389 self.camera.Azimuth(azimuth) 3390 if roll: 3391 self.camera.Roll(roll) 3392 3393 if len(viewup) > 0: 3394 b = self.renderer.ComputeVisiblePropBounds() 3395 cm = np.array([(b[1] + b[0]) / 2, (b[3] + b[2]) / 2, (b[5] + b[4]) / 2]) 3396 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3397 if viewup == "x": 3398 sz = np.linalg.norm(sz) 3399 self.camera.SetViewUp([1, 0, 0]) 3400 self.camera.SetPosition(cm + sz) 3401 elif viewup == "y": 3402 sz = np.linalg.norm(sz) 3403 self.camera.SetViewUp([0, 1, 0]) 3404 self.camera.SetPosition(cm + sz) 3405 elif viewup == "z": 3406 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3407 self.camera.SetViewUp([0, 0, 1]) 3408 self.camera.SetPosition(cm + 2 * sz) 3409 elif utils.is_sequence(viewup): 3410 sz = np.linalg.norm(sz) 3411 self.camera.SetViewUp(viewup) 3412 cpos = np.cross([0, 1, 0], viewup) 3413 self.camera.SetPosition(cm - 2 * sz * cpos) 3414 3415 self.renderer.ResetCameraClippingRange() 3416 3417 self.initialize_interactor() 3418 3419 if vedo.settings.immediate_rendering: 3420 self.window.Render() ##################### <-------------- Render 3421 3422 if self.interactor: # can be offscreen or not the vtk backend.. 3423 3424 self.window.SetWindowName(self.title) 3425 3426 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3427 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3428 # print(pic.dataset)# Array 0 name PNGImage 3429 # self.window.SetIcon(pic.dataset) 3430 3431 try: 3432 # Needs "pip install pyobjc" on Mac OSX 3433 if ( 3434 self._cocoa_initialized is False 3435 and "Darwin" in vedo.sys_platform 3436 and not self.offscreen 3437 ): 3438 self._cocoa_initialized = True 3439 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3440 pid = os.getpid() 3441 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3442 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3443 except: 3444 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3445 pass 3446 3447 # Set the interaction style 3448 if mode is not None: 3449 self.user_mode(mode) 3450 if self.qt_widget and mode is None: 3451 self.user_mode(0) 3452 3453 if screenshot: 3454 self.screenshot(screenshot) 3455 3456 if self._interactive and self.interactor: 3457 self.interactor.Start() 3458 if self._must_close_now and self.interactor: 3459 self.interactor.GetRenderWindow().Finalize() 3460 self.interactor.TerminateApp() 3461 self.camera = None 3462 self.renderer = None 3463 self.renderers = [] 3464 self.window = None 3465 self.interactor = None 3466 return self 3467 3468 if rate: 3469 if self.clock is None: # set clock and limit rate 3470 self._clockt0 = time.time() 3471 self.clock = 0.0 3472 else: 3473 t = time.time() - self._clockt0 3474 elapsed = t - self.clock 3475 mint = 1.0 / rate 3476 if elapsed < mint: 3477 time.sleep(mint - elapsed) 3478 self.clock = time.time() - self._clockt0 3479 3480 # 2d #################################################################### 3481 if vedo.settings.default_backend in ["2d"]: 3482 return backends.get_notebook_backend() 3483 ######################################################################### 3484 3485 return self 3486 3487 def add_inset( 3488 self, *objects, **options 3489 ) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3490 """Add a draggable inset space into a renderer. 3491 3492 Arguments: 3493 at : (int) 3494 specify the renderer number 3495 pos : (list) 3496 icon position in the range [1-4] indicating one of the 4 corners, 3497 or it can be a tuple (x,y) as a fraction of the renderer size. 3498 size : (float) 3499 size of the square inset 3500 draggable : (bool) 3501 if True the subrenderer space can be dragged around 3502 c : (color) 3503 color of the inset frame when dragged 3504 3505 Examples: 3506 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3507 3508  3509 """ 3510 if not self.interactor: 3511 return None 3512 3513 if not self.renderer: 3514 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3515 return None 3516 3517 options = dict(options) 3518 pos = options.pop("pos", 0) 3519 size = options.pop("size", 0.1) 3520 c = options.pop("c", "lb") 3521 at = options.pop("at", None) 3522 draggable = options.pop("draggable", True) 3523 3524 r, g, b = vedo.get_color(c) 3525 widget = vtki.vtkOrientationMarkerWidget() 3526 widget.SetOutlineColor(r, g, b) 3527 if len(objects) == 1: 3528 widget.SetOrientationMarker(objects[0].actor) 3529 else: 3530 widget.SetOrientationMarker(vedo.Assembly(objects)) 3531 3532 widget.SetInteractor(self.interactor) 3533 3534 if utils.is_sequence(pos): 3535 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3536 else: 3537 if pos < 2: 3538 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3539 elif pos == 2: 3540 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3541 elif pos == 3: 3542 widget.SetViewport(0, 0, size * 2, size * 2) 3543 elif pos == 4: 3544 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3545 widget.EnabledOn() 3546 widget.SetInteractive(draggable) 3547 if at is not None and at < len(self.renderers): 3548 widget.SetCurrentRenderer(self.renderers[at]) 3549 else: 3550 widget.SetCurrentRenderer(self.renderer) 3551 self.widgets.append(widget) 3552 return widget 3553 3554 def clear(self, at=None, deep=False) -> Self: 3555 """Clear the scene from all meshes and volumes.""" 3556 renderer = self.renderer if at is None else self.renderers[at] 3557 if not renderer: 3558 return self 3559 3560 if deep: 3561 renderer.RemoveAllViewProps() 3562 else: 3563 for ob in set( 3564 self.get_meshes() 3565 + self.get_volumes() 3566 + self.objects 3567 + self.axes_instances 3568 ): 3569 if isinstance(ob, vedo.shapes.Text2D): 3570 continue 3571 self.remove(ob) 3572 try: 3573 if ob.scalarbar: 3574 self.remove(ob.scalarbar) 3575 except AttributeError: 3576 pass 3577 return self 3578 3579 def break_interaction(self) -> Self: 3580 """Break window interaction and return to the python execution flow""" 3581 if self.interactor: 3582 self.check_actors_trasform() 3583 self.interactor.ExitCallback() 3584 return self 3585 3586 def freeze(self, value=True) -> Self: 3587 """Freeze the current renderer. Use this with `sharecam=False`.""" 3588 if not self.interactor: 3589 return self 3590 if not self.renderer: 3591 return self 3592 self.renderer.SetInteractive(not value) 3593 return self 3594 3595 def user_mode(self, mode) -> Self: 3596 """ 3597 Modify the user interaction mode. 3598 3599 Examples: 3600 ```python 3601 from vedo import * 3602 mode = interactor_modes.MousePan() 3603 mesh = Mesh(dataurl+"cow.vtk") 3604 plt = Plotter().user_mode(mode) 3605 plt.show(mesh, axes=1) 3606 ``` 3607 See also: 3608 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3609 """ 3610 if not self.interactor: 3611 return self 3612 3613 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3614 # print("Current style:", curr_style) 3615 if curr_style.endswith("Actor"): 3616 self.check_actors_trasform() 3617 3618 if isinstance(mode, (str, int)): 3619 # Set the style of interaction 3620 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3621 if mode in (0, "TrackballCamera"): 3622 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3623 self.interactor.RemoveObservers("CharEvent") 3624 elif mode in (1, "TrackballActor"): 3625 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3626 elif mode in (2, "JoystickCamera"): 3627 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3628 elif mode in (3, "JoystickActor"): 3629 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3630 elif mode in (4, "Flight"): 3631 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3632 elif mode in (5, "RubberBand2D"): 3633 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3634 elif mode in (6, "RubberBand3D"): 3635 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3636 elif mode in (7, "RubberBandZoom"): 3637 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3638 elif mode in (8, "Terrain"): 3639 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3640 elif mode in (9, "Unicam"): 3641 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3642 elif mode in (10, "Image", "image", "2d"): 3643 astyle = vtki.new("InteractorStyleImage") 3644 astyle.SetInteractionModeToImage3D() 3645 self.interactor.SetInteractorStyle(astyle) 3646 else: 3647 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3648 3649 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3650 # set a custom interactor style 3651 if hasattr(mode, "interactor"): 3652 mode.interactor = self.interactor 3653 mode.renderer = self.renderer # type: ignore 3654 mode.SetInteractor(self.interactor) 3655 mode.SetDefaultRenderer(self.renderer) 3656 self.interactor.SetInteractorStyle(mode) 3657 3658 return self 3659 3660 def close(self) -> Self: 3661 """Close the plotter.""" 3662 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3663 vedo.last_figure = None 3664 self.last_event = None 3665 self.sliders = [] 3666 self.buttons = [] 3667 self.widgets = [] 3668 self.hover_legends = [] 3669 self.background_renderer = None 3670 self._extralight = None 3671 3672 self.hint_widget = None 3673 self.cutter_widget = None 3674 3675 if vedo.settings.dry_run_mode >= 2: 3676 return self 3677 3678 if not hasattr(self, "window"): 3679 return self 3680 if not self.window: 3681 return self 3682 if not hasattr(self, "interactor"): 3683 return self 3684 if not self.interactor: 3685 return self 3686 3687 ################################################### 3688 3689 self._must_close_now = True 3690 3691 if self.interactor: 3692 if self._interactive: 3693 self.break_interaction() 3694 self.interactor.GetRenderWindow().Finalize() 3695 try: 3696 if "Darwin" in vedo.sys_platform: 3697 self.interactor.ProcessEvents() 3698 except: 3699 pass 3700 self.interactor.TerminateApp() 3701 self.camera = None 3702 self.renderer = None 3703 self.renderers = [] 3704 self.window = None 3705 self.interactor = None 3706 3707 if vedo.plotter_instance == self: 3708 vedo.plotter_instance = None 3709 return self # must return self for consistency 3710 3711 3712 @property 3713 def camera(self): 3714 """Return the current active camera.""" 3715 if self.renderer: 3716 return self.renderer.GetActiveCamera() 3717 3718 @camera.setter 3719 def camera(self, cam): 3720 if self.renderer: 3721 if isinstance(cam, dict): 3722 cam = utils.camera_from_dict(cam) 3723 self.renderer.SetActiveCamera(cam) 3724 3725 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3726 """ 3727 Take a screenshot of the Plotter window. 3728 3729 Arguments: 3730 scale : (int) 3731 set image magnification as an integer multiplicating factor 3732 asarray : (bool) 3733 return a numpy array of the image instead of writing a file 3734 3735 Warning: 3736 If you get black screenshots try to set `interactive=False` in `show()` 3737 then call `screenshot()` and `plt.interactive()` afterwards. 3738 3739 Example: 3740 ```py 3741 from vedo import * 3742 sphere = Sphere().linewidth(1) 3743 plt = show(sphere, interactive=False) 3744 plt.screenshot('image.png') 3745 plt.interactive() 3746 plt.close() 3747 ``` 3748 3749 Example: 3750 ```py 3751 from vedo import * 3752 sphere = Sphere().linewidth(1) 3753 plt = show(sphere, interactive=False) 3754 plt.screenshot('anotherimage.png') 3755 plt.interactive() 3756 plt.close() 3757 ``` 3758 """ 3759 return vedo.file_io.screenshot(filename, scale, asarray) 3760 3761 def toimage(self, scale=1) -> "vedo.image.Image": 3762 """ 3763 Generate a `Image` object from the current rendering window. 3764 3765 Arguments: 3766 scale : (int) 3767 set image magnification as an integer multiplicating factor 3768 """ 3769 if vedo.settings.screeshot_large_image: 3770 w2if = vtki.new("RenderLargeImage") 3771 w2if.SetInput(self.renderer) 3772 w2if.SetMagnification(scale) 3773 else: 3774 w2if = vtki.new("WindowToImageFilter") 3775 w2if.SetInput(self.window) 3776 if hasattr(w2if, "SetScale"): 3777 w2if.SetScale(scale, scale) 3778 if vedo.settings.screenshot_transparent_background: 3779 w2if.SetInputBufferTypeToRGBA() 3780 w2if.ReadFrontBufferOff() # read from the back buffer 3781 w2if.Update() 3782 return vedo.image.Image(w2if.GetOutput()) 3783 3784 def export(self, filename="scene.npz", binary=False) -> Self: 3785 """ 3786 Export scene to file to HTML, X3D or Numpy file. 3787 3788 Examples: 3789 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3790 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3791 """ 3792 vedo.file_io.export_window(filename, binary=binary) 3793 return self 3794 3795 def color_picker(self, xy, verbose=False): 3796 """Pick color of specific (x,y) pixel on the screen.""" 3797 w2if = vtki.new("WindowToImageFilter") 3798 w2if.SetInput(self.window) 3799 w2if.ReadFrontBufferOff() 3800 w2if.Update() 3801 nx, ny = self.window.GetSize() 3802 varr = w2if.GetOutput().GetPointData().GetScalars() 3803 3804 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3805 x, y = int(xy[0]), int(xy[1]) 3806 if y < ny and x < nx: 3807 3808 rgb = arr[y, x] 3809 3810 if verbose: 3811 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3812 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3813 vedo.printc("█", c=[0, rgb[1], 0], end="") 3814 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3815 vedo.printc("] = ", end="") 3816 cnm = vedo.get_color_name(rgb) 3817 if np.sum(rgb) < 150: 3818 vedo.printc( 3819 rgb.tolist(), 3820 vedo.colors.rgb2hex(np.array(rgb) / 255), 3821 c="w", 3822 bc=rgb, 3823 invert=1, 3824 end="", 3825 ) 3826 vedo.printc(" -> " + cnm, invert=1, c="w") 3827 else: 3828 vedo.printc( 3829 rgb.tolist(), 3830 vedo.colors.rgb2hex(np.array(rgb) / 255), 3831 c=rgb, 3832 end="", 3833 ) 3834 vedo.printc(" -> " + cnm, c=cnm) 3835 3836 return rgb 3837 3838 return None 3839 3840 ####################################################################### 3841 def _default_mouseleftclick(self, iren, event) -> None: 3842 x, y = iren.GetEventPosition() 3843 renderer = iren.FindPokedRenderer(x, y) 3844 picker = vtki.vtkPropPicker() 3845 picker.PickProp(x, y, renderer) 3846 3847 self.renderer = renderer 3848 3849 clicked_actor = picker.GetActor() 3850 # clicked_actor2D = picker.GetActor2D() 3851 3852 # print('_default_mouseleftclick mouse at', x, y) 3853 # print("picked Volume:", [picker.GetVolume()]) 3854 # print("picked Actor2D:", [picker.GetActor2D()]) 3855 # print("picked Assembly:", [picker.GetAssembly()]) 3856 # print("picked Prop3D:", [picker.GetProp3D()]) 3857 3858 if not clicked_actor: 3859 clicked_actor = picker.GetAssembly() 3860 3861 if not clicked_actor: 3862 clicked_actor = picker.GetProp3D() 3863 3864 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3865 return 3866 3867 self.picked3d = picker.GetPickPosition() 3868 self.picked2d = np.array([x, y]) 3869 3870 if not clicked_actor: 3871 return 3872 3873 self.justremoved = None 3874 self.clicked_actor = clicked_actor 3875 3876 try: # might not be a vedo obj 3877 self.clicked_object = clicked_actor.retrieve_object() 3878 # save this info in the object itself 3879 self.clicked_object.picked3d = self.picked3d 3880 self.clicked_object.picked2d = self.picked2d 3881 except AttributeError: 3882 pass 3883 3884 # ----------- 3885 # if "Histogram1D" in picker.GetAssembly().__class__.__name__: 3886 # histo = picker.GetAssembly() 3887 # if histo.verbose: 3888 # x = self.picked3d[0] 3889 # idx = np.digitize(x, histo.edges) - 1 3890 # f = histo.frequencies[idx] 3891 # cn = histo.centers[idx] 3892 # vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3893 3894 ####################################################################### 3895 def _default_keypress(self, iren, event) -> None: 3896 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3897 3898 key = iren.GetKeySym() 3899 3900 if "_L" in key or "_R" in key: 3901 return 3902 3903 if iren.GetShiftKey(): 3904 key = key.upper() 3905 3906 if iren.GetControlKey(): 3907 key = "Ctrl+" + key 3908 3909 if iren.GetAltKey(): 3910 key = "Alt+" + key 3911 3912 ####################################################### 3913 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3914 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3915 # iren.GetKeyCode(), iren.GetRepeatCount()) 3916 ####################################################### 3917 3918 x, y = iren.GetEventPosition() 3919 renderer = iren.FindPokedRenderer(x, y) 3920 3921 if key in ["q", "Return"]: 3922 self.break_interaction() 3923 return 3924 3925 elif key in ["Ctrl+q", "Ctrl+w", "Escape"]: 3926 self.close() 3927 return 3928 3929 elif key == "F1": 3930 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3931 self.break_interaction() 3932 sys.exit(0) 3933 3934 elif key == "Down": 3935 if self.clicked_object and self.clicked_object in self.get_meshes(): 3936 self.clicked_object.alpha(0.02) 3937 if hasattr(self.clicked_object, "properties_backface"): 3938 bfp = self.clicked_actor.GetBackfaceProperty() 3939 self.clicked_object.properties_backface = bfp # save it 3940 self.clicked_actor.SetBackfaceProperty(None) 3941 else: 3942 for obj in self.get_meshes(): 3943 if obj: 3944 obj.alpha(0.02) 3945 bfp = obj.actor.GetBackfaceProperty() 3946 if bfp and hasattr(obj, "properties_backface"): 3947 obj.properties_backface = bfp 3948 obj.actor.SetBackfaceProperty(None) 3949 3950 elif key == "Left": 3951 if self.clicked_object and self.clicked_object in self.get_meshes(): 3952 ap = self.clicked_object.properties 3953 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3954 ap.SetOpacity(aal) 3955 bfp = self.clicked_actor.GetBackfaceProperty() 3956 if bfp and hasattr(self.clicked_object, "properties_backface"): 3957 self.clicked_object.properties_backface = bfp 3958 self.clicked_actor.SetBackfaceProperty(None) 3959 else: 3960 for a in self.get_meshes(): 3961 if a: 3962 ap = a.properties 3963 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3964 ap.SetOpacity(aal) 3965 bfp = a.actor.GetBackfaceProperty() 3966 if bfp and hasattr(a, "properties_backface"): 3967 a.properties_backface = bfp 3968 a.actor.SetBackfaceProperty(None) 3969 3970 elif key == "Right": 3971 if self.clicked_object and self.clicked_object in self.get_meshes(): 3972 ap = self.clicked_object.properties 3973 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3974 ap.SetOpacity(aal) 3975 if ( 3976 aal == 1 3977 and hasattr(self.clicked_object, "properties_backface") 3978 and self.clicked_object.properties_backface 3979 ): 3980 # put back 3981 self.clicked_actor.SetBackfaceProperty( 3982 self.clicked_object.properties_backface) 3983 else: 3984 for a in self.get_meshes(): 3985 if a: 3986 ap = a.properties 3987 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3988 ap.SetOpacity(aal) 3989 if ( 3990 aal == 1 3991 and hasattr(a, "properties_backface") 3992 and a.properties_backface 3993 ): 3994 a.actor.SetBackfaceProperty(a.properties_backface) 3995 3996 elif key == "Up": 3997 if self.clicked_object and self.clicked_object in self.get_meshes(): 3998 self.clicked_object.properties.SetOpacity(1) 3999 if ( 4000 hasattr(self.clicked_object, "properties_backface") 4001 and self.clicked_object.properties_backface 4002 ): 4003 self.clicked_object.actor.SetBackfaceProperty( 4004 self.clicked_object.properties_backface 4005 ) 4006 else: 4007 for a in self.get_meshes(): 4008 if a: 4009 a.properties.SetOpacity(1) 4010 if hasattr(a, "properties_backface") and a.properties_backface: 4011 a.actor.SetBackfaceProperty(a.properties_backface) 4012 4013 elif key == "P": 4014 if self.clicked_object and self.clicked_object in self.get_meshes(): 4015 objs = [self.clicked_object] 4016 else: 4017 objs = self.get_meshes() 4018 for ia in objs: 4019 try: 4020 ps = ia.properties.GetPointSize() 4021 if ps > 1: 4022 ia.properties.SetPointSize(ps - 1) 4023 ia.properties.SetRepresentationToPoints() 4024 except AttributeError: 4025 pass 4026 4027 elif key == "p": 4028 if self.clicked_object and self.clicked_object in self.get_meshes(): 4029 objs = [self.clicked_object] 4030 else: 4031 objs = self.get_meshes() 4032 for ia in objs: 4033 try: 4034 ps = ia.properties.GetPointSize() 4035 ia.properties.SetPointSize(ps + 2) 4036 ia.properties.SetRepresentationToPoints() 4037 except AttributeError: 4038 pass 4039 4040 elif key == "U": 4041 pval = renderer.GetActiveCamera().GetParallelProjection() 4042 renderer.GetActiveCamera().SetParallelProjection(not pval) 4043 if pval: 4044 renderer.ResetCamera() 4045 4046 elif key == "r": 4047 renderer.ResetCamera() 4048 4049 elif key == "h": 4050 msg = f" vedo {vedo.__version__}" 4051 msg += f" | vtk {vtki.vtkVersion().GetVTKVersion()}" 4052 msg += f" | numpy {np.__version__}" 4053 msg += f" | python {sys.version_info[0]}.{sys.version_info[1]}, press: " 4054 vedo.printc(msg.ljust(75), invert=True) 4055 msg = ( 4056 " i print info about the last clicked object \n" 4057 " I print color of the pixel under the mouse \n" 4058 " Y show the pipeline for this object as a graph \n" 4059 " <- -> use arrows to reduce/increase opacity \n" 4060 " x toggle mesh visibility \n" 4061 " w toggle wireframe/surface style \n" 4062 " l toggle surface edges visibility \n" 4063 " p/P hide surface faces and show only points \n" 4064 " 1-3 cycle surface color (2=light, 3=dark) \n" 4065 " 4 cycle color map (press shift-4 to go back) \n" 4066 " 5-6 cycle point-cell arrays (shift to go back) \n" 4067 " 7-8 cycle background and gradient color \n" 4068 " 09+- cycle axes styles (on keypad, or press +/-) \n" 4069 " k cycle available lighting styles \n" 4070 " K toggle shading as flat or phong \n" 4071 " A toggle anti-aliasing \n" 4072 " D toggle depth-peeling (for transparencies) \n" 4073 " U toggle perspective/parallel projection \n" 4074 " o/O toggle extra light to scene and rotate it \n" 4075 " a toggle interaction to Actor Mode \n" 4076 " n toggle surface normals \n" 4077 " r reset camera position \n" 4078 " R reset camera to the closest orthogonal view \n" 4079 " . fly camera to the last clicked point \n" 4080 " C print the current camera parameters state \n" 4081 " X invoke a cutter widget tool \n" 4082 " S save a screenshot of the current scene \n" 4083 " E/F export 3D scene to numpy file or X3D \n" 4084 " q return control to python script \n" 4085 " Esc abort execution and exit python kernel " 4086 ) 4087 vedo.printc(msg, dim=True, italic=True, bold=True) 4088 vedo.printc( 4089 " Check out the documentation at: https://vedo.embl.es ".ljust(75), 4090 invert=True, 4091 bold=True, 4092 ) 4093 return 4094 4095 elif key == "a": 4096 cur = iren.GetInteractorStyle() 4097 if isinstance(cur, vtki.get_class("InteractorStyleTrackballCamera")): 4098 msg = "Interactor style changed to TrackballActor\n" 4099 msg += " you can now move and rotate individual meshes:\n" 4100 msg += " press X twice to save the repositioned mesh\n" 4101 msg += " press 'a' to go back to normal style" 4102 vedo.printc(msg) 4103 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 4104 else: 4105 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 4106 return 4107 4108 elif key == "A": # toggle antialiasing 4109 msam = self.window.GetMultiSamples() 4110 if not msam: 4111 self.window.SetMultiSamples(16) 4112 else: 4113 self.window.SetMultiSamples(0) 4114 msam = self.window.GetMultiSamples() 4115 if msam: 4116 vedo.printc(f"Antialiasing set to {msam} samples", c=bool(msam)) 4117 else: 4118 vedo.printc("Antialiasing disabled", c=bool(msam)) 4119 4120 elif key == "D": # toggle depthpeeling 4121 udp = not renderer.GetUseDepthPeeling() 4122 renderer.SetUseDepthPeeling(udp) 4123 # self.renderer.SetUseDepthPeelingForVolumes(udp) 4124 if udp: 4125 self.window.SetAlphaBitPlanes(1) 4126 renderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 4127 renderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 4128 self.interactor.Render() 4129 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 4130 rnr = self.renderers.index(renderer) 4131 vedo.printc(f"Depth peeling set to {udp} for renderer nr.{rnr}", c=udp) 4132 if not wasUsed and udp: 4133 vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True) 4134 return 4135 4136 elif key == "period": 4137 if self.picked3d: 4138 self.fly_to(self.picked3d) 4139 return 4140 4141 elif key == "S": 4142 fname = "screenshot.png" 4143 i = 1 4144 while os.path.isfile(fname): 4145 fname = f"screenshot{i}.png" 4146 i += 1 4147 vedo.file_io.screenshot(fname) 4148 vedo.printc(rf":camera: Saved rendering window to {fname}", c="b") 4149 return 4150 4151 elif key == "C": 4152 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 4153 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 4154 # This could happen for plotting geological models with UTM coordinate systems 4155 cam = renderer.GetActiveCamera() 4156 vedo.printc("\n###################################################", c="y") 4157 vedo.printc("## Template python code to position this camera: ##", c="y") 4158 vedo.printc("cam = dict(", c="y") 4159 vedo.printc(" pos=" + utils.precision(cam.GetPosition(), 6) + ",", c="y") 4160 vedo.printc(" focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y") 4161 vedo.printc(" viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y") 4162 vedo.printc(" roll=" + utils.precision(cam.GetRoll(), 6) + ",", c="y") 4163 if cam.GetParallelProjection(): 4164 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 4165 else: 4166 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 4167 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 4168 vedo.printc(')', c='y') 4169 vedo.printc('show(mymeshes, camera=cam)', c='y') 4170 vedo.printc('###################################################', c='y') 4171 return 4172 4173 elif key == "R": 4174 self.reset_viewup() 4175 4176 elif key == "w": 4177 try: 4178 if self.clicked_object.properties.GetRepresentation() == 1: # toggle 4179 self.clicked_object.properties.SetRepresentationToSurface() 4180 else: 4181 self.clicked_object.properties.SetRepresentationToWireframe() 4182 except AttributeError: 4183 pass 4184 4185 elif key == "1": 4186 try: 4187 self._icol += 1 4188 self.clicked_object.mapper.ScalarVisibilityOff() 4189 pal = vedo.colors.palettes[vedo.settings.palette % len(vedo.colors.palettes)] 4190 self.clicked_object.c(pal[(self._icol) % 10]) 4191 self.remove(self.clicked_object.scalarbar) 4192 except AttributeError: 4193 pass 4194 4195 elif key == "2": # dark colors 4196 try: 4197 bsc = ["k1", "k2", "k3", "k4", 4198 "b1", "b2", "b3", "b4", 4199 "p1", "p2", "p3", "p4", 4200 "g1", "g2", "g3", "g4", 4201 "r1", "r2", "r3", "r4", 4202 "o1", "o2", "o3", "o4", 4203 "y1", "y2", "y3", "y4"] 4204 self._icol += 1 4205 if self.clicked_object: 4206 self.clicked_object.mapper.ScalarVisibilityOff() 4207 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4208 self.clicked_object.c(newcol) 4209 self.remove(self.clicked_object.scalarbar) 4210 except AttributeError: 4211 pass 4212 4213 elif key == "3": # light colors 4214 try: 4215 bsc = ["k6", "k7", "k8", "k9", 4216 "b6", "b7", "b8", "b9", 4217 "p6", "p7", "p8", "p9", 4218 "g6", "g7", "g8", "g9", 4219 "r6", "r7", "r8", "r9", 4220 "o6", "o7", "o8", "o9", 4221 "y6", "y7", "y8", "y9"] 4222 self._icol += 1 4223 if self.clicked_object: 4224 self.clicked_object.mapper.ScalarVisibilityOff() 4225 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4226 self.clicked_object.c(newcol) 4227 self.remove(self.clicked_object.scalarbar) 4228 except AttributeError: 4229 pass 4230 4231 elif key == "4": # cmap name cycle 4232 ob = self.clicked_object 4233 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4234 return 4235 if not ob.mapper.GetScalarVisibility(): 4236 return 4237 onwhat = ob.mapper.GetScalarModeAsString() # UsePointData/UseCellData 4238 4239 cmap_names = [ 4240 "Accent", 4241 "Paired", 4242 "rainbow", 4243 "rainbow_r", 4244 "Spectral", 4245 "Spectral_r", 4246 "gist_ncar", 4247 "gist_ncar_r", 4248 "viridis", 4249 "viridis_r", 4250 "hot", 4251 "hot_r", 4252 "terrain", 4253 "ocean", 4254 "coolwarm", 4255 "seismic", 4256 "PuOr", 4257 "RdYlGn", 4258 ] 4259 try: 4260 i = cmap_names.index(ob._cmap_name) 4261 if iren.GetShiftKey(): 4262 i -= 1 4263 else: 4264 i += 1 4265 if i >= len(cmap_names): 4266 i = 0 4267 if i < 0: 4268 i = len(cmap_names) - 1 4269 except ValueError: 4270 i = 0 4271 4272 ob._cmap_name = cmap_names[i] 4273 ob.cmap(ob._cmap_name, on=onwhat) 4274 if ob.scalarbar: 4275 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4276 self.remove(ob.scalarbar) 4277 title = ob.scalarbar.GetTitle() 4278 ob.add_scalarbar(title=title) 4279 self.add(ob.scalarbar).render() 4280 elif isinstance(ob.scalarbar, vedo.Assembly): 4281 self.remove(ob.scalarbar) 4282 ob.add_scalarbar3d(title=ob._cmap_name) 4283 self.add(ob.scalarbar) 4284 4285 vedo.printc( 4286 f"Name:'{ob.name}'," if ob.name else "", 4287 f"range:{utils.precision(ob.mapper.GetScalarRange(),3)},", 4288 f"colormap:'{ob._cmap_name}'", c="g", bold=False, 4289 ) 4290 4291 elif key == "5": # cycle pointdata array 4292 ob = self.clicked_object 4293 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4294 return 4295 4296 arrnames = ob.pointdata.keys() 4297 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4298 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4299 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4300 if len(arrnames) == 0: 4301 return 4302 ob.mapper.SetScalarVisibility(1) 4303 4304 if not ob._cmap_name: 4305 ob._cmap_name = "rainbow" 4306 4307 try: 4308 curr_name = ob.dataset.GetPointData().GetScalars().GetName() 4309 i = arrnames.index(curr_name) 4310 if "normals" in curr_name.lower(): 4311 return 4312 if iren.GetShiftKey(): 4313 i -= 1 4314 else: 4315 i += 1 4316 if i >= len(arrnames): 4317 i = 0 4318 if i < 0: 4319 i = len(arrnames) - 1 4320 except (ValueError, AttributeError): 4321 i = 0 4322 4323 ob.cmap(ob._cmap_name, arrnames[i], on="points") 4324 if ob.scalarbar: 4325 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4326 self.remove(ob.scalarbar) 4327 title = ob.scalarbar.GetTitle() 4328 ob.scalarbar = None 4329 ob.add_scalarbar(title=arrnames[i]) 4330 self.add(ob.scalarbar) 4331 elif isinstance(ob.scalarbar, vedo.Assembly): 4332 self.remove(ob.scalarbar) 4333 ob.scalarbar = None 4334 ob.add_scalarbar3d(title=arrnames[i]) 4335 self.add(ob.scalarbar) 4336 else: 4337 vedo.printc( 4338 f"Name:'{ob.name}'," if ob.name else "", 4339 f"active pointdata array: '{arrnames[i]}'", 4340 c="g", 4341 bold=False, 4342 ) 4343 4344 elif key == "6": # cycle celldata array 4345 ob = self.clicked_object 4346 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4347 return 4348 4349 arrnames = ob.celldata.keys() 4350 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4351 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4352 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4353 if len(arrnames) == 0: 4354 return 4355 ob.mapper.SetScalarVisibility(1) 4356 4357 if not ob._cmap_name: 4358 ob._cmap_name = "rainbow" 4359 4360 try: 4361 curr_name = ob.dataset.GetCellData().GetScalars().GetName() 4362 i = arrnames.index(curr_name) 4363 if "normals" in curr_name.lower(): 4364 return 4365 if iren.GetShiftKey(): 4366 i -= 1 4367 else: 4368 i += 1 4369 if i >= len(arrnames): 4370 i = 0 4371 if i < 0: 4372 i = len(arrnames) - 1 4373 except (ValueError, AttributeError): 4374 i = 0 4375 4376 ob.cmap(ob._cmap_name, arrnames[i], on="cells") 4377 if ob.scalarbar: 4378 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4379 self.remove(ob.scalarbar) 4380 title = ob.scalarbar.GetTitle() 4381 ob.scalarbar = None 4382 ob.add_scalarbar(title=arrnames[i]) 4383 self.add(ob.scalarbar) 4384 elif isinstance(ob.scalarbar, vedo.Assembly): 4385 self.remove(ob.scalarbar) 4386 ob.scalarbar = None 4387 ob.add_scalarbar3d(title=arrnames[i]) 4388 self.add(ob.scalarbar) 4389 else: 4390 vedo.printc( 4391 f"Name:'{ob.name}'," if ob.name else "", 4392 f"active celldata array: '{arrnames[i]}'", 4393 c="g", bold=False, 4394 ) 4395 4396 elif key == "7": 4397 bgc = np.array(renderer.GetBackground()).sum() / 3 4398 if bgc <= 0: 4399 bgc = 0.223 4400 elif 0 < bgc < 1: 4401 bgc = 1 4402 else: 4403 bgc = 0 4404 renderer.SetBackground(bgc, bgc, bgc) 4405 4406 elif key == "8": 4407 bg2cols = [ 4408 "lightyellow", 4409 "darkseagreen", 4410 "palegreen", 4411 "steelblue", 4412 "lightblue", 4413 "cadetblue", 4414 "lavender", 4415 "white", 4416 "blackboard", 4417 "black", 4418 ] 4419 bg2name = vedo.get_color_name(renderer.GetBackground2()) 4420 if bg2name in bg2cols: 4421 idx = bg2cols.index(bg2name) 4422 else: 4423 idx = 4 4424 if idx is not None: 4425 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 4426 if not bg2name_next: 4427 renderer.GradientBackgroundOff() 4428 else: 4429 renderer.GradientBackgroundOn() 4430 renderer.SetBackground2(vedo.get_color(bg2name_next)) 4431 4432 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 4433 i = self.renderers.index(renderer) 4434 try: 4435 self.axes_instances[i].EnabledOff() 4436 self.axes_instances[i].SetInteractor(None) 4437 except AttributeError: 4438 # print("Cannot remove widget", [self.axes_instances[i]]) 4439 try: 4440 self.remove(self.axes_instances[i]) 4441 except: 4442 print("Cannot remove axes", [self.axes_instances[i]]) 4443 return 4444 self.axes_instances[i] = None 4445 4446 if not self.axes: 4447 self.axes = 0 4448 if isinstance(self.axes, dict): 4449 self.axes = 1 4450 4451 if key in ["minus", "KP_Subtract"]: 4452 if not self.camera.GetParallelProjection() and self.axes == 0: 4453 self.axes -= 1 # jump ruler doesnt make sense in perspective mode 4454 bns = self.renderer.ComputeVisiblePropBounds() 4455 addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None, bounds=bns) 4456 else: 4457 if not self.camera.GetParallelProjection() and self.axes == 12: 4458 self.axes += 1 # jump ruler doesnt make sense in perspective mode 4459 bns = self.renderer.ComputeVisiblePropBounds() 4460 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns) 4461 self.render() 4462 4463 elif "KP_" in key or key in [ 4464 "Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior" 4465 ]: 4466 asso = { # change axes style 4467 "KP_Insert": 0, "KP_0": 0, "Insert": 0, 4468 "KP_End": 1, "KP_1": 1, "End": 1, 4469 "KP_Down": 2, "KP_2": 2, "Down": 2, 4470 "KP_Next": 3, "KP_3": 3, "Next": 3, 4471 "KP_Left": 4, "KP_4": 4, "Left": 4, 4472 "KP_Begin": 5, "KP_5": 5, "Begin": 5, 4473 "KP_Right": 6, "KP_6": 6, "Right": 6, 4474 "KP_Home": 7, "KP_7": 7, "Home": 7, 4475 "KP_Up": 8, "KP_8": 8, "Up": 8, 4476 "Prior": 9, # on windows OS 4477 } 4478 clickedr = self.renderers.index(renderer) 4479 if key in asso: 4480 if self.axes_instances[clickedr]: 4481 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 4482 self.axes_instances[clickedr].EnabledOff() 4483 else: 4484 try: 4485 renderer.RemoveActor(self.axes_instances[clickedr]) 4486 except: 4487 pass 4488 self.axes_instances[clickedr] = None 4489 bounds = renderer.ComputeVisiblePropBounds() 4490 addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds) 4491 self.interactor.Render() 4492 4493 if key == "O": 4494 renderer.RemoveLight(self._extralight) 4495 self._extralight = None 4496 4497 elif key == "o": 4498 vbb, sizes, _, _ = addons.compute_visible_bounds() 4499 cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2) 4500 if not self._extralight: 4501 vup = renderer.GetActiveCamera().GetViewUp() 4502 pos = cm + utils.vector(vup) * utils.mag(sizes) 4503 self._extralight = addons.Light(pos, focal_point=cm, intensity=0.4) 4504 renderer.AddLight(self._extralight) 4505 vedo.printc("Press 'o' again to rotate light source, or 'O' to remove it.", c='y') 4506 else: 4507 cpos = utils.vector(self._extralight.GetPosition()) 4508 x, y, z = self._extralight.GetPosition() - cm 4509 r, th, ph = transformations.cart2spher(x, y, z) 4510 th += 0.2 4511 if th > np.pi: 4512 th = np.random.random() * np.pi / 2 4513 ph += 0.3 4514 cpos = transformations.spher2cart(r, th, ph).T + cm 4515 self._extralight.SetPosition(cpos) 4516 4517 elif key == "l": 4518 if self.clicked_object in self.get_meshes(): 4519 objs = [self.clicked_object] 4520 else: 4521 objs = self.get_meshes() 4522 for ia in objs: 4523 try: 4524 ev = ia.properties.GetEdgeVisibility() 4525 ia.properties.SetEdgeVisibility(not ev) 4526 ia.properties.SetRepresentationToSurface() 4527 ia.properties.SetLineWidth(0.1) 4528 except AttributeError: 4529 pass 4530 4531 elif key == "k": # lightings 4532 if self.clicked_object in self.get_meshes(): 4533 objs = [self.clicked_object] 4534 else: 4535 objs = self.get_meshes() 4536 shds = ("default", "metallic", "plastic", "shiny", "glossy", "off") 4537 for ia in objs: 4538 try: 4539 lnr = (ia._ligthingnr + 1) % 6 4540 ia.lighting(shds[lnr]) 4541 ia._ligthingnr = lnr 4542 except AttributeError: 4543 pass 4544 4545 elif key == "K": # shading 4546 if self.clicked_object in self.get_meshes(): 4547 objs = [self.clicked_object] 4548 else: 4549 objs = self.get_meshes() 4550 for ia in objs: 4551 if isinstance(ia, vedo.Mesh): 4552 ia.compute_normals(cells=False) 4553 intrp = ia.properties.GetInterpolation() 4554 if intrp > 0: 4555 ia.properties.SetInterpolation(0) # flat 4556 else: 4557 ia.properties.SetInterpolation(2) # phong 4558 4559 elif key == "n": # show normals to an actor 4560 self.remove("added_auto_normals") 4561 if self.clicked_object in self.get_meshes(): 4562 if self.clicked_actor.GetPickable(): 4563 norml = vedo.shapes.NormalLines(self.clicked_object) 4564 norml.name = "added_auto_normals" 4565 self.add(norml) 4566 4567 elif key == "x": 4568 if self.justremoved is None: 4569 if self.clicked_object in self.get_meshes() or isinstance( 4570 self.clicked_object, vtki.vtkAssembly 4571 ): 4572 self.justremoved = self.clicked_actor 4573 self.renderer.RemoveActor(self.clicked_actor) 4574 else: 4575 self.renderer.AddActor(self.justremoved) 4576 self.justremoved = None 4577 4578 elif key == "X": 4579 if self.clicked_object: 4580 if not self.cutter_widget: 4581 self.cutter_widget = addons.BoxCutter(self.clicked_object) 4582 self.add(self.cutter_widget) 4583 vedo.printc("Press i to toggle the cutter on/off", c='g', dim=1) 4584 vedo.printc(" u to flip selection", c='g', dim=1) 4585 vedo.printc(" r to reset cutting planes", c='g', dim=1) 4586 vedo.printc(" Shift+X to close the cutter box widget", c='g', dim=1) 4587 vedo.printc(" Ctrl+S to save the cut section to file.", c='g', dim=1) 4588 else: 4589 self.remove(self.cutter_widget) 4590 self.cutter_widget = None 4591 vedo.printc("Click object and press X to open the cutter box widget.", c='g') 4592 4593 elif key == "E": 4594 vedo.printc(r":camera: Exporting 3D window to file scene.npz", c="b", end="") 4595 vedo.file_io.export_window("scene.npz") 4596 vedo.printc(", try:\n> vedo scene.npz # (this is experimental!)", c="b") 4597 return 4598 4599 elif key == "F": 4600 vedo.file_io.export_window("scene.x3d") 4601 vedo.printc(r":camera: Exporting 3D window to file", c="b", end="") 4602 vedo.file_io.export_window("scene.npz") 4603 vedo.printc(". Try:\n> firefox scene.html", c="b") 4604 4605 # elif key == "G": # not working with last version of k3d 4606 # vedo.file_io.export_window("scene.html") 4607 # vedo.printc(r":camera: Exporting K3D window to file", c="b", end="") 4608 # vedo.file_io.export_window("scene.html") 4609 # vedo.printc(". Try:\n> firefox scene.html", c="b") 4610 4611 elif key == "i": # print info 4612 if self.clicked_object: 4613 print(self.clicked_object) 4614 else: 4615 print(self) 4616 4617 elif key == "I": # print color under the mouse 4618 x, y = iren.GetEventPosition() 4619 self.color_picker([x, y], verbose=True) 4620 4621 elif key == "Y": 4622 if self.clicked_object and self.clicked_object.pipeline: 4623 self.clicked_object.pipeline.show() 4624 4625 if iren: 4626 iren.Render()
Main class to manage objects.
397 def __init__( 398 self, 399 shape=(1, 1), 400 N=None, 401 pos=(0, 0), 402 size="auto", 403 screensize="auto", 404 title="vedo", 405 bg="white", 406 bg2=None, 407 axes=None, 408 sharecam=True, 409 resetcam=True, 410 interactive=None, 411 offscreen=False, 412 qt_widget=None, 413 wx_widget=None, 414 ): 415 """ 416 Arguments: 417 shape : (str, list) 418 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 419 N : (int) 420 number of desired renderers arranged in a grid automatically. 421 pos : (list) 422 (x,y) position in pixels of top-left corner of the rendering window on the screen 423 size : (str, list) 424 size of the rendering window. If 'auto', guess it based on screensize. 425 screensize : (list) 426 physical size of the monitor screen in pixels 427 bg : (color, str) 428 background color or specify jpg image file name with path 429 bg2 : (color) 430 background color of a gradient towards the top 431 title : (str) 432 window title 433 axes : (int) 434 axis type-1 can be fully customized by passing a dictionary. 435 Check `addons.Axes()` for the full list of options. 436 Set the type of axes to be shown: 437 - 0, no axes 438 - 1, draw three gray grid walls 439 - 2, show cartesian axes from (0,0,0) 440 - 3, show positive range of cartesian axes from (0,0,0) 441 - 4, show a triad at bottom left 442 - 5, show a cube at bottom left 443 - 6, mark the corners of the bounding box 444 - 7, draw a 3D ruler at each side of the cartesian axes 445 - 8, show the `vtkCubeAxesActor` object 446 - 9, show the bounding box outLine 447 - 10, show three circles representing the maximum bounding box 448 - 11, show a large grid on the x-y plane 449 - 12, show polar axes 450 - 13, draw a simple ruler at the bottom of the window 451 - 14: draw a camera orientation widget 452 453 sharecam : (bool) 454 if False each renderer will have an independent camera 455 interactive : (bool) 456 if True will stop after show() to allow interaction with the 3d scene 457 offscreen : (bool) 458 if True will not show the rendering window 459 qt_widget : (QVTKRenderWindowInteractor) 460 render in a Qt-Widget using an QVTKRenderWindowInteractor. 461 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 462 """ 463 vedo.plotter_instance = self 464 465 if interactive is None: 466 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 467 self._interactive = interactive 468 # print("interactive", interactive, N, shape) 469 470 self.objects = [] # list of objects to be shown 471 self.clicked_object = None # holds the object that has been clicked 472 self.clicked_actor = None # holds the actor that has been clicked 473 474 self.shape = shape # nr. of subwindows in grid 475 self.axes = axes # show axes type nr. 476 self.title = title # window title 477 self.size = size # window size 478 self.backgrcol = bg # used also by backend notebooks 479 480 self.offscreen= offscreen 481 self.resetcam = resetcam 482 self.sharecam = sharecam # share the same camera if multiple renderers 483 self.pos = pos # used by vedo.file_io 484 485 self.picker = None # hold the vtkPicker object 486 self.picked2d = None # 2d coords of a clicked point on the rendering window 487 self.picked3d = None # 3d coords of a clicked point on an actor 488 489 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 490 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 491 self.interactor = None 492 self.window = None 493 self.renderer = None 494 self.renderers = [] # list of renderers 495 496 # mostly internal stuff: 497 self.hover_legends = [] 498 self.justremoved = None 499 self.axes_instances = [] 500 self.clock = 0 501 self.sliders = [] 502 self.buttons = [] 503 self.widgets = [] 504 self.cutter_widget = None 505 self.hint_widget = None 506 self.background_renderer = None 507 self.last_event = None 508 self.skybox = None 509 self._icol = 0 510 self._clockt0 = time.time() 511 self._extralight = None 512 self._cocoa_initialized = False 513 self._cocoa_process_events = True # make one call in show() 514 self._must_close_now = False 515 516 ##################################################################### 517 if vedo.settings.default_backend == "2d": 518 self.offscreen = True 519 if self.size == "auto": 520 self.size = (800, 600) 521 522 elif vedo.settings.default_backend == "k3d": 523 if self.size == "auto": 524 self.size = (1000, 1000) 525 #################################### 526 return ############################ 527 #################################### 528 529 ############################################################# 530 if screensize == "auto": 531 screensize = (2160, 1440) # TODO: get actual screen size 532 533 # build the rendering window: 534 self.window = vtki.vtkRenderWindow() 535 536 self.window.GlobalWarningDisplayOff() 537 538 if self.title == "vedo": # check if dev version 539 if "dev" in vedo.__version__: 540 self.title = f"vedo ({vedo.__version__})" 541 self.window.SetWindowName(self.title) 542 543 # more vedo.settings 544 if vedo.settings.use_depth_peeling: 545 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 546 self.window.SetMultiSamples(vedo.settings.multi_samples) 547 548 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 549 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 550 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 551 552 ############################################################# 553 if N: # N = number of renderers. Find out the best 554 555 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 556 vedo.logger.warning("having set N, shape is ignored.") 557 558 x, y = screensize 559 nx = int(np.sqrt(int(N * y / x) + 1)) 560 ny = int(np.sqrt(int(N * x / y) + 1)) 561 lm = [ 562 (nx, ny), 563 (nx, ny + 1), 564 (nx - 1, ny), 565 (nx + 1, ny), 566 (nx, ny - 1), 567 (nx - 1, ny + 1), 568 (nx + 1, ny - 1), 569 (nx + 1, ny + 1), 570 (nx - 1, ny - 1), 571 ] 572 ind, minl = 0, 1000 573 for i, m in enumerate(lm): 574 l = m[0] * m[1] 575 if N <= l < minl: 576 ind = i 577 minl = l 578 shape = lm[ind] 579 580 ################################################## 581 if isinstance(shape, str): 582 583 if "|" in shape: 584 if self.size == "auto": 585 self.size = (800, 1200) 586 n = int(shape.split("|")[0]) 587 m = int(shape.split("|")[1]) 588 rangen = reversed(range(n)) 589 rangem = reversed(range(m)) 590 else: 591 if self.size == "auto": 592 self.size = (1200, 800) 593 m = int(shape.split("/")[0]) 594 n = int(shape.split("/")[1]) 595 rangen = range(n) 596 rangem = range(m) 597 598 if n >= m: 599 xsplit = m / (n + m) 600 else: 601 xsplit = 1 - n / (n + m) 602 if vedo.settings.window_splitting_position: 603 xsplit = vedo.settings.window_splitting_position 604 605 for i in rangen: 606 arenderer = vtki.vtkRenderer() 607 if "|" in shape: 608 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 609 else: 610 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 611 self.renderers.append(arenderer) 612 613 for i in rangem: 614 arenderer = vtki.vtkRenderer() 615 616 if "|" in shape: 617 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 618 else: 619 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 620 self.renderers.append(arenderer) 621 622 for r in self.renderers: 623 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 624 625 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 626 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 627 if vedo.settings.use_depth_peeling: 628 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 629 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 630 r.SetUseFXAA(vedo.settings.use_fxaa) 631 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 632 633 r.SetBackground(vedo.get_color(self.backgrcol)) 634 635 self.axes_instances.append(None) 636 637 self.shape = (n + m,) 638 639 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 640 # passing a sequence of dicts for renderers specifications 641 642 if self.size == "auto": 643 self.size = (1000, 800) 644 645 for rd in shape: 646 x0, y0 = rd["bottomleft"] 647 x1, y1 = rd["topright"] 648 bg_ = rd.pop("bg", "white") 649 bg2_ = rd.pop("bg2", None) 650 651 arenderer = vtki.vtkRenderer() 652 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 653 654 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 655 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 656 if vedo.settings.use_depth_peeling: 657 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 658 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 659 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 660 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 661 662 arenderer.SetViewport(x0, y0, x1, y1) 663 arenderer.SetBackground(vedo.get_color(bg_)) 664 if bg2_: 665 arenderer.GradientBackgroundOn() 666 arenderer.SetBackground2(vedo.get_color(bg2_)) 667 668 self.renderers.append(arenderer) 669 self.axes_instances.append(None) 670 671 self.shape = (len(shape),) 672 673 else: 674 675 if isinstance(self.size, str) and self.size == "auto": 676 # figure out a reasonable window size 677 f = 1.5 678 x, y = screensize 679 xs = y / f * shape[1] # because y<x 680 ys = y / f * shape[0] 681 if xs > x / f: # shrink 682 xs = x / f 683 ys = xs / shape[1] * shape[0] 684 if ys > y / f: 685 ys = y / f 686 xs = ys / shape[0] * shape[1] 687 self.size = (int(xs), int(ys)) 688 if shape == (1, 1): 689 self.size = (int(y / f), int(y / f)) # because y<x 690 else: 691 self.size = (self.size[0], self.size[1]) 692 693 try: 694 image_actor = None 695 bgname = str(self.backgrcol).lower() 696 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 697 self.window.SetNumberOfLayers(2) 698 self.background_renderer = vtki.vtkRenderer() 699 self.background_renderer.SetLayer(0) 700 self.background_renderer.InteractiveOff() 701 self.background_renderer.SetBackground(vedo.get_color(bg2)) 702 image_actor = vedo.Image(self.backgrcol).actor 703 self.window.AddRenderer(self.background_renderer) 704 self.background_renderer.AddActor(image_actor) 705 except AttributeError: 706 pass 707 708 for i in reversed(range(shape[0])): 709 for j in range(shape[1]): 710 arenderer = vtki.vtkRenderer() 711 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 712 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 713 714 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 715 if vedo.settings.use_depth_peeling: 716 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 717 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 718 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 719 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 720 721 if image_actor: 722 arenderer.SetLayer(1) 723 724 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 725 if bg2: 726 arenderer.GradientBackgroundOn() 727 arenderer.SetBackground2(vedo.get_color(bg2)) 728 729 x0 = i / shape[0] 730 y0 = j / shape[1] 731 x1 = (i + 1) / shape[0] 732 y1 = (j + 1) / shape[1] 733 arenderer.SetViewport(y0, x0, y1, x1) 734 self.renderers.append(arenderer) 735 self.axes_instances.append(None) 736 self.shape = shape 737 738 if self.renderers: 739 self.renderer = self.renderers[0] 740 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 741 742 ######################################################### 743 if self.qt_widget or self.wx_widget: 744 if self.qt_widget: 745 self.window = self.qt_widget.GetRenderWindow() # overwrite 746 else: 747 self.window = self.wx_widget.GetRenderWindow() 748 self.interactor = self.window.GetInteractor() 749 750 ######################################################### 751 for r in self.renderers: 752 self.window.AddRenderer(r) 753 # set the background gradient if any 754 if vedo.settings.background_gradient_orientation > 0: 755 try: 756 modes = [ 757 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 758 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 759 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 760 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 761 ] 762 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 763 r.GradientBackgroundOn() 764 except AttributeError: 765 pass 766 767 # Backend #################################################### 768 if vedo.settings.default_backend in ["panel", "trame", "k3d"]: 769 return ################ 770 ######################## 771 772 ######################################################### 773 if self.qt_widget or self.wx_widget: 774 self.interactor.SetRenderWindow(self.window) 775 if vedo.settings.enable_default_keyboard_callbacks: 776 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 777 if vedo.settings.enable_default_mouse_callbacks: 778 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 779 return ################ 780 ######################## 781 782 if self.size[0] == "f": # full screen 783 self.size = "fullscreen" 784 self.window.SetFullScreen(True) 785 self.window.BordersOn() 786 else: 787 self.window.SetSize(int(self.size[0]), int(self.size[1])) 788 789 if self.offscreen: 790 if self.axes in (4, 5, 8, 12, 14): 791 self.axes = 0 # does not work with those 792 self.window.SetOffScreenRendering(True) 793 self.interactor = None 794 self._interactive = False 795 return ################ 796 ######################## 797 798 self.window.SetPosition(pos) 799 800 ######################################################### 801 self.interactor = vtki.vtkRenderWindowInteractor() 802 803 self.interactor.SetRenderWindow(self.window) 804 vsty = vtki.new("InteractorStyleTrackballCamera") 805 self.interactor.SetInteractorStyle(vsty) 806 self.interactor.RemoveObservers("CharEvent") 807 808 if vedo.settings.enable_default_keyboard_callbacks: 809 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 810 if vedo.settings.enable_default_mouse_callbacks: 811 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)
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
- 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
.
882 def print(self): 883 """Print information about the current instance.""" 884 print(self.__str__()) 885 return self
Print information about the current instance.
903 def initialize_interactor(self) -> Self: 904 """Initialize the interactor if not already initialized.""" 905 if self.offscreen: 906 return self 907 if self.interactor: 908 if not self.interactor.GetInitialized(): 909 self.interactor.Initialize() 910 self.interactor.RemoveObservers("CharEvent") 911 return self
Initialize the interactor if not already initialized.
913 def process_events(self) -> Self: 914 """Process all pending events.""" 915 self.initialize_interactor() 916 if self.interactor: 917 try: 918 self.interactor.ProcessEvents() 919 except AttributeError: 920 pass 921 return self
Process all pending events.
923 def at(self, nren: int, yren=None) -> Self: 924 """ 925 Select the current renderer number as an int. 926 Can also use the `[nx, ny]` format. 927 """ 928 if utils.is_sequence(nren): 929 if len(nren) == 2: 930 nren, yren = nren 931 else: 932 vedo.logger.error("at() argument must be a single number or a list of two numbers") 933 raise TypeError 934 935 if yren is not None: 936 a, b = self.shape 937 x, y = nren, yren 938 nren_orig = nren 939 nren = x * b + y 940 # print("at (", x, y, ") -> ren", nren) 941 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 942 vedo.logger.error(f"at({nren_orig, yren}) is malformed!") 943 raise RuntimeError 944 945 self.renderer = self.renderers[nren] 946 return self
Select the current renderer number as an int.
Can also use the [nx, ny]
format.
948 def add(self, *objs, at=None) -> Self: 949 """ 950 Append the input objects to the internal list of objects to be shown. 951 952 Arguments: 953 at : (int) 954 add the object at the specified renderer 955 """ 956 ren = self.renderer if at is None else self.renderers[at] 957 958 objs = utils.flatten(objs) 959 for ob in objs: 960 if ob and ob not in self.objects: 961 self.objects.append(ob) 962 963 acts = self._scan_input_return_acts(objs) 964 965 for a in acts: 966 967 if ren: 968 if isinstance(a, vedo.addons.BaseCutter): 969 a.add_to(self) # from cutters 970 continue 971 972 if isinstance(a, vtki.vtkLight): 973 ren.AddLight(a) 974 continue 975 976 try: 977 ren.AddActor(a) 978 except TypeError: 979 ren.AddActor(a.actor) 980 981 try: 982 ir = self.renderers.index(ren) 983 a.rendered_at.add(ir) # might not have rendered_at 984 except (AttributeError, ValueError): 985 pass 986 987 if isinstance(a, vtki.vtkFollower): 988 a.SetCamera(self.camera) 989 elif isinstance(a, vedo.visual.LightKit): 990 a.lightkit.AddLightsToRenderer(ren) 991 992 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
994 def remove(self, *objs, at=None) -> Self: 995 """ 996 Remove input object to the internal list of objects to be shown. 997 998 Objects to be removed can be referenced by their assigned name, 999 1000 Arguments: 1001 at : (int) 1002 remove the object at the specified renderer 1003 """ 1004 # TODO and you can also use wildcards like `*` and `?`. 1005 1006 ren = self.renderer if at is None else self.renderers[at] 1007 1008 objs = [ob for ob in utils.flatten(objs) if ob] 1009 1010 has_str = False 1011 for ob in objs: 1012 if isinstance(ob, str): 1013 has_str = True 1014 break 1015 1016 has_actor = False 1017 for ob in objs: 1018 if hasattr(ob, "actor") and ob.actor: 1019 has_actor = True 1020 break 1021 1022 if has_str or has_actor: 1023 # need to get the actors to search for 1024 for a in self.get_actors(include_non_pickables=True): 1025 # print("PARSING", [a]) 1026 try: 1027 if (a.name and a.name in objs) or a in objs: 1028 objs.append(a) 1029 # if a.name: 1030 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1031 # if any(bools) or a in objs: 1032 # objs.append(a) 1033 # print('a.name',a.name, objs,any(bools)) 1034 except AttributeError: # no .name 1035 # passing the actor so get back the object with .retrieve_object() 1036 try: 1037 vobj = a.retrieve_object() 1038 if (vobj.name and vobj.name in objs) or vobj in objs: 1039 # print('vobj.name', vobj.name) 1040 objs.append(vobj) 1041 except AttributeError: 1042 pass 1043 1044 if ren is None: 1045 return self 1046 ir = self.renderers.index(ren) 1047 1048 ids = [] 1049 for ob in set(objs): 1050 1051 # will remove it from internal list if possible 1052 try: 1053 idx = self.objects.index(ob) 1054 ids.append(idx) 1055 except ValueError: 1056 pass 1057 1058 if ren: ### remove it from the renderer 1059 1060 if isinstance(ob, vedo.addons.BaseCutter): 1061 ob.remove_from(self) # from cutters 1062 continue 1063 1064 try: 1065 ren.RemoveActor(ob) 1066 except TypeError: 1067 try: 1068 ren.RemoveActor(ob.actor) 1069 except AttributeError: 1070 pass 1071 1072 if hasattr(ob, "rendered_at"): 1073 ob.rendered_at.discard(ir) 1074 1075 if hasattr(ob, "scalarbar") and ob.scalarbar: 1076 ren.RemoveActor(ob.scalarbar) 1077 if hasattr(ob, "_caption") and ob._caption: 1078 ren.RemoveActor(ob._caption) 1079 if hasattr(ob, "shadows") and ob.shadows: 1080 for sha in ob.shadows: 1081 ren.RemoveActor(sha.actor) 1082 if hasattr(ob, "trail") and ob.trail: 1083 ren.RemoveActor(ob.trail.actor) 1084 ob.trail_points = [] 1085 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1086 for sha in ob.trail.shadows: 1087 ren.RemoveActor(sha.actor) 1088 1089 elif isinstance(ob, vedo.visual.LightKit): 1090 ob.lightkit.RemoveLightsFromRenderer(ren) 1091 1092 # for i in ids: # WRONG way of doing it! 1093 # del self.objects[i] 1094 # instead we do: 1095 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1096 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
1098 @property 1099 def actors(self): 1100 """Return the list of actors.""" 1101 return [ob.actor for ob in self.objects if hasattr(ob, "actor")]
Return the list of actors.
1103 def remove_lights(self) -> Self: 1104 """Remove all the present lights in the current renderer.""" 1105 if self.renderer: 1106 self.renderer.RemoveAllLights() 1107 return self
Remove all the present lights in the current renderer.
1109 def pop(self, at=None) -> Self: 1110 """ 1111 Remove the last added object from the rendering window. 1112 This method is typically used in loops or callback functions. 1113 """ 1114 if at is not None and not isinstance(at, int): 1115 # wrong usage pitfall 1116 vedo.logger.error("argument of pop() must be an integer") 1117 raise RuntimeError() 1118 1119 if self.objects: 1120 self.remove(self.objects[-1], at) 1121 return self
Remove the last added object from the rendering window. This method is typically used in loops or callback functions.
1123 def render(self, resetcam=False) -> Self: 1124 """Render the scene. This method is typically used in loops or callback functions.""" 1125 1126 if vedo.settings.dry_run_mode >= 2: 1127 return self 1128 1129 if not self.window: 1130 return self 1131 1132 self.initialize_interactor() 1133 1134 if resetcam: 1135 self.renderer.ResetCamera() 1136 1137 self.window.Render() 1138 1139 if self._cocoa_process_events and self.interactor and self.interactor.GetInitialized(): 1140 if "Darwin" in vedo.sys_platform and not self.offscreen: 1141 self.interactor.ProcessEvents() 1142 self._cocoa_process_events = False 1143 return self
Render the scene. This method is typically used in loops or callback functions.
1145 def interactive(self) -> Self: 1146 """ 1147 Start window interaction. 1148 Analogous to `show(..., interactive=True)`. 1149 """ 1150 if vedo.settings.dry_run_mode >= 1: 1151 return self 1152 self.initialize_interactor() 1153 if self.interactor: 1154 # print("self.interactor.Start()") 1155 self.interactor.Start() 1156 # print("self.interactor.Start() done") 1157 if self._must_close_now: 1158 # print("self.interactor.TerminateApp()") 1159 if self.interactor: 1160 self.interactor.GetRenderWindow().Finalize() 1161 self.interactor.TerminateApp() 1162 self.interactor = None 1163 self.window = None 1164 self.renderer = None 1165 self.renderers = [] 1166 self.camera = None 1167 return self
Start window interaction.
Analogous to show(..., interactive=True)
.
1169 def use_depth_peeling(self, at=None, value=True) -> Self: 1170 """ 1171 Specify whether use depth peeling algorithm at this specific renderer 1172 Call this method before the first rendering. 1173 """ 1174 ren = self.renderer if at is None else self.renderers[at] 1175 ren.SetUseDepthPeeling(value) 1176 return self
Specify whether use depth peeling algorithm at this specific renderer Call this method before the first rendering.
1178 def background(self, c1=None, c2=None, at=None, mode=0) -> Union[Self, "np.ndarray"]: 1179 """Set the color of the background for the current renderer. 1180 A different renderer index can be specified by keyword `at`. 1181 1182 Arguments: 1183 c1 : (list) 1184 background main color. 1185 c2 : (list) 1186 background color for the upper part of the window. 1187 at : (int) 1188 renderer index. 1189 mode : (int) 1190 background mode (needs vtk version >= 9.3) 1191 0 = vertical, 1192 1 = horizontal, 1193 2 = radial farthest side, 1194 3 = radia farthest corner. 1195 """ 1196 if not self.renderers: 1197 return self 1198 r = self.renderer if at is None else self.renderers[at] 1199 1200 if c1 is None and c2 is None: 1201 return np.array(r.GetBackground()) 1202 1203 if r: 1204 if c1 is not None: 1205 r.SetBackground(vedo.get_color(c1)) 1206 if c2 is not None: 1207 r.GradientBackgroundOn() 1208 r.SetBackground2(vedo.get_color(c2)) 1209 if mode: 1210 try: # only works with vtk>=9.3 1211 modes = [ 1212 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1213 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1214 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1215 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1216 ] 1217 r.SetGradientMode(modes[mode]) 1218 except AttributeError: 1219 pass 1220 1221 else: 1222 r.GradientBackgroundOff() 1223 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.
1226 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1227 """ 1228 Return a list of Meshes from the specified renderer. 1229 1230 Arguments: 1231 at : (int) 1232 specify which renderer to look at. 1233 include_non_pickables : (bool) 1234 include non-pickable objects 1235 unpack_assemblies : (bool) 1236 unpack assemblies into their components 1237 """ 1238 if at is None: 1239 renderer = self.renderer 1240 at = self.renderers.index(renderer) 1241 elif isinstance(at, int): 1242 renderer = self.renderers[at] 1243 1244 has_global_axes = False 1245 if isinstance(self.axes_instances[at], vedo.Assembly): 1246 has_global_axes = True 1247 1248 if unpack_assemblies: 1249 acs = renderer.GetActors() 1250 else: 1251 acs = renderer.GetViewProps() 1252 1253 objs = [] 1254 acs.InitTraversal() 1255 for _ in range(acs.GetNumberOfItems()): 1256 1257 if unpack_assemblies: 1258 a = acs.GetNextItem() 1259 else: 1260 a = acs.GetNextProp() 1261 1262 if isinstance(a, vtki.vtkVolume): 1263 continue 1264 1265 if include_non_pickables or a.GetPickable(): 1266 if a == self.axes_instances[at]: 1267 continue 1268 if has_global_axes and a in self.axes_instances[at].actors: 1269 continue 1270 try: 1271 objs.append(a.retrieve_object()) 1272 except AttributeError: 1273 pass 1274 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
1276 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1277 """ 1278 Return a list of Volumes from the specified renderer. 1279 1280 Arguments: 1281 at : (int) 1282 specify which renderer to look at 1283 include_non_pickables : (bool) 1284 include non-pickable objects 1285 """ 1286 renderer = self.renderer if at is None else self.renderers[at] 1287 1288 vols = [] 1289 acs = renderer.GetVolumes() 1290 acs.InitTraversal() 1291 for _ in range(acs.GetNumberOfItems()): 1292 a = acs.GetNextItem() 1293 if include_non_pickables or a.GetPickable(): 1294 try: 1295 vols.append(a.retrieve_object()) 1296 except AttributeError: 1297 pass 1298 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
1300 def get_actors(self, at=None, include_non_pickables=False) -> list: 1301 """ 1302 Return a list of Volumes from the specified renderer. 1303 1304 Arguments: 1305 at : (int) 1306 specify which renderer to look at 1307 include_non_pickables : (bool) 1308 include non-pickable objects 1309 """ 1310 renderer = self.renderer if at is None else self.renderers[at] 1311 if renderer is None: 1312 return [] 1313 1314 acts = [] 1315 acs = renderer.GetViewProps() 1316 acs.InitTraversal() 1317 for _ in range(acs.GetNumberOfItems()): 1318 a = acs.GetNextProp() 1319 if include_non_pickables or a.GetPickable(): 1320 acts.append(a) 1321 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
1323 def check_actors_trasform(self, at=None) -> Self: 1324 """ 1325 Reset the transformation matrix of all actors at specified renderer. 1326 This is only useful when actors have been moved/rotated/scaled manually 1327 in an already rendered scene using interactors like 1328 'TrackballActor' or 'JoystickActor'. 1329 """ 1330 # see issue https://github.com/marcomusy/vedo/issues/1046 1331 for a in self.get_actors(at=at, include_non_pickables=True): 1332 try: 1333 M = a.GetMatrix() 1334 except AttributeError: 1335 continue 1336 if M and not M.IsIdentity(): 1337 try: 1338 a.retrieve_object().apply_transform_from_actor() 1339 # vedo.logger.info( 1340 # f"object '{a.retrieve_object().name}' " 1341 # "was manually moved. Updated to its current position." 1342 # ) 1343 except AttributeError: 1344 pass 1345 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'.
1347 def reset_camera(self, tight=None) -> Self: 1348 """ 1349 Reset the camera position and zooming. 1350 If tight (float) is specified the zooming reserves a padding space 1351 in the xy-plane expressed in percent of the average size. 1352 """ 1353 if tight is None: 1354 self.renderer.ResetCamera() 1355 else: 1356 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1357 cam = self.camera 1358 1359 self.renderer.ComputeAspect() 1360 aspect = self.renderer.GetAspect() 1361 angle = np.pi * cam.GetViewAngle() / 180.0 1362 dx = x1 - x0 1363 dy = y1 - y0 1364 dist = max(dx / aspect[0], dy) / np.tan(angle / 2) / 2 1365 1366 cam.SetViewUp(0, 1, 0) 1367 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1368 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1369 if cam.GetParallelProjection(): 1370 ps = max(dx / aspect[0], dy) / 2 1371 cam.SetParallelScale(ps * (1 + tight)) 1372 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1373 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.
1375 def reset_clipping_range(self, bounds=None) -> Self: 1376 """ 1377 Reset the camera clipping range to include all visible actors. 1378 If bounds is given, it will be used instead of computing it. 1379 """ 1380 if bounds is None: 1381 self.renderer.ResetCameraClippingRange() 1382 else: 1383 self.renderer.ResetCameraClippingRange(bounds) 1384 return self
Reset the camera clipping range to include all visible actors. If bounds is given, it will be used instead of computing it.
1386 def reset_viewup(self, smooth=True) -> Self: 1387 """ 1388 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1389 """ 1390 vbb = addons.compute_visible_bounds()[0] 1391 x0, x1, y0, y1, z0, z1 = vbb 1392 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1393 d = self.camera.GetDistance() 1394 1395 viewups = np.array( 1396 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1397 ) 1398 positions = np.array( 1399 [ 1400 (mx, my, mz + d), 1401 (mx, my, mz - d), 1402 (mx, my + d, mz), 1403 (mx, my - d, mz), 1404 (mx + d, my, mz), 1405 (mx - d, my, mz), 1406 ] 1407 ) 1408 1409 vu = np.array(self.camera.GetViewUp()) 1410 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1411 1412 poc = np.array(self.camera.GetPosition()) 1413 foc = np.array(self.camera.GetFocalPoint()) 1414 a = poc - foc 1415 b = positions - foc 1416 a = a / np.linalg.norm(a) 1417 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1418 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1419 1420 if smooth: 1421 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1422 for t in outtimes: 1423 vv = vu * (1 - t) + viewups[vui] * t 1424 pp = poc * (1 - t) + positions[pui] * t 1425 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1426 self.camera.SetViewUp(vv) 1427 self.camera.SetPosition(pp) 1428 self.camera.SetFocalPoint(ff) 1429 self.render() 1430 1431 # interpolator does not respect parallel view...: 1432 # cam1 = dict( 1433 # pos=poc, 1434 # viewup=vu, 1435 # focal_point=(mx,my,mz), 1436 # clipping_range=self.camera.GetClippingRange() 1437 # ) 1438 # # cam1 = self.camera 1439 # cam2 = dict( 1440 # pos=positions[pui], 1441 # viewup=viewups[vui], 1442 # focal_point=(mx,my,mz), 1443 # clipping_range=self.camera.GetClippingRange() 1444 # ) 1445 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1446 # for c in vcams: 1447 # self.renderer.SetActiveCamera(c) 1448 # self.render() 1449 else: 1450 1451 self.camera.SetViewUp(viewups[vui]) 1452 self.camera.SetPosition(positions[pui]) 1453 self.camera.SetFocalPoint(mx, my, mz) 1454 1455 self.renderer.ResetCameraClippingRange() 1456 1457 # vbb, _, _, _ = addons.compute_visible_bounds() 1458 # x0,x1, y0,y1, z0,z1 = vbb 1459 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1460 self.render() 1461 return self
Reset the orientation of the camera to the closest orthogonal direction and view-up.
1463 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1464 """ 1465 Takes as input two cameras set camera at an interpolated position: 1466 1467 Cameras can be vtkCamera or dictionaries in format: 1468 1469 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1470 1471 Press `shift-C` key in interactive mode to dump a python snipplet 1472 of parameters for the current camera view. 1473 """ 1474 nc = len(cameras) 1475 if len(times) == 0: 1476 times = np.linspace(0, 1, num=nc, endpoint=True) 1477 1478 assert len(times) == nc 1479 1480 cin = vtki.new("CameraInterpolator") 1481 1482 # cin.SetInterpolationTypeToLinear() # buggy? 1483 if nc > 2 and smooth: 1484 cin.SetInterpolationTypeToSpline() 1485 1486 for i, cam in enumerate(cameras): 1487 vcam = cam 1488 if isinstance(cam, dict): 1489 vcam = utils.camera_from_dict(cam) 1490 cin.AddCamera(times[i], vcam) 1491 1492 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1493 rng = maxt - mint 1494 1495 if len(output_times) == 0: 1496 cin.InterpolateCamera(t * rng, self.camera) 1497 return [self.camera] 1498 else: 1499 vcams = [] 1500 for tt in output_times: 1501 c = vtki.vtkCamera() 1502 cin.InterpolateCamera(tt * rng, c) 1503 vcams.append(c) 1504 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.
1506 def fly_to(self, point) -> Self: 1507 """ 1508 Fly camera to the specified point. 1509 1510 Arguments: 1511 point : (list) 1512 point in space to place camera. 1513 1514 Example: 1515 ```python 1516 from vedo import * 1517 cone = Cone() 1518 plt = Plotter(axes=1) 1519 plt.show(cone) 1520 plt.fly_to([1,0,0]) 1521 plt.interactive().close() 1522 ``` 1523 """ 1524 if self.interactor: 1525 self.resetcam = False 1526 self.interactor.FlyTo(self.renderer, point) 1527 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()
1529 def look_at(self, plane="xy") -> Self: 1530 """Move the camera so that it looks at the specified cartesian plane""" 1531 cam = self.renderer.GetActiveCamera() 1532 fp = np.array(cam.GetFocalPoint()) 1533 p = np.array(cam.GetPosition()) 1534 dist = np.linalg.norm(fp - p) 1535 plane = plane.lower() 1536 if "x" in plane and "y" in plane: 1537 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1538 cam.SetViewUp(0.0, 1.0, 0.0) 1539 elif "x" in plane and "z" in plane: 1540 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1541 cam.SetViewUp(0.0, 0.0, 1.0) 1542 elif "y" in plane and "z" in plane: 1543 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1544 cam.SetViewUp(0.0, 0.0, 1.0) 1545 else: 1546 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1547 return self
Move the camera so that it looks at the specified cartesian plane
1549 def record(self, filename="") -> str: 1550 """ 1551 Record camera, mouse, keystrokes and all other events. 1552 Recording can be toggled on/off by pressing key "R". 1553 1554 Arguments: 1555 filename : (str) 1556 ascii file to store events. 1557 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1558 1559 Returns: 1560 a string descriptor of events. 1561 1562 Examples: 1563 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1564 """ 1565 if vedo.settings.dry_run_mode >= 1: 1566 return "" 1567 if not self.interactor: 1568 vedo.logger.warning("Cannot record events, no interactor defined.") 1569 return "" 1570 erec = vtki.new("InteractorEventRecorder") 1571 erec.SetInteractor(self.interactor) 1572 if not filename: 1573 if not os.path.exists(vedo.settings.cache_directory): 1574 os.makedirs(vedo.settings.cache_directory) 1575 home_dir = os.path.expanduser("~") 1576 filename = os.path.join( 1577 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1578 print("Events will be recorded in", filename) 1579 erec.SetFileName(filename) 1580 erec.SetKeyPressActivationValue("R") 1581 erec.EnabledOn() 1582 erec.Record() 1583 self.interactor.Start() 1584 erec.Stop() 1585 erec.EnabledOff() 1586 with open(filename, "r", encoding="UTF-8") as fl: 1587 events = fl.read() 1588 erec = None 1589 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:
1591 def play(self, recorded_events="", repeats=0) -> Self: 1592 """ 1593 Play camera, mouse, keystrokes and all other events. 1594 1595 Arguments: 1596 events : (str) 1597 file o string of events. 1598 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1599 repeats : (int) 1600 number of extra repeats of the same events. The default is 0. 1601 1602 Examples: 1603 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1604 """ 1605 if vedo.settings.dry_run_mode >= 1: 1606 return self 1607 if not self.interactor: 1608 vedo.logger.warning("Cannot play events, no interactor defined.") 1609 return self 1610 1611 erec = vtki.new("InteractorEventRecorder") 1612 erec.SetInteractor(self.interactor) 1613 1614 if not recorded_events: 1615 home_dir = os.path.expanduser("~") 1616 recorded_events = os.path.join( 1617 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1618 1619 if recorded_events.endswith(".log"): 1620 erec.ReadFromInputStringOff() 1621 erec.SetFileName(recorded_events) 1622 else: 1623 erec.ReadFromInputStringOn() 1624 erec.SetInputString(recorded_events) 1625 1626 erec.Play() 1627 for _ in range(repeats): 1628 erec.Rewind() 1629 erec.Play() 1630 erec.EnabledOff() 1631 erec = None 1632 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:
1634 def parallel_projection(self, value=True, at=None) -> Self: 1635 """ 1636 Use parallel projection `at` a specified renderer. 1637 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1638 An input value equal to -1 will toggle it on/off. 1639 """ 1640 r = self.renderer if at is None else self.renderers[at] 1641 1642 if value == -1: 1643 val = r.GetActiveCamera().GetParallelProjection() 1644 value = not val 1645 r.GetActiveCamera().SetParallelProjection(value) 1646 r.Modified() 1647 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.
1654 def fov(self, angle: float) -> Self: 1655 """ 1656 Set the field of view angle for the camera. 1657 This is the angle of the camera frustum in the horizontal direction. 1658 High values will result in a wide-angle lens (fish-eye effect), 1659 and low values will result in a telephoto lens. 1660 1661 Default value is 30 degrees. 1662 """ 1663 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1664 self.renderer.GetActiveCamera().SetViewAngle(angle) 1665 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.
1667 def zoom(self, zoom: float) -> Self: 1668 """Apply a zooming factor for the current camera view""" 1669 self.renderer.GetActiveCamera().Zoom(zoom) 1670 return self
Apply a zooming factor for the current camera view
1672 def azimuth(self, angle: float) -> Self: 1673 """Rotate camera around the view up vector.""" 1674 self.renderer.GetActiveCamera().Azimuth(angle) 1675 return self
Rotate camera around the view up vector.
1677 def elevation(self, angle: float) -> Self: 1678 """Rotate the camera around the cross product of the negative 1679 of the direction of projection and the view up vector.""" 1680 self.renderer.GetActiveCamera().Elevation(angle) 1681 return self
Rotate the camera around the cross product of the negative of the direction of projection and the view up vector.
1683 def roll(self, angle: float) -> Self: 1684 """Roll the camera about the direction of projection.""" 1685 self.renderer.GetActiveCamera().Roll(angle) 1686 return self
Roll the camera about the direction of projection.
1688 def dolly(self, value: float) -> Self: 1689 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1690 self.renderer.GetActiveCamera().Dolly(value) 1691 return self
Move the camera towards (value>0) or away from (value<0) the focal point.
1694 def add_slider( 1695 self, 1696 sliderfunc, 1697 xmin, 1698 xmax, 1699 value=None, 1700 pos=4, 1701 title="", 1702 font="Calco", 1703 title_size=1, 1704 c=None, 1705 alpha=1, 1706 show_value=True, 1707 delayed=False, 1708 **options, 1709 ) -> "vedo.addons.Slider2D": 1710 """ 1711 Add a `vedo.addons.Slider2D` which can call an external custom function. 1712 1713 Arguments: 1714 sliderfunc : (Callable) 1715 external function to be called by the widget 1716 xmin : (float) 1717 lower value of the slider 1718 xmax : (float) 1719 upper value 1720 value : (float) 1721 current value 1722 pos : (list, str) 1723 position corner number: horizontal [1-5] or vertical [11-15] 1724 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1725 and also by a string descriptor (eg. "bottom-left") 1726 title : (str) 1727 title text 1728 font : (str) 1729 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1730 title_size : (float) 1731 title text scale [1.0] 1732 show_value : (bool) 1733 if True current value is shown 1734 delayed : (bool) 1735 if True the callback is delayed until when the mouse button is released 1736 alpha : (float) 1737 opacity of the scalar bar texts 1738 slider_length : (float) 1739 slider length 1740 slider_width : (float) 1741 slider width 1742 end_cap_length : (float) 1743 length of the end cap 1744 end_cap_width : (float) 1745 width of the end cap 1746 tube_width : (float) 1747 width of the tube 1748 title_height : (float) 1749 width of the title 1750 tformat : (str) 1751 format of the title 1752 1753 Examples: 1754 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1755 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1756 1757  1758 """ 1759 if c is None: # automatic black or white 1760 c = (0.8, 0.8, 0.8) 1761 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1762 c = (0.2, 0.2, 0.2) 1763 else: 1764 c = vedo.get_color(c) 1765 1766 slider2d = addons.Slider2D( 1767 sliderfunc, 1768 xmin, 1769 xmax, 1770 value, 1771 pos, 1772 title, 1773 font, 1774 title_size, 1775 c, 1776 alpha, 1777 show_value, 1778 delayed, 1779 **options, 1780 ) 1781 1782 if self.renderer: 1783 slider2d.renderer = self.renderer 1784 if self.interactor: 1785 slider2d.interactor = self.interactor 1786 slider2d.on() 1787 self.sliders.append([slider2d, sliderfunc]) 1788 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:
1790 def add_slider3d( 1791 self, 1792 sliderfunc, 1793 pos1, 1794 pos2, 1795 xmin, 1796 xmax, 1797 value=None, 1798 s=0.03, 1799 t=1, 1800 title="", 1801 rotation=0.0, 1802 c=None, 1803 show_value=True, 1804 ) -> "vedo.addons.Slider3D": 1805 """ 1806 Add a 3D slider widget which can call an external custom function. 1807 1808 Arguments: 1809 sliderfunc : (function) 1810 external function to be called by the widget 1811 pos1 : (list) 1812 first position 3D coordinates 1813 pos2 : (list) 1814 second position coordinates 1815 xmin : (float) 1816 lower value 1817 xmax : (float) 1818 upper value 1819 value : (float) 1820 initial value 1821 s : (float) 1822 label scaling factor 1823 t : (float) 1824 tube scaling factor 1825 title : (str) 1826 title text 1827 c : (color) 1828 slider color 1829 rotation : (float) 1830 title rotation around slider axis 1831 show_value : (bool) 1832 if True current value is shown 1833 1834 Examples: 1835 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1836 1837  1838 """ 1839 if c is None: # automatic black or white 1840 c = (0.8, 0.8, 0.8) 1841 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1842 c = (0.2, 0.2, 0.2) 1843 else: 1844 c = vedo.get_color(c) 1845 1846 slider3d = addons.Slider3D( 1847 sliderfunc, 1848 pos1, 1849 pos2, 1850 xmin, 1851 xmax, 1852 value, 1853 s, 1854 t, 1855 title, 1856 rotation, 1857 c, 1858 show_value, 1859 ) 1860 slider3d.renderer = self.renderer 1861 slider3d.interactor = self.interactor 1862 slider3d.on() 1863 self.sliders.append([slider3d, sliderfunc]) 1864 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:
1923 def add_spline_tool( 1924 self, 1925 points, 1926 pc="k", 1927 ps=8, 1928 lc="r4", 1929 ac="g5", 1930 lw=2, 1931 alpha=1, 1932 closed=False, 1933 ontop=True, 1934 can_add_nodes=True, 1935 ) -> "vedo.addons.SplineTool": 1936 """ 1937 Add a spline tool to the current plotter. 1938 Nodes of the spline can be dragged in space with the mouse. 1939 Clicking on the line itself adds an extra point. 1940 Selecting a point and pressing del removes it. 1941 1942 Arguments: 1943 points : (Mesh, Points, array) 1944 the set of coordinates forming the spline nodes. 1945 pc : (str) 1946 point color. The default is 'k'. 1947 ps : (str) 1948 point size. The default is 8. 1949 lc : (str) 1950 line color. The default is 'r4'. 1951 ac : (str) 1952 active point marker color. The default is 'g5'. 1953 lw : (int) 1954 line width. The default is 2. 1955 alpha : (float) 1956 line transparency. 1957 closed : (bool) 1958 spline is meant to be closed. The default is False. 1959 1960 Returns: 1961 a `SplineTool` object. 1962 1963 Examples: 1964 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1965 1966  1967 """ 1968 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1969 sw.interactor = self.interactor 1970 sw.on() 1971 sw.Initialize(sw.points.dataset) 1972 sw.representation.SetRenderer(self.renderer) 1973 sw.representation.SetClosedLoop(closed) 1974 sw.representation.BuildRepresentation() 1975 self.widgets.append(sw) 1976 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 coordinates 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:
1978 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1979 """Add an inset icon mesh into the same renderer. 1980 1981 Arguments: 1982 pos : (int, list) 1983 icon position in the range [1-4] indicating one of the 4 corners, 1984 or it can be a tuple (x,y) as a fraction of the renderer size. 1985 size : (float) 1986 size of the square inset. 1987 1988 Examples: 1989 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1990 """ 1991 iconw = addons.Icon(icon, pos, size) 1992 1993 iconw.SetInteractor(self.interactor) 1994 iconw.EnabledOn() 1995 iconw.InteractiveOff() 1996 self.widgets.append(iconw) 1997 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:
1999 def add_global_axes(self, axtype=None, c=None) -> Self: 2000 """Draw axes on scene. Available axes types: 2001 2002 Arguments: 2003 axtype : (int) 2004 - 0, no axes, 2005 - 1, draw three gray grid walls 2006 - 2, show cartesian axes from (0,0,0) 2007 - 3, show positive range of cartesian axes from (0,0,0) 2008 - 4, show a triad at bottom left 2009 - 5, show a cube at bottom left 2010 - 6, mark the corners of the bounding box 2011 - 7, draw a 3D ruler at each side of the cartesian axes 2012 - 8, show the vtkCubeAxesActor object 2013 - 9, show the bounding box outLine 2014 - 10, show three circles representing the maximum bounding box 2015 - 11, show a large grid on the x-y plane 2016 - 12, show polar axes 2017 - 13, draw a simple ruler at the bottom of the window 2018 2019 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 2020 2021 Example: 2022 ```python 2023 from vedo import Box, show 2024 b = Box(pos=(0, 0, 0), size=(80, 90, 70)).alpha(0.1) 2025 show( 2026 b, 2027 axes={ 2028 "xtitle": "Some long variable [a.u.]", 2029 "number_of_divisions": 4, 2030 # ... 2031 }, 2032 ) 2033 ``` 2034 2035 Examples: 2036 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2037 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2038 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2039 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2040 2041 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2042 """ 2043 addons.add_global_axes(axtype, c) 2044 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), size=(80, 90, 70)).alpha(0.1) show( b, axes={ "xtitle": "Some long variable [a.u.]", "number_of_divisions": 4, # ... }, )
Examples:
2046 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2047 """Add a legend to the top right. 2048 2049 Examples: 2050 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/legendbox.py), 2051 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/other/flag_labels1.py) 2052 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/other/flag_labels2.py) 2053 """ 2054 acts = self.get_meshes() 2055 lb = addons.LegendBox(acts, **kwargs) 2056 self.add(lb) 2057 return lb
2059 def add_hint( 2060 self, 2061 obj, 2062 text="", 2063 c="k", 2064 bg="yellow9", 2065 font="Calco", 2066 size=18, 2067 justify=0, 2068 angle=0, 2069 delay=250, 2070 ) -> Union[vtki.vtkBalloonWidget, None]: 2071 """ 2072 Create a pop-up hint style message when hovering an object. 2073 Use `add_hint(obj, False)` to disable a hinting a specific object. 2074 Use `add_hint(None)` to disable all hints. 2075 2076 Arguments: 2077 obj : (Mesh, Points) 2078 the object to associate the pop-up to 2079 text : (str) 2080 string description of the pop-up 2081 delay : (int) 2082 milliseconds to wait before pop-up occurs 2083 """ 2084 if self.offscreen or not self.interactor: 2085 return None 2086 2087 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2088 # Linux vtk9.0 is bugged 2089 vedo.logger.warning( 2090 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2091 ) 2092 return None 2093 2094 if obj is None: 2095 self.hint_widget.EnabledOff() 2096 self.hint_widget.SetInteractor(None) 2097 self.hint_widget = None 2098 return self.hint_widget 2099 2100 if text is False and self.hint_widget: 2101 self.hint_widget.RemoveBalloon(obj) 2102 return self.hint_widget 2103 2104 if text == "": 2105 if obj.name: 2106 text = obj.name 2107 elif obj.filename: 2108 text = obj.filename 2109 else: 2110 return None 2111 2112 if not self.hint_widget: 2113 self.hint_widget = vtki.vtkBalloonWidget() 2114 2115 rep = self.hint_widget.GetRepresentation() 2116 rep.SetBalloonLayoutToImageRight() 2117 2118 trep = rep.GetTextProperty() 2119 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2120 trep.SetFontFile(utils.get_font_path(font)) 2121 trep.SetFontSize(size) 2122 trep.SetColor(vedo.get_color(c)) 2123 trep.SetBackgroundColor(vedo.get_color(bg)) 2124 trep.SetShadow(0) 2125 trep.SetJustification(justify) 2126 trep.UseTightBoundingBoxOn() 2127 2128 self.hint_widget.ManagesCursorOff() 2129 self.hint_widget.SetTimerDuration(delay) 2130 self.hint_widget.SetInteractor(self.interactor) 2131 if angle: 2132 trep.SetOrientation(angle) 2133 trep.SetBackgroundOpacity(0) 2134 # else: 2135 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2136 self.hint_widget.SetRepresentation(rep) 2137 self.widgets.append(self.hint_widget) 2138 self.hint_widget.EnabledOn() 2139 2140 bst = self.hint_widget.GetBalloonString(obj.actor) 2141 if bst: 2142 self.hint_widget.UpdateBalloonString(obj.actor, text) 2143 else: 2144 self.hint_widget.AddBalloon(obj.actor, text) 2145 2146 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
2148 def add_shadows(self) -> Self: 2149 """Add shadows at the current renderer.""" 2150 if self.renderer: 2151 shadows = vtki.new("ShadowMapPass") 2152 seq = vtki.new("SequencePass") 2153 passes = vtki.new("RenderPassCollection") 2154 passes.AddItem(shadows.GetShadowMapBakerPass()) 2155 passes.AddItem(shadows) 2156 seq.SetPasses(passes) 2157 camerapass = vtki.new("CameraPass") 2158 camerapass.SetDelegatePass(seq) 2159 self.renderer.SetPass(camerapass) 2160 return self
Add shadows at the current renderer.
2162 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> Self: 2163 """ 2164 Screen Space Ambient Occlusion. 2165 2166 For every pixel on the screen, the pixel shader samples the depth values around 2167 the current pixel and tries to compute the amount of occlusion from each of the sampled 2168 points. 2169 2170 Arguments: 2171 radius : (float) 2172 radius of influence in absolute units 2173 bias : (float) 2174 bias of the normals 2175 blur : (bool) 2176 add a blurring to the sampled positions 2177 samples : (int) 2178 number of samples to probe 2179 2180 Examples: 2181 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2182 2183  2184 """ 2185 lights = vtki.new("LightsPass") 2186 2187 opaque = vtki.new("OpaquePass") 2188 2189 ssaoCam = vtki.new("CameraPass") 2190 ssaoCam.SetDelegatePass(opaque) 2191 2192 ssao = vtki.new("SSAOPass") 2193 ssao.SetRadius(radius) 2194 ssao.SetBias(bias) 2195 ssao.SetBlur(blur) 2196 ssao.SetKernelSize(samples) 2197 ssao.SetDelegatePass(ssaoCam) 2198 2199 translucent = vtki.new("TranslucentPass") 2200 2201 volpass = vtki.new("VolumetricPass") 2202 ddp = vtki.new("DualDepthPeelingPass") 2203 ddp.SetTranslucentPass(translucent) 2204 ddp.SetVolumetricPass(volpass) 2205 2206 over = vtki.new("OverlayPass") 2207 2208 collection = vtki.new("RenderPassCollection") 2209 collection.AddItem(lights) 2210 collection.AddItem(ssao) 2211 collection.AddItem(ddp) 2212 collection.AddItem(over) 2213 2214 sequence = vtki.new("SequencePass") 2215 sequence.SetPasses(collection) 2216 2217 cam = vtki.new("CameraPass") 2218 cam.SetDelegatePass(sequence) 2219 2220 self.renderer.SetPass(cam) 2221 return self
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:
2223 def add_depth_of_field(self, autofocus=True) -> Self: 2224 """Add a depth of field effect in the scene.""" 2225 lights = vtki.new("LightsPass") 2226 2227 opaque = vtki.new("OpaquePass") 2228 2229 dofCam = vtki.new("CameraPass") 2230 dofCam.SetDelegatePass(opaque) 2231 2232 dof = vtki.new("DepthOfFieldPass") 2233 dof.SetAutomaticFocalDistance(autofocus) 2234 dof.SetDelegatePass(dofCam) 2235 2236 collection = vtki.new("RenderPassCollection") 2237 collection.AddItem(lights) 2238 collection.AddItem(dof) 2239 2240 sequence = vtki.new("SequencePass") 2241 sequence.SetPasses(collection) 2242 2243 cam = vtki.new("CameraPass") 2244 cam.SetDelegatePass(sequence) 2245 2246 self.renderer.SetPass(cam) 2247 return self
Add a depth of field effect in the scene.
2278 def add_renderer_frame(self, 2279 c=None, alpha=None, lw=None, 2280 padding=None, pattern="brtl") -> "vedo.addons.RendererFrame": 2281 """ 2282 Add a frame to the renderer subwindow. 2283 2284 Arguments: 2285 c : (color) 2286 color name or index 2287 alpha : (float) 2288 opacity level 2289 lw : (int) 2290 line width in pixels. 2291 padding : (float) 2292 padding space in pixels. 2293 pattern : (str) 2294 a string made of characters 'b', 'r', 't', 'l' 2295 to show the frame line at the bottom, right, top, left. 2296 """ 2297 if c is None: # automatic black or white 2298 c = (0.9, 0.9, 0.9) 2299 if self.renderer: 2300 if np.sum(self.renderer.GetBackground()) > 1.5: 2301 c = (0.1, 0.1, 0.1) 2302 renf = addons.RendererFrame(c, alpha, lw, padding, pattern) 2303 if renf: 2304 self.renderer.AddActor(renf) 2305 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.
- pattern : (str) a string made of characters 'b', 'r', 't', 'l' to show the frame line at the bottom, right, top, left.
2307 def add_hover_legend( 2308 self, 2309 at=None, 2310 c=None, 2311 pos="bottom-left", 2312 font="Calco", 2313 s=0.75, 2314 bg="auto", 2315 alpha=0.1, 2316 maxlength=24, 2317 use_info=False, 2318 ) -> int: 2319 """ 2320 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2321 2322 The created text object are stored in `plotter.hover_legends`. 2323 2324 Returns: 2325 the id of the callback function. 2326 2327 Arguments: 2328 c : (color) 2329 Text color. If None then black or white is chosen automatically 2330 pos : (str) 2331 text positioning 2332 font : (str) 2333 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2334 s : (float) 2335 text size scale 2336 bg : (color) 2337 background color of the 2D box containing the text 2338 alpha : (float) 2339 box transparency 2340 maxlength : (int) 2341 maximum number of characters per line 2342 use_info : (bool) 2343 visualize the content of the `obj.info` attribute 2344 2345 Examples: 2346 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2347 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2348 2349  2350 """ 2351 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2352 2353 if at is None: 2354 at = self.renderers.index(self.renderer) 2355 2356 def _legfunc(evt): 2357 if not evt.object or not self.renderer or at != evt.at: 2358 if hoverlegend.mapper.GetInput(): # clear and return 2359 hoverlegend.mapper.SetInput("") 2360 self.render() 2361 return 2362 2363 if use_info: 2364 if hasattr(evt.object, "info"): 2365 t = str(evt.object.info) 2366 else: 2367 return 2368 else: 2369 t, tp = "", "" 2370 if evt.isMesh: 2371 tp = "Mesh " 2372 elif evt.isPoints: 2373 tp = "Points " 2374 elif evt.isVolume: 2375 tp = "Volume " 2376 elif evt.isImage: 2377 tp = "Image " 2378 elif evt.isAssembly: 2379 tp = "Assembly " 2380 else: 2381 return 2382 2383 if evt.isAssembly: 2384 if not evt.object.name: 2385 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2386 else: 2387 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2388 else: 2389 if evt.object.name: 2390 t += f"{tp}name" 2391 if evt.isPoints: 2392 t += " " 2393 if evt.isMesh: 2394 t += " " 2395 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2396 2397 if evt.object.filename: 2398 t += f"{tp}filename: " 2399 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2400 t += "\n" 2401 if not evt.object.file_size: 2402 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2403 if evt.object.file_size: 2404 t += " : " 2405 sz, created = evt.object.file_size, evt.object.created 2406 t += f"{created[4:-5]} ({sz})" + "\n" 2407 2408 if evt.isPoints: 2409 indata = evt.object.dataset 2410 if indata.GetNumberOfPoints(): 2411 t += ( 2412 f"#points/cells: {indata.GetNumberOfPoints()}" 2413 f" / {indata.GetNumberOfCells()}" 2414 ) 2415 pdata = indata.GetPointData() 2416 cdata = indata.GetCellData() 2417 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2418 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2419 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2420 t += " *" 2421 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2422 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2423 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2424 t += " *" 2425 2426 if evt.isImage: 2427 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2428 t += f"\nImage shape: {evt.object.shape}" 2429 pcol = self.color_picker(evt.picked2d) 2430 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2431 2432 # change box color if needed in 'auto' mode 2433 if evt.isPoints and "auto" in str(bg): 2434 actcol = evt.object.properties.GetColor() 2435 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2436 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2437 2438 # adapt to changes in bg color 2439 bgcol = self.renderers[at].GetBackground() 2440 _bgcol = c 2441 if _bgcol is None: # automatic black or white 2442 _bgcol = (0.9, 0.9, 0.9) 2443 if sum(bgcol) > 1.5: 2444 _bgcol = (0.1, 0.1, 0.1) 2445 if len(set(_bgcol).intersection(bgcol)) < 3: 2446 hoverlegend.color(_bgcol) 2447 2448 if hoverlegend.mapper.GetInput() != t: 2449 hoverlegend.mapper.SetInput(t) 2450 self.interactor.Render() 2451 2452 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2453 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2454 2455 self.add(hoverlegend, at=at) 2456 self.hover_legends.append(hoverlegend) 2457 idcall = self.add_callback("MouseMove", _legfunc) 2458 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:
2460 def add_scale_indicator( 2461 self, 2462 pos=(0.7, 0.05), 2463 s=0.02, 2464 length=2, 2465 lw=4, 2466 c="k1", 2467 alpha=1, 2468 units="", 2469 gap=0.05, 2470 ) -> Union["vedo.visual.Actor2D", None]: 2471 """ 2472 Add a Scale Indicator. Only works in parallel mode (no perspective). 2473 2474 Arguments: 2475 pos : (list) 2476 fractional (x,y) position on the screen. 2477 s : (float) 2478 size of the text. 2479 length : (float) 2480 length of the line. 2481 units : (str) 2482 string to show units. 2483 gap : (float) 2484 separation of line and text. 2485 2486 Example: 2487 ```python 2488 from vedo import settings, Cube, Plotter 2489 settings.use_parallel_projection = True # or else it does not make sense! 2490 cube = Cube().alpha(0.2) 2491 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2492 plt.add_scale_indicator(units='um', c='blue4') 2493 plt.show(cube, "Scale indicator with units").close() 2494 ``` 2495  2496 """ 2497 # Note that this cannot go in addons.py 2498 # because it needs callbacks and window size 2499 if not self.interactor: 2500 return None 2501 2502 ppoints = vtki.vtkPoints() # Generate the polyline 2503 psqr = [[0.0, gap], [length / 10, gap]] 2504 dd = psqr[1][0] - psqr[0][0] 2505 for i, pt in enumerate(psqr): 2506 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2507 lines = vtki.vtkCellArray() 2508 lines.InsertNextCell(len(psqr)) 2509 for i in range(len(psqr)): 2510 lines.InsertCellPoint(i) 2511 pd = vtki.vtkPolyData() 2512 pd.SetPoints(ppoints) 2513 pd.SetLines(lines) 2514 2515 wsx, wsy = self.window.GetSize() 2516 if not self.camera.GetParallelProjection(): 2517 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2518 return None 2519 2520 rlabel = vtki.new("VectorText") 2521 rlabel.SetText("scale") 2522 tf = vtki.new("TransformPolyDataFilter") 2523 tf.SetInputConnection(rlabel.GetOutputPort()) 2524 t = vtki.vtkTransform() 2525 t.Scale(s * wsy / wsx, s, 1) 2526 tf.SetTransform(t) 2527 2528 app = vtki.new("AppendPolyData") 2529 app.AddInputConnection(tf.GetOutputPort()) 2530 app.AddInputData(pd) 2531 2532 mapper = vtki.new("PolyDataMapper2D") 2533 mapper.SetInputConnection(app.GetOutputPort()) 2534 cs = vtki.vtkCoordinate() 2535 cs.SetCoordinateSystem(1) 2536 mapper.SetTransformCoordinate(cs) 2537 2538 fractor = vedo.visual.Actor2D() 2539 csys = fractor.GetPositionCoordinate() 2540 csys.SetCoordinateSystem(3) 2541 fractor.SetPosition(pos) 2542 fractor.SetMapper(mapper) 2543 fractor.GetProperty().SetColor(vedo.get_color(c)) 2544 fractor.GetProperty().SetOpacity(alpha) 2545 fractor.GetProperty().SetLineWidth(lw) 2546 fractor.GetProperty().SetDisplayLocationToForeground() 2547 2548 def sifunc(iren, ev): 2549 wsx, wsy = self.window.GetSize() 2550 ps = self.camera.GetParallelScale() 2551 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2552 if units: 2553 newtxt += " " + units 2554 if rlabel.GetText() != newtxt: 2555 rlabel.SetText(newtxt) 2556 2557 self.renderer.AddActor(fractor) 2558 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2559 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2560 self.interactor.AddObserver("InteractionEvent", sifunc) 2561 sifunc(0, 0) 2562 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()
2564 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2565 """ 2566 Create an Event object with information of what was clicked. 2567 2568 If `enable_picking` is False, no picking will be performed. 2569 This can be useful to avoid double picking when using buttons. 2570 """ 2571 if not self.interactor: 2572 return Event() 2573 2574 if len(pos) > 0: 2575 x, y = pos 2576 self.interactor.SetEventPosition(pos) 2577 else: 2578 x, y = self.interactor.GetEventPosition() 2579 self.renderer = self.interactor.FindPokedRenderer(x, y) 2580 2581 self.picked2d = (x, y) 2582 2583 key = self.interactor.GetKeySym() 2584 2585 if key: 2586 if "_L" in key or "_R" in key: 2587 # skip things like Shift_R 2588 key = "" # better than None 2589 else: 2590 if self.interactor.GetShiftKey(): 2591 key = key.upper() 2592 2593 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2594 key = "underscore" 2595 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2596 key = "plus" 2597 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2598 key = "?" 2599 2600 if self.interactor.GetControlKey(): 2601 key = "Ctrl+" + key 2602 2603 if self.interactor.GetAltKey(): 2604 key = "Alt+" + key 2605 2606 if enable_picking: 2607 if not self.picker: 2608 self.picker = vtki.vtkPropPicker() 2609 2610 self.picker.PickProp(x, y, self.renderer) 2611 actor = self.picker.GetProp3D() 2612 # Note that GetProp3D already picks Assembly 2613 2614 xp, yp = self.interactor.GetLastEventPosition() 2615 dx, dy = x - xp, y - yp 2616 2617 delta3d = np.array([0, 0, 0]) 2618 2619 if actor: 2620 picked3d = np.array(self.picker.GetPickPosition()) 2621 2622 try: 2623 vobj = actor.retrieve_object() 2624 old_pt = np.asarray(vobj.picked3d) 2625 vobj.picked3d = picked3d 2626 delta3d = picked3d - old_pt 2627 except (AttributeError, TypeError): 2628 pass 2629 2630 else: 2631 picked3d = None 2632 2633 if not actor: # try 2D 2634 actor = self.picker.GetActor2D() 2635 2636 event = Event() 2637 event.name = ename 2638 event.title = self.title 2639 event.id = -1 # will be set by the timer wrapper function 2640 event.timerid = -1 # will be set by the timer wrapper function 2641 event.priority = -1 # will be set by the timer wrapper function 2642 event.time = time.time() 2643 event.at = self.renderers.index(self.renderer) 2644 event.keypress = key 2645 if enable_picking: 2646 try: 2647 event.object = actor.retrieve_object() 2648 except AttributeError: 2649 event.object = actor 2650 try: 2651 event.actor = actor.retrieve_object() # obsolete use object instead 2652 except AttributeError: 2653 event.actor = actor 2654 event.picked3d = picked3d 2655 event.picked2d = (x, y) 2656 event.delta2d = (dx, dy) 2657 event.angle2d = np.arctan2(dy, dx) 2658 event.speed2d = np.sqrt(dx * dx + dy * dy) 2659 event.delta3d = delta3d 2660 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2661 event.isPoints = isinstance(event.object, vedo.Points) 2662 event.isMesh = isinstance(event.object, vedo.Mesh) 2663 event.isAssembly = isinstance(event.object, vedo.Assembly) 2664 event.isVolume = isinstance(event.object, vedo.Volume) 2665 event.isImage = isinstance(event.object, vedo.Image) 2666 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2667 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.
2669 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2670 """ 2671 Add a function to be executed while show() is active. 2672 2673 Return a unique id for the callback. 2674 2675 The callback function (see example below) exposes a dictionary 2676 with the following information: 2677 - `name`: event name, 2678 - `id`: event unique identifier, 2679 - `priority`: event priority (float), 2680 - `interactor`: the interactor object, 2681 - `at`: renderer nr. where the event occurred 2682 - `keypress`: key pressed as string 2683 - `actor`: object picked by the mouse 2684 - `picked3d`: point picked in world coordinates 2685 - `picked2d`: screen coords of the mouse pointer 2686 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2687 - `delta3d`: ...same but in 3D world coords 2688 - `angle2d`: angle of mouse movement on screen 2689 - `speed2d`: speed of mouse movement on screen 2690 - `speed3d`: speed of picked point in world coordinates 2691 - `isPoints`: True if of class 2692 - `isMesh`: True if of class 2693 - `isAssembly`: True if of class 2694 - `isVolume`: True if of class Volume 2695 - `isImage`: True if of class 2696 2697 If `enable_picking` is False, no picking will be performed. 2698 This can be useful to avoid double picking when using buttons. 2699 2700 Frequently used events are: 2701 - `KeyPress`, `KeyRelease`: listen to keyboard events 2702 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2703 - `MiddleButtonPress`, `MiddleButtonRelease` 2704 - `RightButtonPress`, `RightButtonRelease` 2705 - `MouseMove`: listen to mouse pointer changing position 2706 - `MouseWheelForward`, `MouseWheelBackward` 2707 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2708 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2709 - `ResetCamera`, `ResetCameraClippingRange` 2710 - `Error`, `Warning` 2711 - `Char` 2712 - `Timer` 2713 2714 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2715 2716 Example: 2717 ```python 2718 from vedo import * 2719 2720 def func(evt): 2721 # this function is called every time the mouse moves 2722 # (evt is a dotted dictionary) 2723 if not evt.object: 2724 return # no hit, return 2725 print("point coords =", evt.picked3d) 2726 # print(evt) # full event dump 2727 2728 elli = Ellipsoid() 2729 plt = Plotter(axes=1) 2730 plt.add_callback('mouse hovering', func) 2731 plt.show(elli).close() 2732 ``` 2733 2734 Examples: 2735 - [spline_draw1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw1.py) 2736 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2737 2738  2739 2740 - ..and many others! 2741 """ 2742 from vtkmodules.util.misc import calldata_type # noqa 2743 2744 if not self.interactor: 2745 return 0 2746 2747 if vedo.settings.dry_run_mode >= 1: 2748 return 0 2749 2750 ######################################### 2751 @calldata_type(vtki.VTK_INT) 2752 def _func_wrap(iren, ename, timerid=None): 2753 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2754 event.timerid = timerid 2755 event.id = cid 2756 event.priority = priority 2757 self.last_event = event 2758 func(event) 2759 2760 ######################################### 2761 2762 event_name = utils.get_vtk_name_event(event_name) 2763 2764 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2765 # print(f"Registering event: {event_name} with id={cid}") 2766 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_draw1.py
..and many others!
2768 def remove_callback(self, cid: Union[int, str]) -> Self: 2769 """ 2770 Remove a callback function by its id 2771 or a whole category of callbacks by their name. 2772 2773 Arguments: 2774 cid : (int, str) 2775 Unique id of the callback. 2776 If an event name is passed all callbacks of that type are removed. 2777 """ 2778 if self.interactor: 2779 if isinstance(cid, str): 2780 cid = utils.get_vtk_name_event(cid) 2781 self.interactor.RemoveObservers(cid) 2782 else: 2783 self.interactor.RemoveObserver(cid) 2784 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.
2786 def remove_all_observers(self) -> Self: 2787 """ 2788 Remove all observers. 2789 2790 Example: 2791 ```python 2792 from vedo import * 2793 2794 def kfunc(event): 2795 print("Key pressed:", event.keypress) 2796 if event.keypress == 'q': 2797 plt.close() 2798 2799 def rfunc(event): 2800 if event.isImage: 2801 printc("Right-clicked!", event) 2802 plt.render() 2803 2804 img = Image(dataurl+"images/embryo.jpg") 2805 2806 plt = Plotter(size=(1050, 600)) 2807 plt.parallel_projection(True) 2808 plt.remove_all_observers() 2809 plt.add_callback("key press", kfunc) 2810 plt.add_callback("mouse right click", rfunc) 2811 plt.show("Right-Click Me! Press q to exit.", img) 2812 plt.close() 2813 ``` 2814 """ 2815 if self.interactor: 2816 self.interactor.RemoveAllObservers() 2817 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()
2819 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2820 """ 2821 Start or stop an existing timer. 2822 2823 Arguments: 2824 action : (str) 2825 Either "create"/"start" or "destroy"/"stop" 2826 timer_id : (int) 2827 When stopping the timer, the ID of the timer as returned when created 2828 dt : (int) 2829 time in milliseconds between each repeated call 2830 one_shot : (bool) 2831 create a one shot timer of prescribed duration instead of a repeating one 2832 2833 Examples: 2834 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2835 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2836 2837  2838 """ 2839 if action in ("create", "start"): 2840 2841 if "Windows" in vedo.sys_platform: 2842 # otherwise on windows it gets stuck 2843 self.initialize_interactor() 2844 2845 if timer_id is not None: 2846 vedo.logger.warning("you set a timer_id but it will be ignored.") 2847 if one_shot: 2848 timer_id = self.interactor.CreateOneShotTimer(dt) 2849 else: 2850 timer_id = self.interactor.CreateRepeatingTimer(dt) 2851 return timer_id 2852 2853 elif action in ("destroy", "stop"): 2854 if timer_id is not None: 2855 self.interactor.DestroyTimer(timer_id) 2856 else: 2857 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2858 else: 2859 e = f"in timer_callback(). Cannot understand action: {action}\n" 2860 e += " allowed actions are: ['start', 'stop']. Skipped." 2861 vedo.logger.error(e) 2862 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:
2864 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2865 """ 2866 Add a callback function that will be called when an event occurs. 2867 Consider using `add_callback()` instead. 2868 """ 2869 if not self.interactor: 2870 return -1 2871 event_name = utils.get_vtk_name_event(event_name) 2872 idd = self.interactor.AddObserver(event_name, func, priority) 2873 return idd
Add a callback function that will be called when an event occurs.
Consider using add_callback()
instead.
2875 def compute_world_coordinate( 2876 self, 2877 pos2d: MutableSequence[float], 2878 at=None, 2879 objs=(), 2880 bounds=(), 2881 offset=None, 2882 pixeltol=None, 2883 worldtol=None, 2884 ) -> np.ndarray: 2885 """ 2886 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2887 If a set of meshes is passed then points are placed onto these. 2888 2889 Arguments: 2890 pos2d : (list) 2891 2D screen coordinates point. 2892 at : (int) 2893 renderer number. 2894 objs : (list) 2895 list of Mesh objects to project the point onto. 2896 bounds : (list) 2897 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2898 offset : (float) 2899 specify an offset value. 2900 pixeltol : (int) 2901 screen tolerance in pixels. 2902 worldtol : (float) 2903 world coordinates tolerance. 2904 2905 Returns: 2906 numpy array, the point in 3D world coordinates. 2907 2908 Examples: 2909 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2910 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2911 2912  2913 """ 2914 renderer = self.renderer if at is None else self.renderers[at] 2915 2916 if not objs: 2917 pp = vtki.vtkFocalPlanePointPlacer() 2918 else: 2919 pps = vtki.vtkPolygonalSurfacePointPlacer() 2920 for ob in objs: 2921 pps.AddProp(ob.actor) 2922 pp = pps # type: ignore 2923 2924 if len(bounds) == 6: 2925 pp.SetPointBounds(bounds) 2926 if pixeltol: 2927 pp.SetPixelTolerance(pixeltol) 2928 if worldtol: 2929 pp.SetWorldTolerance(worldtol) 2930 if offset: 2931 pp.SetOffset(offset) 2932 2933 worldPos: MutableSequence[float] = [0, 0, 0] 2934 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2935 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2936 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2937 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2938 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:
2940 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2941 """ 2942 Given a 3D points in the current renderer (or full window), 2943 find the screen pixel coordinates. 2944 2945 Example: 2946 ```python 2947 from vedo import * 2948 2949 elli = Ellipsoid().point_size(5) 2950 2951 plt = Plotter() 2952 plt.show(elli, "Press q to continue and print the info") 2953 2954 xyscreen = plt.compute_screen_coordinates(elli) 2955 print('xyscreen coords:', xyscreen) 2956 2957 # simulate an event happening at one point 2958 event = plt.fill_event(pos=xyscreen[123]) 2959 print(event) 2960 ``` 2961 """ 2962 try: 2963 obj = obj.coordinates 2964 except AttributeError: 2965 pass 2966 2967 if utils.is_sequence(obj): 2968 pts = obj 2969 p2d = [] 2970 cs = vtki.vtkCoordinate() 2971 cs.SetCoordinateSystemToWorld() 2972 cs.SetViewport(self.renderer) 2973 for p in pts: 2974 cs.SetValue(p) 2975 if full_window: 2976 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2977 else: 2978 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2979 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)
2981 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2982 """ 2983 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2984 2985 Returns a frustum Mesh that contains the visible field of view. 2986 This can be used to select objects in a scene or select vertices. 2987 2988 Example: 2989 ```python 2990 from vedo import * 2991 2992 settings.enable_default_mouse_callbacks = False 2993 2994 def mode_select(objs): 2995 print("Selected objects:", objs) 2996 d0 = mode.start_x, mode.start_y # display coords 2997 d1 = mode.end_x, mode.end_y 2998 2999 frustum = plt.pick_area(d0, d1) 3000 col = np.random.randint(0, 10) 3001 infru = frustum.inside_points(mesh) 3002 infru.point_size(10).color(col) 3003 plt.add(frustum, infru).render() 3004 3005 mesh = Mesh(dataurl+"cow.vtk") 3006 mesh.color("k5").linewidth(1) 3007 3008 mode = interactor_modes.BlenderStyle() 3009 mode.callback_select = mode_select 3010 3011 plt = Plotter().user_mode(mode) 3012 plt.show(mesh, axes=1) 3013 ``` 3014 """ 3015 ren = self.renderer if at is None else self.renderers[at] 3016 area_picker = vtki.vtkAreaPicker() 3017 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 3018 planes = area_picker.GetFrustum() 3019 3020 fru = vtki.new("FrustumSource") 3021 fru.SetPlanes(planes) 3022 fru.ShowLinesOff() 3023 fru.Update() 3024 3025 afru = vedo.Mesh(fru.GetOutput()) 3026 afru.alpha(0.1).lw(1).pickable(False) 3027 afru.name = "Frustum" 3028 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)
3151 def show( 3152 self, 3153 *objects, 3154 at=None, 3155 axes=None, 3156 resetcam=None, 3157 zoom=False, 3158 interactive=None, 3159 viewup="", 3160 azimuth=0.0, 3161 elevation=0.0, 3162 roll=0.0, 3163 camera=None, 3164 mode=None, 3165 rate=None, 3166 bg=None, 3167 bg2=None, 3168 size=None, 3169 title=None, 3170 screenshot="", 3171 ) -> Any: 3172 """ 3173 Render a list of objects. 3174 3175 Arguments: 3176 at : (int) 3177 number of the renderer to plot to, in case of more than one exists 3178 3179 axes : (int) 3180 axis type-1 can be fully customized by passing a dictionary. 3181 Check `addons.Axes()` for the full list of options. 3182 set the type of axes to be shown: 3183 - 0, no axes 3184 - 1, draw three gray grid walls 3185 - 2, show cartesian axes from (0,0,0) 3186 - 3, show positive range of cartesian axes from (0,0,0) 3187 - 4, show a triad at bottom left 3188 - 5, show a cube at bottom left 3189 - 6, mark the corners of the bounding box 3190 - 7, draw a 3D ruler at each side of the cartesian axes 3191 - 8, show the `vtkCubeAxesActor` object 3192 - 9, show the bounding box outLine 3193 - 10, show three circles representing the maximum bounding box 3194 - 11, show a large grid on the x-y plane 3195 - 12, show polar axes 3196 - 13, draw a simple ruler at the bottom of the window 3197 3198 azimuth/elevation/roll : (float) 3199 move camera accordingly the specified value 3200 3201 viewup: str, list 3202 either `['x', 'y', 'z']` or a vector to set vertical direction 3203 3204 resetcam : (bool) 3205 re-adjust camera position to fit objects 3206 3207 camera : (dict, vtkCamera) 3208 camera parameters can further be specified with a dictionary 3209 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3210 - pos, `(list)`, the position of the camera in world coordinates 3211 - focal_point `(list)`, the focal point of the camera in world coordinates 3212 - viewup `(list)`, the view up direction for the camera 3213 - distance `(float)`, set the focal point to the specified distance from the camera position. 3214 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3215 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3216 in world-coordinate distances. The default is 1. 3217 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3218 This method has no effect in perspective projection mode. 3219 3220 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3221 plane to be set a distance 'thickness' beyond the near clipping plane. 3222 3223 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3224 measured in degrees. The default angle is 30 degrees. 3225 This method has no effect in parallel projection mode. 3226 The formula for setting the angle up for perfect perspective viewing is: 3227 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3228 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3229 3230 interactive : (bool) 3231 pause and interact with window (True) or continue execution (False) 3232 3233 rate : (float) 3234 maximum rate of `show()` in Hertz 3235 3236 mode : (int, str) 3237 set the type of interaction: 3238 - 0 = TrackballCamera [default] 3239 - 1 = TrackballActor 3240 - 2 = JoystickCamera 3241 - 3 = JoystickActor 3242 - 4 = Flight 3243 - 5 = RubberBand2D 3244 - 6 = RubberBand3D 3245 - 7 = RubberBandZoom 3246 - 8 = Terrain 3247 - 9 = Unicam 3248 - 10 = Image 3249 - Check out `vedo.interaction_modes` for more options. 3250 3251 bg : (str, list) 3252 background color in RGB format, or string name 3253 3254 bg2 : (str, list) 3255 second background color to create a gradient background 3256 3257 size : (str, list) 3258 size of the window, e.g. size="fullscreen", or size=[600,400] 3259 3260 title : (str) 3261 window title text 3262 3263 screenshot : (str) 3264 save a screenshot of the window to file 3265 """ 3266 3267 if vedo.settings.dry_run_mode >= 2: 3268 return self 3269 3270 if self.wx_widget: 3271 return self 3272 3273 if self.renderers: # in case of notebooks 3274 3275 if at is None: 3276 at = self.renderers.index(self.renderer) 3277 3278 else: 3279 3280 if at >= len(self.renderers): 3281 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3282 vedo.logger.error(t) 3283 return self 3284 3285 self.renderer = self.renderers[at] 3286 3287 if title is not None: 3288 self.title = title 3289 3290 if size is not None: 3291 self.size = size 3292 if self.size[0] == "f": # full screen 3293 self.size = "fullscreen" 3294 self.window.SetFullScreen(True) 3295 self.window.BordersOn() 3296 else: 3297 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3298 3299 if vedo.settings.default_backend == "vtk": 3300 if str(bg).endswith(".hdr"): 3301 self._add_skybox(bg) 3302 else: 3303 if bg is not None: 3304 self.backgrcol = vedo.get_color(bg) 3305 self.renderer.SetBackground(self.backgrcol) 3306 if bg2 is not None: 3307 self.renderer.GradientBackgroundOn() 3308 self.renderer.SetBackground2(vedo.get_color(bg2)) 3309 3310 if axes is not None: 3311 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3312 objects = list(objects) 3313 objects.append(axes) # move it into the list of normal things to show 3314 axes = 0 3315 self.axes = axes 3316 3317 if interactive is not None: 3318 self._interactive = interactive 3319 if self.offscreen: 3320 self._interactive = False 3321 3322 # camera stuff 3323 if resetcam is not None: 3324 self.resetcam = resetcam 3325 3326 if camera is not None: 3327 self.resetcam = False 3328 viewup = "" 3329 if isinstance(camera, vtki.vtkCamera): 3330 cameracopy = vtki.vtkCamera() 3331 cameracopy.DeepCopy(camera) 3332 self.camera = cameracopy 3333 else: 3334 self.camera = utils.camera_from_dict(camera) 3335 3336 self.add(objects) 3337 3338 # Backend ############################################################### 3339 if vedo.settings.default_backend in ["k3d", "panel"]: 3340 return backends.get_notebook_backend(self.objects) 3341 ######################################################################### 3342 3343 for ia in utils.flatten(objects): 3344 try: 3345 # fix gray color labels and title to white or black 3346 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3347 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3348 c = (0.9, 0.9, 0.9) 3349 if np.sum(self.renderer.GetBackground()) > 1.5: 3350 c = (0.1, 0.1, 0.1) 3351 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3352 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3353 except AttributeError: 3354 pass 3355 3356 if self.sharecam: 3357 for r in self.renderers: 3358 r.SetActiveCamera(self.camera) 3359 3360 if self.axes is not None: 3361 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3362 bns = self.renderer.ComputeVisiblePropBounds() 3363 addons.add_global_axes(self.axes, bounds=bns) 3364 3365 # Backend ############################################################### 3366 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3367 return backends.get_notebook_backend() 3368 ######################################################################### 3369 3370 if self.resetcam and self.renderer: 3371 self.renderer.ResetCamera() 3372 3373 if len(self.renderers) > 1: 3374 self.add_renderer_frame() 3375 3376 if vedo.settings.default_backend == "2d" and not zoom: 3377 zoom = "tightest" 3378 3379 if zoom: 3380 if zoom == "tight": 3381 self.reset_camera(tight=0.04) 3382 elif zoom == "tightest": 3383 self.reset_camera(tight=0.0001) 3384 else: 3385 self.camera.Zoom(zoom) 3386 if elevation: 3387 self.camera.Elevation(elevation) 3388 if azimuth: 3389 self.camera.Azimuth(azimuth) 3390 if roll: 3391 self.camera.Roll(roll) 3392 3393 if len(viewup) > 0: 3394 b = self.renderer.ComputeVisiblePropBounds() 3395 cm = np.array([(b[1] + b[0]) / 2, (b[3] + b[2]) / 2, (b[5] + b[4]) / 2]) 3396 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3397 if viewup == "x": 3398 sz = np.linalg.norm(sz) 3399 self.camera.SetViewUp([1, 0, 0]) 3400 self.camera.SetPosition(cm + sz) 3401 elif viewup == "y": 3402 sz = np.linalg.norm(sz) 3403 self.camera.SetViewUp([0, 1, 0]) 3404 self.camera.SetPosition(cm + sz) 3405 elif viewup == "z": 3406 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3407 self.camera.SetViewUp([0, 0, 1]) 3408 self.camera.SetPosition(cm + 2 * sz) 3409 elif utils.is_sequence(viewup): 3410 sz = np.linalg.norm(sz) 3411 self.camera.SetViewUp(viewup) 3412 cpos = np.cross([0, 1, 0], viewup) 3413 self.camera.SetPosition(cm - 2 * sz * cpos) 3414 3415 self.renderer.ResetCameraClippingRange() 3416 3417 self.initialize_interactor() 3418 3419 if vedo.settings.immediate_rendering: 3420 self.window.Render() ##################### <-------------- Render 3421 3422 if self.interactor: # can be offscreen or not the vtk backend.. 3423 3424 self.window.SetWindowName(self.title) 3425 3426 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3427 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3428 # print(pic.dataset)# Array 0 name PNGImage 3429 # self.window.SetIcon(pic.dataset) 3430 3431 try: 3432 # Needs "pip install pyobjc" on Mac OSX 3433 if ( 3434 self._cocoa_initialized is False 3435 and "Darwin" in vedo.sys_platform 3436 and not self.offscreen 3437 ): 3438 self._cocoa_initialized = True 3439 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3440 pid = os.getpid() 3441 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3442 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3443 except: 3444 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3445 pass 3446 3447 # Set the interaction style 3448 if mode is not None: 3449 self.user_mode(mode) 3450 if self.qt_widget and mode is None: 3451 self.user_mode(0) 3452 3453 if screenshot: 3454 self.screenshot(screenshot) 3455 3456 if self._interactive and self.interactor: 3457 self.interactor.Start() 3458 if self._must_close_now and self.interactor: 3459 self.interactor.GetRenderWindow().Finalize() 3460 self.interactor.TerminateApp() 3461 self.camera = None 3462 self.renderer = None 3463 self.renderers = [] 3464 self.window = None 3465 self.interactor = None 3466 return self 3467 3468 if rate: 3469 if self.clock is None: # set clock and limit rate 3470 self._clockt0 = time.time() 3471 self.clock = 0.0 3472 else: 3473 t = time.time() - self._clockt0 3474 elapsed = t - self.clock 3475 mint = 1.0 / rate 3476 if elapsed < mint: 3477 time.sleep(mint - elapsed) 3478 self.clock = time.time() - self._clockt0 3479 3480 # 2d #################################################################### 3481 if vedo.settings.default_backend in ["2d"]: 3482 return backends.get_notebook_backend() 3483 ######################################################################### 3484 3485 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
3487 def add_inset( 3488 self, *objects, **options 3489 ) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3490 """Add a draggable inset space into a renderer. 3491 3492 Arguments: 3493 at : (int) 3494 specify the renderer number 3495 pos : (list) 3496 icon position in the range [1-4] indicating one of the 4 corners, 3497 or it can be a tuple (x,y) as a fraction of the renderer size. 3498 size : (float) 3499 size of the square inset 3500 draggable : (bool) 3501 if True the subrenderer space can be dragged around 3502 c : (color) 3503 color of the inset frame when dragged 3504 3505 Examples: 3506 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3507 3508  3509 """ 3510 if not self.interactor: 3511 return None 3512 3513 if not self.renderer: 3514 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3515 return None 3516 3517 options = dict(options) 3518 pos = options.pop("pos", 0) 3519 size = options.pop("size", 0.1) 3520 c = options.pop("c", "lb") 3521 at = options.pop("at", None) 3522 draggable = options.pop("draggable", True) 3523 3524 r, g, b = vedo.get_color(c) 3525 widget = vtki.vtkOrientationMarkerWidget() 3526 widget.SetOutlineColor(r, g, b) 3527 if len(objects) == 1: 3528 widget.SetOrientationMarker(objects[0].actor) 3529 else: 3530 widget.SetOrientationMarker(vedo.Assembly(objects)) 3531 3532 widget.SetInteractor(self.interactor) 3533 3534 if utils.is_sequence(pos): 3535 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3536 else: 3537 if pos < 2: 3538 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3539 elif pos == 2: 3540 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3541 elif pos == 3: 3542 widget.SetViewport(0, 0, size * 2, size * 2) 3543 elif pos == 4: 3544 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3545 widget.EnabledOn() 3546 widget.SetInteractive(draggable) 3547 if at is not None and at < len(self.renderers): 3548 widget.SetCurrentRenderer(self.renderers[at]) 3549 else: 3550 widget.SetCurrentRenderer(self.renderer) 3551 self.widgets.append(widget) 3552 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:
3554 def clear(self, at=None, deep=False) -> Self: 3555 """Clear the scene from all meshes and volumes.""" 3556 renderer = self.renderer if at is None else self.renderers[at] 3557 if not renderer: 3558 return self 3559 3560 if deep: 3561 renderer.RemoveAllViewProps() 3562 else: 3563 for ob in set( 3564 self.get_meshes() 3565 + self.get_volumes() 3566 + self.objects 3567 + self.axes_instances 3568 ): 3569 if isinstance(ob, vedo.shapes.Text2D): 3570 continue 3571 self.remove(ob) 3572 try: 3573 if ob.scalarbar: 3574 self.remove(ob.scalarbar) 3575 except AttributeError: 3576 pass 3577 return self
Clear the scene from all meshes and volumes.
3579 def break_interaction(self) -> Self: 3580 """Break window interaction and return to the python execution flow""" 3581 if self.interactor: 3582 self.check_actors_trasform() 3583 self.interactor.ExitCallback() 3584 return self
Break window interaction and return to the python execution flow
3586 def freeze(self, value=True) -> Self: 3587 """Freeze the current renderer. Use this with `sharecam=False`.""" 3588 if not self.interactor: 3589 return self 3590 if not self.renderer: 3591 return self 3592 self.renderer.SetInteractive(not value) 3593 return self
Freeze the current renderer. Use this with sharecam=False
.
3595 def user_mode(self, mode) -> Self: 3596 """ 3597 Modify the user interaction mode. 3598 3599 Examples: 3600 ```python 3601 from vedo import * 3602 mode = interactor_modes.MousePan() 3603 mesh = Mesh(dataurl+"cow.vtk") 3604 plt = Plotter().user_mode(mode) 3605 plt.show(mesh, axes=1) 3606 ``` 3607 See also: 3608 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3609 """ 3610 if not self.interactor: 3611 return self 3612 3613 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3614 # print("Current style:", curr_style) 3615 if curr_style.endswith("Actor"): 3616 self.check_actors_trasform() 3617 3618 if isinstance(mode, (str, int)): 3619 # Set the style of interaction 3620 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3621 if mode in (0, "TrackballCamera"): 3622 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3623 self.interactor.RemoveObservers("CharEvent") 3624 elif mode in (1, "TrackballActor"): 3625 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3626 elif mode in (2, "JoystickCamera"): 3627 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3628 elif mode in (3, "JoystickActor"): 3629 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3630 elif mode in (4, "Flight"): 3631 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3632 elif mode in (5, "RubberBand2D"): 3633 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3634 elif mode in (6, "RubberBand3D"): 3635 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3636 elif mode in (7, "RubberBandZoom"): 3637 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3638 elif mode in (8, "Terrain"): 3639 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3640 elif mode in (9, "Unicam"): 3641 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3642 elif mode in (10, "Image", "image", "2d"): 3643 astyle = vtki.new("InteractorStyleImage") 3644 astyle.SetInteractionModeToImage3D() 3645 self.interactor.SetInteractorStyle(astyle) 3646 else: 3647 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3648 3649 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3650 # set a custom interactor style 3651 if hasattr(mode, "interactor"): 3652 mode.interactor = self.interactor 3653 mode.renderer = self.renderer # type: ignore 3654 mode.SetInteractor(self.interactor) 3655 mode.SetDefaultRenderer(self.renderer) 3656 self.interactor.SetInteractorStyle(mode) 3657 3658 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
3660 def close(self) -> Self: 3661 """Close the plotter.""" 3662 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3663 vedo.last_figure = None 3664 self.last_event = None 3665 self.sliders = [] 3666 self.buttons = [] 3667 self.widgets = [] 3668 self.hover_legends = [] 3669 self.background_renderer = None 3670 self._extralight = None 3671 3672 self.hint_widget = None 3673 self.cutter_widget = None 3674 3675 if vedo.settings.dry_run_mode >= 2: 3676 return self 3677 3678 if not hasattr(self, "window"): 3679 return self 3680 if not self.window: 3681 return self 3682 if not hasattr(self, "interactor"): 3683 return self 3684 if not self.interactor: 3685 return self 3686 3687 ################################################### 3688 3689 self._must_close_now = True 3690 3691 if self.interactor: 3692 if self._interactive: 3693 self.break_interaction() 3694 self.interactor.GetRenderWindow().Finalize() 3695 try: 3696 if "Darwin" in vedo.sys_platform: 3697 self.interactor.ProcessEvents() 3698 except: 3699 pass 3700 self.interactor.TerminateApp() 3701 self.camera = None 3702 self.renderer = None 3703 self.renderers = [] 3704 self.window = None 3705 self.interactor = None 3706 3707 if vedo.plotter_instance == self: 3708 vedo.plotter_instance = None 3709 return self # must return self for consistency
Close the plotter.
3712 @property 3713 def camera(self): 3714 """Return the current active camera.""" 3715 if self.renderer: 3716 return self.renderer.GetActiveCamera()
Return the current active camera.
3725 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3726 """ 3727 Take a screenshot of the Plotter window. 3728 3729 Arguments: 3730 scale : (int) 3731 set image magnification as an integer multiplicating factor 3732 asarray : (bool) 3733 return a numpy array of the image instead of writing a file 3734 3735 Warning: 3736 If you get black screenshots try to set `interactive=False` in `show()` 3737 then call `screenshot()` and `plt.interactive()` afterwards. 3738 3739 Example: 3740 ```py 3741 from vedo import * 3742 sphere = Sphere().linewidth(1) 3743 plt = show(sphere, interactive=False) 3744 plt.screenshot('image.png') 3745 plt.interactive() 3746 plt.close() 3747 ``` 3748 3749 Example: 3750 ```py 3751 from vedo import * 3752 sphere = Sphere().linewidth(1) 3753 plt = show(sphere, interactive=False) 3754 plt.screenshot('anotherimage.png') 3755 plt.interactive() 3756 plt.close() 3757 ``` 3758 """ 3759 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()
3761 def toimage(self, scale=1) -> "vedo.image.Image": 3762 """ 3763 Generate a `Image` object from the current rendering window. 3764 3765 Arguments: 3766 scale : (int) 3767 set image magnification as an integer multiplicating factor 3768 """ 3769 if vedo.settings.screeshot_large_image: 3770 w2if = vtki.new("RenderLargeImage") 3771 w2if.SetInput(self.renderer) 3772 w2if.SetMagnification(scale) 3773 else: 3774 w2if = vtki.new("WindowToImageFilter") 3775 w2if.SetInput(self.window) 3776 if hasattr(w2if, "SetScale"): 3777 w2if.SetScale(scale, scale) 3778 if vedo.settings.screenshot_transparent_background: 3779 w2if.SetInputBufferTypeToRGBA() 3780 w2if.ReadFrontBufferOff() # read from the back buffer 3781 w2if.Update() 3782 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
3784 def export(self, filename="scene.npz", binary=False) -> Self: 3785 """ 3786 Export scene to file to HTML, X3D or Numpy file. 3787 3788 Examples: 3789 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3790 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3791 """ 3792 vedo.file_io.export_window(filename, binary=binary) 3793 return self
3795 def color_picker(self, xy, verbose=False): 3796 """Pick color of specific (x,y) pixel on the screen.""" 3797 w2if = vtki.new("WindowToImageFilter") 3798 w2if.SetInput(self.window) 3799 w2if.ReadFrontBufferOff() 3800 w2if.Update() 3801 nx, ny = self.window.GetSize() 3802 varr = w2if.GetOutput().GetPointData().GetScalars() 3803 3804 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3805 x, y = int(xy[0]), int(xy[1]) 3806 if y < ny and x < nx: 3807 3808 rgb = arr[y, x] 3809 3810 if verbose: 3811 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3812 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3813 vedo.printc("█", c=[0, rgb[1], 0], end="") 3814 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3815 vedo.printc("] = ", end="") 3816 cnm = vedo.get_color_name(rgb) 3817 if np.sum(rgb) < 150: 3818 vedo.printc( 3819 rgb.tolist(), 3820 vedo.colors.rgb2hex(np.array(rgb) / 255), 3821 c="w", 3822 bc=rgb, 3823 invert=1, 3824 end="", 3825 ) 3826 vedo.printc(" -> " + cnm, invert=1, c="w") 3827 else: 3828 vedo.printc( 3829 rgb.tolist(), 3830 vedo.colors.rgb2hex(np.array(rgb) / 255), 3831 c=rgb, 3832 end="", 3833 ) 3834 vedo.printc(" -> " + cnm, c=cnm) 3835 3836 return rgb 3837 3838 return None
Pick color of specific (x,y) pixel on the screen.
135def show( 136 *objects, 137 at=None, 138 shape=(1, 1), 139 N=None, 140 pos=(0, 0), 141 size="auto", 142 screensize="auto", 143 title="vedo", 144 bg="white", 145 bg2=None, 146 axes=None, 147 interactive=None, 148 offscreen=False, 149 sharecam=True, 150 resetcam=True, 151 zoom=None, 152 viewup="", 153 azimuth=0.0, 154 elevation=0.0, 155 roll=0.0, 156 camera=None, 157 mode=None, 158 screenshot="", 159 new=False, 160) -> Union[Self, None]: 161 """ 162 Create on the fly an instance of class Plotter and show the object(s) provided. 163 164 Arguments: 165 at : (int) 166 number of the renderer to plot to, in case of more than one exists 167 shape : (list, str) 168 Number of sub-render windows inside of the main window. E.g.: 169 specify two across with shape=(2,1) and a two by two grid 170 with shape=(2, 2). By default there is only one renderer. 171 172 Can also accept a shape as string descriptor. E.g.: 173 - shape="3|1" means 3 plots on the left and 1 on the right, 174 - shape="4/2" means 4 plots on top of 2 at bottom. 175 176 N : (int) 177 number of desired sub-render windows arranged automatically in a grid 178 pos : (list) 179 position coordinates of the top-left corner of the rendering window 180 on the screen 181 size : (list) 182 size of the rendering window 183 screensize : (list) 184 physical size of the monitor screen 185 title : (str) 186 window title 187 bg : (color) 188 background color or specify jpg image file name with path 189 bg2 : (color) 190 background color of a gradient towards the top 191 axes : (int) 192 set the type of axes to be shown: 193 - 0, no axes 194 - 1, draw three gray grid walls 195 - 2, show cartesian axes from (0,0,0) 196 - 3, show positive range of cartesian axes from (0,0,0) 197 - 4, show a triad at bottom left 198 - 5, show a cube at bottom left 199 - 6, mark the corners of the bounding box 200 - 7, draw a 3D ruler at each side of the cartesian axes 201 - 8, show the `vtkCubeAxesActor` object 202 - 9, show the bounding box outLine 203 - 10, show three circles representing the maximum bounding box 204 - 11, show a large grid on the x-y plane 205 - 12, show polar axes 206 - 13, draw a simple ruler at the bottom of the window 207 - 14: draw a `CameraOrientationWidget` 208 209 Axis type-1 can be fully customized by passing a dictionary. 210 Check `vedo.addons.Axes()` for the full list of options. 211 azimuth/elevation/roll : (float) 212 move camera accordingly the specified value 213 viewup : (str, list) 214 either `['x', 'y', 'z']` or a vector to set vertical direction 215 resetcam : (bool) 216 re-adjust camera position to fit objects 217 camera : (dict, vtkCamera) 218 camera parameters can further be specified with a dictionary 219 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 220 - **pos** (list), the position of the camera in world coordinates 221 - **focal_point** (list), the focal point of the camera in world coordinates 222 - **viewup** (list), the view up direction for the camera 223 - **distance** (float), set the focal point to the specified distance from the camera position. 224 - **clipping_range** (float), distance of the near and far clipping planes along the direction of projection. 225 - **parallel_scale** (float), 226 scaling used for a parallel projection, i.e. the height of the viewport 227 in world-coordinate distances. The default is 1. Note that the "scale" parameter works as 228 an "inverse scale", larger numbers produce smaller images. 229 This method has no effect in perspective projection mode. 230 - **thickness** (float), 231 set the distance between clipping planes. This method adjusts the far clipping 232 plane to be set a distance 'thickness' beyond the near clipping plane. 233 - **view_angle** (float), 234 the camera view angle, which is the angular height of the camera view 235 measured in degrees. The default angle is 30 degrees. 236 This method has no effect in parallel projection mode. 237 The formula for setting the angle up for perfect perspective viewing is: 238 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 239 (measured by holding a ruler up to your screen) and d is the distance 240 from your eyes to the screen. 241 interactive : (bool) 242 pause and interact with window (True) or continue execution (False) 243 rate : (float) 244 maximum rate of `show()` in Hertz 245 mode : (int, str) 246 set the type of interaction: 247 - 0 = TrackballCamera [default] 248 - 1 = TrackballActor 249 - 2 = JoystickCamera 250 - 3 = JoystickActor 251 - 4 = Flight 252 - 5 = RubberBand2D 253 - 6 = RubberBand3D 254 - 7 = RubberBandZoom 255 - 8 = Terrain 256 - 9 = Unicam 257 - 10 = Image 258 new : (bool) 259 if set to `True`, a call to show will instantiate 260 a new Plotter object (a new window) instead of reusing the first created. 261 If new is `True`, but the existing plotter was instantiated with a different 262 argument for `offscreen`, `new` is ignored and a new Plotter is created anyway. 263 """ 264 if len(objects) == 0: 265 objects = None 266 elif len(objects) == 1: 267 objects = objects[0] 268 else: 269 objects = utils.flatten(objects) 270 271 # If a plotter instance is already present, check if the offscreen argument 272 # is the same as the one requested by the user. If not, create a new 273 # plotter instance (see https://github.com/marcomusy/vedo/issues/1026) 274 if vedo.plotter_instance and vedo.plotter_instance.offscreen != offscreen: 275 new = True 276 277 if vedo.plotter_instance and not new: # Plotter exists 278 plt = vedo.plotter_instance 279 280 else: # Plotter must be created 281 282 if utils.is_sequence(at): # user passed a sequence for "at" 283 284 if not utils.is_sequence(objects): 285 vedo.logger.error("in show() input must be a list.") 286 raise RuntimeError() 287 if len(at) != len(objects): 288 vedo.logger.error("in show() lists 'input' and 'at' must have equal lengths") 289 raise RuntimeError() 290 if shape == (1, 1) and N is None: 291 N = max(at) + 1 292 293 elif at is None and (N or shape != (1, 1)): 294 295 if not utils.is_sequence(objects): 296 e = "in show(), N or shape is set, but input is not a sequence\n" 297 e += " you may need to specify e.g. at=0" 298 vedo.logger.error(e) 299 raise RuntimeError() 300 at = list(range(len(objects))) 301 302 plt = Plotter( 303 shape=shape, 304 N=N, 305 pos=pos, 306 size=size, 307 screensize=screensize, 308 title=title, 309 axes=axes, 310 sharecam=sharecam, 311 resetcam=resetcam, 312 interactive=interactive, 313 offscreen=offscreen, 314 bg=bg, 315 bg2=bg2, 316 ) 317 318 if vedo.settings.dry_run_mode >= 2: 319 return plt 320 321 # use _plt_to_return because plt.show() can return a k3d plot 322 _plt_to_return = None 323 324 if utils.is_sequence(at): 325 326 for i, act in enumerate(objects): 327 _plt_to_return = plt.show( 328 act, 329 at=i, 330 zoom=zoom, 331 resetcam=resetcam, 332 viewup=viewup, 333 azimuth=azimuth, 334 elevation=elevation, 335 roll=roll, 336 camera=camera, 337 interactive=False, 338 mode=mode, 339 screenshot=screenshot, 340 bg=bg, 341 bg2=bg2, 342 axes=axes, 343 ) 344 345 if ( 346 interactive 347 or len(at) == N 348 or (isinstance(shape[0], int) and len(at) == shape[0] * shape[1]) 349 ): 350 # note that shape can be a string 351 if plt.interactor and not offscreen and (interactive is None or interactive): 352 plt.interactor.Start() 353 if plt._must_close_now: 354 plt.interactor.GetRenderWindow().Finalize() 355 plt.interactor.TerminateApp() 356 plt.interactor = None 357 plt.window = None 358 plt.renderer = None 359 plt.renderers = [] 360 plt.camera = None 361 362 else: 363 364 _plt_to_return = plt.show( 365 objects, 366 at=at, 367 zoom=zoom, 368 resetcam=resetcam, 369 viewup=viewup, 370 azimuth=azimuth, 371 elevation=elevation, 372 roll=roll, 373 camera=camera, 374 interactive=interactive, 375 mode=mode, 376 screenshot=screenshot, 377 bg=bg, 378 bg2=bg2, 379 axes=axes, 380 ) 381 382 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.
385def close() -> None: 386 """Close the last created Plotter instance if it exists.""" 387 if not vedo.plotter_instance: 388 return 389 vedo.plotter_instance.close() 390 return
Close the last created Plotter instance if it exists.