Source code for surface_poly_fit.cli

"""
Command line interface for surface polynomial fitting.

.. autosummary::
   :toctree: generated/

"""


[docs] def read_polyhedral_surface(file_name): """ Read mesh from file. Uses :mod:`meshio` to parse file. :type file_name: :obj:`str` :param file_name: Mesh file path. :rtype: :obj:`surface_poly_fit.core.PolyhedralSurface` """ import meshio from .core import PolyhedralSurface mesh = meshio.read(file_name) return PolyhedralSurface.from_meshio_mesh(mesh)
[docs] def write_result_array(output_file_name, result_ary, polyhedral_surface=None): """ Writes polynomial fit results and :samp:`{polyhedral_surface}` vertices, face-lists and vertex-normals to :samp:`.npz` file. The *fields* within the written :samp:`.npz` are: `"surface_poly_fit_results"` The :samp:`{result_ary}`. `"vertices"` The :samp:`{polyhedral_surface}.get_vertices()`. `"faces"` The :samp:`{polyhedral_surface}.get_faces()`. `"vertex_normals"` The :samp:`{polyhedral_surface}.get_vertex_normals()`. :type output_file_name: :obj:`str` :param output_file_name: Output file path for :func:`numpy.savez_compressed` file. :type result_ary: :obj:`numpy.ndarray` :param result_ary: Polynomial fitting result array (e.g. as returned by :meth:`surface_poly_fit.core.MongeJetFitter.fit_all`). :type polyhedral_surface: :obj:`surface_poly_fit.core.PolyhedralSurface` :param polyhedral_surface: If not :obj:`None`, write vertices, faces and vertex-normals the :samp:`.npz` file. """ from pathlib import Path import numpy as np from . import __version__ as spf_version output_file_path = Path(output_file_name) if not output_file_path.parent.exists(): output_file_path.parent.mkdir(parents=True, exists_ok=True) vertices = None faces = None vertex_normals = None if polyhedral_surface is not None: vertices = polyhedral_surface.get_vertices() faces = polyhedral_surface.get_faces() vertex_normals = polyhedral_surface.get_vertex_normals() np.savez_compressed( output_file_path, version={"surface_poly_fit": spf_version}, surface_poly_fit_results=result_ary, vertices=vertices, faces=faces, vertex_normals=vertex_normals )
[docs] def surface_poly_fit_cli(args): """ Command line interface for reading mesh file, fitting polynomials and writing results to file. :type args: :obj:`types.SimpleNamespace` :param args: Parsed command line arguments (e.g. as returned by :samp:`surface_poly_fit.cli.get_argument_parser().parse_args()`. """ import logging import os import numpy as np from .core import MongeJetFitter as Fitter logging_format = '%(asctime)s|%(process)-8s|%(name)-8s|%(levelname)-8s|%(message)s' logging.basicConfig(format=logging_format, level=getattr(logging, args.log_level)) logger = logging.getLogger() output_file_name = args.output_file if output_file_name is None: output_file_name = os.path.splitext(os.path.split(args.mesh_file)[1])[0] output_file_name += "_surface_poly_fit" + os.path.extsep + "npz" num_rings_list = eval(args.num_rings) # Convert the argument string to enum type. args.poly_fit_basis_type = Fitter.FittingBasisType.__members__[args.poly_fit_basis_type] logger.info("Reading mesh from file %s...", args.mesh_file) polyhedral_surface = read_polyhedral_surface(args.mesh_file) logger.info( "Mesh num_vertices=%8d, num_faces=%8d...", polyhedral_surface.num_vertices, polyhedral_surface.num_faces ) fitter = \ Fitter( polyhedral_surface, degree_poly_fit=args.degree_poly_fit, degree_monge=args.degree_monge ) fitter.ring_normal_gaussian_sigma = args.poly_fit_basis_gaussian_sigma logger.info("poly_fit_basis_type=%s...", args.poly_fit_basis_type) logger.info("Fitting for num_rings=%s...", num_rings_list[0]) result_ary = \ fitter.fit_all( num_rings=num_rings_list[0], fit_basis_type=args.poly_fit_basis_type ) for num_rings in num_rings_list[1:]: logger.info("Fitting for num_rings=%s...", num_rings) result_ary = np.hstack((result_ary, fitter.fit_all(num_rings=num_rings))) logger.info("Writing fitting result to file %s...", output_file_name) write_result_array(output_file_name, result_ary, polyhedral_surface)
[docs] def get_argument_parser(): """ Returns a :obj:`argparse.ArgumentParser` to handle command line option processing. :rtype: :obj:`argparse.ArgumentParser` :return: Argument parser. """ import argparse from . import MongeJetFitter ap = \ argparse.ArgumentParser( "surface_poly_fit", description=( "Fit a Monge polynomial to vertex neighbourhoods (for all vertices of a mesh)." ), formatter_class=argparse.ArgumentDefaultsHelpFormatter ) ap.add_argument( "-l", "--log_level", action='store', help="Amount of logging.", choices=("NONE", "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"), default="INFO" ) ap.add_argument( "-o", "--output_file", action='store', help="Name of the .npz file containing per-vertex poly-fit info.", default=None ) ap.add_argument( "-p", "--degree_poly_fit", action='store', help="Degree of polynomial fit to the surface.", type=int, default=2 ) ap.add_argument( "-m", "--degree_monge", action='store', help="Degree of monge-polynomial form.", type=int, default=2 ) ap.add_argument( "-r", "--num_rings", action='store', help="List of neighbourhood-ring sizes.", default="[2, 4, 8]" ) ap.add_argument( "--poly_fit_basis_type", choices=tuple(MongeJetFitter.FittingBasisType.__members__.keys()), help=( "How the polynomial fitting basis is determined from the neighbourhood-ring" + " of vertex-coorindates and/or the vertex-normals." ), default="RING_NORMAL_GAUSSIAN_WEIGHTED_MEAN" ) ap.add_argument( "--poly_fit_basis_gaussian_sigma", action="store", help=( "The sigma value used when calculating Gaussian weights for " + " RING_NORMAL_GAUSSIAN_WEIGHTED_MEAN_SIGMA polynomial fitting basis." ), type=float, default=2.0 ) ap.add_argument( "mesh_file", help="Mesh file path." ) return ap
[docs] def surface_poly_fit_main_cli(): """ Entry-point function for command line interface. """ surface_poly_fit_cli(get_argument_parser().parse_args())
if __name__ == "__main__": surface_poly_fit_main_cli()