Module vedo.io

Submodule to read/write meshes and other objects in different formats, and other I/O functionalities.

Expand source code
import glob
import os
import time
from tempfile import NamedTemporaryFile, TemporaryDirectory

import numpy as np
import vtk
import vedo
from vedo import settings
from vedo import colors
from vedo import utils
from vedo.assembly import Assembly
from vedo.picture import Picture
from vedo.pointcloud import Points
from vedo.mesh import Mesh
from vedo.volume import Volume

__doc__ = """
Submodule to read/write meshes and other objects in different formats,
and other I/O functionalities.
"""

__all__ = [
    "load",
    "download",
    "gunzip",
    "loadStructuredPoints",
    "loadStructuredGrid",
    "loadRectilinearGrid",
    "loadUnStructuredGrid",
    "loadTransform",
    "writeTransform",
    "write",
    "exportWindow",
    "importWindow",
    "screenshot",
    "ask",
    "Video",
]


# example web page for X3D
_x3d_html = """
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title> vedo with x3d </title>

  <!-- THESE ARE THE RELEVANT LINES: -->
  <script src='https://www.x3dom.org/download/x3dom.js'> </script>
  <link rel='stylesheet' type='text/css' href='https://www.x3dom.org/download/x3dom.css'/>

  <style>
     table, td, th { border: 1px solid black; background-color: powderblue;}
     table {width: 70%; border-collapse: collapse;}
     table th {width: 35%;}
  </style>
</head>

<body style="font-family: Verdana">
  <h1>Example html generated by vedo</h1>
  This example loads a 3D scene from file ~fileoutput generated by
  <a href="https://github.com/marcomusy/vedo">vedo</a>
  (see <a href="https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py">export_x3d.py</a>).
  <br><br>


  <!-- THESE ARE THE RELEVANT LINES: -->
  <x3d width='~widthpx' height='~heightpx'>
     <scene>
        <Inline url="~fileoutput"> </Inline>
     </scene>
  </x3d>

  <h3>Nothing shows up above this line?</h3>
  Enable your browser to load local files:
  <br><b>Firefox</b>: type <code>about:config</code> in the URL bar and
  change <code>privacy.file_unique_origin</code> from <code>True</code> to <code>False</code>
  <br><b>Chrome</b>: from terminal type:
  <code>google-chrome --enable-webgl --allow-file-access-from-files</code>
  (see <a href="https://cmatskas.com/interacting-with-local-data-files-using-chrome/">here</a>)

  <br>
  <h3>Controls:</h3>
  <h4><strong>Examine Mode (activate with key 'e'):</strong></h4>
  <table>
     <tbody>
        <tr class="even description">
           <th>Button</th>
           <th>Function</th>
        </tr>
        <tr>
           <td>Left Button / Left Button + Shift</td>
           <td>Rotate</td>
        </tr>
        <tr>
           <td>Mid Button / Left Button + Ctl</td>
           <td>Pan</td>
        </tr>
        <tr>
           <td>Right Button / Wheel / Left Button + Alt</td>
           <td>Zoom</td>
        </tr>
        <tr>
           <td>Left double click</td>
           <td>Set center of rotation</td>
        </tr>
     </tbody>
  </table>
  <h4><strong>Walk Mode (activate with key 'w'):</strong></h4>
  <table>
     <tbody>
        <tr class="even description">
           <th>Button</th>
           <th>Function</th>
        </tr>
        <tr>
           <td>Left Button</td>
           <td>Move forward</td>
        </tr>
        <tr>
           <td>Right Button</td>
           <td>Move backward</td>
        </tr>
     </tbody>
  </table>
  <h4><strong>Fly Mode (activate with key 'f'):</strong></h4>
  <table>
     <tbody>
        <tr class="even description">
           <th>Button</th>
           <th>Function</th>
        </tr>
        <tr>
           <td>Left Button</td>
           <td>Move forward</td>
        </tr>
        <tr>
           <td>Right Button</td>
           <td>Move backward</td>
        </tr>
     </tbody>
  </table>
  <h3>Non-interactive camera movement</h3>
  <table>
     <tbody>
        <tr class="even description">
           <th>Key</th>
           <th>Function</th>
        </tr>
        <tr>
           <td>r</td>
           <td>reset view</td>
        </tr>
        <tr>
           <td>a</td>
           <td>show all</td>
        </tr>
        <tr>
           <td>u</td>
           <td>upright</td>
        </tr>
     </tbody>
  </table>
</body>
</html>
"""


def load(inputobj, unpack=True, force=False):
    """
    Load ``Mesh``, ``Volume`` and ``Picture`` objects from file or from the web.

    The output will depend on the file extension. See examples below.
    Unzip is made on the fly, if file ends with `.gz`.
    Can load an object directly from a URL address.

    Parameters
    ----------

    unpack : bool
        unpack MultiBlockData into a flat list of objects.

    force : bool
        when downloading a file ignore any previous cached downloads and force a new one.

    Example:
        .. code-block:: python

            from vedo import dataurl, load, show

            # Return a Mesh object
            g = load(dataurl+'250.vtk')
            show(g)

            # Return a list of 2 meshes
            g = load([dataurl+'250.vtk', dataurl+'270.vtk'])
            show(g)

            # Return a list of meshes by reading all files in a directory
            # (if directory contains DICOM files then a Volume is returned)
            g = load('mydicomdir/')
            show(g)

            # Download a file from a URL address and unzip it on the fly
            g = load('https://vedo.embl.es/examples/panther.stl.gz')
            show(g)
    """
    acts = []
    if utils.isSequence(inputobj):
        flist = inputobj
    elif isinstance(inputobj, str) and inputobj.startswith("https://"):
        flist = [inputobj]
    else:
        flist = sorted(glob.glob(inputobj))

    for fod in flist:

        if fod.startswith("https://"):
            fod = download(fod, force=force, verbose=False)

        if os.path.isfile(fod):  ### it's a file

            if fod.endswith(".gz"):
                fod = gunzip(fod)

            a = _load_file(fod, unpack)
            acts.append(a)

        elif os.path.isdir(fod):  ### it's a directory or DICOM
            flist = os.listdir(fod)
            if ".dcm" in flist[0]:  ### it's DICOM
                reader = vtk.vtkDICOMImageReader()
                reader.SetDirectoryName(fod)
                reader.Update()
                image = reader.GetOutput()
                actor = Volume(image)

                actor.info["PixelSpacing"] = reader.GetPixelSpacing()
                actor.info["Width"] = reader.GetWidth()
                actor.info["Height"] = reader.GetHeight()
                actor.info["PositionPatient"] = reader.GetImagePositionPatient()
                actor.info["OrientationPatient"] = reader.GetImageOrientationPatient()
                actor.info["BitsAllocated"] = reader.GetBitsAllocated()
                actor.info["PixelRepresentation"] = reader.GetPixelRepresentation()
                actor.info["NumberOfComponents"] = reader.GetNumberOfComponents()
                actor.info["TransferSyntaxUID"] = reader.GetTransferSyntaxUID()
                actor.info["RescaleSlope"] = reader.GetRescaleSlope()
                actor.info["RescaleOffset"] = reader.GetRescaleOffset()
                actor.info["PatientName"] = reader.GetPatientName()
                actor.info["StudyUID"] = reader.GetStudyUID()
                actor.info["StudyID"] = reader.GetStudyID()
                actor.info["GantryAngle"] = reader.GetGantryAngle()

                acts.append(actor)

            else:  ### it's a normal directory
                utils.humansort(flist)
                for ifile in flist:
                    a = _load_file(fod + "/" + ifile, unpack)
                    acts.append(a)
        else:
            vedo.logger.error(f"in load(), cannot find {fod}")

    if len(acts) == 1:
        if "numpy" in str(type(acts[0])):
            return acts[0]
        if not acts[0]:
            vedo.logger.error(f"in load(), cannot load {inputobj}")
        return acts[0]

    elif len(acts) == 0:
        vedo.logger.error(f"in load(), cannot load {inputobj}")
        return None

    else:
        return acts


def _load_file(filename, unpack):
    fl = filename.lower()

    ################################################################# other formats:
    if fl.endswith(".xml") or fl.endswith(".xml.gz") or fl.endswith(".xdmf"):
        # Fenics tetrahedral file
        actor = loadDolfin(filename)
    elif fl.endswith(".neutral") or fl.endswith(".neu"):  # neutral tetrahedral file
        actor = loadNeutral(filename)
    elif fl.endswith(".gmsh"):  # gmesh file
        actor = loadGmesh(filename)
    elif fl.endswith(".pcd"):  # PCL point-cloud format
        actor = loadPCD(filename)
        actor.GetProperty().SetPointSize(2)
    elif fl.endswith(".off"):
        actor = loadOFF(filename)
    elif fl.endswith(".3ds"):  # 3ds format
        actor = load3DS(filename)
    elif fl.endswith(".wrl"):
        importer = vtk.vtkVRMLImporter()
        importer.SetFileName(filename)
        importer.Read()
        importer.Update()
        actors = importer.GetRenderer().GetActors()  # vtkActorCollection
        actors.InitTraversal()
        wacts = []
        for i in range(actors.GetNumberOfItems()):
            act = actors.GetNextActor()
            wacts.append(act)
        actor = Assembly(wacts)

        ################################################################# volumetric:
    elif (
        fl.endswith(".tif")
        or fl.endswith(".tiff")
        or fl.endswith(".slc")
        or fl.endswith(".vti")
        or fl.endswith(".mhd")
        or fl.endswith(".nrrd")
        or fl.endswith(".nii")
        or fl.endswith(".dem")
    ):
        img = loadImageData(filename)
        actor = Volume(img)

        ################################################################# 2D images:
    elif (
        fl.endswith(".png")
        or fl.endswith(".jpg")
        or fl.endswith(".bmp")
        or fl.endswith(".jpeg")
        or fl.endswith(".gif")
    ):
        if ".png" in fl:
            picr = vtk.vtkPNGReader()
        elif ".jpg" in fl or ".jpeg" in fl:
            picr = vtk.vtkJPEGReader()
        elif ".bmp" in fl:
            picr = vtk.vtkBMPReader()
        elif ".gif" in fl:
            from PIL import Image, ImageSequence

            img = Image.open(filename)
            frames = []
            for frame in ImageSequence.Iterator(img):
                a = np.array(frame.convert("RGB").getdata(), dtype=np.uint8)
                a = a.reshape(frame.size[1], frame.size[0], 3)
                frames.append(Picture(a))
            return frames

        picr.SetFileName(filename)
        picr.Update()
        actor = Picture(picr.GetOutput())  # object derived from vtk.vtkImageActor()

        ################################################################# multiblock:
    elif fl.endswith(".vtm") or fl.endswith(".vtmb"):
        read = vtk.vtkXMLMultiBlockDataReader()
        read.SetFileName(filename)
        read.Update()
        mb = read.GetOutput()
        if unpack:
            acts = []
            for i in range(mb.GetNumberOfBlocks()):
                b =  mb.GetBlock(i)
                if isinstance(b, (vtk.vtkPolyData,
                                  vtk.vtkUnstructuredGrid,
                                  vtk.vtkStructuredGrid,
                                  vtk.vtkRectilinearGrid)):
                    acts.append(Mesh(b))
                elif isinstance(b, vtk.vtkImageData):
                    acts.append(Volume(b))
                elif isinstance(b, vtk.vtkUnstructuredGrid):
                    acts.append(vedo.UGrid(b))
            return acts
        else:
            return mb

        ################################################################# numpy:
    elif fl.endswith(".npy") or fl.endswith(".npz"):
        acts = loadnumpy(filename)

        if unpack is False:
            return Assembly(acts)
        return acts

    elif fl.endswith(".geojson"):
        return loadGeoJSON(filename)

    elif fl.endswith(".pvd"):
        return loadPVD(filename)

    elif fl.endswith(".pdb"):
        return loadPDB(filename)

        ################################################################# polygonal mesh:
    else:
        if fl.endswith(".vtk"):  # read all legacy vtk types

            # output can be:
            # PolyData, StructuredGrid, StructuredPoints, UnstructuredGrid, RectilinearGrid
            reader = vtk.vtkDataSetReader()
            reader.ReadAllScalarsOn()
            reader.ReadAllVectorsOn()
            reader.ReadAllTensorsOn()
            reader.ReadAllFieldsOn()
            reader.ReadAllNormalsOn()
            reader.ReadAllColorScalarsOn()

        elif fl.endswith(".ply"):
            reader = vtk.vtkPLYReader()
        elif fl.endswith(".obj"):
            reader = vtk.vtkOBJReader()
        elif fl.endswith(".stl"):
            reader = vtk.vtkSTLReader()
        elif fl.endswith(".byu") or fl.endswith(".g"):
            reader = vtk.vtkBYUReader()
        elif fl.endswith(".foam"):  # OpenFoam
            reader = vtk.vtkOpenFOAMReader()
        elif fl.endswith(".pvd"):
            reader = vtk.vtkXMLGenericDataObjectReader()
        elif fl.endswith(".vtp"):
            reader = vtk.vtkXMLPolyDataReader()
        elif fl.endswith(".vts"):
            reader = vtk.vtkXMLStructuredGridReader()
        elif fl.endswith(".vtu"):
            reader = vtk.vtkXMLUnstructuredGridReader()
        elif fl.endswith(".vtr"):
            reader = vtk.vtkXMLRectilinearGridReader()
        elif fl.endswith(".pvtk"):
            reader = vtk.vtkPDataSetReader()
        elif fl.endswith(".pvtr"):
            reader = vtk.vtkXMLPRectilinearGridReader()
        elif fl.endswith("pvtu"):
            reader = vtk.vtkXMLPUnstructuredGridReader()
        elif fl.endswith(".txt") or fl.endswith(".xyz"):
            reader = vtk.vtkParticleReader()  # (format is x, y, z, scalar)
        elif fl.endswith(".facet"):
            reader = vtk.vtkFacetReader()
        else:
            return None

        reader.SetFileName(filename)
        reader.Update()
        routput = reader.GetOutput()

        if not routput:
            vedo.logger.error(f"unable to load {filename}")
            return None

        if isinstance(routput, vtk.vtkUnstructuredGrid):
            actor = vedo.TetMesh(routput)

        else:
            actor = Mesh(routput)
            if fl.endswith(".txt") or fl.endswith(".xyz"):
                actor.GetProperty().SetPointSize(4)

    actor.filename = filename
    actor.fileSize, actor.created = fileInfo(filename)
    return actor


