vedo.base

Base classes. Do not instantiate.

   1#!/usr/bin/env python3
   2# -*- coding: utf-8 -*-
   3import time
   4import numpy as np
   5
   6try:
   7    import vedo.vtkclasses as vtk
   8except ImportError:
   9    import vtkmodules.all as vtk
  10
  11import vedo
  12from vedo import colors
  13from vedo import utils
  14
  15__docformat__ = "google"
  16
  17__doc__ = "Base classes. Do not instantiate."
  18
  19__all__ = [
  20    "Base3DProp",
  21    "BaseActor",
  22    "BaseActor2D",
  23    "BaseGrid",
  24    "probe_points",
  25    "probe_line",
  26    "probe_plane",
  27]
  28
  29
  30###############################################################################
  31class _DataArrayHelper:
  32    # Helper class to manage data associated to either
  33    # points (or vertices) and cells (or faces).
  34    # Internal use only.
  35    def __init__(self, actor, association):
  36        self.actor = actor
  37        self.association = association
  38
  39    def __getitem__(self, key):
  40
  41        if self.association == 0:
  42            data = self.actor.inputdata().GetPointData()
  43
  44        elif self.association == 1:
  45            data = self.actor.inputdata().GetCellData()
  46
  47        elif self.association == 2:
  48            data = self.actor.inputdata().GetFieldData()
  49
  50            varr = data.GetAbstractArray(key)
  51            if isinstance(varr, vtk.vtkStringArray):
  52                if isinstance(key, int):
  53                    key = data.GetArrayName(key)
  54                n = varr.GetNumberOfValues()
  55                narr = [varr.GetValue(i) for i in range(n)]
  56                return narr
  57                ###########
  58
  59        else:
  60            raise RuntimeError()
  61
  62        if isinstance(key, int):
  63            key = data.GetArrayName(key)
  64
  65        arr = data.GetArray(key)
  66        if not arr:
  67            return None
  68        return utils.vtk2numpy(arr)
  69
  70    def __setitem__(self, key, input_array):
  71
  72        if self.association == 0:
  73            data = self.actor.inputdata().GetPointData()
  74            n = self.actor.inputdata().GetNumberOfPoints()
  75            self.actor._mapper.SetScalarModeToUsePointData()
  76
  77        elif self.association == 1:
  78            data = self.actor.inputdata().GetCellData()
  79            n = self.actor.inputdata().GetNumberOfCells()
  80            self.actor._mapper.SetScalarModeToUseCellData()
  81
  82        elif self.association == 2:
  83            data = self.actor.inputdata().GetFieldData()
  84            if not utils.is_sequence(input_array):
  85                input_array = [input_array]
  86
  87            if isinstance(input_array[0], str):
  88                varr = vtk.vtkStringArray()
  89                varr.SetName(key)
  90                varr.SetNumberOfComponents(1)
  91                varr.SetNumberOfTuples(len(input_array))
  92                for i, iarr in enumerate(input_array):
  93                    if isinstance(iarr, np.ndarray):
  94                        iarr = iarr.tolist()  # better format
  95                        # Note: a string k can be converted to numpy with
  96                        # import json; k = np.array(json.loads(k))
  97                    varr.InsertValue(i, str(iarr))
  98            else:
  99                try:
 100                    varr = utils.numpy2vtk(input_array, name=key)
 101                except TypeError as e:
 102                    vedo.logger.error(
 103                        f"cannot create metadata with input object:\n"
 104                        f"{input_array}"
 105                        f"\n\nAllowed content examples are:\n"
 106                        f"- flat list of strings ['a','b', 1, [1,2,3], ...]"
 107                        f" (first item must be a string in this case)\n"
 108                        f"  hint: use k = np.array(json.loads(k)) to convert strings\n"
 109                        f"- numpy arrays of any shape"
 110                    )
 111                    raise e
 112
 113            data.AddArray(varr)
 114            return  ############
 115
 116        else:
 117            raise RuntimeError()
 118
 119        if len(input_array) != n:
 120            vedo.logger.error(
 121                f"Error in point/cell data: length of input {len(input_array)}"
 122                f" !=  {n} nr. of elements"
 123            )
 124            raise RuntimeError()
 125
 126        input_array = np.asarray(input_array)
 127        varr = utils.numpy2vtk(input_array, name=key)
 128        data.AddArray(varr)
 129
 130        if len(input_array.shape) == 1:  # scalars
 131            data.SetActiveScalars(key)
 132        elif len(input_array.shape) == 2 and input_array.shape[1] == 3:  # vectors
 133            if key.lower() == "normals":
 134                data.SetActiveNormals(key)
 135            else:
 136                data.SetActiveVectors(key)
 137
 138    def keys(self):
 139        """Return the list of available data array names"""
 140        if self.association == 0:
 141            data = self.actor.inputdata().GetPointData()
 142        elif self.association == 1:
 143            data = self.actor.inputdata().GetCellData()
 144        elif self.association == 2:
 145            data = self.actor.inputdata().GetFieldData()
 146        arrnames = []
 147        for i in range(data.GetNumberOfArrays()):
 148            name = data.GetArray(i).GetName()
 149            if name:
 150                arrnames.append(name)
 151        return arrnames
 152
 153    def remove(self, key):
 154        """Remove a data array by name or number"""
 155        if self.association == 0:
 156            self.actor.inputdata().GetPointData().RemoveArray(key)
 157        elif self.association == 1:
 158            self.actor.inputdata().GetCellData().RemoveArray(key)
 159        elif self.association == 2:
 160            self.actor.inputdata().GetFieldData().RemoveArray(key)
 161
 162    def clear(self):
 163        """Remove all data associated to this object"""
 164        if self.association == 0:
 165            data = self.actor.inputdata().GetPointData()
 166        elif self.association == 1:
 167            data = self.actor.inputdata().GetCellData()
 168        elif self.association == 2:
 169            data = self.actor.inputdata().GetFieldData()
 170        for i in range(data.GetNumberOfArrays()):
 171            name = data.GetArray(i).GetName()
 172            data.RemoveArray(name)
 173
 174    def rename(self, oldname, newname):
 175        """Rename an array"""
 176        if self.association == 0:
 177            varr = self.actor.inputdata().GetPointData().GetArray(oldname)
 178        elif self.association == 1:
 179            varr = self.actor.inputdata().GetCellData().GetArray(oldname)
 180        elif self.association == 2:
 181            varr = self.actor.inputdata().GetFieldData().GetArray(oldname)
 182        if varr:
 183            varr.SetName(newname)
 184        else:
 185            vedo.logger.warning(f"Cannot rename non existing array {oldname} to {newname}")
 186
 187    def select(self, key):
 188        """Select one specific array by its name to make it the `active` one."""
 189        if self.association == 0:
 190            data = self.actor.inputdata().GetPointData()
 191            self.actor.mapper().SetScalarModeToUsePointData()
 192        else:
 193            data = self.actor.inputdata().GetCellData()
 194            self.actor.mapper().SetScalarModeToUseCellData()
 195
 196        if isinstance(key, int):
 197            key = data.GetArrayName(key)
 198
 199        arr = data.GetArray(key)
 200        if not arr:
 201            return
 202
 203        nc = arr.GetNumberOfComponents()
 204        if nc == 1:
 205            data.SetActiveScalars(key)
 206        elif nc >= 2:
 207            if "rgb" in key.lower():
 208                data.SetActiveScalars(key)
 209                # try:
 210                #     self.actor.mapper().SetColorModeToDirectScalars()
 211                # except AttributeError:
 212                #     pass
 213            else:
 214                data.SetActiveVectors(key)
 215        elif nc >= 4:
 216            data.SetActiveTensors(key)
 217
 218        try:
 219            self.actor.mapper().SetArrayName(key)
 220            self.actor.mapper().ScalarVisibilityOn()
 221            # .. could be a volume mapper
 222        except AttributeError:
 223            pass
 224
 225    def select_scalars(self, key):
 226        """Select one specific scalar array by its name to make it the `active` one."""
 227        if self.association == 0:
 228            data = self.actor.inputdata().GetPointData()
 229            self.actor.mapper().SetScalarModeToUsePointData()
 230        else:
 231            data = self.actor.inputdata().GetCellData()
 232            self.actor.mapper().SetScalarModeToUseCellData()
 233
 234        if isinstance(key, int):
 235            key = data.GetArrayName(key)
 236
 237        data.SetActiveScalars(key)
 238
 239        try:
 240            self.actor.mapper().SetArrayName(key)
 241            self.actor.mapper().ScalarVisibilityOn()
 242        except AttributeError:
 243            pass
 244
 245    def select_vectors(self, key):
 246        """Select one specific vector array by its name to make it the `active` one."""
 247        if self.association == 0:
 248            data = self.actor.inputdata().GetPointData()
 249            self.actor.mapper().SetScalarModeToUsePointData()
 250        else:
 251            data = self.actor.inputdata().GetCellData()
 252            self.actor.mapper().SetScalarModeToUseCellData()
 253
 254        if isinstance(key, int):
 255            key = data.GetArrayName(key)
 256
 257        data.SetActiveVectors(key)
 258
 259        try:
 260            self.actor.mapper().SetArrayName(key)
 261            self.actor.mapper().ScalarVisibilityOn()
 262        except AttributeError:
 263            pass
 264
 265    def print(self, **kwargs):
 266        """Print the array names available to terminal"""
 267        colors.printc(self.keys(), **kwargs)
 268
 269    def __repr__(self) -> str:
 270        """Representation"""
 271
 272        def _get_str(pd, header):
 273            if pd.GetNumberOfArrays():
 274                out = f"\x1b[2m\x1b[1m\x1b[7m{header}"
 275                if self.actor.name:
 276                    out += f" in {self.actor.name}"
 277                out += f" contains {pd.GetNumberOfArrays()} array(s)\x1b[0m"
 278                for i in range(pd.GetNumberOfArrays()):
 279                    varr = pd.GetArray(i)
 280                    out += f"\n\x1b[1m\x1b[4mArray name    : {varr.GetName()}\x1b[0m"
 281                    out += "\nindex".ljust(15) + f": {i}"
 282                    t = varr.GetDataType()
 283                    if t in vedo.utils.array_types:
 284                        out += f"\ntype".ljust(15)
 285                        out += f": {vedo.utils.array_types[t][1]} ({vedo.utils.array_types[t][0]})"
 286                    shape = (varr.GetNumberOfTuples(), varr.GetNumberOfComponents())
 287                    out += "\nshape".ljust(15) + f": {shape}"
 288                    out += "\nrange".ljust(15) + f": {np.array(varr.GetRange())}"
 289                    out += "\nmax id".ljust(15) + f": {varr.GetMaxId()}"
 290                    out += "\nlook up table".ljust(15) + f": {bool(varr.GetLookupTable())}"
 291                    out += "\nin-memory size".ljust(15) + f": {varr.GetActualMemorySize()} KB"
 292            else:
 293                out += " has no associated data."
 294            return out
 295
 296        if self.association == 0:
 297            out = _get_str(self.actor._data.GetPointData(), "Point Data")
 298        elif self.association == 1:
 299            out = _get_str(self.actor._data.GetCellData(), "Cell Data")
 300        elif self.association == 2:
 301            pd = self.actor._data.GetFieldData()
 302            if pd.GetNumberOfArrays():
 303                out = f"\x1b[2m\x1b[1m\x1b[7mMeta Data"
 304                if self.actor.name:
 305                    out += f" in {self.actor.name}"
 306                out += f" contains {pd.GetNumberOfArrays()} entries\x1b[0m"
 307                for i in range(pd.GetNumberOfArrays()):
 308                    varr = pd.GetAbstractArray(i)
 309                    out += f"\n\x1b[1m\x1b[4mEntry name    : {varr.GetName()}\x1b[0m"
 310                    out += "\nindex".ljust(15) + f": {i}"
 311                    shape = (varr.GetNumberOfTuples(), varr.GetNumberOfComponents())
 312                    out += "\nshape".ljust(15) + f": {shape}"
 313
 314        return out
 315
 316
 317###############################################################################
 318class Base3DProp:
 319    """
 320    Base class to manage positioning and size of the objects in space and other properties.
 321
 322    .. warning:: Do not use this class to instantiate objects
 323    """
 324
 325    def __init__(self):
 326        """
 327        Base class to manage positioning and size of the objects in space and other properties.
 328        """
 329        self.filename = ""
 330        self.name = ""
 331        self.file_size = ""
 332        self.created = ""
 333        self.trail = None
 334        self.trail_points = []
 335        self.trail_segment_size = 0
 336        self.trail_offset = None
 337        self.shadows = []
 338        self.axes = None
 339        self.picked3d = None
 340        self.units = None
 341        self.top = np.array([0, 0, 1])
 342        self.base = np.array([0, 0, 0])
 343        self.info = {}
 344        self.time = time.time()
 345        self.rendered_at = set()
 346        self.transform = None
 347        self._isfollower = False  # set by mesh.follow_camera()
 348
 349        self.point_locator = None
 350        self.cell_locator = None
 351
 352        self.scalarbar = None
 353        # self.scalarbars = dict() #TODO
 354        self.pipeline = None
 355
 356    def address(self):
 357        """
 358        Return a unique memory address integer which may serve as the ID of the
 359        object, or passed to c++ code.
 360        """
 361        # https://www.linkedin.com/pulse/speedup-your-code-accessing-python-vtk-objects-from-c-pletzer/
 362        # https://github.com/tfmoraes/polydata_connectivity
 363        return int(self.inputdata().GetAddressAsString("")[5:], 16)
 364
 365    def pickable(self, value=None):
 366        """Set/get the pickability property of an object."""
 367        if value is None:
 368            return self.GetPickable()
 369        self.SetPickable(value)
 370        return self
 371
 372    def draggable(self, value=None):  # NOT FUNCTIONAL?
 373        """Set/get the draggability property of an object."""
 374        if value is None:
 375            return self.GetDragable()
 376        self.SetDragable(value)
 377        return self
 378
 379    def origin(self, x=None, y=None, z=None):
 380        """
 381        Set/get object's origin.
 382
 383        Relevant to control the scaling with `scale()` and rotations.
 384        Has no effect on position.
 385        """
 386        if x is None:
 387            return np.array(self.GetOrigin()) + self.GetPosition()
 388
 389        if z is None and y is None:  # assume x is of the form (x,y,z)
 390            if len(x) == 3:
 391                x, y, z = x
 392            else:
 393                x, y = x
 394                z = 0
 395        elif z is None:  # assume x,y is of the form x, y
 396            z = 0
 397        self.SetOrigin([x, y, z] - np.array(self.GetPosition()))
 398        return self
 399
 400    def pos(self, x=None, y=None, z=None):
 401        """Set/Get object position."""
 402        if x is None:  # get functionality
 403            return np.array(self.GetPosition())
 404
 405        if z is None and y is None:  # assume x is of the form (x,y,z)
 406            if len(x) == 3:
 407                x, y, z = x
 408            else:
 409                x, y = x
 410                z = 0
 411        elif z is None:  # assume x,y is of the form x, y
 412            z = 0
 413        self.SetPosition(x, y, z)
 414
 415        self.point_locator = None
 416        self.cell_locator = None
 417        return self  # return itself to concatenate methods
 418
 419    def shift(self, dx=0, dy=0, dz=0):
 420        """Add a vector to the current object position."""
 421        p = np.array(self.GetPosition())
 422
 423        if utils.is_sequence(dx):
 424            if len(dx) == 2:
 425                self.SetPosition(p + [dx[0], dx[1], 0])
 426            else:
 427                self.SetPosition(p + dx)
 428        else:
 429            self.SetPosition(p + [dx, dy, dz])
 430
 431        self.point_locator = None
 432        self.cell_locator = None
 433        return self
 434
 435    def x(self, val=None):
 436        """Set/Get object position along x axis."""
 437        p = self.GetPosition()
 438        if val is None:
 439            return p[0]
 440        self.pos(val, p[1], p[2])
 441        return self
 442
 443    def y(self, val=None):
 444        """Set/Get object position along y axis."""
 445        p = self.GetPosition()
 446        if val is None:
 447            return p[1]
 448        self.pos(p[0], val, p[2])
 449        return self
 450
 451    def z(self, val=None):
 452        """Set/Get object position along z axis."""
 453        p = self.GetPosition()
 454        if val is None:
 455            return p[2]
 456        self.pos(p[0], p[1], val)
 457        return self
 458
 459    def rotate(self, angle, axis=(1, 0, 0), point=(0, 0, 0), rad=False):
 460        """
 461        Rotate around an arbitrary `axis` passing through `point`.
 462
 463        Example:
 464            ```python
 465            from vedo import *
 466            c1 = Cube()
 467            c2 = c1.clone().c('violet').alpha(0.5) # copy of c1
 468            v = vector(0.2,1,0)
 469            p = vector(1,0,0)  # axis passes through this point
 470            c2.rotate(90, axis=v, point=p)
 471            l = Line(-v+p, v+p).lw(3).c('red')
 472            show(c1, l, c2, axes=1).close()
 473            ```
 474            ![](https://vedo.embl.es/images/feats/rotate_axis.png)
 475        """
 476        if rad:
 477            anglerad = angle
 478        else:
 479            anglerad = np.deg2rad(angle)
 480        axis = utils.versor(axis)
 481        a = np.cos(anglerad / 2)
 482        b, c, d = -axis * np.sin(anglerad / 2)
 483        aa, bb, cc, dd = a * a, b * b, c * c, d * d
 484        bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
 485        R = np.array(
 486            [
 487                [aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)],
 488                [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)],
 489                [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc],
 490            ]
 491        )
 492        rv = np.dot(R, self.GetPosition() - np.asarray(point)) + point
 493
 494        if rad:
 495            angle *= 180.0 / np.pi
 496        # this vtk method only rotates in the origin of the object:
 497        self.RotateWXYZ(angle, axis[0], axis[1], axis[2])
 498        self.pos(rv)
 499        return self
 500
 501    def _rotatexyz(self, a, angle, rad, around):
 502        if rad:
 503            angle *= 180 / np.pi
 504
 505        T = vtk.vtkTransform()
 506        T.SetMatrix(self.GetMatrix())
 507        T.PostMultiply()
 508
 509        rot = dict(x=T.RotateX, y=T.RotateY, z=T.RotateZ)
 510
 511        if around is None:
 512            # rotate around its origin
 513            rot[a](angle)
 514        else:
 515            if around == "itself":
 516                around = self.GetPosition()
 517            # displacement needed to bring it back to the origin
 518            # and disregard origin
 519            disp = around - np.array(self.GetOrigin())
 520            T.Translate(-disp)
 521            rot[a](angle)
 522            T.Translate(disp)
 523
 524        self.SetOrientation(T.GetOrientation())
 525        self.SetPosition(T.GetPosition())
 526
 527        self.point_locator = None
 528        self.cell_locator = None
 529        return self
 530
 531    def rotate_x(self, angle, rad=False, around=None):
 532        """
 533        Rotate around x-axis. If angle is in radians set `rad=True`.
 534
 535        Use `around` to define a pivoting point.
 536        """
 537        return self._rotatexyz("x", angle, rad, around)
 538
 539    def rotate_y(self, angle, rad=False, around=None):
 540        """
 541        Rotate around y-axis. If angle is in radians set `rad=True`.
 542
 543        Use `around` to define a pivoting point.
 544        """
 545        return self._rotatexyz("y", angle, rad, around)
 546
 547    def rotate_z(self, angle, rad=False, around=None):
 548        """
 549        Rotate around z-axis. If angle is in radians set `rad=True`.
 550
 551        Use `around` to define a pivoting point.
 552        """
 553        return self._rotatexyz("z", angle, rad, around)
 554
 555    def orientation(self, newaxis=None, rotation=0, concatenate=False, xyplane=False, rad=False):
 556        """
 557        Set/Get object orientation.
 558
 559        Arguments:
 560            rotation : (float)
 561                rotate object around newaxis.
 562            concatenate : (bool)
 563                concatenate the orientation operation with the previous existing transform (if any)
 564            xyplane : (bool)
 565                make an extra rotation to keep the object aligned to the xy-plane
 566            rad : (bool)
 567                set to True if angle is expressed in radians.
 568
 569        Example:
 570            ```python
 571            from vedo import *
 572            center = np.array([581/2,723/2,0])
 573            objs = []
 574            for a in np.linspace(0, 6.28, 7):
 575                v = vector(cos(a), sin(a), 0)*1000
 576                pic = Picture(dataurl+"images/dog.jpg").rotate_z(10)
 577                pic.orientation(v, xyplane=True)
 578                pic.origin(center)
 579                pic.pos(v - center)
 580                objs += [pic, Arrow(v, v+v)]
 581            show(objs, Point(), axes=1).close()
 582            ```
 583            ![](https://vedo.embl.es/images/feats/orientation.png)
 584
 585        Examples:
 586            - [gyroscope2.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/gyroscope2.py)
 587
 588            ![](https://vedo.embl.es/images/simulations/50738942-687b5780-11d9-11e9-97f0-72bbd63f7d6e.gif)
 589        """
 590        if self.top is None or self.base is None:
 591            initaxis = (0, 0, 1)
 592        else:
 593            initaxis = utils.versor(self.top - self.base)
 594
 595        newaxis = utils.versor(newaxis)
 596        p = np.array(self.GetPosition())
 597        crossvec = np.cross(initaxis, newaxis)
 598
 599        angleth = np.arccos(np.dot(initaxis, newaxis))
 600
 601        T = vtk.vtkTransform()
 602        if concatenate:
 603            try:
 604                M = self.GetMatrix()
 605                T.SetMatrix(M)
 606            except:
 607                pass
 608        T.PostMultiply()
 609        T.Translate(-p)
 610        if rotation:
 611            if rad:
 612                rotation *= 180.0 / np.pi
 613            T.RotateWXYZ(rotation, initaxis)
 614        if xyplane:
 615            angleph = np.arctan2(newaxis[1], newaxis[0])
 616            T.RotateWXYZ(np.rad2deg(angleph + angleth), initaxis)  # compensation
 617        T.RotateWXYZ(np.rad2deg(angleth), crossvec)
 618        T.Translate(p)
 619
 620        self.SetOrientation(T.GetOrientation())
 621
 622        self.point_locator = None
 623        self.cell_locator = None
 624        return self
 625
 626        # newaxis = utils.versor(newaxis)
 627        # pos = np.array(self.GetPosition())
 628        # crossvec = np.cross(initaxis, newaxis)
 629        # angle = np.arccos(np.dot(initaxis, newaxis))
 630        # T = vtk.vtkTransform()
 631        # T.PostMultiply()
 632        # T.Translate(-pos)
 633        # if rotation:
 634        #     T.RotateWXYZ(rotation, initaxis)
 635        # T.RotateWXYZ(np.rad2deg(angle), crossvec)
 636        # T.Translate(pos)
 637        # self.SetUserTransform(T)
 638        # self.transform = T
 639
 640    def scale(self, s=None, reset=False):
 641        """
 642        Set/get object's scaling factor.
 643
 644        Arguments:
 645            s : (list, float)
 646                scaling factor(s).
 647            reset : (bool)
 648                if True previous scaling factors are ignored.
 649
 650        Note:
 651            use `s=(sx,sy,sz)` to scale differently in the three coordinates.
 652        """
 653        if s is None:
 654            return np.array(self.GetScale())
 655
 656        # assert s[0] != 0
 657        # assert s[1] != 0
 658        # assert s[2] != 0
 659
 660        if reset:
 661            self.SetScale(s)
 662        else:
 663            self.SetScale(np.multiply(self.GetScale(), s))
 664
 665        self.point_locator = None
 666        self.cell_locator = None
 667        return self
 668
 669    def get_transform(self, invert=False):
 670        """
 671        Check if `object.transform` exists and returns a `vtkTransform`.
 672        Otherwise return current user transformation (where the object is currently placed).
 673
 674        Use `invert` to return the inverse of the current transformation
 675
 676        Example:
 677            ```python
 678            from vedo import *
 679
 680            c1 = Cube()
 681            c2 = c1.clone().c('violet').alpha(0.5) # copy of c1
 682            v = vector(0.2,1,0)
 683            p = vector(1,0,0)  # axis passes through this point
 684            c2.rotate(90, axis=v, point=p)
 685
 686            # get the inverse of the current transformation
 687            T = c2.get_transform(invert=True)
 688            c2.apply_transform(T)  # put back c2 in place
 689
 690            l = Line(p-v, p+v).lw(3).c('red')
 691            show(c1.wireframe().lw(3), l, c2, axes=1).close()
 692            ```
 693            ![](https://vedo.embl.es/images/feats/get_transf.png)
 694        """
 695        if self.transform:
 696            tr = self.transform
 697            if invert:
 698                tr = tr.GetInverse()
 699            return tr
 700
 701        T = self.GetMatrix()
 702        tr = vtk.vtkTransform()
 703        tr.SetMatrix(T)
 704        if invert:
 705            tr = tr.GetInverse()
 706        return tr
 707
 708    def apply_transform(self, T, reset=False, concatenate=False):
 709        """
 710        Transform object position and orientation.
 711
 712        Arguments:
 713            reset : (bool)
 714                no effect, this is superseded by `pointcloud.apply_transform()`
 715            concatenate : (bool)
 716                no effect, this is superseded by `pointcloud.apply_transform()`
 717        """
 718        if isinstance(T, vtk.vtkMatrix4x4):
 719            self.SetUserMatrix(T)
 720        elif utils.is_sequence(T):
 721            vm = vtk.vtkMatrix4x4()
 722            for i in [0, 1, 2, 3]:
 723                for j in [0, 1, 2, 3]:
 724                    vm.SetElement(i, j, T[i][j])
 725            self.SetUserMatrix(vm)
 726        else:
 727            self.SetUserTransform(T)
 728        self.transform = T
 729
 730        self.point_locator = None
 731        self.cell_locator = None
 732        return self
 733
 734    def align_to_bounding_box(self, msh, rigid=False):
 735        """
 736        Align the current object's bounding box to the bounding box
 737        of the input object.
 738
 739        Use `rigid` to disable scaling.
 740
 741        Examples:
 742            - [align6.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/align6.py)
 743        """
 744        lmt = vtk.vtkLandmarkTransform()
 745        ss = vtk.vtkPoints()
 746        xss0, xss1, yss0, yss1, zss0, zss1 = self.bounds()
 747        for p in [
 748            [xss0, yss0, zss0],
 749            [xss1, yss0, zss0],
 750            [xss1, yss1, zss0],
 751            [xss0, yss1, zss0],
 752            [xss0, yss0, zss1],
 753            [xss1, yss0, zss1],
 754            [xss1, yss1, zss1],
 755            [xss0, yss1, zss1],
 756        ]:
 757            ss.InsertNextPoint(p)
 758        st = vtk.vtkPoints()
 759        xst0, xst1, yst0, yst1, zst0, zst1 = msh.bounds()
 760        for p in [
 761            [xst0, yst0, zst0],
 762            [xst1, yst0, zst0],
 763            [xst1, yst1, zst0],
 764            [xst0, yst1, zst0],
 765            [xst0, yst0, zst1],
 766            [xst1, yst0, zst1],
 767            [xst1, yst1, zst1],
 768            [xst0, yst1, zst1],
 769        ]:
 770            st.InsertNextPoint(p)
 771
 772        lmt.SetSourceLandmarks(ss)
 773        lmt.SetTargetLandmarks(st)
 774        lmt.SetModeToAffine()
 775        if rigid:
 776            lmt.SetModeToRigidBody()
 777        lmt.Update()
 778        self.apply_transform(lmt)
 779        self.transform = lmt
 780
 781        self.point_locator = None
 782        self.cell_locator = None
 783        return self
 784
 785    def on(self):
 786        """Switch on  object visibility. Object is not removed."""
 787        self.VisibilityOn()
 788        try:
 789            self.scalarbar.VisibilityOn()
 790        except AttributeError:
 791            pass
 792        try:
 793            self.trail.VisibilityOn()
 794        except AttributeError:
 795            pass
 796        try:
 797            for sh in self.shadows:
 798                sh.VisibilityOn()
 799        except AttributeError:
 800            pass
 801        return self
 802
 803    def off(self):
 804        """Switch off object visibility. Object is not removed."""
 805        self.VisibilityOff()
 806        try:
 807            self.scalarbar.VisibilityOff()
 808        except AttributeError:
 809            pass
 810        try:
 811            self.trail.VisibilityOff()
 812        except AttributeError:
 813            pass
 814        try:
 815            for sh in self.shadows:
 816                sh.VisibilityOff()
 817        except AttributeError:
 818            pass
 819        return self
 820
 821    def toggle(self):
 822        """Toggle object visibility on/off."""
 823        v = self.GetVisibility()
 824        if v:
 825            self.off()
 826        else:
 827            self.on()
 828        return self
 829
 830    def box(self, scale=1, padding=0, fill=False):
 831        """
 832        Return the bounding box as a new `Mesh`.
 833
 834        Arguments:
 835            scale : (float)
 836                box size can be scaled by a factor
 837            padding : (float, list)
 838                a constant padding can be added (can be a list [padx,pady,padz])
 839
 840        Examples:
 841            - [latex.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/latex.py)
 842        """
 843        b = self.bounds()
 844        if not utils.is_sequence(padding):
 845            padding = [padding, padding, padding]
 846        length, width, height = b[1] - b[0], b[3] - b[2], b[5] - b[4]
 847        tol = (length + width + height) / 30000  # useful for boxing 2D text
 848        pos = [(b[0] + b[1]) / 2, (b[3] + b[2]) / 2, (b[5] + b[4]) / 2 - tol]
 849        bx = vedo.shapes.Box(
 850            pos,
 851            length * scale + padding[0],
 852            width * scale + padding[1],
 853            height * scale + padding[2],
 854            c="gray",
 855        )
 856        if hasattr(self, "GetProperty"):  # could be Assembly
 857            if isinstance(self.GetProperty(), vtk.vtkProperty):  # could be volume
 858                pr = vtk.vtkProperty()
 859                pr.DeepCopy(self.GetProperty())
 860                bx.SetProperty(pr)
 861                bx.property = pr
 862        bx.wireframe(not fill)
 863        bx.flat().lighting("off")
 864        return bx
 865
 866    def use_bounds(self, ub=True):
 867        """
 868        Instruct the current camera to either take into account or ignore
 869        the object bounds when resetting.
 870        """
 871        self.SetUseBounds(ub)
 872        return self
 873
 874    def bounds(self):
 875        """
 876        Get the object bounds.
 877        Returns a list in format `[xmin,xmax, ymin,ymax, zmin,zmax]`.
 878        """
 879        try:
 880            pts = self.points()
 881            xmin, ymin, zmin = np.min(pts, axis=0)
 882            xmax, ymax, zmax = np.max(pts, axis=0)
 883            return [xmin, xmax, ymin, ymax, zmin, zmax]
 884        except (AttributeError, ValueError):
 885            return self.GetBounds()
 886
 887    def xbounds(self, i=None):
 888        """Get the bounds `[xmin,xmax]`. Can specify upper or lower with i (0,1)."""
 889        b = self.bounds()
 890        if i is not None:
 891            return b[i]
 892        return (b[0], b[1])
 893
 894    def ybounds(self, i=None):
 895        """Get the bounds `[ymin,ymax]`. Can specify upper or lower with i (0,1)."""
 896        b = self.bounds()
 897        if i == 0:
 898            return b[2]
 899        if i == 1:
 900            return b[3]
 901        return (b[2], b[3])
 902
 903    def zbounds(self, i=None):
 904        """Get the bounds `[zmin,zmax]`. Can specify upper or lower with i (0,1)."""
 905        b = self.bounds()
 906        if i == 0:
 907            return b[4]
 908        if i == 1:
 909            return b[5]
 910        return (b[4], b[5])
 911
 912
 913    def diagonal_size(self):
 914        """Get the length of the diagonal of mesh bounding box."""
 915        b = self.bounds()
 916        return np.sqrt((b[1] - b[0]) ** 2 + (b[3] - b[2]) ** 2 + (b[5] - b[4]) ** 2)
 917        # return self.GetLength() # ???different???
 918
 919
 920    def copy_data_from(self, obj):
 921        """Copy all data (point and cell data) from this input object"""
 922        self.inputdata().GetPointData().PassData(obj.inputdata().GetPointData())
 923        self.inputdata().GetCellData().PassData(obj.inputdata().GetCellData())
 924        self.pipeline = utils.OperationNode(
 925            f"copy_data_from\n{obj.__class__.__name__}",
 926            parents=[self, obj],
 927            shape="note",
 928            c="#ccc5b9",
 929        )
 930        return self
 931
 932    def print(self):
 933        """Print information about an object."""
 934        utils.print_info(self)
 935        return self
 936
 937    def show(self, **options):
 938        """
 939        Create on the fly an instance of class `Plotter` or use the last existing one to
 940        show one single object.
 941
 942        This method is meant as a shortcut. If more than one object needs to be visualised
 943        please use the syntax `show(mesh1, mesh2, volume, ..., options)`.
 944
 945        Returns the `Plotter` class instance.
 946        """
 947        return vedo.plotter.show(self, **options)
 948
 949    def thumbnail(self, zoom=1.25, size=(200, 200), bg="white", azimuth=0, elevation=0, axes=False):
 950        """Build a thumbnail of the object and return it as an array."""
 951        # speed is about 20Hz for size=[200,200]
 952        ren = vtk.vtkRenderer()
 953        ren.AddActor(self)
 954        if axes:
 955            axes = vedo.addons.Axes(self)
 956            ren.AddActor(axes)
 957        ren.ResetCamera()
 958        cam = ren.GetActiveCamera()
 959        cam.Zoom(zoom)
 960        cam.Elevation(elevation)
 961        cam.Azimuth(azimuth)
 962
 963        ren_win = vtk.vtkRenderWindow()
 964        ren_win.SetOffScreenRendering(True)
 965        ren_win.SetSize(size)
 966        ren.SetBackground(colors.get_color(bg))
 967        ren_win.AddRenderer(ren)
 968        ren_win.Render()
 969
 970        nx, ny = ren_win.GetSize()
 971        arr = vtk.vtkUnsignedCharArray()
 972        ren_win.GetRGBACharPixelData(0, 0, nx - 1, ny - 1, 0, arr)
 973        narr = utils.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3])
 974        narr = np.ascontiguousarray(np.flip(narr, axis=0))
 975
 976        ren.RemoveActor(self)
 977        if axes:
 978            ren.RemoveActor(axes)
 979        ren_win.Finalize()
 980        del ren_win
 981        return narr
 982
 983
 984########################################################################################
 985class BaseActor2D(vtk.vtkActor2D):
 986    """
 987    Base class.
 988
 989    .. warning:: Do not use this class to instantiate objects.
 990    """
 991
 992    def __init__(self):
 993        """Manage 2D objects."""
 994        super().__init__()
 995        self._mapper = None
 996        self.property = self.GetProperty()
 997        self.filename = ""
 998
 999    def layer(self, value=None):
