vedo.plotter
This module defines the main class Plotter to manage objects and 3D rendering.
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3import os.path 4import sys 5import time 6from typing import MutableSequence, Callable, Any, Union 7from typing_extensions import Self 8import numpy as np 9 10import vedo.vtkclasses as vtki # a wrapper for lazy imports 11 12import vedo 13from vedo import transformations 14from vedo import utils 15from vedo import backends 16from vedo import addons 17 18 19__docformat__ = "google" 20 21__doc__ = """ 22This module defines the main class Plotter to manage objects and 3D rendering. 23 24![](https://vedo.embl.es/images/basic/multirenderers.png) 25""" 26 27__all__ = ["Plotter", "show", "close"] 28 29######################################################################################## 30class Event: 31 """ 32 This class holds the info from an event in the window, works as dictionary too. 33 """ 34 35 __slots__ = [ 36 "name", 37 "title", 38 "id", 39 "timerid", 40 "time", 41 "priority", 42 "at", 43 "object", 44 "actor", 45 "picked3d", 46 "keypress", 47 "picked2d", 48 "delta2d", 49 "angle2d", 50 "speed2d", 51 "delta3d", 52 "speed3d", 53 "isPoints", 54 "isMesh", 55 "isAssembly", 56 "isVolume", 57 "isImage", 58 "isActor2D", 59 ] 60 61 def __init__(self): 62 self.name = "event" 63 self.title = "" 64 self.id = 0 65 self.timerid = 0 66 self.time = 0 67 self.priority = 0 68 self.at = 0 69 self.object = None 70 self.actor = None 71 self.picked3d = () 72 self.keypress = "" 73 self.picked2d = () 74 self.delta2d = () 75 self.angle2d = 0 76 self.speed2d = () 77 self.delta3d = () 78 self.speed3d = 0 79 self.isPoints = False 80 self.isMesh = False 81 self.isAssembly = False 82 self.isVolume = False 83 self.isImage = False 84 self.isActor2D = False 85 86 def __getitem__(self, key): 87 return getattr(self, key) 88 89 def __setitem__(self, key, value): 90 setattr(self, key, value) 91 92 def __str__(self): 93 module = self.__class__.__module__ 94 name = self.__class__.__name__ 95 out = vedo.printc( 96 f"{module}.{name} at ({hex(id(self))})".ljust(75), 97 bold=True, invert=True, return_string=True, 98 ) 99 out += "\x1b[0m" 100 for n in self.__slots__: 101 if n == "actor": 102 continue 103 out += f"{n}".ljust(11) + ": " 104 val = str(self[n]).replace("\n", "")[:65].rstrip() 105 if val == "True": 106 out += "\x1b[32;1m" 107 elif val == "False": 108 out += "\x1b[31;1m" 109 out += val + "\x1b[0m\n" 110 return out.rstrip() 111 112 def keys(self): 113 return self.__slots__ 114 115 116############################################################################################## 117def show( 118 *objects, 119 at=None, 120 shape=(1, 1), 121 N=None, 122 pos=(0, 0), 123 size="auto", 124 screensize="auto", 125 title="vedo", 126 bg="white", 127 bg2=None, 128 axes=None, 129 interactive=None, 130 offscreen=False, 131 sharecam=True, 132 resetcam=True, 133 zoom=None, 134 viewup="", 135 azimuth=0.0, 136 elevation=0.0, 137 roll=0.0, 138 camera=None, 139 mode=None, 140 screenshot="", 141 new=False, 142) -> Union[Self, None]: 143 """ 144 Create on the fly an instance of class Plotter and show the object(s) provided. 145 146 Arguments: 147 at : (int) 148 number of the renderer to plot to, in case of more than one exists 149 shape : (list, str) 150 Number of sub-render windows inside of the main window. E.g.: 151 specify two across with shape=(2,1) and a two by two grid 152 with shape=(2, 2). By default there is only one renderer. 153 154 Can also accept a shape as string descriptor. E.g.: 155 - shape="3|1" means 3 plots on the left and 1 on the right, 156 - shape="4/2" means 4 plots on top of 2 at bottom. 157 N : (int) 158 number of desired sub-render windows arranged automatically in a grid 159 pos : (list) 160 position coordinates of the top-left corner of the rendering window 161 on the screen 162 size : (list) 163 size of the rendering window 164 screensize : (list) 165 physical size of the monitor screen 166 title : (str) 167 window title 168 bg : (color) 169 background color or specify jpg image file name with path 170 bg2 : (color) 171 background color of a gradient towards the top 172 axes : (int) 173 set the type of axes to be shown: 174 - 0, no axes 175 - 1, draw three gray grid walls 176 - 2, show cartesian axes from (0,0,0) 177 - 3, show positive range of cartesian axes from (0,0,0) 178 - 4, show a triad at bottom left 179 - 5, show a cube at bottom left 180 - 6, mark the corners of the bounding box 181 - 7, draw a 3D ruler at each side of the cartesian axes 182 - 8, show the `vtkCubeAxesActor` object 183 - 9, show the bounding box outLine 184 - 10, show three circles representing the maximum bounding box 185 - 11, show a large grid on the x-y plane 186 - 12, show polar axes 187 - 13, draw a simple ruler at the bottom of the window 188 - 14: draw a `CameraOrientationWidget` 189 190 Axis type-1 can be fully customized by passing a dictionary. 191 Check `vedo.addons.Axes()` for the full list of options. 192 azimuth/elevation/roll : (float) 193 move camera accordingly the specified value 194 viewup : (str, list) 195 either `['x', 'y', 'z']` or a vector to set vertical direction 196 resetcam : (bool) 197 re-adjust camera position to fit objects 198 camera : (dict, vtkCamera) 199 camera parameters can further be specified with a dictionary 200 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 201 - **pos** (list), the position of the camera in world coordinates 202 - **focal_point** (list), the focal point of the camera in world coordinates 203 - **viewup** (list), the view up direction for the camera 204 - **distance** (float), set the focal point to the specified distance from the camera position. 205 - **clipping_range** (float), distance of the near and far clipping planes along the direction of projection. 206 - **parallel_scale** (float), 207 scaling used for a parallel projection, i.e. the height of the viewport 208 in world-coordinate distances. The default is 1. Note that the "scale" parameter works as 209 an "inverse scale", larger numbers produce smaller images. 210 This method has no effect in perspective projection mode. 211 - **thickness** (float), 212 set the distance between clipping planes. This method adjusts the far clipping 213 plane to be set a distance 'thickness' beyond the near clipping plane. 214 - **view_angle** (float), 215 the camera view angle, which is the angular height of the camera view 216 measured in degrees. The default angle is 30 degrees. 217 This method has no effect in parallel projection mode. 218 The formula for setting the angle up for perfect perspective viewing is: 219 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 220 (measured by holding a ruler up to your screen) and d is the distance 221 from your eyes to the screen. 222 interactive : (bool) 223 pause and interact with window (True) or continue execution (False) 224 rate : (float) 225 maximum rate of `show()` in Hertz 226 mode : (int, str) 227 set the type of interaction: 228 - 0 = TrackballCamera [default] 229 - 1 = TrackballActor 230 - 2 = JoystickCamera 231 - 3 = JoystickActor 232 - 4 = Flight 233 - 5 = RubberBand2D 234 - 6 = RubberBand3D 235 - 7 = RubberBandZoom 236 - 8 = Terrain 237 - 9 = Unicam 238 - 10 = Image 239 new : (bool) 240 if set to `True`, a call to show will instantiate 241 a new Plotter object (a new window) instead of reusing the first created. 242 If new is `True`, but the existing plotter was instantiated with a different 243 argument for `offscreen`, `new` is ignored and a new Plotter is created anyway. 244 """ 245 if len(objects) == 0: 246 objects = None 247 elif len(objects) == 1: 248 objects = objects[0] 249 else: 250 objects = utils.flatten(objects) 251 252 # If a plotter instance is already present, check if the offscreen argument 253 # is the same as the one requested by the user. If not, create a new 254 # plotter instance (see https://github.com/marcomusy/vedo/issues/1026) 255 if vedo.plotter_instance and vedo.plotter_instance.offscreen != offscreen: 256 new = True 257 258 if vedo.plotter_instance and not new: # Plotter exists 259 plt = vedo.plotter_instance 260 261 else: # Plotter must be created 262 263 if utils.is_sequence(at): # user passed a sequence for "at" 264 265 if not utils.is_sequence(objects): 266 vedo.logger.error("in show() input must be a list.") 267 raise RuntimeError() 268 if len(at) != len(objects): 269 vedo.logger.error("in show() lists 'input' and 'at' must have equal lengths") 270 raise RuntimeError() 271 if shape == (1, 1) and N is None: 272 N = max(at) + 1 273 274 elif at is None and (N or shape != (1, 1)): 275 276 if not utils.is_sequence(objects): 277 e = "in show(), N or shape is set, but input is not a sequence\n" 278 e += " you may need to specify e.g. at=0" 279 vedo.logger.error(e) 280 raise RuntimeError() 281 at = list(range(len(objects))) 282 283 plt = Plotter( 284 shape=shape, 285 N=N, 286 pos=pos, 287 size=size, 288 screensize=screensize, 289 title=title, 290 axes=axes, 291 sharecam=sharecam, 292 resetcam=resetcam, 293 interactive=interactive, 294 offscreen=offscreen, 295 bg=bg, 296 bg2=bg2, 297 ) 298 299 if vedo.settings.dry_run_mode >= 2: 300 return plt 301 302 # use _plt_to_return because plt.show() can return a k3d plot 303 _plt_to_return = None 304 305 if utils.is_sequence(at): 306 307 for i, act in enumerate(objects): 308 _plt_to_return = plt.show( 309 act, 310 at=i, 311 zoom=zoom, 312 resetcam=resetcam, 313 viewup=viewup, 314 azimuth=azimuth, 315 elevation=elevation, 316 roll=roll, 317 camera=camera, 318 interactive=False, 319 mode=mode, 320 screenshot=screenshot, 321 bg=bg, 322 bg2=bg2, 323 axes=axes, 324 ) 325 326 if ( 327 interactive 328 or len(at) == N 329 or (isinstance(shape[0], int) and len(at) == shape[0] * shape[1]) 330 ): 331 # note that shape can be a string 332 if plt.interactor and not offscreen and (interactive is None or interactive): 333 plt.interactor.Start() 334 if plt._must_close_now: 335 plt.interactor.GetRenderWindow().Finalize() 336 plt.interactor.TerminateApp() 337 plt.interactor = None 338 plt.window = None 339 plt.renderer = None 340 plt.renderers = [] 341 plt.camera = None 342 343 else: 344 345 _plt_to_return = plt.show( 346 objects, 347 at=at, 348 zoom=zoom, 349 resetcam=resetcam, 350 viewup=viewup, 351 azimuth=azimuth, 352 elevation=elevation, 353 roll=roll, 354 camera=camera, 355 interactive=interactive, 356 mode=mode, 357 screenshot=screenshot, 358 bg=bg, 359 bg2=bg2, 360 axes=axes, 361 ) 362 363 return _plt_to_return 364 365 366def close() -> None: 367 """Close the last created Plotter instance if it exists.""" 368 if not vedo.plotter_instance: 369 return 370 vedo.plotter_instance.close() 371 return 372 373 374######################################################################## 375class Plotter: 376 """Main class to manage objects.""" 377 378 def __init__( 379 self, 380 shape=(1, 1), 381 N=None, 382 pos=(0, 0), 383 size="auto", 384 screensize="auto", 385 title="vedo", 386 bg="white", 387 bg2=None, 388 axes=None, 389 sharecam=True, 390 resetcam=True, 391 interactive=None, 392 offscreen=False, 393 qt_widget=None, 394 wx_widget=None, 395 ): 396 """ 397 Arguments: 398 shape : (str, list) 399 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 400 N : (int) 401 number of desired renderers arranged in a grid automatically. 402 pos : (list) 403 (x,y) position in pixels of top-left corner of the rendering window on the screen 404 size : (str, list) 405 size of the rendering window. If 'auto', guess it based on screensize. 406 screensize : (list) 407 physical size of the monitor screen in pixels 408 bg : (color, str) 409 background color or specify jpg image file name with path 410 bg2 : (color) 411 background color of a gradient towards the top 412 title : (str) 413 window title 414 415 axes : (int) 416 417 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 418 Check out `vedo.addons.Axes()` for the available options. 419 420 - 0, no axes 421 - 1, draw three gray grid walls 422 - 2, show cartesian axes from (0,0,0) 423 - 3, show positive range of cartesian axes from (0,0,0) 424 - 4, show a triad at bottom left 425 - 5, show a cube at bottom left 426 - 6, mark the corners of the bounding box 427 - 7, draw a 3D ruler at each side of the cartesian axes 428 - 8, show the VTK CubeAxesActor object 429 - 9, show the bounding box outLine 430 - 10, show three circles representing the maximum bounding box 431 - 11, show a large grid on the x-y plane (use with zoom=8) 432 - 12, show polar axes 433 - 13, draw a simple ruler at the bottom of the window 434 - 14: draw a camera orientation widget 435 436 sharecam : (bool) 437 if False each renderer will have an independent camera 438 interactive : (bool) 439 if True will stop after show() to allow interaction with the 3d scene 440 offscreen : (bool) 441 if True will not show the rendering window 442 qt_widget : (QVTKRenderWindowInteractor) 443 render in a Qt-Widget using an QVTKRenderWindowInteractor. 444 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 445 """ 446 vedo.plotter_instance = self 447 448 if interactive is None: 449 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 450 self._interactive = interactive 451 # print("interactive", interactive, N, shape) 452 453 self.objects = [] # list of objects to be shown 454 self.clicked_object = None # holds the object that has been clicked 455 self.clicked_actor = None # holds the actor that has been clicked 456 457 self.shape = shape # nr. of subwindows in grid 458 self.axes = axes # show axes type nr. 459 self.title = title # window title 460 self.size = size # window size 461 self.backgrcol = bg # used also by backend notebooks 462 463 self.offscreen= offscreen 464 self.resetcam = resetcam 465 self.sharecam = sharecam # share the same camera if multiple renderers 466 self.pos = pos # used by vedo.file_io 467 468 self.picker = None # hold the vtkPicker object 469 self.picked2d = None # 2d coords of a clicked point on the rendering window 470 self.picked3d = None # 3d coords of a clicked point on an actor 471 472 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 473 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 474 self.interactor = None 475 self.window = None 476 self.renderer = None 477 self.renderers = [] # list of renderers 478 479 # mostly internal stuff: 480 self.hover_legends = [] 481 self.justremoved = None 482 self.axes_instances = [] 483 self.clock = 0 484 self.sliders = [] 485 self.buttons = [] 486 self.widgets = [] 487 self.cutter_widget = None 488 self.hint_widget = None 489 self.background_renderer = None 490 self.last_event = None 491 self.skybox = None 492 self._icol = 0 493 self._clockt0 = time.time() 494 self._extralight = None 495 self._cocoa_initialized = False 496 self._cocoa_process_events = True # make one call in show() 497 self._must_close_now = False 498 499 ##################################################################### 500 if vedo.settings.default_backend == "2d": 501 self.offscreen = True 502 if self.size == "auto": 503 self.size = (800, 600) 504 505 elif vedo.settings.default_backend == "k3d": 506 if self.size == "auto": 507 self.size = (1000, 1000) 508 #################################### 509 return ############################ 510 #################################### 511 512 ############################################################# 513 if screensize == "auto": 514 screensize = (2160, 1440) # TODO: get actual screen size 515 516 # build the rendering window: 517 self.window = vtki.vtkRenderWindow() 518 519 self.window.GlobalWarningDisplayOff() 520 521 if self.title == "vedo": # check if dev version 522 if "dev" in vedo.__version__: 523 self.title = f"vedo ({vedo.__version__})" 524 self.window.SetWindowName(self.title) 525 526 # more vedo.settings 527 if vedo.settings.use_depth_peeling: 528 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 529 self.window.SetMultiSamples(vedo.settings.multi_samples) 530 531 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 532 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 533 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 534 535 536 ############################################################# 537 if N: # N = number of renderers. Find out the best 538 539 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 540 vedo.logger.warning("having set N, shape is ignored.") 541 542 x, y = screensize 543 nx = int(np.sqrt(int(N * y / x) + 1)) 544 ny = int(np.sqrt(int(N * x / y) + 1)) 545 lm = [ 546 (nx, ny), 547 (nx, ny + 1), 548 (nx - 1, ny), 549 (nx + 1, ny), 550 (nx, ny - 1), 551 (nx - 1, ny + 1), 552 (nx + 1, ny - 1), 553 (nx + 1, ny + 1), 554 (nx - 1, ny - 1), 555 ] 556 ind, minl = 0, 1000 557 for i, m in enumerate(lm): 558 l = m[0] * m[1] 559 if N <= l < minl: 560 ind = i 561 minl = l 562 shape = lm[ind] 563 564 ################################################## 565 if isinstance(shape, str): 566 567 if "|" in shape: 568 if self.size == "auto": 569 self.size = (800, 1200) 570 n = int(shape.split("|")[0]) 571 m = int(shape.split("|")[1]) 572 rangen = reversed(range(n)) 573 rangem = reversed(range(m)) 574 else: 575 if self.size == "auto": 576 self.size = (1200, 800) 577 m = int(shape.split("/")[0]) 578 n = int(shape.split("/")[1]) 579 rangen = range(n) 580 rangem = range(m) 581 582 if n >= m: 583 xsplit = m / (n + m) 584 else: 585 xsplit = 1 - n / (n + m) 586 if vedo.settings.window_splitting_position: 587 xsplit = vedo.settings.window_splitting_position 588 589 for i in rangen: 590 arenderer = vtki.vtkRenderer() 591 if "|" in shape: 592 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 593 else: 594 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 595 self.renderers.append(arenderer) 596 597 for i in rangem: 598 arenderer = vtki.vtkRenderer() 599 600 if "|" in shape: 601 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 602 else: 603 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 604 self.renderers.append(arenderer) 605 606 for r in self.renderers: 607 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 608 609 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 610 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 611 if vedo.settings.use_depth_peeling: 612 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 613 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 614 r.SetUseFXAA(vedo.settings.use_fxaa) 615 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 616 617 r.SetBackground(vedo.get_color(self.backgrcol)) 618 619 self.axes_instances.append(None) 620 621 self.shape = (n + m,) 622 623 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 624 # passing a sequence of dicts for renderers specifications 625 626 if self.size == "auto": 627 self.size = (1000, 800) 628 629 for rd in shape: 630 x0, y0 = rd["bottomleft"] 631 x1, y1 = rd["topright"] 632 bg_ = rd.pop("bg", "white") 633 bg2_ = rd.pop("bg2", None) 634 635 arenderer = vtki.vtkRenderer() 636 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 637 638 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 639 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 640 if vedo.settings.use_depth_peeling: 641 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 642 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 643 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 644 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 645 646 arenderer.SetViewport(x0, y0, x1, y1) 647 arenderer.SetBackground(vedo.get_color(bg_)) 648 if bg2_: 649 arenderer.GradientBackgroundOn() 650 arenderer.SetBackground2(vedo.get_color(bg2_)) 651 652 self.renderers.append(arenderer) 653 self.axes_instances.append(None) 654 655 self.shape = (len(shape),) 656 657 else: 658 659 if isinstance(self.size, str) and self.size == "auto": 660 # figure out a reasonable window size 661 f = 1.5 662 x, y = screensize 663 xs = y / f * shape[1] # because y<x 664 ys = y / f * shape[0] 665 if xs > x / f: # shrink 666 xs = x / f 667 ys = xs / shape[1] * shape[0] 668 if ys > y / f: 669 ys = y / f 670 xs = ys / shape[0] * shape[1] 671 self.size = (int(xs), int(ys)) 672 if shape == (1, 1): 673 self.size = (int(y / f), int(y / f)) # because y<x 674 else: 675 self.size = (self.size[0], self.size[1]) 676 677 try: 678 image_actor = None 679 bgname = str(self.backgrcol).lower() 680 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 681 self.window.SetNumberOfLayers(2) 682 self.background_renderer = vtki.vtkRenderer() 683 self.background_renderer.SetLayer(0) 684 self.background_renderer.InteractiveOff() 685 self.background_renderer.SetBackground(vedo.get_color(bg2)) 686 image_actor = vedo.Image(self.backgrcol).actor 687 self.window.AddRenderer(self.background_renderer) 688 self.background_renderer.AddActor(image_actor) 689 except AttributeError: 690 pass 691 692 for i in reversed(range(shape[0])): 693 for j in range(shape[1]): 694 arenderer = vtki.vtkRenderer() 695 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 696 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 697 698 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 699 if vedo.settings.use_depth_peeling: 700 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 701 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 702 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 703 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 704 705 if image_actor: 706 arenderer.SetLayer(1) 707 708 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 709 if bg2: 710 arenderer.GradientBackgroundOn() 711 arenderer.SetBackground2(vedo.get_color(bg2)) 712 713 x0 = i / shape[0] 714 y0 = j / shape[1] 715 x1 = (i + 1) / shape[0] 716 y1 = (j + 1) / shape[1] 717 arenderer.SetViewport(y0, x0, y1, x1) 718 self.renderers.append(arenderer) 719 self.axes_instances.append(None) 720 self.shape = shape 721 722 if self.renderers: 723 self.renderer = self.renderers[0] 724 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 725 726 ######################################################### 727 if self.qt_widget or self.wx_widget: 728 if self.qt_widget: 729 self.window = self.qt_widget.GetRenderWindow() # overwrite 730 else: 731 self.window = self.wx_widget.GetRenderWindow() 732 self.interactor = self.window.GetInteractor() 733 734 ######################################################### 735 for r in self.renderers: 736 self.window.AddRenderer(r) 737 # set the background gradient if any 738 if vedo.settings.background_gradient_orientation > 0: 739 try: 740 modes = [ 741 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 742 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 743 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 744 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 745 ] 746 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 747 r.GradientBackgroundOn() 748 except AttributeError: 749 pass 750 751 # Backend #################################################### 752 if vedo.settings.default_backend in ["panel", "trame", "k3d"]: 753 return ################ 754 ######################## 755 756 ######################################################### 757 if self.qt_widget or self.wx_widget: 758 self.interactor.SetRenderWindow(self.window) 759 if vedo.settings.enable_default_keyboard_callbacks: 760 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 761 if vedo.settings.enable_default_mouse_callbacks: 762 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 763 return ################ 764 ######################## 765 766 if self.size[0] == "f": # full screen 767 self.size = "fullscreen" 768 self.window.SetFullScreen(True) 769 self.window.BordersOn() 770 else: 771 self.window.SetSize(int(self.size[0]), int(self.size[1])) 772 773 if self.offscreen: 774 if self.axes in (4, 5, 8, 12, 14): 775 self.axes = 0 # does not work with those 776 self.window.SetOffScreenRendering(True) 777 self.interactor = None 778 self._interactive = False 779 return ################ 780 ######################## 781 782 self.window.SetPosition(pos) 783 784 ######################################################### 785 self.interactor = vtki.vtkRenderWindowInteractor() 786 787 self.interactor.SetRenderWindow(self.window) 788 vsty = vtki.new("InteractorStyleTrackballCamera") 789 self.interactor.SetInteractorStyle(vsty) 790 self.interactor.RemoveObservers("CharEvent") 791 792 if vedo.settings.enable_default_keyboard_callbacks: 793 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 794 if vedo.settings.enable_default_mouse_callbacks: 795 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 796 797 ##################################################################### ..init ends here. 798 799 800 def __str__(self): 801 """Return Plotter info.""" 802 axtype = { 803 0: "(no axes)", 804 1: "(default customizable grid walls)", 805 2: "(cartesian axes from origin", 806 3: "(positive range of cartesian axes from origin", 807 4: "(axes triad at bottom left)", 808 5: "(oriented cube at bottom left)", 809 6: "(mark the corners of the bounding box)", 810 7: "(3D ruler at each side of the cartesian axes)", 811 8: "(the vtkCubeAxesActor object)", 812 9: "(the bounding box outline)", 813 10: "(circles of maximum bounding box range)", 814 11: "(show a large grid on the x-y plane)", 815 12: "(show polar axes)", 816 13: "(simple ruler at the bottom of the window)", 817 14: "(the vtkCameraOrientationWidget object)", 818 } 819 820 module = self.__class__.__module__ 821 name = self.__class__.__name__ 822 out = vedo.printc( 823 f"{module}.{name} at ({hex(id(self))})".ljust(75), 824 bold=True, invert=True, return_string=True, 825 ) 826 out += "\x1b[0m" 827 if self.interactor: 828 out += "window title".ljust(14) + ": " + self.title + "\n" 829 out += "window size".ljust(14) + f": {self.window.GetSize()}" 830 out += f", full_screen={self.window.GetScreenSize()}\n" 831 out += "activ renderer".ljust(14) + ": nr." + str(self.renderers.index(self.renderer)) 832 out += f" (out of {len(self.renderers)} renderers)\n" 833 834 bns, totpt = [], 0 835 for a in self.objects: 836 try: 837 b = a.bounds() 838 bns.append(b) 839 except AttributeError: 840 pass 841 try: 842 totpt += a.npoints 843 except AttributeError: 844 pass 845 out += "n. of objects".ljust(14) + f": {len(self.objects)}" 846 out += f" ({totpt} vertices)\n" if totpt else "\n" 847 848 if len(bns) > 0: 849 min_bns = np.min(bns, axis=0) 850 max_bns = np.max(bns, axis=0) 851 bx1, bx2 = utils.precision(min_bns[0], 3), utils.precision(max_bns[1], 3) 852 by1, by2 = utils.precision(min_bns[2], 3), utils.precision(max_bns[3], 3) 853 bz1, bz2 = utils.precision(min_bns[4], 3), utils.precision(max_bns[5], 3) 854 out += "bounds".ljust(14) + ":" 855 out += " x=(" + bx1 + ", " + bx2 + ")," 856 out += " y=(" + by1 + ", " + by2 + ")," 857 out += " z=(" + bz1 + ", " + bz2 + ")\n" 858 859 if utils.is_integer(self.axes): 860 out += "axes style".ljust(14) + f": {self.axes} {axtype[self.axes]}\n" 861 elif isinstance(self.axes, dict): 862 out += "axes style".ljust(14) + f": 1 {axtype[1]}\n" 863 else: 864 out += "axes style".ljust(14) + f": {[self.axes]}\n" 865 return out.rstrip() + "\x1b[0m" 866 867 def print(self): 868 """Print information about the current instance.""" 869 print(self.__str__()) 870 return self 871 872 def __iadd__(self, objects): 873 self.add(objects) 874 return self 875 876 def __isub__(self, objects): 877 self.remove(objects) 878 return self 879 880 def __enter__(self): 881 # context manager like in "with Plotter() as plt:" 882 return self 883 884 def __exit__(self, *args, **kwargs): 885 # context manager like in "with Plotter() as plt:" 886 self.close() 887 888 def initialize_interactor(self) -> Self: 889 """Initialize the interactor if not already initialized.""" 890 if self.offscreen: 891 return self 892 if self.interactor: 893 if not self.interactor.GetInitialized(): 894 self.interactor.Initialize() 895 self.interactor.RemoveObservers("CharEvent") 896 return self 897 898 def process_events(self) -> Self: 899 """Process all pending events.""" 900 self.initialize_interactor() 901 if self.interactor: 902 try: 903 self.interactor.ProcessEvents() 904 except AttributeError: 905 pass 906 return self 907 908 def at(self, nren: int, yren=None) -> Self: 909 """ 910 Select the current renderer number as an int. 911 Can also use the `[nx, ny]` format. 912 """ 913 if utils.is_sequence(nren): 914 if len(nren) == 2: 915 nren, yren = nren 916 else: 917 vedo.logger.error("at() argument must be a single number or a list of two numbers") 918 raise RuntimeError 919 920 if yren is not None: 921 a, b = self.shape 922 x, y = nren, yren 923 nren = x * b + y 924 # print("at (", x, y, ") -> ren", nren) 925 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 926 vedo.logger.error(f"at({nren, yren}) is malformed!") 927 raise RuntimeError 928 929 self.renderer = self.renderers[nren] 930 return self 931 932 def add(self, *objs, at=None) -> Self: 933 """ 934 Append the input objects to the internal list of objects to be shown. 935 936 Arguments: 937 at : (int) 938 add the object at the specified renderer 939 """ 940 if at is not None: 941 ren = self.renderers[at] 942 else: 943 ren = self.renderer 944 945 objs = utils.flatten(objs) 946 for ob in objs: 947 if ob and ob not in self.objects: 948 self.objects.append(ob) 949 950 acts = self._scan_input_return_acts(objs) 951 952 for a in acts: 953 954 if ren: 955 if isinstance(a, vedo.addons.BaseCutter): 956 a.add_to(self) # from cutters 957 continue 958 959 if isinstance(a, vtki.vtkLight): 960 ren.AddLight(a) 961 continue 962 963 try: 964 ren.AddActor(a) 965 except TypeError: 966 ren.AddActor(a.actor) 967 968 try: 969 ir = self.renderers.index(ren) 970 a.rendered_at.add(ir) # might not have rendered_at 971 except (AttributeError, ValueError): 972 pass 973 974 if isinstance(a, vtki.vtkFollower): 975 a.SetCamera(self.camera) 976 elif isinstance(a, vedo.visual.LightKit): 977 a.lightkit.AddLightsToRenderer(ren) 978 979 return self 980 981 def remove(self, *objs, at=None) -> Self: 982 """ 983 Remove input object to the internal list of objects to be shown. 984 985 Objects to be removed can be referenced by their assigned name, 986 987 Arguments: 988 at : (int) 989 remove the object at the specified renderer 990 """ 991 # TODO and you can also use wildcards like `*` and `?`. 992 if at is not None: 993 ren = self.renderers[at] 994 else: 995 ren = self.renderer 996 997 objs = [ob for ob in utils.flatten(objs) if ob] 998 999 has_str = False 1000 for ob in objs: 1001 if isinstance(ob, str): 1002 has_str = True 1003 break 1004 1005 has_actor = False 1006 for ob in objs: 1007 if hasattr(ob, "actor") and ob.actor: 1008 has_actor = True 1009 break 1010 1011 if has_str or has_actor: 1012 # need to get the actors to search for 1013 for a in self.get_actors(include_non_pickables=True): 1014 # print("PARSING", [a]) 1015 try: 1016 if (a.name and a.name in objs) or a in objs: 1017 objs.append(a) 1018 # if a.name: 1019 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1020 # if any(bools) or a in objs: 1021 # objs.append(a) 1022 # print('a.name',a.name, objs,any(bools)) 1023 except AttributeError: # no .name 1024 # passing the actor so get back the object with .retrieve_object() 1025 try: 1026 vobj = a.retrieve_object() 1027 if (vobj.name and vobj.name in objs) or vobj in objs: 1028 # print('vobj.name', vobj.name) 1029 objs.append(vobj) 1030 except AttributeError: 1031 pass 1032 1033 if ren is None: 1034 return self 1035 ir = self.renderers.index(ren) 1036 1037 ids = [] 1038 for ob in set(objs): 1039 1040 # will remove it from internal list if possible 1041 try: 1042 idx = self.objects.index(ob) 1043 ids.append(idx) 1044 except ValueError: 1045 pass 1046 1047 if ren: ### remove it from the renderer 1048 1049 if isinstance(ob, vedo.addons.BaseCutter): 1050 ob.remove_from(self) # from cutters 1051 continue 1052 1053 try: 1054 ren.RemoveActor(ob) 1055 except TypeError: 1056 try: 1057 ren.RemoveActor(ob.actor) 1058 except AttributeError: 1059 pass 1060 1061 if hasattr(ob, "rendered_at"): 1062 ob.rendered_at.discard(ir) 1063 1064 if hasattr(ob, "scalarbar") and ob.scalarbar: 1065 ren.RemoveActor(ob.scalarbar) 1066 if hasattr(ob, "_caption") and ob._caption: 1067 ren.RemoveActor(ob._caption) 1068 if hasattr(ob, "shadows") and ob.shadows: 1069 for sha in ob.shadows: 1070 ren.RemoveActor(sha.actor) 1071 if hasattr(ob, "trail") and ob.trail: 1072 ren.RemoveActor(ob.trail.actor) 1073 ob.trail_points = [] 1074 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1075 for sha in ob.trail.shadows: 1076 ren.RemoveActor(sha.actor) 1077 1078 elif isinstance(ob, vedo.visual.LightKit): 1079 ob.lightkit.RemoveLightsFromRenderer(ren) 1080 1081 # for i in ids: # WRONG way of doing it! 1082 # del self.objects[i] 1083 # instead we do: 1084 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1085 return self 1086 1087 @property 1088 def actors(self): 1089 """Return the list of actors.""" 1090 return [ob.actor for ob in self.objects if hasattr(ob, "actor")] 1091 1092 def remove_lights(self) -> Self: 1093 """Remove all the present lights in the current renderer.""" 1094 if self.renderer: 1095 self.renderer.RemoveAllLights() 1096 return self 1097 1098 def pop(self, at=None) -> Self: 1099 """ 1100 Remove the last added object from the rendering window. 1101 This method is typically used in loops or callback functions. 1102 """ 1103 if at is not None and not isinstance(at, int): 1104 # wrong usage pitfall 1105 vedo.logger.error("argument of pop() must be an integer") 1106 raise RuntimeError() 1107 1108 if self.objects: 1109 self.remove(self.objects[-1], at) 1110 return self 1111 1112 def render(self, resetcam=False) -> Self: 1113 """Render the scene. This method is typically used in loops or callback functions.""" 1114 1115 if vedo.settings.dry_run_mode >= 2: 1116 return self 1117 1118 if not self.window: 1119 return self 1120 1121 self.initialize_interactor() 1122 1123 if resetcam: 1124 self.renderer.ResetCamera() 1125 1126 self.window.Render() 1127 1128 if self._cocoa_process_events and self.interactor and self.interactor.GetInitialized(): 1129 if "Darwin" in vedo.sys_platform and not self.offscreen: 1130 self.interactor.ProcessEvents() 1131 self._cocoa_process_events = False 1132 return self 1133 1134 def interactive(self) -> Self: 1135 """ 1136 Start window interaction. 1137 Analogous to `show(..., interactive=True)`. 1138 """ 1139 if vedo.settings.dry_run_mode >= 1: 1140 return self 1141 self.initialize_interactor() 1142 if self.interactor: 1143 # print("self.interactor.Start()") 1144 self.interactor.Start() 1145 # print("self.interactor.Start() done") 1146 if self._must_close_now: 1147 # print("self.interactor.TerminateApp()") 1148 if self.interactor: 1149 self.interactor.GetRenderWindow().Finalize() 1150 self.interactor.TerminateApp() 1151 self.interactor = None 1152 self.window = None 1153 self.renderer = None 1154 self.renderers = [] 1155 self.camera = None 1156 return self 1157 1158 def use_depth_peeling(self, at=None, value=True) -> Self: 1159 """ 1160 Specify whether use depth peeling algorithm at this specific renderer 1161 Call this method before the first rendering. 1162 """ 1163 if at is None: 1164 ren = self.renderer 1165 else: 1166 ren = self.renderers[at] 1167 ren.SetUseDepthPeeling(value) 1168 return self 1169 1170 def background(self, c1=None, c2=None, at=None, mode=0) -> Union[Self, "np.ndarray"]: 1171 """Set the color of the background for the current renderer. 1172 A different renderer index can be specified by keyword `at`. 1173 1174 Arguments: 1175 c1 : (list) 1176 background main color. 1177 c2 : (list) 1178 background color for the upper part of the window. 1179 at : (int) 1180 renderer index. 1181 mode : (int) 1182 background mode (needs vtk version >= 9.3) 1183 0 = vertical, 1184 1 = horizontal, 1185 2 = radial farthest side, 1186 3 = radia farthest corner. 1187 """ 1188 if not self.renderers: 1189 return self 1190 if at is None: 1191 r = self.renderer 1192 else: 1193 r = self.renderers[at] 1194 1195 if c1 is None and c2 is None: 1196 return np.array(r.GetBackground()) 1197 1198 if r: 1199 if c1 is not None: 1200 r.SetBackground(vedo.get_color(c1)) 1201 if c2 is not None: 1202 r.GradientBackgroundOn() 1203 r.SetBackground2(vedo.get_color(c2)) 1204 if mode: 1205 try: # only works with vtk>=9.3 1206 modes = [ 1207 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1208 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1209 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1210 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1211 ] 1212 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 1213 except AttributeError: 1214 pass 1215 1216 else: 1217 r.GradientBackgroundOff() 1218 return self 1219 1220 ################################################################## 1221 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1222 """ 1223 Return a list of Meshes from the specified renderer. 1224 1225 Arguments: 1226 at : (int) 1227 specify which renderer to look at. 1228 include_non_pickables : (bool) 1229 include non-pickable objects 1230 unpack_assemblies : (bool) 1231 unpack assemblies into their components 1232 """ 1233 if at is None: 1234 renderer = self.renderer 1235 at = self.renderers.index(renderer) 1236 elif isinstance(at, int): 1237 renderer = self.renderers[at] 1238 1239 has_global_axes = False 1240 if isinstance(self.axes_instances[at], vedo.Assembly): 1241 has_global_axes = True 1242 1243 if unpack_assemblies: 1244 acs = renderer.GetActors() 1245 else: 1246 acs = renderer.GetViewProps() 1247 1248 objs = [] 1249 acs.InitTraversal() 1250 for _ in range(acs.GetNumberOfItems()): 1251 1252 if unpack_assemblies: 1253 a = acs.GetNextItem() 1254 else: 1255 a = acs.GetNextProp() 1256 1257 if isinstance(a, vtki.vtkVolume): 1258 continue 1259 1260 if include_non_pickables or a.GetPickable(): 1261 if a == self.axes_instances[at]: 1262 continue 1263 if has_global_axes and a in self.axes_instances[at].actors: 1264 continue 1265 try: 1266 objs.append(a.retrieve_object()) 1267 except AttributeError: 1268 pass 1269 return objs 1270 1271 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1272 """ 1273 Return a list of Volumes from the specified renderer. 1274 1275 Arguments: 1276 at : (int) 1277 specify which renderer to look at 1278 include_non_pickables : (bool) 1279 include non-pickable objects 1280 """ 1281 if at is None: 1282 renderer = self.renderer 1283 at = self.renderers.index(renderer) 1284 elif isinstance(at, int): 1285 renderer = 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 if at is None: 1310 renderer = self.renderer 1311 if renderer is None: 1312 return [] 1313 at = self.renderers.index(renderer) 1314 elif isinstance(at, int): 1315 renderer = self.renderers[at] 1316 1317 acts = [] 1318 acs = renderer.GetViewProps() 1319 acs.InitTraversal() 1320 for _ in range(acs.GetNumberOfItems()): 1321 a = acs.GetNextProp() 1322 if include_non_pickables or a.GetPickable(): 1323 acts.append(a) 1324 return acts 1325 1326 def check_actors_trasform(self, at=None) -> Self: 1327 """ 1328 Reset the transformation matrix of all actors at specified renderer. 1329 This is only useful when actors have been moved/rotated/scaled manually 1330 in an already rendered scene using interactors like 1331 'TrackballActor' or 'JoystickActor'. 1332 """ 1333 # see issue https://github.com/marcomusy/vedo/issues/1046 1334 for a in self.get_actors(at=at, include_non_pickables=True): 1335 try: 1336 M = a.GetMatrix() 1337 except AttributeError: 1338 continue 1339 if M and not M.IsIdentity(): 1340 try: 1341 a.retrieve_object().apply_transform_from_actor() 1342 # vedo.logger.info( 1343 # f"object '{a.retrieve_object().name}' " 1344 # "was manually moved. Updated to its current position." 1345 # ) 1346 except AttributeError: 1347 pass 1348 return self 1349 1350 def reset_camera(self, tight=None) -> Self: 1351 """ 1352 Reset the camera position and zooming. 1353 If tight (float) is specified the zooming reserves a padding space 1354 in the xy-plane expressed in percent of the average size. 1355 """ 1356 if tight is None: 1357 self.renderer.ResetCamera() 1358 else: 1359 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1360 cam = self.camera 1361 1362 self.renderer.ComputeAspect() 1363 aspect = self.renderer.GetAspect() 1364 angle = np.pi * cam.GetViewAngle() / 180.0 1365 dx = x1 - x0 1366 dy = y1 - y0 1367 dist = max(dx / aspect[0], dy) / np.tan(angle / 2) / 2 1368 1369 cam.SetViewUp(0, 1, 0) 1370 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1371 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1372 if cam.GetParallelProjection(): 1373 ps = max(dx / aspect[0], dy) / 2 1374 cam.SetParallelScale(ps * (1 + tight)) 1375 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1376 return self 1377 1378 def reset_clipping_range(self, bounds=None) -> Self: 1379 """ 1380 Reset the camera clipping range to include all visible actors. 1381 If bounds is given, it will be used instead of computing it. 1382 """ 1383 if bounds is None: 1384 self.renderer.ResetCameraClippingRange() 1385 else: 1386 self.renderer.ResetCameraClippingRange(bounds) 1387 return self 1388 1389 def reset_viewup(self, smooth=True) -> Self: 1390 """ 1391 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1392 """ 1393 vbb = addons.compute_visible_bounds()[0] 1394 x0, x1, y0, y1, z0, z1 = vbb 1395 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1396 d = self.camera.GetDistance() 1397 1398 viewups = np.array( 1399 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1400 ) 1401 positions = np.array( 1402 [ 1403 (mx, my, mz + d), 1404 (mx, my, mz - d), 1405 (mx, my + d, mz), 1406 (mx, my - d, mz), 1407 (mx + d, my, mz), 1408 (mx - d, my, mz), 1409 ] 1410 ) 1411 1412 vu = np.array(self.camera.GetViewUp()) 1413 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1414 1415 poc = np.array(self.camera.GetPosition()) 1416 foc = np.array(self.camera.GetFocalPoint()) 1417 a = poc - foc 1418 b = positions - foc 1419 a = a / np.linalg.norm(a) 1420 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1421 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1422 1423 if smooth: 1424 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1425 for t in outtimes: 1426 vv = vu * (1 - t) + viewups[vui] * t 1427 pp = poc * (1 - t) + positions[pui] * t 1428 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1429 self.camera.SetViewUp(vv) 1430 self.camera.SetPosition(pp) 1431 self.camera.SetFocalPoint(ff) 1432 self.render() 1433 1434 # interpolator does not respect parallel view...: 1435 # cam1 = dict( 1436 # pos=poc, 1437 # viewup=vu, 1438 # focal_point=(mx,my,mz), 1439 # clipping_range=self.camera.GetClippingRange() 1440 # ) 1441 # # cam1 = self.camera 1442 # cam2 = dict( 1443 # pos=positions[pui], 1444 # viewup=viewups[vui], 1445 # focal_point=(mx,my,mz), 1446 # clipping_range=self.camera.GetClippingRange() 1447 # ) 1448 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1449 # for c in vcams: 1450 # self.renderer.SetActiveCamera(c) 1451 # self.render() 1452 else: 1453 1454 self.camera.SetViewUp(viewups[vui]) 1455 self.camera.SetPosition(positions[pui]) 1456 self.camera.SetFocalPoint(mx, my, mz) 1457 1458 self.renderer.ResetCameraClippingRange() 1459 1460 # vbb, _, _, _ = addons.compute_visible_bounds() 1461 # x0,x1, y0,y1, z0,z1 = vbb 1462 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1463 self.render() 1464 return self 1465 1466 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1467 """ 1468 Takes as input two cameras set camera at an interpolated position: 1469 1470 Cameras can be vtkCamera or dictionaries in format: 1471 1472 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1473 1474 Press `shift-C` key in interactive mode to dump a python snipplet 1475 of parameters for the current camera view. 1476 """ 1477 nc = len(cameras) 1478 if len(times) == 0: 1479 times = np.linspace(0, 1, num=nc, endpoint=True) 1480 1481 assert len(times) == nc 1482 1483 cin = vtki.new("CameraInterpolator") 1484 1485 # cin.SetInterpolationTypeToLinear() # buggy? 1486 if nc > 2 and smooth: 1487 cin.SetInterpolationTypeToSpline() 1488 1489 for i, cam in enumerate(cameras): 1490 vcam = cam 1491 if isinstance(cam, dict): 1492 vcam = utils.camera_from_dict(cam) 1493 cin.AddCamera(times[i], vcam) 1494 1495 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1496 rng = maxt - mint 1497 1498 if len(output_times) == 0: 1499 cin.InterpolateCamera(t * rng, self.camera) 1500 return [self.camera] 1501 else: 1502 vcams = [] 1503 for tt in output_times: 1504 c = vtki.vtkCamera() 1505 cin.InterpolateCamera(tt * rng, c) 1506 vcams.append(c) 1507 return vcams 1508 1509 def fly_to(self, point) -> Self: 1510 """ 1511 Fly camera to the specified point. 1512 1513 Arguments: 1514 point : (list) 1515 point in space to place camera. 1516 1517 Example: 1518 ```python 1519 from vedo import * 1520 cone = Cone() 1521 plt = Plotter(axes=1) 1522 plt.show(cone) 1523 plt.fly_to([1,0,0]) 1524 plt.interactive().close() 1525 ``` 1526 """ 1527 if self.interactor: 1528 self.resetcam = False 1529 self.interactor.FlyTo(self.renderer, point) 1530 return self 1531 1532 def look_at(self, plane="xy") -> Self: 1533 """Move the camera so that it looks at the specified cartesian plane""" 1534 cam = self.renderer.GetActiveCamera() 1535 fp = np.array(cam.GetFocalPoint()) 1536 p = np.array(cam.GetPosition()) 1537 dist = np.linalg.norm(fp - p) 1538 plane = plane.lower() 1539 if "x" in plane and "y" in plane: 1540 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1541 cam.SetViewUp(0.0, 1.0, 0.0) 1542 elif "x" in plane and "z" in plane: 1543 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1544 cam.SetViewUp(0.0, 0.0, 1.0) 1545 elif "y" in plane and "z" in plane: 1546 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1547 cam.SetViewUp(0.0, 0.0, 1.0) 1548 else: 1549 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1550 return self 1551 1552 def record(self, filename="") -> str: 1553 """ 1554 Record camera, mouse, keystrokes and all other events. 1555 Recording can be toggled on/off by pressing key "R". 1556 1557 Arguments: 1558 filename : (str) 1559 ascii file to store events. 1560 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1561 1562 Returns: 1563 a string descriptor of events. 1564 1565 Examples: 1566 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1567 """ 1568 if vedo.settings.dry_run_mode >= 1: 1569 return "" 1570 if not self.interactor: 1571 vedo.logger.warning("Cannot record events, no interactor defined.") 1572 return "" 1573 erec = vtki.new("InteractorEventRecorder") 1574 erec.SetInteractor(self.interactor) 1575 if not filename: 1576 if not os.path.exists(vedo.settings.cache_directory): 1577 os.makedirs(vedo.settings.cache_directory) 1578 home_dir = os.path.expanduser("~") 1579 filename = os.path.join( 1580 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1581 print("Events will be recorded in", filename) 1582 erec.SetFileName(filename) 1583 erec.SetKeyPressActivationValue("R") 1584 erec.EnabledOn() 1585 erec.Record() 1586 self.interactor.Start() 1587 erec.Stop() 1588 erec.EnabledOff() 1589 with open(filename, "r", encoding="UTF-8") as fl: 1590 events = fl.read() 1591 erec = None 1592 return events 1593 1594 def play(self, recorded_events="", repeats=0) -> Self: 1595 """ 1596 Play camera, mouse, keystrokes and all other events. 1597 1598 Arguments: 1599 events : (str) 1600 file o string of events. 1601 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1602 repeats : (int) 1603 number of extra repeats of the same events. The default is 0. 1604 1605 Examples: 1606 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1607 """ 1608 if vedo.settings.dry_run_mode >= 1: 1609 return self 1610 if not self.interactor: 1611 vedo.logger.warning("Cannot play events, no interactor defined.") 1612 return self 1613 1614 erec = vtki.new("InteractorEventRecorder") 1615 erec.SetInteractor(self.interactor) 1616 1617 if not recorded_events: 1618 home_dir = os.path.expanduser("~") 1619 recorded_events = os.path.join( 1620 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1621 1622 if recorded_events.endswith(".log"): 1623 erec.ReadFromInputStringOff() 1624 erec.SetFileName(recorded_events) 1625 else: 1626 erec.ReadFromInputStringOn() 1627 erec.SetInputString(recorded_events) 1628 1629 erec.Play() 1630 for _ in range(repeats): 1631 erec.Rewind() 1632 erec.Play() 1633 erec.EnabledOff() 1634 erec = None 1635 return self 1636 1637 def parallel_projection(self, value=True, at=None) -> Self: 1638 """ 1639 Use parallel projection `at` a specified renderer. 1640 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1641 An input value equal to -1 will toggle it on/off. 1642 """ 1643 if at is not None: 1644 r = self.renderers[at] 1645 else: 1646 r = self.renderer 1647 if value == -1: 1648 val = r.GetActiveCamera().GetParallelProjection() 1649 value = not val 1650 r.GetActiveCamera().SetParallelProjection(value) 1651 r.Modified() 1652 return self 1653 1654 def render_hidden_lines(self, value=True) -> Self: 1655 """Remove hidden lines when in wireframe mode.""" 1656 self.renderer.SetUseHiddenLineRemoval(not value) 1657 return self 1658 1659 def fov(self, angle: float) -> Self: 1660 """ 1661 Set the field of view angle for the camera. 1662 This is the angle of the camera frustum in the horizontal direction. 1663 High values will result in a wide-angle lens (fish-eye effect), 1664 and low values will result in a telephoto lens. 1665 1666 Default value is 30 degrees. 1667 """ 1668 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1669 self.renderer.GetActiveCamera().SetViewAngle(angle) 1670 return self 1671 1672 def zoom(self, zoom: float) -> Self: 1673 """Apply a zooming factor for the current camera view""" 1674 self.renderer.GetActiveCamera().Zoom(zoom) 1675 return self 1676 1677 def azimuth(self, angle: float) -> Self: 1678 """Rotate camera around the view up vector.""" 1679 self.renderer.GetActiveCamera().Azimuth(angle) 1680 return self 1681 1682 def elevation(self, angle: float) -> Self: 1683 """Rotate the camera around the cross product of the negative 1684 of the direction of projection and the view up vector.""" 1685 self.renderer.GetActiveCamera().Elevation(angle) 1686 return self 1687 1688 def roll(self, angle: float) -> Self: 1689 """Roll the camera about the direction of projection.""" 1690 self.renderer.GetActiveCamera().Roll(angle) 1691 return self 1692 1693 def dolly(self, value: float) -> Self: 1694 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1695 self.renderer.GetActiveCamera().Dolly(value) 1696 return self 1697 1698 ################################################################## 1699 def add_slider( 1700 self, 1701 sliderfunc, 1702 xmin, 1703 xmax, 1704 value=None, 1705 pos=4, 1706 title="", 1707 font="Calco", 1708 title_size=1, 1709 c=None, 1710 alpha=1, 1711 show_value=True, 1712 delayed=False, 1713 **options, 1714 ) -> "vedo.addons.Slider2D": 1715 """ 1716 Add a `vedo.addons.Slider2D` which can call an external custom function. 1717 1718 Arguments: 1719 sliderfunc : (Callable) 1720 external function to be called by the widget 1721 xmin : (float) 1722 lower value of the slider 1723 xmax : (float) 1724 upper value 1725 value : (float) 1726 current value 1727 pos : (list, str) 1728 position corner number: horizontal [1-5] or vertical [11-15] 1729 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1730 and also by a string descriptor (eg. "bottom-left") 1731 title : (str) 1732 title text 1733 font : (str) 1734 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1735 title_size : (float) 1736 title text scale [1.0] 1737 show_value : (bool) 1738 if True current value is shown 1739 delayed : (bool) 1740 if True the callback is delayed until when the mouse button is released 1741 alpha : (float) 1742 opacity of the scalar bar texts 1743 slider_length : (float) 1744 slider length 1745 slider_width : (float) 1746 slider width 1747 end_cap_length : (float) 1748 length of the end cap 1749 end_cap_width : (float) 1750 width of the end cap 1751 tube_width : (float) 1752 width of the tube 1753 title_height : (float) 1754 width of the title 1755 tformat : (str) 1756 format of the title 1757 1758 Examples: 1759 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1760 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1761 1762 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1763 """ 1764 if c is None: # automatic black or white 1765 c = (0.8, 0.8, 0.8) 1766 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1767 c = (0.2, 0.2, 0.2) 1768 else: 1769 c = vedo.get_color(c) 1770 1771 slider2d = addons.Slider2D( 1772 sliderfunc, 1773 xmin, 1774 xmax, 1775 value, 1776 pos, 1777 title, 1778 font, 1779 title_size, 1780 c, 1781 alpha, 1782 show_value, 1783 delayed, 1784 **options, 1785 ) 1786 1787 if self.renderer: 1788 slider2d.renderer = self.renderer 1789 if self.interactor: 1790 slider2d.interactor = self.interactor 1791 slider2d.on() 1792 self.sliders.append([slider2d, sliderfunc]) 1793 return slider2d 1794 1795 def add_slider3d( 1796 self, 1797 sliderfunc, 1798 pos1, 1799 pos2, 1800 xmin, 1801 xmax, 1802 value=None, 1803 s=0.03, 1804 t=1, 1805 title="", 1806 rotation=0.0, 1807 c=None, 1808 show_value=True, 1809 ) -> "vedo.addons.Slider3D": 1810 """ 1811 Add a 3D slider widget which can call an external custom function. 1812 1813 Arguments: 1814 sliderfunc : (function) 1815 external function to be called by the widget 1816 pos1 : (list) 1817 first position 3D coordinates 1818 pos2 : (list) 1819 second position coordinates 1820 xmin : (float) 1821 lower value 1822 xmax : (float) 1823 upper value 1824 value : (float) 1825 initial value 1826 s : (float) 1827 label scaling factor 1828 t : (float) 1829 tube scaling factor 1830 title : (str) 1831 title text 1832 c : (color) 1833 slider color 1834 rotation : (float) 1835 title rotation around slider axis 1836 show_value : (bool) 1837 if True current value is shown 1838 1839 Examples: 1840 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1841 1842 ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png) 1843 """ 1844 if c is None: # automatic black or white 1845 c = (0.8, 0.8, 0.8) 1846 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1847 c = (0.2, 0.2, 0.2) 1848 else: 1849 c = vedo.get_color(c) 1850 1851 slider3d = addons.Slider3D( 1852 sliderfunc, 1853 pos1, 1854 pos2, 1855 xmin, 1856 xmax, 1857 value, 1858 s, 1859 t, 1860 title, 1861 rotation, 1862 c, 1863 show_value, 1864 ) 1865 slider3d.renderer = self.renderer 1866 slider3d.interactor = self.interactor 1867 slider3d.on() 1868 self.sliders.append([slider3d, sliderfunc]) 1869 return slider3d 1870 1871 def add_button( 1872 self, 1873 fnc=None, 1874 states=("On", "Off"), 1875 c=("w", "w"), 1876 bc=("green4", "red4"), 1877 pos=(0.7, 0.1), 1878 size=24, 1879 font="Courier", 1880 bold=True, 1881 italic=False, 1882 alpha=1, 1883 angle=0, 1884 ) -> Union["vedo.addons.Button", None]: 1885 """ 1886 Add a button to the renderer window. 1887 1888 Arguments: 1889 states : (list) 1890 a list of possible states, e.g. ['On', 'Off'] 1891 c : (list) 1892 a list of colors for each state 1893 bc : (list) 1894 a list of background colors for each state 1895 pos : (list) 1896 2D position from left-bottom corner 1897 size : (float) 1898 size of button font 1899 font : (str) 1900 font type. Check [available fonts here](https://vedo.embl.es/fonts). 1901 bold : (bool) 1902 bold font face (False) 1903 italic : (bool) 1904 italic font face (False) 1905 alpha : (float) 1906 opacity level 1907 angle : (float) 1908 anticlockwise rotation in degrees 1909 1910 Returns: 1911 `vedo.addons.Button` object. 1912 1913 Examples: 1914 - [buttons1.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons1.py) 1915 - [buttons2.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons2.py) 1916 1917 ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg) 1918 """ 1919 if self.interactor: 1920 bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle) 1921 self.renderer.AddActor2D(bu) 1922 self.buttons.append(bu) 1923 # bu.function_id = self.add_callback("LeftButtonPress", bu.function) # not good 1924 bu.function_id = bu.add_observer("pick", bu.function, priority=10) 1925 return bu 1926 return None 1927 1928 def add_spline_tool( 1929 self, points, pc="k", ps=8, lc="r4", ac="g5", 1930 lw=2, alpha=1, closed=False, ontop=True, can_add_nodes=True, 1931 ) -> "vedo.addons.SplineTool": 1932 """ 1933 Add a spline tool to the current plotter. 1934 Nodes of the spline can be dragged in space with the mouse. 1935 Clicking on the line itself adds an extra point. 1936 Selecting a point and pressing del removes it. 1937 1938 Arguments: 1939 points : (Mesh, Points, array) 1940 the set of vertices forming the spline nodes. 1941 pc : (str) 1942 point color. The default is 'k'. 1943 ps : (str) 1944 point size. The default is 8. 1945 lc : (str) 1946 line color. The default is 'r4'. 1947 ac : (str) 1948 active point marker color. The default is 'g5'. 1949 lw : (int) 1950 line width. The default is 2. 1951 alpha : (float) 1952 line transparency. 1953 closed : (bool) 1954 spline is meant to be closed. The default is False. 1955 1956 Returns: 1957 a `SplineTool` object. 1958 1959 Examples: 1960 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1961 1962 ![](https://vedo.embl.es/images/basic/spline_tool.png) 1963 """ 1964 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1965 sw.interactor = self.interactor 1966 sw.on() 1967 sw.Initialize(sw.points.dataset) 1968 sw.representation.SetRenderer(self.renderer) 1969 sw.representation.SetClosedLoop(closed) 1970 sw.representation.BuildRepresentation() 1971 self.widgets.append(sw) 1972 return sw 1973 1974 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1975 """Add an inset icon mesh into the same renderer. 1976 1977 Arguments: 1978 pos : (int, list) 1979 icon position in the range [1-4] indicating one of the 4 corners, 1980 or it can be a tuple (x,y) as a fraction of the renderer size. 1981 size : (float) 1982 size of the square inset. 1983 1984 Examples: 1985 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1986 """ 1987 iconw = addons.Icon(icon, pos, size) 1988 1989 iconw.SetInteractor(self.interactor) 1990 iconw.EnabledOn() 1991 iconw.InteractiveOff() 1992 self.widgets.append(iconw) 1993 return iconw 1994 1995 def add_global_axes(self, axtype=None, c=None) -> Self: 1996 """Draw axes on scene. Available axes types: 1997 1998 Arguments: 1999 axtype : (int) 2000 - 0, no axes, 2001 - 1, draw three gray grid walls 2002 - 2, show cartesian axes from (0,0,0) 2003 - 3, show positive range of cartesian axes from (0,0,0) 2004 - 4, show a triad at bottom left 2005 - 5, show a cube at bottom left 2006 - 6, mark the corners of the bounding box 2007 - 7, draw a 3D ruler at each side of the cartesian axes 2008 - 8, show the vtkCubeAxesActor object 2009 - 9, show the bounding box outLine 2010 - 10, show three circles representing the maximum bounding box 2011 - 11, show a large grid on the x-y plane 2012 - 12, show polar axes 2013 - 13, draw a simple ruler at the bottom of the window 2014 2015 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 2016 2017 Example: 2018 ```python 2019 from vedo import Box, show 2020 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 2021 show( 2022 b, 2023 axes={ 2024 "xtitle": "Some long variable [a.u.]", 2025 "number_of_divisions": 4, 2026 # ... 2027 }, 2028 ) 2029 ``` 2030 2031 Examples: 2032 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2033 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2034 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2035 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2036 2037 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2038 """ 2039 addons.add_global_axes(axtype, c) 2040 return self 2041 2042 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2043 """Add a legend to the top right. 2044 2045 Examples: 2046 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 2047 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 2048 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 2049 """ 2050 acts = self.get_meshes() 2051 lb = addons.LegendBox(acts, **kwargs) 2052 self.add(lb) 2053 return lb 2054 2055 def add_hint( 2056 self, 2057 obj, 2058 text="", 2059 c="k", 2060 bg="yellow9", 2061 font="Calco", 2062 size=18, 2063 justify=0, 2064 angle=0, 2065 delay=250, 2066 ) -> Union[vtki.vtkBalloonWidget, None]: 2067 """ 2068 Create a pop-up hint style message when hovering an object. 2069 Use `add_hint(obj, False)` to disable a hinting a specific object. 2070 Use `add_hint(None)` to disable all hints. 2071 2072 Arguments: 2073 obj : (Mesh, Points) 2074 the object to associate the pop-up to 2075 text : (str) 2076 string description of the pop-up 2077 delay : (int) 2078 milliseconds to wait before pop-up occurs 2079 """ 2080 if self.offscreen or not self.interactor: 2081 return None 2082 2083 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2084 # Linux vtk9.0 is bugged 2085 vedo.logger.warning( 2086 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2087 ) 2088 return None 2089 2090 if obj is None: 2091 self.hint_widget.EnabledOff() 2092 self.hint_widget.SetInteractor(None) 2093 self.hint_widget = None 2094 return self.hint_widget 2095 2096 if text is False and self.hint_widget: 2097 self.hint_widget.RemoveBalloon(obj) 2098 return self.hint_widget 2099 2100 if text == "": 2101 if obj.name: 2102 text = obj.name 2103 elif obj.filename: 2104 text = obj.filename 2105 else: 2106 return None 2107 2108 if not self.hint_widget: 2109 self.hint_widget = vtki.vtkBalloonWidget() 2110 2111 rep = self.hint_widget.GetRepresentation() 2112 rep.SetBalloonLayoutToImageRight() 2113 2114 trep = rep.GetTextProperty() 2115 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2116 trep.SetFontFile(utils.get_font_path(font)) 2117 trep.SetFontSize(size) 2118 trep.SetColor(vedo.get_color(c)) 2119 trep.SetBackgroundColor(vedo.get_color(bg)) 2120 trep.SetShadow(0) 2121 trep.SetJustification(justify) 2122 trep.UseTightBoundingBoxOn() 2123 2124 self.hint_widget.ManagesCursorOff() 2125 self.hint_widget.SetTimerDuration(delay) 2126 self.hint_widget.SetInteractor(self.interactor) 2127 if angle: 2128 trep.SetOrientation(angle) 2129 trep.SetBackgroundOpacity(0) 2130 # else: 2131 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2132 self.hint_widget.SetRepresentation(rep) 2133 self.widgets.append(self.hint_widget) 2134 self.hint_widget.EnabledOn() 2135 2136 bst = self.hint_widget.GetBalloonString(obj.actor) 2137 if bst: 2138 self.hint_widget.UpdateBalloonString(obj.actor, text) 2139 else: 2140 self.hint_widget.AddBalloon(obj.actor, text) 2141 2142 return self.hint_widget 2143 2144 def add_shadows(self) -> Self: 2145 """Add shadows at the current renderer.""" 2146 if self.renderer: 2147 shadows = vtki.new("ShadowMapPass") 2148 seq = vtki.new("SequencePass") 2149 passes = vtki.new("RenderPassCollection") 2150 passes.AddItem(shadows.GetShadowMapBakerPass()) 2151 passes.AddItem(shadows) 2152 seq.SetPasses(passes) 2153 camerapass = vtki.new("CameraPass") 2154 camerapass.SetDelegatePass(seq) 2155 self.renderer.SetPass(camerapass) 2156 return self 2157 2158 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> Self: 2159 """ 2160 Screen Space Ambient Occlusion. 2161 2162 For every pixel on the screen, the pixel shader samples the depth values around 2163 the current pixel and tries to compute the amount of occlusion from each of the sampled 2164 points. 2165 2166 Arguments: 2167 radius : (float) 2168 radius of influence in absolute units 2169 bias : (float) 2170 bias of the normals 2171 blur : (bool) 2172 add a blurring to the sampled positions 2173 samples : (int) 2174 number of samples to probe 2175 2176 Examples: 2177 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2178 2179 ![](https://vedo.embl.es/images/basic/ssao.jpg) 2180 """ 2181 lights = vtki.new("LightsPass") 2182 2183 opaque = vtki.new("OpaquePass") 2184 2185 ssaoCam = vtki.new("CameraPass") 2186 ssaoCam.SetDelegatePass(opaque) 2187 2188 ssao = vtki.new("SSAOPass") 2189 ssao.SetRadius(radius) 2190 ssao.SetBias(bias) 2191 ssao.SetBlur(blur) 2192 ssao.SetKernelSize(samples) 2193 ssao.SetDelegatePass(ssaoCam) 2194 2195 translucent = vtki.new("TranslucentPass") 2196 2197 volpass = vtki.new("VolumetricPass") 2198 ddp = vtki.new("DualDepthPeelingPass") 2199 ddp.SetTranslucentPass(translucent) 2200 ddp.SetVolumetricPass(volpass) 2201 2202 over = vtki.new("OverlayPass") 2203 2204 collection = vtki.new("RenderPassCollection") 2205 collection.AddItem(lights) 2206 collection.AddItem(ssao) 2207 collection.AddItem(ddp) 2208 collection.AddItem(over) 2209 2210 sequence = vtki.new("SequencePass") 2211 sequence.SetPasses(collection) 2212 2213 cam = vtki.new("CameraPass") 2214 cam.SetDelegatePass(sequence) 2215 2216 self.renderer.SetPass(cam) 2217 return self 2218 2219 def add_depth_of_field(self, autofocus=True) -> Self: 2220 """Add a depth of field effect in the scene.""" 2221 lights = vtki.new("LightsPass") 2222 2223 opaque = vtki.new("OpaquePass") 2224 2225 dofCam = vtki.new("CameraPass") 2226 dofCam.SetDelegatePass(opaque) 2227 2228 dof = vtki.new("DepthOfFieldPass") 2229 dof.SetAutomaticFocalDistance(autofocus) 2230 dof.SetDelegatePass(dofCam) 2231 2232 collection = vtki.new("RenderPassCollection") 2233 collection.AddItem(lights) 2234 collection.AddItem(dof) 2235 2236 sequence = vtki.new("SequencePass") 2237 sequence.SetPasses(collection) 2238 2239 cam = vtki.new("CameraPass") 2240 cam.SetDelegatePass(sequence) 2241 2242 self.renderer.SetPass(cam) 2243 return self 2244 2245 def _add_skybox(self, hdrfile: str) -> Self: 2246 # many hdr files are at https://polyhaven.com/all 2247 2248 reader = vtki.new("HDRReader") 2249 # Check the image can be read. 2250 if not reader.CanReadFile(hdrfile): 2251 vedo.logger.error(f"Cannot read HDR file {hdrfile}") 2252 return self 2253 reader.SetFileName(hdrfile) 2254 reader.Update() 2255 2256 texture = vtki.vtkTexture() 2257 texture.SetColorModeToDirectScalars() 2258 texture.SetInputData(reader.GetOutput()) 2259 2260 # Convert to a cube map 2261 tcm = vtki.new("EquirectangularToCubeMapTexture") 2262 tcm.SetInputTexture(texture) 2263 # Enable mipmapping to handle HDR image 2264 tcm.MipmapOn() 2265 tcm.InterpolateOn() 2266 2267 self.renderer.SetEnvironmentTexture(tcm) 2268 self.renderer.UseImageBasedLightingOn() 2269 self.skybox = vtki.new("Skybox") 2270 self.skybox.SetTexture(tcm) 2271 self.renderer.AddActor(self.skybox) 2272 return self 2273 2274 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None) -> "vedo.addons.RendererFrame": 2275 """ 2276 Add a frame to the renderer subwindow. 2277 2278 Arguments: 2279 c : (color) 2280 color name or index 2281 alpha : (float) 2282 opacity level 2283 lw : (int) 2284 line width in pixels. 2285 padding : (float) 2286 padding space in pixels. 2287 """ 2288 if c is None: # automatic black or white 2289 c = (0.9, 0.9, 0.9) 2290 if self.renderer: 2291 if np.sum(self.renderer.GetBackground()) > 1.5: 2292 c = (0.1, 0.1, 0.1) 2293 renf = addons.RendererFrame(c, alpha, lw, padding) 2294 if renf: 2295 self.renderer.AddActor(renf) 2296 return renf 2297 2298 def add_hover_legend( 2299 self, 2300 at=None, 2301 c=None, 2302 pos="bottom-left", 2303 font="Calco", 2304 s=0.75, 2305 bg="auto", 2306 alpha=0.1, 2307 maxlength=24, 2308 use_info=False, 2309 ) -> int: 2310 """ 2311 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2312 2313 The created text object are stored in `plotter.hover_legends`. 2314 2315 Returns: 2316 the id of the callback function. 2317 2318 Arguments: 2319 c : (color) 2320 Text color. If None then black or white is chosen automatically 2321 pos : (str) 2322 text positioning 2323 font : (str) 2324 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2325 s : (float) 2326 text size scale 2327 bg : (color) 2328 background color of the 2D box containing the text 2329 alpha : (float) 2330 box transparency 2331 maxlength : (int) 2332 maximum number of characters per line 2333 use_info : (bool) 2334 visualize the content of the `obj.info` attribute 2335 2336 Examples: 2337 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2338 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2339 2340 ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg) 2341 """ 2342 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2343 2344 if at is None: 2345 at = self.renderers.index(self.renderer) 2346 2347 def _legfunc(evt): 2348 if not evt.object or not self.renderer or at != evt.at: 2349 if hoverlegend.mapper.GetInput(): # clear and return 2350 hoverlegend.mapper.SetInput("") 2351 self.render() 2352 return 2353 2354 if use_info: 2355 if hasattr(evt.object, "info"): 2356 t = str(evt.object.info) 2357 else: 2358 return 2359 else: 2360 t, tp = "", "" 2361 if evt.isMesh: 2362 tp = "Mesh " 2363 elif evt.isPoints: 2364 tp = "Points " 2365 elif evt.isVolume: 2366 tp = "Volume " 2367 elif evt.isImage: 2368 tp = "Image " 2369 elif evt.isAssembly: 2370 tp = "Assembly " 2371 else: 2372 return 2373 2374 if evt.isAssembly: 2375 if not evt.object.name: 2376 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2377 else: 2378 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2379 else: 2380 if evt.object.name: 2381 t += f"{tp}name" 2382 if evt.isPoints: 2383 t += " " 2384 if evt.isMesh: 2385 t += " " 2386 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2387 2388 if evt.object.filename: 2389 t += f"{tp}filename: " 2390 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2391 t += "\n" 2392 if not evt.object.file_size: 2393 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2394 if evt.object.file_size: 2395 t += " : " 2396 sz, created = evt.object.file_size, evt.object.created 2397 t += f"{created[4:-5]} ({sz})" + "\n" 2398 2399 if evt.isPoints: 2400 indata = evt.object.dataset 2401 if indata.GetNumberOfPoints(): 2402 t += ( 2403 f"#points/cells: {indata.GetNumberOfPoints()}" 2404 f" / {indata.GetNumberOfCells()}" 2405 ) 2406 pdata = indata.GetPointData() 2407 cdata = indata.GetCellData() 2408 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2409 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2410 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2411 t += " *" 2412 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2413 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2414 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2415 t += " *" 2416 2417 if evt.isImage: 2418 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2419 t += f"\nImage shape: {evt.object.shape}" 2420 pcol = self.color_picker(evt.picked2d) 2421 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2422 2423 # change box color if needed in 'auto' mode 2424 if evt.isPoints and "auto" in str(bg): 2425 actcol = evt.object.properties.GetColor() 2426 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2427 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2428 2429 # adapt to changes in bg color 2430 bgcol = self.renderers[at].GetBackground() 2431 _bgcol = c 2432 if _bgcol is None: # automatic black or white 2433 _bgcol = (0.9, 0.9, 0.9) 2434 if sum(bgcol) > 1.5: 2435 _bgcol = (0.1, 0.1, 0.1) 2436 if len(set(_bgcol).intersection(bgcol)) < 3: 2437 hoverlegend.color(_bgcol) 2438 2439 if hoverlegend.mapper.GetInput() != t: 2440 hoverlegend.mapper.SetInput(t) 2441 self.interactor.Render() 2442 2443 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2444 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2445 2446 self.add(hoverlegend, at=at) 2447 self.hover_legends.append(hoverlegend) 2448 idcall = self.add_callback("MouseMove", _legfunc) 2449 return idcall 2450 2451 def add_scale_indicator( 2452 self, 2453 pos=(0.7, 0.05), 2454 s=0.02, 2455 length=2, 2456 lw=4, 2457 c="k1", 2458 alpha=1, 2459 units="", 2460 gap=0.05, 2461 ) -> Union["vedo.visual.Actor2D", None]: 2462 """ 2463 Add a Scale Indicator. Only works in parallel mode (no perspective). 2464 2465 Arguments: 2466 pos : (list) 2467 fractional (x,y) position on the screen. 2468 s : (float) 2469 size of the text. 2470 length : (float) 2471 length of the line. 2472 units : (str) 2473 string to show units. 2474 gap : (float) 2475 separation of line and text. 2476 2477 Example: 2478 ```python 2479 from vedo import settings, Cube, Plotter 2480 settings.use_parallel_projection = True # or else it does not make sense! 2481 cube = Cube().alpha(0.2) 2482 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2483 plt.add_scale_indicator(units='um', c='blue4') 2484 plt.show(cube, "Scale indicator with units").close() 2485 ``` 2486 ![](https://vedo.embl.es/images/feats/scale_indicator.png) 2487 """ 2488 # Note that this cannot go in addons.py 2489 # because it needs callbacks and window size 2490 if not self.interactor: 2491 return None 2492 2493 ppoints = vtki.vtkPoints() # Generate the polyline 2494 psqr = [[0.0, gap], [length / 10, gap]] 2495 dd = psqr[1][0] - psqr[0][0] 2496 for i, pt in enumerate(psqr): 2497 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2498 lines = vtki.vtkCellArray() 2499 lines.InsertNextCell(len(psqr)) 2500 for i in range(len(psqr)): 2501 lines.InsertCellPoint(i) 2502 pd = vtki.vtkPolyData() 2503 pd.SetPoints(ppoints) 2504 pd.SetLines(lines) 2505 2506 wsx, wsy = self.window.GetSize() 2507 if not self.camera.GetParallelProjection(): 2508 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2509 return None 2510 2511 rlabel = vtki.new("VectorText") 2512 rlabel.SetText("scale") 2513 tf = vtki.new("TransformPolyDataFilter") 2514 tf.SetInputConnection(rlabel.GetOutputPort()) 2515 t = vtki.vtkTransform() 2516 t.Scale(s * wsy / wsx, s, 1) 2517 tf.SetTransform(t) 2518 2519 app = vtki.new("AppendPolyData") 2520 app.AddInputConnection(tf.GetOutputPort()) 2521 app.AddInputData(pd) 2522 2523 mapper = vtki.new("PolyDataMapper2D") 2524 mapper.SetInputConnection(app.GetOutputPort()) 2525 cs = vtki.vtkCoordinate() 2526 cs.SetCoordinateSystem(1) 2527 mapper.SetTransformCoordinate(cs) 2528 2529 fractor = vedo.visual.Actor2D() 2530 csys = fractor.GetPositionCoordinate() 2531 csys.SetCoordinateSystem(3) 2532 fractor.SetPosition(pos) 2533 fractor.SetMapper(mapper) 2534 fractor.GetProperty().SetColor(vedo.get_color(c)) 2535 fractor.GetProperty().SetOpacity(alpha) 2536 fractor.GetProperty().SetLineWidth(lw) 2537 fractor.GetProperty().SetDisplayLocationToForeground() 2538 2539 def sifunc(iren, ev): 2540 wsx, wsy = self.window.GetSize() 2541 ps = self.camera.GetParallelScale() 2542 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2543 if units: 2544 newtxt += " " + units 2545 if rlabel.GetText() != newtxt: 2546 rlabel.SetText(newtxt) 2547 2548 self.renderer.AddActor(fractor) 2549 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2550 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2551 self.interactor.AddObserver("InteractionEvent", sifunc) 2552 sifunc(0, 0) 2553 return fractor 2554 2555 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2556 """ 2557 Create an Event object with information of what was clicked. 2558 2559 If `enable_picking` is False, no picking will be performed. 2560 This can be useful to avoid double picking when using buttons. 2561 """ 2562 if not self.interactor: 2563 return Event() 2564 2565 if len(pos) > 0: 2566 x, y = pos 2567 self.interactor.SetEventPosition(pos) 2568 else: 2569 x, y = self.interactor.GetEventPosition() 2570 self.renderer = self.interactor.FindPokedRenderer(x, y) 2571 2572 self.picked2d = (x, y) 2573 2574 key = self.interactor.GetKeySym() 2575 2576 if key: 2577 if "_L" in key or "_R" in key: 2578 # skip things like Shift_R 2579 key = "" # better than None 2580 else: 2581 if self.interactor.GetShiftKey(): 2582 key = key.upper() 2583 2584 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2585 key = "underscore" 2586 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2587 key = "plus" 2588 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2589 key = "?" 2590 2591 if self.interactor.GetControlKey(): 2592 key = "Ctrl+" + key 2593 2594 if self.interactor.GetAltKey(): 2595 key = "Alt+" + key 2596 2597 if enable_picking: 2598 if not self.picker: 2599 self.picker = vtki.vtkPropPicker() 2600 2601 self.picker.PickProp(x, y, self.renderer) 2602 actor = self.picker.GetProp3D() 2603 # Note that GetProp3D already picks Assembly 2604 2605 xp, yp = self.interactor.GetLastEventPosition() 2606 dx, dy = x - xp, y - yp 2607 2608 delta3d = np.array([0, 0, 0]) 2609 2610 if actor: 2611 picked3d = np.array(self.picker.GetPickPosition()) 2612 2613 try: 2614 vobj = actor.retrieve_object() 2615 old_pt = np.asarray(vobj.picked3d) 2616 vobj.picked3d = picked3d 2617 delta3d = picked3d - old_pt 2618 except (AttributeError, TypeError): 2619 pass 2620 2621 else: 2622 picked3d = None 2623 2624 if not actor: # try 2D 2625 actor = self.picker.GetActor2D() 2626 2627 event = Event() 2628 event.name = ename 2629 event.title = self.title 2630 event.id = -1 # will be set by the timer wrapper function 2631 event.timerid = -1 # will be set by the timer wrapper function 2632 event.priority = -1 # will be set by the timer wrapper function 2633 event.time = time.time() 2634 event.at = self.renderers.index(self.renderer) 2635 event.keypress = key 2636 if enable_picking: 2637 try: 2638 event.object = actor.retrieve_object() 2639 except AttributeError: 2640 event.object = actor 2641 try: 2642 event.actor = actor.retrieve_object() # obsolete use object instead 2643 except AttributeError: 2644 event.actor = actor 2645 event.picked3d = picked3d 2646 event.picked2d = (x, y) 2647 event.delta2d = (dx, dy) 2648 event.angle2d = np.arctan2(dy, dx) 2649 event.speed2d = np.sqrt(dx * dx + dy * dy) 2650 event.delta3d = delta3d 2651 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2652 event.isPoints = isinstance(event.object, vedo.Points) 2653 event.isMesh = isinstance(event.object, vedo.Mesh) 2654 event.isAssembly = isinstance(event.object, vedo.Assembly) 2655 event.isVolume = isinstance(event.object, vedo.Volume) 2656 event.isImage = isinstance(event.object, vedo.Image) 2657 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2658 return event 2659 2660 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2661 """ 2662 Add a function to be executed while show() is active. 2663 2664 Return a unique id for the callback. 2665 2666 The callback function (see example below) exposes a dictionary 2667 with the following information: 2668 - `name`: event name, 2669 - `id`: event unique identifier, 2670 - `priority`: event priority (float), 2671 - `interactor`: the interactor object, 2672 - `at`: renderer nr. where the event occurred 2673 - `keypress`: key pressed as string 2674 - `actor`: object picked by the mouse 2675 - `picked3d`: point picked in world coordinates 2676 - `picked2d`: screen coords of the mouse pointer 2677 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2678 - `delta3d`: ...same but in 3D world coords 2679 - `angle2d`: angle of mouse movement on screen 2680 - `speed2d`: speed of mouse movement on screen 2681 - `speed3d`: speed of picked point in world coordinates 2682 - `isPoints`: True if of class 2683 - `isMesh`: True if of class 2684 - `isAssembly`: True if of class 2685 - `isVolume`: True if of class Volume 2686 - `isImage`: True if of class 2687 2688 If `enable_picking` is False, no picking will be performed. 2689 This can be useful to avoid double picking when using buttons. 2690 2691 Frequently used events are: 2692 - `KeyPress`, `KeyRelease`: listen to keyboard events 2693 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2694 - `MiddleButtonPress`, `MiddleButtonRelease` 2695 - `RightButtonPress`, `RightButtonRelease` 2696 - `MouseMove`: listen to mouse pointer changing position 2697 - `MouseWheelForward`, `MouseWheelBackward` 2698 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2699 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2700 - `ResetCamera`, `ResetCameraClippingRange` 2701 - `Error`, `Warning` 2702 - `Char` 2703 - `Timer` 2704 2705 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2706 2707 Example: 2708 ```python 2709 from vedo import * 2710 2711 def func(evt): 2712 # this function is called every time the mouse moves 2713 # (evt is a dotted dictionary) 2714 if not evt.object: 2715 return # no hit, return 2716 print("point coords =", evt.picked3d) 2717 # print(evt) # full event dump 2718 2719 elli = Ellipsoid() 2720 plt = Plotter(axes=1) 2721 plt.add_callback('mouse hovering', func) 2722 plt.show(elli).close() 2723 ``` 2724 2725 Examples: 2726 - [spline_draw1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw1.py) 2727 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2728 2729 ![](https://vedo.embl.es/images/advanced/spline_draw.png) 2730 2731 - ..and many others! 2732 """ 2733 from vtkmodules.util.misc import calldata_type 2734 2735 if not self.interactor: 2736 return 0 2737 2738 if vedo.settings.dry_run_mode >= 1: 2739 return 0 2740 2741 ######################################### 2742 @calldata_type(vtki.VTK_INT) 2743 def _func_wrap(iren, ename, timerid=None): 2744 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2745 event.timerid = timerid 2746 event.id = cid 2747 event.priority = priority 2748 self.last_event = event 2749 func(event) 2750 2751 ######################################### 2752 2753 event_name = utils.get_vtk_name_event(event_name) 2754 2755 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2756 # print(f"Registering event: {event_name} with id={cid}") 2757 return cid 2758 2759 def remove_callback(self, cid: Union[int, str]) -> Self: 2760 """ 2761 Remove a callback function by its id 2762 or a whole category of callbacks by their name. 2763 2764 Arguments: 2765 cid : (int, str) 2766 Unique id of the callback. 2767 If an event name is passed all callbacks of that type are removed. 2768 """ 2769 if self.interactor: 2770 if isinstance(cid, str): 2771 cid = utils.get_vtk_name_event(cid) 2772 self.interactor.RemoveObservers(cid) 2773 else: 2774 self.interactor.RemoveObserver(cid) 2775 return self 2776 2777 def remove_all_observers(self) -> Self: 2778 """ 2779 Remove all observers. 2780 2781 Example: 2782 ```python 2783 from vedo import * 2784 2785 def kfunc(event): 2786 print("Key pressed:", event.keypress) 2787 if event.keypress == 'q': 2788 plt.close() 2789 2790 def rfunc(event): 2791 if event.isImage: 2792 printc("Right-clicked!", event) 2793 plt.render() 2794 2795 img = Image(dataurl+"images/embryo.jpg") 2796 2797 plt = Plotter(size=(1050, 600)) 2798 plt.parallel_projection(True) 2799 plt.remove_all_observers() 2800 plt.add_callback("key press", kfunc) 2801 plt.add_callback("mouse right click", rfunc) 2802 plt.show("Right-Click Me! Press q to exit.", img) 2803 plt.close() 2804 ``` 2805 """ 2806 if self.interactor: 2807 self.interactor.RemoveAllObservers() 2808 return self 2809 2810 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2811 """ 2812 Start or stop an existing timer. 2813 2814 Arguments: 2815 action : (str) 2816 Either "create"/"start" or "destroy"/"stop" 2817 timer_id : (int) 2818 When stopping the timer, the ID of the timer as returned when created 2819 dt : (int) 2820 time in milliseconds between each repeated call 2821 one_shot : (bool) 2822 create a one shot timer of prescribed duration instead of a repeating one 2823 2824 Examples: 2825 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2826 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2827 2828 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 2829 """ 2830 if action in ("create", "start"): 2831 if timer_id is not None: 2832 vedo.logger.warning("you set a timer_id but it will be ignored.") 2833 if one_shot: 2834 timer_id = self.interactor.CreateOneShotTimer(dt) 2835 else: 2836 timer_id = self.interactor.CreateRepeatingTimer(dt) 2837 return timer_id 2838 2839 elif action in ("destroy", "stop"): 2840 if timer_id is not None: 2841 self.interactor.DestroyTimer(timer_id) 2842 else: 2843 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2844 else: 2845 e = f"in timer_callback(). Cannot understand action: {action}\n" 2846 e += " allowed actions are: ['start', 'stop']. Skipped." 2847 vedo.logger.error(e) 2848 return timer_id 2849 2850 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2851 """ 2852 Add a callback function that will be called when an event occurs. 2853 Consider using `add_callback()` instead. 2854 """ 2855 if not self.interactor: 2856 return -1 2857 event_name = utils.get_vtk_name_event(event_name) 2858 idd = self.interactor.AddObserver(event_name, func, priority) 2859 return idd 2860 2861 def compute_world_coordinate( 2862 self, 2863 pos2d: MutableSequence[float], 2864 at=None, 2865 objs=(), 2866 bounds=(), 2867 offset=None, 2868 pixeltol=None, 2869 worldtol=None, 2870 ) -> np.ndarray: 2871 """ 2872 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2873 If a set of meshes is passed then points are placed onto these. 2874 2875 Arguments: 2876 pos2d : (list) 2877 2D screen coordinates point. 2878 at : (int) 2879 renderer number. 2880 objs : (list) 2881 list of Mesh objects to project the point onto. 2882 bounds : (list) 2883 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2884 offset : (float) 2885 specify an offset value. 2886 pixeltol : (int) 2887 screen tolerance in pixels. 2888 worldtol : (float) 2889 world coordinates tolerance. 2890 2891 Returns: 2892 numpy array, the point in 3D world coordinates. 2893 2894 Examples: 2895 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2896 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2897 2898 ![](https://vedo.embl.es/images/basic/mousehover3.jpg) 2899 """ 2900 if at is not None: 2901 renderer = self.renderers[at] 2902 else: 2903 renderer = self.renderer 2904 2905 if not objs: 2906 pp = vtki.vtkFocalPlanePointPlacer() 2907 else: 2908 pps = vtki.vtkPolygonalSurfacePointPlacer() 2909 for ob in objs: 2910 pps.AddProp(ob.actor) 2911 pp = pps # type: ignore 2912 2913 if len(bounds) == 6: 2914 pp.SetPointBounds(bounds) 2915 if pixeltol: 2916 pp.SetPixelTolerance(pixeltol) 2917 if worldtol: 2918 pp.SetWorldTolerance(worldtol) 2919 if offset: 2920 pp.SetOffset(offset) 2921 2922 worldPos: MutableSequence[float] = [0, 0, 0] 2923 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2924 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2925 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2926 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2927 return np.array(worldPos) 2928 2929 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2930 """ 2931 Given a 3D points in the current renderer (or full window), 2932 find the screen pixel coordinates. 2933 2934 Example: 2935 ```python 2936 from vedo import * 2937 2938 elli = Ellipsoid().point_size(5) 2939 2940 plt = Plotter() 2941 plt.show(elli, "Press q to continue and print the info") 2942 2943 xyscreen = plt.compute_screen_coordinates(elli) 2944 print('xyscreen coords:', xyscreen) 2945 2946 # simulate an event happening at one point 2947 event = plt.fill_event(pos=xyscreen[123]) 2948 print(event) 2949 ``` 2950 """ 2951 try: 2952 obj = obj.vertices 2953 except AttributeError: 2954 pass 2955 2956 if utils.is_sequence(obj): 2957 pts = obj 2958 p2d = [] 2959 cs = vtki.vtkCoordinate() 2960 cs.SetCoordinateSystemToWorld() 2961 cs.SetViewport(self.renderer) 2962 for p in pts: 2963 cs.SetValue(p) 2964 if full_window: 2965 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2966 else: 2967 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2968 return np.array(p2d, dtype=int) 2969 2970 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2971 """ 2972 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2973 2974 Returns a frustum Mesh that contains the visible field of view. 2975 This can be used to select objects in a scene or select vertices. 2976 2977 Example: 2978 ```python 2979 from vedo import * 2980 2981 settings.enable_default_mouse_callbacks = False 2982 2983 def mode_select(objs): 2984 print("Selected objects:", objs) 2985 d0 = mode.start_x, mode.start_y # display coords 2986 d1 = mode.end_x, mode.end_y 2987 2988 frustum = plt.pick_area(d0, d1) 2989 col = np.random.randint(0, 10) 2990 infru = frustum.inside_points(mesh) 2991 infru.point_size(10).color(col) 2992 plt.add(frustum, infru).render() 2993 2994 mesh = Mesh(dataurl+"cow.vtk") 2995 mesh.color("k5").linewidth(1) 2996 2997 mode = interactor_modes.BlenderStyle() 2998 mode.callback_select = mode_select 2999 3000 plt = Plotter().user_mode(mode) 3001 plt.show(mesh, axes=1) 3002 ``` 3003 """ 3004 if at is not None: 3005 ren = self.renderers[at] 3006 else: 3007 ren = self.renderer 3008 area_picker = vtki.vtkAreaPicker() 3009 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 3010 planes = area_picker.GetFrustum() 3011 3012 fru = vtki.new("FrustumSource") 3013 fru.SetPlanes(planes) 3014 fru.ShowLinesOff() 3015 fru.Update() 3016 3017 afru = vedo.Mesh(fru.GetOutput()) 3018 afru.alpha(0.1).lw(1).pickable(False) 3019 afru.name = "Frustum" 3020 return afru 3021 3022 def _scan_input_return_acts(self, objs) -> Any: 3023 # scan the input and return a list of actors 3024 if not utils.is_sequence(objs): 3025 objs = [objs] 3026 3027 ################# 3028 wannabe_acts = [] 3029 for a in objs: 3030 3031 try: 3032 wannabe_acts.append(a.actor) 3033 except AttributeError: 3034 wannabe_acts.append(a) # already actor 3035 3036 try: 3037 wannabe_acts.append(a.scalarbar) 3038 except AttributeError: 3039 pass 3040 3041 try: 3042 for sh in a.shadows: 3043 wannabe_acts.append(sh.actor) 3044 except AttributeError: 3045 pass 3046 3047 try: 3048 wannabe_acts.append(a.trail.actor) 3049 if a.trail.shadows: # trails may also have shadows 3050 for sh in a.trail.shadows: 3051 wannabe_acts.append(sh.actor) 3052 except AttributeError: 3053 pass 3054 3055 ################# 3056 scanned_acts = [] 3057 for a in wannabe_acts: # scan content of list 3058 3059 if a is None: 3060 pass 3061 3062 elif isinstance(a, (vtki.vtkActor, vtki.vtkActor2D)): 3063 scanned_acts.append(a) 3064 3065 elif isinstance(a, str): 3066 # assume a 2D comment was given 3067 changed = False # check if one already exists so to just update text 3068 if self.renderer: # might be jupyter 3069 acs = self.renderer.GetActors2D() 3070 acs.InitTraversal() 3071 for i in range(acs.GetNumberOfItems()): 3072 act = acs.GetNextItem() 3073 if isinstance(act, vedo.shapes.Text2D): 3074 aposx, aposy = act.GetPosition() 3075 if aposx < 0.01 and aposy > 0.99: # "top-left" 3076 act.text(a) # update content! no appending nada 3077 changed = True 3078 break 3079 if not changed: 3080 out = vedo.shapes.Text2D(a) # append a new one 3081 scanned_acts.append(out) 3082 # scanned_acts.append(vedo.shapes.Text2D(a)) # naive version 3083 3084 elif isinstance(a, vtki.vtkPolyData): 3085 scanned_acts.append(vedo.Mesh(a).actor) 3086 3087 elif isinstance(a, vtki.vtkImageData): 3088 scanned_acts.append(vedo.Volume(a).actor) 3089 3090 elif isinstance(a, vedo.RectilinearGrid): 3091 scanned_acts.append(a.actor) 3092 3093 elif isinstance(a, vedo.StructuredGrid): 3094 scanned_acts.append(a.actor) 3095 3096 elif isinstance(a, vtki.vtkLight): 3097 scanned_acts.append(a) 3098 3099 elif isinstance(a, vedo.visual.LightKit): 3100 a.lightkit.AddLightsToRenderer(self.renderer) 3101 3102 elif isinstance(a, vtki.get_class("MultiBlockDataSet")): 3103 for i in range(a.GetNumberOfBlocks()): 3104 b = a.GetBlock(i) 3105 if isinstance(b, vtki.vtkPolyData): 3106 scanned_acts.append(vedo.Mesh(b).actor) 3107 elif isinstance(b, vtki.vtkImageData): 3108 scanned_acts.append(vedo.Volume(b).actor) 3109 3110 elif isinstance(a, (vtki.vtkProp, vtki.vtkInteractorObserver)): 3111 scanned_acts.append(a) 3112 3113 elif "trimesh" in str(type(a)): 3114 scanned_acts.append(utils.trimesh2vedo(a)) 3115 3116 elif "meshlab" in str(type(a)): 3117 if "MeshSet" in str(type(a)): 3118 for i in range(a.number_meshes()): 3119 if a.mesh_id_exists(i): 3120 scanned_acts.append(utils.meshlab2vedo(a.mesh(i))) 3121 else: 3122 scanned_acts.append(utils.meshlab2vedo(a)) 3123 3124 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 3125 import vedo.dolfin as vdlf 3126 3127 scanned_acts.append(vdlf.IMesh(a).actor) 3128 3129 elif "madcad" in str(type(a)): 3130 scanned_acts.append(utils.madcad2vedo(a).actor) 3131 3132 elif "TetgenIO" in str(type(a)): 3133 scanned_acts.append(vedo.TetMesh(a).shrink(0.9).c("pink7").actor) 3134 3135 elif "matplotlib.figure.Figure" in str(type(a)): 3136 scanned_acts.append(vedo.Image(a).clone2d("top-right", 0.6)) 3137 3138 else: 3139 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 3140 3141 return scanned_acts 3142 3143 def show( 3144 self, 3145 *objects, 3146 at=None, 3147 axes=None, 3148 resetcam=None, 3149 zoom=False, 3150 interactive=None, 3151 viewup="", 3152 azimuth=0.0, 3153 elevation=0.0, 3154 roll=0.0, 3155 camera=None, 3156 mode=None, 3157 rate=None, 3158 bg=None, 3159 bg2=None, 3160 size=None, 3161 title=None, 3162 screenshot="", 3163 ) -> Any: 3164 """ 3165 Render a list of objects. 3166 3167 Arguments: 3168 at : (int) 3169 number of the renderer to plot to, in case of more than one exists 3170 3171 axes : (int) 3172 axis type-1 can be fully customized by passing a dictionary. 3173 Check `addons.Axes()` for the full list of options. 3174 set the type of axes to be shown: 3175 - 0, no axes 3176 - 1, draw three gray grid walls 3177 - 2, show cartesian axes from (0,0,0) 3178 - 3, show positive range of cartesian axes from (0,0,0) 3179 - 4, show a triad at bottom left 3180 - 5, show a cube at bottom left 3181 - 6, mark the corners of the bounding box 3182 - 7, draw a 3D ruler at each side of the cartesian axes 3183 - 8, show the `vtkCubeAxesActor` object 3184 - 9, show the bounding box outLine 3185 - 10, show three circles representing the maximum bounding box 3186 - 11, show a large grid on the x-y plane 3187 - 12, show polar axes 3188 - 13, draw a simple ruler at the bottom of the window 3189 3190 azimuth/elevation/roll : (float) 3191 move camera accordingly the specified value 3192 3193 viewup: str, list 3194 either `['x', 'y', 'z']` or a vector to set vertical direction 3195 3196 resetcam : (bool) 3197 re-adjust camera position to fit objects 3198 3199 camera : (dict, vtkCamera) 3200 camera parameters can further be specified with a dictionary 3201 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3202 - pos, `(list)`, the position of the camera in world coordinates 3203 - focal_point `(list)`, the focal point of the camera in world coordinates 3204 - viewup `(list)`, the view up direction for the camera 3205 - distance `(float)`, set the focal point to the specified distance from the camera position. 3206 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3207 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3208 in world-coordinate distances. The default is 1. 3209 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3210 This method has no effect in perspective projection mode. 3211 3212 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3213 plane to be set a distance 'thickness' beyond the near clipping plane. 3214 3215 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3216 measured in degrees. The default angle is 30 degrees. 3217 This method has no effect in parallel projection mode. 3218 The formula for setting the angle up for perfect perspective viewing is: 3219 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3220 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3221 3222 interactive : (bool) 3223 pause and interact with window (True) or continue execution (False) 3224 3225 rate : (float) 3226 maximum rate of `show()` in Hertz 3227 3228 mode : (int, str) 3229 set the type of interaction: 3230 - 0 = TrackballCamera [default] 3231 - 1 = TrackballActor 3232 - 2 = JoystickCamera 3233 - 3 = JoystickActor 3234 - 4 = Flight 3235 - 5 = RubberBand2D 3236 - 6 = RubberBand3D 3237 - 7 = RubberBandZoom 3238 - 8 = Terrain 3239 - 9 = Unicam 3240 - 10 = Image 3241 - Check out `vedo.interaction_modes` for more options. 3242 3243 bg : (str, list) 3244 background color in RGB format, or string name 3245 3246 bg2 : (str, list) 3247 second background color to create a gradient background 3248 3249 size : (str, list) 3250 size of the window, e.g. size="fullscreen", or size=[600,400] 3251 3252 title : (str) 3253 window title text 3254 3255 screenshot : (str) 3256 save a screenshot of the window to file 3257 """ 3258 3259 if vedo.settings.dry_run_mode >= 2: 3260 return self 3261 3262 if self.wx_widget: 3263 return self 3264 3265 if self.renderers: # in case of notebooks 3266 3267 if at is None: 3268 at = self.renderers.index(self.renderer) 3269 3270 else: 3271 3272 if at >= len(self.renderers): 3273 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3274 vedo.logger.error(t) 3275 return self 3276 3277 self.renderer = self.renderers[at] 3278 3279 if title is not None: 3280 self.title = title 3281 3282 if size is not None: 3283 self.size = size 3284 if self.size[0] == "f": # full screen 3285 self.size = "fullscreen" 3286 self.window.SetFullScreen(True) 3287 self.window.BordersOn() 3288 else: 3289 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3290 3291 if vedo.settings.default_backend == "vtk": 3292 if str(bg).endswith(".hdr"): 3293 self._add_skybox(bg) 3294 else: 3295 if bg is not None: 3296 self.backgrcol = vedo.get_color(bg) 3297 self.renderer.SetBackground(self.backgrcol) 3298 if bg2 is not None: 3299 self.renderer.GradientBackgroundOn() 3300 self.renderer.SetBackground2(vedo.get_color(bg2)) 3301 3302 if axes is not None: 3303 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3304 objects = list(objects) 3305 objects.append(axes) # move it into the list of normal things to show 3306 axes = 0 3307 self.axes = axes 3308 3309 if interactive is not None: 3310 self._interactive = interactive 3311 if self.offscreen: 3312 self._interactive = False 3313 3314 # camera stuff 3315 if resetcam is not None: 3316 self.resetcam = resetcam 3317 3318 if camera is not None: 3319 self.resetcam = False 3320 viewup = "" 3321 if isinstance(camera, vtki.vtkCamera): 3322 cameracopy = vtki.vtkCamera() 3323 cameracopy.DeepCopy(camera) 3324 self.camera = cameracopy 3325 else: 3326 self.camera = utils.camera_from_dict(camera) 3327 3328 self.add(objects) 3329 3330 # Backend ############################################################### 3331 if vedo.settings.default_backend in ["k3d", "panel"]: 3332 return backends.get_notebook_backend(self.objects) 3333 ######################################################################### 3334 3335 for ia in utils.flatten(objects): 3336 try: 3337 # fix gray color labels and title to white or black 3338 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3339 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3340 c = (0.9, 0.9, 0.9) 3341 if np.sum(self.renderer.GetBackground()) > 1.5: 3342 c = (0.1, 0.1, 0.1) 3343 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3344 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3345 except AttributeError: 3346 pass 3347 3348 if self.sharecam: 3349 for r in self.renderers: 3350 r.SetActiveCamera(self.camera) 3351 3352 if self.axes is not None: 3353 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3354 bns = self.renderer.ComputeVisiblePropBounds() 3355 addons.add_global_axes(self.axes, bounds=bns) 3356 3357 # Backend ############################################################### 3358 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3359 return backends.get_notebook_backend() 3360 ######################################################################### 3361 3362 if self.resetcam: 3363 self.renderer.ResetCamera() 3364 3365 if len(self.renderers) > 1: 3366 self.add_renderer_frame() 3367 3368 if vedo.settings.default_backend == "2d" and not zoom: 3369 zoom = "tightest" 3370 3371 if zoom: 3372 if zoom == "tight": 3373 self.reset_camera(tight=0.04) 3374 elif zoom == "tightest": 3375 self.reset_camera(tight=0.0001) 3376 else: 3377 self.camera.Zoom(zoom) 3378 if elevation: 3379 self.camera.Elevation(elevation) 3380 if azimuth: 3381 self.camera.Azimuth(azimuth) 3382 if roll: 3383 self.camera.Roll(roll) 3384 3385 if len(viewup) > 0: 3386 b = self.renderer.ComputeVisiblePropBounds() 3387 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3388 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3389 if viewup == "x": 3390 sz = np.linalg.norm(sz) 3391 self.camera.SetViewUp([1, 0, 0]) 3392 self.camera.SetPosition(cm + sz) 3393 elif viewup == "y": 3394 sz = np.linalg.norm(sz) 3395 self.camera.SetViewUp([0, 1, 0]) 3396 self.camera.SetPosition(cm + sz) 3397 elif viewup == "z": 3398 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3399 self.camera.SetViewUp([0, 0, 1]) 3400 self.camera.SetPosition(cm + 2 * sz) 3401 elif utils.is_sequence(viewup): 3402 sz = np.linalg.norm(sz) 3403 self.camera.SetViewUp(viewup) 3404 cpos = np.cross([0, 1, 0], viewup) 3405 self.camera.SetPosition(cm - 2 * sz * cpos) 3406 3407 self.renderer.ResetCameraClippingRange() 3408 3409 self.initialize_interactor() 3410 3411 if vedo.settings.immediate_rendering: 3412 self.window.Render() ##################### <-------------- Render 3413 3414 if self.interactor: # can be offscreen or not the vtk backend.. 3415 3416 self.window.SetWindowName(self.title) 3417 3418 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3419 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3420 # print(pic.dataset)# Array 0 name PNGImage 3421 # self.window.SetIcon(pic.dataset) 3422 3423 try: 3424 # Needs "pip install pyobjc" on Mac OSX 3425 if ( 3426 self._cocoa_initialized is False 3427 and "Darwin" in vedo.sys_platform 3428 and not self.offscreen 3429 ): 3430 self._cocoa_initialized = True 3431 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3432 pid = os.getpid() 3433 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3434 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3435 except: 3436 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3437 pass 3438 3439 # Set the interaction style 3440 if mode is not None: 3441 self.user_mode(mode) 3442 if self.qt_widget and mode is None: 3443 self.user_mode(0) 3444 3445 if screenshot: 3446 self.screenshot(screenshot) 3447 3448 if self._interactive: 3449 self.interactor.Start() 3450 if self._must_close_now: 3451 self.interactor.GetRenderWindow().Finalize() 3452 self.interactor.TerminateApp() 3453 self.camera = None 3454 self.renderer = None 3455 self.renderers = [] 3456 self.window = None 3457 self.interactor = None 3458 return self 3459 3460 if rate: 3461 if self.clock is None: # set clock and limit rate 3462 self._clockt0 = time.time() 3463 self.clock = 0.0 3464 else: 3465 t = time.time() - self._clockt0 3466 elapsed = t - self.clock 3467 mint = 1.0 / rate 3468 if elapsed < mint: 3469 time.sleep(mint - elapsed) 3470 self.clock = time.time() - self._clockt0 3471 3472 # 2d #################################################################### 3473 if vedo.settings.default_backend in ["2d"]: 3474 return backends.get_notebook_backend() 3475 ######################################################################### 3476 3477 return self 3478 3479 3480 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3481 """Add a draggable inset space into a renderer. 3482 3483 Arguments: 3484 at : (int) 3485 specify the renderer number 3486 pos : (list) 3487 icon position in the range [1-4] indicating one of the 4 corners, 3488 or it can be a tuple (x,y) as a fraction of the renderer size. 3489 size : (float) 3490 size of the square inset 3491 draggable : (bool) 3492 if True the subrenderer space can be dragged around 3493 c : (color) 3494 color of the inset frame when dragged 3495 3496 Examples: 3497 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3498 3499 ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg) 3500 """ 3501 if not self.interactor: 3502 return None 3503 3504 if not self.renderer: 3505 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3506 return None 3507 3508 options = dict(options) 3509 pos = options.pop("pos", 0) 3510 size = options.pop("size", 0.1) 3511 c = options.pop("c", "lb") 3512 at = options.pop("at", None) 3513 draggable = options.pop("draggable", True) 3514 3515 r, g, b = vedo.get_color(c) 3516 widget = vtki.vtkOrientationMarkerWidget() 3517 widget.SetOutlineColor(r, g, b) 3518 if len(objects) == 1: 3519 widget.SetOrientationMarker(objects[0].actor) 3520 else: 3521 widget.SetOrientationMarker(vedo.Assembly(objects)) 3522 3523 widget.SetInteractor(self.interactor) 3524 3525 if utils.is_sequence(pos): 3526 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3527 else: 3528 if pos < 2: 3529 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3530 elif pos == 2: 3531 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3532 elif pos == 3: 3533 widget.SetViewport(0, 0, size * 2, size * 2) 3534 elif pos == 4: 3535 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3536 widget.EnabledOn() 3537 widget.SetInteractive(draggable) 3538 if at is not None and at < len(self.renderers): 3539 widget.SetCurrentRenderer(self.renderers[at]) 3540 else: 3541 widget.SetCurrentRenderer(self.renderer) 3542 self.widgets.append(widget) 3543 return widget 3544 3545 def clear(self, at=None, deep=False) -> Self: 3546 """Clear the scene from all meshes and volumes.""" 3547 if at is not None: 3548 renderer = self.renderers[at] 3549 else: 3550 renderer = self.renderer 3551 if not renderer: 3552 return self 3553 3554 if deep: 3555 renderer.RemoveAllViewProps() 3556 else: 3557 for ob in set( 3558 self.get_meshes() 3559 + self.get_volumes() 3560 + self.objects 3561 + self.axes_instances 3562 ): 3563 if isinstance(ob, vedo.shapes.Text2D): 3564 continue 3565 self.remove(ob) 3566 try: 3567 if ob.scalarbar: 3568 self.remove(ob.scalarbar) 3569 except AttributeError: 3570 pass 3571 return self 3572 3573 def break_interaction(self) -> Self: 3574 """Break window interaction and return to the python execution flow""" 3575 if self.interactor: 3576 self.check_actors_trasform() 3577 self.interactor.ExitCallback() 3578 return self 3579 3580 def freeze(self, value=True) -> Self: 3581 """Freeze the current renderer. Use this with `sharecam=False`.""" 3582 if not self.interactor: 3583 return self 3584 if not self.renderer: 3585 return self 3586 self.renderer.SetInteractive(not value) 3587 return self 3588 3589 def user_mode(self, mode) -> Self: 3590 """ 3591 Modify the user interaction mode. 3592 3593 Examples: 3594 ```python 3595 from vedo import * 3596 mode = interactor_modes.MousePan() 3597 mesh = Mesh(dataurl+"cow.vtk") 3598 plt = Plotter().user_mode(mode) 3599 plt.show(mesh, axes=1) 3600 ``` 3601 See also: 3602 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3603 """ 3604 if not self.interactor: 3605 return self 3606 3607 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3608 # print("Current style:", curr_style) 3609 if curr_style.endswith("Actor"): 3610 self.check_actors_trasform() 3611 3612 if isinstance(mode, (str, int)): 3613 # Set the style of interaction 3614 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3615 if mode in (0, "TrackballCamera"): 3616 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3617 self.interactor.RemoveObservers("CharEvent") 3618 elif mode in (1, "TrackballActor"): 3619 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3620 elif mode in (2, "JoystickCamera"): 3621 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3622 elif mode in (3, "JoystickActor"): 3623 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3624 elif mode in (4, "Flight"): 3625 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3626 elif mode in (5, "RubberBand2D"): 3627 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3628 elif mode in (6, "RubberBand3D"): 3629 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3630 elif mode in (7, "RubberBandZoom"): 3631 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3632 elif mode in (8, "Terrain"): 3633 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3634 elif mode in (9, "Unicam"): 3635 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3636 elif mode in (10, "Image", "image", "2d"): 3637 astyle = vtki.new("InteractorStyleImage") 3638 astyle.SetInteractionModeToImage3D() 3639 self.interactor.SetInteractorStyle(astyle) 3640 else: 3641 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3642 3643 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3644 # set a custom interactor style 3645 if hasattr(mode, "interactor"): 3646 mode.interactor = self.interactor 3647 mode.renderer = self.renderer # type: ignore 3648 mode.SetInteractor(self.interactor) 3649 mode.SetDefaultRenderer(self.renderer) 3650 self.interactor.SetInteractorStyle(mode) 3651 3652 return self 3653 3654 def close(self) -> Self: 3655 """Close the plotter.""" 3656 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3657 vedo.last_figure = None 3658 self.last_event = None 3659 self.sliders = [] 3660 self.buttons = [] 3661 self.widgets = [] 3662 self.hover_legends = [] 3663 self.background_renderer = None 3664 self._extralight = None 3665 3666 self.hint_widget = None 3667 self.cutter_widget = None 3668 3669 if vedo.settings.dry_run_mode >= 2: 3670 return self 3671 3672 if not hasattr(self, "window"): 3673 return self 3674 if not self.window: 3675 return self 3676 if not hasattr(self, "interactor"): 3677 return self 3678 if not self.interactor: 3679 return self 3680 3681 ################################################### 3682 try: 3683 if "Darwin" in vedo.sys_platform: 3684 self.interactor.ProcessEvents() 3685 except: 3686 pass 3687 3688 self._must_close_now = True 3689 3690 if vedo.plotter_instance == self: 3691 vedo.plotter_instance = None 3692 3693 if self.interactor and self._interactive: 3694 self.break_interaction() 3695 elif self._must_close_now: 3696 # dont call ExitCallback here 3697 if self.interactor: 3698 self.break_interaction() 3699 self.interactor.GetRenderWindow().Finalize() 3700 self.interactor.TerminateApp() 3701 self.camera = None 3702 self.renderer = None 3703 self.renderers = [] 3704 self.window = None 3705 self.interactor = None 3706 return self 3707 3708 @property 3709 def camera(self): 3710 """Return the current active camera.""" 3711 if self.renderer: 3712 return self.renderer.GetActiveCamera() 3713 3714 @camera.setter 3715 def camera(self, cam): 3716 if self.renderer: 3717 if isinstance(cam, dict): 3718 cam = utils.camera_from_dict(cam) 3719 self.renderer.SetActiveCamera(cam) 3720 3721 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3722 """ 3723 Take a screenshot of the Plotter window. 3724 3725 Arguments: 3726 scale : (int) 3727 set image magnification as an integer multiplicating factor 3728 asarray : (bool) 3729 return a numpy array of the image instead of writing a file 3730 3731 Warning: 3732 If you get black screenshots try to set `interactive=False` in `show()` 3733 then call `screenshot()` and `plt.interactive()` afterwards. 3734 3735 Example: 3736 ```py 3737 from vedo import * 3738 sphere = Sphere().linewidth(1) 3739 plt = show(sphere, interactive=False) 3740 plt.screenshot('image.png') 3741 plt.interactive() 3742 plt.close() 3743 ``` 3744 3745 Example: 3746 ```py 3747 from vedo import * 3748 sphere = Sphere().linewidth(1) 3749 plt = show(sphere, interactive=False) 3750 plt.screenshot('anotherimage.png') 3751 plt.interactive() 3752 plt.close() 3753 ``` 3754 """ 3755 return vedo.file_io.screenshot(filename, scale, asarray) 3756 3757 def toimage(self, scale=1) -> "vedo.image.Image": 3758 """ 3759 Generate a `Image` object from the current rendering window. 3760 3761 Arguments: 3762 scale : (int) 3763 set image magnification as an integer multiplicating factor 3764 """ 3765 if vedo.settings.screeshot_large_image: 3766 w2if = vtki.new("RenderLargeImage") 3767 w2if.SetInput(self.renderer) 3768 w2if.SetMagnification(scale) 3769 else: 3770 w2if = vtki.new("WindowToImageFilter") 3771 w2if.SetInput(self.window) 3772 if hasattr(w2if, "SetScale"): 3773 w2if.SetScale(scale, scale) 3774 if vedo.settings.screenshot_transparent_background: 3775 w2if.SetInputBufferTypeToRGBA() 3776 w2if.ReadFrontBufferOff() # read from the back buffer 3777 w2if.Update() 3778 return vedo.image.Image(w2if.GetOutput()) 3779 3780 def export(self, filename="scene.npz", binary=False) -> Self: 3781 """ 3782 Export scene to file to HTML, X3D or Numpy file. 3783 3784 Examples: 3785 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3786 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3787 """ 3788 vedo.file_io.export_window(filename, binary=binary) 3789 return self 3790 3791 def color_picker(self, xy, verbose=False): 3792 """Pick color of specific (x,y) pixel on the screen.""" 3793 w2if = vtki.new("WindowToImageFilter") 3794 w2if.SetInput(self.window) 3795 w2if.ReadFrontBufferOff() 3796 w2if.Update() 3797 nx, ny = self.window.GetSize() 3798 varr = w2if.GetOutput().GetPointData().GetScalars() 3799 3800 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3801 x, y = int(xy[0]), int(xy[1]) 3802 if y < ny and x < nx: 3803 3804 rgb = arr[y, x] 3805 3806 if verbose: 3807 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3808 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3809 vedo.printc("█", c=[0, rgb[1], 0], end="") 3810 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3811 vedo.printc("] = ", end="") 3812 cnm = vedo.get_color_name(rgb) 3813 if np.sum(rgb) < 150: 3814 vedo.printc( 3815 rgb.tolist(), 3816 vedo.colors.rgb2hex(np.array(rgb) / 255), 3817 c="w", 3818 bc=rgb, 3819 invert=1, 3820 end="", 3821 ) 3822 vedo.printc(" -> " + cnm, invert=1, c="w") 3823 else: 3824 vedo.printc( 3825 rgb.tolist(), 3826 vedo.colors.rgb2hex(np.array(rgb) / 255), 3827 c=rgb, 3828 end="", 3829 ) 3830 vedo.printc(" -> " + cnm, c=cnm) 3831 3832 return rgb 3833 3834 return None 3835 3836 ####################################################################### 3837 def _default_mouseleftclick(self, iren, event) -> None: 3838 x, y = iren.GetEventPosition() 3839 renderer = iren.FindPokedRenderer(x, y) 3840 picker = vtki.vtkPropPicker() 3841 picker.PickProp(x, y, renderer) 3842 3843 self.renderer = renderer 3844 3845 clicked_actor = picker.GetActor() 3846 # clicked_actor2D = picker.GetActor2D() 3847 3848 # print('_default_mouseleftclick mouse at', x, y) 3849 # print("picked Volume:", [picker.GetVolume()]) 3850 # print("picked Actor2D:", [picker.GetActor2D()]) 3851 # print("picked Assembly:", [picker.GetAssembly()]) 3852 # print("picked Prop3D:", [picker.GetProp3D()]) 3853 3854 if not clicked_actor: 3855 clicked_actor = picker.GetAssembly() 3856 3857 if not clicked_actor: 3858 clicked_actor = picker.GetProp3D() 3859 3860 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3861 return 3862 3863 self.picked3d = picker.GetPickPosition() 3864 self.picked2d = np.array([x, y]) 3865 3866 if not clicked_actor: 3867 return 3868 3869 self.justremoved = None 3870 self.clicked_actor = clicked_actor 3871 3872 try: # might not be a vedo obj 3873 self.clicked_object = clicked_actor.retrieve_object() 3874 # save this info in the object itself 3875 self.clicked_object.picked3d = self.picked3d 3876 self.clicked_object.picked2d = self.picked2d 3877 except AttributeError: 3878 pass 3879 3880 # ----------- 3881 # if "Histogram1D" in picker.GetAssembly().__class__.__name__: 3882 # histo = picker.GetAssembly() 3883 # if histo.verbose: 3884 # x = self.picked3d[0] 3885 # idx = np.digitize(x, histo.edges) - 1 3886 # f = histo.frequencies[idx] 3887 # cn = histo.centers[idx] 3888 # vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3889 3890 ####################################################################### 3891 def _default_keypress(self, iren, event) -> None: 3892 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3893 3894 key = iren.GetKeySym() 3895 3896 if "_L" in key or "_R" in key: 3897 return 3898 3899 if iren.GetShiftKey(): 3900 key = key.upper() 3901 3902 if iren.GetControlKey(): 3903 key = "Ctrl+" + key 3904 3905 if iren.GetAltKey(): 3906 key = "Alt+" + key 3907 3908 ####################################################### 3909 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3910 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3911 # iren.GetKeyCode(), iren.GetRepeatCount()) 3912 ####################################################### 3913 3914 x, y = iren.GetEventPosition() 3915 renderer = iren.FindPokedRenderer(x, y) 3916 3917 if key in ["q", "Return"]: 3918 self.break_interaction() 3919 return 3920 3921 elif key in ["Ctrl+q", "Ctrl+w", "Escape"]: 3922 self.close() 3923 return 3924 3925 elif key == "F1": 3926 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3927 self.break_interaction() 3928 sys.exit(0) 3929 3930 elif key == "Down": 3931 if self.clicked_object and self.clicked_object in self.get_meshes(): 3932 self.clicked_object.alpha(0.02) 3933 if hasattr(self.clicked_object, "properties_backface"): 3934 bfp = self.clicked_actor.GetBackfaceProperty() 3935 self.clicked_object.properties_backface = bfp # save it 3936 self.clicked_actor.SetBackfaceProperty(None) 3937 else: 3938 for obj in self.get_meshes(): 3939 if obj: 3940 obj.alpha(0.02) 3941 bfp = obj.actor.GetBackfaceProperty() 3942 if bfp and hasattr(obj, "properties_backface"): 3943 obj.properties_backface = bfp 3944 obj.actor.SetBackfaceProperty(None) 3945 3946 elif key == "Left": 3947 if self.clicked_object and self.clicked_object in self.get_meshes(): 3948 ap = self.clicked_object.properties 3949 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3950 ap.SetOpacity(aal) 3951 bfp = self.clicked_actor.GetBackfaceProperty() 3952 if bfp and hasattr(self.clicked_object, "properties_backface"): 3953 self.clicked_object.properties_backface = bfp 3954 self.clicked_actor.SetBackfaceProperty(None) 3955 else: 3956 for a in self.get_meshes(): 3957 if a: 3958 ap = a.properties 3959 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3960 ap.SetOpacity(aal) 3961 bfp = a.actor.GetBackfaceProperty() 3962 if bfp and hasattr(a, "properties_backface"): 3963 a.properties_backface = bfp 3964 a.actor.SetBackfaceProperty(None) 3965 3966 elif key == "Right": 3967 if self.clicked_object and self.clicked_object in self.get_meshes(): 3968 ap = self.clicked_object.properties 3969 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3970 ap.SetOpacity(aal) 3971 if ( 3972 aal == 1 3973 and hasattr(self.clicked_object, "properties_backface") 3974 and self.clicked_object.properties_backface 3975 ): 3976 # put back 3977 self.clicked_actor.SetBackfaceProperty( 3978 self.clicked_object.properties_backface) 3979 else: 3980 for a in self.get_meshes(): 3981 if a: 3982 ap = a.properties 3983 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3984 ap.SetOpacity(aal) 3985 if aal == 1 and hasattr(a, "properties_backface") and a.properties_backface: 3986 a.actor.SetBackfaceProperty(a.properties_backface) 3987 3988 elif key == "Up": 3989 if self.clicked_object and self.clicked_object in self.get_meshes(): 3990 self.clicked_object.properties.SetOpacity(1) 3991 if hasattr(self.clicked_object, "properties_backface") and self.clicked_object.properties_backface: 3992 self.clicked_object.actor.SetBackfaceProperty(self.clicked_object.properties_backface) 3993 else: 3994 for a in self.get_meshes(): 3995 if a: 3996 a.properties.SetOpacity(1) 3997 if hasattr(a, "properties_backface") and a.properties_backface: 3998 a.actor.SetBackfaceProperty(a.properties_backface) 3999 4000 elif key == "P": 4001 if self.clicked_object and self.clicked_object in self.get_meshes(): 4002 objs = [self.clicked_object] 4003 else: 4004 objs = self.get_meshes() 4005 for ia in objs: 4006 try: 4007 ps = ia.properties.GetPointSize() 4008 if ps > 1: 4009 ia.properties.SetPointSize(ps - 1) 4010 ia.properties.SetRepresentationToPoints() 4011 except AttributeError: 4012 pass 4013 4014 elif key == "p": 4015 if self.clicked_object and self.clicked_object in self.get_meshes(): 4016 objs = [self.clicked_object] 4017 else: 4018 objs = self.get_meshes() 4019 for ia in objs: 4020 try: 4021 ps = ia.properties.GetPointSize() 4022 ia.properties.SetPointSize(ps + 2) 4023 ia.properties.SetRepresentationToPoints() 4024 except AttributeError: 4025 pass 4026 4027 elif key == "U": 4028 pval = renderer.GetActiveCamera().GetParallelProjection() 4029 renderer.GetActiveCamera().SetParallelProjection(not pval) 4030 if pval: 4031 renderer.ResetCamera() 4032 4033 elif key == "r": 4034 renderer.ResetCamera() 4035 4036 elif key == "h": 4037 msg = f" vedo {vedo.__version__}" 4038 msg += f" | vtk {vtki.vtkVersion().GetVTKVersion()}" 4039 msg += f" | numpy {np.__version__}" 4040 msg += f" | python {sys.version_info[0]}.{sys.version_info[1]}, press: " 4041 vedo.printc(msg.ljust(75), invert=True) 4042 msg = ( 4043 " i print info about the last clicked object \n" 4044 " I print color of the pixel under the mouse \n" 4045 " Y show the pipeline for this object as a graph \n" 4046 " <- -> use arrows to reduce/increase opacity \n" 4047 " x toggle mesh visibility \n" 4048 " w toggle wireframe/surface style \n" 4049 " l toggle surface edges visibility \n" 4050 " p/P hide surface faces and show only points \n" 4051 " 1-3 cycle surface color (2=light, 3=dark) \n" 4052 " 4 cycle color map (press shift-4 to go back) \n" 4053 " 5-6 cycle point-cell arrays (shift to go back) \n" 4054 " 7-8 cycle background and gradient color \n" 4055 " 09+- cycle axes styles (on keypad, or press +/-) \n" 4056 " k cycle available lighting styles \n" 4057 " K toggle shading as flat or phong \n" 4058 " A toggle anti-aliasing \n" 4059 " D toggle depth-peeling (for transparencies) \n" 4060 " U toggle perspective/parallel projection \n" 4061 " o/O toggle extra light to scene and rotate it \n" 4062 " a toggle interaction to Actor Mode \n" 4063 " n toggle surface normals \n" 4064 " r reset camera position \n" 4065 " R reset camera to the closest orthogonal view \n" 4066 " . fly camera to the last clicked point \n" 4067 " C print the current camera parameters state \n" 4068 " X invoke a cutter widget tool \n" 4069 " S save a screenshot of the current scene \n" 4070 " E/F export 3D scene to numpy file or X3D \n" 4071 " q return control to python script \n" 4072 " Esc abort execution and exit python kernel " 4073 ) 4074 vedo.printc(msg, dim=True, italic=True, bold=True) 4075 vedo.printc( 4076 " Check out the documentation at: https://vedo.embl.es ".ljust(75), 4077 invert=True, 4078 bold=True, 4079 ) 4080 return 4081 4082 elif key == "a": 4083 cur = iren.GetInteractorStyle() 4084 if isinstance(cur, vtki.get_class("InteractorStyleTrackballCamera")): 4085 msg = "Interactor style changed to TrackballActor\n" 4086 msg += " you can now move and rotate individual meshes:\n" 4087 msg += " press X twice to save the repositioned mesh\n" 4088 msg += " press 'a' to go back to normal style" 4089 vedo.printc(msg) 4090 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 4091 else: 4092 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 4093 return 4094 4095 elif key == "A": # toggle antialiasing 4096 msam = self.window.GetMultiSamples() 4097 if not msam: 4098 self.window.SetMultiSamples(16) 4099 else: 4100 self.window.SetMultiSamples(0) 4101 msam = self.window.GetMultiSamples() 4102 if msam: 4103 vedo.printc(f"Antialiasing set to {msam} samples", c=bool(msam)) 4104 else: 4105 vedo.printc("Antialiasing disabled", c=bool(msam)) 4106 4107 elif key == "D": # toggle depthpeeling 4108 udp = not renderer.GetUseDepthPeeling() 4109 renderer.SetUseDepthPeeling(udp) 4110 # self.renderer.SetUseDepthPeelingForVolumes(udp) 4111 if udp: 4112 self.window.SetAlphaBitPlanes(1) 4113 renderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 4114 renderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 4115 self.interactor.Render() 4116 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 4117 rnr = self.renderers.index(renderer) 4118 vedo.printc(f"Depth peeling set to {udp} for renderer nr.{rnr}", c=udp) 4119 if not wasUsed and udp: 4120 vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True) 4121 return 4122 4123 elif key == "period": 4124 if self.picked3d: 4125 self.fly_to(self.picked3d) 4126 return 4127 4128 elif key == "S": 4129 fname = "screenshot.png" 4130 i = 1 4131 while os.path.isfile(fname): 4132 fname = f"screenshot{i}.png" 4133 i += 1 4134 vedo.file_io.screenshot(fname) 4135 vedo.printc(rf":camera: Saved rendering window to {fname}", c="b") 4136 return 4137 4138 elif key == "C": 4139 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 4140 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 4141 # This could happen for plotting geological models with UTM coordinate systems 4142 cam = renderer.GetActiveCamera() 4143 vedo.printc("\n###################################################", c="y") 4144 vedo.printc("## Template python code to position this camera: ##", c="y") 4145 vedo.printc("cam = dict(", c="y") 4146 vedo.printc(" pos=" + utils.precision(cam.GetPosition(), 6) + ",", c="y") 4147 vedo.printc(" focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y") 4148 vedo.printc(" viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y") 4149 vedo.printc(" roll=" + utils.precision(cam.GetRoll(), 6) + ",", c="y") 4150 if cam.GetParallelProjection(): 4151 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 4152 else: 4153 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 4154 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 4155 vedo.printc(')', c='y') 4156 vedo.printc('show(mymeshes, camera=cam)', c='y') 4157 vedo.printc('###################################################', c='y') 4158 return 4159 4160 elif key == "R": 4161 self.reset_viewup() 4162 4163 elif key == "w": 4164 try: 4165 if self.clicked_object.properties.GetRepresentation() == 1: # toggle 4166 self.clicked_object.properties.SetRepresentationToSurface() 4167 else: 4168 self.clicked_object.properties.SetRepresentationToWireframe() 4169 except AttributeError: 4170 pass 4171 4172 elif key == "1": 4173 try: 4174 self._icol += 1 4175 self.clicked_object.mapper.ScalarVisibilityOff() 4176 pal = vedo.colors.palettes[vedo.settings.palette % len(vedo.colors.palettes)] 4177 self.clicked_object.c(pal[(self._icol) % 10]) 4178 self.remove(self.clicked_object.scalarbar) 4179 except AttributeError: 4180 pass 4181 4182 elif key == "2": # dark colors 4183 try: 4184 bsc = ["k1", "k2", "k3", "k4", 4185 "b1", "b2", "b3", "b4", 4186 "p1", "p2", "p3", "p4", 4187 "g1", "g2", "g3", "g4", 4188 "r1", "r2", "r3", "r4", 4189 "o1", "o2", "o3", "o4", 4190 "y1", "y2", "y3", "y4"] 4191 self._icol += 1 4192 if self.clicked_object: 4193 self.clicked_object.mapper.ScalarVisibilityOff() 4194 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4195 self.clicked_object.c(newcol) 4196 self.remove(self.clicked_object.scalarbar) 4197 except AttributeError: 4198 pass 4199 4200 elif key == "3": # light colors 4201 try: 4202 bsc = ["k6", "k7", "k8", "k9", 4203 "b6", "b7", "b8", "b9", 4204 "p6", "p7", "p8", "p9", 4205 "g6", "g7", "g8", "g9", 4206 "r6", "r7", "r8", "r9", 4207 "o6", "o7", "o8", "o9", 4208 "y6", "y7", "y8", "y9"] 4209 self._icol += 1 4210 if self.clicked_object: 4211 self.clicked_object.mapper.ScalarVisibilityOff() 4212 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4213 self.clicked_object.c(newcol) 4214 self.remove(self.clicked_object.scalarbar) 4215 except AttributeError: 4216 pass 4217 4218 elif key == "4": # cmap name cycle 4219 ob = self.clicked_object 4220 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4221 return 4222 if not ob.mapper.GetScalarVisibility(): 4223 return 4224 onwhat = ob.mapper.GetScalarModeAsString() # UsePointData/UseCellData 4225 4226 cmap_names = [ 4227 "Accent", "Paired", 4228 "rainbow", "rainbow_r", 4229 "Spectral", "Spectral_r", 4230 "gist_ncar", "gist_ncar_r", 4231 "viridis", "viridis_r", 4232 "hot", "hot_r", 4233 "terrain", "ocean", 4234 "coolwarm", "seismic", "PuOr", "RdYlGn", 4235 ] 4236 try: 4237 i = cmap_names.index(ob._cmap_name) 4238 if iren.GetShiftKey(): 4239 i -= 1 4240 else: 4241 i += 1 4242 if i >= len(cmap_names): 4243 i = 0 4244 if i < 0: 4245 i = len(cmap_names) - 1 4246 except ValueError: 4247 i = 0 4248 4249 ob._cmap_name = cmap_names[i] 4250 ob.cmap(ob._cmap_name, on=onwhat) 4251 if ob.scalarbar: 4252 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4253 self.remove(ob.scalarbar) 4254 title = ob.scalarbar.GetTitle() 4255 ob.add_scalarbar(title=title) 4256 self.add(ob.scalarbar).render() 4257 elif isinstance(ob.scalarbar, vedo.Assembly): 4258 self.remove(ob.scalarbar) 4259 ob.add_scalarbar3d(title=ob._cmap_name) 4260 self.add(ob.scalarbar) 4261 4262 vedo.printc( 4263 f"Name:'{ob.name}'," if ob.name else "", 4264 f"range:{utils.precision(ob.mapper.GetScalarRange(),3)},", 4265 f"colormap:'{ob._cmap_name}'", c="g", bold=False, 4266 ) 4267 4268 elif key == "5": # cycle pointdata array 4269 ob = self.clicked_object 4270 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4271 return 4272 4273 arrnames = ob.pointdata.keys() 4274 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4275 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4276 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4277 if len(arrnames) == 0: 4278 return 4279 ob.mapper.SetScalarVisibility(1) 4280 4281 if not ob._cmap_name: 4282 ob._cmap_name = "rainbow" 4283 4284 try: 4285 curr_name = ob.dataset.GetPointData().GetScalars().GetName() 4286 i = arrnames.index(curr_name) 4287 if "normals" in curr_name.lower(): 4288 return 4289 if iren.GetShiftKey(): 4290 i -= 1 4291 else: 4292 i += 1 4293 if i >= len(arrnames): 4294 i = 0 4295 if i < 0: 4296 i = len(arrnames) - 1 4297 except (ValueError, AttributeError): 4298 i = 0 4299 4300 ob.cmap(ob._cmap_name, arrnames[i], on="points") 4301 if ob.scalarbar: 4302 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4303 self.remove(ob.scalarbar) 4304 title = ob.scalarbar.GetTitle() 4305 ob.scalarbar = None 4306 ob.add_scalarbar(title=arrnames[i]) 4307 self.add(ob.scalarbar) 4308 elif isinstance(ob.scalarbar, vedo.Assembly): 4309 self.remove(ob.scalarbar) 4310 ob.scalarbar = None 4311 ob.add_scalarbar3d(title=arrnames[i]) 4312 self.add(ob.scalarbar) 4313 else: 4314 vedo.printc( 4315 f"Name:'{ob.name}'," if ob.name else "", 4316 f"active pointdata array: '{arrnames[i]}'", 4317 c="g", bold=False, 4318 ) 4319 4320 elif key == "6": # cycle celldata array 4321 ob = self.clicked_object 4322 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4323 return 4324 4325 arrnames = ob.celldata.keys() 4326 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4327 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4328 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4329 if len(arrnames) == 0: 4330 return 4331 ob.mapper.SetScalarVisibility(1) 4332 4333 if not ob._cmap_name: 4334 ob._cmap_name = "rainbow" 4335 4336 try: 4337 curr_name = ob.dataset.GetCellData().GetScalars().GetName() 4338 i = arrnames.index(curr_name) 4339 if "normals" in curr_name.lower(): 4340 return 4341 if iren.GetShiftKey(): 4342 i -= 1 4343 else: 4344 i += 1 4345 if i >= len(arrnames): 4346 i = 0 4347 if i < 0: 4348 i = len(arrnames) - 1 4349 except (ValueError, AttributeError): 4350 i = 0 4351 4352 ob.cmap(ob._cmap_name, arrnames[i], on="cells") 4353 if ob.scalarbar: 4354 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4355 self.remove(ob.scalarbar) 4356 title = ob.scalarbar.GetTitle() 4357 ob.scalarbar = None 4358 ob.add_scalarbar(title=arrnames[i]) 4359 self.add(ob.scalarbar) 4360 elif isinstance(ob.scalarbar, vedo.Assembly): 4361 self.remove(ob.scalarbar) 4362 ob.scalarbar = None 4363 ob.add_scalarbar3d(title=arrnames[i]) 4364 self.add(ob.scalarbar) 4365 else: 4366 vedo.printc( 4367 f"Name:'{ob.name}'," if ob.name else "", 4368 f"active celldata array: '{arrnames[i]}'", 4369 c="g", bold=False, 4370 ) 4371 4372 elif key == "7": 4373 bgc = np.array(renderer.GetBackground()).sum() / 3 4374 if bgc <= 0: 4375 bgc = 0.223 4376 elif 0 < bgc < 1: 4377 bgc = 1 4378 else: 4379 bgc = 0 4380 renderer.SetBackground(bgc, bgc, bgc) 4381 4382 elif key == "8": 4383 bg2cols = [ 4384 "lightyellow", 4385 "darkseagreen", 4386 "palegreen", 4387 "steelblue", 4388 "lightblue", 4389 "cadetblue", 4390 "lavender", 4391 "white", 4392 "blackboard", 4393 "black", 4394 ] 4395 bg2name = vedo.get_color_name(renderer.GetBackground2()) 4396 if bg2name in bg2cols: 4397 idx = bg2cols.index(bg2name) 4398 else: 4399 idx = 4 4400 if idx is not None: 4401 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 4402 if not bg2name_next: 4403 renderer.GradientBackgroundOff() 4404 else: 4405 renderer.GradientBackgroundOn() 4406 renderer.SetBackground2(vedo.get_color(bg2name_next)) 4407 4408 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 4409 i = self.renderers.index(renderer) 4410 try: 4411 self.axes_instances[i].EnabledOff() 4412 self.axes_instances[i].SetInteractor(None) 4413 except AttributeError: 4414 # print("Cannot remove widget", [self.axes_instances[i]]) 4415 try: 4416 self.remove(self.axes_instances[i]) 4417 except: 4418 print("Cannot remove axes", [self.axes_instances[i]]) 4419 return 4420 self.axes_instances[i] = None 4421 4422 if not self.axes: 4423 self.axes = 0 4424 if isinstance(self.axes, dict): 4425 self.axes = 1 4426 4427 if key in ["minus", "KP_Subtract"]: 4428 if not self.camera.GetParallelProjection() and self.axes == 0: 4429 self.axes -= 1 # jump ruler doesnt make sense in perspective mode 4430 bns = self.renderer.ComputeVisiblePropBounds() 4431 addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None, bounds=bns) 4432 else: 4433 if not self.camera.GetParallelProjection() and self.axes == 12: 4434 self.axes += 1 # jump ruler doesnt make sense in perspective mode 4435 bns = self.renderer.ComputeVisiblePropBounds() 4436 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns) 4437 self.render() 4438 4439 elif "KP_" in key or key in [ 4440 "Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior" 4441 ]: 4442 asso = { # change axes style 4443 "KP_Insert": 0, "KP_0": 0, "Insert": 0, 4444 "KP_End": 1, "KP_1": 1, "End": 1, 4445 "KP_Down": 2, "KP_2": 2, "Down": 2, 4446 "KP_Next": 3, "KP_3": 3, "Next": 3, 4447 "KP_Left": 4, "KP_4": 4, "Left": 4, 4448 "KP_Begin": 5, "KP_5": 5, "Begin": 5, 4449 "KP_Right": 6, "KP_6": 6, "Right": 6, 4450 "KP_Home": 7, "KP_7": 7, "Home": 7, 4451 "KP_Up": 8, "KP_8": 8, "Up": 8, 4452 "Prior": 9, # on windows OS 4453 } 4454 clickedr = self.renderers.index(renderer) 4455 if key in asso: 4456 if self.axes_instances[clickedr]: 4457 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 4458 self.axes_instances[clickedr].EnabledOff() 4459 else: 4460 try: 4461 renderer.RemoveActor(self.axes_instances[clickedr]) 4462 except: 4463 pass 4464 self.axes_instances[clickedr] = None 4465 bounds = renderer.ComputeVisiblePropBounds() 4466 addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds) 4467 self.interactor.Render() 4468 4469 if key == "O": 4470 renderer.RemoveLight(self._extralight) 4471 self._extralight = None 4472 4473 elif key == "o": 4474 vbb, sizes, _, _ = addons.compute_visible_bounds() 4475 cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2) 4476 if not self._extralight: 4477 vup = renderer.GetActiveCamera().GetViewUp() 4478 pos = cm + utils.vector(vup) * utils.mag(sizes) 4479 self._extralight = addons.Light(pos, focal_point=cm, intensity=0.4) 4480 renderer.AddLight(self._extralight) 4481 vedo.printc("Press 'o' again to rotate light source, or 'O' to remove it.", c='y') 4482 else: 4483 cpos = utils.vector(self._extralight.GetPosition()) 4484 x, y, z = self._extralight.GetPosition() - cm 4485 r, th, ph = transformations.cart2spher(x, y, z) 4486 th += 0.2 4487 if th > np.pi: 4488 th = np.random.random() * np.pi / 2 4489 ph += 0.3 4490 cpos = transformations.spher2cart(r, th, ph).T + cm 4491 self._extralight.SetPosition(cpos) 4492 4493 elif key == "l": 4494 if self.clicked_object in self.get_meshes(): 4495 objs = [self.clicked_object] 4496 else: 4497 objs = self.get_meshes() 4498 for ia in objs: 4499 try: 4500 ev = ia.properties.GetEdgeVisibility() 4501 ia.properties.SetEdgeVisibility(not ev) 4502 ia.properties.SetRepresentationToSurface() 4503 ia.properties.SetLineWidth(0.1) 4504 except AttributeError: 4505 pass 4506 4507 elif key == "k": # lightings 4508 if self.clicked_object in self.get_meshes(): 4509 objs = [self.clicked_object] 4510 else: 4511 objs = self.get_meshes() 4512 shds = ("default", "metallic", "plastic", "shiny", "glossy", "off") 4513 for ia in objs: 4514 try: 4515 lnr = (ia._ligthingnr + 1) % 6 4516 ia.lighting(shds[lnr]) 4517 ia._ligthingnr = lnr 4518 except AttributeError: 4519 pass 4520 4521 elif key == "K": # shading 4522 if self.clicked_object in self.get_meshes(): 4523 objs = [self.clicked_object] 4524 else: 4525 objs = self.get_meshes() 4526 for ia in objs: 4527 if isinstance(ia, vedo.Mesh): 4528 ia.compute_normals(cells=False) 4529 intrp = ia.properties.GetInterpolation() 4530 if intrp > 0: 4531 ia.properties.SetInterpolation(0) # flat 4532 else: 4533 ia.properties.SetInterpolation(2) # phong 4534 4535 elif key == "n": # show normals to an actor 4536 self.remove("added_auto_normals") 4537 if self.clicked_object in self.get_meshes(): 4538 if self.clicked_actor.GetPickable(): 4539 norml = vedo.shapes.NormalLines(self.clicked_object) 4540 norml.name = "added_auto_normals" 4541 self.add(norml) 4542 4543 elif key == "x": 4544 if self.justremoved is None: 4545 if self.clicked_object in self.get_meshes() or isinstance( 4546 self.clicked_object, vtki.vtkAssembly 4547 ): 4548 self.justremoved = self.clicked_actor 4549 self.renderer.RemoveActor(self.clicked_actor) 4550 else: 4551 self.renderer.AddActor(self.justremoved) 4552 self.justremoved = None 4553 4554 elif key == "X": 4555 if self.clicked_object: 4556 if not self.cutter_widget: 4557 self.cutter_widget = addons.BoxCutter(self.clicked_object) 4558 self.add(self.cutter_widget) 4559 vedo.printc("Press i to toggle the cutter on/off", c='g', dim=1) 4560 vedo.printc(" u to flip selection", c='g', dim=1) 4561 vedo.printc(" r to reset cutting planes", c='g', dim=1) 4562 vedo.printc(" Shift+X to close the cutter box widget", c='g', dim=1) 4563 vedo.printc(" Ctrl+S to save the cut section to file.", c='g', dim=1) 4564 else: 4565 self.remove(self.cutter_widget) 4566 self.cutter_widget = None 4567 vedo.printc("Click object and press X to open the cutter box widget.", c='g') 4568 4569 elif key == "E": 4570 vedo.printc(r":camera: Exporting 3D window to file scene.npz", c="b", end="") 4571 vedo.file_io.export_window("scene.npz") 4572 vedo.printc(", try:\n> vedo scene.npz # (this is experimental!)", c="b") 4573 return 4574 4575 elif key == "F": 4576 vedo.file_io.export_window("scene.x3d") 4577 vedo.printc(r":camera: Exporting 3D window to file", c="b", end="") 4578 vedo.file_io.export_window("scene.npz") 4579 vedo.printc(". Try:\n> firefox scene.html", c="b") 4580 4581 # elif key == "G": # not working with last version of k3d 4582 # vedo.file_io.export_window("scene.html") 4583 # vedo.printc(r":camera: Exporting K3D window to file", c="b", end="") 4584 # vedo.file_io.export_window("scene.html") 4585 # vedo.printc(". Try:\n> firefox scene.html", c="b") 4586 4587 elif key == "i": # print info 4588 if self.clicked_object: 4589 print(self.clicked_object) 4590 else: 4591 print(self) 4592 4593 elif key == "I": # print color under the mouse 4594 x, y = iren.GetEventPosition() 4595 self.color_picker([x, y], verbose=True) 4596 4597 elif key == "Y": 4598 if self.clicked_object and self.clicked_object.pipeline: 4599 self.clicked_object.pipeline.show() 4600 4601 if iren: 4602 iren.Render()
376class Plotter: 377 """Main class to manage objects.""" 378 379 def __init__( 380 self, 381 shape=(1, 1), 382 N=None, 383 pos=(0, 0), 384 size="auto", 385 screensize="auto", 386 title="vedo", 387 bg="white", 388 bg2=None, 389 axes=None, 390 sharecam=True, 391 resetcam=True, 392 interactive=None, 393 offscreen=False, 394 qt_widget=None, 395 wx_widget=None, 396 ): 397 """ 398 Arguments: 399 shape : (str, list) 400 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 401 N : (int) 402 number of desired renderers arranged in a grid automatically. 403 pos : (list) 404 (x,y) position in pixels of top-left corner of the rendering window on the screen 405 size : (str, list) 406 size of the rendering window. If 'auto', guess it based on screensize. 407 screensize : (list) 408 physical size of the monitor screen in pixels 409 bg : (color, str) 410 background color or specify jpg image file name with path 411 bg2 : (color) 412 background color of a gradient towards the top 413 title : (str) 414 window title 415 416 axes : (int) 417 418 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 419 Check out `vedo.addons.Axes()` for the available options. 420 421 - 0, no axes 422 - 1, draw three gray grid walls 423 - 2, show cartesian axes from (0,0,0) 424 - 3, show positive range of cartesian axes from (0,0,0) 425 - 4, show a triad at bottom left 426 - 5, show a cube at bottom left 427 - 6, mark the corners of the bounding box 428 - 7, draw a 3D ruler at each side of the cartesian axes 429 - 8, show the VTK CubeAxesActor object 430 - 9, show the bounding box outLine 431 - 10, show three circles representing the maximum bounding box 432 - 11, show a large grid on the x-y plane (use with zoom=8) 433 - 12, show polar axes 434 - 13, draw a simple ruler at the bottom of the window 435 - 14: draw a camera orientation widget 436 437 sharecam : (bool) 438 if False each renderer will have an independent camera 439 interactive : (bool) 440 if True will stop after show() to allow interaction with the 3d scene 441 offscreen : (bool) 442 if True will not show the rendering window 443 qt_widget : (QVTKRenderWindowInteractor) 444 render in a Qt-Widget using an QVTKRenderWindowInteractor. 445 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 446 """ 447 vedo.plotter_instance = self 448 449 if interactive is None: 450 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 451 self._interactive = interactive 452 # print("interactive", interactive, N, shape) 453 454 self.objects = [] # list of objects to be shown 455 self.clicked_object = None # holds the object that has been clicked 456 self.clicked_actor = None # holds the actor that has been clicked 457 458 self.shape = shape # nr. of subwindows in grid 459 self.axes = axes # show axes type nr. 460 self.title = title # window title 461 self.size = size # window size 462 self.backgrcol = bg # used also by backend notebooks 463 464 self.offscreen= offscreen 465 self.resetcam = resetcam 466 self.sharecam = sharecam # share the same camera if multiple renderers 467 self.pos = pos # used by vedo.file_io 468 469 self.picker = None # hold the vtkPicker object 470 self.picked2d = None # 2d coords of a clicked point on the rendering window 471 self.picked3d = None # 3d coords of a clicked point on an actor 472 473 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 474 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 475 self.interactor = None 476 self.window = None 477 self.renderer = None 478 self.renderers = [] # list of renderers 479 480 # mostly internal stuff: 481 self.hover_legends = [] 482 self.justremoved = None 483 self.axes_instances = [] 484 self.clock = 0 485 self.sliders = [] 486 self.buttons = [] 487 self.widgets = [] 488 self.cutter_widget = None 489 self.hint_widget = None 490 self.background_renderer = None 491 self.last_event = None 492 self.skybox = None 493 self._icol = 0 494 self._clockt0 = time.time() 495 self._extralight = None 496 self._cocoa_initialized = False 497 self._cocoa_process_events = True # make one call in show() 498 self._must_close_now = False 499 500 ##################################################################### 501 if vedo.settings.default_backend == "2d": 502 self.offscreen = True 503 if self.size == "auto": 504 self.size = (800, 600) 505 506 elif vedo.settings.default_backend == "k3d": 507 if self.size == "auto": 508 self.size = (1000, 1000) 509 #################################### 510 return ############################ 511 #################################### 512 513 ############################################################# 514 if screensize == "auto": 515 screensize = (2160, 1440) # TODO: get actual screen size 516 517 # build the rendering window: 518 self.window = vtki.vtkRenderWindow() 519 520 self.window.GlobalWarningDisplayOff() 521 522 if self.title == "vedo": # check if dev version 523 if "dev" in vedo.__version__: 524 self.title = f"vedo ({vedo.__version__})" 525 self.window.SetWindowName(self.title) 526 527 # more vedo.settings 528 if vedo.settings.use_depth_peeling: 529 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 530 self.window.SetMultiSamples(vedo.settings.multi_samples) 531 532 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 533 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 534 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 535 536 537 ############################################################# 538 if N: # N = number of renderers. Find out the best 539 540 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 541 vedo.logger.warning("having set N, shape is ignored.") 542 543 x, y = screensize 544 nx = int(np.sqrt(int(N * y / x) + 1)) 545 ny = int(np.sqrt(int(N * x / y) + 1)) 546 lm = [ 547 (nx, ny), 548 (nx, ny + 1), 549 (nx - 1, ny), 550 (nx + 1, ny), 551 (nx, ny - 1), 552 (nx - 1, ny + 1), 553 (nx + 1, ny - 1), 554 (nx + 1, ny + 1), 555 (nx - 1, ny - 1), 556 ] 557 ind, minl = 0, 1000 558 for i, m in enumerate(lm): 559 l = m[0] * m[1] 560 if N <= l < minl: 561 ind = i 562 minl = l 563 shape = lm[ind] 564 565 ################################################## 566 if isinstance(shape, str): 567 568 if "|" in shape: 569 if self.size == "auto": 570 self.size = (800, 1200) 571 n = int(shape.split("|")[0]) 572 m = int(shape.split("|")[1]) 573 rangen = reversed(range(n)) 574 rangem = reversed(range(m)) 575 else: 576 if self.size == "auto": 577 self.size = (1200, 800) 578 m = int(shape.split("/")[0]) 579 n = int(shape.split("/")[1]) 580 rangen = range(n) 581 rangem = range(m) 582 583 if n >= m: 584 xsplit = m / (n + m) 585 else: 586 xsplit = 1 - n / (n + m) 587 if vedo.settings.window_splitting_position: 588 xsplit = vedo.settings.window_splitting_position 589 590 for i in rangen: 591 arenderer = vtki.vtkRenderer() 592 if "|" in shape: 593 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 594 else: 595 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 596 self.renderers.append(arenderer) 597 598 for i in rangem: 599 arenderer = vtki.vtkRenderer() 600 601 if "|" in shape: 602 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 603 else: 604 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 605 self.renderers.append(arenderer) 606 607 for r in self.renderers: 608 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 609 610 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 611 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 612 if vedo.settings.use_depth_peeling: 613 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 614 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 615 r.SetUseFXAA(vedo.settings.use_fxaa) 616 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 617 618 r.SetBackground(vedo.get_color(self.backgrcol)) 619 620 self.axes_instances.append(None) 621 622 self.shape = (n + m,) 623 624 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 625 # passing a sequence of dicts for renderers specifications 626 627 if self.size == "auto": 628 self.size = (1000, 800) 629 630 for rd in shape: 631 x0, y0 = rd["bottomleft"] 632 x1, y1 = rd["topright"] 633 bg_ = rd.pop("bg", "white") 634 bg2_ = rd.pop("bg2", None) 635 636 arenderer = vtki.vtkRenderer() 637 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 638 639 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 640 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 641 if vedo.settings.use_depth_peeling: 642 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 643 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 644 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 645 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 646 647 arenderer.SetViewport(x0, y0, x1, y1) 648 arenderer.SetBackground(vedo.get_color(bg_)) 649 if bg2_: 650 arenderer.GradientBackgroundOn() 651 arenderer.SetBackground2(vedo.get_color(bg2_)) 652 653 self.renderers.append(arenderer) 654 self.axes_instances.append(None) 655 656 self.shape = (len(shape),) 657 658 else: 659 660 if isinstance(self.size, str) and self.size == "auto": 661 # figure out a reasonable window size 662 f = 1.5 663 x, y = screensize 664 xs = y / f * shape[1] # because y<x 665 ys = y / f * shape[0] 666 if xs > x / f: # shrink 667 xs = x / f 668 ys = xs / shape[1] * shape[0] 669 if ys > y / f: 670 ys = y / f 671 xs = ys / shape[0] * shape[1] 672 self.size = (int(xs), int(ys)) 673 if shape == (1, 1): 674 self.size = (int(y / f), int(y / f)) # because y<x 675 else: 676 self.size = (self.size[0], self.size[1]) 677 678 try: 679 image_actor = None 680 bgname = str(self.backgrcol).lower() 681 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 682 self.window.SetNumberOfLayers(2) 683 self.background_renderer = vtki.vtkRenderer() 684 self.background_renderer.SetLayer(0) 685 self.background_renderer.InteractiveOff() 686 self.background_renderer.SetBackground(vedo.get_color(bg2)) 687 image_actor = vedo.Image(self.backgrcol).actor 688 self.window.AddRenderer(self.background_renderer) 689 self.background_renderer.AddActor(image_actor) 690 except AttributeError: 691 pass 692 693 for i in reversed(range(shape[0])): 694 for j in range(shape[1]): 695 arenderer = vtki.vtkRenderer() 696 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 697 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 698 699 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 700 if vedo.settings.use_depth_peeling: 701 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 702 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 703 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 704 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 705 706 if image_actor: 707 arenderer.SetLayer(1) 708 709 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 710 if bg2: 711 arenderer.GradientBackgroundOn() 712 arenderer.SetBackground2(vedo.get_color(bg2)) 713 714 x0 = i / shape[0] 715 y0 = j / shape[1] 716 x1 = (i + 1) / shape[0] 717 y1 = (j + 1) / shape[1] 718 arenderer.SetViewport(y0, x0, y1, x1) 719 self.renderers.append(arenderer) 720 self.axes_instances.append(None) 721 self.shape = shape 722 723 if self.renderers: 724 self.renderer = self.renderers[0] 725 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 726 727 ######################################################### 728 if self.qt_widget or self.wx_widget: 729 if self.qt_widget: 730 self.window = self.qt_widget.GetRenderWindow() # overwrite 731 else: 732 self.window = self.wx_widget.GetRenderWindow() 733 self.interactor = self.window.GetInteractor() 734 735 ######################################################### 736 for r in self.renderers: 737 self.window.AddRenderer(r) 738 # set the background gradient if any 739 if vedo.settings.background_gradient_orientation > 0: 740 try: 741 modes = [ 742 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 743 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 744 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 745 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 746 ] 747 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 748 r.GradientBackgroundOn() 749 except AttributeError: 750 pass 751 752 # Backend #################################################### 753 if vedo.settings.default_backend in ["panel", "trame", "k3d"]: 754 return ################ 755 ######################## 756 757 ######################################################### 758 if self.qt_widget or self.wx_widget: 759 self.interactor.SetRenderWindow(self.window) 760 if vedo.settings.enable_default_keyboard_callbacks: 761 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 762 if vedo.settings.enable_default_mouse_callbacks: 763 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 764 return ################ 765 ######################## 766 767 if self.size[0] == "f": # full screen 768 self.size = "fullscreen" 769 self.window.SetFullScreen(True) 770 self.window.BordersOn() 771 else: 772 self.window.SetSize(int(self.size[0]), int(self.size[1])) 773 774 if self.offscreen: 775 if self.axes in (4, 5, 8, 12, 14): 776 self.axes = 0 # does not work with those 777 self.window.SetOffScreenRendering(True) 778 self.interactor = None 779 self._interactive = False 780 return ################ 781 ######################## 782 783 self.window.SetPosition(pos) 784 785 ######################################################### 786 self.interactor = vtki.vtkRenderWindowInteractor() 787 788 self.interactor.SetRenderWindow(self.window) 789 vsty = vtki.new("InteractorStyleTrackballCamera") 790 self.interactor.SetInteractorStyle(vsty) 791 self.interactor.RemoveObservers("CharEvent") 792 793 if vedo.settings.enable_default_keyboard_callbacks: 794 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 795 if vedo.settings.enable_default_mouse_callbacks: 796 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 797 798 ##################################################################### ..init ends here. 799 800 801 def __str__(self): 802 """Return Plotter info.""" 803 axtype = { 804 0: "(no axes)", 805 1: "(default customizable grid walls)", 806 2: "(cartesian axes from origin", 807 3: "(positive range of cartesian axes from origin", 808 4: "(axes triad at bottom left)", 809 5: "(oriented cube at bottom left)", 810 6: "(mark the corners of the bounding box)", 811 7: "(3D ruler at each side of the cartesian axes)", 812 8: "(the vtkCubeAxesActor object)", 813 9: "(the bounding box outline)", 814 10: "(circles of maximum bounding box range)", 815 11: "(show a large grid on the x-y plane)", 816 12: "(show polar axes)", 817 13: "(simple ruler at the bottom of the window)", 818 14: "(the vtkCameraOrientationWidget object)", 819 } 820 821 module = self.__class__.__module__ 822 name = self.__class__.__name__ 823 out = vedo.printc( 824 f"{module}.{name} at ({hex(id(self))})".ljust(75), 825 bold=True, invert=True, return_string=True, 826 ) 827 out += "\x1b[0m" 828 if self.interactor: 829 out += "window title".ljust(14) + ": " + self.title + "\n" 830 out += "window size".ljust(14) + f": {self.window.GetSize()}" 831 out += f", full_screen={self.window.GetScreenSize()}\n" 832 out += "activ renderer".ljust(14) + ": nr." + str(self.renderers.index(self.renderer)) 833 out += f" (out of {len(self.renderers)} renderers)\n" 834 835 bns, totpt = [], 0 836 for a in self.objects: 837 try: 838 b = a.bounds() 839 bns.append(b) 840 except AttributeError: 841 pass 842 try: 843 totpt += a.npoints 844 except AttributeError: 845 pass 846 out += "n. of objects".ljust(14) + f": {len(self.objects)}" 847 out += f" ({totpt} vertices)\n" if totpt else "\n" 848 849 if len(bns) > 0: 850 min_bns = np.min(bns, axis=0) 851 max_bns = np.max(bns, axis=0) 852 bx1, bx2 = utils.precision(min_bns[0], 3), utils.precision(max_bns[1], 3) 853 by1, by2 = utils.precision(min_bns[2], 3), utils.precision(max_bns[3], 3) 854 bz1, bz2 = utils.precision(min_bns[4], 3), utils.precision(max_bns[5], 3) 855 out += "bounds".ljust(14) + ":" 856 out += " x=(" + bx1 + ", " + bx2 + ")," 857 out += " y=(" + by1 + ", " + by2 + ")," 858 out += " z=(" + bz1 + ", " + bz2 + ")\n" 859 860 if utils.is_integer(self.axes): 861 out += "axes style".ljust(14) + f": {self.axes} {axtype[self.axes]}\n" 862 elif isinstance(self.axes, dict): 863 out += "axes style".ljust(14) + f": 1 {axtype[1]}\n" 864 else: 865 out += "axes style".ljust(14) + f": {[self.axes]}\n" 866 return out.rstrip() + "\x1b[0m" 867 868 def print(self): 869 """Print information about the current instance.""" 870 print(self.__str__()) 871 return self 872 873 def __iadd__(self, objects): 874 self.add(objects) 875 return self 876 877 def __isub__(self, objects): 878 self.remove(objects) 879 return self 880 881 def __enter__(self): 882 # context manager like in "with Plotter() as plt:" 883 return self 884 885 def __exit__(self, *args, **kwargs): 886 # context manager like in "with Plotter() as plt:" 887 self.close() 888 889 def initialize_interactor(self) -> Self: 890 """Initialize the interactor if not already initialized.""" 891 if self.offscreen: 892 return self 893 if self.interactor: 894 if not self.interactor.GetInitialized(): 895 self.interactor.Initialize() 896 self.interactor.RemoveObservers("CharEvent") 897 return self 898 899 def process_events(self) -> Self: 900 """Process all pending events.""" 901 self.initialize_interactor() 902 if self.interactor: 903 try: 904 self.interactor.ProcessEvents() 905 except AttributeError: 906 pass 907 return self 908 909 def at(self, nren: int, yren=None) -> Self: 910 """ 911 Select the current renderer number as an int. 912 Can also use the `[nx, ny]` format. 913 """ 914 if utils.is_sequence(nren): 915 if len(nren) == 2: 916 nren, yren = nren 917 else: 918 vedo.logger.error("at() argument must be a single number or a list of two numbers") 919 raise RuntimeError 920 921 if yren is not None: 922 a, b = self.shape 923 x, y = nren, yren 924 nren = x * b + y 925 # print("at (", x, y, ") -> ren", nren) 926 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 927 vedo.logger.error(f"at({nren, yren}) is malformed!") 928 raise RuntimeError 929 930 self.renderer = self.renderers[nren] 931 return self 932 933 def add(self, *objs, at=None) -> Self: 934 """ 935 Append the input objects to the internal list of objects to be shown. 936 937 Arguments: 938 at : (int) 939 add the object at the specified renderer 940 """ 941 if at is not None: 942 ren = self.renderers[at] 943 else: 944 ren = self.renderer 945 946 objs = utils.flatten(objs) 947 for ob in objs: 948 if ob and ob not in self.objects: 949 self.objects.append(ob) 950 951 acts = self._scan_input_return_acts(objs) 952 953 for a in acts: 954 955 if ren: 956 if isinstance(a, vedo.addons.BaseCutter): 957 a.add_to(self) # from cutters 958 continue 959 960 if isinstance(a, vtki.vtkLight): 961 ren.AddLight(a) 962 continue 963 964 try: 965 ren.AddActor(a) 966 except TypeError: 967 ren.AddActor(a.actor) 968 969 try: 970 ir = self.renderers.index(ren) 971 a.rendered_at.add(ir) # might not have rendered_at 972 except (AttributeError, ValueError): 973 pass 974 975 if isinstance(a, vtki.vtkFollower): 976 a.SetCamera(self.camera) 977 elif isinstance(a, vedo.visual.LightKit): 978 a.lightkit.AddLightsToRenderer(ren) 979 980 return self 981 982 def remove(self, *objs, at=None) -> Self: 983 """ 984 Remove input object to the internal list of objects to be shown. 985 986 Objects to be removed can be referenced by their assigned name, 987 988 Arguments: 989 at : (int) 990 remove the object at the specified renderer 991 """ 992 # TODO and you can also use wildcards like `*` and `?`. 993 if at is not None: 994 ren = self.renderers[at] 995 else: 996 ren = self.renderer 997 998 objs = [ob for ob in utils.flatten(objs) if ob] 999 1000 has_str = False 1001 for ob in objs: 1002 if isinstance(ob, str): 1003 has_str = True 1004 break 1005 1006 has_actor = False 1007 for ob in objs: 1008 if hasattr(ob, "actor") and ob.actor: 1009 has_actor = True 1010 break 1011 1012 if has_str or has_actor: 1013 # need to get the actors to search for 1014 for a in self.get_actors(include_non_pickables=True): 1015 # print("PARSING", [a]) 1016 try: 1017 if (a.name and a.name in objs) or a in objs: 1018 objs.append(a) 1019 # if a.name: 1020 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1021 # if any(bools) or a in objs: 1022 # objs.append(a) 1023 # print('a.name',a.name, objs,any(bools)) 1024 except AttributeError: # no .name 1025 # passing the actor so get back the object with .retrieve_object() 1026 try: 1027 vobj = a.retrieve_object() 1028 if (vobj.name and vobj.name in objs) or vobj in objs: 1029 # print('vobj.name', vobj.name) 1030 objs.append(vobj) 1031 except AttributeError: 1032 pass 1033 1034 if ren is None: 1035 return self 1036 ir = self.renderers.index(ren) 1037 1038 ids = [] 1039 for ob in set(objs): 1040 1041 # will remove it from internal list if possible 1042 try: 1043 idx = self.objects.index(ob) 1044 ids.append(idx) 1045 except ValueError: 1046 pass 1047 1048 if ren: ### remove it from the renderer 1049 1050 if isinstance(ob, vedo.addons.BaseCutter): 1051 ob.remove_from(self) # from cutters 1052 continue 1053 1054 try: 1055 ren.RemoveActor(ob) 1056 except TypeError: 1057 try: 1058 ren.RemoveActor(ob.actor) 1059 except AttributeError: 1060 pass 1061 1062 if hasattr(ob, "rendered_at"): 1063 ob.rendered_at.discard(ir) 1064 1065 if hasattr(ob, "scalarbar") and ob.scalarbar: 1066 ren.RemoveActor(ob.scalarbar) 1067 if hasattr(ob, "_caption") and ob._caption: 1068 ren.RemoveActor(ob._caption) 1069 if hasattr(ob, "shadows") and ob.shadows: 1070 for sha in ob.shadows: 1071 ren.RemoveActor(sha.actor) 1072 if hasattr(ob, "trail") and ob.trail: 1073 ren.RemoveActor(ob.trail.actor) 1074 ob.trail_points = [] 1075 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1076 for sha in ob.trail.shadows: 1077 ren.RemoveActor(sha.actor) 1078 1079 elif isinstance(ob, vedo.visual.LightKit): 1080 ob.lightkit.RemoveLightsFromRenderer(ren) 1081 1082 # for i in ids: # WRONG way of doing it! 1083 # del self.objects[i] 1084 # instead we do: 1085 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1086 return self 1087 1088 @property 1089 def actors(self): 1090 """Return the list of actors.""" 1091 return [ob.actor for ob in self.objects if hasattr(ob, "actor")] 1092 1093 def remove_lights(self) -> Self: 1094 """Remove all the present lights in the current renderer.""" 1095 if self.renderer: 1096 self.renderer.RemoveAllLights() 1097 return self 1098 1099 def pop(self, at=None) -> Self: 1100 """ 1101 Remove the last added object from the rendering window. 1102 This method is typically used in loops or callback functions. 1103 """ 1104 if at is not None and not isinstance(at, int): 1105 # wrong usage pitfall 1106 vedo.logger.error("argument of pop() must be an integer") 1107 raise RuntimeError() 1108 1109 if self.objects: 1110 self.remove(self.objects[-1], at) 1111 return self 1112 1113 def render(self, resetcam=False) -> Self: 1114 """Render the scene. This method is typically used in loops or callback functions.""" 1115 1116 if vedo.settings.dry_run_mode >= 2: 1117 return self 1118 1119 if not self.window: 1120 return self 1121 1122 self.initialize_interactor() 1123 1124 if resetcam: 1125 self.renderer.ResetCamera() 1126 1127 self.window.Render() 1128 1129 if self._cocoa_process_events and self.interactor and self.interactor.GetInitialized(): 1130 if "Darwin" in vedo.sys_platform and not self.offscreen: 1131 self.interactor.ProcessEvents() 1132 self._cocoa_process_events = False 1133 return self 1134 1135 def interactive(self) -> Self: 1136 """ 1137 Start window interaction. 1138 Analogous to `show(..., interactive=True)`. 1139 """ 1140 if vedo.settings.dry_run_mode >= 1: 1141 return self 1142 self.initialize_interactor() 1143 if self.interactor: 1144 # print("self.interactor.Start()") 1145 self.interactor.Start() 1146 # print("self.interactor.Start() done") 1147 if self._must_close_now: 1148 # print("self.interactor.TerminateApp()") 1149 if self.interactor: 1150 self.interactor.GetRenderWindow().Finalize() 1151 self.interactor.TerminateApp() 1152 self.interactor = None 1153 self.window = None 1154 self.renderer = None 1155 self.renderers = [] 1156 self.camera = None 1157 return self 1158 1159 def use_depth_peeling(self, at=None, value=True) -> Self: 1160 """ 1161 Specify whether use depth peeling algorithm at this specific renderer 1162 Call this method before the first rendering. 1163 """ 1164 if at is None: 1165 ren = self.renderer 1166 else: 1167 ren = self.renderers[at] 1168 ren.SetUseDepthPeeling(value) 1169 return self 1170 1171 def background(self, c1=None, c2=None, at=None, mode=0) -> Union[Self, "np.ndarray"]: 1172 """Set the color of the background for the current renderer. 1173 A different renderer index can be specified by keyword `at`. 1174 1175 Arguments: 1176 c1 : (list) 1177 background main color. 1178 c2 : (list) 1179 background color for the upper part of the window. 1180 at : (int) 1181 renderer index. 1182 mode : (int) 1183 background mode (needs vtk version >= 9.3) 1184 0 = vertical, 1185 1 = horizontal, 1186 2 = radial farthest side, 1187 3 = radia farthest corner. 1188 """ 1189 if not self.renderers: 1190 return self 1191 if at is None: 1192 r = self.renderer 1193 else: 1194 r = self.renderers[at] 1195 1196 if c1 is None and c2 is None: 1197 return np.array(r.GetBackground()) 1198 1199 if r: 1200 if c1 is not None: 1201 r.SetBackground(vedo.get_color(c1)) 1202 if c2 is not None: 1203 r.GradientBackgroundOn() 1204 r.SetBackground2(vedo.get_color(c2)) 1205 if mode: 1206 try: # only works with vtk>=9.3 1207 modes = [ 1208 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1209 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1210 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1211 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1212 ] 1213 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 1214 except AttributeError: 1215 pass 1216 1217 else: 1218 r.GradientBackgroundOff() 1219 return self 1220 1221 ################################################################## 1222 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1223 """ 1224 Return a list of Meshes from the specified renderer. 1225 1226 Arguments: 1227 at : (int) 1228 specify which renderer to look at. 1229 include_non_pickables : (bool) 1230 include non-pickable objects 1231 unpack_assemblies : (bool) 1232 unpack assemblies into their components 1233 """ 1234 if at is None: 1235 renderer = self.renderer 1236 at = self.renderers.index(renderer) 1237 elif isinstance(at, int): 1238 renderer = self.renderers[at] 1239 1240 has_global_axes = False 1241 if isinstance(self.axes_instances[at], vedo.Assembly): 1242 has_global_axes = True 1243 1244 if unpack_assemblies: 1245 acs = renderer.GetActors() 1246 else: 1247 acs = renderer.GetViewProps() 1248 1249 objs = [] 1250 acs.InitTraversal() 1251 for _ in range(acs.GetNumberOfItems()): 1252 1253 if unpack_assemblies: 1254 a = acs.GetNextItem() 1255 else: 1256 a = acs.GetNextProp() 1257 1258 if isinstance(a, vtki.vtkVolume): 1259 continue 1260 1261 if include_non_pickables or a.GetPickable(): 1262 if a == self.axes_instances[at]: 1263 continue 1264 if has_global_axes and a in self.axes_instances[at].actors: 1265 continue 1266 try: 1267 objs.append(a.retrieve_object()) 1268 except AttributeError: 1269 pass 1270 return objs 1271 1272 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1273 """ 1274 Return a list of Volumes from the specified renderer. 1275 1276 Arguments: 1277 at : (int) 1278 specify which renderer to look at 1279 include_non_pickables : (bool) 1280 include non-pickable objects 1281 """ 1282 if at is None: 1283 renderer = self.renderer 1284 at = self.renderers.index(renderer) 1285 elif isinstance(at, int): 1286 renderer = 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 if at is None: 1311 renderer = self.renderer 1312 if renderer is None: 1313 return [] 1314 at = self.renderers.index(renderer) 1315 elif isinstance(at, int): 1316 renderer = self.renderers[at] 1317 1318 acts = [] 1319 acs = renderer.GetViewProps() 1320 acs.InitTraversal() 1321 for _ in range(acs.GetNumberOfItems()): 1322 a = acs.GetNextProp() 1323 if include_non_pickables or a.GetPickable(): 1324 acts.append(a) 1325 return acts 1326 1327 def check_actors_trasform(self, at=None) -> Self: 1328 """ 1329 Reset the transformation matrix of all actors at specified renderer. 1330 This is only useful when actors have been moved/rotated/scaled manually 1331 in an already rendered scene using interactors like 1332 'TrackballActor' or 'JoystickActor'. 1333 """ 1334 # see issue https://github.com/marcomusy/vedo/issues/1046 1335 for a in self.get_actors(at=at, include_non_pickables=True): 1336 try: 1337 M = a.GetMatrix() 1338 except AttributeError: 1339 continue 1340 if M and not M.IsIdentity(): 1341 try: 1342 a.retrieve_object().apply_transform_from_actor() 1343 # vedo.logger.info( 1344 # f"object '{a.retrieve_object().name}' " 1345 # "was manually moved. Updated to its current position." 1346 # ) 1347 except AttributeError: 1348 pass 1349 return self 1350 1351 def reset_camera(self, tight=None) -> Self: 1352 """ 1353 Reset the camera position and zooming. 1354 If tight (float) is specified the zooming reserves a padding space 1355 in the xy-plane expressed in percent of the average size. 1356 """ 1357 if tight is None: 1358 self.renderer.ResetCamera() 1359 else: 1360 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1361 cam = self.camera 1362 1363 self.renderer.ComputeAspect() 1364 aspect = self.renderer.GetAspect() 1365 angle = np.pi * cam.GetViewAngle() / 180.0 1366 dx = x1 - x0 1367 dy = y1 - y0 1368 dist = max(dx / aspect[0], dy) / np.tan(angle / 2) / 2 1369 1370 cam.SetViewUp(0, 1, 0) 1371 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1372 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1373 if cam.GetParallelProjection(): 1374 ps = max(dx / aspect[0], dy) / 2 1375 cam.SetParallelScale(ps * (1 + tight)) 1376 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1377 return self 1378 1379 def reset_clipping_range(self, bounds=None) -> Self: 1380 """ 1381 Reset the camera clipping range to include all visible actors. 1382 If bounds is given, it will be used instead of computing it. 1383 """ 1384 if bounds is None: 1385 self.renderer.ResetCameraClippingRange() 1386 else: 1387 self.renderer.ResetCameraClippingRange(bounds) 1388 return self 1389 1390 def reset_viewup(self, smooth=True) -> Self: 1391 """ 1392 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1393 """ 1394 vbb = addons.compute_visible_bounds()[0] 1395 x0, x1, y0, y1, z0, z1 = vbb 1396 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1397 d = self.camera.GetDistance() 1398 1399 viewups = np.array( 1400 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1401 ) 1402 positions = np.array( 1403 [ 1404 (mx, my, mz + d), 1405 (mx, my, mz - d), 1406 (mx, my + d, mz), 1407 (mx, my - d, mz), 1408 (mx + d, my, mz), 1409 (mx - d, my, mz), 1410 ] 1411 ) 1412 1413 vu = np.array(self.camera.GetViewUp()) 1414 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1415 1416 poc = np.array(self.camera.GetPosition()) 1417 foc = np.array(self.camera.GetFocalPoint()) 1418 a = poc - foc 1419 b = positions - foc 1420 a = a / np.linalg.norm(a) 1421 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1422 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1423 1424 if smooth: 1425 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1426 for t in outtimes: 1427 vv = vu * (1 - t) + viewups[vui] * t 1428 pp = poc * (1 - t) + positions[pui] * t 1429 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1430 self.camera.SetViewUp(vv) 1431 self.camera.SetPosition(pp) 1432 self.camera.SetFocalPoint(ff) 1433 self.render() 1434 1435 # interpolator does not respect parallel view...: 1436 # cam1 = dict( 1437 # pos=poc, 1438 # viewup=vu, 1439 # focal_point=(mx,my,mz), 1440 # clipping_range=self.camera.GetClippingRange() 1441 # ) 1442 # # cam1 = self.camera 1443 # cam2 = dict( 1444 # pos=positions[pui], 1445 # viewup=viewups[vui], 1446 # focal_point=(mx,my,mz), 1447 # clipping_range=self.camera.GetClippingRange() 1448 # ) 1449 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1450 # for c in vcams: 1451 # self.renderer.SetActiveCamera(c) 1452 # self.render() 1453 else: 1454 1455 self.camera.SetViewUp(viewups[vui]) 1456 self.camera.SetPosition(positions[pui]) 1457 self.camera.SetFocalPoint(mx, my, mz) 1458 1459 self.renderer.ResetCameraClippingRange() 1460 1461 # vbb, _, _, _ = addons.compute_visible_bounds() 1462 # x0,x1, y0,y1, z0,z1 = vbb 1463 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1464 self.render() 1465 return self 1466 1467 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1468 """ 1469 Takes as input two cameras set camera at an interpolated position: 1470 1471 Cameras can be vtkCamera or dictionaries in format: 1472 1473 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1474 1475 Press `shift-C` key in interactive mode to dump a python snipplet 1476 of parameters for the current camera view. 1477 """ 1478 nc = len(cameras) 1479 if len(times) == 0: 1480 times = np.linspace(0, 1, num=nc, endpoint=True) 1481 1482 assert len(times) == nc 1483 1484 cin = vtki.new("CameraInterpolator") 1485 1486 # cin.SetInterpolationTypeToLinear() # buggy? 1487 if nc > 2 and smooth: 1488 cin.SetInterpolationTypeToSpline() 1489 1490 for i, cam in enumerate(cameras): 1491 vcam = cam 1492 if isinstance(cam, dict): 1493 vcam = utils.camera_from_dict(cam) 1494 cin.AddCamera(times[i], vcam) 1495 1496 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1497 rng = maxt - mint 1498 1499 if len(output_times) == 0: 1500 cin.InterpolateCamera(t * rng, self.camera) 1501 return [self.camera] 1502 else: 1503 vcams = [] 1504 for tt in output_times: 1505 c = vtki.vtkCamera() 1506 cin.InterpolateCamera(tt * rng, c) 1507 vcams.append(c) 1508 return vcams 1509 1510 def fly_to(self, point) -> Self: 1511 """ 1512 Fly camera to the specified point. 1513 1514 Arguments: 1515 point : (list) 1516 point in space to place camera. 1517 1518 Example: 1519 ```python 1520 from vedo import * 1521 cone = Cone() 1522 plt = Plotter(axes=1) 1523 plt.show(cone) 1524 plt.fly_to([1,0,0]) 1525 plt.interactive().close() 1526 ``` 1527 """ 1528 if self.interactor: 1529 self.resetcam = False 1530 self.interactor.FlyTo(self.renderer, point) 1531 return self 1532 1533 def look_at(self, plane="xy") -> Self: 1534 """Move the camera so that it looks at the specified cartesian plane""" 1535 cam = self.renderer.GetActiveCamera() 1536 fp = np.array(cam.GetFocalPoint()) 1537 p = np.array(cam.GetPosition()) 1538 dist = np.linalg.norm(fp - p) 1539 plane = plane.lower() 1540 if "x" in plane and "y" in plane: 1541 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1542 cam.SetViewUp(0.0, 1.0, 0.0) 1543 elif "x" in plane and "z" in plane: 1544 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1545 cam.SetViewUp(0.0, 0.0, 1.0) 1546 elif "y" in plane and "z" in plane: 1547 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1548 cam.SetViewUp(0.0, 0.0, 1.0) 1549 else: 1550 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1551 return self 1552 1553 def record(self, filename="") -> str: 1554 """ 1555 Record camera, mouse, keystrokes and all other events. 1556 Recording can be toggled on/off by pressing key "R". 1557 1558 Arguments: 1559 filename : (str) 1560 ascii file to store events. 1561 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1562 1563 Returns: 1564 a string descriptor of events. 1565 1566 Examples: 1567 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1568 """ 1569 if vedo.settings.dry_run_mode >= 1: 1570 return "" 1571 if not self.interactor: 1572 vedo.logger.warning("Cannot record events, no interactor defined.") 1573 return "" 1574 erec = vtki.new("InteractorEventRecorder") 1575 erec.SetInteractor(self.interactor) 1576 if not filename: 1577 if not os.path.exists(vedo.settings.cache_directory): 1578 os.makedirs(vedo.settings.cache_directory) 1579 home_dir = os.path.expanduser("~") 1580 filename = os.path.join( 1581 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1582 print("Events will be recorded in", filename) 1583 erec.SetFileName(filename) 1584 erec.SetKeyPressActivationValue("R") 1585 erec.EnabledOn() 1586 erec.Record() 1587 self.interactor.Start() 1588 erec.Stop() 1589 erec.EnabledOff() 1590 with open(filename, "r", encoding="UTF-8") as fl: 1591 events = fl.read() 1592 erec = None 1593 return events 1594 1595 def play(self, recorded_events="", repeats=0) -> Self: 1596 """ 1597 Play camera, mouse, keystrokes and all other events. 1598 1599 Arguments: 1600 events : (str) 1601 file o string of events. 1602 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1603 repeats : (int) 1604 number of extra repeats of the same events. The default is 0. 1605 1606 Examples: 1607 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1608 """ 1609 if vedo.settings.dry_run_mode >= 1: 1610 return self 1611 if not self.interactor: 1612 vedo.logger.warning("Cannot play events, no interactor defined.") 1613 return self 1614 1615 erec = vtki.new("InteractorEventRecorder") 1616 erec.SetInteractor(self.interactor) 1617 1618 if not recorded_events: 1619 home_dir = os.path.expanduser("~") 1620 recorded_events = os.path.join( 1621 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1622 1623 if recorded_events.endswith(".log"): 1624 erec.ReadFromInputStringOff() 1625 erec.SetFileName(recorded_events) 1626 else: 1627 erec.ReadFromInputStringOn() 1628 erec.SetInputString(recorded_events) 1629 1630 erec.Play() 1631 for _ in range(repeats): 1632 erec.Rewind() 1633 erec.Play() 1634 erec.EnabledOff() 1635 erec = None 1636 return self 1637 1638 def parallel_projection(self, value=True, at=None) -> Self: 1639 """ 1640 Use parallel projection `at` a specified renderer. 1641 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1642 An input value equal to -1 will toggle it on/off. 1643 """ 1644 if at is not None: 1645 r = self.renderers[at] 1646 else: 1647 r = self.renderer 1648 if value == -1: 1649 val = r.GetActiveCamera().GetParallelProjection() 1650 value = not val 1651 r.GetActiveCamera().SetParallelProjection(value) 1652 r.Modified() 1653 return self 1654 1655 def render_hidden_lines(self, value=True) -> Self: 1656 """Remove hidden lines when in wireframe mode.""" 1657 self.renderer.SetUseHiddenLineRemoval(not value) 1658 return self 1659 1660 def fov(self, angle: float) -> Self: 1661 """ 1662 Set the field of view angle for the camera. 1663 This is the angle of the camera frustum in the horizontal direction. 1664 High values will result in a wide-angle lens (fish-eye effect), 1665 and low values will result in a telephoto lens. 1666 1667 Default value is 30 degrees. 1668 """ 1669 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1670 self.renderer.GetActiveCamera().SetViewAngle(angle) 1671 return self 1672 1673 def zoom(self, zoom: float) -> Self: 1674 """Apply a zooming factor for the current camera view""" 1675 self.renderer.GetActiveCamera().Zoom(zoom) 1676 return self 1677 1678 def azimuth(self, angle: float) -> Self: 1679 """Rotate camera around the view up vector.""" 1680 self.renderer.GetActiveCamera().Azimuth(angle) 1681 return self 1682 1683 def elevation(self, angle: float) -> Self: 1684 """Rotate the camera around the cross product of the negative 1685 of the direction of projection and the view up vector.""" 1686 self.renderer.GetActiveCamera().Elevation(angle) 1687 return self 1688 1689 def roll(self, angle: float) -> Self: 1690 """Roll the camera about the direction of projection.""" 1691 self.renderer.GetActiveCamera().Roll(angle) 1692 return self 1693 1694 def dolly(self, value: float) -> Self: 1695 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1696 self.renderer.GetActiveCamera().Dolly(value) 1697 return self 1698 1699 ################################################################## 1700 def add_slider( 1701 self, 1702 sliderfunc, 1703 xmin, 1704 xmax, 1705 value=None, 1706 pos=4, 1707 title="", 1708 font="Calco", 1709 title_size=1, 1710 c=None, 1711 alpha=1, 1712 show_value=True, 1713 delayed=False, 1714 **options, 1715 ) -> "vedo.addons.Slider2D": 1716 """ 1717 Add a `vedo.addons.Slider2D` which can call an external custom function. 1718 1719 Arguments: 1720 sliderfunc : (Callable) 1721 external function to be called by the widget 1722 xmin : (float) 1723 lower value of the slider 1724 xmax : (float) 1725 upper value 1726 value : (float) 1727 current value 1728 pos : (list, str) 1729 position corner number: horizontal [1-5] or vertical [11-15] 1730 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1731 and also by a string descriptor (eg. "bottom-left") 1732 title : (str) 1733 title text 1734 font : (str) 1735 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1736 title_size : (float) 1737 title text scale [1.0] 1738 show_value : (bool) 1739 if True current value is shown 1740 delayed : (bool) 1741 if True the callback is delayed until when the mouse button is released 1742 alpha : (float) 1743 opacity of the scalar bar texts 1744 slider_length : (float) 1745 slider length 1746 slider_width : (float) 1747 slider width 1748 end_cap_length : (float) 1749 length of the end cap 1750 end_cap_width : (float) 1751 width of the end cap 1752 tube_width : (float) 1753 width of the tube 1754 title_height : (float) 1755 width of the title 1756 tformat : (str) 1757 format of the title 1758 1759 Examples: 1760 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1761 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1762 1763 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1764 """ 1765 if c is None: # automatic black or white 1766 c = (0.8, 0.8, 0.8) 1767 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1768 c = (0.2, 0.2, 0.2) 1769 else: 1770 c = vedo.get_color(c) 1771 1772 slider2d = addons.Slider2D( 1773 sliderfunc, 1774 xmin, 1775 xmax, 1776 value, 1777 pos, 1778 title, 1779 font, 1780 title_size, 1781 c, 1782 alpha, 1783 show_value, 1784 delayed, 1785 **options, 1786 ) 1787 1788 if self.renderer: 1789 slider2d.renderer = self.renderer 1790 if self.interactor: 1791 slider2d.interactor = self.interactor 1792 slider2d.on() 1793 self.sliders.append([slider2d, sliderfunc]) 1794 return slider2d 1795 1796 def add_slider3d( 1797 self, 1798 sliderfunc, 1799 pos1, 1800 pos2, 1801 xmin, 1802 xmax, 1803 value=None, 1804 s=0.03, 1805 t=1, 1806 title="", 1807 rotation=0.0, 1808 c=None, 1809 show_value=True, 1810 ) -> "vedo.addons.Slider3D": 1811 """ 1812 Add a 3D slider widget which can call an external custom function. 1813 1814 Arguments: 1815 sliderfunc : (function) 1816 external function to be called by the widget 1817 pos1 : (list) 1818 first position 3D coordinates 1819 pos2 : (list) 1820 second position coordinates 1821 xmin : (float) 1822 lower value 1823 xmax : (float) 1824 upper value 1825 value : (float) 1826 initial value 1827 s : (float) 1828 label scaling factor 1829 t : (float) 1830 tube scaling factor 1831 title : (str) 1832 title text 1833 c : (color) 1834 slider color 1835 rotation : (float) 1836 title rotation around slider axis 1837 show_value : (bool) 1838 if True current value is shown 1839 1840 Examples: 1841 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1842 1843 ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png) 1844 """ 1845 if c is None: # automatic black or white 1846 c = (0.8, 0.8, 0.8) 1847 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1848 c = (0.2, 0.2, 0.2) 1849 else: 1850 c = vedo.get_color(c) 1851 1852 slider3d = addons.Slider3D( 1853 sliderfunc, 1854 pos1, 1855 pos2, 1856 xmin, 1857 xmax, 1858 value, 1859 s, 1860 t, 1861 title, 1862 rotation, 1863 c, 1864 show_value, 1865 ) 1866 slider3d.renderer = self.renderer 1867 slider3d.interactor = self.interactor 1868 slider3d.on() 1869 self.sliders.append([slider3d, sliderfunc]) 1870 return slider3d 1871 1872 def add_button( 1873 self, 1874 fnc=None, 1875 states=("On", "Off"), 1876 c=("w", "w"), 1877 bc=("green4", "red4"), 1878 pos=(0.7, 0.1), 1879 size=24, 1880 font="Courier", 1881 bold=True, 1882 italic=False, 1883 alpha=1, 1884 angle=0, 1885 ) -> Union["vedo.addons.Button", None]: 1886 """ 1887 Add a button to the renderer window. 1888 1889 Arguments: 1890 states : (list) 1891 a list of possible states, e.g. ['On', 'Off'] 1892 c : (list) 1893 a list of colors for each state 1894 bc : (list) 1895 a list of background colors for each state 1896 pos : (list) 1897 2D position from left-bottom corner 1898 size : (float) 1899 size of button font 1900 font : (str) 1901 font type. Check [available fonts here](https://vedo.embl.es/fonts). 1902 bold : (bool) 1903 bold font face (False) 1904 italic : (bool) 1905 italic font face (False) 1906 alpha : (float) 1907 opacity level 1908 angle : (float) 1909 anticlockwise rotation in degrees 1910 1911 Returns: 1912 `vedo.addons.Button` object. 1913 1914 Examples: 1915 - [buttons1.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons1.py) 1916 - [buttons2.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons2.py) 1917 1918 ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg) 1919 """ 1920 if self.interactor: 1921 bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle) 1922 self.renderer.AddActor2D(bu) 1923 self.buttons.append(bu) 1924 # bu.function_id = self.add_callback("LeftButtonPress", bu.function) # not good 1925 bu.function_id = bu.add_observer("pick", bu.function, priority=10) 1926 return bu 1927 return None 1928 1929 def add_spline_tool( 1930 self, points, pc="k", ps=8, lc="r4", ac="g5", 1931 lw=2, alpha=1, closed=False, ontop=True, can_add_nodes=True, 1932 ) -> "vedo.addons.SplineTool": 1933 """ 1934 Add a spline tool to the current plotter. 1935 Nodes of the spline can be dragged in space with the mouse. 1936 Clicking on the line itself adds an extra point. 1937 Selecting a point and pressing del removes it. 1938 1939 Arguments: 1940 points : (Mesh, Points, array) 1941 the set of vertices forming the spline nodes. 1942 pc : (str) 1943 point color. The default is 'k'. 1944 ps : (str) 1945 point size. The default is 8. 1946 lc : (str) 1947 line color. The default is 'r4'. 1948 ac : (str) 1949 active point marker color. The default is 'g5'. 1950 lw : (int) 1951 line width. The default is 2. 1952 alpha : (float) 1953 line transparency. 1954 closed : (bool) 1955 spline is meant to be closed. The default is False. 1956 1957 Returns: 1958 a `SplineTool` object. 1959 1960 Examples: 1961 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1962 1963 ![](https://vedo.embl.es/images/basic/spline_tool.png) 1964 """ 1965 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1966 sw.interactor = self.interactor 1967 sw.on() 1968 sw.Initialize(sw.points.dataset) 1969 sw.representation.SetRenderer(self.renderer) 1970 sw.representation.SetClosedLoop(closed) 1971 sw.representation.BuildRepresentation() 1972 self.widgets.append(sw) 1973 return sw 1974 1975 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1976 """Add an inset icon mesh into the same renderer. 1977 1978 Arguments: 1979 pos : (int, list) 1980 icon position in the range [1-4] indicating one of the 4 corners, 1981 or it can be a tuple (x,y) as a fraction of the renderer size. 1982 size : (float) 1983 size of the square inset. 1984 1985 Examples: 1986 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1987 """ 1988 iconw = addons.Icon(icon, pos, size) 1989 1990 iconw.SetInteractor(self.interactor) 1991 iconw.EnabledOn() 1992 iconw.InteractiveOff() 1993 self.widgets.append(iconw) 1994 return iconw 1995 1996 def add_global_axes(self, axtype=None, c=None) -> Self: 1997 """Draw axes on scene. Available axes types: 1998 1999 Arguments: 2000 axtype : (int) 2001 - 0, no axes, 2002 - 1, draw three gray grid walls 2003 - 2, show cartesian axes from (0,0,0) 2004 - 3, show positive range of cartesian axes from (0,0,0) 2005 - 4, show a triad at bottom left 2006 - 5, show a cube at bottom left 2007 - 6, mark the corners of the bounding box 2008 - 7, draw a 3D ruler at each side of the cartesian axes 2009 - 8, show the vtkCubeAxesActor object 2010 - 9, show the bounding box outLine 2011 - 10, show three circles representing the maximum bounding box 2012 - 11, show a large grid on the x-y plane 2013 - 12, show polar axes 2014 - 13, draw a simple ruler at the bottom of the window 2015 2016 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 2017 2018 Example: 2019 ```python 2020 from vedo import Box, show 2021 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 2022 show( 2023 b, 2024 axes={ 2025 "xtitle": "Some long variable [a.u.]", 2026 "number_of_divisions": 4, 2027 # ... 2028 }, 2029 ) 2030 ``` 2031 2032 Examples: 2033 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2034 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2035 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2036 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2037 2038 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2039 """ 2040 addons.add_global_axes(axtype, c) 2041 return self 2042 2043 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2044 """Add a legend to the top right. 2045 2046 Examples: 2047 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 2048 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 2049 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 2050 """ 2051 acts = self.get_meshes() 2052 lb = addons.LegendBox(acts, **kwargs) 2053 self.add(lb) 2054 return lb 2055 2056 def add_hint( 2057 self, 2058 obj, 2059 text="", 2060 c="k", 2061 bg="yellow9", 2062 font="Calco", 2063 size=18, 2064 justify=0, 2065 angle=0, 2066 delay=250, 2067 ) -> Union[vtki.vtkBalloonWidget, None]: 2068 """ 2069 Create a pop-up hint style message when hovering an object. 2070 Use `add_hint(obj, False)` to disable a hinting a specific object. 2071 Use `add_hint(None)` to disable all hints. 2072 2073 Arguments: 2074 obj : (Mesh, Points) 2075 the object to associate the pop-up to 2076 text : (str) 2077 string description of the pop-up 2078 delay : (int) 2079 milliseconds to wait before pop-up occurs 2080 """ 2081 if self.offscreen or not self.interactor: 2082 return None 2083 2084 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2085 # Linux vtk9.0 is bugged 2086 vedo.logger.warning( 2087 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2088 ) 2089 return None 2090 2091 if obj is None: 2092 self.hint_widget.EnabledOff() 2093 self.hint_widget.SetInteractor(None) 2094 self.hint_widget = None 2095 return self.hint_widget 2096 2097 if text is False and self.hint_widget: 2098 self.hint_widget.RemoveBalloon(obj) 2099 return self.hint_widget 2100 2101 if text == "": 2102 if obj.name: 2103 text = obj.name 2104 elif obj.filename: 2105 text = obj.filename 2106 else: 2107 return None 2108 2109 if not self.hint_widget: 2110 self.hint_widget = vtki.vtkBalloonWidget() 2111 2112 rep = self.hint_widget.GetRepresentation() 2113 rep.SetBalloonLayoutToImageRight() 2114 2115 trep = rep.GetTextProperty() 2116 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2117 trep.SetFontFile(utils.get_font_path(font)) 2118 trep.SetFontSize(size) 2119 trep.SetColor(vedo.get_color(c)) 2120 trep.SetBackgroundColor(vedo.get_color(bg)) 2121 trep.SetShadow(0) 2122 trep.SetJustification(justify) 2123 trep.UseTightBoundingBoxOn() 2124 2125 self.hint_widget.ManagesCursorOff() 2126 self.hint_widget.SetTimerDuration(delay) 2127 self.hint_widget.SetInteractor(self.interactor) 2128 if angle: 2129 trep.SetOrientation(angle) 2130 trep.SetBackgroundOpacity(0) 2131 # else: 2132 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2133 self.hint_widget.SetRepresentation(rep) 2134 self.widgets.append(self.hint_widget) 2135 self.hint_widget.EnabledOn() 2136 2137 bst = self.hint_widget.GetBalloonString(obj.actor) 2138 if bst: 2139 self.hint_widget.UpdateBalloonString(obj.actor, text) 2140 else: 2141 self.hint_widget.AddBalloon(obj.actor, text) 2142 2143 return self.hint_widget 2144 2145 def add_shadows(self) -> Self: 2146 """Add shadows at the current renderer.""" 2147 if self.renderer: 2148 shadows = vtki.new("ShadowMapPass") 2149 seq = vtki.new("SequencePass") 2150 passes = vtki.new("RenderPassCollection") 2151 passes.AddItem(shadows.GetShadowMapBakerPass()) 2152 passes.AddItem(shadows) 2153 seq.SetPasses(passes) 2154 camerapass = vtki.new("CameraPass") 2155 camerapass.SetDelegatePass(seq) 2156 self.renderer.SetPass(camerapass) 2157 return self 2158 2159 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> Self: 2160 """ 2161 Screen Space Ambient Occlusion. 2162 2163 For every pixel on the screen, the pixel shader samples the depth values around 2164 the current pixel and tries to compute the amount of occlusion from each of the sampled 2165 points. 2166 2167 Arguments: 2168 radius : (float) 2169 radius of influence in absolute units 2170 bias : (float) 2171 bias of the normals 2172 blur : (bool) 2173 add a blurring to the sampled positions 2174 samples : (int) 2175 number of samples to probe 2176 2177 Examples: 2178 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2179 2180 ![](https://vedo.embl.es/images/basic/ssao.jpg) 2181 """ 2182 lights = vtki.new("LightsPass") 2183 2184 opaque = vtki.new("OpaquePass") 2185 2186 ssaoCam = vtki.new("CameraPass") 2187 ssaoCam.SetDelegatePass(opaque) 2188 2189 ssao = vtki.new("SSAOPass") 2190 ssao.SetRadius(radius) 2191 ssao.SetBias(bias) 2192 ssao.SetBlur(blur) 2193 ssao.SetKernelSize(samples) 2194 ssao.SetDelegatePass(ssaoCam) 2195 2196 translucent = vtki.new("TranslucentPass") 2197 2198 volpass = vtki.new("VolumetricPass") 2199 ddp = vtki.new("DualDepthPeelingPass") 2200 ddp.SetTranslucentPass(translucent) 2201 ddp.SetVolumetricPass(volpass) 2202 2203 over = vtki.new("OverlayPass") 2204 2205 collection = vtki.new("RenderPassCollection") 2206 collection.AddItem(lights) 2207 collection.AddItem(ssao) 2208 collection.AddItem(ddp) 2209 collection.AddItem(over) 2210 2211 sequence = vtki.new("SequencePass") 2212 sequence.SetPasses(collection) 2213 2214 cam = vtki.new("CameraPass") 2215 cam.SetDelegatePass(sequence) 2216 2217 self.renderer.SetPass(cam) 2218 return self 2219 2220 def add_depth_of_field(self, autofocus=True) -> Self: 2221 """Add a depth of field effect in the scene.""" 2222 lights = vtki.new("LightsPass") 2223 2224 opaque = vtki.new("OpaquePass") 2225 2226 dofCam = vtki.new("CameraPass") 2227 dofCam.SetDelegatePass(opaque) 2228 2229 dof = vtki.new("DepthOfFieldPass") 2230 dof.SetAutomaticFocalDistance(autofocus) 2231 dof.SetDelegatePass(dofCam) 2232 2233 collection = vtki.new("RenderPassCollection") 2234 collection.AddItem(lights) 2235 collection.AddItem(dof) 2236 2237 sequence = vtki.new("SequencePass") 2238 sequence.SetPasses(collection) 2239 2240 cam = vtki.new("CameraPass") 2241 cam.SetDelegatePass(sequence) 2242 2243 self.renderer.SetPass(cam) 2244 return self 2245 2246 def _add_skybox(self, hdrfile: str) -> Self: 2247 # many hdr files are at https://polyhaven.com/all 2248 2249 reader = vtki.new("HDRReader") 2250 # Check the image can be read. 2251 if not reader.CanReadFile(hdrfile): 2252 vedo.logger.error(f"Cannot read HDR file {hdrfile}") 2253 return self 2254 reader.SetFileName(hdrfile) 2255 reader.Update() 2256 2257 texture = vtki.vtkTexture() 2258 texture.SetColorModeToDirectScalars() 2259 texture.SetInputData(reader.GetOutput()) 2260 2261 # Convert to a cube map 2262 tcm = vtki.new("EquirectangularToCubeMapTexture") 2263 tcm.SetInputTexture(texture) 2264 # Enable mipmapping to handle HDR image 2265 tcm.MipmapOn() 2266 tcm.InterpolateOn() 2267 2268 self.renderer.SetEnvironmentTexture(tcm) 2269 self.renderer.UseImageBasedLightingOn() 2270 self.skybox = vtki.new("Skybox") 2271 self.skybox.SetTexture(tcm) 2272 self.renderer.AddActor(self.skybox) 2273 return self 2274 2275 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None) -> "vedo.addons.RendererFrame": 2276 """ 2277 Add a frame to the renderer subwindow. 2278 2279 Arguments: 2280 c : (color) 2281 color name or index 2282 alpha : (float) 2283 opacity level 2284 lw : (int) 2285 line width in pixels. 2286 padding : (float) 2287 padding space in pixels. 2288 """ 2289 if c is None: # automatic black or white 2290 c = (0.9, 0.9, 0.9) 2291 if self.renderer: 2292 if np.sum(self.renderer.GetBackground()) > 1.5: 2293 c = (0.1, 0.1, 0.1) 2294 renf = addons.RendererFrame(c, alpha, lw, padding) 2295 if renf: 2296 self.renderer.AddActor(renf) 2297 return renf 2298 2299 def add_hover_legend( 2300 self, 2301 at=None, 2302 c=None, 2303 pos="bottom-left", 2304 font="Calco", 2305 s=0.75, 2306 bg="auto", 2307 alpha=0.1, 2308 maxlength=24, 2309 use_info=False, 2310 ) -> int: 2311 """ 2312 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2313 2314 The created text object are stored in `plotter.hover_legends`. 2315 2316 Returns: 2317 the id of the callback function. 2318 2319 Arguments: 2320 c : (color) 2321 Text color. If None then black or white is chosen automatically 2322 pos : (str) 2323 text positioning 2324 font : (str) 2325 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2326 s : (float) 2327 text size scale 2328 bg : (color) 2329 background color of the 2D box containing the text 2330 alpha : (float) 2331 box transparency 2332 maxlength : (int) 2333 maximum number of characters per line 2334 use_info : (bool) 2335 visualize the content of the `obj.info` attribute 2336 2337 Examples: 2338 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2339 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2340 2341 ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg) 2342 """ 2343 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2344 2345 if at is None: 2346 at = self.renderers.index(self.renderer) 2347 2348 def _legfunc(evt): 2349 if not evt.object or not self.renderer or at != evt.at: 2350 if hoverlegend.mapper.GetInput(): # clear and return 2351 hoverlegend.mapper.SetInput("") 2352 self.render() 2353 return 2354 2355 if use_info: 2356 if hasattr(evt.object, "info"): 2357 t = str(evt.object.info) 2358 else: 2359 return 2360 else: 2361 t, tp = "", "" 2362 if evt.isMesh: 2363 tp = "Mesh " 2364 elif evt.isPoints: 2365 tp = "Points " 2366 elif evt.isVolume: 2367 tp = "Volume " 2368 elif evt.isImage: 2369 tp = "Image " 2370 elif evt.isAssembly: 2371 tp = "Assembly " 2372 else: 2373 return 2374 2375 if evt.isAssembly: 2376 if not evt.object.name: 2377 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2378 else: 2379 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2380 else: 2381 if evt.object.name: 2382 t += f"{tp}name" 2383 if evt.isPoints: 2384 t += " " 2385 if evt.isMesh: 2386 t += " " 2387 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2388 2389 if evt.object.filename: 2390 t += f"{tp}filename: " 2391 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2392 t += "\n" 2393 if not evt.object.file_size: 2394 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2395 if evt.object.file_size: 2396 t += " : " 2397 sz, created = evt.object.file_size, evt.object.created 2398 t += f"{created[4:-5]} ({sz})" + "\n" 2399 2400 if evt.isPoints: 2401 indata = evt.object.dataset 2402 if indata.GetNumberOfPoints(): 2403 t += ( 2404 f"#points/cells: {indata.GetNumberOfPoints()}" 2405 f" / {indata.GetNumberOfCells()}" 2406 ) 2407 pdata = indata.GetPointData() 2408 cdata = indata.GetCellData() 2409 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2410 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2411 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2412 t += " *" 2413 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2414 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2415 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2416 t += " *" 2417 2418 if evt.isImage: 2419 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2420 t += f"\nImage shape: {evt.object.shape}" 2421 pcol = self.color_picker(evt.picked2d) 2422 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2423 2424 # change box color if needed in 'auto' mode 2425 if evt.isPoints and "auto" in str(bg): 2426 actcol = evt.object.properties.GetColor() 2427 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2428 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2429 2430 # adapt to changes in bg color 2431 bgcol = self.renderers[at].GetBackground() 2432 _bgcol = c 2433 if _bgcol is None: # automatic black or white 2434 _bgcol = (0.9, 0.9, 0.9) 2435 if sum(bgcol) > 1.5: 2436 _bgcol = (0.1, 0.1, 0.1) 2437 if len(set(_bgcol).intersection(bgcol)) < 3: 2438 hoverlegend.color(_bgcol) 2439 2440 if hoverlegend.mapper.GetInput() != t: 2441 hoverlegend.mapper.SetInput(t) 2442 self.interactor.Render() 2443 2444 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2445 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2446 2447 self.add(hoverlegend, at=at) 2448 self.hover_legends.append(hoverlegend) 2449 idcall = self.add_callback("MouseMove", _legfunc) 2450 return idcall 2451 2452 def add_scale_indicator( 2453 self, 2454 pos=(0.7, 0.05), 2455 s=0.02, 2456 length=2, 2457 lw=4, 2458 c="k1", 2459 alpha=1, 2460 units="", 2461 gap=0.05, 2462 ) -> Union["vedo.visual.Actor2D", None]: 2463 """ 2464 Add a Scale Indicator. Only works in parallel mode (no perspective). 2465 2466 Arguments: 2467 pos : (list) 2468 fractional (x,y) position on the screen. 2469 s : (float) 2470 size of the text. 2471 length : (float) 2472 length of the line. 2473 units : (str) 2474 string to show units. 2475 gap : (float) 2476 separation of line and text. 2477 2478 Example: 2479 ```python 2480 from vedo import settings, Cube, Plotter 2481 settings.use_parallel_projection = True # or else it does not make sense! 2482 cube = Cube().alpha(0.2) 2483 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2484 plt.add_scale_indicator(units='um', c='blue4') 2485 plt.show(cube, "Scale indicator with units").close() 2486 ``` 2487 ![](https://vedo.embl.es/images/feats/scale_indicator.png) 2488 """ 2489 # Note that this cannot go in addons.py 2490 # because it needs callbacks and window size 2491 if not self.interactor: 2492 return None 2493 2494 ppoints = vtki.vtkPoints() # Generate the polyline 2495 psqr = [[0.0, gap], [length / 10, gap]] 2496 dd = psqr[1][0] - psqr[0][0] 2497 for i, pt in enumerate(psqr): 2498 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2499 lines = vtki.vtkCellArray() 2500 lines.InsertNextCell(len(psqr)) 2501 for i in range(len(psqr)): 2502 lines.InsertCellPoint(i) 2503 pd = vtki.vtkPolyData() 2504 pd.SetPoints(ppoints) 2505 pd.SetLines(lines) 2506 2507 wsx, wsy = self.window.GetSize() 2508 if not self.camera.GetParallelProjection(): 2509 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2510 return None 2511 2512 rlabel = vtki.new("VectorText") 2513 rlabel.SetText("scale") 2514 tf = vtki.new("TransformPolyDataFilter") 2515 tf.SetInputConnection(rlabel.GetOutputPort()) 2516 t = vtki.vtkTransform() 2517 t.Scale(s * wsy / wsx, s, 1) 2518 tf.SetTransform(t) 2519 2520 app = vtki.new("AppendPolyData") 2521 app.AddInputConnection(tf.GetOutputPort()) 2522 app.AddInputData(pd) 2523 2524 mapper = vtki.new("PolyDataMapper2D") 2525 mapper.SetInputConnection(app.GetOutputPort()) 2526 cs = vtki.vtkCoordinate() 2527 cs.SetCoordinateSystem(1) 2528 mapper.SetTransformCoordinate(cs) 2529 2530 fractor = vedo.visual.Actor2D() 2531 csys = fractor.GetPositionCoordinate() 2532 csys.SetCoordinateSystem(3) 2533 fractor.SetPosition(pos) 2534 fractor.SetMapper(mapper) 2535 fractor.GetProperty().SetColor(vedo.get_color(c)) 2536 fractor.GetProperty().SetOpacity(alpha) 2537 fractor.GetProperty().SetLineWidth(lw) 2538 fractor.GetProperty().SetDisplayLocationToForeground() 2539 2540 def sifunc(iren, ev): 2541 wsx, wsy = self.window.GetSize() 2542 ps = self.camera.GetParallelScale() 2543 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2544 if units: 2545 newtxt += " " + units 2546 if rlabel.GetText() != newtxt: 2547 rlabel.SetText(newtxt) 2548 2549 self.renderer.AddActor(fractor) 2550 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2551 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2552 self.interactor.AddObserver("InteractionEvent", sifunc) 2553 sifunc(0, 0) 2554 return fractor 2555 2556 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2557 """ 2558 Create an Event object with information of what was clicked. 2559 2560 If `enable_picking` is False, no picking will be performed. 2561 This can be useful to avoid double picking when using buttons. 2562 """ 2563 if not self.interactor: 2564 return Event() 2565 2566 if len(pos) > 0: 2567 x, y = pos 2568 self.interactor.SetEventPosition(pos) 2569 else: 2570 x, y = self.interactor.GetEventPosition() 2571 self.renderer = self.interactor.FindPokedRenderer(x, y) 2572 2573 self.picked2d = (x, y) 2574 2575 key = self.interactor.GetKeySym() 2576 2577 if key: 2578 if "_L" in key or "_R" in key: 2579 # skip things like Shift_R 2580 key = "" # better than None 2581 else: 2582 if self.interactor.GetShiftKey(): 2583 key = key.upper() 2584 2585 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2586 key = "underscore" 2587 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2588 key = "plus" 2589 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2590 key = "?" 2591 2592 if self.interactor.GetControlKey(): 2593 key = "Ctrl+" + key 2594 2595 if self.interactor.GetAltKey(): 2596 key = "Alt+" + key 2597 2598 if enable_picking: 2599 if not self.picker: 2600 self.picker = vtki.vtkPropPicker() 2601 2602 self.picker.PickProp(x, y, self.renderer) 2603 actor = self.picker.GetProp3D() 2604 # Note that GetProp3D already picks Assembly 2605 2606 xp, yp = self.interactor.GetLastEventPosition() 2607 dx, dy = x - xp, y - yp 2608 2609 delta3d = np.array([0, 0, 0]) 2610 2611 if actor: 2612 picked3d = np.array(self.picker.GetPickPosition()) 2613 2614 try: 2615 vobj = actor.retrieve_object() 2616 old_pt = np.asarray(vobj.picked3d) 2617 vobj.picked3d = picked3d 2618 delta3d = picked3d - old_pt 2619 except (AttributeError, TypeError): 2620 pass 2621 2622 else: 2623 picked3d = None 2624 2625 if not actor: # try 2D 2626 actor = self.picker.GetActor2D() 2627 2628 event = Event() 2629 event.name = ename 2630 event.title = self.title 2631 event.id = -1 # will be set by the timer wrapper function 2632 event.timerid = -1 # will be set by the timer wrapper function 2633 event.priority = -1 # will be set by the timer wrapper function 2634 event.time = time.time() 2635 event.at = self.renderers.index(self.renderer) 2636 event.keypress = key 2637 if enable_picking: 2638 try: 2639 event.object = actor.retrieve_object() 2640 except AttributeError: 2641 event.object = actor 2642 try: 2643 event.actor = actor.retrieve_object() # obsolete use object instead 2644 except AttributeError: 2645 event.actor = actor 2646 event.picked3d = picked3d 2647 event.picked2d = (x, y) 2648 event.delta2d = (dx, dy) 2649 event.angle2d = np.arctan2(dy, dx) 2650 event.speed2d = np.sqrt(dx * dx + dy * dy) 2651 event.delta3d = delta3d 2652 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2653 event.isPoints = isinstance(event.object, vedo.Points) 2654 event.isMesh = isinstance(event.object, vedo.Mesh) 2655 event.isAssembly = isinstance(event.object, vedo.Assembly) 2656 event.isVolume = isinstance(event.object, vedo.Volume) 2657 event.isImage = isinstance(event.object, vedo.Image) 2658 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2659 return event 2660 2661 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2662 """ 2663 Add a function to be executed while show() is active. 2664 2665 Return a unique id for the callback. 2666 2667 The callback function (see example below) exposes a dictionary 2668 with the following information: 2669 - `name`: event name, 2670 - `id`: event unique identifier, 2671 - `priority`: event priority (float), 2672 - `interactor`: the interactor object, 2673 - `at`: renderer nr. where the event occurred 2674 - `keypress`: key pressed as string 2675 - `actor`: object picked by the mouse 2676 - `picked3d`: point picked in world coordinates 2677 - `picked2d`: screen coords of the mouse pointer 2678 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2679 - `delta3d`: ...same but in 3D world coords 2680 - `angle2d`: angle of mouse movement on screen 2681 - `speed2d`: speed of mouse movement on screen 2682 - `speed3d`: speed of picked point in world coordinates 2683 - `isPoints`: True if of class 2684 - `isMesh`: True if of class 2685 - `isAssembly`: True if of class 2686 - `isVolume`: True if of class Volume 2687 - `isImage`: True if of class 2688 2689 If `enable_picking` is False, no picking will be performed. 2690 This can be useful to avoid double picking when using buttons. 2691 2692 Frequently used events are: 2693 - `KeyPress`, `KeyRelease`: listen to keyboard events 2694 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2695 - `MiddleButtonPress`, `MiddleButtonRelease` 2696 - `RightButtonPress`, `RightButtonRelease` 2697 - `MouseMove`: listen to mouse pointer changing position 2698 - `MouseWheelForward`, `MouseWheelBackward` 2699 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2700 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2701 - `ResetCamera`, `ResetCameraClippingRange` 2702 - `Error`, `Warning` 2703 - `Char` 2704 - `Timer` 2705 2706 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2707 2708 Example: 2709 ```python 2710 from vedo import * 2711 2712 def func(evt): 2713 # this function is called every time the mouse moves 2714 # (evt is a dotted dictionary) 2715 if not evt.object: 2716 return # no hit, return 2717 print("point coords =", evt.picked3d) 2718 # print(evt) # full event dump 2719 2720 elli = Ellipsoid() 2721 plt = Plotter(axes=1) 2722 plt.add_callback('mouse hovering', func) 2723 plt.show(elli).close() 2724 ``` 2725 2726 Examples: 2727 - [spline_draw1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw1.py) 2728 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2729 2730 ![](https://vedo.embl.es/images/advanced/spline_draw.png) 2731 2732 - ..and many others! 2733 """ 2734 from vtkmodules.util.misc import calldata_type 2735 2736 if not self.interactor: 2737 return 0 2738 2739 if vedo.settings.dry_run_mode >= 1: 2740 return 0 2741 2742 ######################################### 2743 @calldata_type(vtki.VTK_INT) 2744 def _func_wrap(iren, ename, timerid=None): 2745 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2746 event.timerid = timerid 2747 event.id = cid 2748 event.priority = priority 2749 self.last_event = event 2750 func(event) 2751 2752 ######################################### 2753 2754 event_name = utils.get_vtk_name_event(event_name) 2755 2756 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2757 # print(f"Registering event: {event_name} with id={cid}") 2758 return cid 2759 2760 def remove_callback(self, cid: Union[int, str]) -> Self: 2761 """ 2762 Remove a callback function by its id 2763 or a whole category of callbacks by their name. 2764 2765 Arguments: 2766 cid : (int, str) 2767 Unique id of the callback. 2768 If an event name is passed all callbacks of that type are removed. 2769 """ 2770 if self.interactor: 2771 if isinstance(cid, str): 2772 cid = utils.get_vtk_name_event(cid) 2773 self.interactor.RemoveObservers(cid) 2774 else: 2775 self.interactor.RemoveObserver(cid) 2776 return self 2777 2778 def remove_all_observers(self) -> Self: 2779 """ 2780 Remove all observers. 2781 2782 Example: 2783 ```python 2784 from vedo import * 2785 2786 def kfunc(event): 2787 print("Key pressed:", event.keypress) 2788 if event.keypress == 'q': 2789 plt.close() 2790 2791 def rfunc(event): 2792 if event.isImage: 2793 printc("Right-clicked!", event) 2794 plt.render() 2795 2796 img = Image(dataurl+"images/embryo.jpg") 2797 2798 plt = Plotter(size=(1050, 600)) 2799 plt.parallel_projection(True) 2800 plt.remove_all_observers() 2801 plt.add_callback("key press", kfunc) 2802 plt.add_callback("mouse right click", rfunc) 2803 plt.show("Right-Click Me! Press q to exit.", img) 2804 plt.close() 2805 ``` 2806 """ 2807 if self.interactor: 2808 self.interactor.RemoveAllObservers() 2809 return self 2810 2811 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2812 """ 2813 Start or stop an existing timer. 2814 2815 Arguments: 2816 action : (str) 2817 Either "create"/"start" or "destroy"/"stop" 2818 timer_id : (int) 2819 When stopping the timer, the ID of the timer as returned when created 2820 dt : (int) 2821 time in milliseconds between each repeated call 2822 one_shot : (bool) 2823 create a one shot timer of prescribed duration instead of a repeating one 2824 2825 Examples: 2826 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2827 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2828 2829 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 2830 """ 2831 if action in ("create", "start"): 2832 if timer_id is not None: 2833 vedo.logger.warning("you set a timer_id but it will be ignored.") 2834 if one_shot: 2835 timer_id = self.interactor.CreateOneShotTimer(dt) 2836 else: 2837 timer_id = self.interactor.CreateRepeatingTimer(dt) 2838 return timer_id 2839 2840 elif action in ("destroy", "stop"): 2841 if timer_id is not None: 2842 self.interactor.DestroyTimer(timer_id) 2843 else: 2844 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2845 else: 2846 e = f"in timer_callback(). Cannot understand action: {action}\n" 2847 e += " allowed actions are: ['start', 'stop']. Skipped." 2848 vedo.logger.error(e) 2849 return timer_id 2850 2851 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2852 """ 2853 Add a callback function that will be called when an event occurs. 2854 Consider using `add_callback()` instead. 2855 """ 2856 if not self.interactor: 2857 return -1 2858 event_name = utils.get_vtk_name_event(event_name) 2859 idd = self.interactor.AddObserver(event_name, func, priority) 2860 return idd 2861 2862 def compute_world_coordinate( 2863 self, 2864 pos2d: MutableSequence[float], 2865 at=None, 2866 objs=(), 2867 bounds=(), 2868 offset=None, 2869 pixeltol=None, 2870 worldtol=None, 2871 ) -> np.ndarray: 2872 """ 2873 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2874 If a set of meshes is passed then points are placed onto these. 2875 2876 Arguments: 2877 pos2d : (list) 2878 2D screen coordinates point. 2879 at : (int) 2880 renderer number. 2881 objs : (list) 2882 list of Mesh objects to project the point onto. 2883 bounds : (list) 2884 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2885 offset : (float) 2886 specify an offset value. 2887 pixeltol : (int) 2888 screen tolerance in pixels. 2889 worldtol : (float) 2890 world coordinates tolerance. 2891 2892 Returns: 2893 numpy array, the point in 3D world coordinates. 2894 2895 Examples: 2896 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2897 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2898 2899 ![](https://vedo.embl.es/images/basic/mousehover3.jpg) 2900 """ 2901 if at is not None: 2902 renderer = self.renderers[at] 2903 else: 2904 renderer = self.renderer 2905 2906 if not objs: 2907 pp = vtki.vtkFocalPlanePointPlacer() 2908 else: 2909 pps = vtki.vtkPolygonalSurfacePointPlacer() 2910 for ob in objs: 2911 pps.AddProp(ob.actor) 2912 pp = pps # type: ignore 2913 2914 if len(bounds) == 6: 2915 pp.SetPointBounds(bounds) 2916 if pixeltol: 2917 pp.SetPixelTolerance(pixeltol) 2918 if worldtol: 2919 pp.SetWorldTolerance(worldtol) 2920 if offset: 2921 pp.SetOffset(offset) 2922 2923 worldPos: MutableSequence[float] = [0, 0, 0] 2924 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2925 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2926 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2927 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2928 return np.array(worldPos) 2929 2930 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2931 """ 2932 Given a 3D points in the current renderer (or full window), 2933 find the screen pixel coordinates. 2934 2935 Example: 2936 ```python 2937 from vedo import * 2938 2939 elli = Ellipsoid().point_size(5) 2940 2941 plt = Plotter() 2942 plt.show(elli, "Press q to continue and print the info") 2943 2944 xyscreen = plt.compute_screen_coordinates(elli) 2945 print('xyscreen coords:', xyscreen) 2946 2947 # simulate an event happening at one point 2948 event = plt.fill_event(pos=xyscreen[123]) 2949 print(event) 2950 ``` 2951 """ 2952 try: 2953 obj = obj.vertices 2954 except AttributeError: 2955 pass 2956 2957 if utils.is_sequence(obj): 2958 pts = obj 2959 p2d = [] 2960 cs = vtki.vtkCoordinate() 2961 cs.SetCoordinateSystemToWorld() 2962 cs.SetViewport(self.renderer) 2963 for p in pts: 2964 cs.SetValue(p) 2965 if full_window: 2966 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2967 else: 2968 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2969 return np.array(p2d, dtype=int) 2970 2971 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2972 """ 2973 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2974 2975 Returns a frustum Mesh that contains the visible field of view. 2976 This can be used to select objects in a scene or select vertices. 2977 2978 Example: 2979 ```python 2980 from vedo import * 2981 2982 settings.enable_default_mouse_callbacks = False 2983 2984 def mode_select(objs): 2985 print("Selected objects:", objs) 2986 d0 = mode.start_x, mode.start_y # display coords 2987 d1 = mode.end_x, mode.end_y 2988 2989 frustum = plt.pick_area(d0, d1) 2990 col = np.random.randint(0, 10) 2991 infru = frustum.inside_points(mesh) 2992 infru.point_size(10).color(col) 2993 plt.add(frustum, infru).render() 2994 2995 mesh = Mesh(dataurl+"cow.vtk") 2996 mesh.color("k5").linewidth(1) 2997 2998 mode = interactor_modes.BlenderStyle() 2999 mode.callback_select = mode_select 3000 3001 plt = Plotter().user_mode(mode) 3002 plt.show(mesh, axes=1) 3003 ``` 3004 """ 3005 if at is not None: 3006 ren = self.renderers[at] 3007 else: 3008 ren = self.renderer 3009 area_picker = vtki.vtkAreaPicker() 3010 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 3011 planes = area_picker.GetFrustum() 3012 3013 fru = vtki.new("FrustumSource") 3014 fru.SetPlanes(planes) 3015 fru.ShowLinesOff() 3016 fru.Update() 3017 3018 afru = vedo.Mesh(fru.GetOutput()) 3019 afru.alpha(0.1).lw(1).pickable(False) 3020 afru.name = "Frustum" 3021 return afru 3022 3023 def _scan_input_return_acts(self, objs) -> Any: 3024 # scan the input and return a list of actors 3025 if not utils.is_sequence(objs): 3026 objs = [objs] 3027 3028 ################# 3029 wannabe_acts = [] 3030 for a in objs: 3031 3032 try: 3033 wannabe_acts.append(a.actor) 3034 except AttributeError: 3035 wannabe_acts.append(a) # already actor 3036 3037 try: 3038 wannabe_acts.append(a.scalarbar) 3039 except AttributeError: 3040 pass 3041 3042 try: 3043 for sh in a.shadows: 3044 wannabe_acts.append(sh.actor) 3045 except AttributeError: 3046 pass 3047 3048 try: 3049 wannabe_acts.append(a.trail.actor) 3050 if a.trail.shadows: # trails may also have shadows 3051 for sh in a.trail.shadows: 3052 wannabe_acts.append(sh.actor) 3053 except AttributeError: 3054 pass 3055 3056 ################# 3057 scanned_acts = [] 3058 for a in wannabe_acts: # scan content of list 3059 3060 if a is None: 3061 pass 3062 3063 elif isinstance(a, (vtki.vtkActor, vtki.vtkActor2D)): 3064 scanned_acts.append(a) 3065 3066 elif isinstance(a, str): 3067 # assume a 2D comment was given 3068 changed = False # check if one already exists so to just update text 3069 if self.renderer: # might be jupyter 3070 acs = self.renderer.GetActors2D() 3071 acs.InitTraversal() 3072 for i in range(acs.GetNumberOfItems()): 3073 act = acs.GetNextItem() 3074 if isinstance(act, vedo.shapes.Text2D): 3075 aposx, aposy = act.GetPosition() 3076 if aposx < 0.01 and aposy > 0.99: # "top-left" 3077 act.text(a) # update content! no appending nada 3078 changed = True 3079 break 3080 if not changed: 3081 out = vedo.shapes.Text2D(a) # append a new one 3082 scanned_acts.append(out) 3083 # scanned_acts.append(vedo.shapes.Text2D(a)) # naive version 3084 3085 elif isinstance(a, vtki.vtkPolyData): 3086 scanned_acts.append(vedo.Mesh(a).actor) 3087 3088 elif isinstance(a, vtki.vtkImageData): 3089 scanned_acts.append(vedo.Volume(a).actor) 3090 3091 elif isinstance(a, vedo.RectilinearGrid): 3092 scanned_acts.append(a.actor) 3093 3094 elif isinstance(a, vedo.StructuredGrid): 3095 scanned_acts.append(a.actor) 3096 3097 elif isinstance(a, vtki.vtkLight): 3098 scanned_acts.append(a) 3099 3100 elif isinstance(a, vedo.visual.LightKit): 3101 a.lightkit.AddLightsToRenderer(self.renderer) 3102 3103 elif isinstance(a, vtki.get_class("MultiBlockDataSet")): 3104 for i in range(a.GetNumberOfBlocks()): 3105 b = a.GetBlock(i) 3106 if isinstance(b, vtki.vtkPolyData): 3107 scanned_acts.append(vedo.Mesh(b).actor) 3108 elif isinstance(b, vtki.vtkImageData): 3109 scanned_acts.append(vedo.Volume(b).actor) 3110 3111 elif isinstance(a, (vtki.vtkProp, vtki.vtkInteractorObserver)): 3112 scanned_acts.append(a) 3113 3114 elif "trimesh" in str(type(a)): 3115 scanned_acts.append(utils.trimesh2vedo(a)) 3116 3117 elif "meshlab" in str(type(a)): 3118 if "MeshSet" in str(type(a)): 3119 for i in range(a.number_meshes()): 3120 if a.mesh_id_exists(i): 3121 scanned_acts.append(utils.meshlab2vedo(a.mesh(i))) 3122 else: 3123 scanned_acts.append(utils.meshlab2vedo(a)) 3124 3125 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 3126 import vedo.dolfin as vdlf 3127 3128 scanned_acts.append(vdlf.IMesh(a).actor) 3129 3130 elif "madcad" in str(type(a)): 3131 scanned_acts.append(utils.madcad2vedo(a).actor) 3132 3133 elif "TetgenIO" in str(type(a)): 3134 scanned_acts.append(vedo.TetMesh(a).shrink(0.9).c("pink7").actor) 3135 3136 elif "matplotlib.figure.Figure" in str(type(a)): 3137 scanned_acts.append(vedo.Image(a).clone2d("top-right", 0.6)) 3138 3139 else: 3140 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 3141 3142 return scanned_acts 3143 3144 def show( 3145 self, 3146 *objects, 3147 at=None, 3148 axes=None, 3149 resetcam=None, 3150 zoom=False, 3151 interactive=None, 3152 viewup="", 3153 azimuth=0.0, 3154 elevation=0.0, 3155 roll=0.0, 3156 camera=None, 3157 mode=None, 3158 rate=None, 3159 bg=None, 3160 bg2=None, 3161 size=None, 3162 title=None, 3163 screenshot="", 3164 ) -> Any: 3165 """ 3166 Render a list of objects. 3167 3168 Arguments: 3169 at : (int) 3170 number of the renderer to plot to, in case of more than one exists 3171 3172 axes : (int) 3173 axis type-1 can be fully customized by passing a dictionary. 3174 Check `addons.Axes()` for the full list of options. 3175 set the type of axes to be shown: 3176 - 0, no axes 3177 - 1, draw three gray grid walls 3178 - 2, show cartesian axes from (0,0,0) 3179 - 3, show positive range of cartesian axes from (0,0,0) 3180 - 4, show a triad at bottom left 3181 - 5, show a cube at bottom left 3182 - 6, mark the corners of the bounding box 3183 - 7, draw a 3D ruler at each side of the cartesian axes 3184 - 8, show the `vtkCubeAxesActor` object 3185 - 9, show the bounding box outLine 3186 - 10, show three circles representing the maximum bounding box 3187 - 11, show a large grid on the x-y plane 3188 - 12, show polar axes 3189 - 13, draw a simple ruler at the bottom of the window 3190 3191 azimuth/elevation/roll : (float) 3192 move camera accordingly the specified value 3193 3194 viewup: str, list 3195 either `['x', 'y', 'z']` or a vector to set vertical direction 3196 3197 resetcam : (bool) 3198 re-adjust camera position to fit objects 3199 3200 camera : (dict, vtkCamera) 3201 camera parameters can further be specified with a dictionary 3202 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3203 - pos, `(list)`, the position of the camera in world coordinates 3204 - focal_point `(list)`, the focal point of the camera in world coordinates 3205 - viewup `(list)`, the view up direction for the camera 3206 - distance `(float)`, set the focal point to the specified distance from the camera position. 3207 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3208 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3209 in world-coordinate distances. The default is 1. 3210 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3211 This method has no effect in perspective projection mode. 3212 3213 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3214 plane to be set a distance 'thickness' beyond the near clipping plane. 3215 3216 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3217 measured in degrees. The default angle is 30 degrees. 3218 This method has no effect in parallel projection mode. 3219 The formula for setting the angle up for perfect perspective viewing is: 3220 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3221 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3222 3223 interactive : (bool) 3224 pause and interact with window (True) or continue execution (False) 3225 3226 rate : (float) 3227 maximum rate of `show()` in Hertz 3228 3229 mode : (int, str) 3230 set the type of interaction: 3231 - 0 = TrackballCamera [default] 3232 - 1 = TrackballActor 3233 - 2 = JoystickCamera 3234 - 3 = JoystickActor 3235 - 4 = Flight 3236 - 5 = RubberBand2D 3237 - 6 = RubberBand3D 3238 - 7 = RubberBandZoom 3239 - 8 = Terrain 3240 - 9 = Unicam 3241 - 10 = Image 3242 - Check out `vedo.interaction_modes` for more options. 3243 3244 bg : (str, list) 3245 background color in RGB format, or string name 3246 3247 bg2 : (str, list) 3248 second background color to create a gradient background 3249 3250 size : (str, list) 3251 size of the window, e.g. size="fullscreen", or size=[600,400] 3252 3253 title : (str) 3254 window title text 3255 3256 screenshot : (str) 3257 save a screenshot of the window to file 3258 """ 3259 3260 if vedo.settings.dry_run_mode >= 2: 3261 return self 3262 3263 if self.wx_widget: 3264 return self 3265 3266 if self.renderers: # in case of notebooks 3267 3268 if at is None: 3269 at = self.renderers.index(self.renderer) 3270 3271 else: 3272 3273 if at >= len(self.renderers): 3274 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3275 vedo.logger.error(t) 3276 return self 3277 3278 self.renderer = self.renderers[at] 3279 3280 if title is not None: 3281 self.title = title 3282 3283 if size is not None: 3284 self.size = size 3285 if self.size[0] == "f": # full screen 3286 self.size = "fullscreen" 3287 self.window.SetFullScreen(True) 3288 self.window.BordersOn() 3289 else: 3290 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3291 3292 if vedo.settings.default_backend == "vtk": 3293 if str(bg).endswith(".hdr"): 3294 self._add_skybox(bg) 3295 else: 3296 if bg is not None: 3297 self.backgrcol = vedo.get_color(bg) 3298 self.renderer.SetBackground(self.backgrcol) 3299 if bg2 is not None: 3300 self.renderer.GradientBackgroundOn() 3301 self.renderer.SetBackground2(vedo.get_color(bg2)) 3302 3303 if axes is not None: 3304 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3305 objects = list(objects) 3306 objects.append(axes) # move it into the list of normal things to show 3307 axes = 0 3308 self.axes = axes 3309 3310 if interactive is not None: 3311 self._interactive = interactive 3312 if self.offscreen: 3313 self._interactive = False 3314 3315 # camera stuff 3316 if resetcam is not None: 3317 self.resetcam = resetcam 3318 3319 if camera is not None: 3320 self.resetcam = False 3321 viewup = "" 3322 if isinstance(camera, vtki.vtkCamera): 3323 cameracopy = vtki.vtkCamera() 3324 cameracopy.DeepCopy(camera) 3325 self.camera = cameracopy 3326 else: 3327 self.camera = utils.camera_from_dict(camera) 3328 3329 self.add(objects) 3330 3331 # Backend ############################################################### 3332 if vedo.settings.default_backend in ["k3d", "panel"]: 3333 return backends.get_notebook_backend(self.objects) 3334 ######################################################################### 3335 3336 for ia in utils.flatten(objects): 3337 try: 3338 # fix gray color labels and title to white or black 3339 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3340 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3341 c = (0.9, 0.9, 0.9) 3342 if np.sum(self.renderer.GetBackground()) > 1.5: 3343 c = (0.1, 0.1, 0.1) 3344 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3345 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3346 except AttributeError: 3347 pass 3348 3349 if self.sharecam: 3350 for r in self.renderers: 3351 r.SetActiveCamera(self.camera) 3352 3353 if self.axes is not None: 3354 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3355 bns = self.renderer.ComputeVisiblePropBounds() 3356 addons.add_global_axes(self.axes, bounds=bns) 3357 3358 # Backend ############################################################### 3359 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3360 return backends.get_notebook_backend() 3361 ######################################################################### 3362 3363 if self.resetcam: 3364 self.renderer.ResetCamera() 3365 3366 if len(self.renderers) > 1: 3367 self.add_renderer_frame() 3368 3369 if vedo.settings.default_backend == "2d" and not zoom: 3370 zoom = "tightest" 3371 3372 if zoom: 3373 if zoom == "tight": 3374 self.reset_camera(tight=0.04) 3375 elif zoom == "tightest": 3376 self.reset_camera(tight=0.0001) 3377 else: 3378 self.camera.Zoom(zoom) 3379 if elevation: 3380 self.camera.Elevation(elevation) 3381 if azimuth: 3382 self.camera.Azimuth(azimuth) 3383 if roll: 3384 self.camera.Roll(roll) 3385 3386 if len(viewup) > 0: 3387 b = self.renderer.ComputeVisiblePropBounds() 3388 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3389 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3390 if viewup == "x": 3391 sz = np.linalg.norm(sz) 3392 self.camera.SetViewUp([1, 0, 0]) 3393 self.camera.SetPosition(cm + sz) 3394 elif viewup == "y": 3395 sz = np.linalg.norm(sz) 3396 self.camera.SetViewUp([0, 1, 0]) 3397 self.camera.SetPosition(cm + sz) 3398 elif viewup == "z": 3399 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3400 self.camera.SetViewUp([0, 0, 1]) 3401 self.camera.SetPosition(cm + 2 * sz) 3402 elif utils.is_sequence(viewup): 3403 sz = np.linalg.norm(sz) 3404 self.camera.SetViewUp(viewup) 3405 cpos = np.cross([0, 1, 0], viewup) 3406 self.camera.SetPosition(cm - 2 * sz * cpos) 3407 3408 self.renderer.ResetCameraClippingRange() 3409 3410 self.initialize_interactor() 3411 3412 if vedo.settings.immediate_rendering: 3413 self.window.Render() ##################### <-------------- Render 3414 3415 if self.interactor: # can be offscreen or not the vtk backend.. 3416 3417 self.window.SetWindowName(self.title) 3418 3419 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3420 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3421 # print(pic.dataset)# Array 0 name PNGImage 3422 # self.window.SetIcon(pic.dataset) 3423 3424 try: 3425 # Needs "pip install pyobjc" on Mac OSX 3426 if ( 3427 self._cocoa_initialized is False 3428 and "Darwin" in vedo.sys_platform 3429 and not self.offscreen 3430 ): 3431 self._cocoa_initialized = True 3432 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3433 pid = os.getpid() 3434 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3435 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3436 except: 3437 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3438 pass 3439 3440 # Set the interaction style 3441 if mode is not None: 3442 self.user_mode(mode) 3443 if self.qt_widget and mode is None: 3444 self.user_mode(0) 3445 3446 if screenshot: 3447 self.screenshot(screenshot) 3448 3449 if self._interactive: 3450 self.interactor.Start() 3451 if self._must_close_now: 3452 self.interactor.GetRenderWindow().Finalize() 3453 self.interactor.TerminateApp() 3454 self.camera = None 3455 self.renderer = None 3456 self.renderers = [] 3457 self.window = None 3458 self.interactor = None 3459 return self 3460 3461 if rate: 3462 if self.clock is None: # set clock and limit rate 3463 self._clockt0 = time.time() 3464 self.clock = 0.0 3465 else: 3466 t = time.time() - self._clockt0 3467 elapsed = t - self.clock 3468 mint = 1.0 / rate 3469 if elapsed < mint: 3470 time.sleep(mint - elapsed) 3471 self.clock = time.time() - self._clockt0 3472 3473 # 2d #################################################################### 3474 if vedo.settings.default_backend in ["2d"]: 3475 return backends.get_notebook_backend() 3476 ######################################################################### 3477 3478 return self 3479 3480 3481 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3482 """Add a draggable inset space into a renderer. 3483 3484 Arguments: 3485 at : (int) 3486 specify the renderer number 3487 pos : (list) 3488 icon position in the range [1-4] indicating one of the 4 corners, 3489 or it can be a tuple (x,y) as a fraction of the renderer size. 3490 size : (float) 3491 size of the square inset 3492 draggable : (bool) 3493 if True the subrenderer space can be dragged around 3494 c : (color) 3495 color of the inset frame when dragged 3496 3497 Examples: 3498 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3499 3500 ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg) 3501 """ 3502 if not self.interactor: 3503 return None 3504 3505 if not self.renderer: 3506 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3507 return None 3508 3509 options = dict(options) 3510 pos = options.pop("pos", 0) 3511 size = options.pop("size", 0.1) 3512 c = options.pop("c", "lb") 3513 at = options.pop("at", None) 3514 draggable = options.pop("draggable", True) 3515 3516 r, g, b = vedo.get_color(c) 3517 widget = vtki.vtkOrientationMarkerWidget() 3518 widget.SetOutlineColor(r, g, b) 3519 if len(objects) == 1: 3520 widget.SetOrientationMarker(objects[0].actor) 3521 else: 3522 widget.SetOrientationMarker(vedo.Assembly(objects)) 3523 3524 widget.SetInteractor(self.interactor) 3525 3526 if utils.is_sequence(pos): 3527 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3528 else: 3529 if pos < 2: 3530 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3531 elif pos == 2: 3532 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3533 elif pos == 3: 3534 widget.SetViewport(0, 0, size * 2, size * 2) 3535 elif pos == 4: 3536 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3537 widget.EnabledOn() 3538 widget.SetInteractive(draggable) 3539 if at is not None and at < len(self.renderers): 3540 widget.SetCurrentRenderer(self.renderers[at]) 3541 else: 3542 widget.SetCurrentRenderer(self.renderer) 3543 self.widgets.append(widget) 3544 return widget 3545 3546 def clear(self, at=None, deep=False) -> Self: 3547 """Clear the scene from all meshes and volumes.""" 3548 if at is not None: 3549 renderer = self.renderers[at] 3550 else: 3551 renderer = self.renderer 3552 if not renderer: 3553 return self 3554 3555 if deep: 3556 renderer.RemoveAllViewProps() 3557 else: 3558 for ob in set( 3559 self.get_meshes() 3560 + self.get_volumes() 3561 + self.objects 3562 + self.axes_instances 3563 ): 3564 if isinstance(ob, vedo.shapes.Text2D): 3565 continue 3566 self.remove(ob) 3567 try: 3568 if ob.scalarbar: 3569 self.remove(ob.scalarbar) 3570 except AttributeError: 3571 pass 3572 return self 3573 3574 def break_interaction(self) -> Self: 3575 """Break window interaction and return to the python execution flow""" 3576 if self.interactor: 3577 self.check_actors_trasform() 3578 self.interactor.ExitCallback() 3579 return self 3580 3581 def freeze(self, value=True) -> Self: 3582 """Freeze the current renderer. Use this with `sharecam=False`.""" 3583 if not self.interactor: 3584 return self 3585 if not self.renderer: 3586 return self 3587 self.renderer.SetInteractive(not value) 3588 return self 3589 3590 def user_mode(self, mode) -> Self: 3591 """ 3592 Modify the user interaction mode. 3593 3594 Examples: 3595 ```python 3596 from vedo import * 3597 mode = interactor_modes.MousePan() 3598 mesh = Mesh(dataurl+"cow.vtk") 3599 plt = Plotter().user_mode(mode) 3600 plt.show(mesh, axes=1) 3601 ``` 3602 See also: 3603 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3604 """ 3605 if not self.interactor: 3606 return self 3607 3608 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3609 # print("Current style:", curr_style) 3610 if curr_style.endswith("Actor"): 3611 self.check_actors_trasform() 3612 3613 if isinstance(mode, (str, int)): 3614 # Set the style of interaction 3615 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3616 if mode in (0, "TrackballCamera"): 3617 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3618 self.interactor.RemoveObservers("CharEvent") 3619 elif mode in (1, "TrackballActor"): 3620 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3621 elif mode in (2, "JoystickCamera"): 3622 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3623 elif mode in (3, "JoystickActor"): 3624 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3625 elif mode in (4, "Flight"): 3626 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3627 elif mode in (5, "RubberBand2D"): 3628 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3629 elif mode in (6, "RubberBand3D"): 3630 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3631 elif mode in (7, "RubberBandZoom"): 3632 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3633 elif mode in (8, "Terrain"): 3634 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3635 elif mode in (9, "Unicam"): 3636 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3637 elif mode in (10, "Image", "image", "2d"): 3638 astyle = vtki.new("InteractorStyleImage") 3639 astyle.SetInteractionModeToImage3D() 3640 self.interactor.SetInteractorStyle(astyle) 3641 else: 3642 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3643 3644 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3645 # set a custom interactor style 3646 if hasattr(mode, "interactor"): 3647 mode.interactor = self.interactor 3648 mode.renderer = self.renderer # type: ignore 3649 mode.SetInteractor(self.interactor) 3650 mode.SetDefaultRenderer(self.renderer) 3651 self.interactor.SetInteractorStyle(mode) 3652 3653 return self 3654 3655 def close(self) -> Self: 3656 """Close the plotter.""" 3657 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3658 vedo.last_figure = None 3659 self.last_event = None 3660 self.sliders = [] 3661 self.buttons = [] 3662 self.widgets = [] 3663 self.hover_legends = [] 3664 self.background_renderer = None 3665 self._extralight = None 3666 3667 self.hint_widget = None 3668 self.cutter_widget = None 3669 3670 if vedo.settings.dry_run_mode >= 2: 3671 return self 3672 3673 if not hasattr(self, "window"): 3674 return self 3675 if not self.window: 3676 return self 3677 if not hasattr(self, "interactor"): 3678 return self 3679 if not self.interactor: 3680 return self 3681 3682 ################################################### 3683 try: 3684 if "Darwin" in vedo.sys_platform: 3685 self.interactor.ProcessEvents() 3686 except: 3687 pass 3688 3689 self._must_close_now = True 3690 3691 if vedo.plotter_instance == self: 3692 vedo.plotter_instance = None 3693 3694 if self.interactor and self._interactive: 3695 self.break_interaction() 3696 elif self._must_close_now: 3697 # dont call ExitCallback here 3698 if self.interactor: 3699 self.break_interaction() 3700 self.interactor.GetRenderWindow().Finalize() 3701 self.interactor.TerminateApp() 3702 self.camera = None 3703 self.renderer = None 3704 self.renderers = [] 3705 self.window = None 3706 self.interactor = None 3707 return self 3708 3709 @property 3710 def camera(self): 3711 """Return the current active camera.""" 3712 if self.renderer: 3713 return self.renderer.GetActiveCamera() 3714 3715 @camera.setter 3716 def camera(self, cam): 3717 if self.renderer: 3718 if isinstance(cam, dict): 3719 cam = utils.camera_from_dict(cam) 3720 self.renderer.SetActiveCamera(cam) 3721 3722 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3723 """ 3724 Take a screenshot of the Plotter window. 3725 3726 Arguments: 3727 scale : (int) 3728 set image magnification as an integer multiplicating factor 3729 asarray : (bool) 3730 return a numpy array of the image instead of writing a file 3731 3732 Warning: 3733 If you get black screenshots try to set `interactive=False` in `show()` 3734 then call `screenshot()` and `plt.interactive()` afterwards. 3735 3736 Example: 3737 ```py 3738 from vedo import * 3739 sphere = Sphere().linewidth(1) 3740 plt = show(sphere, interactive=False) 3741 plt.screenshot('image.png') 3742 plt.interactive() 3743 plt.close() 3744 ``` 3745 3746 Example: 3747 ```py 3748 from vedo import * 3749 sphere = Sphere().linewidth(1) 3750 plt = show(sphere, interactive=False) 3751 plt.screenshot('anotherimage.png') 3752 plt.interactive() 3753 plt.close() 3754 ``` 3755 """ 3756 return vedo.file_io.screenshot(filename, scale, asarray) 3757 3758 def toimage(self, scale=1) -> "vedo.image.Image": 3759 """ 3760 Generate a `Image` object from the current rendering window. 3761 3762 Arguments: 3763 scale : (int) 3764 set image magnification as an integer multiplicating factor 3765 """ 3766 if vedo.settings.screeshot_large_image: 3767 w2if = vtki.new("RenderLargeImage") 3768 w2if.SetInput(self.renderer) 3769 w2if.SetMagnification(scale) 3770 else: 3771 w2if = vtki.new("WindowToImageFilter") 3772 w2if.SetInput(self.window) 3773 if hasattr(w2if, "SetScale"): 3774 w2if.SetScale(scale, scale) 3775 if vedo.settings.screenshot_transparent_background: 3776 w2if.SetInputBufferTypeToRGBA() 3777 w2if.ReadFrontBufferOff() # read from the back buffer 3778 w2if.Update() 3779 return vedo.image.Image(w2if.GetOutput()) 3780 3781 def export(self, filename="scene.npz", binary=False) -> Self: 3782 """ 3783 Export scene to file to HTML, X3D or Numpy file. 3784 3785 Examples: 3786 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3787 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3788 """ 3789 vedo.file_io.export_window(filename, binary=binary) 3790 return self 3791 3792 def color_picker(self, xy, verbose=False): 3793 """Pick color of specific (x,y) pixel on the screen.""" 3794 w2if = vtki.new("WindowToImageFilter") 3795 w2if.SetInput(self.window) 3796 w2if.ReadFrontBufferOff() 3797 w2if.Update() 3798 nx, ny = self.window.GetSize() 3799 varr = w2if.GetOutput().GetPointData().GetScalars() 3800 3801 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3802 x, y = int(xy[0]), int(xy[1]) 3803 if y < ny and x < nx: 3804 3805 rgb = arr[y, x] 3806 3807 if verbose: 3808 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3809 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3810 vedo.printc("█", c=[0, rgb[1], 0], end="") 3811 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3812 vedo.printc("] = ", end="") 3813 cnm = vedo.get_color_name(rgb) 3814 if np.sum(rgb) < 150: 3815 vedo.printc( 3816 rgb.tolist(), 3817 vedo.colors.rgb2hex(np.array(rgb) / 255), 3818 c="w", 3819 bc=rgb, 3820 invert=1, 3821 end="", 3822 ) 3823 vedo.printc(" -> " + cnm, invert=1, c="w") 3824 else: 3825 vedo.printc( 3826 rgb.tolist(), 3827 vedo.colors.rgb2hex(np.array(rgb) / 255), 3828 c=rgb, 3829 end="", 3830 ) 3831 vedo.printc(" -> " + cnm, c=cnm) 3832 3833 return rgb 3834 3835 return None 3836 3837 ####################################################################### 3838 def _default_mouseleftclick(self, iren, event) -> None: 3839 x, y = iren.GetEventPosition() 3840 renderer = iren.FindPokedRenderer(x, y) 3841 picker = vtki.vtkPropPicker() 3842 picker.PickProp(x, y, renderer) 3843 3844 self.renderer = renderer 3845 3846 clicked_actor = picker.GetActor() 3847 # clicked_actor2D = picker.GetActor2D() 3848 3849 # print('_default_mouseleftclick mouse at', x, y) 3850 # print("picked Volume:", [picker.GetVolume()]) 3851 # print("picked Actor2D:", [picker.GetActor2D()]) 3852 # print("picked Assembly:", [picker.GetAssembly()]) 3853 # print("picked Prop3D:", [picker.GetProp3D()]) 3854 3855 if not clicked_actor: 3856 clicked_actor = picker.GetAssembly() 3857 3858 if not clicked_actor: 3859 clicked_actor = picker.GetProp3D() 3860 3861 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3862 return 3863 3864 self.picked3d = picker.GetPickPosition() 3865 self.picked2d = np.array([x, y]) 3866 3867 if not clicked_actor: 3868 return 3869 3870 self.justremoved = None 3871 self.clicked_actor = clicked_actor 3872 3873 try: # might not be a vedo obj 3874 self.clicked_object = clicked_actor.retrieve_object() 3875 # save this info in the object itself 3876 self.clicked_object.picked3d = self.picked3d 3877 self.clicked_object.picked2d = self.picked2d 3878 except AttributeError: 3879 pass 3880 3881 # ----------- 3882 # if "Histogram1D" in picker.GetAssembly().__class__.__name__: 3883 # histo = picker.GetAssembly() 3884 # if histo.verbose: 3885 # x = self.picked3d[0] 3886 # idx = np.digitize(x, histo.edges) - 1 3887 # f = histo.frequencies[idx] 3888 # cn = histo.centers[idx] 3889 # vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3890 3891 ####################################################################### 3892 def _default_keypress(self, iren, event) -> None: 3893 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3894 3895 key = iren.GetKeySym() 3896 3897 if "_L" in key or "_R" in key: 3898 return 3899 3900 if iren.GetShiftKey(): 3901 key = key.upper() 3902 3903 if iren.GetControlKey(): 3904 key = "Ctrl+" + key 3905 3906 if iren.GetAltKey(): 3907 key = "Alt+" + key 3908 3909 ####################################################### 3910 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3911 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3912 # iren.GetKeyCode(), iren.GetRepeatCount()) 3913 ####################################################### 3914 3915 x, y = iren.GetEventPosition() 3916 renderer = iren.FindPokedRenderer(x, y) 3917 3918 if key in ["q", "Return"]: 3919 self.break_interaction() 3920 return 3921 3922 elif key in ["Ctrl+q", "Ctrl+w", "Escape"]: 3923 self.close() 3924 return 3925 3926 elif key == "F1": 3927 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3928 self.break_interaction() 3929 sys.exit(0) 3930 3931 elif key == "Down": 3932 if self.clicked_object and self.clicked_object in self.get_meshes(): 3933 self.clicked_object.alpha(0.02) 3934 if hasattr(self.clicked_object, "properties_backface"): 3935 bfp = self.clicked_actor.GetBackfaceProperty() 3936 self.clicked_object.properties_backface = bfp # save it 3937 self.clicked_actor.SetBackfaceProperty(None) 3938 else: 3939 for obj in self.get_meshes(): 3940 if obj: 3941 obj.alpha(0.02) 3942 bfp = obj.actor.GetBackfaceProperty() 3943 if bfp and hasattr(obj, "properties_backface"): 3944 obj.properties_backface = bfp 3945 obj.actor.SetBackfaceProperty(None) 3946 3947 elif key == "Left": 3948 if self.clicked_object and self.clicked_object in self.get_meshes(): 3949 ap = self.clicked_object.properties 3950 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3951 ap.SetOpacity(aal) 3952 bfp = self.clicked_actor.GetBackfaceProperty() 3953 if bfp and hasattr(self.clicked_object, "properties_backface"): 3954 self.clicked_object.properties_backface = bfp 3955 self.clicked_actor.SetBackfaceProperty(None) 3956 else: 3957 for a in self.get_meshes(): 3958 if a: 3959 ap = a.properties 3960 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3961 ap.SetOpacity(aal) 3962 bfp = a.actor.GetBackfaceProperty() 3963 if bfp and hasattr(a, "properties_backface"): 3964 a.properties_backface = bfp 3965 a.actor.SetBackfaceProperty(None) 3966 3967 elif key == "Right": 3968 if self.clicked_object and self.clicked_object in self.get_meshes(): 3969 ap = self.clicked_object.properties 3970 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3971 ap.SetOpacity(aal) 3972 if ( 3973 aal == 1 3974 and hasattr(self.clicked_object, "properties_backface") 3975 and self.clicked_object.properties_backface 3976 ): 3977 # put back 3978 self.clicked_actor.SetBackfaceProperty( 3979 self.clicked_object.properties_backface) 3980 else: 3981 for a in self.get_meshes(): 3982 if a: 3983 ap = a.properties 3984 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3985 ap.SetOpacity(aal) 3986 if aal == 1 and hasattr(a, "properties_backface") and a.properties_backface: 3987 a.actor.SetBackfaceProperty(a.properties_backface) 3988 3989 elif key == "Up": 3990 if self.clicked_object and self.clicked_object in self.get_meshes(): 3991 self.clicked_object.properties.SetOpacity(1) 3992 if hasattr(self.clicked_object, "properties_backface") and self.clicked_object.properties_backface: 3993 self.clicked_object.actor.SetBackfaceProperty(self.clicked_object.properties_backface) 3994 else: 3995 for a in self.get_meshes(): 3996 if a: 3997 a.properties.SetOpacity(1) 3998 if hasattr(a, "properties_backface") and a.properties_backface: 3999 a.actor.SetBackfaceProperty(a.properties_backface) 4000 4001 elif key == "P": 4002 if self.clicked_object and self.clicked_object in self.get_meshes(): 4003 objs = [self.clicked_object] 4004 else: 4005 objs = self.get_meshes() 4006 for ia in objs: 4007 try: 4008 ps = ia.properties.GetPointSize() 4009 if ps > 1: 4010 ia.properties.SetPointSize(ps - 1) 4011 ia.properties.SetRepresentationToPoints() 4012 except AttributeError: 4013 pass 4014 4015 elif key == "p": 4016 if self.clicked_object and self.clicked_object in self.get_meshes(): 4017 objs = [self.clicked_object] 4018 else: 4019 objs = self.get_meshes() 4020 for ia in objs: 4021 try: 4022 ps = ia.properties.GetPointSize() 4023 ia.properties.SetPointSize(ps + 2) 4024 ia.properties.SetRepresentationToPoints() 4025 except AttributeError: 4026 pass 4027 4028 elif key == "U": 4029 pval = renderer.GetActiveCamera().GetParallelProjection() 4030 renderer.GetActiveCamera().SetParallelProjection(not pval) 4031 if pval: 4032 renderer.ResetCamera() 4033 4034 elif key == "r": 4035 renderer.ResetCamera() 4036 4037 elif key == "h": 4038 msg = f" vedo {vedo.__version__}" 4039 msg += f" | vtk {vtki.vtkVersion().GetVTKVersion()}" 4040 msg += f" | numpy {np.__version__}" 4041 msg += f" | python {sys.version_info[0]}.{sys.version_info[1]}, press: " 4042 vedo.printc(msg.ljust(75), invert=True) 4043 msg = ( 4044 " i print info about the last clicked object \n" 4045 " I print color of the pixel under the mouse \n" 4046 " Y show the pipeline for this object as a graph \n" 4047 " <- -> use arrows to reduce/increase opacity \n" 4048 " x toggle mesh visibility \n" 4049 " w toggle wireframe/surface style \n" 4050 " l toggle surface edges visibility \n" 4051 " p/P hide surface faces and show only points \n" 4052 " 1-3 cycle surface color (2=light, 3=dark) \n" 4053 " 4 cycle color map (press shift-4 to go back) \n" 4054 " 5-6 cycle point-cell arrays (shift to go back) \n" 4055 " 7-8 cycle background and gradient color \n" 4056 " 09+- cycle axes styles (on keypad, or press +/-) \n" 4057 " k cycle available lighting styles \n" 4058 " K toggle shading as flat or phong \n" 4059 " A toggle anti-aliasing \n" 4060 " D toggle depth-peeling (for transparencies) \n" 4061 " U toggle perspective/parallel projection \n" 4062 " o/O toggle extra light to scene and rotate it \n" 4063 " a toggle interaction to Actor Mode \n" 4064 " n toggle surface normals \n" 4065 " r reset camera position \n" 4066 " R reset camera to the closest orthogonal view \n" 4067 " . fly camera to the last clicked point \n" 4068 " C print the current camera parameters state \n" 4069 " X invoke a cutter widget tool \n" 4070 " S save a screenshot of the current scene \n" 4071 " E/F export 3D scene to numpy file or X3D \n" 4072 " q return control to python script \n" 4073 " Esc abort execution and exit python kernel " 4074 ) 4075 vedo.printc(msg, dim=True, italic=True, bold=True) 4076 vedo.printc( 4077 " Check out the documentation at: https://vedo.embl.es ".ljust(75), 4078 invert=True, 4079 bold=True, 4080 ) 4081 return 4082 4083 elif key == "a": 4084 cur = iren.GetInteractorStyle() 4085 if isinstance(cur, vtki.get_class("InteractorStyleTrackballCamera")): 4086 msg = "Interactor style changed to TrackballActor\n" 4087 msg += " you can now move and rotate individual meshes:\n" 4088 msg += " press X twice to save the repositioned mesh\n" 4089 msg += " press 'a' to go back to normal style" 4090 vedo.printc(msg) 4091 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 4092 else: 4093 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 4094 return 4095 4096 elif key == "A": # toggle antialiasing 4097 msam = self.window.GetMultiSamples() 4098 if not msam: 4099 self.window.SetMultiSamples(16) 4100 else: 4101 self.window.SetMultiSamples(0) 4102 msam = self.window.GetMultiSamples() 4103 if msam: 4104 vedo.printc(f"Antialiasing set to {msam} samples", c=bool(msam)) 4105 else: 4106 vedo.printc("Antialiasing disabled", c=bool(msam)) 4107 4108 elif key == "D": # toggle depthpeeling 4109 udp = not renderer.GetUseDepthPeeling() 4110 renderer.SetUseDepthPeeling(udp) 4111 # self.renderer.SetUseDepthPeelingForVolumes(udp) 4112 if udp: 4113 self.window.SetAlphaBitPlanes(1) 4114 renderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 4115 renderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 4116 self.interactor.Render() 4117 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 4118 rnr = self.renderers.index(renderer) 4119 vedo.printc(f"Depth peeling set to {udp} for renderer nr.{rnr}", c=udp) 4120 if not wasUsed and udp: 4121 vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True) 4122 return 4123 4124 elif key == "period": 4125 if self.picked3d: 4126 self.fly_to(self.picked3d) 4127 return 4128 4129 elif key == "S": 4130 fname = "screenshot.png" 4131 i = 1 4132 while os.path.isfile(fname): 4133 fname = f"screenshot{i}.png" 4134 i += 1 4135 vedo.file_io.screenshot(fname) 4136 vedo.printc(rf":camera: Saved rendering window to {fname}", c="b") 4137 return 4138 4139 elif key == "C": 4140 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 4141 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 4142 # This could happen for plotting geological models with UTM coordinate systems 4143 cam = renderer.GetActiveCamera() 4144 vedo.printc("\n###################################################", c="y") 4145 vedo.printc("## Template python code to position this camera: ##", c="y") 4146 vedo.printc("cam = dict(", c="y") 4147 vedo.printc(" pos=" + utils.precision(cam.GetPosition(), 6) + ",", c="y") 4148 vedo.printc(" focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y") 4149 vedo.printc(" viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y") 4150 vedo.printc(" roll=" + utils.precision(cam.GetRoll(), 6) + ",", c="y") 4151 if cam.GetParallelProjection(): 4152 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 4153 else: 4154 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 4155 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 4156 vedo.printc(')', c='y') 4157 vedo.printc('show(mymeshes, camera=cam)', c='y') 4158 vedo.printc('###################################################', c='y') 4159 return 4160 4161 elif key == "R": 4162 self.reset_viewup() 4163 4164 elif key == "w": 4165 try: 4166 if self.clicked_object.properties.GetRepresentation() == 1: # toggle 4167 self.clicked_object.properties.SetRepresentationToSurface() 4168 else: 4169 self.clicked_object.properties.SetRepresentationToWireframe() 4170 except AttributeError: 4171 pass 4172 4173 elif key == "1": 4174 try: 4175 self._icol += 1 4176 self.clicked_object.mapper.ScalarVisibilityOff() 4177 pal = vedo.colors.palettes[vedo.settings.palette % len(vedo.colors.palettes)] 4178 self.clicked_object.c(pal[(self._icol) % 10]) 4179 self.remove(self.clicked_object.scalarbar) 4180 except AttributeError: 4181 pass 4182 4183 elif key == "2": # dark colors 4184 try: 4185 bsc = ["k1", "k2", "k3", "k4", 4186 "b1", "b2", "b3", "b4", 4187 "p1", "p2", "p3", "p4", 4188 "g1", "g2", "g3", "g4", 4189 "r1", "r2", "r3", "r4", 4190 "o1", "o2", "o3", "o4", 4191 "y1", "y2", "y3", "y4"] 4192 self._icol += 1 4193 if self.clicked_object: 4194 self.clicked_object.mapper.ScalarVisibilityOff() 4195 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4196 self.clicked_object.c(newcol) 4197 self.remove(self.clicked_object.scalarbar) 4198 except AttributeError: 4199 pass 4200 4201 elif key == "3": # light colors 4202 try: 4203 bsc = ["k6", "k7", "k8", "k9", 4204 "b6", "b7", "b8", "b9", 4205 "p6", "p7", "p8", "p9", 4206 "g6", "g7", "g8", "g9", 4207 "r6", "r7", "r8", "r9", 4208 "o6", "o7", "o8", "o9", 4209 "y6", "y7", "y8", "y9"] 4210 self._icol += 1 4211 if self.clicked_object: 4212 self.clicked_object.mapper.ScalarVisibilityOff() 4213 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4214 self.clicked_object.c(newcol) 4215 self.remove(self.clicked_object.scalarbar) 4216 except AttributeError: 4217 pass 4218 4219 elif key == "4": # cmap name cycle 4220 ob = self.clicked_object 4221 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4222 return 4223 if not ob.mapper.GetScalarVisibility(): 4224 return 4225 onwhat = ob.mapper.GetScalarModeAsString() # UsePointData/UseCellData 4226 4227 cmap_names = [ 4228 "Accent", "Paired", 4229 "rainbow", "rainbow_r", 4230 "Spectral", "Spectral_r", 4231 "gist_ncar", "gist_ncar_r", 4232 "viridis", "viridis_r", 4233 "hot", "hot_r", 4234 "terrain", "ocean", 4235 "coolwarm", "seismic", "PuOr", "RdYlGn", 4236 ] 4237 try: 4238 i = cmap_names.index(ob._cmap_name) 4239 if iren.GetShiftKey(): 4240 i -= 1 4241 else: 4242 i += 1 4243 if i >= len(cmap_names): 4244 i = 0 4245 if i < 0: 4246 i = len(cmap_names) - 1 4247 except ValueError: 4248 i = 0 4249 4250 ob._cmap_name = cmap_names[i] 4251 ob.cmap(ob._cmap_name, on=onwhat) 4252 if ob.scalarbar: 4253 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4254 self.remove(ob.scalarbar) 4255 title = ob.scalarbar.GetTitle() 4256 ob.add_scalarbar(title=title) 4257 self.add(ob.scalarbar).render() 4258 elif isinstance(ob.scalarbar, vedo.Assembly): 4259 self.remove(ob.scalarbar) 4260 ob.add_scalarbar3d(title=ob._cmap_name) 4261 self.add(ob.scalarbar) 4262 4263 vedo.printc( 4264 f"Name:'{ob.name}'," if ob.name else "", 4265 f"range:{utils.precision(ob.mapper.GetScalarRange(),3)},", 4266 f"colormap:'{ob._cmap_name}'", c="g", bold=False, 4267 ) 4268 4269 elif key == "5": # cycle pointdata array 4270 ob = self.clicked_object 4271 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4272 return 4273 4274 arrnames = ob.pointdata.keys() 4275 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4276 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4277 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4278 if len(arrnames) == 0: 4279 return 4280 ob.mapper.SetScalarVisibility(1) 4281 4282 if not ob._cmap_name: 4283 ob._cmap_name = "rainbow" 4284 4285 try: 4286 curr_name = ob.dataset.GetPointData().GetScalars().GetName() 4287 i = arrnames.index(curr_name) 4288 if "normals" in curr_name.lower(): 4289 return 4290 if iren.GetShiftKey(): 4291 i -= 1 4292 else: 4293 i += 1 4294 if i >= len(arrnames): 4295 i = 0 4296 if i < 0: 4297 i = len(arrnames) - 1 4298 except (ValueError, AttributeError): 4299 i = 0 4300 4301 ob.cmap(ob._cmap_name, arrnames[i], on="points") 4302 if ob.scalarbar: 4303 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4304 self.remove(ob.scalarbar) 4305 title = ob.scalarbar.GetTitle() 4306 ob.scalarbar = None 4307 ob.add_scalarbar(title=arrnames[i]) 4308 self.add(ob.scalarbar) 4309 elif isinstance(ob.scalarbar, vedo.Assembly): 4310 self.remove(ob.scalarbar) 4311 ob.scalarbar = None 4312 ob.add_scalarbar3d(title=arrnames[i]) 4313 self.add(ob.scalarbar) 4314 else: 4315 vedo.printc( 4316 f"Name:'{ob.name}'," if ob.name else "", 4317 f"active pointdata array: '{arrnames[i]}'", 4318 c="g", bold=False, 4319 ) 4320 4321 elif key == "6": # cycle celldata array 4322 ob = self.clicked_object 4323 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4324 return 4325 4326 arrnames = ob.celldata.keys() 4327 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4328 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4329 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4330 if len(arrnames) == 0: 4331 return 4332 ob.mapper.SetScalarVisibility(1) 4333 4334 if not ob._cmap_name: 4335 ob._cmap_name = "rainbow" 4336 4337 try: 4338 curr_name = ob.dataset.GetCellData().GetScalars().GetName() 4339 i = arrnames.index(curr_name) 4340 if "normals" in curr_name.lower(): 4341 return 4342 if iren.GetShiftKey(): 4343 i -= 1 4344 else: 4345 i += 1 4346 if i >= len(arrnames): 4347 i = 0 4348 if i < 0: 4349 i = len(arrnames) - 1 4350 except (ValueError, AttributeError): 4351 i = 0 4352 4353 ob.cmap(ob._cmap_name, arrnames[i], on="cells") 4354 if ob.scalarbar: 4355 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4356 self.remove(ob.scalarbar) 4357 title = ob.scalarbar.GetTitle() 4358 ob.scalarbar = None 4359 ob.add_scalarbar(title=arrnames[i]) 4360 self.add(ob.scalarbar) 4361 elif isinstance(ob.scalarbar, vedo.Assembly): 4362 self.remove(ob.scalarbar) 4363 ob.scalarbar = None 4364 ob.add_scalarbar3d(title=arrnames[i]) 4365 self.add(ob.scalarbar) 4366 else: 4367 vedo.printc( 4368 f"Name:'{ob.name}'," if ob.name else "", 4369 f"active celldata array: '{arrnames[i]}'", 4370 c="g", bold=False, 4371 ) 4372 4373 elif key == "7": 4374 bgc = np.array(renderer.GetBackground()).sum() / 3 4375 if bgc <= 0: 4376 bgc = 0.223 4377 elif 0 < bgc < 1: 4378 bgc = 1 4379 else: 4380 bgc = 0 4381 renderer.SetBackground(bgc, bgc, bgc) 4382 4383 elif key == "8": 4384 bg2cols = [ 4385 "lightyellow", 4386 "darkseagreen", 4387 "palegreen", 4388 "steelblue", 4389 "lightblue", 4390 "cadetblue", 4391 "lavender", 4392 "white", 4393 "blackboard", 4394 "black", 4395 ] 4396 bg2name = vedo.get_color_name(renderer.GetBackground2()) 4397 if bg2name in bg2cols: 4398 idx = bg2cols.index(bg2name) 4399 else: 4400 idx = 4 4401 if idx is not None: 4402 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 4403 if not bg2name_next: 4404 renderer.GradientBackgroundOff() 4405 else: 4406 renderer.GradientBackgroundOn() 4407 renderer.SetBackground2(vedo.get_color(bg2name_next)) 4408 4409 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 4410 i = self.renderers.index(renderer) 4411 try: 4412 self.axes_instances[i].EnabledOff() 4413 self.axes_instances[i].SetInteractor(None) 4414 except AttributeError: 4415 # print("Cannot remove widget", [self.axes_instances[i]]) 4416 try: 4417 self.remove(self.axes_instances[i]) 4418 except: 4419 print("Cannot remove axes", [self.axes_instances[i]]) 4420 return 4421 self.axes_instances[i] = None 4422 4423 if not self.axes: 4424 self.axes = 0 4425 if isinstance(self.axes, dict): 4426 self.axes = 1 4427 4428 if key in ["minus", "KP_Subtract"]: 4429 if not self.camera.GetParallelProjection() and self.axes == 0: 4430 self.axes -= 1 # jump ruler doesnt make sense in perspective mode 4431 bns = self.renderer.ComputeVisiblePropBounds() 4432 addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None, bounds=bns) 4433 else: 4434 if not self.camera.GetParallelProjection() and self.axes == 12: 4435 self.axes += 1 # jump ruler doesnt make sense in perspective mode 4436 bns = self.renderer.ComputeVisiblePropBounds() 4437 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns) 4438 self.render() 4439 4440 elif "KP_" in key or key in [ 4441 "Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior" 4442 ]: 4443 asso = { # change axes style 4444 "KP_Insert": 0, "KP_0": 0, "Insert": 0, 4445 "KP_End": 1, "KP_1": 1, "End": 1, 4446 "KP_Down": 2, "KP_2": 2, "Down": 2, 4447 "KP_Next": 3, "KP_3": 3, "Next": 3, 4448 "KP_Left": 4, "KP_4": 4, "Left": 4, 4449 "KP_Begin": 5, "KP_5": 5, "Begin": 5, 4450 "KP_Right": 6, "KP_6": 6, "Right": 6, 4451 "KP_Home": 7, "KP_7": 7, "Home": 7, 4452 "KP_Up": 8, "KP_8": 8, "Up": 8, 4453 "Prior": 9, # on windows OS 4454 } 4455 clickedr = self.renderers.index(renderer) 4456 if key in asso: 4457 if self.axes_instances[clickedr]: 4458 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 4459 self.axes_instances[clickedr].EnabledOff() 4460 else: 4461 try: 4462 renderer.RemoveActor(self.axes_instances[clickedr]) 4463 except: 4464 pass 4465 self.axes_instances[clickedr] = None 4466 bounds = renderer.ComputeVisiblePropBounds() 4467 addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds) 4468 self.interactor.Render() 4469 4470 if key == "O": 4471 renderer.RemoveLight(self._extralight) 4472 self._extralight = None 4473 4474 elif key == "o": 4475 vbb, sizes, _, _ = addons.compute_visible_bounds() 4476 cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2) 4477 if not self._extralight: 4478 vup = renderer.GetActiveCamera().GetViewUp() 4479 pos = cm + utils.vector(vup) * utils.mag(sizes) 4480 self._extralight = addons.Light(pos, focal_point=cm, intensity=0.4) 4481 renderer.AddLight(self._extralight) 4482 vedo.printc("Press 'o' again to rotate light source, or 'O' to remove it.", c='y') 4483 else: 4484 cpos = utils.vector(self._extralight.GetPosition()) 4485 x, y, z = self._extralight.GetPosition() - cm 4486 r, th, ph = transformations.cart2spher(x, y, z) 4487 th += 0.2 4488 if th > np.pi: 4489 th = np.random.random() * np.pi / 2 4490 ph += 0.3 4491 cpos = transformations.spher2cart(r, th, ph).T + cm 4492 self._extralight.SetPosition(cpos) 4493 4494 elif key == "l": 4495 if self.clicked_object in self.get_meshes(): 4496 objs = [self.clicked_object] 4497 else: 4498 objs = self.get_meshes() 4499 for ia in objs: 4500 try: 4501 ev = ia.properties.GetEdgeVisibility() 4502 ia.properties.SetEdgeVisibility(not ev) 4503 ia.properties.SetRepresentationToSurface() 4504 ia.properties.SetLineWidth(0.1) 4505 except AttributeError: 4506 pass 4507 4508 elif key == "k": # lightings 4509 if self.clicked_object in self.get_meshes(): 4510 objs = [self.clicked_object] 4511 else: 4512 objs = self.get_meshes() 4513 shds = ("default", "metallic", "plastic", "shiny", "glossy", "off") 4514 for ia in objs: 4515 try: 4516 lnr = (ia._ligthingnr + 1) % 6 4517 ia.lighting(shds[lnr]) 4518 ia._ligthingnr = lnr 4519 except AttributeError: 4520 pass 4521 4522 elif key == "K": # shading 4523 if self.clicked_object in self.get_meshes(): 4524 objs = [self.clicked_object] 4525 else: 4526 objs = self.get_meshes() 4527 for ia in objs: 4528 if isinstance(ia, vedo.Mesh): 4529 ia.compute_normals(cells=False) 4530 intrp = ia.properties.GetInterpolation() 4531 if intrp > 0: 4532 ia.properties.SetInterpolation(0) # flat 4533 else: 4534 ia.properties.SetInterpolation(2) # phong 4535 4536 elif key == "n": # show normals to an actor 4537 self.remove("added_auto_normals") 4538 if self.clicked_object in self.get_meshes(): 4539 if self.clicked_actor.GetPickable(): 4540 norml = vedo.shapes.NormalLines(self.clicked_object) 4541 norml.name = "added_auto_normals" 4542 self.add(norml) 4543 4544 elif key == "x": 4545 if self.justremoved is None: 4546 if self.clicked_object in self.get_meshes() or isinstance( 4547 self.clicked_object, vtki.vtkAssembly 4548 ): 4549 self.justremoved = self.clicked_actor 4550 self.renderer.RemoveActor(self.clicked_actor) 4551 else: 4552 self.renderer.AddActor(self.justremoved) 4553 self.justremoved = None 4554 4555 elif key == "X": 4556 if self.clicked_object: 4557 if not self.cutter_widget: 4558 self.cutter_widget = addons.BoxCutter(self.clicked_object) 4559 self.add(self.cutter_widget) 4560 vedo.printc("Press i to toggle the cutter on/off", c='g', dim=1) 4561 vedo.printc(" u to flip selection", c='g', dim=1) 4562 vedo.printc(" r to reset cutting planes", c='g', dim=1) 4563 vedo.printc(" Shift+X to close the cutter box widget", c='g', dim=1) 4564 vedo.printc(" Ctrl+S to save the cut section to file.", c='g', dim=1) 4565 else: 4566 self.remove(self.cutter_widget) 4567 self.cutter_widget = None 4568 vedo.printc("Click object and press X to open the cutter box widget.", c='g') 4569 4570 elif key == "E": 4571 vedo.printc(r":camera: Exporting 3D window to file scene.npz", c="b", end="") 4572 vedo.file_io.export_window("scene.npz") 4573 vedo.printc(", try:\n> vedo scene.npz # (this is experimental!)", c="b") 4574 return 4575 4576 elif key == "F": 4577 vedo.file_io.export_window("scene.x3d") 4578 vedo.printc(r":camera: Exporting 3D window to file", c="b", end="") 4579 vedo.file_io.export_window("scene.npz") 4580 vedo.printc(". Try:\n> firefox scene.html", c="b") 4581 4582 # elif key == "G": # not working with last version of k3d 4583 # vedo.file_io.export_window("scene.html") 4584 # vedo.printc(r":camera: Exporting K3D window to file", c="b", end="") 4585 # vedo.file_io.export_window("scene.html") 4586 # vedo.printc(". Try:\n> firefox scene.html", c="b") 4587 4588 elif key == "i": # print info 4589 if self.clicked_object: 4590 print(self.clicked_object) 4591 else: 4592 print(self) 4593 4594 elif key == "I": # print color under the mouse 4595 x, y = iren.GetEventPosition() 4596 self.color_picker([x, y], verbose=True) 4597 4598 elif key == "Y": 4599 if self.clicked_object and self.clicked_object.pipeline: 4600 self.clicked_object.pipeline.show() 4601 4602 if iren: 4603 iren.Render()
Main class to manage objects.
379 def __init__( 380 self, 381 shape=(1, 1), 382 N=None, 383 pos=(0, 0), 384 size="auto", 385 screensize="auto", 386 title="vedo", 387 bg="white", 388 bg2=None, 389 axes=None, 390 sharecam=True, 391 resetcam=True, 392 interactive=None, 393 offscreen=False, 394 qt_widget=None, 395 wx_widget=None, 396 ): 397 """ 398 Arguments: 399 shape : (str, list) 400 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 401 N : (int) 402 number of desired renderers arranged in a grid automatically. 403 pos : (list) 404 (x,y) position in pixels of top-left corner of the rendering window on the screen 405 size : (str, list) 406 size of the rendering window. If 'auto', guess it based on screensize. 407 screensize : (list) 408 physical size of the monitor screen in pixels 409 bg : (color, str) 410 background color or specify jpg image file name with path 411 bg2 : (color) 412 background color of a gradient towards the top 413 title : (str) 414 window title 415 416 axes : (int) 417 418 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 419 Check out `vedo.addons.Axes()` for the available options. 420 421 - 0, no axes 422 - 1, draw three gray grid walls 423 - 2, show cartesian axes from (0,0,0) 424 - 3, show positive range of cartesian axes from (0,0,0) 425 - 4, show a triad at bottom left 426 - 5, show a cube at bottom left 427 - 6, mark the corners of the bounding box 428 - 7, draw a 3D ruler at each side of the cartesian axes 429 - 8, show the VTK CubeAxesActor object 430 - 9, show the bounding box outLine 431 - 10, show three circles representing the maximum bounding box 432 - 11, show a large grid on the x-y plane (use with zoom=8) 433 - 12, show polar axes 434 - 13, draw a simple ruler at the bottom of the window 435 - 14: draw a camera orientation widget 436 437 sharecam : (bool) 438 if False each renderer will have an independent camera 439 interactive : (bool) 440 if True will stop after show() to allow interaction with the 3d scene 441 offscreen : (bool) 442 if True will not show the rendering window 443 qt_widget : (QVTKRenderWindowInteractor) 444 render in a Qt-Widget using an QVTKRenderWindowInteractor. 445 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 446 """ 447 vedo.plotter_instance = self 448 449 if interactive is None: 450 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 451 self._interactive = interactive 452 # print("interactive", interactive, N, shape) 453 454 self.objects = [] # list of objects to be shown 455 self.clicked_object = None # holds the object that has been clicked 456 self.clicked_actor = None # holds the actor that has been clicked 457 458 self.shape = shape # nr. of subwindows in grid 459 self.axes = axes # show axes type nr. 460 self.title = title # window title 461 self.size = size # window size 462 self.backgrcol = bg # used also by backend notebooks 463 464 self.offscreen= offscreen 465 self.resetcam = resetcam 466 self.sharecam = sharecam # share the same camera if multiple renderers 467 self.pos = pos # used by vedo.file_io 468 469 self.picker = None # hold the vtkPicker object 470 self.picked2d = None # 2d coords of a clicked point on the rendering window 471 self.picked3d = None # 3d coords of a clicked point on an actor 472 473 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 474 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 475 self.interactor = None 476 self.window = None 477 self.renderer = None 478 self.renderers = [] # list of renderers 479 480 # mostly internal stuff: 481 self.hover_legends = [] 482 self.justremoved = None 483 self.axes_instances = [] 484 self.clock = 0 485 self.sliders = [] 486 self.buttons = [] 487 self.widgets = [] 488 self.cutter_widget = None 489 self.hint_widget = None 490 self.background_renderer = None 491 self.last_event = None 492 self.skybox = None 493 self._icol = 0 494 self._clockt0 = time.time() 495 self._extralight = None 496 self._cocoa_initialized = False 497 self._cocoa_process_events = True # make one call in show() 498 self._must_close_now = False 499 500 ##################################################################### 501 if vedo.settings.default_backend == "2d": 502 self.offscreen = True 503 if self.size == "auto": 504 self.size = (800, 600) 505 506 elif vedo.settings.default_backend == "k3d": 507 if self.size == "auto": 508 self.size = (1000, 1000) 509 #################################### 510 return ############################ 511 #################################### 512 513 ############################################################# 514 if screensize == "auto": 515 screensize = (2160, 1440) # TODO: get actual screen size 516 517 # build the rendering window: 518 self.window = vtki.vtkRenderWindow() 519 520 self.window.GlobalWarningDisplayOff() 521 522 if self.title == "vedo": # check if dev version 523 if "dev" in vedo.__version__: 524 self.title = f"vedo ({vedo.__version__})" 525 self.window.SetWindowName(self.title) 526 527 # more vedo.settings 528 if vedo.settings.use_depth_peeling: 529 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 530 self.window.SetMultiSamples(vedo.settings.multi_samples) 531 532 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 533 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 534 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 535 536 537 ############################################################# 538 if N: # N = number of renderers. Find out the best 539 540 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 541 vedo.logger.warning("having set N, shape is ignored.") 542 543 x, y = screensize 544 nx = int(np.sqrt(int(N * y / x) + 1)) 545 ny = int(np.sqrt(int(N * x / y) + 1)) 546 lm = [ 547 (nx, ny), 548 (nx, ny + 1), 549 (nx - 1, ny), 550 (nx + 1, ny), 551 (nx, ny - 1), 552 (nx - 1, ny + 1), 553 (nx + 1, ny - 1), 554 (nx + 1, ny + 1), 555 (nx - 1, ny - 1), 556 ] 557 ind, minl = 0, 1000 558 for i, m in enumerate(lm): 559 l = m[0] * m[1] 560 if N <= l < minl: 561 ind = i 562 minl = l 563 shape = lm[ind] 564 565 ################################################## 566 if isinstance(shape, str): 567 568 if "|" in shape: 569 if self.size == "auto": 570 self.size = (800, 1200) 571 n = int(shape.split("|")[0]) 572 m = int(shape.split("|")[1]) 573 rangen = reversed(range(n)) 574 rangem = reversed(range(m)) 575 else: 576 if self.size == "auto": 577 self.size = (1200, 800) 578 m = int(shape.split("/")[0]) 579 n = int(shape.split("/")[1]) 580 rangen = range(n) 581 rangem = range(m) 582 583 if n >= m: 584 xsplit = m / (n + m) 585 else: 586 xsplit = 1 - n / (n + m) 587 if vedo.settings.window_splitting_position: 588 xsplit = vedo.settings.window_splitting_position 589 590 for i in rangen: 591 arenderer = vtki.vtkRenderer() 592 if "|" in shape: 593 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 594 else: 595 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 596 self.renderers.append(arenderer) 597 598 for i in rangem: 599 arenderer = vtki.vtkRenderer() 600 601 if "|" in shape: 602 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 603 else: 604 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 605 self.renderers.append(arenderer) 606 607 for r in self.renderers: 608 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 609 610 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 611 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 612 if vedo.settings.use_depth_peeling: 613 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 614 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 615 r.SetUseFXAA(vedo.settings.use_fxaa) 616 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 617 618 r.SetBackground(vedo.get_color(self.backgrcol)) 619 620 self.axes_instances.append(None) 621 622 self.shape = (n + m,) 623 624 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 625 # passing a sequence of dicts for renderers specifications 626 627 if self.size == "auto": 628 self.size = (1000, 800) 629 630 for rd in shape: 631 x0, y0 = rd["bottomleft"] 632 x1, y1 = rd["topright"] 633 bg_ = rd.pop("bg", "white") 634 bg2_ = rd.pop("bg2", None) 635 636 arenderer = vtki.vtkRenderer() 637 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 638 639 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 640 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 641 if vedo.settings.use_depth_peeling: 642 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 643 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 644 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 645 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 646 647 arenderer.SetViewport(x0, y0, x1, y1) 648 arenderer.SetBackground(vedo.get_color(bg_)) 649 if bg2_: 650 arenderer.GradientBackgroundOn() 651 arenderer.SetBackground2(vedo.get_color(bg2_)) 652 653 self.renderers.append(arenderer) 654 self.axes_instances.append(None) 655 656 self.shape = (len(shape),) 657 658 else: 659 660 if isinstance(self.size, str) and self.size == "auto": 661 # figure out a reasonable window size 662 f = 1.5 663 x, y = screensize 664 xs = y / f * shape[1] # because y<x 665 ys = y / f * shape[0] 666 if xs > x / f: # shrink 667 xs = x / f 668 ys = xs / shape[1] * shape[0] 669 if ys > y / f: 670 ys = y / f 671 xs = ys / shape[0] * shape[1] 672 self.size = (int(xs), int(ys)) 673 if shape == (1, 1): 674 self.size = (int(y / f), int(y / f)) # because y<x 675 else: 676 self.size = (self.size[0], self.size[1]) 677 678 try: 679 image_actor = None 680 bgname = str(self.backgrcol).lower() 681 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 682 self.window.SetNumberOfLayers(2) 683 self.background_renderer = vtki.vtkRenderer() 684 self.background_renderer.SetLayer(0) 685 self.background_renderer.InteractiveOff() 686 self.background_renderer.SetBackground(vedo.get_color(bg2)) 687 image_actor = vedo.Image(self.backgrcol).actor 688 self.window.AddRenderer(self.background_renderer) 689 self.background_renderer.AddActor(image_actor) 690 except AttributeError: 691 pass 692 693 for i in reversed(range(shape[0])): 694 for j in range(shape[1]): 695 arenderer = vtki.vtkRenderer() 696 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 697 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 698 699 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 700 if vedo.settings.use_depth_peeling: 701 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 702 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 703 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 704 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 705 706 if image_actor: 707 arenderer.SetLayer(1) 708 709 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 710 if bg2: 711 arenderer.GradientBackgroundOn() 712 arenderer.SetBackground2(vedo.get_color(bg2)) 713 714 x0 = i / shape[0] 715 y0 = j / shape[1] 716 x1 = (i + 1) / shape[0] 717 y1 = (j + 1) / shape[1] 718 arenderer.SetViewport(y0, x0, y1, x1) 719 self.renderers.append(arenderer) 720 self.axes_instances.append(None) 721 self.shape = shape 722 723 if self.renderers: 724 self.renderer = self.renderers[0] 725 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 726 727 ######################################################### 728 if self.qt_widget or self.wx_widget: 729 if self.qt_widget: 730 self.window = self.qt_widget.GetRenderWindow() # overwrite 731 else: 732 self.window = self.wx_widget.GetRenderWindow() 733 self.interactor = self.window.GetInteractor() 734 735 ######################################################### 736 for r in self.renderers: 737 self.window.AddRenderer(r) 738 # set the background gradient if any 739 if vedo.settings.background_gradient_orientation > 0: 740 try: 741 modes = [ 742 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 743 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 744 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 745 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 746 ] 747 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 748 r.GradientBackgroundOn() 749 except AttributeError: 750 pass 751 752 # Backend #################################################### 753 if vedo.settings.default_backend in ["panel", "trame", "k3d"]: 754 return ################ 755 ######################## 756 757 ######################################################### 758 if self.qt_widget or self.wx_widget: 759 self.interactor.SetRenderWindow(self.window) 760 if vedo.settings.enable_default_keyboard_callbacks: 761 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 762 if vedo.settings.enable_default_mouse_callbacks: 763 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 764 return ################ 765 ######################## 766 767 if self.size[0] == "f": # full screen 768 self.size = "fullscreen" 769 self.window.SetFullScreen(True) 770 self.window.BordersOn() 771 else: 772 self.window.SetSize(int(self.size[0]), int(self.size[1])) 773 774 if self.offscreen: 775 if self.axes in (4, 5, 8, 12, 14): 776 self.axes = 0 # does not work with those 777 self.window.SetOffScreenRendering(True) 778 self.interactor = None 779 self._interactive = False 780 return ################ 781 ######################## 782 783 self.window.SetPosition(pos) 784 785 ######################################################### 786 self.interactor = vtki.vtkRenderWindowInteractor() 787 788 self.interactor.SetRenderWindow(self.window) 789 vsty = vtki.new("InteractorStyleTrackballCamera") 790 self.interactor.SetInteractorStyle(vsty) 791 self.interactor.RemoveObservers("CharEvent") 792 793 if vedo.settings.enable_default_keyboard_callbacks: 794 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 795 if vedo.settings.enable_default_mouse_callbacks: 796 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick)
Arguments:
- shape : (str, list) shape of the grid of renderers in format (rows, columns). Ignored if N is specified.
- N : (int) number of desired renderers arranged in a grid automatically.
- pos : (list) (x,y) position in pixels of top-left corner of the rendering window on the screen
- size : (str, list) size of the rendering window. If 'auto', guess it based on screensize.
- screensize : (list) physical size of the monitor screen in pixels
- bg : (color, str) background color or specify jpg image file name with path
- bg2 : (color) background color of a gradient towards the top
- title : (str) window title
axes : (int)
Note that Axes type-1 can be fully customized by passing a dictionary
axes=dict()
. Check outvedo.addons.Axes()
for the available options.- 0, no axes - 1, draw three gray grid walls - 2, show cartesian axes from (0,0,0) - 3, show positive range of cartesian axes from (0,0,0) - 4, show a triad at bottom left - 5, show a cube at bottom left - 6, mark the corners of the bounding box - 7, draw a 3D ruler at each side of the cartesian axes - 8, show the VTK CubeAxesActor object - 9, show the bounding box outLine - 10, show three circles representing the maximum bounding box - 11, show a large grid on the x-y plane (use with zoom=8) - 12, show polar axes - 13, draw a simple ruler at the bottom of the window - 14: draw a camera orientation widget
- sharecam : (bool) if False each renderer will have an independent camera
- interactive : (bool) if True will stop after show() to allow interaction with the 3d scene
- offscreen : (bool) if True will not show the rendering window
- qt_widget : (QVTKRenderWindowInteractor)
render in a Qt-Widget using an QVTKRenderWindowInteractor.
See examples
qt_windows[1,2,3].py
andqt_cutter.py
.
868 def print(self): 869 """Print information about the current instance.""" 870 print(self.__str__()) 871 return self
Print information about the current instance.
889 def initialize_interactor(self) -> Self: 890 """Initialize the interactor if not already initialized.""" 891 if self.offscreen: 892 return self 893 if self.interactor: 894 if not self.interactor.GetInitialized(): 895 self.interactor.Initialize() 896 self.interactor.RemoveObservers("CharEvent") 897 return self
Initialize the interactor if not already initialized.
899 def process_events(self) -> Self: 900 """Process all pending events.""" 901 self.initialize_interactor() 902 if self.interactor: 903 try: 904 self.interactor.ProcessEvents() 905 except AttributeError: 906 pass 907 return self
Process all pending events.
909 def at(self, nren: int, yren=None) -> Self: 910 """ 911 Select the current renderer number as an int. 912 Can also use the `[nx, ny]` format. 913 """ 914 if utils.is_sequence(nren): 915 if len(nren) == 2: 916 nren, yren = nren 917 else: 918 vedo.logger.error("at() argument must be a single number or a list of two numbers") 919 raise RuntimeError 920 921 if yren is not None: 922 a, b = self.shape 923 x, y = nren, yren 924 nren = x * b + y 925 # print("at (", x, y, ") -> ren", nren) 926 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 927 vedo.logger.error(f"at({nren, yren}) is malformed!") 928 raise RuntimeError 929 930 self.renderer = self.renderers[nren] 931 return self
Select the current renderer number as an int.
Can also use the [nx, ny]
format.
933 def add(self, *objs, at=None) -> Self: 934 """ 935 Append the input objects to the internal list of objects to be shown. 936 937 Arguments: 938 at : (int) 939 add the object at the specified renderer 940 """ 941 if at is not None: 942 ren = self.renderers[at] 943 else: 944 ren = self.renderer 945 946 objs = utils.flatten(objs) 947 for ob in objs: 948 if ob and ob not in self.objects: 949 self.objects.append(ob) 950 951 acts = self._scan_input_return_acts(objs) 952 953 for a in acts: 954 955 if ren: 956 if isinstance(a, vedo.addons.BaseCutter): 957 a.add_to(self) # from cutters 958 continue 959 960 if isinstance(a, vtki.vtkLight): 961 ren.AddLight(a) 962 continue 963 964 try: 965 ren.AddActor(a) 966 except TypeError: 967 ren.AddActor(a.actor) 968 969 try: 970 ir = self.renderers.index(ren) 971 a.rendered_at.add(ir) # might not have rendered_at 972 except (AttributeError, ValueError): 973 pass 974 975 if isinstance(a, vtki.vtkFollower): 976 a.SetCamera(self.camera) 977 elif isinstance(a, vedo.visual.LightKit): 978 a.lightkit.AddLightsToRenderer(ren) 979 980 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
982 def remove(self, *objs, at=None) -> Self: 983 """ 984 Remove input object to the internal list of objects to be shown. 985 986 Objects to be removed can be referenced by their assigned name, 987 988 Arguments: 989 at : (int) 990 remove the object at the specified renderer 991 """ 992 # TODO and you can also use wildcards like `*` and `?`. 993 if at is not None: 994 ren = self.renderers[at] 995 else: 996 ren = self.renderer 997 998 objs = [ob for ob in utils.flatten(objs) if ob] 999 1000 has_str = False 1001 for ob in objs: 1002 if isinstance(ob, str): 1003 has_str = True 1004 break 1005 1006 has_actor = False 1007 for ob in objs: 1008 if hasattr(ob, "actor") and ob.actor: 1009 has_actor = True 1010 break 1011 1012 if has_str or has_actor: 1013 # need to get the actors to search for 1014 for a in self.get_actors(include_non_pickables=True): 1015 # print("PARSING", [a]) 1016 try: 1017 if (a.name and a.name in objs) or a in objs: 1018 objs.append(a) 1019 # if a.name: 1020 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1021 # if any(bools) or a in objs: 1022 # objs.append(a) 1023 # print('a.name',a.name, objs,any(bools)) 1024 except AttributeError: # no .name 1025 # passing the actor so get back the object with .retrieve_object() 1026 try: 1027 vobj = a.retrieve_object() 1028 if (vobj.name and vobj.name in objs) or vobj in objs: 1029 # print('vobj.name', vobj.name) 1030 objs.append(vobj) 1031 except AttributeError: 1032 pass 1033 1034 if ren is None: 1035 return self 1036 ir = self.renderers.index(ren) 1037 1038 ids = [] 1039 for ob in set(objs): 1040 1041 # will remove it from internal list if possible 1042 try: 1043 idx = self.objects.index(ob) 1044 ids.append(idx) 1045 except ValueError: 1046 pass 1047 1048 if ren: ### remove it from the renderer 1049 1050 if isinstance(ob, vedo.addons.BaseCutter): 1051 ob.remove_from(self) # from cutters 1052 continue 1053 1054 try: 1055 ren.RemoveActor(ob) 1056 except TypeError: 1057 try: 1058 ren.RemoveActor(ob.actor) 1059 except AttributeError: 1060 pass 1061 1062 if hasattr(ob, "rendered_at"): 1063 ob.rendered_at.discard(ir) 1064 1065 if hasattr(ob, "scalarbar") and ob.scalarbar: 1066 ren.RemoveActor(ob.scalarbar) 1067 if hasattr(ob, "_caption") and ob._caption: 1068 ren.RemoveActor(ob._caption) 1069 if hasattr(ob, "shadows") and ob.shadows: 1070 for sha in ob.shadows: 1071 ren.RemoveActor(sha.actor) 1072 if hasattr(ob, "trail") and ob.trail: 1073 ren.RemoveActor(ob.trail.actor) 1074 ob.trail_points = [] 1075 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1076 for sha in ob.trail.shadows: 1077 ren.RemoveActor(sha.actor) 1078 1079 elif isinstance(ob, vedo.visual.LightKit): 1080 ob.lightkit.RemoveLightsFromRenderer(ren) 1081 1082 # for i in ids: # WRONG way of doing it! 1083 # del self.objects[i] 1084 # instead we do: 1085 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1086 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
1088 @property 1089 def actors(self): 1090 """Return the list of actors.""" 1091 return [ob.actor for ob in self.objects if hasattr(ob, "actor")]
Return the list of actors.
1093 def remove_lights(self) -> Self: 1094 """Remove all the present lights in the current renderer.""" 1095 if self.renderer: 1096 self.renderer.RemoveAllLights() 1097 return self
Remove all the present lights in the current renderer.
1099 def pop(self, at=None) -> Self: 1100 """ 1101 Remove the last added object from the rendering window. 1102 This method is typically used in loops or callback functions. 1103 """ 1104 if at is not None and not isinstance(at, int): 1105 # wrong usage pitfall 1106 vedo.logger.error("argument of pop() must be an integer") 1107 raise RuntimeError() 1108 1109 if self.objects: 1110 self.remove(self.objects[-1], at) 1111 return self
Remove the last added object from the rendering window. This method is typically used in loops or callback functions.
1113 def render(self, resetcam=False) -> Self: 1114 """Render the scene. This method is typically used in loops or callback functions.""" 1115 1116 if vedo.settings.dry_run_mode >= 2: 1117 return self 1118 1119 if not self.window: 1120 return self 1121 1122 self.initialize_interactor() 1123 1124 if resetcam: 1125 self.renderer.ResetCamera() 1126 1127 self.window.Render() 1128 1129 if self._cocoa_process_events and self.interactor and self.interactor.GetInitialized(): 1130 if "Darwin" in vedo.sys_platform and not self.offscreen: 1131 self.interactor.ProcessEvents() 1132 self._cocoa_process_events = False 1133 return self
Render the scene. This method is typically used in loops or callback functions.
1135 def interactive(self) -> Self: 1136 """ 1137 Start window interaction. 1138 Analogous to `show(..., interactive=True)`. 1139 """ 1140 if vedo.settings.dry_run_mode >= 1: 1141 return self 1142 self.initialize_interactor() 1143 if self.interactor: 1144 # print("self.interactor.Start()") 1145 self.interactor.Start() 1146 # print("self.interactor.Start() done") 1147 if self._must_close_now: 1148 # print("self.interactor.TerminateApp()") 1149 if self.interactor: 1150 self.interactor.GetRenderWindow().Finalize() 1151 self.interactor.TerminateApp() 1152 self.interactor = None 1153 self.window = None 1154 self.renderer = None 1155 self.renderers = [] 1156 self.camera = None 1157 return self
Start window interaction.
Analogous to show(..., interactive=True)
.
1159 def use_depth_peeling(self, at=None, value=True) -> Self: 1160 """ 1161 Specify whether use depth peeling algorithm at this specific renderer 1162 Call this method before the first rendering. 1163 """ 1164 if at is None: 1165 ren = self.renderer 1166 else: 1167 ren = self.renderers[at] 1168 ren.SetUseDepthPeeling(value) 1169 return self
Specify whether use depth peeling algorithm at this specific renderer Call this method before the first rendering.
1171 def background(self, c1=None, c2=None, at=None, mode=0) -> Union[Self, "np.ndarray"]: 1172 """Set the color of the background for the current renderer. 1173 A different renderer index can be specified by keyword `at`. 1174 1175 Arguments: 1176 c1 : (list) 1177 background main color. 1178 c2 : (list) 1179 background color for the upper part of the window. 1180 at : (int) 1181 renderer index. 1182 mode : (int) 1183 background mode (needs vtk version >= 9.3) 1184 0 = vertical, 1185 1 = horizontal, 1186 2 = radial farthest side, 1187 3 = radia farthest corner. 1188 """ 1189 if not self.renderers: 1190 return self 1191 if at is None: 1192 r = self.renderer 1193 else: 1194 r = self.renderers[at] 1195 1196 if c1 is None and c2 is None: 1197 return np.array(r.GetBackground()) 1198 1199 if r: 1200 if c1 is not None: 1201 r.SetBackground(vedo.get_color(c1)) 1202 if c2 is not None: 1203 r.GradientBackgroundOn() 1204 r.SetBackground2(vedo.get_color(c2)) 1205 if mode: 1206 try: # only works with vtk>=9.3 1207 modes = [ 1208 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1209 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1210 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1211 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1212 ] 1213 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 1214 except AttributeError: 1215 pass 1216 1217 else: 1218 r.GradientBackgroundOff() 1219 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.
1222 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1223 """ 1224 Return a list of Meshes from the specified renderer. 1225 1226 Arguments: 1227 at : (int) 1228 specify which renderer to look at. 1229 include_non_pickables : (bool) 1230 include non-pickable objects 1231 unpack_assemblies : (bool) 1232 unpack assemblies into their components 1233 """ 1234 if at is None: 1235 renderer = self.renderer 1236 at = self.renderers.index(renderer) 1237 elif isinstance(at, int): 1238 renderer = self.renderers[at] 1239 1240 has_global_axes = False 1241 if isinstance(self.axes_instances[at], vedo.Assembly): 1242 has_global_axes = True 1243 1244 if unpack_assemblies: 1245 acs = renderer.GetActors() 1246 else: 1247 acs = renderer.GetViewProps() 1248 1249 objs = [] 1250 acs.InitTraversal() 1251 for _ in range(acs.GetNumberOfItems()): 1252 1253 if unpack_assemblies: 1254 a = acs.GetNextItem() 1255 else: 1256 a = acs.GetNextProp() 1257 1258 if isinstance(a, vtki.vtkVolume): 1259 continue 1260 1261 if include_non_pickables or a.GetPickable(): 1262 if a == self.axes_instances[at]: 1263 continue 1264 if has_global_axes and a in self.axes_instances[at].actors: 1265 continue 1266 try: 1267 objs.append(a.retrieve_object()) 1268 except AttributeError: 1269 pass 1270 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
1272 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1273 """ 1274 Return a list of Volumes from the specified renderer. 1275 1276 Arguments: 1277 at : (int) 1278 specify which renderer to look at 1279 include_non_pickables : (bool) 1280 include non-pickable objects 1281 """ 1282 if at is None: 1283 renderer = self.renderer 1284 at = self.renderers.index(renderer) 1285 elif isinstance(at, int): 1286 renderer = 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 if at is None: 1311 renderer = self.renderer 1312 if renderer is None: 1313 return [] 1314 at = self.renderers.index(renderer) 1315 elif isinstance(at, int): 1316 renderer = self.renderers[at] 1317 1318 acts = [] 1319 acs = renderer.GetViewProps() 1320 acs.InitTraversal() 1321 for _ in range(acs.GetNumberOfItems()): 1322 a = acs.GetNextProp() 1323 if include_non_pickables or a.GetPickable(): 1324 acts.append(a) 1325 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
1327 def check_actors_trasform(self, at=None) -> Self: 1328 """ 1329 Reset the transformation matrix of all actors at specified renderer. 1330 This is only useful when actors have been moved/rotated/scaled manually 1331 in an already rendered scene using interactors like 1332 'TrackballActor' or 'JoystickActor'. 1333 """ 1334 # see issue https://github.com/marcomusy/vedo/issues/1046 1335 for a in self.get_actors(at=at, include_non_pickables=True): 1336 try: 1337 M = a.GetMatrix() 1338 except AttributeError: 1339 continue 1340 if M and not M.IsIdentity(): 1341 try: 1342 a.retrieve_object().apply_transform_from_actor() 1343 # vedo.logger.info( 1344 # f"object '{a.retrieve_object().name}' " 1345 # "was manually moved. Updated to its current position." 1346 # ) 1347 except AttributeError: 1348 pass 1349 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'.
1351 def reset_camera(self, tight=None) -> Self: 1352 """ 1353 Reset the camera position and zooming. 1354 If tight (float) is specified the zooming reserves a padding space 1355 in the xy-plane expressed in percent of the average size. 1356 """ 1357 if tight is None: 1358 self.renderer.ResetCamera() 1359 else: 1360 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1361 cam = self.camera 1362 1363 self.renderer.ComputeAspect() 1364 aspect = self.renderer.GetAspect() 1365 angle = np.pi * cam.GetViewAngle() / 180.0 1366 dx = x1 - x0 1367 dy = y1 - y0 1368 dist = max(dx / aspect[0], dy) / np.tan(angle / 2) / 2 1369 1370 cam.SetViewUp(0, 1, 0) 1371 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1372 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1373 if cam.GetParallelProjection(): 1374 ps = max(dx / aspect[0], dy) / 2 1375 cam.SetParallelScale(ps * (1 + tight)) 1376 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1377 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.
1379 def reset_clipping_range(self, bounds=None) -> Self: 1380 """ 1381 Reset the camera clipping range to include all visible actors. 1382 If bounds is given, it will be used instead of computing it. 1383 """ 1384 if bounds is None: 1385 self.renderer.ResetCameraClippingRange() 1386 else: 1387 self.renderer.ResetCameraClippingRange(bounds) 1388 return self
Reset the camera clipping range to include all visible actors. If bounds is given, it will be used instead of computing it.
1390 def reset_viewup(self, smooth=True) -> Self: 1391 """ 1392 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1393 """ 1394 vbb = addons.compute_visible_bounds()[0] 1395 x0, x1, y0, y1, z0, z1 = vbb 1396 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1397 d = self.camera.GetDistance() 1398 1399 viewups = np.array( 1400 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1401 ) 1402 positions = np.array( 1403 [ 1404 (mx, my, mz + d), 1405 (mx, my, mz - d), 1406 (mx, my + d, mz), 1407 (mx, my - d, mz), 1408 (mx + d, my, mz), 1409 (mx - d, my, mz), 1410 ] 1411 ) 1412 1413 vu = np.array(self.camera.GetViewUp()) 1414 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1415 1416 poc = np.array(self.camera.GetPosition()) 1417 foc = np.array(self.camera.GetFocalPoint()) 1418 a = poc - foc 1419 b = positions - foc 1420 a = a / np.linalg.norm(a) 1421 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1422 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1423 1424 if smooth: 1425 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1426 for t in outtimes: 1427 vv = vu * (1 - t) + viewups[vui] * t 1428 pp = poc * (1 - t) + positions[pui] * t 1429 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1430 self.camera.SetViewUp(vv) 1431 self.camera.SetPosition(pp) 1432 self.camera.SetFocalPoint(ff) 1433 self.render() 1434 1435 # interpolator does not respect parallel view...: 1436 # cam1 = dict( 1437 # pos=poc, 1438 # viewup=vu, 1439 # focal_point=(mx,my,mz), 1440 # clipping_range=self.camera.GetClippingRange() 1441 # ) 1442 # # cam1 = self.camera 1443 # cam2 = dict( 1444 # pos=positions[pui], 1445 # viewup=viewups[vui], 1446 # focal_point=(mx,my,mz), 1447 # clipping_range=self.camera.GetClippingRange() 1448 # ) 1449 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1450 # for c in vcams: 1451 # self.renderer.SetActiveCamera(c) 1452 # self.render() 1453 else: 1454 1455 self.camera.SetViewUp(viewups[vui]) 1456 self.camera.SetPosition(positions[pui]) 1457 self.camera.SetFocalPoint(mx, my, mz) 1458 1459 self.renderer.ResetCameraClippingRange() 1460 1461 # vbb, _, _, _ = addons.compute_visible_bounds() 1462 # x0,x1, y0,y1, z0,z1 = vbb 1463 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1464 self.render() 1465 return self
Reset the orientation of the camera to the closest orthogonal direction and view-up.
1467 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1468 """ 1469 Takes as input two cameras set camera at an interpolated position: 1470 1471 Cameras can be vtkCamera or dictionaries in format: 1472 1473 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1474 1475 Press `shift-C` key in interactive mode to dump a python snipplet 1476 of parameters for the current camera view. 1477 """ 1478 nc = len(cameras) 1479 if len(times) == 0: 1480 times = np.linspace(0, 1, num=nc, endpoint=True) 1481 1482 assert len(times) == nc 1483 1484 cin = vtki.new("CameraInterpolator") 1485 1486 # cin.SetInterpolationTypeToLinear() # buggy? 1487 if nc > 2 and smooth: 1488 cin.SetInterpolationTypeToSpline() 1489 1490 for i, cam in enumerate(cameras): 1491 vcam = cam 1492 if isinstance(cam, dict): 1493 vcam = utils.camera_from_dict(cam) 1494 cin.AddCamera(times[i], vcam) 1495 1496 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1497 rng = maxt - mint 1498 1499 if len(output_times) == 0: 1500 cin.InterpolateCamera(t * rng, self.camera) 1501 return [self.camera] 1502 else: 1503 vcams = [] 1504 for tt in output_times: 1505 c = vtki.vtkCamera() 1506 cin.InterpolateCamera(tt * rng, c) 1507 vcams.append(c) 1508 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.
1510 def fly_to(self, point) -> Self: 1511 """ 1512 Fly camera to the specified point. 1513 1514 Arguments: 1515 point : (list) 1516 point in space to place camera. 1517 1518 Example: 1519 ```python 1520 from vedo import * 1521 cone = Cone() 1522 plt = Plotter(axes=1) 1523 plt.show(cone) 1524 plt.fly_to([1,0,0]) 1525 plt.interactive().close() 1526 ``` 1527 """ 1528 if self.interactor: 1529 self.resetcam = False 1530 self.interactor.FlyTo(self.renderer, point) 1531 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()
1533 def look_at(self, plane="xy") -> Self: 1534 """Move the camera so that it looks at the specified cartesian plane""" 1535 cam = self.renderer.GetActiveCamera() 1536 fp = np.array(cam.GetFocalPoint()) 1537 p = np.array(cam.GetPosition()) 1538 dist = np.linalg.norm(fp - p) 1539 plane = plane.lower() 1540 if "x" in plane and "y" in plane: 1541 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1542 cam.SetViewUp(0.0, 1.0, 0.0) 1543 elif "x" in plane and "z" in plane: 1544 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1545 cam.SetViewUp(0.0, 0.0, 1.0) 1546 elif "y" in plane and "z" in plane: 1547 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1548 cam.SetViewUp(0.0, 0.0, 1.0) 1549 else: 1550 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1551 return self
Move the camera so that it looks at the specified cartesian plane
1553 def record(self, filename="") -> str: 1554 """ 1555 Record camera, mouse, keystrokes and all other events. 1556 Recording can be toggled on/off by pressing key "R". 1557 1558 Arguments: 1559 filename : (str) 1560 ascii file to store events. 1561 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1562 1563 Returns: 1564 a string descriptor of events. 1565 1566 Examples: 1567 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1568 """ 1569 if vedo.settings.dry_run_mode >= 1: 1570 return "" 1571 if not self.interactor: 1572 vedo.logger.warning("Cannot record events, no interactor defined.") 1573 return "" 1574 erec = vtki.new("InteractorEventRecorder") 1575 erec.SetInteractor(self.interactor) 1576 if not filename: 1577 if not os.path.exists(vedo.settings.cache_directory): 1578 os.makedirs(vedo.settings.cache_directory) 1579 home_dir = os.path.expanduser("~") 1580 filename = os.path.join( 1581 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1582 print("Events will be recorded in", filename) 1583 erec.SetFileName(filename) 1584 erec.SetKeyPressActivationValue("R") 1585 erec.EnabledOn() 1586 erec.Record() 1587 self.interactor.Start() 1588 erec.Stop() 1589 erec.EnabledOff() 1590 with open(filename, "r", encoding="UTF-8") as fl: 1591 events = fl.read() 1592 erec = None 1593 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:
1595 def play(self, recorded_events="", repeats=0) -> Self: 1596 """ 1597 Play camera, mouse, keystrokes and all other events. 1598 1599 Arguments: 1600 events : (str) 1601 file o string of events. 1602 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1603 repeats : (int) 1604 number of extra repeats of the same events. The default is 0. 1605 1606 Examples: 1607 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1608 """ 1609 if vedo.settings.dry_run_mode >= 1: 1610 return self 1611 if not self.interactor: 1612 vedo.logger.warning("Cannot play events, no interactor defined.") 1613 return self 1614 1615 erec = vtki.new("InteractorEventRecorder") 1616 erec.SetInteractor(self.interactor) 1617 1618 if not recorded_events: 1619 home_dir = os.path.expanduser("~") 1620 recorded_events = os.path.join( 1621 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1622 1623 if recorded_events.endswith(".log"): 1624 erec.ReadFromInputStringOff() 1625 erec.SetFileName(recorded_events) 1626 else: 1627 erec.ReadFromInputStringOn() 1628 erec.SetInputString(recorded_events) 1629 1630 erec.Play() 1631 for _ in range(repeats): 1632 erec.Rewind() 1633 erec.Play() 1634 erec.EnabledOff() 1635 erec = None 1636 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:
1638 def parallel_projection(self, value=True, at=None) -> Self: 1639 """ 1640 Use parallel projection `at` a specified renderer. 1641 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1642 An input value equal to -1 will toggle it on/off. 1643 """ 1644 if at is not None: 1645 r = self.renderers[at] 1646 else: 1647 r = self.renderer 1648 if value == -1: 1649 val = r.GetActiveCamera().GetParallelProjection() 1650 value = not val 1651 r.GetActiveCamera().SetParallelProjection(value) 1652 r.Modified() 1653 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.
1660 def fov(self, angle: float) -> Self: 1661 """ 1662 Set the field of view angle for the camera. 1663 This is the angle of the camera frustum in the horizontal direction. 1664 High values will result in a wide-angle lens (fish-eye effect), 1665 and low values will result in a telephoto lens. 1666 1667 Default value is 30 degrees. 1668 """ 1669 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1670 self.renderer.GetActiveCamera().SetViewAngle(angle) 1671 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.
1673 def zoom(self, zoom: float) -> Self: 1674 """Apply a zooming factor for the current camera view""" 1675 self.renderer.GetActiveCamera().Zoom(zoom) 1676 return self
Apply a zooming factor for the current camera view
1678 def azimuth(self, angle: float) -> Self: 1679 """Rotate camera around the view up vector.""" 1680 self.renderer.GetActiveCamera().Azimuth(angle) 1681 return self
Rotate camera around the view up vector.
1683 def elevation(self, angle: float) -> Self: 1684 """Rotate the camera around the cross product of the negative 1685 of the direction of projection and the view up vector.""" 1686 self.renderer.GetActiveCamera().Elevation(angle) 1687 return self
Rotate the camera around the cross product of the negative of the direction of projection and the view up vector.
1689 def roll(self, angle: float) -> Self: 1690 """Roll the camera about the direction of projection.""" 1691 self.renderer.GetActiveCamera().Roll(angle) 1692 return self
Roll the camera about the direction of projection.
1694 def dolly(self, value: float) -> Self: 1695 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1696 self.renderer.GetActiveCamera().Dolly(value) 1697 return self
Move the camera towards (value>0) or away from (value<0) the focal point.
1700 def add_slider( 1701 self, 1702 sliderfunc, 1703 xmin, 1704 xmax, 1705 value=None, 1706 pos=4, 1707 title="", 1708 font="Calco", 1709 title_size=1, 1710 c=None, 1711 alpha=1, 1712 show_value=True, 1713 delayed=False, 1714 **options, 1715 ) -> "vedo.addons.Slider2D": 1716 """ 1717 Add a `vedo.addons.Slider2D` which can call an external custom function. 1718 1719 Arguments: 1720 sliderfunc : (Callable) 1721 external function to be called by the widget 1722 xmin : (float) 1723 lower value of the slider 1724 xmax : (float) 1725 upper value 1726 value : (float) 1727 current value 1728 pos : (list, str) 1729 position corner number: horizontal [1-5] or vertical [11-15] 1730 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1731 and also by a string descriptor (eg. "bottom-left") 1732 title : (str) 1733 title text 1734 font : (str) 1735 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1736 title_size : (float) 1737 title text scale [1.0] 1738 show_value : (bool) 1739 if True current value is shown 1740 delayed : (bool) 1741 if True the callback is delayed until when the mouse button is released 1742 alpha : (float) 1743 opacity of the scalar bar texts 1744 slider_length : (float) 1745 slider length 1746 slider_width : (float) 1747 slider width 1748 end_cap_length : (float) 1749 length of the end cap 1750 end_cap_width : (float) 1751 width of the end cap 1752 tube_width : (float) 1753 width of the tube 1754 title_height : (float) 1755 width of the title 1756 tformat : (str) 1757 format of the title 1758 1759 Examples: 1760 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1761 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1762 1763 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1764 """ 1765 if c is None: # automatic black or white 1766 c = (0.8, 0.8, 0.8) 1767 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1768 c = (0.2, 0.2, 0.2) 1769 else: 1770 c = vedo.get_color(c) 1771 1772 slider2d = addons.Slider2D( 1773 sliderfunc, 1774 xmin, 1775 xmax, 1776 value, 1777 pos, 1778 title, 1779 font, 1780 title_size, 1781 c, 1782 alpha, 1783 show_value, 1784 delayed, 1785 **options, 1786 ) 1787 1788 if self.renderer: 1789 slider2d.renderer = self.renderer 1790 if self.interactor: 1791 slider2d.interactor = self.interactor 1792 slider2d.on() 1793 self.sliders.append([slider2d, sliderfunc]) 1794 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:
1796 def add_slider3d( 1797 self, 1798 sliderfunc, 1799 pos1, 1800 pos2, 1801 xmin, 1802 xmax, 1803 value=None, 1804 s=0.03, 1805 t=1, 1806 title="", 1807 rotation=0.0, 1808 c=None, 1809 show_value=True, 1810 ) -> "vedo.addons.Slider3D": 1811 """ 1812 Add a 3D slider widget which can call an external custom function. 1813 1814 Arguments: 1815 sliderfunc : (function) 1816 external function to be called by the widget 1817 pos1 : (list) 1818 first position 3D coordinates 1819 pos2 : (list) 1820 second position coordinates 1821 xmin : (float) 1822 lower value 1823 xmax : (float) 1824 upper value 1825 value : (float) 1826 initial value 1827 s : (float) 1828 label scaling factor 1829 t : (float) 1830 tube scaling factor 1831 title : (str) 1832 title text 1833 c : (color) 1834 slider color 1835 rotation : (float) 1836 title rotation around slider axis 1837 show_value : (bool) 1838 if True current value is shown 1839 1840 Examples: 1841 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1842 1843 ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png) 1844 """ 1845 if c is None: # automatic black or white 1846 c = (0.8, 0.8, 0.8) 1847 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1848 c = (0.2, 0.2, 0.2) 1849 else: 1850 c = vedo.get_color(c) 1851 1852 slider3d = addons.Slider3D( 1853 sliderfunc, 1854 pos1, 1855 pos2, 1856 xmin, 1857 xmax, 1858 value, 1859 s, 1860 t, 1861 title, 1862 rotation, 1863 c, 1864 show_value, 1865 ) 1866 slider3d.renderer = self.renderer 1867 slider3d.interactor = self.interactor 1868 slider3d.on() 1869 self.sliders.append([slider3d, sliderfunc]) 1870 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:
1929 def add_spline_tool( 1930 self, points, pc="k", ps=8, lc="r4", ac="g5", 1931 lw=2, alpha=1, closed=False, ontop=True, can_add_nodes=True, 1932 ) -> "vedo.addons.SplineTool": 1933 """ 1934 Add a spline tool to the current plotter. 1935 Nodes of the spline can be dragged in space with the mouse. 1936 Clicking on the line itself adds an extra point. 1937 Selecting a point and pressing del removes it. 1938 1939 Arguments: 1940 points : (Mesh, Points, array) 1941 the set of vertices forming the spline nodes. 1942 pc : (str) 1943 point color. The default is 'k'. 1944 ps : (str) 1945 point size. The default is 8. 1946 lc : (str) 1947 line color. The default is 'r4'. 1948 ac : (str) 1949 active point marker color. The default is 'g5'. 1950 lw : (int) 1951 line width. The default is 2. 1952 alpha : (float) 1953 line transparency. 1954 closed : (bool) 1955 spline is meant to be closed. The default is False. 1956 1957 Returns: 1958 a `SplineTool` object. 1959 1960 Examples: 1961 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1962 1963 ![](https://vedo.embl.es/images/basic/spline_tool.png) 1964 """ 1965 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1966 sw.interactor = self.interactor 1967 sw.on() 1968 sw.Initialize(sw.points.dataset) 1969 sw.representation.SetRenderer(self.renderer) 1970 sw.representation.SetClosedLoop(closed) 1971 sw.representation.BuildRepresentation() 1972 self.widgets.append(sw) 1973 return sw
Add a spline tool to the current plotter. Nodes of the spline can be dragged in space with the mouse. Clicking on the line itself adds an extra point. Selecting a point and pressing del removes it.
Arguments:
- points : (Mesh, Points, array) the set of vertices forming the spline nodes.
- pc : (str) point color. The default is 'k'.
- ps : (str) point size. The default is 8.
- lc : (str) line color. The default is 'r4'.
- ac : (str) active point marker color. The default is 'g5'.
- lw : (int) line width. The default is 2.
- alpha : (float) line transparency.
- closed : (bool) spline is meant to be closed. The default is False.
Returns:
a
SplineTool
object.
Examples:
1975 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1976 """Add an inset icon mesh into the same renderer. 1977 1978 Arguments: 1979 pos : (int, list) 1980 icon position in the range [1-4] indicating one of the 4 corners, 1981 or it can be a tuple (x,y) as a fraction of the renderer size. 1982 size : (float) 1983 size of the square inset. 1984 1985 Examples: 1986 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1987 """ 1988 iconw = addons.Icon(icon, pos, size) 1989 1990 iconw.SetInteractor(self.interactor) 1991 iconw.EnabledOn() 1992 iconw.InteractiveOff() 1993 self.widgets.append(iconw) 1994 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:
1996 def add_global_axes(self, axtype=None, c=None) -> Self: 1997 """Draw axes on scene. Available axes types: 1998 1999 Arguments: 2000 axtype : (int) 2001 - 0, no axes, 2002 - 1, draw three gray grid walls 2003 - 2, show cartesian axes from (0,0,0) 2004 - 3, show positive range of cartesian axes from (0,0,0) 2005 - 4, show a triad at bottom left 2006 - 5, show a cube at bottom left 2007 - 6, mark the corners of the bounding box 2008 - 7, draw a 3D ruler at each side of the cartesian axes 2009 - 8, show the vtkCubeAxesActor object 2010 - 9, show the bounding box outLine 2011 - 10, show three circles representing the maximum bounding box 2012 - 11, show a large grid on the x-y plane 2013 - 12, show polar axes 2014 - 13, draw a simple ruler at the bottom of the window 2015 2016 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 2017 2018 Example: 2019 ```python 2020 from vedo import Box, show 2021 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 2022 show( 2023 b, 2024 axes={ 2025 "xtitle": "Some long variable [a.u.]", 2026 "number_of_divisions": 4, 2027 # ... 2028 }, 2029 ) 2030 ``` 2031 2032 Examples: 2033 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2034 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2035 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2036 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2037 2038 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2039 """ 2040 addons.add_global_axes(axtype, c) 2041 return self
Draw axes on scene. Available axes types:
Arguments:
- axtype : (int)
- 0, no axes,
- 1, draw three gray grid walls
- 2, show cartesian axes from (0,0,0)
- 3, show positive range of cartesian axes from (0,0,0)
- 4, show a triad at bottom left
- 5, show a cube at bottom left
- 6, mark the corners of the bounding box
- 7, draw a 3D ruler at each side of the cartesian axes
- 8, show the vtkCubeAxesActor object
- 9, show the bounding box outLine
- 10, show three circles representing the maximum bounding box
- 11, show a large grid on the x-y plane
- 12, show polar axes
- 13, draw a simple ruler at the bottom of the window
- Axis type-1 can be fully customized by passing a dictionary axes=dict().
Example:
from vedo import Box, show b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) show( b, axes={ "xtitle": "Some long variable [a.u.]", "number_of_divisions": 4, # ... }, )
Examples:
2043 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2044 """Add a legend to the top right. 2045 2046 Examples: 2047 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 2048 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 2049 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 2050 """ 2051 acts = self.get_meshes() 2052 lb = addons.LegendBox(acts, **kwargs) 2053 self.add(lb) 2054 return lb
2056 def add_hint( 2057 self, 2058 obj, 2059 text="", 2060 c="k", 2061 bg="yellow9", 2062 font="Calco", 2063 size=18, 2064 justify=0, 2065 angle=0, 2066 delay=250, 2067 ) -> Union[vtki.vtkBalloonWidget, None]: 2068 """ 2069 Create a pop-up hint style message when hovering an object. 2070 Use `add_hint(obj, False)` to disable a hinting a specific object. 2071 Use `add_hint(None)` to disable all hints. 2072 2073 Arguments: 2074 obj : (Mesh, Points) 2075 the object to associate the pop-up to 2076 text : (str) 2077 string description of the pop-up 2078 delay : (int) 2079 milliseconds to wait before pop-up occurs 2080 """ 2081 if self.offscreen or not self.interactor: 2082 return None 2083 2084 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2085 # Linux vtk9.0 is bugged 2086 vedo.logger.warning( 2087 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2088 ) 2089 return None 2090 2091 if obj is None: 2092 self.hint_widget.EnabledOff() 2093 self.hint_widget.SetInteractor(None) 2094 self.hint_widget = None 2095 return self.hint_widget 2096 2097 if text is False and self.hint_widget: 2098 self.hint_widget.RemoveBalloon(obj) 2099 return self.hint_widget 2100 2101 if text == "": 2102 if obj.name: 2103 text = obj.name 2104 elif obj.filename: 2105 text = obj.filename 2106 else: 2107 return None 2108 2109 if not self.hint_widget: 2110 self.hint_widget = vtki.vtkBalloonWidget() 2111 2112 rep = self.hint_widget.GetRepresentation() 2113 rep.SetBalloonLayoutToImageRight() 2114 2115 trep = rep.GetTextProperty() 2116 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2117 trep.SetFontFile(utils.get_font_path(font)) 2118 trep.SetFontSize(size) 2119 trep.SetColor(vedo.get_color(c)) 2120 trep.SetBackgroundColor(vedo.get_color(bg)) 2121 trep.SetShadow(0) 2122 trep.SetJustification(justify) 2123 trep.UseTightBoundingBoxOn() 2124 2125 self.hint_widget.ManagesCursorOff() 2126 self.hint_widget.SetTimerDuration(delay) 2127 self.hint_widget.SetInteractor(self.interactor) 2128 if angle: 2129 trep.SetOrientation(angle) 2130 trep.SetBackgroundOpacity(0) 2131 # else: 2132 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2133 self.hint_widget.SetRepresentation(rep) 2134 self.widgets.append(self.hint_widget) 2135 self.hint_widget.EnabledOn() 2136 2137 bst = self.hint_widget.GetBalloonString(obj.actor) 2138 if bst: 2139 self.hint_widget.UpdateBalloonString(obj.actor, text) 2140 else: 2141 self.hint_widget.AddBalloon(obj.actor, text) 2142 2143 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
2145 def add_shadows(self) -> Self: 2146 """Add shadows at the current renderer.""" 2147 if self.renderer: 2148 shadows = vtki.new("ShadowMapPass") 2149 seq = vtki.new("SequencePass") 2150 passes = vtki.new("RenderPassCollection") 2151 passes.AddItem(shadows.GetShadowMapBakerPass()) 2152 passes.AddItem(shadows) 2153 seq.SetPasses(passes) 2154 camerapass = vtki.new("CameraPass") 2155 camerapass.SetDelegatePass(seq) 2156 self.renderer.SetPass(camerapass) 2157 return self
Add shadows at the current renderer.
2159 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> Self: 2160 """ 2161 Screen Space Ambient Occlusion. 2162 2163 For every pixel on the screen, the pixel shader samples the depth values around 2164 the current pixel and tries to compute the amount of occlusion from each of the sampled 2165 points. 2166 2167 Arguments: 2168 radius : (float) 2169 radius of influence in absolute units 2170 bias : (float) 2171 bias of the normals 2172 blur : (bool) 2173 add a blurring to the sampled positions 2174 samples : (int) 2175 number of samples to probe 2176 2177 Examples: 2178 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2179 2180 ![](https://vedo.embl.es/images/basic/ssao.jpg) 2181 """ 2182 lights = vtki.new("LightsPass") 2183 2184 opaque = vtki.new("OpaquePass") 2185 2186 ssaoCam = vtki.new("CameraPass") 2187 ssaoCam.SetDelegatePass(opaque) 2188 2189 ssao = vtki.new("SSAOPass") 2190 ssao.SetRadius(radius) 2191 ssao.SetBias(bias) 2192 ssao.SetBlur(blur) 2193 ssao.SetKernelSize(samples) 2194 ssao.SetDelegatePass(ssaoCam) 2195 2196 translucent = vtki.new("TranslucentPass") 2197 2198 volpass = vtki.new("VolumetricPass") 2199 ddp = vtki.new("DualDepthPeelingPass") 2200 ddp.SetTranslucentPass(translucent) 2201 ddp.SetVolumetricPass(volpass) 2202 2203 over = vtki.new("OverlayPass") 2204 2205 collection = vtki.new("RenderPassCollection") 2206 collection.AddItem(lights) 2207 collection.AddItem(ssao) 2208 collection.AddItem(ddp) 2209 collection.AddItem(over) 2210 2211 sequence = vtki.new("SequencePass") 2212 sequence.SetPasses(collection) 2213 2214 cam = vtki.new("CameraPass") 2215 cam.SetDelegatePass(sequence) 2216 2217 self.renderer.SetPass(cam) 2218 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:
2220 def add_depth_of_field(self, autofocus=True) -> Self: 2221 """Add a depth of field effect in the scene.""" 2222 lights = vtki.new("LightsPass") 2223 2224 opaque = vtki.new("OpaquePass") 2225 2226 dofCam = vtki.new("CameraPass") 2227 dofCam.SetDelegatePass(opaque) 2228 2229 dof = vtki.new("DepthOfFieldPass") 2230 dof.SetAutomaticFocalDistance(autofocus) 2231 dof.SetDelegatePass(dofCam) 2232 2233 collection = vtki.new("RenderPassCollection") 2234 collection.AddItem(lights) 2235 collection.AddItem(dof) 2236 2237 sequence = vtki.new("SequencePass") 2238 sequence.SetPasses(collection) 2239 2240 cam = vtki.new("CameraPass") 2241 cam.SetDelegatePass(sequence) 2242 2243 self.renderer.SetPass(cam) 2244 return self
Add a depth of field effect in the scene.
2275 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None) -> "vedo.addons.RendererFrame": 2276 """ 2277 Add a frame to the renderer subwindow. 2278 2279 Arguments: 2280 c : (color) 2281 color name or index 2282 alpha : (float) 2283 opacity level 2284 lw : (int) 2285 line width in pixels. 2286 padding : (float) 2287 padding space in pixels. 2288 """ 2289 if c is None: # automatic black or white 2290 c = (0.9, 0.9, 0.9) 2291 if self.renderer: 2292 if np.sum(self.renderer.GetBackground()) > 1.5: 2293 c = (0.1, 0.1, 0.1) 2294 renf = addons.RendererFrame(c, alpha, lw, padding) 2295 if renf: 2296 self.renderer.AddActor(renf) 2297 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.
2299 def add_hover_legend( 2300 self, 2301 at=None, 2302 c=None, 2303 pos="bottom-left", 2304 font="Calco", 2305 s=0.75, 2306 bg="auto", 2307 alpha=0.1, 2308 maxlength=24, 2309 use_info=False, 2310 ) -> int: 2311 """ 2312 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2313 2314 The created text object are stored in `plotter.hover_legends`. 2315 2316 Returns: 2317 the id of the callback function. 2318 2319 Arguments: 2320 c : (color) 2321 Text color. If None then black or white is chosen automatically 2322 pos : (str) 2323 text positioning 2324 font : (str) 2325 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2326 s : (float) 2327 text size scale 2328 bg : (color) 2329 background color of the 2D box containing the text 2330 alpha : (float) 2331 box transparency 2332 maxlength : (int) 2333 maximum number of characters per line 2334 use_info : (bool) 2335 visualize the content of the `obj.info` attribute 2336 2337 Examples: 2338 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2339 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2340 2341 ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg) 2342 """ 2343 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2344 2345 if at is None: 2346 at = self.renderers.index(self.renderer) 2347 2348 def _legfunc(evt): 2349 if not evt.object or not self.renderer or at != evt.at: 2350 if hoverlegend.mapper.GetInput(): # clear and return 2351 hoverlegend.mapper.SetInput("") 2352 self.render() 2353 return 2354 2355 if use_info: 2356 if hasattr(evt.object, "info"): 2357 t = str(evt.object.info) 2358 else: 2359 return 2360 else: 2361 t, tp = "", "" 2362 if evt.isMesh: 2363 tp = "Mesh " 2364 elif evt.isPoints: 2365 tp = "Points " 2366 elif evt.isVolume: 2367 tp = "Volume " 2368 elif evt.isImage: 2369 tp = "Image " 2370 elif evt.isAssembly: 2371 tp = "Assembly " 2372 else: 2373 return 2374 2375 if evt.isAssembly: 2376 if not evt.object.name: 2377 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2378 else: 2379 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2380 else: 2381 if evt.object.name: 2382 t += f"{tp}name" 2383 if evt.isPoints: 2384 t += " " 2385 if evt.isMesh: 2386 t += " " 2387 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2388 2389 if evt.object.filename: 2390 t += f"{tp}filename: " 2391 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2392 t += "\n" 2393 if not evt.object.file_size: 2394 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2395 if evt.object.file_size: 2396 t += " : " 2397 sz, created = evt.object.file_size, evt.object.created 2398 t += f"{created[4:-5]} ({sz})" + "\n" 2399 2400 if evt.isPoints: 2401 indata = evt.object.dataset 2402 if indata.GetNumberOfPoints(): 2403 t += ( 2404 f"#points/cells: {indata.GetNumberOfPoints()}" 2405 f" / {indata.GetNumberOfCells()}" 2406 ) 2407 pdata = indata.GetPointData() 2408 cdata = indata.GetCellData() 2409 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2410 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2411 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2412 t += " *" 2413 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2414 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2415 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2416 t += " *" 2417 2418 if evt.isImage: 2419 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2420 t += f"\nImage shape: {evt.object.shape}" 2421 pcol = self.color_picker(evt.picked2d) 2422 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2423 2424 # change box color if needed in 'auto' mode 2425 if evt.isPoints and "auto" in str(bg): 2426 actcol = evt.object.properties.GetColor() 2427 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2428 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2429 2430 # adapt to changes in bg color 2431 bgcol = self.renderers[at].GetBackground() 2432 _bgcol = c 2433 if _bgcol is None: # automatic black or white 2434 _bgcol = (0.9, 0.9, 0.9) 2435 if sum(bgcol) > 1.5: 2436 _bgcol = (0.1, 0.1, 0.1) 2437 if len(set(_bgcol).intersection(bgcol)) < 3: 2438 hoverlegend.color(_bgcol) 2439 2440 if hoverlegend.mapper.GetInput() != t: 2441 hoverlegend.mapper.SetInput(t) 2442 self.interactor.Render() 2443 2444 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2445 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2446 2447 self.add(hoverlegend, at=at) 2448 self.hover_legends.append(hoverlegend) 2449 idcall = self.add_callback("MouseMove", _legfunc) 2450 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:
2452 def add_scale_indicator( 2453 self, 2454 pos=(0.7, 0.05), 2455 s=0.02, 2456 length=2, 2457 lw=4, 2458 c="k1", 2459 alpha=1, 2460 units="", 2461 gap=0.05, 2462 ) -> Union["vedo.visual.Actor2D", None]: 2463 """ 2464 Add a Scale Indicator. Only works in parallel mode (no perspective). 2465 2466 Arguments: 2467 pos : (list) 2468 fractional (x,y) position on the screen. 2469 s : (float) 2470 size of the text. 2471 length : (float) 2472 length of the line. 2473 units : (str) 2474 string to show units. 2475 gap : (float) 2476 separation of line and text. 2477 2478 Example: 2479 ```python 2480 from vedo import settings, Cube, Plotter 2481 settings.use_parallel_projection = True # or else it does not make sense! 2482 cube = Cube().alpha(0.2) 2483 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2484 plt.add_scale_indicator(units='um', c='blue4') 2485 plt.show(cube, "Scale indicator with units").close() 2486 ``` 2487 ![](https://vedo.embl.es/images/feats/scale_indicator.png) 2488 """ 2489 # Note that this cannot go in addons.py 2490 # because it needs callbacks and window size 2491 if not self.interactor: 2492 return None 2493 2494 ppoints = vtki.vtkPoints() # Generate the polyline 2495 psqr = [[0.0, gap], [length / 10, gap]] 2496 dd = psqr[1][0] - psqr[0][0] 2497 for i, pt in enumerate(psqr): 2498 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2499 lines = vtki.vtkCellArray() 2500 lines.InsertNextCell(len(psqr)) 2501 for i in range(len(psqr)): 2502 lines.InsertCellPoint(i) 2503 pd = vtki.vtkPolyData() 2504 pd.SetPoints(ppoints) 2505 pd.SetLines(lines) 2506 2507 wsx, wsy = self.window.GetSize() 2508 if not self.camera.GetParallelProjection(): 2509 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2510 return None 2511 2512 rlabel = vtki.new("VectorText") 2513 rlabel.SetText("scale") 2514 tf = vtki.new("TransformPolyDataFilter") 2515 tf.SetInputConnection(rlabel.GetOutputPort()) 2516 t = vtki.vtkTransform() 2517 t.Scale(s * wsy / wsx, s, 1) 2518 tf.SetTransform(t) 2519 2520 app = vtki.new("AppendPolyData") 2521 app.AddInputConnection(tf.GetOutputPort()) 2522 app.AddInputData(pd) 2523 2524 mapper = vtki.new("PolyDataMapper2D") 2525 mapper.SetInputConnection(app.GetOutputPort()) 2526 cs = vtki.vtkCoordinate() 2527 cs.SetCoordinateSystem(1) 2528 mapper.SetTransformCoordinate(cs) 2529 2530 fractor = vedo.visual.Actor2D() 2531 csys = fractor.GetPositionCoordinate() 2532 csys.SetCoordinateSystem(3) 2533 fractor.SetPosition(pos) 2534 fractor.SetMapper(mapper) 2535 fractor.GetProperty().SetColor(vedo.get_color(c)) 2536 fractor.GetProperty().SetOpacity(alpha) 2537 fractor.GetProperty().SetLineWidth(lw) 2538 fractor.GetProperty().SetDisplayLocationToForeground() 2539 2540 def sifunc(iren, ev): 2541 wsx, wsy = self.window.GetSize() 2542 ps = self.camera.GetParallelScale() 2543 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2544 if units: 2545 newtxt += " " + units 2546 if rlabel.GetText() != newtxt: 2547 rlabel.SetText(newtxt) 2548 2549 self.renderer.AddActor(fractor) 2550 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2551 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2552 self.interactor.AddObserver("InteractionEvent", sifunc) 2553 sifunc(0, 0) 2554 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()
2556 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2557 """ 2558 Create an Event object with information of what was clicked. 2559 2560 If `enable_picking` is False, no picking will be performed. 2561 This can be useful to avoid double picking when using buttons. 2562 """ 2563 if not self.interactor: 2564 return Event() 2565 2566 if len(pos) > 0: 2567 x, y = pos 2568 self.interactor.SetEventPosition(pos) 2569 else: 2570 x, y = self.interactor.GetEventPosition() 2571 self.renderer = self.interactor.FindPokedRenderer(x, y) 2572 2573 self.picked2d = (x, y) 2574 2575 key = self.interactor.GetKeySym() 2576 2577 if key: 2578 if "_L" in key or "_R" in key: 2579 # skip things like Shift_R 2580 key = "" # better than None 2581 else: 2582 if self.interactor.GetShiftKey(): 2583 key = key.upper() 2584 2585 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2586 key = "underscore" 2587 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2588 key = "plus" 2589 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2590 key = "?" 2591 2592 if self.interactor.GetControlKey(): 2593 key = "Ctrl+" + key 2594 2595 if self.interactor.GetAltKey(): 2596 key = "Alt+" + key 2597 2598 if enable_picking: 2599 if not self.picker: 2600 self.picker = vtki.vtkPropPicker() 2601 2602 self.picker.PickProp(x, y, self.renderer) 2603 actor = self.picker.GetProp3D() 2604 # Note that GetProp3D already picks Assembly 2605 2606 xp, yp = self.interactor.GetLastEventPosition() 2607 dx, dy = x - xp, y - yp 2608 2609 delta3d = np.array([0, 0, 0]) 2610 2611 if actor: 2612 picked3d = np.array(self.picker.GetPickPosition()) 2613 2614 try: 2615 vobj = actor.retrieve_object() 2616 old_pt = np.asarray(vobj.picked3d) 2617 vobj.picked3d = picked3d 2618 delta3d = picked3d - old_pt 2619 except (AttributeError, TypeError): 2620 pass 2621 2622 else: 2623 picked3d = None 2624 2625 if not actor: # try 2D 2626 actor = self.picker.GetActor2D() 2627 2628 event = Event() 2629 event.name = ename 2630 event.title = self.title 2631 event.id = -1 # will be set by the timer wrapper function 2632 event.timerid = -1 # will be set by the timer wrapper function 2633 event.priority = -1 # will be set by the timer wrapper function 2634 event.time = time.time() 2635 event.at = self.renderers.index(self.renderer) 2636 event.keypress = key 2637 if enable_picking: 2638 try: 2639 event.object = actor.retrieve_object() 2640 except AttributeError: 2641 event.object = actor 2642 try: 2643 event.actor = actor.retrieve_object() # obsolete use object instead 2644 except AttributeError: 2645 event.actor = actor 2646 event.picked3d = picked3d 2647 event.picked2d = (x, y) 2648 event.delta2d = (dx, dy) 2649 event.angle2d = np.arctan2(dy, dx) 2650 event.speed2d = np.sqrt(dx * dx + dy * dy) 2651 event.delta3d = delta3d 2652 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2653 event.isPoints = isinstance(event.object, vedo.Points) 2654 event.isMesh = isinstance(event.object, vedo.Mesh) 2655 event.isAssembly = isinstance(event.object, vedo.Assembly) 2656 event.isVolume = isinstance(event.object, vedo.Volume) 2657 event.isImage = isinstance(event.object, vedo.Image) 2658 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2659 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.
2661 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2662 """ 2663 Add a function to be executed while show() is active. 2664 2665 Return a unique id for the callback. 2666 2667 The callback function (see example below) exposes a dictionary 2668 with the following information: 2669 - `name`: event name, 2670 - `id`: event unique identifier, 2671 - `priority`: event priority (float), 2672 - `interactor`: the interactor object, 2673 - `at`: renderer nr. where the event occurred 2674 - `keypress`: key pressed as string 2675 - `actor`: object picked by the mouse 2676 - `picked3d`: point picked in world coordinates 2677 - `picked2d`: screen coords of the mouse pointer 2678 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2679 - `delta3d`: ...same but in 3D world coords 2680 - `angle2d`: angle of mouse movement on screen 2681 - `speed2d`: speed of mouse movement on screen 2682 - `speed3d`: speed of picked point in world coordinates 2683 - `isPoints`: True if of class 2684 - `isMesh`: True if of class 2685 - `isAssembly`: True if of class 2686 - `isVolume`: True if of class Volume 2687 - `isImage`: True if of class 2688 2689 If `enable_picking` is False, no picking will be performed. 2690 This can be useful to avoid double picking when using buttons. 2691 2692 Frequently used events are: 2693 - `KeyPress`, `KeyRelease`: listen to keyboard events 2694 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2695 - `MiddleButtonPress`, `MiddleButtonRelease` 2696 - `RightButtonPress`, `RightButtonRelease` 2697 - `MouseMove`: listen to mouse pointer changing position 2698 - `MouseWheelForward`, `MouseWheelBackward` 2699 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2700 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2701 - `ResetCamera`, `ResetCameraClippingRange` 2702 - `Error`, `Warning` 2703 - `Char` 2704 - `Timer` 2705 2706 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2707 2708 Example: 2709 ```python 2710 from vedo import * 2711 2712 def func(evt): 2713 # this function is called every time the mouse moves 2714 # (evt is a dotted dictionary) 2715 if not evt.object: 2716 return # no hit, return 2717 print("point coords =", evt.picked3d) 2718 # print(evt) # full event dump 2719 2720 elli = Ellipsoid() 2721 plt = Plotter(axes=1) 2722 plt.add_callback('mouse hovering', func) 2723 plt.show(elli).close() 2724 ``` 2725 2726 Examples: 2727 - [spline_draw1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw1.py) 2728 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2729 2730 ![](https://vedo.embl.es/images/advanced/spline_draw.png) 2731 2732 - ..and many others! 2733 """ 2734 from vtkmodules.util.misc import calldata_type 2735 2736 if not self.interactor: 2737 return 0 2738 2739 if vedo.settings.dry_run_mode >= 1: 2740 return 0 2741 2742 ######################################### 2743 @calldata_type(vtki.VTK_INT) 2744 def _func_wrap(iren, ename, timerid=None): 2745 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2746 event.timerid = timerid 2747 event.id = cid 2748 event.priority = priority 2749 self.last_event = event 2750 func(event) 2751 2752 ######################################### 2753 2754 event_name = utils.get_vtk_name_event(event_name) 2755 2756 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2757 # print(f"Registering event: {event_name} with id={cid}") 2758 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!
2760 def remove_callback(self, cid: Union[int, str]) -> Self: 2761 """ 2762 Remove a callback function by its id 2763 or a whole category of callbacks by their name. 2764 2765 Arguments: 2766 cid : (int, str) 2767 Unique id of the callback. 2768 If an event name is passed all callbacks of that type are removed. 2769 """ 2770 if self.interactor: 2771 if isinstance(cid, str): 2772 cid = utils.get_vtk_name_event(cid) 2773 self.interactor.RemoveObservers(cid) 2774 else: 2775 self.interactor.RemoveObserver(cid) 2776 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.
2778 def remove_all_observers(self) -> Self: 2779 """ 2780 Remove all observers. 2781 2782 Example: 2783 ```python 2784 from vedo import * 2785 2786 def kfunc(event): 2787 print("Key pressed:", event.keypress) 2788 if event.keypress == 'q': 2789 plt.close() 2790 2791 def rfunc(event): 2792 if event.isImage: 2793 printc("Right-clicked!", event) 2794 plt.render() 2795 2796 img = Image(dataurl+"images/embryo.jpg") 2797 2798 plt = Plotter(size=(1050, 600)) 2799 plt.parallel_projection(True) 2800 plt.remove_all_observers() 2801 plt.add_callback("key press", kfunc) 2802 plt.add_callback("mouse right click", rfunc) 2803 plt.show("Right-Click Me! Press q to exit.", img) 2804 plt.close() 2805 ``` 2806 """ 2807 if self.interactor: 2808 self.interactor.RemoveAllObservers() 2809 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()
2811 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2812 """ 2813 Start or stop an existing timer. 2814 2815 Arguments: 2816 action : (str) 2817 Either "create"/"start" or "destroy"/"stop" 2818 timer_id : (int) 2819 When stopping the timer, the ID of the timer as returned when created 2820 dt : (int) 2821 time in milliseconds between each repeated call 2822 one_shot : (bool) 2823 create a one shot timer of prescribed duration instead of a repeating one 2824 2825 Examples: 2826 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2827 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2828 2829 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 2830 """ 2831 if action in ("create", "start"): 2832 if timer_id is not None: 2833 vedo.logger.warning("you set a timer_id but it will be ignored.") 2834 if one_shot: 2835 timer_id = self.interactor.CreateOneShotTimer(dt) 2836 else: 2837 timer_id = self.interactor.CreateRepeatingTimer(dt) 2838 return timer_id 2839 2840 elif action in ("destroy", "stop"): 2841 if timer_id is not None: 2842 self.interactor.DestroyTimer(timer_id) 2843 else: 2844 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2845 else: 2846 e = f"in timer_callback(). Cannot understand action: {action}\n" 2847 e += " allowed actions are: ['start', 'stop']. Skipped." 2848 vedo.logger.error(e) 2849 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:
2851 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2852 """ 2853 Add a callback function that will be called when an event occurs. 2854 Consider using `add_callback()` instead. 2855 """ 2856 if not self.interactor: 2857 return -1 2858 event_name = utils.get_vtk_name_event(event_name) 2859 idd = self.interactor.AddObserver(event_name, func, priority) 2860 return idd
Add a callback function that will be called when an event occurs.
Consider using add_callback()
instead.
2862 def compute_world_coordinate( 2863 self, 2864 pos2d: MutableSequence[float], 2865 at=None, 2866 objs=(), 2867 bounds=(), 2868 offset=None, 2869 pixeltol=None, 2870 worldtol=None, 2871 ) -> np.ndarray: 2872 """ 2873 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2874 If a set of meshes is passed then points are placed onto these. 2875 2876 Arguments: 2877 pos2d : (list) 2878 2D screen coordinates point. 2879 at : (int) 2880 renderer number. 2881 objs : (list) 2882 list of Mesh objects to project the point onto. 2883 bounds : (list) 2884 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2885 offset : (float) 2886 specify an offset value. 2887 pixeltol : (int) 2888 screen tolerance in pixels. 2889 worldtol : (float) 2890 world coordinates tolerance. 2891 2892 Returns: 2893 numpy array, the point in 3D world coordinates. 2894 2895 Examples: 2896 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2897 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2898 2899 ![](https://vedo.embl.es/images/basic/mousehover3.jpg) 2900 """ 2901 if at is not None: 2902 renderer = self.renderers[at] 2903 else: 2904 renderer = self.renderer 2905 2906 if not objs: 2907 pp = vtki.vtkFocalPlanePointPlacer() 2908 else: 2909 pps = vtki.vtkPolygonalSurfacePointPlacer() 2910 for ob in objs: 2911 pps.AddProp(ob.actor) 2912 pp = pps # type: ignore 2913 2914 if len(bounds) == 6: 2915 pp.SetPointBounds(bounds) 2916 if pixeltol: 2917 pp.SetPixelTolerance(pixeltol) 2918 if worldtol: 2919 pp.SetWorldTolerance(worldtol) 2920 if offset: 2921 pp.SetOffset(offset) 2922 2923 worldPos: MutableSequence[float] = [0, 0, 0] 2924 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2925 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2926 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2927 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2928 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:
2930 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2931 """ 2932 Given a 3D points in the current renderer (or full window), 2933 find the screen pixel coordinates. 2934 2935 Example: 2936 ```python 2937 from vedo import * 2938 2939 elli = Ellipsoid().point_size(5) 2940 2941 plt = Plotter() 2942 plt.show(elli, "Press q to continue and print the info") 2943 2944 xyscreen = plt.compute_screen_coordinates(elli) 2945 print('xyscreen coords:', xyscreen) 2946 2947 # simulate an event happening at one point 2948 event = plt.fill_event(pos=xyscreen[123]) 2949 print(event) 2950 ``` 2951 """ 2952 try: 2953 obj = obj.vertices 2954 except AttributeError: 2955 pass 2956 2957 if utils.is_sequence(obj): 2958 pts = obj 2959 p2d = [] 2960 cs = vtki.vtkCoordinate() 2961 cs.SetCoordinateSystemToWorld() 2962 cs.SetViewport(self.renderer) 2963 for p in pts: 2964 cs.SetValue(p) 2965 if full_window: 2966 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2967 else: 2968 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2969 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)
2971 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2972 """ 2973 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2974 2975 Returns a frustum Mesh that contains the visible field of view. 2976 This can be used to select objects in a scene or select vertices. 2977 2978 Example: 2979 ```python 2980 from vedo import * 2981 2982 settings.enable_default_mouse_callbacks = False 2983 2984 def mode_select(objs): 2985 print("Selected objects:", objs) 2986 d0 = mode.start_x, mode.start_y # display coords 2987 d1 = mode.end_x, mode.end_y 2988 2989 frustum = plt.pick_area(d0, d1) 2990 col = np.random.randint(0, 10) 2991 infru = frustum.inside_points(mesh) 2992 infru.point_size(10).color(col) 2993 plt.add(frustum, infru).render() 2994 2995 mesh = Mesh(dataurl+"cow.vtk") 2996 mesh.color("k5").linewidth(1) 2997 2998 mode = interactor_modes.BlenderStyle() 2999 mode.callback_select = mode_select 3000 3001 plt = Plotter().user_mode(mode) 3002 plt.show(mesh, axes=1) 3003 ``` 3004 """ 3005 if at is not None: 3006 ren = self.renderers[at] 3007 else: 3008 ren = self.renderer 3009 area_picker = vtki.vtkAreaPicker() 3010 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 3011 planes = area_picker.GetFrustum() 3012 3013 fru = vtki.new("FrustumSource") 3014 fru.SetPlanes(planes) 3015 fru.ShowLinesOff() 3016 fru.Update() 3017 3018 afru = vedo.Mesh(fru.GetOutput()) 3019 afru.alpha(0.1).lw(1).pickable(False) 3020 afru.name = "Frustum" 3021 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)
3144 def show( 3145 self, 3146 *objects, 3147 at=None, 3148 axes=None, 3149 resetcam=None, 3150 zoom=False, 3151 interactive=None, 3152 viewup="", 3153 azimuth=0.0, 3154 elevation=0.0, 3155 roll=0.0, 3156 camera=None, 3157 mode=None, 3158 rate=None, 3159 bg=None, 3160 bg2=None, 3161 size=None, 3162 title=None, 3163 screenshot="", 3164 ) -> Any: 3165 """ 3166 Render a list of objects. 3167 3168 Arguments: 3169 at : (int) 3170 number of the renderer to plot to, in case of more than one exists 3171 3172 axes : (int) 3173 axis type-1 can be fully customized by passing a dictionary. 3174 Check `addons.Axes()` for the full list of options. 3175 set the type of axes to be shown: 3176 - 0, no axes 3177 - 1, draw three gray grid walls 3178 - 2, show cartesian axes from (0,0,0) 3179 - 3, show positive range of cartesian axes from (0,0,0) 3180 - 4, show a triad at bottom left 3181 - 5, show a cube at bottom left 3182 - 6, mark the corners of the bounding box 3183 - 7, draw a 3D ruler at each side of the cartesian axes 3184 - 8, show the `vtkCubeAxesActor` object 3185 - 9, show the bounding box outLine 3186 - 10, show three circles representing the maximum bounding box 3187 - 11, show a large grid on the x-y plane 3188 - 12, show polar axes 3189 - 13, draw a simple ruler at the bottom of the window 3190 3191 azimuth/elevation/roll : (float) 3192 move camera accordingly the specified value 3193 3194 viewup: str, list 3195 either `['x', 'y', 'z']` or a vector to set vertical direction 3196 3197 resetcam : (bool) 3198 re-adjust camera position to fit objects 3199 3200 camera : (dict, vtkCamera) 3201 camera parameters can further be specified with a dictionary 3202 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3203 - pos, `(list)`, the position of the camera in world coordinates 3204 - focal_point `(list)`, the focal point of the camera in world coordinates 3205 - viewup `(list)`, the view up direction for the camera 3206 - distance `(float)`, set the focal point to the specified distance from the camera position. 3207 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3208 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3209 in world-coordinate distances. The default is 1. 3210 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3211 This method has no effect in perspective projection mode. 3212 3213 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3214 plane to be set a distance 'thickness' beyond the near clipping plane. 3215 3216 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3217 measured in degrees. The default angle is 30 degrees. 3218 This method has no effect in parallel projection mode. 3219 The formula for setting the angle up for perfect perspective viewing is: 3220 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3221 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3222 3223 interactive : (bool) 3224 pause and interact with window (True) or continue execution (False) 3225 3226 rate : (float) 3227 maximum rate of `show()` in Hertz 3228 3229 mode : (int, str) 3230 set the type of interaction: 3231 - 0 = TrackballCamera [default] 3232 - 1 = TrackballActor 3233 - 2 = JoystickCamera 3234 - 3 = JoystickActor 3235 - 4 = Flight 3236 - 5 = RubberBand2D 3237 - 6 = RubberBand3D 3238 - 7 = RubberBandZoom 3239 - 8 = Terrain 3240 - 9 = Unicam 3241 - 10 = Image 3242 - Check out `vedo.interaction_modes` for more options. 3243 3244 bg : (str, list) 3245 background color in RGB format, or string name 3246 3247 bg2 : (str, list) 3248 second background color to create a gradient background 3249 3250 size : (str, list) 3251 size of the window, e.g. size="fullscreen", or size=[600,400] 3252 3253 title : (str) 3254 window title text 3255 3256 screenshot : (str) 3257 save a screenshot of the window to file 3258 """ 3259 3260 if vedo.settings.dry_run_mode >= 2: 3261 return self 3262 3263 if self.wx_widget: 3264 return self 3265 3266 if self.renderers: # in case of notebooks 3267 3268 if at is None: 3269 at = self.renderers.index(self.renderer) 3270 3271 else: 3272 3273 if at >= len(self.renderers): 3274 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3275 vedo.logger.error(t) 3276 return self 3277 3278 self.renderer = self.renderers[at] 3279 3280 if title is not None: 3281 self.title = title 3282 3283 if size is not None: 3284 self.size = size 3285 if self.size[0] == "f": # full screen 3286 self.size = "fullscreen" 3287 self.window.SetFullScreen(True) 3288 self.window.BordersOn() 3289 else: 3290 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3291 3292 if vedo.settings.default_backend == "vtk": 3293 if str(bg).endswith(".hdr"): 3294 self._add_skybox(bg) 3295 else: 3296 if bg is not None: 3297 self.backgrcol = vedo.get_color(bg) 3298 self.renderer.SetBackground(self.backgrcol) 3299 if bg2 is not None: 3300 self.renderer.GradientBackgroundOn() 3301 self.renderer.SetBackground2(vedo.get_color(bg2)) 3302 3303 if axes is not None: 3304 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3305 objects = list(objects) 3306 objects.append(axes) # move it into the list of normal things to show 3307 axes = 0 3308 self.axes = axes 3309 3310 if interactive is not None: 3311 self._interactive = interactive 3312 if self.offscreen: 3313 self._interactive = False 3314 3315 # camera stuff 3316 if resetcam is not None: 3317 self.resetcam = resetcam 3318 3319 if camera is not None: 3320 self.resetcam = False 3321 viewup = "" 3322 if isinstance(camera, vtki.vtkCamera): 3323 cameracopy = vtki.vtkCamera() 3324 cameracopy.DeepCopy(camera) 3325 self.camera = cameracopy 3326 else: 3327 self.camera = utils.camera_from_dict(camera) 3328 3329 self.add(objects) 3330 3331 # Backend ############################################################### 3332 if vedo.settings.default_backend in ["k3d", "panel"]: 3333 return backends.get_notebook_backend(self.objects) 3334 ######################################################################### 3335 3336 for ia in utils.flatten(objects): 3337 try: 3338 # fix gray color labels and title to white or black 3339 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3340 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3341 c = (0.9, 0.9, 0.9) 3342 if np.sum(self.renderer.GetBackground()) > 1.5: 3343 c = (0.1, 0.1, 0.1) 3344 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3345 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3346 except AttributeError: 3347 pass 3348 3349 if self.sharecam: 3350 for r in self.renderers: 3351 r.SetActiveCamera(self.camera) 3352 3353 if self.axes is not None: 3354 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3355 bns = self.renderer.ComputeVisiblePropBounds() 3356 addons.add_global_axes(self.axes, bounds=bns) 3357 3358 # Backend ############################################################### 3359 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3360 return backends.get_notebook_backend() 3361 ######################################################################### 3362 3363 if self.resetcam: 3364 self.renderer.ResetCamera() 3365 3366 if len(self.renderers) > 1: 3367 self.add_renderer_frame() 3368 3369 if vedo.settings.default_backend == "2d" and not zoom: 3370 zoom = "tightest" 3371 3372 if zoom: 3373 if zoom == "tight": 3374 self.reset_camera(tight=0.04) 3375 elif zoom == "tightest": 3376 self.reset_camera(tight=0.0001) 3377 else: 3378 self.camera.Zoom(zoom) 3379 if elevation: 3380 self.camera.Elevation(elevation) 3381 if azimuth: 3382 self.camera.Azimuth(azimuth) 3383 if roll: 3384 self.camera.Roll(roll) 3385 3386 if len(viewup) > 0: 3387 b = self.renderer.ComputeVisiblePropBounds() 3388 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3389 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3390 if viewup == "x": 3391 sz = np.linalg.norm(sz) 3392 self.camera.SetViewUp([1, 0, 0]) 3393 self.camera.SetPosition(cm + sz) 3394 elif viewup == "y": 3395 sz = np.linalg.norm(sz) 3396 self.camera.SetViewUp([0, 1, 0]) 3397 self.camera.SetPosition(cm + sz) 3398 elif viewup == "z": 3399 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3400 self.camera.SetViewUp([0, 0, 1]) 3401 self.camera.SetPosition(cm + 2 * sz) 3402 elif utils.is_sequence(viewup): 3403 sz = np.linalg.norm(sz) 3404 self.camera.SetViewUp(viewup) 3405 cpos = np.cross([0, 1, 0], viewup) 3406 self.camera.SetPosition(cm - 2 * sz * cpos) 3407 3408 self.renderer.ResetCameraClippingRange() 3409 3410 self.initialize_interactor() 3411 3412 if vedo.settings.immediate_rendering: 3413 self.window.Render() ##################### <-------------- Render 3414 3415 if self.interactor: # can be offscreen or not the vtk backend.. 3416 3417 self.window.SetWindowName(self.title) 3418 3419 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3420 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3421 # print(pic.dataset)# Array 0 name PNGImage 3422 # self.window.SetIcon(pic.dataset) 3423 3424 try: 3425 # Needs "pip install pyobjc" on Mac OSX 3426 if ( 3427 self._cocoa_initialized is False 3428 and "Darwin" in vedo.sys_platform 3429 and not self.offscreen 3430 ): 3431 self._cocoa_initialized = True 3432 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3433 pid = os.getpid() 3434 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3435 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3436 except: 3437 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3438 pass 3439 3440 # Set the interaction style 3441 if mode is not None: 3442 self.user_mode(mode) 3443 if self.qt_widget and mode is None: 3444 self.user_mode(0) 3445 3446 if screenshot: 3447 self.screenshot(screenshot) 3448 3449 if self._interactive: 3450 self.interactor.Start() 3451 if self._must_close_now: 3452 self.interactor.GetRenderWindow().Finalize() 3453 self.interactor.TerminateApp() 3454 self.camera = None 3455 self.renderer = None 3456 self.renderers = [] 3457 self.window = None 3458 self.interactor = None 3459 return self 3460 3461 if rate: 3462 if self.clock is None: # set clock and limit rate 3463 self._clockt0 = time.time() 3464 self.clock = 0.0 3465 else: 3466 t = time.time() - self._clockt0 3467 elapsed = t - self.clock 3468 mint = 1.0 / rate 3469 if elapsed < mint: 3470 time.sleep(mint - elapsed) 3471 self.clock = time.time() - self._clockt0 3472 3473 # 2d #################################################################### 3474 if vedo.settings.default_backend in ["2d"]: 3475 return backends.get_notebook_backend() 3476 ######################################################################### 3477 3478 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
3481 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3482 """Add a draggable inset space into a renderer. 3483 3484 Arguments: 3485 at : (int) 3486 specify the renderer number 3487 pos : (list) 3488 icon position in the range [1-4] indicating one of the 4 corners, 3489 or it can be a tuple (x,y) as a fraction of the renderer size. 3490 size : (float) 3491 size of the square inset 3492 draggable : (bool) 3493 if True the subrenderer space can be dragged around 3494 c : (color) 3495 color of the inset frame when dragged 3496 3497 Examples: 3498 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3499 3500 ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg) 3501 """ 3502 if not self.interactor: 3503 return None 3504 3505 if not self.renderer: 3506 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3507 return None 3508 3509 options = dict(options) 3510 pos = options.pop("pos", 0) 3511 size = options.pop("size", 0.1) 3512 c = options.pop("c", "lb") 3513 at = options.pop("at", None) 3514 draggable = options.pop("draggable", True) 3515 3516 r, g, b = vedo.get_color(c) 3517 widget = vtki.vtkOrientationMarkerWidget() 3518 widget.SetOutlineColor(r, g, b) 3519 if len(objects) == 1: 3520 widget.SetOrientationMarker(objects[0].actor) 3521 else: 3522 widget.SetOrientationMarker(vedo.Assembly(objects)) 3523 3524 widget.SetInteractor(self.interactor) 3525 3526 if utils.is_sequence(pos): 3527 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3528 else: 3529 if pos < 2: 3530 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3531 elif pos == 2: 3532 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3533 elif pos == 3: 3534 widget.SetViewport(0, 0, size * 2, size * 2) 3535 elif pos == 4: 3536 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3537 widget.EnabledOn() 3538 widget.SetInteractive(draggable) 3539 if at is not None and at < len(self.renderers): 3540 widget.SetCurrentRenderer(self.renderers[at]) 3541 else: 3542 widget.SetCurrentRenderer(self.renderer) 3543 self.widgets.append(widget) 3544 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:
3546 def clear(self, at=None, deep=False) -> Self: 3547 """Clear the scene from all meshes and volumes.""" 3548 if at is not None: 3549 renderer = self.renderers[at] 3550 else: 3551 renderer = self.renderer 3552 if not renderer: 3553 return self 3554 3555 if deep: 3556 renderer.RemoveAllViewProps() 3557 else: 3558 for ob in set( 3559 self.get_meshes() 3560 + self.get_volumes() 3561 + self.objects 3562 + self.axes_instances 3563 ): 3564 if isinstance(ob, vedo.shapes.Text2D): 3565 continue 3566 self.remove(ob) 3567 try: 3568 if ob.scalarbar: 3569 self.remove(ob.scalarbar) 3570 except AttributeError: 3571 pass 3572 return self
Clear the scene from all meshes and volumes.
3574 def break_interaction(self) -> Self: 3575 """Break window interaction and return to the python execution flow""" 3576 if self.interactor: 3577 self.check_actors_trasform() 3578 self.interactor.ExitCallback() 3579 return self
Break window interaction and return to the python execution flow
3581 def freeze(self, value=True) -> Self: 3582 """Freeze the current renderer. Use this with `sharecam=False`.""" 3583 if not self.interactor: 3584 return self 3585 if not self.renderer: 3586 return self 3587 self.renderer.SetInteractive(not value) 3588 return self
Freeze the current renderer. Use this with sharecam=False
.
3590 def user_mode(self, mode) -> Self: 3591 """ 3592 Modify the user interaction mode. 3593 3594 Examples: 3595 ```python 3596 from vedo import * 3597 mode = interactor_modes.MousePan() 3598 mesh = Mesh(dataurl+"cow.vtk") 3599 plt = Plotter().user_mode(mode) 3600 plt.show(mesh, axes=1) 3601 ``` 3602 See also: 3603 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3604 """ 3605 if not self.interactor: 3606 return self 3607 3608 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3609 # print("Current style:", curr_style) 3610 if curr_style.endswith("Actor"): 3611 self.check_actors_trasform() 3612 3613 if isinstance(mode, (str, int)): 3614 # Set the style of interaction 3615 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3616 if mode in (0, "TrackballCamera"): 3617 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3618 self.interactor.RemoveObservers("CharEvent") 3619 elif mode in (1, "TrackballActor"): 3620 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3621 elif mode in (2, "JoystickCamera"): 3622 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3623 elif mode in (3, "JoystickActor"): 3624 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3625 elif mode in (4, "Flight"): 3626 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3627 elif mode in (5, "RubberBand2D"): 3628 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3629 elif mode in (6, "RubberBand3D"): 3630 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3631 elif mode in (7, "RubberBandZoom"): 3632 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3633 elif mode in (8, "Terrain"): 3634 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3635 elif mode in (9, "Unicam"): 3636 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3637 elif mode in (10, "Image", "image", "2d"): 3638 astyle = vtki.new("InteractorStyleImage") 3639 astyle.SetInteractionModeToImage3D() 3640 self.interactor.SetInteractorStyle(astyle) 3641 else: 3642 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3643 3644 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3645 # set a custom interactor style 3646 if hasattr(mode, "interactor"): 3647 mode.interactor = self.interactor 3648 mode.renderer = self.renderer # type: ignore 3649 mode.SetInteractor(self.interactor) 3650 mode.SetDefaultRenderer(self.renderer) 3651 self.interactor.SetInteractorStyle(mode) 3652 3653 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
3655 def close(self) -> Self: 3656 """Close the plotter.""" 3657 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3658 vedo.last_figure = None 3659 self.last_event = None 3660 self.sliders = [] 3661 self.buttons = [] 3662 self.widgets = [] 3663 self.hover_legends = [] 3664 self.background_renderer = None 3665 self._extralight = None 3666 3667 self.hint_widget = None 3668 self.cutter_widget = None 3669 3670 if vedo.settings.dry_run_mode >= 2: 3671 return self 3672 3673 if not hasattr(self, "window"): 3674 return self 3675 if not self.window: 3676 return self 3677 if not hasattr(self, "interactor"): 3678 return self 3679 if not self.interactor: 3680 return self 3681 3682 ################################################### 3683 try: 3684 if "Darwin" in vedo.sys_platform: 3685 self.interactor.ProcessEvents() 3686 except: 3687 pass 3688 3689 self._must_close_now = True 3690 3691 if vedo.plotter_instance == self: 3692 vedo.plotter_instance = None 3693 3694 if self.interactor and self._interactive: 3695 self.break_interaction() 3696 elif self._must_close_now: 3697 # dont call ExitCallback here 3698 if self.interactor: 3699 self.break_interaction() 3700 self.interactor.GetRenderWindow().Finalize() 3701 self.interactor.TerminateApp() 3702 self.camera = None 3703 self.renderer = None 3704 self.renderers = [] 3705 self.window = None 3706 self.interactor = None 3707 return self
Close the plotter.
3709 @property 3710 def camera(self): 3711 """Return the current active camera.""" 3712 if self.renderer: 3713 return self.renderer.GetActiveCamera()
Return the current active camera.
3722 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3723 """ 3724 Take a screenshot of the Plotter window. 3725 3726 Arguments: 3727 scale : (int) 3728 set image magnification as an integer multiplicating factor 3729 asarray : (bool) 3730 return a numpy array of the image instead of writing a file 3731 3732 Warning: 3733 If you get black screenshots try to set `interactive=False` in `show()` 3734 then call `screenshot()` and `plt.interactive()` afterwards. 3735 3736 Example: 3737 ```py 3738 from vedo import * 3739 sphere = Sphere().linewidth(1) 3740 plt = show(sphere, interactive=False) 3741 plt.screenshot('image.png') 3742 plt.interactive() 3743 plt.close() 3744 ``` 3745 3746 Example: 3747 ```py 3748 from vedo import * 3749 sphere = Sphere().linewidth(1) 3750 plt = show(sphere, interactive=False) 3751 plt.screenshot('anotherimage.png') 3752 plt.interactive() 3753 plt.close() 3754 ``` 3755 """ 3756 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()
3758 def toimage(self, scale=1) -> "vedo.image.Image": 3759 """ 3760 Generate a `Image` object from the current rendering window. 3761 3762 Arguments: 3763 scale : (int) 3764 set image magnification as an integer multiplicating factor 3765 """ 3766 if vedo.settings.screeshot_large_image: 3767 w2if = vtki.new("RenderLargeImage") 3768 w2if.SetInput(self.renderer) 3769 w2if.SetMagnification(scale) 3770 else: 3771 w2if = vtki.new("WindowToImageFilter") 3772 w2if.SetInput(self.window) 3773 if hasattr(w2if, "SetScale"): 3774 w2if.SetScale(scale, scale) 3775 if vedo.settings.screenshot_transparent_background: 3776 w2if.SetInputBufferTypeToRGBA() 3777 w2if.ReadFrontBufferOff() # read from the back buffer 3778 w2if.Update() 3779 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
3781 def export(self, filename="scene.npz", binary=False) -> Self: 3782 """ 3783 Export scene to file to HTML, X3D or Numpy file. 3784 3785 Examples: 3786 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3787 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3788 """ 3789 vedo.file_io.export_window(filename, binary=binary) 3790 return self
3792 def color_picker(self, xy, verbose=False): 3793 """Pick color of specific (x,y) pixel on the screen.""" 3794 w2if = vtki.new("WindowToImageFilter") 3795 w2if.SetInput(self.window) 3796 w2if.ReadFrontBufferOff() 3797 w2if.Update() 3798 nx, ny = self.window.GetSize() 3799 varr = w2if.GetOutput().GetPointData().GetScalars() 3800 3801 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3802 x, y = int(xy[0]), int(xy[1]) 3803 if y < ny and x < nx: 3804 3805 rgb = arr[y, x] 3806 3807 if verbose: 3808 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3809 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3810 vedo.printc("█", c=[0, rgb[1], 0], end="") 3811 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3812 vedo.printc("] = ", end="") 3813 cnm = vedo.get_color_name(rgb) 3814 if np.sum(rgb) < 150: 3815 vedo.printc( 3816 rgb.tolist(), 3817 vedo.colors.rgb2hex(np.array(rgb) / 255), 3818 c="w", 3819 bc=rgb, 3820 invert=1, 3821 end="", 3822 ) 3823 vedo.printc(" -> " + cnm, invert=1, c="w") 3824 else: 3825 vedo.printc( 3826 rgb.tolist(), 3827 vedo.colors.rgb2hex(np.array(rgb) / 255), 3828 c=rgb, 3829 end="", 3830 ) 3831 vedo.printc(" -> " + cnm, c=cnm) 3832 3833 return rgb 3834 3835 return None
Pick color of specific (x,y) pixel on the screen.
118def show( 119 *objects, 120 at=None, 121 shape=(1, 1), 122 N=None, 123 pos=(0, 0), 124 size="auto", 125 screensize="auto", 126 title="vedo", 127 bg="white", 128 bg2=None, 129 axes=None, 130 interactive=None, 131 offscreen=False, 132 sharecam=True, 133 resetcam=True, 134 zoom=None, 135 viewup="", 136 azimuth=0.0, 137 elevation=0.0, 138 roll=0.0, 139 camera=None, 140 mode=None, 141 screenshot="", 142 new=False, 143) -> Union[Self, None]: 144 """ 145 Create on the fly an instance of class Plotter and show the object(s) provided. 146 147 Arguments: 148 at : (int) 149 number of the renderer to plot to, in case of more than one exists 150 shape : (list, str) 151 Number of sub-render windows inside of the main window. E.g.: 152 specify two across with shape=(2,1) and a two by two grid 153 with shape=(2, 2). By default there is only one renderer. 154 155 Can also accept a shape as string descriptor. E.g.: 156 - shape="3|1" means 3 plots on the left and 1 on the right, 157 - shape="4/2" means 4 plots on top of 2 at bottom. 158 N : (int) 159 number of desired sub-render windows arranged automatically in a grid 160 pos : (list) 161 position coordinates of the top-left corner of the rendering window 162 on the screen 163 size : (list) 164 size of the rendering window 165 screensize : (list) 166 physical size of the monitor screen 167 title : (str) 168 window title 169 bg : (color) 170 background color or specify jpg image file name with path 171 bg2 : (color) 172 background color of a gradient towards the top 173 axes : (int) 174 set the type of axes to be shown: 175 - 0, no axes 176 - 1, draw three gray grid walls 177 - 2, show cartesian axes from (0,0,0) 178 - 3, show positive range of cartesian axes from (0,0,0) 179 - 4, show a triad at bottom left 180 - 5, show a cube at bottom left 181 - 6, mark the corners of the bounding box 182 - 7, draw a 3D ruler at each side of the cartesian axes 183 - 8, show the `vtkCubeAxesActor` object 184 - 9, show the bounding box outLine 185 - 10, show three circles representing the maximum bounding box 186 - 11, show a large grid on the x-y plane 187 - 12, show polar axes 188 - 13, draw a simple ruler at the bottom of the window 189 - 14: draw a `CameraOrientationWidget` 190 191 Axis type-1 can be fully customized by passing a dictionary. 192 Check `vedo.addons.Axes()` for the full list of options. 193 azimuth/elevation/roll : (float) 194 move camera accordingly the specified value 195 viewup : (str, list) 196 either `['x', 'y', 'z']` or a vector to set vertical direction 197 resetcam : (bool) 198 re-adjust camera position to fit objects 199 camera : (dict, vtkCamera) 200 camera parameters can further be specified with a dictionary 201 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 202 - **pos** (list), the position of the camera in world coordinates 203 - **focal_point** (list), the focal point of the camera in world coordinates 204 - **viewup** (list), the view up direction for the camera 205 - **distance** (float), set the focal point to the specified distance from the camera position. 206 - **clipping_range** (float), distance of the near and far clipping planes along the direction of projection. 207 - **parallel_scale** (float), 208 scaling used for a parallel projection, i.e. the height of the viewport 209 in world-coordinate distances. The default is 1. Note that the "scale" parameter works as 210 an "inverse scale", larger numbers produce smaller images. 211 This method has no effect in perspective projection mode. 212 - **thickness** (float), 213 set the distance between clipping planes. This method adjusts the far clipping 214 plane to be set a distance 'thickness' beyond the near clipping plane. 215 - **view_angle** (float), 216 the camera view angle, which is the angular height of the camera view 217 measured in degrees. The default angle is 30 degrees. 218 This method has no effect in parallel projection mode. 219 The formula for setting the angle up for perfect perspective viewing is: 220 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 221 (measured by holding a ruler up to your screen) and d is the distance 222 from your eyes to the screen. 223 interactive : (bool) 224 pause and interact with window (True) or continue execution (False) 225 rate : (float) 226 maximum rate of `show()` in Hertz 227 mode : (int, str) 228 set the type of interaction: 229 - 0 = TrackballCamera [default] 230 - 1 = TrackballActor 231 - 2 = JoystickCamera 232 - 3 = JoystickActor 233 - 4 = Flight 234 - 5 = RubberBand2D 235 - 6 = RubberBand3D 236 - 7 = RubberBandZoom 237 - 8 = Terrain 238 - 9 = Unicam 239 - 10 = Image 240 new : (bool) 241 if set to `True`, a call to show will instantiate 242 a new Plotter object (a new window) instead of reusing the first created. 243 If new is `True`, but the existing plotter was instantiated with a different 244 argument for `offscreen`, `new` is ignored and a new Plotter is created anyway. 245 """ 246 if len(objects) == 0: 247 objects = None 248 elif len(objects) == 1: 249 objects = objects[0] 250 else: 251 objects = utils.flatten(objects) 252 253 # If a plotter instance is already present, check if the offscreen argument 254 # is the same as the one requested by the user. If not, create a new 255 # plotter instance (see https://github.com/marcomusy/vedo/issues/1026) 256 if vedo.plotter_instance and vedo.plotter_instance.offscreen != offscreen: 257 new = True 258 259 if vedo.plotter_instance and not new: # Plotter exists 260 plt = vedo.plotter_instance 261 262 else: # Plotter must be created 263 264 if utils.is_sequence(at): # user passed a sequence for "at" 265 266 if not utils.is_sequence(objects): 267 vedo.logger.error("in show() input must be a list.") 268 raise RuntimeError() 269 if len(at) != len(objects): 270 vedo.logger.error("in show() lists 'input' and 'at' must have equal lengths") 271 raise RuntimeError() 272 if shape == (1, 1) and N is None: 273 N = max(at) + 1 274 275 elif at is None and (N or shape != (1, 1)): 276 277 if not utils.is_sequence(objects): 278 e = "in show(), N or shape is set, but input is not a sequence\n" 279 e += " you may need to specify e.g. at=0" 280 vedo.logger.error(e) 281 raise RuntimeError() 282 at = list(range(len(objects))) 283 284 plt = Plotter( 285 shape=shape, 286 N=N, 287 pos=pos, 288 size=size, 289 screensize=screensize, 290 title=title, 291 axes=axes, 292 sharecam=sharecam, 293 resetcam=resetcam, 294 interactive=interactive, 295 offscreen=offscreen, 296 bg=bg, 297 bg2=bg2, 298 ) 299 300 if vedo.settings.dry_run_mode >= 2: 301 return plt 302 303 # use _plt_to_return because plt.show() can return a k3d plot 304 _plt_to_return = None 305 306 if utils.is_sequence(at): 307 308 for i, act in enumerate(objects): 309 _plt_to_return = plt.show( 310 act, 311 at=i, 312 zoom=zoom, 313 resetcam=resetcam, 314 viewup=viewup, 315 azimuth=azimuth, 316 elevation=elevation, 317 roll=roll, 318 camera=camera, 319 interactive=False, 320 mode=mode, 321 screenshot=screenshot, 322 bg=bg, 323 bg2=bg2, 324 axes=axes, 325 ) 326 327 if ( 328 interactive 329 or len(at) == N 330 or (isinstance(shape[0], int) and len(at) == shape[0] * shape[1]) 331 ): 332 # note that shape can be a string 333 if plt.interactor and not offscreen and (interactive is None or interactive): 334 plt.interactor.Start() 335 if plt._must_close_now: 336 plt.interactor.GetRenderWindow().Finalize() 337 plt.interactor.TerminateApp() 338 plt.interactor = None 339 plt.window = None 340 plt.renderer = None 341 plt.renderers = [] 342 plt.camera = None 343 344 else: 345 346 _plt_to_return = plt.show( 347 objects, 348 at=at, 349 zoom=zoom, 350 resetcam=resetcam, 351 viewup=viewup, 352 azimuth=azimuth, 353 elevation=elevation, 354 roll=roll, 355 camera=camera, 356 interactive=interactive, 357 mode=mode, 358 screenshot=screenshot, 359 bg=bg, 360 bg2=bg2, 361 axes=axes, 362 ) 363 364 return _plt_to_return
Create on the fly an instance of class Plotter and show the object(s) provided.
Arguments:
- at : (int) number of the renderer to plot to, in case of more than one exists
shape : (list, str) Number of sub-render windows inside of the main window. E.g.: specify two across with shape=(2,1) and a two by two grid with shape=(2, 2). By default there is only one renderer.
Can also accept a shape as string descriptor. E.g.:
- shape="3|1" means 3 plots on the left and 1 on the right,
- shape="4/2" means 4 plots on top of 2 at bottom.
- N : (int) number of desired sub-render windows arranged automatically in a grid
- pos : (list) position coordinates of the top-left corner of the rendering window on the screen
- size : (list) size of the rendering window
- screensize : (list) physical size of the monitor screen
- title : (str) window title
- bg : (color) background color or specify jpg image file name with path
- bg2 : (color) background color of a gradient towards the top
axes : (int) set the type of axes to be shown:
- 0, no axes
- 1, draw three gray grid walls
- 2, show cartesian axes from (0,0,0)
- 3, show positive range of cartesian axes from (0,0,0)
- 4, show a triad at bottom left
- 5, show a cube at bottom left
- 6, mark the corners of the bounding box
- 7, draw a 3D ruler at each side of the cartesian axes
- 8, show the
vtkCubeAxesActor
object - 9, show the bounding box outLine
- 10, show three circles representing the maximum bounding box
- 11, show a large grid on the x-y plane
- 12, show polar axes
- 13, draw a simple ruler at the bottom of the window
- 14: draw a
CameraOrientationWidget
Axis type-1 can be fully customized by passing a dictionary. Check
vedo.addons.Axes()
for the full list of options.- azimuth/elevation/roll : (float) move camera accordingly the specified value
- viewup : (str, list)
either
['x', 'y', 'z']
or a vector to set vertical direction - resetcam : (bool) re-adjust camera position to fit objects
- camera : (dict, vtkCamera)
camera parameters can further be specified with a dictionary
assigned to the
camera
keyword (E.g.show(camera={'pos':(1,2,3), 'thickness':1000,})
):- pos (list), the position of the camera in world coordinates
- focal_point (list), the focal point of the camera in world coordinates
- viewup (list), the view up direction for the camera
- distance (float), set the focal point to the specified distance from the camera position.
- clipping_range (float), distance of the near and far clipping planes along the direction of projection.
- parallel_scale (float), scaling used for a parallel projection, i.e. the height of the viewport in world-coordinate distances. The default is 1. Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. This method has no effect in perspective projection mode.
- thickness (float), set the distance between clipping planes. This method adjusts the far clipping plane to be set a distance 'thickness' beyond the near clipping plane.
- view_angle (float), the camera view angle, which is the angular height of the camera view measured in degrees. The default angle is 30 degrees. This method has no effect in parallel projection mode. The formula for setting the angle up for perfect perspective viewing is: angle = 2*atan((h/2)/d) where h is the height of the RenderWindow (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen.
- interactive : (bool) pause and interact with window (True) or continue execution (False)
- rate : (float)
maximum rate of
show()
in Hertz - mode : (int, str)
set the type of interaction:
- 0 = TrackballCamera [default]
- 1 = TrackballActor
- 2 = JoystickCamera
- 3 = JoystickActor
- 4 = Flight
- 5 = RubberBand2D
- 6 = RubberBand3D
- 7 = RubberBandZoom
- 8 = Terrain
- 9 = Unicam
- 10 = Image
- new : (bool)
if set to
True
, a call to show will instantiate a new Plotter object (a new window) instead of reusing the first created. If new isTrue
, but the existing plotter was instantiated with a different argument foroffscreen
,new
is ignored and a new Plotter is created anyway.
367def close() -> None: 368 """Close the last created Plotter instance if it exists.""" 369 if not vedo.plotter_instance: 370 return 371 vedo.plotter_instance.close() 372 return
Close the last created Plotter instance if it exists.