1
0
Fork 0
flightgear/Tools/Construct/Triangulate/triangle.cxx
curt ae0d64594e Rewrote the polygon.[ch]xx class to save actual point data, rather than
indices to points.
Then with that in place I added wrapper functions for the libgpc calls so
  I could ensure that I was always deallocating any allocated memory (which
  was a concern before.)
This was all done in order to facilitate sliver detection and elimination
  which is my next order of business.
These changes then were propogated through out the construction tools.
1999-06-06 02:31:34 +00:00

500 lines
15 KiB
C++

// triangle.cxx -- "Triangle" interface class
//
// Written by Curtis Olson, started March 1999.
//
// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program 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
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// $Id$
#include "polygon.hxx"
#include "triangle.hxx"
// Constructor
FGTriangle::FGTriangle( void ) {
}
// Destructor
FGTriangle::~FGTriangle( void ) {
}
// populate this class based on the specified gpc_polys list
int
FGTriangle::build( const point_list& corner_list,
const point_list& fit_list,
const FGPolyList& gpc_polys )
{
int debug_counter = 0;
FGPolygon poly;
int index;
in_nodes.clear();
in_segs.clear();
// Point3D junkp;
// int junkc = 0;
// char junkn[256];
// FILE *junkfp;
// traverse the dem corner and fit lists and gpc_polys building a
// unified node list and converting the polygons so that they
// reference the node list by index (starting at zero) rather than
// listing the points explicitely
// first the corners since these are important
const_point_list_iterator f_current, f_last;
f_current = corner_list.begin();
f_last = corner_list.end();
for ( ; f_current != f_last; ++f_current ) {
index = in_nodes.unique_add( *f_current );
}
// next process the polygons
FGPolygon gpc_poly;
const_poly_list_iterator current, last;
// process polygons in priority order
cout << "prepairing node list and polygons" << endl;
for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) {
polylist[i].clear();
// cout << "area type = " << i << endl;
debug_counter = 0;
current = gpc_polys.polys[i].begin();
last = gpc_polys.polys[i].end();
for ( ; current != last; ++current ) {
gpc_poly = *current;
cout << "processing a polygon, contours = "
<< gpc_poly.contours() << endl;
if (gpc_poly.contours() <= 0 ) {
cout << "FATAL ERROR! no contours in this polygon" << endl;
exit(-1);
}
poly.erase();
int j;
for ( j = 0; j < gpc_poly.contours(); ++j ) {
cout << " processing contour = " << j << ", nodes = "
<< gpc_poly.contour_size( j ) << ", hole = "
<< gpc_poly.get_hole_flag( j ) << endl;
// sprintf(junkn, "g.%d", junkc++);
// junkfp = fopen(junkn, "w");
for ( int k = 0; k < gpc_poly.contour_size( j ); k++ ) {
Point3D p = gpc_poly.get_pt( j, k );
index = in_nodes.unique_add( p );
// junkp = in_nodes.get_node( index );
// fprintf(junkfp, "%.4f %.4f\n", junkp.x(), junkp.y());
poly.add_node(j, p);
// cout << " - " << index << endl;
}
// fprintf(junkfp, "%.4f %.4f\n",
// gpc_poly->contour[j].vertex[0].x,
// gpc_poly->contour[j].vertex[0].y);
// fclose(junkfp);
poly.set_hole_flag( j, gpc_poly.get_hole_flag( j ) );
}
for ( j = 0; j < gpc_poly.contours(); ++j ) {
poly.calc_point_inside( j, in_nodes );
}
#if 0
// temporary ... write out hole/polygon info for debugging
for ( j = 0; j < (int)poly.contours(); ++j ) {
char pname[256];
sprintf(pname, "poly%02d-%02d-%02d", i, debug_counter, j);
FILE *fp = fopen( pname, "w" );
Point3D point;
for ( int k = 0; k < poly.contour_size( j ); ++k ) {
point = poly.get_pt( j, k );
fprintf( fp, "%.6f %.6f\n", point.x(), point.y() );
}
point = poly.get_pt( j, 0 );
fprintf( fp, "%.6f %.6f\n", point.x(), point.y() );
fclose(fp);
char hname[256];
sprintf(hname, "hole%02d-%02d-%02d", i, debug_counter, j);
FILE *fh = fopen( hname, "w" );
point = poly.get_point_inside( j );
fprintf( fh, "%.6f %.6f\n", point.x(), point.y() );
fclose(fh);
}
#endif
polylist[i].push_back( poly );
++debug_counter;
}
}
// last, do the rest of the height nodes
f_current = fit_list.begin();
f_last = fit_list.end();
for ( ; f_current != f_last; ++f_current ) {
index = in_nodes.course_add( *f_current );
}
for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) {
if ( polylist[i].size() ) {
cout << get_area_name((AreaType)i) << " = "
<< polylist[i].size() << endl;
}
}
// traverse the polygon lists and build the segment (edge) list
// that is used by the "Triangle" lib.
int i1, i2;
Point3D p1, p2;
point_list node_list = in_nodes.get_node_list();
for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) {
// cout << "area type = " << i << endl;
poly_list_iterator tp_current, tp_last;
tp_current = polylist[i].begin();
tp_last = polylist[i].end();
// process each polygon in list
for ( ; tp_current != tp_last; ++tp_current ) {
poly = *tp_current;
for ( int j = 0; j < (int)poly.contours(); ++j) {
for ( int k = 0; k < (int)(poly.contour_size(j) - 1); ++k ) {
p1 = poly.get_pt( j, k );
p2 = poly.get_pt( j, k + 1 );
i1 = in_nodes.find( p1 );
i2 = in_nodes.find( p2 );
// calc_line_params(i1, i2, &m, &b);
in_segs.unique_divide_and_add( node_list, FGTriSeg(i1, i2) );
}
p1 = poly.get_pt( j, 0 );
p2 = poly.get_pt( j, poly.contour_size(j) - 1 );
i1 = in_nodes.find( p1 );
i2 = in_nodes.find( p2 );
// calc_line_params(i1, i2, &m, &b);
in_segs.unique_divide_and_add( node_list, FGTriSeg(i1, i2) );
}
}
}
return 0;
}
// populate this class based on the specified gpc_polys list
int FGTriangle::rebuild( FGConstruct& c ) {
in_nodes.clear();
in_segs.clear();
in_nodes = c.get_tri_nodes();
in_segs = c.get_tri_segs();
return 0;
}
static void write_out_data(struct triangulateio *out) {
FILE *node = fopen("tile.node", "w");
fprintf(node, "%d 2 %d 0\n",
out->numberofpoints, out->numberofpointattributes);
for (int i = 0; i < out->numberofpoints; ++i) {
fprintf(node, "%d %.6f %.6f %.2f\n",
i, out->pointlist[2*i], out->pointlist[2*i + 1], 0.0);
}
fclose(node);
FILE *ele = fopen("tile.ele", "w");
fprintf(ele, "%d 3 0\n", out->numberoftriangles);
for (int i = 0; i < out->numberoftriangles; ++i) {
fprintf(ele, "%d ", i);
for (int j = 0; j < out->numberofcorners; ++j) {
fprintf(ele, "%d ", out->trianglelist[i * out->numberofcorners + j]);
}
for (int j = 0; j < out->numberoftriangleattributes; ++j) {
fprintf(ele, "%.6f ",
out->triangleattributelist[i
* out->numberoftriangleattributes
+ j]
);
}
fprintf(ele, "\n");
}
fclose(ele);
FILE *fp = fopen("tile.poly", "w");
fprintf(fp, "0 2 1 0\n");
fprintf(fp, "%d 0\n", out->numberofsegments);
for (int i = 0; i < out->numberofsegments; ++i) {
fprintf(fp, "%d %d %d\n",
i, out->segmentlist[2*i], out->segmentlist[2*i + 1]);
}
fprintf(fp, "%d\n", out->numberofholes);
for (int i = 0; i < out->numberofholes; ++i) {
fprintf(fp, "%d %.6f %.6f\n",
i, out->holelist[2*i], out->holelist[2*i + 1]);
}
fprintf(fp, "%d\n", out->numberofregions);
for (int i = 0; i < out->numberofregions; ++i) {
fprintf(fp, "%d %.6f %.6f %.6f\n",
i, out->regionlist[4*i], out->regionlist[4*i + 1],
out->regionlist[4*i + 2]);
}
fclose(fp);
}
// Front end triangulator for polygon list. Allocates and builds up
// all the needed structures for the triangulator, runs it, copies the
// results, and frees all the data structures used by the
// triangulator. "pass" can be 1 or 2. 1 = first pass which
// generates extra nodes for a better triangulation. 2 = second pass
// after split/reassem where we don't want any extra nodes generated.
int FGTriangle::run_triangulate( const string& angle, const int pass ) {
FGPolygon poly;
Point3D p;
struct triangulateio in, out, vorout;
int counter;
// point list
point_list node_list = in_nodes.get_node_list();
in.numberofpoints = node_list.size();
in.pointlist = (REAL *) malloc(in.numberofpoints * 2 * sizeof(REAL));
point_list_iterator tn_current, tn_last;
tn_current = node_list.begin();
tn_last = node_list.end();
counter = 0;
for ( ; tn_current != tn_last; ++tn_current ) {
in.pointlist[counter++] = tn_current->x();
in.pointlist[counter++] = tn_current->y();
}
in.numberofpointattributes = 1;
in.pointattributelist = (REAL *) malloc(in.numberofpoints *
in.numberofpointattributes *
sizeof(REAL));
for ( int i = 0; i < in.numberofpoints * in.numberofpointattributes; ++i) {
in.pointattributelist[i] = 0.0;
}
in.pointmarkerlist = (int *) malloc(in.numberofpoints * sizeof(int));
for ( int i = 0; i < in.numberofpoints; ++i) {
in.pointmarkerlist[i] = 0;
}
// triangle list
in.numberoftriangles = 0;
// segment list
triseg_list seg_list = in_segs.get_seg_list();
in.numberofsegments = seg_list.size();
in.segmentlist = (int *) malloc(in.numberofsegments * 2 * sizeof(int));
in.segmentmarkerlist = (int *) NULL;
triseg_list_iterator s_current, s_last;
s_current = seg_list.begin();
s_last = seg_list.end();
counter = 0;
for ( ; s_current != s_last; ++s_current ) {
in.segmentlist[counter++] = s_current->get_n1();
in.segmentlist[counter++] = s_current->get_n2();
}
// hole list (make holes for airport ignore areas)
in.numberofholes = polylist[(int)AirportIgnoreArea].size();
in.holelist = (REAL *) malloc(in.numberofholes * 2 * sizeof(REAL));
poly_list_iterator h_current, h_last;
h_current = polylist[(int)AirportIgnoreArea].begin();
h_last = polylist[(int)AirportIgnoreArea].end();
counter = 0;
for ( ; h_current != h_last; ++h_current ) {
poly = *h_current;
for ( int j = 0; j < poly.contours(); ++j ) {
p = poly.get_point_inside( j );
in.holelist[counter++] = p.x();
in.holelist[counter++] = p.y();
}
}
// region list
in.numberofregions = 0;
for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) {
poly_list_iterator h_current, h_last;
h_current = polylist[i].begin();
h_last = polylist[i].end();
for ( ; h_current != h_last; ++h_current ) {
poly = *h_current;
for ( int j = 0; j < poly.contours(); ++j ) {
if ( ! poly.get_hole_flag( j ) ) {
++in.numberofregions;
}
}
}
}
in.regionlist = (REAL *) malloc(in.numberofregions * 4 * sizeof(REAL));
counter = 0;
for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) {
poly_list_iterator h_current, h_last;
h_current = polylist[(int)i].begin();
h_last = polylist[(int)i].end();
for ( ; h_current != h_last; ++h_current ) {
poly = *h_current;
for ( int j = 0; j < poly.contours(); ++j ) {
if ( ! poly.get_hole_flag( j ) ) {
p = poly.get_point_inside( j );
cout << "Region point = " << p << endl;
in.regionlist[counter++] = p.x(); // x coord
in.regionlist[counter++] = p.y(); // y coord
in.regionlist[counter++] = i; // region attribute
in.regionlist[counter++] = -1.0; // area constraint
// (unused)
}
}
}
}
// prep the output structures
out.pointlist = (REAL *) NULL; // Not needed if -N switch used.
// Not needed if -N switch used or number of point attributes is zero:
out.pointattributelist = (REAL *) NULL;
out.pointmarkerlist = (int *) NULL; // Not needed if -N or -B switch used.
out.trianglelist = (int *) NULL; // Not needed if -E switch used.
// Not needed if -E switch used or number of triangle attributes is zero:
out.triangleattributelist = (REAL *) NULL;
out.neighborlist = (int *) NULL; // Needed only if -n switch used.
// Needed only if segments are output (-p or -c) and -P not used:
out.segmentlist = (int *) NULL;
// Needed only if segments are output (-p or -c) and -P and -B not used:
out.segmentmarkerlist = (int *) NULL;
out.edgelist = (int *) NULL; // Needed only if -e switch used.
out.edgemarkerlist = (int *) NULL; // Needed if -e used and -B not used.
vorout.pointlist = (REAL *) NULL; // Needed only if -v switch used.
// Needed only if -v switch used and number of attributes is not zero:
vorout.pointattributelist = (REAL *) NULL;
vorout.edgelist = (int *) NULL; // Needed only if -v switch used.
vorout.normlist = (REAL *) NULL; // Needed only if -v switch used.
// TEMPORARY
write_out_data(&in);
// Triangulate the points. Switches are chosen to read and write
// a PSLG (p), preserve the convex hull (c), number everything
// from zero (z), assign a regional attribute to each element (A),
// and produce an edge list (e), and a triangle neighbor list (n).
string tri_options;
if ( pass == 1 ) {
// use a quality value of 10 (q10) meaning no interior
// triangle angles less than 10 degrees
// tri_options = "pczAen";
if ( angle == "0" ) {
tri_options = "pczAen";
} else {
tri_options = "pczq" + angle + "Aen";
}
// // string tri_options = "pzAen";
// // string tri_options = "pczq15S400Aen";
} else if ( pass == 2 ) {
// no new points on boundary (Y), no internal segment
// splitting (YY), no quality refinement ()
tri_options = "pczYYAen";
} else {
cout << "unknown pass number = " << pass
<< " in FGTriangle::run_triangulate()" << endl;
exit(-1);
}
cout << "Triangulation with options = " << tri_options << endl;
triangulate(tri_options.c_str(), &in, &out, &vorout);
// TEMPORARY
// write_out_data(&out);
// now copy the results back into the corresponding FGTriangle
// structures
// nodes
out_nodes.clear();
for ( int i = 0; i < out.numberofpoints; ++i ) {
Point3D p( out.pointlist[2*i], out.pointlist[2*i + 1], 0.0 );
// cout << "point = " << p << endl;
out_nodes.simple_add( p );
}
// segments
out_segs.clear();
for ( int i = 0; i < out.numberofsegments; ++i ) {
out_segs.unique_add( FGTriSeg( out.segmentlist[2*i],
out.segmentlist[2*i+1] ) );
}
// triangles
elelist.clear();
int n1, n2, n3;
double attribute;
for ( int i = 0; i < out.numberoftriangles; ++i ) {
n1 = out.trianglelist[i * 3];
n2 = out.trianglelist[i * 3 + 1];
n3 = out.trianglelist[i * 3 + 2];
if ( out.numberoftriangleattributes > 0 ) {
attribute = out.triangleattributelist[i];
} else {
attribute = 0.0;
}
// cout << "triangle = " << n1 << " " << n2 << " " << n3 << endl;
elelist.push_back( FGTriEle( n1, n2, n3, attribute ) );
}
// free mem allocated to the "Triangle" structures
free(in.pointlist);
free(in.pointattributelist);
free(in.pointmarkerlist);
free(in.regionlist);
free(out.pointlist);
free(out.pointattributelist);
free(out.pointmarkerlist);
free(out.trianglelist);
free(out.triangleattributelist);
// free(out.trianglearealist);
free(out.neighborlist);
free(out.segmentlist);
free(out.segmentmarkerlist);
free(out.edgelist);
free(out.edgemarkerlist);
free(vorout.pointlist);
free(vorout.pointattributelist);
free(vorout.edgelist);
free(vorout.normlist);
return 0;
}