1000        """Set/Get the layer number in the overlay planes into which to render."""
1001        if value is None:
1002            return self.GetLayerNumber()
1003        self.SetLayerNumber(value)
1004        return self
1005
1006    def pos(self, px=None, py=None):
1007        """Set/Get the screen-coordinate position."""
1008        if isinstance(px, str):
1009            vedo.logger.error("Use string descriptors only inside the constructor")
1010            return self
1011        if px is None:
1012            return np.array(self.GetPosition(), dtype=int)
1013        if py is not None:
1014            p = [px, py]
1015        else:
1016            p = px
1017        assert len(p) == 2, "Error: len(pos) must be 2 for BaseActor2D"
1018        self.SetPosition(p)
1019        return self
1020
1021    def coordinate_system(self, value=None):
1022        """
1023        Set/get the coordinate system which this coordinate is defined in.
1024
1025        The options are:
1026            0. Display
1027            1. Normalized Display
1028            2. Viewport
1029            3. Normalized Viewport
1030            4. View
1031            5. Pose
1032            6. World
1033        """
1034        coor = self.GetPositionCoordinate()
1035        if value is None:
1036            return coor.GetCoordinateSystem()
1037        coor.SetCoordinateSystem(value)
1038        return self
1039
1040    def on(self):
1041        """Set object visibility."""
1042        self.VisibilityOn()
1043        return self
1044
1045    def off(self):
1046        """Set object visibility."""
1047        self.VisibilityOn()
1048        return self
1049
1050    def toggle(self):
1051        """Toggle object visibility."""
1052        self.SetVisibility(not self.GetVisibility())
1053        return self
1054
1055    def pickable(self, value=True):
1056        self.SetPickable(value)
1057        return self
1058
1059    def alpha(self, value=None):
1060        """Set/Get the object opacity."""
1061        if value is None:
1062            return self.GetProperty().GetOpacity()
1063        self.GetProperty().SetOpacity(value)
1064        return self
1065
1066    def ps(self, point_size=None):
1067        if point_size is None:
1068            return self.GetProperty().GetPointSize()
1069        self.GetProperty().SetPointSize(point_size)
1070        return self
1071
1072    def ontop(self, value=True):
1073        """Keep the object always on top of everything else."""
1074        if value:
1075            self.GetProperty().SetDisplayLocationToForeground()
1076        else:
1077            self.GetProperty().SetDisplayLocationToBackground()
1078        return self
1079
1080
1081########################################################################################
1082class BaseActor(Base3DProp):
1083    """
1084    Base class.
1085
1086    .. warning:: Do not use this class to instantiate objects, use one the above instead.
1087    """
1088
1089    def __init__(self):
1090        """
1091        Base class to add operative and data
1092        functionality to `Mesh`, `Assembly`, `Volume` and `Picture` objects.
1093        """
1094
1095        super().__init__()
1096
1097        self._mapper = None
1098        self._caption = None
1099        self.property = None
1100
1101    def mapper(self, new_mapper=None):
1102        """Return the `vtkMapper` data object, or update it with a new one."""
1103        if new_mapper:
1104            self.SetMapper(new_mapper)
1105            if self._mapper:
1106                iptdata = self._mapper.GetInput()
1107                if iptdata:
1108                    new_mapper.SetInputData(self._mapper.GetInput())
1109            self._mapper = new_mapper
1110            self._mapper.Modified()
1111        return self._mapper
1112
1113    def inputdata(self):
1114        """Return the VTK input data object."""
1115        if self._mapper:
1116            return self._mapper.GetInput()
1117        return self.GetMapper().GetInput()
1118
1119    def modified(self):
1120        """Use in conjunction with `tonumpy()`
1121        to update any modifications to the volume array"""
1122        sc = self.inputdata().GetPointData().GetScalars()
1123        if sc:
1124            sc.Modified()
1125        self.inputdata().GetPointData().Modified()
1126        return self
1127
1128    @property
1129    def npoints(self):
1130        """Retrieve the number of points."""
1131        return self.inputdata().GetNumberOfPoints()
1132
1133    @property
1134    def ncells(self):
1135        """Retrieve the number of cells."""
1136        return self.inputdata().GetNumberOfCells()
1137
1138    def points(self, pts=None, transformed=True):
1139        """
1140        Set/Get the vertex coordinates of a mesh or point cloud.
1141        Argument can be an index, a set of indices
1142        or a complete new set of points to update the mesh.
1143
1144        Set `transformed=False` to ignore any previous transformation applied to the mesh.
1145        """
1146        if pts is None:  ### getter
1147
1148            if isinstance(self, vedo.Points):
1149                vpts = self.polydata(transformed).GetPoints()
1150            elif isinstance(self, vedo.BaseVolume):
1151                v2p = vtk.vtkImageToPoints()
1152                v2p.SetInputData(self.imagedata())
1153                v2p.Update()
1154                vpts = v2p.GetOutput().GetPoints()
1155            else:  # tetmesh et al
1156                vpts = self.inputdata().GetPoints()
1157
1158            if vpts:
1159                return utils.vtk2numpy(vpts.GetData())
1160            return np.array([], dtype=np.float32)
1161
1162        else:  ### setter
1163
1164            if len(pts) == 3 and len(pts[0]) != 3:
1165                # assume plist is in the format [all_x, all_y, all_z]
1166                pts = np.stack((pts[0], pts[1], pts[2]), axis=1)
1167            pts = np.asarray(pts, dtype=np.float32)
1168            if pts.shape[1] == 2:
1169                pts = np.c_[pts, np.zeros(pts.shape[0], dtype=np.float32)]
1170            vpts = self.inputdata().GetPoints()
1171            arr = utils.numpy2vtk(pts, dtype=np.float32)
1172            vpts.SetData(arr)
1173            vpts.Modified()
1174            # reset mesh to identity matrix position/rotation:
1175            self.PokeMatrix(vtk.vtkMatrix4x4())
1176            self.point_locator = None
1177            self.cell_locator = None
1178            return self
1179
1180
1181    def cell_centers(self):
1182        """
1183        Get the coordinates of the cell centers.
1184
1185        Examples:
1186            - [delaunay2d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/delaunay2d.py)
1187        """
1188        vcen = vtk.vtkCellCenters()
1189        if hasattr(self, "polydata"):
1190            vcen.SetInputData(self.polydata())
1191        else:
1192            vcen.SetInputData(self.inputdata())
1193        vcen.Update()
1194        return utils.vtk2numpy(vcen.GetOutput().GetPoints().GetData())
1195
1196    def delete_cells(self, ids):
1197        """
1198        Remove cells from the mesh object by their ID.
1199        Points (vertices) are not removed (you may use `.clean()` to remove those).
1200        """
1201        data = self.inputdata()
1202        data.BuildLinks()
1203        for cid in ids:
1204            data.DeleteCell(cid)
1205        data.RemoveDeletedCells()
1206        data.Modified()
1207        self._mapper.Modified()
1208        self.pipeline = utils.OperationNode(
1209            "delete_cells", parents=[self], comment=f"#cells {self._data.GetNumberOfCells()}"
1210        )
1211        return self
1212
1213    def mark_boundaries(self):
1214        """
1215        Mark cells and vertices of the mesh if they lie on a boundary.
1216        A new array called `BoundaryCells` is added to the mesh.
1217        """
1218        mb = vtk.vtkMarkBoundaryFilter()
1219        mb.SetInputData(self._data)
1220        mb.Update()
1221        out = self._update(mb.GetOutput())
1222        out.pipeline = utils.OperationNode("mark_boundaries", parents=[self])
1223        return out
1224
1225    def find_cells_in(self, xbounds=(), ybounds=(), zbounds=()):
1226        """
1227        Find cells that are within the specified bounds.
1228        Setting a color will add a vtk array to colorize these cells.
1229        """
1230        if len(xbounds) == 6:
1231            bnds = xbounds
1232        else:
1233            bnds = list(self.bounds())
1234            if len(xbounds) == 2:
1235                bnds[0] = xbounds[0]
1236                bnds[1] = xbounds[1]
1237            if len(ybounds) == 2:
1238                bnds[2] = ybounds[0]
1239                bnds[3] = ybounds[1]
1240            if len(zbounds) == 2:
1241                bnds[4] = zbounds[0]
1242                bnds[5] = zbounds[1]
1243
1244        cellIds = vtk.vtkIdList()
1245        self.cell_locator = vtk.vtkCellTreeLocator()
1246        self.cell_locator.SetDataSet(self.polydata())
1247        self.cell_locator.BuildLocator()
1248        self.cell_locator.FindCellsWithinBounds(bnds, cellIds)
1249
1250        cids = []
1251        for i in range(cellIds.GetNumberOfIds()):
1252            cid = cellIds.GetId(i)
1253            cids.append(cid)
1254
1255        return np.array(cids)
1256
1257    def count_vertices(self):
1258        """Count the number of vertices each cell has and return it as a numpy array"""
1259        vc = vtk.vtkCountVertices()
1260        vc.SetInputData(self._data)
1261        vc.SetOutputArrayName("VertexCount")
1262        vc.Update()
1263        varr = vc.GetOutput().GetCellData().GetArray("VertexCount")
1264        return utils.vtk2numpy(varr)
1265
1266    def lighting(
1267        self,
1268        style="",
1269        ambient=None,
1270        diffuse=None,
1271        specular=None,
1272        specular_power=None,
1273        specular_color=None,
1274        metallicity=None,
1275        roughness=None,
1276    ):
1277        """
1278        Set the ambient, diffuse, specular and specular_power lighting constants.
1279
1280        Arguments:
1281            style : (str)
1282                preset style, options are `[metallic, plastic, shiny, glossy, ambient, off]`
1283            ambient : (float)
1284                ambient fraction of emission [0-1]
1285            diffuse : (float)
1286                emission of diffused light in fraction [0-1]
1287            specular : (float)
1288                fraction of reflected light [0-1]
1289            specular_power : (float)
1290                precision of reflection [1-100]
1291            specular_color : (color)
1292                color that is being reflected by the surface
1293
1294        <img src="https://upload.wikimedia.org/wikipedia/commons/6/6b/Phong_components_version_4.png" alt="", width=700px>
1295
1296        Examples:
1297            - [specular.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/specular.py)
1298        """
1299        pr = self.GetProperty()
1300
1301        if style:
1302
1303            if isinstance(pr, vtk.vtkVolumeProperty):
1304                self.shade(True)
1305                if style == "off":
1306                    self.shade(False)
1307                elif style == "ambient":
1308                    style = "default"
1309                    self.shade(False)
1310            else:
1311                if style != "off":
1312                    pr.LightingOn()
1313
1314            if style == "off":
1315                pr.SetInterpolationToFlat()
1316                pr.LightingOff()
1317                return self  ##############
1318
1319            if hasattr(pr, "GetColor"):  # could be Volume
1320                c = pr.GetColor()
1321            else:
1322                c = (1, 1, 0.99)
1323            mpr = self._mapper
1324            if hasattr(mpr, 'GetScalarVisibility') and mpr.GetScalarVisibility():
1325                c = (1,1,0.99)
1326            if   style=='metallic': pars = [0.1, 0.3, 1.0, 10, c]
1327            elif style=='plastic' : pars = [0.3, 0.4, 0.3,  5, c]
1328            elif style=='shiny'   : pars = [0.2, 0.6, 0.8, 50, c]
1329            elif style=='glossy'  : pars = [0.1, 0.7, 0.9, 90, (1,1,0.99)]
1330            elif style=='ambient' : pars = [0.8, 0.1, 0.0,  1, (1,1,1)]
1331            elif style=='default' : pars = [0.1, 1.0, 0.05, 5, c]
1332            else:
1333                vedo.logger.error("in lighting(): Available styles are")
1334                vedo.logger.error("[default, metallic, plastic, shiny, glossy, ambient, off]")
1335                raise RuntimeError()
1336            pr.SetAmbient(pars[0])
1337            pr.SetDiffuse(pars[1])
1338            pr.SetSpecular(pars[2])
1339            pr.SetSpecularPower(pars[3])
1340            if hasattr(pr, "GetColor"):
1341                pr.SetSpecularColor(pars[4])
1342
1343        if ambient is not None: pr.SetAmbient(ambient)
1344        if diffuse is not None: pr.SetDiffuse(diffuse)
1345        if specular is not None: pr.SetSpecular(specular)
1346        if specular_power is not None: pr.SetSpecularPower(specular_power)
1347        if specular_color is not None: pr.SetSpecularColor(colors.get_color(specular_color))
1348        if utils.vtk_version_at_least(9):
1349            if metallicity is not None:
1350                pr.SetInterpolationToPBR()
1351                pr.SetMetallic(metallicity)
1352            if roughness is not None:
1353                pr.SetInterpolationToPBR()
1354                pr.SetRoughness(roughness)
1355
1356        return self
1357
1358    def print_histogram(
1359        self,
1360        bins=10,
1361        height=10,
1362        logscale=False,
1363        minbin=0,
1364        horizontal=False,
1365        char="\U00002589",
1366        c=None,
1367        bold=True,
1368        title="Histogram",
1369    ):
1370        """
1371        Ascii histogram printing on terminal.
1372        Input can be `Volume` or `Mesh` (will grab the active point array).
1373
1374        Arguments:
1375            bins : (int)
1376                number of histogram bins
1377            height : (int)
1378                height of the histogram in character units
1379            logscale : (bool)
1380                use logscale for frequencies
1381            minbin : (int)
1382                ignore bins before minbin
1383            horizontal : (bool)
1384                show histogram horizontally
1385            char : (str)
1386                character to be used as marker
1387            c : (color)
1388                ascii color
1389            bold : (bool)
1390                use boldface
1391            title : (str)
1392                histogram title
1393
1394        ![](https://vedo.embl.es/images/feats/histoprint.png)
1395        """
1396        utils.print_histogram(
1397            self, bins, height, logscale, minbin, horizontal, char, c, bold, title
1398        )
1399        return self
1400
1401    def c(self, color=False, alpha=None):
1402        """
1403        Shortcut for `color()`.
1404        If None is passed as input, will use colors from current active scalars.
1405        """
1406        return self.color(color, alpha)
1407
1408    @property
1409    def pointdata(self):
1410        """
1411        Create and/or return a `numpy.array` associated to points (vertices).
1412        A data array can be indexed either as a string or by an integer number.
1413        E.g.:  `myobj.pointdata["arrayname"]`
1414
1415        Usage:
1416
1417            `myobj.pointdata.keys()` to return the available data array names
1418
1419            `myobj.pointdata.select(name)` to make this array the active one
1420
1421            `myobj.pointdata.remove(name)` to remove this array
1422        """
1423        return _DataArrayHelper(self, 0)
1424
1425    @property
1426    def celldata(self):
1427        """
1428        Create and/or return a `numpy.array` associated to cells (faces).
1429        A data array can be indexed either as a string or by an integer number.
1430        E.g.:  `myobj.celldata["arrayname"]`
1431
1432        Usage:
1433
1434            `myobj.celldata.keys()` to return the available data array names
1435
1436            `myobj.celldata.select(name)` to make this array the active one
1437
1438            `myobj.celldata.remove(name)` to remove this array
1439        """
1440        return _DataArrayHelper(self, 1)
1441
1442    @property
1443    def metadata(self):
1444        """
1445        Create and/or return a `numpy.array` associated to neither cells nor faces.
1446        A data array can be indexed either as a string or by an integer number.
1447        E.g.:  `myobj.metadata["arrayname"]`
1448
1449        Usage:
1450
1451            `myobj.metadata.keys()` to return the available data array names
1452
1453            `myobj.metadata.select(name)` to make this array the active one
1454
1455            `myobj.metadata.remove(name)` to remove this array
1456        """
1457        return _DataArrayHelper(self, 2)
1458
1459    def map_cells_to_points(self, arrays=(), move=False):
1460        """
1461        Interpolate cell data (i.e., data specified per cell or face)
1462        into point data (i.e., data specified at each vertex).
1463        The method of transformation is based on averaging the data values
1464        of all cells using a particular point.
1465
1466        A custom list of arrays to be mapped can be passed in input.
1467
1468        Set `move=True` to delete the original `celldata` array.
1469        """
1470        c2p = vtk.vtkCellDataToPointData()
1471        c2p.SetInputData(self.inputdata())
1472        if not move:
1473            c2p.PassCellDataOn()
1474        if arrays:
1475            c2p.ClearCellDataArrays()
1476            c2p.ProcessAllArraysOff()
1477            for arr in arrays:
1478                c2p.AddCellDataArray(arr)
1479        else:
1480            c2p.ProcessAllArraysOn()
1481        c2p.Update()
1482        self._mapper.SetScalarModeToUsePointData()
1483        out = self._update(c2p.GetOutput())
1484        out.pipeline = utils.OperationNode("map cell\nto point data", parents=[self])
1485        return out
1486
1487    def map_points_to_cells(self, arrays=(), move=False):
1488        """
1489        Interpolate point data (i.e., data specified per point or vertex)
1490        into cell data (i.e., data specified per cell).
1491        The method of transformation is based on averaging the data values
1492        of all points defining a particular cell.
1493
1494        A custom list of arrays to be mapped can be passed in input.
1495
1496        Set `move=True` to delete the original `pointdata` array.
1497
1498        Examples:
1499            - [mesh_map2cell.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_map2cell.py)
1500        """
1501        p2c = vtk.vtkPointDataToCellData()
1502        p2c.SetInputData(self.inputdata())
1503        if not move:
1504            p2c.PassPointDataOn()
1505        if arrays:
1506            p2c.ClearPointDataArrays()
1507            p2c.ProcessAllArraysOff()
1508            for arr in arrays:
1509                p2c.AddPointDataArray(arr)
1510        else:
1511            p2c.ProcessAllArraysOn()
1512        p2c.Update()
1513        self._mapper.SetScalarModeToUseCellData()
1514        out = self._update(p2c.GetOutput())
1515        out.pipeline = utils.OperationNode("map point\nto cell data", parents=[self])
1516        return out
1517
1518    def resample_data_from(self, source, tol=None, categorical=False):
1519        """
1520        Resample point and cell data from another dataset.
1521        The output has the same structure but its point data have
1522        the resampled values from target.
1523
1524        Use `tol` to set the tolerance used to compute whether
1525        a point in the source is in a cell of the current object.
1526        Points without resampled values, and their cells, are marked as blank.
1527        If the data is categorical, then the resulting data will be determined
1528        by a nearest neighbor interpolation scheme.
1529
1530        Example:
1531        ```python
1532        from vedo import *
1533        m1 = Mesh(dataurl+'bunny.obj')#.add_gaussian_noise(0.1)
1534        pts = m1.points()
1535        ces = m1.cell_centers()
1536        m1.pointdata["xvalues"] = np.power(pts[:,0], 3)
1537        m1.celldata["yvalues"]  = np.power(ces[:,1], 3)
1538        m2 = Mesh(dataurl+'bunny.obj')
1539        m2.resample_arrays_from(m1)
1540        # print(m2.pointdata["xvalues"])
1541        show(m1, m2 , N=2, axes=1)
1542        ```
1543        """
1544        rs = vtk.vtkResampleWithDataSet()
1545        rs.SetInputData(self.inputdata())
1546        rs.SetSourceData(source.inputdata())
1547
1548        rs.SetPassPointArrays(True)
1549        rs.SetPassCellArrays(True)
1550        rs.SetPassFieldArrays(True)
1551        rs.SetCategoricalData(categorical)
1552
1553        rs.SetComputeTolerance(True)
1554        if tol:
1555            rs.SetComputeTolerance(False)
1556            rs.SetTolerance(tol)
1557        rs.Update()
1558        self._update(rs.GetOutput())
1559        self.pipeline = utils.OperationNode(
1560            f"resample_data_from\n{source.__class__.__name__}", parents=[self, source]
1561        )
1562        return self
1563
1564    def add_ids(self):
1565        """Generate point and cell ids arrays."""
1566        ids = vtk.vtkIdFilter()
1567        ids.SetInputData(self.inputdata())
1568        ids.PointIdsOn()
1569        ids.CellIdsOn()
1570        ids.FieldDataOff()
1571        ids.SetPointIdsArrayName("PointID")
1572        ids.SetCellIdsArrayName("CellID")
1573        ids.Update()
1574        self._update(ids.GetOutput())
1575        self.pipeline = utils.OperationNode("add_ids", parents=[self])
1576        return self
1577
1578    def gradient(self, input_array=None, on="points", fast=False):
1579        """
1580        Compute and return the gradiend of the active scalar field as a numpy array.
1581
1582        Arguments:
1583            input_array : (str)
1584                array of the scalars to compute the gradient,
1585                if None the current active array is selected
1586            on : (str)
1587                compute either on 'points' or 'cells' data
1588            fast : (bool)
1589                if True, will use a less accurate algorithm
1590                that performs fewer derivative calculations (and is therefore faster).
1591
1592        Examples:
1593            - [isolines.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/isolines.py)
1594
1595            ![](https://user-images.githubusercontent.com/32848391/72433087-f00a8780-3798-11ea-9778-991f0abeca70.png)
1596        """
1597        gra = vtk.vtkGradientFilter()
1598        if on.startswith("p"):
1599            varr = self.inputdata().GetPointData()
1600            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS
1601        else:
1602            varr = self.inputdata().GetCellData()
1603            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS
1604
1605        if input_array is None:
1606            if varr.GetScalars():
1607                input_array = varr.GetScalars().GetName()
1608            else:
1609                vedo.logger.error(f"in gradient: no scalars found for {on}")
1610                raise RuntimeError
1611
1612        gra.SetInputData(self.inputdata())
1613        gra.SetInputScalars(tp, input_array)
1614        gra.SetResultArrayName("Gradient")
1615        gra.SetFasterApproximation(fast)
1616        gra.ComputeDivergenceOff()
1617        gra.ComputeVorticityOff()
1618        gra.ComputeGradientOn()
1619        gra.Update()
1620        if on.startswith("p"):
1621            gvecs = utils.vtk2numpy(gra.GetOutput().GetPointData().GetArray("Gradient"))
1622        else:
1623            gvecs = utils.vtk2numpy(gra.GetOutput().GetCellData().GetArray("Gradient"))
1624        return gvecs
1625
1626    def divergence(self, array_name=None, on="points", fast=False):
1627        """
1628        Compute and return the divergence of a vector field as a numpy array.
1629
1630        Arguments:
1631            array_name : (str)
1632                name of the array of vectors to compute the divergence,
1633                if None the current active array is selected
1634            on : (str)
1635                compute either on 'points' or 'cells' data
1636            fast : (bool)
1637                if True, will use a less accurate algorithm
1638                that performs fewer derivative calculations (and is therefore faster).
1639        """
1640        div = vtk.vtkGradientFilter()
1641        if on.startswith("p"):
1642            varr = self.inputdata().GetPointData()
1643            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS
1644        else:
1645            varr = self.inputdata().GetCellData()
1646            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS
1647
1648        if array_name is None:
1649            if varr.GetVectors():
1650                array_name = varr.GetVectors().GetName()
1651            else:
1652                vedo.logger.error(f"in divergence(): no vectors found for {on}")
1653                raise RuntimeError
1654
1655        div.SetInputData(self.inputdata())
1656        div.SetInputScalars(tp, array_name)
1657        div.ComputeDivergenceOn()
1658        div.ComputeGradientOff()
1659        div.ComputeVorticityOff()
1660        div.SetDivergenceArrayName("Divergence")
1661        div.SetFasterApproximation(fast)
1662        div.Update()
1663        if on.startswith("p"):
1664            dvecs = utils.vtk2numpy(div.GetOutput().GetPointData().GetArray("Divergence"))
1665        else:
1666            dvecs = utils.vtk2numpy(div.GetOutput().GetCellData().GetArray("Divergence"))
1667        return dvecs
1668
1669    def vorticity(self, array_name=None, on="points", fast=False):
1670        """
1671        Compute and return the vorticity of a vector field as a numpy array.
1672
1673        Arguments:
1674            array_name : (str)
1675                name of the array to compute the vorticity,
1676                if None the current active array is selected
1677            on : (str)
1678                compute either on 'points' or 'cells' data
1679            fast : (bool)
1680                if True, will use a less accurate algorithm
1681                that performs fewer derivative calculations (and is therefore faster).
1682        """
1683        vort = vtk.vtkGradientFilter()
1684        if on.startswith("p"):
1685            varr = self.inputdata().GetPointData()
1686            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS
1687        else:
1688            varr = self.inputdata().GetCellData()
1689            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS
1690
1691        if array_name is None:
1692            if varr.GetVectors():
1693                array_name = varr.GetVectors().GetName()
1694            else:
1695                vedo.logger.error(f"in vorticity(): no vectors found for {on}")
1696                raise RuntimeError
1697
1698        vort.SetInputData(self.inputdata())
1699        vort.SetInputScalars(tp, array_name)
1700        vort.ComputeDivergenceOff()
1701        vort.ComputeGradientOff()
1702        vort.ComputeVorticityOn()
1703        vort.SetVorticityArrayName("Vorticity")
1704        vort.SetFasterApproximation(fast)
1705        vort.Update()
1706        if on.startswith("p"):
1707            vvecs = utils.vtk2numpy(vort.GetOutput().GetPointData().GetArray("Vorticity"))
1708        else:
1709            vvecs = utils.vtk2numpy(vort.GetOutput().GetCellData().GetArray("Vorticity"))
1710        return vvecs
1711
1712    def add_scalarbar(
1713        self,
1714        title="",
1715        pos=(0.8, 0.05),
1716        title_yoffset=15,
1717        font_size=12,
1718        size=(None, None),
1719        nlabels=None,
1720        c=None,
1721        horizontal=False,
1722        use_alpha=True,
1723        label_format=":6.3g",
1724    ):
1725        """
1726        Add a 2D scalar bar for the specified obj.
1727
1728        Examples:
1729            - [mesh_coloring.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_coloring.py)
1730            - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py)
1731        """
1732        plt = vedo.plotter_instance
1733
1734        if plt and plt.renderer:
1735            c = (0.9, 0.9, 0.9)
1736            if np.sum(plt.renderer.GetBackground()) > 1.5:
1737                c = (0.1, 0.1, 0.1)
1738            if isinstance(self.scalarbar, vtk.vtkActor):
1739                plt.renderer.RemoveActor(self.scalarbar)
1740            elif isinstance(self.scalarbar, vedo.Assembly):
1741                for a in self.scalarbar.unpack():
1742                    plt.renderer.RemoveActor(a)
1743        if c is None:
1744            c = "gray"
1745
1746        sb = vedo.addons.ScalarBar(
1747            self,
1748            title,
1749            pos,
1750            title_yoffset,
1751            font_size,
1752            size,
1753            nlabels,
1754            c,
1755            horizontal,
1756            use_alpha,
1757            label_format,
1758        )
1759        self.scalarbar = sb
1760        return self
1761
1762    def add_scalarbar3d(
1763        self,
1764        title="",
1765        pos=None,
1766        size=(None, None),
1767        title_font="",
1768        title_xoffset=-1.5,
1769        title_yoffset=0.0,
1770        title_size=1.5,
1771        title_rotation=0.0,
1772        nlabels=9,
1773        label_font="",
1774        label_size=1,
1775        label_offset=0.375,
1776        label_rotation=0,
1777        label_format="",
1778        italic=0,
1779        c=None,
1780        draw_box=True,
1781        above_text=None,
1782        below_text=None,
1783        nan_text="NaN",
1784        categories=None,
1785    ):
1786        """
1787        Associate a 3D scalar bar to the object and add it to the scene.
1788        The new scalarbar object (Assembly) will be accessible as obj.scalarbar
1789
1790        Arguments:
1791            size : (list)
1792                (thickness, length) of scalarbar
1793            title : (str)
1794                scalar bar title
1795            title_xoffset : (float)
1796                horizontal space btw title and color scalarbar
1797            title_yoffset : (float)
1798                vertical space offset
1799            title_size : (float)
1800                size of title wrt numeric labels
1801            title_rotation : (float)
1802                title rotation in degrees
1803            nlabels : (int)
1804                number of numeric labels
1805            label_font : (str)
1806                font type for labels
1807            label_size : (float)
1808                label scale factor
1809            label_offset : (float)
1810                space btw numeric labels and scale
1811            label_rotation : (float)
1812                label rotation in degrees
1813            label_format : (str)
1814                label format for floats and integers (e.g. `':.2f'`)
1815            draw_box : (bool)
1816                draw a box around the colorbar
1817            categories : (list)
1818                make a categorical scalarbar,
1819                the input list will have the format `[value, color, alpha, textlabel]`
1820
1821        Examples:
1822            - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py)
1823        """
1824        plt = vedo.plotter_instance
1825        if plt and c is None:  # automatic black or white
1826            c = (0.9, 0.9, 0.9)
1827            if np.sum(vedo.get_color(plt.backgrcol)) > 1.5:
1828                c = (0.1, 0.1, 0.1)
1829        if c is None:
1830            c = (0, 0, 0)
1831        c = vedo.get_color(c)
1832
1833        self.scalarbar = vedo.addons.ScalarBar3D(
1834            self,
1835            title,
1836            pos,
1837            size,
1838            title_font,
1839            title_xoffset,
1840            title_yoffset,
1841            title_size,
1842            title_rotation,
1843            nlabels,
1844            label_font,
1845            label_size,
1846            label_offset,
1847            label_rotation,
1848            label_format,
1849            italic,
1850            c,
1851            draw_box,
1852            above_text,
1853            below_text,
1854            nan_text,
1855            categories,
1856        )
1857        return self
1858
1859    ###################################################################################
1860    def write(self, filename, binary=True):
1861        """Write object to file."""
1862        out = vedo.file_io.write(self, filename, binary)
1863        out.pipeline = utils.OperationNode(
1864            "write", parents=[self], comment=filename[:15], shape="folder", c="#8a817c"
1865        )
1866        return out
1867
1868
1869########################################################################################
1870class BaseGrid(BaseActor):
1871    """
1872    Base class for grid datasets.
1873
1874    .. warning:: Do not use this class to instantiate objects.
1875    """
1876
1877    def __init__(self):
1878        """Base class for grid datasets."""
1879
1880        super().__init__()
1881
1882        self._data = None
1883        self.useCells = True
1884        self._color = None
1885        self._alpha = [0, 1]
1886
1887        # -----------------------------------------------------------
1888
1889    def _update(self, data):
1890        self._data = data
1891        self._mapper.SetInputData(self.tomesh().polydata())
1892        self._mapper.Modified()
1893        return self
1894
1895    def tomesh(self, fill=True, shrink=1.0):
1896        """
1897        Build a polygonal Mesh from the current Grid object.
1898
1899        If `fill=True`, the interior faces of all the cells are created.
1900        (setting a `shrink` value slightly smaller than the default 1.0
1901        can avoid flickering due to internal adjacent faces).
1902
1903        If `fill=False`, only the boundary faces will be generated.
1904        """
1905        gf = vtk.vtkGeometryFilter()
1906        if fill:
1907            sf = vtk.vtkShrinkFilter()
1908            sf.SetInputData(self._data)
1909            sf.SetShrinkFactor(shrink)
1910            sf.Update()
1911            gf.SetInputData(sf.GetOutput())
1912            gf.Update()
1913            poly = gf.GetOutput()
1914            if shrink == 1.0:
1915                cleanPolyData = vtk.vtkCleanPolyData()
1916                cleanPolyData.PointMergingOn()
1917                cleanPolyData.ConvertLinesToPointsOn()
1918                cleanPolyData.ConvertPolysToLinesOn()
1919                cleanPolyData.ConvertStripsToPolysOn()
1920                cleanPolyData.SetInputData(poly)
1921                cleanPolyData.Update()
1922                poly = cleanPolyData.GetOutput()
1923        else:
1924            gf.SetInputData(self._data)
1925            gf.Update()
1926            poly = gf.GetOutput()
1927
1928        msh = vedo.mesh.Mesh(poly).flat()
1929        msh.scalarbar = self.scalarbar
1930        lut = utils.ctf2lut(self)
1931        if lut:
1932            msh.mapper().SetLookupTable(lut)
1933        if self.useCells:
1934            msh.mapper().SetScalarModeToUseCellData()
1935        else:
1936            msh.mapper().SetScalarModeToUsePointData()
1937
1938        msh.pipeline = utils.OperationNode(
1939            "tomesh", parents=[self], comment=f"fill={fill}", c="#9e2a2b:#e9c46a"
1940        )
1941        return msh
1942
1943    def cells(self):
1944        """
1945        Get the cells connectivity ids as a numpy array.
1946
1947        The output format is: `[[id0 ... idn], [id0 ... idm],  etc]`.
1948        """
1949        arr1d = utils.vtk2numpy(self._data.GetCells().GetData())
1950        if arr1d is None:
1951            return []
1952
1953        # Get cell connettivity ids as a 1D array. vtk format is:
1954        # [nids1, id0 ... idn, niids2, id0 ... idm,  etc].
1955        i = 0
1956        conn = []
1957        n = len(arr1d)
1958        if n:
1959            while True:
1960                cell = [arr1d[i + k] for k in range(1, arr1d[i] + 1)]
1961                conn.append(cell)
1962                i += arr1d[i] + 1
1963                if i >= n:
1964                    break
1965        return conn
1966
1967    def color(self, col, alpha=None, vmin=None, vmax=None):
1968        """
1969        Assign a color or a set of colors along the range of the scalar value.
1970        A single constant color can also be assigned.
1971        Any matplotlib color map name is also accepted, e.g. `volume.color('jet')`.
1972
1973        E.g.: say that your cells scalar runs from -3 to 6,
1974        and you want -3 to show red and 1.5 violet and 6 green, then just set:
1975
1976        `volume.color(['red', 'violet', 'green'])`
1977
1978        You can also assign a specific color to a aspecific value with eg.:
1979
1980        `volume.color([(0,'red', (0.5,'violet'), (1,'green')])`
1981
1982        Arguments:
1983            alpha : (list)
1984                use a list to specify transparencies along the scalar range
1985            vmin : (float)
1986                force the min of the scalar range to be this value
1987            vmax : (float)
1988                force the max of the scalar range to be this value
1989        """
1990        # supersedes method in Points, Mesh
1991
1992        if col is None:
1993            return self
1994        
1995        if vmin is None:
1996            vmin, _ = self._data.GetScalarRange()
1997        if vmax is None:
1998            _, vmax = self._data.GetScalarRange()
1999        ctf = self.GetProperty().GetRGBTransferFunction()
2000        ctf.RemoveAllPoints()
2001        self._color = col
2002
2003        if utils.is_sequence(col):
2004            if utils.is_sequence(col[0]) and len(col[0]) == 2:
2005                # user passing [(value1, color1), ...]
2006                for x, ci in col:
2007                    r, g, b = colors.get_color(ci)
2008                    ctf.AddRGBPoint(x, r, g, b)
2009                    # colors.printc('color at', round(x, 1),
2010                    #               'set to', colors.get_color_name((r, g, b)),
2011                    #               c='w', bold=0)
2012            else:
2013                # user passing [color1, color2, ..]
2014                for i, ci in enumerate(col):
2015                    r, g, b = colors.get_color(ci)
2016                    x = vmin + (vmax - vmin) * i / (len(col) - 1)
2017                    ctf.AddRGBPoint(x, r, g, b)
2018        elif isinstance(col, str):
2019            if col in colors.colors.keys() or col in colors.color_nicks.keys():
2020                r, g, b = colors.get_color(col)
2021                ctf.AddRGBPoint(vmin, r, g, b)  # constant color
2022                ctf.AddRGBPoint(vmax, r, g, b)
2023            else:  # assume it's a colormap
2024                for x in np.linspace(vmin, vmax, num=64, endpoint=True):
2025                    r, g, b = colors.color_map(x, name=col, vmin=vmin, vmax=vmax)
2026                    ctf.AddRGBPoint(x, r, g, b)
2027        elif isinstance(col, int):
2028            r, g, b = colors.get_color(col)
2029            ctf.AddRGBPoint(vmin, r, g, b)  # constant color
2030            ctf.AddRGBPoint(vmax, r, g, b)
2031        else:
2032            vedo.logger.warning(f"in color() unknown input type {type(col)}")
2033
2034        if alpha is not None:
2035            self.alpha(alpha, vmin=vmin, vmax=vmax)
2036        return self
2037
2038    def alpha(self, alpha, vmin=None, vmax=None):
2039        """
2040        Assign a set of tranparencies along the range of the scalar value.
2041        A single constant value can also be assigned.
2042
2043        E.g.: say `alpha=(0.0, 0.3, 0.9, 1)` and the scalar range goes from -10 to 150.
2044        Then all cells with a value close to -10 will be completely transparent, cells at 1/4
2045        of the range will get an alpha equal to 0.3 and voxels with value close to 150
2046        will be completely opaque.
2047
2048        As a second option one can set explicit (x, alpha_x) pairs to define the transfer function.
2049
2050        E.g.: say `alpha=[(-5, 0), (35, 0.4) (123,0.9)]` and the scalar range goes from -10 to 150.
2051        Then all cells below -5 will be completely transparent, cells with a scalar value of 35
2052        will get an opacity of 40% and above 123 alpha is set to 90%.
2053        """
2054        if vmin is None:
2055            vmin, _ = self._data.GetScalarRange()
2056        if vmax is None:
2057            _, vmax = self._data.GetScalarRange()
2058        otf = self.GetProperty().GetScalarOpacity()
2059        otf.RemoveAllPoints()
2060        self._alpha = alpha
2061
2062        if utils.is_sequence(alpha):
2063            alpha = np.array(alpha)
2064            if len(alpha.shape) == 1:  # user passing a flat list e.g. (0.0, 0.3, 0.9, 1)
2065                for i, al in enumerate(alpha):
2066                    xalpha = vmin + (vmax - vmin) * i / (len(alpha) - 1)
2067                    # Create transfer mapping scalar value to opacity
2068                    otf.AddPoint(xalpha, al)
2069                    # colors.printc("alpha at", round(xalpha, 1), "\tset to", al)
2070            elif len(alpha.shape) == 2:  # user passing [(x0,alpha0), ...]
2071                otf.AddPoint(vmin, alpha[0][1])
2072                for xalpha, al in alpha:
2073                    # Create transfer mapping scalar value to opacity
2074                    otf.AddPoint(xalpha, al)
2075                otf.AddPoint(vmax, alpha[-1][1])
2076
2077        else:
2078
2079            otf.AddPoint(vmin, alpha)  # constant alpha
2080            otf.AddPoint(vmax, alpha)
2081
2082        return self
2083
2084    def alpha_unit(self, u=None):
2085        """
2086        Defines light attenuation per unit length. Default is 1.
2087        The larger the unit length, the further light has to travel to attenuate the same amount.
2088
2089        E.g., if you set the unit distance to 0, you will get full opacity.
2090        It means that when light travels 0 distance it's already attenuated a finite amount.
2091        Thus, any finite distance should attenuate all light.
2092        The larger you make the unit distance, the more transparent the rendering becomes.
2093        """
2094        if u is None:
2095            return self.GetProperty().GetScalarOpacityUnitDistance()
2096        self.GetProperty().SetScalarOpacityUnitDistance(u)
2097        return self
2098
2099    def shrink(self, fraction=0.8):
2100        """
2101        Shrink the individual cells to improve visibility.
2102
2103        ![](https://vedo.embl.es/images/feats/shrink_hex.png)
2104        """
2105        sf = vtk.vtkShrinkFilter()
2106        sf.SetInputData(self.inputdata())
2107        sf.SetShrinkFactor(fraction)
2108        sf.Update()
2109        self._update(sf.GetOutput())
2110        self.pipeline = utils.OperationNode(
2111            "shrink", comment=f"by {fraction}", parents=[self], c="#9e2a2b"
2112        )
2113        return self
2114
2115    def isosurface(self, value=None, flying_edges=True):
2116        """
2117        Return an `Mesh` isosurface extracted from the `Volume` object.
2118
2119        Set `value` as single float or list of values to draw the isosurface(s).
2120        Use flying_edges for faster results (but sometimes can interfere with `smooth()`).
2121
2122        Examples:
2123            - [isosurfaces.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/isosurfaces.py)
2124
2125                ![](https://vedo.embl.es/images/volumetric/isosurfaces.png)
2126        """
2127        scrange = self._data.GetScalarRange()
2128
2129        if flying_edges:
2130            cf = vtk.vtkFlyingEdges3D()
2131            cf.InterpolateAttributesOn()
2132        else:
2133            cf = vtk.vtkContourFilter()
2134            cf.UseScalarTreeOn()
2135
2136        cf.SetInputData(self._data)
2137        cf.ComputeNormalsOn()
2138
2139        if utils.is_sequence(value):
2140            cf.SetNumberOfContours(len(value))
2141            for i, t in enumerate(value):
2142                cf.SetValue(i, t)
2143        else:
2144            if value is None:
2145                value = (2 * scrange[0] + scrange[1]) / 3.0
2146                # print("automatic isosurface value =", value)
2147            cf.SetValue(0, value)
2148
2149        cf.Update()
2150        poly = cf.GetOutput()
2151
2152        out = vedo.mesh.Mesh(poly, c=None).phong()
2153        out.mapper().SetScalarRange(scrange[0], scrange[1])
2154
2155        out.pipeline = utils.OperationNode(
2156            "isosurface",
2157            parents=[self],
2158            comment=f"#pts {out.inputdata().GetNumberOfPoints()}",
2159            c="#4cc9f0:#e9c46a",
2160        )
2161        return out
2162
2163    def legosurface(
2164        self, vmin=None, vmax=None, invert=False, boundary=False, array_name="input_scalars"
2165    ):
2166        """
2167        Represent an object - typically a `Volume` - as lego blocks (voxels).
2168        By default colors correspond to the volume's scalar.
2169        Returns an `Mesh` object.
2170
2171        Arguments:
2172            vmin : (float)
2173                the lower threshold, voxels below this value are not shown.
2174            vmax : (float)
2175                the upper threshold, voxels above this value are not shown.
2176            boundary : (bool)
2177                controls whether to include cells that are partially inside
2178            array_name : (int, str)
2179                name or index of the scalar array to be considered
2180
2181        Examples:
2182            - [legosurface.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/legosurface.py)
2183
2184                ![](https://vedo.embl.es/images/volumetric/56820682-da40e500-684c-11e9-8ea3-91cbcba24b3a.png)
2185        """
2186        dataset = vtk.vtkImplicitDataSet()
2187        dataset.SetDataSet(self._data)
2188        window = vtk.vtkImplicitWindowFunction()
2189        window.SetImplicitFunction(dataset)
2190
2191        srng = list(self._data.GetScalarRange())
2192        if vmin is not None:
2193            srng[0] = vmin
2194        if vmax is not None:
2195            srng[1] = vmax
2196        tol = 0.00001 * (srng[1] - srng[0])
2197        srng[0] -= tol
2198        srng[1] += tol
2199        window.SetWindowRange(srng)
2200
2201        extract = vtk.vtkExtractGeometry()
2202        extract.SetInputData(self._data)
2203        extract.SetImplicitFunction(window)
2204        extract.SetExtractInside(invert)
2205        extract.SetExtractBoundaryCells(boundary)
2206        extract.Update()
2207
2208        gf = vtk.vtkGeometryFilter()
2209        gf.SetInputData(extract.GetOutput())
2210        gf.Update()
2211
2212        m = vedo.mesh.Mesh(gf.GetOutput()).lw(0.1).flat()
2213        m.map_points_to_cells()
2214        m.celldata.select(array_name)
2215
2216        m.pipeline = utils.OperationNode(
2217            "legosurface", parents=[self], comment=f"array: {array_name}", c="#4cc9f0:#e9c46a"
2218        )
2219        return m
2220
2221    def cut_with_plane(self, origin=(0, 0, 0), normal="x"):
2222        """
2223        Cut the object with the plane defined by a point and a normal.
2224
2225        Arguments:
2226            origin : (list)
2227                the cutting plane goes through this point
2228            normal : (list, str)
2229                normal vector to the cutting plane
2230        """
2231        strn = str(normal)
2232        if strn   ==  "x": normal = (1, 0, 0)
2233        elif strn ==  "y": normal = (0, 1, 0)
2234        elif strn ==  "z": normal = (0, 0, 1)
2235        elif strn == "-x": normal = (-1, 0, 0)
2236        elif strn == "-y": normal = (0, -1, 0)
2237        elif strn == "-z": normal = (0, 0, -1)
2238        plane = vtk.vtkPlane()
2239        plane.SetOrigin(origin)
2240        plane.SetNormal(normal)
2241        clipper = vtk.vtkClipDataSet()
2242        clipper.SetInputData(self._data)
2243        clipper.SetClipFunction(plane)
2244        clipper.GenerateClipScalarsOff()
2245        clipper.GenerateClippedOutputOff()
2246        clipper.SetValue(0)
2247        clipper.Update()
2248        cout = clipper.GetOutput()
2249        self._update(cout)
2250        self.pipeline = utils.OperationNode("cut_with_plane", parents=[self], c="#9e2a2b")
2251        return self
2252
2253    def cut_with_box(self, box):
2254        """
2255        Cut the grid with the specified bounding box.
2256
2257        Parameter box has format [xmin, xmax, ymin, ymax, zmin, zmax].
2258        If an object is passed, its bounding box are used.
2259
2260        Example:
2261            ```python
2262            from vedo import *
2263            tetmesh = TetMesh(dataurl+'limb_ugrid.vtk')
2264            tetmesh.color('rainbow')
2265            cu = Cube(side=500).x(500) # any Mesh works
2266            tetmesh.cut_with_box(cu).show(axes=1)
2267            ```
2268            ![](https://vedo.embl.es/images/feats/tet_cut_box.png)
2269        """
2270        bc = vtk.vtkBoxClipDataSet()
2271        bc.SetInputData(self._data)
2272        if isinstance(box, vtk.vtkProp):
2273            boxb = box.GetBounds()
2274        else:
2275            boxb = box
2276        bc.SetBoxClip(*boxb)
2277        bc.Update()
2278        self._update(bc.GetOutput())
2279        self.pipeline = utils.OperationNode("cut_with_box", parents=[self, box], c="#9e2a2b")
2280        return self
2281
2282    def cut_with_mesh(self, mesh, invert=False, whole_cells=False, only_boundary=False):
2283        """
2284        Cut a UGrid, TetMesh or Volume with a Mesh.
2285
2286        Use `invert` to return cut off part of the input object.
2287        """
2288        polymesh = mesh.polydata()
2289        ug = self._data
2290
2291        ippd = vtk.vtkImplicitPolyDataDistance()
2292        ippd.SetInput(polymesh)
2293
2294        if whole_cells or only_boundary:
2295            clipper = vtk.vtkExtractGeometry()
2296            clipper.SetInputData(ug)
2297            clipper.SetImplicitFunction(ippd)
2298            clipper.SetExtractInside(not invert)
2299            clipper.SetExtractBoundaryCells(False)
2300            if only_boundary:
2301                clipper.SetExtractBoundaryCells(True)
2302                clipper.SetExtractOnlyBoundaryCells(True)
2303        else:
2304            signedDistances = vtk.vtkFloatArray()
2305            signedDistances.SetNumberOfComponents(1)
2306            signedDistances.SetName("SignedDistances")
2307            for pointId in range(ug.GetNumberOfPoints()):
2308                p = ug.GetPoint(pointId)
2309                signedDistance = ippd.EvaluateFunction(p)
2310                signedDistances.InsertNextValue(signedDistance)
2311            ug.GetPointData().AddArray(signedDistances)
2312            ug.GetPointData().SetActiveScalars("SignedDistances")
2313            clipper = vtk.vtkClipDataSet()
2314            clipper.SetInputData(ug)
2315            clipper.SetInsideOut(not invert)
2316            clipper.SetValue(0.0)
2317
2318        clipper.Update()
2319        cug = clipper.GetOutput()
2320
2321        if ug.GetCellData().GetScalars():  # not working
2322            scalname = ug.GetCellData().GetScalars().GetName()
2323            if scalname:  # not working
2324                if self.useCells:
2325                    self.celldata.select(scalname)
2326                else:
2327                    self.pointdata.select(scalname)
2328
2329        self._update(cug)
2330        self.pipeline = utils.OperationNode("cut_with_mesh", parents=[self, mesh], c="#9e2a2b")
2331        return self
2332
2333    def extract_cells_on_plane(self, origin, normal):
2334        """
2335        Extract cells that are lying of the specified surface.
2336        """
2337        bf = vtk.vtk3DLinearGridCrinkleExtractor()
2338        bf.SetInputData(self._data)
2339        bf.CopyPointDataOn()
2340        bf.CopyCellDataOn()
2341        bf.RemoveUnusedPointsOff()
2342
2343        plane = vtk.vtkPlane()
2344        plane.SetOrigin(origin)
2345        plane.SetNormal(normal)
2346        bf.SetImplicitFunction(plane)
2347        bf.Update()
2348
2349        self._update(bf.GetOutput())
2350        self.pipeline = utils.OperationNode(
2351            "extract_cells_on_plane",
2352            parents=[self],
2353            comment=f"#cells {self._data.GetNumberOfCells()}",
2354            c="#9e2a2b",
2355        )
2356        return self
2357
2358    def extract_cells_on_sphere(self, center, radius):
2359        """
2360        Extract cells that are lying of the specified surface.
2361        """
2362        bf = vtk.vtk3DLinearGridCrinkleExtractor()
2363        bf.SetInputData(self._data)
2364        bf.CopyPointDataOn()
2365        bf.CopyCellDataOn()
2366        bf.RemoveUnusedPointsOff()
2367
2368        sph = vtk.vtkSphere()
2369        sph.SetRadius(radius)
2370        sph.SetCenter(center)
2371        bf.SetImplicitFunction(sph)
2372        bf.Update()
2373
2374        self._update(bf.GetOutput())
2375        self.pipeline = utils.OperationNode(
2376            "extract_cells_on_sphere",
2377            parents=[self],
2378            comment=f"#cells {self._data.GetNumberOfCells()}",
2379            c="#9e2a2b",
2380        )
2381        return self
2382
2383    def extract_cells_on_cylinder(self, center, axis, radius):
2384        """
2385        Extract cells that are lying of the specified surface.
2386        """
2387        bf = vtk.vtk3DLinearGridCrinkleExtractor()
2388        bf.SetInputData(self._data)
2389        bf.CopyPointDataOn()
2390        bf.CopyCellDataOn()
2391        bf.RemoveUnusedPointsOff()
2392
2393        cyl = vtk.vtkCylinder()
2394        cyl.SetRadius(radius)
2395        cyl.SetCenter(center)
2396        cyl.SetAxis(axis)
2397        bf.SetImplicitFunction(cyl)
2398        bf.Update()
2399
2400        self.pipeline = utils.OperationNode(
2401            "extract_cells_on_cylinder",
2402            parents=[self],
2403            comment=f"#cells {self._data.GetNumberOfCells()}",
2404            c="#9e2a2b",
2405        )
2406        self._update(bf.GetOutput())
2407        return self
2408
2409    def clean(self):
2410        """
2411        Cleanup unused points and empty cells
2412        """
2413        cl = vtk.vtkStaticCleanUnstructuredGrid()
2414        cl.SetInputData(self._data)
2415        cl.RemoveUnusedPointsOn()
2416        cl.ProduceMergeMapOff()
2417        cl.AveragePointDataOff()
2418        cl.Update()
2419
2420        self._update(cl.GetOutput())
2421        self.pipeline = utils.OperationNode(
2422            "clean", parents=[self], comment=f"#cells {self._data.GetNumberOfCells()}", c="#9e2a2b"
2423        )
2424        return self
2425
2426    def find_cell(self, p):
2427        """Locate the cell that contains a point and return the cell ID."""
2428        cell = vtk.vtkTetra()
2429        cellId = vtk.mutable(0)
2430        tol2 = vtk.mutable(0)
2431        subId = vtk.mutable(0)
2432        pcoords = [0, 0, 0]
2433        weights = [0, 0, 0]
2434        cid = self._data.FindCell(p, cell, cellId, tol2, subId, pcoords, weights)
2435        return cid
2436
2437    def extract_cells_by_id(self, idlist, use_point_ids=False):
2438        """Return a new UGrid composed of the specified subset of indices."""
2439        selectionNode = vtk.vtkSelectionNode()
2440        if use_point_ids:
2441            selectionNode.SetFieldType(vtk.vtkSelectionNode.POINT)
2442            contcells = vtk.vtkSelectionNode.CONTAINING_CELLS()
2443            selectionNode.GetProperties().Set(contcells, 1)
2444        else:
2445            selectionNode.SetFieldType(vtk.vtkSelectionNode.CELL)
2446        selectionNode.SetContentType(vtk.vtkSelectionNode.INDICES)
2447        vidlist = utils.numpy2vtk(idlist, dtype="id")
2448        selectionNode.SetSelectionList(vidlist)
2449        selection = vtk.vtkSelection()
2450        selection.AddNode(selectionNode)
2451        es = vtk.vtkExtractSelection()
2452        es.SetInputData(0, self._data)
2453        es.SetInputData(1, selection)
2454        es.Update()
2455
2456        ug = vedo.ugrid.UGrid(es.GetOutput())
2457        pr = vtk.vtkProperty()
2458        pr.DeepCopy(self.GetProperty())
2459        ug.SetProperty(pr)
2460        ug.property = pr
2461
2462        # assign the same transformation to the copy
2463        ug.SetOrigin(self.GetOrigin())
2464        ug.SetScale(self.GetScale())
2465        ug.SetOrientation(self.GetOrientation())
2466        ug.SetPosition(self.GetPosition())
2467        ug.mapper().SetLookupTable(utils.ctf2lut(self))
2468        ug.pipeline = utils.OperationNode(
2469            "extract_cells_by_id",
2470            parents=[self],
2471            comment=f"#cells {self._data.GetNumberOfCells()}",
2472            c="#9e2a2b",
2473        )
2474        return ug
2475
2476
2477############################################################################### funcs
2478def _getinput(obj):
2479    if isinstance(obj, (vtk.vtkVolume, vtk.vtkActor)):
2480        return obj.GetMapper().GetInput()
2481    return obj
2482
2483
2484def probe_points(dataset, pts):
2485    """
2486    Takes a `Volume` (or any other vtk data set)
2487    and probes its scalars at the specified points in space.
2488
2489    Note that a mask is also output with valid/invalid points which can be accessed
2490    with `mesh.pointdata['vtkValidPointMask']`.
2491
2492    Examples:
2493        - [probe_points.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/probe_points.py)
2494
2495            ![](https://vedo.embl.es/images/volumetric/probePoints.png)
2496    """
2497    if isinstance(pts, vedo.pointcloud.Points):
2498        pts = pts.points()
2499
2500    def _readpoints():
2501        output = src.GetPolyDataOutput()
2502        points = vtk.vtkPoints()
2503        for p in pts:
2504            x, y, z = p
2505            points.InsertNextPoint(x, y, z)
2506        output.SetPoints(points)
2507
2508        cells = vtk.vtkCellArray()
2509        cells.InsertNextCell(len(pts))
2510        for i in range(len(pts)):
2511            cells.InsertCellPoint(i)
2512        output.SetVerts(cells)
2513
2514    src = vtk.vtkProgrammableSource()
2515    src.SetExecuteMethod(_readpoints)
2516    src.Update()
2517    img = _getinput(dataset)
2518    probeFilter = vtk.vtkProbeFilter()
2519    probeFilter.SetSourceData(img)
2520    probeFilter.SetInputConnection(src.GetOutputPort())
2521    probeFilter.Update()
2522    poly = probeFilter.GetOutput()
2523    pm = vedo.mesh.Mesh(poly)
2524    pm.name = "ProbePoints"
2525    pm.pipeline = utils.OperationNode("probe_points", parents=[dataset])
2526    return pm
2527
2528
2529def probe_line(dataset, p1, p2, res=100):
2530    """
2531    Takes a `Volume`  (or any other vtk data set)
2532    and probes its scalars along a line defined by 2 points `p1` and `p2`.
2533
2534    Note that a mask is also output with valid/invalid points which can be accessed
2535    with `mesh.pointdata['vtkValidPointMask']`.
2536
2537    Use `res` to set the nr of points along the line
2538
2539    Examples:
2540        - [probe_line1.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/probe_line1.py)
2541        - [probe_line2.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/probe_line2.py)
2542
2543            ![](https://vedo.embl.es/images/volumetric/probeLine2.png)
2544    """
2545    line = vtk.vtkLineSource()
2546    line.SetResolution(res)
2547    line.SetPoint1(p1)
2548    line.SetPoint2(p2)
2549    img = _getinput(dataset)
2550    probeFilter = vtk.vtkProbeFilter()
2551    probeFilter.SetSourceData(img)
2552    probeFilter.SetInputConnection(line.GetOutputPort())
2553    probeFilter.Update()
2554    poly = probeFilter.GetOutput()
2555    lnn = vedo.mesh.Mesh(poly)
2556    lnn.name = "ProbeLine"
2557    lnn.pipeline = utils.OperationNode("probe_line", parents=[dataset])
2558    return lnn
2559
2560
2561def probe_plane(dataset, origin=(0, 0, 0), normal=(1, 0, 0)):
2562    """
2563    Takes a `Volume` (or any other vtk data set)
2564    and probes its scalars on a plane defined by a point and a normal.
2565
2566    Examples:
2567        - [slice_plane1.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/slice_plane1.py)
2568        - [slice_plane2.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/slice_plane2.py)
2569
2570            ![](https://vedo.embl.es/images/volumetric/slicePlane2.png)
2571    """
2572    img = _getinput(dataset)
2573    plane = vtk.vtkPlane()
2574    plane.SetOrigin(origin)
2575    plane.SetNormal(normal)
2576    planeCut = vtk.vtkCutter()
2577    planeCut.SetInputData(img)
2578    planeCut.SetCutFunction(plane)
2579    planeCut.Update()
2580    poly = planeCut.GetOutput()
2581    cutmesh = vedo.mesh.Mesh(poly)
2582    cutmesh.name = "ProbePlane"
2583    cutmesh.pipeline = utils.OperationNode("probe_plane", parents=[dataset])
2584    return cutmesh
class Base3DProp:
319class Base3DProp:
320    """
321    Base class to manage positioning and size of the objects in space and other properties.
322
323    .. warning:: Do not use this class to instantiate objects
324    """
325
326    def __init__(self):
327        """
328        Base class to manage positioning and size of the objects in space and other properties.
329        """
330        self.filename = ""
331        self.name = ""
332        self.file_size = ""
333        self.created = ""
334        self.trail = None
335        self.trail_points = []
336        self.trail_segment_size = 0
337        self.trail_offset = None
338        self.shadows = []
339        self.axes = None
340        self.picked3d = None
341        self.units = None
342        self.top = np.array([0, 0, 1])
343        self.base = np.array([0, 0, 0])
344        self.info = {}
345        self.time = time.time()
346        self.rendered_at = set()
347        self.transform = None
348        self._isfollower = False  # set by mesh.follow_camera()
349
350        self.point_locator = None
351        self.cell_locator = None
352
353        self.scalarbar = None
354        # self.scalarbars = dict() #TODO
355        self.pipeline = None
356
357    def address(self):
358        """
359        Return a unique memory address integer which may serve as the ID of the
360        object, or passed to c++ code.
361        """
362        # https://www.linkedin.com/pulse/speedup-your-code-accessing-python-vtk-objects-from-c-pletzer/
363        # https://github.com/tfmoraes/polydata_connectivity
364        return int(self.inputdata().GetAddressAsString("")[5:], 16)
365
366    def pickable(self, value=None):
367        """Set/get the pickability property of an object."""
368        if value is None:
369            return self.GetPickable()
370        self.SetPickable(value)
371        return self
372
373    def draggable(self, value=None):  # NOT FUNCTIONAL?
374        """Set/get the draggability property of an object."""
375        if value is None:
376            return self.GetDragable()
377        self.SetDragable(value)
378        return self
379
380    def origin(self, x=None, y=None, z=None):
381        """
382        Set/get object's origin.
383
384        Relevant to control the scaling with `scale()` and rotations.
385        Has no effect on position.
386        """
387        if x is None:
388            return np.array(self.GetOrigin()) + self.GetPosition()
389
390        if z is None and y is None:  # assume x is of the form (x,y,z)
391            if len(x) == 3:
392                x, y, z = x
393            else:
394                x, y = x
395                z = 0
396        elif z is None:  # assume x,y is of the form x, y
397            z = 0
398        self.SetOrigin([x, y, z] - np.array(self.GetPosition()))
399        return self
400
401    def pos(self, x=None, y=None, z=None):
402        """Set/Get object position."""
403        if x is None:  # get functionality
404            return np.array(self.GetPosition())
405
406        if z is None and y is None:  # assume x is of the form (x,y,z)
407            if len(x) == 3:
408                x, y, z = x
409            else:
410                x, y = x
411                z = 0
412        elif z is None:  # assume x,y is of the form x, y
413            z = 0
414        self.SetPosition(x, y, z)
415
416        self.point_locator = None
417        self.cell_locator = None
418        return self  # return itself to concatenate methods
419
420    def shift(self, dx=0, dy=0, dz=0):
421        """Add a vector to the current object position."""
422        p = np.array(self.GetPosition())
423
424        if utils.is_sequence(dx):
425            if len(dx) == 2:
426                self.SetPosition(p + [dx[0], dx[1], 0])
427            else:
428                self.SetPosition(p + dx)
429        else:
430            self.SetPosition(p + [dx, dy, dz])
431
432        self.point_locator = None
433        self.cell_locator = None
434        return self
435
436    def x(self, val=None):
437        """Set/Get object position along x axis."""
438        p = self.GetPosition()
439        if val is None:
440            return p[0]
441        self.pos(val, p[1], p[2])
442        return self
443
444    def y(self, val=None):
445        """Set/Get object position along y axis."""
446        p = self.GetPosition()
447        if val is None:
448            return p[1]
449        self.pos(p[0], val, p[2])
450        return self
451
452    def z(self, val=None):
453        """Set/Get object position along z axis."""
454        p = self.GetPosition()
455        if val is None:
456            return p[2]
457        self.pos(p[0], p[1], val)
458        return self
459
460    def rotate(self, angle, axis=(1, 0, 0), point=(0, 0, 0), rad=False):
461        """
462        Rotate around an arbitrary `axis` passing through `point`.
463
464        Example:
465            ```python
466            from vedo import *
467            c1 = Cube()
468            c2 = c1.clone().c('violet').alpha(0.5) # copy of c1
469            v = vector(0.2,1,0)
470            p = vector(1,0,0)  # axis passes through this point
471            c2.rotate(90, axis=v, point=p)
472            l = Line(-v+p, v+p).lw(3).c('red')
473            show(c1, l, c2, axes=1).close()
474            ```
475            ![](https://vedo.embl.es/images/feats/rotate_axis.png)
476        """
477        if rad:
478            anglerad = angle
479        else:
480            anglerad = np.deg2rad(angle)
481        axis = utils.versor(axis)
482        a = np.cos(anglerad / 2)
483        b, c, d = -axis * np.sin(anglerad / 2)
484        aa, bb, cc, dd = a * a, b * b, c * c, d * d
485        bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
486        R = np.array(
487            [
488                [aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)],
489                [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)],
490                [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc],
491            ]
492        )
493        rv = np.dot(R, self.GetPosition() - np.asarray(point)) + point
494
495        if rad:
496            angle *= 180.0 / np.pi
497        # this vtk method only rotates in the origin of the object:
498        self.RotateWXYZ(angle, axis[0], axis[1], axis[2])
499        self.pos(rv)
500        return self
501
502    def _rotatexyz(self, a, angle, rad, around):
503        if rad:
504            angle *= 180 / np.pi
505
506        T = vtk.vtkTransform()
507        T.SetMatrix(self.GetMatrix())
508        T.PostMultiply()
509
510        rot = dict(x=T.RotateX, y=T.RotateY, z=T.RotateZ)
511
512        if around is None:
513            # rotate around its origin
514            rot[a](angle)
515        else:
516            if around == "itself":
517                around = self.GetPosition()
518            # displacement needed to bring it back to the origin
519            # and disregard origin
520            disp = around - np.array(self.GetOrigin())
521            T.Translate(-disp)
522            rot[a](angle)
523            T.Translate(disp)
524
525        self.SetOrientation(T.GetOrientation())
526        self.SetPosition(T.GetPosition())
527
528        self.point_locator = None
529        self.cell_locator = None
530        return self
531
532    def rotate_x(self, angle, rad=False, around=None):
533        """
534        Rotate around x-axis. If angle is in radians set `rad=True`.
535
536        Use `around` to define a pivoting point.
537        """
538        return self._rotatexyz("x", angle, rad, around)
539
540    def rotate_y(self, angle, rad=False, around=None):
541        """
542        Rotate around y-axis. If angle is in radians set `rad=True`.
543
544        Use `around` to define a pivoting point.
545        """
546        return self._rotatexyz("y", angle, rad, around)
547
548    def rotate_z(self, angle, rad=False, around=None):
549        """
550        Rotate around z-axis. If angle is in radians set `rad=True`.
551
552        Use `around` to define a pivoting point.
553        """
554        return self._rotatexyz("z", angle, rad, around)
555
556    def orientation(self, newaxis=None, rotation=0, concatenate=False, xyplane=False, rad=False):
557        """
558        Set/Get object orientation.
559
560        Arguments:
561            rotation : (float)
562                rotate object around newaxis.
563            concatenate : (bool)
564                concatenate the orientation operation with the previous existing transform (if any)
565            xyplane : (bool)
566                make an extra rotation to keep the object aligned to the xy-plane
567            rad : (bool)
568                set to True if angle is expressed in radians.
569
570        Example:
571            ```python
572            from vedo import *
573            center = np.array([581/2,723/2,0])
574            objs = []
575            for a in np.linspace(0, 6.28, 7):
576                v = vector(cos(a), sin(a), 0)*1000
577                pic = Picture(dataurl+"images/dog.jpg").rotate_z(10)
578                pic.orientation(v, xyplane=True)
579                pic.origin(center)
580                pic.pos(v - center)
581                objs += [pic, Arrow(v, v+v)]
582            show(objs, Point(), axes=1).close()
583            ```
584            ![](https://vedo.embl.es/images/feats/orientation.png)
585
586        Examples:
587            - [gyroscope2.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/gyroscope2.py)
588
589            ![](https://vedo.embl.es/images/simulations/50738942-687b5780-11d9-11e9-97f0-72bbd63f7d6e.gif)
590        """
591        if self.top is None or self.base is None:
592            initaxis = (0, 0, 1)
593        else:
594            initaxis = utils.versor(self.top - self.base)
595
596        newaxis = utils.versor(newaxis)
597        p = np.array(self.GetPosition())
598        crossvec = np.cross(initaxis, newaxis)
599
600        angleth = np.arccos(np.dot(initaxis, newaxis))
601
602        T = vtk.vtkTransform()
603        if concatenate:
604            try:
605                M = self.GetMatrix()
606                T.SetMatrix(M)
607            except:
608                pass
609        T.PostMultiply()
610        T.Translate(-p)
611        if rotation:
612            if rad:
613                rotation *= 180.0 / np.pi
614            T.RotateWXYZ(rotation, initaxis)
615        if xyplane:
616            angleph = np.arctan2(newaxis[1], newaxis[0])
617            T.RotateWXYZ(np.rad2deg(angleph + angleth), initaxis)  # compensation
618        T.RotateWXYZ(np.rad2deg(angleth), crossvec)
619        T.Translate(p)
620
621        self.SetOrientation(T.GetOrientation())
622
623        self.point_locator = None
624        self.cell_locator = None
625        return self
626
627        # newaxis = utils.versor(newaxis)
628        # pos = np.array(self.GetPosition())
629        # crossvec = np.cross(initaxis, newaxis)
630        # angle = np.arccos(np.dot(initaxis, newaxis))
631        # T = vtk.vtkTransform()
632        # T.PostMultiply()
633        # T.Translate(-pos)
634        # if rotation:
635        #     T.RotateWXYZ(rotation, initaxis)
636        # T.RotateWXYZ(np.rad2deg(angle), crossvec)
637        # T.Translate(pos)
638        # self.SetUserTransform(T)
639        # self.transform = T
640
641    def scale(self, s=None, reset=False):
642        """
643        Set/get object's scaling factor.
644
645        Arguments:
646            s : (list, float)
647                scaling factor(s).
648            reset : (bool)
649                if True previous scaling factors are ignored.
650
651        Note:
652            use `s=(sx,sy,sz)` to scale differently in the three coordinates.
653        """
654        if s is None:
655            return np.array(self.GetScale())
656
657        # assert s[0] != 0
658        # assert s[1] != 0
659        # assert s[2] != 0
660
661        if reset:
662            self.SetScale(s)
663        else:
664            self.SetScale(np.multiply(self.GetScale(), s))
665
666        self.point_locator = None
667        self.cell_locator = None
668        return self
669
670    def get_transform(self, invert=False):
671        """
672        Check if `object.transform` exists and returns a `vtkTransform`.
673        Otherwise return current user transformation (where the object is currently placed).
674
675        Use `invert` to return the inverse of the current transformation
676
677        Example:
678            ```python
679            from vedo import *
680
681            c1 = Cube()
682            c2 = c1.clone().c('violet').alpha(0.5) # copy of c1
683            v = vector(0.2,1,0)
684            p = vector(1,0,0)  # axis passes through this point
685            c2.rotate(90, axis=v, point=p)
686
687            # get the inverse of the current transformation
688            T = c2.get_transform(invert=True)
689            c2.apply_transform(T)  # put back c2 in place
690
691            l = Line(p-v, p+v).lw(3).c('red')
692            show(c1.wireframe().lw(3), l, c2, axes=1).close()
693            ```
694            ![](https://vedo.embl.es/images/feats/get_transf.png)
695        """
696        if self.transform:
697            tr = self.transform
698            if invert:
699                tr = tr.GetInverse()
700            return tr
701
702        T = self.GetMatrix()
703        tr = vtk.vtkTransform()
704        tr.SetMatrix(T)
705        if invert:
706            tr = tr.GetInverse()
707        return tr
708
709    def apply_transform(self, T, reset=False, concatenate=False):
710        """
711        Transform object position and orientation.
712
713        Arguments:
714            reset : (bool)
715                no effect, this is superseded by `pointcloud.apply_transform()`
716            concatenate : (bool)
717                no effect, this is superseded by `pointcloud.apply_transform()`
718        """
719        if isinstance(T, vtk.vtkMatrix4x4):
720            self.SetUserMatrix(T)
721        elif utils.is_sequence(T):
722            vm = vtk.vtkMatrix4x4()
723            for i in [0, 1, 2, 3]:
724                for j in [0, 1, 2, 3]:
725                    vm.SetElement(i, j, T[i][j])
726            self.SetUserMatrix(vm)
727        else:
728            self.SetUserTransform(T)
729        self.transform = T
730
731        self.point_locator = None
732        self.cell_locator = None
733        return self
734
735    def align_to_bounding_box(self, msh, rigid=False):
736        """
737        Align the current object's bounding box to the bounding box
738        of the input object.
739
740        Use `rigid` to disable scaling.
741
742        Examples:
743            - [align6.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/align6.py)
744        """
745        lmt = vtk.vtkLandmarkTransform()
746        ss = vtk.vtkPoints()
747        xss0, xss1, yss0, yss1, zss0, zss1 = self.bounds()
748        for p in [
749            [xss0, yss0, zss0],
750            [xss1, yss0, zss0],
751            [xss1, yss1, zss0],
752            [xss0, yss1, zss0],
753            [xss0, yss0, zss1],
754            [xss1, yss0, zss1],
755            [xss1, yss1, zss1],
756            [xss0, yss1, zss1],
757        ]:
758            ss.InsertNextPoint(p)
759        st = vtk.vtkPoints()
760        xst0, xst1, yst0, yst1, zst0, zst1 = msh.bounds()
761        for p in [
762            [xst0, yst0, zst0],
763            [xst1, yst0, zst0],
764            [xst1, yst1, zst0],
765            [xst0, yst1, zst0],
766            [xst0, yst0, zst1],
767            [xst1, yst0, zst1],
768            [xst1, yst1, zst1],
769            [xst0, yst1, zst1],
770        ]:
771            st.InsertNextPoint(p)
772
773        lmt.SetSourceLandmarks(ss)
774        lmt.SetTargetLandmarks(st)
775        lmt.SetModeToAffine()
776        if rigid:
777            lmt.SetModeToRigidBody()
778        lmt.Update()
779        self.apply_transform(lmt)
780        self.transform = lmt
781
782        self.point_locator = None
783        self.cell_locator = None
784        return self
785
786    def on(self):
787        """Switch on  object visibility. Object is not removed."""
788        self.VisibilityOn()
789        try:
790            self.scalarbar.VisibilityOn()
791        except AttributeError:
792            pass
793        try:
794            self.trail.VisibilityOn()
795        except AttributeError:
796            pass
797        try:
798            for sh in self.shadows:
799                sh.VisibilityOn()
800        except AttributeError:
801            pass
802        return self
803
804    def off(self):
805        """Switch off object visibility. Object is not removed."""
806        self.VisibilityOff()
807        try:
808            self.scalarbar.VisibilityOff()
809        except AttributeError:
810            pass
811        try:
812            self.trail.VisibilityOff()
813        except AttributeError:
814            pass
815        try:
816            for sh in self.shadows:
817                sh.VisibilityOff()
818        except AttributeError:
819            pass
820        return self
821
822    def toggle(self):
823        """Toggle object visibility on/off."""
824        v = self.GetVisibility()
825        if v:
826            self.off()
827        else:
828            self.on()
829        return self
830
831    def box(self, scale=1, padding=0, fill=False):
832        """
833        Return the bounding box as a new `Mesh`.
834
835        Arguments:
836            scale : (float)
837                box size can be scaled by a factor
838            padding : (float, list)
839                a constant padding can be added (can be a list [padx,pady,padz])
840
841        Examples:
842            - [latex.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/latex.py)
843        """
844        b = self.bounds()
845        if not utils.is_sequence(padding):
846            padding = [padding, padding, padding]
847        length, width, height = b[1] - b[0], b[3] - b[2], b[5] - b[4]
848        tol = (length + width + height) / 30000  # useful for boxing 2D text
849        pos = [(b[0] + b[1]) / 2, (b[3] + b[2]) / 2, (b[5] + b[4]) / 2 - tol]
850        bx = vedo.shapes.Box(
851            pos,
852            length * scale + padding[0],
853            width * scale + padding[1],
854            height * scale + padding[2],
855            c="gray",
856        )
857        if hasattr(self, "GetProperty"):  # could be Assembly
858            if isinstance(self.GetProperty(), vtk.vtkProperty):  # could be volume
859                pr = vtk.vtkProperty()
860                pr.DeepCopy(self.GetProperty())
861                bx.SetProperty(pr)
862                bx.property = pr
863        bx.wireframe(not fill)
864        bx.flat().lighting("off")
865        return bx
866
867    def use_bounds(self, ub=True):
868        """
869        Instruct the current camera to either take into account or ignore
870        the object bounds when resetting.
871        """
872        self.SetUseBounds(ub)
873        return self
874
875    def bounds(self):
876        """
877        Get the object bounds.
878        Returns a list in format `[xmin,xmax, ymin,ymax, zmin,zmax]`.
879        """
880        try:
881            pts = self.points()
882            xmin, ymin, zmin = np.min(pts, axis=0)
883            xmax, ymax, zmax = np.max(pts, axis=0)
884            return [xmin, xmax, ymin, ymax, zmin, zmax]
885        except (AttributeError, ValueError):
886            return self.GetBounds()
887
888    def xbounds(self, i=None):
889        """Get the bounds `[xmin,xmax]`. Can specify upper or lower with i (0,1)."""
890        b = self.bounds()
891        if i is not None:
892            return b[i]
893        return (b[0], b[1])
894
895    def ybounds(self, i=None):
896        """Get the bounds `[ymin,ymax]`. Can specify upper or lower with i (0,1)."""
897        b = self.bounds()
898        if i == 0:
899            return b[2]
900        if i == 1:
901            return b[3]
902        return (b[2], b[3])
903
904    def zbounds(self, i=None):
905        """Get the bounds `[zmin,zmax]`. Can specify upper or lower with i (0,1)."""
906        b = self.bounds()
907        if i == 0:
908            return b[4]
909        if i == 1:
910            return b[5]
911        return (b[4], b[5])
912
913
914    def diagonal_size(self):
915        """Get the length of the diagonal of mesh bounding box."""
916        b = self.bounds()
917        return np.sqrt((b[1] - b[0]) ** 2 + (b[3] - b[2]) ** 2 + (b[5] - b[4]) ** 2)
918        # return self.GetLength() # ???different???
919
920
921    def copy_data_from(self, obj):
922        """Copy all data (point and cell data) from this input object"""
923        self.inputdata().GetPointData().PassData(obj.inputdata().GetPointData())
924        self.inputdata().GetCellData().PassData(obj.inputdata().GetCellData())
925        self.pipeline = utils.OperationNode(
926            f"copy_data_from\n{obj.__class__.__name__}",
927            parents=[self, obj],
928            shape="note",
929            c="#ccc5b9",
930        )
931        return self
932
933    def print(self):
934        """Print information about an object."""
935        utils.print_info(self)
936        return self
937
938    def show(self, **options):
939        """
940        Create on the fly an instance of class `Plotter` or use the last existing one to
941        show one single object.
942
943        This method is meant as a shortcut. If more than one object needs to be visualised
944        please use the syntax `show(mesh1, mesh2, volume, ..., options)`.
945
946        Returns the `Plotter` class instance.
947        """
948        return vedo.plotter.show(self, **options)
949
950    def thumbnail(self, zoom=1.25, size=(200, 200), bg="white", azimuth=0, elevation=0, axes=False):
951        """Build a thumbnail of the object and return it as an array."""
952        # speed is about 20Hz for size=[200,200]
953        ren = vtk.vtkRenderer()
954        ren.AddActor(self)
955        if axes:
956            axes = vedo.addons.Axes(self)
957            ren.AddActor(axes)
958        ren.ResetCamera()
959        cam = ren.GetActiveCamera()
960        cam.Zoom(zoom)
961        cam.Elevation(elevation)
962        cam.Azimuth(azimuth)
963
964        ren_win = vtk.vtkRenderWindow()
965        ren_win.SetOffScreenRendering(True)
966        ren_win.SetSize(size)
967        ren.SetBackground(colors.get_color(bg))
968        ren_win.AddRenderer(ren)
969        ren_win.Render()
970
971        nx, ny = ren_win.GetSize()
972        arr = vtk.vtkUnsignedCharArray()
973        ren_win.GetRGBACharPixelData(0, 0, nx - 1, ny - 1, 0, arr)
974        narr = utils.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3])
975        narr = np.ascontiguousarray(np.flip(narr, axis=0))
976
977        ren.RemoveActor(self)
978        if axes:
979            ren.RemoveActor(axes)
980        ren_win.Finalize()
981        del ren_win
982        return narr

