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 <math.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
31 #ifndef PI
32 #define PI 3.14159265359
33 #endif
34
35 typedef enum { NUMBER, COST } StopOptions;
36 typedef enum { COST_LENGTH, COST_OPTIMIZED, COST_ANGLE } CostOptions;
37 typedef enum { MIDVERTEX, OPTIMIZED } MidvertexOptions;
38
stop_number_verbose(gdouble cost,guint number,guint * min)39 static gboolean stop_number_verbose (gdouble cost, guint number, guint * min)
40 {
41 static guint nmax = 0, nold = 0;
42 static GTimer * timer = NULL, * total_timer = NULL;
43
44 g_return_val_if_fail (min != NULL, TRUE);
45
46 if (timer == NULL) {
47 nmax = nold = number;
48 timer = g_timer_new ();
49 total_timer = g_timer_new ();
50 g_timer_start (total_timer);
51 }
52
53 if (number != nold && number % 121 == 0 &&
54 number < nmax && nmax > *min) {
55 gdouble total_elapsed = g_timer_elapsed (total_timer, NULL);
56 gdouble remaining;
57 gdouble hours, mins, secs;
58 gdouble hours1, mins1, secs1;
59
60 g_timer_stop (timer);
61
62 hours = floor (total_elapsed/3600.);
63 mins = floor ((total_elapsed - 3600.*hours)/60.);
64 secs = floor (total_elapsed - 3600.*hours - 60.*mins);
65
66 remaining = total_elapsed*((nmax - *min)/(gdouble) (nmax - number) - 1.);
67 hours1 = floor (remaining/3600.);
68 mins1 = floor ((remaining - 3600.*hours1)/60.);
69 secs1 = floor (remaining - 3600.*hours1 - 60.*mins1);
70
71 fprintf (stderr,
72 "\rEdges: %10u %3.0f%% %6.0f edges/s "
73 "Elapsed: %02.0f:%02.0f:%02.0f "
74 "Remaining: %02.0f:%02.0f:%02.0f ",
75 number,
76 100.*(nmax - number)/(nmax - *min),
77 (nold - number)/g_timer_elapsed (timer, NULL),
78 hours, mins, secs,
79 hours1, mins1, secs1);
80 fflush (stderr);
81
82 nold = number;
83 g_timer_start (timer);
84 }
85 if (number < *min) {
86 g_timer_destroy (timer);
87 g_timer_destroy (total_timer);
88 return TRUE;
89 }
90 return FALSE;
91 }
92
stop_cost_verbose(gdouble cost,guint number,gdouble * max)93 static gboolean stop_cost_verbose (gdouble cost, guint number, gdouble * max)
94 {
95 g_return_val_if_fail (max != NULL, TRUE);
96
97 if (number % 121 == 0) {
98 fprintf (stderr, "\rEdges: %10u Cost: %10g ", number, cost);
99 fflush (stderr);
100 }
101 if (cost > *max)
102 return TRUE;
103 return FALSE;
104 }
105
stop_log_cost(gdouble cost,guint number)106 static gboolean stop_log_cost (gdouble cost, guint number)
107 {
108 fprintf (stderr, "%d %g\n", number, cost);
109 return FALSE;
110 }
111
cost_angle(GtsEdge * e)112 static gdouble cost_angle (GtsEdge * e)
113 {
114 if (e->triangles && e->triangles->next)
115 return fabs (gts_triangles_angle (e->triangles->data,
116 e->triangles->next->data));
117 return G_MAXDOUBLE;
118 }
119
120 /* coarsen - produce a coarsened version of the input */
main(int argc,char * argv[])121 int main (int argc, char * argv[])
122 {
123 GtsSurface * s;
124 GtsPSurface * ps = NULL;
125 gboolean verbose = FALSE;
126 gboolean progressive = FALSE;
127 gboolean log_cost = FALSE;
128 guint number = 0;
129 gdouble cmax = 0.0;
130 StopOptions stop = NUMBER;
131 CostOptions cost = COST_OPTIMIZED;
132 MidvertexOptions mid = OPTIMIZED;
133 GtsKeyFunc cost_func = NULL;
134 GtsCoarsenFunc coarsen_func = NULL;
135 GtsStopFunc stop_func = NULL;
136 gpointer stop_data = NULL;
137 int c = 0;
138 GtsFile * fp;
139 gdouble fold = PI/180.;
140 GtsVolumeOptimizedParams params = { 0.5, 0.5, 0. };
141 gpointer coarsen_data = NULL, cost_data = NULL;
142
143 /* parse options using getopt */
144 while (c != EOF) {
145 #ifdef HAVE_GETOPT_LONG
146 static struct option long_options[] = {
147 {"angle", no_argument, NULL, 'a'},
148 {"progressive", no_argument, NULL, 'p'},
149 {"help", no_argument, NULL, 'h'},
150 {"verbose", no_argument, NULL, 'v'},
151 {"number", required_argument, NULL, 'n'},
152 {"length", no_argument, NULL, 'l'},
153 {"midvertex", no_argument, NULL, 'm'},
154 {"cost", required_argument, NULL, 'c'},
155 {"fold", required_argument, NULL, 'f'},
156 {"vweight", required_argument, NULL, 'w'},
157 {"bweight", required_argument, NULL, 'b'},
158 {"sweight", required_argument, NULL, 's'},
159 {"log", no_argument, NULL, 'L'}
160 };
161 int option_index = 0;
162 switch ((c = getopt_long (argc, argv, "hvmc:n:lpf:w:b:s:La",
163 long_options, &option_index))) {
164 #else /* not HAVE_GETOPT_LONG */
165 switch ((c = getopt (argc, argv, "hvmc:n:lpf:w:b:s:La"))) {
166 #endif /* not HAVE_GETOPT_LONG */
167 case 'a': /* angle */
168 cost = COST_ANGLE;
169 break;
170 case 'L': /* log */
171 log_cost = TRUE;
172 break;
173 case 'p': /* write progressive surface */
174 progressive = TRUE;
175 break;
176 case 'n': /* stop by number */
177 stop = NUMBER;
178 number = atoi (optarg);
179 break;
180 case 'c': /* stop by cost */
181 stop = COST;
182 cmax = atof (optarg);
183 break;
184 case 'v': /* verbose */
185 verbose = TRUE;
186 break;
187 case 'm': /* midvertex */
188 mid = MIDVERTEX;
189 break;
190 case 'l': /* cost is length */
191 cost = COST_LENGTH;
192 break;
193 case 'f': /* fold angle */
194 fold = atof (optarg)*PI/180.;
195 break;
196 case 'w': /* volume optimized weight */
197 params.volume_weight = atof (optarg);
198 break;
199 case 'b': /* boundary optimized weight */
200 params.boundary_weight = atof (optarg);
201 break;
202 case 's': /* shape optimized weight */
203 params.shape_weight = atof (optarg);
204 break;
205 case 'h': /* help */
206 fprintf (stderr,
207 "Usage: coarsen [OPTION] < file.gts\n"
208 "Construct a coarsened version of the input.\n"
209 "\n"
210 " -n N, --number=N stop the coarsening process if the number of\n"
211 " edges was to fall below N\n"
212 " -c C, --cost=C stop the coarsening process if the cost of collapsing\n"
213 " an edge is larger than C\n"
214 " -m --midvertex use midvertex as replacement vertex\n"
215 " default is volume optimized point\n"
216 " -l --length use length^2 as cost function\n"
217 " default is optimized point cost\n"
218 " -f F, --fold=F set maximum fold angle to F degrees\n"
219 " default is one degree\n"
220 " -w W, --vweight=W set weight used for volume optimization\n"
221 " default is 0.5\n"
222 " -b W, --bweight=W set weight used for boundary optimization\n"
223 " default is 0.5\n"
224 " -s W, --sweight=W set weight used for shape optimization\n"
225 " default is 0.0\n"
226 " -p --progressive write progressive surface file\n"
227 " -L --log logs the evolution of the cost\n"
228 " -v --verbose print statistics about the surface\n"
229 " -h --help display this help and exit\n"
230 "\n"
231 "Reports bugs to %s\n",
232 GTS_MAINTAINER);
233 return 0; /* success */
234 break;
235 case '?': /* wrong options */
236 fprintf (stderr, "Try `coarsen --help' for more information.\n");
237 return 1; /* failure */
238 }
239 }
240
241 /* read surface in */
242 s = gts_surface_new (gts_surface_class (),
243 gts_face_class (),
244 gts_edge_class (),
245 gts_vertex_class ());
246 fp = gts_file_new (stdin);
247 if (gts_surface_read (s, fp)) {
248 fputs ("coarsen: the file on standard input is not a valid GTS file\n",
249 stderr);
250 fprintf (stderr, "stdin:%d:%d: %s\n", fp->line, fp->pos, fp->error);
251 return 1; /* failure */
252 }
253
254 /* if verbose on print stats */
255 if (verbose) {
256 gts_surface_print_stats (s, stderr);
257 fprintf (stderr, "# volume: %g area: %g\n",
258 gts_surface_volume (s), gts_surface_area (s));
259 }
260
261 /* select the right coarsening process */
262 switch (cost) {
263 case COST_OPTIMIZED:
264 cost_func = (GtsKeyFunc) gts_volume_optimized_cost;
265 cost_data = ¶ms;
266 break;
267 case COST_LENGTH:
268 cost_func = NULL; break;
269 case COST_ANGLE:
270 cost_func = (GtsKeyFunc) cost_angle; break;
271 default:
272 g_assert_not_reached ();
273 }
274 switch (mid) {
275 case MIDVERTEX:
276 coarsen_func = NULL;
277 break;
278 case OPTIMIZED:
279 coarsen_func = (GtsCoarsenFunc) gts_volume_optimized_vertex;
280 coarsen_data = ¶ms;
281 break;
282 default:
283 g_assert_not_reached ();
284 }
285 if (log_cost)
286 stop_func = (GtsStopFunc) stop_log_cost;
287 else {
288 switch (stop) {
289 case NUMBER:
290 if (verbose)
291 stop_func = (GtsStopFunc) stop_number_verbose;
292 else
293 stop_func = (GtsStopFunc) gts_coarsen_stop_number;
294 stop_data = &number;
295 break;
296 case COST:
297 if (verbose)
298 stop_func = (GtsStopFunc) stop_cost_verbose;
299 else
300 stop_func = (GtsStopFunc) gts_coarsen_stop_cost;
301 stop_data = &cmax;
302 break;
303 default:
304 g_assert_not_reached ();
305 }
306 }
307 if (progressive)
308 ps = gts_psurface_new (gts_psurface_class (),
309 s, gts_split_class (),
310 cost_func, cost_data,
311 coarsen_func, coarsen_data,
312 stop_func, stop_data,
313 fold);
314 else
315 gts_surface_coarsen (s,
316 cost_func, cost_data,
317 coarsen_func, coarsen_data,
318 stop_func, stop_data, fold);
319
320 /* if verbose on print stats */
321 if (verbose) {
322 fputc ('\n', stderr);
323 gts_surface_print_stats (s, stderr);
324 fprintf (stderr, "# volume: %g area: %g\n",
325 gts_surface_volume (s), gts_surface_area (s));
326 }
327
328 /* write resulting surface to standard output */
329 if (progressive)
330 gts_psurface_write (ps, stdout);
331 else
332 gts_surface_write (s, stdout);
333
334 return 0; /* success */
335 }
336