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