Base class to manage positioning and size of the objects in space and other properties.

Do not use this class to instantiate objects
Base3DProp()
326    def __init__(self):
327        """
328        Base class to manage positioning and size of the objects in space and other properties.
329        """
330        self.filename = ""
331        self.name = ""
332        self.file_size = ""
333        self.created = ""
334        self.trail = None
335        self.trail_points = []
336        self.trail_segment_size = 0
337        self.trail_offset = None
338        self.shadows = []
339        self.axes = None
340        self.picked3d = None
341        self.units = None
342        self.top = np.array([0, 0, 1])
343        self.base = np.array([0, 0, 0])
344        self.info = {}
345        self.time = time.time()
346        self.rendered_at = set()
347        self.transform = None
348        self._isfollower = False  # set by mesh.follow_camera()
349
350        self.point_locator = None
351        self.cell_locator = None
352
353        self.scalarbar = None
354        # self.scalarbars = dict() #TODO
355        self.pipeline = None

Base class to manage positioning and size of the objects in space and other properties.

def address(self):
357    def address(self):
358        """
359        Return a unique memory address integer which may serve as the ID of the
360        object, or passed to c++ code.
361        """
362        # https://www.linkedin.com/pulse/speedup-your-code-accessing-python-vtk-objects-from-c-pletzer/
363        # https://github.com/tfmoraes/polydata_connectivity
364        return int(self.inputdata().GetAddressAsString("")[5:], 16)

