vedo.plotter

This module defines the main class Plotter to manage actors and 3D rendering.

   1#!/usr/bin/env python3
   2# -*- coding: utf-8 -*-
   3import os.path
   4import sys
   5import time
   6from typing import Callable
   7import numpy as np
   8
   9try:
  10    import vedo.vtkclasses as vtk
  11except ImportError:
  12    import vtkmodules.all as vtk
  13
  14import vedo
  15from vedo import settings
  16from vedo import utils
  17from vedo import backends
  18from vedo import addons
  19
  20
  21__docformat__ = "google"
  22
  23__doc__ = """
  24This module defines the main class Plotter to manage actors and 3D rendering.
  25
  26![](https://vedo.embl.es/images/basic/multirenderers.png)
  27"""
  28
  29__all__ = ["Plotter", "show", "close"]
  30
  31########################################################################################
  32class Event:
  33    """
  34    This class holds the info from an event in the window, works as dictionary too
  35    """
  36    __slots__ = [
  37        "name",
  38        "title",
  39        "id",
  40        "timerid",
  41        "time",
  42        "priority",
  43        "at",
  44        "actor",
  45        "picked3d",
  46        "keyPressed",  # obsolete, will disappear. Use "keypress"
  47        "keypress",
  48        "picked2d",
  49        "delta2d",
  50        "angle2d",
  51        "speed2d",
  52        "delta3d",
  53        "speed3d",
  54        "isPoints",
  55        "isMesh",
  56        "isAssembly",
  57        "isVolume",
  58        "isPicture",
  59        "isActor2D",
  60    ]
  61
  62    def __init__(self):
  63        return
  64
  65    def __getitem__(self, key):
  66        """Make the class work like a dictionary too"""
  67        return getattr(self, key)
  68
  69    def __setitem__(self, key, value):
  70        """Make the class work like a dictionary too"""
  71        setattr(self, key, value)
  72
  73    def __repr__(self):
  74        f = "---------- <vedo.plotter.Event object> ----------\n"
  75        for n in self.__slots__:
  76            try:
  77                if n == "actor" and self.actor and self.actor.name:
  78                        f += f"event.{n} = {self.actor.name} ({self.actor.npoints} points)\n"
  79                else:
  80                    f += f"event.{n} = " + str(self[n]).replace("\n", "")[:60] + "\n"
  81            except AttributeError:
  82                pass
  83                
  84        return f
  85
  86    def keys(self):
  87        return self.__slots__
  88
  89
  90##############################################################################################
  91def show(
  92    *actors,
  93    at=None,
  94    shape=(1, 1),
  95    N=None,
  96    pos=(0, 0),
  97    size="auto",
  98    screensize="auto",
  99    title="vedo",
 100    bg="white",
 101    bg2=None,
 102    axes=None,
 103    interactive=None,
 104    offscreen=False,
 105    sharecam=True,
 106    resetcam=True,
 107    zoom=None,
 108    viewup="",
 109    azimuth=0.0,
 110    elevation=0.0,
 111    roll=0.0,
 112    camera=None,
 113    mode=0,
 114    new=False,
 115):
 116    """
 117    Create on the fly an instance of class Plotter and show the object(s) provided.
 118
 119    Allowed input objects types are:
 120        ``str, Mesh, Volume, Picture, Assembly
 121        vtkPolyData, vtkActor, vtkActor2D, vtkImageActor,
 122        vtkAssembly or vtkVolume``
 123
 124    Arguments:
 125        at : (int)
 126            number of the renderer to plot to, in case of more than one exists
 127        shape : (list, str)
 128            Number of sub-render windows inside of the main window. E.g.:
 129            specify two across with shape=(2,1) and a two by two grid
 130            with shape=(2, 2).  By default there is only one renderer.
 131
 132            Can also accept a shape as string descriptor. E.g.:
 133            - shape="3|1" means 3 plots on the left and 1 on the right,
 134            - shape="4/2" means 4 plots on top of 2 at bottom.
 135
 136        axes : (int)
 137            set the type of axes to be shown:
 138            - 0,  no axes
 139            - 1,  draw three gray grid walls
 140            - 2,  show cartesian axes from (0,0,0)
 141            - 3,  show positive range of cartesian axes from (0,0,0)
 142            - 4,  show a triad at bottom left
 143            - 5,  show a cube at bottom left
 144            - 6,  mark the corners of the bounding box
 145            - 7,  draw a 3D ruler at each side of the cartesian axes
 146            - 8,  show the `vtkCubeAxesActor` object
 147            - 9,  show the bounding box outLine
 148            - 10, show three circles representing the maximum bounding box
 149            - 11, show a large grid on the x-y plane
 150            - 12, show polar axes
 151            - 13, draw a simple ruler at the bottom of the window
 152
 153            Axis type-1 can be fully customized by passing a dictionary.
 154            Check `vedo.addons.Axes()` for the full list of options.
 155        azimuth/elevation/roll : (float)
 156            move camera accordingly the specified value
 157        viewup : (str, list)
 158            either `['x', 'y', 'z']` or a vector to set vertical direction
 159        resetcam : (bool)
 160            re-adjust camera position to fit objects
 161        camera : (dict, vtkCamera)
 162            camera parameters can further be specified with a dictionary
 163            assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`):
 164            - **pos** (list),  the position of the camera in world coordinates
 165            - **focal_point** (list), the focal point of the camera in world coordinates
 166            - **viewup** (list), the view up direction for the camera
 167            - **distance** (float), set the focal point to the specified distance from the camera position.
 168            - **clipping_range** (float), distance of the near and far clipping planes along the direction of projection.
 169            - **parallel_scale** (float),
 170            scaling used for a parallel projection, i.e. the height of the viewport
 171            in world-coordinate distances. The default is 1. Note that the "scale" parameter works as
 172            an "inverse scale", larger numbers produce smaller images.
 173            This method has no effect in perspective projection mode.
 174            - **thickness** (float),
 175            set the distance between clipping planes. This method adjusts the far clipping
 176            plane to be set a distance 'thickness' beyond the near clipping plane.
 177            - **view_angle** (float),
 178            the camera view angle, which is the angular height of the camera view
 179            measured in degrees. The default angle is 30 degrees.
 180            This method has no effect in parallel projection mode.
 181            The formula for setting the angle up for perfect perspective viewing is:
 182            angle = 2*atan((h/2)/d) where h is the height of the RenderWindow
 183            (measured by holding a ruler up to your screen) and d is the distance
 184            from your eyes to the screen.
 185        interactive : (bool)
 186            pause and interact with window (True) or continue execution (False)
 187        rate : (float)
 188            maximum rate of `show()` in Hertz
 189        mode : (int, str)
 190            set the type of interaction:
 191            - 0 = TrackballCamera [default]
 192            - 1 = TrackballActor
 193            - 2 = JoystickCamera
 194            - 3 = JoystickActor
 195            - 4 = Flight
 196            - 5 = RubberBand2D
 197            - 6 = RubberBand3D
 198            - 7 = RubberBandZoom
 199            - 8 = Terrain
 200            - 9 = Unicam
 201            - 10 = Image
 202        new : (bool)
 203            if set to `True`, a call to show will instantiate
 204            a new Plotter object (a new window) instead of reusing the first created.
 205    """
 206    if len(actors) == 0:
 207        actors = None
 208    elif len(actors) == 1:
 209        actors = actors[0]
 210    else:
 211        actors = utils.flatten(actors)
 212
 213    if vedo.plotter_instance and not new:  # Plotter exists
 214        plt = vedo.plotter_instance
 215
 216    else:  # Plotter must be created
 217
 218        if utils.is_sequence(at):  # user passed a sequence for "at"
 219
 220            if not utils.is_sequence(actors):
 221                vedo.logger.error("in show() input must be a list.")
 222                raise RuntimeError()
 223            if len(at) != len(actors):
 224                vedo.logger.error("in show() lists 'input' and 'at' must have equal lengths")
 225                raise RuntimeError()
 226            if shape == (1, 1) and N is None:
 227                N = max(at) + 1
 228
 229        elif at is None and (N or shape != (1, 1)):
 230
 231            if not utils.is_sequence(actors):
 232                e = "in show(), N or shape is set, but input is not a sequence\n"
 233                e += "              you may need to specify e.g. at=0"
 234                vedo.logger.error(e)
 235                raise RuntimeError()
 236            at = list(range(len(actors)))
 237
 238        plt = Plotter(
 239            shape=shape,
 240            N=N,
 241            pos=pos,
 242            size=size,
 243            screensize=screensize,
 244            title=title,
 245            axes=axes,
 246            sharecam=sharecam,
 247            resetcam=resetcam,
 248            interactive=interactive,
 249            offscreen=offscreen,
 250            bg=bg,
 251            bg2=bg2,
 252        )
 253
 254    # use _plt_to_return because plt.show() can return a k3d plot
 255    _plt_to_return = None
 256
 257    if utils.is_sequence(at):
 258
 259        for i, act in enumerate(actors):
 260            _plt_to_return = plt.show(
 261                act,
 262                at=i,
 263                zoom=zoom,
 264                resetcam=resetcam,
 265                viewup=viewup,
 266                azimuth=azimuth,
 267                elevation=elevation,
 268                roll=roll,
 269                camera=camera,
 270                interactive=False,
 271                mode=mode,
 272                bg=bg,
 273                bg2=bg2,
 274                axes=axes,
 275            )
 276
 277        if (
 278            interactive
 279            or len(at) == N
 280            or (isinstance(shape[0], int) and len(at) == shape[0] * shape[1])
 281        ):
 282            # note that shape can be a string
 283            if plt.interactor and not offscreen and (interactive is None or interactive):
 284                plt.interactor.Start()
 285
 286    else:
 287
 288        _plt_to_return = plt.show(
 289            actors,
 290            at=at,
 291            zoom=zoom,
 292            resetcam=resetcam,
 293            viewup=viewup,
 294            azimuth=azimuth,
 295            elevation=elevation,
 296            roll=roll,
 297            camera=camera,
 298            interactive=interactive,
 299            mode=mode,
 300            bg=bg,
 301            bg2=bg2,
 302            axes=axes,
 303        )
 304
 305    return _plt_to_return
 306
 307
 308def close():
 309    """Close the last created Plotter instance if it exists."""
 310    if not vedo.plotter_instance:
 311        return
 312    vedo.plotter_instance.close()
 313    return
 314
 315
 316########################################################################
 317class Plotter:
 318    """Main class to manage actors."""
 319    def __init__(
 320        self,
 321        shape=(1, 1),
 322        N=None,
 323        pos=(0, 0),
 324        size="auto",
 325        screensize="auto",
 326        title="vedo",
 327        bg="white",
 328        bg2=None,
 329        axes=None,
 330        sharecam=True,
 331        resetcam=True,
 332        interactive=None,
 333        offscreen=False,
 334        qt_widget=None,
 335        wx_widget=None,
 336    ):
 337        """
 338        Arguments:
 339            shape : (str, list)
 340                shape of the grid of renderers in format (rows, columns). Ignored if N is specified.
 341            N : (int)
 342                number of desired renderers arranged in a grid automatically.
 343            pos : (list)
 344                (x,y) position in pixels of top-left corner of the rendering window on the screen
 345            size : (str, list)
 346                size of the rendering window. If 'auto', guess it based on screensize.
 347            screensize : (list)
 348                physical size of the monitor screen in pixels
 349            bg : (color, str)
 350                background color or specify jpg image file name with path
 351            bg2 : (color)
 352                background color of a gradient towards the top
 353            axes : (int)
 354
 355                Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`.
 356                Check out `vedo.addons.Axes()` for the available options.
 357                - 0,  no axes
 358                - 1,  draw three gray grid walls
 359                - 2,  show cartesian axes from (0,0,0)
 360                - 3,  show positive range of cartesian axes from (0,0,0)
 361                - 4,  show a triad at bottom left
 362                - 5,  show a cube at bottom left
 363                - 6,  mark the corners of the bounding box
 364                - 7,  draw a 3D ruler at each side of the cartesian axes
 365                - 8,  show the VTK CubeAxesActor object
 366                - 9,  show the bounding box outLine,
 367                - 10, show three circles representing the maximum bounding box,
 368                - 11, show a large grid on the x-y plane (use with zoom=8)
 369                - 12, show polar axes.
 370                - 13, draw a simple ruler at the bottom of the window
 371
 372            sharecam : (bool)
 373                if False each renderer will have an independent vtkCamera
 374            interactive : (bool)
 375                if True will stop after show() to allow interaction w/ window
 376            offscreen : (bool)
 377                if True will not show the rendering window
 378            qt_widget : (QVTKRenderWindowInteractor)
 379                render in a Qt-Widget using an QVTKRenderWindowInteractor.
 380                Overrides offscreen to True.
 381                Overrides interactive to False.
 382                See examples `qt_windows1.py` and `qt_windows2.py`
 383        """
 384        vedo.plotter_instance = self
 385
 386        if qt_widget is not None:
 387            # overrides the interactive and offscreen properties
 388            interactive = False
 389            offscreen = True
 390
 391        if wx_widget is not None:
 392            # overrides the interactive property
 393            interactive = False
 394
 395        if interactive is None:
 396            if N == 1:
 397                interactive = True
 398            elif N or shape != (1, 1):
 399                interactive = False
 400            else:
 401                interactive = True
 402
 403        self.actors = []  # list of actors to be shown
 404        self.clicked_actor = None  # holds the actor that has been clicked
 405        self.renderer = None  # current renderer
 406        self.renderers = []  # list of renderers
 407        self.shape = shape  # don't remove this line
 408        self._interactive = interactive  # allows to interact with renderer
 409        self.axes = axes  # show axes type nr.
 410        self.title = title  # window title
 411        self.sharecam = sharecam  # share the same camera if multiple renderers
 412        self.picker = None  # the vtkPicker object
 413        self.picked2d = None  # 2d coords of a clicked point on the rendering window
 414        self.picked3d = None  # 3d coords of a clicked point on an actor
 415        self.offscreen = offscreen
 416        self.resetcam = resetcam
 417        self.last_event = None
 418
 419        self.qt_widget = qt_widget  #  QVTKRenderWindowInteractor
 420        self.wx_widget = wx_widget  # wxVTKRenderWindowInteractor
 421
 422        self.skybox = None
 423
 424        # mostly internal stuff:
 425        self.hover_legends = []
 426        self.backgrcol = bg
 427        self.pos = pos  # used by vedo.file_io
 428        self.justremoved = None
 429        self.axes_instances = []
 430        self.clock = 0
 431        self.sliders = []
 432        self.buttons = []
 433        self.widgets = []
 434        self.cutter_widget = None
 435        self.hint_widget = None
 436        self.background_renderer = None
 437        self.size = size
 438        self.interactor = None
 439        self.camera = None
 440
 441        self._icol = 0
 442        self._clockt0 = time.time()
 443        self._extralight = None
 444        self._cocoa_initialized = False
 445        self._bg = bg  # used by backend notebooks
 446
 447        #####################################################################
 448        if settings.default_backend != "vtk":
 449            if settings.default_backend == "2d":
 450                self.offscreen = True
 451                if self.size == "auto":
 452                    self.size = (800, 600)
 453
 454            elif settings.default_backend == "k3d":
 455                self._interactive = False
 456                self.interactor = None
 457                self.window = None
 458                self.camera = None  # let the backend choose
 459                if self.size == "auto":
 460                    self.size = (1000, 1000)
 461                #############################################################
 462                return  ######################################################
 463                #############################################################
 464        #####################################################################
 465
 466        # build the rendering window:
 467        self.window = vtk.vtkRenderWindow()
 468
 469        self.window.GlobalWarningDisplayOff()
 470        self.window.SetWindowName(self.title)
 471
 472        # more settings
 473        if settings.use_depth_peeling:
 474            self.window.SetAlphaBitPlanes(settings.alpha_bit_planes)
 475        self.window.SetMultiSamples(settings.multi_samples)
 476
 477        self.window.SetPolygonSmoothing(settings.polygon_smoothing)
 478        self.window.SetLineSmoothing(settings.line_smoothing)
 479        self.window.SetPointSmoothing(settings.point_smoothing)
 480
 481        # sort out screen size
 482        if screensize == "auto":
 483            screensize = (2160, 1440)  # might go wrong, use a default 1.5 ratio
 484
 485            ### BUG in GetScreenSize in VTK 9.1.0
 486            ### https://discourse.vtk.org/t/vtk9-1-0-problems/7094/3
 487            if settings.hack_call_screen_size:  # True
 488
 489                vtkvers = vedo.vtk_version
 490                if not self.offscreen and (vtkvers[0] < 9 or vtkvers[0] == 9 and vtkvers[1] == 0):
 491                    aus = self.window.GetScreenSize()
 492                    if aus and len(aus) == 2 and aus[0] > 100 and aus[1] > 100:  # seems ok
 493                        if aus[0] / aus[1] > 2:  # looks like there are 2 or more screens
 494                            screensize = (int(aus[0] / 2), aus[1])
 495                        else:
 496                            screensize = aus
 497
 498        x, y = screensize
 499
 500        if N:  # N = number of renderers. Find out the best
 501
 502            if shape != (1, 1):  # arrangement based on minimum nr. of empty renderers
 503                vedo.logger.warning("having set N, shape is ignored.")
 504
 505            nx = int(np.sqrt(int(N * y / x) + 1))
 506            ny = int(np.sqrt(int(N * x / y) + 1))
 507            lm = [
 508                (nx, ny),
 509                (nx, ny + 1),
 510                (nx - 1, ny),
 511                (nx + 1, ny),
 512                (nx, ny - 1),
 513                (nx - 1, ny + 1),
 514                (nx + 1, ny - 1),
 515                (nx + 1, ny + 1),
 516                (nx - 1, ny - 1),
 517            ]
 518            ind, minl = 0, 1000
 519            for i, m in enumerate(lm):
 520                l = m[0] * m[1]
 521                if N <= l < minl:
 522                    ind = i
 523                    minl = l
 524            shape = lm[ind]
 525
 526        ##################################################
 527        if isinstance(shape, str):
 528
 529            if "|" in shape:
 530                if self.size == "auto":
 531                    self.size = (800, 1200)
 532                n = int(shape.split("|")[0])
 533                m = int(shape.split("|")[1])
 534                rangen = reversed(range(n))
 535                rangem = reversed(range(m))
 536            else:
 537                if self.size == "auto":
 538                    self.size = (1200, 800)
 539                m = int(shape.split("/")[0])
 540                n = int(shape.split("/")[1])
 541                rangen = range(n)
 542                rangem = range(m)
 543
 544            if n >= m:
 545                xsplit = m / (n + m)
 546            else:
 547                xsplit = 1 - n / (n + m)
 548            if settings.window_splitting_position:
 549                xsplit = settings.window_splitting_position
 550
 551            for i in rangen:
 552                arenderer = vtk.vtkRenderer()
 553                if "|" in shape:
 554                    arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n)
 555                else:
 556                    arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit)
 557                self.renderers.append(arenderer)
 558
 559            for i in rangem:
 560                arenderer = vtk.vtkRenderer()
 561
 562                if "|" in shape:
 563                    arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m)
 564                else:
 565                    arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1)
 566                self.renderers.append(arenderer)
 567
 568            for r in self.renderers:
 569                r.SetUseHiddenLineRemoval(settings.hidden_line_removal)
 570                r.SetLightFollowCamera(settings.light_follows_camera)
 571
 572                r.SetUseDepthPeeling(settings.use_depth_peeling)
 573                # r.SetUseDepthPeelingForVolumes(settings.use_depth_peeling)
 574                if settings.use_depth_peeling:
 575                    r.SetMaximumNumberOfPeels(settings.max_number_of_peels)
 576                    r.SetOcclusionRatio(settings.occlusion_ratio)
 577                r.SetUseFXAA(settings.use_fxaa)
 578                r.SetPreserveDepthBuffer(settings.preserve_depth_buffer)
 579
 580                r.SetBackground(vedo.get_color(self.backgrcol))
 581
 582                self.axes_instances.append(None)
 583
 584            self.shape = (n + m,)
 585
 586        elif utils.is_sequence(shape) and isinstance(shape[0], dict):
 587            # passing a sequence of dicts for renderers specifications
 588
 589            if self.size == "auto":
 590                self.size = (1200, 900)
 591
 592            for rd in shape:
 593                x0, y0 = rd["bottomleft"]
 594                x1, y1 = rd["topright"]
 595                bg_ = rd.pop("bg", "white")
 596                bg2_ = rd.pop("bg2", None)
 597
 598                arenderer = vtk.vtkRenderer()
 599                arenderer.SetUseHiddenLineRemoval(settings.hidden_line_removal)
 600                arenderer.SetLightFollowCamera(settings.light_follows_camera)
 601
 602                arenderer.SetUseDepthPeeling(settings.use_depth_peeling)
 603                # arenderer.SetUseDepthPeelingForVolumes(settings.use_depth_peeling)
 604                if settings.use_depth_peeling:
 605                    arenderer.SetMaximumNumberOfPeels(settings.max_number_of_peels)
 606                    arenderer.SetOcclusionRatio(settings.occlusion_ratio)
 607                arenderer.SetUseFXAA(settings.use_fxaa)
 608                arenderer.SetPreserveDepthBuffer(settings.preserve_depth_buffer)
 609
 610                arenderer.SetViewport(x0, y0, x1, y1)
 611                arenderer.SetBackground(vedo.get_color(bg_))
 612                if bg2_:
 613                    arenderer.GradientBackgroundOn()
 614                    arenderer.SetBackground2(vedo.get_color(bg2_))
 615
 616                self.renderers.append(arenderer)
 617                self.axes_instances.append(None)
 618
 619            self.shape = (len(shape),)
 620
 621        else:
 622
 623            if isinstance(self.size, str) and self.size == "auto":
 624                # figure out a reasonable window size
 625                f = 1.5
 626                xs = y / f * shape[1]  # because y<x
 627                ys = y / f * shape[0]
 628                if xs > x / f:  # shrink
 629                    xs = x / f
 630                    ys = xs / shape[1] * shape[0]
 631                if ys > y / f:
 632                    ys = y / f
 633                    xs = ys / shape[0] * shape[1]
 634                self.size = (int(xs), int(ys))
 635                if shape == (1, 1):
 636                    self.size = (int(y / f), int(y / f))  # because y<x
 637            else:
 638                self.size = (self.size[0], self.size[1])
 639
 640            image_actor = None
 641            bgname = str(self.backgrcol).lower()
 642            if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname:
 643                self.window.SetNumberOfLayers(2)
 644                self.background_renderer = vtk.vtkRenderer()
 645                self.background_renderer.SetLayer(0)
 646                self.background_renderer.InteractiveOff()
 647                self.background_renderer.SetBackground(vedo.get_color(bg2))
 648                image_actor = vedo.Picture(self.backgrcol)
 649                self.window.AddRenderer(self.background_renderer)
 650                self.background_renderer.AddActor(image_actor)
 651
 652            for i in reversed(range(shape[0])):
 653                for j in range(shape[1]):
 654                    arenderer = vtk.vtkRenderer()
 655                    arenderer.SetUseHiddenLineRemoval(settings.hidden_line_removal)
 656                    arenderer.SetLightFollowCamera(settings.light_follows_camera)
 657                    arenderer.SetTwoSidedLighting(settings.two_sided_lighting)
 658
 659                    arenderer.SetUseDepthPeeling(settings.use_depth_peeling)
 660                    # arenderer.SetUseDepthPeelingForVolumes(settings.use_depth_peeling)
 661                    if settings.use_depth_peeling:
 662                        arenderer.SetMaximumNumberOfPeels(settings.max_number_of_peels)
 663                        arenderer.SetOcclusionRatio(settings.occlusion_ratio)
 664                    arenderer.SetUseFXAA(settings.use_fxaa)
 665                    arenderer.SetPreserveDepthBuffer(settings.preserve_depth_buffer)
 666
 667                    if image_actor:
 668                        arenderer.SetLayer(1)
 669
 670                    arenderer.SetBackground(vedo.get_color(self.backgrcol))
 671                    if bg2:
 672                        arenderer.GradientBackgroundOn()
 673                        arenderer.SetBackground2(vedo.get_color(bg2))
 674
 675                    x0 = i / shape[0]
 676                    y0 = j / shape[1]
 677                    x1 = (i + 1) / shape[0]
 678                    y1 = (j + 1) / shape[1]
 679                    arenderer.SetViewport(y0, x0, y1, x1)
 680                    self.renderers.append(arenderer)
 681                    self.axes_instances.append(None)
 682            self.shape = shape
 683
 684        if self.renderers:
 685            self.renderer = self.renderers[0]
 686            self.camera = self.renderer.GetActiveCamera()
 687            self.camera.SetParallelProjection(settings.use_parallel_projection)
 688
 689        if self.size[0] == "f":  # full screen
 690            self.size = "fullscreen"
 691            self.window.SetFullScreen(True)
 692            self.window.BordersOn()
 693        else:
 694            self.window.SetSize(int(self.size[0]), int(self.size[1]))
 695
 696        if self.wx_widget is not None:
 697            settings.immediate_rendering = False  # override
 698            self.window = self.wx_widget.GetRenderWindow()  # overwrite
 699            self.interactor = self.window.GetInteractor()
 700            for r in self.renderers:
 701                self.window.AddRenderer(r)
 702            self.wx_widget.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
 703            self.camera = self.renderer.GetActiveCamera()
 704            ########################
 705            return  ################
 706            ########################
 707
 708        if self.qt_widget is not None:
 709            self.interactor = self.qt_widget.GetRenderWindow().GetInteractor()
 710            self.window = self.qt_widget.GetRenderWindow()  # overwrite
 711            ########################
 712            return  ################
 713            ########################
 714
 715        self.window.SetPosition(pos)
 716
 717        for r in self.renderers:
 718            self.window.AddRenderer(r)
 719
 720        if self.offscreen:
 721            if self.axes in (4, 5):
 722                self.axes = 0  # does not work with those
 723            self.window.SetOffScreenRendering(True)
 724            self._interactive = False
 725            self.interactor = None
 726            ########################
 727            return  ################
 728            ########################
 729
 730        self.interactor = vtk.vtkRenderWindowInteractor()
 731
 732        self.interactor.SetRenderWindow(self.window)
 733        vsty = vtk.vtkInteractorStyleTrackballCamera()
 734        self.interactor.SetInteractorStyle(vsty)
 735
 736        if settings.enable_default_mouse_callbacks:
 737            self.interactor.AddObserver("LeftButtonPressEvent", self._mouseleftclick)
 738
 739        if settings.enable_default_keyboard_callbacks:
 740            self.interactor.AddObserver("KeyPressEvent", self._keypress)
 741
 742        # self._timer_event_id = None
 743        # if settings.allow_interaction:
 744            # def win_interact(iren, event):  # flushing interactor events
 745            #     if event == "TimerEvent":
 746            #         iren.ExitCallback()
 747            # self._timer_event_id = self.interactor.AddObserver("TimerEvent", win_interact)
 748
 749    ##################################################################### ..init ends here.
 750
 751
 752    # def allow_interaction(self):
 753    #     """Call this method from inside a loop to allow mouse and keyboard interaction."""
 754    #     if (
 755    #         self.interactor
 756    #         and self._timer_event_id is not None
 757    #         and settings.immediate_rendering
 758    #     ):
 759    #         self._repeatingtimer_id = self.interactor.CreateRepeatingTimer(1)
 760    #         self.interactor.Start()
 761    #         if self.interactor:
 762    #             self.interactor.DestroyTimer(self._repeatingtimer_id)
 763    #         self._repeatingtimer_id = None
 764    #     return self
 765
 766    def __iadd__(self, actors):
 767        self.add(actors)
 768        return self
 769
 770    def __isub__(self, actors):
 771        self.remove(actors)
 772        return self
 773
 774    def __enter__(self):
 775        # context manager like in "with Plotter() as plt:"
 776        return self
 777
 778    def __exit__(self, *args, **kwargs):
 779        # context manager like in "with Plotter() as plt:"
 780        self.close()
 781
 782    def process_events(self):
 783        if self.interactor:
 784            try:
 785                self.interactor.ProcessEvents()
 786            except AttributeError:
 787                pass
 788        return self
 789
 790    def at(self, nren, yren=None):
 791        """
 792        Select the current renderer number as an int.
 793        Can also use the [nx, ny] format.
 794        """
 795        if yren is not None:
 796            nren = (yren) * self.shape[1] + (nren)
 797            if nren < 0 or nren > len(self.renderers):
 798                vedo.logger.error(f"at({nren, yren}) is malformed!")
 799                raise RuntimeError
 800
 801        self.renderer = self.renderers[nren]
 802        self.camera = self.renderer.GetActiveCamera()
 803        return self
 804
 805
 806    def add(self, *actors, at=None):
 807        """
 808        Append the input objects to the internal list of actors to be shown.
 809        This method is typically used in loops or callback functions.
 810
 811        Arguments:
 812            at : (int)
 813                add the object at the specified renderer
 814        """
 815        if at is not None:
 816            ren = self.renderers[at]
 817        else:
 818            ren = self.renderer
 819
 820        actors = utils.flatten(actors)
 821        actors = self._scan_input(actors)
 822
 823        for a in actors:
 824            if isinstance(a, vtk.vtkInteractorObserver):
 825                a.add_to(self)
 826                continue
 827
 828            if a not in self.actors:
 829                self.actors.append(a)
 830
 831            if ren:
 832                ren.AddActor(a)
 833
 834                if hasattr(a, "rendered_at"):
 835                    ir = self.renderers.index(ren)
 836                    a.rendered_at.add(ir)
 837
 838                if hasattr(a, "scalarbar") and a.scalarbar:
 839                    ren.AddActor(a.scalarbar)
 840
 841                if hasattr(a, "_isfollower") and a._isfollower:  # set by mesh.follow_camera()
 842                    a.SetCamera(self.camera)
 843
 844        return self
 845
 846    def remove(self, *actors, at=None):
 847        """
 848        Remove input object to the internal list of actors to be shown.
 849        This method is typically used in loops or callback functions.
 850        Objects to be removed can be referenced by their assigned name.
 851
 852        Arguments:
 853            at : (int)
 854                remove the object at the specified renderer
 855        """
 856        if at is not None:
 857            ren = self.renderers[at]
 858        else:
 859            ren = self.renderer
 860
 861        actors = utils.flatten(actors)
 862
 863        actors_in_ren = None
 864
 865        actors_r = []
 866        for i, a in enumerate(actors):
 867
 868            if isinstance(a, vtk.vtkInteractorObserver):
 869                a.remove_from(self)
 870                continue ###
 871
 872            if isinstance(a, str):
 873                if actors_in_ren is None:
 874                    actors_in_ren = self.get_meshes(
 875                        include_non_pickables=True,
 876                        unpack_assemblies=False,
 877                    )
 878
 879                for b in set(self.actors + actors_in_ren):
 880                    if hasattr(b, "name") and a in b.name:
 881                        actors_r.append(b)
 882
 883            else:
 884                actors_r.append(a)
 885
 886        for a in set(actors_r):
 887            if ren:
 888                ren.RemoveActor(a)
 889                if hasattr(a, "rendered_at"):
 890                    ir = self.renderers.index(ren)
 891                    a.rendered_at.discard(ir)
 892                if hasattr(a, "scalarbar") and a.scalarbar:
 893                    ren.RemoveActor(a.scalarbar)
 894                if hasattr(a, "_caption") and a._caption:
 895                    ren.RemoveActor(a._caption)
 896                if hasattr(a, "shadows") and a.shadows:
 897                    for sha in a.shadows:
 898                        ren.RemoveActor(sha)
 899                if hasattr(a, "trail") and a.trail:
 900                    ren.RemoveActor(a.trail)
 901                    a.trail_points = []
 902                    if hasattr(a.trail, "shadows") and a.trail.shadows:
 903                        for sha in a.trail.shadows:
 904                            ren.RemoveActor(sha)
 905
 906            if a in self.actors:
 907                i = self.actors.index(a)
 908                del self.actors[i]
 909
 910        return self
 911
 912    def remove_lights(self):
 913        """Remove all the present lights in the current renderer."""
 914        if self.renderer:
 915            self.renderer.RemoveAllLights()
 916        return self
 917
 918    def pop(self, at=None):
 919        """
 920        Remove the last added object from the rendering window.
 921        This method is typically used in loops or callback functions.
 922        """
 923        if at is not None and not isinstance(at, int):
 924            # wrong usage pitfall
 925            vedo.logger.error("argument of pop() must be an integer")
 926            raise RuntimeError()
 927
 928        if self.actors:
 929            self.remove(self.actors[-1], at)
 930        return self
 931
 932    def render(self, resetcam=False):
 933        """Render the scene. This method is typically used in loops or callback functions."""
 934        if not self.window:
 935            return self
 936
 937        if self.wx_widget:
 938            if resetcam:
 939                self.renderer.ResetCamera()
 940            self.wx_widget.Render()
 941            return self
 942
 943        if self.qt_widget:
 944            if resetcam:
 945                self.renderer.ResetCamera()
 946            self.qt_widget.Render()
 947            return self
 948
 949        if self.interactor:
 950            if not self.interactor.GetInitialized():
 951                self.interactor.Initialize()
 952
 953        self.camera = self.renderer.GetActiveCamera()
 954        if resetcam:
 955            self.renderer.ResetCamera()
 956
 957        self.window.Render()
 958        return self
 959
 960    def interactive(self):
 961        """
 962        Start window interaction.
 963        Analogous to `show(..., interactive=True)`.
 964        """
 965        if self.interactor:
 966            self.interactor.Start()
 967        return self
 968
 969    def use_depth_peeling(self, at=None, value=True):
 970        """
 971        Specify whether use depth peeling algorithm at this specific renderer
 972        Call this method before the first rendering.
 973        """
 974        if at is None:
 975            ren = self.renderer
 976        else:
 977            ren = self.renderers[at]
 978        ren.SetUseDepthPeeling(value)
 979        return self
 980
 981    def background(self, c1=None, c2=None, at=None):
 982        """Set the color of the background for the current renderer.
 983        A different renderer index can be specified by keyword ``at``.
 984
 985        Arguments:
 986            c1 : (list)
 987                background main color.
 988            c2 : (list)
 989                background color for the upper part of the window.
 990            at : (int)
 991                renderer index.
 992        """
 993        if not self.renderers:
 994            return self
 995        if at is None:
 996            r = self.renderer
 997        else:
 998            r = self.renderers[at]
 999        if r:
1000            if c1 is not None:
1001                r.SetBackground(vedo.get_color(c1))
1002                self._bg = r.GetBackground()  # notebooks
1003            if c2 is not None:
1004                r.GradientBackgroundOn()
1005                r.SetBackground2(vedo.get_color(c2))
1006            else:
1007                r.GradientBackgroundOff()
1008        return self
1009
1010    ##################################################################
1011    def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True):
1012        """
1013        Return a list of Meshes from the specified renderer.
1014
1015        Arguments:
1016            at : (int)
1017                specify which renderer to look at.
1018            include_non_pickables : (bool)
1019                include non-pickable objects
1020            unpack_assemblies : (bool)
1021                unpack assemblies into their components
1022        """
1023        if at is None:
1024            renderer = self.renderer
1025            at = self.renderers.index(renderer)
1026        elif isinstance(at, int):
1027            renderer = self.renderers[at]
1028
1029        has_global_axes = False
1030        if isinstance(self.axes_instances[at], vedo.Assembly):
1031            has_global_axes = True
1032
1033        if unpack_assemblies:
1034            acs = renderer.GetActors()
1035        else:
1036            acs = renderer.GetViewProps()
1037
1038        actors = []
1039        acs.InitTraversal()
1040        for _ in range(acs.GetNumberOfItems()):
1041
1042            if unpack_assemblies:
1043                a = acs.GetNextItem()
1044            else:
1045                a = acs.GetNextProp()
1046
1047            if isinstance(a, vtk.vtkVolume):
1048                continue
1049
1050            if include_non_pickables or a.GetPickable():
1051                if a == self.axes_instances[at]:
1052                    continue
1053                if has_global_axes and a in self.axes_instances[at].actors:
1054                    continue
1055                actors.append(a)
1056        return actors
1057
1058    def get_volumes(self, at=None, include_non_pickables=False):
1059        """
1060        Return a list of Volumes from the specified renderer.
1061
1062        Arguments:
1063            at : (int)
1064                specify which renderer to look at
1065            include_non_pickables : (bool)
1066                include non-pickable objects
1067        """
1068        if at is None:
1069            renderer = self.renderer
1070            at = self.renderers.index(renderer)
1071        elif isinstance(at, int):
1072            renderer = self.renderers[at]
1073
1074        vols = []
1075        acs = renderer.GetVolumes()
1076        acs.InitTraversal()
1077        for _ in range(acs.GetNumberOfItems()):
1078            a = acs.GetNextItem()
1079            if include_non_pickables or a.GetPickable():
1080                vols.append(a)
1081        return vols
1082
1083    def reset_camera(self, tight=None):
1084        """
1085        Reset the camera position and zooming.
1086        If tight (float) is specified the zooming reserves a padding space in the xy-plane
1087        expressed in percent of the average size.
1088        """
1089        if tight is None:
1090            self.renderer.ResetCamera()
1091        else:
1092            x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds()
1093
1094            cam = self.renderer.GetActiveCamera()
1095
1096            self.renderer.ComputeAspect()
1097            aspect = self.renderer.GetAspect()
1098            angle = np.pi * cam.GetViewAngle() / 180.0
1099            dx, dy = (x1 - x0) * 0.999, (y1 - y0) * 0.999
1100            dist = max(dx / aspect[0], dy) / np.sin(angle / 2) / 2
1101
1102            cam.SetViewUp(0, 1, 0)
1103            cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight))
1104            cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0)
1105            if cam.GetParallelProjection():
1106                ps = max(dx / aspect[0], dy) / 2
1107                cam.SetParallelScale(ps * (1 + tight))
1108            self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1)
1109        return self
1110
1111    def reset_viewup(self, smooth=True):
1112        """
1113        Reset the orientation of the camera to the closest orthogonal direction and view-up.
1114        """
1115        vbb = addons.compute_visible_bounds()[0]
1116        x0, x1, y0, y1, z0, z1 = vbb
1117        mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2
1118        d = self.camera.GetDistance()
1119
1120        viewups = np.array([
1121            (0, 1, 0), ( 0, -1,  0),
1122            (0, 0, 1), ( 0,  0, -1),
1123            (1, 0, 0), (-1,  0,  0),
1124        ])
1125        positions = np.array([
1126            (mx, my, mz+d), (mx, my, mz-d),
1127            (mx, my+d, mz), (mx, my-d, mz),
1128            (mx+d, my, mz), (mx-d, my, mz),
1129        ])
1130
1131        vu = np.array(self.camera.GetViewUp())
1132        vui = np.argmin(np.linalg.norm(viewups - vu, axis=1))
1133
1134        poc = np.array(self.camera.GetPosition())
1135        foc = np.array(self.camera.GetFocalPoint())
1136        a = poc - foc
1137        b = positions - foc
1138        a = a / np.linalg.norm(a)
1139        b = b.T * (1 / np.linalg.norm(b, axis=1))
1140        pui = np.argmin(np.linalg.norm(b.T - a, axis=1))
1141
1142        if smooth:
1143            outtimes = np.linspace(0, 1, num=11, endpoint=True)
1144            for t in outtimes:
1145                vv = vu * (1 - t) + viewups[vui] * t
1146                pp = poc * (1 - t) + positions[pui] * t
1147                ff = foc * (1 - t) + np.array([mx, my, mz]) * t
1148                self.camera.SetViewUp(vv)
1149                self.camera.SetPosition(pp)
1150                self.camera.SetFocalPoint(ff)
1151                self.render()
1152
1153            # interpolator does not respect parallel view...:
1154            # cam1 = dict(
1155            #     pos=poc,
1156            #     viewup=vu,
1157            #     focal_point=(mx,my,mz),
1158            #     clipping_range=self.camera.GetClippingRange()
1159            # )
1160            # # cam1 = self.camera
1161            # cam2 = dict(
1162            #     pos=positions[pui],
1163            #     viewup=viewups[vui],
1164            #     focal_point=(mx,my,mz),
1165            #     clipping_range=self.camera.GetClippingRange()
1166            # )
1167            # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0)
1168            # for c in vcams:
1169            #     self.renderer.SetActiveCamera(c)
1170            #     self.render()
1171        else:
1172
1173            self.camera.SetViewUp(viewups[vui])
1174            self.camera.SetPosition(positions[pui])
1175            self.camera.SetFocalPoint(mx, my, mz)
1176
1177        self.renderer.ResetCameraClippingRange()
1178
1179        # vbb, _, _, _ = addons.compute_visible_bounds()
1180        # x0,x1, y0,y1, z0,z1 = vbb
1181        # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1)
1182        self.render()
1183        return self
1184
1185    def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()):
1186        """
1187        Takes as input two cameras set camera at an interpolated position:
1188
1189        Cameras can be vtkCamera or dictionaries in format:
1190
1191            `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)`
1192
1193        Press `shift-C` key in interactive mode to dump a python snipplet
1194        of parameters for the current camera view.
1195        """
1196        nc = len(cameras)
1197        if len(times) == 0:
1198            times = np.linspace(0, 1, num=nc, endpoint=True)
1199
1200        assert len(times) == nc
1201
1202        cin = vtk.vtkCameraInterpolator()
1203
1204        # cin.SetInterpolationTypeToLinear() # bugged?
1205        if nc > 2 and smooth:
1206            cin.SetInterpolationTypeToSpline()
1207
1208        for i, cam in enumerate(cameras):
1209            vcam = cam
1210            if isinstance(cam, dict):
1211                vcam = utils.camera_from_dict(cam)
1212            cin.AddCamera(times[i], vcam)
1213
1214        mint, maxt = cin.GetMinimumT(), cin.GetMaximumT()
1215        rng = maxt - mint
1216
1217        if len(output_times) == 0:
1218            cin.InterpolateCamera(t * rng, self.camera)
1219            self.renderer.SetActiveCamera(self.camera)
1220            return [self.camera]
1221        else:
1222            vcams = []
1223            for tt in output_times:
1224                c = vtk.vtkCamera()
1225                cin.InterpolateCamera(tt * rng, c)
1226                vcams.append(c)
1227            return vcams
1228
1229    def fly_to(self, point):
1230        """
1231        Fly camera to the specified point.
1232
1233        Arguments:
1234            point : (list)
1235                point in space to place camera.
1236
1237        Example:
1238            ```python
1239            from vedo import *
1240            cone = Cone()
1241            plt = Plotter(axes=1)
1242            plt.show(cone)
1243            plt.fly_to([1,0,0])
1244            plt.interactive().close()
1245            ```
1246        """
1247        if self.interactor:
1248            self.resetcam = False
1249            self.interactor.FlyTo(self.renderer, point)
1250            self.camera = self.renderer.GetActiveCamera()
1251        return self
1252
1253    def look_at(self, plane="xy"):
1254        """Move the camera so that it looks at the specified cartesian plane"""
1255        cam = self.renderer.GetActiveCamera()
1256        fp = np.array(cam.GetFocalPoint())
1257        p = np.array(cam.GetPosition())
1258        dist = np.linalg.norm(fp - p)
1259        plane = plane.lower()
1260        if "x" in plane and "y" in plane:
1261            cam.SetPosition(fp[0], fp[1], fp[2] + dist)
1262            cam.SetViewUp(0.0, 1.0, 0.0)
1263        elif "x" in plane and "z" in plane:
1264            cam.SetPosition(fp[0], fp[1] - dist, fp[2])
1265            cam.SetViewUp(0.0, 0.0, 1.0)
1266        elif "y" in plane and "z" in plane:
1267            cam.SetPosition(fp[0] + dist, fp[1], fp[2])
1268            cam.SetViewUp(0.0, 0.0, 1.0)
1269        else:
1270            vedo.logger.error(f"in plotter.look() cannot understand argument {plane}")
1271        return self
1272
1273    def record(self, filename=".vedo_recorded_events.log"):
1274        """
1275        Record camera, mouse, keystrokes and all other events.
1276        Recording can be toggled on/off by pressing key "R".
1277
1278        Arguments:
1279            filename : (str)
1280                ascii file to store events. The default is '.vedo_recorded_events.log'.
1281
1282        Returns:
1283            a string descriptor of events.
1284
1285        Examples:
1286            - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py)
1287        """
1288        erec = vtk.vtkInteractorEventRecorder()
1289        erec.SetInteractor(self.interactor)
1290        erec.SetFileName(filename)
1291        erec.SetKeyPressActivationValue("R")
1292        erec.EnabledOn()
1293        erec.Record()
1294        self.interactor.Start()
1295        erec.Stop()
1296        erec.EnabledOff()
1297        with open(filename, "r", encoding="UTF-8") as fl:
1298            events = fl.read()
1299        erec = None
1300        return events
1301
1302    def play(self, events=".vedo_recorded_events.log", repeats=0):
1303        """
1304        Play camera, mouse, keystrokes and all other events.
1305
1306        Arguments:
1307            events : (str)
1308                file o string of events. The default is '.vedo_recorded_events.log'.
1309            repeats : (int)
1310                number of extra repeats of the same events. The default is 0.
1311
1312        Examples:
1313            - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py)
1314        """
1315        erec = vtk.vtkInteractorEventRecorder()
1316        erec.SetInteractor(self.interactor)
1317
1318        if events.endswith(".log"):
1319            erec.ReadFromInputStringOff()
1320            erec.SetFileName(events)
1321        else:
1322            erec.ReadFromInputStringOn()
1323            erec.SetInputString(events)
1324
1325        erec.Play()
1326        for _i in range(repeats):
1327            erec.Rewind()
1328            erec.Play()
1329        erec.EnabledOff()
1330        erec = None
1331        return self
1332
1333    def parallel_projection(self, value=True, at=None):
1334        """
1335        Use parallel projection `at` a specified renderer.
1336        Object is seen from "infinite" distance, e.i. remove any perspective effects.
1337        An input value equal to -1 will toggle it on/off.
1338        """
1339        if at is not None:
1340            r = self.renderers[at]
1341        else:
1342            r = self.renderer
1343        if value == -1:
1344            val = r.GetActiveCamera().GetParallelProjection()
1345            value = not val
1346        r.GetActiveCamera().SetParallelProjection(value)
1347        r.Modified()
1348        return self
1349
1350    def fov(self, angle):
1351        """
1352        Set the field of view angle for the camera.
1353        This is the angle of the camera frustum in the horizontal direction.
1354        High values will result in a wide-angle lens (fish-eye effect),
1355        and low values will result in a telephoto lens.
1356
1357        Default value is 30 degrees.
1358        """
1359        self.renderer.GetActiveCamera().UseHorizontalViewAngleOn()
1360        self.renderer.GetActiveCamera().SetViewAngle(angle)
1361        return self
1362
1363    def zoom(self, zoom):
1364        """Apply a zooming factor for the current camera view"""
1365        self.renderer.GetActiveCamera().Zoom(zoom)
1366        return self
1367    
1368    def azimuth(self, angle):
1369        """Rotate camera around the view up vector."""
1370        self.renderer.GetActiveCamera().Azimuth(angle)
1371        return self
1372    
1373    def elevation(self, angle):
1374        """Rotate the camera around the cross product of the negative
1375        of the direction of projection and the view up vector."""
1376        self.renderer.GetActiveCamera().Elevation(angle)
1377        return self
1378    
1379    def roll(self, angle):
1380        """Roll the camera about the direction of projection."""
1381        self.renderer.GetActiveCamera().Roll(angle)
1382        return self
1383    
1384    def dolly(self, value):
1385        """Move the camera towards (value>0) or away from (value<0) the focal point."""
1386        self.renderer.GetActiveCamera().Dolly(value)
1387        return self
1388
1389
1390    ##################################################################
1391    def add_slider(
1392        self,
1393        sliderfunc: Callable,
1394        xmin,
1395        xmax,
1396        value=None,
1397        pos=4,
1398        title="",
1399        font="",
1400        title_size=1,
1401        c=None,
1402        alpha=1,
1403        show_value=True,
1404        delayed=False,
1405        **options,
1406    ):
1407        """
1408        Add a `vedo.addons.Slider2D` which can call an external custom function.
1409
1410        Arguments:
1411            sliderfunc : (Callable)
1412                external function to be called by the widget
1413            xmin : (float)
1414                lower value of the slider
1415            xmax : (float)
1416                upper value
1417            value : (float)
1418                current value
1419            pos : (list, str)
1420                position corner number: horizontal [1-5] or vertical [11-15]
1421                it can also be specified by corners coordinates [(x1,y1), (x2,y2)]
1422                and also by a string descriptor (eg. "bottom-left")
1423            title : (str)
1424                title text
1425            font : (str)
1426                title font face. Check [available fonts here](https://vedo.embl.es/fonts).
1427            title_size : (float)
1428                title text scale [1.0]
1429            show_value : (bool)
1430                if True current value is shown
1431            delayed : (bool)
1432                if True the callback is delayed until when the mouse button is released
1433            alpha : (float)
1434                opacity of the scalar bar texts
1435            slider_length : (float)
1436                slider length
1437            slider_width : (float)
1438                slider width
1439            end_cap_length : (float)
1440                length of the end cap
1441            end_cap_width : (float)
1442                width of the end cap
1443            tube_width : (float)
1444                width of the tube
1445            title_height : (float)
1446                width of the title
1447            tformat : (str)
1448                format of the title
1449
1450        Examples:
1451            - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py)
1452            - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py)
1453
1454            ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg)
1455        """
1456        if c is None:  # automatic black or white
1457            c = (0.8, 0.8, 0.8)
1458            if np.sum(vedo.get_color(self.backgrcol)) > 1.5:
1459                c = (0.2, 0.2, 0.2)
1460        else:
1461            c = vedo.get_color(c)
1462
1463        slider2d = addons.Slider2D(
1464            sliderfunc,
1465            xmin,
1466            xmax,
1467            value,
1468            pos,
1469            title,
1470            font,
1471            title_size,
1472            c,
1473            alpha,
1474            show_value,
1475            delayed,
1476            **options,
1477        )
1478
1479        if self.renderer:
1480            slider2d.renderer = self.renderer
1481            if self.interactor:
1482                slider2d.interactor = self.interactor
1483                slider2d.on()
1484                self.sliders.append([slider2d, sliderfunc])
1485        return slider2d
1486
1487
1488    def add_slider3d(
1489        self,
1490        sliderfunc,
1491        pos1,
1492        pos2,
1493        xmin,
1494        xmax,
1495        value=None,
1496        s=0.03,
1497        t=1,
1498        title="",
1499        rotation=0.0,
1500        c=None,
1501        show_value=True,
1502    ):
1503        """
1504        Add a 3D slider widget which can call an external custom function.
1505
1506        Arguments:
1507            sliderfunc : (function)
1508                external function to be called by the widget
1509            pos1 : (list)
1510                first position 3D coordinates
1511            pos2 : (list)
1512                second position coordinates
1513            xmin : (float)
1514                lower value
1515            xmax : (float)
1516                upper value
1517            value : (float)
1518                initial value
1519            s : (float)
1520                label scaling factor
1521            t : (float)
1522                tube scaling factor
1523            title : (str)
1524                title text
1525            c : (color)
1526                slider color
1527            rotation : (float)
1528                title rotation around slider axis
1529            show_value : (bool)
1530                if True current value is shown
1531
1532        Examples:
1533            - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py)
1534
1535            ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png)
1536        """
1537        if c is None:  # automatic black or white
1538            c = (0.8, 0.8, 0.8)
1539            if np.sum(vedo.get_color(self.backgrcol)) > 1.5:
1540                c = (0.2, 0.2, 0.2)
1541        else:
1542            c = vedo.get_color(c)
1543
1544        slider3d = addons.Slider3D(
1545            sliderfunc, pos1, pos2, xmin, xmax, value, s, t, title, rotation, c, show_value
1546        )
1547        slider3d.renderer = self.renderer
1548        slider3d.interactor = self.interactor
1549        slider3d.on()
1550        self.sliders.append([slider3d, sliderfunc])
1551        return slider3d
1552
1553
1554    def add_button(
1555        self,
1556        fnc=None,
1557        states=("On", "Off"),
1558        c=("w", "w"),
1559        bc=("green4", "red4"),
1560        pos=(0.7, 0.05),
1561        size=24,
1562        font=None,
1563        bold=False,
1564        italic=False,
1565        alpha=1,
1566        angle=0,
1567        name="Button",
1568    ):
1569        """
1570        Add a button to the renderer window.
1571
1572        Arguments:
1573            states : (list)
1574                a list of possible states, e.g. ['On', 'Off']
1575            c : (list)
1576                a list of colors for each state
1577            bc : (list)
1578                a list of background colors for each state
1579            pos : (list)
1580                2D position in pixels from left-bottom corner
1581            size : (float)
1582                size of button font
1583            font : (str)
1584                font type. Check [available fonts here](https://vedo.embl.es/fonts).
1585            bold : (bool)
1586                bold font face (False)
1587            italic : (bool)
1588                italic font face (False)
1589            alpha : (float)
1590                opacity level
1591            angle : (float)
1592                anticlockwise rotation in degrees
1593
1594        Returns:
1595            `vedo.addons.Button` object.
1596
1597        Examples:
1598            - [buttons.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons.py)
1599
1600            ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg)
1601        """
1602        if self.interactor:
1603            bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle, name)
1604            self.renderer.AddActor2D(bu)
1605            self.buttons.append(bu)
1606            bu.function_id = self.add_callback("LeftButtonPress", bu.function)
1607            return bu
1608
1609    def add_spline_tool(
1610        self, points, pc="k", ps=8, lc="r4", ac="g5", lw=2, closed=False, interactive=False
1611    ):
1612        """
1613        Add a spline tool to the current plotter.
1614        Nodes of the spline can be dragged in space with the mouse.
1615        Clicking on the line itself adds an extra point.
1616        Selecting a point and pressing del removes it.
1617
1618        Arguments:
1619            points : (Mesh, Points, array)
1620                the set of vertices forming the spline nodes.
1621            pc : (str)
1622                point color. The default is 'k'.
1623            ps : (str)
1624                point size. The default is 8.
1625            lc : (str)
1626                line color. The default is 'r4'.
1627            ac : (str)
1628                active point marker color. The default is 'g5'.
1629            lw : (int)
1630                line width. The default is 2.
1631            closed : (bool)
1632                spline is meant to be closed. The default is False.
1633
1634        Returns:
1635            a `SplineTool` object.
1636
1637        Examples:
1638            - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py)
1639
1640            ![](https://vedo.embl.es/images/basic/spline_tool.png)
1641        """
1642        sw = addons.SplineTool(points, pc, ps, lc, ac, lw, closed)
1643        if self.interactor:
1644            sw.SetInteractor(self.interactor)
1645        else:
1646            vedo.logger.error("in add_spline_tool(), No interactor found.")
1647            raise RuntimeError
1648        sw.On()
1649        sw.Initialize(sw.points.polydata())
1650        if sw.closed:
1651            sw.representation.ClosedLoopOn()
1652        sw.representation.SetRenderer(self.renderer)
1653        sw.representation.BuildRepresentation()
1654        sw.Render()
1655        if interactive:
1656            self.interactor.Start()
1657        else:
1658            self.interactor.Render()
1659        return sw
1660
1661    def add_icon(self, icon, pos=3, size=0.08):
1662        """Add an inset icon mesh into the same renderer.
1663
1664        Arguments:
1665            pos : (int, list)
1666                icon position in the range [1-4] indicating one of the 4 corners,
1667                or it can be a tuple (x,y) as a fraction of the renderer size.
1668            size : (float)
1669                size of the square inset.
1670
1671        Examples:
1672            - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py)
1673        """
1674        iconw = addons.Icon(icon, pos, size)
1675
1676        iconw.SetInteractor(self.interactor)
1677        iconw.EnabledOn()
1678        iconw.InteractiveOff()
1679        self.widgets.append(iconw)
1680        return iconw
1681
1682
1683    def add_global_axes(self, axtype=None, c=None):
1684        """Draw axes on scene. Available axes types:
1685
1686        Arguments:
1687            axtype : (int)
1688                - 0,  no axes,
1689                - 1,  draw three gray grid walls
1690                - 2,  show cartesian axes from (0,0,0)
1691                - 3,  show positive range of cartesian axes from (0,0,0)
1692                - 4,  show a triad at bottom left
1693                - 5,  show a cube at bottom left
1694                - 6,  mark the corners of the bounding box
1695                - 7,  draw a 3D ruler at each side of the cartesian axes
1696                - 8,  show the vtkCubeAxesActor object
1697                - 9,  show the bounding box outLine
1698                - 10, show three circles representing the maximum bounding box
1699                - 11, show a large grid on the x-y plane
1700                - 12, show polar axes
1701                - 13, draw a simple ruler at the bottom of the window
1702
1703            Axis type-1 can be fully customized by passing a dictionary axes=dict().
1704
1705        Example:
1706            ```python
1707            from vedo import Box, show
1708            b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1)
1709            show(
1710                b,
1711                axes={
1712                    "xtitle": "Some long variable [a.u.]",
1713                    "number_of_divisions": 4,
1714                    # ...
1715                },
1716            )
1717            ```
1718
1719        Examples:
1720            - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py)
1721            - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py)
1722            - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py)
1723            - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py)
1724
1725            <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600">
1726        """
1727        addons.add_global_axes(axtype, c)
1728        return self
1729
1730    def add_legend_box(self, **kwargs):
1731        """Add a legend to the top right.
1732
1733        Examples:
1734            - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py),
1735            - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py)
1736            - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py)
1737        """
1738        acts = self.get_meshes()
1739        lb = addons.LegendBox(acts, **kwargs)
1740        self.add(lb)
1741        return self
1742
1743    def add_hint(
1744        self,
1745        obj,
1746        text="",
1747        c="k",
1748        bc="yellow8",
1749        font="Calco",
1750        size=18,
1751        justify=0,
1752        angle=0,
1753        delay=100,
1754    ):
1755        """
1756        Create a pop-up hint style message when hovering an object.
1757        Use add_hint(False) to disable all hints.
1758
1759        Arguments:
1760            obj : (Mesh, Points)
1761                the object to associate the pop-up to
1762            text : (str)
1763                string description of the pop-up
1764            delay : (int)
1765                milliseconds to wait before pop-up occurs
1766        """
1767        if self.offscreen:
1768            return self
1769
1770        if vedo.vtk_version[0] == 9 and "Linux" in vedo.sys_platform:  # Linux vtk9 is bugged
1771            vedo.logger.warning("add_hint() is not available on Linux platforms.")
1772            return self
1773
1774        if obj is False:
1775            self.hint_widget.EnabledOff()
1776            self.hint_widget = None
1777            return self
1778
1779        if text is False and self.hint_widget:
1780            self.hint_widget.RemoveBalloon(obj)
1781            return self
1782
1783        if text == "":
1784            if obj.name:
1785                text = obj.name
1786            elif obj.filename:
1787                text = obj.filename
1788            else:
1789                return self
1790
1791        if not self.hint_widget:
1792            self.hint_widget = vtk.vtkBalloonWidget()
1793
1794            rep = vtk.vtkBalloonRepresentation()
1795            rep.SetBalloonLayoutToImageRight()
1796
1797            trep = rep.GetTextProperty()
1798            trep.SetFontFamily(vtk.VTK_FONT_FILE)
1799            trep.SetFontFile(utils.get_font_path(font))
1800            trep.SetFontSize(size)
1801            trep.SetColor(vedo.get_color(c))
1802            trep.SetBackgroundColor(vedo.get_color(bc))
1803            trep.SetShadow(False)
1804            trep.SetJustification(justify)
1805            trep.UseTightBoundingBoxOn()
1806
1807            self.hint_widget.ManagesCursorOff()
1808            self.hint_widget.SetTimerDuration(delay)
1809            self.hint_widget.SetInteractor(self.interactor)
1810            if angle:
1811                rep.SetOrientation(angle)
1812                rep.SetBackgroundOpacity(0)
1813            self.hint_widget.SetRepresentation(rep)
1814            self.widgets.append(self.hint_widget)
1815            if self.interactor.GetInitialized():
1816                self.hint_widget.EnabledOn()
1817            else:
1818                vedo.logger.warning("add_hint() must be called after show(). Skip.")
1819                return self
1820
1821        bst = self.hint_widget.GetBalloonString(obj)
1822        if bst:
1823            self.hint_widget.UpdateBalloonString(obj, text)
1824        else:
1825            self.hint_widget.AddBalloon(obj, text)
1826
1827        return self
1828
1829
1830    def add_shadows(self):
1831        """Add shadows at the current renderer."""
1832        shadows = vtk.vtkShadowMapPass()
1833        seq = vtk.vtkSequencePass()
1834        passes = vtk.vtkRenderPassCollection()
1835        passes.AddItem(shadows.GetShadowMapBakerPass())
1836        passes.AddItem(shadows)
1837        seq.SetPasses(passes)
1838        camerapass = vtk.vtkCameraPass()
1839        camerapass.SetDelegatePass(seq)
1840        self.renderer.SetPass(camerapass)
1841        return self
1842
1843    def add_ambient_occlusion(self, radius, bias=0.01, blur=True, samples=100):
1844        """
1845        Screen Space Ambient Occlusion.
1846
1847        For every pixel on the screen, the pixel shader samples the depth values around
1848        the current pixel and tries to compute the amount of occlusion from each of the sampled
1849        points.
1850
1851        Arguments:
1852            radius : (float)
1853                radius of influence in absolute units
1854            bias : (float)
1855                bias of the normals
1856            blur : (bool)
1857                add a blurring to the sampled positions
1858            samples : (int)
1859                number of samples to probe
1860
1861        Examples:
1862            - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py)
1863
1864            ![](https://vedo.embl.es/images/basic/ssao.jpg)
1865        """
1866        lights = vtk.vtkLightsPass()
1867
1868        opaque = vtk.vtkOpaquePass()
1869
1870        ssaoCam = vtk.vtkCameraPass()
1871        ssaoCam.SetDelegatePass(opaque)
1872
1873        ssao = vtk.vtkSSAOPass()
1874        ssao.SetRadius(radius)
1875        ssao.SetBias(bias)
1876        ssao.SetBlur(blur)
1877        ssao.SetKernelSize(samples)
1878        ssao.SetDelegatePass(ssaoCam)
1879
1880        translucent = vtk.vtkTranslucentPass()
1881
1882        volpass = vtk.vtkVolumetricPass()
1883        ddp = vtk.vtkDualDepthPeelingPass()
1884        ddp.SetTranslucentPass(translucent)
1885        ddp.SetVolumetricPass(volpass)
1886
1887        over = vtk.vtkOverlayPass()
1888
1889        collection = vtk.vtkRenderPassCollection()
1890        collection.AddItem(lights)
1891        collection.AddItem(ssao)
1892        collection.AddItem(ddp)
1893        collection.AddItem(over)
1894
1895        sequence = vtk.vtkSequencePass()
1896        sequence.SetPasses(collection)
1897
1898        cam = vtk.vtkCameraPass()
1899        cam.SetDelegatePass(sequence)
1900
1901        self.renderer.SetPass(cam)
1902        return self
1903
1904    def add_depth_of_field(self, autofocus=True):
1905        """Add a depth of field effect in the scene."""
1906        lights = vtk.vtkLightsPass()
1907
1908        opaque = vtk.vtkOpaquePass()
1909
1910        dofCam = vtk.vtkCameraPass()
1911        dofCam.SetDelegatePass(opaque)
1912
1913        dof = vtk.vtkDepthOfFieldPass()
1914        dof.SetAutomaticFocalDistance(autofocus)
1915        dof.SetDelegatePass(dofCam)
1916
1917        collection = vtk.vtkRenderPassCollection()
1918        collection.AddItem(lights)
1919        collection.AddItem(dof)
1920
1921        sequence = vtk.vtkSequencePass()
1922        sequence.SetPasses(collection)
1923
1924        cam = vtk.vtkCameraPass()
1925        cam.SetDelegatePass(sequence)
1926
1927        self.renderer.SetPass(cam)
1928        return self
1929
1930    def _add_skybox(self, hdrfile):
1931        # many hdr files are at https://polyhaven.com/all
1932
1933        if utils.vtk_version_at_least(9):
1934            reader = vtk.vtkHDRReader()
1935            # Check the image can be read.
1936            if not reader.CanReadFile(hdrfile):
1937                vedo.logger.error(f"Cannot read HDR file {hdrfile}")
1938                return self
1939            reader.SetFileName(hdrfile)
1940            reader.Update()
1941
1942            texture = vtk.vtkTexture()
1943            texture.SetColorModeToDirectScalars()
1944            texture.SetInputData(reader.GetOutput())
1945
1946            # Convert to a cube map
1947            tcm = vtk.vtkEquirectangularToCubeMapTexture()
1948            tcm.SetInputTexture(texture)
1949            # Enable mipmapping to handle HDR image
1950            tcm.MipmapOn()
1951            tcm.InterpolateOn()
1952
1953            self.renderer.SetEnvironmentTexture(tcm)
1954            self.renderer.UseImageBasedLightingOn()
1955            self.skybox = vtk.vtkSkybox()
1956            self.skybox.SetTexture(tcm)
1957            self.renderer.AddActor(self.skybox)
1958
1959        else:
1960            vedo.logger.error("add_skybox not supported in this VTK version. Skip.")
1961
1962        return self
1963
1964    def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None):
1965        """
1966        Add a frame to the renderer subwindow.
1967
1968        Arguments:
1969            c : (color)
1970                color name or index
1971            alpha : (float)
1972                opacity level
1973            lw : (int)
1974                line width in pixels.
1975            padding : (float)
1976                padding space in pixels.
1977        """
1978        if c is None:  # automatic black or white
1979            c = (0.9, 0.9, 0.9)
1980            if np.sum(vedo.plotter_instance.renderer.GetBackground()) > 1.5:
1981                c = (0.1, 0.1, 0.1)
1982        renf = addons.RendererFrame(c, alpha, lw, padding)
1983        self.renderer.AddActor(renf)
1984        return self
1985
1986    def add_hover_legend(
1987        self,
1988        at=None,
1989        c=None,
1990        pos="bottom-left",
1991        font="Calco",
1992        s=0.75,
1993        bg="auto",
1994        alpha=0.1,
1995        maxlength=24,
1996        use_info=False,
1997    ):
1998        """
1999        Add a legend with 2D text which is triggered by hovering the mouse on an object.
2000
2001        The created text object are stored in plotter.hover_legends
2002
2003        Arguments:
2004            c : (color)
2005                Text color. If None then black or white is chosen automatically
2006            pos : (str)
2007                text positioning
2008            font : (str)
2009                text font type. Check [available fonts here](https://vedo.embl.es/fonts).
2010            s : (float)
2011                text size scale
2012            bg : (color)
2013                background color of the 2D box containing the text
2014            alpha : (float)
2015                box transparency
2016            maxlength : (int)
2017                maximum number of characters per line
2018            use_info : (bool)
2019                visualize the content of the `obj.info` attribute
2020
2021        Examples:
2022            - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py)
2023            - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py)
2024
2025            ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg)
2026        """
2027        hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg)
2028
2029        if at is None:
2030            at = self.renderers.index(self.renderer)
2031
2032        def _legfunc(evt):
2033            if not evt.actor or not self.renderer or at != evt.at:
2034                if hoverlegend._mapper.GetInput():  # clear and return
2035                    hoverlegend._mapper.SetInput("")
2036                    self.interactor.Render()
2037                return
2038
2039            if use_info:
2040                if hasattr(evt.actor, "info"):
2041                    t = str(evt.actor.info)
2042                else:
2043                    return
2044            else:
2045                t, tp = "", ""
2046                if evt.isMesh:
2047                    tp = "Mesh "
2048                elif evt.isPoints:
2049                    tp = "Points "
2050                # elif evt.isVolume:
2051                #     tp = "Volume "
2052                elif evt.isPicture:
2053                    tp = "Pict "
2054                elif evt.isAssembly:
2055                    tp = "Assembly "
2056                else:
2057                    return
2058
2059                if evt.isAssembly:
2060                    if not evt.actor.name:
2061                        t += f"Assembly object of {len(evt.actor.unpack())} parts\n"
2062                    else:
2063                        t += f"Assembly name: {evt.actor.name} ({len(evt.actor.unpack())} parts)\n"
2064                else:
2065                    if evt.actor.name:
2066                        t += f"{tp}name"
2067                        if evt.isPoints:
2068                            t += "  "
2069                        if evt.isMesh:
2070                            t += "  "
2071                        t += f": {evt.actor.name[:maxlength]}".ljust(maxlength) + "\n"
2072
2073                if evt.actor.filename:
2074                    t += f"{tp}filename: "
2075                    t += f"{os.path.basename(evt.actor.filename[-maxlength:])}".ljust(maxlength)
2076                    t += "\n"
2077                    if not evt.actor.file_size:
2078                        evt.actor.file_size, evt.actor.created = vedo.file_io.file_info(evt.actor.filename)
2079                    if evt.actor.file_size:
2080                        t += "             : "
2081                        sz, created = evt.actor.file_size, evt.actor.created
2082                        t += f"{created[4:-5]} ({sz})" + "\n"
2083
2084                if evt.isPoints:
2085                    indata = evt.actor.polydata(False)
2086                    if indata.GetNumberOfPoints():
2087                        t += (
2088                            f"#points/cells: {indata.GetNumberOfPoints()}"
2089                            f" / {indata.GetNumberOfCells()}"
2090                        )
2091                    pdata = indata.GetPointData()
2092                    cdata = indata.GetCellData()
2093                    if pdata.GetScalars() and pdata.GetScalars().GetName():
2094                        t += f"\nPoint array  : {pdata.GetScalars().GetName()}"
2095                        if pdata.GetScalars().GetName() == evt.actor.mapper().GetArrayName():
2096                            t += " *"
2097                    if cdata.GetScalars() and cdata.GetScalars().GetName():
2098                        t += f"\nCell  array  : {cdata.GetScalars().GetName()}"
2099                        if cdata.GetScalars().GetName() == evt.actor.mapper().GetArrayName():
2100                            t += " *"
2101
2102                if evt.isPicture:
2103                    t = f"{os.path.basename(evt.actor.filename[:maxlength+10])}".ljust(maxlength+10)
2104                    t += f"\nImage shape: {evt.actor.shape}"
2105                    pcol = self.color_picker(evt.picked2d)
2106                    t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}"
2107
2108            # change box color if needed in 'auto' mode
2109            if evt.isPoints and "auto" in str(bg):
2110                actcol = evt.actor.GetProperty().GetColor()
2111                if hoverlegend._mapper.GetTextProperty().GetBackgroundColor() != actcol:
2112                    hoverlegend._mapper.GetTextProperty().SetBackgroundColor(actcol)
2113
2114            # adapt to changes in bg color
2115            bgcol = self.renderers[at].GetBackground()
2116            _bgcol = c
2117            if _bgcol is None:  # automatic black or white
2118                _bgcol = (0.9, 0.9, 0.9)
2119                if sum(bgcol) > 1.5:
2120                    _bgcol = (0.1, 0.1, 0.1)
2121                if len(set(_bgcol).intersection(bgcol)) < 3:
2122                    hoverlegend.color(_bgcol)
2123
2124            if hoverlegend._mapper.GetInput() != t:
2125                hoverlegend._mapper.SetInput(t)
2126                self.interactor.Render()
2127
2128        self.add(hoverlegend, at=at)
2129        self.hover_legends.append(hoverlegend)
2130        self.add_callback("MouseMove", _legfunc)
2131        return self
2132
2133
2134    #####################################################################
2135    def add_scale_indicator(
2136        self, pos=(0.7, 0.05), s=0.02, length=2, lw=4, c="k1", alpha=1, units="", gap=0.05
2137    ):
2138        """
2139        Add a Scale Indicator. Only works in parallel mode (no perspective).
2140
2141        Arguments:
2142            pos : (list)
2143                fractional (x,y) position on the screen.
2144            s : (float)
2145                size of the text.
2146            length : (float)
2147                length of the line.
2148            units : (str)
2149                string to show units.
2150            gap : (float)
2151                separation of line and text.
2152
2153        Example:
2154            ```python
2155            from vedo import settings, Cube, Plotter
2156            settings.use_parallel_projection = True # or else it does not make sense!
2157            cube = Cube().alpha(0.2)
2158            plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)'))
2159            plt.add_scale_indicator(units='um', c='blue4')
2160            plt.show(cube, "Scale indicator with units").close()
2161            ```
2162            ![](https://vedo.embl.es/images/feats/scale_indicator.png)
2163        """
2164        ppoints = vtk.vtkPoints()  # Generate the polyline
2165        psqr = [[0.0, gap], [length / 10, gap]]
2166        dd = psqr[1][0] - psqr[0][0]
2167        for i, pt in enumerate(psqr):
2168            ppoints.InsertPoint(i, pt[0], pt[1], 0)
2169        lines = vtk.vtkCellArray()
2170        lines.InsertNextCell(len(psqr))
2171        for i in range(len(psqr)):
2172            lines.InsertCellPoint(i)
2173        pd = vtk.vtkPolyData()
2174        pd.SetPoints(ppoints)
2175        pd.SetLines(lines)
2176
2177        wsx, wsy = self.window.GetSize()
2178        if not settings.use_parallel_projection:
2179            vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.")
2180            return None
2181
2182        rlabel = vtk.vtkVectorText()
2183        rlabel.SetText("scale")
2184        tf = vtk.vtkTransformPolyDataFilter()
2185        tf.SetInputConnection(rlabel.GetOutputPort())
2186        t = vtk.vtkTransform()
2187        t.Scale(s * wsy / wsx, s, 1)
2188        tf.SetTransform(t)
2189
2190        app = vtk.vtkAppendPolyData()
2191        app.AddInputConnection(tf.GetOutputPort())
2192        app.AddInputData(pd)
2193
2194        mapper = vtk.vtkPolyDataMapper2D()
2195        mapper.SetInputConnection(app.GetOutputPort())
2196        cs = vtk.vtkCoordinate()
2197        cs.SetCoordinateSystem(1)
2198        mapper.SetTransformCoordinate(cs)
2199
2200        fractor = vtk.vtkActor2D()
2201        csys = fractor.GetPositionCoordinate()
2202        csys.SetCoordinateSystem(3)
2203        fractor.SetPosition(pos)
2204        fractor.SetMapper(mapper)
2205        fractor.GetProperty().SetColor(vedo.get_color(c))
2206        fractor.GetProperty().SetOpacity(alpha)
2207        fractor.GetProperty().SetLineWidth(lw)
2208        fractor.GetProperty().SetDisplayLocationToForeground()
2209
2210        def sifunc(iren, ev):
2211            wsx, wsy = self.window.GetSize()
2212            ps = self.camera.GetParallelScale()
2213            newtxt = utils.precision(ps / wsy * wsx * length * dd, 3)
2214            if units:
2215                newtxt += " " + units
2216            if rlabel.GetText() != newtxt:
2217                rlabel.SetText(newtxt)
2218
2219        self.renderer.AddActor(fractor)
2220        self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc)
2221        self.interactor.AddObserver("MouseWheelForwardEvent", sifunc)
2222        self.interactor.AddObserver("InteractionEvent", sifunc)
2223        sifunc(0, 0)
2224        return fractor
2225
2226    def fill_event(self, ename="", pos=()):
2227        """
2228        Create an Event object.
2229
2230        A 2D screen-position can be provided to be picked.
2231        """
2232        if not self.interactor:
2233            return Event()
2234
2235        if len(pos):
2236            x, y = pos
2237            self.interactor.SetEventPosition(pos)
2238        else:
2239            x, y = self.interactor.GetEventPosition()
2240        self.renderer = self.interactor.FindPokedRenderer(x, y)
2241        if not self.picker:
2242            self.picker = vtk.vtkPropPicker()
2243        self.picked2d = (x, y)
2244        self.picker.PickProp(x, y, self.renderer)
2245        xp, yp = self.interactor.GetLastEventPosition()
2246        actor = self.picker.GetProp3D()
2247        delta3d = np.array([0, 0, 0])
2248        if actor:
2249            picked3d = np.array(self.picker.GetPickPosition())
2250            if isinstance(actor, vedo.base.Base3DProp):  # needed!
2251                if actor.picked3d is not None:
2252                    delta3d = picked3d - actor.picked3d
2253            actor.picked3d = picked3d
2254        else:
2255            picked3d = None
2256
2257        if not actor:  # try 2D
2258            actor = self.picker.GetActor2D()
2259
2260        dx, dy = x - xp, y - yp
2261
2262        key = self.interactor.GetKeySym()
2263
2264        if key:
2265            if "_L" in key or "_R" in key:
2266                # skip things like Shift_R
2267                key = ""  # better than None
2268            else:
2269                if self.interactor.GetShiftKey():
2270                    key = key.upper()
2271
2272                if key == "MINUS":  # fix: vtk9 is ignoring shift chars..
2273                    key = "underscore"
2274                elif key == "EQUAL":  # fix: vtk9 is ignoring shift chars..
2275                    key = "plus"
2276                elif key == "SLASH":  # fix: vtk9 is ignoring shift chars..
2277                    key = "?"
2278
2279                if self.interactor.GetControlKey():
2280                    key = "Ctrl+" + key
2281
2282                if self.interactor.GetAltKey():
2283                    key = "Alt+" + key
2284
2285        event = Event()
2286        event.name = ename
2287        event.title = self.title
2288        event.id = -1  # will be set by the timer wrapper function
2289        event.timerid = -1  # will be set by the timer wrapper function
2290        event.priority = -1  # will be set by the timer wrapper function
2291        event.time = time.time()
2292        event.at = self.renderers.index(self.renderer)
2293        event.actor = actor
2294        event.picked3d = picked3d
2295        event.keyPressed = key  # obsolete, will disappear. Use "keypress"
2296        event.keypress = key
2297        event.picked2d = (x, y)
2298        event.delta2d = (dx, dy)
2299        event.angle2d = np.arctan2(dy, dx)
2300        event.speed2d = np.sqrt(dx * dx + dy * dy)
2301        event.delta3d = delta3d
2302        event.speed3d = np.sqrt(np.dot(delta3d, delta3d))
2303        event.isPoints = isinstance(actor, vedo.Points)
2304        event.isMesh = isinstance(actor, vedo.Mesh)
2305        event.isAssembly = isinstance(actor, vedo.Assembly)
2306        event.isVolume = isinstance(actor, vedo.Volume)
2307        event.isPicture = isinstance(actor, vedo.Picture)
2308        event.isActor2D = isinstance(actor, vtk.vtkActor2D)
2309        return event
2310
2311
2312    def add_callback(self, event_name, func, priority=0.0):
2313        """
2314        Add a function to be executed while show() is active.
2315        Information about the event can be acquired with method getEvent().
2316
2317        Return a unique id for the callback.
2318
2319        The callback function (see example below) exposes a dictionary
2320        with the following information:
2321        - `name`: event name,
2322        - `id`: event unique identifier,
2323        - `priority`: event priority (float),
2324        - `interactor`: the interactor object,
2325        - `at`: renderer nr. where the event occurred
2326        - `actor`: object picked by the mouse
2327        - `picked3d`: point picked in world coordinates
2328        - `keypress`: key pressed as string
2329        - `picked2d`: screen coords of the mouse pointer
2330        - `delta2d`: shift wrt previous position (to calculate speed, direction)
2331        - `delta3d`: ...same but in 3D world coords
2332        - `angle2d`: angle of mouse movement on screen
2333        - `speed2d`: speed of mouse movement on screen
2334        - `speed3d`: speed of picked point in world coordinates
2335        - `isPoints`: True if of class
2336        - `isMesh`: True if of class
2337        - `isAssembly`: True if of class
2338        - `isVolume`: True if of class Volume
2339        - `isPicture`: True if of class
2340
2341        Frequently used events are:
2342        - `KeyPress`, `KeyRelease`: listen to keyboard events
2343        - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks
2344        - `MiddleButtonPress`, `MiddleButtonRelease`
2345        - `RightButtonPress`, `RightButtonRelease`
2346        - `MouseMove`: listen to mouse pointer changing position
2347        - `MouseWheelForward`, `MouseWheelBackward`
2348        - `Enter`, `Leave`: listen to mouse entering or leaving the window
2349        - `Pick`, `StartPick`, `EndPick`: listen to object picking
2350        - `ResetCamera`, `ResetCameraClippingRange`
2351        - `Error`, `Warning`
2352        - `Char`
2353        - `Timer`
2354
2355        Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html).
2356
2357        Example:
2358            ```python
2359            from vedo import *
2360
2361            def func(evt):
2362                # this function is called every time the mouse moves
2363                # (evt is a dotted dictionary)
2364                if not evt.actor:
2365                    return  # no hit, return
2366                print("point coords =", evt.picked3d)
2367                # print("full event dump:", evt)
2368
2369            elli = Ellipsoid()
2370            plt = Plotter(axes=1)
2371            plt.add_callback('mouse hovering', func)
2372            plt.show(elli).close()
2373            ```
2374
2375        Examples:
2376            - [spline_draw.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw.py)
2377            - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py)
2378
2379                ![](https://vedo.embl.es/images/advanced/spline_draw.png)
2380
2381            - ..and many others!
2382        """
2383        from vtkmodules.util.misc import calldata_type
2384
2385        if not self.interactor:
2386            return None
2387
2388        # as vtk names are ugly and difficult to remember:
2389        ln = event_name.lower()
2390        if "click" in ln or "button" in ln:
2391            event_name = "LeftButtonPress"
2392            if "right" in ln:
2393                event_name = "RightButtonPress"
2394            elif "mid" in ln:
2395                event_name = "MiddleButtonPress"
2396            if "release" in ln:
2397                # event_name = event_name.replace("Press","Release") # vtk bug
2398                event_name = "EndInteraction"
2399        else:
2400            if "key" in ln:
2401                if "release" in ln:
2402                    event_name = "KeyRelease"
2403                else:
2404                    event_name = "KeyPress"
2405
2406        if ("mouse" in ln and "mov" in ln) or "over" in ln:
2407            event_name = "MouseMove"
2408        if "timer" in ln:
2409            event_name = "Timer"
2410
2411        if not event_name.endswith("Event"):
2412            event_name += "Event"
2413
2414        @calldata_type(vtk.VTK_INT)
2415        def _func_wrap(iren, ename, timerid=None):
2416            event = self.fill_event(ename=ename)
2417            event.timerid = timerid
2418            event.id = cid
2419            event.priority = priority
2420            self.last_event = event
2421            func(event)
2422            return  ## _func_wrap
2423
2424        # Not compatible with ProcessEvents()
2425        if "MouseMove" in event_name or "Timer" in event_name:
2426            settings.allow_interaction = False
2427
2428        cid = self.interactor.AddObserver(event_name, _func_wrap, priority)
2429        vedo.logger.debug(f"registering event: {event_name} with id={cid}")
2430        return cid
2431
2432    def remove_callback(self, cid):
2433        """
2434        Remove a callback function by its id
2435        or a whole category of callbacks by their name.
2436
2437        Arguments:
2438            cid : (int, str)
2439                Unique id of the callback.
2440                If an event name is passed all callbacks of that type are removed.
2441        """
2442        if self.interactor:
2443            if isinstance(cid, str):
2444                # as vtk names are ugly and difficult to remember:
2445                ln = cid.lower()
2446                if "click" in ln or "button" in ln:
2447                    cid = "LeftButtonPress"
2448                    if "right" in ln:
2449                        cid = "RightButtonPress"
2450                    elif "mid" in ln:
2451                        cid = "MiddleButtonPress"
2452                    if "release" in ln:
2453                        cid.replace("Press", "Release")
2454                else:
2455                    if "key" in ln:
2456                        if "release" in ln:
2457                            cid = "KeyRelease"
2458                        else:
2459                            cid = "KeyPress"
2460                if ("mouse" in ln and "mov" in ln) or "over" in ln:
2461                    cid = "MouseMove"
2462                if "timer" in ln:
2463                    cid = "Timer"
2464                if not cid.endswith("Event"):
2465                    cid += "Event"
2466                self.interactor.RemoveObservers(cid)
2467            else:
2468                self.interactor.RemoveObserver(cid)
2469        return self
2470
2471    def timer_callback(self, action, timer_id=None, dt=1, one_shot=False):
2472        """
2473        Start or stop an existing timer.
2474
2475        Arguments:
2476            action : (str)
2477                Either "create"/"start" or "destroy"/"stop"
2478            timer_id : (int)
2479                When stopping the timer, the ID of the timer as returned when created
2480            dt : (int)
2481                time in milliseconds between each repeated call
2482            one_shot : (bool)
2483                create a one shot timer of prescribed duration instead of a repeating one
2484
2485        Examples:
2486            - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py)
2487            - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py)
2488
2489            ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg)
2490        """
2491        if action in ("create", "start"):
2492            if timer_id is not None:
2493                vedo.logger.warning("you set a timer_id but it will be ignored.")
2494            if one_shot:
2495                timer_id = self.interactor.CreateOneShotTimer(dt)
2496            else:
2497                timer_id = self.interactor.CreateRepeatingTimer(dt)
2498            return timer_id
2499
2500        elif action in ("destroy", "stop"):
2501            if timer_id is not None:
2502                self.interactor.DestroyTimer(timer_id)
2503            else:
2504                vedo.logger.warning("please set a timer_id. Cannot stop timer.")
2505        else:
2506            e = f"in timer_callback(). Cannot understand action: {action}\n"
2507            e += " allowed actions are: ['start', 'stop']. Skipped."
2508            vedo.logger.error(e)
2509        return timer_id
2510
2511    def compute_world_coordinate(
2512        self, pos2d, at=None, objs=(), bounds=(), offset=None, pixeltol=None, worldtol=None
2513    ):
2514        """
2515        Transform a 2D point on the screen into a 3D point inside the rendering scene.
2516        If a set of meshes is passed then points are placed onto these.
2517
2518        Arguments:
2519            pos2d : (list)
2520                2D screen coordinates point.
2521            at : (int)
2522                renderer number.
2523            objs : (list)
2524                list of Mesh objects to project the point onto.
2525            bounds : (list)
2526                specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax].
2527            offset : (float)
2528                specify an offset value.
2529            pixeltol : (int)
2530                screen tolerance in pixels.
2531            worldtol : (float)
2532                world coordinates tolerance.
2533
2534        Returns:
2535            numpy array, the point in 3D world coordinates.
2536
2537        Examples:
2538            - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py)
2539            - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py)
2540
2541            ![](https://vedo.embl.es/images/basic/mousehover3.jpg)
2542        """
2543        if at is not None:
2544            renderer = self.renderers[at]
2545        else:
2546            renderer = self.renderer
2547
2548        if not objs:
2549            pp = vtk.vtkFocalPlanePointPlacer()
2550        else:
2551            pp = vtk.vtkPolygonalSurfacePointPlacer()
2552            for ob in objs:
2553                pp.AddProp(ob)
2554
2555        if len(bounds) == 6:
2556            pp.SetPointBounds(bounds)
2557        if pixeltol:
2558            pp.SetPixelTolerance(pixeltol)
2559        if worldtol:
2560            pp.SetWorldTolerance(worldtol)
2561        if offset:
2562            pp.SetOffset(offset)
2563
2564        worldPos = [0, 0, 0]
2565        worldOrient = [0, 0, 0, 0, 0, 0, 0, 0, 0]
2566        pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient)
2567        # validw = pp.ValidateWorldPosition(worldPos, worldOrient)
2568        # validd = pp.ValidateDisplayPosition(renderer, pos2d)
2569        return np.array(worldPos)
2570
2571    def compute_screen_coordinates(self, obj, full_window=False):
2572        """
2573        Given a 3D points in the current renderer (or full window),
2574        find the screen pixel coordinates.
2575
2576        Example:
2577            ```python
2578            from vedo import *
2579
2580            elli = Ellipsoid().rotate_y(30)
2581
2582            plt = Plotter()
2583            plt.show(elli)
2584
2585            xyscreen = plt.compute_screen_positions(elli)
2586            print('xyscreen coords:', xyscreen)
2587
2588            # simulate an event happening at one point
2589            event = plt.fill_event(pos=xyscreen[123])
2590            print(event)
2591            ```
2592        """
2593        if isinstance(obj, vedo.base.Base3DProp):
2594            pts = obj.points()
2595        elif utils.is_sequence(obj):
2596            pts = obj
2597        p2d = []
2598        cs = vtk.vtkCoordinate()
2599        cs.SetCoordinateSystemToWorld()
2600        cs.SetViewport(self.renderer)
2601        for p in pts:
2602            cs.SetValue(p)
2603            if full_window:
2604                p2d.append(cs.GetComputedDisplayValue(self.renderer))
2605            else:
2606                p2d.append(cs.GetComputedViewportValue(self.renderer))
2607        return np.array(p2d, dtype=int)
2608    
2609    def pick_area(self, pos1, pos2, at=None):
2610        """
2611        Pick all objects within a box defined by two corner points in 2D screen coordinates.
2612        
2613        Returns a frustum Mesh that contains the visible field of view.
2614        This can be used to select objects in a scene or select vertices.
2615
2616        Example:
2617            ```python
2618            from vedo import *
2619
2620            settings.enable_default_mouse_callbacks = False
2621
2622            def mode_select(objs):
2623                print("Selected objects:", objs)
2624                d0 = mode.start_x, mode.start_y # display coords
2625                d1 = mode.end_x, mode.end_y
2626
2627                frustum = plt.pick_area(d0, d1)
2628                infru = frustum.inside_points(mesh)
2629                col = np.random.randint(0, 10)
2630                infru.ps(10).c(col)
2631                plt.add(frustum, infru).render()
2632
2633            mesh = Mesh(dataurl+"cow.vtk").c("k5").lw(1)
2634
2635            mode = interactor_modes.BlenderStyle()
2636            mode.callback_select = mode_select
2637
2638            plt = Plotter().user_mode(mode)
2639            plt.show(mesh, axes=1)
2640            ```
2641        """
2642        if at is not None:
2643            ren = self.renderers[at]
2644        else:
2645            ren = self.renderer
2646        area_picker = vtk.vtkAreaPicker()
2647        area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren)
2648        planes = area_picker.GetFrustum()
2649
2650        fru = vtk.vtkFrustumSource()
2651        fru.SetPlanes(planes)
2652        fru.ShowLinesOff()
2653        fru.Update()
2654
2655        afru = vedo.Mesh(fru.GetOutput())
2656        afru.alpha(0.1).lw(1).pickable(False)
2657        afru.name = "Frustrum"
2658        return afru
2659
2660
2661    def _scan_input(self, wannabeacts):
2662        # scan the input of show
2663        if not utils.is_sequence(wannabeacts):
2664            wannabeacts = [wannabeacts]
2665
2666        scannedacts = []
2667        for a in wannabeacts:  # scan content of list
2668
2669            if a is None:
2670                pass
2671
2672            elif isinstance(a, vtk.vtkActor):
2673
2674                scannedacts.append(a)
2675
2676                if isinstance(a, vedo.base.BaseActor):
2677                    if a.shadows:
2678                        # a.update_shadows()
2679                        scannedacts.extend(a.shadows)
2680
2681                    if a.trail and a.trail not in self.actors:
2682                        # a.update_trail()
2683                        scannedacts.append(a.trail)
2684                        # trails may also have shadows:
2685                        if a.trail.shadows:
2686                            # a.trail.update_shadows()
2687                            scannedacts.extend(a.trail.shadows)
2688
2689                    if a._caption and a._caption not in self.actors:
2690                        scannedacts.append(a._caption)
2691
2692            elif isinstance(a, vtk.vtkActor2D):
2693                scannedacts.append(a)
2694
2695            elif isinstance(a, vtk.vtkAssembly):
2696                scannedacts.append(a)
2697                if a.trail and a.trail not in self.actors:
2698                    scannedacts.append(a.trail)
2699
2700            elif isinstance(a, (vedo.Volume, vedo.VolumeSlice)):
2701                scannedacts.append(a)
2702
2703            elif isinstance(a, vtk.vtkImageData):
2704                scannedacts.append(vedo.Volume(a))
2705
2706            elif isinstance(a, vedo.TetMesh):
2707                # check ugrid is all made of tets
2708                ugrid = a.inputdata()
2709                uarr = ugrid.GetCellTypesArray()
2710                celltypes = np.unique(utils.vtk2numpy(uarr))
2711                ncelltypes = len(celltypes)
2712                if ncelltypes > 1 or (ncelltypes == 1 and celltypes[0] != 10):
2713                    scannedacts.append(a.tomesh())
2714                else:
2715                    if not ugrid.GetPointData().GetScalars():
2716                        if not ugrid.GetCellData().GetScalars():
2717                            # add dummy array for vtkProjectedTetrahedraMapper to work:
2718                            a.celldata["DummyOneArray"] = np.ones(a.ncells)
2719                    scannedacts.append(a)
2720
2721            elif isinstance(a, vedo.UGrid):
2722                scannedacts.append(a.tomesh())
2723
2724            elif isinstance(a, vtk.vtkVolume):  # order matters! dont move above TetMesh
2725                vvol = vedo.Volume(a.GetMapper().GetInput())
2726                vprop = vtk.vtkVolumeProperty()
2727                vprop.DeepCopy(a.GetProperty())
2728                vvol.SetProperty(vprop)
2729                scannedacts.append(vvol)
2730
2731            elif isinstance(a, str):
2732                # assume a 2D comment was given
2733                changed = False  # check if one already exists so to just update text
2734                if self.renderer:  # might be jupyter
2735                    acs = self.renderer.GetActors2D()
2736                    acs.InitTraversal()
2737                    for i in range(acs.GetNumberOfItems()):
2738                        act = acs.GetNextItem()
2739                        if isinstance(act, vedo.shapes.Text2D):
2740                            aposx, aposy = act.GetPosition()
2741                            if aposx < 0.01 and aposy > 0.99:  # "top-left"
2742                                act.text(a)  # update content! no appending nada
2743                                changed = True
2744                                break
2745                    if not changed:
2746                        out = vedo.shapes.Text2D(a)  # append a new one
2747                        scannedacts.append(out)
2748
2749            elif isinstance(a, vtk.vtkImageActor):
2750                scannedacts.append(a)
2751
2752            elif isinstance(a, vtk.vtkBillboardTextActor3D):
2753                scannedacts.append(a)
2754
2755            elif isinstance(a, vtk.vtkLight):
2756                self.renderer.AddLight(a)
2757
2758            elif isinstance(a, vtk.vtkMultiBlockDataSet):
2759                for i in range(a.GetNumberOfBlocks()):
2760                    b = a.GetBlock(i)
2761                    if isinstance(b, vtk.vtkPolyData):
2762                        scannedacts.append(vedo.Mesh(b))
2763                    elif isinstance(b, vtk.vtkImageData):
2764                        scannedacts.append(vedo.Volume(b))
2765
2766            elif "PolyData" in str(type(a)):  # assume pyvista or vtkPolydata
2767                scannedacts.append(vedo.Mesh(a))
2768
2769            elif "dolfin" in str(type(a)):  # assume a dolfin.Mesh object
2770                import vedo.dolfin as dlf
2771                scannedacts.append(dlf.MeshActor(a))
2772
2773            elif "trimesh" in str(type(a)):
2774                scannedacts.append(utils.trimesh2vedo(a))
2775
2776            elif "meshlab" in str(type(a)):
2777                if "MeshSet" in str(type(a)):
2778                    for i in range(a.number_meshes()):
2779                        if a.mesh_id_exists(i):
2780                            scannedacts.append(vedo.Mesh(utils.meshlab2vedo(a.mesh(i))))
2781                else:
2782                    scannedacts.append(vedo.Mesh(utils.meshlab2vedo(a)))
2783
2784            elif isinstance(a, (vtk.vtkProp, vtk.vtkInteractorObserver)):
2785                scannedacts.append(a)
2786
2787            else:
2788                vedo.logger.error(f"cannot understand input in show(): {type(a)}")
2789        return scannedacts
2790
2791
2792    def show(
2793        self,
2794        *actors,
2795        at=None,
2796        axes=None,
2797        resetcam=None,
2798        zoom=False,
2799        interactive=None,
2800        viewup="",
2801        azimuth=0.0,
2802        elevation=0.0,
2803        roll=0.0,
2804        camera=None,
2805        mode=0,
2806        rate=None,
2807        bg=None,
2808        bg2=None,
2809        size=None,
2810        title=None,
2811    ):
2812        """
2813        Render a list of objects.
2814
2815        Arguments:
2816            at : (int)
2817                number of the renderer to plot to, in case of more than one exists
2818
2819            axes : (int)
2820                axis type-1 can be fully customized by passing a dictionary.
2821                Check `addons.Axes()` for the full list of options.
2822                set the type of axes to be shown:
2823                - 0,  no axes
2824                - 1,  draw three gray grid walls
2825                - 2,  show cartesian axes from (0,0,0)
2826                - 3,  show positive range of cartesian axes from (0,0,0)
2827                - 4,  show a triad at bottom left
2828                - 5,  show a cube at bottom left
2829                - 6,  mark the corners of the bounding box
2830                - 7,  draw a 3D ruler at each side of the cartesian axes
2831                - 8,  show the `vtkCubeAxesActor` object
2832                - 9,  show the bounding box outLine
2833                - 10, show three circles representing the maximum bounding box
2834                - 11, show a large grid on the x-y plane
2835                - 12, show polar axes
2836                - 13, draw a simple ruler at the bottom of the window
2837
2838            azimuth/elevation/roll : (float)
2839                move camera accordingly the specified value
2840
2841            viewup: str, list
2842                either `['x', 'y', 'z']` or a vector to set vertical direction
2843
2844            resetcam : (bool)
2845                re-adjust camera position to fit objects
2846
2847            camera : (dict, vtkCamera)
2848                camera parameters can further be specified with a dictionary
2849                assigned to the ``camera`` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`):
2850                - pos, `(list)`,  the position of the camera in world coordinates
2851                - focal_point `(list)`, the focal point of the camera in world coordinates
2852                - viewup `(list)`, the view up direction for the camera
2853                - distance `(float)`, set the focal point to the specified distance from the camera position.
2854                - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection.
2855                - parallel_scale `(float)`,
2856                scaling used for a parallel projection, i.e. the height of the viewport
2857                in world-coordinate distances. The default is 1. Note that the "scale" parameter works as
2858                an "inverse scale", larger numbers produce smaller images.
2859                This method has no effect in perspective projection mode.
2860
2861                - thickness `(float)`,
2862                set the distance between clipping planes. This method adjusts the far clipping
2863                plane to be set a distance 'thickness' beyond the near clipping plane.
2864
2865                - view_angle `(float)`,
2866                the camera view angle, which is the angular height of the camera view
2867                measured in degrees. The default angle is 30 degrees.
2868                This method has no effect in parallel projection mode.
2869                The formula for setting the angle up for perfect perspective viewing is:
2870                angle = 2*atan((h/2)/d) where h is the height of the RenderWindow
2871                (measured by holding a ruler up to your screen) and d is the distance
2872                from your eyes to the screen.
2873
2874            interactive : (bool)
2875                pause and interact with window (True) or continue execution (False)
2876
2877            rate : (float)
2878                maximum rate of `show()` in Hertz
2879
2880            mode : (int, str)
2881                set the type of interaction:
2882                - 0 = TrackballCamera [default]
2883                - 1 = TrackballActor
2884                - 2 = JoystickCamera
2885                - 3 = JoystickActor
2886                - 4 = Flight
2887                - 5 = RubberBand2D
2888                - 6 = RubberBand3D
2889                - 7 = RubberBandZoom
2890                - 8 = Terrain
2891                - 9 = Unicam
2892                - 10 = Image
2893                - Check out `vedo.interaction_modes` for more options.
2894        """
2895        if self.wx_widget:
2896            return self
2897
2898        if self.renderers:  # in case of notebooks
2899
2900            if at is None:
2901                at = self.renderers.index(self.renderer)
2902
2903            else:
2904
2905                if at >= len(self.renderers):
2906                    t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist"
2907                    vedo.logger.error(t)
2908                    return self
2909
2910                self.renderer = self.renderers[at]
2911
2912        if title is not None:
2913            self.title = title
2914
2915        if size is not None:
2916            self.size = size
2917            if self.size[0] == "f":  # full screen
2918                self.size = "fullscreen"
2919                self.window.SetFullScreen(True)
2920                self.window.BordersOn()
2921            else:
2922                self.window.SetSize(int(self.size[0]), int(self.size[1]))
2923
2924        if settings.default_backend == "vtk":
2925            if str(bg).endswith(".hdr"):
2926                self._add_skybox(bg)
2927            else:
2928                if bg is not None:
2929                    self.backgrcol = vedo.get_color(bg)
2930                    self.renderer.SetBackground(self.backgrcol)
2931                if bg2 is not None:
2932                    self.renderer.GradientBackgroundOn()
2933                    self.renderer.SetBackground2(vedo.get_color(bg2))
2934
2935        if axes is not None:
2936            if isinstance(axes, vedo.Assembly):  # user passing show(..., axes=myaxes)
2937                actors = list(actors)
2938                actors.append(axes)  # move it into the list of normal things to show
2939                axes = 0
2940            self.axes = axes
2941
2942        if self.offscreen:
2943            interactive = False
2944            self._interactive = False
2945
2946        # camera stuff
2947        if resetcam is not None:
2948            self.resetcam = resetcam
2949
2950        if camera is not None:
2951            self.resetcam = False
2952            if isinstance(camera, vtk.vtkCamera):
2953                self.camera = camera
2954            else:
2955                self.camera = utils.camera_from_dict(camera)
2956            if self.renderer:
2957                self.renderer.SetActiveCamera(self.camera)
2958
2959        if self.renderer:
2960            self.camera = self.renderer.GetActiveCamera()
2961
2962        self.add(actors)
2963
2964        # Backend ###############################################################
2965        if settings.default_backend != "vtk":
2966            if settings.default_backend in ["k3d"]:
2967                return backends.get_notebook_backend(self.actors)
2968        #########################################################################
2969
2970        for ia in utils.flatten(actors):
2971            if isinstance(ia, vedo.base.Base3DProp):
2972                try:
2973                    # fix gray color labels and title to white or black
2974                    ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor())
2975                    if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05:
2976                        c = (0.9, 0.9, 0.9)
2977                        if np.sum(self.renderer.GetBackground()) > 1.5:
2978                            c = (0.1, 0.1, 0.1)
2979                        ia.scalarbar.GetLabelTextProperty().SetColor(c)
2980                        ia.scalarbar.GetTitleTextProperty().SetColor(c)
2981                except AttributeError:
2982                    pass
2983
2984        if self.sharecam:
2985            for r in self.renderers:
2986                r.SetActiveCamera(self.camera)
2987
2988        if self.qt_widget is not None:
2989            self.qt_widget.GetRenderWindow().AddRenderer(self.renderer)
2990
2991
2992        if self.axes is not None:
2993            if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict):
2994                bns = self.renderer.ComputeVisiblePropBounds()
2995                addons.add_global_axes(self.axes, bounds=bns)
2996
2997        # Backend ###############################################################
2998        if settings.default_backend in ["ipyvtk", "trame"]:
2999            return backends.get_notebook_backend()
3000        #########################################################################
3001
3002        if self.resetcam:
3003            self.renderer.ResetCamera()
3004
3005        if len(self.renderers) > 1:
3006            self.add_renderer_frame()
3007
3008        if settings.default_backend == "2d" and not zoom:
3009            zoom = "tightest"
3010
3011        if zoom:
3012            if zoom == "tight":
3013                self.reset_camera(tight=0.04)
3014            elif zoom == "tightest":
3015                self.reset_camera(tight=0.0001)
3016            else:
3017                self.camera.Zoom(zoom)
3018        if elevation:
3019            self.camera.Elevation(elevation)
3020        if azimuth:
3021            self.camera.Azimuth(azimuth)
3022        if roll:
3023            self.camera.Roll(roll)
3024
3025        if len(viewup) > 0:
3026            b = self.renderer.ComputeVisiblePropBounds()
3027            cm = np.array([(b[1] + b[0]) / 2, (b[3] + b[2]) / 2, (b[5] + b[4]) / 2])
3028            sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])])
3029            if viewup == "x":
3030                sz = np.linalg.norm(sz)
3031                self.camera.SetViewUp([1, 0, 0])
3032                self.camera.SetPosition(cm + sz)
3033            elif viewup == "y":
3034                sz = np.linalg.norm(sz)
3035                self.camera.SetViewUp([0, 1, 0])
3036                self.camera.SetPosition(cm + sz)
3037            elif viewup == "z":
3038                sz = np.array([(b[1] - b[0]) * 0.7, -(b[3] - b[2]) * 1.0, (b[5] - b[4]) * 1.2])
3039                self.camera.SetViewUp([0, 0, 1])
3040                self.camera.SetPosition(cm + 2 * sz)
3041            elif utils.is_sequence(viewup):
3042                sz = np.linalg.norm(sz)
3043                self.camera.SetViewUp(viewup)
3044                cpos = np.cross([0, 1, 0], viewup)
3045                self.camera.SetPosition(cm - 2 * sz * cpos)
3046
3047        self.renderer.ResetCameraClippingRange()
3048
3049        if self.interactor and not self.interactor.GetInitialized():
3050            self.interactor.Initialize()
3051            self.interactor.RemoveObservers("CharEvent")
3052
3053        if settings.immediate_rendering:
3054            self.window.Render()  ##################### <-------------- Render
3055
3056        # 2d ####################################################################
3057        if settings.default_backend == "2d":
3058            return backends.get_notebook_backend()
3059        #########################################################################
3060
3061        self.window.SetWindowName(self.title)
3062
3063        try:
3064            # Needs "pip install pyobjc" on Mac OSX
3065            if (
3066                self._cocoa_initialized is False
3067                and "Darwin" in vedo.sys_platform
3068                and not self.offscreen
3069            ):
3070                self._cocoa_initialized = True
3071                from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps
3072                pid = os.getpid()
3073                x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid))
3074                x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps)
3075        except:
3076            pass
3077            # vedo.logger.debug("On Mac OSX try: pip install pyobjc")
3078
3079        if self.interactor:  # can be offscreen..
3080
3081            if interactive is not None:
3082                self._interactive = interactive
3083
3084            self.user_mode(mode)
3085
3086            if self._interactive:
3087                self.interactor.Start()
3088                
3089            if rate:
3090                if self.clock is None:  # set clock and limit rate
3091                    self._clockt0 = time.time()
3092                    self.clock = 0.0
3093                else:
3094                    t = time.time() - self._clockt0
3095                    elapsed = t - self.clock
3096                    mint = 1.0 / rate
3097                    if elapsed < mint:
3098                        time.sleep(mint - elapsed)
3099                    self.clock = time.time() - self._clockt0
3100
3101        return self
3102
3103
3104    def add_inset(self, *actors, **options):
3105        """Add a draggable inset space into a renderer.
3106
3107        Arguments:
3108            at : (int)
3109                specify the renderer number
3110            pos : (list)
3111                icon position in the range [1-4] indicating one of the 4 corners,
3112                or it can be a tuple (x,y) as a fraction of the renderer size.
3113            size : (float)
3114                size of the square inset
3115            draggable : (bool)
3116                if True the subrenderer space can be dragged around
3117            c : (color)
3118                color of the inset frame when dragged
3119
3120        Examples:
3121            - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py)
3122
3123            ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg)
3124        """
3125        if not self.interactor:
3126            return None
3127        pos = options.pop("pos", 0)
3128        size = options.pop("size", 0.1)
3129        c = options.pop("c", "lb")
3130        at = options.pop("at", None)
3131        draggable = options.pop("draggable", True)
3132
3133        if not self.renderer:
3134            vedo.logger.warning("call add_inset() only after first rendering of the scene.")
3135            save_int = self._interactive
3136            self.show(interactive=0)
3137            self._interactive = save_int
3138        widget = vtk.vtkOrientationMarkerWidget()
3139        r, g, b = vedo.get_color(c)
3140        widget.SetOutlineColor(r, g, b)
3141        if len(actors) == 1:
3142            widget.SetOrientationMarker(actors[0])
3143        else:
3144            widget.SetOrientationMarker(vedo.Assembly(actors))
3145
3146        widget.SetInteractor(self.interactor)
3147
3148        if utils.is_sequence(pos):
3149            widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size)
3150        else:
3151            if pos < 2:
3152                widget.SetViewport(0, 1 - 2 * size, size * 2, 1)
3153            elif pos == 2:
3154                widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1)
3155            elif pos == 3:
3156                widget.SetViewport(0, 0, size * 2, size * 2)
3157            elif pos == 4:
3158                widget.SetViewport(1 - 2 * size, 0, 1, size * 2)
3159        widget.EnabledOn()
3160        widget.SetInteractive(draggable)
3161        if at is not None and at < len(self.renderers):
3162            widget.SetCurrentRenderer(self.renderers[at])
3163        self.widgets.append(widget)
3164        return widget
3165
3166    def clear(self, at=None, deep=False):
3167        """Clear the scene from all meshes and volumes."""
3168        if at is not None:
3169            renderer = self.renderers[at]
3170        else:
3171            renderer = self.renderer
3172        if not renderer:
3173            return self
3174
3175        if deep:
3176            renderer.RemoveAllViewProps()
3177        else:
3178            for a in set(self.get_meshes() + self.get_volumes() + self.actors + self.axes_instances):
3179                if isinstance(a, vedo.shapes.Text2D):
3180                    continue
3181                self.remove(a)
3182                try:
3183                    if a.scalarbar:
3184                        self.remove(a.scalarbar)
3185                except AttributeError:
3186                    pass
3187            self.actors = []
3188        return self
3189
3190    def break_interaction(self):
3191        """Break window interaction and return to the python execution flow"""
3192        if self.interactor:
3193            self.interactor.ExitCallback()
3194        return self
3195
3196    def user_mode(self, mode):
3197        """
3198        Modify the user interaction mode.
3199
3200        Examples:
3201            ```python
3202            from vedo import *
3203            mode = interactor_modes.MousePan()
3204            mesh = Mesh(dataurl+"cow.vtk")
3205            plt = Plotter().user_mode(mode)
3206            plt.show(mesh, axes=1)
3207           ```
3208        See also:
3209        [interactors](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html)
3210        """
3211        if not self.interactor:
3212            return None
3213
3214        if isinstance(mode, (str, int)):
3215            # Set the style of interaction
3216            # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html
3217            if mode in (0, "TrackballCamera"):
3218                if self.qt_widget:
3219                    self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
3220            elif mode in (1, "TrackballActor"):
3221                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleTrackballActor())
3222            elif mode in (2, "JoystickCamera"):
3223                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleJoystickCamera())
3224            elif mode in (3, "JoystickActor"):
3225                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleJoystickActor())
3226            elif mode in (4, "Flight"):
3227                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleFlight())
3228            elif mode in (5, "RubberBand2D"):
3229                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleRubberBand2D())
3230            elif mode in (6, "RubberBand3D"):
3231                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleRubberBand3D())
3232            elif mode in (7, "RubberBandZoom"):
3233                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleRubberBandZoom())
3234            elif mode in (8, "Terrain"):
3235                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleTerrain())
3236            elif mode in (9, "Unicam"):
3237                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleUnicam())
3238            elif mode in (10, "Image", "image", "2d"):
3239                astyle = vtk.vtkInteractorStyleImage()
3240                astyle.SetInteractionModeToImage3D()
3241                self.interactor.SetInteractorStyle(astyle)
3242            else:
3243                vedo.logger.warning(f"Unknown interaction mode: {mode}")
3244
3245        elif isinstance(mode, vtk.vtkInteractorStyleUser):
3246            # set a custom interactor style
3247            mode.interactor = self.interactor
3248            mode.renderer = self.renderer
3249            mode.SetInteractor(self.interactor)
3250            mode.SetDefaultRenderer(self.renderer)
3251            self.interactor.SetInteractorStyle(mode)
3252
3253        return self
3254
3255    def close_window(self):
3256        """Close the current or the input rendering window.
3257
3258        Examples:
3259            - [closewindow.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/closewindow.py)
3260        """
3261        vedo.last_figure = None
3262        self.sliders = []
3263        self.buttons = []
3264        self.widgets = []
3265        self.hover_legends = []
3266        self.background_renderer = None
3267        self._extralight = None
3268
3269        self.hint_widget = None
3270        self.cutter_widget = None
3271
3272        for r in self.renderers:
3273            r.RemoveAllObservers()
3274        if hasattr(self, "window") and self.window:
3275            if hasattr(self, "interactor") and self.interactor:
3276                self.interactor.ExitCallback()
3277                try:
3278                    self.interactor.SetDone(True)
3279                except AttributeError:
3280                    pass
3281                self.interactor.TerminateApp()
3282
3283                # self.interactor = None
3284            self.window.Finalize()  # this must be done here
3285
3286            if hasattr(self, "interactor") and self.interactor:
3287                if "Darwin" in vedo.sys_platform:
3288                    try:
3289                        self.interactor.ProcessEvents()
3290                    except:
3291                        pass
3292                self.interactor = None
3293
3294            self.window = None
3295
3296        self.renderer = None  # current renderer
3297        self.renderers = []
3298        self.camera = None
3299        self.skybox = None
3300        return self
3301
3302    def close(self):
3303        """Close the Plotter instance and release resources."""
3304        self.close_window()
3305        self.actors = []
3306        if vedo.plotter_instance == self:
3307            vedo.plotter_instance = None
3308
3309    def screenshot(self, filename="screenshot.png", scale=1, asarray=False):
3310        """
3311        Take a screenshot of the Plotter window.
3312
3313        Arguments:
3314            scale : (int)
3315                set image magnification as an integer multiplicating factor
3316            asarray : (bool)
3317                return a numpy array of the image instead of writing a file
3318        """
3319        return vedo.file_io.screenshot(filename, scale, asarray)
3320
3321    def topicture(self, scale=1):
3322        """
3323        Generate a Picture object from the current rendering window.
3324
3325        Arguments:
3326            scale : (int)
3327                set image magnification as an integer multiplicating factor
3328        """
3329        if settings.screeshot_large_image:
3330            w2if = vtk.vtkRenderLargeImage()
3331            w2if.SetInput(self.renderer)
3332            w2if.SetMagnification(scale)
3333        else:
3334            w2if = vtk.vtkWindowToImageFilter()
3335            w2if.SetInput(self.window)
3336            if hasattr(w2if, "SetScale"):
3337                w2if.SetScale(scale, scale)
3338            if settings.screenshot_transparent_background:
3339                w2if.SetInputBufferTypeToRGBA()
3340            w2if.ReadFrontBufferOff()  # read from the back buffer
3341        w2if.Update()
3342        return vedo.picture.Picture(w2if.GetOutput())
3343
3344    def export(self, filename="scene.npz", binary=False):
3345        """
3346        Export scene to file to HTML, X3D or Numpy file.
3347
3348        Examples:
3349            - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py)
3350            - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py)
3351        """
3352        vedo.file_io.export_window(filename, binary=binary)
3353        return self
3354
3355    def color_picker(self, xy, verbose=False):
3356        """Pick color of specific (x,y) pixel on the screen."""
3357        w2if = vtk.vtkWindowToImageFilter()
3358        w2if.SetInput(self.window)
3359        w2if.ReadFrontBufferOff()
3360        w2if.Update()
3361        nx, ny = self.window.GetSize()
3362        varr = w2if.GetOutput().GetPointData().GetScalars()
3363
3364        arr = utils.vtk2numpy(varr).reshape(ny, nx, 3)
3365        x, y = int(xy[0]), int(xy[1])
3366        if y < ny and x < nx:
3367
3368            rgb = arr[y, x]
3369
3370            if verbose:
3371                vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="")
3372                vedo.printc("█", c=[rgb[0], 0, 0], end="")
3373                vedo.printc("█", c=[0, rgb[1], 0], end="")
3374                vedo.printc("█", c=[0, 0, rgb[2]], end="")
3375                vedo.printc("] = ", end="")
3376                cnm = vedo.get_color_name(rgb)
3377                if np.sum(rgb) < 150:
3378                    vedo.printc(
3379                        rgb.tolist(),
3380                        vedo.colors.rgb2hex(np.array(rgb) / 255),
3381                        c="w",
3382                        bc=rgb,
3383                        invert=1,
3384                        end="",
3385                    )
3386                    vedo.printc("  -> " + cnm, invert=1, c="w")
3387                else:
3388                    vedo.printc(
3389                        rgb.tolist(), vedo.colors.rgb2hex(np.array(rgb) / 255), c=rgb, end=""
3390                    )
3391                    vedo.printc("  -> " + cnm, c=cnm)
3392
3393            return rgb
3394
3395        return None
3396
3397    #######################################################################
3398    def _mouseleftclick(self, iren, event):
3399
3400        x, y = iren.GetEventPosition()
3401
3402        renderer = iren.FindPokedRenderer(x, y)
3403        picker = vtk.vtkPropPicker()
3404        picker.PickProp(x, y, renderer)
3405
3406        self.renderer = renderer
3407
3408        clicked_actor = picker.GetActor()
3409        # clicked_actor2D = picker.GetActor2D()
3410
3411        # print('_mouseleftclick mouse at', x, y)
3412        # print("picked Volume:",   [picker.GetVolume()])
3413        # print("picked Actor2D:",  [picker.GetActor2D()])
3414        # print("picked Assembly:", [picker.GetAssembly()])
3415        # print("picked Prop3D:",   [picker.GetProp3D()])
3416
3417        if not clicked_actor:
3418            clicked_actor = picker.GetAssembly()
3419
3420        if not clicked_actor:
3421            clicked_actor = picker.GetProp3D()
3422
3423        if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable():
3424            return
3425
3426        self.picked3d = picker.GetPickPosition()
3427        self.picked2d = np.array([x, y])
3428
3429        if not clicked_actor:
3430            return
3431
3432        self.justremoved = None
3433
3434        self.clicked_actor = clicked_actor
3435        if hasattr(clicked_actor, "picked3d"):  # might be not a vedo obj
3436            clicked_actor.picked3d = picker.GetPickPosition()
3437            x, y = iren.GetEventPosition()
3438
3439        # -----------
3440        if "Histogram1D" in picker.GetAssembly().__class__.__name__:
3441            histo = picker.GetAssembly()
3442            if histo.verbose:
3443                x = self.picked3d[0]
3444                idx = np.digitize(x, histo.edges) - 1
3445                f = histo.frequencies[idx]
3446                cn = histo.centers[idx]
3447                vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}")
3448
3449
3450    #######################################################################
3451    def _keypress(self, iren, event):
3452
3453        # NB: qt creates and passes a vtkGenericRenderWindowInteractor
3454
3455        key = iren.GetKeySym()
3456
3457        if "_L" in key or "_R" in key:
3458            return
3459
3460        if iren.GetShiftKey():
3461            key = key.upper()
3462
3463        if iren.GetControlKey():
3464            key = "Ctrl+" + key
3465
3466        if iren.GetAltKey():
3467            key = "Alt+" + key
3468
3469        # utils.vedo.printc('Pressed key:', key, c='y', box='-')
3470        # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(),
3471        #       iren.GetKeyCode(), iren.GetRepeatCount())
3472        # iren.ExitCallback()
3473        # return
3474
3475        x, y = iren.GetEventPosition()
3476        renderer = iren.FindPokedRenderer(x, y)
3477
3478        if key in ["q", "Ctrl+q", "Ctrl+w", "Escape"]:
3479            iren.ExitCallback()
3480            return
3481
3482        elif key == "F1":
3483            vedo.logger.info("Execution aborted. Exiting python kernel now.")
3484            iren.ExitCallback()
3485            sys.exit(0)
3486
3487        elif key == "Down":
3488            if self.clicked_actor in self.get_meshes():
3489                self.clicked_actor.GetProperty().SetOpacity(0.02)
3490                bfp = self.clicked_actor.GetBackfaceProperty()
3491                if bfp and hasattr(self.clicked_actor, "_bfprop"):
3492                    self.clicked_actor._bfprop = bfp  # save it
3493                    self.clicked_actor.SetBackfaceProperty(None)
3494            else:
3495                for a in self.get_meshes():
3496                    a.GetProperty().SetOpacity(0.02)
3497                    bfp = a.GetBackfaceProperty()
3498                    if bfp and hasattr(a, "_bfprop"):
3499                        a._bfprop = bfp
3500                        a.SetBackfaceProperty(None)
3501
3502        elif key == "Left":
3503            if self.clicked_actor in self.get_meshes():
3504                ap = self.clicked_actor.GetProperty()
3505                aal = max([ap.GetOpacity() * 0.75, 0.01])
3506                ap.SetOpacity(aal)
3507                bfp = self.clicked_actor.GetBackfaceProperty()
3508                if bfp and hasattr(self.clicked_actor, "_bfprop"):
3509                    self.clicked_actor._bfprop = bfp
3510                    self.clicked_actor.SetBackfaceProperty(None)
3511            else:
3512                for a in self.get_meshes():
3513                    ap = a.GetProperty()
3514                    aal = max([ap.GetOpacity() * 0.75, 0.01])
3515                    ap.SetOpacity(aal)
3516                    bfp = a.GetBackfaceProperty()
3517                    if bfp and hasattr(a, "_bfprop"):
3518                        a._bfprop = bfp
3519                        a.SetBackfaceProperty(None)
3520
3521        elif key == "Right":
3522            if self.clicked_actor in self.get_meshes():
3523                ap = self.clicked_actor.GetProperty()
3524                aal = min([ap.GetOpacity() * 1.25, 1.0])
3525                ap.SetOpacity(aal)
3526                if (
3527                    aal == 1
3528                    and hasattr(self.clicked_actor, "_bfprop")
3529                    and self.clicked_actor._bfprop
3530                ):
3531                    # put back
3532                    self.clicked_actor.SetBackfaceProperty(self.clicked_actor._bfprop)
3533            else:
3534                for a in self.get_meshes():
3535                    ap = a.GetProperty()
3536                    aal = min([ap.GetOpacity() * 1.25, 1.0])
3537                    ap.SetOpacity(aal)
3538                    if aal == 1 and hasattr(a, "_bfprop") and a._bfprop:
3539                        a.SetBackfaceProperty(a._bfprop)
3540
3541        elif key in ("slash", "Up"):
3542            if self.clicked_actor in self.get_meshes():
3543                self.clicked_actor.GetProperty().SetOpacity(1)
3544                if hasattr(self.clicked_actor, "_bfprop") and self.clicked_actor._bfprop:
3545                    self.clicked_actor.SetBackfaceProperty(self.clicked_actor._bfprop)
3546            else:
3547                for a in self.get_meshes():
3548                    a.GetProperty().SetOpacity(1)
3549                    if hasattr(a, "_bfprop") and a._bfprop:
3550                        a.SetBackfaceProperty(a._bfprop)
3551
3552        elif key == "P":
3553            if self.clicked_actor in self.get_meshes():
3554                acts = [self.clicked_actor]
3555            else:
3556                acts = self.get_meshes()
3557            for ia in acts:
3558                try:
3559                    ps = ia.GetProperty().GetPointSize()
3560                    if ps > 1:
3561                        ia.GetProperty().SetPointSize(ps - 1)
3562                    ia.GetProperty().SetRepresentationToPoints()
3563                except AttributeError:
3564                    pass
3565
3566        elif key == "U":
3567            pval = renderer.GetActiveCamera().GetParallelProjection()
3568            renderer.GetActiveCamera().SetParallelProjection(not pval)
3569            if pval:
3570                renderer.ResetCamera()
3571
3572        elif key == "p":
3573            if self.clicked_actor in self.get_meshes():
3574                acts = [self.clicked_actor]
3575            else:
3576                acts = self.get_meshes()
3577            for ia in acts:
3578                try:
3579                    ps = ia.GetProperty().GetPointSize()
3580                    ia.GetProperty().SetPointSize(ps + 2)
3581                    ia.GetProperty().SetRepresentationToPoints()
3582                except AttributeError:
3583                    pass
3584
3585        elif key == "w":
3586            if self.clicked_actor and self.clicked_actor in self.get_meshes():
3587                self.clicked_actor.GetProperty().SetRepresentationToWireframe()
3588            else:
3589                for a in self.get_meshes():
3590                    if a.GetProperty().GetRepresentation() == 1:  # toggle
3591                        a.GetProperty().SetRepresentationToSurface()
3592                    else:
3593                        a.GetProperty().SetRepresentationToWireframe()
3594
3595        elif key == "r":
3596            renderer.ResetCamera()
3597
3598        elif key == "h":
3599            msg = (
3600                "  ============================================================\n"
3601                " | Press: i     print info about selected object              |\n"
3602                " |        I     print the RGB color under the mouse           |\n"
3603                " |        y     show the pipeline for this object as a graph  |\n"
3604                " |        <-->  use arrows to reduce/increase opacity         |\n"
3605                " |        w/s   toggle wireframe/surface style                |\n"
3606                " |        p/P   change point size of vertices                 |\n"
3607                " |        l     toggle edges visibility                       |\n"
3608                " |        x     toggle mesh visibility                        |\n"
3609                " |        X     invoke a cutter widget tool                   |\n"
3610                " |        1-3   change mesh color                             |\n"
3611                " |        4     use data array as colors, if present          |\n"
3612                " |        5-6   change background color(s)                    |\n"
3613                " |        09+-  (on keypad) or +/- to cycle axes style        |\n"
3614                " |        k     cycle available lighting styles               |\n"
3615                " |        K     cycle available shading styles                |\n"
3616                " |        A     toggle anti-aliasing                          |\n"
3617                " |        D     toggle depth-peeling (for transparencies)     |\n"
3618                " |        o/O   add/remove light to scene and rotate it       |\n"
3619                " |        n     show surface mesh normals                     |\n"
3620                " |        a     toggle interaction to Actor Mode              |\n"
3621                " |        j     toggle interaction to Joystick Mode           |\n"
3622                " |        U     toggle perspective/parallel projection        |\n"
3623                " |        r     reset camera position                         |\n"
3624                " |        R     reset camera orientation to orthogonal view   |\n"
3625                " |        .     fly camera towards last clicked point         |\n"
3626                " |        C     print current camera settings                 |\n"
3627                " |        S     save a screenshot                             |\n"
3628                " |        E/F   export 3D scene to numpy file or X3D          |\n"
3629                " |        q     return control to python script               |\n"
3630                " |        Esc   abort execution and exit python kernel        |\n"
3631                " |------------------------------------------------------------|\n"
3632                " | Mouse: Left-click    rotate scene / pick actors            |\n"
3633                " |        Middle-click  pan scene                             |\n"
3634                " |        Right-click   zoom scene in or out                  |\n"
3635                " |        Cntrl-click   rotate scene                          |\n"
3636                " |------------------------------------------------------------|\n"
3637                " |   Check out the documentation at:  https://vedo.embl.es    |\n"
3638                "  ============================================================"
3639            )
3640            vedo.printc(msg, dim=True)
3641
3642            msg = " vedo " + vedo.__version__ + " "
3643            vedo.printc(msg, invert=True, dim=True, end="")
3644            vtkVers = vtk.vtkVersion().GetVTKVersion()
3645            msg = "| vtk " + str(vtkVers)
3646            msg += " | numpy " + str(np.__version__)
3647            msg += " | python " + str(sys.version_info[0]) + "." + str(sys.version_info[1])
3648            vedo.printc(msg, invert=False, dim=True)
3649            return
3650
3651        elif key == "a":
3652            iren.ExitCallback()
3653            cur = iren.GetInteractorStyle()
3654            if isinstance(cur, vtk.vtkInteractorStyleTrackballCamera):
3655                msg = "\nInteractor style changed to TrackballActor\n"
3656                msg += "  you can now move and rotate individual meshes:\n"
3657                msg += "  press X twice to save the repositioned mesh\n"
3658                msg += "  press 'a' to go back to normal style"
3659                vedo.printc(msg)
3660                iren.SetInteractorStyle(vtk.vtkInteractorStyleTrackballActor())
3661            else:
3662                iren.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
3663            iren.Start()
3664            return
3665
3666        elif key == "A":  # toggle antialiasing
3667            msam = self.window.GetMultiSamples()
3668            if not msam:
3669                self.window.SetMultiSamples(8)
3670            else:
3671                self.window.SetMultiSamples(0)
3672            msam = self.window.GetMultiSamples()
3673            if msam:
3674                vedo.printc(f"Antialiasing is now set to {msam} samples", c=bool(msam))
3675            else:
3676                vedo.printc("Antialiasing is now disabled", c=bool(msam))
3677
3678        elif key == "D":  # toggle depthpeeling
3679            udp = not renderer.GetUseDepthPeeling()
3680            renderer.SetUseDepthPeeling(udp)
3681            # self.renderer.SetUseDepthPeelingForVolumes(udp)
3682            # print(self.window.GetAlphaBitPlanes())
3683            if udp:
3684                self.window.SetAlphaBitPlanes(1)
3685                renderer.SetMaximumNumberOfPeels(settings.max_number_of_peels)
3686                renderer.SetOcclusionRatio(settings.occlusion_ratio)
3687            self.interactor.Render()
3688            wasUsed = renderer.GetLastRenderingUsedDepthPeeling()
3689            rnr = self.renderers.index(renderer)
3690            vedo.printc(f"Depth peeling is now set to {udp} for renderer nr.{rnr}", c=udp)
3691            if not wasUsed and udp:
3692                vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True)
3693            return
3694
3695        elif key == "j":
3696            iren.ExitCallback()
3697            cur = iren.GetInteractorStyle()
3698            if isinstance(cur, vtk.vtkInteractorStyleJoystickCamera):
3699                iren.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
3700            else:
3701                vedo.printc("\nInteractor style changed to Joystick,", end="")
3702                vedo.printc(" press j to go back to normal.")
3703                iren.SetInteractorStyle(vtk.vtkInteractorStyleJoystickCamera())
3704            iren.Start()
3705            return
3706
3707        elif key == "period":
3708            if self.picked3d:
3709                self.fly_to(self.picked3d)
3710            return
3711
3712        elif key == "S":
3713            vedo.file_io.screenshot("screenshot.png")
3714            vedo.printc(r":camera: Saved rendering window to 'screenshot.png'", c="b")
3715            return
3716
3717        elif key == "C":
3718            # Precision needs to be 7 (or even larger) to guarantee a consistent camera when
3719            #   the model coordinates are not centered at (0, 0, 0) and the mode is large.
3720            # This could happen for plotting geological models with UTM coordinate systems
3721            cam = renderer.GetActiveCamera()
3722            vedo.printc("\n###################################################", c="y")
3723            vedo.printc("## Template python code to position this camera: ##", c="y")
3724            vedo.printc("cam = dict(", c="y")
3725            vedo.printc("    position=" + utils.precision(cam.GetPosition(), 6) + ",", c="y")
3726            vedo.printc("    focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y")
3727            vedo.printc("    viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y")
3728            if settings.use_parallel_projection:
3729                vedo.printc('    parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y')
3730            else:
3731                vedo.printc('    distance='     +utils.precision(cam.GetDistance(),6)+',', c='y')
3732            vedo.printc('    clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y')
3733            vedo.printc(')', c='y')
3734            vedo.printc('show(mymeshes, camera=cam)', c='y')
3735            vedo.printc('###################################################', c='y')
3736            return
3737
3738        elif key == "R":
3739            self.reset_viewup()
3740
3741        elif key == "s":
3742            if self.clicked_actor and self.clicked_actor in self.get_meshes():
3743                self.clicked_actor.GetProperty().SetRepresentationToSurface()
3744            else:
3745                for a in self.get_meshes():
3746                    a.GetProperty().SetRepresentationToSurface()
3747
3748        elif key == "1":
3749            self._icol += 1
3750            if isinstance(self.clicked_actor, vedo.Points):
3751                self.clicked_actor.GetMapper().ScalarVisibilityOff()
3752                pal = vedo.colors.palettes[settings.palette % len(vedo.colors.palettes)]
3753                self.clicked_actor.GetProperty().SetColor(pal[(self._icol) % 10])
3754
3755        elif key == "2":
3756            self._icol += 1
3757            settings.palette += 1
3758            settings.palette = settings.palette % len(vedo.colors.palettes)
3759            if isinstance(self.clicked_actor, vedo.Points):
3760                self.clicked_actor.GetMapper().ScalarVisibilityOff()
3761                pal = vedo.colors.palettes[settings.palette % len(vedo.colors.palettes)]
3762                self.clicked_actor.GetProperty().SetColor(pal[(self._icol) % 10])
3763
3764        elif key == "3":
3765            bsc = ["b5", "cyan5", "g4", "o5", "p5", "r4", "teal4", "yellow4"]
3766            self._icol += 1
3767            if isinstance(self.clicked_actor, vedo.Points):
3768                self.clicked_actor.GetMapper().ScalarVisibilityOff()
3769                self.clicked_actor.GetProperty().SetColor(vedo.get_color(bsc[(self._icol) % len(bsc)]))
3770
3771        elif key == "4":
3772            if self.clicked_actor:
3773                acts = [self.clicked_actor]
3774            else:
3775                acts = self.get_meshes()
3776            for ia in acts:
3777                if not hasattr(ia, "_cmap_name"):
3778                    continue
3779                cmap_name = ia._cmap_name
3780                if not cmap_name:
3781                    cmap_name = "rainbow"
3782                if isinstance(ia, vedo.pointcloud.Points):
3783                    arnames = ia.pointdata.keys()
3784                    if len(arnames) > 0:
3785                        arnam = arnames[ia._scals_idx]
3786                        if arnam and ("normals" not in arnam.lower()):  # exclude normals
3787                            ia.cmap(cmap_name, arnam, on="points")
3788                            vedo.printc("..active point data set to:", arnam, c="g", bold=0)
3789                            ia._scals_idx += 1
3790                            if ia._scals_idx >= len(arnames):
3791                                ia._scals_idx = 0
3792                    else:
3793                        arnames = ia.celldata.keys()
3794                        if len(arnames) > 0:
3795                            arnam = arnames[ia._scals_idx]
3796                            if arnam and ("normals" not in arnam.lower()):  # exclude normals
3797                                ia.cmap(cmap_name, arnam, on="cells")
3798                                vedo.printc("..active cell array set to:", arnam, c="g", bold=0)
3799                                ia._scals_idx += 1
3800                                if ia._scals_idx >= len(arnames):
3801                                    ia._scals_idx = 0
3802
3803        elif key == "5":
3804            bgc = np.array(renderer.GetBackground()).sum() / 3
3805            if bgc <= 0:
3806                bgc = 0.223
3807            elif 0 < bgc < 1:
3808                bgc = 1
3809            else:
3810                bgc = 0
3811            renderer.SetBackground(bgc, bgc, bgc)
3812
3813        elif key == "6":
3814            bg2cols = [
3815                "lightyellow",
3816                "darkseagreen",
3817                "palegreen",
3818                "steelblue",
3819                "lightblue",
3820                "cadetblue",
3821                "lavender",
3822                "white",
3823                "blackboard",
3824                "black",
3825            ]
3826            bg2name = vedo.get_color_name(renderer.GetBackground2())
3827            if bg2name in bg2cols:
3828                idx = bg2cols.index(bg2name)
3829            else:
3830                idx = 4
3831            if idx is not None:
3832                bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)]
3833            if not bg2name_next:
3834                renderer.GradientBackgroundOff()
3835            else:
3836                renderer.GradientBackgroundOn()
3837                renderer.SetBackground2(vedo.get_color(bg2name_next))
3838
3839        elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]:  # cycle axes style
3840            clickedr = self.renderers.index(renderer)
3841            if self.axes_instances[clickedr]:
3842                if hasattr(self.axes_instances[clickedr], "EnabledOff"):  # widget
3843                    self.axes_instances[clickedr].EnabledOff()
3844                else:
3845                    try:
3846                        renderer.RemoveActor(self.axes_instances[clickedr])
3847                    except:
3848                        pass
3849                self.axes_instances[clickedr] = None
3850            if not self.axes:
3851                self.axes = 0
3852            if isinstance(self.axes, dict):
3853                self.axes = 1
3854            if key in ["minus", "KP_Subtract"]:
3855                if not settings.use_parallel_projection and self.axes == 0:
3856                    self.axes -= 1  # jump ruler doesnt make sense in perspective mode
3857                bns = self.renderer.ComputeVisiblePropBounds()
3858                addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None, bounds=bns)
3859            else:
3860                if not settings.use_parallel_projection and self.axes == 12:
3861                    self.axes += 1  # jump ruler doesnt make sense in perspective mode
3862                bns = self.renderer.ComputeVisiblePropBounds()
3863                addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns)
3864            self.interactor.Render()
3865
3866        elif "KP_" in key or key in [
3867            "Insert",
3868            "End",
3869            "Down",
3870            "Next",
3871            "Left",
3872            "Begin",
3873            "Right",
3874            "Home",
3875            "Up",
3876            "Prior",
3877        ]:
3878            # change axes style
3879            asso = {
3880                "KP_Insert": 0,
3881                "KP_0": 0,
3882                "KP_End": 1,
3883                "KP_1": 1,
3884                "KP_Down": 2,
3885                "KP_2": 2,
3886                "KP_Next": 3,
3887                "KP_3": 3,
3888                "KP_Left": 4,
3889                "KP_4": 4,
3890                "KP_Begin": 5,
3891                "KP_5": 5,
3892                "KP_Right": 6,
3893                "KP_6": 6,
3894                "KP_Home": 7,
3895                "KP_7": 7,
3896                "KP_Up": 8,
3897                "KP_8": 8,
3898                "Prior": 9,  # on windows OS
3899                "Insert": 0,
3900                "End": 1,
3901                "Down": 2,
3902                "Next": 3,
3903                "Left": 4,
3904                "Begin": 5,
3905                "Right": 6,
3906                "Home": 7,
3907                "Up": 8,
3908                "Prior": 9,
3909            }
3910            clickedr = self.renderers.index(renderer)
3911            if key in asso:
3912                if self.axes_instances[clickedr]:
3913                    if hasattr(self.axes_instances[clickedr], "EnabledOff"):  # widget
3914                        self.axes_instances[clickedr].EnabledOff()
3915                    else:
3916                        try:
3917                            renderer.RemoveActor(self.axes_instances[clickedr])
3918                        except:
3919                            pass
3920                    self.axes_instances[clickedr] = None
3921                bounds = renderer.ComputeVisiblePropBounds()
3922                addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds)
3923                self.interactor.Render()
3924
3925        if key == "O":
3926            renderer.RemoveLight(self._extralight)
3927            self._extralight = None
3928
3929        elif key == "o":
3930            vbb, sizes, _, _ = addons.compute_visible_bounds()
3931            cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2)
3932            if not self._extralight:
3933                vup = renderer.GetActiveCamera().GetViewUp()
3934                pos = cm + utils.vector(vup) * utils.mag(sizes)
3935                self._extralight = addons.Light(pos, focal_point=cm)
3936                renderer.AddLight(self._extralight)
3937                print("Press again o to rotate light source, or O to remove it.")
3938            else:
3939                cpos = utils.vector(self._extralight.GetPosition())
3940                x, y, z = self._extralight.GetPosition() - cm
3941                r, th, ph = utils.cart2spher(x, y, z)
3942                th += 0.2
3943                if th > np.pi:
3944                    th = np.random.random() * np.pi / 2
3945                ph += 0.3
3946                cpos = utils.spher2cart(r, th, ph) + cm
3947                self._extralight.SetPosition(cpos)
3948
3949            self.window.Render()
3950
3951        elif key == "l":
3952            if self.clicked_actor in self.get_meshes():
3953                acts = [self.clicked_actor]
3954            else:
3955                acts = self.get_meshes()
3956            for ia in acts:
3957                try:
3958                    ev = ia.GetProperty().GetEdgeVisibility()
3959                    ia.GetProperty().SetEdgeVisibility(not ev)
3960                    ia.GetProperty().SetRepresentationToSurface()
3961                    ia.GetProperty().SetLineWidth(0.1)
3962                except AttributeError:
3963                    pass
3964
3965        elif key == "k":  # lightings
3966            if self.clicked_actor in self.get_meshes():
3967                acts = [self.clicked_actor]
3968            else:
3969                acts = self.get_meshes()
3970            shds = ("default", "metallic", "plastic", "shiny", "glossy", "off")
3971            for ia in acts:
3972                try:
3973                    lnr = (ia._ligthingnr + 1) % 6
3974                    ia.lighting(shds[lnr])
3975                    ia._ligthingnr = lnr
3976                except AttributeError:
3977                    pass
3978
3979        elif key == "K":  # shading
3980            if self.clicked_actor in self.get_meshes():
3981                acts = [self.clicked_actor]
3982            else:
3983                acts = self.get_meshes()
3984            for ia in acts:
3985                if isinstance(ia, vedo.Mesh):
3986                    ia.compute_normals(cells=False)
3987                    intrp = ia.GetProperty().GetInterpolation()
3988                    # print(intrp, ia.GetProperty().GetInterpolationAsString())
3989                    if intrp > 0:
3990                        ia.GetProperty().SetInterpolation(0)  # flat
3991                    else:
3992                        ia.GetProperty().SetInterpolation(2)  # phong
3993
3994        elif key == "n":  # show normals to an actor
3995            if self.clicked_actor in self.get_meshes():
3996                if self.clicked_actor.GetPickable():
3997                    self.renderer.AddActor(vedo.shapes.NormalLines(self.clicked_actor))
3998                    iren.Render()
3999            else:
4000                print("Click an actor and press n to add normals.")
4001
4002        elif key == "x":
4003            if self.justremoved is None:
4004                if self.clicked_actor in self.get_meshes() or isinstance(
4005                    self.clicked_actor, vtk.vtkAssembly
4006                ):
4007                    self.justremoved = self.clicked_actor
4008                    self.renderer.RemoveActor(self.clicked_actor)
4009            else:
4010                self.renderer.AddActor(self.justremoved)
4011                self.renderer.Render()
4012                self.justremoved = None
4013
4014        elif key == "X":
4015            if self.clicked_actor:
4016                if not self.cutter_widget:
4017                    self.cutter_widget = addons.BoxCutter(self.clicked_actor)
4018                    self.add(self.cutter_widget)
4019                    print("Press Shift+X to close the cutter box widget, Ctrl+s to save the cut section.")
4020                else:
4021                    self.remove(self.cutter_widget)
4022                    self.cutter_widget = None
4023            else:
4024                for a in self.actors:
4025                    if isinstance(a, vtk.vtkVolume):
4026                        addons.add_cutter_tool(a)
4027                        return
4028
4029                vedo.printc("Click object and press X to open the cutter box widget.", c=4)
4030
4031        elif key == "E":
4032            vedo.printc(r":camera: Exporting 3D window to file", c="blue", end="")
4033            vedo.file_io.export_window("scene.npz")
4034            vedo.printc(". Try:\n> vedo scene.npz", c="blue")
4035
4036        elif key == "F":
4037            vedo.file_io.export_window("scene.x3d")
4038            vedo.printc(":idea: Try: firefox scene.html", c="blue")
4039
4040        elif key == "i":  # print info
4041            if self.clicked_actor:
4042                utils.print_info(self.clicked_actor)
4043            else:
4044                utils.print_info(self)
4045
4046        elif key == "I":  # print color under the mouse
4047            x, y = iren.GetEventPosition()
4048            self.color_picker([x, y], verbose=True)
4049
4050        elif key == "y":
4051            if self.clicked_actor and self.clicked_actor.pipeline:
4052                # self.clicked_actor.pipeline =  utils.OperationNode(
4053                #         "show", parents=[self.clicked_actor],
4054                #         shape="circle",
4055                # )
4056                self.clicked_actor.pipeline.show()
4057
4058        if iren:
4059            iren.Render()
class Plotter:
 318class Plotter:
 319    """Main class to manage actors."""
 320    def __init__(
 321        self,
 322        shape=(1, 1),
 323        N=None,
 324        pos=(0, 0),
 325        size="auto",
 326        screensize="auto",
 327        title="vedo",
 328        bg="white",
 329        bg2=None,
 330        axes=None,
 331        sharecam=True,
 332        resetcam=True,
 333        interactive=None,
 334        offscreen=False,
 335        qt_widget=None,
 336        wx_widget=None,
 337    ):
 338        """
 339        Arguments:
 340            shape : (str, list)
 341                shape of the grid of renderers in format (rows, columns). Ignored if N is specified.
 342            N : (int)
 343                number of desired renderers arranged in a grid automatically.
 344            pos : (list)
 345                (x,y) position in pixels of top-left corner of the rendering window on the screen
 346            size : (str, list)
 347                size of the rendering window. If 'auto', guess it based on screensize.
 348            screensize : (list)
 349                physical size of the monitor screen in pixels
 350            bg : (color, str)
 351                background color or specify jpg image file name with path
 352            bg2 : (color)
 353                background color of a gradient towards the top
 354            axes : (int)
 355
 356                Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`.
 357                Check out `vedo.addons.Axes()` for the available options.
 358                - 0,  no axes
 359                - 1,  draw three gray grid walls
 360                - 2,  show cartesian axes from (0,0,0)
 361                - 3,  show positive range of cartesian axes from (0,0,0)
 362                - 4,  show a triad at bottom left
 363                - 5,  show a cube at bottom left
 364                - 6,  mark the corners of the bounding box
 365                - 7,  draw a 3D ruler at each side of the cartesian axes
 366                - 8,  show the VTK CubeAxesActor object
 367                - 9,  show the bounding box outLine,
 368                - 10, show three circles representing the maximum bounding box,
 369                - 11, show a large grid on the x-y plane (use with zoom=8)
 370                - 12, show polar axes.
 371                - 13, draw a simple ruler at the bottom of the window
 372
 373            sharecam : (bool)
 374                if False each renderer will have an independent vtkCamera
 375            interactive : (bool)
 376                if True will stop after show() to allow interaction w/ window
 377            offscreen : (bool)
 378                if True will not show the rendering window
 379            qt_widget : (QVTKRenderWindowInteractor)
 380                render in a Qt-Widget using an QVTKRenderWindowInteractor.
 381                Overrides offscreen to True.
 382                Overrides interactive to False.
 383                See examples `qt_windows1.py` and `qt_windows2.py`
 384        """
 385        vedo.plotter_instance = self
 386
 387        if qt_widget is not None:
 388            # overrides the interactive and offscreen properties
 389            interactive = False
 390            offscreen = True
 391
 392        if wx_widget is not None:
 393            # overrides the interactive property
 394            interactive = False
 395
 396        if interactive is None:
 397            if N == 1:
 398                interactive = True
 399            elif N or shape != (1, 1):
 400                interactive = False
 401            else:
 402                interactive = True
 403
 404        self.actors = []  # list of actors to be shown
 405        self.clicked_actor = None  # holds the actor that has been clicked
 406        self.renderer = None  # current renderer
 407        self.renderers = []  # list of renderers
 408        self.shape = shape  # don't remove this line
 409        self._interactive = interactive  # allows to interact with renderer
 410        self.axes = axes  # show axes type nr.
 411        self.title = title  # window title
 412        self.sharecam = sharecam  # share the same camera if multiple renderers
 413        self.picker = None  # the vtkPicker object
 414        self.picked2d = None  # 2d coords of a clicked point on the rendering window
 415        self.picked3d = None  # 3d coords of a clicked point on an actor
 416        self.offscreen = offscreen
 417        self.resetcam = resetcam
 418        self.last_event = None
 419
 420        self.qt_widget = qt_widget  #  QVTKRenderWindowInteractor
 421        self.wx_widget = wx_widget  # wxVTKRenderWindowInteractor
 422
 423        self.skybox = None
 424
 425        # mostly internal stuff:
 426        self.hover_legends = []
 427        self.backgrcol = bg
 428        self.pos = pos  # used by vedo.file_io
 429        self.justremoved = None
 430        self.axes_instances = []
 431        self.clock = 0
 432        self.sliders = []
 433        self.buttons = []
 434        self.widgets = []
 435        self.cutter_widget = None
 436        self.hint_widget = None
 437        self.background_renderer = None
 438        self.size = size
 439        self.interactor = None
 440        self.camera = None
 441
 442        self._icol = 0
 443        self._clockt0 = time.time()
 444        self._extralight = None
 445        self._cocoa_initialized = False
 446        self._bg = bg  # used by backend notebooks
 447
 448        #####################################################################
 449        if settings.default_backend != "vtk":
 450            if settings.default_backend == "2d":
 451                self.offscreen = True
 452                if self.size == "auto":
 453                    self.size = (800, 600)
 454
 455            elif settings.default_backend == "k3d":
 456                self._interactive = False
 457                self.interactor = None
 458                self.window = None
 459                self.camera = None  # let the backend choose
 460                if self.size == "auto":
 461                    self.size = (1000, 1000)
 462                #############################################################
 463                return  ######################################################
 464                #############################################################
 465        #####################################################################
 466
 467        # build the rendering window:
 468        self.window = vtk.vtkRenderWindow()
 469
 470        self.window.GlobalWarningDisplayOff()
 471        self.window.SetWindowName(self.title)
 472
 473        # more settings
 474        if settings.use_depth_peeling:
 475            self.window.SetAlphaBitPlanes(settings.alpha_bit_planes)
 476        self.window.SetMultiSamples(settings.multi_samples)
 477
 478        self.window.SetPolygonSmoothing(settings.polygon_smoothing)
 479        self.window.SetLineSmoothing(settings.line_smoothing)
 480        self.window.SetPointSmoothing(settings.point_smoothing)
 481
 482        # sort out screen size
 483        if screensize == "auto":
 484            screensize = (2160, 1440)  # might go wrong, use a default 1.5 ratio
 485
 486            ### BUG in GetScreenSize in VTK 9.1.0
 487            ### https://discourse.vtk.org/t/vtk9-1-0-problems/7094/3
 488            if settings.hack_call_screen_size:  # True
 489
 490                vtkvers = vedo.vtk_version
 491                if not self.offscreen and (vtkvers[0] < 9 or vtkvers[0] == 9 and vtkvers[1] == 0):
 492                    aus = self.window.GetScreenSize()
 493                    if aus and len(aus) == 2 and aus[0] > 100 and aus[1] > 100:  # seems ok
 494                        if aus[0] / aus[1] > 2:  # looks like there are 2 or more screens
 495                            screensize = (int(aus[0] / 2), aus[1])
 496                        else:
 497                            screensize = aus
 498
 499        x, y = screensize
 500
 501        if N:  # N = number of renderers. Find out the best
 502
 503            if shape != (1, 1):  # arrangement based on minimum nr. of empty renderers
 504                vedo.logger.warning("having set N, shape is ignored.")
 505
 506            nx = int(np.sqrt(int(N * y / x) + 1))
 507            ny = int(np.sqrt(int(N * x / y) + 1))
 508            lm = [
 509                (nx, ny),
 510                (nx, ny + 1),
 511                (nx - 1, ny),
 512                (nx + 1, ny),
 513                (nx, ny - 1),
 514                (nx - 1, ny + 1),
 515                (nx + 1, ny - 1),
 516                (nx + 1, ny + 1),
 517                (nx - 1, ny - 1),
 518            ]
 519            ind, minl = 0, 1000
 520            for i, m in enumerate(lm):
 521                l = m[0] * m[1]
 522                if N <= l < minl:
 523                    ind = i
 524                    minl = l
 525            shape = lm[ind]
 526
 527        ##################################################
 528        if isinstance(shape, str):
 529
 530            if "|" in shape:
 531                if self.size == "auto":
 532                    self.size = (800, 1200)
 533                n = int(shape.split("|")[0])
 534                m = int(shape.split("|")[1])
 535                rangen = reversed(range(n))
 536                rangem = reversed(range(m))
 537            else:
 538                if self.size == "auto":
 539                    self.size = (1200, 800)
 540                m = int(shape.split("/")[0])
 541                n = int(shape.split("/")[1])
 542                rangen = range(n)
 543                rangem = range(m)
 544
 545            if n >= m:
 546                xsplit = m / (n + m)
 547            else:
 548                xsplit = 1 - n / (n + m)
 549            if settings.window_splitting_position:
 550                xsplit = settings.window_splitting_position
 551
 552            for i in rangen:
 553                arenderer = vtk.vtkRenderer()
 554                if "|" in shape:
 555                    arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n)
 556                else:
 557                    arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit)
 558                self.renderers.append(arenderer)
 559
 560            for i in rangem:
 561                arenderer = vtk.vtkRenderer()
 562
 563                if "|" in shape:
 564                    arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m)
 565                else:
 566                    arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1)
 567                self.renderers.append(arenderer)
 568
 569            for r in self.renderers:
 570                r.SetUseHiddenLineRemoval(settings.hidden_line_removal)
 571                r.SetLightFollowCamera(settings.light_follows_camera)
 572
 573                r.SetUseDepthPeeling(settings.use_depth_peeling)
 574                # r.SetUseDepthPeelingForVolumes(settings.use_depth_peeling)
 575                if settings.use_depth_peeling:
 576                    r.SetMaximumNumberOfPeels(settings.max_number_of_peels)
 577                    r.SetOcclusionRatio(settings.occlusion_ratio)
 578                r.SetUseFXAA(settings.use_fxaa)
 579                r.SetPreserveDepthBuffer(settings.preserve_depth_buffer)
 580
 581                r.SetBackground(vedo.get_color(self.backgrcol))
 582
 583                self.axes_instances.append(None)
 584
 585            self.shape = (n + m,)
 586
 587        elif utils.is_sequence(shape) and isinstance(shape[0], dict):
 588            # passing a sequence of dicts for renderers specifications
 589
 590            if self.size == "auto":
 591                self.size = (1200, 900)
 592
 593            for rd in shape:
 594                x0, y0 = rd["bottomleft"]
 595                x1, y1 = rd["topright"]
 596                bg_ = rd.pop("bg", "white")
 597                bg2_ = rd.pop("bg2", None)
 598
 599                arenderer = vtk.vtkRenderer()
 600                arenderer.SetUseHiddenLineRemoval(settings.hidden_line_removal)
 601                arenderer.SetLightFollowCamera(settings.light_follows_camera)
 602
 603                arenderer.SetUseDepthPeeling(settings.use_depth_peeling)
 604                # arenderer.SetUseDepthPeelingForVolumes(settings.use_depth_peeling)
 605                if settings.use_depth_peeling:
 606                    arenderer.SetMaximumNumberOfPeels(settings.max_number_of_peels)
 607                    arenderer.SetOcclusionRatio(settings.occlusion_ratio)
 608                arenderer.SetUseFXAA(settings.use_fxaa)
 609                arenderer.SetPreserveDepthBuffer(settings.preserve_depth_buffer)
 610
 611                arenderer.SetViewport(x0, y0, x1, y1)
 612                arenderer.SetBackground(vedo.get_color(bg_))
 613                if bg2_:
 614                    arenderer.GradientBackgroundOn()
 615                    arenderer.SetBackground2(vedo.get_color(bg2_))
 616
 617                self.renderers.append(arenderer)
 618                self.axes_instances.append(None)
 619
 620            self.shape = (len(shape),)
 621
 622        else:
 623
 624            if isinstance(self.size, str) and self.size == "auto":
 625                # figure out a reasonable window size
 626                f = 1.5
 627                xs = y / f * shape[1]  # because y<x
 628                ys = y / f * shape[0]
 629                if xs > x / f:  # shrink
 630                    xs = x / f
 631                    ys = xs / shape[1] * shape[0]
 632                if ys > y / f:
 633                    ys = y / f
 634                    xs = ys / shape[0] * shape[1]
 635                self.size = (int(xs), int(ys))
 636                if shape == (1, 1):
 637                    self.size = (int(y / f), int(y / f))  # because y<x
 638            else:
 639                self.size = (self.size[0], self.size[1])
 640
 641            image_actor = None
 642            bgname = str(self.backgrcol).lower()
 643            if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname:
 644                self.window.SetNumberOfLayers(2)
 645                self.background_renderer = vtk.vtkRenderer()
 646                self.background_renderer.SetLayer(0)
 647                self.background_renderer.InteractiveOff()
 648                self.background_renderer.SetBackground(vedo.get_color(bg2))
 649                image_actor = vedo.Picture(self.backgrcol)
 650                self.window.AddRenderer(self.background_renderer)
 651                self.background_renderer.AddActor(image_actor)
 652
 653            for i in reversed(range(shape[0])):
 654                for j in range(shape[1]):
 655                    arenderer = vtk.vtkRenderer()
 656                    arenderer.SetUseHiddenLineRemoval(settings.hidden_line_removal)
 657                    arenderer.SetLightFollowCamera(settings.light_follows_camera)
 658                    arenderer.SetTwoSidedLighting(settings.two_sided_lighting)
 659
 660                    arenderer.SetUseDepthPeeling(settings.use_depth_peeling)
 661                    # arenderer.SetUseDepthPeelingForVolumes(settings.use_depth_peeling)
 662                    if settings.use_depth_peeling:
 663                        arenderer.SetMaximumNumberOfPeels(settings.max_number_of_peels)
 664                        arenderer.SetOcclusionRatio(settings.occlusion_ratio)
 665                    arenderer.SetUseFXAA(settings.use_fxaa)
 666                    arenderer.SetPreserveDepthBuffer(settings.preserve_depth_buffer)
 667
 668                    if image_actor:
 669                        arenderer.SetLayer(1)
 670
 671                    arenderer.SetBackground(vedo.get_color(self.backgrcol))
 672                    if bg2:
 673                        arenderer.GradientBackgroundOn()
 674                        arenderer.SetBackground2(vedo.get_color(bg2))
 675
 676                    x0 = i / shape[0]
 677                    y0 = j / shape[1]
 678                    x1 = (i + 1) / shape[0]
 679                    y1 = (j + 1) / shape[1]
 680                    arenderer.SetViewport(y0, x0, y1, x1)
 681                    self.renderers.append(arenderer)
 682                    self.axes_instances.append(None)
 683            self.shape = shape
 684
 685        if self.renderers:
 686            self.renderer = self.renderers[0]
 687            self.camera = self.renderer.GetActiveCamera()
 688            self.camera.SetParallelProjection(settings.use_parallel_projection)
 689
 690        if self.size[0] == "f":  # full screen
 691            self.size = "fullscreen"
 692            self.window.SetFullScreen(True)
 693            self.window.BordersOn()
 694        else:
 695            self.window.SetSize(int(self.size[0]), int(self.size[1]))
 696
 697        if self.wx_widget is not None:
 698            settings.immediate_rendering = False  # override
 699            self.window = self.wx_widget.GetRenderWindow()  # overwrite
 700            self.interactor = self.window.GetInteractor()
 701            for r in self.renderers:
 702                self.window.AddRenderer(r)
 703            self.wx_widget.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
 704            self.camera = self.renderer.GetActiveCamera()
 705            ########################
 706            return  ################
 707            ########################
 708
 709        if self.qt_widget is not None:
 710            self.interactor = self.qt_widget.GetRenderWindow().GetInteractor()
 711            self.window = self.qt_widget.GetRenderWindow()  # overwrite
 712            ########################
 713            return  ################
 714            ########################
 715
 716        self.window.SetPosition(pos)
 717
 718        for r in self.renderers:
 719            self.window.AddRenderer(r)
 720
 721        if self.offscreen:
 722            if self.axes in (4, 5):
 723                self.axes = 0  # does not work with those
 724            self.window.SetOffScreenRendering(True)
 725            self._interactive = False
 726            self.interactor = None
 727            ########################
 728            return  ################
 729            ########################
 730
 731        self.interactor = vtk.vtkRenderWindowInteractor()
 732
 733        self.interactor.SetRenderWindow(self.window)
 734        vsty = vtk.vtkInteractorStyleTrackballCamera()
 735        self.interactor.SetInteractorStyle(vsty)
 736
 737        if settings.enable_default_mouse_callbacks:
 738            self.interactor.AddObserver("LeftButtonPressEvent", self._mouseleftclick)
 739
 740        if settings.enable_default_keyboard_callbacks:
 741            self.interactor.AddObserver("KeyPressEvent", self._keypress)
 742
 743        # self._timer_event_id = None
 744        # if settings.allow_interaction:
 745            # def win_interact(iren, event):  # flushing interactor events
 746            #     if event == "TimerEvent":
 747            #         iren.ExitCallback()
 748            # self._timer_event_id = self.interactor.AddObserver("TimerEvent", win_interact)
 749
 750    ##################################################################### ..init ends here.
 751
 752
 753    # def allow_interaction(self):
 754    #     """Call this method from inside a loop to allow mouse and keyboard interaction."""
 755    #     if (
 756    #         self.interactor
 757    #         and self._timer_event_id is not None
 758    #         and settings.immediate_rendering
 759    #     ):
 760    #         self._repeatingtimer_id = self.interactor.CreateRepeatingTimer(1)
 761    #         self.interactor.Start()
 762    #         if self.interactor:
 763    #             self.interactor.DestroyTimer(self._repeatingtimer_id)
 764    #         self._repeatingtimer_id = None
 765    #     return self
 766
 767    def __iadd__(self, actors):
 768        self.add(actors)
 769        return self
 770
 771    def __isub__(self, actors):
 772        self.remove(actors)
 773        return self
 774
 775    def __enter__(self):
 776        # context manager like in "with Plotter() as plt:"
 777        return self
 778
 779    def __exit__(self, *args, **kwargs):
 780        # context manager like in "with Plotter() as plt:"
 781        self.close()
 782
 783    def process_events(self):
 784        if self.interactor:
 785            try:
 786                self.interactor.ProcessEvents()
 787            except AttributeError:
 788                pass
 789        return self
 790
 791    def at(self, nren, yren=None):
 792        """
 793        Select the current renderer number as an int.
 794        Can also use the [nx, ny] format.
 795        """
 796        if yren is not None:
 797            nren = (yren) * self.shape[1] + (nren)
 798            if nren < 0 or nren > len(self.renderers):
 799                vedo.logger.error(f"at({nren, yren}) is malformed!")
 800                raise RuntimeError
 801
 802        self.renderer = self.renderers[nren]
 803        self.camera = self.renderer.GetActiveCamera()
 804        return self
 805
 806
 807    def add(self, *actors, at=None):
 808        """
 809        Append the input objects to the internal list of actors to be shown.
 810        This method is typically used in loops or callback functions.
 811
 812        Arguments:
 813            at : (int)
 814                add the object at the specified renderer
 815        """
 816        if at is not None:
 817            ren = self.renderers[at]
 818        else:
 819            ren = self.renderer
 820
 821        actors = utils.flatten(actors)
 822        actors = self._scan_input(actors)
 823
 824        for a in actors:
 825            if isinstance(a, vtk.vtkInteractorObserver):
 826                a.add_to(self)
 827                continue
 828
 829            if a not in self.actors:
 830                self.actors.append(a)
 831
 832            if ren:
 833                ren.AddActor(a)
 834
 835                if hasattr(a, "rendered_at"):
 836                    ir = self.renderers.index(ren)
 837                    a.rendered_at.add(ir)
 838
 839                if hasattr(a, "scalarbar") and a.scalarbar:
 840                    ren.AddActor(a.scalarbar)
 841
 842                if hasattr(a, "_isfollower") and a._isfollower:  # set by mesh.follow_camera()
 843                    a.SetCamera(self.camera)
 844
 845        return self
 846
 847    def remove(self, *actors, at=None):
 848        """
 849        Remove input object to the internal list of actors to be shown.
 850        This method is typically used in loops or callback functions.
 851        Objects to be removed can be referenced by their assigned name.
 852
 853        Arguments:
 854            at : (int)
 855                remove the object at the specified renderer
 856        """
 857        if at is not None:
 858            ren = self.renderers[at]
 859        else:
 860            ren = self.renderer
 861
 862        actors = utils.flatten(actors)
 863
 864        actors_in_ren = None
 865
 866        actors_r = []
 867        for i, a in enumerate(actors):
 868
 869            if isinstance(a, vtk.vtkInteractorObserver):
 870                a.remove_from(self)
 871                continue ###
 872
 873            if isinstance(a, str):
 874                if actors_in_ren is None:
 875                    actors_in_ren = self.get_meshes(
 876                        include_non_pickables=True,
 877                        unpack_assemblies=False,
 878                    )
 879
 880                for b in set(self.actors + actors_in_ren):
 881                    if hasattr(b, "name") and a in b.name:
 882                        actors_r.append(b)
 883
 884            else:
 885                actors_r.append(a)
 886
 887        for a in set(actors_r):
 888            if ren:
 889                ren.RemoveActor(a)
 890                if hasattr(a, "rendered_at"):
 891                    ir = self.renderers.index(ren)
 892                    a.rendered_at.discard(ir)
 893                if hasattr(a, "scalarbar") and a.scalarbar:
 894                    ren.RemoveActor(a.scalarbar)
 895                if hasattr(a, "_caption") and a._caption:
 896                    ren.RemoveActor(a._caption)
 897                if hasattr(a, "shadows") and a.shadows:
 898                    for sha in a.shadows:
 899                        ren.RemoveActor(sha)
 900                if hasattr(a, "trail") and a.trail:
 901                    ren.RemoveActor(a.trail)
 902                    a.trail_points = []
 903                    if hasattr(a.trail, "shadows") and a.trail.shadows:
 904                        for sha in a.trail.shadows:
 905                            ren.RemoveActor(sha)
 906
 907            if a in self.actors:
 908                i = self.actors.index(a)
 909                del self.actors[i]
 910
 911        return self
 912
 913    def remove_lights(self):
 914        """Remove all the present lights in the current renderer."""
 915        if self.renderer:
 916            self.renderer.RemoveAllLights()
 917        return self
 918
 919    def pop(self, at=None):
 920        """
 921        Remove the last added object from the rendering window.
 922        This method is typically used in loops or callback functions.
 923        """
 924        if at is not None and not isinstance(at, int):
 925            # wrong usage pitfall
 926            vedo.logger.error("argument of pop() must be an integer")
 927            raise RuntimeError()
 928
 929        if self.actors:
 930            self.remove(self.actors[-1], at)
 931        return self
 932
 933    def render(self, resetcam=False):
 934        """Render the scene. This method is typically used in loops or callback functions."""
 935        if not self.window:
 936            return self
 937
 938        if self.wx_widget:
 939            if resetcam:
 940                self.renderer.ResetCamera()
 941            self.wx_widget.Render()
 942            return self
 943
 944        if self.qt_widget:
 945            if resetcam:
 946                self.renderer.ResetCamera()
 947            self.qt_widget.Render()
 948            return self
 949
 950        if self.interactor:
 951            if not self.interactor.GetInitialized():
 952                self.interactor.Initialize()
 953
 954        self.camera = self.renderer.GetActiveCamera()
 955        if resetcam:
 956            self.renderer.ResetCamera()
 957
 958        self.window.Render()
 959        return self
 960
 961    def interactive(self):
 962        """
 963        Start window interaction.
 964        Analogous to `show(..., interactive=True)`.
 965        """
 966        if self.interactor:
 967            self.interactor.Start()
 968        return self
 969
 970    def use_depth_peeling(self, at=None, value=True):
 971        """
 972        Specify whether use depth peeling algorithm at this specific renderer
 973        Call this method before the first rendering.
 974        """
 975        if at is None:
 976            ren = self.renderer
 977        else:
 978            ren = self.renderers[at]
 979        ren.SetUseDepthPeeling(value)
 980        return self
 981
 982    def background(self, c1=None, c2=None, at=None):
 983        """Set the color of the background for the current renderer.
 984        A different renderer index can be specified by keyword ``at``.
 985
 986        Arguments:
 987            c1 : (list)
 988                background main color.
 989            c2 : (list)
 990                background color for the upper part of the window.
 991            at : (int)
 992                renderer index.
 993        """
 994        if not self.renderers:
 995            return self
 996        if at is None:
 997            r = self.renderer
 998        else:
 999            r = self.renderers[at]
1000        if r:
1001            if c1 is not None:
1002                r.SetBackground(vedo.get_color(c1))
1003                self._bg = r.GetBackground()  # notebooks
1004            if c2 is not None:
1005                r.GradientBackgroundOn()
1006                r.SetBackground2(vedo.get_color(c2))
1007            else:
1008                r.GradientBackgroundOff()
1009        return self
1010
1011    ##################################################################
1012    def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True):
1013        """
1014        Return a list of Meshes from the specified renderer.
1015
1016        Arguments:
1017            at : (int)
1018                specify which renderer to look at.
1019            include_non_pickables : (bool)
1020                include non-pickable objects
1021            unpack_assemblies : (bool)
1022                unpack assemblies into their components
1023        """
1024        if at is None:
1025            renderer = self.renderer
1026            at = self.renderers.index(renderer)
1027        elif isinstance(at, int):
1028            renderer = self.renderers[at]
1029
1030        has_global_axes = False
1031        if isinstance(self.axes_instances[at], vedo.Assembly):
1032            has_global_axes = True
1033
1034        if unpack_assemblies:
1035            acs = renderer.GetActors()
1036        else:
1037            acs = renderer.GetViewProps()
1038
1039        actors = []
1040        acs.InitTraversal()
1041        for _ in range(acs.GetNumberOfItems()):
1042
1043            if unpack_assemblies:
1044                a = acs.GetNextItem()
1045            else:
1046                a = acs.GetNextProp()
1047
1048            if isinstance(a, vtk.vtkVolume):
1049                continue
1050
1051            if include_non_pickables or a.GetPickable():
1052                if a == self.axes_instances[at]:
1053                    continue
1054                if has_global_axes and a in self.axes_instances[at].actors:
1055                    continue
1056                actors.append(a)
1057        return actors
1058
1059    def get_volumes(self, at=None, include_non_pickables=False):
1060        """
1061        Return a list of Volumes from the specified renderer.
1062
1063        Arguments:
1064            at : (int)
1065                specify which renderer to look at
1066            include_non_pickables : (bool)
1067                include non-pickable objects
1068        """
1069        if at is None:
1070            renderer = self.renderer
1071            at = self.renderers.index(renderer)
1072        elif isinstance(at, int):
1073            renderer = self.renderers[at]
1074
1075        vols = []
1076        acs = renderer.GetVolumes()
1077        acs.InitTraversal()
1078        for _ in range(acs.GetNumberOfItems()):
1079            a = acs.GetNextItem()
1080            if include_non_pickables or a.GetPickable():
1081                vols.append(a)
1082        return vols
1083
1084    def reset_camera(self, tight=None):
1085        """
1086        Reset the camera position and zooming.
1087        If tight (float) is specified the zooming reserves a padding space in the xy-plane
1088        expressed in percent of the average size.
1089        """
1090        if tight is None:
1091            self.renderer.ResetCamera()
1092        else:
1093            x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds()
1094
1095            cam = self.renderer.GetActiveCamera()
1096
1097            self.renderer.ComputeAspect()
1098            aspect = self.renderer.GetAspect()
1099            angle = np.pi * cam.GetViewAngle() / 180.0
1100            dx, dy = (x1 - x0) * 0.999, (y1 - y0) * 0.999
1101            dist = max(dx / aspect[0], dy) / np.sin(angle / 2) / 2
1102
1103            cam.SetViewUp(0, 1, 0)
1104            cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight))
1105            cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0)
1106            if cam.GetParallelProjection():
1107                ps = max(dx / aspect[0], dy) / 2
1108                cam.SetParallelScale(ps * (1 + tight))
1109            self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1)
1110        return self
1111
1112    def reset_viewup(self, smooth=True):
1113        """
1114        Reset the orientation of the camera to the closest orthogonal direction and view-up.
1115        """
1116        vbb = addons.compute_visible_bounds()[0]
1117        x0, x1, y0, y1, z0, z1 = vbb
1118        mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2
1119        d = self.camera.GetDistance()
1120
1121        viewups = np.array([
1122            (0, 1, 0), ( 0, -1,  0),
1123            (0, 0, 1), ( 0,  0, -1),
1124            (1, 0, 0), (-1,  0,  0),
1125        ])
1126        positions = np.array([
1127            (mx, my, mz+d), (mx, my, mz-d),
1128            (mx, my+d, mz), (mx, my-d, mz),
1129            (mx+d, my, mz), (mx-d, my, mz),
1130        ])
1131
1132        vu = np.array(self.camera.GetViewUp())
1133        vui = np.argmin(np.linalg.norm(viewups - vu, axis=1))
1134
1135        poc = np.array(self.camera.GetPosition())
1136        foc = np.array(self.camera.GetFocalPoint())
1137        a = poc - foc
1138        b = positions - foc
1139        a = a / np.linalg.norm(a)
1140        b = b.T * (1 / np.linalg.norm(b, axis=1))
1141        pui = np.argmin(np.linalg.norm(b.T - a, axis=1))
1142
1143        if smooth:
1144            outtimes = np.linspace(0, 1, num=11, endpoint=True)
1145            for t in outtimes:
1146                vv = vu * (1 - t) + viewups[vui] * t
1147                pp = poc * (1 - t) + positions[pui] * t
1148                ff = foc * (1 - t) + np.array([mx, my, mz]) * t
1149                self.camera.SetViewUp(vv)
1150                self.camera.SetPosition(pp)
1151                self.camera.SetFocalPoint(ff)
1152                self.render()
1153
1154            # interpolator does not respect parallel view...:
1155            # cam1 = dict(
1156            #     pos=poc,
1157            #     viewup=vu,
1158            #     focal_point=(mx,my,mz),
1159            #     clipping_range=self.camera.GetClippingRange()
1160            # )
1161            # # cam1 = self.camera
1162            # cam2 = dict(
1163            #     pos=positions[pui],
1164            #     viewup=viewups[vui],
1165            #     focal_point=(mx,my,mz),
1166            #     clipping_range=self.camera.GetClippingRange()
1167            # )
1168            # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0)
1169            # for c in vcams:
1170            #     self.renderer.SetActiveCamera(c)
1171            #     self.render()
1172        else:
1173
1174            self.camera.SetViewUp(viewups[vui])
1175            self.camera.SetPosition(positions[pui])
1176            self.camera.SetFocalPoint(mx, my, mz)
1177
1178        self.renderer.ResetCameraClippingRange()
1179
1180        # vbb, _, _, _ = addons.compute_visible_bounds()
1181        # x0,x1, y0,y1, z0,z1 = vbb
1182        # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1)
1183        self.render()
1184        return self
1185
1186    def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()):
1187        """
1188        Takes as input two cameras set camera at an interpolated position:
1189
1190        Cameras can be vtkCamera or dictionaries in format:
1191
1192            `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)`
1193
1194        Press `shift-C` key in interactive mode to dump a python snipplet
1195        of parameters for the current camera view.
1196        """
1197        nc = len(cameras)
1198        if len(times) == 0:
1199            times = np.linspace(0, 1, num=nc, endpoint=True)
1200
1201        assert len(times) == nc
1202
1203        cin = vtk.vtkCameraInterpolator()
1204
1205        # cin.SetInterpolationTypeToLinear() # bugged?
1206        if nc > 2 and smooth:
1207            cin.SetInterpolationTypeToSpline()
1208
1209        for i, cam in enumerate(cameras):
1210            vcam = cam
1211            if isinstance(cam, dict):
1212                vcam = utils.camera_from_dict(cam)
1213            cin.AddCamera(times[i], vcam)
1214
1215        mint, maxt = cin.GetMinimumT(), cin.GetMaximumT()
1216        rng = maxt - mint
1217
1218        if len(output_times) == 0:
1219            cin.InterpolateCamera(t * rng, self.camera)
1220            self.renderer.SetActiveCamera(self.camera)
1221            return [self.camera]
1222        else:
1223            vcams = []
1224            for tt in output_times:
1225                c = vtk.vtkCamera()
1226                cin.InterpolateCamera(tt * rng, c)
1227                vcams.append(c)
1228            return vcams
1229
1230    def fly_to(self, point):
1231        """
1232        Fly camera to the specified point.
1233
1234        Arguments:
1235            point : (list)
1236                point in space to place camera.
1237
1238        Example:
1239            ```python
1240            from vedo import *
1241            cone = Cone()
1242            plt = Plotter(axes=1)
1243            plt.show(cone)
1244            plt.fly_to([1,0,0])
1245            plt.interactive().close()
1246            ```
1247        """
1248        if self.interactor:
1249            self.resetcam = False
1250            self.interactor.FlyTo(self.renderer, point)
1251            self.camera = self.renderer.GetActiveCamera()
1252        return self
1253
1254    def look_at(self, plane="xy"):
1255        """Move the camera so that it looks at the specified cartesian plane"""
1256        cam = self.renderer.GetActiveCamera()
1257        fp = np.array(cam.GetFocalPoint())
1258        p = np.array(cam.GetPosition())
1259        dist = np.linalg.norm(fp - p)
1260        plane = plane.lower()
1261        if "x" in plane and "y" in plane:
1262            cam.SetPosition(fp[0], fp[1], fp[2] + dist)
1263            cam.SetViewUp(0.0, 1.0, 0.0)
1264        elif "x" in plane and "z" in plane:
1265            cam.SetPosition(fp[0], fp[1] - dist, fp[2])
1266            cam.SetViewUp(0.0, 0.0, 1.0)
1267        elif "y" in plane and "z" in plane:
1268            cam.SetPosition(fp[0] + dist, fp[1], fp[2])
1269            cam.SetViewUp(0.0, 0.0, 1.0)
1270        else:
1271            vedo.logger.error(f"in plotter.look() cannot understand argument {plane}")
1272        return self
1273
1274    def record(self, filename=".vedo_recorded_events.log"):
1275        """
1276        Record camera, mouse, keystrokes and all other events.
1277        Recording can be toggled on/off by pressing key "R".
1278
1279        Arguments:
1280            filename : (str)
1281                ascii file to store events. The default is '.vedo_recorded_events.log'.
1282
1283        Returns:
1284            a string descriptor of events.
1285
1286        Examples:
1287            - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py)
1288        """
1289        erec = vtk.vtkInteractorEventRecorder()
1290        erec.SetInteractor(self.interactor)
1291        erec.SetFileName(filename)
1292        erec.SetKeyPressActivationValue("R")
1293        erec.EnabledOn()
1294        erec.Record()
1295        self.interactor.Start()
1296        erec.Stop()
1297        erec.EnabledOff()
1298        with open(filename, "r", encoding="UTF-8") as fl:
1299            events = fl.read()
1300        erec = None
1301        return events
1302
1303    def play(self, events=".vedo_recorded_events.log", repeats=0):
1304        """
1305        Play camera, mouse, keystrokes and all other events.
1306
1307        Arguments:
1308            events : (str)
1309                file o string of events. The default is '.vedo_recorded_events.log'.
1310            repeats : (int)
1311                number of extra repeats of the same events. The default is 0.
1312
1313        Examples:
1314            - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py)
1315        """
1316        erec = vtk.vtkInteractorEventRecorder()
1317        erec.SetInteractor(self.interactor)
1318
1319        if events.endswith(".log"):
1320            erec.ReadFromInputStringOff()
1321            erec.SetFileName(events)
1322        else:
1323            erec.ReadFromInputStringOn()
1324            erec.SetInputString(events)
1325
1326        erec.Play()
1327        for _i in range(repeats):
1328            erec.Rewind()
1329            erec.Play()
1330        erec.EnabledOff()
1331        erec = None
1332        return self
1333
1334    def parallel_projection(self, value=True, at=None):
1335        """
1336        Use parallel projection `at` a specified renderer.
1337        Object is seen from "infinite" distance, e.i. remove any perspective effects.
1338        An input value equal to -1 will toggle it on/off.
1339        """
1340        if at is not None:
1341            r = self.renderers[at]
1342        else:
1343            r = self.renderer
1344        if value == -1:
1345            val = r.GetActiveCamera().GetParallelProjection()
1346            value = not val
1347        r.GetActiveCamera().SetParallelProjection(value)
1348        r.Modified()
1349        return self
1350
1351    def fov(self, angle):
1352        """
1353        Set the field of view angle for the camera.
1354        This is the angle of the camera frustum in the horizontal direction.
1355        High values will result in a wide-angle lens (fish-eye effect),
1356        and low values will result in a telephoto lens.
1357
1358        Default value is 30 degrees.
1359        """
1360        self.renderer.GetActiveCamera().UseHorizontalViewAngleOn()
1361        self.renderer.GetActiveCamera().SetViewAngle(angle)
1362        return self
1363
1364    def zoom(self, zoom):
1365        """Apply a zooming factor for the current camera view"""
1366        self.renderer.GetActiveCamera().Zoom(zoom)
1367        return self
1368    
1369    def azimuth(self, angle):
1370        """Rotate camera around the view up vector."""
1371        self.renderer.GetActiveCamera().Azimuth(angle)
1372        return self
1373    
1374    def elevation(self, angle):
1375        """Rotate the camera around the cross product of the negative
1376        of the direction of projection and the view up vector."""
1377        self.renderer.GetActiveCamera().Elevation(angle)
1378        return self
1379    
1380    def roll(self, angle):
1381        """Roll the camera about the direction of projection."""
1382        self.renderer.GetActiveCamera().Roll(angle)
1383        return self
1384    
1385    def dolly(self, value):
1386        """Move the camera towards (value>0) or away from (value<0) the focal point."""
1387        self.renderer.GetActiveCamera().Dolly(value)
1388        return self
1389
1390
1391    ##################################################################
1392    def add_slider(
1393        self,
1394        sliderfunc: Callable,
1395        xmin,
1396        xmax,
1397        value=None,
1398        pos=4,
1399        title="",
1400        font="",
1401        title_size=1,
1402        c=None,
1403        alpha=1,
1404        show_value=True,
1405        delayed=False,
1406        **options,
1407    ):
1408        """
1409        Add a `vedo.addons.Slider2D` which can call an external custom function.
1410
1411        Arguments:
1412            sliderfunc : (Callable)
1413                external function to be called by the widget
1414            xmin : (float)
1415                lower value of the slider
1416            xmax : (float)
1417                upper value
1418            value : (float)
1419                current value
1420            pos : (list, str)
1421                position corner number: horizontal [1-5] or vertical [11-15]
1422                it can also be specified by corners coordinates [(x1,y1), (x2,y2)]
1423                and also by a string descriptor (eg. "bottom-left")
1424            title : (str)
1425                title text
1426            font : (str)
1427                title font face. Check [available fonts here](https://vedo.embl.es/fonts).
1428            title_size : (float)
1429                title text scale [1.0]
1430            show_value : (bool)
1431                if True current value is shown
1432            delayed : (bool)
1433                if True the callback is delayed until when the mouse button is released
1434            alpha : (float)
1435                opacity of the scalar bar texts
1436            slider_length : (float)
1437                slider length
1438            slider_width : (float)
1439                slider width
1440            end_cap_length : (float)
1441                length of the end cap
1442            end_cap_width : (float)
1443                width of the end cap
1444            tube_width : (float)
1445                width of the tube
1446            title_height : (float)
1447                width of the title
1448            tformat : (str)
1449                format of the title
1450
1451        Examples:
1452            - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py)
1453            - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py)
1454
1455            ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg)
1456        """
1457        if c is None:  # automatic black or white
1458            c = (0.8, 0.8, 0.8)
1459            if np.sum(vedo.get_color(self.backgrcol)) > 1.5:
1460                c = (0.2, 0.2, 0.2)
1461        else:
1462            c = vedo.get_color(c)
1463
1464        slider2d = addons.Slider2D(
1465            sliderfunc,
1466            xmin,
1467            xmax,
1468            value,
1469            pos,
1470            title,
1471            font,
1472            title_size,
1473            c,
1474            alpha,
1475            show_value,
1476            delayed,
1477            **options,
1478        )
1479
1480        if self.renderer:
1481            slider2d.renderer = self.renderer
1482            if self.interactor:
1483                slider2d.interactor = self.interactor
1484                slider2d.on()
1485                self.sliders.append([slider2d, sliderfunc])
1486        return slider2d
1487
1488
1489    def add_slider3d(
1490        self,
1491        sliderfunc,
1492        pos1,
1493        pos2,
1494        xmin,
1495        xmax,
1496        value=None,
1497        s=0.03,
1498        t=1,
1499        title="",
1500        rotation=0.0,
1501        c=None,
1502        show_value=True,
1503    ):
1504        """
1505        Add a 3D slider widget which can call an external custom function.
1506
1507        Arguments:
1508            sliderfunc : (function)
1509                external function to be called by the widget
1510            pos1 : (list)
1511                first position 3D coordinates
1512            pos2 : (list)
1513                second position coordinates
1514            xmin : (float)
1515                lower value
1516            xmax : (float)
1517                upper value
1518            value : (float)
1519                initial value
1520            s : (float)
1521                label scaling factor
1522            t : (float)
1523                tube scaling factor
1524            title : (str)
1525                title text
1526            c : (color)
1527                slider color
1528            rotation : (float)
1529                title rotation around slider axis
1530            show_value : (bool)
1531                if True current value is shown
1532
1533        Examples:
1534            - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py)
1535
1536            ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png)
1537        """
1538        if c is None:  # automatic black or white
1539            c = (0.8, 0.8, 0.8)
1540            if np.sum(vedo.get_color(self.backgrcol)) > 1.5:
1541                c = (0.2, 0.2, 0.2)
1542        else:
1543            c = vedo.get_color(c)
1544
1545        slider3d = addons.Slider3D(
1546            sliderfunc, pos1, pos2, xmin, xmax, value, s, t, title, rotation, c, show_value
1547        )
1548        slider3d.renderer = self.renderer
1549        slider3d.interactor = self.interactor
1550        slider3d.on()
1551        self.sliders.append([slider3d, sliderfunc])
1552        return slider3d
1553
1554
1555    def add_button(
1556        self,
1557        fnc=None,
1558        states=("On", "Off"),
1559        c=("w", "w"),
1560        bc=("green4", "red4"),
1561        pos=(0.7, 0.05),
1562        size=24,
1563        font=None,
1564        bold=False,
1565        italic=False,
1566        alpha=1,
1567        angle=0,
1568        name="Button",
1569    ):
1570        """
1571        Add a button to the renderer window.
1572
1573        Arguments:
1574            states : (list)
1575                a list of possible states, e.g. ['On', 'Off']
1576            c : (list)
1577                a list of colors for each state
1578            bc : (list)
1579                a list of background colors for each state
1580            pos : (list)
1581                2D position in pixels from left-bottom corner
1582            size : (float)
1583                size of button font
1584            font : (str)
1585                font type. Check [available fonts here](https://vedo.embl.es/fonts).
1586            bold : (bool)
1587                bold font face (False)
1588            italic : (bool)
1589                italic font face (False)
1590            alpha : (float)
1591                opacity level
1592            angle : (float)
1593                anticlockwise rotation in degrees
1594
1595        Returns:
1596            `vedo.addons.Button` object.
1597
1598        Examples:
1599            - [buttons.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons.py)
1600
1601            ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg)
1602        """
1603        if self.interactor:
1604            bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle, name)
1605            self.renderer.AddActor2D(bu)
1606            self.buttons.append(bu)
1607            bu.function_id = self.add_callback("LeftButtonPress", bu.function)
1608            return bu
1609
1610    def add_spline_tool(
1611        self, points, pc="k", ps=8, lc="r4", ac="g5", lw=2, closed=False, interactive=False
1612    ):
1613        """
1614        Add a spline tool to the current plotter.
1615        Nodes of the spline can be dragged in space with the mouse.
1616        Clicking on the line itself adds an extra point.
1617        Selecting a point and pressing del removes it.
1618
1619        Arguments:
1620            points : (Mesh, Points, array)
1621                the set of vertices forming the spline nodes.
1622            pc : (str)
1623                point color. The default is 'k'.
1624            ps : (str)
1625                point size. The default is 8.
1626            lc : (str)
1627                line color. The default is 'r4'.
1628            ac : (str)
1629                active point marker color. The default is 'g5'.
1630            lw : (int)
1631                line width. The default is 2.
1632            closed : (bool)
1633                spline is meant to be closed. The default is False.
1634
1635        Returns:
1636            a `SplineTool` object.
1637
1638        Examples:
1639            - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py)
1640
1641            ![](https://vedo.embl.es/images/basic/spline_tool.png)
1642        """
1643        sw = addons.SplineTool(points, pc, ps, lc, ac, lw, closed)
1644        if self.interactor:
1645            sw.SetInteractor(self.interactor)
1646        else:
1647            vedo.logger.error("in add_spline_tool(), No interactor found.")
1648            raise RuntimeError
1649        sw.On()
1650        sw.Initialize(sw.points.polydata())
1651        if sw.closed:
1652            sw.representation.ClosedLoopOn()
1653        sw.representation.SetRenderer(self.renderer)
1654        sw.representation.BuildRepresentation()
1655        sw.Render()
1656        if interactive:
1657            self.interactor.Start()
1658        else:
1659            self.interactor.Render()
1660        return sw
1661
1662    def add_icon(self, icon, pos=3, size=0.08):
1663        """Add an inset icon mesh into the same renderer.
1664
1665        Arguments:
1666            pos : (int, list)
1667                icon position in the range [1-4] indicating one of the 4 corners,
1668                or it can be a tuple (x,y) as a fraction of the renderer size.
1669            size : (float)
1670                size of the square inset.
1671
1672        Examples:
1673            - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py)
1674        """
1675        iconw = addons.Icon(icon, pos, size)
1676
1677        iconw.SetInteractor(self.interactor)
1678        iconw.EnabledOn()
1679        iconw.InteractiveOff()
1680        self.widgets.append(iconw)
1681        return iconw
1682
1683
1684    def add_global_axes(self, axtype=None, c=None):
1685        """Draw axes on scene. Available axes types:
1686
1687        Arguments:
1688            axtype : (int)
1689                - 0,  no axes,
1690                - 1,  draw three gray grid walls
1691                - 2,  show cartesian axes from (0,0,0)
1692                - 3,  show positive range of cartesian axes from (0,0,0)
1693                - 4,  show a triad at bottom left
1694                - 5,  show a cube at bottom left
1695                - 6,  mark the corners of the bounding box
1696                - 7,  draw a 3D ruler at each side of the cartesian axes
1697                - 8,  show the vtkCubeAxesActor object
1698                - 9,  show the bounding box outLine
1699                - 10, show three circles representing the maximum bounding box
1700                - 11, show a large grid on the x-y plane
1701                - 12, show polar axes
1702                - 13, draw a simple ruler at the bottom of the window
1703
1704            Axis type-1 can be fully customized by passing a dictionary axes=dict().
1705
1706        Example:
1707            ```python
1708            from vedo import Box, show
1709            b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1)
1710            show(
1711                b,
1712                axes={
1713                    "xtitle": "Some long variable [a.u.]",
1714                    "number_of_divisions": 4,
1715                    # ...
1716                },
1717            )
1718            ```
1719
1720        Examples:
1721            - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py)
1722            - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py)
1723            - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py)
1724            - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py)
1725
1726            <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600">
1727        """
1728        addons.add_global_axes(axtype, c)
1729        return self
1730
1731    def add_legend_box(self, **kwargs):
1732        """Add a legend to the top right.
1733
1734        Examples:
1735            - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py),
1736            - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py)
1737            - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py)
1738        """
1739        acts = self.get_meshes()
1740        lb = addons.LegendBox(acts, **kwargs)
1741        self.add(lb)
1742        return self
1743
1744    def add_hint(
1745        self,
1746        obj,
1747        text="",
1748        c="k",
1749        bc="yellow8",
1750        font="Calco",
1751        size=18,
1752        justify=0,
1753        angle=0,
1754        delay=100,
1755    ):
1756        """
1757        Create a pop-up hint style message when hovering an object.
1758        Use add_hint(False) to disable all hints.
1759
1760        Arguments:
1761            obj : (Mesh, Points)
1762                the object to associate the pop-up to
1763            text : (str)
1764                string description of the pop-up
1765            delay : (int)
1766                milliseconds to wait before pop-up occurs
1767        """
1768        if self.offscreen:
1769            return self
1770
1771        if vedo.vtk_version[0] == 9 and "Linux" in vedo.sys_platform:  # Linux vtk9 is bugged
1772            vedo.logger.warning("add_hint() is not available on Linux platforms.")
1773            return self
1774
1775        if obj is False:
1776            self.hint_widget.EnabledOff()
1777            self.hint_widget = None
1778            return self
1779
1780        if text is False and self.hint_widget:
1781            self.hint_widget.RemoveBalloon(obj)
1782            return self
1783
1784        if text == "":
1785            if obj.name:
1786                text = obj.name
1787            elif obj.filename:
1788                text = obj.filename
1789            else:
1790                return self
1791
1792        if not self.hint_widget:
1793            self.hint_widget = vtk.vtkBalloonWidget()
1794
1795            rep = vtk.vtkBalloonRepresentation()
1796            rep.SetBalloonLayoutToImageRight()
1797
1798            trep = rep.GetTextProperty()
1799            trep.SetFontFamily(vtk.VTK_FONT_FILE)
1800            trep.SetFontFile(utils.get_font_path(font))
1801            trep.SetFontSize(size)
1802            trep.SetColor(vedo.get_color(c))
1803            trep.SetBackgroundColor(vedo.get_color(bc))
1804            trep.SetShadow(False)
1805            trep.SetJustification(justify)
1806            trep.UseTightBoundingBoxOn()
1807
1808            self.hint_widget.ManagesCursorOff()
1809            self.hint_widget.SetTimerDuration(delay)
1810            self.hint_widget.SetInteractor(self.interactor)
1811            if angle:
1812                rep.SetOrientation(angle)
1813                rep.SetBackgroundOpacity(0)
1814            self.hint_widget.SetRepresentation(rep)
1815            self.widgets.append(self.hint_widget)
1816            if self.interactor.GetInitialized():
1817                self.hint_widget.EnabledOn()
1818            else:
1819                vedo.logger.warning("add_hint() must be called after show(). Skip.")
1820                return self
1821
1822        bst = self.hint_widget.GetBalloonString(obj)
1823        if bst:
1824            self.hint_widget.UpdateBalloonString(obj, text)
1825        else:
1826            self.hint_widget.AddBalloon(obj, text)
1827
1828        return self
1829
1830
1831    def add_shadows(self):
1832        """Add shadows at the current renderer."""
1833        shadows = vtk.vtkShadowMapPass()
1834        seq = vtk.vtkSequencePass()
1835        passes = vtk.vtkRenderPassCollection()
1836        passes.AddItem(shadows.GetShadowMapBakerPass())
1837        passes.AddItem(shadows)
1838        seq.SetPasses(passes)
1839        camerapass = vtk.vtkCameraPass()
1840        camerapass.SetDelegatePass(seq)
1841        self.renderer.SetPass(camerapass)
1842        return self
1843
1844    def add_ambient_occlusion(self, radius, bias=0.01, blur=True, samples=100):
1845        """
1846        Screen Space Ambient Occlusion.
1847
1848        For every pixel on the screen, the pixel shader samples the depth values around
1849        the current pixel and tries to compute the amount of occlusion from each of the sampled
1850        points.
1851
1852        Arguments:
1853            radius : (float)
1854                radius of influence in absolute units
1855            bias : (float)
1856                bias of the normals
1857            blur : (bool)
1858                add a blurring to the sampled positions
1859            samples : (int)
1860                number of samples to probe
1861
1862        Examples:
1863            - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py)
1864
1865            ![](https://vedo.embl.es/images/basic/ssao.jpg)
1866        """
1867        lights = vtk.vtkLightsPass()
1868
1869        opaque = vtk.vtkOpaquePass()
1870
1871        ssaoCam = vtk.vtkCameraPass()
1872        ssaoCam.SetDelegatePass(opaque)
1873
1874        ssao = vtk.vtkSSAOPass()
1875        ssao.SetRadius(radius)
1876        ssao.SetBias(bias)
1877        ssao.SetBlur(blur)
1878        ssao.SetKernelSize(samples)
1879        ssao.SetDelegatePass(ssaoCam)
1880
1881        translucent = vtk.vtkTranslucentPass()
1882
1883        volpass = vtk.vtkVolumetricPass()
1884        ddp = vtk.vtkDualDepthPeelingPass()
1885        ddp.SetTranslucentPass(translucent)
1886        ddp.SetVolumetricPass(volpass)
1887
1888        over = vtk.vtkOverlayPass()
1889
1890        collection = vtk.vtkRenderPassCollection()
1891        collection.AddItem(lights)
1892        collection.AddItem(ssao)
1893        collection.AddItem(ddp)
1894        collection.AddItem(over)
1895
1896        sequence = vtk.vtkSequencePass()
1897        sequence.SetPasses(collection)
1898
1899        cam = vtk.vtkCameraPass()
1900        cam.SetDelegatePass(sequence)
1901
1902        self.renderer.SetPass(cam)
1903        return self
1904
1905    def add_depth_of_field(self, autofocus=True):
1906        """Add a depth of field effect in the scene."""
1907        lights = vtk.vtkLightsPass()
1908
1909        opaque = vtk.vtkOpaquePass()
1910
1911        dofCam = vtk.vtkCameraPass()
1912        dofCam.SetDelegatePass(opaque)
1913
1914        dof = vtk.vtkDepthOfFieldPass()
1915        dof.SetAutomaticFocalDistance(autofocus)
1916        dof.SetDelegatePass(dofCam)
1917
1918        collection = vtk.vtkRenderPassCollection()
1919        collection.AddItem(lights)
1920        collection.AddItem(dof)
1921
1922        sequence = vtk.vtkSequencePass()
1923        sequence.SetPasses(collection)
1924
1925        cam = vtk.vtkCameraPass()
1926        cam.SetDelegatePass(sequence)
1927
1928        self.renderer.SetPass(cam)
1929        return self
1930
1931    def _add_skybox(self, hdrfile):
1932        # many hdr files are at https://polyhaven.com/all
1933
1934        if utils.vtk_version_at_least(9):
1935            reader = vtk.vtkHDRReader()
1936            # Check the image can be read.
1937            if not reader.CanReadFile(hdrfile):
1938                vedo.logger.error(f"Cannot read HDR file {hdrfile}")
1939                return self
1940            reader.SetFileName(hdrfile)
1941            reader.Update()
1942
1943            texture = vtk.vtkTexture()
1944            texture.SetColorModeToDirectScalars()
1945            texture.SetInputData(reader.GetOutput())
1946
1947            # Convert to a cube map
1948            tcm = vtk.vtkEquirectangularToCubeMapTexture()
1949            tcm.SetInputTexture(texture)
1950            # Enable mipmapping to handle HDR image
1951            tcm.MipmapOn()
1952            tcm.InterpolateOn()
1953
1954            self.renderer.SetEnvironmentTexture(tcm)
1955            self.renderer.UseImageBasedLightingOn()
1956            self.skybox = vtk.vtkSkybox()
1957            self.skybox.SetTexture(tcm)
1958            self.renderer.AddActor(self.skybox)
1959
1960        else:
1961            vedo.logger.error("add_skybox not supported in this VTK version. Skip.")
1962
1963        return self
1964
1965    def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None):
1966        """
1967        Add a frame to the renderer subwindow.
1968
1969        Arguments:
1970            c : (color)
1971                color name or index
1972            alpha : (float)
1973                opacity level
1974            lw : (int)
1975                line width in pixels.
1976            padding : (float)
1977                padding space in pixels.
1978        """
1979        if c is None:  # automatic black or white
1980            c = (0.9, 0.9, 0.9)
1981            if np.sum(vedo.plotter_instance.renderer.GetBackground()) > 1.5:
1982                c = (0.1, 0.1, 0.1)
1983        renf = addons.RendererFrame(c, alpha, lw, padding)
1984        self.renderer.AddActor(renf)
1985        return self
1986
1987    def add_hover_legend(
1988        self,
1989        at=None,
1990        c=None,
1991        pos="bottom-left",
1992        font="Calco",
1993        s=0.75,
1994        bg="auto",
1995        alpha=0.1,
1996        maxlength=24,
1997        use_info=False,
1998    ):
1999        """
2000        Add a legend with 2D text which is triggered by hovering the mouse on an object.
2001
2002        The created text object are stored in plotter.hover_legends
2003
2004        Arguments:
2005            c : (color)
2006                Text color. If None then black or white is chosen automatically
2007            pos : (str)
2008                text positioning
2009            font : (str)
2010                text font type. Check [available fonts here](https://vedo.embl.es/fonts).
2011            s : (float)
2012                text size scale
2013            bg : (color)
2014                background color of the 2D box containing the text
2015            alpha : (float)
2016                box transparency
2017            maxlength : (int)
2018                maximum number of characters per line
2019            use_info : (bool)
2020                visualize the content of the `obj.info` attribute
2021
2022        Examples:
2023            - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py)
2024            - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py)
2025
2026            ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg)
2027        """
2028        hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg)
2029
2030        if at is None:
2031            at = self.renderers.index(self.renderer)
2032
2033        def _legfunc(evt):
2034            if not evt.actor or not self.renderer or at != evt.at:
2035                if hoverlegend._mapper.GetInput():  # clear and return
2036                    hoverlegend._mapper.SetInput("")
2037                    self.interactor.Render()
2038                return
2039
2040            if use_info:
2041                if hasattr(evt.actor, "info"):
2042                    t = str(evt.actor.info)
2043                else:
2044                    return
2045            else:
2046                t, tp = "", ""
2047                if evt.isMesh:
2048                    tp = "Mesh "
2049                elif evt.isPoints:
2050                    tp = "Points "
2051                # elif evt.isVolume:
2052                #     tp = "Volume "
2053                elif evt.isPicture:
2054                    tp = "Pict "
2055                elif evt.isAssembly:
2056                    tp = "Assembly "
2057                else:
2058                    return
2059
2060                if evt.isAssembly:
2061                    if not evt.actor.name:
2062                        t += f"Assembly object of {len(evt.actor.unpack())} parts\n"
2063                    else:
2064                        t += f"Assembly name: {evt.actor.name} ({len(evt.actor.unpack())} parts)\n"
2065                else:
2066                    if evt.actor.name:
2067                        t += f"{tp}name"
2068                        if evt.isPoints:
2069                            t += "  "
2070                        if evt.isMesh:
2071                            t += "  "
2072                        t += f": {evt.actor.name[:maxlength]}".ljust(maxlength) + "\n"
2073
2074                if evt.actor.filename:
2075                    t += f"{tp}filename: "
2076                    t += f"{os.path.basename(evt.actor.filename[-maxlength:])}".ljust(maxlength)
2077                    t += "\n"
2078                    if not evt.actor.file_size:
2079                        evt.actor.file_size, evt.actor.created = vedo.file_io.file_info(evt.actor.filename)
2080                    if evt.actor.file_size:
2081                        t += "             : "
2082                        sz, created = evt.actor.file_size, evt.actor.created
2083                        t += f"{created[4:-5]} ({sz})" + "\n"
2084
2085                if evt.isPoints:
2086                    indata = evt.actor.polydata(False)
2087                    if indata.GetNumberOfPoints():
2088                        t += (
2089                            f"#points/cells: {indata.GetNumberOfPoints()}"
2090                            f" / {indata.GetNumberOfCells()}"
2091                        )
2092                    pdata = indata.GetPointData()
2093                    cdata = indata.GetCellData()
2094                    if pdata.GetScalars() and pdata.GetScalars().GetName():
2095                        t += f"\nPoint array  : {pdata.GetScalars().GetName()}"
2096                        if pdata.GetScalars().GetName() == evt.actor.mapper().GetArrayName():
2097                            t += " *"
2098                    if cdata.GetScalars() and cdata.GetScalars().GetName():
2099                        t += f"\nCell  array  : {cdata.GetScalars().GetName()}"
2100                        if cdata.GetScalars().GetName() == evt.actor.mapper().GetArrayName():
2101                            t += " *"
2102
2103                if evt.isPicture:
2104                    t = f"{os.path.basename(evt.actor.filename[:maxlength+10])}".ljust(maxlength+10)
2105                    t += f"\nImage shape: {evt.actor.shape}"
2106                    pcol = self.color_picker(evt.picked2d)
2107                    t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}"
2108
2109            # change box color if needed in 'auto' mode
2110            if evt.isPoints and "auto" in str(bg):
2111                actcol = evt.actor.GetProperty().GetColor()
2112                if hoverlegend._mapper.GetTextProperty().GetBackgroundColor() != actcol:
2113                    hoverlegend._mapper.GetTextProperty().SetBackgroundColor(actcol)
2114
2115            # adapt to changes in bg color
2116            bgcol = self.renderers[at].GetBackground()
2117            _bgcol = c
2118            if _bgcol is None:  # automatic black or white
2119                _bgcol = (0.9, 0.9, 0.9)
2120                if sum(bgcol) > 1.5:
2121                    _bgcol = (0.1, 0.1, 0.1)
2122                if len(set(_bgcol).intersection(bgcol)) < 3:
2123                    hoverlegend.color(_bgcol)
2124
2125            if hoverlegend._mapper.GetInput() != t:
2126                hoverlegend._mapper.SetInput(t)
2127                self.interactor.Render()
2128
2129        self.add(hoverlegend, at=at)
2130        self.hover_legends.append(hoverlegend)
2131        self.add_callback("MouseMove", _legfunc)
2132        return self
2133
2134
2135    #####################################################################
2136    def add_scale_indicator(
2137        self, pos=(0.7, 0.05), s=0.02, length=2, lw=4, c="k1", alpha=1, units="", gap=0.05
2138    ):
2139        """
2140        Add a Scale Indicator. Only works in parallel mode (no perspective).
2141
2142        Arguments:
2143            pos : (list)
2144                fractional (x,y) position on the screen.
2145            s : (float)
2146                size of the text.
2147            length : (float)
2148                length of the line.
2149            units : (str)
2150                string to show units.
2151            gap : (float)
2152                separation of line and text.
2153
2154        Example:
2155            ```python
2156            from vedo import settings, Cube, Plotter
2157            settings.use_parallel_projection = True # or else it does not make sense!
2158            cube = Cube().alpha(0.2)
2159            plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)'))
2160            plt.add_scale_indicator(units='um', c='blue4')
2161            plt.show(cube, "Scale indicator with units").close()
2162            ```
2163            ![](https://vedo.embl.es/images/feats/scale_indicator.png)
2164        """
2165        ppoints = vtk.vtkPoints()  # Generate the polyline
2166        psqr = [[0.0, gap], [length / 10, gap]]
2167        dd = psqr[1][0] - psqr[0][0]
2168        for i, pt in enumerate(psqr):
2169            ppoints.InsertPoint(i, pt[0], pt[1], 0)
2170        lines = vtk.vtkCellArray()
2171        lines.InsertNextCell(len(psqr))
2172        for i in range(len(psqr)):
2173            lines.InsertCellPoint(i)
2174        pd = vtk.vtkPolyData()
2175        pd.SetPoints(ppoints)
2176        pd.SetLines(lines)
2177
2178        wsx, wsy = self.window.GetSize()
2179        if not settings.use_parallel_projection:
2180            vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.")
2181            return None
2182
2183        rlabel = vtk.vtkVectorText()
2184        rlabel.SetText("scale")
2185        tf = vtk.vtkTransformPolyDataFilter()
2186        tf.SetInputConnection(rlabel.GetOutputPort())
2187        t = vtk.vtkTransform()
2188        t.Scale(s * wsy / wsx, s, 1)
2189        tf.SetTransform(t)
2190
2191        app = vtk.vtkAppendPolyData()
2192        app.AddInputConnection(tf.GetOutputPort())
2193        app.AddInputData(pd)
2194
2195        mapper = vtk.vtkPolyDataMapper2D()
2196        mapper.SetInputConnection(app.GetOutputPort())
2197        cs = vtk.vtkCoordinate()
2198        cs.SetCoordinateSystem(1)
2199        mapper.SetTransformCoordinate(cs)
2200
2201        fractor = vtk.vtkActor2D()
2202        csys = fractor.GetPositionCoordinate()
2203        csys.SetCoordinateSystem(3)
2204        fractor.SetPosition(pos)
2205        fractor.SetMapper(mapper)
2206        fractor.GetProperty().SetColor(vedo.get_color(c))
2207        fractor.GetProperty().SetOpacity(alpha)
2208        fractor.GetProperty().SetLineWidth(lw)
2209        fractor.GetProperty().SetDisplayLocationToForeground()
2210
2211        def sifunc(iren, ev):
2212            wsx, wsy = self.window.GetSize()
2213            ps = self.camera.GetParallelScale()
2214            newtxt = utils.precision(ps / wsy * wsx * length * dd, 3)
2215            if units:
2216                newtxt += " " + units
2217            if rlabel.GetText() != newtxt:
2218                rlabel.SetText(newtxt)
2219
2220        self.renderer.AddActor(fractor)
2221        self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc)
2222        self.interactor.AddObserver("MouseWheelForwardEvent", sifunc)
2223        self.interactor.AddObserver("InteractionEvent", sifunc)
2224        sifunc(0, 0)
2225        return fractor
2226
2227    def fill_event(self, ename="", pos=()):
2228        """
2229        Create an Event object.
2230
2231        A 2D screen-position can be provided to be picked.
2232        """
2233        if not self.interactor:
2234            return Event()
2235
2236        if len(pos):
2237            x, y = pos
2238            self.interactor.SetEventPosition(pos)
2239        else:
2240            x, y = self.interactor.GetEventPosition()
2241        self.renderer = self.interactor.FindPokedRenderer(x, y)
2242        if not self.picker:
2243            self.picker = vtk.vtkPropPicker()
2244        self.picked2d = (x, y)
2245        self.picker.PickProp(x, y, self.renderer)
2246        xp, yp = self.interactor.GetLastEventPosition()
2247        actor = self.picker.GetProp3D()
2248        delta3d = np.array([0, 0, 0])
2249        if actor:
2250            picked3d = np.array(self.picker.GetPickPosition())
2251            if isinstance(actor, vedo.base.Base3DProp):  # needed!
2252                if actor.picked3d is not None:
2253                    delta3d = picked3d - actor.picked3d
2254            actor.picked3d = picked3d
2255        else:
2256            picked3d = None
2257
2258        if not actor:  # try 2D
2259            actor = self.picker.GetActor2D()
2260
2261        dx, dy = x - xp, y - yp
2262
2263        key = self.interactor.GetKeySym()
2264
2265        if key:
2266            if "_L" in key or "_R" in key:
2267                # skip things like Shift_R
2268                key = ""  # better than None
2269            else:
2270                if self.interactor.GetShiftKey():
2271                    key = key.upper()
2272
2273                if key == "MINUS":  # fix: vtk9 is ignoring shift chars..
2274                    key = "underscore"
2275                elif key == "EQUAL":  # fix: vtk9 is ignoring shift chars..
2276                    key = "plus"
2277                elif key == "SLASH":  # fix: vtk9 is ignoring shift chars..
2278                    key = "?"
2279
2280                if self.interactor.GetControlKey():
2281                    key = "Ctrl+" + key
2282
2283                if self.interactor.GetAltKey():
2284                    key = "Alt+" + key
2285
2286        event = Event()
2287        event.name = ename
2288        event.title = self.title
2289        event.id = -1  # will be set by the timer wrapper function
2290        event.timerid = -1  # will be set by the timer wrapper function
2291        event.priority = -1  # will be set by the timer wrapper function
2292        event.time = time.time()
2293        event.at = self.renderers.index(self.renderer)
2294        event.actor = actor
2295        event.picked3d = picked3d
2296        event.keyPressed = key  # obsolete, will disappear. Use "keypress"
2297        event.keypress = key
2298        event.picked2d = (x, y)
2299        event.delta2d = (dx, dy)
2300        event.angle2d = np.arctan2(dy, dx)
2301        event.speed2d = np.sqrt(dx * dx + dy * dy)
2302        event.delta3d = delta3d
2303        event.speed3d = np.sqrt(np.dot(delta3d, delta3d))
2304        event.isPoints = isinstance(actor, vedo.Points)
2305        event.isMesh = isinstance(actor, vedo.Mesh)
2306        event.isAssembly = isinstance(actor, vedo.Assembly)
2307        event.isVolume = isinstance(actor, vedo.Volume)
2308        event.isPicture = isinstance(actor, vedo.Picture)
2309        event.isActor2D = isinstance(actor, vtk.vtkActor2D)
2310        return event
2311
2312
2313    def add_callback(self, event_name, func, priority=0.0):
2314        """
2315        Add a function to be executed while show() is active.
2316        Information about the event can be acquired with method getEvent().
2317
2318        Return a unique id for the callback.
2319
2320        The callback function (see example below) exposes a dictionary
2321        with the following information:
2322        - `name`: event name,
2323        - `id`: event unique identifier,
2324        - `priority`: event priority (float),
2325        - `interactor`: the interactor object,
2326        - `at`: renderer nr. where the event occurred
2327        - `actor`: object picked by the mouse
2328        - `picked3d`: point picked in world coordinates
2329        - `keypress`: key pressed as string
2330        - `picked2d`: screen coords of the mouse pointer
2331        - `delta2d`: shift wrt previous position (to calculate speed, direction)
2332        - `delta3d`: ...same but in 3D world coords
2333        - `angle2d`: angle of mouse movement on screen
2334        - `speed2d`: speed of mouse movement on screen
2335        - `speed3d`: speed of picked point in world coordinates
2336        - `isPoints`: True if of class
2337        - `isMesh`: True if of class
2338        - `isAssembly`: True if of class
2339        - `isVolume`: True if of class Volume
2340        - `isPicture`: True if of class
2341
2342        Frequently used events are:
2343        - `KeyPress`, `KeyRelease`: listen to keyboard events
2344        - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks
2345        - `MiddleButtonPress`, `MiddleButtonRelease`
2346        - `RightButtonPress`, `RightButtonRelease`
2347        - `MouseMove`: listen to mouse pointer changing position
2348        - `MouseWheelForward`, `MouseWheelBackward`
2349        - `Enter`, `Leave`: listen to mouse entering or leaving the window
2350        - `Pick`, `StartPick`, `EndPick`: listen to object picking
2351        - `ResetCamera`, `ResetCameraClippingRange`
2352        - `Error`, `Warning`
2353        - `Char`
2354        - `Timer`
2355
2356        Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html).
2357
2358        Example:
2359            ```python
2360            from vedo import *
2361
2362            def func(evt):
2363                # this function is called every time the mouse moves
2364                # (evt is a dotted dictionary)
2365                if not evt.actor:
2366                    return  # no hit, return
2367                print("point coords =", evt.picked3d)
2368                # print("full event dump:", evt)
2369
2370            elli = Ellipsoid()
2371            plt = Plotter(axes=1)
2372            plt.add_callback('mouse hovering', func)
2373            plt.show(elli).close()
2374            ```
2375
2376        Examples:
2377            - [spline_draw.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw.py)
2378            - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py)
2379
2380                ![](https://vedo.embl.es/images/advanced/spline_draw.png)
2381
2382            - ..and many others!
2383        """
2384        from vtkmodules.util.misc import calldata_type
2385
2386        if not self.interactor:
2387            return None
2388
2389        # as vtk names are ugly and difficult to remember:
2390        ln = event_name.lower()
2391        if "click" in ln or "button" in ln:
2392            event_name = "LeftButtonPress"
2393            if "right" in ln:
2394                event_name = "RightButtonPress"
2395            elif "mid" in ln:
2396                event_name = "MiddleButtonPress"
2397            if "release" in ln:
2398                # event_name = event_name.replace("Press","Release") # vtk bug
2399                event_name = "EndInteraction"
2400        else:
2401            if "key" in ln:
2402                if "release" in ln:
2403                    event_name = "KeyRelease"
2404                else:
2405                    event_name = "KeyPress"
2406
2407        if ("mouse" in ln and "mov" in ln) or "over" in ln:
2408            event_name = "MouseMove"
2409        if "timer" in ln:
2410            event_name = "Timer"
2411
2412        if not event_name.endswith("Event"):
2413            event_name += "Event"
2414
2415        @calldata_type(vtk.VTK_INT)
2416        def _func_wrap(iren, ename, timerid=None):
2417            event = self.fill_event(ename=ename)
2418            event.timerid = timerid
2419            event.id = cid
2420            event.priority = priority
2421            self.last_event = event
2422            func(event)
2423            return  ## _func_wrap
2424
2425        # Not compatible with ProcessEvents()
2426        if "MouseMove" in event_name or "Timer" in event_name:
2427            settings.allow_interaction = False
2428
2429        cid = self.interactor.AddObserver(event_name, _func_wrap, priority)
2430        vedo.logger.debug(f"registering event: {event_name} with id={cid}")
2431        return cid
2432
2433    def remove_callback(self, cid):
2434        """
2435        Remove a callback function by its id
2436        or a whole category of callbacks by their name.
2437
2438        Arguments:
2439            cid : (int, str)
2440                Unique id of the callback.
2441                If an event name is passed all callbacks of that type are removed.
2442        """
2443        if self.interactor:
2444            if isinstance(cid, str):
2445                # as vtk names are ugly and difficult to remember:
2446                ln = cid.lower()
2447                if "click" in ln or "button" in ln:
2448                    cid = "LeftButtonPress"
2449                    if "right" in ln:
2450                        cid = "RightButtonPress"
2451                    elif "mid" in ln:
2452                        cid = "MiddleButtonPress"
2453                    if "release" in ln:
2454                        cid.replace("Press", "Release")
2455                else:
2456                    if "key" in ln:
2457                        if "release" in ln:
2458                            cid = "KeyRelease"
2459                        else:
2460                            cid = "KeyPress"
2461                if ("mouse" in ln and "mov" in ln) or "over" in ln:
2462                    cid = "MouseMove"
2463                if "timer" in ln:
2464                    cid = "Timer"
2465                if not cid.endswith("Event"):
2466                    cid += "Event"
2467                self.interactor.RemoveObservers(cid)
2468            else:
2469                self.interactor.RemoveObserver(cid)
2470        return self
2471
2472    def timer_callback(self, action, timer_id=None, dt=1, one_shot=False):
2473        """
2474        Start or stop an existing timer.
2475
2476        Arguments:
2477            action : (str)
2478                Either "create"/"start" or "destroy"/"stop"
2479            timer_id : (int)
2480                When stopping the timer, the ID of the timer as returned when created
2481            dt : (int)
2482                time in milliseconds between each repeated call
2483            one_shot : (bool)
2484                create a one shot timer of prescribed duration instead of a repeating one
2485
2486        Examples:
2487            - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py)
2488            - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py)
2489
2490            ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg)
2491        """
2492        if action in ("create", "start"):
2493            if timer_id is not None:
2494                vedo.logger.warning("you set a timer_id but it will be ignored.")
2495            if one_shot:
2496                timer_id = self.interactor.CreateOneShotTimer(dt)
2497            else:
2498                timer_id = self.interactor.CreateRepeatingTimer(dt)
2499            return timer_id
2500
2501        elif action in ("destroy", "stop"):
2502            if timer_id is not None:
2503                self.interactor.DestroyTimer(timer_id)
2504            else:
2505                vedo.logger.warning("please set a timer_id. Cannot stop timer.")
2506        else:
2507            e = f"in timer_callback(). Cannot understand action: {action}\n"
2508            e += " allowed actions are: ['start', 'stop']. Skipped."
2509            vedo.logger.error(e)
2510        return timer_id
2511
2512    def compute_world_coordinate(
2513        self, pos2d, at=None, objs=(), bounds=(), offset=None, pixeltol=None, worldtol=None
2514    ):
2515        """
2516        Transform a 2D point on the screen into a 3D point inside the rendering scene.
2517        If a set of meshes is passed then points are placed onto these.
2518
2519        Arguments:
2520            pos2d : (list)
2521                2D screen coordinates point.
2522            at : (int)
2523                renderer number.
2524            objs : (list)
2525                list of Mesh objects to project the point onto.
2526            bounds : (list)
2527                specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax].
2528            offset : (float)
2529                specify an offset value.
2530            pixeltol : (int)
2531                screen tolerance in pixels.
2532            worldtol : (float)
2533                world coordinates tolerance.
2534
2535        Returns:
2536            numpy array, the point in 3D world coordinates.
2537
2538        Examples:
2539            - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py)
2540            - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py)
2541
2542            ![](https://vedo.embl.es/images/basic/mousehover3.jpg)
2543        """
2544        if at is not None:
2545            renderer = self.renderers[at]
2546        else:
2547            renderer = self.renderer
2548
2549        if not objs:
2550            pp = vtk.vtkFocalPlanePointPlacer()
2551        else:
2552            pp = vtk.vtkPolygonalSurfacePointPlacer()
2553            for ob in objs:
2554                pp.AddProp(ob)
2555
2556        if len(bounds) == 6:
2557            pp.SetPointBounds(bounds)
2558        if pixeltol:
2559            pp.SetPixelTolerance(pixeltol)
2560        if worldtol:
2561            pp.SetWorldTolerance(worldtol)
2562        if offset:
2563            pp.SetOffset(offset)
2564
2565        worldPos = [0, 0, 0]
2566        worldOrient = [0, 0, 0, 0, 0, 0, 0, 0, 0]
2567        pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient)
2568        # validw = pp.ValidateWorldPosition(worldPos, worldOrient)
2569        # validd = pp.ValidateDisplayPosition(renderer, pos2d)
2570        return np.array(worldPos)
2571
2572    def compute_screen_coordinates(self, obj, full_window=False):
2573        """
2574        Given a 3D points in the current renderer (or full window),
2575        find the screen pixel coordinates.
2576
2577        Example:
2578            ```python
2579            from vedo import *
2580
2581            elli = Ellipsoid().rotate_y(30)
2582
2583            plt = Plotter()
2584            plt.show(elli)
2585
2586            xyscreen = plt.compute_screen_positions(elli)
2587            print('xyscreen coords:', xyscreen)
2588
2589            # simulate an event happening at one point
2590            event = plt.fill_event(pos=xyscreen[123])
2591            print(event)
2592            ```
2593        """
2594        if isinstance(obj, vedo.base.Base3DProp):
2595            pts = obj.points()
2596        elif utils.is_sequence(obj):
2597            pts = obj
2598        p2d = []
2599        cs = vtk.vtkCoordinate()
2600        cs.SetCoordinateSystemToWorld()
2601        cs.SetViewport(self.renderer)
2602        for p in pts:
2603            cs.SetValue(p)
2604            if full_window:
2605                p2d.append(cs.GetComputedDisplayValue(self.renderer))
2606            else:
2607                p2d.append(cs.GetComputedViewportValue(self.renderer))
2608        return np.array(p2d, dtype=int)
2609    
2610    def pick_area(self, pos1, pos2, at=None):
2611        """
2612        Pick all objects within a box defined by two corner points in 2D screen coordinates.
2613        
2614        Returns a frustum Mesh that contains the visible field of view.
2615        This can be used to select objects in a scene or select vertices.
2616
2617        Example:
2618            ```python
2619            from vedo import *
2620
2621            settings.enable_default_mouse_callbacks = False
2622
2623            def mode_select(objs):
2624                print("Selected objects:", objs)
2625                d0 = mode.start_x, mode.start_y # display coords
2626                d1 = mode.end_x, mode.end_y
2627
2628                frustum = plt.pick_area(d0, d1)
2629                infru = frustum.inside_points(mesh)
2630                col = np.random.randint(0, 10)
2631                infru.ps(10).c(col)
2632                plt.add(frustum, infru).render()
2633
2634            mesh = Mesh(dataurl+"cow.vtk").c("k5").lw(1)
2635
2636            mode = interactor_modes.BlenderStyle()
2637            mode.callback_select = mode_select
2638
2639            plt = Plotter().user_mode(mode)
2640            plt.show(mesh, axes=1)
2641            ```
2642        """
2643        if at is not None:
2644            ren = self.renderers[at]
2645        else:
2646            ren = self.renderer
2647        area_picker = vtk.vtkAreaPicker()
2648        area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren)
2649        planes = area_picker.GetFrustum()
2650
2651        fru = vtk.vtkFrustumSource()
2652        fru.SetPlanes(planes)
2653        fru.ShowLinesOff()
2654        fru.Update()
2655
2656        afru = vedo.Mesh(fru.GetOutput())
2657        afru.alpha(0.1).lw(1).pickable(False)
2658        afru.name = "Frustrum"
2659        return afru
2660
2661
2662    def _scan_input(self, wannabeacts):
2663        # scan the input of show
2664        if not utils.is_sequence(wannabeacts):
2665            wannabeacts = [wannabeacts]
2666
2667        scannedacts = []
2668        for a in wannabeacts:  # scan content of list
2669
2670            if a is None:
2671                pass
2672
2673            elif isinstance(a, vtk.vtkActor):
2674
2675                scannedacts.append(a)
2676
2677                if isinstance(a, vedo.base.BaseActor):
2678                    if a.shadows:
2679                        # a.update_shadows()
2680                        scannedacts.extend(a.shadows)
2681
2682                    if a.trail and a.trail not in self.actors:
2683                        # a.update_trail()
2684                        scannedacts.append(a.trail)
2685                        # trails may also have shadows:
2686                        if a.trail.shadows:
2687                            # a.trail.update_shadows()
2688                            scannedacts.extend(a.trail.shadows)
2689
2690                    if a._caption and a._caption not in self.actors:
2691                        scannedacts.append(a._caption)
2692
2693            elif isinstance(a, vtk.vtkActor2D):
2694                scannedacts.append(a)
2695
2696            elif isinstance(a, vtk.vtkAssembly):
2697                scannedacts.append(a)
2698                if a.trail and a.trail not in self.actors:
2699                    scannedacts.append(a.trail)
2700
2701            elif isinstance(a, (vedo.Volume, vedo.VolumeSlice)):
2702                scannedacts.append(a)
2703
2704            elif isinstance(a, vtk.vtkImageData):
2705                scannedacts.append(vedo.Volume(a))
2706
2707            elif isinstance(a, vedo.TetMesh):
2708                # check ugrid is all made of tets
2709                ugrid = a.inputdata()
2710                uarr = ugrid.GetCellTypesArray()
2711                celltypes = np.unique(utils.vtk2numpy(uarr))
2712                ncelltypes = len(celltypes)
2713                if ncelltypes > 1 or (ncelltypes == 1 and celltypes[0] != 10):
2714                    scannedacts.append(a.tomesh())
2715                else:
2716                    if not ugrid.GetPointData().GetScalars():
2717                        if not ugrid.GetCellData().GetScalars():
2718                            # add dummy array for vtkProjectedTetrahedraMapper to work:
2719                            a.celldata["DummyOneArray"] = np.ones(a.ncells)
2720                    scannedacts.append(a)
2721
2722            elif isinstance(a, vedo.UGrid):
2723                scannedacts.append(a.tomesh())
2724
2725            elif isinstance(a, vtk.vtkVolume):  # order matters! dont move above TetMesh
2726                vvol = vedo.Volume(a.GetMapper().GetInput())
2727                vprop = vtk.vtkVolumeProperty()
2728                vprop.DeepCopy(a.GetProperty())
2729                vvol.SetProperty(vprop)
2730                scannedacts.append(vvol)
2731
2732            elif isinstance(a, str):
2733                # assume a 2D comment was given
2734                changed = False  # check if one already exists so to just update text
2735                if self.renderer:  # might be jupyter
2736                    acs = self.renderer.GetActors2D()
2737                    acs.InitTraversal()
2738                    for i in range(acs.GetNumberOfItems()):
2739                        act = acs.GetNextItem()
2740                        if isinstance(act, vedo.shapes.Text2D):
2741                            aposx, aposy = act.GetPosition()
2742                            if aposx < 0.01 and aposy > 0.99:  # "top-left"
2743                                act.text(a)  # update content! no appending nada
2744                                changed = True
2745                                break
2746                    if not changed:
2747                        out = vedo.shapes.Text2D(a)  # append a new one
2748                        scannedacts.append(out)
2749
2750            elif isinstance(a, vtk.vtkImageActor):
2751                scannedacts.append(a)
2752
2753            elif isinstance(a, vtk.vtkBillboardTextActor3D):
2754                scannedacts.append(a)
2755
2756            elif isinstance(a, vtk.vtkLight):
2757                self.renderer.AddLight(a)
2758
2759            elif isinstance(a, vtk.vtkMultiBlockDataSet):
2760                for i in range(a.GetNumberOfBlocks()):
2761                    b = a.GetBlock(i)
2762                    if isinstance(b, vtk.vtkPolyData):
2763                        scannedacts.append(vedo.Mesh(b))
2764                    elif isinstance(b, vtk.vtkImageData):
2765                        scannedacts.append(vedo.Volume(b))
2766
2767            elif "PolyData" in str(type(a)):  # assume pyvista or vtkPolydata
2768                scannedacts.append(vedo.Mesh(a))
2769
2770            elif "dolfin" in str(type(a)):  # assume a dolfin.Mesh object
2771                import vedo.dolfin as dlf
2772                scannedacts.append(dlf.MeshActor(a))
2773
2774            elif "trimesh" in str(type(a)):
2775                scannedacts.append(utils.trimesh2vedo(a))
2776
2777            elif "meshlab" in str(type(a)):
2778                if "MeshSet" in str(type(a)):
2779                    for i in range(a.number_meshes()):
2780                        if a.mesh_id_exists(i):
2781                            scannedacts.append(vedo.Mesh(utils.meshlab2vedo(a.mesh(i))))
2782                else:
2783                    scannedacts.append(vedo.Mesh(utils.meshlab2vedo(a)))
2784
2785            elif isinstance(a, (vtk.vtkProp, vtk.vtkInteractorObserver)):
2786                scannedacts.append(a)
2787
2788            else:
2789                vedo.logger.error(f"cannot understand input in show(): {type(a)}")
2790        return scannedacts
2791
2792
2793    def show(
2794        self,
2795        *actors,
2796        at=None,
2797        axes=None,
2798        resetcam=None,
2799        zoom=False,
2800        interactive=None,
2801        viewup="",
2802        azimuth=0.0,
2803        elevation=0.0,
2804        roll=0.0,
2805        camera=None,
2806        mode=0,
2807        rate=None,
2808        bg=None,
2809        bg2=None,
2810        size=None,
2811        title=None,
2812    ):
2813        """
2814        Render a list of objects.
2815
2816        Arguments:
2817            at : (int)
2818                number of the renderer to plot to, in case of more than one exists
2819
2820            axes : (int)
2821                axis type-1 can be fully customized by passing a dictionary.
2822                Check `addons.Axes()` for the full list of options.
2823                set the type of axes to be shown:
2824                - 0,  no axes
2825                - 1,  draw three gray grid walls
2826                - 2,  show cartesian axes from (0,0,0)
2827                - 3,  show positive range of cartesian axes from (0,0,0)
2828                - 4,  show a triad at bottom left
2829                - 5,  show a cube at bottom left
2830                - 6,  mark the corners of the bounding box
2831                - 7,  draw a 3D ruler at each side of the cartesian axes
2832                - 8,  show the `vtkCubeAxesActor` object
2833                - 9,  show the bounding box outLine
2834                - 10, show three circles representing the maximum bounding box
2835                - 11, show a large grid on the x-y plane
2836                - 12, show polar axes
2837                - 13, draw a simple ruler at the bottom of the window
2838
2839            azimuth/elevation/roll : (float)
2840                move camera accordingly the specified value
2841
2842            viewup: str, list
2843                either `['x', 'y', 'z']` or a vector to set vertical direction
2844
2845            resetcam : (bool)
2846                re-adjust camera position to fit objects
2847
2848            camera : (dict, vtkCamera)
2849                camera parameters can further be specified with a dictionary
2850                assigned to the ``camera`` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`):
2851                - pos, `(list)`,  the position of the camera in world coordinates
2852                - focal_point `(list)`, the focal point of the camera in world coordinates
2853                - viewup `(list)`, the view up direction for the camera
2854                - distance `(float)`, set the focal point to the specified distance from the camera position.
2855                - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection.
2856                - parallel_scale `(float)`,
2857                scaling used for a parallel projection, i.e. the height of the viewport
2858                in world-coordinate distances. The default is 1. Note that the "scale" parameter works as
2859                an "inverse scale", larger numbers produce smaller images.
2860                This method has no effect in perspective projection mode.
2861
2862                - thickness `(float)`,
2863                set the distance between clipping planes. This method adjusts the far clipping
2864                plane to be set a distance 'thickness' beyond the near clipping plane.
2865
2866                - view_angle `(float)`,
2867                the camera view angle, which is the angular height of the camera view
2868                measured in degrees. The default angle is 30 degrees.
2869                This method has no effect in parallel projection mode.
2870                The formula for setting the angle up for perfect perspective viewing is:
2871                angle = 2*atan((h/2)/d) where h is the height of the RenderWindow
2872                (measured by holding a ruler up to your screen) and d is the distance
2873                from your eyes to the screen.
2874
2875            interactive : (bool)
2876                pause and interact with window (True) or continue execution (False)
2877
2878            rate : (float)
2879                maximum rate of `show()` in Hertz
2880
2881            mode : (int, str)
2882                set the type of interaction:
2883                - 0 = TrackballCamera [default]
2884                - 1 = TrackballActor
2885                - 2 = JoystickCamera
2886                - 3 = JoystickActor
2887                - 4 = Flight
2888                - 5 = RubberBand2D
2889                - 6 = RubberBand3D
2890                - 7 = RubberBandZoom
2891                - 8 = Terrain
2892                - 9 = Unicam
2893                - 10 = Image
2894                - Check out `vedo.interaction_modes` for more options.
2895        """
2896        if self.wx_widget:
2897            return self
2898
2899        if self.renderers:  # in case of notebooks
2900
2901            if at is None:
2902                at = self.renderers.index(self.renderer)
2903
2904            else:
2905
2906                if at >= len(self.renderers):
2907                    t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist"
2908                    vedo.logger.error(t)
2909                    return self
2910
2911                self.renderer = self.renderers[at]
2912
2913        if title is not None:
2914            self.title = title
2915
2916        if size is not None:
2917            self.size = size
2918            if self.size[0] == "f":  # full screen
2919                self.size = "fullscreen"
2920                self.window.SetFullScreen(True)
2921                self.window.BordersOn()
2922            else:
2923                self.window.SetSize(int(self.size[0]), int(self.size[1]))
2924
2925        if settings.default_backend == "vtk":
2926            if str(bg).endswith(".hdr"):
2927                self._add_skybox(bg)
2928            else:
2929                if bg is not None:
2930                    self.backgrcol = vedo.get_color(bg)
2931                    self.renderer.SetBackground(self.backgrcol)
2932                if bg2 is not None:
2933                    self.renderer.GradientBackgroundOn()
2934                    self.renderer.SetBackground2(vedo.get_color(bg2))
2935
2936        if axes is not None:
2937            if isinstance(axes, vedo.Assembly):  # user passing show(..., axes=myaxes)
2938                actors = list(actors)
2939                actors.append(axes)  # move it into the list of normal things to show
2940                axes = 0
2941            self.axes = axes
2942
2943        if self.offscreen:
2944            interactive = False
2945            self._interactive = False
2946
2947        # camera stuff
2948        if resetcam is not None:
2949            self.resetcam = resetcam
2950
2951        if camera is not None:
2952            self.resetcam = False
2953            if isinstance(camera, vtk.vtkCamera):
2954                self.camera = camera
2955            else:
2956                self.camera = utils.camera_from_dict(camera)
2957            if self.renderer:
2958                self.renderer.SetActiveCamera(self.camera)
2959
2960        if self.renderer:
2961            self.camera = self.renderer.GetActiveCamera()
2962
2963        self.add(actors)
2964
2965        # Backend ###############################################################
2966        if settings.default_backend != "vtk":
2967            if settings.default_backend in ["k3d"]:
2968                return backends.get_notebook_backend(self.actors)
2969        #########################################################################
2970
2971        for ia in utils.flatten(actors):
2972            if isinstance(ia, vedo.base.Base3DProp):
2973                try:
2974                    # fix gray color labels and title to white or black
2975                    ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor())
2976                    if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05:
2977                        c = (0.9, 0.9, 0.9)
2978                        if np.sum(self.renderer.GetBackground()) > 1.5:
2979                            c = (0.1, 0.1, 0.1)
2980                        ia.scalarbar.GetLabelTextProperty().SetColor(c)
2981                        ia.scalarbar.GetTitleTextProperty().SetColor(c)
2982                except AttributeError:
2983                    pass
2984
2985        if self.sharecam:
2986            for r in self.renderers:
2987                r.SetActiveCamera(self.camera)
2988
2989        if self.qt_widget is not None:
2990            self.qt_widget.GetRenderWindow().AddRenderer(self.renderer)
2991
2992
2993        if self.axes is not None:
2994            if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict):
2995                bns = self.renderer.ComputeVisiblePropBounds()
2996                addons.add_global_axes(self.axes, bounds=bns)
2997
2998        # Backend ###############################################################
2999        if settings.default_backend in ["ipyvtk", "trame"]:
3000            return backends.get_notebook_backend()
3001        #########################################################################
3002
3003        if self.resetcam:
3004            self.renderer.ResetCamera()
3005
3006        if len(self.renderers) > 1:
3007            self.add_renderer_frame()
3008
3009        if settings.default_backend == "2d" and not zoom:
3010            zoom = "tightest"
3011
3012        if zoom:
3013            if zoom == "tight":
3014                self.reset_camera(tight=0.04)
3015            elif zoom == "tightest":
3016                self.reset_camera(tight=0.0001)
3017            else:
3018                self.camera.Zoom(zoom)
3019        if elevation:
3020            self.camera.Elevation(elevation)
3021        if azimuth:
3022            self.camera.Azimuth(azimuth)
3023        if roll:
3024            self.camera.Roll(roll)
3025
3026        if len(viewup) > 0:
3027            b = self.renderer.ComputeVisiblePropBounds()
3028            cm = np.array([(b[1] + b[0]) / 2, (b[3] + b[2]) / 2, (b[5] + b[4]) / 2])
3029            sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])])
3030            if viewup == "x":
3031                sz = np.linalg.norm(sz)
3032                self.camera.SetViewUp([1, 0, 0])
3033                self.camera.SetPosition(cm + sz)
3034            elif viewup == "y":
3035                sz = np.linalg.norm(sz)
3036                self.camera.SetViewUp([0, 1, 0])
3037                self.camera.SetPosition(cm + sz)
3038            elif viewup == "z":
3039                sz = np.array([(b[1] - b[0]) * 0.7, -(b[3] - b[2]) * 1.0, (b[5] - b[4]) * 1.2])
3040                self.camera.SetViewUp([0, 0, 1])
3041                self.camera.SetPosition(cm + 2 * sz)
3042            elif utils.is_sequence(viewup):
3043                sz = np.linalg.norm(sz)
3044                self.camera.SetViewUp(viewup)
3045                cpos = np.cross([0, 1, 0], viewup)
3046                self.camera.SetPosition(cm - 2 * sz * cpos)
3047
3048        self.renderer.ResetCameraClippingRange()
3049
3050        if self.interactor and not self.interactor.GetInitialized():
3051            self.interactor.Initialize()
3052            self.interactor.RemoveObservers("CharEvent")
3053
3054        if settings.immediate_rendering:
3055            self.window.Render()  ##################### <-------------- Render
3056
3057        # 2d ####################################################################
3058        if settings.default_backend == "2d":
3059            return backends.get_notebook_backend()
3060        #########################################################################
3061
3062        self.window.SetWindowName(self.title)
3063
3064        try:
3065            # Needs "pip install pyobjc" on Mac OSX
3066            if (
3067                self._cocoa_initialized is False
3068                and "Darwin" in vedo.sys_platform
3069                and not self.offscreen
3070            ):
3071                self._cocoa_initialized = True
3072                from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps
3073                pid = os.getpid()
3074                x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid))
3075                x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps)
3076        except:
3077            pass
3078            # vedo.logger.debug("On Mac OSX try: pip install pyobjc")
3079
3080        if self.interactor:  # can be offscreen..
3081
3082            if interactive is not None:
3083                self._interactive = interactive
3084
3085            self.user_mode(mode)
3086
3087            if self._interactive:
3088                self.interactor.Start()
3089                
3090            if rate:
3091                if self.clock is None:  # set clock and limit rate
3092                    self._clockt0 = time.time()
3093                    self.clock = 0.0
3094                else:
3095                    t = time.time() - self._clockt0
3096                    elapsed = t - self.clock
3097                    mint = 1.0 / rate
3098                    if elapsed < mint:
3099                        time.sleep(mint - elapsed)
3100                    self.clock = time.time() - self._clockt0
3101
3102        return self
3103
3104
3105    def add_inset(self, *actors, **options):
3106        """Add a draggable inset space into a renderer.
3107
3108        Arguments:
3109            at : (int)
3110                specify the renderer number
3111            pos : (list)
3112                icon position in the range [1-4] indicating one of the 4 corners,
3113                or it can be a tuple (x,y) as a fraction of the renderer size.
3114            size : (float)
3115                size of the square inset
3116            draggable : (bool)
3117                if True the subrenderer space can be dragged around
3118            c : (color)
3119                color of the inset frame when dragged
3120
3121        Examples:
3122            - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py)
3123
3124            ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg)
3125        """
3126        if not self.interactor:
3127            return None
3128        pos = options.pop("pos", 0)
3129        size = options.pop("size", 0.1)
3130        c = options.pop("c", "lb")
3131        at = options.pop("at", None)
3132        draggable = options.pop("draggable", True)
3133
3134        if not self.renderer:
3135            vedo.logger.warning("call add_inset() only after first rendering of the scene.")
3136            save_int = self._interactive
3137            self.show(interactive=0)
3138            self._interactive = save_int
3139        widget = vtk.vtkOrientationMarkerWidget()
3140        r, g, b = vedo.get_color(c)
3141        widget.SetOutlineColor(r, g, b)
3142        if len(actors) == 1:
3143            widget.SetOrientationMarker(actors[0])
3144        else:
3145            widget.SetOrientationMarker(vedo.Assembly(actors))
3146
3147        widget.SetInteractor(self.interactor)
3148
3149        if utils.is_sequence(pos):
3150            widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size)
3151        else:
3152            if pos < 2:
3153                widget.SetViewport(0, 1 - 2 * size, size * 2, 1)
3154            elif pos == 2:
3155                widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1)
3156            elif pos == 3:
3157                widget.SetViewport(0, 0, size * 2, size * 2)
3158            elif pos == 4:
3159                widget.SetViewport(1 - 2 * size, 0, 1, size * 2)
3160        widget.EnabledOn()
3161        widget.SetInteractive(draggable)
3162        if at is not None and at < len(self.renderers):
3163            widget.SetCurrentRenderer(self.renderers[at])
3164        self.widgets.append(widget)
3165        return widget
3166
3167    def clear(self, at=None, deep=False):
3168        """Clear the scene from all meshes and volumes."""
3169        if at is not None:
3170            renderer = self.renderers[at]
3171        else:
3172            renderer = self.renderer
3173        if not renderer:
3174            return self
3175
3176        if deep:
3177            renderer.RemoveAllViewProps()
3178        else:
3179            for a in set(self.get_meshes() + self.get_volumes() + self.actors + self.axes_instances):
3180                if isinstance(a, vedo.shapes.Text2D):
3181                    continue
3182                self.remove(a)
3183                try:
3184                    if a.scalarbar:
3185                        self.remove(a.scalarbar)
3186                except AttributeError:
3187                    pass
3188            self.actors = []
3189        return self
3190
3191    def break_interaction(self):
3192        """Break window interaction and return to the python execution flow"""
3193        if self.interactor:
3194            self.interactor.ExitCallback()
3195        return self
3196
3197    def user_mode(self, mode):
3198        """
3199        Modify the user interaction mode.
3200
3201        Examples:
3202            ```python
3203            from vedo import *
3204            mode = interactor_modes.MousePan()
3205            mesh = Mesh(dataurl+"cow.vtk")
3206            plt = Plotter().user_mode(mode)
3207            plt.show(mesh, axes=1)
3208           ```
3209        See also:
3210        [interactors](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html)
3211        """
3212        if not self.interactor:
3213            return None
3214
3215        if isinstance(mode, (str, int)):
3216            # Set the style of interaction
3217            # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html
3218            if mode in (0, "TrackballCamera"):
3219                if self.qt_widget:
3220                    self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
3221            elif mode in (1, "TrackballActor"):
3222                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleTrackballActor())
3223            elif mode in (2, "JoystickCamera"):
3224                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleJoystickCamera())
3225            elif mode in (3, "JoystickActor"):
3226                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleJoystickActor())
3227            elif mode in (4, "Flight"):
3228                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleFlight())
3229            elif mode in (5, "RubberBand2D"):
3230                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleRubberBand2D())
3231            elif mode in (6, "RubberBand3D"):
3232                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleRubberBand3D())
3233            elif mode in (7, "RubberBandZoom"):
3234                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleRubberBandZoom())
3235            elif mode in (8, "Terrain"):
3236                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleTerrain())
3237            elif mode in (9, "Unicam"):
3238                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleUnicam())
3239            elif mode in (10, "Image", "image", "2d"):
3240                astyle = vtk.vtkInteractorStyleImage()
3241                astyle.SetInteractionModeToImage3D()
3242                self.interactor.SetInteractorStyle(astyle)
3243            else:
3244                vedo.logger.warning(f"Unknown interaction mode: {mode}")
3245
3246        elif isinstance(mode, vtk.vtkInteractorStyleUser):
3247            # set a custom interactor style
3248            mode.interactor = self.interactor
3249            mode.renderer = self.renderer
3250            mode.SetInteractor(self.interactor)
3251            mode.SetDefaultRenderer(self.renderer)
3252            self.interactor.SetInteractorStyle(mode)
3253
3254        return self
3255
3256    def close_window(self):
3257        """Close the current or the input rendering window.
3258
3259        Examples:
3260            - [closewindow.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/closewindow.py)
3261        """
3262        vedo.last_figure = None
3263        self.sliders = []
3264        self.buttons = []
3265        self.widgets = []
3266        self.hover_legends = []
3267        self.background_renderer = None
3268        self._extralight = None
3269
3270        self.hint_widget = None
3271        self.cutter_widget = None
3272
3273        for r in self.renderers:
3274            r.RemoveAllObservers()
3275        if hasattr(self, "window") and self.window:
3276            if hasattr(self, "interactor") and self.interactor:
3277                self.interactor.ExitCallback()
3278                try:
3279                    self.interactor.SetDone(True)
3280                except AttributeError:
3281                    pass
3282                self.interactor.TerminateApp()
3283
3284                # self.interactor = None
3285            self.window.Finalize()  # this must be done here
3286
3287            if hasattr(self, "interactor") and self.interactor:
3288                if "Darwin" in vedo.sys_platform:
3289                    try:
3290                        self.interactor.ProcessEvents()
3291                    except:
3292                        pass
3293                self.interactor = None
3294
3295            self.window = None
3296
3297        self.renderer = None  # current renderer
3298        self.renderers = []
3299        self.camera = None
3300        self.skybox = None
3301        return self
3302
3303    def close(self):
3304        """Close the Plotter instance and release resources."""
3305        self.close_window()
3306        self.actors = []
3307        if vedo.plotter_instance == self:
3308            vedo.plotter_instance = None
3309
3310    def screenshot(self, filename="screenshot.png", scale=1, asarray=False):
3311        """
3312        Take a screenshot of the Plotter window.
3313
3314        Arguments:
3315            scale : (int)
3316                set image magnification as an integer multiplicating factor
3317            asarray : (bool)
3318                return a numpy array of the image instead of writing a file
3319        """
3320        return vedo.file_io.screenshot(filename, scale, asarray)
3321
3322    def topicture(self, scale=1):
3323        """
3324        Generate a Picture object from the current rendering window.
3325
3326        Arguments:
3327            scale : (int)
3328                set image magnification as an integer multiplicating factor
3329        """
3330        if settings.screeshot_large_image:
3331            w2if = vtk.vtkRenderLargeImage()
3332            w2if.SetInput(self.renderer)
3333            w2if.SetMagnification(scale)
3334        else:
3335            w2if = vtk.vtkWindowToImageFilter()
3336            w2if.SetInput(self.window)
3337            if hasattr(w2if, "SetScale"):
3338                w2if.SetScale(scale, scale)
3339            if settings.screenshot_transparent_background:
3340                w2if.SetInputBufferTypeToRGBA()
3341            w2if.ReadFrontBufferOff()  # read from the back buffer
3342        w2if.Update()
3343        return vedo.picture.Picture(w2if.GetOutput())
3344
3345    def export(self, filename="scene.npz", binary=False):
3346        """
3347        Export scene to file to HTML, X3D or Numpy file.
3348
3349        Examples:
3350            - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py)
3351            - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py)
3352        """
3353        vedo.file_io.export_window(filename, binary=binary)
3354        return self
3355
3356    def color_picker(self, xy, verbose=False):
3357        """Pick color of specific (x,y) pixel on the screen."""
3358        w2if = vtk.vtkWindowToImageFilter()
3359        w2if.SetInput(self.window)
3360        w2if.ReadFrontBufferOff()
3361        w2if.Update()
3362        nx, ny = self.window.GetSize()
3363        varr = w2if.GetOutput().GetPointData().GetScalars()
3364
3365        arr = utils.vtk2numpy(varr).reshape(ny, nx, 3)
3366        x, y = int(xy[0]), int(xy[1])
3367        if y < ny and x < nx:
3368
3369            rgb = arr[y, x]
3370
3371            if verbose:
3372                vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="")
3373                vedo.printc("█", c=[rgb[0], 0, 0], end="")
3374                vedo.printc("█", c=[0, rgb[1], 0], end="")
3375                vedo.printc("█", c=[0, 0, rgb[2]], end="")
3376                vedo.printc("] = ", end="")
3377                cnm = vedo.get_color_name(rgb)
3378                if np.sum(rgb) < 150:
3379                    vedo.printc(
3380                        rgb.tolist(),
3381                        vedo.colors.rgb2hex(np.array(rgb) / 255),
3382                        c="w",
3383                        bc=rgb,
3384                        invert=1,
3385                        end="",
3386                    )
3387                    vedo.printc("  -> " + cnm, invert=1, c="w")
3388                else:
3389                    vedo.printc(
3390                        rgb.tolist(), vedo.colors.rgb2hex(np.array(rgb) / 255), c=rgb, end=""
3391                    )
3392                    vedo.printc("  -> " + cnm, c=cnm)
3393
3394            return rgb
3395
3396        return None
3397
3398    #######################################################################
3399    def _mouseleftclick(self, iren, event):
3400
3401        x, y = iren.GetEventPosition()
3402
3403        renderer = iren.FindPokedRenderer(x, y)
3404        picker = vtk.vtkPropPicker()
3405        picker.PickProp(x, y, renderer)
3406
3407        self.renderer = renderer
3408
3409        clicked_actor = picker.GetActor()
3410        # clicked_actor2D = picker.GetActor2D()
3411
3412        # print('_mouseleftclick mouse at', x, y)
3413        # print("picked Volume:",   [picker.GetVolume()])
3414        # print("picked Actor2D:",  [picker.GetActor2D()])
3415        # print("picked Assembly:", [picker.GetAssembly()])
3416        # print("picked Prop3D:",   [picker.GetProp3D()])
3417
3418        if not clicked_actor:
3419            clicked_actor = picker.GetAssembly()
3420
3421        if not clicked_actor:
3422            clicked_actor = picker.GetProp3D()
3423
3424        if not hasattr(clicked_actor, "GetPickable") or not clicked_actor.GetPickable():
3425            return
3426
3427        self.picked3d = picker.GetPickPosition()
3428        self.picked2d = np.array([x, y])
3429
3430        if not clicked_actor:
3431            return
3432
3433        self.justremoved = None
3434
3435        self.clicked_actor = clicked_actor
3436        if hasattr(clicked_actor, "picked3d"):  # might be not a vedo obj
3437            clicked_actor.picked3d = picker.GetPickPosition()
3438            x, y = iren.GetEventPosition()
3439
3440        # -----------
3441        if "Histogram1D" in picker.GetAssembly().__class__.__name__:
3442            histo = picker.GetAssembly()
3443            if histo.verbose:
3444                x = self.picked3d[0]
3445                idx = np.digitize(x, histo.edges) - 1
3446                f = histo.frequencies[idx]
3447                cn = histo.centers[idx]
3448                vedo.colors.printc(f"{histo.name}, bin={idx}, center={cn}, value={f}")
3449
3450
3451    #######################################################################
3452    def _keypress(self, iren, event):
3453
3454        # NB: qt creates and passes a vtkGenericRenderWindowInteractor
3455
3456        key = iren.GetKeySym()
3457
3458        if "_L" in key or "_R" in key:
3459            return
3460
3461        if iren.GetShiftKey():
3462            key = key.upper()
3463
3464        if iren.GetControlKey():
3465            key = "Ctrl+" + key
3466
3467        if iren.GetAltKey():
3468            key = "Alt+" + key
3469
3470        # utils.vedo.printc('Pressed key:', key, c='y', box='-')
3471        # print(key, iren.GetShiftKey(), iren.GetAltKey(), iren.GetControlKey(),
3472        #       iren.GetKeyCode(), iren.GetRepeatCount())
3473        # iren.ExitCallback()
3474        # return
3475
3476        x, y = iren.GetEventPosition()
3477        renderer = iren.FindPokedRenderer(x, y)
3478
3479        if key in ["q", "Ctrl+q", "Ctrl+w", "Escape"]:
3480            iren.ExitCallback()
3481            return
3482
3483        elif key == "F1":
3484            vedo.logger.info("Execution aborted. Exiting python kernel now.")
3485            iren.ExitCallback()
3486            sys.exit(0)
3487
3488        elif key == "Down":
3489            if self.clicked_actor in self.get_meshes():
3490                self.clicked_actor.GetProperty().SetOpacity(0.02)
3491                bfp = self.clicked_actor.GetBackfaceProperty()
3492                if bfp and hasattr(self.clicked_actor, "_bfprop"):
3493                    self.clicked_actor._bfprop = bfp  # save it
3494                    self.clicked_actor.SetBackfaceProperty(None)
3495            else:
3496                for a in self.get_meshes():
3497                    a.GetProperty().SetOpacity(0.02)
3498                    bfp = a.GetBackfaceProperty()
3499                    if bfp and hasattr(a, "_bfprop"):
3500                        a._bfprop = bfp
3501                        a.SetBackfaceProperty(None)
3502
3503        elif key == "Left":
3504            if self.clicked_actor in self.get_meshes():
3505                ap = self.clicked_actor.GetProperty()
3506                aal = max([ap.GetOpacity() * 0.75, 0.01])
3507                ap.SetOpacity(aal)
3508                bfp = self.clicked_actor.GetBackfaceProperty()
3509                if bfp and hasattr(self.clicked_actor, "_bfprop"):
3510                    self.clicked_actor._bfprop = bfp
3511                    self.clicked_actor.SetBackfaceProperty(None)
3512            else:
3513                for a in self.get_meshes():
3514                    ap = a.GetProperty()
3515                    aal = max([ap.GetOpacity() * 0.75, 0.01])
3516                    ap.SetOpacity(aal)
3517                    bfp = a.GetBackfaceProperty()
3518                    if bfp and hasattr(a, "_bfprop"):
3519                        a._bfprop = bfp
3520                        a.SetBackfaceProperty(None)
3521
3522        elif key == "Right":
3523            if self.clicked_actor in self.get_meshes():
3524                ap = self.clicked_actor.GetProperty()
3525                aal = min([ap.GetOpacity() * 1.25, 1.0])
3526                ap.SetOpacity(aal)
3527                if (
3528                    aal == 1
3529                    and hasattr(self.clicked_actor, "_bfprop")
3530                    and self.clicked_actor._bfprop
3531                ):
3532                    # put back
3533                    self.clicked_actor.SetBackfaceProperty(self.clicked_actor._bfprop)
3534            else:
3535                for a in self.get_meshes():
3536                    ap = a.GetProperty()
3537                    aal = min([ap.GetOpacity() * 1.25, 1.0])
3538                    ap.SetOpacity(aal)
3539                    if aal == 1 and hasattr(a, "_bfprop") and a._bfprop:
3540                        a.SetBackfaceProperty(a._bfprop)
3541
3542        elif key in ("slash", "Up"):
3543            if self.clicked_actor in self.get_meshes():
3544                self.clicked_actor.GetProperty().SetOpacity(1)
3545                if hasattr(self.clicked_actor, "_bfprop") and self.clicked_actor._bfprop:
3546                    self.clicked_actor.SetBackfaceProperty(self.clicked_actor._bfprop)
3547            else:
3548                for a in self.get_meshes():
3549                    a.GetProperty().SetOpacity(1)
3550                    if hasattr(a, "_bfprop") and a._bfprop:
3551                        a.SetBackfaceProperty(a._bfprop)
3552
3553        elif key == "P":
3554            if self.clicked_actor in self.get_meshes():
3555                acts = [self.clicked_actor]
3556            else:
3557                acts = self.get_meshes()
3558            for ia in acts:
3559                try:
3560                    ps = ia.GetProperty().GetPointSize()
3561                    if ps > 1:
3562                        ia.GetProperty().SetPointSize(ps - 1)
3563                    ia.GetProperty().SetRepresentationToPoints()
3564                except AttributeError:
3565                    pass
3566
3567        elif key == "U":
3568            pval = renderer.GetActiveCamera().GetParallelProjection()
3569            renderer.GetActiveCamera().SetParallelProjection(not pval)
3570            if pval:
3571                renderer.ResetCamera()
3572
3573        elif key == "p":
3574            if self.clicked_actor in self.get_meshes():
3575                acts = [self.clicked_actor]
3576            else:
3577                acts = self.get_meshes()
3578            for ia in acts:
3579                try:
3580                    ps = ia.GetProperty().GetPointSize()
3581                    ia.GetProperty().SetPointSize(ps + 2)
3582                    ia.GetProperty().SetRepresentationToPoints()
3583                except AttributeError:
3584                    pass
3585
3586        elif key == "w":
3587            if self.clicked_actor and self.clicked_actor in self.get_meshes():
3588                self.clicked_actor.GetProperty().SetRepresentationToWireframe()
3589            else:
3590                for a in self.get_meshes():
3591                    if a.GetProperty().GetRepresentation() == 1:  # toggle
3592                        a.GetProperty().SetRepresentationToSurface()
3593                    else:
3594                        a.GetProperty().SetRepresentationToWireframe()
3595
3596        elif key == "r":
3597            renderer.ResetCamera()
3598
3599        elif key == "h":
3600            msg = (
3601                "  ============================================================\n"
3602                " | Press: i     print info about selected object              |\n"
3603                " |        I     print the RGB color under the mouse           |\n"
3604                " |        y     show the pipeline for this object as a graph  |\n"
3605                " |        <-->  use arrows to reduce/increase opacity         |\n"
3606                " |        w/s   toggle wireframe/surface style                |\n"
3607                " |        p/P   change point size of vertices                 |\n"
3608                " |        l     toggle edges visibility                       |\n"
3609                " |        x     toggle mesh visibility                        |\n"
3610                " |        X     invoke a cutter widget tool                   |\n"
3611                " |        1-3   change mesh color                             |\n"
3612                " |        4     use data array as colors, if present          |\n"
3613                " |        5-6   change background color(s)                    |\n"
3614                " |        09+-  (on keypad) or +/- to cycle axes style        |\n"
3615                " |        k     cycle available lighting styles               |\n"
3616                " |        K     cycle available shading styles                |\n"
3617                " |        A     toggle anti-aliasing                          |\n"
3618                " |        D     toggle depth-peeling (for transparencies)     |\n"
3619                " |        o/O   add/remove light to scene and rotate it       |\n"
3620                " |        n     show surface mesh normals                     |\n"
3621                " |        a     toggle interaction to Actor Mode              |\n"
3622                " |        j     toggle interaction to Joystick Mode           |\n"
3623                " |        U     toggle perspective/parallel projection        |\n"
3624                " |        r     reset camera position                         |\n"
3625                " |        R     reset camera orientation to orthogonal view   |\n"
3626                " |        .     fly camera towards last clicked point         |\n"
3627                " |        C     print current camera settings                 |\n"
3628                " |        S     save a screenshot                             |\n"
3629                " |        E/F   export 3D scene to numpy file or X3D          |\n"
3630                " |        q     return control to python script               |\n"
3631                " |        Esc   abort execution and exit python kernel        |\n"
3632                " |------------------------------------------------------------|\n"
3633                " | Mouse: Left-click    rotate scene / pick actors            |\n"
3634                " |        Middle-click  pan scene                             |\n"
3635                " |        Right-click   zoom scene in or out                  |\n"
3636                " |        Cntrl-click   rotate scene                          |\n"
3637                " |------------------------------------------------------------|\n"
3638                " |   Check out the documentation at:  https://vedo.embl.es    |\n"
3639                "  ============================================================"
3640            )
3641            vedo.printc(msg, dim=True)
3642
3643            msg = " vedo " + vedo.__version__ + " "
3644            vedo.printc(msg, invert=True, dim=True, end="")
3645            vtkVers = vtk.vtkVersion().GetVTKVersion()
3646            msg = "| vtk " + str(vtkVers)
3647            msg += " | numpy " + str(np.__version__)
3648            msg += " | python " + str(sys.version_info[0]) + "." + str(sys.version_info[1])
3649            vedo.printc(msg, invert=False, dim=True)
3650            return
3651
3652        elif key == "a":
3653            iren.ExitCallback()
3654            cur = iren.GetInteractorStyle()
3655            if isinstance(cur, vtk.vtkInteractorStyleTrackballCamera):
3656                msg = "\nInteractor style changed to TrackballActor\n"
3657                msg += "  you can now move and rotate individual meshes:\n"
3658                msg += "  press X twice to save the repositioned mesh\n"
3659                msg += "  press 'a' to go back to normal style"
3660                vedo.printc(msg)
3661                iren.SetInteractorStyle(vtk.vtkInteractorStyleTrackballActor())
3662            else:
3663                iren.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
3664            iren.Start()
3665            return
3666
3667        elif key == "A":  # toggle antialiasing
3668            msam = self.window.GetMultiSamples()
3669            if not msam:
3670                self.window.SetMultiSamples(8)
3671            else:
3672                self.window.SetMultiSamples(0)
3673            msam = self.window.GetMultiSamples()
3674            if msam:
3675                vedo.printc(f"Antialiasing is now set to {msam} samples", c=bool(msam))
3676            else:
3677                vedo.printc("Antialiasing is now disabled", c=bool(msam))
3678
3679        elif key == "D":  # toggle depthpeeling
3680            udp = not renderer.GetUseDepthPeeling()
3681            renderer.SetUseDepthPeeling(udp)
3682            # self.renderer.SetUseDepthPeelingForVolumes(udp)
3683            # print(self.window.GetAlphaBitPlanes())
3684            if udp:
3685                self.window.SetAlphaBitPlanes(1)
3686                renderer.SetMaximumNumberOfPeels(settings.max_number_of_peels)
3687                renderer.SetOcclusionRatio(settings.occlusion_ratio)
3688            self.interactor.Render()
3689            wasUsed = renderer.GetLastRenderingUsedDepthPeeling()
3690            rnr = self.renderers.index(renderer)
3691            vedo.printc(f"Depth peeling is now set to {udp} for renderer nr.{rnr}", c=udp)
3692            if not wasUsed and udp:
3693                vedo.printc("\t...but last rendering did not actually used it!", c=udp, invert=True)
3694            return
3695
3696        elif key == "j":
3697            iren.ExitCallback()
3698            cur = iren.GetInteractorStyle()
3699            if isinstance(cur, vtk.vtkInteractorStyleJoystickCamera):
3700                iren.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
3701            else:
3702                vedo.printc("\nInteractor style changed to Joystick,", end="")
3703                vedo.printc(" press j to go back to normal.")
3704                iren.SetInteractorStyle(vtk.vtkInteractorStyleJoystickCamera())
3705            iren.Start()
3706            return
3707
3708        elif key == "period":
3709            if self.picked3d:
3710                self.fly_to(self.picked3d)
3711            return
3712
3713        elif key == "S":
3714            vedo.file_io.screenshot("screenshot.png")
3715            vedo.printc(r":camera: Saved rendering window to 'screenshot.png'", c="b")
3716            return
3717
3718        elif key == "C":
3719            # Precision needs to be 7 (or even larger) to guarantee a consistent camera when
3720            #   the model coordinates are not centered at (0, 0, 0) and the mode is large.
3721            # This could happen for plotting geological models with UTM coordinate systems
3722            cam = renderer.GetActiveCamera()
3723            vedo.printc("\n###################################################", c="y")
3724            vedo.printc("## Template python code to position this camera: ##", c="y")
3725            vedo.printc("cam = dict(", c="y")
3726            vedo.printc("    position=" + utils.precision(cam.GetPosition(), 6) + ",", c="y")
3727            vedo.printc("    focal_point=" + utils.precision(cam.GetFocalPoint(), 6) + ",", c="y")
3728            vedo.printc("    viewup=" + utils.precision(cam.GetViewUp(), 6) + ",", c="y")
3729            if settings.use_parallel_projection:
3730                vedo.printc('    parallel_scale='+utils.precision(cam.GetParallelScale(),6)+',', c='y')
3731            else:
3732                vedo.printc('    distance='     +utils.precision(cam.GetDistance(),6)+',', c='y')
3733            vedo.printc('    clipping_range='+utils.precision(cam.GetClippingRange(),6)+',', c='y')
3734            vedo.printc(')', c='y')
3735            vedo.printc('show(mymeshes, camera=cam)', c='y')
3736            vedo.printc('###################################################', c='y')
3737            return
3738
3739        elif key == "R":
3740            self.reset_viewup()
3741
3742        elif key == "s":
3743            if self.clicked_actor and self.clicked_actor in self.get_meshes():
3744                self.clicked_actor.GetProperty().SetRepresentationToSurface()
3745            else:
3746                for a in self.get_meshes():
3747                    a.GetProperty().SetRepresentationToSurface()
3748
3749        elif key == "1":
3750            self._icol += 1
3751            if isinstance(self.clicked_actor, vedo.Points):
3752                self.clicked_actor.GetMapper().ScalarVisibilityOff()
3753                pal = vedo.colors.palettes[settings.palette % len(vedo.colors.palettes)]
3754                self.clicked_actor.GetProperty().SetColor(pal[(self._icol) % 10])
3755
3756        elif key == "2":
3757            self._icol += 1
3758            settings.palette += 1
3759            settings.palette = settings.palette % len(vedo.colors.palettes)
3760            if isinstance(self.clicked_actor, vedo.Points):
3761                self.clicked_actor.GetMapper().ScalarVisibilityOff()
3762                pal = vedo.colors.palettes[settings.palette % len(vedo.colors.palettes)]
3763                self.clicked_actor.GetProperty().SetColor(pal[(self._icol) % 10])
3764
3765        elif key == "3":
3766            bsc = ["b5", "cyan5", "g4", "o5", "p5", "r4", "teal4", "yellow4"]
3767            self._icol += 1
3768            if isinstance(self.clicked_actor, vedo.Points):
3769                self.clicked_actor.GetMapper().ScalarVisibilityOff()
3770                self.clicked_actor.GetProperty().SetColor(vedo.get_color(bsc[(self._icol) % len(bsc)]))
3771
3772        elif key == "4":
3773            if self.clicked_actor:
3774                acts = [self.clicked_actor]
3775            else:
3776                acts = self.get_meshes()
3777            for ia in acts:
3778                if not hasattr(ia, "_cmap_name"):
3779                    continue
3780                cmap_name = ia._cmap_name
3781                if not cmap_name:
3782                    cmap_name = "rainbow"
3783                if isinstance(ia, vedo.pointcloud.Points):
3784                    arnames = ia.pointdata.keys()
3785                    if len(arnames) > 0:
3786                        arnam = arnames[ia._scals_idx]
3787                        if arnam and ("normals" not in arnam.lower()):  # exclude normals
3788                            ia.cmap(cmap_name, arnam, on="points")
3789                            vedo.printc("..active point data set to:", arnam, c="g", bold=0)
3790                            ia._scals_idx += 1
3791                            if ia._scals_idx >= len(arnames):
3792                                ia._scals_idx = 0
3793                    else:
3794                        arnames = ia.celldata.keys()
3795                        if len(arnames) > 0:
3796                            arnam = arnames[ia._scals_idx]
3797                            if arnam and ("normals" not in arnam.lower()):  # exclude normals
3798                                ia.cmap(cmap_name, arnam, on="cells")
3799                                vedo.printc("..active cell array set to:", arnam, c="g", bold=0)
3800                                ia._scals_idx += 1
3801                                if ia._scals_idx >= len(arnames):
3802                                    ia._scals_idx = 0
3803
3804        elif key == "5":
3805            bgc = np.array(renderer.GetBackground()).sum() / 3
3806            if bgc <= 0:
3807                bgc = 0.223
3808            elif 0 < bgc < 1:
3809                bgc = 1
3810            else:
3811                bgc = 0
3812            renderer.SetBackground(bgc, bgc, bgc)
3813
3814        elif key == "6":
3815            bg2cols = [
3816                "lightyellow",
3817                "darkseagreen",
3818                "palegreen",
3819                "steelblue",
3820                "lightblue",
3821                "cadetblue",
3822                "lavender",
3823                "white",
3824                "blackboard",
3825                "black",
3826            ]
3827            bg2name = vedo.get_color_name(renderer.GetBackground2())
3828            if bg2name in bg2cols:
3829                idx = bg2cols.index(bg2name)
3830            else:
3831                idx = 4
3832            if idx is not None:
3833                bg2name_next = bg2cols[(idx + 1) % (len(bg2cols) - 1)]
3834            if not bg2name_next:
3835                renderer.GradientBackgroundOff()
3836            else:
3837                renderer.GradientBackgroundOn()
3838                renderer.SetBackground2(vedo.get_color(bg2name_next))
3839
3840        elif key in ["plus", "equal", "KP_Add", "minus", "KP_Subtract"]:  # cycle axes style
3841            clickedr = self.renderers.index(renderer)
3842            if self.axes_instances[clickedr]:
3843                if hasattr(self.axes_instances[clickedr], "EnabledOff"):  # widget
3844                    self.axes_instances[clickedr].EnabledOff()
3845                else:
3846                    try:
3847                        renderer.RemoveActor(self.axes_instances[clickedr])
3848                    except:
3849                        pass
3850                self.axes_instances[clickedr] = None
3851            if not self.axes:
3852                self.axes = 0
3853            if isinstance(self.axes, dict):
3854                self.axes = 1
3855            if key in ["minus", "KP_Subtract"]:
3856                if not settings.use_parallel_projection and self.axes == 0:
3857                    self.axes -= 1  # jump ruler doesnt make sense in perspective mode
3858                bns = self.renderer.ComputeVisiblePropBounds()
3859                addons.add_global_axes(axtype=(self.axes - 1) % 15, c=None, bounds=bns)
3860            else:
3861                if not settings.use_parallel_projection and self.axes == 12:
3862                    self.axes += 1  # jump ruler doesnt make sense in perspective mode
3863                bns = self.renderer.ComputeVisiblePropBounds()
3864                addons.add_global_axes(axtype=(self.axes + 1) % 15, c=None, bounds=bns)
3865            self.interactor.Render()
3866
3867        elif "KP_" in key or key in [
3868            "Insert",
3869            "End",
3870            "Down",
3871            "Next",
3872            "Left",
3873            "Begin",
3874            "Right",
3875            "Home",
3876            "Up",
3877            "Prior",
3878        ]:
3879            # change axes style
3880            asso = {
3881                "KP_Insert": 0,
3882                "KP_0": 0,
3883                "KP_End": 1,
3884                "KP_1": 1,
3885                "KP_Down": 2,
3886                "KP_2": 2,
3887                "KP_Next": 3,
3888                "KP_3": 3,
3889                "KP_Left": 4,
3890                "KP_4": 4,
3891                "KP_Begin": 5,
3892                "KP_5": 5,
3893                "KP_Right": 6,
3894                "KP_6": 6,
3895                "KP_Home": 7,
3896                "KP_7": 7,
3897                "KP_Up": 8,
3898                "KP_8": 8,
3899                "Prior": 9,  # on windows OS
3900                "Insert": 0,
3901                "End": 1,
3902                "Down": 2,
3903                "Next": 3,
3904                "Left": 4,
3905                "Begin": 5,
3906                "Right": 6,
3907                "Home": 7,
3908                "Up": 8,
3909                "Prior": 9,
3910            }
3911            clickedr = self.renderers.index(renderer)
3912            if key in asso:
3913                if self.axes_instances[clickedr]:
3914                    if hasattr(self.axes_instances[clickedr], "EnabledOff"):  # widget
3915                        self.axes_instances[clickedr].EnabledOff()
3916                    else:
3917                        try:
3918                            renderer.RemoveActor(self.axes_instances[clickedr])
3919                        except:
3920                            pass
3921                    self.axes_instances[clickedr] = None
3922                bounds = renderer.ComputeVisiblePropBounds()
3923                addons.add_global_axes(axtype=asso[key], c=None, bounds=bounds)
3924                self.interactor.Render()
3925
3926        if key == "O":
3927            renderer.RemoveLight(self._extralight)
3928            self._extralight = None
3929
3930        elif key == "o":
3931            vbb, sizes, _, _ = addons.compute_visible_bounds()
3932            cm = utils.vector((vbb[0] + vbb[1]) / 2, (vbb[2] + vbb[3]) / 2, (vbb[4] + vbb[5]) / 2)
3933            if not self._extralight:
3934                vup = renderer.GetActiveCamera().GetViewUp()
3935                pos = cm + utils.vector(vup) * utils.mag(sizes)
3936                self._extralight = addons.Light(pos, focal_point=cm)
3937                renderer.AddLight(self._extralight)
3938                print("Press again o to rotate light source, or O to remove it.")
3939            else:
3940                cpos = utils.vector(self._extralight.GetPosition())
3941                x, y, z = self._extralight.GetPosition() - cm
3942                r, th, ph = utils.cart2spher(x, y, z)
3943                th += 0.2
3944                if th > np.pi:
3945                    th = np.random.random() * np.pi / 2
3946                ph += 0.3
3947                cpos = utils.spher2cart(r, th, ph) + cm
3948                self._extralight.SetPosition(cpos)
3949
3950            self.window.Render()
3951
3952        elif key == "l":
3953            if self.clicked_actor in self.get_meshes():
3954                acts = [self.clicked_actor]
3955            else:
3956                acts = self.get_meshes()
3957            for ia in acts:
3958                try:
3959                    ev = ia.GetProperty().GetEdgeVisibility()
3960                    ia.GetProperty().SetEdgeVisibility(not ev)
3961                    ia.GetProperty().SetRepresentationToSurface()
3962                    ia.GetProperty().SetLineWidth(0.1)
3963                except AttributeError:
3964                    pass
3965
3966        elif key == "k":  # lightings
3967            if self.clicked_actor in self.get_meshes():
3968                acts = [self.clicked_actor]
3969            else:
3970                acts = self.get_meshes()
3971            shds = ("default", "metallic", "plastic", "shiny", "glossy", "off")
3972            for ia in acts:
3973                try:
3974                    lnr = (ia._ligthingnr + 1) % 6
3975                    ia.lighting(shds[lnr])
3976                    ia._ligthingnr = lnr
3977                except AttributeError:
3978                    pass
3979
3980        elif key == "K":  # shading
3981            if self.clicked_actor in self.get_meshes():
3982                acts = [self.clicked_actor]
3983            else:
3984                acts = self.get_meshes()
3985            for ia in acts:
3986                if isinstance(ia, vedo.Mesh):
3987                    ia.compute_normals(cells=False)
3988                    intrp = ia.GetProperty().GetInterpolation()
3989                    # print(intrp, ia.GetProperty().GetInterpolationAsString())
3990                    if intrp > 0:
3991                        ia.GetProperty().SetInterpolation(0)  # flat
3992                    else:
3993                        ia.GetProperty().SetInterpolation(2)  # phong
3994
3995        elif key == "n":  # show normals to an actor
3996            if self.clicked_actor in self.get_meshes():
3997                if self.clicked_actor.GetPickable():
3998                    self.renderer.AddActor(vedo.shapes.NormalLines(self.clicked_actor))
3999                    iren.Render()
4000            else:
4001                print("Click an actor and press n to add normals.")
4002
4003        elif key == "x":
4004            if self.justremoved is None:
4005                if self.clicked_actor in self.get_meshes() or isinstance(
4006                    self.clicked_actor, vtk.vtkAssembly
4007                ):
4008                    self.justremoved = self.clicked_actor
4009                    self.renderer.RemoveActor(self.clicked_actor)
4010            else:
4011                self.renderer.AddActor(self.justremoved)
4012                self.renderer.Render()
4013                self.justremoved = None
4014
4015        elif key == "X":
4016            if self.clicked_actor:
4017                if not self.cutter_widget:
4018                    self.cutter_widget = addons.BoxCutter(self.clicked_actor)
4019                    self.add(self.cutter_widget)
4020                    print("Press Shift+X to close the cutter box widget, Ctrl+s to save the cut section.")
4021                else:
4022                    self.remove(self.cutter_widget)
4023                    self.cutter_widget = None
4024            else:
4025                for a in self.actors:
4026                    if isinstance(a, vtk.vtkVolume):
4027                        addons.add_cutter_tool(a)
4028                        return
4029
4030                vedo.printc("Click object and press X to open the cutter box widget.", c=4)
4031
4032        elif key == "E":
4033            vedo.printc(r":camera: Exporting 3D window to file", c="blue", end="")
4034            vedo.file_io.export_window("scene.npz")
4035            vedo.printc(". Try:\n> vedo scene.npz", c="blue")
4036
4037        elif key == "F":
4038            vedo.file_io.export_window("scene.x3d")
4039            vedo.printc(":idea: Try: firefox scene.html", c="blue")
4040
4041        elif key == "i":  # print info
4042            if self.clicked_actor:
4043                utils.print_info(self.clicked_actor)
4044            else:
4045                utils.print_info(self)
4046
4047        elif key == "I":  # print color under the mouse
4048            x, y = iren.GetEventPosition()
4049            self.color_picker([x, y], verbose=True)
4050
4051        elif key == "y":
4052            if self.clicked_actor and self.clicked_actor.pipeline:
4053                # self.clicked_actor.pipeline =  utils.OperationNode(
4054                #         "show", parents=[self.clicked_actor],
4055                #         shape="circle",
4056                # )
4057                self.clicked_actor.pipeline.show()
4058
4059        if iren:
4060            iren.Render()

Main class to manage actors.

Plotter( shape=(1, 1), N=None, pos=(0, 0), size='auto', screensize='auto', title='vedo', bg='white', bg2=None, axes=None, sharecam=True, resetcam=True, interactive=None, offscreen=False, qt_widget=None, wx_widget=None)
320    def __init__(
321        self,
322        shape=(1, 1),
323        N=None,
324        pos=(0, 0),
325        size="auto",
326        screensize="auto",
327        title="vedo",
328        bg="white",
329        bg2=None,
330        axes=None,
331        sharecam=True,
332        resetcam=True,
333        interactive=None,
334        offscreen=False,
335        qt_widget=None,
336        wx_widget=None,
337    ):
338        """
339        Arguments:
340            shape : (str, list)
341                shape of the grid of renderers in format (rows, columns). Ignored if N is specified.
342            N : (int)
343                number of desired renderers arranged in a grid automatically.
344            pos : (list)
345                (x,y) position in pixels of top-left corner of the rendering window on the screen
346            size : (str, list)
347                size of the rendering window. If 'auto', guess it based on screensize.
348            screensize : (list)
349                physical size of the monitor screen in pixels
350            bg : (color, str)
351                background color or specify jpg image file name with path
352            bg2 : (color)
353                background color of a gradient towards the top
354            axes : (int)
355
356                Note that Axes type-1 can be fully customized by passing a dictionary `axes=dict()`.
357                Check out `vedo.addons.Axes()` for the available options.
358                - 0,  no axes
359                - 1,  draw three gray grid walls
360                - 2,  show cartesian axes from (0,0,0)
361                - 3,  show positive range of cartesian axes from (0,0,0)
362                - 4,  show a triad at bottom left
363                - 5,  show a cube at bottom left
364                - 6,  mark the corners of the bounding box
365                - 7,  draw a 3D ruler at each side of the cartesian axes
366                - 8,  show the VTK CubeAxesActor object
367                - 9,  show the bounding box outLine,
368                - 10, show three circles representing the maximum bounding box,
369                - 11, show a large grid on the x-y plane (use with zoom=8)
370                - 12, show polar axes.
371                - 13, draw a simple ruler at the bottom of the window
372
373            sharecam : (bool)
374                if False each renderer will have an independent vtkCamera
375            interactive : (bool)
376                if True will stop after show() to allow interaction w/ window
377            offscreen : (bool)
378                if True will not show the rendering window
379            qt_widget : (QVTKRenderWindowInteractor)
380                render in a Qt-Widget using an QVTKRenderWindowInteractor.
381                Overrides offscreen to True.
382                Overrides interactive to False.
383                See examples `qt_windows1.py` and `qt_windows2.py`
384        """
385        vedo.plotter_instance = self
386
387        if qt_widget is not None:
388            # overrides the interactive and offscreen properties
389            interactive = False
390            offscreen = True
391
392        if wx_widget is not None:
393            # overrides the interactive property
394            interactive = False
395
396        if interactive is None:
397            if N == 1:
398                interactive = True
399            elif N or shape != (1, 1):
400                interactive = False
401            else:
402                interactive = True
403
404        self.actors = []  # list of actors to be shown
405        self.clicked_actor = None  # holds the actor that has been clicked
406        self.renderer = None  # current renderer
407        self.renderers = []  # list of renderers
408        self.shape = shape  # don't remove this line
409        self._interactive = interactive  # allows to interact with renderer
410        self.axes = axes  # show axes type nr.
411        self.title = title  # window title
412        self.sharecam = sharecam  # share the same camera if multiple renderers
413        self.picker = None  # the vtkPicker object
414        self.picked2d = None  # 2d coords of a clicked point on the rendering window
415        self.picked3d = None  # 3d coords of a clicked point on an actor
416        self.offscreen = offscreen
417        self.resetcam = resetcam
418        self.last_event = None
419
420        self.qt_widget = qt_widget  #  QVTKRenderWindowInteractor
421        self.wx_widget = wx_widget  # wxVTKRenderWindowInteractor
422
423        self.skybox = None
424
425        # mostly internal stuff:
426        self.hover_legends = []
427        self.backgrcol = bg
428        self.pos = pos  # used by vedo.file_io
429        self.justremoved = None
430        self.axes_instances = []
431        self.clock = 0
432        self.sliders = []
433        self.buttons = []
434        self.widgets = []
435        self.cutter_widget = None
436        self.hint_widget = None
437        self.background_renderer = None
438        self.size = size
439        self.interactor = None
440        self.camera = None
441
442        self._icol = 0
443        self._clockt0 = time.time()
444        self._extralight = None
445        self._cocoa_initialized = False
446        self._bg = bg  # used by backend notebooks
447
448        #####################################################################
449        if settings.default_backend != "vtk":
450            if settings.default_backend == "2d":
451                self.offscreen = True
452                if self.size == "auto":
453                    self.size = (800, 600)
454
455            elif settings.default_backend == "k3d":
456                self._interactive = False
457                self.interactor = None
458                self.window = None
459                self.camera = None  # let the backend choose
460                if self.size == "auto":
461                    self.size = (1000, 1000)
462                #############################################################
463                return  ######################################################
464                #############################################################
465        #####################################################################
466
467        # build the rendering window:
468        self.window = vtk.vtkRenderWindow()
469
470        self.window.GlobalWarningDisplayOff()
471        self.window.SetWindowName(self.title)
472
473        # more settings
474        if settings.use_depth_peeling:
475            self.window.SetAlphaBitPlanes(settings.alpha_bit_planes)
476        self.window.SetMultiSamples(settings.multi_samples)
477
478        self.window.SetPolygonSmoothing(settings.polygon_smoothing)
479        self.window.SetLineSmoothing(settings.line_smoothing)
480        self.window.SetPointSmoothing(settings.point_smoothing)
481
482        # sort out screen size
483        if screensize == "auto":
484            screensize = (2160, 1440)  # might go wrong, use a default 1.5 ratio
485
486            ### BUG in GetScreenSize in VTK 9.1.0
487            ### https://discourse.vtk.org/t/vtk9-1-0-problems/7094/3
488            if settings.hack_call_screen_size:  # True
489
490                vtkvers = vedo.vtk_version
491                if not self.offscreen and (vtkvers[0] < 9 or vtkvers[0] == 9 and vtkvers[1] == 0):
492                    aus = self.window.GetScreenSize()
493                    if aus and len(aus) == 2 and aus[0] > 100 and aus[1] > 100:  # seems ok
494                        if aus[0] / aus[1] > 2:  # looks like there are 2 or more screens
495                            screensize = (int(aus[0] / 2), aus[1])
496                        else:
497                            screensize = aus
498
499        x, y = screensize
500
501        if N:  # N = number of renderers. Find out the best
502
503            if shape != (1, 1):  # arrangement based on minimum nr. of empty renderers
504                vedo.logger.warning("having set N, shape is ignored.")
505
506            nx = int(np.sqrt(int(N * y / x) + 1))
507            ny = int(np.sqrt(int(N * x / y) + 1))
508            lm = [
509                (nx, ny),
510                (nx, ny + 1),
511                (nx - 1, ny),
512                (nx + 1, ny),
513                (nx, ny - 1),
514                (nx - 1, ny + 1),
515                (nx + 1, ny - 1),
516                (nx + 1, ny + 1),
517                (nx - 1, ny - 1),
518            ]
519            ind, minl = 0, 1000
520            for i, m in enumerate(lm):
521                l = m[0] * m[1]
522                if N <= l < minl:
523                    ind = i
524                    minl = l
525            shape = lm[ind]
526
527        ##################################################
528        if isinstance(shape, str):
529
530            if "|" in shape:
531                if self.size == "auto":
532                    self.size = (800, 1200)
533                n = int(shape.split("|")[0])
534                m = int(shape.split("|")[1])
535                rangen = reversed(range(n))
536                rangem = reversed(range(m))
537            else:
538                if self.size == "auto":
539                    self.size = (1200, 800)
540                m = int(shape.split("/")[0])
541                n = int(shape.split("/")[1])
542                rangen = range(n)
543                rangem = range(m)
544
545            if n >= m:
546                xsplit = m / (n + m)
547            else:
548                xsplit = 1 - n / (n + m)
549            if settings.window_splitting_position:
550                xsplit = settings.window_splitting_position
551
552            for i in rangen:
553                arenderer = vtk.vtkRenderer()
554                if "|" in shape:
555                    arenderer.SetViewport(0, i / n, xsplit, (i + 1) / n)
556                else:
557                    arenderer.SetViewport(i / n, 0, (i + 1) / n, xsplit)
558                self.renderers.append(arenderer)
559
560            for i in rangem:
561                arenderer = vtk.vtkRenderer()
562
563                if "|" in shape:
564                    arenderer.SetViewport(xsplit, i / m, 1, (i + 1) / m)
565                else:
566                    arenderer.SetViewport(i / m, xsplit, (i + 1) / m, 1)
567                self.renderers.append(arenderer)
568
569            for r in self.renderers:
570                r.SetUseHiddenLineRemoval(settings.hidden_line_removal)
571                r.SetLightFollowCamera(settings.light_follows_camera)
572
573                r.SetUseDepthPeeling(settings.use_depth_peeling)
574                # r.SetUseDepthPeelingForVolumes(settings.use_depth_peeling)
575                if settings.use_depth_peeling:
576                    r.SetMaximumNumberOfPeels(settings.max_number_of_peels)
577                    r.SetOcclusionRatio(settings.occlusion_ratio)
578                r.SetUseFXAA(settings.use_fxaa)
579                r.SetPreserveDepthBuffer(settings.preserve_depth_buffer)
580
581                r.SetBackground(vedo.get_color(self.backgrcol))
582
583                self.axes_instances.append(None)
584
585            self.shape = (n + m,)
586
587        elif utils.is_sequence(shape) and isinstance(shape[0], dict):
588            # passing a sequence of dicts for renderers specifications
589
590            if self.size == "auto":
591                self.size = (1200, 900)
592
593            for rd in shape:
594                x0, y0 = rd["bottomleft"]
595                x1, y1 = rd["topright"]
596                bg_ = rd.pop("bg", "white")
597                bg2_ = rd.pop("bg2", None)
598
599                arenderer = vtk.vtkRenderer()
600                arenderer.SetUseHiddenLineRemoval(settings.hidden_line_removal)
601                arenderer.SetLightFollowCamera(settings.light_follows_camera)
602
603                arenderer.SetUseDepthPeeling(settings.use_depth_peeling)
604                # arenderer.SetUseDepthPeelingForVolumes(settings.use_depth_peeling)
605                if settings.use_depth_peeling:
606                    arenderer.SetMaximumNumberOfPeels(settings.max_number_of_peels)
607                    arenderer.SetOcclusionRatio(settings.occlusion_ratio)
608                arenderer.SetUseFXAA(settings.use_fxaa)
609                arenderer.SetPreserveDepthBuffer(settings.preserve_depth_buffer)
610
611                arenderer.SetViewport(x0, y0, x1, y1)
612                arenderer.SetBackground(vedo.get_color(bg_))
613                if bg2_:
614                    arenderer.GradientBackgroundOn()
615                    arenderer.SetBackground2(vedo.get_color(bg2_))
616
617                self.renderers.append(arenderer)
618                self.axes_instances.append(None)
619
620            self.shape = (len(shape),)
621
622        else:
623
624            if isinstance(self.size, str) and self.size == "auto":
625                # figure out a reasonable window size
626                f = 1.5
627                xs = y / f * shape[1]  # because y<x
628                ys = y / f * shape[0]
629                if xs > x / f:  # shrink
630                    xs = x / f
631                    ys = xs / shape[1] * shape[0]
632                if ys > y / f:
633                    ys = y / f
634                    xs = ys / shape[0] * shape[1]
635                self.size = (int(xs), int(ys))
636                if shape == (1, 1):
637                    self.size = (int(y / f), int(y / f))  # because y<x
638            else:
639                self.size = (self.size[0], self.size[1])
640
641            image_actor = None
642            bgname = str(self.backgrcol).lower()
643            if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname:
644                self.window.SetNumberOfLayers(2)
645                self.background_renderer = vtk.vtkRenderer()
646                self.background_renderer.SetLayer(0)
647                self.background_renderer.InteractiveOff()
648                self.background_renderer.SetBackground(vedo.get_color(bg2))
649                image_actor = vedo.Picture(self.backgrcol)
650                self.window.AddRenderer(self.background_renderer)
651                self.background_renderer.AddActor(image_actor)
652
653            for i in reversed(range(shape[0])):
654                for j in range(shape[1]):
655                    arenderer = vtk.vtkRenderer()
656                    arenderer.SetUseHiddenLineRemoval(settings.hidden_line_removal)
657                    arenderer.SetLightFollowCamera(settings.light_follows_camera)
658                    arenderer.SetTwoSidedLighting(settings.two_sided_lighting)
659
660                    arenderer.SetUseDepthPeeling(settings.use_depth_peeling)
661                    # arenderer.SetUseDepthPeelingForVolumes(settings.use_depth_peeling)
662                    if settings.use_depth_peeling:
663                        arenderer.SetMaximumNumberOfPeels(settings.max_number_of_peels)
664                        arenderer.SetOcclusionRatio(settings.occlusion_ratio)
665                    arenderer.SetUseFXAA(settings.use_fxaa)
666                    arenderer.SetPreserveDepthBuffer(settings.preserve_depth_buffer)
667
668                    if image_actor:
669                        arenderer.SetLayer(1)
670
671                    arenderer.SetBackground(vedo.get_color(self.backgrcol))
672                    if bg2:
673                        arenderer.GradientBackgroundOn()
674                        arenderer.SetBackground2(vedo.get_color(bg2))
675
676                    x0 = i / shape[0]
677                    y0 = j / shape[1]
678                    x1 = (i + 1) / shape[0]
679                    y1 = (j + 1) / shape[1]
680                    arenderer.SetViewport(y0, x0, y1, x1)
681                    self.renderers.append(arenderer)
682                    self.axes_instances.append(None)
683            self.shape = shape
684
685        if self.renderers:
686            self.renderer = self.renderers[0]
687            self.camera = self.renderer.GetActiveCamera()
688            self.camera.SetParallelProjection(settings.use_parallel_projection)
689
690        if self.size[0] == "f":  # full screen
691            self.size = "fullscreen"
692            self.window.SetFullScreen(True)
693            self.window.BordersOn()
694        else:
695            self.window.SetSize(int(self.size[0]), int(self.size[1]))
696
697        if self.wx_widget is not None:
698            settings.immediate_rendering = False  # override
699            self.window = self.wx_widget.GetRenderWindow()  # overwrite
700            self.interactor = self.window.GetInteractor()
701            for r in self.renderers:
702                self.window.AddRenderer(r)
703            self.wx_widget.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
704            self.camera = self.renderer.GetActiveCamera()
705            ########################
706            return  ################
707            ########################
708
709        if self.qt_widget is not None:
710            self.interactor = self.qt_widget.GetRenderWindow().GetInteractor()
711            self.window = self.qt_widget.GetRenderWindow()  # overwrite
712            ########################
713            return  ################
714            ########################
715
716        self.window.SetPosition(pos)
717
718        for r in self.renderers:
719            self.window.AddRenderer(r)
720
721        if self.offscreen:
722            if self.axes in (4, 5):
723                self.axes = 0  # does not work with those
724            self.window.SetOffScreenRendering(True)
725            self._interactive = False
726            self.interactor = None
727            ########################
728            return  ################
729            ########################
730
731        self.interactor = vtk.vtkRenderWindowInteractor()
732
733        self.interactor.SetRenderWindow(self.window)
734        vsty = vtk.vtkInteractorStyleTrackballCamera()
735        self.interactor.SetInteractorStyle(vsty)
736
737        if settings.enable_default_mouse_callbacks:
738            self.interactor.AddObserver("LeftButtonPressEvent", self._mouseleftclick)
739
740        if settings.enable_default_keyboard_callbacks:
741            self.interactor.AddObserver("KeyPressEvent", self._keypress)
742
743        # self._timer_event_id = None
744        # if settings.allow_interaction:
745            # def win_interact(iren, event):  # flushing interactor events
746            #     if event == "TimerEvent":
747            #         iren.ExitCallback()
748            # self._timer_event_id = self.interactor.AddObserver("TimerEvent", win_interact)
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
  • axes : (int)

    Note that Axes type-1 can be fully customized by passing a dictionary axes=dict(). Check out vedo.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
  • sharecam : (bool) if False each renderer will have an independent vtkCamera
  • interactive : (bool) if True will stop after show() to allow interaction w/ window
  • offscreen : (bool) if True will not show the rendering window
  • qt_widget : (QVTKRenderWindowInteractor) render in a Qt-Widget using an QVTKRenderWindowInteractor. Overrides offscreen to True. Overrides interactive to False. See examples qt_windows1.py and qt_windows2.py
def process_events(self):
783    def process_events(self):
784        if self.interactor:
785            try:
786                self.interactor.ProcessEvents()
787            except AttributeError:
788                pass
789        return self
def at(self, nren, yren=None):
791    def at(self, nren, yren=None):
792        """
793        Select the current renderer number as an int.
794        Can also use the [nx, ny] format.
795        """
796        if yren is not None:
797            nren = (yren) * self.shape[1] + (nren)
798            if nren < 0 or nren > len(self.renderers):
799                vedo.logger.error(f"at({nren, yren}) is malformed!")
800                raise RuntimeError
801
802        self.renderer = self.renderers[nren]
803        self.camera = self.renderer.GetActiveCamera()
804        return self

Select the current renderer number as an int. Can also use the [nx, ny] format.

def add(self, *actors, at=None):
807    def add(self, *actors, at=None):
808        """
809        Append the input objects to the internal list of actors to be shown.
810        This method is typically used in loops or callback functions.
811
812        Arguments:
813            at : (int)
814                add the object at the specified renderer
815        """
816        if at is not None:
817            ren = self.renderers[at]
818        else:
819            ren = self.renderer
820
821        actors = utils.flatten(actors)
822        actors = self._scan_input(actors)
823
824        for a in actors:
825            if isinstance(a, vtk.vtkInteractorObserver):
826                a.add_to(self)
827                continue
828
829            if a not in self.actors:
830                self.actors.append(a)
831
832            if ren:
833                ren.AddActor(a)
834
835                if hasattr(a, "rendered_at"):
836                    ir = self.renderers.index(ren)
837                    a.rendered_at.add(ir)
838
839                if hasattr(a, "scalarbar") and a.scalarbar:
840                    ren.AddActor(a.scalarbar)
841
842                if hasattr(a, "_isfollower") and a._isfollower:  # set by mesh.follow_camera()
843                    a.SetCamera(self.camera)
844
845        return self

Append the input objects to the internal list of actors to be shown. This method is typically used in loops or callback functions.

Arguments:
  • at : (int) add the object at the specified renderer
def remove(self, *actors, at=None):
847    def remove(self, *actors, at=None):
848        """
849        Remove input object to the internal list of actors to be shown.
850        This method is typically used in loops or callback functions.
851        Objects to be removed can be referenced by their assigned name.
852
853        Arguments:
854            at : (int)
855                remove the object at the specified renderer
856        """
857        if at is not None:
858            ren = self.renderers[at]
859        else:
860            ren = self.renderer
861
862        actors = utils.flatten(actors)
863
864        actors_in_ren = None
865
866        actors_r = []
867        for i, a in enumerate(actors):
868
869            if isinstance(a, vtk.vtkInteractorObserver):
870                a.remove_from(self)
871                continue ###
872
873            if isinstance(a, str):
874                if actors_in_ren is None:
875                    actors_in_ren = self.get_meshes(
876                        include_non_pickables=True,
877                        unpack_assemblies=False,
878                    )
879
880                for b in set(self.actors + actors_in_ren):
881                    if hasattr(b, "name") and a in b.name:
882                        actors_r.append(b)
883
884            else:
885                actors_r.append(a)
886
887        for a in set(actors_r):
888            if ren:
889                ren.RemoveActor(a)
890                if hasattr(a, "rendered_at"):
891                    ir = self.renderers.index(ren)
892                    a.rendered_at.discard(ir)
893                if hasattr(a, "scalarbar") and a.scalarbar:
894                    ren.RemoveActor(a.scalarbar)
895                if hasattr(a, "_caption") and a._caption:
896                    ren.RemoveActor(a._caption)
897                if hasattr(a, "shadows") and a.shadows:
898                    for sha in a.shadows:
899                        ren.RemoveActor(sha)
900                if hasattr(a, "trail") and a.trail:
901                    ren.RemoveActor(a.trail)
902                    a.trail_points = []
903                    if hasattr(a.trail, "shadows") and a.trail.shadows:
904                        for sha in a.trail.shadows:
905                            ren.RemoveActor(sha)
906
907            if a in self.actors:
908                i = self.actors.index(a)
909                del self.actors[i]
910
911        return self

Remove input object to the internal list of actors to be shown. This method is typically used in loops or callback functions. Objects to be removed can be referenced by their assigned name.

Arguments:
  • at : (int) remove the object at the specified renderer
def remove_lights(self):
913    def remove_lights(self):
914        """Remove all the present lights in the current renderer."""
915        if self.renderer:
916            self.renderer.RemoveAllLights()
917        return self

Remove all the present lights in the current renderer.

def pop(self, at=None):
919    def pop(self, at=None):
920        """
921        Remove the last added object from the rendering window.
922        This method is typically used in loops or callback functions.
923        """
924        if at is not None and not isinstance(at, int):
925            # wrong usage pitfall
926            vedo.logger.error("argument of pop() must be an integer")
927            raise RuntimeError()
928
929        if self.actors:
930            self.remove(self.actors[-1], at)
931        return self

Remove the last added object from the rendering window. This method is typically used in loops or callback functions.

def render(self, resetcam=False):
933    def render(self, resetcam=False):
934        """Render the scene. This method is typically used in loops or callback functions."""
935        if not self.window:
936            return self
937
938        if self.wx_widget:
939            if resetcam:
940                self.renderer.ResetCamera()
941            self.wx_widget.Render()
942            return self
943
944        if self.qt_widget:
945            if resetcam:
946                self.renderer.ResetCamera()
947            self.qt_widget.Render()
948            return self
949
950        if self.interactor:
951            if not self.interactor.GetInitialized():
952                self.interactor.Initialize()
953
954        self.camera = self.renderer.GetActiveCamera()
955        if resetcam:
956            self.renderer.ResetCamera()
957
958        self.window.Render()
959        return self

Render the scene. This method is typically used in loops or callback functions.

def interactive(self):
961    def interactive(self):
962        """
963        Start window interaction.
964        Analogous to `show(..., interactive=True)`.
965        """
966        if self.interactor:
967            self.interactor.Start()
968        return self

Start window interaction. Analogous to show(..., interactive=True).

def use_depth_peeling(self, at=None, value=True):
970    def use_depth_peeling(self, at=None, value=True):
971        """
972        Specify whether use depth peeling algorithm at this specific renderer
973        Call this method before the first rendering.
974        """
975        if at is None:
976            ren = self.renderer
977        else:
978            ren = self.renderers[at]
979        ren.SetUseDepthPeeling(value)
980        return self

Specify whether use depth peeling algorithm at this specific renderer Call this method before the first rendering.

def background(self, c1=None, c2=None, at=None):
 982    def background(self, c1=None, c2=None, at=None):
 983        """Set the color of the background for the current renderer.
 984        A different renderer index can be specified by keyword ``at``.
 985
 986        Arguments:
 987            c1 : (list)
 988                background main color.
 989            c2 : (list)
 990                background color for the upper part of the window.
 991            at : (int)
 992                renderer index.
 993        """
 994        if not self.renderers:
 995            return self
 996        if at is None:
 997            r = self.renderer
 998        else:
 999            r = self.renderers[at]
1000        if r:
1001            if c1 is not None:
1002                r.SetBackground(vedo.get_color(c1))
1003                self._bg = r.GetBackground()  # notebooks
1004            if c2 is not None:
1005                r.GradientBackgroundOn()
1006                r.SetBackground2(vedo.get_color(c2))
1007            else:
1008                r.GradientBackgroundOff()
1009        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.
def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True):
1012    def get_meshes(self, at=None, include_non_pickables=False, unpack_assemblies=True):
1013        """
1014        Return a list of Meshes from the specified renderer.
1015
1016        Arguments:
1017            at : (int)
1018                specify which renderer to look at.
1019            include_non_pickables : (bool)
1020                include non-pickable objects
1021            unpack_assemblies : (bool)
1022                unpack assemblies into their components
1023        """
1024        if at is None:
1025            renderer = self.renderer
1026            at = self.renderers.index(renderer)
1027        elif isinstance(at, int):
1028            renderer = self.renderers[at]
1029
1030        has_global_axes = False
1031        if isinstance(self.axes_instances[at], vedo.Assembly):
1032            has_global_axes = True
1033
1034        if unpack_assemblies:
1035            acs = renderer.GetActors()
1036        else:
1037            acs = renderer.GetViewProps()
1038
1039        actors = []
1040        acs.InitTraversal()
1041        for _ in range(acs.GetNumberOfItems()):
1042
1043            if unpack_assemblies:
1044                a = acs.GetNextItem()
1045            else:
1046                a = acs.GetNextProp()
1047
1048            if isinstance(a, vtk.vtkVolume):
1049                continue
1050
1051            if include_non_pickables or a.GetPickable():
1052                if a == self.axes_instances[at]:
1053                    continue
1054                if has_global_axes and a in self.axes_instances[at].actors:
1055                    continue
1056                actors.append(a)
1057        return actors

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
def get_volumes(self, at=None, include_non_pickables=False):
1059    def get_volumes(self, at=None, include_non_pickables=False):
1060        """
1061        Return a list of Volumes from the specified renderer.
1062
1063        Arguments:
1064            at : (int)
1065                specify which renderer to look at
1066            include_non_pickables : (bool)
1067                include non-pickable objects
1068        """
1069        if at is None:
1070            renderer = self.renderer
1071            at = self.renderers.index(renderer)
1072        elif isinstance(at, int):
1073            renderer = self.renderers[at]
1074
1075        vols = []
1076        acs = renderer.GetVolumes()
1077        acs.InitTraversal()
1078        for _ in range(acs.GetNumberOfItems()):
1079            a = acs.GetNextItem()
1080            if include_non_pickables or a.GetPickable():
1081                vols.append(a)
1082        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
def reset_camera(self, tight=None):
1084    def reset_camera(self, tight=None):
1085        """
1086        Reset the camera position and zooming.
1087        If tight (float) is specified the zooming reserves a padding space in the xy-plane
1088        expressed in percent of the average size.
1089        """
1090        if tight is None:
1091            self.renderer.ResetCamera()
1092        else:
1093            x0, x1, y0, y1, z0, z1 = self.renderer.ComputeVisiblePropBounds()
1094
1095            cam = self.renderer.GetActiveCamera()
1096
1097            self.renderer.ComputeAspect()
1098            aspect = self.renderer.GetAspect()
1099            angle = np.pi * cam.GetViewAngle() / 180.0
1100            dx, dy = (x1 - x0) * 0.999, (y1 - y0) * 0.999
1101            dist = max(dx / aspect[0], dy) / np.sin(angle / 2) / 2
1102
1103            cam.SetViewUp(0, 1, 0)
1104            cam.SetPosition(x0 + dx / 2, y0 + dy / 2, dist * (1 + tight))
1105            cam.SetFocalPoint(x0 + dx / 2, y0 + dy / 2, 0)
1106            if cam.GetParallelProjection():
1107                ps = max(dx / aspect[0], dy) / 2
1108                cam.SetParallelScale(ps * (1 + tight))
1109            self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1)
1110        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.

def reset_viewup(self, smooth=True):
1112    def reset_viewup(self, smooth=True):
1113        """
1114        Reset the orientation of the camera to the closest orthogonal direction and view-up.
1115        """
1116        vbb = addons.compute_visible_bounds()[0]
1117        x0, x1, y0, y1, z0, z1 = vbb
1118        mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2
1119        d = self.camera.GetDistance()
1120
1121        viewups = np.array([
1122            (0, 1, 0), ( 0, -1,  0),
1123            (0, 0, 1), ( 0,  0, -1),
1124            (1, 0, 0), (-1,  0,  0),
1125        ])
1126        positions = np.array([
1127            (mx, my, mz+d), (mx, my, mz-d),
1128            (mx, my+d, mz), (mx, my-d, mz),
1129            (mx+d, my, mz), (mx-d, my, mz),
1130        ])
1131
1132        vu = np.array(self.camera.GetViewUp())
1133        vui = np.argmin(np.linalg.norm(viewups - vu, axis=1))
1134
1135        poc = np.array(self.camera.GetPosition())
1136        foc = np.array(self.camera.GetFocalPoint())
1137        a = poc - foc
1138        b = positions - foc
1139        a = a / np.linalg.norm(a)
1140        b = b.T * (1 / np.linalg.norm(b, axis=1))
1141        pui = np.argmin(np.linalg.norm(b.T - a, axis=1))
1142
1143        if smooth:
1144            outtimes = np.linspace(0, 1, num=11, endpoint=True)
1145            for t in outtimes:
1146                vv = vu * (1 - t) + viewups[vui] * t
1147                pp = poc * (1 - t) + positions[pui] * t
1148                ff = foc * (1 - t) + np.array([mx, my, mz]) * t
1149                self.camera.SetViewUp(vv)
1150                self.camera.SetPosition(pp)
1151                self.camera.SetFocalPoint(ff)
1152                self.render()
1153
1154            # interpolator does not respect parallel view...:
1155            # cam1 = dict(
1156            #     pos=poc,
1157            #     viewup=vu,
1158            #     focal_point=(mx,my,mz),
1159            #     clipping_range=self.camera.GetClippingRange()
1160            # )
1161            # # cam1 = self.camera
1162            # cam2 = dict(
1163            #     pos=positions[pui],
1164            #     viewup=viewups[vui],
1165            #     focal_point=(mx,my,mz),
1166            #     clipping_range=self.camera.GetClippingRange()
1167            # )
1168            # vcams = self.move_camera([cam1, cam2], output_times=outtimes, smooth=0)
1169            # for c in vcams:
1170            #     self.renderer.SetActiveCamera(c)
1171            #     self.render()
1172        else:
1173
1174            self.camera.SetViewUp(viewups[vui])
1175            self.camera.SetPosition(positions[pui])
1176            self.camera.SetFocalPoint(mx, my, mz)
1177
1178        self.renderer.ResetCameraClippingRange()
1179
1180        # vbb, _, _, _ = addons.compute_visible_bounds()
1181        # x0,x1, y0,y1, z0,z1 = vbb
1182        # self.renderer.ResetCameraClippingRange(x0, x1, y0, y1, z0, z1)
1183        self.render()
1184        return self

Reset the orientation of the camera to the closest orthogonal direction and view-up.

def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()):
1186    def move_camera(self, cameras, t=0, times=(), smooth=True, output_times=()):
1187        """
1188        Takes as input two cameras set camera at an interpolated position:
1189
1190        Cameras can be vtkCamera or dictionaries in format:
1191
1192            `dict(pos=..., focal_point=..., viewup=..., distance=..., clipping_range=...)`
1193
1194        Press `shift-C` key in interactive mode to dump a python snipplet
1195        of parameters for the current camera view.
1196        """
1197        nc = len(cameras)
1198        if len(times) == 0:
1199            times = np.linspace(0, 1, num=nc, endpoint=True)
1200
1201        assert len(times) == nc
1202
1203        cin = vtk.vtkCameraInterpolator()
1204
1205        # cin.SetInterpolationTypeToLinear() # bugged?
1206        if nc > 2 and smooth:
1207            cin.SetInterpolationTypeToSpline()
1208
1209        for i, cam in enumerate(cameras):
1210            vcam = cam
1211            if isinstance(cam, dict):
1212                vcam = utils.camera_from_dict(cam)
1213            cin.AddCamera(times[i], vcam)
1214
1215        mint, maxt = cin.GetMinimumT(), cin.GetMaximumT()
1216        rng = maxt - mint
1217
1218        if len(output_times) == 0:
1219            cin.InterpolateCamera(t * rng, self.camera)
1220            self.renderer.SetActiveCamera(self.camera)
1221            return [self.camera]
1222        else:
1223            vcams = []
1224            for tt in output_times:
1225                c = vtk.vtkCamera()
1226                cin.InterpolateCamera(tt * rng, c)
1227                vcams.append(c)
1228            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.

def fly_to(self, point):
1230    def fly_to(self, point):
1231        """
1232        Fly camera to the specified point.
1233
1234        Arguments:
1235            point : (list)
1236                point in space to place camera.
1237
1238        Example:
1239            ```python
1240            from vedo import *
1241            cone = Cone()
1242            plt = Plotter(axes=1)
1243            plt.show(cone)
1244            plt.fly_to([1,0,0])
1245            plt.interactive().close()
1246            ```
1247        """
1248        if self.interactor:
1249            self.resetcam = False
1250            self.interactor.FlyTo(self.renderer, point)
1251            self.camera = self.renderer.GetActiveCamera()
1252        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()
def look_at(self, plane='xy'):
1254    def look_at(self, plane="xy"):
1255        """Move the camera so that it looks at the specified cartesian plane"""
1256        cam = self.renderer.GetActiveCamera()
1257        fp = np.array(cam.GetFocalPoint())
1258        p = np.array(cam.GetPosition())
1259        dist = np.linalg.norm(fp - p)
1260        plane = plane.lower()
1261        if "x" in plane and "y" in plane:
1262            cam.SetPosition(fp[0], fp[1], fp[2] + dist)
1263            cam.SetViewUp(0.0, 1.0, 0.0)
1264        elif "x" in plane and "z" in plane:
1265            cam.SetPosition(fp[0], fp[1] - dist, fp[2])
1266            cam.SetViewUp(0.0, 0.0, 1.0)
1267        elif "y" in plane and "z" in plane:
1268            cam.SetPosition(fp[0] + dist, fp[1], fp[2])
1269            cam.SetViewUp(0.0, 0.0, 1.0)
1270        else:
1271            vedo.logger.error(f"in plotter.look() cannot understand argument {plane}")
1272        return self

Move the camera so that it looks at the specified cartesian plane

def record(self, filename='.vedo_recorded_events.log'):
1274    def record(self, filename=".vedo_recorded_events.log"):
1275        """
1276        Record camera, mouse, keystrokes and all other events.
1277        Recording can be toggled on/off by pressing key "R".
1278
1279        Arguments:
1280            filename : (str)
1281                ascii file to store events. The default is '.vedo_recorded_events.log'.
1282
1283        Returns:
1284            a string descriptor of events.
1285
1286        Examples:
1287            - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py)
1288        """
1289        erec = vtk.vtkInteractorEventRecorder()
1290        erec.SetInteractor(self.interactor)
1291        erec.SetFileName(filename)
1292        erec.SetKeyPressActivationValue("R")
1293        erec.EnabledOn()
1294        erec.Record()
1295        self.interactor.Start()
1296        erec.Stop()
1297        erec.EnabledOff()
1298        with open(filename, "r", encoding="UTF-8") as fl:
1299            events = fl.read()
1300        erec = None
1301        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_recorded_events.log'.
Returns:

a string descriptor of events.

Examples:
def play(self, events='.vedo_recorded_events.log', repeats=0):
1303    def play(self, events=".vedo_recorded_events.log", repeats=0):
1304        """
1305        Play camera, mouse, keystrokes and all other events.
1306
1307        Arguments:
1308            events : (str)
1309                file o string of events. The default is '.vedo_recorded_events.log'.
1310            repeats : (int)
1311                number of extra repeats of the same events. The default is 0.
1312
1313        Examples:
1314            - [record_play.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/record_play.py)
1315        """
1316        erec = vtk.vtkInteractorEventRecorder()
1317        erec.SetInteractor(self.interactor)
1318
1319        if events.endswith(".log"):
1320            erec.ReadFromInputStringOff()
1321            erec.SetFileName(events)
1322        else:
1323            erec.ReadFromInputStringOn()
1324            erec.SetInputString(events)
1325
1326        erec.Play()
1327        for _i in range(repeats):
1328            erec.Rewind()
1329            erec.Play()
1330        erec.EnabledOff()
1331        erec = None
1332        return self

Play camera, mouse, keystrokes and all other events.

Arguments:
  • events : (str) file o string of events. The default is '.vedo_recorded_events.log'.
  • repeats : (int) number of extra repeats of the same events. The default is 0.
Examples:
def parallel_projection(self, value=True, at=None):
1334    def parallel_projection(self, value=True, at=None):
1335        """
1336        Use parallel projection `at` a specified renderer.
1337        Object is seen from "infinite" distance, e.i. remove any perspective effects.
1338        An input value equal to -1 will toggle it on/off.
1339        """
1340        if at is not None:
1341            r = self.renderers[at]
1342        else:
1343            r = self.renderer
1344        if value == -1:
1345            val = r.GetActiveCamera().GetParallelProjection()
1346            value = not val
1347        r.GetActiveCamera().SetParallelProjection(value)
1348        r.Modified()
1349        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.

def fov(self, angle):
1351    def fov(self, angle):
1352        """
1353        Set the field of view angle for the camera.
1354        This is the angle of the camera frustum in the horizontal direction.
1355        High values will result in a wide-angle lens (fish-eye effect),
1356        and low values will result in a telephoto lens.
1357
1358        Default value is 30 degrees.
1359        """
1360        self.renderer.GetActiveCamera().UseHorizontalViewAngleOn()
1361        self.renderer.GetActiveCamera().SetViewAngle(angle)
1362        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.

def zoom(self, zoom):
1364    def zoom(self, zoom):
1365        """Apply a zooming factor for the current camera view"""
1366        self.renderer.GetActiveCamera().Zoom(zoom)
1367        return self

Apply a zooming factor for the current camera view

def azimuth(self, angle):
1369    def azimuth(self, angle):
1370        """Rotate camera around the view up vector."""
1371        self.renderer.GetActiveCamera().Azimuth(angle)
1372        return self

Rotate camera around the view up vector.

def elevation(self, angle):
1374    def elevation(self, angle):
1375        """Rotate the camera around the cross product of the negative
1376        of the direction of projection and the view up vector."""
1377        self.renderer.GetActiveCamera().Elevation(angle)
1378        return self

Rotate the camera around the cross product of the negative of the direction of projection and the view up vector.

def roll(self, angle):
1380    def roll(self, angle):
1381        """Roll the camera about the direction of projection."""
1382        self.renderer.GetActiveCamera().Roll(angle)
1383        return self

Roll the camera about the direction of projection.

def dolly(self, value):
1385    def dolly(self, value):
1386        """Move the camera towards (value>0) or away from (value<0) the focal point."""
1387        self.renderer.GetActiveCamera().Dolly(value)
1388        return self

Move the camera towards (value>0) or away from (value<0) the focal point.

def add_slider( self, sliderfunc: Callable, xmin, xmax, value=None, pos=4, title='', font='', title_size=1, c=None, alpha=1, show_value=True, delayed=False, **options):
1392    def add_slider(
1393        self,
1394        sliderfunc: Callable,
1395        xmin,
1396        xmax,
1397        value=None,
1398        pos=4,
1399        title="",
1400        font="",
1401        title_size=1,
1402        c=None,
1403        alpha=1,
1404        show_value=True,
1405        delayed=False,
1406        **options,
1407    ):
1408        """
1409        Add a `vedo.addons.Slider2D` which can call an external custom function.
1410
1411        Arguments:
1412            sliderfunc : (Callable)
1413                external function to be called by the widget
1414            xmin : (float)
1415                lower value of the slider
1416            xmax : (float)
1417                upper value
1418            value : (float)
1419                current value
1420            pos : (list, str)
1421                position corner number: horizontal [1-5] or vertical [11-15]
1422                it can also be specified by corners coordinates [(x1,y1), (x2,y2)]
1423                and also by a string descriptor (eg. "bottom-left")
1424            title : (str)
1425                title text
1426            font : (str)
1427                title font face. Check [available fonts here](https://vedo.embl.es/fonts).
1428            title_size : (float)
1429                title text scale [1.0]
1430            show_value : (bool)
1431                if True current value is shown
1432            delayed : (bool)
1433                if True the callback is delayed until when the mouse button is released
1434            alpha : (float)
1435                opacity of the scalar bar texts
1436            slider_length : (float)
1437                slider length
1438            slider_width : (float)
1439                slider width
1440            end_cap_length : (float)
1441                length of the end cap
1442            end_cap_width : (float)
1443                width of the end cap
1444            tube_width : (float)
1445                width of the tube
1446            title_height : (float)
1447                width of the title
1448            tformat : (str)
1449                format of the title
1450
1451        Examples:
1452            - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py)
1453            - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py)
1454
1455            ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg)
1456        """
1457        if c is None:  # automatic black or white
1458            c = (0.8, 0.8, 0.8)
1459            if np.sum(vedo.get_color(self.backgrcol)) > 1.5:
1460                c = (0.2, 0.2, 0.2)
1461        else:
1462            c = vedo.get_color(c)
1463
1464        slider2d = addons.Slider2D(
1465            sliderfunc,
1466            xmin,
1467            xmax,
1468            value,
1469            pos,
1470            title,
1471            font,
1472            title_size,
1473            c,
1474            alpha,
1475            show_value,
1476            delayed,
1477            **options,
1478        )
1479
1480        if self.renderer:
1481            slider2d.renderer = self.renderer
1482            if self.interactor:
1483                slider2d.interactor = self.interactor
1484                slider2d.on()
1485                self.sliders.append([slider2d, sliderfunc])
1486        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:

def add_slider3d( self, sliderfunc, pos1, pos2, xmin, xmax, value=None, s=0.03, t=1, title='', rotation=0.0, c=None, show_value=True):
1489    def add_slider3d(
1490        self,
1491        sliderfunc,
1492        pos1,
1493        pos2,
1494        xmin,
1495        xmax,
1496        value=None,
1497        s=0.03,
1498        t=1,
1499        title="",
1500        rotation=0.0,
1501        c=None,
1502        show_value=True,
1503    ):
1504        """
1505        Add a 3D slider widget which can call an external custom function.
1506
1507        Arguments:
1508            sliderfunc : (function)
1509                external function to be called by the widget
1510            pos1 : (list)
1511                first position 3D coordinates
1512            pos2 : (list)
1513                second position coordinates
1514            xmin : (float)
1515                lower value
1516            xmax : (float)
1517                upper value
1518            value : (float)
1519                initial value
1520            s : (float)
1521                label scaling factor
1522            t : (float)
1523                tube scaling factor
1524            title : (str)
1525                title text
1526            c : (color)
1527                slider color
1528            rotation : (float)
1529                title rotation around slider axis
1530            show_value : (bool)
1531                if True current value is shown
1532
1533        Examples:
1534            - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py)
1535
1536            ![](https://user-images.githubusercontent.com/32848391/52859555-4efcf200-312d-11e9-9290-6988c8295163.png)
1537        """
1538        if c is None:  # automatic black or white
1539            c = (0.8, 0.8, 0.8)
1540            if np.sum(vedo.get_color(self.backgrcol)) > 1.5:
1541                c = (0.2, 0.2, 0.2)
1542        else:
1543            c = vedo.get_color(c)
1544
1545        slider3d = addons.Slider3D(
1546            sliderfunc, pos1, pos2, xmin, xmax, value, s, t, title, rotation, c, show_value
1547        )
1548        slider3d.renderer = self.renderer
1549        slider3d.interactor = self.interactor
1550        slider3d.on()
1551        self.sliders.append([slider3d, sliderfunc])
1552        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:

def add_button( self, fnc=None, states=('On', 'Off'), c=('w', 'w'), bc=('green4', 'red4'), pos=(0.7, 0.05), size=24, font=None, bold=False, italic=False, alpha=1, angle=0, name='Button'):
1555    def add_button(
1556        self,
1557        fnc=None,
1558        states=("On", "Off"),
1559        c=("w", "w"),
1560        bc=("green4", "red4"),
1561        pos=(0.7, 0.05),
1562        size=24,
1563        font=None,
1564        bold=False,
1565        italic=False,
1566        alpha=1,
1567        angle=0,
1568        name="Button",
1569    ):
1570        """
1571        Add a button to the renderer window.
1572
1573        Arguments:
1574            states : (list)
1575                a list of possible states, e.g. ['On', 'Off']
1576            c : (list)
1577                a list of colors for each state
1578            bc : (list)
1579                a list of background colors for each state
1580            pos : (list)
1581                2D position in pixels from left-bottom corner
1582            size : (float)
1583                size of button font
1584            font : (str)
1585                font type. Check [available fonts here](https://vedo.embl.es/fonts).
1586            bold : (bool)
1587                bold font face (False)
1588            italic : (bool)
1589                italic font face (False)
1590            alpha : (float)
1591                opacity level
1592            angle : (float)
1593                anticlockwise rotation in degrees
1594
1595        Returns:
1596            `vedo.addons.Button` object.
1597
1598        Examples:
1599            - [buttons.py](https://github.com/marcomusy/vedo/blob/master/examples/basic/buttons.py)
1600
1601            ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg)
1602        """
1603        if self.interactor:
1604            bu = addons.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle, name)
1605            self.renderer.AddActor2D(bu)
1606            self.buttons.append(bu)
1607            bu.function_id = self.add_callback("LeftButtonPress", bu.function)
1608            return bu

Add a button to the renderer window.

Arguments:
  • states : (list) a list of possible states, e.g. ['On', 'Off']
  • c : (list) a list of colors for each state
  • bc : (list) a list of background colors for each state
  • pos : (list) 2D position in pixels from left-bottom corner
  • size : (float) size of button font
  • font : (str) font type. Check available fonts here.
  • bold : (bool) bold font face (False)
  • italic : (bool) italic font face (False)
  • alpha : (float) opacity level
  • angle : (float) anticlockwise rotation in degrees
Returns:

vedo.addons.Button object.

Examples:

def add_spline_tool( self, points, pc='k', ps=8, lc='r4', ac='g5', lw=2, closed=False, interactive=False):
1610    def add_spline_tool(
1611        self, points, pc="k", ps=8, lc="r4", ac="g5", lw=2, closed=False, interactive=False
1612    ):
1613        """
1614        Add a spline tool to the current plotter.
1615        Nodes of the spline can be dragged in space with the mouse.
1616        Clicking on the line itself adds an extra point.
1617        Selecting a point and pressing del removes it.
1618
1619        Arguments:
1620            points : (Mesh, Points, array)
1621                the set of vertices forming the spline nodes.
1622            pc : (str)
1623                point color. The default is 'k'.
1624            ps : (str)
1625                point size. The default is 8.
1626            lc : (str)
1627                line color. The default is 'r4'.
1628            ac : (str)
1629                active point marker color. The default is 'g5'.
1630            lw : (int)
1631                line width. The default is 2.
1632            closed : (bool)
1633                spline is meant to be closed. The default is False.
1634
1635        Returns:
1636            a `SplineTool` object.
1637
1638        Examples:
1639            - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py)
1640
1641            ![](https://vedo.embl.es/images/basic/spline_tool.png)
1642        """
1643        sw = addons.SplineTool(points, pc, ps, lc, ac, lw, closed)
1644        if self.interactor:
1645            sw.SetInteractor(self.interactor)
1646        else:
1647            vedo.logger.error("in add_spline_tool(), No interactor found.")
1648            raise RuntimeError
1649        sw.On()
1650        sw.Initialize(sw.points.polydata())
1651        if sw.closed:
1652            sw.representation.ClosedLoopOn()
1653        sw.representation.SetRenderer(self.renderer)
1654        sw.representation.BuildRepresentation()
1655        sw.Render()
1656        if interactive:
1657            self.interactor.Start()
1658        else:
1659            self.interactor.Render()
1660        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.
  • closed : (bool) spline is meant to be closed. The default is False.
Returns:

a SplineTool object.

Examples:

def add_icon(self, icon, pos=3, size=0.08):
1662    def add_icon(self, icon, pos=3, size=0.08):
1663        """Add an inset icon mesh into the same renderer.
1664
1665        Arguments:
1666            pos : (int, list)
1667                icon position in the range [1-4] indicating one of the 4 corners,
1668                or it can be a tuple (x,y) as a fraction of the renderer size.
1669            size : (float)
1670                size of the square inset.
1671
1672        Examples:
1673            - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py)
1674        """
1675        iconw = addons.Icon(icon, pos, size)
1676
1677        iconw.SetInteractor(self.interactor)
1678        iconw.EnabledOn()
1679        iconw.InteractiveOff()
1680        self.widgets.append(iconw)
1681        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:
def add_global_axes(self, axtype=None, c=None):
1684    def add_global_axes(self, axtype=None, c=None):
1685        """Draw axes on scene. Available axes types:
1686
1687        Arguments:
1688            axtype : (int)
1689                - 0,  no axes,
1690                - 1,  draw three gray grid walls
1691                - 2,  show cartesian axes from (0,0,0)
1692                - 3,  show positive range of cartesian axes from (0,0,0)
1693                - 4,  show a triad at bottom left
1694                - 5,  show a cube at bottom left
1695                - 6,  mark the corners of the bounding box
1696                - 7,  draw a 3D ruler at each side of the cartesian axes
1697                - 8,  show the vtkCubeAxesActor object
1698                - 9,  show the bounding box outLine
1699                - 10, show three circles representing the maximum bounding box
1700                - 11, show a large grid on the x-y plane
1701                - 12, show polar axes
1702                - 13, draw a simple ruler at the bottom of the window
1703
1704            Axis type-1 can be fully customized by passing a dictionary axes=dict().
1705
1706        Example:
1707            ```python
1708            from vedo import Box, show
1709            b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1)
1710            show(
1711                b,
1712                axes={
1713                    "xtitle": "Some long variable [a.u.]",
1714                    "number_of_divisions": 4,
1715                    # ...
1716                },
1717            )
1718            ```
1719
1720        Examples:
1721            - [custom_axes1.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes1.py)
1722            - [custom_axes2.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes2.py)
1723            - [custom_axes3.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes3.py)
1724            - [custom_axes4.py](https://github.com/marcomusy/vedo/blob/master/examples/pyplot/custom_axes4.py)
1725
1726            <img src="https://user-images.githubusercontent.com/32848391/72752870-ab7d5280-3bc3-11ea-8911-9ace00211e23.png" width="600">
1727        """
1728        addons.add_global_axes(axtype, c)
1729        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:

def add_legend_box(self, **kwargs):
1731    def add_legend_box(self, **kwargs):
1732        """Add a legend to the top right.
1733
1734        Examples:
1735            - [legendbox.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/basic/legendbox.py),
1736            - [flag_labels1.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels1.py)
1737            - [flag_labels2.py](https://github.com/marcomusy/vedo/blob/master/examples/examples/other/flag_labels2.py)
1738        """
1739        acts = self.get_meshes()
1740        lb = addons.LegendBox(acts, **kwargs)
1741        self.add(lb)
1742        return self

Add a legend to the top right.

Examples:
def add_hint( self, obj, text='', c='k', bc='yellow8', font='Calco', size=18, justify=0, angle=0, delay=100):
1744    def add_hint(
1745        self,
1746        obj,
1747        text="",
1748        c="k",
1749        bc="yellow8",
1750        font="Calco",
1751        size=18,
1752        justify=0,
1753        angle=0,
1754        delay=100,
1755    ):
1756        """
1757        Create a pop-up hint style message when hovering an object.
1758        Use add_hint(False) to disable all hints.
1759
1760        Arguments:
1761            obj : (Mesh, Points)
1762                the object to associate the pop-up to
1763            text : (str)
1764                string description of the pop-up
1765            delay : (int)
1766                milliseconds to wait before pop-up occurs
1767        """
1768        if self.offscreen:
1769            return self
1770
1771        if vedo.vtk_version[0] == 9 and "Linux" in vedo.sys_platform:  # Linux vtk9 is bugged
1772            vedo.logger.warning("add_hint() is not available on Linux platforms.")
1773            return self
1774
1775        if obj is False:
1776            self.hint_widget.EnabledOff()
1777            self.hint_widget = None
1778            return self
1779
1780        if text is False and self.hint_widget:
1781            self.hint_widget.RemoveBalloon(obj)
1782            return self
1783
1784        if text == "":
1785            if obj.name:
1786                text = obj.name
1787            elif obj.filename:
1788                text = obj.filename
1789            else:
1790                return self
1791
1792        if not self.hint_widget:
1793            self.hint_widget = vtk.vtkBalloonWidget()
1794
1795            rep = vtk.vtkBalloonRepresentation()
1796            rep.SetBalloonLayoutToImageRight()
1797
1798            trep = rep.GetTextProperty()
1799            trep.SetFontFamily(vtk.VTK_FONT_FILE)
1800            trep.SetFontFile(utils.get_font_path(font))
1801            trep.SetFontSize(size)
1802            trep.SetColor(vedo.get_color(c))
1803            trep.SetBackgroundColor(vedo.get_color(bc))
1804            trep.SetShadow(False)
1805            trep.SetJustification(justify)
1806            trep.UseTightBoundingBoxOn()
1807
1808            self.hint_widget.ManagesCursorOff()
1809            self.hint_widget.SetTimerDuration(delay)
1810            self.hint_widget.SetInteractor(self.interactor)
1811            if angle:
1812                rep.SetOrientation(angle)
1813                rep.SetBackgroundOpacity(0)
1814            self.hint_widget.SetRepresentation(rep)
1815            self.widgets.append(self.hint_widget)
1816            if self.interactor.GetInitialized():
1817                self.hint_widget.EnabledOn()
1818            else:
1819                vedo.logger.warning("add_hint() must be called after show(). Skip.")
1820                return self
1821
1822        bst = self.hint_widget.GetBalloonString(obj)
1823        if bst:
1824            self.hint_widget.UpdateBalloonString(obj, text)
1825        else:
1826            self.hint_widget.AddBalloon(obj, text)
1827
1828        return self

Create a pop-up hint style message when hovering an object. Use add_hint(False) 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
def add_shadows(self):
1831    def add_shadows(self):
1832        """Add shadows at the current renderer."""
1833        shadows = vtk.vtkShadowMapPass()
1834        seq = vtk.vtkSequencePass()
1835        passes = vtk.vtkRenderPassCollection()
1836        passes.AddItem(shadows.GetShadowMapBakerPass())
1837        passes.AddItem(shadows)
1838        seq.SetPasses(passes)
1839        camerapass = vtk.vtkCameraPass()
1840        camerapass.SetDelegatePass(seq)
1841        self.renderer.SetPass(camerapass)
1842        return self

Add shadows at the current renderer.

def add_ambient_occlusion(self, radius, bias=0.01, blur=True, samples=100):
1844    def add_ambient_occlusion(self, radius, bias=0.01, blur=True, samples=100):
1845        """
1846        Screen Space Ambient Occlusion.
1847
1848        For every pixel on the screen, the pixel shader samples the depth values around
1849        the current pixel and tries to compute the amount of occlusion from each of the sampled
1850        points.
1851
1852        Arguments:
1853            radius : (float)
1854                radius of influence in absolute units
1855            bias : (float)
1856                bias of the normals
1857            blur : (bool)
1858                add a blurring to the sampled positions
1859            samples : (int)
1860                number of samples to probe
1861
1862        Examples:
1863            - [ssao.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/ssao.py)
1864
1865            ![](https://vedo.embl.es/images/basic/ssao.jpg)
1866        """
1867        lights = vtk.vtkLightsPass()
1868
1869        opaque = vtk.vtkOpaquePass()
1870
1871        ssaoCam = vtk.vtkCameraPass()
1872        ssaoCam.SetDelegatePass(opaque)
1873
1874        ssao = vtk.vtkSSAOPass()
1875        ssao.SetRadius(radius)
1876        ssao.SetBias(bias)
1877        ssao.SetBlur(blur)
1878        ssao.SetKernelSize(samples)
1879        ssao.SetDelegatePass(ssaoCam)
1880
1881        translucent = vtk.vtkTranslucentPass()
1882
1883        volpass = vtk.vtkVolumetricPass()
1884        ddp = vtk.vtkDualDepthPeelingPass()
1885        ddp.SetTranslucentPass(translucent)
1886        ddp.SetVolumetricPass(volpass)
1887
1888        over = vtk.vtkOverlayPass()
1889
1890        collection = vtk.vtkRenderPassCollection()
1891        collection.AddItem(lights)
1892        collection.AddItem(ssao)
1893        collection.AddItem(ddp)
1894        collection.AddItem(over)
1895
1896        sequence = vtk.vtkSequencePass()
1897        sequence.SetPasses(collection)
1898
1899        cam = vtk.vtkCameraPass()
1900        cam.SetDelegatePass(sequence)
1901
1902        self.renderer.SetPass(cam)
1903        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:

def add_depth_of_field(self, autofocus=True):
1905    def add_depth_of_field(self, autofocus=True):
1906        """Add a depth of field effect in the scene."""
1907        lights = vtk.vtkLightsPass()
1908
1909        opaque = vtk.vtkOpaquePass()
1910
1911        dofCam = vtk.vtkCameraPass()
1912        dofCam.SetDelegatePass(opaque)
1913
1914        dof = vtk.vtkDepthOfFieldPass()
1915        dof.SetAutomaticFocalDistance(autofocus)
1916        dof.SetDelegatePass(dofCam)
1917
1918        collection = vtk.vtkRenderPassCollection()
1919        collection.AddItem(lights)
1920        collection.AddItem(dof)
1921
1922        sequence = vtk.vtkSequencePass()
1923        sequence.SetPasses(collection)
1924
1925        cam = vtk.vtkCameraPass()
1926        cam.SetDelegatePass(sequence)
1927
1928        self.renderer.SetPass(cam)
1929        return self

Add a depth of field effect in the scene.

def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None):
1965    def add_renderer_frame(self, c=None, alpha=None, lw=None, padding=None):
1966        """
1967        Add a frame to the renderer subwindow.
1968
1969        Arguments:
1970            c : (color)
1971                color name or index
1972            alpha : (float)
1973                opacity level
1974            lw : (int)
1975                line width in pixels.
1976            padding : (float)
1977                padding space in pixels.
1978        """
1979        if c is None:  # automatic black or white
1980            c = (0.9, 0.9, 0.9)
1981            if np.sum(vedo.plotter_instance.renderer.GetBackground()) > 1.5:
1982                c = (0.1, 0.1, 0.1)
1983        renf = addons.RendererFrame(c, alpha, lw, padding)
1984        self.renderer.AddActor(renf)
1985        return self

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.
def add_hover_legend( self, at=None, c=None, pos='bottom-left', font='Calco', s=0.75, bg='auto', alpha=0.1, maxlength=24, use_info=False):
1987    def add_hover_legend(
1988        self,
1989        at=None,
1990        c=None,
1991        pos="bottom-left",
1992        font="Calco",
1993        s=0.75,
1994        bg="auto",
1995        alpha=0.1,
1996        maxlength=24,
1997        use_info=False,
1998    ):
1999        """
2000        Add a legend with 2D text which is triggered by hovering the mouse on an object.
2001
2002        The created text object are stored in plotter.hover_legends
2003
2004        Arguments:
2005            c : (color)
2006                Text color. If None then black or white is chosen automatically
2007            pos : (str)
2008                text positioning
2009            font : (str)
2010                text font type. Check [available fonts here](https://vedo.embl.es/fonts).
2011            s : (float)
2012                text size scale
2013            bg : (color)
2014                background color of the 2D box containing the text
2015            alpha : (float)
2016                box transparency
2017            maxlength : (int)
2018                maximum number of characters per line
2019            use_info : (bool)
2020                visualize the content of the `obj.info` attribute
2021
2022        Examples:
2023            - [hover_legend.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/hover_legend.py)
2024            - [earthquake_browser.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/earthquake_browser.py)
2025
2026            ![](https://vedo.embl.es/images/pyplot/earthquake_browser.jpg)
2027        """
2028        hoverlegend = vedo.shapes.Text2D(pos=pos, font=font, c=c, s=s, alpha=alpha, bg=bg)
2029
2030        if at is None:
2031            at = self.renderers.index(self.renderer)
2032
2033        def _legfunc(evt):
2034            if not evt.actor or not self.renderer or at != evt.at:
2035                if hoverlegend._mapper.GetInput():  # clear and return
2036                    hoverlegend._mapper.SetInput("")
2037                    self.interactor.Render()
2038                return
2039
2040            if use_info:
2041                if hasattr(evt.actor, "info"):
2042                    t = str(evt.actor.info)
2043                else:
2044                    return
2045            else:
2046                t, tp = "", ""
2047                if evt.isMesh:
2048                    tp = "Mesh "
2049                elif evt.isPoints:
2050                    tp = "Points "
2051                # elif evt.isVolume:
2052                #     tp = "Volume "
2053                elif evt.isPicture:
2054                    tp = "Pict "
2055                elif evt.isAssembly:
2056                    tp = "Assembly "
2057                else:
2058                    return
2059
2060                if evt.isAssembly:
2061                    if not evt.actor.name:
2062                        t += f"Assembly object of {len(evt.actor.unpack())} parts\n"
2063                    else:
2064                        t += f"Assembly name: {evt.actor.name} ({len(evt.actor.unpack())} parts)\n"
2065                else:
2066                    if evt.actor.name:
2067                        t += f"{tp}name"
2068                        if evt.isPoints:
2069                            t += "  "
2070                        if evt.isMesh:
2071                            t += "  "
2072                        t += f": {evt.actor.name[:maxlength]}".ljust(maxlength) + "\n"
2073
2074                if evt.actor.filename:
2075                    t += f"{tp}filename: "
2076                    t += f"{os.path.basename(evt.actor.filename[-maxlength:])}".ljust(maxlength)
2077                    t += "\n"
2078                    if not evt.actor.file_size:
2079                        evt.actor.file_size, evt.actor.created = vedo.file_io.file_info(evt.actor.filename)
2080                    if evt.actor.file_size:
2081                        t += "             : "
2082                        sz, created = evt.actor.file_size, evt.actor.created
2083                        t += f"{created[4:-5]} ({sz})" + "\n"
2084
2085                if evt.isPoints:
2086                    indata = evt.actor.polydata(False)
2087                    if indata.GetNumberOfPoints():
2088                        t += (
2089                            f"#points/cells: {indata.GetNumberOfPoints()}"
2090                            f" / {indata.GetNumberOfCells()}"
2091                        )
2092                    pdata = indata.GetPointData()
2093                    cdata = indata.GetCellData()
2094                    if pdata.GetScalars() and pdata.GetScalars().GetName():
2095                        t += f"\nPoint array  : {pdata.GetScalars().GetName()}"
2096                        if pdata.GetScalars().GetName() == evt.actor.mapper().GetArrayName():
2097                            t += " *"
2098                    if cdata.GetScalars() and cdata.GetScalars().GetName():
2099                        t += f"\nCell  array  : {cdata.GetScalars().GetName()}"
2100                        if cdata.GetScalars().GetName() == evt.actor.mapper().GetArrayName():
2101                            t += " *"
2102
2103                if evt.isPicture:
2104                    t = f"{os.path.basename(evt.actor.filename[:maxlength+10])}".ljust(maxlength+10)
2105                    t += f"\nImage shape: {evt.actor.shape}"
2106                    pcol = self.color_picker(evt.picked2d)
2107                    t += f"\nPixel color: {vedo.colors.rgb2hex(pcol/255)} {pcol}"
2108
2109            # change box color if needed in 'auto' mode
2110            if evt.isPoints and "auto" in str(bg):
2111                actcol = evt.actor.GetProperty().GetColor()
2112                if hoverlegend._mapper.GetTextProperty().GetBackgroundColor() != actcol:
2113                    hoverlegend._mapper.GetTextProperty().SetBackgroundColor(actcol)
2114
2115            # adapt to changes in bg color
2116            bgcol = self.renderers[at].GetBackground()
2117            _bgcol = c
2118            if _bgcol is None:  # automatic black or white
2119                _bgcol = (0.9, 0.9, 0.9)
2120                if sum(bgcol) > 1.5:
2121                    _bgcol = (0.1, 0.1, 0.1)
2122                if len(set(_bgcol).intersection(bgcol)) < 3:
2123                    hoverlegend.color(_bgcol)
2124
2125            if hoverlegend._mapper.GetInput() != t:
2126                hoverlegend._mapper.SetInput(t)
2127                self.interactor.Render()
2128
2129        self.add(hoverlegend, at=at)
2130        self.hover_legends.append(hoverlegend)
2131        self.add_callback("MouseMove", _legfunc)
2132        return self

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

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:

def add_scale_indicator( self, pos=(0.7, 0.05), s=0.02, length=2, lw=4, c='k1', alpha=1, units='', gap=0.05):
2136    def add_scale_indicator(
2137        self, pos=(0.7, 0.05), s=0.02, length=2, lw=4, c="k1", alpha=1, units="", gap=0.05
2138    ):
2139        """
2140        Add a Scale Indicator. Only works in parallel mode (no perspective).
2141
2142        Arguments:
2143            pos : (list)
2144                fractional (x,y) position on the screen.
2145            s : (float)
2146                size of the text.
2147            length : (float)
2148                length of the line.
2149            units : (str)
2150                string to show units.
2151            gap : (float)
2152                separation of line and text.
2153
2154        Example:
2155            ```python
2156            from vedo import settings, Cube, Plotter
2157            settings.use_parallel_projection = True # or else it does not make sense!
2158            cube = Cube().alpha(0.2)
2159            plt = Plotter(size=(900,600), axes=dict(xtitle='x (um)'))
2160            plt.add_scale_indicator(units='um', c='blue4')
2161            plt.show(cube, "Scale indicator with units").close()
2162            ```
2163            ![](https://vedo.embl.es/images/feats/scale_indicator.png)
2164        """
2165        ppoints = vtk.vtkPoints()  # Generate the polyline
2166        psqr = [[0.0, gap], [length / 10, gap]]
2167        dd = psqr[1][0] - psqr[0][0]
2168        for i, pt in enumerate(psqr):
2169            ppoints.InsertPoint(i, pt[0], pt[1], 0)
2170        lines = vtk.vtkCellArray()
2171        lines.InsertNextCell(len(psqr))
2172        for i in range(len(psqr)):
2173            lines.InsertCellPoint(i)
2174        pd = vtk.vtkPolyData()
2175        pd.SetPoints(ppoints)
2176        pd.SetLines(lines)
2177
2178        wsx, wsy = self.window.GetSize()
2179        if not settings.use_parallel_projection:
2180            vedo.logger.warning("add_scale_indicator called with use_parallel_projection OFF. Skip.")
2181            return None
2182
2183        rlabel = vtk.vtkVectorText()
2184        rlabel.SetText("scale")
2185        tf = vtk.vtkTransformPolyDataFilter()
2186        tf.SetInputConnection(rlabel.GetOutputPort())
2187        t = vtk.vtkTransform()
2188        t.Scale(s * wsy / wsx, s, 1)
2189        tf.SetTransform(t)
2190
2191        app = vtk.vtkAppendPolyData()
2192        app.AddInputConnection(tf.GetOutputPort())
2193        app.AddInputData(pd)
2194
2195        mapper = vtk.vtkPolyDataMapper2D()
2196        mapper.SetInputConnection(app.GetOutputPort())
2197        cs = vtk.vtkCoordinate()
2198        cs.SetCoordinateSystem(1)
2199        mapper.SetTransformCoordinate(cs)
2200
2201        fractor = vtk.vtkActor2D()
2202        csys = fractor.GetPositionCoordinate()
2203        csys.SetCoordinateSystem(3)
2204        fractor.SetPosition(pos)
2205        fractor.SetMapper(mapper)
2206        fractor.GetProperty().SetColor(vedo.get_color(c))
2207        fractor.GetProperty().SetOpacity(alpha)
2208        fractor.GetProperty().SetLineWidth(lw)
2209        fractor.GetProperty().SetDisplayLocationToForeground()
2210
2211        def sifunc(iren, ev):
2212            wsx, wsy = self.window.GetSize()
2213            ps = self.camera.GetParallelScale()
2214            newtxt = utils.precision(ps / wsy * wsx * length * dd, 3)
2215            if units:
2216                newtxt += " " + units
2217            if rlabel.GetText() != newtxt:
2218                rlabel.SetText(newtxt)
2219
2220        self.renderer.AddActor(fractor)
2221        self.interactor.AddObserver("MouseWheelBackwardEvent", sifunc)
2222        self.interactor.AddObserver("MouseWheelForwardEvent", sifunc)
2223        self.interactor.AddObserver("InteractionEvent", sifunc)
2224        sifunc(0, 0)
2225        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()

def fill_event(self, ename='', pos=()):
2227    def fill_event(self, ename="", pos=()):
2228        """
2229        Create an Event object.
2230
2231        A 2D screen-position can be provided to be picked.
2232        """
2233        if not self.interactor:
2234            return Event()
2235
2236        if len(pos):
2237            x, y = pos
2238            self.interactor.SetEventPosition(pos)
2239        else:
2240            x, y = self.interactor.GetEventPosition()
2241        self.renderer = self.interactor.FindPokedRenderer(x, y)
2242        if not self.picker:
2243            self.picker = vtk.vtkPropPicker()
2244        self.picked2d = (x, y)
2245        self.picker.PickProp(x, y, self.renderer)
2246        xp, yp = self.interactor.GetLastEventPosition()
2247        actor = self.picker.GetProp3D()
2248        delta3d = np.array([0, 0, 0])
2249        if actor:
2250            picked3d = np.array(self.picker.GetPickPosition())
2251            if isinstance(actor, vedo.base.Base3DProp):  # needed!
2252                if actor.picked3d is not None:
2253                    delta3d = picked3d - actor.picked3d
2254            actor.picked3d = picked3d
2255        else:
2256            picked3d = None
2257
2258        if not actor:  # try 2D
2259            actor = self.picker.GetActor2D()
2260
2261        dx, dy = x - xp, y - yp
2262
2263        key = self.interactor.GetKeySym()
2264
2265        if key:
2266            if "_L" in key or "_R" in key:
2267                # skip things like Shift_R
2268                key = ""  # better than None
2269            else:
2270                if self.interactor.GetShiftKey():
2271                    key = key.upper()
2272
2273                if key == "MINUS":  # fix: vtk9 is ignoring shift chars..
2274                    key = "underscore"
2275                elif key == "EQUAL":  # fix: vtk9 is ignoring shift chars..
2276                    key = "plus"
2277                elif key == "SLASH":  # fix: vtk9 is ignoring shift chars..
2278                    key = "?"
2279
2280                if self.interactor.GetControlKey():
2281                    key = "Ctrl+" + key
2282
2283                if self.interactor.GetAltKey():
2284                    key = "Alt+" + key
2285
2286        event = Event()
2287        event.name = ename
2288        event.title = self.title
2289        event.id = -1  # will be set by the timer wrapper function
2290        event.timerid = -1  # will be set by the timer wrapper function
2291        event.priority = -1  # will be set by the timer wrapper function
2292        event.time = time.time()
2293        event.at = self.renderers.index(self.renderer)
2294        event.actor = actor
2295        event.picked3d = picked3d
2296        event.keyPressed = key  # obsolete, will disappear. Use "keypress"
2297        event.keypress = key
2298        event.picked2d = (x, y)
2299        event.delta2d = (dx, dy)
2300        event.angle2d = np.arctan2(dy, dx)
2301        event.speed2d = np.sqrt(dx * dx + dy * dy)
2302        event.delta3d = delta3d
2303        event.speed3d = np.sqrt(np.dot(delta3d, delta3d))
2304        event.isPoints = isinstance(actor, vedo.Points)
2305        event.isMesh = isinstance(actor, vedo.Mesh)
2306        event.isAssembly = isinstance(actor, vedo.Assembly)
2307        event.isVolume = isinstance(actor, vedo.Volume)
2308        event.isPicture = isinstance(actor, vedo.Picture)
2309        event.isActor2D = isinstance(actor, vtk.vtkActor2D)
2310        return event

Create an Event object.

A 2D screen-position can be provided to be picked.

def add_callback(self, event_name, func, priority=0.0):
2313    def add_callback(self, event_name, func, priority=0.0):
2314        """
2315        Add a function to be executed while show() is active.
2316        Information about the event can be acquired with method getEvent().
2317
2318        Return a unique id for the callback.
2319
2320        The callback function (see example below) exposes a dictionary
2321        with the following information:
2322        - `name`: event name,
2323        - `id`: event unique identifier,
2324        - `priority`: event priority (float),
2325        - `interactor`: the interactor object,
2326        - `at`: renderer nr. where the event occurred
2327        - `actor`: object picked by the mouse
2328        - `picked3d`: point picked in world coordinates
2329        - `keypress`: key pressed as string
2330        - `picked2d`: screen coords of the mouse pointer
2331        - `delta2d`: shift wrt previous position (to calculate speed, direction)
2332        - `delta3d`: ...same but in 3D world coords
2333        - `angle2d`: angle of mouse movement on screen
2334        - `speed2d`: speed of mouse movement on screen
2335        - `speed3d`: speed of picked point in world coordinates
2336        - `isPoints`: True if of class
2337        - `isMesh`: True if of class
2338        - `isAssembly`: True if of class
2339        - `isVolume`: True if of class Volume
2340        - `isPicture`: True if of class
2341
2342        Frequently used events are:
2343        - `KeyPress`, `KeyRelease`: listen to keyboard events
2344        - `LeftButtonPress`, `LeftButtonRelease`: listen to mouse clicks
2345        - `MiddleButtonPress`, `MiddleButtonRelease`
2346        - `RightButtonPress`, `RightButtonRelease`
2347        - `MouseMove`: listen to mouse pointer changing position
2348        - `MouseWheelForward`, `MouseWheelBackward`
2349        - `Enter`, `Leave`: listen to mouse entering or leaving the window
2350        - `Pick`, `StartPick`, `EndPick`: listen to object picking
2351        - `ResetCamera`, `ResetCameraClippingRange`
2352        - `Error`, `Warning`
2353        - `Char`
2354        - `Timer`
2355
2356        Check the complete list of events [here](https://vtk.org/doc/nightly/html/classvtkCommand.html).
2357
2358        Example:
2359            ```python
2360            from vedo import *
2361
2362            def func(evt):
2363                # this function is called every time the mouse moves
2364                # (evt is a dotted dictionary)
2365                if not evt.actor:
2366                    return  # no hit, return
2367                print("point coords =", evt.picked3d)
2368                # print("full event dump:", evt)
2369
2370            elli = Ellipsoid()
2371            plt = Plotter(axes=1)
2372            plt.add_callback('mouse hovering', func)
2373            plt.show(elli).close()
2374            ```
2375
2376        Examples:
2377            - [spline_draw.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/spline_draw.py)
2378            - [colorlines.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/colorlines.py)
2379
2380                ![](https://vedo.embl.es/images/advanced/spline_draw.png)
2381
2382            - ..and many others!
2383        """
2384        from vtkmodules.util.misc import calldata_type
2385
2386        if not self.interactor:
2387            return None
2388
2389        # as vtk names are ugly and difficult to remember:
2390        ln = event_name.lower()
2391        if "click" in ln or "button" in ln:
2392            event_name = "LeftButtonPress"
2393            if "right" in ln:
2394                event_name = "RightButtonPress"
2395            elif "mid" in ln:
2396                event_name = "MiddleButtonPress"
2397            if "release" in ln:
2398                # event_name = event_name.replace("Press","Release") # vtk bug
2399                event_name = "EndInteraction"
2400        else:
2401            if "key" in ln:
2402                if "release" in ln:
2403                    event_name = "KeyRelease"
2404                else:
2405                    event_name = "KeyPress"
2406
2407        if ("mouse" in ln and "mov" in ln) or "over" in ln:
2408            event_name = "MouseMove"
2409        if "timer" in ln:
2410            event_name = "Timer"
2411
2412        if not event_name.endswith("Event"):
2413            event_name += "Event"
2414
2415        @calldata_type(vtk.VTK_INT)
2416        def _func_wrap(iren, ename, timerid=None):
2417            event = self.fill_event(ename=ename)
2418            event.timerid = timerid
2419            event.id = cid
2420            event.priority = priority
2421            self.last_event = event
2422            func(event)
2423            return  ## _func_wrap
2424
2425        # Not compatible with ProcessEvents()
2426        if "MouseMove" in event_name or "Timer" in event_name:
2427            settings.allow_interaction = False
2428
2429        cid = self.interactor.AddObserver(event_name, _func_wrap, priority)
2430        vedo.logger.debug(f"registering event: {event_name} with id={cid}")
2431        return cid

Add a function to be executed while show() is active. Information about the event can be acquired with method getEvent().

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 occurred
  • actor: object picked by the mouse
  • picked3d: point picked in world coordinates
  • keypress: key pressed as string
  • picked2d: screen coords of the mouse pointer
  • delta2d: shift wrt previous position (to calculate speed, direction)
  • delta3d: ...same but in 3D world coords
  • angle2d: angle of mouse movement on screen
  • speed2d: speed of mouse movement on screen
  • speed3d: speed of picked point in world coordinates
  • isPoints: True if of class
  • isMesh: True if of class
  • isAssembly: True if of class
  • isVolume: True if of class Volume
  • isPicture: True if of class

Frequently used events are:

  • KeyPress, KeyRelease: listen to keyboard events
  • LeftButtonPress, LeftButtonRelease: listen to mouse clicks
  • MiddleButtonPress, MiddleButtonRelease
  • RightButtonPress, RightButtonRelease
  • MouseMove: listen to mouse pointer changing position
  • MouseWheelForward, MouseWheelBackward
  • Enter, Leave: listen to mouse entering or leaving the window
  • Pick, StartPick, EndPick: listen to object picking
  • ResetCamera, 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.actor:
        return  # no hit, return
    print("point coords =", evt.picked3d)
    # print("full event dump:", evt)

elli = Ellipsoid()
plt = Plotter(axes=1)
plt.add_callback('mouse hovering', func)
plt.show(elli).close()
Examples:
def remove_callback(self, cid):
2433    def remove_callback(self, cid):
2434        """
2435        Remove a callback function by its id
2436        or a whole category of callbacks by their name.
2437
2438        Arguments:
2439            cid : (int, str)
2440                Unique id of the callback.
2441                If an event name is passed all callbacks of that type are removed.
2442        """
2443        if self.interactor:
2444            if isinstance(cid, str):
2445                # as vtk names are ugly and difficult to remember:
2446                ln = cid.lower()
2447                if "click" in ln or "button" in ln:
2448                    cid = "LeftButtonPress"
2449                    if "right" in ln:
2450                        cid = "RightButtonPress"
2451                    elif "mid" in ln:
2452                        cid = "MiddleButtonPress"
2453                    if "release" in ln:
2454                        cid.replace("Press", "Release")
2455                else:
2456                    if "key" in ln:
2457                        if "release" in ln:
2458                            cid = "KeyRelease"
2459                        else:
2460                            cid = "KeyPress"
2461                if ("mouse" in ln and "mov" in ln) or "over" in ln:
2462                    cid = "MouseMove"
2463                if "timer" in ln:
2464                    cid = "Timer"
2465                if not cid.endswith("Event"):
2466                    cid += "Event"
2467                self.interactor.RemoveObservers(cid)
2468            else:
2469                self.interactor.RemoveObserver(cid)
2470        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.
def timer_callback(self, action, timer_id=None, dt=1, one_shot=False):
2472    def timer_callback(self, action, timer_id=None, dt=1, one_shot=False):
2473        """
2474        Start or stop an existing timer.
2475
2476        Arguments:
2477            action : (str)
2478                Either "create"/"start" or "destroy"/"stop"
2479            timer_id : (int)
2480                When stopping the timer, the ID of the timer as returned when created
2481            dt : (int)
2482                time in milliseconds between each repeated call
2483            one_shot : (bool)
2484                create a one shot timer of prescribed duration instead of a repeating one
2485
2486        Examples:
2487            - [timer_callback1.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback1.py)
2488            - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py)
2489
2490            ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg)
2491        """
2492        if action in ("create", "start"):
2493            if timer_id is not None:
2494                vedo.logger.warning("you set a timer_id but it will be ignored.")
2495            if one_shot:
2496                timer_id = self.interactor.CreateOneShotTimer(dt)
2497            else:
2498                timer_id = self.interactor.CreateRepeatingTimer(dt)
2499            return timer_id
2500
2501        elif action in ("destroy", "stop"):
2502            if timer_id is not None:
2503                self.interactor.DestroyTimer(timer_id)
2504            else:
2505                vedo.logger.warning("please set a timer_id. Cannot stop timer.")
2506        else:
2507            e = f"in timer_callback(). Cannot understand action: {action}\n"
2508            e += " allowed actions are: ['start', 'stop']. Skipped."
2509            vedo.logger.error(e)
2510        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:

def compute_world_coordinate( self, pos2d, at=None, objs=(), bounds=(), offset=None, pixeltol=None, worldtol=None):
2512    def compute_world_coordinate(
2513        self, pos2d, at=None, objs=(), bounds=(), offset=None, pixeltol=None, worldtol=None
2514    ):
2515        """
2516        Transform a 2D point on the screen into a 3D point inside the rendering scene.
2517        If a set of meshes is passed then points are placed onto these.
2518
2519        Arguments:
2520            pos2d : (list)
2521                2D screen coordinates point.
2522            at : (int)
2523                renderer number.
2524            objs : (list)
2525                list of Mesh objects to project the point onto.
2526            bounds : (list)
2527                specify a bounding box as [xmin,xmax, ymin,ymax, zmin,zmax].
2528            offset : (float)
2529                specify an offset value.
2530            pixeltol : (int)
2531                screen tolerance in pixels.
2532            worldtol : (float)
2533                world coordinates tolerance.
2534
2535        Returns:
2536            numpy array, the point in 3D world coordinates.
2537
2538        Examples:
2539            - [cut_freehand.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/cut_freehand.py)
2540            - [mousehover3.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mousehover3.py)
2541
2542            ![](https://vedo.embl.es/images/basic/mousehover3.jpg)
2543        """
2544        if at is not None:
2545            renderer = self.renderers[at]
2546        else:
2547            renderer = self.renderer
2548
2549        if not objs:
2550            pp = vtk.vtkFocalPlanePointPlacer()
2551        else:
2552            pp = vtk.vtkPolygonalSurfacePointPlacer()
2553            for ob in objs:
2554                pp.AddProp(ob)
2555
2556        if len(bounds) == 6:
2557            pp.SetPointBounds(bounds)
2558        if pixeltol:
2559            pp.SetPixelTolerance(pixeltol)
2560        if worldtol:
2561            pp.SetWorldTolerance(worldtol)
2562        if offset:
2563            pp.SetOffset(offset)
2564
2565        worldPos = [0, 0, 0]
2566        worldOrient = [0, 0, 0, 0, 0, 0, 0, 0, 0]
2567        pp.ComputeWorldPosition(renderer, pos2d, worldPos, worldOrient)
2568        # validw = pp.ValidateWorldPosition(worldPos, worldOrient)
2569        # validd = pp.ValidateDisplayPosition(renderer, pos2d)
2570        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:

def compute_screen_coordinates(self, obj, full_window=False):
2572    def compute_screen_coordinates(self, obj, full_window=False):
2573        """
2574        Given a 3D points in the current renderer (or full window),
2575        find the screen pixel coordinates.
2576
2577        Example:
2578            ```python
2579            from vedo import *
2580
2581            elli = Ellipsoid().rotate_y(30)
2582
2583            plt = Plotter()
2584            plt.show(elli)
2585
2586            xyscreen = plt.compute_screen_positions(elli)
2587            print('xyscreen coords:', xyscreen)
2588
2589            # simulate an event happening at one point
2590            event = plt.fill_event(pos=xyscreen[123])
2591            print(event)
2592            ```
2593        """
2594        if isinstance(obj, vedo.base.Base3DProp):
2595            pts = obj.points()
2596        elif utils.is_sequence(obj):
2597            pts = obj
2598        p2d = []
2599        cs = vtk.vtkCoordinate()
2600        cs.SetCoordinateSystemToWorld()
2601        cs.SetViewport(self.renderer)
2602        for p in pts:
2603            cs.SetValue(p)
2604            if full_window:
2605                p2d.append(cs.GetComputedDisplayValue(self.renderer))
2606            else:
2607                p2d.append(cs.GetComputedViewportValue(self.renderer))
2608        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().rotate_y(30)

plt = Plotter()
plt.show(elli)

xyscreen = plt.compute_screen_positions(elli)
print('xyscreen coords:', xyscreen)

# simulate an event happening at one point
event = plt.fill_event(pos=xyscreen[123])
print(event)
def pick_area(self, pos1, pos2, at=None):
2610    def pick_area(self, pos1, pos2, at=None):
2611        """
2612        Pick all objects within a box defined by two corner points in 2D screen coordinates.
2613        
2614        Returns a frustum Mesh that contains the visible field of view.
2615        This can be used to select objects in a scene or select vertices.
2616
2617        Example:
2618            ```python
2619            from vedo import *
2620
2621            settings.enable_default_mouse_callbacks = False
2622
2623            def mode_select(objs):
2624                print("Selected objects:", objs)
2625                d0 = mode.start_x, mode.start_y # display coords
2626                d1 = mode.end_x, mode.end_y
2627
2628                frustum = plt.pick_area(d0, d1)
2629                infru = frustum.inside_points(mesh)
2630                col = np.random.randint(0, 10)
2631                infru.ps(10).c(col)
2632                plt.add(frustum, infru).render()
2633
2634            mesh = Mesh(dataurl+"cow.vtk").c("k5").lw(1)
2635
2636            mode = interactor_modes.BlenderStyle()
2637            mode.callback_select = mode_select
2638
2639            plt = Plotter().user_mode(mode)
2640            plt.show(mesh, axes=1)
2641            ```
2642        """
2643        if at is not None:
2644            ren = self.renderers[at]
2645        else:
2646            ren = self.renderer
2647        area_picker = vtk.vtkAreaPicker()
2648        area_picker.AreaPick(pos1[0], pos1[1], pos2[0], pos2[1], ren)
2649        planes = area_picker.GetFrustum()
2650
2651        fru = vtk.vtkFrustumSource()
2652        fru.SetPlanes(planes)
2653        fru.ShowLinesOff()
2654        fru.Update()
2655
2656        afru = vedo.Mesh(fru.GetOutput())
2657        afru.alpha(0.1).lw(1).pickable(False)
2658        afru.name = "Frustrum"
2659        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)
    infru = frustum.inside_points(mesh)
    col = np.random.randint(0, 10)
    infru.ps(10).c(col)
    plt.add(frustum, infru).render()

mesh = Mesh(dataurl+"cow.vtk").c("k5").lw(1)

mode = interactor_modes.BlenderStyle()
mode.callback_select = mode_select

plt = Plotter().user_mode(mode)
plt.show(mesh, axes=1)
def show( self, *actors, at=None, axes=None, resetcam=None, zoom=False, interactive=None, viewup='', azimuth=0.0, elevation=0.0, roll=0.0, camera=None, mode=0, rate=None, bg=None, bg2=None, size=None, title=None):
2793    def show(
2794        self,
2795        *actors,
2796        at=None,
2797        axes=None,
2798        resetcam=None,
2799        zoom=False,
2800        interactive=None,
2801        viewup="",
2802        azimuth=0.0,
2803        elevation=0.0,
2804        roll=0.0,
2805        camera=None,
2806        mode=0,
2807        rate=None,
2808        bg=None,
2809        bg2=None,
2810        size=None,
2811        title=None,
2812    ):
2813        """
2814        Render a list of objects.
2815
2816        Arguments:
2817            at : (int)
2818                number of the renderer to plot to, in case of more than one exists
2819
2820            axes : (int)
2821                axis type-1 can be fully customized by passing a dictionary.
2822                Check `addons.Axes()` for the full list of options.
2823                set the type of axes to be shown:
2824                - 0,  no axes
2825                - 1,  draw three gray grid walls
2826                - 2,  show cartesian axes from (0,0,0)
2827                - 3,  show positive range of cartesian axes from (0,0,0)
2828                - 4,  show a triad at bottom left
2829                - 5,  show a cube at bottom left
2830                - 6,  mark the corners of the bounding box
2831                - 7,  draw a 3D ruler at each side of the cartesian axes
2832                - 8,  show the `vtkCubeAxesActor` object
2833                - 9,  show the bounding box outLine
2834                - 10, show three circles representing the maximum bounding box
2835                - 11, show a large grid on the x-y plane
2836                - 12, show polar axes
2837                - 13, draw a simple ruler at the bottom of the window
2838
2839            azimuth/elevation/roll : (float)
2840                move camera accordingly the specified value
2841
2842            viewup: str, list
2843                either `['x', 'y', 'z']` or a vector to set vertical direction
2844
2845            resetcam : (bool)
2846                re-adjust camera position to fit objects
2847
2848            camera : (dict, vtkCamera)
2849                camera parameters can further be specified with a dictionary
2850                assigned to the ``camera`` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`):
2851                - pos, `(list)`,  the position of the camera in world coordinates
2852                - focal_point `(list)`, the focal point of the camera in world coordinates
2853                - viewup `(list)`, the view up direction for the camera
2854                - distance `(float)`, set the focal point to the specified distance from the camera position.
2855                - clipping_range `(float)`, distance of the near and far clipping planes along the direction of projection.
2856                - parallel_scale `(float)`,
2857                scaling used for a parallel projection, i.e. the height of the viewport
2858                in world-coordinate distances. The default is 1. Note that the "scale" parameter works as
2859                an "inverse scale", larger numbers produce smaller images.
2860                This method has no effect in perspective projection mode.
2861
2862                - thickness `(float)`,
2863                set the distance between clipping planes. This method adjusts the far clipping
2864                plane to be set a distance 'thickness' beyond the near clipping plane.
2865
2866                - view_angle `(float)`,
2867                the camera view angle, which is the angular height of the camera view
2868                measured in degrees. The default angle is 30 degrees.
2869                This method has no effect in parallel projection mode.
2870                The formula for setting the angle up for perfect perspective viewing is:
2871                angle = 2*atan((h/2)/d) where h is the height of the RenderWindow
2872                (measured by holding a ruler up to your screen) and d is the distance
2873                from your eyes to the screen.
2874
2875            interactive : (bool)
2876                pause and interact with window (True) or continue execution (False)
2877
2878            rate : (float)
2879                maximum rate of `show()` in Hertz
2880
2881            mode : (int, str)
2882                set the type of interaction:
2883                - 0 = TrackballCamera [default]
2884                - 1 = TrackballActor
2885                - 2 = JoystickCamera
2886                - 3 = JoystickActor
2887                - 4 = Flight
2888                - 5 = RubberBand2D
2889                - 6 = RubberBand3D
2890                - 7 = RubberBandZoom
2891                - 8 = Terrain
2892                - 9 = Unicam
2893                - 10 = Image
2894                - Check out `vedo.interaction_modes` for more options.
2895        """
2896        if self.wx_widget:
2897            return self
2898
2899        if self.renderers:  # in case of notebooks
2900
2901            if at is None:
2902                at = self.renderers.index(self.renderer)
2903
2904            else:
2905
2906                if at >= len(self.renderers):
2907                    t = f"trying to show(at={at}) but only {len(self.renderers)} renderers exist"
2908                    vedo.logger.error(t)
2909                    return self
2910
2911                self.renderer = self.renderers[at]
2912
2913        if title is not None:
2914            self.title = title
2915
2916        if size is not None:
2917            self.size = size
2918            if self.size[0] == "f":  # full screen
2919                self.size = "fullscreen"
2920                self.window.SetFullScreen(True)
2921                self.window.BordersOn()
2922            else:
2923                self.window.SetSize(int(self.size[0]), int(self.size[1]))
2924
2925        if settings.default_backend == "vtk":
2926            if str(bg).endswith(".hdr"):
2927                self._add_skybox(bg)
2928            else:
2929                if bg is not None:
2930                    self.backgrcol = vedo.get_color(bg)
2931                    self.renderer.SetBackground(self.backgrcol)
2932                if bg2 is not None:
2933                    self.renderer.GradientBackgroundOn()
2934                    self.renderer.SetBackground2(vedo.get_color(bg2))
2935
2936        if axes is not None:
2937            if isinstance(axes, vedo.Assembly):  # user passing show(..., axes=myaxes)
2938                actors = list(actors)
2939                actors.append(axes)  # move it into the list of normal things to show
2940                axes = 0
2941            self.axes = axes
2942
2943        if self.offscreen:
2944            interactive = False
2945            self._interactive = False
2946
2947        # camera stuff
2948        if resetcam is not None:
2949            self.resetcam = resetcam
2950
2951        if camera is not None:
2952            self.resetcam = False
2953            if isinstance(camera, vtk.vtkCamera):
2954                self.camera = camera
2955            else:
2956                self.camera = utils.camera_from_dict(camera)
2957            if self.renderer:
2958                self.renderer.SetActiveCamera(self.camera)
2959
2960        if self.renderer:
2961            self.camera = self.renderer.GetActiveCamera()
2962
2963        self.add(actors)
2964
2965        # Backend ###############################################################
2966        if settings.default_backend != "vtk":
2967            if settings.default_backend in ["k3d"]:
2968                return backends.get_notebook_backend(self.actors)
2969        #########################################################################
2970
2971        for ia in utils.flatten(actors):
2972            if isinstance(ia, vedo.base.Base3DProp):
2973                try:
2974                    # fix gray color labels and title to white or black
2975                    ltc = np.array(ia.scalarbar.GetLabelTextProperty().GetColor())
2976                    if np.linalg.norm(ltc - (0.5, 0.5, 0.5)) / 3 < 0.05:
2977                        c = (0.9, 0.9, 0.9)
2978                        if np.sum(self.renderer.GetBackground()) > 1.5:
2979                            c = (0.1, 0.1, 0.1)
2980                        ia.scalarbar.GetLabelTextProperty().SetColor(c)
2981                        ia.scalarbar.GetTitleTextProperty().SetColor(c)
2982                except AttributeError:
2983                    pass
2984
2985        if self.sharecam:
2986            for r in self.renderers:
2987                r.SetActiveCamera(self.camera)
2988
2989        if self.qt_widget is not None:
2990            self.qt_widget.GetRenderWindow().AddRenderer(self.renderer)
2991
2992
2993        if self.axes is not None:
2994            if viewup != "2d" or self.axes in [1, 8] or isinstance(self.axes, dict):
2995                bns = self.renderer.ComputeVisiblePropBounds()
2996                addons.add_global_axes(self.axes, bounds=bns)
2997
2998        # Backend ###############################################################
2999        if settings.default_backend in ["ipyvtk", "trame"]:
3000            return backends.get_notebook_backend()
3001        #########################################################################
3002
3003        if self.resetcam:
3004            self.renderer.ResetCamera()
3005
3006        if len(self.renderers) > 1:
3007            self.add_renderer_frame()
3008
3009        if settings.default_backend == "2d" and not zoom:
3010            zoom = "tightest"
3011
3012        if zoom:
3013            if zoom == "tight":
3014                self.reset_camera(tight=0.04)
3015            elif zoom == "tightest":
3016                self.reset_camera(tight=0.0001)
3017            else:
3018                self.camera.Zoom(zoom)
3019        if elevation:
3020            self.camera.Elevation(elevation)
3021        if azimuth:
3022            self.camera.Azimuth(azimuth)
3023        if roll:
3024            self.camera.Roll(roll)
3025
3026        if len(viewup) > 0:
3027            b = self.renderer.ComputeVisiblePropBounds()
3028            cm = np.array([(b[1] + b[0]) / 2, (b[3] + b[2]) / 2, (b[5] + b[4]) / 2])
3029            sz = np.array([(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])])
3030            if viewup == "x":
3031                sz = np.linalg.norm(sz)
3032                self.camera.SetViewUp([1, 0, 0])
3033                self.camera.SetPosition(cm + sz)
3034            elif viewup == "y":
3035                sz = np.linalg.norm(sz)
3036                self.camera.SetViewUp([0, 1, 0])
3037                self.camera.SetPosition(cm + sz)
3038            elif viewup == "z":
3039                sz = np.array([(b[1] - b[0]) * 0.7, -(b[3] - b[2]) * 1.0, (b[5] - b[4]) * 1.2])
3040                self.camera.SetViewUp([0, 0, 1])
3041                self.camera.SetPosition(cm + 2 * sz)
3042            elif utils.is_sequence(viewup):
3043                sz = np.linalg.norm(sz)
3044                self.camera.SetViewUp(viewup)
3045                cpos = np.cross([0, 1, 0], viewup)
3046                self.camera.SetPosition(cm - 2 * sz * cpos)
3047
3048        self.renderer.ResetCameraClippingRange()
3049
3050        if self.interactor and not self.interactor.GetInitialized():
3051            self.interactor.Initialize()
3052            self.interactor.RemoveObservers("CharEvent")
3053
3054        if settings.immediate_rendering:
3055            self.window.Render()  ##################### <-------------- Render
3056
3057        # 2d ####################################################################
3058        if settings.default_backend == "2d":
3059            return backends.get_notebook_backend()
3060        #########################################################################
3061
3062        self.window.SetWindowName(self.title)
3063
3064        try:
3065            # Needs "pip install pyobjc" on Mac OSX
3066            if (
3067                self._cocoa_initialized is False
3068                and "Darwin" in vedo.sys_platform
3069                and not self.offscreen
3070            ):
3071                self._cocoa_initialized = True
3072                from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps
3073                pid = os.getpid()
3074                x = NSRunningApplication.runningApplicationWithProcessIdentifier_(int(pid))
3075                x.activateWithOptions_(NSApplicationActivateIgnoringOtherApps)
3076        except:
3077            pass
3078            # vedo.logger.debug("On Mac OSX try: pip install pyobjc")
3079
3080        if self.interactor:  # can be offscreen..
3081
3082            if interactive is not None:
3083                self._interactive = interactive
3084
3085            self.user_mode(mode)
3086
3087            if self._interactive:
3088                self.interactor.Start()
3089                
3090            if rate:
3091                if self.clock is None:  # set clock and limit rate
3092                    self._clockt0 = time.time()
3093                    self.clock = 0.0
3094                else:
3095                    t = time.time() - self._clockt0
3096                    elapsed = t - self.clock
3097                    mint = 1.0 / rate
3098                    if elapsed < mint:
3099                        time.sleep(mint - elapsed)
3100                    self.clock = time.time() - self._clockt0
3101
3102        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.

  • 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.
def add_inset(self, *actors, **options):
3105    def add_inset(self, *actors, **options):
3106        """Add a draggable inset space into a renderer.
3107
3108        Arguments:
3109            at : (int)
3110                specify the renderer number
3111            pos : (list)
3112                icon position in the range [1-4] indicating one of the 4 corners,
3113                or it can be a tuple (x,y) as a fraction of the renderer size.
3114            size : (float)
3115                size of the square inset
3116            draggable : (bool)
3117                if True the subrenderer space can be dragged around
3118            c : (color)
3119                color of the inset frame when dragged
3120
3121        Examples:
3122            - [inset.py](https://github.com/marcomusy/vedo/tree/master/examples/other/inset.py)
3123
3124            ![](https://user-images.githubusercontent.com/32848391/56758560-3c3f1300-6797-11e9-9b33-49f5a4876039.jpg)
3125        """
3126        if not self.interactor:
3127            return None
3128        pos = options.pop("pos", 0)
3129        size = options.pop("size", 0.1)
3130        c = options.pop("c", "lb")
3131        at = options.pop("at", None)
3132        draggable = options.pop("draggable", True)
3133
3134        if not self.renderer:
3135            vedo.logger.warning("call add_inset() only after first rendering of the scene.")
3136            save_int = self._interactive
3137            self.show(interactive=0)
3138            self._interactive = save_int
3139        widget = vtk.vtkOrientationMarkerWidget()
3140        r, g, b = vedo.get_color(c)
3141        widget.SetOutlineColor(r, g, b)
3142        if len(actors) == 1:
3143            widget.SetOrientationMarker(actors[0])
3144        else:
3145            widget.SetOrientationMarker(vedo.Assembly(actors))
3146
3147        widget.SetInteractor(self.interactor)
3148
3149        if utils.is_sequence(pos):
3150            widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size)
3151        else:
3152            if pos < 2:
3153                widget.SetViewport(0, 1 - 2 * size, size * 2, 1)
3154            elif pos == 2:
3155                widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1)
3156            elif pos == 3:
3157                widget.SetViewport(0, 0, size * 2, size * 2)
3158            elif pos == 4:
3159                widget.SetViewport(1 - 2 * size, 0, 1, size * 2)
3160        widget.EnabledOn()
3161        widget.SetInteractive(draggable)
3162        if at is not None and at < len(self.renderers):
3163            widget.SetCurrentRenderer(self.renderers[at])
3164        self.widgets.append(widget)
3165        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:

def clear(self, at=None, deep=False):
3167    def clear(self, at=None, deep=False):
3168        """Clear the scene from all meshes and volumes."""
3169        if at is not None:
3170            renderer = self.renderers[at]
3171        else:
3172            renderer = self.renderer
3173        if not renderer:
3174            return self
3175
3176        if deep:
3177            renderer.RemoveAllViewProps()
3178        else:
3179            for a in set(self.get_meshes() + self.get_volumes() + self.actors + self.axes_instances):
3180                if isinstance(a, vedo.shapes.Text2D):
3181                    continue
3182                self.remove(a)
3183                try:
3184                    if a.scalarbar:
3185                        self.remove(a.scalarbar)
3186                except AttributeError:
3187                    pass
3188            self.actors = []
3189        return self

Clear the scene from all meshes and volumes.

def break_interaction(self):
3191    def break_interaction(self):
3192        """Break window interaction and return to the python execution flow"""
3193        if self.interactor:
3194            self.interactor.ExitCallback()
3195        return self

Break window interaction and return to the python execution flow

def user_mode(self, mode):
3197    def user_mode(self, mode):
3198        """
3199        Modify the user interaction mode.
3200
3201        Examples:
3202            ```python
3203            from vedo import *
3204            mode = interactor_modes.MousePan()
3205            mesh = Mesh(dataurl+"cow.vtk")
3206            plt = Plotter().user_mode(mode)
3207            plt.show(mesh, axes=1)
3208           ```
3209        See also:
3210        [interactors](https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html)
3211        """
3212        if not self.interactor:
3213            return None
3214
3215        if isinstance(mode, (str, int)):
3216            # Set the style of interaction
3217            # see https://vtk.org/doc/nightly/html/classvtkInteractorStyle.html
3218            if mode in (0, "TrackballCamera"):
3219                if self.qt_widget:
3220                    self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
3221            elif mode in (1, "TrackballActor"):
3222                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleTrackballActor())
3223            elif mode in (2, "JoystickCamera"):
3224                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleJoystickCamera())
3225            elif mode in (3, "JoystickActor"):
3226                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleJoystickActor())
3227            elif mode in (4, "Flight"):
3228                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleFlight())
3229            elif mode in (5, "RubberBand2D"):
3230                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleRubberBand2D())
3231            elif mode in (6, "RubberBand3D"):
3232                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleRubberBand3D())
3233            elif mode in (7, "RubberBandZoom"):
3234                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleRubberBandZoom())
3235            elif mode in (8, "Terrain"):
3236                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleTerrain())
3237            elif mode in (9, "Unicam"):
3238                self.interactor.SetInteractorStyle(vtk.vtkInteractorStyleUnicam())
3239            elif mode in (10, "Image", "image", "2d"):
3240                astyle = vtk.vtkInteractorStyleImage()
3241                astyle.SetInteractionModeToImage3D()
3242                self.interactor.SetInteractorStyle(astyle)
3243            else:
3244                vedo.logger.warning(f"Unknown interaction mode: {mode}")
3245
3246        elif isinstance(mode, vtk.vtkInteractorStyleUser):
3247            # set a custom interactor style
3248            mode.interactor = self.interactor
3249            mode.renderer = self.renderer
3250            mode.SetInteractor(self.interactor)
3251            mode.SetDefaultRenderer(self.renderer)
3252            self.interactor.SetInteractorStyle(mode)
3253
3254        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: interactors

def close_window(self):
3256    def close_window(self):
3257        """Close the current or the input rendering window.
3258
3259        Examples:
3260            - [closewindow.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/closewindow.py)
3261        """
3262        vedo.last_figure = None
3263        self.sliders = []
3264        self.buttons = []
3265        self.widgets = []
3266        self.hover_legends = []
3267        self.background_renderer = None
3268        self._extralight = None
3269
3270        self.hint_widget = None
3271        self.cutter_widget = None
3272
3273        for r in self.renderers:
3274            r.RemoveAllObservers()
3275        if hasattr(self, "window") and self.window:
3276            if hasattr(self, "interactor") and self.interactor:
3277                self.interactor.ExitCallback()
3278                try:
3279                    self.interactor.SetDone(True)
3280                except AttributeError:
3281                    pass
3282                self.interactor.TerminateApp()
3283
3284                # self.interactor = None
3285            self.window.Finalize()  # this must be done here
3286
3287            if hasattr(self, "interactor") and self.interactor:
3288                if "Darwin" in vedo.sys_platform:
3289                    try:
3290                        self.interactor.ProcessEvents()
3291                    except:
3292                        pass
3293                self.interactor = None
3294
3295            self.window = None
3296
3297        self.renderer = None  # current renderer
3298        self.renderers = []
3299        self.camera = None
3300        self.skybox = None
3301        return self

Close the current or the input rendering window.

Examples:
def close(self):
3303    def close(self):
3304        """Close the Plotter instance and release resources."""
3305        self.close_window()
3306        self.actors = []
3307        if vedo.plotter_instance == self:
3308            vedo.plotter_instance = None

Close the Plotter instance and release resources.

def screenshot(self, filename='screenshot.png', scale=1, asarray=False):
3310    def screenshot(self, filename="screenshot.png", scale=1, asarray=False):
3311        """
3312        Take a screenshot of the Plotter window.
3313
3314        Arguments:
3315            scale : (int)
3316                set image magnification as an integer multiplicating factor
3317            asarray : (bool)
3318                return a numpy array of the image instead of writing a file
3319        """
3320        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
def topicture(self, scale=1):
3322    def topicture(self, scale=1):
3323        """
3324        Generate a Picture object from the current rendering window.
3325
3326        Arguments:
3327            scale : (int)
3328                set image magnification as an integer multiplicating factor
3329        """
3330        if settings.screeshot_large_image:
3331            w2if = vtk.vtkRenderLargeImage()
3332            w2if.SetInput(self.renderer)
3333            w2if.SetMagnification(scale)
3334        else:
3335            w2if = vtk.vtkWindowToImageFilter()
3336            w2if.SetInput(self.window)
3337            if hasattr(w2if, "SetScale"):
3338                w2if.SetScale(scale, scale)
3339            if settings.screenshot_transparent_background:
3340                w2if.SetInputBufferTypeToRGBA()
3341            w2if.ReadFrontBufferOff()  # read from the back buffer
3342        w2if.Update()
3343        return vedo.picture.Picture(w2if.GetOutput())

Generate a Picture object from the current rendering window.

Arguments:
  • scale : (int) set image magnification as an integer multiplicating factor
def export(self, filename='scene.npz', binary=False):
3345    def export(self, filename="scene.npz", binary=False):
3346        """
3347        Export scene to file to HTML, X3D or Numpy file.
3348
3349        Examples:
3350            - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py)
3351            - [export_numpy.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_numpy.py)
3352        """
3353        vedo.file_io.export_window(filename, binary=binary)
3354        return self

Export scene to file to HTML, X3D or Numpy file.

Examples:
def color_picker(self, xy, verbose=False):
3356    def color_picker(self, xy, verbose=False):
3357        """Pick color of specific (x,y) pixel on the screen."""
3358        w2if = vtk.vtkWindowToImageFilter()
3359        w2if.SetInput(self.window)
3360        w2if.ReadFrontBufferOff()
3361        w2if.Update()
3362        nx, ny = self.window.GetSize()
3363        varr = w2if.GetOutput().GetPointData().GetScalars()
3364
3365        arr = utils.vtk2numpy(varr).reshape(ny, nx, 3)
3366        x, y = int(xy[0]), int(xy[1])
3367        if y < ny and x < nx:
3368
3369            rgb = arr[y, x]
3370
3371            if verbose:
3372                vedo.printc(":rainbow:Pixel", [x, y], "has RGB[", end="")
3373                vedo.printc("█", c=[rgb[0], 0, 0], end="")
3374                vedo.printc("█", c=[0, rgb[1], 0], end="")
3375                vedo.printc("█", c=[0, 0, rgb[2]], end="")
3376                vedo.printc("] = ", end="")
3377                cnm = vedo.get_color_name(rgb)
3378                if np.sum(rgb) < 150:
3379                    vedo.printc(
3380                        rgb.tolist(),
3381                        vedo.colors.rgb2hex(np.array(rgb) / 255),
3382                        c="w",
3383                        bc=rgb,
3384                        invert=1,
3385                        end="",
3386                    )
3387                    vedo.printc("  -> " + cnm, invert=1, c="w")
3388                else:
3389                    vedo.printc(
3390                        rgb.tolist(), vedo.colors.rgb2hex(np.array(rgb) / 255), c=rgb, end=""
3391                    )
3392                    vedo.printc("  -> " + cnm, c=cnm)
3393
3394            return rgb
3395
3396        return None

Pick color of specific (x,y) pixel on the screen.

def show( *actors, at=None, shape=(1, 1), N=None, pos=(0, 0), size='auto', screensize='auto', title='vedo', bg='white', bg2=None, axes=None, interactive=None, offscreen=False, sharecam=True, resetcam=True, zoom=None, viewup='', azimuth=0.0, elevation=0.0, roll=0.0, camera=None, mode=0, new=False):
 92def show(
 93    *actors,
 94    at=None,
 95    shape=(1, 1),
 96    N=None,
 97    pos=(0, 0),
 98    size="auto",
 99    screensize="auto",
100    title="vedo",
101    bg="white",
102    bg2=None,
103    axes=None,
104    interactive=None,
105    offscreen=False,
106    sharecam=True,
107    resetcam=True,
108    zoom=None,
109    viewup="",
110    azimuth=0.0,
111    elevation=0.0,
112    roll=0.0,
113    camera=None,
114    mode=0,
115    new=False,
116):
117    """
118    Create on the fly an instance of class Plotter and show the object(s) provided.
119
120    Allowed input objects types are:
121        ``str, Mesh, Volume, Picture, Assembly
122        vtkPolyData, vtkActor, vtkActor2D, vtkImageActor,
123        vtkAssembly or vtkVolume``
124
125    Arguments:
126        at : (int)
127            number of the renderer to plot to, in case of more than one exists
128        shape : (list, str)
129            Number of sub-render windows inside of the main window. E.g.:
130            specify two across with shape=(2,1) and a two by two grid
131            with shape=(2, 2).  By default there is only one renderer.
132
133            Can also accept a shape as string descriptor. E.g.:
134            - shape="3|1" means 3 plots on the left and 1 on the right,
135            - shape="4/2" means 4 plots on top of 2 at bottom.
136
137        axes : (int)
138            set the type of axes to be shown:
139            - 0,  no axes
140            - 1,  draw three gray grid walls
141            - 2,  show cartesian axes from (0,0,0)
142            - 3,  show positive range of cartesian axes from (0,0,0)
143            - 4,  show a triad at bottom left
144            - 5,  show a cube at bottom left
145            - 6,  mark the corners of the bounding box
146            - 7,  draw a 3D ruler at each side of the cartesian axes
147            - 8,  show the `vtkCubeAxesActor` object
148            - 9,  show the bounding box outLine
149            - 10, show three circles representing the maximum bounding box
150            - 11, show a large grid on the x-y plane
151            - 12, show polar axes
152            - 13, draw a simple ruler at the bottom of the window
153
154            Axis type-1 can be fully customized by passing a dictionary.
155            Check `vedo.addons.Axes()` for the full list of options.
156        azimuth/elevation/roll : (float)
157            move camera accordingly the specified value
158        viewup : (str, list)
159            either `['x', 'y', 'z']` or a vector to set vertical direction
160        resetcam : (bool)
161            re-adjust camera position to fit objects
162        camera : (dict, vtkCamera)
163            camera parameters can further be specified with a dictionary
164            assigned to the `camera` keyword (E.g. `show(camera={'pos':(1,2,3), 'thickness':1000,})`):
165            - **pos** (list),  the position of the camera in world coordinates
166            - **focal_point** (list), the focal point of the camera in world coordinates
167            - **viewup** (list), the view up direction for the camera
168            - **distance** (float), set the focal point to the specified distance from the camera position.
169            - **clipping_range** (float), distance of the near and far clipping planes along the direction of projection.
170            - **parallel_scale** (float),
171            scaling used for a parallel projection, i.e. the height of the viewport
172            in world-coordinate distances. The default is 1. Note that the "scale" parameter works as
173            an "inverse scale", larger numbers produce smaller images.
174            This method has no effect in perspective projection mode.
175            - **thickness** (float),
176            set the distance between clipping planes. This method adjusts the far clipping
177            plane to be set a distance 'thickness' beyond the near clipping plane.
178            - **view_angle** (float),
179            the camera view angle, which is the angular height of the camera view
180            measured in degrees. The default angle is 30 degrees.
181            This method has no effect in parallel projection mode.
182            The formula for setting the angle up for perfect perspective viewing is:
183            angle = 2*atan((h/2)/d) where h is the height of the RenderWindow
184            (measured by holding a ruler up to your screen) and d is the distance
185            from your eyes to the screen.
186        interactive : (bool)
187            pause and interact with window (True) or continue execution (False)
188        rate : (float)
189            maximum rate of `show()` in Hertz
190        mode : (int, str)
191            set the type of interaction:
192            - 0 = TrackballCamera [default]
193            - 1 = TrackballActor
194            - 2 = JoystickCamera
195            - 3 = JoystickActor
196            - 4 = Flight
197            - 5 = RubberBand2D
198            - 6 = RubberBand3D
199            - 7 = RubberBandZoom
200            - 8 = Terrain
201            - 9 = Unicam
202            - 10 = Image
203        new : (bool)
204            if set to `True`, a call to show will instantiate
205            a new Plotter object (a new window) instead of reusing the first created.
206    """
207    if len(actors) == 0:
208        actors = None
209    elif len(actors) == 1:
210        actors = actors[0]
211    else:
212        actors = utils.flatten(actors)
213
214    if vedo.plotter_instance and not new:  # Plotter exists
215        plt = vedo.plotter_instance
216
217    else:  # Plotter must be created
218
219        if utils.is_sequence(at):  # user passed a sequence for "at"
220
221            if not utils.is_sequence(actors):
222                vedo.logger.error("in show() input must be a list.")
223                raise RuntimeError()
224            if len(at) != len(actors):
225                vedo.logger.error("in show() lists 'input' and 'at' must have equal lengths")
226                raise RuntimeError()
227            if shape == (1, 1) and N is None:
228                N = max(at) + 1
229
230        elif at is None and (N or shape != (1, 1)):
231
232            if not utils.is_sequence(actors):
233                e = "in show(), N or shape is set, but input is not a sequence\n"
234                e += "              you may need to specify e.g. at=0"
235                vedo.logger.error(e)
236                raise RuntimeError()
237            at = list(range(len(actors)))
238
239        plt = Plotter(
240            shape=shape,
241            N=N,
242            pos=pos,
243            size=size,
244            screensize=screensize,
245            title=title,
246            axes=axes,
247            sharecam=sharecam,
248            resetcam=resetcam,
249            interactive=interactive,
250            offscreen=offscreen,
251            bg=bg,
252            bg2=bg2,
253        )
254
255    # use _plt_to_return because plt.show() can return a k3d plot
256    _plt_to_return = None
257
258    if utils.is_sequence(at):
259
260        for i, act in enumerate(actors):
261            _plt_to_return = plt.show(
262                act,
263                at=i,
264                zoom=zoom,
265                resetcam=resetcam,
266                viewup=viewup,
267                azimuth=azimuth,
268                elevation=elevation,
269                roll=roll,
270                camera=camera,
271                interactive=False,
272                mode=mode,
273                bg=bg,
274                bg2=bg2,
275                axes=axes,
276            )
277
278        if (
279            interactive
280            or len(at) == N
281            or (isinstance(shape[0], int) and len(at) == shape[0] * shape[1])
282        ):
283            # note that shape can be a string
284            if plt.interactor and not offscreen and (interactive is None or interactive):
285                plt.interactor.Start()
286
287    else:
288
289        _plt_to_return = plt.show(
290            actors,
291            at=at,
292            zoom=zoom,
293            resetcam=resetcam,
294            viewup=viewup,
295            azimuth=azimuth,
296            elevation=elevation,
297            roll=roll,
298            camera=camera,
299            interactive=interactive,
300            mode=mode,
301            bg=bg,
302            bg2=bg2,
303            axes=axes,
304        )
305
306    return _plt_to_return

Create on the fly an instance of class Plotter and show the object(s) provided.

Allowed input objects types are:

str, Mesh, Volume, Picture, Assembly vtkPolyData, vtkActor, vtkActor2D, vtkImageActor, vtkAssembly or vtkVolume

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.
  • 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

    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.
def close():
309def close():
310    """Close the last created Plotter instance if it exists."""
311    if not vedo.plotter_instance:
312        return
313    vedo.plotter_instance.close()
314    return

Close the last created Plotter instance if it exists.