def download(url, force=False, verbose=True):
    """Retrieve a file from a url, save it locally and return its path.
    Use ``force`` to force reload and discard cache copies."""

    if not url.startswith("https://"):
        vedo.logger.error(f"Invalid URL (must start with https):\n{url}")
        return url
    url = url.replace("www.dropbox", "dl.dropbox")

    if "github.com" in url:
        url = url.replace("/blob/", "/raw/")

    basename = os.path.basename(url)

    if "?" in basename:
        basename = basename.split("?")[0]

    tmp_file = NamedTemporaryFile(delete=False)
    tmp_file.name = os.path.join(
        os.path.dirname(tmp_file.name), os.path.basename(basename)
    )

    if not force and os.path.exists(tmp_file.name):
        if verbose:
            colors.printc("reusing cached file:", tmp_file.name)
            # colors.printc("     (use force=True to force a new download)")
        return tmp_file.name

    try:
        from urllib.request import urlopen, Request

        req = Request(url, headers={"User-Agent": "Mozilla/5.0"})
        if verbose:
            colors.printc('reading', basename, 'from', url.split('/')[2][:40],'...', end='')
    except ImportError:
        import urllib2
        import contextlib

        urlopen = lambda url_: contextlib.closing(urllib2.urlopen(url_))
        req = url
        if verbose:
            colors.printc('reading', basename, 'from',
                          url.split('/')[2][:40],'...', end='')

    with urlopen(req) as response, open(tmp_file.name, "wb") as output:
        output.write(response.read())

    if verbose: colors.printc(' done.')
    return tmp_file.name


def gunzip(filename):
    """Unzip a `.gz` file to a temporary file and returns its path."""
    if not filename.endswith(".gz"):
        # colors.printc("gunzip() error: file must end with .gz", c='r')
        return filename
    import gzip

    tmp_file = NamedTemporaryFile(delete=False)
    tmp_file.name = os.path.join(
        os.path.dirname(tmp_file.name), os.path.basename(filename).replace(".gz", "")
    )
    inF = gzip.open(filename, "rb")
    with open(tmp_file.name, "wb") as outF:
        outF.write(inF.read())
    inF.close()
    return tmp_file.name


def fileInfo(file_path):
    """Return the file size and creation time of input file"""
    sz, created = "", ""
    if os.path.isfile(file_path):
        file_info = os.stat(file_path)
        num = file_info.st_size
        for x in ["B", "KB", "MB", "GB", "TB"]:
            if num < 1024.0:
                break
            num /= 1024.0
        sz = "%3.1f%s" % (num, x)
        created = time.ctime(os.path.getmtime(file_path))
    return sz, created


###################################################################
def loadStructuredPoints(filename):
    """Load and return a ``vtkStructuredPoints`` object from file."""
    reader = vtk.vtkStructuredPointsReader()
    reader.SetFileName(filename)
    reader.Update()
    return reader.GetOutput()


def loadStructuredGrid(filename):
    """Load and return a ``vtkStructuredGrid`` object from file."""
    if filename.endswith(".vts"):
        reader = vtk.vtkXMLStructuredGridReader()
    else:
        reader = vtk.vtkStructuredGridReader()
    reader.SetFileName(filename)
    reader.Update()
    return reader.GetOutput()


def loadUnStructuredGrid(filename):
    """Load and return a ``vtkunStructuredGrid`` object from file."""
    if filename.endswith(".vtu"):
        reader = vtk.vtkXMLUnstructuredGridReader()
    else:
        reader = vtk.vtkUnstructuredGridReader()
    reader.SetFileName(filename)
    reader.Update()
    return reader.GetOutput()


def loadRectilinearGrid(filename):
    """Load and return a ``vtkRectilinearGrid`` object from file."""
    if filename.endswith(".vtr"):
        reader = vtk.vtkXMLRectilinearGridReader()
    else:
        reader = vtk.vtkRectilinearGridReader()
    reader.SetFileName(filename)
    reader.Update()
    return reader.GetOutput()


def loadXMLData(filename):
    """Read any type of vtk data object encoded in XML format."""
    reader = vtk.vtkXMLGenericDataObjectReader()
    reader.SetFileName(filename)
    reader.Update()
    return reader.GetOutput()


###################################################################
def load3DS(filename):
    """Load ``3DS`` file format from file. Return an ``Assembly(vtkAssembly)`` object."""
    renderer = vtk.vtkRenderer()
    renWin = vtk.vtkRenderWindow()
    renWin.AddRenderer(renderer)

    importer = vtk.vtk3DSImporter()
    importer.SetFileName(filename)
    importer.ComputeNormalsOn()
    importer.SetRenderWindow(renWin)
    importer.Update()

    actors = renderer.GetActors()  # vtkActorCollection
    acts = []
    for i in range(actors.GetNumberOfItems()):
        a = actors.GetItemAsObject(i)
        acts.append(a)
    del renWin
    return Assembly(acts)


def loadOFF(filename):
    """Read the OFF file format (polygonal mesh)."""
    with open(filename, "r") as f:
        lines = f.readlines()

    vertices = []
    faces = []
    NumberOfVertices = None
    i = -1
    for text in lines:
        if len(text) == 0:
            continue
        if text == "\n":
            continue
        if "#" in text:
            continue
        if "OFF" in text:
            continue

        ts = text.split()
        n = len(ts)

        if not NumberOfVertices and n > 1:
            NumberOfVertices, NumberOfFaces = int(ts[0]), int(ts[1])
            continue
        i += 1

        if i < NumberOfVertices and n == 3:
            x, y, z = float(ts[0]), float(ts[1]), float(ts[2])
            vertices.append([x, y, z])

        ids = []
        if NumberOfVertices <= i < (NumberOfVertices + NumberOfFaces + 1) and n > 2:
            ids += [int(xx) for xx in ts[1:]]
            faces.append(ids)

    return Mesh(utils.buildPolyData(vertices, faces))


def loadGeoJSON(filename):
    """Load GeoJSON files."""
    jr = vtk.vtkGeoJSONReader()
    jr.SetFileName(filename)
    jr.Update()
    return Mesh(jr.GetOutput())


def loadDolfin(filename, exterior=False):
    """Reads a `Fenics/Dolfin` file format (.xml or .xdmf).
    Return an ``Mesh`` object."""
    import dolfin

    if filename.lower().endswith(".xdmf"):
        f = dolfin.XDMFFile(filename)
        m = dolfin.Mesh()
        f.read(m)
    else:
        m = dolfin.Mesh(filename)

    bm = dolfin.BoundaryMesh(m, "exterior")

    if exterior:
        poly = utils.buildPolyData(bm.coordinates(), bm.cells(), fast=True, tetras=True)
    else:
        polyb = utils.buildPolyData(bm.coordinates(), bm.cells(), fast=True, tetras=True)
        polym = utils.buildPolyData(m.coordinates(), m.cells(), fast=True, tetras=True)
        app = vtk.vtkAppendPolyData()
        app.AddInputData(polym)
        app.AddInputData(polyb)
        app.Update()
        poly = app.GetOutput()
    return Mesh(poly).lw(0.1)


def loadPVD(filename):
    """Reads a paraview set of files."""
    import xml.etree.ElementTree as et

    tree = et.parse(filename)

    dname = os.path.dirname(filename)
    if not dname:
        dname = "."

    listofobjs = []
    for coll in tree.getroot():
        for dataset in coll:
            fname = dataset.get("file")
            ob = load(dname + "/" + fname)
            tm = dataset.get("timestep")
            if tm:
                ob.time(tm)
            listofobjs.append(ob)
    if len(listofobjs) == 1:
        return listofobjs[0]
    elif len(listofobjs) == 0:
        return None
    else:
        return listofobjs


def loadPDB(filename, bondScale=1, hydrogenBondScale=1, coilWidth=0.3, helixWidth=1.3):
    """Reads a molecule Protein Data Bank file."""
    rr = vtk.vtkPDBReader()
    rr.SetFileName(filename)
    rr.SetBScale(bondScale)
    rr.SetHBScale(hydrogenBondScale)
    rr.Update()
    prf = vtk.vtkProteinRibbonFilter()
    prf.SetCoilWidth(coilWidth)
    prf.SetHelixWidth(helixWidth)
    prf.SetInputData(rr.GetOutput())
    prf.Update()
    return Mesh(prf.GetOutput())


def loadNeutral(filename):
    """Reads a `Neutral` tetrahedral file format. Return an ``Mesh`` object."""
    with open(filename, "r") as f:
        lines = f.readlines()

    ncoords = int(lines[0])
    coords = []
    for i in range(1, ncoords + 1):
        x, y, z = lines[i].split()
        coords.append([float(x), float(y), float(z)])

    ntets = int(lines[ncoords + 1])
    idolf_tets = []
    for i in range(ncoords + 2, ncoords + ntets + 2):
        text = lines[i].split()
        v0, v1, v2, v3 = int(text[1])-1, int(text[2])-1, int(text[3])-1, int(text[4])-1
#        p0, p1, p2, p3 = np.array(coords[v1]), np.array(coords[v0]), coords[v3], coords[v2]
#        d10 = p1-p0
#        d21 = p2-p1
#        dc = np.cross(d10, d21)
#        print(np.dot(dc,p3-p0))
        idolf_tets.append([v0, v1, v2, v3])

    poly = utils.buildPolyData(coords, idolf_tets)
    return Mesh(poly)


def loadGmesh(filename):
    """Reads a `gmesh` file format. Return an ``Mesh`` object."""
    with open(filename, "r") as f:
        lines = f.readlines()

    nnodes = 0
    index_nodes = 0
    for i, line in enumerate(lines):
        if "$Nodes" in line:
            index_nodes = i + 1
            nnodes = int(lines[index_nodes])
            break
    node_coords = []
    for i in range(index_nodes + 1, index_nodes + 1 + nnodes):
        cn = lines[i].split()
        node_coords.append([float(cn[1]), float(cn[2]), float(cn[3])])

    nelements = 0
    index_elements = 0
    for i, line in enumerate(lines):
        if "$Elements" in line:
            index_elements = i + 1
            nelements = int(lines[index_elements])
            break
    elements = []
    for i in range(index_elements + 1, index_elements + 1 + nelements):
        ele = lines[i].split()
        elements.append([int(ele[-3]), int(ele[-2]), int(ele[-1])])

    poly = utils.buildPolyData(node_coords, elements, indexOffset=1)
    return Mesh(poly)


def loadPCD(filename):
    """Return a ``Mesh`` made of only vertex points
    from `Point Cloud` file format. Return an ``Points`` object."""
    with open(filename, "r") as f:
        lines = f.readlines()

    start = False
    pts = []
    N, expN = 0, 0
    for text in lines:
        if start:
            if N >= expN:
                break
            l = text.split()
            pts.append([float(l[0]), float(l[1]), float(l[2])])
            N += 1
        if not start and "POINTS" in text:
            expN = int(text.split()[1])
        if not start and "DATA ascii" in text:
            start = True
    if expN != N:
        vedo.logger.warning(f"Mismatch in PCD file {expN} != {len(pts)}")
    poly = utils.buildPolyData(pts)
    return Points(poly).pointSize(4)