Return a unique memory address integer which may serve as the ID of the object, or passed to c++ code.

def pickable(self, value=None):
366    def pickable(self, value=None):
367        """Set/get the pickability property of an object."""
368        if value is None:
369            return self.GetPickable()
370        self.SetPickable(value)
371        return self

Set/get the pickability property of an object.

def draggable(self, value=None):
373    def draggable(self, value=None):  # NOT FUNCTIONAL?
374        """Set/get the draggability property of an object."""
375        if value is None:
376            return self.GetDragable()
377        self.SetDragable(value)
378        return self

Set/get the draggability property of an object.

def origin(self, x=None, y=None, z=None):
380    def origin(self, x=None, y=None, z=None):
381        """
382        Set/get object's origin.
383
384        Relevant to control the scaling with `scale()` and rotations.
385        Has no effect on position.
386        """
387        if x is None:
388            return np.array(self.GetOrigin()) + self.GetPosition()
389
390        if z is None and y is None:  # assume x is of the form (x,y,z)
391            if len(x) == 3:
392                x, y, z = x
393            else:
394                x, y = x
395                z = 0
396        elif z is None:  # assume x,y is of the form x, y
397            z = 0
398        self.SetOrigin([x, y, z] - np.array(self.GetPosition()))
399        return self

Set/get object's origin.

Relevant to control the scaling with scale() and rotations. Has no effect on position.

def pos(self, x=None, y=None, z=None):
401    def pos(self, x=None, y=None, z=None):
402        """Set/Get object position."""
403        if x is None:  # get functionality
404            return np.array(self.GetPosition())
405
406        if z is None and y is None:  # assume x is of the form (x,y,z)
407            if len(x) == 3:
408                x, y, z = x
409            else:
410                x, y = x
411                z = 0
412        elif z is None:  # assume x,y is of the form x, y
413            z = 0
414        self.SetPosition(x, y, z)
415
416        self.point_locator = None
417        self.cell_locator = None
418        return self  # return itself to concatenate methods

Set/Get object position.

def shift(self, dx=0, dy=0, dz=0):
420    def shift(self, dx=0, dy=0, dz=0):
421        """Add a vector to the current object position."""
422        p = np.array(self.GetPosition())
423
424        if utils.is_sequence(dx):
425            if len(dx) == 2:
426                self.SetPosition(p + [dx[0], dx[1], 0])
427            else:
428                self.SetPosition(p + dx)
429        else:
430            self.SetPosition(p + [dx, dy, dz])
431
432        self.point_locator = None
433        self.cell_locator = None
434        return self

Add a vector to the current object position.

def x(self, val=None):
436    def x(self, val=None):
437        """Set/Get object position along x axis."""
438        p = self.GetPosition()
439        if val is None:
440            return p[0]
441        self.pos(val, p[1], p[2])
442        return self

Set/Get object position along x axis.

def y(self, val=None):
444    def y(self, val=None):
445        """Set/Get object position along y axis."""
446        p = self.GetPosition()
447        if val is None:
448            return p[1]
449        self.pos(p[0], val, p[2])
450        return self

Set/Get object position along y axis.

def z(self, val=None):
452    def z(self, val=None):
453        """Set/Get object position along z axis."""
454        p = self.GetPosition()
455        if val is None:
456            return p[2]
457        self.pos(p[0], p[1], val)
458        return self

Set/Get object position along z axis.

def rotate(self, angle, axis=(1, 0, 0), point=(0, 0, 0), rad=False):
460    def rotate(self, angle, axis=(1, 0, 0), point=(0, 0, 0), rad=False):
461        """
462        Rotate around an arbitrary `axis` passing through `point`.
463
464        Example:
465            ```python
466            from vedo import *
467            c1 = Cube()
468            c2 = c1.clone().c('violet').alpha(0.5) # copy of c1
469            v = vector(0.2,1,0)
470            p = vector(1,0,0)  # axis passes through this point
471            c2.rotate(90, axis=v, point=p)
472            l = Line(-v+p, v+p).lw(3).c('red')
473            show(c1, l, c2, axes=1).close()
474            ```
475            ![](https://vedo.embl.es/images/feats/rotate_axis.png)
476        """
477        if rad:
478            anglerad = angle
479        else:
480            anglerad = np.deg2rad(angle)
481        axis = utils.versor(axis)
482        a = np.cos(anglerad / 2)
483        b, c, d = -axis * np.sin(anglerad / 2)
484        aa, bb, cc, dd = a * a, b * b, c * c, d * d
485        bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
486        R = np.array(
487            [
488                [aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)],
489                [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)],
490                [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc],
491            ]
492        )
493        rv = np.dot(R, self.GetPosition() - np.asarray(point)) + point
494
495        if rad:
496            angle *= 180.0 / np.pi
497        # this vtk method only rotates in the origin of the object:
498        self.RotateWXYZ(angle, axis[0], axis[1], axis[2])
499        self.pos(rv)
500        return self

Rotate around an arbitrary axis passing through point.

Example:
from vedo import *
c1 = Cube()
c2 = c1.clone().c('violet').alpha(0.5) # copy of c1
v = vector(0.2,1,0)
p = vector(1,0,0)  # axis passes through this point
c2.rotate(90, axis=v, point=p)
l = Line(-v+p, v+p).lw(3).c('red')
show(c1, l, c2, axes=1).close()

def rotate_x(self, angle, rad=False, around=None):
532    def rotate_x(self, angle, rad=False, around=None):
533        """
534        Rotate around x-axis. If angle is in radians set `rad=True`.
535
536        Use `around` to define a pivoting point.
537        """
538        return self._rotatexyz("x", angle, rad, around)

Rotate around x-axis. If angle is in radians set rad=True.

Use around to define a pivoting point.

def rotate_y(self, angle, rad=False, around=None):
540    def rotate_y(self, angle, rad=False, around=None):
541        """
542        Rotate around y-axis. If angle is in radians set `rad=True`.
543
544        Use `around` to define a pivoting point.
545        """
546        return self._rotatexyz("y", angle, rad, around)

Rotate around y-axis. If angle is in radians set rad=True.

Use around to define a pivoting point.

def rotate_z(self, angle, rad=False, around=None):
548    def rotate_z(self, angle, rad=False, around=None):
549        """
550        Rotate around z-axis. If angle is in radians set `rad=True`.
551
552        Use `around` to define a pivoting point.
553        """
554        return self._rotatexyz("z", angle, rad, around)

Rotate around z-axis. If angle is in radians set rad=True.

Use around to define a pivoting point.

def orientation( self, newaxis=None, rotation=0, concatenate=False, xyplane=False, rad=False):
556    def orientation(self, newaxis=None, rotation=0, concatenate=False, xyplane=False, rad=False):
557        """
558        Set/Get object orientation.
559
560        Arguments:
561            rotation : (float)
562                rotate object around newaxis.
563            concatenate : (bool)
564                concatenate the orientation operation with the previous existing transform (if any)
565            xyplane : (bool)
566                make an extra rotation to keep the object aligned to the xy-plane
567            rad : (bool)
568                set to True if angle is expressed in radians.
569
570        Example:
571            ```python
572            from vedo import *
573            center = np.array([581/2,723/2,0])
574            objs = []
575            for a in np.linspace(0, 6.28, 7):
576                v = vector(cos(a), sin(a), 0)*1000
577                pic = Picture(dataurl+"images/dog.jpg").rotate_z(10)
578                pic.orientation(v, xyplane=True)
579                pic.origin(center)
580                pic.pos(v - center)
581                objs += [pic, Arrow(v, v+v)]
582            show(objs, Point(), axes=1).close()
583            ```
584            ![](https://vedo.embl.es/images/feats/orientation.png)
585
586        Examples:
587            - [gyroscope2.py](https://github.com/marcomusy/vedo/tree/master/examples/simulations/gyroscope2.py)
588
589            ![](https://vedo.embl.es/images/simulations/50738942-687b5780-11d9-11e9-97f0-72bbd63f7d6e.gif)
590        """
591        if self.top is None or self.base is None:
592            initaxis = (0, 0, 1)
593        else:
594            initaxis = utils.versor(self.top - self.base)
595
596        newaxis = utils.versor(newaxis)
597        p = np.array(self.GetPosition())
598        crossvec = np.cross(initaxis, newaxis)
599
600        angleth = np.arccos(np.dot(initaxis, newaxis))
601
602        T = vtk.vtkTransform()
603        if concatenate:
604            try:
605                M = self.GetMatrix()
606                T.SetMatrix(M)
607            except:
608                pass
609        T.PostMultiply()
610        T.Translate(-p)
611        if rotation:
612            if rad:
613                rotation *= 180.0 / np.pi
614            T.RotateWXYZ(rotation, initaxis)
615        if xyplane:
616            angleph = np.arctan2(newaxis[1], newaxis[0])
617            T.RotateWXYZ(np.rad2deg(angleph + angleth), initaxis)  # compensation
618        T.RotateWXYZ(np.rad2deg(angleth), crossvec)
619        T.Translate(p)
620
621        self.SetOrientation(T.GetOrientation())
622
623        self.point_locator = None
624        self.cell_locator = None
625        return self
626
627        # newaxis = utils.versor(newaxis)
628        # pos = np.array(self.GetPosition())
629        # crossvec = np.cross(initaxis, newaxis)
630        # angle = np.arccos(np.dot(initaxis, newaxis))
631        # T = vtk.vtkTransform()
632        # T.PostMultiply()
633        # T.Translate(-pos)
634        # if rotation:
635        #     T.RotateWXYZ(rotation, initaxis)
636        # T.RotateWXYZ(np.rad2deg(angle), crossvec)
637        # T.Translate(pos)
638        # self.SetUserTransform(T)
639        # self.transform = T

Set/Get object orientation.

Arguments:
  • rotation : (float) rotate object around newaxis.
  • concatenate : (bool) concatenate the orientation operation with the previous existing transform (if any)
  • xyplane : (bool) make an extra rotation to keep the object aligned to the xy-plane
  • rad : (bool) set to True if angle is expressed in radians.
Example:
from vedo import *
center = np.array([581/2,723/2,0])
objs = []
for a in np.linspace(0, 6.28, 7):
    v = vector(cos(a), sin(a), 0)*1000
    pic = Picture(dataurl+"images/dog.jpg").rotate_z(10)
    pic.orientation(v, xyplane=True)
    pic.origin(center)
    pic.pos(v - center)
    objs += [pic, Arrow(v, v+v)]
show(objs, Point(), axes=1).close()

Examples:

def scale(self, s=None, reset=False):
641    def scale(self, s=None, reset=False):
642        """
643        Set/get object's scaling factor.
644
645        Arguments:
646            s : (list, float)
647                scaling factor(s).
648            reset : (bool)
649                if True previous scaling factors are ignored.
650
651        Note:
652            use `s=(sx,sy,sz)` to scale differently in the three coordinates.
653        """
654        if s is None:
655            return np.array(self.GetScale())
656
657        # assert s[0] != 0
658        # assert s[1] != 0
659        # assert s[2] != 0
660
661        if reset:
662            self.SetScale(s)
663        else:
664            self.SetScale(np.multiply(self.GetScale(), s))
665
666        self.point_locator = None
667        self.cell_locator = None
668        return self

Set/get object's scaling factor.

Arguments:
  • s : (list, float) scaling factor(s).
  • reset : (bool) if True previous scaling factors are ignored.
Note:

use s=(sx,sy,sz) to scale differently in the three coordinates.

def get_transform(self, invert=False):
670    def get_transform(self, invert=False):
671        """
672        Check if `object.transform` exists and returns a `vtkTransform`.
673        Otherwise return current user transformation (where the object is currently placed).
674
675        Use `invert` to return the inverse of the current transformation
676
677        Example:
678            ```python
679            from vedo import *
680
681            c1 = Cube()
682            c2 = c1.clone().c('violet').alpha(0.5) # copy of c1
683            v = vector(0.2,1,0)
684            p = vector(1,0,0)  # axis passes through this point
685            c2.rotate(90, axis=v, point=p)
686
687            # get the inverse of the current transformation
688            T = c2.get_transform(invert=True)
689            c2.apply_transform(T)  # put back c2 in place
690
691            l = Line(p-v, p+v).lw(3).c('red')
692            show(c1.wireframe().lw(3), l, c2, axes=1).close()
693            ```
694            ![](https://vedo.embl.es/images/feats/get_transf.png)
695        """
696        if self.transform:
697            tr = self.transform
698            if invert:
699                tr = tr.GetInverse()
700            return tr
701
702        T = self.GetMatrix()
703        tr = vtk.vtkTransform()
704        tr.SetMatrix(T)
705        if invert:
706            tr = tr.GetInverse()
707        return tr

Check if object.transform exists and returns a vtkTransform. Otherwise return current user transformation (where the object is currently placed).

Use invert to return the inverse of the current transformation

Example:
from vedo import *

c1 = Cube()
c2 = c1.clone().c('violet').alpha(0.5) # copy of c1
v = vector(0.2,1,0)
p = vector(1,0,0)  # axis passes through this point
c2.rotate(90, axis=v, point=p)

# get the inverse of the current transformation
T = c2.get_transform(invert=True)
c2.apply_transform(T)  # put back c2 in place

l = Line(p-v, p+v).lw(3).c('red')
show(c1.wireframe().lw(3), l, c2, axes=1).close()

def apply_transform(self, T, reset=False, concatenate=False):
709    def apply_transform(self, T, reset=False, concatenate=False):
710        """
711        Transform object position and orientation.
712
713        Arguments:
714            reset : (bool)
715                no effect, this is superseded by `pointcloud.apply_transform()`
716            concatenate : (bool)
717                no effect, this is superseded by `pointcloud.apply_transform()`
718        """
719        if isinstance(T, vtk.vtkMatrix4x4):
720            self.SetUserMatrix(T)
721        elif utils.is_sequence(T):
722            vm = vtk.vtkMatrix4x4()
723            for i in [0, 1, 2, 3]:
724                for j in [0, 1, 2, 3]:
725                    vm.SetElement(i, j, T[i][j])
726            self.SetUserMatrix(vm)
727        else:
728            self.SetUserTransform(T)
729        self.transform = T
730
731        self.point_locator = None
732        self.cell_locator = None
733        return self

Transform object position and orientation.

Arguments:
  • reset : (bool) no effect, this is superseded by pointcloud.apply_transform()
  • concatenate : (bool) no effect, this is superseded by pointcloud.apply_transform()
def align_to_bounding_box(self, msh, rigid=False):
735    def align_to_bounding_box(self, msh, rigid=False):
736        """
737        Align the current object's bounding box to the bounding box
738        of the input object.
739
740        Use `rigid` to disable scaling.
741
742        Examples:
743            - [align6.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/align6.py)
744        """
745        lmt = vtk.vtkLandmarkTransform()
746        ss = vtk.vtkPoints()
747        xss0, xss1, yss0, yss1, zss0, zss1 = self.bounds()
748        for p in [
749            [xss0, yss0, zss0],
750            [xss1, yss0, zss0],
751            [xss1, yss1, zss0],
752            [xss0, yss1, zss0],
753            [xss0, yss0, zss1],
754            [xss1, yss0, zss1],
755            [xss1, yss1, zss1],
756            [xss0, yss1, zss1],
757        ]:
758            ss.InsertNextPoint(p)
759        st = vtk.vtkPoints()
760        xst0, xst1, yst0, yst1, zst0, zst1 = msh.bounds()
761        for p in [
762            [xst0, yst0, zst0],
763            [xst1, yst0, zst0],
764            [xst1, yst1, zst0],
765            [xst0, yst1, zst0],
766            [xst0, yst0, zst1],
767            [xst1, yst0, zst1],
768            [xst1, yst1, zst1],
769            [xst0, yst1, zst1],
770        ]:
771            st.InsertNextPoint(p)
772
773        lmt.SetSourceLandmarks(ss)
774        lmt.SetTargetLandmarks(st)
775        lmt.SetModeToAffine()
776        if rigid:
777            lmt.SetModeToRigidBody()
778        lmt.Update()
779        self.apply_transform(lmt)
780        self.transform = lmt
781
782        self.point_locator = None
783        self.cell_locator = None
784        return self

