1
0
Fork 0

A bit of a re-work (again) but looks to be worth it.

Instead of cleaning polys after clipping, to get good triangulation, try to
clean them before clipping, snap the verticies to 1 1cm x 1cm grid, and
merge any resultant slivers back into adjacent polys.
This is very similar to how fgfs-construct worked.

Although the previous methos only failed on 3 airports, the results weren't
very good.  There were lots of cracks, and mmissing triangles, as the polys were
cleaned.

New methos freezes on two airports during base triangulation, and crashes on four.
I think the quality of the generated airports overcomes the slight regression.

I also added some debug switches so I don't have to keep rebuilding from source
to debug crashes.  --dump-rwy=xxx --dump-pvmt=xxx, --dump-feat=xxx and --dum-base=1
can be used to create shapefiles to debug the particular polys before clipping.
Currently, there needs to be a cp_debug directory to place the shapefiles into.
Probably need better debug infrastructure going forward...

NOTE: I turned off runway shoulder generation in this build.  Polygon snapping for runways,
helipads, and shoulders not implemented yet.  This may help on the failing airports.
This is next on my todo list.
This commit is contained in:
Peter Sadrozinski 2012-03-04 09:29:58 -05:00 committed by Christian Schmitt
parent 0788bf0fcb
commit f0a7f39e7b
21 changed files with 529 additions and 435 deletions

View file

