1
0
Fork 0
fgdata/Aircraft/ufo/ufo.nas
mfranz e959633725 s/cursor/marker/ and put the marker toggle function on the m-key. It isn't
a cursor, after all, and the term "cursor" is already used for the startup
model. That one should really be called "model", but can't, because this is
already used for the ufo 3D model property ... sigh.   :-]
2007-02-27 22:56:22 +00:00

956 lines
24 KiB
Text

# maximum speed -----------------------------------------------------------------------------------
var maxspeed = props.globals.getNode("engines/engine/speed-max-mps");
var speed = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000];
var current = 7;
controls.flapsDown = func(x) {
if (!x) {
return;
} elsif (x < 0 and current > 0) {
current -= 1;
} elsif (x > 0 and current < size(speed) - 1) {
current += 1;
}
var s = speed[current];
maxspeed.setDoubleValue(s);
gui.popupTip("Max. Speed " ~ s ~ " m/s");
}
# library stuff -----------------------------------------------------------------------------------
var EPSILON = 0.0000000000001;
var ERAD = 6378138.12; # Earth radius (m)
var D2R = math.pi / 180;
var R2D = 180 / math.pi;
var FT2M = 0.3048;
var M2FT = 3.28083989501312335958;
var printf = func(_...) { print(call(sprintf, _)) }
var floor = func(v) { v < 0.0 ? -int(-v) - 1 : int(v) }
var ceil = func(v) { -floor(-v) }
var pow = func(v, w) { v < 0 ? nil : v ? math.exp(math.ln(v) * w) : 0 }
var pow2 = func(e) { e ? 2 * pow2(e - 1) : 1 }
var sin = math.sin;
var cos = math.cos;
var atan2 = math.atan2;
var sqrt = math.sqrt;
var asin = func(v) { math.atan2(v, math.sqrt(1 - v * v)) }
var acos = func(v) { math.atan2(math.sqrt(1 - v * v), v) }
var mod = func(v, w) {
var x = v - w * int(v / w);
return x < 0 ? x + abs(w) : x;
}
# class that maintains one set of geographical coordinates and provides
# simple conversion methods that assume a spherical Earth
#
var Coord = {
new : func(copy = nil) {
var m = { parents: [Coord] };
m._pdirty = 1; # polar
m._cdirty = 1; # cartesian
m._lon = nil; # in radian
m._lat = nil;
m._alt = nil; # ASL
m._x = nil; # in m
m._y = nil;
m._z = nil;
if (copy != nil) {
m.set(copy);
}
return m;
},
_cupdate : func {
me._cdirty or return;
var rad = ERAD + me._alt;
var cosphi = cos(me._lat) * rad;
me._x = cosphi * cos(me._lon);
me._y = cosphi * sin(me._lon);
me._z = sin(me._lat) * rad;
me._cdirty = 0;
},
_pupdate : func {
me._pdirty or return;
me._lat = atan2(me._z, sqrt(me._x * me._x + me._y * me._y));
me._lon = atan2(me._y, me._x);
me._alt = sqrt(me._x * me._x + me._y * me._y + me._z * me._z) - ERAD;
me._pdirty = 0;
},
x : func { me._cupdate(); me._x },
y : func { me._cupdate(); me._y },
z : func { me._cupdate(); me._z },
xyz : func { me._cupdate(); [me._x, me._y, me._z] },
lon : func { me._pupdate(); me._lon * R2D }, # return in degree
lat : func { me._pupdate(); me._lat * R2D },
alt : func { me._pupdate(); me._alt },
lonlat : func { me._pupdate(); [me._lon, me._lat, me._alt] },
set_x : func(x) { me._pupdate(); me._pdirty = 1; me._x = x; me },
set_y : func(y) { me._pupdate(); me._pdirty = 1; me._y = y; me },
set_z : func(z) { me._pupdate(); me._pdirty = 1; me._z = z; me },
set_lon : func(lon) { me._cupdate(); me._cdirty = 1; me._lon = lon * D2R; me },
set_lat : func(lat) { me._cupdate(); me._cdirty = 1; me._lat = lat * D2R; me },
set_alt : func(alt) { me._cupdate(); me._cdirty = 1; me._alt = alt; me },
set : func(c) {
c._pupdate();
me._lon = c._lon;
me._lat = c._lat;
me._alt = c._alt;
me._cdirty = 1;
me._pdirty = 0;
me;
},
set_lonlat : func(lon, lat, alt = 0) {
me._lon = lon * D2R;
me._lat = lat * D2R;
me._alt = alt;
me._cdirty = 1;
me._pdirty = 0;
me;
},
set_xyz : func(x, y, z) {
me._x = x;
me._y = y;
me._z = z;
me._pdirty = 1;
me._cdirty = 0;
me;
},
apply_course_distance : func(course, dist) {
me._pupdate();
course *= D2R;
dist /= ERAD;
me._lat = asin(sin(me._lat) * cos(dist) + cos(me._lat) * sin(dist) * cos(course));
if (cos(me._lat) > EPSILON) {
me._lon = math.pi - mod(math.pi - me._lon - asin(sin(course) * sin(dist)
/ cos(me._lat)), 2 * math.pi);
}
me._cdirty = 1;
me;
},
course_to : func(dest) {
me._pupdate();
dest._pupdate();
if (me._lon == dest._lon and me._lat == dest._lat) {
return 0;
}
var dlon = dest._lon - me._lon;
return mod(atan2(sin(dlon) * cos(dest._lat), cos(me._lat) * sin(dest._lat)
- sin(me._lat) * cos(dest._lat) * cos(dlon)), 2 * math.pi) * R2D;
},
# arc distance on an earth sphere; doesn't consider altitude
distance_to : func(dest) {
me._pupdate();
dest._pupdate();
if (me._lon == dest._lon and me._lat == dest._lat) {
return 0;
}
var o = sin((me._lon - dest._lon) * 0.5);
var a = sin((me._lat - dest._lat) * 0.5);
return 2.0 * ERAD * asin(sqrt(a * a + cos(me._lat) * cos(dest._lat) * o * o));
},
direct_distance_to : func(dest) {
me._cupdate();
dest._cupdate();
var dx = dest._x - me._x;
var dy = dest._y - me._y;
var dz = dest._z - me._z;
return sqrt(dx * dx + dy * dy + dz * dz);
},
dump : func {
if (me._cdirty and me._pdirty) {
print("Coord.print(): coord undefined");
}
me._cupdate();
me._pupdate();
printf("x=%f y=%f z=%f lon=%f lat=%f alt=%f",
me.x(), me.y(), me.z(), me.lon(), me.lat(), me.alt());
},
};
var init_prop = func(prop, value) {
if (prop.getValue() != nil) {
value = prop.getValue();
}
prop.setDoubleValue(value);
return value;
}
# sort vector of strings (bubblesort)
#
var sort = func(l) {
while (1) {
var n = 0;
for (var i = 0; i < size(l) - 1; i += 1) {
if (cmp(l[i], l[i + 1]) > 0) {
var t = l[i + 1];
l[i + 1] = l[i];
l[i] = t;
n += 1;
}
}
if (!n) {
return l;
}
}
}
# binary search of string in sorted vector; returns index or -1 if not found
#
var search = func(list, which) {
var left = 0;
var right = size(list);
var middle = nil;
while (1) {
middle = int((left + right) / 2);
var c = cmp(list[middle], which);
if (!c) {
return middle;
} elsif (left == middle) {
return -1;
} elsif (c > 0) {
right = middle;
} else {
left = middle;
}
}
}
# scan all objects in subdir of $FG_ROOT. (Prefer *.xml files to *.ac files
# if both exist)
#
var scan_models = func(base) {
var result = [];
var list = directory(getprop("/sim/fg-root") ~ "/" ~ base);
if (list == nil) {
return result;
}
var xml = {};
var ac = {};
foreach (var d; list) {
if (d[0] != `.` and d != "CVS") {
if (substr(d, size(d) - 4) == ".xml") {
xml[base ~ "/" ~ d] = 1;
} elsif (substr(d, size(d) - 3) == ".ac") {
ac[base ~ "/" ~ d] = 1;
} else {
foreach (var s; scan_models(base ~ "/" ~ d)) {
append(result, s);
}
}
}
}
foreach (var m; keys(xml)) {
append(result, m);
delete(ac, substr(m, 0, size(m) - 3) ~ "ac");
}
foreach (var m; keys(ac)) {
append(result, m);
}
return result;
}
# normalize degree to 0 <= angle < 360
#
var normdeg = func(angle) {
while (angle < 0) {
angle += 360;
}
while (angle >= 360) {
angle -= 360;
}
return angle;
}
var bucket_span = func(lat) {
if (lat >= 89.0 ) {
360.0;
} elsif (lat >= 88.0 ) {
8.0;
} elsif (lat >= 86.0 ) {
4.0;
} elsif (lat >= 83.0 ) {
2.0;
} elsif (lat >= 76.0 ) {
1.0;
} elsif (lat >= 62.0 ) {
0.5;
} elsif (lat >= 22.0 ) {
0.25;
} elsif (lat >= -22.0 ) {
0.125;
} elsif (lat >= -62.0 ) {
0.25;
} elsif (lat >= -76.0 ) {
0.5;
} elsif (lat >= -83.0 ) {
1.0;
} elsif (lat >= -86.0 ) {
2.0;
} elsif (lat >= -88.0 ) {
4.0;
} elsif (lat >= -89.0 ) {
8.0;
} else {
360.0;
}
}
var tile_index = func(lon, lat) {
var lon_floor = floor(lon);
var lat_floor = floor(lat);
var span = bucket_span(lat);
var x = 0;
if (span < 0.0000001) {
lon = 0;
} elsif (span <= 1.0) {
x = int((lon - lon_floor) / span);
} else {
if (lon >= 0) {
lon = int(int(lon / span) * span);
} else {
lon = int(int((lon + 1) / span) * span - span);
if (lon < -180) {
lon = -180;
}
}
}
var y = int((lat - lat_floor) * 8);
(lon_floor + 180) * 16384 + (lat_floor + 90) * 64 + y * 8 + x;
}
var format = func(lon, lat) {
sprintf("%s%03d%s%02d", lon < 0 ? "w" : "e", abs(lon), lat < 0 ? "s" : "n", abs(lat));
}
var tile_path = func(lon, lat) {
var p = format(floor(lon / 10.0) * 10, floor(lat / 10.0) * 10);
p ~= "/" ~ format(floor(lon), floor(lat));
p ~= "/" ~ tile_index(lon, lat) ~ ".stg";
}
# -------------------------------------------------------------------------------------------------
# loop that generates the model flashing pulse
#
var clock = 0;
var clock_loop = func {
clock = !clock;
settimer(clock_loop, 0.3);
}
clock_loop();
var ufo_position = func {
var lon = getprop("/position/longitude-deg");
var lat = getprop("/position/latitude-deg");
var alt = getprop("/position/altitude-ft") * FT2M;
Coord.new().set_lonlat(lon, lat, alt);
}
# class that maintains one adjustable model property (see src/Model/modelmgr.cxx)
#
var ModelValue = {
new : func(base, name, value) {
var m = { parents: [ModelValue] };
m.propN = base.getNode(name, 1);
m.propN.setDoubleValue(value);
base.getNode(name ~ "-prop", 1).setValue(m.propN.getPath());
return m;
},
set : func(v) {
me.propN.setDoubleValue(v);
},
get : func {
me.propN.getValue();
},
};
# class that maintains one scenery object (see src/Model/modelmgr.cxx)
#
var Model = {
new : func(path, pos, data = nil) {
var m = { parents: [Model] };
m.pos = pos;
m.path = path;
m.selected = 1;
m.visible = 1;
m.flash_until = 0;
m.loopid = 0;
m.elapsedN = props.globals.getNode("/sim/time/elapsed-sec", 1);
var models = props.globals.getNode("/models", 1);
for (var i = 0; 1; i += 1) {
if (models.getChild("model", i, 0) == nil) {
m.node = models.getChild("model", i, 1);
break;
}
}
m.node.getNode("legend", 1).setValue("");
if (isa(data, props.Node)) {
props.copy(data, m.node); # import node
}
var hdg = init_prop(m.node.getNode("heading-deg", 1), 0);
var pitch = init_prop(m.node.getNode("pitch-deg", 1), 0);
var roll = init_prop(m.node.getNode("roll-deg", 1), 0);
m.node.getNode("path", 1).setValue(path);
m.lon = ModelValue.new(m.node, "longitude-deg", pos.lon());
m.lat = ModelValue.new(m.node, "latitude-deg", pos.lat());
m.alt = ModelValue.new(m.node, "elevation-ft", pos.alt() * M2FT);
m.hdg = ModelValue.new(m.node, "heading-deg", hdg);
m.pitch = ModelValue.new(m.node, "pitch-deg", pitch);
m.roll = ModelValue.new(m.node, "roll-deg", roll);
m.node.getNode("load", 1).setValue(1);
m.node.removeChildren("load");
return m;
},
remove : func {
props.globals.getNode("/models", 1).removeChild("model", me.node.getIndex());
},
clone : func(path) {
Model.new(path, me.pos, me.node);
},
move : func(pos) {
var v = me.visible;
me.unhide();
me.pos.set(pos);
me.lon.set(me.pos.lon());
me.lat.set(me.pos.lat());
me.alt.set(me.pos.alt() * M2FT);
v or me.hide();
},
raise : func (dist) {
var v = me.visible;
me.unhide();
me.pos.set_alt(me.pos.alt() + dist);
me.alt.set(me.pos.alt() * M2FT);
v or me.hide();
},
apply_course_distance : func(course, dist) {
me.pos.apply_course_distance(course, dist);
me.lon.set(me.pos.lon());
me.lat.set(me.pos.lat());
},
direct_distance_to : func(dest) {
me.pos.direct_distance_to(dest);
},
flash : func(v) {
me.loopid += 1;
if (v) {
me.flash_until = me.elapsedN.getValue() + 2;
me._flash_(me.loopid);
} else {
me.unhide();
}
},
_flash_ : func(id) {
id == me.loopid or return;
if (me.elapsedN.getValue() > me.flash_until) {
return me.unhide();
} elsif (clock) {
me.hide();
} else {
me.unhide();
}
settimer(func { me._flash_(id) }, 0);
},
hide : func {
me.visible or return;
me.alt.set(me.alt.get() - ERAD);
me.visible = 0;
},
unhide : func {
me.visible and return;
me.alt.set(me.alt.get() + ERAD);
me.visible = 1;
},
get_data : func {
var n = props.Node.new();
props.copy(me.node, n);
me.add_derived_data(n);
return n;
},
add_derived_data : func(node) {
node.removeChildren("longitude-deg-prop");
node.removeChildren("latitude-deg-prop");
node.removeChildren("elevation-ft-prop");
node.removeChildren("heading-deg-prop");
node.removeChildren("pitch-deg-prop");
node.removeChildren("roll-deg-prop");
var path = node.getNode("path").getValue();
var lon = node.getNode("longitude-deg").getValue();
var lat = node.getNode("latitude-deg").getValue();
var elev = node.getNode("elevation-ft").getValue();
var hdg = node.getNode("heading-deg").getValue();
var legend = node.getNode("legend").getValue();
var type = nil;
var spec = "";
if (path == "Aircraft/ufo/Models/sign.ac") {
type = "OBJECT_SIGN";
if (legend == "") {
legend = "{@size=10,@material=RedSign}NO_CONTENTS_" ~ int(10000 * rand());
}
foreach (var c; split('', legend)) {
if (c != ' ') {
spec ~= c;
}
}
} else {
type = "OBJECT_SHARED";
spec = path;
}
var elev_m = elev * FT2M;
var stg_hdg = normdeg(360 - hdg);
var stg_path = tile_path(lon, lat);
var abs_path = getprop("/sim/fg-root") ~ "/" ~ path;
var obj_line = sprintf("%s %s %.8f %.8f %.4f %.1f", type, spec, lon, lat, elev_m, stg_hdg);
node.getNode("absolute-path", 1).setValue(abs_path);
node.getNode("legend", 1).setValue(legend);
node.getNode("stg-path", 1).setValue(stg_path);
node.getNode("stg-heading-deg", 1).setDoubleValue(stg_hdg);
node.getNode("elevation-m", 1).setDoubleValue(elev_m);
node.getNode("object-line", 1).setValue(obj_line);
return node;
},
};
var ModelMgr = {
new : func(path) {
var m = { parents: [ModelMgr] };
m.active = nil;
m.models = [];
m.legendN = props.globals.getNode("/sim/gui/dialogs/ufo-status/input", 1);
m.legendN.setValue("");
m.mouse_coord = ufo_position();
m.import();
m.marker = Model.new("Aircraft/ufo/Models/marker.ac", Coord.new().set_xyz(0, 0, 0));
m.marker.hide();
m.modelpath = path;
if (path != "Aircraft/ufo/Models/cursor.ac") {
status_dialog.open();
}
return m;
},
click : func(mouse_coord) {
me.mouse_coord = mouse_coord;
status_dialog.open();
adjust_dialog.center_sliders();
if (KbdAlt.getBoolValue()) { # move active object here (and other selected ones along with it)
(me.active == nil) and return;
var course = me.active.pos.course_to(me.mouse_coord);
var distance = me.active.pos.distance_to(me.mouse_coord);
foreach (var m; me.models) {
m.pos.set_alt(me.mouse_coord.alt());
m.selected and m.apply_course_distance(course, distance);
}
me.marker.move(me.active.pos);
return;
}
if (!KbdShift.getBoolValue()) {
me.deselect_all();
}
if (KbdCtrl.getBoolValue()) { # select existing object
me.select();
} else { # add one new object
me.active = Model.new(me.modelpath, mouse_coord, me.sticky_data());
append(me.models, me.active);
me.display_status(me.modelpath);
me.marker.move(me.active.pos);
if (KbdShift.getBoolValue()) {
foreach (var m; me.models) {
m.flash(m.selected);
}
}
}
},
select : func() {
if (!size(me.models)) {
me.active = nil;
me.marker.move(Coord.new().set_xyz(0, 0, 0));
return;
}
var min_dist = 10 * ERAD;
forindex (var i; me.models) {
var dist = me.models[i].direct_distance_to(me.mouse_coord);
if (dist < min_dist) {
min_dist = dist;
me.active = me.models[i];
}
}
me.active.selected = 1;
me.marker.move(me.active.pos);
foreach (var m; me.models) {
m.flash(m.selected);
}
me.display_status(me.modelpath = me.active.path);
},
deselect_all : func {
foreach (var m; me.models) {
m.flash(m.selected = 0);
}
},
remove_selected : func {
var models = [];
foreach (var m; me.models) {
if (m.selected) {
m.remove();
} else {
append(models, m);
}
}
me.models = models;
me.select();
me.display_status(me.modelpath);
},
set_modelpath : func(path) {
me.modelpath = path;
me.display_status(path);
},
update_legend : func {
if (me.active != nil) {
me.active.node.getNode("legend", 1).setValue(me.legendN.getValue());
}
},
display_status : func(path) {
var legend = me.active == nil ? "" : me.active.node.getNode("legend", 1).getValue();
me.legendN.setValue(legend);
setprop("/sim/model/ufo/status", "(" ~ size(me.models) ~ ") " ~ path);
},
get_data : func {
var n = props.Node.new();
forindex (var i; me.models) {
props.copy(me.models[i].get_data(), n.getChild("model", i, 1));
}
return n;
},
cycle : func(up) {
var i = search(modellist, me.modelpath) + up;
if (i < 0) {
i = size(modellist) - 1;
} elsif (i >= size(modellist)) {
i = 0;
}
me.set_modelpath(modellist[i]);
var models = [];
foreach (var m; me.models) {
if (m.selected) {
append(models, m.clone(modellist[i]));
m.remove();
} else {
append(models, m);
}
}
me.models = models;
},
sticky_data : func {
var n = props.Node.new();
if (me.active == nil) {
return n;
}
var hdg = n.getNode("heading-deg", 1);
var pitch = n.getNode("pitch-deg", 1);
var roll = n.getNode("roll-deg", 1);
if (getprop("/models/adjust/sticky-heading")) {
hdg.setDoubleValue(me.active.node.getNode("heading-deg").getValue());
} else {
hdg.setDoubleValue(0);
}
if (getprop("/models/adjust/sticky-orientation")) {
pitch.setDoubleValue(me.active.node.getNode("pitch-deg").getValue());
roll.setDoubleValue(me.active.node.getNode("roll-deg").getValue());
} else {
pitch.setDoubleValue(0);
roll.setDoubleValue(0);
}
return n;
},
reset_heading : func {
foreach (var m; me.models) {
if (m.selected) {
m.hdg.set(0);
}
}
},
reset_orientation : func {
foreach (var m; me.models) {
if (m.selected) {
m.pitch.set(0);
m.roll.set(0);
}
}
},
import : func {
me.active = nil;
var mandatory = ["path", "longitude-deg", "latitude-deg", "elevation-ft"];
foreach (var m; props.globals.getNode("models", 1).getChildren("model")) {
var ok = 1;
foreach (var a; mandatory) {
if (m.getNode(a) == nil or m.getNode(a).getType() == "NONE") {
ok = 0;
}
}
if (ok) {
var tmp = props.Node.new({ legend:"", "heading-deg":0, "pitch-deg":0, "roll-deg":0 });
props.copy(m, tmp);
m.getParent().removeChild(m.getName(), m.getIndex());
var c = Coord.new().set_lonlat(
tmp.getNode("longitude-deg").getValue(),
tmp.getNode("latitude-deg").getValue(),
tmp.getNode("elevation-ft").getValue() * FT2M);
append(me.models, me.active = Model.new(tmp.getNode("path").getValue(), c, tmp));
}
}
},
adjust : func(name, value, scale = 0) {
if (!size(me.models) or me.active == nil) {
return;
}
var ufo = ufo_position();
var dist = scale ? ufo.distance_to(me.active.pos) * 0.05 : 1;
if (name == "longitudinal") {
var dir = ufo.course_to(me.active.pos);
foreach (var m; me.models) {
m.selected and m.apply_course_distance(dir, value * dist);
}
} elsif (name == "transversal") {
var dir = ufo.course_to(me.active.pos) + 90;
foreach (var m; me.models) {
m.selected and m.apply_course_distance(dir, value * dist);
}
} elsif (name == "altitude") {
foreach (var m; me.models) {
m.selected and m.raise(value * dist * 0.4);
}
} elsif (name == "heading") {
foreach (var m; me.models) {
m.selected and m.hdg.set(m.hdg.get() + value * 4);
}
} elsif (name == "pitch") {
foreach (var m; me.models) {
m.selected and m.pitch.set(m.pitch.get() + value * 6);
}
} elsif (name == "roll") {
foreach (var m; me.models) {
m.selected and m.roll.set(m.roll.get() + value * 6);
}
}
me.marker.move(me.active.pos);
},
toggle_marker : func {
me.marker.visible ? me.marker.hide() : me.marker.unhide();
},
};
var scan_dirs = func(csv) {
var list = ["Aircraft/ufo/Models/sign.ac"];
foreach(var dir; split(",", csv)) {
foreach(var m; scan_models(dir)) {
append(list, m);
}
}
return sort(list);
}
var print_ufo_data = func {
print("\n\n------------------------------ UFO -------------------------------\n");
var lon = getprop("/position/longitude-deg");
var lat = getprop("/position/latitude-deg");
var alt_ft = getprop("/position/altitude-ft");
var elev_m = getprop("/position/ground-elev-m");
var heading = getprop("/orientation/heading-deg");
var agl_ft = alt_ft - elev_m * M2FT;
printf("Longitude: %.8f deg", lon);
printf("Latitude: %.8f deg", lat);
printf("Altitude ASL: %.4f m (%.4f ft)", alt_ft * FT2M, alt_ft);
printf("Altitude AGL: %.4f m (%.4f ft)", agl_ft * FT2M, agl_ft);
printf("Heading: %.1f deg", normdeg(heading));
printf("Ground Elev: %.4f m (%.4f ft)", elev_m, elev_m * M2FT);
print();
print("# " ~ tile_path(lon, lat));
printf("OBJECT_STATIC %.8f %.8f %.4f %.1f", lon, lat, elev_m, normdeg(360 - heading));
print();
var hdg = normdeg(heading + getprop("/sim/current-view/goal-pitch-offset-deg"));
var fgfs = sprintf("$ fgfs --aircraft=ufo --lon=%.6f --lat=%.6f --altitude=%.2f --heading=%.1f",
lon, lat, agl_ft, hdg);
print(fgfs);
}
var print_model_data = func(prop) {
print("\n\n------------------------ Selected Object -------------------------\n");
var elev = prop.getNode("elevation-ft").getValue();
printf("Path: %s", prop.getNode("path").getValue());
printf("Longitude: %.8f deg", prop.getNode("longitude-deg").getValue());
printf("Latitude: %.8f deg", prop.getNode("latitude-deg").getValue());
printf("Altitude ASL: %.4f m (%.4f ft)", elev * FT2M, elev);
printf("Heading: %.1f deg", prop.getNode("heading-deg").getValue());
printf("Pitch: %.1f deg", prop.getNode("pitch-deg").getValue());
printf("Roll: %.1f deg", prop.getNode("roll-deg").getValue());
}
# interface functions -----------------------------------------------------------------------------
var print_data = func {
var rule = "\n------------------------------------------------------------------\n";
print("\n\n");
print_ufo_data();
var data = modelmgr.get_data();
var selected = data.getChild("model", 0);
if (selected == nil) {
print(rule);
return;
}
print_model_data(selected);
print(rule);
# group all objects of a bucket
var bucket = {};
foreach (var m; data.getChildren("model")) {
var stg = m.getNode("stg-path").getValue();
var obj = m.getNode("object-line").getValue();
if (contains(bucket, stg)) {
append(bucket[stg], obj);
} else {
bucket[stg] = [obj];
}
}
foreach (var key; keys(bucket)) {
print("\n# ", key);
foreach (var obj; bucket[key]) {
print(obj);
}
}
print(rule);
}
var export_data = func {
savexml = func(name, node) {
fgcommand("savexml", props.Node.new({ "filename": name, "sourcenode": node }));
}
var tmp = "save-ufo-data";
save = props.globals.getNode(tmp, 1);
props.copy(modelmgr.get_data(), save.getNode("models", 1));
var path = getprop("/sim/fg-home") ~ "/ufo-model-export.xml";
savexml(path, save.getPath());
print("model data exported to ", path);
props.globals.removeChild(tmp);
}
# dialogs -----------------------------------------------------------------------------------------
var status_dialog = gui.Dialog.new("/sim/gui/dialogs/ufo/status/dialog", "Aircraft/ufo/Dialogs/status.xml");
var select_dialog = gui.Dialog.new("/sim/gui/dialogs/ufo/select/dialog", "Aircraft/ufo/Dialogs/select.xml");
var adjust_dialog = gui.Dialog.new("/sim/gui/dialogs/ufo/adjust/dialog", "Aircraft/ufo/Dialogs/adjust.xml");
adjust_dialog.center_sliders = func {
var ns = adjust_dialog.namespace();
if (ns != nil) {
ns.center();
}
}
# hide status line in screenshots
#
var status_restore = nil;
setlistener("/sim/signals/screenshot", func {
if (cmdarg().getBoolValue()) {
status_restore = status_dialog.is_open();
status_dialog.close();
} else {
status_restore and status_dialog.open();
}
});
# init --------------------------------------------------------------------------------------------
var KbdShift = props.globals.getNode("/devices/status/keyboard/shift");
var KbdCtrl = props.globals.getNode("/devices/status/keyboard/ctrl");
var KbdAlt = props.globals.getNode("/devices/status/keyboard/alt");
var click_lon = props.globals.getNode("/sim/input/click/longitude-deg", 1);
var click_lat = props.globals.getNode("/sim/input/click/latitude-deg", 1);
var click_elev = props.globals.getNode("/sim/input/click/elevation-m", 1);
var modellist = scan_dirs(getprop("/source"));
var modelmgr = ModelMgr.new(getprop("/cursor"));
setlistener("/sim/signals/click", func {
var lon = click_lon.getValue();
var lat = click_lat.getValue();
var elev = click_elev.getValue();
modelmgr.click(Coord.new().set_lonlat(lon, lat, elev));
});