Align the current object's bounding box to the bounding box of the input object.

Use rigid to disable scaling.

Examples:
def on(self):
786    def on(self):
787        """Switch on  object visibility. Object is not removed."""
788        self.VisibilityOn()
789        try:
790            self.scalarbar.VisibilityOn()
791        except AttributeError:
792            pass
793        try:
794            self.trail.VisibilityOn()
795        except AttributeError:
796            pass
797        try:
798            for sh in self.shadows:
799                sh.VisibilityOn()
800        except AttributeError:
801            pass
802        return self

Switch on object visibility. Object is not removed.

def off(self):
804    def off(self):
805        """Switch off object visibility. Object is not removed."""
806        self.VisibilityOff()
807        try:
808            self.scalarbar.VisibilityOff()
809        except AttributeError:
810            pass
811        try:
812            self.trail.VisibilityOff()
813        except AttributeError:
814            pass
815        try:
816            for sh in self.shadows:
817                sh.VisibilityOff()
818        except AttributeError:
819            pass
820        return self

Switch off object visibility. Object is not removed.

def toggle(self):
822    def toggle(self):
823        """Toggle object visibility on/off."""
824        v = self.GetVisibility()
825        if v:
826            self.off()
827        else:
828            self.on()
829        return self

Toggle object visibility on/off.

def box(self, scale=1, padding=0, fill=False):
831    def box(self, scale=1, padding=0, fill=False):
832        """
833        Return the bounding box as a new `Mesh`.
834
835        Arguments:
836            scale : (float)
837                box size can be scaled by a factor
838            padding : (float, list)
839                a constant padding can be added (can be a list [padx,pady,padz])
840
841        Examples:
842            - [latex.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/latex.py)
843        """
844        b = self.bounds()
845        if not utils.is_sequence(padding):
846            padding = [padding, padding, padding]
847        length, width, height = b[1] - b[0], b[3] - b[2], b[5] - b[4]
848        tol = (length + width + height) / 30000  # useful for boxing 2D text
849        pos = [(b[0] + b[1]) / 2, (b[3] + b[2]) / 2, (b[5] + b[4]) / 2 - tol]
850        bx = vedo.shapes.Box(
851            pos,
852            length * scale + padding[0],
853            width * scale + padding[1],
854            height * scale + padding[2],
855            c="gray",
856        )
857        if hasattr(self, "GetProperty"):  # could be Assembly
858            if isinstance(self.GetProperty(), vtk.vtkProperty):  # could be volume
859                pr = vtk.vtkProperty()
860                pr.DeepCopy(self.GetProperty())
861                bx.SetProperty(pr)
862                bx.property = pr
863        bx.wireframe(not fill)
864        bx.flat().lighting("off")
865        return bx

Return the bounding box as a new Mesh.

Arguments:
  • scale : (float) box size can be scaled by a factor
  • padding : (float, list) a constant padding can be added (can be a list [padx,pady,padz])
Examples:
def use_bounds(self, ub=True):
867    def use_bounds(self, ub=True):
868        """
869        Instruct the current camera to either take into account or ignore
870        the object bounds when resetting.
871        """
872        self.SetUseBounds(ub)
873        return self

Instruct the current camera to either take into account or ignore the object bounds when resetting.

def bounds(self):
875    def bounds(self):
876        """
877        Get the object bounds.
878        Returns a list in format `[xmin,xmax, ymin,ymax, zmin,zmax]`.
879        """
880        try:
881            pts = self.points()
882            xmin, ymin, zmin = np.min(pts, axis=0)
883            xmax, ymax, zmax = np.max(pts, axis=0)
884            return [xmin, xmax, ymin, ymax, zmin, zmax]
885        except (AttributeError, ValueError):
886            return self.GetBounds()

Get the object bounds. Returns a list in format [xmin,xmax, ymin,ymax, zmin,zmax].

def xbounds(self, i=None):
888    def xbounds(self, i=None):
889        """Get the bounds `[xmin,xmax]`. Can specify upper or lower with i (0,1)."""
890        b = self.bounds()
891        if i is not None:
892            return b[i]
893        return (b[0], b[1])

Get the bounds [xmin,xmax]. Can specify upper or lower with i (0,1).

def ybounds(self, i=None):
895    def ybounds(self, i=None):
896        """Get the bounds `[ymin,ymax]`. Can specify upper or lower with i (0,1)."""
897        b = self.bounds()
898        if i == 0:
899            return b[2]
900        if i == 1:
901            return b[3]
902        return (b[2], b[3])

Get the bounds [ymin,ymax]. Can specify upper or lower with i (0,1).

def zbounds(self, i=None):
904    def zbounds(self, i=None):
905        """Get the bounds `[zmin,zmax]`. Can specify upper or lower with i (0,1)."""
906        b = self.bounds()
907        if i == 0:
908            return b[4]
909        if i == 1:
910            return b[5]
911        return (b[4], b[5])

Get the bounds [zmin,zmax]. Can specify upper or lower with i (0,1).

def diagonal_size(self):
914    def diagonal_size(self):
915        """Get the length of the diagonal of mesh bounding box."""
916        b = self.bounds()
917        return np.sqrt((b[1] - b[0]) ** 2 + (b[3] - b[2]) ** 2 + (b[5] - b[4]) ** 2)
918        # return self.GetLength() # ???different???

Get the length of the diagonal of mesh bounding box.

def copy_data_from(self, obj):
921    def copy_data_from(self, obj):
922        """Copy all data (point and cell data) from this input object"""
923        self.inputdata().GetPointData().PassData(obj.inputdata().GetPointData())
924        self.inputdata().GetCellData().PassData(obj.inputdata().GetCellData())
925        self.pipeline = utils.OperationNode(
926            f"copy_data_from\n{obj.__class__.__name__}",
927            parents=[self, obj],
928            shape="note",
929            c="#ccc5b9",
930        )
931        return self

Copy all data (point and cell data) from this input object

def print(self):
933    def print(self):
934        """Print information about an object."""
935        utils.print_info(self)
936        return self

Print information about an object.

def show(self, **options):
938    def show(self, **options):
939        """
940        Create on the fly an instance of class `Plotter` or use the last existing one to
941        show one single object.
942
943        This method is meant as a shortcut. If more than one object needs to be visualised
944        please use the syntax `show(mesh1, mesh2, volume, ..., options)`.
945
946        Returns the `Plotter` class instance.
947        """
948        return vedo.plotter.show(self, **options)

Create on the fly an instance of class Plotter or use the last existing one to show one single object.

This method is meant as a shortcut. If more than one object needs to be visualised please use the syntax show(mesh1, mesh2, volume, ..., options).

Returns the Plotter class instance.

def thumbnail( self, zoom=1.25, size=(200, 200), bg='white', azimuth=0, elevation=0, axes=False):
950    def thumbnail(self, zoom=1.25, size=(200, 200), bg="white", azimuth=0, elevation=0, axes=False):
951        """Build a thumbnail of the object and return it as an array."""
952        # speed is about 20Hz for size=[200,200]
953        ren = vtk.vtkRenderer()
954        ren.AddActor(self)
955        if axes:
956            axes = vedo.addons.Axes(self)
957            ren.AddActor(axes)
958        ren.ResetCamera()
959        cam = ren.GetActiveCamera()
960        cam.Zoom(zoom)
961        cam.Elevation(elevation)
962        cam.Azimuth(azimuth)
963
964        ren_win = vtk.vtkRenderWindow()
965        ren_win.SetOffScreenRendering(True)
966        ren_win.SetSize(size)
967        ren.SetBackground(colors.get_color(bg))
968        ren_win.AddRenderer(ren)
969        ren_win.Render()
970
971        nx, ny = ren_win.GetSize()
972        arr = vtk.vtkUnsignedCharArray()
973        ren_win.GetRGBACharPixelData(0, 0, nx - 1, ny - 1, 0, arr)
974        narr = utils.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3])
975        narr = np.ascontiguousarray(np.flip(narr, axis=0))
976
977        ren.RemoveActor(self)
978        if axes:
979            ren.RemoveActor(axes)
980        ren_win.Finalize()
981        del ren_win
982        return narr

Build a thumbnail of the object and return it as an array.

class BaseActor(Base3DProp):
1083class BaseActor(Base3DProp):
1084    """
1085    Base class.
1086
1087    .. warning:: Do not use this class to instantiate objects, use one the above instead.
1088    """
1089
1090    def __init__(self):
1091        """
1092        Base class to add operative and data
1093        functionality to `Mesh`, `Assembly`, `Volume` and `Picture` objects.
1094        """
1095
1096        super().__init__()
1097
1098        self._mapper = None
1099        self._caption = None
1100        self.property = None
1101
1102    def mapper(self, new_mapper=None):
1103        """Return the `vtkMapper` data object, or update it with a new one."""
1104        if new_mapper:
1105            self.SetMapper(new_mapper)
1106            if self._mapper:
1107                iptdata = self._mapper.GetInput()
1108                if iptdata:
1109                    new_mapper.SetInputData(self._mapper.GetInput())
1110            self._mapper = new_mapper
1111            self._mapper.Modified()
1112        return self._mapper
1113
1114    def inputdata(self):
1115        """Return the VTK input data object."""
1116        if self._mapper:
1117            return self._mapper.GetInput()
1118        return self.GetMapper().GetInput()
1119
1120    def modified(self):
1121        """Use in conjunction with `tonumpy()`
1122        to update any modifications to the volume array"""
1123        sc = self.inputdata().GetPointData().GetScalars()
1124        if sc:
1125            sc.Modified()
1126        self.inputdata().GetPointData().Modified()
1127        return self
1128
1129    @property
1130    def npoints(self):
1131        """Retrieve the number of points."""
1132        return self.inputdata().GetNumberOfPoints()
1133
1134    @property
1135    def ncells(self):
1136        """Retrieve the number of cells."""
1137        return self.inputdata().GetNumberOfCells()
1138
1139    def points(self, pts=None, transformed=True):
1140        """
1141        Set/Get the vertex coordinates of a mesh or point cloud.
1142        Argument can be an index, a set of indices
1143        or a complete new set of points to update the mesh.
1144
1145        Set `transformed=False` to ignore any previous transformation applied to the mesh.
1146        """
1147        if pts is None:  ### getter
1148
1149            if isinstance(self, vedo.Points):
1150                vpts = self.polydata(transformed).GetPoints()
1151            elif isinstance(self, vedo.BaseVolume):
1152                v2p = vtk.vtkImageToPoints()
1153                v2p.SetInputData(self.imagedata())
1154                v2p.Update()
1155                vpts = v2p.GetOutput().GetPoints()
1156            else:  # tetmesh et al
1157                vpts = self.inputdata().GetPoints()
1158
1159            if vpts:
1160                return utils.vtk2numpy(vpts.GetData())
1161            return np.array([], dtype=np.float32)
1162
1163        else:  ### setter
1164
1165            if len(pts) == 3 and len(pts[0]) != 3:
1166                # assume plist is in the format [all_x, all_y, all_z]
1167                pts = np.stack((pts[0], pts[1], pts[2]), axis=1)
1168            pts = np.asarray(pts, dtype=np.float32)
1169            if pts.shape[1] == 2:
1170                pts = np.c_[pts, np.zeros(pts.shape[0], dtype=np.float32)]
1171            vpts = self.inputdata().GetPoints()
1172            arr = utils.numpy2vtk(pts, dtype=np.float32)
1173            vpts.SetData(arr)
1174            vpts.Modified()
1175            # reset mesh to identity matrix position/rotation:
1176            self.PokeMatrix(vtk.vtkMatrix4x4())
1177            self.point_locator = None
1178            self.cell_locator = None
1179            return self
1180
1181
1182    def cell_centers(self):
1183        """
1184        Get the coordinates of the cell centers.
1185
1186        Examples:
1187            - [delaunay2d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/delaunay2d.py)
1188        """
1189        vcen = vtk.vtkCellCenters()
1190        if hasattr(self, "polydata"):
1191            vcen.SetInputData(self.polydata())
1192        else:
1193            vcen.SetInputData(self.inputdata())
1194        vcen.Update()
1195        return utils.vtk2numpy(vcen.GetOutput().GetPoints().GetData())
1196
1197    def delete_cells(self, ids):
1198        """
1199        Remove cells from the mesh object by their ID.
1200        Points (vertices) are not removed (you may use `.clean()` to remove those).
1201        """
1202        data = self.inputdata()
1203        data.BuildLinks()
1204        for cid in ids:
1205            data.DeleteCell(cid)
1206        data.RemoveDeletedCells()
1207        data.Modified()
1208        self._mapper.Modified()
1209        self.pipeline = utils.OperationNode(
1210            "delete_cells", parents=[self], comment=f"#cells {self._data.GetNumberOfCells()}"
1211        )
1212        return self
1213
1214    def mark_boundaries(self):
1215        """
1216        Mark cells and vertices of the mesh if they lie on a boundary.
1217        A new array called `BoundaryCells` is added to the mesh.
1218        """
1219        mb = vtk.vtkMarkBoundaryFilter()
1220        mb.SetInputData(self._data)
1221        mb.Update()
1222        out = self._update(mb.GetOutput())
1223        out.pipeline = utils.OperationNode("mark_boundaries", parents=[self])
1224        return out
1225
1226    def find_cells_in(self, xbounds=(), ybounds=(), zbounds=()):
1227        """
1228        Find cells that are within the specified bounds.
1229        Setting a color will add a vtk array to colorize these cells.
1230        """
1231        if len(xbounds) == 6:
1232            bnds = xbounds
1233        else:
1234            bnds = list(self.bounds())
1235            if len(xbounds) == 2:
1236                bnds[0] = xbounds[0]
1237                bnds[1] = xbounds[1]
1238            if len(ybounds) == 2:
1239                bnds[2] = ybounds[0]
1240                bnds[3] = ybounds[1]
1241            if len(zbounds) == 2:
1242                bnds[4] = zbounds[0]
1243                bnds[5] = zbounds[1]
1244
1245        cellIds = vtk.vtkIdList()
1246        self.cell_locator = vtk.vtkCellTreeLocator()
1247        self.cell_locator.SetDataSet(self.polydata())
1248        self.cell_locator.BuildLocator()
1249        self.cell_locator.FindCellsWithinBounds(bnds, cellIds)
1250
1251        cids = []
1252        for i in range(cellIds.GetNumberOfIds()):
1253            cid = cellIds.GetId(i)
1254            cids.append(cid)
1255
1256        return np.array(cids)
1257
1258    def count_vertices(self):
1259        """Count the number of vertices each cell has and return it as a numpy array"""
1260        vc = vtk.vtkCountVertices()
1261        vc.SetInputData(self._data)
1262        vc.SetOutputArrayName("VertexCount")
1263        vc.Update()
1264        varr = vc.GetOutput().GetCellData().GetArray("VertexCount")
1265        return utils.vtk2numpy(varr)
1266
1267    def lighting(
1268        self,
1269        style="",
1270        ambient=None,
1271        diffuse=None,
1272        specular=None,
1273        specular_power=None,
1274        specular_color=None,
1275        metallicity=None,
1276        roughness=None,
1277    ):
1278        """
1279        Set the ambient, diffuse, specular and specular_power lighting constants.
1280
1281        Arguments:
1282            style : (str)
1283                preset style, options are `[metallic, plastic, shiny, glossy, ambient, off]`
1284            ambient : (float)
1285                ambient fraction of emission [0-1]
1286            diffuse : (float)
1287                emission of diffused light in fraction [0-1]
1288            specular : (float)
1289                fraction of reflected light [0-1]
1290            specular_power : (float)
1291                precision of reflection [1-100]
1292            specular_color : (color)
1293                color that is being reflected by the surface
1294
1295        <img src="https://upload.wikimedia.org/wikipedia/commons/6/6b/Phong_components_version_4.png" alt="", width=700px>
1296
1297        Examples:
1298            - [specular.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/specular.py)
1299        """
1300        pr = self.GetProperty()
1301
1302        if style:
1303
1304            if isinstance(pr, vtk.vtkVolumeProperty):
1305                self.shade(True)
1306                if style == "off":
1307                    self.shade(False)
1308                elif style == "ambient":
1309                    style = "default"
1310                    self.shade(False)
1311            else:
1312                if style != "off":
1313                    pr.LightingOn()
1314
1315            if style == "off":
1316                pr.SetInterpolationToFlat()
1317                pr.LightingOff()
1318                return self  ##############
1319
1320            if hasattr(pr, "GetColor"):  # could be Volume
1321                c = pr.GetColor()
1322            else:
1323                c = (1, 1, 0.99)
1324            mpr = self._mapper
1325            if hasattr(mpr, 'GetScalarVisibility') and mpr.GetScalarVisibility():
1326                c = (1,1,0.99)
1327            if   style=='metallic': pars = [0.1, 0.3, 1.0, 10, c]
1328            elif style=='plastic' : pars = [0.3, 0.4, 0.3,  5, c]
1329            elif style=='shiny'   : pars = [0.2, 0.6, 0.8, 50, c]
1330            elif style=='glossy'  : pars = [0.1, 0.7, 0.9, 90, (1,1,0.99)]
1331            elif style=='ambient' : pars = [0.8, 0.1, 0.0,  1, (1,1,1)]
1332            elif style=='default' : pars = [0.1, 1.0, 0.05, 5, c]
1333            else:
1334                vedo.logger.error("in lighting(): Available styles are")
1335                vedo.logger.error("[default, metallic, plastic, shiny, glossy, ambient, off]")
1336                raise RuntimeError()
1337            pr.SetAmbient(pars[0])
1338            pr.SetDiffuse(pars[1])
1339            pr.SetSpecular(pars[2])
1340            pr.SetSpecularPower(pars[3])
1341            if hasattr(pr, "GetColor"):
1342                pr.SetSpecularColor(pars[4])
1343
1344        if ambient is not None: pr.SetAmbient(ambient)
1345        if diffuse is not None: pr.SetDiffuse(diffuse)
1346        if specular is not None: pr.SetSpecular(specular)
1347        if specular_power is not None: pr.SetSpecularPower(specular_power)
1348        if specular_color is not None: pr.SetSpecularColor(colors.get_color(specular_color))
1349        if utils.vtk_version_at_least(9):
1350            if metallicity is not None:
1351                pr.SetInterpolationToPBR()
1352                pr.SetMetallic(metallicity)
1353            if roughness is not None:
1354                pr.SetInterpolationToPBR()
1355                pr.SetRoughness(roughness)
1356
1357        return self
1358
1359    def print_histogram(
1360        self,
1361        bins=10,
1362        height=10,
1363        logscale=False,
1364        minbin=0,
1365        horizontal=False,
1366        char="\U00002589",
1367        c=None,
1368        bold=True,
1369        title="Histogram",
1370    ):
1371        """
1372        Ascii histogram printing on terminal.
1373        Input can be `Volume` or `Mesh` (will grab the active point array).
1374
1375        Arguments:
1376            bins : (int)
1377                number of histogram bins
1378            height : (int)
1379                height of the histogram in character units
1380            logscale : (bool)
1381                use logscale for frequencies
1382            minbin : (int)
1383                ignore bins before minbin
1384            horizontal : (bool)
1385                show histogram horizontally
1386            char : (str)
1387                character to be used as marker
1388            c : (color)
1389                ascii color
1390            bold : (bool)
1391                use boldface
1392            title : (str)
1393                histogram title
1394
1395        ![](https://vedo.embl.es/images/feats/histoprint.png)
1396        """
1397        utils.print_histogram(
1398            self, bins, height, logscale, minbin, horizontal, char, c, bold, title
1399        )
1400        return self
1401
1402    def c(self, color=False, alpha=None):
1403        """
1404        Shortcut for `color()`.
1405        If None is passed as input, will use colors from current active scalars.
1406        """
1407        return self.color(color, alpha)
1408
1409    @property
1410    def pointdata(self):
1411        """
1412        Create and/or return a `numpy.array` associated to points (vertices).
1413        A data array can be indexed either as a string or by an integer number.
1414        E.g.:  `myobj.pointdata["arrayname"]`
1415
1416        Usage:
1417
1418            `myobj.pointdata.keys()` to return the available data array names
1419
1420            `myobj.pointdata.select(name)` to make this array the active one
1421
1422            `myobj.pointdata.remove(name)` to remove this array
1423        """
1424        return _DataArrayHelper(self, 0)
1425
1426    @property
1427    def celldata(self):
1428        """
1429        Create and/or return a `numpy.array` associated to cells (faces).
1430        A data array can be indexed either as a string or by an integer number.
1431        E.g.:  `myobj.celldata["arrayname"]`
1432
1433        Usage:
1434
1435            `myobj.celldata.keys()` to return the available data array names
1436
1437            `myobj.celldata.select(name)` to make this array the active one
1438
1439            `myobj.celldata.remove(name)` to remove this array
1440        """
1441        return _DataArrayHelper(self, 1)
1442
1443    @property
1444    def metadata(self):
1445        """
1446        Create and/or return a `numpy.array` associated to neither cells nor faces.
1447        A data array can be indexed either as a string or by an integer number.
1448        E.g.:  `myobj.metadata["arrayname"]`
1449
1450        Usage:
1451
1452            `myobj.metadata.keys()` to return the available data array names
1453
1454            `myobj.metadata.select(name)` to make this array the active one
1455
1456            `myobj.metadata.remove(name)` to remove this array
1457        """
1458        return _DataArrayHelper(self, 2)
1459
1460    def map_cells_to_points(self, arrays=(), move=False):
1461        """
1462        Interpolate cell data (i.e., data specified per cell or face)
1463        into point data (i.e., data specified at each vertex).
1464        The method of transformation is based on averaging the data values
1465        of all cells using a particular point.
1466
1467        A custom list of arrays to be mapped can be passed in input.
1468
1469        Set `move=True` to delete the original `celldata` array.
1470        """
1471        c2p = vtk.vtkCellDataToPointData()
1472        c2p.SetInputData(self.inputdata())
1473        if not move:
1474            c2p.PassCellDataOn()
1475        if arrays:
1476            c2p.ClearCellDataArrays()
1477            c2p.ProcessAllArraysOff()
1478            for arr in arrays:
1479                c2p.AddCellDataArray(arr)
1480        else:
1481            c2p.ProcessAllArraysOn()
1482        c2p.Update()
1483        self._mapper.SetScalarModeToUsePointData()
1484        out = self._update(c2p.GetOutput())
1485        out.pipeline = utils.OperationNode("map cell\nto point data", parents=[self])
1486        return out
1487
1488    def map_points_to_cells(self, arrays=(), move=False):
1489        """
1490        Interpolate point data (i.e., data specified per point or vertex)
1491        into cell data (i.e., data specified per cell).
1492        The method of transformation is based on averaging the data values
1493        of all points defining a particular cell.
1494
1495        A custom list of arrays to be mapped can be passed in input.
1496
1497        Set `move=True` to delete the original `pointdata` array.
1498
1499        Examples:
1500            - [mesh_map2cell.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_map2cell.py)
1501        """
1502        p2c = vtk.vtkPointDataToCellData()
1503        p2c.SetInputData(self.inputdata())
1504        if not move:
1505            p2c.PassPointDataOn()
1506        if arrays:
1507            p2c.ClearPointDataArrays()
1508            p2c.ProcessAllArraysOff()
1509            for arr in arrays:
1510                p2c.AddPointDataArray(arr)
1511        else:
1512            p2c.ProcessAllArraysOn()
1513        p2c.Update()
1514        self._mapper.SetScalarModeToUseCellData()
1515        out = self._update(p2c.GetOutput())
1516        out.pipeline = utils.OperationNode("map point\nto cell data", parents=[self])
1517        return out
1518
1519    def resample_data_from(self, source, tol=None, categorical=False):
1520        """
1521        Resample point and cell data from another dataset.
1522        The output has the same structure but its point data have
1523        the resampled values from target.
1524
1525        Use `tol` to set the tolerance used to compute whether
1526        a point in the source is in a cell of the current object.
1527        Points without resampled values, and their cells, are marked as blank.
1528        If the data is categorical, then the resulting data will be determined
1529        by a nearest neighbor interpolation scheme.
1530
1531        Example:
1532        ```python
1533        from vedo import *
1534        m1 = Mesh(dataurl+'bunny.obj')#.add_gaussian_noise(0.1)
1535        pts = m1.points()
1536        ces = m1.cell_centers()
1537        m1.pointdata["xvalues"] = np.power(pts[:,0], 3)
1538        m1.celldata["yvalues"]  = np.power(ces[:,1], 3)
1539        m2 = Mesh(dataurl+'bunny.obj')
1540        m2.resample_arrays_from(m1)
1541        # print(m2.pointdata["xvalues"])
1542        show(m1, m2 , N=2, axes=1)
1543        ```
1544        """
1545        rs = vtk.vtkResampleWithDataSet()
1546        rs.SetInputData(self.inputdata())
1547        rs.SetSourceData(source.inputdata())
1548
1549        rs.SetPassPointArrays(True)
1550        rs.SetPassCellArrays(True)
1551        rs.SetPassFieldArrays(True)
1552        rs.SetCategoricalData(categorical)
1553
1554        rs.SetComputeTolerance(True)
1555        if tol:
1556            rs.SetComputeTolerance(False)
1557            rs.SetTolerance(tol)
1558        rs.Update()
1559        self._update(rs.GetOutput())
1560        self.pipeline = utils.OperationNode(
1561            f"resample_data_from\n{source.__class__.__name__}", parents=[self, source]
1562        )
1563        return self
1564
1565    def add_ids(self):
1566        """Generate point and cell ids arrays."""
1567        ids = vtk.vtkIdFilter()
1568        ids.SetInputData(self.inputdata())
1569        ids.PointIdsOn()
1570        ids.CellIdsOn()
1571        ids.FieldDataOff()
1572        ids.SetPointIdsArrayName("PointID")
1573        ids.SetCellIdsArrayName("CellID")
1574        ids.Update()
1575        self._update(ids.GetOutput())
1576        self.pipeline = utils.OperationNode("add_ids", parents=[self])
1577        return self
1578
1579    def gradient(self, input_array=None, on="points", fast=False):
1580        """
1581        Compute and return the gradiend of the active scalar field as a numpy array.
1582
1583        Arguments:
1584            input_array : (str)
1585                array of the scalars to compute the gradient,
1586                if None the current active array is selected
1587            on : (str)
1588                compute either on 'points' or 'cells' data
1589            fast : (bool)
1590                if True, will use a less accurate algorithm
1591                that performs fewer derivative calculations (and is therefore faster).
1592
1593        Examples:
1594            - [isolines.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/isolines.py)
1595
1596            ![](https://user-images.githubusercontent.com/32848391/72433087-f00a8780-3798-11ea-9778-991f0abeca70.png)
1597        """
1598        gra = vtk.vtkGradientFilter()
1599        if on.startswith("p"):
1600            varr = self.inputdata().GetPointData()
1601            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS
1602        else:
1603            varr = self.inputdata().GetCellData()
1604            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS
1605
1606        if input_array is None:
1607            if varr.GetScalars():
1608                input_array = varr.GetScalars().GetName()
1609            else:
1610                vedo.logger.error(f"in gradient: no scalars found for {on}")
1611                raise RuntimeError
1612
1613        gra.SetInputData(self.inputdata())
1614        gra.SetInputScalars(tp, input_array)
1615        gra.SetResultArrayName("Gradient")
1616        gra.SetFasterApproximation(fast)
1617        gra.ComputeDivergenceOff()
1618        gra.ComputeVorticityOff()
1619        gra.ComputeGradientOn()
1620        gra.Update()
1621        if on.startswith("p"):
1622            gvecs = utils.vtk2numpy(gra.GetOutput().GetPointData().GetArray("Gradient"))
1623        else:
1624            gvecs = utils.vtk2numpy(gra.GetOutput().GetCellData().GetArray("Gradient"))
1625        return gvecs
1626
1627    def divergence(self, array_name=None, on="points", fast=False):
1628        """
1629        Compute and return the divergence of a vector field as a numpy array.
1630
1631        Arguments:
1632            array_name : (str)
1633                name of the array of vectors to compute the divergence,
1634                if None the current active array is selected
1635            on : (str)
1636                compute either on 'points' or 'cells' data
1637            fast : (bool)
1638                if True, will use a less accurate algorithm
1639                that performs fewer derivative calculations (and is therefore faster).
1640        """
1641        div = vtk.vtkGradientFilter()
1642        if on.startswith("p"):
1643            varr = self.inputdata().GetPointData()
1644            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS
1645        else:
1646            varr = self.inputdata().GetCellData()
1647            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS
1648
1649        if array_name is None:
1650            if varr.GetVectors():
1651                array_name = varr.GetVectors().GetName()
1652            else:
1653                vedo.logger.error(f"in divergence(): no vectors found for {on}")
1654                raise RuntimeError
1655
1656        div.SetInputData(self.inputdata())
1657        div.SetInputScalars(tp, array_name)
1658        div.ComputeDivergenceOn()
1659        div.ComputeGradientOff()
1660        div.ComputeVorticityOff()
1661        div.SetDivergenceArrayName("Divergence")
1662        div.SetFasterApproximation(fast)
1663        div.Update()
1664        if on.startswith("p"):
1665            dvecs = utils.vtk2numpy(div.GetOutput().GetPointData().GetArray("Divergence"))
1666        else:
1667            dvecs = utils.vtk2numpy(div.GetOutput().GetCellData().GetArray("Divergence"))
1668        return dvecs
1669
1670    def vorticity(self, array_name=None, on="points", fast=False):
1671        """
1672        Compute and return the vorticity of a vector field as a numpy array.
1673
1674        Arguments:
1675            array_name : (str)
1676                name of the array to compute the vorticity,
1677                if None the current active array is selected
1678            on : (str)
1679                compute either on 'points' or 'cells' data
1680            fast : (bool)
1681                if True, will use a less accurate algorithm
1682                that performs fewer derivative calculations (and is therefore faster).
1683        """
1684        vort = vtk.vtkGradientFilter()
1685        if on.startswith("p"):
1686            varr = self.inputdata().GetPointData()
1687            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS
1688        else:
1689            varr = self.inputdata().GetCellData()
1690            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS
1691
1692        if array_name is None:
1693            if varr.GetVectors():
1694                array_name = varr.GetVectors().GetName()
1695            else:
1696                vedo.logger.error(f"in vorticity(): no vectors found for {on}")
1697                raise RuntimeError
1698
1699        vort.SetInputData(self.inputdata())
1700        vort.SetInputScalars(tp, array_name)
1701        vort.ComputeDivergenceOff()
1702        vort.ComputeGradientOff()
1703        vort.ComputeVorticityOn()
1704        vort.SetVorticityArrayName("Vorticity")
1705        vort.SetFasterApproximation(fast)
1706        vort.Update()
1707        if on.startswith("p"):
1708            vvecs = utils.vtk2numpy(vort.GetOutput().GetPointData().GetArray("Vorticity"))
1709        else:
1710            vvecs = utils.vtk2numpy(vort.GetOutput().GetCellData().GetArray("Vorticity"))
1711        return vvecs
1712
1713    def add_scalarbar(
1714        self,
1715        title="",
1716        pos=(0.8, 0.05),
1717        title_yoffset=15,
1718        font_size=12,
1719        size=(None, None),
1720        nlabels=None,
1721        c=None,
1722        horizontal=False,
1723        use_alpha=True,
1724        label_format=":6.3g",
1725    ):
1726        """
1727        Add a 2D scalar bar for the specified obj.
1728
1729        Examples:
1730            - [mesh_coloring.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_coloring.py)
1731            - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py)
1732        """
1733        plt = vedo.plotter_instance
1734
1735        if plt and plt.renderer:
1736            c = (0.9, 0.9, 0.9)
1737            if np.sum(plt.renderer.GetBackground()) > 1.5:
1738                c = (0.1, 0.1, 0.1)
1739            if isinstance(self.scalarbar, vtk.vtkActor):
1740                plt.renderer.RemoveActor(self.scalarbar)
1741            elif isinstance(self.scalarbar, vedo.Assembly):
1742                for a in self.scalarbar.unpack():
1743                    plt.renderer.RemoveActor(a)
1744        if c is None:
1745            c = "gray"
1746
1747        sb = vedo.addons.ScalarBar(
1748            self,
1749            title,
1750            pos,
1751            title_yoffset,
1752            font_size,
1753            size,
1754            nlabels,
1755            c,
1756            horizontal,
1757            use_alpha,
1758            label_format,
1759        )
1760        self.scalarbar = sb
1761        return self
1762
1763    def add_scalarbar3d(
1764        self,
1765        title="",
1766        pos=None,
1767        size=(None, None),
1768        title_font="",
1769        title_xoffset=-1.5,
1770        title_yoffset=0.0,
1771        title_size=1.5,
1772        title_rotation=0.0,
1773        nlabels=9,
1774        label_font="",
1775        label_size=1,
1776        label_offset=0.375,
1777        label_rotation=0,
1778        label_format="",
1779        italic=0,
1780        c=None,
1781        draw_box=True,
1782        above_text=None,
1783        below_text=None,
1784        nan_text="NaN",
1785        categories=None,
1786    ):
1787        """
1788        Associate a 3D scalar bar to the object and add it to the scene.
1789        The new scalarbar object (Assembly) will be accessible as obj.scalarbar
1790
1791        Arguments:
1792            size : (list)
1793                (thickness, length) of scalarbar
1794            title : (str)
1795                scalar bar title
1796            title_xoffset : (float)
1797                horizontal space btw title and color scalarbar
1798            title_yoffset : (float)
1799                vertical space offset
1800            title_size : (float)
1801                size of title wrt numeric labels
1802            title_rotation : (float)
1803                title rotation in degrees
1804            nlabels : (int)
1805                number of numeric labels
1806            label_font : (str)
1807                font type for labels
1808            label_size : (float)
1809                label scale factor
1810            label_offset : (float)
1811                space btw numeric labels and scale
1812            label_rotation : (float)
1813                label rotation in degrees
1814            label_format : (str)
1815                label format for floats and integers (e.g. `':.2f'`)
1816            draw_box : (bool)
1817                draw a box around the colorbar
1818            categories : (list)
1819                make a categorical scalarbar,
1820                the input list will have the format `[value, color, alpha, textlabel]`
1821
1822        Examples:
1823            - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py)
1824        """
1825        plt = vedo.plotter_instance
1826        if plt and c is None:  # automatic black or white
1827            c = (0.9, 0.9, 0.9)
1828            if np.sum(vedo.get_color(plt.backgrcol)) > 1.5:
1829                c = (0.1, 0.1, 0.1)
1830        if c is None:
1831            c = (0, 0, 0)
1832        c = vedo.get_color(c)
1833
1834        self.scalarbar = vedo.addons.ScalarBar3D(
1835            self,
1836            title,
1837            pos,
1838            size,
1839            title_font,
1840            title_xoffset,
1841            title_yoffset,
1842            title_size,
1843            title_rotation,
1844            nlabels,
1845            label_font,
1846            label_size,
1847            label_offset,
1848            label_rotation,
1849            label_format,
1850            italic,
1851            c,
1852            draw_box,
1853            above_text,
1854            below_text,
1855            nan_text,
1856            categories,
1857        )
1858        return self
1859
1860    ###################################################################################
1861    def write(self, filename, binary=True):
1862        """Write object to file."""
1863        out = vedo.file_io.write(self, filename, binary)
1864        out.pipeline = utils.OperationNode(
1865            "write", parents=[self], comment=filename[:15], shape="folder", c="#8a817c"
1866        )
1867        return out

