Changeset 13020

Show
Ignore:
Timestamp:
03/06/12 11:08:49 (2 years ago)
Author:
robert
Message:

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.
"

Location:
OpenSceneGraph/trunk
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • OpenSceneGraph/trunk/include/osgManipulator/Projector

    r9196 r13020  
    296296        mutable bool       _onCylinder; 
    297297        mutable osg::Vec3d _planeLineStart, _planeLineEnd; 
     298        mutable bool       _parallelPlane; 
    298299}; 
    299300 
  • OpenSceneGraph/trunk/src/osgManipulator/Projector.cpp

    r11483 r13020  
    1717using namespace osgManipulator; 
    1818 
     19// When the squared magnitude (length2) of the cross product of 2 
     20// angles is less than this tolerance, they are considered parallel. 
     21// osg::Vec3 a, b; (a ^ b).length2() 
     22#define CROSS_PRODUCT_ANGLE_TOLERANCE 1.0e-1 
     23 
    1924namespace 
    2025{ 
     
    195200} 
    196201 
    197 osg::Plane computePlaneParallelToAxisAndOrientedToEye(const osg::Vec3d& eyeDir, const osg::Matrix& localToWorld, 
    198                                                       const osg::Vec3d& axisDir, double radius, 
    199                                                       osg::Vec3d& planeLineStart, osg::Vec3d& planeLineEnd, 
    200                                                       bool front) 
    201 { 
    202     osg::Vec3d perpDir = axisDir ^ getLocalEyeDirection(eyeDir, localToWorld); 
     202// Computes a plane to be used as a basis for determining a displacement.  When eyeDir is close 
     203// to the cylinder axis, then the plane will be set to be perpendicular to the cylinder axis. 
     204// Otherwise it will be set to be parallel to the cylinder axis and oriented towards eyeDir. 
     205osg::Plane computeIntersectionPlane(const osg::Vec3d& eyeDir, const osg::Matrix& localToWorld, 
     206                                    const osg::Vec3d& axisDir, const osg::Cylinder& cylinder, 
     207                                    osg::Vec3d& planeLineStart, osg::Vec3d& planeLineEnd, 
     208                                    bool& parallelPlane, bool front) 
     209{ 
     210    osg::Plane plane; 
     211 
     212    osg::Vec3d unitAxisDir = axisDir; 
     213    unitAxisDir.normalize(); 
     214    osg::Vec3d perpDir = unitAxisDir ^ getLocalEyeDirection(eyeDir, localToWorld); 
     215 
     216    // Check to make sure eye and cylinder axis are not too close 
     217    if(perpDir.length2() < CROSS_PRODUCT_ANGLE_TOLERANCE) 
     218    { 
     219        // Too close, so instead return plane perpendicular to cylinder axis. 
     220        plane.set(unitAxisDir, cylinder.getCenter()); 
     221        parallelPlane = false; 
     222        return plane; 
     223    } 
     224 
     225    // Otherwise compute plane along axisDir oriented towards eye 
    203226    osg::Vec3d planeDir = perpDir ^ axisDir; 
    204227    planeDir.normalize(); 
     
    206229        planeDir = -planeDir; 
    207230 
    208     osg::Vec3d planePoint = planeDir * radius + axisDir; 
    209     osg::Plane plane; 
     231    osg::Vec3d planePoint = planeDir * cylinder.getRadius() + axisDir; 
    210232    plane.set(planeDir, planePoint); 
    211233 
    212234    planeLineStart = planePoint; 
    213235    planeLineEnd = planePoint + axisDir; 
     236    parallelPlane = true; 
    214237    return plane; 
    215238} 
    216239 
    217 } 
     240} // namespace 
    218241 
    219242 
     
    564587    objectFarPoint  = farPoint * getWorldToLocal(); 
    565588 
    566     // Find the intersection of the sphere with the line. 
    567     osg::Vec3d cylIntersection; 
    568     bool hitCylinder = false; 
    569     if (_front) 
    570     { 
    571         osg::Vec3d dontCare; 
    572         hitCylinder = getCylinderLineIntersection(*_cylinder, objectNearPoint, objectFarPoint, cylIntersection, dontCare); 
    573     } 
    574     else 
    575     { 
    576         osg::Vec3d dontCare; 
    577         hitCylinder = getCylinderLineIntersection(*_cylinder, objectNearPoint, objectFarPoint, dontCare, cylIntersection); 
    578     } 
    579  
    580     // Compute plane oriented to the eye. 
    581     _plane = computePlaneParallelToAxisAndOrientedToEye(pi.getEyeDir(), getLocalToWorld(), _cylinderAxis, 
    582                                                         getCylinder()->getRadius(), _planeLineStart, _planeLineEnd, 
    583                                    _front); 
    584  
    585     // Find the intersection on the plane. 
    586     osg::Vec3d planeIntersection; 
    587     getPlaneLineIntersection(_plane.asVec4(), objectNearPoint, objectFarPoint, planeIntersection); 
    588  
    589     if (hitCylinder) 
    590     { 
    591         osg::Vec3d projectIntersection; 
    592         getPlaneLineIntersection(_plane.asVec4(), cylIntersection, cylIntersection + _plane.getNormal(), projectIntersection); 
    593  
    594         osg::Vec3d closestPointToCylAxis; 
    595         computeClosestPointOnLine(getCylinder()->getCenter(), getCylinder()->getCenter() + _cylinderAxis, 
    596                                   projectIntersection, closestPointToCylAxis); 
    597  
    598         // Distance from the plane intersection point to the closest point on the cylinder axis. 
    599         double dist = (projectIntersection - closestPointToCylAxis).length(); 
    600  
    601         if (dist < getCylinder()->getRadius()) 
    602         { 
    603             if (!hitCylinder) return false; 
    604             projectedPoint = cylIntersection; 
    605             _onCylinder = true; 
    606         } 
    607         else 
    608         { 
    609             projectedPoint = planeIntersection; 
    610             _onCylinder = false; 
    611         } 
    612     } 
    613     else 
    614     { 
    615         projectedPoint = planeIntersection; 
    616         _onCylinder = false; 
    617     } 
    618  
     589    // Computes either a plane parallel to cylinder axis oriented to the eye or the plane 
     590    // perpendicular to the cylinder axis if the eye-cylinder angle is close. 
     591    _plane = computeIntersectionPlane(pi.getEyeDir(), getLocalToWorld(), _cylinderAxis, 
     592                                      *_cylinder, _planeLineStart, _planeLineEnd, 
     593                                     _parallelPlane, _front); 
     594 
     595    // Now find the point of intersection on our newly-calculated plane. 
     596    getPlaneLineIntersection(_plane.asVec4(), objectNearPoint, objectFarPoint, projectedPoint); 
    619597    return true; 
    620598} 
     
    622600osg::Quat CylinderPlaneProjector::getRotation(const osg::Vec3d& p1, bool p1OnCyl, const osg::Vec3d& p2, bool p2OnCyl) const 
    623601{ 
    624     if (p1OnCyl && p2OnCyl) 
    625     { 
    626         osg::Vec3d closestPointToCylAxis1, closestPointToCylAxis2; 
    627         computeClosestPointOnLine(getCylinder()->getCenter(), getCylinder()->getCenter() + _cylinderAxis * getCylinder()->getHeight(), 
    628                                   p1, closestPointToCylAxis1); 
    629         computeClosestPointOnLine(getCylinder()->getCenter(), getCylinder()->getCenter() + _cylinderAxis * getCylinder()->getHeight(), 
    630                                   p2, closestPointToCylAxis2); 
    631  
    632         osg::Vec3d v1 = p1 - closestPointToCylAxis1; 
    633         osg::Vec3d v2 = p2 - closestPointToCylAxis2; 
    634  
    635         double cosAngle = v1 * v2 / (v1.length() * v2.length()); 
    636  
    637         if (cosAngle > 1.0 || cosAngle < -1.0) 
    638             return osg::Quat(); 
    639  
    640         double angle = acosf(cosAngle); 
    641         osg::Vec3d rotAxis = v1 ^ v2; 
    642  
    643         return osg::Quat(angle, rotAxis); 
    644     } 
    645     else if (!p1OnCyl && !p2OnCyl) 
     602    if(_parallelPlane) 
    646603    { 
    647604        osg::Vec3d closestPointToPlaneLine1, closestPointToPlaneLine2; 
     
    657614        double d = diff.length(); 
    658615 
     616        // The amount of rotation is inversely proportional to the size of the cylinder 
    659617        double angle = (getCylinder()->getRadius() == 0.0) ? 0.0 : (d / getCylinder()->getRadius()); 
    660618        osg::Vec3d rotAxis = _plane.getNormal() ^ v1; 
    661619 
    662620        if (v2.length() > v1.length()) 
    663             return osg::Quat(angle, rotAxis); 
     621           return osg::Quat(angle, rotAxis); 
    664622        else 
    665             return osg::Quat(-angle, rotAxis); 
    666  
     623           return osg::Quat(-angle, rotAxis); 
    667624    } 
    668625    else 
    669626    { 
    670         osg::Vec3d offCylinderPt = (p1OnCyl) ? p2 : p1; 
    671  
    672         osg::Vec3d linePtNearest; 
    673         computeClosestPointOnLine(_planeLineStart, _planeLineEnd, 
    674                                   offCylinderPt, linePtNearest); 
    675         osg::Vec3d dirToOffCylinderPt = offCylinderPt - linePtNearest; 
    676         dirToOffCylinderPt.normalize(); 
    677  
    678         osg::Vec3d ptOnCylinder = linePtNearest + dirToOffCylinderPt * getCylinder()->getRadius(); 
    679  
    680         if (p1OnCyl) 
    681             return (getRotation(p1, true, ptOnCylinder, true) * 
    682                     getRotation(ptOnCylinder, false, p2, false)); 
    683         else 
    684             return (getRotation(p1, false, ptOnCylinder, false) * 
    685                     getRotation(ptOnCylinder, true, p2, true)); 
    686     } 
    687 } 
     627        osg::Vec3d v1 = p1 - getCylinder()->getCenter(); 
     628        osg::Vec3d v2 = p2 - getCylinder()->getCenter(); 
     629 
     630        double cosAngle = v1 * v2 / (v1.length() * v2.length()); 
     631 
     632        if (cosAngle > 1.0 || cosAngle < -1.0) 
     633            return osg::Quat(); 
     634 
     635        double angle = acosf(cosAngle); 
     636        osg::Vec3d rotAxis = v1 ^ v2; 
     637 
     638        return osg::Quat(angle, rotAxis); 
     639    } 
     640}