plugins.export — pyFormex 1.0.5 documentation

# Source code for plugins.export

    # $Id$
    ##
    ##  This file is part of pyFormex 1.0.5  (Sat Feb 16 10:40:32 CET 2019)
    ##  pyFormex is a tool for generating, manipulating and transforming 3D
    ##  geometrical models by sequences of mathematical operations.
    ##  Home page: http://pyformex.org
    ##  Project page:  http://savannah.nongnu.org/projects/pyformex/
    ##  Copyright 2004-2018 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be)
    ##  Distributed under the GNU General Public License version 3 or later.
    ##
    ##  This program is free software: you can redistribute it and/or modify
    ##  it under the terms of the GNU General Public License as published by
    ##  the Free Software Foundation, either version 3 of the License, or
    ##  (at your option) any later version.
    ##
    ##  This program is distributed in the hope that it will be useful,
    ##  but WITHOUT ANY WARRANTY; without even the implied warranty of
    ##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ##  GNU General Public License for more details.
    ##
    ##  You should have received a copy of the GNU General Public License
    ##  along with this program.  If not, see http://www.gnu.org/licenses/.
    ##

    """Classes and functions for exporting geometry in various formats.

    - ObjFile: wavefront .obj format
    - PlyFile: .ply format

    These classes do not necessarily implement all features of the specified
    formats. They are usually fitted to export and sometimes imports simple
    mesh format geometries.
    """
    from __future__ import absolute_import,division, print_function

    import sys
    import pyformex as pf
    import numpy as np

    class ObjFile(object):
        """Export a mesh in OBJ format.

        This class exports a mesh in Wavefront OBJ format
        (see `<https://en.wikipedia.org/wiki/Wavefront_.obj_file>`_).

        The class instantiator takes a filename as parameter.
        Normally, the file name should end in '.obj', but anything is accepted.
        The ObjFile instance is a context manager.

        Usage::

          with ObjFile(PATH_TO_OBJFILE) as fil:
              fil.write(MESH)

        """

        def __init__(self, filename):
            self.filename = filename

        def __enter__(self):
            self.fil = open(self.filename, 'w')
            self.fil.write("# .obj file written by %s\n" % pf.Version())
            return self

        def __exit__(self, *args):
            self.fil.write('# End\n')
            self.fil.close()

        def write(self,mesh,name=None):
            """Write a mesh to file in .obj format.

            `mesh` is a Mesh instance or another object having compatible
            coords and elems attributes. `name` is an optional name of the object.
            """
            if name is not None:
                self.fil.write("o %s\n" % str(name))

            for v in mesh.coords:
                self.fil.write("v %s %s %s\n" % tuple(v))

            # element code: p(oint), l(ine) or f(ace)
            nplex = mesh.elems.shape[1]
            code = { 1:'p', 2:'l' }.get(nplex, 'f')
            s = code+(' %s'*nplex)+'\n'
            for e in mesh.elems+1:   # .obj format starts at 1
                self.fil.write(s % tuple(e))

    class PlyFile(object):
        """Export a mesh in PLY format.

        This class exports a mesh in Polygon File Format
        (see `<https://en.wikipedia.org/wiki/PLY_(file_format)>`_).

        The class instantiator takes a filename as parameter.
        Normally, the file name should end in '.ply', but anything is accepted.
        The PlyFile instance is a context manager.

        Usage::

          with PlyFile(PATH_TO_PLYFILE) as fil:
              fil.write(MESH)

        """

        def __init__(self, filename, binary=False):
            self.filename = filename
            self.binary = binary

        def __enter__(self):
            self.fil = open(self.filename, 'w')
            if self.binary:
                fmt = 'binary_' + sys.byteorder + '_endian 1.0'
            else:
                fmt = 'ascii 1.0'

            self.fil.write("""ply
    format %s
    comment .ply file written by %s
    """ %(fmt, pf.Version()))
            return self

        def __exit__(self, *args):
            self.fil.close()

        def write(self,mesh,comment=None,color_table= None):
            """Write a mesh to file in .ply format.

            Parameters:

            - `mesh`: a Mesh instance or another object having compatible
              coords and elems attributes.
            - `comment`: an extra comment written into the output file.
            - `color_table`: BEWARE! THIS IS SUBJECT TO CHANGES!

            color_table currentlyis a list of 2 elements. The first entry
            is a string that can assume 2 values 'v' or 'e', to indicate
            whether the color table represents nodal or element
            values. The second entry is an array of shape (ncoords,3) or
            (nelems,3) of interger RGB values between 0 and 255. If RGB
            values are passed as float between 0 and 1, they will be
            converted to RGB integers.
    """
            if comment is not None:
                self.fil.write("comment %s\n" % str(comment))

            coords = mesh.coords.astype('object')
            els = mesh.elems.astype('object')
            nplex = mesh.elems.shape[1]
            el_type = {2:'edge'}.get(nplex, 'face')
            vcol_type = ecol_type = ''
            if el_type == 'edge':
                eprop_type = """property int vertex1
    property int vertex2
    """
            else:
                eprop_type = """property list uchar int vertex_indices
    """

            if color_table is not None:
                color_location, color_table = color_table
                if not(np.issubclass_(color_table.dtype.type, np.integer)):
                    color_table = (255*color_table.clip(0.,1.)).astype(np.integer)
                # float can be added but they are implemented yet
                cprop_type = {}.get(color_table.dtype, 'uchar')
                cprop_types = """property %s red
    property %s green
    property %s blue
    """%((cprop_type,)*3)
                if color_location == 'v':
                    vcol_type = cprop_types
                    coords = np.hstack([coords, color_table])
                if color_location == 'e':
                    ecol_type = cprop_types
                    els = np.hstack([els, color_table])
            self.fil.write("""element vertex %s
    property float x
    property float y
    property float z
    %selement %s %s
    %s%send_header
    """ % (mesh.ncoords(), vcol_type, el_type, mesh.nelems(), eprop_type, ecol_type))

            for v in coords:
                self.fil.write('%s '*coords.shape[1]%tuple(v) +'\n')

            code = {2:'' }.get(nplex, '%s '%nplex)
            s = code+('%s '*els.shape[1])+'\n'
            for e in els:
                self.fil.write(s % tuple(e))

    # End