vedo.addons

Create additional objects like axes, legends, lights, etc.

   1#!/usr/bin/env python3
   2# -*- coding: utf-8 -*-
   3import numpy as np
   4
   5try:
   6    import vedo.vtkclasses as vtk
   7except ImportError:
   8    import vtkmodules.all as vtk
   9
  10import vedo
  11from vedo import settings
  12from vedo import utils
  13from vedo import shapes
  14from vedo.assembly import Assembly, Group
  15from vedo.colors import get_color, build_lut, color_map, printc
  16from vedo.mesh import Mesh
  17from vedo.pointcloud import Points, Point, merge
  18from vedo.tetmesh import TetMesh
  19from vedo.volume import Volume
  20
  21__docformat__ = "google"
  22
  23__doc__ = """
  24Create additional objects like axes, legends, lights, etc.
  25
  26![](https://vedo.embl.es/images/pyplot/customAxes2.png)
  27"""
  28
  29__all__ = [
  30    "ScalarBar",
  31    "ScalarBar3D",
  32    "Slider2D",
  33    "Slider3D",
  34    "Icon",
  35    "LegendBox",
  36    "Light",
  37    "Axes",
  38    "RendererFrame",
  39    "Ruler",
  40    "RulerAxes",
  41    "Ruler2D",
  42    "DistanceTool",
  43    "SplineTool",
  44    "Goniometer",
  45    "Button",
  46    "Flagpost",
  47    "ProgressBarWidget",
  48    "BoxCutter",
  49    "PlaneCutter",
  50    "SphereCutter",
  51]
  52
  53########################################################################################
  54class Flagpost(vtk.vtkFlagpoleLabel):
  55    """
  56    Create a flag post style element to describe an object.
  57    """
  58
  59    def __init__(
  60        self,
  61        txt="",
  62        base=(0, 0, 0),
  63        top=(0, 0, 1),
  64        s=1,
  65        c="k9",
  66        bc="k1",
  67        alpha=1,
  68        lw=0,
  69        font="Calco",
  70        justify="center-left",
  71        vspacing=1,
  72    ):
  73        """
  74        Create a flag post style element to describe an object.
  75
  76        Arguments:
  77            txt : (str)
  78                Text to display. The default is the filename or the object name.
  79            base : (list)
  80                position of the flag anchor point.
  81            top : (list)
  82                a 3D displacement or offset.
  83            s : (float)
  84                size of the text to be shown
  85            c : (list)
  86                color of text and line
  87            bc : (list)
  88                color of the flag background
  89            alpha : (float)
  90                opacity of text and box.
  91            lw : (int)
  92                line with of box frame. The default is 0.
  93            font : (str)
  94                font name. Use a monospace font for better rendering. The default is "Calco".
  95                Type `vedo -r fonts` for a font demo.
  96                Check [available fonts here](https://vedo.embl.es/fonts).
  97            justify : (str)
  98                internal text justification. The default is "center-left".
  99            vspacing : (float)
 100                vertical spacing between lines.
 101
 102        Examples:
 103            - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/examples/other/flag_labels2.py)
 104
 105            ![](https://vedo.embl.es/images/other/flag_labels2.png)
 106        """
 107
 108        vtk.vtkFlagpoleLabel.__init__(self)
 109
 110        base = utils.make3d(base)
 111        top = utils.make3d(top)
 112
 113        self.SetBasePosition(*base)
 114        self.SetTopPosition(*top)
 115
 116        self.SetFlagSize(s)
 117        self.SetInput(txt)
 118        self.PickableOff()
 119
 120        self.GetProperty().LightingOff()
 121        self.GetProperty().SetLineWidth(lw + 1)
 122
 123        prop = self.GetTextProperty()
 124        if bc is not None:
 125            prop.SetBackgroundColor(get_color(bc))
 126
 127        prop.SetOpacity(alpha)
 128        prop.SetBackgroundOpacity(alpha)
 129        if bc is not None and len(bc) == 4:
 130            prop.SetBackgroundRGBA(alpha)
 131
 132        c = get_color(c)
 133        prop.SetColor(c)
 134        self.GetProperty().SetColor(c)
 135
 136        prop.SetFrame(bool(lw))
 137        prop.SetFrameWidth(lw)
 138        prop.SetFrameColor(prop.GetColor())
 139
 140        prop.SetFontFamily(vtk.VTK_FONT_FILE)
 141        fl = utils.get_font_path(font)
 142        prop.SetFontFile(fl)
 143        prop.ShadowOff()
 144        prop.BoldOff()
 145        prop.SetOpacity(alpha)
 146        prop.SetJustificationToLeft()
 147        if "top" in justify:
 148            prop.SetVerticalJustificationToTop()
 149        if "bottom" in justify:
 150            prop.SetVerticalJustificationToBottom()
 151        if "cent" in justify:
 152            prop.SetVerticalJustificationToCentered()
 153            prop.SetJustificationToCentered()
 154        if "left" in justify:
 155            prop.SetJustificationToLeft()
 156        if "right" in justify:
 157            prop.SetJustificationToRight()
 158        prop.SetLineSpacing(vspacing * 1.2)
 159        self.SetUseBounds(False)
 160
 161    def text(self, value):
 162        self.SetInput(value)
 163        return self
 164
 165    def on(self):
 166        self.VisibilityOn()
 167        return self
 168
 169    def off(self):
 170        self.VisibilityOff()
 171        return self
 172
 173    def toggle(self):
 174        self.SetVisibility(not self.GetVisibility())
 175        return self
 176
 177    def use_bounds(self, value=True):
 178        self.SetUseBounds(value)
 179        return self
 180
 181    def color(self, c):
 182        c = get_color(c)
 183        self.GetTextProperty().SetColor(c)
 184        self.GetProperty().SetColor(c)
 185        return self
 186
 187    def pos(self, p):
 188        p = np.asarray(p)
 189        self.top = self.top - self.base + p
 190        self.base = p
 191        return self
 192
 193    @property
 194    def base(self):
 195        return np.array(self.GetBasePosition())
 196
 197    @property
 198    def top(self):
 199        return np.array(self.GetTopPosition())
 200
 201    @base.setter
 202    def base(self, value):
 203        self.SetBasePosition(*value)
 204
 205    @top.setter
 206    def top(self, value):
 207        self.SetTopPosition(*value)
 208
 209
 210
 211###########################################################################################
 212class LegendBox(shapes.TextBase, vtk.vtkLegendBoxActor):
 213    """
 214    Create a 2D legend box.
 215    """
 216    def __init__(
 217        self,
 218        entries=(),
 219        nmax=12,
 220        c=None,
 221        font="",
 222        width=0.18,
 223        height=None,
 224        padding=2,
 225        bg="k8",
 226        alpha=0.25,
 227        pos="top-right",
 228        markers=None,
 229    ):
 230        """
 231        Create a 2D legend box for the list of specified objects.
 232
 233        Arguments:
 234            nmax : (int)
 235                max number of legend entries
 236            c : (color)
 237                text color, leave as None to pick the mesh color automatically
 238            font : (str)
 239                Check [available fonts here](https://vedo.embl.es/fonts)
 240            width : (float)
 241                width of the box as fraction of the window width
 242            height : (float)
 243                height of the box as fraction of the window height
 244            padding : (int)
 245                padding space in units of pixels
 246            bg : (color)
 247                background color of the box
 248            alpha: (float)
 249                opacity of the box
 250            pos : (str, list)
 251                position of the box, can be either a string or a (x,y) screen position in range [0,1]
 252
 253        Examples:
 254            - [legendbox.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/legendbox.py)
 255            - [flag_labels1.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels1.py)
 256            - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels2.py)
 257
 258                ![](https://vedo.embl.es/images/other/flag_labels.png)
 259        """
 260        vtk.vtkLegendBoxActor.__init__(self)
 261        shapes.TextBase.__init__(self)
 262
 263        self.name = "LegendBox"
 264        self.entries = entries[:nmax]
 265        self.property = self.GetEntryTextProperty()
 266
 267        n = 0
 268        texts = []
 269        for e in self.entries:
 270            ename = e.name
 271            if "legend" in e.info.keys():
 272                if not e.info["legend"]:
 273                    ename = ""
 274                else:
 275                    ename = str(e.info["legend"])
 276
 277            if not isinstance(e, vtk.vtkActor):
 278                ename = ""
 279            if ename:
 280                n += 1
 281            texts.append(ename)
 282        self.SetNumberOfEntries(n)
 283
 284        if not n:
 285            return
 286
 287        self.ScalarVisibilityOff()
 288        self.PickableOff()
 289        self.SetPadding(padding)
 290
 291        self.property.ShadowOff()
 292        self.property.BoldOff()
 293
 294        # self.property.SetJustificationToLeft() # no effect
 295        # self.property.SetVerticalJustificationToTop()
 296
 297        if not font:
 298            font = settings.default_font
 299
 300        self.font(font)
 301
 302        n = 0
 303        for i in range(len(self.entries)):
 304            ti = texts[i]
 305            if not ti:
 306                continue
 307            e = entries[i]
 308            if c is None:
 309                col = e.GetProperty().GetColor()
 310                if col == (1, 1, 1):
 311                    col = (0.2, 0.2, 0.2)
 312            else:
 313                col = get_color(c)
 314            if markers is None:  # default
 315                poly = e.inputdata()
 316            else:
 317                marker = markers[i] if utils.is_sequence(markers) else markers
 318                if isinstance(marker, vedo.Points):
 319                    poly = marker.clone(deep=False).normalize().shift(0, 1, 0).polydata()
 320                else:  # assume string marker
 321                    poly = vedo.shapes.Marker(marker, s=1).shift(0, 1, 0).polydata()
 322
 323            self.SetEntry(n, poly, ti, col)
 324            n += 1
 325
 326        self.SetWidth(width)
 327        if height is None:
 328            self.SetHeight(width / 3.0 * n)
 329        else:
 330            self.SetHeight(height)
 331
 332        sx, sy = 1 - self.GetWidth(), 1 - self.GetHeight()
 333        if pos == 1 or ("top" in pos and "left" in pos):
 334            self.GetPositionCoordinate().SetValue(0, sy)
 335        elif pos == 2 or ("top" in pos and "right" in pos):
 336            self.GetPositionCoordinate().SetValue(sx, sy)
 337        elif pos == 3 or ("bottom" in pos and "left" in pos):
 338            self.GetPositionCoordinate().SetValue(0, 0)
 339        elif pos == 4 or ("bottom" in pos and "right" in pos):
 340            self.GetPositionCoordinate().SetValue(sx, 0)
 341        if alpha:
 342            self.UseBackgroundOn()
 343            self.SetBackgroundColor(get_color(bg))
 344            self.SetBackgroundOpacity(alpha)
 345        else:
 346            self.UseBackgroundOff()
 347        self.LockBorderOn()
 348
 349
 350class Button(vtk.vtkTextActor):
 351    """
 352    Build a Button object.
 353    """
 354    def __init__(
 355            self, 
 356            fnc=None, 
 357            states=("Button"), 
 358            c=("white"), 
 359            bc=("green4"),
 360            pos=(0.7, 0.05), 
 361            size=24, 
 362            font=None, 
 363            bold=False, 
 364            italic=False, 
 365            alpha=1, 
 366            angle=0,
 367            name="Button",
 368        ):
 369        """
 370        Build a Button object to be shown in the rendering window.
 371
 372        Arguments:
 373            fnc : (function)
 374                external function to be called by the widget
 375            states : (list)
 376                the list of possible states, eg. ['On', 'Off']
 377            c : (list)
 378                the list of colors for each state eg. ['red3', 'green5']
 379            bc : (list)
 380                the list of background colors for each state
 381            pos : (list, str)
 382                2D position in pixels from left-bottom corner
 383            size : (int)
 384                size of button font
 385            font : (str)
 386                font type
 387            bold : (bool)
 388                set bold font face
 389            italic : (bool)
 390                italic font face
 391            alpha : (float)
 392                opacity level
 393            angle : (float)
 394                anticlockwise rotation in degrees
 395            name : (str)
 396                name of the button (useful for multiple buttons in callbacks)
 397    
 398        Examples:
 399            - [buttons.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/buttons.py)
 400
 401        Examples:
 402            - [buttons.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/buttons.py)
 403
 404                ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg)
 405
 406            - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py)
 407
 408                ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg)
 409        """
 410        vtk.vtkTextActor.__init__(self)
 411
 412        self.status_idx = 0
 413        self.states = states
 414
 415        if not utils.is_sequence(c):
 416            c = [c]
 417        self.colors = c
 418
 419        if not utils.is_sequence(bc):
 420            bc = [bc]
 421        self.bcolors = bc
 422
 423        assert len(c) == len(bc), "in Button color number mismatch!"
 424
 425        self.function = fnc
 426        self.function_id = None
 427        self.name = name
 428
 429        self.GetActualPositionCoordinate().SetCoordinateSystemToNormalizedViewport()
 430        self.SetPosition(pos[0], pos[1])
 431
 432        self.offset = 5
 433        self.spacer = " "
 434
 435        self.len_states = max([len(s) for s in states])
 436
 437        self.text_property = self.GetTextProperty()
 438        self.text_property.SetJustificationToCentered()
 439
 440        if not font:
 441            font = settings.default_font
 442
 443        if font.lower() == "courier":
 444            self.text_property.SetFontFamilyToCourier()
 445        elif font.lower() == "times":
 446            self.text_property.SetFontFamilyToTimes()
 447        elif font.lower() == "arial":
 448            self.text_property.SetFontFamilyToArial()
 449        else:
 450            self.text_property.SetFontFamily(vtk.VTK_FONT_FILE)
 451            self.text_property.SetFontFile(utils.get_font_path(font))
 452        self.text_property.SetFontSize(size)
 453
 454        self.text_property.SetBackgroundOpacity(alpha)
 455
 456        self.text_property.BoldOff()
 457        if bold:
 458            self.text_property.BoldOn()
 459
 460        self.text_property.ItalicOff()
 461        if italic:
 462            self.text_property.ItalicOn()
 463
 464        self.text_property.ShadowOff()
 465        self.text_property.SetOrientation(angle)
 466        self.text_property.SetLineOffset(self.offset)
 467
 468        self.hasframe = hasattr(self.text_property, "FrameOn")
 469
 470        self.status(0)
 471
 472    def text(self, txt="", c=None):
 473        if txt:
 474            self.SetInput(self.spacer + str(txt) + self.spacer)
 475        else:
 476            return self.GetInput()
 477
 478        if c is not None:
 479            self.text_property.SetColor(get_color(c))
 480        return self
 481
 482    def backcolor(self, c):
 483        self.text_property.SetBackgroundColor(get_color(c))
 484        return self
 485
 486    def frame(self, lw=None, c=None):
 487        if self.hasframe:
 488            self.text_property.FrameOn()
 489            if lw is not None:
 490                if lw > 0:
 491                    self.text_property.FrameOn()
 492                    self.text_property.SetFrameWidth(lw)
 493                else:
 494                    self.text_property.FrameOff()
 495                    return self
 496            if c is not None:
 497                self.text_property.SetFrameColor(get_color(c))
 498        return self
 499
 500    def status(self, s=None):
 501        """
 502        Set/Get the status of the button.
 503        """
 504        if s is None:
 505            return self.states[self.status_idx]
 506
 507        if isinstance(s, str):
 508            s = self.states.index(s)
 509        self.status_idx = s
 510        self.text(self.states[s])
 511        s = s % len(self.bcolors)
 512        self.text(c=self.colors[s])
 513        self.backcolor(self.bcolors[s])
 514        return self
 515
 516    def switch(self):
 517        """
 518        Change/cycle button status to the next defined status in states list.
 519        """
 520        self.status_idx = (self.status_idx + 1) % len(self.states)
 521        self.status(self.status_idx)
 522        return self
 523
 524
 525#####################################################################
 526class SplineTool(vtk.vtkContourWidget):
 527    """
 528    Spline tool, draw a spline through a set of points interactively.
 529    """
 530
 531    def __init__(self, points, pc="k", ps=8, lc="r4", ac="g5", lw=2, closed=False, ontop=True):
 532        """
 533        Spline tool, draw a spline through a set of points interactively.
 534
 535        Arguments:
 536            points : (list), Points
 537                initial set of points.
 538            pc : (str)
 539                point color.
 540            ps : (int)
 541                point size.
 542            lc : (str)
 543                line color.
 544            ac : (str)
 545                active point color.
 546            lw : (int)
 547                line width.
 548            closed : (bool)
 549                spline is closed or open.
 550            ontop : (bool)
 551                show it always on top of other objects.
 552
 553        Examples:
 554            - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py)
 555
 556                ![](https://vedo.embl.es/images/basic/spline_tool.png)
 557        """
 558        vtk.vtkContourWidget.__init__(self)
 559
 560        self.representation = vtk.vtkOrientedGlyphContourRepresentation()
 561        self.representation.SetAlwaysOnTop(ontop)
 562
 563        self.representation.GetLinesProperty().SetColor(get_color(lc))
 564        self.representation.GetLinesProperty().SetLineWidth(lw)
 565
 566        self.representation.GetProperty().SetColor(get_color(pc))
 567        self.representation.GetProperty().SetPointSize(ps)
 568        self.representation.GetProperty().RenderPointsAsSpheresOn()
 569
 570        self.representation.GetActiveProperty().SetColor(get_color(ac))
 571        self.representation.GetActiveProperty().SetLineWidth(lw + 1)
 572
 573        self.SetRepresentation(self.representation)
 574
 575        if utils.is_sequence(points):
 576            self.points = Points(points)
 577        else:
 578            self.points = points
 579
 580        self.closed = closed
 581
 582    def add(self, pt):
 583        """
 584        Add one point at a specified position in space if 3D,
 585        or 2D screen-display position if 2D.
 586        """
 587        if len(pt) == 2:
 588            self.representation.AddNodeAtDisplayPosition(int(pt[0]), int(pt[1]))
 589        else:
 590            self.representation.AddNodeAtWorldPosition(pt)
 591        return self
 592
 593    def remove(self, i):
 594        """Remove specific node by its index"""
 595        self.representation.DeleteNthNode(i)
 596        return self
 597
 598    def on(self):
 599        """Activate/Enable the tool"""
 600        self.On()
 601        self.Render()
 602        return self
 603
 604    def off(self):
 605        """Disactivate/Disable the tool"""
 606        self.Off()
 607        self.Render()
 608        return self
 609
 610    def render(self):
 611        """Render the spline"""
 612        self.Render()
 613        return self
 614
 615    def bounds(self):
 616        """Retrieve the bounding box of the spline as [x0,x1, y0,y1, z0,z1]"""
 617        return self.GetBounds()
 618
 619    def spline(self):
 620        """Return the vedo.Spline object."""
 621        self.representation.SetClosedLoop(self.closed)
 622        self.representation.BuildRepresentation()
 623        pd = self.representation.GetContourRepresentationAsPolyData()
 624        pts = utils.vtk2numpy(pd.GetPoints().GetData())
 625        ln = vedo.Line(pts, lw=2, c="k")
 626        return ln
 627
 628    def nodes(self, onscreen=False):
 629        """Return the current position in space (or on 2D screen-display) of the spline nodes."""
 630        n = self.representation.GetNumberOfNodes()
 631        pts = []
 632        for i in range(n):
 633            p = [0.0, 0.0, 0.0]
 634            if onscreen:
 635                self.representation.GetNthNodeDisplayPosition(i, p)
 636            else:
 637                self.representation.GetNthNodeWorldPosition(i, p)
 638            pts.append(p)
 639        return np.array(pts)
 640
 641
 642#####################################################################
 643class SliderWidget(vtk.vtkSliderWidget):
 644    """Helper class for vtkSliderWidget"""
 645
 646    def __init__(self):
 647        vtk.vtkSliderWidget.__init__(self)
 648
 649    @property
 650    def interactor(self):
 651        return self.GetInteractor()
 652
 653    @interactor.setter
 654    def interactor(self, iren):
 655        self.SetInteractor(iren)
 656
 657    @property
 658    def representation(self):
 659        return self.GetRepresentation()
 660
 661    @property
 662    def value(self):
 663        return self.GetRepresentation().GetValue()
 664
 665    @value.setter
 666    def value(self, val):
 667        self.GetRepresentation().SetValue(val)
 668
 669    @property
 670    def renderer(self):
 671        return self.GetCurrentRenderer()
 672
 673    @renderer.setter
 674    def renderer(self, ren):
 675        self.SetCurrentRenderer(ren)
 676
 677    @property
 678    def title(self):
 679        self.GetRepresentation().GetTitleText()
 680
 681    @title.setter
 682    def title(self, txt):
 683        self.GetRepresentation().SetTitleText(str(txt))
 684
 685    @property
 686    def range(self):
 687        xmin = self.GetRepresentation().GetMinimumValue()
 688        xmax = self.GetRepresentation().GetMaximumValue()
 689        return [xmin, xmax]
 690
 691    @range.setter
 692    def range(self, vals):
 693        if vals[0] is not None:
 694            self.GetRepresentation().SetMinimumValue(vals[0])
 695        if vals[1] is not None:
 696            self.GetRepresentation().SetMaximumValue(vals[1])
 697
 698    def on(self):
 699        self.EnabledOn()
 700
 701    def off(self):
 702        self.EnabledOff()
 703
 704
 705#####################################################################
 706def Goniometer(
 707    p1,
 708    p2,
 709    p3,
 710    font="",
 711    arc_size=0.4,
 712    s=1,
 713    italic=0,
 714    rotation=0,
 715    prefix="",
 716    lc="k2",
 717    c="white",
 718    alpha=1,
 719    lw=2,
 720    precision=3,
 721):
 722    """
 723    Build a graphical goniometer to measure the angle formed by 3 points in space.
 724
 725    Arguments:
 726        p1 : (list)
 727            first point 3D coordinates.
 728        p2 : (list)
 729            the vertex point.
 730        p3 : (list)
 731            the last point defining the angle.
 732        font : (str)
 733            Font face. Check [available fonts here](https://vedo.embl.es/fonts).
 734        arc_size : (float)
 735            dimension of the arc wrt the smallest axis.
 736        s : (float)
 737            size of the text.
 738        italic : (float, bool)
 739            italic text.
 740        rotation : (float)
 741            rotation of text in degrees.
 742        prefix : (str)
 743            append this string to the numeric value of the angle.
 744        lc : (list)
 745            color of the goniometer lines.
 746        c : (str)
 747            color of the goniometer angle filling. Set alpha=0 to remove it.
 748        alpha : (float)
 749            transparency level.
 750        lw : (float)
 751            line width.
 752        precision : (int)
 753            number of significant digits.
 754
 755    Examples:
 756        - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py)
 757
 758            ![](https://vedo.embl.es/images/pyplot/goniometer.png)
 759    """
 760    if isinstance(p1, Points): p1 = p1.GetPosition()
 761    if isinstance(p2, Points): p2 = p2.GetPosition()
 762    if isinstance(p3, Points): p3 = p3.GetPosition()
 763    if len(p1)==2: p1=[p1[0], p1[1], 0.0]
 764    if len(p2)==2: p2=[p2[0], p2[1], 0.0]
 765    if len(p3)==2: p3=[p3[0], p3[1], 0.0]
 766    p1, p2, p3 = np.array(p1), np.array(p2), np.array(p3)
 767
 768    acts = []
 769    ln = shapes.Line([p1, p2, p3], lw=lw, c=lc)
 770    acts.append(ln)
 771
 772    va = utils.versor(p1 - p2)
 773    vb = utils.versor(p3 - p2)
 774    r = min(utils.mag(p3 - p2), utils.mag(p1 - p2)) * arc_size
 775    ptsarc = []
 776    res = 120
 777    imed = int(res / 2)
 778    for i in range(res + 1):
 779        vi = utils.versor(vb * i / res + va * (res - i) / res)
 780        if i == imed:
 781            vc = np.array(vi)
 782        ptsarc.append(p2 + vi * r)
 783    arc = shapes.Line(ptsarc).lw(lw).c(lc)
 784    acts.append(arc)
 785
 786    angle = np.arccos(np.dot(va, vb)) * 180 / np.pi
 787
 788    lb = shapes.Text3D(
 789        prefix + utils.precision(angle, precision) + "º",
 790        s=r / 12 * s,
 791        font=font,
 792        italic=italic,
 793        justify="center",
 794    )
 795    cr = np.cross(va, vb)
 796    lb.pos(p2 + vc * r / 1.75).orientation(cr * np.sign(cr[2]), rotation=rotation)
 797    lb.c(c).bc("tomato").lighting("off")
 798    acts.append(lb)
 799
 800    if alpha > 0:
 801        pts = [p2] + arc.points().tolist() + [p2]
 802        msh = Mesh([pts, [list(range(arc.npoints + 2))]], c=lc, alpha=alpha)
 803        msh.lighting("off")
 804        msh.triangulate()
 805        msh.shift(0, 0, -r / 10000)  # to resolve 2d conflicts..
 806        acts.append(msh)
 807
 808    asse = Assembly(acts)
 809    return asse
 810
 811
 812def Light(pos, focal_point=(0, 0, 0), angle=180, c=None, intensity=1):
 813    """
 814    Generate a source of light placed at `pos` and directed to `focal point`.
 815    Returns a `vtkLight` object.
 816
 817    Arguments:
 818        focal_point : (list)
 819            focal point, if a `vedo` object is passed then will grab its position.
 820        angle : (float)
 821            aperture angle of the light source, in degrees
 822        c : (color)
 823            set the light color
 824        intensity : (float)
 825            intensity value between 0 and 1.
 826
 827    Check also:
 828        `plotter.Plotter.remove_lights()`
 829
 830    Examples:
 831        - [lights.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/lights.py)
 832
 833            ![](https://vedo.embl.es/images/basic/lights.png)
 834    """
 835    if c is None:
 836        try:
 837            c = pos.color()
 838        except AttributeError:
 839            c = "white"
 840
 841    if isinstance(pos, vedo.Base3DProp):
 842        pos = pos.pos()
 843
 844    if isinstance(focal_point, vedo.Base3DProp):
 845        focal_point = focal_point.pos()
 846
 847    light = vtk.vtkLight()
 848    light.SetLightTypeToSceneLight()
 849    light.SetPosition(pos)
 850    light.SetConeAngle(angle)
 851    light.SetFocalPoint(focal_point)
 852    light.SetIntensity(intensity)
 853    light.SetColor(get_color(c))
 854    return light
 855
 856
 857#####################################################################
 858def ScalarBar(
 859    obj,
 860    title="",
 861    pos=(0.8, 0.05),
 862    title_yoffset=15,
 863    font_size=12,
 864    size=(None, None),
 865    nlabels=None,
 866    c="k",
 867    horizontal=False,
 868    use_alpha=True,
 869    label_format=":6.3g",
 870):
 871    """
 872    A 2D scalar bar for the specified obj.
 873
 874    Arguments:
 875        pos : (list)
 876            fractional x and y position in the 2D window
 877        size : (list)
 878            size of the scalarbar in pixel units (width, height)
 879        nlabels : (int)
 880            number of numeric labels to be shown
 881        use_alpha : (bool)
 882            retain transparency in scalarbar
 883        horizontal : (bool)
 884            show in horizontal layout
 885
 886    Examples:
 887        - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py)
 888
 889        ![](https://user-images.githubusercontent.com/32848391/62940174-4bdc7900-bdd3-11e9-9713-e4f3e2fdab63.png)
 890    """
 891
 892    if isinstance(obj, Points):
 893        vtkscalars = obj.inputdata().GetPointData().GetScalars()
 894        if vtkscalars is None:
 895            vtkscalars = obj.inputdata().GetCellData().GetScalars()
 896        if not vtkscalars:
 897            return None
 898        lut = vtkscalars.GetLookupTable()
 899        if not lut:
 900            lut = obj.mapper().GetLookupTable()
 901            if not lut:
 902                return None
 903
 904    elif isinstance(obj, (Volume, TetMesh)):
 905        lut = utils.ctf2lut(obj)
 906
 907    elif utils.is_sequence(obj) and len(obj) == 2:
 908        x = np.linspace(obj[0], obj[1], 256)
 909        data = []
 910        for i in range(256):
 911            rgb = color_map(i, c, 0, 256)
 912            data.append([x[i], rgb])
 913        lut = build_lut(data)
 914        
 915    elif not hasattr(obj, "mapper"):
 916        vedo.logger.error(f"in add_scalarbar(): input is invalid {type(obj)}. Skip.")
 917        return None
 918
 919    else:
 920        return None
 921
 922    c = get_color(c)
 923    sb = vtk.vtkScalarBarActor()
 924
 925    # print(sb.GetLabelFormat())
 926    label_format = label_format.replace(":", "%-#")
 927    sb.SetLabelFormat(label_format)
 928
 929    sb.SetLookupTable(lut)
 930    sb.SetUseOpacity(use_alpha)
 931    sb.SetDrawFrame(0)
 932    sb.SetDrawBackground(0)
 933    if lut.GetUseBelowRangeColor():
 934        sb.DrawBelowRangeSwatchOn()
 935        sb.SetBelowRangeAnnotation("")
 936    if lut.GetUseAboveRangeColor():
 937        sb.DrawAboveRangeSwatchOn()
 938        sb.SetAboveRangeAnnotation("")
 939    if lut.GetNanColor() != (0.5, 0.0, 0.0, 1.0):
 940        sb.DrawNanAnnotationOn()
 941        sb.SetNanAnnotation("nan")
 942
 943    if title:
 944        if "\\" in repr(title):
 945            for r in shapes._reps:
 946                title = title.replace(r[0], r[1])
 947        titprop = sb.GetTitleTextProperty()
 948        titprop.BoldOn()
 949        titprop.ItalicOff()
 950        titprop.ShadowOff()
 951        titprop.SetColor(c)
 952        titprop.SetVerticalJustificationToTop()
 953        titprop.SetFontSize(font_size)
 954        titprop.SetFontFamily(vtk.VTK_FONT_FILE)
 955        titprop.SetFontFile(utils.get_font_path(settings.default_font))
 956        sb.SetTitle(title)
 957        sb.SetVerticalTitleSeparation(title_yoffset)
 958        sb.SetTitleTextProperty(titprop)
 959
 960    sb.UnconstrainedFontSizeOn()
 961    sb.DrawAnnotationsOn()
 962    sb.DrawTickLabelsOn()
 963    sb.SetMaximumNumberOfColors(256)
 964
 965    if horizontal:
 966        sb.SetOrientationToHorizontal()
 967        sb.SetNumberOfLabels(3)
 968        sb.SetTextPositionToSucceedScalarBar()
 969        sb.SetPosition(pos)
 970        sb.SetMaximumWidthInPixels(1000)
 971        sb.SetMaximumHeightInPixels(50)
 972    else:
 973        sb.SetNumberOfLabels(7)
 974        sb.SetTextPositionToPrecedeScalarBar()
 975        sb.SetPosition(pos[0] + 0.09, pos[1])
 976        sb.SetMaximumWidthInPixels(60)
 977        sb.SetMaximumHeightInPixels(250)
 978
 979    if size[0] is not None:
 980        sb.SetMaximumWidthInPixels(size[0])
 981    if size[1] is not None:
 982        sb.SetMaximumHeightInPixels(size[1])
 983
 984    if nlabels is not None:
 985        sb.SetNumberOfLabels(nlabels)
 986
 987    sctxt = sb.GetLabelTextProperty()
 988    sctxt.SetFontFamily(vtk.VTK_FONT_FILE)
 989    sctxt.SetFontFile(utils.get_font_path(settings.default_font))
 990    sctxt.SetColor(c)
 991    sctxt.SetShadow(0)
 992    sctxt.SetFontSize(font_size - 2)
 993    sb.SetAnnotationTextProperty(sctxt)
 994    sb.PickableOff()
 995    return sb
 996
 997
 998#####################################################################
 999def ScalarBar3D(
1000    obj,
1001    title="",
1002    pos=None,
1003    size=(None, None),
1004    title_font="",
1005    title_xoffset=-1.5,
1006    title_yoffset=0.0,
1007    title_size=1.5,
1008    title_rotation=0.0,
1009    nlabels=8,
1010    label_font="",
1011    label_size=1,
1012    label_offset=0.375,
1013    label_rotation=0,
1014    label_format="",
1015    italic=0,
1016    c=None,
1017    draw_box=True,
1018    above_text=None,
1019    below_text=None,
1020    nan_text="NaN",
1021    categories=None,
1022):
1023    """
1024    Create a 3D scalar bar for the specified object.
1025
1026    Input `obj` input can be:
1027
1028        - a list of numbers,
1029        - a list of two numbers in the form (min, max),
1030        - a Mesh already containing a set of scalars associated to vertices or cells,
1031        - if None the last object in the list of actors will be used.
1032
1033    Arguments:
1034        size : (list)
1035            (thickness, length) of scalarbar
1036        title : (str)
1037            scalar bar title
1038        title_xoffset : (float)
1039            horizontal space btw title and color scalarbar
1040        title_yoffset : (float)
1041            vertical space offset
1042        title_size : (float)
1043            size of title wrt numeric labels
1044        title_rotation : (float)
1045            title rotation in degrees
1046        nlabels : (int)
1047            number of numeric labels
1048        label_font : (str)
1049            font type for labels
1050        label_size : (float)
1051            label scale factor
1052        label_offset : (float)
1053            space btw numeric labels and scale
1054        label_rotation : (float)
1055            label rotation in degrees
1056        draw_box : (bool)
1057            draw a box around the colorbar
1058        categories : (list)
1059            make a categorical scalarbar,
1060            the input list will have the format [value, color, alpha, textlabel]
1061
1062    Examples:
1063        - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py)
1064    """
1065
1066    if isinstance(obj, Points):
1067        lut = obj.mapper().GetLookupTable()
1068        if not lut or lut.GetTable().GetNumberOfTuples() == 0:
1069            # create the most similar to the default
1070            obj.cmap("jet_r")
1071            lut = obj.mapper().GetLookupTable()
1072        vmin, vmax = lut.GetRange()
1073
1074    elif isinstance(obj, (Volume, TetMesh)):
1075        lut = utils.ctf2lut(obj)
1076        vmin, vmax = lut.GetRange()
1077
1078    elif utils.is_sequence(obj):
1079        vmin, vmax = np.min(obj), np.max(obj)
1080
1081    else:
1082        vedo.logger.error("in ScalarBar3D(): input must be a vedo object with bounds.")
1083        return obj
1084
1085    bns = obj.bounds()
1086    sx, sy = size
1087    if sy is None:
1088        sy = bns[3] - bns[2]
1089    if sx is None:
1090        sx = sy / 18
1091
1092    if categories is not None:  ################################
1093        ncats = len(categories)
1094        scale = shapes.Grid([-float(sx) * label_offset, 0, 0],
1095                            c=c, alpha=1, s=(sx, sy), res=(1, ncats))
1096        cols, alphas = [], []
1097        ticks_pos, ticks_txt = [0.0], [""]
1098        for i, cat in enumerate(categories):
1099            cl = get_color(cat[1])
1100            cols.append(cl)
1101            if len(cat) > 2:
1102                alphas.append(cat[2])
1103            else:
1104                alphas.append(1)
1105            if len(cat) > 3:
1106                ticks_txt.append(cat[3])
1107            else:
1108                ticks_txt.append("")
1109            ticks_pos.append((i + 0.5) / ncats)
1110        ticks_pos.append(1.0)
1111        ticks_txt.append("")
1112        rgba = np.c_[np.array(cols) * 255, np.array(alphas) * 255]
1113        scale.cell_individual_colors(rgba)
1114
1115    else:  ########################################################
1116
1117        # build the color scale part
1118        scale = shapes.Grid(
1119            [-float(sx) * label_offset, 0, 0],
1120            c=c,
1121            alpha=1,
1122            s=(sx, sy),
1123            res=(1, lut.GetTable().GetNumberOfTuples()),
1124        )
1125        cscals = np.linspace(vmin, vmax, lut.GetTable().GetNumberOfTuples())
1126
1127        if lut.GetScale():  # logarithmic scale
1128            lut10 = vtk.vtkLookupTable()
1129            lut10.DeepCopy(lut)
1130            lut10.SetScaleToLinear()
1131            scale.cmap(lut10, cscals, on="cells")
1132            tk = utils.make_ticks(vmin, vmax, nlabels, logscale=True, useformat=label_format)
1133        else:
1134            scale.cmap(lut, cscals, on="cells")
1135            tk = utils.make_ticks(vmin, vmax, nlabels, logscale=False, useformat=label_format)
1136        ticks_pos, ticks_txt = tk
1137    scale.lw(0).wireframe(False).lighting("off")
1138
1139    scales = [scale]
1140
1141    xbns = scale.xbounds()
1142    if pos is None:
1143        d = sx / 2
1144        if title:
1145            d = np.sqrt((bns[1] - bns[0]) ** 2 + sy * sy) / 20
1146        pos = (bns[1] - xbns[0] + d, (bns[2] + bns[3]) / 2, bns[4])
1147
1148    lsize = sy / 60 * label_size
1149
1150    tacts = []
1151    for i, p in enumerate(ticks_pos):
1152        tx = ticks_txt[i]
1153        if i and tx:
1154            # build numeric text
1155            y = (p - 0.5) * sy
1156            if label_rotation:
1157                a = shapes.Text3D(
1158                    tx,
1159                    pos=[sx * label_offset, y, 0],
1160                    s=lsize,
1161                    justify="center-top",
1162                    c=c,
1163                    italic=italic,
1164                    font=label_font,
1165                )
1166                a.RotateZ(label_rotation)
1167            else:
1168                a = shapes.Text3D(
1169                    tx,
1170                    pos=[sx * label_offset, y, 0],
1171                    s=lsize,
1172                    justify="center-left",
1173                    c=c,
1174                    italic=italic,
1175                    font=label_font,
1176                )
1177
1178            tacts.append(a)
1179
1180            # build ticks
1181            tic = shapes.Line([xbns[1], y, 0], [xbns[1] + sx * label_offset / 4, y, 0], lw=2, c=c)
1182            tacts.append(tic)
1183
1184    # build title
1185    if title:
1186        t = shapes.Text3D(
1187            title,
1188            (0, 0, 0),
1189            s=sy / 50 * title_size,
1190            c=c,
1191            justify="centered",
1192            italic=italic,
1193            font=title_font,
1194        )
1195        t.RotateZ(90 + title_rotation)
1196        t.pos(sx * title_xoffset, title_yoffset, 0)
1197        tacts.append(t)
1198
1199    # build below scale
1200    if lut.GetUseBelowRangeColor():
1201        r, g, b, alfa = lut.GetBelowRangeColor()
1202        sx = float(sx)
1203        sy = float(sy)
1204        brect = shapes.Rectangle(
1205            [-sx * label_offset - sx / 2, -sy / 2 - sx - sx * 0.1, 0],
1206            [-sx * label_offset + sx / 2, -sy / 2 - sx * 0.1, 0],
1207            c=(r, g, b),
1208            alpha=alfa,
1209        )
1210        brect.lw(1).lc(c).lighting("off")
1211        scales += [brect]
1212        if below_text is None:
1213            below_text = " <" + str(vmin)
1214        if below_text:
1215            if label_rotation:
1216                btx = shapes.Text3D(
1217                    below_text,
1218                    (0, 0, 0),
1219                    s=lsize,
1220                    c=c,
1221                    justify="center-top",
1222                    italic=italic,
1223                    font=label_font,
1224                )
1225                btx.RotateZ(label_rotation)
1226            else:
1227                btx = shapes.Text3D(
1228                    below_text,
1229                    (0, 0, 0),
1230                    s=lsize,
1231                    c=c,
1232                    justify="center-left",
1233                    italic=italic,
1234                    font=label_font,
1235                )
1236
1237            btx.pos(sx * label_offset, -sy / 2 - sx * 0.66, 0)
1238            tacts.append(btx)
1239
1240    # build above scale
1241    if lut.GetUseAboveRangeColor():
1242        r, g, b, alfa = lut.GetAboveRangeColor()
1243        arect = shapes.Rectangle(
1244            [-sx * label_offset - sx / 2, sy / 2 + sx * 0.1, 0],
1245            [-sx * label_offset + sx / 2, sy / 2 + sx + sx * 0.1, 0],
1246            c=(r, g, b),
1247            alpha=alfa,
1248        )
1249        arect.lw(1).lc(c).lighting("off")
1250        scales += [arect]
1251        if above_text is None:
1252            above_text = " >" + str(vmax)
1253        if above_text:
1254            if label_rotation:
1255                atx = shapes.Text3D(
1256                    above_text,
1257                    (0, 0, 0),
1258                    s=lsize,
1259                    c=c,
1260                    justify="center-top",
1261                    italic=italic,
1262                    font=label_font,
1263                )
1264                atx.RotateZ(label_rotation)
1265            else:
1266                atx = shapes.Text3D(
1267                    above_text,
1268                    (0, 0, 0),
1269                    s=lsize,
1270                    c=c,
1271                    justify="center-left",
1272                    italic=italic,
1273                    font=label_font,
1274                )
1275
1276            atx.pos(sx * label_offset, sy / 2 + sx * 0.66, 0)
1277            tacts.append(atx)
1278
1279    # build NaN scale
1280    if lut.GetNanColor() != (0.5, 0.0, 0.0, 1.0):
1281        nanshift = sx * 0.1
1282        if brect:
1283            nanshift += sx
1284        r, g, b, alfa = lut.GetNanColor()
1285        nanrect = shapes.Rectangle(
1286            [-sx * label_offset - sx / 2, -sy / 2 - sx - sx * 0.1 - nanshift, 0],
1287            [-sx * label_offset + sx / 2, -sy / 2 - sx * 0.1 - nanshift, 0],
1288            c=(r, g, b),
1289            alpha=alfa,
1290        )
1291        nanrect.lw(1).lc(c).lighting("off")
1292        scales += [nanrect]
1293        if label_rotation:
1294            nantx = shapes.Text3D(
1295                nan_text,
1296                (0, 0, 0),
1297                s=lsize,
1298                c=c,
1299                justify="center-left",
1300                italic=italic,
1301                font=label_font,
1302            )
1303            nantx.RotateZ(label_rotation)
1304        else:
1305            nantx = shapes.Text3D(
1306                nan_text,
1307                (0, 0, 0),
1308                s=lsize,
1309                c=c,
1310                justify="center-left",
1311                italic=italic,
1312                font=label_font,
1313            )
1314        nantx.pos(sx * label_offset, -sy / 2 - sx * 0.66 - nanshift, 0)
1315        tacts.append(nantx)
1316
1317    if draw_box:
1318        tacts.append(scale.box().lw(1))
1319
1320    for a in tacts:
1321        a.PickableOff()
1322
1323    mtacts = merge(tacts).lighting("off")
1324    mtacts.PickableOff()
1325    scale.PickableOff()
1326
1327    sact = Assembly(scales + tacts)
1328    sact.SetPosition(pos)
1329    sact.PickableOff()
1330    sact.UseBoundsOff()
1331    sact.name = "ScalarBar3D"
1332    return sact
1333
1334
1335#####################################################################
1336class Slider2D(SliderWidget):
1337    """
1338    Add a slider which can call an external custom function.
1339    """
1340    def __init__(
1341        self,
1342        sliderfunc,
1343        xmin,
1344        xmax,
1345        value=None,
1346        pos=4,
1347        title="",
1348        font="Calco",
1349        title_size=1,
1350        c="k",
1351        alpha=1,
1352        show_value=True,
1353        delayed=False,
1354        **options,
1355    ):
1356        """
1357        Add a slider which can call an external custom function.
1358        Set any value as float to increase the number of significant digits above the slider.
1359
1360        Use `play()` to start an animation between the current slider value and the last value.
1361
1362        Arguments:
1363            sliderfunc : (function)
1364                external function to be called by the widget
1365            xmin : (float)
1366                lower value of the slider
1367            xmax : (float)
1368                upper value
1369            value : (float)
1370                current value
1371            pos : (list, str)
1372                position corner number: horizontal [1-5] or vertical [11-15]
1373                it can also be specified by corners coordinates [(x1,y1), (x2,y2)]
1374                and also by a string descriptor (eg. "bottom-left")
1375            title : (str)
1376                title text
1377            font : (str)
1378                title font face. Check [available fonts here](https://vedo.embl.es/fonts).
1379            title_size : (float)
1380                title text scale [1.0]
1381            show_value : (bool)
1382                if True current value is shown
1383            delayed : (bool)
1384                if True the callback is delayed until when the mouse button is released
1385            alpha : (float)
1386                opacity of the scalar bar texts
1387            slider_length : (float)
1388                slider length
1389            slider_width : (float)
1390                slider width
1391            end_cap_length : (float)
1392                length of the end cap
1393            end_cap_width : (float)
1394                width of the end cap
1395            tube_width : (float)
1396                width of the tube
1397            title_height : (float)
1398                height of the title
1399            tformat : (str)
1400                format of the title
1401
1402        Examples:
1403            - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py)
1404            - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py)
1405
1406            ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg)
1407        """
1408        slider_length = options.pop("slider_length",  0.015)
1409        slider_width  = options.pop("slider_width",   0.025)
1410        end_cap_length= options.pop("end_cap_length", 0.0015)
1411        end_cap_width = options.pop("end_cap_width",  0.0125)
1412        tube_width    = options.pop("tube_width",     0.0075)
1413        title_height  = options.pop("title_height",   0.025)
1414        if options:
1415            vedo.logger.warning(f"in Slider2D unknown option(s): {options}")
1416
1417        c = get_color(c)
1418
1419        if value is None or value < xmin:
1420            value = xmin
1421
1422        slider_rep = vtk.vtkSliderRepresentation2D()
1423        slider_rep.SetMinimumValue(xmin)
1424        slider_rep.SetMaximumValue(xmax)
1425        slider_rep.SetValue(value)
1426        slider_rep.SetSliderLength(slider_length)
1427        slider_rep.SetSliderWidth(slider_width)
1428        slider_rep.SetEndCapLength(end_cap_length)
1429        slider_rep.SetEndCapWidth(end_cap_width)
1430        slider_rep.SetTubeWidth(tube_width)
1431        slider_rep.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay()
1432        slider_rep.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay()
1433
1434        if isinstance(pos, str):
1435            if "top" in pos:
1436                if "left" in pos:
1437                    if "vert" in pos:
1438                        pos = 11
1439                    else:
1440                        pos = 1
1441                elif "right" in pos:
1442                    if "vert" in pos:
1443                        pos = 12
1444                    else:
1445                        pos = 2
1446            elif "bott" in pos:
1447                if "left" in pos:
1448                    if "vert" in pos:
1449                        pos = 13
1450                    else:
1451                        pos = 3
1452                elif "right" in pos:
1453                    if "vert" in pos:
1454                        if "span" in pos:
1455                            pos = 15
1456                        else:
1457                            pos = 14
1458                    else:
1459                        pos = 4
1460                elif "span" in pos:
1461                    pos = 5
1462
1463        if utils.is_sequence(pos):
1464            slider_rep.GetPoint1Coordinate().SetValue(pos[0][0], pos[0][1])
1465            slider_rep.GetPoint2Coordinate().SetValue(pos[1][0], pos[1][1])
1466        elif pos == 1:  # top-left horizontal
1467            slider_rep.GetPoint1Coordinate().SetValue(0.04, 0.93)
1468            slider_rep.GetPoint2Coordinate().SetValue(0.45, 0.93)
1469        elif pos == 2:
1470            slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.93)
1471            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.93)
1472        elif pos == 3:
1473            slider_rep.GetPoint1Coordinate().SetValue(0.05, 0.06)
1474            slider_rep.GetPoint2Coordinate().SetValue(0.45, 0.06)
1475        elif pos == 4:  # bottom-right
1476            slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.06)
1477            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06)
1478        elif pos == 5:  # bottom span horizontal
1479            slider_rep.GetPoint1Coordinate().SetValue(0.04, 0.06)
1480            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06)
1481        elif pos == 11:  # top-left vertical
1482            slider_rep.GetPoint1Coordinate().SetValue(0.065, 0.54)
1483            slider_rep.GetPoint2Coordinate().SetValue(0.065, 0.9)
1484        elif pos == 12:
1485            slider_rep.GetPoint1Coordinate().SetValue(0.94, 0.54)
1486            slider_rep.GetPoint2Coordinate().SetValue(0.94, 0.9)
1487        elif pos == 13:
1488            slider_rep.GetPoint1Coordinate().SetValue(0.065, 0.1)
1489            slider_rep.GetPoint2Coordinate().SetValue(0.065, 0.54)
1490        elif pos == 14:  # bottom-right vertical
1491            slider_rep.GetPoint1Coordinate().SetValue(0.94, 0.1)
1492            slider_rep.GetPoint2Coordinate().SetValue(0.94, 0.54)
1493        elif pos == 15:  # right margin vertical
1494            slider_rep.GetPoint1Coordinate().SetValue(0.95, 0.1)
1495            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.9)
1496        else:  # bottom-right
1497            slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.06)
1498            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06)
1499
1500        if show_value:
1501            if isinstance(xmin, int) and isinstance(xmax, int) and isinstance(value, int):
1502                frm = "%0.0f"
1503            else:
1504                frm = "%0.2f"
1505
1506            frm = options.pop("tformat", frm)
1507
1508            slider_rep.SetLabelFormat(frm)  # default is '%0.3g'
1509            slider_rep.GetLabelProperty().SetShadow(0)
1510            slider_rep.GetLabelProperty().SetBold(0)
1511            slider_rep.GetLabelProperty().SetOpacity(alpha)
1512            slider_rep.GetLabelProperty().SetColor(c)
1513            if isinstance(pos, int) and pos > 10:
1514                slider_rep.GetLabelProperty().SetOrientation(90)
1515        else:
1516            slider_rep.ShowSliderLabelOff()
1517        slider_rep.GetTubeProperty().SetColor(c)
1518        slider_rep.GetTubeProperty().SetOpacity(0.75)
1519        slider_rep.GetSliderProperty().SetColor(c)
1520        slider_rep.GetSelectedProperty().SetColor(np.sqrt(np.array(c)))
1521        slider_rep.GetCapProperty().SetColor(c)
1522
1523        slider_rep.SetTitleHeight(title_height * title_size)
1524        slider_rep.GetTitleProperty().SetShadow(0)
1525        slider_rep.GetTitleProperty().SetColor(c)
1526        slider_rep.GetTitleProperty().SetOpacity(alpha)
1527        slider_rep.GetTitleProperty().SetBold(0)
1528        if font.lower() == "courier":
1529            slider_rep.GetTitleProperty().SetFontFamilyToCourier()
1530        elif font.lower() == "times":
1531            slider_rep.GetTitleProperty().SetFontFamilyToTimes()
1532        elif font.lower() == "arial":
1533            slider_rep.GetTitleProperty().SetFontFamilyToArial()
1534        else:
1535            if font == "":
1536                font = utils.get_font_path(settings.default_font)
1537            else:
1538                font = utils.get_font_path(font)
1539            slider_rep.GetTitleProperty().SetFontFamily(vtk.VTK_FONT_FILE)
1540            slider_rep.GetLabelProperty().SetFontFamily(vtk.VTK_FONT_FILE)
1541            slider_rep.GetTitleProperty().SetFontFile(font)
1542            slider_rep.GetLabelProperty().SetFontFile(font)
1543
1544        if title:
1545            slider_rep.SetTitleText(title)
1546            if not utils.is_sequence(pos):
1547                if isinstance(pos, int) and pos > 10:
1548                    slider_rep.GetTitleProperty().SetOrientation(90)
1549            else:
1550                if abs(pos[0][0] - pos[1][0]) < 0.1:
1551                    slider_rep.GetTitleProperty().SetOrientation(90)
1552
1553        SliderWidget.__init__(self)
1554
1555        self.SetAnimationModeToJump()
1556        self.SetRepresentation(slider_rep)
1557        if delayed:
1558            self.AddObserver("EndInteractionEvent", sliderfunc)
1559        else:
1560            self.AddObserver("InteractionEvent", sliderfunc)
1561
1562
1563#####################################################################
1564class Slider3D(SliderWidget):
1565    """
1566    Add a 3D slider which can call an external custom function.
1567    """
1568
1569    def __init__(
1570        self,
1571        sliderfunc,
1572        pos1,
1573        pos2,
1574        xmin,
1575        xmax,
1576        value=None,
1577        s=0.03,
1578        t=1,
1579        title="",
1580        rotation=0,
1581        c=None,
1582        show_value=True,
1583    ):
1584        """
1585        Add a 3D slider which can call an external custom function.
1586
1587        Arguments:
1588            sliderfunc : (function)
1589                external function to be called by the widget
1590            pos1 : (list)
1591                first position 3D coordinates
1592            pos2 : (list)
1593                second position 3D coordinates
1594            xmin : (float)
1595                lower value
1596            xmax : (float)
1597                upper value
1598            value : (float)
1599                initial value
1600            s : (float)
1601                label scaling factor
1602            t : (float)
1603                tube scaling factor
1604            title : (str)
1605                title text
1606            c : (color)
1607                slider color
1608            rotation : (float)
1609                title rotation around slider axis
1610            show_value : (bool)
1611                if True current value is shown on top of the slider
1612
1613        Examples:
1614            - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py)
1615        """
1616        c = get_color(c)
1617
1618        if value is None or value < xmin:
1619            value = xmin
1620
1621        slider_rep = vtk.vtkSliderRepresentation3D()
1622        slider_rep.SetMinimumValue(xmin)
1623        slider_rep.SetMaximumValue(xmax)
1624        slider_rep.SetValue(value)
1625
1626        slider_rep.GetPoint1Coordinate().SetCoordinateSystemToWorld()
1627        slider_rep.GetPoint2Coordinate().SetCoordinateSystemToWorld()
1628        slider_rep.GetPoint1Coordinate().SetValue(pos2)
1629        slider_rep.GetPoint2Coordinate().SetValue(pos1)
1630
1631        # slider_rep.SetPoint1InWorldCoordinates(pos2[0], pos2[1], pos2[2])
1632        # slider_rep.SetPoint2InWorldCoordinates(pos1[0], pos1[1], pos1[2])
1633
1634        slider_rep.SetSliderWidth(0.03 * t)
1635        slider_rep.SetTubeWidth(0.01 * t)
1636        slider_rep.SetSliderLength(0.04 * t)
1637        slider_rep.SetSliderShapeToCylinder()
1638        slider_rep.GetSelectedProperty().SetColor(np.sqrt(np.array(c)))
1639        slider_rep.GetSliderProperty().SetColor(np.array(c) / 1.5)
1640        slider_rep.GetCapProperty().SetOpacity(0)
1641        slider_rep.SetRotation(rotation)
1642
1643        if not show_value:
1644            slider_rep.ShowSliderLabelOff()
1645
1646        slider_rep.SetTitleText(title)
1647        slider_rep.SetTitleHeight(s * t)
1648        slider_rep.SetLabelHeight(s * t * 0.85)
1649
1650        slider_rep.GetTubeProperty().SetColor(c)
1651
1652        SliderWidget.__init__(self)
1653
1654        self.SetRepresentation(slider_rep)
1655        self.SetAnimationModeToJump()
1656        self.AddObserver("InteractionEvent", sliderfunc)
1657
1658class BaseCutter:
1659    """
1660    Base class for Cutter widgets.
1661    """
1662    def __init__(self):
1663        self._implicit_func = None
1664        self.widget = None
1665        self.clipper=None
1666        self.cutter=None
1667        self._alpha = 0.5
1668        self._keypress_id = None
1669
1670    def invert(self):
1671        """Invert selection."""
1672        self.clipper.SetInsideOut(not self.clipper.GetInsideOut())
1673        return self
1674
1675    def bounds(self, value=None):
1676        """Set or get the bounding box."""
1677        if value is None:
1678            return self.cutter.GetBounds()
1679        else:
1680            self._implicit_func.SetBounds(value)
1681            return self
1682    
1683    def on(self):
1684        """Switch the widget on or off."""
1685        self.widget.On()
1686        return self
1687
1688    def off(self):
1689        """Switch the widget on or off."""
1690        self.widget.Off()
1691        return self    
1692
1693    def add_to(self, plt):
1694        """Assign the widget to the provided `Plotter` instance."""
1695        self.widget.SetInteractor(plt.interactor)
1696        self.widget.SetCurrentRenderer(plt.renderer)
1697        if self.widget not in plt.widgets:
1698            plt.widgets.append(self.widget)
1699
1700        cpoly = self.clipper.GetOutput()
1701        self.mesh._update(cpoly)
1702
1703        out = self.clipper.GetClippedOutputPort()
1704        self.remnant.mapper().SetInputConnection(out)  
1705        self.remnant.alpha(self._alpha).color((0.5, 0.5, 0.5))
1706        self.remnant.lighting('off').wireframe()
1707        plt.add(self.remnant)
1708        self._keypress_id = plt.interactor.AddObserver("KeyPressEvent", self._keypress)
1709        if plt.interactor and plt.interactor.GetInitialized():
1710            self.widget.On()
1711            self._select_polygons(self.widget, "InteractionEvent")
1712            plt.interactor.Render()
1713        return self
1714    
1715    def remove_from(self, plt):
1716        """Remove the widget to the provided `Plotter` instance."""
1717        self.widget.Off()
1718        self.RemoveAllObservers()
1719        plt.remove(self.remnant)
1720        if self.widget in plt.widgets:
1721            plt.widgets.remove(self.widget)
1722        if self._keypress_id:
1723            plt.interactor.RemoveObserver(self._keypress_id)
1724        return self
1725
1726
1727class PlaneCutter(vtk.vtkPlaneWidget, BaseCutter):
1728    """
1729    Create a box widget to cut away parts of a Mesh.
1730    """
1731    def __init__(
1732            self,
1733            mesh,
1734            invert=False,
1735            can_translate=True,
1736            can_scale=True,
1737            c=(0.25, 0.25, 0.25),
1738            origin=(),
1739            normal=(),
1740            padding=0.05,
1741            alpha=0.05,
1742    ):
1743        """
1744        Create a box widget to cut away parts of a Mesh.
1745
1746        Arguments:
1747            mesh : (Mesh)
1748                the input mesh
1749            invert : (bool)
1750                invert the clipping plane
1751            can_translate : (bool)
1752                enable translation of the widget
1753            can_scale : (bool)
1754                enable scaling of the widget
1755            origin : (list)
1756                origin of the plane
1757            normal : (list)
1758                normal to the plane
1759            padding : (float)
1760                padding around the input mesh
1761            c : (color)
1762                color of the box cutter widget
1763            alpha : (float)
1764                transparency of the cut-off part of the input mesh
1765        """
1766        super().__init__()
1767
1768        self.mesh = mesh
1769        self.remnant = Mesh()
1770        self.remnant.name = mesh.name + "Remnant"
1771        self.remnant.pickable(False)
1772        
1773        self._alpha = alpha
1774        self._keypress_id = None
1775
1776        self._implicit_func = vtk.vtkPlane()
1777
1778        poly = mesh.polydata()
1779        self.clipper = vtk.vtkClipPolyData()
1780        self.clipper.GenerateClipScalarsOff()
1781        self.clipper.SetInputData(poly)
1782        self.clipper.SetClipFunction(self._implicit_func)
1783        self.clipper.SetInsideOut(invert)
1784        self.clipper.GenerateClippedOutputOn()
1785        self.clipper.Update()
1786
1787        self.widget = vtk.vtkImplicitPlaneWidget()
1788
1789        # self.widget.KeyPressActivationOff()
1790        # self.widget.SetKeyPressActivationValue('i')
1791
1792        self.widget.SetOriginTranslation(can_translate)
1793        self.widget.SetOutlineTranslation(can_translate)
1794        self.widget.SetScaleEnabled(can_scale)
1795
1796        self.widget.GetOutlineProperty().SetColor(c)
1797        self.widget.GetOutlineProperty().SetOpacity(0.25)
1798        self.widget.GetOutlineProperty().SetLineWidth(1)
1799        self.widget.GetOutlineProperty().LightingOff()
1800
1801        self.widget.GetSelectedOutlineProperty().SetColor(get_color("red3"))
1802
1803        self.widget.SetTubing(0)
1804        self.widget.SetDrawPlane(1)
1805        self.widget.GetPlaneProperty().LightingOff()
1806        self.widget.GetPlaneProperty().SetOpacity(0.05)
1807        self.widget.GetSelectedPlaneProperty().SetColor(get_color("red5"))
1808        self.widget.GetSelectedPlaneProperty().LightingOff()
1809
1810        self.widget.SetPlaceFactor(1.0 + padding)
1811        self.widget.SetInputData(poly)
1812        self.widget.PlaceWidget()
1813        self.widget.AddObserver("InteractionEvent", self._select_polygons)
1814
1815        if len(origin) == 3:
1816            self.widget.SetOrigin(origin)
1817        else:
1818            self.widget.SetOrigin(mesh.center_of_mass())
1819        
1820        if len(normal) == 3:
1821            self.widget.SetNormal(normal)
1822        else:
1823            self.widget.SetNormal((1, 0, 0))
1824
1825        
1826    def _select_polygons(self, vobj, event):
1827        vobj.GetPlane(self._implicit_func)
1828
1829    def _keypress(self, vobj, event):
1830        if vobj.GetKeySym() == "r": # reset planes
1831            self.widget.GetPlane(self._implicit_func)
1832            self.widget.PlaceWidget()
1833            self.widget.GetInteractor().Render()
1834        elif vobj.GetKeySym() == "u": # invert cut
1835            self.invert()
1836            self.widget.GetInteractor().Render()
1837        elif vobj.GetKeySym() == "x": # set normal along x
1838            self.widget.SetNormal((1, 0, 0))
1839            self.widget.GetPlane(self._implicit_func)
1840            self.widget.PlaceWidget()
1841            self.widget.GetInteractor().Render()
1842        elif vobj.GetKeySym() == "y": # set normal along y
1843            self.widget.SetNormal((0, 1, 0))
1844            self.widget.GetPlane(self._implicit_func)
1845            self.widget.PlaceWidget()
1846            self.widget.GetInteractor().Render()
1847        elif vobj.GetKeySym() == "z": # set normal along z
1848            self.widget.SetNormal((0, 0, 1))
1849            self.widget.GetPlane(self._implicit_func)
1850            self.widget.PlaceWidget()
1851            self.widget.GetInteractor().Render()        
1852        elif vobj.GetKeySym() == "s": # Ctrl+s to save mesh
1853            if self.widget.GetInteractor():
1854                if self.widget.GetInteractor().GetControlKey():
1855                    printc(":save: saving mesh to vedo_clipped.vtk")
1856                    self.mesh.write("vedo_clipped.vtk")
1857
1858
1859class BoxCutter(vtk.vtkBoxWidget, BaseCutter):
1860    """
1861    Create a box widget to cut away parts of a Mesh.
1862    """
1863    def __init__(
1864            self,
1865            mesh,
1866            invert=False,
1867            can_rotate=True,
1868            can_translate=True,
1869            can_scale=True,
1870            initial_bounds=(),
1871            padding=0.025,
1872            c=(0.25, 0.25, 0.25),
1873            alpha=0.05,
1874    ):
1875        """
1876        Create a box widget to cut away parts of a Mesh.
1877
1878        Arguments:
1879            mesh : (Mesh)
1880                the input mesh
1881            invert : (bool)
1882                invert the clipping plane
1883            can_rotate : (bool)
1884                enable rotation of the widget
1885            can_translate : (bool)
1886                enable translation of the widget
1887            can_scale : (bool)
1888                enable scaling of the widget
1889            initial_bounds : (list)
1890                initial bounds of the box widget
1891            c : (color)
1892                color of the box cutter widget
1893            alpha : (float)
1894                transparency of the cut-off part of the input mesh
1895        """
1896        super().__init__()
1897
1898        self.mesh = mesh
1899        self.remnant = Mesh()
1900        self.remnant.name = mesh.name + "Remnant"
1901        self.remnant.pickable(False)
1902
1903        self._alpha = alpha
1904        self._keypress_id = None
1905        self._init_bounds = initial_bounds
1906        if len(self._init_bounds) == 0:
1907            self._init_bounds = mesh.bounds()
1908        else:
1909            self._init_bounds = initial_bounds
1910
1911        self._implicit_func = vtk.vtkPlanes()
1912        self._implicit_func.SetBounds(self._init_bounds)
1913
1914        poly = mesh.polydata()
1915        self.clipper = vtk.vtkClipPolyData()
1916        self.clipper.GenerateClipScalarsOff()
1917        self.clipper.SetInputData(poly)
1918        self.clipper.SetClipFunction(self._implicit_func)
1919        self.clipper.SetInsideOut(not invert)
1920        self.clipper.GenerateClippedOutputOn()
1921        self.clipper.Update()
1922
1923        self.widget = vtk.vtkBoxWidget()
1924
1925        self.widget.SetRotationEnabled(can_rotate)
1926        self.widget.SetTranslationEnabled(can_translate)
1927        self.widget.SetScalingEnabled(can_scale)
1928
1929        self.widget.OutlineCursorWiresOn()
1930        self.widget.GetSelectedOutlineProperty().SetColor(get_color("red3"))
1931        self.widget.GetSelectedHandleProperty().SetColor(get_color("red5"))
1932       
1933        self.widget.GetOutlineProperty().SetColor(c)
1934        self.widget.GetOutlineProperty().SetOpacity(1)
1935        self.widget.GetOutlineProperty().SetLineWidth(1)
1936        self.widget.GetOutlineProperty().LightingOff()
1937
1938        self.widget.GetSelectedFaceProperty().LightingOff()
1939        self.widget.GetSelectedFaceProperty().SetOpacity(0.1)
1940
1941        self.widget.SetPlaceFactor(1.0 + padding)
1942        self.widget.SetInputData(poly)
1943        self.widget.PlaceWidget()
1944        self.widget.AddObserver("InteractionEvent", self._select_polygons)
1945
1946    def _select_polygons(self, vobj, event):
1947        vobj.GetPlanes(self._implicit_func)
1948
1949    def _keypress(self, vobj, event):
1950        if vobj.GetKeySym() == "r":  # reset planes
1951            self._implicit_func.SetBounds(self._init_bounds)
1952            self.widget.GetPlanes(self._implicit_func)
1953            self.widget.PlaceWidget()
1954            self.widget.GetInteractor().Render()
1955        elif vobj.GetKeySym() == "u":
1956            self.invert()
1957            self.widget.GetInteractor().Render()
1958        elif vobj.GetKeySym() == "s": # Ctrl+s to save mesh
1959            if self.widget.GetInteractor():
1960                if self.widget.GetInteractor().GetControlKey():
1961                    printc(":save: saving mesh to vedo_clipped.vtk")
1962                    self.mesh.write("vedo_clipped.vtk")
1963
1964
1965class SphereCutter(vtk.vtkSphereWidget, BaseCutter):
1966    """
1967    Create a box widget to cut away parts of a Mesh.
1968    """
1969    def __init__(
1970            self,
1971            mesh,
1972            invert=False,
1973            can_translate=True,
1974            can_scale=True,
1975            origin=(),
1976            radius=0,
1977            res=60,
1978            c='white',
1979            alpha=0.05,
1980    ):
1981        """
1982        Create a box widget to cut away parts of a Mesh.
1983
1984        Arguments:
1985            mesh : Mesh
1986                the input mesh
1987            invert : bool
1988                invert the clipping
1989            can_translate : bool
1990                enable translation of the widget
1991            can_scale : bool
1992                enable scaling of the widget
1993            origin : list
1994                initial position of the sphere widget
1995            radius : float
1996                initial radius of the sphere widget
1997            res : int
1998                resolution of the sphere widget
1999            c : color
2000                color of the box cutter widget
2001            alpha : float
2002                transparency of the cut-off part of the input mesh
2003        """
2004        super().__init__()
2005
2006        self.mesh = mesh
2007        self.remnant = Mesh()
2008        self.remnant.name = mesh.name + "Remnant"
2009        self.remnant.pickable(False)
2010
2011        self._alpha = alpha
2012        self._keypress_id = None
2013
2014        self._implicit_func = vtk.vtkSphere()
2015
2016        if len(origin) == 3:
2017            self._implicit_func.SetCenter(origin)
2018        else:
2019            origin = mesh.center_of_mass()
2020            self._implicit_func.SetCenter(origin)
2021        
2022        if radius > 0:
2023            self._implicit_func.SetRadius(radius)
2024        else:
2025            radius = mesh.average_size() * 2
2026            self._implicit_func.SetRadius(radius)
2027            
2028        poly = mesh.polydata()
2029        self.clipper = vtk.vtkClipPolyData()
2030        self.clipper.GenerateClipScalarsOff()
2031        self.clipper.SetInputData(poly)
2032        self.clipper.SetClipFunction(self._implicit_func)
2033        self.clipper.SetInsideOut(not invert)
2034        self.clipper.GenerateClippedOutputOn()
2035        self.clipper.Update()
2036
2037        self.widget = vtk.vtkSphereWidget()
2038
2039        self.widget.SetThetaResolution(res*2)
2040        self.widget.SetPhiResolution(res)
2041        self.widget.SetRadius(radius)
2042        self.widget.SetCenter(origin)
2043        self.widget.SetRepresentation(2)
2044        self.widget.HandleVisibilityOff()
2045
2046        self.widget.SetTranslation(can_translate)
2047        self.widget.SetScale(can_scale)
2048
2049        self.widget.HandleVisibilityOff()
2050        self.widget.GetSphereProperty().SetColor(get_color(c))
2051        self.widget.GetSphereProperty().SetOpacity(0.2)
2052        self.widget.GetSelectedSphereProperty().SetColor(get_color("red5"))
2053        self.widget.GetSelectedSphereProperty().SetOpacity(0.2)
2054
2055        self.widget.SetPlaceFactor(1.0)
2056        self.widget.SetInputData(poly)
2057        self.widget.PlaceWidget()
2058        self.widget.AddObserver("InteractionEvent", self._select_polygons)
2059
2060    def _select_polygons(self, vobj, event):
2061        vobj.GetSphere(self._implicit_func)
2062
2063    def _keypress(self, vobj, event):
2064        if vobj.GetKeySym() == "r":  # reset planes
2065            self._implicit_func.SetBounds(self._init_bounds)
2066            self.widget.GetPlanes(self._implicit_func)
2067            self.widget.PlaceWidget()
2068            self.widget.GetInteractor().Render()
2069        elif vobj.GetKeySym() == "u":
2070            self.invert()
2071            self.widget.GetInteractor().Render()
2072        elif vobj.GetKeySym() == "s": # Ctrl+s to save mesh
2073            if self.widget.GetInteractor():
2074                if self.widget.GetInteractor().GetControlKey():
2075                    printc(":save: saving mesh to vedo_clipped.vtk")
2076                    self.mesh.write("vedo_clipped.vtk")
2077
2078
2079#####################################################################
2080class RendererFrame(vtk.vtkActor2D):
2081    """
2082    Add a line around the renderer subwindow.
2083    """
2084
2085    def __init__(self, c="k", alpha=None, lw=None, padding=None):
2086        """
2087        Add a line around the renderer subwindow.
2088
2089        Arguments:
2090            c : (color)
2091                color of the line.
2092            alpha : (float)
2093                opacity.
2094            lw : (int)
2095                line width in pixels.
2096            padding : (int)
2097                padding in pixel units.
2098        """
2099
2100        if lw is None:
2101            lw = settings.renderer_frame_width
2102        if lw == 0:
2103            return None
2104
2105        if alpha is None:
2106            alpha = settings.renderer_frame_alpha
2107
2108        if padding is None:
2109            padding = settings.renderer_frame_padding
2110
2111        c = get_color(c)
2112
2113        ppoints = vtk.vtkPoints()  # Generate the polyline
2114        xy = 1 - padding
2115        psqr = [[padding, padding], [padding, xy], [xy, xy], [xy, padding], [padding, padding]]
2116        for i, pt in enumerate(psqr):
2117            ppoints.InsertPoint(i, pt[0], pt[1], 0)
2118        lines = vtk.vtkCellArray()
2119        lines.InsertNextCell(len(psqr))
2120        for i in range(len(psqr)):
2121            lines.InsertCellPoint(i)
2122        pd = vtk.vtkPolyData()
2123        pd.SetPoints(ppoints)
2124        pd.SetLines(lines)
2125
2126        mapper = vtk.vtkPolyDataMapper2D()
2127        mapper.SetInputData(pd)
2128        cs = vtk.vtkCoordinate()
2129        cs.SetCoordinateSystemToNormalizedViewport()
2130        mapper.SetTransformCoordinate(cs)
2131
2132        vtk.vtkActor2D.__init__(self)
2133
2134        self.GetPositionCoordinate().SetValue(0, 0)
2135        self.GetPosition2Coordinate().SetValue(1, 1)
2136        self.SetMapper(mapper)
2137        self.GetProperty().SetColor(c)
2138        self.GetProperty().SetOpacity(alpha)
2139        self.GetProperty().SetLineWidth(lw)
2140
2141#####################################################################
2142class ProgressBarWidget(vtk.vtkActor2D):
2143    """
2144    Add a progress bar in the rendering window.
2145    """
2146    def __init__(self, n=None, c="blue5", alpha=0.8, lw=10, autohide=True):
2147        """
2148        Add a progress bar window.
2149
2150        Arguments:
2151            n : (int)
2152                number of iterations. 
2153                If None, you need to call `update(fraction)` manually.
2154            c : (color)
2155                color of the line.
2156            alpha : (float)
2157                opacity of the line.
2158            lw : (int)
2159                line width in pixels.
2160            autohide : (bool)
2161                if True, hide the progress bar when completed.
2162        """
2163        self.n = 0
2164        self.iterations = n
2165        self.autohide = autohide
2166
2167        ppoints = vtk.vtkPoints()  # Generate the line
2168        psqr = [[0, 0, 0], [1, 0, 0]]
2169        for i, pt in enumerate(psqr):
2170            ppoints.InsertPoint(i, *pt)
2171        lines = vtk.vtkCellArray()
2172        lines.InsertNextCell(len(psqr))
2173        for i in range(len(psqr)):
2174            lines.InsertCellPoint(i)
2175        pd = vtk.vtkPolyData()
2176        pd.SetPoints(ppoints)
2177        pd.SetLines(lines)
2178        self._data = pd
2179
2180        mapper = vtk.vtkPolyDataMapper2D()
2181        mapper.SetInputData(pd)
2182        cs = vtk.vtkCoordinate()
2183        cs.SetCoordinateSystemToNormalizedViewport()
2184        mapper.SetTransformCoordinate(cs)
2185
2186        vtk.vtkActor2D.__init__(self)
2187        
2188        self.SetMapper(mapper)
2189        self.GetProperty().SetOpacity(alpha)
2190        self.GetProperty().SetColor(get_color(c))
2191        self.GetProperty().SetLineWidth(lw*2)
2192
2193        
2194    def lw(self, value):
2195        """Set width."""
2196        self.GetProperty().SetLineWidth(value*2)
2197        return self
2198
2199    def c(self, color):
2200        """Set color."""
2201        c = get_color(color)
2202        self.GetProperty().SetColor(c)
2203        return self
2204    
2205    def alpha(self, value):
2206        """Set opacity."""
2207        self.GetProperty().SetOpacity(value)
2208        return self
2209
2210    def update(self, fraction=None):
2211        """Update progress bar to fraction of the window width."""
2212        if fraction is None:
2213            if self.iterations is None:
2214                vedo.printc("Error in ProgressBarWindow: must specify iterations", c='r')
2215                return self
2216            self.n += 1
2217            fraction = self.n / self.iterations
2218
2219        if fraction >= 1 and self.autohide:
2220            fraction = 0
2221
2222        psqr = [[0, 0, 0], [fraction, 0, 0]]
2223        vpts = utils.numpy2vtk(psqr, dtype=np.float32)
2224        self._data.GetPoints().SetData(vpts)
2225        return self
2226    
2227    def reset(self):
2228        """Reset progress bar."""
2229        self.n = 0
2230        self.update(0)
2231        return self
2232
2233
2234#####################################################################
2235class Icon(vtk.vtkOrientationMarkerWidget):
2236    """
2237    Add an inset icon mesh into the renderer.
2238    """
2239
2240    def __init__(self, mesh, pos=3, size=0.08):
2241        """
2242        Arguments:
2243            pos : (list, int)
2244                icon position in the range [1-4] indicating one of the 4 corners,
2245                or it can be a tuple (x,y) as a fraction of the renderer size.
2246            size : (float)
2247                size of the icon space as fraction of the window size.
2248
2249        Examples:
2250            - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py)
2251        """
2252        vtk.vtkOrientationMarkerWidget.__init__(self)
2253        self.SetOrientationMarker(mesh)
2254
2255        if utils.is_sequence(pos):
2256            self.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size)
2257        else:
2258            if pos < 2:
2259                self.SetViewport(0, 1 - 2 * size, size * 2, 1)
2260            elif pos == 2:
2261                self.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1)
2262            elif pos == 3:
2263                self.SetViewport(0, 0, size * 2, size * 2)
2264            elif pos == 4:
2265                self.SetViewport(1 - 2 * size, 0, 1, size * 2)
2266
2267
2268#####################################################################
2269def compute_visible_bounds(actors=None):
2270    """Calculate max meshes bounds and sizes."""
2271    bns = []
2272
2273    if actors is None:
2274        actors = vedo.plotter_instance.actors
2275    elif not utils.is_sequence(actors):
2276        actors = [actors]
2277
2278    try:
2279        # this block fails for VolumeSlice as vtkImageSlice.GetBounds() returns a pointer..
2280        # in any case we dont need axes for that one.
2281        for a in actors:
2282            if a and a.GetUseBounds():
2283                b = a.GetBounds()
2284                if b:
2285                    bns.append(b)
2286        if bns:
2287            max_bns = np.max(bns, axis=0)
2288            min_bns = np.min(bns, axis=0)
2289            vbb = [min_bns[0], max_bns[1], min_bns[2], max_bns[3], min_bns[4], max_bns[5]]
2290        else:
2291            vbb = list(vedo.plotter_instance.renderer.ComputeVisiblePropBounds())
2292            max_bns = vbb
2293            min_bns = vbb
2294        sizes = np.array(
2295            [max_bns[1] - min_bns[0], max_bns[3] - min_bns[2], max_bns[5] - min_bns[4]]
2296        )
2297        return [vbb, sizes, min_bns, max_bns]
2298    except:
2299        return [[0, 0, 0, 0, 0, 0], [0, 0, 0], 0, 0]
2300
2301
2302#####################################################################
2303def Ruler(
2304    p1,
2305    p2,
2306    units_scale=1,
2307    label="",
2308    s=None,
2309    font=None,
2310    italic=0,
2311    prefix="",
2312    units="",  # eg.'μm'
2313    c=(0.2, 0.1, 0.1),
2314    alpha=1,
2315    lw=1,
2316    precision=3,
2317    label_rotation=0,
2318    axis_rotation=0,
2319    tick_angle=90,
2320):
2321    """
2322    Build a 3D ruler to indicate the distance of two points p1 and p2.
2323
2324    Arguments:
2325        label : (str)
2326            alternative fixed label to be shown
2327        units_scale : (float)
2328            factor to scale units (e.g. μm to mm)
2329        s : (float)
2330            size of the label
2331        font : (str)
2332            font face.  Check [available fonts here](https://vedo.embl.es/fonts).
2333        italic : (float)
2334            italicness of the font in the range [0,1]
2335        units : (str)
2336            string to be appended to the numeric value
2337        lw : (int)
2338            line width in pixel units
2339        precision : (int)
2340            nr of significant digits to be shown
2341        label_rotation : (float)
2342            initial rotation of the label around the z-axis
2343        axis_rotation : (float)
2344            initial rotation of the line around the main axis
2345        tick_angle : (float)
2346            initial rotation of the line around the main axis
2347
2348    Examples:
2349        - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py)
2350
2351        ![](https://vedo.embl.es/images/pyplot/goniometer.png)
2352    """
2353    if units_scale != 1.0 and units == "":
2354        raise ValueError(
2355            "When setting 'units_scale' to a value other than 1, "
2356            + "a 'units' arguments must be specified."
2357        )
2358
2359    if isinstance(p1, Points):
2360        p1 = p1.GetPosition()
2361    if isinstance(p2, Points):
2362        p2 = p2.GetPosition()
2363
2364    if len(p1) == 2:
2365        p1 = [p1[0], p1[1], 0.0]
2366    if len(p2) == 2:
2367        p2 = [p2[0], p2[1], 0.0]
2368
2369    p1, p2 = np.array(p1), np.array(p2)
2370    q1, q2 = [0, 0, 0], [utils.mag(p2 - p1), 0, 0]
2371    q1, q2 = np.array(q1), np.array(q2)
2372    v = q2 - q1
2373    d = utils.mag(v) * units_scale
2374
2375    if s is None:
2376        s = d * 0.02 * (1 / units_scale)
2377
2378    if not label:
2379        label = str(d)
2380        if precision:
2381            label = utils.precision(d, precision)
2382    if prefix:
2383        label = prefix + "~" + label
2384    if units:
2385        label += "~" + units
2386
2387    lb = shapes.Text3D(label, pos=(q1 + q2) / 2, s=s, font=font, italic=italic, justify="center")
2388    if label_rotation:
2389        lb.RotateZ(label_rotation)
2390
2391    x0, x1 = lb.xbounds()
2392    gap = [(x1 - x0) / 2, 0, 0]
2393    pc1 = (v / 2 - gap) * 0.9 + q1
2394    pc2 = q2 - (v / 2 - gap) * 0.9
2395
2396    lc1 = shapes.Line(q1 - v / 50, pc1)
2397    lc2 = shapes.Line(q2 + v / 50, pc2)
2398
2399    zs = np.array([0, d / 50 * (1 / units_scale), 0])
2400    ml1 = shapes.Line(-zs, zs).pos(q1)
2401    ml2 = shapes.Line(-zs, zs).pos(q2)
2402    ml1.RotateZ(tick_angle - 90)
2403    ml2.RotateZ(tick_angle - 90)
2404
2405    c1 = shapes.Circle(q1, r=d / 180 * (1 / units_scale), res=20)
2406    c2 = shapes.Circle(q2, r=d / 180 * (1 / units_scale), res=20)
2407
2408    acts = [lb, lc1, lc2, c1, c2, ml1, ml2]
2409    macts = merge(acts).pos(p1).c(c).alpha(alpha)
2410    macts.GetProperty().LightingOff()
2411    macts.GetProperty().SetLineWidth(lw)
2412    macts.UseBoundsOff()
2413    macts.base = q1
2414    macts.top = q2
2415    macts.orientation(p2 - p1, rotation=axis_rotation).bc("t").pickable(False)
2416    return macts
2417
2418
2419def RulerAxes(
2420    inputobj,
2421    xtitle="",
2422    ytitle="",
2423    ztitle="",
2424    xlabel="",
2425    ylabel="",
2426    zlabel="",
2427    xpadding=0.05,
2428    ypadding=0.04,
2429    zpadding=0,
2430    font="Normografo",
2431    s=None,
2432    italic=0,
2433    units="",
2434    c=(0.2, 0, 0),
2435    alpha=1,
2436    lw=1,
2437    precision=3,
2438    label_rotation=0,
2439    xaxis_rotation=0,
2440    yaxis_rotation=0,
2441    zaxis_rotation=0,
2442    xycross=True,
2443):
2444    """
2445    A 3D ruler axes to indicate the sizes of the input scene or object.
2446
2447    Arguments:
2448        xtitle : (str)
2449            name of the axis or title
2450        xlabel : (str)
2451            alternative fixed label to be shown instead of the distance
2452        s : (float)
2453            size of the label
2454        font : (str)
2455            font face. Check [available fonts here](https://vedo.embl.es/fonts).
2456        italic : (float)
2457            italicness of the font in the range [0,1]
2458        units : (str)
2459            string to be appended to the numeric value
2460        lw : (int)
2461            line width in pixel units
2462        precision : (int)
2463            nr of significant digits to be shown
2464        label_rotation : (float)
2465            initial rotation of the label around the z-axis
2466        [x,y,z]axis_rotation : (float)
2467            initial rotation of the line around the main axis in degrees
2468        xycross : (bool)
2469            show two back crossing lines in the xy plane
2470
2471    Examples:
2472        - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py)
2473    """
2474    if utils.is_sequence(inputobj):
2475        x0, x1, y0, y1, z0, z1 = inputobj
2476    else:
2477        x0, x1, y0, y1, z0, z1 = inputobj.bounds()
2478    dx, dy, dz = (y1 - y0) * xpadding, (x1 - x0) * ypadding, (y1 - y0) * zpadding
2479    d = np.sqrt((y1 - y0) ** 2 + (x1 - x0) ** 2 + (z1 - z0) ** 2)
2480
2481    if not d:
2482        return None
2483
2484    if s is None:
2485        s = d / 75
2486
2487    acts, rx, ry = [], None, None
2488    if xtitle is not None and (x1 - x0) / d > 0.1:
2489        rx = Ruler(
2490            [x0, y0 - dx, z0],
2491            [x1, y0 - dx, z0],
2492            s=s,
2493            font=font,
2494            precision=precision,
2495            label_rotation=label_rotation,
2496            axis_rotation=xaxis_rotation,
2497            lw=lw,
2498            italic=italic,
2499            prefix=xtitle,
2500            label=xlabel,
2501            units=units,
2502        )
2503        acts.append(rx)
2504    if ytitle is not None and (y1 - y0) / d > 0.1:
2505        ry = Ruler(
2506            [x1 + dy, y0, z0],
2507            [x1 + dy, y1, z0],
2508            s=s,
2509            font=font,
2510            precision=precision,
2511            label_rotation=label_rotation,
2512            axis_rotation=yaxis_rotation,
2513            lw=lw,
2514            italic=italic,
2515            prefix=ytitle,
2516            label=ylabel,
2517            units=units,
2518        )
2519        acts.append(ry)
2520    if ztitle is not None and (z1 - z0) / d > 0.1:
2521        rz = Ruler(
2522            [x0 - dy, y0 + dz, z0],
2523            [x0 - dy, y0 + dz, z1],
2524            s=s,
2525            font=font,
2526            precision=precision,
2527            label_rotation=label_rotation,
2528            axis_rotation=zaxis_rotation + 90,
2529            lw=lw,
2530            italic=italic,
2531            prefix=ztitle,
2532            label=zlabel,
2533            units=units,
2534        )
2535        acts.append(rz)
2536
2537    if xycross and rx and ry:
2538        lx = shapes.Line([x0, y0, z0], [x0, y1 + dx, z0])
2539        ly = shapes.Line([x0 - dy, y1, z0], [x1, y1, z0])
2540        d = min((x1 - x0), (y1 - y0)) / 200
2541        cxy = shapes.Circle([x0, y1, z0], r=d, res=15)
2542        acts.extend([lx, ly, cxy])
2543
2544    macts = merge(acts)
2545    if not macts:
2546        return None
2547    macts.c(c).alpha(alpha).bc("t")
2548    macts.UseBoundsOff()
2549    macts.PickableOff()
2550    return macts
2551
2552
2553#####################################################################
2554class Ruler2D(vtk.vtkAxisActor2D):
2555    """
2556    Create a ruler with tick marks, labels and a title.
2557    """
2558
2559    def __init__(
2560        self,
2561        lw=2,
2562        ticks=True,
2563        labels=False,
2564        c="k",
2565        alpha=1,
2566        title="",
2567        font="Calco",
2568        font_size=24,
2569        bc=None,
2570    ):
2571        """
2572        Create a ruler with tick marks, labels and a title.
2573
2574        Ruler2D is a 2D actor; that is, it is drawn on the overlay
2575        plane and is not occluded by 3D geometry.
2576        To use this class, specify two points defining the start and end
2577        with update_points() as 3D points.
2578
2579        This class decides decides how to create reasonable tick
2580        marks and labels.
2581
2582        Labels are drawn on the "right" side of the axis.
2583        The "right" side is the side of the axis on the right.
2584        The way the labels and title line up with the axis and tick marks
2585        depends on whether the line is considered horizontal or vertical.
2586
2587        Arguments:
2588            lw : (int)
2589                width of the line in pixel units
2590            ticks : (bool)
2591                control if drawing the tick marks
2592            labels : (bool)
2593                control if drawing the numeric labels
2594            c : (color)
2595                color of the object
2596            alpha : (float)
2597                opacity of the object
2598            title : (str)
2599                title of the ruler
2600            font : (str)
2601                font face name. Check [available fonts here](https://vedo.embl.es/fonts).
2602            font_size : (int)
2603                font size
2604            bc : (color)
2605                background color of the title
2606
2607        Example:
2608            ```python
2609            from vedo  import *
2610            plt = Plotter(axes=1, interactive=False)
2611            plt.show(Cube())
2612            rul = Ruler2D()
2613            rul.set_points([0,0,0], [0.5,0.5,0.5])
2614            plt.add(rul)
2615            plt.interactive().close()
2616            ```
2617            ![](https://vedo.embl.es/images/feats/dist_tool.png)
2618        """
2619        vtk.vtkAxisActor2D.__init__(self)
2620
2621        plt = vedo.plotter_instance
2622        if not plt:
2623            vedo.logger.error("Ruler2D need to initialize Plotter first.")
2624            raise RuntimeError()
2625
2626        self.p0 = [0, 0, 0]
2627        self.p1 = [0, 0, 0]
2628        self.distance = 0
2629        self.title = title
2630
2631        prop = self.GetProperty()
2632        tprop = self.GetTitleTextProperty()
2633
2634        self.SetTitle(title)
2635        self.SetNumberOfLabels(9)
2636
2637        if not font:
2638            font = settings.default_font
2639        if font.lower() == "courier":
2640            tprop.SetFontFamilyToCourier()
2641        elif font.lower() == "times":
2642            tprop.SetFontFamilyToTimes()
2643        elif font.lower() == "arial":
2644            tprop.SetFontFamilyToArial()
2645        else:
2646            tprop.SetFontFamily(vtk.VTK_FONT_FILE)
2647            tprop.SetFontFile(utils.get_font_path(font))
2648        tprop.SetFontSize(font_size)
2649        tprop.BoldOff()
2650        tprop.ItalicOff()
2651        tprop.ShadowOff()
2652        tprop.SetColor(get_color(c))
2653        tprop.SetOpacity(alpha)
2654        if bc is not None:
2655            bc = get_color(bc)
2656            tprop.SetBackgroundColor(bc)
2657            tprop.SetBackgroundOpacity(alpha)
2658
2659        lprop = vtk.vtkTextProperty()
2660        lprop.ShallowCopy(tprop)
2661        self.SetLabelTextProperty(lprop)
2662
2663        self.SetLabelFormat("%0.3g")
2664        self.SetTickVisibility(ticks)
2665        self.SetLabelVisibility(labels)
2666        prop.SetLineWidth(lw)
2667        prop.SetColor(get_color(c))
2668
2669        self.renderer = plt.renderer
2670        self.cid = plt.interactor.AddObserver("RenderEvent", self._update_viz, 1.0)
2671
2672    def color(self, c):
2673        """Assign a new color."""
2674        c = get_color(c)
2675        self.GetTitleTextProperty().SetColor(c)
2676        self.GetLabelTextProperty().SetColor(c)
2677        self.GetProperty().SetColor(c)
2678        return self
2679
2680    def off(self):
2681        """Switch off the ruler completely."""
2682        self.renderer.RemoveObserver(self.cid)
2683        self.renderer.RemoveActor(self)
2684
2685    def set_points(self, p0, p1):
2686        """Set new values for the ruler start and end points."""
2687        self.p0 = np.asarray(p0)
2688        self.p1 = np.asarray(p1)
2689        self._update_viz(0, 0)
2690        return self
2691
2692    def _update_viz(self, evt, name):
2693        ren = self.renderer
2694        view_size = np.array(ren.GetSize())
2695
2696        ren.SetWorldPoint(*self.p0, 1)
2697        ren.WorldToDisplay()
2698        disp_point1 = ren.GetDisplayPoint()[:2]
2699        disp_point1 = np.array(disp_point1) / view_size
2700
2701        ren.SetWorldPoint(*self.p1, 1)
2702        ren.WorldToDisplay()
2703        disp_point2 = ren.GetDisplayPoint()[:2]
2704        disp_point2 = np.array(disp_point2) / view_size
2705
2706        self.SetPoint1(*disp_point1)
2707        self.SetPoint2(*disp_point2)
2708        self.distance = np.linalg.norm(self.p1 - self.p0)
2709        self.SetRange(0, self.distance)
2710        if not self.title:
2711            self.SetTitle(utils.precision(self.distance, 3))
2712
2713
2714#####################################################################
2715class DistanceTool(Group):
2716    """
2717    Create a tool to measure the distance between two clicked points.
2718    """
2719
2720    def __init__(self, plotter=None, c="k", lw=2):
2721        """
2722        Create a tool to measure the distance between two clicked points.
2723
2724        Example:
2725            ```python
2726            from vedo import *
2727            mesh = ParametricShape("RandomHills").c("red5")
2728            plt = Plotter(axes=1)
2729            dtool = DistanceTool()
2730            dtool.on()
2731            plt.show(mesh, dtool)
2732            dtool.off()
2733            ```
2734            ![](https://vedo.embl.es/images/feats/dist_tool.png)
2735        """
2736        Group.__init__(self)
2737
2738        self.p0 = [0, 0, 0]
2739        self.p1 = [0, 0, 0]
2740        self.distance = 0
2741        if plotter is None:
2742            plotter = vedo.plotter_instance
2743        self.plotter = plotter
2744        self.callback = None
2745        self.cid = None
2746        self.color = c
2747        self.linewidth = lw
2748        self.toggle = True
2749        self.ruler = None
2750        self.title = ""
2751
2752    def on(self):
2753        """Switch tool on."""
2754        self.cid = self.plotter.add_callback("click", self._onclick)
2755        self.VisibilityOn()
2756        self.plotter.render()
2757        return self
2758
2759    def off(self):
2760        """Switch tool off."""
2761        self.plotter.remove_callback(self.cid)
2762        self.VisibilityOff()
2763        self.ruler.off()
2764        self.plotter.render()
2765
2766    def _onclick(self, event):
2767        if not event.actor:
2768            return
2769
2770        self.clear()
2771
2772        acts = []
2773        if self.toggle:
2774            self.p0 = event.picked3d
2775            acts.append(Point(self.p0, c=self.color))
2776        else:
2777            self.p1 = event.picked3d
2778            self.distance = np.linalg.norm(self.p1 - self.p0)
2779            acts.append(Point(self.p0, c=self.color))
2780            acts.append(Point(self.p1, c=self.color))
2781            self.ruler = Ruler2D(c=self.color)
2782            self.ruler.set_points(self.p0, self.p1)
2783            acts.append(self.ruler)
2784
2785            if self.callback is not None:
2786                self.callback(event)
2787
2788        self += acts
2789        self.toggle = not self.toggle
2790
2791
2792#####################################################################
2793def Axes(
2794        obj=None,
2795        xtitle='x', ytitle='y', ztitle='z',
2796        xrange=None, yrange=None, zrange=None,
2797        c=None,
2798        number_of_divisions=None,
2799        digits=None,
2800        limit_ratio=0.04,
2801        title_depth=0,
2802        title_font="", # grab settings.default_font
2803        text_scale=1.0,
2804        x_values_and_labels=None, y_values_and_labels=None, z_values_and_labels=None,
2805        htitle="",
2806        htitle_size=0.03,
2807        htitle_font=None,
2808        htitle_italic=False,
2809        htitle_color=None,
2810        htitle_justify='bottom-center',
2811        htitle_rotation=0,
2812        htitle_offset=(0, 0.01, 0),
2813        xtitle_position=0.95, ytitle_position=0.95, ztitle_position=0.95,
2814        xtitle_offset=0.025,  ytitle_offset=0.0275, ztitle_offset=0.02, # can be a list (dx,dy,dz)
2815        xtitle_justify=None,  ytitle_justify=None,  ztitle_justify=None,
2816        xtitle_rotation=0, ytitle_rotation=0, ztitle_rotation=0,         # can be a list (rx,ry,rz)
2817        xtitle_box=False,  ytitle_box=False,
2818        xtitle_size=0.025, ytitle_size=0.025, ztitle_size=0.025,
2819        xtitle_color=None, ytitle_color=None, ztitle_color=None,
2820        xtitle_backface_color=None, ytitle_backface_color=None, ztitle_backface_color=None,
2821        xtitle_italic=0, ytitle_italic=0, ztitle_italic=0,
2822        grid_linewidth=1,
2823        xygrid=True,   yzgrid=False,  zxgrid=False,
2824        xygrid2=False, yzgrid2=False, zxgrid2=False,
2825        xygrid_transparent=False,  yzgrid_transparent=False,  zxgrid_transparent=False,
2826        xygrid2_transparent=False, yzgrid2_transparent=False, zxgrid2_transparent=False,
2827        xyplane_color=None, yzplane_color=None, zxplane_color=None,
2828        xygrid_color=None, yzgrid_color=None, zxgrid_color=None,
2829        xyalpha=0.075, yzalpha=0.075, zxalpha=0.075,
2830        xyframe_line=None, yzframe_line=None, zxframe_line=None,
2831        xyframe_color=None, yzframe_color=None, zxframe_color=None,
2832        axes_linewidth=1,
2833        xline_color=None, yline_color=None, zline_color=None,
2834        xhighlight_zero=False, yhighlight_zero=False, zhighlight_zero=False,
2835        xhighlight_zero_color='red4', yhighlight_zero_color='green4', zhighlight_zero_color='blue4',
2836        show_ticks=True,
2837        xtick_length=0.015, ytick_length=0.015, ztick_length=0.015,
2838        xtick_thickness=0.0025, ytick_thickness=0.0025, ztick_thickness=0.0025,
2839        xminor_ticks=1, yminor_ticks=1, zminor_ticks=1,
2840        tip_size=None,
2841        label_font="", # grab settings.default_font
2842        xlabel_color=None, ylabel_color=None, zlabel_color=None,
2843        xlabel_size=0.016, ylabel_size=0.016, zlabel_size=0.016,
2844        xlabel_offset=0.8, ylabel_offset=0.8, zlabel_offset=0.8, # each can be a list (dx,dy,dz)
2845        xlabel_justify=None, ylabel_justify=None, zlabel_justify=None,
2846        xlabel_rotation=0, ylabel_rotation=0, zlabel_rotation=0, # each can be a list (rx,ry,rz)
2847        xaxis_rotation=0, yaxis_rotation=0, zaxis_rotation=0,    # rotate all elements around axis
2848        xyshift=0, yzshift=0, zxshift=0,
2849        xshift_along_y=0, xshift_along_z=0,
2850        yshift_along_x=0, yshift_along_z=0,
2851        zshift_along_x=0, zshift_along_y=0,
2852        x_use_bounds=True, y_use_bounds=True, z_use_bounds=False,
2853        x_inverted=False, y_inverted=False, z_inverted=False,
2854        use_global=False,
2855        tol=0.001,
2856        **options,
2857    ):
2858    """
2859    Draw axes for the input object.
2860    Check [available fonts here](https://vedo.embl.es/fonts).
2861
2862    Returns an `vedo.Assembly` object.
2863
2864    Parameters
2865    ----------
2866
2867    - `xtitle`,                  ['x'], x-axis title text
2868    - `xrange`,                 [None], x-axis range in format (xmin, ymin), default is automatic.
2869    - `number_of_divisions`,    [None], approximate number of divisions on the longest axis
2870    - `axes_linewidth`,           [1], width of the axes lines
2871    - `grid_linewidth`,           [1], width of the grid lines
2872    - `title_depth`,              [0], extrusion fractional depth of title text
2873    - `x_values_and_labels`        [], assign custom tick positions and labels [(pos1, label1), ...]
2874    - `xygrid`,                [True], show a gridded wall on plane xy
2875    - `yzgrid`,                [True], show a gridded wall on plane yz
2876    - `zxgrid`,                [True], show a gridded wall on plane zx
2877    - `zxgrid2`,              [False], show zx plane on opposite side of the bounding box
2878    - `xygrid_transparent`    [False], make grid plane completely transparent
2879    - `xygrid2_transparent`   [False], make grid plane completely transparent on opposite side box
2880    - `xyplane_color`,       ['None'], color of the plane
2881    - `xygrid_color`,        ['None'], grid line color
2882    - `xyalpha`,               [0.15], grid plane opacity
2883    - `xyframe_line`,             [0], add a frame for the plane, use value as the thickness
2884    - `xyframe_color`,         [None], color for the frame of the plane
2885    - `show_ticks`,            [True], show major ticks
2886    - `digits`,                [None], use this number of significant digits in scientific notation
2887    - `title_font`,              [''], font for axes titles
2888    - `label_font`,              [''], font for numeric labels
2889    - `text_scale`,             [1.0], global scaling factor for all text elements (titles, labels)
2890    - `htitle`,                  [''], header title
2891    - `htitle_size`,           [0.03], header title size
2892    - `htitle_font`,           [None], header font (defaults to `title_font`)
2893    - `htitle_italic`,         [True], header font is italic
2894    - `htitle_color`,          [None], header title color (defaults to `xtitle_color`)
2895    - `htitle_justify`, ['bottom-center'], origin of the title justification
2896    - `htitle_offset`,   [(0,0.01,0)], control offsets of header title in x, y and z
2897    - `xtitle_position`,       [0.32], title fractional positions along axis
2898    - `xtitle_offset`,         [0.05], title fractional offset distance from axis line, can be a list
2899    - `xtitle_justify`,        [None], choose the origin of the bounding box of title
2900    - `xtitle_rotation`,          [0], add a rotation of the axis title, can be a list (rx,ry,rz)
2901    - `xtitle_box`,           [False], add a box around title text
2902    - `xline_color`,      [automatic], color of the x-axis
2903    - `xtitle_color`,     [automatic], color of the axis title
2904    - `xtitle_backface_color`, [None], color of axis title on its backface
2905    - `xtitle_size`,          [0.025], size of the axis title
2906    - `xtitle_italic`,            [0], a bool or float to make the font italic
2907    - `xhighlight_zero`,       [True], draw a line highlighting zero position if in range
2908    - `xhighlight_zero_color`, [autom], color of the line highlighting the zero position
2909    - `xtick_length`,         [0.005], radius of the major ticks
2910    - `xtick_thickness`,     [0.0025], thickness of the major ticks along their axis
2911    - `xminor_ticks`,             [1], number of minor ticks between two major ticks
2912    - `xlabel_color`,     [automatic], color of numeric labels and ticks
2913    - `xLabelPrecision`,          [2], nr. of significative digits to be shown
2914    - `xlabel_size`,          [0.015], size of the numeric labels along axis
2915    - `xlabel_rotation`,          [0], numeric labels rotation (can be a list of 3 rotations)
2916    - `xlabel_offset`,          [0.8], offset of the numeric labels (can be a list of 3 offsets)
2917    - `xlabel_justify`,        [None], choose the origin of the bounding box of labels
2918    - `xaxis_rotation`,           [0], rotate the X axis elements (ticks and labels) around this same axis
2919    - `xyshift`                 [0.0], slide the xy-plane along z (the range is [0,1])
2920    - `xshift_along_y`           [0.0], slide x-axis along the y-axis (the range is [0,1])
2921    - `tip_size`,               [0.01], size of the arrow tip
2922    - `limit_ratio`,            [0.04], below this ratio don't plot smaller axis
2923    - `x_use_bounds`,           [True], keep into account space occupied by labels when setting camera
2924    - `x_inverted`,            [False], invert labels order and direction (only visually!)
2925    - `use_global`,             [False], try to compute the global bounding box of visible actors
2926
2927    Example:
2928        ```python
2929        from vedo import Axes, Box, show
2930        box = Box(pos=(1,2,3), length=8, width=9, height=7).alpha(0.1)
2931        axs = Axes(box, c='k')  # returns an Assembly object
2932        for a in axs.unpack():
2933            print(a.name)
2934        show(box, axs).close()
2935        ```
2936        ![](https://vedo.embl.es/images/feaxes1ats/axes1.png)
2937
2938    Examples:
2939        - [custom_axes1.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes1.py)
2940        - [custom_axes2.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes2.py)
2941        - [custom_axes3.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes3.py)
2942        - [custom_axes4.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes4.py)
2943
2944        ![](https://vedo.embl.es/images/pyplot/customAxes3.png)
2945    """
2946    # make sure options are correct and user passed snake_case instead of camelCase
2947    if len(options):
2948        for k in options:
2949            if k.lower() == k:
2950                vedo.logger.warning(f"Unrecognised keyword '{k}' is ignored")
2951            else:
2952                vedo.logger.warning(f"Unrecognised keyword '{k}'. Please use snake_case notation")
2953
2954    if not title_font:
2955        title_font = settings.default_font
2956    if not label_font:
2957        label_font = settings.default_font
2958
2959    if c is None:  # automatic black or white
2960        c = (0.1, 0.1, 0.1)
2961        plt = vedo.plotter_instance
2962        if plt and plt.renderer:
2963            bgcol = plt.renderer.GetBackground()
2964        else:
2965            bgcol = (1, 1, 1)
2966        if np.sum(bgcol) < 1.5:
2967            c = (0.9, 0.9, 0.9)
2968    else:
2969        c = get_color(c)
2970
2971    if use_global:
2972        vbb, drange, min_bns, max_bns = compute_visible_bounds()
2973    else:
2974        if obj is not None:
2975            vbb, drange, min_bns, max_bns = compute_visible_bounds(obj)
2976        else:
2977            vbb = np.zeros(6)
2978            drange = np.zeros(3)
2979            if zrange is None:
2980                zrange = (0, 0)
2981            if xrange is None or yrange is None:
2982                vedo.logger.error("in Axes() must specify axes ranges!")
2983                raise RuntimeError()
2984
2985    if xrange is not None:
2986        if xrange[1] < xrange[0]:
2987            x_inverted = True
2988            xrange = [xrange[1], xrange[0]]
2989        vbb[0], vbb[1] = xrange
2990        drange[0] = vbb[1] - vbb[0]
2991        min_bns = vbb
2992        max_bns = vbb
2993    if yrange is not None:
2994        if yrange[1] < yrange[0]:
2995            y_inverted = True
2996            yrange = [yrange[1], yrange[0]]
2997        vbb[2], vbb[3] = yrange
2998        drange[1] = vbb[3] - vbb[2]
2999        min_bns = vbb
3000        max_bns = vbb
3001    if zrange is not None:
3002        if zrange[1] < zrange[0]:
3003            z_inverted = True
3004            zrange = [zrange[1], zrange[0]]
3005        vbb[4], vbb[5] = zrange
3006        drange[2] = vbb[5] - vbb[4]
3007        min_bns = vbb
3008        max_bns = vbb
3009
3010    drangemax = max(drange)
3011    if not drangemax:
3012        return None
3013
3014    if drange[0] / drangemax < limit_ratio:
3015        drange[0] = 0
3016        xtitle = ""
3017    if drange[1] / drangemax < limit_ratio:
3018        drange[1] = 0
3019        ytitle = ""
3020    if drange[2] / drangemax < limit_ratio:
3021        drange[2] = 0
3022        ztitle = ""
3023
3024    x0, x1, y0, y1, z0, z1 = vbb
3025    dx, dy, dz = drange
3026
3027    gscale = np.sqrt(dx * dx + dy * dy + dz * dz) * 0.75
3028
3029    if not xyplane_color: xyplane_color = c
3030    if not yzplane_color: yzplane_color = c
3031    if not zxplane_color: zxplane_color = c
3032    if not xygrid_color:  xygrid_color = c
3033    if not yzgrid_color:  yzgrid_color = c
3034    if not zxgrid_color:  zxgrid_color = c
3035    if not xtitle_color:  xtitle_color = c
3036    if not ytitle_color:  ytitle_color = c
3037    if not ztitle_color:  ztitle_color = c
3038    if not xline_color:   xline_color = c
3039    if not yline_color:   yline_color = c
3040    if not zline_color:   zline_color = c
3041    if not xlabel_color:  xlabel_color = xline_color
3042    if not ylabel_color:  ylabel_color = yline_color
3043    if not zlabel_color:  zlabel_color = zline_color
3044
3045    # vtk version<9 dont like depthpeeling: force switching off grids
3046    if settings.use_depth_peeling and not utils.vtk_version_at_least(9):
3047        xygrid = False
3048        yzgrid = False
3049        zxgrid = False
3050        xygrid2 = False
3051        yzgrid2 = False
3052        zxgrid2 = False
3053
3054    if tip_size is None:
3055        tip_size = 0.005 * gscale
3056        if not ztitle:
3057            tip_size = 0  # switch off in xy 2d
3058
3059    ndiv = 4
3060    if not ztitle or not ytitle or not xtitle:  # make more default ticks if 2D
3061        ndiv = 6
3062        if not ztitle:
3063            if xyframe_line is None:
3064                xyframe_line = True
3065            if tip_size is None:
3066                tip_size = False
3067
3068    if utils.is_sequence(number_of_divisions):
3069        rx, ry, rz = number_of_divisions
3070    else:
3071        if not number_of_divisions:
3072            number_of_divisions = ndiv
3073
3074    rx, ry, rz = np.ceil(drange / drangemax * number_of_divisions).astype(int)
3075
3076    if xtitle:
3077        xticks_float, xticks_str = utils.make_ticks(x0, x1, rx, x_values_and_labels, digits)
3078        xticks_float = xticks_float * dx
3079        if x_inverted:
3080            xticks_float = np.flip(-(xticks_float - xticks_float[-1]))
3081            xticks_str = list(reversed(xticks_str))
3082            xticks_str[-1] = ""
3083            xhighlight_zero = False
3084    if ytitle:
3085        yticks_float, yticks_str = utils.make_ticks(y0, y1, ry, y_values_and_labels, digits)
3086        yticks_float = yticks_float * dy
3087        if y_inverted:
3088            yticks_float = np.flip(-(yticks_float - yticks_float[-1]))
3089            yticks_str = list(reversed(yticks_str))
3090            yticks_str[-1] = ""
3091            yhighlight_zero = False
3092    if ztitle:
3093        zticks_float, zticks_str = utils.make_ticks(z0, z1, rz, z_values_and_labels, digits)
3094        zticks_float = zticks_float * dz
3095        if z_inverted:
3096            zticks_float = np.flip(-(zticks_float - zticks_float[-1]))
3097            zticks_str = list(reversed(zticks_str))
3098            zticks_str[-1] = ""
3099            zhighlight_zero = False
3100
3101    ################################################ axes lines
3102    lines = []
3103    if xtitle:
3104        axlinex = shapes.Line([0,0,0], [dx,0,0], c=xline_color, lw=axes_linewidth)
3105        if xyshift: axlinex.shift(0,0,xyshift*dz)
3106        if zxshift: axlinex.shift(0,zxshift*dy,0)
3107        if xshift_along_y: axlinex.shift(0,xshift_along_y*dy,0)
3108        if xshift_along_z: axlinex.shift(0,0,xshift_along_z*dz)
3109        axlinex.name = 'xAxis'
3110        lines.append(axlinex)
3111    if ytitle:
3112        axliney = shapes.Line([0,0,0], [0,dy,0], c=yline_color, lw=axes_linewidth)
3113        if xyshift: axliney.shift(0,0,xyshift*dz)
3114        if yzshift: axliney.shift(yzshift*dx,0,0)
3115        if yshift_along_x: axliney.shift(yshift_along_x*dx,0,0)
3116        if yshift_along_z: axliney.shift(0,0,yshift_along_z*dz)
3117        axliney.name = 'yAxis'
3118        lines.append(axliney)
3119    if ztitle:
3120        axlinez = shapes.Line([0,0,0], [0,0,dz], c=zline_color, lw=axes_linewidth)
3121        if yzshift: axlinez.shift(yzshift*dx,0,0)
3122        if zxshift: axlinez.shift(0,zxshift*dy,0)
3123        if zshift_along_x: axlinez.shift(zshift_along_x*dx,0,0)
3124        if zshift_along_y: axlinez.shift(0,zshift_along_y*dy,0)
3125        axlinez.name = 'zAxis'
3126        lines.append(axlinez)
3127
3128    ################################################ grid planes
3129    # all shapes have a name to keep track of them in the Assembly
3130    # if user wants to unpack it
3131    grids = []
3132    if xygrid and xtitle and ytitle:
3133        if not xygrid_transparent:
3134            gxy = shapes.Grid(s=(xticks_float, yticks_float))
3135            gxy.alpha(xyalpha).c(xyplane_color).lw(0)
3136            if xyshift: gxy.shift(0,0,xyshift*dz)
3137            elif tol:   gxy.shift(0,0,-tol*gscale)
3138            gxy.name = "xyGrid"
3139            grids.append(gxy)
3140        if grid_linewidth:
3141            gxy_lines = shapes.Grid(s=(xticks_float, yticks_float))
3142            gxy_lines.c(xyplane_color).lw(grid_linewidth).alpha(xyalpha)
3143            if xyshift: gxy_lines.shift(0,0,xyshift*dz)
3144            elif tol:   gxy_lines.shift(0,0,-tol*gscale)
3145            gxy_lines.name = "xyGridLines"
3146            grids.append(gxy_lines)
3147
3148    if yzgrid and ytitle and ztitle:
3149        if not yzgrid_transparent:
3150            gyz = shapes.Grid(s=(zticks_float, yticks_float))
3151            gyz.alpha(yzalpha).c(yzplane_color).lw(0).RotateY(-90)
3152            if yzshift: gyz.shift(yzshift*dx,0,0)
3153            elif tol:   gyz.shift(-tol*gscale,0,0)
3154            gyz.name = "yzGrid"
3155            grids.append(gyz)
3156        if grid_linewidth:
3157            gyz_lines = shapes.Grid(s=(zticks_float, yticks_float))
3158            gyz_lines.c(yzplane_color).lw(grid_linewidth).alpha(yzalpha).RotateY(-90)
3159            if yzshift: gyz_lines.shift(yzshift*dx,0,0)
3160            elif tol:   gyz_lines.shift(-tol*gscale,0,0)
3161            gyz_lines.name = "yzGridLines"
3162            grids.append(gyz_lines)
3163
3164    if zxgrid and ztitle and xtitle:
3165        if not zxgrid_transparent:
3166            gzx = shapes.Grid(s=(xticks_float, zticks_float))
3167            gzx.alpha(zxalpha).c(zxplane_color).lw(0).RotateX(90)
3168            if zxshift: gzx.shift(0,zxshift*dy,0)
3169            elif tol:   gzx.shift(0,-tol*gscale,0)
3170            gzx.name = "zxGrid"
3171            grids.append(gzx)
3172        if grid_linewidth:
3173            gzx_lines = shapes.Grid(s=(xticks_float, zticks_float))
3174            gzx_lines.c(zxplane_color).lw(grid_linewidth).alpha(zxalpha).RotateX(90)
3175            if zxshift: gzx_lines.shift(0,zxshift*dy,0)
3176            elif tol:   gzx_lines.shift(0,-tol*gscale,0)
3177            gzx_lines.name = "zxGridLines"
3178            grids.append(gzx_lines)
3179
3180    # Grid2
3181    if xygrid2 and xtitle and ytitle:
3182        if not xygrid2_transparent:
3183            gxy2 = shapes.Grid(s=(xticks_float, yticks_float)).z(dz)
3184            gxy2.alpha(xyalpha).c(xyplane_color).lw(0)
3185            if tol: gxy2.shift(0,tol*gscale,0)
3186            gxy2.name = "xyGrid2"
3187            grids.append(gxy2)
3188        if grid_linewidth:
3189            gxy2_lines = shapes.Grid(s=(xticks_float, yticks_float)).z(dz)
3190            gxy2_lines.c(xyplane_color).lw(grid_linewidth).alpha(xyalpha)
3191            if tol: gxy2_lines.shift(0,tol*gscale,0)
3192            gxy2_lines.name = "xygrid2Lines"
3193            grids.append(gxy2_lines)
3194
3195    if yzgrid2 and ytitle and ztitle:
3196        if not yzgrid2_transparent:
3197            gyz2 = shapes.Grid(s=(zticks_float, yticks_float)).x(dx)
3198            gyz2.alpha(yzalpha).c(yzplane_color).lw(0).RotateY(-90)
3199            if tol: gyz2.shift(tol*gscale,0,0)
3200            gyz2.name = "yzGrid2"
3201            grids.append(gyz2)
3202        if grid_linewidth:
3203            gyz2_lines = shapes.Grid(s=(zticks_float, yticks_float)).x(dx)
3204            gyz2_lines.c(yzplane_color).lw(grid_linewidth).alpha(yzalpha).RotateY(-90)
3205            if tol: gyz2_lines.shift(tol*gscale,0,0)
3206            gyz2_lines.name = "yzGrid2Lines"
3207            grids.append(gyz2_lines)
3208
3209    if zxgrid2 and ztitle and xtitle:
3210        if not zxgrid2_transparent:
3211            gzx2 = shapes.Grid(s=(xticks_float, zticks_float)).y(dy)
3212            gzx2.alpha(zxalpha).c(zxplane_color).lw(0).RotateX(90)
3213            if tol: gzx2.shift(0,tol*gscale,0)
3214            gzx2.name = "zxGrid2"
3215            grids.append(gzx2)
3216        if grid_linewidth:
3217            gzx2_lines = shapes.Grid(s=(xticks_float, zticks_float)).y(dy)
3218            gzx2_lines.c(zxplane_color).lw(grid_linewidth).alpha(zxalpha).RotateX(90)
3219            if tol: gzx2_lines.shift(0,tol*gscale,0)
3220            gzx2_lines.name = "zxGrid2Lines"
3221            grids.append(gzx2_lines)
3222
3223    ################################################ frame lines
3224    framelines = []
3225    if xyframe_line and xtitle and ytitle:
3226        if not xyframe_color:
3227            xyframe_color = xygrid_color
3228        frxy = shapes.Line([[0,dy,0],[dx,dy,0],[dx,0,0],[0,0,0],[0,dy,0]],
3229                           c=xyframe_color, lw=xyframe_line)
3230        if xyshift: frxy.shift(0,0,xyshift*dz)
3231        frxy.name = 'xyFrameLine'
3232        framelines.append(frxy)
3233    if yzframe_line and ytitle and ztitle:
3234        if not yzframe_color:
3235            yzframe_color = yzgrid_color
3236        fryz = shapes.Line([[0,0,dz],[0,dy,dz],[0,dy,0],[0,0,0],[0,0,dz]],
3237                           c=yzframe_color, lw=yzframe_line)
3238        if yzshift: fryz.shift(yzshift*dx,0,0)
3239        fryz.name = 'yzFrameLine'
3240        framelines.append(fryz)
3241    if zxframe_line and ztitle and xtitle:
3242        if not zxframe_color:
3243            zxframe_color = zxgrid_color
3244        frzx = shapes.Line([[0,0,dz],[dx,0,dz],[dx,0,0],[0,0,0],[0,0,dz]],
3245                           c=zxframe_color, lw=zxframe_line)
3246        if zxshift: frzx.shift(0,zxshift*dy,0)
3247        frzx.name = 'zxFrameLine'
3248        framelines.append(frzx)
3249
3250    ################################################ zero lines highlights
3251    highlights = []
3252    if xygrid and xtitle and ytitle:
3253        if xhighlight_zero and min_bns[0] <= 0 and max_bns[1] > 0:
3254            xhl = -min_bns[0]
3255            hxy = shapes.Line([xhl,0,0], [xhl,dy,0], c=xhighlight_zero_color)
3256            hxy.alpha(np.sqrt(xyalpha)).lw(grid_linewidth*2)
3257            if xyshift: hxy.shift(0,0,xyshift*dz)
3258            hxy.name = "xyHighlightZero"
3259            highlights.append(hxy)
3260        if yhighlight_zero and min_bns[2] <= 0 and max_bns[3] > 0:
3261            yhl = -min_bns[2]
3262            hyx = shapes.Line([0,yhl,0], [dx,yhl,0], c=yhighlight_zero_color)
3263            hyx.alpha(np.sqrt(yzalpha)).lw(grid_linewidth*2)
3264            if xyshift: hyx.shift(0,0,xyshift*dz)
3265            hyx.name = "yxHighlightZero"
3266            highlights.append(hyx)
3267
3268    if yzgrid and ytitle and ztitle:
3269        if yhighlight_zero and min_bns[2] <= 0 and max_bns[3] > 0:
3270            yhl = -min_bns[2]
3271            hyz = shapes.Line([0,yhl,0], [0,yhl,dz], c=yhighlight_zero_color)
3272            hyz.alpha(np.sqrt(yzalpha)).lw(grid_linewidth*2)
3273            if yzshift: hyz.shift(yzshift*dx,0,0)
3274            hyz.name = "yzHighlightZero"
3275            highlights.append(hyz)
3276        if zhighlight_zero and min_bns[4] <= 0 and max_bns[5] > 0:
3277            zhl = -min_bns[4]
3278            hzy = shapes.Line([0,0,zhl], [0,dy,zhl], c=zhighlight_zero_color)
3279            hzy.alpha(np.sqrt(yzalpha)).lw(grid_linewidth*2)
3280            if yzshift: hzy.shift(yzshift*dx,0,0)
3281            hzy.name = "zyHighlightZero"
3282            highlights.append(hzy)
3283
3284    if zxgrid and ztitle and xtitle:
3285        if zhighlight_zero and min_bns[4] <= 0 and max_bns[5] > 0:
3286            zhl = -min_bns[4]
3287            hzx = shapes.Line([0,0,zhl], [dx,0,zhl], c=zhighlight_zero_color)
3288            hzx.alpha(np.sqrt(zxalpha)).lw(grid_linewidth*2)
3289            if zxshift: hzx.shift(0,zxshift*dy,0)
3290            hzx.name = "zxHighlightZero"
3291            highlights.append(hzx)
3292        if xhighlight_zero and min_bns[0] <= 0 and max_bns[1] > 0:
3293            xhl = -min_bns[0]
3294            hxz = shapes.Line([xhl,0,0], [xhl,0,dz], c=xhighlight_zero_color)
3295            hxz.alpha(np.sqrt(zxalpha)).lw(grid_linewidth*2)
3296            if zxshift: hxz.shift(0,zxshift*dy,0)
3297            hxz.name = "xzHighlightZero"
3298            highlights.append(hxz)
3299
3300    ################################################ arrow cone
3301    cones = []
3302
3303    if tip_size:
3304
3305        if xtitle:
3306            if x_inverted:
3307                cx = shapes.Cone(
3308                    r=tip_size, height=tip_size * 2, axis=(-1, 0, 0), c=xline_color, res=12
3309                )
3310            else:
3311                cx = shapes.Cone((dx,0,0), r=tip_size, height=tip_size*2,
3312                                 axis=(1,0,0), c=xline_color, res=12)
3313            if xyshift: cx.shift(0,0,xyshift*dz)
3314            if zxshift: cx.shift(0,zxshift*dy,0)
3315            if xshift_along_y: cx.shift(0,xshift_along_y*dy,0)
3316            if xshift_along_z: cx.shift(0,0,xshift_along_z*dz)
3317            cx.name = "xTipCone"
3318            cones.append(cx)
3319
3320        if ytitle:
3321            if y_inverted:
3322                cy = shapes.Cone(r=tip_size, height=tip_size*2,
3323                                 axis=(0,-1,0), c=yline_color, res=12)
3324            else:
3325                cy = shapes.Cone((0,dy,0), r=tip_size, height=tip_size*2,
3326                                 axis=(0,1,0), c=yline_color, res=12)
3327            if xyshift: cy.shift(0,0,xyshift*dz)
3328            if yzshift: cy.shift(yzshift*dx,0,0)
3329            if yshift_along_x: cy.shift(yshift_along_x*dx,0,0)
3330            if yshift_along_z: cy.shift(0,0,yshift_along_z*dz)
3331            cy.name = "yTipCone"
3332            cones.append(cy)
3333
3334        if ztitle:
3335            if z_inverted:
3336                cz = shapes.Cone(r=tip_size, height=tip_size*2,
3337                                 axis=(0,0,-1), c=zline_color, res=12)
3338            else:
3339                cz = shapes.Cone((0,0,dz), r=tip_size, height=tip_size*2,
3340                                 axis=(0,0,1), c=zline_color, res=12)
3341            if yzshift: cz.shift(yzshift*dx,0,0)
3342            if zxshift: cz.shift(0,zxshift*dy,0)
3343            if zshift_along_x: cz.shift(zshift_along_x*dx,0,0)
3344            if zshift_along_y: cz.shift(0,zshift_along_y*dy,0)
3345            cz.name = "zTipCone"
3346            cones.append(cz)
3347
3348    ################################################################# MAJOR ticks
3349    majorticks, minorticks = [], []
3350    xticks, yticks, zticks = [], [], []
3351    if show_ticks:
3352        if xtitle:
3353            tickThickness = xtick_thickness * gscale / 2
3354            tickLength = xtick_length * gscale / 2
3355            for i in range(1, len(xticks_float) - 1):
3356                v1 = (xticks_float[i] - tickThickness, -tickLength, 0)
3357                v2 = (xticks_float[i] + tickThickness, tickLength, 0)
3358                xticks.append(shapes.Rectangle(v1, v2))
3359            if len(xticks) > 1:
3360                xmajticks = merge(xticks).c(xlabel_color)
3361                if xaxis_rotation:
3362                    xmajticks.RotateX(xaxis_rotation)
3363                if xyshift: xmajticks.shift(0,0,xyshift*dz)
3364                if zxshift: xmajticks.shift(0,zxshift*dy,0)
3365                if xshift_along_y: xmajticks.shift(0,xshift_along_y*dy,0)
3366                if xshift_along_z: xmajticks.shift(0,0,xshift_along_z*dz)
3367                xmajticks.name = "xMajorTicks"
3368                majorticks.append(xmajticks)
3369        if ytitle:
3370            tickThickness = ytick_thickness * gscale / 2
3371            tickLength = ytick_length * gscale / 2
3372            for i in range(1, len(yticks_float) - 1):
3373                v1 = (-tickLength, yticks_float[i] - tickThickness, 0)
3374                v2 = (tickLength, yticks_float[i] + tickThickness, 0)
3375                yticks.append(shapes.Rectangle(v1, v2))
3376            if len(yticks) > 1:
3377                ymajticks = merge(yticks).c(ylabel_color)
3378                if yaxis_rotation:
3379                    ymajticks.RotateY(yaxis_rotation)
3380                if xyshift: ymajticks.shift(0,0,xyshift*dz)
3381                if yzshift: ymajticks.shift(yzshift*dx,0,0)
3382                if yshift_along_x: ymajticks.shift(yshift_along_x*dx,0,0)
3383                if yshift_along_z: ymajticks.shift(0,0,yshift_along_z*dz)
3384                ymajticks.name = "yMajorTicks"
3385                majorticks.append(ymajticks)
3386        if ztitle:
3387            tickThickness = ztick_thickness * gscale / 2
3388            tickLength = ztick_length * gscale / 2.85
3389            for i in range(1, len(zticks_float) - 1):
3390                v1 = (zticks_float[i] - tickThickness, -tickLength, 0)
3391                v2 = (zticks_float[i] + tickThickness, tickLength, 0)
3392                zticks.append(shapes.Rectangle(v1, v2))
3393            if len(zticks) > 1:
3394                zmajticks = merge(zticks).c(zlabel_color)
3395                zmajticks.RotateZ(-45 + zaxis_rotation)
3396                zmajticks.RotateY(-90)
3397                if yzshift: zmajticks.shift(yzshift*dx,0,0)
3398                if zxshift: zmajticks.shift(0,zxshift*dy,0)
3399                if zshift_along_x: zmajticks.shift(zshift_along_x*dx,0,0)
3400                if zshift_along_y: zmajticks.shift(0,zshift_along_y*dy,0)
3401                zmajticks.name = "zMajorTicks"
3402                majorticks.append(zmajticks)
3403
3404        ############################################################# MINOR ticks
3405        if xtitle and xminor_ticks and len(xticks) > 1:
3406            tickThickness = xtick_thickness * gscale / 4
3407            tickLength = xtick_length * gscale / 4
3408            xminor_ticks += 1
3409            ticks = []
3410            for i in range(1, len(xticks)):
3411                t0, t1 = xticks[i - 1].pos(), xticks[i].pos()
3412                dt = t1 - t0
3413                for j in range(1, xminor_ticks):
3414                    mt = dt * (j / xminor_ticks) + t0
3415                    v1 = (mt[0] - tickThickness, -tickLength, 0)
3416                    v2 = (mt[0] + tickThickness, tickLength, 0)
3417                    ticks.append(shapes.Rectangle(v1, v2))
3418
3419            # finish off the fist lower range from start to first tick
3420            t0, t1 = xticks[0].pos(), xticks[1].pos()
3421            dt = t1 - t0
3422            for j in range(1, xminor_ticks):
3423                mt = t0 - dt * (j / xminor_ticks)
3424                if mt[0] < 0:
3425                    break
3426                v1 = (mt[0] - tickThickness, -tickLength, 0)
3427                v2 = (mt[0] + tickThickness, tickLength, 0)
3428                ticks.append(shapes.Rectangle(v1, v2))
3429
3430            # finish off the last upper range from last tick to end
3431            t0, t1 = xticks[-2].pos(), xticks[-1].pos()
3432            dt = t1 - t0
3433            for j in range(1, xminor_ticks):
3434                mt = t1 + dt * (j / xminor_ticks)
3435                if mt[0] > dx:
3436                    break
3437                v1 = (mt[0] - tickThickness, -tickLength, 0)
3438                v2 = (mt[0] + tickThickness, tickLength, 0)
3439                ticks.append(shapes.Rectangle(v1, v2))
3440
3441            if ticks:
3442                xminticks = merge(ticks).c(xlabel_color)
3443                if xaxis_rotation:
3444                    xminticks.RotateX(xaxis_rotation)
3445                if xyshift: xminticks.shift(0,0,xyshift*dz)
3446                if zxshift: xminticks.shift(0,zxshift*dy,0)
3447                if xshift_along_y: xminticks.shift(0,xshift_along_y*dy,0)
3448                if xshift_along_z: xminticks.shift(0,0,xshift_along_z*dz)
3449                xminticks.name = "xMinorTicks"
3450                minorticks.append(xminticks)
3451
3452        if ytitle and yminor_ticks and len(yticks) > 1:  ##### y
3453            tickThickness = ytick_thickness * gscale / 4
3454            tickLength = ytick_length * gscale / 4
3455            yminor_ticks += 1
3456            ticks = []
3457            for i in range(1, len(yticks)):
3458                t0, t1 = yticks[i - 1].pos(), yticks[i].pos()
3459                dt = t1 - t0
3460                for j in range(1, yminor_ticks):
3461                    mt = dt * (j / yminor_ticks) + t0
3462                    v1 = (-tickLength, mt[1] - tickThickness, 0)
3463                    v2 = (tickLength, mt[1] + tickThickness, 0)
3464                    ticks.append(shapes.Rectangle(v1, v2))
3465
3466            # finish off the fist lower range from start to first tick
3467            t0, t1 = yticks[0].pos(), yticks[1].pos()
3468            dt = t1 - t0
3469            for j in range(1, yminor_ticks):
3470                mt = t0 - dt * (j / yminor_ticks)
3471                if mt[1] < 0:
3472                    break
3473                v1 = (-tickLength, mt[1] - tickThickness, 0)
3474                v2 = (tickLength, mt[1] + tickThickness, 0)
3475                ticks.append(shapes.Rectangle(v1, v2))
3476
3477            # finish off the last upper range from last tick to end
3478            t0, t1 = yticks[-2].pos(), yticks[-1].pos()
3479            dt = t1 - t0
3480            for j in range(1, yminor_ticks):
3481                mt = t1 + dt * (j / yminor_ticks)
3482                if mt[1] > dy:
3483                    break
3484                v1 = (-tickLength, mt[1] - tickThickness, 0)
3485                v2 = (tickLength, mt[1] + tickThickness, 0)
3486                ticks.append(shapes.Rectangle(v1, v2))
3487
3488            if ticks:
3489                yminticks = merge(ticks).c(ylabel_color)
3490                if yaxis_rotation:
3491                    yminticks.RotateY(yaxis_rotation)
3492                if xyshift: yminticks.shift(0,0,xyshift*dz)
3493                if yzshift: yminticks.shift(yzshift*dx,0,0)
3494                if yshift_along_x: yminticks.shift(yshift_along_x*dx,0,0)
3495                if yshift_along_z: yminticks.shift(0,0,yshift_along_z*dz)
3496                yminticks.name = "yMinorTicks"
3497                minorticks.append(yminticks)
3498
3499        if ztitle and zminor_ticks and len(zticks) > 1:  ##### z
3500            tickThickness = ztick_thickness * gscale / 4
3501            tickLength = ztick_length * gscale / 5
3502            zminor_ticks += 1
3503            ticks = []
3504            for i in range(1, len(zticks)):
3505                t0, t1 = zticks[i - 1].pos(), zticks[i].pos()
3506                dt = t1 - t0
3507                for j in range(1, zminor_ticks):
3508                    mt = dt * (j / zminor_ticks) + t0
3509                    v1 = (mt[0] - tickThickness, -tickLength, 0)
3510                    v2 = (mt[0] + tickThickness, tickLength, 0)
3511                    ticks.append(shapes.Rectangle(v1, v2))
3512
3513            # finish off the fist lower range from start to first tick
3514            t0, t1 = zticks[0].pos(), zticks[1].pos()
3515            dt = t1 - t0
3516            for j in range(1, zminor_ticks):
3517                mt = t0 - dt * (j / zminor_ticks)
3518                if mt[0] < 0:
3519                    break
3520                v1 = (mt[0] - tickThickness, -tickLength, 0)
3521                v2 = (mt[0] + tickThickness, tickLength, 0)
3522                ticks.append(shapes.Rectangle(v1, v2))
3523
3524            # finish off the last upper range from last tick to end
3525            t0, t1 = zticks[-2].pos(), zticks[-1].pos()
3526            dt = t1 - t0
3527            for j in range(1, zminor_ticks):
3528                mt = t1 + dt * (j / zminor_ticks)
3529                if mt[0] > dz:
3530                    break
3531                v1 = (mt[0] - tickThickness, -tickLength, 0)
3532                v2 = (mt[0] + tickThickness, tickLength, 0)
3533                ticks.append(shapes.Rectangle(v1, v2))
3534
3535            if ticks:
3536                zminticks = merge(ticks).c(zlabel_color)
3537                zminticks.RotateZ(-45 + zaxis_rotation)
3538                zminticks.RotateY(-90)
3539                if yzshift: zminticks.shift(yzshift*dx,0,0)
3540                if zxshift: zminticks.shift(0,zxshift*dy,0)
3541                if zshift_along_x: zminticks.shift(zshift_along_x*dx,0,0)
3542                if zshift_along_y: zminticks.shift(0,zshift_along_y*dy,0)
3543                zminticks.name = "zMinorTicks"
3544                minorticks.append(zminticks)
3545
3546    ################################################ axes NUMERIC text labels
3547    labels = []
3548    xlab, ylab, zlab = None, None, None
3549
3550    if xlabel_size and xtitle:
3551
3552        xRot, yRot, zRot = 0, 0, 0
3553        if utils.is_sequence(xlabel_rotation):  # unpck 3 rotations
3554            zRot, xRot, yRot = xlabel_rotation
3555        else:
3556            zRot = xlabel_rotation
3557        if zRot < 0:  # deal with negative angles
3558            zRot += 360
3559
3560        jus = "center-top"
3561        if zRot:
3562            if zRot >  24: jus = "top-right"
3563            if zRot >  67: jus = "center-right"
3564            if zRot > 112: jus = "right-bottom"
3565            if zRot > 157: jus = "center-bottom"
3566            if zRot > 202: jus = "bottom-left"
3567            if zRot > 247: jus = "center-left"
3568            if zRot > 292: jus = "top-left"
3569            if zRot > 337: jus = "top-center"
3570        if xlabel_justify is not None:
3571            jus = xlabel_justify
3572
3573        for i in range(1, len(xticks_str)):
3574            t = xticks_str[i]
3575            if not t:
3576                continue
3577            if utils.is_sequence(xlabel_offset):
3578                xoffs, yoffs, zoffs = xlabel_offset
3579            else:
3580                xoffs, yoffs, zoffs = 0, xlabel_offset, 0
3581            xlab = shapes.Text3D(
3582                t, s=xlabel_size * text_scale * gscale, font=label_font, justify=jus
3583            )
3584            tb = xlab.ybounds()  # must be ybounds: height of char
3585            v = (xticks_float[i], 0, 0)
3586            offs = -np.array([xoffs, yoffs, zoffs]) * (tb[1] - tb[0])
3587            xlab.pos(v + offs)
3588            if xaxis_rotation:
3589                xlab.rotate_x(xaxis_rotation)
3590            if zRot: xlab.RotateZ(zRot)
3591            if xRot: xlab.RotateX(xRot)
3592            if yRot: xlab.RotateY(yRot)
3593            if xyshift: xlab.shift(0,0,xyshift*dz)
3594            if zxshift: xlab.shift(0,zxshift*dy,0)
3595            if xshift_along_y: xlab.shift(0,xshift_along_y*dy,0)
3596            if xshift_along_z: xlab.shift(0,0,xshift_along_z*dz)
3597            xlab.name = f"xNumericLabel{i}"
3598            xlab.SetUseBounds(x_use_bounds)
3599            labels.append(xlab.c(xlabel_color))
3600
3601    if ylabel_size and ytitle:
3602
3603        xRot, yRot, zRot = 0, 0, 0
3604        if utils.is_sequence(ylabel_rotation):  # unpck 3 rotations
3605            zRot, yRot, xRot = ylabel_rotation
3606        else:
3607            zRot = ylabel_rotation
3608        if zRot < 0:
3609            zRot += 360  # deal with negative angles
3610
3611        jus = "center-right"
3612        if zRot:
3613            if zRot >  24: jus = "bottom-right"
3614            if zRot >  67: jus = "center-bottom"
3615            if zRot > 112: jus = "left-bottom"
3616            if zRot > 157: jus = "center-left"
3617            if zRot > 202: jus = "top-left"
3618            if zRot > 247: jus = "center-top"
3619            if zRot > 292: jus = "top-right"
3620            if zRot > 337: jus = "right-center"
3621        if ylabel_justify is not None:
3622            jus = ylabel_justify
3623
3624        for i in range(1, len(yticks_str)):
3625            t = yticks_str[i]
3626            if not t:
3627                continue
3628            if utils.is_sequence(ylabel_offset):
3629                xoffs, yoffs, zoffs = ylabel_offset
3630            else:
3631                xoffs, yoffs, zoffs = ylabel_offset, 0, 0
3632            ylab = shapes.Text3D(
3633                t, s=ylabel_size * text_scale * gscale, font=label_font, justify=jus
3634            )
3635            tb = ylab.ybounds()  # must be ybounds: height of char
3636            v = (0, yticks_float[i], 0)
3637            offs = -np.array([xoffs, yoffs, zoffs]) * (tb[1] - tb[0])
3638            ylab.pos(v + offs)
3639            if yaxis_rotation:
3640                ylab.rotate_y(yaxis_rotation)
3641            if zRot: ylab.RotateZ(zRot)
3642            if yRot: ylab.RotateY(yRot)
3643            if xRot: ylab.RotateX(xRot)
3644            if xyshift: ylab.shift(0,0,xyshift*dz)
3645            if yzshift: ylab.shift(yzshift*dx,0,0)
3646            if yshift_along_x: ylab.shift(yshift_along_x*dx,0,0)
3647            if yshift_along_z: ylab.shift(0,0,yshift_along_z*dz)
3648            ylab.name = f"yNumericLabel{i}"
3649            ylab.SetUseBounds(y_use_bounds)
3650            labels.append(ylab.c(ylabel_color))
3651
3652    if zlabel_size and ztitle:
3653
3654        xRot, yRot, zRot = 0, 0, 0
3655        if utils.is_sequence(zlabel_rotation):  # unpck 3 rotations
3656            xRot, yRot, zRot = zlabel_rotation
3657        else:
3658            xRot = zlabel_rotation
3659        if xRot < 0: xRot += 360 # deal with negative angles
3660
3661        jus = "center-right"
3662        if xRot:
3663            if xRot >  24: jus = "bottom-right"
3664            if xRot >  67: jus = "center-bottom"
3665            if xRot > 112: jus = "left-bottom"
3666            if xRot > 157: jus = "center-left"
3667            if xRot > 202: jus = "top-left"
3668            if xRot > 247: jus = "center-top"
3669            if xRot > 292: jus = "top-right"
3670            if xRot > 337: jus = "right-center"
3671        if zlabel_justify is not None:
3672            jus = zlabel_justify
3673
3674        for i in range(1, len(zticks_str)):
3675            t = zticks_str[i]
3676            if not t:
3677                continue
3678            if utils.is_sequence(zlabel_offset):
3679                xoffs, yoffs, zoffs = zlabel_offset
3680            else:
3681                xoffs, yoffs, zoffs = zlabel_offset, zlabel_offset, 0
3682            zlab = shapes.Text3D(
3683                t, s=zlabel_size * text_scale * gscale, font=label_font, justify=jus
3684            )
3685            tb = zlab.ybounds()  # must be ybounds: height of char
3686            v = (0, 0, zticks_float[i])
3687            offs = -np.array([xoffs, yoffs, zoffs]) * (tb[1] - tb[0]) / 1.5
3688            angle = 90
3689            if dx:
3690                angle = np.arctan2(dy, dx) * 57.3
3691            zlab.RotateZ(angle + yRot)  # vtk inverts order of rotations
3692            if xRot:
3693                zlab.RotateY(-xRot)  # ..second
3694            zlab.RotateX(90 + zRot)  # ..first
3695            zlab.pos(v + offs)
3696            if zaxis_rotation:
3697                zlab.rotate_z(zaxis_rotation)
3698            if yzshift: zlab.shift(yzshift*dx,0,0)
3699            if zxshift: zlab.shift(0,zxshift*dy,0)
3700            if zshift_along_x: zlab.shift(zshift_along_x*dx,0,0)
3701            if zshift_along_y: zlab.shift(0,zshift_along_y*dy,0)
3702            zlab.SetUseBounds(z_use_bounds)
3703            zlab.name = f"zNumericLabel{i}"
3704            labels.append(zlab.c(zlabel_color))
3705
3706    ################################################ axes titles
3707    titles = []
3708
3709    if xtitle:
3710
3711        xRot, yRot, zRot = 0, 0, 0
3712        if utils.is_sequence(xtitle_rotation):  # unpack 3 rotations
3713            zRot, xRot, yRot = xtitle_rotation
3714        else:
3715            zRot = xtitle_rotation
3716        if zRot < 0:  # deal with negative angles
3717            zRot += 360
3718
3719        if utils.is_sequence(xtitle_offset):
3720            xoffs, yoffs, zoffs = xtitle_offset
3721        else:
3722            xoffs, yoffs, zoffs = 0, xtitle_offset, 0
3723
3724        if xtitle_justify is not None:
3725            jus = xtitle_justify
3726        else:
3727            # find best justfication for given rotation(s)
3728            jus = "right-top"
3729            if zRot:
3730                if zRot >  24: jus = "center-right"
3731                if zRot >  67: jus = "right-bottom"
3732                if zRot > 157: jus = "bottom-left"
3733                if zRot > 202: jus = "center-left"
3734                if zRot > 247: jus = "top-left"
3735                if zRot > 337: jus = "top-right"
3736
3737        xt = shapes.Text3D(
3738            xtitle,
3739            s=xtitle_size * text_scale * gscale,
3740            font=title_font,
3741            c=xtitle_color,
3742            justify=jus,
3743            depth=title_depth,
3744            italic=xtitle_italic,
3745        )
3746        if xtitle_backface_color:
3747            xt.backcolor(xtitle_backface_color)
3748        if zRot:
3749            xt.RotateZ(zRot)
3750        if xRot:
3751            xt.RotateX(xRot)
3752        if yRot:
3753            xt.RotateY(yRot)
3754        shift = 0
3755        if xlab:  # xlab is the last created numeric text label..
3756            lt0, lt1 = xlab.GetBounds()[2:4]
3757            shift = lt1 - lt0
3758        xt.pos(
3759            [(xoffs + xtitle_position) * dx, -(yoffs + xtick_length / 2) * dy - shift, zoffs * dz]
3760        )
3761        if xaxis_rotation:
3762            xt.rotate_x(xaxis_rotation)
3763        if xyshift:
3764            xt.shift(0, 0, xyshift * dz)
3765        if xshift_along_y:
3766            xt.shift(0, xshift_along_y * dy, 0)
3767        if xshift_along_z:
3768            xt.shift(0, 0, xshift_along_z * dz)
3769        xt.SetUseBounds(x_use_bounds)
3770        if xtitle == " ":
3771            xt.SetUseBounds(False)
3772        xt.name = f"xtitle {xtitle}"
3773        titles.append(xt)
3774        if xtitle_box:
3775            titles.append(xt.box(scale=1.1).use_bounds(x_use_bounds))
3776
3777    if ytitle:
3778
3779        xRot, yRot, zRot = 0, 0, 0
3780        if utils.is_sequence(ytitle_rotation):  # unpck 3 rotations
3781            zRot, yRot, xRot = ytitle_rotation
3782        else:
3783            zRot = ytitle_rotation
3784            if len(ytitle) > 3:
3785                zRot += 90
3786                ytitle_position *= 0.975
3787        if zRot < 0:
3788            zRot += 360  # deal with negative angles
3789
3790        if utils.is_sequence(ytitle_offset):
3791            xoffs, yoffs, zoffs = ytitle_offset
3792        else:
3793            xoffs, yoffs, zoffs = ytitle_offset, 0, 0
3794
3795        if ytitle_justify is not None:
3796            jus = ytitle_justify
3797        else:
3798            jus = "center-right"
3799            if zRot:
3800                if zRot >  24: jus = "bottom-right"
3801                if zRot > 112: jus = "left-bottom"
3802                if zRot > 157: jus = "center-left"
3803                if zRot > 202: jus = "top-left"
3804                if zRot > 292: jus = "top-right"
3805                if zRot > 337: jus = "right-center"
3806
3807        yt = shapes.Text3D(
3808            ytitle,
3809            s=ytitle_size * text_scale * gscale,
3810            font=title_font,
3811            c=ytitle_color,
3812            justify=jus,
3813            depth=title_depth,
3814            italic=ytitle_italic,
3815        )
3816        if ytitle_backface_color:
3817            yt.backcolor(ytitle_backface_color)
3818
3819        if zRot: yt.RotateZ(zRot)
3820        if yRot: yt.RotateY(yRot)
3821        if xRot: yt.RotateX(xRot)
3822
3823        shift = 0
3824        if ylab:  # this is the last created num label..
3825            lt0, lt1 = ylab.GetBounds()[0:2]
3826            shift = lt1 - lt0
3827
3828        yt.pos(-(xoffs + ytick_length / 2) * dx - shift, (yoffs + ytitle_position) * dy, zoffs * dz)
3829        if yaxis_rotation:
3830            yt.rotate_y(yaxis_rotation)
3831        if xyshift:        yt.shift(0, 0, xyshift*dz)
3832        if yshift_along_x: yt.shift(yshift_along_x*dx, 0, 0)
3833        if yshift_along_z: yt.shift(0, 0, yshift_along_z*dz)
3834        yt.SetUseBounds(y_use_bounds)
3835        if ytitle == " ":
3836            yt.SetUseBounds(False)
3837        yt.name = f"ytitle {ytitle}"
3838        titles.append(yt)
3839        if ytitle_box:
3840            titles.append(yt.box(scale=1.1).use_bounds(y_use_bounds))
3841
3842    if ztitle:
3843
3844        xRot, yRot, zRot = 0, 0, 0
3845        if utils.is_sequence(ztitle_rotation):  # unpck 3 rotations
3846            xRot, yRot, zRot = ztitle_rotation
3847        else:
3848            xRot = ztitle_rotation
3849            if len(ztitle) > 3:
3850                xRot += 90
3851                ztitle_position *= 0.975
3852        if xRot < 0:
3853            xRot += 360  # deal with negative angles
3854
3855        if ztitle_justify is not None:
3856            jus = ztitle_justify
3857        else:
3858            jus = "center-right"
3859            if xRot:
3860                if xRot >  24: jus = "bottom-right"
3861                if xRot > 112: jus = "left-bottom"
3862                if xRot > 157: jus = "center-left"
3863                if xRot > 202: jus = "top-left"
3864                if xRot > 292: jus = "top-right"
3865                if xRot > 337: jus = "right-center"
3866
3867        zt = shapes.Text3D(
3868            ztitle,
3869            s=ztitle_size * text_scale * gscale,
3870            font=title_font,
3871            c=ztitle_color,
3872            justify=jus,
3873            depth=title_depth,
3874            italic=ztitle_italic,
3875        )
3876        if ztitle_backface_color:
3877            zt.backcolor(ztitle_backface_color)
3878
3879        angle = 90
3880        if dx:
3881            angle = np.arctan2(dy, dx) * 57.3
3882        zt.RotateZ(angle + yRot)  # vtk inverts order of rotations
3883        if xRot:
3884            zt.RotateY(-xRot)  # ..second
3885        zt.RotateX(90 + zRot)  # ..first
3886
3887        shift = 0
3888        if zlab:  # this is the last created one..
3889            lt0, lt1 = zlab.GetBounds()[0:2]
3890            shift = lt1 - lt0
3891        zt.pos(
3892            -(ztitle_offset + ztick_length / 5) * dx - shift,
3893            -(ztitle_offset + ztick_length / 5) * dy - shift,
3894            ztitle_position * dz,
3895        )
3896        if zaxis_rotation:
3897            zt.rotate_z(zaxis_rotation)
3898        if zxshift: zt.shift(0,zxshift*dy,0)
3899        if zshift_along_x: zt.shift(zshift_along_x*dx,0,0)
3900        if zshift_along_y: zt.shift(0,zshift_along_y*dy,0)
3901        zt.SetUseBounds(z_use_bounds)
3902        if ztitle == " ":
3903            zt.SetUseBounds(False)
3904        zt.name = f"ztitle {ztitle}"
3905        titles.append(zt)
3906
3907    ################################################### header title
3908    if htitle:
3909        if htitle_font is None:
3910            htitle_font = title_font
3911        if htitle_color is None:
3912            htitle_color = xtitle_color
3913        htit = shapes.Text3D(
3914            htitle,
3915            s=htitle_size * gscale * text_scale,
3916            font=htitle_font,
3917            c=htitle_color,
3918            justify=htitle_justify,
3919            depth=title_depth,
3920            italic=htitle_italic,
3921        )
3922        if htitle_rotation:
3923            htit.RotateX(htitle_rotation)
3924        wpos = [(0.5 + htitle_offset[0]) * dx, (1 + htitle_offset[1]) * dy, htitle_offset[2] * dz]
3925        htit.pos(wpos)
3926        if xyshift:
3927            htit.shift(0, 0, xyshift * dz)
3928        htit.name = f"htitle {htitle}"
3929        titles.append(htit)
3930
3931    ######
3932    acts = titles + lines + labels + grids + framelines
3933    acts += highlights + majorticks + minorticks + cones
3934    orig = (min_bns[0], min_bns[2], min_bns[4])
3935    for a in acts:
3936        a.PickableOff()
3937        a.AddPosition(orig)
3938        a.GetProperty().LightingOff()
3939    asse = Assembly(acts)
3940    asse.SetOrigin(orig)
3941    asse.PickableOff()
3942    asse.name = "Axes"
3943    return asse
3944
3945
3946def add_global_axes(axtype=None, c=None, bounds=()):
3947    """
3948    Draw axes on scene. Available axes types are
3949
3950    Parameters
3951    ----------
3952    axtype : (int)
3953        - 0,  no axes,
3954        - 1,  draw three gray grid walls
3955        - 2,  show cartesian axes from (0,0,0)
3956        - 3,  show positive range of cartesian axes from (0,0,0)
3957        - 4,  show a triad at bottom left
3958        - 5,  show a cube at bottom left
3959        - 6,  mark the corners of the bounding box
3960        - 7,  draw a 3D ruler at each side of the cartesian axes
3961        - 8,  show the `vtkCubeAxesActor` object
3962        - 9,  show the bounding box outLine
3963        - 10, show three circles representing the maximum bounding box
3964        - 11, show a large grid on the x-y plane (use with zoom=8)
3965        - 12, show polar axes
3966        - 13, draw a simple ruler at the bottom of the window
3967        - 14, show the vtk default `vtkCameraOrientationWidget` object
3968
3969    Axis type-1 can be fully customized by passing a dictionary `axes=dict()`,
3970    see `vedo.Axes` for the complete list of options.
3971
3972    Example
3973    -------
3974        .. code-block:: python
3975
3976            from vedo import Box, show
3977            b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1)
3978            show(
3979                b,
3980                axes={
3981                    "xtitle": "Some long variable [a.u.]",
3982                    "number_of_divisions": 4,
3983                    # ...
3984                },
3985            )
3986    """
3987    plt = vedo.plotter_instance
3988    if axtype is not None:
3989        plt.axes = axtype  # override
3990
3991    r = plt.renderers.index(plt.renderer)
3992
3993    if not plt.axes:
3994        return
3995
3996    if c is None:  # automatic black or white
3997        c = (0.9, 0.9, 0.9)
3998        if np.sum(plt.renderer.GetBackground()) > 1.5:
3999            c = (0.1, 0.1, 0.1)
4000    else:
4001        c = get_color(c)  # for speed
4002
4003    if not plt.renderer:
4004        return None
4005
4006    if plt.axes_instances[r]:
4007        return None
4008
4009    ############################################################
4010    # custom grid walls
4011    if plt.axes == 1 or plt.axes is True or isinstance(plt.axes, dict):
4012
4013        if len(bounds) == 6:
4014            bnds = bounds
4015            xrange = (bnds[0], bnds[1])
4016            yrange = (bnds[2], bnds[3])
4017            zrange = (bnds[4], bnds[5])
4018        else:
4019            xrange=None
4020            yrange=None
4021            zrange=None
4022
4023        if isinstance(plt.axes, dict):
4024            plt.axes.update({"use_global": True})
4025            # protect from invalid camelCase options from vedo<=2.3
4026            for k in plt.axes:
4027                if k.lower() != k:
4028                    return
4029            if "xrange" in plt.axes:
4030                xrange = plt.axes.pop("xrange")
4031            if "yrange" in plt.axes:
4032                yrange = plt.axes.pop("yrange")
4033            if "zrange" in plt.axes:
4034                zrange = plt.axes.pop("zrange")
4035            asse = Axes(**plt.axes, xrange=xrange, yrange=yrange, zrange=zrange)
4036        else:
4037            asse = Axes(xrange=xrange, yrange=yrange, zrange=zrange)
4038
4039        plt.renderer.AddActor(asse)
4040        plt.axes_instances[r] = asse
4041
4042    elif plt.axes in (2, 3):
4043        x0, x1, y0, y1, z0, z1 = plt.renderer.ComputeVisiblePropBounds()
4044        xcol, ycol, zcol = "dr", "dg", "db"
4045        s = 1
4046        alpha = 1
4047        centered = False
4048        dx, dy, dz = x1 - x0, y1 - y0, z1 - z0
4049        aves = np.sqrt(dx * dx + dy * dy + dz * dz) / 2
4050        x0, x1 = min(x0, 0), max(x1, 0)
4051        y0, y1 = min(y0, 0), max(y1, 0)
4052        z0, z1 = min(z0, 0), max(z1, 0)
4053
4054        if plt.axes == 3:
4055            if x1 > 0:
4056                x0 = 0
4057            if y1 > 0:
4058                y0 = 0
4059            if z1 > 0:
4060                z0 = 0
4061
4062        dx, dy, dz = x1 - x0, y1 - y0, z1 - z0
4063        acts = []
4064        if x0 * x1 <= 0 or y0 * z1 <= 0 or z0 * z1 <= 0:  # some ranges contain origin
4065            zero = shapes.Sphere(r=aves / 120 * s, c="k", alpha=alpha, res=10)
4066            acts += [zero]
4067
4068        if dx > aves / 100:
4069            xl = shapes.Cylinder([[x0, 0, 0], [x1, 0, 0]], r=aves / 250 * s, c=xcol, alpha=alpha)
4070            xc = shapes.Cone(
4071                pos=[x1, 0, 0],
4072                c=xcol,
4073                alpha=alpha,
4074                r=aves / 100 * s,
4075                height=aves / 25 * s,
4076                axis=[1, 0, 0],
4077                res=10,
4078            )
4079            wpos = [x1, -aves / 25 * s, 0]  # aligned to arrow tip
4080            if centered:
4081                wpos = [(x0 + x1) / 2, -aves / 25 * s, 0]
4082            xt = shapes.Text3D("x", pos=wpos, s=aves / 40 * s, c=xcol)
4083            acts += [xl, xc, xt]
4084
4085        if dy > aves / 100:
4086            yl = shapes.Cylinder([[0, y0, 0], [0, y1, 0]], r=aves / 250 * s, c=ycol, alpha=alpha)
4087            yc = shapes.Cone(
4088                pos=[0, y1, 0],
4089                c=ycol,
4090                alpha=alpha,
4091                r=aves / 100 * s,
4092                height=aves / 25 * s,
4093                axis=[0, 1, 0],
4094                res=10,
4095            )
4096            wpos = [-aves / 40 * s, y1, 0]
4097            if centered:
4098                wpos = [-aves / 40 * s, (y0 + y1) / 2, 0]
4099            yt = shapes.Text3D("y", pos=(0, 0, 0), s=aves / 40 * s, c=ycol)
4100            yt.pos(wpos).RotateZ(90)
4101            acts += [yl, yc, yt]
4102
4103        if dz > aves / 100:
4104            zl = shapes.Cylinder([[0, 0, z0], [0, 0, z1]], r=aves / 250 * s, c=zcol, alpha=alpha)
4105            zc = shapes.Cone(
4106                pos=[0, 0, z1],
4107                c=zcol,
4108                alpha=alpha,
4109                r=aves / 100 * s,
4110                height=aves / 25 * s,
4111                axis=[0, 0, 1],
4112                res=10,
4113            )
4114            wpos = [-aves / 50 * s, -aves / 50 * s, z1]
4115            if centered:
4116                wpos = [-aves / 50 * s, -aves / 50 * s, (z0 + z1) / 2]
4117            zt = shapes.Text3D("z", pos=(0, 0, 0), s=aves / 40 * s, c=zcol)
4118            zt.pos(wpos).RotateZ(45)
4119            zt.RotateX(90)
4120            acts += [zl, zc, zt]
4121        for a in acts:
4122            a.PickableOff()
4123        ass = Assembly(acts)
4124        ass.PickableOff()
4125        plt.renderer.AddActor(ass)
4126        plt.axes_instances[r] = ass
4127
4128    elif plt.axes == 4:
4129        axact = vtk.vtkAxesActor()
4130        axact.SetShaftTypeToCylinder()
4131        axact.SetCylinderRadius(0.03)
4132        axact.SetXAxisLabelText("x")
4133        axact.SetYAxisLabelText("y")
4134        axact.SetZAxisLabelText("z")
4135        axact.GetXAxisShaftProperty().SetColor(1, 0, 0)
4136        axact.GetYAxisShaftProperty().SetColor(0, 1, 0)
4137        axact.GetZAxisShaftProperty().SetColor(0, 0, 1)
4138        axact.GetXAxisTipProperty().SetColor(1, 0, 0)
4139        axact.GetYAxisTipProperty().SetColor(0, 1, 0)
4140        axact.GetZAxisTipProperty().SetColor(0, 0, 1)
4141        bc = np.array(plt.renderer.GetBackground())
4142        if np.sum(bc) < 1.5:
4143            lc = (1, 1, 1)
4144        else:
4145            lc = (0, 0, 0)
4146        axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().BoldOff()
4147        axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().BoldOff()
4148        axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().BoldOff()
4149        axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().ItalicOff()
4150        axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().ItalicOff()
4151        axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().ItalicOff()
4152        axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().ShadowOff()
4153        axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().ShadowOff()
4154        axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().ShadowOff()
4155        axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().SetColor(lc)
4156        axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().SetColor(lc)
4157        axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().SetColor(lc)
4158        axact.PickableOff()
4159        icn = Icon(axact, size=0.1)
4160        plt.axes_instances[r] = icn
4161        icn.SetInteractor(plt.interactor)
4162        icn.EnabledOn()
4163        icn.InteractiveOff()
4164        plt.widgets.append(icn)
4165
4166    elif plt.axes == 5:
4167        axact = vtk.vtkAnnotatedCubeActor()
4168        axact.GetCubeProperty().SetColor(get_color(settings.annotated_cube_color))
4169        axact.SetTextEdgesVisibility(0)
4170        axact.SetFaceTextScale(settings.annotated_cube_text_scale)
4171        axact.SetXPlusFaceText(settings.annotated_cube_texts[0])  # XPlus
4172        axact.SetXMinusFaceText(settings.annotated_cube_texts[1])  # XMinus
4173        axact.SetYPlusFaceText(settings.annotated_cube_texts[2])  # YPlus
4174        axact.SetYMinusFaceText(settings.annotated_cube_texts[3])  # YMinus
4175        axact.SetZPlusFaceText(settings.annotated_cube_texts[4])  # ZPlus
4176        axact.SetZMinusFaceText(settings.annotated_cube_texts[5])  # ZMinus
4177        axact.SetZFaceTextRotation(90)
4178
4179        if settings.annotated_cube_text_color is None:  # use default
4180            axact.GetXPlusFaceProperty().SetColor(get_color("r"))
4181            axact.GetXMinusFaceProperty().SetColor(get_color("dr"))
4182            axact.GetYPlusFaceProperty().SetColor(get_color("g"))
4183            axact.GetYMinusFaceProperty().SetColor(get_color("dg"))
4184            axact.GetZPlusFaceProperty().SetColor(get_color("b"))
4185            axact.GetZMinusFaceProperty().SetColor(get_color("db"))
4186        else:  # use single user color
4187            ac = get_color(settings.annotated_cube_text_color)
4188            axact.GetXPlusFaceProperty().SetColor(ac)
4189            axact.GetXMinusFaceProperty().SetColor(ac)
4190            axact.GetYPlusFaceProperty().SetColor(ac)
4191            axact.GetYMinusFaceProperty().SetColor(ac)
4192            axact.GetZPlusFaceProperty().SetColor(ac)
4193            axact.GetZMinusFaceProperty().SetColor(ac)
4194
4195        axact.PickableOff()
4196        icn = Icon(axact, size=0.06)
4197        plt.axes_instances[r] = icn
4198        icn.SetInteractor(plt.interactor)
4199        icn.EnabledOn()
4200        icn.InteractiveOff()
4201        plt.widgets.append(icn)
4202
4203    elif plt.axes == 6:
4204        ocf = vtk.vtkOutlineCornerFilter()
4205        ocf.SetCornerFactor(0.1)
4206        largestact, sz = None, -1
4207        for a in plt.actors:
4208            if a.GetPickable():
4209                b = a.GetBounds()
4210                if b is None:
4211                    return
4212                d = max(b[1] - b[0], b[3] - b[2], b[5] - b[4])
4213                if sz < d:
4214                    largestact = a
4215                    sz = d
4216        if isinstance(largestact, Assembly):
4217            ocf.SetInputData(largestact.unpack(0).GetMapper().GetInput())
4218        else:
4219            ocf.SetInputData(largestact.GetMapper().GetInput())
4220        ocf.Update()
4221        oc_mapper = vtk.vtkHierarchicalPolyDataMapper()
4222        oc_mapper.SetInputConnection(0, ocf.GetOutputPort(0))
4223        oc_actor = vtk.vtkActor()
4224        oc_actor.SetMapper(oc_mapper)
4225        bc = np.array(plt.renderer.GetBackground())
4226        if np.sum(bc) < 1.5:
4227            lc = (1, 1, 1)
4228        else:
4229            lc = (0, 0, 0)
4230        oc_actor.GetProperty().SetColor(lc)
4231        oc_actor.PickableOff()
4232        oc_actor.UseBoundsOn()
4233        plt.renderer.AddActor(oc_actor)
4234        plt.axes_instances[r] = oc_actor
4235        plt.renderer.AddActor(oc_actor)
4236
4237    elif plt.axes == 7:
4238        vbb = compute_visible_bounds()[0]
4239        rulax = RulerAxes(vbb, c=c, xtitle="x - ", ytitle="y - ", ztitle="z - ")
4240        plt.axes_instances[r] = rulax
4241        if not rulax:
4242            return None
4243        rulax.UseBoundsOn()
4244        rulax.PickableOff()
4245        plt.renderer.AddActor(rulax)
4246
4247    elif plt.axes == 8:
4248        vbb = compute_visible_bounds()[0]
4249        ca = vtk.vtkCubeAxesActor()
4250        ca.SetBounds(vbb)
4251        ca.SetCamera(plt.renderer.GetActiveCamera())
4252        ca.GetXAxesLinesProperty().SetColor(c)
4253        ca.GetYAxesLinesProperty().SetColor(c)
4254        ca.GetZAxesLinesProperty().SetColor(c)
4255        for i in range(3):
4256            ca.GetLabelTextProperty(i).SetColor(c)
4257            ca.GetTitleTextProperty(i).SetColor(c)
4258        ca.SetTitleOffset(5)
4259        ca.SetFlyMode(3)
4260        ca.SetXTitle("x")
4261        ca.SetYTitle("y")
4262        ca.SetZTitle("z")
4263        ca.PickableOff()
4264        ca.UseBoundsOff()
4265        plt.axes_instances[r] = ca
4266        plt.renderer.AddActor(ca)
4267
4268    elif plt.axes == 9:
4269        vbb = compute_visible_bounds()[0]
4270        src = vtk.vtkCubeSource()
4271        src.SetXLength(vbb[1] - vbb[0])
4272        src.SetYLength(vbb[3] - vbb[2])
4273        src.SetZLength(vbb[5] - vbb[4])
4274        src.Update()
4275        ca = Mesh(src.GetOutput(), c, 0.5).wireframe(True)
4276        ca.pos((vbb[0] + vbb[1]) / 2, (vbb[3] + vbb[2]) / 2, (vbb[5] + vbb[4]) / 2)
4277        ca.PickableOff()
4278        ca.UseBoundsOff()
4279        plt.axes_instances[r] = ca
4280        plt.renderer.AddActor(ca)
4281
4282    elif plt.axes == 10:
4283        vbb = compute_visible_bounds()[0]
4284        x0 = (vbb[0] + vbb[1]) / 2, (vbb[3] + vbb[2]) / 2, (vbb[5] + vbb[4]) / 2
4285        rx, ry, rz = (vbb[1] - vbb[0]) / 2, (vbb[3] - vbb[2]) / 2, (vbb[5] - vbb[4]) / 2
4286        rm = max(rx, ry, rz)
4287        xc = shapes.Disc(x0, r1=rm, r2=rm, c="lr", res=(1, 72))
4288        yc = shapes.Disc(x0, r1=rm, r2=rm, c="lg", res=(1, 72))
4289        yc.RotateX(90)
4290        zc = shapes.Disc(x0, r1=rm, r2=rm, c="lb", res=(1, 72))
4291        yc.RotateY(90)
4292        xc.clean().alpha(0.5).wireframe().linewidth(2).PickableOff()
4293        yc.clean().alpha(0.5).wireframe().linewidth(2).PickableOff()
4294        zc.clean().alpha(0.5).wireframe().linewidth(2).PickableOff()
4295        ca = xc + yc + zc
4296        ca.PickableOff()
4297        ca.UseBoundsOn()
4298        plt.renderer.AddActor(ca)
4299        plt.axes_instances[r] = ca
4300
4301    elif plt.axes == 11:
4302        vbb, ss = compute_visible_bounds()[0:2]
4303        xpos, ypos = (vbb[1] + vbb[0]) / 2, (vbb[3] + vbb[2]) / 2
4304        gs = sum(ss) * 3
4305        gr = shapes.Grid((xpos, ypos, vbb[4]), s=(gs, gs), res=(11, 11), c=c, alpha=0.1)
4306        gr.lighting("off").PickableOff()
4307        gr.UseBoundsOff()
4308        plt.axes_instances[r] = gr
4309        plt.renderer.AddActor(gr)
4310
4311    elif plt.axes == 12:
4312        polaxes = vtk.vtkPolarAxesActor()
4313        vbb = compute_visible_bounds()[0]
4314
4315        polaxes.SetPolarAxisTitle("radial distance")
4316        polaxes.SetPole(0, 0, vbb[4])
4317        rd = max(abs(vbb[0]), abs(vbb[2]), abs(vbb[1]), abs(vbb[3]))
4318        polaxes.SetMaximumRadius(rd)
4319        polaxes.AutoSubdividePolarAxisOff()
4320        polaxes.SetNumberOfPolarAxisTicks(10)
4321        polaxes.SetCamera(plt.renderer.GetActiveCamera())
4322        polaxes.SetPolarLabelFormat("%6.1f")
4323        polaxes.PolarLabelVisibilityOff()  # due to bad overlap of labels
4324
4325        polaxes.GetPolarArcsProperty().SetColor(c)
4326        polaxes.GetPolarAxisProperty().SetColor(c)
4327        polaxes.GetPolarAxisTitleTextProperty().SetColor(c)
4328        polaxes.GetPolarAxisLabelTextProperty().SetColor(c)
4329        polaxes.GetLastRadialAxisTextProperty().SetColor(c)
4330        polaxes.GetSecondaryRadialAxesTextProperty().SetColor(c)
4331        polaxes.GetSecondaryRadialAxesProperty().SetColor(c)
4332        polaxes.GetSecondaryPolarArcsProperty().SetColor(c)
4333
4334        polaxes.SetMinimumAngle(0.0)
4335        polaxes.SetMaximumAngle(315.0)
4336        polaxes.SetNumberOfPolarAxisTicks(5)
4337        polaxes.UseBoundsOn()
4338        polaxes.PickableOff()
4339        plt.axes_instances[r] = polaxes
4340        plt.renderer.AddActor(polaxes)
4341
4342    elif plt.axes == 13:
4343        # draws a simple ruler at the bottom of the window
4344        ls = vtk.vtkLegendScaleActor()
4345        ls.RightAxisVisibilityOff()
4346        ls.TopAxisVisibilityOff()
4347        ls.LeftAxisVisibilityOff()
4348        ls.LegendVisibilityOff()
4349        ls.SetBottomBorderOffset(50)
4350        ls.GetBottomAxis().SetNumberOfMinorTicks(1)
4351        ls.GetBottomAxis().SetFontFactor(1.1)
4352        ls.GetBottomAxis().GetProperty().SetColor(c)
4353        ls.GetBottomAxis().GetProperty().SetOpacity(1.0)
4354        ls.GetBottomAxis().GetProperty().SetLineWidth(2)
4355        ls.GetBottomAxis().GetLabelTextProperty().SetColor(c)
4356        ls.GetBottomAxis().GetLabelTextProperty().BoldOff()
4357        ls.GetBottomAxis().GetLabelTextProperty().ItalicOff()
4358        pr = ls.GetBottomAxis().GetLabelTextProperty()
4359        pr.SetFontFamily(vtk.VTK_FONT_FILE)
4360        pr.SetFontFile(utils.get_font_path(settings.default_font))
4361        ls.PickableOff()
4362        # if not plt.renderer.GetActiveCamera().GetParallelProjection():
4363        #     vedo.logger.warning("Axes type 13 should be used with parallel projection")
4364        plt.axes_instances[r] = ls
4365        plt.renderer.AddActor(ls)
4366
4367    elif plt.axes == 14:
4368        try:
4369            cow = vtk.vtkCameraOrientationWidget()
4370            cow.SetParentRenderer(plt.renderer)
4371            cow.On()
4372            plt.axes_instances[r] = cow
4373        except AttributeError:
4374            vedo.logger.warning("axes mode 14 is unavailable in this vtk version")
4375
4376    else:
4377        e = "\bomb Keyword axes type must be in range [0-13]."
4378        e += "Available axes types are:\n\n"
4379        e += "0 = no axes\n"
4380        e += "1 = draw three customizable gray grid walls\n"
4381        e += "2 = show cartesian axes from (0,0,0)\n"
4382        e += "3 = show positive range of cartesian axes from (0,0,0)\n"
4383        e += "4 = show a triad at bottom left\n"
4384        e += "5 = show a cube at bottom left\n"
4385        e += "6 = mark the corners of the bounding box\n"
4386        e += "7 = draw a 3D ruler at each side of the cartesian axes\n"
4387        e += "8 = show the vtkCubeAxesActor object\n"
4388        e += "9 = show the bounding box outline\n"
4389        e += "10 = show three circles representing the maximum bounding box\n"
4390        e += "11 = show a large grid on the x-y plane (use with zoom=8)\n"
4391        e += "12 = show polar axes\n"
4392        e += "13 = draw a simple ruler at the bottom of the window\n"
4393        e += "14 = show the CameraOrientationWidget object"
4394        vedo.logger.warning(e)
4395
4396    if not plt.axes_instances[r]:
4397        plt.axes_instances[r] = True
4398    return None
def ScalarBar( obj, title='', pos=(0.8, 0.05), title_yoffset=15, font_size=12, size=(None, None), nlabels=None, c='k', horizontal=False, use_alpha=True, label_format=':6.3g'):
859def ScalarBar(
860    obj,
861    title="",
862    pos=(0.8, 0.05),
863    title_yoffset=15,
864    font_size=12,
865    size=(None, None),
866    nlabels=None,
867    c="k",
868    horizontal=False,
869    use_alpha=True,
870    label_format=":6.3g",
871):
872    """
873    A 2D scalar bar for the specified obj.
874
875    Arguments:
876        pos : (list)
877            fractional x and y position in the 2D window
878        size : (list)
879            size of the scalarbar in pixel units (width, height)
880        nlabels : (int)
881            number of numeric labels to be shown
882        use_alpha : (bool)
883            retain transparency in scalarbar
884        horizontal : (bool)
885            show in horizontal layout
886
887    Examples:
888        - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py)
889
890        ![](https://user-images.githubusercontent.com/32848391/62940174-4bdc7900-bdd3-11e9-9713-e4f3e2fdab63.png)
891    """
892
893    if isinstance(obj, Points):
894        vtkscalars = obj.inputdata().GetPointData().GetScalars()
895        if vtkscalars is None:
896            vtkscalars = obj.inputdata().GetCellData().GetScalars()
897        if not vtkscalars:
898            return None
899        lut = vtkscalars.GetLookupTable()
900        if not lut:
901            lut = obj.mapper().GetLookupTable()
902            if not lut:
903                return None
904
905    elif isinstance(obj, (Volume, TetMesh)):
906        lut = utils.ctf2lut(obj)
907
908    elif utils.is_sequence(obj) and len(obj) == 2:
909        x = np.linspace(obj[0], obj[1], 256)
910        data = []
911        for i in range(256):
912            rgb = color_map(i, c, 0, 256)
913            data.append([x[i], rgb])
914        lut = build_lut(data)
915        
916    elif not hasattr(obj, "mapper"):
917        vedo.logger.error(f"in add_scalarbar(): input is invalid {type(obj)}. Skip.")
918        return None
919
920    else:
921        return None
922
923    c = get_color(c)
924    sb = vtk.vtkScalarBarActor()
925
926    # print(sb.GetLabelFormat())
927    label_format = label_format.replace(":", "%-#")
928    sb.SetLabelFormat(label_format)
929
930    sb.SetLookupTable(lut)
931    sb.SetUseOpacity(use_alpha)
932    sb.SetDrawFrame(0)
933    sb.SetDrawBackground(0)
934    if lut.GetUseBelowRangeColor():
935        sb.DrawBelowRangeSwatchOn()
936        sb.SetBelowRangeAnnotation("")
937    if lut.GetUseAboveRangeColor():
938        sb.DrawAboveRangeSwatchOn()
939        sb.SetAboveRangeAnnotation("")
940    if lut.GetNanColor() != (0.5, 0.0, 0.0, 1.0):
941        sb.DrawNanAnnotationOn()
942        sb.SetNanAnnotation("nan")
943
944    if title:
945        if "\\" in repr(title):
946            for r in shapes._reps:
947                title = title.replace(r[0], r[1])
948        titprop = sb.GetTitleTextProperty()
949        titprop.BoldOn()
950        titprop.ItalicOff()
951        titprop.ShadowOff()
952        titprop.SetColor(c)
953        titprop.SetVerticalJustificationToTop()
954        titprop.SetFontSize(font_size)
955        titprop.SetFontFamily(vtk.VTK_FONT_FILE)
956        titprop.SetFontFile(utils.get_font_path(settings.default_font))
957        sb.SetTitle(title)
958        sb.SetVerticalTitleSeparation(title_yoffset)
959        sb.SetTitleTextProperty(titprop)
960
961    sb.UnconstrainedFontSizeOn()
962    sb.DrawAnnotationsOn()
963    sb.DrawTickLabelsOn()
964    sb.SetMaximumNumberOfColors(256)
965
966    if horizontal:
967        sb.SetOrientationToHorizontal()
968        sb.SetNumberOfLabels(3)
969        sb.SetTextPositionToSucceedScalarBar()
970        sb.SetPosition(pos)
971        sb.SetMaximumWidthInPixels(1000)
972        sb.SetMaximumHeightInPixels(50)
973    else:
974        sb.SetNumberOfLabels(7)
975        sb.SetTextPositionToPrecedeScalarBar()
976        sb.SetPosition(pos[0] + 0.09, pos[1])
977        sb.SetMaximumWidthInPixels(60)
978        sb.SetMaximumHeightInPixels(250)
979
980    if size[0] is not None:
981        sb.SetMaximumWidthInPixels(size[0])
982    if size[1] is not None:
983        sb.SetMaximumHeightInPixels(size[1])
984
985    if nlabels is not None:
986        sb.SetNumberOfLabels(nlabels)
987
988    sctxt = sb.GetLabelTextProperty()
989    sctxt.SetFontFamily(vtk.VTK_FONT_FILE)
990    sctxt.SetFontFile(utils.get_font_path(settings.default_font))
991    sctxt.SetColor(c)
992    sctxt.SetShadow(0)
993    sctxt.SetFontSize(font_size - 2)
994    sb.SetAnnotationTextProperty(sctxt)
995    sb.PickableOff()
996    return sb

A 2D scalar bar for the specified obj.

Arguments:
  • pos : (list) fractional x and y position in the 2D window
  • size : (list) size of the scalarbar in pixel units (width, height)
  • nlabels : (int) number of numeric labels to be shown
  • use_alpha : (bool) retain transparency in scalarbar
  • horizontal : (bool) show in horizontal layout
Examples:

def ScalarBar3D( obj, title='', pos=None, size=(None, None), title_font='', title_xoffset=-1.5, title_yoffset=0.0, title_size=1.5, title_rotation=0.0, nlabels=8, label_font='', label_size=1, label_offset=0.375, label_rotation=0, label_format='', italic=0, c=None, draw_box=True, above_text=None, below_text=None, nan_text='NaN', categories=None):
1000def ScalarBar3D(
1001    obj,
1002    title="",
1003    pos=None,
1004    size=(None, None),
1005    title_font="",
1006    title_xoffset=-1.5,
1007    title_yoffset=0.0,
1008    title_size=1.5,
1009    title_rotation=0.0,
1010    nlabels=8,
1011    label_font="",
1012    label_size=1,
1013    label_offset=0.375,
1014    label_rotation=0,
1015    label_format="",
1016    italic=0,
1017    c=None,
1018    draw_box=True,
1019    above_text=None,
1020    below_text=None,
1021    nan_text="NaN",
1022    categories=None,
1023):
1024    """
1025    Create a 3D scalar bar for the specified object.
1026
1027    Input `obj` input can be:
1028
1029        - a list of numbers,
1030        - a list of two numbers in the form (min, max),
1031        - a Mesh already containing a set of scalars associated to vertices or cells,
1032        - if None the last object in the list of actors will be used.
1033
1034    Arguments:
1035        size : (list)
1036            (thickness, length) of scalarbar
1037        title : (str)
1038            scalar bar title
1039        title_xoffset : (float)
1040            horizontal space btw title and color scalarbar
1041        title_yoffset : (float)
1042            vertical space offset
1043        title_size : (float)
1044            size of title wrt numeric labels
1045        title_rotation : (float)
1046            title rotation in degrees
1047        nlabels : (int)
1048            number of numeric labels
1049        label_font : (str)
1050            font type for labels
1051        label_size : (float)
1052            label scale factor
1053        label_offset : (float)
1054            space btw numeric labels and scale
1055        label_rotation : (float)
1056            label rotation in degrees
1057        draw_box : (bool)
1058            draw a box around the colorbar
1059        categories : (list)
1060            make a categorical scalarbar,
1061            the input list will have the format [value, color, alpha, textlabel]
1062
1063    Examples:
1064        - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py)
1065    """
1066
1067    if isinstance(obj, Points):
1068        lut = obj.mapper().GetLookupTable()
1069        if not lut or lut.GetTable().GetNumberOfTuples() == 0:
1070            # create the most similar to the default
1071            obj.cmap("jet_r")
1072            lut = obj.mapper().GetLookupTable()
1073        vmin, vmax = lut.GetRange()
1074
1075    elif isinstance(obj, (Volume, TetMesh)):
1076        lut = utils.ctf2lut(obj)
1077        vmin, vmax = lut.GetRange()
1078
1079    elif utils.is_sequence(obj):
1080        vmin, vmax = np.min(obj), np.max(obj)
1081
1082    else:
1083        vedo.logger.error("in ScalarBar3D(): input must be a vedo object with bounds.")
1084        return obj
1085
1086    bns = obj.bounds()
1087    sx, sy = size
1088    if sy is None:
1089        sy = bns[3] - bns[2]
1090    if sx is None:
1091        sx = sy / 18
1092
1093    if categories is not None:  ################################
1094        ncats = len(categories)
1095        scale = shapes.Grid([-float(sx) * label_offset, 0, 0],
1096                            c=c, alpha=1, s=(sx, sy), res=(1, ncats))
1097        cols, alphas = [], []
1098        ticks_pos, ticks_txt = [0.0], [""]
1099        for i, cat in enumerate(categories):
1100            cl = get_color(cat[1])
1101            cols.append(cl)
1102            if len(cat) > 2:
1103                alphas.append(cat[2])
1104            else:
1105                alphas.append(1)
1106            if len(cat) > 3:
1107                ticks_txt.append(cat[3])
1108            else:
1109                ticks_txt.append("")
1110            ticks_pos.append((i + 0.5) / ncats)
1111        ticks_pos.append(1.0)
1112        ticks_txt.append("")
1113        rgba = np.c_[np.array(cols) * 255, np.array(alphas) * 255]
1114        scale.cell_individual_colors(rgba)
1115
1116    else:  ########################################################
1117
1118        # build the color scale part
1119        scale = shapes.Grid(
1120            [-float(sx) * label_offset, 0, 0],
1121            c=c,
1122            alpha=1,
1123            s=(sx, sy),
1124            res=(1, lut.GetTable().GetNumberOfTuples()),
1125        )
1126        cscals = np.linspace(vmin, vmax, lut.GetTable().GetNumberOfTuples())
1127
1128        if lut.GetScale():  # logarithmic scale
1129            lut10 = vtk.vtkLookupTable()
1130            lut10.DeepCopy(lut)
1131            lut10.SetScaleToLinear()
1132            scale.cmap(lut10, cscals, on="cells")
1133            tk = utils.make_ticks(vmin, vmax, nlabels, logscale=True, useformat=label_format)
1134        else:
1135            scale.cmap(lut, cscals, on="cells")
1136            tk = utils.make_ticks(vmin, vmax, nlabels, logscale=False, useformat=label_format)
1137        ticks_pos, ticks_txt = tk
1138    scale.lw(0).wireframe(False).lighting("off")
1139
1140    scales = [scale]
1141
1142    xbns = scale.xbounds()
1143    if pos is None:
1144        d = sx / 2
1145        if title:
1146            d = np.sqrt((bns[1] - bns[0]) ** 2 + sy * sy) / 20
1147        pos = (bns[1] - xbns[0] + d, (bns[2] + bns[3]) / 2, bns[4])
1148
1149    lsize = sy / 60 * label_size
1150
1151    tacts = []
1152    for i, p in enumerate(ticks_pos):
1153        tx = ticks_txt[i]
1154        if i and tx:
1155            # build numeric text
1156            y = (p - 0.5) * sy
1157            if label_rotation:
1158                a = shapes.Text3D(
1159                    tx,
1160                    pos=[sx * label_offset, y, 0],
1161                    s=lsize,
1162                    justify="center-top",
1163                    c=c,
1164                    italic=italic,
1165                    font=label_font,
1166                )
1167                a.RotateZ(label_rotation)
1168            else:
1169                a = shapes.Text3D(
1170                    tx,
1171                    pos=[sx * label_offset, y, 0],
1172                    s=lsize,
1173                    justify="center-left",
1174                    c=c,
1175                    italic=italic,
1176                    font=label_font,
1177                )
1178
1179            tacts.append(a)
1180
1181            # build ticks
1182            tic = shapes.Line([xbns[1], y, 0], [xbns[1] + sx * label_offset / 4, y, 0], lw=2, c=c)
1183            tacts.append(tic)
1184
1185    # build title
1186    if title:
1187        t = shapes.Text3D(
1188            title,
1189            (0, 0, 0),
1190            s=sy / 50 * title_size,
1191            c=c,
1192            justify="centered",
1193            italic=italic,
1194            font=title_font,
1195        )
1196        t.RotateZ(90 + title_rotation)
1197        t.pos(sx * title_xoffset, title_yoffset, 0)
1198        tacts.append(t)
1199
1200    # build below scale
1201    if lut.GetUseBelowRangeColor():
1202        r, g, b, alfa = lut.GetBelowRangeColor()
1203        sx = float(sx)
1204        sy = float(sy)
1205        brect = shapes.Rectangle(
1206            [-sx * label_offset - sx / 2, -sy / 2 - sx - sx * 0.1, 0],
1207            [-sx * label_offset + sx / 2, -sy / 2 - sx * 0.1, 0],
1208            c=(r, g, b),
1209            alpha=alfa,
1210        )
1211        brect.lw(1).lc(c).lighting("off")
1212        scales += [brect]
1213        if below_text is None:
1214            below_text = " <" + str(vmin)
1215        if below_text:
1216            if label_rotation:
1217                btx = shapes.Text3D(
1218                    below_text,
1219                    (0, 0, 0),
1220                    s=lsize,
1221                    c=c,
1222                    justify="center-top",
1223                    italic=italic,
1224                    font=label_font,
1225                )
1226                btx.RotateZ(label_rotation)
1227            else:
1228                btx = shapes.Text3D(
1229                    below_text,
1230                    (0, 0, 0),
1231                    s=lsize,
1232                    c=c,
1233                    justify="center-left",
1234                    italic=italic,
1235                    font=label_font,
1236                )
1237
1238            btx.pos(sx * label_offset, -sy / 2 - sx * 0.66, 0)
1239            tacts.append(btx)
1240
1241    # build above scale
1242    if lut.GetUseAboveRangeColor():
1243        r, g, b, alfa = lut.GetAboveRangeColor()
1244        arect = shapes.Rectangle(
1245            [-sx * label_offset - sx / 2, sy / 2 + sx * 0.1, 0],
1246            [-sx * label_offset + sx / 2, sy / 2 + sx + sx * 0.1, 0],
1247            c=(r, g, b),
1248            alpha=alfa,
1249        )
1250        arect.lw(1).lc(c).lighting("off")
1251        scales += [arect]
1252        if above_text is None:
1253            above_text = " >" + str(vmax)
1254        if above_text:
1255            if label_rotation:
1256                atx = shapes.Text3D(
1257                    above_text,
1258                    (0, 0, 0),
1259                    s=lsize,
1260                    c=c,
1261                    justify="center-top",
1262                    italic=italic,
1263                    font=label_font,
1264                )
1265                atx.RotateZ(label_rotation)
1266            else:
1267                atx = shapes.Text3D(
1268                    above_text,
1269                    (0, 0, 0),
1270                    s=lsize,
1271                    c=c,
1272                    justify="center-left",
1273                    italic=italic,
1274                    font=label_font,
1275                )
1276
1277            atx.pos(sx * label_offset, sy / 2 + sx * 0.66, 0)
1278            tacts.append(atx)
1279
1280    # build NaN scale
1281    if lut.GetNanColor() != (0.5, 0.0, 0.0, 1.0):
1282        nanshift = sx * 0.1
1283        if brect:
1284            nanshift += sx
1285        r, g, b, alfa = lut.GetNanColor()
1286        nanrect = shapes.Rectangle(
1287            [-sx * label_offset - sx / 2, -sy / 2 - sx - sx * 0.1 - nanshift, 0],
1288            [-sx * label_offset + sx / 2, -sy / 2 - sx * 0.1 - nanshift, 0],
1289            c=(r, g, b),
1290            alpha=alfa,
1291        )
1292        nanrect.lw(1).lc(c).lighting("off")
1293        scales += [nanrect]
1294        if label_rotation:
1295            nantx = shapes.Text3D(
1296                nan_text,
1297                (0, 0, 0),
1298                s=lsize,
1299                c=c,
1300                justify="center-left",
1301                italic=italic,
1302                font=label_font,
1303            )
1304            nantx.RotateZ(label_rotation)
1305        else:
1306            nantx = shapes.Text3D(
1307                nan_text,
1308                (0, 0, 0),
1309                s=lsize,
1310                c=c,
1311                justify="center-left",
1312                italic=italic,
1313                font=label_font,
1314            )
1315        nantx.pos(sx * label_offset, -sy / 2 - sx * 0.66 - nanshift, 0)
1316        tacts.append(nantx)
1317
1318    if draw_box:
1319        tacts.append(scale.box().lw(1))
1320
1321    for a in tacts:
1322        a.PickableOff()
1323
1324    mtacts = merge(tacts).lighting("off")
1325    mtacts.PickableOff()
1326    scale.PickableOff()
1327
1328    sact = Assembly(scales + tacts)
1329    sact.SetPosition(pos)
1330    sact.PickableOff()
1331    sact.UseBoundsOff()
1332    sact.name = "ScalarBar3D"
1333    return sact

Create a 3D scalar bar for the specified object.

Input obj input can be:

- a list of numbers,
- a list of two numbers in the form (min, max),
- a Mesh already containing a set of scalars associated to vertices or cells,
- if None the last object in the list of actors will be used.
Arguments:
  • size : (list) (thickness, length) of scalarbar
  • title : (str) scalar bar title
  • title_xoffset : (float) horizontal space btw title and color scalarbar
  • title_yoffset : (float) vertical space offset
  • title_size : (float) size of title wrt numeric labels
  • title_rotation : (float) title rotation in degrees
  • nlabels : (int) number of numeric labels
  • label_font : (str) font type for labels
  • label_size : (float) label scale factor
  • label_offset : (float) space btw numeric labels and scale
  • label_rotation : (float) label rotation in degrees
  • draw_box : (bool) draw a box around the colorbar
  • categories : (list) make a categorical scalarbar, the input list will have the format [value, color, alpha, textlabel]
Examples:
class Slider2D(SliderWidget):
1337class Slider2D(SliderWidget):
1338    """
1339    Add a slider which can call an external custom function.
1340    """
1341    def __init__(
1342        self,
1343        sliderfunc,
1344        xmin,
1345        xmax,
1346        value=None,
1347        pos=4,
1348        title="",
1349        font="Calco",
1350        title_size=1,
1351        c="k",
1352        alpha=1,
1353        show_value=True,
1354        delayed=False,
1355        **options,
1356    ):
1357        """
1358        Add a slider which can call an external custom function.
1359        Set any value as float to increase the number of significant digits above the slider.
1360
1361        Use `play()` to start an animation between the current slider value and the last value.
1362
1363        Arguments:
1364            sliderfunc : (function)
1365                external function to be called by the widget
1366            xmin : (float)
1367                lower value of the slider
1368            xmax : (float)
1369                upper value
1370            value : (float)
1371                current value
1372            pos : (list, str)
1373                position corner number: horizontal [1-5] or vertical [11-15]
1374                it can also be specified by corners coordinates [(x1,y1), (x2,y2)]
1375                and also by a string descriptor (eg. "bottom-left")
1376            title : (str)
1377                title text
1378            font : (str)
1379                title font face. Check [available fonts here](https://vedo.embl.es/fonts).
1380            title_size : (float)
1381                title text scale [1.0]
1382            show_value : (bool)
1383                if True current value is shown
1384            delayed : (bool)
1385                if True the callback is delayed until when the mouse button is released
1386            alpha : (float)
1387                opacity of the scalar bar texts
1388            slider_length : (float)
1389                slider length
1390            slider_width : (float)
1391                slider width
1392            end_cap_length : (float)
1393                length of the end cap
1394            end_cap_width : (float)
1395                width of the end cap
1396            tube_width : (float)
1397                width of the tube
1398            title_height : (float)
1399                height of the title
1400            tformat : (str)
1401                format of the title
1402
1403        Examples:
1404            - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py)
1405            - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py)
1406
1407            ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg)
1408        """
1409        slider_length = options.pop("slider_length",  0.015)
1410        slider_width  = options.pop("slider_width",   0.025)
1411        end_cap_length= options.pop("end_cap_length", 0.0015)
1412        end_cap_width = options.pop("end_cap_width",  0.0125)
1413        tube_width    = options.pop("tube_width",     0.0075)
1414        title_height  = options.pop("title_height",   0.025)
1415        if options:
1416            vedo.logger.warning(f"in Slider2D unknown option(s): {options}")
1417
1418        c = get_color(c)
1419
1420        if value is None or value < xmin:
1421            value = xmin
1422
1423        slider_rep = vtk.vtkSliderRepresentation2D()
1424        slider_rep.SetMinimumValue(xmin)
1425        slider_rep.SetMaximumValue(xmax)
1426        slider_rep.SetValue(value)
1427        slider_rep.SetSliderLength(slider_length)
1428        slider_rep.SetSliderWidth(slider_width)
1429        slider_rep.SetEndCapLength(end_cap_length)
1430        slider_rep.SetEndCapWidth(end_cap_width)
1431        slider_rep.SetTubeWidth(tube_width)
1432        slider_rep.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay()
1433        slider_rep.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay()
1434
1435        if isinstance(pos, str):
1436            if "top" in pos:
1437                if "left" in pos:
1438                    if "vert" in pos:
1439                        pos = 11
1440                    else:
1441                        pos = 1
1442                elif "right" in pos:
1443                    if "vert" in pos:
1444                        pos = 12
1445                    else:
1446                        pos = 2
1447            elif "bott" in pos:
1448                if "left" in pos:
1449                    if "vert" in pos:
1450                        pos = 13
1451                    else:
1452                        pos = 3
1453                elif "right" in pos:
1454                    if "vert" in pos:
1455                        if "span" in pos:
1456                            pos = 15
1457                        else:
1458                            pos = 14
1459                    else:
1460                        pos = 4
1461                elif "span" in pos:
1462                    pos = 5
1463
1464        if utils.is_sequence(pos):
1465            slider_rep.GetPoint1Coordinate().SetValue(pos[0][0], pos[0][1])
1466            slider_rep.GetPoint2Coordinate().SetValue(pos[1][0], pos[1][1])
1467        elif pos == 1:  # top-left horizontal
1468            slider_rep.GetPoint1Coordinate().SetValue(0.04, 0.93)
1469            slider_rep.GetPoint2Coordinate().SetValue(0.45, 0.93)
1470        elif pos == 2:
1471            slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.93)
1472            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.93)
1473        elif pos == 3:
1474            slider_rep.GetPoint1Coordinate().SetValue(0.05, 0.06)
1475            slider_rep.GetPoint2Coordinate().SetValue(0.45, 0.06)
1476        elif pos == 4:  # bottom-right
1477            slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.06)
1478            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06)
1479        elif pos == 5:  # bottom span horizontal
1480            slider_rep.GetPoint1Coordinate().SetValue(0.04, 0.06)
1481            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06)
1482        elif pos == 11:  # top-left vertical
1483            slider_rep.GetPoint1Coordinate().SetValue(0.065, 0.54)
1484            slider_rep.GetPoint2Coordinate().SetValue(0.065, 0.9)
1485        elif pos == 12:
1486            slider_rep.GetPoint1Coordinate().SetValue(0.94, 0.54)
1487            slider_rep.GetPoint2Coordinate().SetValue(0.94, 0.9)
1488        elif pos == 13:
1489            slider_rep.GetPoint1Coordinate().SetValue(0.065, 0.1)
1490            slider_rep.GetPoint2Coordinate().SetValue(0.065, 0.54)
1491        elif pos == 14:  # bottom-right vertical
1492            slider_rep.GetPoint1Coordinate().SetValue(0.94, 0.1)
1493            slider_rep.GetPoint2Coordinate().SetValue(0.94, 0.54)
1494        elif pos == 15:  # right margin vertical
1495            slider_rep.GetPoint1Coordinate().SetValue(0.95, 0.1)
1496            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.9)
1497        else:  # bottom-right
1498            slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.06)
1499            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06)
1500
1501        if show_value:
1502            if isinstance(xmin, int) and isinstance(xmax, int) and isinstance(value, int):
1503                frm = "%0.0f"
1504            else:
1505                frm = "%0.2f"
1506
1507            frm = options.pop("tformat", frm)
1508
1509            slider_rep.SetLabelFormat(frm)  # default is '%0.3g'
1510            slider_rep.GetLabelProperty().SetShadow(0)
1511            slider_rep.GetLabelProperty().SetBold(0)
1512            slider_rep.GetLabelProperty().SetOpacity(alpha)
1513            slider_rep.GetLabelProperty().SetColor(c)
1514            if isinstance(pos, int) and pos > 10:
1515                slider_rep.GetLabelProperty().SetOrientation(90)
1516        else:
1517            slider_rep.ShowSliderLabelOff()
1518        slider_rep.GetTubeProperty().SetColor(c)
1519        slider_rep.GetTubeProperty().SetOpacity(0.75)
1520        slider_rep.GetSliderProperty().SetColor(c)
1521        slider_rep.GetSelectedProperty().SetColor(np.sqrt(np.array(c)))
1522        slider_rep.GetCapProperty().SetColor(c)
1523
1524        slider_rep.SetTitleHeight(title_height * title_size)
1525        slider_rep.GetTitleProperty().SetShadow(0)
1526        slider_rep.GetTitleProperty().SetColor(c)
1527        slider_rep.GetTitleProperty().SetOpacity(alpha)
1528        slider_rep.GetTitleProperty().SetBold(0)
1529        if font.lower() == "courier":
1530            slider_rep.GetTitleProperty().SetFontFamilyToCourier()
1531        elif font.lower() == "times":
1532            slider_rep.GetTitleProperty().SetFontFamilyToTimes()
1533        elif font.lower() == "arial":
1534            slider_rep.GetTitleProperty().SetFontFamilyToArial()
1535        else:
1536            if font == "":
1537                font = utils.get_font_path(settings.default_font)
1538            else:
1539                font = utils.get_font_path(font)
1540            slider_rep.GetTitleProperty().SetFontFamily(vtk.VTK_FONT_FILE)
1541            slider_rep.GetLabelProperty().SetFontFamily(vtk.VTK_FONT_FILE)
1542            slider_rep.GetTitleProperty().SetFontFile(font)
1543            slider_rep.GetLabelProperty().SetFontFile(font)
1544
1545        if title:
1546            slider_rep.SetTitleText(title)
1547            if not utils.is_sequence(pos):
1548                if isinstance(pos, int) and pos > 10:
1549                    slider_rep.GetTitleProperty().SetOrientation(90)
1550            else:
1551                if abs(pos[0][0] - pos[1][0]) < 0.1:
1552                    slider_rep.GetTitleProperty().SetOrientation(90)
1553
1554        SliderWidget.__init__(self)
1555
1556        self.SetAnimationModeToJump()
1557        self.SetRepresentation(slider_rep)
1558        if delayed:
1559            self.AddObserver("EndInteractionEvent", sliderfunc)
1560        else:
1561            self.AddObserver("InteractionEvent", sliderfunc)

Add a slider which can call an external custom function.

Slider2D( sliderfunc, xmin, xmax, value=None, pos=4, title='', font='Calco', title_size=1, c='k', alpha=1, show_value=True, delayed=False, **options)
1341    def __init__(
1342        self,
1343        sliderfunc,
1344        xmin,
1345        xmax,
1346        value=None,
1347        pos=4,
1348        title="",
1349        font="Calco",
1350        title_size=1,
1351        c="k",
1352        alpha=1,
1353        show_value=True,
1354        delayed=False,
1355        **options,
1356    ):
1357        """
1358        Add a slider which can call an external custom function.
1359        Set any value as float to increase the number of significant digits above the slider.
1360
1361        Use `play()` to start an animation between the current slider value and the last value.
1362
1363        Arguments:
1364            sliderfunc : (function)
1365                external function to be called by the widget
1366            xmin : (float)
1367                lower value of the slider
1368            xmax : (float)
1369                upper value
1370            value : (float)
1371                current value
1372            pos : (list, str)
1373                position corner number: horizontal [1-5] or vertical [11-15]
1374                it can also be specified by corners coordinates [(x1,y1), (x2,y2)]
1375                and also by a string descriptor (eg. "bottom-left")
1376            title : (str)
1377                title text
1378            font : (str)
1379                title font face. Check [available fonts here](https://vedo.embl.es/fonts).
1380            title_size : (float)
1381                title text scale [1.0]
1382            show_value : (bool)
1383                if True current value is shown
1384            delayed : (bool)
1385                if True the callback is delayed until when the mouse button is released
1386            alpha : (float)
1387                opacity of the scalar bar texts
1388            slider_length : (float)
1389                slider length
1390            slider_width : (float)
1391                slider width
1392            end_cap_length : (float)
1393                length of the end cap
1394            end_cap_width : (float)
1395                width of the end cap
1396            tube_width : (float)
1397                width of the tube
1398            title_height : (float)
1399                height of the title
1400            tformat : (str)
1401                format of the title
1402
1403        Examples:
1404            - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py)
1405            - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py)
1406
1407            ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg)
1408        """
1409        slider_length = options.pop("slider_length",  0.015)
1410        slider_width  = options.pop("slider_width",   0.025)
1411        end_cap_length= options.pop("end_cap_length", 0.0015)
1412        end_cap_width = options.pop("end_cap_width",  0.0125)
1413        tube_width    = options.pop("tube_width",     0.0075)
1414        title_height  = options.pop("title_height",   0.025)
1415        if options:
1416            vedo.logger.warning(f"in Slider2D unknown option(s): {options}")
1417
1418        c = get_color(c)
1419
1420        if value is None or value < xmin:
1421            value = xmin
1422
1423        slider_rep = vtk.vtkSliderRepresentation2D()
1424        slider_rep.SetMinimumValue(xmin)
1425        slider_rep.SetMaximumValue(xmax)
1426        slider_rep.SetValue(value)
1427        slider_rep.SetSliderLength(slider_length)
1428        slider_rep.SetSliderWidth(slider_width)
1429        slider_rep.SetEndCapLength(end_cap_length)
1430        slider_rep.SetEndCapWidth(end_cap_width)
1431        slider_rep.SetTubeWidth(tube_width)
1432        slider_rep.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay()
1433        slider_rep.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay()
1434
1435        if isinstance(pos, str):
1436            if "top" in pos:
1437                if "left" in pos:
1438                    if "vert" in pos:
1439                        pos = 11
1440                    else:
1441                        pos = 1
1442                elif "right" in pos:
1443                    if "vert" in pos:
1444                        pos = 12
1445                    else:
1446                        pos = 2
1447            elif "bott" in pos:
1448                if "left" in pos:
1449                    if "vert" in pos:
1450                        pos = 13
1451                    else:
1452                        pos = 3
1453                elif "right" in pos:
1454                    if "vert" in pos:
1455                        if "span" in pos:
1456                            pos = 15
1457                        else:
1458                            pos = 14
1459                    else:
1460                        pos = 4
1461                elif "span" in pos:
1462                    pos = 5
1463
1464        if utils.is_sequence(pos):
1465            slider_rep.GetPoint1Coordinate().SetValue(pos[0][0], pos[0][1])
1466            slider_rep.GetPoint2Coordinate().SetValue(pos[1][0], pos[1][1])
1467        elif pos == 1:  # top-left horizontal
1468            slider_rep.GetPoint1Coordinate().SetValue(0.04, 0.93)
1469            slider_rep.GetPoint2Coordinate().SetValue(0.45, 0.93)
1470        elif pos == 2:
1471            slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.93)
1472            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.93)
1473        elif pos == 3:
1474            slider_rep.GetPoint1Coordinate().SetValue(0.05, 0.06)
1475            slider_rep.GetPoint2Coordinate().SetValue(0.45, 0.06)
1476        elif pos == 4:  # bottom-right
1477            slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.06)
1478            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06)
1479        elif pos == 5:  # bottom span horizontal
1480            slider_rep.GetPoint1Coordinate().SetValue(0.04, 0.06)
1481            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06)
1482        elif pos == 11:  # top-left vertical
1483            slider_rep.GetPoint1Coordinate().SetValue(0.065, 0.54)
1484            slider_rep.GetPoint2Coordinate().SetValue(0.065, 0.9)
1485        elif pos == 12:
1486            slider_rep.GetPoint1Coordinate().SetValue(0.94, 0.54)
1487            slider_rep.GetPoint2Coordinate().SetValue(0.94, 0.9)
1488        elif pos == 13:
1489            slider_rep.GetPoint1Coordinate().SetValue(0.065, 0.1)
1490            slider_rep.GetPoint2Coordinate().SetValue(0.065, 0.54)
1491        elif pos == 14:  # bottom-right vertical
1492            slider_rep.GetPoint1Coordinate().SetValue(0.94, 0.1)
1493            slider_rep.GetPoint2Coordinate().SetValue(0.94, 0.54)
1494        elif pos == 15:  # right margin vertical
1495            slider_rep.GetPoint1Coordinate().SetValue(0.95, 0.1)
1496            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.9)
1497        else:  # bottom-right
1498            slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.06)
1499            slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06)
1500
1501        if show_value:
1502            if isinstance(xmin, int) and isinstance(xmax, int) and isinstance(value, int):
1503                frm = "%0.0f"
1504            else:
1505                frm = "%0.2f"
1506
1507            frm = options.pop("tformat", frm)
1508
1509            slider_rep.SetLabelFormat(frm)  # default is '%0.3g'
1510            slider_rep.GetLabelProperty().SetShadow(0)
1511            slider_rep.GetLabelProperty().SetBold(0)
1512            slider_rep.GetLabelProperty().SetOpacity(alpha)
1513            slider_rep.GetLabelProperty().SetColor(c)
1514            if isinstance(pos, int) and pos > 10:
1515                slider_rep.GetLabelProperty().SetOrientation(90)
1516        else:
1517            slider_rep.ShowSliderLabelOff()
1518        slider_rep.GetTubeProperty().SetColor(c)
1519        slider_rep.GetTubeProperty().SetOpacity(0.75)
1520        slider_rep.GetSliderProperty().SetColor(c)
1521        slider_rep.GetSelectedProperty().SetColor(np.sqrt(np.array(c)))
1522        slider_rep.GetCapProperty().SetColor(c)
1523
1524        slider_rep.SetTitleHeight(title_height * title_size)
1525        slider_rep.GetTitleProperty().SetShadow(0)
1526        slider_rep.GetTitleProperty().SetColor(c)
1527        slider_rep.GetTitleProperty().SetOpacity(alpha)
1528        slider_rep.GetTitleProperty().SetBold(0)
1529        if font.lower() == "courier":
1530            slider_rep.GetTitleProperty().SetFontFamilyToCourier()
1531        elif font.lower() == "times":
1532            slider_rep.GetTitleProperty().SetFontFamilyToTimes()
1533        elif font.lower() == "arial":
1534            slider_rep.GetTitleProperty().SetFontFamilyToArial()
1535        else:
1536            if font == "":
1537                font = utils.get_font_path(settings.default_font)
1538            else:
1539                font = utils.get_font_path(font)
1540            slider_rep.GetTitleProperty().SetFontFamily(vtk.VTK_FONT_FILE)
1541            slider_rep.GetLabelProperty().SetFontFamily(vtk.VTK_FONT_FILE)
1542            slider_rep.GetTitleProperty().SetFontFile(font)
1543            slider_rep.GetLabelProperty().SetFontFile(font)
1544
1545        if title:
1546            slider_rep.SetTitleText(title)
1547            if not utils.is_sequence(pos):
1548                if isinstance(pos, int) and pos > 10:
1549                    slider_rep.GetTitleProperty().SetOrientation(90)
1550            else:
1551                if abs(pos[0][0] - pos[1][0]) < 0.1:
1552                    slider_rep.GetTitleProperty().SetOrientation(90)
1553
1554        SliderWidget.__init__(self)
1555
1556        self.SetAnimationModeToJump()
1557        self.SetRepresentation(slider_rep)
1558        if delayed:
1559            self.AddObserver("EndInteractionEvent", sliderfunc)
1560        else:
1561            self.AddObserver("InteractionEvent", sliderfunc)

Add a slider which can call an external custom function. Set any value as float to increase the number of significant digits above the slider.

Use play() to start an animation between the current slider value and the last value.

Arguments:
  • sliderfunc : (function) 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) height of the title
  • tformat : (str) format of the title
Examples:

Inherited Members
SliderWidget
on
off
class Slider3D(SliderWidget):
1565class Slider3D(SliderWidget):
1566    """
1567    Add a 3D slider which can call an external custom function.
1568    """
1569
1570    def __init__(
1571        self,
1572        sliderfunc,
1573        pos1,
1574        pos2,
1575        xmin,
1576        xmax,
1577        value=None,
1578        s=0.03,
1579        t=1,
1580        title="",
1581        rotation=0,
1582        c=None,
1583        show_value=True,
1584    ):
1585        """
1586        Add a 3D slider which can call an external custom function.
1587
1588        Arguments:
1589            sliderfunc : (function)
1590                external function to be called by the widget
1591            pos1 : (list)
1592                first position 3D coordinates
1593            pos2 : (list)
1594                second position 3D coordinates
1595            xmin : (float)
1596                lower value
1597            xmax : (float)
1598                upper value
1599            value : (float)
1600                initial value
1601            s : (float)
1602                label scaling factor
1603            t : (float)
1604                tube scaling factor
1605            title : (str)
1606                title text
1607            c : (color)
1608                slider color
1609            rotation : (float)
1610                title rotation around slider axis
1611            show_value : (bool)
1612                if True current value is shown on top of the slider
1613
1614        Examples:
1615            - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py)
1616        """
1617        c = get_color(c)
1618
1619        if value is None or value < xmin:
1620            value = xmin
1621
1622        slider_rep = vtk.vtkSliderRepresentation3D()
1623        slider_rep.SetMinimumValue(xmin)
1624        slider_rep.SetMaximumValue(xmax)
1625        slider_rep.SetValue(value)
1626
1627        slider_rep.GetPoint1Coordinate().SetCoordinateSystemToWorld()
1628        slider_rep.GetPoint2Coordinate().SetCoordinateSystemToWorld()
1629        slider_rep.GetPoint1Coordinate().SetValue(pos2)
1630        slider_rep.GetPoint2Coordinate().SetValue(pos1)
1631
1632        # slider_rep.SetPoint1InWorldCoordinates(pos2[0], pos2[1], pos2[2])
1633        # slider_rep.SetPoint2InWorldCoordinates(pos1[0], pos1[1], pos1[2])
1634
1635        slider_rep.SetSliderWidth(0.03 * t)
1636        slider_rep.SetTubeWidth(0.01 * t)
1637        slider_rep.SetSliderLength(0.04 * t)
1638        slider_rep.SetSliderShapeToCylinder()
1639        slider_rep.GetSelectedProperty().SetColor(np.sqrt(np.array(c)))
1640        slider_rep.GetSliderProperty().SetColor(np.array(c) / 1.5)
1641        slider_rep.GetCapProperty().SetOpacity(0)
1642        slider_rep.SetRotation(rotation)
1643
1644        if not show_value:
1645            slider_rep.ShowSliderLabelOff()
1646
1647        slider_rep.SetTitleText(title)
1648        slider_rep.SetTitleHeight(s * t)
1649        slider_rep.SetLabelHeight(s * t * 0.85)
1650
1651        slider_rep.GetTubeProperty().SetColor(c)
1652
1653        SliderWidget.__init__(self)
1654
1655        self.SetRepresentation(slider_rep)
1656        self.SetAnimationModeToJump()
1657        self.AddObserver("InteractionEvent", sliderfunc)

Add a 3D slider which can call an external custom function.

Slider3D( sliderfunc, pos1, pos2, xmin, xmax, value=None, s=0.03, t=1, title='', rotation=0, c=None, show_value=True)
1570    def __init__(
1571        self,
1572        sliderfunc,
1573        pos1,
1574        pos2,
1575        xmin,
1576        xmax,
1577        value=None,
1578        s=0.03,
1579        t=1,
1580        title="",
1581        rotation=0,
1582        c=None,
1583        show_value=True,
1584    ):
1585        """
1586        Add a 3D slider which can call an external custom function.
1587
1588        Arguments:
1589            sliderfunc : (function)
1590                external function to be called by the widget
1591            pos1 : (list)
1592                first position 3D coordinates
1593            pos2 : (list)
1594                second position 3D coordinates
1595            xmin : (float)
1596                lower value
1597            xmax : (float)
1598                upper value
1599            value : (float)
1600                initial value
1601            s : (float)
1602                label scaling factor
1603            t : (float)
1604                tube scaling factor
1605            title : (str)
1606                title text
1607            c : (color)
1608                slider color
1609            rotation : (float)
1610                title rotation around slider axis
1611            show_value : (bool)
1612                if True current value is shown on top of the slider
1613
1614        Examples:
1615            - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py)
1616        """
1617        c = get_color(c)
1618
1619        if value is None or value < xmin:
1620            value = xmin
1621
1622        slider_rep = vtk.vtkSliderRepresentation3D()
1623        slider_rep.SetMinimumValue(xmin)
1624        slider_rep.SetMaximumValue(xmax)
1625        slider_rep.SetValue(value)
1626
1627        slider_rep.GetPoint1Coordinate().SetCoordinateSystemToWorld()
1628        slider_rep.GetPoint2Coordinate().SetCoordinateSystemToWorld()
1629        slider_rep.GetPoint1Coordinate().SetValue(pos2)
1630        slider_rep.GetPoint2Coordinate().SetValue(pos1)
1631
1632        # slider_rep.SetPoint1InWorldCoordinates(pos2[0], pos2[1], pos2[2])
1633        # slider_rep.SetPoint2InWorldCoordinates(pos1[0], pos1[1], pos1[2])
1634
1635        slider_rep.SetSliderWidth(0.03 * t)
1636        slider_rep.SetTubeWidth(0.01 * t)
1637        slider_rep.SetSliderLength(0.04 * t)
1638        slider_rep.SetSliderShapeToCylinder()
1639        slider_rep.GetSelectedProperty().SetColor(np.sqrt(np.array(c)))
1640        slider_rep.GetSliderProperty().SetColor(np.array(c) / 1.5)
1641        slider_rep.GetCapProperty().SetOpacity(0)
1642        slider_rep.SetRotation(rotation)
1643
1644        if not show_value:
1645            slider_rep.ShowSliderLabelOff()
1646
1647        slider_rep.SetTitleText(title)
1648        slider_rep.SetTitleHeight(s * t)
1649        slider_rep.SetLabelHeight(s * t * 0.85)
1650
1651        slider_rep.GetTubeProperty().SetColor(c)
1652
1653        SliderWidget.__init__(self)
1654
1655        self.SetRepresentation(slider_rep)
1656        self.SetAnimationModeToJump()
1657        self.AddObserver("InteractionEvent", sliderfunc)

Add a 3D slider 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 3D 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 on top of the slider
Examples:
Inherited Members
SliderWidget
on
off
class Icon(vtkmodules.vtkInteractionWidgets.vtkOrientationMarkerWidget):
2236class Icon(vtk.vtkOrientationMarkerWidget):
2237    """
2238    Add an inset icon mesh into the renderer.
2239    """
2240
2241    def __init__(self, mesh, pos=3, size=0.08):
2242        """
2243        Arguments:
2244            pos : (list, int)
2245                icon position in the range [1-4] indicating one of the 4 corners,
2246                or it can be a tuple (x,y) as a fraction of the renderer size.
2247            size : (float)
2248                size of the icon space as fraction of the window size.
2249
2250        Examples:
2251            - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py)
2252        """
2253        vtk.vtkOrientationMarkerWidget.__init__(self)
2254        self.SetOrientationMarker(mesh)
2255
2256        if utils.is_sequence(pos):
2257            self.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size)
2258        else:
2259            if pos < 2:
2260                self.SetViewport(0, 1 - 2 * size, size * 2, 1)
2261            elif pos == 2:
2262                self.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1)
2263            elif pos == 3:
2264                self.SetViewport(0, 0, size * 2, size * 2)
2265            elif pos == 4:
2266                self.SetViewport(1 - 2 * size, 0, 1, size * 2)

Add an inset icon mesh into the renderer.

Icon(mesh, pos=3, size=0.08)
2241    def __init__(self, mesh, pos=3, size=0.08):
2242        """
2243        Arguments:
2244            pos : (list, int)
2245                icon position in the range [1-4] indicating one of the 4 corners,
2246                or it can be a tuple (x,y) as a fraction of the renderer size.
2247            size : (float)
2248                size of the icon space as fraction of the window size.
2249
2250        Examples:
2251            - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py)
2252        """
2253        vtk.vtkOrientationMarkerWidget.__init__(self)
2254        self.SetOrientationMarker(mesh)
2255
2256        if utils.is_sequence(pos):
2257            self.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size)
2258        else:
2259            if pos < 2:
2260                self.SetViewport(0, 1 - 2 * size, size * 2, 1)
2261            elif pos == 2:
2262                self.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1)
2263            elif pos == 3:
2264                self.SetViewport(0, 0, size * 2, size * 2)
2265            elif pos == 4:
2266                self.SetViewport(1 - 2 * size, 0, 1, size * 2)
Arguments:
  • pos : (list, int) 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 icon space as fraction of the window size.
Examples:
class LegendBox(vedo.shapes.TextBase, vtkmodules.vtkRenderingAnnotation.vtkLegendBoxActor):
213class LegendBox(shapes.TextBase, vtk.vtkLegendBoxActor):
214    """
215    Create a 2D legend box.
216    """
217    def __init__(
218        self,
219        entries=(),
220        nmax=12,
221        c=None,
222        font="",
223        width=0.18,
224        height=None,
225        padding=2,
226        bg="k8",
227        alpha=0.25,
228        pos="top-right",
229        markers=None,
230    ):
231        """
232        Create a 2D legend box for the list of specified objects.
233
234        Arguments:
235            nmax : (int)
236                max number of legend entries
237            c : (color)
238                text color, leave as None to pick the mesh color automatically
239            font : (str)
240                Check [available fonts here](https://vedo.embl.es/fonts)
241            width : (float)
242                width of the box as fraction of the window width
243            height : (float)
244                height of the box as fraction of the window height
245            padding : (int)
246                padding space in units of pixels
247            bg : (color)
248                background color of the box
249            alpha: (float)
250                opacity of the box
251            pos : (str, list)
252                position of the box, can be either a string or a (x,y) screen position in range [0,1]
253
254        Examples:
255            - [legendbox.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/legendbox.py)
256            - [flag_labels1.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels1.py)
257            - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels2.py)
258
259                ![](https://vedo.embl.es/images/other/flag_labels.png)
260        """
261        vtk.vtkLegendBoxActor.__init__(self)
262        shapes.TextBase.__init__(self)
263
264        self.name = "LegendBox"
265        self.entries = entries[:nmax]
266        self.property = self.GetEntryTextProperty()
267
268        n = 0
269        texts = []
270        for e in self.entries:
271            ename = e.name
272            if "legend" in e.info.keys():
273                if not e.info["legend"]:
274                    ename = ""
275                else:
276                    ename = str(e.info["legend"])
277
278            if not isinstance(e, vtk.vtkActor):
279                ename = ""
280            if ename:
281                n += 1
282            texts.append(ename)
283        self.SetNumberOfEntries(n)
284
285        if not n:
286            return
287
288        self.ScalarVisibilityOff()
289        self.PickableOff()
290        self.SetPadding(padding)
291
292        self.property.ShadowOff()
293        self.property.BoldOff()
294
295        # self.property.SetJustificationToLeft() # no effect
296        # self.property.SetVerticalJustificationToTop()
297
298        if not font:
299            font = settings.default_font
300
301        self.font(font)
302
303        n = 0
304        for i in range(len(self.entries)):
305            ti = texts[i]
306            if not ti:
307                continue
308            e = entries[i]
309            if c is None:
310                col = e.GetProperty().GetColor()
311                if col == (1, 1, 1):
312                    col = (0.2, 0.2, 0.2)
313            else:
314                col = get_color(c)
315            if markers is None:  # default
316                poly = e.inputdata()
317            else:
318                marker = markers[i] if utils.is_sequence(markers) else markers
319                if isinstance(marker, vedo.Points):
320                    poly = marker.clone(deep=False).normalize().shift(0, 1, 0).polydata()
321                else:  # assume string marker
322                    poly = vedo.shapes.Marker(marker, s=1).shift(0, 1, 0).polydata()
323
324            self.SetEntry(n, poly, ti, col)
325            n += 1
326
327        self.SetWidth(width)
328        if height is None:
329            self.SetHeight(width / 3.0 * n)
330        else:
331            self.SetHeight(height)
332
333        sx, sy = 1 - self.GetWidth(), 1 - self.GetHeight()
334        if pos == 1 or ("top" in pos and "left" in pos):
335            self.GetPositionCoordinate().SetValue(0, sy)
336        elif pos == 2 or ("top" in pos and "right" in pos):
337            self.GetPositionCoordinate().SetValue(sx, sy)
338        elif pos == 3 or ("bottom" in pos and "left" in pos):
339            self.GetPositionCoordinate().SetValue(0, 0)
340        elif pos == 4 or ("bottom" in pos and "right" in pos):
341            self.GetPositionCoordinate().SetValue(sx, 0)
342        if alpha:
343            self.UseBackgroundOn()
344            self.SetBackgroundColor(get_color(bg))
345            self.SetBackgroundOpacity(alpha)
346        else:
347            self.UseBackgroundOff()
348        self.LockBorderOn()

Create a 2D legend box.

LegendBox( entries=(), nmax=12, c=None, font='', width=0.18, height=None, padding=2, bg='k8', alpha=0.25, pos='top-right', markers=None)
217    def __init__(
218        self,
219        entries=(),
220        nmax=12,
221        c=None,
222        font="",
223        width=0.18,
224        height=None,
225        padding=2,
226        bg="k8",
227        alpha=0.25,
228        pos="top-right",
229        markers=None,
230    ):
231        """
232        Create a 2D legend box for the list of specified objects.
233
234        Arguments:
235            nmax : (int)
236                max number of legend entries
237            c : (color)
238                text color, leave as None to pick the mesh color automatically
239            font : (str)
240                Check [available fonts here](https://vedo.embl.es/fonts)
241            width : (float)
242                width of the box as fraction of the window width
243            height : (float)
244                height of the box as fraction of the window height
245            padding : (int)
246                padding space in units of pixels
247            bg : (color)
248                background color of the box
249            alpha: (float)
250                opacity of the box
251            pos : (str, list)
252                position of the box, can be either a string or a (x,y) screen position in range [0,1]
253
254        Examples:
255            - [legendbox.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/legendbox.py)
256            - [flag_labels1.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels1.py)
257            - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels2.py)
258
259                ![](https://vedo.embl.es/images/other/flag_labels.png)
260        """
261        vtk.vtkLegendBoxActor.__init__(self)
262        shapes.TextBase.__init__(self)
263
264        self.name = "LegendBox"
265        self.entries = entries[:nmax]
266        self.property = self.GetEntryTextProperty()
267
268        n = 0
269        texts = []
270        for e in self.entries:
271            ename = e.name
272            if "legend" in e.info.keys():
273                if not e.info["legend"]:
274                    ename = ""
275                else:
276                    ename = str(e.info["legend"])
277
278            if not isinstance(e, vtk.vtkActor):
279                ename = ""
280            if ename:
281                n += 1
282            texts.append(ename)
283        self.SetNumberOfEntries(n)
284
285        if not n:
286            return
287
288        self.ScalarVisibilityOff()
289        self.PickableOff()
290        self.SetPadding(padding)
291
292        self.property.ShadowOff()
293        self.property.BoldOff()
294
295        # self.property.SetJustificationToLeft() # no effect
296        # self.property.SetVerticalJustificationToTop()
297
298        if not font:
299            font = settings.default_font
300
301        self.font(font)
302
303        n = 0
304        for i in range(len(self.entries)):
305            ti = texts[i]
306            if not ti:
307                continue
308            e = entries[i]
309            if c is None:
310                col = e.GetProperty().GetColor()
311                if col == (1, 1, 1):
312                    col = (0.2, 0.2, 0.2)
313            else:
314                col = get_color(c)
315            if markers is None:  # default
316                poly = e.inputdata()
317            else:
318                marker = markers[i] if utils.is_sequence(markers) else markers
319                if isinstance(marker, vedo.Points):
320                    poly = marker.clone(deep=False).normalize().shift(0, 1, 0).polydata()
321                else:  # assume string marker
322                    poly = vedo.shapes.Marker(marker, s=1).shift(0, 1, 0).polydata()
323
324            self.SetEntry(n, poly, ti, col)
325            n += 1
326
327        self.SetWidth(width)
328        if height is None:
329            self.SetHeight(width / 3.0 * n)
330        else:
331            self.SetHeight(height)
332
333        sx, sy = 1 - self.GetWidth(), 1 - self.GetHeight()
334        if pos == 1 or ("top" in pos and "left" in pos):
335            self.GetPositionCoordinate().SetValue(0, sy)
336        elif pos == 2 or ("top" in pos and "right" in pos):
337            self.GetPositionCoordinate().SetValue(sx, sy)
338        elif pos == 3 or ("bottom" in pos and "left" in pos):
339            self.GetPositionCoordinate().SetValue(0, 0)
340        elif pos == 4 or ("bottom" in pos and "right" in pos):
341            self.GetPositionCoordinate().SetValue(sx, 0)
342        if alpha:
343            self.UseBackgroundOn()
344            self.SetBackgroundColor(get_color(bg))
345            self.SetBackgroundOpacity(alpha)
346        else:
347            self.UseBackgroundOff()
348        self.LockBorderOn()

Create a 2D legend box for the list of specified objects.

Arguments:
  • nmax : (int) max number of legend entries
  • c : (color) text color, leave as None to pick the mesh color automatically
  • font : (str) Check available fonts here
  • width : (float) width of the box as fraction of the window width
  • height : (float) height of the box as fraction of the window height
  • padding : (int) padding space in units of pixels
  • bg : (color) background color of the box
  • alpha: (float) opacity of the box
  • pos : (str, list) position of the box, can be either a string or a (x,y) screen position in range [0,1]
Examples:
def Light(pos, focal_point=(0, 0, 0), angle=180, c=None, intensity=1):
813def Light(pos, focal_point=(0, 0, 0), angle=180, c=None, intensity=1):
814    """
815    Generate a source of light placed at `pos` and directed to `focal point`.
816    Returns a `vtkLight` object.
817
818    Arguments:
819        focal_point : (list)
820            focal point, if a `vedo` object is passed then will grab its position.
821        angle : (float)
822            aperture angle of the light source, in degrees
823        c : (color)
824            set the light color
825        intensity : (float)
826            intensity value between 0 and 1.
827
828    Check also:
829        `plotter.Plotter.remove_lights()`
830
831    Examples:
832        - [lights.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/lights.py)
833
834            ![](https://vedo.embl.es/images/basic/lights.png)
835    """
836    if c is None:
837        try:
838            c = pos.color()
839        except AttributeError:
840            c = "white"
841
842    if isinstance(pos, vedo.Base3DProp):
843        pos = pos.pos()
844
845    if isinstance(focal_point, vedo.Base3DProp):
846        focal_point = focal_point.pos()
847
848    light = vtk.vtkLight()
849    light.SetLightTypeToSceneLight()
850    light.SetPosition(pos)
851    light.SetConeAngle(angle)
852    light.SetFocalPoint(focal_point)
853    light.SetIntensity(intensity)
854    light.SetColor(get_color(c))
855    return light

Generate a source of light placed at pos and directed to focal point. Returns a vtkLight object.

Arguments:
  • focal_point : (list) focal point, if a vedo object is passed then will grab its position.
  • angle : (float) aperture angle of the light source, in degrees
  • c : (color) set the light color
  • intensity : (float) intensity value between 0 and 1.
Check also:

plotter.Plotter.remove_lights()

Examples:
def Axes( obj=None, xtitle='x', ytitle='y', ztitle='z', xrange=None, yrange=None, zrange=None, c=None, number_of_divisions=None, digits=None, limit_ratio=0.04, title_depth=0, title_font='', text_scale=1.0, x_values_and_labels=None, y_values_and_labels=None, z_values_and_labels=None, htitle='', htitle_size=0.03, htitle_font=None, htitle_italic=False, htitle_color=None, htitle_justify='bottom-center', htitle_rotation=0, htitle_offset=(0, 0.01, 0), xtitle_position=0.95, ytitle_position=0.95, ztitle_position=0.95, xtitle_offset=0.025, ytitle_offset=0.0275, ztitle_offset=0.02, xtitle_justify=None, ytitle_justify=None, ztitle_justify=None, xtitle_rotation=0, ytitle_rotation=0, ztitle_rotation=0, xtitle_box=False, ytitle_box=False, xtitle_size=0.025, ytitle_size=0.025, ztitle_size=0.025, xtitle_color=None, ytitle_color=None, ztitle_color=None, xtitle_backface_color=None, ytitle_backface_color=None, ztitle_backface_color=None, xtitle_italic=0, ytitle_italic=0, ztitle_italic=0, grid_linewidth=1, xygrid=True, yzgrid=False, zxgrid=False, xygrid2=False, yzgrid2=False, zxgrid2=False, xygrid_transparent=False, yzgrid_transparent=False, zxgrid_transparent=False, xygrid2_transparent=False, yzgrid2_transparent=False, zxgrid2_transparent=False, xyplane_color=None, yzplane_color=None, zxplane_color=None, xygrid_color=None, yzgrid_color=None, zxgrid_color=None, xyalpha=0.075, yzalpha=0.075, zxalpha=0.075, xyframe_line=None, yzframe_line=None, zxframe_line=None, xyframe_color=None, yzframe_color=None, zxframe_color=None, axes_linewidth=1, xline_color=None, yline_color=None, zline_color=None, xhighlight_zero=False, yhighlight_zero=False, zhighlight_zero=False, xhighlight_zero_color='red4', yhighlight_zero_color='green4', zhighlight_zero_color='blue4', show_ticks=True, xtick_length=0.015, ytick_length=0.015, ztick_length=0.015, xtick_thickness=0.0025, ytick_thickness=0.0025, ztick_thickness=0.0025, xminor_ticks=1, yminor_ticks=1, zminor_ticks=1, tip_size=None, label_font='', xlabel_color=None, ylabel_color=None, zlabel_color=None, xlabel_size=0.016, ylabel_size=0.016, zlabel_size=0.016, xlabel_offset=0.8, ylabel_offset=0.8, zlabel_offset=0.8, xlabel_justify=None, ylabel_justify=None, zlabel_justify=None, xlabel_rotation=0, ylabel_rotation=0, zlabel_rotation=0, xaxis_rotation=0, yaxis_rotation=0, zaxis_rotation=0, xyshift=0, yzshift=0, zxshift=0, xshift_along_y=0, xshift_along_z=0, yshift_along_x=0, yshift_along_z=0, zshift_along_x=0, zshift_along_y=0, x_use_bounds=True, y_use_bounds=True, z_use_bounds=False, x_inverted=False, y_inverted=False, z_inverted=False, use_global=False, tol=0.001, **options):
2794def Axes(
2795        obj=None,
2796        xtitle='x', ytitle='y', ztitle='z',
2797        xrange=None, yrange=None, zrange=None,
2798        c=None,
2799        number_of_divisions=None,
2800        digits=None,
2801        limit_ratio=0.04,
2802        title_depth=0,
2803        title_font="", # grab settings.default_font
2804        text_scale=1.0,
2805        x_values_and_labels=None, y_values_and_labels=None, z_values_and_labels=None,
2806        htitle="",
2807        htitle_size=0.03,
2808        htitle_font=None,
2809        htitle_italic=False,
2810        htitle_color=None,
2811        htitle_justify='bottom-center',
2812        htitle_rotation=0,
2813        htitle_offset=(0, 0.01, 0),
2814        xtitle_position=0.95, ytitle_position=0.95, ztitle_position=0.95,
2815        xtitle_offset=0.025,  ytitle_offset=0.0275, ztitle_offset=0.02, # can be a list (dx,dy,dz)
2816        xtitle_justify=None,  ytitle_justify=None,  ztitle_justify=None,
2817        xtitle_rotation=0, ytitle_rotation=0, ztitle_rotation=0,         # can be a list (rx,ry,rz)
2818        xtitle_box=False,  ytitle_box=False,
2819        xtitle_size=0.025, ytitle_size=0.025, ztitle_size=0.025,
2820        xtitle_color=None, ytitle_color=None, ztitle_color=None,
2821        xtitle_backface_color=None, ytitle_backface_color=None, ztitle_backface_color=None,
2822        xtitle_italic=0, ytitle_italic=0, ztitle_italic=0,
2823        grid_linewidth=1,
2824        xygrid=True,   yzgrid=False,  zxgrid=False,
2825        xygrid2=False, yzgrid2=False, zxgrid2=False,
2826        xygrid_transparent=False,  yzgrid_transparent=False,  zxgrid_transparent=False,
2827        xygrid2_transparent=False, yzgrid2_transparent=False, zxgrid2_transparent=False,
2828        xyplane_color=None, yzplane_color=None, zxplane_color=None,
2829        xygrid_color=None, yzgrid_color=None, zxgrid_color=None,
2830        xyalpha=0.075, yzalpha=0.075, zxalpha=0.075,
2831        xyframe_line=None, yzframe_line=None, zxframe_line=None,
2832        xyframe_color=None, yzframe_color=None, zxframe_color=None,
2833        axes_linewidth=1,
2834        xline_color=None, yline_color=None, zline_color=None,
2835        xhighlight_zero=False, yhighlight_zero=False, zhighlight_zero=False,
2836        xhighlight_zero_color='red4', yhighlight_zero_color='green4', zhighlight_zero_color='blue4',
2837        show_ticks=True,
2838        xtick_length=0.015, ytick_length=0.015, ztick_length=0.015,
2839        xtick_thickness=0.0025, ytick_thickness=0.0025, ztick_thickness=0.0025,
2840        xminor_ticks=1, yminor_ticks=1, zminor_ticks=1,
2841        tip_size=None,
2842        label_font="", # grab settings.default_font
2843        xlabel_color=None, ylabel_color=None, zlabel_color=None,
2844        xlabel_size=0.016, ylabel_size=0.016, zlabel_size=0.016,
2845        xlabel_offset=0.8, ylabel_offset=0.8, zlabel_offset=0.8, # each can be a list (dx,dy,dz)
2846        xlabel_justify=None, ylabel_justify=None, zlabel_justify=None,
2847        xlabel_rotation=0, ylabel_rotation=0, zlabel_rotation=0, # each can be a list (rx,ry,rz)
2848        xaxis_rotation=0, yaxis_rotation=0, zaxis_rotation=0,    # rotate all elements around axis
2849        xyshift=0, yzshift=0, zxshift=0,
2850        xshift_along_y=0, xshift_along_z=0,
2851        yshift_along_x=0, yshift_along_z=0,
2852        zshift_along_x=0, zshift_along_y=0,
2853        x_use_bounds=True, y_use_bounds=True, z_use_bounds=False,
2854        x_inverted=False, y_inverted=False, z_inverted=False,
2855        use_global=False,
2856        tol=0.001,
2857        **options,
2858    ):
2859    """
2860    Draw axes for the input object.
2861    Check [available fonts here](https://vedo.embl.es/fonts).
2862
2863    Returns an `vedo.Assembly` object.
2864
2865    Parameters
2866    ----------
2867
2868    - `xtitle`,                  ['x'], x-axis title text
2869    - `xrange`,                 [None], x-axis range in format (xmin, ymin), default is automatic.
2870    - `number_of_divisions`,    [None], approximate number of divisions on the longest axis
2871    - `axes_linewidth`,           [1], width of the axes lines
2872    - `grid_linewidth`,           [1], width of the grid lines
2873    - `title_depth`,              [0], extrusion fractional depth of title text
2874    - `x_values_and_labels`        [], assign custom tick positions and labels [(pos1, label1), ...]
2875    - `xygrid`,                [True], show a gridded wall on plane xy
2876    - `yzgrid`,                [True], show a gridded wall on plane yz
2877    - `zxgrid`,                [True], show a gridded wall on plane zx
2878    - `zxgrid2`,              [False], show zx plane on opposite side of the bounding box
2879    - `xygrid_transparent`    [False], make grid plane completely transparent
2880    - `xygrid2_transparent`   [False], make grid plane completely transparent on opposite side box
2881    - `xyplane_color`,       ['None'], color of the plane
2882    - `xygrid_color`,        ['None'], grid line color
2883    - `xyalpha`,               [0.15], grid plane opacity
2884    - `xyframe_line`,             [0], add a frame for the plane, use value as the thickness
2885    - `xyframe_color`,         [None], color for the frame of the plane
2886    - `show_ticks`,            [True], show major ticks
2887    - `digits`,                [None], use this number of significant digits in scientific notation
2888    - `title_font`,              [''], font for axes titles
2889    - `label_font`,              [''], font for numeric labels
2890    - `text_scale`,             [1.0], global scaling factor for all text elements (titles, labels)
2891    - `htitle`,                  [''], header title
2892    - `htitle_size`,           [0.03], header title size
2893    - `htitle_font`,           [None], header font (defaults to `title_font`)
2894    - `htitle_italic`,         [True], header font is italic
2895    - `htitle_color`,          [None], header title color (defaults to `xtitle_color`)
2896    - `htitle_justify`, ['bottom-center'], origin of the title justification
2897    - `htitle_offset`,   [(0,0.01,0)], control offsets of header title in x, y and z
2898    - `xtitle_position`,       [0.32], title fractional positions along axis
2899    - `xtitle_offset`,         [0.05], title fractional offset distance from axis line, can be a list
2900    - `xtitle_justify`,        [None], choose the origin of the bounding box of title
2901    - `xtitle_rotation`,          [0], add a rotation of the axis title, can be a list (rx,ry,rz)
2902    - `xtitle_box`,           [False], add a box around title text
2903    - `xline_color`,      [automatic], color of the x-axis
2904    - `xtitle_color`,     [automatic], color of the axis title
2905    - `xtitle_backface_color`, [None], color of axis title on its backface
2906    - `xtitle_size`,          [0.025], size of the axis title
2907    - `xtitle_italic`,            [0], a bool or float to make the font italic
2908    - `xhighlight_zero`,       [True], draw a line highlighting zero position if in range
2909    - `xhighlight_zero_color`, [autom], color of the line highlighting the zero position
2910    - `xtick_length`,         [0.005], radius of the major ticks
2911    - `xtick_thickness`,     [0.0025], thickness of the major ticks along their axis
2912    - `xminor_ticks`,             [1], number of minor ticks between two major ticks
2913    - `xlabel_color`,     [automatic], color of numeric labels and ticks
2914    - `xLabelPrecision`,          [2], nr. of significative digits to be shown
2915    - `xlabel_size`,          [0.015], size of the numeric labels along axis
2916    - `xlabel_rotation`,          [0], numeric labels rotation (can be a list of 3 rotations)
2917    - `xlabel_offset`,          [0.8], offset of the numeric labels (can be a list of 3 offsets)
2918    - `xlabel_justify`,        [None], choose the origin of the bounding box of labels
2919    - `xaxis_rotation`,           [0], rotate the X axis elements (ticks and labels) around this same axis
2920    - `xyshift`                 [0.0], slide the xy-plane along z (the range is [0,1])
2921    - `xshift_along_y`           [0.0], slide x-axis along the y-axis (the range is [0,1])
2922    - `tip_size`,               [0.01], size of the arrow tip
2923    - `limit_ratio`,            [0.04], below this ratio don't plot smaller axis
2924    - `x_use_bounds`,           [True], keep into account space occupied by labels when setting camera
2925    - `x_inverted`,            [False], invert labels order and direction (only visually!)
2926    - `use_global`,             [False], try to compute the global bounding box of visible actors
2927
2928    Example:
2929        ```python
2930        from vedo import Axes, Box, show
2931        box = Box(pos=(1,2,3), length=8, width=9, height=7).alpha(0.1)
2932        axs = Axes(box, c='k')  # returns an Assembly object
2933        for a in axs.unpack():
2934            print(a.name)
2935        show(box, axs).close()
2936        ```
2937        ![](https://vedo.embl.es/images/feaxes1ats/axes1.png)
2938
2939    Examples:
2940        - [custom_axes1.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes1.py)
2941        - [custom_axes2.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes2.py)
2942        - [custom_axes3.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes3.py)
2943        - [custom_axes4.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes4.py)
2944
2945        ![](https://vedo.embl.es/images/pyplot/customAxes3.png)
2946    """
2947    # make sure options are correct and user passed snake_case instead of camelCase
2948    if len(options):
2949        for k in options:
2950            if k.lower() == k:
2951                vedo.logger.warning(f"Unrecognised keyword '{k}' is ignored")
2952            else:
2953                vedo.logger.warning(f"Unrecognised keyword '{k}'. Please use snake_case notation")
2954
2955    if not title_font:
2956        title_font = settings.default_font
2957    if not label_font:
2958        label_font = settings.default_font
2959
2960    if c is None:  # automatic black or white
2961        c = (0.1, 0.1, 0.1)
2962        plt = vedo.plotter_instance
2963        if plt and plt.renderer:
2964            bgcol = plt.renderer.GetBackground()
2965        else:
2966            bgcol = (1, 1, 1)
2967        if np.sum(bgcol) < 1.5:
2968            c = (0.9, 0.9, 0.9)
2969    else:
2970        c = get_color(c)
2971
2972    if use_global:
2973        vbb, drange, min_bns, max_bns = compute_visible_bounds()
2974    else:
2975        if obj is not None:
2976            vbb, drange, min_bns, max_bns = compute_visible_bounds(obj)
2977        else:
2978            vbb = np.zeros(6)
2979            drange = np.zeros(3)
2980            if zrange is None:
2981                zrange = (0, 0)
2982            if xrange is None or yrange is None:
2983                vedo.logger.error("in Axes() must specify axes ranges!")
2984                raise RuntimeError()
2985
2986    if xrange is not None:
2987        if xrange[1] < xrange[0]:
2988            x_inverted = True
2989            xrange = [xrange[1], xrange[0]]
2990        vbb[0], vbb[1] = xrange
2991        drange[0] = vbb[1] - vbb[0]
2992        min_bns = vbb
2993        max_bns = vbb
2994    if yrange is not None:
2995        if yrange[1] < yrange[0]:
2996            y_inverted = True
2997            yrange = [yrange[1], yrange[0]]
2998        vbb[2], vbb[3] = yrange
2999        drange[1] = vbb[3] - vbb[2]
3000        min_bns = vbb
3001        max_bns = vbb
3002    if zrange is not None:
3003        if zrange[1] < zrange[0]:
3004            z_inverted = True
3005            zrange = [zrange[1], zrange[0]]
3006        vbb[4], vbb[5] = zrange
3007        drange[2] = vbb[5] - vbb[4]
3008        min_bns = vbb
3009        max_bns = vbb
3010
3011    drangemax = max(drange)
3012    if not drangemax:
3013        return None
3014
3015    if drange[0] / drangemax < limit_ratio:
3016        drange[0] = 0
3017        xtitle = ""
3018    if drange[1] / drangemax < limit_ratio:
3019        drange[1] = 0
3020        ytitle = ""
3021    if drange[2] / drangemax < limit_ratio:
3022        drange[2] = 0
3023        ztitle = ""
3024
3025    x0, x1, y0, y1, z0, z1 = vbb
3026    dx, dy, dz = drange
3027
3028    gscale = np.sqrt(dx * dx + dy * dy + dz * dz) * 0.75
3029
3030    if not xyplane_color: xyplane_color = c
3031    if not yzplane_color: yzplane_color = c
3032    if not zxplane_color: zxplane_color = c
3033    if not xygrid_color:  xygrid_color = c
3034    if not yzgrid_color:  yzgrid_color = c
3035    if not zxgrid_color:  zxgrid_color = c
3036    if not xtitle_color:  xtitle_color = c
3037    if not ytitle_color:  ytitle_color = c
3038    if not ztitle_color:  ztitle_color = c
3039    if not xline_color:   xline_color = c
3040    if not yline_color:   yline_color = c
3041    if not zline_color:   zline_color = c
3042    if not xlabel_color:  xlabel_color = xline_color
3043    if not ylabel_color:  ylabel_color = yline_color
3044    if not zlabel_color:  zlabel_color = zline_color
3045
3046    # vtk version<9 dont like depthpeeling: force switching off grids
3047    if settings.use_depth_peeling and not utils.vtk_version_at_least(9):
3048        xygrid = False
3049        yzgrid = False
3050        zxgrid = False
3051        xygrid2 = False
3052        yzgrid2 = False
3053        zxgrid2 = False
3054
3055    if tip_size is None:
3056        tip_size = 0.005 * gscale
3057        if not ztitle:
3058            tip_size = 0  # switch off in xy 2d
3059
3060    ndiv = 4
3061    if not ztitle or not ytitle or not xtitle:  # make more default ticks if 2D
3062        ndiv = 6
3063        if not ztitle:
3064            if xyframe_line is None:
3065                xyframe_line = True
3066            if tip_size is None:
3067                tip_size = False
3068
3069    if utils.is_sequence(number_of_divisions):
3070        rx, ry, rz = number_of_divisions
3071    else:
3072        if not number_of_divisions:
3073            number_of_divisions = ndiv
3074
3075    rx, ry, rz = np.ceil(drange / drangemax * number_of_divisions).astype(int)
3076
3077    if xtitle:
3078        xticks_float, xticks_str = utils.make_ticks(x0, x1, rx, x_values_and_labels, digits)
3079        xticks_float = xticks_float * dx
3080        if x_inverted:
3081            xticks_float = np.flip(-(xticks_float - xticks_float[-1]))
3082            xticks_str = list(reversed(xticks_str))
3083            xticks_str[-1] = ""
3084            xhighlight_zero = False
3085    if ytitle:
3086        yticks_float, yticks_str = utils.make_ticks(y0, y1, ry, y_values_and_labels, digits)
3087        yticks_float = yticks_float * dy
3088        if y_inverted:
3089            yticks_float = np.flip(-(yticks_float - yticks_float[-1]))
3090            yticks_str = list(reversed(yticks_str))
3091            yticks_str[-1] = ""
3092            yhighlight_zero = False
3093    if ztitle:
3094        zticks_float, zticks_str = utils.make_ticks(z0, z1, rz, z_values_and_labels, digits)
3095        zticks_float = zticks_float * dz
3096        if z_inverted:
3097            zticks_float = np.flip(-(zticks_float - zticks_float[-1]))
3098            zticks_str = list(reversed(zticks_str))
3099            zticks_str[-1] = ""
3100            zhighlight_zero = False
3101
3102    ################################################ axes lines
3103    lines = []
3104    if xtitle:
3105        axlinex = shapes.Line([0,0,0], [dx,0,0], c=xline_color, lw=axes_linewidth)
3106        if xyshift: axlinex.shift(0,0,xyshift*dz)
3107        if zxshift: axlinex.shift(0,zxshift*dy,0)
3108        if xshift_along_y: axlinex.shift(0,xshift_along_y*dy,0)
3109        if xshift_along_z: axlinex.shift(0,0,xshift_along_z*dz)
3110        axlinex.name = 'xAxis'
3111        lines.append(axlinex)
3112    if ytitle:
3113        axliney = shapes.Line([0,0,0], [0,dy,0], c=yline_color, lw=axes_linewidth)
3114        if xyshift: axliney.shift(0,0,xyshift*dz)
3115        if yzshift: axliney.shift(yzshift*dx,0,0)
3116        if yshift_along_x: axliney.shift(yshift_along_x*dx,0,0)
3117        if yshift_along_z: axliney.shift(0,0,yshift_along_z*dz)
3118        axliney.name = 'yAxis'
3119        lines.append(axliney)
3120    if ztitle:
3121        axlinez = shapes.Line([0,0,0], [0,0,dz], c=zline_color, lw=axes_linewidth)
3122        if yzshift: axlinez.shift(yzshift*dx,0,0)
3123        if zxshift: axlinez.shift(0,zxshift*dy,0)
3124        if zshift_along_x: axlinez.shift(zshift_along_x*dx,0,0)
3125        if zshift_along_y: axlinez.shift(0,zshift_along_y*dy,0)
3126        axlinez.name = 'zAxis'
3127        lines.append(axlinez)
3128
3129    ################################################ grid planes
3130    # all shapes have a name to keep track of them in the Assembly
3131    # if user wants to unpack it
3132    grids = []
3133    if xygrid and xtitle and ytitle:
3134        if not xygrid_transparent:
3135            gxy = shapes.Grid(s=(xticks_float, yticks_float))
3136            gxy.alpha(xyalpha).c(xyplane_color).lw(0)
3137            if xyshift: gxy.shift(0,0,xyshift*dz)
3138            elif tol:   gxy.shift(0,0,-tol*gscale)
3139            gxy.name = "xyGrid"
3140            grids.append(gxy)
3141        if grid_linewidth:
3142            gxy_lines = shapes.Grid(s=(xticks_float, yticks_float))
3143            gxy_lines.c(xyplane_color).lw(grid_linewidth).alpha(xyalpha)
3144            if xyshift: gxy_lines.shift(0,0,xyshift*dz)
3145            elif tol:   gxy_lines.shift(0,0,-tol*gscale)
3146            gxy_lines.name = "xyGridLines"
3147            grids.append(gxy_lines)
3148
3149    if yzgrid and ytitle and ztitle:
3150        if not yzgrid_transparent:
3151            gyz = shapes.Grid(s=(zticks_float, yticks_float))
3152            gyz.alpha(yzalpha).c(yzplane_color).lw(0).RotateY(-90)
3153            if yzshift: gyz.shift(yzshift*dx,0,0)
3154            elif tol:   gyz.shift(-tol*gscale,0,0)
3155            gyz.name = "yzGrid"
3156            grids.append(gyz)
3157        if grid_linewidth:
3158            gyz_lines = shapes.Grid(s=(zticks_float, yticks_float))
3159            gyz_lines.c(yzplane_color).lw(grid_linewidth).alpha(yzalpha).RotateY(-90)
3160            if yzshift: gyz_lines.shift(yzshift*dx,0,0)
3161            elif tol:   gyz_lines.shift(-tol*gscale,0,0)
3162            gyz_lines.name = "yzGridLines"
3163            grids.append(gyz_lines)
3164
3165    if zxgrid and ztitle and xtitle:
3166        if not zxgrid_transparent:
3167            gzx = shapes.Grid(s=(xticks_float, zticks_float))
3168            gzx.alpha(zxalpha).c(zxplane_color).lw(0).RotateX(90)
3169            if zxshift: gzx.shift(0,zxshift*dy,0)
3170            elif tol:   gzx.shift(0,-tol*gscale,0)
3171            gzx.name = "zxGrid"
3172            grids.append(gzx)
3173        if grid_linewidth:
3174            gzx_lines = shapes.Grid(s=(xticks_float, zticks_float))
3175            gzx_lines.c(zxplane_color).lw(grid_linewidth).alpha(zxalpha).RotateX(90)
3176            if zxshift: gzx_lines.shift(0,zxshift*dy,0)
3177            elif tol:   gzx_lines.shift(0,-tol*gscale,0)
3178            gzx_lines.name = "zxGridLines"
3179            grids.append(gzx_lines)
3180
3181    # Grid2
3182    if xygrid2 and xtitle and ytitle:
3183        if not xygrid2_transparent:
3184            gxy2 = shapes.Grid(s=(xticks_float, yticks_float)).z(dz)
3185            gxy2.alpha(xyalpha).c(xyplane_color).lw(0)
3186            if tol: gxy2.shift(0,tol*gscale,0)
3187            gxy2.name = "xyGrid2"
3188            grids.append(gxy2)
3189        if grid_linewidth:
3190            gxy2_lines = shapes.Grid(s=(xticks_float, yticks_float)).z(dz)
3191            gxy2_lines.c(xyplane_color).lw(grid_linewidth).alpha(xyalpha)
3192            if tol: gxy2_lines.shift(0,tol*gscale,0)
3193            gxy2_lines.name = "xygrid2Lines"
3194            grids.append(gxy2_lines)
3195
3196    if yzgrid2 and ytitle and ztitle:
3197        if not yzgrid2_transparent:
3198            gyz2 = shapes.Grid(s=(zticks_float, yticks_float)).x(dx)
3199            gyz2.alpha(yzalpha).c(yzplane_color).lw(0).RotateY(-90)
3200            if tol: gyz2.shift(tol*gscale,0,0)
3201            gyz2.name = "yzGrid2"
3202            grids.append(gyz2)
3203        if grid_linewidth:
3204            gyz2_lines = shapes.Grid(s=(zticks_float, yticks_float)).x(dx)
3205            gyz2_lines.c(yzplane_color).lw(grid_linewidth).alpha(yzalpha).RotateY(-90)
3206            if tol: gyz2_lines.shift(tol*gscale,0,0)
3207            gyz2_lines.name = "yzGrid2Lines"
3208            grids.append(gyz2_lines)
3209
3210    if zxgrid2 and ztitle and xtitle:
3211        if not zxgrid2_transparent:
3212            gzx2 = shapes.Grid(s=(xticks_float, zticks_float)).y(dy)
3213            gzx2.alpha(zxalpha).c(zxplane_color).lw(0).RotateX(90)
3214            if tol: gzx2.shift(0,tol*gscale,0)
3215            gzx2.name = "zxGrid2"
3216            grids.append(gzx2)
3217        if grid_linewidth:
3218            gzx2_lines = shapes.Grid(s=(xticks_float, zticks_float)).y(dy)
3219            gzx2_lines.c(zxplane_color).lw(grid_linewidth).alpha(zxalpha).RotateX(90)
3220            if tol: gzx2_lines.shift(0,tol*gscale,0)
3221            gzx2_lines.name = "zxGrid2Lines"
3222            grids.append(gzx2_lines)
3223
3224    ################################################ frame lines
3225    framelines = []
3226    if xyframe_line and xtitle and ytitle:
3227        if not xyframe_color:
3228            xyframe_color = xygrid_color
3229        frxy = shapes.Line([[0,dy,0],[dx,dy,0],[dx,0,0],[0,0,0],[0,dy,0]],
3230                           c=xyframe_color, lw=xyframe_line)
3231        if xyshift: frxy.shift(0,0,xyshift*dz)
3232        frxy.name = 'xyFrameLine'
3233        framelines.append(frxy)
3234    if yzframe_line and ytitle and ztitle:
3235        if not yzframe_color:
3236            yzframe_color = yzgrid_color
3237        fryz = shapes.Line([[0,0,dz],[0,dy,dz],[0,dy,0],[0,0,0],[0,0,dz]],
3238                           c=yzframe_color, lw=yzframe_line)
3239        if yzshift: fryz.shift(yzshift*dx,0,0)
3240        fryz.name = 'yzFrameLine'
3241        framelines.append(fryz)
3242    if zxframe_line and ztitle and xtitle:
3243        if not zxframe_color:
3244            zxframe_color = zxgrid_color
3245        frzx = shapes.Line([[0,0,dz],[dx,0,dz],[dx,0,0],[0,0,0],[0,0,dz]],
3246                           c=zxframe_color, lw=zxframe_line)
3247        if zxshift: frzx.shift(0,zxshift*dy,0)
3248        frzx.name = 'zxFrameLine'
3249        framelines.append(frzx)
3250
3251    ################################################ zero lines highlights
3252    highlights = []
3253    if xygrid and xtitle and ytitle:
3254        if xhighlight_zero and min_bns[0] <= 0 and max_bns[1] > 0:
3255            xhl = -min_bns[0]
3256            hxy = shapes.Line([xhl,0,0], [xhl,dy,0], c=xhighlight_zero_color)
3257            hxy.alpha(np.sqrt(xyalpha)).lw(grid_linewidth*2)
3258            if xyshift: hxy.shift(0,0,xyshift*dz)
3259            hxy.name = "xyHighlightZero"
3260            highlights.append(hxy)
3261        if yhighlight_zero and min_bns[2] <= 0 and max_bns[3] > 0:
3262            yhl = -min_bns[2]
3263            hyx = shapes.Line([0,yhl,0], [dx,yhl,0], c=yhighlight_zero_color)
3264            hyx.alpha(np.sqrt(yzalpha)).lw(grid_linewidth*2)
3265            if xyshift: hyx.shift(0,0,xyshift*dz)
3266            hyx.name = "yxHighlightZero"
3267            highlights.append(hyx)
3268
3269    if yzgrid and ytitle and ztitle:
3270        if yhighlight_zero and min_bns[2] <= 0 and max_bns[3] > 0:
3271            yhl = -min_bns[2]
3272            hyz = shapes.Line([0,yhl,0], [0,yhl,dz], c=yhighlight_zero_color)
3273            hyz.alpha(np.sqrt(yzalpha)).lw(grid_linewidth*2)
3274            if yzshift: hyz.shift(yzshift*dx,0,0)
3275            hyz.name = "yzHighlightZero"
3276            highlights.append(hyz)
3277        if zhighlight_zero and min_bns[4] <= 0 and max_bns[5] > 0:
3278            zhl = -min_bns[4]
3279            hzy = shapes.Line([0,0,zhl], [0,dy,zhl], c=zhighlight_zero_color)
3280            hzy.alpha(np.sqrt(yzalpha)).lw(grid_linewidth*2)
3281            if yzshift: hzy.shift(yzshift*dx,0,0)
3282            hzy.name = "zyHighlightZero"
3283            highlights.append(hzy)
3284
3285    if zxgrid and ztitle and xtitle:
3286        if zhighlight_zero and min_bns[4] <= 0 and max_bns[5] > 0:
3287            zhl = -min_bns[4]
3288            hzx = shapes.Line([0,0,zhl], [dx,0,zhl], c=zhighlight_zero_color)
3289            hzx.alpha(np.sqrt(zxalpha)).lw(grid_linewidth*2)
3290            if zxshift: hzx.shift(0,zxshift*dy,0)
3291            hzx.name = "zxHighlightZero"
3292            highlights.append(hzx)
3293        if xhighlight_zero and min_bns[0] <= 0 and max_bns[1] > 0:
3294            xhl = -min_bns[0]
3295            hxz = shapes.Line([xhl,0,0], [xhl,0,dz], c=xhighlight_zero_color)
3296            hxz.alpha(np.sqrt(zxalpha)).lw(grid_linewidth*2)
3297            if zxshift: hxz.shift(0,zxshift*dy,0)
3298            hxz.name = "xzHighlightZero"
3299            highlights.append(hxz)
3300
3301    ################################################ arrow cone
3302    cones = []
3303
3304    if tip_size:
3305
3306        if xtitle:
3307            if x_inverted:
3308                cx = shapes.Cone(
3309                    r=tip_size, height=tip_size * 2, axis=(-1, 0, 0), c=xline_color, res=12
3310                )
3311            else:
3312                cx = shapes.Cone((dx,0,0), r=tip_size, height=tip_size*2,
3313                                 axis=(1,0,0), c=xline_color, res=12)
3314            if xyshift: cx.shift(0,0,xyshift*dz)
3315            if zxshift: cx.shift(0,zxshift*dy,0)
3316            if xshift_along_y: cx.shift(0,xshift_along_y*dy,0)
3317            if xshift_along_z: cx.shift(0,0,xshift_along_z*dz)
3318            cx.name = "xTipCone"
3319            cones.append(cx)
3320
3321        if ytitle:
3322            if y_inverted:
3323                cy = shapes.Cone(r=tip_size, height=tip_size*2,
3324                                 axis=(0,-1,0), c=yline_color, res=12)
3325            else:
3326                cy = shapes.Cone((0,dy,0), r=tip_size, height=tip_size*2,
3327                                 axis=(0,1,0), c=yline_color, res=12)
3328            if xyshift: cy.shift(0,0,xyshift*dz)
3329            if yzshift: cy.shift(yzshift*dx,0,0)
3330            if yshift_along_x: cy.shift(yshift_along_x*dx,0,0)
3331            if yshift_along_z: cy.shift(0,0,yshift_along_z*dz)
3332            cy.name = "yTipCone"
3333            cones.append(cy)
3334
3335        if ztitle:
3336            if z_inverted:
3337                cz = shapes.Cone(r=tip_size, height=tip_size*2,
3338                                 axis=(0,0,-1), c=zline_color, res=12)
3339            else:
3340                cz = shapes.Cone((0,0,dz), r=tip_size, height=tip_size*2,
3341                                 axis=(0,0,1), c=zline_color, res=12)
3342            if yzshift: cz.shift(yzshift*dx,0,0)
3343            if zxshift: cz.shift(0,zxshift*dy,0)
3344            if zshift_along_x: cz.shift(zshift_along_x*dx,0,0)
3345            if zshift_along_y: cz.shift(0,zshift_along_y*dy,0)
3346            cz.name = "zTipCone"
3347            cones.append(cz)
3348
3349    ################################################################# MAJOR ticks
3350    majorticks, minorticks = [], []
3351    xticks, yticks, zticks = [], [], []
3352    if show_ticks:
3353        if xtitle:
3354            tickThickness = xtick_thickness * gscale / 2
3355            tickLength = xtick_length * gscale / 2
3356            for i in range(1, len(xticks_float) - 1):
3357                v1 = (xticks_float[i] - tickThickness, -tickLength, 0)
3358                v2 = (xticks_float[i] + tickThickness, tickLength, 0)
3359                xticks.append(shapes.Rectangle(v1, v2))
3360            if len(xticks) > 1:
3361                xmajticks = merge(xticks).c(xlabel_color)
3362                if xaxis_rotation:
3363                    xmajticks.RotateX(xaxis_rotation)
3364                if xyshift: xmajticks.shift(0,0,xyshift*dz)
3365                if zxshift: xmajticks.shift(0,zxshift*dy,0)
3366                if xshift_along_y: xmajticks.shift(0,xshift_along_y*dy,0)
3367                if xshift_along_z: xmajticks.shift(0,0,xshift_along_z*dz)
3368                xmajticks.name = "xMajorTicks"
3369                majorticks.append(xmajticks)
3370        if ytitle:
3371            tickThickness = ytick_thickness * gscale / 2
3372            tickLength = ytick_length * gscale / 2
3373            for i in range(1, len(yticks_float) - 1):
3374                v1 = (-tickLength, yticks_float[i] - tickThickness, 0)
3375                v2 = (tickLength, yticks_float[i] + tickThickness, 0)
3376                yticks.append(shapes.Rectangle(v1, v2))
3377            if len(yticks) > 1:
3378                ymajticks = merge(yticks).c(ylabel_color)
3379                if yaxis_rotation:
3380                    ymajticks.RotateY(yaxis_rotation)
3381                if xyshift: ymajticks.shift(0,0,xyshift*dz)
3382                if yzshift: ymajticks.shift(yzshift*dx,0,0)
3383                if yshift_along_x: ymajticks.shift(yshift_along_x*dx,0,0)
3384                if yshift_along_z: ymajticks.shift(0,0,yshift_along_z*dz)
3385                ymajticks.name = "yMajorTicks"
3386                majorticks.append(ymajticks)
3387        if ztitle:
3388            tickThickness = ztick_thickness * gscale / 2
3389            tickLength = ztick_length * gscale / 2.85
3390            for i in range(1, len(zticks_float) - 1):
3391                v1 = (zticks_float[i] - tickThickness, -tickLength, 0)
3392                v2 = (zticks_float[i] + tickThickness, tickLength, 0)
3393                zticks.append(shapes.Rectangle(v1, v2))
3394            if len(zticks) > 1:
3395                zmajticks = merge(zticks).c(zlabel_color)
3396                zmajticks.RotateZ(-45 + zaxis_rotation)
3397                zmajticks.RotateY(-90)
3398                if yzshift: zmajticks.shift(yzshift*dx,0,0)
3399                if zxshift: zmajticks.shift(0,zxshift*dy,0)
3400                if zshift_along_x: zmajticks.shift(zshift_along_x*dx,0,0)
3401                if zshift_along_y: zmajticks.shift(0,zshift_along_y*dy,0)
3402                zmajticks.name = "zMajorTicks"
3403                majorticks.append(zmajticks)
3404
3405        ############################################################# MINOR ticks
3406        if xtitle and xminor_ticks and len(xticks) > 1:
3407            tickThickness = xtick_thickness * gscale / 4
3408            tickLength = xtick_length * gscale / 4
3409            xminor_ticks += 1
3410            ticks = []
3411            for i in range(1, len(xticks)):
3412                t0, t1 = xticks[i - 1].pos(), xticks[i].pos()
3413                dt = t1 - t0
3414                for j in range(1, xminor_ticks):
3415                    mt = dt * (j / xminor_ticks) + t0
3416                    v1 = (mt[0] - tickThickness, -tickLength, 0)
3417                    v2 = (mt[0] + tickThickness, tickLength, 0)
3418                    ticks.append(shapes.Rectangle(v1, v2))
3419
3420            # finish off the fist lower range from start to first tick
3421            t0, t1 = xticks[0].pos(), xticks[1].pos()
3422            dt = t1 - t0
3423            for j in range(1, xminor_ticks):
3424                mt = t0 - dt * (j / xminor_ticks)
3425                if mt[0] < 0:
3426                    break
3427                v1 = (mt[0] - tickThickness, -tickLength, 0)
3428                v2 = (mt[0] + tickThickness, tickLength, 0)
3429                ticks.append(shapes.Rectangle(v1, v2))
3430
3431            # finish off the last upper range from last tick to end
3432            t0, t1 = xticks[-2].pos(), xticks[-1].pos()
3433            dt = t1 - t0
3434            for j in range(1, xminor_ticks):
3435                mt = t1 + dt * (j / xminor_ticks)
3436                if mt[0] > dx:
3437                    break
3438                v1 = (mt[0] - tickThickness, -tickLength, 0)
3439                v2 = (mt[0] + tickThickness, tickLength, 0)
3440                ticks.append(shapes.Rectangle(v1, v2))
3441
3442            if ticks:
3443                xminticks = merge(ticks).c(xlabel_color)
3444                if xaxis_rotation:
3445                    xminticks.RotateX(xaxis_rotation)
3446                if xyshift: xminticks.shift(0,0,xyshift*dz)
3447                if zxshift: xminticks.shift(0,zxshift*dy,0)
3448                if xshift_along_y: xminticks.shift(0,xshift_along_y*dy,0)
3449                if xshift_along_z: xminticks.shift(0,0,xshift_along_z*dz)
3450                xminticks.name = "xMinorTicks"
3451                minorticks.append(xminticks)
3452
3453        if ytitle and yminor_ticks and len(yticks) > 1:  ##### y
3454            tickThickness = ytick_thickness * gscale / 4
3455            tickLength = ytick_length * gscale / 4
3456            yminor_ticks += 1
3457            ticks = []
3458            for i in range(1, len(yticks)):
3459                t0, t1 = yticks[i - 1].pos(), yticks[i].pos()
3460                dt = t1 - t0
3461                for j in range(1, yminor_ticks):
3462                    mt = dt * (j / yminor_ticks) + t0
3463                    v1 = (-tickLength, mt[1] - tickThickness, 0)
3464                    v2 = (tickLength, mt[1] + tickThickness, 0)
3465                    ticks.append(shapes.Rectangle(v1, v2))
3466
3467            # finish off the fist lower range from start to first tick
3468            t0, t1 = yticks[0].pos(), yticks[1].pos()
3469            dt = t1 - t0
3470            for j in range(1, yminor_ticks):
3471                mt = t0 - dt * (j / yminor_ticks)
3472                if mt[1] < 0:
3473                    break
3474                v1 = (-tickLength, mt[1] - tickThickness, 0)
3475                v2 = (tickLength, mt[1] + tickThickness, 0)
3476                ticks.append(shapes.Rectangle(v1, v2))
3477
3478            # finish off the last upper range from last tick to end
3479            t0, t1 = yticks[-2].pos(), yticks[-1].pos()
3480            dt = t1 - t0
3481            for j in range(1, yminor_ticks):
3482                mt = t1 + dt * (j / yminor_ticks)
3483                if mt[1] > dy:
3484                    break
3485                v1 = (-tickLength, mt[1] - tickThickness, 0)
3486                v2 = (tickLength, mt[1] + tickThickness, 0)
3487                ticks.append(shapes.Rectangle(v1, v2))
3488
3489            if ticks:
3490                yminticks = merge(ticks).c(ylabel_color)
3491                if yaxis_rotation:
3492                    yminticks.RotateY(yaxis_rotation)
3493                if xyshift: yminticks.shift(0,0,xyshift*dz)
3494                if yzshift: yminticks.shift(yzshift*dx,0,0)
3495                if yshift_along_x: yminticks.shift(yshift_along_x*dx,0,0)
3496                if yshift_along_z: yminticks.shift(0,0,yshift_along_z*dz)
3497                yminticks.name = "yMinorTicks"
3498                minorticks.append(yminticks)
3499
3500        if ztitle and zminor_ticks and len(zticks) > 1:  ##### z
3501            tickThickness = ztick_thickness * gscale / 4
3502            tickLength = ztick_length * gscale / 5
3503            zminor_ticks += 1
3504            ticks = []
3505            for i in range(1, len(zticks)):
3506                t0, t1 = zticks[i - 1].pos(), zticks[i].pos()
3507                dt = t1 - t0
3508                for j in range(1, zminor_ticks):
3509                    mt = dt * (j / zminor_ticks) + t0
3510                    v1 = (mt[0] - tickThickness, -tickLength, 0)
3511                    v2 = (mt[0] + tickThickness, tickLength, 0)
3512                    ticks.append(shapes.Rectangle(v1, v2))
3513
3514            # finish off the fist lower range from start to first tick
3515            t0, t1 = zticks[0].pos(), zticks[1].pos()
3516            dt = t1 - t0
3517            for j in range(1, zminor_ticks):
3518                mt = t0 - dt * (j / zminor_ticks)
3519                if mt[0] < 0:
3520                    break
3521                v1 = (mt[0] - tickThickness, -tickLength, 0)
3522                v2 = (mt[0] + tickThickness, tickLength, 0)
3523                ticks.append(shapes.Rectangle(v1, v2))
3524
3525            # finish off the last upper range from last tick to end
3526            t0, t1 = zticks[-2].pos(), zticks[-1].pos()
3527            dt = t1 - t0
3528            for j in range(1, zminor_ticks):
3529                mt = t1 + dt * (j / zminor_ticks)
3530                if mt[0] > dz:
3531                    break
3532                v1 = (mt[0] - tickThickness, -tickLength, 0)
3533                v2 = (mt[0] + tickThickness, tickLength, 0)
3534                ticks.append(shapes.Rectangle(v1, v2))
3535
3536            if ticks:
3537                zminticks = merge(ticks).c(zlabel_color)
3538                zminticks.RotateZ(-45 + zaxis_rotation)
3539                zminticks.RotateY(-90)
3540                if yzshift: zminticks.shift(yzshift*dx,0,0)
3541                if zxshift: zminticks.shift(0,zxshift*dy,0)
3542                if zshift_along_x: zminticks.shift(zshift_along_x*dx,0,0)
3543                if zshift_along_y: zminticks.shift(0,zshift_along_y*dy,0)
3544                zminticks.name = "zMinorTicks"
3545                minorticks.append(zminticks)
3546
3547    ################################################ axes NUMERIC text labels
3548    labels = []
3549    xlab, ylab, zlab = None, None, None
3550
3551    if xlabel_size and xtitle:
3552
3553        xRot, yRot, zRot = 0, 0, 0
3554        if utils.is_sequence(xlabel_rotation):  # unpck 3 rotations
3555            zRot, xRot, yRot = xlabel_rotation
3556        else:
3557            zRot = xlabel_rotation
3558        if zRot < 0:  # deal with negative angles
3559            zRot += 360
3560
3561        jus = "center-top"
3562        if zRot:
3563            if zRot >  24: jus = "top-right"
3564            if zRot >  67: jus = "center-right"
3565            if zRot > 112: jus = "right-bottom"
3566            if zRot > 157: jus = "center-bottom"
3567            if zRot > 202: jus = "bottom-left"
3568            if zRot > 247: jus = "center-left"
3569            if zRot > 292: jus = "top-left"
3570            if zRot > 337: jus = "top-center"
3571        if xlabel_justify is not None:
3572            jus = xlabel_justify
3573
3574        for i in range(1, len(xticks_str)):
3575            t = xticks_str[i]
3576            if not t:
3577                continue
3578            if utils.is_sequence(xlabel_offset):
3579                xoffs, yoffs, zoffs = xlabel_offset
3580            else:
3581                xoffs, yoffs, zoffs = 0, xlabel_offset, 0
3582            xlab = shapes.Text3D(
3583                t, s=xlabel_size * text_scale * gscale, font=label_font, justify=jus
3584            )
3585            tb = xlab.ybounds()  # must be ybounds: height of char
3586            v = (xticks_float[i], 0, 0)
3587            offs = -np.array([xoffs, yoffs, zoffs]) * (tb[1] - tb[0])
3588            xlab.pos(v + offs)
3589            if xaxis_rotation:
3590                xlab.rotate_x(xaxis_rotation)
3591            if zRot: xlab.RotateZ(zRot)
3592            if xRot: xlab.RotateX(xRot)
3593            if yRot: xlab.RotateY(yRot)
3594            if xyshift: xlab.shift(0,0,xyshift*dz)
3595            if zxshift: xlab.shift(0,zxshift*dy,0)
3596            if xshift_along_y: xlab.shift(0,xshift_along_y*dy,0)
3597            if xshift_along_z: xlab.shift(0,0,xshift_along_z*dz)
3598            xlab.name = f"xNumericLabel{i}"
3599            xlab.SetUseBounds(x_use_bounds)
3600            labels.append(xlab.c(xlabel_color))
3601
3602    if ylabel_size and ytitle:
3603
3604        xRot, yRot, zRot = 0, 0, 0
3605        if utils.is_sequence(ylabel_rotation):  # unpck 3 rotations
3606            zRot, yRot, xRot = ylabel_rotation
3607        else:
3608            zRot = ylabel_rotation
3609        if zRot < 0:
3610            zRot += 360  # deal with negative angles
3611
3612        jus = "center-right"
3613        if zRot:
3614            if zRot >  24: jus = "bottom-right"
3615            if zRot >  67: jus = "center-bottom"
3616            if zRot > 112: jus = "left-bottom"
3617            if zRot > 157: jus = "center-left"
3618            if zRot > 202: jus = "top-left"
3619            if zRot > 247: jus = "center-top"
3620            if zRot > 292: jus = "top-right"
3621            if zRot > 337: jus = "right-center"
3622        if ylabel_justify is not None:
3623            jus = ylabel_justify
3624
3625        for i in range(1, len(yticks_str)):
3626            t = yticks_str[i]
3627            if not t:
3628                continue
3629            if utils.is_sequence(ylabel_offset):
3630                xoffs, yoffs, zoffs = ylabel_offset
3631            else:
3632                xoffs, yoffs, zoffs = ylabel_offset, 0, 0
3633            ylab = shapes.Text3D(
3634                t, s=ylabel_size * text_scale * gscale, font=label_font, justify=jus
3635            )
3636            tb = ylab.ybounds()  # must be ybounds: height of char
3637            v = (0, yticks_float[i], 0)
3638            offs = -np.array([xoffs, yoffs, zoffs]) * (tb[1] - tb[0])
3639            ylab.pos(v + offs)
3640            if yaxis_rotation:
3641                ylab.rotate_y(yaxis_rotation)
3642            if zRot: ylab.RotateZ(zRot)
3643            if yRot: ylab.RotateY(yRot)
3644            if xRot: ylab.RotateX(xRot)
3645            if xyshift: ylab.shift(0,0,xyshift*dz)
3646            if yzshift: ylab.shift(yzshift*dx,0,0)
3647            if yshift_along_x: ylab.shift(yshift_along_x*dx,0,0)
3648            if yshift_along_z: ylab.shift(0,0,yshift_along_z*dz)
3649            ylab.name = f"yNumericLabel{i}"
3650            ylab.SetUseBounds(y_use_bounds)
3651            labels.append(ylab.c(ylabel_color))
3652
3653    if zlabel_size and ztitle:
3654
3655        xRot, yRot, zRot = 0, 0, 0
3656        if utils.is_sequence(zlabel_rotation):  # unpck 3 rotations
3657            xRot, yRot, zRot = zlabel_rotation
3658        else:
3659            xRot = zlabel_rotation
3660        if xRot < 0: xRot += 360 # deal with negative angles
3661
3662        jus = "center-right"
3663        if xRot:
3664            if xRot >  24: jus = "bottom-right"
3665            if xRot >  67: jus = "center-bottom"
3666            if xRot > 112: jus = "left-bottom"
3667            if xRot > 157: jus = "center-left"
3668            if xRot > 202: jus = "top-left"
3669            if xRot > 247: jus = "center-top"
3670            if xRot > 292: jus = "top-right"
3671            if xRot > 337: jus = "right-center"
3672        if zlabel_justify is not None:
3673            jus = zlabel_justify
3674
3675        for i in range(1, len(zticks_str)):
3676            t = zticks_str[i]
3677            if not t:
3678                continue
3679            if utils.is_sequence(zlabel_offset):
3680                xoffs, yoffs, zoffs = zlabel_offset
3681            else:
3682                xoffs, yoffs, zoffs = zlabel_offset, zlabel_offset, 0
3683            zlab = shapes.Text3D(
3684                t, s=zlabel_size * text_scale * gscale, font=label_font, justify=jus
3685            )
3686            tb = zlab.ybounds()  # must be ybounds: height of char
3687            v = (0, 0, zticks_float[i])
3688            offs = -np.array([xoffs, yoffs, zoffs]) * (tb[1] - tb[0]) / 1.5
3689            angle = 90
3690            if dx:
3691                angle = np.arctan2(dy, dx) * 57.3
3692            zlab.RotateZ(angle + yRot)  # vtk inverts order of rotations
3693            if xRot:
3694                zlab.RotateY(-xRot)  # ..second
3695            zlab.RotateX(90 + zRot)  # ..first
3696            zlab.pos(v + offs)
3697            if zaxis_rotation:
3698                zlab.rotate_z(zaxis_rotation)
3699            if yzshift: zlab.shift(yzshift*dx,0,0)
3700            if zxshift: zlab.shift(0,zxshift*dy,0)
3701            if zshift_along_x: zlab.shift(zshift_along_x*dx,0,0)
3702            if zshift_along_y: zlab.shift(0,zshift_along_y*dy,0)
3703            zlab.SetUseBounds(z_use_bounds)
3704            zlab.name = f"zNumericLabel{i}"
3705            labels.append(zlab.c(zlabel_color))
3706
3707    ################################################ axes titles
3708    titles = []
3709
3710    if xtitle:
3711
3712        xRot, yRot, zRot = 0, 0, 0
3713        if utils.is_sequence(xtitle_rotation):  # unpack 3 rotations
3714            zRot, xRot, yRot = xtitle_rotation
3715        else:
3716            zRot = xtitle_rotation
3717        if zRot < 0:  # deal with negative angles
3718            zRot += 360
3719
3720        if utils.is_sequence(xtitle_offset):
3721            xoffs, yoffs, zoffs = xtitle_offset
3722        else:
3723            xoffs, yoffs, zoffs = 0, xtitle_offset, 0
3724
3725        if xtitle_justify is not None:
3726            jus = xtitle_justify
3727        else:
3728            # find best justfication for given rotation(s)
3729            jus = "right-top"
3730            if zRot:
3731                if zRot >  24: jus = "center-right"
3732                if zRot >  67: jus = "right-bottom"
3733                if zRot > 157: jus = "bottom-left"
3734                if zRot > 202: jus = "center-left"
3735                if zRot > 247: jus = "top-left"
3736                if zRot > 337: jus = "top-right"
3737
3738        xt = shapes.Text3D(
3739            xtitle,
3740            s=xtitle_size * text_scale * gscale,
3741            font=title_font,
3742            c=xtitle_color,
3743            justify=jus,
3744            depth=title_depth,
3745            italic=xtitle_italic,
3746        )
3747        if xtitle_backface_color:
3748            xt.backcolor(xtitle_backface_color)
3749        if zRot:
3750            xt.RotateZ(zRot)
3751        if xRot:
3752            xt.RotateX(xRot)
3753        if yRot:
3754            xt.RotateY(yRot)
3755        shift = 0
3756        if xlab:  # xlab is the last created numeric text label..
3757            lt0, lt1 = xlab.GetBounds()[2:4]
3758            shift = lt1 - lt0
3759        xt.pos(
3760            [(xoffs + xtitle_position) * dx, -(yoffs + xtick_length / 2) * dy - shift, zoffs * dz]
3761        )
3762        if xaxis_rotation:
3763            xt.rotate_x(xaxis_rotation)
3764        if xyshift:
3765            xt.shift(0, 0, xyshift * dz)
3766        if xshift_along_y:
3767            xt.shift(0, xshift_along_y * dy, 0)
3768        if xshift_along_z:
3769            xt.shift(0, 0, xshift_along_z * dz)
3770        xt.SetUseBounds(x_use_bounds)
3771        if xtitle == " ":
3772            xt.SetUseBounds(False)
3773        xt.name = f"xtitle {xtitle}"
3774        titles.append(xt)
3775        if xtitle_box:
3776            titles.append(xt.box(scale=1.1).use_bounds(x_use_bounds))
3777
3778    if ytitle:
3779
3780        xRot, yRot, zRot = 0, 0, 0
3781        if utils.is_sequence(ytitle_rotation):  # unpck 3 rotations
3782            zRot, yRot, xRot = ytitle_rotation
3783        else:
3784            zRot = ytitle_rotation
3785            if len(ytitle) > 3:
3786                zRot += 90
3787                ytitle_position *= 0.975
3788        if zRot < 0:
3789            zRot += 360  # deal with negative angles
3790
3791        if utils.is_sequence(ytitle_offset):
3792            xoffs, yoffs, zoffs = ytitle_offset
3793        else:
3794            xoffs, yoffs, zoffs = ytitle_offset, 0, 0
3795
3796        if ytitle_justify is not None:
3797            jus = ytitle_justify
3798        else:
3799            jus = "center-right"
3800            if zRot:
3801                if zRot >  24: jus = "bottom-right"
3802                if zRot > 112: jus = "left-bottom"
3803                if zRot > 157: jus = "center-left"
3804                if zRot > 202: jus = "top-left"
3805                if zRot > 292: jus = "top-right"
3806                if zRot > 337: jus = "right-center"
3807
3808        yt = shapes.Text3D(
3809            ytitle,
3810            s=ytitle_size * text_scale * gscale,
3811            font=title_font,
3812            c=ytitle_color,
3813            justify=jus,
3814            depth=title_depth,
3815            italic=ytitle_italic,
3816        )
3817        if ytitle_backface_color:
3818            yt.backcolor(ytitle_backface_color)
3819
3820        if zRot: yt.RotateZ(zRot)
3821        if yRot: yt.RotateY(yRot)
3822        if xRot: yt.RotateX(xRot)
3823
3824        shift = 0
3825        if ylab:  # this is the last created num label..
3826            lt0, lt1 = ylab.GetBounds()[0:2]
3827            shift = lt1 - lt0
3828
3829        yt.pos(-(xoffs + ytick_length / 2) * dx - shift, (yoffs + ytitle_position) * dy, zoffs * dz)
3830        if yaxis_rotation:
3831            yt.rotate_y(yaxis_rotation)
3832        if xyshift:        yt.shift(0, 0, xyshift*dz)
3833        if yshift_along_x: yt.shift(yshift_along_x*dx, 0, 0)
3834        if yshift_along_z: yt.shift(0, 0, yshift_along_z*dz)
3835        yt.SetUseBounds(y_use_bounds)
3836        if ytitle == " ":
3837            yt.SetUseBounds(False)
3838        yt.name = f"ytitle {ytitle}"
3839        titles.append(yt)
3840        if ytitle_box:
3841            titles.append(yt.box(scale=1.1).use_bounds(y_use_bounds))
3842
3843    if ztitle:
3844
3845        xRot, yRot, zRot = 0, 0, 0
3846        if utils.is_sequence(ztitle_rotation):  # unpck 3 rotations
3847            xRot, yRot, zRot = ztitle_rotation
3848        else:
3849            xRot = ztitle_rotation
3850            if len(ztitle) > 3:
3851                xRot += 90
3852                ztitle_position *= 0.975
3853        if xRot < 0:
3854            xRot += 360  # deal with negative angles
3855
3856        if ztitle_justify is not None:
3857            jus = ztitle_justify
3858        else:
3859            jus = "center-right"
3860            if xRot:
3861                if xRot >  24: jus = "bottom-right"
3862                if xRot > 112: jus = "left-bottom"
3863                if xRot > 157: jus = "center-left"
3864                if xRot > 202: jus = "top-left"
3865                if xRot > 292: jus = "top-right"
3866                if xRot > 337: jus = "right-center"
3867
3868        zt = shapes.Text3D(
3869            ztitle,
3870            s=ztitle_size * text_scale * gscale,
3871            font=title_font,
3872            c=ztitle_color,
3873            justify=jus,
3874            depth=title_depth,
3875            italic=ztitle_italic,
3876        )
3877        if ztitle_backface_color:
3878            zt.backcolor(ztitle_backface_color)
3879
3880        angle = 90
3881        if dx:
3882            angle = np.arctan2(dy, dx) * 57.3
3883        zt.RotateZ(angle + yRot)  # vtk inverts order of rotations
3884        if xRot:
3885            zt.RotateY(-xRot)  # ..second
3886        zt.RotateX(90 + zRot)  # ..first
3887
3888        shift = 0
3889        if zlab:  # this is the last created one..
3890            lt0, lt1 = zlab.GetBounds()[0:2]
3891            shift = lt1 - lt0
3892        zt.pos(
3893            -(ztitle_offset + ztick_length / 5) * dx - shift,
3894            -(ztitle_offset + ztick_length / 5) * dy - shift,
3895            ztitle_position * dz,
3896        )
3897        if zaxis_rotation:
3898            zt.rotate_z(zaxis_rotation)
3899        if zxshift: zt.shift(0,zxshift*dy,0)
3900        if zshift_along_x: zt.shift(zshift_along_x*dx,0,0)
3901        if zshift_along_y: zt.shift(0,zshift_along_y*dy,0)
3902        zt.SetUseBounds(z_use_bounds)
3903        if ztitle == " ":
3904            zt.SetUseBounds(False)
3905        zt.name = f"ztitle {ztitle}"
3906        titles.append(zt)
3907
3908    ################################################### header title
3909    if htitle:
3910        if htitle_font is None:
3911            htitle_font = title_font
3912        if htitle_color is None:
3913            htitle_color = xtitle_color
3914        htit = shapes.Text3D(
3915            htitle,
3916            s=htitle_size * gscale * text_scale,
3917            font=htitle_font,
3918            c=htitle_color,
3919            justify=htitle_justify,
3920            depth=title_depth,
3921            italic=htitle_italic,
3922        )
3923        if htitle_rotation:
3924            htit.RotateX(htitle_rotation)
3925        wpos = [(0.5 + htitle_offset[0]) * dx, (1 + htitle_offset[1]) * dy, htitle_offset[2] * dz]
3926        htit.pos(wpos)
3927        if xyshift:
3928            htit.shift(0, 0, xyshift * dz)
3929        htit.name = f"htitle {htitle}"
3930        titles.append(htit)
3931
3932    ######
3933    acts = titles + lines + labels + grids + framelines
3934    acts += highlights + majorticks + minorticks + cones
3935    orig = (min_bns[0], min_bns[2], min_bns[4])
3936    for a in acts:
3937        a.PickableOff()
3938        a.AddPosition(orig)
3939        a.GetProperty().LightingOff()
3940    asse = Assembly(acts)
3941    asse.SetOrigin(orig)
3942    asse.PickableOff()
3943    asse.name = "Axes"
3944    return asse

Draw axes for the input object. Check available fonts here.

Returns an vedo.Assembly object.

Parameters

  • xtitle, ['x'], x-axis title text
  • xrange, [None], x-axis range in format (xmin, ymin), default is automatic.
  • number_of_divisions, [None], approximate number of divisions on the longest axis
  • axes_linewidth, [1], width of the axes lines
  • grid_linewidth, [1], width of the grid lines
  • title_depth, [0], extrusion fractional depth of title text
  • x_values_and_labels [], assign custom tick positions and labels [(pos1, label1), ...]
  • xygrid, [True], show a gridded wall on plane xy
  • yzgrid, [True], show a gridded wall on plane yz
  • zxgrid, [True], show a gridded wall on plane zx
  • zxgrid2, [False], show zx plane on opposite side of the bounding box
  • xygrid_transparent [False], make grid plane completely transparent
  • xygrid2_transparent [False], make grid plane completely transparent on opposite side box
  • xyplane_color, ['None'], color of the plane
  • xygrid_color, ['None'], grid line color
  • xyalpha, [0.15], grid plane opacity
  • xyframe_line, [0], add a frame for the plane, use value as the thickness
  • xyframe_color, [None], color for the frame of the plane
  • show_ticks, [True], show major ticks
  • digits, [None], use this number of significant digits in scientific notation
  • title_font, [''], font for axes titles
  • label_font, [''], font for numeric labels
  • text_scale, [1.0], global scaling factor for all text elements (titles, labels)
  • htitle, [''], header title
  • htitle_size, [0.03], header title size
  • htitle_font, [None], header font (defaults to title_font)
  • htitle_italic, [True], header font is italic
  • htitle_color, [None], header title color (defaults to xtitle_color)
  • htitle_justify, ['bottom-center'], origin of the title justification
  • htitle_offset, [(0,0.01,0)], control offsets of header title in x, y and z
  • xtitle_position, [0.32], title fractional positions along axis
  • xtitle_offset, [0.05], title fractional offset distance from axis line, can be a list
  • xtitle_justify, [None], choose the origin of the bounding box of title
  • xtitle_rotation, [0], add a rotation of the axis title, can be a list (rx,ry,rz)
  • xtitle_box, [False], add a box around title text
  • xline_color, [automatic], color of the x-axis
  • xtitle_color, [automatic], color of the axis title
  • xtitle_backface_color, [None], color of axis title on its backface
  • xtitle_size, [0.025], size of the axis title
  • xtitle_italic, [0], a bool or float to make the font italic
  • xhighlight_zero, [True], draw a line highlighting zero position if in range
  • xhighlight_zero_color, [autom], color of the line highlighting the zero position
  • xtick_length, [0.005], radius of the major ticks
  • xtick_thickness, [0.0025], thickness of the major ticks along their axis
  • xminor_ticks, [1], number of minor ticks between two major ticks
  • xlabel_color, [automatic], color of numeric labels and ticks
  • xLabelPrecision, [2], nr. of significative digits to be shown
  • xlabel_size, [0.015], size of the numeric labels along axis
  • xlabel_rotation, [0], numeric labels rotation (can be a list of 3 rotations)
  • xlabel_offset, [0.8], offset of the numeric labels (can be a list of 3 offsets)
  • xlabel_justify, [None], choose the origin of the bounding box of labels
  • xaxis_rotation, [0], rotate the X axis elements (ticks and labels) around this same axis
  • xyshift [0.0], slide the xy-plane along z (the range is [0,1])
  • xshift_along_y [0.0], slide x-axis along the y-axis (the range is [0,1])
  • tip_size, [0.01], size of the arrow tip
  • limit_ratio, [0.04], below this ratio don't plot smaller axis
  • x_use_bounds, [True], keep into account space occupied by labels when setting camera
  • x_inverted, [False], invert labels order and direction (only visually!)
  • use_global, [False], try to compute the global bounding box of visible actors
Example:
from vedo import Axes, Box, show
box = Box(pos=(1,2,3), length=8, width=9, height=7).alpha(0.1)
axs = Axes(box, c='k')  # returns an Assembly object
for a in axs.unpack():
    print(a.name)
show(box, axs).close()

Examples:

class RendererFrame(vtkmodules.vtkRenderingCore.vtkActor2D):
2081class RendererFrame(vtk.vtkActor2D):
2082    """
2083    Add a line around the renderer subwindow.
2084    """
2085
2086    def __init__(self, c="k", alpha=None, lw=None, padding=None):
2087        """
2088        Add a line around the renderer subwindow.
2089
2090        Arguments:
2091            c : (color)
2092                color of the line.
2093            alpha : (float)
2094                opacity.
2095            lw : (int)
2096                line width in pixels.
2097            padding : (int)
2098                padding in pixel units.
2099        """
2100
2101        if lw is None:
2102            lw = settings.renderer_frame_width
2103        if lw == 0:
2104            return None
2105
2106        if alpha is None:
2107            alpha = settings.renderer_frame_alpha
2108
2109        if padding is None:
2110            padding = settings.renderer_frame_padding
2111
2112        c = get_color(c)
2113
2114        ppoints = vtk.vtkPoints()  # Generate the polyline
2115        xy = 1 - padding
2116        psqr = [[padding, padding], [padding, xy], [xy, xy], [xy, padding], [padding, padding]]
2117        for i, pt in enumerate(psqr):
2118            ppoints.InsertPoint(i, pt[0], pt[1], 0)
2119        lines = vtk.vtkCellArray()
2120        lines.InsertNextCell(len(psqr))
2121        for i in range(len(psqr)):
2122            lines.InsertCellPoint(i)
2123        pd = vtk.vtkPolyData()
2124        pd.SetPoints(ppoints)
2125        pd.SetLines(lines)
2126
2127        mapper = vtk.vtkPolyDataMapper2D()
2128        mapper.SetInputData(pd)
2129        cs = vtk.vtkCoordinate()
2130        cs.SetCoordinateSystemToNormalizedViewport()
2131        mapper.SetTransformCoordinate(cs)
2132
2133        vtk.vtkActor2D.__init__(self)
2134
2135        self.GetPositionCoordinate().SetValue(0, 0)
2136        self.GetPosition2Coordinate().SetValue(1, 1)
2137        self.SetMapper(mapper)
2138        self.GetProperty().SetColor(c)
2139        self.GetProperty().SetOpacity(alpha)
2140        self.GetProperty().SetLineWidth(lw)

Add a line around the renderer subwindow.

RendererFrame(c='k', alpha=None, lw=None, padding=None)
2086    def __init__(self, c="k", alpha=None, lw=None, padding=None):
2087        """
2088        Add a line around the renderer subwindow.
2089
2090        Arguments:
2091            c : (color)
2092                color of the line.
2093            alpha : (float)
2094                opacity.
2095            lw : (int)
2096                line width in pixels.
2097            padding : (int)
2098                padding in pixel units.
2099        """
2100
2101        if lw is None:
2102            lw = settings.renderer_frame_width
2103        if lw == 0:
2104            return None
2105
2106        if alpha is None:
2107            alpha = settings.renderer_frame_alpha
2108
2109        if padding is None:
2110            padding = settings.renderer_frame_padding
2111
2112        c = get_color(c)
2113
2114        ppoints = vtk.vtkPoints()  # Generate the polyline
2115        xy = 1 - padding
2116        psqr = [[padding, padding], [padding, xy], [xy, xy], [xy, padding], [padding, padding]]
2117        for i, pt in enumerate(psqr):
2118            ppoints.InsertPoint(i, pt[0], pt[1], 0)
2119        lines = vtk.vtkCellArray()
2120        lines.InsertNextCell(len(psqr))
2121        for i in range(len(psqr)):
2122            lines.InsertCellPoint(i)
2123        pd = vtk.vtkPolyData()
2124        pd.SetPoints(ppoints)
2125        pd.SetLines(lines)
2126
2127        mapper = vtk.vtkPolyDataMapper2D()
2128        mapper.SetInputData(pd)
2129        cs = vtk.vtkCoordinate()
2130        cs.SetCoordinateSystemToNormalizedViewport()
2131        mapper.SetTransformCoordinate(cs)
2132
2133        vtk.vtkActor2D.__init__(self)
2134
2135        self.GetPositionCoordinate().SetValue(0, 0)
2136        self.GetPosition2Coordinate().SetValue(1, 1)
2137        self.SetMapper(mapper)
2138        self.GetProperty().SetColor(c)
2139        self.GetProperty().SetOpacity(alpha)
2140        self.GetProperty().SetLineWidth(lw)

Add a line around the renderer subwindow.

Arguments:
  • c : (color) color of the line.
  • alpha : (float) opacity.
  • lw : (int) line width in pixels.
  • padding : (int) padding in pixel units.
def Ruler( p1, p2, units_scale=1, label='', s=None, font=None, italic=0, prefix='', units='', c=(0.2, 0.1, 0.1), alpha=1, lw=1, precision=3, label_rotation=0, axis_rotation=0, tick_angle=90):
2304def Ruler(
2305    p1,
2306    p2,
2307    units_scale=1,
2308    label="",
2309    s=None,
2310    font=None,
2311    italic=0,
2312    prefix="",
2313    units="",  # eg.'μm'
2314    c=(0.2, 0.1, 0.1),
2315    alpha=1,
2316    lw=1,
2317    precision=3,
2318    label_rotation=0,
2319    axis_rotation=0,
2320    tick_angle=90,
2321):
2322    """
2323    Build a 3D ruler to indicate the distance of two points p1 and p2.
2324
2325    Arguments:
2326        label : (str)
2327            alternative fixed label to be shown
2328        units_scale : (float)
2329            factor to scale units (e.g. μm to mm)
2330        s : (float)
2331            size of the label
2332        font : (str)
2333            font face.  Check [available fonts here](https://vedo.embl.es/fonts).
2334        italic : (float)
2335            italicness of the font in the range [0,1]
2336        units : (str)
2337            string to be appended to the numeric value
2338        lw : (int)
2339            line width in pixel units
2340        precision : (int)
2341            nr of significant digits to be shown
2342        label_rotation : (float)
2343            initial rotation of the label around the z-axis
2344        axis_rotation : (float)
2345            initial rotation of the line around the main axis
2346        tick_angle : (float)
2347            initial rotation of the line around the main axis
2348
2349    Examples:
2350        - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py)
2351
2352        ![](https://vedo.embl.es/images/pyplot/goniometer.png)
2353    """
2354    if units_scale != 1.0 and units == "":
2355        raise ValueError(
2356            "When setting 'units_scale' to a value other than 1, "
2357            + "a 'units' arguments must be specified."
2358        )
2359
2360    if isinstance(p1, Points):
2361        p1 = p1.GetPosition()
2362    if isinstance(p2, Points):
2363        p2 = p2.GetPosition()
2364
2365    if len(p1) == 2:
2366        p1 = [p1[0], p1[1], 0.0]
2367    if len(p2) == 2:
2368        p2 = [p2[0], p2[1], 0.0]
2369
2370    p1, p2 = np.array(p1), np.array(p2)
2371    q1, q2 = [0, 0, 0], [utils.mag(p2 - p1), 0, 0]
2372    q1, q2 = np.array(q1), np.array(q2)
2373    v = q2 - q1
2374    d = utils.mag(v) * units_scale
2375
2376    if s is None:
2377        s = d * 0.02 * (1 / units_scale)
2378
2379    if not label:
2380        label = str(d)
2381        if precision:
2382            label = utils.precision(d, precision)
2383    if prefix:
2384        label = prefix + "~" + label
2385    if units:
2386        label += "~" + units
2387
2388    lb = shapes.Text3D(label, pos=(q1 + q2) / 2, s=s, font=font, italic=italic, justify="center")
2389    if label_rotation:
2390        lb.RotateZ(label_rotation)
2391
2392    x0, x1 = lb.xbounds()
2393    gap = [(x1 - x0) / 2, 0, 0]
2394    pc1 = (v / 2 - gap) * 0.9 + q1
2395    pc2 = q2 - (v / 2 - gap) * 0.9
2396
2397    lc1 = shapes.Line(q1 - v / 50, pc1)
2398    lc2 = shapes.Line(q2 + v / 50, pc2)
2399
2400    zs = np.array([0, d / 50 * (1 / units_scale), 0])
2401    ml1 = shapes.Line(-zs, zs).pos(q1)
2402    ml2 = shapes.Line(-zs, zs).pos(q2)
2403    ml1.RotateZ(tick_angle - 90)
2404    ml2.RotateZ(tick_angle - 90)
2405
2406    c1 = shapes.Circle(q1, r=d / 180 * (1 / units_scale), res=20)
2407    c2 = shapes.Circle(q2, r=d / 180 * (1 / units_scale), res=20)
2408
2409    acts = [lb, lc1, lc2, c1, c2, ml1, ml2]
2410    macts = merge(acts).pos(p1).c(c).alpha(alpha)
2411    macts.GetProperty().LightingOff()
2412    macts.GetProperty().SetLineWidth(lw)
2413    macts.UseBoundsOff()
2414    macts.base = q1
2415    macts.top = q2
2416    macts.orientation(p2 - p1, rotation=axis_rotation).bc("t").pickable(False)
2417    return macts

Build a 3D ruler to indicate the distance of two points p1 and p2.

Arguments:
  • label : (str) alternative fixed label to be shown
  • units_scale : (float) factor to scale units (e.g. μm to mm)
  • s : (float) size of the label
  • font : (str) font face. Check available fonts here.
  • italic : (float) italicness of the font in the range [0,1]
  • units : (str) string to be appended to the numeric value
  • lw : (int) line width in pixel units
  • precision : (int) nr of significant digits to be shown
  • label_rotation : (float) initial rotation of the label around the z-axis
  • axis_rotation : (float) initial rotation of the line around the main axis
  • tick_angle : (float) initial rotation of the line around the main axis
Examples:

def RulerAxes( inputobj, xtitle='', ytitle='', ztitle='', xlabel='', ylabel='', zlabel='', xpadding=0.05, ypadding=0.04, zpadding=0, font='Normografo', s=None, italic=0, units='', c=(0.2, 0, 0), alpha=1, lw=1, precision=3, label_rotation=0, xaxis_rotation=0, yaxis_rotation=0, zaxis_rotation=0, xycross=True):
2420def RulerAxes(
2421    inputobj,
2422    xtitle="",
2423    ytitle="",
2424    ztitle="",
2425    xlabel="",
2426    ylabel="",
2427    zlabel="",
2428    xpadding=0.05,
2429    ypadding=0.04,
2430    zpadding=0,
2431    font="Normografo",
2432    s=None,
2433    italic=0,
2434    units="",
2435    c=(0.2, 0, 0),
2436    alpha=1,
2437    lw=1,
2438    precision=3,
2439    label_rotation=0,
2440    xaxis_rotation=0,
2441    yaxis_rotation=0,
2442    zaxis_rotation=0,
2443    xycross=True,
2444):
2445    """
2446    A 3D ruler axes to indicate the sizes of the input scene or object.
2447
2448    Arguments:
2449        xtitle : (str)
2450            name of the axis or title
2451        xlabel : (str)
2452            alternative fixed label to be shown instead of the distance
2453        s : (float)
2454            size of the label
2455        font : (str)
2456            font face. Check [available fonts here](https://vedo.embl.es/fonts).
2457        italic : (float)
2458            italicness of the font in the range [0,1]
2459        units : (str)
2460            string to be appended to the numeric value
2461        lw : (int)
2462            line width in pixel units
2463        precision : (int)
2464            nr of significant digits to be shown
2465        label_rotation : (float)
2466            initial rotation of the label around the z-axis
2467        [x,y,z]axis_rotation : (float)
2468            initial rotation of the line around the main axis in degrees
2469        xycross : (bool)
2470            show two back crossing lines in the xy plane
2471
2472    Examples:
2473        - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py)
2474    """
2475    if utils.is_sequence(inputobj):
2476        x0, x1, y0, y1, z0, z1 = inputobj
2477    else:
2478        x0, x1, y0, y1, z0, z1 = inputobj.bounds()
2479    dx, dy, dz = (y1 - y0) * xpadding, (x1 - x0) * ypadding, (y1 - y0) * zpadding
2480    d = np.sqrt((y1 - y0) ** 2 + (x1 - x0) ** 2 + (z1 - z0) ** 2)
2481
2482    if not d:
2483        return None
2484
2485    if s is None:
2486        s = d / 75
2487
2488    acts, rx, ry = [], None, None
2489    if xtitle is not None and (x1 - x0) / d > 0.1:
2490        rx = Ruler(
2491            [x0, y0 - dx, z0],
2492            [x1, y0 - dx, z0],
2493            s=s,
2494            font=font,
2495            precision=precision,
2496            label_rotation=label_rotation,
2497            axis_rotation=xaxis_rotation,
2498            lw=lw,
2499            italic=italic,
2500            prefix=xtitle,
2501            label=xlabel,
2502            units=units,
2503        )
2504        acts.append(rx)
2505    if ytitle is not None and (y1 - y0) / d > 0.1:
2506        ry = Ruler(
2507            [x1 + dy, y0, z0],
2508            [x1 + dy, y1, z0],
2509            s=s,
2510            font=font,
2511            precision=precision,
2512            label_rotation=label_rotation,
2513            axis_rotation=yaxis_rotation,
2514            lw=lw,
2515            italic=italic,
2516            prefix=ytitle,
2517            label=ylabel,
2518            units=units,
2519        )
2520        acts.append(ry)
2521    if ztitle is not None and (z1 - z0) / d > 0.1:
2522        rz = Ruler(
2523            [x0 - dy, y0 + dz, z0],
2524            [x0 - dy, y0 + dz, z1],
2525            s=s,
2526            font=font,
2527            precision=precision,
2528            label_rotation=label_rotation,
2529            axis_rotation=zaxis_rotation + 90,
2530            lw=lw,
2531            italic=italic,
2532            prefix=ztitle,
2533            label=zlabel,
2534            units=units,
2535        )
2536        acts.append(rz)
2537
2538    if xycross and rx and ry:
2539        lx = shapes.Line([x0, y0, z0], [x0, y1 + dx, z0])
2540        ly = shapes.Line([x0 - dy, y1, z0], [x1, y1, z0])
2541        d = min((x1 - x0), (y1 - y0)) / 200
2542        cxy = shapes.Circle([x0, y1, z0], r=d, res=15)
2543        acts.extend([lx, ly, cxy])
2544
2545    macts = merge(acts)
2546    if not macts:
2547        return None
2548    macts.c(c).alpha(alpha).bc("t")
2549    macts.UseBoundsOff()
2550    macts.PickableOff()
2551    return macts

A 3D ruler axes to indicate the sizes of the input scene or object.

Arguments:
  • xtitle : (str) name of the axis or title
  • xlabel : (str) alternative fixed label to be shown instead of the distance
  • s : (float) size of the label
  • font : (str) font face. Check available fonts here.
  • italic : (float) italicness of the font in the range [0,1]
  • units : (str) string to be appended to the numeric value
  • lw : (int) line width in pixel units
  • precision : (int) nr of significant digits to be shown
  • label_rotation : (float) initial rotation of the label around the z-axis
  • [x,y,z]axis_rotation : (float) initial rotation of the line around the main axis in degrees
  • xycross : (bool) show two back crossing lines in the xy plane
Examples:
class Ruler2D(vtkmodules.vtkRenderingAnnotation.vtkAxisActor2D):
2555class Ruler2D(vtk.vtkAxisActor2D):
2556    """
2557    Create a ruler with tick marks, labels and a title.
2558    """
2559
2560    def __init__(
2561        self,
2562        lw=2,
2563        ticks=True,
2564        labels=False,
2565        c="k",
2566        alpha=1,
2567        title="",
2568        font="Calco",
2569        font_size=24,
2570        bc=None,
2571    ):
2572        """
2573        Create a ruler with tick marks, labels and a title.
2574
2575        Ruler2D is a 2D actor; that is, it is drawn on the overlay
2576        plane and is not occluded by 3D geometry.
2577        To use this class, specify two points defining the start and end
2578        with update_points() as 3D points.
2579
2580        This class decides decides how to create reasonable tick
2581        marks and labels.
2582
2583        Labels are drawn on the "right" side of the axis.
2584        The "right" side is the side of the axis on the right.
2585        The way the labels and title line up with the axis and tick marks
2586        depends on whether the line is considered horizontal or vertical.
2587
2588        Arguments:
2589            lw : (int)
2590                width of the line in pixel units
2591            ticks : (bool)
2592                control if drawing the tick marks
2593            labels : (bool)
2594                control if drawing the numeric labels
2595            c : (color)
2596                color of the object
2597            alpha : (float)
2598                opacity of the object
2599            title : (str)
2600                title of the ruler
2601            font : (str)
2602                font face name. Check [available fonts here](https://vedo.embl.es/fonts).
2603            font_size : (int)
2604                font size
2605            bc : (color)
2606                background color of the title
2607
2608        Example:
2609            ```python
2610            from vedo  import *
2611            plt = Plotter(axes=1, interactive=False)
2612            plt.show(Cube())
2613            rul = Ruler2D()
2614            rul.set_points([0,0,0], [0.5,0.5,0.5])
2615            plt.add(rul)
2616            plt.interactive().close()
2617            ```
2618            ![](https://vedo.embl.es/images/feats/dist_tool.png)
2619        """
2620        vtk.vtkAxisActor2D.__init__(self)
2621
2622        plt = vedo.plotter_instance
2623        if not plt:
2624            vedo.logger.error("Ruler2D need to initialize Plotter first.")
2625            raise RuntimeError()
2626
2627        self.p0 = [0, 0, 0]
2628        self.p1 = [0, 0, 0]
2629        self.distance = 0
2630        self.title = title
2631
2632        prop = self.GetProperty()
2633        tprop = self.GetTitleTextProperty()
2634
2635        self.SetTitle(title)
2636        self.SetNumberOfLabels(9)
2637
2638        if not font:
2639            font = settings.default_font
2640        if font.lower() == "courier":
2641            tprop.SetFontFamilyToCourier()
2642        elif font.lower() == "times":
2643            tprop.SetFontFamilyToTimes()
2644        elif font.lower() == "arial":
2645            tprop.SetFontFamilyToArial()
2646        else:
2647            tprop.SetFontFamily(vtk.VTK_FONT_FILE)
2648            tprop.SetFontFile(utils.get_font_path(font))
2649        tprop.SetFontSize(font_size)
2650        tprop.BoldOff()
2651        tprop.ItalicOff()
2652        tprop.ShadowOff()
2653        tprop.SetColor(get_color(c))
2654        tprop.SetOpacity(alpha)
2655        if bc is not None:
2656            bc = get_color(bc)
2657            tprop.SetBackgroundColor(bc)
2658            tprop.SetBackgroundOpacity(alpha)
2659
2660        lprop = vtk.vtkTextProperty()
2661        lprop.ShallowCopy(tprop)
2662        self.SetLabelTextProperty(lprop)
2663
2664        self.SetLabelFormat("%0.3g")
2665        self.SetTickVisibility(ticks)
2666        self.SetLabelVisibility(labels)
2667        prop.SetLineWidth(lw)
2668        prop.SetColor(get_color(c))
2669
2670        self.renderer = plt.renderer
2671        self.cid = plt.interactor.AddObserver("RenderEvent", self._update_viz, 1.0)
2672
2673    def color(self, c):
2674        """Assign a new color."""
2675        c = get_color(c)
2676        self.GetTitleTextProperty().SetColor(c)
2677        self.GetLabelTextProperty().SetColor(c)
2678        self.GetProperty().SetColor(c)
2679        return self
2680
2681    def off(self):
2682        """Switch off the ruler completely."""
2683        self.renderer.RemoveObserver(self.cid)
2684        self.renderer.RemoveActor(self)
2685
2686    def set_points(self, p0, p1):
2687        """Set new values for the ruler start and end points."""
2688        self.p0 = np.asarray(p0)
2689        self.p1 = np.asarray(p1)
2690        self._update_viz(0, 0)
2691        return self
2692
2693    def _update_viz(self, evt, name):
2694        ren = self.renderer
2695        view_size = np.array(ren.GetSize())
2696
2697        ren.SetWorldPoint(*self.p0, 1)
2698        ren.WorldToDisplay()
2699        disp_point1 = ren.GetDisplayPoint()[:2]
2700        disp_point1 = np.array(disp_point1) / view_size
2701
2702        ren.SetWorldPoint(*self.p1, 1)
2703        ren.WorldToDisplay()
2704        disp_point2 = ren.GetDisplayPoint()[:2]
2705        disp_point2 = np.array(disp_point2) / view_size
2706
2707        self.SetPoint1(*disp_point1)
2708        self.SetPoint2(*disp_point2)
2709        self.distance = np.linalg.norm(self.p1 - self.p0)
2710        self.SetRange(0, self.distance)
2711        if not self.title:
2712            self.SetTitle(utils.precision(self.distance, 3))

Create a ruler with tick marks, labels and a title.

Ruler2D( lw=2, ticks=True, labels=False, c='k', alpha=1, title='', font='Calco', font_size=24, bc=None)
2560    def __init__(
2561        self,
2562        lw=2,
2563        ticks=True,
2564        labels=False,
2565        c="k",
2566        alpha=1,
2567        title="",
2568        font="Calco",
2569        font_size=24,
2570        bc=None,
2571    ):
2572        """
2573        Create a ruler with tick marks, labels and a title.
2574
2575        Ruler2D is a 2D actor; that is, it is drawn on the overlay
2576        plane and is not occluded by 3D geometry.
2577        To use this class, specify two points defining the start and end
2578        with update_points() as 3D points.
2579
2580        This class decides decides how to create reasonable tick
2581        marks and labels.
2582
2583        Labels are drawn on the "right" side of the axis.
2584        The "right" side is the side of the axis on the right.
2585        The way the labels and title line up with the axis and tick marks
2586        depends on whether the line is considered horizontal or vertical.
2587
2588        Arguments:
2589            lw : (int)
2590                width of the line in pixel units
2591            ticks : (bool)
2592                control if drawing the tick marks
2593            labels : (bool)
2594                control if drawing the numeric labels
2595            c : (color)
2596                color of the object
2597            alpha : (float)
2598                opacity of the object
2599            title : (str)
2600                title of the ruler
2601            font : (str)
2602                font face name. Check [available fonts here](https://vedo.embl.es/fonts).
2603            font_size : (int)
2604                font size
2605            bc : (color)
2606                background color of the title
2607
2608        Example:
2609            ```python
2610            from vedo  import *
2611            plt = Plotter(axes=1, interactive=False)
2612            plt.show(Cube())
2613            rul = Ruler2D()
2614            rul.set_points([0,0,0], [0.5,0.5,0.5])
2615            plt.add(rul)
2616            plt.interactive().close()
2617            ```
2618            ![](https://vedo.embl.es/images/feats/dist_tool.png)
2619        """
2620        vtk.vtkAxisActor2D.__init__(self)
2621
2622        plt = vedo.plotter_instance
2623        if not plt:
2624            vedo.logger.error("Ruler2D need to initialize Plotter first.")
2625            raise RuntimeError()
2626
2627        self.p0 = [0, 0, 0]
2628        self.p1 = [0, 0, 0]
2629        self.distance = 0
2630        self.title = title
2631
2632        prop = self.GetProperty()
2633        tprop = self.GetTitleTextProperty()
2634
2635        self.SetTitle(title)
2636        self.SetNumberOfLabels(9)
2637
2638        if not font:
2639            font = settings.default_font
2640        if font.lower() == "courier":
2641            tprop.SetFontFamilyToCourier()
2642        elif font.lower() == "times":
2643            tprop.SetFontFamilyToTimes()
2644        elif font.lower() == "arial":
2645            tprop.SetFontFamilyToArial()
2646        else:
2647            tprop.SetFontFamily(vtk.VTK_FONT_FILE)
2648            tprop.SetFontFile(utils.get_font_path(font))
2649        tprop.SetFontSize(font_size)
2650        tprop.BoldOff()
2651        tprop.ItalicOff()
2652        tprop.ShadowOff()
2653        tprop.SetColor(get_color(c))
2654        tprop.SetOpacity(alpha)
2655        if bc is not None:
2656            bc = get_color(bc)
2657            tprop.SetBackgroundColor(bc)
2658            tprop.SetBackgroundOpacity(alpha)
2659
2660        lprop = vtk.vtkTextProperty()
2661        lprop.ShallowCopy(tprop)
2662        self.SetLabelTextProperty(lprop)
2663
2664        self.SetLabelFormat("%0.3g")
2665        self.SetTickVisibility(ticks)
2666        self.SetLabelVisibility(labels)
2667        prop.SetLineWidth(lw)
2668        prop.SetColor(get_color(c))
2669
2670        self.renderer = plt.renderer
2671        self.cid = plt.interactor.AddObserver("RenderEvent", self._update_viz, 1.0)

Create a ruler with tick marks, labels and a title.

Ruler2D is a 2D actor; that is, it is drawn on the overlay plane and is not occluded by 3D geometry. To use this class, specify two points defining the start and end with update_points() as 3D points.

This class decides decides how to create reasonable tick marks and labels.

Labels are drawn on the "right" side of the axis. The "right" side is the side of the axis on the right. The way the labels and title line up with the axis and tick marks depends on whether the line is considered horizontal or vertical.

Arguments:
  • lw : (int) width of the line in pixel units
  • ticks : (bool) control if drawing the tick marks
  • labels : (bool) control if drawing the numeric labels
  • c : (color) color of the object
  • alpha : (float) opacity of the object
  • title : (str) title of the ruler
  • font : (str) font face name. Check available fonts here.
  • font_size : (int) font size
  • bc : (color) background color of the title
Example:
from vedo  import *
plt = Plotter(axes=1, interactive=False)
plt.show(Cube())
rul = Ruler2D()
rul.set_points([0,0,0], [0.5,0.5,0.5])
plt.add(rul)
plt.interactive().close()

def color(self, c):
2673    def color(self, c):
2674        """Assign a new color."""
2675        c = get_color(c)
2676        self.GetTitleTextProperty().SetColor(c)
2677        self.GetLabelTextProperty().SetColor(c)
2678        self.GetProperty().SetColor(c)
2679        return self

Assign a new color.

def off(self):
2681    def off(self):
2682        """Switch off the ruler completely."""
2683        self.renderer.RemoveObserver(self.cid)
2684        self.renderer.RemoveActor(self)

Switch off the ruler completely.

def set_points(self, p0, p1):
2686    def set_points(self, p0, p1):
2687        """Set new values for the ruler start and end points."""
2688        self.p0 = np.asarray(p0)
2689        self.p1 = np.asarray(p1)
2690        self._update_viz(0, 0)
2691        return self

Set new values for the ruler start and end points.

class DistanceTool(vedo.assembly.Group):
2716class DistanceTool(Group):
2717    """
2718    Create a tool to measure the distance between two clicked points.
2719    """
2720
2721    def __init__(self, plotter=None, c="k", lw=2):
2722        """
2723        Create a tool to measure the distance between two clicked points.
2724
2725        Example:
2726            ```python
2727            from vedo import *
2728            mesh = ParametricShape("RandomHills").c("red5")
2729            plt = Plotter(axes=1)
2730            dtool = DistanceTool()
2731            dtool.on()
2732            plt.show(mesh, dtool)
2733            dtool.off()
2734            ```
2735            ![](https://vedo.embl.es/images/feats/dist_tool.png)
2736        """
2737        Group.__init__(self)
2738
2739        self.p0 = [0, 0, 0]
2740        self.p1 = [0, 0, 0]
2741        self.distance = 0
2742        if plotter is None:
2743            plotter = vedo.plotter_instance
2744        self.plotter = plotter
2745        self.callback = None
2746        self.cid = None
2747        self.color = c
2748        self.linewidth = lw
2749        self.toggle = True
2750        self.ruler = None
2751        self.title = ""
2752
2753    def on(self):
2754        """Switch tool on."""
2755        self.cid = self.plotter.add_callback("click", self._onclick)
2756        self.VisibilityOn()
2757        self.plotter.render()
2758        return self
2759
2760    def off(self):
2761        """Switch tool off."""
2762        self.plotter.remove_callback(self.cid)
2763        self.VisibilityOff()
2764        self.ruler.off()
2765        self.plotter.render()
2766
2767    def _onclick(self, event):
2768        if not event.actor:
2769            return
2770
2771        self.clear()
2772
2773        acts = []
2774        if self.toggle:
2775            self.p0 = event.picked3d
2776            acts.append(Point(self.p0, c=self.color))
2777        else:
2778            self.p1 = event.picked3d
2779            self.distance = np.linalg.norm(self.p1 - self.p0)
2780            acts.append(Point(self.p0, c=self.color))
2781            acts.append(Point(self.p1, c=self.color))
2782            self.ruler = Ruler2D(c=self.color)
2783            self.ruler.set_points(self.p0, self.p1)
2784            acts.append(self.ruler)
2785
2786            if self.callback is not None:
2787                self.callback(event)
2788
2789        self += acts
2790        self.toggle = not self.toggle

Create a tool to measure the distance between two clicked points.

DistanceTool(plotter=None, c='k', lw=2)
2721    def __init__(self, plotter=None, c="k", lw=2):
2722        """
2723        Create a tool to measure the distance between two clicked points.
2724
2725        Example:
2726            ```python
2727            from vedo import *
2728            mesh = ParametricShape("RandomHills").c("red5")
2729            plt = Plotter(axes=1)
2730            dtool = DistanceTool()
2731            dtool.on()
2732            plt.show(mesh, dtool)
2733            dtool.off()
2734            ```
2735            ![](https://vedo.embl.es/images/feats/dist_tool.png)
2736        """
2737        Group.__init__(self)
2738
2739        self.p0 = [0, 0, 0]
2740        self.p1 = [0, 0, 0]
2741        self.distance = 0
2742        if plotter is None:
2743            plotter = vedo.plotter_instance
2744        self.plotter = plotter
2745        self.callback = None
2746        self.cid = None
2747        self.color = c
2748        self.linewidth = lw
2749        self.toggle = True
2750        self.ruler = None
2751        self.title = ""

Create a tool to measure the distance between two clicked points.

Example:
from vedo import *
mesh = ParametricShape("RandomHills").c("red5")
plt = Plotter(axes=1)
dtool = DistanceTool()
dtool.on()
plt.show(mesh, dtool)
dtool.off()

def on(self):
2753    def on(self):
2754        """Switch tool on."""
2755        self.cid = self.plotter.add_callback("click", self._onclick)
2756        self.VisibilityOn()
2757        self.plotter.render()
2758        return self

Switch tool on.

def off(self):
2760    def off(self):
2761        """Switch tool off."""
2762        self.plotter.remove_callback(self.cid)
2763        self.VisibilityOff()
2764        self.ruler.off()
2765        self.plotter.render()

Switch tool off.

class SplineTool(vtkmodules.vtkInteractionWidgets.vtkContourWidget):
527class SplineTool(vtk.vtkContourWidget):
528    """
529    Spline tool, draw a spline through a set of points interactively.
530    """
531
532    def __init__(self, points, pc="k", ps=8, lc="r4", ac="g5", lw=2, closed=False, ontop=True):
533        """
534        Spline tool, draw a spline through a set of points interactively.
535
536        Arguments:
537            points : (list), Points
538                initial set of points.
539            pc : (str)
540                point color.
541            ps : (int)
542                point size.
543            lc : (str)
544                line color.
545            ac : (str)
546                active point color.
547            lw : (int)
548                line width.
549            closed : (bool)
550                spline is closed or open.
551            ontop : (bool)
552                show it always on top of other objects.
553
554        Examples:
555            - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py)
556
557                ![](https://vedo.embl.es/images/basic/spline_tool.png)
558        """
559        vtk.vtkContourWidget.__init__(self)
560
561        self.representation = vtk.vtkOrientedGlyphContourRepresentation()
562        self.representation.SetAlwaysOnTop(ontop)
563
564        self.representation.GetLinesProperty().SetColor(get_color(lc))
565        self.representation.GetLinesProperty().SetLineWidth(lw)
566
567        self.representation.GetProperty().SetColor(get_color(pc))
568        self.representation.GetProperty().SetPointSize(ps)
569        self.representation.GetProperty().RenderPointsAsSpheresOn()
570
571        self.representation.GetActiveProperty().SetColor(get_color(ac))
572        self.representation.GetActiveProperty().SetLineWidth(lw + 1)
573
574        self.SetRepresentation(self.representation)
575
576        if utils.is_sequence(points):
577            self.points = Points(points)
578        else:
579            self.points = points
580
581        self.closed = closed
582
583    def add(self, pt):
584        """
585        Add one point at a specified position in space if 3D,
586        or 2D screen-display position if 2D.
587        """
588        if len(pt) == 2:
589            self.representation.AddNodeAtDisplayPosition(int(pt[0]), int(pt[1]))
590        else:
591            self.representation.AddNodeAtWorldPosition(pt)
592        return self
593
594    def remove(self, i):
595        """Remove specific node by its index"""
596        self.representation.DeleteNthNode(i)
597        return self
598
599    def on(self):
600        """Activate/Enable the tool"""
601        self.On()
602        self.Render()
603        return self
604
605    def off(self):
606        """Disactivate/Disable the tool"""
607        self.Off()
608        self.Render()
609        return self
610
611    def render(self):
612        """Render the spline"""
613        self.Render()
614        return self
615
616    def bounds(self):
617        """Retrieve the bounding box of the spline as [x0,x1, y0,y1, z0,z1]"""
618        return self.GetBounds()
619
620    def spline(self):
621        """Return the vedo.Spline object."""
622        self.representation.SetClosedLoop(self.closed)
623        self.representation.BuildRepresentation()
624        pd = self.representation.GetContourRepresentationAsPolyData()
625        pts = utils.vtk2numpy(pd.GetPoints().GetData())
626        ln = vedo.Line(pts, lw=2, c="k")
627        return ln
628
629    def nodes(self, onscreen=False):
630        """Return the current position in space (or on 2D screen-display) of the spline nodes."""
631        n = self.representation.GetNumberOfNodes()
632        pts = []
633        for i in range(n):
634            p = [0.0, 0.0, 0.0]
635            if onscreen:
636                self.representation.GetNthNodeDisplayPosition(i, p)
637            else:
638                self.representation.GetNthNodeWorldPosition(i, p)
639            pts.append(p)
640        return np.array(pts)

Spline tool, draw a spline through a set of points interactively.

SplineTool( points, pc='k', ps=8, lc='r4', ac='g5', lw=2, closed=False, ontop=True)
532    def __init__(self, points, pc="k", ps=8, lc="r4", ac="g5", lw=2, closed=False, ontop=True):
533        """
534        Spline tool, draw a spline through a set of points interactively.
535
536        Arguments:
537            points : (list), Points
538                initial set of points.
539            pc : (str)
540                point color.
541            ps : (int)
542                point size.
543            lc : (str)
544                line color.
545            ac : (str)
546                active point color.
547            lw : (int)
548                line width.
549            closed : (bool)
550                spline is closed or open.
551            ontop : (bool)
552                show it always on top of other objects.
553
554        Examples:
555            - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py)
556
557                ![](https://vedo.embl.es/images/basic/spline_tool.png)
558        """
559        vtk.vtkContourWidget.__init__(self)
560
561        self.representation = vtk.vtkOrientedGlyphContourRepresentation()
562        self.representation.SetAlwaysOnTop(ontop)
563
564        self.representation.GetLinesProperty().SetColor(get_color(lc))
565        self.representation.GetLinesProperty().SetLineWidth(lw)
566
567        self.representation.GetProperty().SetColor(get_color(pc))
568        self.representation.GetProperty().SetPointSize(ps)
569        self.representation.GetProperty().RenderPointsAsSpheresOn()
570
571        self.representation.GetActiveProperty().SetColor(get_color(ac))
572        self.representation.GetActiveProperty().SetLineWidth(lw + 1)
573
574        self.SetRepresentation(self.representation)
575
576        if utils.is_sequence(points):
577            self.points = Points(points)
578        else:
579            self.points = points
580
581        self.closed = closed

Spline tool, draw a spline through a set of points interactively.

Arguments:
  • points : (list), Points initial set of points.
  • pc : (str) point color.
  • ps : (int) point size.
  • lc : (str) line color.
  • ac : (str) active point color.
  • lw : (int) line width.
  • closed : (bool) spline is closed or open.
  • ontop : (bool) show it always on top of other objects.
Examples:
def add(self, pt):
583    def add(self, pt):
584        """
585        Add one point at a specified position in space if 3D,
586        or 2D screen-display position if 2D.
587        """
588        if len(pt) == 2:
589            self.representation.AddNodeAtDisplayPosition(int(pt[0]), int(pt[1]))
590        else:
591            self.representation.AddNodeAtWorldPosition(pt)
592        return self

Add one point at a specified position in space if 3D, or 2D screen-display position if 2D.

def remove(self, i):
594    def remove(self, i):
595        """Remove specific node by its index"""
596        self.representation.DeleteNthNode(i)
597        return self

Remove specific node by its index

def on(self):
599    def on(self):
600        """Activate/Enable the tool"""
601        self.On()
602        self.Render()
603        return self

Activate/Enable the tool

def off(self):
605    def off(self):
606        """Disactivate/Disable the tool"""
607        self.Off()
608        self.Render()
609        return self

Disactivate/Disable the tool

def render(self):
611    def render(self):
612        """Render the spline"""
613        self.Render()
614        return self

Render the spline

def bounds(self):
616    def bounds(self):
617        """Retrieve the bounding box of the spline as [x0,x1, y0,y1, z0,z1]"""
618        return self.GetBounds()

Retrieve the bounding box of the spline as [x0,x1, y0,y1, z0,z1]

def spline(self):
620    def spline(self):
621        """Return the vedo.Spline object."""
622        self.representation.SetClosedLoop(self.closed)
623        self.representation.BuildRepresentation()
624        pd = self.representation.GetContourRepresentationAsPolyData()
625        pts = utils.vtk2numpy(pd.GetPoints().GetData())
626        ln = vedo.Line(pts, lw=2, c="k")
627        return ln

Return the vedo.Spline object.

def nodes(self, onscreen=False):
629    def nodes(self, onscreen=False):
630        """Return the current position in space (or on 2D screen-display) of the spline nodes."""
631        n = self.representation.GetNumberOfNodes()
632        pts = []
633        for i in range(n):
634            p = [0.0, 0.0, 0.0]
635            if onscreen:
636                self.representation.GetNthNodeDisplayPosition(i, p)
637            else:
638                self.representation.GetNthNodeWorldPosition(i, p)
639            pts.append(p)
640        return np.array(pts)

Return the current position in space (or on 2D screen-display) of the spline nodes.

def Goniometer( p1, p2, p3, font='', arc_size=0.4, s=1, italic=0, rotation=0, prefix='', lc='k2', c='white', alpha=1, lw=2, precision=3):
707def Goniometer(
708    p1,
709    p2,
710    p3,
711    font="",
712    arc_size=0.4,
713    s=1,
714    italic=0,
715    rotation=0,
716    prefix="",
717    lc="k2",
718    c="white",
719    alpha=1,
720    lw=2,
721    precision=3,
722):
723    """
724    Build a graphical goniometer to measure the angle formed by 3 points in space.
725
726    Arguments:
727        p1 : (list)
728            first point 3D coordinates.
729        p2 : (list)
730            the vertex point.
731        p3 : (list)
732            the last point defining the angle.
733        font : (str)
734            Font face. Check [available fonts here](https://vedo.embl.es/fonts).
735        arc_size : (float)
736            dimension of the arc wrt the smallest axis.
737        s : (float)
738            size of the text.
739        italic : (float, bool)
740            italic text.
741        rotation : (float)
742            rotation of text in degrees.
743        prefix : (str)
744            append this string to the numeric value of the angle.
745        lc : (list)
746            color of the goniometer lines.
747        c : (str)
748            color of the goniometer angle filling. Set alpha=0 to remove it.
749        alpha : (float)
750            transparency level.
751        lw : (float)
752            line width.
753        precision : (int)
754            number of significant digits.
755
756    Examples:
757        - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py)
758
759            ![](https://vedo.embl.es/images/pyplot/goniometer.png)
760    """
761    if isinstance(p1, Points): p1 = p1.GetPosition()
762    if isinstance(p2, Points): p2 = p2.GetPosition()
763    if isinstance(p3, Points): p3 = p3.GetPosition()
764    if len(p1)==2: p1=[p1[0], p1[1], 0.0]
765    if len(p2)==2: p2=[p2[0], p2[1], 0.0]
766    if len(p3)==2: p3=[p3[0], p3[1], 0.0]
767    p1, p2, p3 = np.array(p1), np.array(p2), np.array(p3)
768
769    acts = []
770    ln = shapes.Line([p1, p2, p3], lw=lw, c=lc)
771    acts.append(ln)
772
773    va = utils.versor(p1 - p2)
774    vb = utils.versor(p3 - p2)
775    r = min(utils.mag(p3 - p2), utils.mag(p1 - p2)) * arc_size
776    ptsarc = []
777    res = 120
778    imed = int(res / 2)
779    for i in range(res + 1):
780        vi = utils.versor(vb * i / res + va * (res - i) / res)
781        if i == imed:
782            vc = np.array(vi)
783        ptsarc.append(p2 + vi * r)
784    arc = shapes.Line(ptsarc).lw(lw).c(lc)
785    acts.append(arc)
786
787    angle = np.arccos(np.dot(va, vb)) * 180 / np.pi
788
789    lb = shapes.Text3D(
790        prefix + utils.precision(angle, precision) + "º",
791        s=r / 12 * s,
792        font=font,
793        italic=italic,
794        justify="center",
795    )
796    cr = np.cross(va, vb)
797    lb.pos(p2 + vc * r / 1.75).orientation(cr * np.sign(cr[2]), rotation=rotation)
798    lb.c(c).bc("tomato").lighting("off")
799    acts.append(lb)
800
801    if alpha > 0:
802        pts = [p2] + arc.points().tolist() + [p2]
803        msh = Mesh([pts, [list(range(arc.npoints + 2))]], c=lc, alpha=alpha)
804        msh.lighting("off")
805        msh.triangulate()
806        msh.shift(0, 0, -r / 10000)  # to resolve 2d conflicts..
807        acts.append(msh)
808
809    asse = Assembly(acts)
810    return asse

Build a graphical goniometer to measure the angle formed by 3 points in space.

Arguments:
  • p1 : (list) first point 3D coordinates.
  • p2 : (list) the vertex point.
  • p3 : (list) the last point defining the angle.
  • font : (str) Font face. Check available fonts here.
  • arc_size : (float) dimension of the arc wrt the smallest axis.
  • s : (float) size of the text.
  • italic : (float, bool) italic text.
  • rotation : (float) rotation of text in degrees.
  • prefix : (str) append this string to the numeric value of the angle.
  • lc : (list) color of the goniometer lines.
  • c : (str) color of the goniometer angle filling. Set alpha=0 to remove it.
  • alpha : (float) transparency level.
  • lw : (float) line width.
  • precision : (int) number of significant digits.
Examples:
class Button(vtkmodules.vtkRenderingCore.vtkTextActor):
351class Button(vtk.vtkTextActor):
352    """
353    Build a Button object.
354    """
355    def __init__(
356            self, 
357            fnc=None, 
358            states=("Button"), 
359            c=("white"), 
360            bc=("green4"),
361            pos=(0.7, 0.05), 
362            size=24, 
363            font=None, 
364            bold=False, 
365            italic=False, 
366            alpha=1, 
367            angle=0,
368            name="Button",
369        ):
370        """
371        Build a Button object to be shown in the rendering window.
372
373        Arguments:
374            fnc : (function)
375                external function to be called by the widget
376            states : (list)
377                the list of possible states, eg. ['On', 'Off']
378            c : (list)
379                the list of colors for each state eg. ['red3', 'green5']
380            bc : (list)
381                the list of background colors for each state
382            pos : (list, str)
383                2D position in pixels from left-bottom corner
384            size : (int)
385                size of button font
386            font : (str)
387                font type
388            bold : (bool)
389                set bold font face
390            italic : (bool)
391                italic font face
392            alpha : (float)
393                opacity level
394            angle : (float)
395                anticlockwise rotation in degrees
396            name : (str)
397                name of the button (useful for multiple buttons in callbacks)
398    
399        Examples:
400            - [buttons.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/buttons.py)
401
402        Examples:
403            - [buttons.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/buttons.py)
404
405                ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg)
406
407            - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py)
408
409                ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg)
410        """
411        vtk.vtkTextActor.__init__(self)
412
413        self.status_idx = 0
414        self.states = states
415
416        if not utils.is_sequence(c):
417            c = [c]
418        self.colors = c
419
420        if not utils.is_sequence(bc):
421            bc = [bc]
422        self.bcolors = bc
423
424        assert len(c) == len(bc), "in Button color number mismatch!"
425
426        self.function = fnc
427        self.function_id = None
428        self.name = name
429
430        self.GetActualPositionCoordinate().SetCoordinateSystemToNormalizedViewport()
431        self.SetPosition(pos[0], pos[1])
432
433        self.offset = 5
434        self.spacer = " "
435
436        self.len_states = max([len(s) for s in states])
437
438        self.text_property = self.GetTextProperty()
439        self.text_property.SetJustificationToCentered()
440
441        if not font:
442            font = settings.default_font
443
444        if font.lower() == "courier":
445            self.text_property.SetFontFamilyToCourier()
446        elif font.lower() == "times":
447            self.text_property.SetFontFamilyToTimes()
448        elif font.lower() == "arial":
449            self.text_property.SetFontFamilyToArial()
450        else:
451            self.text_property.SetFontFamily(vtk.VTK_FONT_FILE)
452            self.text_property.SetFontFile(utils.get_font_path(font))
453        self.text_property.SetFontSize(size)
454
455        self.text_property.SetBackgroundOpacity(alpha)
456
457        self.text_property.BoldOff()
458        if bold:
459            self.text_property.BoldOn()
460
461        self.text_property.ItalicOff()
462        if italic:
463            self.text_property.ItalicOn()
464
465        self.text_property.ShadowOff()
466        self.text_property.SetOrientation(angle)
467        self.text_property.SetLineOffset(self.offset)
468
469        self.hasframe = hasattr(self.text_property, "FrameOn")
470
471        self.status(0)
472
473    def text(self, txt="", c=None):
474        if txt:
475            self.SetInput(self.spacer + str(txt) + self.spacer)
476        else:
477            return self.GetInput()
478
479        if c is not None:
480            self.text_property.SetColor(get_color(c))
481        return self
482
483    def backcolor(self, c):
484        self.text_property.SetBackgroundColor(get_color(c))
485        return self
486
487    def frame(self, lw=None, c=None):
488        if self.hasframe:
489            self.text_property.FrameOn()
490            if lw is not None:
491                if lw > 0:
492                    self.text_property.FrameOn()
493                    self.text_property.SetFrameWidth(lw)
494                else:
495                    self.text_property.FrameOff()
496                    return self
497            if c is not None:
498                self.text_property.SetFrameColor(get_color(c))
499        return self
500
501    def status(self, s=None):
502        """
503        Set/Get the status of the button.
504        """
505        if s is None:
506            return self.states[self.status_idx]
507
508        if isinstance(s, str):
509            s = self.states.index(s)
510        self.status_idx = s
511        self.text(self.states[s])
512        s = s % len(self.bcolors)
513        self.text(c=self.colors[s])
514        self.backcolor(self.bcolors[s])
515        return self
516
517    def switch(self):
518        """
519        Change/cycle button status to the next defined status in states list.
520        """
521        self.status_idx = (self.status_idx + 1) % len(self.states)
522        self.status(self.status_idx)
523        return self

Build a Button object.

Button( fnc=None, states='Button', c='white', bc='green4', pos=(0.7, 0.05), size=24, font=None, bold=False, italic=False, alpha=1, angle=0, name='Button')
355    def __init__(
356            self, 
357            fnc=None, 
358            states=("Button"), 
359            c=("white"), 
360            bc=("green4"),
361            pos=(0.7, 0.05), 
362            size=24, 
363            font=None, 
364            bold=False, 
365            italic=False, 
366            alpha=1, 
367            angle=0,
368            name="Button",
369        ):
370        """
371        Build a Button object to be shown in the rendering window.
372
373        Arguments:
374            fnc : (function)
375                external function to be called by the widget
376            states : (list)
377                the list of possible states, eg. ['On', 'Off']
378            c : (list)
379                the list of colors for each state eg. ['red3', 'green5']
380            bc : (list)
381                the list of background colors for each state
382            pos : (list, str)
383                2D position in pixels from left-bottom corner
384            size : (int)
385                size of button font
386            font : (str)
387                font type
388            bold : (bool)
389                set bold font face
390            italic : (bool)
391                italic font face
392            alpha : (float)
393                opacity level
394            angle : (float)
395                anticlockwise rotation in degrees
396            name : (str)
397                name of the button (useful for multiple buttons in callbacks)
398    
399        Examples:
400            - [buttons.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/buttons.py)
401
402        Examples:
403            - [buttons.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/buttons.py)
404
405                ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg)
406
407            - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py)
408
409                ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg)
410        """
411        vtk.vtkTextActor.__init__(self)
412
413        self.status_idx = 0
414        self.states = states
415
416        if not utils.is_sequence(c):
417            c = [c]
418        self.colors = c
419
420        if not utils.is_sequence(bc):
421            bc = [bc]
422        self.bcolors = bc
423
424        assert len(c) == len(bc), "in Button color number mismatch!"
425
426        self.function = fnc
427        self.function_id = None
428        self.name = name
429
430        self.GetActualPositionCoordinate().SetCoordinateSystemToNormalizedViewport()
431        self.SetPosition(pos[0], pos[1])
432
433        self.offset = 5
434        self.spacer = " "
435
436        self.len_states = max([len(s) for s in states])
437
438        self.text_property = self.GetTextProperty()
439        self.text_property.SetJustificationToCentered()
440
441        if not font:
442            font = settings.default_font
443
444        if font.lower() == "courier":
445            self.text_property.SetFontFamilyToCourier()
446        elif font.lower() == "times":
447            self.text_property.SetFontFamilyToTimes()
448        elif font.lower() == "arial":
449            self.text_property.SetFontFamilyToArial()
450        else:
451            self.text_property.SetFontFamily(vtk.VTK_FONT_FILE)
452            self.text_property.SetFontFile(utils.get_font_path(font))
453        self.text_property.SetFontSize(size)
454
455        self.text_property.SetBackgroundOpacity(alpha)
456
457        self.text_property.BoldOff()
458        if bold:
459            self.text_property.BoldOn()
460
461        self.text_property.ItalicOff()
462        if italic:
463            self.text_property.ItalicOn()
464
465        self.text_property.ShadowOff()
466        self.text_property.SetOrientation(angle)
467        self.text_property.SetLineOffset(self.offset)
468
469        self.hasframe = hasattr(self.text_property, "FrameOn")
470
471        self.status(0)

Build a Button object to be shown in the rendering window.

Arguments:
  • fnc : (function) external function to be called by the widget
  • states : (list) the list of possible states, eg. ['On', 'Off']
  • c : (list) the list of colors for each state eg. ['red3', 'green5']
  • bc : (list) the list of background colors for each state
  • pos : (list, str) 2D position in pixels from left-bottom corner
  • size : (int) size of button font
  • font : (str) font type
  • bold : (bool) set bold font face
  • italic : (bool) italic font face
  • alpha : (float) opacity level
  • angle : (float) anticlockwise rotation in degrees
  • name : (str) name of the button (useful for multiple buttons in callbacks)
Examples:
Examples:
def text(self, txt='', c=None):
473    def text(self, txt="", c=None):
474        if txt:
475            self.SetInput(self.spacer + str(txt) + self.spacer)
476        else:
477            return self.GetInput()
478
479        if c is not None:
480            self.text_property.SetColor(get_color(c))
481        return self
def backcolor(self, c):
483    def backcolor(self, c):
484        self.text_property.SetBackgroundColor(get_color(c))
485        return self
def frame(self, lw=None, c=None):
487    def frame(self, lw=None, c=None):
488        if self.hasframe:
489            self.text_property.FrameOn()
490            if lw is not None:
491                if lw > 0:
492                    self.text_property.FrameOn()
493                    self.text_property.SetFrameWidth(lw)
494                else:
495                    self.text_property.FrameOff()
496                    return self
497            if c is not None:
498                self.text_property.SetFrameColor(get_color(c))
499        return self
def status(self, s=None):
501    def status(self, s=None):
502        """
503        Set/Get the status of the button.
504        """
505        if s is None:
506            return self.states[self.status_idx]
507
508        if isinstance(s, str):
509            s = self.states.index(s)
510        self.status_idx = s
511        self.text(self.states[s])
512        s = s % len(self.bcolors)
513        self.text(c=self.colors[s])
514        self.backcolor(self.bcolors[s])
515        return self

Set/Get the status of the button.

def switch(self):
517    def switch(self):
518        """
519        Change/cycle button status to the next defined status in states list.
520        """
521        self.status_idx = (self.status_idx + 1) % len(self.states)
522        self.status(self.status_idx)
523        return self

Change/cycle button status to the next defined status in states list.

class Flagpost(vtkmodules.vtkRenderingCore.vtkFlagpoleLabel):
 55class Flagpost(vtk.vtkFlagpoleLabel):
 56    """
 57    Create a flag post style element to describe an object.
 58    """
 59
 60    def __init__(
 61        self,
 62        txt="",
 63        base=(0, 0, 0),
 64        top=(0, 0, 1),
 65        s=1,
 66        c="k9",
 67        bc="k1",
 68        alpha=1,
 69        lw=0,
 70        font="Calco",
 71        justify="center-left",
 72        vspacing=1,
 73    ):
 74        """
 75        Create a flag post style element to describe an object.
 76
 77        Arguments:
 78            txt : (str)
 79                Text to display. The default is the filename or the object name.
 80            base : (list)
 81                position of the flag anchor point.
 82            top : (list)
 83                a 3D displacement or offset.
 84            s : (float)
 85                size of the text to be shown
 86            c : (list)
 87                color of text and line
 88            bc : (list)
 89                color of the flag background
 90            alpha : (float)
 91                opacity of text and box.
 92            lw : (int)
 93                line with of box frame. The default is 0.
 94            font : (str)
 95                font name. Use a monospace font for better rendering. The default is "Calco".
 96                Type `vedo -r fonts` for a font demo.
 97                Check [available fonts here](https://vedo.embl.es/fonts).
 98            justify : (str)
 99                internal text justification. The default is "center-left".
100            vspacing : (float)
101                vertical spacing between lines.
102
103        Examples:
104            - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/examples/other/flag_labels2.py)
105
106            ![](https://vedo.embl.es/images/other/flag_labels2.png)
107        """
108
109        vtk.vtkFlagpoleLabel.__init__(self)
110
111        base = utils.make3d(base)
112        top = utils.make3d(top)
113
114        self.SetBasePosition(*base)
115        self.SetTopPosition(*top)
116
117        self.SetFlagSize(s)
118        self.SetInput(txt)
119        self.PickableOff()
120
121        self.GetProperty().LightingOff()
122        self.GetProperty().SetLineWidth(lw + 1)
123
124        prop = self.GetTextProperty()
125        if bc is not None:
126            prop.SetBackgroundColor(get_color(bc))
127
128        prop.SetOpacity(alpha)
129        prop.SetBackgroundOpacity(alpha)
130        if bc is not None and len(bc) == 4:
131            prop.SetBackgroundRGBA(alpha)
132
133        c = get_color(c)
134        prop.SetColor(c)
135        self.GetProperty().SetColor(c)
136
137        prop.SetFrame(bool(lw))
138        prop.SetFrameWidth(lw)
139        prop.SetFrameColor(prop.GetColor())
140
141        prop.SetFontFamily(vtk.VTK_FONT_FILE)
142        fl = utils.get_font_path(font)
143        prop.SetFontFile(fl)
144        prop.ShadowOff()
145        prop.BoldOff()
146        prop.SetOpacity(alpha)
147        prop.SetJustificationToLeft()
148        if "top" in justify:
149            prop.SetVerticalJustificationToTop()
150        if "bottom" in justify:
151            prop.SetVerticalJustificationToBottom()
152        if "cent" in justify:
153            prop.SetVerticalJustificationToCentered()
154            prop.SetJustificationToCentered()
155        if "left" in justify:
156            prop.SetJustificationToLeft()
157        if "right" in justify:
158            prop.SetJustificationToRight()
159        prop.SetLineSpacing(vspacing * 1.2)
160        self.SetUseBounds(False)
161
162    def text(self, value):
163        self.SetInput(value)
164        return self
165
166    def on(self):
167        self.VisibilityOn()
168        return self
169
170    def off(self):
171        self.VisibilityOff()
172        return self
173
174    def toggle(self):
175        self.SetVisibility(not self.GetVisibility())
176        return self
177
178    def use_bounds(self, value=True):
179        self.SetUseBounds(value)
180        return self
181
182    def color(self, c):
183        c = get_color(c)
184        self.GetTextProperty().SetColor(c)
185        self.GetProperty().SetColor(c)
186        return self
187
188    def pos(self, p):
189        p = np.asarray(p)
190        self.top = self.top - self.base + p
191        self.base = p
192        return self
193
194    @property
195    def base(self):
196        return np.array(self.GetBasePosition())
197
198    @property
199    def top(self):
200        return np.array(self.GetTopPosition())
201
202    @base.setter
203    def base(self, value):
204        self.SetBasePosition(*value)
205
206    @top.setter
207    def top(self, value):
208        self.SetTopPosition(*value)

Create a flag post style element to describe an object.

Flagpost( txt='', base=(0, 0, 0), top=(0, 0, 1), s=1, c='k9', bc='k1', alpha=1, lw=0, font='Calco', justify='center-left', vspacing=1)
 60    def __init__(
 61        self,
 62        txt="",
 63        base=(0, 0, 0),
 64        top=(0, 0, 1),
 65        s=1,
 66        c="k9",
 67        bc="k1",
 68        alpha=1,
 69        lw=0,
 70        font="Calco",
 71        justify="center-left",
 72        vspacing=1,
 73    ):
 74        """
 75        Create a flag post style element to describe an object.
 76
 77        Arguments:
 78            txt : (str)
 79                Text to display. The default is the filename or the object name.
 80            base : (list)
 81                position of the flag anchor point.
 82            top : (list)
 83                a 3D displacement or offset.
 84            s : (float)
 85                size of the text to be shown
 86            c : (list)
 87                color of text and line
 88            bc : (list)
 89                color of the flag background
 90            alpha : (float)
 91                opacity of text and box.
 92            lw : (int)
 93                line with of box frame. The default is 0.
 94            font : (str)
 95                font name. Use a monospace font for better rendering. The default is "Calco".
 96                Type `vedo -r fonts` for a font demo.
 97                Check [available fonts here](https://vedo.embl.es/fonts).
 98            justify : (str)
 99                internal text justification. The default is "center-left".
100            vspacing : (float)
101                vertical spacing between lines.
102
103        Examples:
104            - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/examples/other/flag_labels2.py)
105
106            ![](https://vedo.embl.es/images/other/flag_labels2.png)
107        """
108
109        vtk.vtkFlagpoleLabel.__init__(self)
110
111        base = utils.make3d(base)
112        top = utils.make3d(top)
113
114        self.SetBasePosition(*base)
115        self.SetTopPosition(*top)
116
117        self.SetFlagSize(s)
118        self.SetInput(txt)
119        self.PickableOff()
120
121        self.GetProperty().LightingOff()
122        self.GetProperty().SetLineWidth(lw + 1)
123
124        prop = self.GetTextProperty()
125        if bc is not None:
126            prop.SetBackgroundColor(get_color(bc))
127
128        prop.SetOpacity(alpha)
129        prop.SetBackgroundOpacity(alpha)
130        if bc is not None and len(bc) == 4:
131            prop.SetBackgroundRGBA(alpha)
132
133        c = get_color(c)
134        prop.SetColor(c)
135        self.GetProperty().SetColor(c)
136
137        prop.SetFrame(bool(lw))
138        prop.SetFrameWidth(lw)
139        prop.SetFrameColor(prop.GetColor())
140
141        prop.SetFontFamily(vtk.VTK_FONT_FILE)
142        fl = utils.get_font_path(font)
143        prop.SetFontFile(fl)
144        prop.ShadowOff()
145        prop.BoldOff()
146        prop.SetOpacity(alpha)
147        prop.SetJustificationToLeft()
148        if "top" in justify:
149            prop.SetVerticalJustificationToTop()
150        if "bottom" in justify:
151            prop.SetVerticalJustificationToBottom()
152        if "cent" in justify:
153            prop.SetVerticalJustificationToCentered()
154            prop.SetJustificationToCentered()
155        if "left" in justify:
156            prop.SetJustificationToLeft()
157        if "right" in justify:
158            prop.SetJustificationToRight()
159        prop.SetLineSpacing(vspacing * 1.2)
160        self.SetUseBounds(False)

Create a flag post style element to describe an object.

Arguments:
  • txt : (str) Text to display. The default is the filename or the object name.
  • base : (list) position of the flag anchor point.
  • top : (list) a 3D displacement or offset.
  • s : (float) size of the text to be shown
  • c : (list) color of text and line
  • bc : (list) color of the flag background
  • alpha : (float) opacity of text and box.
  • lw : (int) line with of box frame. The default is 0.
  • font : (str) font name. Use a monospace font for better rendering. The default is "Calco". Type vedo -r fonts for a font demo. Check available fonts here.
  • justify : (str) internal text justification. The default is "center-left".
  • vspacing : (float) vertical spacing between lines.
Examples:

def text(self, value):
162    def text(self, value):
163        self.SetInput(value)
164        return self
def on(self):
166    def on(self):
167        self.VisibilityOn()
168        return self
def off(self):
170    def off(self):
171        self.VisibilityOff()
172        return self
def toggle(self):
174    def toggle(self):
175        self.SetVisibility(not self.GetVisibility())
176        return self
def use_bounds(self, value=True):
178    def use_bounds(self, value=True):
179        self.SetUseBounds(value)
180        return self
def color(self, c):
182    def color(self, c):
183        c = get_color(c)
184        self.GetTextProperty().SetColor(c)
185        self.GetProperty().SetColor(c)
186        return self
def pos(self, p):
188    def pos(self, p):
189        p = np.asarray(p)
190        self.top = self.top - self.base + p
191        self.base = p
192        return self
class ProgressBarWidget(vtkmodules.vtkRenderingCore.vtkActor2D):
2143class ProgressBarWidget(vtk.vtkActor2D):
2144    """
2145    Add a progress bar in the rendering window.
2146    """
2147    def __init__(self, n=None, c="blue5", alpha=0.8, lw=10, autohide=True):
2148        """
2149        Add a progress bar window.
2150
2151        Arguments:
2152            n : (int)
2153                number of iterations. 
2154                If None, you need to call `update(fraction)` manually.
2155            c : (color)
2156                color of the line.
2157            alpha : (float)
2158                opacity of the line.
2159            lw : (int)
2160                line width in pixels.
2161            autohide : (bool)
2162                if True, hide the progress bar when completed.
2163        """
2164        self.n = 0
2165        self.iterations = n
2166        self.autohide = autohide
2167
2168        ppoints = vtk.vtkPoints()  # Generate the line
2169        psqr = [[0, 0, 0], [1, 0, 0]]
2170        for i, pt in enumerate(psqr):
2171            ppoints.InsertPoint(i, *pt)
2172        lines = vtk.vtkCellArray()
2173        lines.InsertNextCell(len(psqr))
2174        for i in range(len(psqr)):
2175            lines.InsertCellPoint(i)
2176        pd = vtk.vtkPolyData()
2177        pd.SetPoints(ppoints)
2178        pd.SetLines(lines)
2179        self._data = pd
2180
2181        mapper = vtk.vtkPolyDataMapper2D()
2182        mapper.SetInputData(pd)
2183        cs = vtk.vtkCoordinate()
2184        cs.SetCoordinateSystemToNormalizedViewport()
2185        mapper.SetTransformCoordinate(cs)
2186
2187        vtk.vtkActor2D.__init__(self)
2188        
2189        self.SetMapper(mapper)
2190        self.GetProperty().SetOpacity(alpha)
2191        self.GetProperty().SetColor(get_color(c))
2192        self.GetProperty().SetLineWidth(lw*2)
2193
2194        
2195    def lw(self, value):
2196        """Set width."""
2197        self.GetProperty().SetLineWidth(value*2)
2198        return self
2199
2200    def c(self, color):
2201        """Set color."""
2202        c = get_color(color)
2203        self.GetProperty().SetColor(c)
2204        return self
2205    
2206    def alpha(self, value):
2207        """Set opacity."""
2208        self.GetProperty().SetOpacity(value)
2209        return self
2210
2211    def update(self, fraction=None):
2212        """Update progress bar to fraction of the window width."""
2213        if fraction is None:
2214            if self.iterations is None:
2215                vedo.printc("Error in ProgressBarWindow: must specify iterations", c='r')
2216                return self
2217            self.n += 1
2218            fraction = self.n / self.iterations
2219
2220        if fraction >= 1 and self.autohide:
2221            fraction = 0
2222
2223        psqr = [[0, 0, 0], [fraction, 0, 0]]
2224        vpts = utils.numpy2vtk(psqr, dtype=np.float32)
2225        self._data.GetPoints().SetData(vpts)
2226        return self
2227    
2228    def reset(self):
2229        """Reset progress bar."""
2230        self.n = 0
2231        self.update(0)
2232        return self

Add a progress bar in the rendering window.

ProgressBarWidget(n=None, c='blue5', alpha=0.8, lw=10, autohide=True)
2147    def __init__(self, n=None, c="blue5", alpha=0.8, lw=10, autohide=True):
2148        """
2149        Add a progress bar window.
2150
2151        Arguments:
2152            n : (int)
2153                number of iterations. 
2154                If None, you need to call `update(fraction)` manually.
2155            c : (color)
2156                color of the line.
2157            alpha : (float)
2158                opacity of the line.
2159            lw : (int)
2160                line width in pixels.
2161            autohide : (bool)
2162                if True, hide the progress bar when completed.
2163        """
2164        self.n = 0
2165        self.iterations = n
2166        self.autohide = autohide
2167
2168        ppoints = vtk.vtkPoints()  # Generate the line
2169        psqr = [[0, 0, 0], [1, 0, 0]]
2170        for i, pt in enumerate(psqr):
2171            ppoints.InsertPoint(i, *pt)
2172        lines = vtk.vtkCellArray()
2173        lines.InsertNextCell(len(psqr))
2174        for i in range(len(psqr)):
2175            lines.InsertCellPoint(i)
2176        pd = vtk.vtkPolyData()
2177        pd.SetPoints(ppoints)
2178        pd.SetLines(lines)
2179        self._data = pd
2180
2181        mapper = vtk.vtkPolyDataMapper2D()
2182        mapper.SetInputData(pd)
2183        cs = vtk.vtkCoordinate()
2184        cs.SetCoordinateSystemToNormalizedViewport()
2185        mapper.SetTransformCoordinate(cs)
2186
2187        vtk.vtkActor2D.__init__(self)
2188        
2189        self.SetMapper(mapper)
2190        self.GetProperty().SetOpacity(alpha)
2191        self.GetProperty().SetColor(get_color(c))
2192        self.GetProperty().SetLineWidth(lw*2)

Add a progress bar window.

Arguments:
  • n : (int) number of iterations. If None, you need to call update(fraction) manually.
  • c : (color) color of the line.
  • alpha : (float) opacity of the line.
  • lw : (int) line width in pixels.
  • autohide : (bool) if True, hide the progress bar when completed.
def lw(self, value):
2195    def lw(self, value):
2196        """Set width."""
2197        self.GetProperty().SetLineWidth(value*2)
2198        return self

Set width.

def c(self, color):
2200    def c(self, color):
2201        """Set color."""
2202        c = get_color(color)
2203        self.GetProperty().SetColor(c)
2204        return self

Set color.

def alpha(self, value):
2206    def alpha(self, value):
2207        """Set opacity."""
2208        self.GetProperty().SetOpacity(value)
2209        return self

Set opacity.

def update(self, fraction=None):
2211    def update(self, fraction=None):
2212        """Update progress bar to fraction of the window width."""
2213        if fraction is None:
2214            if self.iterations is None:
2215                vedo.printc("Error in ProgressBarWindow: must specify iterations", c='r')
2216                return self
2217            self.n += 1
2218            fraction = self.n / self.iterations
2219
2220        if fraction >= 1 and self.autohide:
2221            fraction = 0
2222
2223        psqr = [[0, 0, 0], [fraction, 0, 0]]
2224        vpts = utils.numpy2vtk(psqr, dtype=np.float32)
2225        self._data.GetPoints().SetData(vpts)
2226        return self

Update progress bar to fraction of the window width.

def reset(self):
2228    def reset(self):
2229        """Reset progress bar."""
2230        self.n = 0
2231        self.update(0)
2232        return self

Reset progress bar.

class BoxCutter(vtkmodules.vtkInteractionWidgets.vtkBoxWidget, BaseCutter):
1860class BoxCutter(vtk.vtkBoxWidget, BaseCutter):
1861    """
1862    Create a box widget to cut away parts of a Mesh.
1863    """
1864    def __init__(
1865            self,
1866            mesh,
1867            invert=False,
1868            can_rotate=True,
1869            can_translate=True,
1870            can_scale=True,
1871            initial_bounds=(),
1872            padding=0.025,
1873            c=(0.25, 0.25, 0.25),
1874            alpha=0.05,
1875    ):
1876        """
1877        Create a box widget to cut away parts of a Mesh.
1878
1879        Arguments:
1880            mesh : (Mesh)
1881                the input mesh
1882            invert : (bool)
1883                invert the clipping plane
1884            can_rotate : (bool)
1885                enable rotation of the widget
1886            can_translate : (bool)
1887                enable translation of the widget
1888            can_scale : (bool)
1889                enable scaling of the widget
1890            initial_bounds : (list)
1891                initial bounds of the box widget
1892            c : (color)
1893                color of the box cutter widget
1894            alpha : (float)
1895                transparency of the cut-off part of the input mesh
1896        """
1897        super().__init__()
1898
1899        self.mesh = mesh
1900        self.remnant = Mesh()
1901        self.remnant.name = mesh.name + "Remnant"
1902        self.remnant.pickable(False)
1903
1904        self._alpha = alpha
1905        self._keypress_id = None
1906        self._init_bounds = initial_bounds
1907        if len(self._init_bounds) == 0:
1908            self._init_bounds = mesh.bounds()
1909        else:
1910            self._init_bounds = initial_bounds
1911
1912        self._implicit_func = vtk.vtkPlanes()
1913        self._implicit_func.SetBounds(self._init_bounds)
1914
1915        poly = mesh.polydata()
1916        self.clipper = vtk.vtkClipPolyData()
1917        self.clipper.GenerateClipScalarsOff()
1918        self.clipper.SetInputData(poly)
1919        self.clipper.SetClipFunction(self._implicit_func)
1920        self.clipper.SetInsideOut(not invert)
1921        self.clipper.GenerateClippedOutputOn()
1922        self.clipper.Update()
1923
1924        self.widget = vtk.vtkBoxWidget()
1925
1926        self.widget.SetRotationEnabled(can_rotate)
1927        self.widget.SetTranslationEnabled(can_translate)
1928        self.widget.SetScalingEnabled(can_scale)
1929
1930        self.widget.OutlineCursorWiresOn()
1931        self.widget.GetSelectedOutlineProperty().SetColor(get_color("red3"))
1932        self.widget.GetSelectedHandleProperty().SetColor(get_color("red5"))
1933       
1934        self.widget.GetOutlineProperty().SetColor(c)
1935        self.widget.GetOutlineProperty().SetOpacity(1)
1936        self.widget.GetOutlineProperty().SetLineWidth(1)
1937        self.widget.GetOutlineProperty().LightingOff()
1938
1939        self.widget.GetSelectedFaceProperty().LightingOff()
1940        self.widget.GetSelectedFaceProperty().SetOpacity(0.1)
1941
1942        self.widget.SetPlaceFactor(1.0 + padding)
1943        self.widget.SetInputData(poly)
1944        self.widget.PlaceWidget()
1945        self.widget.AddObserver("InteractionEvent", self._select_polygons)
1946
1947    def _select_polygons(self, vobj, event):
1948        vobj.GetPlanes(self._implicit_func)
1949
1950    def _keypress(self, vobj, event):
1951        if vobj.GetKeySym() == "r":  # reset planes
1952            self._implicit_func.SetBounds(self._init_bounds)
1953            self.widget.GetPlanes(self._implicit_func)
1954            self.widget.PlaceWidget()
1955            self.widget.GetInteractor().Render()
1956        elif vobj.GetKeySym() == "u":
1957            self.invert()
1958            self.widget.GetInteractor().Render()
1959        elif vobj.GetKeySym() == "s": # Ctrl+s to save mesh
1960            if self.widget.GetInteractor():
1961                if self.widget.GetInteractor().GetControlKey():
1962                    printc(":save: saving mesh to vedo_clipped.vtk")
1963                    self.mesh.write("vedo_clipped.vtk")

Create a box widget to cut away parts of a Mesh.

BoxCutter( mesh, invert=False, can_rotate=True, can_translate=True, can_scale=True, initial_bounds=(), padding=0.025, c=(0.25, 0.25, 0.25), alpha=0.05)
1864    def __init__(
1865            self,
1866            mesh,
1867            invert=False,
1868            can_rotate=True,
1869            can_translate=True,
1870            can_scale=True,
1871            initial_bounds=(),
1872            padding=0.025,
1873            c=(0.25, 0.25, 0.25),
1874            alpha=0.05,
1875    ):
1876        """
1877        Create a box widget to cut away parts of a Mesh.
1878
1879        Arguments:
1880            mesh : (Mesh)
1881                the input mesh
1882            invert : (bool)
1883                invert the clipping plane
1884            can_rotate : (bool)
1885                enable rotation of the widget
1886            can_translate : (bool)
1887                enable translation of the widget
1888            can_scale : (bool)
1889                enable scaling of the widget
1890            initial_bounds : (list)
1891                initial bounds of the box widget
1892            c : (color)
1893                color of the box cutter widget
1894            alpha : (float)
1895                transparency of the cut-off part of the input mesh
1896        """
1897        super().__init__()
1898
1899        self.mesh = mesh
1900        self.remnant = Mesh()
1901        self.remnant.name = mesh.name + "Remnant"
1902        self.remnant.pickable(False)
1903
1904        self._alpha = alpha
1905        self._keypress_id = None
1906        self._init_bounds = initial_bounds
1907        if len(self._init_bounds) == 0:
1908            self._init_bounds = mesh.bounds()
1909        else:
1910            self._init_bounds = initial_bounds
1911
1912        self._implicit_func = vtk.vtkPlanes()
1913        self._implicit_func.SetBounds(self._init_bounds)
1914
1915        poly = mesh.polydata()
1916        self.clipper = vtk.vtkClipPolyData()
1917        self.clipper.GenerateClipScalarsOff()
1918        self.clipper.SetInputData(poly)
1919        self.clipper.SetClipFunction(self._implicit_func)
1920        self.clipper.SetInsideOut(not invert)
1921        self.clipper.GenerateClippedOutputOn()
1922        self.clipper.Update()
1923
1924        self.widget = vtk.vtkBoxWidget()
1925
1926        self.widget.SetRotationEnabled(can_rotate)
1927        self.widget.SetTranslationEnabled(can_translate)
1928        self.widget.SetScalingEnabled(can_scale)
1929
1930        self.widget.OutlineCursorWiresOn()
1931        self.widget.GetSelectedOutlineProperty().SetColor(get_color("red3"))
1932        self.widget.GetSelectedHandleProperty().SetColor(get_color("red5"))
1933       
1934        self.widget.GetOutlineProperty().SetColor(c)
1935        self.widget.GetOutlineProperty().SetOpacity(1)
1936        self.widget.GetOutlineProperty().SetLineWidth(1)
1937        self.widget.GetOutlineProperty().LightingOff()
1938
1939        self.widget.GetSelectedFaceProperty().LightingOff()
1940        self.widget.GetSelectedFaceProperty().SetOpacity(0.1)
1941
1942        self.widget.SetPlaceFactor(1.0 + padding)
1943        self.widget.SetInputData(poly)
1944        self.widget.PlaceWidget()
1945        self.widget.AddObserver("InteractionEvent", self._select_polygons)

Create a box widget to cut away parts of a Mesh.

Arguments:
  • mesh : (Mesh) the input mesh
  • invert : (bool) invert the clipping plane
  • can_rotate : (bool) enable rotation of the widget
  • can_translate : (bool) enable translation of the widget
  • can_scale : (bool) enable scaling of the widget
  • initial_bounds : (list) initial bounds of the box widget
  • c : (color) color of the box cutter widget
  • alpha : (float) transparency of the cut-off part of the input mesh
class PlaneCutter(vtkmodules.vtkInteractionWidgets.vtkPlaneWidget, BaseCutter):
1728class PlaneCutter(vtk.vtkPlaneWidget, BaseCutter):
1729    """
1730    Create a box widget to cut away parts of a Mesh.
1731    """
1732    def __init__(
1733            self,
1734            mesh,
1735            invert=False,
1736            can_translate=True,
1737            can_scale=True,
1738            c=(0.25, 0.25, 0.25),
1739            origin=(),
1740            normal=(),
1741            padding=0.05,
1742            alpha=0.05,
1743    ):
1744        """
1745        Create a box widget to cut away parts of a Mesh.
1746
1747        Arguments:
1748            mesh : (Mesh)
1749                the input mesh
1750            invert : (bool)
1751                invert the clipping plane
1752            can_translate : (bool)
1753                enable translation of the widget
1754            can_scale : (bool)
1755                enable scaling of the widget
1756            origin : (list)
1757                origin of the plane
1758            normal : (list)
1759                normal to the plane
1760            padding : (float)
1761                padding around the input mesh
1762            c : (color)
1763                color of the box cutter widget
1764            alpha : (float)
1765                transparency of the cut-off part of the input mesh
1766        """
1767        super().__init__()
1768
1769        self.mesh = mesh
1770        self.remnant = Mesh()
1771        self.remnant.name = mesh.name + "Remnant"
1772        self.remnant.pickable(False)
1773        
1774        self._alpha = alpha
1775        self._keypress_id = None
1776
1777        self._implicit_func = vtk.vtkPlane()
1778
1779        poly = mesh.polydata()
1780        self.clipper = vtk.vtkClipPolyData()
1781        self.clipper.GenerateClipScalarsOff()
1782        self.clipper.SetInputData(poly)
1783        self.clipper.SetClipFunction(self._implicit_func)
1784        self.clipper.SetInsideOut(invert)
1785        self.clipper.GenerateClippedOutputOn()
1786        self.clipper.Update()
1787
1788        self.widget = vtk.vtkImplicitPlaneWidget()
1789
1790        # self.widget.KeyPressActivationOff()
1791        # self.widget.SetKeyPressActivationValue('i')
1792
1793        self.widget.SetOriginTranslation(can_translate)
1794        self.widget.SetOutlineTranslation(can_translate)
1795        self.widget.SetScaleEnabled(can_scale)
1796
1797        self.widget.GetOutlineProperty().SetColor(c)
1798        self.widget.GetOutlineProperty().SetOpacity(0.25)
1799        self.widget.GetOutlineProperty().SetLineWidth(1)
1800        self.widget.GetOutlineProperty().LightingOff()
1801
1802        self.widget.GetSelectedOutlineProperty().SetColor(get_color("red3"))
1803
1804        self.widget.SetTubing(0)
1805        self.widget.SetDrawPlane(1)
1806        self.widget.GetPlaneProperty().LightingOff()
1807        self.widget.GetPlaneProperty().SetOpacity(0.05)
1808        self.widget.GetSelectedPlaneProperty().SetColor(get_color("red5"))
1809        self.widget.GetSelectedPlaneProperty().LightingOff()
1810
1811        self.widget.SetPlaceFactor(1.0 + padding)
1812        self.widget.SetInputData(poly)
1813        self.widget.PlaceWidget()
1814        self.widget.AddObserver("InteractionEvent", self._select_polygons)
1815
1816        if len(origin) == 3:
1817            self.widget.SetOrigin(origin)
1818        else:
1819            self.widget.SetOrigin(mesh.center_of_mass())
1820        
1821        if len(normal) == 3:
1822            self.widget.SetNormal(normal)
1823        else:
1824            self.widget.SetNormal((1, 0, 0))
1825
1826        
1827    def _select_polygons(self, vobj, event):
1828        vobj.GetPlane(self._implicit_func)
1829
1830    def _keypress(self, vobj, event):
1831        if vobj.GetKeySym() == "r": # reset planes
1832            self.widget.GetPlane(self._implicit_func)
1833            self.widget.PlaceWidget()
1834            self.widget.GetInteractor().Render()
1835        elif vobj.GetKeySym() == "u": # invert cut
1836            self.invert()
1837            self.widget.GetInteractor().Render()
1838        elif vobj.GetKeySym() == "x": # set normal along x
1839            self.widget.SetNormal((1, 0, 0))
1840            self.widget.GetPlane(self._implicit_func)
1841            self.widget.PlaceWidget()
1842            self.widget.GetInteractor().Render()
1843        elif vobj.GetKeySym() == "y": # set normal along y
1844            self.widget.SetNormal((0, 1, 0))
1845            self.widget.GetPlane(self._implicit_func)
1846            self.widget.PlaceWidget()
1847            self.widget.GetInteractor().Render()
1848        elif vobj.GetKeySym() == "z": # set normal along z
1849            self.widget.SetNormal((0, 0, 1))
1850            self.widget.GetPlane(self._implicit_func)
1851            self.widget.PlaceWidget()
1852            self.widget.GetInteractor().Render()        
1853        elif vobj.GetKeySym() == "s": # Ctrl+s to save mesh
1854            if self.widget.GetInteractor():
1855                if self.widget.GetInteractor().GetControlKey():
1856                    printc(":save: saving mesh to vedo_clipped.vtk")
1857                    self.mesh.write("vedo_clipped.vtk")

Create a box widget to cut away parts of a Mesh.

PlaneCutter( mesh, invert=False, can_translate=True, can_scale=True, c=(0.25, 0.25, 0.25), origin=(), normal=(), padding=0.05, alpha=0.05)
1732    def __init__(
1733            self,
1734            mesh,
1735            invert=False,
1736            can_translate=True,
1737            can_scale=True,
1738            c=(0.25, 0.25, 0.25),
1739            origin=(),
1740            normal=(),
1741            padding=0.05,
1742            alpha=0.05,
1743    ):
1744        """
1745        Create a box widget to cut away parts of a Mesh.
1746
1747        Arguments:
1748            mesh : (Mesh)
1749                the input mesh
1750            invert : (bool)
1751                invert the clipping plane
1752            can_translate : (bool)
1753                enable translation of the widget
1754            can_scale : (bool)
1755                enable scaling of the widget
1756            origin : (list)
1757                origin of the plane
1758            normal : (list)
1759                normal to the plane
1760            padding : (float)
1761                padding around the input mesh
1762            c : (color)
1763                color of the box cutter widget
1764            alpha : (float)
1765                transparency of the cut-off part of the input mesh
1766        """
1767        super().__init__()
1768
1769        self.mesh = mesh
1770        self.remnant = Mesh()
1771        self.remnant.name = mesh.name + "Remnant"
1772        self.remnant.pickable(False)
1773        
1774        self._alpha = alpha
1775        self._keypress_id = None
1776
1777        self._implicit_func = vtk.vtkPlane()
1778
1779        poly = mesh.polydata()
1780        self.clipper = vtk.vtkClipPolyData()
1781        self.clipper.GenerateClipScalarsOff()
1782        self.clipper.SetInputData(poly)
1783        self.clipper.SetClipFunction(self._implicit_func)
1784        self.clipper.SetInsideOut(invert)
1785        self.clipper.GenerateClippedOutputOn()
1786        self.clipper.Update()
1787
1788        self.widget = vtk.vtkImplicitPlaneWidget()
1789
1790        # self.widget.KeyPressActivationOff()
1791        # self.widget.SetKeyPressActivationValue('i')
1792
1793        self.widget.SetOriginTranslation(can_translate)
1794        self.widget.SetOutlineTranslation(can_translate)
1795        self.widget.SetScaleEnabled(can_scale)
1796
1797        self.widget.GetOutlineProperty().SetColor(c)
1798        self.widget.GetOutlineProperty().SetOpacity(0.25)
1799        self.widget.GetOutlineProperty().SetLineWidth(1)
1800        self.widget.GetOutlineProperty().LightingOff()
1801
1802        self.widget.GetSelectedOutlineProperty().SetColor(get_color("red3"))
1803
1804        self.widget.SetTubing(0)
1805        self.widget.SetDrawPlane(1)
1806        self.widget.GetPlaneProperty().LightingOff()
1807        self.widget.GetPlaneProperty().SetOpacity(0.05)
1808        self.widget.GetSelectedPlaneProperty().SetColor(get_color("red5"))
1809        self.widget.GetSelectedPlaneProperty().LightingOff()
1810
1811        self.widget.SetPlaceFactor(1.0 + padding)
1812        self.widget.SetInputData(poly)
1813        self.widget.PlaceWidget()
1814        self.widget.AddObserver("InteractionEvent", self._select_polygons)
1815
1816        if len(origin) == 3:
1817            self.widget.SetOrigin(origin)
1818        else:
1819            self.widget.SetOrigin(mesh.center_of_mass())
1820        
1821        if len(normal) == 3:
1822            self.widget.SetNormal(normal)
1823        else:
1824            self.widget.SetNormal((1, 0, 0))

Create a box widget to cut away parts of a Mesh.

Arguments:
  • mesh : (Mesh) the input mesh
  • invert : (bool) invert the clipping plane
  • can_translate : (bool) enable translation of the widget
  • can_scale : (bool) enable scaling of the widget
  • origin : (list) origin of the plane
  • normal : (list) normal to the plane
  • padding : (float) padding around the input mesh
  • c : (color) color of the box cutter widget
  • alpha : (float) transparency of the cut-off part of the input mesh
class SphereCutter(vtkmodules.vtkInteractionWidgets.vtkSphereWidget, BaseCutter):
1966class SphereCutter(vtk.vtkSphereWidget, BaseCutter):
1967    """
1968    Create a box widget to cut away parts of a Mesh.
1969    """
1970    def __init__(
1971            self,
1972            mesh,
1973            invert=False,
1974            can_translate=True,
1975            can_scale=True,
1976            origin=(),
1977            radius=0,
1978            res=60,
1979            c='white',
1980            alpha=0.05,
1981    ):
1982        """
1983        Create a box widget to cut away parts of a Mesh.
1984
1985        Arguments:
1986            mesh : Mesh
1987                the input mesh
1988            invert : bool
1989                invert the clipping
1990            can_translate : bool
1991                enable translation of the widget
1992            can_scale : bool
1993                enable scaling of the widget
1994            origin : list
1995                initial position of the sphere widget
1996            radius : float
1997                initial radius of the sphere widget
1998            res : int
1999                resolution of the sphere widget
2000            c : color
2001                color of the box cutter widget
2002            alpha : float
2003                transparency of the cut-off part of the input mesh
2004        """
2005        super().__init__()
2006
2007        self.mesh = mesh
2008        self.remnant = Mesh()
2009        self.remnant.name = mesh.name + "Remnant"
2010        self.remnant.pickable(False)
2011
2012        self._alpha = alpha
2013        self._keypress_id = None
2014
2015        self._implicit_func = vtk.vtkSphere()
2016
2017        if len(origin) == 3:
2018            self._implicit_func.SetCenter(origin)
2019        else:
2020            origin = mesh.center_of_mass()
2021            self._implicit_func.SetCenter(origin)
2022        
2023        if radius > 0:
2024            self._implicit_func.SetRadius(radius)
2025        else:
2026            radius = mesh.average_size() * 2
2027            self._implicit_func.SetRadius(radius)
2028            
2029        poly = mesh.polydata()
2030        self.clipper = vtk.vtkClipPolyData()
2031        self.clipper.GenerateClipScalarsOff()
2032        self.clipper.SetInputData(poly)
2033        self.clipper.SetClipFunction(self._implicit_func)
2034        self.clipper.SetInsideOut(not invert)
2035        self.clipper.GenerateClippedOutputOn()
2036        self.clipper.Update()
2037
2038        self.widget = vtk.vtkSphereWidget()
2039
2040        self.widget.SetThetaResolution(res*2)
2041        self.widget.SetPhiResolution(res)
2042        self.widget.SetRadius(radius)
2043        self.widget.SetCenter(origin)
2044        self.widget.SetRepresentation(2)
2045        self.widget.HandleVisibilityOff()
2046
2047        self.widget.SetTranslation(can_translate)
2048        self.widget.SetScale(can_scale)
2049
2050        self.widget.HandleVisibilityOff()
2051        self.widget.GetSphereProperty().SetColor(get_color(c))
2052        self.widget.GetSphereProperty().SetOpacity(0.2)
2053        self.widget.GetSelectedSphereProperty().SetColor(get_color("red5"))
2054        self.widget.GetSelectedSphereProperty().SetOpacity(0.2)
2055
2056        self.widget.SetPlaceFactor(1.0)
2057        self.widget.SetInputData(poly)
2058        self.widget.PlaceWidget()
2059        self.widget.AddObserver("InteractionEvent", self._select_polygons)
2060
2061    def _select_polygons(self, vobj, event):
2062        vobj.GetSphere(self._implicit_func)
2063
2064    def _keypress(self, vobj, event):
2065        if vobj.GetKeySym() == "r":  # reset planes
2066            self._implicit_func.SetBounds(self._init_bounds)
2067            self.widget.GetPlanes(self._implicit_func)
2068            self.widget.PlaceWidget()
2069            self.widget.GetInteractor().Render()
2070        elif vobj.GetKeySym() == "u":
2071            self.invert()
2072            self.widget.GetInteractor().Render()
2073        elif vobj.GetKeySym() == "s": # Ctrl+s to save mesh
2074            if self.widget.GetInteractor():
2075                if self.widget.GetInteractor().GetControlKey():
2076                    printc(":save: saving mesh to vedo_clipped.vtk")
2077                    self.mesh.write("vedo_clipped.vtk")

Create a box widget to cut away parts of a Mesh.

SphereCutter( mesh, invert=False, can_translate=True, can_scale=True, origin=(), radius=0, res=60, c='white', alpha=0.05)
1970    def __init__(
1971            self,
1972            mesh,
1973            invert=False,
1974            can_translate=True,
1975            can_scale=True,
1976            origin=(),
1977            radius=0,
1978            res=60,
1979            c='white',
1980            alpha=0.05,
1981    ):
1982        """
1983        Create a box widget to cut away parts of a Mesh.
1984
1985        Arguments:
1986            mesh : Mesh
1987                the input mesh
1988            invert : bool
1989                invert the clipping
1990            can_translate : bool
1991                enable translation of the widget
1992            can_scale : bool
1993                enable scaling of the widget
1994            origin : list
1995                initial position of the sphere widget
1996            radius : float
1997                initial radius of the sphere widget
1998            res : int
1999                resolution of the sphere widget
2000            c : color
2001                color of the box cutter widget
2002            alpha : float
2003                transparency of the cut-off part of the input mesh
2004        """
2005        super().__init__()
2006
2007        self.mesh = mesh
2008        self.remnant = Mesh()
2009        self.remnant.name = mesh.name + "Remnant"
2010        self.remnant.pickable(False)
2011
2012        self._alpha = alpha
2013        self._keypress_id = None
2014
2015        self._implicit_func = vtk.vtkSphere()
2016
2017        if len(origin) == 3:
2018            self._implicit_func.SetCenter(origin)
2019        else:
2020            origin = mesh.center_of_mass()
2021            self._implicit_func.SetCenter(origin)
2022        
2023        if radius > 0:
2024            self._implicit_func.SetRadius(radius)
2025        else:
2026            radius = mesh.average_size() * 2
2027            self._implicit_func.SetRadius(radius)
2028            
2029        poly = mesh.polydata()
2030        self.clipper = vtk.vtkClipPolyData()
2031        self.clipper.GenerateClipScalarsOff()
2032        self.clipper.SetInputData(poly)
2033        self.clipper.SetClipFunction(self._implicit_func)
2034        self.clipper.SetInsideOut(not invert)
2035        self.clipper.GenerateClippedOutputOn()
2036        self.clipper.Update()
2037
2038        self.widget = vtk.vtkSphereWidget()
2039
2040        self.widget.SetThetaResolution(res*2)
2041        self.widget.SetPhiResolution(res)
2042        self.widget.SetRadius(radius)
2043        self.widget.SetCenter(origin)
2044        self.widget.SetRepresentation(2)
2045        self.widget.HandleVisibilityOff()
2046
2047        self.widget.SetTranslation(can_translate)
2048        self.widget.SetScale(can_scale)
2049
2050        self.widget.HandleVisibilityOff()
2051        self.widget.GetSphereProperty().SetColor(get_color(c))
2052        self.widget.GetSphereProperty().SetOpacity(0.2)
2053        self.widget.GetSelectedSphereProperty().SetColor(get_color("red5"))
2054        self.widget.GetSelectedSphereProperty().SetOpacity(0.2)
2055
2056        self.widget.SetPlaceFactor(1.0)
2057        self.widget.SetInputData(poly)
2058        self.widget.PlaceWidget()
2059        self.widget.AddObserver("InteractionEvent", self._select_polygons)

Create a box widget to cut away parts of a Mesh.

Arguments:
  • mesh : Mesh the input mesh
  • invert : bool invert the clipping
  • can_translate : bool enable translation of the widget
  • can_scale : bool enable scaling of the widget
  • origin : list initial position of the sphere widget
  • radius : float initial radius of the sphere widget
  • res : int resolution of the sphere widget
  • c : color color of the box cutter widget
  • alpha : float transparency of the cut-off part of the input mesh