vedo.plotter
This module defines the main class Plotter to manage objects and 3D rendering.
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3import os.path 4import sys 5import time 6from typing import MutableSequence, Callable, Any, Union 7from typing_extensions import Self 8import numpy as np 9 10import vedo.vtkclasses as vtki # a wrapper for lazy imports 11 12import vedo 13from vedo import transformations 14from vedo import utils 15from vedo import backends 16from vedo import addons 17 18 19__docformat__ = "google" 20 21__doc__ = """ 22This module defines the main class Plotter to manage objects and 3D rendering. 23 24![](https://vedo.embl.es/images/basic/multirenderers.png) 25""" 26 27__all__ = ["Plotter", "show", "close"] 28 29######################################################################################## 30class Event: 31 """ 32 This class holds the info from an event in the window, works as dictionary too. 33 """ 34 35 __slots__ = [ 36 "name", 37 "title", 38 "id", 39 "timerid", 40 "time", 41 "priority", 42 "at", 43 "object", 44 "actor", 45 "picked3d", 46 "keypress", 47 "picked2d", 48 "delta2d", 49 "angle2d", 50 "speed2d", 51 "delta3d", 52 "speed3d", 53 "isPoints", 54 "isMesh", 55 "isAssembly", 56 "isVolume", 57 "isImage", 58 "isActor2D", 59 ] 60 61 def __init__(self): 62 self.name = "event" 63 self.title = "" 64 self.id = 0 65 self.timerid = 0 66 self.time = 0 67 self.priority = 0 68 self.at = 0 69 self.object = None 70 self.actor = None 71 self.picked3d = () 72 self.keypress = "" 73 self.picked2d = () 74 self.delta2d = () 75 self.angle2d = 0 76 self.speed2d = () 77 self.delta3d = () 78 self.speed3d = 0 79 self.isPoints = False 80 self.isMesh = False 81 self.isAssembly = False 82 self.isVolume = False 83 self.isImage = False 84 self.isActor2D = False 85 86 def __getitem__(self, key): 87 return getattr(self, key) 88 89 def __setitem__(self, key, value): 90 setattr(self, key, value) 91 92 def __str__(self): 93 module = self.__class__.__module__ 94 name = self.__class__.__name__ 95 out = vedo.printc( 96 f"{module}.{name} at ({hex(id(self))})".ljust(75), 97 bold=True, invert=True, return_string=True, 98 ) 99 out += "\x1b[0m" 100 for n in self.__slots__: 101 if n == "actor": 102 continue 103 out += f"{n}".ljust(11) + ": " 104 val = str(self[n]).replace("\n", "")[:65].rstrip() 105 if val == "True": 106 out += "\x1b[32;1m" 107 elif val == "False": 108 out += "\x1b[31;1m" 109 out += val + "\x1b[0m\n" 110 return out.rstrip() 111 112 def keys(self): 113 return self.__slots__ 114 115 116############################################################################################## 117def show( 118 *objects, 119 at=None, 120 shape=(1, 1), 121 N=None, 122 pos=(0, 0), 123 size="auto", 124 screensize="auto", 125 title="vedo", 126 bg="white", 127 bg2=None, 128 axes=None, 129 interactive=None, 130 offscreen=False, 131 sharecam=True, 132 resetcam=True, 133 zoom=None, 134 viewup="", 135 azimuth=0.0, 136 elevation=0.0, 137 roll=0.0, 138 camera=None, 139 mode=None, 140 screenshot="", 141 new=False, 142) -> Union[Self, None]: 143 """ 144 Create on the fly an instance of class Plotter and show the object(s) provided. 145 146 Arguments: 147 at : (int) 148 number of the renderer to plot to, in case of more than one exists 149 shape : (list, str) 150 Number of sub-render windows inside of the main window. E.g.: 151 specify two across with shape=(2,1) and a two by two grid 152 with shape=(2, 2). By default there is only one renderer. 153 154 Can also accept a shape as string descriptor. E.g.: 155 - shape="3|1" means 3 plots on the left and 1 on the right, 156 - shape="4/2" means 4 plots on top of 2 at bottom. 157 N : (int) 158 number of desired sub-render windows arranged automatically in a grid 159 pos : (list) 160 position coordinates of the top-left corner of the rendering window 161 on the screen 162 size : (list) 163 size of the rendering window 164 screensize : (list) 165 physical size of the monitor screen 166 title : (str) 167 window title 168 bg : (color) 169 background color or specify jpg image file name with path 170 bg2 : (color) 171 background color of a gradient towards the top 172 axes : (int) 173 set the type of axes to be shown: 174 - 0, no axes 175 - 1, draw three gray grid walls 176 - 2, show cartesian axes from (0,0,0) 177 - 3, show positive range of cartesian axes from (0,0,0) 178 - 4, show a triad at bottom left 179 - 5, show a cube at bottom left 180 - 6, mark the corners of the bounding box 181 - 7, draw a 3D ruler at each side of the cartesian axes 182 - 8, show the `vtkCubeAxesActor` object 183 - 9, show the bounding box outLine 184 - 10, show three circles representing the maximum bounding box 185 - 11, show a large grid on the x-y plane 186 - 12, show polar axes 187 - 13, draw a simple ruler at the bottom of the window 188 - 14: draw a `CameraOrientationWidget` 189 190 Axis type-1 can be fully customized by passing a dictionary. 191 Check `vedo.addons.Axes()` for the full list of options. 192 azimuth/elevation/roll : (float) 193 move camera accordingly the specified value 194 viewup : (str, list) 195 either `['x', 'y', 'z']` or a vector to set vertical direction 196 resetcam : (bool) 197 re-adjust camera position to fit objects 198 camera : (dict, vtkCamera) 199 camera parameters can further be specified with a dictionary 200 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 201 - **pos** (list), the position of the camera in world coordinates 202 - **focal_point** (list), the focal point of the camera in world coordinates 203 - **viewup** (list), the view up direction for the camera 204 - **distance** (float), set the focal point to the specified distance from the camera position. 205 - **clipping_range** (float), distance of the near and far clipping planes along the direction of projection. 206 - **parallel_scale** (float), 207 scaling used for a parallel projection, i.e. the height of the viewport 208 in world-coordinate distances. The default is 1. Note that the "scale" parameter works as 209 an "inverse scale", larger numbers produce smaller images. 210 This method has no effect in perspective projection mode. 211 - **thickness** (float), 212 set the distance between clipping planes. This method adjusts the far clipping 213 plane to be set a distance 'thickness' beyond the near clipping plane. 214 - **view_angle** (float), 215 the camera view angle, which is the angular height of the camera view 216 measured in degrees. The default angle is 30 degrees. 217 This method has no effect in parallel projection mode. 218 The formula for setting the angle up for perfect perspective viewing is: 219 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 220 (measured by holding a ruler up to your screen) and d is the distance 221 from your eyes to the screen. 222 interactive : (bool) 223 pause and interact with window (True) or continue execution (False) 224 rate : (float) 225 maximum rate of `show()` in Hertz 226 mode : (int, str) 227 set the type of interaction: 228 - 0 = TrackballCamera [default] 229 - 1 = TrackballActor 230 - 2 = JoystickCamera 231 - 3 = JoystickActor 232 - 4 = Flight 233 - 5 = RubberBand2D 234 - 6 = RubberBand3D 235 - 7 = RubberBandZoom 236 - 8 = Terrain 237 - 9 = Unicam 238 - 10 = Image 239 new : (bool) 240 if set to `True`, a call to show will instantiate 241 a new Plotter object (a new window) instead of reusing the first created. 242 If new is `True`, but the existing plotter was instantiated with a different 243 argument for `offscreen`, `new` is ignored and a new Plotter is created anyway. 244 """ 245 if len(objects) == 0: 246 objects = None 247 elif len(objects) == 1: 248 objects = objects[0] 249 else: 250 objects = utils.flatten(objects) 251 252 # If a plotter instance is already present, check if the offscreen argument 253 # is the same as the one requested by the user. If not, create a new 254 # plotter instance (see https://github.com/marcomusy/vedo/issues/1026) 255 if vedo.plotter_instance and vedo.plotter_instance.offscreen != offscreen: 256 new = True 257 258 if vedo.plotter_instance and not new: # Plotter exists 259 plt = vedo.plotter_instance 260 261 else: # Plotter must be created 262 263 if utils.is_sequence(at): # user passed a sequence for "at" 264 265 if not utils.is_sequence(objects): 266 vedo.logger.error("in show() input must be a list.") 267 raise RuntimeError() 268 if len(at) != len(objects): 269 vedo.logger.error("in show() lists 'input' and 'at' must have equal lengths") 270 raise RuntimeError() 271 if shape == (1, 1) and N is None: 272 N = max(at) + 1 273 274 elif at is None and (N or shape != (1, 1)): 275 276 if not utils.is_sequence(objects): 277 e = "in show(), N or shape is set, but input is not a sequence\n" 278 e += " you may need to specify e.g. at=0" 279 vedo.logger.error(e) 280 raise RuntimeError() 281 at = list(range(len(objects))) 282 283 plt = Plotter( 284 shape=shape, 285 N=N, 286 pos=pos, 287 size=size, 288 screensize=screensize, 289 title=title, 290 axes=axes, 291 sharecam=sharecam, 292 resetcam=resetcam, 293 interactive=interactive, 294 offscreen=offscreen, 295 bg=bg, 296 bg2=bg2, 297 ) 298 299 if vedo.settings.dry_run_mode >= 2: 300 return plt 301 302 # use _plt_to_return because plt.show() can return a k3d plot 303 _plt_to_return = None 304 305 if utils.is_sequence(at): 306 307 for i, act in enumerate(objects): 308 _plt_to_return = plt.show( 309 act, 310 at=i, 311 zoom=zoom, 312 resetcam=resetcam, 313 viewup=viewup, 314 azimuth=azimuth, 315 elevation=elevation, 316 roll=roll, 317 camera=camera, 318 interactive=False, 319 mode=mode, 320 screenshot=screenshot, 321 bg=bg, 322 bg2=bg2, 323 axes=axes, 324 ) 325 326 if ( 327 interactive 328 or len(at) == N 329 or (isinstance(shape[0], int) and len(at) == shape[0] * shape[1]) 330 ): 331 # note that shape can be a string 332 if plt.interactor and not offscreen and (interactive is None or interactive): 333 plt.interactor.Start() 334 if plt._must_close_now: 335 plt.interactor.GetRenderWindow().Finalize() 336 plt.interactor.TerminateApp() 337 plt.interactor = None 338 plt.window = None 339 plt.renderer = None 340 plt.renderers = [] 341 plt.camera = None 342 343 else: 344 345 _plt_to_return = plt.show( 346 objects, 347 at=at, 348 zoom=zoom, 349 resetcam=resetcam, 350 viewup=viewup, 351 azimuth=azimuth, 352 elevation=elevation, 353 roll=roll, 354 camera=camera, 355 interactive=interactive, 356 mode=mode, 357 screenshot=screenshot, 358 bg=bg, 359 bg2=bg2, 360 axes=axes, 361 ) 362 363 return _plt_to_return 364 365 366def close() -> None: 367 """Close the last created Plotter instance if it exists.""" 368 if not vedo.plotter_instance: 369 return 370 vedo.plotter_instance.close() 371 return 372 373 374######################################################################## 375class Plotter: 376 """Main class to manage objects.""" 377 378 def __init__( 379 self, 380 shape=(1, 1), 381 N=None, 382 pos=(0, 0), 383 size="auto", 384 screensize="auto", 385 title="vedo", 386 bg="white", 387 bg2=None, 388 axes=None, 389 sharecam=True, 390 resetcam=True, 391 interactive=None, 392 offscreen=False, 393 qt_widget=None, 394 wx_widget=None, 395 ): 396 """ 397 Arguments: 398 shape : (str, list) 399 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 400 N : (int) 401 number of desired renderers arranged in a grid automatically. 402 pos : (list) 403 (x,y) position in pixels of top-left corner of the rendering window on the screen 404 size : (str, list) 405 size of the rendering window. If 'auto', guess it based on screensize. 406 screensize : (list) 407 physical size of the monitor screen in pixels 408 bg : (color, str) 409 background color or specify jpg image file name with path 410 bg2 : (color) 411 background color of a gradient towards the top 412 title : (str) 413 window title 414 415 axes : (int) 416 417 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 418 Check out `vedo.addons.Axes()` for the available options. 419 420 - 0, no axes 421 - 1, draw three gray grid walls 422 - 2, show cartesian axes from (0,0,0) 423 - 3, show positive range of cartesian axes from (0,0,0) 424 - 4, show a triad at bottom left 425 - 5, show a cube at bottom left 426 - 6, mark the corners of the bounding box 427 - 7, draw a 3D ruler at each side of the cartesian axes 428 - 8, show the VTK CubeAxesActor object 429 - 9, show the bounding box outLine 430 - 10, show three circles representing the maximum bounding box 431 - 11, show a large grid on the x-y plane (use with zoom=8) 432 - 12, show polar axes 433 - 13, draw a simple ruler at the bottom of the window 434 - 14: draw a camera orientation widget 435 436 sharecam : (bool) 437 if False each renderer will have an independent camera 438 interactive : (bool) 439 if True will stop after show() to allow interaction with the 3d scene 440 offscreen : (bool) 441 if True will not show the rendering window 442 qt_widget : (QVTKRenderWindowInteractor) 443 render in a Qt-Widget using an QVTKRenderWindowInteractor. 444 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 445 """ 446 vedo.plotter_instance = self 447 448 if interactive is None: 449 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 450 self._interactive = interactive 451 # print("interactive", interactive, N, shape) 452 453 self.objects = [] # list of objects to be shown 454 self.clicked_object = None # holds the object that has been clicked 455 self.clicked_actor = None # holds the actor that has been clicked 456 457 self.shape = shape # nr. of subwindows in grid 458 self.axes = axes # show axes type nr. 459 self.title = title # window title 460 self.size = size # window size 461 self.backgrcol = bg # used also by backend notebooks 462 463 self.offscreen= offscreen 464 self.resetcam = resetcam 465 self.sharecam = sharecam # share the same camera if multiple renderers 466 self.pos = pos # used by vedo.file_io 467 468 self.picker = None # hold the vtkPicker object 469 self.picked2d = None # 2d coords of a clicked point on the rendering window 470 self.picked3d = None # 3d coords of a clicked point on an actor 471 472 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 473 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 474 self.interactor = None 475 self.window = None 476 self.renderer = None 477 self.renderers = [] # list of renderers 478 479 # mostly internal stuff: 480 self.hover_legends = [] 481 self.justremoved = None 482 self.axes_instances = [] 483 self.clock = 0 484 self.sliders = [] 485 self.buttons = [] 486 self.widgets = [] 487 self.cutter_widget = None 488 self.hint_widget = None 489 self.background_renderer = None 490 self.last_event = None 491 self.skybox = None 492 self._icol = 0 493 self._clockt0 = time.time() 494 self._extralight = None 495 self._cocoa_initialized = False 496 self._cocoa_process_events = True # make one call in show() 497 self._must_close_now = False 498 499 ##################################################################### 500 if vedo.settings.default_backend == "2d": 501 self.offscreen = True 502 if self.size == "auto": 503 self.size = (800, 600) 504 505 elif vedo.settings.default_backend == "k3d": 506 if self.size == "auto": 507 self.size = (1000, 1000) 508 #################################### 509 return ############################ 510 #################################### 511 512 ############################################################# 513 if vedo.settings.default_backend in ["vtk", "2d", "trame"]: 514 515 if screensize == "auto": 516 screensize = (2160, 1440) # TODO: get actual screen size 517 518 # build the rendering window: 519 self.window = vtki.vtkRenderWindow() 520 521 self.window.GlobalWarningDisplayOff() 522 523 if self.title == "vedo": # check if dev version 524 if "dev" in vedo.__version__: 525 self.title = f"vedo ({vedo.__version__})" 526 self.window.SetWindowName(self.title) 527 528 # more vedo.settings 529 if vedo.settings.use_depth_peeling: 530 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 531 self.window.SetMultiSamples(vedo.settings.multi_samples) 532 533 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 534 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 535 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 536 537 ############################################################# 538 if N: # N = number of renderers. Find out the best 539 540 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 541 vedo.logger.warning("having set N, shape is ignored.") 542 543 x, y = screensize 544 nx = int(np.sqrt(int(N * y / x) + 1)) 545 ny = int(np.sqrt(int(N * x / y) + 1)) 546 lm = [ 547 (nx, ny), 548 (nx, ny + 1), 549 (nx - 1, ny), 550 (nx + 1, ny), 551 (nx, ny - 1), 552 (nx - 1, ny + 1), 553 (nx + 1, ny - 1), 554 (nx + 1, ny + 1), 555 (nx - 1, ny - 1), 556 ] 557 ind, minl = 0, 1000 558 for i, m in enumerate(lm): 559 l = m[0] * m[1] 560 if N <= l < minl: 561 ind = i 562 minl = l 563 shape = lm[ind] 564 565 ################################################## 566 if isinstance(shape, str): 567 568 if "|" in shape: 569 if self.size == "auto": 570 self.size = (800, 1200) 571 n = int(shape.split("|")[0]) 572 m = int(shape.split("|")[1]) 573 rangen = reversed(range(n)) 574 rangem = reversed(range(m)) 575 else: 576 if self.size == "auto": 577 self.size = (1200, 800) 578 m = int(shape.split("/")[0]) 579 n = int(shape.split("/")[1]) 580 rangen = range(n) 581 rangem = range(m) 582 583 if n >= m: 584 xsplit = m / (n + m) 585 else: 586 xsplit = 1 - n / (n + m) 587 if vedo.settings.window_splitting_position: 588 xsplit = vedo.settings.window_splitting_position 589 590 for i in rangen: 591 arenderer = vtki.vtkRenderer() 592 if "|" in shape: 593 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 594 else: 595 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 596 self.renderers.append(arenderer) 597 598 for i in rangem: 599 arenderer = vtki.vtkRenderer() 600 601 if "|" in shape: 602 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 603 else: 604 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 605 self.renderers.append(arenderer) 606 607 for r in self.renderers: 608 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 609 610 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 611 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 612 if vedo.settings.use_depth_peeling: 613 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 614 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 615 r.SetUseFXAA(vedo.settings.use_fxaa) 616 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 617 618 r.SetBackground(vedo.get_color(self.backgrcol)) 619 620 self.axes_instances.append(None) 621 622 self.shape = (n + m,) 623 624 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 625 # passing a sequence of dicts for renderers specifications 626 627 if self.size == "auto": 628 self.size = (1000, 800) 629 630 for rd in shape: 631 x0, y0 = rd["bottomleft"] 632 x1, y1 = rd["topright"] 633 bg_ = rd.pop("bg", "white") 634 bg2_ = rd.pop("bg2", None) 635 636 arenderer = vtki.vtkRenderer() 637 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 638 639 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 640 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 641 if vedo.settings.use_depth_peeling: 642 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 643 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 644 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 645 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 646 647 arenderer.SetViewport(x0, y0, x1, y1) 648 arenderer.SetBackground(vedo.get_color(bg_)) 649 if bg2_: 650 arenderer.GradientBackgroundOn() 651 arenderer.SetBackground2(vedo.get_color(bg2_)) 652 653 self.renderers.append(arenderer) 654 self.axes_instances.append(None) 655 656 self.shape = (len(shape),) 657 658 else: 659 660 if isinstance(self.size, str) and self.size == "auto": 661 # figure out a reasonable window size 662 f = 1.5 663 x, y = screensize 664 xs = y / f * shape[1] # because y<x 665 ys = y / f * shape[0] 666 if xs > x / f: # shrink 667 xs = x / f 668 ys = xs / shape[1] * shape[0] 669 if ys > y / f: 670 ys = y / f 671 xs = ys / shape[0] * shape[1] 672 self.size = (int(xs), int(ys)) 673 if shape == (1, 1): 674 self.size = (int(y / f), int(y / f)) # because y<x 675 else: 676 self.size = (self.size[0], self.size[1]) 677 678 try: 679 image_actor = None 680 bgname = str(self.backgrcol).lower() 681 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 682 self.window.SetNumberOfLayers(2) 683 self.background_renderer = vtki.vtkRenderer() 684 self.background_renderer.SetLayer(0) 685 self.background_renderer.InteractiveOff() 686 self.background_renderer.SetBackground(vedo.get_color(bg2)) 687 image_actor = vedo.Image(self.backgrcol).actor 688 self.window.AddRenderer(self.background_renderer) 689 self.background_renderer.AddActor(image_actor) 690 except AttributeError: 691 pass 692 693 for i in reversed(range(shape[0])): 694 for j in range(shape[1]): 695 arenderer = vtki.vtkRenderer() 696 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 697 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 698 699 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 700 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 701 if vedo.settings.use_depth_peeling: 702 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 703 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 704 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 705 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 706 707 if image_actor: 708 arenderer.SetLayer(1) 709 710 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 711 if bg2: 712 arenderer.GradientBackgroundOn() 713 arenderer.SetBackground2(vedo.get_color(bg2)) 714 715 x0 = i / shape[0] 716 y0 = j / shape[1] 717 x1 = (i + 1) / shape[0] 718 y1 = (j + 1) / shape[1] 719 arenderer.SetViewport(y0, x0, y1, x1) 720 self.renderers.append(arenderer) 721 self.axes_instances.append(None) 722 self.shape = shape 723 724 if self.renderers: 725 self.renderer = self.renderers[0] 726 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 727 728 ######################################################### 729 if self.qt_widget or self.wx_widget: 730 if self.qt_widget: 731 self.window = self.qt_widget.GetRenderWindow() # overwrite 732 else: 733 self.window = self.wx_widget.GetRenderWindow() 734 self.interactor = self.window.GetInteractor() 735 736 ######################################################### 737 for r in self.renderers: 738 self.window.AddRenderer(r) 739 # set the background gradient if any 740 if vedo.settings.background_gradient_orientation > 0: 741 try: 742 modes = [ 743 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 744 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 745 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 746 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 747 ] 748 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 749 r.GradientBackgroundOn() 750 except AttributeError: 751 pass 752 753 ######################################################### 754 if self.qt_widget or self.wx_widget: 755 # self.window.SetSize(int(self.size[0]), int(self.size[1])) 756 self.interactor.SetRenderWindow(self.window) 757 # vsty = vtki.new("InteractorStyleTrackballCamera") 758 # self.interactor.SetInteractorStyle(vsty) 759 if vedo.settings.enable_default_keyboard_callbacks: 760 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 761 if vedo.settings.enable_default_mouse_callbacks: 762 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 763 return ################ 764 ######################## 765 766 if self.size[0] == "f": # full screen 767 self.size = "fullscreen" 768 self.window.SetFullScreen(True) 769 self.window.BordersOn() 770 else: 771 self.window.SetSize(int(self.size[0]), int(self.size[1])) 772 773 if self.offscreen: 774 if self.axes in (4, 5, 8, 12, 14): 775 self.axes = 0 # does not work with those 776 self.window.SetOffScreenRendering(True) 777 self.interactor = None 778 self._interactive = False 779 return ################ 780 ######################## 781 782 self.window.SetPosition(pos) 783 784 ######################################################### 785 self.interactor = vtki.vtkRenderWindowInteractor() 786 787 self.interactor.SetRenderWindow(self.window) 788 vsty = vtki.new("InteractorStyleTrackballCamera") 789 self.interactor.SetInteractorStyle(vsty) 790 self.interactor.RemoveObservers("CharEvent") 791 792 if vedo.settings.enable_default_keyboard_callbacks: 793 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 794 if vedo.settings.enable_default_mouse_callbacks: 795 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 796 797 ##################################################################### ..init ends here. 798 799 def __str__(self): 800 """Return Plotter info.""" 801 axtype = { 802 0: "(no axes)", 803 1: "(default customizable grid walls)", 804 2: "(cartesian axes from origin", 805 3: "(positive range of cartesian axes from origin", 806 4: "(axes triad at bottom left)", 807 5: "(oriented cube at bottom left)", 808 6: "(mark the corners of the bounding box)", 809 7: "(3D ruler at each side of the cartesian axes)", 810 8: "(the vtkCubeAxesActor object)", 811 9: "(the bounding box outline)", 812 10: "(circles of maximum bounding box range)", 813 11: "(show a large grid on the x-y plane)", 814 12: "(show polar axes)", 815 13: "(simple ruler at the bottom of the window)", 816 14: "(the vtkCameraOrientationWidget object)", 817 } 818 819 module = self.__class__.__module__ 820 name = self.__class__.__name__ 821 out = vedo.printc( 822 f"{module}.{name} at ({hex(id(self))})".ljust(75), 823 bold=True, invert=True, return_string=True, 824 ) 825 out += "\x1b[0m" 826 if self.interactor: 827 out += "window title".ljust(14) + ": " + self.title + "\n" 828 out += "window size".ljust(14) + f": {self.window.GetSize()}" 829 out += f", full_screen={self.window.GetScreenSize()}\n" 830 out += "activ renderer".ljust(14) + ": nr." + str(self.renderers.index(self.renderer)) 831 out += f" (out of {len(self.renderers)} renderers)\n" 832 833 bns, totpt = [], 0 834 for a in self.objects: 835 try: 836 b = a.bounds() 837 bns.append(b) 838 except AttributeError: 839 pass 840 try: 841 totpt += a.npoints 842 except AttributeError: 843 pass 844 out += "n. of objects".ljust(14) + f": {len(self.objects)}" 845 out += f" ({totpt} vertices)\n" if totpt else "\n" 846 847 if len(bns) > 0: 848 min_bns = np.min(bns, axis=0) 849 max_bns = np.max(bns, axis=0) 850 bx1, bx2 = utils.precision(min_bns[0], 3), utils.precision(max_bns[1], 3) 851 by1, by2 = utils.precision(min_bns[2], 3), utils.precision(max_bns[3], 3) 852 bz1, bz2 = utils.precision(min_bns[4], 3), utils.precision(max_bns[5], 3) 853 out += "bounds".ljust(14) + ":" 854 out += " x=(" + bx1 + ", " + bx2 + ")," 855 out += " y=(" + by1 + ", " + by2 + ")," 856 out += " z=(" + bz1 + ", " + bz2 + ")\n" 857 858 if utils.is_integer(self.axes): 859 out += "axes style".ljust(14) + f": {self.axes} {axtype[self.axes]}\n" 860 elif isinstance(self.axes, dict): 861 out += "axes style".ljust(14) + f": 1 {axtype[1]}\n" 862 else: 863 out += "axes style".ljust(14) + f": {[self.axes]}\n" 864 return out.rstrip() + "\x1b[0m" 865 866 def print(self): 867 """Print information about the current instance.""" 868 print(self.__str__()) 869 return self 870 871 def __iadd__(self, objects): 872 self.add(objects) 873 return self 874 875 def __isub__(self, objects): 876 self.remove(objects) 877 return self 878 879 def __enter__(self): 880 # context manager like in "with Plotter() as plt:" 881 return self 882 883 def __exit__(self, *args, **kwargs): 884 # context manager like in "with Plotter() as plt:" 885 self.close() 886 887 def initialize_interactor(self) -> Self: 888 """Initialize the interactor if not already initialized.""" 889 if self.offscreen: 890 return self 891 if self.interactor: 892 if not self.interactor.GetInitialized(): 893 self.interactor.Initialize() 894 self.interactor.RemoveObservers("CharEvent") 895 return self 896 897 def process_events(self) -> Self: 898 """Process all pending events.""" 899 self.initialize_interactor() 900 if self.interactor: 901 try: 902 self.interactor.ProcessEvents() 903 except AttributeError: 904 pass 905 return self 906 907 def at(self, nren: int, yren=None) -> Self: 908 """ 909 Select the current renderer number as an int. 910 Can also use the `[nx, ny]` format. 911 """ 912 if utils.is_sequence(nren): 913 if len(nren) == 2: 914 nren, yren = nren 915 else: 916 vedo.logger.error("at() argument must be a single number or a list of two numbers") 917 raise RuntimeError 918 919 if yren is not None: 920 a, b = self.shape 921 x, y = nren, yren 922 nren = x * b + y 923 # print("at (", x, y, ") -> ren", nren) 924 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 925 vedo.logger.error(f"at({nren, yren}) is malformed!") 926 raise RuntimeError 927 928 self.renderer = self.renderers[nren] 929 return self 930 931 def add(self, *objs, at=None) -> Self: 932 """ 933 Append the input objects to the internal list of objects to be shown. 934 935 Arguments: 936 at : (int) 937 add the object at the specified renderer 938 """ 939 if at is not None: 940 ren = self.renderers[at] 941 else: 942 ren = self.renderer 943 944 objs = utils.flatten(objs) 945 for ob in objs: 946 if ob and ob not in self.objects: 947 self.objects.append(ob) 948 949 acts = self._scan_input_return_acts(objs) 950 951 for a in acts: 952 953 if ren: 954 if isinstance(a, vedo.addons.BaseCutter): 955 a.add_to(self) # from cutters 956 continue 957 958 if isinstance(a, vtki.vtkLight): 959 ren.AddLight(a) 960 continue 961 962 try: 963 ren.AddActor(a) 964 except TypeError: 965 ren.AddActor(a.actor) 966 967 try: 968 ir = self.renderers.index(ren) 969 a.rendered_at.add(ir) # might not have rendered_at 970 except (AttributeError, ValueError): 971 pass 972 973 if isinstance(a, vtki.vtkFollower): 974 a.SetCamera(self.camera) 975 elif isinstance(a, vedo.visual.LightKit): 976 a.lightkit.AddLightsToRenderer(ren) 977 978 return self 979 980 def remove(self, *objs, at=None) -> Self: 981 """ 982 Remove input object to the internal list of objects to be shown. 983 984 Objects to be removed can be referenced by their assigned name, 985 986 Arguments: 987 at : (int) 988 remove the object at the specified renderer 989 """ 990 # TODO and you can also use wildcards like `*` and `?`. 991 if at is not None: 992 ren = self.renderers[at] 993 else: 994 ren = self.renderer 995 996 objs = [ob for ob in utils.flatten(objs) if ob] 997 998 has_str = False 999 for ob in objs: 1000 if isinstance(ob, str): 1001 has_str = True 1002 break 1003 1004 has_actor = False 1005 for ob in objs: 1006 if hasattr(ob, "actor") and ob.actor: 1007 has_actor = True 1008 break 1009 1010 if has_str or has_actor: 1011 # need to get the actors to search for 1012 for a in self.get_actors(include_non_pickables=True): 1013 # print("PARSING", [a]) 1014 try: 1015 if (a.name and a.name in objs) or a in objs: 1016 objs.append(a) 1017 # if a.name: 1018 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1019 # if any(bools) or a in objs: 1020 # objs.append(a) 1021 # print('a.name',a.name, objs,any(bools)) 1022 except AttributeError: # no .name 1023 # passing the actor so get back the object with .retrieve_object() 1024 try: 1025 vobj = a.retrieve_object() 1026 if (vobj.name and vobj.name in objs) or vobj in objs: 1027 # print('vobj.name', vobj.name) 1028 objs.append(vobj) 1029 except AttributeError: 1030 pass 1031 1032 ir = self.renderers.index(ren) 1033 1034 ids = [] 1035 for ob in set(objs): 1036 1037 # will remove it from internal list if possible 1038 try: 1039 idx = self.objects.index(ob) 1040 ids.append(idx) 1041 except ValueError: 1042 pass 1043 1044 if ren: ### remove it from the renderer 1045 1046 if isinstance(ob, vedo.addons.BaseCutter): 1047 ob.remove_from(self) # from cutters 1048 continue 1049 1050 try: 1051 ren.RemoveActor(ob) 1052 except TypeError: 1053 try: 1054 ren.RemoveActor(ob.actor) 1055 except AttributeError: 1056 pass 1057 1058 if hasattr(ob, "rendered_at"): 1059 ob.rendered_at.discard(ir) 1060 1061 if hasattr(ob, "scalarbar") and ob.scalarbar: 1062 ren.RemoveActor(ob.scalarbar) 1063 if hasattr(ob, "_caption") and ob._caption: 1064 ren.RemoveActor(ob._caption) 1065 if hasattr(ob, "shadows") and ob.shadows: 1066 for sha in ob.shadows: 1067 ren.RemoveActor(sha.actor) 1068 if hasattr(ob, "trail") and ob.trail: 1069 ren.RemoveActor(ob.trail.actor) 1070 ob.trail_points = [] 1071 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1072 for sha in ob.trail.shadows: 1073 ren.RemoveActor(sha.actor) 1074 1075 elif isinstance(ob, vedo.visual.LightKit): 1076 ob.lightkit.RemoveLightsFromRenderer(ren) 1077 1078 # for i in ids: # WRONG way of doing it! 1079 # del self.objects[i] 1080 # instead we do: 1081 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1082 return self 1083 1084 @property 1085 def actors(self): 1086 """Return the list of actors.""" 1087 return [ob.actor for ob in self.objects if hasattr(ob, "actor")] 1088 1089 def remove_lights(self) -> Self: 1090 """Remove all the present lights in the current renderer.""" 1091 if self.renderer: 1092 self.renderer.RemoveAllLights() 1093 return self 1094 1095 def pop(self, at=None) -> Self: 1096 """ 1097 Remove the last added object from the rendering window. 1098 This method is typically used in loops or callback functions. 1099 """ 1100 if at is not None and not isinstance(at, int): 1101 # wrong usage pitfall 1102 vedo.logger.error("argument of pop() must be an integer") 1103 raise RuntimeError() 1104 1105 if self.objects: 1106 self.remove(self.objects[-1], at) 1107 return self 1108 1109 def render(self, resetcam=False) -> Self: 1110 """Render the scene. This method is typically used in loops or callback functions.""" 1111 1112 if vedo.settings.dry_run_mode >= 2: 1113 return self 1114 1115 if not self.window: 1116 return self 1117 1118 self.initialize_interactor() 1119 1120 if resetcam: 1121 self.renderer.ResetCamera() 1122 1123 self.window.Render() 1124 1125 if self._cocoa_process_events and self.interactor and self.interactor.GetInitialized(): 1126 if "Darwin" in vedo.sys_platform and not self.offscreen: 1127 self.interactor.ProcessEvents() 1128 self._cocoa_process_events = False 1129 return self 1130 1131 def interactive(self) -> Self: 1132 """ 1133 Start window interaction. 1134 Analogous to `show(..., interactive=True)`. 1135 """ 1136 if vedo.settings.dry_run_mode >= 1: 1137 return self 1138 self.initialize_interactor() 1139 if self.interactor: 1140 # print("self.interactor.Start()") 1141 self.interactor.Start() 1142 # print("self.interactor.Start() done") 1143 if self._must_close_now: 1144 # print("self.interactor.TerminateApp()") 1145 if self.interactor: 1146 self.interactor.GetRenderWindow().Finalize() 1147 self.interactor.TerminateApp() 1148 self.interactor = None 1149 self.window = None 1150 self.renderer = None 1151 self.renderers = [] 1152 self.camera = None 1153 return self 1154 1155 def use_depth_peeling(self, at=None, value=True) -> Self: 1156 """ 1157 Specify whether use depth peeling algorithm at this specific renderer 1158 Call this method before the first rendering. 1159 """ 1160 if at is None: 1161 ren = self.renderer 1162 else: 1163 ren = self.renderers[at] 1164 ren.SetUseDepthPeeling(value) 1165 return self 1166 1167 def background(self, c1=None, c2=None, at=None, mode=0) -> Union[Self, "np.ndarray"]: 1168 """Set the color of the background for the current renderer. 1169 A different renderer index can be specified by keyword `at`. 1170 1171 Arguments: 1172 c1 : (list) 1173 background main color. 1174 c2 : (list) 1175 background color for the upper part of the window. 1176 at : (int) 1177 renderer index. 1178 mode : (int) 1179 background mode (needs vtk version >= 9.3) 1180 0 = vertical, 1181 1 = horizontal, 1182 2 = radial farthest side, 1183 3 = radia farthest corner. 1184 """ 1185 if not self.renderers: 1186 return self 1187 if at is None: 1188 r = self.renderer 1189 else: 1190 r = self.renderers[at] 1191 1192 if c1 is None and c2 is None: 1193 return np.array(r.GetBackground()) 1194 1195 if r: 1196 if c1 is not None: 1197 r.SetBackground(vedo.get_color(c1)) 1198 if c2 is not None: 1199 r.GradientBackgroundOn() 1200 r.SetBackground2(vedo.get_color(c2)) 1201 if mode: 1202 try: # only works with vtk>=9.3 1203 modes = [ 1204 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1205 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1206 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1207 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1208 ] 1209 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 1210 except AttributeError: 1211 pass 1212 1213 else: 1214 r.GradientBackgroundOff() 1215 return self 1216 1217 ################################################################## 1218 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1219 """ 1220 Return a list of Meshes from the specified renderer. 1221 1222 Arguments: 1223 at : (int) 1224 specify which renderer to look at. 1225 include_non_pickables : (bool) 1226 include non-pickable objects 1227 unpack_assemblies : (bool) 1228 unpack assemblies into their components 1229 """ 1230 if at is None: 1231 renderer = self.renderer 1232 at = self.renderers.index(renderer) 1233 elif isinstance(at, int): 1234 renderer = self.renderers[at] 1235 1236 has_global_axes = False 1237 if isinstance(self.axes_instances[at], vedo.Assembly): 1238 has_global_axes = True 1239 1240 if unpack_assemblies: 1241 acs = renderer.GetActors() 1242 else: 1243 acs = renderer.GetViewProps() 1244 1245 objs = [] 1246 acs.InitTraversal() 1247 for _ in range(acs.GetNumberOfItems()): 1248 1249 if unpack_assemblies: 1250 a = acs.GetNextItem() 1251 else: 1252 a = acs.GetNextProp() 1253 1254 if isinstance(a, vtki.vtkVolume): 1255 continue 1256 1257 if include_non_pickables or a.GetPickable(): 1258 if a == self.axes_instances[at]: 1259 continue 1260 if has_global_axes and a in self.axes_instances[at].actors: 1261 continue 1262 try: 1263 objs.append(a.retrieve_object()) 1264 except AttributeError: 1265 pass 1266 return objs 1267 1268 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1269 """ 1270 Return a list of Volumes from the specified renderer. 1271 1272 Arguments: 1273 at : (int) 1274 specify which renderer to look at 1275 include_non_pickables : (bool) 1276 include non-pickable objects 1277 """ 1278 if at is None: 1279 renderer = self.renderer 1280 at = self.renderers.index(renderer) 1281 elif isinstance(at, int): 1282 renderer = self.renderers[at] 1283 1284 vols = [] 1285 acs = renderer.GetVolumes() 1286 acs.InitTraversal() 1287 for _ in range(acs.GetNumberOfItems()): 1288 a = acs.GetNextItem() 1289 if include_non_pickables or a.GetPickable(): 1290 try: 1291 vols.append(a.retrieve_object()) 1292 except AttributeError: 1293 pass 1294 return vols 1295 1296 def get_actors(self, at=None, include_non_pickables=False) -> list: 1297 """ 1298 Return a list of Volumes from the specified renderer. 1299 1300 Arguments: 1301 at : (int) 1302 specify which renderer to look at 1303 include_non_pickables : (bool) 1304 include non-pickable objects 1305 """ 1306 if at is None: 1307 renderer = self.renderer 1308 at = self.renderers.index(renderer) 1309 elif isinstance(at, int): 1310 renderer = self.renderers[at] 1311 1312 acts = [] 1313 acs = renderer.GetViewProps() 1314 acs.InitTraversal() 1315 for _ in range(acs.GetNumberOfItems()): 1316 a = acs.GetNextProp() 1317 if include_non_pickables or a.GetPickable(): 1318 acts.append(a) 1319 return acts 1320 1321 def check_actors_trasform(self, at=None) -> Self: 1322 """ 1323 Reset the transformation matrix of all actors at specified renderer. 1324 This is only useful when actors have been moved/rotated/scaled manually 1325 in an already rendered scene using interactors like 1326 'TrackballActor' or 'JoystickActor'. 1327 """ 1328 # see issue https://github.com/marcomusy/vedo/issues/1046 1329 for a in self.get_actors(at=at, include_non_pickables=True): 1330 try: 1331 M = a.GetMatrix() 1332 except AttributeError: 1333 continue 1334 if M and not M.IsIdentity(): 1335 try: 1336 a.retrieve_object().apply_transform_from_actor() 1337 # vedo.logger.info( 1338 # f"object '{a.retrieve_object().name}' " 1339 # "was manually moved. Updated to its current position." 1340 # ) 1341 except AttributeError: 1342 pass 1343 return self 1344 1345 def reset_camera(self, tight=None) -> Self: 1346 """ 1347 Reset the camera position and zooming. 1348 If tight (float) is specified the zooming reserves a padding space 1349 in the xy-plane expressed in percent of the average size. 1350 """ 1351 if tight is None: 1352 self.renderer.ResetCamera() 1353 else: 1354 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1355 cam = self.camera 1356 1357 self.renderer.ComputeAspect() 1358 aspect = self.renderer.GetAspect() 1359 angle = np.pi * cam.GetViewAngle() / 180.0 1360 dx = x1 - x0 1361 dy = y1 - y0 1362 dist = max(dx / aspect[0], dy) / np.tan(angle / 2) / 2 1363 1364 cam.SetViewUp(0, 1, 0) 1365 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1366 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1367 if cam.GetParallelProjection(): 1368 ps = max(dx / aspect[0], dy) / 2 1369 cam.SetParallelScale(ps * (1 + tight)) 1370 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1371 return self 1372 1373 def reset_clipping_range(self, bounds=None) -> Self: 1374 """ 1375 Reset the camera clipping range to include all visible actors. 1376 If bounds is given, it will be used instead of computing it. 1377 """ 1378 if bounds is None: 1379 self.renderer.ResetCameraClippingRange() 1380 else: 1381 self.renderer.ResetCameraClippingRange(bounds) 1382 return self 1383 1384 def reset_viewup(self, smooth=True) -> Self: 1385 """ 1386 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1387 """ 1388 vbb = addons.compute_visible_bounds()[0] 1389 x0, x1, y0, y1, z0, z1 = vbb 1390 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1391 d = self.camera.GetDistance() 1392 1393 viewups = np.array( 1394 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1395 ) 1396 positions = np.array( 1397 [ 1398 (mx, my, mz + d), 1399 (mx, my, mz - d), 1400 (mx, my + d, mz), 1401 (mx, my - d, mz), 1402 (mx + d, my, mz), 1403 (mx - d, my, mz), 1404 ] 1405 ) 1406 1407 vu = np.array(self.camera.GetViewUp()) 1408 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1409 1410 poc = np.array(self.camera.GetPosition()) 1411 foc = np.array(self.camera.GetFocalPoint()) 1412 a = poc - foc 1413 b = positions - foc 1414 a = a / np.linalg.norm(a) 1415 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1416 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1417 1418 if smooth: 1419 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1420 for t in outtimes: 1421 vv = vu * (1 - t) + viewups[vui] * t 1422 pp = poc * (1 - t) + positions[pui] * t 1423 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1424 self.camera.SetViewUp(vv) 1425 self.camera.SetPosition(pp) 1426 self.camera.SetFocalPoint(ff) 1427 self.render() 1428 1429 # interpolator does not respect parallel view...: 1430 # cam1 = dict( 1431 # pos=poc, 1432 # viewup=vu, 1433 # focal_point=(mx,my,mz), 1434 # clipping_range=self.camera.GetClippingRange() 1435 # ) 1436 # # cam1 = self.camera 1437 # cam2 = dict( 1438 # pos=positions[pui], 1439 # viewup=viewups[vui], 1440 # focal_point=(mx,my,mz), 1441 # clipping_range=self.camera.GetClippingRange() 1442 # ) 1443 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1444 # for c in vcams: 1445 # self.renderer.SetActiveCamera(c) 1446 # self.render() 1447 else: 1448 1449 self.camera.SetViewUp(viewups[vui]) 1450 self.camera.SetPosition(positions[pui]) 1451 self.camera.SetFocalPoint(mx, my, mz) 1452 1453 self.renderer.ResetCameraClippingRange() 1454 1455 # vbb, _, _, _ = addons.compute_visible_bounds() 1456 # x0,x1, y0,y1, z0,z1 = vbb 1457 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1458 self.render() 1459 return self 1460 1461 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1462 """ 1463 Takes as input two cameras set camera at an interpolated position: 1464 1465 Cameras can be vtkCamera or dictionaries in format: 1466 1467 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1468 1469 Press `shift-C` key in interactive mode to dump a python snipplet 1470 of parameters for the current camera view. 1471 """ 1472 nc = len(cameras) 1473 if len(times) == 0: 1474 times = np.linspace(0, 1, num=nc, endpoint=True) 1475 1476 assert len(times) == nc 1477 1478 cin = vtki.new("CameraInterpolator") 1479 1480 # cin.SetInterpolationTypeToLinear() # buggy? 1481 if nc > 2 and smooth: 1482 cin.SetInterpolationTypeToSpline() 1483 1484 for i, cam in enumerate(cameras): 1485 vcam = cam 1486 if isinstance(cam, dict): 1487 vcam = utils.camera_from_dict(cam) 1488 cin.AddCamera(times[i], vcam) 1489 1490 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1491 rng = maxt - mint 1492 1493 if len(output_times) == 0: 1494 cin.InterpolateCamera(t * rng, self.camera) 1495 return [self.camera] 1496 else: 1497 vcams = [] 1498 for tt in output_times: 1499 c = vtki.vtkCamera() 1500 cin.InterpolateCamera(tt * rng, c) 1501 vcams.append(c) 1502 return vcams 1503 1504 def fly_to(self, point) -> Self: 1505 """ 1506 Fly camera to the specified point. 1507 1508 Arguments: 1509 point : (list) 1510 point in space to place camera. 1511 1512 Example: 1513 ```python 1514 from vedo import * 1515 cone = Cone() 1516 plt = Plotter(axes=1) 1517 plt.show(cone) 1518 plt.fly_to([1,0,0]) 1519 plt.interactive().close() 1520 ``` 1521 """ 1522 if self.interactor: 1523 self.resetcam = False 1524 self.interactor.FlyTo(self.renderer, point) 1525 return self 1526 1527 def look_at(self, plane="xy") -> Self: 1528 """Move the camera so that it looks at the specified cartesian plane""" 1529 cam = self.renderer.GetActiveCamera() 1530 fp = np.array(cam.GetFocalPoint()) 1531 p = np.array(cam.GetPosition()) 1532 dist = np.linalg.norm(fp - p) 1533 plane = plane.lower() 1534 if "x" in plane and "y" in plane: 1535 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1536 cam.SetViewUp(0.0, 1.0, 0.0) 1537 elif "x" in plane and "z" in plane: 1538 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1539 cam.SetViewUp(0.0, 0.0, 1.0) 1540 elif "y" in plane and "z" in plane: 1541 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1542 cam.SetViewUp(0.0, 0.0, 1.0) 1543 else: 1544 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1545 return self 1546 1547 def record(self, filename="") -> str: 1548 """ 1549 Record camera, mouse, keystrokes and all other events. 1550 Recording can be toggled on/off by pressing key "R". 1551 1552 Arguments: 1553 filename : (str) 1554 ascii file to store events. 1555 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1556 1557 Returns: 1558 a string descriptor of events. 1559 1560 Examples: 1561 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1562 """ 1563 if vedo.settings.dry_run_mode >= 1: 1564 return "" 1565 if not self.interactor: 1566 vedo.logger.warning("Cannot record events, no interactor defined.") 1567 return "" 1568 erec = vtki.new("InteractorEventRecorder") 1569 erec.SetInteractor(self.interactor) 1570 if not filename: 1571 if not os.path.exists(vedo.settings.cache_directory): 1572 os.makedirs(vedo.settings.cache_directory) 1573 home_dir = os.path.expanduser("~") 1574 filename = os.path.join( 1575 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1576 print("Events will be recorded in", filename) 1577 erec.SetFileName(filename) 1578 erec.SetKeyPressActivationValue("R") 1579 erec.EnabledOn() 1580 erec.Record() 1581 self.interactor.Start() 1582 erec.Stop() 1583 erec.EnabledOff() 1584 with open(filename, "r", encoding="UTF-8") as fl: 1585 events = fl.read() 1586 erec = None 1587 return events 1588 1589 def play(self, recorded_events="", repeats=0) -> Self: 1590 """ 1591 Play camera, mouse, keystrokes and all other events. 1592 1593 Arguments: 1594 events : (str) 1595 file o string of events. 1596 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1597 repeats : (int) 1598 number of extra repeats of the same events. The default is 0. 1599 1600 Examples: 1601 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1602 """ 1603 if vedo.settings.dry_run_mode >= 1: 1604 return self 1605 if not self.interactor: 1606 vedo.logger.warning("Cannot play events, no interactor defined.") 1607 return self 1608 1609 erec = vtki.new("InteractorEventRecorder") 1610 erec.SetInteractor(self.interactor) 1611 1612 if not recorded_events: 1613 home_dir = os.path.expanduser("~") 1614 recorded_events = os.path.join( 1615 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1616 1617 if recorded_events.endswith(".log"): 1618 erec.ReadFromInputStringOff() 1619 erec.SetFileName(recorded_events) 1620 else: 1621 erec.ReadFromInputStringOn() 1622 erec.SetInputString(recorded_events) 1623 1624 erec.Play() 1625 for _ in range(repeats): 1626 erec.Rewind() 1627 erec.Play() 1628 erec.EnabledOff() 1629 erec = None 1630 return self 1631 1632 def parallel_projection(self, value=True, at=None) -> Self: 1633 """ 1634 Use parallel projection `at` a specified renderer. 1635 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1636 An input value equal to -1 will toggle it on/off. 1637 """ 1638 if at is not None: 1639 r = self.renderers[at] 1640 else: 1641 r = self.renderer 1642 if value == -1: 1643 val = r.GetActiveCamera().GetParallelProjection() 1644 value = not val 1645 r.GetActiveCamera().SetParallelProjection(value) 1646 r.Modified() 1647 return self 1648 1649 def render_hidden_lines(self, value=True) -> Self: 1650 """Remove hidden lines when in wireframe mode.""" 1651 self.renderer.SetUseHiddenLineRemoval(not value) 1652 return self 1653 1654 def fov(self, angle: float) -> Self: 1655 """ 1656 Set the field of view angle for the camera. 1657 This is the angle of the camera frustum in the horizontal direction. 1658 High values will result in a wide-angle lens (fish-eye effect), 1659 and low values will result in a telephoto lens. 1660 1661 Default value is 30 degrees. 1662 """ 1663 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1664 self.renderer.GetActiveCamera().SetViewAngle(angle) 1665 return self 1666 1667 def zoom(self, zoom: float) -> Self: 1668 """Apply a zooming factor for the current camera view""" 1669 self.renderer.GetActiveCamera().Zoom(zoom) 1670 return self 1671 1672 def azimuth(self, angle: float) -> Self: 1673 """Rotate camera around the view up vector.""" 1674 self.renderer.GetActiveCamera().Azimuth(angle) 1675 return self 1676 1677 def elevation(self, angle: float) -> Self: 1678 """Rotate the camera around the cross product of the negative 1679 of the direction of projection and the view up vector.""" 1680 self.renderer.GetActiveCamera().Elevation(angle) 1681 return self 1682 1683 def roll(self, angle: float) -> Self: 1684 """Roll the camera about the direction of projection.""" 1685 self.renderer.GetActiveCamera().Roll(angle) 1686 return self 1687 1688 def dolly(self, value: float) -> Self: 1689 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1690 self.renderer.GetActiveCamera().Dolly(value) 1691 return self 1692 1693 ################################################################## 1694 def add_slider( 1695 self, 1696 sliderfunc, 1697 xmin, 1698 xmax, 1699 value=None, 1700 pos=4, 1701 title="", 1702 font="Calco", 1703 title_size=1, 1704 c=None, 1705 alpha=1, 1706 show_value=True, 1707 delayed=False, 1708 **options, 1709 ) -> "vedo.addons.Slider2D": 1710 """ 1711 Add a `vedo.addons.Slider2D` which can call an external custom function. 1712 1713 Arguments: 1714 sliderfunc : (Callable) 1715 external function to be called by the widget 1716 xmin : (float) 1717 lower value of the slider 1718 xmax : (float) 1719 upper value 1720 value : (float) 1721 current value 1722 pos : (list, str) 1723 position corner number: horizontal [1-5] or vertical [11-15] 1724 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1725 and also by a string descriptor (eg. "bottom-left") 1726 title : (str) 1727 title text 1728 font : (str) 1729 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1730 title_size : (float) 1731 title text scale [1.0] 1732 show_value : (bool) 1733 if True current value is shown 1734 delayed : (bool) 1735 if True the callback is delayed until when the mouse button is released 1736 alpha : (float) 1737 opacity of the scalar bar texts 1738 slider_length : (float) 1739 slider length 1740 slider_width : (float) 1741 slider width 1742 end_cap_length : (float) 1743 length of the end cap 1744 end_cap_width : (float) 1745 width of the end cap 1746 tube_width : (float) 1747 width of the tube 1748 title_height : (float) 1749 width of the title 1750 tformat : (str) 1751 format of the title 1752 1753 Examples: 1754 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1755 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1756 1757 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1758 """ 1759 if c is None: # automatic black or white 1760 c = (0.8, 0.8, 0.8) 1761 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1762 c = (0.2, 0.2, 0.2) 1763 else: 1764 c = vedo.get_color(c) 1765 1766 slider2d = addons.Slider2D( 1767 sliderfunc, 1768 xmin, 1769 xmax, 1770 value, 1771 pos, 1772 title, 1773 font, 1774 title_size, 1775 c, 1776 alpha, 1777 show_value, 1778 delayed, 1779 **options, 1780 ) 1781 1782 if self.renderer: 1783 slider2d.renderer = self.renderer 1784 if self.interactor: 1785 slider2d.interactor = self.interactor 1786 slider2d.on() 1787 self.sliders.append([slider2d, sliderfunc]) 1788 return slider2d 1789 1790 def add_slider3d( 1791 self, 1792 sliderfunc, 1793 pos1, 1794 pos2, 1795 xmin, 1796 xmax, 1797 value=None, 1798 s=0.03, 1799 t=1, 1800 title="", 1801 rotation=0.0, 1802 c=None, 1803 show_value=True, 1804 ) -> "vedo.addons.Slider3D": 1805 """ 1806 Add a 3D slider widget which can call an external custom function. 1807 1808 Arguments: 1809 sliderfunc : (function) 1810 external function to be called by the widget 1811 pos1 : (list) 1812 first position 3D coordinates 1813 pos2 : (list) 1814 second position coordinates 1815 xmin : (float) 1816 lower value 1817 xmax : (float) 1818 upper value 1819 value : (float) 1820 initial value 1821 s : (float) 1822 label scaling factor 1823 t : (float) 1824 tube scaling factor 1825 title : (str) 1826 title text 1827 c : (color) 1828 slider color 1829 rotation : (float) 1830 title rotation around slider axis 1831 show_value : (bool) 1832 if True current value is shown 1833 1834 Examples: 1835 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1836 1837 ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png) 1838 """ 1839 if c is None: # automatic black or white 1840 c = (0.8, 0.8, 0.8) 1841 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1842 c = (0.2, 0.2, 0.2) 1843 else: 1844 c = vedo.get_color(c) 1845 1846 slider3d = addons.Slider3D( 1847 sliderfunc, 1848 pos1, 1849 pos2, 1850 xmin, 1851 xmax, 1852 value, 1853 s, 1854 t, 1855 title, 1856 rotation, 1857 c, 1858 show_value, 1859 ) 1860 slider3d.renderer = self.renderer 1861 slider3d.interactor = self.interactor 1862 slider3d.on() 1863 self.sliders.append([slider3d, sliderfunc]) 1864 return slider3d 1865 1866 def add_button( 1867 self, 1868 fnc=None, 1869 states=("On", "Off"), 1870 c=("w", "w"), 1871 bc=("green4", "red4"), 1872 pos=(0.7, 0.1), 1873 size=24, 1874 font="Courier", 1875 bold=True, 1876 italic=False, 1877 alpha=1, 1878 angle=0, 1879 ) -> Union["vedo.addons.Button", None]: 1880 """ 1881 Add a button to the renderer window. 1882 1883 Arguments: 1884 states : (list) 1885 a list of possible states, e.g. ['On', 'Off'] 1886 c : (list) 1887 a list of colors for each state 1888 bc : (list) 1889 a list of background colors for each state 1890 pos : (list) 1891 2D position from left-bottom corner 1892 size : (float) 1893 size of button font 1894 font : (str) 1895 font type. Check [available fonts here](https://vedo.embl.es/fonts). 1896 bold : (bool) 1897 bold font face (False) 1898 italic : (bool) 1899 italic font face (False) 1900 alpha : (float) 1901 opacity level 1902 angle : (float) 1903 anticlockwise rotation in degrees 1904 1905 Returns: 1906 `vedo.addons.Button` object. 1907 1908 Examples: 1909 - [buttons1.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons1.py) 1910 - [buttons2.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons2.py) 1911 1912 ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg) 1913 """ 1914 if self.interactor: 1915 bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle) 1916 self.renderer.AddActor2D(bu) 1917 self.buttons.append(bu) 1918 # bu.function_id = self.add_callback("LeftButtonPress", bu.function) # not good 1919 bu.function_id = bu.add_observer("pick", bu.function, priority=10) 1920 return bu 1921 return None 1922 1923 def add_spline_tool( 1924 self, points, pc="k", ps=8, lc="r4", ac="g5", 1925 lw=2, alpha=1, closed=False, ontop=True, can_add_nodes=True, 1926 ) -> "vedo.addons.SplineTool": 1927 """ 1928 Add a spline tool to the current plotter. 1929 Nodes of the spline can be dragged in space with the mouse. 1930 Clicking on the line itself adds an extra point. 1931 Selecting a point and pressing del removes it. 1932 1933 Arguments: 1934 points : (Mesh, Points, array) 1935 the set of vertices forming the spline nodes. 1936 pc : (str) 1937 point color. The default is 'k'. 1938 ps : (str) 1939 point size. The default is 8. 1940 lc : (str) 1941 line color. The default is 'r4'. 1942 ac : (str) 1943 active point marker color. The default is 'g5'. 1944 lw : (int) 1945 line width. The default is 2. 1946 alpha : (float) 1947 line transparency. 1948 closed : (bool) 1949 spline is meant to be closed. The default is False. 1950 1951 Returns: 1952 a `SplineTool` object. 1953 1954 Examples: 1955 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1956 1957 ![](https://vedo.embl.es/images/basic/spline_tool.png) 1958 """ 1959 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1960 sw.interactor = self.interactor 1961 sw.on() 1962 sw.Initialize(sw.points.dataset) 1963 sw.representation.SetRenderer(self.renderer) 1964 sw.representation.SetClosedLoop(closed) 1965 sw.representation.BuildRepresentation() 1966 self.widgets.append(sw) 1967 return sw 1968 1969 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1970 """Add an inset icon mesh into the same renderer. 1971 1972 Arguments: 1973 pos : (int, list) 1974 icon position in the range [1-4] indicating one of the 4 corners, 1975 or it can be a tuple (x,y) as a fraction of the renderer size. 1976 size : (float) 1977 size of the square inset. 1978 1979 Examples: 1980 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1981 """ 1982 iconw = addons.Icon(icon, pos, size) 1983 1984 iconw.SetInteractor(self.interactor) 1985 iconw.EnabledOn() 1986 iconw.InteractiveOff() 1987 self.widgets.append(iconw) 1988 return iconw 1989 1990 def add_global_axes(self, axtype=None, c=None) -> Self: 1991 """Draw axes on scene. Available axes types: 1992 1993 Arguments: 1994 axtype : (int) 1995 - 0, no axes, 1996 - 1, draw three gray grid walls 1997 - 2, show cartesian axes from (0,0,0) 1998 - 3, show positive range of cartesian axes from (0,0,0) 1999 - 4, show a triad at bottom left 2000 - 5, show a cube at bottom left 2001 - 6, mark the corners of the bounding box 2002 - 7, draw a 3D ruler at each side of the cartesian axes 2003 - 8, show the vtkCubeAxesActor object 2004 - 9, show the bounding box outLine 2005 - 10, show three circles representing the maximum bounding box 2006 - 11, show a large grid on the x-y plane 2007 - 12, show polar axes 2008 - 13, draw a simple ruler at the bottom of the window 2009 2010 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 2011 2012 Example: 2013 ```python 2014 from vedo import Box, show 2015 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 2016 show( 2017 b, 2018 axes={ 2019 "xtitle": "Some long variable [a.u.]", 2020 "number_of_divisions": 4, 2021 # ... 2022 }, 2023 ) 2024 ``` 2025 2026 Examples: 2027 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2028 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2029 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2030 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2031 2032 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2033 """ 2034 addons.add_global_axes(axtype, c) 2035 return self 2036 2037 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2038 """Add a legend to the top right. 2039 2040 Examples: 2041 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 2042 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 2043 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 2044 """ 2045 acts = self.get_meshes() 2046 lb = addons.LegendBox(acts, **kwargs) 2047 self.add(lb) 2048 return lb 2049 2050 def add_hint( 2051 self, 2052 obj, 2053 text="", 2054 c="k", 2055 bg="yellow9", 2056 font="Calco", 2057 size=18, 2058 justify=0, 2059 angle=0, 2060 delay=250, 2061 ) -> Union[vtki.vtkBalloonWidget, None]: 2062 """ 2063 Create a pop-up hint style message when hovering an object. 2064 Use `add_hint(obj, False)` to disable a hinting a specific object. 2065 Use `add_hint(None)` to disable all hints. 2066 2067 Arguments: 2068 obj : (Mesh, Points) 2069 the object to associate the pop-up to 2070 text : (str) 2071 string description of the pop-up 2072 delay : (int) 2073 milliseconds to wait before pop-up occurs 2074 """ 2075 if self.offscreen or not self.interactor: 2076 return None 2077 2078 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2079 # Linux vtk9.0 is bugged 2080 vedo.logger.warning( 2081 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2082 ) 2083 return None 2084 2085 if obj is None: 2086 self.hint_widget.EnabledOff() 2087 self.hint_widget.SetInteractor(None) 2088 self.hint_widget = None 2089 return self.hint_widget 2090 2091 if text is False and self.hint_widget: 2092 self.hint_widget.RemoveBalloon(obj) 2093 return self.hint_widget 2094 2095 if text == "": 2096 if obj.name: 2097 text = obj.name 2098 elif obj.filename: 2099 text = obj.filename 2100 else: 2101 return None 2102 2103 if not self.hint_widget: 2104 self.hint_widget = vtki.vtkBalloonWidget() 2105 2106 rep = self.hint_widget.GetRepresentation() 2107 rep.SetBalloonLayoutToImageRight() 2108 2109 trep = rep.GetTextProperty() 2110 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2111 trep.SetFontFile(utils.get_font_path(font)) 2112 trep.SetFontSize(size) 2113 trep.SetColor(vedo.get_color(c)) 2114 trep.SetBackgroundColor(vedo.get_color(bg)) 2115 trep.SetShadow(0) 2116 trep.SetJustification(justify) 2117 trep.UseTightBoundingBoxOn() 2118 2119 self.hint_widget.ManagesCursorOff() 2120 self.hint_widget.SetTimerDuration(delay) 2121 self.hint_widget.SetInteractor(self.interactor) 2122 if angle: 2123 trep.SetOrientation(angle) 2124 trep.SetBackgroundOpacity(0) 2125 # else: 2126 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2127 self.hint_widget.SetRepresentation(rep) 2128 self.widgets.append(self.hint_widget) 2129 self.hint_widget.EnabledOn() 2130 2131 bst = self.hint_widget.GetBalloonString(obj.actor) 2132 if bst: 2133 self.hint_widget.UpdateBalloonString(obj.actor, text) 2134 else: 2135 self.hint_widget.AddBalloon(obj.actor, text) 2136 2137 return self.hint_widget 2138 2139 def add_shadows(self) -> Self: 2140 """Add shadows at the current renderer.""" 2141 if self.renderer: 2142 shadows = vtki.new("ShadowMapPass") 2143 seq = vtki.new("SequencePass") 2144 passes = vtki.new("RenderPassCollection") 2145 passes.AddItem(shadows.GetShadowMapBakerPass()) 2146 passes.AddItem(shadows) 2147 seq.SetPasses(passes) 2148 camerapass = vtki.new("CameraPass") 2149 camerapass.SetDelegatePass(seq) 2150 self.renderer.SetPass(camerapass) 2151 return self 2152 2153 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> Self: 2154 """ 2155 Screen Space Ambient Occlusion. 2156 2157 For every pixel on the screen, the pixel shader samples the depth values around 2158 the current pixel and tries to compute the amount of occlusion from each of the sampled 2159 points. 2160 2161 Arguments: 2162 radius : (float) 2163 radius of influence in absolute units 2164 bias : (float) 2165 bias of the normals 2166 blur : (bool) 2167 add a blurring to the sampled positions 2168 samples : (int) 2169 number of samples to probe 2170 2171 Examples: 2172 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2173 2174 ![](https://vedo.embl.es/images/basic/ssao.jpg) 2175 """ 2176 lights = vtki.new("LightsPass") 2177 2178 opaque = vtki.new("OpaquePass") 2179 2180 ssaoCam = vtki.new("CameraPass") 2181 ssaoCam.SetDelegatePass(opaque) 2182 2183 ssao = vtki.new("SSAOPass") 2184 ssao.SetRadius(radius) 2185 ssao.SetBias(bias) 2186 ssao.SetBlur(blur) 2187 ssao.SetKernelSize(samples) 2188 ssao.SetDelegatePass(ssaoCam) 2189 2190 translucent = vtki.new("TranslucentPass") 2191 2192 volpass = vtki.new("VolumetricPass") 2193 ddp = vtki.new("DualDepthPeelingPass") 2194 ddp.SetTranslucentPass(translucent) 2195 ddp.SetVolumetricPass(volpass) 2196 2197 over = vtki.new("OverlayPass") 2198 2199 collection = vtki.new("RenderPassCollection") 2200 collection.AddItem(lights) 2201 collection.AddItem(ssao) 2202 collection.AddItem(ddp) 2203 collection.AddItem(over) 2204 2205 sequence = vtki.new("SequencePass") 2206 sequence.SetPasses(collection) 2207 2208 cam = vtki.new("CameraPass") 2209 cam.SetDelegatePass(sequence) 2210 2211 self.renderer.SetPass(cam) 2212 return self 2213 2214 def add_depth_of_field(self, autofocus=True) -> Self: 2215 """Add a depth of field effect in the scene.""" 2216 lights = vtki.new("LightsPass") 2217 2218 opaque = vtki.new("OpaquePass") 2219 2220 dofCam = vtki.new("CameraPass") 2221 dofCam.SetDelegatePass(opaque) 2222 2223 dof = vtki.new("DepthOfFieldPass") 2224 dof.SetAutomaticFocalDistance(autofocus) 2225 dof.SetDelegatePass(dofCam) 2226 2227 collection = vtki.new("RenderPassCollection") 2228 collection.AddItem(lights) 2229 collection.AddItem(dof) 2230 2231 sequence = vtki.new("SequencePass") 2232 sequence.SetPasses(collection) 2233 2234 cam = vtki.new("CameraPass") 2235 cam.SetDelegatePass(sequence) 2236 2237 self.renderer.SetPass(cam) 2238 return self 2239 2240 def _add_skybox(self, hdrfile: str) -> Self: 2241 # many hdr files are at https://polyhaven.com/all 2242 2243 reader = vtki.new("HDRReader") 2244 # Check the image can be read. 2245 if not reader.CanReadFile(hdrfile): 2246 vedo.logger.error(f"Cannot read HDR file {hdrfile}") 2247 return self 2248 reader.SetFileName(hdrfile) 2249 reader.Update() 2250 2251 texture = vtki.vtkTexture() 2252 texture.SetColorModeToDirectScalars() 2253 texture.SetInputData(reader.GetOutput()) 2254 2255 # Convert to a cube map 2256 tcm = vtki.new("EquirectangularToCubeMapTexture") 2257 tcm.SetInputTexture(texture) 2258 # Enable mipmapping to handle HDR image 2259 tcm.MipmapOn() 2260 tcm.InterpolateOn() 2261 2262 self.renderer.SetEnvironmentTexture(tcm) 2263 self.renderer.UseImageBasedLightingOn() 2264 self.skybox = vtki.new("Skybox") 2265 self.skybox.SetTexture(tcm) 2266 self.renderer.AddActor(self.skybox) 2267 return self 2268 2269 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None) -> "vedo.addons.RendererFrame": 2270 """ 2271 Add a frame to the renderer subwindow. 2272 2273 Arguments: 2274 c : (color) 2275 color name or index 2276 alpha : (float) 2277 opacity level 2278 lw : (int) 2279 line width in pixels. 2280 padding : (float) 2281 padding space in pixels. 2282 """ 2283 if c is None: # automatic black or white 2284 c = (0.9, 0.9, 0.9) 2285 if self.renderer: 2286 if np.sum(self.renderer.GetBackground()) > 1.5: 2287 c = (0.1, 0.1, 0.1) 2288 renf = addons.RendererFrame(c, alpha, lw, padding) 2289 if renf: 2290 self.renderer.AddActor(renf) 2291 return renf 2292 2293 def add_hover_legend( 2294 self, 2295 at=None, 2296 c=None, 2297 pos="bottom-left", 2298 font="Calco", 2299 s=0.75, 2300 bg="auto", 2301 alpha=0.1, 2302 maxlength=24, 2303 use_info=False, 2304 ) -> int: 2305 """ 2306 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2307 2308 The created text object are stored in `plotter.hover_legends`. 2309 2310 Returns: 2311 the id of the callback function. 2312 2313 Arguments: 2314 c : (color) 2315 Text color. If None then black or white is chosen automatically 2316 pos : (str) 2317 text positioning 2318 font : (str) 2319 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2320 s : (float) 2321 text size scale 2322 bg : (color) 2323 background color of the 2D box containing the text 2324 alpha : (float) 2325 box transparency 2326 maxlength : (int) 2327 maximum number of characters per line 2328 use_info : (bool) 2329 visualize the content of the `obj.info` attribute 2330 2331 Examples: 2332 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2333 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2334 2335 ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg) 2336 """ 2337 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2338 2339 if at is None: 2340 at = self.renderers.index(self.renderer) 2341 2342 def _legfunc(evt): 2343 if not evt.object or not self.renderer or at != evt.at: 2344 if hoverlegend.mapper.GetInput(): # clear and return 2345 hoverlegend.mapper.SetInput("") 2346 self.render() 2347 return 2348 2349 if use_info: 2350 if hasattr(evt.object, "info"): 2351 t = str(evt.object.info) 2352 else: 2353 return 2354 else: 2355 t, tp = "", "" 2356 if evt.isMesh: 2357 tp = "Mesh " 2358 elif evt.isPoints: 2359 tp = "Points " 2360 elif evt.isVolume: 2361 tp = "Volume " 2362 elif evt.isImage: 2363 tp = "Image " 2364 elif evt.isAssembly: 2365 tp = "Assembly " 2366 else: 2367 return 2368 2369 if evt.isAssembly: 2370 if not evt.object.name: 2371 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2372 else: 2373 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2374 else: 2375 if evt.object.name: 2376 t += f"{tp}name" 2377 if evt.isPoints: 2378 t += " " 2379 if evt.isMesh: 2380 t += " " 2381 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2382 2383 if evt.object.filename: 2384 t += f"{tp}filename: " 2385 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2386 t += "\n" 2387 if not evt.object.file_size: 2388 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2389 if evt.object.file_size: 2390 t += " : " 2391 sz, created = evt.object.file_size, evt.object.created 2392 t += f"{created[4:-5]} ({sz})" + "\n" 2393 2394 if evt.isPoints: 2395 indata = evt.object.dataset 2396 if indata.GetNumberOfPoints(): 2397 t += ( 2398 f"#points/cells: {indata.GetNumberOfPoints()}" 2399 f" / {indata.GetNumberOfCells()}" 2400 ) 2401 pdata = indata.GetPointData() 2402 cdata = indata.GetCellData() 2403 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2404 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2405 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2406 t += " *" 2407 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2408 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2409 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2410 t += " *" 2411 2412 if evt.isImage: 2413 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2414 t += f"\nImage shape: {evt.object.shape}" 2415 pcol = self.color_picker(evt.picked2d) 2416 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2417 2418 # change box color if needed in 'auto' mode 2419 if evt.isPoints and "auto" in str(bg): 2420 actcol = evt.object.properties.GetColor() 2421 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2422 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2423 2424 # adapt to changes in bg color 2425 bgcol = self.renderers[at].GetBackground() 2426 _bgcol = c 2427 if _bgcol is None: # automatic black or white 2428 _bgcol = (0.9, 0.9, 0.9) 2429 if sum(bgcol) > 1.5: 2430 _bgcol = (0.1, 0.1, 0.1) 2431 if len(set(_bgcol).intersection(bgcol)) < 3: 2432 hoverlegend.color(_bgcol) 2433 2434 if hoverlegend.mapper.GetInput() != t: 2435 hoverlegend.mapper.SetInput(t) 2436 self.interactor.Render() 2437 2438 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2439 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2440 2441 self.add(hoverlegend, at=at) 2442 self.hover_legends.append(hoverlegend) 2443 idcall = self.add_callback("MouseMove", _legfunc) 2444 return idcall 2445 2446 def add_scale_indicator( 2447 self, 2448 pos=(0.7, 0.05), 2449 s=0.02, 2450 length=2, 2451 lw=4, 2452 c="k1", 2453 alpha=1, 2454 units="", 2455 gap=0.05, 2456 ) -> Union["vedo.visual.Actor2D", None]: 2457 """ 2458 Add a Scale Indicator. Only works in parallel mode (no perspective). 2459 2460 Arguments: 2461 pos : (list) 2462 fractional (x,y) position on the screen. 2463 s : (float) 2464 size of the text. 2465 length : (float) 2466 length of the line. 2467 units : (str) 2468 string to show units. 2469 gap : (float) 2470 separation of line and text. 2471 2472 Example: 2473 ```python 2474 from vedo import settings, Cube, Plotter 2475 settings.use_parallel_projection = True # or else it does not make sense! 2476 cube = Cube().alpha(0.2) 2477 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2478 plt.add_scale_indicator(units='um', c='blue4') 2479 plt.show(cube, "Scale indicator with units").close() 2480 ``` 2481 ![](https://vedo.embl.es/images/feats/scale_indicator.png) 2482 """ 2483 # Note that this cannot go in addons.py 2484 # because it needs callbacks and window size 2485 if not self.interactor: 2486 return None 2487 2488 ppoints = vtki.vtkPoints() # Generate the polyline 2489 psqr = [[0.0, gap], [length / 10, gap]] 2490 dd = psqr[1][0] - psqr[0][0] 2491 for i, pt in enumerate(psqr): 2492 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2493 lines = vtki.vtkCellArray() 2494 lines.InsertNextCell(len(psqr)) 2495 for i in range(len(psqr)): 2496 lines.InsertCellPoint(i) 2497 pd = vtki.vtkPolyData() 2498 pd.SetPoints(ppoints) 2499 pd.SetLines(lines) 2500 2501 wsx, wsy = self.window.GetSize() 2502 if not self.camera.GetParallelProjection(): 2503 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2504 return None 2505 2506 rlabel = vtki.new("VectorText") 2507 rlabel.SetText("scale") 2508 tf = vtki.new("TransformPolyDataFilter") 2509 tf.SetInputConnection(rlabel.GetOutputPort()) 2510 t = vtki.vtkTransform() 2511 t.Scale(s * wsy / wsx, s, 1) 2512 tf.SetTransform(t) 2513 2514 app = vtki.new("AppendPolyData") 2515 app.AddInputConnection(tf.GetOutputPort()) 2516 app.AddInputData(pd) 2517 2518 mapper = vtki.new("PolyDataMapper2D") 2519 mapper.SetInputConnection(app.GetOutputPort()) 2520 cs = vtki.vtkCoordinate() 2521 cs.SetCoordinateSystem(1) 2522 mapper.SetTransformCoordinate(cs) 2523 2524 fractor = vedo.visual.Actor2D() 2525 csys = fractor.GetPositionCoordinate() 2526 csys.SetCoordinateSystem(3) 2527 fractor.SetPosition(pos) 2528 fractor.SetMapper(mapper) 2529 fractor.GetProperty().SetColor(vedo.get_color(c)) 2530 fractor.GetProperty().SetOpacity(alpha) 2531 fractor.GetProperty().SetLineWidth(lw) 2532 fractor.GetProperty().SetDisplayLocationToForeground() 2533 2534 def sifunc(iren, ev): 2535 wsx, wsy = self.window.GetSize() 2536 ps = self.camera.GetParallelScale() 2537 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2538 if units: 2539 newtxt += " " + units 2540 if rlabel.GetText() != newtxt: 2541 rlabel.SetText(newtxt) 2542 2543 self.renderer.AddActor(fractor) 2544 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2545 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2546 self.interactor.AddObserver("InteractionEvent", sifunc) 2547 sifunc(0, 0) 2548 return fractor 2549 2550 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2551 """ 2552 Create an Event object with information of what was clicked. 2553 2554 If `enable_picking` is False, no picking will be performed. 2555 This can be useful to avoid double picking when using buttons. 2556 """ 2557 if not self.interactor: 2558 return Event() 2559 2560 if len(pos) > 0: 2561 x, y = pos 2562 self.interactor.SetEventPosition(pos) 2563 else: 2564 x, y = self.interactor.GetEventPosition() 2565 self.renderer = self.interactor.FindPokedRenderer(x, y) 2566 2567 self.picked2d = (x, y) 2568 2569 key = self.interactor.GetKeySym() 2570 2571 if key: 2572 if "_L" in key or "_R" in key: 2573 # skip things like Shift_R 2574 key = "" # better than None 2575 else: 2576 if self.interactor.GetShiftKey(): 2577 key = key.upper() 2578 2579 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2580 key = "underscore" 2581 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2582 key = "plus" 2583 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2584 key = "?" 2585 2586 if self.interactor.GetControlKey(): 2587 key = "Ctrl+" + key 2588 2589 if self.interactor.GetAltKey(): 2590 key = "Alt+" + key 2591 2592 if enable_picking: 2593 if not self.picker: 2594 self.picker = vtki.vtkPropPicker() 2595 2596 self.picker.PickProp(x, y, self.renderer) 2597 actor = self.picker.GetProp3D() 2598 # Note that GetProp3D already picks Assembly 2599 2600 xp, yp = self.interactor.GetLastEventPosition() 2601 dx, dy = x - xp, y - yp 2602 2603 delta3d = np.array([0, 0, 0]) 2604 2605 if actor: 2606 picked3d = np.array(self.picker.GetPickPosition()) 2607 2608 try: 2609 vobj = actor.retrieve_object() 2610 old_pt = np.asarray(vobj.picked3d) 2611 vobj.picked3d = picked3d 2612 delta3d = picked3d - old_pt 2613 except (AttributeError, TypeError): 2614 pass 2615 2616 else: 2617 picked3d = None 2618 2619 if not actor: # try 2D 2620 actor = self.picker.GetActor2D() 2621 2622 event = Event() 2623 event.name = ename 2624 event.title = self.title 2625 event.id = -1 # will be set by the timer wrapper function 2626 event.timerid = -1 # will be set by the timer wrapper function 2627 event.priority = -1 # will be set by the timer wrapper function 2628 event.time = time.time() 2629 event.at = self.renderers.index(self.renderer) 2630 event.keypress = key 2631 if enable_picking: 2632 try: 2633 event.object = actor.retrieve_object() 2634 except AttributeError: 2635 event.object = actor 2636 try: 2637 event.actor = actor.retrieve_object() # obsolete use object instead 2638 except AttributeError: 2639 event.actor = actor 2640 event.picked3d = picked3d 2641 event.picked2d = (x, y) 2642 event.delta2d = (dx, dy) 2643 event.angle2d = np.arctan2(dy, dx) 2644 event.speed2d = np.sqrt(dx * dx + dy * dy) 2645 event.delta3d = delta3d 2646 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2647 event.isPoints = isinstance(event.object, vedo.Points) 2648 event.isMesh = isinstance(event.object, vedo.Mesh) 2649 event.isAssembly = isinstance(event.object, vedo.Assembly) 2650 event.isVolume = isinstance(event.object, vedo.Volume) 2651 event.isImage = isinstance(event.object, vedo.Image) 2652 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2653 return event 2654 2655 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2656 """ 2657 Add a function to be executed while show() is active. 2658 2659 Return a unique id for the callback. 2660 2661 The callback function (see example below) exposes a dictionary 2662 with the following information: 2663 - `name`: event name, 2664 - `id`: event unique identifier, 2665 - `priority`: event priority (float), 2666 - `interactor`: the interactor object, 2667 - `at`: renderer nr. where the event occurred 2668 - `keypress`: key pressed as string 2669 - `actor`: object picked by the mouse 2670 - `picked3d`: point picked in world coordinates 2671 - `picked2d`: screen coords of the mouse pointer 2672 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2673 - `delta3d`: ...same but in 3D world coords 2674 - `angle2d`: angle of mouse movement on screen 2675 - `speed2d`: speed of mouse movement on screen 2676 - `speed3d`: speed of picked point in world coordinates 2677 - `isPoints`: True if of class 2678 - `isMesh`: True if of class 2679 - `isAssembly`: True if of class 2680 - `isVolume`: True if of class Volume 2681 - `isImage`: True if of class 2682 2683 If `enable_picking` is False, no picking will be performed. 2684 This can be useful to avoid double picking when using buttons. 2685 2686 Frequently used events are: 2687 - `KeyPress`, `KeyRelease`: listen to keyboard events 2688 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2689 - `MiddleButtonPress`, `MiddleButtonRelease` 2690 - `RightButtonPress`, `RightButtonRelease` 2691 - `MouseMove`: listen to mouse pointer changing position 2692 - `MouseWheelForward`, `MouseWheelBackward` 2693 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2694 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2695 - `ResetCamera`, `ResetCameraClippingRange` 2696 - `Error`, `Warning` 2697 - `Char` 2698 - `Timer` 2699 2700 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2701 2702 Example: 2703 ```python 2704 from vedo import * 2705 2706 def func(evt): 2707 # this function is called every time the mouse moves 2708 # (evt is a dotted dictionary) 2709 if not evt.object: 2710 return # no hit, return 2711 print("point coords =", evt.picked3d) 2712 # print(evt) # full event dump 2713 2714 elli = Ellipsoid() 2715 plt = Plotter(axes=1) 2716 plt.add_callback('mouse hovering', func) 2717 plt.show(elli).close() 2718 ``` 2719 2720 Examples: 2721 - [spline_draw1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw1.py) 2722 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2723 2724 ![](https://vedo.embl.es/images/advanced/spline_draw.png) 2725 2726 - ..and many others! 2727 """ 2728 from vtkmodules.util.misc import calldata_type 2729 2730 if not self.interactor: 2731 return 0 2732 2733 if vedo.settings.dry_run_mode >= 1: 2734 return 0 2735 2736 ######################################### 2737 @calldata_type(vtki.VTK_INT) 2738 def _func_wrap(iren, ename, timerid=None): 2739 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2740 event.timerid = timerid 2741 event.id = cid 2742 event.priority = priority 2743 self.last_event = event 2744 func(event) 2745 2746 ######################################### 2747 2748 event_name = utils.get_vtk_name_event(event_name) 2749 2750 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2751 # print(f"Registering event: {event_name} with id={cid}") 2752 return cid 2753 2754 def remove_callback(self, cid: Union[int, str]) -> Self: 2755 """ 2756 Remove a callback function by its id 2757 or a whole category of callbacks by their name. 2758 2759 Arguments: 2760 cid : (int, str) 2761 Unique id of the callback. 2762 If an event name is passed all callbacks of that type are removed. 2763 """ 2764 if self.interactor: 2765 if isinstance(cid, str): 2766 cid = utils.get_vtk_name_event(cid) 2767 self.interactor.RemoveObservers(cid) 2768 else: 2769 self.interactor.RemoveObserver(cid) 2770 return self 2771 2772 def remove_all_observers(self) -> Self: 2773 """ 2774 Remove all observers. 2775 2776 Example: 2777 ```python 2778 from vedo import * 2779 2780 def kfunc(event): 2781 print("Key pressed:", event.keypress) 2782 if event.keypress == 'q': 2783 plt.close() 2784 2785 def rfunc(event): 2786 if event.isImage: 2787 printc("Right-clicked!", event) 2788 plt.render() 2789 2790 img = Image(dataurl+"images/embryo.jpg") 2791 2792 plt = Plotter(size=(1050, 600)) 2793 plt.parallel_projection(True) 2794 plt.remove_all_observers() 2795 plt.add_callback("key press", kfunc) 2796 plt.add_callback("mouse right click", rfunc) 2797 plt.show("Right-Click Me! Press q to exit.", img) 2798 plt.close() 2799 ``` 2800 """ 2801 if self.interactor: 2802 self.interactor.RemoveAllObservers() 2803 return self 2804 2805 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2806 """ 2807 Start or stop an existing timer. 2808 2809 Arguments: 2810 action : (str) 2811 Either "create"/"start" or "destroy"/"stop" 2812 timer_id : (int) 2813 When stopping the timer, the ID of the timer as returned when created 2814 dt : (int) 2815 time in milliseconds between each repeated call 2816 one_shot : (bool) 2817 create a one shot timer of prescribed duration instead of a repeating one 2818 2819 Examples: 2820 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2821 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2822 2823 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 2824 """ 2825 if action in ("create", "start"): 2826 if timer_id is not None: 2827 vedo.logger.warning("you set a timer_id but it will be ignored.") 2828 if one_shot: 2829 timer_id = self.interactor.CreateOneShotTimer(dt) 2830 else: 2831 timer_id = self.interactor.CreateRepeatingTimer(dt) 2832 return timer_id 2833 2834 elif action in ("destroy", "stop"): 2835 if timer_id is not None: 2836 self.interactor.DestroyTimer(timer_id) 2837 else: 2838 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2839 else: 2840 e = f"in timer_callback(). Cannot understand action: {action}\n" 2841 e += " allowed actions are: ['start', 'stop']. Skipped." 2842 vedo.logger.error(e) 2843 return timer_id 2844 2845 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2846 """ 2847 Add a callback function that will be called when an event occurs. 2848 Consider using `add_callback()` instead. 2849 """ 2850 if not self.interactor: 2851 return -1 2852 event_name = utils.get_vtk_name_event(event_name) 2853 idd = self.interactor.AddObserver(event_name, func, priority) 2854 return idd 2855 2856 def compute_world_coordinate( 2857 self, 2858 pos2d: MutableSequence[float], 2859 at=None, 2860 objs=(), 2861 bounds=(), 2862 offset=None, 2863 pixeltol=None, 2864 worldtol=None, 2865 ) -> np.ndarray: 2866 """ 2867 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2868 If a set of meshes is passed then points are placed onto these. 2869 2870 Arguments: 2871 pos2d : (list) 2872 2D screen coordinates point. 2873 at : (int) 2874 renderer number. 2875 objs : (list) 2876 list of Mesh objects to project the point onto. 2877 bounds : (list) 2878 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2879 offset : (float) 2880 specify an offset value. 2881 pixeltol : (int) 2882 screen tolerance in pixels. 2883 worldtol : (float) 2884 world coordinates tolerance. 2885 2886 Returns: 2887 numpy array, the point in 3D world coordinates. 2888 2889 Examples: 2890 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2891 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2892 2893 ![](https://vedo.embl.es/images/basic/mousehover3.jpg) 2894 """ 2895 if at is not None: 2896 renderer = self.renderers[at] 2897 else: 2898 renderer = self.renderer 2899 2900 if not objs: 2901 pp = vtki.vtkFocalPlanePointPlacer() 2902 else: 2903 pps = vtki.vtkPolygonalSurfacePointPlacer() 2904 for ob in objs: 2905 pps.AddProp(ob.actor) 2906 pp = pps # type: ignore 2907 2908 if len(bounds) == 6: 2909 pp.SetPointBounds(bounds) 2910 if pixeltol: 2911 pp.SetPixelTolerance(pixeltol) 2912 if worldtol: 2913 pp.SetWorldTolerance(worldtol) 2914 if offset: 2915 pp.SetOffset(offset) 2916 2917 worldPos: MutableSequence[float] = [0, 0, 0] 2918 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2919 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2920 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2921 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2922 return np.array(worldPos) 2923 2924 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2925 """ 2926 Given a 3D points in the current renderer (or full window), 2927 find the screen pixel coordinates. 2928 2929 Example: 2930 ```python 2931 from vedo import * 2932 2933 elli = Ellipsoid().point_size(5) 2934 2935 plt = Plotter() 2936 plt.show(elli, "Press q to continue and print the info") 2937 2938 xyscreen = plt.compute_screen_coordinates(elli) 2939 print('xyscreen coords:', xyscreen) 2940 2941 # simulate an event happening at one point 2942 event = plt.fill_event(pos=xyscreen[123]) 2943 print(event) 2944 ``` 2945 """ 2946 try: 2947 obj = obj.vertices 2948 except AttributeError: 2949 pass 2950 2951 if utils.is_sequence(obj): 2952 pts = obj 2953 p2d = [] 2954 cs = vtki.vtkCoordinate() 2955 cs.SetCoordinateSystemToWorld() 2956 cs.SetViewport(self.renderer) 2957 for p in pts: 2958 cs.SetValue(p) 2959 if full_window: 2960 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2961 else: 2962 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2963 return np.array(p2d, dtype=int) 2964 2965 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2966 """ 2967 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2968 2969 Returns a frustum Mesh that contains the visible field of view. 2970 This can be used to select objects in a scene or select vertices. 2971 2972 Example: 2973 ```python 2974 from vedo import * 2975 2976 settings.enable_default_mouse_callbacks = False 2977 2978 def mode_select(objs): 2979 print("Selected objects:", objs) 2980 d0 = mode.start_x, mode.start_y # display coords 2981 d1 = mode.end_x, mode.end_y 2982 2983 frustum = plt.pick_area(d0, d1) 2984 col = np.random.randint(0, 10) 2985 infru = frustum.inside_points(mesh) 2986 infru.point_size(10).color(col) 2987 plt.add(frustum, infru).render() 2988 2989 mesh = Mesh(dataurl+"cow.vtk") 2990 mesh.color("k5").linewidth(1) 2991 2992 mode = interactor_modes.BlenderStyle() 2993 mode.callback_select = mode_select 2994 2995 plt = Plotter().user_mode(mode) 2996 plt.show(mesh, axes=1) 2997 ``` 2998 """ 2999 if at is not None: 3000 ren = self.renderers[at] 3001 else: 3002 ren = self.renderer 3003 area_picker = vtki.vtkAreaPicker() 3004 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 3005 planes = area_picker.GetFrustum() 3006 3007 fru = vtki.new("FrustumSource") 3008 fru.SetPlanes(planes) 3009 fru.ShowLinesOff() 3010 fru.Update() 3011 3012 afru = vedo.Mesh(fru.GetOutput()) 3013 afru.alpha(0.1).lw(1).pickable(False) 3014 afru.name = "Frustum" 3015 return afru 3016 3017 def _scan_input_return_acts(self, objs) -> Any: 3018 # scan the input and return a list of actors 3019 if not utils.is_sequence(objs): 3020 objs = [objs] 3021 3022 ################# 3023 wannabe_acts = [] 3024 for a in objs: 3025 3026 try: 3027 wannabe_acts.append(a.actor) 3028 except AttributeError: 3029 wannabe_acts.append(a) # already actor 3030 3031 try: 3032 wannabe_acts.append(a.scalarbar) 3033 except AttributeError: 3034 pass 3035 3036 try: 3037 for sh in a.shadows: 3038 wannabe_acts.append(sh.actor) 3039 except AttributeError: 3040 pass 3041 3042 try: 3043 wannabe_acts.append(a.trail.actor) 3044 if a.trail.shadows: # trails may also have shadows 3045 for sh in a.trail.shadows: 3046 wannabe_acts.append(sh.actor) 3047 except AttributeError: 3048 pass 3049 3050 ################# 3051 scanned_acts = [] 3052 for a in wannabe_acts: # scan content of list 3053 3054 if a is None: 3055 pass 3056 3057 elif isinstance(a, (vtki.vtkActor, vtki.vtkActor2D)): 3058 scanned_acts.append(a) 3059 3060 elif isinstance(a, str): 3061 # assume a 2D comment was given 3062 changed = False # check if one already exists so to just update text 3063 if self.renderer: # might be jupyter 3064 acs = self.renderer.GetActors2D() 3065 acs.InitTraversal() 3066 for i in range(acs.GetNumberOfItems()): 3067 act = acs.GetNextItem() 3068 if isinstance(act, vedo.shapes.Text2D): 3069 aposx, aposy = act.GetPosition() 3070 if aposx < 0.01 and aposy > 0.99: # "top-left" 3071 act.text(a) # update content! no appending nada 3072 changed = True 3073 break 3074 if not changed: 3075 out = vedo.shapes.Text2D(a) # append a new one 3076 scanned_acts.append(out) 3077 # scanned_acts.append(vedo.shapes.Text2D(a)) # naive version 3078 3079 elif isinstance(a, vtki.vtkPolyData): 3080 scanned_acts.append(vedo.Mesh(a).actor) 3081 3082 elif isinstance(a, vtki.vtkImageData): 3083 scanned_acts.append(vedo.Volume(a).actor) 3084 3085 elif isinstance(a, vedo.RectilinearGrid): 3086 scanned_acts.append(a.actor) 3087 3088 elif isinstance(a, vedo.StructuredGrid): 3089 scanned_acts.append(a.actor) 3090 3091 elif isinstance(a, vtki.vtkLight): 3092 scanned_acts.append(a) 3093 3094 elif isinstance(a, vedo.visual.LightKit): 3095 a.lightkit.AddLightsToRenderer(self.renderer) 3096 3097 elif isinstance(a, vtki.get_class("MultiBlockDataSet")): 3098 for i in range(a.GetNumberOfBlocks()): 3099 b = a.GetBlock(i) 3100 if isinstance(b, vtki.vtkPolyData): 3101 scanned_acts.append(vedo.Mesh(b).actor) 3102 elif isinstance(b, vtki.vtkImageData): 3103 scanned_acts.append(vedo.Volume(b).actor) 3104 3105 elif isinstance(a, (vtki.vtkProp, vtki.vtkInteractorObserver)): 3106 scanned_acts.append(a) 3107 3108 elif "trimesh" in str(type(a)): 3109 scanned_acts.append(utils.trimesh2vedo(a)) 3110 3111 elif "meshlab" in str(type(a)): 3112 if "MeshSet" in str(type(a)): 3113 for i in range(a.number_meshes()): 3114 if a.mesh_id_exists(i): 3115 scanned_acts.append(utils.meshlab2vedo(a.mesh(i))) 3116 else: 3117 scanned_acts.append(utils.meshlab2vedo(a)) 3118 3119 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 3120 import vedo.dolfin as vdlf 3121 3122 scanned_acts.append(vdlf.IMesh(a).actor) 3123 3124 elif "madcad" in str(type(a)): 3125 scanned_acts.append(utils.madcad2vedo(a).actor) 3126 3127 elif "TetgenIO" in str(type(a)): 3128 scanned_acts.append(vedo.TetMesh(a).shrink(0.9).c("pink7").actor) 3129 3130 elif "matplotlib.figure.Figure" in str(type(a)): 3131 scanned_acts.append(vedo.Image(a).clone2d("top-right", 0.6)) 3132 3133 else: 3134 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 3135 3136 return scanned_acts 3137 3138 def show( 3139 self, 3140 *objects, 3141 at=None, 3142 axes=None, 3143 resetcam=None, 3144 zoom=False, 3145 interactive=None, 3146 viewup="", 3147 azimuth=0.0, 3148 elevation=0.0, 3149 roll=0.0, 3150 camera=None, 3151 mode=None, 3152 rate=None, 3153 bg=None, 3154 bg2=None, 3155 size=None, 3156 title=None, 3157 screenshot="", 3158 ) -> Any: 3159 """ 3160 Render a list of objects. 3161 3162 Arguments: 3163 at : (int) 3164 number of the renderer to plot to, in case of more than one exists 3165 3166 axes : (int) 3167 axis type-1 can be fully customized by passing a dictionary. 3168 Check `addons.Axes()` for the full list of options. 3169 set the type of axes to be shown: 3170 - 0, no axes 3171 - 1, draw three gray grid walls 3172 - 2, show cartesian axes from (0,0,0) 3173 - 3, show positive range of cartesian axes from (0,0,0) 3174 - 4, show a triad at bottom left 3175 - 5, show a cube at bottom left 3176 - 6, mark the corners of the bounding box 3177 - 7, draw a 3D ruler at each side of the cartesian axes 3178 - 8, show the `vtkCubeAxesActor` object 3179 - 9, show the bounding box outLine 3180 - 10, show three circles representing the maximum bounding box 3181 - 11, show a large grid on the x-y plane 3182 - 12, show polar axes 3183 - 13, draw a simple ruler at the bottom of the window 3184 3185 azimuth/elevation/roll : (float) 3186 move camera accordingly the specified value 3187 3188 viewup: str, list 3189 either `['x', 'y', 'z']` or a vector to set vertical direction 3190 3191 resetcam : (bool) 3192 re-adjust camera position to fit objects 3193 3194 camera : (dict, vtkCamera) 3195 camera parameters can further be specified with a dictionary 3196 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3197 - pos, `(list)`, the position of the camera in world coordinates 3198 - focal_point `(list)`, the focal point of the camera in world coordinates 3199 - viewup `(list)`, the view up direction for the camera 3200 - distance `(float)`, set the focal point to the specified distance from the camera position. 3201 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3202 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3203 in world-coordinate distances. The default is 1. 3204 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3205 This method has no effect in perspective projection mode. 3206 3207 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3208 plane to be set a distance 'thickness' beyond the near clipping plane. 3209 3210 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3211 measured in degrees. The default angle is 30 degrees. 3212 This method has no effect in parallel projection mode. 3213 The formula for setting the angle up for perfect perspective viewing is: 3214 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3215 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3216 3217 interactive : (bool) 3218 pause and interact with window (True) or continue execution (False) 3219 3220 rate : (float) 3221 maximum rate of `show()` in Hertz 3222 3223 mode : (int, str) 3224 set the type of interaction: 3225 - 0 = TrackballCamera [default] 3226 - 1 = TrackballActor 3227 - 2 = JoystickCamera 3228 - 3 = JoystickActor 3229 - 4 = Flight 3230 - 5 = RubberBand2D 3231 - 6 = RubberBand3D 3232 - 7 = RubberBandZoom 3233 - 8 = Terrain 3234 - 9 = Unicam 3235 - 10 = Image 3236 - Check out `vedo.interaction_modes` for more options. 3237 3238 bg : (str, list) 3239 background color in RGB format, or string name 3240 3241 bg2 : (str, list) 3242 second background color to create a gradient background 3243 3244 size : (str, list) 3245 size of the window, e.g. size="fullscreen", or size=[600,400] 3246 3247 title : (str) 3248 window title text 3249 3250 screenshot : (str) 3251 save a screenshot of the window to file 3252 """ 3253 3254 if vedo.settings.dry_run_mode >= 2: 3255 return self 3256 3257 if self.wx_widget: 3258 return self 3259 3260 if self.renderers: # in case of notebooks 3261 3262 if at is None: 3263 at = self.renderers.index(self.renderer) 3264 3265 else: 3266 3267 if at >= len(self.renderers): 3268 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3269 vedo.logger.error(t) 3270 return self 3271 3272 self.renderer = self.renderers[at] 3273 3274 if title is not None: 3275 self.title = title 3276 3277 if size is not None: 3278 self.size = size 3279 if self.size[0] == "f": # full screen 3280 self.size = "fullscreen" 3281 self.window.SetFullScreen(True) 3282 self.window.BordersOn() 3283 else: 3284 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3285 3286 if vedo.settings.default_backend == "vtk": 3287 if str(bg).endswith(".hdr"): 3288 self._add_skybox(bg) 3289 else: 3290 if bg is not None: 3291 self.backgrcol = vedo.get_color(bg) 3292 self.renderer.SetBackground(self.backgrcol) 3293 if bg2 is not None: 3294 self.renderer.GradientBackgroundOn() 3295 self.renderer.SetBackground2(vedo.get_color(bg2)) 3296 3297 if axes is not None: 3298 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3299 objects = list(objects) 3300 objects.append(axes) # move it into the list of normal things to show 3301 axes = 0 3302 self.axes = axes 3303 3304 if interactive is not None: 3305 self._interactive = interactive 3306 if self.offscreen: 3307 self._interactive = False 3308 3309 # camera stuff 3310 if resetcam is not None: 3311 self.resetcam = resetcam 3312 3313 if camera is not None: 3314 self.resetcam = False 3315 viewup = "" 3316 if isinstance(camera, vtki.vtkCamera): 3317 cameracopy = vtki.vtkCamera() 3318 cameracopy.DeepCopy(camera) 3319 self.camera = cameracopy 3320 else: 3321 self.camera = utils.camera_from_dict(camera) 3322 3323 self.add(objects) 3324 3325 # Backend ############################################################### 3326 if vedo.settings.default_backend in ["k3d"]: 3327 return backends.get_notebook_backend(self.objects) 3328 ######################################################################### 3329 3330 for ia in utils.flatten(objects): 3331 try: 3332 # fix gray color labels and title to white or black 3333 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3334 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3335 c = (0.9, 0.9, 0.9) 3336 if np.sum(self.renderer.GetBackground()) > 1.5: 3337 c = (0.1, 0.1, 0.1) 3338 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3339 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3340 except AttributeError: 3341 pass 3342 3343 if self.sharecam: 3344 for r in self.renderers: 3345 r.SetActiveCamera(self.camera) 3346 3347 if self.axes is not None: 3348 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3349 bns = self.renderer.ComputeVisiblePropBounds() 3350 addons.add_global_axes(self.axes, bounds=bns) 3351 3352 # Backend ############################################################### 3353 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3354 return backends.get_notebook_backend() 3355 ######################################################################### 3356 3357 if self.resetcam: 3358 self.renderer.ResetCamera() 3359 3360 if len(self.renderers) > 1: 3361 self.add_renderer_frame() 3362 3363 if vedo.settings.default_backend == "2d" and not zoom: 3364 zoom = "tightest" 3365 3366 if zoom: 3367 if zoom == "tight": 3368 self.reset_camera(tight=0.04) 3369 elif zoom == "tightest": 3370 self.reset_camera(tight=0.0001) 3371 else: 3372 self.camera.Zoom(zoom) 3373 if elevation: 3374 self.camera.Elevation(elevation) 3375 if azimuth: 3376 self.camera.Azimuth(azimuth) 3377 if roll: 3378 self.camera.Roll(roll) 3379 3380 if len(viewup) > 0: 3381 b = self.renderer.ComputeVisiblePropBounds() 3382 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3383 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3384 if viewup == "x": 3385 sz = np.linalg.norm(sz) 3386 self.camera.SetViewUp([1, 0, 0]) 3387 self.camera.SetPosition(cm + sz) 3388 elif viewup == "y": 3389 sz = np.linalg.norm(sz) 3390 self.camera.SetViewUp([0, 1, 0]) 3391 self.camera.SetPosition(cm + sz) 3392 elif viewup == "z": 3393 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3394 self.camera.SetViewUp([0, 0, 1]) 3395 self.camera.SetPosition(cm + 2 * sz) 3396 elif utils.is_sequence(viewup): 3397 sz = np.linalg.norm(sz) 3398 self.camera.SetViewUp(viewup) 3399 cpos = np.cross([0, 1, 0], viewup) 3400 self.camera.SetPosition(cm - 2 * sz * cpos) 3401 3402 self.renderer.ResetCameraClippingRange() 3403 3404 self.initialize_interactor() 3405 3406 if vedo.settings.immediate_rendering: 3407 self.window.Render() ##################### <-------------- Render 3408 3409 if self.interactor: # can be offscreen or not the vtk backend.. 3410 3411 self.window.SetWindowName(self.title) 3412 3413 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3414 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3415 # print(pic.dataset)# Array 0 name PNGImage 3416 # self.window.SetIcon(pic.dataset) 3417 3418 try: 3419 # Needs "pip install pyobjc" on Mac OSX 3420 if ( 3421 self._cocoa_initialized is False 3422 and "Darwin" in vedo.sys_platform 3423 and not self.offscreen 3424 ): 3425 self._cocoa_initialized = True 3426 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3427 pid = os.getpid() 3428 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3429 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3430 except: 3431 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3432 pass 3433 3434 # Set the interaction style 3435 if mode is not None: 3436 self.user_mode(mode) 3437 if self.qt_widget and mode is None: 3438 self.user_mode(0) 3439 3440 if screenshot: 3441 self.screenshot(screenshot) 3442 3443 if self._interactive: 3444 self.interactor.Start() 3445 if self._must_close_now: 3446 self.interactor.GetRenderWindow().Finalize() 3447 self.interactor.TerminateApp() 3448 self.camera = None 3449 self.renderer = None 3450 self.renderers = [] 3451 self.window = None 3452 self.interactor = None 3453 return self 3454 3455 if rate: 3456 if self.clock is None: # set clock and limit rate 3457 self._clockt0 = time.time() 3458 self.clock = 0.0 3459 else: 3460 t = time.time() - self._clockt0 3461 elapsed = t - self.clock 3462 mint = 1.0 / rate 3463 if elapsed < mint: 3464 time.sleep(mint - elapsed) 3465 self.clock = time.time() - self._clockt0 3466 3467 # 2d #################################################################### 3468 if vedo.settings.default_backend == "2d": 3469 return backends.get_notebook_backend() 3470 ######################################################################### 3471 3472 return self 3473 3474 3475 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3476 """Add a draggable inset space into a renderer. 3477 3478 Arguments: 3479 at : (int) 3480 specify the renderer number 3481 pos : (list) 3482 icon position in the range [1-4] indicating one of the 4 corners, 3483 or it can be a tuple (x,y) as a fraction of the renderer size. 3484 size : (float) 3485 size of the square inset 3486 draggable : (bool) 3487 if True the subrenderer space can be dragged around 3488 c : (color) 3489 color of the inset frame when dragged 3490 3491 Examples: 3492 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3493 3494 ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg) 3495 """ 3496 if not self.interactor: 3497 return None 3498 3499 if not self.renderer: 3500 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3501 return None 3502 3503 options = dict(options) 3504 pos = options.pop("pos", 0) 3505 size = options.pop("size", 0.1) 3506 c = options.pop("c", "lb") 3507 at = options.pop("at", None) 3508 draggable = options.pop("draggable", True) 3509 3510 r, g, b = vedo.get_color(c) 3511 widget = vtki.vtkOrientationMarkerWidget() 3512 widget.SetOutlineColor(r, g, b) 3513 if len(objects) == 1: 3514 widget.SetOrientationMarker(objects[0].actor) 3515 else: 3516 widget.SetOrientationMarker(vedo.Assembly(objects)) 3517 3518 widget.SetInteractor(self.interactor) 3519 3520 if utils.is_sequence(pos): 3521 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3522 else: 3523 if pos < 2: 3524 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3525 elif pos == 2: 3526 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3527 elif pos == 3: 3528 widget.SetViewport(0, 0, size * 2, size * 2) 3529 elif pos == 4: 3530 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3531 widget.EnabledOn() 3532 widget.SetInteractive(draggable) 3533 if at is not None and at < len(self.renderers): 3534 widget.SetCurrentRenderer(self.renderers[at]) 3535 else: 3536 widget.SetCurrentRenderer(self.renderer) 3537 self.widgets.append(widget) 3538 return widget 3539 3540 def clear(self, at=None, deep=False) -> Self: 3541 """Clear the scene from all meshes and volumes.""" 3542 if at is not None: 3543 renderer = self.renderers[at] 3544 else: 3545 renderer = self.renderer 3546 if not renderer: 3547 return self 3548 3549 if deep: 3550 renderer.RemoveAllViewProps() 3551 else: 3552 for ob in set( 3553 self.get_meshes() 3554 + self.get_volumes() 3555 + self.objects 3556 + self.axes_instances 3557 ): 3558 if isinstance(ob, vedo.shapes.Text2D): 3559 continue 3560 self.remove(ob) 3561 try: 3562 if ob.scalarbar: 3563 self.remove(ob.scalarbar) 3564 except AttributeError: 3565 pass 3566 return self 3567 3568 def break_interaction(self) -> Self: 3569 """Break window interaction and return to the python execution flow""" 3570 if self.interactor: 3571 self.check_actors_trasform() 3572 self.interactor.ExitCallback() 3573 return self 3574 3575 def freeze(self, value=True) -> Self: 3576 """Freeze the current renderer. Use this with `sharecam=False`.""" 3577 if not self.interactor: 3578 return self 3579 if not self.renderer: 3580 return self 3581 self.renderer.SetInteractive(not value) 3582 return self 3583 3584 def user_mode(self, mode) -> Self: 3585 """ 3586 Modify the user interaction mode. 3587 3588 Examples: 3589 ```python 3590 from vedo import * 3591 mode = interactor_modes.MousePan() 3592 mesh = Mesh(dataurl+"cow.vtk") 3593 plt = Plotter().user_mode(mode) 3594 plt.show(mesh, axes=1) 3595 ``` 3596 See also: 3597 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3598 """ 3599 if not self.interactor: 3600 return self 3601 3602 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3603 # print("Current style:", curr_style) 3604 if curr_style.endswith("Actor"): 3605 self.check_actors_trasform() 3606 3607 if isinstance(mode, (str, int)): 3608 # Set the style of interaction 3609 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3610 if mode in (0, "TrackballCamera"): 3611 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3612 self.interactor.RemoveObservers("CharEvent") 3613 elif mode in (1, "TrackballActor"): 3614 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3615 elif mode in (2, "JoystickCamera"): 3616 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3617 elif mode in (3, "JoystickActor"): 3618 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3619 elif mode in (4, "Flight"): 3620 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3621 elif mode in (5, "RubberBand2D"): 3622 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3623 elif mode in (6, "RubberBand3D"): 3624 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3625 elif mode in (7, "RubberBandZoom"): 3626 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3627 elif mode in (8, "Terrain"): 3628 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3629 elif mode in (9, "Unicam"): 3630 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3631 elif mode in (10, "Image", "image", "2d"): 3632 astyle = vtki.new("InteractorStyleImage") 3633 astyle.SetInteractionModeToImage3D() 3634 self.interactor.SetInteractorStyle(astyle) 3635 else: 3636 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3637 3638 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3639 # set a custom interactor style 3640 if hasattr(mode, "interactor"): 3641 mode.interactor = self.interactor 3642 mode.renderer = self.renderer # type: ignore 3643 mode.SetInteractor(self.interactor) 3644 mode.SetDefaultRenderer(self.renderer) 3645 self.interactor.SetInteractorStyle(mode) 3646 3647 return self 3648 3649 def close(self) -> Self: 3650 """Close the plotter.""" 3651 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3652 vedo.last_figure = None 3653 self.last_event = None 3654 self.sliders = [] 3655 self.buttons = [] 3656 self.widgets = [] 3657 self.hover_legends = [] 3658 self.background_renderer = None 3659 self._extralight = None 3660 3661 self.hint_widget = None 3662 self.cutter_widget = None 3663 3664 if vedo.settings.dry_run_mode >= 2: 3665 return self 3666 3667 if not hasattr(self, "window"): 3668 return self 3669 if not self.window: 3670 return self 3671 if not hasattr(self, "interactor"): 3672 return self 3673 if not self.interactor: 3674 return self 3675 3676 ################################################### 3677 try: 3678 if "Darwin" in vedo.sys_platform: 3679 self.interactor.ProcessEvents() 3680 except: 3681 pass 3682 3683 self._must_close_now = True 3684 3685 if vedo.plotter_instance == self: 3686 vedo.plotter_instance = None 3687 3688 if self.interactor and self._interactive: 3689 self.break_interaction() 3690 elif self._must_close_now: 3691 # dont call ExitCallback here 3692 if self.interactor: 3693 self.break_interaction() 3694 self.interactor.GetRenderWindow().Finalize() 3695 self.interactor.TerminateApp() 3696 self.camera = None 3697 self.renderer = None 3698 self.renderers = [] 3699 self.window = None 3700 self.interactor = None 3701 return self 3702 3703 @property 3704 def camera(self): 3705 """Return the current active camera.""" 3706 if self.renderer: 3707 return self.renderer.GetActiveCamera() 3708 3709 @camera.setter 3710 def camera(self, cam): 3711 if self.renderer: 3712 if isinstance(cam, dict): 3713 cam = utils.camera_from_dict(cam) 3714 self.renderer.SetActiveCamera(cam) 3715 3716 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3717 """ 3718 Take a screenshot of the Plotter window. 3719 3720 Arguments: 3721 scale : (int) 3722 set image magnification as an integer multiplicating factor 3723 asarray : (bool) 3724 return a numpy array of the image instead of writing a file 3725 3726 Warning: 3727 If you get black screenshots try to set `interactive=False` in `show()` 3728 then call `screenshot()` and `plt.interactive()` afterwards. 3729 3730 Example: 3731 ```py 3732 from vedo import * 3733 sphere = Sphere().linewidth(1) 3734 plt = show(sphere, interactive=False) 3735 plt.screenshot('image.png') 3736 plt.interactive() 3737 plt.close() 3738 ``` 3739 3740 Example: 3741 ```py 3742 from vedo import * 3743 sphere = Sphere().linewidth(1) 3744 plt = show(sphere, interactive=False) 3745 plt.screenshot('anotherimage.png') 3746 plt.interactive() 3747 plt.close() 3748 ``` 3749 """ 3750 return vedo.file_io.screenshot(filename, scale, asarray) 3751 3752 def toimage(self, scale=1) -> "vedo.image.Image": 3753 """ 3754 Generate a `Image` object from the current rendering window. 3755 3756 Arguments: 3757 scale : (int) 3758 set image magnification as an integer multiplicating factor 3759 """ 3760 if vedo.settings.screeshot_large_image: 3761 w2if = vtki.new("RenderLargeImage") 3762 w2if.SetInput(self.renderer) 3763 w2if.SetMagnification(scale) 3764 else: 3765 w2if = vtki.new("WindowToImageFilter") 3766 w2if.SetInput(self.window) 3767 if hasattr(w2if, "SetScale"): 3768 w2if.SetScale(scale, scale) 3769 if vedo.settings.screenshot_transparent_background: 3770 w2if.SetInputBufferTypeToRGBA() 3771 w2if.ReadFrontBufferOff() # read from the back buffer 3772 w2if.Update() 3773 return vedo.image.Image(w2if.GetOutput()) 3774 3775 def export(self, filename="scene.npz", binary=False) -> Self: 3776 """ 3777 Export scene to file to HTML, X3D or Numpy file. 3778 3779 Examples: 3780 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3781 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3782 """ 3783 vedo.file_io.export_window(filename, binary=binary) 3784 return self 3785 3786 def color_picker(self, xy, verbose=False): 3787 """Pick color of specific (x,y) pixel on the screen.""" 3788 w2if = vtki.new("WindowToImageFilter") 3789 w2if.SetInput(self.window) 3790 w2if.ReadFrontBufferOff() 3791 w2if.Update() 3792 nx, ny = self.window.GetSize() 3793 varr = w2if.GetOutput().GetPointData().GetScalars() 3794 3795 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3796 x, y = int(xy[0]), int(xy[1]) 3797 if y < ny and x < nx: 3798 3799 rgb = arr[y, x] 3800 3801 if verbose: 3802 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3803 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3804 vedo.printc("█", c=[0, rgb[1], 0], end="") 3805 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3806 vedo.printc("] = ", end="") 3807 cnm = vedo.get_color_name(rgb) 3808 if np.sum(rgb) < 150: 3809 vedo.printc( 3810 rgb.tolist(), 3811 vedo.colors.rgb2hex(np.array(rgb) / 255), 3812 c="w", 3813 bc=rgb, 3814 invert=1, 3815 end="", 3816 ) 3817 vedo.printc(" -> " + cnm, invert=1, c="w") 3818 else: 3819 vedo.printc( 3820 rgb.tolist(), 3821 vedo.colors.rgb2hex(np.array(rgb) / 255), 3822 c=rgb, 3823 end="", 3824 ) 3825 vedo.printc(" -> " + cnm, c=cnm) 3826 3827 return rgb 3828 3829 return None 3830 3831 ####################################################################### 3832 def _default_mouseleftclick(self, iren, event) -> None: 3833 x, y = iren.GetEventPosition() 3834 renderer = iren.FindPokedRenderer(x, y) 3835 picker = vtki.vtkPropPicker() 3836 picker.PickProp(x, y, renderer) 3837 3838 self.renderer = renderer 3839 3840 clicked_actor = picker.GetActor() 3841 # clicked_actor2D = picker.GetActor2D() 3842 3843 # print('_default_mouseleftclick mouse at', x, y) 3844 # print("picked Volume:", [picker.GetVolume()]) 3845 # print("picked Actor2D:", [picker.GetActor2D()]) 3846 # print("picked Assembly:", [picker.GetAssembly()]) 3847 # print("picked Prop3D:", [picker.GetProp3D()]) 3848 3849 if not clicked_actor: 3850 clicked_actor = picker.GetAssembly() 3851 3852 if not clicked_actor: 3853 clicked_actor = picker.GetProp3D() 3854 3855 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3856 return 3857 3858 self.picked3d = picker.GetPickPosition() 3859 self.picked2d = np.array([x, y]) 3860 3861 if not clicked_actor: 3862 return 3863 3864 self.justremoved = None 3865 self.clicked_actor = clicked_actor 3866 3867 try: # might not be a vedo obj 3868 self.clicked_object = clicked_actor.retrieve_object() 3869 # save this info in the object itself 3870 self.clicked_object.picked3d = self.picked3d 3871 self.clicked_object.picked2d = self.picked2d 3872 except AttributeError: 3873 pass 3874 3875 # ----------- 3876 # if "Histogram1D" in picker.GetAssembly().__class__.__name__: 3877 # histo = picker.GetAssembly() 3878 # if histo.verbose: 3879 # x = self.picked3d[0] 3880 # idx = np.digitize(x, histo.edges) - 1 3881 # f = histo.frequencies[idx] 3882 # cn = histo.centers[idx] 3883 # vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3884 3885 ####################################################################### 3886 def _default_keypress(self, iren, event) -> None: 3887 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3888 3889 key = iren.GetKeySym() 3890 3891 if "_L" in key or "_R" in key: 3892 return 3893 3894 if iren.GetShiftKey(): 3895 key = key.upper() 3896 3897 if iren.GetControlKey(): 3898 key = "Ctrl+" + key 3899 3900 if iren.GetAltKey(): 3901 key = "Alt+" + key 3902 3903 ####################################################### 3904 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3905 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3906 # iren.GetKeyCode(), iren.GetRepeatCount()) 3907 ####################################################### 3908 3909 x, y = iren.GetEventPosition() 3910 renderer = iren.FindPokedRenderer(x, y) 3911 3912 if key in ["q", "Return"]: 3913 self.break_interaction() 3914 return 3915 3916 elif key in ["Ctrl+q", "Ctrl+w", "Escape"]: 3917 self.close() 3918 return 3919 3920 elif key == "F1": 3921 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3922 self.break_interaction() 3923 sys.exit(0) 3924 3925 elif key == "Down": 3926 if self.clicked_object and self.clicked_object in self.get_meshes(): 3927 self.clicked_object.alpha(0.02) 3928 if hasattr(self.clicked_object, "properties_backface"): 3929 bfp = self.clicked_actor.GetBackfaceProperty() 3930 self.clicked_object.properties_backface = bfp # save it 3931 self.clicked_actor.SetBackfaceProperty(None) 3932 else: 3933 for obj in self.get_meshes(): 3934 if obj: 3935 obj.alpha(0.02) 3936 bfp = obj.actor.GetBackfaceProperty() 3937 if bfp and hasattr(obj, "properties_backface"): 3938 obj.properties_backface = bfp 3939 obj.actor.SetBackfaceProperty(None) 3940 3941 elif key == "Left": 3942 if self.clicked_object and self.clicked_object in self.get_meshes(): 3943 ap = self.clicked_object.properties 3944 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3945 ap.SetOpacity(aal) 3946 bfp = self.clicked_actor.GetBackfaceProperty() 3947 if bfp and hasattr(self.clicked_object, "properties_backface"): 3948 self.clicked_object.properties_backface = bfp 3949 self.clicked_actor.SetBackfaceProperty(None) 3950 else: 3951 for a in self.get_meshes(): 3952 if a: 3953 ap = a.properties 3954 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3955 ap.SetOpacity(aal) 3956 bfp = a.actor.GetBackfaceProperty() 3957 if bfp and hasattr(a, "properties_backface"): 3958 a.properties_backface = bfp 3959 a.actor.SetBackfaceProperty(None) 3960 3961 elif key == "Right": 3962 if self.clicked_object and self.clicked_object in self.get_meshes(): 3963 ap = self.clicked_object.properties 3964 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3965 ap.SetOpacity(aal) 3966 if ( 3967 aal == 1 3968 and hasattr(self.clicked_object, "properties_backface") 3969 and self.clicked_object.properties_backface 3970 ): 3971 # put back 3972 self.clicked_actor.SetBackfaceProperty( 3973 self.clicked_object.properties_backface) 3974 else: 3975 for a in self.get_meshes(): 3976 if a: 3977 ap = a.properties 3978 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3979 ap.SetOpacity(aal) 3980 if aal == 1 and hasattr(a, "properties_backface") and a.properties_backface: 3981 a.actor.SetBackfaceProperty(a.properties_backface) 3982 3983 elif key == "Up": 3984 if self.clicked_object and self.clicked_object in self.get_meshes(): 3985 self.clicked_object.properties.SetOpacity(1) 3986 if hasattr(self.clicked_object, "properties_backface") and self.clicked_object.properties_backface: 3987 self.clicked_object.actor.SetBackfaceProperty(self.clicked_object.properties_backface) 3988 else: 3989 for a in self.get_meshes(): 3990 if a: 3991 a.properties.SetOpacity(1) 3992 if hasattr(a, "properties_backface") and a.properties_backface: 3993 a.actor.SetBackfaceProperty(a.properties_backface) 3994 3995 elif key == "P": 3996 if self.clicked_object and self.clicked_object in self.get_meshes(): 3997 objs = [self.clicked_object] 3998 else: 3999 objs = self.get_meshes() 4000 for ia in objs: 4001 try: 4002 ps = ia.properties.GetPointSize() 4003 if ps > 1: 4004 ia.properties.SetPointSize(ps - 1) 4005 ia.properties.SetRepresentationToPoints() 4006 except AttributeError: 4007 pass 4008 4009 elif key == "p": 4010 if self.clicked_object and self.clicked_object in self.get_meshes(): 4011 objs = [self.clicked_object] 4012 else: 4013 objs = self.get_meshes() 4014 for ia in objs: 4015 try: 4016 ps = ia.properties.GetPointSize() 4017 ia.properties.SetPointSize(ps + 2) 4018 ia.properties.SetRepresentationToPoints() 4019 except AttributeError: 4020 pass 4021 4022 elif key == "U": 4023 pval = renderer.GetActiveCamera().GetParallelProjection() 4024 renderer.GetActiveCamera().SetParallelProjection(not pval) 4025 if pval: 4026 renderer.ResetCamera() 4027 4028 elif key == "r": 4029 renderer.ResetCamera() 4030 4031 elif key == "h": 4032 msg = f" vedo {vedo.__version__}" 4033 msg += f" | vtk {vtki.vtkVersion().GetVTKVersion()}" 4034 msg += f" | numpy {np.__version__}" 4035 msg += f" | python {sys.version_info[0]}.{sys.version_info[1]}, press: " 4036 vedo.printc(msg.ljust(75), invert=True) 4037 msg = ( 4038 " i print info about the last clicked object \n" 4039 " I print color of the pixel under the mouse \n" 4040 " Y show the pipeline for this object as a graph \n" 4041 " <- -> use arrows to reduce/increase opacity \n" 4042 " x toggle mesh visibility \n" 4043 " w toggle wireframe/surface style \n" 4044 " l toggle surface edges visibility \n" 4045 " p/P hide surface faces and show only points \n" 4046 " 1-3 cycle surface color (2=light, 3=dark) \n" 4047 " 4 cycle color map (press shift-4 to go back) \n" 4048 " 5-6 cycle point-cell arrays (shift to go back) \n" 4049 " 7-8 cycle background and gradient color \n" 4050 " 09+- cycle axes styles (on keypad, or press +/-) \n" 4051 " k cycle available lighting styles \n" 4052 " K toggle shading as flat or phong \n" 4053 " A toggle anti-aliasing \n" 4054 " D toggle depth-peeling (for transparencies) \n" 4055 " U toggle perspective/parallel projection \n" 4056 " o/O toggle extra light to scene and rotate it \n" 4057 " a toggle interaction to Actor Mode \n" 4058 " n toggle surface normals \n" 4059 " r reset camera position \n" 4060 " R reset camera to the closest orthogonal view \n" 4061 " . fly camera to the last clicked point \n" 4062 " C print the current camera parameters state \n" 4063 " X invoke a cutter widget tool \n" 4064 " S save a screenshot of the current scene \n" 4065 " E/F export 3D scene to numpy file or X3D \n" 4066 " q return control to python script \n" 4067 " Esc abort execution and exit python kernel " 4068 ) 4069 vedo.printc(msg, dim=True, italic=True, bold=True) 4070 vedo.printc( 4071 " Check out the documentation at: https://vedo.embl.es ".ljust(75), 4072 invert=True, 4073 bold=True, 4074 ) 4075 return 4076 4077 elif key == "a": 4078 cur = iren.GetInteractorStyle() 4079 if isinstance(cur, vtki.get_class("InteractorStyleTrackballCamera")): 4080 msg = "Interactor style changed to TrackballActor\n" 4081 msg += " you can now move and rotate individual meshes:\n" 4082 msg += " press X twice to save the repositioned mesh\n" 4083 msg += " press 'a' to go back to normal style" 4084 vedo.printc(msg) 4085 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 4086 else: 4087 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 4088 return 4089 4090 elif key == "A": # toggle antialiasing 4091 msam = self.window.GetMultiSamples() 4092 if not msam: 4093 self.window.SetMultiSamples(16) 4094 else: 4095 self.window.SetMultiSamples(0) 4096 msam = self.window.GetMultiSamples() 4097 if msam: 4098 vedo.printc(f"Antialiasing set to {msam} samples", c=bool(msam)) 4099 else: 4100 vedo.printc("Antialiasing disabled", c=bool(msam)) 4101 4102 elif key == "D": # toggle depthpeeling 4103 udp = not renderer.GetUseDepthPeeling() 4104 renderer.SetUseDepthPeeling(udp) 4105 # self.renderer.SetUseDepthPeelingForVolumes(udp) 4106 if udp: 4107 self.window.SetAlphaBitPlanes(1) 4108 renderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 4109 renderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 4110 self.interactor.Render() 4111 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 4112 rnr = self.renderers.index(renderer) 4113 vedo.printc(f"Depth peeling set to {udp} for renderer nr.{rnr}", c=udp) 4114 if not wasUsed and udp: 4115 vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True) 4116 return 4117 4118 elif key == "period": 4119 if self.picked3d: 4120 self.fly_to(self.picked3d) 4121 return 4122 4123 elif key == "S": 4124 fname = "screenshot.png" 4125 i = 1 4126 while os.path.isfile(fname): 4127 fname = f"screenshot{i}.png" 4128 i += 1 4129 vedo.file_io.screenshot(fname) 4130 vedo.printc(rf":camera: Saved rendering window to {fname}", c="b") 4131 return 4132 4133 elif key == "C": 4134 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 4135 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 4136 # This could happen for plotting geological models with UTM coordinate systems 4137 cam = renderer.GetActiveCamera() 4138 vedo.printc("\n###################################################", c="y") 4139 vedo.printc("## Template python code to position this camera: ##", c="y") 4140 vedo.printc("cam = dict(", c="y") 4141 vedo.printc(" pos=" + utils.precision(cam.GetPosition(), 6) + ",", c="y") 4142 vedo.printc(" focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y") 4143 vedo.printc(" viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y") 4144 vedo.printc(" roll=" + utils.precision(cam.GetRoll(), 6) + ",", c="y") 4145 if cam.GetParallelProjection(): 4146 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 4147 else: 4148 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 4149 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 4150 vedo.printc(')', c='y') 4151 vedo.printc('show(mymeshes, camera=cam)', c='y') 4152 vedo.printc('###################################################', c='y') 4153 return 4154 4155 elif key == "R": 4156 self.reset_viewup() 4157 4158 elif key == "w": 4159 try: 4160 if self.clicked_object.properties.GetRepresentation() == 1: # toggle 4161 self.clicked_object.properties.SetRepresentationToSurface() 4162 else: 4163 self.clicked_object.properties.SetRepresentationToWireframe() 4164 except AttributeError: 4165 pass 4166 4167 elif key == "1": 4168 try: 4169 self._icol += 1 4170 self.clicked_object.mapper.ScalarVisibilityOff() 4171 pal = vedo.colors.palettes[vedo.settings.palette % len(vedo.colors.palettes)] 4172 self.clicked_object.c(pal[(self._icol) % 10]) 4173 self.remove(self.clicked_object.scalarbar) 4174 except AttributeError: 4175 pass 4176 4177 elif key == "2": # dark colors 4178 try: 4179 bsc = ["k1", "k2", "k3", "k4", 4180 "b1", "b2", "b3", "b4", 4181 "p1", "p2", "p3", "p4", 4182 "g1", "g2", "g3", "g4", 4183 "r1", "r2", "r3", "r4", 4184 "o1", "o2", "o3", "o4", 4185 "y1", "y2", "y3", "y4"] 4186 self._icol += 1 4187 if self.clicked_object: 4188 self.clicked_object.mapper.ScalarVisibilityOff() 4189 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4190 self.clicked_object.c(newcol) 4191 self.remove(self.clicked_object.scalarbar) 4192 except AttributeError: 4193 pass 4194 4195 elif key == "3": # light colors 4196 try: 4197 bsc = ["k6", "k7", "k8", "k9", 4198 "b6", "b7", "b8", "b9", 4199 "p6", "p7", "p8", "p9", 4200 "g6", "g7", "g8", "g9", 4201 "r6", "r7", "r8", "r9", 4202 "o6", "o7", "o8", "o9", 4203 "y6", "y7", "y8", "y9"] 4204 self._icol += 1 4205 if self.clicked_object: 4206 self.clicked_object.mapper.ScalarVisibilityOff() 4207 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4208 self.clicked_object.c(newcol) 4209 self.remove(self.clicked_object.scalarbar) 4210 except AttributeError: 4211 pass 4212 4213 elif key == "4": # cmap name cycle 4214 ob = self.clicked_object 4215 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4216 return 4217 if not ob.mapper.GetScalarVisibility(): 4218 return 4219 onwhat = ob.mapper.GetScalarModeAsString() # UsePointData/UseCellData 4220 4221 cmap_names = [ 4222 "Accent", "Paired", 4223 "rainbow", "rainbow_r", 4224 "Spectral", "Spectral_r", 4225 "gist_ncar", "gist_ncar_r", 4226 "viridis", "viridis_r", 4227 "hot", "hot_r", 4228 "terrain", "ocean", 4229 "coolwarm", "seismic", "PuOr", "RdYlGn", 4230 ] 4231 try: 4232 i = cmap_names.index(ob._cmap_name) 4233 if iren.GetShiftKey(): 4234 i -= 1 4235 else: 4236 i += 1 4237 if i >= len(cmap_names): 4238 i = 0 4239 if i < 0: 4240 i = len(cmap_names) - 1 4241 except ValueError: 4242 i = 0 4243 4244 ob._cmap_name = cmap_names[i] 4245 ob.cmap(ob._cmap_name, on=onwhat) 4246 if ob.scalarbar: 4247 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4248 self.remove(ob.scalarbar) 4249 title = ob.scalarbar.GetTitle() 4250 ob.add_scalarbar(title=title) 4251 self.add(ob.scalarbar).render() 4252 elif isinstance(ob.scalarbar, vedo.Assembly): 4253 self.remove(ob.scalarbar) 4254 ob.add_scalarbar3d(title=ob._cmap_name) 4255 self.add(ob.scalarbar) 4256 4257 vedo.printc( 4258 f"Name:'{ob.name}'," if ob.name else "", 4259 f"range:{utils.precision(ob.mapper.GetScalarRange(),3)},", 4260 f"colormap:'{ob._cmap_name}'", c="g", bold=False, 4261 ) 4262 4263 elif key == "5": # cycle pointdata array 4264 ob = self.clicked_object 4265 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4266 return 4267 4268 arrnames = ob.pointdata.keys() 4269 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4270 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4271 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4272 if len(arrnames) == 0: 4273 return 4274 ob.mapper.SetScalarVisibility(1) 4275 4276 if not ob._cmap_name: 4277 ob._cmap_name = "rainbow" 4278 4279 try: 4280 curr_name = ob.dataset.GetPointData().GetScalars().GetName() 4281 i = arrnames.index(curr_name) 4282 if "normals" in curr_name.lower(): 4283 return 4284 if iren.GetShiftKey(): 4285 i -= 1 4286 else: 4287 i += 1 4288 if i >= len(arrnames): 4289 i = 0 4290 if i < 0: 4291 i = len(arrnames) - 1 4292 except (ValueError, AttributeError): 4293 i = 0 4294 4295 ob.cmap(ob._cmap_name, arrnames[i], on="points") 4296 if ob.scalarbar: 4297 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4298 self.remove(ob.scalarbar) 4299 title = ob.scalarbar.GetTitle() 4300 ob.scalarbar = None 4301 ob.add_scalarbar(title=arrnames[i]) 4302 self.add(ob.scalarbar) 4303 elif isinstance(ob.scalarbar, vedo.Assembly): 4304 self.remove(ob.scalarbar) 4305 ob.scalarbar = None 4306 ob.add_scalarbar3d(title=arrnames[i]) 4307 self.add(ob.scalarbar) 4308 else: 4309 vedo.printc( 4310 f"Name:'{ob.name}'," if ob.name else "", 4311 f"active pointdata array: '{arrnames[i]}'", 4312 c="g", bold=False, 4313 ) 4314 4315 elif key == "6": # cycle celldata array 4316 ob = self.clicked_object 4317 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4318 return 4319 4320 arrnames = ob.celldata.keys() 4321 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4322 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4323 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4324 if len(arrnames) == 0: 4325 return 4326 ob.mapper.SetScalarVisibility(1) 4327 4328 if not ob._cmap_name: 4329 ob._cmap_name = "rainbow" 4330 4331 try: 4332 curr_name = ob.dataset.GetCellData().GetScalars().GetName() 4333 i = arrnames.index(curr_name) 4334 if "normals" in curr_name.lower(): 4335 return 4336 if iren.GetShiftKey(): 4337 i -= 1 4338 else: 4339 i += 1 4340 if i >= len(arrnames): 4341 i = 0 4342 if i < 0: 4343 i = len(arrnames) - 1 4344 except (ValueError, AttributeError): 4345 i = 0 4346 4347 ob.cmap(ob._cmap_name, arrnames[i], on="cells") 4348 if ob.scalarbar: 4349 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4350 self.remove(ob.scalarbar) 4351 title = ob.scalarbar.GetTitle() 4352 ob.scalarbar = None 4353 ob.add_scalarbar(title=arrnames[i]) 4354 self.add(ob.scalarbar) 4355 elif isinstance(ob.scalarbar, vedo.Assembly): 4356 self.remove(ob.scalarbar) 4357 ob.scalarbar = None 4358 ob.add_scalarbar3d(title=arrnames[i]) 4359 self.add(ob.scalarbar) 4360 else: 4361 vedo.printc( 4362 f"Name:'{ob.name}'," if ob.name else "", 4363 f"active celldata array: '{arrnames[i]}'", 4364 c="g", bold=False, 4365 ) 4366 4367 elif key == "7": 4368 bgc = np.array(renderer.GetBackground()).sum() / 3 4369 if bgc <= 0: 4370 bgc = 0.223 4371 elif 0 < bgc < 1: 4372 bgc = 1 4373 else: 4374 bgc = 0 4375 renderer.SetBackground(bgc, bgc, bgc) 4376 4377 elif key == "8": 4378 bg2cols = [ 4379 "lightyellow", 4380 "darkseagreen", 4381 "palegreen", 4382 "steelblue", 4383 "lightblue", 4384 "cadetblue", 4385 "lavender", 4386 "white", 4387 "blackboard", 4388 "black", 4389 ] 4390 bg2name = vedo.get_color_name(renderer.GetBackground2()) 4391 if bg2name in bg2cols: 4392 idx = bg2cols.index(bg2name) 4393 else: 4394 idx = 4 4395 if idx is not None: 4396 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 4397 if not bg2name_next: 4398 renderer.GradientBackgroundOff() 4399 else: 4400 renderer.GradientBackgroundOn() 4401 renderer.SetBackground2(vedo.get_color(bg2name_next)) 4402 4403 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 4404 i = self.renderers.index(renderer) 4405 try: 4406 self.axes_instances[i].EnabledOff() 4407 self.axes_instances[i].SetInteractor(None) 4408 except AttributeError: 4409 # print("Cannot remove widget", [self.axes_instances[i]]) 4410 try: 4411 self.remove(self.axes_instances[i]) 4412 except: 4413 print("Cannot remove axes", [self.axes_instances[i]]) 4414 return 4415 self.axes_instances[i] = None 4416 4417 if not self.axes: 4418 self.axes = 0 4419 if isinstance(self.axes, dict): 4420 self.axes = 1 4421 4422 if key in ["minus", "KP_Subtract"]: 4423 if not self.camera.GetParallelProjection() and self.axes == 0: 4424 self.axes -= 1 # jump ruler doesnt make sense in perspective mode 4425 bns = self.renderer.ComputeVisiblePropBounds() 4426 addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None, bounds=bns) 4427 else: 4428 if not self.camera.GetParallelProjection() and self.axes == 12: 4429 self.axes += 1 # jump ruler doesnt make sense in perspective mode 4430 bns = self.renderer.ComputeVisiblePropBounds() 4431 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns) 4432 self.render() 4433 4434 elif "KP_" in key or key in [ 4435 "Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior" 4436 ]: 4437 asso = { # change axes style 4438 "KP_Insert": 0, "KP_0": 0, "Insert": 0, 4439 "KP_End": 1, "KP_1": 1, "End": 1, 4440 "KP_Down": 2, "KP_2": 2, "Down": 2, 4441 "KP_Next": 3, "KP_3": 3, "Next": 3, 4442 "KP_Left": 4, "KP_4": 4, "Left": 4, 4443 "KP_Begin": 5, "KP_5": 5, "Begin": 5, 4444 "KP_Right": 6, "KP_6": 6, "Right": 6, 4445 "KP_Home": 7, "KP_7": 7, "Home": 7, 4446 "KP_Up": 8, "KP_8": 8, "Up": 8, 4447 "Prior": 9, # on windows OS 4448 } 4449 clickedr = self.renderers.index(renderer) 4450 if key in asso: 4451 if self.axes_instances[clickedr]: 4452 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 4453 self.axes_instances[clickedr].EnabledOff() 4454 else: 4455 try: 4456 renderer.RemoveActor(self.axes_instances[clickedr]) 4457 except: 4458 pass 4459 self.axes_instances[clickedr] = None 4460 bounds = renderer.ComputeVisiblePropBounds() 4461 addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds) 4462 self.interactor.Render() 4463 4464 if key == "O": 4465 renderer.RemoveLight(self._extralight) 4466 self._extralight = None 4467 4468 elif key == "o": 4469 vbb, sizes, _, _ = addons.compute_visible_bounds() 4470 cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2) 4471 if not self._extralight: 4472 vup = renderer.GetActiveCamera().GetViewUp() 4473 pos = cm + utils.vector(vup) * utils.mag(sizes) 4474 self._extralight = addons.Light(pos, focal_point=cm, intensity=0.4) 4475 renderer.AddLight(self._extralight) 4476 vedo.printc("Press 'o' again to rotate light source, or 'O' to remove it.", c='y') 4477 else: 4478 cpos = utils.vector(self._extralight.GetPosition()) 4479 x, y, z = self._extralight.GetPosition() - cm 4480 r, th, ph = transformations.cart2spher(x, y, z) 4481 th += 0.2 4482 if th > np.pi: 4483 th = np.random.random() * np.pi / 2 4484 ph += 0.3 4485 cpos = transformations.spher2cart(r, th, ph).T + cm 4486 self._extralight.SetPosition(cpos) 4487 4488 elif key == "l": 4489 if self.clicked_object in self.get_meshes(): 4490 objs = [self.clicked_object] 4491 else: 4492 objs = self.get_meshes() 4493 for ia in objs: 4494 try: 4495 ev = ia.properties.GetEdgeVisibility() 4496 ia.properties.SetEdgeVisibility(not ev) 4497 ia.properties.SetRepresentationToSurface() 4498 ia.properties.SetLineWidth(0.1) 4499 except AttributeError: 4500 pass 4501 4502 elif key == "k": # lightings 4503 if self.clicked_object in self.get_meshes(): 4504 objs = [self.clicked_object] 4505 else: 4506 objs = self.get_meshes() 4507 shds = ("default", "metallic", "plastic", "shiny", "glossy", "off") 4508 for ia in objs: 4509 try: 4510 lnr = (ia._ligthingnr + 1) % 6 4511 ia.lighting(shds[lnr]) 4512 ia._ligthingnr = lnr 4513 except AttributeError: 4514 pass 4515 4516 elif key == "K": # shading 4517 if self.clicked_object in self.get_meshes(): 4518 objs = [self.clicked_object] 4519 else: 4520 objs = self.get_meshes() 4521 for ia in objs: 4522 if isinstance(ia, vedo.Mesh): 4523 ia.compute_normals(cells=False) 4524 intrp = ia.properties.GetInterpolation() 4525 if intrp > 0: 4526 ia.properties.SetInterpolation(0) # flat 4527 else: 4528 ia.properties.SetInterpolation(2) # phong 4529 4530 elif key == "n": # show normals to an actor 4531 self.remove("added_auto_normals") 4532 if self.clicked_object in self.get_meshes(): 4533 if self.clicked_actor.GetPickable(): 4534 norml = vedo.shapes.NormalLines(self.clicked_object) 4535 norml.name = "added_auto_normals" 4536 self.add(norml) 4537 4538 elif key == "x": 4539 if self.justremoved is None: 4540 if self.clicked_object in self.get_meshes() or isinstance( 4541 self.clicked_object, vtki.vtkAssembly 4542 ): 4543 self.justremoved = self.clicked_actor 4544 self.renderer.RemoveActor(self.clicked_actor) 4545 else: 4546 self.renderer.AddActor(self.justremoved) 4547 self.justremoved = None 4548 4549 elif key == "X": 4550 if self.clicked_object: 4551 if not self.cutter_widget: 4552 self.cutter_widget = addons.BoxCutter(self.clicked_object) 4553 self.add(self.cutter_widget) 4554 vedo.printc("Press i to toggle the cutter on/off", c='g', dim=1) 4555 vedo.printc(" u to flip selection", c='g', dim=1) 4556 vedo.printc(" r to reset cutting planes", c='g', dim=1) 4557 vedo.printc(" Shift+X to close the cutter box widget", c='g', dim=1) 4558 vedo.printc(" Ctrl+S to save the cut section to file.", c='g', dim=1) 4559 else: 4560 self.remove(self.cutter_widget) 4561 self.cutter_widget = None 4562 vedo.printc("Click object and press X to open the cutter box widget.", c='g') 4563 4564 elif key == "E": 4565 vedo.printc(r":camera: Exporting 3D window to file scene.npz", c="b", end="") 4566 vedo.file_io.export_window("scene.npz") 4567 vedo.printc(", try:\n> vedo scene.npz # (this is experimental!)", c="b") 4568 return 4569 4570 elif key == "F": 4571 vedo.file_io.export_window("scene.x3d") 4572 vedo.printc(r":camera: Exporting 3D window to file", c="b", end="") 4573 vedo.file_io.export_window("scene.npz") 4574 vedo.printc(". Try:\n> firefox scene.html", c="b") 4575 4576 # elif key == "G": # not working with last version of k3d 4577 # vedo.file_io.export_window("scene.html") 4578 # vedo.printc(r":camera: Exporting K3D window to file", c="b", end="") 4579 # vedo.file_io.export_window("scene.html") 4580 # vedo.printc(". Try:\n> firefox scene.html", c="b") 4581 4582 elif key == "i": # print info 4583 if self.clicked_object: 4584 print(self.clicked_object) 4585 else: 4586 print(self) 4587 4588 elif key == "I": # print color under the mouse 4589 x, y = iren.GetEventPosition() 4590 self.color_picker([x, y], verbose=True) 4591 4592 elif key == "Y": 4593 if self.clicked_object and self.clicked_object.pipeline: 4594 self.clicked_object.pipeline.show() 4595 4596 if iren: 4597 iren.Render()
376class Plotter: 377 """Main class to manage objects.""" 378 379 def __init__( 380 self, 381 shape=(1, 1), 382 N=None, 383 pos=(0, 0), 384 size="auto", 385 screensize="auto", 386 title="vedo", 387 bg="white", 388 bg2=None, 389 axes=None, 390 sharecam=True, 391 resetcam=True, 392 interactive=None, 393 offscreen=False, 394 qt_widget=None, 395 wx_widget=None, 396 ): 397 """ 398 Arguments: 399 shape : (str, list) 400 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 401 N : (int) 402 number of desired renderers arranged in a grid automatically. 403 pos : (list) 404 (x,y) position in pixels of top-left corner of the rendering window on the screen 405 size : (str, list) 406 size of the rendering window. If 'auto', guess it based on screensize. 407 screensize : (list) 408 physical size of the monitor screen in pixels 409 bg : (color, str) 410 background color or specify jpg image file name with path 411 bg2 : (color) 412 background color of a gradient towards the top 413 title : (str) 414 window title 415 416 axes : (int) 417 418 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 419 Check out `vedo.addons.Axes()` for the available options. 420 421 - 0, no axes 422 - 1, draw three gray grid walls 423 - 2, show cartesian axes from (0,0,0) 424 - 3, show positive range of cartesian axes from (0,0,0) 425 - 4, show a triad at bottom left 426 - 5, show a cube at bottom left 427 - 6, mark the corners of the bounding box 428 - 7, draw a 3D ruler at each side of the cartesian axes 429 - 8, show the VTK CubeAxesActor object 430 - 9, show the bounding box outLine 431 - 10, show three circles representing the maximum bounding box 432 - 11, show a large grid on the x-y plane (use with zoom=8) 433 - 12, show polar axes 434 - 13, draw a simple ruler at the bottom of the window 435 - 14: draw a camera orientation widget 436 437 sharecam : (bool) 438 if False each renderer will have an independent camera 439 interactive : (bool) 440 if True will stop after show() to allow interaction with the 3d scene 441 offscreen : (bool) 442 if True will not show the rendering window 443 qt_widget : (QVTKRenderWindowInteractor) 444 render in a Qt-Widget using an QVTKRenderWindowInteractor. 445 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 446 """ 447 vedo.plotter_instance = self 448 449 if interactive is None: 450 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 451 self._interactive = interactive 452 # print("interactive", interactive, N, shape) 453 454 self.objects = [] # list of objects to be shown 455 self.clicked_object = None # holds the object that has been clicked 456 self.clicked_actor = None # holds the actor that has been clicked 457 458 self.shape = shape # nr. of subwindows in grid 459 self.axes = axes # show axes type nr. 460 self.title = title # window title 461 self.size = size # window size 462 self.backgrcol = bg # used also by backend notebooks 463 464 self.offscreen= offscreen 465 self.resetcam = resetcam 466 self.sharecam = sharecam # share the same camera if multiple renderers 467 self.pos = pos # used by vedo.file_io 468 469 self.picker = None # hold the vtkPicker object 470 self.picked2d = None # 2d coords of a clicked point on the rendering window 471 self.picked3d = None # 3d coords of a clicked point on an actor 472 473 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 474 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 475 self.interactor = None 476 self.window = None 477 self.renderer = None 478 self.renderers = [] # list of renderers 479 480 # mostly internal stuff: 481 self.hover_legends = [] 482 self.justremoved = None 483 self.axes_instances = [] 484 self.clock = 0 485 self.sliders = [] 486 self.buttons = [] 487 self.widgets = [] 488 self.cutter_widget = None 489 self.hint_widget = None 490 self.background_renderer = None 491 self.last_event = None 492 self.skybox = None 493 self._icol = 0 494 self._clockt0 = time.time() 495 self._extralight = None 496 self._cocoa_initialized = False 497 self._cocoa_process_events = True # make one call in show() 498 self._must_close_now = False 499 500 ##################################################################### 501 if vedo.settings.default_backend == "2d": 502 self.offscreen = True 503 if self.size == "auto": 504 self.size = (800, 600) 505 506 elif vedo.settings.default_backend == "k3d": 507 if self.size == "auto": 508 self.size = (1000, 1000) 509 #################################### 510 return ############################ 511 #################################### 512 513 ############################################################# 514 if vedo.settings.default_backend in ["vtk", "2d", "trame"]: 515 516 if screensize == "auto": 517 screensize = (2160, 1440) # TODO: get actual screen size 518 519 # build the rendering window: 520 self.window = vtki.vtkRenderWindow() 521 522 self.window.GlobalWarningDisplayOff() 523 524 if self.title == "vedo": # check if dev version 525 if "dev" in vedo.__version__: 526 self.title = f"vedo ({vedo.__version__})" 527 self.window.SetWindowName(self.title) 528 529 # more vedo.settings 530 if vedo.settings.use_depth_peeling: 531 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 532 self.window.SetMultiSamples(vedo.settings.multi_samples) 533 534 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 535 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 536 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 537 538 ############################################################# 539 if N: # N = number of renderers. Find out the best 540 541 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 542 vedo.logger.warning("having set N, shape is ignored.") 543 544 x, y = screensize 545 nx = int(np.sqrt(int(N * y / x) + 1)) 546 ny = int(np.sqrt(int(N * x / y) + 1)) 547 lm = [ 548 (nx, ny), 549 (nx, ny + 1), 550 (nx - 1, ny), 551 (nx + 1, ny), 552 (nx, ny - 1), 553 (nx - 1, ny + 1), 554 (nx + 1, ny - 1), 555 (nx + 1, ny + 1), 556 (nx - 1, ny - 1), 557 ] 558 ind, minl = 0, 1000 559 for i, m in enumerate(lm): 560 l = m[0] * m[1] 561 if N <= l < minl: 562 ind = i 563 minl = l 564 shape = lm[ind] 565 566 ################################################## 567 if isinstance(shape, str): 568 569 if "|" in shape: 570 if self.size == "auto": 571 self.size = (800, 1200) 572 n = int(shape.split("|")[0]) 573 m = int(shape.split("|")[1]) 574 rangen = reversed(range(n)) 575 rangem = reversed(range(m)) 576 else: 577 if self.size == "auto": 578 self.size = (1200, 800) 579 m = int(shape.split("/")[0]) 580 n = int(shape.split("/")[1]) 581 rangen = range(n) 582 rangem = range(m) 583 584 if n >= m: 585 xsplit = m / (n + m) 586 else: 587 xsplit = 1 - n / (n + m) 588 if vedo.settings.window_splitting_position: 589 xsplit = vedo.settings.window_splitting_position 590 591 for i in rangen: 592 arenderer = vtki.vtkRenderer() 593 if "|" in shape: 594 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 595 else: 596 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 597 self.renderers.append(arenderer) 598 599 for i in rangem: 600 arenderer = vtki.vtkRenderer() 601 602 if "|" in shape: 603 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 604 else: 605 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 606 self.renderers.append(arenderer) 607 608 for r in self.renderers: 609 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 610 611 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 612 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 613 if vedo.settings.use_depth_peeling: 614 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 615 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 616 r.SetUseFXAA(vedo.settings.use_fxaa) 617 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 618 619 r.SetBackground(vedo.get_color(self.backgrcol)) 620 621 self.axes_instances.append(None) 622 623 self.shape = (n + m,) 624 625 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 626 # passing a sequence of dicts for renderers specifications 627 628 if self.size == "auto": 629 self.size = (1000, 800) 630 631 for rd in shape: 632 x0, y0 = rd["bottomleft"] 633 x1, y1 = rd["topright"] 634 bg_ = rd.pop("bg", "white") 635 bg2_ = rd.pop("bg2", None) 636 637 arenderer = vtki.vtkRenderer() 638 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 639 640 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 641 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 642 if vedo.settings.use_depth_peeling: 643 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 644 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 645 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 646 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 647 648 arenderer.SetViewport(x0, y0, x1, y1) 649 arenderer.SetBackground(vedo.get_color(bg_)) 650 if bg2_: 651 arenderer.GradientBackgroundOn() 652 arenderer.SetBackground2(vedo.get_color(bg2_)) 653 654 self.renderers.append(arenderer) 655 self.axes_instances.append(None) 656 657 self.shape = (len(shape),) 658 659 else: 660 661 if isinstance(self.size, str) and self.size == "auto": 662 # figure out a reasonable window size 663 f = 1.5 664 x, y = screensize 665 xs = y / f * shape[1] # because y<x 666 ys = y / f * shape[0] 667 if xs > x / f: # shrink 668 xs = x / f 669 ys = xs / shape[1] * shape[0] 670 if ys > y / f: 671 ys = y / f 672 xs = ys / shape[0] * shape[1] 673 self.size = (int(xs), int(ys)) 674 if shape == (1, 1): 675 self.size = (int(y / f), int(y / f)) # because y<x 676 else: 677 self.size = (self.size[0], self.size[1]) 678 679 try: 680 image_actor = None 681 bgname = str(self.backgrcol).lower() 682 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 683 self.window.SetNumberOfLayers(2) 684 self.background_renderer = vtki.vtkRenderer() 685 self.background_renderer.SetLayer(0) 686 self.background_renderer.InteractiveOff() 687 self.background_renderer.SetBackground(vedo.get_color(bg2)) 688 image_actor = vedo.Image(self.backgrcol).actor 689 self.window.AddRenderer(self.background_renderer) 690 self.background_renderer.AddActor(image_actor) 691 except AttributeError: 692 pass 693 694 for i in reversed(range(shape[0])): 695 for j in range(shape[1]): 696 arenderer = vtki.vtkRenderer() 697 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 698 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 699 700 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 701 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 702 if vedo.settings.use_depth_peeling: 703 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 704 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 705 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 706 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 707 708 if image_actor: 709 arenderer.SetLayer(1) 710 711 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 712 if bg2: 713 arenderer.GradientBackgroundOn() 714 arenderer.SetBackground2(vedo.get_color(bg2)) 715 716 x0 = i / shape[0] 717 y0 = j / shape[1] 718 x1 = (i + 1) / shape[0] 719 y1 = (j + 1) / shape[1] 720 arenderer.SetViewport(y0, x0, y1, x1) 721 self.renderers.append(arenderer) 722 self.axes_instances.append(None) 723 self.shape = shape 724 725 if self.renderers: 726 self.renderer = self.renderers[0] 727 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 728 729 ######################################################### 730 if self.qt_widget or self.wx_widget: 731 if self.qt_widget: 732 self.window = self.qt_widget.GetRenderWindow() # overwrite 733 else: 734 self.window = self.wx_widget.GetRenderWindow() 735 self.interactor = self.window.GetInteractor() 736 737 ######################################################### 738 for r in self.renderers: 739 self.window.AddRenderer(r) 740 # set the background gradient if any 741 if vedo.settings.background_gradient_orientation > 0: 742 try: 743 modes = [ 744 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 745 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 746 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 747 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 748 ] 749 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 750 r.GradientBackgroundOn() 751 except AttributeError: 752 pass 753 754 ######################################################### 755 if self.qt_widget or self.wx_widget: 756 # self.window.SetSize(int(self.size[0]), int(self.size[1])) 757 self.interactor.SetRenderWindow(self.window) 758 # vsty = vtki.new("InteractorStyleTrackballCamera") 759 # self.interactor.SetInteractorStyle(vsty) 760 if vedo.settings.enable_default_keyboard_callbacks: 761 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 762 if vedo.settings.enable_default_mouse_callbacks: 763 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 764 return ################ 765 ######################## 766 767 if self.size[0] == "f": # full screen 768 self.size = "fullscreen" 769 self.window.SetFullScreen(True) 770 self.window.BordersOn() 771 else: 772 self.window.SetSize(int(self.size[0]), int(self.size[1])) 773 774 if self.offscreen: 775 if self.axes in (4, 5, 8, 12, 14): 776 self.axes = 0 # does not work with those 777 self.window.SetOffScreenRendering(True) 778 self.interactor = None 779 self._interactive = False 780 return ################ 781 ######################## 782 783 self.window.SetPosition(pos) 784 785 ######################################################### 786 self.interactor = vtki.vtkRenderWindowInteractor() 787 788 self.interactor.SetRenderWindow(self.window) 789 vsty = vtki.new("InteractorStyleTrackballCamera") 790 self.interactor.SetInteractorStyle(vsty) 791 self.interactor.RemoveObservers("CharEvent") 792 793 if vedo.settings.enable_default_keyboard_callbacks: 794 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 795 if vedo.settings.enable_default_mouse_callbacks: 796 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 797 798 ##################################################################### ..init ends here. 799 800 def __str__(self): 801 """Return Plotter info.""" 802 axtype = { 803 0: "(no axes)", 804 1: "(default customizable grid walls)", 805 2: "(cartesian axes from origin", 806 3: "(positive range of cartesian axes from origin", 807 4: "(axes triad at bottom left)", 808 5: "(oriented cube at bottom left)", 809 6: "(mark the corners of the bounding box)", 810 7: "(3D ruler at each side of the cartesian axes)", 811 8: "(the vtkCubeAxesActor object)", 812 9: "(the bounding box outline)", 813 10: "(circles of maximum bounding box range)", 814 11: "(show a large grid on the x-y plane)", 815 12: "(show polar axes)", 816 13: "(simple ruler at the bottom of the window)", 817 14: "(the vtkCameraOrientationWidget object)", 818 } 819 820 module = self.__class__.__module__ 821 name = self.__class__.__name__ 822 out = vedo.printc( 823 f"{module}.{name} at ({hex(id(self))})".ljust(75), 824 bold=True, invert=True, return_string=True, 825 ) 826 out += "\x1b[0m" 827 if self.interactor: 828 out += "window title".ljust(14) + ": " + self.title + "\n" 829 out += "window size".ljust(14) + f": {self.window.GetSize()}" 830 out += f", full_screen={self.window.GetScreenSize()}\n" 831 out += "activ renderer".ljust(14) + ": nr." + str(self.renderers.index(self.renderer)) 832 out += f" (out of {len(self.renderers)} renderers)\n" 833 834 bns, totpt = [], 0 835 for a in self.objects: 836 try: 837 b = a.bounds() 838 bns.append(b) 839 except AttributeError: 840 pass 841 try: 842 totpt += a.npoints 843 except AttributeError: 844 pass 845 out += "n. of objects".ljust(14) + f": {len(self.objects)}" 846 out += f" ({totpt} vertices)\n" if totpt else "\n" 847 848 if len(bns) > 0: 849 min_bns = np.min(bns, axis=0) 850 max_bns = np.max(bns, axis=0) 851 bx1, bx2 = utils.precision(min_bns[0], 3), utils.precision(max_bns[1], 3) 852 by1, by2 = utils.precision(min_bns[2], 3), utils.precision(max_bns[3], 3) 853 bz1, bz2 = utils.precision(min_bns[4], 3), utils.precision(max_bns[5], 3) 854 out += "bounds".ljust(14) + ":" 855 out += " x=(" + bx1 + ", " + bx2 + ")," 856 out += " y=(" + by1 + ", " + by2 + ")," 857 out += " z=(" + bz1 + ", " + bz2 + ")\n" 858 859 if utils.is_integer(self.axes): 860 out += "axes style".ljust(14) + f": {self.axes} {axtype[self.axes]}\n" 861 elif isinstance(self.axes, dict): 862 out += "axes style".ljust(14) + f": 1 {axtype[1]}\n" 863 else: 864 out += "axes style".ljust(14) + f": {[self.axes]}\n" 865 return out.rstrip() + "\x1b[0m" 866 867 def print(self): 868 """Print information about the current instance.""" 869 print(self.__str__()) 870 return self 871 872 def __iadd__(self, objects): 873 self.add(objects) 874 return self 875 876 def __isub__(self, objects): 877 self.remove(objects) 878 return self 879 880 def __enter__(self): 881 # context manager like in "with Plotter() as plt:" 882 return self 883 884 def __exit__(self, *args, **kwargs): 885 # context manager like in "with Plotter() as plt:" 886 self.close() 887 888 def initialize_interactor(self) -> Self: 889 """Initialize the interactor if not already initialized.""" 890 if self.offscreen: 891 return self 892 if self.interactor: 893 if not self.interactor.GetInitialized(): 894 self.interactor.Initialize() 895 self.interactor.RemoveObservers("CharEvent") 896 return self 897 898 def process_events(self) -> Self: 899 """Process all pending events.""" 900 self.initialize_interactor() 901 if self.interactor: 902 try: 903 self.interactor.ProcessEvents() 904 except AttributeError: 905 pass 906 return self 907 908 def at(self, nren: int, yren=None) -> Self: 909 """ 910 Select the current renderer number as an int. 911 Can also use the `[nx, ny]` format. 912 """ 913 if utils.is_sequence(nren): 914 if len(nren) == 2: 915 nren, yren = nren 916 else: 917 vedo.logger.error("at() argument must be a single number or a list of two numbers") 918 raise RuntimeError 919 920 if yren is not None: 921 a, b = self.shape 922 x, y = nren, yren 923 nren = x * b + y 924 # print("at (", x, y, ") -> ren", nren) 925 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 926 vedo.logger.error(f"at({nren, yren}) is malformed!") 927 raise RuntimeError 928 929 self.renderer = self.renderers[nren] 930 return self 931 932 def add(self, *objs, at=None) -> Self: 933 """ 934 Append the input objects to the internal list of objects to be shown. 935 936 Arguments: 937 at : (int) 938 add the object at the specified renderer 939 """ 940 if at is not None: 941 ren = self.renderers[at] 942 else: 943 ren = self.renderer 944 945 objs = utils.flatten(objs) 946 for ob in objs: 947 if ob and ob not in self.objects: 948 self.objects.append(ob) 949 950 acts = self._scan_input_return_acts(objs) 951 952 for a in acts: 953 954 if ren: 955 if isinstance(a, vedo.addons.BaseCutter): 956 a.add_to(self) # from cutters 957 continue 958 959 if isinstance(a, vtki.vtkLight): 960 ren.AddLight(a) 961 continue 962 963 try: 964 ren.AddActor(a) 965 except TypeError: 966 ren.AddActor(a.actor) 967 968 try: 969 ir = self.renderers.index(ren) 970 a.rendered_at.add(ir) # might not have rendered_at 971 except (AttributeError, ValueError): 972 pass 973 974 if isinstance(a, vtki.vtkFollower): 975 a.SetCamera(self.camera) 976 elif isinstance(a, vedo.visual.LightKit): 977 a.lightkit.AddLightsToRenderer(ren) 978 979 return self 980 981 def remove(self, *objs, at=None) -> Self: 982 """ 983 Remove input object to the internal list of objects to be shown. 984 985 Objects to be removed can be referenced by their assigned name, 986 987 Arguments: 988 at : (int) 989 remove the object at the specified renderer 990 """ 991 # TODO and you can also use wildcards like `*` and `?`. 992 if at is not None: 993 ren = self.renderers[at] 994 else: 995 ren = self.renderer 996 997 objs = [ob for ob in utils.flatten(objs) if ob] 998 999 has_str = False 1000 for ob in objs: 1001 if isinstance(ob, str): 1002 has_str = True 1003 break 1004 1005 has_actor = False 1006 for ob in objs: 1007 if hasattr(ob, "actor") and ob.actor: 1008 has_actor = True 1009 break 1010 1011 if has_str or has_actor: 1012 # need to get the actors to search for 1013 for a in self.get_actors(include_non_pickables=True): 1014 # print("PARSING", [a]) 1015 try: 1016 if (a.name and a.name in objs) or a in objs: 1017 objs.append(a) 1018 # if a.name: 1019 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1020 # if any(bools) or a in objs: 1021 # objs.append(a) 1022 # print('a.name',a.name, objs,any(bools)) 1023 except AttributeError: # no .name 1024 # passing the actor so get back the object with .retrieve_object() 1025 try: 1026 vobj = a.retrieve_object() 1027 if (vobj.name and vobj.name in objs) or vobj in objs: 1028 # print('vobj.name', vobj.name) 1029 objs.append(vobj) 1030 except AttributeError: 1031 pass 1032 1033 ir = self.renderers.index(ren) 1034 1035 ids = [] 1036 for ob in set(objs): 1037 1038 # will remove it from internal list if possible 1039 try: 1040 idx = self.objects.index(ob) 1041 ids.append(idx) 1042 except ValueError: 1043 pass 1044 1045 if ren: ### remove it from the renderer 1046 1047 if isinstance(ob, vedo.addons.BaseCutter): 1048 ob.remove_from(self) # from cutters 1049 continue 1050 1051 try: 1052 ren.RemoveActor(ob) 1053 except TypeError: 1054 try: 1055 ren.RemoveActor(ob.actor) 1056 except AttributeError: 1057 pass 1058 1059 if hasattr(ob, "rendered_at"): 1060 ob.rendered_at.discard(ir) 1061 1062 if hasattr(ob, "scalarbar") and ob.scalarbar: 1063 ren.RemoveActor(ob.scalarbar) 1064 if hasattr(ob, "_caption") and ob._caption: 1065 ren.RemoveActor(ob._caption) 1066 if hasattr(ob, "shadows") and ob.shadows: 1067 for sha in ob.shadows: 1068 ren.RemoveActor(sha.actor) 1069 if hasattr(ob, "trail") and ob.trail: 1070 ren.RemoveActor(ob.trail.actor) 1071 ob.trail_points = [] 1072 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1073 for sha in ob.trail.shadows: 1074 ren.RemoveActor(sha.actor) 1075 1076 elif isinstance(ob, vedo.visual.LightKit): 1077 ob.lightkit.RemoveLightsFromRenderer(ren) 1078 1079 # for i in ids: # WRONG way of doing it! 1080 # del self.objects[i] 1081 # instead we do: 1082 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1083 return self 1084 1085 @property 1086 def actors(self): 1087 """Return the list of actors.""" 1088 return [ob.actor for ob in self.objects if hasattr(ob, "actor")] 1089 1090 def remove_lights(self) -> Self: 1091 """Remove all the present lights in the current renderer.""" 1092 if self.renderer: 1093 self.renderer.RemoveAllLights() 1094 return self 1095 1096 def pop(self, at=None) -> Self: 1097 """ 1098 Remove the last added object from the rendering window. 1099 This method is typically used in loops or callback functions. 1100 """ 1101 if at is not None and not isinstance(at, int): 1102 # wrong usage pitfall 1103 vedo.logger.error("argument of pop() must be an integer") 1104 raise RuntimeError() 1105 1106 if self.objects: 1107 self.remove(self.objects[-1], at) 1108 return self 1109 1110 def render(self, resetcam=False) -> Self: 1111 """Render the scene. This method is typically used in loops or callback functions.""" 1112 1113 if vedo.settings.dry_run_mode >= 2: 1114 return self 1115 1116 if not self.window: 1117 return self 1118 1119 self.initialize_interactor() 1120 1121 if resetcam: 1122 self.renderer.ResetCamera() 1123 1124 self.window.Render() 1125 1126 if self._cocoa_process_events and self.interactor and self.interactor.GetInitialized(): 1127 if "Darwin" in vedo.sys_platform and not self.offscreen: 1128 self.interactor.ProcessEvents() 1129 self._cocoa_process_events = False 1130 return self 1131 1132 def interactive(self) -> Self: 1133 """ 1134 Start window interaction. 1135 Analogous to `show(..., interactive=True)`. 1136 """ 1137 if vedo.settings.dry_run_mode >= 1: 1138 return self 1139 self.initialize_interactor() 1140 if self.interactor: 1141 # print("self.interactor.Start()") 1142 self.interactor.Start() 1143 # print("self.interactor.Start() done") 1144 if self._must_close_now: 1145 # print("self.interactor.TerminateApp()") 1146 if self.interactor: 1147 self.interactor.GetRenderWindow().Finalize() 1148 self.interactor.TerminateApp() 1149 self.interactor = None 1150 self.window = None 1151 self.renderer = None 1152 self.renderers = [] 1153 self.camera = None 1154 return self 1155 1156 def use_depth_peeling(self, at=None, value=True) -> Self: 1157 """ 1158 Specify whether use depth peeling algorithm at this specific renderer 1159 Call this method before the first rendering. 1160 """ 1161 if at is None: 1162 ren = self.renderer 1163 else: 1164 ren = self.renderers[at] 1165 ren.SetUseDepthPeeling(value) 1166 return self 1167 1168 def background(self, c1=None, c2=None, at=None, mode=0) -> Union[Self, "np.ndarray"]: 1169 """Set the color of the background for the current renderer. 1170 A different renderer index can be specified by keyword `at`. 1171 1172 Arguments: 1173 c1 : (list) 1174 background main color. 1175 c2 : (list) 1176 background color for the upper part of the window. 1177 at : (int) 1178 renderer index. 1179 mode : (int) 1180 background mode (needs vtk version >= 9.3) 1181 0 = vertical, 1182 1 = horizontal, 1183 2 = radial farthest side, 1184 3 = radia farthest corner. 1185 """ 1186 if not self.renderers: 1187 return self 1188 if at is None: 1189 r = self.renderer 1190 else: 1191 r = self.renderers[at] 1192 1193 if c1 is None and c2 is None: 1194 return np.array(r.GetBackground()) 1195 1196 if r: 1197 if c1 is not None: 1198 r.SetBackground(vedo.get_color(c1)) 1199 if c2 is not None: 1200 r.GradientBackgroundOn() 1201 r.SetBackground2(vedo.get_color(c2)) 1202 if mode: 1203 try: # only works with vtk>=9.3 1204 modes = [ 1205 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1206 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1207 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1208 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1209 ] 1210 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 1211 except AttributeError: 1212 pass 1213 1214 else: 1215 r.GradientBackgroundOff() 1216 return self 1217 1218 ################################################################## 1219 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1220 """ 1221 Return a list of Meshes from the specified renderer. 1222 1223 Arguments: 1224 at : (int) 1225 specify which renderer to look at. 1226 include_non_pickables : (bool) 1227 include non-pickable objects 1228 unpack_assemblies : (bool) 1229 unpack assemblies into their components 1230 """ 1231 if at is None: 1232 renderer = self.renderer 1233 at = self.renderers.index(renderer) 1234 elif isinstance(at, int): 1235 renderer = self.renderers[at] 1236 1237 has_global_axes = False 1238 if isinstance(self.axes_instances[at], vedo.Assembly): 1239 has_global_axes = True 1240 1241 if unpack_assemblies: 1242 acs = renderer.GetActors() 1243 else: 1244 acs = renderer.GetViewProps() 1245 1246 objs = [] 1247 acs.InitTraversal() 1248 for _ in range(acs.GetNumberOfItems()): 1249 1250 if unpack_assemblies: 1251 a = acs.GetNextItem() 1252 else: 1253 a = acs.GetNextProp() 1254 1255 if isinstance(a, vtki.vtkVolume): 1256 continue 1257 1258 if include_non_pickables or a.GetPickable(): 1259 if a == self.axes_instances[at]: 1260 continue 1261 if has_global_axes and a in self.axes_instances[at].actors: 1262 continue 1263 try: 1264 objs.append(a.retrieve_object()) 1265 except AttributeError: 1266 pass 1267 return objs 1268 1269 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1270 """ 1271 Return a list of Volumes from the specified renderer. 1272 1273 Arguments: 1274 at : (int) 1275 specify which renderer to look at 1276 include_non_pickables : (bool) 1277 include non-pickable objects 1278 """ 1279 if at is None: 1280 renderer = self.renderer 1281 at = self.renderers.index(renderer) 1282 elif isinstance(at, int): 1283 renderer = self.renderers[at] 1284 1285 vols = [] 1286 acs = renderer.GetVolumes() 1287 acs.InitTraversal() 1288 for _ in range(acs.GetNumberOfItems()): 1289 a = acs.GetNextItem() 1290 if include_non_pickables or a.GetPickable(): 1291 try: 1292 vols.append(a.retrieve_object()) 1293 except AttributeError: 1294 pass 1295 return vols 1296 1297 def get_actors(self, at=None, include_non_pickables=False) -> list: 1298 """ 1299 Return a list of Volumes from the specified renderer. 1300 1301 Arguments: 1302 at : (int) 1303 specify which renderer to look at 1304 include_non_pickables : (bool) 1305 include non-pickable objects 1306 """ 1307 if at is None: 1308 renderer = self.renderer 1309 at = self.renderers.index(renderer) 1310 elif isinstance(at, int): 1311 renderer = self.renderers[at] 1312 1313 acts = [] 1314 acs = renderer.GetViewProps() 1315 acs.InitTraversal() 1316 for _ in range(acs.GetNumberOfItems()): 1317 a = acs.GetNextProp() 1318 if include_non_pickables or a.GetPickable(): 1319 acts.append(a) 1320 return acts 1321 1322 def check_actors_trasform(self, at=None) -> Self: 1323 """ 1324 Reset the transformation matrix of all actors at specified renderer. 1325 This is only useful when actors have been moved/rotated/scaled manually 1326 in an already rendered scene using interactors like 1327 'TrackballActor' or 'JoystickActor'. 1328 """ 1329 # see issue https://github.com/marcomusy/vedo/issues/1046 1330 for a in self.get_actors(at=at, include_non_pickables=True): 1331 try: 1332 M = a.GetMatrix() 1333 except AttributeError: 1334 continue 1335 if M and not M.IsIdentity(): 1336 try: 1337 a.retrieve_object().apply_transform_from_actor() 1338 # vedo.logger.info( 1339 # f"object '{a.retrieve_object().name}' " 1340 # "was manually moved. Updated to its current position." 1341 # ) 1342 except AttributeError: 1343 pass 1344 return self 1345 1346 def reset_camera(self, tight=None) -> Self: 1347 """ 1348 Reset the camera position and zooming. 1349 If tight (float) is specified the zooming reserves a padding space 1350 in the xy-plane expressed in percent of the average size. 1351 """ 1352 if tight is None: 1353 self.renderer.ResetCamera() 1354 else: 1355 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1356 cam = self.camera 1357 1358 self.renderer.ComputeAspect() 1359 aspect = self.renderer.GetAspect() 1360 angle = np.pi * cam.GetViewAngle() / 180.0 1361 dx = x1 - x0 1362 dy = y1 - y0 1363 dist = max(dx / aspect[0], dy) / np.tan(angle / 2) / 2 1364 1365 cam.SetViewUp(0, 1, 0) 1366 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1367 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1368 if cam.GetParallelProjection(): 1369 ps = max(dx / aspect[0], dy) / 2 1370 cam.SetParallelScale(ps * (1 + tight)) 1371 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1372 return self 1373 1374 def reset_clipping_range(self, bounds=None) -> Self: 1375 """ 1376 Reset the camera clipping range to include all visible actors. 1377 If bounds is given, it will be used instead of computing it. 1378 """ 1379 if bounds is None: 1380 self.renderer.ResetCameraClippingRange() 1381 else: 1382 self.renderer.ResetCameraClippingRange(bounds) 1383 return self 1384 1385 def reset_viewup(self, smooth=True) -> Self: 1386 """ 1387 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1388 """ 1389 vbb = addons.compute_visible_bounds()[0] 1390 x0, x1, y0, y1, z0, z1 = vbb 1391 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1392 d = self.camera.GetDistance() 1393 1394 viewups = np.array( 1395 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1396 ) 1397 positions = np.array( 1398 [ 1399 (mx, my, mz + d), 1400 (mx, my, mz - d), 1401 (mx, my + d, mz), 1402 (mx, my - d, mz), 1403 (mx + d, my, mz), 1404 (mx - d, my, mz), 1405 ] 1406 ) 1407 1408 vu = np.array(self.camera.GetViewUp()) 1409 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1410 1411 poc = np.array(self.camera.GetPosition()) 1412 foc = np.array(self.camera.GetFocalPoint()) 1413 a = poc - foc 1414 b = positions - foc 1415 a = a / np.linalg.norm(a) 1416 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1417 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1418 1419 if smooth: 1420 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1421 for t in outtimes: 1422 vv = vu * (1 - t) + viewups[vui] * t 1423 pp = poc * (1 - t) + positions[pui] * t 1424 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1425 self.camera.SetViewUp(vv) 1426 self.camera.SetPosition(pp) 1427 self.camera.SetFocalPoint(ff) 1428 self.render() 1429 1430 # interpolator does not respect parallel view...: 1431 # cam1 = dict( 1432 # pos=poc, 1433 # viewup=vu, 1434 # focal_point=(mx,my,mz), 1435 # clipping_range=self.camera.GetClippingRange() 1436 # ) 1437 # # cam1 = self.camera 1438 # cam2 = dict( 1439 # pos=positions[pui], 1440 # viewup=viewups[vui], 1441 # focal_point=(mx,my,mz), 1442 # clipping_range=self.camera.GetClippingRange() 1443 # ) 1444 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1445 # for c in vcams: 1446 # self.renderer.SetActiveCamera(c) 1447 # self.render() 1448 else: 1449 1450 self.camera.SetViewUp(viewups[vui]) 1451 self.camera.SetPosition(positions[pui]) 1452 self.camera.SetFocalPoint(mx, my, mz) 1453 1454 self.renderer.ResetCameraClippingRange() 1455 1456 # vbb, _, _, _ = addons.compute_visible_bounds() 1457 # x0,x1, y0,y1, z0,z1 = vbb 1458 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1459 self.render() 1460 return self 1461 1462 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1463 """ 1464 Takes as input two cameras set camera at an interpolated position: 1465 1466 Cameras can be vtkCamera or dictionaries in format: 1467 1468 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1469 1470 Press `shift-C` key in interactive mode to dump a python snipplet 1471 of parameters for the current camera view. 1472 """ 1473 nc = len(cameras) 1474 if len(times) == 0: 1475 times = np.linspace(0, 1, num=nc, endpoint=True) 1476 1477 assert len(times) == nc 1478 1479 cin = vtki.new("CameraInterpolator") 1480 1481 # cin.SetInterpolationTypeToLinear() # buggy? 1482 if nc > 2 and smooth: 1483 cin.SetInterpolationTypeToSpline() 1484 1485 for i, cam in enumerate(cameras): 1486 vcam = cam 1487 if isinstance(cam, dict): 1488 vcam = utils.camera_from_dict(cam) 1489 cin.AddCamera(times[i], vcam) 1490 1491 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1492 rng = maxt - mint 1493 1494 if len(output_times) == 0: 1495 cin.InterpolateCamera(t * rng, self.camera) 1496 return [self.camera] 1497 else: 1498 vcams = [] 1499 for tt in output_times: 1500 c = vtki.vtkCamera() 1501 cin.InterpolateCamera(tt * rng, c) 1502 vcams.append(c) 1503 return vcams 1504 1505 def fly_to(self, point) -> Self: 1506 """ 1507 Fly camera to the specified point. 1508 1509 Arguments: 1510 point : (list) 1511 point in space to place camera. 1512 1513 Example: 1514 ```python 1515 from vedo import * 1516 cone = Cone() 1517 plt = Plotter(axes=1) 1518 plt.show(cone) 1519 plt.fly_to([1,0,0]) 1520 plt.interactive().close() 1521 ``` 1522 """ 1523 if self.interactor: 1524 self.resetcam = False 1525 self.interactor.FlyTo(self.renderer, point) 1526 return self 1527 1528 def look_at(self, plane="xy") -> Self: 1529 """Move the camera so that it looks at the specified cartesian plane""" 1530 cam = self.renderer.GetActiveCamera() 1531 fp = np.array(cam.GetFocalPoint()) 1532 p = np.array(cam.GetPosition()) 1533 dist = np.linalg.norm(fp - p) 1534 plane = plane.lower() 1535 if "x" in plane and "y" in plane: 1536 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1537 cam.SetViewUp(0.0, 1.0, 0.0) 1538 elif "x" in plane and "z" in plane: 1539 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1540 cam.SetViewUp(0.0, 0.0, 1.0) 1541 elif "y" in plane and "z" in plane: 1542 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1543 cam.SetViewUp(0.0, 0.0, 1.0) 1544 else: 1545 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1546 return self 1547 1548 def record(self, filename="") -> str: 1549 """ 1550 Record camera, mouse, keystrokes and all other events. 1551 Recording can be toggled on/off by pressing key "R". 1552 1553 Arguments: 1554 filename : (str) 1555 ascii file to store events. 1556 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1557 1558 Returns: 1559 a string descriptor of events. 1560 1561 Examples: 1562 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1563 """ 1564 if vedo.settings.dry_run_mode >= 1: 1565 return "" 1566 if not self.interactor: 1567 vedo.logger.warning("Cannot record events, no interactor defined.") 1568 return "" 1569 erec = vtki.new("InteractorEventRecorder") 1570 erec.SetInteractor(self.interactor) 1571 if not filename: 1572 if not os.path.exists(vedo.settings.cache_directory): 1573 os.makedirs(vedo.settings.cache_directory) 1574 home_dir = os.path.expanduser("~") 1575 filename = os.path.join( 1576 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1577 print("Events will be recorded in", filename) 1578 erec.SetFileName(filename) 1579 erec.SetKeyPressActivationValue("R") 1580 erec.EnabledOn() 1581 erec.Record() 1582 self.interactor.Start() 1583 erec.Stop() 1584 erec.EnabledOff() 1585 with open(filename, "r", encoding="UTF-8") as fl: 1586 events = fl.read() 1587 erec = None 1588 return events 1589 1590 def play(self, recorded_events="", repeats=0) -> Self: 1591 """ 1592 Play camera, mouse, keystrokes and all other events. 1593 1594 Arguments: 1595 events : (str) 1596 file o string of events. 1597 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1598 repeats : (int) 1599 number of extra repeats of the same events. The default is 0. 1600 1601 Examples: 1602 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1603 """ 1604 if vedo.settings.dry_run_mode >= 1: 1605 return self 1606 if not self.interactor: 1607 vedo.logger.warning("Cannot play events, no interactor defined.") 1608 return self 1609 1610 erec = vtki.new("InteractorEventRecorder") 1611 erec.SetInteractor(self.interactor) 1612 1613 if not recorded_events: 1614 home_dir = os.path.expanduser("~") 1615 recorded_events = os.path.join( 1616 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1617 1618 if recorded_events.endswith(".log"): 1619 erec.ReadFromInputStringOff() 1620 erec.SetFileName(recorded_events) 1621 else: 1622 erec.ReadFromInputStringOn() 1623 erec.SetInputString(recorded_events) 1624 1625 erec.Play() 1626 for _ in range(repeats): 1627 erec.Rewind() 1628 erec.Play() 1629 erec.EnabledOff() 1630 erec = None 1631 return self 1632 1633 def parallel_projection(self, value=True, at=None) -> Self: 1634 """ 1635 Use parallel projection `at` a specified renderer. 1636 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1637 An input value equal to -1 will toggle it on/off. 1638 """ 1639 if at is not None: 1640 r = self.renderers[at] 1641 else: 1642 r = self.renderer 1643 if value == -1: 1644 val = r.GetActiveCamera().GetParallelProjection() 1645 value = not val 1646 r.GetActiveCamera().SetParallelProjection(value) 1647 r.Modified() 1648 return self 1649 1650 def render_hidden_lines(self, value=True) -> Self: 1651 """Remove hidden lines when in wireframe mode.""" 1652 self.renderer.SetUseHiddenLineRemoval(not value) 1653 return self 1654 1655 def fov(self, angle: float) -> Self: 1656 """ 1657 Set the field of view angle for the camera. 1658 This is the angle of the camera frustum in the horizontal direction. 1659 High values will result in a wide-angle lens (fish-eye effect), 1660 and low values will result in a telephoto lens. 1661 1662 Default value is 30 degrees. 1663 """ 1664 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1665 self.renderer.GetActiveCamera().SetViewAngle(angle) 1666 return self 1667 1668 def zoom(self, zoom: float) -> Self: 1669 """Apply a zooming factor for the current camera view""" 1670 self.renderer.GetActiveCamera().Zoom(zoom) 1671 return self 1672 1673 def azimuth(self, angle: float) -> Self: 1674 """Rotate camera around the view up vector.""" 1675 self.renderer.GetActiveCamera().Azimuth(angle) 1676 return self 1677 1678 def elevation(self, angle: float) -> Self: 1679 """Rotate the camera around the cross product of the negative 1680 of the direction of projection and the view up vector.""" 1681 self.renderer.GetActiveCamera().Elevation(angle) 1682 return self 1683 1684 def roll(self, angle: float) -> Self: 1685 """Roll the camera about the direction of projection.""" 1686 self.renderer.GetActiveCamera().Roll(angle) 1687 return self 1688 1689 def dolly(self, value: float) -> Self: 1690 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1691 self.renderer.GetActiveCamera().Dolly(value) 1692 return self 1693 1694 ################################################################## 1695 def add_slider( 1696 self, 1697 sliderfunc, 1698 xmin, 1699 xmax, 1700 value=None, 1701 pos=4, 1702 title="", 1703 font="Calco", 1704 title_size=1, 1705 c=None, 1706 alpha=1, 1707 show_value=True, 1708 delayed=False, 1709 **options, 1710 ) -> "vedo.addons.Slider2D": 1711 """ 1712 Add a `vedo.addons.Slider2D` which can call an external custom function. 1713 1714 Arguments: 1715 sliderfunc : (Callable) 1716 external function to be called by the widget 1717 xmin : (float) 1718 lower value of the slider 1719 xmax : (float) 1720 upper value 1721 value : (float) 1722 current value 1723 pos : (list, str) 1724 position corner number: horizontal [1-5] or vertical [11-15] 1725 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1726 and also by a string descriptor (eg. "bottom-left") 1727 title : (str) 1728 title text 1729 font : (str) 1730 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1731 title_size : (float) 1732 title text scale [1.0] 1733 show_value : (bool) 1734 if True current value is shown 1735 delayed : (bool) 1736 if True the callback is delayed until when the mouse button is released 1737 alpha : (float) 1738 opacity of the scalar bar texts 1739 slider_length : (float) 1740 slider length 1741 slider_width : (float) 1742 slider width 1743 end_cap_length : (float) 1744 length of the end cap 1745 end_cap_width : (float) 1746 width of the end cap 1747 tube_width : (float) 1748 width of the tube 1749 title_height : (float) 1750 width of the title 1751 tformat : (str) 1752 format of the title 1753 1754 Examples: 1755 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1756 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1757 1758 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1759 """ 1760 if c is None: # automatic black or white 1761 c = (0.8, 0.8, 0.8) 1762 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1763 c = (0.2, 0.2, 0.2) 1764 else: 1765 c = vedo.get_color(c) 1766 1767 slider2d = addons.Slider2D( 1768 sliderfunc, 1769 xmin, 1770 xmax, 1771 value, 1772 pos, 1773 title, 1774 font, 1775 title_size, 1776 c, 1777 alpha, 1778 show_value, 1779 delayed, 1780 **options, 1781 ) 1782 1783 if self.renderer: 1784 slider2d.renderer = self.renderer 1785 if self.interactor: 1786 slider2d.interactor = self.interactor 1787 slider2d.on() 1788 self.sliders.append([slider2d, sliderfunc]) 1789 return slider2d 1790 1791 def add_slider3d( 1792 self, 1793 sliderfunc, 1794 pos1, 1795 pos2, 1796 xmin, 1797 xmax, 1798 value=None, 1799 s=0.03, 1800 t=1, 1801 title="", 1802 rotation=0.0, 1803 c=None, 1804 show_value=True, 1805 ) -> "vedo.addons.Slider3D": 1806 """ 1807 Add a 3D slider widget which can call an external custom function. 1808 1809 Arguments: 1810 sliderfunc : (function) 1811 external function to be called by the widget 1812 pos1 : (list) 1813 first position 3D coordinates 1814 pos2 : (list) 1815 second position coordinates 1816 xmin : (float) 1817 lower value 1818 xmax : (float) 1819 upper value 1820 value : (float) 1821 initial value 1822 s : (float) 1823 label scaling factor 1824 t : (float) 1825 tube scaling factor 1826 title : (str) 1827 title text 1828 c : (color) 1829 slider color 1830 rotation : (float) 1831 title rotation around slider axis 1832 show_value : (bool) 1833 if True current value is shown 1834 1835 Examples: 1836 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1837 1838 ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png) 1839 """ 1840 if c is None: # automatic black or white 1841 c = (0.8, 0.8, 0.8) 1842 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1843 c = (0.2, 0.2, 0.2) 1844 else: 1845 c = vedo.get_color(c) 1846 1847 slider3d = addons.Slider3D( 1848 sliderfunc, 1849 pos1, 1850 pos2, 1851 xmin, 1852 xmax, 1853 value, 1854 s, 1855 t, 1856 title, 1857 rotation, 1858 c, 1859 show_value, 1860 ) 1861 slider3d.renderer = self.renderer 1862 slider3d.interactor = self.interactor 1863 slider3d.on() 1864 self.sliders.append([slider3d, sliderfunc]) 1865 return slider3d 1866 1867 def add_button( 1868 self, 1869 fnc=None, 1870 states=("On", "Off"), 1871 c=("w", "w"), 1872 bc=("green4", "red4"), 1873 pos=(0.7, 0.1), 1874 size=24, 1875 font="Courier", 1876 bold=True, 1877 italic=False, 1878 alpha=1, 1879 angle=0, 1880 ) -> Union["vedo.addons.Button", None]: 1881 """ 1882 Add a button to the renderer window. 1883 1884 Arguments: 1885 states : (list) 1886 a list of possible states, e.g. ['On', 'Off'] 1887 c : (list) 1888 a list of colors for each state 1889 bc : (list) 1890 a list of background colors for each state 1891 pos : (list) 1892 2D position from left-bottom corner 1893 size : (float) 1894 size of button font 1895 font : (str) 1896 font type. Check [available fonts here](https://vedo.embl.es/fonts). 1897 bold : (bool) 1898 bold font face (False) 1899 italic : (bool) 1900 italic font face (False) 1901 alpha : (float) 1902 opacity level 1903 angle : (float) 1904 anticlockwise rotation in degrees 1905 1906 Returns: 1907 `vedo.addons.Button` object. 1908 1909 Examples: 1910 - [buttons1.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons1.py) 1911 - [buttons2.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons2.py) 1912 1913 ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg) 1914 """ 1915 if self.interactor: 1916 bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle) 1917 self.renderer.AddActor2D(bu) 1918 self.buttons.append(bu) 1919 # bu.function_id = self.add_callback("LeftButtonPress", bu.function) # not good 1920 bu.function_id = bu.add_observer("pick", bu.function, priority=10) 1921 return bu 1922 return None 1923 1924 def add_spline_tool( 1925 self, points, pc="k", ps=8, lc="r4", ac="g5", 1926 lw=2, alpha=1, closed=False, ontop=True, can_add_nodes=True, 1927 ) -> "vedo.addons.SplineTool": 1928 """ 1929 Add a spline tool to the current plotter. 1930 Nodes of the spline can be dragged in space with the mouse. 1931 Clicking on the line itself adds an extra point. 1932 Selecting a point and pressing del removes it. 1933 1934 Arguments: 1935 points : (Mesh, Points, array) 1936 the set of vertices forming the spline nodes. 1937 pc : (str) 1938 point color. The default is 'k'. 1939 ps : (str) 1940 point size. The default is 8. 1941 lc : (str) 1942 line color. The default is 'r4'. 1943 ac : (str) 1944 active point marker color. The default is 'g5'. 1945 lw : (int) 1946 line width. The default is 2. 1947 alpha : (float) 1948 line transparency. 1949 closed : (bool) 1950 spline is meant to be closed. The default is False. 1951 1952 Returns: 1953 a `SplineTool` object. 1954 1955 Examples: 1956 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1957 1958 ![](https://vedo.embl.es/images/basic/spline_tool.png) 1959 """ 1960 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1961 sw.interactor = self.interactor 1962 sw.on() 1963 sw.Initialize(sw.points.dataset) 1964 sw.representation.SetRenderer(self.renderer) 1965 sw.representation.SetClosedLoop(closed) 1966 sw.representation.BuildRepresentation() 1967 self.widgets.append(sw) 1968 return sw 1969 1970 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1971 """Add an inset icon mesh into the same renderer. 1972 1973 Arguments: 1974 pos : (int, list) 1975 icon position in the range [1-4] indicating one of the 4 corners, 1976 or it can be a tuple (x,y) as a fraction of the renderer size. 1977 size : (float) 1978 size of the square inset. 1979 1980 Examples: 1981 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1982 """ 1983 iconw = addons.Icon(icon, pos, size) 1984 1985 iconw.SetInteractor(self.interactor) 1986 iconw.EnabledOn() 1987 iconw.InteractiveOff() 1988 self.widgets.append(iconw) 1989 return iconw 1990 1991 def add_global_axes(self, axtype=None, c=None) -> Self: 1992 """Draw axes on scene. Available axes types: 1993 1994 Arguments: 1995 axtype : (int) 1996 - 0, no axes, 1997 - 1, draw three gray grid walls 1998 - 2, show cartesian axes from (0,0,0) 1999 - 3, show positive range of cartesian axes from (0,0,0) 2000 - 4, show a triad at bottom left 2001 - 5, show a cube at bottom left 2002 - 6, mark the corners of the bounding box 2003 - 7, draw a 3D ruler at each side of the cartesian axes 2004 - 8, show the vtkCubeAxesActor object 2005 - 9, show the bounding box outLine 2006 - 10, show three circles representing the maximum bounding box 2007 - 11, show a large grid on the x-y plane 2008 - 12, show polar axes 2009 - 13, draw a simple ruler at the bottom of the window 2010 2011 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 2012 2013 Example: 2014 ```python 2015 from vedo import Box, show 2016 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 2017 show( 2018 b, 2019 axes={ 2020 "xtitle": "Some long variable [a.u.]", 2021 "number_of_divisions": 4, 2022 # ... 2023 }, 2024 ) 2025 ``` 2026 2027 Examples: 2028 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2029 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2030 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2031 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2032 2033 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2034 """ 2035 addons.add_global_axes(axtype, c) 2036 return self 2037 2038 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2039 """Add a legend to the top right. 2040 2041 Examples: 2042 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 2043 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 2044 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 2045 """ 2046 acts = self.get_meshes() 2047 lb = addons.LegendBox(acts, **kwargs) 2048 self.add(lb) 2049 return lb 2050 2051 def add_hint( 2052 self, 2053 obj, 2054 text="", 2055 c="k", 2056 bg="yellow9", 2057 font="Calco", 2058 size=18, 2059 justify=0, 2060 angle=0, 2061 delay=250, 2062 ) -> Union[vtki.vtkBalloonWidget, None]: 2063 """ 2064 Create a pop-up hint style message when hovering an object. 2065 Use `add_hint(obj, False)` to disable a hinting a specific object. 2066 Use `add_hint(None)` to disable all hints. 2067 2068 Arguments: 2069 obj : (Mesh, Points) 2070 the object to associate the pop-up to 2071 text : (str) 2072 string description of the pop-up 2073 delay : (int) 2074 milliseconds to wait before pop-up occurs 2075 """ 2076 if self.offscreen or not self.interactor: 2077 return None 2078 2079 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2080 # Linux vtk9.0 is bugged 2081 vedo.logger.warning( 2082 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2083 ) 2084 return None 2085 2086 if obj is None: 2087 self.hint_widget.EnabledOff() 2088 self.hint_widget.SetInteractor(None) 2089 self.hint_widget = None 2090 return self.hint_widget 2091 2092 if text is False and self.hint_widget: 2093 self.hint_widget.RemoveBalloon(obj) 2094 return self.hint_widget 2095 2096 if text == "": 2097 if obj.name: 2098 text = obj.name 2099 elif obj.filename: 2100 text = obj.filename 2101 else: 2102 return None 2103 2104 if not self.hint_widget: 2105 self.hint_widget = vtki.vtkBalloonWidget() 2106 2107 rep = self.hint_widget.GetRepresentation() 2108 rep.SetBalloonLayoutToImageRight() 2109 2110 trep = rep.GetTextProperty() 2111 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2112 trep.SetFontFile(utils.get_font_path(font)) 2113 trep.SetFontSize(size) 2114 trep.SetColor(vedo.get_color(c)) 2115 trep.SetBackgroundColor(vedo.get_color(bg)) 2116 trep.SetShadow(0) 2117 trep.SetJustification(justify) 2118 trep.UseTightBoundingBoxOn() 2119 2120 self.hint_widget.ManagesCursorOff() 2121 self.hint_widget.SetTimerDuration(delay) 2122 self.hint_widget.SetInteractor(self.interactor) 2123 if angle: 2124 trep.SetOrientation(angle) 2125 trep.SetBackgroundOpacity(0) 2126 # else: 2127 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2128 self.hint_widget.SetRepresentation(rep) 2129 self.widgets.append(self.hint_widget) 2130 self.hint_widget.EnabledOn() 2131 2132 bst = self.hint_widget.GetBalloonString(obj.actor) 2133 if bst: 2134 self.hint_widget.UpdateBalloonString(obj.actor, text) 2135 else: 2136 self.hint_widget.AddBalloon(obj.actor, text) 2137 2138 return self.hint_widget 2139 2140 def add_shadows(self) -> Self: 2141 """Add shadows at the current renderer.""" 2142 if self.renderer: 2143 shadows = vtki.new("ShadowMapPass") 2144 seq = vtki.new("SequencePass") 2145 passes = vtki.new("RenderPassCollection") 2146 passes.AddItem(shadows.GetShadowMapBakerPass()) 2147 passes.AddItem(shadows) 2148 seq.SetPasses(passes) 2149 camerapass = vtki.new("CameraPass") 2150 camerapass.SetDelegatePass(seq) 2151 self.renderer.SetPass(camerapass) 2152 return self 2153 2154 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> Self: 2155 """ 2156 Screen Space Ambient Occlusion. 2157 2158 For every pixel on the screen, the pixel shader samples the depth values around 2159 the current pixel and tries to compute the amount of occlusion from each of the sampled 2160 points. 2161 2162 Arguments: 2163 radius : (float) 2164 radius of influence in absolute units 2165 bias : (float) 2166 bias of the normals 2167 blur : (bool) 2168 add a blurring to the sampled positions 2169 samples : (int) 2170 number of samples to probe 2171 2172 Examples: 2173 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2174 2175 ![](https://vedo.embl.es/images/basic/ssao.jpg) 2176 """ 2177 lights = vtki.new("LightsPass") 2178 2179 opaque = vtki.new("OpaquePass") 2180 2181 ssaoCam = vtki.new("CameraPass") 2182 ssaoCam.SetDelegatePass(opaque) 2183 2184 ssao = vtki.new("SSAOPass") 2185 ssao.SetRadius(radius) 2186 ssao.SetBias(bias) 2187 ssao.SetBlur(blur) 2188 ssao.SetKernelSize(samples) 2189 ssao.SetDelegatePass(ssaoCam) 2190 2191 translucent = vtki.new("TranslucentPass") 2192 2193 volpass = vtki.new("VolumetricPass") 2194 ddp = vtki.new("DualDepthPeelingPass") 2195 ddp.SetTranslucentPass(translucent) 2196 ddp.SetVolumetricPass(volpass) 2197 2198 over = vtki.new("OverlayPass") 2199 2200 collection = vtki.new("RenderPassCollection") 2201 collection.AddItem(lights) 2202 collection.AddItem(ssao) 2203 collection.AddItem(ddp) 2204 collection.AddItem(over) 2205 2206 sequence = vtki.new("SequencePass") 2207 sequence.SetPasses(collection) 2208 2209 cam = vtki.new("CameraPass") 2210 cam.SetDelegatePass(sequence) 2211 2212 self.renderer.SetPass(cam) 2213 return self 2214 2215 def add_depth_of_field(self, autofocus=True) -> Self: 2216 """Add a depth of field effect in the scene.""" 2217 lights = vtki.new("LightsPass") 2218 2219 opaque = vtki.new("OpaquePass") 2220 2221 dofCam = vtki.new("CameraPass") 2222 dofCam.SetDelegatePass(opaque) 2223 2224 dof = vtki.new("DepthOfFieldPass") 2225 dof.SetAutomaticFocalDistance(autofocus) 2226 dof.SetDelegatePass(dofCam) 2227 2228 collection = vtki.new("RenderPassCollection") 2229 collection.AddItem(lights) 2230 collection.AddItem(dof) 2231 2232 sequence = vtki.new("SequencePass") 2233 sequence.SetPasses(collection) 2234 2235 cam = vtki.new("CameraPass") 2236 cam.SetDelegatePass(sequence) 2237 2238 self.renderer.SetPass(cam) 2239 return self 2240 2241 def _add_skybox(self, hdrfile: str) -> Self: 2242 # many hdr files are at https://polyhaven.com/all 2243 2244 reader = vtki.new("HDRReader") 2245 # Check the image can be read. 2246 if not reader.CanReadFile(hdrfile): 2247 vedo.logger.error(f"Cannot read HDR file {hdrfile}") 2248 return self 2249 reader.SetFileName(hdrfile) 2250 reader.Update() 2251 2252 texture = vtki.vtkTexture() 2253 texture.SetColorModeToDirectScalars() 2254 texture.SetInputData(reader.GetOutput()) 2255 2256 # Convert to a cube map 2257 tcm = vtki.new("EquirectangularToCubeMapTexture") 2258 tcm.SetInputTexture(texture) 2259 # Enable mipmapping to handle HDR image 2260 tcm.MipmapOn() 2261 tcm.InterpolateOn() 2262 2263 self.renderer.SetEnvironmentTexture(tcm) 2264 self.renderer.UseImageBasedLightingOn() 2265 self.skybox = vtki.new("Skybox") 2266 self.skybox.SetTexture(tcm) 2267 self.renderer.AddActor(self.skybox) 2268 return self 2269 2270 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None) -> "vedo.addons.RendererFrame": 2271 """ 2272 Add a frame to the renderer subwindow. 2273 2274 Arguments: 2275 c : (color) 2276 color name or index 2277 alpha : (float) 2278 opacity level 2279 lw : (int) 2280 line width in pixels. 2281 padding : (float) 2282 padding space in pixels. 2283 """ 2284 if c is None: # automatic black or white 2285 c = (0.9, 0.9, 0.9) 2286 if self.renderer: 2287 if np.sum(self.renderer.GetBackground()) > 1.5: 2288 c = (0.1, 0.1, 0.1) 2289 renf = addons.RendererFrame(c, alpha, lw, padding) 2290 if renf: 2291 self.renderer.AddActor(renf) 2292 return renf 2293 2294 def add_hover_legend( 2295 self, 2296 at=None, 2297 c=None, 2298 pos="bottom-left", 2299 font="Calco", 2300 s=0.75, 2301 bg="auto", 2302 alpha=0.1, 2303 maxlength=24, 2304 use_info=False, 2305 ) -> int: 2306 """ 2307 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2308 2309 The created text object are stored in `plotter.hover_legends`. 2310 2311 Returns: 2312 the id of the callback function. 2313 2314 Arguments: 2315 c : (color) 2316 Text color. If None then black or white is chosen automatically 2317 pos : (str) 2318 text positioning 2319 font : (str) 2320 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2321 s : (float) 2322 text size scale 2323 bg : (color) 2324 background color of the 2D box containing the text 2325 alpha : (float) 2326 box transparency 2327 maxlength : (int) 2328 maximum number of characters per line 2329 use_info : (bool) 2330 visualize the content of the `obj.info` attribute 2331 2332 Examples: 2333 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2334 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2335 2336 ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg) 2337 """ 2338 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2339 2340 if at is None: 2341 at = self.renderers.index(self.renderer) 2342 2343 def _legfunc(evt): 2344 if not evt.object or not self.renderer or at != evt.at: 2345 if hoverlegend.mapper.GetInput(): # clear and return 2346 hoverlegend.mapper.SetInput("") 2347 self.render() 2348 return 2349 2350 if use_info: 2351 if hasattr(evt.object, "info"): 2352 t = str(evt.object.info) 2353 else: 2354 return 2355 else: 2356 t, tp = "", "" 2357 if evt.isMesh: 2358 tp = "Mesh " 2359 elif evt.isPoints: 2360 tp = "Points " 2361 elif evt.isVolume: 2362 tp = "Volume " 2363 elif evt.isImage: 2364 tp = "Image " 2365 elif evt.isAssembly: 2366 tp = "Assembly " 2367 else: 2368 return 2369 2370 if evt.isAssembly: 2371 if not evt.object.name: 2372 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2373 else: 2374 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2375 else: 2376 if evt.object.name: 2377 t += f"{tp}name" 2378 if evt.isPoints: 2379 t += " " 2380 if evt.isMesh: 2381 t += " " 2382 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2383 2384 if evt.object.filename: 2385 t += f"{tp}filename: " 2386 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2387 t += "\n" 2388 if not evt.object.file_size: 2389 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2390 if evt.object.file_size: 2391 t += " : " 2392 sz, created = evt.object.file_size, evt.object.created 2393 t += f"{created[4:-5]} ({sz})" + "\n" 2394 2395 if evt.isPoints: 2396 indata = evt.object.dataset 2397 if indata.GetNumberOfPoints(): 2398 t += ( 2399 f"#points/cells: {indata.GetNumberOfPoints()}" 2400 f" / {indata.GetNumberOfCells()}" 2401 ) 2402 pdata = indata.GetPointData() 2403 cdata = indata.GetCellData() 2404 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2405 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2406 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2407 t += " *" 2408 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2409 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2410 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2411 t += " *" 2412 2413 if evt.isImage: 2414 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2415 t += f"\nImage shape: {evt.object.shape}" 2416 pcol = self.color_picker(evt.picked2d) 2417 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2418 2419 # change box color if needed in 'auto' mode 2420 if evt.isPoints and "auto" in str(bg): 2421 actcol = evt.object.properties.GetColor() 2422 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2423 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2424 2425 # adapt to changes in bg color 2426 bgcol = self.renderers[at].GetBackground() 2427 _bgcol = c 2428 if _bgcol is None: # automatic black or white 2429 _bgcol = (0.9, 0.9, 0.9) 2430 if sum(bgcol) > 1.5: 2431 _bgcol = (0.1, 0.1, 0.1) 2432 if len(set(_bgcol).intersection(bgcol)) < 3: 2433 hoverlegend.color(_bgcol) 2434 2435 if hoverlegend.mapper.GetInput() != t: 2436 hoverlegend.mapper.SetInput(t) 2437 self.interactor.Render() 2438 2439 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2440 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2441 2442 self.add(hoverlegend, at=at) 2443 self.hover_legends.append(hoverlegend) 2444 idcall = self.add_callback("MouseMove", _legfunc) 2445 return idcall 2446 2447 def add_scale_indicator( 2448 self, 2449 pos=(0.7, 0.05), 2450 s=0.02, 2451 length=2, 2452 lw=4, 2453 c="k1", 2454 alpha=1, 2455 units="", 2456 gap=0.05, 2457 ) -> Union["vedo.visual.Actor2D", None]: 2458 """ 2459 Add a Scale Indicator. Only works in parallel mode (no perspective). 2460 2461 Arguments: 2462 pos : (list) 2463 fractional (x,y) position on the screen. 2464 s : (float) 2465 size of the text. 2466 length : (float) 2467 length of the line. 2468 units : (str) 2469 string to show units. 2470 gap : (float) 2471 separation of line and text. 2472 2473 Example: 2474 ```python 2475 from vedo import settings, Cube, Plotter 2476 settings.use_parallel_projection = True # or else it does not make sense! 2477 cube = Cube().alpha(0.2) 2478 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2479 plt.add_scale_indicator(units='um', c='blue4') 2480 plt.show(cube, "Scale indicator with units").close() 2481 ``` 2482 ![](https://vedo.embl.es/images/feats/scale_indicator.png) 2483 """ 2484 # Note that this cannot go in addons.py 2485 # because it needs callbacks and window size 2486 if not self.interactor: 2487 return None 2488 2489 ppoints = vtki.vtkPoints() # Generate the polyline 2490 psqr = [[0.0, gap], [length / 10, gap]] 2491 dd = psqr[1][0] - psqr[0][0] 2492 for i, pt in enumerate(psqr): 2493 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2494 lines = vtki.vtkCellArray() 2495 lines.InsertNextCell(len(psqr)) 2496 for i in range(len(psqr)): 2497 lines.InsertCellPoint(i) 2498 pd = vtki.vtkPolyData() 2499 pd.SetPoints(ppoints) 2500 pd.SetLines(lines) 2501 2502 wsx, wsy = self.window.GetSize() 2503 if not self.camera.GetParallelProjection(): 2504 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2505 return None 2506 2507 rlabel = vtki.new("VectorText") 2508 rlabel.SetText("scale") 2509 tf = vtki.new("TransformPolyDataFilter") 2510 tf.SetInputConnection(rlabel.GetOutputPort()) 2511 t = vtki.vtkTransform() 2512 t.Scale(s * wsy / wsx, s, 1) 2513 tf.SetTransform(t) 2514 2515 app = vtki.new("AppendPolyData") 2516 app.AddInputConnection(tf.GetOutputPort()) 2517 app.AddInputData(pd) 2518 2519 mapper = vtki.new("PolyDataMapper2D") 2520 mapper.SetInputConnection(app.GetOutputPort()) 2521 cs = vtki.vtkCoordinate() 2522 cs.SetCoordinateSystem(1) 2523 mapper.SetTransformCoordinate(cs) 2524 2525 fractor = vedo.visual.Actor2D() 2526 csys = fractor.GetPositionCoordinate() 2527 csys.SetCoordinateSystem(3) 2528 fractor.SetPosition(pos) 2529 fractor.SetMapper(mapper) 2530 fractor.GetProperty().SetColor(vedo.get_color(c)) 2531 fractor.GetProperty().SetOpacity(alpha) 2532 fractor.GetProperty().SetLineWidth(lw) 2533 fractor.GetProperty().SetDisplayLocationToForeground() 2534 2535 def sifunc(iren, ev): 2536 wsx, wsy = self.window.GetSize() 2537 ps = self.camera.GetParallelScale() 2538 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2539 if units: 2540 newtxt += " " + units 2541 if rlabel.GetText() != newtxt: 2542 rlabel.SetText(newtxt) 2543 2544 self.renderer.AddActor(fractor) 2545 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2546 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2547 self.interactor.AddObserver("InteractionEvent", sifunc) 2548 sifunc(0, 0) 2549 return fractor 2550 2551 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2552 """ 2553 Create an Event object with information of what was clicked. 2554 2555 If `enable_picking` is False, no picking will be performed. 2556 This can be useful to avoid double picking when using buttons. 2557 """ 2558 if not self.interactor: 2559 return Event() 2560 2561 if len(pos) > 0: 2562 x, y = pos 2563 self.interactor.SetEventPosition(pos) 2564 else: 2565 x, y = self.interactor.GetEventPosition() 2566 self.renderer = self.interactor.FindPokedRenderer(x, y) 2567 2568 self.picked2d = (x, y) 2569 2570 key = self.interactor.GetKeySym() 2571 2572 if key: 2573 if "_L" in key or "_R" in key: 2574 # skip things like Shift_R 2575 key = "" # better than None 2576 else: 2577 if self.interactor.GetShiftKey(): 2578 key = key.upper() 2579 2580 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2581 key = "underscore" 2582 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2583 key = "plus" 2584 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2585 key = "?" 2586 2587 if self.interactor.GetControlKey(): 2588 key = "Ctrl+" + key 2589 2590 if self.interactor.GetAltKey(): 2591 key = "Alt+" + key 2592 2593 if enable_picking: 2594 if not self.picker: 2595 self.picker = vtki.vtkPropPicker() 2596 2597 self.picker.PickProp(x, y, self.renderer) 2598 actor = self.picker.GetProp3D() 2599 # Note that GetProp3D already picks Assembly 2600 2601 xp, yp = self.interactor.GetLastEventPosition() 2602 dx, dy = x - xp, y - yp 2603 2604 delta3d = np.array([0, 0, 0]) 2605 2606 if actor: 2607 picked3d = np.array(self.picker.GetPickPosition()) 2608 2609 try: 2610 vobj = actor.retrieve_object() 2611 old_pt = np.asarray(vobj.picked3d) 2612 vobj.picked3d = picked3d 2613 delta3d = picked3d - old_pt 2614 except (AttributeError, TypeError): 2615 pass 2616 2617 else: 2618 picked3d = None 2619 2620 if not actor: # try 2D 2621 actor = self.picker.GetActor2D() 2622 2623 event = Event() 2624 event.name = ename 2625 event.title = self.title 2626 event.id = -1 # will be set by the timer wrapper function 2627 event.timerid = -1 # will be set by the timer wrapper function 2628 event.priority = -1 # will be set by the timer wrapper function 2629 event.time = time.time() 2630 event.at = self.renderers.index(self.renderer) 2631 event.keypress = key 2632 if enable_picking: 2633 try: 2634 event.object = actor.retrieve_object() 2635 except AttributeError: 2636 event.object = actor 2637 try: 2638 event.actor = actor.retrieve_object() # obsolete use object instead 2639 except AttributeError: 2640 event.actor = actor 2641 event.picked3d = picked3d 2642 event.picked2d = (x, y) 2643 event.delta2d = (dx, dy) 2644 event.angle2d = np.arctan2(dy, dx) 2645 event.speed2d = np.sqrt(dx * dx + dy * dy) 2646 event.delta3d = delta3d 2647 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2648 event.isPoints = isinstance(event.object, vedo.Points) 2649 event.isMesh = isinstance(event.object, vedo.Mesh) 2650 event.isAssembly = isinstance(event.object, vedo.Assembly) 2651 event.isVolume = isinstance(event.object, vedo.Volume) 2652 event.isImage = isinstance(event.object, vedo.Image) 2653 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2654 return event 2655 2656 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2657 """ 2658 Add a function to be executed while show() is active. 2659 2660 Return a unique id for the callback. 2661 2662 The callback function (see example below) exposes a dictionary 2663 with the following information: 2664 - `name`: event name, 2665 - `id`: event unique identifier, 2666 - `priority`: event priority (float), 2667 - `interactor`: the interactor object, 2668 - `at`: renderer nr. where the event occurred 2669 - `keypress`: key pressed as string 2670 - `actor`: object picked by the mouse 2671 - `picked3d`: point picked in world coordinates 2672 - `picked2d`: screen coords of the mouse pointer 2673 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2674 - `delta3d`: ...same but in 3D world coords 2675 - `angle2d`: angle of mouse movement on screen 2676 - `speed2d`: speed of mouse movement on screen 2677 - `speed3d`: speed of picked point in world coordinates 2678 - `isPoints`: True if of class 2679 - `isMesh`: True if of class 2680 - `isAssembly`: True if of class 2681 - `isVolume`: True if of class Volume 2682 - `isImage`: True if of class 2683 2684 If `enable_picking` is False, no picking will be performed. 2685 This can be useful to avoid double picking when using buttons. 2686 2687 Frequently used events are: 2688 - `KeyPress`, `KeyRelease`: listen to keyboard events 2689 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2690 - `MiddleButtonPress`, `MiddleButtonRelease` 2691 - `RightButtonPress`, `RightButtonRelease` 2692 - `MouseMove`: listen to mouse pointer changing position 2693 - `MouseWheelForward`, `MouseWheelBackward` 2694 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2695 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2696 - `ResetCamera`, `ResetCameraClippingRange` 2697 - `Error`, `Warning` 2698 - `Char` 2699 - `Timer` 2700 2701 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2702 2703 Example: 2704 ```python 2705 from vedo import * 2706 2707 def func(evt): 2708 # this function is called every time the mouse moves 2709 # (evt is a dotted dictionary) 2710 if not evt.object: 2711 return # no hit, return 2712 print("point coords =", evt.picked3d) 2713 # print(evt) # full event dump 2714 2715 elli = Ellipsoid() 2716 plt = Plotter(axes=1) 2717 plt.add_callback('mouse hovering', func) 2718 plt.show(elli).close() 2719 ``` 2720 2721 Examples: 2722 - [spline_draw1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw1.py) 2723 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2724 2725 ![](https://vedo.embl.es/images/advanced/spline_draw.png) 2726 2727 - ..and many others! 2728 """ 2729 from vtkmodules.util.misc import calldata_type 2730 2731 if not self.interactor: 2732 return 0 2733 2734 if vedo.settings.dry_run_mode >= 1: 2735 return 0 2736 2737 ######################################### 2738 @calldata_type(vtki.VTK_INT) 2739 def _func_wrap(iren, ename, timerid=None): 2740 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2741 event.timerid = timerid 2742 event.id = cid 2743 event.priority = priority 2744 self.last_event = event 2745 func(event) 2746 2747 ######################################### 2748 2749 event_name = utils.get_vtk_name_event(event_name) 2750 2751 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2752 # print(f"Registering event: {event_name} with id={cid}") 2753 return cid 2754 2755 def remove_callback(self, cid: Union[int, str]) -> Self: 2756 """ 2757 Remove a callback function by its id 2758 or a whole category of callbacks by their name. 2759 2760 Arguments: 2761 cid : (int, str) 2762 Unique id of the callback. 2763 If an event name is passed all callbacks of that type are removed. 2764 """ 2765 if self.interactor: 2766 if isinstance(cid, str): 2767 cid = utils.get_vtk_name_event(cid) 2768 self.interactor.RemoveObservers(cid) 2769 else: 2770 self.interactor.RemoveObserver(cid) 2771 return self 2772 2773 def remove_all_observers(self) -> Self: 2774 """ 2775 Remove all observers. 2776 2777 Example: 2778 ```python 2779 from vedo import * 2780 2781 def kfunc(event): 2782 print("Key pressed:", event.keypress) 2783 if event.keypress == 'q': 2784 plt.close() 2785 2786 def rfunc(event): 2787 if event.isImage: 2788 printc("Right-clicked!", event) 2789 plt.render() 2790 2791 img = Image(dataurl+"images/embryo.jpg") 2792 2793 plt = Plotter(size=(1050, 600)) 2794 plt.parallel_projection(True) 2795 plt.remove_all_observers() 2796 plt.add_callback("key press", kfunc) 2797 plt.add_callback("mouse right click", rfunc) 2798 plt.show("Right-Click Me! Press q to exit.", img) 2799 plt.close() 2800 ``` 2801 """ 2802 if self.interactor: 2803 self.interactor.RemoveAllObservers() 2804 return self 2805 2806 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2807 """ 2808 Start or stop an existing timer. 2809 2810 Arguments: 2811 action : (str) 2812 Either "create"/"start" or "destroy"/"stop" 2813 timer_id : (int) 2814 When stopping the timer, the ID of the timer as returned when created 2815 dt : (int) 2816 time in milliseconds between each repeated call 2817 one_shot : (bool) 2818 create a one shot timer of prescribed duration instead of a repeating one 2819 2820 Examples: 2821 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2822 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2823 2824 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 2825 """ 2826 if action in ("create", "start"): 2827 if timer_id is not None: 2828 vedo.logger.warning("you set a timer_id but it will be ignored.") 2829 if one_shot: 2830 timer_id = self.interactor.CreateOneShotTimer(dt) 2831 else: 2832 timer_id = self.interactor.CreateRepeatingTimer(dt) 2833 return timer_id 2834 2835 elif action in ("destroy", "stop"): 2836 if timer_id is not None: 2837 self.interactor.DestroyTimer(timer_id) 2838 else: 2839 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2840 else: 2841 e = f"in timer_callback(). Cannot understand action: {action}\n" 2842 e += " allowed actions are: ['start', 'stop']. Skipped." 2843 vedo.logger.error(e) 2844 return timer_id 2845 2846 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2847 """ 2848 Add a callback function that will be called when an event occurs. 2849 Consider using `add_callback()` instead. 2850 """ 2851 if not self.interactor: 2852 return -1 2853 event_name = utils.get_vtk_name_event(event_name) 2854 idd = self.interactor.AddObserver(event_name, func, priority) 2855 return idd 2856 2857 def compute_world_coordinate( 2858 self, 2859 pos2d: MutableSequence[float], 2860 at=None, 2861 objs=(), 2862 bounds=(), 2863 offset=None, 2864 pixeltol=None, 2865 worldtol=None, 2866 ) -> np.ndarray: 2867 """ 2868 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2869 If a set of meshes is passed then points are placed onto these. 2870 2871 Arguments: 2872 pos2d : (list) 2873 2D screen coordinates point. 2874 at : (int) 2875 renderer number. 2876 objs : (list) 2877 list of Mesh objects to project the point onto. 2878 bounds : (list) 2879 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2880 offset : (float) 2881 specify an offset value. 2882 pixeltol : (int) 2883 screen tolerance in pixels. 2884 worldtol : (float) 2885 world coordinates tolerance. 2886 2887 Returns: 2888 numpy array, the point in 3D world coordinates. 2889 2890 Examples: 2891 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2892 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2893 2894 ![](https://vedo.embl.es/images/basic/mousehover3.jpg) 2895 """ 2896 if at is not None: 2897 renderer = self.renderers[at] 2898 else: 2899 renderer = self.renderer 2900 2901 if not objs: 2902 pp = vtki.vtkFocalPlanePointPlacer() 2903 else: 2904 pps = vtki.vtkPolygonalSurfacePointPlacer() 2905 for ob in objs: 2906 pps.AddProp(ob.actor) 2907 pp = pps # type: ignore 2908 2909 if len(bounds) == 6: 2910 pp.SetPointBounds(bounds) 2911 if pixeltol: 2912 pp.SetPixelTolerance(pixeltol) 2913 if worldtol: 2914 pp.SetWorldTolerance(worldtol) 2915 if offset: 2916 pp.SetOffset(offset) 2917 2918 worldPos: MutableSequence[float] = [0, 0, 0] 2919 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2920 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2921 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2922 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2923 return np.array(worldPos) 2924 2925 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2926 """ 2927 Given a 3D points in the current renderer (or full window), 2928 find the screen pixel coordinates. 2929 2930 Example: 2931 ```python 2932 from vedo import * 2933 2934 elli = Ellipsoid().point_size(5) 2935 2936 plt = Plotter() 2937 plt.show(elli, "Press q to continue and print the info") 2938 2939 xyscreen = plt.compute_screen_coordinates(elli) 2940 print('xyscreen coords:', xyscreen) 2941 2942 # simulate an event happening at one point 2943 event = plt.fill_event(pos=xyscreen[123]) 2944 print(event) 2945 ``` 2946 """ 2947 try: 2948 obj = obj.vertices 2949 except AttributeError: 2950 pass 2951 2952 if utils.is_sequence(obj): 2953 pts = obj 2954 p2d = [] 2955 cs = vtki.vtkCoordinate() 2956 cs.SetCoordinateSystemToWorld() 2957 cs.SetViewport(self.renderer) 2958 for p in pts: 2959 cs.SetValue(p) 2960 if full_window: 2961 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2962 else: 2963 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2964 return np.array(p2d, dtype=int) 2965 2966 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2967 """ 2968 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2969 2970 Returns a frustum Mesh that contains the visible field of view. 2971 This can be used to select objects in a scene or select vertices. 2972 2973 Example: 2974 ```python 2975 from vedo import * 2976 2977 settings.enable_default_mouse_callbacks = False 2978 2979 def mode_select(objs): 2980 print("Selected objects:", objs) 2981 d0 = mode.start_x, mode.start_y # display coords 2982 d1 = mode.end_x, mode.end_y 2983 2984 frustum = plt.pick_area(d0, d1) 2985 col = np.random.randint(0, 10) 2986 infru = frustum.inside_points(mesh) 2987 infru.point_size(10).color(col) 2988 plt.add(frustum, infru).render() 2989 2990 mesh = Mesh(dataurl+"cow.vtk") 2991 mesh.color("k5").linewidth(1) 2992 2993 mode = interactor_modes.BlenderStyle() 2994 mode.callback_select = mode_select 2995 2996 plt = Plotter().user_mode(mode) 2997 plt.show(mesh, axes=1) 2998 ``` 2999 """ 3000 if at is not None: 3001 ren = self.renderers[at] 3002 else: 3003 ren = self.renderer 3004 area_picker = vtki.vtkAreaPicker() 3005 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 3006 planes = area_picker.GetFrustum() 3007 3008 fru = vtki.new("FrustumSource") 3009 fru.SetPlanes(planes) 3010 fru.ShowLinesOff() 3011 fru.Update() 3012 3013 afru = vedo.Mesh(fru.GetOutput()) 3014 afru.alpha(0.1).lw(1).pickable(False) 3015 afru.name = "Frustum" 3016 return afru 3017 3018 def _scan_input_return_acts(self, objs) -> Any: 3019 # scan the input and return a list of actors 3020 if not utils.is_sequence(objs): 3021 objs = [objs] 3022 3023 ################# 3024 wannabe_acts = [] 3025 for a in objs: 3026 3027 try: 3028 wannabe_acts.append(a.actor) 3029 except AttributeError: 3030 wannabe_acts.append(a) # already actor 3031 3032 try: 3033 wannabe_acts.append(a.scalarbar) 3034 except AttributeError: 3035 pass 3036 3037 try: 3038 for sh in a.shadows: 3039 wannabe_acts.append(sh.actor) 3040 except AttributeError: 3041 pass 3042 3043 try: 3044 wannabe_acts.append(a.trail.actor) 3045 if a.trail.shadows: # trails may also have shadows 3046 for sh in a.trail.shadows: 3047 wannabe_acts.append(sh.actor) 3048 except AttributeError: 3049 pass 3050 3051 ################# 3052 scanned_acts = [] 3053 for a in wannabe_acts: # scan content of list 3054 3055 if a is None: 3056 pass 3057 3058 elif isinstance(a, (vtki.vtkActor, vtki.vtkActor2D)): 3059 scanned_acts.append(a) 3060 3061 elif isinstance(a, str): 3062 # assume a 2D comment was given 3063 changed = False # check if one already exists so to just update text 3064 if self.renderer: # might be jupyter 3065 acs = self.renderer.GetActors2D() 3066 acs.InitTraversal() 3067 for i in range(acs.GetNumberOfItems()): 3068 act = acs.GetNextItem() 3069 if isinstance(act, vedo.shapes.Text2D): 3070 aposx, aposy = act.GetPosition() 3071 if aposx < 0.01 and aposy > 0.99: # "top-left" 3072 act.text(a) # update content! no appending nada 3073 changed = True 3074 break 3075 if not changed: 3076 out = vedo.shapes.Text2D(a) # append a new one 3077 scanned_acts.append(out) 3078 # scanned_acts.append(vedo.shapes.Text2D(a)) # naive version 3079 3080 elif isinstance(a, vtki.vtkPolyData): 3081 scanned_acts.append(vedo.Mesh(a).actor) 3082 3083 elif isinstance(a, vtki.vtkImageData): 3084 scanned_acts.append(vedo.Volume(a).actor) 3085 3086 elif isinstance(a, vedo.RectilinearGrid): 3087 scanned_acts.append(a.actor) 3088 3089 elif isinstance(a, vedo.StructuredGrid): 3090 scanned_acts.append(a.actor) 3091 3092 elif isinstance(a, vtki.vtkLight): 3093 scanned_acts.append(a) 3094 3095 elif isinstance(a, vedo.visual.LightKit): 3096 a.lightkit.AddLightsToRenderer(self.renderer) 3097 3098 elif isinstance(a, vtki.get_class("MultiBlockDataSet")): 3099 for i in range(a.GetNumberOfBlocks()): 3100 b = a.GetBlock(i) 3101 if isinstance(b, vtki.vtkPolyData): 3102 scanned_acts.append(vedo.Mesh(b).actor) 3103 elif isinstance(b, vtki.vtkImageData): 3104 scanned_acts.append(vedo.Volume(b).actor) 3105 3106 elif isinstance(a, (vtki.vtkProp, vtki.vtkInteractorObserver)): 3107 scanned_acts.append(a) 3108 3109 elif "trimesh" in str(type(a)): 3110 scanned_acts.append(utils.trimesh2vedo(a)) 3111 3112 elif "meshlab" in str(type(a)): 3113 if "MeshSet" in str(type(a)): 3114 for i in range(a.number_meshes()): 3115 if a.mesh_id_exists(i): 3116 scanned_acts.append(utils.meshlab2vedo(a.mesh(i))) 3117 else: 3118 scanned_acts.append(utils.meshlab2vedo(a)) 3119 3120 elif "dolfin" in str(type(a)): # assume a dolfin.Mesh object 3121 import vedo.dolfin as vdlf 3122 3123 scanned_acts.append(vdlf.IMesh(a).actor) 3124 3125 elif "madcad" in str(type(a)): 3126 scanned_acts.append(utils.madcad2vedo(a).actor) 3127 3128 elif "TetgenIO" in str(type(a)): 3129 scanned_acts.append(vedo.TetMesh(a).shrink(0.9).c("pink7").actor) 3130 3131 elif "matplotlib.figure.Figure" in str(type(a)): 3132 scanned_acts.append(vedo.Image(a).clone2d("top-right", 0.6)) 3133 3134 else: 3135 vedo.logger.error(f"cannot understand input in show(): {type(a)}") 3136 3137 return scanned_acts 3138 3139 def show( 3140 self, 3141 *objects, 3142 at=None, 3143 axes=None, 3144 resetcam=None, 3145 zoom=False, 3146 interactive=None, 3147 viewup="", 3148 azimuth=0.0, 3149 elevation=0.0, 3150 roll=0.0, 3151 camera=None, 3152 mode=None, 3153 rate=None, 3154 bg=None, 3155 bg2=None, 3156 size=None, 3157 title=None, 3158 screenshot="", 3159 ) -> Any: 3160 """ 3161 Render a list of objects. 3162 3163 Arguments: 3164 at : (int) 3165 number of the renderer to plot to, in case of more than one exists 3166 3167 axes : (int) 3168 axis type-1 can be fully customized by passing a dictionary. 3169 Check `addons.Axes()` for the full list of options. 3170 set the type of axes to be shown: 3171 - 0, no axes 3172 - 1, draw three gray grid walls 3173 - 2, show cartesian axes from (0,0,0) 3174 - 3, show positive range of cartesian axes from (0,0,0) 3175 - 4, show a triad at bottom left 3176 - 5, show a cube at bottom left 3177 - 6, mark the corners of the bounding box 3178 - 7, draw a 3D ruler at each side of the cartesian axes 3179 - 8, show the `vtkCubeAxesActor` object 3180 - 9, show the bounding box outLine 3181 - 10, show three circles representing the maximum bounding box 3182 - 11, show a large grid on the x-y plane 3183 - 12, show polar axes 3184 - 13, draw a simple ruler at the bottom of the window 3185 3186 azimuth/elevation/roll : (float) 3187 move camera accordingly the specified value 3188 3189 viewup: str, list 3190 either `['x', 'y', 'z']` or a vector to set vertical direction 3191 3192 resetcam : (bool) 3193 re-adjust camera position to fit objects 3194 3195 camera : (dict, vtkCamera) 3196 camera parameters can further be specified with a dictionary 3197 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3198 - pos, `(list)`, the position of the camera in world coordinates 3199 - focal_point `(list)`, the focal point of the camera in world coordinates 3200 - viewup `(list)`, the view up direction for the camera 3201 - distance `(float)`, set the focal point to the specified distance from the camera position. 3202 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3203 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3204 in world-coordinate distances. The default is 1. 3205 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3206 This method has no effect in perspective projection mode. 3207 3208 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3209 plane to be set a distance 'thickness' beyond the near clipping plane. 3210 3211 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3212 measured in degrees. The default angle is 30 degrees. 3213 This method has no effect in parallel projection mode. 3214 The formula for setting the angle up for perfect perspective viewing is: 3215 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3216 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3217 3218 interactive : (bool) 3219 pause and interact with window (True) or continue execution (False) 3220 3221 rate : (float) 3222 maximum rate of `show()` in Hertz 3223 3224 mode : (int, str) 3225 set the type of interaction: 3226 - 0 = TrackballCamera [default] 3227 - 1 = TrackballActor 3228 - 2 = JoystickCamera 3229 - 3 = JoystickActor 3230 - 4 = Flight 3231 - 5 = RubberBand2D 3232 - 6 = RubberBand3D 3233 - 7 = RubberBandZoom 3234 - 8 = Terrain 3235 - 9 = Unicam 3236 - 10 = Image 3237 - Check out `vedo.interaction_modes` for more options. 3238 3239 bg : (str, list) 3240 background color in RGB format, or string name 3241 3242 bg2 : (str, list) 3243 second background color to create a gradient background 3244 3245 size : (str, list) 3246 size of the window, e.g. size="fullscreen", or size=[600,400] 3247 3248 title : (str) 3249 window title text 3250 3251 screenshot : (str) 3252 save a screenshot of the window to file 3253 """ 3254 3255 if vedo.settings.dry_run_mode >= 2: 3256 return self 3257 3258 if self.wx_widget: 3259 return self 3260 3261 if self.renderers: # in case of notebooks 3262 3263 if at is None: 3264 at = self.renderers.index(self.renderer) 3265 3266 else: 3267 3268 if at >= len(self.renderers): 3269 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3270 vedo.logger.error(t) 3271 return self 3272 3273 self.renderer = self.renderers[at] 3274 3275 if title is not None: 3276 self.title = title 3277 3278 if size is not None: 3279 self.size = size 3280 if self.size[0] == "f": # full screen 3281 self.size = "fullscreen" 3282 self.window.SetFullScreen(True) 3283 self.window.BordersOn() 3284 else: 3285 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3286 3287 if vedo.settings.default_backend == "vtk": 3288 if str(bg).endswith(".hdr"): 3289 self._add_skybox(bg) 3290 else: 3291 if bg is not None: 3292 self.backgrcol = vedo.get_color(bg) 3293 self.renderer.SetBackground(self.backgrcol) 3294 if bg2 is not None: 3295 self.renderer.GradientBackgroundOn() 3296 self.renderer.SetBackground2(vedo.get_color(bg2)) 3297 3298 if axes is not None: 3299 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3300 objects = list(objects) 3301 objects.append(axes) # move it into the list of normal things to show 3302 axes = 0 3303 self.axes = axes 3304 3305 if interactive is not None: 3306 self._interactive = interactive 3307 if self.offscreen: 3308 self._interactive = False 3309 3310 # camera stuff 3311 if resetcam is not None: 3312 self.resetcam = resetcam 3313 3314 if camera is not None: 3315 self.resetcam = False 3316 viewup = "" 3317 if isinstance(camera, vtki.vtkCamera): 3318 cameracopy = vtki.vtkCamera() 3319 cameracopy.DeepCopy(camera) 3320 self.camera = cameracopy 3321 else: 3322 self.camera = utils.camera_from_dict(camera) 3323 3324 self.add(objects) 3325 3326 # Backend ############################################################### 3327 if vedo.settings.default_backend in ["k3d"]: 3328 return backends.get_notebook_backend(self.objects) 3329 ######################################################################### 3330 3331 for ia in utils.flatten(objects): 3332 try: 3333 # fix gray color labels and title to white or black 3334 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3335 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3336 c = (0.9, 0.9, 0.9) 3337 if np.sum(self.renderer.GetBackground()) > 1.5: 3338 c = (0.1, 0.1, 0.1) 3339 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3340 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3341 except AttributeError: 3342 pass 3343 3344 if self.sharecam: 3345 for r in self.renderers: 3346 r.SetActiveCamera(self.camera) 3347 3348 if self.axes is not None: 3349 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3350 bns = self.renderer.ComputeVisiblePropBounds() 3351 addons.add_global_axes(self.axes, bounds=bns) 3352 3353 # Backend ############################################################### 3354 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3355 return backends.get_notebook_backend() 3356 ######################################################################### 3357 3358 if self.resetcam: 3359 self.renderer.ResetCamera() 3360 3361 if len(self.renderers) > 1: 3362 self.add_renderer_frame() 3363 3364 if vedo.settings.default_backend == "2d" and not zoom: 3365 zoom = "tightest" 3366 3367 if zoom: 3368 if zoom == "tight": 3369 self.reset_camera(tight=0.04) 3370 elif zoom == "tightest": 3371 self.reset_camera(tight=0.0001) 3372 else: 3373 self.camera.Zoom(zoom) 3374 if elevation: 3375 self.camera.Elevation(elevation) 3376 if azimuth: 3377 self.camera.Azimuth(azimuth) 3378 if roll: 3379 self.camera.Roll(roll) 3380 3381 if len(viewup) > 0: 3382 b = self.renderer.ComputeVisiblePropBounds() 3383 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3384 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3385 if viewup == "x": 3386 sz = np.linalg.norm(sz) 3387 self.camera.SetViewUp([1, 0, 0]) 3388 self.camera.SetPosition(cm + sz) 3389 elif viewup == "y": 3390 sz = np.linalg.norm(sz) 3391 self.camera.SetViewUp([0, 1, 0]) 3392 self.camera.SetPosition(cm + sz) 3393 elif viewup == "z": 3394 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3395 self.camera.SetViewUp([0, 0, 1]) 3396 self.camera.SetPosition(cm + 2 * sz) 3397 elif utils.is_sequence(viewup): 3398 sz = np.linalg.norm(sz) 3399 self.camera.SetViewUp(viewup) 3400 cpos = np.cross([0, 1, 0], viewup) 3401 self.camera.SetPosition(cm - 2 * sz * cpos) 3402 3403 self.renderer.ResetCameraClippingRange() 3404 3405 self.initialize_interactor() 3406 3407 if vedo.settings.immediate_rendering: 3408 self.window.Render() ##################### <-------------- Render 3409 3410 if self.interactor: # can be offscreen or not the vtk backend.. 3411 3412 self.window.SetWindowName(self.title) 3413 3414 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3415 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3416 # print(pic.dataset)# Array 0 name PNGImage 3417 # self.window.SetIcon(pic.dataset) 3418 3419 try: 3420 # Needs "pip install pyobjc" on Mac OSX 3421 if ( 3422 self._cocoa_initialized is False 3423 and "Darwin" in vedo.sys_platform 3424 and not self.offscreen 3425 ): 3426 self._cocoa_initialized = True 3427 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3428 pid = os.getpid() 3429 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3430 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3431 except: 3432 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3433 pass 3434 3435 # Set the interaction style 3436 if mode is not None: 3437 self.user_mode(mode) 3438 if self.qt_widget and mode is None: 3439 self.user_mode(0) 3440 3441 if screenshot: 3442 self.screenshot(screenshot) 3443 3444 if self._interactive: 3445 self.interactor.Start() 3446 if self._must_close_now: 3447 self.interactor.GetRenderWindow().Finalize() 3448 self.interactor.TerminateApp() 3449 self.camera = None 3450 self.renderer = None 3451 self.renderers = [] 3452 self.window = None 3453 self.interactor = None 3454 return self 3455 3456 if rate: 3457 if self.clock is None: # set clock and limit rate 3458 self._clockt0 = time.time() 3459 self.clock = 0.0 3460 else: 3461 t = time.time() - self._clockt0 3462 elapsed = t - self.clock 3463 mint = 1.0 / rate 3464 if elapsed < mint: 3465 time.sleep(mint - elapsed) 3466 self.clock = time.time() - self._clockt0 3467 3468 # 2d #################################################################### 3469 if vedo.settings.default_backend == "2d": 3470 return backends.get_notebook_backend() 3471 ######################################################################### 3472 3473 return self 3474 3475 3476 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3477 """Add a draggable inset space into a renderer. 3478 3479 Arguments: 3480 at : (int) 3481 specify the renderer number 3482 pos : (list) 3483 icon position in the range [1-4] indicating one of the 4 corners, 3484 or it can be a tuple (x,y) as a fraction of the renderer size. 3485 size : (float) 3486 size of the square inset 3487 draggable : (bool) 3488 if True the subrenderer space can be dragged around 3489 c : (color) 3490 color of the inset frame when dragged 3491 3492 Examples: 3493 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3494 3495 ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg) 3496 """ 3497 if not self.interactor: 3498 return None 3499 3500 if not self.renderer: 3501 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3502 return None 3503 3504 options = dict(options) 3505 pos = options.pop("pos", 0) 3506 size = options.pop("size", 0.1) 3507 c = options.pop("c", "lb") 3508 at = options.pop("at", None) 3509 draggable = options.pop("draggable", True) 3510 3511 r, g, b = vedo.get_color(c) 3512 widget = vtki.vtkOrientationMarkerWidget() 3513 widget.SetOutlineColor(r, g, b) 3514 if len(objects) == 1: 3515 widget.SetOrientationMarker(objects[0].actor) 3516 else: 3517 widget.SetOrientationMarker(vedo.Assembly(objects)) 3518 3519 widget.SetInteractor(self.interactor) 3520 3521 if utils.is_sequence(pos): 3522 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3523 else: 3524 if pos < 2: 3525 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3526 elif pos == 2: 3527 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3528 elif pos == 3: 3529 widget.SetViewport(0, 0, size * 2, size * 2) 3530 elif pos == 4: 3531 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3532 widget.EnabledOn() 3533 widget.SetInteractive(draggable) 3534 if at is not None and at < len(self.renderers): 3535 widget.SetCurrentRenderer(self.renderers[at]) 3536 else: 3537 widget.SetCurrentRenderer(self.renderer) 3538 self.widgets.append(widget) 3539 return widget 3540 3541 def clear(self, at=None, deep=False) -> Self: 3542 """Clear the scene from all meshes and volumes.""" 3543 if at is not None: 3544 renderer = self.renderers[at] 3545 else: 3546 renderer = self.renderer 3547 if not renderer: 3548 return self 3549 3550 if deep: 3551 renderer.RemoveAllViewProps() 3552 else: 3553 for ob in set( 3554 self.get_meshes() 3555 + self.get_volumes() 3556 + self.objects 3557 + self.axes_instances 3558 ): 3559 if isinstance(ob, vedo.shapes.Text2D): 3560 continue 3561 self.remove(ob) 3562 try: 3563 if ob.scalarbar: 3564 self.remove(ob.scalarbar) 3565 except AttributeError: 3566 pass 3567 return self 3568 3569 def break_interaction(self) -> Self: 3570 """Break window interaction and return to the python execution flow""" 3571 if self.interactor: 3572 self.check_actors_trasform() 3573 self.interactor.ExitCallback() 3574 return self 3575 3576 def freeze(self, value=True) -> Self: 3577 """Freeze the current renderer. Use this with `sharecam=False`.""" 3578 if not self.interactor: 3579 return self 3580 if not self.renderer: 3581 return self 3582 self.renderer.SetInteractive(not value) 3583 return self 3584 3585 def user_mode(self, mode) -> Self: 3586 """ 3587 Modify the user interaction mode. 3588 3589 Examples: 3590 ```python 3591 from vedo import * 3592 mode = interactor_modes.MousePan() 3593 mesh = Mesh(dataurl+"cow.vtk") 3594 plt = Plotter().user_mode(mode) 3595 plt.show(mesh, axes=1) 3596 ``` 3597 See also: 3598 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3599 """ 3600 if not self.interactor: 3601 return self 3602 3603 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3604 # print("Current style:", curr_style) 3605 if curr_style.endswith("Actor"): 3606 self.check_actors_trasform() 3607 3608 if isinstance(mode, (str, int)): 3609 # Set the style of interaction 3610 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3611 if mode in (0, "TrackballCamera"): 3612 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3613 self.interactor.RemoveObservers("CharEvent") 3614 elif mode in (1, "TrackballActor"): 3615 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3616 elif mode in (2, "JoystickCamera"): 3617 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3618 elif mode in (3, "JoystickActor"): 3619 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3620 elif mode in (4, "Flight"): 3621 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3622 elif mode in (5, "RubberBand2D"): 3623 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3624 elif mode in (6, "RubberBand3D"): 3625 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3626 elif mode in (7, "RubberBandZoom"): 3627 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3628 elif mode in (8, "Terrain"): 3629 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3630 elif mode in (9, "Unicam"): 3631 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3632 elif mode in (10, "Image", "image", "2d"): 3633 astyle = vtki.new("InteractorStyleImage") 3634 astyle.SetInteractionModeToImage3D() 3635 self.interactor.SetInteractorStyle(astyle) 3636 else: 3637 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3638 3639 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3640 # set a custom interactor style 3641 if hasattr(mode, "interactor"): 3642 mode.interactor = self.interactor 3643 mode.renderer = self.renderer # type: ignore 3644 mode.SetInteractor(self.interactor) 3645 mode.SetDefaultRenderer(self.renderer) 3646 self.interactor.SetInteractorStyle(mode) 3647 3648 return self 3649 3650 def close(self) -> Self: 3651 """Close the plotter.""" 3652 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3653 vedo.last_figure = None 3654 self.last_event = None 3655 self.sliders = [] 3656 self.buttons = [] 3657 self.widgets = [] 3658 self.hover_legends = [] 3659 self.background_renderer = None 3660 self._extralight = None 3661 3662 self.hint_widget = None 3663 self.cutter_widget = None 3664 3665 if vedo.settings.dry_run_mode >= 2: 3666 return self 3667 3668 if not hasattr(self, "window"): 3669 return self 3670 if not self.window: 3671 return self 3672 if not hasattr(self, "interactor"): 3673 return self 3674 if not self.interactor: 3675 return self 3676 3677 ################################################### 3678 try: 3679 if "Darwin" in vedo.sys_platform: 3680 self.interactor.ProcessEvents() 3681 except: 3682 pass 3683 3684 self._must_close_now = True 3685 3686 if vedo.plotter_instance == self: 3687 vedo.plotter_instance = None 3688 3689 if self.interactor and self._interactive: 3690 self.break_interaction() 3691 elif self._must_close_now: 3692 # dont call ExitCallback here 3693 if self.interactor: 3694 self.break_interaction() 3695 self.interactor.GetRenderWindow().Finalize() 3696 self.interactor.TerminateApp() 3697 self.camera = None 3698 self.renderer = None 3699 self.renderers = [] 3700 self.window = None 3701 self.interactor = None 3702 return self 3703 3704 @property 3705 def camera(self): 3706 """Return the current active camera.""" 3707 if self.renderer: 3708 return self.renderer.GetActiveCamera() 3709 3710 @camera.setter 3711 def camera(self, cam): 3712 if self.renderer: 3713 if isinstance(cam, dict): 3714 cam = utils.camera_from_dict(cam) 3715 self.renderer.SetActiveCamera(cam) 3716 3717 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3718 """ 3719 Take a screenshot of the Plotter window. 3720 3721 Arguments: 3722 scale : (int) 3723 set image magnification as an integer multiplicating factor 3724 asarray : (bool) 3725 return a numpy array of the image instead of writing a file 3726 3727 Warning: 3728 If you get black screenshots try to set `interactive=False` in `show()` 3729 then call `screenshot()` and `plt.interactive()` afterwards. 3730 3731 Example: 3732 ```py 3733 from vedo import * 3734 sphere = Sphere().linewidth(1) 3735 plt = show(sphere, interactive=False) 3736 plt.screenshot('image.png') 3737 plt.interactive() 3738 plt.close() 3739 ``` 3740 3741 Example: 3742 ```py 3743 from vedo import * 3744 sphere = Sphere().linewidth(1) 3745 plt = show(sphere, interactive=False) 3746 plt.screenshot('anotherimage.png') 3747 plt.interactive() 3748 plt.close() 3749 ``` 3750 """ 3751 return vedo.file_io.screenshot(filename, scale, asarray) 3752 3753 def toimage(self, scale=1) -> "vedo.image.Image": 3754 """ 3755 Generate a `Image` object from the current rendering window. 3756 3757 Arguments: 3758 scale : (int) 3759 set image magnification as an integer multiplicating factor 3760 """ 3761 if vedo.settings.screeshot_large_image: 3762 w2if = vtki.new("RenderLargeImage") 3763 w2if.SetInput(self.renderer) 3764 w2if.SetMagnification(scale) 3765 else: 3766 w2if = vtki.new("WindowToImageFilter") 3767 w2if.SetInput(self.window) 3768 if hasattr(w2if, "SetScale"): 3769 w2if.SetScale(scale, scale) 3770 if vedo.settings.screenshot_transparent_background: 3771 w2if.SetInputBufferTypeToRGBA() 3772 w2if.ReadFrontBufferOff() # read from the back buffer 3773 w2if.Update() 3774 return vedo.image.Image(w2if.GetOutput()) 3775 3776 def export(self, filename="scene.npz", binary=False) -> Self: 3777 """ 3778 Export scene to file to HTML, X3D or Numpy file. 3779 3780 Examples: 3781 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3782 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3783 """ 3784 vedo.file_io.export_window(filename, binary=binary) 3785 return self 3786 3787 def color_picker(self, xy, verbose=False): 3788 """Pick color of specific (x,y) pixel on the screen.""" 3789 w2if = vtki.new("WindowToImageFilter") 3790 w2if.SetInput(self.window) 3791 w2if.ReadFrontBufferOff() 3792 w2if.Update() 3793 nx, ny = self.window.GetSize() 3794 varr = w2if.GetOutput().GetPointData().GetScalars() 3795 3796 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3797 x, y = int(xy[0]), int(xy[1]) 3798 if y < ny and x < nx: 3799 3800 rgb = arr[y, x] 3801 3802 if verbose: 3803 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3804 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3805 vedo.printc("█", c=[0, rgb[1], 0], end="") 3806 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3807 vedo.printc("] = ", end="") 3808 cnm = vedo.get_color_name(rgb) 3809 if np.sum(rgb) < 150: 3810 vedo.printc( 3811 rgb.tolist(), 3812 vedo.colors.rgb2hex(np.array(rgb) / 255), 3813 c="w", 3814 bc=rgb, 3815 invert=1, 3816 end="", 3817 ) 3818 vedo.printc(" -> " + cnm, invert=1, c="w") 3819 else: 3820 vedo.printc( 3821 rgb.tolist(), 3822 vedo.colors.rgb2hex(np.array(rgb) / 255), 3823 c=rgb, 3824 end="", 3825 ) 3826 vedo.printc(" -> " + cnm, c=cnm) 3827 3828 return rgb 3829 3830 return None 3831 3832 ####################################################################### 3833 def _default_mouseleftclick(self, iren, event) -> None: 3834 x, y = iren.GetEventPosition() 3835 renderer = iren.FindPokedRenderer(x, y) 3836 picker = vtki.vtkPropPicker() 3837 picker.PickProp(x, y, renderer) 3838 3839 self.renderer = renderer 3840 3841 clicked_actor = picker.GetActor() 3842 # clicked_actor2D = picker.GetActor2D() 3843 3844 # print('_default_mouseleftclick mouse at', x, y) 3845 # print("picked Volume:", [picker.GetVolume()]) 3846 # print("picked Actor2D:", [picker.GetActor2D()]) 3847 # print("picked Assembly:", [picker.GetAssembly()]) 3848 # print("picked Prop3D:", [picker.GetProp3D()]) 3849 3850 if not clicked_actor: 3851 clicked_actor = picker.GetAssembly() 3852 3853 if not clicked_actor: 3854 clicked_actor = picker.GetProp3D() 3855 3856 if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable(): 3857 return 3858 3859 self.picked3d = picker.GetPickPosition() 3860 self.picked2d = np.array([x, y]) 3861 3862 if not clicked_actor: 3863 return 3864 3865 self.justremoved = None 3866 self.clicked_actor = clicked_actor 3867 3868 try: # might not be a vedo obj 3869 self.clicked_object = clicked_actor.retrieve_object() 3870 # save this info in the object itself 3871 self.clicked_object.picked3d = self.picked3d 3872 self.clicked_object.picked2d = self.picked2d 3873 except AttributeError: 3874 pass 3875 3876 # ----------- 3877 # if "Histogram1D" in picker.GetAssembly().__class__.__name__: 3878 # histo = picker.GetAssembly() 3879 # if histo.verbose: 3880 # x = self.picked3d[0] 3881 # idx = np.digitize(x, histo.edges) - 1 3882 # f = histo.frequencies[idx] 3883 # cn = histo.centers[idx] 3884 # vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}") 3885 3886 ####################################################################### 3887 def _default_keypress(self, iren, event) -> None: 3888 # NB: qt creates and passes a vtkGenericRenderWindowInteractor 3889 3890 key = iren.GetKeySym() 3891 3892 if "_L" in key or "_R" in key: 3893 return 3894 3895 if iren.GetShiftKey(): 3896 key = key.upper() 3897 3898 if iren.GetControlKey(): 3899 key = "Ctrl+" + key 3900 3901 if iren.GetAltKey(): 3902 key = "Alt+" + key 3903 3904 ####################################################### 3905 # utils.vedo.printc('Pressed key:', key, c='y', box='-') 3906 # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(), 3907 # iren.GetKeyCode(), iren.GetRepeatCount()) 3908 ####################################################### 3909 3910 x, y = iren.GetEventPosition() 3911 renderer = iren.FindPokedRenderer(x, y) 3912 3913 if key in ["q", "Return"]: 3914 self.break_interaction() 3915 return 3916 3917 elif key in ["Ctrl+q", "Ctrl+w", "Escape"]: 3918 self.close() 3919 return 3920 3921 elif key == "F1": 3922 vedo.logger.info("Execution aborted. Exiting python kernel now.") 3923 self.break_interaction() 3924 sys.exit(0) 3925 3926 elif key == "Down": 3927 if self.clicked_object and self.clicked_object in self.get_meshes(): 3928 self.clicked_object.alpha(0.02) 3929 if hasattr(self.clicked_object, "properties_backface"): 3930 bfp = self.clicked_actor.GetBackfaceProperty() 3931 self.clicked_object.properties_backface = bfp # save it 3932 self.clicked_actor.SetBackfaceProperty(None) 3933 else: 3934 for obj in self.get_meshes(): 3935 if obj: 3936 obj.alpha(0.02) 3937 bfp = obj.actor.GetBackfaceProperty() 3938 if bfp and hasattr(obj, "properties_backface"): 3939 obj.properties_backface = bfp 3940 obj.actor.SetBackfaceProperty(None) 3941 3942 elif key == "Left": 3943 if self.clicked_object and self.clicked_object in self.get_meshes(): 3944 ap = self.clicked_object.properties 3945 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3946 ap.SetOpacity(aal) 3947 bfp = self.clicked_actor.GetBackfaceProperty() 3948 if bfp and hasattr(self.clicked_object, "properties_backface"): 3949 self.clicked_object.properties_backface = bfp 3950 self.clicked_actor.SetBackfaceProperty(None) 3951 else: 3952 for a in self.get_meshes(): 3953 if a: 3954 ap = a.properties 3955 aal = max([ap.GetOpacity() * 0.75, 0.01]) 3956 ap.SetOpacity(aal) 3957 bfp = a.actor.GetBackfaceProperty() 3958 if bfp and hasattr(a, "properties_backface"): 3959 a.properties_backface = bfp 3960 a.actor.SetBackfaceProperty(None) 3961 3962 elif key == "Right": 3963 if self.clicked_object and self.clicked_object in self.get_meshes(): 3964 ap = self.clicked_object.properties 3965 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3966 ap.SetOpacity(aal) 3967 if ( 3968 aal == 1 3969 and hasattr(self.clicked_object, "properties_backface") 3970 and self.clicked_object.properties_backface 3971 ): 3972 # put back 3973 self.clicked_actor.SetBackfaceProperty( 3974 self.clicked_object.properties_backface) 3975 else: 3976 for a in self.get_meshes(): 3977 if a: 3978 ap = a.properties 3979 aal = min([ap.GetOpacity() * 1.25, 1.0]) 3980 ap.SetOpacity(aal) 3981 if aal == 1 and hasattr(a, "properties_backface") and a.properties_backface: 3982 a.actor.SetBackfaceProperty(a.properties_backface) 3983 3984 elif key == "Up": 3985 if self.clicked_object and self.clicked_object in self.get_meshes(): 3986 self.clicked_object.properties.SetOpacity(1) 3987 if hasattr(self.clicked_object, "properties_backface") and self.clicked_object.properties_backface: 3988 self.clicked_object.actor.SetBackfaceProperty(self.clicked_object.properties_backface) 3989 else: 3990 for a in self.get_meshes(): 3991 if a: 3992 a.properties.SetOpacity(1) 3993 if hasattr(a, "properties_backface") and a.properties_backface: 3994 a.actor.SetBackfaceProperty(a.properties_backface) 3995 3996 elif key == "P": 3997 if self.clicked_object and self.clicked_object in self.get_meshes(): 3998 objs = [self.clicked_object] 3999 else: 4000 objs = self.get_meshes() 4001 for ia in objs: 4002 try: 4003 ps = ia.properties.GetPointSize() 4004 if ps > 1: 4005 ia.properties.SetPointSize(ps - 1) 4006 ia.properties.SetRepresentationToPoints() 4007 except AttributeError: 4008 pass 4009 4010 elif key == "p": 4011 if self.clicked_object and self.clicked_object in self.get_meshes(): 4012 objs = [self.clicked_object] 4013 else: 4014 objs = self.get_meshes() 4015 for ia in objs: 4016 try: 4017 ps = ia.properties.GetPointSize() 4018 ia.properties.SetPointSize(ps + 2) 4019 ia.properties.SetRepresentationToPoints() 4020 except AttributeError: 4021 pass 4022 4023 elif key == "U": 4024 pval = renderer.GetActiveCamera().GetParallelProjection() 4025 renderer.GetActiveCamera().SetParallelProjection(not pval) 4026 if pval: 4027 renderer.ResetCamera() 4028 4029 elif key == "r": 4030 renderer.ResetCamera() 4031 4032 elif key == "h": 4033 msg = f" vedo {vedo.__version__}" 4034 msg += f" | vtk {vtki.vtkVersion().GetVTKVersion()}" 4035 msg += f" | numpy {np.__version__}" 4036 msg += f" | python {sys.version_info[0]}.{sys.version_info[1]}, press: " 4037 vedo.printc(msg.ljust(75), invert=True) 4038 msg = ( 4039 " i print info about the last clicked object \n" 4040 " I print color of the pixel under the mouse \n" 4041 " Y show the pipeline for this object as a graph \n" 4042 " <- -> use arrows to reduce/increase opacity \n" 4043 " x toggle mesh visibility \n" 4044 " w toggle wireframe/surface style \n" 4045 " l toggle surface edges visibility \n" 4046 " p/P hide surface faces and show only points \n" 4047 " 1-3 cycle surface color (2=light, 3=dark) \n" 4048 " 4 cycle color map (press shift-4 to go back) \n" 4049 " 5-6 cycle point-cell arrays (shift to go back) \n" 4050 " 7-8 cycle background and gradient color \n" 4051 " 09+- cycle axes styles (on keypad, or press +/-) \n" 4052 " k cycle available lighting styles \n" 4053 " K toggle shading as flat or phong \n" 4054 " A toggle anti-aliasing \n" 4055 " D toggle depth-peeling (for transparencies) \n" 4056 " U toggle perspective/parallel projection \n" 4057 " o/O toggle extra light to scene and rotate it \n" 4058 " a toggle interaction to Actor Mode \n" 4059 " n toggle surface normals \n" 4060 " r reset camera position \n" 4061 " R reset camera to the closest orthogonal view \n" 4062 " . fly camera to the last clicked point \n" 4063 " C print the current camera parameters state \n" 4064 " X invoke a cutter widget tool \n" 4065 " S save a screenshot of the current scene \n" 4066 " E/F export 3D scene to numpy file or X3D \n" 4067 " q return control to python script \n" 4068 " Esc abort execution and exit python kernel " 4069 ) 4070 vedo.printc(msg, dim=True, italic=True, bold=True) 4071 vedo.printc( 4072 " Check out the documentation at: https://vedo.embl.es ".ljust(75), 4073 invert=True, 4074 bold=True, 4075 ) 4076 return 4077 4078 elif key == "a": 4079 cur = iren.GetInteractorStyle() 4080 if isinstance(cur, vtki.get_class("InteractorStyleTrackballCamera")): 4081 msg = "Interactor style changed to TrackballActor\n" 4082 msg += " you can now move and rotate individual meshes:\n" 4083 msg += " press X twice to save the repositioned mesh\n" 4084 msg += " press 'a' to go back to normal style" 4085 vedo.printc(msg) 4086 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 4087 else: 4088 iren.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 4089 return 4090 4091 elif key == "A": # toggle antialiasing 4092 msam = self.window.GetMultiSamples() 4093 if not msam: 4094 self.window.SetMultiSamples(16) 4095 else: 4096 self.window.SetMultiSamples(0) 4097 msam = self.window.GetMultiSamples() 4098 if msam: 4099 vedo.printc(f"Antialiasing set to {msam} samples", c=bool(msam)) 4100 else: 4101 vedo.printc("Antialiasing disabled", c=bool(msam)) 4102 4103 elif key == "D": # toggle depthpeeling 4104 udp = not renderer.GetUseDepthPeeling() 4105 renderer.SetUseDepthPeeling(udp) 4106 # self.renderer.SetUseDepthPeelingForVolumes(udp) 4107 if udp: 4108 self.window.SetAlphaBitPlanes(1) 4109 renderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 4110 renderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 4111 self.interactor.Render() 4112 wasUsed = renderer.GetLastRenderingUsedDepthPeeling() 4113 rnr = self.renderers.index(renderer) 4114 vedo.printc(f"Depth peeling set to {udp} for renderer nr.{rnr}", c=udp) 4115 if not wasUsed and udp: 4116 vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True) 4117 return 4118 4119 elif key == "period": 4120 if self.picked3d: 4121 self.fly_to(self.picked3d) 4122 return 4123 4124 elif key == "S": 4125 fname = "screenshot.png" 4126 i = 1 4127 while os.path.isfile(fname): 4128 fname = f"screenshot{i}.png" 4129 i += 1 4130 vedo.file_io.screenshot(fname) 4131 vedo.printc(rf":camera: Saved rendering window to {fname}", c="b") 4132 return 4133 4134 elif key == "C": 4135 # Precision needs to be 7 (or even larger) to guarantee a consistent camera when 4136 # the model coordinates are not centered at (0, 0, 0) and the mode is large. 4137 # This could happen for plotting geological models with UTM coordinate systems 4138 cam = renderer.GetActiveCamera() 4139 vedo.printc("\n###################################################", c="y") 4140 vedo.printc("## Template python code to position this camera: ##", c="y") 4141 vedo.printc("cam = dict(", c="y") 4142 vedo.printc(" pos=" + utils.precision(cam.GetPosition(), 6) + ",", c="y") 4143 vedo.printc(" focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y") 4144 vedo.printc(" viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y") 4145 vedo.printc(" roll=" + utils.precision(cam.GetRoll(), 6) + ",", c="y") 4146 if cam.GetParallelProjection(): 4147 vedo.printc(' parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y') 4148 else: 4149 vedo.printc(' distance=' +utils.precision(cam.GetDistance(),6)+',', c='y') 4150 vedo.printc(' clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y') 4151 vedo.printc(')', c='y') 4152 vedo.printc('show(mymeshes, camera=cam)', c='y') 4153 vedo.printc('###################################################', c='y') 4154 return 4155 4156 elif key == "R": 4157 self.reset_viewup() 4158 4159 elif key == "w": 4160 try: 4161 if self.clicked_object.properties.GetRepresentation() == 1: # toggle 4162 self.clicked_object.properties.SetRepresentationToSurface() 4163 else: 4164 self.clicked_object.properties.SetRepresentationToWireframe() 4165 except AttributeError: 4166 pass 4167 4168 elif key == "1": 4169 try: 4170 self._icol += 1 4171 self.clicked_object.mapper.ScalarVisibilityOff() 4172 pal = vedo.colors.palettes[vedo.settings.palette % len(vedo.colors.palettes)] 4173 self.clicked_object.c(pal[(self._icol) % 10]) 4174 self.remove(self.clicked_object.scalarbar) 4175 except AttributeError: 4176 pass 4177 4178 elif key == "2": # dark colors 4179 try: 4180 bsc = ["k1", "k2", "k3", "k4", 4181 "b1", "b2", "b3", "b4", 4182 "p1", "p2", "p3", "p4", 4183 "g1", "g2", "g3", "g4", 4184 "r1", "r2", "r3", "r4", 4185 "o1", "o2", "o3", "o4", 4186 "y1", "y2", "y3", "y4"] 4187 self._icol += 1 4188 if self.clicked_object: 4189 self.clicked_object.mapper.ScalarVisibilityOff() 4190 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4191 self.clicked_object.c(newcol) 4192 self.remove(self.clicked_object.scalarbar) 4193 except AttributeError: 4194 pass 4195 4196 elif key == "3": # light colors 4197 try: 4198 bsc = ["k6", "k7", "k8", "k9", 4199 "b6", "b7", "b8", "b9", 4200 "p6", "p7", "p8", "p9", 4201 "g6", "g7", "g8", "g9", 4202 "r6", "r7", "r8", "r9", 4203 "o6", "o7", "o8", "o9", 4204 "y6", "y7", "y8", "y9"] 4205 self._icol += 1 4206 if self.clicked_object: 4207 self.clicked_object.mapper.ScalarVisibilityOff() 4208 newcol = vedo.get_color(bsc[(self._icol) % len(bsc)]) 4209 self.clicked_object.c(newcol) 4210 self.remove(self.clicked_object.scalarbar) 4211 except AttributeError: 4212 pass 4213 4214 elif key == "4": # cmap name cycle 4215 ob = self.clicked_object 4216 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4217 return 4218 if not ob.mapper.GetScalarVisibility(): 4219 return 4220 onwhat = ob.mapper.GetScalarModeAsString() # UsePointData/UseCellData 4221 4222 cmap_names = [ 4223 "Accent", "Paired", 4224 "rainbow", "rainbow_r", 4225 "Spectral", "Spectral_r", 4226 "gist_ncar", "gist_ncar_r", 4227 "viridis", "viridis_r", 4228 "hot", "hot_r", 4229 "terrain", "ocean", 4230 "coolwarm", "seismic", "PuOr", "RdYlGn", 4231 ] 4232 try: 4233 i = cmap_names.index(ob._cmap_name) 4234 if iren.GetShiftKey(): 4235 i -= 1 4236 else: 4237 i += 1 4238 if i >= len(cmap_names): 4239 i = 0 4240 if i < 0: 4241 i = len(cmap_names) - 1 4242 except ValueError: 4243 i = 0 4244 4245 ob._cmap_name = cmap_names[i] 4246 ob.cmap(ob._cmap_name, on=onwhat) 4247 if ob.scalarbar: 4248 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4249 self.remove(ob.scalarbar) 4250 title = ob.scalarbar.GetTitle() 4251 ob.add_scalarbar(title=title) 4252 self.add(ob.scalarbar).render() 4253 elif isinstance(ob.scalarbar, vedo.Assembly): 4254 self.remove(ob.scalarbar) 4255 ob.add_scalarbar3d(title=ob._cmap_name) 4256 self.add(ob.scalarbar) 4257 4258 vedo.printc( 4259 f"Name:'{ob.name}'," if ob.name else "", 4260 f"range:{utils.precision(ob.mapper.GetScalarRange(),3)},", 4261 f"colormap:'{ob._cmap_name}'", c="g", bold=False, 4262 ) 4263 4264 elif key == "5": # cycle pointdata array 4265 ob = self.clicked_object 4266 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4267 return 4268 4269 arrnames = ob.pointdata.keys() 4270 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4271 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4272 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4273 if len(arrnames) == 0: 4274 return 4275 ob.mapper.SetScalarVisibility(1) 4276 4277 if not ob._cmap_name: 4278 ob._cmap_name = "rainbow" 4279 4280 try: 4281 curr_name = ob.dataset.GetPointData().GetScalars().GetName() 4282 i = arrnames.index(curr_name) 4283 if "normals" in curr_name.lower(): 4284 return 4285 if iren.GetShiftKey(): 4286 i -= 1 4287 else: 4288 i += 1 4289 if i >= len(arrnames): 4290 i = 0 4291 if i < 0: 4292 i = len(arrnames) - 1 4293 except (ValueError, AttributeError): 4294 i = 0 4295 4296 ob.cmap(ob._cmap_name, arrnames[i], on="points") 4297 if ob.scalarbar: 4298 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4299 self.remove(ob.scalarbar) 4300 title = ob.scalarbar.GetTitle() 4301 ob.scalarbar = None 4302 ob.add_scalarbar(title=arrnames[i]) 4303 self.add(ob.scalarbar) 4304 elif isinstance(ob.scalarbar, vedo.Assembly): 4305 self.remove(ob.scalarbar) 4306 ob.scalarbar = None 4307 ob.add_scalarbar3d(title=arrnames[i]) 4308 self.add(ob.scalarbar) 4309 else: 4310 vedo.printc( 4311 f"Name:'{ob.name}'," if ob.name else "", 4312 f"active pointdata array: '{arrnames[i]}'", 4313 c="g", bold=False, 4314 ) 4315 4316 elif key == "6": # cycle celldata array 4317 ob = self.clicked_object 4318 if not isinstance(ob, (vedo.Points, vedo.UnstructuredGrid)): 4319 return 4320 4321 arrnames = ob.celldata.keys() 4322 arrnames = [a for a in arrnames if "normal" not in a.lower()] 4323 arrnames = [a for a in arrnames if "tcoord" not in a.lower()] 4324 arrnames = [a for a in arrnames if "textur" not in a.lower()] 4325 if len(arrnames) == 0: 4326 return 4327 ob.mapper.SetScalarVisibility(1) 4328 4329 if not ob._cmap_name: 4330 ob._cmap_name = "rainbow" 4331 4332 try: 4333 curr_name = ob.dataset.GetCellData().GetScalars().GetName() 4334 i = arrnames.index(curr_name) 4335 if "normals" in curr_name.lower(): 4336 return 4337 if iren.GetShiftKey(): 4338 i -= 1 4339 else: 4340 i += 1 4341 if i >= len(arrnames): 4342 i = 0 4343 if i < 0: 4344 i = len(arrnames) - 1 4345 except (ValueError, AttributeError): 4346 i = 0 4347 4348 ob.cmap(ob._cmap_name, arrnames[i], on="cells") 4349 if ob.scalarbar: 4350 if isinstance(ob.scalarbar, vtki.vtkActor2D): 4351 self.remove(ob.scalarbar) 4352 title = ob.scalarbar.GetTitle() 4353 ob.scalarbar = None 4354 ob.add_scalarbar(title=arrnames[i]) 4355 self.add(ob.scalarbar) 4356 elif isinstance(ob.scalarbar, vedo.Assembly): 4357 self.remove(ob.scalarbar) 4358 ob.scalarbar = None 4359 ob.add_scalarbar3d(title=arrnames[i]) 4360 self.add(ob.scalarbar) 4361 else: 4362 vedo.printc( 4363 f"Name:'{ob.name}'," if ob.name else "", 4364 f"active celldata array: '{arrnames[i]}'", 4365 c="g", bold=False, 4366 ) 4367 4368 elif key == "7": 4369 bgc = np.array(renderer.GetBackground()).sum() / 3 4370 if bgc <= 0: 4371 bgc = 0.223 4372 elif 0 < bgc < 1: 4373 bgc = 1 4374 else: 4375 bgc = 0 4376 renderer.SetBackground(bgc, bgc, bgc) 4377 4378 elif key == "8": 4379 bg2cols = [ 4380 "lightyellow", 4381 "darkseagreen", 4382 "palegreen", 4383 "steelblue", 4384 "lightblue", 4385 "cadetblue", 4386 "lavender", 4387 "white", 4388 "blackboard", 4389 "black", 4390 ] 4391 bg2name = vedo.get_color_name(renderer.GetBackground2()) 4392 if bg2name in bg2cols: 4393 idx = bg2cols.index(bg2name) 4394 else: 4395 idx = 4 4396 if idx is not None: 4397 bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)] 4398 if not bg2name_next: 4399 renderer.GradientBackgroundOff() 4400 else: 4401 renderer.GradientBackgroundOn() 4402 renderer.SetBackground2(vedo.get_color(bg2name_next)) 4403 4404 elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]: # cycle axes style 4405 i = self.renderers.index(renderer) 4406 try: 4407 self.axes_instances[i].EnabledOff() 4408 self.axes_instances[i].SetInteractor(None) 4409 except AttributeError: 4410 # print("Cannot remove widget", [self.axes_instances[i]]) 4411 try: 4412 self.remove(self.axes_instances[i]) 4413 except: 4414 print("Cannot remove axes", [self.axes_instances[i]]) 4415 return 4416 self.axes_instances[i] = None 4417 4418 if not self.axes: 4419 self.axes = 0 4420 if isinstance(self.axes, dict): 4421 self.axes = 1 4422 4423 if key in ["minus", "KP_Subtract"]: 4424 if not self.camera.GetParallelProjection() and self.axes == 0: 4425 self.axes -= 1 # jump ruler doesnt make sense in perspective mode 4426 bns = self.renderer.ComputeVisiblePropBounds() 4427 addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None, bounds=bns) 4428 else: 4429 if not self.camera.GetParallelProjection() and self.axes == 12: 4430 self.axes += 1 # jump ruler doesnt make sense in perspective mode 4431 bns = self.renderer.ComputeVisiblePropBounds() 4432 addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns) 4433 self.render() 4434 4435 elif "KP_" in key or key in [ 4436 "Insert","End","Down","Next","Left","Begin","Right","Home","Up","Prior" 4437 ]: 4438 asso = { # change axes style 4439 "KP_Insert": 0, "KP_0": 0, "Insert": 0, 4440 "KP_End": 1, "KP_1": 1, "End": 1, 4441 "KP_Down": 2, "KP_2": 2, "Down": 2, 4442 "KP_Next": 3, "KP_3": 3, "Next": 3, 4443 "KP_Left": 4, "KP_4": 4, "Left": 4, 4444 "KP_Begin": 5, "KP_5": 5, "Begin": 5, 4445 "KP_Right": 6, "KP_6": 6, "Right": 6, 4446 "KP_Home": 7, "KP_7": 7, "Home": 7, 4447 "KP_Up": 8, "KP_8": 8, "Up": 8, 4448 "Prior": 9, # on windows OS 4449 } 4450 clickedr = self.renderers.index(renderer) 4451 if key in asso: 4452 if self.axes_instances[clickedr]: 4453 if hasattr(self.axes_instances[clickedr], "EnabledOff"): # widget 4454 self.axes_instances[clickedr].EnabledOff() 4455 else: 4456 try: 4457 renderer.RemoveActor(self.axes_instances[clickedr]) 4458 except: 4459 pass 4460 self.axes_instances[clickedr] = None 4461 bounds = renderer.ComputeVisiblePropBounds() 4462 addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds) 4463 self.interactor.Render() 4464 4465 if key == "O": 4466 renderer.RemoveLight(self._extralight) 4467 self._extralight = None 4468 4469 elif key == "o": 4470 vbb, sizes, _, _ = addons.compute_visible_bounds() 4471 cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2) 4472 if not self._extralight: 4473 vup = renderer.GetActiveCamera().GetViewUp() 4474 pos = cm + utils.vector(vup) * utils.mag(sizes) 4475 self._extralight = addons.Light(pos, focal_point=cm, intensity=0.4) 4476 renderer.AddLight(self._extralight) 4477 vedo.printc("Press 'o' again to rotate light source, or 'O' to remove it.", c='y') 4478 else: 4479 cpos = utils.vector(self._extralight.GetPosition()) 4480 x, y, z = self._extralight.GetPosition() - cm 4481 r, th, ph = transformations.cart2spher(x, y, z) 4482 th += 0.2 4483 if th > np.pi: 4484 th = np.random.random() * np.pi / 2 4485 ph += 0.3 4486 cpos = transformations.spher2cart(r, th, ph).T + cm 4487 self._extralight.SetPosition(cpos) 4488 4489 elif key == "l": 4490 if self.clicked_object in self.get_meshes(): 4491 objs = [self.clicked_object] 4492 else: 4493 objs = self.get_meshes() 4494 for ia in objs: 4495 try: 4496 ev = ia.properties.GetEdgeVisibility() 4497 ia.properties.SetEdgeVisibility(not ev) 4498 ia.properties.SetRepresentationToSurface() 4499 ia.properties.SetLineWidth(0.1) 4500 except AttributeError: 4501 pass 4502 4503 elif key == "k": # lightings 4504 if self.clicked_object in self.get_meshes(): 4505 objs = [self.clicked_object] 4506 else: 4507 objs = self.get_meshes() 4508 shds = ("default", "metallic", "plastic", "shiny", "glossy", "off") 4509 for ia in objs: 4510 try: 4511 lnr = (ia._ligthingnr + 1) % 6 4512 ia.lighting(shds[lnr]) 4513 ia._ligthingnr = lnr 4514 except AttributeError: 4515 pass 4516 4517 elif key == "K": # shading 4518 if self.clicked_object in self.get_meshes(): 4519 objs = [self.clicked_object] 4520 else: 4521 objs = self.get_meshes() 4522 for ia in objs: 4523 if isinstance(ia, vedo.Mesh): 4524 ia.compute_normals(cells=False) 4525 intrp = ia.properties.GetInterpolation() 4526 if intrp > 0: 4527 ia.properties.SetInterpolation(0) # flat 4528 else: 4529 ia.properties.SetInterpolation(2) # phong 4530 4531 elif key == "n": # show normals to an actor 4532 self.remove("added_auto_normals") 4533 if self.clicked_object in self.get_meshes(): 4534 if self.clicked_actor.GetPickable(): 4535 norml = vedo.shapes.NormalLines(self.clicked_object) 4536 norml.name = "added_auto_normals" 4537 self.add(norml) 4538 4539 elif key == "x": 4540 if self.justremoved is None: 4541 if self.clicked_object in self.get_meshes() or isinstance( 4542 self.clicked_object, vtki.vtkAssembly 4543 ): 4544 self.justremoved = self.clicked_actor 4545 self.renderer.RemoveActor(self.clicked_actor) 4546 else: 4547 self.renderer.AddActor(self.justremoved) 4548 self.justremoved = None 4549 4550 elif key == "X": 4551 if self.clicked_object: 4552 if not self.cutter_widget: 4553 self.cutter_widget = addons.BoxCutter(self.clicked_object) 4554 self.add(self.cutter_widget) 4555 vedo.printc("Press i to toggle the cutter on/off", c='g', dim=1) 4556 vedo.printc(" u to flip selection", c='g', dim=1) 4557 vedo.printc(" r to reset cutting planes", c='g', dim=1) 4558 vedo.printc(" Shift+X to close the cutter box widget", c='g', dim=1) 4559 vedo.printc(" Ctrl+S to save the cut section to file.", c='g', dim=1) 4560 else: 4561 self.remove(self.cutter_widget) 4562 self.cutter_widget = None 4563 vedo.printc("Click object and press X to open the cutter box widget.", c='g') 4564 4565 elif key == "E": 4566 vedo.printc(r":camera: Exporting 3D window to file scene.npz", c="b", end="") 4567 vedo.file_io.export_window("scene.npz") 4568 vedo.printc(", try:\n> vedo scene.npz # (this is experimental!)", c="b") 4569 return 4570 4571 elif key == "F": 4572 vedo.file_io.export_window("scene.x3d") 4573 vedo.printc(r":camera: Exporting 3D window to file", c="b", end="") 4574 vedo.file_io.export_window("scene.npz") 4575 vedo.printc(". Try:\n> firefox scene.html", c="b") 4576 4577 # elif key == "G": # not working with last version of k3d 4578 # vedo.file_io.export_window("scene.html") 4579 # vedo.printc(r":camera: Exporting K3D window to file", c="b", end="") 4580 # vedo.file_io.export_window("scene.html") 4581 # vedo.printc(". Try:\n> firefox scene.html", c="b") 4582 4583 elif key == "i": # print info 4584 if self.clicked_object: 4585 print(self.clicked_object) 4586 else: 4587 print(self) 4588 4589 elif key == "I": # print color under the mouse 4590 x, y = iren.GetEventPosition() 4591 self.color_picker([x, y], verbose=True) 4592 4593 elif key == "Y": 4594 if self.clicked_object and self.clicked_object.pipeline: 4595 self.clicked_object.pipeline.show() 4596 4597 if iren: 4598 iren.Render()
Main class to manage objects.
379 def __init__( 380 self, 381 shape=(1, 1), 382 N=None, 383 pos=(0, 0), 384 size="auto", 385 screensize="auto", 386 title="vedo", 387 bg="white", 388 bg2=None, 389 axes=None, 390 sharecam=True, 391 resetcam=True, 392 interactive=None, 393 offscreen=False, 394 qt_widget=None, 395 wx_widget=None, 396 ): 397 """ 398 Arguments: 399 shape : (str, list) 400 shape of the grid of renderers in format (rows, columns). Ignored if N is specified. 401 N : (int) 402 number of desired renderers arranged in a grid automatically. 403 pos : (list) 404 (x,y) position in pixels of top-left corner of the rendering window on the screen 405 size : (str, list) 406 size of the rendering window. If 'auto', guess it based on screensize. 407 screensize : (list) 408 physical size of the monitor screen in pixels 409 bg : (color, str) 410 background color or specify jpg image file name with path 411 bg2 : (color) 412 background color of a gradient towards the top 413 title : (str) 414 window title 415 416 axes : (int) 417 418 Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`. 419 Check out `vedo.addons.Axes()` for the available options. 420 421 - 0, no axes 422 - 1, draw three gray grid walls 423 - 2, show cartesian axes from (0,0,0) 424 - 3, show positive range of cartesian axes from (0,0,0) 425 - 4, show a triad at bottom left 426 - 5, show a cube at bottom left 427 - 6, mark the corners of the bounding box 428 - 7, draw a 3D ruler at each side of the cartesian axes 429 - 8, show the VTK CubeAxesActor object 430 - 9, show the bounding box outLine 431 - 10, show three circles representing the maximum bounding box 432 - 11, show a large grid on the x-y plane (use with zoom=8) 433 - 12, show polar axes 434 - 13, draw a simple ruler at the bottom of the window 435 - 14: draw a camera orientation widget 436 437 sharecam : (bool) 438 if False each renderer will have an independent camera 439 interactive : (bool) 440 if True will stop after show() to allow interaction with the 3d scene 441 offscreen : (bool) 442 if True will not show the rendering window 443 qt_widget : (QVTKRenderWindowInteractor) 444 render in a Qt-Widget using an QVTKRenderWindowInteractor. 445 See examples `qt_windows[1,2,3].py` and `qt_cutter.py`. 446 """ 447 vedo.plotter_instance = self 448 449 if interactive is None: 450 interactive = bool(N in (0, 1, None) and shape == (1, 1)) 451 self._interactive = interactive 452 # print("interactive", interactive, N, shape) 453 454 self.objects = [] # list of objects to be shown 455 self.clicked_object = None # holds the object that has been clicked 456 self.clicked_actor = None # holds the actor that has been clicked 457 458 self.shape = shape # nr. of subwindows in grid 459 self.axes = axes # show axes type nr. 460 self.title = title # window title 461 self.size = size # window size 462 self.backgrcol = bg # used also by backend notebooks 463 464 self.offscreen= offscreen 465 self.resetcam = resetcam 466 self.sharecam = sharecam # share the same camera if multiple renderers 467 self.pos = pos # used by vedo.file_io 468 469 self.picker = None # hold the vtkPicker object 470 self.picked2d = None # 2d coords of a clicked point on the rendering window 471 self.picked3d = None # 3d coords of a clicked point on an actor 472 473 self.qt_widget = qt_widget # QVTKRenderWindowInteractor 474 self.wx_widget = wx_widget # wxVTKRenderWindowInteractor 475 self.interactor = None 476 self.window = None 477 self.renderer = None 478 self.renderers = [] # list of renderers 479 480 # mostly internal stuff: 481 self.hover_legends = [] 482 self.justremoved = None 483 self.axes_instances = [] 484 self.clock = 0 485 self.sliders = [] 486 self.buttons = [] 487 self.widgets = [] 488 self.cutter_widget = None 489 self.hint_widget = None 490 self.background_renderer = None 491 self.last_event = None 492 self.skybox = None 493 self._icol = 0 494 self._clockt0 = time.time() 495 self._extralight = None 496 self._cocoa_initialized = False 497 self._cocoa_process_events = True # make one call in show() 498 self._must_close_now = False 499 500 ##################################################################### 501 if vedo.settings.default_backend == "2d": 502 self.offscreen = True 503 if self.size == "auto": 504 self.size = (800, 600) 505 506 elif vedo.settings.default_backend == "k3d": 507 if self.size == "auto": 508 self.size = (1000, 1000) 509 #################################### 510 return ############################ 511 #################################### 512 513 ############################################################# 514 if vedo.settings.default_backend in ["vtk", "2d", "trame"]: 515 516 if screensize == "auto": 517 screensize = (2160, 1440) # TODO: get actual screen size 518 519 # build the rendering window: 520 self.window = vtki.vtkRenderWindow() 521 522 self.window.GlobalWarningDisplayOff() 523 524 if self.title == "vedo": # check if dev version 525 if "dev" in vedo.__version__: 526 self.title = f"vedo ({vedo.__version__})" 527 self.window.SetWindowName(self.title) 528 529 # more vedo.settings 530 if vedo.settings.use_depth_peeling: 531 self.window.SetAlphaBitPlanes(vedo.settings.alpha_bit_planes) 532 self.window.SetMultiSamples(vedo.settings.multi_samples) 533 534 self.window.SetPolygonSmoothing(vedo.settings.polygon_smoothing) 535 self.window.SetLineSmoothing(vedo.settings.line_smoothing) 536 self.window.SetPointSmoothing(vedo.settings.point_smoothing) 537 538 ############################################################# 539 if N: # N = number of renderers. Find out the best 540 541 if shape != (1, 1): # arrangement based on minimum nr. of empty renderers 542 vedo.logger.warning("having set N, shape is ignored.") 543 544 x, y = screensize 545 nx = int(np.sqrt(int(N * y / x) + 1)) 546 ny = int(np.sqrt(int(N * x / y) + 1)) 547 lm = [ 548 (nx, ny), 549 (nx, ny + 1), 550 (nx - 1, ny), 551 (nx + 1, ny), 552 (nx, ny - 1), 553 (nx - 1, ny + 1), 554 (nx + 1, ny - 1), 555 (nx + 1, ny + 1), 556 (nx - 1, ny - 1), 557 ] 558 ind, minl = 0, 1000 559 for i, m in enumerate(lm): 560 l = m[0] * m[1] 561 if N <= l < minl: 562 ind = i 563 minl = l 564 shape = lm[ind] 565 566 ################################################## 567 if isinstance(shape, str): 568 569 if "|" in shape: 570 if self.size == "auto": 571 self.size = (800, 1200) 572 n = int(shape.split("|")[0]) 573 m = int(shape.split("|")[1]) 574 rangen = reversed(range(n)) 575 rangem = reversed(range(m)) 576 else: 577 if self.size == "auto": 578 self.size = (1200, 800) 579 m = int(shape.split("/")[0]) 580 n = int(shape.split("/")[1]) 581 rangen = range(n) 582 rangem = range(m) 583 584 if n >= m: 585 xsplit = m / (n + m) 586 else: 587 xsplit = 1 - n / (n + m) 588 if vedo.settings.window_splitting_position: 589 xsplit = vedo.settings.window_splitting_position 590 591 for i in rangen: 592 arenderer = vtki.vtkRenderer() 593 if "|" in shape: 594 arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n) 595 else: 596 arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit) 597 self.renderers.append(arenderer) 598 599 for i in rangem: 600 arenderer = vtki.vtkRenderer() 601 602 if "|" in shape: 603 arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m) 604 else: 605 arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1) 606 self.renderers.append(arenderer) 607 608 for r in self.renderers: 609 r.SetLightFollowCamera(vedo.settings.light_follows_camera) 610 611 r.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 612 # r.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 613 if vedo.settings.use_depth_peeling: 614 r.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 615 r.SetOcclusionRatio(vedo.settings.occlusion_ratio) 616 r.SetUseFXAA(vedo.settings.use_fxaa) 617 r.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 618 619 r.SetBackground(vedo.get_color(self.backgrcol)) 620 621 self.axes_instances.append(None) 622 623 self.shape = (n + m,) 624 625 elif utils.is_sequence(shape) and isinstance(shape[0], dict): 626 # passing a sequence of dicts for renderers specifications 627 628 if self.size == "auto": 629 self.size = (1000, 800) 630 631 for rd in shape: 632 x0, y0 = rd["bottomleft"] 633 x1, y1 = rd["topright"] 634 bg_ = rd.pop("bg", "white") 635 bg2_ = rd.pop("bg2", None) 636 637 arenderer = vtki.vtkRenderer() 638 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 639 640 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 641 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 642 if vedo.settings.use_depth_peeling: 643 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 644 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 645 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 646 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 647 648 arenderer.SetViewport(x0, y0, x1, y1) 649 arenderer.SetBackground(vedo.get_color(bg_)) 650 if bg2_: 651 arenderer.GradientBackgroundOn() 652 arenderer.SetBackground2(vedo.get_color(bg2_)) 653 654 self.renderers.append(arenderer) 655 self.axes_instances.append(None) 656 657 self.shape = (len(shape),) 658 659 else: 660 661 if isinstance(self.size, str) and self.size == "auto": 662 # figure out a reasonable window size 663 f = 1.5 664 x, y = screensize 665 xs = y / f * shape[1] # because y<x 666 ys = y / f * shape[0] 667 if xs > x / f: # shrink 668 xs = x / f 669 ys = xs / shape[1] * shape[0] 670 if ys > y / f: 671 ys = y / f 672 xs = ys / shape[0] * shape[1] 673 self.size = (int(xs), int(ys)) 674 if shape == (1, 1): 675 self.size = (int(y / f), int(y / f)) # because y<x 676 else: 677 self.size = (self.size[0], self.size[1]) 678 679 try: 680 image_actor = None 681 bgname = str(self.backgrcol).lower() 682 if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname: 683 self.window.SetNumberOfLayers(2) 684 self.background_renderer = vtki.vtkRenderer() 685 self.background_renderer.SetLayer(0) 686 self.background_renderer.InteractiveOff() 687 self.background_renderer.SetBackground(vedo.get_color(bg2)) 688 image_actor = vedo.Image(self.backgrcol).actor 689 self.window.AddRenderer(self.background_renderer) 690 self.background_renderer.AddActor(image_actor) 691 except AttributeError: 692 pass 693 694 for i in reversed(range(shape[0])): 695 for j in range(shape[1]): 696 arenderer = vtki.vtkRenderer() 697 arenderer.SetLightFollowCamera(vedo.settings.light_follows_camera) 698 arenderer.SetTwoSidedLighting(vedo.settings.two_sided_lighting) 699 700 arenderer.SetUseDepthPeeling(vedo.settings.use_depth_peeling) 701 # arenderer.SetUseDepthPeelingForVolumes(vedo.settings.use_depth_peeling) 702 if vedo.settings.use_depth_peeling: 703 arenderer.SetMaximumNumberOfPeels(vedo.settings.max_number_of_peels) 704 arenderer.SetOcclusionRatio(vedo.settings.occlusion_ratio) 705 arenderer.SetUseFXAA(vedo.settings.use_fxaa) 706 arenderer.SetPreserveDepthBuffer(vedo.settings.preserve_depth_buffer) 707 708 if image_actor: 709 arenderer.SetLayer(1) 710 711 arenderer.SetBackground(vedo.get_color(self.backgrcol)) 712 if bg2: 713 arenderer.GradientBackgroundOn() 714 arenderer.SetBackground2(vedo.get_color(bg2)) 715 716 x0 = i / shape[0] 717 y0 = j / shape[1] 718 x1 = (i + 1) / shape[0] 719 y1 = (j + 1) / shape[1] 720 arenderer.SetViewport(y0, x0, y1, x1) 721 self.renderers.append(arenderer) 722 self.axes_instances.append(None) 723 self.shape = shape 724 725 if self.renderers: 726 self.renderer = self.renderers[0] 727 self.camera.SetParallelProjection(vedo.settings.use_parallel_projection) 728 729 ######################################################### 730 if self.qt_widget or self.wx_widget: 731 if self.qt_widget: 732 self.window = self.qt_widget.GetRenderWindow() # overwrite 733 else: 734 self.window = self.wx_widget.GetRenderWindow() 735 self.interactor = self.window.GetInteractor() 736 737 ######################################################### 738 for r in self.renderers: 739 self.window.AddRenderer(r) 740 # set the background gradient if any 741 if vedo.settings.background_gradient_orientation > 0: 742 try: 743 modes = [ 744 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 745 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 746 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 747 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 748 ] 749 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 750 r.GradientBackgroundOn() 751 except AttributeError: 752 pass 753 754 ######################################################### 755 if self.qt_widget or self.wx_widget: 756 # self.window.SetSize(int(self.size[0]), int(self.size[1])) 757 self.interactor.SetRenderWindow(self.window) 758 # vsty = vtki.new("InteractorStyleTrackballCamera") 759 # self.interactor.SetInteractorStyle(vsty) 760 if vedo.settings.enable_default_keyboard_callbacks: 761 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 762 if vedo.settings.enable_default_mouse_callbacks: 763 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick) 764 return ################ 765 ######################## 766 767 if self.size[0] == "f": # full screen 768 self.size = "fullscreen" 769 self.window.SetFullScreen(True) 770 self.window.BordersOn() 771 else: 772 self.window.SetSize(int(self.size[0]), int(self.size[1])) 773 774 if self.offscreen: 775 if self.axes in (4, 5, 8, 12, 14): 776 self.axes = 0 # does not work with those 777 self.window.SetOffScreenRendering(True) 778 self.interactor = None 779 self._interactive = False 780 return ################ 781 ######################## 782 783 self.window.SetPosition(pos) 784 785 ######################################################### 786 self.interactor = vtki.vtkRenderWindowInteractor() 787 788 self.interactor.SetRenderWindow(self.window) 789 vsty = vtki.new("InteractorStyleTrackballCamera") 790 self.interactor.SetInteractorStyle(vsty) 791 self.interactor.RemoveObservers("CharEvent") 792 793 if vedo.settings.enable_default_keyboard_callbacks: 794 self.interactor.AddObserver("KeyPressEvent", self._default_keypress) 795 if vedo.settings.enable_default_mouse_callbacks: 796 self.interactor.AddObserver("LeftButtonPressEvent", self._default_mouseleftclick)
Arguments:
- shape : (str, list) shape of the grid of renderers in format (rows, columns). Ignored if N is specified.
- N : (int) number of desired renderers arranged in a grid automatically.
- pos : (list) (x,y) position in pixels of top-left corner of the rendering window on the screen
- size : (str, list) size of the rendering window. If 'auto', guess it based on screensize.
- screensize : (list) physical size of the monitor screen in pixels
- bg : (color, str) background color or specify jpg image file name with path
- bg2 : (color) background color of a gradient towards the top
- title : (str) window title
axes : (int)
Note that Axes type-1 can be fully customized by passing a dictionary
axes=dict()
. Check outvedo.addons.Axes()
for the available options.- 0, no axes - 1, draw three gray grid walls - 2, show cartesian axes from (0,0,0) - 3, show positive range of cartesian axes from (0,0,0) - 4, show a triad at bottom left - 5, show a cube at bottom left - 6, mark the corners of the bounding box - 7, draw a 3D ruler at each side of the cartesian axes - 8, show the VTK CubeAxesActor object - 9, show the bounding box outLine - 10, show three circles representing the maximum bounding box - 11, show a large grid on the x-y plane (use with zoom=8) - 12, show polar axes - 13, draw a simple ruler at the bottom of the window - 14: draw a camera orientation widget
- sharecam : (bool) if False each renderer will have an independent camera
- interactive : (bool) if True will stop after show() to allow interaction with the 3d scene
- offscreen : (bool) if True will not show the rendering window
- qt_widget : (QVTKRenderWindowInteractor)
render in a Qt-Widget using an QVTKRenderWindowInteractor.
See examples
qt_windows[1,2,3].py
andqt_cutter.py
.
867 def print(self): 868 """Print information about the current instance.""" 869 print(self.__str__()) 870 return self
Print information about the current instance.
888 def initialize_interactor(self) -> Self: 889 """Initialize the interactor if not already initialized.""" 890 if self.offscreen: 891 return self 892 if self.interactor: 893 if not self.interactor.GetInitialized(): 894 self.interactor.Initialize() 895 self.interactor.RemoveObservers("CharEvent") 896 return self
Initialize the interactor if not already initialized.
898 def process_events(self) -> Self: 899 """Process all pending events.""" 900 self.initialize_interactor() 901 if self.interactor: 902 try: 903 self.interactor.ProcessEvents() 904 except AttributeError: 905 pass 906 return self
Process all pending events.
908 def at(self, nren: int, yren=None) -> Self: 909 """ 910 Select the current renderer number as an int. 911 Can also use the `[nx, ny]` format. 912 """ 913 if utils.is_sequence(nren): 914 if len(nren) == 2: 915 nren, yren = nren 916 else: 917 vedo.logger.error("at() argument must be a single number or a list of two numbers") 918 raise RuntimeError 919 920 if yren is not None: 921 a, b = self.shape 922 x, y = nren, yren 923 nren = x * b + y 924 # print("at (", x, y, ") -> ren", nren) 925 if nren < 0 or nren > len(self.renderers) or x >= a or y >= b: 926 vedo.logger.error(f"at({nren, yren}) is malformed!") 927 raise RuntimeError 928 929 self.renderer = self.renderers[nren] 930 return self
Select the current renderer number as an int.
Can also use the [nx, ny]
format.
932 def add(self, *objs, at=None) -> Self: 933 """ 934 Append the input objects to the internal list of objects to be shown. 935 936 Arguments: 937 at : (int) 938 add the object at the specified renderer 939 """ 940 if at is not None: 941 ren = self.renderers[at] 942 else: 943 ren = self.renderer 944 945 objs = utils.flatten(objs) 946 for ob in objs: 947 if ob and ob not in self.objects: 948 self.objects.append(ob) 949 950 acts = self._scan_input_return_acts(objs) 951 952 for a in acts: 953 954 if ren: 955 if isinstance(a, vedo.addons.BaseCutter): 956 a.add_to(self) # from cutters 957 continue 958 959 if isinstance(a, vtki.vtkLight): 960 ren.AddLight(a) 961 continue 962 963 try: 964 ren.AddActor(a) 965 except TypeError: 966 ren.AddActor(a.actor) 967 968 try: 969 ir = self.renderers.index(ren) 970 a.rendered_at.add(ir) # might not have rendered_at 971 except (AttributeError, ValueError): 972 pass 973 974 if isinstance(a, vtki.vtkFollower): 975 a.SetCamera(self.camera) 976 elif isinstance(a, vedo.visual.LightKit): 977 a.lightkit.AddLightsToRenderer(ren) 978 979 return self
Append the input objects to the internal list of objects to be shown.
Arguments:
- at : (int) add the object at the specified renderer
981 def remove(self, *objs, at=None) -> Self: 982 """ 983 Remove input object to the internal list of objects to be shown. 984 985 Objects to be removed can be referenced by their assigned name, 986 987 Arguments: 988 at : (int) 989 remove the object at the specified renderer 990 """ 991 # TODO and you can also use wildcards like `*` and `?`. 992 if at is not None: 993 ren = self.renderers[at] 994 else: 995 ren = self.renderer 996 997 objs = [ob for ob in utils.flatten(objs) if ob] 998 999 has_str = False 1000 for ob in objs: 1001 if isinstance(ob, str): 1002 has_str = True 1003 break 1004 1005 has_actor = False 1006 for ob in objs: 1007 if hasattr(ob, "actor") and ob.actor: 1008 has_actor = True 1009 break 1010 1011 if has_str or has_actor: 1012 # need to get the actors to search for 1013 for a in self.get_actors(include_non_pickables=True): 1014 # print("PARSING", [a]) 1015 try: 1016 if (a.name and a.name in objs) or a in objs: 1017 objs.append(a) 1018 # if a.name: 1019 # bools = [utils.parse_pattern(ob, a.name)[0] for ob in objs] 1020 # if any(bools) or a in objs: 1021 # objs.append(a) 1022 # print('a.name',a.name, objs,any(bools)) 1023 except AttributeError: # no .name 1024 # passing the actor so get back the object with .retrieve_object() 1025 try: 1026 vobj = a.retrieve_object() 1027 if (vobj.name and vobj.name in objs) or vobj in objs: 1028 # print('vobj.name', vobj.name) 1029 objs.append(vobj) 1030 except AttributeError: 1031 pass 1032 1033 ir = self.renderers.index(ren) 1034 1035 ids = [] 1036 for ob in set(objs): 1037 1038 # will remove it from internal list if possible 1039 try: 1040 idx = self.objects.index(ob) 1041 ids.append(idx) 1042 except ValueError: 1043 pass 1044 1045 if ren: ### remove it from the renderer 1046 1047 if isinstance(ob, vedo.addons.BaseCutter): 1048 ob.remove_from(self) # from cutters 1049 continue 1050 1051 try: 1052 ren.RemoveActor(ob) 1053 except TypeError: 1054 try: 1055 ren.RemoveActor(ob.actor) 1056 except AttributeError: 1057 pass 1058 1059 if hasattr(ob, "rendered_at"): 1060 ob.rendered_at.discard(ir) 1061 1062 if hasattr(ob, "scalarbar") and ob.scalarbar: 1063 ren.RemoveActor(ob.scalarbar) 1064 if hasattr(ob, "_caption") and ob._caption: 1065 ren.RemoveActor(ob._caption) 1066 if hasattr(ob, "shadows") and ob.shadows: 1067 for sha in ob.shadows: 1068 ren.RemoveActor(sha.actor) 1069 if hasattr(ob, "trail") and ob.trail: 1070 ren.RemoveActor(ob.trail.actor) 1071 ob.trail_points = [] 1072 if hasattr(ob.trail, "shadows") and ob.trail.shadows: 1073 for sha in ob.trail.shadows: 1074 ren.RemoveActor(sha.actor) 1075 1076 elif isinstance(ob, vedo.visual.LightKit): 1077 ob.lightkit.RemoveLightsFromRenderer(ren) 1078 1079 # for i in ids: # WRONG way of doing it! 1080 # del self.objects[i] 1081 # instead we do: 1082 self.objects = [ele for i, ele in enumerate(self.objects) if i not in ids] 1083 return self
Remove input object to the internal list of objects to be shown.
Objects to be removed can be referenced by their assigned name,
Arguments:
- at : (int) remove the object at the specified renderer
1085 @property 1086 def actors(self): 1087 """Return the list of actors.""" 1088 return [ob.actor for ob in self.objects if hasattr(ob, "actor")]
Return the list of actors.
1090 def remove_lights(self) -> Self: 1091 """Remove all the present lights in the current renderer.""" 1092 if self.renderer: 1093 self.renderer.RemoveAllLights() 1094 return self
Remove all the present lights in the current renderer.
1096 def pop(self, at=None) -> Self: 1097 """ 1098 Remove the last added object from the rendering window. 1099 This method is typically used in loops or callback functions. 1100 """ 1101 if at is not None and not isinstance(at, int): 1102 # wrong usage pitfall 1103 vedo.logger.error("argument of pop() must be an integer") 1104 raise RuntimeError() 1105 1106 if self.objects: 1107 self.remove(self.objects[-1], at) 1108 return self
Remove the last added object from the rendering window. This method is typically used in loops or callback functions.
1110 def render(self, resetcam=False) -> Self: 1111 """Render the scene. This method is typically used in loops or callback functions.""" 1112 1113 if vedo.settings.dry_run_mode >= 2: 1114 return self 1115 1116 if not self.window: 1117 return self 1118 1119 self.initialize_interactor() 1120 1121 if resetcam: 1122 self.renderer.ResetCamera() 1123 1124 self.window.Render() 1125 1126 if self._cocoa_process_events and self.interactor and self.interactor.GetInitialized(): 1127 if "Darwin" in vedo.sys_platform and not self.offscreen: 1128 self.interactor.ProcessEvents() 1129 self._cocoa_process_events = False 1130 return self
Render the scene. This method is typically used in loops or callback functions.
1132 def interactive(self) -> Self: 1133 """ 1134 Start window interaction. 1135 Analogous to `show(..., interactive=True)`. 1136 """ 1137 if vedo.settings.dry_run_mode >= 1: 1138 return self 1139 self.initialize_interactor() 1140 if self.interactor: 1141 # print("self.interactor.Start()") 1142 self.interactor.Start() 1143 # print("self.interactor.Start() done") 1144 if self._must_close_now: 1145 # print("self.interactor.TerminateApp()") 1146 if self.interactor: 1147 self.interactor.GetRenderWindow().Finalize() 1148 self.interactor.TerminateApp() 1149 self.interactor = None 1150 self.window = None 1151 self.renderer = None 1152 self.renderers = [] 1153 self.camera = None 1154 return self
Start window interaction.
Analogous to show(..., interactive=True)
.
1156 def use_depth_peeling(self, at=None, value=True) -> Self: 1157 """ 1158 Specify whether use depth peeling algorithm at this specific renderer 1159 Call this method before the first rendering. 1160 """ 1161 if at is None: 1162 ren = self.renderer 1163 else: 1164 ren = self.renderers[at] 1165 ren.SetUseDepthPeeling(value) 1166 return self
Specify whether use depth peeling algorithm at this specific renderer Call this method before the first rendering.
1168 def background(self, c1=None, c2=None, at=None, mode=0) -> Union[Self, "np.ndarray"]: 1169 """Set the color of the background for the current renderer. 1170 A different renderer index can be specified by keyword `at`. 1171 1172 Arguments: 1173 c1 : (list) 1174 background main color. 1175 c2 : (list) 1176 background color for the upper part of the window. 1177 at : (int) 1178 renderer index. 1179 mode : (int) 1180 background mode (needs vtk version >= 9.3) 1181 0 = vertical, 1182 1 = horizontal, 1183 2 = radial farthest side, 1184 3 = radia farthest corner. 1185 """ 1186 if not self.renderers: 1187 return self 1188 if at is None: 1189 r = self.renderer 1190 else: 1191 r = self.renderers[at] 1192 1193 if c1 is None and c2 is None: 1194 return np.array(r.GetBackground()) 1195 1196 if r: 1197 if c1 is not None: 1198 r.SetBackground(vedo.get_color(c1)) 1199 if c2 is not None: 1200 r.GradientBackgroundOn() 1201 r.SetBackground2(vedo.get_color(c2)) 1202 if mode: 1203 try: # only works with vtk>=9.3 1204 modes = [ 1205 vtki.vtkViewport.GradientModes.VTK_GRADIENT_VERTICAL, 1206 vtki.vtkViewport.GradientModes.VTK_GRADIENT_HORIZONTAL, 1207 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE, 1208 vtki.vtkViewport.GradientModes.VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_CORNER, 1209 ] 1210 r.SetGradientMode(modes[vedo.settings.background_gradient_orientation]) 1211 except AttributeError: 1212 pass 1213 1214 else: 1215 r.GradientBackgroundOff() 1216 return self
Set the color of the background for the current renderer.
A different renderer index can be specified by keyword at
.
Arguments:
- c1 : (list) background main color.
- c2 : (list) background color for the upper part of the window.
- at : (int) renderer index.
- mode : (int) background mode (needs vtk version >= 9.3) 0 = vertical, 1 = horizontal, 2 = radial farthest side, 3 = radia farthest corner.
1219 def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True) -> list: 1220 """ 1221 Return a list of Meshes from the specified renderer. 1222 1223 Arguments: 1224 at : (int) 1225 specify which renderer to look at. 1226 include_non_pickables : (bool) 1227 include non-pickable objects 1228 unpack_assemblies : (bool) 1229 unpack assemblies into their components 1230 """ 1231 if at is None: 1232 renderer = self.renderer 1233 at = self.renderers.index(renderer) 1234 elif isinstance(at, int): 1235 renderer = self.renderers[at] 1236 1237 has_global_axes = False 1238 if isinstance(self.axes_instances[at], vedo.Assembly): 1239 has_global_axes = True 1240 1241 if unpack_assemblies: 1242 acs = renderer.GetActors() 1243 else: 1244 acs = renderer.GetViewProps() 1245 1246 objs = [] 1247 acs.InitTraversal() 1248 for _ in range(acs.GetNumberOfItems()): 1249 1250 if unpack_assemblies: 1251 a = acs.GetNextItem() 1252 else: 1253 a = acs.GetNextProp() 1254 1255 if isinstance(a, vtki.vtkVolume): 1256 continue 1257 1258 if include_non_pickables or a.GetPickable(): 1259 if a == self.axes_instances[at]: 1260 continue 1261 if has_global_axes and a in self.axes_instances[at].actors: 1262 continue 1263 try: 1264 objs.append(a.retrieve_object()) 1265 except AttributeError: 1266 pass 1267 return objs
Return a list of Meshes from the specified renderer.
Arguments:
- at : (int) specify which renderer to look at.
- include_non_pickables : (bool) include non-pickable objects
- unpack_assemblies : (bool) unpack assemblies into their components
1269 def get_volumes(self, at=None, include_non_pickables=False) -> list: 1270 """ 1271 Return a list of Volumes from the specified renderer. 1272 1273 Arguments: 1274 at : (int) 1275 specify which renderer to look at 1276 include_non_pickables : (bool) 1277 include non-pickable objects 1278 """ 1279 if at is None: 1280 renderer = self.renderer 1281 at = self.renderers.index(renderer) 1282 elif isinstance(at, int): 1283 renderer = self.renderers[at] 1284 1285 vols = [] 1286 acs = renderer.GetVolumes() 1287 acs.InitTraversal() 1288 for _ in range(acs.GetNumberOfItems()): 1289 a = acs.GetNextItem() 1290 if include_non_pickables or a.GetPickable(): 1291 try: 1292 vols.append(a.retrieve_object()) 1293 except AttributeError: 1294 pass 1295 return vols
Return a list of Volumes from the specified renderer.
Arguments:
- at : (int) specify which renderer to look at
- include_non_pickables : (bool) include non-pickable objects
1297 def get_actors(self, at=None, include_non_pickables=False) -> list: 1298 """ 1299 Return a list of Volumes from the specified renderer. 1300 1301 Arguments: 1302 at : (int) 1303 specify which renderer to look at 1304 include_non_pickables : (bool) 1305 include non-pickable objects 1306 """ 1307 if at is None: 1308 renderer = self.renderer 1309 at = self.renderers.index(renderer) 1310 elif isinstance(at, int): 1311 renderer = self.renderers[at] 1312 1313 acts = [] 1314 acs = renderer.GetViewProps() 1315 acs.InitTraversal() 1316 for _ in range(acs.GetNumberOfItems()): 1317 a = acs.GetNextProp() 1318 if include_non_pickables or a.GetPickable(): 1319 acts.append(a) 1320 return acts
Return a list of Volumes from the specified renderer.
Arguments:
- at : (int) specify which renderer to look at
- include_non_pickables : (bool) include non-pickable objects
1322 def check_actors_trasform(self, at=None) -> Self: 1323 """ 1324 Reset the transformation matrix of all actors at specified renderer. 1325 This is only useful when actors have been moved/rotated/scaled manually 1326 in an already rendered scene using interactors like 1327 'TrackballActor' or 'JoystickActor'. 1328 """ 1329 # see issue https://github.com/marcomusy/vedo/issues/1046 1330 for a in self.get_actors(at=at, include_non_pickables=True): 1331 try: 1332 M = a.GetMatrix() 1333 except AttributeError: 1334 continue 1335 if M and not M.IsIdentity(): 1336 try: 1337 a.retrieve_object().apply_transform_from_actor() 1338 # vedo.logger.info( 1339 # f"object '{a.retrieve_object().name}' " 1340 # "was manually moved. Updated to its current position." 1341 # ) 1342 except AttributeError: 1343 pass 1344 return self
Reset the transformation matrix of all actors at specified renderer. This is only useful when actors have been moved/rotated/scaled manually in an already rendered scene using interactors like 'TrackballActor' or 'JoystickActor'.
1346 def reset_camera(self, tight=None) -> Self: 1347 """ 1348 Reset the camera position and zooming. 1349 If tight (float) is specified the zooming reserves a padding space 1350 in the xy-plane expressed in percent of the average size. 1351 """ 1352 if tight is None: 1353 self.renderer.ResetCamera() 1354 else: 1355 x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds() 1356 cam = self.camera 1357 1358 self.renderer.ComputeAspect() 1359 aspect = self.renderer.GetAspect() 1360 angle = np.pi * cam.GetViewAngle() / 180.0 1361 dx = x1 - x0 1362 dy = y1 - y0 1363 dist = max(dx / aspect[0], dy) / np.tan(angle / 2) / 2 1364 1365 cam.SetViewUp(0, 1, 0) 1366 cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight)) 1367 cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0) 1368 if cam.GetParallelProjection(): 1369 ps = max(dx / aspect[0], dy) / 2 1370 cam.SetParallelScale(ps * (1 + tight)) 1371 self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1372 return self
Reset the camera position and zooming. If tight (float) is specified the zooming reserves a padding space in the xy-plane expressed in percent of the average size.
1374 def reset_clipping_range(self, bounds=None) -> Self: 1375 """ 1376 Reset the camera clipping range to include all visible actors. 1377 If bounds is given, it will be used instead of computing it. 1378 """ 1379 if bounds is None: 1380 self.renderer.ResetCameraClippingRange() 1381 else: 1382 self.renderer.ResetCameraClippingRange(bounds) 1383 return self
Reset the camera clipping range to include all visible actors. If bounds is given, it will be used instead of computing it.
1385 def reset_viewup(self, smooth=True) -> Self: 1386 """ 1387 Reset the orientation of the camera to the closest orthogonal direction and view-up. 1388 """ 1389 vbb = addons.compute_visible_bounds()[0] 1390 x0, x1, y0, y1, z0, z1 = vbb 1391 mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2 1392 d = self.camera.GetDistance() 1393 1394 viewups = np.array( 1395 [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1), (1, 0, 0), (-1, 0, 0)] 1396 ) 1397 positions = np.array( 1398 [ 1399 (mx, my, mz + d), 1400 (mx, my, mz - d), 1401 (mx, my + d, mz), 1402 (mx, my - d, mz), 1403 (mx + d, my, mz), 1404 (mx - d, my, mz), 1405 ] 1406 ) 1407 1408 vu = np.array(self.camera.GetViewUp()) 1409 vui = np.argmin(np.linalg.norm(viewups - vu, axis=1)) 1410 1411 poc = np.array(self.camera.GetPosition()) 1412 foc = np.array(self.camera.GetFocalPoint()) 1413 a = poc - foc 1414 b = positions - foc 1415 a = a / np.linalg.norm(a) 1416 b = b.T * (1 / np.linalg.norm(b, axis=1)) 1417 pui = np.argmin(np.linalg.norm(b.T - a, axis=1)) 1418 1419 if smooth: 1420 outtimes = np.linspace(0, 1, num=11, endpoint=True) 1421 for t in outtimes: 1422 vv = vu * (1 - t) + viewups[vui] * t 1423 pp = poc * (1 - t) + positions[pui] * t 1424 ff = foc * (1 - t) + np.array([mx, my, mz]) * t 1425 self.camera.SetViewUp(vv) 1426 self.camera.SetPosition(pp) 1427 self.camera.SetFocalPoint(ff) 1428 self.render() 1429 1430 # interpolator does not respect parallel view...: 1431 # cam1 = dict( 1432 # pos=poc, 1433 # viewup=vu, 1434 # focal_point=(mx,my,mz), 1435 # clipping_range=self.camera.GetClippingRange() 1436 # ) 1437 # # cam1 = self.camera 1438 # cam2 = dict( 1439 # pos=positions[pui], 1440 # viewup=viewups[vui], 1441 # focal_point=(mx,my,mz), 1442 # clipping_range=self.camera.GetClippingRange() 1443 # ) 1444 # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0) 1445 # for c in vcams: 1446 # self.renderer.SetActiveCamera(c) 1447 # self.render() 1448 else: 1449 1450 self.camera.SetViewUp(viewups[vui]) 1451 self.camera.SetPosition(positions[pui]) 1452 self.camera.SetFocalPoint(mx, my, mz) 1453 1454 self.renderer.ResetCameraClippingRange() 1455 1456 # vbb, _, _, _ = addons.compute_visible_bounds() 1457 # x0,x1, y0,y1, z0,z1 = vbb 1458 # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1) 1459 self.render() 1460 return self
Reset the orientation of the camera to the closest orthogonal direction and view-up.
1462 def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()) -> list: 1463 """ 1464 Takes as input two cameras set camera at an interpolated position: 1465 1466 Cameras can be vtkCamera or dictionaries in format: 1467 1468 `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)` 1469 1470 Press `shift-C` key in interactive mode to dump a python snipplet 1471 of parameters for the current camera view. 1472 """ 1473 nc = len(cameras) 1474 if len(times) == 0: 1475 times = np.linspace(0, 1, num=nc, endpoint=True) 1476 1477 assert len(times) == nc 1478 1479 cin = vtki.new("CameraInterpolator") 1480 1481 # cin.SetInterpolationTypeToLinear() # buggy? 1482 if nc > 2 and smooth: 1483 cin.SetInterpolationTypeToSpline() 1484 1485 for i, cam in enumerate(cameras): 1486 vcam = cam 1487 if isinstance(cam, dict): 1488 vcam = utils.camera_from_dict(cam) 1489 cin.AddCamera(times[i], vcam) 1490 1491 mint, maxt = cin.GetMinimumT(), cin.GetMaximumT() 1492 rng = maxt - mint 1493 1494 if len(output_times) == 0: 1495 cin.InterpolateCamera(t * rng, self.camera) 1496 return [self.camera] 1497 else: 1498 vcams = [] 1499 for tt in output_times: 1500 c = vtki.vtkCamera() 1501 cin.InterpolateCamera(tt * rng, c) 1502 vcams.append(c) 1503 return vcams
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.
1505 def fly_to(self, point) -> Self: 1506 """ 1507 Fly camera to the specified point. 1508 1509 Arguments: 1510 point : (list) 1511 point in space to place camera. 1512 1513 Example: 1514 ```python 1515 from vedo import * 1516 cone = Cone() 1517 plt = Plotter(axes=1) 1518 plt.show(cone) 1519 plt.fly_to([1,0,0]) 1520 plt.interactive().close() 1521 ``` 1522 """ 1523 if self.interactor: 1524 self.resetcam = False 1525 self.interactor.FlyTo(self.renderer, point) 1526 return self
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()
1528 def look_at(self, plane="xy") -> Self: 1529 """Move the camera so that it looks at the specified cartesian plane""" 1530 cam = self.renderer.GetActiveCamera() 1531 fp = np.array(cam.GetFocalPoint()) 1532 p = np.array(cam.GetPosition()) 1533 dist = np.linalg.norm(fp - p) 1534 plane = plane.lower() 1535 if "x" in plane and "y" in plane: 1536 cam.SetPosition(fp[0], fp[1], fp[2] + dist) 1537 cam.SetViewUp(0.0, 1.0, 0.0) 1538 elif "x" in plane and "z" in plane: 1539 cam.SetPosition(fp[0], fp[1] - dist, fp[2]) 1540 cam.SetViewUp(0.0, 0.0, 1.0) 1541 elif "y" in plane and "z" in plane: 1542 cam.SetPosition(fp[0] + dist, fp[1], fp[2]) 1543 cam.SetViewUp(0.0, 0.0, 1.0) 1544 else: 1545 vedo.logger.error(f"in plotter.look() cannot understand argument {plane}") 1546 return self
Move the camera so that it looks at the specified cartesian plane
1548 def record(self, filename="") -> str: 1549 """ 1550 Record camera, mouse, keystrokes and all other events. 1551 Recording can be toggled on/off by pressing key "R". 1552 1553 Arguments: 1554 filename : (str) 1555 ascii file to store events. 1556 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1557 1558 Returns: 1559 a string descriptor of events. 1560 1561 Examples: 1562 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1563 """ 1564 if vedo.settings.dry_run_mode >= 1: 1565 return "" 1566 if not self.interactor: 1567 vedo.logger.warning("Cannot record events, no interactor defined.") 1568 return "" 1569 erec = vtki.new("InteractorEventRecorder") 1570 erec.SetInteractor(self.interactor) 1571 if not filename: 1572 if not os.path.exists(vedo.settings.cache_directory): 1573 os.makedirs(vedo.settings.cache_directory) 1574 home_dir = os.path.expanduser("~") 1575 filename = os.path.join( 1576 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1577 print("Events will be recorded in", filename) 1578 erec.SetFileName(filename) 1579 erec.SetKeyPressActivationValue("R") 1580 erec.EnabledOn() 1581 erec.Record() 1582 self.interactor.Start() 1583 erec.Stop() 1584 erec.EnabledOff() 1585 with open(filename, "r", encoding="UTF-8") as fl: 1586 events = fl.read() 1587 erec = None 1588 return events
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:
1590 def play(self, recorded_events="", repeats=0) -> Self: 1591 """ 1592 Play camera, mouse, keystrokes and all other events. 1593 1594 Arguments: 1595 events : (str) 1596 file o string of events. 1597 The default is `vedo.settings.cache_directory+"vedo/recorded_events.log"`. 1598 repeats : (int) 1599 number of extra repeats of the same events. The default is 0. 1600 1601 Examples: 1602 - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py) 1603 """ 1604 if vedo.settings.dry_run_mode >= 1: 1605 return self 1606 if not self.interactor: 1607 vedo.logger.warning("Cannot play events, no interactor defined.") 1608 return self 1609 1610 erec = vtki.new("InteractorEventRecorder") 1611 erec.SetInteractor(self.interactor) 1612 1613 if not recorded_events: 1614 home_dir = os.path.expanduser("~") 1615 recorded_events = os.path.join( 1616 home_dir, vedo.settings.cache_directory, "vedo", "recorded_events.log") 1617 1618 if recorded_events.endswith(".log"): 1619 erec.ReadFromInputStringOff() 1620 erec.SetFileName(recorded_events) 1621 else: 1622 erec.ReadFromInputStringOn() 1623 erec.SetInputString(recorded_events) 1624 1625 erec.Play() 1626 for _ in range(repeats): 1627 erec.Rewind() 1628 erec.Play() 1629 erec.EnabledOff() 1630 erec = None 1631 return self
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:
1633 def parallel_projection(self, value=True, at=None) -> Self: 1634 """ 1635 Use parallel projection `at` a specified renderer. 1636 Object is seen from "infinite" distance, e.i. remove any perspective effects. 1637 An input value equal to -1 will toggle it on/off. 1638 """ 1639 if at is not None: 1640 r = self.renderers[at] 1641 else: 1642 r = self.renderer 1643 if value == -1: 1644 val = r.GetActiveCamera().GetParallelProjection() 1645 value = not val 1646 r.GetActiveCamera().SetParallelProjection(value) 1647 r.Modified() 1648 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.
1655 def fov(self, angle: float) -> Self: 1656 """ 1657 Set the field of view angle for the camera. 1658 This is the angle of the camera frustum in the horizontal direction. 1659 High values will result in a wide-angle lens (fish-eye effect), 1660 and low values will result in a telephoto lens. 1661 1662 Default value is 30 degrees. 1663 """ 1664 self.renderer.GetActiveCamera().UseHorizontalViewAngleOn() 1665 self.renderer.GetActiveCamera().SetViewAngle(angle) 1666 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.
1668 def zoom(self, zoom: float) -> Self: 1669 """Apply a zooming factor for the current camera view""" 1670 self.renderer.GetActiveCamera().Zoom(zoom) 1671 return self
Apply a zooming factor for the current camera view
1673 def azimuth(self, angle: float) -> Self: 1674 """Rotate camera around the view up vector.""" 1675 self.renderer.GetActiveCamera().Azimuth(angle) 1676 return self
Rotate camera around the view up vector.
1678 def elevation(self, angle: float) -> Self: 1679 """Rotate the camera around the cross product of the negative 1680 of the direction of projection and the view up vector.""" 1681 self.renderer.GetActiveCamera().Elevation(angle) 1682 return self
Rotate the camera around the cross product of the negative of the direction of projection and the view up vector.
1684 def roll(self, angle: float) -> Self: 1685 """Roll the camera about the direction of projection.""" 1686 self.renderer.GetActiveCamera().Roll(angle) 1687 return self
Roll the camera about the direction of projection.
1689 def dolly(self, value: float) -> Self: 1690 """Move the camera towards (value>0) or away from (value<0) the focal point.""" 1691 self.renderer.GetActiveCamera().Dolly(value) 1692 return self
Move the camera towards (value>0) or away from (value<0) the focal point.
1695 def add_slider( 1696 self, 1697 sliderfunc, 1698 xmin, 1699 xmax, 1700 value=None, 1701 pos=4, 1702 title="", 1703 font="Calco", 1704 title_size=1, 1705 c=None, 1706 alpha=1, 1707 show_value=True, 1708 delayed=False, 1709 **options, 1710 ) -> "vedo.addons.Slider2D": 1711 """ 1712 Add a `vedo.addons.Slider2D` which can call an external custom function. 1713 1714 Arguments: 1715 sliderfunc : (Callable) 1716 external function to be called by the widget 1717 xmin : (float) 1718 lower value of the slider 1719 xmax : (float) 1720 upper value 1721 value : (float) 1722 current value 1723 pos : (list, str) 1724 position corner number: horizontal [1-5] or vertical [11-15] 1725 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1726 and also by a string descriptor (eg. "bottom-left") 1727 title : (str) 1728 title text 1729 font : (str) 1730 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1731 title_size : (float) 1732 title text scale [1.0] 1733 show_value : (bool) 1734 if True current value is shown 1735 delayed : (bool) 1736 if True the callback is delayed until when the mouse button is released 1737 alpha : (float) 1738 opacity of the scalar bar texts 1739 slider_length : (float) 1740 slider length 1741 slider_width : (float) 1742 slider width 1743 end_cap_length : (float) 1744 length of the end cap 1745 end_cap_width : (float) 1746 width of the end cap 1747 tube_width : (float) 1748 width of the tube 1749 title_height : (float) 1750 width of the title 1751 tformat : (str) 1752 format of the title 1753 1754 Examples: 1755 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1756 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1757 1758 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1759 """ 1760 if c is None: # automatic black or white 1761 c = (0.8, 0.8, 0.8) 1762 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1763 c = (0.2, 0.2, 0.2) 1764 else: 1765 c = vedo.get_color(c) 1766 1767 slider2d = addons.Slider2D( 1768 sliderfunc, 1769 xmin, 1770 xmax, 1771 value, 1772 pos, 1773 title, 1774 font, 1775 title_size, 1776 c, 1777 alpha, 1778 show_value, 1779 delayed, 1780 **options, 1781 ) 1782 1783 if self.renderer: 1784 slider2d.renderer = self.renderer 1785 if self.interactor: 1786 slider2d.interactor = self.interactor 1787 slider2d.on() 1788 self.sliders.append([slider2d, sliderfunc]) 1789 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:
1791 def add_slider3d( 1792 self, 1793 sliderfunc, 1794 pos1, 1795 pos2, 1796 xmin, 1797 xmax, 1798 value=None, 1799 s=0.03, 1800 t=1, 1801 title="", 1802 rotation=0.0, 1803 c=None, 1804 show_value=True, 1805 ) -> "vedo.addons.Slider3D": 1806 """ 1807 Add a 3D slider widget which can call an external custom function. 1808 1809 Arguments: 1810 sliderfunc : (function) 1811 external function to be called by the widget 1812 pos1 : (list) 1813 first position 3D coordinates 1814 pos2 : (list) 1815 second position coordinates 1816 xmin : (float) 1817 lower value 1818 xmax : (float) 1819 upper value 1820 value : (float) 1821 initial value 1822 s : (float) 1823 label scaling factor 1824 t : (float) 1825 tube scaling factor 1826 title : (str) 1827 title text 1828 c : (color) 1829 slider color 1830 rotation : (float) 1831 title rotation around slider axis 1832 show_value : (bool) 1833 if True current value is shown 1834 1835 Examples: 1836 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1837 1838 ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png) 1839 """ 1840 if c is None: # automatic black or white 1841 c = (0.8, 0.8, 0.8) 1842 if np.sum(vedo.get_color(self.backgrcol)) > 1.5: 1843 c = (0.2, 0.2, 0.2) 1844 else: 1845 c = vedo.get_color(c) 1846 1847 slider3d = addons.Slider3D( 1848 sliderfunc, 1849 pos1, 1850 pos2, 1851 xmin, 1852 xmax, 1853 value, 1854 s, 1855 t, 1856 title, 1857 rotation, 1858 c, 1859 show_value, 1860 ) 1861 slider3d.renderer = self.renderer 1862 slider3d.interactor = self.interactor 1863 slider3d.on() 1864 self.sliders.append([slider3d, sliderfunc]) 1865 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:
1924 def add_spline_tool( 1925 self, points, pc="k", ps=8, lc="r4", ac="g5", 1926 lw=2, alpha=1, closed=False, ontop=True, can_add_nodes=True, 1927 ) -> "vedo.addons.SplineTool": 1928 """ 1929 Add a spline tool to the current plotter. 1930 Nodes of the spline can be dragged in space with the mouse. 1931 Clicking on the line itself adds an extra point. 1932 Selecting a point and pressing del removes it. 1933 1934 Arguments: 1935 points : (Mesh, Points, array) 1936 the set of vertices forming the spline nodes. 1937 pc : (str) 1938 point color. The default is 'k'. 1939 ps : (str) 1940 point size. The default is 8. 1941 lc : (str) 1942 line color. The default is 'r4'. 1943 ac : (str) 1944 active point marker color. The default is 'g5'. 1945 lw : (int) 1946 line width. The default is 2. 1947 alpha : (float) 1948 line transparency. 1949 closed : (bool) 1950 spline is meant to be closed. The default is False. 1951 1952 Returns: 1953 a `SplineTool` object. 1954 1955 Examples: 1956 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 1957 1958 ![](https://vedo.embl.es/images/basic/spline_tool.png) 1959 """ 1960 sw = addons.SplineTool(points, pc, ps, lc, ac, lw, alpha, closed, ontop, can_add_nodes) 1961 sw.interactor = self.interactor 1962 sw.on() 1963 sw.Initialize(sw.points.dataset) 1964 sw.representation.SetRenderer(self.renderer) 1965 sw.representation.SetClosedLoop(closed) 1966 sw.representation.BuildRepresentation() 1967 self.widgets.append(sw) 1968 return sw
Add a spline tool to the current plotter. Nodes of the spline can be dragged in space with the mouse. Clicking on the line itself adds an extra point. Selecting a point and pressing del removes it.
Arguments:
- points : (Mesh, Points, array) the set of vertices forming the spline nodes.
- pc : (str) point color. The default is 'k'.
- ps : (str) point size. The default is 8.
- lc : (str) line color. The default is 'r4'.
- ac : (str) active point marker color. The default is 'g5'.
- lw : (int) line width. The default is 2.
- alpha : (float) line transparency.
- closed : (bool) spline is meant to be closed. The default is False.
Returns:
a
SplineTool
object.
Examples:
1970 def add_icon(self, icon, pos=3, size=0.08) -> "vedo.addons.Icon": 1971 """Add an inset icon mesh into the same renderer. 1972 1973 Arguments: 1974 pos : (int, list) 1975 icon position in the range [1-4] indicating one of the 4 corners, 1976 or it can be a tuple (x,y) as a fraction of the renderer size. 1977 size : (float) 1978 size of the square inset. 1979 1980 Examples: 1981 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 1982 """ 1983 iconw = addons.Icon(icon, pos, size) 1984 1985 iconw.SetInteractor(self.interactor) 1986 iconw.EnabledOn() 1987 iconw.InteractiveOff() 1988 self.widgets.append(iconw) 1989 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:
1991 def add_global_axes(self, axtype=None, c=None) -> Self: 1992 """Draw axes on scene. Available axes types: 1993 1994 Arguments: 1995 axtype : (int) 1996 - 0, no axes, 1997 - 1, draw three gray grid walls 1998 - 2, show cartesian axes from (0,0,0) 1999 - 3, show positive range of cartesian axes from (0,0,0) 2000 - 4, show a triad at bottom left 2001 - 5, show a cube at bottom left 2002 - 6, mark the corners of the bounding box 2003 - 7, draw a 3D ruler at each side of the cartesian axes 2004 - 8, show the vtkCubeAxesActor object 2005 - 9, show the bounding box outLine 2006 - 10, show three circles representing the maximum bounding box 2007 - 11, show a large grid on the x-y plane 2008 - 12, show polar axes 2009 - 13, draw a simple ruler at the bottom of the window 2010 2011 Axis type-1 can be fully customized by passing a dictionary axes=dict(). 2012 2013 Example: 2014 ```python 2015 from vedo import Box, show 2016 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 2017 show( 2018 b, 2019 axes={ 2020 "xtitle": "Some long variable [a.u.]", 2021 "number_of_divisions": 4, 2022 # ... 2023 }, 2024 ) 2025 ``` 2026 2027 Examples: 2028 - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py) 2029 - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py) 2030 - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py) 2031 - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py) 2032 2033 <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600"> 2034 """ 2035 addons.add_global_axes(axtype, c) 2036 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:
2038 def add_legend_box(self, **kwargs) -> "vedo.addons.LegendBox": 2039 """Add a legend to the top right. 2040 2041 Examples: 2042 - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py), 2043 - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py) 2044 - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py) 2045 """ 2046 acts = self.get_meshes() 2047 lb = addons.LegendBox(acts, **kwargs) 2048 self.add(lb) 2049 return lb
2051 def add_hint( 2052 self, 2053 obj, 2054 text="", 2055 c="k", 2056 bg="yellow9", 2057 font="Calco", 2058 size=18, 2059 justify=0, 2060 angle=0, 2061 delay=250, 2062 ) -> Union[vtki.vtkBalloonWidget, None]: 2063 """ 2064 Create a pop-up hint style message when hovering an object. 2065 Use `add_hint(obj, False)` to disable a hinting a specific object. 2066 Use `add_hint(None)` to disable all hints. 2067 2068 Arguments: 2069 obj : (Mesh, Points) 2070 the object to associate the pop-up to 2071 text : (str) 2072 string description of the pop-up 2073 delay : (int) 2074 milliseconds to wait before pop-up occurs 2075 """ 2076 if self.offscreen or not self.interactor: 2077 return None 2078 2079 if vedo.vtk_version[:2] == (9, 0) and "Linux" in vedo.sys_platform: 2080 # Linux vtk9.0 is bugged 2081 vedo.logger.warning( 2082 f"add_hint() is not available on Linux platforms for vtk{vedo.vtk_version}." 2083 ) 2084 return None 2085 2086 if obj is None: 2087 self.hint_widget.EnabledOff() 2088 self.hint_widget.SetInteractor(None) 2089 self.hint_widget = None 2090 return self.hint_widget 2091 2092 if text is False and self.hint_widget: 2093 self.hint_widget.RemoveBalloon(obj) 2094 return self.hint_widget 2095 2096 if text == "": 2097 if obj.name: 2098 text = obj.name 2099 elif obj.filename: 2100 text = obj.filename 2101 else: 2102 return None 2103 2104 if not self.hint_widget: 2105 self.hint_widget = vtki.vtkBalloonWidget() 2106 2107 rep = self.hint_widget.GetRepresentation() 2108 rep.SetBalloonLayoutToImageRight() 2109 2110 trep = rep.GetTextProperty() 2111 trep.SetFontFamily(vtki.VTK_FONT_FILE) 2112 trep.SetFontFile(utils.get_font_path(font)) 2113 trep.SetFontSize(size) 2114 trep.SetColor(vedo.get_color(c)) 2115 trep.SetBackgroundColor(vedo.get_color(bg)) 2116 trep.SetShadow(0) 2117 trep.SetJustification(justify) 2118 trep.UseTightBoundingBoxOn() 2119 2120 self.hint_widget.ManagesCursorOff() 2121 self.hint_widget.SetTimerDuration(delay) 2122 self.hint_widget.SetInteractor(self.interactor) 2123 if angle: 2124 trep.SetOrientation(angle) 2125 trep.SetBackgroundOpacity(0) 2126 # else: 2127 # trep.SetBackgroundOpacity(0.5) # doesnt work well 2128 self.hint_widget.SetRepresentation(rep) 2129 self.widgets.append(self.hint_widget) 2130 self.hint_widget.EnabledOn() 2131 2132 bst = self.hint_widget.GetBalloonString(obj.actor) 2133 if bst: 2134 self.hint_widget.UpdateBalloonString(obj.actor, text) 2135 else: 2136 self.hint_widget.AddBalloon(obj.actor, text) 2137 2138 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
2140 def add_shadows(self) -> Self: 2141 """Add shadows at the current renderer.""" 2142 if self.renderer: 2143 shadows = vtki.new("ShadowMapPass") 2144 seq = vtki.new("SequencePass") 2145 passes = vtki.new("RenderPassCollection") 2146 passes.AddItem(shadows.GetShadowMapBakerPass()) 2147 passes.AddItem(shadows) 2148 seq.SetPasses(passes) 2149 camerapass = vtki.new("CameraPass") 2150 camerapass.SetDelegatePass(seq) 2151 self.renderer.SetPass(camerapass) 2152 return self
Add shadows at the current renderer.
2154 def add_ambient_occlusion(self, radius: float, bias=0.01, blur=True, samples=100) -> Self: 2155 """ 2156 Screen Space Ambient Occlusion. 2157 2158 For every pixel on the screen, the pixel shader samples the depth values around 2159 the current pixel and tries to compute the amount of occlusion from each of the sampled 2160 points. 2161 2162 Arguments: 2163 radius : (float) 2164 radius of influence in absolute units 2165 bias : (float) 2166 bias of the normals 2167 blur : (bool) 2168 add a blurring to the sampled positions 2169 samples : (int) 2170 number of samples to probe 2171 2172 Examples: 2173 - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py) 2174 2175 ![](https://vedo.embl.es/images/basic/ssao.jpg) 2176 """ 2177 lights = vtki.new("LightsPass") 2178 2179 opaque = vtki.new("OpaquePass") 2180 2181 ssaoCam = vtki.new("CameraPass") 2182 ssaoCam.SetDelegatePass(opaque) 2183 2184 ssao = vtki.new("SSAOPass") 2185 ssao.SetRadius(radius) 2186 ssao.SetBias(bias) 2187 ssao.SetBlur(blur) 2188 ssao.SetKernelSize(samples) 2189 ssao.SetDelegatePass(ssaoCam) 2190 2191 translucent = vtki.new("TranslucentPass") 2192 2193 volpass = vtki.new("VolumetricPass") 2194 ddp = vtki.new("DualDepthPeelingPass") 2195 ddp.SetTranslucentPass(translucent) 2196 ddp.SetVolumetricPass(volpass) 2197 2198 over = vtki.new("OverlayPass") 2199 2200 collection = vtki.new("RenderPassCollection") 2201 collection.AddItem(lights) 2202 collection.AddItem(ssao) 2203 collection.AddItem(ddp) 2204 collection.AddItem(over) 2205 2206 sequence = vtki.new("SequencePass") 2207 sequence.SetPasses(collection) 2208 2209 cam = vtki.new("CameraPass") 2210 cam.SetDelegatePass(sequence) 2211 2212 self.renderer.SetPass(cam) 2213 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:
2215 def add_depth_of_field(self, autofocus=True) -> Self: 2216 """Add a depth of field effect in the scene.""" 2217 lights = vtki.new("LightsPass") 2218 2219 opaque = vtki.new("OpaquePass") 2220 2221 dofCam = vtki.new("CameraPass") 2222 dofCam.SetDelegatePass(opaque) 2223 2224 dof = vtki.new("DepthOfFieldPass") 2225 dof.SetAutomaticFocalDistance(autofocus) 2226 dof.SetDelegatePass(dofCam) 2227 2228 collection = vtki.new("RenderPassCollection") 2229 collection.AddItem(lights) 2230 collection.AddItem(dof) 2231 2232 sequence = vtki.new("SequencePass") 2233 sequence.SetPasses(collection) 2234 2235 cam = vtki.new("CameraPass") 2236 cam.SetDelegatePass(sequence) 2237 2238 self.renderer.SetPass(cam) 2239 return self
Add a depth of field effect in the scene.
2270 def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None) -> "vedo.addons.RendererFrame": 2271 """ 2272 Add a frame to the renderer subwindow. 2273 2274 Arguments: 2275 c : (color) 2276 color name or index 2277 alpha : (float) 2278 opacity level 2279 lw : (int) 2280 line width in pixels. 2281 padding : (float) 2282 padding space in pixels. 2283 """ 2284 if c is None: # automatic black or white 2285 c = (0.9, 0.9, 0.9) 2286 if self.renderer: 2287 if np.sum(self.renderer.GetBackground()) > 1.5: 2288 c = (0.1, 0.1, 0.1) 2289 renf = addons.RendererFrame(c, alpha, lw, padding) 2290 if renf: 2291 self.renderer.AddActor(renf) 2292 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.
2294 def add_hover_legend( 2295 self, 2296 at=None, 2297 c=None, 2298 pos="bottom-left", 2299 font="Calco", 2300 s=0.75, 2301 bg="auto", 2302 alpha=0.1, 2303 maxlength=24, 2304 use_info=False, 2305 ) -> int: 2306 """ 2307 Add a legend with 2D text which is triggered by hovering the mouse on an object. 2308 2309 The created text object are stored in `plotter.hover_legends`. 2310 2311 Returns: 2312 the id of the callback function. 2313 2314 Arguments: 2315 c : (color) 2316 Text color. If None then black or white is chosen automatically 2317 pos : (str) 2318 text positioning 2319 font : (str) 2320 text font type. Check [available fonts here](https://vedo.embl.es/fonts). 2321 s : (float) 2322 text size scale 2323 bg : (color) 2324 background color of the 2D box containing the text 2325 alpha : (float) 2326 box transparency 2327 maxlength : (int) 2328 maximum number of characters per line 2329 use_info : (bool) 2330 visualize the content of the `obj.info` attribute 2331 2332 Examples: 2333 - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py) 2334 - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py) 2335 2336 ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg) 2337 """ 2338 hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg) 2339 2340 if at is None: 2341 at = self.renderers.index(self.renderer) 2342 2343 def _legfunc(evt): 2344 if not evt.object or not self.renderer or at != evt.at: 2345 if hoverlegend.mapper.GetInput(): # clear and return 2346 hoverlegend.mapper.SetInput("") 2347 self.render() 2348 return 2349 2350 if use_info: 2351 if hasattr(evt.object, "info"): 2352 t = str(evt.object.info) 2353 else: 2354 return 2355 else: 2356 t, tp = "", "" 2357 if evt.isMesh: 2358 tp = "Mesh " 2359 elif evt.isPoints: 2360 tp = "Points " 2361 elif evt.isVolume: 2362 tp = "Volume " 2363 elif evt.isImage: 2364 tp = "Image " 2365 elif evt.isAssembly: 2366 tp = "Assembly " 2367 else: 2368 return 2369 2370 if evt.isAssembly: 2371 if not evt.object.name: 2372 t += f"Assembly object of {len(evt.object.unpack())} parts\n" 2373 else: 2374 t += f"Assembly name: {evt.object.name} ({len(evt.object.unpack())} parts)\n" 2375 else: 2376 if evt.object.name: 2377 t += f"{tp}name" 2378 if evt.isPoints: 2379 t += " " 2380 if evt.isMesh: 2381 t += " " 2382 t += f": {evt.object.name[:maxlength]}".ljust(maxlength) + "\n" 2383 2384 if evt.object.filename: 2385 t += f"{tp}filename: " 2386 t += f"{os.path.basename(evt.object.filename[-maxlength:])}".ljust(maxlength) 2387 t += "\n" 2388 if not evt.object.file_size: 2389 evt.object.file_size, evt.object.created = vedo.file_io.file_info(evt.object.filename) 2390 if evt.object.file_size: 2391 t += " : " 2392 sz, created = evt.object.file_size, evt.object.created 2393 t += f"{created[4:-5]} ({sz})" + "\n" 2394 2395 if evt.isPoints: 2396 indata = evt.object.dataset 2397 if indata.GetNumberOfPoints(): 2398 t += ( 2399 f"#points/cells: {indata.GetNumberOfPoints()}" 2400 f" / {indata.GetNumberOfCells()}" 2401 ) 2402 pdata = indata.GetPointData() 2403 cdata = indata.GetCellData() 2404 if pdata.GetScalars() and pdata.GetScalars().GetName(): 2405 t += f"\nPoint array : {pdata.GetScalars().GetName()}" 2406 if pdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2407 t += " *" 2408 if cdata.GetScalars() and cdata.GetScalars().GetName(): 2409 t += f"\nCell array : {cdata.GetScalars().GetName()}" 2410 if cdata.GetScalars().GetName() == evt.object.mapper.GetArrayName(): 2411 t += " *" 2412 2413 if evt.isImage: 2414 t = f"{os.path.basename(evt.object.filename[:maxlength+10])}".ljust(maxlength+10) 2415 t += f"\nImage shape: {evt.object.shape}" 2416 pcol = self.color_picker(evt.picked2d) 2417 t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}" 2418 2419 # change box color if needed in 'auto' mode 2420 if evt.isPoints and "auto" in str(bg): 2421 actcol = evt.object.properties.GetColor() 2422 if hoverlegend.mapper.GetTextProperty().GetBackgroundColor() != actcol: 2423 hoverlegend.mapper.GetTextProperty().SetBackgroundColor(actcol) 2424 2425 # adapt to changes in bg color 2426 bgcol = self.renderers[at].GetBackground() 2427 _bgcol = c 2428 if _bgcol is None: # automatic black or white 2429 _bgcol = (0.9, 0.9, 0.9) 2430 if sum(bgcol) > 1.5: 2431 _bgcol = (0.1, 0.1, 0.1) 2432 if len(set(_bgcol).intersection(bgcol)) < 3: 2433 hoverlegend.color(_bgcol) 2434 2435 if hoverlegend.mapper.GetInput() != t: 2436 hoverlegend.mapper.SetInput(t) 2437 self.interactor.Render() 2438 2439 # print("ABORT", idcall, hoverlegend.actor.GetCommand(idcall)) 2440 # hoverlegend.actor.GetCommand(idcall).AbortFlagOn() 2441 2442 self.add(hoverlegend, at=at) 2443 self.hover_legends.append(hoverlegend) 2444 idcall = self.add_callback("MouseMove", _legfunc) 2445 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:
2447 def add_scale_indicator( 2448 self, 2449 pos=(0.7, 0.05), 2450 s=0.02, 2451 length=2, 2452 lw=4, 2453 c="k1", 2454 alpha=1, 2455 units="", 2456 gap=0.05, 2457 ) -> Union["vedo.visual.Actor2D", None]: 2458 """ 2459 Add a Scale Indicator. Only works in parallel mode (no perspective). 2460 2461 Arguments: 2462 pos : (list) 2463 fractional (x,y) position on the screen. 2464 s : (float) 2465 size of the text. 2466 length : (float) 2467 length of the line. 2468 units : (str) 2469 string to show units. 2470 gap : (float) 2471 separation of line and text. 2472 2473 Example: 2474 ```python 2475 from vedo import settings, Cube, Plotter 2476 settings.use_parallel_projection = True # or else it does not make sense! 2477 cube = Cube().alpha(0.2) 2478 plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)')) 2479 plt.add_scale_indicator(units='um', c='blue4') 2480 plt.show(cube, "Scale indicator with units").close() 2481 ``` 2482 ![](https://vedo.embl.es/images/feats/scale_indicator.png) 2483 """ 2484 # Note that this cannot go in addons.py 2485 # because it needs callbacks and window size 2486 if not self.interactor: 2487 return None 2488 2489 ppoints = vtki.vtkPoints() # Generate the polyline 2490 psqr = [[0.0, gap], [length / 10, gap]] 2491 dd = psqr[1][0] - psqr[0][0] 2492 for i, pt in enumerate(psqr): 2493 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2494 lines = vtki.vtkCellArray() 2495 lines.InsertNextCell(len(psqr)) 2496 for i in range(len(psqr)): 2497 lines.InsertCellPoint(i) 2498 pd = vtki.vtkPolyData() 2499 pd.SetPoints(ppoints) 2500 pd.SetLines(lines) 2501 2502 wsx, wsy = self.window.GetSize() 2503 if not self.camera.GetParallelProjection(): 2504 vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.") 2505 return None 2506 2507 rlabel = vtki.new("VectorText") 2508 rlabel.SetText("scale") 2509 tf = vtki.new("TransformPolyDataFilter") 2510 tf.SetInputConnection(rlabel.GetOutputPort()) 2511 t = vtki.vtkTransform() 2512 t.Scale(s * wsy / wsx, s, 1) 2513 tf.SetTransform(t) 2514 2515 app = vtki.new("AppendPolyData") 2516 app.AddInputConnection(tf.GetOutputPort()) 2517 app.AddInputData(pd) 2518 2519 mapper = vtki.new("PolyDataMapper2D") 2520 mapper.SetInputConnection(app.GetOutputPort()) 2521 cs = vtki.vtkCoordinate() 2522 cs.SetCoordinateSystem(1) 2523 mapper.SetTransformCoordinate(cs) 2524 2525 fractor = vedo.visual.Actor2D() 2526 csys = fractor.GetPositionCoordinate() 2527 csys.SetCoordinateSystem(3) 2528 fractor.SetPosition(pos) 2529 fractor.SetMapper(mapper) 2530 fractor.GetProperty().SetColor(vedo.get_color(c)) 2531 fractor.GetProperty().SetOpacity(alpha) 2532 fractor.GetProperty().SetLineWidth(lw) 2533 fractor.GetProperty().SetDisplayLocationToForeground() 2534 2535 def sifunc(iren, ev): 2536 wsx, wsy = self.window.GetSize() 2537 ps = self.camera.GetParallelScale() 2538 newtxt = utils.precision(ps / wsy * wsx * length * dd, 3) 2539 if units: 2540 newtxt += " " + units 2541 if rlabel.GetText() != newtxt: 2542 rlabel.SetText(newtxt) 2543 2544 self.renderer.AddActor(fractor) 2545 self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc) 2546 self.interactor.AddObserver("MouseWheelForwardEvent", sifunc) 2547 self.interactor.AddObserver("InteractionEvent", sifunc) 2548 sifunc(0, 0) 2549 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()
2551 def fill_event(self, ename="", pos=(), enable_picking=True) -> "Event": 2552 """ 2553 Create an Event object with information of what was clicked. 2554 2555 If `enable_picking` is False, no picking will be performed. 2556 This can be useful to avoid double picking when using buttons. 2557 """ 2558 if not self.interactor: 2559 return Event() 2560 2561 if len(pos) > 0: 2562 x, y = pos 2563 self.interactor.SetEventPosition(pos) 2564 else: 2565 x, y = self.interactor.GetEventPosition() 2566 self.renderer = self.interactor.FindPokedRenderer(x, y) 2567 2568 self.picked2d = (x, y) 2569 2570 key = self.interactor.GetKeySym() 2571 2572 if key: 2573 if "_L" in key or "_R" in key: 2574 # skip things like Shift_R 2575 key = "" # better than None 2576 else: 2577 if self.interactor.GetShiftKey(): 2578 key = key.upper() 2579 2580 if key == "MINUS": # fix: vtk9 is ignoring shift chars.. 2581 key = "underscore" 2582 elif key == "EQUAL": # fix: vtk9 is ignoring shift chars.. 2583 key = "plus" 2584 elif key == "SLASH": # fix: vtk9 is ignoring shift chars.. 2585 key = "?" 2586 2587 if self.interactor.GetControlKey(): 2588 key = "Ctrl+" + key 2589 2590 if self.interactor.GetAltKey(): 2591 key = "Alt+" + key 2592 2593 if enable_picking: 2594 if not self.picker: 2595 self.picker = vtki.vtkPropPicker() 2596 2597 self.picker.PickProp(x, y, self.renderer) 2598 actor = self.picker.GetProp3D() 2599 # Note that GetProp3D already picks Assembly 2600 2601 xp, yp = self.interactor.GetLastEventPosition() 2602 dx, dy = x - xp, y - yp 2603 2604 delta3d = np.array([0, 0, 0]) 2605 2606 if actor: 2607 picked3d = np.array(self.picker.GetPickPosition()) 2608 2609 try: 2610 vobj = actor.retrieve_object() 2611 old_pt = np.asarray(vobj.picked3d) 2612 vobj.picked3d = picked3d 2613 delta3d = picked3d - old_pt 2614 except (AttributeError, TypeError): 2615 pass 2616 2617 else: 2618 picked3d = None 2619 2620 if not actor: # try 2D 2621 actor = self.picker.GetActor2D() 2622 2623 event = Event() 2624 event.name = ename 2625 event.title = self.title 2626 event.id = -1 # will be set by the timer wrapper function 2627 event.timerid = -1 # will be set by the timer wrapper function 2628 event.priority = -1 # will be set by the timer wrapper function 2629 event.time = time.time() 2630 event.at = self.renderers.index(self.renderer) 2631 event.keypress = key 2632 if enable_picking: 2633 try: 2634 event.object = actor.retrieve_object() 2635 except AttributeError: 2636 event.object = actor 2637 try: 2638 event.actor = actor.retrieve_object() # obsolete use object instead 2639 except AttributeError: 2640 event.actor = actor 2641 event.picked3d = picked3d 2642 event.picked2d = (x, y) 2643 event.delta2d = (dx, dy) 2644 event.angle2d = np.arctan2(dy, dx) 2645 event.speed2d = np.sqrt(dx * dx + dy * dy) 2646 event.delta3d = delta3d 2647 event.speed3d = np.sqrt(np.dot(delta3d, delta3d)) 2648 event.isPoints = isinstance(event.object, vedo.Points) 2649 event.isMesh = isinstance(event.object, vedo.Mesh) 2650 event.isAssembly = isinstance(event.object, vedo.Assembly) 2651 event.isVolume = isinstance(event.object, vedo.Volume) 2652 event.isImage = isinstance(event.object, vedo.Image) 2653 event.isActor2D = isinstance(event.object, vtki.vtkActor2D) 2654 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.
2656 def add_callback(self, event_name: str, func: Callable, priority=0.0, enable_picking=True) -> int: 2657 """ 2658 Add a function to be executed while show() is active. 2659 2660 Return a unique id for the callback. 2661 2662 The callback function (see example below) exposes a dictionary 2663 with the following information: 2664 - `name`: event name, 2665 - `id`: event unique identifier, 2666 - `priority`: event priority (float), 2667 - `interactor`: the interactor object, 2668 - `at`: renderer nr. where the event occurred 2669 - `keypress`: key pressed as string 2670 - `actor`: object picked by the mouse 2671 - `picked3d`: point picked in world coordinates 2672 - `picked2d`: screen coords of the mouse pointer 2673 - `delta2d`: shift wrt previous position (to calculate speed, direction) 2674 - `delta3d`: ...same but in 3D world coords 2675 - `angle2d`: angle of mouse movement on screen 2676 - `speed2d`: speed of mouse movement on screen 2677 - `speed3d`: speed of picked point in world coordinates 2678 - `isPoints`: True if of class 2679 - `isMesh`: True if of class 2680 - `isAssembly`: True if of class 2681 - `isVolume`: True if of class Volume 2682 - `isImage`: True if of class 2683 2684 If `enable_picking` is False, no picking will be performed. 2685 This can be useful to avoid double picking when using buttons. 2686 2687 Frequently used events are: 2688 - `KeyPress`, `KeyRelease`: listen to keyboard events 2689 - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks 2690 - `MiddleButtonPress`, `MiddleButtonRelease` 2691 - `RightButtonPress`, `RightButtonRelease` 2692 - `MouseMove`: listen to mouse pointer changing position 2693 - `MouseWheelForward`, `MouseWheelBackward` 2694 - `Enter`, `Leave`: listen to mouse entering or leaving the window 2695 - `Pick`, `StartPick`, `EndPick`: listen to object picking 2696 - `ResetCamera`, `ResetCameraClippingRange` 2697 - `Error`, `Warning` 2698 - `Char` 2699 - `Timer` 2700 2701 Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html). 2702 2703 Example: 2704 ```python 2705 from vedo import * 2706 2707 def func(evt): 2708 # this function is called every time the mouse moves 2709 # (evt is a dotted dictionary) 2710 if not evt.object: 2711 return # no hit, return 2712 print("point coords =", evt.picked3d) 2713 # print(evt) # full event dump 2714 2715 elli = Ellipsoid() 2716 plt = Plotter(axes=1) 2717 plt.add_callback('mouse hovering', func) 2718 plt.show(elli).close() 2719 ``` 2720 2721 Examples: 2722 - [spline_draw1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw1.py) 2723 - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py) 2724 2725 ![](https://vedo.embl.es/images/advanced/spline_draw.png) 2726 2727 - ..and many others! 2728 """ 2729 from vtkmodules.util.misc import calldata_type 2730 2731 if not self.interactor: 2732 return 0 2733 2734 if vedo.settings.dry_run_mode >= 1: 2735 return 0 2736 2737 ######################################### 2738 @calldata_type(vtki.VTK_INT) 2739 def _func_wrap(iren, ename, timerid=None): 2740 event = self.fill_event(ename=ename, enable_picking=enable_picking) 2741 event.timerid = timerid 2742 event.id = cid 2743 event.priority = priority 2744 self.last_event = event 2745 func(event) 2746 2747 ######################################### 2748 2749 event_name = utils.get_vtk_name_event(event_name) 2750 2751 cid = self.interactor.AddObserver(event_name, _func_wrap, priority) 2752 # print(f"Registering event: {event_name} with id={cid}") 2753 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!
2755 def remove_callback(self, cid: Union[int, str]) -> Self: 2756 """ 2757 Remove a callback function by its id 2758 or a whole category of callbacks by their name. 2759 2760 Arguments: 2761 cid : (int, str) 2762 Unique id of the callback. 2763 If an event name is passed all callbacks of that type are removed. 2764 """ 2765 if self.interactor: 2766 if isinstance(cid, str): 2767 cid = utils.get_vtk_name_event(cid) 2768 self.interactor.RemoveObservers(cid) 2769 else: 2770 self.interactor.RemoveObserver(cid) 2771 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.
2773 def remove_all_observers(self) -> Self: 2774 """ 2775 Remove all observers. 2776 2777 Example: 2778 ```python 2779 from vedo import * 2780 2781 def kfunc(event): 2782 print("Key pressed:", event.keypress) 2783 if event.keypress == 'q': 2784 plt.close() 2785 2786 def rfunc(event): 2787 if event.isImage: 2788 printc("Right-clicked!", event) 2789 plt.render() 2790 2791 img = Image(dataurl+"images/embryo.jpg") 2792 2793 plt = Plotter(size=(1050, 600)) 2794 plt.parallel_projection(True) 2795 plt.remove_all_observers() 2796 plt.add_callback("key press", kfunc) 2797 plt.add_callback("mouse right click", rfunc) 2798 plt.show("Right-Click Me! Press q to exit.", img) 2799 plt.close() 2800 ``` 2801 """ 2802 if self.interactor: 2803 self.interactor.RemoveAllObservers() 2804 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()
2806 def timer_callback(self, action: str, timer_id=None, dt=1, one_shot=False) -> int: 2807 """ 2808 Start or stop an existing timer. 2809 2810 Arguments: 2811 action : (str) 2812 Either "create"/"start" or "destroy"/"stop" 2813 timer_id : (int) 2814 When stopping the timer, the ID of the timer as returned when created 2815 dt : (int) 2816 time in milliseconds between each repeated call 2817 one_shot : (bool) 2818 create a one shot timer of prescribed duration instead of a repeating one 2819 2820 Examples: 2821 - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py) 2822 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 2823 2824 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 2825 """ 2826 if action in ("create", "start"): 2827 if timer_id is not None: 2828 vedo.logger.warning("you set a timer_id but it will be ignored.") 2829 if one_shot: 2830 timer_id = self.interactor.CreateOneShotTimer(dt) 2831 else: 2832 timer_id = self.interactor.CreateRepeatingTimer(dt) 2833 return timer_id 2834 2835 elif action in ("destroy", "stop"): 2836 if timer_id is not None: 2837 self.interactor.DestroyTimer(timer_id) 2838 else: 2839 vedo.logger.warning("please set a timer_id. Cannot stop timer.") 2840 else: 2841 e = f"in timer_callback(). Cannot understand action: {action}\n" 2842 e += " allowed actions are: ['start', 'stop']. Skipped." 2843 vedo.logger.error(e) 2844 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:
2846 def add_observer(self, event_name: str, func: Callable, priority=0.0) -> int: 2847 """ 2848 Add a callback function that will be called when an event occurs. 2849 Consider using `add_callback()` instead. 2850 """ 2851 if not self.interactor: 2852 return -1 2853 event_name = utils.get_vtk_name_event(event_name) 2854 idd = self.interactor.AddObserver(event_name, func, priority) 2855 return idd
Add a callback function that will be called when an event occurs.
Consider using add_callback()
instead.
2857 def compute_world_coordinate( 2858 self, 2859 pos2d: MutableSequence[float], 2860 at=None, 2861 objs=(), 2862 bounds=(), 2863 offset=None, 2864 pixeltol=None, 2865 worldtol=None, 2866 ) -> np.ndarray: 2867 """ 2868 Transform a 2D point on the screen into a 3D point inside the rendering scene. 2869 If a set of meshes is passed then points are placed onto these. 2870 2871 Arguments: 2872 pos2d : (list) 2873 2D screen coordinates point. 2874 at : (int) 2875 renderer number. 2876 objs : (list) 2877 list of Mesh objects to project the point onto. 2878 bounds : (list) 2879 specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax]. 2880 offset : (float) 2881 specify an offset value. 2882 pixeltol : (int) 2883 screen tolerance in pixels. 2884 worldtol : (float) 2885 world coordinates tolerance. 2886 2887 Returns: 2888 numpy array, the point in 3D world coordinates. 2889 2890 Examples: 2891 - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py) 2892 - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py) 2893 2894 ![](https://vedo.embl.es/images/basic/mousehover3.jpg) 2895 """ 2896 if at is not None: 2897 renderer = self.renderers[at] 2898 else: 2899 renderer = self.renderer 2900 2901 if not objs: 2902 pp = vtki.vtkFocalPlanePointPlacer() 2903 else: 2904 pps = vtki.vtkPolygonalSurfacePointPlacer() 2905 for ob in objs: 2906 pps.AddProp(ob.actor) 2907 pp = pps # type: ignore 2908 2909 if len(bounds) == 6: 2910 pp.SetPointBounds(bounds) 2911 if pixeltol: 2912 pp.SetPixelTolerance(pixeltol) 2913 if worldtol: 2914 pp.SetWorldTolerance(worldtol) 2915 if offset: 2916 pp.SetOffset(offset) 2917 2918 worldPos: MutableSequence[float] = [0, 0, 0] 2919 worldOrient: MutableSequence[float] = [0, 0, 0, 0, 0, 0, 0, 0, 0] 2920 pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient) 2921 # validw = pp.ValidateWorldPosition(worldPos, worldOrient) 2922 # validd = pp.ValidateDisplayPosition(renderer, pos2d) 2923 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:
2925 def compute_screen_coordinates(self, obj, full_window=False) -> np.ndarray: 2926 """ 2927 Given a 3D points in the current renderer (or full window), 2928 find the screen pixel coordinates. 2929 2930 Example: 2931 ```python 2932 from vedo import * 2933 2934 elli = Ellipsoid().point_size(5) 2935 2936 plt = Plotter() 2937 plt.show(elli, "Press q to continue and print the info") 2938 2939 xyscreen = plt.compute_screen_coordinates(elli) 2940 print('xyscreen coords:', xyscreen) 2941 2942 # simulate an event happening at one point 2943 event = plt.fill_event(pos=xyscreen[123]) 2944 print(event) 2945 ``` 2946 """ 2947 try: 2948 obj = obj.vertices 2949 except AttributeError: 2950 pass 2951 2952 if utils.is_sequence(obj): 2953 pts = obj 2954 p2d = [] 2955 cs = vtki.vtkCoordinate() 2956 cs.SetCoordinateSystemToWorld() 2957 cs.SetViewport(self.renderer) 2958 for p in pts: 2959 cs.SetValue(p) 2960 if full_window: 2961 p2d.append(cs.GetComputedDisplayValue(self.renderer)) 2962 else: 2963 p2d.append(cs.GetComputedViewportValue(self.renderer)) 2964 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)
2966 def pick_area(self, pos1, pos2, at=None) -> "vedo.Mesh": 2967 """ 2968 Pick all objects within a box defined by two corner points in 2D screen coordinates. 2969 2970 Returns a frustum Mesh that contains the visible field of view. 2971 This can be used to select objects in a scene or select vertices. 2972 2973 Example: 2974 ```python 2975 from vedo import * 2976 2977 settings.enable_default_mouse_callbacks = False 2978 2979 def mode_select(objs): 2980 print("Selected objects:", objs) 2981 d0 = mode.start_x, mode.start_y # display coords 2982 d1 = mode.end_x, mode.end_y 2983 2984 frustum = plt.pick_area(d0, d1) 2985 col = np.random.randint(0, 10) 2986 infru = frustum.inside_points(mesh) 2987 infru.point_size(10).color(col) 2988 plt.add(frustum, infru).render() 2989 2990 mesh = Mesh(dataurl+"cow.vtk") 2991 mesh.color("k5").linewidth(1) 2992 2993 mode = interactor_modes.BlenderStyle() 2994 mode.callback_select = mode_select 2995 2996 plt = Plotter().user_mode(mode) 2997 plt.show(mesh, axes=1) 2998 ``` 2999 """ 3000 if at is not None: 3001 ren = self.renderers[at] 3002 else: 3003 ren = self.renderer 3004 area_picker = vtki.vtkAreaPicker() 3005 area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren) 3006 planes = area_picker.GetFrustum() 3007 3008 fru = vtki.new("FrustumSource") 3009 fru.SetPlanes(planes) 3010 fru.ShowLinesOff() 3011 fru.Update() 3012 3013 afru = vedo.Mesh(fru.GetOutput()) 3014 afru.alpha(0.1).lw(1).pickable(False) 3015 afru.name = "Frustum" 3016 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)
3139 def show( 3140 self, 3141 *objects, 3142 at=None, 3143 axes=None, 3144 resetcam=None, 3145 zoom=False, 3146 interactive=None, 3147 viewup="", 3148 azimuth=0.0, 3149 elevation=0.0, 3150 roll=0.0, 3151 camera=None, 3152 mode=None, 3153 rate=None, 3154 bg=None, 3155 bg2=None, 3156 size=None, 3157 title=None, 3158 screenshot="", 3159 ) -> Any: 3160 """ 3161 Render a list of objects. 3162 3163 Arguments: 3164 at : (int) 3165 number of the renderer to plot to, in case of more than one exists 3166 3167 axes : (int) 3168 axis type-1 can be fully customized by passing a dictionary. 3169 Check `addons.Axes()` for the full list of options. 3170 set the type of axes to be shown: 3171 - 0, no axes 3172 - 1, draw three gray grid walls 3173 - 2, show cartesian axes from (0,0,0) 3174 - 3, show positive range of cartesian axes from (0,0,0) 3175 - 4, show a triad at bottom left 3176 - 5, show a cube at bottom left 3177 - 6, mark the corners of the bounding box 3178 - 7, draw a 3D ruler at each side of the cartesian axes 3179 - 8, show the `vtkCubeAxesActor` object 3180 - 9, show the bounding box outLine 3181 - 10, show three circles representing the maximum bounding box 3182 - 11, show a large grid on the x-y plane 3183 - 12, show polar axes 3184 - 13, draw a simple ruler at the bottom of the window 3185 3186 azimuth/elevation/roll : (float) 3187 move camera accordingly the specified value 3188 3189 viewup: str, list 3190 either `['x', 'y', 'z']` or a vector to set vertical direction 3191 3192 resetcam : (bool) 3193 re-adjust camera position to fit objects 3194 3195 camera : (dict, vtkCamera) 3196 camera parameters can further be specified with a dictionary 3197 assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`): 3198 - pos, `(list)`, the position of the camera in world coordinates 3199 - focal_point `(list)`, the focal point of the camera in world coordinates 3200 - viewup `(list)`, the view up direction for the camera 3201 - distance `(float)`, set the focal point to the specified distance from the camera position. 3202 - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection. 3203 - parallel_scale `(float)`, scaling used for a parallel projection, i.e. the height of the viewport 3204 in world-coordinate distances. The default is 1. 3205 Note that the "scale" parameter works as an "inverse scale", larger numbers produce smaller images. 3206 This method has no effect in perspective projection mode. 3207 3208 - thickness `(float)`, set the distance between clipping planes. This method adjusts the far clipping 3209 plane to be set a distance 'thickness' beyond the near clipping plane. 3210 3211 - view_angle `(float)`, the camera view angle, which is the angular height of the camera view 3212 measured in degrees. The default angle is 30 degrees. 3213 This method has no effect in parallel projection mode. 3214 The formula for setting the angle up for perfect perspective viewing is: 3215 angle = 2*atan((h/2)/d) where h is the height of the RenderWindow 3216 (measured by holding a ruler up to your screen) and d is the distance from your eyes to the screen. 3217 3218 interactive : (bool) 3219 pause and interact with window (True) or continue execution (False) 3220 3221 rate : (float) 3222 maximum rate of `show()` in Hertz 3223 3224 mode : (int, str) 3225 set the type of interaction: 3226 - 0 = TrackballCamera [default] 3227 - 1 = TrackballActor 3228 - 2 = JoystickCamera 3229 - 3 = JoystickActor 3230 - 4 = Flight 3231 - 5 = RubberBand2D 3232 - 6 = RubberBand3D 3233 - 7 = RubberBandZoom 3234 - 8 = Terrain 3235 - 9 = Unicam 3236 - 10 = Image 3237 - Check out `vedo.interaction_modes` for more options. 3238 3239 bg : (str, list) 3240 background color in RGB format, or string name 3241 3242 bg2 : (str, list) 3243 second background color to create a gradient background 3244 3245 size : (str, list) 3246 size of the window, e.g. size="fullscreen", or size=[600,400] 3247 3248 title : (str) 3249 window title text 3250 3251 screenshot : (str) 3252 save a screenshot of the window to file 3253 """ 3254 3255 if vedo.settings.dry_run_mode >= 2: 3256 return self 3257 3258 if self.wx_widget: 3259 return self 3260 3261 if self.renderers: # in case of notebooks 3262 3263 if at is None: 3264 at = self.renderers.index(self.renderer) 3265 3266 else: 3267 3268 if at >= len(self.renderers): 3269 t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist" 3270 vedo.logger.error(t) 3271 return self 3272 3273 self.renderer = self.renderers[at] 3274 3275 if title is not None: 3276 self.title = title 3277 3278 if size is not None: 3279 self.size = size 3280 if self.size[0] == "f": # full screen 3281 self.size = "fullscreen" 3282 self.window.SetFullScreen(True) 3283 self.window.BordersOn() 3284 else: 3285 self.window.SetSize(int(self.size[0]), int(self.size[1])) 3286 3287 if vedo.settings.default_backend == "vtk": 3288 if str(bg).endswith(".hdr"): 3289 self._add_skybox(bg) 3290 else: 3291 if bg is not None: 3292 self.backgrcol = vedo.get_color(bg) 3293 self.renderer.SetBackground(self.backgrcol) 3294 if bg2 is not None: 3295 self.renderer.GradientBackgroundOn() 3296 self.renderer.SetBackground2(vedo.get_color(bg2)) 3297 3298 if axes is not None: 3299 if isinstance(axes, vedo.Assembly): # user passing show(..., axes=myaxes) 3300 objects = list(objects) 3301 objects.append(axes) # move it into the list of normal things to show 3302 axes = 0 3303 self.axes = axes 3304 3305 if interactive is not None: 3306 self._interactive = interactive 3307 if self.offscreen: 3308 self._interactive = False 3309 3310 # camera stuff 3311 if resetcam is not None: 3312 self.resetcam = resetcam 3313 3314 if camera is not None: 3315 self.resetcam = False 3316 viewup = "" 3317 if isinstance(camera, vtki.vtkCamera): 3318 cameracopy = vtki.vtkCamera() 3319 cameracopy.DeepCopy(camera) 3320 self.camera = cameracopy 3321 else: 3322 self.camera = utils.camera_from_dict(camera) 3323 3324 self.add(objects) 3325 3326 # Backend ############################################################### 3327 if vedo.settings.default_backend in ["k3d"]: 3328 return backends.get_notebook_backend(self.objects) 3329 ######################################################################### 3330 3331 for ia in utils.flatten(objects): 3332 try: 3333 # fix gray color labels and title to white or black 3334 ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor()) 3335 if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05: 3336 c = (0.9, 0.9, 0.9) 3337 if np.sum(self.renderer.GetBackground()) > 1.5: 3338 c = (0.1, 0.1, 0.1) 3339 ia.scalarbar.GetLabelTextProperty().SetColor(c) 3340 ia.scalarbar.GetTitleTextProperty().SetColor(c) 3341 except AttributeError: 3342 pass 3343 3344 if self.sharecam: 3345 for r in self.renderers: 3346 r.SetActiveCamera(self.camera) 3347 3348 if self.axes is not None: 3349 if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict): 3350 bns = self.renderer.ComputeVisiblePropBounds() 3351 addons.add_global_axes(self.axes, bounds=bns) 3352 3353 # Backend ############################################################### 3354 if vedo.settings.default_backend in ["ipyvtk", "trame"]: 3355 return backends.get_notebook_backend() 3356 ######################################################################### 3357 3358 if self.resetcam: 3359 self.renderer.ResetCamera() 3360 3361 if len(self.renderers) > 1: 3362 self.add_renderer_frame() 3363 3364 if vedo.settings.default_backend == "2d" and not zoom: 3365 zoom = "tightest" 3366 3367 if zoom: 3368 if zoom == "tight": 3369 self.reset_camera(tight=0.04) 3370 elif zoom == "tightest": 3371 self.reset_camera(tight=0.0001) 3372 else: 3373 self.camera.Zoom(zoom) 3374 if elevation: 3375 self.camera.Elevation(elevation) 3376 if azimuth: 3377 self.camera.Azimuth(azimuth) 3378 if roll: 3379 self.camera.Roll(roll) 3380 3381 if len(viewup) > 0: 3382 b = self.renderer.ComputeVisiblePropBounds() 3383 cm = np.array([(b[1] + b[0])/2, (b[3] + b[2])/2, (b[5] + b[4])/2]) 3384 sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]) 3385 if viewup == "x": 3386 sz = np.linalg.norm(sz) 3387 self.camera.SetViewUp([1, 0, 0]) 3388 self.camera.SetPosition(cm + sz) 3389 elif viewup == "y": 3390 sz = np.linalg.norm(sz) 3391 self.camera.SetViewUp([0, 1, 0]) 3392 self.camera.SetPosition(cm + sz) 3393 elif viewup == "z": 3394 sz = np.array([(b[1]-b[0])*0.7, -(b[3]-b[2])*1.0, (b[5]-b[4])*1.2]) 3395 self.camera.SetViewUp([0, 0, 1]) 3396 self.camera.SetPosition(cm + 2 * sz) 3397 elif utils.is_sequence(viewup): 3398 sz = np.linalg.norm(sz) 3399 self.camera.SetViewUp(viewup) 3400 cpos = np.cross([0, 1, 0], viewup) 3401 self.camera.SetPosition(cm - 2 * sz * cpos) 3402 3403 self.renderer.ResetCameraClippingRange() 3404 3405 self.initialize_interactor() 3406 3407 if vedo.settings.immediate_rendering: 3408 self.window.Render() ##################### <-------------- Render 3409 3410 if self.interactor: # can be offscreen or not the vtk backend.. 3411 3412 self.window.SetWindowName(self.title) 3413 3414 # pic = vedo.Image(vedo.dataurl+'images/vtk_logo.png') 3415 # pic = vedo.Image('/home/musy/Downloads/icons8-3d-96.png') 3416 # print(pic.dataset)# Array 0 name PNGImage 3417 # self.window.SetIcon(pic.dataset) 3418 3419 try: 3420 # Needs "pip install pyobjc" on Mac OSX 3421 if ( 3422 self._cocoa_initialized is False 3423 and "Darwin" in vedo.sys_platform 3424 and not self.offscreen 3425 ): 3426 self._cocoa_initialized = True 3427 from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps # type: ignore 3428 pid = os.getpid() 3429 x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid)) 3430 x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) 3431 except: 3432 # vedo.logger.debug("On Mac OSX try: pip install pyobjc") 3433 pass 3434 3435 # Set the interaction style 3436 if mode is not None: 3437 self.user_mode(mode) 3438 if self.qt_widget and mode is None: 3439 self.user_mode(0) 3440 3441 if screenshot: 3442 self.screenshot(screenshot) 3443 3444 if self._interactive: 3445 self.interactor.Start() 3446 if self._must_close_now: 3447 self.interactor.GetRenderWindow().Finalize() 3448 self.interactor.TerminateApp() 3449 self.camera = None 3450 self.renderer = None 3451 self.renderers = [] 3452 self.window = None 3453 self.interactor = None 3454 return self 3455 3456 if rate: 3457 if self.clock is None: # set clock and limit rate 3458 self._clockt0 = time.time() 3459 self.clock = 0.0 3460 else: 3461 t = time.time() - self._clockt0 3462 elapsed = t - self.clock 3463 mint = 1.0 / rate 3464 if elapsed < mint: 3465 time.sleep(mint - elapsed) 3466 self.clock = time.time() - self._clockt0 3467 3468 # 2d #################################################################### 3469 if vedo.settings.default_backend == "2d": 3470 return backends.get_notebook_backend() 3471 ######################################################################### 3472 3473 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
3476 def add_inset(self, *objects, **options) -> Union[vtki.vtkOrientationMarkerWidget, None]: 3477 """Add a draggable inset space into a renderer. 3478 3479 Arguments: 3480 at : (int) 3481 specify the renderer number 3482 pos : (list) 3483 icon position in the range [1-4] indicating one of the 4 corners, 3484 or it can be a tuple (x,y) as a fraction of the renderer size. 3485 size : (float) 3486 size of the square inset 3487 draggable : (bool) 3488 if True the subrenderer space can be dragged around 3489 c : (color) 3490 color of the inset frame when dragged 3491 3492 Examples: 3493 - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py) 3494 3495 ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg) 3496 """ 3497 if not self.interactor: 3498 return None 3499 3500 if not self.renderer: 3501 vedo.logger.warning("call add_inset() only after first rendering of the scene.") 3502 return None 3503 3504 options = dict(options) 3505 pos = options.pop("pos", 0) 3506 size = options.pop("size", 0.1) 3507 c = options.pop("c", "lb") 3508 at = options.pop("at", None) 3509 draggable = options.pop("draggable", True) 3510 3511 r, g, b = vedo.get_color(c) 3512 widget = vtki.vtkOrientationMarkerWidget() 3513 widget.SetOutlineColor(r, g, b) 3514 if len(objects) == 1: 3515 widget.SetOrientationMarker(objects[0].actor) 3516 else: 3517 widget.SetOrientationMarker(vedo.Assembly(objects)) 3518 3519 widget.SetInteractor(self.interactor) 3520 3521 if utils.is_sequence(pos): 3522 widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 3523 else: 3524 if pos < 2: 3525 widget.SetViewport(0, 1 - 2 * size, size * 2, 1) 3526 elif pos == 2: 3527 widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 3528 elif pos == 3: 3529 widget.SetViewport(0, 0, size * 2, size * 2) 3530 elif pos == 4: 3531 widget.SetViewport(1 - 2 * size, 0, 1, size * 2) 3532 widget.EnabledOn() 3533 widget.SetInteractive(draggable) 3534 if at is not None and at < len(self.renderers): 3535 widget.SetCurrentRenderer(self.renderers[at]) 3536 else: 3537 widget.SetCurrentRenderer(self.renderer) 3538 self.widgets.append(widget) 3539 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:
3541 def clear(self, at=None, deep=False) -> Self: 3542 """Clear the scene from all meshes and volumes.""" 3543 if at is not None: 3544 renderer = self.renderers[at] 3545 else: 3546 renderer = self.renderer 3547 if not renderer: 3548 return self 3549 3550 if deep: 3551 renderer.RemoveAllViewProps() 3552 else: 3553 for ob in set( 3554 self.get_meshes() 3555 + self.get_volumes() 3556 + self.objects 3557 + self.axes_instances 3558 ): 3559 if isinstance(ob, vedo.shapes.Text2D): 3560 continue 3561 self.remove(ob) 3562 try: 3563 if ob.scalarbar: 3564 self.remove(ob.scalarbar) 3565 except AttributeError: 3566 pass 3567 return self
Clear the scene from all meshes and volumes.
3569 def break_interaction(self) -> Self: 3570 """Break window interaction and return to the python execution flow""" 3571 if self.interactor: 3572 self.check_actors_trasform() 3573 self.interactor.ExitCallback() 3574 return self
Break window interaction and return to the python execution flow
3576 def freeze(self, value=True) -> Self: 3577 """Freeze the current renderer. Use this with `sharecam=False`.""" 3578 if not self.interactor: 3579 return self 3580 if not self.renderer: 3581 return self 3582 self.renderer.SetInteractive(not value) 3583 return self
Freeze the current renderer. Use this with sharecam=False
.
3585 def user_mode(self, mode) -> Self: 3586 """ 3587 Modify the user interaction mode. 3588 3589 Examples: 3590 ```python 3591 from vedo import * 3592 mode = interactor_modes.MousePan() 3593 mesh = Mesh(dataurl+"cow.vtk") 3594 plt = Plotter().user_mode(mode) 3595 plt.show(mesh, axes=1) 3596 ``` 3597 See also: 3598 [VTK interactor styles](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html) 3599 """ 3600 if not self.interactor: 3601 return self 3602 3603 curr_style = self.interactor.GetInteractorStyle().GetClassName() 3604 # print("Current style:", curr_style) 3605 if curr_style.endswith("Actor"): 3606 self.check_actors_trasform() 3607 3608 if isinstance(mode, (str, int)): 3609 # Set the style of interaction 3610 # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html 3611 if mode in (0, "TrackballCamera"): 3612 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballCamera")) 3613 self.interactor.RemoveObservers("CharEvent") 3614 elif mode in (1, "TrackballActor"): 3615 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTrackballActor")) 3616 elif mode in (2, "JoystickCamera"): 3617 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickCamera")) 3618 elif mode in (3, "JoystickActor"): 3619 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleJoystickActor")) 3620 elif mode in (4, "Flight"): 3621 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleFlight")) 3622 elif mode in (5, "RubberBand2D"): 3623 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand2D")) 3624 elif mode in (6, "RubberBand3D"): 3625 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBand3D")) 3626 elif mode in (7, "RubberBandZoom"): 3627 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleRubberBandZoom")) 3628 elif mode in (8, "Terrain"): 3629 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleTerrain")) 3630 elif mode in (9, "Unicam"): 3631 self.interactor.SetInteractorStyle(vtki.new("InteractorStyleUnicam")) 3632 elif mode in (10, "Image", "image", "2d"): 3633 astyle = vtki.new("InteractorStyleImage") 3634 astyle.SetInteractionModeToImage3D() 3635 self.interactor.SetInteractorStyle(astyle) 3636 else: 3637 vedo.logger.warning(f"Unknown interaction mode: {mode}") 3638 3639 elif isinstance(mode, vtki.vtkInteractorStyleUser): 3640 # set a custom interactor style 3641 if hasattr(mode, "interactor"): 3642 mode.interactor = self.interactor 3643 mode.renderer = self.renderer # type: ignore 3644 mode.SetInteractor(self.interactor) 3645 mode.SetDefaultRenderer(self.renderer) 3646 self.interactor.SetInteractorStyle(mode) 3647 3648 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
3650 def close(self) -> Self: 3651 """Close the plotter.""" 3652 # https://examples.vtk.org/site/Cxx/Visualization/CloseWindow/ 3653 vedo.last_figure = None 3654 self.last_event = None 3655 self.sliders = [] 3656 self.buttons = [] 3657 self.widgets = [] 3658 self.hover_legends = [] 3659 self.background_renderer = None 3660 self._extralight = None 3661 3662 self.hint_widget = None 3663 self.cutter_widget = None 3664 3665 if vedo.settings.dry_run_mode >= 2: 3666 return self 3667 3668 if not hasattr(self, "window"): 3669 return self 3670 if not self.window: 3671 return self 3672 if not hasattr(self, "interactor"): 3673 return self 3674 if not self.interactor: 3675 return self 3676 3677 ################################################### 3678 try: 3679 if "Darwin" in vedo.sys_platform: 3680 self.interactor.ProcessEvents() 3681 except: 3682 pass 3683 3684 self._must_close_now = True 3685 3686 if vedo.plotter_instance == self: 3687 vedo.plotter_instance = None 3688 3689 if self.interactor and self._interactive: 3690 self.break_interaction() 3691 elif self._must_close_now: 3692 # dont call ExitCallback here 3693 if self.interactor: 3694 self.break_interaction() 3695 self.interactor.GetRenderWindow().Finalize() 3696 self.interactor.TerminateApp() 3697 self.camera = None 3698 self.renderer = None 3699 self.renderers = [] 3700 self.window = None 3701 self.interactor = None 3702 return self
Close the plotter.
3704 @property 3705 def camera(self): 3706 """Return the current active camera.""" 3707 if self.renderer: 3708 return self.renderer.GetActiveCamera()
Return the current active camera.
3717 def screenshot(self, filename="screenshot.png", scale=1, asarray=False) -> Any: 3718 """ 3719 Take a screenshot of the Plotter window. 3720 3721 Arguments: 3722 scale : (int) 3723 set image magnification as an integer multiplicating factor 3724 asarray : (bool) 3725 return a numpy array of the image instead of writing a file 3726 3727 Warning: 3728 If you get black screenshots try to set `interactive=False` in `show()` 3729 then call `screenshot()` and `plt.interactive()` afterwards. 3730 3731 Example: 3732 ```py 3733 from vedo import * 3734 sphere = Sphere().linewidth(1) 3735 plt = show(sphere, interactive=False) 3736 plt.screenshot('image.png') 3737 plt.interactive() 3738 plt.close() 3739 ``` 3740 3741 Example: 3742 ```py 3743 from vedo import * 3744 sphere = Sphere().linewidth(1) 3745 plt = show(sphere, interactive=False) 3746 plt.screenshot('anotherimage.png') 3747 plt.interactive() 3748 plt.close() 3749 ``` 3750 """ 3751 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()
3753 def toimage(self, scale=1) -> "vedo.image.Image": 3754 """ 3755 Generate a `Image` object from the current rendering window. 3756 3757 Arguments: 3758 scale : (int) 3759 set image magnification as an integer multiplicating factor 3760 """ 3761 if vedo.settings.screeshot_large_image: 3762 w2if = vtki.new("RenderLargeImage") 3763 w2if.SetInput(self.renderer) 3764 w2if.SetMagnification(scale) 3765 else: 3766 w2if = vtki.new("WindowToImageFilter") 3767 w2if.SetInput(self.window) 3768 if hasattr(w2if, "SetScale"): 3769 w2if.SetScale(scale, scale) 3770 if vedo.settings.screenshot_transparent_background: 3771 w2if.SetInputBufferTypeToRGBA() 3772 w2if.ReadFrontBufferOff() # read from the back buffer 3773 w2if.Update() 3774 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
3776 def export(self, filename="scene.npz", binary=False) -> Self: 3777 """ 3778 Export scene to file to HTML, X3D or Numpy file. 3779 3780 Examples: 3781 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 3782 - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py) 3783 """ 3784 vedo.file_io.export_window(filename, binary=binary) 3785 return self
3787 def color_picker(self, xy, verbose=False): 3788 """Pick color of specific (x,y) pixel on the screen.""" 3789 w2if = vtki.new("WindowToImageFilter") 3790 w2if.SetInput(self.window) 3791 w2if.ReadFrontBufferOff() 3792 w2if.Update() 3793 nx, ny = self.window.GetSize() 3794 varr = w2if.GetOutput().GetPointData().GetScalars() 3795 3796 arr = utils.vtk2numpy(varr).reshape(ny, nx, 3) 3797 x, y = int(xy[0]), int(xy[1]) 3798 if y < ny and x < nx: 3799 3800 rgb = arr[y, x] 3801 3802 if verbose: 3803 vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="") 3804 vedo.printc("█", c=[rgb[0], 0, 0], end="") 3805 vedo.printc("█", c=[0, rgb[1], 0], end="") 3806 vedo.printc("█", c=[0, 0, rgb[2]], end="") 3807 vedo.printc("] = ", end="") 3808 cnm = vedo.get_color_name(rgb) 3809 if np.sum(rgb) < 150: 3810 vedo.printc( 3811 rgb.tolist(), 3812 vedo.colors.rgb2hex(np.array(rgb) / 255), 3813 c="w", 3814 bc=rgb, 3815 invert=1, 3816 end="", 3817 ) 3818 vedo.printc(" -> " + cnm, invert=1, c="w") 3819 else: 3820 vedo.printc( 3821 rgb.tolist(), 3822 vedo.colors.rgb2hex(np.array(rgb) / 255), 3823 c=rgb, 3824 end="", 3825 ) 3826 vedo.printc(" -> " + cnm, c=cnm) 3827 3828 return rgb 3829 3830 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.