OpenSceneGraph/src/osgSim/SphereSegment.cpp
2005-11-17 13:35:53 +00:00

3042 lines
103 KiB
C++

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 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 <osgSim/SphereSegment>
#include <osg/Notify>
#include <osg/CullFace>
#include <osg/LineWidth>
#include <osg/Transform>
#include <osg/Geometry>
#include <osg/TriangleIndexFunctor>
#include <osg/ShapeDrawable>
#include <osg/io_utils>
#include <algorithm>
#include <list>
using namespace osgSim;
// Define the collection of nested classes, all Drawables, which make
// up the parts of the sphere segment.
/**
SphereSegment::Surface is the Drawable which represents the specified area of the
sphere's surface.
*/
class SphereSegment::Surface: public osg::Drawable
{
public:
Surface(SphereSegment* ss): osg::Drawable(), _ss(ss) {}
Surface():_ss(0)
{
osg::notify(osg::WARN)<<
"Warning: unexpected call to osgSim::SphereSegment::Surface() default constructor"<<std::endl;
}
Surface(const Surface& rhs, const osg::CopyOp& co=osg::CopyOp::SHALLOW_COPY):osg::Drawable(rhs,co), _ss(0)
{
osg::notify(osg::WARN)<<
"Warning: unexpected call to osgSim::SphereSegment::Surface() copy constructor"<<std::endl;
}
META_Object(osgSim,Surface)
void drawImplementation(osg::State& state) const;
virtual osg::BoundingBox computeBound() const;
protected:
private:
SphereSegment* _ss;
};
void SphereSegment::Surface::drawImplementation(osg::State& state) const
{
_ss->Surface_drawImplementation(state);
}
osg:: BoundingBox SphereSegment::Surface::computeBound() const
{
osg:: BoundingBox bbox;
_ss->Surface_computeBound(bbox);
return bbox;
}
/**
SphereSegment::EdgeLine is the Drawable which represents the line around the edge
of the specified area of the sphere's EdgeLine.
*/
class SphereSegment::EdgeLine: public osg::Drawable
{
public:
EdgeLine(SphereSegment* ss): osg::Drawable(), _ss(ss) { init(); }
EdgeLine():_ss(0)
{
init();
osg::notify(osg::WARN)<<
"Warning: unexpected call to osgSim::SphereSegment::EdgeLine() default constructor"<<std::endl;
}
EdgeLine(const EdgeLine& rhs, const osg::CopyOp& co=osg::CopyOp::SHALLOW_COPY):osg::Drawable(rhs,co), _ss(0)
{
osg::notify(osg::WARN)<<
"Warning: unexpected call to osgSim::SphereSegment::EdgeLine() copy constructor"<<std::endl;
}
META_Object(osgSim,EdgeLine)
void drawImplementation(osg::State& state) const;
protected:
void init()
{
// switch off lighting.
getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
//getOrCreateStateSet()->setAttributeAndModes(new osg::LineWidth(2.0),osg::StateAttribute::OFF);
}
virtual osg::BoundingBox computeBound() const;
private:
SphereSegment* _ss;
};
void SphereSegment::EdgeLine::drawImplementation(osg::State& state) const
{
_ss->EdgeLine_drawImplementation(state);
}
osg::BoundingBox SphereSegment::EdgeLine::computeBound() const
{
osg::BoundingBox bbox;
_ss->EdgeLine_computeBound(bbox);
return bbox;
}
/**
SphereSegment::Side is a Drawable which represents one of the
planar areas, at either the minimum or maxium azimuth.
*/
class SphereSegment::Side: public osg::Drawable
{
public:
Side(SphereSegment* ss, SphereSegment::SideOrientation po, SphereSegment::BoundaryAngle pa):
osg::Drawable(), _ss(ss), _planeOrientation(po), _BoundaryAngle(pa) {}
Side():_ss(0)
{
osg::notify(osg::WARN)<<
"Warning: unexpected call to osgSim::SphereSegment::Side() default constructor"<<std::endl;
}
Side(const Side& rhs, const osg::CopyOp& co=osg:: CopyOp::SHALLOW_COPY): osg::Drawable(rhs,co), _ss(0)
{
osg::notify(osg::WARN)<<
"Warning: unexpected call to osgSim::SphereSegment::Side() copy constructor"<<std::endl;
}
META_Object(osgSim,Side)
void drawImplementation(osg::State& state) const;
protected:
virtual osg::BoundingBox computeBound() const;
private:
SphereSegment* _ss;
SphereSegment::SideOrientation _planeOrientation;
SphereSegment::BoundaryAngle _BoundaryAngle;
};
void SphereSegment::Side::drawImplementation(osg::State& state) const
{
_ss->Side_drawImplementation(state, _planeOrientation, _BoundaryAngle);
}
osg::BoundingBox SphereSegment::Side::computeBound() const
{
osg::BoundingBox bbox;
_ss->Side_computeBound(bbox, _planeOrientation, _BoundaryAngle);
return bbox;
}
/**
SphereSegment::Spoke is a Drawable which represents a spoke.
*/
class SphereSegment::Spoke: public osg::Drawable
{
public:
Spoke(SphereSegment* ss, SphereSegment::BoundaryAngle azAngle, SphereSegment::BoundaryAngle elevAngle):
osg::Drawable(), _ss(ss), _azAngle(azAngle), _elevAngle(elevAngle) { init(); }
Spoke():_ss(0)
{
init();
osg::notify(osg::WARN)<<
"Warning: unexpected call to osgSim::SphereSegment::Spoke() default constructor"<<std::endl;
}
Spoke(const Spoke& rhs, const osg::CopyOp& co=osg:: CopyOp::SHALLOW_COPY): osg::Drawable(rhs,co), _ss(0)
{
osg::notify(osg::WARN)<<
"Warning: unexpected call to osgSim::SphereSegment::Spoke() copy constructor"<<std::endl;
}
META_Object(osgSim,Spoke)
void drawImplementation(osg::State& state) const;
protected:
void init()
{
// switch off lighting.
getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
//getOrCreateStateSet()->setAttributeAndModes(new osg::LineWidth(2.0),osg::StateAttribute::OFF);
}
virtual osg::BoundingBox computeBound() const;
private:
SphereSegment* _ss;
SphereSegment::BoundaryAngle _azAngle, _elevAngle;
};
void SphereSegment::Spoke::drawImplementation(osg::State& state) const
{
_ss->Spoke_drawImplementation(state, _azAngle, _elevAngle);
}
osg::BoundingBox SphereSegment::Spoke::computeBound() const
{
osg::BoundingBox bbox;
_ss->Spoke_computeBound(bbox, _azAngle, _elevAngle);
return bbox;
}
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::setCentre(const osg::Vec3& c)
{
_centre = c;
dirtyAllDrawableDisplayLists();
dirtyAllDrawableBounds();
dirtyBound();
}
const osg::Vec3& SphereSegment::getCentre() const
{
return _centre;
}
void SphereSegment::setRadius(float r)
{
_radius = r;
dirtyAllDrawableDisplayLists();
dirtyAllDrawableBounds();
dirtyBound();
}
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 elev = asin(vec.z()); // Elevation angle
elevRange /= 2.0f;
_elevMin = elev - elevRange;
_elevMax = elev + elevRange;
// Calculate the azimuth range, cater for trig ambiguities
float xyLen = cos(elev);
float az;
if(vec.x() != 0.0f) az = asin(vec.x()/xyLen);
else az = acos(vec.y()/xyLen);
azRange /= 2.0f;
_azMin = az - azRange;
_azMax = az + azRange;
dirtyAllDrawableDisplayLists();
dirtyAllDrawableBounds();
dirtyBound();
}
void SphereSegment::getArea(osg::Vec3& vec, float& azRange, float& elevRange) const
{
azRange = _azMax - _azMin;
elevRange = _elevMax - _elevMin;
float az = azRange/2.0f;
float elev = elevRange/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;
dirtyAllDrawableDisplayLists();
dirtyAllDrawableBounds();
dirtyBound();
}
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;
dirtyAllDrawableDisplayLists();
}
int SphereSegment::getDensity() const
{
return _density;
}
void SphereSegment::init()
{
addDrawable(new Surface(this));
addDrawable(new EdgeLine(this));
addDrawable(new Side(this,AZIM,MIN));
addDrawable(new Side(this,AZIM,MAX));
addDrawable(new Side(this,ELEV,MIN));
addDrawable(new Side(this,ELEV,MAX));
addDrawable(new Spoke(this,MIN,MIN));
addDrawable(new Spoke(this,MIN,MAX));
addDrawable(new Spoke(this,MAX,MIN));
addDrawable(new Spoke(this,MAX,MAX));
}
void SphereSegment::dirtyAllDrawableDisplayLists()
{
for(DrawableList::iterator itr = _drawables.begin();
itr != _drawables.end();
++itr)
{
(*itr)->dirtyDisplayList();
}
}
void SphereSegment::dirtyAllDrawableBounds()
{
for(DrawableList::iterator itr = _drawables.begin();
itr != _drawables.end();
++itr)
{
(*itr)->dirtyBound();
}
}
void SphereSegment::Surface_drawImplementation(osg::State& /* state */) const
{
const float azIncr = (_azMax - _azMin)/_density;
const float elevIncr = (_elevMax - _elevMin)/_density;
// Draw the area on the sphere surface if needed
// ---------------------------------------------
if(_drawMask & SURFACE)
{
glColor4fv(_surfaceColor.ptr());
bool drawBackSide = true;
bool drawFrontSide = true;
// draw back side.
if (drawBackSide)
{
for(int i=0; i+1<=_density; i++)
{
// Because we're drawing quad strips, we need to work out
// two azimuth values, to form each edge of the (z-vertical)
// strips
float az1 = _azMin + (i*azIncr);
float az2 = _azMin + ((i+1)*azIncr);
glBegin(GL_QUAD_STRIP);
for (int j=0; j<=_density; j++)
{
float elev = _elevMin + (j*elevIncr);
// QuadStrip Edge formed at az1
// ----------------------------
// Work out the sphere normal
float x = cos(elev)*sin(az1);
float y = cos(elev)*cos(az1);
float z = sin(elev);
glNormal3f(-x, -y, -z);
glVertex3f(_centre.x() + _radius*x,
_centre.y() + _radius*y,
_centre.z() + _radius*z);
// QuadStrip Edge formed at az2
// ----------------------------
// Work out the sphere normal
x = cos(elev)*sin(az2);
y = cos(elev)*cos(az2);
// z = sin(elev); z doesn't change
glNormal3f(-x, -y, -z);
glVertex3f(_centre.x() + _radius*x,
_centre.y() + _radius*y,
_centre.z() + _radius*z);
}
glEnd();
}
}
// draw front side
if (drawFrontSide)
{
for(int i=0; i+1<=_density; i++)
{
// Because we're drawing quad strips, we need to work out
// two azimuth values, to form each edge of the (z-vertical)
// strips
float az1 = _azMin + (i*azIncr);
float az2 = _azMin + ((i+1)*azIncr);
glBegin(GL_QUAD_STRIP);
for (int j=0; j<=_density; j++)
{
float elev = _elevMin + (j*elevIncr);
// QuadStrip Edge formed at az1
// ----------------------------
// Work out the sphere normal
float x = cos(elev)*sin(az2);
float y = cos(elev)*cos(az2);
float z = sin(elev);
glNormal3f(x, y, z);
glVertex3f(_centre.x() + _radius*x,
_centre.y() + _radius*y,
_centre.z() + _radius*z);
// QuadStrip Edge formed at az2
// ----------------------------
// Work out the sphere normal
// z = sin(elev); z doesn't change
x = cos(elev)*sin(az1);
y = cos(elev)*cos(az1);
glNormal3f(x, y, z);
glVertex3f(_centre.x() + _radius*x,
_centre.y() + _radius*y,
_centre.z() + _radius*z);
}
glEnd();
}
}
}
}
bool SphereSegment::Surface_computeBound(osg::BoundingBox& bbox) const
{
bbox.init();
float azIncr = (_azMax - _azMin)/_density;
float elevIncr = (_elevMax - _elevMin)/_density;
for(int i=0; i<=_density; i++){
float az = _azMin + (i*azIncr);
for(int j=0; j<=_density; j++){
float elev = _elevMin + (j*elevIncr);
bbox.expandBy(
osg::Vec3(_centre.x() + _radius*cos(elev)*sin(az),
_centre.y() + _radius*cos(elev)*cos(az),
_centre.z() + _radius*sin(elev))
);
}
}
return true;
}
void SphereSegment::EdgeLine_drawImplementation(osg::State& /* state */) const
{
const float azIncr = (_azMax - _azMin)/_density;
const float elevIncr = (_elevMax - _elevMin)/_density;
// Draw the edgeline if necessary
// ------------------------------
if(_drawMask & EDGELINE)
{
glColor4fv(_edgeLineColor.ptr());
// Top edge
glBegin(GL_LINE_STRIP);
int i;
for(i=0; i<=_density; i++)
{
float az = _azMin + (i*azIncr);
glVertex3f(
_centre.x() + _radius*cos(_elevMax)*sin(az),
_centre.y() + _radius*cos(_elevMax)*cos(az),
_centre.z() + _radius*sin(_elevMax));
}
glEnd();
// Bottom edge
glBegin(GL_LINE_STRIP);
for(i=0; i<=_density; i++)
{
float az = _azMin + (i*azIncr);
glVertex3f(
_centre.x() + _radius*cos(_elevMin)*sin(az),
_centre.y() + _radius*cos(_elevMin)*cos(az),
_centre.z() + _radius*sin(_elevMin));
}
glEnd();
// Left edge
glBegin(GL_LINE_STRIP);
int j;
for(j=0; j<=_density; j++)
{
float elev = _elevMin + (j*elevIncr);
glVertex3f(
_centre.x() + _radius*cos(elev)*sin(_azMin),
_centre.y() + _radius*cos(elev)*cos(_azMin),
_centre.z() + _radius*sin(elev));
}
glEnd();
// Right edge
glBegin(GL_LINE_STRIP);
for(j=0; j<=_density; j++)
{
float elev = _elevMin + (j*elevIncr);
glVertex3f(
_centre.x() + _radius*cos(elev)*sin(_azMax),
_centre.y() + _radius*cos(elev)*cos(_azMax),
_centre.z() + _radius*sin(elev));
}
glEnd();
#if 0
// Split right
glBegin(GL_LINE_STRIP);
glVertex3f(
_centre.x() + _radius*cos(_elevMin)*sin(_azMax),
_centre.y() + _radius*cos(_elevMin)*cos(_azMax),
_centre.z() + _radius*sin(_elevMin));
glVertex3f(_centre.x(), _centre.y(), _centre.z());
glVertex3f(
_centre.x() + _radius*cos(_elevMax)*sin(_azMax),
_centre.y() + _radius*cos(_elevMax)*cos(_azMax),
_centre.z() + _radius*sin(_elevMax));
glEnd();
// Split left
glBegin(GL_LINE_STRIP);
glVertex3f(
_centre.x() + _radius*cos(_elevMin)*sin(_azMin),
_centre.y() + _radius*cos(_elevMin)*cos(_azMin),
_centre.z() + _radius*sin(_elevMin));
glVertex3f(_centre.x(), _centre.y(), _centre.z());
glVertex3f(
_centre.x() + _radius*cos(_elevMax)*sin(_azMin),
_centre.y() + _radius*cos(_elevMax)*cos(_azMin),
_centre.z() + _radius*sin(_elevMax));
glEnd();
#endif
}
}
bool SphereSegment::EdgeLine_computeBound(osg::BoundingBox& bbox) const
{
bbox.init();
float azIncr = (_azMax - _azMin)/_density;
float elevIncr = (_elevMax - _elevMin)/_density;
// Top edge
int i;
for(i=0; i<=_density; i++)
{
float az = _azMin + (i*azIncr);
bbox.expandBy(
_centre.x() + _radius*cos(_elevMax)*sin(az),
_centre.y() + _radius*cos(_elevMax)*cos(az),
_centre.z() + _radius*sin(_elevMax));
}
// Bottom edge
for(i=0; i<=_density; i++)
{
float az = _azMin + (i*azIncr);
bbox.expandBy(
_centre.x() + _radius*cos(_elevMin)*sin(az),
_centre.y() + _radius*cos(_elevMin)*cos(az),
_centre.z() + _radius*sin(_elevMin));
}
// Left edge
int j;
for(j=0; j<=_density; j++)
{
float elev = _elevMin + (j*elevIncr);
bbox.expandBy(
_centre.x() + _radius*cos(elev)*sin(_azMin),
_centre.y() + _radius*cos(elev)*cos(_azMin),
_centre.z() + _radius*sin(elev));
}
// Right edge
for(j=0; j<=_density; j++)
{
float elev = _elevMin + (j*elevIncr);
bbox.expandBy(
_centre.x() + _radius*cos(elev)*sin(_azMax),
_centre.y() + _radius*cos(elev)*cos(_azMax),
_centre.z() + _radius*sin(elev));
}
return true;
}
void SphereSegment::Side_drawImplementation(osg::State& /* state */,
SphereSegment::SideOrientation orientation,
SphereSegment::BoundaryAngle boundaryAngle) const
{
// Draw the planes if necessary
// ----------------------------
if(_drawMask & SIDES)
{
bool drawBackSide = true;
bool drawFrontSide = true;
int start, end, delta;
glColor4fv(_planeColor.ptr());
// draw back side.
if (drawBackSide)
{
if(orientation == AZIM) // This is a plane at a given azimuth
{
const float az = (boundaryAngle==MIN?_azMin:_azMax);
const float elevIncr = (_elevMax - _elevMin)/_density;
// Normal
osg::Vec3 normal = osg::Vec3(cos(_elevMin)*sin(az), cos(_elevMin)*cos(az), sin(_elevMin))
^ osg::Vec3(cos(_elevMax)*sin(az), cos(_elevMax)*cos(az), sin(_elevMax));
if (boundaryAngle==MIN)
{
start = _density;
end = 0;
}
else
{
start = 0;
end = _density;
normal = -normal; // Make sure normals orientationint 'outwards'
}
delta = end>start?1:-1;
if (drawBackSide)
{
// Tri fan
glNormal3f(-normal.x(),-normal.y(),-normal.z());
glBegin(GL_TRIANGLE_FAN);
glVertex3fv(_centre.ptr());
for (int j=start; j!=end+delta; j+=delta)
{
float elev = _elevMin + (j*elevIncr);
glVertex3f( _centre.x() + _radius*cos(elev)*sin(az),
_centre.y() + _radius*cos(elev)*cos(az),
_centre.z() + _radius*sin(elev));
}
glEnd();
}
if (boundaryAngle==MIN)
{
start = 0;
end = _density;
}
else
{
start = _density;
end = 0;
}
delta = end>start?1:-1;
if (drawFrontSide)
{
glNormal3fv(normal.ptr());
glBegin(GL_TRIANGLE_FAN);
glVertex3fv(_centre.ptr());
for (int j=start; j!=end+delta; j+=delta)
{
float elev = _elevMin + (j*elevIncr);
glVertex3f( _centre.x() + _radius*cos(elev)*sin(az),
_centre.y() + _radius*cos(elev)*cos(az),
_centre.z() + _radius*sin(elev));
}
glEnd();
}
}
else if(orientation == ELEV) // This is a plane at a given elevation
{
const float elev = (boundaryAngle==MIN?_elevMin:_elevMax);
const float azIncr = (_azMax - _azMin)/_density;
// Normal
osg::Vec3 normal = osg::Vec3(cos(elev)*sin(_azMax), cos(elev)*cos(_azMax), sin(elev))
^ osg::Vec3(cos(elev)*sin(_azMin), cos(elev)*cos(_azMin), sin(elev));
if (boundaryAngle==MIN)
{
start = _density;
end = 0;
normal = -normal; // Make sure normals orientationint 'outwards'
}
else
{
start = 0;
end = _density;
}
delta = end>start?1:-1;
if (drawBackSide)
{
glNormal3f(-normal.x(),-normal.y(),-normal.z());
// Tri fan
glBegin(GL_TRIANGLE_FAN);
glVertex3fv(_centre.ptr());
for (int j=start; j!=end+delta; j+=delta)
{
float az = _azMin + (j*azIncr);
glVertex3f( _centre.x() + _radius*cos(elev)*sin(az),
_centre.y() + _radius*cos(elev)*cos(az),
_centre.z() + _radius*sin(elev));
}
glEnd();
}
if (boundaryAngle==MIN)
{
start = 0;
end = _density;
}
else
{
start = _density;
end = 0;
}
delta = end>start?1:-1;
if (drawFrontSide)
{
glNormal3fv(normal.ptr());
// Tri fan
glBegin(GL_TRIANGLE_FAN);
glVertex3fv(_centre.ptr());
for (int j=start; j!=end+delta; j+=delta)
{
float az = _azMin + (j*azIncr);
glVertex3f( _centre.x() + _radius*cos(elev)*sin(az),
_centre.y() + _radius*cos(elev)*cos(az),
_centre.z() + _radius*sin(elev));
}
glEnd();
}
}
}
}
}
bool SphereSegment::Side_computeBound(osg::BoundingBox& bbox,
SphereSegment::SideOrientation orientation,
SphereSegment::BoundaryAngle boundaryAngle) const
{
bbox.init();
bbox.expandBy(_centre);
if(orientation == AZIM) // This is a plane at a given azimuth
{
const float az = (boundaryAngle==MIN?_azMin:_azMax);
const float elevIncr = (_elevMax - _elevMin)/_density;
for (int j=0; j<=_density; j++)
{
float elev = _elevMin + (j*elevIncr);
bbox.expandBy(
_centre.x() + _radius*cos(elev)*sin(az),
_centre.y() + _radius*cos(elev)*cos(az),
_centre.z() + _radius*sin(elev));
}
}
else if(orientation == ELEV) // This is a plane at a given elevation
{
const float elev = (boundaryAngle==MIN?_elevMin:_elevMax);
const float azIncr = (_azMax - _azMin)/_density;
for(int i=0; i<=_density; i++)
{
float az = _azMin + (i*azIncr);
bbox.expandBy(
_centre.x() + _radius*cos(elev)*sin(az),
_centre.y() + _radius*cos(elev)*cos(az),
_centre.z() + _radius*sin(elev));
}
}
return true;
}
void SphereSegment::Spoke_drawImplementation(osg::State&, BoundaryAngle azAngle, BoundaryAngle elevAngle) const
{
if(_drawMask & SPOKES){
glColor4fv(_spokeColor.ptr());
const float az = (azAngle==MIN?_azMin:_azMax);
const float elev = (elevAngle==MIN?_elevMin:_elevMax);
glBegin(GL_LINES);
glVertex3fv(_centre.ptr());
glVertex3f( _centre.x() + _radius*cos(elev)*sin(az),
_centre.y() + _radius*cos(elev)*cos(az),
_centre.z() + _radius*sin(elev));
glEnd();
}
}
bool SphereSegment::Spoke_computeBound(osg::BoundingBox& bbox, BoundaryAngle azAngle, BoundaryAngle elevAngle) const
{
const float az = (azAngle==MIN?_azMin:_azMax);
const float elev = (elevAngle==MIN?_elevMin:_elevMax);
bbox.expandBy(_centre);
bbox.expandBy( _centre.x() + _radius*cos(elev)*sin(az),
_centre.y() + _radius*cos(elev)*cos(az),
_centre.z() + _radius*sin(elev));
return true;
}
void SphereSegment::setDrawMask(DrawMask dm)
{
_drawMask=dm;
dirtyAllDrawableDisplayLists();
dirtyAllDrawableBounds();
dirtyBound();
}
struct ActivateTransparencyOnType
{
ActivateTransparencyOnType(const std::type_info& t): _t(t) {}
void operator()(osg::ref_ptr<osg::Drawable>& dptr) const
{
if(typeid(*dptr)==_t)
{
osg::StateSet* ss = dptr->getOrCreateStateSet();
ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
ss->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK),osg::StateAttribute::ON);
ss->setMode(GL_BLEND,osg::StateAttribute::ON);
dptr->dirtyDisplayList();
}
}
const std::type_info& _t;
};
struct DeactivateTransparencyOnType
{
DeactivateTransparencyOnType(const std::type_info& t): _t(t) {}
void operator()(osg::ref_ptr<osg::Drawable>& dptr) const
{
if(typeid(*dptr)==_t)
{
osg::StateSet* ss = dptr->getStateSet();
if(ss) ss->setRenderingHint(osg::StateSet::OPAQUE_BIN);
dptr->dirtyDisplayList();
}
}
const std::type_info& _t;
};
void SphereSegment::setSurfaceColor(const osg::Vec4& c)
{
_surfaceColor=c;
if(c.w() != 1.0) std::for_each(_drawables.begin(), _drawables.end(), ActivateTransparencyOnType(typeid(Surface)));
else std::for_each(_drawables.begin(), _drawables.end(), DeactivateTransparencyOnType(typeid(Surface)));
}
void SphereSegment::setSpokeColor(const osg::Vec4& c)
{
_spokeColor=c;
if(c.w() != 1.0) std::for_each(_drawables.begin(), _drawables.end(), ActivateTransparencyOnType(typeid(Spoke)));
else std::for_each(_drawables.begin(), _drawables.end(), DeactivateTransparencyOnType(typeid(Spoke)));
}
void SphereSegment::setEdgeLineColor(const osg::Vec4& c)
{
_edgeLineColor=c;
if(c.w() != 1.0) std::for_each(_drawables.begin(), _drawables.end(), ActivateTransparencyOnType(typeid(EdgeLine)));
else std::for_each(_drawables.begin(), _drawables.end(), DeactivateTransparencyOnType(typeid(EdgeLine)));
}
void SphereSegment::setSideColor(const osg::Vec4& c)
{
_planeColor=c;
if(c.w() != 1.0) std::for_each(_drawables.begin(), _drawables.end(), ActivateTransparencyOnType(typeid(Side)));
else std::for_each(_drawables.begin(), _drawables.end(), DeactivateTransparencyOnType(typeid(Side)));
}
void SphereSegment::setAllColors(const osg::Vec4& c)
{
setSurfaceColor(c);
setSpokeColor(c);
setEdgeLineColor(c);
setSideColor(c);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SphereSegment interesection code.
class PolytopeVisitor : public osg::NodeVisitor
{
public:
typedef std::pair<osg::Matrix, osg::Polytope> MatrixPolytopePair;
typedef std::vector<MatrixPolytopePair> 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<osg::Drawable> _drawable;
};
typedef std::vector<Hit> 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);
}
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()))
{
const osg::Polytope& polytope = _polytopeStack.front().second;
osg::Matrix matrix = _polytopeStack.back().first;
transform.computeLocalToWorldMatrix(matrix, this);
_polytopeStack.push_back(MatrixPolytopePair());
_polytopeStack.back().first = matrix;
_polytopeStack.back().second.setAndTransformProvidingInverse(polytope, matrix);
traverse(transform);
_polytopeStack.back();
}
}
void apply(osg::Geode& node)
{
if (_polytopeStack.back().second.contains(node.getBound()))
{
for(unsigned int i=0; i<node.getNumDrawables(); ++i)
{
if (_polytopeStack.back().second.contains(node.getDrawable(i)->getBound()))
{
_hits.push_back(Hit(_polytopeStack.back().first,getNodePath(),node.getDrawable(i)));
}
}
traverse(node);
}
}
HitList& getHits() { return _hits; }
protected:
PolytopeStack _polytopeStack;
HitList _hits;
};
SphereSegment::LineList SphereSegment::computeIntersection(const osg::Matrixd& transform, osg::Node* subgraph)
{
osg::notify(osg::INFO)<<"Creating line intersection between sphere segment and subgraph."<<std::endl;
osg::BoundingBox bb = getBoundingBox();
osg::Polytope polytope;
polytope.add(osg::Plane(1.0,0.0,0.0,-bb.xMin()));
polytope.add(osg::Plane(-1.0,0.0,0.0,bb.xMax()));
polytope.add(osg::Plane(0.0,1.0,0.0,-bb.yMin()));
polytope.add(osg::Plane(0.0,-1.0,0.0,bb.yMax()));
polytope.add(osg::Plane(0.0,0.0,1.0,-bb.zMin()));
polytope.add(osg::Plane(0.0,0.0,-1.0,bb.zMax()));
osg::Plane pl;
pl.set(osg::Vec3(-1.0,0.0,0.0), bb.corner(0));
PolytopeVisitor polytopeVisitor(transform, polytope);
subgraph->accept(polytopeVisitor);
if (polytopeVisitor.getHits().empty())
{
osg::notify(osg::INFO)<<"No hits found."<<std::endl;
return LineList();
}
// create a LineList to store all the compute line segments
LineList all_lines;
// compute the line intersections with each of the hit drawables
osg::notify(osg::INFO)<<"Hits found. "<<polytopeVisitor.getHits().size()<<std::endl;
PolytopeVisitor::HitList& hits = polytopeVisitor.getHits();
for(PolytopeVisitor::HitList::iterator itr = hits.begin();
itr != hits.end();
++itr)
{
SphereSegment::LineList lines = computeIntersection(itr->_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::notify(osg::INFO)<<"Creating line intersection between sphere segment and subgraph."<<std::endl;
osg::BoundingBox bb = getBoundingBox();
osg::Polytope polytope;
polytope.add(osg::Plane(1.0,0.0,0.0,-bb.xMin()));
polytope.add(osg::Plane(-1.0,0.0,0.0,bb.xMax()));
polytope.add(osg::Plane(0.0,1.0,0.0,-bb.yMin()));
polytope.add(osg::Plane(0.0,-1.0,0.0,bb.yMax()));
polytope.add(osg::Plane(0.0,0.0,1.0,-bb.zMin()));
polytope.add(osg::Plane(0.0,0.0,-1.0,bb.zMax()));
osg::Plane pl;
pl.set(osg::Vec3(-1.0,0.0,0.0), bb.corner(0));
PolytopeVisitor polytopeVisitor(transform, polytope);
subgraph->accept(polytopeVisitor);
if (polytopeVisitor.getHits().empty())
{
osg::notify(osg::INFO)<<"No hits found."<<std::endl;
return 0;
}
// create a LineList to store all the compute line segments
osg::Group* group = new osg::Group;
// compute the line intersections with each of the hit drawables
osg::notify(osg::INFO)<<"Hits found. "<<polytopeVisitor.getHits().size()<<std::endl;
PolytopeVisitor::HitList& hits = polytopeVisitor.getHits();
for(PolytopeVisitor::HitList::iterator itr = hits.begin();
itr != hits.end();
++itr)
{
group->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<class T, class U>
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;
};
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<Triangle*> 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 (_p1<edge._p1) return true;
else if (_p1>edge._p1) return false;
else return _p2<edge._p2;
}
inline void addTriangle(Triangle* tri)
{
TriangleList::iterator itr = std::find(_triangles.begin(), _triangles.end(), tri);
if (itr==_triangles.end()) _triangles.push_back(tri);
}
void removeFromToTraverseList(Triangle* tri)
{
TriangleList::iterator itr = std::find(_toTraverse.begin(), _toTraverse.end(), tri);
if (itr!=_toTraverse.end()) _toTraverse.erase(itr);
}
bool completlyOutside() const { return _p1Outside && _p2Outside; }
unsigned int _p1;
unsigned int _p2;
TriangleList _triangles;
// intersection information
IntersectionType _intersectionType;
osg::Vec3 _intersectionVertex;
unsigned int _intersectionVertexIndex;
bool _p1Outside;
bool _p2Outside;
TriangleList _toTraverse;
SurfaceType _intersectionEdge;
};
typedef std::list< osg::ref_ptr<Edge> > 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::notify(osg::INFO)<<"Edge problem"<<std::endl;
return 0;
}
if (edge!=_e1 && _e1 && _e1->_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),
_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 (elevation<elevMin) _bottomSurface = OUTSIDE;
else if (elevation>elevMin) _bottomSurface = INSIDE;
else _bottomSurface = INTERSECTS;
// top surface
if (elevation>elevMax) _topSurface = OUTSIDE;
else if (elevation<elevMax) _topSurface = INSIDE;
else _topSurface = INTERSECTS;
double dot_alphaMin = cos(azimMin)*vertex.x() - sin(azimMin)*vertex.y();
if (dot_alphaMin<0.0) _leftSurface = OUTSIDE;
else if (dot_alphaMin>0.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<azimRange)
{
_leftRightSurfaces = INSIDE;
}
else if (azimDelta==azimRange)
{
_leftRightSurfaces = INTERSECTS;
}
}
Classification _radiusSurface;
Classification _leftRightSurfaces;
Classification _leftSurface;
Classification _rightSurface;
Classification _bottomSurface;
Classification _topSurface;
};
struct RegionCounter
{
RegionCounter():
_numVertices(0),
_outside_radiusSurface(0),
_inside_radiusSurface(0),
_intersects_radiusSurface(0),
_outside_leftRightSurfaces(0),
_inside_leftRightSurfaces(0),
_intersects_leftRightSurfaces(0),
_outside_leftSurface(0),
_inside_leftSurface(0),
_intersects_leftSurface(0),
_outside_rightSurface(0),
_inside_rightSurface(0),
_intersects_rightSurface(0),
_outside_bottomSurface(0),
_inside_bottomSurface(0),
_intersects_bottomSurface(0),
_outside_topSurface(0),
_inside_topSurface(0),
_intersects_topSurface(0) {}
void add(const Region& region)
{
++_numVertices;
if (region._radiusSurface == Region::OUTSIDE) ++_outside_radiusSurface;
if (region._radiusSurface == Region::INSIDE) ++_inside_radiusSurface;
if (region._radiusSurface == Region::INTERSECTS) ++_intersects_radiusSurface;
if (region._leftRightSurfaces == Region::OUTSIDE) ++_outside_leftRightSurfaces;
if (region._leftRightSurfaces == Region::INSIDE) ++_inside_leftRightSurfaces;
if (region._leftRightSurfaces == Region::INTERSECTS) ++_intersects_leftRightSurfaces;
if (region._leftSurface == Region::OUTSIDE) ++_outside_leftSurface;
if (region._leftSurface == Region::INSIDE) ++_inside_leftSurface;
if (region._leftSurface == Region::INTERSECTS) ++_intersects_leftSurface;
if (region._rightSurface == Region::OUTSIDE) ++_outside_rightSurface;
if (region._rightSurface == Region::INSIDE) ++_inside_rightSurface;
if (region._rightSurface == Region::INTERSECTS) ++_intersects_rightSurface;
if (region._bottomSurface == Region::OUTSIDE) ++_outside_bottomSurface;
if (region._bottomSurface == Region::INSIDE) ++_inside_bottomSurface;
if (region._bottomSurface == Region::INTERSECTS) ++_intersects_bottomSurface;
if (region._topSurface == Region::OUTSIDE) ++_outside_topSurface;
if (region._topSurface == Region::INSIDE) ++_inside_topSurface;
if (region._topSurface == Region::INTERSECTS) ++_intersects_topSurface;
}
Region::Classification overallClassification() const
{
// if all vertices are outside any of the surfaces then we are completely outside
if (_outside_radiusSurface==_numVertices ||
_outside_leftRightSurfaces==_numVertices ||
_outside_topSurface==_numVertices ||
_outside_bottomSurface==_numVertices) return Region::OUTSIDE;
// if all the vertices on all the sides and inside then we are completely inside
if (_inside_radiusSurface==_numVertices &&
_inside_leftRightSurfaces==_numVertices &&
_inside_topSurface==_numVertices &&
_inside_bottomSurface==_numVertices) return Region::INSIDE;
return Region::INTERSECTS;
}
bool intersecting(SurfaceType surfaceType) const
{
// if all vertices are outside any of the surfaces then we are completely outside
if ((surfaceType!=RADIUS_SURFACE && _outside_radiusSurface!=0) ||
(surfaceType!=LEFT_SURFACE && _outside_leftSurface!=0) ||
(surfaceType!=RIGHT_SURFACE && _outside_rightSurface!=0) ||
(surfaceType!=TOP_SURFACE && _outside_topSurface!=0) ||
(surfaceType!=BOTTOM_SURFACE && _outside_bottomSurface!=0)) return false;
// if all the vertices on all the sides and inside then we are completely inside
if ((surfaceType!=RADIUS_SURFACE && _inside_radiusSurface!=0) &&
(surfaceType!=LEFT_SURFACE && _inside_leftSurface!=0) &&
(surfaceType!=RIGHT_SURFACE && _inside_rightSurface!=0) &&
(surfaceType!=TOP_SURFACE && _inside_topSurface!=0) &&
(surfaceType!=BOTTOM_SURFACE && _inside_bottomSurface!=0)) return false;
return true;
}
int numberOfIntersectingSurfaces() const
{
int sidesThatIntersect = 0;
if (_outside_radiusSurface!=_numVertices && _inside_radiusSurface!=_numVertices) ++sidesThatIntersect;
if (_outside_leftSurface!=_numVertices && _inside_leftSurface!=_numVertices) ++sidesThatIntersect;
if (_outside_rightSurface!=_numVertices && _inside_rightSurface!=_numVertices) ++sidesThatIntersect;
if (_outside_topSurface!=_numVertices && _inside_topSurface!=_numVertices) ++sidesThatIntersect;
if (_outside_bottomSurface!=_numVertices && _inside_bottomSurface!=_numVertices) ++sidesThatIntersect;
return sidesThatIntersect;
}
unsigned int _numVertices;
unsigned int _outside_radiusSurface;
unsigned int _inside_radiusSurface;
unsigned int _intersects_radiusSurface;
unsigned int _outside_leftRightSurfaces;
unsigned int _inside_leftRightSurfaces;
unsigned int _intersects_leftRightSurfaces;
unsigned int _outside_leftSurface;
unsigned int _inside_leftSurface;
unsigned int _intersects_leftSurface;
unsigned int _outside_rightSurface;
unsigned int _inside_rightSurface;
unsigned int _intersects_rightSurface;
unsigned int _outside_bottomSurface;
unsigned int _inside_bottomSurface;
unsigned int _intersects_bottomSurface;
unsigned int _outside_topSurface;
unsigned int _inside_topSurface;
unsigned int _intersects_topSurface;
};
typedef std::vector< osg::Vec3 > VertexArray;
typedef std::vector< Region > RegionArray;
typedef std::vector< bool > BoolArray;
typedef std::vector< unsigned int > IndexArray;
typedef std::vector< osg::ref_ptr<Triangle> > TriangleArray;
typedef std::set< osg::ref_ptr<Edge>, 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<array.size(); ++i)
{
osg::Vec3 vertex = array[i]*matrix - _centre;
_originalVertices[i] = vertex;
_regions[i].classify(vertex, radius2, _azMin, _azMax, _elevMin, _elevMax);
}
}
inline void operator()(unsigned int p1, unsigned int p2, unsigned int p3)
{
RegionCounter rc;
rc.add(_regions[p1]);
rc.add(_regions[p2]);
rc.add(_regions[p3]);
Region::Classification classification = rc.overallClassification();
// reject if outside.
if (classification==Region::OUTSIDE)
{
++_numOutside;
return;
}
if (rc.numberOfIntersectingSurfaces()==0)
{
++_numInside;
return;
}
++_numIntersecting;
_triangles.push_back(new Triangle(p1,p2,p3));
if (!_vertexInIntersectionSet[p1])
{
_vertexInIntersectionSet[p1] = true;
_candidateVertexIndices.push_back(p1);
}
if (!_vertexInIntersectionSet[p2])
{
_vertexInIntersectionSet[p2] = true;
_candidateVertexIndices.push_back(p2);
}
if (!_vertexInIntersectionSet[p3])
{
_vertexInIntersectionSet[p3] = true;
_candidateVertexIndices.push_back(p3);
}
}
void removeDuplicateVertices()
{
osg::notify(osg::INFO)<<"Removing duplicates : num vertices in "<<_candidateVertexIndices.size()<<std::endl;
if (_candidateVertexIndices.size()<2) return;
std::sort(_candidateVertexIndices.begin(), _candidateVertexIndices.end(), SortFunctor(_originalVertices));
_remapIndices.resize(_originalVertices.size());
for(unsigned int i=0; i< _originalVertices.size(); ++i)
{
_remapIndices[i] = i;
}
bool verticesRemapped = false;
IndexArray::iterator itr = _candidateVertexIndices.begin();
unsigned int lastUniqueIndex = *(itr++);
for(; itr != _candidateVertexIndices.end(); ++itr)
{
//unsigned int i = *itr;
// osg::notify(osg::INFO)<<" i="<<i<<" lastUniqueIndex="<<lastUniqueIndex<<std::endl;
if (_originalVertices[*itr]==_originalVertices[lastUniqueIndex])
{
osg::notify(osg::INFO)<<"Combining vertex "<<*itr<<" with "<<lastUniqueIndex<<std::endl;
_remapIndices[*itr] = lastUniqueIndex;
verticesRemapped = true;
}
else
{
lastUniqueIndex = *itr;
}
}
if (verticesRemapped)
{
osg::notify(osg::INFO)<<"Remapping triangle vertices "<<std::endl;
for(TriangleArray::iterator titr = _triangles.begin();
titr != _triangles.end();
++titr)
{
(*titr)->_p1 = _remapIndices[(*titr)->_p1];
(*titr)->_p2 = _remapIndices[(*titr)->_p2];
(*titr)->_p3 = _remapIndices[(*titr)->_p3];
(*titr)->sort();
}
}
}
void removeDuplicateTriangles()
{
osg::notify(osg::INFO)<<"Removing duplicate triangles : num triangles in "<<_triangles.size()<<std::endl;
if (_triangles.size()<2) return;
std::sort(_triangles.begin(), _triangles.end(), dereference_less());
unsigned int lastUniqueTriangle = 0;
unsigned int numDuplicates = 0;
for(unsigned int i=1; i<_triangles.size(); ++i)
{
if ( *(_triangles[lastUniqueTriangle]) != *(_triangles[i]) )
{
++lastUniqueTriangle;
if (lastUniqueTriangle!=i)
{
_triangles[lastUniqueTriangle] = _triangles[i];
}
}
else
{
++numDuplicates;
}
}
if (lastUniqueTriangle<_triangles.size()-1)
{
_triangles.erase(_triangles.begin()+lastUniqueTriangle+1, _triangles.end());
}
osg::notify(osg::INFO)<<"Removed duplicate triangles : num duplicates found "<<numDuplicates<<std::endl;
osg::notify(osg::INFO)<<"Removed duplicate triangles : num triangles out "<<_triangles.size()<<std::endl;
}
void buildEdges(Triangle* tri)
{
tri->_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::notify(osg::INFO)<<"Number of edges "<<_edges.size()<<std::endl;
unsigned int numZeroConnections = 0;
unsigned int numSingleConnections = 0;
unsigned int numDoubleConnections = 0;
unsigned int numMultiConnections = 0;
osg::notify(osg::INFO)<<"Number of edges "<<_edges.size()<<std::endl;
for(EdgeSet::iterator eitr = _edges.begin();
eitr != _edges.end();
++eitr)
{
const Edge* edge = eitr->get();
unsigned int numConnections = edge->_triangles.size();
if (numConnections==0) ++numZeroConnections;
else if (numConnections==1) ++numSingleConnections;
else if (numConnections==2) ++numDoubleConnections;
else ++numMultiConnections;
}
osg::notify(osg::INFO)<<"Number of numZeroConnections "<<numZeroConnections<<std::endl;
osg::notify(osg::INFO)<<"Number of numSingleConnections "<<numSingleConnections<<std::endl;
osg::notify(osg::INFO)<<"Number of numDoubleConnections "<<numDoubleConnections<<std::endl;
osg::notify(osg::INFO)<<"Number of numMultiConnections "<<numMultiConnections<<std::endl;
}
Edge* addEdge(unsigned int p1, unsigned int p2, Triangle* tri)
{
osg::ref_ptr<Edge> edge = new Edge(p1, p2);
EdgeSet::iterator itr = _edges.find(edge);
if (itr==_edges.end())
{
edge->addTriangle(tri);
_edges.insert(edge);
return edge.get();
}
else
{
Edge* edge = const_cast<Edge*>(itr->get());
edge->addTriangle(tri);
return edge;
}
}
SphereSegment::LineList connectIntersections(EdgeList& hitEdges)
{
SphereSegment::LineList lineList;
osg::notify(osg::INFO)<<"Number of edge intersections "<<hitEdges.size()<<std::endl;
if (hitEdges.empty()) return lineList;
// now need to build the toTraverse list for each hit edge,
// but should only contain traingles that actually hit the intersection surface
EdgeList::iterator hitr;
for(hitr = hitEdges.begin();
hitr != hitEdges.end();
++hitr)
{
Edge* edge = hitr->get();
edge->_toTraverse.clear();
//osg::notify(osg::INFO)<<"edge= "<<edge<<std::endl;
for(Edge::TriangleList::iterator titr = 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::notify(osg::INFO)<<" adding tri="<<tri<<std::endl;
edge->_toTraverse.push_back(tri);
}
// osg::notify(osg::INFO)<<"Number active edges "<<numActiveEdges<<" num orignal edges "<<numEdges<<std::endl;
}
}
while(!hitEdges.empty())
{
// find the an open edge
for(hitr = hitEdges.begin();
hitr != hitEdges.end();
++hitr)
{
Edge* edge = hitr->get();
if (edge->_toTraverse.size()==1) break;
}
if (hitr == hitEdges.end())
{
hitr = hitEdges.begin();
}
// osg::notify(osg::INFO)<<"New line "<<std::endl;
osg::Vec3Array* newLine = new osg::Vec3Array;
lineList.push_back(newLine);
Edge* edge = hitr->get();
while (edge)
{
// osg::notify(osg::INFO)<<" vertex "<<edge->_intersectionVertex<<std::endl;
newLine->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::notify(osg::INFO)<<" tri="<<tri<<" edge="<<edge<<" newEdge="<<newEdge<<std::endl;
if (edge==newEdge)
{
osg::notify(osg::INFO)<<" edge returned to itself problem "<<std::endl;
}
}
else
{
newEdge = 0;
}
if (edge->_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<class I>
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<Edge*>(itr->get());
if (intersector(edge))
{
hitEdges.push_back(edge);
}
}
return connectIntersections(hitEdges);
}
template<class I>
void trim(SphereSegment::LineList& lineList, osg::Vec3Array* sourceLine, I intersector)
{
if (sourceLine->empty()) return;
// osg::notify(osg::INFO)<<"Testing line of "<<sourceLine->size()<<std::endl;
unsigned int first=0;
while (first<sourceLine->size())
{
// find first valid vertex.
for(; first<sourceLine->size(); ++first)
{
if (intersector.distance((*sourceLine)[first]-_centre)>=0.0) break;
}
if (first==sourceLine->size())
{
// osg::notify(osg::INFO)<<"No valid points found"<<std::endl;
return;
}
// find last valid vertex.
unsigned int last = first+1;
for(; last<sourceLine->size(); ++last)
{
if (intersector.distance((*sourceLine)[last]-_centre)<0.0) break;
}
if (first==0 && last==sourceLine->size())
{
// osg::notify(osg::INFO)<<"Copying complete line"<<std::endl;
lineList.push_back(sourceLine);
}
else
{
// osg::notify(osg::INFO)<<"Copying partial line line"<<first<<" "<<last<<std::endl;
osg::Vec3Array* newLine = new osg::Vec3Array;
if (first>0)
{
newLine->push_back(intersector.intersectionPoint((*sourceLine)[first-1]-_centre, (*sourceLine)[first]-_centre)+_centre);
}
for(unsigned int i=first; i<last; ++i)
{
newLine->push_back((*sourceLine)[i]);
}
if (last<sourceLine->size())
{
newLine->push_back(intersector.intersectionPoint((*sourceLine)[last-1]-_centre, (*sourceLine)[last]-_centre)+_centre);
}
lineList.push_back(newLine);
}
first = last;
}
}
// handle a paird 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<class I>
void trim(SphereSegment::LineList& lineList, osg::Vec3Array* sourceLine, I intersector1, I intersector2)
{
if (sourceLine->empty()) return;
// osg::notify(osg::INFO)<<"Testing line of "<<sourceLine->size()<<std::endl;
unsigned int first=0;
while (first<sourceLine->size())
{
// find first valid vertex.
for(; first<sourceLine->size(); ++first)
{
osg::Vec3 v = (*sourceLine)[first]-_centre;
if (intersector1.distance(v)>=0.0 || intersector2.distance(v)>=0.0 ) break;
}
if (first==sourceLine->size())
{
// osg::notify(osg::INFO)<<"No valid points found"<<std::endl;
return;
}
// find last valid vertex.
unsigned int last = first+1;
for(; last<sourceLine->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::notify(osg::INFO)<<"Copying complete line"<<std::endl;
lineList.push_back(sourceLine);
}
else
{
osg::notify(osg::INFO)<<"Copying partial line line"<<first<<" "<<last<<std::endl;
osg::Vec3Array* newLine = new osg::Vec3Array;
if (first>0)
{
osg::Vec3 start = (*sourceLine)[first-1]-_centre;
osg::Vec3 end = (*sourceLine)[first]-_centre;
float end1 = intersector1.distance(end);
float end2 = intersector2.distance(end);
// work out which intersector to use by discounting the one that
// isn't a plausible candiate.
bool possible1 = end1>=0.0;
bool possible2 = end2>=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 (r1<r2)
{
osg::notify(osg::INFO)<<"start point, 1 near to end than 2"<<r1<<" "<<r2<<std::endl;
possible1 = true;
possible2 = false;
}
else
{
osg::notify(osg::INFO)<<"start point, 2 near to end than 1"<<std::endl;
possible1 = false;
possible2 = true;
}
}
if (possible1)
{
newLine->push_back(intersector1.intersectionPoint(start, end)+_centre);
}
else
{
newLine->push_back(intersector2.intersectionPoint(start, end)+_centre);
}
}
for(unsigned int i=first; i<last; ++i)
{
newLine->push_back((*sourceLine)[i]);
}
if (last<sourceLine->size())
{
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 candiate.
bool possible1 = start1>=0.0;
bool possible2 = start2>=0.0;
if (possible1 && possible2)
{
double end1 = intersector1.distance(end);
double end2 = intersector2.distance(end);
possible1 = end1<0.0;
possible2 = end2<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::notify(osg::INFO)<<"end point, 1 near to end than 2"<<r1<<" "<<r2<<std::endl;
possible1 = true;
possible2 = false;
}
else
{
osg::notify(osg::INFO)<<"end point, 2 near to end than 1"<<std::endl;
possible1 = false;
possible2 = true;
}
}
}
if (possible1)
{
newLine->push_back(intersector1.intersectionPoint(start, end)+_centre);
}
else
{
newLine->push_back(intersector2.intersectionPoint(start, end)+_centre);
}
}
lineList.push_back(newLine);
}
first = last;
}
}
template<class I>
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<class I>
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) {}
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<osg::Vec3Array> _line;
unsigned int _lineEnd;
osg::ref_ptr<osg::Vec3Array> _neighbourLine;
unsigned int _neighbourLineEnd;
float _distance;
};
void joinEnds(float fuseDistance, bool doFuse, bool allowJoinToSelf)
{
SphereSegment::LineList fusedLines;
SphereSegment::LineList unfusedLines;
// first seperat 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<LinePair> LinePairSet;
LinePairSet linePairs;
for(unsigned int i=0; i<unfusedLines.size(); ++i)
{
unsigned int j = allowJoinToSelf ? i : i+1;
if (j<unfusedLines.size())
{
LinePair linePair(unfusedLines[i].get());
for(; j<unfusedLines.size(); ++j)
{
linePair.consider(unfusedLines[j].get());
}
linePairs.insert(linePair);
}
}
if (linePairs.empty())
{
osg::notify(osg::INFO)<<"Line Pairs empty"<<std::endl;
break;
}
for(LinePairSet::iterator itr = linePairs.begin();
itr != linePairs.end();
++itr)
{
osg::notify(osg::INFO)<<"Line "<<itr->_line.get()<<" "<<itr->_lineEnd<<" neighbour "<<itr->_neighbourLine.get()<<" "<<itr->_neighbourLineEnd<<" distance="<<itr->_distance<<std::endl;
}
LinePair linePair = *linePairs.begin();
if (linePair._distance > fuseDistance)
{
osg::notify(osg::INFO)<<"Completed work, shortest distance left is "<<linePair._distance<<std::endl;
break;
}
if (linePair._line == linePair._neighbourLine)
{
osg::notify(osg::INFO)<<"Fusing line to itself"<<std::endl;
osg::Vec3Array* line = linePair._line.get();
osg::Vec3 average = ((*line)[0]+(*line)[line->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::notify(osg::INFO)<<"Error couldn't find line in unfused list, exiting fusing loop."<<std::endl;
break;
}
}
else
{
osg::Vec3Array* line1 = linePair._line.get();
int fuseEnd1 = linePair._lineEnd;
int openEnd1 = fuseEnd1==0 ? line1->size()-1 : 0;
int direction1 = openEnd1<fuseEnd1 ? 1 : -1;
osg::Vec3Array* line2 = linePair._neighbourLine.get();
int fuseEnd2 = linePair._neighbourLineEnd;
int openEnd2 = fuseEnd2==0 ? line2->size()-1 : 0;
int direction2 = fuseEnd2<openEnd2 ? 1 : -1;
osg::Vec3Array* newline = new osg::Vec3Array;
// copy across all but fuse end of line1
for(int i=openEnd1;
i != fuseEnd1;
i += direction1)
{
newline->push_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::notify(osg::INFO)<<"Fusing two seperate lines "<<newline<<std::endl;
}
_generatedLines = fusedLines;
_generatedLines.insert(_generatedLines.end(), unfusedLines.begin(), unfusedLines.end());
}
}
};
bool computeQuadraticSolution(double a, double b, double c, double& s1, double& s2)
{
// avoid division by zero.
if (a==0.0)
{
s1 = 0.0;
s2 = 0.0;
return false;
}
double inside_sqrt = b*b - 4.0*a*c;
// avoid sqrt of negative number
if (inside_sqrt<0.0)
{
s1 = 0.0;
s2 = 0.0;
return false;
}
double rhs = sqrt(inside_sqrt);
s1 = (-b + rhs)/(2.0*a);
s2 = (-b - rhs)/(2.0*a);
return true;
}
struct AzimPlaneIntersector
{
AzimPlaneIntersector(TriangleIntersectOperator& tif, double azim, bool lowerOutside):
_tif(tif),
_lowerOutside(lowerOutside)
{
_plane.set(cos(azim),-sin(azim),0.0,0.0);
_endPlane.set(sin(azim),cos(azim),0.0,0.0);
}
TriangleIntersectOperator& _tif;
osg::Plane _plane;
osg::Plane _endPlane;
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 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 disgard
if (d1<0.0 && d2<0.0) return false;
// if both points outside then disgard
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::notify(osg::INFO)<<"r = "<<r<<std::endl;
double one_minus_r = 1.0-r;
edge->_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) ;
}
};
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 disgard
if (elev1<_elev && elev2<_elev) return false;
// if both points outside then disgard
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::notify(osg::INFO)<<"neither segment intersects s1="<<s1<<" s2="<<s2<<std::endl;
edge->_intersectionType = TriangleIntersectOperator::Edge::NO_INTERSECTION;
return false;
}
double one_minus_r = 1.0-r;
edge->_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 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))
{
osg::notify(osg::INFO)<<"Warning::neither segment intersects s1="<<s1<<" s2="<<s2<<std::endl;
return v1;
}
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::notify(osg::INFO)<<"Warning::neither segment intersects s1="<<s1<<" s2="<<s2<<std::endl;
return v1;
}
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)
{
double length_xy = sqrt(v.x()*v.x() + v.y()*v.y());
double computedElev = atan2((double)v.z(),length_xy);
return _lowerOutside ? computedElev-_elev : _elev-computedElev ;
}
};
struct RadiusIntersector
{
RadiusIntersector(TriangleIntersectOperator& tif):
_tif(tif) {}
TriangleIntersectOperator& _tif;
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 radius1 = v1.length();
double radius2 = v2.length();
edge->_p1Outside = radius1>_tif._radius;
edge->_p2Outside = radius2>_tif._radius;
// if both points inside then disgard
if (radius1<_tif._radius && radius2<_tif._radius) return false;
// if both points outside then disgard
if (radius1>_tif._radius && radius2>_tif._radius) return false;
if (radius1==_tif._radius)
{
if (radius2==_tif._radius)
{
edge->_intersectionType = TriangleIntersectOperator::Edge::BOTH_ENDS;
}
else
{
edge->_intersectionType = TriangleIntersectOperator::Edge::POINT_1;
}
}
else if (radius2==_tif._radius)
{
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 a = dx*dx + dy*dy + dz*dz;
double b = 2.0*(v1.x()*dx + v1.y()*dy + v1.z()*dz);
double c = v1.x()*v1.x() + v1.y()*v1.y() + v1.z()*v1.z() - _tif._radius*_tif._radius;
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::notify(osg::INFO)<<"neither segment intersects s1="<<s1<<" s2="<<s2<<std::endl;
edge->_intersectionType = TriangleIntersectOperator::Edge::NO_INTERSECTION;
return false;
}
double one_minus_r = 1.0-r;
edge->_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 dx = v2.x()-v1.x();
double dy = v2.y()-v1.y();
double dz = v2.z()-v1.z();
double a = dx*dx + dy*dy + dz*dz;
double b = 2.0*(v1.x()*dx + v1.y()*dy + v1.z()*dz);
double c = v1.x()*v1.x() + v1.y()*v1.y() + v1.z()*v1.z() - _tif._radius*_tif._radius;
double s1, s2;
if (!computeQuadraticSolution(a,b,c,s1,s2))
{
osg::notify(osg::INFO)<<"Warning: neither segment intersects s1="<<s1<<" s2="<<s2<<std::endl;
return v1;
}
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::notify(osg::INFO)<<"Warning: neither segment intersects s1="<<s1<<" s2="<<s2<<std::endl;
return v1;
}
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 _tif._radius-v.length();
}
};
}
using namespace SphereSegmentIntersector;
SphereSegment::LineList SphereSegment::computeIntersection(const osg::Matrixd& matrix, osg::Drawable* drawable)
{
// cast to Geometry, return empty handed if Drawable not a Geometry.
osg::Geometry* geometry = dynamic_cast<osg::Geometry*>(drawable);
if (!geometry) return SphereSegment::LineList();
// get vertices from geometry, return empty handed if a Vec3Array not present.
osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
if (!vertices) return SphereSegment::LineList();
typedef osg::TriangleIndexFunctor<TriangleIntersectOperator> TriangleIntersectFunctor;
TriangleIntersectFunctor tif;
tif._centre = _centre;
tif._radius = _radius;
tif._azMin = _azMin;
tif._azMax = _azMax;
tif._elevMin = _elevMin;
tif._elevMax = _elevMax;
tif.computePositionAndRegions(matrix, *vertices);
// traverse the triangles in the Geometry dedicating intersections
geometry->accept(tif);
osg::notify(osg::INFO)<<"_numOutside = "<<tif._numOutside<<std::endl;
osg::notify(osg::INFO)<<"_numInside = "<<tif._numInside<<std::endl;
osg::notify(osg::INFO)<<"_numIntersecting = "<<tif._numIntersecting<<std::endl;
tif.removeDuplicateVertices();
tif.removeDuplicateTriangles();
tif.buildEdges();
RadiusIntersector radiusIntersector(tif);
AzimPlaneIntersector azMinIntersector(tif,_azMin, true);
AzimPlaneIntersector azMinEndIntersector(tif,_azMin-osg::PI*0.5, true);
AzimPlaneIntersector azMaxIntersector(tif,_azMax, false);
AzimPlaneIntersector azMaxEndIntersector(tif,_azMax-osg::PI*0.5, true);
ElevationIntersector elevMinIntersector(tif,_elevMin, true);
ElevationIntersector elevMaxIntersector(tif,_elevMax, false);
// create the line intersections with the terrain
SphereSegment::LineList radiusLines = tif.computeIntersections(radiusIntersector);
SphereSegment::LineList elevMinLines = tif.computeIntersections(elevMinIntersector);
SphereSegment::LineList elevMaxLines = tif.computeIntersections(elevMaxIntersector);
SphereSegment::LineList azMinLines;
SphereSegment::LineList azMaxLines;
double azimRange = _azMax-_azMin;
if (azimRange<2.0*osg::PI)
{
azMinLines = tif.computeIntersections(azMinIntersector);
azMaxLines = tif.computeIntersections(azMaxIntersector);
// trim the azimuth intersection lines by the radius
tif.trim(azMinLines,radiusIntersector);
tif.trim(azMaxLines,radiusIntersector);
// trim the azim intersection lines by the elevation
tif.trim(azMinLines, elevMinIntersector);
tif.trim(azMaxLines, elevMinIntersector);
// trim the centeral ends of the azim lines
tif.trim(azMinLines,azMinEndIntersector);
tif.trim(azMaxLines,azMaxEndIntersector);
if (azimRange<=osg::PI)
{
// trim the radius and elevation intersection lines by the azimMin
tif.trim(radiusLines, azMinIntersector);
tif.trim(elevMinLines, azMinIntersector);
tif.trim(elevMaxLines, azMinIntersector);
// trim the radius and elevation intersection lines by the azimMax
tif.trim(radiusLines, azMaxIntersector);
tif.trim(elevMinLines, azMaxIntersector);
tif.trim(elevMaxLines, azMaxIntersector);
}
else
{
// need to have new intersector which handles convex azim planes
tif.trim(radiusLines, azMinIntersector, azMaxIntersector);
tif.trim(elevMinLines, azMinIntersector, azMaxIntersector);
tif.trim(elevMaxLines, azMinIntersector, azMaxIntersector);
}
}
// trim elevation intersection lines by radius
tif.trim(elevMinLines,radiusIntersector);
tif.trim(elevMaxLines,radiusIntersector);
// trim the radius and elevation intersection lines by the elevMin
tif.trim(radiusLines, elevMinIntersector);
// trim the radius and elevation intersection lines by the elevMax
tif.trim(radiusLines, elevMaxIntersector);
// collect all lines together.
tif._generatedLines.insert(tif._generatedLines.end(), radiusLines.begin(), radiusLines.end());
tif._generatedLines.insert(tif._generatedLines.end(), azMinLines.begin(), azMinLines.end());
tif._generatedLines.insert(tif._generatedLines.end(), azMaxLines.begin(), azMaxLines.end());
tif._generatedLines.insert(tif._generatedLines.end(), elevMinLines.begin(), elevMinLines.end());
tif._generatedLines.insert(tif._generatedLines.end(), elevMaxLines.begin(), elevMaxLines.end());
osg::notify(osg::INFO)<<"number of seperate lines = "<<tif._generatedLines.size()<<std::endl;
float fuseDistance = 1.0;
tif.joinEnds(fuseDistance, true, true);
osg::notify(osg::INFO)<<"number of seperate lines after fuse = "<<tif._generatedLines.size()<<std::endl;
float joinDistance = 1e8;
tif.joinEnds(joinDistance, false, false);
osg::notify(osg::INFO)<<"number of seperate lines after join = "<<tif._generatedLines.size()<<std::endl;
tif.joinEnds(joinDistance, false, true);
osg::notify(osg::INFO)<<"number of seperate lines after second join = "<<tif._generatedLines.size()<<std::endl;
return tif._generatedLines;
}
osg::Node* SphereSegment::computeIntersectionSubgraph(const osg::Matrixd& matrix, osg::Drawable* drawable)
{
SphereSegment::LineList generatedLines = computeIntersection(matrix, drawable);
osg::Geode* geode = new osg::Geode;
geode->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
for(osgSim::SphereSegment::LineList::iterator itr = generatedLines.begin();
itr != generatedLines.end();
++itr)
{
osg::Geometry* geom = new osg::Geometry;
geode->addDrawable(geom);
osg::Vec3Array* vertices = itr->get();
geom->setVertexArray(vertices);
geom->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP, 0, vertices->getNumElements()));
}
#if 0
float radius = 0.1f;
for(unsigned int i=0; i<tif._originalVertices.size(); ++i)
{
osg::ShapeDrawable* sd = new osg::ShapeDrawable(new osg::Sphere(tif._originalVertices[i]+tif._centre,radius));
TriangleIntersectFunctor::RegionCounter rc;
rc.add(tif._regions[i]);
TriangleIntersectFunctor::Region::Classification region = rc.overallClassification();
if (region==TriangleIntersectFunctor::Region::OUTSIDE)
{
sd->setColor(osg::Vec4(1.0,0.0,0.0,1.0));
}
else if (region==TriangleIntersectFunctor::Region::INSIDE)
{
sd->setColor(osg::Vec4(1.0,1.0,0.0,1.0));
}
else if (region==TriangleIntersectFunctor::Region::INTERSECTS)
{
sd->setColor(osg::Vec4(1.0,1.0,1.0,1.0));
}
geode->addDrawable(sd);
}
#endif
return geode;
}