From Chuck Seberino, "Here is a fix for the RotateCylinderDragger. This patch fixes the case where the picking direction is close to the cylinder axis. The current behavior is this:
* If the eyepoint and cylinder axis are close to parallel (given some tolerance), then it uses a plane perpendicular to the cylinder axis. * Otherwise it uses a plane parallel to the cylinder axis oriented towards the eyepoint (previous behavior). This gives decent behavior and is the only path that was taken in the previous code. I kept with previous behavior and that allowed a good bit of code to be removed, simplifying things. There is now no need for the _onCylinder flag, but since there is a public accessor, I wasn't sure how to handle it for backwards compatibility, so I left it in. NOTE - there is no default initialized value, so if it is kept in, it should be set to 'false' to keep same behavior as before. I am not quite sure how the _onCylinder case was supposed to behave as even forcing that path gave undesirable behavior, even with carefully controlled dragging. "
This commit is contained in:
parent
8d3790d594
commit
fd97a9a800
@ -295,6 +295,7 @@ class OSGMANIPULATOR_EXPORT CylinderPlaneProjector : public CylinderProjector
|
||||
mutable osg::Plane _plane;
|
||||
mutable bool _onCylinder;
|
||||
mutable osg::Vec3d _planeLineStart, _planeLineEnd;
|
||||
mutable bool _parallelPlane;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -16,6 +16,11 @@
|
||||
|
||||
using namespace osgManipulator;
|
||||
|
||||
// When the squared magnitude (length2) of the cross product of 2
|
||||
// angles is less than this tolerance, they are considered parallel.
|
||||
// osg::Vec3 a, b; (a ^ b).length2()
|
||||
#define CROSS_PRODUCT_ANGLE_TOLERANCE 1.0e-1
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
@ -194,27 +199,45 @@ osg::Plane computePlaneThruPointAndOrientedToEye(const osg::Vec3d& eyeDir, const
|
||||
return plane;
|
||||
}
|
||||
|
||||
osg::Plane computePlaneParallelToAxisAndOrientedToEye(const osg::Vec3d& eyeDir, const osg::Matrix& localToWorld,
|
||||
const osg::Vec3d& axisDir, double radius,
|
||||
// Computes a plane to be used as a basis for determining a displacement. When eyeDir is close
|
||||
// to the cylinder axis, then the plane will be set to be perpendicular to the cylinder axis.
|
||||
// Otherwise it will be set to be parallel to the cylinder axis and oriented towards eyeDir.
|
||||
osg::Plane computeIntersectionPlane(const osg::Vec3d& eyeDir, const osg::Matrix& localToWorld,
|
||||
const osg::Vec3d& axisDir, const osg::Cylinder& cylinder,
|
||||
osg::Vec3d& planeLineStart, osg::Vec3d& planeLineEnd,
|
||||
bool front)
|
||||
bool& parallelPlane, bool front)
|
||||
{
|
||||
osg::Vec3d perpDir = axisDir ^ getLocalEyeDirection(eyeDir, localToWorld);
|
||||
osg::Plane plane;
|
||||
|
||||
osg::Vec3d unitAxisDir = axisDir;
|
||||
unitAxisDir.normalize();
|
||||
osg::Vec3d perpDir = unitAxisDir ^ getLocalEyeDirection(eyeDir, localToWorld);
|
||||
|
||||
// Check to make sure eye and cylinder axis are not too close
|
||||
if(perpDir.length2() < CROSS_PRODUCT_ANGLE_TOLERANCE)
|
||||
{
|
||||
// Too close, so instead return plane perpendicular to cylinder axis.
|
||||
plane.set(unitAxisDir, cylinder.getCenter());
|
||||
parallelPlane = false;
|
||||
return plane;
|
||||
}
|
||||
|
||||
// Otherwise compute plane along axisDir oriented towards eye
|
||||
osg::Vec3d planeDir = perpDir ^ axisDir;
|
||||
planeDir.normalize();
|
||||
if (! front)
|
||||
planeDir = -planeDir;
|
||||
|
||||
osg::Vec3d planePoint = planeDir * radius + axisDir;
|
||||
osg::Plane plane;
|
||||
osg::Vec3d planePoint = planeDir * cylinder.getRadius() + axisDir;
|
||||
plane.set(planeDir, planePoint);
|
||||
|
||||
planeLineStart = planePoint;
|
||||
planeLineEnd = planePoint + axisDir;
|
||||
parallelPlane = true;
|
||||
return plane;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
Projector::Projector() : _worldToLocalDirty(false)
|
||||
@ -563,86 +586,20 @@ bool CylinderPlaneProjector::project(const PointerInfo& pi, osg::Vec3d& projecte
|
||||
objectNearPoint = nearPoint * getWorldToLocal();
|
||||
objectFarPoint = farPoint * getWorldToLocal();
|
||||
|
||||
// Find the intersection of the sphere with the line.
|
||||
osg::Vec3d cylIntersection;
|
||||
bool hitCylinder = false;
|
||||
if (_front)
|
||||
{
|
||||
osg::Vec3d dontCare;
|
||||
hitCylinder = getCylinderLineIntersection(*_cylinder, objectNearPoint, objectFarPoint, cylIntersection, dontCare);
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::Vec3d dontCare;
|
||||
hitCylinder = getCylinderLineIntersection(*_cylinder, objectNearPoint, objectFarPoint, dontCare, cylIntersection);
|
||||
}
|
||||
|
||||
// Compute plane oriented to the eye.
|
||||
_plane = computePlaneParallelToAxisAndOrientedToEye(pi.getEyeDir(), getLocalToWorld(), _cylinderAxis,
|
||||
getCylinder()->getRadius(), _planeLineStart, _planeLineEnd,
|
||||
_front);
|
||||
|
||||
// Find the intersection on the plane.
|
||||
osg::Vec3d planeIntersection;
|
||||
getPlaneLineIntersection(_plane.asVec4(), objectNearPoint, objectFarPoint, planeIntersection);
|
||||
|
||||
if (hitCylinder)
|
||||
{
|
||||
osg::Vec3d projectIntersection;
|
||||
getPlaneLineIntersection(_plane.asVec4(), cylIntersection, cylIntersection + _plane.getNormal(), projectIntersection);
|
||||
|
||||
osg::Vec3d closestPointToCylAxis;
|
||||
computeClosestPointOnLine(getCylinder()->getCenter(), getCylinder()->getCenter() + _cylinderAxis,
|
||||
projectIntersection, closestPointToCylAxis);
|
||||
|
||||
// Distance from the plane intersection point to the closest point on the cylinder axis.
|
||||
double dist = (projectIntersection - closestPointToCylAxis).length();
|
||||
|
||||
if (dist < getCylinder()->getRadius())
|
||||
{
|
||||
if (!hitCylinder) return false;
|
||||
projectedPoint = cylIntersection;
|
||||
_onCylinder = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
projectedPoint = planeIntersection;
|
||||
_onCylinder = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
projectedPoint = planeIntersection;
|
||||
_onCylinder = false;
|
||||
}
|
||||
// Computes either a plane parallel to cylinder axis oriented to the eye or the plane
|
||||
// perpendicular to the cylinder axis if the eye-cylinder angle is close.
|
||||
_plane = computeIntersectionPlane(pi.getEyeDir(), getLocalToWorld(), _cylinderAxis,
|
||||
*_cylinder, _planeLineStart, _planeLineEnd,
|
||||
_parallelPlane, _front);
|
||||
|
||||
// Now find the point of intersection on our newly-calculated plane.
|
||||
getPlaneLineIntersection(_plane.asVec4(), objectNearPoint, objectFarPoint, projectedPoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
osg::Quat CylinderPlaneProjector::getRotation(const osg::Vec3d& p1, bool p1OnCyl, const osg::Vec3d& p2, bool p2OnCyl) const
|
||||
{
|
||||
if (p1OnCyl && p2OnCyl)
|
||||
{
|
||||
osg::Vec3d closestPointToCylAxis1, closestPointToCylAxis2;
|
||||
computeClosestPointOnLine(getCylinder()->getCenter(), getCylinder()->getCenter() + _cylinderAxis * getCylinder()->getHeight(),
|
||||
p1, closestPointToCylAxis1);
|
||||
computeClosestPointOnLine(getCylinder()->getCenter(), getCylinder()->getCenter() + _cylinderAxis * getCylinder()->getHeight(),
|
||||
p2, closestPointToCylAxis2);
|
||||
|
||||
osg::Vec3d v1 = p1 - closestPointToCylAxis1;
|
||||
osg::Vec3d v2 = p2 - closestPointToCylAxis2;
|
||||
|
||||
double cosAngle = v1 * v2 / (v1.length() * v2.length());
|
||||
|
||||
if (cosAngle > 1.0 || cosAngle < -1.0)
|
||||
return osg::Quat();
|
||||
|
||||
double angle = acosf(cosAngle);
|
||||
osg::Vec3d rotAxis = v1 ^ v2;
|
||||
|
||||
return osg::Quat(angle, rotAxis);
|
||||
}
|
||||
else if (!p1OnCyl && !p2OnCyl)
|
||||
if(_parallelPlane)
|
||||
{
|
||||
osg::Vec3d closestPointToPlaneLine1, closestPointToPlaneLine2;
|
||||
computeClosestPointOnLine(_planeLineStart, _planeLineEnd,
|
||||
@ -656,6 +613,7 @@ osg::Quat CylinderPlaneProjector::getRotation(const osg::Vec3d& p1, bool p1OnCyl
|
||||
osg::Vec3d diff = v2 - v1;
|
||||
double d = diff.length();
|
||||
|
||||
// The amount of rotation is inversely proportional to the size of the cylinder
|
||||
double angle = (getCylinder()->getRadius() == 0.0) ? 0.0 : (d / getCylinder()->getRadius());
|
||||
osg::Vec3d rotAxis = _plane.getNormal() ^ v1;
|
||||
|
||||
@ -663,25 +621,20 @@ osg::Quat CylinderPlaneProjector::getRotation(const osg::Vec3d& p1, bool p1OnCyl
|
||||
return osg::Quat(angle, rotAxis);
|
||||
else
|
||||
return osg::Quat(-angle, rotAxis);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::Vec3d offCylinderPt = (p1OnCyl) ? p2 : p1;
|
||||
osg::Vec3d v1 = p1 - getCylinder()->getCenter();
|
||||
osg::Vec3d v2 = p2 - getCylinder()->getCenter();
|
||||
|
||||
osg::Vec3d linePtNearest;
|
||||
computeClosestPointOnLine(_planeLineStart, _planeLineEnd,
|
||||
offCylinderPt, linePtNearest);
|
||||
osg::Vec3d dirToOffCylinderPt = offCylinderPt - linePtNearest;
|
||||
dirToOffCylinderPt.normalize();
|
||||
double cosAngle = v1 * v2 / (v1.length() * v2.length());
|
||||
|
||||
osg::Vec3d ptOnCylinder = linePtNearest + dirToOffCylinderPt * getCylinder()->getRadius();
|
||||
if (cosAngle > 1.0 || cosAngle < -1.0)
|
||||
return osg::Quat();
|
||||
|
||||
if (p1OnCyl)
|
||||
return (getRotation(p1, true, ptOnCylinder, true) *
|
||||
getRotation(ptOnCylinder, false, p2, false));
|
||||
else
|
||||
return (getRotation(p1, false, ptOnCylinder, false) *
|
||||
getRotation(ptOnCylinder, true, p2, true));
|
||||
double angle = acosf(cosAngle);
|
||||
osg::Vec3d rotAxis = v1 ^ v2;
|
||||
|
||||
return osg::Quat(angle, rotAxis);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user