def tonumpy(obj):
    """Dump a vedo object to numpy format."""

    adict = {}
    adict["type"] = "unknown"

    ########################################################
    def _fillcommon(obj, adict):
        adict["filename"] = obj.filename
        adict["name"] = obj.name
        adict["time"] = obj.time()
        adict["rendered_at"] = obj.renderedAt
        adict["position"] = obj.pos()
        adict["info"] = obj.info
        m = np.eye(4)
        vm = obj.getTransform().GetMatrix()
        for i in [0, 1, 2, 3]:
            for j in [0, 1, 2, 3]:
                m[i, j] = vm.GetElement(i, j)
        adict["transform"] = m
        minv = np.eye(4)
        vm.Invert()
        for i in [0, 1, 2, 3]:
            for j in [0, 1, 2, 3]:
                minv[i, j] = vm.GetElement(i, j)
        adict["transform_inverse"] = minv

    ########################################################
    def _fillmesh(obj, adict):

        adict["points"] = obj.points(transformed=False).astype(np.float32)
        poly = obj.polydata()
        adict["flagText"] = obj.flagText

        adict["cells"] = None
        if poly.GetNumberOfPolys():
            try:
                adict["cells"] = np.array(obj.faces(), dtype=np.uint32)
            except ValueError:
                adict["cells"] = obj.faces()

        adict["lines"] = None
        if poly.GetNumberOfLines():
            adict["lines"] = obj.lines()

        adict["pointdata"] = []
        for iname in obj.pointdata.keys():
            if not iname:
                continue
            if "Normals" in iname.lower():
                continue
            arr = poly.GetPointData().GetArray(iname)
            adict["pointdata"].append([utils.vtk2numpy(arr), iname])

        adict["celldata"] = []
        for iname in obj.celldata.keys():
            if not iname:
                continue
            if "Normals" in iname.lower():
                continue
            arr = poly.GetCellData().GetArray(iname)
            adict["celldata"].append([utils.vtk2numpy(arr), iname])

        adict["activedata"] = None
        if poly.GetPointData().GetScalars():
            adict['activedata'] = ['pointdata', poly.GetPointData().GetScalars().GetName()]
        elif poly.GetCellData().GetScalars():
            adict['activedata'] = ['celldata',  poly.GetCellData().GetScalars().GetName()]

        adict["LUT"] = None
        adict["LUT_range"] = None
        lut = obj.mapper().GetLookupTable()
        if lut:
            nlut = lut.GetNumberOfTableValues()
            lutvals = []
            for i in range(nlut):
                v4 = lut.GetTableValue(i)  # r, g, b, alpha
                lutvals.append(v4)
            adict["LUT"] = lutvals
            adict["LUT_range"] = lut.GetRange()

        prp = obj.GetProperty()
        adict["alpha"] = prp.GetOpacity()
        adict["representation"] = prp.GetRepresentation()
        adict["pointsize"] = prp.GetPointSize()

        adict["linecolor"] = None
        adict["linewidth"] = None
        if prp.GetEdgeVisibility():
            adict["linewidth"] = obj.lineWidth()
            adict["linecolor"] = prp.GetEdgeColor()

        adict["ambient"] = prp.GetAmbient()
        adict["diffuse"] = prp.GetDiffuse()
        adict["specular"] = prp.GetSpecular()
        adict["specularpower"] = prp.GetSpecularPower()
        adict["specularcolor"] = prp.GetSpecularColor()
        adict["shading"] = prp.GetInterpolation()  # flat phong..:
        adict["color"] = prp.GetColor()
        adict["lightingIsOn"] = prp.GetLighting()
        adict["backColor"] = None
        if obj.GetBackfaceProperty():
            adict["backColor"] = obj.GetBackfaceProperty().GetColor()

        adict["scalarvisibility"] = obj.mapper().GetScalarVisibility()
        adict["texture"] = None

    ######################################################## Assembly
    if isinstance(obj, Assembly):
        pass
        # adict['type'] = 'Assembly'
        # _fillcommon(obj, adict)
        # adict['actors'] = []
        # for a in obj.unpack():
        #     assdict = dict()
        #     if isinstance(a, Mesh):
        #         _fillmesh(a, assdict)
        #         adict['actors'].append(assdict)

    ######################################################## Points/Mesh
    elif isinstance(obj, Points):
        adict["type"] = "Mesh"
        _fillcommon(obj, adict)
        _fillmesh(obj, adict)

    ######################################################## Volume
    elif isinstance(obj, Volume):
        adict["type"] = "Volume"
        _fillcommon(obj, adict)
        imgdata = obj.inputdata()
        arr = utils.vtk2numpy(imgdata.GetPointData().GetScalars())
        adict["array"] = arr.reshape(imgdata.GetDimensions())
        adict["mode"] = obj.mode()
        # adict['jittering'] = obj.mapper().GetUseJittering()

        prp = obj.GetProperty()
        ctf = prp.GetRGBTransferFunction()
        otf = prp.GetScalarOpacity()
        gotf = prp.GetGradientOpacity()
        smin, smax = ctf.GetRange()
        xs = np.linspace(smin, smax, num=100, endpoint=True)
        cols, als, algrs = [], [], []
        for x in xs:
            cols.append(ctf.GetColor(x))
            als.append(otf.GetValue(x))
            if gotf:
                algrs.append(gotf.GetValue(x))
        adict["color"] = cols
        adict["alpha"] = als
        adict["alphagrad"] = algrs

    ######################################################## Picture
    elif isinstance(obj, Picture):
        adict["type"] = "Picture"
        _fillcommon(obj, adict)
        adict["array"] = obj.tonumpy()

    ######################################################## Text2D
    elif isinstance(obj, vedo.Text2D):
        adict["type"] = "Text2D"
        adict["rendered_at"] = obj.renderedAt
        adict["text"] = obj.text()
        adict["position"] = obj.GetPosition()
        adict["color"] = obj.property.GetColor()
        adict["font"] = obj.fontname
        adict["size"] = obj.property.GetFontSize() / 22.5
        adict["bgcol"] = obj.property.GetBackgroundColor()
        adict["alpha"] = obj.property.GetBackgroundOpacity()
        adict["frame"] = obj.property.GetFrame()
        # print('tonumpy(): vedo.Text2D', obj.text()[:10], obj.font(), obj.GetPosition())

    else:
        pass
        # colors.printc('Unknown object type in tonumpy()', [obj], c='r')

    return adict


def loadnumpy(inobj):
    """Load a vedo format file or scene."""

    # make sure the numpy file is not containing a scene
    if isinstance(inobj, str):  # user passing a file

        if inobj.endswith(".npy"):
            data = np.load(inobj, allow_pickle=True, encoding="latin1")  # .flatten()
        elif inobj.endswith(".npz"):
            data = np.load(inobj, allow_pickle=True)["vedo_scenes"]

        isdict = hasattr(data[0], "keys")

        if isdict and "objects" in data[0].keys():  # loading a full scene!!
            return importWindow(data[0])

        # it's a very normal numpy data object? just return it!
        if not isdict:
            return data
        if "type" not in data[0].keys():
            return data

    else:
        data = inobj

    ######################################################
    def _loadcommon(obj, d):
        keys = d.keys()
        if 'time' in keys: obj.time(d['time'])
        if 'name' in keys: obj.name = d['name']
        if 'filename' in keys: obj.filename = d['filename']
        if 'info' in keys: obj.info = d['info']

        if "transform" in keys and len(d["transform"]) == 4:
            vm = vtk.vtkMatrix4x4()
            for i in [0, 1, 2, 3]:
                for j in [0, 1, 2, 3]:
                    vm.SetElement(i, j, d["transform"][i, j])
            obj.applyTransform(vm)
        elif "position" in keys:
            obj.pos(d["position"])

    ######################################################
    def _buildmesh(d):
        keys = d.keys()

        vertices = d["points"]
        if len(vertices) == 0:
            return None

        cells = None
        if "cells" in keys:
            cells = d["cells"]

        lines = None
        if "lines" in keys:
            lines = d["lines"]

        poly = utils.buildPolyData(vertices, cells, lines)
        msh = Mesh(poly)
        _loadcommon(msh, d)

        prp = msh.GetProperty()
        if 'ambient' in keys:        prp.SetAmbient(d['ambient'])
        if 'diffuse' in keys:        prp.SetDiffuse(d['diffuse'])
        if 'specular' in keys:       prp.SetSpecular(d['specular'])
        if 'specularpower' in keys:  prp.SetSpecularPower(d['specularpower'])
        if 'specularcolor' in keys:  prp.SetSpecularColor(d['specularcolor'])
        if 'lightingIsOn' in keys:   prp.SetLighting(d['lightingIsOn'])
        if 'shading' in keys:        prp.SetInterpolation(d['shading'])
        if 'alpha' in keys:          prp.SetOpacity(d['alpha'])
        if 'opacity' in keys:        prp.SetOpacity(d['opacity']) # synonym
        if 'representation' in keys: prp.SetRepresentation(d['representation'])
        if 'pointsize' in keys and d['pointsize']: prp.SetPointSize(d['pointsize'])

        if 'linewidth' in keys and d['linewidth']: msh.lineWidth(d['linewidth'])
        if 'linecolor' in keys and d['linecolor']: msh.lineColor(d['linecolor'])

        if 'color' in keys and d['color'] is not None:
            msh.color(d['color'])
        if 'backColor' in keys and d['backColor'] is not None:
            msh.backColor(d['backColor'])

        if 'flagText' in keys and d['flagText']:   msh.flag(d['flagText'])

        if "celldata" in keys:
            for csc, cscname in d["celldata"]:
                msh.addCellArray(csc, cscname)
        if "pointdata" in keys:
            for psc, pscname in d["pointdata"]:
                msh.addPointArray(psc, pscname)

        msh.mapper().ScalarVisibilityOff()  # deactivate scalars

        if "LUT" in keys and "activedata" in keys and d["activedata"]:
            # print(d['activedata'],'', msh.filename)
            lut_list = d["LUT"]
            ncols = len(lut_list)
            lut = vtk.vtkLookupTable()
            lut.SetNumberOfTableValues(ncols)
            lut.SetRange(d["LUT_range"])
            for i in range(ncols):
                r, g, b, a = lut_list[i]
                lut.SetTableValue(i, r, g, b, a)
            lut.Build()
            msh.mapper().SetLookupTable(lut)
            msh.mapper().ScalarVisibilityOn()  # activate scalars
            msh.mapper().SetScalarRange(d["LUT_range"])
            if d["activedata"][0] == "celldata":
                poly.GetCellData().SetActiveScalars(d["activedata"][1])
            if d["activedata"][0] == "pointdata":
                poly.GetPointData().SetActiveScalars(d["activedata"][1])

        if "shading" in keys and int(d["shading"]) > 0:
            msh.computeNormals(cells=0)  # otherwise cannot renderer phong

        if "scalarvisibility" in keys:
            if d["scalarvisibility"]:
                msh.mapper().ScalarVisibilityOn()
            else:
                msh.mapper().ScalarVisibilityOff()

        if "texture" in keys and d["texture"]:
            msh.texture(d["texture"])

        return msh

    ######################################################

    objs = []
    for d in data:
        # print('loadnumpy:', d['type'], d)

        ### Mesh
        if "mesh" == d["type"].lower():
            a = _buildmesh(d)
            if a:
                objs.append(a)

        ### Assembly
        elif "assembly" == d["type"].lower():
            assacts = []
            for ad in d["actors"]:
                assacts.append(_buildmesh(ad))
            asse = Assembly(assacts)
            _loadcommon(asse, d)
            objs.append(asse)

        ### Volume
        elif "volume" == d["type"].lower():
            vol = Volume(d["array"])
            _loadcommon(vol, d)
            if "jittering" in d.keys():
                vol.jittering(d["jittering"])
            # print(d['mode'])
            vol.mode(d["mode"])
            vol.color(d["color"])
            vol.alpha(d["alpha"])
            vol.alphaGradient(d["alphagrad"])
            objs.append(vol)

        ### Picture
        elif "picture" == d["type"].lower():
            vimg = Picture(d["array"])
            _loadcommon(vimg, d)
            objs.append(vimg)

        ### Text2D
        elif "text2d" == d["type"].lower():
            t = vedo.shapes.Text2D(d["text"], font=d["font"], c=d["color"])
            t.pos(d["position"]).size(d["size"])
            t.background(d["bgcol"], d["alpha"])
            if d["frame"]:
                t.frame(d["bgcol"])
            objs.append(t)

        ### Annotation ## backward compatibility - will disappear
        elif "annotation" == d["type"].lower():

            pos = d["position"]
            if isinstance(pos, int):
                pos = "top-left"
                d["size"] *= 2.7
            t = vedo.shapes.Text2D(d["text"], font=d["font"], c=d["color"]).pos(pos)
            t.background(d["bgcol"], d["alpha"]).size(d["size"]).frame(d["bgcol"])
            objs.append(t)  ## backward compatibility

    if len(objs) == 1:
        return objs[0]
    elif len(objs) == 0:
        return None
    else:
        return objs


def loadImageData(filename):
    """Read and return a ``vtkImageData`` object from file."""
    if ".tif" in filename.lower():
        reader = vtk.vtkTIFFReader()
        # print("GetOrientationType ", reader.GetOrientationType())
        reader.SetOrientationType(settings.tiffOrientationType)
    elif ".slc" in filename.lower():
        reader = vtk.vtkSLCReader()
        if not reader.CanReadFile(filename):
            vedo.logger.error(f"sorry, bad SLC file {filename}")
            return None
    elif ".vti" in filename.lower():
        reader = vtk.vtkXMLImageDataReader()
    elif ".mhd" in filename.lower():
        reader = vtk.vtkMetaImageReader()
    elif ".dem" in filename.lower():
        reader = vtk.vtkDEMReader()
    elif ".nii" in filename.lower():
        reader = vtk.vtkNIFTIImageReader()
    elif ".nrrd" in filename.lower():
        reader = vtk.vtkNrrdReader()
        if not reader.CanReadFile(filename):
            vedo.logger.error(f"sorry, bad NRRD file {filename}")
            return None
    reader.SetFileName(filename)
    reader.Update()
    image = reader.GetOutput()
    return image


