1
0
Fork 0

MapStructure work & (partial) integration

In time for 3.0. The API is still not fully complete, and not fully
cleaned up, but this is good enough for this release cycle (and it
should offer benefit longer term, if not now -- hopefully performance as
well).

Many thanks to Hooray as well, who has helped prepare things while I
could not, and often suggested ideas.
This commit is contained in:
Philosopher 2014-01-09 21:04:36 -06:00
parent 9af485ad93
commit 7ca8482b07
21 changed files with 1086 additions and 558 deletions

View file

@ -1,3 +1,123 @@
var dump_obj = func(m) {
var h = {};
foreach (var k; keys(m))
if (k != "parents")
h[k] = m[k];
debug.dump(h);
};
##
# must be either of:
# 1) draw* callback, 2) SVG filename, 3) Drawable class (with styling/LOD support)
var SymbolDrawable = {
new: func() {
},
};
## wrapper for each element
## i.e. keeps the canvas and texture map coordinates
var CachedElement = {
new: func(canvas_path, name, source, offset) {
var m = {parents:[CachedElement] };
m.canvas_src = canvas_path;
m.name = name;
m.source = source;
m.offset = offset;
return m;
}, # new()
render: func(group) {
# create a raster image child in the render target/group
return
group.createChild("image", me.name)
.setFile( me.canvas_src )
# TODO: fix .setSourceRect() to accept a single vector for coordinates ...
.setSourceRect(left:me.source[0],top:me.source[1],right:me.source[2],bottom:me.source[3] , normalized:0)
.setTranslation(me.offset); # FIXME: make sure this stays like this and isn't overridden
}, # render()
}; # of CachedElement
var SymbolCache = {
# We can draw symbols either with left/top, centered,
# or right/bottom alignment. Specify two in a vector
# to mix and match, e.g. left/centered would be
# [SymbolCache.DRAW_LEFT_TOP,SymbolCache.DRAW_CENTERED]
DRAW_LEFT_TOP: 0.0,
DRAW_CENTERED: 0.5,
DRAW_RIGHT_BOTTOM: 1.0,
new: func(dim...) {
var m = { parents:[SymbolCache] };
# to keep track of the next free caching spot (in px)
m.next_free = [0, 0];
# to store each type of symbol
m.dict = {};
if (size(dim) == 1 and typeof(dim[0]) == 'vector')
dim = dim[0];
# Two sizes: canvas and symbol
if (size(dim) == 2) {
var canvas_x = var canvas_y = dim[0];
var image_x = var image_y = dim[1];
# Two widths (canvas and symbol) and then height/width ratio
} else if (size(dim) == 3) {
var (canvas_x,image_x,ratio) = dim;
var canvas_y = canvas_x * ratio;
var image_y = image_x * ratio;
# Explicit canvas and symbol widths/heights
} else if (size(dim) == 4) {
var (canvas_x,canvas_y,image_x,image_y) = dim;
}
m.canvas_sz = [canvas_x, canvas_y];
m.image_sz = [image_x, image_y];
# allocate a canvas
m.canvas_texture = canvas.new( {
"name": "SymbolCache"~canvas_x~'x'~canvas_y,
"size": m.canvas_sz,
"view": m.canvas_sz,
"mipmapping": 1
});
# add a placement
m.canvas_texture.addPlacement( {"type": "ref"} );
return m;
},
add: func(name, callback, draw_mode=0) {
if (typeof(draw_mode) == 'scalar')
var draw_mode0 = var draw_mode1 = draw_mode;
else var (draw_mode0,draw_mode1) = draw_mode;
# get canvas texture that we use as cache
# get next free spot in texture (column/row)
# run the draw callback and render into a group
var gr = me.canvas_texture.createGroup();
gr.setTranslation( me.next_free[0] + me.image_sz[0]*draw_mode0,
me.next_free[1] + me.image_sz[1]*draw_mode1);
#settimer(func debug.dump ( gr.getTransformedBounds() ), 0); # XXX: these are only updated when rendered
#debug.dump ( gr.getTransformedBounds() );
gr.update(); # apparently this doesn't result in sane output from .getTransformedBounds() either
#debug.dump ( gr.getTransformedBounds() );
# draw the symbol
callback(gr);
# get the bounding box, i.e. coordinates for texture map, or use the .setTranslation() params
var coords = me.next_free~me.next_free;
foreach (var i; [0,1])
coords[i+2] += me.image_sz[i];
# get the offset we used to position correctly in the bounds of the canvas
var offset = [me.image_sz[0]*draw_mode0, me.image_sz[1]*draw_mode1];
# store texture map coordinates in lookup map using the name as identifier
me.dict[name] = CachedElement.new(me.canvas_texture.getPath(), name, coords, offset );
# update next free position in cache (column/row)
me.next_free[0] += me.image_sz[0];
if (me.next_free[0] >= me.canvas_sz[0])
{ me.next_free[0] = 0; me.next_free[1] += me.image_sz[1] }
if (me.next_free[1] >= me.canvas_sz[1])
die("SymbolCache: ran out of space after adding '"~name~"'");
}, # add()
get: func(name) {
if(!contains(me.dict,name)) die("No SymbolCache entry for key:"~ name);
return me.dict[name];
}, # get()
};
var Symbol = { var Symbol = {
# Static/singleton: # Static/singleton:
registry: {}, registry: {},
@ -29,7 +149,7 @@ Symbol.Controller = {
# Static/singleton: # Static/singleton:
registry: {}, registry: {},
add: func(type, class) add: func(type, class)
registry[type] = class, me.registry[type] = class,
get: func(type) get: func(type)
if ((var class = me.registry[type]) == nil) if ((var class = me.registry[type]) == nil)
die("unknown type '"~type~"'"); die("unknown type '"~type~"'");
@ -58,13 +178,17 @@ var getpos_fromghost = func(positioned_g)
# (geo.Coord and positioned ghost currently) # (geo.Coord and positioned ghost currently)
Symbol.Controller.getpos = func(obj) { Symbol.Controller.getpos = func(obj) {
if (typeof(obj) == 'ghost') if (typeof(obj) == 'ghost')
if (ghosttype(obj) == 'positioned' or ghosttype(obj) == 'Navaid') if (ghosttype(obj) == 'positioned' or ghosttype(obj) == 'Navaid' or ghosttype(obj)=='Fix' or ghosttype(obj)=='flightplan-leg')
return getpos_fromghost(obj); return getpos_fromghost(obj);
else else
die("bad ghost of type '"~ghosttype(obj)~"'"); die("bad ghost of type '"~ghosttype(obj)~"'");
if (typeof(obj) == 'hash') if (typeof(obj) == 'hash')
if (isa(obj, geo.Coord)) if (isa(obj, geo.Coord))
return obj.latlon(); return obj.latlon();
if (contains(obj,'lat') and contains(obj,'lon'))
return [obj.lat, obj.lon];
debug.dump(obj);
die("no suitable getpos() found! Of type: "~typeof(obj)); die("no suitable getpos() found! Of type: "~typeof(obj));
}; };
@ -82,38 +206,17 @@ var DotSym = {
element_id: nil, element_id: nil,
# Static/singleton: # Static/singleton:
makeinstance: func(name, hash) { makeinstance: func(name, hash) {
assert_ms(hash, if (!isa(hash, DotSym))
"element_type", # type of Canvas element die("OOP error");
#"element_id", # optional Canvas id #assert_ms(hash,
#"init", # initialize routine # "element_type", # type of Canvas element
"draw", # init/update routine # #"element_id", # optional Canvas id
#getpos", # get position from model in [x_units,y_units] (optional) # #"init", # initialize routine
); # "draw", # init/update routine
hash.parents = [DotSym]; # #getpos", # get position from model in [x_units,y_units] (optional)
#);
return Symbol.add(name, hash); return Symbol.add(name, hash);
}, },
readinstance: func(file, name=nil) {
#print(file);
if (name == nil)
var name = split("/", file)[-1];
if (substr(name, size(name)-4) == ".draw")
name = substr(name, 0, size(name)-5);
var code = io.readfile(file);
var code = call(compile, [code], var err=[]);
if (size(err)) {
if (substr(err[0], 0, 12) == "Parse error:") { # hack around Nasal feature
var e = split(" at line ", err[0]);
if (size(e) == 2)
err[0] = string.join("", [e[0], "\n at ", file, ", line ", e[1], "\n "]);
}
for (var i = 1; (var c = caller(i)) != nil; i += 1)
err ~= subvec(c, 2, 2);
debug.printerror(err);
return;
}
call(code, nil, nil, var hash = { parents:[DotSym] });
me.makeinstance(name, hash);
},
# For the instances returned from makeinstance: # For the instances returned from makeinstance:
# @param group The Canvas group to add this to. # @param group The Canvas group to add this to.
# @param model A correct object (e.g. positioned ghost) as # @param model A correct object (e.g. positioned ghost) as
@ -132,6 +235,10 @@ var DotSym = {
), ),
}; };
if (m.controller != nil) { if (m.controller != nil) {
#print("Creating controller");
temp = m.controller.new(m.model,m);
if (temp != nil)
m.controller = temp;
#print("Initializing controller"); #print("Initializing controller");
m.controller.init(model); m.controller.init(model);
} }
@ -281,93 +388,117 @@ SymbolLayer.Controller = {
die("searchCmd() not implemented for this SymbolLayer.Controller type!"), die("searchCmd() not implemented for this SymbolLayer.Controller type!"),
}; # of SymbolLayer.Controller }; # of SymbolLayer.Controller
settimer(func { var AnimatedLayer = {
Map.Controller = {
# Static/singleton:
registry: {},
add: func(type, class)
me.registry[type] = class,
get: func(type)
if ((var class = me.registry[type]) == nil)
die("unknown type '"~type~"'");
else return class,
# Calls corresonding controller constructor
# @param map The #SymbolMap this controller is responsible for.
new: func(type, layer, arg...)
return call((var class = me.get(type)).new, [map]~arg, class),
}; };
####### LOAD FILES ####### var CompassLayer = {
#print("loading files"); };
(func {
var FG_ROOT = getprop("/sim/fg-root"); var AltitudeArcLayer = {
var load = func(file, name) { };
#print(file);
if (name == nil) load_MapStructure = func {
var name = split("/", file)[-1]; Map.Controller = {
if (substr(name, size(name)-4) == ".draw") # Static/singleton:
name = substr(name, 0, size(name)-5); registry: {},
#print("reading file"); add: func(type, class)
var code = io.readfile(file); me.registry[type] = class,
#print("compiling file"); get: func(type)
# This segfaults for some reason: if ((var class = me.registry[type]) == nil)
#var code = call(compile, [code], var err=[]); die("unknown type '"~type~"'");
var code = call(func compile(code, file), [code], var err=[]); else return class,
if (size(err)) { # Calls corresonding controller constructor
#print("handling error"); # @param map The #SymbolMap this controller is responsible for.
if (substr(err[0], 0, 12) == "Parse error:") { # hack around Nasal feature new: func(type, layer, arg...)
var e = split(" at line ", err[0]); return call((var class = me.get(type)).new, [map]~arg, class),
if (size(e) == 2) };
err[0] = string.join("", [e[0], "\n at ", file, ", line ", e[1], "\n "]);
####### LOAD FILES #######
#print("loading files");
(func {
var FG_ROOT = getprop("/sim/fg-root");
var load = func(file, name) {
#print(file);
if (name == nil)
var name = split("/", file)[-1];
if (substr(name, size(name)-4) == ".draw")
name = substr(name, 0, size(name)-5);
#print("reading file");
var code = io.readfile(file);
#print("compiling file");
# This segfaults for some reason:
#var code = call(compile, [code], var err=[]);
var code = call(func compile(code, file), [code], var err=[]);
if (size(err)) {
#print("handling error");
if (substr(err[0], 0, 12) == "Parse error:") { # hack around Nasal feature
var e = split(" at line ", err[0]);
if (size(e) == 2)
err[0] = string.join("", [e[0], "\n at ", file, ", line ", e[1], "\n "]);
}
for (var i = 1; (var c = caller(i)) != nil; i += 1)
err ~= subvec(c, 2, 2);
debug.printerror(err);
return;
} }
for (var i = 1; (var c = caller(i)) != nil; i += 1) #print("calling code");
err ~= subvec(c, 2, 2); call(code, nil, nil, var hash = {});
debug.printerror(err); #debug.dump(keys(hash));
return; return hash;
};
var load_deps = func(name) {
load(FG_ROOT~"/Nasal/canvas/map/"~name~".lcontroller", name);
load(FG_ROOT~"/Nasal/canvas/map/"~name~".symbol", name);
load(FG_ROOT~"/Nasal/canvas/map/"~name~".scontroller", name);
} }
#print("calling code");
call(code, nil, nil, var hash = {});
#debug.dump(keys(hash));
return hash;
};
load(FG_ROOT~"/Nasal/canvas/map/VOR.lcontroller", "VOR");
DotSym.readinstance(FG_ROOT~"/Nasal/canvas/map/VOR.symbol", "VOR");
load(FG_ROOT~"/Nasal/canvas/map/VOR.scontroller", "VOR");
load(FG_ROOT~"/Nasal/canvas/map/aircraftpos.controller", "VOR");
})();
#print("finished loading files");
####### TEST SYMBOL #######
if (0) foreach( var name; ['VOR','FIX','NDB','DME','WPT'] )
settimer(func { load_deps( name );
if (caller(0)[0] != globals.canvas) load(FG_ROOT~"/Nasal/canvas/map/aircraftpos.controller", name);
return call(caller(0)[1], arg, nil, globals.canvas);
print("Running MapStructure test code"); ###
var TestCanvas = canvas.new({ # set up a cache for 32x32 symbols
"name": "Map Test", var SymbolCache32x32 = SymbolCache.new(1024,32);
"size": [1024, 1024],
"view": [1024, 1024],
"mipmapping": 1
});
var dlg = canvas.Window.new([400, 400], "dialog");
dlg.setCanvas(TestCanvas);
var TestMap = TestCanvas.createGroup().createChild("map"); # we should not directly use a canvas here, but instead a LayeredMap.new()
TestMap.addLayer(factory: SymbolLayer, type_arg: "VOR"); # the ID should be also exposed in the property tree for each group (layer), i.e. better debugging
# Center the map's origin:
TestMap.setTranslation(512,512); # FIXME: don't hardcode these values, but read in canvas texture dimensions, otherwise it will break once someone uses non 1024x1024 textures ...
# Initialize a range (TODO: LayeredMap.Controller):
TestMap.set("range", 100);
# Little cursor of current position:
TestMap.createChild("path").rect(-5,-5,10,10).setColorFill(1,1,1).setColor(0,1,0);
# And make it move with our aircraft:
TestMap.setController("Aircraft position"); # from aircraftpos.controller
dlg.del = func() {
TestMap.del();
# call inherited 'del'
delete(me, "del");
me.del();
};
}, 1);
}, 0); # end ugly module init timer hack
var drawVOR = func(color, width=3) return func(group) {
# print("drawing vor");
var bbox = group.createChild("path")
.moveTo(-15,0)
.lineTo(-7.5,12.5)
.lineTo(7.5,12.5)
.lineTo(15,0)
.lineTo(7.5,-12.5)
.lineTo(-7.5,-12.5)
.close()
.setStrokeLineWidth(width)
.setColor( color );
# debug.dump( bbox.getBoundingBox() );
};
var cachedVOR1 = SymbolCache32x32.add( "VOR-BLUE", drawVOR( color:[0, 0.6, 0.85], width:3), SymbolCache.DRAW_CENTERED );
var cachedVOR2 = SymbolCache32x32.add( "VOR-RED" , drawVOR( color:[1.0, 0, 0], width: 3), SymbolCache.DRAW_CENTERED );
var cachedVOR3 = SymbolCache32x32.add( "VOR-GREEN" , drawVOR( color:[0, 1, 0], width: 3), SymbolCache.DRAW_CENTERED );
var cachedVOR4 = SymbolCache32x32.add( "VOR-WHITE" , drawVOR( color:[1, 1, 1], width: 3), SymbolCache.DRAW_CENTERED );
# STRESS TEST
if (0) {
for(var i=0;i <= 1024/32*4 - 4; i+=1)
SymbolCache32x32.add( "VOR-YELLOW"~i , drawVOR( color:[1, 1, 0], width: 3) );
var dlg = canvas.Window.new([640,320],"dialog");
var my_canvas = dlg.createCanvas().setColorBackground(1,1,1,1);
var root = my_canvas.createGroup();
SymbolCache32x32.get(name:"VOR-BLUE").render( group: root ).setGeoPosition(getprop("/position/latitude-deg"),getprop("/position/longitude-deg"));
}
})();
#print("finished loading files");
####### TEST SYMBOL #######
canvas.load_MapStructure = func;
}; # load_MapStructure
setlistener("/nasal/canvas/loaded", load_MapStructure); # end ugly module init listener hack

View file

@ -462,47 +462,45 @@ var Map = {
addLayer: func(factory, type_arg=nil, priority=nil) addLayer: func(factory, type_arg=nil, priority=nil)
{ {
if (!contains(me, "layers")) if (!contains(me, "layers"))
me.layers = []; me.layers = {};
if(contains(me.layers, type_arg))
print("addLayer() warning: overwriting existing layer:", type_arg);
# print("addLayer():", type_arg);
# Argument handling # Argument handling
if (type_arg != nil) if (type_arg != nil)
var type = factory.get(type_arg); var type = factory.get(type_arg);
else var type = factory; else var type = factory;
me.layers[type_arg]= type.new(me);
if (priority == nil) if (priority == nil)
priority = type.df_priority; priority = type.df_priority;
append(me.layers, [type.new(me), priority]);
if (priority != nil) if (priority != nil)
me._sort_priority(); me.layers[type_arg].setInt("z-index", priority);
return me; return me;
}, },
setPos: func(lat,lon,hdg=nil) getLayer: func(type_arg) me.layers[type_arg],
setPos: func(lat, lon, hdg=nil, range=nil)
{ {
me.set("ref-lat", lat); me.set("ref-lat", lat);
me.set("ref-lon", lon); me.set("ref-lon", lon);
if (hdg != nil) if (hdg != nil)
me.set("hdg", hdg); me.set("hdg", hdg);
if (range != nil)
# me.map.set("range", 100); me.set("range", range);
}, },
# Update each layer on this Map. Called by # Update each layer on this Map. Called by
# me.controller. # me.controller.
update: func update: func
{ {
foreach (var l; me.layers) foreach (var l; keys(me.layers)) {
call(l[0].update, arg, l[0]); var layer = me.layers[l];
call(layer.update, arg, layer);
}
return me; return me;
}, },
# private:
_sort_priority: func()
{
me.layers = sort(me.layers, me._sort_cmp);
forindex (var i; me.layers)
me.layers[i].set("z-index", i);
},
_sort_cmp: func(a,b) {
a[1] != b[1] and a[1] != nil and b[1] != nil and (a[1] < b[1] ? -1 : 1)
},
}; };
# Text # Text

