1 // sp_panel.cxx - default, 2D single-engine prop instrument panel
3 // Written by David Megginson, started January 2000.
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 #include <simgear/debug/logstream.hxx>
33 #include <Main/bfi.hxx>
34 #include <Main/globals.hxx>
38 #include "radiostack.hxx"
42 // Macros for instrument sizes
43 // (these aren't used consistently
44 // anyway, and should probably be
49 #define SIX_SPACING (SIX_W + 5)
52 #define createTexture(a) FGTextureManager::createTexture(a)
53 #define V(n) (current_properties.getValue(n, true))
57 ////////////////////////////////////////////////////////////////////////
58 // Static functions for obtaining settings.
60 // These are all temporary, and should be moved somewhere else
61 // as soon as convenient.
62 ////////////////////////////////////////////////////////////////////////
64 static const string &panelGetTime ()
66 static string out; // FIXME: not thread-safe
68 struct tm * t = globals->get_time_params()->getGmt();
69 sprintf(buf, " %.2d:%.2d:%.2d",
70 t->tm_hour, t->tm_min, t->tm_sec);
75 static bool panelGetNAV1TO ()
77 if (current_radiostack->get_nav1_inrange()) {
78 double heading = current_radiostack->get_nav1_heading();
79 double radial = current_radiostack->get_nav1_radial();
80 double var = FGBFI::getMagVar();
81 if (current_radiostack->get_nav1_loc()) {
82 double offset = fabs(heading - radial);
83 return (offset<= 8.0 || offset >= 352.0);
86 fabs(heading - var - radial);
87 return (offset <= 20.0 || offset >= 340.0);
94 static bool panelGetNAV1FROM ()
96 if (current_radiostack->get_nav1_inrange()) {
97 double heading = current_radiostack->get_nav1_heading();
98 double radial = current_radiostack->get_nav1_radial();
99 double var = FGBFI::getMagVar();
100 if (current_radiostack->get_nav1_loc()) {
101 double offset = fabs(heading - radial);
102 return (offset >= 172.0 && offset<= 188.0);
105 fabs(heading - var - radial);
106 return (offset >= 160.0 && offset <= 200.0);
113 static bool panelGetNAV2TO ()
115 if (current_radiostack->get_nav2_inrange()) {
116 double heading = current_radiostack->get_nav2_heading();
117 double radial = current_radiostack->get_nav2_radial();
118 double var = FGBFI::getMagVar();
119 if (current_radiostack->get_nav2_loc()) {
120 double offset = fabs(heading - radial);
121 return (offset<= 8.0 || offset >= 352.0);
124 fabs(heading - var - radial);
125 return (offset <= 20.0 || offset >= 340.0);
132 static bool panelGetNAV2FROM ()
134 if (current_radiostack->get_nav2_inrange()) {
135 double heading = current_radiostack->get_nav2_heading();
136 double radial = current_radiostack->get_nav2_radial();
137 double var = FGBFI::getMagVar();
138 if (current_radiostack->get_nav2_loc()) {
139 double offset = fabs(heading - radial);
140 return (offset >= 172.0 && offset<= 188.0);
143 fabs(heading - var - radial);
144 return (offset >= 160.0 && offset <= 200.0);
153 ////////////////////////////////////////////////////////////////////////
154 // Cropped textures, with string keys.
155 ////////////////////////////////////////////////////////////////////////
157 // External representation of data.
160 const char * name; // the cropped texture's name
161 const char * path; // the source texture file
163 // the rectangular area containing
164 // the texture (0.0-1.0)
165 float xMin, yMin, xMax, yMax;
169 // This table is temporarily hard-coded,
170 // but soon it will be initialized from
171 // an XML file at runtime.
172 TextureData textureData[] = {
173 {"compassFront", "Textures/Panel/misc-1.rgb",
174 48.0/128.0, 0.0, 1.0, 24.0/128.0},
175 {"airspeedBG", "Textures/Panel/faces-2.rgb",
177 {"longNeedle", "Textures/Panel/misc-1.rgb",
178 102.0/128.0, 100.0/128.0, 107.0/128.0, 1.0},
179 {"horizonBG", "Textures/Panel/faces-2.rgb",
181 {"horizonFloat", "Textures/Panel/misc-1.rgb",
182 15.0/32.0, 54.0/128.0, 28.0/32.0, 87.0/128.0},
183 {"horizonRim", "Textures/Panel/faces-2.rgb",
185 {"horizonFront", "Textures/Panel/faces-2.rgb",
187 {"altimeterBG", "Textures/Panel/faces-1.rgb",
189 {"shortNeedle", "Textures/Panel/misc-1.rgb",
190 107.0/128.0, 110.0/128.0, 113.0/128.0, 1.0},
191 {"bug", "Textures/Panel/misc-1.rgb",
192 108.0/128.0, 104.0/128.0, 112.0/128.0, 108.0/128.0},
193 {"turnBG", "Textures/Panel/faces-1.rgb",
195 {"turnPlane", "Textures/Panel/misc-1.rgb",
196 0.0, 3.0/8.0, 3.0/8.0, 0.5},
197 {"turnBall", "Textures/Panel/misc-1.rgb",
198 108.0/128.0, 100.0/128.0, 112.0/128.0, 104.0/128.0},
199 {"compassBG", "Textures/Panel/faces-1.rgb",
201 {"compassCenter", "Textures/Panel/misc-1.rgb",
202 15.0/32.0, 11.0/16.0, 25.0/32.0, 1.0},
203 {"headingKnob", "Textures/Panel/misc-1.rgb",
204 0, 64.0/128.0, 21.0/128.0, 85.0/128.0},
205 {"knob", "Textures/Panel/misc-1.rgb",
206 79.0/128.0, 31.0/128.0, 101.0/128.0, 53.0/128.0},
207 {"verticalBG", "Textures/Panel/faces-1.rgb",
209 {"rpmBG", "Textures/Panel/faces-3.rgb",
211 {"flapsBG", "Textures/Panel/faces-3.rgb",
213 {"clockBG", "Textures/Panel/faces-3.rgb",
215 {"controlsBG", "Textures/Panel/faces-3.rgb",
217 {"navFG", "Textures/Panel/misc-1.rgb",
218 0, 0, 0.25, 5.0/16.0},
219 {"obsKnob", "Textures/Panel/misc-1.rgb",
220 0.0, 86.0/128.0, 21.0/128.0, 107.0/128.0},
221 {"toFlag", "Textures/Panel/misc-1.rgb",
222 120.0/128.0, 74.0/128.0, 1.0, 80.0/128.0},
223 {"fromFlag", "Textures/Panel/misc-1.rgb",
224 120.0/128.0, 80.0/128.0, 1.0, 86.0/128.0},
225 {"offFlag", "Textures/Panel/misc-1.rgb",
226 120.0/128.0, 0.5, 1.0, 70.0/128.0},
227 {"navNeedle", "Textures/Panel/misc-1.rgb",
228 56.0/128.0, 0.5, 58.0/128.0, 1.0},
229 {"adfNeedle", "Textures/Panel/misc-1.rgb",
230 120.0/128.0, 88.0/128.0, 1.0, 1.0},
231 {"adfKnob", "Textures/Panel/misc-1.rgb",
232 0.0, 107.0/128.0, 21.0/128.0, 1.0},
233 {"adfPlane", "Textures/Panel/misc-1.rgb",
234 102.0/128.0, 32.0/128.0, 1.0, 48.0/128.0},
235 {"adfFace", "Textures/Panel/faces-4.rgb",
237 {"navRadioBG", "Textures/Panel/radios-1.rgb",
238 0.0, 0.75, 1.0, 1.0},
239 {"adfRadioBG", "Textures/Panel/radios-1.rgb",
240 0.0, 0.5, 1.0, 0.75},
241 {"autopilotBG", "Textures/Panel/radios-1.rgb",
242 0.0, 0.375, 1.0, 0.5},
243 {"hdgButtonOn", "Textures/Panel/misc-1.rgb",
244 39.0/128.0, 118.0/128.0, 54.0/128.0, 128.0/128.0},
245 {"hdgButtonOff", "Textures/Panel/misc-1.rgb",
246 22.0/128.0, 118.0/128.0, 37.0/128.0, 128.0/128.0},
247 {"navButtonOn", "Textures/Panel/misc-1.rgb",
248 39.0/128.0, 106.0/128.0, 54.0/128.0, 116.0/128.0},
249 {"navButtonOff", "Textures/Panel/misc-1.rgb",
250 22.0/128.0, 106.0/128.0, 37.0/128.0, 116.0/128.0},
251 {"altButtonOn", "Textures/Panel/misc-1.rgb",
252 39.0/128.0, 82.0/128.0, 54.0/128.0, 92.0/128.0},
253 {"altButtonOff", "Textures/Panel/misc-1.rgb",
254 22.0/128.0, 82.0/128.0, 37.0/128.0, 92.0/128.0},
255 {"dmeBG", "Textures/Panel/radios-1.rgb",
256 0.0, 0.25, 0.375, 0.375},
257 {"compassRibbon", "Textures/Panel/compass-ribbon.rgb",
262 // Internal representation of
264 map<const char *,CroppedTexture> tex;
269 * Ugly kludge to delay texture loading.
271 class MyTexturedLayer : public FGTexturedLayer
274 MyTexturedLayer (const char * name, int w = -1, int h = -1)
275 : FGTexturedLayer(w, h), _name(name), _textureLoaded(false) {}
277 virtual void draw ();
278 virtual CroppedTexture &getTexture ();
282 mutable bool _textureLoaded;
286 MyTexturedLayer::getTexture ()
288 MyTexturedLayer * me = (MyTexturedLayer *)this;
289 if (!_textureLoaded) {
290 _textureLoaded = true;
291 me->setTexture(tex[_name]);
293 return FGTexturedLayer::getTexture();
297 MyTexturedLayer::draw ()
300 FGTexturedLayer::draw();
306 * Populate the textureMap from the data table.
311 for (int i = 0; textureData[i].name; i++) {
312 tex[textureData[i].name] =
313 CroppedTexture(textureData[i].path, textureData[i].xMin,
314 textureData[i].yMin, textureData[i].xMax,
315 textureData[i].yMax);
321 ////////////////////////////////////////////////////////////////////////
322 // Special class for magnetic compass ribbon layer.
323 ////////////////////////////////////////////////////////////////////////
325 class MagRibbon : public MyTexturedLayer
328 MagRibbon (int w, int h);
329 virtual ~MagRibbon () {}
331 virtual void draw ();
334 MagRibbon::MagRibbon (int w, int h)
335 : MyTexturedLayer("compassRibbon", w, h)
342 double heading = FGSteam::get_MH_deg();
343 double xoffset, yoffset;
345 while (heading >= 360.0) {
348 while (heading < 0.0) {
352 if (heading >= 60.0 && heading <= 180.0) {
353 xoffset = heading / 240.0;
355 } else if (heading >= 150.0 && heading <= 270.0) {
356 xoffset = (heading - 90.0) / 240.0;
358 } else if (heading >= 240.0 && heading <= 360.0) {
359 xoffset = (heading - 180.0) / 240.0;
364 xoffset = (heading - 270.0) / 240.0;
368 xoffset = 1.0 - xoffset;
369 // Adjust to put the number in the centre
372 CroppedTexture &t = getTexture();
375 t.maxX = xoffset + 0.5;
376 t.maxY = yoffset + 0.25;
377 MyTexturedLayer::draw();
382 ////////////////////////////////////////////////////////////////////////
384 ////////////////////////////////////////////////////////////////////////
395 const char * propName;
396 float min, max, factor, offset;
401 FGInstrumentLayer * layer;
402 TransData transformations[16];
405 struct InstrumentData
409 FGPanelAction * actions[16];
410 LayerData layers[16];
413 InstrumentData instruments[] =
416 {"magcompass", 768, 459, SIX_W, SIX_W/2, {}, {
417 {new MagRibbon(int(SIX_W*0.8), int(SIX_W*0.2))},
418 {new MyTexturedLayer("compassFront", SIX_W, SIX_W*(24.0/80.0))}
421 {"airspeed", SIX_X, SIX_Y, SIX_W, SIX_W, {}, {
422 {new MyTexturedLayer("airspeedBG", -1, -1)},
423 {new MyTexturedLayer("longNeedle", int(SIX_W*(5.0/64.0)),
424 int(SIX_W*(7.0/16.0))), {
425 {TransData::Rotation, "/steam/airspeed",
426 30.0, 220.0, 36.0/20.0, -54.0},
427 {TransData::YShift, 0,
428 0.0, 0.0, 0.0, SIX_W*(12.0/64.0)}
432 {"horizon", SIX_X + SIX_SPACING, SIX_Y, SIX_W, SIX_W, {}, {
433 {new MyTexturedLayer("horizonBG", -1, -1), {
434 {TransData::Rotation, "/orientation/roll", -360.0, 360.0, -1.0, 0.0}
436 {new MyTexturedLayer("horizonFloat",
437 int(SIX_W * (13.0/16.0)), int(SIX_W * (33.0/64.0))), {
438 {TransData::Rotation, "/orientation/roll", -360.0, 360.0, -1.0, 0.0},
439 {TransData::YShift, "/orientation/pitch",
440 -20.0, 20.0, -(1.5/160.0)*SIX_W, 0.0}
442 {new MyTexturedLayer("horizonRim", -1, -1), {
443 {TransData::Rotation, "/orientation/roll", -360.0, 360.0, -1.0, 0.0}
445 {new MyTexturedLayer("horizonFront", -1, -1)}
448 {"altimeter", SIX_X + SIX_SPACING + SIX_SPACING, SIX_Y, SIX_W, SIX_W, {}, {
449 {new MyTexturedLayer("altimeterBG", -1, -1)},
450 {new MyTexturedLayer("longNeedle",
451 int(SIX_W*(5.0/64.0)), int(SIX_W*(7.0/16.0))), {
452 {TransData::Rotation, "/steam/altitude",
453 0.0, 100000.0, 360.0/1000.0, 0.0},
454 {TransData::YShift, 0, 0.0, 0.0, 0.0, SIX_W*(12.0/64.0)}
456 {new MyTexturedLayer("shortNeedle",
457 int(SIX_W*(6.0/64.0)), int(SIX_W*(18.0/64.0))), {
458 {TransData::Rotation, "/steam/altitude",
459 0.0, 100000.0, 360.0/10000.0, 0.0},
460 {TransData::YShift, 0, 0.0, 0.0, 0.0, SIX_W/8.0}
462 {new MyTexturedLayer("bug", int(SIX_W*(4.0/64.0)), int(SIX_W*(4.0/64.0))), {
463 {TransData::Rotation, "/steam/altitude",
464 0.0, 100000.0, 360.0/100000.0, 0.0},
465 {TransData::YShift, 0, 0.0, 0.0, 0.0, SIX_W/2.0-4}
469 {"turn", SIX_X, SIX_Y-SIX_SPACING, SIX_W, SIX_W, {}, {
470 {new MyTexturedLayer("turnBG", -1, -1)},
471 {new MyTexturedLayer("turnPlane", int(SIX_W * 0.75), int(SIX_W * 0.25)), {
472 {TransData::Rotation, "/steam/turn-rate", -2.5, 2.5, 20.0, 0.0},
473 {TransData::YShift, 0, 0.0, 0.0, 0.0, int(SIX_W * 0.0625)}
475 {new MyTexturedLayer("turnBall",
476 int(SIX_W * (4.0/64.0)), int(SIX_W * (4.0/64.0))), {
477 {TransData::Rotation, "/steam/slip-skid",
478 -0.1, 0.1, 400.0, 0.0},
479 {TransData::YShift, 0, 0.0, 0.0, 0.0, -(SIX_W/4)+4}
483 {"verticalVelocity", SIX_X+SIX_SPACING+SIX_SPACING, SIX_Y-SIX_SPACING,
485 {new MyTexturedLayer("verticalBG", -1, -1)},
486 {new MyTexturedLayer("longNeedle",
487 int(SIX_W*(5.0/64.0)), int(SIX_W*(7.0/16.0))), {
488 {TransData::Rotation, "/steam/vertical-speed",
489 -2000.0, 2000.0, 42.0/500.0, 270.0},
490 {TransData::YShift, 0, 0.0, 0.0, 0.0, SIX_W*12.0/64.0}
494 {"controls", SIX_X, SIX_Y-(SIX_SPACING*2), SMALL_W, SMALL_W, {}, {
495 {new MyTexturedLayer("controlsBG", -1, -1)},
496 {new MyTexturedLayer("bug", int(SIX_W*4.0/64.0), int(SIX_W*4.0/64.0)), {
497 {TransData::XShift, "/controls/aileron",
498 -1.0, 1.0, SMALL_W*0.75/2.0, 0.0},
499 {TransData::YShift, 0, 0.0, 0.0, 0.0, (SIX_W/2.0)-12.0}
501 {new MyTexturedLayer("bug", int(SIX_W*4.0/64.0), int(SIX_W*4.0/64.0)), {
502 {TransData::Rotation, 0, 0.0, 0.0, 0.0, 180.0},
503 {TransData::XShift, "/controls/rudder",
504 -1.0, 1.0, -SMALL_W*0.75/2.0, 0.0},
505 {TransData::YShift, 0, 0.0, 0.0, 0.0, SIX_W/2.0-12.0}
507 {new MyTexturedLayer("bug", int(SIX_W*4.0/64.0), int(SIX_W*4.0/64.0)), {
508 {TransData::Rotation, 0, 0.0, 0.0, 0.0, 270.0},
509 {TransData::YShift, 0, 0.0, 0.0, 0.0, -SMALL_W*3.0/8.0},
510 {TransData::XShift, "/controls/elevator-trim",
511 -1.0, 1.0, SMALL_W*0.75/2.0, 0.0},
512 {TransData::YShift, 0, 0.0, 0.0, 0.0, (SIX_W/2.0)-12.0}
514 {new MyTexturedLayer("bug", int(SIX_W*4.0/64.0), int(SIX_W*4.0/64.0)), {
515 {TransData::Rotation, 0, 0.0, 0.0, 0.0, 90.0},
516 {TransData::YShift, 0, 0.0, 0.0, 0.0, -SMALL_W*(3.0/8.0)},
517 {TransData::XShift, "/controls/elevator",
518 -1.0, 1.0, -SMALL_W*0.75/2.0, 0.0},
519 {TransData::YShift, 0, 0.0, 0.0, 0.0, (SIX_W/2.0)-12.0}
523 {"flaps", SIX_X+SIX_SPACING, SIX_Y-(SIX_SPACING*2), SMALL_W, SMALL_W, {}, {
524 {new MyTexturedLayer("flapsBG", -1, -1)},
525 {new MyTexturedLayer("longNeedle",
526 int(SIX_W*(5.0/64.0)), int(SIX_W*(7.0/16.0))), {
527 {TransData::XShift, 0, 0.0, 0.0, 0.0, -(SMALL_W/4) + (SMALL_W/16)},
528 {TransData::Rotation, "/controls/flaps", 0.0, 1.0, 120.0, 30.0},
529 {TransData::YShift, 0, 0.0, 0.0, 0.0, SIX_W*12.0/64.0}
533 {"rpm", SIX_X+(SIX_SPACING*2), SIX_Y-(SIX_SPACING*2), SMALL_W, SMALL_W, {}, {
534 {new MyTexturedLayer("rpmBG", -1, -1)},
535 {new MyTexturedLayer("longNeedle",
536 int(SIX_W*(5.0/64.0)), int(SIX_W*(7.0/16.0))), {
537 {TransData::Rotation, "/controls/throttle", 0.0, 100.0, 300.0, -150.0},
538 {TransData::YShift, 0, 0.0, 0.0, 0.0, SIX_W*12.0/64.0}
542 {"gyro", SIX_X+SIX_SPACING, SIX_Y-SIX_SPACING, SIX_W, SIX_W, {
543 new FGAdjustAction(0, SIX_W/2-SIX_W/5, -SIX_W/2, SIX_W/10, SIX_W/5,
544 V("/autopilot/settings/heading-magnetic"),
545 -1.0, -360.0, 360.0, true),
546 new FGAdjustAction(0, SIX_W/2 - SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
547 V("/autopilot/settings/heading-magnetic"),
548 1.0, -360.0, 360.0, true),
549 new FGAdjustAction(1, SIX_W/2 - SIX_W/5, -SIX_W/2, SIX_W/10, SIX_W/5,
550 V("/autopilot/settings/heading-magnetic"),
551 -5.0, -360.0, 360.0, true),
552 new FGAdjustAction(1, SIX_W/2 - SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
553 V("/autopilot/settings/heading-magnetic"),
554 5.0, -360.0, 360.0, true),
555 new FGAdjustAction(0, -SIX_W/2, -SIX_W/2, SIX_W/10, SIX_W/5,
556 V("/steam/gyro-compass-error"),
557 -1.0, -360.0, 360.0, true),
558 new FGAdjustAction(0, -SIX_W/2+SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
559 V("/steam/gyro-compass-error"),
560 1.0, -360.0, 360.0, true),
561 new FGAdjustAction(1, -SIX_W/2, -SIX_W/2, SIX_W/10, SIX_W/5,
562 V("/steam/gyro-compass-error"),
563 -5.0, -360.0, 360.0, true),
564 new FGAdjustAction(1, -SIX_W/2+SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
565 V("/steam/gyro-compass-error"),
566 5.0, -360.0, 360.0, true)
568 {new MyTexturedLayer("compassBG", -1, -1), {
569 {TransData::Rotation, "/steam/gyro-compass", -720.0, 720.0, -1.0, 0.0}
571 {new MyTexturedLayer("bug",
572 int(SIX_W*(4.0/64.0)), int(SIX_W*(4.0/64.0))), {
573 {TransData::Rotation, "/autopilot/settings/heading",
574 -720.0, 720.0, 1.0, 180.0},
575 {TransData::Rotation, "/steam/gyro-compass", -720.0, 720.0, -1.0, 0.0},
576 {TransData::YShift, 0, 0.0, 0.0, 0.0, -(SIX_W/2.0)+4}
578 {new MyTexturedLayer("compassCenter", int(SIX_W*0.625), int(SIX_W*0.625))},
579 {new MyTexturedLayer("headingKnob",
580 int(SIX_W*(21.0/112.0)), int(SIX_W*(21.0/112.0))), {
581 {TransData::XShift, 0, 0.0, 0.0, 0.0, SIX_W/2-10},
582 {TransData::YShift, 0, 0.0, 0.0, 0.0, -SIX_W/2+10},
583 {TransData::Rotation, "/autopilot/settings/heading",
584 -360.0, 360.0, 1.0, 0.0}
586 {new MyTexturedLayer("knob",
587 int(SIX_W*(22.0/112.0)), int(SIX_W*(22.0/112.0))), {
588 {TransData::XShift, 0, 0.0, 0.0, 0.0, -SIX_W/2+10},
589 {TransData::YShift, 0, 0.0, 0.0, 0.0, -SIX_W/2+10}
594 {"chronometer", SIX_X-SIX_SPACING-8, SIX_Y, SMALL_W, SMALL_W, {}, {
595 {new MyTexturedLayer("clockBG")},
596 {new FGTextLayer(SMALL_W, SMALL_W,
597 new FGTextLayer::Chunk(FGTextLayer::TEXT_VALUE,
598 V("/panel/time"))), {
599 {TransData::XShift, 0, 0.0, 0.0, 0.0, SMALL_W*-0.38},
600 {TransData::YShift, 0, 0.0, 0.0, 0.0, SMALL_W*-0.06}
604 {"nav1", SIX_X+(SIX_SPACING*3)+20, SIX_Y, SIX_W, SIX_W, {
605 new FGAdjustAction(0, -SIX_W/2, -SIX_W/2, SIX_W/10, SIX_W/5,
606 V("/radios/nav1/radials/selected"),
607 1.0, 0.0, 360.0, true),
608 new FGAdjustAction(0, -SIX_W/2 + SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
609 V("/radios/nav1/radials/selected"),
610 -1.0, 0.0, 360.0, true),
611 new FGAdjustAction(1, -SIX_W/2, -SIX_W/2, SIX_W/10, SIX_W/5,
612 V("/radios/nav1/radials/selected"),
613 5.0, 0.0, 360.0, true),
614 new FGAdjustAction(1, -SIX_W/2 + SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
615 V("/radios/nav1/radials/selected"),
616 -5.0, 0.0, 360.0, true)
618 {new MyTexturedLayer("compassBG"), {
619 {TransData::Rotation, "/radios/nav1/radials/selected",
620 -360.0, 360.0, -1.0, 0.0}
622 {new MyTexturedLayer("navFG", SIX_W/2, int(SIX_W*(5.0/8.0)))},
623 {new MyTexturedLayer("obsKnob", int(SIX_W*(21.0/112.0)),
624 int(SIX_W*(21.0/112.0))), {
625 {TransData::XShift, 0, 0.0, 0.0, 0.0, -SIX_W/2+10},
626 {TransData::YShift, 0, 0.0, 0.0, 0.0, -SIX_W/2+10},
627 {TransData::Rotation, "/radios/nav1/radials/selected",
628 -360.0, 360.0, 1.0, 0.0}
630 {new FGSwitchLayer(SIX_W/8, SIX_W/8, V("/panel/vor1/to-flag"),
631 new MyTexturedLayer("toFlag", SIX_W/8, SIX_W/8),
632 new FGSwitchLayer(SIX_W/8, SIX_W/8,
633 V("/panel/vor1/from-flag"),
634 new MyTexturedLayer("fromFlag",
637 new MyTexturedLayer("offFlag",
640 {TransData::YShift, 0, 0.0, 0.0, 0.0, -int(SIX_W*0.1875)}
642 {new MyTexturedLayer("navNeedle", SIX_W/32, SIX_W/2), {
643 {TransData::XShift, "/steam/vor1",
644 -10.0, 10.0, SIX_W/40.0, 0.0}
646 {new MyTexturedLayer("navNeedle", SIX_W/32, SIX_W/2), {
647 {TransData::YShift, "/steam/glidescope1", -1.0, 1.0, SIX_W/5.0, 0.0},
648 {TransData::Rotation, 0, 0.0, 0.0, 0.0, 90}
652 {"nav2", SIX_X+(SIX_SPACING*3)+20, SIX_Y-SIX_SPACING, SIX_W, SIX_W, {
653 new FGAdjustAction(0, -SIX_W/2, -SIX_W/2, SIX_W/10, SIX_W/5,
654 V("/radios/nav2/radials/selected"),
655 1.0, 0.0, 360.0, true),
656 new FGAdjustAction(0, -SIX_W/2 + SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
657 V("/radios/nav2/radials/selected"),
658 -1.0, 0.0, 360.0, true),
659 new FGAdjustAction(1, -SIX_W/2, -SIX_W/2, SIX_W/10, SIX_W/5,
660 V("/radios/nav2/radials/selected"),
661 5.0, 0.0, 360.0, true),
662 new FGAdjustAction(1, -SIX_W/2 + SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
663 V("/radios/nav2/radials/selected"),
664 -5.0, 0.0, 360.0, true)
666 {new MyTexturedLayer("compassBG"), {
667 {TransData::Rotation, "/radios/nav2/radials/selected",
668 -360.0, 360.0, -1.0, 0.0}
670 {new MyTexturedLayer("navFG", SIX_W/2, int(SIX_W*(5.0/8.0)))},
671 {new MyTexturedLayer("obsKnob", int(SIX_W*(21.0/112.0)),
672 int(SIX_W*(21.0/112.0))), {
673 {TransData::XShift, 0, 0.0, 0.0, 0.0, -SIX_W/2+10},
674 {TransData::YShift, 0, 0.0, 0.0, 0.0, -SIX_W/2+10},
675 {TransData::Rotation, "/radios/nav2/radials/selected",
676 -360.0, 360.0, 1.0, 0.0}
678 {new FGSwitchLayer(SIX_W/8, SIX_W/8, V("/panel/vor2/to-flag"),
679 new MyTexturedLayer("toFlag", SIX_W/8, SIX_W/8),
680 new FGSwitchLayer(SIX_W/8, SIX_W/8,
681 V("/panel/vor2/from-flag"),
682 new MyTexturedLayer("fromFlag",
685 new MyTexturedLayer("offFlag",
688 {TransData::YShift, 0, 0.0, 0.0, 0.0, -int(SIX_W*0.1875)}
690 {new MyTexturedLayer("navNeedle", SIX_W/32, SIX_W/2), {
691 {TransData::XShift, "/steam/vor2",
692 -10.0, 10.0, SIX_W/40.0, 0.0}
696 {"adf", SIX_X+(SIX_SPACING*3)+20, SIX_Y-(SIX_SPACING*2), SIX_W, SIX_W, {
697 new FGAdjustAction(0, -SIX_W/2, -SIX_W/2, SIX_W/10, SIX_W/5,
698 V("/radios/adf/rotation"),
699 -1.0, 0.0, 360.0, true),
700 new FGAdjustAction(0, -SIX_W/2 + SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
701 V("/radios/adf/rotation"),
702 1.0, 0.0, 360.0, true),
703 new FGAdjustAction(1, -SIX_W/2, -SIX_W/2, SIX_W/10, SIX_W/5,
704 V("/radios/adf/rotation"),
705 -5.0, 0.0, 360.0, true),
707 new FGAdjustAction(1, -SIX_W/2 + SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
708 V("/radios/adf/rotation"),
709 5.0, 0.0, 360.0, true)
711 {new MyTexturedLayer("compassBG"), {
712 {TransData::Rotation, "/radios/adf/rotation", 0.0, 360.0, 1.0, 0.0}
714 {new MyTexturedLayer("adfFace", -1, -1), {}},
715 {new MyTexturedLayer("adfNeedle", SIX_W/8, int(SIX_W*0.625)), {
716 {TransData::Rotation, "/steam/adf", -720.0, 720.0, 1.0, 0.0}
718 {new MyTexturedLayer("adfKnob", int(SIX_W*(21.0/112.0)),
719 int(SIX_W*(21.0/112.0))), {
720 {TransData::XShift, 0, 0.0, 0.0, 0.0, -SIX_W/2+10},
721 {TransData::YShift, 0, 0.0, 0.0, 0.0, -SIX_W/2+10},
722 {TransData::Rotation, "/radios/adf/rotation", 0.0, 360.0, 1.0, 0.0}
732 ////////////////////////////////////////////////////////////////////////
733 // Static factory functions to create textured gauges.
735 // These will be replaced first with a giant table, and then with
736 // configuration files read from an external source, but for now
737 // they're hard-coded.
738 ////////////////////////////////////////////////////////////////////////
742 * Create Nav-Com radio 1.
744 static FGPanelInstrument *
745 createNavCom1 (int x, int y)
747 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W*2, SIX_W/2);
749 // Use the button to swap standby and active
751 inst->addAction(new FGSwapAction(0, int(SIX_W * .375),
752 -SIX_W/4, SIX_W/4, SIX_W/4,
753 V("/radios/nav1/frequencies/selected"),
754 V("/radios/nav1/frequencies/standby")));
756 // Use the knob to tune the standby NAV
757 inst->addAction(new FGAdjustAction(0, SIX_W-SIX_W/4, -SIX_W/4,
759 V("/radios/nav1/frequencies/standby"),
760 -0.05, 108.0, 117.95, true));
761 inst->addAction(new FGAdjustAction(0, SIX_W-SIX_W/8, -SIX_W/4,
763 V("/radios/nav1/frequencies/standby"),
764 0.05, 108.0, 117.95, true));
765 inst->addAction(new FGAdjustAction(1, SIX_W-SIX_W/4, -SIX_W/4,
767 V("/radios/nav1/frequencies/standby"),
768 -0.5, 108.0, 117.95, true));
769 inst->addAction(new FGAdjustAction(1, SIX_W-SIX_W/8, -SIX_W/4,
771 V("/radios/nav1/frequencies/standby"),
772 0.5, 108.0, 117.95, true));
774 // Layer 0: background
775 inst->addLayer(tex["navRadioBG"], SIX_W*2, SIX_W/2);
777 // Layer 1: NAV frequencies
778 FGTextLayer * text = new FGTextLayer(SIX_W*2, SMALL_W/2);
780 FGTextLayer::Chunk(FGTextLayer::DOUBLE_VALUE,
781 V("/radios/nav1/frequencies/selected"),
784 FGTextLayer::Chunk(FGTextLayer::DOUBLE_VALUE,
785 V("/radios/nav1/frequencies/standby"),
787 text->setPointSize(14);
788 text->setColor(1.0, 0.5, 0.0);
789 inst->addLayer(text);
790 inst->addTransformation
791 (new FGPanelTransformation(FGPanelTransformation::XSHIFT, 0,
793 inst->addTransformation
794 (new FGPanelTransformation(FGPanelTransformation::YSHIFT, 0,
802 * Create Nav-Com radio 2.
804 static FGPanelInstrument *
805 createNavCom2 (int x, int y)
807 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W*2, SIX_W/2);
809 // Use the button to swap standby and active
811 inst->addAction(new FGSwapAction(0, int(SIX_W * .375), -SIX_W/4, SIX_W/4, SIX_W/4,
812 V("/radios/nav2/frequencies/selected"),
813 V("/radios/nav2/frequencies/standby")));
815 // Use the knob to tune the standby NAV
816 inst->addAction(new FGAdjustAction(0, SIX_W-SIX_W/4, -SIX_W/4, SIX_W/8, SIX_W/4,
817 V("/radios/nav2/frequencies/standby"),
818 -0.05, 108.0, 117.95, true));
819 inst->addAction(new FGAdjustAction(0, SIX_W-SIX_W/8, -SIX_W/4, SIX_W/8, SIX_W/4,
820 V("/radios/nav2/frequencies/standby"),
821 0.05, 108.0, 117.95, true));
822 inst->addAction(new FGAdjustAction(1, SIX_W-SIX_W/4, -SIX_W/4, SIX_W/8, SIX_W/4,
823 V("/radios/nav2/frequencies/standby"),
824 -0.5, 108.0, 117.95, true));
825 inst->addAction(new FGAdjustAction(1, SIX_W-SIX_W/8, -SIX_W/4, SIX_W/8, SIX_W/4,
826 V("/radios/nav2/frequencies/standby"),
827 0.5, 108.0, 117.95, true));
829 // Layer 0: background
830 inst->addLayer(tex["navRadioBG"], SIX_W*2, SIX_W/2);
832 // Layer 1: NAV frequencies
833 FGTextLayer * text = new FGTextLayer(SIX_W*2, SIX_W/2);
835 FGTextLayer::Chunk(FGTextLayer::DOUBLE_VALUE,
836 V("/radios/nav2/frequencies/selected"),
839 FGTextLayer::Chunk(FGTextLayer::DOUBLE_VALUE,
840 V("/radios/nav2/frequencies/standby"),
842 text->setPointSize(14);
843 text->setColor(1.0, 0.5, 0.0);
844 inst->addLayer(text);
845 inst->addTransformation
846 (new FGPanelTransformation(FGPanelTransformation::XSHIFT, 0,
848 inst->addTransformation
849 (new FGPanelTransformation(FGPanelTransformation::YSHIFT, 0,
859 static FGPanelInstrument *
860 createADFRadio (int x, int y)
862 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W*2, SIX_W/2);
864 // Use the knob to tune the standby NAV
865 inst->addAction(new FGAdjustAction(0, int(SIX_W * 0.7), int(-SIX_W * 0.07),
866 int(SIX_W * 0.09), int(SIX_W * 0.14),
867 V("/radios/adf/frequencies/selected"),
868 -1.0, 100.0, 1299, true));
869 inst->addAction(new FGAdjustAction(0, int(SIX_W * 0.79), int(-SIX_W * 0.07),
870 int(SIX_W * 0.09), int(SIX_W * 0.14),
871 V("/radios/adf/frequencies/selected"),
872 1.0, 100.0, 1299, true));
873 inst->addAction(new FGAdjustAction(1, int(SIX_W * 0.7), int(-SIX_W * 0.07),
874 int(SIX_W * 0.09), int(SIX_W * 0.14),
875 V("/radios/adf/frequencies/selected"),
876 -25.0, 100.0, 1299, true));
877 inst->addAction(new FGAdjustAction(1, int(SIX_W * 0.79), int(-SIX_W * 0.07),
878 int(SIX_W * 0.09), int(SIX_W * 0.14),
879 V("/radios/adf/frequencies/selected"),
880 25.0, 100.0, 1299, true));
882 // Layer 0: background
883 inst->addLayer(tex["adfRadioBG"], SIX_W*2, SIX_W/2);
885 // Layer: ADF frequency
886 FGTextLayer * text = new FGTextLayer(SIX_W*2, SIX_W/2);
888 FGTextLayer::Chunk(FGTextLayer::DOUBLE_VALUE,
889 V("/radios/adf/frequencies/selected"),
891 text->setPointSize(14);
892 text->setColor(1.0, 0.5, 0.0);
893 inst->addLayer(text);
894 inst->addTransformation
895 (new FGPanelTransformation(FGPanelTransformation::XSHIFT, 0,
896 0.0, 0.0, 1.0, -SIX_W + 18));
903 * Construct the autopilot.
907 createAP (int x, int y)
909 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W*2, SIX_W/4);
911 // Action: select HDG button
912 inst->addAction(new FGToggleAction(0, int(-SIX_W*0.6125), -SIX_W/16, SIX_W/4, SIX_W/8,
913 V("/autopilot/locks/heading")));
915 // Action: select NAV button
916 inst->addAction(new FGToggleAction(0, int(-SIX_W*0.3625), -SIX_W/16, SIX_W/4, SIX_W/8,
917 V("/autopilot/locks/nav1")));
919 // Action: select ALT button
920 inst->addAction(new FGToggleAction(0, int(-SIX_W*0.1125), -SIX_W/16, SIX_W/4, SIX_W/8,
921 V("/autopilot/locks/altitude")));
923 // Layer: AP background
924 inst->addLayer(tex["autopilotBG"], SIX_W*2, SIX_W/4);
926 // Display HDG button
928 new FGSwitchLayer(SIX_W/4, SIX_W/8, V("/autopilot/locks/heading"),
929 new FGTexturedLayer(tex["hdgButtonOn"], SIX_W/4, SIX_W/8),
930 new FGTexturedLayer(tex["hdgButtonOff"], SIX_W/4, SIX_W/8));
932 inst->addTransformation
933 (new FGPanelTransformation(FGPanelTransformation::XSHIFT, 0,
934 0.0, 0.0, 1.0, -SIX_W * 0.5));
936 // Display NAV button
937 sw = new FGSwitchLayer(SIX_W/4, SIX_W/8, V("/autopilot/locks/nav1"),
938 new FGTexturedLayer(tex["navButtonOn"], SIX_W/4, SIX_W/8),
939 new FGTexturedLayer(tex["navButtonOff"], SIX_W/4, SIX_W/8));
941 inst->addTransformation
942 (new FGPanelTransformation(FGPanelTransformation::XSHIFT, 0,
943 0.0, 0.0, 1.0, -SIX_W * 0.25));
945 // Display ALT button
946 sw = new FGSwitchLayer(SIX_W/4, SIX_W/8, V("/autopilot/locks/altitude"),
947 new FGTexturedLayer(tex["altButtonOn"], SIX_W/4, SIX_W/8),
948 new FGTexturedLayer(tex["altButtonOff"], SIX_W/4, SIX_W/8));
955 createDME (int x, int y)
957 FGLayeredInstrument * inst =
958 new FGLayeredInstrument(x, y, int(SIX_W * 0.75), int(SIX_W * 0.25));
961 inst->addLayer(tex["dmeBG"]);
963 // Layer: current distance
965 FGTextLayer * text1 = new FGTextLayer(SIX_W/2, SIX_W/4);
966 text1->addChunk(new FGTextLayer::Chunk(FGTextLayer::DOUBLE_VALUE,
967 V("/radios/nav1/dme/distance"),
970 text1->setPointSize(12);
971 text1->setColor(1.0, 0.5, 0.0);
973 FGTextLayer * text2 = new FGTextLayer(SIX_W/2, SIX_W/4);
974 text2->addChunk(new FGTextLayer::Chunk("---.-"));
975 text2->setPointSize(12);
976 text2->setColor(1.0, 0.5, 0.0);
979 new FGSwitchLayer(SIX_W/2, SIX_W/4, V("/radios/nav1/dme/in-range"),
983 inst->addTransformation
984 (new FGPanelTransformation(FGPanelTransformation::XSHIFT, 0,
985 0.0, 0.0, 1.0, -20));
986 inst->addTransformation
987 (new FGPanelTransformation(FGPanelTransformation::YSHIFT, 0,
995 ////////////////////////////////////////////////////////////////////////
996 // Construct a panel for a small, single-prop plane.
997 ////////////////////////////////////////////////////////////////////////
1000 fgCreateSmallSinglePropPanel (int xpos, int ypos, int finx, int finy)
1002 int w = finx - xpos;
1003 int h = finy - ypos;
1004 FGPanel * panel = new FGPanel(xpos, ypos, w, h);
1007 // Tie local functions
1008 // FIXME: define these elsewhere...
1009 current_properties.tieString("/panel/time", panelGetTime);
1010 current_properties.tieBool("/panel/vor1/to-flag", panelGetNAV1TO);
1011 current_properties.tieBool("/panel/vor1/from-flag", panelGetNAV1FROM);
1012 current_properties.tieBool("/panel/vor2/to-flag", panelGetNAV2TO);
1013 current_properties.tieBool("/panel/vor2/from-flag", panelGetNAV2FROM);
1017 // Read gauges from data table.
1018 for (int i = 0; instruments[i].name; i++) {
1019 InstrumentData &gauge = instruments[i];
1020 FGLayeredInstrument * inst =
1021 new FGLayeredInstrument(gauge.x, gauge.y, gauge.w, gauge.h);
1023 for (int j = 0; gauge.actions[j]; j++) {
1024 inst->addAction(gauge.actions[j]);
1027 for (int j = 0; gauge.layers[j].layer; j++) {
1028 LayerData &layer = gauge.layers[j];
1029 // inst->addLayer(tex[layer.textureName], layer.w, layer.h);
1030 inst->addLayer(layer.layer);
1032 for (int k = 0; layer.transformations[k].type; k++) {
1033 TransData &trans = layer.transformations[k];
1034 FGPanelTransformation::Type type;
1035 switch (trans.type) {
1036 case TransData::Rotation:
1037 type = FGPanelTransformation::ROTATION;
1039 case TransData::XShift:
1040 type = FGPanelTransformation::XSHIFT;
1042 case TransData::YShift:
1043 type = FGPanelTransformation::YSHIFT;
1048 if (trans.propName != 0) {
1049 inst->addTransformation
1050 (new FGPanelTransformation(type, V(trans.propName),
1051 trans.min, trans.max,
1052 trans.factor, trans.offset));
1054 inst->addTransformation
1055 (new FGPanelTransformation(type, 0,
1056 0.0, 0.0, 1.0, trans.offset));
1061 panel->addInstrument(inst);
1067 // Set the background texture
1068 panel->setBackground(createTexture("Textures/Panel/panel-bg.rgb"));
1071 x = SIX_X + (SIX_SPACING * 5);
1073 panel->addInstrument(createDME(x, y));
1074 y -= (int)(SIX_W * 0.375);
1075 panel->addInstrument(createNavCom1(x, y));
1077 panel->addInstrument(createNavCom2(x, y));
1079 panel->addInstrument(createADFRadio(x, y));
1080 y -= (int)(SIX_W * 0.375);
1081 panel->addInstrument(createAP(x, y));
1086 // end of sp_panel.cxx