/* 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 <stdlib.h>
#include "config.h"
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif /* HAVE_GETOPT_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include "gts.h"
typedef struct _CartesianGrid CartesianGrid;
struct _CartesianGrid {
GtsVertex *** vertices;
guint nx, ny;
gdouble xmin, xmax, ymin, ymax;
};
static void ** malloc2D (guint nx, guint ny, gulong size)
{
void ** m = g_malloc (nx*sizeof (void *));
guint i;
for (i = 0; i < nx; i++)
m[i] = g_malloc0 (ny*size);
return m;
}
static void free2D (void ** m, guint nx)
{
guint i;
g_return_if_fail (m != NULL);
for (i = 0; i < nx; i++)
g_free (m[i]);
g_free (m);
}
static CartesianGrid * cartesian_grid_new (guint nx, guint ny)
{
CartesianGrid * grid;
grid = g_malloc (sizeof (CartesianGrid));
grid->vertices = (GtsVertex ***) malloc2D (nx, ny, sizeof (GtsVertex *));
grid->nx = nx;
grid->ny = ny;
grid->xmin = G_MAXDOUBLE;
grid->xmax = - G_MAXDOUBLE;
grid->ymin = G_MAXDOUBLE;
grid->ymax = - G_MAXDOUBLE;
return grid;
}
static void cartesian_grid_destroy (CartesianGrid * g,
gboolean destroy_vertices)
{
g_return_if_fail (g != NULL);
if (destroy_vertices) {
guint i, j;
gts_allow_floating_vertices = TRUE;
for (i = 0; i < g->nx; i++)
for (j = 0; j < g->ny; j++)
if (g->vertices[i][j])
gts_object_destroy (GTS_OBJECT (g->vertices[i][j]));
gts_allow_floating_vertices = FALSE;
}
free2D ((void **) g->vertices, g->nx);
g_free (g);
}
static CartesianGrid * cartesian_grid_read (GtsVertexClass * klass,
guint * line)
{
CartesianGrid * grid;
guint nx, ny, line_number = 1, i, j;
g_return_val_if_fail (klass != NULL, NULL);
if (scanf ("%u %u", &nx, &ny) != 2) {
if (line)
*line = line_number;
return NULL;
}
line_number++;
grid = cartesian_grid_new (nx, ny);
for (i = 0; i < nx; i++)
for (j = 0; j < ny; j++) {
gdouble x, y, z;
if (scanf ("%lf %lf %lf", &x, &y, &z) != 3) {
cartesian_grid_destroy (grid, TRUE);
if (line)
*line = line_number;
return NULL;
}
if (z != 0.)
grid->vertices[i][j] = gts_vertex_new (klass, x, y, z);
if (x > grid->xmax) grid->xmax = x;
if (x < grid->xmin) grid->xmin = x;
if (y > grid->ymax) grid->ymax = y;
if (y < grid->ymin) grid->ymin = y;
line_number++;
}
return grid;
}
static GtsEdge * new_edge (GtsVertex * v1, GtsVertex * v2)
{
GtsSegment * s = gts_vertices_are_connected (v1, v2);
return s == NULL ?
gts_edge_new (GTS_EDGE_CLASS (gts_constraint_class ()), v1, v2) :
GTS_EDGE (s);
}
static void cartesian_grid_triangulate (CartesianGrid * g,
GtsSurface * s)
{
gint i, j;
GtsVertex *** v;
g_return_if_fail (g != NULL);
g_return_if_fail (s != NULL);
v = g->vertices;
for (i = 0; i < g->nx - 1; i++)
for (j = 0; j < g->ny - 1; j++)
if (v[i][j]) {
if (v[i][j+1]) {
if (v[i+1][j+1]) {
GtsEdge * e1 = new_edge (v[i][j+1], v[i][j]);
GtsEdge * e2 =
gts_edge_new (GTS_EDGE_CLASS (gts_constraint_class ()),
v[i][j], v[i+1][j+1]);
GtsEdge * e3 = new_edge (v[i+1][j+1], v[i][j+1]);
gts_surface_add_face (s, gts_face_new (s->face_class, e1, e2, e3));
if (v[i+1][j]) {
e1 = new_edge (v[i+1][j], v[i+1][j+1]);
e3 = new_edge (v[i][j], v[i+1][j]);
gts_surface_add_face (s,
gts_face_new (s->face_class, e1, e2, e3));
}
}
else if (v[i+1][j]) {
GtsEdge * e1 = new_edge (v[i][j+1], v[i][j]);
GtsEdge * e2 = new_edge (v[i][j], v[i+1][j]);
GtsEdge * e3 =
gts_edge_new (GTS_EDGE_CLASS (gts_constraint_class ()),
v[i+1][j], v[i][j+1]);
gts_surface_add_face (s, gts_face_new (s->face_class, e1, e2, e3));
}
}
else if (v[i+1][j] && v[i+1][j+1]) {
GtsEdge * e1 = new_edge (v[i][j], v[i+1][j]);
GtsEdge * e2 = new_edge (v[i+1][j], v[i+1][j+1]);
GtsEdge * e3 =
gts_edge_new (GTS_EDGE_CLASS (gts_constraint_class ()),
v[i+1][j+1], v[i][j]);
gts_surface_add_face (s, gts_face_new (s->face_class, e1, e2, e3));
}
}
else if (v[i][j+1] && v[i+1][j+1] && v[i+1][j]) {
GtsEdge * e1 = new_edge (v[i+1][j], v[i+1][j+1]);
GtsEdge * e2 = new_edge (v[i+1][j+1], v[i][j+1]);
GtsEdge * e3 =
gts_edge_new (GTS_EDGE_CLASS (gts_constraint_class ()),
v[i][j+1], v[i+1][j]);
gts_surface_add_face (s, gts_face_new (s->face_class, e1, e2, e3));
}
}
static gboolean triangle_is_hole (GtsTriangle * t)
{
GtsEdge * e1, * e2, * e3;
GtsVertex * v1, * v2, * v3;
gts_triangle_vertices_edges (t, NULL, &v1, &v2, &v3, &e1, &e2, &e3);
if ((GTS_IS_CONSTRAINT (e1) && GTS_SEGMENT (e1)->v2 != v1) ||
(GTS_IS_CONSTRAINT (e2) && GTS_SEGMENT (e2)->v2 != v2) ||
(GTS_IS_CONSTRAINT (e3) && GTS_SEGMENT (e3)->v2 != v3))
return TRUE;
return FALSE;
}
static void mark_as_hole (GtsFace * f, GtsSurface * s)
{
GtsEdge * e1, * e2, * e3;
if (GTS_OBJECT (f)->reserved == f)
return;
GTS_OBJECT (f)->reserved = f;
e1 = GTS_TRIANGLE (f)->e1;
e2 = GTS_TRIANGLE (f)->e2;
e3 = GTS_TRIANGLE (f)->e3;
if (!GTS_IS_CONSTRAINT (e1)) {
GSList * i = e1->triangles;
while (i) {
GtsFace * f1 = i->data;
if (f1 != f && GTS_IS_FACE (f1) && gts_face_has_parent_surface (f1, s))
mark_as_hole (f1, s);
i = i->next;
}
}
if (!GTS_IS_CONSTRAINT (e2)) {
GSList * i = e2->triangles;
while (i) {
GtsFace * f1 = i->data;
if (f1 != f && GTS_IS_FACE (f1) && gts_face_has_parent_surface (f1, s))
mark_as_hole (f1, s);
i = i->next;
}
}
if (!GTS_IS_CONSTRAINT (e3)) {
GSList * i = e3->triangles;
while (i) {
GtsFace * f1 = i->data;
if (f1 != f && GTS_IS_FACE (f1) && gts_face_has_parent_surface (f1, s))
mark_as_hole (f1, s);
i = i->next;
}
}
}
static void edge_mark_as_hole (GtsEdge * e, GtsSurface * s)
{
GSList * i = e->triangles;
while (i) {
GtsFace * f = i->data;
if (GTS_IS_FACE (f) &&
gts_face_has_parent_surface (f, s) &&
triangle_is_hole (GTS_TRIANGLE (f)))
mark_as_hole (f, s);
i = i->next;
}
}
static gboolean face_is_marked (GtsObject * o)
{
if (o->reserved == o || gts_triangle_is_duplicate (GTS_TRIANGLE (o)))
return TRUE;
return FALSE;
}
static void build_constraint_list (GtsEdge * e, gpointer * data)
{
GtsSurface * s = data[0];
GSList ** constraints = data[1];
if (gts_edge_is_boundary (e, s))
*constraints = g_slist_prepend (*constraints, e);
}
static GtsSurface * cartesian_grid_triangulate_holes (CartesianGrid * grid,
GtsSurface * s)
{
GtsVertex * v1, * v2, * v3, * v4;
GtsEdge * e1, * e2, * e3, * e4, * e5;
gdouble w, h;
GtsSurface * box;
GSList * constraints = NULL, * vertices = NULL, * i;
gpointer data[2];
g_return_val_if_fail (grid != NULL, NULL);
g_return_val_if_fail (s != NULL, NULL);
/* build enclosing box */
w = grid->xmax - grid->xmin;
h = grid->ymax - grid->ymin;
v1 = gts_vertex_new (s->vertex_class, grid->xmin - w, grid->ymin - h, 0.);
v2 = gts_vertex_new (s->vertex_class, grid->xmax + w, grid->ymin - h, 0.);
v3 = gts_vertex_new (s->vertex_class, grid->xmax + w, grid->ymax + h, 0.);
v4 = gts_vertex_new (s->vertex_class, grid->xmin - w, grid->ymax + h, 0.);
e1 = gts_edge_new (s->edge_class, v1, v2);
e2 = gts_edge_new (s->edge_class, v2, v3);
e3 = gts_edge_new (s->edge_class, v3, v4);
e4 = gts_edge_new (s->edge_class, v4, v1);
e5 = gts_edge_new (s->edge_class, v1, v3);
box = gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass),
s->face_class,
s->edge_class,
s->vertex_class);
gts_surface_add_face (box, gts_face_new (s->face_class, e1, e2, e5));
gts_surface_add_face (box, gts_face_new (s->face_class, e3, e4, e5));
/* build vertex and constraint list from the boundaries of the input
surface s */
data[0] = s;
data[1] = &constraints;
gts_surface_foreach_edge (s, (GtsFunc) build_constraint_list, data);
vertices = gts_vertices_from_segments (constraints);
/* triangulate holes */
i = vertices;
while (i) {
g_assert (!gts_delaunay_add_vertex (box, i->data, NULL));
i = i->next;
}
g_slist_free (vertices);
i = constraints;
while (i) {
g_assert (!gts_delaunay_add_constraint (box, i->data));
i = i->next;
}
/* destroy corners of the enclosing box */
gts_allow_floating_vertices = TRUE;
gts_object_destroy (GTS_OBJECT (v1));
gts_object_destroy (GTS_OBJECT (v2));
gts_object_destroy (GTS_OBJECT (v3));
gts_object_destroy (GTS_OBJECT (v4));
gts_allow_floating_vertices = FALSE;
/* remove parts of the mesh which are not holes */
i = constraints;
while (i) {
edge_mark_as_hole (i->data, box);
i = i->next;
}
g_slist_free (constraints);
/* remove marked and duplicate faces */
gts_surface_foreach_face_remove (box, (GtsFunc) face_is_marked, NULL);
/* box now contains only the triangulated holes. */
return box;
}
int main (int argc, char * argv[])
{
gboolean verbose = FALSE;
gboolean triangulate_holes = TRUE;
gboolean write_holes = FALSE;
int c = 0;
CartesianGrid * grid;
guint line = 0;
GtsSurface * s;
GTimer * timer;
/* parse options using getopt */
while (c != EOF) {
#ifdef HAVE_GETOPT_LONG
static struct option long_options[] = {
{"holes", no_argument, NULL, 'H'},
{"keep", no_argument, NULL, 'k'},
{"help", no_argument, NULL, 'h'},
{"verbose", no_argument, NULL, 'v'}
};
int option_index = 0;
switch ((c = getopt_long (argc, argv, "hvHk",
long_options, &option_index))) {
#else /* not HAVE_GETOPT_LONG */
switch ((c = getopt (argc, argv, "hvHk"))) {
#endif /* not HAVE_GETOPT_LONG */
case 'H': /* holes */
write_holes = TRUE;
break;
case 'k': /* keep */
triangulate_holes = FALSE;
break;
case 'v': /* verbose */
verbose = TRUE;
break;
case 'h': /* help */
fprintf (stderr,
"Usage: cartesian [OPTION] < FILE\n"
"Triangulates vertices of a regular cartesian mesh\n"
"(possibly containing holes)\n"
"\n"
" -k --keep keep holes\n"
" -H --holes write holes only\n"
" -v --verbose print statistics about the surface\n"
" -h --help display this help and exit\n"
"\n"
"Reports bugs to %s\n",
GTS_MAINTAINER);
return 0; /* success */
break;
case '?': /* wrong options */
fprintf (stderr, "Try `cartesian --help' for more information.\n");
return 1; /* failure */
}
}
grid = cartesian_grid_read (gts_vertex_class (), &line);
if (grid == NULL) {
fprintf (stderr,
"cartesian: file on standard input is not a valid cartesian grid\n"
"error at line %u\n", line);
return 1;
}
timer = g_timer_new ();
g_timer_start (timer);
s = gts_surface_new (gts_surface_class (),
gts_face_class (),
gts_edge_class (),
gts_vertex_class ());
cartesian_grid_triangulate (grid, s);
if (triangulate_holes) {
GtsSurface * holes = cartesian_grid_triangulate_holes (grid, s);
if (write_holes) {
gts_object_destroy (GTS_OBJECT (s));
s = holes;
}
else
gts_surface_merge (s, holes);
}
g_timer_stop (timer);
if (verbose) {
gts_surface_print_stats (s, stderr);
fprintf (stderr, "# Triangulation time: %g s speed: %.0f vertex/s\n",
g_timer_elapsed (timer, NULL),
gts_surface_vertex_number (s)/g_timer_elapsed (timer, NULL));
}
gts_surface_write (s, stdout);
return 0; /* success */
}
syntax highlighted by Code2HTML, v. 0.9.1