/* GTS - Library for the manipulation of triangulated surfaces * Copyright (C) 1999 Stéphane Popinet * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include #include "config.h" #ifdef HAVE_GETOPT_H # include #endif /* HAVE_GETOPT_H */ #ifdef HAVE_UNISTD_H # include #endif /* HAVE_UNISTD_H */ #include "gts.h" #ifdef NATIVE_WIN32 # include # include #endif static GPtrArray * stl_read (FILE * fp) { GPtrArray * a = g_ptr_array_new (); char tag[6]; fgets (tag, 6, fp); rewind (fp); if (!strcmp (tag, "solid")) { /* ASCII file */ GtsFile * f = gts_file_new (fp); while (f->type == GTS_STRING) { if (!strcmp (f->token->str, "vertex")) { gdouble x, y, z; gts_file_next_token (f); if (f->type != GTS_INT && f->type != GTS_FLOAT) { fprintf (stderr, "Input file is not a valid STL file\n" "stdin:%d:%d: expecting a number (x-coordinate)\n", f->line, f->pos); exit (1); } x = atof (f->token->str); gts_file_next_token (f); if (f->type != GTS_INT && f->type != GTS_FLOAT) { fprintf (stderr, "Input file is not a valid STL file\n" "stdin:%d:%d: expecting a number (y-coordinate)\n", f->line, f->pos); exit (1); } y = atof (f->token->str); gts_file_next_token (f); if (f->type != GTS_INT && f->type != GTS_FLOAT) { fprintf (stderr, "Input file is not a valid STL file\n" "stdin:%d:%d: expecting a number (z-coordinate)\n", f->line, f->pos); exit (1); } z = atof (f->token->str); g_ptr_array_add (a, gts_vertex_new (gts_vertex_class (), x, y, z)); } else if (!strcmp (f->token->str, "endsolid")) break; gts_file_first_token_after (f, '\n'); } gts_file_destroy (f); } else { /* binary file */ guint32 nf; gchar header[80]; guint i; #ifdef NATIVE_WIN32 _setmode ( _fileno (fp), _O_BINARY); #endif if (fread (header, sizeof (gchar), 80, fp) != 80) { fprintf (stderr, "Input file is not a valid STL file\n" "stdin: incomplete header\n"); exit (1); } if (fread (&nf, sizeof (guint32), 1, fp) != 1) { fprintf (stderr, "Input file is not a valid STL file\n" "stdin: missing number of facets\n"); exit (1); } i = nf; while (i > 0) { gfloat x, y, z; guint j; guint16 attbytecount; if (fread (&x, sizeof (gfloat), 1, fp) != 1) { fprintf (stderr, "Input file is not a valid STL file\n" "stdin: missing normal x-coordinate\n"); exit (1); } if (fread (&y, sizeof (gfloat), 1, fp) != 1) { fprintf (stderr, "Input file is not a valid STL file\n" "stdin: missing normal y-coordinate\n"); exit (1); } if (fread (&z, sizeof (gfloat), 1, fp) != 1) { fprintf (stderr, "Input file is not a valid STL file\n" "stdin: missing normal z-coordinate\n"); exit (1); } for (j = 0; j < 3; j++) { if (fread (&x, sizeof (gfloat), 1, fp) != 1) { fprintf (stderr, "Input file is not a valid STL file\n" "stdin: missing vertex x-coordinate\n"); exit (1); } if (fread (&y, sizeof (gfloat), 1, fp) != 1) { fprintf (stderr, "Input file is not a valid STL file\n" "stdin: missing vertex y-coordinate\n"); exit (1); } if (fread (&z, sizeof (gfloat), 1, fp) != 1) { fprintf (stderr, "Input file is not a valid STL file\n" "stdin: missing vertex z-coordinate\n"); exit (1); } g_ptr_array_add (a, gts_vertex_new (gts_vertex_class (), x, y, z)); } if (fread (&attbytecount, sizeof (guint16), 1, fp) != 1) { fprintf (stderr, "Input file is not a valid STL file\n" "stdin: missing attribute byte count\n"); exit (1); } i--; } } return a; } static void vertices_merge (GPtrArray * stl, gdouble epsilon) { GPtrArray * array; GNode * kdtree; guint i; array = g_ptr_array_new (); for (i = 0; i < stl->len; i++) g_ptr_array_add (array, stl->pdata[i]); kdtree = gts_kdtree_new (array, NULL); g_ptr_array_free (array, TRUE); for (i = 0; i < stl->len; i++) { GtsVertex * v = stl->pdata[i]; if (!GTS_OBJECT (v)->reserved) { /* Do something only if v is active */ GtsBBox * bbox; GSList * selected, * j; /* build bounding box */ bbox = gts_bbox_new (gts_bbox_class (), v, GTS_POINT (v)->x - epsilon, GTS_POINT (v)->y - epsilon, GTS_POINT (v)->z - epsilon, GTS_POINT (v)->x + epsilon, GTS_POINT (v)->y + epsilon, GTS_POINT (v)->z + epsilon); /* select vertices which are inside bbox using kdtree */ j = selected = gts_kdtree_range (kdtree, bbox, NULL); while (j) { GtsVertex * sv = j->data; if (sv != v && !GTS_OBJECT (sv)->reserved) GTS_OBJECT (sv)->reserved = v; /* mark sv as inactive */ j = j->next; } g_slist_free (selected); gts_object_destroy (GTS_OBJECT (bbox)); } } gts_kdtree_destroy (kdtree); /* destroy inactive vertices */ /* we want to control vertex destruction */ gts_allow_floating_vertices = TRUE; for (i = 0; i < stl->len; i++) { GtsVertex * v = stl->pdata[i]; if (GTS_OBJECT (v)->reserved) { /* v is inactive */ stl->pdata[i] = GTS_OBJECT (v)->reserved; gts_object_destroy (GTS_OBJECT (v)); } } gts_allow_floating_vertices = FALSE; } static void add_stl (GtsSurface * s, GPtrArray * stl) { guint i; for (i = 0; i < stl->len/3; i++) { GtsEdge * e1 = GTS_EDGE (gts_vertices_are_connected (stl->pdata[3*i], stl->pdata[3*i + 1])); GtsEdge * e2 = GTS_EDGE (gts_vertices_are_connected (stl->pdata[3*i + 1], stl->pdata[3*i + 2])); GtsEdge * e3 = GTS_EDGE (gts_vertices_are_connected (stl->pdata[3*i + 2], stl->pdata[3*i])); if (e1 == NULL && stl->pdata[3*i] != stl->pdata[3*i + 1]) e1 = gts_edge_new (s->edge_class, stl->pdata[3*i], stl->pdata[3*i + 1]); if (e2 == NULL && stl->pdata[3*i + 1] != stl->pdata[3*i + 2]) e2 = gts_edge_new (s->edge_class, stl->pdata[3*i + 1], stl->pdata[3*i + 2]); if (e3 == NULL && stl->pdata[3*i + 2] != stl->pdata[3*i]) e3 = gts_edge_new (s->edge_class, stl->pdata[3*i + 2], stl->pdata[3*i]); if (e1 != NULL && e2 != NULL && e3 != NULL) gts_surface_add_face (s, gts_face_new (s->face_class, e1, e2, e3)); else fprintf (stderr, "stl2gts: warning: ignoring degenerate facet!"); } } int main (int argc, char * argv[]) { int c = 0; gboolean verbose = FALSE; gboolean nomerge = FALSE; gboolean revert = FALSE; GPtrArray * stl; GtsSurface * s; if (!setlocale (LC_ALL, "POSIX")) g_warning ("cannot set locale to POSIX"); while (c != EOF) { #ifdef HAVE_GETOPT_LONG static struct option long_options[] = { {"revert", no_argument, NULL, 'r'}, {"nomerge", no_argument, NULL, 'n'}, {"help", no_argument, NULL, 'h'}, {"verbose", no_argument, NULL, 'v'}, { NULL } }; int option_index = 0; switch ((c = getopt_long (argc, argv, "hvnr", long_options, &option_index))) { #else /* not HAVE_GETOPT_LONG */ switch ((c = getopt (argc, argv, "hvnr"))) { #endif /* not HAVE_GETOPT_LONG */ case 'r': /* revert */ revert = TRUE; break; case 'n': /* nomerge */ nomerge = TRUE; break; case 'h': /* help */ fprintf (stderr, "Usage: stl2gts [OPTION]... < input.stl > output.gts\n" "Convert an STL file to GTS format.\n" "\n" " -r, --revert revert face normals\n" " -n, --nomerge do not merge vertices\n" " -v, --verbose display surface statistics\n" " -h, --help display this help and exit\n" "\n" "Report bugs to %s\n", GTS_MAINTAINER); return 0; break; case 'v': verbose = TRUE; break; case '?': /* wrong options */ fprintf (stderr, "Try `stl2gts --help' for more information.\n"); return 1; } } stl = stl_read (stdin); if (!nomerge) vertices_merge (stl, 0.); s = gts_surface_new (gts_surface_class (), gts_face_class (), gts_edge_class (), gts_vertex_class ()); add_stl (s, stl); if (revert) gts_surface_foreach_face (s, (GtsFunc) gts_triangle_revert, NULL); if (verbose) gts_surface_print_stats (s, stderr); gts_surface_write (s, stdout); return 0; }