Base class.

Do not use this class to instantiate objects, use one the above instead.
BaseActor()
1090    def __init__(self):
1091        """
1092        Base class to add operative and data
1093        functionality to `Mesh`, `Assembly`, `Volume` and `Picture` objects.
1094        """
1095
1096        super().__init__()
1097
1098        self._mapper = None
1099        self._caption = None
1100        self.property = None

Base class to add operative and data functionality to Mesh, Assembly, Volume and Picture objects.

def mapper(self, new_mapper=None):
1102    def mapper(self, new_mapper=None):
1103        """Return the `vtkMapper` data object, or update it with a new one."""
1104        if new_mapper:
1105            self.SetMapper(new_mapper)
1106            if self._mapper:
1107                iptdata = self._mapper.GetInput()
1108                if iptdata:
1109                    new_mapper.SetInputData(self._mapper.GetInput())
1110            self._mapper = new_mapper
1111            self._mapper.Modified()
1112        return self._mapper

Return the vtkMapper data object, or update it with a new one.

def inputdata(self):
1114    def inputdata(self):
1115        """Return the VTK input data object."""
1116        if self._mapper:
1117            return self._mapper.GetInput()
1118        return self.GetMapper().GetInput()

Return the VTK input data object.

def modified(self):
1120    def modified(self):
1121        """Use in conjunction with `tonumpy()`
1122        to update any modifications to the volume array"""
1123        sc = self.inputdata().GetPointData().GetScalars()
1124        if sc:
1125            sc.Modified()
1126        self.inputdata().GetPointData().Modified()
1127        return self

Use in conjunction with tonumpy() to update any modifications to the volume array

npoints

Retrieve the number of points.

ncells

Retrieve the number of cells.

def points(self, pts=None, transformed=True):
1139    def points(self, pts=None, transformed=True):
1140        """
1141        Set/Get the vertex coordinates of a mesh or point cloud.
1142        Argument can be an index, a set of indices
1143        or a complete new set of points to update the mesh.
1144
1145        Set `transformed=False` to ignore any previous transformation applied to the mesh.
1146        """
1147        if pts is None:  ### getter
1148
1149            if isinstance(self, vedo.Points):
1150                vpts = self.polydata(transformed).GetPoints()
1151            elif isinstance(self, vedo.BaseVolume):
1152                v2p = vtk.vtkImageToPoints()
1153                v2p.SetInputData(self.imagedata())
1154                v2p.Update()
1155                vpts = v2p.GetOutput().GetPoints()
1156            else:  # tetmesh et al
1157                vpts = self.inputdata().GetPoints()
1158
1159            if vpts:
1160                return utils.vtk2numpy(vpts.GetData())
1161            return np.array([], dtype=np.float32)
1162
1163        else:  ### setter
1164
1165            if len(pts) == 3 and len(pts[0]) != 3:
1166                # assume plist is in the format [all_x, all_y, all_z]
1167                pts = np.stack((pts[0], pts[1], pts[2]), axis=1)
1168            pts = np.asarray(pts, dtype=np.float32)
1169            if pts.shape[1] == 2:
1170                pts = np.c_[pts, np.zeros(pts.shape[0], dtype=np.float32)]
1171            vpts = self.inputdata().GetPoints()
1172            arr = utils.numpy2vtk(pts, dtype=np.float32)
1173            vpts.SetData(arr)
1174            vpts.Modified()
1175            # reset mesh to identity matrix position/rotation:
1176            self.PokeMatrix(vtk.vtkMatrix4x4())
1177            self.point_locator = None
1178            self.cell_locator = None
1179            return self

Set/Get the vertex coordinates of a mesh or point cloud. Argument can be an index, a set of indices or a complete new set of points to update the mesh.

Set transformed=False to ignore any previous transformation applied to the mesh.

def cell_centers(self):
1182    def cell_centers(self):
1183        """
1184        Get the coordinates of the cell centers.
1185
1186        Examples:
1187            - [delaunay2d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/delaunay2d.py)
1188        """
1189        vcen = vtk.vtkCellCenters()
1190        if hasattr(self, "polydata"):
1191            vcen.SetInputData(self.polydata())
1192        else:
1193            vcen.SetInputData(self.inputdata())
1194        vcen.Update()
1195        return utils.vtk2numpy(vcen.GetOutput().GetPoints().GetData())

Get the coordinates of the cell centers.

Examples:
def delete_cells(self, ids):
1197    def delete_cells(self, ids):
1198        """
1199        Remove cells from the mesh object by their ID.
1200        Points (vertices) are not removed (you may use `.clean()` to remove those).
1201        """
1202        data = self.inputdata()
1203        data.BuildLinks()
1204        for cid in ids:
1205            data.DeleteCell(cid)
1206        data.RemoveDeletedCells()
1207        data.Modified()
1208        self._mapper.Modified()
1209        self.pipeline = utils.OperationNode(
1210            "delete_cells", parents=[self], comment=f"#cells {self._data.GetNumberOfCells()}"
1211        )
1212        return self

Remove cells from the mesh object by their ID. Points (vertices) are not removed (you may use .clean() to remove those).

def mark_boundaries(self):
1214    def mark_boundaries(self):
1215        """
1216        Mark cells and vertices of the mesh if they lie on a boundary.
1217        A new array called `BoundaryCells` is added to the mesh.
1218        """
1219        mb = vtk.vtkMarkBoundaryFilter()
1220        mb.SetInputData(self._data)
1221        mb.Update()
1222        out = self._update(mb.GetOutput())
1223        out.pipeline = utils.OperationNode("mark_boundaries", parents=[self])
1224        return out

Mark cells and vertices of the mesh if they lie on a boundary. A new array called BoundaryCells is added to the mesh.

def find_cells_in(self, xbounds=(), ybounds=(), zbounds=()):
1226    def find_cells_in(self, xbounds=(), ybounds=(), zbounds=()):
1227        """
1228        Find cells that are within the specified bounds.
1229        Setting a color will add a vtk array to colorize these cells.
1230        """
1231        if len(xbounds) == 6:
1232            bnds = xbounds
1233        else:
1234            bnds = list(self.bounds())
1235            if len(xbounds) == 2:
1236                bnds[0] = xbounds[0]
1237                bnds[1] = xbounds[1]
1238            if len(ybounds) == 2:
1239                bnds[2] = ybounds[0]
1240                bnds[3] = ybounds[1]
1241            if len(zbounds) == 2:
1242                bnds[4] = zbounds[0]
1243                bnds[5] = zbounds[1]
1244
1245        cellIds = vtk.vtkIdList()
1246        self.cell_locator = vtk.vtkCellTreeLocator()
1247        self.cell_locator.SetDataSet(self.polydata())
1248        self.cell_locator.BuildLocator()
1249        self.cell_locator.FindCellsWithinBounds(bnds, cellIds)
1250
1251        cids = []
1252        for i in range(cellIds.GetNumberOfIds()):
1253            cid = cellIds.GetId(i)
1254            cids.append(cid)
1255
1256        return np.array(cids)

Find cells that are within the specified bounds. Setting a color will add a vtk array to colorize these cells.

def count_vertices(self):
1258    def count_vertices(self):
1259        """Count the number of vertices each cell has and return it as a numpy array"""
1260        vc = vtk.vtkCountVertices()
1261        vc.SetInputData(self._data)
1262        vc.SetOutputArrayName("VertexCount")
1263        vc.Update()
1264        varr = vc.GetOutput().GetCellData().GetArray("VertexCount")
1265        return utils.vtk2numpy(varr)

Count the number of vertices each cell has and return it as a numpy array

def lighting( self, style='', ambient=None, diffuse=None, specular=None, specular_power=None, specular_color=None, metallicity=None, roughness=None):
1267    def lighting(
1268        self,
1269        style="",
1270        ambient=None,
1271        diffuse=None,
1272        specular=None,
1273        specular_power=None,
1274        specular_color=None,
1275        metallicity=None,
1276        roughness=None,
1277    ):
1278        """
1279        Set the ambient, diffuse, specular and specular_power lighting constants.
1280
1281        Arguments:
1282            style : (str)
1283                preset style, options are `[metallic, plastic, shiny, glossy, ambient, off]`
1284            ambient : (float)
1285                ambient fraction of emission [0-1]
1286            diffuse : (float)
1287                emission of diffused light in fraction [0-1]
1288            specular : (float)
1289                fraction of reflected light [0-1]
1290            specular_power : (float)
1291                precision of reflection [1-100]
1292            specular_color : (color)
1293                color that is being reflected by the surface
1294
1295        <img src="https://upload.wikimedia.org/wikipedia/commons/6/6b/Phong_components_version_4.png" alt="", width=700px>
1296
1297        Examples:
1298            - [specular.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/specular.py)
1299        """
1300        pr = self.GetProperty()
1301
1302        if style:
1303
1304            if isinstance(pr, vtk.vtkVolumeProperty):
1305                self.shade(True)
1306                if style == "off":
1307                    self.shade(False)
1308                elif style == "ambient":
1309                    style = "default"
1310                    self.shade(False)
1311            else:
1312                if style != "off":
1313                    pr.LightingOn()
1314
1315            if style == "off":
1316                pr.SetInterpolationToFlat()
1317                pr.LightingOff()
1318                return self  ##############
1319
1320            if hasattr(pr, "GetColor"):  # could be Volume
1321                c = pr.GetColor()
1322            else:
1323                c = (1, 1, 0.99)
1324            mpr = self._mapper
1325            if hasattr(mpr, 'GetScalarVisibility') and mpr.GetScalarVisibility():
1326                c = (1,1,0.99)
1327            if   style=='metallic': pars = [0.1, 0.3, 1.0, 10, c]
1328            elif style=='plastic' : pars = [0.3, 0.4, 0.3,  5, c]
1329            elif style=='shiny'   : pars = [0.2, 0.6, 0.8, 50, c]
1330            elif style=='glossy'  : pars = [0.1, 0.7, 0.9, 90, (1,1,0.99)]
1331            elif style=='ambient' : pars = [0.8, 0.1, 0.0,  1, (1,1,1)]
1332            elif style=='default' : pars = [0.1, 1.0, 0.05, 5, c]
1333            else:
1334                vedo.logger.error("in lighting(): Available styles are")
1335                vedo.logger.error("[default, metallic, plastic, shiny, glossy, ambient, off]")
1336                raise RuntimeError()
1337            pr.SetAmbient(pars[0])
1338            pr.SetDiffuse(pars[1])
1339            pr.SetSpecular(pars[2])
1340            pr.SetSpecularPower(pars[3])
1341            if hasattr(pr, "GetColor"):
1342                pr.SetSpecularColor(pars[4])
1343
1344        if ambient is not None: pr.SetAmbient(ambient)
1345        if diffuse is not None: pr.SetDiffuse(diffuse)
1346        if specular is not None: pr.SetSpecular(specular)
1347        if specular_power is not None: pr.SetSpecularPower(specular_power)
1348        if specular_color is not None: pr.SetSpecularColor(colors.get_color(specular_color))
1349        if utils.vtk_version_at_least(9):
1350            if metallicity is not None:
1351                pr.SetInterpolationToPBR()
1352                pr.SetMetallic(metallicity)
1353            if roughness is not None:
1354                pr.SetInterpolationToPBR()
1355                pr.SetRoughness(roughness)
1356
1357        return self

Set the ambient, diffuse, specular and specular_power lighting constants.

Arguments:
  • style : (str) preset style, options are [metallic, plastic, shiny, glossy, ambient, off]
  • ambient : (float) ambient fraction of emission [0-1]
  • diffuse : (float) emission of diffused light in fraction [0-1]
  • specular : (float) fraction of reflected light [0-1]
  • specular_power : (float) precision of reflection [1-100]
  • specular_color : (color) color that is being reflected by the surface

Examples:
def print_histogram( self, bins=10, height=10, logscale=False, minbin=0, horizontal=False, char='â–‰', c=None, bold=True, title='Histogram'):
1359    def print_histogram(
1360        self,
1361        bins=10,
1362        height=10,
1363        logscale=False,
1364        minbin=0,
1365        horizontal=False,
1366        char="\U00002589",
1367        c=None,
1368        bold=True,
1369        title="Histogram",
1370    ):
1371        """
1372        Ascii histogram printing on terminal.
1373        Input can be `Volume` or `Mesh` (will grab the active point array).
1374
1375        Arguments:
1376            bins : (int)
1377                number of histogram bins
1378            height : (int)
1379                height of the histogram in character units
1380            logscale : (bool)
1381                use logscale for frequencies
1382            minbin : (int)
1383                ignore bins before minbin
1384            horizontal : (bool)
1385                show histogram horizontally
1386            char : (str)
1387                character to be used as marker
1388            c : (color)
1389                ascii color
1390            bold : (bool)
1391                use boldface
1392            title : (str)
1393                histogram title
1394
1395        ![](https://vedo.embl.es/images/feats/histoprint.png)
1396        """
1397        utils.print_histogram(
1398            self, bins, height, logscale, minbin, horizontal, char, c, bold, title
1399        )
1400        return self

Ascii histogram printing on terminal. Input can be Volume or Mesh (will grab the active point array).

Arguments:
  • bins : (int) number of histogram bins
  • height : (int) height of the histogram in character units
  • logscale : (bool) use logscale for frequencies
  • minbin : (int) ignore bins before minbin
  • horizontal : (bool) show histogram horizontally
  • char : (str) character to be used as marker
  • c : (color) ascii color
  • bold : (bool) use boldface
  • title : (str) histogram title

def c(self, color=False, alpha=None):
1402    def c(self, color=False, alpha=None):
1403        """
1404        Shortcut for `color()`.
1405        If None is passed as input, will use colors from current active scalars.
1406        """
1407        return self.color(color, alpha)

Shortcut for color(). If None is passed as input, will use colors from current active scalars.

pointdata

Create and/or return a numpy.array associated to points (vertices). A data array can be indexed either as a string or by an integer number. E.g.: myobj.pointdata["arrayname"]

Usage:

myobj.pointdata.keys() to return the available data array names

myobj.pointdata.select(name) to make this array the active one

myobj.pointdata.remove(name) to remove this array

celldata

Create and/or return a numpy.array associated to cells (faces). A data array can be indexed either as a string or by an integer number. E.g.: myobj.celldata["arrayname"]

Usage:

myobj.celldata.keys() to return the available data array names

myobj.celldata.select(name) to make this array the active one

myobj.celldata.remove(name) to remove this array

metadata

Create and/or return a numpy.array associated to neither cells nor faces. A data array can be indexed either as a string or by an integer number. E.g.: myobj.metadata["arrayname"]

Usage:

myobj.metadata.keys() to return the available data array names

myobj.metadata.select(name) to make this array the active one

myobj.metadata.remove(name) to remove this array

def map_cells_to_points(self, arrays=(), move=False):
1460    def map_cells_to_points(self, arrays=(), move=False):
1461        """
1462        Interpolate cell data (i.e., data specified per cell or face)
1463        into point data (i.e., data specified at each vertex).
1464        The method of transformation is based on averaging the data values
1465        of all cells using a particular point.
1466
1467        A custom list of arrays to be mapped can be passed in input.
1468
1469        Set `move=True` to delete the original `celldata` array.
1470        """
1471        c2p = vtk.vtkCellDataToPointData()
1472        c2p.SetInputData(self.inputdata())
1473        if not move:
1474            c2p.PassCellDataOn()
1475        if arrays:
1476            c2p.ClearCellDataArrays()
1477            c2p.ProcessAllArraysOff()
1478            for arr in arrays:
1479                c2p.AddCellDataArray(arr)
1480        else:
1481            c2p.ProcessAllArraysOn()
1482        c2p.Update()
1483        self._mapper.SetScalarModeToUsePointData()
1484        out = self._update(c2p.GetOutput())
1485        out.pipeline = utils.OperationNode("map cell\nto point data", parents=[self])
1486        return out

Interpolate cell data (i.e., data specified per cell or face) into point data (i.e., data specified at each vertex). The method of transformation is based on averaging the data values of all cells using a particular point.

A custom list of arrays to be mapped can be passed in input.

Set move=True to delete the original celldata array.

def map_points_to_cells(self, arrays=(), move=False):
1488    def map_points_to_cells(self, arrays=(), move=False):
1489        """
1490        Interpolate point data (i.e., data specified per point or vertex)
1491        into cell data (i.e., data specified per cell).
1492        The method of transformation is based on averaging the data values
1493        of all points defining a particular cell.
1494
1495        A custom list of arrays to be mapped can be passed in input.
1496
1497        Set `move=True` to delete the original `pointdata` array.
1498
1499        Examples:
1500            - [mesh_map2cell.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_map2cell.py)
1501        """
1502        p2c = vtk.vtkPointDataToCellData()
1503        p2c.SetInputData(self.inputdata())
1504        if not move:
1505            p2c.PassPointDataOn()
1506        if arrays:
1507            p2c.ClearPointDataArrays()
1508            p2c.ProcessAllArraysOff()
1509            for arr in arrays:
1510                p2c.AddPointDataArray(arr)
1511        else:
1512            p2c.ProcessAllArraysOn()
1513        p2c.Update()
1514        self._mapper.SetScalarModeToUseCellData()
1515        out = self._update(p2c.GetOutput())
1516        out.pipeline = utils.OperationNode("map point\nto cell data", parents=[self])
1517        return out

Interpolate point data (i.e., data specified per point or vertex) into cell data (i.e., data specified per cell). The method of transformation is based on averaging the data values of all points defining a particular cell.

A custom list of arrays to be mapped can be passed in input.

Set move=True to delete the original pointdata array.

Examples:
def resample_data_from(self, source, tol=None, categorical=False):
1519    def resample_data_from(self, source, tol=None, categorical=False):
1520        """
1521        Resample point and cell data from another dataset.
1522        The output has the same structure but its point data have
1523        the resampled values from target.
1524
1525        Use `tol` to set the tolerance used to compute whether
1526        a point in the source is in a cell of the current object.
1527        Points without resampled values, and their cells, are marked as blank.
1528        If the data is categorical, then the resulting data will be determined
1529        by a nearest neighbor interpolation scheme.
1530
1531        Example:
1532        ```python
1533        from vedo import *
1534        m1 = Mesh(dataurl+'bunny.obj')#.add_gaussian_noise(0.1)
1535        pts = m1.points()
1536        ces = m1.cell_centers()
1537        m1.pointdata["xvalues"] = np.power(pts[:,0], 3)
1538        m1.celldata["yvalues"]  = np.power(ces[:,1], 3)
1539        m2 = Mesh(dataurl+'bunny.obj')
1540        m2.resample_arrays_from(m1)
1541        # print(m2.pointdata["xvalues"])
1542        show(m1, m2 , N=2, axes=1)
1543        ```
1544        """
1545        rs = vtk.vtkResampleWithDataSet()
1546        rs.SetInputData(self.inputdata())
1547        rs.SetSourceData(source.inputdata())
1548
1549        rs.SetPassPointArrays(True)
1550        rs.SetPassCellArrays(True)
1551        rs.SetPassFieldArrays(True)
1552        rs.SetCategoricalData(categorical)
1553
1554        rs.SetComputeTolerance(True)
1555        if tol:
1556            rs.SetComputeTolerance(False)
1557            rs.SetTolerance(tol)
1558        rs.Update()
1559        self._update(rs.GetOutput())
1560        self.pipeline = utils.OperationNode(
1561            f"resample_data_from\n{source.__class__.__name__}", parents=[self, source]
1562        )
1563        return self

Resample point and cell data from another dataset. The output has the same structure but its point data have the resampled values from target.

Use tol to set the tolerance used to compute whether a point in the source is in a cell of the current object. Points without resampled values, and their cells, are marked as blank. If the data is categorical, then the resulting data will be determined by a nearest neighbor interpolation scheme.

Example:

from vedo import *
m1 = Mesh(dataurl+'bunny.obj')#.add_gaussian_noise(0.1)
pts = m1.points()
ces = m1.cell_centers()
m1.pointdata["xvalues"] = np.power(pts[:,0], 3)
m1.celldata["yvalues"]  = np.power(ces[:,1], 3)
m2 = Mesh(dataurl+'bunny.obj')
m2.resample_arrays_from(m1)
# print(m2.pointdata["xvalues"])
show(m1, m2 , N=2, axes=1)
def add_ids(self):
1565    def add_ids(self):
1566        """Generate point and cell ids arrays."""
1567        ids = vtk.vtkIdFilter()
1568        ids.SetInputData(self.inputdata())
1569        ids.PointIdsOn()
1570        ids.CellIdsOn()
1571        ids.FieldDataOff()
1572        ids.SetPointIdsArrayName("PointID")
1573        ids.SetCellIdsArrayName("CellID")
1574        ids.Update()
1575        self._update(ids.GetOutput())
1576        self.pipeline = utils.OperationNode("add_ids", parents=[self])
1577        return self

Generate point and cell ids arrays.

def gradient(self, input_array=None, on='points', fast=False):
1579    def gradient(self, input_array=None, on="points", fast=False):
1580        """
1581        Compute and return the gradiend of the active scalar field as a numpy array.
1582
1583        Arguments:
1584            input_array : (str)
1585                array of the scalars to compute the gradient,
1586                if None the current active array is selected
1587            on : (str)
1588                compute either on 'points' or 'cells' data
1589            fast : (bool)
1590                if True, will use a less accurate algorithm
1591                that performs fewer derivative calculations (and is therefore faster).
1592
1593        Examples:
1594            - [isolines.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/isolines.py)
1595
1596            ![](https://user-images.githubusercontent.com/32848391/72433087-f00a8780-3798-11ea-9778-991f0abeca70.png)
1597        """
1598        gra = vtk.vtkGradientFilter()
1599        if on.startswith("p"):
1600            varr = self.inputdata().GetPointData()
1601            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS
1602        else:
1603            varr = self.inputdata().GetCellData()
1604            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS
1605
1606        if input_array is None:
1607            if varr.GetScalars():
1608                input_array = varr.GetScalars().GetName()
1609            else:
1610                vedo.logger.error(f"in gradient: no scalars found for {on}")
1611                raise RuntimeError
1612
1613        gra.SetInputData(self.inputdata())
1614        gra.SetInputScalars(tp, input_array)
1615        gra.SetResultArrayName("Gradient")
1616        gra.SetFasterApproximation(fast)
1617        gra.ComputeDivergenceOff()
1618        gra.ComputeVorticityOff()
1619        gra.ComputeGradientOn()
1620        gra.Update()
1621        if on.startswith("p"):
1622            gvecs = utils.vtk2numpy(gra.GetOutput().GetPointData().GetArray("Gradient"))
1623        else:
1624            gvecs = utils.vtk2numpy(gra.GetOutput().GetCellData().GetArray("Gradient"))
1625        return gvecs

Compute and return the gradiend of the active scalar field as a numpy array.

Arguments:
  • input_array : (str) array of the scalars to compute the gradient, if None the current active array is selected
  • on : (str) compute either on 'points' or 'cells' data
  • fast : (bool) if True, will use a less accurate algorithm that performs fewer derivative calculations (and is therefore faster).
Examples:

def divergence(self, array_name=None, on='points', fast=False):
1627    def divergence(self, array_name=None, on="points", fast=False):
1628        """
1629        Compute and return the divergence of a vector field as a numpy array.
1630
1631        Arguments:
1632            array_name : (str)
1633                name of the array of vectors to compute the divergence,
1634                if None the current active array is selected
1635            on : (str)
1636                compute either on 'points' or 'cells' data
1637            fast : (bool)
1638                if True, will use a less accurate algorithm
1639                that performs fewer derivative calculations (and is therefore faster).
1640        """
1641        div = vtk.vtkGradientFilter()
1642        if on.startswith("p"):
1643            varr = self.inputdata().GetPointData()
1644            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS
1645        else:
1646            varr = self.inputdata().GetCellData()
1647            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS
1648
1649        if array_name is None:
1650            if varr.GetVectors():
1651                array_name = varr.GetVectors().GetName()
1652            else:
1653                vedo.logger.error(f"in divergence(): no vectors found for {on}")
1654                raise RuntimeError
1655
1656        div.SetInputData(self.inputdata())
1657        div.SetInputScalars(tp, array_name)
1658        div.ComputeDivergenceOn()
1659        div.ComputeGradientOff()
1660        div.ComputeVorticityOff()
1661        div.SetDivergenceArrayName("Divergence")
1662        div.SetFasterApproximation(fast)
1663        div.Update()
1664        if on.startswith("p"):
1665            dvecs = utils.vtk2numpy(div.GetOutput().GetPointData().GetArray("Divergence"))
1666        else:
1667            dvecs = utils.vtk2numpy(div.GetOutput().GetCellData().GetArray("Divergence"))
1668        return dvecs

Compute and return the divergence of a vector field as a numpy array.

Arguments:
  • array_name : (str) name of the array of vectors to compute the divergence, if None the current active array is selected
  • on : (str) compute either on 'points' or 'cells' data
  • fast : (bool) if True, will use a less accurate algorithm that performs fewer derivative calculations (and is therefore faster).
def vorticity(self, array_name=None, on='points', fast=False):
1670    def vorticity(self, array_name=None, on="points", fast=False):
1671        """
1672        Compute and return the vorticity of a vector field as a numpy array.
1673
1674        Arguments:
1675            array_name : (str)
1676                name of the array to compute the vorticity,
1677                if None the current active array is selected
1678            on : (str)
1679                compute either on 'points' or 'cells' data
1680            fast : (bool)
1681                if True, will use a less accurate algorithm
1682                that performs fewer derivative calculations (and is therefore faster).
1683        """
1684        vort = vtk.vtkGradientFilter()
1685        if on.startswith("p"):
1686            varr = self.inputdata().GetPointData()
1687            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS
1688        else:
1689            varr = self.inputdata().GetCellData()
1690            tp = vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS
1691
1692        if array_name is None:
1693            if varr.GetVectors():
1694                array_name = varr.GetVectors().GetName()
1695            else:
1696                vedo.logger.error(f"in vorticity(): no vectors found for {on}")
1697                raise RuntimeError
1698
1699        vort.SetInputData(self.inputdata())
1700        vort.SetInputScalars(tp, array_name)
1701        vort.ComputeDivergenceOff()
1702        vort.ComputeGradientOff()
1703        vort.ComputeVorticityOn()
1704        vort.SetVorticityArrayName("Vorticity")
1705        vort.SetFasterApproximation(fast)
1706        vort.Update()
1707        if on.startswith("p"):
1708            vvecs = utils.vtk2numpy(vort.GetOutput().GetPointData().GetArray("Vorticity"))
1709        else:
1710            vvecs = utils.vtk2numpy(vort.GetOutput().GetCellData().GetArray("Vorticity"))
1711        return vvecs

Compute and return the vorticity of a vector field as a numpy array.

Arguments:
  • array_name : (str) name of the array to compute the vorticity, if None the current active array is selected
  • on : (str) compute either on 'points' or 'cells' data
  • fast : (bool) if True, will use a less accurate algorithm that performs fewer derivative calculations (and is therefore faster).
def add_scalarbar( self, title='', pos=(0.8, 0.05), title_yoffset=15, font_size=12, size=(None, None), nlabels=None, c=None, horizontal=False, use_alpha=True, label_format=':6.3g'):
1713    def add_scalarbar(
1714        self,
1715        title="",
1716        pos=(0.8, 0.05),
1717        title_yoffset=15,
1718        font_size=12,
1719        size=(None, None),
1720        nlabels=None,
1721        c=None,
1722        horizontal=False,
1723        use_alpha=True,
1724        label_format=":6.3g",
1725    ):
1726        """
1727        Add a 2D scalar bar for the specified obj.
1728
1729        Examples:
1730            - [mesh_coloring.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/mesh_coloring.py)
1731            - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py)
1732        """
1733        plt = vedo.plotter_instance
1734
1735        if plt and plt.renderer:
1736            c = (0.9, 0.9, 0.9)
1737            if np.sum(plt.renderer.GetBackground()) > 1.5:
1738                c = (0.1, 0.1, 0.1)
1739            if isinstance(self.scalarbar, vtk.vtkActor):
1740                plt.renderer.RemoveActor(self.scalarbar)
1741            elif isinstance(self.scalarbar, vedo.Assembly):
1742                for a in self.scalarbar.unpack():
1743                    plt.renderer.RemoveActor(a)
1744        if c is None:
1745            c = "gray"
1746
1747        sb = vedo.addons.ScalarBar(
1748            self,
1749            title,
1750            pos,
1751            title_yoffset,
1752            font_size,
1753            size,
1754            nlabels,
1755            c,
1756            horizontal,
1757            use_alpha,
1758            label_format,
1759        )
1760        self.scalarbar = sb
1761        return self

Add a 2D scalar bar for the specified obj.

