
My thoughts were that it should basically resemble the C++ 3D animation system and be invisible (enough) that it could easily be replaced with more C++ should we need more performance (or just 'cause). Exposing SGExpression to Nasal would be helpful soon but not necessary yet.
This is my current prototype – I'm missing an expr() function but it should otherwise work.
- Code: Select all
var _get_xml = func {
var repl = 0;
if (!size(arg)) {
if (!contains(caller(0)[0], "xml"))
{ var xml = caller(1)[0]["xml"]; var repl=1 }
} else var xml = arg[0];
if (typeof(xml) == 'scalar') {
var path = resolvepath(xml);
if (!path) die("could not reopve path: '"~xml~"'");
xml = io.read_properties(path);
}
if (!isa(xml, props.Node))
die("bad xml arg");
if (repl) caller(1)[0].xml = xml;
return xml;
}
var AnimWrapElement = {
new: func(name, group, interval, on_update, on_create, on_destroy) {
var me = { parents:[AnimWrapElement, canvas],
name:name, interval:interval,
on_update:on_update, gr:group,
on_create:on_create,
on_destroy:on_destroy };
if (isa(group, canvas.Canvas))
me.canvas = group;
else me.canvas = group.getCanvas();
me.on_create(me);
me.loop = maketimer(interval, me, me.update);
return me;
},
start: func() {
me.loop.start();
return me;
},
update: func() {
var ou = me.on_update;
if (typeof(ou) == 'hash')
ou = values(ou);
foreach (var f; ou) {
if (typeof(f) == 'func')
f(me);
else {
if (typeof(f) == 'vector')
ou ~= f;
elsif (typeof(f)== 'hash')
ou ~= values(f);
if (size(ou) > 30)
die("max size of callbacks exceeded");
}
}
return me;
},
stop: func() {
me.loop.stop();
return me;
},
del: func() {
me.stop();
me.on_destroy();
me.gr.del():
return nil;
},
};
var create_elements = func(xml, group=nil, interval=0, options=nil) {
_get_xml();
var res = [];
foreach (var n; xml.getChildren("element"))
append(res, create_element(n, group, interval, options));
return res;
}
var create_element = func(xml, group=nil, interval=0, options=nil) {
_get_xml();
if (group == nil) group = canvas.new();
var svg = canvas.parse_svg(group, xml.getValue("path"), options);
var animations = create_animations(xml, group);
var ou = { "animations": animations, "": [] };
var cr = var de = func{};
if ((var sc=xml.getValue("nasal/load")) != nil) {
var code = compile(sc, "<canvas animation load>");
if (typeof(code) == 'func')
cr = code;
}
if ((var sc=xml.getValue("nasal/unload")) != nil) {
var code = compile(sc, "<canvas animation unload>");
if (typeof(code) == 'func')
de = code;
}
var instr = AnimWrapElement.new(xml.getValue("name"),
group, interval, ou, cr, de);
return instr;
}
var create_animations = func(xml, group) {
_get_xml();
var res = [];
foreach (var n; xml.getChildren("animation"))
append(res, create_animation(n, group));
return res;
}
var create_animation = func(xml, group) {
_get_xml();
var matrix = func matrix = group.createTransform();
foreach (var m; keys(xml))
if (substr(m,0,3)=='get' and typeof(xml[m])=='func')
caller(0)[0][m] =
(func{ func call(xml[m], arg, xml) })();
var getVal = getValue;
var getN = getNode;
var type = getVal("type");
if ((var e_id=getVal("id")) != nil)
group = group.findElementById(e_id);
if (type == "translate") {
matrix();
var x = expr(getN("x"));
var y = expr(getN("y"));
return func(instr) {
matrix.setTranslation(x(),y());
};
} elsif (type == "color") {
var r = expr(getN("r"));
var g = expr(getN("g"));
var b = expr(getN("b"));
return func(instr) {
group.setColor(r(),g(),b());
};
} elsif (type == "rotate") {
matrix();
var rot = expr(getN("rotation"));
return func(instr) {
matrix.setRotation(rot());
};
} elsif (type == "scale") {
matrix();
var scale = expr(getN("scale"));
return func(instr) {
matrix.setScale(scale());
};
} else die("unknown animation type: '"~type~"'");
}
Very small sample XML:
- Code: Select all
<PropertyList>
<element>
<name>747-PFD-pilot</name>
<svg-path>Aircaft/747-400/Instruments/PFD.svg</svg-path>
<animation>
<id>artificial-horizon</id>
<type>translate</type>
<y>
<mul>
<property>/orientation/pitch-deg</property>
<value>6.666666666</value>
</mul>
</y>
</animation>
</element>
</PropertyList>
We could easily extend this to add a <map> element with <layer>s (aka MapStructure) or even more PFD ideas, but this is just a way to easily animate an SVG.
Obviously there's a lot of work to be done here but my question for you is: is this the general way we want to do it?