diff --git a/Aircraft/Instruments-3d/radardist/radardist.nas b/Aircraft/Instruments-3d/radardist/radardist.nas
index 6e2e0b5be..910f2e307 100644
--- a/Aircraft/Instruments-3d/radardist/radardist.nas
+++ b/Aircraft/Instruments-3d/radardist/radardist.nas
@@ -45,159 +45,159 @@ var FT2M = 0.3048;
var NM2KM = 1.852;
var my_maxrange = func(a) {
- max_range = 0;
- radar_range = 0;
- radar_area = 0;
- acname = aircraftData[a] or 0;
- if ( acname ) {
- have_radar = radarData[acname][4];
- if ( have_radar != "none" and have_radar != "unknown") {
- radar_area = radarData[acname][7];
- radar_range = radarData[acname][5];
- if ( radar_area > 0 ) { max_range = radar_range / radar_area }
- }
- }
- return( max_range );
+ max_range = 0;
+ radar_range = 0;
+ radar_area = 0;
+ acname = aircraftData[a] or 0;
+ if ( acname ) {
+ have_radar = radarData[acname][4];
+ if ( have_radar != "none" and have_radar != "unknown") {
+ radar_area = radarData[acname][7];
+ radar_range = radarData[acname][5];
+ if ( radar_area > 0 ) { max_range = radar_range / radar_area }
+ }
+ }
+ return( max_range );
}
var get_ecm_type_num = func(a) {
- acname = aircraftData[a] or 0;
- var num = 0;
- if ( acname ) {
- num = radarData[acname][8];
- }
- return( num );
+ acname = aircraftData[a] or 0;
+ var num = 0;
+ if ( acname ) {
+ num = radarData[acname][8];
+ }
+ return( num );
}
var get_aircraft_name = func( t ) {
- # Get the multiplayer aircraft name.
- mpnode_string = t;
- mpnode = props.globals.getNode(mpnode_string);
- if ( find("tanker", mpnode_string) > 0 ) {
- cutname = "KC135";
- } else {
- mpname_node_string = mpnode_string ~ "/sim/model/path";
- mpname_node = props.globals.getNode(mpname_node_string);
- if (mpname_node == nil) { return(0) }
+ # Get the multiplayer aircraft name.
+ mpnode_string = t;
+ mpnode = props.globals.getNode(mpnode_string);
+ if ( find("tanker", mpnode_string) > 0 ) {
+ cutname = "KC135";
+ } else {
+ mpname_node_string = mpnode_string ~ "/sim/model/path";
+ mpname_node = props.globals.getNode(mpname_node_string);
+ if (mpname_node == nil) { return(0) }
- var mpname = mpname_node.getValue();
- if (mpname == nil) { return(0) }
+ var mpname = mpname_node.getValue();
+ if (mpname == nil) { return(0) }
- splitname = split("/", mpname);
- cutname = splitname[1];
-
- }
- return( cutname );
+ splitname = split("/", mpname);
+ #
+ # cutname = splitname[1];
+ #
+ # **** by 5H1N0B1 05/01/2014
+ # Fixed a problem onboard radar happens automatically when you are in range of an mp gamer that uses "OpenRadar"
+ #
+ cutname = splitname[size(splitname)-1];
+
+ }
+ return( cutname );
}
var radis = func(t, my_radarcorr) {
- cutname = get_aircraft_name(t);
- # Calculate the rcs detection range,
- # if aircraft is not found in list, 0 (generic) will be used.
- acname = aircraftData[cutname];
- if ( acname == nil ) { acname = 0 }
- rcs_4r = radarData[acname][3];
+ cutname = get_aircraft_name(t);
+ # Calculate the rcs detection range,
+ # if aircraft is not found in list, 0 (generic) will be used.
+ acname = aircraftData[cutname];
+ if ( acname == nil ) { acname = 0 }
+ rcs_4r = radarData[acname][3];
- # Add a correction factor for altitude, as lower alt means
- # shorter radar distance (due to air turbulence).
- alt_corr = 1;
- alt_ac = mpnode.getNode("position/altitude-ft").getValue();
- if (alt_ac <= 1000) {
- alt_corr = 0.6;
- } elsif ((alt_ac > 1000) and (alt_ac <= 5000)) {
- alt_corr = 0.8;
- }
- # Add a correction factor for altitude AGL. Skip if AI tanker.
- agl_corr = 1;
- if ( find("tanker", t) == 0 ) {
- mp_lon = mpnode.getNode("position/longitude-deg").getValue();
- pos_elev = geo.elevation(mp_lat, mp_lon);
- if (pos_elev != nil) {
- mp_agl = alt_ac - ( pos_elev / FT2M );
- if (mp_agl <= 40) {
- agl_corr = 0.03;
- } elsif ((mp_agl > 40) and (mp_agl <= 80)) {
- agl_corr = 0.07;
- } elsif ((mp_agl > 80) and (mp_agl <= 120)) {
- agl_corr = 0.25;
- } elsif ((mp_agl > 120) and (mp_agl <= 300)) {
- agl_corr = 0.4;
- } elsif ((mp_agl > 300) and (mp_agl <= 600)) {
- agl_corr = 0.7;
- } elsif ((mp_agl > 600) and (mp_agl <= 1000)) {
- agl_corr = 0.85;
- }
- }
- }
- # Calculate the detection distance for this multiplayer.
- det_range = my_radarcorr * rcs_4r * alt_corr * agl_corr / NM2KM;
+ # Add a correction factor for altitude, as lower alt means
+ # shorter radar distance (due to air turbulence).
+ alt_corr = 1;
+ alt_ac = mpnode.getNode("position/altitude-ft").getValue();
+ if (alt_ac <= 1000) {
+ alt_corr = 0.6;
+ } elsif ((alt_ac > 1000) and (alt_ac <= 5000)) {
+ alt_corr = 0.8;
+ }
+ # Add a correction factor for altitude AGL. Skip if AI tanker.
+ agl_corr = 1;
+ if ( find("tanker", t) == 0 ) {
+ mp_lon = mpnode.getNode("position/longitude-deg").getValue();
+ pos_elev = geo.elevation(mp_lat, mp_lon);
+ if (pos_elev != nil) {
+ mp_agl = alt_ac - ( pos_elev / FT2M );
+ if (mp_agl <= 40) {
+ agl_corr = 0.03;
+ } elsif ((mp_agl > 40) and (mp_agl <= 80)) {
+ agl_corr = 0.07;
+ } elsif ((mp_agl > 80) and (mp_agl <= 120)) {
+ agl_corr = 0.25;
+ } elsif ((mp_agl > 120) and (mp_agl <= 300)) {
+ agl_corr = 0.4;
+ } elsif ((mp_agl > 300) and (mp_agl <= 600)) {
+ agl_corr = 0.7;
+ } elsif ((mp_agl > 600) and (mp_agl <= 1000)) {
+ agl_corr = 0.85;
+ }
+ }
+ }
+ # Calculate the detection distance for this multiplayer.
+ det_range = my_radarcorr * rcs_4r * alt_corr * agl_corr / NM2KM;
- # Compare if aircraft is in detection range and return.
- act_range = mpnode.getNode("radar/range-nm").getValue() or 500;
- if (det_range >= act_range) {
- return(1);
- }
- return(0);
+ # Compare if aircraft is in detection range and return.
+ act_range = mpnode.getNode("radar/range-nm").getValue() or 500;
+ if (det_range >= act_range) {
+ return(1);
+ }
+ return(0);
}
var radar_horizon = func(our_alt_ft, target_alt_ft) {
- if (our_alt_ft < 0 or our_alt_ft == nil) { our_alt_ft = 0 }
- if (target_alt_ft < 0 or target_alt_ft == nil) { target_alt_ft = 0 }
- return( 2.2 * ( math.sqrt(our_alt_ft * FT2M) + math.sqrt(target_alt_ft * FT2M) ) );
+ if (our_alt_ft < 0 or our_alt_ft == nil) { our_alt_ft = 0 }
+ if (target_alt_ft < 0 or target_alt_ft == nil) { target_alt_ft = 0 }
+ return( 2.2 * ( math.sqrt(our_alt_ft * FT2M) + math.sqrt(target_alt_ft * FT2M) ) );
}
var load_data = func {
- # a) converts aircraft model name to lookup (index) number in aircraftData{}.
- # b) appends ordered list of data into radarData[],
- # data is:
- # - acname (the index number)
- # - the first (if several) aircraft model name corresponding to this type,
- # - RCS(m2),
- # - 4th root of RCS,
- # - radar type,
- # - max. radar range(km),
- # - max. radar range target seize(RCS)m2,
- # - 4th root of radar RCS.
- var data_node = props.globals.getNode("instrumentation/radar-performance/data");
- var aircraft_types = data_node.getChildren();
- foreach( var t; aircraft_types ) {
- var index = t.getIndex();
- var aircraft_names = t.getChildren();
- foreach( var n; aircraft_names) {
- if ( n.getName() == "name") {
- aircraftData[n.getValue()] = index;
- }
- }
- var t_list = [
- index,
- t.getNode("name[0]").getValue(),
- t.getNode("rcs-sq-meter").getValue(),
- t.getNode("rcs-4th-root").getValue(),
- t.getNode("radar-type").getValue(),
- t.getNode("max-radar-rng-km").getValue(),
- t.getNode("max-target-sq-meter").getValue(),
- t.getNode("max-target-4th-root").getValue(),
- t.getNode("ecm-type-num").getValue()
- ];
- append(radarData, t_list);
- }
+ # a) converts aircraft model name to lookup (index) number in aircraftData{}.
+ # b) appends ordered list of data into radarData[],
+ # data is:
+ # - acname (the index number)
+ # - the first (if several) aircraft model name corresponding to this type,
+ # - RCS(m2),
+ # - 4th root of RCS,
+ # - radar type,
+ # - max. radar range(km),
+ # - max. radar range target seize(RCS)m2,
+ # - 4th root of radar RCS.
+ var data_node = props.globals.getNode("instrumentation/radar-performance/data");
+ var aircraft_types = data_node.getChildren();
+ foreach( var t; aircraft_types ) {
+ var index = t.getIndex();
+ var aircraft_names = t.getChildren();
+ foreach( var n; aircraft_names) {
+ if ( n.getName() == "name") {
+ aircraftData[n.getValue()] = index;
+ }
+ }
+ var t_list = [
+ index,
+ t.getNode("name[0]").getValue(),
+ t.getNode("rcs-sq-meter").getValue(),
+ t.getNode("rcs-4th-root").getValue(),
+ t.getNode("radar-type").getValue(),
+ t.getNode("max-radar-rng-km").getValue(),
+ t.getNode("max-target-sq-meter").getValue(),
+ t.getNode("max-target-4th-root").getValue(),
+ t.getNode("ecm-type-num").getValue()
+ ];
+ append(radarData, t_list);
+ }
}
var launched = 0;
var init = func {
- if (! launched) {
- print("Initializing Radar Data");
- io.read_properties(data_path, props.globals);
- load_data();
- launched = 1;
- }
+ if (! launched) {
+ print("Initializing Radar Data");
+ io.read_properties(data_path, props.globals);
+ load_data();
+ launched = 1;
+ }
}
-
-
-
-
-
-
diff --git a/Input/Joysticks/Genius/f31.xml b/Input/Joysticks/Genius/f31.xml
index 783594e88..30f3862f1 100644
--- a/Input/Joysticks/Genius/f31.xml
+++ b/Input/Joysticks/Genius/f31.xml
@@ -1,28 +1,36 @@
-
+
Padix Co. Ltd. 10-Button USB Joystick
@@ -44,7 +52,7 @@ Buttons:
property-scale
/controls/flight/elevator
2
- -1.0
+ -1.0
@@ -58,7 +66,7 @@ Buttons:
property-scale
/controls/flight/rudder
2
- -1.0
+ -1.0
@@ -75,7 +83,7 @@ Buttons:
- View Direction
+ View direction
4
6
@@ -85,7 +93,7 @@ Buttons:
true
+ 3.0
@@ -145,28 +153,37 @@ Buttons:
property-adjust
/sim/current-view/goal-pitch-offset-deg
- -3.0
+ -3.0
-
-
-
+
+
+
diff --git a/Input/Joysticks/Logitech/g940.xml b/Input/Joysticks/Logitech/g940.xml
new file mode 100644
index 000000000..a29bc2409
--- /dev/null
+++ b/Input/Joysticks/Logitech/g940.xml
@@ -0,0 +1,661 @@
+
+
+
+
+ G940
+
+
+
+
+ Aileron
+
+ property-scale
+ /controls/flight/aileron
+
+
+
+
+ Elevator
+
+ property-scale
+ /controls/flight/elevator
+ -1.0
+
+
+
+
+ Aileron trim
+
+ property-scale
+ /controls/flight/aileron-trim
+
+
+
+
+
+
+ View direction
+
+ 12
+
+
+ View left
+ true
+
+
+ nasal
+
+
+
+
+ View right
+ true
+
+
+ nasal
+
+
+
+
+
+
+ View elevation
+
+ 13
+
+
+ View down
+ true
+
+ property-adjust
+ /sim/current-view/goal-pitch-offset-deg
+ 3.0
+
+
+
+ View up
+ true
+
+ property-adjust
+ /sim/current-view/goal-pitch-offset-deg
+ -3.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Throttle right engine
+
+ 6
+
+
+
+ nasal
+
+
+
+
+
+
+
+ Throttle left engine
+
+ 7
+
+
+ property-scale
+ /controls/engines/engine[0]/throttle
+ -1.0
+ -0.5
+
+
+
+
+ Mixture
+
+ 9
+
+
+ property-scale
+ /controls/engines/engine/mixture
+ -1.0
+ -0.5
+
+
+
+
+ Propeller pitch
+
+ 8
+
+
+ property-scale
+ /controls/engines/engine/propeller-pitch
+ -1.0
+ -0.5
+
+
+
+
+ View zoom
+
+ 14
+
+
+ Zoom out
+ true
+
+ nasal
+
+
+
+
+ Zoom in
+ true
+
+ nasal
+
+
+
+
+
+
+ Reset zoom
+
+ 15
+
+
+ Reset zoom to default
+ false
+
+ property-assign
+ /sim/current-view/field-of-view
+ /sim/view/config/default-field-of-view-deg
+
+
+
+ Reset zoom for greater overview
+ false
+
+ nasal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Rudder
+
+ 2
+
+
+ property-scale
+ /controls/flight/rudder
+ 2
+
+
+
+
+
+ 4
+
+ Left differential break
+
+ property-scale
+ /controls/gear/brake-left
+ -1.0
+ -0.5
+
+
+
+
+
+ 3
+
+ Right differential break
+
+ property-scale
+ /controls/gear/brake-right
+ -1.0
+ -0.5
+
+
+
+
+
diff --git a/Nasal/canvas/MapStructure.nas b/Nasal/canvas/MapStructure.nas
index 5474e9145..2ec29eb80 100644
--- a/Nasal/canvas/MapStructure.nas
+++ b/Nasal/canvas/MapStructure.nas
@@ -1,3 +1,123 @@
+var dump_obj = func(m) {
+ var h = {};
+ foreach (var k; keys(m))
+ if (k != "parents")
+ h[k] = m[k];
+ debug.dump(h);
+};
+
+##
+# must be either of:
+# 1) draw* callback, 2) SVG filename, 3) Drawable class (with styling/LOD support)
+var SymbolDrawable = {
+ new: func() {
+ },
+};
+
+## wrapper for each element
+## i.e. keeps the canvas and texture map coordinates
+var CachedElement = {
+ new: func(canvas_path, name, source, offset) {
+ var m = {parents:[CachedElement] };
+ m.canvas_src = canvas_path;
+ m.name = name;
+ m.source = source;
+ m.offset = offset;
+ return m;
+ }, # new()
+ render: func(group) {
+ # create a raster image child in the render target/group
+ return
+ group.createChild("image", me.name)
+ .setFile( me.canvas_src )
+ # TODO: fix .setSourceRect() to accept a single vector for coordinates ...
+ .setSourceRect(left:me.source[0],top:me.source[1],right:me.source[2],bottom:me.source[3] , normalized:0)
+ .setTranslation(me.offset); # FIXME: make sure this stays like this and isn't overridden
+ }, # render()
+}; # of CachedElement
+
+var SymbolCache = {
+ # We can draw symbols either with left/top, centered,
+ # or right/bottom alignment. Specify two in a vector
+ # to mix and match, e.g. left/centered would be
+ # [SymbolCache.DRAW_LEFT_TOP,SymbolCache.DRAW_CENTERED]
+ DRAW_LEFT_TOP: 0.0,
+ DRAW_CENTERED: 0.5,
+ DRAW_RIGHT_BOTTOM: 1.0,
+ new: func(dim...) {
+ var m = { parents:[SymbolCache] };
+ # to keep track of the next free caching spot (in px)
+ m.next_free = [0, 0];
+ # to store each type of symbol
+ m.dict = {};
+ if (size(dim) == 1 and typeof(dim[0]) == 'vector')
+ dim = dim[0];
+ # Two sizes: canvas and symbol
+ if (size(dim) == 2) {
+ var canvas_x = var canvas_y = dim[0];
+ var image_x = var image_y = dim[1];
+ # Two widths (canvas and symbol) and then height/width ratio
+ } else if (size(dim) == 3) {
+ var (canvas_x,image_x,ratio) = dim;
+ var canvas_y = canvas_x * ratio;
+ var image_y = image_x * ratio;
+ # Explicit canvas and symbol widths/heights
+ } else if (size(dim) == 4) {
+ var (canvas_x,canvas_y,image_x,image_y) = dim;
+ }
+ m.canvas_sz = [canvas_x, canvas_y];
+ m.image_sz = [image_x, image_y];
+
+ # allocate a canvas
+ m.canvas_texture = canvas.new( {
+ "name": "SymbolCache"~canvas_x~'x'~canvas_y,
+ "size": m.canvas_sz,
+ "view": m.canvas_sz,
+ "mipmapping": 1
+ });
+
+ # add a placement
+ m.canvas_texture.addPlacement( {"type": "ref"} );
+
+ return m;
+ },
+ add: func(name, callback, draw_mode=0) {
+ if (typeof(draw_mode) == 'scalar')
+ var draw_mode0 = var draw_mode1 = draw_mode;
+ else var (draw_mode0,draw_mode1) = draw_mode;
+ # get canvas texture that we use as cache
+ # get next free spot in texture (column/row)
+ # run the draw callback and render into a group
+ var gr = me.canvas_texture.createGroup();
+ gr.setTranslation( me.next_free[0] + me.image_sz[0]*draw_mode0,
+ me.next_free[1] + me.image_sz[1]*draw_mode1);
+ #settimer(func debug.dump ( gr.getTransformedBounds() ), 0); # XXX: these are only updated when rendered
+ #debug.dump ( gr.getTransformedBounds() );
+ gr.update(); # apparently this doesn't result in sane output from .getTransformedBounds() either
+ #debug.dump ( gr.getTransformedBounds() );
+ # draw the symbol
+ callback(gr);
+ # get the bounding box, i.e. coordinates for texture map, or use the .setTranslation() params
+ var coords = me.next_free~me.next_free;
+ foreach (var i; [0,1])
+ coords[i+2] += me.image_sz[i];
+ # get the offset we used to position correctly in the bounds of the canvas
+ var offset = [me.image_sz[0]*draw_mode0, me.image_sz[1]*draw_mode1];
+ # store texture map coordinates in lookup map using the name as identifier
+ me.dict[name] = CachedElement.new(me.canvas_texture.getPath(), name, coords, offset );
+ # update next free position in cache (column/row)
+ me.next_free[0] += me.image_sz[0];
+ if (me.next_free[0] >= me.canvas_sz[0])
+ { me.next_free[0] = 0; me.next_free[1] += me.image_sz[1] }
+ if (me.next_free[1] >= me.canvas_sz[1])
+ die("SymbolCache: ran out of space after adding '"~name~"'");
+ }, # add()
+ get: func(name) {
+ if(!contains(me.dict,name)) die("No SymbolCache entry for key:"~ name);
+ return me.dict[name];
+ }, # get()
+};
+
var Symbol = {
# Static/singleton:
registry: {},
@@ -29,7 +149,7 @@ Symbol.Controller = {
# Static/singleton:
registry: {},
add: func(type, class)
- registry[type] = class,
+ me.registry[type] = class,
get: func(type)
if ((var class = me.registry[type]) == nil)
die("unknown type '"~type~"'");
@@ -58,13 +178,17 @@ var getpos_fromghost = func(positioned_g)
# (geo.Coord and positioned ghost currently)
Symbol.Controller.getpos = func(obj) {
if (typeof(obj) == 'ghost')
- if (ghosttype(obj) == 'positioned' or ghosttype(obj) == 'Navaid')
+ if (ghosttype(obj) == 'positioned' or ghosttype(obj) == 'Navaid' or ghosttype(obj)=='Fix' or ghosttype(obj)=='flightplan-leg')
return getpos_fromghost(obj);
else
die("bad ghost of type '"~ghosttype(obj)~"'");
if (typeof(obj) == 'hash')
if (isa(obj, geo.Coord))
return obj.latlon();
+ if (contains(obj,'lat') and contains(obj,'lon'))
+ return [obj.lat, obj.lon];
+
+ debug.dump(obj);
die("no suitable getpos() found! Of type: "~typeof(obj));
};
@@ -82,38 +206,17 @@ var DotSym = {
element_id: nil,
# Static/singleton:
makeinstance: func(name, hash) {
- assert_ms(hash,
- "element_type", # type of Canvas element
- #"element_id", # optional Canvas id
- #"init", # initialize routine
- "draw", # init/update routine
- #getpos", # get position from model in [x_units,y_units] (optional)
- );
- hash.parents = [DotSym];
+ if (!isa(hash, DotSym))
+ die("OOP error");
+ #assert_ms(hash,
+ # "element_type", # type of Canvas element
+ # #"element_id", # optional Canvas id
+ # #"init", # initialize routine
+ # "draw", # init/update routine
+ # #getpos", # get position from model in [x_units,y_units] (optional)
+ #);
return Symbol.add(name, hash);
},
- readinstance: func(file, name=nil) {
- #print(file);
- if (name == nil)
- var name = split("/", file)[-1];
- if (substr(name, size(name)-4) == ".draw")
- name = substr(name, 0, size(name)-5);
- var code = io.readfile(file);
- var code = call(compile, [code], var err=[]);
- if (size(err)) {
- if (substr(err[0], 0, 12) == "Parse error:") { # hack around Nasal feature
- var e = split(" at line ", err[0]);
- if (size(e) == 2)
- err[0] = string.join("", [e[0], "\n at ", file, ", line ", e[1], "\n "]);
- }
- for (var i = 1; (var c = caller(i)) != nil; i += 1)
- err ~= subvec(c, 2, 2);
- debug.printerror(err);
- return;
- }
- call(code, nil, nil, var hash = { parents:[DotSym] });
- me.makeinstance(name, hash);
- },
# For the instances returned from makeinstance:
# @param group The Canvas group to add this to.
# @param model A correct object (e.g. positioned ghost) as
@@ -132,6 +235,10 @@ var DotSym = {
),
};
if (m.controller != nil) {
+ #print("Creating controller");
+ temp = m.controller.new(m.model,m);
+ if (temp != nil)
+ m.controller = temp;
#print("Initializing controller");
m.controller.init(model);
}
@@ -281,93 +388,117 @@ SymbolLayer.Controller = {
die("searchCmd() not implemented for this SymbolLayer.Controller type!"),
}; # of SymbolLayer.Controller
-settimer(func {
-Map.Controller = {
-# Static/singleton:
- registry: {},
- add: func(type, class)
- me.registry[type] = class,
- get: func(type)
- if ((var class = me.registry[type]) == nil)
- die("unknown type '"~type~"'");
- else return class,
- # Calls corresonding controller constructor
- # @param map The #SymbolMap this controller is responsible for.
- new: func(type, layer, arg...)
- return call((var class = me.get(type)).new, [map]~arg, class),
+var AnimatedLayer = {
};
-####### LOAD FILES #######
-#print("loading files");
-(func {
- var FG_ROOT = getprop("/sim/fg-root");
- var load = func(file, name) {
- #print(file);
- if (name == nil)
- var name = split("/", file)[-1];
- if (substr(name, size(name)-4) == ".draw")
- name = substr(name, 0, size(name)-5);
- #print("reading file");
- var code = io.readfile(file);
- #print("compiling file");
- # This segfaults for some reason:
- #var code = call(compile, [code], var err=[]);
- var code = call(func compile(code, file), [code], var err=[]);
- if (size(err)) {
- #print("handling error");
- if (substr(err[0], 0, 12) == "Parse error:") { # hack around Nasal feature
- var e = split(" at line ", err[0]);
- if (size(e) == 2)
- err[0] = string.join("", [e[0], "\n at ", file, ", line ", e[1], "\n "]);
+var CompassLayer = {
+};
+
+var AltitudeArcLayer = {
+};
+
+load_MapStructure = func {
+ Map.Controller = {
+ # Static/singleton:
+ registry: {},
+ add: func(type, class)
+ me.registry[type] = class,
+ get: func(type)
+ if ((var class = me.registry[type]) == nil)
+ die("unknown type '"~type~"'");
+ else return class,
+ # Calls corresonding controller constructor
+ # @param map The #SymbolMap this controller is responsible for.
+ new: func(type, layer, arg...)
+ return call((var class = me.get(type)).new, [map]~arg, class),
+ };
+
+ ####### LOAD FILES #######
+ #print("loading files");
+ (func {
+ var FG_ROOT = getprop("/sim/fg-root");
+ var load = func(file, name) {
+ #print(file);
+ if (name == nil)
+ var name = split("/", file)[-1];
+ if (substr(name, size(name)-4) == ".draw")
+ name = substr(name, 0, size(name)-5);
+ #print("reading file");
+ var code = io.readfile(file);
+ #print("compiling file");
+ # This segfaults for some reason:
+ #var code = call(compile, [code], var err=[]);
+ var code = call(func compile(code, file), [code], var err=[]);
+ if (size(err)) {
+ #print("handling error");
+ if (substr(err[0], 0, 12) == "Parse error:") { # hack around Nasal feature
+ var e = split(" at line ", err[0]);
+ if (size(e) == 2)
+ err[0] = string.join("", [e[0], "\n at ", file, ", line ", e[1], "\n "]);
+ }
+ for (var i = 1; (var c = caller(i)) != nil; i += 1)
+ err ~= subvec(c, 2, 2);
+ debug.printerror(err);
+ return;
}
- for (var i = 1; (var c = caller(i)) != nil; i += 1)
- err ~= subvec(c, 2, 2);
- debug.printerror(err);
- return;
+ #print("calling code");
+ call(code, nil, nil, var hash = {});
+ #debug.dump(keys(hash));
+ return hash;
+ };
+
+ var load_deps = func(name) {
+ load(FG_ROOT~"/Nasal/canvas/map/"~name~".lcontroller", name);
+ load(FG_ROOT~"/Nasal/canvas/map/"~name~".symbol", name);
+ load(FG_ROOT~"/Nasal/canvas/map/"~name~".scontroller", name);
}
- #print("calling code");
- call(code, nil, nil, var hash = {});
- #debug.dump(keys(hash));
- return hash;
- };
- load(FG_ROOT~"/Nasal/canvas/map/VOR.lcontroller", "VOR");
- DotSym.readinstance(FG_ROOT~"/Nasal/canvas/map/VOR.symbol", "VOR");
- load(FG_ROOT~"/Nasal/canvas/map/VOR.scontroller", "VOR");
- load(FG_ROOT~"/Nasal/canvas/map/aircraftpos.controller", "VOR");
-})();
-#print("finished loading files");
-####### TEST SYMBOL #######
-if (0)
-settimer(func {
- if (caller(0)[0] != globals.canvas)
- return call(caller(0)[1], arg, nil, globals.canvas);
+ foreach( var name; ['VOR','FIX','NDB','DME','WPT'] )
+ load_deps( name );
+ load(FG_ROOT~"/Nasal/canvas/map/aircraftpos.controller", name);
- print("Running MapStructure test code");
- var TestCanvas = canvas.new({
- "name": "Map Test",
- "size": [1024, 1024],
- "view": [1024, 1024],
- "mipmapping": 1
- });
- var dlg = canvas.Window.new([400, 400], "dialog");
- dlg.setCanvas(TestCanvas);
- var TestMap = TestCanvas.createGroup().createChild("map"); # we should not directly use a canvas here, but instead a LayeredMap.new()
- TestMap.addLayer(factory: SymbolLayer, type_arg: "VOR"); # the ID should be also exposed in the property tree for each group (layer), i.e. better debugging
- # Center the map's origin:
- TestMap.setTranslation(512,512); # FIXME: don't hardcode these values, but read in canvas texture dimensions, otherwise it will break once someone uses non 1024x1024 textures ...
- # Initialize a range (TODO: LayeredMap.Controller):
- TestMap.set("range", 100);
- # Little cursor of current position:
- TestMap.createChild("path").rect(-5,-5,10,10).setColorFill(1,1,1).setColor(0,1,0);
- # And make it move with our aircraft:
- TestMap.setController("Aircraft position"); # from aircraftpos.controller
- dlg.del = func() {
- TestMap.del();
- # call inherited 'del'
- delete(me, "del");
- me.del();
- };
-}, 1);
-}, 0); # end ugly module init timer hack
+ ###
+ # set up a cache for 32x32 symbols
+ var SymbolCache32x32 = SymbolCache.new(1024,32);
+ var drawVOR = func(color, width=3) return func(group) {
+ # print("drawing vor");
+ var bbox = group.createChild("path")
+ .moveTo(-15,0)
+ .lineTo(-7.5,12.5)
+ .lineTo(7.5,12.5)
+ .lineTo(15,0)
+ .lineTo(7.5,-12.5)
+ .lineTo(-7.5,-12.5)
+ .close()
+ .setStrokeLineWidth(width)
+ .setColor( color );
+ # debug.dump( bbox.getBoundingBox() );
+ };
+
+ var cachedVOR1 = SymbolCache32x32.add( "VOR-BLUE", drawVOR( color:[0, 0.6, 0.85], width:3), SymbolCache.DRAW_CENTERED );
+ var cachedVOR2 = SymbolCache32x32.add( "VOR-RED" , drawVOR( color:[1.0, 0, 0], width: 3), SymbolCache.DRAW_CENTERED );
+ var cachedVOR3 = SymbolCache32x32.add( "VOR-GREEN" , drawVOR( color:[0, 1, 0], width: 3), SymbolCache.DRAW_CENTERED );
+ var cachedVOR4 = SymbolCache32x32.add( "VOR-WHITE" , drawVOR( color:[1, 1, 1], width: 3), SymbolCache.DRAW_CENTERED );
+
+ # STRESS TEST
+ if (0) {
+ for(var i=0;i <= 1024/32*4 - 4; i+=1)
+ SymbolCache32x32.add( "VOR-YELLOW"~i , drawVOR( color:[1, 1, 0], width: 3) );
+
+ var dlg = canvas.Window.new([640,320],"dialog");
+ var my_canvas = dlg.createCanvas().setColorBackground(1,1,1,1);
+ var root = my_canvas.createGroup();
+
+ SymbolCache32x32.get(name:"VOR-BLUE").render( group: root ).setGeoPosition(getprop("/position/latitude-deg"),getprop("/position/longitude-deg"));
+ }
+
+ })();
+ #print("finished loading files");
+ ####### TEST SYMBOL #######
+
+ canvas.load_MapStructure = func;
+
+}; # load_MapStructure
+
+setlistener("/nasal/canvas/loaded", load_MapStructure); # end ugly module init listener hack
diff --git a/Nasal/canvas/api.nas b/Nasal/canvas/api.nas
index 8cccc71f8..bd42c061d 100644
--- a/Nasal/canvas/api.nas
+++ b/Nasal/canvas/api.nas
@@ -462,47 +462,45 @@ var Map = {
addLayer: func(factory, type_arg=nil, priority=nil)
{
if (!contains(me, "layers"))
- me.layers = [];
+ me.layers = {};
+
+ if(contains(me.layers, type_arg))
+ print("addLayer() warning: overwriting existing layer:", type_arg);
+
+ # print("addLayer():", type_arg);
# Argument handling
if (type_arg != nil)
var type = factory.get(type_arg);
else var type = factory;
+ me.layers[type_arg]= type.new(me);
if (priority == nil)
priority = type.df_priority;
- append(me.layers, [type.new(me), priority]);
if (priority != nil)
- me._sort_priority();
+ me.layers[type_arg].setInt("z-index", priority);
return me;
},
- setPos: func(lat,lon,hdg=nil)
+ getLayer: func(type_arg) me.layers[type_arg],
+ setPos: func(lat, lon, hdg=nil, range=nil)
{
me.set("ref-lat", lat);
me.set("ref-lon", lon);
if (hdg != nil)
me.set("hdg", hdg);
-
- # me.map.set("range", 100);
+ if (range != nil)
+ me.set("range", range);
},
# Update each layer on this Map. Called by
# me.controller.
update: func
{
- foreach (var l; me.layers)
- call(l[0].update, arg, l[0]);
+ foreach (var l; keys(me.layers)) {
+ var layer = me.layers[l];
+ call(layer.update, arg, layer);
+ }
return me;
},
-# private:
- _sort_priority: func()
- {
- me.layers = sort(me.layers, me._sort_cmp);
- forindex (var i; me.layers)
- me.layers[i].set("z-index", i);
- },
- _sort_cmp: func(a,b) {
- a[1] != b[1] and a[1] != nil and b[1] != nil and (a[1] < b[1] ? -1 : 1)
- },
};
# Text
diff --git a/Nasal/canvas/map.nas b/Nasal/canvas/map.nas
index 1b91fe76b..187619913 100644
--- a/Nasal/canvas/map.nas
+++ b/Nasal/canvas/map.nas
@@ -429,10 +429,16 @@ var files_with = func(ext) {
}
return results;
}
-foreach(var ext; var extensions = ['.draw','.model','.layer'])
- load_modules(files_with(ext));
+setlistener("/nasal/canvas/loaded", func {
+ foreach(var ext; var extensions = ['.draw','.model','.layer'])
+ load_modules(files_with(ext));
+
+ if (contains(canvas,"load_MapStructure"))
+ load_MapStructure();
+
+ # canvas.MFD = {EFIS:}; # where we'll be storing all MFDs
+ # TODO: should be inside a separate subfolder, i.e. canvas/map/mfd
+ load_modules( files_with('.mfd'), 'canvas' );
+});
-# canvas.MFD = {EFIS:}; # where we'll be storing all MFDs
-# TODO: should be inside a separate subfolder, i.e. canvas/map/mfd
-load_modules( files_with('.mfd'), 'canvas' );
diff --git a/Nasal/canvas/map/DME.lcontroller b/Nasal/canvas/map/DME.lcontroller
new file mode 100644
index 000000000..b2d3c714e
--- /dev/null
+++ b/Nasal/canvas/map/DME.lcontroller
@@ -0,0 +1,32 @@
+# Class things:
+var name = 'DME';
+var parents = [SymbolLayer.Controller];
+var __self__ = caller(0)[0];
+SymbolLayer.Controller.add(name, __self__);
+SymbolLayer.add(name, {
+ parents: [SymbolLayer],
+ type: name, # Symbol type
+ df_controller: __self__, # controller to use by default -- this one
+});
+var a_instance = nil;
+var new = func(layer) {
+ var m = {
+ parents: [__self__],
+ layer: layer,
+ listeners: [],
+ query_range_nm: 25,
+ query_type:'dme',
+ };
+ __self__.a_instance = m;
+ return m;
+};
+var del = func() {
+ foreach (var l; me.listeners)
+ removelistener(l);
+};
+
+var searchCmd = func {
+ #print("Running query:", me.query_type);
+ return positioned.findWithinRange(me.query_range_nm, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch
+};
+
diff --git a/Nasal/canvas/map/DME.scontroller b/Nasal/canvas/map/DME.scontroller
new file mode 100644
index 000000000..0e54d4dc2
--- /dev/null
+++ b/Nasal/canvas/map/DME.scontroller
@@ -0,0 +1,12 @@
+# Class things:
+var name = 'DME';
+var parents = [Symbol.Controller];
+var __self__ = caller(0)[0];
+Symbol.Controller.add(name, __self__);
+Symbol.registry[ name ].df_controller = __self__;
+var new = func(model) ; # this controller doesn't need an instance
+var LayerController = SymbolLayer.Controller.registry[ name ];
+var isActive = func(model) LayerController.a_instance.isActive(model);
+var is_tuned = func()
+ die( name~".scontroller.is_tuned /MUST/ be provided by implementation" );
+
diff --git a/Nasal/canvas/map/DME.symbol b/Nasal/canvas/map/DME.symbol
new file mode 100644
index 000000000..fbf523227
--- /dev/null
+++ b/Nasal/canvas/map/DME.symbol
@@ -0,0 +1,35 @@
+# Class things:
+var name = 'DME';
+var parents = [DotSym];
+var __self__ = caller(0)[0];
+DotSym.makeinstance( name, __self__ );
+
+var element_type = "group"; # we want a group, becomes "me.element"
+var icon_dme = nil;
+
+var draw = func {
+ # Init
+ if (me.icon_dme == nil) {
+ me.icon_dme = me.element.createChild("path")
+ .moveTo(-15,0)
+ .line(-12.5,-7.5)
+ .line(7.5,-12.5)
+ .line(12.5,7.5)
+ .lineTo(7.5,-12.5)
+ .line(12.5,-7.5)
+ .line(7.5,12.5)
+ .line(-12.5,7.5)
+ .lineTo(15,0)
+ .lineTo(7.5,12.5)
+ .vert(14.5)
+ .horiz(-14.5)
+ .vert(-14.5)
+ .close()
+ .setStrokeLineWidth(3);
+ }
+ if (me.controller != nil and me.controller.is_tuned(me.model.frequency/100))
+ me.icon_dme.setColor(0,1,0);
+ else
+ me.icon_dme.setColor(0,0.6,0.85);
+};
+
diff --git a/Nasal/canvas/map/FIX.lcontroller b/Nasal/canvas/map/FIX.lcontroller
new file mode 100644
index 000000000..ed89fb06b
--- /dev/null
+++ b/Nasal/canvas/map/FIX.lcontroller
@@ -0,0 +1,33 @@
+# Class things:
+var name = 'FIX';
+var parents = [SymbolLayer.Controller];
+var __self__ = caller(0)[0];
+SymbolLayer.Controller.add(name, __self__);
+SymbolLayer.add(name, {
+ parents: [SymbolLayer],
+ type: name, # Symbol type
+ df_controller: __self__, # controller to use by default -- this one
+});
+var a_instance = nil;
+var new = func(layer) {
+ var m = {
+ parents: [__self__],
+ layer: layer,
+ listeners: [],
+ query_range_nm: 25,
+ query_type:'fix',
+ };
+ __self__.a_instance = m;
+ return m;
+};
+var del = func() {
+ #print("VOR.lcontroller.del()");
+ foreach (var l; me.listeners)
+ removelistener(l);
+};
+
+var searchCmd = func {
+ #print("Running query:", me.query_type);
+ return positioned.findWithinRange(me.query_range_nm, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch
+};
+
diff --git a/Nasal/canvas/map/FIX.scontroller b/Nasal/canvas/map/FIX.scontroller
new file mode 100644
index 000000000..5726f86ab
--- /dev/null
+++ b/Nasal/canvas/map/FIX.scontroller
@@ -0,0 +1,12 @@
+# Class things:
+var name = 'FIX';
+var parents = [Symbol.Controller];
+var __self__ = caller(0)[0];
+Symbol.Controller.add(name, __self__);
+Symbol.registry[ name ].df_controller = __self__;
+var new = func(model) ; # this controller doesn't need an instance
+var LayerController = SymbolLayer.Controller.registry[ name ];
+var isActive = func(model) LayerController.a_instance.isActive(model);
+var query_range = func()
+ die( name~".scontroller.query_range /MUST/ be provided by implementation" );
+
diff --git a/Nasal/canvas/map/FIX.symbol b/Nasal/canvas/map/FIX.symbol
new file mode 100644
index 000000000..c2eee2c11
--- /dev/null
+++ b/Nasal/canvas/map/FIX.symbol
@@ -0,0 +1,30 @@
+# Class things:
+var name = 'FIX';
+var parents = [DotSym];
+var __self__ = caller(0)[0];
+DotSym.makeinstance( name, __self__ );
+
+var element_type = "group"; # we want a group, becomes "me.element"
+var icon_fix = nil;
+var text_fix = nil;
+
+var draw = func {
+ if (me.icon_fix != nil) return;
+ # the fix symbol
+ me.icon_fix = me.element.createChild("path")
+ .moveTo(-15,15)
+ .lineTo(0,-15)
+ .lineTo(15,15)
+ .close()
+ .setStrokeLineWidth(3)
+ .setColor(0,0.6,0.85)
+ .setScale(0.5,0.5); # FIXME: do proper LOD handling here - we need to scale according to current texture dimensions vs. original/design dimensions
+ # the fix label
+ me.text_fix = me.element.createChild("text")
+ .setDrawMode( canvas.Text.TEXT )
+ .setText(me.model.id)
+ .setFont("LiberationFonts/LiberationSans-Regular.ttf")
+ .setFontSize(28)
+ .setTranslation(5,25);
+};
+
diff --git a/Nasal/canvas/map/NDB.lcontroller b/Nasal/canvas/map/NDB.lcontroller
new file mode 100644
index 000000000..3250fc0f7
--- /dev/null
+++ b/Nasal/canvas/map/NDB.lcontroller
@@ -0,0 +1,30 @@
+# Class things:
+var name = 'NDB';
+var parents = [SymbolLayer.Controller];
+var __self__ = caller(0)[0];
+SymbolLayer.Controller.add(name, __self__);
+SymbolLayer.add(name, {
+ parents: [SymbolLayer],
+ type: name, # Symbol type
+ df_controller: __self__, # controller to use by default -- this one
+});
+var new = func(layer) {
+ var m = {
+ parents: [__self__],
+ layer: layer,
+ listeners: [],
+ query_range_nm: 25,
+ query_type:'ndb',
+ };
+ return m;
+};
+var del = func() {
+ foreach (var l; me.listeners)
+ removelistener(l);
+};
+
+var searchCmd = func {
+ #print("Running query:", me.query_type);
+ return positioned.findWithinRange(me.query_range_nm, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch
+};
+
diff --git a/Nasal/canvas/map/NDB.scontroller b/Nasal/canvas/map/NDB.scontroller
new file mode 100644
index 000000000..7d1011db0
--- /dev/null
+++ b/Nasal/canvas/map/NDB.scontroller
@@ -0,0 +1,12 @@
+# Class things:
+var name = 'NDB';
+var parents = [Symbol.Controller];
+var __self__ = caller(0)[0];
+Symbol.Controller.add(name, __self__);
+Symbol.registry[ name ].df_controller = __self__;
+var new = func(model) ; # this controller doesn't need an instance
+var LayerController = SymbolLayer.Controller.registry[ name ];
+var isActive = func(model) LayerController.a_instance.isActive(model);
+var query_range = func()
+ die( name~".scontroller.query_range /MUST/ be provided by implementation" );
+
diff --git a/Nasal/canvas/map/NDB.symbol b/Nasal/canvas/map/NDB.symbol
new file mode 100644
index 000000000..541b4cebb
--- /dev/null
+++ b/Nasal/canvas/map/NDB.symbol
@@ -0,0 +1,18 @@
+# Class things:
+var name = 'NDB';
+var parents = [DotSym];
+var __self__ = caller(0)[0];
+DotSym.makeinstance( name, __self__ );
+
+var element_type = "group"; # we want a group, becomes "me.element", which we parse a SVG onto
+var svg_path = "/gui/dialogs/images/ndb_symbol.svg"; # speaking of path, this is our path to use
+var local_svg_path = nil; # track changes in the SVG's path
+
+var draw = func {
+ if (me.svg_path == me.local_svg_path) return;
+ me.element.removeAllChildren();
+ me.local_svg_path = me.svg_path;
+ canvas.parsesvg(me.element, me.svg_path);
+ me.inited = 1;
+};
+
diff --git a/Nasal/canvas/map/VOR.lcontroller b/Nasal/canvas/map/VOR.lcontroller
index 1f63e4e67..f01f83da9 100644
--- a/Nasal/canvas/map/VOR.lcontroller
+++ b/Nasal/canvas/map/VOR.lcontroller
@@ -7,6 +7,7 @@ SymbolLayer.add("VOR", {
type: "VOR", # Symbol type
df_controller: __self__, # controller to use by default -- this one
});
+var a_instance = nil;
var new = func(layer) {
var m = {
parents: [__self__],
@@ -14,6 +15,7 @@ var new = func(layer) {
active_vors: [],
navNs: props.globals.getNode("instrumentation").getChildren("nav"),
listeners: [],
+ query_type:'vor',
};
setsize(m.active_vors, size(m.navNs));
foreach (var navN; m.navNs) {
@@ -24,6 +26,7 @@ var new = func(layer) {
}
#call(debug.dump, keys(layer));
m.changed_freq(update:0);
+ __self__.a_instance = m;
return m;
};
var del = func() {
@@ -46,7 +49,7 @@ var changed_freq = func(update=1) {
if (update) me.layer.update();
};
var searchCmd = func {
- #print("Run query");
- return positioned.findWithinRange(100, 'vor'); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch
+ #print("Running query:", me.query_type);
+ return positioned.findWithinRange(100, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch
};
diff --git a/Nasal/canvas/map/VOR.scontroller b/Nasal/canvas/map/VOR.scontroller
index b95bcc4f2..65fe8f408 100644
--- a/Nasal/canvas/map/VOR.scontroller
+++ b/Nasal/canvas/map/VOR.scontroller
@@ -1,10 +1,11 @@
# Class things:
var parents = [Symbol.Controller];
var __self__ = caller(0)[0];
+Symbol.Controller.add("VOR", __self__);
Symbol.registry["VOR"].df_controller = __self__;
var new = func(model) ; # this controller doesn't need an instance
-var LayerController = SymbolLayer.registry["VOR"];
-var isActive = func(model) LayerController.isActive(model);
+var LayerController = SymbolLayer.Controller.registry["VOR"];
+var isActive = func(model) LayerController.a_instance.isActive(model);
var query_range = func()
die("VOR.scontroller.query_range /MUST/ be provided by implementation");
diff --git a/Nasal/canvas/map/VOR.symbol b/Nasal/canvas/map/VOR.symbol
index aaa259dcc..faf37c206 100644
--- a/Nasal/canvas/map/VOR.symbol
+++ b/Nasal/canvas/map/VOR.symbol
@@ -1,52 +1,57 @@
-# Read by the DotSym.readinstance; each variable becomes a derived class's member/method
+# Class things:
+var name = 'VOR';
+var parents = [DotSym];
+var __self__ = caller(0)[0];
+DotSym.makeinstance( name, __self__ );
var element_type = "group"; # we want a group, becomes "me.element"
-var inited = 0; # this allows us to track whether draw() is an init() or an update()
+var icon_vor = nil;
var range_vor = nil; # two elements that get drawn when needed
var radial_vor = nil; # if one is nil, the other has to be nil
var draw = func {
- if (me.inited) {
- # Update
- if (me.controller.isActive(me.model)) {
- if (me.range_vor == nil) {
- var rangeNm = me.controller.query_range();
- # print("VOR is tuned:", me.model.id);
- var radius = (me.model.range_nm/rangeNm)*345;
- me.range_vor = me.element.createChild("path")
- .moveTo(-radius,0)
- .arcSmallCW(radius,radius,0,2*radius,0)
- .arcSmallCW(radius,radius,0,-2*radius,0)
- .setStrokeLineWidth(3)
- .setStrokeDashArray([5, 15, 5, 15, 5])
- .setColor(0,1,0);
+ # Init
+ if (me.icon_vor == nil) {
+ me.icon_vor = me.element.createChild("path")
+ .moveTo(-15,0)
+ .lineTo(-7.5,12.5)
+ .lineTo(7.5,12.5)
+ .lineTo(15,0)
+ .lineTo(7.5,-12.5)
+ .lineTo(-7.5,-12.5)
+ .close()
+ .setStrokeLineWidth(3)
+ .setColor(0,0.6,0.85);
+ }
+ # Update
+ if (me.controller.isActive(me.model)) {
+ if (me.range_vor == nil) {
+ var rangeNm = me.controller.query_range();
+ # print("VOR is tuned:", me.model.id);
+ var radius = (me.model.range_nm/rangeNm)*345;
+ me.range_vor = me.element.createChild("path")
+ .moveTo(-radius,0)
+ .arcSmallCW(radius,radius,0,2*radius,0)
+ .arcSmallCW(radius,radius,0,-2*radius,0)
+ .setStrokeLineWidth(3)
+ .setStrokeDashArray([5, 15, 5, 15, 5])
+ .setColor(0,1,0);
- var course = controller.get_tuned_course(me.model.frequency/100);
- vor_grp.createChild("path")
- .moveTo(0,-radius)
- .vert(2*radius)
- .setStrokeLineWidth(3)
- .setStrokeDashArray([15, 5, 15, 5, 15])
- .setColor(0,1,0)
- .setRotation(course*D2R);
- icon_vor.setColor(0,1,0);
- }
- me.range_vor.show();
- me.radial_vor.show();
- } else {
- me.range_vor.hide();
- me.radial_vor.hide();
+ var course = me.controller.get_tuned_course(me.model.frequency/100);
+ me.radial_vor = me.element.createChild("path")
+ .moveTo(0,-radius)
+ .vert(2*radius)
+ .setStrokeLineWidth(3)
+ .setStrokeDashArray([15, 5, 15, 5, 15])
+ .setColor(0,1,0)
+ .setRotation(course*D2R);
+ me.icon_vor.setColor(0,1,0);
}
- } else # Init
- me.element.createChild("path")
- .moveTo(-15,0)
- .lineTo(-7.5,12.5)
- .lineTo(7.5,12.5)
- .lineTo(15,0)
- .lineTo(7.5,-12.5)
- .lineTo(-7.5,-12.5)
- .close()
- .setStrokeLineWidth(3)
- .setColor(0,0.6,0.85);
+ me.range_vor.show();
+ me.radial_vor.show();
+ } elsif (me.range_vor != nil) {
+ me.range_vor.hide();
+ me.radial_vor.hide();
+ }
};
diff --git a/Nasal/canvas/map/WPT.lcontroller b/Nasal/canvas/map/WPT.lcontroller
new file mode 100644
index 000000000..13bcc2f63
--- /dev/null
+++ b/Nasal/canvas/map/WPT.lcontroller
@@ -0,0 +1,39 @@
+# Class things:
+var name = 'WPT'; # for waypoints
+var parents = [SymbolLayer.Controller];
+var __self__ = caller(0)[0];
+SymbolLayer.Controller.add(name, __self__);
+SymbolLayer.add(name, {
+ parents: [SymbolLayer],
+ type: name, # Symbol type
+ df_controller: __self__, # controller to use by default -- this one
+});
+var new = func(layer) {
+ var m = {
+ parents: [__self__],
+ layer: layer,
+ listeners: [],
+ query_range_nm: 25,
+ query_type:'vor',
+ };
+ return m;
+};
+var del = func() {
+ #print("VOR.lcontroller.del()");
+ foreach (var l; me.listeners)
+ removelistener(l);
+};
+
+var searchCmd = func {
+ #print("Running query: WPT");
+
+ var fp = flightplan();
+ var fpSize = fp.getPlanSize();
+ var result = [];
+ for (var i = 1; i 1890) # Only display suitably large airports
- validApt = 1;
- if (result.id == getprop("autopilot/route-manager/destination/airport") or result.id == getprop("autopilot/route-manager/departure/airport"))
- validApt = 1;
- }
+ var apt = airportinfo(result.id);
+ var runways = apt.runways;
+ var runway_keys = sort(keys(runways),string.icmp);
+ var validApt = 0;
+ foreach(var rwy; runway_keys){
+ var r = runways[rwy];
+ if (r.length > 1890) # Only display suitably large airports
+ validApt = 1;
+ if (result.id == getprop("autopilot/route-manager/destination/airport") or result.id == getprop("autopilot/route-manager/departure/airport"))
+ validApt = 1;
+ }
- if(validApt) {
- #canvas.draw_apt(me.apt_group, result.lat,result.lon,result.id,i);
- me.push(result);
- numResults += 1;
- }
+ if(validApt) {
+ #canvas.draw_apt(me.apt_group, result.lat,result.lon,result.id,i);
+ me.push(result);
+ numResults += 1;
}
}
# set RefPos and hdg to apt !!
diff --git a/Nasal/canvas/map/boeingND.svg b/Nasal/canvas/map/boeingND.svg
index 402c6dbaf..641be3035 100644
--- a/Nasal/canvas/map/boeingND.svg
+++ b/Nasal/canvas/map/boeingND.svg
@@ -17,548 +17,206 @@
height="1024"
width="1024"
version="1.1">image/svg+xmlGijs de Rooy
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-image/svg+xmlGijs de Rooy
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-30
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ style="font-size:39.99999619px">30
@@ -1634,10 +601,10 @@
id="text3088"
y="-108.93858"
x="-845.96948"
- style="font-size:36.000011px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;display:inline;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
+ style="font-size:36.00001144px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
xml:space="preserve"
transform="matrix(0,-1,1,0,0,0)">24
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ style="font-size:39.99995041px">24
@@ -1728,10 +635,10 @@
id="text3096"
y="-1071.5541"
x="-879.19897"
- style="font-size:36.000046px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;display:inline;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
+ style="font-size:36.00004578px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
xml:space="preserve"
transform="matrix(-0.866026,-0.5,0.5,-0.866026,0,0)">18
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ style="font-size:40.00000381px">18
@@ -1822,10 +669,10 @@
id="text3104"
y="-1581.0712"
x="-51.792171"
- style="font-size:36.000004px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;display:inline;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
+ style="font-size:36.00000381px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
xml:space="preserve"
transform="matrix(-0.866025,0.5,-0.5,-0.866025,0,0)">12
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ style="font-size:40.00001144px">12
@@ -1916,10 +703,10 @@
id="text3112"
y="-1123.9312"
x="812.80585"
- style="font-size:36.000042px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;display:inline;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
+ style="font-size:36.00004196px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
xml:space="preserve"
transform="matrix(0,1,-1,0,0,0)">6
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ style="font-size:39.99998093px">6
@@ -2010,10 +737,10 @@
id="text3120"
y="-158.40652"
x="844.79596"
- style="font-size:36.000095px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;display:inline;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
+ style="font-size:36.00009537px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
xml:space="preserve"
transform="matrix(0.866026,0.5,-0.5,0.866026,0,0)">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-33
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ style="font-size:80.43321228px">33
@@ -2466,10 +957,10 @@
id="text3928"
y="265.32059"
x="-503.69513"
- style="font-size:36.000031px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;display:inline;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
+ style="font-size:36.00003052px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
xml:space="preserve"
transform="matrix(0.5,-0.866026,0.866026,0.5,0,0)">27
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ style="font-size:80.43321228px">27
@@ -2560,10 +991,10 @@
id="text3936"
y="-541.37213"
x="-1019.1653"
- style="font-size:35.999966px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;display:inline;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
+ style="font-size:35.99996567px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
xml:space="preserve"
transform="matrix(-0.5,-0.866025,0.866025,-0.5,0,0)">21
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ style="font-size:80.43321228px">21
@@ -2654,10 +1025,10 @@
id="text3944"
y="-1392.2814"
x="-557.84332"
- style="font-size:36.000027px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;display:inline;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
+ style="font-size:36.0000267px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
xml:space="preserve"
transform="scale(-1,-1)">15
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ style="font-size:80.43321228px">15
@@ -2748,10 +1059,10 @@
id="text3952"
y="-1431.3702"
x="409.84222"
- style="font-size:36.000061px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;display:inline;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
+ style="font-size:36.00006104px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
xml:space="preserve"
transform="matrix(-0.5,0.866026,-0.866026,-0.5,0,0)">9
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ style="font-size:80.43321228px">9
@@ -2842,10 +1093,10 @@
id="text3960"
y="-611.09729"
x="941.70276"
- style="font-size:36.000008px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;display:inline;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
+ style="font-size:36.00000763px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
xml:space="preserve"
transform="matrix(0.5,0.866025,-0.866025,0.5,0,0)">3
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ style="font-size:80.43321228px">3
WXR
+
ARPT
+
WPT
+
STA
+
N
+
+
W
+
+
E
+
+
S
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/Nasal/canvas/map/dme.model b/Nasal/canvas/map/dme.model
index 90853158d..53e4d0f61 100644
--- a/Nasal/canvas/map/dme.model
+++ b/Nasal/canvas/map/dme.model
@@ -11,12 +11,10 @@ DMEModel.init = func {
));
}
- var results = positioned.findWithinRange(me._controller.query_range()*2 ,"dme");
+ var results = positioned.findWithinRange(me._controller.query_range() ,"dme");
foreach(result; results) {
me.push(result);
}
me.notifyView();
-}
-
-
+}
\ No newline at end of file
diff --git a/Nasal/canvas/map/fixes.model b/Nasal/canvas/map/fixes.model
index 5d46eeee4..97e2697f9 100644
--- a/Nasal/canvas/map/fixes.model
+++ b/Nasal/canvas/map/fixes.model
@@ -4,10 +4,10 @@ FixModel.new = func make( LayerModel, FixModel );
FixModel.init = func {
me._view.reset(); # wraps removeAllChildren() ATM
- var results = positioned.findWithinRange( me._controller['query_range']()*2 ,"fix");
+ var results = positioned.findWithinRange( me._controller['query_range']() ,"fix");
var numNum = 0;
foreach(result; results) {
- # Skip airport navaids (real thing makes distinction between high/low altitude fixes)
+ # Skip airport fixes
if(string.match(result.id,"*[^0-9]")) {
me.push(result);
numNum = numNum + 1;
diff --git a/Nasal/canvas/map/navaids.model b/Nasal/canvas/map/navaids.model
index 65cf37344..e4edbf2d6 100644
--- a/Nasal/canvas/map/navaids.model
+++ b/Nasal/canvas/map/navaids.model
@@ -2,7 +2,7 @@ var NavaidModel = {};
NavaidModel.new = func make(LayerModel, NavaidModel);
NavaidModel.init = func {
me._view.reset();
- var navaids = findNavaidsWithinRange(15);
+ var navaids = findNavaidsWithinRange(me._controller.query_range());
foreach(var n; navaids)
me.push(n);
me.notifyView();
diff --git a/Nasal/canvas/map/navdisplay.mfd b/Nasal/canvas/map/navdisplay.mfd
index f65dc9b87..f3fc85030 100644
--- a/Nasal/canvas/map/navdisplay.mfd
+++ b/Nasal/canvas/map/navdisplay.mfd
@@ -2,14 +2,10 @@
# Boeing Navigation Display by Gijs de Rooy
# ==============================================================================
-
##
# do we really need to keep track of each drawable here ??
var i = 0;
-
-
-
##
# pseudo DSL-ish: use these as placeholders in the config hash below
var ALWAYS = func 1;
@@ -24,259 +20,319 @@ var trigger_update = func(layer) layer._model.init();
# 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,
+# any aircraft-specific ND behavior should be wrapped here,
# to isolate/decouple things in the generic NavDisplay class
#
-# Note to Gijs: this may look weird and confusing, but it' actually requires
-# less coding now, and it is now even possible to configure things via a little
-# XML wrapper
# TODO: move this to an XML config file
-#
+#
var NDStyles = {
- ##
- # this configures the 744 ND to help generalize the NavDisplay class itself
- 'B747-400': {
- 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/boeingND.svg",
-
-##
-## this loads and configures existing layers (currently, *.layer files in Nasal/canvas/map)
-##
-
- layers: [
- { name:'fixes', update_on:['toggle_range','toggle_waypoints','toggle_display_mode'], predicate: func(nd, layer) {
- # print("Running fixes predicate");
- var visible=nd.get_switch('toggle_waypoints') and nd.in_mode('toggle_display_mode', ['MAP']);
- if(nd.rangeNm() <= 40 and visible) {
- trigger_update( layer );
- } layer._view.setVisible(visible);
-
- }, # end of layer update predicate
- }, # end of fixes layer
-
- # Should redraw every 10 seconds
- { name:'storms', update_on:['toggle_range','toggle_weather','toggle_display_mode'], predicate: func(nd, layer) {
- # print("Running fixes predicate");
- var visible=nd.get_switch('toggle_weather') and nd.get_switch('toggle_display_mode') != "PLAN";
- if (visible) {
- trigger_update( layer );
- } layer._view.setVisible(visible);
-
- }, # end of layer update predicate
- }, # end of storms layer
-
- { name:'airplaneSymbol', update_on:['toggle_range','toggle_display_mode'], predicate: func(nd, layer) {
- # print("Running fixes predicate");
- var visible=nd.get_switch('toggle_display_mode') == "PLAN";
- if (visible) {
- trigger_update( layer );
- } layer._view.setVisible(visible);
-
- }, # end of layer update predicate
- }, # end of storms layer
-
- { name:'airports-nd', update_on:['toggle_range','toggle_airports','toggle_display_mode'], predicate: func(nd, layer) {
- # print("Running airports-nd predicate");
- var visible = nd.get_switch('toggle_airports') and nd.in_mode('toggle_display_mode', ['MAP']);
- if (nd.rangeNm() <= 80 and visible) {
- trigger_update( layer ); # clear & redraw
- }
- layer._view.setVisible( visible);
-
- }, # end of layer update predicate
- }, # end of airports layer
-
- { name:'vor', update_on:['toggle_range','toggle_stations','toggle_display_mode'], predicate: func(nd, layer) {
- var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']);
- if(nd.rangeNm() <= 40 and visible) {
- trigger_update( layer ); # clear & redraw
- }
- layer._view.setVisible( visible );
- }, # end of layer update predicate
- }, # end of VOR layer
-
- { name:'dme', update_on:['toggle_range','toggle_stations','toggle_display_mode'], predicate: func(nd, layer) {
- var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']);
- if(nd.rangeNm() <= 40 and visible){
- trigger_update( layer ); # clear & redraw
- }
- layer._view.setVisible( visible );
- }, # end of layer update predicate
- }, # end of DME layer
-
- { name:'mp-traffic', update_on:['toggle_range','toggle_traffic'], predicate: func(nd, layer) {
- trigger_update( layer ); # clear & redraw
- layer._view.setVisible( nd.get_switch('toggle_traffic') );
- }, # end of layer update predicate
- }, # end of traffic layer
-
- { name:'runway-nd', update_on:['toggle_range','toggle_display_mode'], predicate: func(nd, layer) {
- var visible = (nd.rangeNm() <= 40 and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP','PLAN'])) ;
- if (visible)
- trigger_update( layer ); # clear & redraw
- layer._view.setVisible( visible );
- }, # end of layer update predicate
- }, # end of airports-nd layer
-
- { name:'route', update_on:['toggle_display_mode',], predicate: func(nd, layer) {
- var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN']));
- if (visible) {
- trigger_update( layer ); # clear & redraw
- }
- layer._view.setVisible( visible );
- }, # end of layer update predicate
- }, # end of route 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) getprop("/velocities/airspeed-kt") > 100,
- is_true: func(nd) {
- nd.symbols.tas.setText(sprintf("%3.0f",getprop("/velocities/airspeed-kt") ));
- nd.symbols.tas.show();
- },
- is_false: func(nd) nd.symbols.tas.hide(),
- }, # end of tas behavior callbacks
- }, # end of tas hash
- {
- id: 'wpActiveId',
- impl: {
- init: func(nd,symbol),
- predicate: func(nd) getprop("/autopilot/route-manager/wp/id") != nil and getprop("autopilot/route-manager/active"),
- is_true: func(nd) {
- nd.symbols.wpActiveId.setText(getprop("/autopilot/route-manager/wp/id"));
- nd.symbols.wpActiveId.show();
+ ##
+ # 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";
},
- 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"),
- is_true: func(nd) {
- nd.symbols.wpActiveDist.setText(sprintf("%3.01fNM",getprop("/autopilot/route-manager/wp/dist")));
- nd.symbols.wpActiveDist.show();
- },
- is_false: func(nd) nd.symbols.wpActiveDist.hide(),
- }, # of wpActiveDist.impl
- }, # of wpActiveDist
- {
- id: 'eta',
- impl: {
- init: func(nd,symbol),
- predicate: func(nd) getprop("autopilot/route-manager/wp/eta") != nil and getprop("autopilot/route-manager/active"),
- 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);
- if (h>24) h=h-24;
- etaSec=etaSec-3600*h;
- var m = math.floor(etaSec/60);
- etaSec=etaSec-60*m;
- var s = etaSec;
- nd.symbols.eta.setText(sprintf("%02.0f%02.0f.%02.0fz",h,m,s));
- nd.symbols.eta.show();
- },
- is_false: func(nd) nd.symbols.eta.hide(),
- }, # of eta.impl
- }, # of eta
- { id:'hdg',
- impl: {
- init: func(nd,symbol),
- predicate: ALWAYS, # always true
- is_true: func(nd) nd.symbols.hdg.setText(sprintf("%03.0f", nd.aircraft_source.get_hdg_mag() )),
- is_false: NOTHING,
- }, # of hdg.impl
- }, # of hdg
- { id:'gs',
- impl: {
- init: func(nd,symbol),
- common: func(nd) nd.symbols.gs.setText(sprintf("%3.0f",nd.aircraft_source.get_spd() )),
- predicate: func(nd) nd.aircraft_source.get_spd() >= 30,
- is_true: func(nd) {
- nd.symbols.gs.setFontSize(36);
- },
- is_false: func(nd) nd.symbols.gs.setFontSize(52),
- }, # of gs.impl
- }, # of gs
-
- { id:'rangeArcs',
- impl: {
- init: func(nd,symbol),
- predicate: func(nd) ((nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_weather')) or (nd.get_switch('toggle_display_mode') == "MAP" and !nd.get_switch('toggle_centered'))),
- is_true: func(nd) nd.symbols.rangeArcs.show(),
- is_false: func(nd) nd.symbols.rangeArcs.hide(),
- }, # of rangeArcs.impl
- }, # of rangeArcs
+ # 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/boeingND.svg",
+ ##
+ ## this loads and configures existing layers (currently, *.layer files in Nasal/canvas/map)
+ ##
- ], # end of vector with features
+ layers: [
+ { name:'fixes', disabled:1, update_on:['toggle_range','toggle_waypoints'],
+ predicate: func(nd, layer) {
+ # print("Running fixes predicate");
+ var visible=nd.get_switch('toggle_waypoints') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40);
+ if (visible) {
+ # print("fixes update requested!");
+ trigger_update( layer );
+ }
+ layer._view.setVisible(visible);
+ }, # end of layer update predicate
+ }, # end of fixes layer
+ { 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 vor 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
+ }, # end of FIX layer
+ # Should redraw every 10 seconds
+ { name:'storms', update_on:['toggle_range','toggle_weather','toggle_display_mode'],
+ predicate: func(nd, layer) {
+ # print("Running fixes predicate");
+ var visible=nd.get_switch('toggle_weather') and nd.get_switch('toggle_display_mode') != "PLAN";
+ if (visible) {
+ #print("storms update requested!");
+ trigger_update( layer );
+ }
+ layer._view.setVisible(visible);
+ }, # end of layer update predicate
+ }, # end of storms layer
+
+ { name:'airports-nd', update_on:['toggle_range','toggle_airports','toggle_display_mode'],
+ predicate: func(nd, layer) {
+ # print("Running airports-nd predicate");
+ var visible = nd.get_switch('toggle_airports') and nd.in_mode('toggle_display_mode', ['MAP']);
+ if (visible) {
+ trigger_update( layer ); # clear & redraw
+ }
+ layer._view.setVisible( visible );
+ }, # end of layer update predicate
+ }, # end of airports layer
+
+ # Should distinct between low and high altitude navaids. Hiding above 40 NM for now, to prevent clutter/lag.
+ { name:'vor', disabled:1, update_on:['toggle_range','toggle_stations','toggle_display_mode'],
+ predicate: func(nd, layer) {
+ # print("Running vor layer predicate");
+ var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40);
+ if(visible) {
+ trigger_update( layer ); # clear & redraw
+ }
+ layer._view.setVisible( nd.get_switch('toggle_stations') );
+ }, # end of layer update predicate
+ }, # end of VOR layer
+ { 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) {
+ # print("Running vor layer predicate");
+ # 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");
+ # (Hopefully) smart update
+ layer.update();
+ }
+ }, # end of layer update predicate
+ }, # end of VOR layer
+
+ # Should distinct between low and high altitude navaids. Hiding above 40 NM for now, to prevent clutter/lag.
+ { name:'dme', disabled:1, update_on:['toggle_range','toggle_stations'],
+ predicate: func(nd, layer) {
+ var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40);
+ if(visible) {
+ trigger_update( layer ); # clear & redraw
+ }
+ layer._view.setVisible( nd.get_switch('toggle_stations') );
+ }, # end of layer update predicate
+ }, # end of DME layers
+ { name:'DME', isMapStructure:1, update_on:['toggle_range','toggle_stations'],
+ # FIXME: this is a really ugly place for controller code
+ predicate: func(nd, layer) {
+ # print("Running vor layer predicate");
+ # toggle visibility here
+ layer.group.setVisible( nd.get_switch('toggle_stations') );
+ if (nd.rangeNm() <= 40 and
+ nd.get_switch('toggle_stations') and
+ nd.get_switch('toggle_display_mode') == "MAP") {
+ #print("Updating MapStructure ND layer: DME");
+ # (Hopefully) smart update
+ layer.update();
+ }
+ }, # end of layer update predicate
+ }, # end of DME layer
+
+ { name:'mp-traffic', update_on:['toggle_range','toggle_traffic'],
+ predicate: func(nd, layer) {
+ trigger_update( layer ); # clear & redraw
+ layer._view.setVisible( 1 ); #nd.get_switch('toggle_traffic')
+ }, # end of layer update predicate
+ }, # end of traffic layer
- }, # end of 744 ND style
+ { name:'runway-nd', update_on:['toggle_range','toggle_display_mode'],
+ predicate: func(nd, layer) {
+ var visible = (nd.rangeNm() <= 40 and getprop("autopilot/route-manager/active") ) ;
+ if (visible)
+ trigger_update( layer ); # clear & redraw
+ layer._view.setVisible( visible );
+ }, # end of layer update predicate
+ }, # end of airports-nd layer
+
+ { name:'route', update_on:['toggle_range','toggle_display_mode'],
+ predicate: func(nd, layer) {
+ trigger_update( layer ); # clear & redraw
+ layer._view.setVisible( 1 ); #nd.get_switch('toggle_traffic')
+ }, # end of layer update predicate
+ }, # end of route 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) getprop("/velocities/airspeed-kt") > 100,
+ is_true: func(nd) {
+ nd.symbols.tas.setText(sprintf("%3.0f",getprop("/velocities/airspeed-kt") ));
+ nd.symbols.tas.show();
+ },
+ is_false: func(nd) nd.symbols.tas.hide(),
+ }, # end of tas behavior callbacks
+ }, # end of tas hash
+ {
+ id: 'wpActiveId',
+ impl: {
+ init: func(nd,symbol),
+ predicate: func(nd) getprop("/autopilot/route-manager/wp/id") != nil and getprop("autopilot/route-manager/active"),
+ 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"),
+ is_true: func(nd) {
+ nd.symbols.wpActiveDist.setText(sprintf("%3.01fNM",getprop("/autopilot/route-manager/wp/dist")));
+ nd.symbols.wpActiveDist.show();
+ },
+ is_false: func(nd) nd.symbols.wpActiveDist.hide(),
+ }, # of wpActiveDist.impl
+ }, # of wpActiveDist
+ {
+ id: 'eta',
+ impl: {
+ init: func(nd,symbol),
+ predicate: func(nd) getprop("autopilot/route-manager/wp/eta") != nil and getprop("autopilot/route-manager/active"),
+ 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);
+ if (h>24) h=h-24;
+ etaSec=etaSec-3600*h;
+ var m = math.floor(etaSec/60);
+ etaSec=etaSec-60*m;
+ var s = etaSec;
+ nd.symbols.eta.setText(sprintf("%02.0f%02.0f.%02.0fz",h,m,s));
+ nd.symbols.eta.show();
+ },
+ is_false: func(nd) nd.symbols.eta.hide(),
+ }, # of eta.impl
+ }, # of eta
+ {
+ id:'hdg',
+ impl: {
+ init: func(nd,symbol),
+ predicate: ALWAYS, # always true
+ is_true: func(nd) nd.symbols.hdg.setText(sprintf("%03.0f", nd.aircraft_source.get_hdg_mag() )),
+ is_false: NOTHING,
+ }, # of hdg.impl
+ }, # of hdg
+ {
+ id:'gs',
+ impl: {
+ init: func(nd,symbol),
+ common: func(nd) nd.symbols.gs.setText(sprintf("%3.0f",nd.aircraft_source.get_spd() )),
+ predicate: func(nd) nd.aircraft_source.get_spd() >= 30,
+ is_true: func(nd) {
+ nd.symbols.gs.setFontSize(36);
+ },
+ is_false: func(nd) nd.symbols.gs.setFontSize(52),
+ }, # of gs.impl
+ }, # of gs
+ {
+ id:'rangeArcs',
+ impl: {
+ init: func(nd,symbol),
+ predicate: func(nd) (((nd.get_switch('toggle_display_mode') == "APP" or nd.get_switch('toggle_display_mode') == "VOR") and nd.get_switch('toggle_weather')) or nd.get_switch('toggle_display_mode') == "MAP"),
+ is_true: func(nd) nd.symbols.rangeArcs.show(),
+ is_false: func(nd) nd.symbols.rangeArcs.hide(),
+ }, # of rangeArcs.impl
+ }, # of rangeArcs
+
+
+ ], # end of vector with features
+
+
+ }, # end of Boeing style
#####
##
-## add support for other aircraft/ND types and styles here (737, 757, 777 etc)
+## add support for other aircraft/ND types and styles here (Airbus etc)
##
##
}; # end of NDStyles
-
##
# encapsulate hdg/lat/lon source, so that the ND may also display AI/MP aircraft in a pilot-view at some point (aka stress-testing)
#
var NDSourceDriver = {};
-NDSourceDriver.new = func {
- var m = {parents:[NDSourceDriver]};
- m.get_hdg_mag= func getprop("/orientation/heading-magnetic-deg");
- m.get_hdg_tru= func getprop("/orientation/heading-deg");
- m.get_trk_mag= func getprop("/orientation/track-magnetic-deg");
- m.get_trk_tru= func getprop("/orientation/track-deg");
- m.get_lat= func getprop("/position/latitude-deg");
- m.get_lon= func getprop("/position/longitude-deg");
- m.get_spd= func getprop("/velocities/groundspeed-kt");
- m.get_vspd= func getprop("/velocities/vertical-speed-fps");
-return m;
+NDSourceDriver.new = func {
+ var m = {parents:[NDSourceDriver]};
+ m.get_hdg_mag= func getprop("/orientation/heading-magnetic-deg");
+ m.get_hdg_tru= func getprop("/orientation/heading-deg");
+ m.get_hgg = func getprop("instrumentation/afds/settings/heading");
+ m.get_trk_mag= func
+ {
+ if(getprop("/velocities/groundspeed-kt") > 80)
+ {
+ getprop("/orientation/track-magnetic-deg");
+ }
+ else
+ {
+ getprop("/orientation/heading-magnetic-deg");
+ }
+ };
+ m.get_trk_tru = func
+ {
+ if(getprop("/velocities/groundspeed-kt") > 80)
+ {
+ getprop("/orientation/track-deg");
+ }
+ else
+ {
+ getprop("/orientation/heading-deg");
+ }
+ };
+ m.get_lat= func getprop("/position/latitude-deg");
+ m.get_lon= func getprop("/position/longitude-deg");
+ m.get_spd= func getprop("/velocities/groundspeed-kt");
+ m.get_vspd= func getprop("/velocities/vertical-speed-fps");
+ return m;
}
-
##
# configure aircraft specific cockpit switches here
-# these are some defaults, can be overridden when calling NavDisplay.new() -
-# see the 744 ND.nas file the backend code should never deal directly with
+# these are some defaults, can be overridden when calling NavDisplay.new() -
+# see the 744 ND.nas file the backend code should never deal directly with
# aircraft specific properties using getprop.
# To get started implementing your own ND, just copy the switches hash to your
# ND.nas file and map the keys to your cockpit properties - and things will just work.
@@ -284,18 +340,21 @@ return m;
# TODO: switches are ND specific, so move to the NDStyle hash!
var default_switches = {
- 'toggle_range': {path: '/inputs/range-nm', value:10, type:'INT'},
- 'toggle_weather': {path: '/inputs/wxr', value:0, type:'BOOL'},
- 'toggle_airports': {path: '/inputs/arpt', value:0, type:'BOOL'},
- 'toggle_stations': {path: '/inputs/sta', value:0, type:'BOOL'},
- 'toggle_waypoints': {path: '/inputs/wpt', value:0, type:'BOOL'},
- 'toggle_position': {path: '/inputs/pos', value:0, type:'BOOL'},
- 'toggle_data': {path: '/inputs/data',value:0, type:'BOOL'},
- 'toggle_terrain': {path: '/inputs/terr',value:0, type:'BOOL'},
- 'toggle_traffic': {path: '/inputs/tfc',value:0, type:'BOOL'},
- 'toggle_centered': {path: '/inputs/nd-centered',value:0, type:'BOOL'},
- 'toggle_display_mode': {path: '/mfd/display-mode', value:'MAP', type:'STRING'},
- 'toggle_true_north': {path: '/mfd/true-north', value:0, type:'BOOL'},
+ 'toggle_range': {path: '/inputs/range-nm', value:40, type:'INT'},
+ 'toggle_weather': {path: '/inputs/wxr', value:0, type:'BOOL'},
+ 'toggle_airports': {path: '/inputs/arpt', value:0, type:'BOOL'},
+ 'toggle_stations': {path: '/inputs/sta', value:0, type:'BOOL'},
+ 'toggle_waypoints': {path: '/inputs/wpt', value:0, type:'BOOL'},
+ 'toggle_position': {path: '/inputs/pos', value:0, type:'BOOL'},
+ 'toggle_data': {path: '/inputs/data',value:0, type:'BOOL'},
+ 'toggle_terrain': {path: '/inputs/terr',value:0, type:'BOOL'},
+ 'toggle_traffic': {path: '/inputs/tcas',value:0, type:'BOOL'},
+ 'toggle_centered': {path: '/inputs/nd-centered',value:0, type:'BOOL'},
+ 'toggle_lh_vor_adf': {path: '/inputs/lh-vor-adf',value:0, type:'INT'},
+ 'toggle_rh_vor_adf': {path: '/inputs/rh-vor-adf',value:0, type:'INT'},
+ 'toggle_display_mode': {path: '/mfd/display-mode', value:'MAP', type:'STRING'}, # valid values are: APP, MAP, PLAN or VOR
+ 'toggle_display_type': {path: '/mfd/display-type', value:'CRT', type:'STRING'}, # valid values are: CRT or LCD
+ 'toggle_true_north': {path: '/mfd/true-north', value:0, type:'BOOL'},
};
# Hack to update weather radar once every 10 seconds
@@ -315,297 +374,297 @@ var update_apl_sym = func {
update_apl_sym();
##
-# TODO:
+# TODO:
# - introduce a MFD class (use it also for PFD/EICAS)
# - introduce a SGSubsystem class and use it here
# - introduce a Boeing NavDisplay class
var NavDisplay = {
- # reset handler
- handle_reinit: func {
- print("Cleaning up NavDisplay listeners");
- # shut down all timers and other loops here
- me.update_timer.stop();
- foreach(var l; me.listeners)
- removelistener(l);
- },
+ # reset handler
+ handle_reinit: func {
+ print("Cleaning up NavDisplay listeners");
+ # shut down all timers and other loops here
+ me.update_timer.stop();
+ foreach(var l; me.listeners)
+ removelistener(l);
+ },
- listen: func(p,c) {
- append(me.listeners, setlistener(p,c));
- },
+ listen: func(p,c) {
+ append(me.listeners, setlistener(p,c));
+ },
- # listeners for cockpit switches
- listen_switch: func(s,c) {
- # print("event setup for: ", id(c));
- me.listen( me.get_full_switch_path(s), func {
- # print("listen_switch triggered:", s, " callback id:", id(c) );
- c();
- });
+ # listeners for cockpit switches
+ listen_switch: func(s,c) {
+ # print("event setup for: ", id(c));
+ me.listen( me.get_full_switch_path(s), func {
+ # print("listen_switch triggered:", s, " callback id:", id(c) );
+ c();
+ });
+ },
- },
+ # get the full property path for a given switch
+ get_full_switch_path: func (s) {
+ # debug.dump( me.efis_switches[s] );
+ return me.efis_path ~ me.efis_switches[s].path; # FIXME: should be using props.nas instead of ~
+ },
- # get the full property path for a given switch
- get_full_switch_path: func (s) {
- # debug.dump( me.efis_switches[s] );
- return me.efis_path ~ me.efis_switches[s].path; # FIXME: should be using props.nas instead of ~
- },
+ # helper method for getting configurable cockpit switches (which are usually different in each aircraft)
+ get_switch: func(s) {
+ var switch = me.efis_switches[s];
+ var path = me.efis_path ~ switch.path ;
+ #print(s,":Getting switch prop:", path);
- # helper method for getting configurable cockpit switches (which are usually different in each aircraft)
- get_switch: func(s) {
- var switch = me.efis_switches[s];
- var path = me.efis_path ~ switch.path ;
- #print(s,":Getting switch prop:", path);
-
- return getprop( path );
- },
+ return getprop( path );
+ },
- # for creating NDs that are driven by AI traffic instead of the main aircraft (generalization rocks!)
- connectAI: func(source=nil) {
+ # for creating NDs that are driven by AI traffic instead of the main aircraft (generalization rocks!)
+ connectAI: func(source=nil) {
me.aircraft_source = {
get_hdg_mag: func source.getNode('orientation/heading-magnetic-deg').getValue(),
- get_hdg_tru: func source.getNode('orientation/heading-deg').getValue(),
get_trk_mag: func source.getNode('orientation/track-magnetic-deg').getValue(),
- get_trk_tru: func source.getNode('orientation/track-deg').getValue(),
get_lat: func source.getNode('position/latitude-deg').getValue(),
get_lon: func source.getNode('position/longitude-deg').getValue(),
get_spd: func source.getNode('velocities/true-airspeed-kt').getValue(),
- get_vspd: func source.getNode('velocities/vertical-speed-fps').getValue(),
};
- }, # of connectAI
+ }, # of connectAI
- # TODO: the ctor should allow customization, for different aircraft
- # especially properties and SVG files/handles (747, 757, 777 etc)
- new : func(prop1, switches=default_switches, style='B747-400') {
- var m = { parents : [NavDisplay]};
+ # TODO: the ctor should allow customization, for different aircraft
+ # especially properties and SVG files/handles (747, 757, 777 etc)
+ new : func(prop1, switches=default_switches, style='Boeing') {
+ var m = { parents : [NavDisplay]};
- m.listeners=[]; # for cleanup handling
- m.aircraft_source = NDSourceDriver.new(); # uses the main aircraft as the driver/source (speeds, position, heading)
+ m.listeners=[]; # for cleanup handling
+ m.aircraft_source = NDSourceDriver.new(); # uses the main aircraft as the driver/source (speeds, position, heading)
- m.nd_style = NDStyles[style]; # look up ND specific stuff (file names etc)
+ m.nd_style = NDStyles[style]; # look up ND specific stuff (file names etc)
- m.radio_list=["instrumentation/comm/frequencies","instrumentation/comm[1]/frequencies",
- "instrumentation/nav/frequencies","instrumentation/nav[1]/frequencies"];
- m.mfd_mode_list=["APP","VOR","MAP","PLAN"];
+ m.radio_list=["instrumentation/comm/frequencies","instrumentation/comm[1]/frequencies",
+ "instrumentation/nav/frequencies", "instrumentation/nav[1]/frequencies"];
+ m.mfd_mode_list=["APP","VOR","MAP","PLAN"];
- m.efis_path = prop1;
- m.efis_switches = switches ;
+ m.efis_path = prop1;
+ m.efis_switches = switches;
- # just an alias, to avoid having to rewrite the old code for now
- m.rangeNm = func m.get_switch('toggle_range');
+ # just an alias, to avoid having to rewrite the old code for now
+ m.rangeNm = func m.get_switch('toggle_range');
- m.efis = props.globals.initNode(prop1);
- m.mfd = m.efis.initNode("mfd");
+ m.efis = props.globals.initNode(prop1);
+ m.mfd = m.efis.initNode("mfd");
- # TODO: unify this with switch handling
- m.mfd_mode_num = m.mfd.initNode("mode-num",2,"INT");
- m.mfd_display_mode = m.mfd.initNode("display-mode",m.mfd_mode_list[2],"STRING");
- m.std_mode = m.efis.initNode("inputs/setting-std",0,"BOOL");
- m.previous_set = m.efis.initNode("inhg-previos",29.92); # watch out typo here, check other files before fixing !
- m.kpa_mode = m.efis.initNode("inputs/kpa-mode",0,"BOOL");
- m.kpa_output = m.efis.initNode("inhg-kpa",29.92);
- m.kpa_prevoutput = m.efis.initNode("inhg-kpa-previous",29.92);
- m.temp = m.efis.initNode("fixed-temp",0);
- m.alt_meters = m.efis.initNode("inputs/alt-meters",0,"BOOL");
- m.fpv = m.efis.initNode("inputs/fpv",0,"BOOL");
- m.nd_centered = m.efis.initNode("inputs/nd-centered",0,"BOOL");
-
- m.mins_mode = m.efis.initNode("inputs/minimums-mode",0,"BOOL");
- m.mins_mode_txt = m.efis.initNode("minimums-mode-text","RADIO","STRING");
- m.minimums = m.efis.initNode("minimums",250,"INT");
- m.mk_minimums = props.globals.getNode("instrumentation/mk-viii/inputs/arinc429/decision-height");
+ # TODO: unify this with switch handling
+ m.mfd_mode_num = m.mfd .initNode("mode-num",2,"INT");
+ m.std_mode = m.efis.initNode("inputs/setting-std",0,"BOOL");
+ m.previous_set = m.efis.initNode("inhg-previous",29.92);
+ m.kpa_mode = m.efis.initNode("inputs/kpa-mode",0,"BOOL");
+ m.kpa_output = m.efis.initNode("inhg-kpa",29.92);
+ m.kpa_prevoutput = m.efis.initNode("inhg-kpa-previous",29.92);
+ m.temp = m.efis.initNode("fixed-temp",0);
+ m.alt_meters = m.efis.initNode("inputs/alt-meters",0,"BOOL");
+ m.fpv = m.efis.initNode("inputs/fpv",0,"BOOL");
- # TODO: these are switches, can be unified with switch handling hash above (eventually):
-
- m.rh_vor_adf = m.efis.initNode("inputs/rh-vor-adf",0,"INT"); # not yet in switches hash
- m.lh_vor_adf = m.efis.initNode("inputs/lh-vor-adf",0,"INT"); # not yet in switches hash
- m.nd_plan_wpt = m.efis.initNode("inputs/plan-wpt-index", 0, "INT"); # ditto
+ m.mins_mode = m.efis.initNode("inputs/minimums-mode",0,"BOOL");
+ m.mins_mode_txt = m.efis.initNode("minimums-mode-text","RADIO","STRING");
+ m.minimums = m.efis.initNode("minimums",250,"INT");
+ m.mk_minimums = props.globals.getNode("instrumentation/mk-viii/inputs/arinc429/decision-height");
- ###
- # initialize all switches based on the defaults specified in the switch hash
- #
- foreach(var switch; keys( m.efis_switches ) )
- props.globals.initNode
- ( m.get_full_switch_path (switch),
- m.efis_switches[switch].value,
- m.efis_switches[switch].type
- );
-
+ # TODO: these are switches, can be unified with switch handling hash above (eventually):
+ m.nd_plan_wpt = m.efis.initNode("inputs/plan-wpt-index", 0, "INT"); # not yet in switches hash
- return m;
- },
+ ###
+ # initialize all switches based on the defaults specified in the switch hash
+ #
+ foreach(var switch; keys( m.efis_switches ) )
+ props.globals.initNode
+ ( m.get_full_switch_path (switch),
+ m.efis_switches[switch].value,
+ m.efis_switches[switch].type
+ );
+
+
+ return m;
+ },
newMFD: func(canvas_group)
{
-
me.listen("/sim/signals/reinit", func me.handle_reinit() );
-
me.update_timer = maketimer(0.05, func me.update() ); # TODO: make interval configurable via ctor
me.nd = canvas_group;
-
# load the specified SVG file into the me.nd group and populate all sub groups
-
- canvas.parsesvg(me.nd, me.nd_style.svg_filename, {'font-mapper': me.nd_style.font_mapper});
+ canvas.parsesvg(me.nd, me.nd_style.svg_filename, {'font-mapper': me.nd_style.font_mapper});
me.symbols = {}; # storage for SVG elements, to avoid namespace pollution (all SVG elements end up here)
foreach(var feature; me.nd_style.features ) {
- # print("Setting up SVG feature:", feature.id);
- me.symbols[feature.id] = me.nd.getElementById(feature.id);
- if(contains(feature.impl,'init')) feature.impl.init(me.nd, feature); # call The element's init code (i.e. updateCenter)
+ # print("Setting up SVG feature:", feature.id);
+ me.symbols[feature.id] = me.nd.getElementById(feature.id);
+ if(contains(feature.impl,'init')) feature.impl.init(me.nd, feature); # call The element's init code (i.e. updateCenter)
}
-
+
### this is the "old" method that's less flexible, we want to use the style hash instead (see above)
# because things are much better configurable that way
# now look up all required SVG elements and initialize member fields using the same name to have a convenient handle
- foreach(var element; ["wind",
- "dmeLDist","dmeRDist","vorLId","vorRId",
- "range","status.wxr","status.wpt",
- "status.sta","status.arpt"])
- me.symbols[element] = me.nd.getElementById(element);
+ foreach(var element; ["wind","dmeLDist","dmeRDist","dmeL","dmeR","vorL","vorR","vorLId","vorRId",
+ "range","status.wxr","status.wpt","hdgGroup","status.sta","status.arpt"])
+ me.symbols[element] = me.nd.getElementById(element);
# load elements from vector image, and create instance variables using identical names, and call updateCenter() on each
# anything that needs updatecenter called, should be added to the vector here
- #
- foreach(var element; ["rotateComp","rotateComp2","windArrow","selHdg","selHdg2","hdgGroup","northUp",
- "aplSymMap","aplSymMapCtr","aplSymVor","curHdgPtr","curHdgPtr2",
- "staFromL","staToL","staFromR","staToR","staFromL2","staToL2","staFromR2","staToR2",
- "trkInd","vorCrsPtr2","locPtr","compass","compassApp","hdgTrk","truMag","altArc","planArcs"] )
- me.symbols[element] = me.nd.getElementById(element).updateCenter();
+ #
+ foreach(var element; ["windArrow","compassApp","northUp","aplSymMap","aplSymMapCtr","aplSymVor",
+ "staFromL2","staToL2","staFromR2","staToR2",
+ "locPtr","hdgTrk","truMag","altArc","planArcs",
+ "trkInd","compass","HdgBugCRT","TrkBugLCD","HdgBugLCD","selHdgLine","curHdgPtr",
+ "staFromL","staToL","staFromR","staToR"] )
+ me.symbols[element] = me.nd.getElementById(element).updateCenter();
+
+ foreach(var element; ["HdgBugCRT2","TrkBugLCD2","HdgBugLCD2","selHdgLine2","curHdgPtr2","vorCrsPtr2"] )
+ me.symbols[element] = me.nd.getElementById(element).setCenter(512,565);
# this should probably be using Philosopher's new SymbolLayer ?
me.map = me.nd.createChild("map","map")
.set("clip", "rect(124, 1024, 1024, 0)");
# this callback will be passed onto the model via the controller hash, and used for the positioned queries, to specify max query range:
-
var get_range = func me.get_switch('toggle_range');
-
+
# predicate for the draw controller
- var is_tuned = func(freq) {
- var nav1=getprop("instrumentation/nav[0]/frequencies/selected-mhz");
- var nav2=getprop("instrumentation/nav[1]/frequencies/selected-mhz");
- if (freq == nav1 or freq == nav2) return 1;
- return 0;
+ var is_tuned = func(freq) {
+ var nav1=getprop("instrumentation/nav[0]/frequencies/selected-mhz");
+ var nav2=getprop("instrumentation/nav[1]/frequencies/selected-mhz");
+ if (freq == nav1 or freq == nav2) return 1;
+ return 0;
}
# another predicate for the draw controller
var get_course_by_freq = func(freq) {
- if (freq == getprop("instrumentation/nav[0]/frequencies/selected-mhz"))
- return getprop("instrumentation/nav[0]/radials/selected-deg");
- else
- return getprop("instrumentation/nav[1]/radials/selected-deg");
+ if (freq == getprop("instrumentation/nav[0]/frequencies/selected-mhz"))
+ return getprop("instrumentation/nav[0]/radials/selected-deg");
+ else
+ return getprop("instrumentation/nav[1]/radials/selected-deg");
}
var get_current_position = func {
- return [
- me.aircraft_source.get_lat(), me.aircraft_source.get_lon()
- ];
+ return [
+ me.aircraft_source.get_lat(), me.aircraft_source.get_lon()
+ ];
}
# a hash with controller callbacks, will be passed onto draw routines to customize behavior/appearance
# the point being that draw routines don't know anything about their frontends (instrument or GUI dialog)
# so we need some simple way to communicate between frontend<->backend until we have real controllers
# for now, a single controller hash is shared by most layers - unsupported callbacks are simply ignored by the draw files
- #
- var controller = { query_range: func get_range(),
- is_tuned:is_tuned,
- get_tuned_course:get_course_by_freq,
- get_position: get_current_position,
- };
-
+ #
+ var controller = {
+ query_range: func get_range(),
+ is_tuned:is_tuned,
+ get_tuned_course:get_course_by_freq,
+ get_position: get_current_position,
+ };
+
+ # FIXME: MapStructure: big hack
+ canvas.Symbol.Controller.get("VOR").query_range = controller.query_range;
+ canvas.Symbol.Controller.get("VOR").get_tuned_course = controller.get_tuned_course;
+ canvas.Symbol.Controller.get("DME").is_tuned = controller.is_tuned;
+
###
# set up various layers, controlled via callbacks in the controller hash
# revisit this code once Philosopher's "Smart MVC Symbols/Layers" work is committed and integrated
# helper / closure generator
var make_event_handler = func(predicate, layer) func predicate(me, layer);
-
+
me.layers={}; # storage container for all ND specific layers
# look up all required layers as specified per the NDStyle hash and do the initial setup for event handling
-
- foreach(var layer; me.nd_style.layers) {
- print("newMFD(): Setting up ND layer:", layer.name);
- # huge hack for the alt-arc, which is not rendered as a map group, but directly as part of the toplevel ND group
- var render_target = (!contains(layer,'not_a_map') or !layer.not_a_map) ? me.map : me.nd;
- var the_layer = me.layers[layer.name] = canvas.MAP_LAYERS[layer.name].new( render_target, layer.name, controller );
+ foreach(var layer; me.nd_style.layers) {
+ if(layer['disabled']) continue; # skip this layer
+ #print("newMFD(): Setting up ND layer:", layer.name);
+ # huge hack for the alt-arc, which is not rendered as a map group, but directly as part of the toplevel ND group
+ var render_target = (!contains(layer,'not_a_map') or !layer.not_a_map) ? me.map : me.nd;
- # now register all layer specific notification listeners and their corresponding update predicate/callback
- # pass the ND instance and the layer handle to the predicate when it is called
- # so that it can directly access the ND instance and its own layer (without having to know the layer's name)
-
- var event_handler = make_event_handler(layer.predicate, the_layer);
- foreach(var event; layer.update_on) {
- # print("Setting up subscription:", event, " for ", layer.name, " handler id:", id(event_handler) );
- me.listen_switch(event, event_handler ) ;
- } # foreach event subscription
- # and now update/init each layer once by calling its update predicate for initialization
- event_handler();
- } # foreach layer
+ var the_layer = nil;
+ if(!layer['isMapStructure'])
+ the_layer = me.layers[layer.name] = canvas.MAP_LAYERS[layer.name].new( render_target, layer.name, controller );
+ else {
+ #print("Setting up MapStructure-based layer for ND, name:", layer.name);
+ render_target.addLayer(factory: canvas.SymbolLayer, type_arg: layer.name);
+ the_layer = me.layers[layer.name] = render_target.getLayer(layer.name);
+ }
- print("navdisplay.mfd:ND layer setup completed");
+ # now register all layer specific notification listeners and their corresponding update predicate/callback
+ # pass the ND instance and the layer handle to the predicate when it is called
+ # so that it can directly access the ND instance and its own layer (without having to know the layer's name)
+ var event_handler = make_event_handler(layer.predicate, the_layer);
+ foreach(var event; layer.update_on) {
+ # print("Setting up subscription:", event, " for ", layer.name, " handler id:", id(event_handler) );
+ me.listen_switch(event, event_handler);
+ } # foreach event subscription
+ # and now update/init each layer once by calling its update predicate for initialization
+ event_handler();
+ } # foreach layer
- # start the update timer, which makes sure that the update() will be called
+ #print("navdisplay.mfd:ND layer setup completed");
+
+ # start the update timer, which makes sure that the update() will be called
me.update_timer.start();
-
- # next, radio & autopilot & listeners
- # TODO: move this to .init field in layers hash or to model files
- foreach(var n; var radios = [ "instrumentation/nav/frequencies/selected-mhz",
- "instrumentation/nav[1]/frequencies/selected-mhz"])
- me.listen(n, func() {
- me.drawvor();
- me.drawdme();
- });
- # TODO: move this to the route.model
- # Hack to draw the route on rm activation
- me.listen("/autopilot/route-manager/active", func(active) {
- if(active.getValue()) {
- setprop(me.get_full_switch_path('toggle_display_mode'),getprop(me.get_full_switch_path('toggle_display_mode')));
- } else {
- print("TODO: navdisplay.mfd: implement route-manager/layer clearing!");
- #me.route_group.removeAllChildren(); # HACK!
- }
- });
- me.listen("/autopilot/route-manager/current-wp", func(activeWp) {
- canvas.updatewp( activeWp.getValue() );
- });
+
+ # next, radio & autopilot & listeners
+ # TODO: move this to .init field in layers hash or to model files
+ foreach(var n; var radios = [
+ "instrumentation/nav/frequencies/selected-mhz",
+ "instrumentation/nav[1]/frequencies/selected-mhz"])
+ me.listen(n, func() {
+ # me.drawvor();
+ # me.drawdme();
+ });
+ # TODO: move this to the route.model
+ # Hack to draw the route on rm activation
+ me.listen("/autopilot/route-manager/active", func(active) {
+ if(active.getValue()) {
+ me.drawroute();
+ me.drawrunways();
+ } else {
+ #print("TODO: navdisplay.mfd: implement route-manager/layer clearing!");
+ #me.route_group.removeAllChildren(); # HACK!
+ }
+ });
+ me.listen("/autopilot/route-manager/current-wp", func(activeWp) {
+ canvas.updatewp( activeWp.getValue() );
+ });
},
- drawroute: func print("drawroute no longer used!"),
+ drawroute: func print("drawroute no longer used!"),
drawrunways: func print("drawrunways no longer used!"),
-
- in_mode:func(switch, modes) {
- foreach(var m; modes)
- if (me.get_switch(switch)==m) return 1;
+
+ in_mode:func(switch, modes)
+ {
+ foreach(var m; modes) if(me.get_switch(switch)==m) return 1;
return 0;
},
-
# each model should keep track of when it last got updated, using current lat/lon
# in update(), we can then check if the aircraft has traveled more than 0.5-1 nm (depending on selected range)
# and update each model accordingly
update: func() # FIXME: This stuff is still too aircraft specific, cannot easily be reused by other aircraft
{
-
##
# important constants
var m1 = 111132.92;
var m2 = -559.82;
- var m3 = 1.175;
+ var m3 = 1.175;
var m4 = -0.0023;
- var p1 = 111412.84;
- var p2 = -93.5;
- var p3 = 0.118;
+ var p1 = 111412.84;
+ var p2 = -93.5;
+ var p3 = 0.118;
var latNm = 60;
var lonNm = 60;
# fgcommand('profiler-start');
-
# Heading update
- var userHdgMag = me.aircraft_source.get_hdg_mag();
- var userHdgTru = me.aircraft_source.get_hdg_tru();
- var userTrkMag = me.aircraft_source.get_trk_mag();
- var userTrkTru = me.aircraft_source.get_trk_tru();
- if (me.get_switch('toggle_true_north')) {
+ var userHdgMag = me.aircraft_source.get_hdg_mag();
+ var userHdgTru = me.aircraft_source.get_hdg_tru();
+ var userTrkMag = me.aircraft_source.get_trk_mag();
+ var userTrkTru = me.aircraft_source.get_trk_tru();
+ if(me.get_switch('toggle_true_north')) {
me.symbols.truMag.setText("TRU");
var userHdg=userHdgTru;
var userTrk=userTrkTru;
@@ -615,49 +674,105 @@ var NavDisplay = {
var userTrk=userTrkMag;
}
if (me.aircraft_source.get_spd() < 80)
- userTrk = userHdg;
-
+ userTrk = userHdg;
var userLat = me.aircraft_source.get_lat();
var userLon = me.aircraft_source.get_lon();
var userSpd = me.aircraft_source.get_spd();
var userVSpd = me.aircraft_source.get_vspd();
+ var dispLCD = me.get_switch('toggle_display_type') == "LCD";
# this should only ever happen when testing the experimental AI/MP ND driver hash (not critical)
if (!userHdg or !userTrk or !userLat or !userLon) {
print("aircraft source invalid, returning !");
- return;
+ return;
}
-
- if (me.get_switch('toggle_centered') or me.in_mode('toggle_display_mode', ['PLAN']))
+
+ if(me.in_mode('toggle_display_mode', ['PLAN']))
me.map.setTranslation(512,512);
+ elsif(me.get_switch('toggle_centered'))
+ me.map.setTranslation(512,565);
else
me.map.setTranslation(512,824);
-
- # Calculate length in NM of one degree at current location TODO: expose as methods, for external callbacks
+ # Calculate length in NM of one degree at current location TODO: expose as methods, for external callbacks
var userLatR = userLat*D2R;
var userLonR = userLon*D2R;
var latlen = m1 + (m2 * math.cos(2 * userLatR)) + (m3 * math.cos(4 * userLatR)) + (m4 * math.cos(6 * userLatR));
var lonlen = (p1 * math.cos(userLatR)) + (p2 * math.cos(3 * userLatR)) + (p3 * math.cos(5 * userLatR));
latNm = latlen*M2NM; #60 at equator
lonNm = lonlen*M2NM; #60 at equator
-
- me.symbols.windArrow.setRotation((getprop("/environment/wind-from-heading-deg")-userHdgMag)*D2R);
- me.symbols.wind.setText(sprintf("%3.0f / %2.0f",getprop("/environment/wind-from-heading-deg"),getprop("/environment/wind-speed-kt")));
-
- if ((var navid0=getprop("instrumentation/nav/nav-id"))!=nil )
- me.symbols.vorLId.setText(navid0);
- if ((var navid1=getprop("instrumentation/nav[1]/nav-id"))!=nil )
- me.symbols.vorRId.setText(navid1);
- if((var nav0dist=getprop("instrumentation/nav/nav-distance"))!=nil )
- me.symbols.dmeLDist.setText(sprintf("%3.1f",nav0dist*0.000539));
- if((var nav1dist=getprop("instrumentation/nav[1]/nav-distance"))!=nil )
- me.symbols.dmeRDist.setText(sprintf("%3.1f",nav1dist*0.000539));
-
- me.symbols.range.setText(sprintf("%3.0f",me.rangeNm() ));
- # reposition the map, change heading & range:
+ me.symbols.windArrow.setRotation((getprop("/environment/wind-from-heading-deg")-userHdg)*D2R);
+ me.symbols.wind.setText(sprintf("%3.0f / %2.0f",getprop("/environment/wind-from-heading-deg"),getprop("/environment/wind-speed-kt")));
+
+ if(me.get_switch('toggle_lh_vor_adf') == 1)
+ {
+ me.symbols.vorL.setText("VOR L");
+ me.symbols.vorL.setColor(0.195,0.96,0.097);
+ me.symbols.dmeL.setText("DME");
+ me.symbols.dmeL.setColor(0.195,0.96,0.097);
+ if(getprop("instrumentation/nav/in-range"))
+ me.symbols.vorLId.setText(getprop("instrumentation/nav/nav-id"));
+ else
+ me.symbols.vorLId.setText(getprop("instrumentation/nav/frequencies/selected-mhz-fmt"));
+ me.symbols.vorLId.setColor(0.195,0.96,0.097);
+ if(getprop("instrumentation/nav/dme-in-range"))
+ me.symbols.dmeLDist.setText(sprintf("%3.1f",getprop("instrumentation/nav/nav-distance")*0.000539));
+ else me.symbols.dmeLDist.setText(" ---");
+ me.symbols.dmeLDist.setColor(0.195,0.96,0.097);
+ } elsif(me.get_switch('toggle_lh_vor_adf') == -1) {
+ me.symbols.vorL.setText("ADF L");
+ me.symbols.vorL.setColor(0,0.6,0.85);
+ me.symbols.dmeL.setText("");
+ me.symbols.dmeL.setColor(0,0.6,0.85);
+ if((var navident=getprop("instrumentation/adf/ident")) != "")
+ me.symbols.vorLId.setText(navident);
+ else me.symbols.vorLId.setText(sprintf("%3d",getprop("instrumentation/adf/frequencies/selected-khz")));
+ me.symbols.vorLId.setColor(0,0.6,0.85);
+ me.symbols.dmeLDist.setText("");
+ me.symbols.dmeLDist.setColor(0,0.6,0.85);
+ } else {
+ me.symbols.vorL.setText("");
+ me.symbols.dmeL.setText("");
+ me.symbols.vorLId.setText("");
+ me.symbols.dmeLDist.setText("");
+ }
+ if(me.get_switch('toggle_rh_vor_adf') == 1) {
+ me.symbols.vorR.setText("VOR R");
+ me.symbols.vorR.setColor(0.195,0.96,0.097);
+ me.symbols.dmeR.setText("DME");
+ me.symbols.dmeR.setColor(0.195,0.96,0.097);
+ if(getprop("instrumentation/nav[1]/in-range"))
+ me.symbols.vorRId.setText(getprop("instrumentation/nav[1]/nav-id"));
+ else
+ me.symbols.vorRId.setText(getprop("instrumentation/nav[1]/frequencies/selected-mhz-fmt"));
+ me.symbols.vorRId.setColor(0.195,0.96,0.097);
+ if(getprop("instrumentation/nav[1]/dme-in-range"))
+ me.symbols.dmeRDist.setText(sprintf("%3.1f",getprop("instrumentation/nav[1]/nav-distance")*0.000539));
+ else me.symbols.dmeRDist.setText(" ---");
+ me.symbols.dmeRDist.setColor(0.195,0.96,0.097);
+ } elsif(me.get_switch('toggle_rh_vor_adf') == -1) {
+ me.symbols.vorR.setText("ADF R");
+ me.symbols.vorR.setColor(0,0.6,0.85);
+ me.symbols.dmeR.setText("");
+ me.symbols.dmeR.setColor(0,0.6,0.85);
+ if((var navident=getprop("instrumentation/adf[1]/ident")) != "")
+ me.symbols.vorRId.setText(navident);
+ else me.symbols.vorRId.setText(sprintf("%3d",getprop("instrumentation/adf[1]/frequencies/selected-khz")));
+ me.symbols.vorRId.setColor(0,0.6,0.85);
+ me.symbols.dmeRDist.setText("");
+ me.symbols.dmeRDist.setColor(0,0.6,0.85);
+ } else {
+ me.symbols.vorR.setText("");
+ me.symbols.dmeR.setText("");
+ me.symbols.vorRId.setText("");
+ me.symbols.dmeRDist.setText("");
+ }
+
+ me.symbols.range.setText(sprintf("%3.0f",me.rangeNm()/2));
+
+ # reposition the map, change heading & range:
if(me.in_mode('toggle_display_mode', ['PLAN'])) {
- me.symbols.windArrow.hide();
+ me.symbols.windArrow.setVisible(!dispLCD);
me.map._node.getNode("hdg",1).setDoubleValue(0);
if (getprop(me.efis_path ~ "/inputs/plan-wpt-index") >= 0) {
me.map._node.getNode("ref-lat",1).setDoubleValue(getprop("/autopilot/route-manager/route/wp["~getprop(me.efis_path ~ "/inputs/plan-wpt-index")~"]/latitude-deg"));
@@ -668,26 +783,37 @@ var NavDisplay = {
me.map._node.getNode("ref-lat",1).setDoubleValue(userLat);
me.map._node.getNode("ref-lon",1).setDoubleValue(userLon);
}
- me.map._node.getNode("range",1).setDoubleValue(me.rangeNm()/2); # avoid this here, use a listener instead
+ # The set range of the map does not correspond to what we see in-sim!!
+ me.map._node.getNode("range",1).setDoubleValue(me.rangeNm()/3.2); # avoid this here, use a listener instead
+ var vhdg_bug = getprop("autopilot/settings/heading-bug-deg");
if(me.in_mode('toggle_display_mode', ['MAP'])) {
- me.symbols.rotateComp.setRotation(-userTrk*D2R);
- me.symbols.rotateComp2.setRotation(-userTrk*D2R);
+ me.symbols.HdgBugCRT.setRotation((vhdg_bug-userTrk)*D2R);
+ me.symbols.HdgBugLCD.setRotation((vhdg_bug-userTrk)*D2R);
+ me.symbols.TrkBugLCD.setRotation((vhdg_bug-userTrk)*D2R);
+ me.symbols.selHdgLine.setRotation((vhdg_bug-userTrk)*D2R);
+ me.symbols.HdgBugCRT2.setRotation((vhdg_bug-userTrk)*D2R);
+ me.symbols.TrkBugLCD2.setRotation((vhdg_bug-userTrk)*D2R);
+ me.symbols.selHdgLine2.setRotation((vhdg_bug-userTrk)*D2R);
me.symbols.trkInd.setRotation(0);
- me.symbols.curHdgPtr.setRotation(userHdg*D2R);
- me.symbols.curHdgPtr2.setRotation(userHdg*D2R);
- me.map._node.getNode("hdg",1).setDoubleValue(userTrk);
+ me.symbols.curHdgPtr.setRotation((userHdg-userTrk)*D2R);
+ me.symbols.curHdgPtr2.setRotation((userHdg-userTrk)*D2R);
+ me.map._node.getNode("hdg",1).setDoubleValue(userTrkTru);
me.symbols.compass.setRotation(-userTrk*D2R);
me.symbols.compassApp.setRotation(-userTrk*D2R);
me.symbols.hdgTrk.setText("TRK");
}
if(me.in_mode('toggle_display_mode', ['APP','VOR'])) {
- me.symbols.rotateComp.setRotation(-userHdg*D2R);
- me.symbols.rotateComp2.setRotation(-userHdg*D2R);
+ me.symbols.HdgBugCRT.setRotation((vhdg_bug-userHdg)*D2R);
+ me.symbols.HdgBugLCD.setRotation((vhdg_bug-userHdg)*D2R);
+ me.symbols.selHdgLine.setRotation((vhdg_bug-userHdg)*D2R);
+ me.symbols.HdgBugCRT2.setRotation((vhdg_bug-userHdg)*D2R);
+ me.symbols.HdgBugLCD2.setRotation((vhdg_bug-userHdg)*D2R);
+ me.symbols.selHdgLine2.setRotation((vhdg_bug-userHdg)*D2R);
me.symbols.trkInd.setRotation((userTrk-userHdg)*D2R);
- me.symbols.curHdgPtr.setRotation(userHdg*D2R);
- me.symbols.curHdgPtr2.setRotation(userHdg*D2R);
- me.map._node.getNode("hdg",1).setDoubleValue(userHdg);
+ me.symbols.curHdgPtr.setRotation(0);
+ me.symbols.curHdgPtr2.setRotation(0);
+ me.map._node.getNode("hdg",1).setDoubleValue(userHdgTru);
me.symbols.compass.setRotation(-userHdg*D2R);
me.symbols.compassApp.setRotation(-userHdg*D2R);
me.symbols.hdgTrk.setText("HDG");
@@ -698,7 +824,7 @@ var NavDisplay = {
me.symbols.compassApp.show();
if(getprop("instrumentation/nav/in-range")) {
var deflection = getprop("instrumentation/nav/heading-needle-deflection-norm");
- me.symbols.locPtr.show();
+ me.symbols.locPtr.show();
me.symbols.locPtr.setTranslation(deflection*150,0);
if(abs(deflection < 0.99))
me.symbols.locPtr.setColorFill(1,0,1,1);
@@ -719,46 +845,142 @@ var NavDisplay = {
me.symbols.hdgGroup.setTranslation(0,0);
me.symbols.compassApp.hide();
}
-
+
if ((me.get_switch('toggle_centered') and !me.in_mode('toggle_display_mode', ['PLAN'])) or me.in_mode('toggle_display_mode', ['PLAN'])) {
me.symbols.compass.hide();
} else {
me.symbols.compass.show();
}
-
+
var staPtrVis = !me.in_mode('toggle_display_mode', ['APP','PLAN']);
- if (!me.get_switch('toggle_centered') and me.in_mode('toggle_display_mode', ['APP','MAP','VOR'])) {
- me.symbols.trkInd.show();
- me.symbols.staFromL.setVisible(staPtrVis);
- me.symbols.staFromL2.hide();
- me.symbols.staFromR.setVisible(staPtrVis);
- me.symbols.staFromR2.hide();
- me.symbols.staToL.setVisible(staPtrVis);
- me.symbols.staToL2.hide();
- me.symbols.staToR.setVisible(staPtrVis);
- me.symbols.staToR2.hide();
- me.symbols.rotateComp.setVisible(staPtrVis);
- me.symbols.rotateComp2.hide();
- } else {
- me.symbols.trkInd.hide();
- me.symbols.staFromL.hide();
- me.symbols.staFromL2.setVisible(staPtrVis);
- me.symbols.staFromR.hide();
- me.symbols.staFromR2.setVisible(staPtrVis);
- me.symbols.staToL.hide();
- me.symbols.staToL2.setVisible(staPtrVis);
- me.symbols.staToR.hide();
- me.symbols.staToR2.setVisible(staPtrVis);
- me.symbols.rotateComp.hide();
- me.symbols.rotateComp2.setVisible(staPtrVis);
+ var magVar = getprop("environment/magnetic-variation-deg");
+ if(me.in_mode('toggle_display_mode', ['APP','MAP','VOR','PLAN']))
+ {
+ if(getprop("instrumentation/nav/heading-deg") != nil)
+ var nav0hdg=getprop("instrumentation/nav/heading-deg") - userHdg - magVar;
+ if(getprop("instrumentation/nav[1]/heading-deg") != nil)
+ var nav1hdg=getprop("instrumentation/nav[1]/heading-deg") - userHdg - magVar;
+ var adf0hdg=getprop("instrumentation/adf/indicated-bearing-deg");
+ var adf1hdg=getprop("instrumentation/adf[1]/indicated-bearing-deg");
+ if(!me.get_switch('toggle_centered'))
+ {
+ if(me.in_mode('toggle_display_mode', ['PLAN']))
+ me.symbols.trkInd.hide();
+ else
+ me.symbols.trkInd.show();
+ if((getprop("instrumentation/nav/in-range") and me.get_switch('toggle_lh_vor_adf') == 1)) {
+ me.symbols.staFromL.setVisible(staPtrVis);
+ me.symbols.staToL.setVisible(staPtrVis);
+ me.symbols.staFromL.setColor(0.195,0.96,0.097);
+ me.symbols.staToL.setColor(0.195,0.96,0.097);
+ me.symbols.staFromL.setRotation((nav0hdg+180)*D2R);
+ me.symbols.staToL.setRotation(nav0hdg*D2R);
+ }
+ elsif(getprop("instrumentation/adf/in-range") and (me.get_switch('toggle_lh_vor_adf') == -1)) {
+ me.symbols.staFromL.setVisible(staPtrVis);
+ me.symbols.staToL.setVisible(staPtrVis);
+ me.symbols.staFromL.setColor(0,0.6,0.85);
+ me.symbols.staToL.setColor(0,0.6,0.85);
+ me.symbols.staFromL.setRotation((adf0hdg+180)*D2R);
+ me.symbols.staToL.setRotation(adf0hdg*D2R);
+ } else {
+ me.symbols.staFromL.hide();
+ me.symbols.staToL.hide();
+ }
+ if((getprop("instrumentation/nav[1]/in-range") and me.get_switch('toggle_rh_vor_adf') == 1)) {
+ me.symbols.staFromR.setVisible(staPtrVis);
+ me.symbols.staToR.setVisible(staPtrVis);
+ me.symbols.staFromR.setColor(0.195,0.96,0.097);
+ me.symbols.staToR.setColor(0.195,0.96,0.097);
+ me.symbols.staFromR.setRotation((nav1hdg+180)*D2R);
+ me.symbols.staToR.setRotation(nav1hdg*D2R);
+ } elsif(getprop("instrumentation/adf[1]/in-range") and (me.get_switch('toggle_rh_vor_adf') == -1)) {
+ me.symbols.staFromR.setVisible(staPtrVis);
+ me.symbols.staToR.setVisible(staPtrVis);
+ me.symbols.staFromR.setColor(0,0.6,0.85);
+ me.symbols.staToR.setColor(0,0.6,0.85);
+ me.symbols.staFromR.setRotation((adf1hdg+180)*D2R);
+ me.symbols.staToR.setRotation(adf1hdg*D2R);
+ } else {
+ me.symbols.staFromR.hide();
+ me.symbols.staToR.hide();
+ }
+ me.symbols.staFromL2.hide();
+ me.symbols.staToL2.hide();
+ me.symbols.staFromR2.hide();
+ me.symbols.staToR2.hide();
+ me.symbols.curHdgPtr2.hide();
+ me.symbols.HdgBugCRT2.hide();
+ me.symbols.TrkBugLCD2.hide();
+ me.symbols.HdgBugLCD2.hide();
+ me.symbols.selHdgLine2.hide();
+ me.symbols.curHdgPtr.setVisible(staPtrVis);
+ me.symbols.TrkBugLCD.hide();
+ me.symbols.HdgBugCRT.setVisible(staPtrVis and !dispLCD);
+ me.symbols.HdgBugLCD.setVisible(staPtrVis and dispLCD);
+ me.symbols.selHdgLine.setVisible(staPtrVis);
+ } else {
+ me.symbols.trkInd.hide();
+ if((getprop("instrumentation/nav/in-range") and me.get_switch('toggle_lh_vor_adf') == 1)) {
+ me.symbols.staFromL2.setVisible(staPtrVis);
+ me.symbols.staToL2.setVisible(staPtrVis);
+ me.symbols.staFromL2.setColor(0.195,0.96,0.097);
+ me.symbols.staToL2.setColor(0.195,0.96,0.097);
+ me.symbols.staFromL2.setRotation((nav0hdg+180)*D2R);
+ me.symbols.staToL2.setRotation(nav0hdg*D2R);
+ } elsif(getprop("instrumentation/adf/in-range") and (me.get_switch('toggle_lh_vor_adf') == -1)) {
+ me.symbols.staFromL2.setVisible(staPtrVis);
+ me.symbols.staToL2.setVisible(staPtrVis);
+ me.symbols.staFromL2.setColor(0,0.6,0.85);
+ me.symbols.staToL2.setColor(0,0.6,0.85);
+ me.symbols.staFromL2.setRotation((adf0hdg+180)*D2R);
+ me.symbols.staToL2.setRotation(adf0hdg*D2R);
+ } else {
+ me.symbols.staFromL2.hide();
+ me.symbols.staToL2.hide();
+ }
+ if((getprop("instrumentation/nav[1]/in-range") and me.get_switch('toggle_rh_vor_adf') == 1)) {
+ me.symbols.staFromR2.setVisible(staPtrVis);
+ me.symbols.staToR2.setVisible(staPtrVis);
+ me.symbols.staFromR2.setColor(0.195,0.96,0.097);
+ me.symbols.staToR2.setColor(0.195,0.96,0.097);
+ me.symbols.staFromR2.setRotation((nav1hdg+180)*D2R);
+ me.symbols.staToR2.setRotation(nav1hdg*D2R);
+ } elsif(getprop("instrumentation/adf[1]/in-range") and (me.get_switch('toggle_rh_vor_adf') == -1)) {
+ me.symbols.staFromR2.setVisible(staPtrVis);
+ me.symbols.staToR2.setVisible(staPtrVis);
+ me.symbols.staFromR2.setColor(0,0.6,0.85);
+ me.symbols.staToR2.setColor(0,0.6,0.85);
+ me.symbols.staFromR2.setRotation((adf1hdg+180)*D2R);
+ me.symbols.staToR2.setRotation(adf1hdg*D2R);
+ } else {
+ me.symbols.staFromR2.hide();
+ me.symbols.staToR2.hide();
+ }
+ me.symbols.staFromL.hide();
+ me.symbols.staToL.hide();
+ me.symbols.staFromR.hide();
+ me.symbols.staToR.hide();
+ me.symbols.curHdgPtr.hide();
+ me.symbols.HdgBugCRT.hide();
+ me.symbols.TrkBugLCD.hide();
+ me.symbols.HdgBugLCD.hide();
+ me.symbols.selHdgLine.hide();
+ me.symbols.curHdgPtr2.setVisible(staPtrVis);
+ me.symbols.TrkBugLCD2.hide();
+ me.symbols.HdgBugCRT2.setVisible(staPtrVis and !dispLCD);
+ me.symbols.HdgBugLCD2.setVisible(staPtrVis and dispLCD);
+ me.symbols.selHdgLine2.setVisible(staPtrVis);
+ }
}
+
me.symbols.hdgGroup.setVisible(!me.in_mode('toggle_display_mode', ['PLAN']));
me.symbols.northUp.setVisible(me.in_mode('toggle_display_mode', ['PLAN']));
me.symbols.aplSymMap.setVisible(me.in_mode('toggle_display_mode', ['APP','MAP','VOR']) and !me.get_switch('toggle_centered'));
me.symbols.aplSymMapCtr.setVisible(me.in_mode('toggle_display_mode', ['MAP']) and me.get_switch('toggle_centered'));
me.symbols.aplSymVor.setVisible(me.in_mode('toggle_display_mode', ['APP','VOR']) and me.get_switch('toggle_centered'));
me.symbols.planArcs.setVisible(me.in_mode('toggle_display_mode', ['PLAN']));
-
+
if (abs(userVSpd) > 5) {
var altDiff = getprop("autopilot/settings/target-altitude-ft")-getprop("instrumentation/altimeter/indicated-altitude-ft");
if (abs(altDiff) > 50 and altDiff/userVSpd > 0) {
@@ -775,28 +997,7 @@ var NavDisplay = {
} else {
me.symbols.altArc.hide();
}
-
- ## these would require additional arguments to be moved to an external config hash currently
-
- me.symbols.selHdg.setRotation(getprop("autopilot/settings/true-heading-deg")*D2R);
- me.symbols.selHdg2.setRotation(getprop("autopilot/settings/true-heading-deg")*D2R);
- if (var nav0hdg=getprop("instrumentation/nav/heading-deg") != nil) {
- me.symbols.staFromL.setRotation((nav0hdg-userHdgMag+180)*D2R);
- me.symbols.staFromL2.setRotation((nav0hdg-userHdgMag+180)*D2R);
- }
- if (var nav0hdg=getprop("instrumentation/nav/heading-deg") != nil) {
- me.symbols.staToL.setRotation((nav0hdg-userHdgMag)*D2R);
- me.symbols.staToL2.setRotation((nav0hdg-userHdgMag)*D2R);
- }
- if (var nav1hdg=getprop("instrumentation/nav[1]/heading-deg") != nil) {
- me.symbols.staFromR.setRotation((nav1hdg-userHdgMag+180)*D2R);
- me.symbols.staFromR2.setRotation((nav1hdg-userHdgMag+180)*D2R);
- }
- if (var nav1hdg=getprop("instrumentation/nav[1]/heading-deg") != nil) {
- me.symbols.staToR.setRotation((nav1hdg-userHdgMag)*D2R);
- me.symbols.staToR2.setRotation((nav1hdg-userHdgMag)*D2R);
- }
-
+
## run all predicates in the NDStyle hash and evaluate their true/false behavior callbacks
## this is in line with the original design, but normally we don't need to getprop/poll here,
## using listeners or timers would be more canvas-friendly whenever possible
@@ -804,28 +1005,21 @@ var NavDisplay = {
## will be updated at frame rate too - wasteful ... (check the performance monitor!)
foreach(var feature; me.nd_style.features ) {
-
- # for stuff that always needs to be updated
- if (contains(feature.impl, 'common')) feature.impl.common(me);
- # conditional stuff
- if(!contains(feature.impl, 'predicate')) continue; # no conditional stuff
- if ( var result=feature.impl.predicate(me) ) {
- # print("Update predicate true for ", feature.id);
- feature.impl.is_true(me, result); # pass the result to the predicate
- }
- else {
- # print("Update predicate false for ", feature.id);
- feature.impl.is_false( me, result ); # pass the result to the predicate
- }
+ # for stuff that always needs to be updated
+ if (contains(feature.impl, 'common')) feature.impl.common(me);
+ # conditional stuff
+ if(!contains(feature.impl, 'predicate')) continue; # no conditional stuff
+ if ( var result=feature.impl.predicate(me) )
+ feature.impl.is_true(me, result); # pass the result to the predicate
+ else
+ feature.impl.is_false( me, result ); # pass the result to the predicate
}
## update the status flags shown on the ND (wxr, wpt, arpt, sta)
# this could/should be using listeners instead ...
-
me.symbols['status.wxr'].setVisible( me.get_switch('toggle_weather') and me.in_mode('toggle_display_mode', ['MAP']));
me.symbols['status.wpt'].setVisible( me.get_switch('toggle_waypoints') and me.in_mode('toggle_display_mode', ['MAP']));
me.symbols['status.arpt'].setVisible( me.get_switch('toggle_airports') and me.in_mode('toggle_display_mode', ['MAP']));
me.symbols['status.sta'].setVisible( me.get_switch('toggle_stations') and me.in_mode('toggle_display_mode', ['MAP']));
-
}
};
diff --git a/Nasal/canvas/map/route.model b/Nasal/canvas/map/route.model
index ae43efb07..27dfc3487 100644
--- a/Nasal/canvas/map/route.model
+++ b/Nasal/canvas/map/route.model
@@ -4,10 +4,8 @@ RouteModel.new = func make(LayerModel, RouteModel);
RouteModel.init = func {
me._view.reset();
- if (!getprop("/autopilot/route-manager/active"))
- print("Cannot draw route, route manager inactive!") and return;
-
- print("TODO: route.model is still an empty stub, see route.draw instead");
+ if (!getprop("/autopilot/route-manager/active"))
+ return;
## TODO: all the model stuff is still inside the draw file for now, this just ensures that it will be called once
foreach(var t; [nil] )
diff --git a/Shaders/ubershader-gbuffer.frag b/Shaders/ubershader-gbuffer.frag
index ab38c13f9..12732ae51 100644
--- a/Shaders/ubershader-gbuffer.frag
+++ b/Shaders/ubershader-gbuffer.frag
@@ -130,7 +130,7 @@ void main (void)
//////////////////////////////////////////////////////////////////////
//begin DIRT
//////////////////////////////////////////////////////////////////////
- if (dirt_enabled > 0.0){
+ if (dirt_enabled >= 1){
vec3 dirtFactorIn = vec3 (dirt_r_factor, dirt_g_factor, dirt_b_factor);
vec3 dirtFactor = reflmap.rgb * dirtFactorIn.rgb;
//dirtFactor.r = smoothstep(0.0, 1.0, dirtFactor.r);