/* -*-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 using namespace osgSim; namespace ElevationSliceUtils { struct DistanceHeightCalculator { DistanceHeightCalculator(osg::EllipsoidModel* em, const osg::Vec3d& startPoint, osg::Vec3d& endPoint): _em(em), _startPoint(startPoint), _startNormal(startPoint), _endPoint(endPoint), _endNormal(endPoint) { double latitude, longitude, height; // set up start point variables _em->convertXYZToLatLongHeight(_startPoint.x(), _startPoint.y(), _startPoint.z(), latitude, longitude, height); _startRadius = _startPoint.length() - height; _startNormal.normalize(); // set up end point variables _em->convertXYZToLatLongHeight(_endPoint.x(), _endPoint.y(), _endPoint.z(), latitude, longitude, height); _endRadius = _endPoint.length() - height; _endNormal.normalize(); osg::Vec3d normal = _startNormal ^ _endNormal; normal.normalize(); _angleIncrement = 0.005; _radiusList.push_back(_startRadius); _distanceList.push_back(0.0); osg::Matrixd rotationMatrix; double angleBetweenStartEnd = acos( _startNormal * _endNormal ); double prevRadius = _startRadius; double distance = 0.0; for(double angle = _angleIncrement; angle < angleBetweenStartEnd; angle += _angleIncrement) { rotationMatrix.makeRotate(angle, normal); osg::Vec3d newVector = osg::Matrixd::transform3x3(_startPoint, rotationMatrix); _em->convertXYZToLatLongHeight(newVector.x(), newVector.y(), newVector.z(), latitude, longitude, height); double newRadius = newVector.length() - height; double distanceIncrement = _angleIncrement * (newRadius + prevRadius) *0.5; distance += distanceIncrement; _radiusList.push_back(newRadius); _distanceList.push_back(distance); // osg::notify(osg::NOTICE)<<" newVector = "<height)<<" p2 = "<<(_p2->distance)<<" "<<(_p2->height)<distance - rhs._p1->distance; if (fabs(delta_distance) < epsilon) { if (fabs(_p2->height - rhs._p1->height) < epsilon) return JOINED; } if (delta_distance==0.0) { return SEPERATE; } if (rhs._p2->distance < _p1->distance || _p2->distance < rhs._p1->distance) return SEPERATE; bool rhs_p1_inside = (_p1->distance <= rhs._p1->distance) && (rhs._p1->distance <= _p2->distance); bool rhs_p2_inside = (_p1->distance <= rhs._p2->distance) && (rhs._p2->distance <= _p2->distance); if (rhs_p1_inside && rhs_p2_inside) return ENCLOSING; bool p1_inside = (rhs._p1->distance <= _p1->distance) && (_p1->distance <= rhs._p2->distance); bool p2_inside = (rhs._p1->distance <= _p2->distance) && (_p2->distance <= rhs._p2->distance); if (p1_inside && p2_inside) return ENCLOSED; if (rhs_p1_inside || rhs_p2_inside || p1_inside || p2_inside) return OVERLAPPING; return UNCLASSIFIED; } double height(double d) const { double delta = (_p2->distance - _p1->distance); return _p1->height + ((_p2->height - _p1->height) * (d - _p1->distance) / delta); } double deltaHeight(Point& point) const { return point.height - height(point.distance); } Point* createPoint(double d) const { if (d == _p1->distance) return _p1.get(); if (d == _p2->distance) return _p2.get(); double delta = (_p2->distance - _p1->distance); double r = (d - _p1->distance)/delta; double one_minus_r = 1.0 - r; return new Point(d, _p1->height * one_minus_r + _p2->height * r, _p1->position * one_minus_r + _p2->position * r); } Point* createIntersectionPoint(const Segment& rhs) const { double A = _p1->distance; double B = _p2->distance - _p1->distance; double C = _p1->height; double D = _p2->height - _p1->height; double E = rhs._p1->distance; double F = rhs._p2->distance - rhs._p1->distance; double G = rhs._p1->height; double H = rhs._p2->height - rhs._p1->height; double div = D*F - B*H; if (div==0.0) { osg::notify(osg::NOTICE)<<"ElevationSlideUtils::Segment::createIntersectionPoint(): error Segments are parallel."<height<<" p2 = "<<_p2->distance<<" "<<_p2->height<height<<" p2 = "<distance<<" "<height<position + (_p2->position - _p1->position)*r); } osg::ref_ptr _p1; osg::ref_ptr _p2; }; struct LineConstructor { typedef std::set SegmentSet; LineConstructor() {} void add(double d, double h, const osg::Vec3d& pos) { osg::ref_ptr newPoint = new Point(d,h,pos); if (_previousPoint.valid() && newPoint->distance != _previousPoint->distance) { const double maxGradient = 100.0; double gradient = fabs( (newPoint->height - _previousPoint->height) / (newPoint->distance - _previousPoint->distance) ); if (gradient < maxGradient) { _segments.insert( Segment(_previousPoint.get(), newPoint.get()) ); } } _previousPoint = newPoint; } void endline() { _previousPoint = 0; } void report() { osg::notify(osg::NOTICE)<<"Number of segments = "<<_segments.size()<height)<<" p2 = "<<(seg._p2->distance)<<" "<<(seg._p2->height)<<"\t"; SegmentSet::iterator nextItr = itr; ++nextItr; if (nextItr != _segments.end()) { Segment::Classification classification = itr->compare(*nextItr); switch(classification) { case(Segment::IDENTICAL): osg::notify(osg::NOTICE)<<"i"; break; case(Segment::SEPERATE): osg::notify(osg::NOTICE)<<"s"<position; _em->convertXYZToLatLongHeight(p.x(), p.y(), p.z(), latitude, longitude, height); double delta1 = height - s._p1->height; p = s._p1->position; _em->convertXYZToLatLongHeight(p.x(), p.y(), p.z(), latitude, longitude, height); double delta2 = height - s._p2->height; if (delta1>0.0 || delta2>0.0) { osg::notify(osg::NOTICE)<<" "<<&s<<" computed height delta ="<=Segment::OVERLAPPING) { switch(classification) { case(Segment::OVERLAPPING): { // cases.... // compute new end points for both segments // need to work out which points are overlapping - lhs_p2 && rhs_p1 or lhs_p1 and rhs_p2 // also need to check for cross cases. const Segment& lhs = *itr; const Segment& rhs = *nextItr; bool rhs_p1_inside = (lhs._p1->distance <= rhs._p1->distance) && (rhs._p1->distance <= lhs._p2->distance); bool lhs_p2_inside = (rhs._p1->distance <= lhs._p2->distance) && (lhs._p2->distance <= rhs._p2->distance); if (rhs_p1_inside && lhs_p2_inside) { double distance_between = osg::Vec2d(lhs._p2->distance - rhs._p1->distance, lhs._p2->height - rhs._p1->height).length2(); if (distance_between < epsilon) { // osg::notify(osg::NOTICE)<<"OVERLAPPING : distance_between acceptable "<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance) ); Segment seg2( rhs._p1.get(), cp ); Segment seg3( cp, lhs._p2.get() ); Segment seg4( rhs.createPoint(lhs._p2->distance), lhs._p2.get() ); _segments.erase(nextItr); _segments.erase(itr); _segments.insert(seg1); _segments.insert(seg2); _segments.insert(seg3); _segments.insert(seg4); itr = _segments.find(seg1); nextItr = itr; ++nextItr; } else if (dh1 <= 0.0 && dh2 <= 0.0) { // osg::notify(osg::NOTICE)<<"++ OVERLAPPING : rhs below lhs "<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance), rhs._p2.get()); _segments.erase(nextItr); _segments.insert(newSeg); nextItr = itr; ++nextItr; // osg::notify(osg::NOTICE)<<" newSeg_p1 "<distance<<" "<height<distance<<" "<height<= 0.0 && dh2 >= 0.0) { // osg::notify(osg::NOTICE)<<"++ OVERLAPPING : rhs above lhs "<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance)); _segments.erase(itr); _segments.insert(newSeg); itr = _segments.find(newSeg); nextItr = itr; ++nextItr; // osg::notify(osg::NOTICE)<<" newSeg_p1 "<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<=0.0 && dh2>=0.0) { double d_left = enclosed._p1->distance - enclosing._p1->distance; double d_right = enclosing._p2->distance - enclosed._p2->distance; if (d_left < epsilon && d_right < epsilon) { // treat ENCLOSED as ENCLOSING. // osg::notify(osg::NOTICE)<<" Treat enclosed above as enclosing "<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance), enclosing._p2.get()); nextItr = itr; ++nextItr; _segments.erase(itr); _segments.insert(newSeg); itr = nextItr; ++nextItr; // osg::notify(osg::NOTICE)<<" newSeg_p1 "<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance) ); _segments.insert(newSeg); _segments.erase(itr); itr = _segments.find(newSeg); nextItr = itr; ++nextItr; // osg::notify(osg::NOTICE)<<" newSeg_p1 "<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance) ); Segment newSegRight(enclosing.createPoint(enclosed._p2->distance), enclosing._p2.get()); _segments.erase(itr); _segments.insert(newSegLeft); _segments.insert(newSegRight); itr = _segments.find(newSegLeft); nextItr = itr; ++nextItr; // osg::notify(osg::NOTICE)<<" newSegLeft_p1 "<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance - enclosing._p1->distance; double d_right = enclosing._p2->distance - enclosed._p2->distance; if (d_left < epsilon && d_right < epsilon) { // treat ENCLOSED as ENCLOSING. if (dh1 < 0.0) { // osg::notify(osg::NOTICE)<<" >> enclosing left side is above enclosed left side"<> enclosing left side is above enclosed left side"<> Replace enclosing with right section"<> enclosing left side is above enclosed left side"<distance), enclosing._p2.get()); _segments.erase(itr); _segments.erase(nextItr); _segments.insert(newSegLeft); _segments.insert(newSegMid); _segments.insert(newSegRight); itr = _segments.find(newSegLeft); nextItr = itr; ++nextItr; } else { // osg::notify(osg::NOTICE)<<" >> enclosing left side is above enclosed left side"<> Replace enclosing with left section"<> enclosing left side is above enclosed left side"<> enclosing left side is above enclosed left side"<distance)); Segment newSegMid(enclosed._p1.get(), cp); Segment newSegRight(cp, enclosing._p2.get()); _segments.erase(itr); _segments.erase(nextItr); _segments.insert(newSegLeft); _segments.insert(newSegMid); _segments.insert(newSegRight); itr = _segments.find(newSegLeft); nextItr = itr; ++nextItr; } } else { // osg::notify(osg::NOTICE)<<" >> Replace enclosing with left and right sections"<> enclosing left side is above enclosed left side"<distance), enclosing._p2.get()); _segments.erase(itr); _segments.erase(nextItr); _segments.insert(newSegLeft); _segments.insert(newSegMid); _segments.insert(newSegRight); itr = _segments.find(newSegLeft); nextItr = itr; ++nextItr; } else { // osg::notify(osg::NOTICE)<<" >> enclosing left side is above enclosed left side"<distance)); Segment newSegMid(enclosed._p1.get(), cp); Segment newSegRight(cp, enclosing._p2.get()); _segments.erase(itr); _segments.erase(nextItr); _segments.insert(newSegLeft); _segments.insert(newSegMid); _segments.insert(newSegRight); itr = _segments.find(newSegLeft); nextItr = itr; ++nextItr; } } } else { osg::notify(osg::NOTICE)<<"ENCLOSING: ENCLOSING - error case "<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance - enclosing._p1->distance; double d_right = enclosing._p2->distance - enclosed._p2->distance; if (d_left<=epsilon) { if (dh1<=epsilon && dh2<=epsilon) { // osg::notify(osg::NOTICE)<<"+++ ENCLOSED: ENCLOSING is above enclosed "<=0.0 && dh2>=0.0) { // osg::notify(osg::NOTICE)<<"ENCLOSED: ENCLOSING is below enclosed "<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance), enclosed._p2.get())); _segments.erase(nextItr); nextItr = itr; ++nextItr; } else if (dh1 * dh2 < 0.0) { // osg::notify(osg::NOTICE)<<"ENCLOSED: ENCLOSING is crossing enclosed "<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance), enclosing._p2.get()); _segments.erase(itr); _segments.erase(nextItr); _segments.insert(segLeft); _segments.insert(segMid); _segments.insert(segRight); itr = _segments.find(segLeft); nextItr = itr; ++nextItr; } } } else { osg::notify(osg::NOTICE)<<"ENCLOSED: ENCLOSING - error case "<distance<<" "<height<distance<<" "<height<distance<<" "<height<distance<<" "<height<compare(*nextItr) : Segment::UNCLASSIFIED; } } } unsigned int numOverlapping(SegmentSet::const_iterator startItr) const { if (startItr==_segments.end()) return 0; SegmentSet::const_iterator nextItr = startItr; ++nextItr; unsigned int num = 0; while (nextItr!=_segments.end() && startItr->compare(*nextItr)>=Segment::OVERLAPPING) { ++num; ++nextItr; } return num; } unsigned int totalNumOverlapping() const { unsigned int total = 0; for(SegmentSet::const_iterator itr = _segments.begin(); itr != _segments.end(); ++itr) { total += numOverlapping(itr); } return total; } void copyPoints(ElevationSlice::Vec3dList& intersections, ElevationSlice::DistanceHeightList& distanceHeightIntersections) { SegmentSet::iterator prevItr = _segments.begin(); SegmentSet::iterator nextItr = prevItr; ++nextItr; intersections.push_back( prevItr->_p1->position ); distanceHeightIntersections.push_back( ElevationSlice::DistanceHeight(prevItr->_p1->distance, prevItr->_p1->height) ); intersections.push_back( prevItr->_p2->position ); distanceHeightIntersections.push_back( ElevationSlice::DistanceHeight(prevItr->_p2->distance, prevItr->_p2->height) ); for(; nextItr != _segments.end(); ++nextItr,++prevItr) { Segment::Classification classification = prevItr->compare(*nextItr); switch(classification) { case(Segment::SEPERATE): { intersections.push_back( nextItr->_p1->position ); distanceHeightIntersections.push_back( ElevationSlice::DistanceHeight(nextItr->_p1->distance, nextItr->_p1->height) ); intersections.push_back( nextItr->_p2->position ); distanceHeightIntersections.push_back( ElevationSlice::DistanceHeight(nextItr->_p2->distance, nextItr->_p2->height) ); break; } case(Segment::JOINED): { #if 1 intersections.push_back( nextItr->_p2->position ); distanceHeightIntersections.push_back( ElevationSlice::DistanceHeight(nextItr->_p2->distance, nextItr->_p2->height) ); #else intersections.push_back( nextItr->_p1->position ); distanceHeightIntersections.push_back( ElevationSlice::DistanceHeight(nextItr->_p1->distance, nextItr->_p1->height) ); intersections.push_back( nextItr->_p2->position ); distanceHeightIntersections.push_back( ElevationSlice::DistanceHeight(nextItr->_p2->distance, nextItr->_p2->height) ); #endif break; } default: { intersections.push_back( nextItr->_p1->position ); distanceHeightIntersections.push_back( ElevationSlice::DistanceHeight(nextItr->_p1->distance, nextItr->_p1->height) ); intersections.push_back( nextItr->_p2->position ); distanceHeightIntersections.push_back( ElevationSlice::DistanceHeight(nextItr->_p2->distance, nextItr->_p2->height) ); break; } } } } SegmentSet _segments; osg::ref_ptr _previousPoint; osg::Plane _plane; osg::ref_ptr _em; }; } ElevationSlice::ElevationSlice() { setDatabaseCacheReadCallback(new DatabaseCacheReadCallback); } void ElevationSlice::computeIntersections(osg::Node* scene, osg::Node::NodeMask traversalMask) { osg::CoordinateSystemNode* csn = dynamic_cast(scene); osg::EllipsoidModel* em = csn ? csn->getEllipsoidModel() : 0; osg::Plane plane; osg::Polytope boundingPolytope; if (em) { osg::Vec3d start_upVector = em->computeLocalUpVector(_startPoint.x(), _startPoint.y(), _startPoint.z()); osg::Vec3d end_upVector = em->computeLocalUpVector(_endPoint.x(), _endPoint.y(), _endPoint.z()); double start_latitude, start_longitude, start_height; em->convertXYZToLatLongHeight(_startPoint.x(), _startPoint.y(), _startPoint.z(), start_latitude, start_longitude, start_height); osg::notify(osg::NOTICE)<<"start_lat = "< geode = new osg::Geode; for(itr = intersections.begin(); itr != intersections.end(); ++itr) { osgUtil::PlaneIntersector::Intersection& intersection = *itr; osg::Geometry* geometry = new osg::Geometry; osg::Vec3Array* vertices = new osg::Vec3Array; vertices->reserve(intersection.polyline.size()); for(Polyline::iterator pitr = intersection.polyline.begin(); pitr != intersection.polyline.end(); ++pitr) { vertices->push_back(*pitr); } geometry->setVertexArray( vertices ); geometry->addPrimitiveSet( new osg::DrawArrays(GL_LINE_STRIP, 0, vertices->size()) ); osg::Vec4Array* colours = new osg::Vec4Array; colours->push_back( osg::Vec4(1.0f,1.0f,1.0f,1.0f) ); geometry->setColorArray( colours ); geode->addDrawable( geometry ); geode->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF ); } osgDB::writeNodeFile(*geode, "raw.osg"); #endif ElevationSliceUtils::LineConstructor constructor; constructor._plane = plane; constructor._em = em; if (em) { ElevationSliceUtils::DistanceHeightCalculator dhc(em, _startPoint, _endPoint); // convert into distance/height for(itr = intersections.begin(); itr != intersections.end(); ++itr) { osgUtil::PlaneIntersector::Intersection& intersection = *itr; if (intersection.attributes.size()!=intersection.polyline.size()) continue; Attributes::iterator aitr = intersection.attributes.begin(); for(Polyline::iterator pitr = intersection.polyline.begin(); pitr != intersection.polyline.end(); ++pitr, ++aitr) { const osg::Vec3d& v = *pitr; double distance, height; dhc.computeDistanceHeight(v, distance, height); double pi_height = *aitr; // osg::notify(osg::NOTICE)<<"computed height = "<