observer.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:51k
源码类别:

OpenGL

开发平台:

Visual C++

  1. // observer.cpp
  2. //
  3. // Copyright (C) 2001-2008, Chris Laurel <claurel@shatters.net>
  4. //
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the GNU General Public License
  7. // as published by the Free Software Foundation; either version 2
  8. // of the License, or (at your option) any later version.
  9. #include <celmath/mathlib.h>
  10. #include <celmath/solve.h>
  11. #include "observer.h"
  12. #include "simulation.h"
  13. #include "frametree.h"
  14. static const double maximumSimTime = 730486721060.00073; // 2000000000 Jan 01 12:00:00 UTC
  15. static const double minimumSimTime = -730498278941.99951; // -2000000000 Jan 01 12:00:00 UTC
  16. using namespace std;
  17. #define VELOCITY_CHANGE_TIME      0.25f
  18. static Vec3d slerp(double t, const Vec3d& v0, const Vec3d& v1)
  19. {
  20.     double r0 = v0.length();
  21.     double r1 = v1.length();
  22.     Vec3d u = v0 / r0;
  23.     Vec3d n = u ^ (v1 / r1);
  24.     n.normalize();
  25.     Vec3d v = n ^ u;
  26.     if (v * v1 < 0.0)
  27.         v = -v;
  28.     double cosTheta = u * (v1 / r1);
  29.     double theta = acos(cosTheta);
  30.     return (cos(theta * t) * u + sin(theta * t) * v) * Mathd::lerp(t, r0, r1);
  31. }
  32. /*! Notes on the Observer class
  33.  *  The values position and orientation are in observer's reference frame. positionUniv
  34.  *  and orientationUniv are the equivalent values in the universal coordinate system.
  35.  *  They must be kept in sync. Generally, it's position and orientation that are modified;
  36.  *  after they're changed, the method updateUniversal is called. However, when the observer
  37.  *  frame is changed, positionUniv and orientationUniv are not changed, but the position
  38.  *  and orientation within the frame /do/ change. Thus, a 'reverse' update is necessary.
  39.  *
  40.  *  There are two types of 'automatic' updates to position and orientation that may
  41.  *  occur when the observer's update method is called: updates from free travel, and
  42.  *  updates due to an active goto operation.
  43.  */
  44. Observer::Observer() :
  45.     simTime(0.0),
  46.     position(0.0, 0.0, 0.0),
  47.     orientation(1.0),
  48.     velocity(0.0, 0.0, 0.0),
  49.     angularVelocity(0.0, 0.0, 0.0),
  50.     frame(NULL),
  51.     realTime(0.0),
  52.     targetSpeed(0.0),
  53.     targetVelocity(0.0, 0.0, 0.0),
  54.     initialVelocity(0.0, 0.0, 0.0),
  55.     beginAccelTime(0.0),
  56.     observerMode(Free),
  57.     trackingOrientation(1.0f, 0.0f, 0.0f, 0.0f),
  58.     fov((float) (PI / 4.0)),
  59.     reverseFlag(false),
  60.     locationFilter(~0u)
  61. {
  62.     frame = new ObserverFrame();
  63.     updateUniversal();
  64. }
  65. /*! Copy constructor. */
  66. Observer::Observer(const Observer& o) :
  67. simTime(o.simTime),
  68. position(o.position),
  69. orientation(o.orientation),
  70. velocity(o.velocity),
  71. angularVelocity(o.angularVelocity),
  72. frame(NULL),
  73. realTime(o.realTime),
  74. targetSpeed(o.targetSpeed),
  75. targetVelocity(o.targetVelocity),
  76. beginAccelTime(o.beginAccelTime),
  77. observerMode(o.observerMode),
  78. journey(o.journey),
  79. trackObject(o.trackObject),
  80. trackingOrientation(o.trackingOrientation),
  81. fov(o.fov),
  82. reverseFlag(o.reverseFlag),
  83. locationFilter(o.locationFilter),
  84. displayedSurface(o.displayedSurface)
  85. {
  86. frame = new ObserverFrame(*o.frame);
  87. updateUniversal();
  88. }
  89. Observer& Observer::operator=(const Observer& o)
  90. {
  91. simTime = o.simTime;
  92. position = o.position;
  93. orientation = o.orientation;
  94. velocity = o.velocity;
  95. angularVelocity = o.angularVelocity;
  96. frame = NULL;
  97. realTime = o.realTime;
  98. targetSpeed = o.targetSpeed;
  99. targetVelocity = o.targetVelocity;
  100. beginAccelTime = o.beginAccelTime;
  101. observerMode = o.observerMode;
  102. journey = o.journey;
  103. trackObject = o.trackObject;
  104. trackingOrientation = o.trackingOrientation;
  105. fov = o.fov;
  106. reverseFlag = o.reverseFlag;
  107. locationFilter = o.locationFilter;
  108. displayedSurface = o.displayedSurface;
  109. setFrame(*o.frame);
  110. updateUniversal();
  111. return *this;
  112. }
  113. /*! Get the current simulation time. The time returned is a Julian date,
  114.  *  and the time standard is TDB.
  115.  */
  116. double Observer::getTime() const
  117. {
  118.     return simTime;
  119. };
  120. /*! Get the current real time. The time returned is a Julian date,
  121.  *  and the time standard is TDB.
  122.  */
  123. double Observer::getRealTime() const
  124. {
  125.     return realTime;
  126. };
  127. /*! Set the simulation time (Julian date, TDB time standard)
  128. */
  129. void Observer::setTime(double jd)
  130. {
  131.     simTime = jd;
  132. updateUniversal();
  133. }
  134. /*! Return the position of the observer in universal coordinates. The origin
  135.  *  The origin of this coordinate system is the Solar System Barycenter, and
  136.  *  axes are defined by the J2000 ecliptic and equinox.
  137.  */
  138. UniversalCoord Observer::getPosition() const
  139. {
  140.     return positionUniv;
  141. }
  142. // TODO: Low-precision set position that should be removed
  143. void Observer::setPosition(const Point3d& p)
  144. {
  145.     setPosition(UniversalCoord(p));
  146. }
  147. /*! Set the position of the observer; position is specified in the universal
  148.  *  coordinate system.
  149.  */
  150. void Observer::setPosition(const UniversalCoord& p)
  151. {
  152.     positionUniv = p;
  153.     position = frame->convertFromUniversal(p, getTime());
  154. }
  155. /*! Return the orientation of the observer in the universal coordinate
  156.  *  system.
  157.  */
  158. Quatd Observer::getOrientation() const
  159. {
  160.     return orientationUniv;
  161. }
  162. /*! Reduced precision version of getOrientation()
  163.  */
  164. Quatf Observer::getOrientationf() const
  165. {
  166.     Quatd q = getOrientation();
  167.     return Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
  168. }
  169. /* Set the orientation of the observer. The orientation is specified in
  170.  * the universal coordinate system.
  171.  */
  172. void Observer::setOrientation(const Quatf& q)
  173. {
  174.     /*
  175.     RigidTransform rt = frame.toUniversal(situation, getTime());
  176.     rt.rotation = Quatd(q.w, q.x, q.y, q.z);
  177.     situation = frame.fromUniversal(rt, getTime());
  178.      */
  179.     setOrientation(Quatd(q.w, q.x, q.y, q.z));
  180. }
  181. /*! Set the orientation of the observer. The orientation is specified in
  182.  *  the universal coordinate system.
  183.  */
  184. void Observer::setOrientation(const Quatd& q)
  185. {
  186.     orientationUniv = q;
  187.     orientation = frame->convertFromUniversal(q, getTime());
  188. }
  189. /*! Get the velocity of the observer within the observer's reference frame.
  190.  */
  191. Vec3d Observer::getVelocity() const
  192. {
  193.     return velocity;
  194. }
  195. /*! Set the velocity of the observer within the observer's reference frame.
  196. */
  197. void Observer::setVelocity(const Vec3d& v)
  198. {
  199.     velocity = v;
  200. }
  201. Vec3d Observer::getAngularVelocity() const
  202. {
  203.     return angularVelocity;
  204. }
  205. void Observer::setAngularVelocity(const Vec3d& v)
  206. {
  207.     angularVelocity = v;
  208. }
  209. /*! Determine an orientation that will make the negative z-axis point from
  210.  *  from the observer to the target, with the y-axis pointing in direction
  211.  *  of the component of 'up' that is orthogonal to the z-axis.
  212.  */
  213. // TODO: This is a generally useful function that should be moved to
  214. // the celmath package.
  215. template<class T> static Quaternion<T> 
  216. lookAt(Point3<T> from, Point3<T> to, Vector3<T> up)
  217. {
  218.     Vector3<T> n = to - from;
  219.     n.normalize();
  220.     Vector3<T> v = n ^ up;
  221.     v.normalize();
  222.     Vector3<T> u = v ^ n;
  223.     return Quaternion<T>::matrixToQuaternion(Matrix3<T>(v, u, -n));
  224. }
  225. double Observer::getArrivalTime() const
  226. {
  227.     if (observerMode != Travelling)
  228.         return realTime;
  229.     else
  230.         return journey.startTime + journey.duration;
  231. }
  232. /*! Tick the simulation by dt seconds. Update the observer position
  233.  *  and orientation due to an active goto command or non-zero velocity
  234.  *  or angular velocity.
  235.  */
  236. void Observer::update(double dt, double timeScale)
  237. {
  238.     realTime += dt;
  239.     simTime += (dt / 86400.0) * timeScale;
  240.     if (simTime >= maximumSimTime)
  241.         simTime = maximumSimTime;
  242.     if (simTime <= minimumSimTime)
  243.         simTime = minimumSimTime;
  244.     if (observerMode == Travelling)
  245.     {
  246.         // Compute the fraction of the trip that has elapsed; handle zero
  247.         // durations correctly by skipping directly to the destination.
  248.         float t = 1.0;
  249.         if (journey.duration > 0)
  250.             t = (float) clamp((realTime - journey.startTime) / journey.duration);
  251.         Vec3d jv = journey.to - journey.from;
  252.         UniversalCoord p;
  253.         // Another interpolation method . . . accelerate exponentially,
  254.         // maintain a constant velocity for a period of time, then
  255.         // decelerate.  The portion of the trip spent accelerating is
  256.         // controlled by the parameter journey.accelTime; a value of 1 means
  257.         // that the entire first half of the trip will be spent accelerating
  258.         // and there will be no coasting at constant velocity.
  259.         {
  260.             double u = t < 0.5 ? t * 2 : (1 - t) * 2;
  261.             double x;
  262.             if (u < journey.accelTime)
  263.             {
  264.                 x = exp(journey.expFactor * u) - 1.0;
  265.             }
  266.             else
  267.             {
  268.                 x = exp(journey.expFactor * journey.accelTime) *
  269.                     (journey.expFactor * (u - journey.accelTime) + 1) - 1;
  270.             }
  271.             if (journey.traj == Linear)
  272.             {
  273.                 Vec3d v = jv;
  274.                 if (v.length() == 0.0)
  275.                 {
  276.                     p = journey.from;
  277.                 }
  278.                 else
  279.                 {
  280.                     v.normalize();
  281.                     if (t < 0.5)
  282.                         p = journey.from + v * astro::kilometersToMicroLightYears(x);
  283.                     else
  284.                         p = journey.to - v * astro::kilometersToMicroLightYears(x);
  285.                 }
  286.             }
  287.             else if (journey.traj == GreatCircle)
  288.             {
  289.                 Selection centerObj = frame->getRefObject();
  290.                 if (centerObj.body() != NULL)
  291.                 {
  292.                     Body* body = centerObj.body();
  293.                     if (body->getSystem())
  294.                     {
  295.                         if (body->getSystem()->getPrimaryBody() != NULL)
  296.                             centerObj = Selection(body->getSystem()->getPrimaryBody());
  297.                         else
  298.                             centerObj = Selection(body->getSystem()->getStar());
  299.                     }
  300.                 }
  301.                 UniversalCoord ufrom  = frame->convertToUniversal(journey.from, simTime);
  302.                 UniversalCoord uto    = frame->convertToUniversal(journey.to, simTime);
  303.                 UniversalCoord origin = centerObj.getPosition(simTime);
  304.                 Vec3d v0 = ufrom - origin;
  305.                 Vec3d v1 = uto - origin;
  306.                 if (jv.length() == 0.0)
  307.                 {
  308.                     p = journey.from;
  309.                 }
  310.                 else
  311.                 {
  312.                     x = astro::kilometersToMicroLightYears(x / jv.length());
  313.                     Vec3d v;
  314.                     if (t < 0.5)
  315.                         v = slerp(x, v0, v1);
  316.                     else
  317.                         v = slerp(x, v1, v0);
  318.                     p = frame->convertFromUniversal(origin + v, simTime);
  319.                 }
  320.             }
  321.             else if (journey.traj == CircularOrbit)
  322.             {
  323.                 Selection centerObj = frame->getRefObject();
  324.                 UniversalCoord ufrom = frame->convertToUniversal(journey.from, simTime);
  325.                 UniversalCoord uto   = frame->convertToUniversal(journey.to, simTime);
  326.                 UniversalCoord origin = centerObj.getPosition(simTime);
  327.                 Vec3d v0 = ufrom - origin;
  328.                 Vec3d v1 = uto - origin;
  329.                 if (jv.length() == 0.0)
  330.                 {
  331.                     p = journey.from;
  332.                 }
  333.                 else
  334.                 {
  335.                     //x = astro::kilometersToMicroLightYears(x / jv.length());
  336.                     Quatd q0(1.0);
  337.                     Quatd q1(journey.rotation1.w, journey.rotation1.x,
  338.                              journey.rotation1.y, journey.rotation1.z);
  339.                     p = origin + v0 * Quatd::slerp(q0, q1, t).toMatrix3();
  340.                     p = frame->convertFromUniversal(p, simTime);
  341.                 }
  342.             }
  343.         }
  344.         // Spherically interpolate the orientation over the first half
  345.         // of the journey.
  346.         Quatd q;
  347.         if (t >= journey.startInterpolation && t < journey.endInterpolation )
  348.         {
  349.             // Smooth out the interpolation to avoid jarring changes in
  350.             // orientation
  351.             double v;
  352.             if (journey.traj == CircularOrbit)
  353.             {
  354.                 // In circular orbit mode, interpolation of orientation must
  355.                 // match the interpolation of position.
  356.                 v = t;
  357.             }
  358.             else
  359.             {
  360.                 v = pow(sin((t - journey.startInterpolation) /
  361.                             (journey.endInterpolation - journey.startInterpolation) * PI / 2), 2);
  362.             }
  363.             // Be careful to choose the shortest path when interpolating
  364.             if (norm(journey.initialOrientation - journey.finalOrientation) <
  365.                 norm(journey.initialOrientation + journey.finalOrientation))
  366.             {
  367.                 q = Quatd::slerp(journey.initialOrientation,
  368.                                  journey.finalOrientation, v);
  369.             }
  370.             else
  371.             {
  372.                 q = Quatd::slerp(journey.initialOrientation,
  373.                                 -journey.finalOrientation, v);
  374.             }
  375.         }
  376.         else if (t < journey.startInterpolation)
  377.         {
  378.             q = journey.initialOrientation;
  379.         }
  380.         else // t >= endInterpolation
  381.         {
  382.             q = journey.finalOrientation;
  383.         }
  384.         position = p;
  385.         orientation = q;
  386.         
  387.         // If the journey's complete, reset to manual control
  388.         if (t == 1.0f)
  389.         {
  390.             if (journey.traj != CircularOrbit)
  391.             {
  392.                 //situation = RigidTransform(journey.to, journey.finalOrientation);
  393.                 position = journey.to;
  394.                 orientation = journey.finalOrientation;
  395.             }
  396.             observerMode = Free;
  397.             setVelocity(Vec3d(0, 0, 0));
  398. //            targetVelocity = Vec3d(0, 0, 0);
  399.         }
  400.     }
  401.     if (getVelocity() != targetVelocity)
  402.     {
  403.         double t = clamp((realTime - beginAccelTime) / VELOCITY_CHANGE_TIME);
  404.         Vec3d v = getVelocity() * (1.0 - t) + targetVelocity * t;
  405.         // At some threshold, we just set the velocity to zero; otherwise,
  406.         // we'll end up with ridiculous velocities like 10^-40 m/s.
  407.         if (v.length() < 1.0e-12)
  408.             v = Vec3d(0.0, 0.0, 0.0);
  409.         setVelocity(v);
  410.     }
  411.     // Update the position
  412.     position = position + getVelocity() * dt;
  413.     if (observerMode == Free)
  414.     {
  415.         // Update the observer's orientation
  416.         Vec3d AV = getAngularVelocity();
  417.         Quatd dr = 0.5 * (AV * orientation);
  418.         orientation += dt * dr;
  419.         orientation.normalize();
  420.     }
  421.    
  422.     updateUniversal();
  423.     // Update orientation for tracking--must occur after updateUniversal(), as it
  424.     // relies on the universal position and orientation of the observer.
  425.     if (!trackObject.empty())
  426.     {
  427.         Vec3d up = Vec3d(0, 1, 0) * getOrientation().toMatrix3();
  428.         Vec3d viewDir = trackObject.getPosition(getTime()) - getPosition();
  429.         
  430.         setOrientation(lookAt(Point3d(0, 0, 0),
  431.                               Point3d(viewDir.x, viewDir.y, viewDir.z),
  432.                               up));
  433.     }
  434. }
  435. Selection Observer::getTrackedObject() const
  436. {
  437.     return trackObject;
  438. }
  439. void Observer::setTrackedObject(const Selection& sel)
  440. {
  441.     trackObject = sel;
  442. }
  443. const string& Observer::getDisplayedSurface() const
  444. {
  445.     return displayedSurface;
  446. }
  447. void Observer::setDisplayedSurface(const string& surf)
  448. {
  449.     displayedSurface = surf;
  450. }
  451. uint32 Observer::getLocationFilter() const
  452. {
  453.     return locationFilter;
  454. }
  455. void Observer::setLocationFilter(uint32 _locationFilter)
  456. {
  457.     locationFilter = _locationFilter;
  458. }
  459. void Observer::reverseOrientation()
  460. {
  461.     Quatd q = getOrientation();
  462.     q.yrotate(PI);
  463.     setOrientation(q);
  464.     reverseFlag = !reverseFlag;
  465. }
  466. struct TravelExpFunc : public unary_function<double, double>
  467. {
  468.     double dist, s;
  469.     TravelExpFunc(double d, double _s) : dist(d), s(_s) {};
  470.     double operator()(double x) const
  471.     {
  472.         // return (1.0 / x) * (exp(x / 2.0) - 1.0) - 0.5 - dist / 2.0;
  473.         return exp(x * s) * (x * (1 - s) + 1) - 1 - dist;
  474.     }
  475. };
  476. void Observer::computeGotoParameters(const Selection& destination,
  477.                                      JourneyParams& jparams,
  478.                                      double gotoTime,
  479.                                      double startInter,
  480.                                      double endInter,
  481.                                      Vec3d offset,
  482.                                      ObserverFrame::CoordinateSystem offsetCoordSys,
  483.                                      Vec3f up,
  484.                                      ObserverFrame::CoordinateSystem upCoordSys)
  485. {
  486.     if (frame->getCoordinateSystem() == ObserverFrame::PhaseLock)
  487.     {
  488.         //setFrame(FrameOfReference(astro::Ecliptical, destination));
  489.         setFrame(ObserverFrame::Ecliptical, destination);
  490.     }
  491.     else
  492.     {
  493.         setFrame(frame->getCoordinateSystem(), destination);
  494.     }
  495.     
  496.     UniversalCoord targetPosition = destination.getPosition(getTime());
  497.     Vec3d v = targetPosition - getPosition();
  498.     v.normalize();
  499.     
  500.     jparams.traj = Linear;
  501.     jparams.duration = gotoTime;
  502.     jparams.startTime = realTime;
  503.     
  504.     // Right where we are now . . .
  505.     jparams.from = getPosition();
  506.     
  507.     if (offsetCoordSys == ObserverFrame::ObserverLocal)
  508.     {
  509.         offset = offset * orientationUniv.toMatrix3();
  510.     }
  511.     else
  512.     {
  513.         ObserverFrame offsetFrame(offsetCoordSys, destination);
  514.         offset = offset * offsetFrame.getFrame()->getOrientation(getTime()).toMatrix3();
  515.     }
  516.     jparams.to = targetPosition + offset;
  517.     
  518.     Vec3d upd(up.x, up.y, up.z);
  519.     if (upCoordSys == ObserverFrame::ObserverLocal)
  520.     {
  521.         upd = upd * orientationUniv.toMatrix3();
  522.     }
  523.     else
  524.     {
  525.         ObserverFrame upFrame(upCoordSys, destination);
  526.         upd = upd * upFrame.getFrame()->getOrientation(getTime()).toMatrix3();
  527.     }
  528.     
  529.     jparams.initialOrientation = getOrientation();
  530.     Vec3d vn = targetPosition - jparams.to;
  531.     Point3d focus(vn.x, vn.y, vn.z);
  532.     jparams.finalOrientation = lookAt(Point3d(0, 0, 0), focus, upd);
  533.     jparams.startInterpolation = min(startInter, endInter);
  534.     jparams.endInterpolation   = max(startInter, endInter);
  535.     
  536.     jparams.accelTime = 0.5;
  537.     double distance = astro::microLightYearsToKilometers(jparams.from.distanceTo(jparams.to)) / 2.0;
  538.     pair<double, double> sol = solve_bisection(TravelExpFunc(distance, jparams.accelTime),
  539.                                                0.0001, 100.0,
  540.                                                1e-10);
  541.     jparams.expFactor = sol.first;
  542.     
  543.     // Convert to frame coordinates
  544.     jparams.from = frame->convertFromUniversal(jparams.from, getTime());
  545.     jparams.initialOrientation = frame->convertFromUniversal(jparams.initialOrientation, getTime());
  546.     jparams.to = frame->convertFromUniversal(jparams.to, getTime());
  547.     jparams.finalOrientation = frame->convertFromUniversal(jparams.finalOrientation, getTime());        
  548. }
  549. void Observer::computeGotoParametersGC(const Selection& destination,
  550.                                        JourneyParams& jparams,
  551.                                        double gotoTime,
  552.                                        double startInter,
  553.                                        double endInter,
  554.                                        Vec3d offset,
  555.                                        ObserverFrame::CoordinateSystem offsetCoordSys,
  556.                                        Vec3f up,
  557.                                        ObserverFrame::CoordinateSystem upCoordSys,
  558.                                        const Selection& centerObj)
  559. {
  560.     setFrame(frame->getCoordinateSystem(), destination);
  561.     UniversalCoord targetPosition = destination.getPosition(getTime());
  562.     Vec3d v = targetPosition - getPosition();
  563.     v.normalize();
  564.     jparams.traj = GreatCircle;
  565.     jparams.duration = gotoTime;
  566.     jparams.startTime = realTime;
  567.     jparams.centerObject = centerObj;
  568.     // Right where we are now . . .
  569.     jparams.from = getPosition();
  570.     ObserverFrame offsetFrame(offsetCoordSys, destination);
  571.     offset = offset * offsetFrame.getFrame()->getOrientation(getTime()).toMatrix3();
  572.     
  573.     jparams.to = targetPosition + offset;
  574.     Vec3d upd(up.x, up.y, up.z);
  575.     if (upCoordSys == ObserverFrame::ObserverLocal)
  576.     {
  577.         upd = upd * orientationUniv.toMatrix3();
  578.     }
  579.     else
  580.     {
  581.         ObserverFrame upFrame(upCoordSys, destination);
  582.         upd = upd * upFrame.getFrame()->getOrientation(getTime()).toMatrix3();
  583.     }
  584.     
  585.     jparams.initialOrientation = getOrientation();
  586.     Vec3d vn = targetPosition - jparams.to;
  587.     Point3d focus(vn.x, vn.y, vn.z);
  588.     jparams.finalOrientation = lookAt(Point3d(0, 0, 0), focus, upd);
  589.     jparams.startInterpolation = min(startInter, endInter);
  590.     jparams.endInterpolation   = max(startInter, endInter);
  591.     jparams.accelTime = 0.5;
  592.     double distance = astro::microLightYearsToKilometers(jparams.from.distanceTo(jparams.to)) / 2.0;
  593.     pair<double, double> sol = solve_bisection(TravelExpFunc(distance, jparams.accelTime),
  594.                                                0.0001, 100.0,
  595.                                                1e-10);
  596.     jparams.expFactor = sol.first;
  597.     // Convert to frame coordinates
  598.     jparams.from = frame->convertFromUniversal(jparams.from, getTime());
  599.     jparams.initialOrientation = frame->convertFromUniversal(jparams.initialOrientation, getTime());
  600.     jparams.to = frame->convertFromUniversal(jparams.to, getTime());
  601.     jparams.finalOrientation = frame->convertFromUniversal(jparams.finalOrientation, getTime());    
  602. }
  603. void Observer::computeCenterParameters(const Selection& destination,
  604.                                        JourneyParams& jparams,
  605.                                        double centerTime)
  606. {
  607.     UniversalCoord targetPosition = destination.getPosition(getTime());
  608.     jparams.duration = centerTime;
  609.     jparams.startTime = realTime;
  610.     jparams.traj = Linear;
  611.     // Don't move through space, just rotate the camera
  612.     jparams.from = getPosition();
  613.     jparams.to = jparams.from;
  614.     Vec3d up = Vec3d(0, 1, 0) * getOrientation().toMatrix3();
  615.     jparams.initialOrientation = getOrientation();
  616.     Vec3d vn = targetPosition - jparams.to;
  617.     Point3d focus(vn.x, vn.y, vn.z);
  618.     jparams.finalOrientation = lookAt(Point3d(0, 0, 0), focus, up);
  619.     jparams.startInterpolation = 0;
  620.     jparams.endInterpolation   = 1;
  621.     jparams.accelTime = 0.5;
  622.     jparams.expFactor = 0;
  623.     // Convert to frame coordinates
  624.     jparams.from = frame->convertFromUniversal(jparams.from, getTime());
  625.     jparams.initialOrientation = frame->convertFromUniversal(jparams.initialOrientation, getTime());
  626.     jparams.to = frame->convertFromUniversal(jparams.to, getTime());
  627.     jparams.finalOrientation = frame->convertFromUniversal(jparams.finalOrientation, getTime());    
  628. }
  629. void Observer::computeCenterCOParameters(const Selection& destination,
  630.                                          JourneyParams& jparams,
  631.                                          double centerTime)
  632. {
  633.     jparams.duration = centerTime;
  634.     jparams.startTime = realTime;
  635.     jparams.traj = CircularOrbit;
  636.     jparams.centerObject = frame->getRefObject();
  637.     jparams.expFactor = 0.5;
  638.     Vec3d v = destination.getPosition(getTime()) - getPosition();
  639.     Vec3d w = Vec3d(0.0, 0.0, -1.0) * getOrientation().toMatrix3();
  640.     v.normalize();
  641.     Selection centerObj = frame->getRefObject();
  642.     UniversalCoord centerPos = centerObj.getPosition(getTime());
  643.     UniversalCoord targetPosition = destination.getPosition(getTime());
  644.     Quatd q = Quatd::vecToVecRotation(v, w);
  645.     jparams.from = getPosition();
  646.     jparams.to = centerPos + ((getPosition() - centerPos) * q.toMatrix3());
  647.     jparams.initialOrientation = getOrientation();
  648.     jparams.finalOrientation = getOrientation() * q;
  649.     jparams.startInterpolation = 0.0;
  650.     jparams.endInterpolation = 1.0;
  651.     jparams.rotation1 = q;
  652.     // Convert to frame coordinates
  653.     jparams.from = frame->convertFromUniversal(jparams.from, getTime());
  654.     jparams.initialOrientation = frame->convertFromUniversal(jparams.initialOrientation, getTime());
  655.     jparams.to = frame->convertFromUniversal(jparams.to, getTime());
  656.     jparams.finalOrientation = frame->convertFromUniversal(jparams.finalOrientation, getTime());
  657. }
  658. /*! Center the selection by moving on a circular orbit arround
  659. * the primary body (refObject).
  660. */
  661. void Observer::centerSelectionCO(const Selection& selection, double centerTime)
  662. {
  663.     if (!selection.empty() && !frame->getRefObject().empty())
  664.     {
  665.         computeCenterCOParameters(selection, journey, centerTime);
  666.         observerMode = Travelling;
  667.     }
  668. }
  669. Observer::ObserverMode Observer::getMode() const
  670. {
  671.     return observerMode;
  672. }
  673. void Observer::setMode(Observer::ObserverMode mode)
  674. {
  675.     observerMode = mode;
  676. }
  677. // Private method to convert coordinates when a new observer frame is set.
  678. // Universal coordinates remain the same. All frame coordinates get updated, including
  679. // the goto parameters.
  680. void Observer::convertFrameCoordinates(const ObserverFrame* newFrame)
  681. {
  682.     double now = getTime();
  683.     
  684.     // Universal coordinates don't change.
  685.     // Convert frame coordinates to the new frame.
  686.     position = newFrame->convertFromUniversal(positionUniv, now);
  687.     orientation = newFrame->convertFromUniversal(orientationUniv, now);
  688.     
  689.     // Convert goto parameters to the new frame
  690.     journey.from               = ObserverFrame::convert(frame, newFrame, journey.from, now);
  691.     journey.initialOrientation = ObserverFrame::convert(frame, newFrame, journey.initialOrientation, now);
  692.     journey.to                 = ObserverFrame::convert(frame, newFrame, journey.to, now);
  693.     journey.finalOrientation   = ObserverFrame::convert(frame, newFrame, journey.finalOrientation, now);
  694. }
  695. /*! Set the observer's reference frame. The position of the observer in
  696. *   universal coordinates will not change.
  697. */
  698. void Observer::setFrame(ObserverFrame::CoordinateSystem cs, const Selection& refObj, const Selection& targetObj)
  699. {
  700.     ObserverFrame* newFrame = new ObserverFrame(cs, refObj, targetObj);    
  701.     if (newFrame != NULL)
  702.     {
  703.         convertFrameCoordinates(newFrame);
  704.         delete frame;
  705.         frame = newFrame;
  706.     }
  707. }
  708. /*! Set the observer's reference frame. The position of the observer in
  709. *  universal coordinates will not change.
  710. */
  711. void Observer::setFrame(ObserverFrame::CoordinateSystem cs, const Selection& refObj)
  712. {
  713.     setFrame(cs, refObj, Selection());
  714. }
  715. /*! Set the observer's reference frame. The position of the observer in
  716.  *  universal coordinates will not change.
  717.  */
  718. void Observer::setFrame(const ObserverFrame& f)
  719. {
  720.     if (frame != &f)
  721.     {
  722.         ObserverFrame* newFrame = new ObserverFrame(f);
  723.         
  724.         if (newFrame != NULL)
  725.         {
  726. if (frame != NULL)
  727. {
  728. convertFrameCoordinates(newFrame);
  729. delete frame;
  730. }
  731.             frame = newFrame;
  732.         }
  733.     }    
  734. }
  735. /*! Get the current reference frame for the observer.
  736.  */
  737. const ObserverFrame* Observer::getFrame() const
  738. {
  739.     return frame;
  740. }
  741. /*! Rotate the observer about its center.
  742.  */
  743. void Observer::rotate(Quatf q)
  744. {
  745.     Quatd qd(q.w, q.x, q.y, q.z);
  746.     orientation = qd * orientation;
  747.     updateUniversal();
  748. }
  749. /*! Orbit around the reference object (if there is one.)  This involves changing
  750.  *  both the observer's position and orientation. If there is no current center
  751.  *  object, the specified selection will be used as the center of rotation, and
  752.  *  the observer reference frame will be modified.
  753.  */
  754. void Observer::orbit(const Selection& selection, Quatf q)
  755. {
  756.     Selection center = frame->getRefObject();
  757.     if (center.empty() && !selection.empty())
  758.     {
  759.         // Automatically set the center of the reference frame
  760.         center = selection;
  761.         setFrame(frame->getCoordinateSystem(), center);
  762.     }
  763.     if (!center.empty())
  764.     {
  765.         // Get the focus position (center of rotation) in frame
  766.         // coordinates; in order to make this function work in all
  767.         // frames of reference, it's important to work in frame
  768.         // coordinates.
  769.         UniversalCoord focusPosition = center.getPosition(getTime());
  770.         //focusPosition = frame.fromUniversal(RigidTransform(focusPosition), getTime()).translation;
  771.         focusPosition = frame->convertFromUniversal(focusPosition, getTime());
  772.         // v = the vector from the observer's position to the focus
  773.         //Vec3d v = situation.translation - focusPosition;
  774.         Vec3d v = position - focusPosition;
  775.         // Get a double precision version of the rotation
  776.         Quatd qd(q.w, q.x, q.y, q.z);
  777.         // To give the right feel for rotation, we want to premultiply
  778.         // the current orientation by q.  However, because of the order in
  779.         // which we apply transformations later on, we can't pre-multiply.
  780.         // To get around this, we compute a rotation q2 such
  781.         // that q1 * r = r * q2.
  782.         Quatd qd2 = ~orientation * qd * orientation;
  783.         qd2.normalize();
  784.         // Roundoff errors will accumulate and cause the distance between
  785.         // viewer and focus to drift unless we take steps to keep the
  786.         // length of v constant.
  787.         double distance = v.length();
  788.         v = v * qd2.toMatrix3();
  789.         v.normalize();
  790.         v *= distance;
  791.         //situation.rotation = situation.rotation * qd2;
  792.         //situation.translation = focusPosition + v;
  793.         orientation = orientation * qd2;
  794.         position = focusPosition + v;
  795.         updateUniversal();
  796.     }
  797. }
  798. /*! Exponential camera dolly--move toward or away from the selected object
  799.  * at a rate dependent on the observer's distance from the object.
  800.  */
  801. void Observer::changeOrbitDistance(const Selection& selection, float d)
  802. {
  803.     Selection center = frame->getRefObject();
  804.     if (center.empty() && !selection.empty())
  805.     {
  806.         center = selection;
  807.         setFrame(frame->getCoordinateSystem(), center);
  808.     }
  809.     if (!center.empty())
  810.     {
  811.         UniversalCoord focusPosition = center.getPosition(getTime());
  812.         double size = center.radius();
  813.         // Somewhat arbitrary parameters to chosen to give the camera movement
  814.         // a nice feel.  They should probably be function parameters.
  815.         double minOrbitDistance = astro::kilometersToMicroLightYears(size);
  816.         double naturalOrbitDistance = astro::kilometersToMicroLightYears(4.0 * size);
  817.         // Determine distance and direction to the selected object
  818.         Vec3d v = getPosition() - focusPosition;
  819.         double currentDistance = v.length();
  820.         if (currentDistance < minOrbitDistance)
  821.             minOrbitDistance = currentDistance * 0.5;
  822.         if (currentDistance >= minOrbitDistance && naturalOrbitDistance != 0)
  823.         {
  824.             double r = (currentDistance - minOrbitDistance) / naturalOrbitDistance;
  825.             double newDistance = minOrbitDistance + naturalOrbitDistance * exp(log(r) + d);
  826.             v = v * (newDistance / currentDistance);
  827.             position = frame->convertFromUniversal(focusPosition + v, getTime());
  828.             updateUniversal();
  829.         }
  830.     }
  831. }
  832. void Observer::setTargetSpeed(float s)
  833. {
  834.     targetSpeed = s;
  835.     Vec3d v;
  836.     if (reverseFlag)
  837.         s = -s;
  838.     if (trackObject.empty())
  839.     {
  840.         trackingOrientation = getOrientation();
  841.         // Generate vector for velocity using current orientation
  842.         // and specified speed.
  843.         v = Vec3d(0, 0, -s) * getOrientation().toMatrix4();
  844.     }
  845.     else
  846.     {
  847.         // Use tracking orientation vector to generate target velocity
  848.         v = Vec3d(0, 0, -s) * trackingOrientation.toMatrix4();
  849.     }
  850.     targetVelocity = v;
  851.     initialVelocity = getVelocity();
  852.     beginAccelTime = realTime;
  853. }
  854. float Observer::getTargetSpeed()
  855. {
  856.     return (float) targetSpeed;
  857. }
  858. void Observer::gotoJourney(const JourneyParams& params)
  859. {
  860.     journey = params;
  861.     double distance = astro::microLightYearsToKilometers(journey.from.distanceTo(journey.to)) / 2.0;
  862.     pair<double, double> sol = solve_bisection(TravelExpFunc(distance, journey.accelTime),
  863.                                                0.0001, 100.0,
  864.                                                1e-10);
  865.     journey.expFactor = sol.first;
  866.     journey.startTime = realTime;
  867.     observerMode = Travelling;
  868. }
  869. void Observer::gotoSelection(const Selection& selection,
  870.                              double gotoTime,
  871.                              Vec3f up,
  872.                              ObserverFrame::CoordinateSystem upFrame)
  873. {
  874.     gotoSelection(selection, gotoTime, 0.0, 0.5, up, upFrame);
  875. }
  876. // Return the preferred distance for viewing an object
  877. static double getPreferredDistance(const Selection& selection)
  878. {
  879.     switch (selection.getType())
  880.     {
  881.     case Selection::Type_Body:
  882.         // Handle reference points (i.e. invisible objects) specially, since the
  883.         // actual radius of the point is meaningless. Instead, use the size of
  884.         // bounding sphere of all child objects. This is useful for system
  885.         // barycenters--the normal goto command will place the observer at 
  886.         // a viewpoint in which the entire system can be seen.
  887.         if (selection.body()->getClassification() == Body::Invisible)
  888.         {
  889.             double r = selection.body()->getRadius();
  890.             if (selection.body()->getFrameTree() != NULL)
  891.                 r = selection.body()->getFrameTree()->boundingSphereRadius();
  892.             return min(astro::lightYearsToKilometers(0.1), r * 5.0);
  893.         }
  894.         else
  895.         {
  896.             return 5.0 * selection.radius();
  897.         }
  898.     case Selection::Type_DeepSky:
  899.         return 5.0 * selection.radius();
  900.     case Selection::Type_Star:
  901.         if (selection.star()->getVisibility())
  902.         {
  903.             return 100.0 * selection.radius();
  904.         }
  905.         else
  906.         {
  907.             // Handle star system barycenters specially, using the same approach as
  908.             // for reference points in solar systems.
  909.             double maxOrbitRadius = 0.0;
  910.             const vector<Star*>* orbitingStars = selection.star()->getOrbitingStars();
  911.             if (orbitingStars != NULL)
  912.             {
  913.                 for (vector<Star*>::const_iterator iter = orbitingStars->begin();
  914.                      iter != orbitingStars->end(); iter++)
  915.                 {
  916.                     Orbit* orbit = (*iter)->getOrbit();
  917.                     if (orbit != NULL)
  918.                         maxOrbitRadius = max(orbit->getBoundingRadius(), maxOrbitRadius);
  919.                 }
  920.             }
  921.             if (maxOrbitRadius == 0.0)
  922.                 return astro::AUtoKilometers(1.0);
  923.             else
  924.                 return maxOrbitRadius * 5.0;
  925.         }
  926.     case Selection::Type_Location:
  927.         {
  928.             double maxDist = getPreferredDistance(selection.location()->getParentBody());
  929.             return max(min(selection.location()->getSize() * 50.0, maxDist),
  930.                        1.0);
  931.         }
  932.     default:
  933.         return 1.0;
  934.     }
  935. }
  936. // Given an object and its current distance from the camera, determine how
  937. // close we should go on the next goto.
  938. static double getOrbitDistance(const Selection& selection,
  939.                                double currentDistance)
  940. {
  941.     // If further than 10 times the preferrred distance, goto the
  942.     // preferred distance.  If closer, zoom in 10 times closer or to the
  943.     // minimum distance.
  944.     double maxDist = astro::kilometersToMicroLightYears(getPreferredDistance(selection));
  945.     double minDist = astro::kilometersToMicroLightYears(1.01 * selection.radius());
  946.     double dist = (currentDistance > maxDist * 10.0) ? maxDist : currentDistance * 0.1;
  947.     return max(dist, minDist);
  948. }
  949. void Observer::gotoSelection(const Selection& selection,
  950.                              double gotoTime,
  951.                              double startInter,
  952.                              double endInter,
  953.                              Vec3f up,
  954.                              ObserverFrame::CoordinateSystem upFrame)
  955. {
  956.     if (!selection.empty())
  957.     {
  958.         UniversalCoord pos = selection.getPosition(getTime());
  959.         Vec3d v = pos - getPosition();
  960.         double distance = v.length();
  961.         double orbitDistance = getOrbitDistance(selection, distance);
  962.         computeGotoParameters(selection, journey, gotoTime,
  963.                               startInter, endInter,
  964.                               v * -(orbitDistance / distance),
  965.                               ObserverFrame::Universal,
  966.                               up, upFrame);
  967.         observerMode = Travelling;
  968.     }
  969. }
  970. /*! Like normal goto, except we'll follow a great circle trajectory.  Useful
  971.  *  for travelling between surface locations, where we'd rather not go straight
  972.  *  through the middle of a planet.
  973.  */
  974. void Observer::gotoSelectionGC(const Selection& selection,
  975.                                double gotoTime,
  976.                                double /*startInter*/,       //TODO: remove parameter??
  977.                                double /*endInter*/,         //TODO: remove parameter??
  978.                                Vec3f up,
  979.                                ObserverFrame::CoordinateSystem upFrame)
  980. {
  981.     if (!selection.empty())
  982.     {
  983.         Selection centerObj = selection.parent();
  984.         UniversalCoord pos = selection.getPosition(getTime());
  985.         Vec3d v = pos - centerObj.getPosition(getTime());
  986.         double distanceToCenter = v.length();
  987.         Vec3d viewVec = pos - getPosition();
  988.         double orbitDistance = getOrbitDistance(selection,
  989.                                                 viewVec.length());
  990.         if (selection.location() != NULL)
  991.         {
  992.             Selection parent = selection.parent();
  993.             double maintainDist = astro::kilometersToMicroLightYears(getPreferredDistance(parent));
  994.             Vec3d parentPos = parent.getPosition(getTime()) - getPosition();
  995.             double parentDist = parentPos.length() -
  996.                 astro::kilometersToMicroLightYears(parent.radius());
  997.             if (parentDist <= maintainDist && parentDist > orbitDistance)
  998.             {
  999.                 orbitDistance = parentDist;
  1000.             }
  1001.         }
  1002.         computeGotoParametersGC(selection, journey, gotoTime,
  1003.                                 //startInter, endInter,
  1004.                                 0.25, 0.75,
  1005.                                 v * (orbitDistance / distanceToCenter),
  1006.                                 ObserverFrame::Universal,
  1007.                                 up, upFrame,
  1008.                                 centerObj);
  1009.         observerMode = Travelling;
  1010.     }
  1011. }
  1012. void Observer::gotoSelection(const Selection& selection,
  1013.                              double gotoTime,
  1014.                              double distance,
  1015.                              Vec3f up,
  1016.                              ObserverFrame::CoordinateSystem upFrame)
  1017. {
  1018.     if (!selection.empty())
  1019.     {
  1020.         UniversalCoord pos = selection.getPosition(getTime());
  1021.         // The destination position lies along the line between the current
  1022.         // position and the star
  1023.         Vec3d v = pos - getPosition();
  1024.         v.normalize();
  1025.         computeGotoParameters(selection, journey, gotoTime, 0.25, 0.75,
  1026.                               v * -distance * 1e6, ObserverFrame::Universal,
  1027.                               up, upFrame);
  1028.         observerMode = Travelling;
  1029.     }
  1030. }
  1031. void Observer::gotoSelectionGC(const Selection& selection,
  1032.                                double gotoTime,
  1033.                                double distance,
  1034.                                Vec3f up,
  1035.                                ObserverFrame::CoordinateSystem upFrame)
  1036. {
  1037.     if (!selection.empty())
  1038.     {
  1039.         Selection centerObj = selection.parent();
  1040.         UniversalCoord pos = selection.getPosition(getTime());
  1041.         Vec3d v = pos - centerObj.getPosition(getTime());
  1042.         v.normalize();
  1043.         // The destination position lies along a line extended from the center
  1044.         // object to the target object
  1045.         computeGotoParametersGC(selection, journey, gotoTime, 0.25, 0.75,
  1046.                                 v * -distance * 1e6, ObserverFrame::Universal,
  1047.                                 up, upFrame,
  1048.                                 centerObj);
  1049.         observerMode = Travelling;
  1050.     }
  1051. }
  1052. void Observer::gotoSelectionLongLat(const Selection& selection,
  1053.                                     double gotoTime,
  1054.                                     double distance,
  1055.                                     float longitude,
  1056.                                     float latitude,
  1057.                                     Vec3f up)
  1058. {
  1059.     if (!selection.empty())
  1060.     {
  1061.         double phi = -latitude + PI / 2;
  1062.         double theta = longitude;
  1063.         double x = cos(theta) * sin(phi);
  1064.         double y = cos(phi);
  1065.         double z = -sin(theta) * sin(phi);
  1066.         computeGotoParameters(selection, journey, gotoTime, 0.25, 0.75,
  1067.                               Vec3d(x, y, z) * distance * 1e6, ObserverFrame::BodyFixed,
  1068.                               up, ObserverFrame::BodyFixed);
  1069.         observerMode = Travelling;
  1070.     }
  1071. }
  1072. void Observer::gotoLocation(const UniversalCoord& toPosition,
  1073.                             const Quatd& toOrientation,
  1074.                             double duration)
  1075. {
  1076.     journey.startTime = realTime;
  1077.     journey.duration = duration;
  1078.     journey.from = position;
  1079.     journey.initialOrientation = orientation;    
  1080.     journey.to = toPosition;
  1081.     journey.finalOrientation = toOrientation;
  1082.     
  1083.     journey.startInterpolation = 0.25f;
  1084.     journey.endInterpolation   = 0.75f;
  1085.     journey.accelTime = 0.5;
  1086.     double distance = astro::microLightYearsToKilometers(journey.from.distanceTo(journey.to)) / 2.0;
  1087.     pair<double, double> sol = solve_bisection(TravelExpFunc(distance, journey.accelTime),
  1088.                                                0.0001, 100.0,
  1089.                                                1e-10);
  1090.     journey.expFactor = sol.first;
  1091.     observerMode = Travelling;
  1092. }
  1093. void Observer::getSelectionLongLat(const Selection& selection,
  1094.                                    double& distance,
  1095.                                    double& longitude,
  1096.                                    double& latitude)
  1097. {
  1098.     // Compute distance (km) and lat/long (degrees) of observer with
  1099.     // respect to currently selected object.
  1100.     if (!selection.empty())
  1101.     {
  1102.         ObserverFrame frame(ObserverFrame::BodyFixed, selection);
  1103.         Point3d bfPos = (Point3d) frame.convertFromUniversal(positionUniv, getTime());
  1104.         
  1105.         distance = bfPos.distanceFromOrigin();
  1106.         // Convert from Celestia's coordinate system
  1107.         double x = bfPos.x;
  1108.         double y = -bfPos.z;
  1109.         double z = bfPos.y;
  1110.         longitude = radToDeg(atan2(y, x));
  1111.         latitude = radToDeg(PI/2 - acos(z / distance));
  1112.         
  1113.         // Convert distance from light years to kilometers.
  1114.         distance = astro::microLightYearsToKilometers(distance);
  1115.     }
  1116. }
  1117. void Observer::gotoSurface(const Selection& sel, double duration)
  1118. {
  1119.     Vec3d v = getPosition() - sel.getPosition(getTime());
  1120.     v.normalize();
  1121.     
  1122.     Vec3d viewDir = Vec3d(0, 0, -1) * orientationUniv.toMatrix3();
  1123.     Vec3d up = Vec3d(0, 1, 0) * orientationUniv.toMatrix3();
  1124.     Quatd q = orientationUniv;
  1125.     if (v * viewDir < 0.0)
  1126.     {
  1127.         q = lookAt(Point3d(0.0, 0.0, 0.0), Point3d(0.0, 0.0, 0.0) + up, v);
  1128.     }
  1129.     
  1130.     ObserverFrame frame(ObserverFrame::BodyFixed, sel);
  1131.     UniversalCoord bfPos = frame.convertFromUniversal(positionUniv, getTime());
  1132.     q = frame.convertFromUniversal(q, getTime());
  1133.     
  1134.     double height = 1.0001 * astro::kilometersToMicroLightYears(sel.radius());
  1135.     Vec3d dir = bfPos - Point3d(0.0, 0.0, 0.0);
  1136.     dir.normalize();
  1137.     dir *= height;
  1138.     UniversalCoord nearSurfacePoint(dir.x, dir.y, dir.z);
  1139.     
  1140.     gotoLocation(nearSurfacePoint, q, duration);    
  1141. };
  1142. void Observer::cancelMotion()
  1143. {
  1144.     observerMode = Free;
  1145. }
  1146. void Observer::centerSelection(const Selection& selection, double centerTime)
  1147. {
  1148.     if (!selection.empty())
  1149.     {
  1150.         computeCenterParameters(selection, journey, centerTime);
  1151.         observerMode = Travelling;
  1152.     }
  1153. }
  1154. void Observer::follow(const Selection& selection)
  1155. {
  1156.     setFrame(ObserverFrame::Ecliptical, selection);
  1157. }
  1158. void Observer::geosynchronousFollow(const Selection& selection)
  1159. {
  1160.     if (selection.body() != NULL ||
  1161.         selection.location() != NULL ||
  1162.         selection.star() != NULL)
  1163.     {
  1164.         setFrame(ObserverFrame::BodyFixed, selection);
  1165.     }
  1166. }
  1167. void Observer::phaseLock(const Selection& selection)
  1168. {
  1169.     Selection refObject = frame->getRefObject();
  1170.     
  1171.     if (selection != refObject)
  1172.     {
  1173.         if (refObject.body() != NULL || refObject.star() != NULL)
  1174.         {
  1175.             setFrame(ObserverFrame::PhaseLock, refObject, selection);
  1176.         }
  1177.     }
  1178.     else
  1179.     {
  1180.         // Selection and reference object are identical, so the frame is undefined.
  1181.         // We'll instead use the object's star as the target object.
  1182.         if (selection.body() != NULL)
  1183.         {
  1184.             setFrame(ObserverFrame::PhaseLock, selection, Selection(selection.body()->getSystem()->getStar()));
  1185.         }        
  1186.     }
  1187. }
  1188. void Observer::chase(const Selection& selection)
  1189. {
  1190.     if (selection.body() != NULL || selection.star() != NULL)
  1191.     {
  1192.         setFrame(ObserverFrame::Chase, selection);
  1193.     }
  1194. }
  1195. float Observer::getFOV() const
  1196. {
  1197.     return fov;
  1198. }
  1199. void Observer::setFOV(float _fov)
  1200. {
  1201.     fov = _fov;
  1202. }
  1203. Vec3f Observer::getPickRay(float x, float y) const
  1204. {
  1205.     float s = 2 * (float) tan(fov / 2.0);
  1206.     Vec3f pickDirection = Vec3f(x * s, y * s, -1.0f);
  1207.     pickDirection.normalize();
  1208.     return pickDirection;
  1209. }
  1210. // Internal method to update the position and orientation of the observer in
  1211. // universal coordinates.
  1212. void Observer::updateUniversal()
  1213. {
  1214.     positionUniv = frame->convertToUniversal(position, simTime);
  1215.     orientationUniv = frame->convertToUniversal(orientation, simTime);
  1216. }
  1217. /*! Create the default 'universal' observer frame, with a center at the
  1218.  *  Solar System barycenter and coordinate axes of the J200Ecliptic
  1219.  *  reference frame.
  1220.  */
  1221. ObserverFrame::ObserverFrame() :
  1222.     coordSys(Universal),
  1223.     frame(NULL)
  1224. {
  1225.     frame = createFrame(Universal, Selection(), Selection());
  1226.     frame->addRef();
  1227. }
  1228. /*! Create a new frame with the specified coordinate system and
  1229.  *  reference object. The targetObject is only needed for phase
  1230.  *  lock frames; the argument is ignored for other frames.
  1231.  */
  1232. ObserverFrame::ObserverFrame(CoordinateSystem _coordSys,
  1233.                              const Selection& _refObject,
  1234.                              const Selection& _targetObject) :
  1235.     coordSys(_coordSys),
  1236.     frame(NULL),
  1237.     targetObject(_targetObject)
  1238. {
  1239.     frame = createFrame(_coordSys, _refObject, _targetObject);
  1240.     frame->addRef();
  1241. }
  1242. /*! Create a new ObserverFrame with the specified reference frame.
  1243.  *  The coordinate system of this frame will be marked as unknown.
  1244.  */
  1245. ObserverFrame::ObserverFrame(const ReferenceFrame &f) :
  1246.     coordSys(Unknown),
  1247.     frame(&f)
  1248. {
  1249.     frame->addRef();
  1250. }
  1251. /*! Copy constructor. */
  1252. ObserverFrame::ObserverFrame(const ObserverFrame& f) :
  1253.     coordSys(f.coordSys),
  1254.     frame(f.frame),
  1255.     targetObject(f.targetObject)
  1256. {
  1257.     frame->addRef();
  1258. }
  1259. ObserverFrame& ObserverFrame::operator=(const ObserverFrame& f)
  1260. {
  1261.     coordSys = f.coordSys;
  1262.     targetObject = f.targetObject;
  1263.     
  1264.     // In case frames are the same, make sure we addref before releasing
  1265.     f.frame->addRef();
  1266.     frame->release();
  1267.     frame = f.frame;
  1268.     
  1269.     return *this;
  1270. }
  1271. ObserverFrame::~ObserverFrame()
  1272. {
  1273.     if (frame != NULL)
  1274.         frame->release();
  1275. }
  1276. ObserverFrame::CoordinateSystem
  1277. ObserverFrame::getCoordinateSystem() const
  1278. {
  1279.     return coordSys;
  1280. }
  1281. Selection
  1282. ObserverFrame::getRefObject() const
  1283. {
  1284.     return frame->getCenter();
  1285. }
  1286. Selection
  1287. ObserverFrame::getTargetObject() const
  1288. {
  1289.     return targetObject;
  1290. }
  1291. const ReferenceFrame*
  1292. ObserverFrame::getFrame() const
  1293. {
  1294.     return frame;
  1295. }
  1296. UniversalCoord 
  1297. ObserverFrame::convertFromUniversal(const UniversalCoord& uc, double tjd) const
  1298. {
  1299.     return frame->convertFromUniversal(uc, tjd);
  1300. }
  1301. UniversalCoord
  1302. ObserverFrame::convertToUniversal(const UniversalCoord& uc, double tjd) const
  1303. {
  1304.     return frame->convertToUniversal(uc, tjd);
  1305. }
  1306. Quatd 
  1307. ObserverFrame::convertFromUniversal(const Quatd& q, double tjd) const
  1308. {
  1309.     return frame->convertFromUniversal(q, tjd);
  1310. }
  1311. Quatd
  1312. ObserverFrame::convertToUniversal(const Quatd& q, double tjd) const
  1313. {
  1314.     return frame->convertToUniversal(q, tjd);
  1315. }
  1316. /*! Convert a position from one frame to another.
  1317.  */
  1318. UniversalCoord
  1319. ObserverFrame::convert(const ObserverFrame* fromFrame,
  1320.                        const ObserverFrame* toFrame,
  1321.                        const UniversalCoord& uc,
  1322.                        double t)
  1323. {
  1324.     // Perform the conversion fromFrame -> universal -> toFrame
  1325.     return toFrame->convertFromUniversal(fromFrame->convertToUniversal(uc, t), t);
  1326. }
  1327. /*! Convert an orientation from one frame to another.
  1328. */
  1329. Quatd
  1330. ObserverFrame::convert(const ObserverFrame* fromFrame,
  1331.                        const ObserverFrame* toFrame,
  1332.                        const Quatd& q,
  1333.                        double t)
  1334. {
  1335.     // Perform the conversion fromFrame -> universal -> toFrame
  1336.     return toFrame->convertFromUniversal(fromFrame->convertToUniversal(q, t), t);
  1337. }
  1338. // Create the ReferenceFrame for the specified observer frame parameters.
  1339. ReferenceFrame*
  1340. ObserverFrame::createFrame(CoordinateSystem _coordSys,
  1341.                            const Selection& _refObject,
  1342.                            const Selection& _targetObject)
  1343. {
  1344.     switch (_coordSys)
  1345.     {
  1346.     case Universal:
  1347.         return new J2000EclipticFrame(Selection());
  1348.         
  1349.     case Ecliptical:
  1350.         return new J2000EclipticFrame(_refObject);
  1351.         
  1352.     case Equatorial:
  1353.         return new BodyMeanEquatorFrame(_refObject, _refObject);
  1354.         
  1355.     case BodyFixed:
  1356.         return new BodyFixedFrame(_refObject, _refObject);
  1357.         
  1358.     case PhaseLock:
  1359.     {
  1360.         return new TwoVectorFrame(_refObject,
  1361.                                   FrameVector::createRelativePositionVector(_refObject, _targetObject), 1,
  1362.                                   FrameVector::createRelativeVelocityVector(_refObject, _targetObject), 2);
  1363.     }
  1364.         
  1365.     case Chase:
  1366.     {
  1367.         return new TwoVectorFrame(_refObject,
  1368.                                   FrameVector::createRelativeVelocityVector(_refObject, _refObject.parent()), 1,
  1369.                                   FrameVector::createRelativePositionVector(_refObject, _refObject.parent()), 2);
  1370.     }
  1371.         
  1372.     case PhaseLock_Old:
  1373.     {
  1374.         FrameVector rotAxis(FrameVector::createConstantVector(Vec3d(0, 1, 0),
  1375.                                                               new BodyMeanEquatorFrame(_refObject, _refObject)));
  1376.         return new TwoVectorFrame(_refObject,
  1377.                                   FrameVector::createRelativePositionVector(_refObject, _targetObject), 3,
  1378.                                   rotAxis, 2);
  1379.     }
  1380.         
  1381.     case Chase_Old:
  1382.     {
  1383.         FrameVector rotAxis(FrameVector::createConstantVector(Vec3d(0, 1, 0),
  1384.                                                               new BodyMeanEquatorFrame(_refObject, _refObject)));
  1385.         
  1386.         return new TwoVectorFrame(_refObject,
  1387.                                   FrameVector::createRelativeVelocityVector(_refObject.parent(), _refObject), 3,
  1388.                                   rotAxis, 2);
  1389.     }
  1390.     case ObserverLocal:
  1391.         // TODO: This is only used for computing up vectors for orientation; it does
  1392.         // define a proper frame for the observer position orientation.
  1393.         return new J2000EclipticFrame(Selection());
  1394.         
  1395.     default:
  1396.         return new J2000EclipticFrame(_refObject);
  1397.     }
  1398. }