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 osg::Plane _plane;
|
||||||
mutable bool _onCylinder;
|
mutable bool _onCylinder;
|
||||||
mutable osg::Vec3d _planeLineStart, _planeLineEnd;
|
mutable osg::Vec3d _planeLineStart, _planeLineEnd;
|
||||||
|
mutable bool _parallelPlane;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,11 @@
|
|||||||
|
|
||||||
using namespace osgManipulator;
|
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
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -194,27 +199,45 @@ osg::Plane computePlaneThruPointAndOrientedToEye(const osg::Vec3d& eyeDir, const
|
|||||||
return plane;
|
return plane;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Plane computePlaneParallelToAxisAndOrientedToEye(const osg::Vec3d& eyeDir, const osg::Matrix& localToWorld,
|
// Computes a plane to be used as a basis for determining a displacement. When eyeDir is close
|
||||||
const osg::Vec3d& axisDir, double radius,
|
// 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,
|
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;
|
osg::Vec3d planeDir = perpDir ^ axisDir;
|
||||||
planeDir.normalize();
|
planeDir.normalize();
|
||||||
if (! front)
|
if (! front)
|
||||||
planeDir = -planeDir;
|
planeDir = -planeDir;
|
||||||
|
|
||||||
osg::Vec3d planePoint = planeDir * radius + axisDir;
|
osg::Vec3d planePoint = planeDir * cylinder.getRadius() + axisDir;
|
||||||
osg::Plane plane;
|
|
||||||
plane.set(planeDir, planePoint);
|
plane.set(planeDir, planePoint);
|
||||||
|
|
||||||
planeLineStart = planePoint;
|
planeLineStart = planePoint;
|
||||||
planeLineEnd = planePoint + axisDir;
|
planeLineEnd = planePoint + axisDir;
|
||||||
|
parallelPlane = true;
|
||||||
return plane;
|
return plane;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
Projector::Projector() : _worldToLocalDirty(false)
|
Projector::Projector() : _worldToLocalDirty(false)
|
||||||
@ -563,86 +586,20 @@ bool CylinderPlaneProjector::project(const PointerInfo& pi, osg::Vec3d& projecte
|
|||||||
objectNearPoint = nearPoint * getWorldToLocal();
|
objectNearPoint = nearPoint * getWorldToLocal();
|
||||||
objectFarPoint = farPoint * getWorldToLocal();
|
objectFarPoint = farPoint * getWorldToLocal();
|
||||||
|
|
||||||
// Find the intersection of the sphere with the line.
|
// Computes either a plane parallel to cylinder axis oriented to the eye or the plane
|
||||||
osg::Vec3d cylIntersection;
|
// perpendicular to the cylinder axis if the eye-cylinder angle is close.
|
||||||
bool hitCylinder = false;
|
_plane = computeIntersectionPlane(pi.getEyeDir(), getLocalToWorld(), _cylinderAxis,
|
||||||
if (_front)
|
*_cylinder, _planeLineStart, _planeLineEnd,
|
||||||
{
|
_parallelPlane, _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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Now find the point of intersection on our newly-calculated plane.
|
||||||
|
getPlaneLineIntersection(_plane.asVec4(), objectNearPoint, objectFarPoint, projectedPoint);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Quat CylinderPlaneProjector::getRotation(const osg::Vec3d& p1, bool p1OnCyl, const osg::Vec3d& p2, bool p2OnCyl) const
|
osg::Quat CylinderPlaneProjector::getRotation(const osg::Vec3d& p1, bool p1OnCyl, const osg::Vec3d& p2, bool p2OnCyl) const
|
||||||
{
|
{
|
||||||
if (p1OnCyl && p2OnCyl)
|
if(_parallelPlane)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
osg::Vec3d closestPointToPlaneLine1, closestPointToPlaneLine2;
|
osg::Vec3d closestPointToPlaneLine1, closestPointToPlaneLine2;
|
||||||
computeClosestPointOnLine(_planeLineStart, _planeLineEnd,
|
computeClosestPointOnLine(_planeLineStart, _planeLineEnd,
|
||||||
@ -656,6 +613,7 @@ osg::Quat CylinderPlaneProjector::getRotation(const osg::Vec3d& p1, bool p1OnCyl
|
|||||||
osg::Vec3d diff = v2 - v1;
|
osg::Vec3d diff = v2 - v1;
|
||||||
double d = diff.length();
|
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());
|
double angle = (getCylinder()->getRadius() == 0.0) ? 0.0 : (d / getCylinder()->getRadius());
|
||||||
osg::Vec3d rotAxis = _plane.getNormal() ^ v1;
|
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);
|
return osg::Quat(angle, rotAxis);
|
||||||
else
|
else
|
||||||
return osg::Quat(-angle, rotAxis);
|
return osg::Quat(-angle, rotAxis);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
osg::Vec3d offCylinderPt = (p1OnCyl) ? p2 : p1;
|
osg::Vec3d v1 = p1 - getCylinder()->getCenter();
|
||||||
|
osg::Vec3d v2 = p2 - getCylinder()->getCenter();
|
||||||
|
|
||||||
osg::Vec3d linePtNearest;
|
double cosAngle = v1 * v2 / (v1.length() * v2.length());
|
||||||
computeClosestPointOnLine(_planeLineStart, _planeLineEnd,
|
|
||||||
offCylinderPt, linePtNearest);
|
|
||||||
osg::Vec3d dirToOffCylinderPt = offCylinderPt - linePtNearest;
|
|
||||||
dirToOffCylinderPt.normalize();
|
|
||||||
|
|
||||||
osg::Vec3d ptOnCylinder = linePtNearest + dirToOffCylinderPt * getCylinder()->getRadius();
|
if (cosAngle > 1.0 || cosAngle < -1.0)
|
||||||
|
return osg::Quat();
|
||||||
|
|
||||||
if (p1OnCyl)
|
double angle = acosf(cosAngle);
|
||||||
return (getRotation(p1, true, ptOnCylinder, true) *
|
osg::Vec3d rotAxis = v1 ^ v2;
|
||||||
getRotation(ptOnCylinder, false, p2, false));
|
|
||||||
else
|
return osg::Quat(angle, rotAxis);
|
||||||
return (getRotation(p1, false, ptOnCylinder, false) *
|
|
||||||
getRotation(ptOnCylinder, true, p2, true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user