###########################################################
def write(objct, fileoutput, binary=True):
    """
    Write 3D object to file. (same as `save()`).

    Possile extensions are:
        - vtk, vti, npy, npz, ply, obj, stl, byu, vtp, vti, mhd, xyz, tif, png, bmp.
    """
    obj = objct
    if isinstance(obj, Points):  # picks transformation
        obj = objct.polydata(True)
    elif isinstance(obj, (vtk.vtkActor, vtk.vtkVolume)):
        obj = objct.GetMapper().GetInput()
    elif isinstance(obj, (vtk.vtkPolyData, vtk.vtkImageData)):
        obj = objct

    if hasattr(obj, "filename"):
        obj.filename = fileoutput

    fr = fileoutput.lower()
    if fr.endswith(".vtk"):
        writer = vtk.vtkDataSetWriter()
    elif fr.endswith(".ply"):
        writer = vtk.vtkPLYWriter()
        writer.AddComment("PLY file generated by vedo")
        lut = objct.GetMapper().GetLookupTable()
        if lut:
            pscal = obj.GetPointData().GetScalars()
            if not pscal:
                pscal = obj.GetCellData().GetScalars()
            if pscal and pscal.GetName():
                writer.SetArrayName(pscal.GetName())
            writer.SetLookupTable(lut)
    elif fr.endswith(".stl"):
        writer = vtk.vtkSTLWriter()
    elif fr.endswith(".vtp"):
        writer = vtk.vtkXMLPolyDataWriter()
    elif fr.endswith(".vtu"):
        writer = vtk.vtkXMLUnstructuredGridWriter()
    elif fr.endswith(".vtm"):
        g = vtk.vtkMultiBlockDataGroupFilter()
        for ob in objct:
            if isinstance(ob, (Points, Volume)):  # picks transformation
                ob = ob.polydata(True)
                g.AddInputData(ob)
            # elif isinstance(ob, (vtk.vtkActor, vtk.vtkVolume)):
            #     ob = ob.GetMapper().GetInput()
            #     g.AddInputData(ob)
        g.Update()
        mb = g.GetOutputDataObject(0)
        wri = vtk.vtkXMLMultiBlockDataWriter()
        wri.SetInputData(mb)
        wri.SetFileName(fileoutput)
        wri.Write()
        return mb
    elif fr.endswith(".xyz"):
        writer = vtk.vtkSimplePointsWriter()
    elif fr.endswith(".facet"):
        writer = vtk.vtkFacetWriter()
    elif fr.endswith(".vti"):
        writer = vtk.vtkXMLImageDataWriter()
    elif fr.endswith(".mhd"):
        writer = vtk.vtkMetaImageWriter()
    elif fr.endswith(".nii"):
        writer = vtk.vtkNIFTIImageWriter()
    elif fr.endswith(".png"):
        writer = vtk.vtkPNGWriter()
    elif fr.endswith(".jpg"):
        writer = vtk.vtkJPEGWriter()
    elif fr.endswith(".bmp"):
        writer = vtk.vtkBMPWriter()
    elif fr.endswith(".tif") or fr.endswith(".tiff"):
        writer = vtk.vtkTIFFWriter()
        # print("GetCompression ", writer.GetCompression()) # basically uncompressed..
        writer.SetFileDimensionality(len(obj.GetDimensions()))
    elif fr.endswith(".npy") or fr.endswith(".npz"):
        if utils.isSequence(objct):
            objslist = objct
        else:
            objslist = [objct]
        dicts2save = []
        for obj in objslist:
            dicts2save.append(tonumpy(obj))
        np.save(fileoutput, dicts2save)
        return dicts2save

    elif fr.endswith(".obj"):
        with open(fileoutput, "w") as outF:
            outF.write("# OBJ file format with ext .obj\n")
            outF.write("# File generated by vedo\n")

            for p in objct.points():
                outF.write("v {:.5g} {:.5g} {:.5g}\n".format(*p))

            ptxt = objct.polydata().GetPointData().GetTCoords()
            if ptxt:
                ntxt = utils.vtk2numpy(ptxt)
                for vt in ntxt:
                    outF.write('vt '+ str(vt[0]) +" "+ str(vt[1])+ ' 0.0\n')

            for i, f in enumerate(objct.faces()):
                fs = ""
                for fi in f:
                    if ptxt:
                        fs += f" {fi+1}/{fi+1}"
                    else:
                        fs += f" {fi+1}"
                outF.write(f"f{fs}\n")

            for l in objct.lines():
                ls = ""
                for li in l:
                    ls += str(li + 1) + " "
                outF.write(f"l {ls}\n")

        return objct


    elif fr.endswith(".xml"):  # write tetrahedral dolfin xml
        vertices = objct.points().astype(str)
        faces = np.array(objct.faces()).astype(str)
        ncoords = vertices.shape[0]
        with open(fileoutput, "w") as outF:
            outF.write('<?xml version="1.0" encoding="UTF-8"?>\n')
            outF.write('<dolfin xmlns:dolfin="http://www.fenicsproject.org">\n')

            if len(faces[0]) == 4:  # write tetrahedral mesh
                ntets = faces.shape[0]
                outF.write('  <mesh celltype="tetrahedron" dim="3">\n')
                outF.write('    <vertices size="' + str(ncoords) + '">\n')
                for i in range(ncoords):
                    x, y, z = vertices[i]
                    outF.write('      <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'" z="'+z+'"/>\n')
                outF.write('    </vertices>\n')
                outF.write('    <cells size="' + str(ntets) + '">\n')
                for i in range(ntets):
                    v0, v1, v2, v3 = faces[i]
                    outF.write('     <tetrahedron index="'+str(i)
                               + '" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'" v3="'+v3+'"/>\n')

            elif len(faces[0]) == 3:  # write triangle mesh
                ntri = faces.shape[0]
                outF.write('  <mesh celltype="triangle" dim="2">\n')
                outF.write('    <vertices size="' + str(ncoords) + '">\n')
                for i in range(ncoords):
                    x, y, dummy_z = vertices[i]
                    outF.write('      <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'"/>\n')
                outF.write('    </vertices>\n')
                outF.write('    <cells size="' + str(ntri) + '">\n')
                for i in range(ntri):
                    v0, v1, v2 = faces[i]
                    outF.write('     <triangle index="'+str(i)+'" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'"/>\n')

            outF.write("    </cells>\n")
            outF.write("  </mesh>\n")
            outF.write("</dolfin>\n")
        return objct

    else:
        vedo.logger.error(f"Unknown format {fileoutput}, file not saved")
        return objct

    try:
        if binary:
            writer.SetFileTypeToBinary()
        else:
            writer.SetFileTypeToASCII()
    except:
        pass

    try:
        writer.SetInputData(obj)
        writer.SetFileName(fileoutput)
        writer.Write()
    except:
        vedo.logger.error(f"could not save {fileoutput}")
    return objct


def writeTransform(inobj, filename="transform.mat", comment=""):
    """
    Save a transformation for a mesh or pointcloud to ASCII file.

    Parameters
    ----------
    filename : str, optional
        output file name. The default is 'transform.mat'.

    comment : str, optional
        some optional comment. The default is ''.
    """
    if isinstance(inobj, Points):
        M = inobj.getTransform().GetMatrix()
    elif isinstance(inobj, vtk.vtkTransform):
        M = inobj.GetMatrix()
    elif isinstance(inobj, vtk.vtkMatrix4x4):
        M = inobj
    else:
        vedo.logger.error(
            f"in writeTransform(), cannot understand input type {type(inobj)}"
        )

    with open(filename, "w") as f:
        if comment:
            f.write("# " + comment + "\n")
        for i in range(4):
            f.write( str(M.GetElement(i,0))+' '+
                     str(M.GetElement(i,1))+' '+
                     str(M.GetElement(i,2))+' '+
                     str(M.GetElement(i,3))+'\n',
                    )
        f.write('\n')


def loadTransform(filename):
    """
    Load a ``vtkTransform`` from a file.mat.

    Returns
    -------
    T : vtkTransform
        The transformation to be applied to some object (``use applyTransform()``).

    comment : str
        a comment string associated to this transformation file.
    """
    with open(filename, "r") as f:
        lines = f.readlines()
        M = vtk.vtkMatrix4x4()
        i = 0
        comment = ""
        for l in lines:
            if l.startswith("#"):
                comment = l.replace("#", "").replace("\n", "")
                continue
            vals = l.split(" ")
            if len(vals) == 4:
                for j in range(4):
                    v = vals[j].replace("\n", "")
                    M.SetElement(i, j, float(v))
                i += 1
        T = vtk.vtkTransform()
        T.SetMatrix(M)
    return (T, comment)


###############################################################################
def exportWindow(fileoutput, binary=False):
    """
    Exporter which writes out the renderered scene into an HTML, X3D
    or Numpy file.

    .. hint:: examples/other/export_x3d.py
        Check out the HTML generated webpage [here](https://vedo.embl.es/examples/embryo.html).

        See also: FEniCS [test webpage](https://vedo.embl.es/examples/fenics_elasticity.html).

    .. note:: the rendering window can also be exported to `numpy` file `scene.npz`
        by pressing ``E`` keyboard at any moment during visualization.

        .. image:: https://user-images.githubusercontent.com/32848391/57160341-c6ffbd80-6de8-11e9-95ff-7215ce642bc5.jpg
    """
    fr = fileoutput.lower()

    ####################################################################
    if fr.endswith(".npy") or fr.endswith(".npz"):
        sdict = {}
        plt = vedo.plotter_instance
        sdict["shape"] = plt.shape
        sdict["sharecam"] = plt.sharecam
        sdict["camera"] = dict(
            pos=plt.camera.GetPosition(),
            focalPoint=plt.camera.GetFocalPoint(),
            viewup=plt.camera.GetViewUp(),
            distance=plt.camera.GetDistance(),
            clippingRange=plt.camera.GetClippingRange(),
        )
        sdict["position"] = plt.pos
        sdict["size"] = plt.size
        sdict["axes"] = plt.axes
        sdict["title"] = plt.title
        sdict["backgrcol"] = colors.getColor(plt.backgrcol)
        sdict["backgrcol2"] = None
        if plt.renderer.GetGradientBackground():
            sdict["backgrcol2"] = plt.renderer.GetBackground2()
        sdict["useDepthPeeling"] = settings.useDepthPeeling
        sdict["renderLinesAsTubes"] = settings.renderLinesAsTubes
        sdict["hiddenLineRemoval"] = settings.hiddenLineRemoval
        sdict["visibleGridEdges"] = settings.visibleGridEdges
        # sdict['interactorStyle'] = 0
        sdict["useParallelProjection"] = settings.useParallelProjection
        sdict["defaultFont"] = settings.defaultFont
        sdict["objects"] = []

        allobjs = plt.getMeshes(includeNonPickables=True) + plt.getVolumes(includeNonPickables=True)
        acts2d = plt.renderer.GetActors2D()
        acts2d.InitTraversal()
        for _ in range(acts2d.GetNumberOfItems()):
            a = acts2d.GetNextItem()
            if isinstance(a, vedo.Text2D):
                allobjs.append(a)
        allobjs += plt.actors

        allobjs = list(set(allobjs))  # make sure its unique

        for a in allobjs:
            sdict["objects"].append(tonumpy(a))

        if fr.endswith(".npz"):
            np.savez_compressed(fileoutput, vedo_scenes=[sdict])
        else:
            np.save(fileoutput, [sdict])

    ####################################################################
    elif fr.endswith(".x3d"):
        obj = list(
            set(vedo.plotter_instance.getMeshes() + vedo.plotter_instance.actors)
        )
        if vedo.plotter_instance.axes_instances:
            obj.append(vedo.plotter_instance.axes_instances[0])
        for a in obj:
            if isinstance(a, Mesh):
                newa = a.clone(transformed=True)
                vedo.plotter_instance.remove(a, render=False).add(newa, render=False)

            elif isinstance(a, Assembly):
                for b in a.unpack():
                    if b:
                        newb = b.clone()
                        vedo.plotter_instance.add(newb, render=False)
                vedo.plotter_instance.remove(a)
        vedo.plotter_instance.render()

        exporter = vtk.vtkX3DExporter()
        exporter.SetBinary(binary)
        exporter.FastestOff()
        exporter.SetInput(vedo.plotter_instance.window)
        exporter.SetFileName(fileoutput)
        #        exporter.WriteToOutputStringOn() # see below
        exporter.Update()
        exporter.Write()

# this can reduce the size by more than half...
#        outstring = exporter.GetOutputString().decode("utf-8") # this fails though
#        from vedo.utils import isInteger, isNumber, precision
#        newlines = []
#        for l in outstring.splitlines(True):
#            ls = l.lstrip()
#            content = ls.split()
#            newls = ""
#            for c in content:
#                c2 = c.replace(',','')
#                if isNumber(c2) and not isInteger(c2):
#                    newc = precision(float(c2), 4)
#                    if ',' in c:
#                        newls += newc + ','
#                    else:
#                        newls += newc + ' '
#                else:
#                    newls += c + ' '
#        newlines.append(newls.lstrip()+'\n')
#        with open("fileoutput", 'w') as f:
#            l = "".join(newlines)
#            f.write(l)

        x3d_html = _x3d_html.replace("~fileoutput", fileoutput)
        wsize = vedo.plotter_instance.window.GetSize()
        x3d_html = x3d_html.replace("~width", str(wsize[0]))
        x3d_html = x3d_html.replace("~height", str(wsize[1]))
        with open(fileoutput.replace(".x3d", ".html"), "w") as outF:
            outF.write(x3d_html)
            vedo.logger.info(
                f"Saved files {fileoutput} and {fileoutput.replace('.x3d','.html')}"
            )

    ####################################################################
    elif fr.endswith(".html"):
        savebk = vedo.notebookBackend
        vedo.notebookBackend = "k3d"
        plt = vedo.backends.getNotebookBackend(vedo.plotter_instance.actors, 1.5, "")

        with open(fileoutput, "w") as fp:
            fp.write(plt.get_snapshot())

        vedo.notebookBackend = savebk

    else:
        vedo.logger.error(f"export extension {fr.split('.')[-1]} is not supported")
    return vedo.plotter_instance


def importWindow(fileinput, mtlFile=None, texturePath=None):
    """Import a whole scene from a Numpy or OBJ wavefront file.
    Return a ``Plotter`` instance.

    Parameters
    ----------
    mtlFile : str
        MTL file for OBJ wavefront files.

    texturePath : str
        path of the texture files directory.
    """
    data = None
    if isinstance(fileinput, dict):
        data = fileinput
    elif fileinput.endswith(".npy"):
        data = np.load(fileinput, allow_pickle=True, encoding="latin1").flatten()[0]
    elif fileinput.endswith(".npz"):
        data = np.load(fileinput, allow_pickle=True)["vedo_scenes"][0]

    if data is not None:
        if "renderLinesAsTubes" in data.keys():
            settings.renderLinesAsTubes = data["renderLinesAsTubes"]
        if "hiddenLineRemoval" in data.keys():
            settings.hiddenLineRemoval = data["hiddenLineRemoval"]
        if "visibleGridEdges" in data.keys():
            settings.visibleGridEdges = data["visibleGridEdges"]
        if "interactorStyle" in data.keys():
            vedo.interactorStyle = data["interactorStyle"]
        if "useParallelProjection" in data.keys():
            settings.useParallelProjection = data["useParallelProjection"]
        if "usePolygonOffset" in data.keys():
            settings.usePolygonOffset = data["usePolygonOffset"]
        if "polygonOffsetFactor" in data.keys():
            settings.polygonOffsetFactor = data["polygonOffsetFactor"]
        if "polygonOffsetUnits" in data.keys():
            settings.polygonOffsetUnits = data["polygonOffsetUnits"]
        if "interpolateScalarsBeforeMapping" in data.keys():
            settings.interpolateScalarsBeforeMapping = data[
                "interpolateScalarsBeforeMapping"
            ]
        if "defaultFont" in data.keys():
            settings.defaultFont = data["defaultFont"]
        if "useDepthPeeling" in data.keys():
            settings.useDepthPeeling = data["useDepthPeeling"]

        axes = data.pop("axes", 4)
        title = data.pop("title", "")
        backgrcol = data.pop("backgrcol", "white")
        backgrcol2 = data.pop("backgrcol2", None)
        cam = data.pop("camera", None)

        if data["shape"] != (1, 1):
            data["size"] = "auto"  # disable size

        plt = vedo.Plotter(
            size=data["size"],  # not necessarily a good idea to set it
            # shape=data['shape'], # will need to create a Renderer class first
            axes=axes,
            title=title,
            bg=backgrcol,
            bg2=backgrcol2,
        )

        if cam:
            if "pos" in cam.keys():
                plt.camera.SetPosition(cam["pos"])
            if "focalPoint" in cam.keys():
                plt.camera.SetFocalPoint(cam["focalPoint"])
            if "viewup" in cam.keys():
                plt.camera.SetViewUp(cam["viewup"])
            if "distance" in cam.keys():
                plt.camera.SetDistance(cam["distance"])
            if "clippingRange" in cam.keys():
                plt.camera.SetClippingRange(cam["clippingRange"])
            plt.resetcam = False

        if "objects" in data.keys():
            objs = loadnumpy(data["objects"])
            if not utils.isSequence(objs):
                objs = [objs]
        else:
            # colors.printc("Trying to import a single mesh.. use load() instead.", c='r')
            # colors.printc(" -> try to load a single object with load().", c='r')
            objs = [loadnumpy(fileinput)]

        plt.actors = objs
        plt.add(objs, render=False)
        return plt

    elif ".obj" in fileinput.lower():

        plt = vedo.Plotter()

        importer = vtk.vtkOBJImporter()
        importer.SetFileName(fileinput)
        if mtlFile is not False:
            if mtlFile is None:
                mtlFile = fileinput.replace(".obj", ".mtl").replace(".OBJ", ".MTL")
            importer.SetFileNameMTL(mtlFile)
        if texturePath is not False:
            if texturePath is None:
                texturePath = fileinput.replace(".obj", ".txt").replace(".OBJ", ".TXT")
            importer.SetTexturePath(texturePath)
        importer.SetRenderWindow(plt.window)
        importer.Update()

        actors = plt.renderer.GetActors()
        actors.InitTraversal()
        for _ in range(actors.GetNumberOfItems()):
            vactor = actors.GetNextActor()
            act = Mesh(vactor)
            act_tu = vactor.GetTexture()
            if act_tu:
                act_tu.InterpolateOn()
                act.texture(act_tu)
            plt.actors.append(act)
        return plt


##########################################################
def screenshot(filename="screenshot.png", scale=None, asarray=False):
    """
    Save a screenshot of the current rendering window.

    Parameters
    ----------
    scale : int
        set image magnification as an integer multiplicative factor

    asarray : bool
        return a numpy array of the image
    """
    if not vedo.plotter_instance or not vedo.plotter_instance.window:
        vedo.logger.error("in screenshot(), rendering window is not present, skip.")
        return vedo.plotter_instance

    if filename.endswith(".pdf"):
        writer = vtk.vtkGL2PSExporter()
        writer.SetRenderWindow(vedo.plotter_instance.window)
        writer.Write3DPropsAsRasterImageOff()
        writer.SilentOn()
        writer.SetSortToBSP()
        writer.SetFileFormatToPDF()
        writer.SetFilePrefix(filename.replace(".pdf", ""))
        writer.Write()
        return vedo.plotter_instance  ##########
    elif filename.endswith(".svg"):
        writer = vtk.vtkGL2PSExporter()
        writer.SetRenderWindow(vedo.plotter_instance.window)
        writer.Write3DPropsAsRasterImageOff()
        writer.SilentOn()
        writer.SetSortToBSP()
        writer.SetFileFormatToSVG()
        writer.SetFilePrefix(filename.replace(".svg", ""))
        writer.Write()
        return vedo.plotter_instance  ##########
    elif filename.endswith(".eps"):
        writer = vtk.vtkGL2PSExporter()
        writer.SetRenderWindow(vedo.plotter_instance.window)
        writer.Write3DPropsAsRasterImageOff()
        writer.SilentOn()
        writer.SetSortToBSP()
        writer.SetFileFormatToEPS()
        writer.SetFilePrefix(filename.replace(".eps", ""))
        writer.Write()
        return vedo.plotter_instance  ##########

    if scale is None:
        scale = settings.screeshotScale

    if settings.screeshotLargeImage:
        w2if = vtk.vtkRenderLargeImage()
        w2if.SetInput(vedo.plotter_instance.renderer)
        w2if.SetMagnification(scale)
    else:
        w2if = vtk.vtkWindowToImageFilter()
        w2if.SetInput(vedo.plotter_instance.window)
        if hasattr(w2if, "SetScale"):
            w2if.SetScale(scale, scale)
        if settings.screenshotTransparentBackground:
            w2if.SetInputBufferTypeToRGBA()
        w2if.ReadFrontBufferOff()  # read from the back buffer
    w2if.Update()

    if asarray:
        w2ifout = w2if.GetOutput()
        npdata = utils.vtk2numpy(w2ifout.GetPointData().GetArray("ImageScalars"))
        npdata = npdata[:, [0, 1, 2]]
        ydim, xdim, _ = w2ifout.GetDimensions()
        npdata = npdata.reshape([xdim, ydim, -1])
        npdata = np.flip(npdata, axis=0)
        return npdata

    if filename.lower().endswith(".png"):
        writer = vtk.vtkPNGWriter()
        writer.SetFileName(filename)
        writer.SetInputData(w2if.GetOutput())
        writer.Write()
    elif filename.lower().endswith(".jpg") or filename.lower().endswith(".jpeg"):
        writer = vtk.vtkJPEGWriter()
        writer.SetFileName(filename)
        writer.SetInputData(w2if.GetOutput())
        writer.Write()
    else:  # add .png
        writer = vtk.vtkPNGWriter()
        writer.SetFileName(filename + ".png")
        writer.SetInputData(w2if.GetOutput())
        writer.Write()
    return vedo.plotter_instance


def ask(*question, **kwarg):
    """
    Ask a question from command line. Return the answer as a string.
    See function `printc()` for the description of the options.

    Parameters
    ----------
    options : list
        a python list of possible answers to choose from.

    default : str
        the default answer when just hitting return.

    Example:
        .. code-block:: python

            import vedo
            res = vedo.io.ask("Continue?", options=['Y','n'], default='Y', c='g')
            print(res)
    """
    kwarg.update({"end": " "})
    if "invert" not in kwarg:
        kwarg.update({"invert": True})
    if "box" in kwarg:
        kwarg.update({"box": ""})

    options = kwarg.pop("options", [])
    default = kwarg.pop("default", "")
    if options:
        opt = "["
        for o in options:
            opt += o + "/"
        opt = opt[:-1] + "]"
        colors.printc(*question, opt, **kwarg)
    else:
        colors.printc(*question, **kwarg)

    resp = input()

    if options:
        if resp not in options:
            if default and str(repr(resp)) == "''":
                return default
            colors.printc("Please choose one option in:", opt, italic=True, bold=False)
            kwarg["options"] = options
            return ask(*question, **kwarg)  # ask again
    return resp


##############################################################################################
class Video:
    """
    Class to generate a video from the specified rendering window.
    Program `ffmpeg` is used to create video from each generated frame.

    Parameters
    ----------
    name : str
        name of the output file.

    fps : int
        set the number of frames per second.

    duration : float
        set the total `duration` of the video and recalculates `fps` accordingly.

     ffmpeg : str
         set path to ffmpeg program. Default value assumes ffmpeg command is in the path.

    .. hint:: examples/other/makeVideo.py
        .. image:: https://user-images.githubusercontent.com/32848391/50739007-2bfc2b80-11da-11e9-97e6-620a3541a6fa.jpg
    """

    def __init__(
            self,
            name="movie.mp4",
            duration=None,
            fps=24,
            backend="ffmpeg",
        ):

        self.name = name
        self.duration = duration
        self.backend = backend
        self.fps = float(fps)
        self.command = "ffmpeg -loglevel panic -y -r"
        self.options = "-b:v 8000k"

        self.frames = []
        self.tmp_dir = TemporaryDirectory()
        self.get_filename = lambda x: os.path.join(self.tmp_dir.name, x)
        colors.printc("\video Video", self.name, "is open... ", c="m", end="")

    def addFrame(self):
        """Add frame to current video."""
        fr = self.get_filename(str(len(self.frames)) + ".png")
        screenshot(fr)
        self.frames.append(fr)
        return self

    def pause(self, pause=0):
        """Insert a `pause`, in seconds."""
        fr = self.frames[-1]
        n = int(self.fps * pause)
        for _ in range(n):
            fr2 = self.get_filename(str(len(self.frames)) + ".png")
            self.frames.append(fr2)
            os.system("cp -f %s %s" % (fr, fr2))
        return self

    def action(
        self,
        elevation=(0, 80),
        azimuth=(0, 359),
        cameras=(),
        resetcam=False,
    ):
        """
        Automatic shooting of a static scene by specifying rotation and elevation ranges.

        :param list elevation: initial and final elevation angles
        :param list azimuth_range: initial and final azimuth angles
        :param list cameras: list of cameras to go through, each camera can be dictionary or a vtkCamera
        """
        if not self.duration:
            self.duration = 5

        def build_vtk_cam(cm_input):
            cm = dict(cm_input)
            cm_pos = cm.pop("pos", None)
            cm_focalPoint = cm.pop("focalPoint", None)
            cm_viewup = cm.pop("viewup", None)
            cm_distance = cm.pop("distance", None)
            cm_clippingRange = cm.pop("clippingRange", None)
            cm_parallelScale = cm.pop("parallelScale", None)
            cm_thickness = cm.pop("thickness", None)
            cm_viewAngle = cm.pop("viewAngle", None)
            cm = vtk.vtkCamera()
            if cm_pos is not None: cm.SetPosition(cm_pos)
            if cm_focalPoint is not None: cm.SetFocalPoint(cm_focalPoint)
            if cm_viewup is not None: cm.SetViewUp(cm_viewup)
            if cm_distance is not None: cm.SetDistance(cm_distance)
            if cm_clippingRange is not None: cm.SetClippingRange(cm_clippingRange)
            if cm_parallelScale is not None: cm.SetParallelScale(cm_parallelScale)
            if cm_thickness is not None: cm.SetThickness(cm_thickness)
            if cm_viewAngle is not None: cm.SetViewAngle(cm_viewAngle)
            return cm

        plt = vedo.plotter_instance
        n = int(self.fps * self.duration)

        cams = []
        for cm in cameras:
            cams.append(build_vtk_cam(cm))
        nc = len(cams)

        plt.show(resetcam=resetcam, interactive=False)

        if nc:
            for icam, cam in enumerate(cams):
                if cam == cams[-1]:
                    break
                for i in range(n):
                    plt.moveCamera(cams[icam], cams[icam + 1], i / n)
                    plt.show()
                    self.addFrame()

        else:  ########################################

            for i in range(n):
                plt.camera.Elevation((elevation[1] - elevation[0]) / n)
                plt.camera.Azimuth((azimuth[1] - azimuth[0]) / n)
                plt.show()
                self.addFrame()

        return self

    def close(self):
        """
        Render the video and write to file.
        Return the current ``Plotter`` instance.
        """
        if self.duration:
            self.fps = len(self.frames) / float(self.duration)
            colors.printc("Recalculated video FPS to", round(self.fps, 3), c="m")
        else:
            self.fps = int(self.fps)

        ########################################
        if self.backend == "ffmpeg":
            out = os.system(
                self.command
                + " "
                + str(self.fps)
                + " -i "
                + f"'{self.tmp_dir.name}'"
                + os.sep
                + "%01d.png "
                + self.options
                + " "
                + f"'{self.name}'"
            )
            if out:
                vedo.logger.error(f"backend {self.backend} returning error: {out}")
            else:
                colors.printc(f" \save video saved as {self.name}", c="m")

        ########################################
        elif "cv" in self.backend:
            try:
                import cv2
            except:
                vedo.logger.error("opencv is not installed")
                return

            cap = cv2.VideoCapture(os.path.join(self.tmp_dir.name, "%1d.png"))
            fourcc = cv2.VideoWriter_fourcc(*"mp4v")
            w, h = vedo.plotter_instance.window.GetSize()
            w, h = w * settings.screeshotScale, h * settings.screeshotScale
            writer = cv2.VideoWriter(self.name, fourcc, self.fps, (w, h), True)

            found = False
            while True:
                ret, frame = cap.read()
                if not ret:
                    break
                writer.write(frame)
                found = True

            cap.release()
            writer.release()
            if found:
                vedo.logger.info("video saved as {self.name}")
            else:
                vedo.logger.error("could not find snapshots")

        self.tmp_dir.cleanup()
        return vedo.plotter_instance

Functions

def ask(*question, **kwarg)

Ask a question from command line. Return the answer as a string. See function printc() for the description of the options.

Parameters

options : list
a python list of possible answers to choose from.
default : str
the default answer when just hitting return.

Example

.. code-block:: python

import vedo
res = vedo.io.ask("Continue?", options=['Y','n'], default='Y', c='g')
print(res)
Expand source code
def ask(*question, **kwarg):
    """
    Ask a question from command line. Return the answer as a string.
    See function `printc()` for the description of the options.

    Parameters
    ----------
    options : list
        a python list of possible answers to choose from.

    default : str
        the default answer when just hitting return.

    Example:
        .. code-block:: python

            import vedo
            res = vedo.io.ask("Continue?", options=['Y','n'], default='Y', c='g')
            print(res)
    """
    kwarg.update({"end": " "})
    if "invert" not in kwarg:
        kwarg.update({"invert": True})
    if "box" in kwarg:
        kwarg.update({"box": ""})

    options = kwarg.pop("options", [])
    default = kwarg.pop("default", "")
    if options:
        opt = "["
        for o in options:
            opt += o + "/"
        opt = opt[:-1] + "]"
        colors.printc(*question, opt, **kwarg)
    else:
        colors.printc(*question, **kwarg)

    resp = input()

    if options:
        if resp not in options:
            if default and str(repr(resp)) == "''":
                return default
            colors.printc("Please choose one option in:", opt, italic=True, bold=False)
            kwarg["options"] = options
            return ask(*question, **kwarg)  # ask again
    return resp
def download(url, force=False, verbose=True)

Retrieve a file from a url, save it locally and return its path. Use force to force reload and discard cache copies.

Expand source code
def download(url, force=False, verbose=True):
    """Retrieve a file from a url, save it locally and return its path.
    Use ``force`` to force reload and discard cache copies."""

    if not url.startswith("https://"):
        vedo.logger.error(f"Invalid URL (must start with https):\n{url}")
        return url
    url = url.replace("www.dropbox", "dl.dropbox")

    if "github.com" in url:
        url = url.replace("/blob/", "/raw/")

    basename = os.path.basename(url)

    if "?" in basename:
        basename = basename.split("?")[0]

    tmp_file = NamedTemporaryFile(delete=False)
    tmp_file.name = os.path.join(
        os.path.dirname(tmp_file.name), os.path.basename(basename)
    )

    if not force and os.path.exists(tmp_file.name):
        if verbose:
            colors.printc("reusing cached file:", tmp_file.name)
            # colors.printc("     (use force=True to force a new download)")
        return tmp_file.name

    try:
        from urllib.request import urlopen, Request

        req = Request(url, headers={"User-Agent": "Mozilla/5.0"})
        if verbose:
            colors.printc('reading', basename, 'from', url.split('/')[2][:40],'...', end='')
    except ImportError:
        import urllib2
        import contextlib

        urlopen = lambda url_: contextlib.closing(urllib2.urlopen(url_))
        req = url
        if verbose:
            colors.printc('reading', basename, 'from',
                          url.split('/')[2][:40],'...', end='')

    with urlopen(req) as response, open(tmp_file.name, "wb") as output:
        output.write(response.read())

    if verbose: colors.printc(' done.')
    return tmp_file.name
def exportWindow(fileoutput, binary=False)

Exporter which writes out the renderered scene into an HTML, X3D or Numpy file.

Hint: examples/other/export_x3d.py

Check out the HTML generated webpage here.

See also: FEniCS test webpage.

Note: the rendering window can also be exported to numpy file scene.npz

by pressing E keyboard at any moment during visualization.

Expand source code
def exportWindow(fileoutput, binary=False):
    """
    Exporter which writes out the renderered scene into an HTML, X3D
    or Numpy file.

    .. hint:: examples/other/export_x3d.py
        Check out the HTML generated webpage [here](https://vedo.embl.es/examples/embryo.html).

        See also: FEniCS [test webpage](https://vedo.embl.es/examples/fenics_elasticity.html).

    .. note:: the rendering window can also be exported to `numpy` file `scene.npz`
        by pressing ``E`` keyboard at any moment during visualization.

        .. image:: https://user-images.githubusercontent.com/32848391/57160341-c6ffbd80-6de8-11e9-95ff-7215ce642bc5.jpg
    """
    fr = fileoutput.lower()

    ####################################################################
    if fr.endswith(".npy") or fr.endswith(".npz"):
        sdict = {}
        plt = vedo.plotter_instance
        sdict["shape"] = plt.shape
        sdict["sharecam"] = plt.sharecam
        sdict["camera"] = dict(
            pos=plt.camera.GetPosition(),
            focalPoint=plt.camera.GetFocalPoint(),
            viewup=plt.camera.GetViewUp(),
            distance=plt.camera.GetDistance(),
            clippingRange=plt.camera.GetClippingRange(),
        )
        sdict["position"] = plt.pos
        sdict["size"] = plt.size
        sdict["axes"] = plt.axes
        sdict["title"] = plt.title
        sdict["backgrcol"] = colors.getColor(plt.backgrcol)
        sdict["backgrcol2"] = None
        if plt.renderer.GetGradientBackground():
            sdict["backgrcol2"] = plt.renderer.GetBackground2()
        sdict["useDepthPeeling"] = settings.useDepthPeeling
        sdict["renderLinesAsTubes"] = settings.renderLinesAsTubes
        sdict["hiddenLineRemoval"] = settings.hiddenLineRemoval
        sdict["visibleGridEdges"] = settings.visibleGridEdges
        # sdict['interactorStyle'] = 0
        sdict["useParallelProjection"] = settings.useParallelProjection
        sdict["defaultFont"] = settings.defaultFont
        sdict["objects"] = []

        allobjs = plt.getMeshes(includeNonPickables=True) + plt.getVolumes(includeNonPickables=True)
        acts2d = plt.renderer.GetActors2D()
        acts2d.InitTraversal()
        for _ in range(acts2d.GetNumberOfItems()):
            a = acts2d.GetNextItem()
            if isinstance(a, vedo.Text2D):
                allobjs.append(a)
        allobjs += plt.actors

        allobjs = list(set(allobjs))  # make sure its unique

        for a in allobjs:
            sdict["objects"].append(tonumpy(a))

        if fr.endswith(".npz"):
            np.savez_compressed(fileoutput, vedo_scenes=[sdict])
        else:
            np.save(fileoutput, [sdict])

    ####################################################################
    elif fr.endswith(".x3d"):
        obj = list(
            set(vedo.plotter_instance.getMeshes() + vedo.plotter_instance.actors)
        )
        if vedo.plotter_instance.axes_instances:
            obj.append(vedo.plotter_instance.axes_instances[0])
        for a in obj:
            if isinstance(a, Mesh):
                newa = a.clone(transformed=True)
                vedo.plotter_instance.remove(a, render=False).add(newa, render=False)

            elif isinstance(a, Assembly):
                for b in a.unpack():
                    if b:
                        newb = b.clone()
                        vedo.plotter_instance.add(newb, render=False)
                vedo.plotter_instance.remove(a)
        vedo.plotter_instance.render()

        exporter = vtk.vtkX3DExporter()
        exporter.SetBinary(binary)
        exporter.FastestOff()
        exporter.SetInput(vedo.plotter_instance.window)
        exporter.SetFileName(fileoutput)
        #        exporter.WriteToOutputStringOn() # see below
        exporter.Update()
        exporter.Write()

# this can reduce the size by more than half...
#        outstring = exporter.GetOutputString().decode("utf-8") # this fails though
#        from vedo.utils import isInteger, isNumber, precision
#        newlines = []
#        for l in outstring.splitlines(True):
#            ls = l.lstrip()
#            content = ls.split()
#            newls = ""
#            for c in content:
#                c2 = c.replace(',','')
#                if isNumber(c2) and not isInteger(c2):
#                    newc = precision(float(c2), 4)
#                    if ',' in c:
#                        newls += newc + ','
#                    else:
#                        newls += newc + ' '
#                else:
#                    newls += c + ' '
#        newlines.append(newls.lstrip()+'\n')
#        with open("fileoutput", 'w') as f:
#            l = "".join(newlines)
#            f.write(l)

        x3d_html = _x3d_html.replace("~fileoutput", fileoutput)
        wsize = vedo.plotter_instance.window.GetSize()
        x3d_html = x3d_html.replace("~width", str(wsize[0]))
        x3d_html = x3d_html.replace("~height", str(wsize[1]))
        with open(fileoutput.replace(".x3d", ".html"), "w") as outF:
            outF.write(x3d_html)
            vedo.logger.info(
                f"Saved files {fileoutput} and {fileoutput.replace('.x3d','.html')}"
            )

    ####################################################################
    elif fr.endswith(".html"):
        savebk = vedo.notebookBackend
        vedo.notebookBackend = "k3d"
        plt = vedo.backends.getNotebookBackend(vedo.plotter_instance.actors, 1.5, "")

        with open(fileoutput, "w") as fp:
            fp.write(plt.get_snapshot())

        vedo.notebookBackend = savebk

    else:
        vedo.logger.error(f"export extension {fr.split('.')[-1]} is not supported")
    return vedo.plotter_instance
def gunzip(filename)

Unzip a .gz file to a temporary file and returns its path.

Expand source code
def gunzip(filename):
    """Unzip a `.gz` file to a temporary file and returns its path."""
    if not filename.endswith(".gz"):
        # colors.printc("gunzip() error: file must end with .gz", c='r')
        return filename
    import gzip

    tmp_file = NamedTemporaryFile(delete=False)
    tmp_file.name = os.path.join(
        os.path.dirname(tmp_file.name), os.path.basename(filename).replace(".gz", "")
    )
    inF = gzip.open(filename, "rb")
    with open(tmp_file.name, "wb") as outF:
        outF.write(inF.read())
    inF.close()
    return tmp_file.name
def importWindow(fileinput, mtlFile=None, texturePath=None)

Import a whole scene from a Numpy or OBJ wavefront file. Return a Plotter instance.

Parameters

mtlFile : str
MTL file for OBJ wavefront files.
texturePath : str
path of the texture files directory.
Expand source code
def importWindow(fileinput, mtlFile=None, texturePath=None):
    """Import a whole scene from a Numpy or OBJ wavefront file.
    Return a ``Plotter`` instance.

    Parameters
    ----------
    mtlFile : str
        MTL file for OBJ wavefront files.

    texturePath : str
        path of the texture files directory.
    """
    data = None
    if isinstance(fileinput, dict):
        data = fileinput
    elif fileinput.endswith(".npy"):
        data = np.load(fileinput, allow_pickle=True, encoding="latin1").flatten()[0]
    elif fileinput.endswith(".npz"):
        data = np.load(fileinput, allow_pickle=True)["vedo_scenes"][0]

    if data is not None:
        if "renderLinesAsTubes" in data.keys():
            settings.renderLinesAsTubes = data["renderLinesAsTubes"]
        if "hiddenLineRemoval" in data.keys():
            settings.hiddenLineRemoval = data["hiddenLineRemoval"]
        if "visibleGridEdges" in data.keys():
            settings.visibleGridEdges = data["visibleGridEdges"]
        if "interactorStyle" in data.keys():
            vedo.interactorStyle = data["interactorStyle"]
        if "useParallelProjection" in data.keys():
            settings.useParallelProjection = data["useParallelProjection"]
        if "usePolygonOffset" in data.keys():
            settings.usePolygonOffset = data["usePolygonOffset"]
        if "polygonOffsetFactor" in data.keys():
            settings.polygonOffsetFactor = data["polygonOffsetFactor"]
        if "polygonOffsetUnits" in data.keys():
            settings.polygonOffsetUnits = data["polygonOffsetUnits"]
        if "interpolateScalarsBeforeMapping" in data.keys():
            settings.interpolateScalarsBeforeMapping = data[
                "interpolateScalarsBeforeMapping"
            ]
        if "defaultFont" in data.keys():
            settings.defaultFont = data["defaultFont"]
        if "useDepthPeeling" in data.keys():
            settings.useDepthPeeling = data["useDepthPeeling"]

        axes = data.pop("axes", 4)
        title = data.pop("title", "")
        backgrcol = data.pop("backgrcol", "white")
        backgrcol2 = data.pop("backgrcol2", None)
        cam = data.pop("camera", None)

        if data["shape"] != (1, 1):
            data["size"] = "auto"  # disable size

        plt = vedo.Plotter(
            size=data["size"],  # not necessarily a good idea to set it
            # shape=data['shape'], # will need to create a Renderer class first
            axes=axes,
            title=title,
            bg=backgrcol,
            bg2=backgrcol2,
        )

        if cam:
            if "pos" in cam.keys():
                plt.camera.SetPosition(cam["pos"])
            if "focalPoint" in cam.keys():
                plt.camera.SetFocalPoint(cam["focalPoint"])
            if "viewup" in cam.keys():
                plt.camera.SetViewUp(cam["viewup"])
            if "distance" in cam.keys():
                plt.camera.SetDistance(cam["distance"])
            if "clippingRange" in cam.keys():
                plt.camera.SetClippingRange(cam["clippingRange"])
            plt.resetcam = False

        if "objects" in data.keys():
            objs = loadnumpy(data["objects"])
            if not utils.isSequence(objs):
                objs = [objs]
        else:
            # colors.printc("Trying to import a single mesh.. use load() instead.", c='r')
            # colors.printc(" -> try to load a single object with load().", c='r')
            objs = [loadnumpy(fileinput)]

        plt.actors = objs
        plt.add(objs, render=False)
        return plt

    elif ".obj" in fileinput.lower():

        plt = vedo.Plotter()

        importer = vtk.vtkOBJImporter()
        importer.SetFileName(fileinput)
        if mtlFile is not False:
            if mtlFile is None:
                mtlFile = fileinput.replace(".obj", ".mtl").replace(".OBJ", ".MTL")
            importer.SetFileNameMTL(mtlFile)
        if texturePath is not False:
            if texturePath is None:
                texturePath = fileinput.replace(".obj", ".txt").replace(".OBJ", ".TXT")
            importer.SetTexturePath(texturePath)
        importer.SetRenderWindow(plt.window)
        importer.Update()

        actors = plt.renderer.GetActors()
        actors.InitTraversal()
        for _ in range(actors.GetNumberOfItems()):
            vactor = actors.GetNextActor()
            act = Mesh(vactor)
            act_tu = vactor.GetTexture()
            if act_tu:
                act_tu.InterpolateOn()
                act.texture(act_tu)
            plt.actors.append(act)
        return plt
def load(inputobj, unpack=True, force=False)

Load Mesh, Volume and Picture objects from file or from the web.

The output will depend on the file extension. See examples below. Unzip is made on the fly, if file ends with .gz. Can load an object directly from a URL address.

Parameters

unpack : bool
unpack MultiBlockData into a flat list of objects.
force : bool
when downloading a file ignore any previous cached downloads and force a new one.

Example

.. code-block:: python

from vedo import dataurl, load, show

# Return a Mesh object
g = load(dataurl+'250.vtk')
show(g)

# Return a list of 2 meshes
g = load([dataurl+'250.vtk', dataurl+'270.vtk'])
show(g)

# Return a list of meshes by reading all files in a directory
# (if directory contains DICOM files then a Volume is returned)
g = load('mydicomdir/')
show(g)

# Download a file from a URL address and unzip it on the fly
g = load('https://vedo.embl.es/examples/panther.stl.gz')
show(g)
Expand source code
def load(inputobj, unpack=True, force=False):
    """
    Load ``Mesh``, ``Volume`` and ``Picture`` objects from file or from the web.

    The output will depend on the file extension. See examples below.
    Unzip is made on the fly, if file ends with `.gz`.
    Can load an object directly from a URL address.

    Parameters
    ----------

    unpack : bool
        unpack MultiBlockData into a flat list of objects.

    force : bool
        when downloading a file ignore any previous cached downloads and force a new one.

    Example:
        .. code-block:: python

            from vedo import dataurl, load, show

            # Return a Mesh object
            g = load(dataurl+'250.vtk')
            show(g)

            # Return a list of 2 meshes
            g = load([dataurl+'250.vtk', dataurl+'270.vtk'])
            show(g)

            # Return a list of meshes by reading all files in a directory
            # (if directory contains DICOM files then a Volume is returned)
            g = load('mydicomdir/')
            show(g)

            # Download a file from a URL address and unzip it on the fly
            g = load('https://vedo.embl.es/examples/panther.stl.gz')
            show(g)
    """
    acts = []
    if utils.isSequence(inputobj):
        flist = inputobj
    elif isinstance(inputobj, str) and inputobj.startswith("https://"):
        flist = [inputobj]
    else:
        flist = sorted(glob.glob(inputobj))

    for fod in flist:

        if fod.startswith("https://"):
            fod = download(fod, force=force, verbose=False)

        if os.path.isfile(fod):  ### it's a file

            if fod.endswith(".gz"):
                fod = gunzip(fod)

            a = _load_file(fod, unpack)
            acts.append(a)

        elif os.path.isdir(fod):  ### it's a directory or DICOM
            flist = os.listdir(fod)
            if ".dcm" in flist[0]:  ### it's DICOM
                reader = vtk.vtkDICOMImageReader()
                reader.SetDirectoryName(fod)
                reader.Update()
                image = reader.GetOutput()
                actor = Volume(image)

                actor.info["PixelSpacing"] = reader.GetPixelSpacing()
                actor.info["Width"] = reader.GetWidth()
                actor.info["Height"] = reader.GetHeight()
                actor.info["PositionPatient"] = reader.GetImagePositionPatient()
                actor.info["OrientationPatient"] = reader.GetImageOrientationPatient()
                actor.info["BitsAllocated"] = reader.GetBitsAllocated()
                actor.info["PixelRepresentation"] = reader.GetPixelRepresentation()
                actor.info["NumberOfComponents"] = reader.GetNumberOfComponents()
                actor.info["TransferSyntaxUID"] = reader.GetTransferSyntaxUID()
                actor.info["RescaleSlope"] = reader.GetRescaleSlope()
                actor.info["RescaleOffset"] = reader.GetRescaleOffset()
                actor.info["PatientName"] = reader.GetPatientName()
                actor.info["StudyUID"] = reader.GetStudyUID()
                actor.info["StudyID"] = reader.GetStudyID()
                actor.info["GantryAngle"] = reader.GetGantryAngle()

                acts.append(actor)

            else:  ### it's a normal directory
                utils.humansort(flist)
                for ifile in flist:
                    a = _load_file(fod + "/" + ifile, unpack)
                    acts.append(a)
        else:
            vedo.logger.error(f"in load(), cannot find {fod}")

    if len(acts) == 1:
        if "numpy" in str(type(acts[0])):
            return acts[0]
        if not acts[0]:
            vedo.logger.error(f"in load(), cannot load {inputobj}")
        return acts[0]

    elif len(acts) == 0:
        vedo.logger.error(f"in load(), cannot load {inputobj}")
        return None

    else:
        return acts
def loadRectilinearGrid(filename)

Load and return a vtkRectilinearGrid object from file.

Expand source code
def loadRectilinearGrid(filename):
    """Load and return a ``vtkRectilinearGrid`` object from file."""
    if filename.endswith(".vtr"):
        reader = vtk.vtkXMLRectilinearGridReader()
    else:
        reader = vtk.vtkRectilinearGridReader()
    reader.SetFileName(filename)
    reader.Update()
    return reader.GetOutput()
def loadStructuredGrid(filename)

Load and return a vtkStructuredGrid object from file.

Expand source code
def loadStructuredGrid(filename):
    """Load and return a ``vtkStructuredGrid`` object from file."""
    if filename.endswith(".vts"):
        reader = vtk.vtkXMLStructuredGridReader()
    else:
        reader = vtk.vtkStructuredGridReader()
    reader.SetFileName(filename)
    reader.Update()
    return reader.GetOutput()
def loadStructuredPoints(filename)

Load and return a vtkStructuredPoints object from file.

Expand source code
def loadStructuredPoints(filename):
    """Load and return a ``vtkStructuredPoints`` object from file."""
    reader = vtk.vtkStructuredPointsReader()
    reader.SetFileName(filename)
    reader.Update()
    return reader.GetOutput()
def loadTransform(filename)

Load a vtkTransform from a file.mat.

Returns

T : vtkTransform
The transformation to be applied to some object (use applyTransform()).
comment : str
a comment string associated to this transformation file.
Expand source code
def loadTransform(filename):
    """
    Load a ``vtkTransform`` from a file.mat.

    Returns
    -------
    T : vtkTransform
        The transformation to be applied to some object (``use applyTransform()``).

    comment : str
        a comment string associated to this transformation file.
    """
    with open(filename, "r") as f:
        lines = f.readlines()
        M = vtk.vtkMatrix4x4()
        i = 0
        comment = ""
        for l in lines:
            if l.startswith("#"):
                comment = l.replace("#", "").replace("\n", "")
                continue
            vals = l.split(" ")
            if len(vals) == 4:
                for j in range(4):
                    v = vals[j].replace("\n", "")
                    M.SetElement(i, j, float(v))
                i += 1
        T = vtk.vtkTransform()
        T.SetMatrix(M)
    return (T, comment)
def loadUnStructuredGrid(filename)

Load and return a vtkunStructuredGrid object from file.

Expand source code
def loadUnStructuredGrid(filename):
    """Load and return a ``vtkunStructuredGrid`` object from file."""
    if filename.endswith(".vtu"):
        reader = vtk.vtkXMLUnstructuredGridReader()
    else:
        reader = vtk.vtkUnstructuredGridReader()
    reader.SetFileName(filename)
    reader.Update()
    return reader.GetOutput()
def screenshot(filename='screenshot.png', scale=None, asarray=False)

Save a screenshot of the current rendering window.

Parameters

scale : int
set image magnification as an integer multiplicative factor
asarray : bool
return a numpy array of the image
Expand source code
def screenshot(filename="screenshot.png", scale=None, asarray=False):
    """
    Save a screenshot of the current rendering window.

    Parameters
    ----------
    scale : int
        set image magnification as an integer multiplicative factor

    asarray : bool
        return a numpy array of the image
    """
    if not vedo.plotter_instance or not vedo.plotter_instance.window:
        vedo.logger.error("in screenshot(), rendering window is not present, skip.")
        return vedo.plotter_instance

    if filename.endswith(".pdf"):
        writer = vtk.vtkGL2PSExporter()
        writer.SetRenderWindow(vedo.plotter_instance.window)
        writer.Write3DPropsAsRasterImageOff()
        writer.SilentOn()
        writer.SetSortToBSP()
        writer.SetFileFormatToPDF()
        writer.SetFilePrefix(filename.replace(".pdf", ""))
        writer.Write()
        return vedo.plotter_instance  ##########
    elif filename.endswith(".svg"):
        writer = vtk.vtkGL2PSExporter()
        writer.SetRenderWindow(vedo.plotter_instance.window)
        writer.Write3DPropsAsRasterImageOff()
        writer.SilentOn()
        writer.SetSortToBSP()
        writer.SetFileFormatToSVG()
        writer.SetFilePrefix(filename.replace(".svg", ""))
        writer.Write()
        return vedo.plotter_instance  ##########
    elif filename.endswith(".eps"):
        writer = vtk.vtkGL2PSExporter()
        writer.SetRenderWindow(vedo.plotter_instance.window)
        writer.Write3DPropsAsRasterImageOff()
        writer.SilentOn()
        writer.SetSortToBSP()
        writer.SetFileFormatToEPS()
        writer.SetFilePrefix(filename.replace(".eps", ""))
        writer.Write()
        return vedo.plotter_instance  ##########

    if scale is None:
        scale = settings.screeshotScale

    if settings.screeshotLargeImage:
        w2if = vtk.vtkRenderLargeImage()
        w2if.SetInput(vedo.plotter_instance.renderer)
        w2if.SetMagnification(scale)
    else:
        w2if = vtk.vtkWindowToImageFilter()
        w2if.SetInput(vedo.plotter_instance.window)
        if hasattr(w2if, "SetScale"):
            w2if.SetScale(scale, scale)
        if settings.screenshotTransparentBackground:
            w2if.SetInputBufferTypeToRGBA()
        w2if.ReadFrontBufferOff()  # read from the back buffer
    w2if.Update()

    if asarray:
        w2ifout = w2if.GetOutput()
        npdata = utils.vtk2numpy(w2ifout.GetPointData().GetArray("ImageScalars"))
        npdata = npdata[:, [0, 1, 2]]
        ydim, xdim, _ = w2ifout.GetDimensions()
        npdata = npdata.reshape([xdim, ydim, -1])
        npdata = np.flip(npdata, axis=0)
        return npdata

    if filename.lower().endswith(".png"):
        writer = vtk.vtkPNGWriter()
        writer.SetFileName(filename)
        writer.SetInputData(w2if.GetOutput())
        writer.Write()
    elif filename.lower().endswith(".jpg") or filename.lower().endswith(".jpeg"):
        writer = vtk.vtkJPEGWriter()
        writer.SetFileName(filename)
        writer.SetInputData(w2if.GetOutput())
        writer.Write()
    else:  # add .png
        writer = vtk.vtkPNGWriter()
        writer.SetFileName(filename + ".png")
        writer.SetInputData(w2if.GetOutput())
        writer.Write()
    return vedo.plotter_instance
def write(objct, fileoutput, binary=True)

Write 3D object to file. (same as save()).

Possile extensions are: - vtk, vti, npy, npz, ply, obj, stl, byu, vtp, vti, mhd, xyz, tif, png, bmp.

Expand source code
def write(objct, fileoutput, binary=True):
    """
    Write 3D object to file. (same as `save()`).

    Possile extensions are:
        - vtk, vti, npy, npz, ply, obj, stl, byu, vtp, vti, mhd, xyz, tif, png, bmp.
    """
    obj = objct
    if isinstance(obj, Points):  # picks transformation
        obj = objct.polydata(True)
    elif isinstance(obj, (vtk.vtkActor, vtk.vtkVolume)):
        obj = objct.GetMapper().GetInput()
    elif isinstance(obj, (vtk.vtkPolyData, vtk.vtkImageData)):
        obj = objct

    if hasattr(obj, "filename"):
        obj.filename = fileoutput

    fr = fileoutput.lower()
    if fr.endswith(".vtk"):
        writer = vtk.vtkDataSetWriter()
    elif fr.endswith(".ply"):
        writer = vtk.vtkPLYWriter()
        writer.AddComment("PLY file generated by vedo")
        lut = objct.GetMapper().GetLookupTable()
        if lut:
            pscal = obj.GetPointData().GetScalars()
            if not pscal:
                pscal = obj.GetCellData().GetScalars()
            if pscal and pscal.GetName():
                writer.SetArrayName(pscal.GetName())
            writer.SetLookupTable(lut)
    elif fr.endswith(".stl"):
        writer = vtk.vtkSTLWriter()
    elif fr.endswith(".vtp"):
        writer = vtk.vtkXMLPolyDataWriter()
    elif fr.endswith(".vtu"):
        writer = vtk.vtkXMLUnstructuredGridWriter()
    elif fr.endswith(".vtm"):
        g = vtk.vtkMultiBlockDataGroupFilter()
        for ob in objct:
            if isinstance(ob, (Points, Volume)):  # picks transformation
                ob = ob.polydata(True)
                g.AddInputData(ob)
            # elif isinstance(ob, (vtk.vtkActor, vtk.vtkVolume)):
            #     ob = ob.GetMapper().GetInput()
            #     g.AddInputData(ob)
        g.Update()
        mb = g.GetOutputDataObject(0)
        wri = vtk.vtkXMLMultiBlockDataWriter()
        wri.SetInputData(mb)
        wri.SetFileName(fileoutput)
        wri.Write()
        return mb
    elif fr.endswith(".xyz"):
        writer = vtk.vtkSimplePointsWriter()
    elif fr.endswith(".facet"):
        writer = vtk.vtkFacetWriter()
    elif fr.endswith(".vti"):
        writer = vtk.vtkXMLImageDataWriter()
    elif fr.endswith(".mhd"):
        writer = vtk.vtkMetaImageWriter()
    elif fr.endswith(".nii"):
        writer = vtk.vtkNIFTIImageWriter()
    elif fr.endswith(".png"):
        writer = vtk.vtkPNGWriter()
    elif fr.endswith(".jpg"):
        writer = vtk.vtkJPEGWriter()
    elif fr.endswith(".bmp"):
        writer = vtk.vtkBMPWriter()
    elif fr.endswith(".tif") or fr.endswith(".tiff"):
        writer = vtk.vtkTIFFWriter()
        # print("GetCompression ", writer.GetCompression()) # basically uncompressed..
        writer.SetFileDimensionality(len(obj.GetDimensions()))
    elif fr.endswith(".npy") or fr.endswith(".npz"):
        if utils.isSequence(objct):
            objslist = objct
        else:
            objslist = [objct]
        dicts2save = []
        for obj in objslist:
            dicts2save.append(tonumpy(obj))
        np.save(fileoutput, dicts2save)
        return dicts2save

    elif fr.endswith(".obj"):
        with open(fileoutput, "w") as outF:
            outF.write("# OBJ file format with ext .obj\n")
            outF.write("# File generated by vedo\n")

            for p in objct.points():
                outF.write("v {:.5g} {:.5g} {:.5g}\n".format(*p))

            ptxt = objct.polydata().GetPointData().GetTCoords()
            if ptxt:
                ntxt = utils.vtk2numpy(ptxt)
                for vt in ntxt:
                    outF.write('vt '+ str(vt[0]) +" "+ str(vt[1])+ ' 0.0\n')

            for i, f in enumerate(objct.faces()):
                fs = ""
                for fi in f:
                    if ptxt:
                        fs += f" {fi+1}/{fi+1}"
                    else:
                        fs += f" {fi+1}"
                outF.write(f"f{fs}\n")

            for l in objct.lines():
                ls = ""
                for li in l:
                    ls += str(li + 1) + " "
                outF.write(f"l {ls}\n")

        return objct


    elif fr.endswith(".xml"):  # write tetrahedral dolfin xml
        vertices = objct.points().astype(str)
        faces = np.array(objct.faces()).astype(str)
        ncoords = vertices.shape[0]
        with open(fileoutput, "w") as outF:
            outF.write('<?xml version="1.0" encoding="UTF-8"?>\n')
            outF.write('<dolfin xmlns:dolfin="http://www.fenicsproject.org">\n')

            if len(faces[0]) == 4:  # write tetrahedral mesh
                ntets = faces.shape[0]
                outF.write('  <mesh celltype="tetrahedron" dim="3">\n')
                outF.write('    <vertices size="' + str(ncoords) + '">\n')
                for i in range(ncoords):
                    x, y, z = vertices[i]
                    outF.write('      <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'" z="'+z+'"/>\n')
                outF.write('    </vertices>\n')
                outF.write('    <cells size="' + str(ntets) + '">\n')
                for i in range(ntets):
                    v0, v1, v2, v3 = faces[i]
                    outF.write('     <tetrahedron index="'+str(i)
                               + '" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'" v3="'+v3+'"/>\n')

            elif len(faces[0]) == 3:  # write triangle mesh
                ntri = faces.shape[0]
                outF.write('  <mesh celltype="triangle" dim="2">\n')
                outF.write('    <vertices size="' + str(ncoords) + '">\n')
                for i in range(ncoords):
                    x, y, dummy_z = vertices[i]
                    outF.write('      <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'"/>\n')
                outF.write('    </vertices>\n')
                outF.write('    <cells size="' + str(ntri) + '">\n')
                for i in range(ntri):
                    v0, v1, v2 = faces[i]
                    outF.write('     <triangle index="'+str(i)+'" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'"/>\n')

            outF.write("    </cells>\n")
            outF.write("  </mesh>\n")
            outF.write("</dolfin>\n")
        return objct

    else:
        vedo.logger.error(f"Unknown format {fileoutput}, file not saved")
        return objct

    try:
        if binary:
            writer.SetFileTypeToBinary()
        else:
            writer.SetFileTypeToASCII()
    except:
        pass

    try:
        writer.SetInputData(obj)
        writer.SetFileName(fileoutput)
        writer.Write()
    except:
        vedo.logger.error(f"could not save {fileoutput}")
    return objct
def writeTransform(inobj, filename='transform.mat', comment='')

Save a transformation for a mesh or pointcloud to ASCII file.

Parameters

filename : str, optional
output file name. The default is 'transform.mat'.
comment : str, optional
some optional comment. The default is ''.
Expand source code
def writeTransform(inobj, filename="transform.mat", comment=""):
    """
    Save a transformation for a mesh or pointcloud to ASCII file.

    Parameters
    ----------
    filename : str, optional
        output file name. The default is 'transform.mat'.

    comment : str, optional
        some optional comment. The default is ''.
    """
    if isinstance(inobj, Points):
        M = inobj.getTransform().GetMatrix()
    elif isinstance(inobj, vtk.vtkTransform):
        M = inobj.GetMatrix()
    elif isinstance(inobj, vtk.vtkMatrix4x4):
        M = inobj
    else:
        vedo.logger.error(
            f"in writeTransform(), cannot understand input type {type(inobj)}"
        )

    with open(filename, "w") as f:
        if comment:
            f.write("# " + comment + "\n")
        for i in range(4):
            f.write( str(M.GetElement(i,0))+' '+
                     str(M.GetElement(i,1))+' '+
                     str(M.GetElement(i,2))+' '+
                     str(M.GetElement(i,3))+'\n',
                    )
        f.write('\n')

Classes

class Video (name='movie.mp4', duration=None, fps=24, backend='ffmpeg')

Class to generate a video from the specified rendering window. Program ffmpeg is used to create video from each generated frame.

Parameters

name : str
name of the output file.
fps : int
set the number of frames per second.
duration : float
set the total duration of the video and recalculates fps accordingly.

ffmpeg : str set path to ffmpeg program. Default value assumes ffmpeg command is in the path.

Hint: examples/other/makeVideo.py

Expand source code
class Video:
    """
    Class to generate a video from the specified rendering window.
    Program `ffmpeg` is used to create video from each generated frame.

    Parameters
    ----------
    name : str
        name of the output file.

    fps : int
        set the number of frames per second.

    duration : float
        set the total `duration` of the video and recalculates `fps` accordingly.

     ffmpeg : str
         set path to ffmpeg program. Default value assumes ffmpeg command is in the path.

    .. hint:: examples/other/makeVideo.py
        .. image:: https://user-images.githubusercontent.com/32848391/50739007-2bfc2b80-11da-11e9-97e6-620a3541a6fa.jpg
    """

    def __init__(
            self,
            name="movie.mp4",
            duration=None,
            fps=24,
            backend="ffmpeg",
        ):

        self.name = name
        self.duration = duration
        self.backend = backend
        self.fps = float(fps)
        self.command = "ffmpeg -loglevel panic -y -r"
        self.options = "-b:v 8000k"

        self.frames = []
        self.tmp_dir = TemporaryDirectory()
        self.get_filename = lambda x: os.path.join(self.tmp_dir.name, x)
        colors.printc("\video Video", self.name, "is open... ", c="m", end="")

    def addFrame(self):
        """Add frame to current video."""
        fr = self.get_filename(str(len(self.frames)) + ".png")
        screenshot(fr)
        self.frames.append(fr)
        return self

    def pause(self, pause=0):
        """Insert a `pause`, in seconds."""
        fr = self.frames[-1]
        n = int(self.fps * pause)
        for _ in range(n):
            fr2 = self.get_filename(str(len(self.frames)) + ".png")
            self.frames.append(fr2)
            os.system("cp -f %s %s" % (fr, fr2))
        return self

    def action(
        self,
        elevation=(0, 80),
        azimuth=(0, 359),
        cameras=(),
        resetcam=False,
    ):
        """
        Automatic shooting of a static scene by specifying rotation and elevation ranges.

        :param list elevation: initial and final elevation angles
        :param list azimuth_range: initial and final azimuth angles
        :param list cameras: list of cameras to go through, each camera can be dictionary or a vtkCamera
        """
        if not self.duration:
            self.duration = 5

        def build_vtk_cam(cm_input):
            cm = dict(cm_input)
            cm_pos = cm.pop("pos", None)
            cm_focalPoint = cm.pop("focalPoint", None)
            cm_viewup = cm.pop("viewup", None)
            cm_distance = cm.pop("distance", None)
            cm_clippingRange = cm.pop("clippingRange", None)
            cm_parallelScale = cm.pop("parallelScale", None)
            cm_thickness = cm.pop("thickness", None)
            cm_viewAngle = cm.pop("viewAngle", None)
            cm = vtk.vtkCamera()
            if cm_pos is not None: cm.SetPosition(cm_pos)
            if cm_focalPoint is not None: cm.SetFocalPoint(cm_focalPoint)
            if cm_viewup is not None: cm.SetViewUp(cm_viewup)
            if cm_distance is not None: cm.SetDistance(cm_distance)
            if cm_clippingRange is not None: cm.SetClippingRange(cm_clippingRange)
            if cm_parallelScale is not None: cm.SetParallelScale(cm_parallelScale)
            if cm_thickness is not None: cm.SetThickness(cm_thickness)
            if cm_viewAngle is not None: cm.SetViewAngle(cm_viewAngle)
            return cm

        plt = vedo.plotter_instance
        n = int(self.fps * self.duration)

        cams = []
        for cm in cameras:
            cams.append(build_vtk_cam(cm))
        nc = len(cams)

        plt.show(resetcam=resetcam, interactive=False)

        if nc:
            for icam, cam in enumerate(cams):
                if cam == cams[-1]:
                    break
                for i in range(n):
                    plt.moveCamera(cams[icam], cams[icam + 1], i / n)
                    plt.show()
                    self.addFrame()

        else:  ########################################

            for i in range(n):
                plt.camera.Elevation((elevation[1] - elevation[0]) / n)
                plt.camera.Azimuth((azimuth[1] - azimuth[0]) / n)
                plt.show()
                self.addFrame()

        return self

    def close(self):
        """
        Render the video and write to file.
        Return the current ``Plotter`` instance.
        """
        if self.duration:
            self.fps = len(self.frames) / float(self.duration)
            colors.printc("Recalculated video FPS to", round(self.fps, 3), c="m")
        else:
            self.fps = int(self.fps)

        ########################################
        if self.backend == "ffmpeg":
            out = os.system(
                self.command
                + " "
                + str(self.fps)
                + " -i "
                + f"'{self.tmp_dir.name}'"
                + os.sep
                + "%01d.png "
                + self.options
                + " "
                + f"'{self.name}'"
            )
            if out:
                vedo.logger.error(f"backend {self.backend} returning error: {out}")
            else:
                colors.printc(f" \save video saved as {self.name}", c="m")

        ########################################
        elif "cv" in self.backend:
            try:
                import cv2
            except:
                vedo.logger.error("opencv is not installed")
                return

            cap = cv2.VideoCapture(os.path.join(self.tmp_dir.name, "%1d.png"))
            fourcc = cv2.VideoWriter_fourcc(*"mp4v")
            w, h = vedo.plotter_instance.window.GetSize()
            w, h = w * settings.screeshotScale, h * settings.screeshotScale
            writer = cv2.VideoWriter(self.name, fourcc, self.fps, (w, h), True)

            found = False
            while True:
                ret, frame = cap.read()
                if not ret:
                    break
                writer.write(frame)
                found = True

            cap.release()
            writer.release()
            if found:
                vedo.logger.info("video saved as {self.name}")
            else:
                vedo.logger.error("could not find snapshots")

        self.tmp_dir.cleanup()
        return vedo.plotter_instance

Methods

def action(self, elevation=(0, 80), azimuth=(0, 359), cameras=(), resetcam=False)

Automatic shooting of a static scene by specifying rotation and elevation ranges.

:param list elevation: initial and final elevation angles :param list azimuth_range: initial and final azimuth angles :param list cameras: list of cameras to go through, each camera can be dictionary or a vtkCamera

Expand source code
def action(
    self,
    elevation=(0, 80),
    azimuth=(0, 359),
    cameras=(),
    resetcam=False,
):
    """
    Automatic shooting of a static scene by specifying rotation and elevation ranges.

    :param list elevation: initial and final elevation angles
    :param list azimuth_range: initial and final azimuth angles
    :param list cameras: list of cameras to go through, each camera can be dictionary or a vtkCamera
    """
    if not self.duration:
        self.duration = 5

    def build_vtk_cam(cm_input):
        cm = dict(cm_input)
        cm_pos = cm.pop("pos", None)
        cm_focalPoint = cm.pop("focalPoint", None)
        cm_viewup = cm.pop("viewup", None)
        cm_distance = cm.pop("distance", None)
        cm_clippingRange = cm.pop("clippingRange", None)
        cm_parallelScale = cm.pop("parallelScale", None)
        cm_thickness = cm.pop("thickness", None)
        cm_viewAngle = cm.pop("viewAngle", None)
        cm = vtk.vtkCamera()
        if cm_pos is not None: cm.SetPosition(cm_pos)
        if cm_focalPoint is not None: cm.SetFocalPoint(cm_focalPoint)
        if cm_viewup is not None: cm.SetViewUp(cm_viewup)
        if cm_distance is not None: cm.SetDistance(cm_distance)
        if cm_clippingRange is not None: cm.SetClippingRange(cm_clippingRange)
        if cm_parallelScale is not None: cm.SetParallelScale(cm_parallelScale)
        if cm_thickness is not None: cm.SetThickness(cm_thickness)
        if cm_viewAngle is not None: cm.SetViewAngle(cm_viewAngle)
        return cm

    plt = vedo.plotter_instance
    n = int(self.fps * self.duration)

    cams = []
    for cm in cameras:
        cams.append(build_vtk_cam(cm))
    nc = len(cams)

    plt.show(resetcam=resetcam, interactive=False)

    if nc:
        for icam, cam in enumerate(cams):
            if cam == cams[-1]:
                break
            for i in range(n):
                plt.moveCamera(cams[icam], cams[icam + 1], i / n)
                plt.show()
                self.addFrame()

    else:  ########################################

        for i in range(n):
            plt.camera.Elevation((elevation[1] - elevation[0]) / n)
            plt.camera.Azimuth((azimuth[1] - azimuth[0]) / n)
            plt.show()
            self.addFrame()

    return self
def addFrame(self)

Add frame to current video.

Expand source code
def addFrame(self):
    """Add frame to current video."""
    fr = self.get_filename(str(len(self.frames)) + ".png")
    screenshot(fr)
    self.frames.append(fr)
    return self
def close(self)

Render the video and write to file. Return the current Plotter instance.

Expand source code
def close(self):
    """
    Render the video and write to file.
    Return the current ``Plotter`` instance.
    """
    if self.duration:
        self.fps = len(self.frames) / float(self.duration)
        colors.printc("Recalculated video FPS to", round(self.fps, 3), c="m")
    else:
        self.fps = int(self.fps)

    ########################################
    if self.backend == "ffmpeg":
        out = os.system(
            self.command
            + " "
            + str(self.fps)
            + " -i "
            + f"'{self.tmp_dir.name}'"
            + os.sep
            + "%01d.png "
            + self.options
            + " "
            + f"'{self.name}'"
        )
        if out:
            vedo.logger.error(f"backend {self.backend} returning error: {out}")
        else:
            colors.printc(f" \save video saved as {self.name}", c="m")

    ########################################
    elif "cv" in self.backend:
        try:
            import cv2
        except:
            vedo.logger.error("opencv is not installed")
            return

        cap = cv2.VideoCapture(os.path.join(self.tmp_dir.name, "%1d.png"))
        fourcc = cv2.VideoWriter_fourcc(*"mp4v")
        w, h = vedo.plotter_instance.window.GetSize()
        w, h = w * settings.screeshotScale, h * settings.screeshotScale
        writer = cv2.VideoWriter(self.name, fourcc, self.fps, (w, h), True)

        found = False
        while True:
            ret, frame = cap.read()
            if not ret:
                break
            writer.write(frame)
            found = True

        cap.release()
        writer.release()
        if found:
            vedo.logger.info("video saved as {self.name}")
        else:
            vedo.logger.error("could not find snapshots")

    self.tmp_dir.cleanup()
    return vedo.plotter_instance
def pause(self, pause=0)

Insert a pause, in seconds.

Expand source code
def pause(self, pause=0):
    """Insert a `pause`, in seconds."""
    fr = self.frames[-1]
    n = int(self.fps * pause)
    for _ in range(n):
        fr2 = self.get_filename(str(len(self.frames)) + ".png")
        self.frames.append(fr2)
        os.system("cp -f %s %s" % (fr, fr2))
    return self