@ -85,6 +85,12 @@ Airport::Airport( int c, char* def)
altitude *= SG_FEET_TO_METER;
boundary = NULL;
dbg_rwy_poly = -1;
dbg_pvmt_poly = -1;
dbg_feat_poly = -1;
dbg_base_poly = -1;
SG_LOG( SG_GENERAL, SG_DEBUG, "Created airport with icao " << icao << ", control tower " << ct << ", and description " << description );
}
@ -141,6 +147,15 @@ Airport::~Airport()
}
}
void Airport::SetDebugPolys( int rwy, int pvmt, int feat, int base )
{
dbg_rwy_poly = rwy;
dbg_pvmt_poly = pvmt;
dbg_feat_poly = feat;
dbg_base_poly = base;
}
// TODO: Add to runway object
// calculate texture coordinates for runway section using the provided
// texturing parameters. Returns a mirror polygon to the runway,
@ -362,12 +377,71 @@ static TGPolygon calc_elevations( TGAptSurface &surf,
return result;
}
void Airport::merge_slivers( superpoly_list& polys, poly_list& slivers_list ) {
TGPolygon poly, result, slivers, sliver;
point_list contour;
int original_contours, result_contours;
bool done;
int i, j, k;
for ( i = 0; i < (int)slivers_list.size(); i++ ) {
slivers = slivers_list[i];
for ( j = 0; j < slivers.contours(); ++j ) {
SG_LOG(SG_GENERAL, SG_DEBUG, "Merging sliver = " << i << ", " << j);
// make the sliver polygon
contour = slivers.get_contour( j );
sliver.erase();
sliver.add_contour( contour, 0 );
done = false;
// try to merge the slivers with the list of clipped polys
for ( k = 0; k < (int)polys.size() && !done; ++k ) {
poly = polys[k].get_poly();
original_contours = poly.contours();
result = tgPolygonUnionClipper( poly, sliver );
result_contours = result.contours();
if ( original_contours == result_contours ) {
SG_LOG(SG_GENERAL, SG_DEBUG, " FOUND a poly to merge the sliver with");
polys[k].set_poly( result );
done = true;
// poly.write("orig");
// sliver.write("sliver");
// result.write("result");
// cout << "press return: ";
// string input;
// cin >> input;
} else {
// cout << " poly not a match" << endl;
// cout << " original = " << original_contours
// << " result = " << result_contours << endl;
// cout << " sliver = " << endl;
// for ( k = 0; k < (int)contour.size(); ++k ) {
// cout << " " << contour[k].x() << ", "
// << contour[k].y() << endl;
}
}
if ( !done ) {
SG_LOG(SG_GENERAL, SG_DEBUG, "couldn't merge sliver " << i << ", " << j);
}
}
}
}
void Airport::BuildBtg(const string& root, const string_list& elev_src )
{
ClipPolyType accum;
poly_list slivers;
// try to keep line accumulator in clipper format for speed...
ClipPolyType line_accum;
poly_list line_slivers;
TGPolygon apt_base;
TGPolygon apt_clearing;
@ -388,6 +462,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
Point3D p;
bool verbose_triangulation = false;
bool make_shapefiles = false;
// parse main airport information
double apt_lon = 0.0, apt_lat = 0.0;
@ -432,14 +507,6 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
}
}
#if 0
if (icao == "SSOK")
{
SG_LOG(SG_GENERAL, SG_ALERT, "Injecting error at icao " << icao );
exit(-1);
}
#endif
// Starting to clip the polys
gettimeofday(&build_start, NULL);
@ -450,16 +517,15 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
{
SG_LOG(SG_GENERAL, SG_INFO, "Build Feature Poly " << i + 1 << " of " << features.size() << " : " << features[i]->GetDescription() );
#if 0
if ( i == 22 ) {
features[i]->BuildBtg( altitude, &line_polys, &line_tps, &line_accum, &rwy_lights, true );
} else {
//features[i]->BuildBtg( altitude, &line_polys, &line_tps, &line_accum, &rwy_lights, false );
}
#else
features[i]->BuildBtg( altitude, &line_polys, &line_tps, &line_accum, &rwy_lights, false );
#endif
if ( (dbg_feat_poly >= 0) && (i == (unsigned int)dbg_feat_poly) ) {
SG_LOG(SG_GENERAL, SG_INFO, "Problem feat poly (" << i << ")");
make_shapefiles = true;
} else {
make_shapefiles = false;
}
features[i]->BuildBtg( altitude, &line_polys, &line_tps, &line_accum, &rwy_lights, make_shapefiles );
}
}
else
@ -474,18 +540,22 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
for ( unsigned int i=0; i<runways.size(); i++ )
{
SG_LOG(SG_GENERAL, SG_INFO, "Build Runway " << i + 1 << " of " << runways.size());
slivers.clear();
if ( runways[i]->IsPrecision() )
{
if (boundary)
{
runways[i]->BuildBtg( altitude, &rwy_polys, &rwy_tps, &rwy_lights, &accum, NULL, NULL );
runways[i]->BuildBtg( altitude, &rwy_polys, &rwy_tps, &rwy_lights, &accum, slivers, NULL, NULL );
}
else
{
runways[i]->BuildBtg( altitude, &rwy_polys, &rwy_tps, &rwy_lights, &accum, &apt_base, &apt_clearing );
runways[i]->BuildBtg( altitude, &rwy_polys, &rwy_tps, &rwy_lights, &accum, slivers, &apt_base, &apt_clearing );
}
}
// Now try to merge any slivers we found
merge_slivers( rwy_polys, slivers );
}
gettimeofday(&log_time, NULL);
@ -506,14 +576,19 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
for ( unsigned int i=0; i<helipads.size(); i++ )
{
SG_LOG(SG_GENERAL, SG_INFO, "Build helipad " << i + 1 << " of " << helipads.size());
slivers.clear();
if (boundary)
{
helipads[i]->BuildBtg( altitude, &rwy_polys, &rwy_tps, &rwy_lights, &accum, NULL, NULL );
helipads[i]->BuildBtg( altitude, &rwy_polys, &rwy_tps, &rwy_lights, &accum, slivers, NULL, NULL );
}
else
{
helipads[i]->BuildBtg( altitude, &rwy_polys, &rwy_tps, &rwy_lights, &accum, &apt_base, &apt_clearing );
helipads[i]->BuildBtg( altitude, &rwy_polys, &rwy_tps, &rwy_lights, &accum, slivers, &apt_base, &apt_clearing );
}
// Now try to merge any slivers we found
merge_slivers( rwy_polys, slivers );
}
}
@ -523,23 +598,28 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
for ( unsigned int i=0; i<pavements.size(); i++ )
{
SG_LOG(SG_GENERAL, SG_INFO, "Build Pavement " << i + 1 << " of " << pavements.size() << " : " << pavements[i]->GetDescription());
slivers.clear();
#if 0
if (i == 30) {
sglog().setLogLevels( SG_GENERAL, SG_BULK );
if ( (dbg_pvmt_poly >= 0) && (i == (unsigned int)dbg_pvmt_poly) ) {
SG_LOG(SG_GENERAL, SG_INFO, "Problem pvmt poly (" << i << ")");
make_shapefiles = true;
} else {
sglog().setLogLevels( SG_GENERAL, SG_INFO );
make_shapefiles = false;
}
#endif
if (boundary)
{
pavements[i]->BuildBtg( altitude, &pvmt_polys, &pvmt_tps, &accum, NULL, NULL );
pavements[i]->BuildBtg( altitude, &pvmt_polys, &pvmt_tps, &accum, slivers, NULL, NULL, make_shapefiles );
}
else
{
pavements[i]->BuildBtg( altitude, &pvmt_polys, &pvmt_tps, &accum, &apt_base, &apt_clearing );
pavements[i]->BuildBtg( altitude, &pvmt_polys, &pvmt_tps, &accum, slivers, &apt_base, &apt_clearing, make_shapefiles );
}
// Now try to merge any slivers we found
merge_slivers( rwy_polys, slivers );
merge_slivers( pvmt_polys, slivers );
}
}
else
@ -550,15 +630,20 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
gettimeofday(&log_time, NULL);
SG_LOG( SG_GENERAL, SG_ALERT, "Finished building pavements for " << icao << " at " << ctime(&log_time.tv_sec) );
#if 0
// Build runway shoulders here
#if 0
for ( unsigned int i=0; i<runways.size(); i++ )
{
SG_LOG(SG_GENERAL, SG_INFO, "Build Runway shoulder " << i << " of " << runways.size());
SG_LOG(SG_GENERAL, SG_INFO, "Build Runway shoulder " << i + 1 << " of " << runways.size());
if ( runways[i]->GetsShoulder() )
{
runways[i]->BuildShoulder( altitude, &rwy_polys, &rwy_tps, &accum, &apt_base, &apt_clearing );
slivers.clear();
runways[i]->BuildShoulder( altitude, &rwy_polys, &rwy_tps, &accum, slivers, &apt_base, &apt_clearing );
// Now try to merge any slivers we found
merge_slivers( rwy_polys, slivers );
merge_slivers( pvmt_polys, slivers );
}
}
#endif
@ -567,7 +652,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
if (boundary)
{
SG_LOG(SG_GENERAL, SG_INFO, "Build user defined boundary " );
boundary->BuildBtg( altitude, &apt_base, &apt_clearing );
boundary->BuildBtg( altitude, &apt_base, &apt_clearing, false );
}
if ( apt_base.total_size() == 0 )
@ -576,9 +661,10 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
return;
}
TGPolygon filled_base = tgPolygonStripHoles( apt_base );
TGPolygon divided_base = tgPolygonSplitLongEdges( filled_base, 200.0 );
TGPolygon base_poly = tgPolygonDiff( divided_base, accum );
TGPolygon base_poly = tgPolygonDiffClipper( divided_base, accum );
gettimeofday(&build_end, NULL);
timersub(&build_end, &build_start, &build_time);
@ -690,6 +776,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
SG_LOG(SG_GENERAL, SG_DEBUG, "total size of section " << k << " before =" << poly.total_size());
#if 0
poly = remove_cycles( poly );
poly = remove_dups( poly );
poly = remove_bad_contours( poly );
@ -698,6 +785,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
poly = remove_dups( poly );
poly = remove_bad_contours( poly );
poly = remove_small_contours( poly );
#endif
rwy_polys[k].set_poly( poly );
}
@ -708,6 +796,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
SG_LOG(SG_GENERAL, SG_DEBUG, "total size of section " << k << " before =" << poly.total_size());
#if 0
poly = remove_cycles( poly );
poly = remove_dups( poly );
poly = remove_bad_contours( poly );
@ -716,6 +805,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
poly = remove_dups( poly );
poly = remove_bad_contours( poly );
poly = remove_small_contours( poly );
#endif
pvmt_polys[k].set_poly( poly );
}
@ -750,6 +840,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
base_poly = add_nodes_to_poly( base_poly, tmp_pvmt_nodes );
SG_LOG(SG_GENERAL, SG_DEBUG, " after adding tmp_nodes: " << base_poly);
#if 0
base_poly = remove_cycles( base_poly );
base_poly = remove_dups( base_poly );
base_poly = remove_bad_contours( base_poly );
@ -759,6 +850,15 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
base_poly = remove_dups( base_poly );
base_poly = remove_bad_contours( base_poly );
base_poly = remove_small_contours( base_poly );
#endif
// Finally find slivers in base
SG_LOG(SG_GENERAL, SG_INFO, "before find slivers in base - contours is : " << base_poly.contours() );
tgPolygonFindSlivers( base_poly, slivers );
SG_LOG(SG_GENERAL, SG_INFO, "after find slivers in base - contours is : " << base_poly.contours() );
merge_slivers( rwy_polys, slivers );
merge_slivers( pvmt_polys, slivers );
gettimeofday(&cleanup_end, NULL);
timersub(&cleanup_end, &cleanup_start, &cleanup_time);
@ -772,23 +872,15 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
TGPolygon poly = rwy_polys[i].get_poly();
#if 0
if ( i == 2 ) {
SG_LOG(SG_GENERAL, SG_INFO, "Problem poly before remove_dups: " << poly );
if ( (dbg_rwy_poly >= 0) && (i == (unsigned int)dbg_rwy_poly) ) {
SG_LOG(SG_GENERAL, SG_INFO, "Problem rwy poly (" << i << ") : " << poly );
sglog().setLogLevels( SG_GENERAL, SG_BULK );
poly = remove_dups( poly );
sglog().setLogLevels( SG_GENERAL, SG_INFO );
SG_LOG(SG_GENERAL, SG_INFO, "Problem poly after remove_dups(): " << poly );
tgChopNormalPolygon( "/home/pete", "Base", poly, false );
tgChopNormalPolygon( "/home/pete", "rwy", poly, false );
verbose_triangulation = true;
} else {
verbose_triangulation = false;
}
#endif
SG_LOG(SG_GENERAL, SG_DEBUG, "contours before " << poly.contours() << " total points before = " << poly.total_size());
TGPolygon tri = polygon_tesselate_alt( poly, verbose_triangulation );
@ -808,16 +900,14 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
TGPolygon poly = pvmt_polys[i].get_poly();
#if 0
if ( i == 0 ) {
SG_LOG(SG_GENERAL, SG_INFO, "Problem poly: " << poly );
if ( (dbg_pvmt_poly >= 0) && (i == (unsigned int)dbg_pvmt_poly) ) {
SG_LOG(SG_GENERAL, SG_INFO, "Problem pvmt poly (" << i << ") : " << poly );
tgChopNormalPolygon( "/home/pete", "Base", poly, false );
tgChopNormalPolygon( "/home/pete", "pvmt", poly, false );
verbose_triangulation = true;
} else {
verbose_triangulation = false;
}
#endif
SG_LOG(SG_GENERAL, SG_DEBUG, "contours before " << poly.contours() << " total points before = " << poly.total_size());
TGPolygon tri = polygon_tesselate_alt( poly, verbose_triangulation );
@ -844,16 +934,14 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
TGPolygon poly = line_polys[i].get_poly();
#if 0
if ( i == 282 ) {
SG_LOG(SG_GENERAL, SG_INFO, "Problem poly: " << poly );
if ( (dbg_feat_poly >= 0) && (i == (unsigned int)dbg_feat_poly) ) {
SG_LOG(SG_GENERAL, SG_INFO, "Problem feat poly (" << i << ") : " << poly );
tgChopNormalPolygon( "/home/pete/", "Base", poly, false );
tgChopNormalPolygon( "/home/pete/", "feat", poly, false );
verbose_triangulation = true;
} else {
verbose_triangulation = false;
}
#endif
SG_LOG(SG_GENERAL, SG_DEBUG, "contours before " << poly.contours() << " total points before = " << poly.total_size());
TGPolygon tri = polygon_tesselate_alt( poly, verbose_triangulation );
@ -866,19 +954,18 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
line_polys[i].set_texcoords( tc );
}
#if 0
{
SG_LOG(SG_GENERAL, SG_INFO, "Problem poly: " << base_poly );
if ( dbg_base_poly >= 0 ) {
SG_LOG(SG_GENERAL, SG_INFO, "Problem base poly: " << base_poly );
tgChopNormalPolygon( "/home/pete/", "Base", base_poly, false );
verbose_triangulation = true;
}
#endif
SG_LOG(SG_GENERAL, SG_INFO, "Tesselating base poly ");
TGPolygon base_tris = polygon_tesselate_alt( base_poly, verbose_triangulation );
SG_LOG(SG_GENERAL, SG_INFO, "Tesselating base poly - done");
verbose_triangulation = false;
SG_LOG(SG_GENERAL, SG_INFO, "Tesselating base poly - done : contours = " << base_tris.contours());
gettimeofday(&triangulation_end, NULL);
timersub(&triangulation_end, &triangulation_start, &triangulation_time);
@ -919,12 +1006,16 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src )
int_list pt_n, tri_n, strip_n;
int_list tri_tc, strip_tc;
SG_LOG(SG_GENERAL, SG_DEBUG, "Calculating airport normal" );
// calculate "the" normal for this airport
p.setx( base_tris.get_pt(0, 0).x() * SGD_DEGREES_TO_RADIANS );
p.sety( base_tris.get_pt(0, 0).y() * SGD_DEGREES_TO_RADIANS );
p.setz( 0 );
Point3D vnt = sgGeodToCart( p );
SG_LOG(SG_GENERAL, SG_DEBUG, "Calculating airport normal - done");
// SG_LOG(SG_GENERAL, SG_DEBUG, "geod = " << p);
// SG_LOG(SG_GENERAL, SG_DEBUG, "cart = " << tmp);

View file

@ -103,8 +103,11 @@ public:
tm = cleanup_time;
}
void merge_slivers( superpoly_list& polys, poly_list& slivers );
void BuildBtg( const string& root, const string_list& elev_src );
void SetDebugPolys( int rwy, int pvmt, int feat, int base );
private:
int code; // airport, heliport or sea port
int altitude; // in meters
@ -126,6 +129,12 @@ private:
struct timeval build_time;
struct timeval cleanup_time;
struct timeval triangulation_time;
// debug
int dbg_rwy_poly;
int dbg_pvmt_poly;
int dbg_feat_poly;
int dbg_base_poly;
};
typedef std::vector <Airport *> AirportList;

