99// of the License, or (at your option) any later version.
1010
1111#include < numeric>
12+ #include < optional>
1213#include < celmath/geomutil.h>
14+ #include < celmath/intersect.h>
1315#include < celmath/mathlib.h>
1416#include < celmath/solve.h>
1517#include " body.h"
@@ -29,7 +31,10 @@ namespace math = celestia::math;
2931
3032#define VELOCITY_CHANGE_TIME 0 .25f
3133
32- static Vector3d slerp (double t, const Vector3d& v0, const Vector3d& v1)
34+ namespace
35+ {
36+
37+ Vector3d slerp (double t, const Vector3d& v0, const Vector3d& v1)
3338{
3439 double r0 = v0.norm ();
3540 double r1 = v1.norm ();
@@ -46,6 +51,19 @@ static Vector3d slerp(double t, const Vector3d& v0, const Vector3d& v1)
4651 return (cos (theta * t) * u + sin (theta * t) * v) * math::lerp (t, r0, r1);
4752}
4853
54+ std::optional<Eigen::Vector3d> getNearIntersectionPoint (const Eigen::ParametrizedLine<double , 3 >& ray,
55+ const math::Sphered& sphere)
56+ {
57+ double distance;
58+ bool intersects = math::testIntersection (ray, sphere, distance);
59+ if (intersects)
60+ return ray.origin () + distance * ray.direction ();
61+ else
62+ return std::nullopt ;
63+ }
64+
65+ }
66+
4967
5068/* ! Notes on the Observer class
5169 * The values position and orientation are in observer's reference frame. positionUniv
@@ -909,6 +927,50 @@ void Observer::orbit(const Selection& selection, const Quaternionf& q)
909927 }
910928}
911929
930+ /* ! Orbit around the reference object (if there is one.) rotating the object with intersection point from
931+ * a direction to another. If there is no intersection point, return false.
932+ */
933+ bool Observer::orbit (const Selection& selection, const Eigen::Vector3f &from, const Eigen::Vector3f &to)
934+ {
935+ Selection center = frame->getRefObject ();
936+ if (center.empty ())
937+ {
938+ if (selection.empty ())
939+ return false ;
940+ center = selection;
941+ }
942+
943+ double radius = center.radius ();
944+ if (radius <= 0.0 )
945+ return false ;
946+
947+ // Get the focus position (center of rotation) in frame
948+ // coordinates; in order to make this function work in all
949+ // frames of reference, it's important to work in frame
950+ // coordinates.
951+ UniversalCoord focusPosition = center.getPosition (getTime ());
952+ focusPosition = frame->convertFromUniversal (focusPosition, getTime ());
953+
954+ Eigen::Vector3d objectCenter = focusPosition.offsetFromKm (position);
955+
956+ // Get the rays adjusted to orientation
957+ Eigen::Vector3d transformedFrom = getOrientation ().conjugate () * from.cast <double >();
958+ Eigen::Vector3d transformedTo = getOrientation ().conjugate () * to.cast <double >();
959+
960+ // Find intersections for the rays, if no intersection, return false
961+ math::Sphered sphere { objectCenter, radius };
962+ auto orbitStartPosition = getNearIntersectionPoint (Eigen::ParametrizedLine<double , 3 >(Eigen::Vector3d::Zero (), transformedFrom), sphere);
963+ if (!orbitStartPosition.has_value ())
964+ return false ;
965+
966+ auto orbitEndPosition = getNearIntersectionPoint (Eigen::ParametrizedLine<double , 3 >(Eigen::Vector3d::Zero (), transformedTo), sphere);
967+ if (!orbitEndPosition.has_value ())
968+ return false ;
969+
970+ orbit (selection, Eigen::Quaterniond::FromTwoVectors (transformedOrientation * (orbitStartPosition.value () - objectCenter), transformedOrientation * (orbitEndPosition.value () - objectCenter)).cast <float >());
971+ return true ;
972+ }
973+
912974
913975/* ! Exponential camera dolly--move toward or away from the selected object
914976 * at a rate dependent on the observer's distance from the object.
0 commit comments