Examples:
def add_scalarbar3d( self, 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=9, 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):
1763    def add_scalarbar3d(
1764        self,
1765        title="",
1766        pos=None,
1767        size=(None, None),
1768        title_font="",
1769        title_xoffset=-1.5,
1770        title_yoffset=0.0,
1771        title_size=1.5,
1772        title_rotation=0.0,
1773        nlabels=9,
1774        label_font="",
1775        label_size=1,
1776        label_offset=0.375,
1777        label_rotation=0,
1778        label_format="",
1779        italic=0,
1780        c=None,
1781        draw_box=True,
1782        above_text=None,
1783        below_text=None,
1784        nan_text="NaN",
1785        categories=None,
1786    ):
1787        """
1788        Associate a 3D scalar bar to the object and add it to the scene.
1789        The new scalarbar object (Assembly) will be accessible as obj.scalarbar
1790
1791        Arguments:
1792            size : (list)
1793                (thickness, length) of scalarbar
1794            title : (str)
1795                scalar bar title
1796            title_xoffset : (float)
1797                horizontal space btw title and color scalarbar
1798            title_yoffset : (float)
1799                vertical space offset
1800            title_size : (float)
1801                size of title wrt numeric labels
1802            title_rotation : (float)
1803                title rotation in degrees
1804            nlabels : (int)
1805                number of numeric labels
1806            label_font : (str)
1807                font type for labels
1808            label_size : (float)
1809                label scale factor
1810            label_offset : (float)
1811                space btw numeric labels and scale
1812            label_rotation : (float)
1813                label rotation in degrees
1814            label_format : (str)
1815                label format for floats and integers (e.g. `':.2f'`)
1816            draw_box : (bool)
1817                draw a box around the colorbar
1818            categories : (list)
1819                make a categorical scalarbar,
1820                the input list will have the format `[value, color, alpha, textlabel]`
1821
1822        Examples:
1823            - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py)
1824        """
1825        plt = vedo.plotter_instance
1826        if plt and c is None:  # automatic black or white
1827            c = (0.9, 0.9, 0.9)
1828            if np.sum(vedo.get_color(plt.backgrcol)) > 1.5:
1829                c = (0.1, 0.1, 0.1)
1830        if c is None:
1831            c = (0, 0, 0)
1832        c = vedo.get_color(c)
1833
1834        self.scalarbar = vedo.addons.ScalarBar3D(
1835            self,
1836            title,
1837            pos,
1838            size,
1839            title_font,
1840            title_xoffset,
1841            title_yoffset,
1842            title_size,
1843            title_rotation,
1844            nlabels,
1845            label_font,
1846            label_size,
1847            label_offset,
1848            label_rotation,
1849            label_format,
1850            italic,
1851            c,
1852            draw_box,
1853            above_text,
1854            below_text,
1855            nan_text,
1856            categories,
1857        )
1858        return self

Associate a 3D scalar bar to the object and add it to the scene. The new scalarbar object (Assembly) will be accessible as obj.scalarbar

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
  • label_format : (str) label format for floats and integers (e.g. ':.2f')
  • 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:
def write(self, filename, binary=True):
1861    def write(self, filename, binary=True):
1862        """Write object to file."""
1863        out = vedo.file_io.write(self, filename, binary)
1864        out.pipeline = utils.OperationNode(
1865            "write", parents=[self], comment=filename[:15], shape="folder", c="#8a817c"
1866        )
1867        return out

Write object to file.

class BaseActor2D(vtkmodules.vtkRenderingCore.vtkActor2D):
 986class BaseActor2D(vtk.vtkActor2D):
 987    """
 988    Base class.
 989
 990    .. warning:: Do not use this class to instantiate objects.
 991    """
 992
 993    def __init__(self):
 994        """Manage 2D objects."""
 995        super().__init__()
 996        self._mapper = None
 997        self.property = self.GetProperty()
 998        self.filename = ""
 999
1000    def layer(self, value=None):
1001        """Set/Get the layer number in the overlay planes into which to render."""
1002        if value is None:
1003            return self.GetLayerNumber()
1004        self.SetLayerNumber(value)
1005        return self
1006
1007    def pos(self, px=None, py=None):
1008        """Set/Get the screen-coordinate position."""
1009        if isinstance(px, str):
1010            vedo.logger.error("Use string descriptors only inside the constructor")
1011            return self
1012        if px is None:
1013            return np.array(self.GetPosition(), dtype=int)
1014        if py is not None:
1015            p = [px, py]
1016        else:
1017            p = px
1018        assert len(p) == 2, "Error: len(pos) must be 2 for BaseActor2D"
1019        self.SetPosition(p)
1020        return self
1021
1022    def coordinate_system(self, value=None):
1023        """
1024        Set/get the coordinate system which this coordinate is defined in.
1025
1026        The options are:
1027            0. Display
1028            1. Normalized Display
1029            2. Viewport
1030            3. Normalized Viewport
1031            4. View
1032            5. Pose
1033            6. World
1034        """
1035        coor = self.GetPositionCoordinate()
1036        if value is None:
1037            return coor.GetCoordinateSystem()
1038        coor.SetCoordinateSystem(value)
1039        return self
1040
1041    def on(self):
1042        """Set object visibility."""
1043        self.VisibilityOn()
1044        return self
1045
1046    def off(self):
1047        """Set object visibility."""
1048        self.VisibilityOn()
1049        return self
1050
1051    def toggle(self):
1052        """Toggle object visibility."""
1053        self.SetVisibility(not self.GetVisibility())
1054        return self
1055
1056    def pickable(self, value=True):
1057        self.SetPickable(value)
1058        return self
1059
1060    def alpha(self, value=None):
1061        """Set/Get the object opacity."""
1062        if value is None:
1063            return self.GetProperty().GetOpacity()
1064        self.GetProperty().SetOpacity(value)
1065        return self
1066
1067    def ps(self, point_size=None):
1068        if point_size is None:
1069            return self.GetProperty().GetPointSize()
1070        self.GetProperty().SetPointSize(point_size)
1071        return self
1072
1073    def ontop(self, value=True):
1074        """Keep the object always on top of everything else."""
1075        if value:
1076            self.GetProperty().SetDisplayLocationToForeground()
1077        else:
1078            self.GetProperty().SetDisplayLocationToBackground()
1079        return self

Base class.

Do not use this class to instantiate objects.
BaseActor2D()
993    def __init__(self):
994        """Manage 2D objects."""
995        super().__init__()
996        self._mapper = None
997        self.property = self.GetProperty()
998        self.filename = ""

Manage 2D objects.

def layer(self, value=None):
1000    def layer(self, value=None):
1001        """Set/Get the layer number in the overlay planes into which to render."""
1002        if value is None:
1003            return self.GetLayerNumber()
1004        self.SetLayerNumber(value)
1005        return self

Set/Get the layer number in the overlay planes into which to render.

def pos(self, px=None, py=None):
1007    def pos(self, px=None, py=None):
1008        """Set/Get the screen-coordinate position."""
1009        if isinstance(px, str):
1010            vedo.logger.error("Use string descriptors only inside the constructor")
1011            return self
1012        if px is None:
1013            return np.array(self.GetPosition(), dtype=int)
1014        if py is not None:
1015            p = [px, py]
1016        else:
1017            p = px
1018        assert len(p) == 2, "Error: len(pos) must be 2 for BaseActor2D"
1019        self.SetPosition(p)
1020        return self

Set/Get the screen-coordinate position.

def coordinate_system(self, value=None):
1022    def coordinate_system(self, value=None):
1023        """
1024        Set/get the coordinate system which this coordinate is defined in.
1025
1026        The options are:
1027            0. Display
1028            1. Normalized Display
1029            2. Viewport
1030            3. Normalized Viewport
1031            4. View
1032            5. Pose
1033            6. World
1034        """
1035        coor = self.GetPositionCoordinate()
1036        if value is None:
1037            return coor.GetCoordinateSystem()
1038        coor.SetCoordinateSystem(value)
1039        return self

Set/get the coordinate system which this coordinate is defined in.

The options are:
  1. Display
  2. Normalized Display
  3. Viewport
  4. Normalized Viewport
  5. View
  6. Pose
  7. World
def on(self):
1041    def on(self):
1042        """Set object visibility."""
1043        self.VisibilityOn()
1044        return self

Set object visibility.

def off(self):
1046    def off(self):
1047        """Set object visibility."""
1048        self.VisibilityOn()
1049        return self

Set object visibility.

def toggle(self):
1051    def toggle(self):
1052        """Toggle object visibility."""
1053        self.SetVisibility(not self.GetVisibility())
1054        return self

Toggle object visibility.

def pickable(self, value=True):
1056    def pickable(self, value=True):
1057        self.SetPickable(value)
1058        return self
def alpha(self, value=None):
1060    def alpha(self, value=None):
1061        """Set/Get the object opacity."""
1062        if value is None:
1063            return self.GetProperty().GetOpacity()
1064        self.GetProperty().SetOpacity(value)
1065        return self

Set/Get the object opacity.

def ps(self, point_size=None):
1067    def ps(self, point_size=None):
1068        if point_size is None:
1069            return self.GetProperty().GetPointSize()
1070        self.GetProperty().SetPointSize(point_size)
1071        return self
def ontop(self, value=True):
1073    def ontop(self, value=True):
1074        """Keep the object always on top of everything else."""
1075        if value:
1076            self.GetProperty().SetDisplayLocationToForeground()
1077        else:
1078            self.GetProperty().SetDisplayLocationToBackground()
1079        return self

Keep the object always on top of everything else.

class BaseGrid(BaseActor):
1871class BaseGrid(BaseActor):
1872    """
1873    Base class for grid datasets.
1874
1875    .. warning:: Do not use this class to instantiate objects.
1876    """
1877
1878    def __init__(self):
1879        """Base class for grid datasets."""
1880
1881        super().__init__()
1882
1883        self._data = None
1884        self.useCells = True
1885        self._color = None
1886        self._alpha = [0, 1]
1887
1888        # -----------------------------------------------------------
1889
1890    def _update(self, data):
1891        self._data = data
1892        self._mapper.SetInputData(self.tomesh().polydata())
1893        self._mapper.Modified()
1894        return self
1895
1896    def tomesh(self, fill=True, shrink=1.0):
1897        """
1898        Build a polygonal Mesh from the current Grid object.
1899
1900        If `fill=True`, the interior faces of all the cells are created.
1901        (setting a `shrink` value slightly smaller than the default 1.0
1902        can avoid flickering due to internal adjacent faces).
1903
1904        If `fill=False`, only the boundary faces will be generated.
1905        """
1906        gf = vtk.vtkGeometryFilter()
1907        if fill:
1908            sf = vtk.vtkShrinkFilter()
1909            sf.SetInputData(self._data)
1910            sf.SetShrinkFactor(shrink)
1911            sf.Update()
1912            gf.SetInputData(sf.GetOutput())
1913            gf.Update()
1914            poly = gf.GetOutput()
1915            if shrink == 1.0:
1916                cleanPolyData = vtk.vtkCleanPolyData()
1917                cleanPolyData.PointMergingOn()
1918                cleanPolyData.ConvertLinesToPointsOn()
1919                cleanPolyData.ConvertPolysToLinesOn()
1920                cleanPolyData.ConvertStripsToPolysOn()
1921                cleanPolyData.SetInputData(poly)
1922                cleanPolyData.Update()
1923                poly = cleanPolyData.GetOutput()
1924        else:
1925            gf.SetInputData(self._data)
1926            gf.Update()
1927            poly = gf.GetOutput()
1928
1929        msh = vedo.mesh.Mesh(poly).flat()
1930        msh.scalarbar = self.scalarbar
1931        lut = utils.ctf2lut(self)
1932        if lut:
1933            msh.mapper().SetLookupTable(lut)
1934        if self.useCells:
1935            msh.mapper().SetScalarModeToUseCellData()
1936        else:
1937            msh.mapper().SetScalarModeToUsePointData()
1938
1939        msh.pipeline = utils.OperationNode(
1940            "tomesh", parents=[self], comment=f"fill={fill}", c="#9e2a2b:#e9c46a"
1941        )
1942        return msh
1943
1944    def cells(self):
1945        """
1946        Get the cells connectivity ids as a numpy array.
1947
1948        The output format is: `[[id0 ... idn], [id0 ... idm],  etc]`.
1949        """
1950        arr1d = utils.vtk2numpy(self._data.GetCells().GetData())
1951        if arr1d is None:
1952            return []
1953
1954        # Get cell connettivity ids as a 1D array. vtk format is:
1955        # [nids1, id0 ... idn, niids2, id0 ... idm,  etc].
1956        i = 0
1957        conn = []
1958        n = len(arr1d)
1959        if n:
1960            while True:
1961                cell = [arr1d[i + k] for k in range(1, arr1d[i] + 1)]
1962                conn.append(cell)
1963                i += arr1d[i] + 1
1964                if i >= n:
1965                    break
1966        return conn
1967
1968    def color(self, col, alpha=None, vmin=None, vmax=None):
1969        """
1970        Assign a color or a set of colors along the range of the scalar value.
1971        A single constant color can also be assigned.
1972        Any matplotlib color map name is also accepted, e.g. `volume.color('jet')`.
1973
1974        E.g.: say that your cells scalar runs from -3 to 6,
1975        and you want -3 to show red and 1.5 violet and 6 green, then just set:
1976
1977        `volume.color(['red', 'violet', 'green'])`
1978
1979        You can also assign a specific color to a aspecific value with eg.:
1980
1981        `volume.color([(0,'red', (0.5,'violet'), (1,'green')])`
1982
1983        Arguments:
1984            alpha : (list)
1985                use a list to specify transparencies along the scalar range
1986            vmin : (float)
1987                force the min of the scalar range to be this value
1988            vmax : (float)
1989                force the max of the scalar range to be this value
1990        """
1991        # supersedes method in Points, Mesh
1992
1993        if col is None:
1994            return self
1995        
1996        if vmin is None:
1997            vmin, _ = self._data.GetScalarRange()
1998        if vmax is None:
1999            _, vmax = self._data.GetScalarRange()
2000        ctf = self.GetProperty().GetRGBTransferFunction()
2001        ctf.RemoveAllPoints()
2002        self._color = col
2003
2004        if utils.is_sequence(col):
2005            if utils.is_sequence(col[0]) and len(col[0]) == 2:
2006                # user passing [(value1, color1), ...]
2007                for x, ci in col:
2008                    r, g, b = colors.get_color(ci)
2009                    ctf.AddRGBPoint(x, r, g, b)
2010                    # colors.printc('color at', round(x, 1),
2011                    #               'set to', colors.get_color_name((r, g, b)),
2012                    #               c='w', bold=0)
2013            else:
2014                # user passing [color1, color2, ..]
2015                for i, ci in enumerate(col):
2016                    r, g, b = colors.get_color(ci)
2017                    x = vmin + (vmax - vmin) * i / (len(col) - 1)
2018                    ctf.AddRGBPoint(x, r, g, b)
2019        elif isinstance(col, str):
2020            if col in colors.colors.keys() or col in colors.color_nicks.keys():
2021                r, g, b = colors.get_color(col)
2022                ctf.AddRGBPoint(vmin, r, g, b)  # constant color
2023                ctf.AddRGBPoint(vmax, r, g, b)
2024            else:  # assume it's a colormap
2025                for x in np.linspace(vmin, vmax, num=64, endpoint=True):
2026                    r, g, b = colors.color_map(x, name=col, vmin=vmin, vmax=vmax)
2027                    ctf.AddRGBPoint(x, r, g, b)
2028        elif isinstance(col, int):
2029            r, g, b = colors.get_color(col)
2030            ctf.AddRGBPoint(vmin, r, g, b)  # constant color
2031            ctf.AddRGBPoint(vmax, r, g, b)
2032        else:
2033            vedo.logger.warning(f"in color() unknown input type {type(col)}")
2034
2035        if alpha is not None:
2036            self.alpha(alpha, vmin=vmin, vmax=vmax)
2037        return self
2038
2039    def alpha(self, alpha, vmin=None, vmax=None):
2040        """
2041        Assign a set of tranparencies along the range of the scalar value.
2042        A single constant value can also be assigned.
2043
2044        E.g.: say `alpha=(0.0, 0.3, 0.9, 1)` and the scalar range goes from -10 to 150.
2045        Then all cells with a value close to -10 will be completely transparent, cells at 1/4
2046        of the range will get an alpha equal to 0.3 and voxels with value close to 150
2047        will be completely opaque.
2048
2049        As a second option one can set explicit (x, alpha_x) pairs to define the transfer function.
2050
2051        E.g.: say `alpha=[(-5, 0), (35, 0.4) (123,0.9)]` and the scalar range goes from -10 to 150.
2052        Then all cells below -5 will be completely transparent, cells with a scalar value of 35
2053        will get an opacity of 40% and above 123 alpha is set to 90%.
2054        """
2055        if vmin is None:
2056            vmin, _ = self._data.GetScalarRange()
2057        if vmax is None:
2058            _, vmax = self._data.GetScalarRange()
2059        otf = self.GetProperty().GetScalarOpacity()
2060        otf.RemoveAllPoints()
2061        self._alpha = alpha
2062
2063        if utils.is_sequence(alpha):
2064            alpha = np.array(alpha)
2065            if len(alpha.shape) == 1:  # user passing a flat list e.g. (0.0, 0.3, 0.9, 1)
2066                for i, al in enumerate(alpha):
2067                    xalpha = vmin + (vmax - vmin) * i / (len(alpha) - 1)
2068                    # Create transfer mapping scalar value to opacity
2069                    otf.AddPoint(xalpha, al)
2070                    # colors.printc("alpha at", round(xalpha, 1), "\tset to", al)
2071            elif len(alpha.shape) == 2:  # user passing [(x0,alpha0), ...]
2072                otf.AddPoint(vmin, alpha[0][1])
2073                for xalpha, al in alpha:
2074                    # Create transfer mapping scalar value to opacity
2075                    otf.AddPoint(xalpha, al)
2076                otf.AddPoint(vmax, alpha[-1][1])
2077
2078        else:
2079
2080            otf.AddPoint(vmin, alpha)  # constant alpha
2081            otf.AddPoint(vmax, alpha)
2082
2083        return self
2084
2085    def alpha_unit(self, u=None):
2086        """
2087        Defines light attenuation per unit length. Default is 1.
2088        The larger the unit length, the further light has to travel to attenuate the same amount.
2089
2090        E.g., if you set the unit distance to 0, you will get full opacity.
2091        It means that when light travels 0 distance it's already attenuated a finite amount.
2092        Thus, any finite distance should attenuate all light.
2093        The larger you make the unit distance, the more transparent the rendering becomes.
2094        """
2095        if u is None:
2096            return self.GetProperty().GetScalarOpacityUnitDistance()
2097        self.GetProperty().SetScalarOpacityUnitDistance(u)
2098        return self
2099
2100    def shrink(self, fraction=0.8):
2101        """
2102        Shrink the individual cells to improve visibility.
2103
2104        ![](https://vedo.embl.es/images/feats/shrink_hex.png)
2105        """
2106        sf = vtk.vtkShrinkFilter()
2107        sf.SetInputData(self.inputdata())
2108        sf.SetShrinkFactor(fraction)
2109        sf.Update()
2110        self._update(sf.GetOutput())
2111        self.pipeline = utils.OperationNode(
2112            "shrink", comment=f"by {fraction}", parents=[self], c="#9e2a2b"
2113        )
2114        return self
2115
2116    def isosurface(self, value=None, flying_edges=True):
2117        """
2118        Return an `Mesh` isosurface extracted from the `Volume` object.
2119
2120        Set `value` as single float or list of values to draw the isosurface(s).
2121        Use flying_edges for faster results (but sometimes can interfere with `smooth()`).
2122
2123        Examples:
2124            - [isosurfaces.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/isosurfaces.py)
2125
2126                ![](https://vedo.embl.es/images/volumetric/isosurfaces.png)
2127        """
2128        scrange = self._data.GetScalarRange()
2129
2130        if flying_edges:
2131            cf = vtk.vtkFlyingEdges3D()
2132            cf.InterpolateAttributesOn()
2133        else:
2134            cf = vtk.vtkContourFilter()
2135            cf.UseScalarTreeOn()
2136
2137        cf.SetInputData(self._data)
2138        cf.ComputeNormalsOn()
2139
2140        if utils.is_sequence(value):
2141            cf.SetNumberOfContours(len(value))
2142            for i, t in enumerate(value):
2143                cf.SetValue(i, t)
2144        else:
2145            if value is None:
2146                value = (2 * scrange[0] + scrange[1]) / 3.0
2147                # print("automatic isosurface value =", value)
2148            cf.SetValue(0, value)
2149
2150        cf.Update()
2151        poly = cf.GetOutput()
2152
2153        out = vedo.mesh.Mesh(poly, c=None).phong()
2154        out.mapper().SetScalarRange(scrange[0], scrange[1])
2155
2156        out.pipeline = utils.OperationNode(
2157            "isosurface",
2158            parents=[self],
2159            comment=f"#pts {out.inputdata().GetNumberOfPoints()}",
2160            c="#4cc9f0:#e9c46a",
2161        )
2162        return out
2163
2164    def legosurface(
2165        self, vmin=None, vmax=None, invert=False, boundary=False, array_name="input_scalars"
2166    ):
2167        """
2168        Represent an object - typically a `Volume` - as lego blocks (voxels).
2169        By default colors correspond to the volume's scalar.
2170        Returns an `Mesh` object.
2171
2172        Arguments:
2173            vmin : (float)
2174                the lower threshold, voxels below this value are not shown.
2175            vmax : (float)
2176                the upper threshold, voxels above this value are not shown.
2177            boundary : (bool)
2178                controls whether to include cells that are partially inside
2179            array_name : (int, str)
2180                name or index of the scalar array to be considered
2181
2182        Examples:
2183            - [legosurface.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/legosurface.py)
2184
2185                ![](https://vedo.embl.es/images/volumetric/56820682-da40e500-684c-11e9-8ea3-91cbcba24b3a.png)
2186        """
2187        dataset = vtk.vtkImplicitDataSet()
2188        dataset.SetDataSet(self._data)
2189        window = vtk.vtkImplicitWindowFunction()
2190        window.SetImplicitFunction(dataset)
2191
2192        srng = list(self._data.GetScalarRange())
2193        if vmin is not None:
2194            srng[0] = vmin
2195        if vmax is not None:
2196            srng[1] = vmax
2197        tol = 0.00001 * (srng[1] - srng[0])
2198        srng[0] -= tol
2199        srng[1] += tol
2200        window.SetWindowRange(srng)
2201
2202        extract = vtk.vtkExtractGeometry()
2203        extract.SetInputData(self._data)
2204        extract.SetImplicitFunction(window)
2205        extract.SetExtractInside(invert)
2206        extract.SetExtractBoundaryCells(boundary)
2207        extract.Update()
2208
2209        gf = vtk.vtkGeometryFilter()
2210        gf.SetInputData(extract.GetOutput())
2211        gf.Update()
2212
2213        m = vedo.mesh.Mesh(gf.GetOutput()).lw(0.1).flat()
2214        m.map_points_to_cells()
2215        m.celldata.select(array_name)
2216
2217        m.pipeline = utils.OperationNode(
2218            "legosurface", parents=[self], comment=f"array: {array_name}", c="#4cc9f0:#e9c46a"
2219        )
2220        return m
2221
2222    def cut_with_plane(self, origin=(0, 0, 0), normal="x"):
2223        """
2224        Cut the object with the plane defined by a point and a normal.
2225
2226        Arguments:
2227            origin : (list)
2228                the cutting plane goes through this point
2229            normal : (list, str)
2230                normal vector to the cutting plane
2231        """
2232        strn = str(normal)
2233        if strn   ==  "x": normal = (1, 0, 0)
2234        elif strn ==  "y": normal = (0, 1, 0)
2235        elif strn ==  "z": normal = (0, 0, 1)
2236        elif strn == "-x": normal = (-1, 0, 0)
2237        elif strn == "-y": normal = (0, -1, 0)
2238        elif strn == "-z": normal = (0, 0, -1)
2239        plane = vtk.vtkPlane()
2240        plane.SetOrigin(origin)
2241        plane.SetNormal(normal)
2242        clipper = vtk.vtkClipDataSet()
2243        clipper.SetInputData(self._data)
2244        clipper.SetClipFunction(plane)
2245        clipper.GenerateClipScalarsOff()
2246        clipper.GenerateClippedOutputOff()
2247        clipper.SetValue(0)
2248        clipper.Update()
2249        cout = clipper.GetOutput()
2250        self._update(cout)
2251        self.pipeline = utils.OperationNode("cut_with_plane", parents=[self], c="#9e2a2b")
2252        return self
2253
2254    def cut_with_box(self, box):
2255        """
2256        Cut the grid with the specified bounding box.
2257
2258        Parameter box has format [xmin, xmax, ymin, ymax, zmin, zmax].
2259        If an object is passed, its bounding box are used.
2260
2261        Example:
2262            ```python
2263            from vedo import *
2264            tetmesh = TetMesh(dataurl+'limb_ugrid.vtk')
2265            tetmesh.color('rainbow')
2266            cu = Cube(side=500).x(500) # any Mesh works
2267            tetmesh.cut_with_box(cu).show(axes=1)
2268            ```
2269            ![](https://vedo.embl.es/images/feats/tet_cut_box.png)
2270        """
2271        bc = vtk.vtkBoxClipDataSet()
2272        bc.SetInputData(self._data)
2273        if isinstance(box, vtk.vtkProp):
2274            boxb = box.GetBounds()
2275        else:
2276            boxb = box
2277        bc.SetBoxClip(*boxb)
2278        bc.Update()
2279        self._update(bc.GetOutput())
2280        self.pipeline = utils.OperationNode("cut_with_box", parents=[self, box], c="#9e2a2b")
2281        return self
2282
2283    def cut_with_mesh(self, mesh, invert=False, whole_cells=False, only_boundary=False):
2284        """
2285        Cut a UGrid, TetMesh or Volume with a Mesh.
2286
2287        Use `invert` to return cut off part of the input object.
2288        """
2289        polymesh = mesh.polydata()
2290        ug = self._data
2291
2292        ippd = vtk.vtkImplicitPolyDataDistance()
2293        ippd.SetInput(polymesh)
2294
2295        if whole_cells or only_boundary:
2296            clipper = vtk.vtkExtractGeometry()
2297            clipper.SetInputData(ug)
2298            clipper.SetImplicitFunction(ippd)
2299            clipper.SetExtractInside(not invert)
2300            clipper.SetExtractBoundaryCells(False)
2301            if only_boundary:
2302                clipper.SetExtractBoundaryCells(True)
2303                clipper.SetExtractOnlyBoundaryCells(True)
2304        else:
2305            signedDistances = vtk.vtkFloatArray()
2306            signedDistances.SetNumberOfComponents(1)
2307            signedDistances.SetName("SignedDistances")
2308            for pointId in range(ug.GetNumberOfPoints()):
2309                p = ug.GetPoint(pointId)
2310                signedDistance = ippd.EvaluateFunction(p)
2311                signedDistances.InsertNextValue(signedDistance)
2312            ug.GetPointData().AddArray(signedDistances)
2313            ug.GetPointData().SetActiveScalars("SignedDistances")
2314            clipper = vtk.vtkClipDataSet()
2315            clipper.SetInputData(ug)
2316            clipper.SetInsideOut(not invert)
2317            clipper.SetValue(0.0)
2318
2319        clipper.Update()
2320        cug = clipper.GetOutput()
2321
2322        if ug.GetCellData().GetScalars():  # not working
2323            scalname = ug.GetCellData().GetScalars().GetName()
2324            if scalname:  # not working
2325                if self.useCells:
2326                    self.celldata.select(scalname)
2327                else:
2328                    self.pointdata.select(scalname)
2329
2330        self._update(cug)
2331        self.pipeline = utils.OperationNode("cut_with_mesh", parents=[self, mesh], c="#9e2a2b")
2332        return self
2333
2334    def extract_cells_on_plane(self, origin, normal):
2335        """
2336        Extract cells that are lying of the specified surface.
2337        """
2338        bf = vtk.vtk3DLinearGridCrinkleExtractor()
2339        bf.SetInputData(self._data)
2340        bf.CopyPointDataOn()
2341        bf.CopyCellDataOn()
2342        bf.RemoveUnusedPointsOff()
2343
2344        plane = vtk.vtkPlane()
2345        plane.SetOrigin(origin)
2346        plane.SetNormal(normal)
2347        bf.SetImplicitFunction(plane)
2348        bf.Update()
2349
2350        self._update(bf.GetOutput())
2351        self.pipeline = utils.OperationNode(
2352            "extract_cells_on_plane",
2353            parents=[self],
2354            comment=f"#cells {self._data.GetNumberOfCells()}",
2355            c="#9e2a2b",
2356        )
2357        return self
2358
2359    def extract_cells_on_sphere(self, center, radius):
2360        """
2361        Extract cells that are lying of the specified surface.
2362        """
2363        bf = vtk.vtk3DLinearGridCrinkleExtractor()
2364        bf.SetInputData(self._data)
2365        bf.CopyPointDataOn()
2366        bf.CopyCellDataOn()
2367        bf.RemoveUnusedPointsOff()
2368
2369        sph = vtk.vtkSphere()
2370        sph.SetRadius(radius)
2371        sph.SetCenter(center)
2372        bf.SetImplicitFunction(sph)
2373        bf.Update()
2374
2375        self._update(bf.GetOutput())
2376        self.pipeline = utils.OperationNode(
2377            "extract_cells_on_sphere",
2378            parents=[self],
2379            comment=f"#cells {self._data.GetNumberOfCells()}",
2380            c="#9e2a2b",
2381        )
2382        return self
2383
2384    def extract_cells_on_cylinder(self, center, axis, radius):
2385        """
2386        Extract cells that are lying of the specified surface.
2387        """
2388        bf = vtk.vtk3DLinearGridCrinkleExtractor()
2389        bf.SetInputData(self._data)
2390        bf.CopyPointDataOn()
2391        bf.CopyCellDataOn()
2392        bf.RemoveUnusedPointsOff()
2393
2394        cyl = vtk.vtkCylinder()
2395        cyl.SetRadius(radius)
2396        cyl.SetCenter(center)
2397        cyl.SetAxis(axis)
2398        bf.SetImplicitFunction(cyl)
2399        bf.Update()
2400
2401        self.pipeline = utils.OperationNode(
2402            "extract_cells_on_cylinder",
2403            parents=[self],
2404            comment=f"#cells {self._data.GetNumberOfCells()}",
2405            c="#9e2a2b",
2406        )
2407        self._update(bf.GetOutput())
2408        return self
2409
2410    def clean(self):
2411        """
2412        Cleanup unused points and empty cells
2413        """
2414        cl = vtk.vtkStaticCleanUnstructuredGrid()
2415        cl.SetInputData(self._data)
2416        cl.RemoveUnusedPointsOn()
2417        cl.ProduceMergeMapOff()
2418        cl.AveragePointDataOff()
2419        cl.Update()
2420
2421        self._update(cl.GetOutput())
2422        self.pipeline = utils.OperationNode(
2423            "clean", parents=[self], comment=f"#cells {self._data.GetNumberOfCells()}", c="#9e2a2b"
2424        )
2425        return self
2426
2427    def find_cell(self, p):
2428        """Locate the cell that contains a point and return the cell ID."""
2429        cell = vtk.vtkTetra()
2430        cellId = vtk.mutable(0)
2431        tol2 = vtk.mutable(0)
2432        subId = vtk.mutable(0)
2433        pcoords = [0, 0, 0]
2434        weights = [0, 0, 0]
2435        cid = self._data.FindCell(p, cell, cellId, tol2, subId, pcoords, weights)
2436        return cid
2437
2438    def extract_cells_by_id(self, idlist, use_point_ids=False):
2439        """Return a new UGrid composed of the specified subset of indices."""
2440        selectionNode = vtk.vtkSelectionNode()
2441        if use_point_ids:
2442            selectionNode.SetFieldType(vtk.vtkSelectionNode.POINT)
2443            contcells = vtk.vtkSelectionNode.CONTAINING_CELLS()
2444            selectionNode.GetProperties().Set(contcells, 1)
2445        else:
2446            selectionNode.SetFieldType(vtk.vtkSelectionNode.CELL)
2447        selectionNode.SetContentType(vtk.vtkSelectionNode.INDICES)
2448        vidlist = utils.numpy2vtk(idlist, dtype="id")
2449        selectionNode.SetSelectionList(vidlist)
2450        selection = vtk.vtkSelection()
2451        selection.AddNode(selectionNode)
2452        es = vtk.vtkExtractSelection()
2453        es.SetInputData(0, self._data)
2454        es.SetInputData(1, selection)
2455        es.Update()
2456
2457        ug = vedo.ugrid.UGrid(es.GetOutput())
2458        pr = vtk.vtkProperty()
2459        pr.DeepCopy(self.GetProperty())
2460        ug.SetProperty(pr)
2461        ug.property = pr
2462
2463        # assign the same transformation to the copy
2464        ug.SetOrigin(self.GetOrigin())
2465        ug.SetScale(self.GetScale())
2466        ug.SetOrientation(self.GetOrientation())
2467        ug.SetPosition(self.GetPosition())
2468        ug.mapper().SetLookupTable(utils.ctf2lut(self))
2469        ug.pipeline = utils.OperationNode(
2470            "extract_cells_by_id",
2471            parents=[self],
2472            comment=f"#cells {self._data.GetNumberOfCells()}",
2473            c="#9e2a2b",
2474        )
2475        return ug

Base class for grid datasets.

Do not use this class to instantiate objects.
BaseGrid()
1878    def __init__(self):
1879        """Base class for grid datasets."""
1880
1881        super().__init__()
1882
1883        self._data = None
1884        self.useCells = True
1885        self._color = None
1886        self._alpha = [0, 1]
1887
1888        # -----------------------------------------------------------

