1
0
Fork 0
A320-family/Models/Instruments/ND/canvas/framework/canvas.nas
2019-10-14 12:48:35 -04:00

309 lines
9.1 KiB
Text
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# A3XX ND Canvas
# Joshua Davidson (Octal450)
# Based on work by artix
# Copyright (c) 2019 Joshua Davidson (Octal450)
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))
printlog("warn", "addLayer() warning: overwriting existing layer:", type_arg);
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 = "";
#printlog("warn", "formattedString: invalid type for "~prop~" (" ~ tp ~ ")");
} 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;
#printlog(_MP_dbg_lvl, "redrawing a LineSymbol "~me.layer.type);
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");
};