View file

@ -218,7 +218,8 @@ void gen_tex_section( const TGPolygon& runway,
const string& material,
superpoly_list *rwy_polys,
texparams_list *texparams,
ClipPolyType *accum ) {
ClipPolyType *accum,
poly_list& slivers ) {
int j, k;
@ -315,7 +316,13 @@ void gen_tex_section( const TGPolygon& runway,
}
// Clip the new polygon against what ever has already been created.
#if 0
TGPolygon clipped = tgPolygonDiff( section, *accum );
#else
TGPolygon clipped = tgPolygonDiffClipper( section, *accum );
#endif
tgPolygonFindSlivers( clipped, slivers );
// Split long edges to create an object that can better flow with
// the surface terrain
@ -329,7 +336,12 @@ void gen_tex_section( const TGPolygon& runway,
sp.set_material( prefix + material );
rwy_polys->push_back( sp );
SG_LOG(SG_GENERAL, SG_DEBUG, "section = " << clipped.contours());
#if 0
*accum = tgPolygonUnion( section, *accum );
#else
*accum = tgPolygonUnionClipper( section, *accum );
#endif
// Store away what we need to know for texture coordinate
// calculation. (CLO 10/20/02: why can't we calculate texture

View file

@ -37,6 +37,7 @@ void gen_tex_section( const TGPolygon& runway,
const string& material,
superpoly_list *rwy_polys,
texparams_list *texparams,
ClipPolyType *accum );
ClipPolyType *accum,
poly_list& slivers );
#endif

View file

@ -4,12 +4,16 @@
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <Polygon/polygon.hxx>
#include <Polygon/chop.hxx>
#include <Geometry/poly_support.hxx>
#include "beznode.hxx"
#include "convex_hull.hxx"
#include "closedpoly.hxx"
#define NO_BEZIER (0)
static void stringPurifier( string& s )
{
for ( string::iterator it = s.begin(), itEnd = s.end(); it!=itEnd; ++it) {
@ -63,8 +67,6 @@ ClosedPoly::ClosedPoly( int st, float s, float th, char* desc )
ClosedPoly::~ClosedPoly()
{
SG_LOG( SG_GENERAL, SG_DEBUG, "Deleting ClosedPoly " << description );
}
void ClosedPoly::AddNode( BezNode* node )
@ -100,31 +102,6 @@ void ClosedPoly::AddNode( BezNode* node )
}
}
#if 0
void ClosedPoly::CreateConvexHull( void )
{
TGPolygon convexHull;
point_list nodes;
Point3D p;
unsigned int i;
if (boundary->size() > 2)
{
for (i=0; i<boundary->size(); i++)
{
p = boundary->at(i)->GetLoc();
nodes.push_back( p );
}
convexHull = convex_hull( nodes );
hull = convexHull.get_contour(0);
}
else
{
SG_LOG(SG_GENERAL, SG_ALERT, "Boundary size too small: " << boundary->size() << ". Ignoring..." );
}
}
#endif
void ClosedPoly::CloseCurContour()
{
SG_LOG(SG_GENERAL, SG_DEBUG, "Close Contour");
@ -232,33 +209,15 @@ void ClosedPoly::ConvertContour( BezContour* src, point_list *dst )
}
}
#if 0
double num_meters = total_dist / meter_dist;
// If total distance is < 4 meters, then we need to modify num Segments so that each segment >= 0.5 meter
if (num_meters < 4.0f)
{
num_segs = ((int)num_meters + 1) * 2;
}
else if (num_meters > 800.0f)
{
num_segs = num_meters / 100.0f + 1;
}
else
{
num_segs = 8;
}
#endif
double num_meters = total_dist / meter_dist;
if (num_meters < 4.0f)
if (num_meters < 8.0f)
{
if (curve_type != CURVE_LINEAR)
{
// If total distance is < 4 meters, then we need to modify num Segments so that each segment >= 1/2 meter
num_segs = ((int)num_meters + 1) * 2;
// If total distance is < 4 meters, then we need to modify num Segments so that each segment >= 2 meters
num_segs = ((int)num_meters + 1);
SG_LOG(SG_GENERAL, SG_DEBUG, "Segment from (" << curNode->GetLoc().x() << "," << curNode->GetLoc().y() << ") to (" << nextNode->GetLoc().x() << "," << nextNode->GetLoc().y() << ")" );
SG_LOG(SG_GENERAL, SG_DEBUG, " Distance is " << num_meters << " ( < 4.0) so num_segs is " << num_segs );
SG_LOG(SG_GENERAL, SG_DEBUG, " Distance is " << num_meters << " ( < 16.0) so num_segs is " << num_segs );
}
else
{
@ -277,18 +236,22 @@ void ClosedPoly::ConvertContour( BezContour* src, point_list *dst )
if (curve_type != CURVE_LINEAR)
{
num_segs = 8;
// num_segs = 16;
SG_LOG(SG_GENERAL, SG_DEBUG, "Segment from (" << curNode->GetLoc().x() << "," << curNode->GetLoc().y() << ") to (" << nextNode->GetLoc().x() << "," << nextNode->GetLoc().y() << ")" );
SG_LOG(SG_GENERAL, SG_DEBUG, " Distance is " << num_meters << " (OK) so num_segs is " << num_segs );
}
else
{
// make sure linear segments don't got over 100m
//num_segs = 1;
num_segs = num_meters / 100.0f + 1;
}
// num_segs = 1;
}
#if NO_BEZIER
num_segs = 1;
#endif
// if only one segment, revert to linear
if (num_segs == 1)
{
@ -315,7 +278,9 @@ void ClosedPoly::ConvertContour( BezContour* src, point_list *dst )
// add the pavement vertex
// convert from lat/lon to geo
// (maybe later) - check some simgear objects...
curLoc.snap();
dst->push_back( curLoc );
if (p==0)
{
SG_LOG(SG_GENERAL, SG_DEBUG, "adding Curve Anchor node (type " << curve_type << ") at (" << curLoc.x() << "," << curLoc.y() << ")");
@ -331,12 +296,6 @@ void ClosedPoly::ConvertContour( BezContour* src, point_list *dst )
}
else
{
// nextLoc = nextNode->GetLoc();
// // just add the one vertex - linear
// dst->push_back( curLoc );
// SG_LOG(SG_GENERAL, SG_DEBUG, "adding Linear Anchor node at (" << curLoc.x() << "," << curLoc.y() << ")");
if (num_segs > 1)
{
for (int p=0; p<num_segs; p++)
@ -345,6 +304,7 @@ void ClosedPoly::ConvertContour( BezContour* src, point_list *dst )
nextLoc = CalculateLinearLocation( curNode->GetLoc(), nextNode->GetLoc(), (1.0f/num_segs) * (p+1) );
// add the feature vertex
curLoc.snap();
dst->push_back( curLoc );
if (p==0)
@ -365,6 +325,7 @@ void ClosedPoly::ConvertContour( BezContour* src, point_list *dst )
nextLoc = nextNode->GetLoc();
// just add the one vertex - dist is small
curLoc.snap();
dst->push_back( curLoc );
SG_LOG(SG_GENERAL, SG_DEBUG, "adding Linear Anchor node at (" << curLoc.x() << "," << curLoc.y() << ")");
@ -375,140 +336,6 @@ void ClosedPoly::ConvertContour( BezContour* src, point_list *dst )
}
}
#if 0
void ExpandPoint( Point3D *prev, Point3D *cur, Point3D *next, double expand_by, double *heading, double *offset )
{
double offset_dir;
double next_dir;
double az2;
double dist;
SG_LOG(SG_GENERAL, SG_DEBUG, "Find average angle for contour: prev (" << *prev << "), "
"cur (" << *cur << "), "
"next (" << *next << ")" );
// first, find if the line turns left or right ar src
// for this, take the cross product of the vectors from prev to src, and src to next.
// if the cross product is negetive, we've turned to the left
// if the cross product is positive, we've turned to the right
// if the cross product is 0, then we need to use the direction passed in
SGVec3d dir1 = prev->toSGVec3d() - cur->toSGVec3d();
dir1 = normalize(dir1);
SGVec3d dir2 = next->toSGVec3d() - cur->toSGVec3d();
dir2 = normalize(dir2);
// Now find the average
SGVec3d avg = dir1 + dir2;
avg = normalize(avg);
// find the offset angle
geo_inverse_wgs_84( avg.y(), avg.x(), 0.0f, 0.0f, &offset_dir, &az2, &dist);
// find the direction to the next point
geo_inverse_wgs_84( cur->y(), cur->x(), next->y(), next->x(), &next_dir, &az2, &dist);
// calculate correct distance for the offset point
*offset = (expand_by)/sin(SGMiscd::deg2rad(offset_dir-next_dir));
*heading = offset_dir;
SG_LOG(SG_GENERAL, SG_DEBUG, "heading is " << *heading << " distance is " << *offset );
}
#endif
#if 0
void ClosedPoly::ExpandContour( point_list& src, TGPolygon& dst, double dist )
{
point_list expanded_boundary;
Point3D prevPoint, curPoint, nextPoint;
double theta;
double expanded_x = 0, expanded_y = 0;
Point3D expanded_point;
double h1;
double o1;
double az2;
unsigned int i;
// iterate through each bezier node in the contour
for (i=0; i<src.size(); i++)
{
SG_LOG(SG_GENERAL, SG_DEBUG, "\nExpanding point " << i << "\n\n");
if (i == 0)
{
// set prev node to last in the contour, as all contours must be closed
prevPoint = src.at( src.size()-1 );
}
else
{
// otherwise, it's just the last index
prevPoint = src.at( i-1 );
}
curPoint = src.at(i);
if (i<src.size()-1)
{
nextPoint = src.at(i+1);
}
else
{
// for the last node, next is the first. as all contours are closed
nextPoint = src.at(0);
}
// calculate the angle between cur->prev and cur->next
theta = SGMiscd::rad2deg(CalculateTheta(prevPoint, curPoint, nextPoint));
if ( theta < 90.0 )
{
SG_LOG(SG_GENERAL, SG_DEBUG, "\nClosed POLY case 1 (theta < 90) " << description << ": theta is " << theta );
// calculate the expanded point heading and offset from current point
ExpandPoint( &prevPoint, &curPoint, &nextPoint, dist, &h1, &o1 );
if (o1 > dist*2.0)
{
SG_LOG(SG_GENERAL, SG_DEBUG, "\ntheta is " << theta << " distance is " << o1 << " CLAMPING to " << dist*2 );
o1 = dist*2;
}
geo_direct_wgs_84( curPoint.y(), curPoint.x(), h1, o1, &expanded_y, &expanded_x, &az2 );
expanded_point = Point3D( expanded_x, expanded_y, 0.0f );
expanded_boundary.push_back( expanded_point );
}
else if ( abs(theta - 180.0) < 0.1 )
{
SG_LOG(SG_GENERAL, SG_DEBUG, "\nClosed POLY case 2 (theta close to 180) " << description << ": theta is " << theta );
// calculate the expanded point heading and offset from current point
ExpandPoint( &prevPoint, &curPoint, &nextPoint, dist, &h1, &o1 );
// straight line blows up math - dist should be exactly as given
o1 = dist;
geo_direct_wgs_84( curPoint.y(), curPoint.x(), h1, o1, &expanded_y, &expanded_x, &az2 );
expanded_point = Point3D( expanded_x, expanded_y, 0.0f );
expanded_boundary.push_back( expanded_point );
}
else
{
SG_LOG(SG_GENERAL, SG_DEBUG, "\nClosed POLY case 3 (fall through) " << description << ": theta is " << theta );
// calculate the expanded point heading and offset from current point
ExpandPoint( &prevPoint, &curPoint, &nextPoint, dist, &h1, &o1 );
geo_direct_wgs_84( curPoint.y(), curPoint.x(), h1, o1, &expanded_y, &expanded_x, &az2 );
expanded_point = Point3D( expanded_x, expanded_y, 0.0f );
expanded_boundary.push_back( expanded_point );
}
}
dst.add_contour( expanded_boundary, 9 );
}
#endif
// finish the poly - convert to TGPolygon, and tesselate
void ClosedPoly::Finish()
{
@ -558,10 +385,18 @@ void ClosedPoly::Finish()
holes.clear();
}
int ClosedPoly::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, ClipPolyType* accum, TGPolygon* apt_base, TGPolygon* apt_clearing )
int ClosedPoly::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, ClipPolyType* accum, poly_list& slivers, TGPolygon* apt_base, TGPolygon* apt_clearing, bool make_shapefiles )
{
TGPolygon base, safe_base;
string material;
string material;
void* ds_id = NULL; // If we are going to build shapefiles
void* l_id = NULL; // datasource and layer IDs
if ( make_shapefiles ) {
char ds_name[128];
sprintf(ds_name, "./cp_debug/problem");
ds_id = tgShapefileOpenDatasource( ds_name );
}
if (is_pavement)
{
@ -600,47 +435,49 @@ int ClosedPoly::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list
SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: original poly has " << pre_tess.contours() << " contours");
// do this before clipping and generating the base
pre_tess = tgPolygonSimplify( pre_tess );
pre_tess = reduce_degeneracy( pre_tess );
// grow pretess by a little bit
//pre_tess = tgPolygonExpand( pre_tess, 0.05); // 5cm
// pre_tess = tgPolygonSimplify( pre_tess );
// pre_tess = reduce_degeneracy( pre_tess );
TGSuperPoly sp;
TGTexParams tp;
SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: expanded poly has " << pre_tess.contours() << " contours");
for (int i=0; i<pre_tess.contours(); i++)
{
SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: expanded countour " << i << " has " << pre_tess.contour_size(i) << " points" );
}
#if 1
TGPolygon clipped = tgPolygonDiff( pre_tess, *accum );
#else
TGPolygon clipped = tgPolygonDiffClipper( pre_tess, *accum );
#endif
SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: clipped poly has " << clipped.contours() << " contours");
for (int i=0; i<clipped.contours(); i++)
{
SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: clipped poly countour " << i << " has " << clipped.contour_size(i) << " points" );
}
TGPolygon split = tgPolygonSplitLongEdges( clipped, 400.0 );
SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: split poly has " << split.contours() << " contours");
SG_LOG(SG_GENERAL, SG_DEBUG, "clipped = " << clipped.contours());
tgPolygonFindSlivers( clipped, slivers );
sp.erase();
sp.set_poly( split );
sp.set_poly( clipped );
sp.set_material( material );
//sp.set_flag("taxi");
rwy_polys->push_back( sp );
SG_LOG(SG_GENERAL, SG_DEBUG, "clipped = " << clipped.contours());
#if 1
*accum = tgPolygonUnion( pre_tess, *accum );
#else
*accum = tgPolygonUnionClipper( pre_tess, *accum );
#endif
/* If debugging this poly, write the poly, and clipped poly and the accum buffer into their own layers */
if (ds_id) {
char layer_name[128];
char feature_name[128];
sprintf( layer_name, "original" );
l_id = tgShapefileOpenLayer( ds_id, layer_name );
sprintf( feature_name, "original" );
tgShapefileCreateFeature( ds_id, l_id, pre_tess, feature_name );
sprintf( layer_name, "clipped" );
l_id = tgShapefileOpenLayer( ds_id, layer_name );
sprintf( feature_name, "clipped" );
tgShapefileCreateFeature( ds_id, l_id, clipped, feature_name );
sprintf( layer_name, "accum" );
l_id = tgShapefileOpenLayer( ds_id, layer_name );
sprintf( feature_name, "accum" );
tgShapefileCreateFeature( ds_id, l_id, *accum, feature_name );
tgShapefileCloseDatasource( ds_id );
}
tp = TGTexParams( pre_tess.get_pt(0,0), 5.0, 5.0, texture_heading );
texparams->push_back( tp );
@ -664,7 +501,7 @@ int ClosedPoly::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list
// Just used for user defined border - add a little bit, as some modelers made the border exactly on the edges
// - resulting in no base, which we can't handle
int ClosedPoly::BuildBtg( float alt_m, TGPolygon* apt_base, TGPolygon* apt_clearing )
int ClosedPoly::BuildBtg( float alt_m, TGPolygon* apt_base, TGPolygon* apt_clearing, bool make_shapefiles )
{
TGPolygon base, safe_base;
@ -677,10 +514,10 @@ int ClosedPoly::BuildBtg( float alt_m, TGPolygon* apt_base, TGPolygon* apt_clear
safe_base = tgPolygonExpand( pre_tess, 5.0);
// add this to the airport clearing
*apt_clearing = tgPolygonUnion( safe_base, *apt_clearing);
*apt_clearing = tgPolygonUnionClipper( safe_base, *apt_clearing);
// and add the clearing to the base
*apt_base = tgPolygonUnion( base, *apt_base );
*apt_base = tgPolygonUnionClipper( base, *apt_base );
}
return 1;

View file

@ -25,10 +25,10 @@ public:
void Finish();
// Build BTG for airport base for airports with boundary
int BuildBtg( float alt_m, TGPolygon* apt_base, TGPolygon* apt_clearing );
int BuildBtg( float alt_m, TGPolygon* apt_base, TGPolygon* apt_clearing, bool make_shapefiles );
// Build BTG for pavements for airports with no boundary
int BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, ClipPolyType* accum, TGPolygon* apt_base, TGPolygon* apt_clearing );
int BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, ClipPolyType* accum, poly_list& slivers, TGPolygon* apt_base, TGPolygon* apt_clearing, bool make_shapefiles );
FeatureList* GetFeatures()
{

View file

@ -79,7 +79,7 @@ void Helipad::BuildBtg( float alt_m,
superpoly_list *rwy_polys,
texparams_list *texparams,
superpoly_list *rwy_lights,
ClipPolyType *accum, TGPolygon* apt_base, TGPolygon* apt_clearing )
ClipPolyType *accum, poly_list& slivers, TGPolygon* apt_base, TGPolygon* apt_clearing )
{
SG_LOG( SG_GENERAL, SG_INFO, "Building helipad = " << heli.designator );
@ -110,7 +110,7 @@ void Helipad::BuildBtg( float alt_m,
0.0, 1.0, 0.0, 1.0,
heli.heading, heli.width, heli.length,
"pc_", "heli",
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
// generate area around helipad
@ -123,10 +123,10 @@ void Helipad::BuildBtg( float alt_m,
safe_base = gen_runway_area_w_extend( 0.0, maxsize * 0.5, 0.0, 0.0, maxsize * 0.5 );
// add this to the airport clearing
*apt_clearing = tgPolygonUnion(safe_base, *apt_clearing);
*apt_clearing = tgPolygonUnionClipper(safe_base, *apt_clearing);
// and add the clearing to the base
*apt_base = tgPolygonUnion( base, *apt_base );
*apt_base = tgPolygonUnionClipper( base, *apt_base );
}
if (heli.edge_lights)

View file

@ -27,7 +27,7 @@ class Helipad
{
public:
Helipad(char* def);
void BuildBtg( float alt_m, superpoly_list* heli_polys, texparams_list* texparams, superpoly_list* heli_lights, ClipPolyType* accum, TGPolygon* apt_base, TGPolygon* apt_clearing );
void BuildBtg( float alt_m, superpoly_list* heli_polys, texparams_list* texparams, superpoly_list* heli_lights, ClipPolyType* accum, poly_list& slivers, TGPolygon* apt_base, TGPolygon* apt_clearing );
Point3D GetLoc()
{

View file

@ -742,6 +742,8 @@ int LinearFeature::Finish( bool closed, unsigned int idx )
cur_outer = OffsetPointMiddle( &points[j-1], &points[j], &points[j+1], offset-width/2.0f );
cur_inner = OffsetPointMiddle( &points[j-1], &points[j], &points[j+1], offset+width/2.0f );
}
cur_outer.snap();
cur_inner.snap();
if ( (prev_inner.x() != 0.0f) && (prev_inner.y() != 0.0f) )
{
@ -832,7 +834,6 @@ int LinearFeature::Finish( bool closed, unsigned int idx )
{
SG_LOG(SG_GENERAL, SG_DEBUG, "LinearFeature::Finish: calculating offsets for light " << i << " whose start idx is " << lights[i]->start_idx << " and end idx is " << lights[i]->end_idx << " cur idx is " << j );
// for each point on the PointsList, offset by 2 distnaces from the edge, and add a point to the superpoly contour
if (j == lights[i]->start_idx)
{
// first point on the light - offset heading is 90deg
@ -855,42 +856,34 @@ int LinearFeature::Finish( bool closed, unsigned int idx )
// calculate the heading and distance from prev to cur
geo_inverse_wgs_84( prev_outer.y(), prev_outer.x(), cur_outer.y(), cur_outer.x(), &heading, &az2, &dist);
if (cur_light_dist > dist)
while (cur_light_dist < dist)
{
// no lights in this segment - increment cur_light_dist only
cur_light_dist += dist;
}
else
{
while (cur_light_dist < dist)
if (cur_light_dist == 0.0f)
{
if (cur_light_dist == 0.0f)
{
tmp = prev_outer;
}
else
{
// calculate the position of the next light
geo_direct_wgs_84( prev_outer.y(), prev_outer.x(), heading, cur_light_dist, &pt_y, &pt_x, &az2 );
tmp = Point3D( pt_x, pt_y, 0.0 );
}
poly.add_node(0, tmp);
// calculate the normal
Point3D vec = sgGeodToCart( tmp * SG_DEGREES_TO_RADIANS );
double length = vec.distance3D( Point3D(0.0) );
vec = vec / length;
normals_poly.add_node(0, vec );
// update current light distance
cur_light_dist += light_delta;
tmp = prev_outer;
}
else
{
// calculate the position of the next light
geo_direct_wgs_84( prev_outer.y(), prev_outer.x(), heading, cur_light_dist, &pt_y, &pt_x, &az2 );
tmp = Point3D( pt_x, pt_y, 0.0 );
}
poly.add_node(0, tmp);
// start next segment at the correct distance
cur_light_dist = cur_light_dist - dist;
// calculate the normal
Point3D vec = sgGeodToCart( tmp * SG_DEGREES_TO_RADIANS );
double length = vec.distance3D( Point3D(0.0) );
vec = vec / length;
normals_poly.add_node(0, vec );
// update current light distance
cur_light_dist += light_delta;
}
// start next segment at the correct distance
cur_light_dist = cur_light_dist - dist;
}
prev_outer = cur_outer;
@ -916,30 +909,28 @@ int LinearFeature::Finish( bool closed, unsigned int idx )
return 1;
}
int LinearFeature::BuildBtg(float alt_m, superpoly_list* line_polys, texparams_list* line_tps, ClipPolyType* line_accum, superpoly_list* lights, bool debug )
int LinearFeature::BuildBtg(float alt_m, superpoly_list* line_polys, texparams_list* line_tps, ClipPolyType* line_accum, superpoly_list* lights, bool make_shapefiles )
{
TGPolygon poly;
TGPolygon clipped;
void* ds_id = NULL; // If we are going to build shapefiles
void* l_id = NULL; // datasource and layer IDs
if (debug) {
sglog().setLogLevels( SG_GENERAL, SG_BULK );
} else {
sglog().setLogLevels( SG_GENERAL, SG_INFO );
if ( make_shapefiles ) {
char ds_name[128];
sprintf(ds_name, "./lf_debug/problem");
ds_id = tgShapefileOpenDatasource( ds_name );
}
SG_LOG(SG_GENERAL, SG_DEBUG, "\nLinearFeature::BuildBtg: " << description);
for ( unsigned int i = 0; i < marking_polys.size(); i++)
{
poly = marking_polys[i].get_poly();
poly = tgPolygonSimplify( poly );
poly = remove_tiny_contours( poly );
//poly = tgPolygonSimplify( poly );
//poly = remove_tiny_contours( poly );
SG_LOG(SG_GENERAL, SG_DEBUG, "LinearFeature::BuildBtg: clipping poly " << i << " of " << marking_polys.size() );
#if 1
clipped = tgPolygonDiff( poly, *line_accum );
#else
SG_LOG(SG_GENERAL, SG_DEBUG, "LinearFeature::BuildBtg: clipping poly " << i << " of " << marking_polys.size() << " with CLIPPER ");
clipped = tgPolygonDiffClipper( poly, *line_accum );
#endif
// clean the poly before union with accum
clipped = reduce_degeneracy( clipped );
@ -947,46 +938,33 @@ int LinearFeature::BuildBtg(float alt_m, superpoly_list* line_polys, texparams_l
marking_polys[i].set_poly( clipped );
line_polys->push_back( marking_polys[i] );
#if LF_DEBUG
if ( (debug) && ( i == 78 ) ) {
void* ds_id;
void* l_id;
SG_LOG(SG_GENERAL, SG_INFO, "Problem poly: " << poly );
char ds_name[128];
sprintf(ds_name, "./lf_debug/problem");
ds_id = tgShapefileOpenDatasource( ds_name );
/* If debugging this lf, write the poly, and the accum buffer at each step into their own layers */
if (ds_id) {
char layer_name[128];
sprintf( layer_name, "problem");
sprintf( layer_name, "poly_%d", i );
l_id = tgShapefileOpenLayer( ds_id, layer_name );
char feature_name[128];
sprintf( feature_name, "prob");
sprintf( feature_name, "poly_%d", i);
tgShapefileCreateFeature( ds_id, l_id, poly, feature_name );
sprintf( layer_name, "accum");
sprintf( layer_name, "accum_%d", i );
l_id = tgShapefileOpenLayer( ds_id, layer_name );
sprintf( feature_name, "accum");
sprintf( feature_name, "accum_%d", i );
tgShapefileCreateFeature( ds_id, l_id, *line_accum, feature_name );
tgShapefileCloseDatasource( ds_id );
}
#endif
SG_LOG(SG_GENERAL, SG_DEBUG, "LinearFeature::BuildBtg: union poly " << i << " of " << marking_polys.size() );
#if 1
*line_accum = tgPolygonUnion( poly, *line_accum );
#else
SG_LOG(SG_GENERAL, SG_DEBUG, "LinearFeature::BuildBtg: union poly " << i << " of " << marking_polys.size() << " with CLIPPER " );
*line_accum = tgPolygonUnionClipper( poly, *line_accum );
#endif
line_tps->push_back( marking_tps[i] );
}
if (ds_id) {
tgShapefileCloseDatasource( ds_id );
}
SG_LOG(SG_GENERAL, SG_DEBUG, "LinearFeature::BuildBtg: add " << lighting_polys.size() << " light defs");
for ( unsigned i = 0; i < lighting_polys.size(); i++)
{

View file

@ -127,8 +127,7 @@ int main(int argc, char **argv)
elev_src.clear();
setup_default_elevation_sources(elev_src);
// Set verbose
// sglog().setLogLevels( SG_GENERAL, SG_BULK );
// Set Normal logging
sglog().setLogLevels( SG_GENERAL, SG_INFO );
SG_LOG(SG_GENERAL, SG_INFO, "Run genapt");
@ -140,6 +139,11 @@ int main(int argc, char **argv)
string restart_id = "";
string airport_id = "";
string last_apt_file = "./last_apt.txt";
int dump_rwy_poly = -1;
int dump_pvmt_poly = -1;
int dump_feat_poly = -1;
int dump_base_poly = -1;
int arg_pos;
for (arg_pos = 1; arg_pos < argc; arg_pos++)
@ -225,6 +229,22 @@ int main(int argc, char **argv)
{
slope_max = atof( arg.substr(12).c_str() );
}
else if ( arg.find("--dump-rwy=") == 0 )
{
dump_rwy_poly = atoi( arg.substr(11).c_str() );
}
else if ( arg.find("--dump-pvmt=") == 0 )
{
dump_pvmt_poly = atoi( arg.substr(12).c_str() );
}
else if ( arg.find("--dump-feat=") == 0 )
{
dump_feat_poly = atoi( arg.substr(12).c_str() );
}
else if ( arg.find("--dump-base=") == 0 )
{
dump_base_poly = atoi( arg.substr(12).c_str() );
}
else if ( (arg.find("--help") == 0) || (arg.find("-h") == 0) )
{
help( argc, argv, elev_src );
@ -296,6 +316,9 @@ int main(int argc, char **argv)
// Create the parser...
Parser* parser = new Parser(input_file, work_dir, elev_src);
// Add any debug
parser->SetDebugPolys( dump_rwy_poly, dump_pvmt_poly, dump_feat_poly, dump_base_poly );
// just one airport
if ( airport_id != "" )
{

View file

@ -317,6 +317,14 @@ void Parser::RemoveAirport( string icao )
}
}
void Parser::SetDebugPolys( int rwy, int pvmt, int feat, int base )
{
rwy_poly = rwy;
pvmt_poly = pvmt;
feat_poly = feat;
base_poly = base;
}
void Parser::Parse( string last_apt_file )
{
char tmp[2048];
@ -644,6 +652,7 @@ int Parser::ParseLine(char* line)
SetState( STATE_PARSE_SIMPLE );
SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing land airport: " << line);
cur_airport = new Airport( code, line );
cur_airport->SetDebugPolys( rwy_poly, pvmt_poly, feat_poly, base_poly );
}
else
{
@ -656,6 +665,7 @@ int Parser::ParseLine(char* line)
SetState( STATE_PARSE_SIMPLE );
SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing heliport: " << line);
cur_airport = new Airport( code, line );
cur_airport->SetDebugPolys( rwy_poly, pvmt_poly, feat_poly, base_poly );
}
else
{

View file

@ -83,8 +83,15 @@ public:
cur_sign = NULL;
prev_node = NULL;
cur_state = STATE_NONE;
// Debug
rwy_poly = -1;
pvmt_poly = -1;
feat_poly = -1;
base_poly = -1;
}
void SetDebugPolys( int rwy, int pvmt, int feat, int base );
long FindAirport( string icao );
void AddAirport( string icao );
void AddAirports( long start_pos, float min_lat, float min_lon, float max_lat, float max_lon );
@ -126,6 +133,12 @@ private:
// List of positions in database file to parse
ParseList parse_positions;
IcaoList airport_icaos;
// debug
int rwy_poly;
int pvmt_poly;
int feat_poly;
int base_poly;
};
#endif

View file

@ -79,7 +79,7 @@ TGPolygon WaterRunway::GetNodes()
}
int Runway::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, superpoly_list* rwy_lights, ClipPolyType* accum, TGPolygon* apt_base, TGPolygon* apt_clearing )
int Runway::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, superpoly_list* rwy_lights, ClipPolyType* accum, poly_list& slivers, TGPolygon* apt_base, TGPolygon* apt_clearing )
{
TGPolygon base, safe_base;
string material;
@ -128,7 +128,7 @@ int Runway::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* te
case 1: // asphalt:
case 2: // concrete
SG_LOG( SG_GENERAL, SG_DEBUG, "Build Runway: asphalt or concrete" << rwy.surface);
gen_rwy( alt_m, material, rwy_polys, texparams, accum );
gen_rwy( alt_m, material, rwy_polys, texparams, accum, slivers );
gen_runway_lights( alt_m, rwy_lights );
break;
@ -136,7 +136,7 @@ int Runway::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* te
case 4: // Dirt
case 5: // Gravel
SG_LOG( SG_GENERAL, SG_DEBUG, "Build Runway: Turf, Dirt or Gravel" << rwy.surface );
gen_simple_rwy( alt_m, material, rwy_polys, texparams, accum );
gen_simple_rwy( alt_m, material, rwy_polys, texparams, accum, slivers );
gen_runway_lights( alt_m, rwy_lights );
break;
@ -172,10 +172,10 @@ int Runway::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* te
safe_base = gen_runway_area_w_extend( 0.0, 180.0, -rwy.overrun[0], -rwy.overrun[1], 50.0 );
// add this to the airport clearing
*apt_clearing = tgPolygonUnion(safe_base, *apt_clearing);
*apt_clearing = tgPolygonUnionClipper(safe_base, *apt_clearing);
// and add the clearing to the base
*apt_base = tgPolygonUnion( base, *apt_base );
*apt_base = tgPolygonUnionClipper( base, *apt_base );
}
return 0;

View file

@ -46,11 +46,12 @@ public:
else return false;
}
int BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, superpoly_list* rwy_lights, ClipPolyType* accum, TGPolygon* apt_base, TGPolygon* apt_clearing );
int BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, superpoly_list* rwy_lights, ClipPolyType* accum, poly_list& slivers, TGPolygon* apt_base, TGPolygon* apt_clearing );
void BuildShoulder( float alt_m,
superpoly_list *rwy_polys,
texparams_list *texparams,
ClipPolyType *accum,
poly_list& slivers,
TGPolygon* apt_base,
TGPolygon* apt_clearing );
@ -102,7 +103,8 @@ private:
double &start_pct, double &end_pct,
superpoly_list* rwy_polys,
texparams_list* texparams,
ClipPolyType* accum );
ClipPolyType* accum,
poly_list& slivers );
// generate the runway overrun area
void gen_runway_overrun( const TGPolygon& runway_half,
@ -110,7 +112,8 @@ private:
const std::string& prefix,
superpoly_list* rwy_polys,
texparams_list* texparams,
ClipPolyType* accum );
ClipPolyType* accum,
poly_list& slivers );
// generate a section of runway
void gen_runway_section( const TGPolygon& runway,
@ -122,14 +125,16 @@ private:
const std::string& material,
superpoly_list* rwy_polys,
texparams_list* texparams,
ClipPolyType* accum );
ClipPolyType* accum,
poly_list& slivers );
void gen_simple_rwy( double alt_m, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, ClipPolyType *accum );
void gen_simple_rwy( double alt_m, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, ClipPolyType *accum, poly_list& slivers );
void gen_rwy( double alt_m,
const std::string& material,
superpoly_list* rwy_polys,
texparams_list* texparams,
ClipPolyType* accum );
ClipPolyType* accum,
poly_list& slivers );
void gen_rw_marking( const TGPolygon& runway,
double &start1_pct, double &end1_pct,
@ -137,7 +142,9 @@ private:
const string& material,
superpoly_list* rwy_polys,
texparams_list* texparams,
ClipPolyType* accum, int marking);
ClipPolyType* accum,
poly_list& slivers,
int marking );
void gen_runway_lights( float alt_m, superpoly_list* lights );