View file

@ -429,10 +429,16 @@ var files_with = func(ext) {
} }
return results; return results;
} }
foreach(var ext; var extensions = ['.draw','.model','.layer'])
load_modules(files_with(ext));
setlistener("/nasal/canvas/loaded", func {
foreach(var ext; var extensions = ['.draw','.model','.layer'])
load_modules(files_with(ext));
if (contains(canvas,"load_MapStructure"))
load_MapStructure();
# canvas.MFD = {EFIS:}; # where we'll be storing all MFDs
# TODO: should be inside a separate subfolder, i.e. canvas/map/mfd
load_modules( files_with('.mfd'), 'canvas' );
});
# canvas.MFD = {EFIS:}; # where we'll be storing all MFDs
# TODO: should be inside a separate subfolder, i.e. canvas/map/mfd
load_modules( files_with('.mfd'), 'canvas' );

View file

@ -0,0 +1,32 @@
# Class things:
var name = 'DME';
var parents = [SymbolLayer.Controller];
var __self__ = caller(0)[0];
SymbolLayer.Controller.add(name, __self__);
SymbolLayer.add(name, {
parents: [SymbolLayer],
type: name, # Symbol type
df_controller: __self__, # controller to use by default -- this one
});
var a_instance = nil;
var new = func(layer) {
var m = {
parents: [__self__],
layer: layer,
listeners: [],
query_range_nm: 25,
query_type:'dme',
};
__self__.a_instance = m;
return m;
};
var del = func() {
foreach (var l; me.listeners)
removelistener(l);
};
var searchCmd = func {
#print("Running query:", me.query_type);
return positioned.findWithinRange(me.query_range_nm, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch
};

View file

@ -0,0 +1,12 @@
# Class things:
var name = 'DME';
var parents = [Symbol.Controller];
var __self__ = caller(0)[0];
Symbol.Controller.add(name, __self__);
Symbol.registry[ name ].df_controller = __self__;
var new = func(model) ; # this controller doesn't need an instance
var LayerController = SymbolLayer.Controller.registry[ name ];
var isActive = func(model) LayerController.a_instance.isActive(model);
var is_tuned = func()
die( name~".scontroller.is_tuned /MUST/ be provided by implementation" );

View file

@ -0,0 +1,35 @@
# Class things:
var name = 'DME';
var parents = [DotSym];
var __self__ = caller(0)[0];
DotSym.makeinstance( name, __self__ );
var element_type = "group"; # we want a group, becomes "me.element"
var icon_dme = nil;
var draw = func {
# Init
if (me.icon_dme == nil) {
me.icon_dme = me.element.createChild("path")
.moveTo(-15,0)
.line(-12.5,-7.5)
.line(7.5,-12.5)
.line(12.5,7.5)
.lineTo(7.5,-12.5)
.line(12.5,-7.5)
.line(7.5,12.5)
.line(-12.5,7.5)
.lineTo(15,0)
.lineTo(7.5,12.5)
.vert(14.5)
.horiz(-14.5)
.vert(-14.5)
.close()
.setStrokeLineWidth(3);
}
if (me.controller != nil and me.controller.is_tuned(me.model.frequency/100))
me.icon_dme.setColor(0,1,0);
else
me.icon_dme.setColor(0,0.6,0.85);
};

View file

@ -0,0 +1,33 @@
# Class things:
var name = 'FIX';
var parents = [SymbolLayer.Controller];
var __self__ = caller(0)[0];
SymbolLayer.Controller.add(name, __self__);
SymbolLayer.add(name, {
parents: [SymbolLayer],
type: name, # Symbol type
df_controller: __self__, # controller to use by default -- this one
});
var a_instance = nil;
var new = func(layer) {
var m = {
parents: [__self__],
layer: layer,
listeners: [],
query_range_nm: 25,
query_type:'fix',
};
__self__.a_instance = m;
return m;
};
var del = func() {
#print("VOR.lcontroller.del()");
foreach (var l; me.listeners)
removelistener(l);
};
var searchCmd = func {
#print("Running query:", me.query_type);
return positioned.findWithinRange(me.query_range_nm, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch
};

View file

@ -0,0 +1,12 @@
# Class things:
var name = 'FIX';
var parents = [Symbol.Controller];
var __self__ = caller(0)[0];
Symbol.Controller.add(name, __self__);
Symbol.registry[ name ].df_controller = __self__;
var new = func(model) ; # this controller doesn't need an instance
var LayerController = SymbolLayer.Controller.registry[ name ];
var isActive = func(model) LayerController.a_instance.isActive(model);
var query_range = func()
die( name~".scontroller.query_range /MUST/ be provided by implementation" );

View file

@ -0,0 +1,30 @@
# Class things:
var name = 'FIX';
var parents = [DotSym];
var __self__ = caller(0)[0];
DotSym.makeinstance( name, __self__ );
var element_type = "group"; # we want a group, becomes "me.element"
var icon_fix = nil;
var text_fix = nil;
var draw = func {
if (me.icon_fix != nil) return;
# the fix symbol
me.icon_fix = me.element.createChild("path")
.moveTo(-15,15)
.lineTo(0,-15)
.lineTo(15,15)
.close()
.setStrokeLineWidth(3)
.setColor(0,0.6,0.85)
.setScale(0.5,0.5); # FIXME: do proper LOD handling here - we need to scale according to current texture dimensions vs. original/design dimensions
# the fix label
me.text_fix = me.element.createChild("text")
.setDrawMode( canvas.Text.TEXT )
.setText(me.model.id)
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
.setFontSize(28)
.setTranslation(5,25);
};

View file

@ -0,0 +1,30 @@
# Class things:
var name = 'NDB';
var parents = [SymbolLayer.Controller];
var __self__ = caller(0)[0];
SymbolLayer.Controller.add(name, __self__);
SymbolLayer.add(name, {
parents: [SymbolLayer],
type: name, # Symbol type
df_controller: __self__, # controller to use by default -- this one
});
var new = func(layer) {
var m = {
parents: [__self__],
layer: layer,
listeners: [],
query_range_nm: 25,
query_type:'ndb',
};
return m;
};
var del = func() {
foreach (var l; me.listeners)
removelistener(l);
};
var searchCmd = func {
#print("Running query:", me.query_type);
return positioned.findWithinRange(me.query_range_nm, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch
};

View file

@ -0,0 +1,12 @@
# Class things:
var name = 'NDB';
var parents = [Symbol.Controller];
var __self__ = caller(0)[0];
Symbol.Controller.add(name, __self__);
Symbol.registry[ name ].df_controller = __self__;
var new = func(model) ; # this controller doesn't need an instance
var LayerController = SymbolLayer.Controller.registry[ name ];
var isActive = func(model) LayerController.a_instance.isActive(model);
var query_range = func()
die( name~".scontroller.query_range /MUST/ be provided by implementation" );

View file

@ -0,0 +1,18 @@
# Class things:
var name = 'NDB';
var parents = [DotSym];
var __self__ = caller(0)[0];
DotSym.makeinstance( name, __self__ );
var element_type = "group"; # we want a group, becomes "me.element", which we parse a SVG onto
var svg_path = "/gui/dialogs/images/ndb_symbol.svg"; # speaking of path, this is our path to use
var local_svg_path = nil; # track changes in the SVG's path
var draw = func {
if (me.svg_path == me.local_svg_path) return;
me.element.removeAllChildren();
me.local_svg_path = me.svg_path;
canvas.parsesvg(me.element, me.svg_path);
me.inited = 1;
};

View file

@ -7,6 +7,7 @@ SymbolLayer.add("VOR", {
type: "VOR", # Symbol type type: "VOR", # Symbol type
df_controller: __self__, # controller to use by default -- this one df_controller: __self__, # controller to use by default -- this one
}); });
var a_instance = nil;
var new = func(layer) { var new = func(layer) {
var m = { var m = {
parents: [__self__], parents: [__self__],
@ -14,6 +15,7 @@ var new = func(layer) {
active_vors: [], active_vors: [],
navNs: props.globals.getNode("instrumentation").getChildren("nav"), navNs: props.globals.getNode("instrumentation").getChildren("nav"),
listeners: [], listeners: [],
query_type:'vor',
}; };
setsize(m.active_vors, size(m.navNs)); setsize(m.active_vors, size(m.navNs));
foreach (var navN; m.navNs) { foreach (var navN; m.navNs) {
@ -24,6 +26,7 @@ var new = func(layer) {
} }
#call(debug.dump, keys(layer)); #call(debug.dump, keys(layer));
m.changed_freq(update:0); m.changed_freq(update:0);
__self__.a_instance = m;
return m; return m;
}; };
var del = func() { var del = func() {
@ -46,7 +49,7 @@ var changed_freq = func(update=1) {
if (update) me.layer.update(); if (update) me.layer.update();
}; };
var searchCmd = func { var searchCmd = func {
#print("Run query"); #print("Running query:", me.query_type);
return positioned.findWithinRange(100, 'vor'); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch return positioned.findWithinRange(100, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch
}; };

View file

@ -1,10 +1,11 @@
# Class things: # Class things:
var parents = [Symbol.Controller]; var parents = [Symbol.Controller];
var __self__ = caller(0)[0]; var __self__ = caller(0)[0];
Symbol.Controller.add("VOR", __self__);
Symbol.registry["VOR"].df_controller = __self__; Symbol.registry["VOR"].df_controller = __self__;
var new = func(model) ; # this controller doesn't need an instance var new = func(model) ; # this controller doesn't need an instance
var LayerController = SymbolLayer.registry["VOR"]; var LayerController = SymbolLayer.Controller.registry["VOR"];
var isActive = func(model) LayerController.isActive(model); var isActive = func(model) LayerController.a_instance.isActive(model);
var query_range = func() var query_range = func()
die("VOR.scontroller.query_range /MUST/ be provided by implementation"); die("VOR.scontroller.query_range /MUST/ be provided by implementation");

View file

@ -1,52 +1,57 @@
# Read by the DotSym.readinstance; each variable becomes a derived class's member/method # Class things:
var name = 'VOR';
var parents = [DotSym];
var __self__ = caller(0)[0];
DotSym.makeinstance( name, __self__ );
var element_type = "group"; # we want a group, becomes "me.element" var element_type = "group"; # we want a group, becomes "me.element"
var inited = 0; # this allows us to track whether draw() is an init() or an update() var icon_vor = nil;
var range_vor = nil; # two elements that get drawn when needed var 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 radial_vor = nil; # if one is nil, the other has to be nil
var draw = func { var draw = func {
if (me.inited) { # Init
# Update if (me.icon_vor == nil) {
if (me.controller.isActive(me.model)) { me.icon_vor = me.element.createChild("path")
if (me.range_vor == nil) { .moveTo(-15,0)
var rangeNm = me.controller.query_range(); .lineTo(-7.5,12.5)
# print("VOR is tuned:", me.model.id); .lineTo(7.5,12.5)
var radius = (me.model.range_nm/rangeNm)*345; .lineTo(15,0)
me.range_vor = me.element.createChild("path") .lineTo(7.5,-12.5)
.moveTo(-radius,0) .lineTo(-7.5,-12.5)
.arcSmallCW(radius,radius,0,2*radius,0) .close()
.arcSmallCW(radius,radius,0,-2*radius,0) .setStrokeLineWidth(3)
.setStrokeLineWidth(3) .setColor(0,0.6,0.85);
.setStrokeDashArray([5, 15, 5, 15, 5]) }
.setColor(0,1,0); # Update
if (me.controller.isActive(me.model)) {
if (me.range_vor == nil) {
var rangeNm = me.controller.query_range();
# print("VOR is tuned:", me.model.id);
var radius = (me.model.range_nm/rangeNm)*345;
me.range_vor = me.element.createChild("path")
.moveTo(-radius,0)
.arcSmallCW(radius,radius,0,2*radius,0)
.arcSmallCW(radius,radius,0,-2*radius,0)
.setStrokeLineWidth(3)
.setStrokeDashArray([5, 15, 5, 15, 5])
.setColor(0,1,0);
var course = controller.get_tuned_course(me.model.frequency/100); var course = me.controller.get_tuned_course(me.model.frequency/100);
vor_grp.createChild("path") me.radial_vor = me.element.createChild("path")
.moveTo(0,-radius) .moveTo(0,-radius)
.vert(2*radius) .vert(2*radius)
.setStrokeLineWidth(3) .setStrokeLineWidth(3)
.setStrokeDashArray([15, 5, 15, 5, 15]) .setStrokeDashArray([15, 5, 15, 5, 15])
.setColor(0,1,0) .setColor(0,1,0)
.setRotation(course*D2R); .setRotation(course*D2R);
icon_vor.setColor(0,1,0); me.icon_vor.setColor(0,1,0);
}
me.range_vor.show();
me.radial_vor.show();
} else {
me.range_vor.hide();
me.radial_vor.hide();
} }
} else # Init me.range_vor.show();
me.element.createChild("path") me.radial_vor.show();
.moveTo(-15,0) } elsif (me.range_vor != nil) {
.lineTo(-7.5,12.5) me.range_vor.hide();
.lineTo(7.5,12.5) me.radial_vor.hide();
.lineTo(15,0) }
.lineTo(7.5,-12.5)
.lineTo(-7.5,-12.5)
.close()
.setStrokeLineWidth(3)
.setColor(0,0.6,0.85);
}; };

View file

@ -0,0 +1,39 @@
# Class things:
var name = 'WPT'; # for waypoints
var parents = [SymbolLayer.Controller];
var __self__ = caller(0)[0];
SymbolLayer.Controller.add(name, __self__);
SymbolLayer.add(name, {
parents: [SymbolLayer],
type: name, # Symbol type
df_controller: __self__, # controller to use by default -- this one
});
var new = func(layer) {
var m = {
parents: [__self__],
layer: layer,
listeners: [],
query_range_nm: 25,
query_type:'vor',
};
return m;
};
var del = func() {
#print("VOR.lcontroller.del()");
foreach (var l; me.listeners)
removelistener(l);
};
var searchCmd = func {
#print("Running query: WPT");
var fp = flightplan();
var fpSize = fp.getPlanSize();
var result = [];
for (var i = 1; i <fpSize; i+=1)
append(result, fp.getWP(i).path()[0] );
return result;
# return positioned.findWithinRange(me.query_range_nm, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch
};

View file

@ -0,0 +1,12 @@
# Class things:
var name = 'WPT';
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" );

View file

@ -0,0 +1,34 @@
# Class things:
var name = 'WPT';
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;
var draw = func {
if (me.base != nil) return;
me.base = me.element.createChild("path")
.setStrokeLineWidth(3)
.moveTo(0,-25)
.lineTo(-5,-5)
.lineTo(-25,0)
.lineTo(-5,5)
.lineTo(0,25)
.lineTo(5,5)
.lineTo(25,0)
.lineTo(5,-5)
.setColor(1,1,1)
.close();
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);
};

File diff suppressed because it is too large Load diff

View file

@ -4,8 +4,8 @@ RouteModel.new = func make(LayerModel, RouteModel);
RouteModel.init = func { RouteModel.init = func {
me._view.reset(); me._view.reset();
if (!getprop("/autopilot/route-manager/active")) if (!getprop("/autopilot/route-manager/active"))
return; return;
## TODO: all the model stuff is still inside the draw file for now, this just ensures that it will be called once ## TODO: all the model stuff is still inside the draw file for now, this just ensures that it will be called once
foreach(var t; [nil] ) foreach(var t; [nil] )