#define PROG_NAME "stmesh_view" #define PROG_DESC "visualization of a quantized topological mesh" #define PROG_VERS "1.0" #define stmesh_view_C_COPYRIGHT \ "Copyright © 2015 by State University of Campinas (UNICAMP)" /* Last edited on 2015-11-16 01:09:47 by stolfilocal */ #define PROG_HELP \ " " PROG_NAME " \\\n" \ " -eps {EPS} \\\n" \ " -format { ascii | binary } \\\n" \ " [ -nfGuess {GUESSED_NUM_FACES} ] \\\n" \ " [ -title {TITLE} ] \\\n" \ " [ -sliceFile {SLICE_FILE} ] .. \\\n" \ " [ -noView ] \\\n" \ " " argparser_help_info_HELP " \\\n" \ " [ [ < ] {INFILE} ]" #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 {INFILE}, extracts" \ " its topological structure, and displays it for interactive viewing. Optionally, it" \ " also shows one or more flat polygons, such as slices of the mesh by planes.\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" \ " The mesh is displayed interactively. The object can be rotated around the center" \ " of interest. Manifold edges (those that are incident to exactly two triangles) are" \ " drawn in black. Other edges are drawin in orange-red.\n" \ "\n" \ "INPUT FILES\n" \ " The mesh file {INFILE} should be in STL format, either ascii or" \ " binary. (See the \"-format\" option below.) If {INFILE} is" \ " omitted or \"-\", reads from the standard input.\n" \ "\n" \ " Each {SLICE_FILE} specified with the \"-sliceFile\" keyword" \ " is supposed to contain a planar slice of mesh, in the format" \ " accepted by the {stmesh_section.h}" \ " routines. Namely, " stmesh_section_format_INFO "\n" \ "\n" \ "OPTIONS\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 topology is built.\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(stmesh_view_nfGuess_DEFAULT) "\".\n" \ "\n" \ " -title {WINDOW_TITLE} \n" \ " This optional argument specifies the title to be displayed at the top of the window.\n" \ "\n" \ " -sliceFile {SLICE_FILE}\n" \ " Each occurrence of this keyword specifies a file containing a planar" \ " polygon on some horizontal plane, typically some cross-section of" \ " the mesh. See the INPUT FILES section above for the format.\n" \ "\n" \ " -noView \n" \ " If this optional flag is present, the program exits immediately after" \ " reading the STL file and extracting its topology, without opeing any" \ " windows. This option can be used to check the validity of the file.\n" \ "\n" \ "USER INTERACTION\n" \ " The user can rotate the model around the current center of interest by clicking" \ " anywhere on the window and dragging the mouse: horizontally, to turn the model" \ " around a vertical axi, or vertically, to tilt that axis. The following" \ " keyboard commands are available too:\n" \ "\n" \ " 'q','Q' Quit the program.\n" \ " 'e' Toggle the drawing of the triangle edges.\n" \ " 'f' Toggle the filling of triangles.\n" \ " 's' Toggle the display of the cross-sections.\n" \ " 'p' Toggle the display of the reference plane.\n" \ " 'o' Toggle the display of the origin ({Z=0}) plane.\n" \ " 'Z' Zoom in, towards the center of of interest.\n" \ " 'z' Zoom out, away from the center of of interest.\n" \ "\n" \ "DOCUMENTATION OPTIONS\n" \ argparser_help_info_HELP_INFO "\n" \ "\n" \ "SEE ALSO\n" \ " salamic(1).\n" \ "\n" \ "AUTHOR\n" \ " Created 2015-10-01 by Jorge Stolfi, IC-UNICAMP.\n" \ "\n" \ "MODIFICATION HISTORY\n" \ " 2015-11-14 Added \"-sliceFile\" option.\n" \ "\n" \ "POSSIBLE FUTURE IMPROVEMENTS\n" \ " ??? Move the center of interest by arrow keys.\n" \ " ??? Set the center of interest by clicking with mouse on a vertex.\n" \ " ??? Fix triangle orientation, normals, lights.\n" \ " ??? Keys to move ref plane through odd Z values, highlight cut faces and edges.\n" \ " ??? Use transparency.\n" \ " ??? Show the bounding box.\n" \ "\n" \ "WARRANTY\n" \ argparser_help_info_NO_WARRANTY "\n" \ "\n" \ "RIGHTS\n" \ " " stmesh_view_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 #define INF INFINITY #define stmesh_view_zoom_step_ratio (15.0/16.0) /* Zoom in/out kbd commands multiply/divide the obs distance by this amount. */ typedef struct stmesh_view_options_t { char *title; /* The window title: */ /* Quantization: */ float eps; /* Vertex coords are to be rounded to even multiples of {eps}. */ /* Input model file: */ char *modelFile; /* Name of file with the input STL model. */ bool_t binary; /* FALSE if ASCII STL format, TRUE if binary STL format. */ int nfGuess; /* Hint about number of triangles in the mesh. */ /* The slice files: */ string_vec_t sliceFile; /* Names of files containing the slices. */ bool_t noView; /* If true, exit immediately after reading the mesh. */ } stmesh_view_options_t; typedef struct stmesh_view_state_t { /* The mesh: */ stmesh_t mesh; /* The slices: */ int nSlices; /* Number of given slices. */ stmesh_section_t **slice; /* The slices are {slice[0..nSlices-1]}. */ /* Reference plane: */ /* ??? replace by axes ??? */ bool_t refPlane; /* When TRUE, show reference plane (mutable). */ bool_t orgPlane; /* When TRUE, show reference plane placed at z = 0 (mutable). */ /* What to draw: */ bool_t showEdges; /* True to draw edges (wireframe). */ bool_t showFaces; /* True to fill faces. */ bool_t showSlices; /* True to fill faces. */ /* Current window dimensions (set by {stmesh_view_reshape_method}): */ GLint window_HSize; /* Width (mutable). */ GLint window_VSize; /* Height (mutable). */ /* Observer's position relative to center of interest: */ GLfloat azimuth; /* Azimuth from X axis (degrees, mutable). */ GLfloat elevation; /* Elevation from XY plane (degrees, mutable). */ GLfloat distance; /* Distance (pixels, mutable). */ GLfloat center[3]; /* Center of interest. */ double RAD; /* Nominal radius of mesh (for reasonable Z scale). */ /* Mouse state: */ int mouse_x,mouse_y; /* Position of last mouse event (mutable). */ } stmesh_view_state_t; static stmesh_view_state_t *stmesh_view_state = NULL; /* The state displayed in the GL window. */ /* INTERNAL PROTOTYPES */ int main(int argc, char **argv); /* Main program. */ stmesh_view_options_t *stmesh_view_parse_options(int argc, char **argv); /* Parses the command line arguments and packs them as an {options_t}. */ stmesh_view_state_t *stmesh_view_create_state ( stmesh_t mesh, stmesh_view_options_t *o ); /* Creates and initializes a window data record, suitable for use used by the methods {stmesh_view_display_method} etc. */ stmesh_section_t **stmesh_view_read_slices(stmesh_t mesh, uint32_t nf, char *fname[]); /* Reads the cross-section files whose names are {fname[0..nf-1]}, returns a vector of pointers to their descriptors. Prints warnings if the slice parameters are inconsitent with those of the mesh. */ /* MAJOR PAINTING FUNCTIONS */ void stmesh_view_paint_everything(stmesh_view_state_t *w); /* Repaints the terrain according to the data and parameters in {w}. */ void stmesh_view_set_perspective(stmesh_view_state_t *w); /* Sets the GL perspective view parameters according to the window size, observer position, and bounding box data stored in {w}. */ void stmesh_view_set_lights(stmesh_view_state_t *w); /* Sets the GL lighting parameters according to the state {w}. */ /* GL WINDOW METHODS */ void stmesh_view_display_method(void); /* Processes redisplay requests by the window manager. */ void stmesh_view_reshape_method(int width, int height); /* Processes window reshape events. */ void stmesh_view_keyboard_method(unsigned char key, int x, int y); /* Processes keyboard events. */ void stmesh_view_passivemouse_method( int x, int y); /* Processes passive mouse events (no mouse clicked) */ void stmesh_view_activemouse_method( int x, int y); /* Processes active mouse events (mouse clicked) */ void stmesh_view_special_method(int key, int x, int y); /* Processes special user input events. */ /* IMPLEMENTATIONS */ int main(int argc, char **argv) { /* Initialize the GL libraries. Note that it may delete some command line args. */ stmesh_view_GL_initialize_libraries(&argc, argv); /* Parse the remaining command line options: */ stmesh_view_options_t *o = stmesh_view_parse_options(argc, argv); /* Get the input triangle mesh {mesh} and its {Z}-range {minZ,maxZ}: */ bool_t even = TRUE; /* Rount to even multiples of {eps}, as in the slicer. */ stmesh_t mesh = stmesh_STL_read(o->modelFile, o->binary, o->eps, o->nfGuess, even, FALSE); /* Do we really want to show it? */ if (o->noView) { return 0; } /* Initialize the UI state: */ stmesh_view_state = stmesh_view_create_state(mesh, o); /* Open the window and start interaction: */ stmesh_view_GL_initialize_window ( o->title, stmesh_view_display_method, stmesh_view_reshape_method, stmesh_view_keyboard_method, stmesh_view_passivemouse_method, stmesh_view_activemouse_method, stmesh_view_special_method ); stmesh_view_GL_start_viewing(); return 0; } stmesh_view_state_t *stmesh_view_create_state ( stmesh_t mesh, stmesh_view_options_t *o ) { stmesh_view_state_t *w = notnull(malloc(sizeof(stmesh_view_state_t)), "no mem"); w->window_HSize = stmesh_view_GL_default_window_HSize; w->window_VSize = stmesh_view_GL_default_window_VSize; /* Save the mesh in the state: */ w->mesh = mesh; /* Read the slices and save in state: */ w->nSlices = o->sliceFile.ne; w->slice = stmesh_view_read_slices(mesh, o->sliceFile.ne, o->sliceFile.e); /* Do not show reference planes initially: */ w->refPlane = FALSE; w->orgPlane = FALSE; /* Draw both edges and vertices initially: */ w->showEdges = TRUE; w->showFaces = TRUE; w->showSlices = TRUE; /* Initial center of interest and nominal radius of data: */ float eps = stmesh_get_eps(mesh); i3_t minP, maxP; stmesh_get_bounding_box(mesh, &minP, &maxP); r3_t minR = stmesh_unround_point(&(minP), eps); r3_t maxR = stmesh_unround_point(&(maxP), eps); r3_t ctr, rad; int k; for (k = 0; k < 3; k++) { rad.c[k] = (maxR.c[k] - minR.c[k])/2; ctr.c[k] = (minR.c[k] + maxR.c[k])/2; w->center[k] = (GLfloat)ctr.c[k]; } w->RAD = r3_norm(&rad); /* Initial observer's position: */ w->azimuth = 0; w->elevation = 30; w->distance = (GLfloat)(2*w->RAD); return w; } stmesh_section_t **stmesh_view_read_slices(stmesh_t mesh, uint32_t nf, char *fname[]) { bool_t verbose = TRUE; /* Get mesh parameters for consistency checking: */ float eps_mesh = stmesh_get_eps(mesh); i3_t minQ, maxQ; /* Quantized bounding box of mesh. */ stmesh_get_bounding_box(mesh, &minQ, &maxQ); double fminZ = ((double)eps_mesh)*((double)minQ.c[2]); /* Min {Z} coord in mm. */ double fmaxZ = ((double)eps_mesh)*((double)maxQ.c[2]); /* Max {Z} coord in mm. */ stmesh_section_t **slice = notnull(malloc(nf*sizeof(stmesh_section_t*)), "no mem"); int i; for (i = 0; i < nf; i++) { FILE *rd = open_read(fname[i], verbose); stmesh_section_t *sec = stmesh_section_read(rd); /* Check consistency with mesh parameters: */ if (fabs(((double)sec->eps)/((double)eps_mesh) - 1.0) > 1.0e-7) { fprintf(stderr, "!! warning: {sec.eps} = %+23.15e", (double)sec->eps); fprintf(stderr, " differs from {mesh.eps} = %+23.15e\n", (double)eps_mesh); } double fpZ = ((double)sec->eps)*((double)sec->pZ); if ((fpZ - fminZ < -1.0e-6) || (fpZ - fmaxZ > +1.0e-6)) { fprintf(stderr, "!! warning: {sec.Z} = %+10.7f mm", fpZ); fprintf(stderr, " outside mesh {Z} range [%+10.7f _ %+10.7f]\n", fminZ, fmaxZ); } slice[i] = sec; fclose(rd); } return slice; } void stmesh_view_paint_everything(stmesh_view_state_t *w) { /* Get the corners of the model's bounding box: */ float eps = stmesh_get_eps(w->mesh); i3_t minP, maxP; stmesh_get_bounding_box(w->mesh, &minP, &maxP); r3_t minR = stmesh_unround_point(&(minP),eps); r3_t maxR = stmesh_unround_point(&(maxP),eps); /* Set the GL graphics modes for depth painting: */ glShadeModel(GL_SMOOTH); glDepthMask(GL_TRUE); glEnable(GL_DEPTH_TEST); /* Compute observer's position: */ double az = M_PI*w->azimuth/180; /* Observer's azimuth in radians. */ double ev = M_PI*w->elevation/180; /* Observer's elevation in radians. */ double ca = cos(az), sa = sin(az); double ce = cos(ev), se = sin(ev); double R = w->distance; GLfloat obs[3]; obs[0] = w->center[0] + (GLfloat)(R * ca * ce); obs[1] = w->center[1] + (GLfloat)(R * sa * ce); obs[2] = w->center[2] + (GLfloat)(R * se); glPushMatrix(); /* Set the view transformation matrix: */ gluLookAt( obs[0], obs[1], obs[2], w->center[0], w->center[1], w->center[2], 0.0, 0.0, 1.0); /* Place the lights: */ stmesh_view_set_lights(w); /* Paint the reference planes, if any: */ if (w->refPlane) { stmesh_view_paint_reference_plane(&minR, &maxR, minR.c[2]); stmesh_view_paint_reference_plane(&minR, &maxR, (double)(w->center[2])); stmesh_view_paint_reference_plane(&minR, &maxR, maxR.c[2]); } if (w->orgPlane) { double zplane = 0; stmesh_view_paint_reference_plane(&minR, &maxR, zplane); } /* Paint the mesh: */ stmesh_view_paint_mesh(w->mesh, w->showFaces, w->showEdges); if (w->showSlices) { /* Show the slices: */ stmesh_view_paint_slices(w->mesh, w->nSlices, w->slice); } glPopMatrix(); glDisable(GL_DEPTH_TEST); } void stmesh_view_set_perspective(stmesh_view_state_t *w) { /* Set viewport: */ glViewport(0, 0, (GLint)w->window_HSize, (GLint)w->window_VSize); /* Set perspective matrix: */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); float angheight = 40.0; /* Angular height of window (degrees) */ float aspect = (float)w->window_HSize/(float)w->window_VSize; gluPerspective(angheight, aspect, 0.1, w->distance + 2.0*w->RAD); glMatrixMode(GL_MODELVIEW); } void stmesh_view_set_lights(stmesh_view_state_t *w) { glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); double ambi = 0.3; /* Amount of ambient light. */ double spec = 0.0; /* Amount of shine-scatterable light. */ double diff = 1.0 - ambi - spec; /* Amount of diffusible light. */ if (ambi > 0) { GLfloat light_ambient[] = { (GLfloat)ambi, (GLfloat)ambi, (GLfloat)ambi, (GLfloat)1.0 }; glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); } if (diff > 0) { GLfloat light_diffuse[] = { (GLfloat)diff, (GLfloat)diff, (GLfloat)diff, (GLfloat)1.0 }; glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); } if (spec > 0) { GLfloat light_specular[] = { (GLfloat)spec, (GLfloat)spec, (GLfloat)spec, (GLfloat)1.0 }; glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); } /* Choose light's position relative to observer: */ double daz = +30; /* Azimuth rel to observer (degrees). */ double dev = +30; /* Elevation rel to observer (degrees). */ /* Compute light direction vector: */ double az = M_PI*(w->azimuth + daz)/180; /* Light's azimuth (radians). */ double ev = M_PI*(w->elevation + dev)/180; /* Observer's elevation (radians). */ double ca = cos(az), sa = sin(az); double ce = cos(ev), se = sin(ev); GLfloat dirx = (GLfloat)(ca * ce); GLfloat diry = (GLfloat)(sa * ce); GLfloat dirz = (GLfloat)se; /* Place light at infinity: */ GLfloat pos[] = { dirx, diry, dirz, 0.0 }; glLightfv(GL_LIGHT0, GL_POSITION, pos); } /* GL EVENT-HANDLING METHODS */ void stmesh_view_display_method(void) { if (stmesh_view_debug_GL) { fprintf(stderr, "+ %s\n", __FUNCTION__); } stmesh_view_state_t *w = stmesh_view_state; /* Clear the window: */ glClearColor(0.750, 0.750, 0.750, 1.000); /* Light gray, opaque. */ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); /* Paint the terrain and stuff: */ stmesh_view_paint_everything(w); /* Display the result: */ glutSwapBuffers(); if (stmesh_view_debug_GL) { fprintf(stderr, "- %s\n", __FUNCTION__); } } void stmesh_view_reshape_method(int width, int height) { if (stmesh_view_debug_GL) { fprintf(stderr, "+ %s\n", __FUNCTION__); } stmesh_view_state_t *w = stmesh_view_state; /* Save window size: */ w->window_HSize = width; w->window_VSize = height; /* Adjust the perspective parameters to the window's current size: */ stmesh_view_set_perspective(w); if (stmesh_view_debug_GL) { fprintf(stderr, "- %s\n", __FUNCTION__); } } void stmesh_view_keyboard_method(unsigned char key, int x, int y) { if (stmesh_view_debug_GL) { fprintf(stderr, "+ %s\n", __FUNCTION__); } stmesh_view_state_t *w = stmesh_view_state; switch (key) { case 27: case 'q': case 'Q': /* Quit: */ exit(0); break; case 'e': /* Toggle edge drawing: */ w->showEdges = !w->showEdges; glutPostRedisplay(); break; case 'f': /* Toggle face filling: */ w->showFaces = !w->showFaces; glutPostRedisplay(); break; case 's': /* Toggle cross-section display: */ w->showSlices = !w->showSlices; glutPostRedisplay(); break; case 'p': /* Toggle reference plane: */ w->refPlane = !w->refPlane; glutPostRedisplay(); break; case 'o': /* Toggle reference plane: */ w->orgPlane = !w->orgPlane; glutPostRedisplay(); break; case 'Z': /* Zoom in, down to a limit: */ w->distance = (GLfloat)fmax(w->distance*stmesh_view_zoom_step_ratio, w->RAD/16); glutPostRedisplay(); break; case 'z': /* Zoom out, up to a limit: */ w->distance = (GLfloat)fmin(w->distance/stmesh_view_zoom_step_ratio, w->RAD*16); glutPostRedisplay(); break; } if (stmesh_view_debug_GL) { fprintf(stderr, "- %s\n", __FUNCTION__); } } void stmesh_view_passivemouse_method( int x, int y) { stmesh_view_state_t *w = stmesh_view_state; w->mouse_x = x; w->mouse_y = y; } void stmesh_view_activemouse_method(int x, int y) { stmesh_view_state_t *w = stmesh_view_state; int xvec = w->mouse_x - x; int yvec = w->mouse_y - y; double az = (double)w->azimuth + xvec; double el = (double)w->elevation - yvec; el = fmax(-88.0, fmin(+88.0, el)); w->elevation = (GLfloat)el; w->azimuth = (GLfloat)az; w->mouse_x = x; w->mouse_y = y; glutPostRedisplay(); } void stmesh_view_special_method(int key, int x, int y) { if (stmesh_view_debug_GL) { fprintf(stderr, "+ %s\n", __FUNCTION__); } stmesh_view_state_t *w = stmesh_view_state; switch (key) { case GLUT_KEY_UP: w->elevation = (GLfloat)fmin(w->elevation + 2, +88.0); glutPostRedisplay(); break; case GLUT_KEY_DOWN: w->elevation = (GLfloat)fmax(w->elevation - 2, -88.0); glutPostRedisplay(); break; case GLUT_KEY_LEFT: w->azimuth = w->azimuth + 2; glutPostRedisplay(); break; case GLUT_KEY_RIGHT: w->azimuth = w->azimuth - 2; glutPostRedisplay(); break; } if (stmesh_view_debug_GL) { fprintf(stderr, "- %s\n", __FUNCTION__); } } #define stmesh_view_eps_MIN (0.000001f) /* Minimum value of fundamental unit {eps} (mm). */ #define stmesh_view_eps_MAX (1.0f) /* Maximum value of fundamental unit {eps} (mm). */ #define stmesh_view_nfGuess_MAX (500*1000*1000) /* Default guess for the number of faces in the mesh. */ #define stmesh_view_nfGuess_DEFAULT 100000 /* Maximum guess for the number of faces in the mesh. */ stmesh_view_options_t *stmesh_view_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: */ stmesh_view_options_t *o = notnull(malloc(sizeof(stmesh_view_options_t)), "no mem"); /* Parse keyword parameters: */ /* Window title: */ if (argparser_keyword_present(pp, "-title")) { o->title = argparser_get_next_non_keyword(pp); } else { o->title = ( PROG_NAME " " PROG_VERS ); } /* Fundamental unit: */ argparser_get_keyword(pp, "-eps"); o->eps = (float)argparser_get_next_double(pp, stmesh_view_eps_MIN, stmesh_view_eps_MAX); /* Input format: */ 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"); } /* Size guess: */ if (argparser_keyword_present(pp, "-nfGuess")) { o->nfGuess = (int)argparser_get_next_int(pp, 0, stmesh_view_nfGuess_MAX); } else { o->nfGuess = stmesh_view_nfGuess_DEFAULT; } /* Slice files: */ o->sliceFile = string_vec_new(1000); int nf = 0; while (argparser_keyword_present(pp, "-sliceFile")) { string_vec_expand(&(o->sliceFile), nf); o->sliceFile.e[nf] = argparser_get_next_non_keyword(pp); nf++; } string_vec_trim(&(o->sliceFile), nf); /* Read-only flag: */ o->noView = argparser_keyword_present(pp, "-noView"); /* argparser_get_keyword(pp, "-outPrefix"); */ /* o->outPrefix = argparser_get_next_non_keyword(pp); */ /* Parse positional arguments: */ argparser_skip_parsed(pp); /* Input file name: */ if (argparser_next_is_non_keyword(pp)) { o->modelFile = argparser_get_next(pp); } else { /* Use standard input. */ o->modelFile = "-"; } /* Check for spurious arguments: */ argparser_finish(pp); return o; }