void ConstGeometryIterator::fetchNext() { _next = 0L; if ( _stack.size() == 0 ) return; const Geometry* current = _stack.top(); _stack.pop(); if ( current->getType() == Geometry::TYPE_MULTI && _traverseMulti ) { const MultiGeometry* m = static_cast<const MultiGeometry*>(current); for( GeometryCollection::const_iterator i = m->getComponents().begin(); i != m->getComponents().end(); ++i ) _stack.push( i->get() ); fetchNext(); } else if ( current->getType() == Geometry::TYPE_POLYGON && _traversePolyHoles ) { const Polygon* p = static_cast<const Polygon*>(current); for( RingCollection::const_iterator i = p->getHoles().begin(); i != p->getHoles().end(); ++i ) _stack.push( i->get() ); _next = current; } else { _next = current; } }
int Polygon::getTotalPointCount() const { int total = Ring::getTotalPointCount(); for( RingCollection::const_iterator i = _holes.begin(); i != _holes.end(); ++i ) total += i->get()->getTotalPointCount(); return total; }
bool Polygon::contains2D( double x, double y ) const { // first check the outer ring if ( !Ring::contains2D(x, y) ) return false; // then check each inner ring (holes). Point has to be inside the outer ring, // but NOT inside any of the holes for( RingCollection::const_iterator i = _holes.begin(); i != _holes.end(); ++i ) { if ( i->get()->contains2D(x, y) ) return false; } return true; }
// builds and tessellates a polygon (with or without holes) void BuildGeometryFilter::buildPolygon(Geometry* ring, const SpatialReference* featureSRS, const SpatialReference* mapSRS, bool makeECEF, bool tessellate, osg::Geometry* osgGeom) { if ( !ring->isValid() ) return; int totalPoints = ring->getTotalPointCount(); osg::Vec3Array* allPoints = new osg::Vec3Array(); transformAndLocalize( ring->asVector(), featureSRS, allPoints, mapSRS, _world2local, makeECEF ); GLenum mode = GL_LINE_LOOP; osgGeom->addPrimitiveSet( new osg::DrawArrays( mode, 0, ring->size() ) ); Polygon* poly = dynamic_cast<Polygon*>(ring); if ( poly ) { int offset = ring->size(); for( RingCollection::const_iterator h = poly->getHoles().begin(); h != poly->getHoles().end(); ++h ) { Geometry* hole = h->get(); if ( hole->isValid() ) { transformAndLocalize( hole->asVector(), featureSRS, allPoints, mapSRS, _world2local, makeECEF ); osgGeom->addPrimitiveSet( new osg::DrawArrays( mode, offset, hole->size() ) ); offset += hole->size(); } } } osgGeom->setVertexArray( allPoints ); if ( tessellate ) { osgUtil::Tessellator tess; tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_POSITIVE ); //tess.setBoundaryOnly( true ); tess.retessellatePolygons( *osgGeom ); } //// Normal computation. //// Not completely correct, but better than no normals at all. TODO: update this //// to generate a proper normal vector in ECEF mode. //// //// We cannot accurately rely on triangles from the tessellation, since we could have //// very "degraded" triangles (close to a line), and the normal computation would be bad. //// In this case, we would have to average the normal vector over each triangle of the polygon. //// The Newell's formula is simpler and more direct here. //osg::Vec3 normal( 0.0, 0.0, 0.0 ); //for ( size_t i = 0; i < poly->size(); ++i ) //{ // osg::Vec3 pi = (*poly)[i]; // osg::Vec3 pj = (*poly)[ (i+1) % poly->size() ]; // normal[0] += ( pi[1] - pj[1] ) * ( pi[2] + pj[2] ); // normal[1] += ( pi[2] - pj[2] ) * ( pi[0] + pj[0] ); // normal[2] += ( pi[0] - pj[0] ) * ( pi[1] + pj[1] ); //} //normal.normalize(); //osg::Vec3Array* normals = new osg::Vec3Array(); //normals->push_back( normal ); //osgGeom->setNormalArray( normals ); //osgGeom->setNormalBinding( osg::Geometry::BIND_OVERALL ); }
Polygon::Polygon( const Polygon& rhs ) : Ring( rhs ) { for( RingCollection::const_iterator r = rhs._holes.begin(); r != rhs._holes.end(); ++r ) _holes.push_back( new Ring(*r->get()) ); }
// builds and tessellates a polygon (with or without holes) void BuildGeometryFilter::buildPolygon(Geometry* ring, const SpatialReference* featureSRS, const SpatialReference* mapSRS, bool makeECEF, bool tessellate, osg::Geometry* osgGeom, const osg::Matrixd &world2local) { if ( !ring->isValid() ) return; ring->rewind(osgEarth::Symbology::Geometry::ORIENTATION_CCW); osg::ref_ptr<osg::Vec3Array> allPoints = new osg::Vec3Array(); transformAndLocalize( ring->asVector(), featureSRS, allPoints.get(), mapSRS, world2local, makeECEF ); Polygon* poly = dynamic_cast<Polygon*>(ring); if ( poly ) { RingCollection ordered(poly->getHoles().begin(), poly->getHoles().end()); std::sort(ordered.begin(), ordered.end(), holeCompare); for( RingCollection::const_iterator h = ordered.begin(); h != ordered.end(); ++h ) { Geometry* hole = h->get(); if ( hole->isValid() ) { hole->rewind(osgEarth::Symbology::Geometry::ORIENTATION_CW); osg::ref_ptr<osg::Vec3Array> holePoints = new osg::Vec3Array(); transformAndLocalize( hole->asVector(), featureSRS, holePoints.get(), mapSRS, world2local, makeECEF ); // find the point with the highest x value unsigned int hCursor = 0; for (unsigned int i=1; i < holePoints->size(); i++) { if ((*holePoints)[i].x() > (*holePoints)[hCursor].x()) hCursor = i; } double x1 = (*holePoints)[hCursor].x(); double y1 = (*holePoints)[hCursor].y(); double y2 = (*holePoints)[hCursor].y(); unsigned int edgeCursor = UINT_MAX; double edgeDistance = DBL_MAX; unsigned int foundPointCursor = UINT_MAX; for (unsigned int i=0; i < allPoints->size(); i++) { unsigned int next = i == allPoints->size() - 1 ? 0 : i + 1; double xMax = osg::maximum((*allPoints)[i].x(), (*allPoints)[next].x()); if (xMax > (*holePoints)[hCursor].x()) { double x2 = xMax + 1.0; double x3 = (*allPoints)[i].x(); double y3 = (*allPoints)[i].y(); double x4 = (*allPoints)[next].x(); double y4 = (*allPoints)[next].y(); double xi=0.0, yi=0.0; bool intersects = false; unsigned int hitPointCursor = UINT_MAX; if (y1 == y3 && x3 > x1) { xi = x3; hitPointCursor = i; intersects = true; } else if (y1 == y4 && x4 > x1) { xi = x4; hitPointCursor = next; intersects = true; } else if (segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4, xi, yi)) { intersects = true; } double dist = (osg::Vec2d(xi, yi) - osg::Vec2d(x1, y1)).length(); if (intersects && dist < edgeDistance) { foundPointCursor = hitPointCursor; edgeCursor = hitPointCursor != UINT_MAX ? hitPointCursor : (x3 >= x4 ? i : next); edgeDistance = dist; } } } if (foundPointCursor == UINT_MAX && edgeCursor != UINT_MAX) { // test for intersecting edges between x1 and x2 // (skipping the two segments for which edgeCursor is a vert) double x2 = (*allPoints)[edgeCursor].x(); y2 = (*allPoints)[edgeCursor].y(); bool foundIntersection = false; for (unsigned int i=0; i < allPoints->size(); i++) { unsigned int next = i == allPoints->size() - 1 ? 0 : i + 1; if (i == edgeCursor || next == edgeCursor) continue; double x3 = (*allPoints)[i].x(); double y3 = (*allPoints)[i].y(); double x4 = (*allPoints)[next].x(); double y4 = (*allPoints)[next].y(); foundIntersection = foundIntersection || segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4); if (foundIntersection) { unsigned int prev = i == 0 ? allPoints->size() - 1 : i - 1; if (!isCCW((*allPoints)[prev].x(), (*allPoints)[prev].y(), x3, y3, x4, y4)) { edgeCursor = i; x2 = (*allPoints)[edgeCursor].x(); y2 = (*allPoints)[edgeCursor].y(); foundIntersection = false; } } } } if (edgeCursor != UINT_MAX) { // build array of correctly ordered new points to add to the outer loop osg::ref_ptr<osg::Vec3Array> insertPoints = new osg::Vec3Array(); insertPoints->reserve(holePoints->size() + 2); unsigned int p = hCursor; do { insertPoints->push_back((*holePoints)[p]); p = p == holePoints->size() - 1 ? 0 : p + 1; } while(p != hCursor); insertPoints->push_back((*holePoints)[hCursor]); insertPoints->push_back((*allPoints)[edgeCursor]); // insert new points into outer loop osg::Vec3Array::iterator it = edgeCursor == allPoints->size() - 1 ? allPoints->end() : allPoints->begin() + (edgeCursor + 1); allPoints->insert(it, insertPoints->begin(), insertPoints->end()); } } } } GLenum mode = GL_LINE_LOOP; if ( osgGeom->getVertexArray() == 0L ) { osgGeom->addPrimitiveSet( new osg::DrawArrays( mode, 0, allPoints->size() ) ); osgGeom->setVertexArray( allPoints.get() ); } else { osg::Vec3Array* v = static_cast<osg::Vec3Array*>(osgGeom->getVertexArray()); osgGeom->addPrimitiveSet( new osg::DrawArrays( mode, v->size(), allPoints->size() ) ); //v->reserve(v->size() + allPoints->size()); std::copy(allPoints->begin(), allPoints->end(), std::back_inserter(*v)); } //// Normal computation. //// Not completely correct, but better than no normals at all. TODO: update this //// to generate a proper normal vector in ECEF mode. //// //// We cannot accurately rely on triangles from the tessellation, since we could have //// very "degraded" triangles (close to a line), and the normal computation would be bad. //// In this case, we would have to average the normal vector over each triangle of the polygon. //// The Newell's formula is simpler and more direct here. //osg::Vec3 normal( 0.0, 0.0, 0.0 ); //for ( size_t i = 0; i < poly->size(); ++i ) //{ // osg::Vec3 pi = (*poly)[i]; // osg::Vec3 pj = (*poly)[ (i+1) % poly->size() ]; // normal[0] += ( pi[1] - pj[1] ) * ( pi[2] + pj[2] ); // normal[1] += ( pi[2] - pj[2] ) * ( pi[0] + pj[0] ); // normal[2] += ( pi[0] - pj[0] ) * ( pi[1] + pj[1] ); //} //normal.normalize(); //osg::Vec3Array* normals = new osg::Vec3Array(); //normals->push_back( normal ); //osgGeom->setNormalArray( normals ); //osgGeom->setNormalBinding( osg::Geometry::BIND_OVERALL ); }
bool BuildGeometryFilter::pushRegularFeature( Feature* input, const FilterContext& context ) { GeometryIterator parts( input->getGeometry(), false ); while( parts.hasMore() ) { Geometry* part = parts.next(); osg::PrimitiveSet::Mode primMode = osg::PrimitiveSet::POINTS; const Style& myStyle = input->style().isSet() ? *input->style() : _style; //const Style* myStyle = input->style().isSet() ? input->style()->get() : _style.get(); osg::Vec4f color = osg::Vec4(1,1,1,1); bool tessellatePolys = true; bool setWidth = input->style().isSet(); // otherwise it will be set globally, we assume float width = 1.0f; switch( part->getType() ) { case Geometry::TYPE_POINTSET: { _hasPoints = true; primMode = osg::PrimitiveSet::POINTS; const PointSymbol* point = myStyle.getSymbol<PointSymbol>(); if (point) { color = point->fill()->color(); } } break; case Geometry::TYPE_LINESTRING: { _hasLines = true; primMode = osg::PrimitiveSet::LINE_STRIP; const LineSymbol* lineSymbol = myStyle.getSymbol<LineSymbol>(); if (lineSymbol) { color = lineSymbol->stroke()->color(); width = lineSymbol->stroke()->width().isSet() ? *lineSymbol->stroke()->width() : 1.0f; } } break; case Geometry::TYPE_RING: { _hasLines = true; primMode = osg::PrimitiveSet::LINE_LOOP; const LineSymbol* lineSymbol = myStyle.getSymbol<LineSymbol>(); if (lineSymbol) { color = lineSymbol->stroke()->color(); width = lineSymbol->stroke()->width().isSet() ? *lineSymbol->stroke()->width() : 1.0f; } } break; case Geometry::TYPE_POLYGON: { primMode = osg::PrimitiveSet::LINE_LOOP; // loop will tessellate into polys const PolygonSymbol* poly = myStyle.getSymbol<PolygonSymbol>(); if (poly) { _hasPolygons = true; color = poly->fill()->color(); } else { // if we have a line symbol and no polygon symbol, draw as an outline. _hasLines = true; const LineSymbol* line = myStyle.getSymbol<LineSymbol>(); if ( line ) { color = line->stroke()->color(); width = line->stroke()->width().isSet() ? *line->stroke()->width() : 1.0f; tessellatePolys = false; } } } break; } osg::Geometry* osgGeom = new osg::Geometry(); if ( _featureNameExpr.isSet() ) { const std::string& name = input->eval( _featureNameExpr.mutable_value() ); osgGeom->setName( name ); } osgGeom->setUseVertexBufferObjects( true ); osgGeom->setUseDisplayList( false ); if ( setWidth && width != 1.0f ) { osgGeom->getOrCreateStateSet()->setAttributeAndModes( new osg::LineWidth( width ), osg::StateAttribute::ON ); } if (part->getType() == Geometry::TYPE_POLYGON && static_cast<Polygon*>(part)->getHoles().size() > 0 ) { Polygon* poly = static_cast<Polygon*>(part); int totalPoints = poly->getTotalPointCount(); osg::Vec3Array* allPoints = new osg::Vec3Array( totalPoints ); std::copy( part->begin(), part->end(), allPoints->begin() ); osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) ); int offset = part->size(); for( RingCollection::const_iterator h = poly->getHoles().begin(); h != poly->getHoles().end(); ++h ) { Geometry* hole = h->get(); if ( hole->isValid() ) { std::copy( hole->begin(), hole->end(), allPoints->begin() + offset ); osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, offset, hole->size() ) ); offset += hole->size(); } } osgGeom->setVertexArray( allPoints ); } else { osgGeom->setVertexArray( part->toVec3Array() ); osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) ); } // tessellate all polygon geometries. Tessellating each geometry separately // with TESS_TYPE_GEOMETRY is much faster than doing the whole bunch together // using TESS_TYPE_DRAWABLE. if ( part->getType() == Geometry::TYPE_POLYGON && tessellatePolys ) { osgUtil::Tessellator tess; //tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_DRAWABLE ); //tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_POSITIVE ); tess.retessellatePolygons( *osgGeom ); // the tessellator results in a collection of trifans, strips, etc. This step will // consolidate those into one (or more if necessary) GL_TRIANGLES primitive. MeshConsolidator::run( *osgGeom ); // mark this geometry as DYNAMIC because otherwise the OSG optimizer will destroy it. //osgGeom->setDataVariance( osg::Object::DYNAMIC ); } if ( context.isGeocentric() && part->getType() != Geometry::TYPE_POINTSET ) { double threshold = osg::DegreesToRadians( *_maxAngle_deg ); MeshSubdivider ms( context.referenceFrame(), context.inverseReferenceFrame() ); //ms.setMaxElementsPerEBO( INT_MAX ); ms.run( threshold, *osgGeom ); } // set the color array. We have to do this last, otherwise it screws up any modifications // make by the MeshSubdivider. No idea why. gw //osg::Vec4Array* colors = new osg::Vec4Array( osgGeom->getVertexArray()->getNumElements() ); //for( unsigned c = 0; c < colors->size(); ++c ) // (*colors)[c] = color; //osgGeom->setColorArray( colors ); //osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); // NOTE! per-vertex colors makes the optimizer destroy the geometry.... osg::Vec4Array* colors = new osg::Vec4Array(1); (*colors)[0] = color; osgGeom->setColorArray( colors ); osgGeom->setColorBinding( osg::Geometry::BIND_OVERALL ); // add the part to the geode. _geode->addDrawable( osgGeom ); } return true; }