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 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, TypeError): 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[mode]) 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  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  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  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 coordinates 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  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  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  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  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  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  2829 """ 2830 if action in ("create", "start"): 2831 2832 if "Windows" in vedo.sys_platform: 2833 # otherwise on windows it gets stuck 2834 self.initialize_interactor() 2835 2836 if timer_id is not None: 2837 vedo.logger.warning("you set a timer_id but it will be ignored.") 2838 if one_shot: 2839 timer_id = self.interactor.CreateOneShotTimer(dt) 2840 else: 2841 timer_id = self.interactor.CreateRepeatingTimer(dt) 2842 return timer_id 2843 2844 elif action in ("destroy", "stop"): 2845 if timer_id is not None: 2846 self.interactor.DestroyTimer(timer_id) 2847 else: 2848 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2849 else: 2850 e = f"in timer_callback(). Cannot understand action: {action}\n" 2851 e += " allowed actions are: ['start', 'stop']. Skipped." 2852 vedo.logger.error(e) 2853 return timer_id 2854 2855 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2856 """ 2857 Add a callback function that will be called when an event occurs. 2858 Consider using `add_callback()` instead. 2859 """ 2860 if not self.interactor: 2861 return -1 2862 event_name = utils.get_vtk_name_event(event_name) 2863 idd = self.interactor.AddObserver(event_name, func, priority) 2864 return idd 2865 2866 def compute_world_coordinate( 2867 self, 2868 pos2d: MutableSequence[float], 2869 at=None, 2870 objs=(), 2871 bounds=(), 2872 offset=None, 2873 pixeltol=None, 2874 worldtol=None, 2875 ) -> np.ndarray: 2876 """ 2877 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2878 If a set of meshes is passed then points are placed onto these. 2879 2880 Arguments: 2881 pos2d : (list) 2882 2D screen coordinates point. 2883 at : (int) 2884 renderer number. 2885 objs : (list) 2886 list of Mesh objects to project the point onto. 2887 bounds : (list) 2888 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2889 offset : (float) 2890 specify an offset value. 2891 pixeltol : (int) 2892 screen tolerance in pixels. 2893 worldtol : (float) 2894 world coordinates tolerance. 2895 2896 Returns: 2897 numpy array, the point in 3D world coordinates. 2898 2899 Examples: 2900 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2901 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2902 2903  2904 """ 2905 if at is not None: 2906 renderer = self.renderers[at] 2907 else: 2908 renderer = self.renderer 2909 2910 if not objs: 2911 pp = vtki.vtkFocalPlanePointPlacer() 2912 else: 2913 pps = vtki.vtkPolygonalSurfacePointPlacer() 2914 for ob in objs: 2915 pps.AddProp(ob.actor) 2916 pp = pps # type: ignore 2917 2918 if len(bounds) == 6: 2919 pp.SetPointBounds(bounds) 2920 if pixeltol: 2921 pp.SetPixelTolerance(pixeltol) 2922 if worldtol: 2923 pp.SetWorldTolerance(worldtol) 2924 if offset: 2925 pp.SetOffset(offset) 2926 2927 worldPos: MutableSequence[float] = [0, 0, 0] 2928 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2929 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2930 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2931 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2932 return np.array(worldPos) 2933 2934 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2935 """ 2936 Given a 3D points in the current renderer (or full window), 2937 find the screen pixel coordinates. 2938 2939 Example: 2940 ```python 2941 from vedo import * 2942 2943 elli = Ellipsoid().point_size(5) 2944 2945 plt = Plotter() 2946 plt.show(elli, "Press q to continue and print the info") 2947 2948 xyscreen = plt.compute_screen_coordinates(elli) 2949 print('xyscreen coords:', xyscreen) 2950 2951 # simulate an event happening at one point 2952 event = plt.fill_event(pos=xyscreen[123]) 2953 print(event) 2954 ``` 2955 """ 2956 try: 2957 obj = obj.coordinates 2958 except AttributeError: 2959 pass 2960 2961 if utils.is_sequence(obj): 2962 pts = obj 2963 p2d = [] 2964 cs = vtki.vtkCoordinate() 2965 cs.SetCoordinateSystemToWorld() 2966 cs.SetViewport(self.renderer) 2967 for p in pts: 2968 cs.SetValue(p) 2969 if full_window: 2970 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2971 else: 2972 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2973 return np.array(p2d, dtype=int) 2974 2975 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2976 """ 2977 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2978 2979 Returns a frustum Mesh that contains the visible field of view. 2980 This can be used to select objects in a scene or select vertices. 2981 2982 Example: 2983 ```python 2984 from vedo import * 2985 2986 settings.enable_default_mouse_callbacks = False 2987 2988 def mode_select(objs): 2989 print("Selected objects:", objs) 2990 d0 = mode.start_x, mode.start_y # display coords 2991 d1 = mode.end_x, mode.end_y 2992 2993 frustum = plt.pick_area(d0, d1) 2994 col = np.random.randint(0, 10) 2995 infru = frustum.inside_points(mesh) 2996 infru.point_size(10).color(col) 2997 plt.add(frustum, infru).render() 2998 2999 mesh = Mesh(dataurl+"cow.vtk") 3000 mesh.color("k5").linewidth(1) 3001 3002 mode = interactor_modes.BlenderStyle() 3003 mode.callback_select = mode_select 3004 3005 plt = Plotter().user_mode(mode) 3006 plt.show(mesh, axes=1) 3007 ``` 3008 """ 3009 if at is not None: 3010 ren = self.renderers[at] 3011 else: 3012 ren = self.renderer 3013 area_picker = vtki.vtkAreaPicker() 3014 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 3015 planes = area_picker.GetFrustum() 3016 3017 fru = vtki.new("FrustumSource") 3018 fru.SetPlanes(planes) 3019 fru.ShowLinesOff() 3020 fru.Update() 3021 3022 afru = vedo.Mesh(fru.GetOutput()) 3023 afru.alpha(0.1).lw(1).pickable(False) 3024 afru.name = "Frustum" 3025 return afru 3026 3027 def _scan_input_return_acts(self, objs) -> Any: 3028 # scan the input and return a list of actors 3029 if not utils.is_sequence(objs): 3030 objs = [objs] 3031 3032 ################# 3033 wannabe_acts = [] 3034 for a in objs: 3035 3036 try: 3037 wannabe_acts.append(a.actor) 3038 except AttributeError: 3039 wannabe_acts.append(a) # already actor 3040 3041 try: 3042 wannabe_acts.append(a.scalarbar) 3043 except AttributeError: 3044 pass 3045 3046 try: 3047 for sh in a.shadows: 3048 wannabe_acts.append(sh.actor) 3049 except AttributeError: 3050 pass 3051 3052 try: 3053 wannabe_acts.append(a.trail.actor) 3054 if a.trail.shadows: # trails may also have shadows 3055 for sh in a.trail.shadows: 3056 wannabe_acts.append(sh.actor) 3057 except AttributeError: 3058 pass 3059 3060 ################# 3061 scanned_acts = [] 3062 for a in wannabe_acts: # scan content of list 3063 3064 if a is None: 3065 pass 3066 3067 elif isinstance(a, (vtki.vtkActor, vtki.vtkActor2D)): 3068 scanned_acts.append(a) 3069 3070 elif isinstance(a, str): 3071 # assume a 2D comment was given 3072 changed = False # check if one already exists so to just update text 3073 if self.renderer: # might be jupyter 3074 acs = self.renderer.GetActors2D() 3075 acs.InitTraversal() 3076 for i in range(acs.GetNumberOfItems()): 3077 act = acs.GetNextItem() 3078 if isinstance(act, vedo.shapes.Text2D): 3079 aposx, aposy = act.GetPosition() 3080 if aposx < 0.01 and aposy > 0.99: # "top-left" 3081 act.text(a) # update content! no appending nada 3082 changed = True 3083 break 3084 if not changed: 3085 out = vedo.shapes.Text2D(a) # append a new one 3086 scanned_acts.append(out) 3087 # scanned_acts.append(vedo.shapes.Text2D(a)) # naive version 3088 3089 elif isinstance(a, vtki.vtkPolyData): 3090 scanned_acts.append(vedo.Mesh(a).actor) 3091 3092 elif isinstance(a, vtki.vtkImageData): 3093 scanned_acts.append(vedo.Volume(a).actor) 3094 3095 elif isinstance(a, vedo.RectilinearGrid): 3096 scanned_acts.append(a.actor) 3097 3098 elif isinstance(a, vedo.StructuredGrid): 3099 scanned_acts.append(a.actor) 3100 3101 elif isinstance(a, vtki.vtkLight): 3102 scanned_acts.append(a) 3103 3104 elif isinstance(a, vedo.visual.LightKit): 3105 a.lightkit.AddLightsToRenderer(self.renderer) 3106 3107 elif isinstance(a, vtki.get_class("MultiBlockDataSet")): 3108 for i in range(a.GetNumberOfBlocks()): 3109 b = a.GetBlock(i) 3110 if isinstance(b, vtki.vtkPolyData): 3111 scanned_acts.append(vedo.Mesh(b).actor) 3112 elif isinstance(b, vtki.vtkImageData): 3113 scanned_acts.append(vedo.Volume(b).actor) 3114 3115 elif isinstance(a, (vtki.vtkProp, vtki.vtkInteractorObserver)): 3116 scanned_acts.append(a) 3117 3118 elif "trimesh" in str(type(a)): 3119 scanned_acts.append(utils.trimesh2vedo(a)) 3120 3121 elif "meshlab" in str(type(a)): 3122 if "MeshSet" in str(type(a)): 3123 for i in range(a.number_meshes()): 3124 if a.mesh_id_exists(i): 3125 scanned_acts.append(utils.meshlab2vedo(a.mesh(i))) 3126 else: 3127 scanned_acts.append(utils.meshlab2vedo(a)) 3128 3129 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 3130 import vedo.dolfin as vdlf 3131 3132 scanned_acts.append(vdlf.IMesh(a).actor) 3133 3134 elif "madcad" in str(type(a)): 3135 scanned_acts.append(utils.madcad2vedo(a).actor) 3136 3137 elif "TetgenIO" in str(type(a)): 3138 scanned_acts.append(vedo.TetMesh(a).shrink(0.9).c("pink7").actor) 3139 3140 elif "matplotlib.figure.Figure" in str(type(a)): 3141 scanned_acts.append(vedo.Image(a).clone2d("top-right", 0.6)) 3142 3143 else: 3144 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 3145 3146 return scanned_acts 3147 3148 def show( 3149 self, 3150 *objects, 3151 at=None, 3152 axes=None, 3153 resetcam=None, 3154 zoom=False, 3155 interactive=None, 3156 viewup="", 3157 azimuth=0.0, 3158 elevation=0.0, 3159 roll=0.0, 3160 camera=None, 3161 mode=None, 3162 rate=None, 3163 bg=None, 3164 bg2=None, 3165 size=None, 3166 title=None, 3167 screenshot="", 3168 ) -> Any: 3169 """ 3170 Render a list of objects. 3171 3172 Arguments: 3173 at : (int) 3174 number of the renderer to plot to, in case of more than one exists 3175 3176 axes : (int) 3177 axis type-1 can be fully customized by passing a dictionary. 3178 Check `addons.Axes()` for the full list of options. 3179 set the type of axes to be shown: 3180 - 0, no axes 3181 - 1, draw three gray grid walls 3182 - 2, show cartesian axes from (0,0,0) 3183 - 3, show positive range of cartesian axes from (0,0,0) 3184 - 4, show a triad at bottom left 3185 - 5, show a cube at bottom left 3186 - 6, mark the corners of the bounding box 3187 - 7, draw a 3D ruler at each side of the cartesian axes 3188 - 8, show the `vtkCubeAxesActor` object 3189 - 9, show the bounding box outLine 3190 - 10, show three circles representing the maximum bounding box 3191 - 11, show a large grid on the x-y plane 3192 - 12, show polar axes 3193 - 13, draw a simple ruler at the bottom of the window 3194 3195 azimuth/elevation/roll : (float) 3196 move camera accordingly the specified value 3197 3198 viewup: str, list 3199 either `['x', 'y', 'z']` or a vector to set vertical direction 3200 3201 resetcam : (bool) 3202 re-adjust camera position to fit objects 3203 3204 camera : (dict, vtkCamera) 3205 camera parameters can further be specified with a dictionary 3206 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3207 - pos, `(list)`, the position of the camera in world coordinates 3208 - focal_point `(list)`, the focal point of the camera in world coordinates 3209 - viewup `(list)`, the view up direction for the camera 3210 - distance `(float)`, set the focal point to the specified distance from the camera position. 3211 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3212 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3213 in world-coordinate distances. The default is 1. 3214 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3215 This method has no effect in perspective projection mode. 3216 3217 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3218 plane to be set a distance 'thickness' beyond the near clipping plane. 3219 3220 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3221 measured in degrees. The default angle is 30 degrees. 3222 This method has no effect in parallel projection mode. 3223 The formula for setting the angle up for perfect perspective viewing is: 3224 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3225 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3226 3227 interactive : (bool) 3228 pause and interact with window (True) or continue execution (False) 3229 3230 rate : (float) 3231 maximum rate of `show()` in Hertz 3232 3233 mode : (int, str) 3234 set the type of interaction: 3235 - 0 = TrackballCamera [default] 3236 - 1 = TrackballActor 3237 - 2 = JoystickCamera 3238 - 3 = JoystickActor 3239 - 4 = Flight 3240 - 5 = RubberBand2D 3241 - 6 = RubberBand3D 3242 - 7 = RubberBandZoom 3243 - 8 = Terrain 3244 - 9 = Unicam 3245 - 10 = Image 3246 - Check out `vedo.interaction_modes` for more options. 3247 3248 bg : (str, list) 3249 background color in RGB format, or string name 3250 3251 bg2 : (str, list) 3252 second background color to create a gradient background 3253 3254 size : (str, list) 3255 size of the window, e.g. size="fullscreen", or size=[600,400] 3256 3257 title : (str) 3258 window title text 3259 3260 screenshot : (str) 3261 save a screenshot of the window to file 3262 """ 3263 3264 if vedo.settings.dry_run_mode >= 2: 3265 return self 3266 3267 if self.wx_widget: 3268 return self 3269 3270 if self.renderers: # in case of notebooks 3271 3272 if at is None: 3273 at = self.renderers.index(self.renderer) 3274 3275 else: 3276 3277 if at >= len(self.renderers): 3278 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3279 vedo.logger.error(t) 3280 return self 3281 3282 self.renderer = self.renderers[at] 3283 3284 if title is not None: 3285 self.title = title 3286 3287 if size is not None: 3288 self.size = size 3289 if self.size[0] == "f": # full screen 3290 self.size = "fullscreen" 3291 self.window.SetFullScreen(True) 3292 self.window.BordersOn() 3293 else: 3294 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3295 3296 if vedo.settings.default_backend == "vtk": 3297 if str(bg).endswith(".hdr"): 3298 self._add_skybox(bg) 3299 else: 3300 if bg is not None: 3301 self.backgrcol = vedo.get_color(bg) 3302 self.renderer.SetBackground(self.backgrcol) 3303 if bg2 is not None: 3304 self.renderer.GradientBackgroundOn() 3305 self.renderer.SetBackground2(vedo.get_color(bg2)) 3306 3307 if axes is not None: 3308 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3309 objects = list(objects) 3310 objects.append(axes) # move it into the list of normal things to show 3311 axes = 0 3312 self.axes = axes 3313 3314 if interactive is not None: 3315 self._interactive = interactive 3316 if self.offscreen: 3317 self._interactive = False 3318 3319 # camera stuff 3320 if resetcam is not None: 3321 self.resetcam = resetcam 3322 3323 if camera is not None: 3324 self.resetcam = False 3325 viewup = "" 3326 if isinstance(camera, vtki.vtkCamera): 3327 cameracopy = vtki.vtkCamera() 3328 cameracopy.DeepCopy(camera) 3329 self.camera = cameracopy 3330 else: 3331 self.camera = utils.camera_from_dict(camera) 3332 3333 self.add(objects) 3334 3335 # Backend ############################################################### 3336 if vedo.settings.default_backend in ["k3d", "panel"]: 3337 return backends.get_notebook_backend(self.objects) 3338 ######################################################################### 3339 3340 for ia in utils.flatten(objects): 3341 try: 3342 # fix gray color labels and title to white or black 3343 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3344 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3345 c = (0.9, 0.9, 0.9) 3346 if np.sum(self.renderer.GetBackground()) > 1.5: 3347 c = (0.1, 0.1, 0.1) 3348 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3349 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3350 except AttributeError: 3351 pass 3352 3353 if self.sharecam: 3354 for r in self.renderers: 3355 r.SetActiveCamera(self.camera) 3356 3357 if self.axes is not None: 3358 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3359 bns = self.renderer.ComputeVisiblePropBounds() 3360 addons.add_global_axes(self.axes, bounds=bns) 3361 3362 # Backend ############################################################### 3363 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3364 return backends.get_notebook_backend() 3365 ######################################################################### 3366 3367 if self.resetcam: 3368 self.renderer.ResetCamera() 3369 3370 if len(self.renderers) > 1: 3371 self.add_renderer_frame() 3372 3373 if vedo.settings.default_backend == "2d" and not zoom: 3374 zoom = "tightest" 3375 3376 if zoom: 3377 if zoom == "tight": 3378 self.reset_camera(tight=0.04) 3379 elif zoom == "tightest": 3380 self.reset_camera(tight=0.0001) 3381 else: 3382 self.camera.Zoom(zoom) 3383 if elevation: 3384 self.camera.Elevation(elevation) 3385 if azimuth: 3386 self.camera.Azimuth(azimuth) 3387 if roll: 3388 self.camera.Roll(roll) 3389 3390 if len(viewup) > 0: 3391 b = self.renderer.ComputeVisiblePropBounds() 3392 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3393 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3394 if viewup == "x": 3395 sz = np.linalg.norm(sz) 3396 self.camera.SetViewUp([1, 0, 0]) 3397 self.camera.SetPosition(cm + sz) 3398 elif viewup == "y": 3399 sz = np.linalg.norm(sz) 3400 self.camera.SetViewUp([0, 1, 0]) 3401 self.camera.SetPosition(cm + sz) 3402 elif viewup == "z": 3403 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3404 self.camera.SetViewUp([0, 0, 1]) 3405 self.camera.SetPosition(cm + 2 * sz) 3406 elif utils.is_sequence(viewup): 3407 sz = np.linalg.norm(sz) 3408 self.camera.SetViewUp(viewup) 3409 cpos = np.cross([0, 1, 0], viewup) 3410 self.camera.SetPosition(cm - 2 * sz * cpos) 3411 3412 self.renderer.ResetCameraClippingRange() 3413 3414 self.initialize_interactor() 3415 3416 if vedo.settings.immediate_rendering: 3417 self.window.Render() ##################### <-------------- Render 3418 3419 if self.interactor: # can be offscreen or not the vtk backend.. 3420 3421 self.window.SetWindowName(self.title) 3422 3423 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3424 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3425 # print(pic.dataset)# Array 0 name PNGImage 3426 # self.window.SetIcon(pic.dataset) 3427 3428 try: 3429 # Needs "pip install pyobjc" on Mac OSX 3430 if ( 3431 self._cocoa_initialized is False 3432 and "Darwin" in vedo.sys_platform 3433 and not self.offscreen 3434 ): 3435 self._cocoa_initialized = True 3436 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3437 pid = os.getpid() 3438 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3439 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3440 except: 3441 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3442 pass 3443 3444 # Set the interaction style 3445 if mode is not None: 3446 self.user_mode(mode) 3447 if self.qt_widget and mode is None: 3448 self.user_mode(0) 3449 3450 if screenshot: 3451 self.screenshot(screenshot) 3452 3453 if self._interactive: 3454 self.interactor.Start() 3455 if self._must_close_now: 3456 self.interactor.GetRenderWindow().Finalize() 3457 self.interactor.TerminateApp() 3458 self.camera = None 3459 self.renderer = None 3460 self.renderers = [] 3461 self.window = None 3462 self.interactor = None 3463 return self 3464 3465 if rate: 3466 if self.clock is None: # set clock and limit rate 3467 self._clockt0 = time.time() 3468 self.clock = 0.0 3469 else: 3470 t = time.time() - self._clockt0 3471 elapsed = t - self.clock 3472 mint = 1.0 / rate 3473 if elapsed < mint: 3474 time.sleep(mint - elapsed) 3475 self.clock = time.time() - self._clockt0 3476 3477 # 2d #################################################################### 3478 if vedo.settings.default_backend in ["2d"]: 3479 return backends.get_notebook_backend() 3480 ######################################################################### 3481 3482 return self 3483 3484 3485 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3486 """Add a draggable inset space into a renderer. 3487 3488 Arguments: 3489 at : (int) 3490 specify the renderer number 3491 pos : (list) 3492 icon position in the range [1-4] indicating one of the 4 corners, 3493 or it can be a tuple (x,y) as a fraction of the renderer size. 3494 size : (float) 3495 size of the square inset 3496 draggable : (bool) 3497 if True the subrenderer space can be dragged around 3498 c : (color) 3499 color of the inset frame when dragged 3500 3501 Examples: 3502 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3503 3504  3505 """ 3506 if not self.interactor: 3507 return None 3508 3509 if not self.renderer: 3510 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3511 return None 3512 3513 options = dict(options) 3514 pos = options.pop("pos", 0) 3515 size = options.pop("size", 0.1) 3516 c = options.pop("c", "lb") 3517 at = options.pop("at", None) 3518 draggable = options.pop("draggable", True) 3519 3520 r, g, b = vedo.get_color(c) 3521 widget = vtki.vtkOrientationMarkerWidget() 3522 widget.SetOutlineColor(r, g, b) 3523 if len(objects) == 1: 3524 widget.SetOrientationMarker(objects[0].actor) 3525 else: 3526 widget.SetOrientationMarker(vedo.Assembly(objects)) 3527 3528 widget.SetInteractor(self.interactor) 3529 3530 if utils.is_sequence(pos): 3531 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3532 else: 3533 if pos < 2: 3534 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3535 elif pos == 2: 3536 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3537 elif pos == 3: 3538 widget.SetViewport(0, 0, size * 2, size * 2) 3539 elif pos == 4: 3540 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3541 widget.EnabledOn() 3542 widget.SetInteractive(draggable) 3543 if at is not None and at < len(self.renderers): 3544 widget.SetCurrentRenderer(self.renderers[at]) 3545 else: 3546 widget.SetCurrentRenderer(self.renderer) 3547 self.widgets.append(widget) 3548 return widget 3549 3550 def clear(self, at=None, deep=False) -> Self: 3551 """Clear the scene from all meshes and volumes.""" 3552 if at is not None: 3553 renderer = self.renderers[at] 3554 else: 3555 renderer = self.renderer 3556 if not renderer: 3557 return self 3558 3559 if deep: 3560 renderer.RemoveAllViewProps() 3561 else: 3562 for ob in set( 3563 self.get_meshes() 3564 + self.get_volumes() 3565 + self.objects 3566 + self.axes_instances 3567 ): 3568 if isinstance(ob, vedo.shapes.Text2D): 3569 continue 3570 self.remove(ob) 3571 try: 3572 if ob.scalarbar: 3573 self.remove(ob.scalarbar) 3574 except AttributeError: 3575 pass 3576 return self 3577 3578 def break_interaction(self) -> Self: 3579 """Break window interaction and return to the python execution flow""" 3580 if self.interactor: 3581 self.check_actors_trasform() 3582 self.interactor.ExitCallback() 3583 return self 3584 3585 def freeze(self, value=True) -> Self: 3586 """Freeze the current renderer. Use this with `sharecam=False`.""" 3587 if not self.interactor: 3588 return self 3589 if not self.renderer: 3590 return self 3591 self.renderer.SetInteractive(not value) 3592 return self 3593 3594 def user_mode(self, mode) -> Self: 3595 """ 3596 Modify the user interaction mode. 3597 3598 Examples: 3599 ```python 3600 from vedo import * 3601 mode = interactor_modes.MousePan() 3602 mesh = Mesh(dataurl+"cow.vtk") 3603 plt = Plotter().user_mode(mode) 3604 plt.show(mesh, axes=1) 3605 ``` 3606 See also: 3607 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3608 """ 3609 if not self.interactor: 3610 return self 3611 3612 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3613 # print("Current style:", curr_style) 3614 if curr_style.endswith("Actor"): 3615 self.check_actors_trasform() 3616 3617 if isinstance(mode, (str, int)): 3618 # Set the style of interaction 3619 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3620 if mode in (0, "TrackballCamera"): 3621 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3622 self.interactor.RemoveObservers("CharEvent") 3623 elif mode in (1, "TrackballActor"): 3624 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3625 elif mode in (2, "JoystickCamera"): 3626 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3627 elif mode in (3, "JoystickActor"): 3628 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3629 elif mode in (4, "Flight"): 3630 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3631 elif mode in (5, "RubberBand2D"): 3632 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3633 elif mode in (6, "RubberBand3D"): 3634 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3635 elif mode in (7, "RubberBandZoom"): 3636 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3637 elif mode in (8, "Terrain"): 3638 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3639 elif mode in (9, "Unicam"): 3640 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3641 elif mode in (10, "Image", "image", "2d"): 3642 astyle = vtki.new("InteractorStyleImage") 3643 astyle.SetInteractionModeToImage3D() 3644 self.interactor.SetInteractorStyle(astyle) 3645 else: 3646 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3647 3648 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3649 # set a custom interactor style 3650 if hasattr(mode, "interactor"): 3651 mode.interactor = self.interactor 3652 mode.renderer = self.renderer # type: ignore 3653 mode.SetInteractor(self.interactor) 3654 mode.SetDefaultRenderer(self.renderer) 3655 self.interactor.SetInteractorStyle(mode) 3656 3657 return self 3658 3659 def close(self) -> Self: 3660 """Close the plotter.""" 3661 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3662 vedo.last_figure = None 3663 self.last_event = None 3664 self.sliders = [] 3665 self.buttons = [] 3666 self.widgets = [] 3667 self.hover_legends = [] 3668 self.background_renderer = None 3669 self._extralight = None 3670 3671 self.hint_widget = None 3672 self.cutter_widget = None 3673 3674 if vedo.settings.dry_run_mode >= 2: 3675 return self 3676 3677 if not hasattr(self, "window"): 3678 return self 3679 if not self.window: 3680 return self 3681 if not hasattr(self, "interactor"): 3682 return self 3683 if not self.interactor: 3684 return self 3685 3686 ################################################### 3687 try: 3688 if "Darwin" in vedo.sys_platform: 3689 self.interactor.ProcessEvents() 3690 except: 3691 pass 3692 3693 self._must_close_now = True 3694 3695 if vedo.plotter_instance == self: 3696 vedo.plotter_instance = None 3697 3698 if self.interactor and self._interactive: 3699 self.break_interaction() 3700 elif self._must_close_now: 3701 # dont call ExitCallback here 3702 if self.interactor: 3703 self.break_interaction() 3704 self.interactor.GetRenderWindow().Finalize() 3705 self.interactor.TerminateApp() 3706 self.camera = None 3707 self.renderer = None 3708 self.renderers = [] 3709 self.window = None 3710 self.interactor = None 3711 return self 3712 3713 @property 3714 def camera(self): 3715 """Return the current active camera.""" 3716 if self.renderer: 3717 return self.renderer.GetActiveCamera() 3718 3719 @camera.setter 3720 def camera(self, cam): 3721 if self.renderer: 3722 if isinstance(cam, dict): 3723 cam = utils.camera_from_dict(cam) 3724 self.renderer.SetActiveCamera(cam) 3725 3726 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3727 """ 3728 Take a screenshot of the Plotter window. 3729 3730 Arguments: 3731 scale : (int) 3732 set image magnification as an integer multiplicating factor 3733 asarray : (bool) 3734 return a numpy array of the image instead of writing a file 3735 3736 Warning: 3737 If you get black screenshots try to set `interactive=False` in `show()` 3738 then call `screenshot()` and `plt.interactive()` afterwards. 3739 3740 Example: 3741 ```py 3742 from vedo import * 3743 sphere = Sphere().linewidth(1) 3744 plt = show(sphere, interactive=False) 3745 plt.screenshot('image.png') 3746 plt.interactive() 3747 plt.close() 3748 ``` 3749 3750 Example: 3751 ```py 3752 from vedo import * 3753 sphere = Sphere().linewidth(1) 3754 plt = show(sphere, interactive=False) 3755 plt.screenshot('anotherimage.png') 3756 plt.interactive() 3757 plt.close() 3758 ``` 3759 """ 3760 return vedo.file_io.screenshot(filename, scale, asarray) 3761 3762 def toimage(self, scale=1) -> "vedo.image.Image": 3763 """ 3764 Generate a `Image` object from the current rendering window. 3765 3766 Arguments: 3767 scale : (int) 3768 set image magnification as an integer multiplicating factor 3769 """ 3770 if vedo.settings.screeshot_large_image: 3771 w2if = vtki.new("RenderLargeImage") 3772 w2if.SetInput(self.renderer) 3773 w2if.SetMagnification(scale) 3774 else: 3775 w2if = vtki.new("WindowToImageFilter") 3776 w2if.SetInput(self.window) 3777 if hasattr(w2if, "SetScale"): 3778 w2if.SetScale(scale, scale) 3779 if vedo.settings.screenshot_transparent_background: 3780 w2if.SetInputBufferTypeToRGBA() 3781 w2if.ReadFrontBufferOff() # read from the back buffer 3782 w2if.Update() 3783 return vedo.image.Image(w2if.GetOutput()) 3784 3785 def export(self, filename="scene.npz", binary=False) -> Self: 3786 """ 3787 Export scene to file to HTML, X3D or Numpy file. 3788 3789 Examples: 3790 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3791 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3792 """ 3793 vedo.file_io.export_window(filename, binary=binary) 3794 return self 3795 3796 def color_picker(self, xy, verbose=False): 3797 """Pick color of specific (x,y) pixel on the screen.""" 3798 w2if = vtki.new("WindowToImageFilter") 3799 w2if.SetInput(self.window) 3800 w2if.ReadFrontBufferOff() 3801 w2if.Update() 3802 nx, ny = self.window.GetSize() 3803 varr = w2if.GetOutput().GetPointData().GetScalars() 3804 3805 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3806 x, y = int(xy[0]), int(xy[1]) 3807 if y < ny and x < nx: 3808 3809 rgb = arr[y, x] 3810 3811 if verbose: 3812 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3813 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3814 vedo.printc("█", c=[0, rgb[1], 0], end="") 3815 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3816 vedo.printc("] = ", end="") 3817 cnm = vedo.get_color_name(rgb) 3818 if np.sum(rgb) < 150: 3819 vedo.printc( 3820 rgb.tolist(), 3821 vedo.colors.rgb2hex(np.array(rgb) / 255), 3822 c="w", 3823 bc=rgb, 3824 invert=1, 3825 end="", 3826 ) 3827 vedo.printc(" -> " + cnm, invert=1, c="w") 3828 else: 3829 vedo.printc( 3830 rgb.tolist(), 3831 vedo.colors.rgb2hex(np.array(rgb) / 255), 3832 c=rgb, 3833 end="", 3834 ) 3835 vedo.printc(" -> " + cnm, c=cnm) 3836 3837 return rgb 3838 3839 return None 3840 3841 ####################################################################### 3842 def _default_mouseleftclick(self, iren, event) -> None: 3843 x, y = iren.GetEventPosition() 3844 renderer = iren.FindPokedRenderer(x, y) 3845 picker = vtki.vtkPropPicker() 3846 picker.PickProp(x, y, renderer) 3847 3848 self.renderer = renderer 3849 3850 clicked_actor = picker.GetActor() 3851 # clicked_actor2D = picker.GetActor2D() 3852 3853 # print('_default_mouseleftclick mouse at', x, y) 3854 # print("picked Volume:", [picker.GetVolume()]) 3855 # print("picked Actor2D:", [picker.GetActor2D()]) 3856 # print("picked Assembly:", [picker.GetAssembly()]) 3857 # print("picked Prop3D:", [picker.GetProp3D()]) 3858 3859 if not clicked_actor: 3860 clicked_actor = picker.GetAssembly() 3861 3862 if not clicked_actor: 3863 clicked_actor = picker.GetProp3D() 3864 3865 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3866 return 3867 3868 self.picked3d = picker.GetPickPosition() 3869 self.picked2d = np.array([x, y]) 3870 3871 if not clicked_actor: 3872 return 3873 3874 self.justremoved = None 3875 self.clicked_actor = clicked_actor 3876 3877 try: # might not be a vedo obj 3878 self.clicked_object = clicked_actor.retrieve_object() 3879 # save this info in the object itself 3880 self.clicked_object.picked3d = self.picked3d 3881 self.clicked_object.picked2d = self.picked2d 3882 except AttributeError: 3883 pass 3884 3885 # ----------- 3886 # if "Histogram1D" in picker.GetAssembly().__class__.__name__: 3887 # histo = picker.GetAssembly() 3888 # if histo.verbose: 3889 # x = self.picked3d[0] 3890 # idx = np.digitize(x, histo.edges) - 1 3891 # f = histo.frequencies[idx] 3892 # cn = histo.centers[idx] 3893 # vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3894 3895 ####################################################################### 3896 def _default_keypress(self, iren, event) -> None: 3897 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3898 3899 key = iren.GetKeySym() 3900 3901 if "_L" in key or "_R" in key: 3902 return 3903 3904 if iren.GetShiftKey(): 3905 key = key.upper() 3906 3907 if iren.GetControlKey(): 3908 key = "Ctrl+" + key 3909 3910 if iren.GetAltKey(): 3911 key = "Alt+" + key 3912 3913 ####################################################### 3914 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3915 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3916 # iren.GetKeyCode(), iren.GetRepeatCount()) 3917 ####################################################### 3918 3919 x, y = iren.GetEventPosition() 3920 renderer = iren.FindPokedRenderer(x, y) 3921 3922 if key in ["q", "Return"]: 3923 self.break_interaction() 3924 return 3925 3926 elif key in ["Ctrl+q", "Ctrl+w", "Escape"]: 3927 self.close() 3928 return 3929 3930 elif key == "F1": 3931 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3932 self.break_interaction() 3933 sys.exit(0) 3934 3935 elif key == "Down": 3936 if self.clicked_object and self.clicked_object in self.get_meshes(): 3937 self.clicked_object.alpha(0.02) 3938 if hasattr(self.clicked_object, "properties_backface"): 3939 bfp = self.clicked_actor.GetBackfaceProperty() 3940 self.clicked_object.properties_backface = bfp # save it 3941 self.clicked_actor.SetBackfaceProperty(None) 3942 else: 3943 for obj in self.get_meshes(): 3944 if obj: 3945 obj.alpha(0.02) 3946 bfp = obj.actor.GetBackfaceProperty() 3947 if bfp and hasattr(obj, "properties_backface"): 3948 obj.properties_backface = bfp 3949 obj.actor.SetBackfaceProperty(None) 3950 3951 elif key == "Left": 3952 if self.clicked_object and self.clicked_object in self.get_meshes(): 3953 ap = self.clicked_object.properties 3954 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3955 ap.SetOpacity(aal) 3956 bfp = self.clicked_actor.GetBackfaceProperty() 3957 if bfp and hasattr(self.clicked_object, "properties_backface"): 3958 self.clicked_object.properties_backface = bfp 3959 self.clicked_actor.SetBackfaceProperty(None) 3960 else: 3961 for a in self.get_meshes(): 3962 if a: 3963 ap = a.properties 3964 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3965 ap.SetOpacity(aal) 3966 bfp = a.actor.GetBackfaceProperty() 3967 if bfp and hasattr(a, "properties_backface"): 3968 a.properties_backface = bfp 3969 a.actor.SetBackfaceProperty(None) 3970 3971 elif key == "Right": 3972 if self.clicked_object and self.clicked_object in self.get_meshes(): 3973 ap = self.clicked_object.properties 3974 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3975 ap.SetOpacity(aal) 3976 if ( 3977 aal == 1 3978 and hasattr(self.clicked_object, "properties_backface") 3979 and self.clicked_object.properties_backface 3980 ): 3981 # put back 3982 self.clicked_actor.SetBackfaceProperty( 3983 self.clicked_object.properties_backface) 3984 else: 3985 for a in self.get_meshes(): 3986 if a: 3987 ap = a.properties 3988 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3989 ap.SetOpacity(aal) 3990 if aal == 1 and hasattr(a, "properties_backface") and a.properties_backface: 3991 a.actor.SetBackfaceProperty(a.properties_backface) 3992 3993 elif key == "Up": 3994 if self.clicked_object and self.clicked_object in self.get_meshes(): 3995 self.clicked_object.properties.SetOpacity(1) 3996 if hasattr(self.clicked_object, "properties_backface") and self.clicked_object.properties_backface: 3997 self.clicked_object.actor.SetBackfaceProperty(self.clicked_object.properties_backface) 3998 else: 3999 for a in self.get_meshes(): 4000 if a: 4001 a.properties.SetOpacity(1) 4002 if hasattr(a, "properties_backface") and a.properties_backface: 4003 a.actor.SetBackfaceProperty(a.properties_backface) 4004 4005 elif key == "P": 4006 if self.clicked_object and self.clicked_object in self.get_meshes(): 4007 objs = [self.clicked_object] 4008 else: 4009 objs = self.get_meshes() 4010 for ia in objs: 4011 try: 4012 ps = ia.properties.GetPointSize() 4013 if ps > 1: 4014 ia.properties.SetPointSize(ps - 1) 4015 ia.properties.SetRepresentationToPoints() 4016 except AttributeError: 4017 pass 4018 4019 elif key == "p": 4020 if self.clicked_object and self.clicked_object in self.get_meshes(): 4021 objs = [self.clicked_object] 4022 else: 4023 objs = self.get_meshes() 4024 for ia in objs: 4025 try: 4026 ps = ia.properties.GetPointSize() 4027 ia.properties.SetPointSize(ps + 2) 4028 ia.properties.SetRepresentationToPoints() 4029 except AttributeError: 4030 pass 4031 4032 elif key == "U": 4033 pval = renderer.GetActiveCamera().GetParallelProjection() 4034 renderer.GetActiveCamera().SetParallelProjection(not pval) 4035 if pval: 4036 renderer.ResetCamera() 4037 4038 elif key == "r": 4039 renderer.ResetCamera() 4040 4041 elif key == "h": 4042 msg = f" vedo {vedo.__version__}" 4043 msg += f" | vtk {vtki.vtkVersion().GetVTKVersion()}" 4044 msg += f" | numpy {np.__version__}" 4045 msg += f" | python {sys.version_info[0]}.{sys.version_info[1]}, press: " 4046 vedo.printc(msg.ljust(75), invert=True) 4047 msg = ( 4048 " i print info about the last clicked object \n" 4049 " I print color of the pixel under the mouse \n" 4050 " Y show the pipeline for this object as a graph \n" 4051 " <- -> use arrows to reduce/increase opacity \n" 4052 " x toggle mesh visibility \n" 4053 " w toggle wireframe/surface style \n" 4054 " l toggle surface edges visibility \n" 4055 " p/P hide surface faces and show only points \n" 4056 " 1-3 cycle surface color (2=light, 3=dark) \n" 4057 " 4 cycle color map (press shift-4 to go back) \n" 4058 " 5-6 cycle point-cell arrays (shift to go back) \n" 4059 " 7-8 cycle background and gradient color \n" 4060 " 09+- cycle axes styles (on keypad, or press +/-) \n" 4061 " k cycle available lighting styles \n" 4062 " K toggle shading as flat or phong \n" 4063 " A toggle anti-aliasing \n" 4064 " D toggle depth-peeling (for transparencies) \n" 4065 " U toggle perspective/parallel projection \n" 4066 " o/O toggle extra light to scene and rotate it \n" 4067 " a toggle interaction to Actor Mode \n" 4068 " n toggle surface normals \n" 4069 " r reset camera position \n" 4070 " R reset camera to the closest orthogonal view \n" 4071 " . fly camera to the last clicked point \n" 4072 " C print the current camera parameters state \n" 4073 " X invoke a cutter widget tool \n" 4074 " S save a screenshot of the current scene \n" 4075 " E/F export 3D scene to numpy file or X3D \n" 4076 " q return control to python script \n" 4077 " Esc abort execution and exit python kernel " 4078 ) 4079 vedo.printc(msg, dim=True, italic=True, bold=True) 4080 vedo.printc( 4081 " Check out the documentation at: https://vedo.embl.es ".ljust(75), 4082 invert=True, 4083 bold=True, 4084 ) 4085 return 4086 4087 elif key == "a": 4088 cur = iren.GetInteractorStyle() 4089 if isinstance(cur, vtki.get_class("InteractorStyleTrackballCamera")): 4090 msg = "Interactor style changed to TrackballActor\n" 4091 msg += " you can now move and rotate individual meshes:\n" 4092 msg += " press X twice to save the repositioned mesh\n" 4093 msg += " press 'a' to go back to normal style" 4094 vedo.printc(msg) 4095 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 4096 else: 4097 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 4098 return 4099 4100 elif key == "A": # toggle antialiasing 4101 msam = self.window.GetMultiSamples() 4102 if not msam: 4103 self.window.SetMultiSamples(16) 4104 else: 4105 self.window.SetMultiSamples(0) 4106 msam = self.window.GetMultiSamples() 4107 if msam: 4108 vedo.printc(f"Antialiasing set to {msam} samples", c=bool(msam)) 4109 else: 4110 vedo.printc("Antialiasing disabled", c=bool(msam)) 4111 4112 elif key == "D": # toggle depthpeeling 4113 udp = not renderer.GetUseDepthPeeling() 4114 renderer.SetUseDepthPeeling(udp) 4115 # self.renderer.SetUseDepthPeelingForVolumes(udp) 4116 if udp: 4117 self.window.SetAlphaBitPlanes(1) 4118 renderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 4119 renderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 4120 self.interactor.Render() 4121 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 4122 rnr = self.renderers.index(renderer) 4123 vedo.printc(f"Depth peeling set to {udp} for renderer nr.{rnr}", c=udp) 4124 if not wasUsed and udp: 4125 vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True) 4126 return 4127 4128 elif key == "period": 4129 if self.picked3d: 4130 self.fly_to(self.picked3d) 4131 return 4132 4133 elif key == "S": 4134 fname = "screenshot.png" 4135 i = 1 4136 while os.path.isfile(fname): 4137 fname = f"screenshot{i}.png" 4138 i += 1 4139 vedo.file_io.screenshot(fname) 4140 vedo.printc(rf":camera: Saved rendering window to {fname}", c="b") 4141 return 4142 4143 elif key == "C": 4144 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 4145 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 4146 # This could happen for plotting geological models with UTM coordinate systems 4147 cam = renderer.GetActiveCamera() 4148 vedo.printc("\n###################################################", c="y") 4149 vedo.printc("## Template python code to position this camera: ##", c="y") 4150 vedo.printc("cam = dict(", c="y") 4151 vedo.printc(" pos=" + utils.precision(cam.GetPosition(), 6) + ",", c="y") 4152 vedo.printc(" focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y") 4153 vedo.printc(" viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y") 4154 vedo.printc(" roll=" + utils.precision(cam.GetRoll(), 6) + ",", c="y") 4155 if cam.GetParallelProjection(): 4156 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 4157 else: 4158 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 4159 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 4160 vedo.printc(')', c='y') 4161 vedo.printc('show(mymeshes, camera=cam)', c='y') 4162 vedo.printc('###################################################', c='y') 4163 return 4164 4165 elif key == "R": 4166 self.reset_viewup() 4167 4168 elif key == "w": 4169 try: 4170 if self.clicked_object.properties.GetRepresentation() == 1: # toggle 4171 self.clicked_object.properties.SetRepresentationToSurface() 4172 else: 4173 self.clicked_object.properties.SetRepresentationToWireframe() 4174 except AttributeError: 4175 pass 4176 4177 elif key == "1": 4178 try: 4179 self._icol += 1 4180 self.clicked_object.mapper.ScalarVisibilityOff() 4181 pal = vedo.colors.palettes[vedo.settings.palette % len(vedo.colors.palettes)] 4182 self.clicked_object.c(pal[(self._icol) % 10]) 4183 self.remove(self.clicked_object.scalarbar) 4184 except AttributeError: 4185 pass 4186 4187 elif key == "2": # dark colors 4188 try: 4189 bsc = ["k1", "k2", "k3", "k4", 4190 "b1", "b2", "b3", "b4", 4191 "p1", "p2", "p3", "p4", 4192 "g1", "g2", "g3", "g4", 4193 "r1", "r2", "r3", "r4", 4194 "o1", "o2", "o3", "o4", 4195 "y1", "y2", "y3", "y4"] 4196 self._icol += 1 4197 if self.clicked_object: 4198 self.clicked_object.mapper.ScalarVisibilityOff() 4199 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4200 self.clicked_object.c(newcol) 4201 self.remove(self.clicked_object.scalarbar) 4202 except AttributeError: 4203 pass 4204 4205 elif key == "3": # light colors 4206 try: 4207 bsc = ["k6", "k7", "k8", "k9", 4208 "b6", "b7", "b8", "b9", 4209 "p6", "p7", "p8", "p9", 4210 "g6", "g7", "g8", "g9", 4211 "r6", "r7", "r8", "r9", 4212 "o6", "o7", "o8", "o9", 4213 "y6", "y7", "y8", "y9"] 4214 self._icol += 1 4215 if self.clicked_object: 4216 self.clicked_object.mapper.ScalarVisibilityOff() 4217 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4218 self.clicked_object.c(newcol) 4219 self.remove(self.clicked_object.scalarbar) 4220 except AttributeError: 4221 pass 4222 4223 elif key == "4": # cmap name cycle 4224 ob = self.clicked_object 4225 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4226 return 4227 if not ob.mapper.GetScalarVisibility(): 4228 return 4229 onwhat = ob.mapper.GetScalarModeAsString() # UsePointData/UseCellData 4230 4231 cmap_names = [ 4232 "Accent", "Paired", 4233 "rainbow", "rainbow_r", 4234 "Spectral", "Spectral_r", 4235 "gist_ncar", "gist_ncar_r", 4236 "viridis", "viridis_r", 4237 "hot", "hot_r", 4238 "terrain", "ocean", 4239 "coolwarm", "seismic", "PuOr", "RdYlGn", 4240 ] 4241 try: 4242 i = cmap_names.index(ob._cmap_name) 4243 if iren.GetShiftKey(): 4244 i -= 1 4245 else: 4246 i += 1 4247 if i >= len(cmap_names): 4248 i = 0 4249 if i < 0: 4250 i = len(cmap_names) - 1 4251 except ValueError: 4252 i = 0 4253 4254 ob._cmap_name = cmap_names[i] 4255 ob.cmap(ob._cmap_name, on=onwhat) 4256 if ob.scalarbar: 4257 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4258 self.remove(ob.scalarbar) 4259 title = ob.scalarbar.GetTitle() 4260 ob.add_scalarbar(title=title) 4261 self.add(ob.scalarbar).render() 4262 elif isinstance(ob.scalarbar, vedo.Assembly): 4263 self.remove(ob.scalarbar) 4264 ob.add_scalarbar3d(title=ob._cmap_name) 4265 self.add(ob.scalarbar) 4266 4267 vedo.printc( 4268 f"Name:'{ob.name}'," if ob.name else "", 4269 f"range:{utils.precision(ob.mapper.GetScalarRange(),3)},", 4270 f"colormap:'{ob._cmap_name}'", c="g", bold=False, 4271 ) 4272 4273 elif key == "5": # cycle pointdata array 4274 ob = self.clicked_object 4275 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4276 return 4277 4278 arrnames = ob.pointdata.keys() 4279 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4280 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4281 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4282 if len(arrnames) == 0: 4283 return 4284 ob.mapper.SetScalarVisibility(1) 4285 4286 if not ob._cmap_name: 4287 ob._cmap_name = "rainbow" 4288 4289 try: 4290 curr_name = ob.dataset.GetPointData().GetScalars().GetName() 4291 i = arrnames.index(curr_name) 4292 if "normals" in curr_name.lower(): 4293 return 4294 if iren.GetShiftKey(): 4295 i -= 1 4296 else: 4297 i += 1 4298 if i >= len(arrnames): 4299 i = 0 4300 if i < 0: 4301 i = len(arrnames) - 1 4302 except (ValueError, AttributeError): 4303 i = 0 4304 4305 ob.cmap(ob._cmap_name, arrnames[i], on="points") 4306 if ob.scalarbar: 4307 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4308 self.remove(ob.scalarbar) 4309 title = ob.scalarbar.GetTitle() 4310 ob.scalarbar = None 4311 ob.add_scalarbar(title=arrnames[i]) 4312 self.add(ob.scalarbar) 4313 elif isinstance(ob.scalarbar, vedo.Assembly): 4314 self.remove(ob.scalarbar) 4315 ob.scalarbar = None 4316 ob.add_scalarbar3d(title=arrnames[i]) 4317 self.add(ob.scalarbar) 4318 else: 4319 vedo.printc( 4320 f"Name:'{ob.name}'," if ob.name else "", 4321 f"active pointdata array: '{arrnames[i]}'", 4322 c="g", bold=False, 4323 ) 4324 4325 elif key == "6": # cycle celldata array 4326 ob = self.clicked_object 4327 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4328 return 4329 4330 arrnames = ob.celldata.keys() 4331 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4332 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4333 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4334 if len(arrnames) == 0: 4335 return 4336 ob.mapper.SetScalarVisibility(1) 4337 4338 if not ob._cmap_name: 4339 ob._cmap_name = "rainbow" 4340 4341 try: 4342 curr_name = ob.dataset.GetCellData().GetScalars().GetName() 4343 i = arrnames.index(curr_name) 4344 if "normals" in curr_name.lower(): 4345 return 4346 if iren.GetShiftKey(): 4347 i -= 1 4348 else: 4349 i += 1 4350 if i >= len(arrnames): 4351 i = 0 4352 if i < 0: 4353 i = len(arrnames) - 1 4354 except (ValueError, AttributeError): 4355 i = 0 4356 4357 ob.cmap(ob._cmap_name, arrnames[i], on="cells") 4358 if ob.scalarbar: 4359 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4360 self.remove(ob.scalarbar) 4361 title = ob.scalarbar.GetTitle() 4362 ob.scalarbar = None 4363 ob.add_scalarbar(title=arrnames[i]) 4364 self.add(ob.scalarbar) 4365 elif isinstance(ob.scalarbar, vedo.Assembly): 4366 self.remove(ob.scalarbar) 4367 ob.scalarbar = None 4368 ob.add_scalarbar3d(title=arrnames[i]) 4369 self.add(ob.scalarbar) 4370 else: 4371 vedo.printc( 4372 f"Name:'{ob.name}'," if ob.name else "", 4373 f"active celldata array: '{arrnames[i]}'", 4374 c="g", bold=False, 4375 ) 4376 4377 elif key == "7": 4378 bgc = np.array(renderer.GetBackground()).sum() / 3 4379 if bgc <= 0: 4380 bgc = 0.223 4381 elif 0 < bgc < 1: 4382 bgc = 1 4383 else: 4384 bgc = 0 4385 renderer.SetBackground(bgc, bgc, bgc) 4386 4387 elif key == "8": 4388 bg2cols = [ 4389 "lightyellow", 4390 "darkseagreen", 4391 "palegreen", 4392 "steelblue", 4393 "lightblue", 4394 "cadetblue", 4395 "lavender", 4396 "white", 4397 "blackboard", 4398 "black", 4399 ] 4400 bg2name = vedo.get_color_name(renderer.GetBackground2()) 4401 if bg2name in bg2cols: 4402 idx = bg2cols.index(bg2name) 4403 else: 4404 idx = 4 4405 if idx is not None: 4406 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 4407 if not bg2name_next: 4408 renderer.GradientBackgroundOff() 4409 else: 4410 renderer.GradientBackgroundOn() 4411 renderer.SetBackground2(vedo.get_color(bg2name_next)) 4412 4413 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 4414 i = self.renderers.index(renderer) 4415 try: 4416 self.axes_instances[i].EnabledOff() 4417 self.axes_instances[i].SetInteractor(None) 4418 except AttributeError: 4419 # print("Cannot remove widget", [self.axes_instances[i]]) 4420 try: 4421 self.remove(self.axes_instances[i]) 4422 except: 4423 print("Cannot remove axes", [self.axes_instances[i]]) 4424 return 4425 self.axes_instances[i] = None 4426 4427 if not self.axes: 4428 self.axes = 0 4429 if isinstance(self.axes, dict): 4430 self.axes = 1 4431 4432 if key in ["minus", "KP_Subtract"]: 4433 if not self.camera.GetParallelProjection() and self.axes == 0: 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 else: 4438 if not self.camera.GetParallelProjection() and self.axes == 12: 4439 self.axes += 1 # jump ruler doesnt make sense in perspective mode 4440 bns = self.renderer.ComputeVisiblePropBounds() 4441 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns) 4442 self.render() 4443 4444 elif "KP_" in key or key in [ 4445 "Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior" 4446 ]: 4447 asso = { # change axes style 4448 "KP_Insert": 0, "KP_0": 0, "Insert": 0, 4449 "KP_End": 1, "KP_1": 1, "End": 1, 4450 "KP_Down": 2, "KP_2": 2, "Down": 2, 4451 "KP_Next": 3, "KP_3": 3, "Next": 3, 4452 "KP_Left": 4, "KP_4": 4, "Left": 4, 4453 "KP_Begin": 5, "KP_5": 5, "Begin": 5, 4454 "KP_Right": 6, "KP_6": 6, "Right": 6, 4455 "KP_Home": 7, "KP_7": 7, "Home": 7, 4456 "KP_Up": 8, "KP_8": 8, "Up": 8, 4457 "Prior": 9, # on windows OS 4458 } 4459 clickedr = self.renderers.index(renderer) 4460 if key in asso: 4461 if self.axes_instances[clickedr]: 4462 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 4463 self.axes_instances[clickedr].EnabledOff() 4464 else: 4465 try: 4466 renderer.RemoveActor(self.axes_instances[clickedr]) 4467 except: 4468 pass 4469 self.axes_instances[clickedr] = None 4470 bounds = renderer.ComputeVisiblePropBounds() 4471 addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds) 4472 self.interactor.Render() 4473 4474 if key == "O": 4475 renderer.RemoveLight(self._extralight) 4476 self._extralight = None 4477 4478 elif key == "o": 4479 vbb, sizes, _, _ = addons.compute_visible_bounds() 4480 cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2) 4481 if not self._extralight: 4482 vup = renderer.GetActiveCamera().GetViewUp() 4483 pos = cm + utils.vector(vup) * utils.mag(sizes) 4484 self._extralight = addons.Light(pos, focal_point=cm, intensity=0.4) 4485 renderer.AddLight(self._extralight) 4486 vedo.printc("Press 'o' again to rotate light source, or 'O' to remove it.", c='y') 4487 else: 4488 cpos = utils.vector(self._extralight.GetPosition()) 4489 x, y, z = self._extralight.GetPosition() - cm 4490 r, th, ph = transformations.cart2spher(x, y, z) 4491 th += 0.2 4492 if th > np.pi: 4493 th = np.random.random() * np.pi / 2 4494 ph += 0.3 4495 cpos = transformations.spher2cart(r, th, ph).T + cm 4496 self._extralight.SetPosition(cpos) 4497 4498 elif key == "l": 4499 if self.clicked_object in self.get_meshes(): 4500 objs = [self.clicked_object] 4501 else: 4502 objs = self.get_meshes() 4503 for ia in objs: 4504 try: 4505 ev = ia.properties.GetEdgeVisibility() 4506 ia.properties.SetEdgeVisibility(not ev) 4507 ia.properties.SetRepresentationToSurface() 4508 ia.properties.SetLineWidth(0.1) 4509 except AttributeError: 4510 pass 4511 4512 elif key == "k": # lightings 4513 if self.clicked_object in self.get_meshes(): 4514 objs = [self.clicked_object] 4515 else: 4516 objs = self.get_meshes() 4517 shds = ("default", "metallic", "plastic", "shiny", "glossy", "off") 4518 for ia in objs: 4519 try: 4520 lnr = (ia._ligthingnr + 1) % 6 4521 ia.lighting(shds[lnr]) 4522 ia._ligthingnr = lnr 4523 except AttributeError: 4524 pass 4525 4526 elif key == "K": # shading 4527 if self.clicked_object in self.get_meshes(): 4528 objs = [self.clicked_object] 4529 else: 4530 objs = self.get_meshes() 4531 for ia in objs: 4532 if isinstance(ia, vedo.Mesh): 4533 ia.compute_normals(cells=False) 4534 intrp = ia.properties.GetInterpolation() 4535 if intrp > 0: 4536 ia.properties.SetInterpolation(0) # flat 4537 else: 4538 ia.properties.SetInterpolation(2) # phong 4539 4540 elif key == "n": # show normals to an actor 4541 self.remove("added_auto_normals") 4542 if self.clicked_object in self.get_meshes(): 4543 if self.clicked_actor.GetPickable(): 4544 norml = vedo.shapes.NormalLines(self.clicked_object) 4545 norml.name = "added_auto_normals" 4546 self.add(norml) 4547 4548 elif key == "x": 4549 if self.justremoved is None: 4550 if self.clicked_object in self.get_meshes() or isinstance( 4551 self.clicked_object, vtki.vtkAssembly 4552 ): 4553 self.justremoved = self.clicked_actor 4554 self.renderer.RemoveActor(self.clicked_actor) 4555 else: 4556 self.renderer.AddActor(self.justremoved) 4557 self.justremoved = None 4558 4559 elif key == "X": 4560 if self.clicked_object: 4561 if not self.cutter_widget: 4562 self.cutter_widget = addons.BoxCutter(self.clicked_object) 4563 self.add(self.cutter_widget) 4564 vedo.printc("Press i to toggle the cutter on/off", c='g', dim=1) 4565 vedo.printc(" u to flip selection", c='g', dim=1) 4566 vedo.printc(" r to reset cutting planes", c='g', dim=1) 4567 vedo.printc(" Shift+X to close the cutter box widget", c='g', dim=1) 4568 vedo.printc(" Ctrl+S to save the cut section to file.", c='g', dim=1) 4569 else: 4570 self.remove(self.cutter_widget) 4571 self.cutter_widget = None 4572 vedo.printc("Click object and press X to open the cutter box widget.", c='g') 4573 4574 elif key == "E": 4575 vedo.printc(r":camera: Exporting 3D window to file scene.npz", c="b", end="") 4576 vedo.file_io.export_window("scene.npz") 4577 vedo.printc(", try:\n> vedo scene.npz # (this is experimental!)", c="b") 4578 return 4579 4580 elif key == "F": 4581 vedo.file_io.export_window("scene.x3d") 4582 vedo.printc(r":camera: Exporting 3D window to file", c="b", end="") 4583 vedo.file_io.export_window("scene.npz") 4584 vedo.printc(". Try:\n> firefox scene.html", c="b") 4585 4586 # elif key == "G": # not working with last version of k3d 4587 # vedo.file_io.export_window("scene.html") 4588 # vedo.printc(r":camera: Exporting K3D window to file", c="b", end="") 4589 # vedo.file_io.export_window("scene.html") 4590 # vedo.printc(". Try:\n> firefox scene.html", c="b") 4591 4592 elif key == "i": # print info 4593 if self.clicked_object: 4594 print(self.clicked_object) 4595 else: 4596 print(self) 4597 4598 elif key == "I": # print color under the mouse 4599 x, y = iren.GetEventPosition() 4600 self.color_picker([x, y], verbose=True) 4601 4602 elif key == "Y": 4603 if self.clicked_object and self.clicked_object.pipeline: 4604 self.clicked_object.pipeline.show() 4605 4606 if iren: 4607 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, TypeError): 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[mode]) 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  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  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  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 coordinates 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  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  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  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  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  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  2830 """ 2831 if action in ("create", "start"): 2832 2833 if "Windows" in vedo.sys_platform: 2834 # otherwise on windows it gets stuck 2835 self.initialize_interactor() 2836 2837 if timer_id is not None: 2838 vedo.logger.warning("you set a timer_id but it will be ignored.") 2839 if one_shot: 2840 timer_id = self.interactor.CreateOneShotTimer(dt) 2841 else: 2842 timer_id = self.interactor.CreateRepeatingTimer(dt) 2843 return timer_id 2844 2845 elif action in ("destroy", "stop"): 2846 if timer_id is not None: 2847 self.interactor.DestroyTimer(timer_id) 2848 else: 2849 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2850 else: 2851 e = f"in timer_callback(). Cannot understand action: {action}\n" 2852 e += " allowed actions are: ['start', 'stop']. Skipped." 2853 vedo.logger.error(e) 2854 return timer_id 2855 2856 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2857 """ 2858 Add a callback function that will be called when an event occurs. 2859 Consider using `add_callback()` instead. 2860 """ 2861 if not self.interactor: 2862 return -1 2863 event_name = utils.get_vtk_name_event(event_name) 2864 idd = self.interactor.AddObserver(event_name, func, priority) 2865 return idd 2866 2867 def compute_world_coordinate( 2868 self, 2869 pos2d: MutableSequence[float], 2870 at=None, 2871 objs=(), 2872 bounds=(), 2873 offset=None, 2874 pixeltol=None, 2875 worldtol=None, 2876 ) -> np.ndarray: 2877 """ 2878 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2879 If a set of meshes is passed then points are placed onto these. 2880 2881 Arguments: 2882 pos2d : (list) 2883 2D screen coordinates point. 2884 at : (int) 2885 renderer number. 2886 objs : (list) 2887 list of Mesh objects to project the point onto. 2888 bounds : (list) 2889 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2890 offset : (float) 2891 specify an offset value. 2892 pixeltol : (int) 2893 screen tolerance in pixels. 2894 worldtol : (float) 2895 world coordinates tolerance. 2896 2897 Returns: 2898 numpy array, the point in 3D world coordinates. 2899 2900 Examples: 2901 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2902 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2903 2904  2905 """ 2906 if at is not None: 2907 renderer = self.renderers[at] 2908 else: 2909 renderer = self.renderer 2910 2911 if not objs: 2912 pp = vtki.vtkFocalPlanePointPlacer() 2913 else: 2914 pps = vtki.vtkPolygonalSurfacePointPlacer() 2915 for ob in objs: 2916 pps.AddProp(ob.actor) 2917 pp = pps # type: ignore 2918 2919 if len(bounds) == 6: 2920 pp.SetPointBounds(bounds) 2921 if pixeltol: 2922 pp.SetPixelTolerance(pixeltol) 2923 if worldtol: 2924 pp.SetWorldTolerance(worldtol) 2925 if offset: 2926 pp.SetOffset(offset) 2927 2928 worldPos: MutableSequence[float] = [0, 0, 0] 2929 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2930 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2931 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2932 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2933 return np.array(worldPos) 2934 2935 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2936 """ 2937 Given a 3D points in the current renderer (or full window), 2938 find the screen pixel coordinates. 2939 2940 Example: 2941 ```python 2942 from vedo import * 2943 2944 elli = Ellipsoid().point_size(5) 2945 2946 plt = Plotter() 2947 plt.show(elli, "Press q to continue and print the info") 2948 2949 xyscreen = plt.compute_screen_coordinates(elli) 2950 print('xyscreen coords:', xyscreen) 2951 2952 # simulate an event happening at one point 2953 event = plt.fill_event(pos=xyscreen[123]) 2954 print(event) 2955 ``` 2956 """ 2957 try: 2958 obj = obj.coordinates 2959 except AttributeError: 2960 pass 2961 2962 if utils.is_sequence(obj): 2963 pts = obj 2964 p2d = [] 2965 cs = vtki.vtkCoordinate() 2966 cs.SetCoordinateSystemToWorld() 2967 cs.SetViewport(self.renderer) 2968 for p in pts: 2969 cs.SetValue(p) 2970 if full_window: 2971 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2972 else: 2973 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2974 return np.array(p2d, dtype=int) 2975 2976 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2977 """ 2978 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2979 2980 Returns a frustum Mesh that contains the visible field of view. 2981 This can be used to select objects in a scene or select vertices. 2982 2983 Example: 2984 ```python 2985 from vedo import * 2986 2987 settings.enable_default_mouse_callbacks = False 2988 2989 def mode_select(objs): 2990 print("Selected objects:", objs) 2991 d0 = mode.start_x, mode.start_y # display coords 2992 d1 = mode.end_x, mode.end_y 2993 2994 frustum = plt.pick_area(d0, d1) 2995 col = np.random.randint(0, 10) 2996 infru = frustum.inside_points(mesh) 2997 infru.point_size(10).color(col) 2998 plt.add(frustum, infru).render() 2999 3000 mesh = Mesh(dataurl+"cow.vtk") 3001 mesh.color("k5").linewidth(1) 3002 3003 mode = interactor_modes.BlenderStyle() 3004 mode.callback_select = mode_select 3005 3006 plt = Plotter().user_mode(mode) 3007 plt.show(mesh, axes=1) 3008 ``` 3009 """ 3010 if at is not None: 3011 ren = self.renderers[at] 3012 else: 3013 ren = self.renderer 3014 area_picker = vtki.vtkAreaPicker() 3015 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 3016 planes = area_picker.GetFrustum() 3017 3018 fru = vtki.new("FrustumSource") 3019 fru.SetPlanes(planes) 3020 fru.ShowLinesOff() 3021 fru.Update() 3022 3023 afru = vedo.Mesh(fru.GetOutput()) 3024 afru.alpha(0.1).lw(1).pickable(False) 3025 afru.name = "Frustum" 3026 return afru 3027 3028 def _scan_input_return_acts(self, objs) -> Any: 3029 # scan the input and return a list of actors 3030 if not utils.is_sequence(objs): 3031 objs = [objs] 3032 3033 ################# 3034 wannabe_acts = [] 3035 for a in objs: 3036 3037 try: 3038 wannabe_acts.append(a.actor) 3039 except AttributeError: 3040 wannabe_acts.append(a) # already actor 3041 3042 try: 3043 wannabe_acts.append(a.scalarbar) 3044 except AttributeError: 3045 pass 3046 3047 try: 3048 for sh in a.shadows: 3049 wannabe_acts.append(sh.actor) 3050 except AttributeError: 3051 pass 3052 3053 try: 3054 wannabe_acts.append(a.trail.actor) 3055 if a.trail.shadows: # trails may also have shadows 3056 for sh in a.trail.shadows: 3057 wannabe_acts.append(sh.actor) 3058 except AttributeError: 3059 pass 3060 3061 ################# 3062 scanned_acts = [] 3063 for a in wannabe_acts: # scan content of list 3064 3065 if a is None: 3066 pass 3067 3068 elif isinstance(a, (vtki.vtkActor, vtki.vtkActor2D)): 3069 scanned_acts.append(a) 3070 3071 elif isinstance(a, str): 3072 # assume a 2D comment was given 3073 changed = False # check if one already exists so to just update text 3074 if self.renderer: # might be jupyter 3075 acs = self.renderer.GetActors2D() 3076 acs.InitTraversal() 3077 for i in range(acs.GetNumberOfItems()): 3078 act = acs.GetNextItem() 3079 if isinstance(act, vedo.shapes.Text2D): 3080 aposx, aposy = act.GetPosition() 3081 if aposx < 0.01 and aposy > 0.99: # "top-left" 3082 act.text(a) # update content! no appending nada 3083 changed = True 3084 break 3085 if not changed: 3086 out = vedo.shapes.Text2D(a) # append a new one 3087 scanned_acts.append(out) 3088 # scanned_acts.append(vedo.shapes.Text2D(a)) # naive version 3089 3090 elif isinstance(a, vtki.vtkPolyData): 3091 scanned_acts.append(vedo.Mesh(a).actor) 3092 3093 elif isinstance(a, vtki.vtkImageData): 3094 scanned_acts.append(vedo.Volume(a).actor) 3095 3096 elif isinstance(a, vedo.RectilinearGrid): 3097 scanned_acts.append(a.actor) 3098 3099 elif isinstance(a, vedo.StructuredGrid): 3100 scanned_acts.append(a.actor) 3101 3102 elif isinstance(a, vtki.vtkLight): 3103 scanned_acts.append(a) 3104 3105 elif isinstance(a, vedo.visual.LightKit): 3106 a.lightkit.AddLightsToRenderer(self.renderer) 3107 3108 elif isinstance(a, vtki.get_class("MultiBlockDataSet")): 3109 for i in range(a.GetNumberOfBlocks()): 3110 b = a.GetBlock(i) 3111 if isinstance(b, vtki.vtkPolyData): 3112 scanned_acts.append(vedo.Mesh(b).actor) 3113 elif isinstance(b, vtki.vtkImageData): 3114 scanned_acts.append(vedo.Volume(b).actor) 3115 3116 elif isinstance(a, (vtki.vtkProp, vtki.vtkInteractorObserver)): 3117 scanned_acts.append(a) 3118 3119 elif "trimesh" in str(type(a)): 3120 scanned_acts.append(utils.trimesh2vedo(a)) 3121 3122 elif "meshlab" in str(type(a)): 3123 if "MeshSet" in str(type(a)): 3124 for i in range(a.number_meshes()): 3125 if a.mesh_id_exists(i): 3126 scanned_acts.append(utils.meshlab2vedo(a.mesh(i))) 3127 else: 3128 scanned_acts.append(utils.meshlab2vedo(a)) 3129 3130 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 3131 import vedo.dolfin as vdlf 3132 3133 scanned_acts.append(vdlf.IMesh(a).actor) 3134 3135 elif "madcad" in str(type(a)): 3136 scanned_acts.append(utils.madcad2vedo(a).actor) 3137 3138 elif "TetgenIO" in str(type(a)): 3139 scanned_acts.append(vedo.TetMesh(a).shrink(0.9).c("pink7").actor) 3140 3141 elif "matplotlib.figure.Figure" in str(type(a)): 3142 scanned_acts.append(vedo.Image(a).clone2d("top-right", 0.6)) 3143 3144 else: 3145 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 3146 3147 return scanned_acts 3148 3149 def show( 3150 self, 3151 *objects, 3152 at=None, 3153 axes=None, 3154 resetcam=None, 3155 zoom=False, 3156 interactive=None, 3157 viewup="", 3158 azimuth=0.0, 3159 elevation=0.0, 3160 roll=0.0, 3161 camera=None, 3162 mode=None, 3163 rate=None, 3164 bg=None, 3165 bg2=None, 3166 size=None, 3167 title=None, 3168 screenshot="", 3169 ) -> Any: 3170 """ 3171 Render a list of objects. 3172 3173 Arguments: 3174 at : (int) 3175 number of the renderer to plot to, in case of more than one exists 3176 3177 axes : (int) 3178 axis type-1 can be fully customized by passing a dictionary. 3179 Check `addons.Axes()` for the full list of options. 3180 set the type of axes to be shown: 3181 - 0, no axes 3182 - 1, draw three gray grid walls 3183 - 2, show cartesian axes from (0,0,0) 3184 - 3, show positive range of cartesian axes from (0,0,0) 3185 - 4, show a triad at bottom left 3186 - 5, show a cube at bottom left 3187 - 6, mark the corners of the bounding box 3188 - 7, draw a 3D ruler at each side of the cartesian axes 3189 - 8, show the `vtkCubeAxesActor` object 3190 - 9, show the bounding box outLine 3191 - 10, show three circles representing the maximum bounding box 3192 - 11, show a large grid on the x-y plane 3193 - 12, show polar axes 3194 - 13, draw a simple ruler at the bottom of the window 3195 3196 azimuth/elevation/roll : (float) 3197 move camera accordingly the specified value 3198 3199 viewup: str, list 3200 either `['x', 'y', 'z']` or a vector to set vertical direction 3201 3202 resetcam : (bool) 3203 re-adjust camera position to fit objects 3204 3205 camera : (dict, vtkCamera) 3206 camera parameters can further be specified with a dictionary 3207 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3208 - pos, `(list)`, the position of the camera in world coordinates 3209 - focal_point `(list)`, the focal point of the camera in world coordinates 3210 - viewup `(list)`, the view up direction for the camera 3211 - distance `(float)`, set the focal point to the specified distance from the camera position. 3212 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3213 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3214 in world-coordinate distances. The default is 1. 3215 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3216 This method has no effect in perspective projection mode. 3217 3218 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3219 plane to be set a distance 'thickness' beyond the near clipping plane. 3220 3221 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3222 measured in degrees. The default angle is 30 degrees. 3223 This method has no effect in parallel projection mode. 3224 The formula for setting the angle up for perfect perspective viewing is: 3225 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3226 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3227 3228 interactive : (bool) 3229 pause and interact with window (True) or continue execution (False) 3230 3231 rate : (float) 3232 maximum rate of `show()` in Hertz 3233 3234 mode : (int, str) 3235 set the type of interaction: 3236 - 0 = TrackballCamera [default] 3237 - 1 = TrackballActor 3238 - 2 = JoystickCamera 3239 - 3 = JoystickActor 3240 - 4 = Flight 3241 - 5 = RubberBand2D 3242 - 6 = RubberBand3D 3243 - 7 = RubberBandZoom 3244 - 8 = Terrain 3245 - 9 = Unicam 3246 - 10 = Image 3247 - Check out `vedo.interaction_modes` for more options. 3248 3249 bg : (str, list) 3250 background color in RGB format, or string name 3251 3252 bg2 : (str, list) 3253 second background color to create a gradient background 3254 3255 size : (str, list) 3256 size of the window, e.g. size="fullscreen", or size=[600,400] 3257 3258 title : (str) 3259 window title text 3260 3261 screenshot : (str) 3262 save a screenshot of the window to file 3263 """ 3264 3265 if vedo.settings.dry_run_mode >= 2: 3266 return self 3267 3268 if self.wx_widget: 3269 return self 3270 3271 if self.renderers: # in case of notebooks 3272 3273 if at is None: 3274 at = self.renderers.index(self.renderer) 3275 3276 else: 3277 3278 if at >= len(self.renderers): 3279 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3280 vedo.logger.error(t) 3281 return self 3282 3283 self.renderer = self.renderers[at] 3284 3285 if title is not None: 3286 self.title = title 3287 3288 if size is not None: 3289 self.size = size 3290 if self.size[0] == "f": # full screen 3291 self.size = "fullscreen" 3292 self.window.SetFullScreen(True) 3293 self.window.BordersOn() 3294 else: 3295 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3296 3297 if vedo.settings.default_backend == "vtk": 3298 if str(bg).endswith(".hdr"): 3299 self._add_skybox(bg) 3300 else: 3301 if bg is not None: 3302 self.backgrcol = vedo.get_color(bg) 3303 self.renderer.SetBackground(self.backgrcol) 3304 if bg2 is not None: 3305 self.renderer.GradientBackgroundOn() 3306 self.renderer.SetBackground2(vedo.get_color(bg2)) 3307 3308 if axes is not None: 3309 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3310 objects = list(objects) 3311 objects.append(axes) # move it into the list of normal things to show 3312 axes = 0 3313 self.axes = axes 3314 3315 if interactive is not None: 3316 self._interactive = interactive 3317 if self.offscreen: 3318 self._interactive = False 3319 3320 # camera stuff 3321 if resetcam is not None: 3322 self.resetcam = resetcam 3323 3324 if camera is not None: 3325 self.resetcam = False 3326 viewup = "" 3327 if isinstance(camera, vtki.vtkCamera): 3328 cameracopy = vtki.vtkCamera() 3329 cameracopy.DeepCopy(camera) 3330 self.camera = cameracopy 3331 else: 3332 self.camera = utils.camera_from_dict(camera) 3333 3334 self.add(objects) 3335 3336 # Backend ############################################################### 3337 if vedo.settings.default_backend in ["k3d", "panel"]: 3338 return backends.get_notebook_backend(self.objects) 3339 ######################################################################### 3340 3341 for ia in utils.flatten(objects): 3342 try: 3343 # fix gray color labels and title to white or black 3344 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3345 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3346 c = (0.9, 0.9, 0.9) 3347 if np.sum(self.renderer.GetBackground()) > 1.5: 3348 c = (0.1, 0.1, 0.1) 3349 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3350 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3351 except AttributeError: 3352 pass 3353 3354 if self.sharecam: 3355 for r in self.renderers: 3356 r.SetActiveCamera(self.camera) 3357 3358 if self.axes is not None: 3359 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3360 bns = self.renderer.ComputeVisiblePropBounds() 3361 addons.add_global_axes(self.axes, bounds=bns) 3362 3363 # Backend ############################################################### 3364 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3365 return backends.get_notebook_backend() 3366 ######################################################################### 3367 3368 if self.resetcam: 3369 self.renderer.ResetCamera() 3370 3371 if len(self.renderers) > 1: 3372 self.add_renderer_frame() 3373 3374 if vedo.settings.default_backend == "2d" and not zoom: 3375 zoom = "tightest" 3376 3377 if zoom: 3378 if zoom == "tight": 3379 self.reset_camera(tight=0.04) 3380 elif zoom == "tightest": 3381 self.reset_camera(tight=0.0001) 3382 else: 3383 self.camera.Zoom(zoom) 3384 if elevation: 3385 self.camera.Elevation(elevation) 3386 if azimuth: 3387 self.camera.Azimuth(azimuth) 3388 if roll: 3389 self.camera.Roll(roll) 3390 3391 if len(viewup) > 0: 3392 b = self.renderer.ComputeVisiblePropBounds() 3393 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3394 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3395 if viewup == "x": 3396 sz = np.linalg.norm(sz) 3397 self.camera.SetViewUp([1, 0, 0]) 3398 self.camera.SetPosition(cm + sz) 3399 elif viewup == "y": 3400 sz = np.linalg.norm(sz) 3401 self.camera.SetViewUp([0, 1, 0]) 3402 self.camera.SetPosition(cm + sz) 3403 elif viewup == "z": 3404 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3405 self.camera.SetViewUp([0, 0, 1]) 3406 self.camera.SetPosition(cm + 2 * sz) 3407 elif utils.is_sequence(viewup): 3408 sz = np.linalg.norm(sz) 3409 self.camera.SetViewUp(viewup) 3410 cpos = np.cross([0, 1, 0], viewup) 3411 self.camera.SetPosition(cm - 2 * sz * cpos) 3412 3413 self.renderer.ResetCameraClippingRange() 3414 3415 self.initialize_interactor() 3416 3417 if vedo.settings.immediate_rendering: 3418 self.window.Render() ##################### <-------------- Render 3419 3420 if self.interactor: # can be offscreen or not the vtk backend.. 3421 3422 self.window.SetWindowName(self.title) 3423 3424 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3425 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3426 # print(pic.dataset)# Array 0 name PNGImage 3427 # self.window.SetIcon(pic.dataset) 3428 3429 try: 3430 # Needs "pip install pyobjc" on Mac OSX 3431 if ( 3432 self._cocoa_initialized is False 3433 and "Darwin" in vedo.sys_platform 3434 and not self.offscreen 3435 ): 3436 self._cocoa_initialized = True 3437 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3438 pid = os.getpid() 3439 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3440 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3441 except: 3442 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3443 pass 3444 3445 # Set the interaction style 3446 if mode is not None: 3447 self.user_mode(mode) 3448 if self.qt_widget and mode is None: 3449 self.user_mode(0) 3450 3451 if screenshot: 3452 self.screenshot(screenshot) 3453 3454 if self._interactive: 3455 self.interactor.Start() 3456 if self._must_close_now: 3457 self.interactor.GetRenderWindow().Finalize() 3458 self.interactor.TerminateApp() 3459 self.camera = None 3460 self.renderer = None 3461 self.renderers = [] 3462 self.window = None 3463 self.interactor = None 3464 return self 3465 3466 if rate: 3467 if self.clock is None: # set clock and limit rate 3468 self._clockt0 = time.time() 3469 self.clock = 0.0 3470 else: 3471 t = time.time() - self._clockt0 3472 elapsed = t - self.clock 3473 mint = 1.0 / rate 3474 if elapsed < mint: 3475 time.sleep(mint - elapsed) 3476 self.clock = time.time() - self._clockt0 3477 3478 # 2d #################################################################### 3479 if vedo.settings.default_backend in ["2d"]: 3480 return backends.get_notebook_backend() 3481 ######################################################################### 3482 3483 return self 3484 3485 3486 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3487 """Add a draggable inset space into a renderer. 3488 3489 Arguments: 3490 at : (int) 3491 specify the renderer number 3492 pos : (list) 3493 icon position in the range [1-4] indicating one of the 4 corners, 3494 or it can be a tuple (x,y) as a fraction of the renderer size. 3495 size : (float) 3496 size of the square inset 3497 draggable : (bool) 3498 if True the subrenderer space can be dragged around 3499 c : (color) 3500 color of the inset frame when dragged 3501 3502 Examples: 3503 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3504 3505  3506 """ 3507 if not self.interactor: 3508 return None 3509 3510 if not self.renderer: 3511 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3512 return None 3513 3514 options = dict(options) 3515 pos = options.pop("pos", 0) 3516 size = options.pop("size", 0.1) 3517 c = options.pop("c", "lb") 3518 at = options.pop("at", None) 3519 draggable = options.pop("draggable", True) 3520 3521 r, g, b = vedo.get_color(c) 3522 widget = vtki.vtkOrientationMarkerWidget() 3523 widget.SetOutlineColor(r, g, b) 3524 if len(objects) == 1: 3525 widget.SetOrientationMarker(objects[0].actor) 3526 else: 3527 widget.SetOrientationMarker(vedo.Assembly(objects)) 3528 3529 widget.SetInteractor(self.interactor) 3530 3531 if utils.is_sequence(pos): 3532 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3533 else: 3534 if pos < 2: 3535 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3536 elif pos == 2: 3537 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3538 elif pos == 3: 3539 widget.SetViewport(0, 0, size * 2, size * 2) 3540 elif pos == 4: 3541 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3542 widget.EnabledOn() 3543 widget.SetInteractive(draggable) 3544 if at is not None and at < len(self.renderers): 3545 widget.SetCurrentRenderer(self.renderers[at]) 3546 else: 3547 widget.SetCurrentRenderer(self.renderer) 3548 self.widgets.append(widget) 3549 return widget 3550 3551 def clear(self, at=None, deep=False) -> Self: 3552 """Clear the scene from all meshes and volumes.""" 3553 if at is not None: 3554 renderer = self.renderers[at] 3555 else: 3556 renderer = self.renderer 3557 if not renderer: 3558 return self 3559 3560 if deep: 3561 renderer.RemoveAllViewProps() 3562 else: 3563 for ob in set( 3564 self.get_meshes() 3565 + self.get_volumes() 3566 + self.objects 3567 + self.axes_instances 3568 ): 3569 if isinstance(ob, vedo.shapes.Text2D): 3570 continue 3571 self.remove(ob) 3572 try: 3573 if ob.scalarbar: 3574 self.remove(ob.scalarbar) 3575 except AttributeError: 3576 pass 3577 return self 3578 3579 def break_interaction(self) -> Self: 3580 """Break window interaction and return to the python execution flow""" 3581 if self.interactor: 3582 self.check_actors_trasform() 3583 self.interactor.ExitCallback() 3584 return self 3585 3586 def freeze(self, value=True) -> Self: 3587 """Freeze the current renderer. Use this with `sharecam=False`.""" 3588 if not self.interactor: 3589 return self 3590 if not self.renderer: 3591 return self 3592 self.renderer.SetInteractive(not value) 3593 return self 3594 3595 def user_mode(self, mode) -> Self: 3596 """ 3597 Modify the user interaction mode. 3598 3599 Examples: 3600 ```python 3601 from vedo import * 3602 mode = interactor_modes.MousePan() 3603 mesh = Mesh(dataurl+"cow.vtk") 3604 plt = Plotter().user_mode(mode) 3605 plt.show(mesh, axes=1) 3606 ``` 3607 See also: 3608 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3609 """ 3610 if not self.interactor: 3611 return self 3612 3613 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3614 # print("Current style:", curr_style) 3615 if curr_style.endswith("Actor"): 3616 self.check_actors_trasform() 3617 3618 if isinstance(mode, (str, int)): 3619 # Set the style of interaction 3620 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3621 if mode in (0, "TrackballCamera"): 3622 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3623 self.interactor.RemoveObservers("CharEvent") 3624 elif mode in (1, "TrackballActor"): 3625 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3626 elif mode in (2, "JoystickCamera"): 3627 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3628 elif mode in (3, "JoystickActor"): 3629 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3630 elif mode in (4, "Flight"): 3631 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3632 elif mode in (5, "RubberBand2D"): 3633 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3634 elif mode in (6, "RubberBand3D"): 3635 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3636 elif mode in (7, "RubberBandZoom"): 3637 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3638 elif mode in (8, "Terrain"): 3639 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3640 elif mode in (9, "Unicam"): 3641 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3642 elif mode in (10, "Image", "image", "2d"): 3643 astyle = vtki.new("InteractorStyleImage") 3644 astyle.SetInteractionModeToImage3D() 3645 self.interactor.SetInteractorStyle(astyle) 3646 else: 3647 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3648 3649 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3650 # set a custom interactor style 3651 if hasattr(mode, "interactor"): 3652 mode.interactor = self.interactor 3653 mode.renderer = self.renderer # type: ignore 3654 mode.SetInteractor(self.interactor) 3655 mode.SetDefaultRenderer(self.renderer) 3656 self.interactor.SetInteractorStyle(mode) 3657 3658 return self 3659 3660 def close(self) -> Self: 3661 """Close the plotter.""" 3662 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3663 vedo.last_figure = None 3664 self.last_event = None 3665 self.sliders = [] 3666 self.buttons = [] 3667 self.widgets = [] 3668 self.hover_legends = [] 3669 self.background_renderer = None 3670 self._extralight = None 3671 3672 self.hint_widget = None 3673 self.cutter_widget = None 3674 3675 if vedo.settings.dry_run_mode >= 2: 3676 return self 3677 3678 if not hasattr(self, "window"): 3679 return self 3680 if not self.window: 3681 return self 3682 if not hasattr(self, "interactor"): 3683 return self 3684 if not self.interactor: 3685 return self 3686 3687 ################################################### 3688 try: 3689 if "Darwin" in vedo.sys_platform: 3690 self.interactor.ProcessEvents() 3691 except: 3692 pass 3693 3694 self._must_close_now = True 3695 3696 if vedo.plotter_instance == self: 3697 vedo.plotter_instance = None 3698 3699 if self.interactor and self._interactive: 3700 self.break_interaction() 3701 elif self._must_close_now: 3702 # dont call ExitCallback here 3703 if self.interactor: 3704 self.break_interaction() 3705 self.interactor.GetRenderWindow().Finalize() 3706 self.interactor.TerminateApp() 3707 self.camera = None 3708 self.renderer = None 3709 self.renderers = [] 3710 self.window = None 3711 self.interactor = None 3712 return self 3713 3714 @property 3715 def camera(self): 3716 """Return the current active camera.""" 3717 if self.renderer: 3718 return self.renderer.GetActiveCamera() 3719 3720 @camera.setter 3721 def camera(self, cam): 3722 if self.renderer: 3723 if isinstance(cam, dict): 3724 cam = utils.camera_from_dict(cam) 3725 self.renderer.SetActiveCamera(cam) 3726 3727 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3728 """ 3729 Take a screenshot of the Plotter window. 3730 3731 Arguments: 3732 scale : (int) 3733 set image magnification as an integer multiplicating factor 3734 asarray : (bool) 3735 return a numpy array of the image instead of writing a file 3736 3737 Warning: 3738 If you get black screenshots try to set `interactive=False` in `show()` 3739 then call `screenshot()` and `plt.interactive()` afterwards. 3740 3741 Example: 3742 ```py 3743 from vedo import * 3744 sphere = Sphere().linewidth(1) 3745 plt = show(sphere, interactive=False) 3746 plt.screenshot('image.png') 3747 plt.interactive() 3748 plt.close() 3749 ``` 3750 3751 Example: 3752 ```py 3753 from vedo import * 3754 sphere = Sphere().linewidth(1) 3755 plt = show(sphere, interactive=False) 3756 plt.screenshot('anotherimage.png') 3757 plt.interactive() 3758 plt.close() 3759 ``` 3760 """ 3761 return vedo.file_io.screenshot(filename, scale, asarray) 3762 3763 def toimage(self, scale=1) -> "vedo.image.Image": 3764 """ 3765 Generate a `Image` object from the current rendering window. 3766 3767 Arguments: 3768 scale : (int) 3769 set image magnification as an integer multiplicating factor 3770 """ 3771 if vedo.settings.screeshot_large_image: 3772 w2if = vtki.new("RenderLargeImage") 3773 w2if.SetInput(self.renderer) 3774 w2if.SetMagnification(scale) 3775 else: 3776 w2if = vtki.new("WindowToImageFilter") 3777 w2if.SetInput(self.window) 3778 if hasattr(w2if, "SetScale"): 3779 w2if.SetScale(scale, scale) 3780 if vedo.settings.screenshot_transparent_background: 3781 w2if.SetInputBufferTypeToRGBA() 3782 w2if.ReadFrontBufferOff() # read from the back buffer 3783 w2if.Update() 3784 return vedo.image.Image(w2if.GetOutput()) 3785 3786 def export(self, filename="scene.npz", binary=False) -> Self: 3787 """ 3788 Export scene to file to HTML, X3D or Numpy file. 3789 3790 Examples: 3791 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3792 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3793 """ 3794 vedo.file_io.export_window(filename, binary=binary) 3795 return self 3796 3797 def color_picker(self, xy, verbose=False): 3798 """Pick color of specific (x,y) pixel on the screen.""" 3799 w2if = vtki.new("WindowToImageFilter") 3800 w2if.SetInput(self.window) 3801 w2if.ReadFrontBufferOff() 3802 w2if.Update() 3803 nx, ny = self.window.GetSize() 3804 varr = w2if.GetOutput().GetPointData().GetScalars() 3805 3806 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3807 x, y = int(xy[0]), int(xy[1]) 3808 if y < ny and x < nx: 3809 3810 rgb = arr[y, x] 3811 3812 if verbose: 3813 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3814 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3815 vedo.printc("█", c=[0, rgb[1], 0], end="") 3816 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3817 vedo.printc("] = ", end="") 3818 cnm = vedo.get_color_name(rgb) 3819 if np.sum(rgb) < 150: 3820 vedo.printc( 3821 rgb.tolist(), 3822 vedo.colors.rgb2hex(np.array(rgb) / 255), 3823 c="w", 3824 bc=rgb, 3825 invert=1, 3826 end="", 3827 ) 3828 vedo.printc(" -> " + cnm, invert=1, c="w") 3829 else: 3830 vedo.printc( 3831 rgb.tolist(), 3832 vedo.colors.rgb2hex(np.array(rgb) / 255), 3833 c=rgb, 3834 end="", 3835 ) 3836 vedo.printc(" -> " + cnm, c=cnm) 3837 3838 return rgb 3839 3840 return None 3841 3842 ####################################################################### 3843 def _default_mouseleftclick(self, iren, event) -> None: 3844 x, y = iren.GetEventPosition() 3845 renderer = iren.FindPokedRenderer(x, y) 3846 picker = vtki.vtkPropPicker() 3847 picker.PickProp(x, y, renderer) 3848 3849 self.renderer = renderer 3850 3851 clicked_actor = picker.GetActor() 3852 # clicked_actor2D = picker.GetActor2D() 3853 3854 # print('_default_mouseleftclick mouse at', x, y) 3855 # print("picked Volume:", [picker.GetVolume()]) 3856 # print("picked Actor2D:", [picker.GetActor2D()]) 3857 # print("picked Assembly:", [picker.GetAssembly()]) 3858 # print("picked Prop3D:", [picker.GetProp3D()]) 3859 3860 if not clicked_actor: 3861 clicked_actor = picker.GetAssembly() 3862 3863 if not clicked_actor: 3864 clicked_actor = picker.GetProp3D() 3865 3866 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3867 return 3868 3869 self.picked3d = picker.GetPickPosition() 3870 self.picked2d = np.array([x, y]) 3871 3872 if not clicked_actor: 3873 return 3874 3875 self.justremoved = None 3876 self.clicked_actor = clicked_actor 3877 3878 try: # might not be a vedo obj 3879 self.clicked_object = clicked_actor.retrieve_object() 3880 # save this info in the object itself 3881 self.clicked_object.picked3d = self.picked3d 3882 self.clicked_object.picked2d = self.picked2d 3883 except AttributeError: 3884 pass 3885 3886 # ----------- 3887 # if "Histogram1D" in picker.GetAssembly().__class__.__name__: 3888 # histo = picker.GetAssembly() 3889 # if histo.verbose: 3890 # x = self.picked3d[0] 3891 # idx = np.digitize(x, histo.edges) - 1 3892 # f = histo.frequencies[idx] 3893 # cn = histo.centers[idx] 3894 # vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3895 3896 ####################################################################### 3897 def _default_keypress(self, iren, event) -> None: 3898 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3899 3900 key = iren.GetKeySym() 3901 3902 if "_L" in key or "_R" in key: 3903 return 3904 3905 if iren.GetShiftKey(): 3906 key = key.upper() 3907 3908 if iren.GetControlKey(): 3909 key = "Ctrl+" + key 3910 3911 if iren.GetAltKey(): 3912 key = "Alt+" + key 3913 3914 ####################################################### 3915 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3916 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3917 # iren.GetKeyCode(), iren.GetRepeatCount()) 3918 ####################################################### 3919 3920 x, y = iren.GetEventPosition() 3921 renderer = iren.FindPokedRenderer(x, y) 3922 3923 if key in ["q", "Return"]: 3924 self.break_interaction() 3925 return 3926 3927 elif key in ["Ctrl+q", "Ctrl+w", "Escape"]: 3928 self.close() 3929 return 3930 3931 elif key == "F1": 3932 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3933 self.break_interaction() 3934 sys.exit(0) 3935 3936 elif key == "Down": 3937 if self.clicked_object and self.clicked_object in self.get_meshes(): 3938 self.clicked_object.alpha(0.02) 3939 if hasattr(self.clicked_object, "properties_backface"): 3940 bfp = self.clicked_actor.GetBackfaceProperty() 3941 self.clicked_object.properties_backface = bfp # save it 3942 self.clicked_actor.SetBackfaceProperty(None) 3943 else: 3944 for obj in self.get_meshes(): 3945 if obj: 3946 obj.alpha(0.02) 3947 bfp = obj.actor.GetBackfaceProperty() 3948 if bfp and hasattr(obj, "properties_backface"): 3949 obj.properties_backface = bfp 3950 obj.actor.SetBackfaceProperty(None) 3951 3952 elif key == "Left": 3953 if self.clicked_object and self.clicked_object in self.get_meshes(): 3954 ap = self.clicked_object.properties 3955 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3956 ap.SetOpacity(aal) 3957 bfp = self.clicked_actor.GetBackfaceProperty() 3958 if bfp and hasattr(self.clicked_object, "properties_backface"): 3959 self.clicked_object.properties_backface = bfp 3960 self.clicked_actor.SetBackfaceProperty(None) 3961 else: 3962 for a in self.get_meshes(): 3963 if a: 3964 ap = a.properties 3965 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3966 ap.SetOpacity(aal) 3967 bfp = a.actor.GetBackfaceProperty() 3968 if bfp and hasattr(a, "properties_backface"): 3969 a.properties_backface = bfp 3970 a.actor.SetBackfaceProperty(None) 3971 3972 elif key == "Right": 3973 if self.clicked_object and self.clicked_object in self.get_meshes(): 3974 ap = self.clicked_object.properties 3975 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3976 ap.SetOpacity(aal) 3977 if ( 3978 aal == 1 3979 and hasattr(self.clicked_object, "properties_backface") 3980 and self.clicked_object.properties_backface 3981 ): 3982 # put back 3983 self.clicked_actor.SetBackfaceProperty( 3984 self.clicked_object.properties_backface) 3985 else: 3986 for a in self.get_meshes(): 3987 if a: 3988 ap = a.properties 3989 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3990 ap.SetOpacity(aal) 3991 if aal == 1 and hasattr(a, "properties_backface") and a.properties_backface: 3992 a.actor.SetBackfaceProperty(a.properties_backface) 3993 3994 elif key == "Up": 3995 if self.clicked_object and self.clicked_object in self.get_meshes(): 3996 self.clicked_object.properties.SetOpacity(1) 3997 if hasattr(self.clicked_object, "properties_backface") and self.clicked_object.properties_backface: 3998 self.clicked_object.actor.SetBackfaceProperty(self.clicked_object.properties_backface) 3999 else: 4000 for a in self.get_meshes(): 4001 if a: 4002 a.properties.SetOpacity(1) 4003 if hasattr(a, "properties_backface") and a.properties_backface: 4004 a.actor.SetBackfaceProperty(a.properties_backface) 4005 4006 elif key == "P": 4007 if self.clicked_object and self.clicked_object in self.get_meshes(): 4008 objs = [self.clicked_object] 4009 else: 4010 objs = self.get_meshes() 4011 for ia in objs: 4012 try: 4013 ps = ia.properties.GetPointSize() 4014 if ps > 1: 4015 ia.properties.SetPointSize(ps - 1) 4016 ia.properties.SetRepresentationToPoints() 4017 except AttributeError: 4018 pass 4019 4020 elif key == "p": 4021 if self.clicked_object and self.clicked_object in self.get_meshes(): 4022 objs = [self.clicked_object] 4023 else: 4024 objs = self.get_meshes() 4025 for ia in objs: 4026 try: 4027 ps = ia.properties.GetPointSize() 4028 ia.properties.SetPointSize(ps + 2) 4029 ia.properties.SetRepresentationToPoints() 4030 except AttributeError: 4031 pass 4032 4033 elif key == "U": 4034 pval = renderer.GetActiveCamera().GetParallelProjection() 4035 renderer.GetActiveCamera().SetParallelProjection(not pval) 4036 if pval: 4037 renderer.ResetCamera() 4038 4039 elif key == "r": 4040 renderer.ResetCamera() 4041 4042 elif key == "h": 4043 msg = f" vedo {vedo.__version__}" 4044 msg += f" | vtk {vtki.vtkVersion().GetVTKVersion()}" 4045 msg += f" | numpy {np.__version__}" 4046 msg += f" | python {sys.version_info[0]}.{sys.version_info[1]}, press: " 4047 vedo.printc(msg.ljust(75), invert=True) 4048 msg = ( 4049 " i print info about the last clicked object \n" 4050 " I print color of the pixel under the mouse \n" 4051 " Y show the pipeline for this object as a graph \n" 4052 " <- -> use arrows to reduce/increase opacity \n" 4053 " x toggle mesh visibility \n" 4054 " w toggle wireframe/surface style \n" 4055 " l toggle surface edges visibility \n" 4056 " p/P hide surface faces and show only points \n" 4057 " 1-3 cycle surface color (2=light, 3=dark) \n" 4058 " 4 cycle color map (press shift-4 to go back) \n" 4059 " 5-6 cycle point-cell arrays (shift to go back) \n" 4060 " 7-8 cycle background and gradient color \n" 4061 " 09+- cycle axes styles (on keypad, or press +/-) \n" 4062 " k cycle available lighting styles \n" 4063 " K toggle shading as flat or phong \n" 4064 " A toggle anti-aliasing \n" 4065 " D toggle depth-peeling (for transparencies) \n" 4066 " U toggle perspective/parallel projection \n" 4067 " o/O toggle extra light to scene and rotate it \n" 4068 " a toggle interaction to Actor Mode \n" 4069 " n toggle surface normals \n" 4070 " r reset camera position \n" 4071 " R reset camera to the closest orthogonal view \n" 4072 " . fly camera to the last clicked point \n" 4073 " C print the current camera parameters state \n" 4074 " X invoke a cutter widget tool \n" 4075 " S save a screenshot of the current scene \n" 4076 " E/F export 3D scene to numpy file or X3D \n" 4077 " q return control to python script \n" 4078 " Esc abort execution and exit python kernel " 4079 ) 4080 vedo.printc(msg, dim=True, italic=True, bold=True) 4081 vedo.printc( 4082 " Check out the documentation at: https://vedo.embl.es ".ljust(75), 4083 invert=True, 4084 bold=True, 4085 ) 4086 return 4087 4088 elif key == "a": 4089 cur = iren.GetInteractorStyle() 4090 if isinstance(cur, vtki.get_class("InteractorStyleTrackballCamera")): 4091 msg = "Interactor style changed to TrackballActor\n" 4092 msg += " you can now move and rotate individual meshes:\n" 4093 msg += " press X twice to save the repositioned mesh\n" 4094 msg += " press 'a' to go back to normal style" 4095 vedo.printc(msg) 4096 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 4097 else: 4098 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 4099 return 4100 4101 elif key == "A": # toggle antialiasing 4102 msam = self.window.GetMultiSamples() 4103 if not msam: 4104 self.window.SetMultiSamples(16) 4105 else: 4106 self.window.SetMultiSamples(0) 4107 msam = self.window.GetMultiSamples() 4108 if msam: 4109 vedo.printc(f"Antialiasing set to {msam} samples", c=bool(msam)) 4110 else: 4111 vedo.printc("Antialiasing disabled", c=bool(msam)) 4112 4113 elif key == "D": # toggle depthpeeling 4114 udp = not renderer.GetUseDepthPeeling() 4115 renderer.SetUseDepthPeeling(udp) 4116 # self.renderer.SetUseDepthPeelingForVolumes(udp) 4117 if udp: 4118 self.window.SetAlphaBitPlanes(1) 4119 renderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 4120 renderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 4121 self.interactor.Render() 4122 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 4123 rnr = self.renderers.index(renderer) 4124 vedo.printc(f"Depth peeling set to {udp} for renderer nr.{rnr}", c=udp) 4125 if not wasUsed and udp: 4126 vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True) 4127 return 4128 4129 elif key == "period": 4130 if self.picked3d: 4131 self.fly_to(self.picked3d) 4132 return 4133 4134 elif key == "S": 4135 fname = "screenshot.png" 4136 i = 1 4137 while os.path.isfile(fname): 4138 fname = f"screenshot{i}.png" 4139 i += 1 4140 vedo.file_io.screenshot(fname) 4141 vedo.printc(rf":camera: Saved rendering window to {fname}", c="b") 4142 return 4143 4144 elif key == "C": 4145 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 4146 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 4147 # This could happen for plotting geological models with UTM coordinate systems 4148 cam = renderer.GetActiveCamera() 4149 vedo.printc("\n###################################################", c="y") 4150 vedo.printc("## Template python code to position this camera: ##", c="y") 4151 vedo.printc("cam = dict(", c="y") 4152 vedo.printc(" pos=" + utils.precision(cam.GetPosition(), 6) + ",", c="y") 4153 vedo.printc(" focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y") 4154 vedo.printc(" viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y") 4155 vedo.printc(" roll=" + utils.precision(cam.GetRoll(), 6) + ",", c="y") 4156 if cam.GetParallelProjection(): 4157 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 4158 else: 4159 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 4160 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 4161 vedo.printc(')', c='y') 4162 vedo.printc('show(mymeshes, camera=cam)', c='y') 4163 vedo.printc('###################################################', c='y') 4164 return 4165 4166 elif key == "R": 4167 self.reset_viewup() 4168 4169 elif key == "w": 4170 try: 4171 if self.clicked_object.properties.GetRepresentation() == 1: # toggle 4172 self.clicked_object.properties.SetRepresentationToSurface() 4173 else: 4174 self.clicked_object.properties.SetRepresentationToWireframe() 4175 except AttributeError: 4176 pass 4177 4178 elif key == "1": 4179 try: 4180 self._icol += 1 4181 self.clicked_object.mapper.ScalarVisibilityOff() 4182 pal = vedo.colors.palettes[vedo.settings.palette % len(vedo.colors.palettes)] 4183 self.clicked_object.c(pal[(self._icol) % 10]) 4184 self.remove(self.clicked_object.scalarbar) 4185 except AttributeError: 4186 pass 4187 4188 elif key == "2": # dark colors 4189 try: 4190 bsc = ["k1", "k2", "k3", "k4", 4191 "b1", "b2", "b3", "b4", 4192 "p1", "p2", "p3", "p4", 4193 "g1", "g2", "g3", "g4", 4194 "r1", "r2", "r3", "r4", 4195 "o1", "o2", "o3", "o4", 4196 "y1", "y2", "y3", "y4"] 4197 self._icol += 1 4198 if self.clicked_object: 4199 self.clicked_object.mapper.ScalarVisibilityOff() 4200 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4201 self.clicked_object.c(newcol) 4202 self.remove(self.clicked_object.scalarbar) 4203 except AttributeError: 4204 pass 4205 4206 elif key == "3": # light colors 4207 try: 4208 bsc = ["k6", "k7", "k8", "k9", 4209 "b6", "b7", "b8", "b9", 4210 "p6", "p7", "p8", "p9", 4211 "g6", "g7", "g8", "g9", 4212 "r6", "r7", "r8", "r9", 4213 "o6", "o7", "o8", "o9", 4214 "y6", "y7", "y8", "y9"] 4215 self._icol += 1 4216 if self.clicked_object: 4217 self.clicked_object.mapper.ScalarVisibilityOff() 4218 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4219 self.clicked_object.c(newcol) 4220 self.remove(self.clicked_object.scalarbar) 4221 except AttributeError: 4222 pass 4223 4224 elif key == "4": # cmap name cycle 4225 ob = self.clicked_object 4226 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4227 return 4228 if not ob.mapper.GetScalarVisibility(): 4229 return 4230 onwhat = ob.mapper.GetScalarModeAsString() # UsePointData/UseCellData 4231 4232 cmap_names = [ 4233 "Accent", "Paired", 4234 "rainbow", "rainbow_r", 4235 "Spectral", "Spectral_r", 4236 "gist_ncar", "gist_ncar_r", 4237 "viridis", "viridis_r", 4238 "hot", "hot_r", 4239 "terrain", "ocean", 4240 "coolwarm", "seismic", "PuOr", "RdYlGn", 4241 ] 4242 try: 4243 i = cmap_names.index(ob._cmap_name) 4244 if iren.GetShiftKey(): 4245 i -= 1 4246 else: 4247 i += 1 4248 if i >= len(cmap_names): 4249 i = 0 4250 if i < 0: 4251 i = len(cmap_names) - 1 4252 except ValueError: 4253 i = 0 4254 4255 ob._cmap_name = cmap_names[i] 4256 ob.cmap(ob._cmap_name, on=onwhat) 4257 if ob.scalarbar: 4258 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4259 self.remove(ob.scalarbar) 4260 title = ob.scalarbar.GetTitle() 4261 ob.add_scalarbar(title=title) 4262 self.add(ob.scalarbar).render() 4263 elif isinstance(ob.scalarbar, vedo.Assembly): 4264 self.remove(ob.scalarbar) 4265 ob.add_scalarbar3d(title=ob._cmap_name) 4266 self.add(ob.scalarbar) 4267 4268 vedo.printc( 4269 f"Name:'{ob.name}'," if ob.name else "", 4270 f"range:{utils.precision(ob.mapper.GetScalarRange(),3)},", 4271 f"colormap:'{ob._cmap_name}'", c="g", bold=False, 4272 ) 4273 4274 elif key == "5": # cycle pointdata array 4275 ob = self.clicked_object 4276 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4277 return 4278 4279 arrnames = ob.pointdata.keys() 4280 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4281 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4282 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4283 if len(arrnames) == 0: 4284 return 4285 ob.mapper.SetScalarVisibility(1) 4286 4287 if not ob._cmap_name: 4288 ob._cmap_name = "rainbow" 4289 4290 try: 4291 curr_name = ob.dataset.GetPointData().GetScalars().GetName() 4292 i = arrnames.index(curr_name) 4293 if "normals" in curr_name.lower(): 4294 return 4295 if iren.GetShiftKey(): 4296 i -= 1 4297 else: 4298 i += 1 4299 if i >= len(arrnames): 4300 i = 0 4301 if i < 0: 4302 i = len(arrnames) - 1 4303 except (ValueError, AttributeError): 4304 i = 0 4305 4306 ob.cmap(ob._cmap_name, arrnames[i], on="points") 4307 if ob.scalarbar: 4308 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4309 self.remove(ob.scalarbar) 4310 title = ob.scalarbar.GetTitle() 4311 ob.scalarbar = None 4312 ob.add_scalarbar(title=arrnames[i]) 4313 self.add(ob.scalarbar) 4314 elif isinstance(ob.scalarbar, vedo.Assembly): 4315 self.remove(ob.scalarbar) 4316 ob.scalarbar = None 4317 ob.add_scalarbar3d(title=arrnames[i]) 4318 self.add(ob.scalarbar) 4319 else: 4320 vedo.printc( 4321 f"Name:'{ob.name}'," if ob.name else "", 4322 f"active pointdata array: '{arrnames[i]}'", 4323 c="g", bold=False, 4324 ) 4325 4326 elif key == "6": # cycle celldata array 4327 ob = self.clicked_object 4328 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4329 return 4330 4331 arrnames = ob.celldata.keys() 4332 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4333 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4334 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4335 if len(arrnames) == 0: 4336 return 4337 ob.mapper.SetScalarVisibility(1) 4338 4339 if not ob._cmap_name: 4340 ob._cmap_name = "rainbow" 4341 4342 try: 4343 curr_name = ob.dataset.GetCellData().GetScalars().GetName() 4344 i = arrnames.index(curr_name) 4345 if "normals" in curr_name.lower(): 4346 return 4347 if iren.GetShiftKey(): 4348 i -= 1 4349 else: 4350 i += 1 4351 if i >= len(arrnames): 4352 i = 0 4353 if i < 0: 4354 i = len(arrnames) - 1 4355 except (ValueError, AttributeError): 4356 i = 0 4357 4358 ob.cmap(ob._cmap_name, arrnames[i], on="cells") 4359 if ob.scalarbar: 4360 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4361 self.remove(ob.scalarbar) 4362 title = ob.scalarbar.GetTitle() 4363 ob.scalarbar = None 4364 ob.add_scalarbar(title=arrnames[i]) 4365 self.add(ob.scalarbar) 4366 elif isinstance(ob.scalarbar, vedo.Assembly): 4367 self.remove(ob.scalarbar) 4368 ob.scalarbar = None 4369 ob.add_scalarbar3d(title=arrnames[i]) 4370 self.add(ob.scalarbar) 4371 else: 4372 vedo.printc( 4373 f"Name:'{ob.name}'," if ob.name else "", 4374 f"active celldata array: '{arrnames[i]}'", 4375 c="g", bold=False, 4376 ) 4377 4378 elif key == "7": 4379 bgc = np.array(renderer.GetBackground()).sum() / 3 4380 if bgc <= 0: 4381 bgc = 0.223 4382 elif 0 < bgc < 1: 4383 bgc = 1 4384 else: 4385 bgc = 0 4386 renderer.SetBackground(bgc, bgc, bgc) 4387 4388 elif key == "8": 4389 bg2cols = [ 4390 "lightyellow", 4391 "darkseagreen", 4392 "palegreen", 4393 "steelblue", 4394 "lightblue", 4395 "cadetblue", 4396 "lavender", 4397 "white", 4398 "blackboard", 4399 "black", 4400 ] 4401 bg2name = vedo.get_color_name(renderer.GetBackground2()) 4402 if bg2name in bg2cols: 4403 idx = bg2cols.index(bg2name) 4404 else: 4405 idx = 4 4406 if idx is not None: 4407 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 4408 if not bg2name_next: 4409 renderer.GradientBackgroundOff() 4410 else: 4411 renderer.GradientBackgroundOn() 4412 renderer.SetBackground2(vedo.get_color(bg2name_next)) 4413 4414 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 4415 i = self.renderers.index(renderer) 4416 try: 4417 self.axes_instances[i].EnabledOff() 4418 self.axes_instances[i].SetInteractor(None) 4419 except AttributeError: 4420 # print("Cannot remove widget", [self.axes_instances[i]]) 4421 try: 4422 self.remove(self.axes_instances[i]) 4423 except: 4424 print("Cannot remove axes", [self.axes_instances[i]]) 4425 return 4426 self.axes_instances[i] = None 4427 4428 if not self.axes: 4429 self.axes = 0 4430 if isinstance(self.axes, dict): 4431 self.axes = 1 4432 4433 if key in ["minus", "KP_Subtract"]: 4434 if not self.camera.GetParallelProjection() and self.axes == 0: 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 else: 4439 if not self.camera.GetParallelProjection() and self.axes == 12: 4440 self.axes += 1 # jump ruler doesnt make sense in perspective mode 4441 bns = self.renderer.ComputeVisiblePropBounds() 4442 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns) 4443 self.render() 4444 4445 elif "KP_" in key or key in [ 4446 "Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior" 4447 ]: 4448 asso = { # change axes style 4449 "KP_Insert": 0, "KP_0": 0, "Insert": 0, 4450 "KP_End": 1, "KP_1": 1, "End": 1, 4451 "KP_Down": 2, "KP_2": 2, "Down": 2, 4452 "KP_Next": 3, "KP_3": 3, "Next": 3, 4453 "KP_Left": 4, "KP_4": 4, "Left": 4, 4454 "KP_Begin": 5, "KP_5": 5, "Begin": 5, 4455 "KP_Right": 6, "KP_6": 6, "Right": 6, 4456 "KP_Home": 7, "KP_7": 7, "Home": 7, 4457 "KP_Up": 8, "KP_8": 8, "Up": 8, 4458 "Prior": 9, # on windows OS 4459 } 4460 clickedr = self.renderers.index(renderer) 4461 if key in asso: 4462 if self.axes_instances[clickedr]: 4463 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 4464 self.axes_instances[clickedr].EnabledOff() 4465 else: 4466 try: 4467 renderer.RemoveActor(self.axes_instances[clickedr]) 4468 except: 4469 pass 4470 self.axes_instances[clickedr] = None 4471 bounds = renderer.ComputeVisiblePropBounds() 4472 addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds) 4473 self.interactor.Render() 4474 4475 if key == "O": 4476 renderer.RemoveLight(self._extralight) 4477 self._extralight = None 4478 4479 elif key == "o": 4480 vbb, sizes, _, _ = addons.compute_visible_bounds() 4481 cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2) 4482 if not self._extralight: 4483 vup = renderer.GetActiveCamera().GetViewUp() 4484 pos = cm + utils.vector(vup) * utils.mag(sizes) 4485 self._extralight = addons.Light(pos, focal_point=cm, intensity=0.4) 4486 renderer.AddLight(self._extralight) 4487 vedo.printc("Press 'o' again to rotate light source, or 'O' to remove it.", c='y') 4488 else: 4489 cpos = utils.vector(self._extralight.GetPosition()) 4490 x, y, z = self._extralight.GetPosition() - cm 4491 r, th, ph = transformations.cart2spher(x, y, z) 4492 th += 0.2 4493 if th > np.pi: 4494 th = np.random.random() * np.pi / 2 4495 ph += 0.3 4496 cpos = transformations.spher2cart(r, th, ph).T + cm 4497 self._extralight.SetPosition(cpos) 4498 4499 elif key == "l": 4500 if self.clicked_object in self.get_meshes(): 4501 objs = [self.clicked_object] 4502 else: 4503 objs = self.get_meshes() 4504 for ia in objs: 4505 try: 4506 ev = ia.properties.GetEdgeVisibility() 4507 ia.properties.SetEdgeVisibility(not ev) 4508 ia.properties.SetRepresentationToSurface() 4509 ia.properties.SetLineWidth(0.1) 4510 except AttributeError: 4511 pass 4512 4513 elif key == "k": # lightings 4514 if self.clicked_object in self.get_meshes(): 4515 objs = [self.clicked_object] 4516 else: 4517 objs = self.get_meshes() 4518 shds = ("default", "metallic", "plastic", "shiny", "glossy", "off") 4519 for ia in objs: 4520 try: 4521 lnr = (ia._ligthingnr + 1) % 6 4522 ia.lighting(shds[lnr]) 4523 ia._ligthingnr = lnr 4524 except AttributeError: 4525 pass 4526 4527 elif key == "K": # shading 4528 if self.clicked_object in self.get_meshes(): 4529 objs = [self.clicked_object] 4530 else: 4531 objs = self.get_meshes() 4532 for ia in objs: 4533 if isinstance(ia, vedo.Mesh): 4534 ia.compute_normals(cells=False) 4535 intrp = ia.properties.GetInterpolation() 4536 if intrp > 0: 4537 ia.properties.SetInterpolation(0) # flat 4538 else: 4539 ia.properties.SetInterpolation(2) # phong 4540 4541 elif key == "n": # show normals to an actor 4542 self.remove("added_auto_normals") 4543 if self.clicked_object in self.get_meshes(): 4544 if self.clicked_actor.GetPickable(): 4545 norml = vedo.shapes.NormalLines(self.clicked_object) 4546 norml.name = "added_auto_normals" 4547 self.add(norml) 4548 4549 elif key == "x": 4550 if self.justremoved is None: 4551 if self.clicked_object in self.get_meshes() or isinstance( 4552 self.clicked_object, vtki.vtkAssembly 4553 ): 4554 self.justremoved = self.clicked_actor 4555 self.renderer.RemoveActor(self.clicked_actor) 4556 else: 4557 self.renderer.AddActor(self.justremoved) 4558 self.justremoved = None 4559 4560 elif key == "X": 4561 if self.clicked_object: 4562 if not self.cutter_widget: 4563 self.cutter_widget = addons.BoxCutter(self.clicked_object) 4564 self.add(self.cutter_widget) 4565 vedo.printc("Press i to toggle the cutter on/off", c='g', dim=1) 4566 vedo.printc(" u to flip selection", c='g', dim=1) 4567 vedo.printc(" r to reset cutting planes", c='g', dim=1) 4568 vedo.printc(" Shift+X to close the cutter box widget", c='g', dim=1) 4569 vedo.printc(" Ctrl+S to save the cut section to file.", c='g', dim=1) 4570 else: 4571 self.remove(self.cutter_widget) 4572 self.cutter_widget = None 4573 vedo.printc("Click object and press X to open the cutter box widget.", c='g') 4574 4575 elif key == "E": 4576 vedo.printc(r":camera: Exporting 3D window to file scene.npz", c="b", end="") 4577 vedo.file_io.export_window("scene.npz") 4578 vedo.printc(", try:\n> vedo scene.npz # (this is experimental!)", c="b") 4579 return 4580 4581 elif key == "F": 4582 vedo.file_io.export_window("scene.x3d") 4583 vedo.printc(r":camera: Exporting 3D window to file", c="b", end="") 4584 vedo.file_io.export_window("scene.npz") 4585 vedo.printc(". Try:\n> firefox scene.html", c="b") 4586 4587 # elif key == "G": # not working with last version of k3d 4588 # vedo.file_io.export_window("scene.html") 4589 # vedo.printc(r":camera: Exporting K3D window to file", c="b", end="") 4590 # vedo.file_io.export_window("scene.html") 4591 # vedo.printc(". Try:\n> firefox scene.html", c="b") 4592 4593 elif key == "i": # print info 4594 if self.clicked_object: 4595 print(self.clicked_object) 4596 else: 4597 print(self) 4598 4599 elif key == "I": # print color under the mouse 4600 x, y = iren.GetEventPosition() 4601 self.color_picker([x, y], verbose=True) 4602 4603 elif key == "Y": 4604 if self.clicked_object and self.clicked_object.pipeline: 4605 self.clicked_object.pipeline.show() 4606 4607 if iren: 4608 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[mode]) 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  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  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 coordinates 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  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 coordinates forming the spline nodes.
- pc : (str) point color. The default is 'k'.
- ps : (str) point size. The default is 8.
- lc : (str) line color. The default is 'r4'.
- ac : (str) active point marker color. The default is 'g5'.
- lw : (int) line width. The default is 2.
- alpha : (float) line transparency.
- closed : (bool) spline is meant to be closed. The default is False.
Returns:
a
SplineTool
object.
Examples:
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  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  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  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  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  2830 """ 2831 if action in ("create", "start"): 2832 2833 if "Windows" in vedo.sys_platform: 2834 # otherwise on windows it gets stuck 2835 self.initialize_interactor() 2836 2837 if timer_id is not None: 2838 vedo.logger.warning("you set a timer_id but it will be ignored.") 2839 if one_shot: 2840 timer_id = self.interactor.CreateOneShotTimer(dt) 2841 else: 2842 timer_id = self.interactor.CreateRepeatingTimer(dt) 2843 return timer_id 2844 2845 elif action in ("destroy", "stop"): 2846 if timer_id is not None: 2847 self.interactor.DestroyTimer(timer_id) 2848 else: 2849 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2850 else: 2851 e = f"in timer_callback(). Cannot understand action: {action}\n" 2852 e += " allowed actions are: ['start', 'stop']. Skipped." 2853 vedo.logger.error(e) 2854 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:
2856 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2857 """ 2858 Add a callback function that will be called when an event occurs. 2859 Consider using `add_callback()` instead. 2860 """ 2861 if not self.interactor: 2862 return -1 2863 event_name = utils.get_vtk_name_event(event_name) 2864 idd = self.interactor.AddObserver(event_name, func, priority) 2865 return idd
Add a callback function that will be called when an event occurs.
Consider using add_callback()
instead.
2867 def compute_world_coordinate( 2868 self, 2869 pos2d: MutableSequence[float], 2870 at=None, 2871 objs=(), 2872 bounds=(), 2873 offset=None, 2874 pixeltol=None, 2875 worldtol=None, 2876 ) -> np.ndarray: 2877 """ 2878 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2879 If a set of meshes is passed then points are placed onto these. 2880 2881 Arguments: 2882 pos2d : (list) 2883 2D screen coordinates point. 2884 at : (int) 2885 renderer number. 2886 objs : (list) 2887 list of Mesh objects to project the point onto. 2888 bounds : (list) 2889 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2890 offset : (float) 2891 specify an offset value. 2892 pixeltol : (int) 2893 screen tolerance in pixels. 2894 worldtol : (float) 2895 world coordinates tolerance. 2896 2897 Returns: 2898 numpy array, the point in 3D world coordinates. 2899 2900 Examples: 2901 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2902 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2903 2904  2905 """ 2906 if at is not None: 2907 renderer = self.renderers[at] 2908 else: 2909 renderer = self.renderer 2910 2911 if not objs: 2912 pp = vtki.vtkFocalPlanePointPlacer() 2913 else: 2914 pps = vtki.vtkPolygonalSurfacePointPlacer() 2915 for ob in objs: 2916 pps.AddProp(ob.actor) 2917 pp = pps # type: ignore 2918 2919 if len(bounds) == 6: 2920 pp.SetPointBounds(bounds) 2921 if pixeltol: 2922 pp.SetPixelTolerance(pixeltol) 2923 if worldtol: 2924 pp.SetWorldTolerance(worldtol) 2925 if offset: 2926 pp.SetOffset(offset) 2927 2928 worldPos: MutableSequence[float] = [0, 0, 0] 2929 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2930 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2931 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2932 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2933 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:
2935 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2936 """ 2937 Given a 3D points in the current renderer (or full window), 2938 find the screen pixel coordinates. 2939 2940 Example: 2941 ```python 2942 from vedo import * 2943 2944 elli = Ellipsoid().point_size(5) 2945 2946 plt = Plotter() 2947 plt.show(elli, "Press q to continue and print the info") 2948 2949 xyscreen = plt.compute_screen_coordinates(elli) 2950 print('xyscreen coords:', xyscreen) 2951 2952 # simulate an event happening at one point 2953 event = plt.fill_event(pos=xyscreen[123]) 2954 print(event) 2955 ``` 2956 """ 2957 try: 2958 obj = obj.coordinates 2959 except AttributeError: 2960 pass 2961 2962 if utils.is_sequence(obj): 2963 pts = obj 2964 p2d = [] 2965 cs = vtki.vtkCoordinate() 2966 cs.SetCoordinateSystemToWorld() 2967 cs.SetViewport(self.renderer) 2968 for p in pts: 2969 cs.SetValue(p) 2970 if full_window: 2971 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2972 else: 2973 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2974 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)
2976 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2977 """ 2978 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2979 2980 Returns a frustum Mesh that contains the visible field of view. 2981 This can be used to select objects in a scene or select vertices. 2982 2983 Example: 2984 ```python 2985 from vedo import * 2986 2987 settings.enable_default_mouse_callbacks = False 2988 2989 def mode_select(objs): 2990 print("Selected objects:", objs) 2991 d0 = mode.start_x, mode.start_y # display coords 2992 d1 = mode.end_x, mode.end_y 2993 2994 frustum = plt.pick_area(d0, d1) 2995 col = np.random.randint(0, 10) 2996 infru = frustum.inside_points(mesh) 2997 infru.point_size(10).color(col) 2998 plt.add(frustum, infru).render() 2999 3000 mesh = Mesh(dataurl+"cow.vtk") 3001 mesh.color("k5").linewidth(1) 3002 3003 mode = interactor_modes.BlenderStyle() 3004 mode.callback_select = mode_select 3005 3006 plt = Plotter().user_mode(mode) 3007 plt.show(mesh, axes=1) 3008 ``` 3009 """ 3010 if at is not None: 3011 ren = self.renderers[at] 3012 else: 3013 ren = self.renderer 3014 area_picker = vtki.vtkAreaPicker() 3015 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 3016 planes = area_picker.GetFrustum() 3017 3018 fru = vtki.new("FrustumSource") 3019 fru.SetPlanes(planes) 3020 fru.ShowLinesOff() 3021 fru.Update() 3022 3023 afru = vedo.Mesh(fru.GetOutput()) 3024 afru.alpha(0.1).lw(1).pickable(False) 3025 afru.name = "Frustum" 3026 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)
3149 def show( 3150 self, 3151 *objects, 3152 at=None, 3153 axes=None, 3154 resetcam=None, 3155 zoom=False, 3156 interactive=None, 3157 viewup="", 3158 azimuth=0.0, 3159 elevation=0.0, 3160 roll=0.0, 3161 camera=None, 3162 mode=None, 3163 rate=None, 3164 bg=None, 3165 bg2=None, 3166 size=None, 3167 title=None, 3168 screenshot="", 3169 ) -> Any: 3170 """ 3171 Render a list of objects. 3172 3173 Arguments: 3174 at : (int) 3175 number of the renderer to plot to, in case of more than one exists 3176 3177 axes : (int) 3178 axis type-1 can be fully customized by passing a dictionary. 3179 Check `addons.Axes()` for the full list of options. 3180 set the type of axes to be shown: 3181 - 0, no axes 3182 - 1, draw three gray grid walls 3183 - 2, show cartesian axes from (0,0,0) 3184 - 3, show positive range of cartesian axes from (0,0,0) 3185 - 4, show a triad at bottom left 3186 - 5, show a cube at bottom left 3187 - 6, mark the corners of the bounding box 3188 - 7, draw a 3D ruler at each side of the cartesian axes 3189 - 8, show the `vtkCubeAxesActor` object 3190 - 9, show the bounding box outLine 3191 - 10, show three circles representing the maximum bounding box 3192 - 11, show a large grid on the x-y plane 3193 - 12, show polar axes 3194 - 13, draw a simple ruler at the bottom of the window 3195 3196 azimuth/elevation/roll : (float) 3197 move camera accordingly the specified value 3198 3199 viewup: str, list 3200 either `['x', 'y', 'z']` or a vector to set vertical direction 3201 3202 resetcam : (bool) 3203 re-adjust camera position to fit objects 3204 3205 camera : (dict, vtkCamera) 3206 camera parameters can further be specified with a dictionary 3207 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3208 - pos, `(list)`, the position of the camera in world coordinates 3209 - focal_point `(list)`, the focal point of the camera in world coordinates 3210 - viewup `(list)`, the view up direction for the camera 3211 - distance `(float)`, set the focal point to the specified distance from the camera position. 3212 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3213 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3214 in world-coordinate distances. The default is 1. 3215 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3216 This method has no effect in perspective projection mode. 3217 3218 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3219 plane to be set a distance 'thickness' beyond the near clipping plane. 3220 3221 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3222 measured in degrees. The default angle is 30 degrees. 3223 This method has no effect in parallel projection mode. 3224 The formula for setting the angle up for perfect perspective viewing is: 3225 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3226 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3227 3228 interactive : (bool) 3229 pause and interact with window (True) or continue execution (False) 3230 3231 rate : (float) 3232 maximum rate of `show()` in Hertz 3233 3234 mode : (int, str) 3235 set the type of interaction: 3236 - 0 = TrackballCamera [default] 3237 - 1 = TrackballActor 3238 - 2 = JoystickCamera 3239 - 3 = JoystickActor 3240 - 4 = Flight 3241 - 5 = RubberBand2D 3242 - 6 = RubberBand3D 3243 - 7 = RubberBandZoom 3244 - 8 = Terrain 3245 - 9 = Unicam 3246 - 10 = Image 3247 - Check out `vedo.interaction_modes` for more options. 3248 3249 bg : (str, list) 3250 background color in RGB format, or string name 3251 3252 bg2 : (str, list) 3253 second background color to create a gradient background 3254 3255 size : (str, list) 3256 size of the window, e.g. size="fullscreen", or size=[600,400] 3257 3258 title : (str) 3259 window title text 3260 3261 screenshot : (str) 3262 save a screenshot of the window to file 3263 """ 3264 3265 if vedo.settings.dry_run_mode >= 2: 3266 return self 3267 3268 if self.wx_widget: 3269 return self 3270 3271 if self.renderers: # in case of notebooks 3272 3273 if at is None: 3274 at = self.renderers.index(self.renderer) 3275 3276 else: 3277 3278 if at >= len(self.renderers): 3279 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3280 vedo.logger.error(t) 3281 return self 3282 3283 self.renderer = self.renderers[at] 3284 3285 if title is not None: 3286 self.title = title 3287 3288 if size is not None: 3289 self.size = size 3290 if self.size[0] == "f": # full screen 3291 self.size = "fullscreen" 3292 self.window.SetFullScreen(True) 3293 self.window.BordersOn() 3294 else: 3295 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3296 3297 if vedo.settings.default_backend == "vtk": 3298 if str(bg).endswith(".hdr"): 3299 self._add_skybox(bg) 3300 else: 3301 if bg is not None: 3302 self.backgrcol = vedo.get_color(bg) 3303 self.renderer.SetBackground(self.backgrcol) 3304 if bg2 is not None: 3305 self.renderer.GradientBackgroundOn() 3306 self.renderer.SetBackground2(vedo.get_color(bg2)) 3307 3308 if axes is not None: 3309 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3310 objects = list(objects) 3311 objects.append(axes) # move it into the list of normal things to show 3312 axes = 0 3313 self.axes = axes 3314 3315 if interactive is not None: 3316 self._interactive = interactive 3317 if self.offscreen: 3318 self._interactive = False 3319 3320 # camera stuff 3321 if resetcam is not None: 3322 self.resetcam = resetcam 3323 3324 if camera is not None: 3325 self.resetcam = False 3326 viewup = "" 3327 if isinstance(camera, vtki.vtkCamera): 3328 cameracopy = vtki.vtkCamera() 3329 cameracopy.DeepCopy(camera) 3330 self.camera = cameracopy 3331 else: 3332 self.camera = utils.camera_from_dict(camera) 3333 3334 self.add(objects) 3335 3336 # Backend ############################################################### 3337 if vedo.settings.default_backend in ["k3d", "panel"]: 3338 return backends.get_notebook_backend(self.objects) 3339 ######################################################################### 3340 3341 for ia in utils.flatten(objects): 3342 try: 3343 # fix gray color labels and title to white or black 3344 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3345 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3346 c = (0.9, 0.9, 0.9) 3347 if np.sum(self.renderer.GetBackground()) > 1.5: 3348 c = (0.1, 0.1, 0.1) 3349 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3350 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3351 except AttributeError: 3352 pass 3353 3354 if self.sharecam: 3355 for r in self.renderers: 3356 r.SetActiveCamera(self.camera) 3357 3358 if self.axes is not None: 3359 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3360 bns = self.renderer.ComputeVisiblePropBounds() 3361 addons.add_global_axes(self.axes, bounds=bns) 3362 3363 # Backend ############################################################### 3364 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3365 return backends.get_notebook_backend() 3366 ######################################################################### 3367 3368 if self.resetcam: 3369 self.renderer.ResetCamera() 3370 3371 if len(self.renderers) > 1: 3372 self.add_renderer_frame() 3373 3374 if vedo.settings.default_backend == "2d" and not zoom: 3375 zoom = "tightest" 3376 3377 if zoom: 3378 if zoom == "tight": 3379 self.reset_camera(tight=0.04) 3380 elif zoom == "tightest": 3381 self.reset_camera(tight=0.0001) 3382 else: 3383 self.camera.Zoom(zoom) 3384 if elevation: 3385 self.camera.Elevation(elevation) 3386 if azimuth: 3387 self.camera.Azimuth(azimuth) 3388 if roll: 3389 self.camera.Roll(roll) 3390 3391 if len(viewup) > 0: 3392 b = self.renderer.ComputeVisiblePropBounds() 3393 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3394 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3395 if viewup == "x": 3396 sz = np.linalg.norm(sz) 3397 self.camera.SetViewUp([1, 0, 0]) 3398 self.camera.SetPosition(cm + sz) 3399 elif viewup == "y": 3400 sz = np.linalg.norm(sz) 3401 self.camera.SetViewUp([0, 1, 0]) 3402 self.camera.SetPosition(cm + sz) 3403 elif viewup == "z": 3404 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3405 self.camera.SetViewUp([0, 0, 1]) 3406 self.camera.SetPosition(cm + 2 * sz) 3407 elif utils.is_sequence(viewup): 3408 sz = np.linalg.norm(sz) 3409 self.camera.SetViewUp(viewup) 3410 cpos = np.cross([0, 1, 0], viewup) 3411 self.camera.SetPosition(cm - 2 * sz * cpos) 3412 3413 self.renderer.ResetCameraClippingRange() 3414 3415 self.initialize_interactor() 3416 3417 if vedo.settings.immediate_rendering: 3418 self.window.Render() ##################### <-------------- Render 3419 3420 if self.interactor: # can be offscreen or not the vtk backend.. 3421 3422 self.window.SetWindowName(self.title) 3423 3424 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3425 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3426 # print(pic.dataset)# Array 0 name PNGImage 3427 # self.window.SetIcon(pic.dataset) 3428 3429 try: 3430 # Needs "pip install pyobjc" on Mac OSX 3431 if ( 3432 self._cocoa_initialized is False 3433 and "Darwin" in vedo.sys_platform 3434 and not self.offscreen 3435 ): 3436 self._cocoa_initialized = True 3437 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3438 pid = os.getpid() 3439 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3440 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3441 except: 3442 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3443 pass 3444 3445 # Set the interaction style 3446 if mode is not None: 3447 self.user_mode(mode) 3448 if self.qt_widget and mode is None: 3449 self.user_mode(0) 3450 3451 if screenshot: 3452 self.screenshot(screenshot) 3453 3454 if self._interactive: 3455 self.interactor.Start() 3456 if self._must_close_now: 3457 self.interactor.GetRenderWindow().Finalize() 3458 self.interactor.TerminateApp() 3459 self.camera = None 3460 self.renderer = None 3461 self.renderers = [] 3462 self.window = None 3463 self.interactor = None 3464 return self 3465 3466 if rate: 3467 if self.clock is None: # set clock and limit rate 3468 self._clockt0 = time.time() 3469 self.clock = 0.0 3470 else: 3471 t = time.time() - self._clockt0 3472 elapsed = t - self.clock 3473 mint = 1.0 / rate 3474 if elapsed < mint: 3475 time.sleep(mint - elapsed) 3476 self.clock = time.time() - self._clockt0 3477 3478 # 2d #################################################################### 3479 if vedo.settings.default_backend in ["2d"]: 3480 return backends.get_notebook_backend() 3481 ######################################################################### 3482 3483 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
3486 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3487 """Add a draggable inset space into a renderer. 3488 3489 Arguments: 3490 at : (int) 3491 specify the renderer number 3492 pos : (list) 3493 icon position in the range [1-4] indicating one of the 4 corners, 3494 or it can be a tuple (x,y) as a fraction of the renderer size. 3495 size : (float) 3496 size of the square inset 3497 draggable : (bool) 3498 if True the subrenderer space can be dragged around 3499 c : (color) 3500 color of the inset frame when dragged 3501 3502 Examples: 3503 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3504 3505  3506 """ 3507 if not self.interactor: 3508 return None 3509 3510 if not self.renderer: 3511 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3512 return None 3513 3514 options = dict(options) 3515 pos = options.pop("pos", 0) 3516 size = options.pop("size", 0.1) 3517 c = options.pop("c", "lb") 3518 at = options.pop("at", None) 3519 draggable = options.pop("draggable", True) 3520 3521 r, g, b = vedo.get_color(c) 3522 widget = vtki.vtkOrientationMarkerWidget() 3523 widget.SetOutlineColor(r, g, b) 3524 if len(objects) == 1: 3525 widget.SetOrientationMarker(objects[0].actor) 3526 else: 3527 widget.SetOrientationMarker(vedo.Assembly(objects)) 3528 3529 widget.SetInteractor(self.interactor) 3530 3531 if utils.is_sequence(pos): 3532 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3533 else: 3534 if pos < 2: 3535 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3536 elif pos == 2: 3537 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3538 elif pos == 3: 3539 widget.SetViewport(0, 0, size * 2, size * 2) 3540 elif pos == 4: 3541 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3542 widget.EnabledOn() 3543 widget.SetInteractive(draggable) 3544 if at is not None and at < len(self.renderers): 3545 widget.SetCurrentRenderer(self.renderers[at]) 3546 else: 3547 widget.SetCurrentRenderer(self.renderer) 3548 self.widgets.append(widget) 3549 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:
3551 def clear(self, at=None, deep=False) -> Self: 3552 """Clear the scene from all meshes and volumes.""" 3553 if at is not None: 3554 renderer = self.renderers[at] 3555 else: 3556 renderer = self.renderer 3557 if not renderer: 3558 return self 3559 3560 if deep: 3561 renderer.RemoveAllViewProps() 3562 else: 3563 for ob in set( 3564 self.get_meshes() 3565 + self.get_volumes() 3566 + self.objects 3567 + self.axes_instances 3568 ): 3569 if isinstance(ob, vedo.shapes.Text2D): 3570 continue 3571 self.remove(ob) 3572 try: 3573 if ob.scalarbar: 3574 self.remove(ob.scalarbar) 3575 except AttributeError: 3576 pass 3577 return self
Clear the scene from all meshes and volumes.
3579 def break_interaction(self) -> Self: 3580 """Break window interaction and return to the python execution flow""" 3581 if self.interactor: 3582 self.check_actors_trasform() 3583 self.interactor.ExitCallback() 3584 return self
Break window interaction and return to the python execution flow
3586 def freeze(self, value=True) -> Self: 3587 """Freeze the current renderer. Use this with `sharecam=False`.""" 3588 if not self.interactor: 3589 return self 3590 if not self.renderer: 3591 return self 3592 self.renderer.SetInteractive(not value) 3593 return self
Freeze the current renderer. Use this with sharecam=False
.
3595 def user_mode(self, mode) -> Self: 3596 """ 3597 Modify the user interaction mode. 3598 3599 Examples: 3600 ```python 3601 from vedo import * 3602 mode = interactor_modes.MousePan() 3603 mesh = Mesh(dataurl+"cow.vtk") 3604 plt = Plotter().user_mode(mode) 3605 plt.show(mesh, axes=1) 3606 ``` 3607 See also: 3608 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3609 """ 3610 if not self.interactor: 3611 return self 3612 3613 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3614 # print("Current style:", curr_style) 3615 if curr_style.endswith("Actor"): 3616 self.check_actors_trasform() 3617 3618 if isinstance(mode, (str, int)): 3619 # Set the style of interaction 3620 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3621 if mode in (0, "TrackballCamera"): 3622 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3623 self.interactor.RemoveObservers("CharEvent") 3624 elif mode in (1, "TrackballActor"): 3625 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3626 elif mode in (2, "JoystickCamera"): 3627 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3628 elif mode in (3, "JoystickActor"): 3629 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3630 elif mode in (4, "Flight"): 3631 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3632 elif mode in (5, "RubberBand2D"): 3633 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3634 elif mode in (6, "RubberBand3D"): 3635 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3636 elif mode in (7, "RubberBandZoom"): 3637 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3638 elif mode in (8, "Terrain"): 3639 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3640 elif mode in (9, "Unicam"): 3641 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3642 elif mode in (10, "Image", "image", "2d"): 3643 astyle = vtki.new("InteractorStyleImage") 3644 astyle.SetInteractionModeToImage3D() 3645 self.interactor.SetInteractorStyle(astyle) 3646 else: 3647 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3648 3649 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3650 # set a custom interactor style 3651 if hasattr(mode, "interactor"): 3652 mode.interactor = self.interactor 3653 mode.renderer = self.renderer # type: ignore 3654 mode.SetInteractor(self.interactor) 3655 mode.SetDefaultRenderer(self.renderer) 3656 self.interactor.SetInteractorStyle(mode) 3657 3658 return self
Modify the user interaction mode.
Examples:
from vedo import * mode = interactor_modes.MousePan() mesh = Mesh(dataurl+"cow.vtk") plt = Plotter().user_mode(mode) plt.show(mesh, axes=1)
See also: VTK interactor styles
3660 def close(self) -> Self: 3661 """Close the plotter.""" 3662 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3663 vedo.last_figure = None 3664 self.last_event = None 3665 self.sliders = [] 3666 self.buttons = [] 3667 self.widgets = [] 3668 self.hover_legends = [] 3669 self.background_renderer = None 3670 self._extralight = None 3671 3672 self.hint_widget = None 3673 self.cutter_widget = None 3674 3675 if vedo.settings.dry_run_mode >= 2: 3676 return self 3677 3678 if not hasattr(self, "window"): 3679 return self 3680 if not self.window: 3681 return self 3682 if not hasattr(self, "interactor"): 3683 return self 3684 if not self.interactor: 3685 return self 3686 3687 ################################################### 3688 try: 3689 if "Darwin" in vedo.sys_platform: 3690 self.interactor.ProcessEvents() 3691 except: 3692 pass 3693 3694 self._must_close_now = True 3695 3696 if vedo.plotter_instance == self: 3697 vedo.plotter_instance = None 3698 3699 if self.interactor and self._interactive: 3700 self.break_interaction() 3701 elif self._must_close_now: 3702 # dont call ExitCallback here 3703 if self.interactor: 3704 self.break_interaction() 3705 self.interactor.GetRenderWindow().Finalize() 3706 self.interactor.TerminateApp() 3707 self.camera = None 3708 self.renderer = None 3709 self.renderers = [] 3710 self.window = None 3711 self.interactor = None 3712 return self
Close the plotter.
3714 @property 3715 def camera(self): 3716 """Return the current active camera.""" 3717 if self.renderer: 3718 return self.renderer.GetActiveCamera()
Return the current active camera.
3727 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3728 """ 3729 Take a screenshot of the Plotter window. 3730 3731 Arguments: 3732 scale : (int) 3733 set image magnification as an integer multiplicating factor 3734 asarray : (bool) 3735 return a numpy array of the image instead of writing a file 3736 3737 Warning: 3738 If you get black screenshots try to set `interactive=False` in `show()` 3739 then call `screenshot()` and `plt.interactive()` afterwards. 3740 3741 Example: 3742 ```py 3743 from vedo import * 3744 sphere = Sphere().linewidth(1) 3745 plt = show(sphere, interactive=False) 3746 plt.screenshot('image.png') 3747 plt.interactive() 3748 plt.close() 3749 ``` 3750 3751 Example: 3752 ```py 3753 from vedo import * 3754 sphere = Sphere().linewidth(1) 3755 plt = show(sphere, interactive=False) 3756 plt.screenshot('anotherimage.png') 3757 plt.interactive() 3758 plt.close() 3759 ``` 3760 """ 3761 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()
3763 def toimage(self, scale=1) -> "vedo.image.Image": 3764 """ 3765 Generate a `Image` object from the current rendering window. 3766 3767 Arguments: 3768 scale : (int) 3769 set image magnification as an integer multiplicating factor 3770 """ 3771 if vedo.settings.screeshot_large_image: 3772 w2if = vtki.new("RenderLargeImage") 3773 w2if.SetInput(self.renderer) 3774 w2if.SetMagnification(scale) 3775 else: 3776 w2if = vtki.new("WindowToImageFilter") 3777 w2if.SetInput(self.window) 3778 if hasattr(w2if, "SetScale"): 3779 w2if.SetScale(scale, scale) 3780 if vedo.settings.screenshot_transparent_background: 3781 w2if.SetInputBufferTypeToRGBA() 3782 w2if.ReadFrontBufferOff() # read from the back buffer 3783 w2if.Update() 3784 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
3786 def export(self, filename="scene.npz", binary=False) -> Self: 3787 """ 3788 Export scene to file to HTML, X3D or Numpy file. 3789 3790 Examples: 3791 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3792 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3793 """ 3794 vedo.file_io.export_window(filename, binary=binary) 3795 return self
3797 def color_picker(self, xy, verbose=False): 3798 """Pick color of specific (x,y) pixel on the screen.""" 3799 w2if = vtki.new("WindowToImageFilter") 3800 w2if.SetInput(self.window) 3801 w2if.ReadFrontBufferOff() 3802 w2if.Update() 3803 nx, ny = self.window.GetSize() 3804 varr = w2if.GetOutput().GetPointData().GetScalars() 3805 3806 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3807 x, y = int(xy[0]), int(xy[1]) 3808 if y < ny and x < nx: 3809 3810 rgb = arr[y, x] 3811 3812 if verbose: 3813 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3814 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3815 vedo.printc("█", c=[0, rgb[1], 0], end="") 3816 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3817 vedo.printc("] = ", end="") 3818 cnm = vedo.get_color_name(rgb) 3819 if np.sum(rgb) < 150: 3820 vedo.printc( 3821 rgb.tolist(), 3822 vedo.colors.rgb2hex(np.array(rgb) / 255), 3823 c="w", 3824 bc=rgb, 3825 invert=1, 3826 end="", 3827 ) 3828 vedo.printc(" -> " + cnm, invert=1, c="w") 3829 else: 3830 vedo.printc( 3831 rgb.tolist(), 3832 vedo.colors.rgb2hex(np.array(rgb) / 255), 3833 c=rgb, 3834 end="", 3835 ) 3836 vedo.printc(" -> " + cnm, c=cnm) 3837 3838 return rgb 3839 3840 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.