1
0
Fork 0
fgdata/Nasal/canvas/map/navdisplay.styles
2014-06-20 21:52:35 +02:00

619 lines
22 KiB
Text

# ==============================================================================
# Boeing Navigation Display by Gijs de Rooy
# See: http://wiki.flightgear.org/Canvas_ND_Framework
# ==============================================================================
# This file makes use of the MapStructure framework, see: http://wiki.flightgear.org/Canvas_MapStructure
#
# Sooner or later, some parts will be revamped by coming up with a simple animation framework: http://wiki.flightgear.org/NavDisplay#mapping_vs._SVG_animation
##
# pseudo DSL-ish: use these as placeholders in the config hash below
var ALWAYS = func 1;
var NOTHING = func nil;
##
# TODO: move ND-specific implementation details into this lookup hash
# so that other aircraft and ND types can be more easily supported
#
# any aircraft-specific ND behavior should be wrapped here,
# to isolate/decouple things in the generic NavDisplay class
#
# TODO: move this to an XML config file (maybe supporting SGCondition and/or SGStateMachine markup for the logic?)
#
var NDStyles = {
##
# this configures the 744 ND to help generalize the NavDisplay class itself
'Boeing': {
font_mapper: func(family, weight) {
if( family == "Liberation Sans" and weight == "normal" )
return "LiberationFonts/LiberationSans-Regular.ttf";
},
# where all the symbols are stored
# TODO: SVG elements should be renamed to use boeing/airbus prefix
# aircraft developers should all be editing the same ND.svg image
# the code can deal with the differences now
svg_filename: "Nasal/canvas/map/Images/boeingND.svg",
##
## this loads and configures existing layers (currently, *.layer files in Nasal/canvas/map)
##
# TODO: phase out isMapStructure flag once map.nas & *.draw files are killed
layers: [
# TODO: take z-indices from *.draw files -- now handled by MapStructure in the addLayer method.
{ name:'FIX', isMapStructure:1, update_on:['toggle_range','toggle_waypoints'],
# FIXME: this is a really ugly place for controller code
predicate: func(nd, layer) {
# print("Running fix layer predicate");
# toggle visibility here
var visible=nd.get_switch('toggle_waypoints') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40);
layer.group.setVisible( nd.get_switch('toggle_waypoints') );
if (visible) {
#print("Updating MapStructure ND layer: FIX");
# (Hopefully) smart update
layer.update();
}
}, # end of layer update predicate
'z-index': 3,
}, # end of FIX layer
# Should redraw every 10 seconds TODO: use new MapStructure/WXR here once that works properly (Gijs should check first!)
{ name:'WXR', isMapStructure:1, update_on:[ {rate_hz: 0.1}, 'toggle_range','toggle_weather','toggle_display_mode'],
predicate: func(nd, layer) {
#print("Running storms predicate");
var visible=nd.get_switch('toggle_weather') and nd.get_switch('toggle_display_mode') != "PLAN";
layer.group.setVisible(visible);
if (visible) {
print("storms update requested! (timer issue when closing the dialog?)");
layer.update();
}
}, # end of layer update predicate
}, # end of storms/WXR layer
{ name:'APS', isMapStructure:1, update_on:['toggle_display_mode'],
predicate: func(nd, layer) {
var visible = nd.get_switch('toggle_display_mode') == "PLAN";
layer.group.setVisible( visible );
if (visible) {
layer.update();
}
},
},
{ name:'APT', isMapStructure:1, update_on:['toggle_range','toggle_airports','toggle_display_mode'],
predicate: func(nd, layer) {
# toggle visibility here
var visible=nd.get_switch('toggle_airports') and nd.in_mode('toggle_display_mode', ['MAP']);
layer.group.setVisible( visible );
if (visible) {
#print("Updating MapStructure ND layer: APT");
layer.update();
}
}, # end of layer update predicate
'z-index': 1,
}, # end of APT layer
# Should distinct between low and high altitude navaids. Hiding above 40 NM for now, to prevent clutter/lag.
{ name:'VOR', isMapStructure:1, update_on:['toggle_range','toggle_stations','toggle_display_mode'],
# FIXME: this is a really ugly place for controller code
predicate: func(nd, layer) {
# toggle visibility here
var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40);
layer.group.setVisible( visible );
if (visible) {
#print("Updating MapStructure ND layer: VOR");
layer.update();
}
}, # end of layer update predicate
'z-index': 3,
}, # end of VOR layer
{ name:'DME', isMapStructure:1, update_on:['toggle_range','toggle_stations'],
# FIXME: this is a really ugly place for controller code
predicate: func(nd, layer) {
var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40);
# toggle visibility here
layer.group.setVisible( visible );
if (visible) {
#print("Updating MapStructure ND layer: DME");
layer.update();
}
}, # end of layer update predicate
'z-index': 3,
}, # end of DME layer
{ name:'TFC', isMapStructure:1, update_on:['toggle_range','toggle_traffic'],
predicate: func(nd, layer) {
var visible = nd.get_switch('toggle_traffic');
layer.group.setVisible( visible );
if (visible) {
#print("Updating MapStructure ND layer: TFC");
layer.update();
}
}, # end of layer update predicate
'z-index': 1,
}, # end of traffic layer
{ name:'runway-nd', update_on:['toggle_range','toggle_display_mode'],
predicate: func(nd, layer) {
#print("runway-nd wants to be ported to MapStructure");
var visible = (nd.rangeNm() <= 40) and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP','PLAN']) ;
if (visible)
layer._model.init(); # clear & redraw
layer._view.setVisible( visible );
}, # end of layer update predicate
}, # end of airports-nd layer
{ name:'RTE', isMapStructure:1, update_on:['toggle_range','toggle_display_mode'],
predicate: func(nd, layer) {
var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN']));
layer.group.setVisible( visible );
if (visible)
layer.update();
}, # end of layer update predicate
'z-index': 2, # apparently route.draw doesn't have a z-index?
}, # end of route layer
{ name:'WPT', isMapStructure:1, update_on:['toggle_range','toggle_display_mode'],
predicate: func(nd, layer) {
var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN']));
layer.group.setVisible( visible );
if (visible)
layer.update();
}, # end of layer update predicate
'z-index': 4,
}, # end of waypoint layer
{ name:'ALT-profile', isMapStructure:1, update_on:['toggle_range','toggle_display_mode'],
predicate: func(nd, layer) {
var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN']));
layer.group.setVisible( visible );
if (visible)
layer.update();
}, # end of layer update predicate
'z-index': 4,
}, # end of altitude profile layer
## add other layers here, layer names must match the registered names as used in *.layer files for now
## this will all change once we're using Philosopher's MapStructure framework
], # end of vector with configured layers
# This is where SVG elements are configured by providing "behavior" hashes, i.e. for animations
# to animate each SVG symbol, specify behavior via callbacks (predicate, and true/false implementation)
# SVG identifier, callback etc
# TODO: update_on([]), update_mode (update() vs. timers/listeners)
# TODO: support putting symbols on specific layers
features: [
{
# TODO: taOnly doesn't need to use getprop polling in update(), use a listener instead!
id: 'taOnly', # the SVG ID
impl: { # implementation hash
init: func(nd, symbol), # for updateCenter stuff, called during initialization in the ctor
predicate: func(nd) getprop("instrumentation/tcas/inputs/mode") == 2, # the condition
is_true: func(nd) nd.symbols.taOnly.show(), # if true, run this
is_false: func(nd) nd.symbols.taOnly.hide(), # if false, run this
}, # end of taOnly behavior/callbacks
}, # end of taOnly
{
id: 'tas',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.aircraft_source.get_spd() > 100,
is_true: func(nd) {
nd.symbols.tas.setText(sprintf("%3.0f",nd.aircraft_source.get_spd() ));
nd.symbols.tas.show();
},
is_false: func(nd) nd.symbols.tas.hide(),
},
},
{
id: 'tasLbl',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.aircraft_source.get_spd() > 100,
is_true: func(nd) nd.symbols.tasLbl.show(),
is_false: func(nd) nd.symbols.tasLbl.hide(),
},
},
{
id: 'ilsFreq',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']),
is_true: func(nd) {
nd.symbols.ilsFreq.show();
if(getprop("instrumentation/nav/in-range"))
nd.symbols.ilsFreq.setText(getprop("instrumentation/nav/nav-id"));
else
nd.symbols.ilsFreq.setText(getprop("instrumentation/nav/frequencies/selected-mhz-fmt"));
},
is_false: func(nd) nd.symbols.ilsFreq.hide(),
},
},
{
id: 'ilsLbl',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']),
is_true: func(nd) {
nd.symbols.ilsLbl.show();
},
is_false: func(nd) nd.symbols.ilsLbl.hide(),
},
},
{
id: 'wpActiveId',
impl: {
init: func(nd,symbol),
predicate: func(nd) getprop("/autopilot/route-manager/wp/id") != nil and getprop("autopilot/route-manager/active")
and nd.in_mode('toggle_display_mode', ['MAP','PLAN']),
is_true: func(nd) {
nd.symbols.wpActiveId.setText(getprop("/autopilot/route-manager/wp/id"));
nd.symbols.wpActiveId.show();
},
is_false: func(nd) nd.symbols.wpActiveId.hide(),
}, # of wpActiveId.impl
}, # of wpActiveId
{
id: 'wpActiveDist',
impl: {
init: func(nd,symbol),
predicate: func(nd) getprop("/autopilot/route-manager/wp/dist") != nil and getprop("autopilot/route-manager/active")
and nd.in_mode('toggle_display_mode', ['MAP','PLAN']),
is_true: func(nd) {
nd.symbols.wpActiveDist.setText(sprintf("%3.01f",getprop("/autopilot/route-manager/wp/dist")));
nd.symbols.wpActiveDist.show();
},
is_false: func(nd) nd.symbols.wpActiveDist.hide(),
},
},
{
id: 'wpActiveDistLbl',
impl: {
init: func(nd,symbol),
predicate: func(nd) getprop("/autopilot/route-manager/wp/dist") != nil and getprop("autopilot/route-manager/active")
and nd.in_mode('toggle_display_mode', ['MAP','PLAN']),
is_true: func(nd) {
nd.symbols.wpActiveDistLbl.show();
if(getprop("/autopilot/route-manager/wp/dist") > 1000)
nd.symbols.wpActiveDistLbl.setText(" NM");
},
is_false: func(nd) nd.symbols.wpActiveDistLbl.hide(),
},
},
{
id: 'eta',
impl: {
init: func(nd,symbol),
predicate: func(nd) getprop("autopilot/route-manager/wp/eta") != nil and getprop("autopilot/route-manager/active")
and nd.in_mode('toggle_display_mode', ['MAP','PLAN']),
is_true: func(nd) {
var etaSec = getprop("/sim/time/utc/day-seconds")+getprop("autopilot/route-manager/wp/eta-seconds");
var h = math.floor(etaSec/3600);
etaSec=etaSec-3600*h;
var m = math.floor(etaSec/60);
etaSec=etaSec-60*m;
var s = etaSec/10;
if (h>24) h=h-24;
nd.symbols.eta.setText(sprintf("%02.0f%02.0f.%01.0fz",h,m,s));
nd.symbols.eta.show();
},
is_false: func(nd) nd.symbols.eta.hide(),
}, # of eta.impl
}, # of eta
{
id: 'gsGroup',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']),
is_true: func(nd) {
if(nd.get_switch('toggle_centered'))
nd.symbols.gsGroup.setTranslation(0,0);
else
nd.symbols.gsGroup.setTranslation(0,150);
nd.symbols.gsGroup.show();
},
is_false: func(nd) nd.symbols.gsGroup.hide(),
},
},
{
id:'hdg',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','MAP','VOR']),
is_true: func(nd) {
var hdgText = "";
if((nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_display_type') == "CRT")
or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD"))
{
if(nd.get_switch('toggle_true_north'))
hdgText = nd.aircraft_source.get_trk_tru();
else
hdgText = nd.aircraft_source.get_trk_mag();
} else {
if(nd.get_switch('toggle_true_north'))
hdgText = nd.aircraft_source.get_hdg_tru();
else
hdgText = nd.aircraft_source.get_hdg_mag();
}
if(hdgText < 0.5) hdgText = 360 + hdgText;
elsif(hdgText >= 360.5) hdgText = hdgText - 360;
nd.symbols.hdg.setText(sprintf("%03.0f", hdgText));
},
is_false: NOTHING,
},
},
{
id:'hdgGroup',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','MAP','VOR']),
is_true: func(nd) {
nd.symbols.hdgGroup.show();
if(nd.get_switch('toggle_centered'))
nd.symbols.hdgGroup.setTranslation(0,100);
else
nd.symbols.hdgGroup.setTranslation(0,0);
},
is_false: func(nd) nd.symbols.hdgGroup.hide(),
},
},
{
id:'gs',
impl: {
init: func(nd,symbol),
common: func(nd) nd.symbols.gs.setText(sprintf("%3.0f",nd.aircraft_source.get_gnd_spd() )),
predicate: func(nd) nd.aircraft_source.get_gnd_spd() >= 30,
is_true: func(nd) {
nd.symbols.gs.setFontSize(36);
},
is_false: func(nd) nd.symbols.gs.setFontSize(52),
},
},
{
id:'rangeArcs',
impl: {
init: func(nd,symbol),
predicate: func(nd) !nd.get_switch('toggle_centered') and nd.get_switch('toggle_rangearc'),
is_true: func(nd) nd.symbols.rangeArcs.show(),
is_false: func(nd) nd.symbols.rangeArcs.hide(),
}, # of rangeArcs.impl
}, # of rangeArcs
{
id:'rangePln1',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN",
is_true: func(nd) {
nd.symbols.rangePln1.show();
nd.symbols.rangePln1.setText(sprintf("%3.0f",nd.rangeNm()));
},
is_false: func(nd) nd.symbols.rangePln1.hide(),
},
},
{
id:'rangePln2',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN",
is_true: func(nd) {
nd.symbols.rangePln2.show();
nd.symbols.rangePln2.setText(sprintf("%3.0f",nd.rangeNm()/2));
},
is_false: func(nd) nd.symbols.rangePln2.hide(),
},
},
{
id:'rangePln3',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN",
is_true: func(nd) {
nd.symbols.rangePln3.show();
nd.symbols.rangePln3.setText(sprintf("%3.0f",nd.rangeNm()/2));
},
is_false: func(nd) nd.symbols.rangePln3.hide(),
},
},
{
id:'rangePln4',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN",
is_true: func(nd) {
nd.symbols.rangePln4.show();
nd.symbols.rangePln4.setText(sprintf("%3.0f",nd.rangeNm()));
},
is_false: func(nd) nd.symbols.rangePln4.hide(),
},
},
{
id:'crsLbl',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']),
is_true: func(nd) nd.symbols.crsLbl.show(),
is_false: func(nd) nd.symbols.crsLbl.hide(),
},
},
{
id:'crs',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']),
is_true: func(nd) {
nd.symbols.crs.show();
if(getprop("instrumentation/nav/radials/selected-deg") != nil)
nd.symbols.crs.setText(sprintf("%03.0f",getprop("instrumentation/nav/radials/selected-deg")));
},
is_false: func(nd) nd.symbols.crs.hide(),
},
},
{
id:'dmeLbl',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']),
is_true: func(nd) nd.symbols.dmeLbl.show(),
is_false: func(nd) nd.symbols.dmeLbl.hide(),
},
},
{
id:'dme',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']),
is_true: func(nd) {
nd.symbols.dme.show();
if(getprop("instrumentation/dme/in-range"))
nd.symbols.dme.setText(sprintf("%3.1f",getprop("instrumentation/dme/indicated-distance-nm")));
},
is_false: func(nd) nd.symbols.dme.hide(),
},
},
{
id:'trkInd2',
impl: {
init: func(nd,symbol),
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['MAP','APP','VOR']) and nd.get_switch('toggle_centered')),
is_true: func(nd) {
nd.symbols.trkInd2.show();
},
is_false: func(nd) nd.symbols.trkInd2.hide(),
},
},
{
id:'vorCrsPtr',
impl: {
init: func(nd,symbol),
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered')),
is_true: func(nd) {
nd.symbols.vorCrsPtr.show();
if((nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_display_type') == "CRT")
or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD"))
nd.symbols.vorCrsPtr.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_trk_mag())*D2R);
else
nd.symbols.vorCrsPtr.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_hdg_mag())*D2R);
},
is_false: func(nd) nd.symbols.vorCrsPtr.hide(),
},
},
{
id:'vorCrsPtr2',
impl: {
init: func(nd,symbol),
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_centered')),
is_true: func(nd) {
nd.symbols.vorCrsPtr2.show();
if((nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_display_type') == "CRT")
or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD"))
nd.symbols.vorCrsPtr2.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_trk_mag())*D2R);
else
nd.symbols.vorCrsPtr2.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_hdg_mag())*D2R);
},
is_false: func(nd) nd.symbols.vorCrsPtr2.hide(),
},
},
{
id: 'gsDiamond',
impl: {
init: func(nd,symbol),
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']) and getprop("instrumentation/nav/gs-in-range"),
is_true: func(nd) {
var gs_deflection = getprop("instrumentation/nav/gs-needle-deflection-norm");
if(gs_deflection != nil)
nd.symbols.gsDiamond.setTranslation(gs_deflection*150,0);
if(abs(gs_deflection) < 0.99)
nd.symbols.gsDiamond.setColorFill(1,0,1,1);
else
nd.symbols.gsDiamond.setColorFill(0,0,0,1);
},
is_false: func(nd) nd.symbols.gsGroup.hide(),
},
},
{
id:'locPtr',
impl: {
init: func(nd,symbol),
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered') and getprop("instrumentation/nav/in-range")),
is_true: func(nd) {
nd.symbols.locPtr.show();
var deflection = getprop("instrumentation/nav/heading-needle-deflection-norm");
nd.symbols.locPtr.setTranslation(deflection*150,0);
if(abs(deflection) < 0.99)
nd.symbols.locPtr.setColorFill(1,0,1,1);
else
nd.symbols.locPtr.setColorFill(0,0,0,1);
},
is_false: func(nd) nd.symbols.locPtr.hide(),
},
},
{
id:'locPtr2',
impl: {
init: func(nd,symbol),
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_centered') and getprop("instrumentation/nav/in-range")),
is_true: func(nd) {
nd.symbols.locPtr2.show();
var deflection = getprop("instrumentation/nav/heading-needle-deflection-norm");
nd.symbols.locPtr2.setTranslation(deflection*150,0);
if(abs(deflection) < 0.99)
nd.symbols.locPtr2.setColorFill(1,0,1,1);
else
nd.symbols.locPtr2.setColorFill(0,0,0,1);
},
is_false: func(nd) nd.symbols.locPtr2.hide(),
},
},
{
id:'wind',
impl: {
init: func(nd,symbol),
predicate: ALWAYS,
is_true: func(nd) {
var windDir = getprop("environment/wind-from-heading-deg");
if(!nd.get_switch('toggle_true_north'))
windDir = windDir - getprop("environment/magnetic-variation-deg");
if(windDir < 0.5) windDir = 360 + windDir;
elsif(windDir >= 360.5) windDir = windDir - 360;
nd.symbols.wind.setText(sprintf("%03.0f / %02.0f",windDir,getprop("environment/wind-speed-kt")));
},
is_false: NOTHING,
},
},
{
id:'windArrow',
impl: {
init: func(nd,symbol),
predicate: func(nd) (!(nd.in_mode('toggle_display_mode', ['PLAN']) and (nd.get_switch('toggle_display_type') == "LCD")) and nd.aircraft_source.get_spd() > 100),
is_true: func(nd) {
nd.symbols.windArrow.show();
var windArrowRot = getprop("environment/wind-from-heading-deg");
if((nd.in_mode('toggle_display_mode', ['MAP','PLAN']) and nd.get_switch('toggle_display_type') == "CRT")
or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD"))
windArrowRot = windArrowRot - nd.aircraft_source.get_trk_mag();
else
windArrowRot = windArrowRot - nd.aircraft_source.get_hdg_mag();
nd.symbols.windArrow.setRotation(windArrowRot*D2R);
},
is_false: func(nd) nd.symbols.windArrow.hide(),
},
},
], # end of vector with features
}, # end of Boeing style
#####
##
## add support for other aircraft/ND types and styles here (Airbus etc)
## or move to other files.
##
## see: http://wiki.flightgear.org/NavDisplay#Custom_ND_Styles
## and: http://wiki.flightgear.org/NavDisplay#Adding_new_features
}; # end of NDStyles