]> git.mxchange.org Git - flightgear.git/blob - src/Main/viewmgr.cxx
Make spoilers and speed-brake position accessible
[flightgear.git] / src / Main / viewmgr.cxx
1 // viewmgr.cxx -- class for managing all the views in the flightgear world.
2 //
3 // Written by Curtis Olson, started October 2000.
4 //   partially rewritten by Jim Wilson March 2002
5 //
6 // Copyright (C) 2000  Curtis L. Olson  - curt@flightgear.org
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 //
22 // $Id$
23
24 #include <string.h>             // strcmp
25
26 #include <plib/sg.h>
27
28 #include "viewmgr.hxx"
29 #include "fg_props.hxx"
30
31
32 // Constructor
33 FGViewMgr::FGViewMgr( void ) :
34   axis_long(0),
35   axis_lat(0),
36   current(0)
37 {
38 }
39
40
41 // Destructor
42 FGViewMgr::~FGViewMgr( void ) {
43 }
44
45 void
46 FGViewMgr::init ()
47 {
48   char stridx [ 20 ];
49   string viewpath, nodepath, strdata;
50   bool from_model = false;
51   bool at_model = false;
52   int from_model_index = 0;
53   int at_model_index = 0;
54   double x_offset_m, y_offset_m, z_offset_m, fov_deg;
55   double heading_offset_deg, pitch_offset_deg, roll_offset_deg;
56   double target_x_offset_m, target_y_offset_m, target_z_offset_m;
57   double near_m;
58
59   for (int i = 0; i < fgGetInt("/sim/number-views"); i++) {
60     viewpath = "/sim/view";
61     sprintf(stridx, "[%d]", i);
62     viewpath += stridx;
63
64     // find out what type of view this is...
65     nodepath = viewpath;
66     nodepath += "/type";
67     strdata = fgGetString(nodepath.c_str());
68
69     // FIXME:
70     // this is assumed to be an aircraft model...we will need to read
71     // model-from-type as well.
72     // find out if this is a model we are looking from...
73     nodepath = viewpath;
74     nodepath += "/config/from-model";
75     from_model = fgGetBool(nodepath.c_str());
76
77     // get model index (which model)
78     if (from_model) {
79       nodepath = viewpath;
80       nodepath += "/config/from-model-idx";
81       from_model_index = fgGetInt(nodepath.c_str());     
82     }
83
84     if ( strcmp("lookat",strdata.c_str()) == 0 ) {
85       // find out if this is a model we are looking at...
86       nodepath = viewpath;
87       nodepath += "/config/at-model";
88       at_model = fgGetBool(nodepath.c_str());
89
90       // get model index (which model)
91       if (at_model) {
92         nodepath = viewpath;
93         nodepath += "/config/at-model-idx";
94         at_model_index = fgGetInt(nodepath.c_str());     
95       }
96     }
97
98     nodepath = viewpath;
99     nodepath += "/config/x-offset-m";
100     x_offset_m = fgGetDouble(nodepath.c_str());
101     nodepath = viewpath;
102     nodepath += "/config/y-offset-m";
103     y_offset_m = fgGetDouble(nodepath.c_str());
104     nodepath = viewpath;
105     nodepath += "/config/z-offset-m";
106     z_offset_m = fgGetDouble(nodepath.c_str());
107     nodepath = viewpath;
108     nodepath += "/config/pitch-offset-deg";
109     pitch_offset_deg = fgGetDouble(nodepath.c_str());
110     fgSetDouble(nodepath.c_str(),pitch_offset_deg);
111     nodepath = viewpath;
112     nodepath += "/config/heading-offset-deg";
113     heading_offset_deg = fgGetDouble(nodepath.c_str());
114     fgSetDouble(nodepath.c_str(),heading_offset_deg);
115     nodepath = viewpath;
116     nodepath += "/config/roll-offset-deg";
117     roll_offset_deg = fgGetDouble(nodepath.c_str());
118     fgSetDouble(nodepath.c_str(),roll_offset_deg);
119     nodepath = viewpath;
120     nodepath += "/config/default-field-of-view-deg";
121     fov_deg = fgGetDouble(nodepath.c_str());
122
123     // target offsets for lookat mode only...
124     nodepath = viewpath;
125     nodepath += "/config/target-x-offset-m";
126     target_x_offset_m = fgGetDouble(nodepath.c_str());
127     nodepath = viewpath;
128     nodepath += "/config/target-y-offset-m";
129     target_y_offset_m = fgGetDouble(nodepath.c_str());
130     nodepath = viewpath;
131     nodepath += "/config/target-z-offset-m";
132     target_z_offset_m = fgGetDouble(nodepath.c_str());
133
134     nodepath = viewpath;
135     nodepath += "/config/ground-level-nearplane-m";
136     near_m = fgGetDouble(nodepath.c_str());
137
138     // supporting two types now "lookat" = 1 and "lookfrom" = 0
139     if ( strcmp("lookat",strdata.c_str()) == 0 )
140       add_view(new FGViewer ( FG_LOOKAT, from_model, from_model_index,
141                               at_model, at_model_index, x_offset_m,
142                               y_offset_m,z_offset_m,
143                               heading_offset_deg, pitch_offset_deg,
144                               roll_offset_deg, fov_deg,
145                               target_x_offset_m, target_y_offset_m,
146                               target_z_offset_m, near_m ));
147     else
148       add_view(new FGViewer ( FG_LOOKFROM, from_model, from_model_index, false,
149                               0, x_offset_m, y_offset_m, z_offset_m,
150                               heading_offset_deg, pitch_offset_deg,
151                               roll_offset_deg, fov_deg, 0, 0, 0, near_m ));
152   }
153
154   copyToCurrent();
155   
156 }
157
158 void
159 FGViewMgr::reinit ()
160 {
161   char stridx [ 20 ];
162   string viewpath, nodepath, strdata;
163   double fov_deg;
164
165   // reset offsets and fov to configuration defaults
166
167   for (int i = 0; i < fgGetInt("/sim/number-views"); i++) {
168     viewpath = "/sim/view";
169     sprintf(stridx, "[%d]", i);
170     viewpath += stridx;
171
172     setView(i);
173
174     nodepath = viewpath;
175     nodepath += "/config/x-offset-m";
176     fgSetDouble("/sim/current-view/x-offset-m",fgGetDouble(nodepath.c_str()));
177
178     nodepath = viewpath;
179     nodepath += "/config/y-offset-m";
180     fgSetDouble("/sim/current-view/y-offset-m",fgGetDouble(nodepath.c_str()));
181
182     nodepath = viewpath;
183     nodepath += "/config/z-offset-m";
184     fgSetDouble("/sim/current-view/z-offset-m",fgGetDouble(nodepath.c_str()));
185
186     nodepath = viewpath;
187     nodepath += "/config/pitch-offset-deg";
188     fgSetDouble("/sim/current-view/pitch-offset-deg",
189                 fgGetDouble(nodepath.c_str()));
190
191     nodepath = viewpath;
192     nodepath += "/config/heading-offset-deg";
193     fgSetDouble("/sim/current-view/heading-offset-deg",
194                 fgGetDouble(nodepath.c_str()));
195
196     nodepath = viewpath;
197     nodepath += "/config/roll-offset-deg";
198     fgSetDouble("/sim/current-view/roll-offset-deg",
199                 fgGetDouble(nodepath.c_str()));
200
201     nodepath = viewpath;
202     nodepath += "/config/default-field-of-view-deg";
203     fov_deg = fgGetDouble(nodepath.c_str());
204     if (fov_deg < 10.0) {
205       fov_deg = 55.0;
206     }
207     fgSetDouble("/sim/current-view/field-of-view",fov_deg);
208
209     // target offsets for lookat mode only...
210     nodepath = viewpath;
211     nodepath += "/config/target-x-offset-m";
212     fgSetDouble("/sim/current-view/target-x-offset-deg",
213                 fgGetDouble(nodepath.c_str()));
214
215     nodepath = viewpath;
216     nodepath += "/config/target-y-offset-m";
217     fgSetDouble("/sim/current-view/target-y-offset-deg",
218                 fgGetDouble(nodepath.c_str()));
219
220     nodepath = viewpath;
221     nodepath += "/config/target-z-offset-m";
222     fgSetDouble("/sim/current-view/target-z-offset-deg",
223                 fgGetDouble(nodepath.c_str()));
224
225  }
226
227     setView(0);
228
229 }
230
231 typedef double (FGViewMgr::*double_getter)() const;
232
233 void
234 FGViewMgr::bind ()
235 {
236   // these are bound to the current view properties
237   fgTie("/sim/current-view/heading-offset-deg", this,
238         &FGViewMgr::getViewHeadingOffset_deg,
239         &FGViewMgr::setViewHeadingOffset_deg);
240   fgSetArchivable("/sim/current-view/heading-offset-deg");
241   fgTie("/sim/current-view/goal-heading-offset-deg", this,
242         &FGViewMgr::getViewGoalHeadingOffset_deg,
243         &FGViewMgr::setViewGoalHeadingOffset_deg);
244   fgSetArchivable("/sim/current-view/goal-heading-offset-deg");
245   fgTie("/sim/current-view/pitch-offset-deg", this,
246         &FGViewMgr::getViewPitchOffset_deg,
247         &FGViewMgr::setViewPitchOffset_deg);
248   fgSetArchivable("/sim/current-view/pitch-offset-deg");
249   fgTie("/sim/current-view/goal-pitch-offset-deg", this,
250         &FGViewMgr::getGoalViewPitchOffset_deg,
251         &FGViewMgr::setGoalViewPitchOffset_deg);
252   fgSetArchivable("/sim/current-view/goal-pitch-offset-deg");
253
254   fgTie("/sim/current-view/view-number", this, 
255                       &FGViewMgr::getView, &FGViewMgr::setView);
256   fgSetArchivable("/sim/current-view/view-number", FALSE);
257
258   fgTie("/sim/current-view/axes/long", this,
259         (double_getter)0, &FGViewMgr::setViewAxisLong);
260   fgSetArchivable("/sim/current-view/axes/long");
261
262   fgTie("/sim/current-view/axes/lat", this,
263         (double_getter)0, &FGViewMgr::setViewAxisLat);
264   fgSetArchivable("/sim/current-view/axes/lat");
265
266   fgTie("/sim/current-view/field-of-view", this,
267         &FGViewMgr::getFOV_deg, &FGViewMgr::setFOV_deg);
268   fgSetArchivable("/sim/current-view/field-of-view");
269
270   fgTie("/sim/current-view/ground-level-nearplane-m", this,
271         &FGViewMgr::getNear_m, &FGViewMgr::setNear_m);
272   fgSetArchivable("/sim/current-view/ground-level-nearplane-m");
273
274 }
275
276 void
277 FGViewMgr::unbind ()
278 {
279   // FIXME:
280   // need to redo these bindings to the new locations (move to viewer?)
281   fgUntie("/sim/current-view/heading-offset-deg");
282   fgUntie("/sim/current-view/goal-heading-offset-deg");
283   fgUntie("/sim/current-view/pitch-offset-deg");
284   fgUntie("/sim/current-view/goal-pitch-offset-deg");
285   fgUntie("/sim/field-of-view");
286   fgUntie("/sim/current-view/view-number");
287   fgUntie("/sim/current-view/axes/long");
288   fgUntie("/sim/current-view/axes/lat");
289   fgUntie("/sim/current-view/ground-level-nearplane-m");
290 }
291
292 void
293 FGViewMgr::update (double dt)
294 {
295   char stridx [20];
296   string viewpath, nodepath;
297   double lon_deg, lat_deg, alt_ft, roll_deg, pitch_deg, heading_deg;
298
299   FGViewer * view = get_current_view();
300   if (view == 0)
301     return;
302
303   // 
304   int i = current;
305   viewpath = "/sim/view";
306   sprintf(stridx, "[%d]", i);
307   viewpath += stridx;
308
309   FGViewer *loop_view = (FGViewer *)get_view( i );
310
311                 // Set up view location and orientation
312
313   nodepath = viewpath;
314   nodepath += "/config/from-model";
315   if (!fgGetBool(nodepath.c_str())) {
316     nodepath = viewpath;
317     nodepath += "/config/eye-lon-deg-path";
318     lon_deg = fgGetDouble(fgGetString(nodepath.c_str()));
319     nodepath = viewpath;
320     nodepath += "/config/eye-lat-deg-path";
321     lat_deg = fgGetDouble(fgGetString(nodepath.c_str()));
322     nodepath = viewpath;
323     nodepath += "/config/eye-alt-ft-path";
324     alt_ft = fgGetDouble(fgGetString(nodepath.c_str()));
325     nodepath = viewpath;
326     nodepath += "/config/eye-roll-deg-path";
327     roll_deg = fgGetDouble(fgGetString(nodepath.c_str()));
328     nodepath = viewpath;
329     nodepath += "/config/eye-pitch-deg-path";
330     pitch_deg = fgGetDouble(fgGetString(nodepath.c_str()));
331     nodepath = viewpath;
332     nodepath += "/config/eye-heading-deg-path";
333     heading_deg = fgGetDouble(fgGetString(nodepath.c_str()));
334     loop_view->setPosition(lon_deg, lat_deg, alt_ft);
335     loop_view->setOrientation(roll_deg, pitch_deg, heading_deg);
336   } else {
337     // force recalc in viewer
338     loop_view->set_dirty();
339   }
340
341   // if lookat (type 1) then get target data...
342   if (loop_view->getType() == FG_LOOKAT) {
343     nodepath = viewpath;
344     nodepath += "/config/from-model";
345     if (!fgGetBool(nodepath.c_str())) {
346       nodepath = viewpath;
347       nodepath += "/config/target-lon-deg-path";
348       lon_deg = fgGetDouble(fgGetString(nodepath.c_str()));
349       nodepath = viewpath;
350       nodepath += "/config/target-lat-deg-path";
351       lat_deg = fgGetDouble(fgGetString(nodepath.c_str()));
352       nodepath = viewpath;
353       nodepath += "/config/target-alt-ft-path";
354       alt_ft = fgGetDouble(fgGetString(nodepath.c_str()));
355       nodepath = viewpath;
356       nodepath += "/config/target-roll-deg-path";
357       roll_deg = fgGetDouble(fgGetString(nodepath.c_str()));
358       nodepath = viewpath;
359       nodepath += "/config/target-pitch-deg-path";
360       pitch_deg = fgGetDouble(fgGetString(nodepath.c_str()));
361       nodepath = viewpath;
362       nodepath += "/config/target-heading-deg-path";
363       heading_deg = fgGetDouble(fgGetString(nodepath.c_str()));
364   
365       loop_view ->setTargetPosition(lon_deg, lat_deg, alt_ft);
366       loop_view->setTargetOrientation(roll_deg, pitch_deg, heading_deg);
367     } else {
368       loop_view->set_dirty();
369     }
370   }
371
372   setViewXOffset_m(fgGetDouble("/sim/current-view/x-offset-m"));
373   setViewYOffset_m(fgGetDouble("/sim/current-view/y-offset-m"));
374   setViewZOffset_m(fgGetDouble("/sim/current-view/z-offset-m"));
375
376   setViewTargetXOffset_m(fgGetDouble("/sim/current-view/target-x-offset-m"));
377   setViewTargetYOffset_m(fgGetDouble("/sim/current-view/target-y-offset-m"));
378   setViewTargetZOffset_m(fgGetDouble("/sim/current-view/target-z-offset-m"));
379
380                                 // Update the current view
381   do_axes();
382   view->update(dt);
383 }
384
385 void
386 FGViewMgr::copyToCurrent()
387 {
388     char stridx [20];
389     string viewpath, nodepath;
390
391     int i = current;
392     viewpath = "/sim/view";
393     sprintf(stridx, "[%d]", i);
394     viewpath += stridx;
395
396     // copy certain view config data for default values
397     nodepath = viewpath;
398     nodepath += "/config/default-heading-offset-deg";
399     fgSetDouble("/sim/current-view/config/heading-offset-deg",
400                 fgGetDouble(nodepath.c_str()));
401
402     nodepath = viewpath;
403     nodepath += "/config/pitch-offset-deg";
404     fgSetDouble("/sim/current-view/config/pitch-offset-deg",
405                 fgGetDouble(nodepath.c_str()));
406
407     nodepath = viewpath;
408     nodepath += "/config/roll-offset-deg";
409     fgSetDouble("/sim/current-view/config/roll-offset-deg",
410                 fgGetDouble(nodepath.c_str()));
411
412     nodepath = viewpath;
413     nodepath += "/config/default-field-of-view-deg";
414     fgSetDouble("/sim/current-view/config/default-field-of-view-deg",
415                 fgGetDouble(nodepath.c_str()));
416
417     // copy view data
418     fgSetDouble("/sim/current-view/x-offset-m", getViewXOffset_m());
419     fgSetDouble("/sim/current-view/y-offset-m", getViewYOffset_m());
420     fgSetDouble("/sim/current-view/z-offset-m", getViewZOffset_m());
421     fgSetDouble("/sim/current-view/goal-heading-offset-deg",
422                 get_current_view()->getGoalHeadingOffset_deg());
423     fgSetDouble("/sim/current-view/goal-pitch-offset-deg",
424                 get_current_view()->getGoalPitchOffset_deg());
425     fgSetDouble("/sim/current-view/goal-roll-offset-deg",
426                 get_current_view()->getRollOffset_deg());
427     fgSetDouble("/sim/current-view/heading-offset-deg",
428                 get_current_view()->getHeadingOffset_deg());
429     fgSetDouble("/sim/current-view/pitch-offset-deg",
430                 get_current_view()->getPitchOffset_deg());
431     fgSetDouble("/sim/current-view/roll-offset-deg",
432                 get_current_view()->getRollOffset_deg());
433     fgSetDouble("/sim/current-view/target-x-offset-m",
434                 get_current_view()->getTargetXOffset_m());
435     fgSetDouble("/sim/current-view/target-y-offset-m",
436                 get_current_view()->getTargetYOffset_m());
437     fgSetDouble("/sim/current-view/target-z-offset-m",
438                 get_current_view()->getTargetZOffset_m());
439 }
440
441
442 double
443 FGViewMgr::getViewHeadingOffset_deg () const
444 {
445   const FGViewer * view = get_current_view();
446   return (view == 0 ? 0 : view->getHeadingOffset_deg());
447 }
448
449 void
450 FGViewMgr::setViewHeadingOffset_deg (double offset)
451 {
452   FGViewer * view = get_current_view();
453   if (view != 0) {
454     view->setGoalHeadingOffset_deg(offset);
455     view->setHeadingOffset_deg(offset);
456   }
457 }
458
459 double
460 FGViewMgr::getViewGoalHeadingOffset_deg () const
461 {
462   const FGViewer * view = get_current_view();
463   return (view == 0 ? 0 : view->getGoalHeadingOffset_deg());
464 }
465
466 void
467 FGViewMgr::setViewGoalHeadingOffset_deg (double offset)
468 {
469   FGViewer * view = get_current_view();
470   if (view != 0)
471     view->setGoalHeadingOffset_deg(offset);
472 }
473
474 double
475 FGViewMgr::getViewPitchOffset_deg () const
476 {
477   const FGViewer * view = get_current_view();
478   return (view == 0 ? 0 : view->getPitchOffset_deg());
479 }
480
481 void
482 FGViewMgr::setViewPitchOffset_deg (double tilt)
483 {
484   FGViewer * view = get_current_view();
485   if (view != 0) {
486     view->setGoalPitchOffset_deg(tilt);
487     view->setPitchOffset_deg(tilt);
488   }
489 }
490
491 double
492 FGViewMgr::getGoalViewPitchOffset_deg () const
493 {
494   const FGViewer * view = get_current_view();
495   return (view == 0 ? 0 : view->getGoalPitchOffset_deg());
496 }
497
498 void
499 FGViewMgr::setGoalViewPitchOffset_deg (double tilt)
500 {
501   FGViewer * view = get_current_view();
502   if (view != 0)
503     view->setGoalPitchOffset_deg(tilt);
504 }
505
506 double
507 FGViewMgr::getViewXOffset_m () const
508 {
509   const FGViewer * view = get_current_view();
510   if (view != 0) {
511     return ((FGViewer *)view)->getXOffset_m();
512   } else {
513     return 0;
514   }
515 }
516
517 void
518 FGViewMgr::setViewXOffset_m (double x)
519 {
520   FGViewer * view = get_current_view();
521   if (view != 0) {
522     view->setXOffset_m(x);
523   }
524 }
525
526 double
527 FGViewMgr::getViewYOffset_m () const
528 {
529   const FGViewer * view = get_current_view();
530   if (view != 0) {
531     return ((FGViewer *)view)->getYOffset_m();
532   } else {
533     return 0;
534   }
535 }
536
537 void
538 FGViewMgr::setViewYOffset_m (double y)
539 {
540   FGViewer * view = get_current_view();
541   if (view != 0) {
542     view->setYOffset_m(y);
543   }
544 }
545
546 double
547 FGViewMgr::getViewZOffset_m () const
548 {
549   const FGViewer * view = get_current_view();
550   if (view != 0) {
551     return ((FGViewer *)view)->getZOffset_m();
552   } else {
553     return 0;
554   }
555 }
556
557 void
558 FGViewMgr::setViewZOffset_m (double z)
559 {
560   FGViewer * view = get_current_view();
561   if (view != 0) {
562     view->setZOffset_m(z);
563   }
564 }
565
566 double
567 FGViewMgr::getViewTargetXOffset_m () const
568 {
569   const FGViewer * view = get_current_view();
570   if (view != 0) {
571     return ((FGViewer *)view)->getTargetXOffset_m();
572   } else {
573     return 0;
574   }
575 }
576
577 void
578 FGViewMgr::setViewTargetXOffset_m (double x)
579 {
580   FGViewer * view = get_current_view();
581   if (view != 0) {
582     view->setTargetXOffset_m(x);
583   }
584 }
585
586 double
587 FGViewMgr::getViewTargetYOffset_m () const
588 {
589   const FGViewer * view = get_current_view();
590   if (view != 0) {
591     return ((FGViewer *)view)->getTargetYOffset_m();
592   } else {
593     return 0;
594   }
595 }
596
597 void
598 FGViewMgr::setViewTargetYOffset_m (double y)
599 {
600   FGViewer * view = get_current_view();
601   if (view != 0) {
602     view->setTargetYOffset_m(y);
603   }
604 }
605
606 double
607 FGViewMgr::getViewTargetZOffset_m () const
608 {
609   const FGViewer * view = get_current_view();
610   if (view != 0) {
611     return ((FGViewer *)view)->getTargetZOffset_m();
612   } else {
613     return 0;
614   }
615 }
616
617 void
618 FGViewMgr::setViewTargetZOffset_m (double z)
619 {
620   FGViewer * view = get_current_view();
621   if (view != 0) {
622     view->setTargetZOffset_m(z);
623   }
624 }
625
626 int
627 FGViewMgr::getView () const
628 {
629   return ( current );
630 }
631
632 void
633 FGViewMgr::setView (int newview )
634 {
635   if ( newview < 0 || newview > (int)views.size() ) {
636     newview = 0;
637   }
638   // set new view
639   set_view( newview );
640   // copy in view data
641   copyToCurrent ();
642 }
643
644
645 double
646 FGViewMgr::getFOV_deg () const
647 {
648   const FGViewer * view = get_current_view();
649   return (view == 0 ? 0 : view->get_fov());
650 }
651
652 void
653 FGViewMgr::setFOV_deg (double fov)
654 {
655   FGViewer * view = get_current_view();
656   if (view != 0)
657     view->set_fov(fov);
658 }
659
660 double
661 FGViewMgr::getNear_m () const
662 {
663   const FGViewer * view = get_current_view();
664   return (view == 0 ? 0.5f : view->getNear_m());
665 }
666
667 void
668 FGViewMgr::setNear_m (double near_m)
669 {
670   FGViewer * view = get_current_view();
671   if (view != 0)
672     view->setNear_m(near_m);
673 }
674
675 void
676 FGViewMgr::setViewAxisLong (double axis)
677 {
678   axis_long = axis;
679 }
680
681 void
682 FGViewMgr::setViewAxisLat (double axis)
683 {
684   axis_lat = axis;
685 }
686
687 void
688 FGViewMgr::do_axes ()
689 {
690                                 // Take no action when hat is centered
691   if ( ( axis_long <  0.01 ) &&
692        ( axis_long > -0.01 ) &&
693        ( axis_lat  <  0.01 ) &&
694        ( axis_lat  > -0.01 )
695      )
696     return;
697
698   double viewDir = 999;
699
700   /* Do all the quick and easy cases */
701   if (axis_long < 0) {          // Longitudinal axis forward
702     if (axis_lat == axis_long)
703       viewDir = fgGetDouble("/sim/view/config/front-left-direction-deg");
704     else if (axis_lat == - axis_long)
705       viewDir = fgGetDouble("/sim/view/config/front-right-direction-deg");
706     else if (axis_lat == 0)
707       viewDir = fgGetDouble("/sim/view/config/front-direction-deg");
708   } else if (axis_long > 0) {   // Longitudinal axis backward
709     if (axis_lat == - axis_long)
710       viewDir = fgGetDouble("/sim/view/config/back-left-direction-deg");
711     else if (axis_lat == axis_long)
712       viewDir = fgGetDouble("/sim/view/config/back-right-direction-deg");
713     else if (axis_lat == 0)
714       viewDir = fgGetDouble("/sim/view/config/back-direction-deg");
715   } else if (axis_long == 0) {  // Longitudinal axis neutral
716     if (axis_lat < 0)
717       viewDir = fgGetDouble("/sim/view/config/left-direction-deg");
718     else if (axis_lat > 0)
719       viewDir = fgGetDouble("/sim/view/config/right-direction-deg");
720     else return; /* And assertion failure maybe? */
721   }
722
723                                 // Do all the difficult cases
724   if ( viewDir > 900 )
725     viewDir = SGD_RADIANS_TO_DEGREES * atan2 ( -axis_lat, -axis_long );
726   if ( viewDir < -1 ) viewDir += 360;
727
728   get_current_view()->setGoalHeadingOffset_deg(viewDir);
729 }