diff --git a/src/Airports/GenAirports850/airport.cxx b/src/Airports/GenAirports850/airport.cxx index 81d3724b..231ad337 100644 --- a/src/Airports/GenAirports850/airport.cxx +++ b/src/Airports/GenAirports850/airport.cxx @@ -359,7 +359,10 @@ static TGPolygon calc_elevations( TGAptSurface &surf, void Airport::BuildBtg(const string& root, const string_list& elev_src ) { ClipPolyType accum; + + // try to keep line accumulator in clipper format for speed... ClipPolyType line_accum; + TGPolygon apt_base; TGPolygon apt_clearing; @@ -432,9 +435,6 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) for ( unsigned int i=0; iGetDescription() ); - - // cut the linear feature in until we get the geometry right... - // features[i]->BuildBtg( altitude, &line_polys, &line_tps, &line_accum ); features[i]->BuildBtg( altitude, &line_polys, &line_tps, &line_accum, &rwy_lights ); } } @@ -472,7 +472,6 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) for ( unsigned int i=0; iBuildBtg( altitude, &rwy_lights ); } } @@ -483,7 +482,6 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) for ( unsigned int i=0; iBuildBtg( altitude, &rwy_polys, &rwy_tps, &rwy_lights, &accum, NULL, NULL ); @@ -502,6 +500,14 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) { SG_LOG(SG_GENERAL, SG_INFO, "Build Pavement " << i << " of " << pavements.size() << " : " << pavements[i]->GetDescription()); +#if 0 + if (i == 30) { + sglog().setLogLevels( SG_GENERAL, SG_BULK ); + } else { + sglog().setLogLevels( SG_GENERAL, SG_INFO ); + } +#endif + if (boundary) { pavements[i]->BuildBtg( altitude, &pvmt_polys, &pvmt_tps, &accum, NULL, NULL ); @@ -535,7 +541,6 @@ 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 ); } @@ -556,7 +561,8 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) // add segments to polygons to remove any possible "T" // intersections - TGTriNodes tmp_nodes; + TGTriNodes tmp_pvmt_nodes; + TGTriNodes tmp_feat_nodes; // build temporary node list from runways... SG_LOG(SG_GENERAL, SG_INFO, "Build Node List " ); @@ -568,8 +574,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) { for ( int j = 0; j < poly.contour_size( i ); ++j ) { - tmp_nodes.unique_add( poly.get_pt(i, j) ); - //tmp_nodes.course_add( poly.get_pt(i, j) ); + tmp_pvmt_nodes.unique_add( poly.get_pt(i, j) ); } } } @@ -582,13 +587,12 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) { for ( int j = 0; j < poly.contour_size( i ); ++j ) { - tmp_nodes.unique_add( poly.get_pt(i, j) ); - //tmp_nodes.course_add( poly.get_pt(i, j) ); + tmp_pvmt_nodes.unique_add( poly.get_pt(i, j) ); } } } - // and linear features + // and linear features ( keep Linear feature nodes seperate) for ( unsigned int k = 0; k < line_polys.size(); ++k ) { TGPolygon poly = line_polys[k].get_poly(); @@ -596,8 +600,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) { for ( int j = 0; j < poly.contour_size( i ); ++j ) { - tmp_nodes.unique_add( poly.get_pt(i, j) ); - //tmp_nodes.course_add( poly.get_pt(i, j) ); + tmp_feat_nodes.unique_add( poly.get_pt(i, j) ); } } } @@ -607,8 +610,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) { for ( int j = 0; j < base_poly.contour_size( i ); ++j ) { - tmp_nodes.unique_add( base_poly.get_pt(i, j) ); - //tmp_nodes.course_add( base_poly.get_pt(i, j) ); + tmp_pvmt_nodes.unique_add( base_poly.get_pt(i, j) ); } } @@ -618,8 +620,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) { for ( int j = 0; j < divided_base.contour_size( i ); ++j ) { - tmp_nodes.unique_add( divided_base.get_pt(i, j) ); - //tmp_nodes.course_add( divided_base.get_pt(i, j) ); + tmp_pvmt_nodes.unique_add( divided_base.get_pt(i, j) ); } } @@ -630,9 +631,8 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) for ( unsigned int k = 0; k < rwy_polys.size(); ++k ) { TGPolygon poly = rwy_polys[k].get_poly(); - poly = add_nodes_to_poly( poly, tmp_nodes ); + poly = add_nodes_to_poly( poly, tmp_pvmt_nodes ); SG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.total_size()); - rwy_polys[k].set_poly( poly ); } @@ -640,9 +640,8 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) for ( unsigned int k = 0; k < pvmt_polys.size(); ++k ) { TGPolygon poly = pvmt_polys[k].get_poly(); - poly = add_nodes_to_poly( poly, tmp_nodes ); + poly = add_nodes_to_poly( poly, tmp_pvmt_nodes ); SG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.total_size()); - pvmt_polys[k].set_poly( poly ); } @@ -650,9 +649,8 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) for ( unsigned int k = 0; k < line_polys.size(); ++k ) { TGPolygon poly = line_polys[k].get_poly(); - poly = add_nodes_to_poly( poly, tmp_nodes ); + poly = add_nodes_to_poly( poly, tmp_feat_nodes ); SG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.total_size()); - line_polys[k].set_poly( poly ); } @@ -707,9 +705,10 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) SG_LOG(SG_GENERAL, SG_DEBUG, "add nodes base "); SG_LOG(SG_GENERAL, SG_DEBUG, " before: " << base_poly); - SG_LOG(SG_GENERAL, SG_DEBUG, " tmp_nodes size = " << tmp_nodes.get_node_list().size()); + SG_LOG(SG_GENERAL, SG_DEBUG, " tmp_pvmt_nodes size = " << tmp_pvmt_nodes.get_node_list().size()); + SG_LOG(SG_GENERAL, SG_DEBUG, " tmp_feat_nodes size = " << tmp_feat_nodes.get_node_list().size()); - base_poly = add_nodes_to_poly( base_poly, tmp_nodes ); + base_poly = add_nodes_to_poly( base_poly, tmp_pvmt_nodes ); SG_LOG(SG_GENERAL, SG_DEBUG, " after adding tmp_nodes: " << base_poly); base_poly = remove_cycles( base_poly ); @@ -755,9 +754,11 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) TGPolygon poly = pvmt_polys[i].get_poly(); #if 0 - if ( i == 62 ) { + if ( i == 0 ) { tgChopNormalPolygon( "/home/pete", "Base", poly, false ); verbose_triangulation = true; + } else { + verbose_triangulation = false; } #endif @@ -787,9 +788,11 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) TGPolygon poly = line_polys[i].get_poly(); #if 0 - if ( i == 62 ) { + if ( i == 4558 ) { tgChopNormalPolygon( "/home/pete", "Base", poly, false ); verbose_triangulation = true; + } else { + verbose_triangulation = false; } #endif @@ -813,6 +816,8 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) 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"); + gettimeofday(&triangulation_end, NULL); timersub(&triangulation_end, &triangulation_start, &triangulation_time); @@ -868,6 +873,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) SG_LOG(SG_GENERAL, SG_DEBUG, "found normal for this airport = " << tmp); + SG_LOG(SG_GENERAL, SG_INFO, "Adding runway nodes and normals"); for ( unsigned int k = 0; k < rwy_polys.size(); ++k ) { SG_LOG(SG_GENERAL, SG_DEBUG, "tri " << k); @@ -886,9 +892,8 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) for ( int j = 0; j < tri_poly.contour_size(i); ++j ) { p = tri_poly.get_pt( i, j ); - SG_LOG(SG_GENERAL, SG_DEBUG, "adding runway point = " << p); - index = nodes.unique_add( p ); + SG_LOG(SG_GENERAL, SG_DEBUG, "added rwy point " << p << " at " << index ); tri_v.push_back( index ); // use 'the' normal @@ -906,6 +911,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) } } + SG_LOG(SG_GENERAL, SG_INFO, "Adding pavement nodes and normals"); for ( unsigned int k = 0; k < pvmt_polys.size(); ++k ) { SG_LOG(SG_GENERAL, SG_DEBUG, "tri " << k); @@ -925,6 +931,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) { p = tri_poly.get_pt( i, j ); index = nodes.unique_add( p ); + SG_LOG(SG_GENERAL, SG_DEBUG, "added pvmnt point " << p << " at " << index ); tri_v.push_back( index ); // use 'the' normal @@ -942,6 +949,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) } } + SG_LOG(SG_GENERAL, SG_INFO, "Adding line nodes and normals"); for ( unsigned int k = 0; k < line_polys.size(); ++k ) { SG_LOG(SG_GENERAL, SG_DEBUG, "tri " << k); @@ -961,6 +969,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) { p = tri_poly.get_pt( i, j ); index = nodes.unique_add( p ); + SG_LOG(SG_GENERAL, SG_DEBUG, "added line point " << p << " at " << index ); tri_v.push_back( index ); // use 'the' normal @@ -982,6 +991,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) std::vector< SGVec2f > base_txs; int_list base_tc; + SG_LOG(SG_GENERAL, SG_INFO, "Adding base nodes and normals"); for ( int i = 0; i < base_tris.contours(); ++i ) { tri_v.clear(); @@ -990,9 +1000,8 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) for ( int j = 0; j < base_tris.contour_size(i); ++j ) { p = base_tris.get_pt( i, j ); - - SG_LOG(SG_GENERAL, SG_DEBUG, "adding base point " << p); index = nodes.unique_add( p ); + SG_LOG(SG_GENERAL, SG_DEBUG, "added base point " << p << " at " << index ); tri_v.push_back( index ); index = normals.unique_add( vn ); @@ -1025,14 +1034,12 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) // on rare occasion, one or more of the divided base points can be // missed. Make sure they are all in the node list so we can // build a proper skirt. - for ( int i = 0; i < divided_base.contours(); ++i ) { for ( int j = 0; j < divided_base.contour_size( i ); ++j ) { - SG_LOG(SG_GENERAL, SG_DEBUG, "adding divided base point " << p); - - nodes.unique_add( divided_base.get_pt(i, j) ); + index = nodes.unique_add( divided_base.get_pt(i, j) ); + SG_LOG(SG_GENERAL, SG_DEBUG, "added base point " << divided_base.get_pt(i, j) << " at " << index ); } } @@ -1044,7 +1051,6 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) // airport is located in a bowl or on a hill. SG_LOG(SG_GENERAL, SG_DEBUG, " calcaverage elevation"); - { point_list dbg = nodes.get_node_list(); @@ -1057,14 +1063,13 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) } double average = tgAverageElevation( root, elev_src, nodes.get_node_list() ); - SG_LOG(SG_GENERAL, SG_DEBUG, " done"); // cout << "average airport elevation = " << average << endl; // Now build the fitted airport surface ... // calculation min/max coordinates of airport area - SG_LOG(SG_GENERAL, SG_DEBUG, " calculation min/max coordinates of airport area"); + SG_LOG(SG_GENERAL, SG_INFO, " calculation min/max coordinates of airport area"); Point3D min_deg(9999.0, 9999.0, 0), max_deg(-9999.0, -9999.0, 0); for ( unsigned int j = 0; j < nodes.get_node_list().size(); ++j ) @@ -1072,27 +1077,32 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) Point3D p = nodes.get_node_list()[j]; if ( p.lon() < min_deg.lon() ) { + SG_LOG(SG_GENERAL, SG_DEBUG, "new min lon from node " << j << " is " << p.lon() ); min_deg.setlon( p.lon() ); } if ( p.lon() > max_deg.lon() ) { + SG_LOG(SG_GENERAL, SG_DEBUG, "new max lon from node " << j << " is " << p.lon() ); max_deg.setlon( p.lon() ); } if ( p.lat() < min_deg.lat() ) { + SG_LOG(SG_GENERAL, SG_DEBUG, "new min lat from node " << j << " is " << p.lat() ); min_deg.setlat( p.lat() ); } if ( p.lat() > max_deg.lat() ) { + SG_LOG(SG_GENERAL, SG_DEBUG, "new max lat from node " << j << " is " << p.lat() ); max_deg.setlat( p.lat() ); } } + SG_LOG(SG_GENERAL, SG_INFO, "Before extending for lights: min = " << min_deg << " max = " << max_deg ); + // extend the min/max coordinates of airport area to cover all // lights as well SG_LOG(SG_GENERAL, SG_DEBUG, " extend the min/max coordinates of airport area to cover all lights as well : num rwy lights is " << rwy_lights.size() ); - for ( unsigned int i = 0; i < rwy_lights.size(); ++i ) { SG_LOG(SG_GENERAL, SG_DEBUG, " extend the min/max coordinates of airport area to cover all lights as well : rwy light " << i << "has " << rwy_lights[i].get_poly().get_contour(0).size() << " lights " ); @@ -1128,7 +1138,7 @@ void Airport::BuildBtg(const string& root, const string_list& elev_src ) max_deg.setlon( max_deg.lon() + 0.01 * dlon ); min_deg.setlat( min_deg.lat() - 0.01 * dlat ); max_deg.setlat( max_deg.lat() + 0.01 * dlat ); - SG_LOG(SG_GENERAL, SG_DEBUG, "min = " << min_deg << " max = " << max_deg ); + SG_LOG(SG_GENERAL, SG_INFO, "min = " << min_deg << " max = " << max_deg ); TGAptSurface apt_surf( root, elev_src, min_deg, max_deg, average ); SG_LOG(SG_GENERAL, SG_DEBUG, "Airport surface created"); diff --git a/src/Airports/GenAirports850/beznode.hxx b/src/Airports/GenAirports850/beznode.hxx index b5f2b010..257c9972 100644 --- a/src/Airports/GenAirports850/beznode.hxx +++ b/src/Airports/GenAirports850/beznode.hxx @@ -88,34 +88,42 @@ class BezNode public: BezNode( Point3D l ) : prev_cp(0.0f, 0.0f, 0.0f), next_cp(0.0f, 0.0f, 0.0f) { - loc = l; - mark = 0; + loc = l; + mark = 0; light = 0; + term = false; + close = false; } BezNode( double lat, double lon ) : prev_cp(0.0f, 0.0f, 0.0f), next_cp(0.0f, 0.0f, 0.0f) { - loc = Point3D( lon, lat, 0.0f ); - mark = 0; + loc = Point3D( lon, lat, 0.0f ); + mark = 0; light = 0; + term = false; + close = false; } BezNode( Point3D l, Point3D cp ) { - loc = l; + loc = l; next_cp = cp; prev_cp = Mirror(cp); - mark = 0; - light = 0; + mark = 0; + light = 0; + term = false; + close = false; } BezNode( double lat, double lon, double cp_lat, double cp_lon ) { - loc = Point3D( lon, lat, 0.0f ); + loc = Point3D( lon, lat, 0.0f ); next_cp = Point3D( cp_lon, cp_lat, 0.0f ); prev_cp = Mirror( next_cp ); - mark = 0; - light = 0; + mark = 0; + light = 0; + term = false; + close = false; } Point3D Mirror( Point3D pt ) @@ -144,6 +152,16 @@ public: return light; } + void SetClose( bool c ) + { + close = c; + } + + void SetTerm( bool t ) + { + term = t; + } + bool IsAt( double lat, double lon ) { return ( (loc.lat() == lat) && (loc.lon() == lon) ); @@ -199,6 +217,8 @@ private: Point3D next_cp; unsigned int mark; unsigned int light; + bool term; + bool close; }; diff --git a/src/Airports/GenAirports850/closedpoly.cxx b/src/Airports/GenAirports850/closedpoly.cxx index 5eabb8fa..8763df26 100644 --- a/src/Airports/GenAirports850/closedpoly.cxx +++ b/src/Airports/GenAirports850/closedpoly.cxx @@ -132,7 +132,7 @@ void ClosedPoly::CloseCurContour() if (cur_feature) { SG_LOG(SG_GENERAL, SG_DEBUG, "We still have an active linear feature - add the first node to close it"); - cur_feature->Finish(); + cur_feature->Finish(true); features.push_back(cur_feature); cur_feature = NULL; @@ -535,6 +535,7 @@ 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 = remove_bad_contours( pre_tess ); pre_tess = remove_dups( pre_tess ); pre_tess = reduce_degeneracy( pre_tess ); @@ -545,15 +546,36 @@ int ClosedPoly::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list // SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: contour " << c << " pt " << pt << ": (" << pre_tess.get_pt(c, pt).x() << "," << pre_tess.get_pt(c, pt).y() << ")" ); // } //} + + //SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: original poly has " << pre_tess.contours() << " contours"); + //for (int i=0; ipush_back( sp ); SG_LOG(SG_GENERAL, SG_DEBUG, "clipped = " << clipped.contours()); +#if 1 + *accum = tgPolygonUnionClipper( pre_tess, *accum ); +#else *accum = tgPolygonUnion( pre_tess, *accum ); +#endif tp = TGTexParams( pre_tess.get_pt(0,0), 5.0, 5.0, texture_heading ); texparams->push_back( tp ); @@ -581,10 +607,10 @@ int ClosedPoly::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list safe_base = tgPolygonExpand( pre_tess, 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 ); } } } diff --git a/src/Airports/GenAirports850/helipad.cxx b/src/Airports/GenAirports850/helipad.cxx index 8fbd9812..1489fec0 100644 --- a/src/Airports/GenAirports850/helipad.cxx +++ b/src/Airports/GenAirports850/helipad.cxx @@ -32,7 +32,7 @@ Helipad::Helipad(char* definition) // int fscanf(FILE *stream, const char *format, ...); sscanf(definition, "%s %lf %lf %lf %lf %lf %d %d %d %lf %d", - &heli.designator, &heli.lat, &heli.lon, &heli.heading, &heli.length, &heli.width, &heli.surface, + heli.designator, &heli.lat, &heli.lon, &heli.heading, &heli.length, &heli.width, &heli.surface, &heli.marking, &heli.shoulder, &heli.smoothness, &heli.edge_lights); SG_LOG(SG_GENERAL, SG_DEBUG, "Read helipad: (" << heli.lon << "," << heli.lat << ") heading: " << heli.heading << " length: " << heli.length << " width: " << heli.width ); @@ -131,7 +131,7 @@ void Helipad::BuildBtg( float alt_m, // Now generate the helipad lights superpoly_list s = gen_helipad_lights(); - for ( int i = 0; i < s.size(); ++i ) { - rwy_lights->push_back( s[i] ); - } + for ( unsigned int i = 0; i < s.size(); ++i ) { + rwy_lights->push_back( s[i] ); + } } diff --git a/src/Airports/GenAirports850/linearfeature.cxx b/src/Airports/GenAirports850/linearfeature.cxx index 98131a6e..fa987bb2 100644 --- a/src/Airports/GenAirports850/linearfeature.cxx +++ b/src/Airports/GenAirports850/linearfeature.cxx @@ -10,7 +10,7 @@ #include "linearfeature.hxx" #include "math.h" -void LinearFeature::ConvertContour( BezContour* src ) +void LinearFeature::ConvertContour( BezContour* src, bool closed ) { BezNode* prevNode; BezNode* curNode; @@ -243,9 +243,9 @@ void LinearFeature::ConvertContour( BezContour* src ) Point3D destLoc = nextNode->GetLoc(); geo_inverse_wgs_84( curLoc.y(), curLoc.x(), destLoc.y(), destLoc.x(), &az1, &az2, &dist); - if (dist > 10.0) + if (dist > 100.0) { - int num_segs = (dist / 10.0f) + 1; + int num_segs = (dist / 100.0f) + 1; for (int p=0; pGetLoc(); + // just add the one vertex - dist is small points.push_back( curLoc ); SG_LOG(SG_GENERAL, SG_DEBUG, "adding Linear Anchor node at (" << curLoc.x() << "," << curLoc.y() << ")"); + + prevLoc = curLoc; + curLoc = nextLoc; + + SG_LOG(SG_GENERAL, SG_DEBUG, "Set prevLoc = (" << prevLoc.x() << "," << prevLoc.y() << ") and curLoc = (" << curLoc.x() << "," << curLoc.y() << ")" ); } } } + // TEST TEST TEST : This should do it +#if 1 + if (closed) + { + SG_LOG(SG_GENERAL, SG_DEBUG, "Closed COntour : adding last node at (" << curLoc.x() << "," << curLoc.y() << ")"); + + // need to add the markings for last segment + points.push_back( curLoc ); + } +#endif + // TEST TEST TEST + // check for marking that goes all the way to the end... - if (cur_mark) - { + if (cur_mark) + { SG_LOG(SG_GENERAL, SG_DEBUG, "LinearFeature::ConvertContour Marking from " << cur_mark->start_idx << " with type " << cur_mark->type << " ends at the end of the contour: " << points.size() ); cur_mark->end_idx = points.size()-1; @@ -290,8 +311,8 @@ void LinearFeature::ConvertContour( BezContour* src ) } // check for lighting that goes all the way to the end... - if (cur_light) - { + if (cur_light) + { SG_LOG(SG_GENERAL, SG_DEBUG, "LinearFeature::ConvertContour Lighting from " << cur_light->start_idx << " with type " << cur_light->type << " ends at the end of the contour: " << points.size() ); cur_light->end_idx = points.size()-1; @@ -477,7 +498,7 @@ Point3D midpoint( Point3D p0, Point3D p1 ) return Point3D( (p0.x() + p1.x()) / 2, (p0.y() + p1.y()) / 2, (p0.z() + p1.z()) / 2 ); } -int LinearFeature::Finish() +int LinearFeature::Finish( bool closed ) { TGPolygon poly; TGPolygon normals_poly; @@ -499,7 +520,7 @@ int LinearFeature::Finish() // create the inner and outer boundaries to generate polys // this generates 2 point lists for the contours, and remembers // the start stop points for markings and lights - ConvertContour( &contour ); + ConvertContour( &contour, closed ); // now generate the supoerpoly and texparams lists for markings for (unsigned int i=0; ipush_back( marking_polys[i] ); +#if 1 + *line_accum = tgPolygonUnion( poly, *line_accum ); +#else *line_accum = tgPolygonUnionClipper( poly, *line_accum ); +#endif + line_tps->push_back( marking_tps[i] ); } diff --git a/src/Airports/GenAirports850/linearfeature.hxx b/src/Airports/GenAirports850/linearfeature.hxx index 908447ce..20b3ebb2 100644 --- a/src/Airports/GenAirports850/linearfeature.hxx +++ b/src/Airports/GenAirports850/linearfeature.hxx @@ -91,8 +91,9 @@ public: contour.push_back( b ); } - int Finish(); + int Finish( bool closed ); int BuildBtg( float alt_m, superpoly_list* line_polys, texparams_list* line_tps, ClipPolyType* line_accum, superpoly_list* lights ); + int BuildBtg( float alt_m, superpoly_list* line_polys, texparams_list* line_tps, Polygons* line_accum, superpoly_list* lights ); private: Point3D OffsetPointFirst( Point3D *cur, Point3D *next, double offset_by ); @@ -108,7 +109,7 @@ private: LightingList lights; Lighting* cur_light; - void ConvertContour( BezContour* src ); + void ConvertContour( BezContour* src, bool closed ); // text description string description; diff --git a/src/Airports/GenAirports850/linked_objects.cxx b/src/Airports/GenAirports850/linked_objects.cxx index aba91aa2..121c2315 100644 --- a/src/Airports/GenAirports850/linked_objects.cxx +++ b/src/Airports/GenAirports850/linked_objects.cxx @@ -19,10 +19,15 @@ Beacon::Beacon( char* definition ) Sign::Sign( char* definition ) { char sgdef[256]; + double def_heading; - sscanf(definition, "%lf %lf %lf %d %d %s", &lat, &lon, &heading, &reserved, &size, sgdef ); + sscanf(definition, "%lf %lf %lf %d %d %s", &lat, &lon, &def_heading, &reserved, &size, sgdef ); - SG_LOG(SG_GENERAL, SG_DEBUG, "Read Sign: (" << lon << "," << lat << ") heading " << heading << " size " << size << " definition: " << sgdef ); + // 850 format sign heading is the heading which points away from the visible numbers + // Flightgear wants the heading to be the heading in which the sign is read + heading = -def_heading + 360.0; + + SG_LOG(SG_GENERAL, SG_DEBUG, "Read Sign: (" << lon << "," << lat << ") heading " << def_heading << " size " << size << " definition: " << sgdef << " calc view heading: " << heading ); sgn_def = sgdef; } diff --git a/src/Airports/GenAirports850/parser.cxx b/src/Airports/GenAirports850/parser.cxx index fac47383..8841324f 100644 --- a/src/Airports/GenAirports850/parser.cxx +++ b/src/Airports/GenAirports850/parser.cxx @@ -309,7 +309,6 @@ void Parser::AddAirports( long start_pos, float min_lat, float min_lon, float ma void Parser::Parse() { char tmp[2048]; - int i; struct timeval parse_start; struct timeval parse_end; struct timeval parse_time; @@ -325,7 +324,7 @@ void Parser::Parse() } // for each position in parse_positions, parse an airport - for (i=0; iSetTerm( term ); + curNode->SetClose( close ); + return curNode; } @@ -533,7 +535,7 @@ ClosedPoly* Parser::ParsePavement( char* line ) char *d = NULL; int numParams; - numParams = sscanf(line, "%d %f %f %ls", &st, &s, &th, desc); + numParams = sscanf(line, "%d %f %f %s", &st, &s, &th, desc); if (numParams == 4) { @@ -557,7 +559,7 @@ ClosedPoly* Parser::ParseBoundary( char* line ) char *d = NULL; int numParams; - numParams = sscanf(line, "%ls", desc); + numParams = sscanf(line, "%s", desc); if (numParams == 1) { @@ -595,6 +597,8 @@ int Parser::SetState( int state ) } cur_state = state; + + return cur_state; } // TODO: This should be a loop here, and main should just pass the file name and airport code... @@ -756,7 +760,7 @@ int Parser::ParseLine(char* line) } if (cur_airport) { - cur_feat->Finish(); + cur_feat->Finish( true ); cur_airport->AddFeature( cur_feat ); } cur_feat = NULL; @@ -790,7 +794,7 @@ int Parser::ParseLine(char* line) } if (cur_airport) { - cur_feat->Finish(); + cur_feat->Finish( false ); cur_airport->AddFeature( cur_feat ); } } diff --git a/src/Airports/GenAirports850/runway.cxx b/src/Airports/GenAirports850/runway.cxx index 6cb430fd..662248ac 100644 --- a/src/Airports/GenAirports850/runway.cxx +++ b/src/Airports/GenAirports850/runway.cxx @@ -40,8 +40,8 @@ Runway::Runway(char* definition) // int fscanf(FILE *stream, const char *format, ...); sscanf(definition, "%lf %d %d %lf %d %d %d %s %lf %lf %lf %lf %d %d %d %d %s %lf %lf %lf %lf %d %d %d %d", &rwy.width, &rwy.surface, &rwy.shoulder, &rwy.smoothness, &rwy.centerline_lights, &rwy.edge_lights, &rwy.dist_remain_signs, - &rwy.rwnum[0], &rwy.lat[0], &rwy.lon[0], &rwy.threshold[0], &rwy.overrun[0], &rwy.marking[0], &rwy.approach_lights[0], &rwy.tz_lights[0], &rwy.reil[0], - &rwy.rwnum[1], &rwy.lat[1], &rwy.lon[1], &rwy.threshold[1], &rwy.overrun[1], &rwy.marking[1], &rwy.approach_lights[1], &rwy.tz_lights[1], &rwy.reil[1] + rwy.rwnum[0], &rwy.lat[0], &rwy.lon[0], &rwy.threshold[0], &rwy.overrun[0], &rwy.marking[0], &rwy.approach_lights[0], &rwy.tz_lights[0], &rwy.reil[0], + rwy.rwnum[1], &rwy.lat[1], &rwy.lon[1], &rwy.threshold[1], &rwy.overrun[1], &rwy.marking[1], &rwy.approach_lights[1], &rwy.tz_lights[1], &rwy.reil[1] ); // calculate runway heading and length (used a lot) @@ -53,7 +53,7 @@ Runway::Runway(char* definition) WaterRunway::WaterRunway(char* definition) { - sscanf(definition, "%lf %d %s %lf %lf %s %lf %lf", &width, &buoys, &rwnum[0], &lat[0], &lon[0], &rwnum[1], &lat[1], &lon[1]); + sscanf(definition, "%lf %d %s %lf %lf %s %lf %lf", &width, &buoys, rwnum[0], &lat[0], &lon[0], rwnum[1], &lat[1], &lon[1]); SG_LOG(SG_GENERAL, SG_DEBUG, "Read water runway: (" << lon[0] << "," << lat[0] << ") to (" << lon[1] << "," << lat[1] << ") width: " << width << " buoys = " << buoys ); } @@ -175,4 +175,6 @@ int Runway::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* te // and add the clearing to the base *apt_base = tgPolygonUnion( base, *apt_base ); } + + return 0; } diff --git a/src/Airports/GenAirports850/rwy_gen.cxx b/src/Airports/GenAirports850/rwy_gen.cxx index 53b0c1d7..19a6c660 100644 --- a/src/Airports/GenAirports850/rwy_gen.cxx +++ b/src/Airports/GenAirports850/rwy_gen.cxx @@ -372,6 +372,9 @@ void Runway::BuildShoulder( float alt_m, int numSegs = (rwy.length / max_dist) + 1; double dist = rwy.length / (double)numSegs; + //int numSegs = 1; + //double dist = rwy.length; + // Create both shoulder sides for (int i=0; i<2; ++i){ double step; @@ -395,18 +398,25 @@ void Runway::BuildShoulder( float alt_m, for (int j=0; jpush_back( sp ); +#if 1 *accum = tgPolygonUnion( shoulderSegment, *accum ); +#else + *accum = tgPolygonUnionClipper( shoulderSegment, *accum ); +#endif tp = TGTexParams( shoulderSegment.get_pt(0,0), -shoulder_width, dist, rwy.heading ); if (i == 0){ diff --git a/src/Lib/Geometry/point3d.hxx b/src/Lib/Geometry/point3d.hxx index 449af458..b3f316f8 100644 --- a/src/Lib/Geometry/point3d.hxx +++ b/src/Lib/Geometry/point3d.hxx @@ -46,7 +46,8 @@ #include -const double fgPoint3_Epsilon = 0.0000001; +//const double fgPoint3_Epsilon = 0.0000001; +const double fgPoint3_Epsilon = 0.000001; enum {PX, PY, PZ}; // axes diff --git a/src/Lib/Geometry/poly_support.cxx b/src/Lib/Geometry/poly_support.cxx index 09ee9209..8512d676 100644 --- a/src/Lib/Geometry/poly_support.cxx +++ b/src/Lib/Geometry/poly_support.cxx @@ -349,16 +349,12 @@ TGPolygon polygon_tesselate_alt( TGPolygon &p, bool verbose ) { // Bail right away if polygon is empty if ( p.contours() == 0 ) { - return result; + return result; } // 1. Robustly find a point inside each contour that is not // inside any other contour calc_points_inside( p ); - for ( i = 0; i < p.contours(); ++i ) { - //cout << "final point inside =" << p.get_point_inside( i ) - // << endl; - } // 2. Do a final triangulation of the entire polygon triele_list trieles; @@ -366,8 +362,21 @@ TGPolygon polygon_tesselate_alt( TGPolygon &p, bool verbose ) { string flags; if (verbose) { flags = "pzqenXYY"; +// flags = "pzqenXY"; // allow adding interior points } else { flags = "pzqenXYYQ"; +// flags = "pzqenXYQ"; // allow adding interior points + } + + // check the input for nan point + for (int c = 0; c < p.contours(); c++) { + point_list contour = p.get_contour( c ); + for ( int d = 0; d < (int)contour.size(); ++d ) { + if ( isnan( contour[d].x() ) || isnan( contour[d].y() ) ) { + printf("Uh-oh - got nan before tesselation\n"); + exit(0); + } + } } if ( polygon_tesselate( p, extra_nodes, trieles, nodes, flags ) >= 0 ) { diff --git a/src/Lib/Polygon/clipper.cpp b/src/Lib/Polygon/clipper.cpp index da08072a..ae25226e 100644 --- a/src/Lib/Polygon/clipper.cpp +++ b/src/Lib/Polygon/clipper.cpp @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 4.5.5 * -* Date : 6 October 2011 * +* Version : 4.6.5 * +* Date : 17 January 2011 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2011 * +* Copyright : Angus Johnson 2010-2012 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -21,6 +21,14 @@ * Springer; 1 edition (January 4, 2005) * * http://books.google.com/books?q=vatti+clipping+agoston * * * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24–28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * *******************************************************************************/ /******************************************************************************* @@ -39,28 +47,25 @@ #include #include -// TEMP : clipper debugging -#include - - - -//Workaround for older compilers that don't have std::abs -#if (__GNUC__ == 2 && __GNUC_MINOR__ <= 97) || (defined(_MSC_VER) && _MSC_VER <= 1500) -namespace std -{ - long long abs(long long x) { return x < 0 ? -x : x; } -} -#endif - namespace ClipperLib { static long64 const loRange = 1518500249; //sqrt(2^63 -1)/2 static long64 const hiRange = 6521908912666391106LL; //sqrt(2^127 -1)/2 -static double const horizontal = -3.4E+38; static double const pi = 3.141592653589793238; enum Direction { dRightToLeft, dLeftToRight }; enum RangeTest { rtLo, rtHi, rtError }; +#define HORIZONTAL (-1.0E+40) +#define TOLERANCE (1.0e-20) +#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) +#define NEAR_EQUAL(a, b) NEAR_ZERO((a) - (b)) + +inline long64 Abs(long64 val) +{ + if (val < 0) return -val; else return val; +} +//------------------------------------------------------------------------------ + //------------------------------------------------------------------------------ // Int128 class (enables safe math on signed 64bit integers) // eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 @@ -88,7 +93,7 @@ class Int128 long64 operator = (const long64 &val) { hi = 0; - lo = std::abs(val); + lo = Abs(val); if (val < 0) Negate(*this); return val; } @@ -150,20 +155,20 @@ class Int128 Int128 tmp(*this); if (tmp.hi < 0) Negate(tmp); ulong64 int1Hi = ulong64(tmp.lo) >> 32; - ulong64 int1Lo = tmp.lo & 0xFFFFFFFF; + ulong64 int1Lo = ulong64(tmp.lo & 0xFFFFFFFF); tmp = rhs; if (tmp.hi < 0) Negate(tmp); ulong64 int2Hi = ulong64(tmp.lo) >> 32; - ulong64 int2Lo = tmp.lo & 0xFFFFFFFF; + ulong64 int2Lo = ulong64(tmp.lo & 0xFFFFFFFF); //nb: see comments in clipper.pas ulong64 a = int1Hi * int2Hi; ulong64 b = int1Lo * int2Lo; ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; - tmp.hi = a + (c >> 32); - tmp.lo = c << 32; + tmp.hi = long64(a + (c >> 32)); + tmp.lo = long64(c << 32); tmp.lo += long64(b); if (ulong64(tmp.lo) < b) tmp.hi++; if (negate) Negate(tmp); @@ -292,12 +297,6 @@ private: //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -inline long64 Abs(long64 val) -{ - if ((val < 0)) return -val; else return val; -} -//------------------------------------------------------------------------------ - RangeTest TestRange(const Polygon &pts) { RangeTest result = rtLo; @@ -318,12 +317,12 @@ bool Orientation(const Polygon &poly) if (highI < 2) return false; bool UseFullInt64Range = false; - int j = 0, jplus, jminus; - for (int i = 0; i <= highI; ++i) + int j = 0, jplus, jminus; + for (int i = 0; i <= highI; ++i) { - if (std::abs(poly[i].X) > hiRange || std::abs(poly[i].Y) > hiRange) + if (Abs(poly[i].X) > hiRange || Abs(poly[i].Y) > hiRange) throw "Coordinate exceeds range bounds."; - if (std::abs(poly[i].X) > loRange || std::abs(poly[i].Y) > loRange) + if (Abs(poly[i].X) > loRange || Abs(poly[i].Y) > loRange) UseFullInt64Range = true; if (poly[i].Y < poly[j].Y) continue; if ((poly[i].Y > poly[j].Y || poly[i].X < poly[j].X)) j = i; @@ -384,21 +383,6 @@ bool Orientation(OutRec *outRec, bool UseFullInt64Range) } //------------------------------------------------------------------------------ -int PointCount(OutPt *pts) -{ - if (!pts) return 0; - int result = 0; - OutPt *p = pts; - do - { - result++; - p = p->next; - } - while (p != pts); - return result; -} -//------------------------------------------------------------------------------ - inline bool PointsEqual( const IntPoint &pt1, const IntPoint &pt2) { return ( pt1.X == pt2.X && pt1.Y == pt2.Y ); @@ -409,15 +393,16 @@ double Area(const Polygon &poly) { int highI = (int)poly.size() -1; if (highI < 2) return 0; - bool UseFullInt64Range = false; + bool UseFullInt64Range; RangeTest rt = TestRange(poly); switch (rt) { + case rtLo: + UseFullInt64Range = false; + break; case rtHi: UseFullInt64Range = true; break; - case rtLo: - break; - case rtError: + default: throw "Coordinate exceeds range bounds."; } @@ -524,7 +509,7 @@ bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, double GetDx(const IntPoint pt1, const IntPoint pt2) { - if (pt1.Y == pt2.Y) return horizontal; + if (pt1.Y == pt2.Y) return HORIZONTAL; else return (double)(pt2.X - pt1.X) / (double)(pt2.Y - pt1.Y); } @@ -532,7 +517,7 @@ double GetDx(const IntPoint pt1, const IntPoint pt2) void SetDx(TEdge &e) { - if (e.ybot == e.ytop) e.dx = horizontal; + if (e.ybot == e.ytop) e.dx = HORIZONTAL; else e.dx = (double)(e.xtop - e.xbot) / (double)(e.ytop - e.ybot); } @@ -587,10 +572,10 @@ bool IntersectPoint(TEdge &edge1, TEdge &edge2, { double b1, b2; if (SlopesEqual(edge1, edge2, UseFullInt64Range)) return false; - else if (edge1.dx == 0) + else if (NEAR_ZERO(edge1.dx)) { ip.X = edge1.xbot; - if (edge2.dx == horizontal) + if (NEAR_EQUAL(edge2.dx, HORIZONTAL)) { ip.Y = edge2.ybot; } else @@ -599,10 +584,10 @@ bool IntersectPoint(TEdge &edge1, TEdge &edge2, ip.Y = Round(ip.X/edge2.dx + b2); } } - else if (edge2.dx == 0) + else if (NEAR_ZERO(edge2.dx)) { ip.X = edge2.xbot; - if (edge1.dx == horizontal) + if (NEAR_EQUAL(edge1.dx, HORIZONTAL)) { ip.Y = edge1.ybot; } else @@ -816,7 +801,7 @@ bool ClipperBase::AddPolygon( const Polygon &pg, PolyType polyType) long64 maxVal; if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange; - for (int i = 1; i < len; ++i) + for (int i = 0; i < len; ++i) { if (Abs(pg[i].X) > maxVal || Abs(pg[i].Y) > maxVal) { @@ -828,7 +813,7 @@ bool ClipperBase::AddPolygon( const Polygon &pg, PolyType polyType) m_UseFullRange = true; } - if (PointsEqual(p[j], pg[i])) continue; + if (i == 0 || PointsEqual(p[j], pg[i])) continue; else if (j > 0 && SlopesEqual(p[j-1], p[j], pg[i], m_UseFullRange)) { if (PointsEqual(p[j-1], pg[i])) j--; @@ -884,7 +869,7 @@ bool ClipperBase::AddPolygon( const Polygon &pg, PolyType polyType) //make sure eHighest is positioned so the following loop works safely ... if (eHighest->windDelta > 0) eHighest = eHighest->next; - if (eHighest->dx == horizontal) eHighest = eHighest->next; + if (NEAR_EQUAL(eHighest->dx, HORIZONTAL)) eHighest = eHighest->next; //finally insert each local minima ... e = eHighest; @@ -926,7 +911,7 @@ TEdge* ClipperBase::AddBoundsToLML(TEdge *e) e = e->next; for (;;) { - if ( e->dx == horizontal ) + if (NEAR_EQUAL(e->dx, HORIZONTAL)) { //nb: proceed through horizontals when approaching from their right, // but break on horizontal minima if approaching from their left. @@ -945,7 +930,7 @@ TEdge* ClipperBase::AddBoundsToLML(TEdge *e) newLm->next = 0; newLm->Y = e->prev->ybot; - if ( e->dx == horizontal ) //horizontal edges never start a left bound + if ( NEAR_EQUAL(e->dx, HORIZONTAL) ) //horizontal edges never start a left bound { if (e->xbot != e->prev->xbot) SwapX(*e); newLm->leftBound = e->prev; @@ -965,10 +950,10 @@ TEdge* ClipperBase::AddBoundsToLML(TEdge *e) for (;;) { - if ( e->next->ytop == e->ytop && e->next->dx != horizontal ) break; + if ( e->next->ytop == e->ytop && !NEAR_EQUAL(e->next->dx, HORIZONTAL) ) break; e->nextInLML = e->next; e = e->next; - if ( e->dx == horizontal && e->xbot != e->prev->xtop) SwapX(*e); + if ( NEAR_EQUAL(e->dx, HORIZONTAL) && e->xbot != e->prev->xtop) SwapX(*e); } return e->next; } @@ -1097,14 +1082,14 @@ Clipper::Clipper() : ClipperBase() //constructor m_ExecuteLocked = false; m_UseFullRange = false; m_ReverseOutput = false; -}; +} //------------------------------------------------------------------------------ Clipper::~Clipper() //destructor { Clear(); DisposeScanbeamList(); -}; +} //------------------------------------------------------------------------------ void Clipper::Clear() @@ -1112,7 +1097,7 @@ void Clipper::Clear() if (m_edges.size() == 0) return; //avoids problems with ClipperBase destructor DisposeAllPolyPts(); ClipperBase::Clear(); -}; +} //------------------------------------------------------------------------------ void Clipper::DisposeScanbeamList() @@ -1131,6 +1116,7 @@ void Clipper::Reset() m_Scanbeam = 0; m_ActiveEdges = 0; m_SortedEdges = 0; + DisposeAllPolyPts(); LocalMinima* lm = m_MinimaList; while (lm) { @@ -1344,9 +1330,15 @@ void Clipper::SetWindingCount(TEdge &edge) edge.windCnt = edge.windDelta; edge.windCnt2 = 0; e = m_ActiveEdges; //ie get ready to calc windCnt2 - } else if ( IsNonZeroFillType(edge) ) + } else if ( IsEvenOddFillType(edge) ) { - //nonZero filling ... + //EvenOdd filling ... + edge.windCnt = 1; + edge.windCnt2 = e->windCnt2; + e = e->nextInAEL; //ie get ready to calc windCnt2 + } else + { + //nonZero, Positive or Negative filling ... if ( e->windCnt * e->windDelta < 0 ) { if (Abs(e->windCnt) > 1) @@ -1365,74 +1357,127 @@ void Clipper::SetWindingCount(TEdge &edge) } edge.windCnt2 = e->windCnt2; e = e->nextInAEL; //ie get ready to calc windCnt2 - } else - { - //even-odd filling ... - edge.windCnt = 1; - edge.windCnt2 = e->windCnt2; - e = e->nextInAEL; //ie get ready to calc windCnt2 } //update windCnt2 ... - - if ( IsNonZeroAltFillType(edge) ) + if ( IsEvenOddAltFillType(edge) ) { - //nonZero filling ... - while ( e != &edge ) - { - edge.windCnt2 += e->windDelta; - e = e->nextInAEL; - } - } else - { - //even-odd filling ... + //EvenOdd filling ... while ( e != &edge ) { edge.windCnt2 = (edge.windCnt2 == 0) ? 1 : 0; e = e->nextInAEL; } + } else + { + //nonZero, Positive or Negative filling ... + while ( e != &edge ) + { + edge.windCnt2 += e->windDelta; + e = e->nextInAEL; + } } } //------------------------------------------------------------------------------ -bool Clipper::IsNonZeroFillType(const TEdge& edge) const +bool Clipper::IsEvenOddFillType(const TEdge& edge) const { if (edge.polyType == ptSubject) - return m_SubjFillType == pftNonZero; else - return m_ClipFillType == pftNonZero; + return m_SubjFillType == pftEvenOdd; else + return m_ClipFillType == pftEvenOdd; } //------------------------------------------------------------------------------ -bool Clipper::IsNonZeroAltFillType(const TEdge& edge) const +bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const { if (edge.polyType == ptSubject) - return m_ClipFillType == pftNonZero; else - return m_SubjFillType == pftNonZero; + return m_ClipFillType == pftEvenOdd; else + return m_SubjFillType == pftEvenOdd; } //------------------------------------------------------------------------------ bool Clipper::IsContributing(const TEdge& edge) const { - switch( m_ClipType ){ + PolyFillType pft, pft2; + if (edge.polyType == ptSubject) + { + pft = m_SubjFillType; + pft2 = m_ClipFillType; + } else + { + pft = m_ClipFillType; + pft2 = m_SubjFillType; + } + + switch(pft) + { + case pftEvenOdd: + case pftNonZero: + if (Abs(edge.windCnt) != 1) return false; + break; + case pftPositive: + if (edge.windCnt != 1) return false; + break; + default: //pftNegative + if (edge.windCnt != -1) return false; + } + + switch(m_ClipType) + { case ctIntersection: - if ( edge.polyType == ptSubject ) - return Abs(edge.windCnt) == 1 && edge.windCnt2 != 0; else - return Abs(edge.windCnt2) > 0 && Abs(edge.windCnt) == 1; + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.windCnt2 != 0); + case pftPositive: + return (edge.windCnt2 > 0); + default: + return (edge.windCnt2 < 0); + } case ctUnion: - return Abs(edge.windCnt) == 1 && edge.windCnt2 == 0; + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.windCnt2 == 0); + case pftPositive: + return (edge.windCnt2 <= 0); + default: + return (edge.windCnt2 >= 0); + } case ctDifference: - if ( edge.polyType == ptSubject ) - return std::abs(edge.windCnt) == 1 && edge.windCnt2 == 0; else - return std::abs(edge.windCnt) == 1 && edge.windCnt2 != 0; - default: //case ctXor: - return std::abs(edge.windCnt) == 1; + if (edge.polyType == ptSubject) + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.windCnt2 == 0); + case pftPositive: + return (edge.windCnt2 <= 0); + default: + return (edge.windCnt2 >= 0); + } + else + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.windCnt2 != 0); + case pftPositive: + return (edge.windCnt2 > 0); + default: + return (edge.windCnt2 < 0); + } + default: + return true; } } //------------------------------------------------------------------------------ void Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt) { - if( e2->dx == horizontal || ( e1->dx > e2->dx ) ) + if( NEAR_EQUAL(e2->dx, HORIZONTAL) || ( e1->dx > e2->dx ) ) { AddOutPt( e1, e2, pt ); e2->outIdx = e1->outIdx; @@ -1551,18 +1596,20 @@ void Clipper::InsertLocalMinimaIntoAEL( const long64 botY) InsertScanbeam( lb->ytop ); InsertEdgeIntoAEL( rb ); - if ( IsNonZeroFillType( *lb) ) - rb->windDelta = -lb->windDelta; - else + if (IsEvenOddFillType(*lb)) { lb->windDelta = 1; rb->windDelta = 1; } + else + { + rb->windDelta = -lb->windDelta; + } SetWindingCount( *lb ); rb->windCnt = lb->windCnt; rb->windCnt2 = lb->windCnt2; - if( rb->dx == horizontal ) + if( NEAR_EQUAL(rb->dx, HORIZONTAL) ) { //nb: only rightbounds can have a horizontal bottom edge AddEdgeToSEL( rb ); @@ -1583,7 +1630,7 @@ void Clipper::InsertLocalMinimaIntoAEL( const long64 botY) //if any output polygons share an edge, they'll need joining later ... if (rb->outIdx >= 0) { - if (rb->dx == horizontal) + if (NEAR_EQUAL(rb->dx, HORIZONTAL)) { for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) { @@ -1638,11 +1685,10 @@ void Clipper::DeleteFromSEL(TEdge *e) { TEdge* SelPrev = e->prevInSEL; TEdge* SelNext = e->nextInSEL; - if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted + if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted if( SelPrev ) SelPrev->nextInSEL = SelNext; else m_SortedEdges = SelNext; if( SelNext ) SelNext->prevInSEL = SelPrev; - e->nextInSEL = 0; e->prevInSEL = 0; } @@ -1664,77 +1710,118 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, //assumes that e1 will be to the right of e2 ABOVE the intersection if ( e1->polyType == e2->polyType ) { - if ( IsNonZeroFillType( *e1) ) + if ( IsEvenOddFillType( *e1) ) + { + int oldE1WindCnt = e1->windCnt; + e1->windCnt = e2->windCnt; + e2->windCnt = oldE1WindCnt; + } else { if (e1->windCnt + e2->windDelta == 0 ) e1->windCnt = -e1->windCnt; else e1->windCnt += e2->windDelta; if ( e2->windCnt - e1->windDelta == 0 ) e2->windCnt = -e2->windCnt; else e2->windCnt -= e1->windDelta; - } else - { - int oldE1WindCnt = e1->windCnt; - e1->windCnt = e2->windCnt; - e2->windCnt = oldE1WindCnt; } } else { - if ( IsNonZeroFillType(*e2) ) e1->windCnt2 += e2->windDelta; + if (!IsEvenOddFillType(*e2)) e1->windCnt2 += e2->windDelta; else e1->windCnt2 = ( e1->windCnt2 == 0 ) ? 1 : 0; - if ( IsNonZeroFillType(*e1) ) e2->windCnt2 -= e1->windDelta; + if (!IsEvenOddFillType(*e1)) e2->windCnt2 -= e1->windDelta; else e2->windCnt2 = ( e2->windCnt2 == 0 ) ? 1 : 0; } + PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; + if (e1->polyType == ptSubject) + { + e1FillType = m_SubjFillType; + e1FillType2 = m_ClipFillType; + } else + { + e1FillType = m_ClipFillType; + e1FillType2 = m_SubjFillType; + } + if (e2->polyType == ptSubject) + { + e2FillType = m_SubjFillType; + e2FillType2 = m_ClipFillType; + } else + { + e2FillType = m_ClipFillType; + e2FillType2 = m_SubjFillType; + } + + long64 e1Wc, e2Wc; + switch (e1FillType) + { + case pftPositive: e1Wc = e1->windCnt; break; + case pftNegative: e1Wc = -e1->windCnt; break; + default: e1Wc = Abs(e1->windCnt); + } + switch(e2FillType) + { + case pftPositive: e2Wc = e2->windCnt; break; + case pftNegative: e2Wc = -e2->windCnt; break; + default: e2Wc = Abs(e2->windCnt); + } + if ( e1Contributing && e2contributing ) { - if ( e1stops || e2stops || std::abs(e1->windCnt) > 1 || - std::abs(e2->windCnt) > 1 || + if ( e1stops || e2stops || + (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || (e1->polyType != e2->polyType && m_ClipType != ctXor) ) - AddLocalMaxPoly(e1, e2, pt); else + AddLocalMaxPoly(e1, e2, pt); + else DoBothEdges( e1, e2, pt ); } else if ( e1Contributing ) { - switch( m_ClipType ) { - case ctIntersection: - if ( (e2->polyType == ptSubject || e2->windCnt2 != 0) && - std::abs(e2->windCnt) < 2 ) DoEdge1( e1, e2, pt); - break; - default: - if ( std::abs(e2->windCnt) < 2 ) DoEdge1(e1, e2, pt); - } + if ((e2Wc == 0 || e2Wc == 1) && + (m_ClipType != ctIntersection || + e2->polyType == ptSubject || (e2->windCnt2 != 0))) + DoEdge1(e1, e2, pt); } else if ( e2contributing ) { - if ( m_ClipType == ctIntersection ) - { - if ( (e1->polyType == ptSubject || e1->windCnt2 != 0) && - std::abs(e1->windCnt) < 2 ) DoEdge2( e1, e2, pt ); - } - else - if (std::abs(e1->windCnt) < 2) DoEdge2( e1, e2, pt ); - - } else if ( std::abs(e1->windCnt) < 2 && std::abs(e2->windCnt) < 2 && - !e1stops && !e2stops ) + if ((e1Wc == 0 || e1Wc == 1) && + (m_ClipType != ctIntersection || + e1->polyType == ptSubject || (e1->windCnt2 != 0))) + DoEdge2(e1, e2, pt); + } + else if ( (e1Wc == 0 || e1Wc == 1) && + (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops ) { - //nb: neither edge is currently contributing ... - if ( e1->polyType != e2->polyType ) + //neither edge is currently contributing ... + + long64 e1Wc2, e2Wc2; + switch (e1FillType2) + { + case pftPositive: e1Wc2 = e1->windCnt2; break; + case pftNegative : e1Wc2 = -e1->windCnt2; break; + default: e1Wc2 = Abs(e1->windCnt2); + } + switch (e2FillType2) + { + case pftPositive: e2Wc2 = e2->windCnt2; break; + case pftNegative: e2Wc2 = -e2->windCnt2; break; + default: e2Wc2 = Abs(e2->windCnt2); + } + + if (e1->polyType != e2->polyType) AddLocalMinPoly(e1, e2, pt); - else if ( std::abs(e1->windCnt) == 1 && std::abs(e2->windCnt) == 1 ) + else if (e1Wc == 1 && e2Wc == 1) switch( m_ClipType ) { case ctIntersection: - if ( std::abs(e1->windCnt2) > 0 && std::abs(e2->windCnt2) > 0 ) + if (e1Wc2 > 0 && e2Wc2 > 0) AddLocalMinPoly(e1, e2, pt); break; case ctUnion: - if ( e1->windCnt2 == 0 && e2->windCnt2 == 0 ) + if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) AddLocalMinPoly(e1, e2, pt); break; case ctDifference: - if ( (e1->polyType == ptClip && e2->polyType == ptClip && - e1->windCnt2 != 0 && e2->windCnt2 != 0) || - (e1->polyType == ptSubject && e2->polyType == ptSubject && - e1->windCnt2 == 0 && e2->windCnt2 == 0) ) - AddLocalMinPoly(e1, e2, pt); + if ( (e1->polyType == ptClip && e1Wc2 > 0 && e2Wc2 > 0) || + (e1->polyType == ptSubject && e1Wc2 <= 0 && e2Wc2 <= 0) ) + AddLocalMinPoly(e1, e2, pt); break; case ctXor: AddLocalMinPoly(e1, e2, pt); @@ -1805,9 +1892,16 @@ OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) else if (outRec2->bottomE2 == 0) return outRec1; else { - double dx1 = std::max(outRec1->bottomE1->dx, outRec1->bottomE2->dx); - double dx2 = std::max(outRec2->bottomE1->dx, outRec2->bottomE2->dx); - if (dx2 > dx1) return outRec2; else return outRec1; + long64 y1 = std::max(outRec1->bottomE1->ybot, outRec1->bottomE2->ybot); + long64 y2 = std::max(outRec2->bottomE1->ybot, outRec2->bottomE2->ybot); + if (y2 == y1 || (y1 > outPt1->pt.Y && y2 > outPt1->pt.Y)) + { + double dx1 = std::max(outRec1->bottomE1->dx, outRec1->bottomE2->dx); + double dx2 = std::max(outRec2->bottomE1->dx, outRec2->bottomE2->dx); + if (dx2 > dx1) return outRec2; else return outRec1; + } + else if (y2 > y1) return outRec2; + else return outRec1; } } //------------------------------------------------------------------------------ @@ -1820,13 +1914,10 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) OutRec *holeStateRec = GetLowermostRec(outRec1, outRec2); //fixup hole status ... - if (outRec1->isHole != outRec2->isHole) - { - if (holeStateRec == outRec2) - outRec1->isHole = outRec2->isHole; - else - outRec2->isHole = outRec1->isHole; - } + if (holeStateRec == outRec2) + outRec1->isHole = outRec2->isHole; + else + outRec2->isHole = outRec1->isHole; OutPt* p1_lft = outRec1->pts; OutPt* p1_rt = p1_lft->prev; @@ -1923,8 +2014,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) } //------------------------------------------------------------------------------ - - OutRec* Clipper::CreateOutRec() { OutRec* result = new OutRec; @@ -1940,7 +2029,6 @@ OutRec* Clipper::CreateOutRec() void Clipper::AddOutPt(TEdge *e, TEdge *altE, const IntPoint &pt) { bool ToFront = (e->side == esLeft); - if( e->outIdx < 0 ) { OutRec *outRec = CreateOutRec(); @@ -2188,7 +2276,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error"); return; } - else if( e->dx == horizontal && !IsMinima(e) && !(e->xcurr > e->xtop) ) + else if( NEAR_EQUAL(e->dx, HORIZONTAL) && !IsMinima(e) && !(e->xcurr > e->xtop) ) { //An overlapping horizontal edge. Overlapping horizontal edges are //processed as if layered with the current horizontal edge (horizEdge) @@ -2221,9 +2309,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) if( horzEdge->nextInLML ) { if( horzEdge->outIdx >= 0 ) - { AddOutPt( horzEdge, 0, IntPoint(horzEdge->xtop, horzEdge->ytop)); - } UpdateEdgeIntoAEL( horzEdge ); } else @@ -2255,7 +2341,7 @@ void Clipper::UpdateEdgeIntoAEL(TEdge *&e) e = e->nextInLML; e->prevInAEL = AelPrev; e->nextInAEL = AelNext; - if( e->dx != horizontal ) InsertScanbeam( e->ytop ); + if( !NEAR_EQUAL(e->dx, HORIZONTAL) ) InsertScanbeam( e->ytop ); } //------------------------------------------------------------------------------ @@ -2431,7 +2517,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const long64 topY) { //1. process maxima, treating them as if they're 'bent' horizontal edges, // but exclude maxima with horizontal edges. nb: e can't be a horizontal. - if( IsMaxima(e, topY) && GetMaximaPair(e)->dx != horizontal ) + if( IsMaxima(e, topY) && !NEAR_EQUAL(GetMaximaPair(e)->dx, HORIZONTAL) ) { //'e' might be removed from AEL, as may any following edges so ... TEdge* ePrior = e->prevInAEL; @@ -2442,7 +2528,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const long64 topY) else { //2. promote horizontal edges, otherwise update xcurr and ycurr ... - if( IsIntermediate(e, topY) && e->nextInLML->dx == horizontal ) + if( IsIntermediate(e, topY) && NEAR_EQUAL(e->nextInLML->dx, HORIZONTAL) ) { if (e->outIdx >= 0) { @@ -2482,10 +2568,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const long64 topY) { if( IsIntermediate( e, topY ) ) { - if( e->outIdx >= 0 ) - { - AddOutPt(e, 0, IntPoint(e->xtop,e->ytop)); - } + if( e->outIdx >= 0 ) AddOutPt(e, 0, IntPoint(e->xtop,e->ytop)); UpdateEdgeIntoAEL(e); //if output polygons share an edge, they'll need joining later ... @@ -2564,7 +2647,6 @@ void Clipper::FixupOutPolygon(OutRec &outRec) void Clipper::BuildResult(Polygons &polys) { int k = 0; - polys.resize(m_PolyOuts.size()); for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { @@ -2745,10 +2827,10 @@ void Clipper::CheckHoleLinkages1(OutRec *outRec1, OutRec *outRec2) //polygon contained link to the correct polygon ... for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { - OutRec *or1 = m_PolyOuts[i]; - if (or1->isHole && or1->bottomPt && or1->FirstLeft == outRec1 && - !PointInPolygon(or1->bottomPt->pt, outRec1->pts, m_UseFullRange)) - or1->FirstLeft = outRec2; + OutRec *orec = m_PolyOuts[i]; + if (orec->isHole && orec->bottomPt && orec->FirstLeft == outRec1 && + !PointInPolygon(orec->bottomPt->pt, outRec1->pts, m_UseFullRange)) + orec->FirstLeft = outRec2; } } //---------------------------------------------------------------------- @@ -2850,18 +2932,22 @@ void Clipper::JoinCommonEdges(bool fixHoleLinkages) if (PointInPolygon(outRec2->pts->pt, outRec1->pts, m_UseFullRange)) { + //outRec2 is contained by outRec1 ... outRec2->isHole = !outRec1->isHole; outRec2->FirstLeft = outRec1; if (outRec2->isHole == Orientation(outRec2, m_UseFullRange)) ReversePolyPtLinks(*outRec2->pts); } else if (PointInPolygon(outRec1->pts->pt, outRec2->pts, m_UseFullRange)) { + //outRec1 is contained by outRec2 ... outRec2->isHole = outRec1->isHole; outRec1->isHole = !outRec2->isHole; outRec2->FirstLeft = outRec1->FirstLeft; outRec1->FirstLeft = outRec2; if (outRec1->isHole == Orientation(outRec1, m_UseFullRange)) ReversePolyPtLinks(*outRec1->pts); + //make sure any contained holes now link to the correct polygon ... + if (fixHoleLinkages) CheckHoleLinkages1(outRec1, outRec2); } else { outRec2->isHole = outRec1->isHole; @@ -2920,14 +3006,14 @@ void Clipper::JoinCommonEdges(bool fixHoleLinkages) void ReversePoints(Polygon& p) { std::reverse(p.begin(), p.end()); -}; +} //------------------------------------------------------------------------------ void ReversePoints(Polygons& p) { for (Polygons::size_type i = 0; i < p.size(); ++i) ReversePoints(p[i]); -}; +} //------------------------------------------------------------------------------ // OffsetPolygon functions ... @@ -2961,10 +3047,11 @@ Polygon BuildArc(const IntPoint &pt, DoublePoint GetUnitNormal( const IntPoint &pt1, const IntPoint &pt2) { + if(pt2.X == pt1.X && pt2.Y == pt1.Y) + return DoublePoint(0, 0); + double dx = (double)(pt2.X - pt1.X); double dy = (double)(pt2.Y - pt1.Y); - if( ( dx == 0 ) && ( dy == 0 ) ) return DoublePoint( 0, 0 ); - double f = 1 *1.0/ std::sqrt( dx*dx + dy*dy ); dx *= f; dy *= f; @@ -2980,8 +3067,8 @@ private: Polygons m_p; Polygon* m_curr_poly; std::vector normals; - double m_delta, m_RMin; - int m_highJ; + double m_delta, m_RMin, m_R; + size_t m_i, m_j, m_k; static const int buffLength = 128; JoinType m_jointype; @@ -2991,7 +3078,7 @@ PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, double delta, JoinType jointype, double MiterLimit) { //nb precondition - out_polys != ptsin_polys - if (delta == 0) + if (NEAR_ZERO(delta)) { out_polys = in_polys; return; @@ -3006,47 +3093,52 @@ PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, double deltaSq = delta*delta; out_polys.clear(); out_polys.resize(in_polys.size()); - for (Polygons::size_type i = 0; i < in_polys.size(); i++) + for (m_i = 0; m_i < in_polys.size(); m_i++) { - m_curr_poly = &out_polys[i]; - int len = (int)in_polys[i].size(); - if (len > 1 && m_p[i][0].X == m_p[i][len - 1].X && - m_p[i][0].Y == m_p[i][len - 1].Y) len--; - m_highJ = len - 1; + m_curr_poly = &out_polys[m_i]; + size_t len = in_polys[m_i].size(); + if (len > 1 && m_p[m_i][0].X == m_p[m_i][len - 1].X && + m_p[m_i][0].Y == m_p[m_i][len-1].Y) len--; - //to minimize artefacts, strip out those polygons where - //it's being shrunk and where its area < Sqr(delta) ... - double a1 = Area(in_polys[i]); - if (delta < 0) { if (a1 > 0 && a1 < deltaSq) len = 0; } - else if (a1 < 0 && -a1 < deltaSq) len = 0; //nb: a hole if area < 0 + //when 'shrinking' polygons - to minimize artefacts + //strip those polygons that have an area < pi * delta^2 ... + double a1 = Area(in_polys[m_i]); + if (delta < 0) { if (a1 > 0 && a1 < deltaSq *pi) len = 0; } + else if (a1 < 0 && -a1 < deltaSq *pi) len = 0; //holes have neg. area - if (len == 0 || (len < 3 && delta <= 0)) continue; - if (len == 1) + if (len == 0 || (len < 3 && delta <= 0)) + continue; + else if (len == 1) { Polygon arc; - arc = BuildArc(in_polys[i][m_highJ], 0, 2 * pi, delta); - out_polys[i] = arc; + arc = BuildArc(in_polys[m_i][len-1], 0, 2 * pi, delta); + out_polys[m_i] = arc; continue; } //build normals ... normals.clear(); normals.resize(len); - normals[0] = GetUnitNormal(in_polys[i][m_highJ], in_polys[i][0]); - for (int j = 1; j < len; ++j) - normals[j] = GetUnitNormal(in_polys[i][j - 1], in_polys[i][j]); - - switch (jointype) + normals[len-1] = GetUnitNormal(in_polys[m_i][len-1], in_polys[m_i][0]); + for (m_j = 0; m_j < len -1; ++m_j) + normals[m_j] = GetUnitNormal(in_polys[m_i][m_j], in_polys[m_i][m_j+1]); + + m_k = len -1; + for (m_j = 0; m_j < len; ++m_j) { + switch (jointype) + { case jtMiter: - for (int j = 0; j < len; ++j) DoMiter(i, j, MiterLimit); - break; - case jtRound: - for (int j = 0; j < len; ++j) DoRound(i, j); - break; - case jtSquare: - for (int j = 0; j < len; ++j) DoSquare(i, j, 1.0); - break; + { + m_R = 1 + (normals[m_j].X*normals[m_k].X + + normals[m_j].Y*normals[m_k].Y); + if (m_R >= m_RMin) DoMiter(); else DoSquare(MiterLimit); + break; + } + case jtSquare: DoSquare(); break; + case jtRound: DoRound(); break; + } + m_k = m_j; } } @@ -3055,7 +3147,7 @@ PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, clpr.AddPolygons(out_polys, ptSubject); if (delta > 0) { - if (!clpr.Execute(ctUnion, out_polys, pftNonZero, pftNonZero)) + if (!clpr.Execute(ctUnion, out_polys, pftPositive, pftPositive)) out_polys.clear(); } else @@ -3068,7 +3160,7 @@ PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, outer[3] = IntPoint(r.left - 10, r.top - 10); clpr.AddPolygon(outer, ptSubject); - if (clpr.Execute(ctUnion, out_polys, pftNonZero, pftNonZero)) + if (clpr.Execute(ctUnion, out_polys, pftNegative, pftNegative)) { out_polys.erase(out_polys.begin()); ReversePoints(out_polys); @@ -3081,7 +3173,7 @@ PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, private: -void AddPoint(IntPoint& pt) +void AddPoint(const IntPoint& pt) { Polygon::size_type len = m_curr_poly->size(); if (len == m_curr_poly->capacity()) @@ -3090,95 +3182,85 @@ void AddPoint(IntPoint& pt) } //------------------------------------------------------------------------------ -void DoSquare(int i, int j, double mul) +void DoSquare(double mul = 1.0) { - int k; - if (j == m_highJ) k = 0; else k = j + 1; - IntPoint pt1 = IntPoint((long64)Round(m_p[i][j].X + normals[j].X * m_delta), - (long64)Round(m_p[i][j].Y + normals[j].Y * m_delta)); - IntPoint pt2 = IntPoint((long64)Round(m_p[i][j].X + normals[k].X * m_delta), - (long64)Round(m_p[i][j].Y + normals[k].Y * m_delta)); - if ((normals[j].X * normals[k].Y - normals[k].X * normals[j].Y) * m_delta >= 0) + IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), + (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); + IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), + (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); + if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) { - double a1 = std::atan2(normals[j].Y, normals[j].X); - double a2 = std::atan2(-normals[k].Y, -normals[k].X); + double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); + double a2 = std::atan2(-normals[m_j].Y, -normals[m_j].X); a1 = std::fabs(a2 - a1); if (a1 > pi) a1 = pi * 2 - a1; double dx = std::tan((pi - a1)/4) * std::fabs(m_delta * mul); - pt1 = IntPoint((long64)(pt1.X -normals[j].Y * dx), - (long64)(pt1.Y + normals[j].X * dx)); + pt1 = IntPoint((long64)(pt1.X -normals[m_k].Y * dx), + (long64)(pt1.Y + normals[m_k].X * dx)); AddPoint(pt1); - pt2 = IntPoint((long64)(pt2.X + normals[k].Y * dx), - (long64)(pt2.Y -normals[k].X * dx)); + pt2 = IntPoint((long64)(pt2.X + normals[m_j].Y * dx), + (long64)(pt2.Y -normals[m_j].X * dx)); AddPoint(pt2); } else { AddPoint(pt1); + AddPoint(m_p[m_i][m_j]); AddPoint(pt2); } } //------------------------------------------------------------------------------ -void DoMiter(int i, int j, double mul) +void DoMiter() { - int k; - if (j == m_highJ) k = 0; else k = j + 1; - double R = 1 + (normals[j].X*normals[k].X + normals[j].Y*normals[k].Y); - if (R >= m_RMin) + if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) { - if ((normals[j].X*normals[k].Y - normals[k].X*normals[j].Y) * m_delta >= 0) //ie angle > 180 - { - R = m_delta / R; - IntPoint pt1 = - IntPoint((long64)Round(m_p[i][j].X + (normals[j].X + normals[k].X) *R), - (long64)Round(m_p[i][j].Y + (normals[j].Y + normals[k].Y) *R)); - AddPoint(pt1); - } - else - { - IntPoint pt1 = IntPoint((long64)Round(m_p[i][j].X + normals[j].X * - m_delta), (long64)Round(m_p[i][j].Y + normals[j].Y * m_delta)); - IntPoint pt2 = IntPoint((long64)Round(m_p[i][j].X + normals[k].X * - m_delta), (long64)Round(m_p[i][j].Y + normals[k].Y * m_delta)); - AddPoint(pt1); - AddPoint(pt2); - } + double q = m_delta / m_R; + AddPoint(IntPoint((long64)Round(m_p[m_i][m_j].X + + (normals[m_k].X + normals[m_j].X) * q), + (long64)Round(m_p[m_i][m_j].Y + (normals[m_k].Y + normals[m_j].Y) * q))); } else - DoSquare(i, j, mul); + { + IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * + m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); + IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * + m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); + AddPoint(pt1); + AddPoint(m_p[m_i][m_j]); + AddPoint(pt2); + } } //------------------------------------------------------------------------------ -void DoRound(int i, int j) +void DoRound() { - int k; - if (j == m_highJ) k = 0; else k = j + 1; - IntPoint pt1 = IntPoint((long64)Round(m_p[i][j].X + normals[j].X * m_delta), - (long64)Round(m_p[i][j].Y + normals[j].Y * m_delta)); - IntPoint pt2 = IntPoint((long64)Round(m_p[i][j].X + normals[k].X * m_delta), - (long64)Round(m_p[i][j].Y + normals[k].Y * m_delta)); + IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), + (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); + IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), + (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); AddPoint(pt1); - //round off reflex angles (ie > 180 deg) unless it's - //almost flat (ie < 10deg angle). - //cross product normals < 0 -> angle > 180 deg. - //dot product normals == 1 -> no angle - if ((normals[j].X*normals[k].Y - normals[k].X*normals[j].Y) * m_delta >= 0 && - (normals[k].X * normals[j].X + normals[k].Y * normals[j].Y) < 0.985) + //round off reflex angles (ie > 180 deg) unless almost flat (ie < ~10deg). + if ((normals[m_k].X*normals[m_j].Y - normals[m_j].X*normals[m_k].Y) * m_delta >= 0) { - double a1 = std::atan2(normals[j].Y, normals[j].X); - double a2 = std::atan2(normals[k].Y, normals[k].X); - if (m_delta > 0 && a2 < a1) a2 += pi *2; - else if (m_delta < 0 && a2 > a1) a2 -= pi *2; - Polygon arc = BuildArc(m_p[i][j], a1, a2, m_delta); - for (Polygon::size_type m = 0; m < arc.size(); m++) - AddPoint(arc[m]); + if (normals[m_j].X * normals[m_k].X + normals[m_j].Y * normals[m_k].Y < 0.985) + { + double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); + double a2 = std::atan2(normals[m_j].Y, normals[m_j].X); + if (m_delta > 0 && a2 < a1) a2 += pi *2; + else if (m_delta < 0 && a2 > a1) a2 -= pi *2; + Polygon arc = BuildArc(m_p[m_i][m_j], a1, a2, m_delta); + for (Polygon::size_type m = 0; m < arc.size(); m++) + AddPoint(arc[m]); + } } + else + AddPoint(m_p[m_i][m_j]); AddPoint(pt2); } //-------------------------------------------------------------------------- -}; //class PolyOffsetBuilder +}; //end PolyOffsetBuilder //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ @@ -3195,11 +3277,33 @@ void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, } //------------------------------------------------------------------------------ +void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys) +{ + Clipper c; + c.AddPolygon(in_poly, ptSubject); + c.Execute(ctUnion, out_polys); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys) +{ + Clipper c; + c.AddPolygons(in_polys, ptSubject); + c.Execute(ctUnion, out_polys); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(Polygons &polys) +{ + SimplifyPolygons(polys, polys); +} +//------------------------------------------------------------------------------ + std::ostream& operator <<(std::ostream &s, IntPoint& p) { s << p.X << ' ' << p.Y << "\n"; return s; -}; +} //------------------------------------------------------------------------------ std::ostream& operator <<(std::ostream &s, Polygon &p) @@ -3220,4 +3324,4 @@ std::ostream& operator <<(std::ostream &s, Polygons &p) } //------------------------------------------------------------------------------ -}; //ClipperLib namespace +} //ClipperLib namespace diff --git a/src/Lib/Polygon/clipper.hpp b/src/Lib/Polygon/clipper.hpp index 5ae6185e..5f1f1259 100644 --- a/src/Lib/Polygon/clipper.hpp +++ b/src/Lib/Polygon/clipper.hpp @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 4.5.5 * -* Date : 6 October 2011 * +* Version : 4.6.5 * +* Date : 17 January 2011 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2011 * +* Copyright : Angus Johnson 2010-2012 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -21,6 +21,14 @@ * Springer; 1 edition (January 4, 2005) * * http://books.google.com/books?q=vatti+clipping+agoston * * * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24–28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * *******************************************************************************/ #ifndef clipper_hpp @@ -39,8 +47,8 @@ enum PolyType { ptSubject, ptClip }; //By far the most widely used winding rules for polygon filling are //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) -//see http://www.songho.ca/opengl/gl_tessellation.html#winding_rules -enum PolyFillType { pftEvenOdd, pftNonZero }; +//see http://glprogramming.com/red/chapter11.html +enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; typedef signed long long long64; typedef unsigned long long ulong64; @@ -71,6 +79,9 @@ bool Orientation(const Polygon &poly); double Area(const Polygon &poly); void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, double delta, JoinType jointype = jtSquare, double MiterLimit = 2); +void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys); +void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys); +void SimplifyPolygons(Polygons &polys); void ReversePoints(Polygon& p); void ReversePoints(Polygons& p); @@ -193,13 +204,13 @@ public: Clipper(); ~Clipper(); bool Execute(ClipType clipType, - Polygons &solution, - PolyFillType subjFillType = pftEvenOdd, - PolyFillType clipFillType = pftEvenOdd); + Polygons &solution, + PolyFillType subjFillType = pftEvenOdd, + PolyFillType clipFillType = pftEvenOdd); bool Execute(ClipType clipType, - ExPolygons &solution, - PolyFillType subjFillType = pftEvenOdd, - PolyFillType clipFillType = pftEvenOdd); + ExPolygons &solution, + PolyFillType subjFillType = pftEvenOdd, + PolyFillType clipFillType = pftEvenOdd); void Clear(); bool ReverseSolution() {return m_ReverseOutput;}; void ReverseSolution(bool value) {m_ReverseOutput = value;}; @@ -221,8 +232,8 @@ private: bool m_ReverseOutput; void DisposeScanbeamList(); void SetWindingCount(TEdge& edge); - bool IsNonZeroFillType(const TEdge& edge) const; - bool IsNonZeroAltFillType(const TEdge& edge) const; + bool IsEvenOddFillType(const TEdge& edge) const; + bool IsEvenOddAltFillType(const TEdge& edge) const; void InsertScanbeam(const long64 Y); long64 PopScanbeam(); void InsertLocalMinimaIntoAEL(const long64 botY); diff --git a/src/Lib/Polygon/polygon.cxx b/src/Lib/Polygon/polygon.cxx index e3eb280c..e9c59906 100644 --- a/src/Lib/Polygon/polygon.cxx +++ b/src/Lib/Polygon/polygon.cxx @@ -540,6 +540,38 @@ void make_tg_poly_from_clipper( const Polygons& in, TGPolygon *out ) } } +Polygons clipper_simplify( ExPolygons &in ) +{ + Polygons out; + Polygon contour; + + for (unsigned int i=0; iouter; + if ( !Orientation( contour ) ) { + ReversePoints( contour ); + } + out.push_back( contour ); + + // then the holes + for (unsigned int j = 0; j < pg->holes.size(); j++) + { + contour = pg->holes[j]; + if ( Orientation( contour ) ) { + ReversePoints( contour ); + } + out.push_back( contour ); + } + } + + // Now simplify + SimplifyPolygons(out); + + return out; +} TGPolygon polygon_clip_clipper( clip_op poly_op, const TGPolygon& subject, const TGPolygon& clip ) { @@ -572,7 +604,11 @@ TGPolygon polygon_clip_clipper( clip_op poly_op, const TGPolygon& subject, const c.AddPolygons(clipper_clip, ptClip); c.Execute(op, clipper_result, pftEvenOdd, pftEvenOdd); - make_tg_poly_from_clipper_ex( clipper_result, &result ); + + // verify each result is simple + Polygons simple_result = clipper_simplify( clipper_result ); + + make_tg_poly_from_clipper( simple_result, &result ); return result; } @@ -608,7 +644,6 @@ TGPolygon tgPolygonUnionClipper( const TGPolygon& subject, const TGPolygon& clip return polygon_clip_clipper( POLY_UNION, subject, clip ); } - // canonify the polygon winding, outer contour must be anti-clockwise, // all inner contours must be clockwise. TGPolygon polygon_canonify( const TGPolygon& in_poly ) { diff --git a/src/Lib/Polygon/polygon.hxx b/src/Lib/Polygon/polygon.hxx index 66483a24..1f4e8a40 100644 --- a/src/Lib/Polygon/polygon.hxx +++ b/src/Lib/Polygon/polygon.hxx @@ -275,7 +275,6 @@ TGPolygon tgPolygonDiffClipper( const TGPolygon& subject, const TGPolygon& clip // Union TGPolygon tgPolygonUnionClipper( const TGPolygon& subject, const TGPolygon& clip ); - // Expand / Shrink TGPolygon tgPolygonExpand(const TGPolygon &poly, double delta);