/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library 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 * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include using namespace osgSim; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SphereSegment // SphereSegment::SphereSegment(const osg::Vec3& centre, float radius, float azMin, float azMax, float elevMin, float elevMax, int density): osg::Geode(), _centre(centre), _radius(radius), _azMin(azMin), _azMax(azMax), _elevMin(elevMin), _elevMax(elevMax), _density(density), _drawMask(DrawMask(ALL)) { init(); } SphereSegment::SphereSegment(const SphereSegment& rhs, const osg::CopyOp& co): osg::Geode(rhs,co), _centre(rhs._centre), _radius(rhs._radius), _azMin(rhs._azMin), _azMax(rhs._azMax), _elevMin(rhs._elevMin), _elevMax(rhs._elevMax), _density(rhs._density), _drawMask(rhs._drawMask) { init(); } SphereSegment::SphereSegment(const osg::Vec3& centre, float radius, const osg::Vec3& vec, float azRange, float elevRange, int density): osg::Geode(), _centre(centre), _radius(radius), _density(density), _drawMask(DrawMask(ALL)) { // Rather than store the vector, we'll work out the azimuth boundaries and elev // boundaries now, rather than at draw time. setArea(vec, azRange, elevRange); init(); } void SphereSegment::traverse(osg::NodeVisitor& nv) { osg::Geode::traverse(nv); if ((_drawMask & SURFACE)!=0) { _surfaceGeometry->accept(nv); } if ((_drawMask & SPOKES)!=0) { _spokesGeometry->accept(nv); } if ((_drawMask & EDGELINE)!=0) { _edgeLineGeometry->accept(nv); } if ((_drawMask & SIDES)!=0) { _sidesGeometry->accept(nv); } } osg::BoundingSphere SphereSegment::computeBound() const { _bbox.init(); _bbox.expandBy(_surfaceGeometry->getBoundingBox()); _bbox.expandBy(_centre); return osg::BoundingSphere(_centre, _radius); } void SphereSegment::setCentre(const osg::Vec3& c) { _centre = c; updatePositions(); } const osg::Vec3& SphereSegment::getCentre() const { return _centre; } void SphereSegment::setRadius(float r) { _radius = r; updatePositions(); } float SphereSegment::getRadius() const { return _radius; } void SphereSegment::setArea(const osg::Vec3& v, float azRange, float elevRange) { osg::Vec3 vec(v); vec.normalize(); // Make sure we're unit length // Calculate the elevation range float xyLen = sqrtf(vec.x()*vec.x() + vec.y()*vec.y()); float elev = atan2(vec.z(), xyLen); // Elevation angle elevRange /= 2.0f; _elevMin = elev - elevRange; _elevMax = elev + elevRange; // Calculate the azimuth range, cater for trig ambiguities float az = atan2(vec.x(), vec.y()); azRange /= 2.0f; _azMin = az - azRange; _azMax = az + azRange; updatePositions(); } void SphereSegment::getArea(osg::Vec3& vec, float& azRange, float& elevRange) const { azRange = _azMax - _azMin; elevRange = _elevMax - _elevMin; float az = (_azMax+_azMin)/2.0f; float elev = (_elevMax+_elevMin)/2.0f; vec.set(cos(elev)*sin(az), cos(elev)*cos(az), sin(elev)); } void SphereSegment::setArea(float azMin, float azMax, float elevMin, float elevMax) { _azMin=azMin; _azMax=azMax; _elevMin=elevMin; _elevMax=elevMax; updatePositions(); } void SphereSegment::getArea(float &azMin, float &azMax, float &elevMin, float &elevMax) const { azMin=_azMin; azMax=_azMax; elevMin=_elevMin; elevMax=_elevMax; } void SphereSegment::setDensity(int density) { _density = density; updatePositions(); updatePrimitives(); } int SphereSegment::getDensity() const { return _density; } void SphereSegment::init() { // set up state _litOpaqueState = new osg::StateSet; _unlitOpaqueState = new osg::StateSet(*_litOpaqueState); _unlitOpaqueState->setMode(GL_LIGHTING,osg::StateAttribute::OFF); _litTransparentState = new osg::StateSet; _litTransparentState->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); _litTransparentState->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK),osg::StateAttribute::ON); _litTransparentState->setMode(GL_BLEND,osg::StateAttribute::ON); _unlitTransparentState = new osg::StateSet(*_litTransparentState); _unlitTransparentState->setMode(GL_LIGHTING,osg::StateAttribute::OFF); // set up colors _surfaceColor = new osg::Vec4Array(osg::Array::BIND_OVERALL,1); (*_surfaceColor)[0].set(1.0f,1.0,1.0f,0.5f); _spokeColor = new osg::Vec4Array(osg::Array::BIND_OVERALL,1); (*_spokeColor)[0].set(1.0f,1.0,1.0f,0.5f); _edgeLineColor = new osg::Vec4Array(osg::Array::BIND_OVERALL,1); (*_edgeLineColor)[0].set(1.0f,1.0,1.0f,0.5f); _sideColor = new osg::Vec4Array(osg::Array::BIND_OVERALL,1); (*_sideColor)[0].set(1.0f,1.0,1.0f,0.5f); // set up vertices unsigned int numVertices = (_density+1)*(_density+1)+1; _vertices = new osg::Vec3Array(osg::Array::BIND_OVERALL,numVertices); _normals = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX, numVertices); osg::ref_ptr vbo = new osg::VertexBufferObject; _vertices->setBufferObject(vbo.get()); _normals->setBufferObject(vbo.get()); // set up all the geometries that will render the various parts of the sphere segment _surfaceGeometry = new osg::Geometry; _surfaceGeometry->setVertexArray(_vertices.get()); _surfaceGeometry->setNormalArray(_normals.get()); _surfaceGeometry->setColorArray(_surfaceColor.get()); _surfaceGeometry->setStateSet(getLitStateSet((*_surfaceColor)[0])); _spokesGeometry = new osg::Geometry; _spokesGeometry->setVertexArray(_vertices.get()); _spokesGeometry->setNormalArray(_normals.get()); _spokesGeometry->setColorArray(_spokeColor.get()); _spokesGeometry->setStateSet(getUnlitStateSet((*_spokeColor)[0])); _edgeLineGeometry = new osg::Geometry; _edgeLineGeometry->setVertexArray(_vertices.get()); _edgeLineGeometry->setNormalArray(_normals.get()); _edgeLineGeometry->setColorArray(_edgeLineColor.get()); _edgeLineGeometry->setStateSet(getUnlitStateSet((*_edgeLineColor)[0])); _sidesGeometry = new osg::Geometry; _sidesGeometry->setVertexArray(_vertices.get()); _sidesGeometry->setNormalArray(_normals.get()); _sidesGeometry->setColorArray(_sideColor.get()); _sidesGeometry->setStateSet(getUnlitStateSet((*_sideColor)[0])); updatePositions(); updatePrimitives(); } void SphereSegment::setDrawMask(int dm) { _drawMask=dm; } void SphereSegment::setSurfaceColor(const osg::Vec4& c) { (*_surfaceColor)[0]=c; _surfaceGeometry->setStateSet(getLitStateSet((*_surfaceColor)[0])); } void SphereSegment::setSpokeColor(const osg::Vec4& c) { (*_spokeColor)[0]=c; _spokesGeometry->setStateSet(getUnlitStateSet((*_spokeColor)[0])); } void SphereSegment::setEdgeLineColor(const osg::Vec4& c) { (*_edgeLineColor)[0]=c; _edgeLineGeometry->setStateSet(getUnlitStateSet((*_edgeLineColor)[0])); } void SphereSegment::setSideColor(const osg::Vec4& c) { (*_sideColor)[0]=c; _sidesGeometry->setStateSet(getUnlitStateSet((*_sideColor)[0])); } void SphereSegment::setAllColors(const osg::Vec4& c) { setSurfaceColor(c); setSpokeColor(c); setEdgeLineColor(c); setSideColor(c); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SphereSegment intersection code. class PolytopeVisitor : public osg::NodeVisitor { public: typedef std::pair MatrixPolytopePair; typedef std::vector PolytopeStack; struct Hit { Hit(const osg::Matrix& matrix, osg::NodePath& nodePath, osg::Drawable* drawable): _matrix(matrix), _nodePath(nodePath), _drawable(drawable) {} osg::Matrix _matrix; osg::NodePath _nodePath; osg::ref_ptr _drawable; }; typedef std::vector HitList; PolytopeVisitor(const osg::Matrix& matrix, const osg::Polytope& polytope): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN) { _polytopeStack.push_back(MatrixPolytopePair()); _polytopeStack.back().first = matrix; _polytopeStack.back().second.setAndTransformProvidingInverse(polytope, _polytopeStack.back().first); } META_NodeVisitor("osgSim","PolytopeVisitor") void reset() { _polytopeStack.clear(); _hits.clear(); } void apply(osg::Node& node) { if (_polytopeStack.back().second.contains(node.getBound())) { traverse(node); } } void apply(osg::Transform& transform) { if (_polytopeStack.back().second.contains(transform.getBound())) { osg::Matrix matrix = _polytopeStack.back().first; transform.computeLocalToWorldMatrix(matrix, this); _polytopeStack.push_back(MatrixPolytopePair()); _polytopeStack.back().first = matrix; _polytopeStack.back().second.setAndTransformProvidingInverse(_polytopeStack.front().second, matrix); traverse(transform); _polytopeStack.pop_back(); } } void apply(osg::Drawable& drawable) { if (_polytopeStack.back().second.contains(drawable.getBoundingBox())) { _hits.push_back(Hit(_polytopeStack.back().first,getNodePath(),&drawable)); } } HitList& getHits() { return _hits; } protected: PolytopeStack _polytopeStack; HitList _hits; }; SphereSegment::LineList SphereSegment::computeIntersection(const osg::Matrixd& transform, osg::Node* subgraph) { OSG_INFO<<"Creating line intersection between sphere segment and subgraph."<accept(polytopeVisitor); if (polytopeVisitor.getHits().empty()) { OSG_INFO<<"No hits found."<_matrix, itr->_drawable.get()); all_lines.insert(all_lines.end(), lines.begin(), lines.end()); } // join all the lines that have ends that are close together.. return all_lines; } osg::Node* SphereSegment::computeIntersectionSubgraph(const osg::Matrixd& transform, osg::Node* subgraph) { OSG_INFO<<"Creating line intersection between sphere segment and subgraph."<accept(polytopeVisitor); if (polytopeVisitor.getHits().empty()) { OSG_INFO<<"No hits found."<addChild(computeIntersectionSubgraph(itr->_matrix, itr->_drawable.get())); } // join all the lines that have ends that are close together.. return group; } namespace SphereSegmentIntersector { struct dereference_less { template inline bool operator() (const T& lhs,const U& rhs) const { return *lhs < *rhs; } }; struct SortFunctor { typedef std::vector< osg::Vec3 > VertexArray; SortFunctor(VertexArray& vertices): _vertices(vertices) {} bool operator() (unsigned int p1, unsigned int p2) const { return _vertices[p1]<_vertices[p2]; } VertexArray& _vertices; protected: SortFunctor& operator = (const SortFunctor&) { return *this; } }; struct TriangleIntersectOperator { TriangleIntersectOperator(): _radius(-1.0f), _azMin(0.0f), _azMax(0.0f), _elevMin(0.0f), _elevMax(0.0f), _numOutside(0), _numInside(0), _numIntersecting(0) {} enum SurfaceType { NO_SURFACE = 0, RADIUS_SURFACE, LEFT_SURFACE, RIGHT_SURFACE, BOTTOM_SURFACE, TOP_SURFACE }; struct Triangle; struct Edge : public osg::Referenced { typedef std::vector TriangleList; enum IntersectionType { NO_INTERSECTION, POINT_1, POINT_2, MID_POINT, BOTH_ENDS }; Edge(unsigned int p1, unsigned int p2, SurfaceType intersectionEdge=NO_SURFACE): _intersectionType(NO_INTERSECTION), _intersectionVertexIndex(0), _p1Outside(false), _p2Outside(false), _intersectionEdge(intersectionEdge) { if (p1>p2) { _p1 = p2; _p2 = p1; } else { _p1 = p1; _p2 = p2; } } bool operator < (const Edge& edge) const { if (_p1edge._p1) return false; else return _p2 > EdgeList; struct Triangle : public osg::Referenced { Triangle(unsigned int p1, unsigned int p2, unsigned int p3): _p1(p1), _p2(p2), _p3(p3), _e1(0), _e2(0), _e3(0) { sort(); } bool operator < (const Triangle& rhs) const { if (_p1 < rhs._p1) return true; else if (_p1 > rhs._p1) return false; else if (_p2 < rhs._p2) return true; else if (_p2 > rhs._p2) return false; else return (_p3 < rhs._p3); } bool operator == (const Triangle& rhs) const { return (_p1 == rhs._p1) && (_p2 != rhs._p2) && (_p3 != rhs._p3); } bool operator != (const Triangle& rhs) const { return (_p1 != rhs._p1) || (_p2 != rhs._p2) || (_p3 != rhs._p3); } void sort() { if (_p1>_p2) std::swap(_p1,_p2); if (_p1>_p3) std::swap(_p1,_p3); if (_p2>_p3) std::swap(_p2,_p3); } Edge* oppositeActiveEdge(Edge* edge) { if (edge!=_e1 && edge!=_e2 && edge!=_e3) { OSG_INFO<<"Edge problem"<_intersectionType!=Edge::NO_INTERSECTION) return _e1; if (edge!=_e2 && _e2 && _e2->_intersectionType!=Edge::NO_INTERSECTION) return _e2; if (edge!=_e3 && _e3 && _e3->_intersectionType!=Edge::NO_INTERSECTION) return _e3; return 0; } unsigned int _p1; unsigned int _p2; unsigned int _p3; Edge* _e1; Edge* _e2; Edge* _e3; }; struct Region { enum Classification { INSIDE = -1, INTERSECTS = 0, OUTSIDE = 1 }; Region(): _radiusSurface(OUTSIDE), _leftRightSurfaces(OUTSIDE), _leftSurface(OUTSIDE), _rightSurface(OUTSIDE), _bottomSurface(OUTSIDE), _topSurface(OUTSIDE) {} void classify(const osg::Vec3& vertex, double radius2, double azimMin, double azimMax, double elevMin, double elevMax) { double azimCenter = (azimMax+azimMin)*0.5; double azimRange = (azimMax-azimMin)*0.5; double rad2 = vertex.length2(); double length_xy = sqrtf(vertex.x()*vertex.x() + vertex.y()*vertex.y()); double elevation = atan2((double)vertex.z(),length_xy); // radius surface if (rad2 > radius2) _radiusSurface = OUTSIDE; else if (rad2 < radius2) _radiusSurface = INSIDE; else _radiusSurface = INTERSECTS; // bottom surface if (elevationelevMin) _bottomSurface = INSIDE; else _bottomSurface = INTERSECTS; // top surface if (elevation>elevMax) _topSurface = OUTSIDE; else if (elevation0.0) _leftSurface = INSIDE; else _leftSurface = INTERSECTS; double dot_alphaMax = cos(azimMax)*vertex.x() - sin(azimMax)*vertex.y(); if (dot_alphaMax>0.0) _rightSurface = OUTSIDE; else if (dot_alphaMax<0.0) _rightSurface = INSIDE; else _rightSurface = INTERSECTS; double azim = atan2(vertex.x(),vertex.y()); double azimDelta1 = azim-azimCenter; double azimDelta2 = 2.0*osg::PI + azim-azimCenter; double azimDelta = std::min(fabs(azimDelta1), fabs(azimDelta2)); if (azimDelta>azimRange) { _leftRightSurfaces = OUTSIDE; } else if (azimDelta VertexArray; typedef std::vector< Region > RegionArray; typedef std::vector< bool > BoolArray; typedef std::vector< unsigned int > IndexArray; typedef std::vector< osg::ref_ptr > TriangleArray; typedef std::set< osg::ref_ptr, dereference_less > EdgeSet; VertexArray _originalVertices; RegionArray _regions; BoolArray _vertexInIntersectionSet; IndexArray _candidateVertexIndices; IndexArray _remapIndices; TriangleArray _triangles; EdgeSet _edges; osg::Vec3 _centre; double _radius; double _azMin, _azMax, _elevMin, _elevMax; unsigned int _numOutside; unsigned int _numInside; unsigned int _numIntersecting; SphereSegment::LineList _generatedLines; void computePositionAndRegions(const osg::Matrixd& matrix, osg::Vec3Array& array) { _originalVertices.resize(array.size()); _regions.resize(array.size()); _vertexInIntersectionSet.resize(array.size(), false); _candidateVertexIndices.clear(); double radius2 = _radius*_radius; for(unsigned int i=0; i_p1 = _remapIndices[(*titr)->_p1]; (*titr)->_p2 = _remapIndices[(*titr)->_p2]; (*titr)->_p3 = _remapIndices[(*titr)->_p3]; (*titr)->sort(); } } } void removeDuplicateTriangles() { OSG_INFO<<"Removing duplicate triangles : num triangles in "<<_triangles.size()<_e1 = addEdge(tri->_p1, tri->_p2, tri); tri->_e2 = addEdge(tri->_p2, tri->_p3, tri); tri->_e3 = addEdge(tri->_p1, tri->_p3, tri); } void buildEdges() { _edges.clear(); for(TriangleArray::iterator itr = _triangles.begin(); itr != _triangles.end(); ++itr) { Triangle* tri = itr->get(); RegionCounter rc; rc.add(_regions[tri->_p1]); rc.add(_regions[tri->_p2]); rc.add(_regions[tri->_p3]); int numIntersections = rc.numberOfIntersectingSurfaces(); if (numIntersections>=1) { buildEdges(tri); } } OSG_INFO<<"Number of edges "<<_edges.size()<get(); unsigned int numConnections = edge->_triangles.size(); if (numConnections==0) ++numZeroConnections; else if (numConnections==1) ++numSingleConnections; else if (numConnections==2) ++numDoubleConnections; else ++numMultiConnections; } OSG_INFO<<"Number of numZeroConnections "< new_edge = new Edge(p1, p2); EdgeSet::iterator itr = _edges.find(new_edge); if (itr==_edges.end()) { new_edge->addTriangle(tri); _edges.insert(new_edge); return new_edge.get(); } else { Edge* edge = const_cast(itr->get()); edge->addTriangle(tri); return edge; } } SphereSegment::LineList connectIntersections(EdgeList& hitEdges) { SphereSegment::LineList lineList; OSG_INFO<<"Number of edge intersections "<get(); edge->_toTraverse.clear(); //OSG_INFO<<"edge= "<_triangles.begin(); titr != edge->_triangles.end(); ++titr) { Triangle* tri = *titr; // count how many active edges there are on this triangle unsigned int numActiveEdges = 0; unsigned int numEdges = 0; if (tri->_e1 && tri->_e1->_intersectionType!=Edge::NO_INTERSECTION) ++numActiveEdges; if (tri->_e2 && tri->_e2->_intersectionType!=Edge::NO_INTERSECTION) ++numActiveEdges; if (tri->_e3 && tri->_e3->_intersectionType!=Edge::NO_INTERSECTION) ++numActiveEdges; if (tri->_e1) ++numEdges; if (tri->_e2) ++numEdges; if (tri->_e3) ++numEdges; // if we have one or more then add it into the edges to traverse list if (numActiveEdges>1) { //OSG_INFO<<" adding tri="<_toTraverse.push_back(tri); } // OSG_INFO<<"Number active edges "<get(); if (edge->_toTraverse.size()==1) break; } if (hitr == hitEdges.end()) { hitr = hitEdges.begin(); } // OSG_INFO<<"New line "<get(); while (edge) { // OSG_INFO<<" vertex "<_intersectionVertex<push_back(edge->_intersectionVertex+_centre/*+osg::Vec3(0.0f,0.0f,200.0f)*/); Edge* newEdge = 0; Triangle* tri = !(edge->_toTraverse.empty()) ? edge->_toTraverse.back() : 0; if (tri) { newEdge = tri->oppositeActiveEdge(edge); edge->removeFromToTraverseList(tri); newEdge->removeFromToTraverseList(tri); // OSG_INFO<<" tri="<_toTraverse.empty()) { edge->_intersectionType = Edge::NO_INTERSECTION; // remove edge for the hitEdges. hitr = std::find(hitEdges.begin(), hitEdges.end(), edge); if (hitr!=hitEdges.end()) hitEdges.erase(hitr); } // move on to next edge in line. edge = newEdge; } } return lineList; } template SphereSegment::LineList computeIntersections(I intersector) { // collect all the intersecting edges EdgeList hitEdges; for(EdgeSet::iterator itr = _edges.begin(); itr != _edges.end(); ++itr) { Edge* edge = const_cast(itr->get()); if (intersector(edge)) { hitEdges.push_back(edge); } } return connectIntersections(hitEdges); } template void trim(SphereSegment::LineList& lineList, osg::Vec3Array* sourceLine, I intersector) { if (sourceLine->empty()) return; // OSG_INFO<<"Testing line of "<size()<size()) { // find first valid vertex. for(; firstsize(); ++first) { if (intersector.distance((*sourceLine)[first]-_centre)>=0.0) break; } if (first==sourceLine->size()) { // OSG_INFO<<"No valid points found"<size(); ++last) { if (intersector.distance((*sourceLine)[last]-_centre)<0.0) break; } if (first==0 && last==sourceLine->size()) { // OSG_INFO<<"Copying complete line"<0) { newLine->push_back(intersector.intersectionPoint((*sourceLine)[first-1]-_centre, (*sourceLine)[first]-_centre)+_centre); } for(unsigned int i=first; ipush_back((*sourceLine)[i]); } if (lastsize()) { newLine->push_back(intersector.intersectionPoint((*sourceLine)[last-1]-_centre, (*sourceLine)[last]-_centre)+_centre); } lineList.push_back(newLine); } first = last; } } // handle a paired of surfaces that work to enclose a convex region, which means that // points can be inside either surface to be valid, and be outside both surfaces to be invalid. template void trim(SphereSegment::LineList& lineList, osg::Vec3Array* sourceLine, I intersector1, I intersector2) { if (sourceLine->empty()) return; // OSG_INFO<<"Testing line of "<size()<size()) { // find first valid vertex. for(; firstsize(); ++first) { osg::Vec3 v = (*sourceLine)[first]-_centre; if (intersector1.distance(v)>=0.0 || intersector2.distance(v)>=0.0 ) break; } if (first==sourceLine->size()) { // OSG_INFO<<"No valid points found"<size(); ++last) { osg::Vec3 v = (*sourceLine)[last]-_centre; if (intersector1.distance(v)<0.0 && intersector2.distance(v)<0.0 ) break; } if (first==0 && last==sourceLine->size()) { // OSG_INFO<<"Copying complete line"<0) { osg::Vec3 start = (*sourceLine)[first-1]-_centre; osg::Vec3 end = (*sourceLine)[first]-_centre; // work out which intersector to use by discounting the one that // isn't a plausible candidate. bool possible1 = intersector1.distance(end)>=0.0; bool possible2 = intersector2.distance(end)>=0.0; if (possible1 && possible2) { double start1 = intersector1.distance(start); double start2 = intersector2.distance(start); // need to check which intersection is latest. double end1 = intersector1.distance(end); double delta1 = (end1-start1); double end2 = intersector2.distance(end); double delta2 = (end2-start2); double r1 = fabs(delta1)>0.0 ? start1/delta1 : 0.0; double r2 = fabs(delta2)>0.0 ? start2/delta2 : 0.0; // choose intersection which is nearest the end point. if (r1push_back(intersector1.intersectionPoint(start, end)+_centre); } else { newLine->push_back(intersector2.intersectionPoint(start, end)+_centre); } } for(unsigned int i=first; ipush_back((*sourceLine)[i]); } if (lastsize()) { osg::Vec3 start = (*sourceLine)[last-1]-_centre; osg::Vec3 end = (*sourceLine)[last]-_centre; double start1 = intersector1.distance(start); double start2 = intersector2.distance(start); // work out which intersector to use by discounting the one that // isn't a plausible candidate. bool possible1 = start1>=0.0; bool possible2 = start2>=0.0; if (possible1 && possible2) { possible1 = intersector1.distance(end)<0.0; possible2 = intersector2.distance(end)<0.0; if (possible1 && possible2) { // need to check which intersection is latest. double end1 = intersector1.distance(end); double delta1 = (end1-start1); double end2 = intersector2.distance(end); double delta2 = (end2-start2); double r1 = fabs(delta1)>0.0 ? start1/delta1 : 0.0; double r2 = fabs(delta2)>0.0 ? start2/delta2 : 0.0; // choose intersection which is nearest the end point. if (r1>r2) { OSG_INFO<<"end point, 1 near to end than 2"<push_back(intersector1.intersectionPoint(start, end)+_centre); } else { newLine->push_back(intersector2.intersectionPoint(start, end)+_centre); } } lineList.push_back(newLine); } first = last; } } template void trim(SphereSegment::LineList& lineList, I intersector) { SphereSegment::LineList newLines; // collect all the intersecting edges for(SphereSegment::LineList::iterator itr = lineList.begin(); itr != lineList.end(); ++itr) { osg::Vec3Array* line = itr->get(); trim(newLines, line, intersector); } lineList.swap(newLines); } template void trim(SphereSegment::LineList& lineList, I intersector1, I intersector2) { SphereSegment::LineList newLines; // collect all the intersecting edges for(SphereSegment::LineList::iterator itr = lineList.begin(); itr != lineList.end(); ++itr) { osg::Vec3Array* line = itr->get(); trim(newLines, line, intersector1, intersector2); } lineList.swap(newLines); } struct LinePair { LinePair(osg::Vec3Array* line): _line(line), _lineEnd(0), _neighbourLine(0), _neighbourLineEnd(0), _distance(FLT_MAX) {} bool operator < (const LinePair& linePair) const { return _distance < linePair._distance; } void consider(osg::Vec3Array* testline) { if (_neighbourLine.valid()) { float distance = ((*_line)[0]-(*testline)[0]).length(); if (distance<_distance) { _lineEnd = 0; _neighbourLine = testline; _neighbourLineEnd = 0; _distance = distance; } distance = ((*_line)[0]-(*testline)[testline->size()-1]).length(); if (distance<_distance) { _lineEnd = 0; _neighbourLine = testline; _neighbourLineEnd = testline->size()-1; _distance = distance; } distance = ((*_line)[_line->size()-1]-(*testline)[0]).length(); if (distance<_distance) { _lineEnd = _line->size()-1; _neighbourLine = testline; _neighbourLineEnd = 0; _distance = distance; } distance = ((*_line)[_line->size()-1]-(*testline)[testline->size()-1]).length(); if (distance<_distance) { _lineEnd = _line->size()-1; _neighbourLine = testline; _neighbourLineEnd = testline->size()-1; _distance = distance; } } else { _neighbourLine = testline; if (_neighbourLine==_line) { _lineEnd = 0; _neighbourLineEnd = _neighbourLine->size()-1; _distance = ((*_line)[_lineEnd]-(*_neighbourLine)[_neighbourLineEnd]).length(); } else { _distance = ((*_line)[0]-(*_neighbourLine)[0]).length(); _lineEnd = 0; _neighbourLineEnd = 0; float distance = ((*_line)[0]-(*_neighbourLine)[_neighbourLine->size()-1]).length(); if (distance<_distance) { _lineEnd = 0; _neighbourLineEnd = _neighbourLine->size()-1; _distance = distance; } distance = ((*_line)[_line->size()-1]-(*_neighbourLine)[0]).length(); if (distance<_distance) { _lineEnd = _line->size()-1; _neighbourLineEnd = 0; _distance = distance; } distance = ((*_line)[_line->size()-1]-(*_neighbourLine)[_neighbourLine->size()-1]).length(); if (distance<_distance) { _lineEnd = _line->size()-1; _neighbourLineEnd = _neighbourLine->size()-1; _distance = distance; } } } }; bool contains(osg::Vec3Array* line) const { return _line==line || _neighbourLine==line; } osg::ref_ptr _line; unsigned int _lineEnd; osg::ref_ptr _neighbourLine; unsigned int _neighbourLineEnd; float _distance; }; void joinEnds(float fuseDistance, bool doFuse, bool allowJoinToSelf) { SphereSegment::LineList fusedLines; SphereSegment::LineList unfusedLines; // first separate the already fused lines from the unfused ones. for(SphereSegment::LineList::iterator itr = _generatedLines.begin(); itr != _generatedLines.end(); ++itr) { osg::Vec3Array* line = itr->get(); if (line->size()>=2) { if ((*line)[0]==(*line)[line->size()-1]) { fusedLines.push_back(line); } else { unfusedLines.push_back(line); } } } while (unfusedLines.size()>=1) { // generate a set of line pairs to establish which // line pair has the minimum distance. typedef std::multiset LinePairSet; LinePairSet linePairs; for(unsigned int i=0; i_line.get()<<" "<_lineEnd<<" neighbour "<_neighbourLine.get()<<" "<_neighbourLineEnd<<" distance="<_distance< fuseDistance) { OSG_INFO<<"Completed work, shortest distance left is "<size()-1])*0.5f; if (doFuse) { (*line)[0] = average; (*line)[line->size()-1] = average; } else { // add start of line to end. line->push_back((*line)[0]); } fusedLines.push_back(line); SphereSegment::LineList::iterator fitr = std::find(unfusedLines.begin(), unfusedLines.end(), line); if (fitr != unfusedLines.end()) { unfusedLines.erase(fitr); } else { OSG_INFO<<"Error couldn't find line in unfused list, exiting fusing loop."<size()-1 : 0; int direction1 = openEnd1size()-1 : 0; int direction2 = fuseEnd2push_back((*line1)[i]); } // add the average of the two fused ends if (doFuse) { osg::Vec3 average = ((*line1)[fuseEnd1] + (*line2)[fuseEnd2])*0.5f; newline->push_back(average); } else { newline->push_back((*line1)[fuseEnd1]); newline->push_back((*line2)[fuseEnd2]); } // copy across from the next point in from fuseEnd2 to the openEnd2. for(int j=fuseEnd2 + direction2; j != openEnd2 + direction2; j += direction2) { newline->push_back((*line2)[j]); } // remove line1 from unfused list. SphereSegment::LineList::iterator fitr = std::find(unfusedLines.begin(), unfusedLines.end(), line1); if (fitr != unfusedLines.end()) { unfusedLines.erase(fitr); } // remove line2 from unfused list. fitr = std::find(unfusedLines.begin(), unfusedLines.end(), line2); if (fitr != unfusedLines.end()) { unfusedLines.erase(fitr); } // add the newline into the unfused for further processing. unfusedLines.push_back(newline); OSG_INFO<<"Fusing two separate lines "<_intersectionType = TriangleIntersectOperator::Edge::NO_INTERSECTION; osg::Vec3& v1 = _tif._originalVertices[edge->_p1]; osg::Vec3& v2 = _tif._originalVertices[edge->_p2]; double d1 = _plane.distance(v1); double d2 = _plane.distance(v2); edge->_p1Outside = _lowerOutside ? (d1<0.0) : (d1>0.0); edge->_p2Outside = _lowerOutside ? (d2<0.0) : (d2>0.0); // if both points inside then discard if (d1<0.0 && d2<0.0) return false; // if both points outside then discard if (d1>0.0 && d2>0.0) return false; if (d1==0.0) { if (d2==0.0) { edge->_intersectionType = TriangleIntersectOperator::Edge::BOTH_ENDS; } else { edge->_intersectionType = TriangleIntersectOperator::Edge::POINT_1; } } else if (d2==0.0) { edge->_intersectionType = TriangleIntersectOperator::Edge::POINT_2; } else { double div = d2-d1; if (div==0.0) { edge->_intersectionType = TriangleIntersectOperator::Edge::NO_INTERSECTION; return false; } double r = -d1 / div; if (r<0.0 || r>1.0) { edge->_intersectionType = TriangleIntersectOperator::Edge::NO_INTERSECTION; return false; } // OSG_INFO<<"r = "<_intersectionType = TriangleIntersectOperator::Edge::MID_POINT; edge->_intersectionVertex = v1*one_minus_r + v2*r; } return true; } // compute the intersection between line segment and surface osg::Vec3 intersectionPoint(const osg::Vec3& v1, const osg::Vec3& v2) { double d1 = _plane.distance(v1); double d2 = _plane.distance(v2); double div = d2-d1; if (div==0.0) { return v1; } double r = -d1 / div; double one_minus_r = 1.0-r; return v1*one_minus_r + v2*r; } // positive distance to the inside. double distance(const osg::Vec3& v) { return _lowerOutside ? _plane.distance(v) : -_plane.distance(v) ; } protected: AzimPlaneIntersector& operator = (const AzimPlaneIntersector&) { return *this; } }; struct ElevationIntersector { ElevationIntersector(TriangleIntersectOperator& tif, double elev, bool lowerOutside): _tif(tif), _elev(elev), _lowerOutside(lowerOutside) {} TriangleIntersectOperator& _tif; double _elev; bool _lowerOutside; inline bool operator() (TriangleIntersectOperator::Edge* edge) { edge->_intersectionType = TriangleIntersectOperator::Edge::NO_INTERSECTION; osg::Vec3& v1 = _tif._originalVertices[edge->_p1]; osg::Vec3& v2 = _tif._originalVertices[edge->_p2]; double length_xy1 = sqrt(v1.x()*v1.x() + v1.y()*v1.y()); double elev1 = atan2((double)v1.z(),length_xy1); double length_xy2 = sqrt(v2.x()*v2.x() + v2.y()*v2.y()); double elev2 = atan2((double)v2.z(),length_xy2); edge->_p1Outside = _lowerOutside ? (elev1<_elev) : (elev1>_elev); edge->_p2Outside = _lowerOutside ? (elev2<_elev) : (elev2>_elev); // if both points inside then discard if (elev1<_elev && elev2<_elev) return false; // if both points outside then discard if (elev1>_elev && elev2>_elev) return false; if (elev1==_elev) { if (elev2==_elev) { edge->_intersectionType = TriangleIntersectOperator::Edge::BOTH_ENDS; } else { edge->_intersectionType = TriangleIntersectOperator::Edge::POINT_1; } } else if (elev2==_elev) { edge->_intersectionType = TriangleIntersectOperator::Edge::POINT_2; } else { double dx = v2.x()-v1.x(); double dy = v2.y()-v1.y(); double dz = v2.z()-v1.z(); double t = tan(_elev); double tt = t*t; double a = dz*dz-tt*(dx*dx + dy*dy); double b = 2.0*(v1.z()*dz - tt*(v1.x()*dx + v1.y()*dy)); double c = v1.z()*v1.z() - tt*(v1.x()*v1.x() + v1.y()*v1.y()); double s1, s2; if (!computeQuadraticSolution(a,b,c,s1,s2)) { edge->_intersectionType = TriangleIntersectOperator::Edge::NO_INTERSECTION; return false; } double r = 0.0; if (s1>=0.0 && s1<=1.0) { r = s1; } else if (s2>=0.0 && s2<=1.0) { r = s2; } else { OSG_INFO<<"neither segment intersects s1="<