1
0
Fork 0

Working towards the ability to specify elevations for a polygon in it's

intermediate mode.  The goal then is that these elevations would be
preserved throughout the tile construction process and the surrounding
geometry would fill in without gaps.  This has potential applications for
airports and runways of course as well as roads, rivers, streams, railroads,
or any other object where we might want to control the final elevation in
advance.
This commit is contained in:
curt 2003-03-12 18:23:31 +00:00
parent af89a8a205
commit 9c85f16b5f
13 changed files with 75 additions and 364 deletions

View file

@ -150,8 +150,8 @@ TGAptSurface::TGAptSurface( const string& path,
cout << "Area size = " << x_m << " x " << y_m << " (m)" << endl;
int xdivs = (int)(x_m / 1100.0) + 1;
int ydivs = (int)(y_m / 1100.0) + 1;
int xdivs = (int)(x_m / 1200.0) + 1;
int ydivs = (int)(y_m / 1200.0) + 1;
if ( xdivs < 3 ) { xdivs = 3; }
if ( ydivs < 3 ) { ydivs = 3; }

View file

@ -174,8 +174,8 @@ static TGPolygon rwy_section_tex_coords( const TGPolygon& in_poly,
}
// Determine node elevations based on provide TGAptSurface. Offset is
// added to the final elevation
// Determine node elevations of a point_list based on the provided
// TGAptSurface. Offset is added to the final elevation
static point_list calc_elevations( TGAptSurface &surf,
const point_list& geod_nodes,
double offset )
@ -190,91 +190,22 @@ static point_list calc_elevations( TGAptSurface &surf,
}
#if 0
// fix node elevations. Offset is added to the final elevation
static point_list calc_elevations( const string& root,
const point_list& geod_nodes,
double offset )
// Determine node elevations of each node of a TGPolygon based on the
// provided TGAptSurface. Offset is added to the final elevation
static TGPolygon calc_elevations( TGAptSurface &surf,
const TGPolygon& poly,
double offset )
{
string_list elev_src;
elev_src.clear();
elev_src.push_back( "SRTM-1" );
elev_src.push_back( "SRTM-3" );
elev_src.push_back( "DEM-3" );
elev_src.push_back( "DEM-30" );
TGPolygon result;
for ( int i = 0; i < poly.contours(); ++i ) {
point_list contour = poly.get_contour( i );
point_list elevated = calc_elevations( surf, contour, offset );
bool done = false;
point_list result = geod_nodes;
int i;
unsigned int j;
TGArray array;
// just bail if no work to do
if ( ! result.size() ) {
return result;
}
// set all elevations to -9999
for ( i = 0; i < (int)result.size(); ++i ) {
result[i].setz( -9999.0 );
}
// cout << "result.size() = " << result.size() << endl;
while ( !done ) {
// find first node with -9999 elevation
i = 0;
while ( (i < (int)result.size()) && (result[i].z() > -9000) ) {
++i;
}
if ( i < (int)result.size() ) {
// cout << "First empty elevation = " << i << endl;
SGBucket b( result[i].x(), result[i].y() );
string base = b.gen_base_path();
// try the various elevation sources
bool found_file = false;
unsigned int k = 0;
while ( !found_file && k < elev_src.size() ) {
string array_path = root + "/" + elev_src[k] + "/" + base
+ "/" + b.gen_index_str() + ".arr";
if ( array.open(array_path) ) {
found_file = true;
SG_LOG(SG_GENERAL, SG_DEBUG, "array_path = " << array_path);
}
k++;
}
// this will fill in a zero structure if no array data
// found/opened
array.parse( b );
// update all the non-updated elevations that are inside
// this array file
double elev;
done = true;
for ( j = 0; j < result.size(); ++j ) {
if ( result[j].z() < -9000 ) {
done = false;
elev = array.interpolate_altitude( result[j].x() * 3600.0,
result[j].y() * 3600.0 );
if ( elev > -9000 ) {
result[j].setz( elev + offset );
SG_LOG( SG_GENERAL, SG_INFO,
"interpolating for " << result[j] );
}
}
}
array.close();
} else {
done = true;
}
result.add_contour( elevated, poly.get_hole_flag(i) );
}
return result;
}
#endif
// strip trailing spaces
@ -986,10 +917,10 @@ void build_airport( string airport_raw, float alt_m,
// Extend the area a bit so we don't have wierd things on the edges
double dlon = max_deg.lon() - min_deg.lon();
double dlat = max_deg.lat() - min_deg.lat();
min_deg.setlon( min_deg.lon() - 0.25 * dlon );
max_deg.setlon( max_deg.lon() + 0.25 * dlon );
min_deg.setlat( min_deg.lat() - 0.25 * dlat );
max_deg.setlat( max_deg.lat() + 0.25 * dlat );
min_deg.setlon( min_deg.lon() - 0.1 * dlon );
max_deg.setlon( max_deg.lon() + 0.1 * dlon );
min_deg.setlat( min_deg.lat() - 0.1 * dlat );
max_deg.setlat( max_deg.lat() + 0.1 * dlat );
TGAptSurface apt_surf( root, min_deg, max_deg );
cout << "Surface created" << endl;
@ -997,7 +928,9 @@ void build_airport( string airport_raw, float alt_m,
// calculate node elevations
point_list geod_nodes = calc_elevations( apt_surf, nodes.get_node_list(),
0.0 );
SG_LOG(SG_GENERAL, SG_DEBUG, "Done with calc_elevations()");
divided_base = calc_elevations( apt_surf, divided_base, 0.0 );
SG_LOG(SG_GENERAL, SG_DEBUG, "Done with base calc_elevations()");
// add base skirt (to hide potential cracks)
//
@ -1137,6 +1070,8 @@ void build_airport( string airport_raw, float alt_m,
}
}
SG_LOG(SG_GENERAL, SG_DEBUG, "Done with lighting calc_elevations()");
// pass two, for each light group check if we need to lift (based
// on flag) and do so, then output next structures.
for ( i = 0; i < (int)rwy_lights.size(); ++i ) {
@ -1244,6 +1179,6 @@ void build_airport( string airport_raw, float alt_m,
string holepath = root + "/AirportArea";
// long int poly_index = poly_index_next();
// write_boundary( holepath, b, hull, poly_index );
split_polygon( holepath, HoleArea, divided_base );
split_polygon( holepath, AirportArea, apt_clearing );
tgSplitPolygon( holepath, HoleArea, divided_base, true );
tgSplitPolygon( holepath, AirportArea, apt_clearing, false );
}

View file

@ -65,6 +65,8 @@ bool FGClipper::init() {
// Load a polygon definition file.
bool FGClipper::load_polys(const string& path) {
bool poly3d = false;
string first_line;
string poly_name;
AreaType poly_type = DefaultArea;
int contours, count, i, j;
@ -83,9 +85,20 @@ bool FGClipper::load_polys(const string& path) {
TGPolygon poly;
Point3D p;
in >> skipcomment;
// (this could break things, why is it here) in >> skipcomment;
while ( !in.eof() ) {
in >> poly_name;
in >> first_line;
if ( first_line == "#2D" ) {
poly3d = false;
in >> poly_name;
} else if ( first_line == "#3D" ) {
poly3d = true;
in >> poly_name;
} else {
// support old format (default to 2d)
poly3d = false;
poly_name = first_line;
}
cout << "poly name = " << poly_name << endl;
poly_type = get_area_type( poly_name );
cout << "poly type (int) = " << (int)poly_type << endl;

View file

@ -129,7 +129,9 @@ static void clip_and_write_poly( string root, long int p_index, AreaType area,
// process shape (write polygon to all intersecting tiles)
void split_polygon(const string& path, AreaType area, const TGPolygon& shape) {
void tgSplitPolygon( const string& path, AreaType area,
const TGPolygon& shape, bool preserve3d )
{
Point3D min, max, p;
// point2d min, max;
long int index;
@ -245,7 +247,7 @@ void split_polygon(const string& path, AreaType area, const TGPolygon& shape) {
bottom_clip = horizontal_clip( shape, clip_line, Below );
}
split_polygon(path, area, bottom_clip);
tgSplitPolygon( path, area, bottom_clip, preserve3d );
}
{
@ -274,6 +276,6 @@ void split_polygon(const string& path, AreaType area, const TGPolygon& shape) {
top_clip = horizontal_clip( shape, clip_line, Above );
}
split_polygon(path, area, top_clip);
tgSplitPolygon( path, area, top_clip, preserve3d );
}
}

View file

@ -1,240 +0,0 @@
// split.cxx -- polygon splitting utils
//
// 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 <simgear/compiler.h>
#include STL_STRING
#include <simgear/bucket/newbucket.hxx>
#include <simgear/debug/logstream.hxx>
#ifdef _MSC_VER
# include <win32/mkdir.hpp>
#endif
#include "index.hxx"
#include "names.hxx"
#include "split.hxx"
SG_USING_STD(cout);
static void clip_and_write_poly( string root, long int p_index, AreaType area,
FGBucket b, const FGPolygon& shape ) {
Point3D c, min, max, p;
c = Point3D( b.get_center_lon(), b.get_center_lat(), 0 );
double span = bucket_span( c.y() );
FGPolygon base, result;
char tile_name[256], poly_index[256];
// calculate bucket dimensions
if ( (c.y() >= -89.0) && (c.y() < 89.0) ) {
min.setx( c.x() - span / 2.0 );
max.setx( c.x() + span / 2.0 );
min.sety( c.y() - FG_HALF_BUCKET_SPAN );
max.sety( c.y() + FG_HALF_BUCKET_SPAN );
} else if ( c.y() < -89.0) {
min.setx( -90.0 );
max.setx( -89.0 );
min.sety( -180.0 );
max.sety( 180.0 );
} else if ( c.y() >= 89.0) {
min.setx( 89.0 );
max.setx( 90.0 );
min.sety( -180.0 );
max.sety( 180.0 );
} else {
SG_LOG ( SG_GENERAL, SG_ALERT,
"Out of range latitude in clip_and_write_poly() = " << c.y() );
}
SG_LOG( SG_GENERAL, SG_DEBUG, " (" << min << ") (" << max << ")" );
// set up clipping tile
base.add_node( 0, Point3D(min.x(), min.y(), 0) );
base.add_node( 0, Point3D(max.x(), min.y(), 0) );
base.add_node( 0, Point3D(max.x(), max.y(), 0) );
base.add_node( 0, Point3D(min.x(), max.y(), 0) );
// SG_LOG( SG_GENERAL, SG_DEBUG, "base = 4 vertices" );
/*
FILE *bfp= fopen("base", "w");
gpc_write_polygon(bfp, &base);
fclose(bfp);
*/
result = polygon_int( base, shape );
if ( result.contours() > 0 ) {
long int t_index = b.gen_index();
string path = root + "/" + b.gen_base_path();
#ifdef _MSC_VER
fg_mkdir( path.c_str() );
#else
string command = "mkdir -p " + path;
system( command.c_str() );
#endif
sprintf( tile_name, "%ld", t_index );
string polyfile = path + "/" + tile_name;
sprintf( poly_index, "%ld", p_index );
polyfile += ".";
polyfile += poly_index;
string poly_type = get_area_name( area );
if ( poly_type == "Unknown" )
throw sg_exception("unknown area type in clip_and_write_poly()!");
FILE *rfp= fopen( polyfile.c_str(), "w" );
fprintf( rfp, "%s\n", poly_type.c_str() );
fprintf( rfp, "%d\n", result.contours() );
for ( int i = 0; i < result.contours(); ++i ) {
fprintf( rfp, "%d\n", result.contour_size(i) );
fprintf( rfp, "%d\n", result.get_hole_flag(i) );
for ( int j = 0; j < result.contour_size(i); ++j ) {
p = result.get_pt( i, j );
fprintf( rfp, "%.15f %.15f\n", p.x(), p.y() );
}
}
fclose( rfp );
}
}
// process shape (write polygon to all intersecting tiles)
void split_polygon(const string& path, AreaType area, const FGPolygon& shape) {
Point3D min, max, p;
// point2d min, max;
long int index;
int i, j;
// bail out immediately if polygon is empty
if ( shape.contours() == 0 ) {
return;
}
min = Point3D( 200.0 );
max = Point3D( -200.0 );
// find min/max of polygon
for ( i = 0; i < shape.contours(); i++ ) {
for ( j = 0; j < shape.contour_size(i); j++ ) {
p = shape.get_pt( i, j );
if ( p.x() < min.x() ) { min.setx( p.x() ); }
if ( p.y() < min.y() ) { min.sety( p.y() ); }
if ( p.x() > max.x() ) { max.setx( p.x() ); }
if ( p.y() > max.y() ) { max.sety( p.y() ); }
}
}
// get next polygon index
index = poly_index_next();
SG_LOG( SG_GENERAL, SG_INFO, " min = " << min << " max = " << max );
// find buckets for min, and max points of convex hull.
// note to self: self, you should think about checking for
// polygons that span the date line
FGBucket b_min( min.x(), min.y() );
FGBucket b_max( max.x(), max.y() );
SG_LOG( SG_GENERAL, SG_INFO, " Bucket min = " << b_min );
SG_LOG( SG_GENERAL, SG_INFO, " Bucket max = " << b_max );
if ( b_min == b_max ) {
clip_and_write_poly( path, index, area, b_min, shape );
} else {
FGBucket b_cur;
int dx, dy, i, j;
fgBucketDiff(b_min, b_max, &dx, &dy);
SG_LOG( SG_GENERAL, SG_INFO,
" polygon spans tile boundaries" );
SG_LOG( SG_GENERAL, SG_INFO, " dx = " << dx
<< " dy = " << dy );
if ( (dx > 2880) || (dy > 1440) )
throw sg_exception("something is really wrong in split_polygon()!!!!");
for ( j = 0; j <= dy; j++ ) {
// for performance reasons, we'll clip out just this
// horizontal row, and clip all the tiles in this row
// against the smaller shape
SG_LOG ( SG_GENERAL, SG_INFO,
"Generating clip row " << j << " of " << dy );
FGBucket b_clip = fgBucketOffset(min.x(), min.y(), 0, j);
FGPolygon row, clip_row;
Point3D c, clip_max, clip_min;
c = Point3D( b_clip.get_center_lon(), b_clip.get_center_lat(), 0 );
row.erase();
clip_row.erase();
// calculate bucket clip_min.y and clip_max.y
if ( (c.y() >= -89.0) && (c.y() < 89.0) ) {
clip_min.sety( c.y() - FG_HALF_BUCKET_SPAN );
clip_max.sety( c.y() + FG_HALF_BUCKET_SPAN );
} else if ( c.y() < -89.0) {
clip_min.sety( -90.0 );
clip_max.sety( -89.0 );
} else if ( c.y() >= 89.0) {
clip_min.sety( 89.0 );
clip_max.sety( 90.0 );
} else {
SG_LOG ( SG_GENERAL, SG_ALERT,
"Out of range latitude in clip_and_write_poly() = "
<< c.y() );
}
clip_min.setx( -180.0 );
clip_max.setx( 180.0 );
// set up clipping tile
row.add_node( 0, Point3D(clip_min.x(), clip_min.y(), 0) );
row.add_node( 0, Point3D(clip_max.x(), clip_min.y(), 0) );
row.add_node( 0, Point3D(clip_max.x(), clip_max.y(), 0) );
row.add_node( 0, Point3D(clip_min.x(), clip_max.y(), 0) );
clip_row = polygon_int( row, shape );
/* FILE *sfp = fopen("shape", "w");
gpc_write_polygon(sfp, 0, shape);
fclose(sfp);
sfp = fopen("clip_row", "w");
gpc_write_polygon(sfp, 0, &clip_row);
fclose(sfp); */
for ( i = 0; i <= dx; i++ ) {
b_cur = fgBucketOffset(min.x(), min.y(), i, j);
clip_and_write_poly( path, index, area, b_cur, clip_row );
}
cout << " (done)" << endl;
}
// string answer; cin >> answer;
}
}

View file

@ -30,7 +30,8 @@
// process shape (write polygon to all intersecting tiles)
void split_polygon(const string& path, AreaType area, const TGPolygon& shape);
void tgSplitPolygon( const string& path, AreaType area,
const TGPolygon& shape, bool preserve3d );
#endif // _SPLIT_HXX

View file

@ -164,7 +164,7 @@ static void
processPoints (const E00 &data, const tg::Rectangle &bounds,
AreaType areaType, const string &workDir, int width)
{
double x, y, az;
// double x, y, az;
int nPoints = data.nPoints();
cout << "Processing " << nPoints << " points" << endl;
@ -179,7 +179,7 @@ processPoints (const E00 &data, const tg::Rectangle &bounds,
}
tg::makePolygon(p, width, shape);
split_polygon(workDir, areaType, shape);
tgSplitPolygon(workDir, areaType, shape, false);
}
}
@ -214,7 +214,7 @@ processLines (const E00 &data, const tg::Rectangle &bounds,
// present.
if (aat_list.size() > 0) {
bool status = false;
for (int j = 0; j < aat_list.size(); j++) {
for (unsigned int j = 0; j < aat_list.size(); j++) {
if (checkAttribute(data, i, aat_list[j])) {
status = true;
break;
@ -242,7 +242,7 @@ processLines (const E00 &data, const tg::Rectangle &bounds,
cout << " Minimum angle: "
<< (shape.minangle_contour(0) * SGD_RADIANS_TO_DEGREES) << endl;
split_polygon(workDir, areaType, shape);
tgSplitPolygon(workDir, areaType, shape, false);
}
cout << "Done lines" << endl;
}
@ -266,7 +266,7 @@ processPolygons (const E00 &data, const tg::Rectangle &bounds,
// provided.
if (pat_list.size() > 0) {
bool status = false;
for (int j = 0; j < pat_list.size(); j++) {
for (unsigned int j = 0; j < pat_list.size(); j++) {
if (checkAttribute(data, i, pat_list[j])) {
status = true;
break;
@ -309,7 +309,7 @@ processPolygons (const E00 &data, const tg::Rectangle &bounds,
0.0));
}
}
split_polygon(workDir, areaType, shape);
tgSplitPolygon(workDir, areaType, shape, false);
}
}
@ -530,14 +530,14 @@ main (int argc, const char **argv)
cout << "Ignoring polygon coverage" << endl;
if (useLines && aat_list.size() > 0) {
cout << "Lines must match at least one of the following:" << endl;
for (int i = 0; i < aat_list.size(); i++) {
for (unsigned int i = 0; i < aat_list.size(); i++) {
cout << aat_list[i].file << ' ' << aat_list[i].item << ' '
<< aat_list[i].value << endl;
}
}
if (usePolygons && pat_list.size() > 0) {
cout << "Polygons must match at least one of the following:" << endl;
for (int i = 0; i < pat_list.size(); i++) {
for (unsigned int i = 0; i < pat_list.size(); i++) {
cout << pat_list[i].file << ' ' << pat_list[i].item << ' '
<< pat_list[i].value << endl;
}

View file

@ -83,13 +83,13 @@ void split_and_shift_chunk( const string& path, AreaType area,
upper_shape.shift( -360, 0 );
SG_LOG ( SG_GENERAL, SG_INFO, "Processing lower shape" );
split_polygon(path, area, lower_shape);
tgSplitPolygon(path, area, lower_shape, false);
SG_LOG ( SG_GENERAL, SG_INFO, "Processing center shape" );
split_polygon(path, area, center_shape);
tgSplitPolygon(path, area, center_shape, false);
SG_LOG ( SG_GENERAL, SG_INFO, "Processing upper shape" );
split_polygon(path, area, upper_shape);
tgSplitPolygon(path, area, upper_shape, false);
}

View file

@ -326,7 +326,7 @@ int main( int argc, char **argv ) {
poly_index_init( counter_file );
string holepath = root + "/PhotoArea";
split_polygon( holepath, HoleArea, hole );
tgSplitPolygon( holepath, HoleArea, hole, false );
return 0;
}

View file

@ -417,7 +417,7 @@ int main( int argc, char **argv ) {
// holes are preserved
area = get_area_type( force_area_type );
split_polygon(work_dir, area, shape);
tgSplitPolygon(work_dir, area, shape, false);
} else if ( area == OceanArea ) {
// interior of polygon is ocean, holes are islands
@ -425,7 +425,7 @@ int main( int argc, char **argv ) {
// Ocean data now comes from GSHHS so we want to ignore
// all other ocean data
// split_polygon(work_dir, area, shape);
// tgSplitPolygon(work_dir, area, shape, false);
} else if ( area == VoidArea ) {
// interior is ????
@ -437,7 +437,7 @@ int main( int argc, char **argv ) {
// exit(-1);
}
// split_polygon(work_dir, area, shape);
// tgSplitPolygon(work_dir, area, shape, false);
} else if ( area == NullArea ) {
// interior is ????
@ -449,9 +449,9 @@ int main( int argc, char **argv ) {
// exit(-1);
}
// split_polygon(work_dir, area, shape);
// tgSplitPolygon(work_dir, area, shape, false);
} else {
split_polygon(work_dir, area, shape);
tgSplitPolygon(work_dir, area, shape, false);
}
}

View file

@ -299,7 +299,7 @@ int main( int argc, char **argv ) {
// holes are preserved
area = get_area_type( force_area_type );
split_polygon(work_dir, area, shape);
tgSplitPolygon(work_dir, area, shape, false);
} else if ( area == OceanArea ) {
// interior of polygon is ocean, holes are islands
@ -307,7 +307,7 @@ int main( int argc, char **argv ) {
// Ocean data now comes from GSHHS so we want to ignore
// all other ocean data
// split_polygon(work_dir, area, shape);
// tgSplitPolygon(work_dir, area, shape, false);
} else if ( area == VoidArea ) {
// interior is ????
@ -319,7 +319,7 @@ int main( int argc, char **argv ) {
// exit(-1);
}
// split_polygon(work_dir, area, shape);
// tgSplitPolygon(work_dir, area, shape, false);
} else if ( area == NullArea ) {
// interior is ????
@ -331,9 +331,9 @@ int main( int argc, char **argv ) {
// exit(-1);
}
// split_polygon(work_dir, area, shape);
// tgSplitPolygon(work_dir, area, shape, false);
} else {
split_polygon(work_dir, area, shape);
tgSplitPolygon(work_dir, area, shape, false);
}
}

View file

@ -417,7 +417,7 @@ main (int argc, const char **argv)
cout << "Material type: " << get_area_name(material_type) << endl;
if (width > -1)
cout << "Point and line width: " << width << endl;
for (int x = 0; x < attributes.size(); x++) {
for (unsigned int x = 0; x < attributes.size(); x++) {
cout << "Attribute " << attributes[x].name
<< (attributes[x].state ? " = " : " != ")
<< attributes[x].value << endl;
@ -519,7 +519,7 @@ main (int argc, const char **argv)
if (shape.total_size() >= 3) {
cout << "Polygon with " << shape.total_size() << " points in "
<< shape.contours() << " contour(s)" << endl;
split_polygon(work_dir, material_type, shape);
tgSplitPolygon(work_dir, material_type, shape, false);
}
}
}
@ -532,7 +532,7 @@ main (int argc, const char **argv)
if (mask.total_size() >= 3) {
cout << "Inverse polygon with " << mask.total_size() << " points in "
<< mask.contours() << " contour(s)" << endl;
split_polygon(work_dir, material_type, mask);
tgSplitPolygon(work_dir, material_type, mask, false);
} else {
cout << "Inverse polygon is empty" << endl;
}

View file

@ -65,7 +65,7 @@ add_point (SGPropertyNode_ptr node)
TGPolygon poly;
tg::makePolygon(p, node->getIntValue("width", 500), poly);
poly = polygon_int(poly, bounds_poly);
split_polygon(".", material, poly);
tgSplitPolygon(".", material, poly, false);
}
static void
@ -86,7 +86,7 @@ add_line (SGPropertyNode_ptr node)
TGPolygon poly;
tg::makePolygon(line, node->getIntValue("width", 10), poly);
poly = polygon_int(poly, bounds_poly);
split_polygon(".", material, poly);
tgSplitPolygon(".", material, poly, false);
}
static void
@ -108,7 +108,7 @@ add_polygon (SGPropertyNode_ptr node)
poly.set_hole_flag(i, contour_node->getBoolValue("hole", false));
}
poly = polygon_int(poly, bounds_poly);
split_polygon(".", material, poly);
tgSplitPolygon(".", material, poly, false);
}
void