Merge branch 'master' of gitorious.org:fg/fgdata
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 66 KiB |
|
@ -65,5 +65,13 @@ var PropertyElement = {
|
|||
{
|
||||
me._node.getNode(key, 1).getBoolValue();
|
||||
},
|
||||
|
||||
# Trigger an update of the element
|
||||
#
|
||||
# Elements are automatically updated once a frame, with a delay of one frame.
|
||||
# If you wan't to get an element updated in the current frame you have to use
|
||||
# this method.
|
||||
update: func
|
||||
{
|
||||
me.setBool("update", 1);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -161,6 +161,11 @@ var Element = {
|
|||
|
||||
return factory(parent_ghost);
|
||||
},
|
||||
# Get the canvas this element is placed on
|
||||
getCanvas: func()
|
||||
{
|
||||
wrapCanvas(me._getCanvas());
|
||||
},
|
||||
# Check if elements represent same instance
|
||||
#
|
||||
# @param el Other Element or element ghost
|
||||
|
@ -168,15 +173,6 @@ var Element = {
|
|||
{
|
||||
return me._node.equals(el._node_ghost);
|
||||
},
|
||||
# Trigger an update of the element
|
||||
#
|
||||
# Elements are automatically updated once a frame, with a delay of one frame.
|
||||
# If you wan't to get an element updated in the current frame you have to use
|
||||
# this method.
|
||||
update: func()
|
||||
{
|
||||
me.setInt("update", 1);
|
||||
},
|
||||
# Hide/Show element
|
||||
#
|
||||
# @param visible Whether the element should be visible
|
||||
|
@ -416,6 +412,7 @@ var Group = {
|
|||
# ==============================================================================
|
||||
# Class for a group element on a canvas with possibly geopgraphic positions
|
||||
# which automatically get projected according to the specified projection.
|
||||
# Each map consists of an arbitrary number of layers (canvas groups)
|
||||
#
|
||||
var Map = {
|
||||
df_controller: nil,
|
||||
|
@ -436,7 +433,7 @@ var Map = {
|
|||
me.parents = subvec(me.parents,1);
|
||||
me.del();
|
||||
},
|
||||
setController: func(controller=nil)
|
||||
setController: func(controller=nil, arg...)
|
||||
{
|
||||
if (me.controller != nil) me.controller.del(me);
|
||||
if (controller == nil)
|
||||
|
@ -449,7 +446,7 @@ var Map = {
|
|||
} else {
|
||||
if (!isa(controller, Map.Controller))
|
||||
die("OOP error: controller needs to inherit from Map.Controller");
|
||||
me.controller = call(func controller.new(me), nil, var err=[]); # try...
|
||||
me.controller = call(controller.new, [me]~arg, controller, var err=[]); # try...
|
||||
if (size(err)) {
|
||||
if (err[0] != "No such member: new") # ... and either catch or rethrow
|
||||
die(err[0]);
|
||||
|
@ -463,25 +460,29 @@ var Map = {
|
|||
|
||||
return me;
|
||||
},
|
||||
addLayer: func(factory, type_arg=nil, priority=nil, style=nil, options=nil)
|
||||
addLayer: func(factory, type_arg=nil, priority=nil, style=nil, options=nil, visible=1)
|
||||
{
|
||||
if(contains(me.layers, type_arg))
|
||||
printlog("warn", "addLayer() warning: overwriting existing layer:", type_arg);
|
||||
|
||||
# print("addLayer():", type_arg);
|
||||
|
||||
# Argument handling
|
||||
if (type_arg != nil)
|
||||
if (type_arg != nil) {
|
||||
var layer = factory.new(type:type_arg, group:me, map:me, style:style, options:options, visible:visible);
|
||||
var type = factory.get(type_arg);
|
||||
else var type = factory;
|
||||
var key = type_arg;
|
||||
} else {
|
||||
var layer = factory.new(group:me, map:me, style:style, options:options, visible:visible);
|
||||
var type = factory;
|
||||
var key = factory.type;
|
||||
}
|
||||
me.layers[type_arg] = layer;
|
||||
|
||||
me.layers[type_arg] = type.new(group:me, map:me, style:style,options:options);
|
||||
if (priority == nil)
|
||||
priority = type.df_priority;
|
||||
if (priority != nil)
|
||||
me.layers[type_arg].group.setInt("z-index", priority);
|
||||
layer.group.setInt("z-index", priority);
|
||||
|
||||
return me;
|
||||
return layer; # return new layer to caller() so that we can directly work with it, i.e. to register event handlers (panning/zooming)
|
||||
},
|
||||
getLayer: func(type_arg) me.layers[type_arg],
|
||||
|
||||
|
@ -490,6 +491,7 @@ var Map = {
|
|||
|
||||
setPos: func(lat, lon, hdg=nil, range=nil, alt=nil)
|
||||
{
|
||||
# TODO: also propage setPos events to layers and symbols (e.g. for offset maps)
|
||||
me.set("ref-lat", lat);
|
||||
me.set("ref-lon", lon);
|
||||
if (hdg != nil)
|
||||
|
@ -497,7 +499,7 @@ var Map = {
|
|||
if (range != nil)
|
||||
me.setRange(range);
|
||||
if (alt != nil)
|
||||
me.set("altitude", hdg);
|
||||
me.set("altitude", alt);
|
||||
},
|
||||
getPos: func
|
||||
{
|
||||
|
@ -513,6 +515,10 @@ var Map = {
|
|||
getAlt: func me.get("altitude"),
|
||||
getRange: func me.get("range"),
|
||||
getLatLon: func [me.get("ref-lat"), me.get("ref-lon")],
|
||||
# N.B.: This always returns the same geo.Coord object,
|
||||
# so its values can and will change at any time (call
|
||||
# update() on the coord to ensure it is up-to-date,
|
||||
# which basically calls this method again).
|
||||
getPosCoord: func
|
||||
{
|
||||
var (lat, lon) = (me.get("ref-lat"),
|
||||
|
@ -526,6 +532,8 @@ var Map = {
|
|||
}
|
||||
if (!contains(me, "coord")) {
|
||||
me.coord = geo.Coord.new();
|
||||
var m = me;
|
||||
me.coord.update = func m.getPosCoord();
|
||||
}
|
||||
me.coord.set_latlon(lat,lon,alt or 0);
|
||||
return me.coord;
|
||||
|
@ -534,12 +542,14 @@ var Map = {
|
|||
# me.controller.
|
||||
update: func(predicate=nil)
|
||||
{
|
||||
var t = systime();
|
||||
foreach (var l; keys(me.layers)) {
|
||||
var layer = me.layers[l];
|
||||
# Only update if the predicate allows
|
||||
if (predicate == nil or predicate(layer))
|
||||
call(layer.update, arg, layer);
|
||||
layer.update();
|
||||
}
|
||||
printlog(_MP_dbg_lvl, "Took "~((systime()-t)*1000)~"ms to update map()");
|
||||
return me;
|
||||
},
|
||||
};
|
||||
|
@ -745,6 +755,24 @@ var Path = {
|
|||
|
||||
return me;
|
||||
},
|
||||
addSegmentGeo: func(cmd, coords...)
|
||||
{
|
||||
var coords = _arg2valarray(coords);
|
||||
var num_coords = me.num_coords[cmd];
|
||||
if( size(coords) != num_coords )
|
||||
debug.warn
|
||||
(
|
||||
"Invalid number of arguments (expected " ~ num_coords ~ ")"
|
||||
);
|
||||
else
|
||||
{
|
||||
me.setInt("cmd[" ~ (me._last_cmd += 1) ~ "]", cmd);
|
||||
for(var i = 0; i < num_coords; i += 1)
|
||||
me.set("coord-geo[" ~ (me._last_coord += 1) ~ "]", coords[i]);
|
||||
}
|
||||
|
||||
return me;
|
||||
},
|
||||
# Remove first segment
|
||||
pop_front: func me._removeSegment(1),
|
||||
# Remove last segment
|
||||
|
@ -981,8 +1009,22 @@ var Image = {
|
|||
# @param bottom Rectangle maximum y coordinate
|
||||
# @param normalized Whether to use normalized ([0,1]) or image
|
||||
# ([0, image_width]/[0, image_height]) coordinates
|
||||
setSourceRect: func(left, top, right, bottom, normalized = 1)
|
||||
setSourceRect: func
|
||||
{
|
||||
# Work with both positional arguments and named arguments.
|
||||
# Support first argument being a vector instead of four separate ones.
|
||||
if (size(arg) == 1)
|
||||
arg = arg[0];
|
||||
elsif (size(arg) and size(arg) < 4 and typeof(arg[0]) == 'vector')
|
||||
arg = arg[0]~arg[1:];
|
||||
if (!contains(caller(0)[0], "normalized")) {
|
||||
if (size(arg) > 4)
|
||||
var normalized = arg[4];
|
||||
else var normalized = 1;
|
||||
}
|
||||
if (size(arg) >= 3)
|
||||
var (left,top,right,bottom) = arg;
|
||||
|
||||
me._node.getNode("source", 1).setValues({
|
||||
left: left,
|
||||
top: top,
|
||||
|
@ -1033,7 +1075,7 @@ var Canvas = {
|
|||
# given every texture of the model will be replaced.
|
||||
addPlacement: func(vals)
|
||||
{
|
||||
var placement = me.texture.addChild("placement", 0, 0);
|
||||
var placement = me._node.addChild("placement", 0, 0);
|
||||
placement.setValues(vals);
|
||||
return placement;
|
||||
},
|
||||
|
@ -1052,31 +1094,32 @@ var Canvas = {
|
|||
# Set the background color
|
||||
#
|
||||
# @param color Vector of 3 or 4 values in [0, 1]
|
||||
setColorBackground: func () { me.texture.getNode('background', 1).setValue(_getColor(arg)); me; },
|
||||
getColorBackground: func me.texture.get('background'),
|
||||
setColorBackground: func me.set('background', _getColor(arg)),
|
||||
getColorBackground: func me.get('background'),
|
||||
# Get path of canvas to be used eg. in Image::setFile
|
||||
getPath: func()
|
||||
{
|
||||
return "canvas://by-index/texture[" ~ me.texture.getIndex() ~ "]";
|
||||
return "canvas://by-index/texture[" ~ me._node.getIndex() ~ "]";
|
||||
},
|
||||
# Destructor
|
||||
#
|
||||
# releases associated canvas and makes this object unusable
|
||||
del: func
|
||||
{
|
||||
me.texture.remove();
|
||||
me._node.remove();
|
||||
me.parents = nil; # ensure all ghosts get destroyed
|
||||
}
|
||||
};
|
||||
|
||||
var wrapCanvas = func(canvas_ghost)
|
||||
# @param g Canvas ghost
|
||||
var wrapCanvas = func(g)
|
||||
{
|
||||
var m = {
|
||||
parents: [PropertyElement, Canvas, canvas_ghost],
|
||||
texture: props.wrapNode(canvas_ghost._node_ghost)
|
||||
};
|
||||
m._node = m.texture;
|
||||
return m;
|
||||
if( g != nil and g._impl == nil )
|
||||
g._impl = {
|
||||
parents: [PropertyElement, Canvas],
|
||||
_node: props.wrapNode(g._node_ghost)
|
||||
};
|
||||
return g;
|
||||
}
|
||||
|
||||
# Create a new canvas. Pass parameters as hash, eg:
|
||||
|
@ -1090,7 +1133,7 @@ var wrapCanvas = func(canvas_ghost)
|
|||
var new = func(vals)
|
||||
{
|
||||
var m = wrapCanvas(_newCanvasGhost());
|
||||
m.texture.setValues(vals);
|
||||
m._node.setValues(vals);
|
||||
return m;
|
||||
};
|
||||
|
||||
|
@ -1108,11 +1151,7 @@ var get = func(arg)
|
|||
else
|
||||
die("canvas.new: Invalid argument.");
|
||||
|
||||
var canvas_ghost = _getCanvasGhost(node._g);
|
||||
if( canvas_ghost == nil )
|
||||
return nil;
|
||||
|
||||
return wrapCanvas(canvas_ghost);
|
||||
return wrapCanvas(_getCanvasGhost(node._g));
|
||||
};
|
||||
|
||||
var getDesktop = func()
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
Nothing set in stone yet, we'll document things once the API becomes more stable and once it has been used in several dialogs and instruments
|
||||
|
||||
At the moment, this implements the notion of a "LayeredMap", a LayeredMap is a conventional Canvas Map which has support for easily managing "Layers",
|
||||
which are internally mapped to Canvas Groups. Each Group's "visible" property is managed by the LayeredMap, so that layers can be easily
|
||||
toggled on/off, i.e. via checkboxes.
|
||||
|
||||
Basically, the idea is this, we'll have a MVC (Model/View/Controller) setup, where:
|
||||
- the Model is mapped to the drawable's meta information (i.e. position)
|
||||
- the View is mapped to a conventional canvas group
|
||||
- the Controller is mapped to a bunch of property/timer callbacks to control the Model/View
|
||||
|
||||
|
||||
|
||||
|
||||
Model = PositionedSource
|
||||
View = Canvas
|
||||
Controller = control properties (zoom, range etc)
|
||||
|
||||
LayerElement = callback to create a canvas group
|
||||
Layer = canvas.Group
|
||||
|
||||
Map -> LayeredMap -> GenericMap -> AirportMap
|
||||
|
|
@ -31,13 +31,14 @@ var WindowButton = {
|
|||
_onStateChange: func
|
||||
{
|
||||
var file = style._dir_decoration ~ "/" ~ me._name;
|
||||
file ~= me._window._focused ? "_focused" : "_unfocused";
|
||||
var window_focus = me._windowFocus();
|
||||
file ~= window_focus ? "_focused" : "_unfocused";
|
||||
|
||||
if( me._active )
|
||||
file ~= "_pressed";
|
||||
else if( me._hover )
|
||||
file ~= "_prelight";
|
||||
else if( me._window._focused )
|
||||
else if( window_focus )
|
||||
file ~= "_normal";
|
||||
|
||||
me._root.set("src", file ~ ".png");
|
||||
|
@ -53,6 +54,7 @@ var Window = {
|
|||
var ghost = _newWindowGhost(id);
|
||||
var m = {
|
||||
parents: [Window, PropertyElement, ghost],
|
||||
_ghost: ghost,
|
||||
_node: props.wrapNode(ghost._node_ghost),
|
||||
_focused: 0,
|
||||
_focused_widget: nil,
|
||||
|
@ -80,7 +82,7 @@ var Window = {
|
|||
|
||||
if( me["_canvas"] != nil )
|
||||
{
|
||||
var placements = me._canvas.texture.getChildren("placement");
|
||||
var placements = me._canvas._node.getChildren("placement");
|
||||
# Do not remove canvas if other placements exist
|
||||
if( size(placements) > 1 )
|
||||
foreach(var p; placements)
|
||||
|
@ -124,7 +126,10 @@ var Window = {
|
|||
"blend-destination-alpha": "one"
|
||||
});
|
||||
|
||||
me._canvas._focused_widget = nil;
|
||||
me._canvas.data("focused", me._focused);
|
||||
me._canvas.addEventListener("mousedown", func me.raise());
|
||||
|
||||
return me._canvas;
|
||||
},
|
||||
# Set an existing canvas to be used for this Window
|
||||
|
@ -135,26 +140,30 @@ var Window = {
|
|||
|
||||
canvas_.addPlacement({type: "window", "id": me.get("id")});
|
||||
me['_canvas'] = canvas_;
|
||||
canvas_.data("focused", me._focused);
|
||||
|
||||
# prevent resizing if canvas is placed from somewhere else
|
||||
me.onResize = nil;
|
||||
},
|
||||
# Get the displayed canvas
|
||||
getCanvas: func()
|
||||
getCanvas: func(create = 0)
|
||||
{
|
||||
if( me['_canvas'] == nil and create )
|
||||
me.createCanvas();
|
||||
|
||||
return me['_canvas'];
|
||||
},
|
||||
getCanvasDecoration: func()
|
||||
{
|
||||
return wrapCanvas(me._getCanvasDecoration());
|
||||
},
|
||||
addWidget: func(w)
|
||||
setLayout: func(l)
|
||||
{
|
||||
append(me._widgets, w);
|
||||
w._window = me;
|
||||
if( size(me._widgets) == 2 )
|
||||
w.setFocus();
|
||||
w._onStateChange();
|
||||
if( me['_canvas'] == nil )
|
||||
me.createCanvas();
|
||||
|
||||
me._canvas.update(); # Ensure placement is applied
|
||||
me._ghost.setLayout(l);
|
||||
return me;
|
||||
},
|
||||
#
|
||||
|
@ -223,6 +232,8 @@ var Window = {
|
|||
# protected:
|
||||
_onStateChange: func
|
||||
{
|
||||
var event = canvas.CustomEvent.new("wm.focus-" ~ (me._focused ? "in" : "out"));
|
||||
|
||||
if( me._getCanvasDecoration() != nil )
|
||||
{
|
||||
# Stronger shadow for focused windows
|
||||
|
@ -233,10 +244,16 @@ var Window = {
|
|||
me._title_bar_bg.set("fill", style.getColor("title" ~ suffix));
|
||||
me._title.set( "fill", style.getColor("title-text" ~ suffix));
|
||||
me._top_line.set( "stroke", style.getColor("title-highlight" ~ suffix));
|
||||
|
||||
me.getCanvasDecoration()
|
||||
.data("focused", me._focused)
|
||||
.dispatchEvent(event);
|
||||
}
|
||||
|
||||
foreach(var w; me._widgets)
|
||||
w._onStateChange();
|
||||
if( me.getCanvas() != nil )
|
||||
me.getCanvas()
|
||||
.data("focused", me._focused)
|
||||
.dispatchEvent(event);
|
||||
},
|
||||
# private:
|
||||
_propCallback: func(child, mode)
|
||||
|
@ -388,7 +405,6 @@ var Window = {
|
|||
var button_close = WindowButton.new(title_bar, "close")
|
||||
.move(x, y);
|
||||
button_close.onClick = func me.del();
|
||||
me.addWidget(button_close);
|
||||
|
||||
# title
|
||||
me._title = title_bar.createChild("text", "title")
|
||||
|
@ -435,6 +451,32 @@ var Dialog = {
|
|||
}
|
||||
};
|
||||
|
||||
var createLayoutTest = func
|
||||
{
|
||||
var dlg = canvas.Window.new([350,250], "dialog")
|
||||
.set("resize", 1);
|
||||
dlg.getCanvas(1)
|
||||
.set("background", style.getColor("bg_color"));
|
||||
var root = dlg.getCanvas().createGroup();
|
||||
|
||||
var vbox = VBoxLayout.new();
|
||||
dlg.setLayout(vbox);
|
||||
|
||||
var b = gui.widgets.Button.new(root, style, {}).setText("Stretch");
|
||||
b.setMaximumSize([9999, 9999]);
|
||||
vbox.addItem(b, 1);
|
||||
|
||||
var button_box = HBoxLayout.new();
|
||||
vbox.addItem(button_box);
|
||||
|
||||
var b1 = gui.widgets.Button.new(root, style, {}).setText("Ok");
|
||||
button_box.addItem(b1);
|
||||
b1.setFocus();
|
||||
|
||||
var b2 = gui.widgets.Button.new(root, style, {}).setText("Abort");
|
||||
button_box.addItem(b2);
|
||||
}
|
||||
|
||||
# Canvas GUI demo
|
||||
#
|
||||
# Shows an icon in the top-right corner which upon click opens a simple window
|
||||
|
@ -453,11 +495,13 @@ var initDemo = func
|
|||
{
|
||||
debug.dump( props.wrapNode(event.target._node_ghost) );
|
||||
});
|
||||
my_canvas.addEventListener("click", func
|
||||
my_canvas.addEventListener("click", func(e)
|
||||
{
|
||||
if( e.button == 1 )
|
||||
return createLayoutTest();
|
||||
|
||||
var dlg = canvas.Window.new([400,300], "dialog")
|
||||
.set("resize", 1);
|
||||
|
||||
var my_canvas = dlg.createCanvas()
|
||||
.set("background", style.getColor("bg_color"));
|
||||
|
||||
|
@ -466,6 +510,7 @@ var initDemo = func
|
|||
my_canvas.addEventListener("drag", func(e) { printf("drag: screen(%.1f|%.1f) client(%.1f|%.1f) local(%.1f|%.1f) delta(%.1f|%.1f)", e.screenX, e.screenY, e.clientX, e.clientY, e.localX, e.localY, e.deltaX, e.deltaY); });
|
||||
my_canvas.addEventListener("wheel", func(e) { printf("wheel: screen(%.1f|%.1f) client(%.1f|%.1f) %.1f", e.screenX, e.screenY, e.clientX, e.clientY, e.deltaY); });
|
||||
var root = my_canvas.createGroup();
|
||||
root.addEventListener("test", func(e) { printf("test: %s", e.detail.test); });
|
||||
root.createChild("image")
|
||||
.set("src", "http://wiki.flightgear.org/skins/common/images/icons-fg-135.png");
|
||||
var text =
|
||||
|
@ -487,27 +532,27 @@ root.createChild("image")
|
|||
.set("fill", "#ff0000")
|
||||
.hide();
|
||||
var visible_count = 0;
|
||||
text.addEventListener("click", func root.dispatchEvent(canvas.CustomEvent.new("test", {detail: {"test": "some important data.."}})));
|
||||
text.addEventListener("mouseover", func text_move.show());
|
||||
text.addEventListener("mouseout", func text_move.hide());
|
||||
text.addEventListener("mousemove", func(e) { printf("move: screen(%.1f|%.1f) client(%.1f|%.1f) local(%.1f|%.1f) delta(%.1f|%.1f)", e.screenX, e.screenY, e.clientX, e.clientY, e.localX, e.localY, e.deltaX, e.deltaY); });
|
||||
text.set("fill", style.getColor("text_color"));
|
||||
|
||||
dlg.addWidget( gui.widgets.Button.new(root, style, {size: [64, 26]})
|
||||
.setText("Ok")
|
||||
.move(20, 250) );
|
||||
dlg.addWidget( gui.widgets.Button.new(root, style, {size: [64, 26]})
|
||||
.setText("Apply")
|
||||
.move(100, 250) );
|
||||
dlg.addWidget( gui.widgets.Button.new(root, style, {size: [64, 64]})
|
||||
.setText("Cancel")
|
||||
.move(180, 200) );
|
||||
gui.widgets.Button.new(root, style, {size: [64, 26]})
|
||||
.setText("Ok")
|
||||
.move(20, 250);
|
||||
gui.widgets.Button.new(root, style, {size: [64, 26]})
|
||||
.setText("Apply")
|
||||
.move(100, 250);
|
||||
gui.widgets.Button.new(root, style, {size: [64, 64]})
|
||||
.setText("Cancel")
|
||||
.move(180, 200);
|
||||
|
||||
var scroll = gui.widgets.ScrollArea.new(root, style, {size: [96, 128]})
|
||||
.move(20, 100);
|
||||
var txt = scroll.getContent().createChild("text")
|
||||
.set("text", "01hallo\n02asdasd\n03\n04\n05asdasd06\n07ß\n08\n09asdasd\n10\n11");
|
||||
scroll.update();
|
||||
dlg.addWidget(scroll);
|
||||
|
||||
txt.addEventListener("mouseover", func txt.set("fill", "red"));
|
||||
txt.addEventListener("mouseout", func txt.set("fill", "green"));
|
||||
|
|
|
@ -8,14 +8,14 @@ gui.Widget = {
|
|||
#
|
||||
new: func(derived)
|
||||
{
|
||||
return {
|
||||
return canvas.Widget.new({
|
||||
parents: [derived, gui.Widget],
|
||||
_focused: 0,
|
||||
_focus_policy: gui.Widget.NoFocus,
|
||||
_hover: 0,
|
||||
_root: nil,
|
||||
_size: [64, 64]
|
||||
};
|
||||
});
|
||||
},
|
||||
# Move the widget to the given position (relative to its parent)
|
||||
move: func(x, y)
|
||||
|
@ -35,11 +35,12 @@ gui.Widget = {
|
|||
if( me._focused )
|
||||
return me;
|
||||
|
||||
if( me._window._focused_widget != nil )
|
||||
me._window._focused_widget.clearFocus();
|
||||
var canvas = me.getCanvas();
|
||||
if( canvas._focused_widget != nil )
|
||||
canvas._focused_widget.clearFocus();
|
||||
|
||||
me._focused = 1;
|
||||
me._window._focused_widget = me;
|
||||
canvas._focused_widget = me;
|
||||
|
||||
me.onFocusIn();
|
||||
me._onStateChange();
|
||||
|
@ -53,7 +54,7 @@ gui.Widget = {
|
|||
return me;
|
||||
|
||||
me._focused = 0;
|
||||
me._window._focused_widget = nil;
|
||||
me.getCanvas()._focused_widget = nil;
|
||||
|
||||
me.onFocusOut();
|
||||
me._onStateChange();
|
||||
|
@ -65,10 +66,22 @@ gui.Widget = {
|
|||
onMouseEnter: func {},
|
||||
onMouseLeave: func {},
|
||||
# protected:
|
||||
_MAX_SIZE: 32768, # size for "no size-limit"
|
||||
_onStateChange: func {},
|
||||
_setRoot: func(el)
|
||||
{
|
||||
me._root = el;
|
||||
|
||||
var canvas = el.getCanvas();
|
||||
me.setCanvas(canvas);
|
||||
|
||||
canvas.addEventListener("wm.focus-in", func {
|
||||
me._onStateChange();
|
||||
});
|
||||
canvas.addEventListener("wm.focus-out", func {
|
||||
me._onStateChange();
|
||||
});
|
||||
|
||||
el.addEventListener("mouseenter", func {
|
||||
me._hover = 1;
|
||||
me.onMouseEnter();
|
||||
|
@ -76,15 +89,17 @@ gui.Widget = {
|
|||
});
|
||||
el.addEventListener("mousedown", func {
|
||||
if( bits.test(me._focus_policy, me.ClickFocus / 2) )
|
||||
{
|
||||
me.setFocus();
|
||||
me._window.setFocus();
|
||||
}
|
||||
});
|
||||
el.addEventListener("mouseleave", func {
|
||||
me._hover = 0;
|
||||
me.onMouseLeave();
|
||||
me._onStateChange();
|
||||
});
|
||||
},
|
||||
_windowFocus: func
|
||||
{
|
||||
var canvas = me.getCanvas();
|
||||
return canvas != nil ? canvas.data("focused") : 0;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -28,23 +28,29 @@ DefaultStyle.widgets.button = {
|
|||
new: func(parent, cfg)
|
||||
{
|
||||
me.element = parent.createChild("group", "button");
|
||||
me.size = cfg.get("size", [26, 26]);
|
||||
|
||||
me._bg =
|
||||
me.element.rect( 3,
|
||||
3,
|
||||
me.size[0] - 6,
|
||||
me.size[1] - 6,
|
||||
{"border-radius": 5} );
|
||||
me.element.createChild("path");
|
||||
me._border =
|
||||
me.element.createChild("image", "button")
|
||||
.set("slice", "10 12") #"7")
|
||||
.setSize(me.size);
|
||||
.set("slice", "10 12"); #"7")
|
||||
me._label =
|
||||
me.element.createChild("text")
|
||||
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
|
||||
.set("character-size", 14)
|
||||
.set("alignment", "center-baseline");
|
||||
|
||||
me.setSize( cfg.get("size", [26, 26]) );
|
||||
},
|
||||
setSize: func(size)
|
||||
{
|
||||
me._bg.reset()
|
||||
.rect( 3,
|
||||
3,
|
||||
size[0] - 6,
|
||||
size[1] - 6,
|
||||
{"border-radius": 5} );
|
||||
me._border.setSize(size);
|
||||
me.size = size;
|
||||
},
|
||||
setText: func(text)
|
||||
{
|
||||
|
|
|
@ -13,6 +13,12 @@ gui.widgets.Button = {
|
|||
m._setRoot(m._button.element);
|
||||
}
|
||||
|
||||
m.setMinimumSize([16, 16]);
|
||||
m.setSizeHint([32, 32]);
|
||||
m.setMaximumSize([m._MAX_SIZE, m._MAX_SIZE]);
|
||||
|
||||
m.setSetGeometryFunc(m.setGeometry);
|
||||
|
||||
return m;
|
||||
},
|
||||
setText: func(text)
|
||||
|
@ -39,11 +45,18 @@ gui.widgets.Button = {
|
|||
return me;
|
||||
},
|
||||
onClick: func {},
|
||||
setGeometry: func(geom)
|
||||
{
|
||||
me.move(geom[0], geom[1]);
|
||||
me._button.setSize([geom[2] - geom[0], geom[3] - geom[1]]);
|
||||
me._onStateChange();
|
||||
return me;
|
||||
},
|
||||
# protected:
|
||||
_onStateChange: func
|
||||
{
|
||||
if( me._button != nil )
|
||||
me._button.update(me._active, me._focused, me._hover, !me._window._focused);
|
||||
me._button.update(me._active, me._focused, me._hover, !me._windowFocus());
|
||||
},
|
||||
_setRoot: func(el)
|
||||
{
|
||||
|
|
46
Nasal/canvas/map/ALT-profile.lcontroller
Normal file
|
@ -0,0 +1,46 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# Class things:
|
||||
var name = 'ALT-profile';
|
||||
var parents = [SymbolLayer.Controller];
|
||||
var __self__ = caller(0)[0];
|
||||
SymbolLayer.Controller.add(name, __self__);
|
||||
SymbolLayer.add(name, {
|
||||
parents: [MultiSymbolLayer],
|
||||
type: name, # Symbol type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
df_options: { # default configuration options
|
||||
active_node: "/autopilot/route-manager/active",
|
||||
vnav_node: "/autopilot/route-manager/vnav/",
|
||||
types: ["tc", "td", "sc", "ed"],
|
||||
}
|
||||
});
|
||||
var new = func(layer) {
|
||||
var m = {
|
||||
parents: [__self__],
|
||||
layer: layer,
|
||||
map: layer.map,
|
||||
listeners: [],
|
||||
};
|
||||
layer.searcher._equals = func(a,b) a.getName() == b.getName();
|
||||
append(m.listeners, setlistener(layer.options.active_node, func m.layer.update() ));
|
||||
m.addVisibilityListener();
|
||||
|
||||
return m;
|
||||
};
|
||||
var del = func() {
|
||||
foreach (var l; me.listeners)
|
||||
removelistener(l);
|
||||
};
|
||||
|
||||
var searchCmd = func {
|
||||
var results = [];
|
||||
var symNode = props.globals.getNode(me.layer.options.vnav_node);
|
||||
if (symNode != nil)
|
||||
foreach (var t; me.layer.options.types) {
|
||||
var n = symNode.getNode(t);
|
||||
if (n != nil)
|
||||
append(results, n);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
32
Nasal/canvas/map/ALT-profile.symbol
Normal file
|
@ -0,0 +1,32 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# Class things:
|
||||
var name = 'ALT-profile';
|
||||
var parents = [DotSym];
|
||||
var __self__ = caller(0)[0];
|
||||
DotSym.makeinstance( name, __self__ );
|
||||
|
||||
SymbolLayer.get(name).df_style = {
|
||||
radius: 13,
|
||||
};
|
||||
|
||||
var element_type = "group";
|
||||
|
||||
var init = func {
|
||||
var name = me.model.getName();
|
||||
var disptext = chr(string.toupper(name[0]))~"/"~chr(string.toupper(name[1]));
|
||||
var radius = me.style.radius;
|
||||
me.element.createChild("path")
|
||||
.setStrokeLineWidth(5)
|
||||
.moveTo(-radius, 0)
|
||||
.arcLargeCW(radius, radius, 0, 2 * radius, 0)
|
||||
.arcLargeCW(radius, radius, 0, -2 * radius, 0)
|
||||
.setColor(0.195,0.96,0.097);
|
||||
me.element.createChild("text")
|
||||
.setDrawMode( canvas.Text.TEXT )
|
||||
.setText(disptext)
|
||||
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
|
||||
.setFontSize(28)
|
||||
.setTranslation(25,35)
|
||||
.setColor(0.195,0.96,0.097);
|
||||
}
|
||||
|
|
@ -5,23 +5,18 @@ var parents = [SymbolLayer.Controller];
|
|||
var __self__ = caller(0)[0];
|
||||
SymbolLayer.Controller.add(name, __self__);
|
||||
SymbolLayer.add(name, {
|
||||
parents: [SymbolLayer],
|
||||
parents: [SingleSymbolLayer],
|
||||
type: name, # Symbol type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
df_style: {},
|
||||
});
|
||||
# N.B.: if used, this SymbolLayer should be updated every frame
|
||||
# by the Map Controller, or as often as the position is changed.
|
||||
var new = func(layer) {
|
||||
layer.searcher._equals = func(a,b) {
|
||||
a == b;
|
||||
}
|
||||
return {
|
||||
parents: [__self__],
|
||||
map: layer.map,
|
||||
_model: layer.map.getPosCoord(),
|
||||
};
|
||||
};
|
||||
var del = func;
|
||||
|
||||
var searchCmd = func {
|
||||
var c = me.map.getPosCoord();
|
||||
return [c];
|
||||
};
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# Class things:
|
||||
var name = 'APS';
|
||||
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
|
||||
|
|
@ -1,16 +1,19 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# Class things:
|
||||
var name = 'APS';
|
||||
var parents = [DotSym];
|
||||
var parents = [SVGSymbol];
|
||||
var __self__ = caller(0)[0];
|
||||
DotSym.makeinstance( name, __self__ );
|
||||
|
||||
var element_type = "group";
|
||||
var svg_path = "Nasal/canvas/map/Images/boeingAirplane.svg";
|
||||
var element_id = "airplane";
|
||||
|
||||
var init = func {
|
||||
canvas.parsesvg(me.element, "Nasal/canvas/map/boeingAirplane.svg");
|
||||
me.draw();
|
||||
# Rotate with the main aircraft.
|
||||
# Will have to be adapted if intended for use with other aircraft
|
||||
# (but one could simply copy the layer for that).
|
||||
var draw = func {
|
||||
var rot = getprop("/orientation/heading-deg");
|
||||
rot -= me.layer.map.getHdg();
|
||||
me.element.setRotation(rot*D2R);
|
||||
};
|
||||
var draw = func me.element.setRotation(me.layer.map.getHdg());
|
||||
|
||||
|
|
|
@ -5,9 +5,18 @@ var parents = [SymbolLayer.Controller];
|
|||
var __self__ = caller(0)[0];
|
||||
SymbolLayer.Controller.add(name, __self__);
|
||||
SymbolLayer.add(name, {
|
||||
parents: [SymbolLayer],
|
||||
parents: [MultiSymbolLayer],
|
||||
type: name, # Symbol type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
df_style: {
|
||||
line_width: 3,
|
||||
scale_factor: 1,
|
||||
debug: 1,
|
||||
color_default: [0,0.6,0.85],
|
||||
label_font_color:[0,0.6,0.85],
|
||||
label_font_size: 28,
|
||||
},
|
||||
|
||||
});
|
||||
var a_instance = nil;
|
||||
var new = func(layer) {
|
||||
|
@ -17,7 +26,8 @@ var new = func(layer) {
|
|||
map: layer.map,
|
||||
listeners: [],
|
||||
};
|
||||
__self__.a_instance = m;
|
||||
m.addVisibilityListener();
|
||||
|
||||
return m;
|
||||
};
|
||||
var del = func() {
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# Class things:
|
||||
var name = 'APT';
|
||||
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" );
|
||||
|
|
@ -6,47 +6,28 @@ 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 icon_apt = nil;
|
||||
var text_apt = nil;
|
||||
|
||||
# add the draw routine from airports-nd.draw here
|
||||
var draw = func {
|
||||
if (me.icon_fix != nil) return;
|
||||
var icon_apt = me.element.createChild("path", name ~ " icon" )
|
||||
.moveTo(-17,0)
|
||||
.arcSmallCW(17,17,0,34,0)
|
||||
.arcSmallCW(17,17,0,-34,0)
|
||||
.close()
|
||||
.setColor(0,0.6,0.85)
|
||||
.setStrokeLineWidth(3);
|
||||
var text_apt = me.element.createChild("text", name ~ " label")
|
||||
.setDrawMode( canvas.Text.TEXT )
|
||||
.setTranslation(17,35)
|
||||
.setText(me.model.id)
|
||||
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
|
||||
.setColor(0,0.6,0.85)
|
||||
.setFontSize(28);
|
||||
#me.element.setGeoPosition(lat, lon)
|
||||
# .set("z-index",1); # FIXME: this needs to be configurable!!
|
||||
|
||||
# disabled:
|
||||
if(0) {
|
||||
# the fix symbol
|
||||
me.icon_fix = me.element.createChild("path")
|
||||
.moveTo(-15,15)
|
||||
.lineTo(0,-15)
|
||||
.lineTo(15,15)
|
||||
var init = func {
|
||||
var icon_apt = me.element.createChild("path", name ~ " icon" )
|
||||
.moveTo(-17,0)
|
||||
.arcSmallCW(17,17,0,34,0)
|
||||
.arcSmallCW(17,17,0,-34,0)
|
||||
.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")
|
||||
.setColor(me.layer.style.color_default)
|
||||
.setStrokeLineWidth(me.layer.style.line_width);
|
||||
var text_apt = me.element.createChild("text", name ~ " label")
|
||||
.setDrawMode( canvas.Text.TEXT )
|
||||
.setTranslation(17,35)
|
||||
.setText(me.model.id)
|
||||
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
|
||||
.setFontSize(28)
|
||||
.setTranslation(5,25);
|
||||
}
|
||||
};
|
||||
.setColor(me.layer.style.label_font_color)
|
||||
.setFontSize(me.layer.style.label_font_size);
|
||||
|
||||
# FIXME: this applies scale to the whole group, better do this separately for each element?
|
||||
me.element.setScale(me.layer.style.scale_factor);
|
||||
};
|
||||
var draw = func;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ var parents = [SymbolLayer.Controller];
|
|||
var __self__ = caller(0)[0];
|
||||
SymbolLayer.Controller.add(name, __self__);
|
||||
SymbolLayer.add(name, {
|
||||
parents: [SymbolLayer],
|
||||
parents: [NavaidSymbolLayer],
|
||||
type: name, # Symbol type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
df_style: {
|
||||
|
@ -25,33 +25,16 @@ var new = func(layer) {
|
|||
listeners: [],
|
||||
query_type:'dme',
|
||||
};
|
||||
##
|
||||
# default styling parameters - can be overridden via addLayer( style:{key:value, ...} )
|
||||
|
||||
if (contains(m.layer,'style')) return m; # we already have a proper style
|
||||
|
||||
# otherwise, set up a default style:
|
||||
m.layer.style={};
|
||||
m.layer.style.debug = 0; # HACK: setting this enables benchmarking and printlog statements
|
||||
m.layer.style.animation_test = 0;
|
||||
|
||||
# these are used by the draw() routines, see DME.symbol
|
||||
m.layer.style.scale_factor = 1.0 ; # applied to the whole group for now
|
||||
m.layer.style.line_width = 3.0;
|
||||
m.layer.style.color_tuned = [0,1,0];
|
||||
m.layer.style.color_default = [0,0.6,0.85];
|
||||
m.addVisibilityListener();
|
||||
|
||||
return m;
|
||||
};
|
||||
|
||||
## TODO: also move this to generator helper
|
||||
var del = func() {
|
||||
foreach (var l; me.listeners)
|
||||
removelistener(l);
|
||||
};
|
||||
|
||||
var searchCmd = func {
|
||||
printlog(_MP_dbg_lvl, "Running query:", me.query_type);
|
||||
var range = me.map.getRange();
|
||||
if (range == nil) return;
|
||||
return positioned.findWithinRange(me.map.getPosCoord(), range, me.query_type);
|
||||
};
|
||||
var searchCmd = NavaidSymbolLayer.make(query:'dme');
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# 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, symbol) ; # this controller doesn't need an instance
|
||||
|
|
@ -49,11 +49,6 @@ var del = func {
|
|||
|
||||
# var DMEIcon = StyleableElement.new( [{color:IconColor}, ] );
|
||||
|
||||
###
|
||||
# helper to tell if the symbol is already initialized or not
|
||||
# TODO: encapsulate API-wise (this is a very common thing to do...)
|
||||
var is_initialized = func me.icon_dme != nil;
|
||||
|
||||
###
|
||||
# FIXME: these should probably be part of MapStructure itself
|
||||
# TODO: Need to come up with a StyleableElement class for this sort of stuff
|
||||
|
@ -64,7 +59,7 @@ var apply_styling = func {
|
|||
var current_color = me.icon_dme.getColor();
|
||||
var required_color = nil;
|
||||
|
||||
if (typeof(me.layer.map.controller["is_tuned"]) == 'func' and me.layer.map.controller.is_tuned(me.model.frequency/100))
|
||||
if (typeof(me.map.controller["is_tuned"]) == 'func' and me.map.controller.is_tuned(me.model.frequency/100))
|
||||
#TODO: once we support caching/instancing, we cannot just change the symbol like this - we need to use a different symbol from the cache instead
|
||||
# which is why these things need to be done ONCE during init to set up a cache entry for each symbol variation to come up with a corresponding raster image
|
||||
# TODO: API-wise it would make sense to maintain a vector of required keys, so that the style hash can be validated in the ctor of the layer
|
||||
|
@ -90,7 +85,6 @@ var apply_styling = func {
|
|||
# TODO: also needs to be aware of current range, so that proper LOD handling can be implemented
|
||||
var apply_scale = func {
|
||||
# add all symbols here that need scaling
|
||||
# print("Scaling:", me.layer.style.scale_factor);
|
||||
me.icon_dme.setScale( me.layer.style.scale_factor );
|
||||
}
|
||||
|
||||
|
@ -106,52 +100,39 @@ var init_cache = func {
|
|||
|
||||
##
|
||||
# init is done separately to prepare support for caching (instanced symbols)
|
||||
# NOTE: People should not be "hard-coding" things like color/size here
|
||||
# these need to be encapsulated via a Hash lookup, so that things can be
|
||||
# easily customized
|
||||
#
|
||||
var init_symbols = func {
|
||||
# debug.dump(me.layer.style);
|
||||
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( me.layer.style.line_width ); #TODO: this should be style-able
|
||||
var init = func {
|
||||
# debug.dump(me.layer.style);
|
||||
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( me.layer.style.line_width );
|
||||
|
||||
# finally scale the symbol as requested, this is done last so that people can override this when creating the layer
|
||||
me.apply_scale();
|
||||
# finally scale the symbol as requested, this is done last so that people can override this when creating the layer
|
||||
me.apply_scale();
|
||||
if (me.layer.style.animation_test) {
|
||||
|
||||
me.timer = maketimer(0.33, func me.animate() );
|
||||
me.timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
var updateRun = func {
|
||||
# check if the current station is tuned or not - and style the symbol accordingly (color)
|
||||
me.apply_styling();
|
||||
me.draw();
|
||||
}
|
||||
|
||||
##
|
||||
# this method is REQUIRED (basically, the entry point for drawing - most others are just helpers)
|
||||
# this method is REQUIRED
|
||||
# check if the current station is tuned or not - and style the symbol accordingly (color)
|
||||
var draw = func {
|
||||
# print("DME:draw()");
|
||||
# Init: will set up the symbol if it isn't already
|
||||
if ( !me.is_initialized() )
|
||||
me.init_symbols();
|
||||
|
||||
# wrapper for custom styling, based on tuned/default colors (see lookup hash above)
|
||||
me.updateRun();
|
||||
me.apply_styling();
|
||||
};
|
||||
|
||||
|
|
|
@ -5,17 +5,9 @@ var parents = [SymbolLayer.Controller];
|
|||
var __self__ = caller(0)[0];
|
||||
SymbolLayer.Controller.add(name, __self__);
|
||||
SymbolLayer.add(name, {
|
||||
parents: [SymbolLayer],
|
||||
parents: [NavaidSymbolLayer],
|
||||
type: name, # Symbol type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
|
||||
df_style: {
|
||||
line_width: 3,
|
||||
scale_factor: 0.5,
|
||||
debug: 1,
|
||||
color_default: [0, 0.6, 0.85],
|
||||
}
|
||||
|
||||
});
|
||||
var new = func(layer) {
|
||||
var m = {
|
||||
|
@ -25,6 +17,8 @@ var new = func(layer) {
|
|||
listeners: [],
|
||||
query_type:'fix',
|
||||
};
|
||||
m.addVisibilityListener();
|
||||
|
||||
return m;
|
||||
};
|
||||
var del = func() {
|
||||
|
@ -33,10 +27,5 @@ var del = func() {
|
|||
removelistener(l);
|
||||
};
|
||||
|
||||
var searchCmd = func {
|
||||
printlog(_MP_dbg_lvl, "Running query:", me.query_type);
|
||||
var range = me.map.getRange();
|
||||
if (range == nil) return;
|
||||
return positioned.findWithinRange(me.map.getPosCoord(), range, me.query_type);
|
||||
};
|
||||
var searchCmd = NavaidSymbolLayer.make('fix');
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# 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
|
||||
|
|
@ -5,45 +5,45 @@ var parents = [DotSym];
|
|||
var __self__ = caller(0)[0];
|
||||
DotSym.makeinstance( name, __self__ );
|
||||
|
||||
SymbolLayer.get(name).df_style = {
|
||||
line_width: 3,
|
||||
scale_factor: 1,
|
||||
font: "LiberationFonts/LiberationSans-Regular.ttf",
|
||||
font_color: [0,0,0],
|
||||
font_size: 28,
|
||||
color: [0, 0.6, 0.85],
|
||||
show_labels: 1,
|
||||
};
|
||||
|
||||
|
||||
var element_type = "group"; # we want a group, becomes "me.element"
|
||||
var icon_fix = nil;
|
||||
var text_fix = nil;
|
||||
|
||||
##
|
||||
# used during initialization to populate the symbol cache with a FIX symbol
|
||||
#
|
||||
var drawFIX = func(color, width) func(group) {
|
||||
|
||||
var symbol = group.createChild("path")
|
||||
var drawFIX = func(group) {
|
||||
group.createChild("path")
|
||||
.moveTo(-15,15)
|
||||
.lineTo(0,-15)
|
||||
.lineTo(15,15)
|
||||
.close()
|
||||
.setStrokeLineWidth(width)
|
||||
.setColor(color)
|
||||
.setScale(0.5,0.5); # FIXME: do proper LOD handling here - we need to scale according to current texture dimensions vs. original/design dimensions
|
||||
return symbol;
|
||||
.setStrokeLineWidth(line_width)
|
||||
.setColor(color);
|
||||
}
|
||||
|
||||
var icon_fix_cached = [
|
||||
SymbolCache32x32.add(
|
||||
name: "FIX",
|
||||
callback: drawFIX( color:[0, 0.6, 0.85], width:3 ), # TODO: use the style hash to encapsulate styling stuff
|
||||
draw_mode: SymbolCache.DRAW_CENTERED
|
||||
)
|
||||
];
|
||||
var cache = StyleableCacheable.new(
|
||||
name:name, draw_func: drawFIX,
|
||||
cache: SymbolCache32x32,
|
||||
draw_mode: SymbolCache.DRAW_CENTERED,
|
||||
relevant_keys: ["line_width", "color"],
|
||||
);
|
||||
|
||||
var draw = func {
|
||||
if (me.icon_fix != nil) return; # fix icon already initialized
|
||||
# initialize the fix symbol
|
||||
me.icon_fix = icon_fix_cached[0].render(me.element);
|
||||
var init = func {
|
||||
# initialize the cached fix symbol
|
||||
cache.render(me.element, me.style).setScale(me.style.scale_factor);
|
||||
|
||||
# non-cached stuff:
|
||||
# FIXME: labels need to be LOD-aware (i.e. aware of MapController range, so that we can hide/show them)
|
||||
me.text_fix = me.element.createChild("text")
|
||||
.setDrawMode( canvas.Text.TEXT )
|
||||
.setText(me.model.id)
|
||||
.setFont("LiberationFonts/LiberationSans-Regular.ttf") # TODO: encapsulate styling stuff
|
||||
.setFontSize(28) # TODO: encapsulate styling stuff
|
||||
.setTranslation(5,25);
|
||||
# non-cached stuff:
|
||||
if (me.style.show_labels)
|
||||
me.text_fix = me.newText(me.model.id).setScale(me.style.scale_factor);
|
||||
}
|
||||
var draw = func;
|
||||
|
|
58
Nasal/canvas/map/FLT.lcontroller
Normal file
|
@ -0,0 +1,58 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# Class things:
|
||||
var name = 'FLT';
|
||||
var parents = [SymbolLayer.Controller];
|
||||
var __self__ = caller(0)[0];
|
||||
SymbolLayer.Controller.add(name, __self__);
|
||||
SymbolLayer.add(name, {
|
||||
parents: [MultiSymbolLayer],
|
||||
append: func(idx, item, model) {
|
||||
while (size(me.list) <= idx) {
|
||||
append(me.list, nil);
|
||||
} if (me.list[idx] == nil) {
|
||||
me.list[idx] = me.add_sym(model);
|
||||
}
|
||||
append(model, item);
|
||||
var pos = me.controller.getpos(item);
|
||||
var cmd = me.list[idx].element._last_cmd == -1 ? canvas.Path.VG_MOVE_TO : canvas.Path.VG_LINE_TO;
|
||||
me.list[idx].element.addSegmentGeo(cmd, ["N"~pos[0],"E"~pos[1]]);
|
||||
#props.dump(me.list[idx].element._node);
|
||||
},
|
||||
type: name, # Symbol type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
add_sym: func(model) {
|
||||
return Symbol.new(me.type, me.group, me, model);
|
||||
},
|
||||
});
|
||||
var new = func(layer) {
|
||||
var m = {
|
||||
parents: [__self__],
|
||||
layer: layer,
|
||||
listeners: [],
|
||||
models: [],
|
||||
active_path: 0,
|
||||
};
|
||||
layer.searcher._equals = func(a,b) a == b;
|
||||
m.addVisibilityListener();
|
||||
|
||||
return m;
|
||||
};
|
||||
var del = func() {
|
||||
foreach (var l; me.listeners)
|
||||
removelistener(l);
|
||||
};
|
||||
|
||||
var searchCmd = func() {
|
||||
printlog(_MP_dbg_lvl, "Running query: FLT");
|
||||
var hist = aircraft.history();
|
||||
var path = hist.pathForHistory(1000);
|
||||
printlog(_MP_dbg_lvl, "FLT size: "~size(path));
|
||||
while (size(me.models) <= me.active_path) append(me.models, []);
|
||||
for (var i=size(me.models[me.active_path]); i<size(path); i+=1)
|
||||
me.layer.append(me.active_path, path[i], me.models[me.active_path]);
|
||||
# TODO: filter by in_range()?
|
||||
#debug.dump(me.models);
|
||||
return me.models;
|
||||
};
|
||||
var getpos = Symbol.Controller.getpos;
|
||||
|
19
Nasal/canvas/map/FLT.symbol
Normal file
|
@ -0,0 +1,19 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
|
||||
# Class things:
|
||||
var name = 'FLT';
|
||||
var parents = [LineSymbol];
|
||||
var __self__ = caller(0)[0];
|
||||
LineSymbol.makeinstance( name, __self__ );
|
||||
|
||||
SymbolLayer.get(name).df_style = { # style to use by default
|
||||
line_width: 5,
|
||||
color: [0,0,1]
|
||||
};
|
||||
|
||||
var init = func {
|
||||
me.element.setColor(me.layer.style.color)
|
||||
.setStrokeLineWidth(me.layer.style.line_width);
|
||||
me.needs_update = 0;
|
||||
};
|
||||
|
BIN
Nasal/canvas/map/Images/Circuitodiattesa.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
6742
Nasal/canvas/map/Images/HoldDirectEntry.svg
Normal file
After Width: | Height: | Size: 508 KiB |
6292
Nasal/canvas/map/Images/HoldParallelEntry.svg
Normal file
After Width: | Height: | Size: 474 KiB |
6289
Nasal/canvas/map/Images/HoldTeardropEntry.svg
Normal file
After Width: | Height: | Size: 474 KiB |
6565
Nasal/canvas/map/Images/HoldTeardropEntry2.svg
Normal file
After Width: | Height: | Size: 474 KiB |
|
@ -58,7 +58,7 @@
|
|||
transform="translate(-364.652,-344.745)">
|
||||
<g
|
||||
id="airplane"
|
||||
transform="translate(364.652,346.745)"
|
||||
transform="translate(320.73272,291.98516)"
|
||||
inkscape:label="#g3781">
|
||||
<path
|
||||
id="path3783"
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 5.8 MiB After Width: | Height: | Size: 5.8 MiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
@ -5,9 +5,10 @@ var parents = [SymbolLayer.Controller];
|
|||
var __self__ = caller(0)[0];
|
||||
SymbolLayer.Controller.add(name, __self__);
|
||||
SymbolLayer.add(name, {
|
||||
parents: [SymbolLayer],
|
||||
parents: [NavaidSymbolLayer],
|
||||
type: name, # Symbol type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
df_style: {},
|
||||
});
|
||||
var new = func(layer) {
|
||||
var m = {
|
||||
|
@ -17,6 +18,8 @@ var new = func(layer) {
|
|||
listeners: [],
|
||||
query_type:'ndb',
|
||||
};
|
||||
m.addVisibilityListener();
|
||||
|
||||
return m;
|
||||
};
|
||||
var del = func() {
|
||||
|
@ -24,10 +27,6 @@ var del = func() {
|
|||
removelistener(l);
|
||||
};
|
||||
|
||||
var searchCmd = func {
|
||||
printlog(_MP_dbg_lvl, "Running query:", me.query_type);
|
||||
var range = me.map.getRange();
|
||||
if (range == nil) return;
|
||||
return positioned.findWithinRange(me.map.getPosCoord(), range, me.query_type);
|
||||
};
|
||||
var searchCmd = NavaidSymbolLayer.make('ndb');
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# 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 ];
|
||||
|
|
@ -1,19 +1,7 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# 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;
|
||||
};
|
||||
DotSym.makeinstance('NDB', {
|
||||
parents: [SVGSymbol],
|
||||
svg_path: "/gui/dialogs/images/ndb_symbol.svg",
|
||||
#cacheable: 1,
|
||||
});
|
||||
|
||||
|
|
55
Nasal/canvas/map/RTE.lcontroller
Normal file
|
@ -0,0 +1,55 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# TODO: this layer doesn't make sense to support for AI/MP traffic, because we don't currently have access to flightplan/routing info
|
||||
# that also applies to other layers like WPT or even navaid layers that handle station tuning based on local radio settings
|
||||
#
|
||||
# Class things:
|
||||
var name = 'RTE';
|
||||
var parents = [SymbolLayer.Controller];
|
||||
var __self__ = caller(0)[0];
|
||||
SymbolLayer.Controller.add(name, __self__);
|
||||
SymbolLayer.add(name, {
|
||||
parents: [MultiSymbolLayer],
|
||||
type: name, # Symbol type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
df_options: { # default configuration options
|
||||
active_node: "/autopilot/route-manager/active",
|
||||
current_wp_node: "/autopilot/route-manager/current-wp",
|
||||
}
|
||||
});
|
||||
var new = func(layer) {
|
||||
var m = {
|
||||
parents: [__self__],
|
||||
layer: layer,
|
||||
map: layer.map,
|
||||
listeners: [],
|
||||
};
|
||||
layer.searcher._equals = func(l,r) 0; # TODO: create model objects instead?
|
||||
append(m.listeners, setlistener(layer.options.active_node, func m.layer.update() ));
|
||||
m.addVisibilityListener();
|
||||
|
||||
return m;
|
||||
};
|
||||
var del = func() {
|
||||
foreach (var l; me.listeners)
|
||||
removelistener(l);
|
||||
};
|
||||
|
||||
var searchCmd = func {
|
||||
# FIXME: do we return the current route even if it isn't active?
|
||||
printlog(_MP_dbg_lvl, "Running query: ", name);
|
||||
var plans = []; # TODO: multiple flightplans?
|
||||
|
||||
# http://wiki.flightgear.org/Nasal_Flightplan
|
||||
var fp = flightplan();
|
||||
var fpSize = fp.getPlanSize();
|
||||
var coords = [];
|
||||
for (var i=0; i<fpSize; i += 1) {
|
||||
var leg = fp.getWP(i);
|
||||
foreach (var c; leg.path()) {
|
||||
append(coords, c);
|
||||
}
|
||||
}
|
||||
append(plans, coords);
|
||||
return plans;
|
||||
};
|
||||
|
18
Nasal/canvas/map/RTE.symbol
Normal file
|
@ -0,0 +1,18 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
|
||||
# Class things:
|
||||
var name = 'RTE';
|
||||
var parents = [LineSymbol];
|
||||
var __self__ = caller(0)[0];
|
||||
LineSymbol.makeinstance( name, __self__ );
|
||||
|
||||
SymbolLayer.get(name).df_style = { # style to use by default
|
||||
line_width: 5,
|
||||
color: [1,0,1]
|
||||
};
|
||||
|
||||
var init = func {
|
||||
me.element.setColor(me.style.color)
|
||||
.setStrokeLineWidth(me.style.line_width);
|
||||
};
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# FIXME: this needs to be configurable via the ctor to optionally differentiate between MP and AI traffic, and exclude ground/sea traffic
|
||||
# or at least use a different, non-TCAS symbol for those (looking pretty weird ATM)
|
||||
#
|
||||
# Class things:
|
||||
var name = 'TFC';
|
||||
var parents = [SymbolLayer.Controller];
|
||||
var __self__ = caller(0)[0];
|
||||
SymbolLayer.Controller.add(name, __self__);
|
||||
SymbolLayer.add(name, {
|
||||
parents: [SymbolLayer],
|
||||
parents: [MultiSymbolLayer],
|
||||
type: name, # Symbol type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
df_style: { debug: 0 }, # style to use by default
|
||||
});
|
||||
|
||||
var model_root = props.globals.getNode("/ai/models/");
|
||||
|
@ -22,6 +24,8 @@ var new = func(layer) {
|
|||
searchCmd: searchCmd_default,
|
||||
};
|
||||
layer.searcher._equals = func(l,r) l.equals(r);
|
||||
m.addVisibilityListener();
|
||||
|
||||
return m;
|
||||
};
|
||||
var del = func() {
|
||||
|
@ -40,6 +44,7 @@ var TrafficModel = {
|
|||
id: id,
|
||||
node: node,
|
||||
pos: node.getNode("position",1),
|
||||
type: node.getName(),
|
||||
};
|
||||
return m;
|
||||
},
|
||||
|
@ -57,6 +62,13 @@ var TrafficModel = {
|
|||
get_alt: func() (me.getValue("position/altitude-ft") or 0),
|
||||
};
|
||||
|
||||
var get_alt_diff = func(model) {
|
||||
# debug.dump( keys(me) );
|
||||
var model_alt = model.get_alt();
|
||||
var alt = me.map.getAlt();
|
||||
if (alt == nil or model_alt == nil) return 0;
|
||||
return alt-model_alt;
|
||||
};
|
||||
|
||||
##
|
||||
# dummy/placeholder (will be overridden in ctor and set to the default callback)
|
||||
|
@ -66,6 +78,8 @@ var searchCmd_default = func {
|
|||
# TODO: this would be a good candidate for splitting across frames
|
||||
printlog(_MP_dbg_lvl, "Doing query: "~name);
|
||||
|
||||
if (me.map.getRange() == nil) return;
|
||||
|
||||
var result = [];
|
||||
var models = 0;
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# Class things:
|
||||
var name = 'TFC';
|
||||
var parents = [Symbol.Controller];
|
||||
var __self__ = caller(0)[0];
|
||||
Symbol.Controller.add(name, __self__);
|
||||
Symbol.registry[name].df_controller = __self__;
|
||||
var new = func(model, symbol) ; # this controller doesn't need an instance
|
||||
var get_alt_diff = func(model) {
|
||||
# debug.dump( keys(me) );
|
||||
var model_alt = model.get_alt();
|
||||
var alt = getprop("/position/altitude-ft"); # FIXME: hardcoded - right, we should probably generalize the "NDSourceDriver logic found in navdisplay.mfd and make it part of MapStructure
|
||||
if (alt == nil or model_alt == nil) return 0;
|
||||
return alt-model_alt;
|
||||
};
|
||||
|
|
@ -5,6 +5,17 @@ var parents = [DotSym];
|
|||
var __self__ = caller(0)[0];
|
||||
DotSym.makeinstance( name, __self__ );
|
||||
|
||||
SymbolLayer.get(name).df_style = { # style to use by default
|
||||
line_width: 3,
|
||||
scale_factor: 1,
|
||||
color_by_lvl: {
|
||||
3: [1,0,0], # resolution advisory
|
||||
2: [1,0.5,0], # traffic advisory
|
||||
1: [1,1,1], # proximate traffic
|
||||
},
|
||||
color_default: [1,1,1]
|
||||
};
|
||||
|
||||
var element_type = "group"; # we want a group, becomes "me.element"
|
||||
var text_tcas = nil;
|
||||
var icon_tcas = nil;
|
||||
|
@ -12,8 +23,12 @@ var arrow_tcas = [nil,nil];
|
|||
var arrow_type = nil;
|
||||
|
||||
var draw_tcas_arrow = nil;
|
||||
var color = nil;
|
||||
var threatLvl = 0e-0; # NaN to update even when threatLvl == nil
|
||||
|
||||
# TODO: how to integrate both styling and caching?
|
||||
var draw = func {
|
||||
# TODO: get rid of draw_tcas_arrow hacks
|
||||
if (draw_tcas_arrow == nil)
|
||||
draw_tcas_arrow = [
|
||||
draw_tcas_arrow_above_500,
|
||||
|
@ -23,7 +38,7 @@ var draw = func {
|
|||
# print("Drawing traffic for:", callsign );
|
||||
var threatLvl = me.model.get_threat_lvl();
|
||||
var vspeed = me.model.get_vspd();
|
||||
var altDiff = me.controller.get_alt_diff(me.model);
|
||||
var altDiff = me.layer.controller.get_alt_diff(me.model);
|
||||
# Init
|
||||
if (me.text_tcas == nil) {
|
||||
me.text_tcas = me.element.createChild("text")
|
||||
|
@ -49,48 +64,54 @@ var draw = func {
|
|||
me.arrow_tcas[arrow_type] = me.draw_tcas_arrow[arrow_type](me.element);
|
||||
me.arrow_tcas[arrow_type].show();
|
||||
}
|
||||
## TODO: threat level symbols should also be moved to *.draw files
|
||||
if (threatLvl == 3) {
|
||||
# resolution advisory
|
||||
me.icon_tcas.moveTo(-17,-17)
|
||||
.horiz(34)
|
||||
.vert(34)
|
||||
.horiz(-34)
|
||||
.close()
|
||||
.setColor(1,0,0)
|
||||
.setColorFill(1,0,0);
|
||||
me.text_tcas.setColor(1,0,0);
|
||||
me.arrow_tcas[me.arrow_type].setColor(1,0,0);
|
||||
} elsif (threatLvl == 2) {
|
||||
# traffic advisory
|
||||
me.icon_tcas.moveTo(-17,0)
|
||||
.arcSmallCW(17,17,0,34,0)
|
||||
.arcSmallCW(17,17,0,-34,0)
|
||||
.setColor(1,0.5,0)
|
||||
.setColorFill(1,0.5,0);
|
||||
me.text_tcas.setColor(1,0.5,0);
|
||||
me.arrow_tcas[me.arrow_type].setColor(1,0.5,0);
|
||||
} elsif (threatLvl == 1) {
|
||||
# proximate traffic
|
||||
me.icon_tcas.moveTo(-10,0)
|
||||
.lineTo(0,-17)
|
||||
.lineTo(10,0)
|
||||
.lineTo(0,17)
|
||||
.close()
|
||||
.setColor(1,1,1)
|
||||
.setColorFill(1,1,1);
|
||||
me.text_tcas.setColor(1,1,1);
|
||||
me.arrow_tcas[me.arrow_type].setColor(1,1,1);
|
||||
} else {
|
||||
# other traffic
|
||||
me.icon_tcas.moveTo(-10,0)
|
||||
.lineTo(0,-17)
|
||||
.lineTo(10,0)
|
||||
.lineTo(0,17)
|
||||
.close()
|
||||
.setColor(1,1,1);
|
||||
me.text_tcas.setColor(1,1,1);
|
||||
me.arrow_tcas[me.arrow_type].setColor(1,1,1);
|
||||
if (threatLvl != me.threatLvl) {
|
||||
me.threatLvl = threatLvl;
|
||||
## TODO: should threat level symbols also be moved to *.draw files?
|
||||
if (threatLvl == 3) {
|
||||
# resolution advisory
|
||||
me.icon_tcas.moveTo(-17,-17)
|
||||
.horiz(34)
|
||||
.vert(34)
|
||||
.horiz(-34)
|
||||
.close();
|
||||
} elsif (threatLvl == 2) {
|
||||
# traffic advisory
|
||||
me.icon_tcas.moveTo(-17,0)
|
||||
.arcSmallCW(17,17,0,34,0)
|
||||
.arcSmallCW(17,17,0,-34,0);
|
||||
} elsif (threatLvl == 1) {
|
||||
# proximate traffic
|
||||
me.icon_tcas.moveTo(-10,0)
|
||||
.lineTo(0,-17)
|
||||
.lineTo(10,0)
|
||||
.lineTo(0,17)
|
||||
.close();
|
||||
} else {
|
||||
# other traffic
|
||||
me.icon_tcas.moveTo(-10,0)
|
||||
.lineTo(0,-17)
|
||||
.lineTo(10,0)
|
||||
.lineTo(0,17)
|
||||
.close();
|
||||
}
|
||||
}
|
||||
var color = nil;
|
||||
if (threatLvl != nil)
|
||||
if ((var c = me.style.color_by_lvl[threatLvl]) != nil)
|
||||
var color = canvas._getColor(c);
|
||||
if (color == nil)
|
||||
color = canvas._getColor(me.style.color_default);
|
||||
if (me.color != color) {
|
||||
me.color = color;
|
||||
me.icon_tcas.setColor(color);
|
||||
me.text_tcas.setColor(color);
|
||||
me.arrow_tcas[me.arrow_type].setColor(color);
|
||||
if (num(threatLvl) == nil or (threatLvl < 1 or threatLvl > 3)) {
|
||||
color = [0,0,0,0];
|
||||
}
|
||||
me.icon_tcas.setColorFill(color);
|
||||
}
|
||||
if (me.style.scale_factor != me.element.getScale())
|
||||
me.element.setScale(me.style.scale_factor);
|
||||
};
|
||||
|
||||
|
|
|
@ -5,21 +5,11 @@ var parents = [SymbolLayer.Controller];
|
|||
var __self__ = caller(0)[0];
|
||||
SymbolLayer.Controller.add(name, __self__);
|
||||
SymbolLayer.add(name, {
|
||||
parents: [SymbolLayer],
|
||||
parents: [NavaidSymbolLayer],
|
||||
type: name, # Symbol type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
});
|
||||
var new = func(layer) {
|
||||
|
||||
if(0) {
|
||||
# TODO: generalize and move to MapStructure.nas
|
||||
var required_controllers = [ {name: 'query_range',type:'func'}, ];
|
||||
foreach(var c; required_controllers) {
|
||||
if (!contains(layer.map.controller, c.name) or typeof(layer.map.controller[c.name]) !=c.type)
|
||||
die("Required controller is missing/invalid:"~ c.name ~ ' ['~c.type~']' );
|
||||
}
|
||||
}
|
||||
|
||||
var m = {
|
||||
parents: [__self__],
|
||||
layer: layer,
|
||||
|
@ -38,6 +28,8 @@ if(0) {
|
|||
}
|
||||
#call(debug.dump, keys(layer));
|
||||
m.changed_freq(update:0);
|
||||
m.addVisibilityListener();
|
||||
|
||||
return m;
|
||||
};
|
||||
var del = func() {
|
||||
|
@ -59,10 +51,6 @@ var changed_freq = func(update=1) {
|
|||
me.active_vors[ navN.getIndex() ] = navN.getValue("frequencies/selected-mhz");
|
||||
if (update) me.layer.update();
|
||||
};
|
||||
var searchCmd = func {
|
||||
printlog(_MP_dbg_lvl, "Running query:", me.query_type);
|
||||
var range = me.map.getRange();
|
||||
if (range == nil) return;
|
||||
return positioned.findWithinRange(me.map.getPosCoord(), range, me.query_type);
|
||||
};
|
||||
|
||||
var searchCmd = NavaidSymbolLayer.make('vor');
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# Class things:
|
||||
var name = 'VOR';
|
||||
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
|
||||
|
|
@ -5,33 +5,31 @@ var parents = [DotSym];
|
|||
var __self__ = caller(0)[0];
|
||||
DotSym.makeinstance( name, __self__ );
|
||||
|
||||
SymbolLayer.get(name).df_style = { # style to use by default
|
||||
line_width: 3,
|
||||
range_line_width: 3,
|
||||
radial_line_width: 3,
|
||||
range_dash_array: [5, 15, 5, 15, 5],
|
||||
radial_dash_array: [15, 5, 15, 5, 15],
|
||||
scale_factor: 1,
|
||||
active_color: [0, 1, 0],
|
||||
inactive_color: [0, 0.6, 0.85],
|
||||
};
|
||||
|
||||
var element_type = "group"; # we want a group, becomes "me.element"
|
||||
var icon_vor = nil; # a handle to the cached icon
|
||||
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 active = -1;
|
||||
|
||||
###
|
||||
# this function returns a new function that renders the symbol
|
||||
# into a canvas group
|
||||
# the signature of the first func should contain all customizable
|
||||
# parameters (such as color/width etc)
|
||||
#
|
||||
# you will typically have one such function for each cacheable symbol
|
||||
# and only call it once during initialization (once for each cached style/variation)
|
||||
# the signature of the 2nd func remains untouched, it's internally used
|
||||
##
|
||||
# Callback for drawing a new VOR based on styling
|
||||
#
|
||||
# NOTE: callbacks that create symbols for caching MUST render them using a
|
||||
# transparent background !!
|
||||
#
|
||||
var drawVOR = func(color, width=3) return func(group) {
|
||||
|
||||
# init_calls += 1; # TODO: doing this here is too fragile, this should be done by MapStructure ...
|
||||
# if( init_calls >= 2)
|
||||
# print("Warning: draw() routine for a cached element has been called more than once, should only happen during reset/reinit");
|
||||
|
||||
# print("drawing vor");
|
||||
var symbol = group.createChild("path")
|
||||
var drawVOR = func(group) {
|
||||
group.createChild("path")
|
||||
.moveTo(-15,0)
|
||||
.lineTo(-7.5,12.5)
|
||||
.lineTo(7.5,12.5)
|
||||
|
@ -39,35 +37,16 @@ var drawVOR = func(color, width=3) return func(group) {
|
|||
.lineTo(7.5,-12.5)
|
||||
.lineTo(-7.5,-12.5)
|
||||
.close()
|
||||
.setStrokeLineWidth(width)
|
||||
.setColor( color );
|
||||
|
||||
return symbol; # make this explicit, not everybody knows Nasal internals inside/out ...
|
||||
.setStrokeLineWidth(line_width)
|
||||
.setColor(color);
|
||||
};
|
||||
|
||||
##
|
||||
# a vector with pre-created symbols that are cached during initialization
|
||||
# this needs to be done for each variation of each symbol, i.e. color/width etc
|
||||
# note that scaling should not be done here, likewise for positioning via setGeoPosition()
|
||||
#
|
||||
# FIXME: We still need to encode styling information as part of the key/name lookup
|
||||
# so that the cache entry's name contains styling info, or different maps/layers may
|
||||
# overwrite their symbols
|
||||
#
|
||||
# icon_vor_cache[0] - inactive symbol
|
||||
# icon_vor_cache[1] - active symbol
|
||||
var icon_vor_cached = [
|
||||
SymbolCache32x32.add(
|
||||
name: "VOR-INACTIVE",
|
||||
callback: drawVOR( color:[0, 0.6, 0.85], width:3 ),
|
||||
draw_mode: SymbolCache.DRAW_CENTERED
|
||||
),
|
||||
SymbolCache32x32.add(
|
||||
name: "VOR-ACTIVE",
|
||||
callback: drawVOR( color:[0, 1, 0], width:3 ),
|
||||
draw_mode: SymbolCache.DRAW_CENTERED
|
||||
),
|
||||
];
|
||||
var cache = StyleableCacheable.new(
|
||||
name:name, draw_func: drawVOR,
|
||||
cache: SymbolCache32x32,
|
||||
draw_mode: SymbolCache.DRAW_CENTERED,
|
||||
relevant_keys: ["line_width", "color"],
|
||||
);
|
||||
|
||||
##
|
||||
# TODO: make this a part of the framework, for use by other layers/symbols etc
|
||||
|
@ -84,41 +63,39 @@ var controller_check = func(layer, controller, arg...) {
|
|||
}
|
||||
|
||||
var draw = func {
|
||||
# Init
|
||||
if (0 and me.model.id == "SAC") # hack to test isActive() around KSMF
|
||||
setprop("instrumentation/nav[0]/frequencies/selected-mhz", me.model.frequency/100);
|
||||
var active = controller_check(me.layer, 'isActive', me.model);
|
||||
#print(me.model.id,"/", me.model.frequency/100, " isActive:", active);
|
||||
if (active != me.active) {
|
||||
#print("VOR.symbol: active != me.active: del/render event triggered");
|
||||
if (me.icon_vor != nil) me.icon_vor.del();
|
||||
# look up the correct symbol from the cache and render it into the group as a raster image
|
||||
me.icon_vor = icon_vor_cached[active or 0].render(me.element);
|
||||
#me.icon_vor = icon_vor_cached[active or 0].render(me.element);
|
||||
me.style.color = active ? me.style.active_color : me.style.inactive_color;
|
||||
me.icon_vor = cache.render(me.element, me.style).setScale(me.style.scale_factor);
|
||||
|
||||
me.active = active; # update me.active flag
|
||||
}
|
||||
# Update (also handles non-cached stuff, such as text labels or animations)
|
||||
# TODO: we can use a func generator to pre-create the callback/data structure for this
|
||||
if (active) {
|
||||
if (me.range_vor == nil) {
|
||||
if (me.range_vor == nil) {
|
||||
# initialize me.range and me.radial_vor
|
||||
var rangeNm = me.layer.map.getRange();
|
||||
var rangeNm = me.map.getRange();
|
||||
# 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);
|
||||
.setStrokeLineWidth(me.style.range_line_width)
|
||||
.setStrokeDashArray(me.style.range_dash_array)
|
||||
.setColor(me.style.active_color);
|
||||
|
||||
var course = me.layer.map.controller.get_tuned_course(me.model.frequency/100);
|
||||
var course = me.map.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)
|
||||
.setStrokeLineWidth(me.style.radial_line_width)
|
||||
.setStrokeDashArray(me.style.radial_dash_array)
|
||||
.setColor(me.style.active_color)
|
||||
.setRotation(course*D2R);
|
||||
}
|
||||
me.range_vor.show();
|
||||
|
|
|
@ -5,9 +5,13 @@ var parents = [SymbolLayer.Controller];
|
|||
var __self__ = caller(0)[0];
|
||||
SymbolLayer.Controller.add(name, __self__);
|
||||
SymbolLayer.add(name, {
|
||||
parents: [SymbolLayer],
|
||||
parents: [MultiSymbolLayer],
|
||||
type: name, # Symbol type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
df_options: { # default configuration options
|
||||
active_node: "/autopilot/route-manager/active",
|
||||
current_wp_node: "/autopilot/route-manager/current-wp",
|
||||
}
|
||||
});
|
||||
var new = func(layer) {
|
||||
var m = {
|
||||
|
@ -16,6 +20,10 @@ var new = func(layer) {
|
|||
map: layer.map,
|
||||
listeners: [],
|
||||
};
|
||||
layer.searcher._equals = func(l,r) l.equals(r);
|
||||
append(m.listeners, setlistener(layer.options.active_node, func m.layer.update() ));
|
||||
m.addVisibilityListener();
|
||||
|
||||
return m;
|
||||
};
|
||||
var del = func() {
|
||||
|
@ -24,15 +32,34 @@ var del = func() {
|
|||
removelistener(l);
|
||||
};
|
||||
|
||||
var WPT_model = {
|
||||
new: func(fp, idx) {
|
||||
var m = { parents:[WPT_model], idx:idx };
|
||||
var wp = fp.getWP(idx);
|
||||
|
||||
m.name = wp.wp_name;
|
||||
var alt = wp.alt_cstr;
|
||||
if (alt != 0)
|
||||
m.name ~= "\n"~alt;
|
||||
|
||||
var n = wp.path()[0];
|
||||
(m.lat,m.lon) = (n.lat,n.lon);
|
||||
return m;
|
||||
},
|
||||
equals: func(other) {
|
||||
# this is set on symbol init, so use this for equality...
|
||||
me.name == other.name
|
||||
},
|
||||
};
|
||||
|
||||
var searchCmd = func {
|
||||
printlog(_MP_dbg_lvl, "Running query: "~name);
|
||||
|
||||
|
||||
var fp = flightplan();
|
||||
var fpSize = fp.getPlanSize();
|
||||
var result = [];
|
||||
for (var i = 1; i <fpSize; i+=1)
|
||||
append(result, fp.getWP(i).path()[0] );
|
||||
|
||||
for (var i = 1; i < fpSize; i+=1)
|
||||
append(result, WPT_model.new(fp, i));
|
||||
return result;
|
||||
};
|
||||
|
||||
|
|
|
@ -5,9 +5,25 @@ 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" );
|
||||
var last_idx = nil;
|
||||
|
||||
var new = func(symbol, model) {
|
||||
var m = {
|
||||
parents:[__self__],
|
||||
listeners: [],
|
||||
symbol: symbol,
|
||||
model: model,
|
||||
};
|
||||
append(m.listeners, setlistener(symbol.layer.options.current_wp_node, func(n) {
|
||||
var n = n.getValue();
|
||||
if (m.last_idx == model.idx or n == model.idx)
|
||||
symbol.update();
|
||||
m.last_idx = n;
|
||||
}, 0, 0));
|
||||
|
||||
return m;
|
||||
}
|
||||
var del = func() {
|
||||
foreach (var l; me.listeners)
|
||||
removelistener(l);
|
||||
}
|
||||
|
|
|
@ -5,14 +5,23 @@ var parents = [DotSym];
|
|||
var __self__ = caller(0)[0];
|
||||
DotSym.makeinstance( name, __self__ );
|
||||
|
||||
var element_type = "group"; # we want a group, becomes "me.element"
|
||||
var base = nil;
|
||||
var text_wps = nil;
|
||||
SymbolLayer.get(name).df_style = { # style to use by default
|
||||
line_width: 3,
|
||||
scale_factor: 1,
|
||||
font: "LiberationFonts/LiberationSans-Regular.ttf",
|
||||
font_size: 28,
|
||||
font_color: [1,0,1],
|
||||
active_color: [1,0,1],
|
||||
inactive_color: [1,1,1],
|
||||
};
|
||||
|
||||
var draw = func {
|
||||
if (me.base != nil) return;
|
||||
me.base = me.element.createChild("path")
|
||||
.setStrokeLineWidth(3)
|
||||
var element_type = "group"; # we want a group, becomes "me.element"
|
||||
var active = nil;
|
||||
|
||||
var init = func {
|
||||
# TODO: active vs inactive?
|
||||
me.path = me.element.createChild("path")
|
||||
.setStrokeLineWidth(me.style.line_width)
|
||||
.moveTo(0,-25)
|
||||
.lineTo(-5,-5)
|
||||
.lineTo(-25,0)
|
||||
|
@ -22,14 +31,23 @@ var draw = func {
|
|||
.lineTo(25,0)
|
||||
.lineTo(5,-5)
|
||||
.setColor(1,1,1)
|
||||
.close();
|
||||
.close()
|
||||
.setScale(me.style.scale_factor);
|
||||
|
||||
me.text_wps = wpt_grp.createChild("text")
|
||||
.setDrawMode( canvas.Text.TEXT )
|
||||
.setText(name)
|
||||
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
|
||||
.setFontSize(28)
|
||||
.setTranslation(25,35)
|
||||
.setColor(1,0,1);
|
||||
me.newText(me.model.name, me.style.font_color).setTranslation(25,35)
|
||||
.setScale(me.style.scale_factor);
|
||||
|
||||
me.draw();
|
||||
};
|
||||
var draw = func {
|
||||
var active = (getprop(me.layer.options.current_wp_node) == me.model.idx);
|
||||
if (active != me.active) {
|
||||
me.path.setColor(
|
||||
active ?
|
||||
me.style.active_color :
|
||||
me.style.inactive_color
|
||||
);
|
||||
me.active = active;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
76
Nasal/canvas/map/WXR.lcontroller
Normal file
|
@ -0,0 +1,76 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# Class things:
|
||||
var name = 'WXR';
|
||||
var parents = [SymbolLayer.Controller];
|
||||
var __self__ = caller(0)[0];
|
||||
SymbolLayer.Controller.add(name, __self__);
|
||||
SymbolLayer.add(name, {
|
||||
parents: [MultiSymbolLayer],
|
||||
type: name, # Symbol type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
});
|
||||
|
||||
var new = func(layer) {
|
||||
var m = {
|
||||
parents: [__self__],
|
||||
layer: layer,
|
||||
map: layer.map,
|
||||
listeners: [],
|
||||
searchCmd: searchCmd_default,
|
||||
};
|
||||
layer.searcher._equals = func(l,r) l.equals(r);
|
||||
m.addVisibilityListener();
|
||||
|
||||
return m;
|
||||
};
|
||||
var del = func() {
|
||||
#print(name~".lcontroller.del()");
|
||||
foreach (var l; me.listeners)
|
||||
removelistener(l);
|
||||
};
|
||||
|
||||
##
|
||||
# dummy/placeholder (will be overridden in ctor and set to the default callback)
|
||||
var searchCmd = func;
|
||||
|
||||
# FIXME: TFC/WXR seems only to be updated on range change/setPos currently ?
|
||||
var searchCmd_default = func {
|
||||
# print("WXR running");
|
||||
if (me.map.getRange() == nil) return;
|
||||
|
||||
printlog(_MP_dbg_lvl, "Doing query: "~name);
|
||||
var results = [];
|
||||
foreach (var n; props.globals.getNode("/instrumentation/wxradar",1).getChildren("storm")) {
|
||||
|
||||
# Model 3 degree radar beam
|
||||
var stormLat = n.getNode("latitude-deg").getValue();
|
||||
var stormLon = n.getNode("longitude-deg").getValue();
|
||||
|
||||
print("processing storm at:",stormLat,'/',stormLon);
|
||||
|
||||
# FIXME: once ported to MapStructure, these should use the encapsulated "aircraft source"/driver stuff
|
||||
var acLat = getprop("/position/latitude-deg");
|
||||
var acLon = getprop("/position/longitude-deg");
|
||||
var stormGeo = geo.Coord.new();
|
||||
var acGeo = geo.Coord.new();
|
||||
|
||||
stormGeo.set_latlon(stormLat, stormLon);
|
||||
acGeo.set_latlon(acLat, acLon);
|
||||
|
||||
var directDistance = acGeo.direct_distance_to(stormGeo);
|
||||
var beamH = 0.1719 * directDistance; # M2FT * tan(3deg)
|
||||
var beamBase = getprop("position/altitude-ft") - beamH;
|
||||
|
||||
if (n.getNode("top-altitude-ft").getValue() > beamBase) {
|
||||
var tmp = geo.Coord.new();
|
||||
tmp.set_latlon(stormLat, stormLon);
|
||||
tmp.radiusNm = n.getNode("radius-nm").getValue();
|
||||
tmp._node = n;
|
||||
tmp.equals = func(r) me._node.equals(r._node);
|
||||
append(results, tmp);
|
||||
}
|
||||
} # foreach
|
||||
|
||||
#print("WXR results:", size(results));
|
||||
return results;
|
||||
} # searchCmd_default
|
23
Nasal/canvas/map/WXR.symbol
Normal file
|
@ -0,0 +1,23 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# Class things:
|
||||
var name = 'WXR'; # storms/weather layer for LW/AW (use thunderstom scenario for testing)
|
||||
var parents = [DotSym];
|
||||
var __self__ = caller(0)[0];
|
||||
DotSym.makeinstance( name, __self__ );
|
||||
|
||||
var element_type = "group"; # we want a group, becomes "me.element"
|
||||
|
||||
# TODO: how to integrate both styling and caching?
|
||||
var draw = func {
|
||||
# TODO: once this works properly, consider using caching here
|
||||
# FIXME: scaling seems way off in comparison with the navdisplay - i.e. hard-coded assumptions about texture size/view port ?
|
||||
me.element.createChild("image")
|
||||
.setFile("Nasal/canvas/map/Images/storm.png")
|
||||
.setSize(128*me.model.radiusNm,128*me.model.radiusNm)
|
||||
.setTranslation(-64*me.model.radiusNm,-64*me.model.radiusNm)
|
||||
.setCenter(0,0);
|
||||
# .setScale(0.3);
|
||||
# TODO: overlapping storms should probably set their z-index according to altitudes
|
||||
|
||||
};
|
||||
|
|
@ -18,12 +18,17 @@ SOURCES["main"] = {
|
|||
|
||||
# Layers which get updated every frame
|
||||
var update_instant = [
|
||||
"TFC",
|
||||
"APS",
|
||||
];
|
||||
# Update at a slightly lower rate, but still
|
||||
# unconditionally
|
||||
var update_quickly = [
|
||||
"TFC", "FLT",
|
||||
];
|
||||
|
||||
var new = func(map, source='main') {
|
||||
if (source != 'main')
|
||||
die ("AI/MP traffic not yet supported (WIP)!");
|
||||
if (!contains(SOURCES, source))
|
||||
__die("AI/MP traffic not yet supported (WIP)!");
|
||||
|
||||
var m = {
|
||||
parents: [__self__],
|
||||
|
@ -32,42 +37,56 @@ var new = func(map, source='main') {
|
|||
_pos: nil, _time: nil, _range: nil,
|
||||
};
|
||||
m.timer1 = maketimer(0, m, update_pos);
|
||||
m.timer2 = maketimer(0, m, update_layers);
|
||||
m.timer1.start();
|
||||
m.timer2.start();
|
||||
m.timer2 = maketimer(0.4, m, update_layers);
|
||||
m.start();
|
||||
m.update_pos();
|
||||
return m;
|
||||
};
|
||||
var del = func(map) {
|
||||
if (map != me.map) die();
|
||||
var start = func() {
|
||||
me.timer1.start();
|
||||
me.timer2.start();
|
||||
};
|
||||
var stop = func() {
|
||||
me.timer1.stop();
|
||||
me.timer2.stop();
|
||||
};
|
||||
var del = func(map) {
|
||||
if (map != me.map) die();
|
||||
me.stop();
|
||||
};
|
||||
|
||||
# Controller methods
|
||||
var update_pos = func {
|
||||
var (lat,lon) = me.source.getPosition();
|
||||
me.map.setPos(lat:lat, lon:lon,
|
||||
hdg:getprop("/orientation/heading-deg"),
|
||||
hdg:me.source.getHeading(),
|
||||
alt:me.source.getAltitude());
|
||||
foreach (var t; update_instant)
|
||||
if ((var l=me.map.getLayer(t)) != nil)
|
||||
l.update();
|
||||
};
|
||||
var update_layers = func {
|
||||
var do_all = 1;
|
||||
var pos = me.map.getPosCoord();
|
||||
var time = systime();
|
||||
var range = me.map.getRange();
|
||||
if (me._pos == nil)
|
||||
me._pos = geo.Coord.new(pos);
|
||||
# Always update if range changed
|
||||
# FIXME: FIX doesn't update unless range is changed?
|
||||
elsif (range == me._range) {
|
||||
var dist_m = me._pos.direct_distance_to(pos);
|
||||
# 2 NM until we update again
|
||||
if (dist_m < 2 * NM2M) return;
|
||||
if (dist_m < 2 * NM2M) do_all = 0;
|
||||
# Update at most every 4 seconds to avoid excessive stutter:
|
||||
elsif (time - me._time < 4) return;
|
||||
elsif (time - me._time < 4) do_all = 0;
|
||||
}
|
||||
if (!do_all) {
|
||||
foreach (var t; update_quickly)
|
||||
if ((var l=me.map.getLayer(t)) != nil)
|
||||
l.update();
|
||||
return;
|
||||
} else
|
||||
printlog(_MP_dbg_lvl, "update aircraft position");
|
||||
var (x,y,z) = pos.xyz();
|
||||
me._pos.set_xyz(x,y,z);
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
# WARNING: *.draw files will be deprecated, see: http://wiki.flightgear.org/MapStructure
|
||||
###
|
||||
#
|
||||
#
|
||||
|
||||
var draw_dme = func (group, dme, controller=nil, lod=0) {
|
||||
var lat = dme.lat;
|
||||
var lon = dme.lon;
|
||||
var name = dme.id;
|
||||
var freq = dme.frequency;
|
||||
|
||||
var dme_grp = group.createChild("group","dme");
|
||||
var icon_dme = dme_grp .createChild("path", name)
|
||||
.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)
|
||||
.setColor(0,0.6,0.85);
|
||||
dme_grp.setGeoPosition(lat, lon)
|
||||
.set("z-index",3);
|
||||
|
||||
if( controller != nil and controller['is_tuned'](freq))
|
||||
icon_dme.setColor(0,1,0);
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
# WARNING: *.layer files will be deprecated, see: http://wiki.flightgear.org/MapStructure
|
||||
var DMELayer = {};
|
||||
DMELayer.new = func(group,name,controller=nil) {
|
||||
var m=Layer.new(group, name, DMEModel, controller);
|
||||
m.setDraw (func draw_layer(layer:m, callback: draw_dme, lod:0) );
|
||||
return m;
|
||||
}
|
||||
|
||||
register_layer("dme", DMELayer);
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
# WARNING: *.model files will be deprecated, see: http://wiki.flightgear.org/MapStructure
|
||||
var DMEModel = {};
|
||||
DMEModel.new = func make( LayerModel, DMEModel );
|
||||
|
||||
DMEModel.init = func {
|
||||
me._view.reset();
|
||||
|
||||
if(0) {
|
||||
debug.dump( me._controller );
|
||||
print(typeof(
|
||||
me._controller.query_range()
|
||||
));
|
||||
}
|
||||
|
||||
var results = positioned.findWithinRange(me._controller.query_range() ,"dme");
|
||||
foreach(result; results) {
|
||||
me.push(result);
|
||||
}
|
||||
|
||||
me.notifyView();
|
||||
}
|
|
@ -9,11 +9,11 @@ FixModel.init = func {
|
|||
var numNum = 0;
|
||||
foreach(result; results) {
|
||||
# Skip airport fixes
|
||||
if(string.match(result.id,"*[^0-9]")) {
|
||||
if(string.match(result.id,"*[^0-9]")) { # this may need to be moved to FIX.lcontroller?
|
||||
me.push(result);
|
||||
numNum = numNum + 1;
|
||||
}
|
||||
}
|
||||
|
||||
me.notifyView();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,605 +3,24 @@
|
|||
# See: http://wiki.flightgear.org/Canvas_ND_Framework
|
||||
# ==============================================================================
|
||||
|
||||
##
|
||||
# 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;
|
||||
var NOTHING = func nil;
|
||||
|
||||
##
|
||||
# so that we only need to update a single line ...
|
||||
# this file contains a hash that declares features in a generic fashion
|
||||
# we want to get rid of the bloated update() method sooner than later
|
||||
# PLEASE DO NOT ADD any code to update() !!
|
||||
# Instead, help clean up the file and move things over to the navdisplay.styles file
|
||||
#
|
||||
# This is the only sane way to keep on generalizing the framework, so that we can
|
||||
# also support different makes/models of NDs in the future
|
||||
#
|
||||
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
|
||||
# a huge bloated update() method is going to make that basically IMPOSSIBLE
|
||||
#
|
||||
# any aircraft-specific ND behavior should be wrapped here,
|
||||
# to isolate/decouple things in the generic NavDisplay class
|
||||
#
|
||||
# TODO: move this to an XML config file
|
||||
#
|
||||
var NDStyles = {
|
||||
##
|
||||
# this configures the 744 ND to help generalize the NavDisplay class itself
|
||||
'Boeing': {
|
||||
font_mapper: func(family, weight) {
|
||||
if( family == "Liberation Sans" and weight == "normal" )
|
||||
return "LiberationFonts/LiberationSans-Regular.ttf";
|
||||
},
|
||||
|
||||
# where all the symbols are stored
|
||||
# TODO: SVG elements should be renamed to use boeing/airbus prefix
|
||||
# aircraft developers should all be editing the same ND.svg image
|
||||
# the code can deal with the differences now
|
||||
svg_filename: "Nasal/canvas/map/boeingND.svg",
|
||||
##
|
||||
## this loads and configures existing layers (currently, *.layer files in Nasal/canvas/map)
|
||||
##
|
||||
|
||||
layers: [
|
||||
{ name:'FIX', isMapStructure:1, update_on:['toggle_range','toggle_waypoints'],
|
||||
# FIXME: this is a really ugly place for controller code
|
||||
predicate: func(nd, layer) {
|
||||
# print("Running fix layer predicate");
|
||||
# toggle visibility here
|
||||
var visible=nd.get_switch('toggle_waypoints') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40);
|
||||
layer.group.setVisible( nd.get_switch('toggle_waypoints') );
|
||||
if (visible) {
|
||||
#print("Updating MapStructure ND layer: FIX");
|
||||
# (Hopefully) smart update
|
||||
layer.update();
|
||||
}
|
||||
}, # end of layer update predicate
|
||||
}, # 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:'airplaneSymbol', disabled:1, update_on:['toggle_display_mode'],
|
||||
predicate: func(nd, layer) {
|
||||
var visible = nd.get_switch('toggle_display_mode') == "PLAN";
|
||||
if (visible) {
|
||||
trigger_update( layer );
|
||||
} layer._view.setVisible(visible);
|
||||
},
|
||||
},
|
||||
{ name:'APS', isMapStructure:1, update_on:['toggle_display_mode'],
|
||||
predicate: func(nd, layer) {
|
||||
var visible = nd.get_switch('toggle_display_mode') == "PLAN";
|
||||
layer.group.setVisible( visible );
|
||||
if (visible) {
|
||||
layer.update();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{ name:'APT', isMapStructure:1, update_on:['toggle_range','toggle_airports','toggle_display_mode'],
|
||||
predicate: func(nd, layer) {
|
||||
# toggle visibility here
|
||||
var visible=nd.get_switch('toggle_airports') and nd.in_mode('toggle_display_mode', ['MAP']);
|
||||
layer.group.setVisible( visible );
|
||||
if (visible) {
|
||||
#print("Updating MapStructure ND layer: APT");
|
||||
layer.update();
|
||||
}
|
||||
}, # end of layer update predicate
|
||||
}, # end of APT layer
|
||||
|
||||
# Should distinct between low and high altitude navaids. Hiding above 40 NM for now, to prevent clutter/lag.
|
||||
{ name:'VOR', isMapStructure:1, update_on:['toggle_range','toggle_stations','toggle_display_mode'],
|
||||
# FIXME: this is a really ugly place for controller code
|
||||
predicate: func(nd, layer) {
|
||||
# toggle visibility here
|
||||
var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40);
|
||||
layer.group.setVisible( visible );
|
||||
if (visible) {
|
||||
#print("Updating MapStructure ND layer: VOR");
|
||||
layer.update();
|
||||
}
|
||||
}, # end of layer update predicate
|
||||
}, # end of VOR layer
|
||||
|
||||
{ name:'DME', isMapStructure:1, update_on:['toggle_range','toggle_stations'],
|
||||
# FIXME: this is a really ugly place for controller code
|
||||
predicate: func(nd, layer) {
|
||||
var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40);
|
||||
# toggle visibility here
|
||||
layer.group.setVisible( visible );
|
||||
if (visible) {
|
||||
#print("Updating MapStructure ND layer: DME");
|
||||
layer.update();
|
||||
}
|
||||
}, # end of layer update predicate
|
||||
}, # end of DME layer
|
||||
|
||||
{ name:'TFC', isMapStructure:1, update_on:['toggle_range','toggle_traffic'],
|
||||
predicate: func(nd, layer) {
|
||||
var visible = nd.get_switch('toggle_traffic');
|
||||
layer.group.setVisible( visible );
|
||||
if (visible) {
|
||||
#print("Updating MapStructure ND layer: TFC");
|
||||
layer.update();
|
||||
}
|
||||
}, # end of layer update predicate
|
||||
}, # 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_range','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) nd.aircraft_source.get_spd() > 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(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'tasLbl',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.aircraft_source.get_spd() > 100,
|
||||
is_true: func(nd) nd.symbols.tasLbl.show(),
|
||||
is_false: func(nd) nd.symbols.tasLbl.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ilsFreq',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.ilsFreq.show();
|
||||
if(getprop("instrumentation/nav/in-range"))
|
||||
nd.symbols.ilsFreq.setText(getprop("instrumentation/nav/nav-id"));
|
||||
else
|
||||
nd.symbols.ilsFreq.setText(getprop("instrumentation/nav/frequencies/selected-mhz-fmt"));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.ilsFreq.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ilsLbl',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.ilsLbl.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.ilsLbl.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'wpActiveId',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) getprop("/autopilot/route-manager/wp/id") != nil and getprop("autopilot/route-manager/active")
|
||||
and nd.in_mode('toggle_display_mode', ['MAP','PLAN']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.wpActiveId.setText(getprop("/autopilot/route-manager/wp/id"));
|
||||
nd.symbols.wpActiveId.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.wpActiveId.hide(),
|
||||
}, # of wpActiveId.impl
|
||||
}, # of wpActiveId
|
||||
{
|
||||
id: 'wpActiveDist',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) getprop("/autopilot/route-manager/wp/dist") != nil and getprop("autopilot/route-manager/active")
|
||||
and nd.in_mode('toggle_display_mode', ['MAP','PLAN']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.wpActiveDist.setText(sprintf("%3.01f",getprop("/autopilot/route-manager/wp/dist")));
|
||||
nd.symbols.wpActiveDist.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.wpActiveDist.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'wpActiveDistLbl',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) getprop("/autopilot/route-manager/wp/dist") != nil and getprop("autopilot/route-manager/active")
|
||||
and nd.in_mode('toggle_display_mode', ['MAP','PLAN']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.wpActiveDistLbl.show();
|
||||
if(getprop("/autopilot/route-manager/wp/dist") > 1000)
|
||||
nd.symbols.wpActiveDistLbl.setText(" NM");
|
||||
},
|
||||
is_false: func(nd) nd.symbols.wpActiveDistLbl.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'eta',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) getprop("autopilot/route-manager/wp/eta") != nil and getprop("autopilot/route-manager/active")
|
||||
and nd.in_mode('toggle_display_mode', ['MAP','PLAN']),
|
||||
is_true: func(nd) {
|
||||
var etaSec = getprop("/sim/time/utc/day-seconds")+getprop("autopilot/route-manager/wp/eta-seconds");
|
||||
var h = math.floor(etaSec/3600);
|
||||
etaSec=etaSec-3600*h;
|
||||
var m = math.floor(etaSec/60);
|
||||
etaSec=etaSec-60*m;
|
||||
var s = etaSec/10;
|
||||
if (h>24) h=h-24;
|
||||
nd.symbols.eta.setText(sprintf("%02.0f%02.0f.%01.0fz",h,m,s));
|
||||
nd.symbols.eta.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.eta.hide(),
|
||||
}, # of eta.impl
|
||||
}, # of eta
|
||||
{
|
||||
id: 'gsGroup',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']),
|
||||
is_true: func(nd) {
|
||||
if(nd.get_switch('toggle_centered'))
|
||||
nd.symbols.gsGroup.setTranslation(0,0);
|
||||
else
|
||||
nd.symbols.gsGroup.setTranslation(0,150);
|
||||
nd.symbols.gsGroup.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.gsGroup.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'hdg',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','MAP','VOR']),
|
||||
is_true: func(nd) {
|
||||
var hdgText = "";
|
||||
if((nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_display_type') == "CRT")
|
||||
or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD"))
|
||||
{
|
||||
if(nd.get_switch('toggle_true_north'))
|
||||
hdgText = nd.aircraft_source.get_trk_tru();
|
||||
else
|
||||
hdgText = nd.aircraft_source.get_trk_mag();
|
||||
} else {
|
||||
if(nd.get_switch('toggle_true_north'))
|
||||
hdgText = nd.aircraft_source.get_hdg_tru();
|
||||
else
|
||||
hdgText = nd.aircraft_source.get_hdg_mag();
|
||||
}
|
||||
if(hdgText < 0.5) hdgText = 360 + hdgText;
|
||||
elsif(hdgText >= 360.5) hdgText = hdgText - 360;
|
||||
nd.symbols.hdg.setText(sprintf("%03.0f", hdgText));
|
||||
},
|
||||
is_false: NOTHING,
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'hdgGroup',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','MAP','VOR']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.hdgGroup.show();
|
||||
if(nd.get_switch('toggle_centered'))
|
||||
nd.symbols.hdgGroup.setTranslation(0,100);
|
||||
else
|
||||
nd.symbols.hdgGroup.setTranslation(0,0);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.hdgGroup.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'gs',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
common: func(nd) nd.symbols.gs.setText(sprintf("%3.0f",nd.aircraft_source.get_gnd_spd() )),
|
||||
predicate: func(nd) nd.aircraft_source.get_gnd_spd() >= 30,
|
||||
is_true: func(nd) {
|
||||
nd.symbols.gs.setFontSize(36);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.gs.setFontSize(52),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'rangeArcs',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) !nd.get_switch('toggle_centered') and nd.get_switch('toggle_rangearc'),
|
||||
is_true: func(nd) nd.symbols.rangeArcs.show(),
|
||||
is_false: func(nd) nd.symbols.rangeArcs.hide(),
|
||||
}, # of rangeArcs.impl
|
||||
}, # of rangeArcs
|
||||
{
|
||||
id:'rangePln1',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN",
|
||||
is_true: func(nd) {
|
||||
nd.symbols.rangePln1.show();
|
||||
nd.symbols.rangePln1.setText(sprintf("%3.0f",nd.rangeNm()));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.rangePln1.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'rangePln2',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN",
|
||||
is_true: func(nd) {
|
||||
nd.symbols.rangePln2.show();
|
||||
nd.symbols.rangePln2.setText(sprintf("%3.0f",nd.rangeNm()/2));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.rangePln2.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'rangePln3',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN",
|
||||
is_true: func(nd) {
|
||||
nd.symbols.rangePln3.show();
|
||||
nd.symbols.rangePln3.setText(sprintf("%3.0f",nd.rangeNm()/2));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.rangePln3.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'rangePln4',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN",
|
||||
is_true: func(nd) {
|
||||
nd.symbols.rangePln4.show();
|
||||
nd.symbols.rangePln4.setText(sprintf("%3.0f",nd.rangeNm()));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.rangePln4.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'crsLbl',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']),
|
||||
is_true: func(nd) nd.symbols.crsLbl.show(),
|
||||
is_false: func(nd) nd.symbols.crsLbl.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'crs',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.crs.show();
|
||||
if(getprop("instrumentation/nav/radials/selected-deg") != nil)
|
||||
nd.symbols.crs.setText(sprintf("%03.0f",getprop("instrumentation/nav/radials/selected-deg")));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.crs.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'dmeLbl',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']),
|
||||
is_true: func(nd) nd.symbols.dmeLbl.show(),
|
||||
is_false: func(nd) nd.symbols.dmeLbl.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'dme',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.dme.show();
|
||||
if(getprop("instrumentation/dme/in-range"))
|
||||
nd.symbols.dme.setText(sprintf("%3.1f",getprop("instrumentation/dme/indicated-distance-nm")));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.dme.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'trkInd2',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['MAP','APP','VOR']) and nd.get_switch('toggle_centered')),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.trkInd2.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.trkInd2.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'vorCrsPtr',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered')),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.vorCrsPtr.show();
|
||||
if((nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_display_type') == "CRT")
|
||||
or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD"))
|
||||
nd.symbols.vorCrsPtr.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_trk_mag())*D2R);
|
||||
else
|
||||
nd.symbols.vorCrsPtr.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_hdg_mag())*D2R);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.vorCrsPtr.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'vorCrsPtr2',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_centered')),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.vorCrsPtr2.show();
|
||||
if((nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_display_type') == "CRT")
|
||||
or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD"))
|
||||
nd.symbols.vorCrsPtr2.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_trk_mag())*D2R);
|
||||
else
|
||||
nd.symbols.vorCrsPtr2.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_hdg_mag())*D2R);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.vorCrsPtr2.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'gsDiamond',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']) and getprop("instrumentation/nav/gs-in-range"),
|
||||
is_true: func(nd) {
|
||||
var gs_deflection = getprop("instrumentation/nav/gs-needle-deflection-norm");
|
||||
if(gs_deflection != nil)
|
||||
nd.symbols.gsDiamond.setTranslation(gs_deflection*150,0);
|
||||
if(abs(gs_deflection) < 0.99)
|
||||
nd.symbols.gsDiamond.setColorFill(1,0,1,1);
|
||||
else
|
||||
nd.symbols.gsDiamond.setColorFill(0,0,0,1);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.gsGroup.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'locPtr',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered') and getprop("instrumentation/nav/in-range")),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.locPtr.show();
|
||||
var deflection = getprop("instrumentation/nav/heading-needle-deflection-norm");
|
||||
nd.symbols.locPtr.setTranslation(deflection*150,0);
|
||||
if(abs(deflection) < 0.99)
|
||||
nd.symbols.locPtr.setColorFill(1,0,1,1);
|
||||
else
|
||||
nd.symbols.locPtr.setColorFill(0,0,0,1);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.locPtr.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'locPtr2',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_centered') and getprop("instrumentation/nav/in-range")),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.locPtr2.show();
|
||||
var deflection = getprop("instrumentation/nav/heading-needle-deflection-norm");
|
||||
nd.symbols.locPtr2.setTranslation(deflection*150,0);
|
||||
if(abs(deflection) < 0.99)
|
||||
nd.symbols.locPtr2.setColorFill(1,0,1,1);
|
||||
else
|
||||
nd.symbols.locPtr2.setColorFill(0,0,0,1);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.locPtr2.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'wind',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: ALWAYS,
|
||||
is_true: func(nd) {
|
||||
var windDir = getprop("environment/wind-from-heading-deg");
|
||||
if(!nd.get_switch('toggle_true_north'))
|
||||
windDir = windDir - getprop("environment/magnetic-variation-deg");
|
||||
if(windDir < 0.5) windDir = 360 + windDir;
|
||||
elsif(windDir >= 360.5) windDir = windDir - 360;
|
||||
nd.symbols.wind.setText(sprintf("%03.0f / %02.0f",windDir,getprop("environment/wind-speed-kt")));
|
||||
},
|
||||
is_false: NOTHING,
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'windArrow',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) (!(nd.in_mode('toggle_display_mode', ['PLAN']) and (nd.get_switch('toggle_display_type') == "LCD")) and nd.aircraft_source.get_spd() > 100),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.windArrow.show();
|
||||
var windArrowRot = getprop("environment/wind-from-heading-deg");
|
||||
if((nd.in_mode('toggle_display_mode', ['MAP','PLAN']) and nd.get_switch('toggle_display_type') == "CRT")
|
||||
or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD"))
|
||||
windArrowRot = windArrowRot - nd.aircraft_source.get_trk_mag();
|
||||
else
|
||||
windArrowRot = windArrowRot - nd.aircraft_source.get_hdg_mag();
|
||||
nd.symbols.windArrow.setRotation(windArrowRot*D2R);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.windArrow.hide(),
|
||||
},
|
||||
},
|
||||
|
||||
], # end of vector with features
|
||||
|
||||
|
||||
}, # end of Boeing style
|
||||
#####
|
||||
##
|
||||
## add support for other aircraft/ND types and styles here (Airbus etc)
|
||||
##
|
||||
##
|
||||
|
||||
}; # end of NDStyles
|
||||
io.include("Nasal/canvas/map/navdisplay.styles");
|
||||
|
||||
##
|
||||
# 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)
|
||||
#
|
||||
# TODO: this predates aircraftpos.controller (MapStructure) should probably be unified to some degree ...
|
||||
|
||||
var NDSourceDriver = {};
|
||||
NDSourceDriver.new = func {
|
||||
|
@ -665,18 +84,10 @@ var default_switches = {
|
|||
'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'},
|
||||
'toggle_rangearc': {path: '/mfd/rangearc', value:0, type:'BOOL'},
|
||||
'toggle_track_heading': {path: '/trk-selected', value:0, type:'BOOL'},
|
||||
'toggle_rangearc': {path: '/mfd/rangearc', value:0, type:'BOOL'},
|
||||
'toggle_track_heading':{path: '/trk-selected', value:0, type:'BOOL'},
|
||||
};
|
||||
|
||||
# Hack to update weather radar once every 10 seconds
|
||||
var update_weather = func {
|
||||
if (getprop("/instrumentation/efis/inputs/wxr") != nil)
|
||||
setprop("/instrumentation/efis/inputs/wxr",getprop("/instrumentation/efis/inputs/wxr"));
|
||||
settimer(update_weather, 10);
|
||||
}
|
||||
update_weather();
|
||||
|
||||
##
|
||||
# TODO:
|
||||
# - introduce a MFD class (use it also for PFD/EICAS)
|
||||
|
@ -690,6 +101,8 @@ var NavDisplay = {
|
|||
print("Cleaning up NavDisplay");
|
||||
# shut down all timers and other loops here
|
||||
me.update_timer.stop();
|
||||
foreach(var t; me.timers)
|
||||
t.stop();
|
||||
foreach(var l; me.listeners)
|
||||
removelistener(l);
|
||||
# clean up MapStructure
|
||||
|
@ -701,6 +114,11 @@ var NavDisplay = {
|
|||
NavDisplay.id -= 1;
|
||||
},
|
||||
|
||||
addtimer: func(interval, cb) {
|
||||
append(me.timers, var job=maketimer(interval, cb));
|
||||
return job; # so that we can directly work with the timer (start/stop)
|
||||
},
|
||||
|
||||
listen: func(p,c) {
|
||||
append(me.listeners, setlistener(p,c));
|
||||
},
|
||||
|
@ -741,6 +159,8 @@ var NavDisplay = {
|
|||
};
|
||||
}, # of connectAI
|
||||
|
||||
setTimerInterval: func(update_time=0.05) me.update_timer.restart(update_time),
|
||||
|
||||
# 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') {
|
||||
|
@ -749,6 +169,7 @@ var NavDisplay = {
|
|||
|
||||
m.inited = 0;
|
||||
|
||||
m.timers=[];
|
||||
m.listeners=[]; # for cleanup handling
|
||||
m.aircraft_source = NDSourceDriver.new(); # uses the main aircraft as the driver/source (speeds, position, heading)
|
||||
|
||||
|
@ -799,12 +220,11 @@ var NavDisplay = {
|
|||
|
||||
return m;
|
||||
},
|
||||
newMFD: func(canvas_group, parent=nil, options=nil)
|
||||
newMFD: func(canvas_group, parent=nil, options=nil, update_time=0.05)
|
||||
{
|
||||
if (me.inited) die("MFD already was added to scene");
|
||||
me.inited = 1;
|
||||
#me.listen("/sim/signals/reinit", func(n) me.del() );
|
||||
me.update_timer = maketimer(0.05, func me.update() ); # TODO: make interval configurable via ctor
|
||||
me.update_timer = maketimer(update_time, func me.update() );
|
||||
me.nd = canvas_group;
|
||||
me.canvas_handle = parent;
|
||||
|
||||
|
@ -814,7 +234,6 @@ var NavDisplay = {
|
|||
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).updateCenter();
|
||||
if(contains(feature.impl,'init')) feature.impl.init(me.nd, feature); # call The element's init code (i.e. updateCenter)
|
||||
}
|
||||
|
@ -839,10 +258,9 @@ var NavDisplay = {
|
|||
|
||||
me.map = me.nd.createChild("map","map")
|
||||
.set("clip", "rect(124, 1024, 1024, 0)")
|
||||
.set("screen-range", "700");
|
||||
.set("screen-range", 700);
|
||||
|
||||
# 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');
|
||||
me.update_sub(); # init some map properties based on switches
|
||||
|
||||
# predicate for the draw controller
|
||||
var is_tuned = func(freq) {
|
||||
|
@ -876,7 +294,6 @@ var NavDisplay = {
|
|||
var controller = {
|
||||
parents: [canvas.Map.Controller],
|
||||
_pos: nil, _time: nil,
|
||||
query_range: func get_range(),
|
||||
is_tuned:is_tuned,
|
||||
get_tuned_course:get_course_by_freq,
|
||||
get_position: get_current_position,
|
||||
|
@ -919,18 +336,31 @@ var NavDisplay = {
|
|||
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;
|
||||
|
||||
var the_layer = nil;
|
||||
if(!layer['isMapStructure']) # set up an old layer
|
||||
the_layer = me.layers[layer.name] = canvas.MAP_LAYERS[layer.name].new( render_target, layer.name, controller );
|
||||
if(!layer['isMapStructure']) # set up an old INEFFICIENT and SLOW layer
|
||||
the_layer = me.layers[layer.name] = canvas.MAP_LAYERS[layer.name].new( me.map, layer.name, controller );
|
||||
else {
|
||||
printlog(_MP_dbg_lvl, "Setting up MapStructure-based layer for ND, name:", layer.name);
|
||||
var opt = options != nil and options[layer.name] != nil ? options[layer.name] :nil;
|
||||
# print("Options is: ", opt!=nil?"enabled":"disabled");
|
||||
render_target.addLayer(factory: canvas.SymbolLayer, type_arg: layer.name, options:opt);
|
||||
the_layer = me.layers[layer.name] = render_target.getLayer(layer.name);
|
||||
me.map.addLayer(
|
||||
factory: canvas.SymbolLayer,
|
||||
type_arg: layer.name,
|
||||
options:opt,
|
||||
visible:0,
|
||||
priority: layer['z-index']
|
||||
);
|
||||
the_layer = me.layers[layer.name] = me.map.getLayer(layer.name);
|
||||
if (1) (func {
|
||||
var l = layer;
|
||||
var _predicate = l.predicate;
|
||||
l.predicate = func {
|
||||
var t = systime();
|
||||
call(_predicate, arg, me);
|
||||
printlog(_MP_dbg_lvl, "Took "~((systime()-t)*1000)~"ms to update layer "~l.name);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
# now register all layer specific notification listeners and their corresponding update predicate/callback
|
||||
|
@ -938,6 +368,14 @@ var NavDisplay = {
|
|||
# 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) {
|
||||
# this handles timers
|
||||
if (typeof(event)=='hash' and contains(event, 'rate_hz')) {
|
||||
#print("FIXME: navdisplay.mfd timer handling is broken ATM");
|
||||
var job=me.addtimer(1/event.rate_hz, event_handler);
|
||||
job.start();
|
||||
}
|
||||
# and this listeners
|
||||
else
|
||||
# print("Setting up subscription:", event, " for ", layer.name, " handler id:", id(event_handler) );
|
||||
me.listen_switch(event, event_handler);
|
||||
} # foreach event subscription
|
||||
|
@ -950,17 +388,7 @@ var NavDisplay = {
|
|||
# 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
|
||||
# TODO: move this to RTE.lcontroller ?
|
||||
me.listen("/autopilot/route-manager/current-wp", func(activeWp) {
|
||||
canvas.updatewp( activeWp.getValue() );
|
||||
});
|
||||
|
@ -972,13 +400,10 @@ var NavDisplay = {
|
|||
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
|
||||
# Helper function for below (update()) and above (newMFD())
|
||||
# to ensure position etc. are correct.
|
||||
update_sub: func()
|
||||
{
|
||||
var _time = systime();
|
||||
|
||||
# Variables:
|
||||
var userLat = me.aircraft_source.get_lat();
|
||||
var userLon = me.aircraft_source.get_lon();
|
||||
|
@ -1009,9 +434,9 @@ var NavDisplay = {
|
|||
if (me.aircraft_source.get_gnd_spd() < 80)
|
||||
userTrk = userHdg;
|
||||
|
||||
if((me.in_mode('toggle_display_mode', ['MAP']) and me.get_switch('toggle_display_type') == "CRT")
|
||||
or (me.get_switch('toggle_track_heading') and me.get_switch('toggle_display_type') == "LCD"))
|
||||
{
|
||||
if((me.in_mode('toggle_display_mode', ['MAP']) and me.get_switch('toggle_display_type') == "CRT")
|
||||
or (me.get_switch('toggle_track_heading') and me.get_switch('toggle_display_type') == "LCD"))
|
||||
{
|
||||
userHdgTrk = userTrk;
|
||||
userHdgTrkTru = userTrkTru;
|
||||
me.symbols.hdgTrk.setText("TRK");
|
||||
|
@ -1044,12 +469,23 @@ var NavDisplay = {
|
|||
pos.lon = userLon;
|
||||
}
|
||||
call(me.map.setPos, [pos.lat, pos.lon], me.map, pos);
|
||||
},
|
||||
# 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
|
||||
# TODO: Hooray is still waiting for a really rainy weekend to clean up all the mess here... so plz don't add to it!
|
||||
update: func() # FIXME: This stuff is still too aircraft specific, cannot easily be reused by other aircraft
|
||||
{
|
||||
var _time = systime();
|
||||
|
||||
call(me.update_sub, nil, nil, caller(0)[0]); # call this in the same namespace to "steal" its variables
|
||||
|
||||
# MapStructure update!
|
||||
if (me.map.controller.should_update_all()) {
|
||||
me.map.update();
|
||||
} else {
|
||||
# TODO: ugly list here
|
||||
# FIXME: use a VOLATILE layer helper here that handles TFC, APS, WXR etc ?
|
||||
me.map.update(func(layer) (var n=layer.type) == "TFC" or n == "APS");
|
||||
}
|
||||
|
||||
|
@ -1134,11 +570,11 @@ var NavDisplay = {
|
|||
if (hdg_bug_active == nil)
|
||||
hdg_bug_active = 1;
|
||||
|
||||
if((me.in_mode('toggle_display_mode', ['MAP']) and me.get_switch('toggle_display_type') == "CRT")
|
||||
or (me.get_switch('toggle_track_heading') and me.get_switch('toggle_display_type') == "LCD"))
|
||||
{
|
||||
if((me.in_mode('toggle_display_mode', ['MAP']) and me.get_switch('toggle_display_type') == "CRT")
|
||||
or (me.get_switch('toggle_track_heading') and me.get_switch('toggle_display_type') == "LCD"))
|
||||
{
|
||||
me.symbols.trkInd.setRotation(0);
|
||||
me.symbols.trkInd2.setRotation(0);
|
||||
me.symbols.trkInd2.setRotation(0);
|
||||
me.symbols.curHdgPtr.setRotation((userHdg-userTrk)*D2R);
|
||||
me.symbols.curHdgPtr2.setRotation((userHdg-userTrk)*D2R);
|
||||
}
|
||||
|
@ -1174,26 +610,26 @@ var NavDisplay = {
|
|||
me.symbols.compass.show();
|
||||
}
|
||||
|
||||
var staPtrVis = !me.in_mode('toggle_display_mode', ['PLAN']);
|
||||
if((me.in_mode('toggle_display_mode', ['MAP']) and me.get_switch('toggle_display_type') == "CRT")
|
||||
or (me.get_switch('toggle_track_heading') and me.get_switch('toggle_display_type') == "LCD"))
|
||||
{
|
||||
var vorheading = userTrkTru;
|
||||
var adfheading = userTrkMag;
|
||||
}
|
||||
else
|
||||
{
|
||||
var vorheading = userHdgTru;
|
||||
var adfheading = userHdgMag;
|
||||
}
|
||||
var staPtrVis = !me.in_mode('toggle_display_mode', ['PLAN']);
|
||||
if((me.in_mode('toggle_display_mode', ['MAP']) and me.get_switch('toggle_display_type') == "CRT")
|
||||
or (me.get_switch('toggle_track_heading') and me.get_switch('toggle_display_type') == "LCD"))
|
||||
{
|
||||
var vorheading = userTrkTru;
|
||||
var adfheading = userTrkMag;
|
||||
}
|
||||
else
|
||||
{
|
||||
var vorheading = userHdgTru;
|
||||
var adfheading = userHdgMag;
|
||||
}
|
||||
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") - vorheading;
|
||||
var nav0hdg=getprop("instrumentation/nav/heading-deg") - vorheading;
|
||||
if(getprop("instrumentation/nav[1]/heading-deg") != nil)
|
||||
var nav1hdg=getprop("instrumentation/nav[1]/heading-deg") - vorheading;
|
||||
var adf0hdg=getprop("instrumentation/adf/indicated-bearing-deg") - adfheading;
|
||||
var adf1hdg=getprop("instrumentation/adf[1]/indicated-bearing-deg") - adfheading;
|
||||
var nav1hdg=getprop("instrumentation/nav[1]/heading-deg") - vorheading;
|
||||
var adf0hdg=getprop("instrumentation/adf/indicated-bearing-deg") - adfheading;
|
||||
var adf1hdg=getprop("instrumentation/adf[1]/indicated-bearing-deg") - adfheading;
|
||||
if(!me.get_switch('toggle_centered'))
|
||||
{
|
||||
if(me.in_mode('toggle_display_mode', ['PLAN']))
|
||||
|
@ -1345,8 +781,7 @@ var NavDisplay = {
|
|||
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']));
|
||||
# Okay, _how_ do we hook this up with FGPlot?
|
||||
#print("ND update took "~(systime()-_time)~" seconds");
|
||||
printlog(_MP_dbg_lvl, "Total ND update took "~((systime()-_time)*100)~"ms");
|
||||
setprop("/instrumentation/navdisplay["~ NavDisplay.id ~"]/update-ms", systime() - _time);
|
||||
|
||||
}
|
||||
} # of update() method (50% of our file ...seriously?)
|
||||
};
|
||||
|
|
619
Nasal/canvas/map/navdisplay.styles
Normal file
|
@ -0,0 +1,619 @@
|
|||
# ==============================================================================
|
||||
# Boeing Navigation Display by Gijs de Rooy
|
||||
# See: http://wiki.flightgear.org/Canvas_ND_Framework
|
||||
# ==============================================================================
|
||||
# This file makes use of the MapStructure framework, see: http://wiki.flightgear.org/Canvas_MapStructure
|
||||
#
|
||||
# Sooner or later, some parts will be revamped by coming up with a simple animation framework: http://wiki.flightgear.org/NavDisplay#mapping_vs._SVG_animation
|
||||
|
||||
##
|
||||
# pseudo DSL-ish: use these as placeholders in the config hash below
|
||||
var ALWAYS = func 1;
|
||||
var NOTHING = func nil;
|
||||
|
||||
##
|
||||
# TODO: move ND-specific implementation details into this lookup hash
|
||||
# so that other aircraft and ND types can be more easily supported
|
||||
#
|
||||
# any aircraft-specific ND behavior should be wrapped here,
|
||||
# to isolate/decouple things in the generic NavDisplay class
|
||||
#
|
||||
# TODO: move this to an XML config file (maybe supporting SGCondition and/or SGStateMachine markup for the logic?)
|
||||
#
|
||||
var NDStyles = {
|
||||
##
|
||||
# this configures the 744 ND to help generalize the NavDisplay class itself
|
||||
'Boeing': {
|
||||
font_mapper: func(family, weight) {
|
||||
if( family == "Liberation Sans" and weight == "normal" )
|
||||
return "LiberationFonts/LiberationSans-Regular.ttf";
|
||||
},
|
||||
|
||||
# where all the symbols are stored
|
||||
# TODO: SVG elements should be renamed to use boeing/airbus prefix
|
||||
# aircraft developers should all be editing the same ND.svg image
|
||||
# the code can deal with the differences now
|
||||
svg_filename: "Nasal/canvas/map/Images/boeingND.svg",
|
||||
##
|
||||
## this loads and configures existing layers (currently, *.layer files in Nasal/canvas/map)
|
||||
##
|
||||
|
||||
|
||||
# TODO: phase out isMapStructure flag once map.nas & *.draw files are killed
|
||||
layers: [
|
||||
# TODO: take z-indices from *.draw files -- now handled by MapStructure in the addLayer method.
|
||||
{ name:'FIX', isMapStructure:1, update_on:['toggle_range','toggle_waypoints'],
|
||||
# FIXME: this is a really ugly place for controller code
|
||||
predicate: func(nd, layer) {
|
||||
# print("Running fix layer predicate");
|
||||
# toggle visibility here
|
||||
var visible=nd.get_switch('toggle_waypoints') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40);
|
||||
layer.group.setVisible( nd.get_switch('toggle_waypoints') );
|
||||
if (visible) {
|
||||
#print("Updating MapStructure ND layer: FIX");
|
||||
# (Hopefully) smart update
|
||||
layer.update();
|
||||
}
|
||||
}, # end of layer update predicate
|
||||
'z-index': 3,
|
||||
}, # end of FIX layer
|
||||
|
||||
# Should redraw every 10 seconds TODO: use new MapStructure/WXR here once that works properly (Gijs should check first!)
|
||||
{ name:'WXR', isMapStructure:1, update_on:[ {rate_hz: 0.1}, 'toggle_range','toggle_weather','toggle_display_mode'],
|
||||
predicate: func(nd, layer) {
|
||||
#print("Running storms predicate");
|
||||
var visible=nd.get_switch('toggle_weather') and nd.get_switch('toggle_display_mode') != "PLAN";
|
||||
layer.group.setVisible(visible);
|
||||
if (visible) {
|
||||
print("storms update requested! (timer issue when closing the dialog?)");
|
||||
layer.update();
|
||||
}
|
||||
}, # end of layer update predicate
|
||||
}, # end of storms/WXR layer
|
||||
|
||||
{ name:'APS', isMapStructure:1, update_on:['toggle_display_mode'],
|
||||
predicate: func(nd, layer) {
|
||||
var visible = nd.get_switch('toggle_display_mode') == "PLAN";
|
||||
layer.group.setVisible( visible );
|
||||
if (visible) {
|
||||
layer.update();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{ name:'APT', isMapStructure:1, update_on:['toggle_range','toggle_airports','toggle_display_mode'],
|
||||
predicate: func(nd, layer) {
|
||||
# toggle visibility here
|
||||
var visible=nd.get_switch('toggle_airports') and nd.in_mode('toggle_display_mode', ['MAP']);
|
||||
layer.group.setVisible( visible );
|
||||
if (visible) {
|
||||
#print("Updating MapStructure ND layer: APT");
|
||||
layer.update();
|
||||
}
|
||||
}, # end of layer update predicate
|
||||
'z-index': 1,
|
||||
}, # end of APT layer
|
||||
|
||||
# Should distinct between low and high altitude navaids. Hiding above 40 NM for now, to prevent clutter/lag.
|
||||
{ name:'VOR', isMapStructure:1, update_on:['toggle_range','toggle_stations','toggle_display_mode'],
|
||||
# FIXME: this is a really ugly place for controller code
|
||||
predicate: func(nd, layer) {
|
||||
# toggle visibility here
|
||||
var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40);
|
||||
layer.group.setVisible( visible );
|
||||
if (visible) {
|
||||
#print("Updating MapStructure ND layer: VOR");
|
||||
layer.update();
|
||||
}
|
||||
}, # end of layer update predicate
|
||||
'z-index': 3,
|
||||
}, # end of VOR layer
|
||||
|
||||
{ name:'DME', isMapStructure:1, update_on:['toggle_range','toggle_stations'],
|
||||
# FIXME: this is a really ugly place for controller code
|
||||
predicate: func(nd, layer) {
|
||||
var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40);
|
||||
# toggle visibility here
|
||||
layer.group.setVisible( visible );
|
||||
if (visible) {
|
||||
#print("Updating MapStructure ND layer: DME");
|
||||
layer.update();
|
||||
}
|
||||
}, # end of layer update predicate
|
||||
'z-index': 3,
|
||||
}, # end of DME layer
|
||||
|
||||
{ name:'TFC', isMapStructure:1, update_on:['toggle_range','toggle_traffic'],
|
||||
predicate: func(nd, layer) {
|
||||
var visible = nd.get_switch('toggle_traffic');
|
||||
layer.group.setVisible( visible );
|
||||
if (visible) {
|
||||
#print("Updating MapStructure ND layer: TFC");
|
||||
layer.update();
|
||||
}
|
||||
}, # end of layer update predicate
|
||||
'z-index': 1,
|
||||
}, # end of traffic layer
|
||||
|
||||
{ name:'runway-nd', update_on:['toggle_range','toggle_display_mode'],
|
||||
predicate: func(nd, layer) {
|
||||
#print("runway-nd wants to be ported to MapStructure");
|
||||
var visible = (nd.rangeNm() <= 40) and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP','PLAN']) ;
|
||||
if (visible)
|
||||
layer._model.init(); # clear & redraw
|
||||
layer._view.setVisible( visible );
|
||||
}, # end of layer update predicate
|
||||
}, # end of airports-nd layer
|
||||
|
||||
{ name:'RTE', isMapStructure:1, update_on:['toggle_range','toggle_display_mode'],
|
||||
predicate: func(nd, layer) {
|
||||
var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN']));
|
||||
layer.group.setVisible( visible );
|
||||
if (visible)
|
||||
layer.update();
|
||||
}, # end of layer update predicate
|
||||
'z-index': 2, # apparently route.draw doesn't have a z-index?
|
||||
}, # end of route layer
|
||||
{ name:'WPT', isMapStructure:1, update_on:['toggle_range','toggle_display_mode'],
|
||||
predicate: func(nd, layer) {
|
||||
var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN']));
|
||||
layer.group.setVisible( visible );
|
||||
|
||||
if (visible)
|
||||
layer.update();
|
||||
}, # end of layer update predicate
|
||||
'z-index': 4,
|
||||
}, # end of waypoint layer
|
||||
{ name:'ALT-profile', isMapStructure:1, update_on:['toggle_range','toggle_display_mode'],
|
||||
predicate: func(nd, layer) {
|
||||
var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN']));
|
||||
layer.group.setVisible( visible );
|
||||
|
||||
if (visible)
|
||||
layer.update();
|
||||
}, # end of layer update predicate
|
||||
'z-index': 4,
|
||||
}, # end of altitude profile layer
|
||||
|
||||
## add other layers here, layer names must match the registered names as used in *.layer files for now
|
||||
## this will all change once we're using Philosopher's MapStructure framework
|
||||
|
||||
], # end of vector with configured layers
|
||||
|
||||
# This is where SVG elements are configured by providing "behavior" hashes, i.e. for animations
|
||||
|
||||
# to animate each SVG symbol, specify behavior via callbacks (predicate, and true/false implementation)
|
||||
# SVG identifier, callback etc
|
||||
# TODO: update_on([]), update_mode (update() vs. timers/listeners)
|
||||
# TODO: support putting symbols on specific layers
|
||||
features: [
|
||||
{
|
||||
# TODO: taOnly doesn't need to use getprop polling in update(), use a listener instead!
|
||||
id: 'taOnly', # the SVG ID
|
||||
impl: { # implementation hash
|
||||
init: func(nd, symbol), # for updateCenter stuff, called during initialization in the ctor
|
||||
predicate: func(nd) getprop("instrumentation/tcas/inputs/mode") == 2, # the condition
|
||||
is_true: func(nd) nd.symbols.taOnly.show(), # if true, run this
|
||||
is_false: func(nd) nd.symbols.taOnly.hide(), # if false, run this
|
||||
}, # end of taOnly behavior/callbacks
|
||||
}, # end of taOnly
|
||||
{
|
||||
id: 'tas',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.aircraft_source.get_spd() > 100,
|
||||
is_true: func(nd) {
|
||||
nd.symbols.tas.setText(sprintf("%3.0f",getprop("/velocities/airspeed-kt") ));
|
||||
nd.symbols.tas.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.tas.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'tasLbl',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.aircraft_source.get_spd() > 100,
|
||||
is_true: func(nd) nd.symbols.tasLbl.show(),
|
||||
is_false: func(nd) nd.symbols.tasLbl.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ilsFreq',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.ilsFreq.show();
|
||||
if(getprop("instrumentation/nav/in-range"))
|
||||
nd.symbols.ilsFreq.setText(getprop("instrumentation/nav/nav-id"));
|
||||
else
|
||||
nd.symbols.ilsFreq.setText(getprop("instrumentation/nav/frequencies/selected-mhz-fmt"));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.ilsFreq.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ilsLbl',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.ilsLbl.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.ilsLbl.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'wpActiveId',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) getprop("/autopilot/route-manager/wp/id") != nil and getprop("autopilot/route-manager/active")
|
||||
and nd.in_mode('toggle_display_mode', ['MAP','PLAN']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.wpActiveId.setText(getprop("/autopilot/route-manager/wp/id"));
|
||||
nd.symbols.wpActiveId.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.wpActiveId.hide(),
|
||||
}, # of wpActiveId.impl
|
||||
}, # of wpActiveId
|
||||
{
|
||||
id: 'wpActiveDist',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) getprop("/autopilot/route-manager/wp/dist") != nil and getprop("autopilot/route-manager/active")
|
||||
and nd.in_mode('toggle_display_mode', ['MAP','PLAN']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.wpActiveDist.setText(sprintf("%3.01f",getprop("/autopilot/route-manager/wp/dist")));
|
||||
nd.symbols.wpActiveDist.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.wpActiveDist.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'wpActiveDistLbl',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) getprop("/autopilot/route-manager/wp/dist") != nil and getprop("autopilot/route-manager/active")
|
||||
and nd.in_mode('toggle_display_mode', ['MAP','PLAN']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.wpActiveDistLbl.show();
|
||||
if(getprop("/autopilot/route-manager/wp/dist") > 1000)
|
||||
nd.symbols.wpActiveDistLbl.setText(" NM");
|
||||
},
|
||||
is_false: func(nd) nd.symbols.wpActiveDistLbl.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'eta',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) getprop("autopilot/route-manager/wp/eta") != nil and getprop("autopilot/route-manager/active")
|
||||
and nd.in_mode('toggle_display_mode', ['MAP','PLAN']),
|
||||
is_true: func(nd) {
|
||||
var etaSec = getprop("/sim/time/utc/day-seconds")+getprop("autopilot/route-manager/wp/eta-seconds");
|
||||
var h = math.floor(etaSec/3600);
|
||||
etaSec=etaSec-3600*h;
|
||||
var m = math.floor(etaSec/60);
|
||||
etaSec=etaSec-60*m;
|
||||
var s = etaSec/10;
|
||||
if (h>24) h=h-24;
|
||||
nd.symbols.eta.setText(sprintf("%02.0f%02.0f.%01.0fz",h,m,s));
|
||||
nd.symbols.eta.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.eta.hide(),
|
||||
}, # of eta.impl
|
||||
}, # of eta
|
||||
{
|
||||
id: 'gsGroup',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']),
|
||||
is_true: func(nd) {
|
||||
if(nd.get_switch('toggle_centered'))
|
||||
nd.symbols.gsGroup.setTranslation(0,0);
|
||||
else
|
||||
nd.symbols.gsGroup.setTranslation(0,150);
|
||||
nd.symbols.gsGroup.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.gsGroup.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'hdg',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','MAP','VOR']),
|
||||
is_true: func(nd) {
|
||||
var hdgText = "";
|
||||
if((nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_display_type') == "CRT")
|
||||
or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD"))
|
||||
{
|
||||
if(nd.get_switch('toggle_true_north'))
|
||||
hdgText = nd.aircraft_source.get_trk_tru();
|
||||
else
|
||||
hdgText = nd.aircraft_source.get_trk_mag();
|
||||
} else {
|
||||
if(nd.get_switch('toggle_true_north'))
|
||||
hdgText = nd.aircraft_source.get_hdg_tru();
|
||||
else
|
||||
hdgText = nd.aircraft_source.get_hdg_mag();
|
||||
}
|
||||
if(hdgText < 0.5) hdgText = 360 + hdgText;
|
||||
elsif(hdgText >= 360.5) hdgText = hdgText - 360;
|
||||
nd.symbols.hdg.setText(sprintf("%03.0f", hdgText));
|
||||
},
|
||||
is_false: NOTHING,
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'hdgGroup',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','MAP','VOR']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.hdgGroup.show();
|
||||
if(nd.get_switch('toggle_centered'))
|
||||
nd.symbols.hdgGroup.setTranslation(0,100);
|
||||
else
|
||||
nd.symbols.hdgGroup.setTranslation(0,0);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.hdgGroup.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'gs',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
common: func(nd) nd.symbols.gs.setText(sprintf("%3.0f",nd.aircraft_source.get_gnd_spd() )),
|
||||
predicate: func(nd) nd.aircraft_source.get_gnd_spd() >= 30,
|
||||
is_true: func(nd) {
|
||||
nd.symbols.gs.setFontSize(36);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.gs.setFontSize(52),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'rangeArcs',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) !nd.get_switch('toggle_centered') and nd.get_switch('toggle_rangearc'),
|
||||
is_true: func(nd) nd.symbols.rangeArcs.show(),
|
||||
is_false: func(nd) nd.symbols.rangeArcs.hide(),
|
||||
}, # of rangeArcs.impl
|
||||
}, # of rangeArcs
|
||||
{
|
||||
id:'rangePln1',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN",
|
||||
is_true: func(nd) {
|
||||
nd.symbols.rangePln1.show();
|
||||
nd.symbols.rangePln1.setText(sprintf("%3.0f",nd.rangeNm()));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.rangePln1.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'rangePln2',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN",
|
||||
is_true: func(nd) {
|
||||
nd.symbols.rangePln2.show();
|
||||
nd.symbols.rangePln2.setText(sprintf("%3.0f",nd.rangeNm()/2));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.rangePln2.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'rangePln3',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN",
|
||||
is_true: func(nd) {
|
||||
nd.symbols.rangePln3.show();
|
||||
nd.symbols.rangePln3.setText(sprintf("%3.0f",nd.rangeNm()/2));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.rangePln3.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'rangePln4',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN",
|
||||
is_true: func(nd) {
|
||||
nd.symbols.rangePln4.show();
|
||||
nd.symbols.rangePln4.setText(sprintf("%3.0f",nd.rangeNm()));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.rangePln4.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'crsLbl',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']),
|
||||
is_true: func(nd) nd.symbols.crsLbl.show(),
|
||||
is_false: func(nd) nd.symbols.crsLbl.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'crs',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.crs.show();
|
||||
if(getprop("instrumentation/nav/radials/selected-deg") != nil)
|
||||
nd.symbols.crs.setText(sprintf("%03.0f",getprop("instrumentation/nav/radials/selected-deg")));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.crs.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'dmeLbl',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']),
|
||||
is_true: func(nd) nd.symbols.dmeLbl.show(),
|
||||
is_false: func(nd) nd.symbols.dmeLbl.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'dme',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.dme.show();
|
||||
if(getprop("instrumentation/dme/in-range"))
|
||||
nd.symbols.dme.setText(sprintf("%3.1f",getprop("instrumentation/dme/indicated-distance-nm")));
|
||||
},
|
||||
is_false: func(nd) nd.symbols.dme.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'trkInd2',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['MAP','APP','VOR']) and nd.get_switch('toggle_centered')),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.trkInd2.show();
|
||||
},
|
||||
is_false: func(nd) nd.symbols.trkInd2.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'vorCrsPtr',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered')),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.vorCrsPtr.show();
|
||||
if((nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_display_type') == "CRT")
|
||||
or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD"))
|
||||
nd.symbols.vorCrsPtr.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_trk_mag())*D2R);
|
||||
else
|
||||
nd.symbols.vorCrsPtr.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_hdg_mag())*D2R);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.vorCrsPtr.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'vorCrsPtr2',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_centered')),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.vorCrsPtr2.show();
|
||||
if((nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_display_type') == "CRT")
|
||||
or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD"))
|
||||
nd.symbols.vorCrsPtr2.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_trk_mag())*D2R);
|
||||
else
|
||||
nd.symbols.vorCrsPtr2.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_hdg_mag())*D2R);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.vorCrsPtr2.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'gsDiamond',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']) and getprop("instrumentation/nav/gs-in-range"),
|
||||
is_true: func(nd) {
|
||||
var gs_deflection = getprop("instrumentation/nav/gs-needle-deflection-norm");
|
||||
if(gs_deflection != nil)
|
||||
nd.symbols.gsDiamond.setTranslation(gs_deflection*150,0);
|
||||
if(abs(gs_deflection) < 0.99)
|
||||
nd.symbols.gsDiamond.setColorFill(1,0,1,1);
|
||||
else
|
||||
nd.symbols.gsDiamond.setColorFill(0,0,0,1);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.gsGroup.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'locPtr',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered') and getprop("instrumentation/nav/in-range")),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.locPtr.show();
|
||||
var deflection = getprop("instrumentation/nav/heading-needle-deflection-norm");
|
||||
nd.symbols.locPtr.setTranslation(deflection*150,0);
|
||||
if(abs(deflection) < 0.99)
|
||||
nd.symbols.locPtr.setColorFill(1,0,1,1);
|
||||
else
|
||||
nd.symbols.locPtr.setColorFill(0,0,0,1);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.locPtr.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'locPtr2',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_centered') and getprop("instrumentation/nav/in-range")),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.locPtr2.show();
|
||||
var deflection = getprop("instrumentation/nav/heading-needle-deflection-norm");
|
||||
nd.symbols.locPtr2.setTranslation(deflection*150,0);
|
||||
if(abs(deflection) < 0.99)
|
||||
nd.symbols.locPtr2.setColorFill(1,0,1,1);
|
||||
else
|
||||
nd.symbols.locPtr2.setColorFill(0,0,0,1);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.locPtr2.hide(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'wind',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: ALWAYS,
|
||||
is_true: func(nd) {
|
||||
var windDir = getprop("environment/wind-from-heading-deg");
|
||||
if(!nd.get_switch('toggle_true_north'))
|
||||
windDir = windDir - getprop("environment/magnetic-variation-deg");
|
||||
if(windDir < 0.5) windDir = 360 + windDir;
|
||||
elsif(windDir >= 360.5) windDir = windDir - 360;
|
||||
nd.symbols.wind.setText(sprintf("%03.0f / %02.0f",windDir,getprop("environment/wind-speed-kt")));
|
||||
},
|
||||
is_false: NOTHING,
|
||||
},
|
||||
},
|
||||
{
|
||||
id:'windArrow',
|
||||
impl: {
|
||||
init: func(nd,symbol),
|
||||
predicate: func(nd) (!(nd.in_mode('toggle_display_mode', ['PLAN']) and (nd.get_switch('toggle_display_type') == "LCD")) and nd.aircraft_source.get_spd() > 100),
|
||||
is_true: func(nd) {
|
||||
nd.symbols.windArrow.show();
|
||||
var windArrowRot = getprop("environment/wind-from-heading-deg");
|
||||
if((nd.in_mode('toggle_display_mode', ['MAP','PLAN']) and nd.get_switch('toggle_display_type') == "CRT")
|
||||
or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD"))
|
||||
windArrowRot = windArrowRot - nd.aircraft_source.get_trk_mag();
|
||||
else
|
||||
windArrowRot = windArrowRot - nd.aircraft_source.get_hdg_mag();
|
||||
nd.symbols.windArrow.setRotation(windArrowRot*D2R);
|
||||
},
|
||||
is_false: func(nd) nd.symbols.windArrow.hide(),
|
||||
},
|
||||
},
|
||||
|
||||
], # end of vector with features
|
||||
|
||||
|
||||
}, # end of Boeing style
|
||||
#####
|
||||
##
|
||||
## add support for other aircraft/ND types and styles here (Airbus etc)
|
||||
## or move to other files.
|
||||
##
|
||||
## see: http://wiki.flightgear.org/NavDisplay#Custom_ND_Styles
|
||||
## and: http://wiki.flightgear.org/NavDisplay#Adding_new_features
|
||||
|
||||
}; # end of NDStyles
|
||||
|
|
@ -11,7 +11,7 @@ var draw_storm = func (group, storm, controller=nil, lod=0) {
|
|||
var storm_grp = group.createChild("group","storm"); # one group for each storm
|
||||
|
||||
storm_grp.createChild("image")
|
||||
.setFile("Nasal/canvas/map/storm.png")
|
||||
.setFile("Nasal/canvas/map/Images/storm.png")
|
||||
.setSize(128*radiusNm,128*radiusNm)
|
||||
.setTranslation(-64*radiusNm,-64*radiusNm)
|
||||
.setCenter(0,0);
|
||||
|
|
|
@ -8,18 +8,20 @@ StormModel.init = func {
|
|||
foreach (var n; props.globals.getNode("/instrumentation/wxradar",1).getChildren("storm")) {
|
||||
# Model 3 degree radar beam
|
||||
var stormLat = n.getNode("latitude-deg").getValue();
|
||||
stormLon = n.getNode("longitude-deg").getValue();
|
||||
acLat = getprop("/position/latitude-deg");
|
||||
acLon = getprop("/position/longitude-deg");
|
||||
stormGeo = geo.Coord.new();
|
||||
acGeo = geo.Coord.new();
|
||||
var stormLon = n.getNode("longitude-deg").getValue();
|
||||
|
||||
# FIXME: once ported to MapStructure, these should use the encapsulated "aircraft source"/driver stuff
|
||||
var acLat = getprop("/position/latitude-deg");
|
||||
var acLon = getprop("/position/longitude-deg");
|
||||
var stormGeo = geo.Coord.new();
|
||||
var acGeo = geo.Coord.new();
|
||||
|
||||
stormGeo.set_latlon(stormLat, stormLon);
|
||||
acGeo.set_latlon(acLat, acLon);
|
||||
|
||||
var directDistance = acGeo.direct_distance_to(stormGeo);
|
||||
beamH = 0.1719 * directDistance; # M2FT * tan(3deg)
|
||||
beamBase = getprop("position/altitude-ft") - beamH;
|
||||
var beamH = 0.1719 * directDistance; # M2FT * tan(3deg)
|
||||
var beamBase = getprop("position/altitude-ft") - beamH;
|
||||
|
||||
if (n.getNode("top-altitude-ft").getValue() > beamBase) {
|
||||
me.push( { lat: stormLat, lon : stormLon, radiusNm : n.getNode("radius-nm").getValue() } );
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
<!-- Equipment menu -->
|
||||
<equipment>Equipment</equipment>
|
||||
<map>Map</map>
|
||||
<map-canvas>Map (Canvas)</map-canvas>
|
||||
<stopwatch>Stopwatch</stopwatch>
|
||||
<fuel-and-payload>Fuel and Payload</fuel-and-payload>
|
||||
<radio>Radio Settings</radio>
|
||||
|
|
|
@ -33,6 +33,13 @@
|
|||
</binding>
|
||||
</checkbox>
|
||||
</group>
|
||||
<button>
|
||||
<legend>New Canvas Map</legend>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>canvas.MapStructure_selfTest();</script>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<button>
|
||||
<legend>Close</legend>
|
||||
|
|
498
gui/dialogs/map-canvas.xml
Normal file
|
@ -0,0 +1,498 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList>
|
||||
<name>map-canvas</name>
|
||||
<layout>vbox</layout>
|
||||
<resizable>true</resizable>
|
||||
|
||||
<color>
|
||||
<red type="float">0.41</red>
|
||||
<green type="float">0.4</green>
|
||||
<blue type="float">0.42</blue>
|
||||
<alpha type="float">1.0</alpha>
|
||||
</color>
|
||||
|
||||
<nasal>
|
||||
<open><![CDATA[
|
||||
var self = cmdarg();
|
||||
var listeners = [];
|
||||
var setTransparency = func(updateDialog){
|
||||
var alpha = (getprop("/sim/gui/dialogs/map-canvas/transparent") or 0);
|
||||
self.getNode("color/alpha").setValue(1-alpha*0.3);
|
||||
self.getNode("color/red").setValue(0.41-alpha*0.2);
|
||||
self.getNode("color/green").setValue(0.4-alpha*0.2);
|
||||
self.getNode("color/blue").setValue(0.42-alpha*0.2);
|
||||
var n = props.Node.new({ "dialog-name": "map-canvas" });
|
||||
if (updateDialog)
|
||||
{
|
||||
fgcommand("dialog-close", n);
|
||||
fgcommand("dialog-show", n);
|
||||
}
|
||||
}
|
||||
setTransparency(0);
|
||||
]]></open>
|
||||
|
||||
<close><![CDATA[
|
||||
TestMap.del();
|
||||
foreach (var l; listeners)
|
||||
removelistener(l);
|
||||
setsize(listeners, 0);
|
||||
]]></close>
|
||||
</nasal>
|
||||
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
<empty><stretch>1</stretch></empty>
|
||||
|
||||
<text>
|
||||
<label>Map (Canvas)</label>
|
||||
</text>
|
||||
|
||||
<empty><stretch>1</stretch></empty>
|
||||
|
||||
<button>
|
||||
<pref-width>16</pref-width>
|
||||
<pref-height>16</pref-height>
|
||||
<legend></legend>
|
||||
<keynum>27</keynum>
|
||||
<border>2</border>
|
||||
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
</group>
|
||||
<hrule/>
|
||||
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
<stretch>true</stretch>
|
||||
|
||||
<!-- sidebar -->
|
||||
<group>
|
||||
<layout>vbox</layout>
|
||||
<text>
|
||||
<label>Display:</label>
|
||||
</text>
|
||||
|
||||
<checkbox>
|
||||
<label>Airports</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/draw-APT</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<checkbox>
|
||||
<label>Fixes</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/draw-FIX</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<checkbox>
|
||||
<label>VORs</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/draw-VOR</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<checkbox>
|
||||
<label>DMEs</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/draw-DME</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<checkbox>
|
||||
<label>NDBs</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/draw-NDB</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<!--
|
||||
<button>
|
||||
<legend>Airways</legend>
|
||||
<pref-width>100</pref-width>
|
||||
</button>
|
||||
-->
|
||||
|
||||
<checkbox>
|
||||
<label>Traffic</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/draw-TFC</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<!--
|
||||
<button>
|
||||
<legend>Obstacles</legend>
|
||||
<pref-width>100</pref-width>
|
||||
</button>
|
||||
-->
|
||||
|
||||
<checkbox>
|
||||
<label>Data</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/draw-data</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<checkbox>
|
||||
<label>Flight History</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/draw-FLT</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<checkbox>
|
||||
<!-- layer only supported if LW/AW is active -->
|
||||
<enable>
|
||||
<property>/sim/gui/dialogs/metar/mode/local-weather</property>
|
||||
<value>1</value>
|
||||
</enable>
|
||||
|
||||
<label>Weather</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/draw-WXR</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<!-- layer only supported if tutorial system is active and targets specified-->
|
||||
<!--
|
||||
<checkbox>
|
||||
<enable>
|
||||
<property>/sim/gui/dialogs/metar/mode/local-weather</property>
|
||||
<value>1</value>
|
||||
</enable>
|
||||
<label>Tutorial Goals</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/draw-TUT</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
-->
|
||||
|
||||
<empty><stretch>true</stretch></empty>
|
||||
|
||||
<checkbox>
|
||||
<label>Magnetic Hdgs</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/magnetic-headings</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<checkbox>
|
||||
<label>Center on Acft</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/centre-on-aircraft</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<checkbox>
|
||||
<label>Aircraft Hdg Up</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/aircraft-heading-up</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<checkbox>
|
||||
<label>Transparent</label>
|
||||
<pref-width>100</pref-width>
|
||||
<property>/sim/gui/dialogs/map-canvas/transparent</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>setTransparency(1);</script>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<empty><stretch>true</stretch></empty>
|
||||
|
||||
<button>
|
||||
<name>close</name>
|
||||
<legend>Close</legend>
|
||||
<pref-width>100</pref-width>
|
||||
<default>true</default>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
</group>
|
||||
|
||||
<vrule/>
|
||||
|
||||
<group>
|
||||
<layout>vbox</layout>
|
||||
<stretch>true</stretch>
|
||||
|
||||
<group>
|
||||
<canvas>
|
||||
<name>canvas-map</name>
|
||||
<valign>fill</valign>
|
||||
<halign>fill</halign>
|
||||
<stretch>true</stretch>
|
||||
<pref-width>600</pref-width>
|
||||
<pref-height>400</pref-height>
|
||||
<nasal><load><![CDATA[
|
||||
var myCanvas = canvas.get( cmdarg() );
|
||||
myCanvas.setColorBackground(0,0,0,0.5); # transparent
|
||||
var TestMap = myCanvas.createGroup().createChild("map");
|
||||
|
||||
# Initialize the controller:
|
||||
var ctrl_ns = canvas.Map.Controller.get("Aircraft position");
|
||||
var source = ctrl_ns.SOURCES["map-dialog"];
|
||||
if (source == nil) {
|
||||
# TODO: amend
|
||||
var source = ctrl_ns.SOURCES["map-dialog"] = {
|
||||
getPosition: func subvec(geo.aircraft_position().latlon(), 0, 2),
|
||||
getAltitude: func getprop('/position/altitude-ft'),
|
||||
getHeading: func {
|
||||
if (me.aircraft_heading)
|
||||
getprop('/orientation/heading-deg')
|
||||
else 0
|
||||
},
|
||||
aircraft_heading: 1,
|
||||
};
|
||||
}
|
||||
setlistener("/sim/gui/dialogs/map-canvas/aircraft-heading-up", func(n) {
|
||||
source.aircraft_heading = n.getBoolValue();
|
||||
}, 1);
|
||||
# Make it move with our aircraft:
|
||||
TestMap.setController("Aircraft position", "map-dialog"); # from aircraftpos.controller
|
||||
|
||||
# Initialize a range:
|
||||
TestMap.setRange(20);
|
||||
var range_step = math.log10(TestMap.getRange());
|
||||
# TODO: check if this is valid, IIRC DOM manipulation was fragile when done inside canvas/Nasal region (?)
|
||||
gui.findElementByName(self, "zoomdisplay").setValue("property", TestMap._node.getNode("range").getPath());
|
||||
|
||||
# Center the map's origin: FIXME: move to api.nas, i.e. allow maps to have a size/view that differs from the actual canvas ??
|
||||
TestMap.setTranslation(
|
||||
myCanvas.get("view[0]")/2,
|
||||
myCanvas.get("view[1]")/2
|
||||
);
|
||||
|
||||
##
|
||||
# Styling: This is a bit crude at the moment, i.e. no dedicated APIs yet - but it's
|
||||
# just there to prototype things for now
|
||||
var Styles = {};
|
||||
Styles.get = func(type) return Styles[type];
|
||||
var Options = {};
|
||||
Options.get = func(type) return Options[type];
|
||||
|
||||
## set up a few keys supported by the DME.symbol file to customize appearance:
|
||||
Styles.DME = {};
|
||||
Styles.DME.debug = 1; # HACK for benchmarking/debugging purposes
|
||||
Styles.DME.animation_test = 0; # for prototyping animated symbols
|
||||
|
||||
Styles.DME.scale_factor = 0.4; # 40% (applied to whole group)
|
||||
Styles.DME.line_width = 3.0;
|
||||
Styles.DME.color_tuned = [0,1,0]; #rgb
|
||||
Styles.DME.color_default = [1,1,0]; #rgb
|
||||
|
||||
Styles.APT = {};
|
||||
Styles.APT.scale_factor = 0.4; # 40% (applied to whole group)
|
||||
Styles.APT.line_width = 3.0;
|
||||
Styles.APT.color_default = [0,0.6,0.85]; #rgb
|
||||
Styles.APT.label_font_color = Styles.APT.color_default;
|
||||
Styles.APT.label_font_size=28;
|
||||
|
||||
Styles.TFC = {};
|
||||
Styles.TFC.scale_factor = 0.4; # 40% (applied to whole group)
|
||||
|
||||
Styles.WPT = {};
|
||||
Styles.WPT.scale_factor = 0.5; # 50% (applied to whole group)
|
||||
|
||||
Styles.RTE = {};
|
||||
Styles.RTE.line_width = 2;
|
||||
|
||||
Styles.FLT = {};
|
||||
Styles.FLT.line_width = 3;
|
||||
|
||||
Styles.FIX = {};
|
||||
Styles.FIX.color = [1,0,0];
|
||||
Styles.FIX.scale_factor = 0.4; # 40%
|
||||
|
||||
Styles.VOR = {};
|
||||
Styles.VOR.range_line_width = 2;
|
||||
Styles.VOR.radial_line_width = 1;
|
||||
Styles.VOR.scale_factor = 0.6; # 60%
|
||||
|
||||
Options.FLT = {};
|
||||
var make_update_wrapper = func(name) {
|
||||
if (!contains(Options, name)) Options[name] = {};
|
||||
Options[name].update_wrapper = func(layer, fn) {
|
||||
var t = systime();
|
||||
fn();
|
||||
t = systime() - t;
|
||||
printlog(canvas._MP_dbg_lvl, name~" took "~(t*1000)~"ms")
|
||||
}
|
||||
}
|
||||
|
||||
var ToggleLayerVisible = func(name) {
|
||||
(var l = TestMap.getLayer(name)).setVisible(l.getVisible());
|
||||
};
|
||||
var SetLayerVisible = func(name,n=1) {
|
||||
TestMap.getLayer(name).setVisible(n);
|
||||
};
|
||||
|
||||
Styles.APS = {};
|
||||
Styles.APS.scale_factor = 0.25;
|
||||
|
||||
# TODO: introduce some meta NAV layer that handles both VORs and NDBs, can we instantiate those layers directly ?
|
||||
var r = func(name,vis=1,zindex=nil) return caller(0)[0];
|
||||
# TODO: we'll need some z-indexing here, right now it's just random
|
||||
foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR',0),r('APS'), ] ) {
|
||||
if (1 and type.name != 'APS' and type.name != 'FLT') make_update_wrapper(type.name);
|
||||
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,
|
||||
visible: type.vis, priority: type.zindex,
|
||||
style: Styles.get(type.name),
|
||||
options: Options.get(type.name) );
|
||||
(func {
|
||||
# Notify MapStructure about layer visibility changes:
|
||||
var name = type.name;
|
||||
props.globals.initNode("/sim/gui/dialogs/map-canvas/draw-"~name, type.vis, "BOOL");
|
||||
append(listeners,
|
||||
setlistener("/sim/gui/dialogs/map-canvas/draw-"~name,
|
||||
func(n) SetLayerVisible(name,n.getValue()))
|
||||
);
|
||||
})();
|
||||
}
|
||||
]]></load></nasal>
|
||||
</canvas>
|
||||
<layout>hbox</layout>
|
||||
</group>
|
||||
|
||||
<hrule/>
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
|
||||
<button>
|
||||
<name>zoomout</name>
|
||||
<legend>out</legend>
|
||||
<pref-width>52</pref-width>
|
||||
<pref-height>22</pref-height>
|
||||
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>
|
||||
var range = TestMap.getRange();
|
||||
if (range < 40)
|
||||
TestMap.setRange(range*range_step);
|
||||
</script>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<text>
|
||||
<name>zoomdisplay</name>
|
||||
<label>MMM</label>
|
||||
<format>Zoom %0.1f NM</format>
|
||||
<property>/gui/radar-mapstructure/zoom</property>
|
||||
<live>true</live>
|
||||
</text>
|
||||
|
||||
<button>
|
||||
<name>zoomin</name>
|
||||
<legend>in</legend>
|
||||
<pref-width>52</pref-width>
|
||||
<pref-height>22</pref-height>
|
||||
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>
|
||||
var range = TestMap.getRange();
|
||||
if (range > 1)
|
||||
TestMap.setRange(range/range_step);
|
||||
</script>
|
||||
</binding>
|
||||
</button>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
</PropertyList>
|
||||
|
|
@ -322,13 +322,23 @@
|
|||
|
||||
<item>
|
||||
<name>map</name>
|
||||
<key>Ctrl-M</key>
|
||||
<key>Ctrl-M</key>
|
||||
<binding>
|
||||
<command>dialog-show</command>
|
||||
<dialog-name>map</dialog-name>
|
||||
</binding>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<name>map-canvas</name>
|
||||
<!-- <key>Ctrl-M</key> -->
|
||||
<binding>
|
||||
<command>dialog-show</command>
|
||||
<dialog-name>map-canvas</dialog-name>
|
||||
</binding>
|
||||
</item>
|
||||
|
||||
|
||||
<item>
|
||||
<name>stopwatch</name>
|
||||
<binding>
|
||||
|
|