#define PROG_NAME "salamic" #define PROG_DESC "topological slicing of triangle meshes for 3D printing" #define PROG_VERS "1.0" #define salamic_C_COPYRIGHT \ "Copyright © 2015 by Federal Technological Universioty of Parana (UTFPR) and State University of Campinas (UNICAMP)" /* Last edited on 2015-11-17 14:37:04 by stolfilocal */ #define PROG_HELP \ " " PROG_NAME " \\\n" \ " -modelFile {MODEL_FILE} \\\n" \ " -format { ascii | binary } \\\n" \ " [ -nfGuess {GUESSED_NUM_FACES} ] \\\n" \ " -eps {EPS} \\\n" \ " [ -preSorted ] \\\n" \ " { \\\n" \ " -planeZFile {PZ_FILE} | \\\n" \ " -deltaZ {DELTA_Z} [ -startZ {START_Z} ] \\\n" \ " } \\\n" \ " [ -slicer { TRIVIAL | MINETTO } ] \\\n" \ " [ -closer { NONE | TRIVIAL | STOLFI } ] \\\n" \ " -outPrefix {OUT_PREFIX} \\\n" \ " " argparser_help_info_HELP "" #define PROG_INFO \ "NAME\n" \ " " PROG_NAME " - " PROG_DESC "\n" \ "\n" \ "SYNOPSIS\n" \ PROG_HELP "\n" \ "\n" \ "DESCRIPTION\n" \ " The program reads an unstructured triangle mesh from an STL file {MODEL_FILE}, and slices it" \ " by a list of horizontal planes. The {Z}-coordinates of the planes may be arbitrary.\n" \ "\n" \ " " stmesh_STL_read_INFO "\n" \ "\n" \ " The program does not detect (or care) about purely geometric problems like" \ " two distinct triangles or two distinct edges whose interiors intersect. It also does not check" \ " presently whether every vertex has the manifold topology (at most one ring or" \ " one fan of triangles incident to it).\n" \ "\n" \ "OUTPUT FILES\n" \ " " salamic_closer_output_file_name_INFO "\n" \ "\n" \ " " stmesh_section_format_INFO "\n" \ "\n" \ "OPTIONS\n" \ " -modelFile {MODEL_FILE}\n" \ " This mandatory argument specifies the name of the input STL file.\n" \ "\n" \ " -format ascii\n" \ " -format binary\n" \ " This mandatory argument specifies the format of the input STL file.\n" \ "\n" \ " -nfGuess {GUESSED_NUM_FACES} \n" \ " This optional argument is a hint for the number of triangles that are" \ " expected to be in the input file. It is used by the program to preallocate" \ " internal tables, so that they don't have to be expanded dynamically while" \ " the file is being read. The program is more efficient if {GUESSED_NUM_FACES} is the" \ " actual number of triangles, or somewhat higher. If omitted, the" \ " program assumes \"-nfGuess " stringify(salamic_nfGuess_DEFAULT) "\".\n" \ "\n" \ " -eps {EPS}\n" \ " This mandatory argument specifies the basic unit of length" \ " (in millimeters). All input coordinates will be rounded" \ " to even integer multiples of {EPS} before the mesh topology" \ " is extracted from the STL data.\n" \ "\n" \ " -preSorted\n" \ " This optional argument, if present, tells the program that" \ " the triangles in the input STL file are already sorted by" \ " increasing {minZ} coordinate. The slicing algorithm will" \ " run slightly faster in this case. The program will fail" \ " if this option is specified but the file is not sorted. If this" \ " option is omitted, the program does not make any assumption about" \ " the order of the triangles in the file.\n" \ "\n" \ " -planeZFile {PZ_FILE}\n" \ " This argument specifies the name of the file that contains the Z-coordinates" \ " of the slicing planes. If this argument is present, the \"-deltaZ\" argument" \ " should be omitted, and vice-versa. Each {Z}-coordinate read from the file is" \ " divided by the fundamental unit of length {EPS} and rounded to the nearest" \ " odd integer. Coordinates that do not intersect the model's bounding box" \ " are ignored.\n" \ "\n" \ " " salamic_planes_file_format_INFO "\n" \ "\n" \ " -deltaZ {DELTA_Z}\n" \ " This argument specifies the spacing between the slicing planes, in" \ " millimeters, for uniform slicing. If this argument is" \ " present, the \"-planeZFile\" argument should be omitted, and vice-versa. The" \ " given spacing {DELTA_Z} will be rounded to an even integer multiple of" \ " the {EPS} length unit. Beware that this rounding may cause cumulative" \ " displacement of the slicing planes by up to {NP*EPS} millimeters, where" \ " {NP} is the number of planes.\n" \ "\n" \ " -startZ {START_Z}\n" \ " This optional argument specifies the {Z}-coordinate of one" \ " of the slicing planes. This argument may be present only if the \"-deltaZ\" option (uniform" \ " slicing) is used. The given coordinate {START_Z} will be rounded" \ " to an odd integer multiple of the {EPS} length unit. In spite of" \ " the argument's name, the slicing planes will extend above and" \ " below {START_Z}, spanning the whole height of the model. If this" \ " argument is omitted, the program assumes {START_Z = EPS}.\n" \ "\n" \ " -slicer { TRIVIAL | MINETTO }\n" \ " This optional argument specifies the slicing algorithm to" \ " be used. The \"TRIVIAL\" algorithm tests every triangle against" \ " every slicing plane. The \"MINETTO\" algorithm uses the sweep" \ " plane technique described by Minetto, Volpato and Stolfi (2015). If" \ " this argument is not specified, the program uses the \"MINETTO\" slicer.\n" \ "\n" \ " -closer { NONE | TRIVIAL | STOLFI }\n" \ " This optional argument specifies the algorithm to be used for converting" \ " the unstructured lists of line segments on each slicing plane into" \ " a set of maximal open or closed paths. The \"TRIVIAL\" algorithm" \ " searches for the next segment that extends the current path " \ " by scanning the entire segment list each time. The \"STOLFI\" algorithm" \ " uses a technique similar to the one described by Minetto, Volpato and" \ " Stolfi (2015), except that topological mesh information (instead of" \ " {X,Y} coordinates of path vertices) is used to locate the next" \ " segment. The \"NONE\" option is intended for debugging and timing" \ " tests: if chosen, the program will still find the triangles" \ " intercepted by each slicing plane, but will skip the processing and output" \ " of the slice contours. If this argument is omitted, the" \ " program uses the \"STOLFI\" closer.\n" \ "\n" \ "DOCUMENTATION OPTIONS\n" \ argparser_help_info_HELP_INFO "\n" \ "\n" \ "SEE ALSO\n" \ " bolognac(1), prosciuttox(1), pancettaz(1).\n" \ "\n" \ "AUTHOR\n" \ " Created 2015-09-25 by Jorge Stolfi, IC-UNICAMP from {SliceMain.cpp} by Minetto and Habib.\n" \ "\n" \ "MODIFICATION HISTORY\n" \ " 2015-09-26 Converted to C by J. Stolfi, IC-UNICAMP.\n" \ " 2015-10-01 Separated the {stmesh} library (J. Stolfi).\n" \ " 2015-10-18 Added the \"STOLFI\" closer; bug fixes (J. Stolfi).\n" \ " 2015-11-14 Fixed a bug in slice vertex computation (R. Minetto).\n" \ " 2015-11-14 Changed the format of slice output files (J. Stolfi).\n" \ " 2015-11-14 Moved slice i/o routines to {libstmesh} (J. Stolfi).\n" \ " 2015-11-16 Fixed bug in mesh reading (R. Minetto).\n" \ " 2015-11-16 Fixed bug in binary search (J. Stolfi).\n" \ "\n" \ "WARRANTY\n" \ argparser_help_info_NO_WARRANTY "\n" \ "\n" \ "RIGHTS\n" \ " " salamic_C_COPYRIGHT ".\n" \ "\n" \ argparser_help_info_STANDARD_RIGHTS #define stringify(x) strngf(x) #define strngf(x) #x #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #include */ /* #include */ typedef struct salamic_options_t { /* Quantization: */ float eps; /* Vertex and plane coords are multiple of {eps}. */ /* Parameters for slicing planes: */ float startZ; /* First {Z} coordinate (mm), or {NAN}. */ float deltaZ; /* Spacing for uniform slicing (mm), or {NAN}. */ char *planeZFile; /* Name of file with plane {Z}-coords. */ /* Input files: */ char *modelFile; /* Name of file with the input STL model. */ bool_t binary; /* FALSE if ASCII STL format, TRUE if binary STL format. */ bool_t preSorted; /* If true, assumes that the input triangles be sorted by increasing {Z}. */ int nfGuess; /* Hint about number of triangles in the mesh. */ /* Algorithms: */ char *slicer; /* Slicer algorithm ("TRIVIAL", "MINETTO", "SANG", etc.). */ char *closer; /* Loop closing algorithm ("TRIVIAL", "STOLFI", etc.). */ /* Output files: */ char *outPrefix; /* Prefix for output file names. */ } salamic_options_t; /* INTERNAL PROTOTYPES */ int main(int argc, char **argv); /* Main program. */ salamic_options_t *salamic_parse_options(int argc, char **argv); /* Parses the command line arguments and packs them as an {options_t}. */ /* IMPLEMENTATIONS */ int main(int argc, char **argv) { /* Parse the command line options: */ salamic_options_t *o = salamic_parse_options(argc, argv); /* Get the input triangle mesh {mesh}, quantizing to even integers: */ stmesh_t mesh = stmesh_STL_read(o->modelFile, o->binary, o->eps, o->nfGuess, TRUE, o->preSorted); /* Get the Z-coordinates of the slicing planes, in increasing order: */ i3_t minP, maxP; /* Bounding box of mesh. */ stmesh_get_bounding_box(mesh, &minP, &maxP); bool_t uniform = ((! isnan(o->deltaZ)) && (o->deltaZ != 0)); int_vec_t planeZ; /* The {Z}-coordinates of the slicing planes, quantized by {eps}. */ if (uniform) { planeZ = salamic_planes_get_uniform(o->startZ, o->deltaZ, o->eps, minP.c[2], maxP.c[2]); } else { planeZ = salamic_planes_get_adaptive(o->planeZFile, o->eps, minP.c[2], maxP.c[2]); } /* Process it: */ salamic_stats_t st; salamic_slicer_slice(mesh, o->preSorted, &planeZ, uniform, o->slicer, o->closer, o->outPrefix, &st); /* Show statistics: */ salamic_stats_print(stderr, &st); fprintf(stderr, "done.\n"); return 0; } /* ARGUMENT PARSING */ #define salamic_eps_MIN (0.000001f) /* Minimum value of fundamental unit {eps} (mm). */ #define salamic_eps_MAX (1.0f) /* Maximum value of fundamental unit {eps} (mm). */ #define salamic_deltaZ_MINQ 10 /* Minimum plane spacing for uniform slicing, as multiple of fundamental unit {eps}. */ #define salamic_deltaZ_MAX 1000.0 /* Maximum plane spacing for uniform slicing (mm). */ #define salamic_startZ_MAX (10000.0f) /* Max {Z} coordinate of reference plane (mm). */ #define salamic_startZ_DEFAULTQ 1 /* Default {Z} coordinate of start plane, an odd multiple of fundamental unit {eps}. */ #define salamic_nfGuess_MAX (500*1000*1000) /* Default guess for the number of faces in the mesh. */ #define salamic_nfGuess_DEFAULT 100000 /* Maximum guess for the number of faces in the mesh. */ salamic_options_t *salamic_parse_options(int argc, char** argv) { /* Initialize argument parser: */ argparser_t *pp = argparser_new(stderr, argc, argv); argparser_set_help(pp, PROG_NAME " version " PROG_VERS ", usage:\n" PROG_HELP); argparser_set_info(pp, PROG_INFO); argparser_process_help_info_options(pp); /* Allocate the command line argument record: */ salamic_options_t *o = notnull(malloc(sizeof(salamic_options_t)), "no mem"); /* Parse keyword parameters: */ /* Fundamental unit: */ argparser_get_keyword(pp, "-eps"); o->eps = (float)argparser_get_next_double(pp, salamic_eps_MIN, salamic_eps_MAX); /* {Z}-coordinates of planes: */ if (argparser_keyword_present(pp, "-planeZFile")) { /* Read plane positions from file: */ o->planeZFile = argparser_get_next_non_keyword(pp); o->deltaZ = NAN; o->startZ = NAN; } else { /* Uniform slicing: */ argparser_get_keyword(pp, "-deltaZ"); o->deltaZ = (float)argparser_get_next_double(pp, salamic_deltaZ_MINQ*o->eps, salamic_deltaZ_MAX); if (argparser_keyword_present(pp, "-startZ")) { o->startZ = (float)argparser_get_next_double(pp, -salamic_startZ_MAX, +salamic_startZ_MAX); } else { o->startZ = o->eps; } o->planeZFile = NULL; } /* Model, format, and size guess: */ argparser_get_keyword(pp, "-modelFile"); o->modelFile = argparser_get_next_non_keyword(pp); argparser_get_keyword(pp, "-format"); if (argparser_keyword_present_next(pp, "ascii")) { o->binary = FALSE; } else if (argparser_keyword_present_next(pp, "binary")) { o->binary = TRUE; } else { argparser_error(pp, "unrecognized file format"); } o->preSorted = argparser_keyword_present(pp, "-preSorted"); if (argparser_keyword_present(pp, "-nfGuess")) { o->nfGuess = (int)argparser_get_next_int(pp, 0, salamic_nfGuess_MAX); } else { o->nfGuess = salamic_nfGuess_DEFAULT; } /* Algorithms: */ if (argparser_keyword_present(pp, "-slicer")) { o->slicer = argparser_get_next_non_keyword(pp); } else { o->slicer = "MINETTO"; } if (argparser_keyword_present(pp, "-closer")) { o->closer = argparser_get_next_non_keyword(pp); } else { o->closer = "STOLFI"; } argparser_get_keyword(pp, "-outPrefix"); o->outPrefix = argparser_get_next_non_keyword(pp); /* Parse positional arguments: */ argparser_skip_parsed(pp); /* Check for spurious arguments: */ argparser_finish(pp); return o; }