1
0
Fork 0
A320-family/Models/Instruments/ND/canvas/framework/canvas.nas

310 lines
9.1 KiB
Text
Raw Normal View History

2019-10-14 16:48:35 +00:00
# A3XX ND Canvas
# Joshua Davidson (Octal450)
# Based on work by artix
# Copyright (c) 2021 Josh Davidson (Octal450)
2019-10-14 16:48:35 +00:00
var assert_m = canvas.assert_m;
# --------------------------------
# From FGDATA/Nasal/canvas/api.nas
# --------------------------------
# Recursively get all children of class specified by first param
canvas.Group.getChildrenOfType = func(type, array = nil){
var children = array;
if(children == nil)
children = [];
var my_children = me.getChildren();
if(typeof(type) != "vector")
type = [type];
foreach(var c; my_children){
foreach(var t; type){
if(isa(c, t)){
append(children, c);
}
}
if(isa(c, canvas.Group)){
c.getChildrenOfType(type, children);
}
}
return children;
};
# Set color to children of type Path and Text. It is possible to optionally
# specify which types of children should be affected by passing a vector as
# the last agrument, ie. my_group.setColor(1,1,1,[Path]);
canvas.Group.setColor = func(){
var color = arg;
var types = [Path, Text];
var arg_c = size(color);
if(arg_c > 1 and typeof(color[-1]) == "vector"){
types = color[-1];
color = subvec(color, 0, arg_c - 1);
}
var children = me.getChildrenOfType(types);
if(typeof(color) == "vector"){
var first = color[0];
if(typeof(first) == "vector")
color = first;
}
foreach(var c; children)
c.setColor(color);
};
canvas.Map.addLayer = func(factory, type_arg=nil, priority=nil, style=nil, opts=nil, visible=1)
{
if(contains(me.layers, type_arg))
logprint("warn", "addLayer() warning: overwriting existing layer:", type_arg);
2019-10-14 16:48:35 +00:00
var options = opts;
# Argument handling
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);
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;
if (priority == nil)
priority = type.df_priority;
if (priority != nil)
layer.group.setInt("z-index", priority);
return layer; # return new layer to caller() so that we can directly work with it, i.e. to register event handlers (panning/zooming)
};
# -----------------------------------------
# From FGDATA/Nasal/canvas/MapStructure.nas
# -----------------------------------------
var opt_member = func(h,k) {
if (contains(h, k)) return h[k];
if (contains(h, "parents")) {
var _=h.parents;
for (var i=0;i<size(_);i+=1){
var v = opt_member(_[i], k);
if (v != nil) return v;
}
}
return nil;
};
# Symbol
canvas.Symbol.formattedString = func(frmt, model_props){
if(me.model == nil) return frmt;
var args = [];
foreach(var prop; model_props){
if(contains(me.model, prop)){
var val = me.model[prop];
var tp = typeof(val);
if(tp != "scalar"){
val = "";
#logprint("warn", "formattedString: invalid type for "~prop~" (" ~ tp ~ ")");
2019-10-14 16:48:35 +00:00
} else {
append(args, val);
}
}
}
return call(sprintf, [frmt] ~ args);
};
canvas.Symbol.getOption = func(name, default = nil){
var opt = me.options;
if(opt == nil)
opt = me.layer.options;
if(opt == nil) return default;
var val = opt_member(opt, name);
if(val == nil) return default;
return val;
};
canvas.Symbol.getStyle = func(name, default = nil){
var st = me.style;
if(st == nil)
st = me.layer.style;
if(st == nil) return default;
var val = opt_member(st, name);
if(typeof(val) == "func"){
val = (call(val,[],me));
}
if(val == nil) return default;
return val;
};
canvas.Symbol.getLabelFromModel = func(default_val = nil){
if(me.model == nil) return default_val;
if(default_val == nil and contains(me.model, "id"))
default_val = me.model.id;
var label_content = me.getOption("label_content");
if(label_content == nil) return default_val;
if(typeof(label_content) == "scalar")
label_content = [label_content];
var format_s = me.getOption("label_format");
var label = "";
if(format_s == nil){
format_s = "%s";
}
return me.formattedString(format_s, label_content);
};
canvas.Symbol.callback = func(name, args...){
name = name ~"_callback";
var f = me.getOption(name);
if(typeof(f) == "func"){
return call(f, args, me);
}
};
# DotSym
canvas.DotSym.update = func() {
if (me.controller != nil) {
if (!me.controller.update(me, me.model)) return;
elsif (!me.controller.isVisible(me.model)) {
me.element.hide();
return;
}
} else
me.element.show();
me.draw();
if(me.getOption("disable_position", 0)) return; # << CHECK FOR OPTION "disable_position"
var pos = me.controller.getpos(me.model);
if (size(pos) == 2)
pos~=[nil]; # fall through
if (size(pos) == 3)
var (lat,lon,rotation) = pos;
else __die("DotSym.update(): bad position: "~debug.dump(pos));
# print(me.model.id, ": Position lat/lon: ", lat, "/", lon);
me.element.setGeoPosition(lat,lon);
if (rotation != nil)
me.element.setRotation(rotation);
};
# SVGSymbol
canvas.SVGSymbol.init = func() {
me.callback("init_before");
var opt_path = me.getStyle("svg_path");
if(opt_path != nil)
me.svg_path = opt_path;
if (!me.cacheable) {
if(me.svg_path != nil and me.svg_path != "")
canvas.parsesvg(me.element, me.svg_path);
# hack:
if (var scale = me.layer.style["scale_factor"])
me.element.setScale(scale);
if ((var transl = me.layer.style["translate"]) != nil)
me.element.setTranslation(transl);
} else {
__die("cacheable not implemented yet!");
}
me.callback("init_after");
me.draw();
};
canvas.SVGSymbol.draw = func{
me.callback("draw");
};
# SymbolLayer
canvas.SymbolLayer._new = func(m, style, controller, options) {
# print("SymbolLayer setup options:", m.options!=nil);
m.style = default_hash(style, m.df_style);
m.options = default_hash(options, m.df_options);
if (controller == nil)
controller = m.df_controller;
assert_m(controller, "parents");
if (controller.parents[0] == SymbolLayer.Controller)
controller = controller.new(m);
assert_m(controller, "parents");
assert_m(controller.parents[0], "parents");
if (controller.parents[0].parents[0] != SymbolLayer.Controller)
__die("MultiSymbolLayer: OOP error");
if(options != nil){ # << CHECK FOR CONFIGURABLE LISTENERS
var listeners = opt_member(controller, "listeners");
var listen = opt_member(options, "listen");
if (listen != nil and listeners != nil){
var listen_tp = typeof(listen);
if(listen_tp != "vector" and listen_tp != "scalar")
__die("Options 'listen' cannot be a "~ listen_tp);
if(typeof(listen) == "scalar")
listen = [listen];
foreach(var node_name; listen){
var node = opt_member(options, node_name);
if(node == nil)
node = node_name;
append(controller.listeners,
setlistener(node, func call(m.update,[],m),0,0));
}
}
}
m.controller = controller;
};
# LineSymbol
canvas.LineSymbol.new = func(group, layer, model, controller=nil) {
if (me == nil) __die("Need me reference for LineSymbol.new()");
if (typeof(model) != "vector") {
if(typeof(model) == "hash"){
if(!contains(model, "path"))
canvas.__die("LineSymbol.new(): model hash requires path");
}
else canvas.__die("LineSymbol.new(): need a vector of points or a hash");
}
var m = {
parents: [me],
group: group,
layer: layer,
model: model,
controller: controller == nil ? me.df_controller : controller,
element: group.createChild(
"path", me.element_id
),
};
append(m.parents, m.element);
canvas.Symbol._new(m);
m.init();
return m;
};
# Non-static:
canvas.LineSymbol.draw = func() {
me.callback("draw_before");
if (!me.needs_update) return;
#logprint(_MP_dbg_lvl, "redrawing a LineSymbol "~me.layer.type);
2019-10-14 16:48:35 +00:00
me.element.reset();
var cmds = [];
var coords = [];
var cmd = canvas.Path.VG_MOVE_TO;
var path = me.model;
if(typeof(path) == "hash"){
path = me.model.path;
if(path == nil)
canvas.__die("LineSymbol model requires a 'path' member (vector)");
}
foreach (var m; path) {
if(size(keys(m)) >= 2){
var (lat,lon) = me.controller.getpos(m);
append(coords,"N"~lat);
append(coords,"E"~lon);
append(cmds,cmd);
cmd = canvas.Path.VG_LINE_TO;
} else {
cmd = canvas.Path.VG_MOVE_TO;
}
}
me.element.setDataGeo(cmds, coords);
me.element.update(); # this doesn"t help with flickering, it seems
me.callback("draw_after");
};