1999-03-01 15:39:39 +00:00
|
|
|
// clipper.cxx -- top level routines to take a series of arbitrary areas and
|
|
|
|
// produce a tight fitting puzzle pieces that combine to make a
|
|
|
|
// tile
|
|
|
|
//
|
|
|
|
// Written by Curtis Olson, started February 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 <Debug/logstream.hxx>
|
|
|
|
#include <Include/fg_constants.h>
|
|
|
|
#include <Misc/fgstream.hxx>
|
|
|
|
#include <Polygon/names.hxx>
|
|
|
|
|
1999-03-13 23:51:32 +00:00
|
|
|
#include "clipper.hxx"
|
1999-03-01 15:39:39 +00:00
|
|
|
|
|
|
|
|
1999-03-13 23:51:32 +00:00
|
|
|
// Constructor
|
|
|
|
FGClipper::FGClipper( void ) {
|
|
|
|
}
|
1999-03-01 15:39:39 +00:00
|
|
|
|
1999-03-13 23:51:32 +00:00
|
|
|
|
|
|
|
// Destructor
|
|
|
|
FGClipper::~FGClipper( void ) {
|
|
|
|
}
|
1999-03-01 15:39:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Initialize Clipper (allocate and/or connect structures)
|
1999-03-13 23:51:32 +00:00
|
|
|
bool FGClipper::init() {
|
1999-06-06 02:31:34 +00:00
|
|
|
// v_list.num_vertices = 0;
|
|
|
|
// v_list.vertex = new gpc_vertex[FG_MAX_VERTICES];;
|
1999-03-01 15:39:39 +00:00
|
|
|
|
1999-05-02 18:41:24 +00:00
|
|
|
for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) {
|
|
|
|
polys_in.polys[i].clear();
|
|
|
|
}
|
|
|
|
|
1999-03-01 15:39:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Load a polygon definition file
|
1999-03-13 23:51:32 +00:00
|
|
|
bool FGClipper::load_polys(const string& path) {
|
1999-03-01 15:39:39 +00:00
|
|
|
string poly_name;
|
1999-03-30 13:41:38 +00:00
|
|
|
AreaType poly_type = DefaultArea;
|
1999-03-01 15:39:39 +00:00
|
|
|
int contours, count, i, j;
|
1999-04-29 04:30:24 +00:00
|
|
|
int hole_flag;
|
1999-03-01 15:39:39 +00:00
|
|
|
double startx, starty, x, y, lastx, lasty;
|
|
|
|
|
|
|
|
FG_LOG( FG_CLIPPER, FG_INFO, "Loading " << path << " ..." );
|
|
|
|
|
|
|
|
fg_gzifstream in( path );
|
|
|
|
|
|
|
|
if ( !in ) {
|
|
|
|
FG_LOG( FG_CLIPPER, FG_ALERT, "Cannot open file: " << path );
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
1999-06-06 02:31:34 +00:00
|
|
|
// gpc_polygon *poly = new gpc_polygon;
|
|
|
|
// poly->num_contours = 0;
|
|
|
|
// poly->contour = NULL;
|
|
|
|
FGPolygon poly;
|
1999-03-30 13:41:38 +00:00
|
|
|
|
1999-06-06 02:31:34 +00:00
|
|
|
Point3D p;
|
1999-03-01 15:39:39 +00:00
|
|
|
in >> skipcomment;
|
|
|
|
while ( !in.eof() ) {
|
|
|
|
in >> poly_name;
|
|
|
|
cout << "poly name = " << poly_name << endl;
|
|
|
|
poly_type = get_area_type( poly_name );
|
|
|
|
cout << "poly type (int) = " << (int)poly_type << endl;
|
|
|
|
in >> contours;
|
|
|
|
cout << "num contours = " << contours << endl;
|
|
|
|
|
1999-06-06 02:31:34 +00:00
|
|
|
poly.erase();
|
|
|
|
|
1999-03-01 15:39:39 +00:00
|
|
|
for ( i = 0; i < contours; ++i ) {
|
|
|
|
in >> count;
|
|
|
|
|
|
|
|
if ( count < 3 ) {
|
|
|
|
FG_LOG( FG_CLIPPER, FG_ALERT,
|
|
|
|
"Polygon with less than 3 data points." );
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
1999-04-29 04:30:24 +00:00
|
|
|
in >> hole_flag;
|
|
|
|
|
1999-03-01 15:39:39 +00:00
|
|
|
in >> startx;
|
|
|
|
in >> starty;
|
1999-06-06 02:31:34 +00:00
|
|
|
p = Point3D(startx, starty, 0.0);
|
|
|
|
poly.add_node( i, p );
|
1999-03-01 15:39:39 +00:00
|
|
|
FG_LOG( FG_CLIPPER, FG_BULK, "0 = "
|
|
|
|
<< startx << ", " << starty );
|
|
|
|
|
|
|
|
for ( j = 1; j < count - 1; ++j ) {
|
|
|
|
in >> x;
|
|
|
|
in >> y;
|
1999-06-06 02:31:34 +00:00
|
|
|
p = Point3D( x, y, 0.0 );
|
|
|
|
poly.add_node( i, p );
|
1999-03-01 15:39:39 +00:00
|
|
|
FG_LOG( FG_CLIPPER, FG_BULK, j << " = " << x << ", " << y );
|
|
|
|
}
|
|
|
|
|
|
|
|
in >> lastx;
|
|
|
|
in >> lasty;
|
|
|
|
|
|
|
|
if ( (fabs(startx - lastx) < FG_EPSILON)
|
|
|
|
&& (fabs(starty - lasty) < FG_EPSILON) ) {
|
|
|
|
// last point same as first, discard
|
|
|
|
} else {
|
1999-06-06 02:31:34 +00:00
|
|
|
p = Point3D( lastx, lasty, 0.0 );
|
|
|
|
poly.add_node( i, p );
|
1999-03-01 15:39:39 +00:00
|
|
|
FG_LOG( FG_CLIPPER, FG_BULK, count - 1 << " = "
|
|
|
|
<< lastx << ", " << lasty );
|
|
|
|
}
|
|
|
|
|
1999-06-06 02:31:34 +00:00
|
|
|
// gpc_add_contour( poly, &v_list, hole_flag );
|
1999-03-01 15:39:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
in >> skipcomment;
|
|
|
|
}
|
|
|
|
|
1999-03-30 13:41:38 +00:00
|
|
|
int area = (int)poly_type;
|
1999-05-13 02:36:16 +00:00
|
|
|
if ( area == OceanArea ) {
|
|
|
|
// TEST - Ignore
|
|
|
|
} else if ( area < FG_MAX_AREA_TYPES ) {
|
1999-03-30 13:41:38 +00:00
|
|
|
polys_in.polys[area].push_back(poly);
|
|
|
|
} else {
|
|
|
|
FG_LOG( FG_CLIPPER, FG_ALERT, "Polygon type out of range = "
|
|
|
|
<< (int)poly_type);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
1999-03-01 15:39:39 +00:00
|
|
|
// FILE *ofp= fopen("outfile", "w");
|
|
|
|
// gpc_write_polygon(ofp, &polys.landuse);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-06-07 03:40:33 +00:00
|
|
|
// remove any slivers from in polygon and move them to out polygon.
|
|
|
|
void FGClipper::move_slivers( FGPolygon& in, FGPolygon& out ) {
|
|
|
|
cout << "Begin move slivers" << endl;
|
1999-05-27 00:19:04 +00:00
|
|
|
// traverse each contour of the polygon and attempt to identify
|
|
|
|
// likely slivers
|
1999-06-07 03:40:33 +00:00
|
|
|
|
|
|
|
out.erase();
|
|
|
|
|
|
|
|
double angle_cutoff = 10.0 * DEG_TO_RAD;
|
|
|
|
double area_cutoff = 0.00001;
|
|
|
|
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 ( int i = in.contours() - 1; i >= 0; --i ) {
|
1999-05-27 00:19:04 +00:00
|
|
|
cout << "contour " << i << endl;
|
1999-06-07 03:40:33 +00:00
|
|
|
|
|
|
|
min_angle = in.minangle_contour( i );
|
|
|
|
area = in.area_contour( i );
|
|
|
|
|
|
|
|
cout << " min_angle (rad) = "
|
|
|
|
<< min_angle << endl;
|
|
|
|
cout << " min_angle (deg) = "
|
|
|
|
<< min_angle * 180.0 / FG_PI << endl;
|
|
|
|
cout << " area = " << area << endl;
|
|
|
|
|
1999-06-08 11:20:24 +00:00
|
|
|
if ( ((min_angle < angle_cutoff) && (area < area_cutoff)) ||
|
|
|
|
(area < area_cutoff / 10.0) )
|
|
|
|
{
|
1999-06-07 03:40:33 +00:00
|
|
|
cout << " WE THINK IT'S A SLIVER!" << endl;
|
|
|
|
|
|
|
|
// check if this is a hole
|
|
|
|
hole_flag = in.get_hole_flag( i );
|
|
|
|
|
|
|
|
if ( hole_flag ) {
|
|
|
|
// just delete/eliminate/remove sliver holes
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// for each sliver contour, see if a union with another polygon yields
|
|
|
|
// a polygon with no increased contours (i.e. the sliver is adjacent
|
|
|
|
// and can be merged.) If so, replace the clipped polygon with the
|
|
|
|
// new polygon that has the sliver merged in.
|
|
|
|
void FGClipper::merge_slivers( FGPolyList& clipped, FGPolygon& slivers ) {
|
|
|
|
FGPolygon poly, result, sliver;
|
|
|
|
point_list contour;
|
|
|
|
int original_contours, result_contours;
|
|
|
|
bool done;
|
|
|
|
|
|
|
|
for ( int i = 0; i < slivers.contours(); ++i ) {
|
|
|
|
cout << "Merging sliver = " << i << endl;
|
|
|
|
|
|
|
|
// make the sliver polygon
|
|
|
|
contour = slivers.get_contour( i );
|
|
|
|
sliver.erase();
|
|
|
|
sliver.add_contour( contour, 0 );
|
|
|
|
done = false;
|
|
|
|
|
|
|
|
for ( int area = 0; area < FG_MAX_AREA_TYPES && !done; ++area ) {
|
|
|
|
cout << " testing area = " << area << " with "
|
|
|
|
<< clipped.polys[area].size() << " polys" << endl;
|
|
|
|
for ( int j = 0;
|
|
|
|
j < (int)clipped.polys[area].size() && !done;
|
|
|
|
++j )
|
|
|
|
{
|
|
|
|
cout << " polygon = " << j << endl;
|
|
|
|
|
|
|
|
poly = clipped.polys[area][j];
|
|
|
|
original_contours = poly.contours();
|
|
|
|
result = polygon_union( poly, sliver );
|
|
|
|
result_contours = result.contours();
|
|
|
|
|
|
|
|
if ( original_contours == result_contours ) {
|
|
|
|
cout << " FOUND a poly to merge the sliver with" << endl;
|
|
|
|
clipped.polys[area][j] = 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 ( int k = 0; k < (int)contour.size(); ++k ) {
|
|
|
|
cout << " " << contour[k].x() << ", "
|
|
|
|
<< contour[k].y() << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-05-27 00:19:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-03-01 15:39:39 +00:00
|
|
|
// Do actually clipping work
|
1999-03-13 23:51:32 +00:00
|
|
|
bool FGClipper::clip_all(const point2d& min, const point2d& max) {
|
1999-06-06 02:31:34 +00:00
|
|
|
FGPolygon accum, result_union, tmp;
|
1999-06-07 03:40:33 +00:00
|
|
|
FGPolygon result_diff, slivers, remains;
|
1999-06-06 02:31:34 +00:00
|
|
|
// gpcpoly_iterator current, last;
|
1999-03-01 15:39:39 +00:00
|
|
|
|
|
|
|
FG_LOG( FG_CLIPPER, FG_INFO, "Running master clipper" );
|
|
|
|
|
1999-06-06 02:31:34 +00:00
|
|
|
accum.erase();
|
1999-03-01 15:39:39 +00:00
|
|
|
|
|
|
|
cout << " (" << min.x << "," << min.y << ") ("
|
|
|
|
<< max.x << "," << max.y << ")" << endl;
|
|
|
|
|
|
|
|
// set up clipping tile
|
1999-06-06 02:31:34 +00:00
|
|
|
polys_in.safety_base.erase();
|
|
|
|
polys_in.safety_base.add_node( 0, Point3D(min.x, min.y, 0.0) );
|
|
|
|
polys_in.safety_base.add_node( 0, Point3D(max.x, min.y, 0.0) );
|
|
|
|
polys_in.safety_base.add_node( 0, Point3D(max.x, max.y, 0.0) );
|
|
|
|
polys_in.safety_base.add_node( 0, Point3D(min.x, max.y, 0.0) );
|
1999-03-01 15:39:39 +00:00
|
|
|
|
1999-05-27 00:19:04 +00:00
|
|
|
// int count = 0;
|
1999-03-01 15:39:39 +00:00
|
|
|
// process polygons in priority order
|
1999-03-19 00:26:18 +00:00
|
|
|
for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) {
|
1999-05-02 18:41:24 +00:00
|
|
|
cout << "num polys of type (" << i << ") = "
|
|
|
|
<< polys_in.polys[i].size() << endl;
|
1999-06-06 02:31:34 +00:00
|
|
|
// current = polys_in.polys[i].begin();
|
|
|
|
// last = polys_in.polys[i].end();
|
|
|
|
// for ( ; current != last; ++current ) {
|
|
|
|
for( int j = 0; j < (int)polys_in.polys[i].size(); ++j ) {
|
|
|
|
FGPolygon current = polys_in.polys[i][j];
|
1999-03-01 15:39:39 +00:00
|
|
|
FG_LOG( FG_CLIPPER, FG_DEBUG, get_area_name( (AreaType)i )
|
1999-06-06 02:31:34 +00:00
|
|
|
<< " = " << current.contours() );
|
1999-03-01 15:39:39 +00:00
|
|
|
|
|
|
|
#ifdef EXTRA_SAFETY_CLIP
|
|
|
|
// clip to base tile
|
1999-06-06 02:31:34 +00:00
|
|
|
tmp = polygon_int( current, polys_in.safety_base );
|
1999-03-01 15:39:39 +00:00
|
|
|
#else
|
1999-06-06 02:31:34 +00:00
|
|
|
tmp = current;
|
1999-03-01 15:39:39 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// clip current polygon against previous higher priority
|
|
|
|
// stuff
|
1999-03-19 00:26:18 +00:00
|
|
|
|
1999-06-06 02:31:34 +00:00
|
|
|
// result_diff = new gpc_polygon;
|
|
|
|
// result_diff->num_contours = 0;
|
|
|
|
// result_diff->contour = NULL;
|
|
|
|
|
|
|
|
if ( accum.contours() == 0 ) {
|
|
|
|
result_diff = tmp;
|
1999-03-01 15:39:39 +00:00
|
|
|
result_union = tmp;
|
|
|
|
} else {
|
1999-03-31 23:46:38 +00:00
|
|
|
// cout << "DIFF: tmp.num_contours = " << tmp.num_contours
|
|
|
|
// << " accum.num_contours = " << accum.num_contours
|
|
|
|
// << endl;
|
1999-03-30 23:49:22 +00:00
|
|
|
// tmp output accum
|
|
|
|
|
1999-05-20 04:23:19 +00:00
|
|
|
// FILE *ofp= fopen("tmp-debug", "w");
|
|
|
|
// gpc_write_polygon(ofp, 1, &tmp);
|
|
|
|
// fclose(ofp);
|
|
|
|
|
|
|
|
// ofp= fopen("accum-debug", "w");
|
|
|
|
// gpc_write_polygon(ofp, 1, &accum);
|
|
|
|
// fclose(ofp);
|
1999-03-30 23:49:22 +00:00
|
|
|
|
1999-06-06 02:31:34 +00:00
|
|
|
result_diff = polygon_diff( tmp, accum);
|
|
|
|
result_union = polygon_union( tmp, accum);
|
1999-03-01 15:39:39 +00:00
|
|
|
}
|
1999-03-19 00:26:18 +00:00
|
|
|
|
|
|
|
/*
|
1999-05-02 18:41:24 +00:00
|
|
|
cout << "original contours = " << tmp.num_contours << endl;
|
1999-03-19 00:26:18 +00:00
|
|
|
|
1999-05-02 18:41:24 +00:00
|
|
|
for ( int j = 0; j < tmp.num_contours; j++ ) {
|
|
|
|
for (int k = 0;k < tmp.contour[j].num_vertices;k++ ) {
|
|
|
|
cout << tmp.contour[j].vertex[k].x << ","
|
|
|
|
<< tmp.contour[j].vertex[k].y << endl;
|
|
|
|
}
|
|
|
|
}
|
1999-03-19 00:26:18 +00:00
|
|
|
|
1999-05-02 18:41:24 +00:00
|
|
|
cout << "clipped contours = " << result_diff->num_contours << endl;
|
1999-03-19 00:26:18 +00:00
|
|
|
|
1999-05-02 18:41:24 +00:00
|
|
|
for ( int j = 0; j < result_diff->num_contours; j++ ) {
|
|
|
|
for (int k = 0;k < result_diff->contour[j].num_vertices;k++ ) {
|
|
|
|
cout << result_diff->contour[j].vertex[k].x << ","
|
|
|
|
<< result_diff->contour[j].vertex[k].y << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
1999-03-19 00:26:18 +00:00
|
|
|
|
1999-03-19 22:28:46 +00:00
|
|
|
// only add to output list if the clip left us with a polygon
|
1999-06-06 02:31:34 +00:00
|
|
|
if ( result_diff.contours() > 0 ) {
|
1999-06-07 03:40:33 +00:00
|
|
|
// move slivers from result_diff polygon to slivers polygon
|
|
|
|
move_slivers(result_diff, slivers);
|
|
|
|
cout << " After sliver move:" << endl;
|
|
|
|
cout << " result_diff = " << result_diff.contours() << endl;
|
|
|
|
cout << " slivers = " << slivers.contours() << endl;
|
|
|
|
|
|
|
|
// merge any slivers with previously clipped
|
|
|
|
// neighboring polygons
|
|
|
|
if ( slivers.contours() > 0 ) {
|
|
|
|
merge_slivers(polys_clipped, slivers);
|
|
|
|
}
|
|
|
|
|
|
|
|
// add the sliverless result polygon (from after the
|
|
|
|
// move_slivers) to the clipped polys list
|
|
|
|
if ( result_diff.contours() > 0 ) {
|
|
|
|
polys_clipped.polys[i].push_back(result_diff);
|
|
|
|
}
|
1999-05-20 04:23:19 +00:00
|
|
|
|
|
|
|
// char filename[256];
|
|
|
|
// sprintf(filename, "next-result-%02d", count++);
|
|
|
|
// FILE *tmpfp= fopen(filename, "w");
|
|
|
|
// gpc_write_polygon(tmpfp, 1, result_diff);
|
|
|
|
// fclose(tmpfp);
|
1999-03-19 22:28:46 +00:00
|
|
|
}
|
1999-03-01 15:39:39 +00:00
|
|
|
accum = result_union;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-05-13 02:36:16 +00:00
|
|
|
// finally, what ever is left over goes to ocean
|
1999-03-01 15:39:39 +00:00
|
|
|
|
|
|
|
// clip to accum against original base tile
|
1999-06-06 02:31:34 +00:00
|
|
|
// remains = new gpc_polygon;
|
|
|
|
// remains->num_contours = 0;
|
|
|
|
// remains->contour = NULL;
|
|
|
|
remains = polygon_diff( polys_in.safety_base, accum );
|
|
|
|
|
|
|
|
if ( remains.contours() > 0 ) {
|
1999-06-07 03:40:33 +00:00
|
|
|
cout << "remains contours = " << remains.contours() << endl;
|
|
|
|
// move slivers from remains polygon to slivers polygon
|
|
|
|
move_slivers(remains, slivers);
|
|
|
|
cout << " After sliver move:" << endl;
|
|
|
|
cout << " remains = " << remains.contours() << endl;
|
|
|
|
cout << " slivers = " << slivers.contours() << endl;
|
|
|
|
|
|
|
|
// merge any slivers with previously clipped
|
|
|
|
// neighboring polygons
|
|
|
|
if ( slivers.contours() > 0 ) {
|
|
|
|
merge_slivers(polys_clipped, slivers);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( remains.contours() > 0 ) {
|
|
|
|
polys_clipped.polys[(int)OceanArea].push_back(remains);
|
|
|
|
}
|
1999-03-27 05:20:53 +00:00
|
|
|
}
|
1999-03-01 15:39:39 +00:00
|
|
|
|
1999-06-06 02:31:34 +00:00
|
|
|
#if 0
|
1999-05-02 18:41:24 +00:00
|
|
|
FILE *ofp;
|
|
|
|
|
1999-03-01 15:39:39 +00:00
|
|
|
// tmp output accum
|
1999-05-02 18:41:24 +00:00
|
|
|
if ( accum.num_contours ) {
|
|
|
|
ofp = fopen("accum", "w");
|
|
|
|
gpc_write_polygon(ofp, 1, &accum);
|
|
|
|
fclose(ofp);
|
|
|
|
}
|
1999-03-01 15:39:39 +00:00
|
|
|
|
|
|
|
// tmp output safety_base
|
1999-05-02 18:41:24 +00:00
|
|
|
if ( remains->num_contours ) {
|
|
|
|
ofp= fopen("remains", "w");
|
|
|
|
gpc_write_polygon(ofp, 1, remains);
|
|
|
|
fclose(ofp);
|
|
|
|
}
|
1999-06-06 02:31:34 +00:00
|
|
|
#endif
|
1999-03-01 15:39:39 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|