View file

@ -39,12 +39,13 @@ void Runway::gen_rw_designation( const string& material,
double &start_pct, double &end_pct,
superpoly_list *rwy_polys,
texparams_list *texparams,
ClipPolyType *accum )
ClipPolyType *accum,
poly_list& slivers )
{
if (rwname != "XX"){ /* Do not create a designation block if the runway name is set to none */
string letter = "";
double length = rwy.length / 2.0;
for ( int i = 0; i < rwname.length(); ++i ) {
for ( unsigned int i = 0; i < rwname.length(); ++i ) {
string tmp = rwname.substr(i, 1);
if ( tmp == "L" || tmp == "R" || tmp == "C" ) {
rwname = rwname.substr(0, i);
@ -63,7 +64,7 @@ void Runway::gen_rw_designation( const string& material,
0.0, 1.0, 0.0, 1.0,
heading,
material, letter,
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
}
@ -88,14 +89,14 @@ void Runway::gen_rw_designation( const string& material,
0.0, 1.0, 0.0, 1.0,
heading,
material, tex1,
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
gen_runway_section( poly,
start_pct, end_pct,
0.5, 1.0,
0.0, 1.0, 0.0, 1.0,
heading,
material, tex2,
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
} else if (rwname.length() == 1) {
sprintf( tex1, "%c%c", rwname[0], 'c');
@ -106,7 +107,7 @@ void Runway::gen_rw_designation( const string& material,
0.0, 1.0, 0.0, 1.0,
heading,
material, tex1,
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
}
}
}
@ -117,7 +118,8 @@ void Runway::gen_runway_overrun( const TGPolygon& runway_half,
const string& prefix,
superpoly_list *rwy_polys,
texparams_list *texparams,
ClipPolyType* accum ) {
ClipPolyType* accum,
poly_list& slivers ) {
const float length = rwy.length / 2.0 + 2.0 * SG_FEET_TO_METER;
double start1_pct = 0.0;
double end1_pct = 0.0;
@ -155,7 +157,8 @@ void Runway::gen_runway_overrun( const TGPolygon& runway_half,
"stopway",
rwy_polys,
texparams,
accum);
accum,
slivers );
}
}
@ -170,7 +173,8 @@ void Runway::gen_runway_section( const TGPolygon& runway,
const string& material,
superpoly_list *rwy_polys,
texparams_list *texparams,
ClipPolyType *accum ) {
ClipPolyType *accum,
poly_list& slivers ) {
gen_tex_section( runway,
startl_pct, endl_pct,
startw_pct, endw_pct,
@ -180,6 +184,7 @@ void Runway::gen_runway_section( const TGPolygon& runway,
material,
rwy_polys,
texparams,
accum );
accum,
slivers );
}

View file

@ -38,12 +38,14 @@ struct sections
};
void Runway::gen_rw_marking( const TGPolygon& runway,
double &start1_pct, double &end1_pct,
double heading,
const string& material,
superpoly_list *rwy_polys,
texparams_list *texparams,
ClipPolyType *accum, int marking) {
double &start1_pct, double &end1_pct,
double heading,
const string& material,
superpoly_list *rwy_polys,
texparams_list *texparams,
ClipPolyType *accum,
poly_list& slivers,
int marking) {
std::vector<sections> rw_marking_list;
@ -126,7 +128,7 @@ void Runway::gen_rw_marking( const TGPolygon& runway,
0.0, 1.0, 0.0, 1.0,
heading,
material, rw_marking_list[i].tex,
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
}
}
@ -142,7 +144,8 @@ void Runway::gen_rwy( double alt_m,
const string& material,
superpoly_list *rwy_polys,
texparams_list *texparams,
ClipPolyType *accum )
ClipPolyType *accum,
poly_list& slivers )
{
SG_LOG( SG_GENERAL, SG_DEBUG, "Building runway = " << rwy.rwnum[0] << " / " << rwy.rwnum[1]);
@ -232,7 +235,7 @@ void Runway::gen_rwy( double alt_m,
0.0, 1.0, tex_pct, 1.0,
heading,
material, "dspl_thresh",
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
// main chunks
for ( i = 0; i < count; ++i ) {
@ -244,7 +247,7 @@ void Runway::gen_rwy( double alt_m,
0.0, 1.0, 0.0, 1.0,
heading,
material, "dspl_thresh",
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
}
// final arrows
@ -256,7 +259,7 @@ void Runway::gen_rwy( double alt_m,
0.0, 1.0, 0.0, 1.0,
heading,
material, "dspl_arrows",
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
}
@ -272,7 +275,7 @@ void Runway::gen_rwy( double alt_m,
0.0, 1.0, 0.0, 1.0,
heading,
material, "no_threshold",
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
} else {
// Thresholds for all others
@ -285,19 +288,19 @@ void Runway::gen_rwy( double alt_m,
0.0, 1.0, 0.0, 1.0,
heading,
material, "threshold",
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
}
// Runway designation block
gen_rw_designation( material, runway_half, heading,
rwname, start1_pct, end1_pct, rwy_polys, texparams, accum );
rwname, start1_pct, end1_pct, rwy_polys, texparams, accum, slivers );
if (rwy.marking[rwhalf] > 1){
// Generate remaining markings depending on type of runway
gen_rw_marking( runway_half,
start1_pct, end1_pct,
heading, material,
rwy_polys, texparams, accum, rwy.marking[rwhalf] );
rwy_polys, texparams, accum, slivers, rwy.marking[rwhalf] );
}
//
@ -321,12 +324,12 @@ void Runway::gen_rwy( double alt_m,
0.0, 1.0, 0.0, 1.0,
heading,
material, "rest",
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
}
gen_runway_overrun( runway_half, rwhalf,
material,
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
}
}
@ -334,6 +337,7 @@ void Runway::BuildShoulder( float alt_m,
superpoly_list *rwy_polys,
texparams_list *texparams,
ClipPolyType *accum,
poly_list& slivers,
TGPolygon* apt_base,
TGPolygon* apt_clearing )
{
@ -456,17 +460,20 @@ void Runway::BuildShoulder( float alt_m,
poly.add_node( 0, curInnerLoc );
}
#if 1
#if 0
TGPolygon clipped = tgPolygonDiff( poly, *accum );
#else
TGPolygon clipped = tgPolygonDiffClipper( poly, *accum );
#endif
tgPolygonFindSlivers( clipped, slivers );
sp.erase();
sp.set_poly( clipped );
sp.set_material( shoulder_surface );
rwy_polys->push_back( sp );
#if 1
#if 0
*accum = tgPolygonUnion( poly, *accum );
#else
*accum = tgPolygonUnionClipper( poly, *accum );
@ -486,10 +493,10 @@ void Runway::BuildShoulder( float alt_m,
safe_base = tgPolygonExpand( poly, 50.0f );
// add this to the airport clearing
*apt_clearing = tgPolygonUnion(safe_base, *apt_clearing);
*apt_clearing = tgPolygonUnionClipper(safe_base, *apt_clearing);
// and add the clearing to the base
*apt_base = tgPolygonUnion( base, *apt_base );
*apt_base = tgPolygonUnionClipper( base, *apt_base );
// now set cur locations for the next iteration
curInnerLoc = nextInnerLoc;

View file

@ -36,10 +36,9 @@ void Runway::gen_simple_rwy( double alt_m,
const string& material,
superpoly_list *rwy_polys,
texparams_list *texparams,
ClipPolyType *accum )
ClipPolyType *accum,
poly_list& slivers )
{
int i;
TGPolygon runway = gen_runway_w_mid( alt_m, 0.0, 0.0 );
TGPolygon runway_half;
@ -92,7 +91,7 @@ for ( int rwhalf=0; rwhalf<2; ++rwhalf ){
0.0, 1.0, 0.0, 1.0,
heading,
material, "",
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
}
// Generate runway
Runway::gen_runway_section( runway_half,
@ -101,7 +100,7 @@ for ( int rwhalf=0; rwhalf<2; ++rwhalf ){
0.0, 0.28, 0.0, 1.0,
heading,
material, "",
rwy_polys, texparams, accum );
rwy_polys, texparams, accum, slivers );
}

View file

@ -50,6 +50,9 @@
//const double fgPoint3_Epsilon = 0.0000001;
const double fgPoint3_Epsilon = 0.000001;
#define DO_SNAP 0
#define SNAP_GRID (0.0000001)
enum {PX, PY, PZ}; // axes
// Kludge for msvc++ 6.0 - requires forward decls of friend functions.
@ -101,6 +104,8 @@ public:
void setradius(const double z);
void setelev(const double z);
void snap(void);
// Queries
double& operator [] ( int i); // indexing
@ -214,6 +219,7 @@ inline Point3D Point3D::fromSGGeod(const SGGeod& geod)
pt.setlon(geod.getLongitudeRad());
pt.setlat(geod.getLatitudeRad());
pt.setelev(geod.getElevationM());
return pt;
}
@ -223,6 +229,7 @@ inline Point3D Point3D::fromSGGeoc(const SGGeoc& geoc)
pt.setlon(geoc.getLongitudeRad());
pt.setlat(geoc.getLatitudeRad());
pt.setradius(geoc.getRadiusM());
return pt;
}
@ -232,6 +239,7 @@ inline Point3D Point3D::fromSGVec3(const SGVec3<double>& cart)
pt.setx(cart.x());
pt.sety(cart.y());
pt.setz(cart.z());
return pt;
}
@ -241,6 +249,7 @@ inline Point3D Point3D::fromSGVec3(const SGVec3<float>& cart)
pt.setx(cart.x());
pt.sety(cart.y());
pt.setz(cart.z());
return pt;
}
@ -250,6 +259,7 @@ inline Point3D Point3D::fromSGVec2(const SGVec2<double>& cart)
pt.setx(cart.x());
pt.sety(cart.y());
pt.setz(0);
return pt;
}
@ -259,6 +269,7 @@ inline Point3D Point3D::fromSGVec2(const SGVec2<float>& cart)
pt.setx(cart.x());
pt.sety(cart.y());
pt.setz(0);
return pt;
}
@ -267,27 +278,36 @@ inline Point3D Point3D::fromSGVec2(const SGVec2<float>& cart)
inline Point3D& Point3D::operator = (const Point3D& p)
{
n[PX] = p.n[PX]; n[PY] = p.n[PY]; n[PZ] = p.n[PZ]; return *this;
n[PX] = p.n[PX]; n[PY] = p.n[PY]; n[PZ] = p.n[PZ];
return *this;
}
inline Point3D& Point3D::operator += ( const Point3D& p )
{
n[PX] += p.n[PX]; n[PY] += p.n[PY]; n[PZ] += p.n[PZ]; return *this;
n[PX] += p.n[PX]; n[PY] += p.n[PY]; n[PZ] += p.n[PZ];
return *this;
}
inline Point3D& Point3D::operator -= ( const Point3D& p )
{
n[PX] -= p.n[PX]; n[PY] -= p.n[PY]; n[PZ] -= p.n[PZ]; return *this;
n[PX] -= p.n[PX]; n[PY] -= p.n[PY]; n[PZ] -= p.n[PZ];
return *this;
}
inline Point3D& Point3D::operator *= ( const double d )
{
n[PX] *= d; n[PY] *= d; n[PZ] *= d; return *this;
n[PX] *= d; n[PY] *= d; n[PZ] *= d;
return *this;
}
inline Point3D& Point3D::operator /= ( const double d )
{
double d_inv = 1./d; n[PX] *= d_inv; n[PY] *= d_inv; n[PZ] *= d_inv;
return *this;
}
@ -319,6 +339,13 @@ inline void Point3D::setelev(const double z) {
n[PZ] = z;
}
inline void Point3D::snap( void )
{
n[PX] = SNAP_GRID * round( n[PX]/SNAP_GRID );
n[PY] = SNAP_GRID * round( n[PY]/SNAP_GRID );
n[PZ] = SNAP_GRID * round( n[PZ]/SNAP_GRID );
}
// QUERIES
inline double& Point3D::operator [] ( int i)
@ -417,12 +444,16 @@ inline Point3D operator * (const Point3D& a, const double d)
inline Point3D operator * (const double d, const Point3D& a)
{
return a*d;
Point3D pt = a*d;
return pt;
}
inline Point3D operator / (const Point3D& a, const double d)
{
return Point3D(a) *= (1.0 / d );
Point3D pt = Point3D(a) *= (1.0 / d );
return pt;
}
inline bool operator == (const Point3D& a, const Point3D& b)