Base class for grid datasets.

def tomesh(self, fill=True, shrink=1.0):
1896    def tomesh(self, fill=True, shrink=1.0):
1897        """
1898        Build a polygonal Mesh from the current Grid object.
1899
1900        If `fill=True`, the interior faces of all the cells are created.
1901        (setting a `shrink` value slightly smaller than the default 1.0
1902        can avoid flickering due to internal adjacent faces).
1903
1904        If `fill=False`, only the boundary faces will be generated.
1905        """
1906        gf = vtk.vtkGeometryFilter()
1907        if fill:
1908            sf = vtk.vtkShrinkFilter()
1909            sf.SetInputData(self._data)
1910            sf.SetShrinkFactor(shrink)
1911            sf.Update()
1912            gf.SetInputData(sf.GetOutput())
1913            gf.Update()
1914            poly = gf.GetOutput()
1915            if shrink == 1.0:
1916                cleanPolyData = vtk.vtkCleanPolyData()
1917                cleanPolyData.PointMergingOn()
1918                cleanPolyData.ConvertLinesToPointsOn()
1919                cleanPolyData.ConvertPolysToLinesOn()
1920                cleanPolyData.ConvertStripsToPolysOn()
1921                cleanPolyData.SetInputData(poly)
1922                cleanPolyData.Update()
1923                poly = cleanPolyData.GetOutput()
1924        else:
1925            gf.SetInputData(self._data)
1926            gf.Update()
1927            poly = gf.GetOutput()
1928
1929        msh = vedo.mesh.Mesh(poly).flat()
1930        msh.scalarbar = self.scalarbar
1931        lut = utils.ctf2lut(self)
1932        if lut:
1933            msh.mapper().SetLookupTable(lut)
1934        if self.useCells:
1935            msh.mapper().SetScalarModeToUseCellData()
1936        else:
1937            msh.mapper().SetScalarModeToUsePointData()
1938
1939        msh.pipeline = utils.OperationNode(
1940            "tomesh", parents=[self], comment=f"fill={fill}", c="#9e2a2b:#e9c46a"
1941        )
1942        return msh

Build a polygonal Mesh from the current Grid object.

If fill=True, the interior faces of all the cells are created. (setting a shrink value slightly smaller than the default 1.0 can avoid flickering due to internal adjacent faces).

If fill=False, only the boundary faces will be generated.

def cells(self):
1944    def cells(self):
1945        """
1946        Get the cells connectivity ids as a numpy array.
1947
1948        The output format is: `[[id0 ... idn], [id0 ... idm],  etc]`.
1949        """
1950        arr1d = utils.vtk2numpy(self._data.GetCells().GetData())
1951        if arr1d is None:
1952            return []
1953
1954        # Get cell connettivity ids as a 1D array. vtk format is:
1955        # [nids1, id0 ... idn, niids2, id0 ... idm,  etc].
1956        i = 0
1957        conn = []
1958        n = len(arr1d)
1959        if n:
1960            while True:
1961                cell = [arr1d[i + k] for k in range(1, arr1d[i] + 1)]
1962                conn.append(cell)
1963                i += arr1d[i] + 1
1964                if i >= n:
1965                    break
1966        return conn

Get the cells connectivity ids as a numpy array.

The output format is: [[id0 ... idn], [id0 ... idm], etc].

def color(self, col, alpha=None, vmin=None, vmax=None):
1968    def color(self, col, alpha=None, vmin=None, vmax=None):
1969        """
1970        Assign a color or a set of colors along the range of the scalar value.
1971        A single constant color can also be assigned.
1972        Any matplotlib color map name is also accepted, e.g. `volume.color('jet')`.
1973
1974        E.g.: say that your cells scalar runs from -3 to 6,
1975        and you want -3 to show red and 1.5 violet and 6 green, then just set:
1976
1977        `volume.color(['red', 'violet', 'green'])`
1978
1979        You can also assign a specific color to a aspecific value with eg.:
1980
1981        `volume.color([(0,'red', (0.5,'violet'), (1,'green')])`
1982
1983        Arguments:
1984            alpha : (list)
1985                use a list to specify transparencies along the scalar range
1986            vmin : (float)
1987                force the min of the scalar range to be this value
1988            vmax : (float)
1989                force the max of the scalar range to be this value
1990        """
1991        # supersedes method in Points, Mesh
1992
1993        if col is None:
1994            return self
1995        
1996        if vmin is None:
1997            vmin, _ = self._data.GetScalarRange()
1998        if vmax is None:
1999            _, vmax = self._data.GetScalarRange()
2000        ctf = self.GetProperty().GetRGBTransferFunction()
2001        ctf.RemoveAllPoints()
2002        self._color = col
2003
2004        if utils.is_sequence(col):
2005            if utils.is_sequence(col[0]) and len(col[0]) == 2:
2006                # user passing [(value1, color1), ...]
2007                for x, ci in col:
2008                    r, g, b = colors.get_color(ci)
2009                    ctf.AddRGBPoint(x, r, g, b)
2010                    # colors.printc('color at', round(x, 1),
2011                    #               'set to', colors.get_color_name((r, g, b)),
2012                    #               c='w', bold=0)
2013            else:
2014                # user passing [color1, color2, ..]
2015                for i, ci in enumerate(col):
2016                    r, g, b = colors.get_color(ci)
2017                    x = vmin + (vmax - vmin) * i / (len(col) - 1)
2018                    ctf.AddRGBPoint(x, r, g, b)
2019        elif isinstance(col, str):
2020            if col in colors.colors.keys() or col in colors.color_nicks.keys():
2021                r, g, b = colors.get_color(col)
2022                ctf.AddRGBPoint(vmin, r, g, b)  # constant color
2023                ctf.AddRGBPoint(vmax, r, g, b)
2024            else:  # assume it's a colormap
2025                for x in np.linspace(vmin, vmax, num=64, endpoint=True):
2026                    r, g, b = colors.color_map(x, name=col, vmin=vmin, vmax=vmax)
2027                    ctf.AddRGBPoint(x, r, g, b)
2028        elif isinstance(col, int):
2029            r, g, b = colors.get_color(col)
2030            ctf.AddRGBPoint(vmin, r, g, b)  # constant color
2031            ctf.AddRGBPoint(vmax, r, g, b)
2032        else:
2033            vedo.logger.warning(f"in color() unknown input type {type(col)}")
2034
2035        if alpha is not None:
2036            self.alpha(alpha, vmin=vmin, vmax=vmax)
2037        return self

Assign a color or a set of colors along the range of the scalar value. A single constant color can also be assigned. Any matplotlib color map name is also accepted, e.g. volume.color('jet').

E.g.: say that your cells scalar runs from -3 to 6, and you want -3 to show red and 1.5 violet and 6 green, then just set:

volume.color(['red', 'violet', 'green'])

You can also assign a specific color to a aspecific value with eg.:

volume.color([(0,'red', (0.5,'violet'), (1,'green')])

Arguments:
  • alpha : (list) use a list to specify transparencies along the scalar range
  • vmin : (float) force the min of the scalar range to be this value
  • vmax : (float) force the max of the scalar range to be this value
def alpha(self, alpha, vmin=None, vmax=None):
2039    def alpha(self, alpha, vmin=None, vmax=None):
2040        """
2041        Assign a set of tranparencies along the range of the scalar value.
2042        A single constant value can also be assigned.
2043
2044        E.g.: say `alpha=(0.0, 0.3, 0.9, 1)` and the scalar range goes from -10 to 150.
2045        Then all cells with a value close to -10 will be completely transparent, cells at 1/4
2046        of the range will get an alpha equal to 0.3 and voxels with value close to 150
2047        will be completely opaque.
2048
2049        As a second option one can set explicit (x, alpha_x) pairs to define the transfer function.
2050
2051        E.g.: say `alpha=[(-5, 0), (35, 0.4) (123,0.9)]` and the scalar range goes from -10 to 150.
2052        Then all cells below -5 will be completely transparent, cells with a scalar value of 35
2053        will get an opacity of 40% and above 123 alpha is set to 90%.
2054        """
2055        if vmin is None:
2056            vmin, _ = self._data.GetScalarRange()
2057        if vmax is None:
2058            _, vmax = self._data.GetScalarRange()
2059        otf = self.GetProperty().GetScalarOpacity()
2060        otf.RemoveAllPoints()
2061        self._alpha = alpha
2062
2063        if utils.is_sequence(alpha):
2064            alpha = np.array(alpha)
2065            if len(alpha.shape) == 1:  # user passing a flat list e.g. (0.0, 0.3, 0.9, 1)
2066                for i, al in enumerate(alpha):
2067                    xalpha = vmin + (vmax - vmin) * i / (len(alpha) - 1)
2068                    # Create transfer mapping scalar value to opacity
2069                    otf.AddPoint(xalpha, al)
2070                    # colors.printc("alpha at", round(xalpha, 1), "\tset to", al)
2071            elif len(alpha.shape) == 2:  # user passing [(x0,alpha0), ...]
2072                otf.AddPoint(vmin, alpha[0][1])
2073                for xalpha, al in alpha:
2074                    # Create transfer mapping scalar value to opacity
2075                    otf.AddPoint(xalpha, al)
2076                otf.AddPoint(vmax, alpha[-1][1])
2077
2078        else:
2079
2080            otf.AddPoint(vmin, alpha)  # constant alpha
2081            otf.AddPoint(vmax, alpha)
2082
2083        return self

Assign a set of tranparencies along the range of the scalar value. A single constant value can also be assigned.

E.g.: say alpha=(0.0, 0.3, 0.9, 1) and the scalar range goes from -10 to 150. Then all cells with a value close to -10 will be completely transparent, cells at 1/4 of the range will get an alpha equal to 0.3 and voxels with value close to 150 will be completely opaque.

As a second option one can set explicit (x, alpha_x) pairs to define the transfer function.

E.g.: say alpha=[(-5, 0), (35, 0.4) (123,0.9)] and the scalar range goes from -10 to 150. Then all cells below -5 will be completely transparent, cells with a scalar value of 35 will get an opacity of 40% and above 123 alpha is set to 90%.

def alpha_unit(self, u=None):
2085    def alpha_unit(self, u=None):
2086        """
2087        Defines light attenuation per unit length. Default is 1.
2088        The larger the unit length, the further light has to travel to attenuate the same amount.
2089
2090        E.g., if you set the unit distance to 0, you will get full opacity.
2091        It means that when light travels 0 distance it's already attenuated a finite amount.
2092        Thus, any finite distance should attenuate all light.
2093        The larger you make the unit distance, the more transparent the rendering becomes.
2094        """
2095        if u is None:
2096            return self.GetProperty().GetScalarOpacityUnitDistance()
2097        self.GetProperty().SetScalarOpacityUnitDistance(u)
2098        return self

Defines light attenuation per unit length. Default is 1. The larger the unit length, the further light has to travel to attenuate the same amount.

E.g., if you set the unit distance to 0, you will get full opacity. It means that when light travels 0 distance it's already attenuated a finite amount. Thus, any finite distance should attenuate all light. The larger you make the unit distance, the more transparent the rendering becomes.

def shrink(self, fraction=0.8):
2100    def shrink(self, fraction=0.8):
2101        """
2102        Shrink the individual cells to improve visibility.
2103
2104        ![](https://vedo.embl.es/images/feats/shrink_hex.png)
2105        """
2106        sf = vtk.vtkShrinkFilter()
2107        sf.SetInputData(self.inputdata())
2108        sf.SetShrinkFactor(fraction)
2109        sf.Update()
2110        self._update(sf.GetOutput())
2111        self.pipeline = utils.OperationNode(
2112            "shrink", comment=f"by {fraction}", parents=[self], c="#9e2a2b"
2113        )
2114        return self

Shrink the individual cells to improve visibility.

def isosurface(self, value=None, flying_edges=True):
2116    def isosurface(self, value=None, flying_edges=True):
2117        """
2118        Return an `Mesh` isosurface extracted from the `Volume` object.
2119
2120        Set `value` as single float or list of values to draw the isosurface(s).
2121        Use flying_edges for faster results (but sometimes can interfere with `smooth()`).
2122
2123        Examples:
2124            - [isosurfaces.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/isosurfaces.py)
2125
2126                ![](https://vedo.embl.es/images/volumetric/isosurfaces.png)
2127        """
2128        scrange = self._data.GetScalarRange()
2129
2130        if flying_edges:
2131            cf = vtk.vtkFlyingEdges3D()
2132            cf.InterpolateAttributesOn()
2133        else:
2134            cf = vtk.vtkContourFilter()
2135            cf.UseScalarTreeOn()
2136
2137        cf.SetInputData(self._data)
2138        cf.ComputeNormalsOn()
2139
2140        if utils.is_sequence(value):
2141            cf.SetNumberOfContours(len(value))
2142            for i, t in enumerate(value):
2143                cf.SetValue(i, t)
2144        else:
2145            if value is None:
2146                value = (2 * scrange[0] + scrange[1]) / 3.0
2147                # print("automatic isosurface value =", value)
2148            cf.SetValue(0, value)
2149
2150        cf.Update()
2151        poly = cf.GetOutput()
2152
2153        out = vedo.mesh.Mesh(poly, c=None).phong()
2154        out.mapper().SetScalarRange(scrange[0], scrange[1])
2155
2156        out.pipeline = utils.OperationNode(
2157            "isosurface",
2158            parents=[self],
2159            comment=f"#pts {out.inputdata().GetNumberOfPoints()}",
2160            c="#4cc9f0:#e9c46a",
2161        )
2162        return out

Return an Mesh isosurface extracted from the Volume object.

Set value as single float or list of values to draw the isosurface(s). Use flying_edges for faster results (but sometimes can interfere with smooth()).

Examples:
def legosurface( self, vmin=None, vmax=None, invert=False, boundary=False, array_name='input_scalars'):
2164    def legosurface(
2165        self, vmin=None, vmax=None, invert=False, boundary=False, array_name="input_scalars"
2166    ):
2167        """
2168        Represent an object - typically a `Volume` - as lego blocks (voxels).
2169        By default colors correspond to the volume's scalar.
2170        Returns an `Mesh` object.
2171
2172        Arguments:
2173            vmin : (float)
2174                the lower threshold, voxels below this value are not shown.
2175            vmax : (float)
2176                the upper threshold, voxels above this value are not shown.
2177            boundary : (bool)
2178                controls whether to include cells that are partially inside
2179            array_name : (int, str)
2180                name or index of the scalar array to be considered
2181
2182        Examples:
2183            - [legosurface.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/legosurface.py)
2184
2185                ![](https://vedo.embl.es/images/volumetric/56820682-da40e500-684c-11e9-8ea3-91cbcba24b3a.png)
2186        """
2187        dataset = vtk.vtkImplicitDataSet()
2188        dataset.SetDataSet(self._data)
2189        window = vtk.vtkImplicitWindowFunction()
2190        window.SetImplicitFunction(dataset)
2191
2192        srng = list(self._data.GetScalarRange())
2193        if vmin is not None:
2194            srng[0] = vmin
2195        if vmax is not None:
2196            srng[1] = vmax
2197        tol = 0.00001 * (srng[1] - srng[0])
2198        srng[0] -= tol
2199        srng[1] += tol
2200        window.SetWindowRange(srng)
2201
2202        extract = vtk.vtkExtractGeometry()
2203        extract.SetInputData(self._data)
2204        extract.SetImplicitFunction(window)
2205        extract.SetExtractInside(invert)
2206        extract.SetExtractBoundaryCells(boundary)
2207        extract.Update()
2208
2209        gf = vtk.vtkGeometryFilter()
2210        gf.SetInputData(extract.GetOutput())
2211        gf.Update()
2212
2213        m = vedo.mesh.Mesh(gf.GetOutput()).lw(0.1).flat()
2214        m.map_points_to_cells()
2215        m.celldata.select(array_name)
2216
2217        m.pipeline = utils.OperationNode(
2218            "legosurface", parents=[self], comment=f"array: {array_name}", c="#4cc9f0:#e9c46a"
2219        )
2220        return m

Represent an object - typically a Volume - as lego blocks (voxels). By default colors correspond to the volume's scalar. Returns an Mesh object.

Arguments:
  • vmin : (float) the lower threshold, voxels below this value are not shown.
  • vmax : (float) the upper threshold, voxels above this value are not shown.
  • boundary : (bool) controls whether to include cells that are partially inside
  • array_name : (int, str) name or index of the scalar array to be considered
Examples:
def cut_with_plane(self, origin=(0, 0, 0), normal='x'):
2222    def cut_with_plane(self, origin=(0, 0, 0), normal="x"):
2223        """
2224        Cut the object with the plane defined by a point and a normal.
2225
2226        Arguments:
2227            origin : (list)
2228                the cutting plane goes through this point
2229            normal : (list, str)
2230                normal vector to the cutting plane
2231        """
2232        strn = str(normal)
2233        if strn   ==  "x": normal = (1, 0, 0)
2234        elif strn ==  "y": normal = (0, 1, 0)
2235        elif strn ==  "z": normal = (0, 0, 1)
2236        elif strn == "-x": normal = (-1, 0, 0)
2237        elif strn == "-y": normal = (0, -1, 0)
2238        elif strn == "-z": normal = (0, 0, -1)
2239        plane = vtk.vtkPlane()
2240        plane.SetOrigin(origin)
2241        plane.SetNormal(normal)
2242        clipper = vtk.vtkClipDataSet()
2243        clipper.SetInputData(self._data)
2244        clipper.SetClipFunction(plane)
2245        clipper.GenerateClipScalarsOff()
2246        clipper.GenerateClippedOutputOff()
2247        clipper.SetValue(0)
2248        clipper.Update()
2249        cout = clipper.GetOutput()
2250        self._update(cout)
2251        self.pipeline = utils.OperationNode("cut_with_plane", parents=[self], c="#9e2a2b")
2252        return self

Cut the object with the plane defined by a point and a normal.

Arguments:
  • origin : (list) the cutting plane goes through this point
  • normal : (list, str) normal vector to the cutting plane
def cut_with_box(self, box):
2254    def cut_with_box(self, box):
2255        """
2256        Cut the grid with the specified bounding box.
2257
2258        Parameter box has format [xmin, xmax, ymin, ymax, zmin, zmax].
2259        If an object is passed, its bounding box are used.
2260
2261        Example:
2262            ```python
2263            from vedo import *
2264            tetmesh = TetMesh(dataurl+'limb_ugrid.vtk')
2265            tetmesh.color('rainbow')
2266            cu = Cube(side=500).x(500) # any Mesh works
2267            tetmesh.cut_with_box(cu).show(axes=1)
2268            ```
2269            ![](https://vedo.embl.es/images/feats/tet_cut_box.png)
2270        """
2271        bc = vtk.vtkBoxClipDataSet()
2272        bc.SetInputData(self._data)
2273        if isinstance(box, vtk.vtkProp):
2274            boxb = box.GetBounds()
2275        else:
2276            boxb = box
2277        bc.SetBoxClip(*boxb)
2278        bc.Update()
2279        self._update(bc.GetOutput())
2280        self.pipeline = utils.OperationNode("cut_with_box", parents=[self, box], c="#9e2a2b")
2281        return self

Cut the grid with the specified bounding box.

Parameter box has format [xmin, xmax, ymin, ymax, zmin, zmax]. If an object is passed, its bounding box are used.

Example:
from vedo import *
tetmesh = TetMesh(dataurl+'limb_ugrid.vtk')
tetmesh.color('rainbow')
cu = Cube(side=500).x(500) # any Mesh works
tetmesh.cut_with_box(cu).show(axes=1)

def cut_with_mesh(self, mesh, invert=False, whole_cells=False, only_boundary=False):
2283    def cut_with_mesh(self, mesh, invert=False, whole_cells=False, only_boundary=False):
2284        """
2285        Cut a UGrid, TetMesh or Volume with a Mesh.
2286
2287        Use `invert` to return cut off part of the input object.
2288        """
2289        polymesh = mesh.polydata()
2290        ug = self._data
2291
2292        ippd = vtk.vtkImplicitPolyDataDistance()
2293        ippd.SetInput(polymesh)
2294
2295        if whole_cells or only_boundary:
2296            clipper = vtk.vtkExtractGeometry()
2297            clipper.SetInputData(ug)
2298            clipper.SetImplicitFunction(ippd)
2299            clipper.SetExtractInside(not invert)
2300            clipper.SetExtractBoundaryCells(False)
2301            if only_boundary:
2302                clipper.SetExtractBoundaryCells(True)
2303                clipper.SetExtractOnlyBoundaryCells(True)
2304        else:
2305            signedDistances = vtk.vtkFloatArray()
2306            signedDistances.SetNumberOfComponents(1)
2307            signedDistances.SetName("SignedDistances")
2308            for pointId in range(ug.GetNumberOfPoints()):
2309                p = ug.GetPoint(pointId)
2310                signedDistance = ippd.EvaluateFunction(p)
2311                signedDistances.InsertNextValue(signedDistance)
2312            ug.GetPointData().AddArray(signedDistances)
2313            ug.GetPointData().SetActiveScalars("SignedDistances")
2314            clipper = vtk.vtkClipDataSet()
2315            clipper.SetInputData(ug)
2316            clipper.SetInsideOut(not invert)
2317            clipper.SetValue(0.0)
2318
2319        clipper.Update()
2320        cug = clipper.GetOutput()
2321
2322        if ug.GetCellData().GetScalars():  # not working
2323            scalname = ug.GetCellData().GetScalars().GetName()
2324            if scalname:  # not working
2325                if self.useCells:
2326                    self.celldata.select(scalname)
2327                else:
2328                    self.pointdata.select(scalname)
2329
2330        self._update(cug)
2331        self.pipeline = utils.OperationNode("cut_with_mesh", parents=[self, mesh], c="#9e2a2b")
2332        return self

Cut a UGrid, TetMesh or Volume with a Mesh.

Use invert to return cut off part of the input object.

def extract_cells_on_plane(self, origin, normal):
2334    def extract_cells_on_plane(self, origin, normal):
2335        """
2336        Extract cells that are lying of the specified surface.
2337        """
2338        bf = vtk.vtk3DLinearGridCrinkleExtractor()
2339        bf.SetInputData(self._data)
2340        bf.CopyPointDataOn()
2341        bf.CopyCellDataOn()
2342        bf.RemoveUnusedPointsOff()
2343
2344        plane = vtk.vtkPlane()
2345        plane.SetOrigin(origin)
2346        plane.SetNormal(normal)
2347        bf.SetImplicitFunction(plane)
2348        bf.Update()
2349
2350        self._update(bf.GetOutput())
2351        self.pipeline = utils.OperationNode(
2352            "extract_cells_on_plane",
2353            parents=[self],
2354            comment=f"#cells {self._data.GetNumberOfCells()}",
2355            c="#9e2a2b",
2356        )
2357        return self

Extract cells that are lying of the specified surface.

def extract_cells_on_sphere(self, center, radius):
2359    def extract_cells_on_sphere(self, center, radius):
2360        """
2361        Extract cells that are lying of the specified surface.
2362        """
2363        bf = vtk.vtk3DLinearGridCrinkleExtractor()
2364        bf.SetInputData(self._data)
2365        bf.CopyPointDataOn()
2366        bf.CopyCellDataOn()
2367        bf.RemoveUnusedPointsOff()
2368
2369        sph = vtk.vtkSphere()
2370        sph.SetRadius(radius)
2371        sph.SetCenter(center)
2372        bf.SetImplicitFunction(sph)
2373        bf.Update()
2374
2375        self._update(bf.GetOutput())
2376        self.pipeline = utils.OperationNode(
2377            "extract_cells_on_sphere",
2378            parents=[self],
2379            comment=f"#cells {self._data.GetNumberOfCells()}",
2380            c="#9e2a2b",
2381        )
2382        return self

Extract cells that are lying of the specified surface.

def extract_cells_on_cylinder(self, center, axis, radius):
2384    def extract_cells_on_cylinder(self, center, axis, radius):
2385        """
2386        Extract cells that are lying of the specified surface.
2387        """
2388        bf = vtk.vtk3DLinearGridCrinkleExtractor()
2389        bf.SetInputData(self._data)
2390        bf.CopyPointDataOn()
2391        bf.CopyCellDataOn()
2392        bf.RemoveUnusedPointsOff()
2393
2394        cyl = vtk.vtkCylinder()
2395        cyl.SetRadius(radius)
2396        cyl.SetCenter(center)
2397        cyl.SetAxis(axis)
2398        bf.SetImplicitFunction(cyl)
2399        bf.Update()
2400
2401        self.pipeline = utils.OperationNode(
2402            "extract_cells_on_cylinder",
2403            parents=[self],
2404            comment=f"#cells {self._data.GetNumberOfCells()}",
2405            c="#9e2a2b",
2406        )
2407        self._update(bf.GetOutput())
2408        return self

Extract cells that are lying of the specified surface.

def clean(self):
2410    def clean(self):
2411        """
2412        Cleanup unused points and empty cells
2413        """
2414        cl = vtk.vtkStaticCleanUnstructuredGrid()
2415        cl.SetInputData(self._data)
2416        cl.RemoveUnusedPointsOn()
2417        cl.ProduceMergeMapOff()
2418        cl.AveragePointDataOff()
2419        cl.Update()
2420
2421        self._update(cl.GetOutput())
2422        self.pipeline = utils.OperationNode(
2423            "clean", parents=[self], comment=f"#cells {self._data.GetNumberOfCells()}", c="#9e2a2b"
2424        )
2425        return self

Cleanup unused points and empty cells

def find_cell(self, p):
2427    def find_cell(self, p):
2428        """Locate the cell that contains a point and return the cell ID."""
2429        cell = vtk.vtkTetra()
2430        cellId = vtk.mutable(0)
2431        tol2 = vtk.mutable(0)
2432        subId = vtk.mutable(0)
2433        pcoords = [0, 0, 0]
2434        weights = [0, 0, 0]
2435        cid = self._data.FindCell(p, cell, cellId, tol2, subId, pcoords, weights)
2436        return cid

Locate the cell that contains a point and return the cell ID.

def extract_cells_by_id(self, idlist, use_point_ids=False):
2438    def extract_cells_by_id(self, idlist, use_point_ids=False):
2439        """Return a new UGrid composed of the specified subset of indices."""
2440        selectionNode = vtk.vtkSelectionNode()
2441        if use_point_ids:
2442            selectionNode.SetFieldType(vtk.vtkSelectionNode.POINT)
2443            contcells = vtk.vtkSelectionNode.CONTAINING_CELLS()
2444            selectionNode.GetProperties().Set(contcells, 1)
2445        else:
2446            selectionNode.SetFieldType(vtk.vtkSelectionNode.CELL)
2447        selectionNode.SetContentType(vtk.vtkSelectionNode.INDICES)
2448        vidlist = utils.numpy2vtk(idlist, dtype="id")
2449        selectionNode.SetSelectionList(vidlist)
2450        selection = vtk.vtkSelection()
2451        selection.AddNode(selectionNode)
2452        es = vtk.vtkExtractSelection()
2453        es.SetInputData(0, self._data)
2454        es.SetInputData(1, selection)
2455        es.Update()
2456
2457        ug = vedo.ugrid.UGrid(es.GetOutput())
2458        pr = vtk.vtkProperty()
2459        pr.DeepCopy(self.GetProperty())
2460        ug.SetProperty(pr)
2461        ug.property = pr
2462
2463        # assign the same transformation to the copy
2464        ug.SetOrigin(self.GetOrigin())
2465        ug.SetScale(self.GetScale())
2466        ug.SetOrientation(self.GetOrientation())
2467        ug.SetPosition(self.GetPosition())
2468        ug.mapper().SetLookupTable(utils.ctf2lut(self))
2469        ug.pipeline = utils.OperationNode(
2470            "extract_cells_by_id",
2471            parents=[self],
2472            comment=f"#cells {self._data.GetNumberOfCells()}",
2473            c="#9e2a2b",
2474        )
2475        return ug

Return a new UGrid composed of the specified subset of indices.

def probe_points(dataset, pts):
2485def probe_points(dataset, pts):
2486    """
2487    Takes a `Volume` (or any other vtk data set)
2488    and probes its scalars at the specified points in space.
2489
2490    Note that a mask is also output with valid/invalid points which can be accessed
2491    with `mesh.pointdata['vtkValidPointMask']`.
2492
2493    Examples:
2494        - [probe_points.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/probe_points.py)
2495
2496            ![](https://vedo.embl.es/images/volumetric/probePoints.png)
2497    """
2498    if isinstance(pts, vedo.pointcloud.Points):
2499        pts = pts.points()
2500
2501    def _readpoints():
2502        output = src.GetPolyDataOutput()
2503        points = vtk.vtkPoints()
2504        for p in pts:
2505            x, y, z = p
2506            points.InsertNextPoint(x, y, z)
2507        output.SetPoints(points)
2508
2509        cells = vtk.vtkCellArray()
2510        cells.InsertNextCell(len(pts))
2511        for i in range(len(pts)):
2512            cells.InsertCellPoint(i)
2513        output.SetVerts(cells)
2514
2515    src = vtk.vtkProgrammableSource()
2516    src.SetExecuteMethod(_readpoints)
2517    src.Update()
2518    img = _getinput(dataset)
2519    probeFilter = vtk.vtkProbeFilter()
2520    probeFilter.SetSourceData(img)
2521    probeFilter.SetInputConnection(src.GetOutputPort())
2522    probeFilter.Update()
2523    poly = probeFilter.GetOutput()
2524    pm = vedo.mesh.Mesh(poly)
2525    pm.name = "ProbePoints"
2526    pm.pipeline = utils.OperationNode("probe_points", parents=[dataset])
2527    return pm

Takes a Volume (or any other vtk data set) and probes its scalars at the specified points in space.

Note that a mask is also output with valid/invalid points which can be accessed with mesh.pointdata['vtkValidPointMask'].

Examples:
def probe_line(dataset, p1, p2, res=100):
2530def probe_line(dataset, p1, p2, res=100):
2531    """
2532    Takes a `Volume`  (or any other vtk data set)
2533    and probes its scalars along a line defined by 2 points `p1` and `p2`.
2534
2535    Note that a mask is also output with valid/invalid points which can be accessed
2536    with `mesh.pointdata['vtkValidPointMask']`.
2537
2538    Use `res` to set the nr of points along the line
2539
2540    Examples:
2541        - [probe_line1.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/probe_line1.py)
2542        - [probe_line2.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/probe_line2.py)
2543
2544            ![](https://vedo.embl.es/images/volumetric/probeLine2.png)
2545    """
2546    line = vtk.vtkLineSource()
2547    line.SetResolution(res)
2548    line.SetPoint1(p1)
2549    line.SetPoint2(p2)
2550    img = _getinput(dataset)
2551    probeFilter = vtk.vtkProbeFilter()
2552    probeFilter.SetSourceData(img)
2553    probeFilter.SetInputConnection(line.GetOutputPort())
2554    probeFilter.Update()
2555    poly = probeFilter.GetOutput()
2556    lnn = vedo.mesh.Mesh(poly)
2557    lnn.name = "ProbeLine"
2558    lnn.pipeline = utils.OperationNode("probe_line", parents=[dataset])
2559    return lnn

Takes a Volume (or any other vtk data set) and probes its scalars along a line defined by 2 points p1 and p2.

Note that a mask is also output with valid/invalid points which can be accessed with mesh.pointdata['vtkValidPointMask'].

Use res to set the nr of points along the line

Examples:
def probe_plane(dataset, origin=(0, 0, 0), normal=(1, 0, 0)):
2562def probe_plane(dataset, origin=(0, 0, 0), normal=(1, 0, 0)):
2563    """
2564    Takes a `Volume` (or any other vtk data set)
2565    and probes its scalars on a plane defined by a point and a normal.
2566
2567    Examples:
2568        - [slice_plane1.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/slice_plane1.py)
2569        - [slice_plane2.py](https://github.com/marcomusy/vedo/tree/master/examples/volumetric/slice_plane2.py)
2570
2571            ![](https://vedo.embl.es/images/volumetric/slicePlane2.png)
2572    """
2573    img = _getinput(dataset)
2574    plane = vtk.vtkPlane()
2575    plane.SetOrigin(origin)
2576    plane.SetNormal(normal)
2577    planeCut = vtk.vtkCutter()
2578    planeCut.SetInputData(img)
2579    planeCut.SetCutFunction(plane)
2580    planeCut.Update()
2581    poly = planeCut.GetOutput()
2582    cutmesh = vedo.mesh.Mesh(poly)
2583    cutmesh.name = "ProbePlane"
2584    cutmesh.pipeline = utils.OperationNode("probe_plane", parents=[dataset])
2585    return cutmesh

Takes a Volume (or any other vtk data set) and probes its scalars on a plane defined by a point and a normal.

Examples: