1 /* GTS - Library for the manipulation of triangulated surfaces
2  * Copyright (C) 1999 St�phane Popinet
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 #include <stdlib.h>
21 #include <string.h>
22 #include "config.h"
23 #ifdef HAVE_GETOPT_H
24 #  include <getopt.h>
25 #endif /* HAVE_GETOPT_H */
26 #ifdef HAVE_UNISTD_H
27 #  include <unistd.h>
28 #endif /* HAVE_UNISTD_H */
29 #include "gts.h"
30 
write_edge(GtsSegment * s,FILE * fp)31 static void write_edge (GtsSegment * s, FILE * fp)
32 {
33   fprintf (fp, "VECT 1 2 0 2 0 %g %g %g %g %g %g\n",
34 	   GTS_POINT (s->v1)->x,
35 	   GTS_POINT (s->v1)->y,
36 	   GTS_POINT (s->v1)->z,
37 	   GTS_POINT (s->v2)->x,
38 	   GTS_POINT (s->v2)->y,
39 	   GTS_POINT (s->v2)->z);
40 }
41 
42 /* set - compute set operations between surfaces */
main(int argc,char * argv[])43 int main (int argc, char * argv[])
44 {
45   GtsSurface * s1, * s2, * s3;
46   GtsSurfaceInter * si;
47   GNode * tree1, * tree2;
48   FILE * fptr;
49   GtsFile * fp;
50   int c = 0;
51   gboolean verbose = TRUE;
52   gboolean inter = FALSE;
53   gboolean check_self_intersection = FALSE;
54   gchar * operation, * file1, * file2;
55   gboolean closed = TRUE, is_open1, is_open2;
56 
57   /* parse options using getopt */
58   while (c != EOF) {
59 #ifdef HAVE_GETOPT_LONG
60     static struct option long_options[] = {
61       {"inter", no_argument, NULL, 'i'},
62       {"self", no_argument, NULL, 's'},
63       {"help", no_argument, NULL, 'h'},
64       {"verbose", no_argument, NULL, 'v'}
65     };
66     int option_index = 0;
67     switch ((c = getopt_long (argc, argv, "hvis",
68 			      long_options, &option_index))) {
69 #else /* not HAVE_GETOPT_LONG */
70     switch ((c = getopt (argc, argv, "hvis"))) {
71 #endif /* not HAVE_GETOPT_LONG */
72     case 's': /* self */
73       check_self_intersection = TRUE;
74       break;
75     case 'i': /* inter */
76       inter = TRUE;
77       break;
78     case 'v': /* verbose */
79       verbose = FALSE;
80       break;
81     case 'h': /* help */
82       fprintf (stderr,
83              "Usage: set [OPTION] OPERATION FILE1 FILE2\n"
84              "Compute set operations between surfaces, where OPERATION is either.\n"
85              "union, inter, diff.\n"
86 	     "\n"
87              "  -i      --inter    output an OOGL (Geomview) representation of the curve\n"
88              "                     intersection of the surfaces\n"
89 	     "  -s      --self     checks that the surfaces are not self-intersecting\n"
90              "                     if one of them is, the set of self-intersecting faces\n"
91 	     "                     is written (as a GtsSurface) on standard output\n"
92 	     "  -v      --verbose  do not print statistics about the surface\n"
93 	     "  -h      --help     display this help and exit\n"
94 	     "\n"
95 	     "Reports bugs to %s\n",
96 	     GTS_MAINTAINER);
97       return 0; /* success */
98       break;
99     case '?': /* wrong options */
100       fprintf (stderr, "Try `set --help' for more information.\n");
101       return 1; /* failure */
102     }
103   }
104 
105   if (optind >= argc) { /* missing OPERATION */
106     fprintf (stderr,
107 	     "set: missing OPERATION\n"
108 	     "Try `set --help' for more information.\n");
109     return 1; /* failure */
110   }
111   operation = argv[optind++];
112 
113   if (optind >= argc) { /* missing FILE1 */
114     fprintf (stderr,
115 	     "set: missing FILE1\n"
116 	     "Try `set --help' for more information.\n");
117     return 1; /* failure */
118   }
119   file1 = argv[optind++];
120 
121   if (optind >= argc) { /* missing FILE2 */
122     fprintf (stderr,
123 	     "set: missing FILE2\n"
124 	     "Try `set --help' for more information.\n");
125     return 1; /* failure */
126   }
127   file2 = argv[optind++];
128 
129   /* open first file */
130   if ((fptr = fopen (file1, "rt")) == NULL) {
131     fprintf (stderr, "set: can not open file `%s'\n", file1);
132     return 1;
133   }
134   /* reads in first surface file */
135   s1 = GTS_SURFACE (gts_object_new (GTS_OBJECT_CLASS (gts_surface_class ())));
136   fp = gts_file_new (fptr);
137   if (gts_surface_read (s1, fp)) {
138     fprintf (stderr, "set: `%s' is not a valid GTS surface file\n",
139 	     file1);
140     fprintf (stderr, "%s:%d:%d: %s\n", file1, fp->line, fp->pos, fp->error);
141     return 1;
142   }
143   gts_file_destroy (fp);
144   fclose (fptr);
145 
146   /* open second file */
147   if ((fptr = fopen (file2, "rt")) == NULL) {
148     fprintf (stderr, "set: can not open file `%s'\n", file2);
149     return 1;
150   }
151   /* reads in second surface file */
152   s2 = GTS_SURFACE (gts_object_new (GTS_OBJECT_CLASS (gts_surface_class ())));
153   fp = gts_file_new (fptr);
154   if (gts_surface_read (s2, fp)) {
155     fprintf (stderr, "set: `%s' is not a valid GTS surface file\n",
156 	     file2);
157     fprintf (stderr, "%s:%d:%d: %s\n", file2, fp->line, fp->pos, fp->error);
158     return 1;
159   }
160   gts_file_destroy (fp);
161   fclose (fptr);
162 
163   /* display summary information about both surfaces */
164   if (verbose) {
165     gts_surface_print_stats (s1, stderr);
166     gts_surface_print_stats (s2, stderr);
167   }
168 
169   /* check that the surfaces are orientable manifolds */
170   if (!gts_surface_is_orientable (s1)) {
171     fprintf (stderr, "set: surface `%s' is not an orientable manifold\n",
172 	     file1);
173     return 1;
174   }
175   if (!gts_surface_is_orientable (s2)) {
176     fprintf (stderr, "set: surface `%s' is not an orientable manifold\n",
177 	     file2);
178     return 1;
179   }
180 
181   /* check that the surfaces are not self-intersecting */
182   if (check_self_intersection) {
183     GtsSurface * self_intersects;
184 
185     self_intersects = gts_surface_is_self_intersecting (s1);
186     if (self_intersects != NULL) {
187       fprintf (stderr, "set: surface `%s' is self-intersecting\n", file1);
188       if (verbose)
189 	gts_surface_print_stats (self_intersects, stderr);
190       gts_surface_write (self_intersects, stdout);
191       gts_object_destroy (GTS_OBJECT (self_intersects));
192       return 1;
193     }
194     self_intersects = gts_surface_is_self_intersecting (s2);
195     if (self_intersects != NULL) {
196       fprintf (stderr, "set: surface `%s' is self-intersecting\n", file2);
197       if (verbose)
198 	gts_surface_print_stats (self_intersects, stderr);
199       gts_surface_write (self_intersects, stdout);
200       gts_object_destroy (GTS_OBJECT (self_intersects));
201       return 1;
202     }
203   }
204 
205   /* build bounding box tree for first surface */
206   tree1 = gts_bb_tree_surface (s1);
207   is_open1 = gts_surface_volume (s1) < 0. ? TRUE : FALSE;
208 
209   /* build bounding box tree for second surface */
210   tree2 = gts_bb_tree_surface (s2);
211   is_open2 = gts_surface_volume (s2) < 0. ? TRUE : FALSE;
212 
213   si = gts_surface_inter_new (gts_surface_inter_class (),
214 			      s1, s2, tree1, tree2, is_open1, is_open2);
215   g_assert (gts_surface_inter_check (si, &closed));
216   if (!closed) {
217     fprintf (stderr,
218 	     "set: the intersection of `%s' and `%s' is not a closed curve\n",
219 	     file1, file2);
220     return 1;
221   }
222 
223   s3 = gts_surface_new (gts_surface_class (),
224 			gts_face_class (),
225 			gts_edge_class (),
226 			gts_vertex_class ());
227   if (!strcmp (operation, "union")) {
228     gts_surface_inter_boolean (si, s3, GTS_1_OUT_2);
229     gts_surface_inter_boolean (si, s3, GTS_2_OUT_1);
230   }
231   else if (!strcmp (operation, "inter")) {
232     gts_surface_inter_boolean (si, s3, GTS_1_IN_2);
233     gts_surface_inter_boolean (si, s3, GTS_2_IN_1);
234   }
235   else if (!strcmp (operation, "diff")) {
236     gts_surface_inter_boolean (si, s3, GTS_1_OUT_2);
237     gts_surface_inter_boolean (si, s3, GTS_2_IN_1);
238     gts_surface_foreach_face (si->s2, (GtsFunc) gts_triangle_revert, NULL);
239     gts_surface_foreach_face (s2, (GtsFunc) gts_triangle_revert, NULL);
240   }
241   else {
242     fprintf (stderr,
243 	     "set: operation `%s' unknown\n"
244 	     "Try `set --help' for more information.\n",
245 	     operation);
246     return 1;
247   }
248 
249   /* check that the resulting surface is not self-intersecting */
250   if (check_self_intersection) {
251     GtsSurface * self_intersects;
252 
253     self_intersects = gts_surface_is_self_intersecting (s3);
254     if (self_intersects != NULL) {
255       fprintf (stderr, "set: the resulting surface is self-intersecting\n");
256       if (verbose)
257 	gts_surface_print_stats (self_intersects, stderr);
258       gts_surface_write (self_intersects, stdout);
259       gts_object_destroy (GTS_OBJECT (self_intersects));
260       return 1;
261     }
262   }
263   /* display summary information about the resulting surface */
264   if (verbose)
265     gts_surface_print_stats (s3, stderr);
266   /* write resulting surface to standard output */
267   if (inter) {
268     printf ("LIST {\n");
269     g_slist_foreach (si->edges, (GFunc) write_edge, stdout);
270     printf ("}\n");
271   }
272   else {
273     GTS_POINT_CLASS (gts_vertex_class ())->binary = TRUE;
274     gts_surface_write (s3, stdout);
275   }
276 
277   /* destroy surfaces */
278   gts_object_destroy (GTS_OBJECT (s1));
279   gts_object_destroy (GTS_OBJECT (s2));
280   gts_object_destroy (GTS_OBJECT (s3));
281   gts_object_destroy (GTS_OBJECT (si));
282 
283   /* destroy bounding box trees (including bounding boxes) */
284   gts_bb_tree_destroy (tree1, TRUE);
285   gts_bb_tree_destroy (tree2, TRUE);
286 
287   return 0;
288 }
289