View file

@ -33,6 +33,8 @@
#include <simgear/structure/exception.hxx>
#include <Polygon/polygon.hxx>
#include <Polygon/chop.hxx>
#include <Triangulate/trieles.hxx>
#include <algorithm>
@ -361,10 +363,10 @@ TGPolygon polygon_tesselate_alt( TGPolygon &p, bool verbose ) {
point_list nodes;
string flags;
if (verbose) {
flags = "pzqenXYY";
flags = "pzenXYY";
// flags = "pzqenXY"; // allow adding interior points
} else {
flags = "pzqenXYYQ";
flags = "pzenXYYQ";
// flags = "pzqenXYQ"; // allow adding interior points
}
@ -427,9 +429,9 @@ TGPolygon polygon_tesselate_alt_with_extra( TGPolygon &p, const point_list& extr
point_list nodes;
string flags;
if (verbose) {
flags = "VVpzqenXYY";
flags = "VVpzenXYY";
} else {
flags = "pzqenXYYQ";
flags = "pzenXYYQ";
}
if ( polygon_tesselate( p, extra_nodes, trieles, nodes, flags ) >= 0 ) {
@ -613,7 +615,7 @@ static void calc_point_inside( TGContourNode *node, TGPolygon &p ) {
// cout << endl;
if ( xcuts.size() < 2 || (xcuts.size() % 2) != 0 ) {
throw sg_exception("Geometric inconsistency in calc_point_inside()");
throw sg_exception("Geometric inconsistency in calc_point_inside()");
}
double maxlen=0.0;

View file

@ -163,23 +163,19 @@ double tgPolygonCalcAngle(point2d a, point2d b, point2d c) {
// positive areas indicate clockwise winding.
double TGPolygon::area_contour( const int contour ) const {
// area = 1/2 * sum[i = 0 to k-1][x(i)*y(i+1) - x(i+1)*y(i)]
// where i=k is defined as i=0
point_list c = poly[contour];
int size = c.size();
double sum = 0.0;
double area = 0.0;
int i, j;
for ( int i = 0; i < size; ++i ) {
sum += c[(i+1)%size].x() * c[i].y() - c[i].x() * c[(i+1)%size].y();
j = c.size() - 1;
for (i=0; i<c.size(); i++) {
area += (c[j].x() + c[i].x()) * (c[j].y() - c[i].y());
j=i;
}
// area can be negative or positive depending on the polygon
// winding order
return fabs(sum / 2.0);
return fabs(area * 0.5);
}
// return the smallest interior angle of the contour
double TGPolygon::minangle_contour( const int contour ) {
point_list c = poly[contour];
@ -286,6 +282,74 @@ void TGPolygon::write( const string& file ) const {
}
// Move slivers from in polygon to out polygon.
void tgPolygonFindSlivers( TGPolygon& in, poly_list& slivers )
{
// traverse each contour of the polygon and attempt to identify
// likely slivers
SG_LOG(SG_GENERAL, SG_DEBUG, "tgPolygonFindSlivers()");
TGPolygon out;
int i;
out.erase();
double angle_cutoff = 10.0 * SGD_DEGREES_TO_RADIANS;
double area_cutoff = 0.000000001;
double min_angle;
double area;
point_list contour;
int hole_flag;
// process contours in reverse order so deleting a contour doesn't
// foul up our sequence
for ( i = in.contours() - 1; i >= 0; --i ) {
SG_LOG(SG_GENERAL, SG_DEBUG, "contour " << i );
min_angle = in.minangle_contour( i );
area = in.area_contour( i );
SG_LOG(SG_GENERAL, SG_DEBUG, " min_angle (rad) = " << min_angle );
SG_LOG(SG_GENERAL, SG_DEBUG, " min_angle (deg) = " << min_angle * 180.0 / SGD_PI );
SG_LOG(SG_GENERAL, SG_DEBUG, " area = " << area );
if ( ((min_angle < angle_cutoff) && (area < area_cutoff)) ||
( area < area_cutoff / 10.0) )
{
if ((min_angle < angle_cutoff) && (area < area_cutoff))
{
SG_LOG(SG_GENERAL, SG_DEBUG, " WE THINK IT'S A SLIVER! - min angle < 10 deg, and area < 10 sq meters");
}
else
{
SG_LOG(SG_GENERAL, SG_DEBUG, " WE THINK IT'S A SLIVER! - min angle > 10 deg, but area < 1 sq meters");
}
// check if this is a hole
hole_flag = in.get_hole_flag( i );
if ( hole_flag ) {
// just delete/eliminate/remove sliver holes
// cout << "just deleting a sliver hole" << endl;
in.delete_contour( i );
} else {
// move sliver contour to out polygon
contour = in.get_contour( i );
in.delete_contour( i );
out.add_contour( contour, hole_flag );
}
}
}
if ( out.contours() )
{
slivers.push_back( out );
}
}
// output
void TGPolygon::write_contour( const int contour, const string& file ) const {
FILE *fp = fopen( file.c_str(), "w" );
@ -431,12 +495,16 @@ IntPoint MakeClipperPoint( Point3D pt )
Point3D MakeTGPoint( IntPoint pt )
{
Point3D tg_pt;
double x, y;
x = (double)( ((double)pt.X) / (double)FIXEDPT );
y = (double)( ((double)pt.Y) / (double)FIXEDPT );
return Point3D( x, y, -9999.0f);
tg_pt = Point3D( x, y, -9999.0f);
tg_pt.snap();
return tg_pt;
}
double MakeClipperDelta( double mDelta )

View file

@ -252,6 +252,7 @@ TGPolygon tgPolygonStripHoles( const TGPolygon &poly );
TGPolygon tgPolygon2tristrip( const TGPolygon& poly );
void tgPolygonFindSlivers( TGPolygon& in, poly_list& slivers );
// wrapper functions for gpc polygon clip routines