1
0
Fork 0
fgdata/Aircraft/ufo/ufo.nas
2006-08-26 07:32:16 +00:00

1166 lines
28 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];
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 ERAD = 6378138.12; # Earth radius (m)
var D2R = math.pi / 180;
var R2D = 180 / math.pi;
ft2m = func { arg[0] * 0.3048 }
m2ft = func { arg[0] / 0.3048 }
floor = func(v) { v < 0.0 ? -int(-v) - 1 : int(v) }
ceil = func(v) { -floor(-v) }
pow = func(v, w) { math.exp(math.ln(v) * w) }
pow2 = func(e) { e ? 2 * pow2(e - 1) : 1 }
printf = func(_...) { print(call(sprintf, _)) }
# convert [lon, lat] to [x, y, z]
#
lonlat2xyz = func(lonlat) {
var lonr = lonlat[0] * D2R;
var latr = lonlat[1] * D2R;
var cosphi = math.cos(latr);
var x = cosphi * math.cos(lonr);
var y = cosphi * math.sin(lonr);
var z = math.sin(latr);
return [x, y, z];
}
# convert [x, y, z] to [lon, lat]
#
xyz2lonlat = func(xyz) {
var x = xyz[0];
var y = xyz[1];
var z = xyz[2];
var lat = math.atan2(z, math.sqrt(x * x + y * y)) * R2D;
var lon = math.atan2(y, x) * R2D;
return [lon, lat];
}
# return squared unit distance between two [x, y, z]
# (to get meter take sqrt and multiply with Earth radius ERAD)
#
coord_dist_sq = func(xyz0, xyz1) {
var x = xyz0[0] - xyz1[0];
var y = xyz0[1] - xyz1[1];
var z = xyz0[2] - xyz1[2];
return x * x + y * y + z * z;
}
# sort vector of strings (bubblesort)
#
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
#
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)
#
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
#
normdeg = func(angle) {
while (angle < 0) {
angle += 360;
}
while (angle >= 360) {
angle -= 360;
}
angle;
}
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;
}
}
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;
}
format = func(lon, lat) {
sprintf("%s%03d%s%02d", lon < 0 ? "w" : "e", abs(lon), lat < 0 ? "s" : "n", abs(lat));
}
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";
}
# cursor ------------------------------------------------------------------------------------------
## manages one adjustable value for the Adjust class
# (applies slider offsets etc.)
#
Value = {
new : func(baseN, name, init) {
var m = { parents: [Value] };
m.lastOffs = 0;
m.init = init;
# offset node; fed by the dialog slider
m.inOffsN = baseN.getNode("offsets/" ~ name, 1);
m.inOffsN.setValue(m.lastOffs);
# live number property
m.outN = baseN.getNode("adjust/" ~ name, 1);
m.outN.setDoubleValue(init);
m.listener = setlistener(m.inOffsN, func { m.update() });
return m;
},
del : func {
removelistener(me.listener);
},
reset : func {
me.center();
me.outN.setValue(me.init);
},
center : func {
me.update();
me.inOffsN.setValue(me.lastOffs = 0);
},
update : func {
var offs = me.inOffsN.getValue();
me.outN.setValue(me.outN.getValue() + offs - me.lastOffs);
me.lastOffs = offs;
},
set : func(v) {
me.center();
me.outN.setValue(v);
},
get : func {
me.outN.getValue();
},
add : func(v) {
me.center();
me.outN.setValue(me.outN.getValue() + v);
},
};
## manages a set of adjustable values and is the entry point for data into
# the dynamic model
#
Adjust = {
new : func(prop) {
var m = { parents: [Adjust] };
m.node = props.globals.getNode(prop, 1);
m.val = {
lon: Value.new(m.node, "longitude-deg", 0),
lat: Value.new(m.node, "latitude-deg", 0),
elev: Value.new(m.node, "elevation-ft", -10000),
hdg: Value.new(m.node, "heading-deg", 0),
pitch: Value.new(m.node, "pitch-deg", 0),
roll: Value.new(m.node, "roll-deg", 0),
};
m.stk_hdgN = m.node.getNode("sticky-heading", 1);
m.stk_orientN = m.node.getNode("sticky-orientation", 1);
m.stk_hdgN.setBoolValue(0);
m.stk_orientN.setBoolValue(0);
m.legendN = m.node.getNode("legend", 1);
return m;
},
del : func {
foreach (var v; keys(me.val)) {
me.val[v].del();
}
},
offsetNode : func(which) {
me.val[which].inOffsN;
},
outNode : func(which) {
me.val[which].outN;
},
get : func(which) {
me.val[which].get();
},
set : func(which, value) {
me.val[which].set(value);
},
setall : func(legend, lon, lat, elev, hdg = nil, pitch = nil, roll = nil) {
me.legendN.setValue(legend);
me.val["lon"].set(lon);
me.val["lat"].set(lat);
me.val["elev"].set(elev);
if (hdg != nil) {
me.val["hdg"].set(hdg);
} elsif (!me.stk_hdgN.getBoolValue()) {
me.val["hdg"].reset();
}
if (pitch != nil) {
me.val["pitch"].set(pitch);
} elsif (!me.stk_orientN.getBoolValue()) {
me.val["pitch"].reset();
}
if (roll != nil) {
me.val["roll"].set(roll);
} elsif (!me.stk_orientN.getBoolValue()) {
me.val["roll"].reset();
}
},
reset : func {
foreach (var v; keys(me.val)) {
me.val[v].reset();
}
},
step : func(which, step) {
me.val[which].add(step);
},
upright : func {
me.val["pitch"].set(0);
me.val["roll"].set(0);
},
orient : func {
me.val["hdg"].set(0);
},
center_sliders : func {
foreach (var v; keys(me.val)) {
me.val[v].center();
}
},
};
## base class for the dynamic and static models
#
Model = {
# searches first free slot and sets path
new : func(path) {
var m = { parents: [Model] };
var models = props.globals.getNode("/models", 1);
for (var i = 0; 42; i += 1) {
if (models.getChild("model", i, 0) == nil) {
m.node = models.getChild("model", i, 1);
break;
}
}
m.path = path;
m.node.getNode("path", 1).setValue(m.path);
m.legend = nil;
return m;
},
# signal modelmgr.cxx to load model
load : func {
me.node.getNode("load", 1).setValue(1);
me.node.removeChildren("load");
},
add_derived_props : func(node) {
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 type = nil;
var spec = "";
if (path == "Aircraft/ufo/Models/sign.ac") {
type = "OBJECT_SIGN";
var legend = me.legend != "" ? me.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 = ft2m(elev);
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(me.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)
}
};
## class for static models (see $FG_ROOT/Docs/README.scenery)
#
Static = {
new : func(path, legend, lon, lat, elev, hdg, pitch, roll) {
var m = Model.new(path);
m.parents = [Static, Model];
m.node.getNode("longitude-deg", 1).setDoubleValue(m.lon = lon);
m.node.getNode("latitude-deg", 1).setDoubleValue(m.lat = lat);
m.node.getNode("elevation-ft", 1).setDoubleValue(m.elev = elev);
m.node.getNode("heading-deg", 1).setDoubleValue(m.hdg = hdg);
m.node.getNode("pitch-deg", 1).setDoubleValue(m.pitch = pitch);
m.node.getNode("roll-deg", 1).setDoubleValue(m.roll = roll);
m.legend = legend;
m.load();
return m;
},
del : func {
var parent = me.node.getParent();
if (parent != nil) {
parent.removeChild(me.node.getName(), me.node.getIndex());
}
},
distance_from : func(xyz) {
return coord_dist_sq(xyz, lonlat2xyz([me.lon, me.lat]));
},
get_data : func {
var n = props.Node.new();
props.copy(me.node, n);
me.add_derived_props(n);
return n;
},
};
## class for dynamic models. Only one object is dynamic at a time -- the selected object.
#
Dynamic = {
new : func(path, legend, lon, lat, elev, hdg = nil, pitch = nil, roll = nil) {
var m = Model.new(path);
m.parents = [Dynamic, Model];
adjust.setall(legend, lon, lat, elev, hdg, pitch, roll);
m.node.getNode("longitude-deg-prop", 1).setValue(adjust.outNode("lon").getPath());
m.node.getNode("latitude-deg-prop", 1).setValue(adjust.outNode("lat").getPath());
m.node.getNode("elevation-ft-prop", 1).setValue(adjust.outNode("elev").getPath());
m.node.getNode("heading-deg-prop", 1).setValue(adjust.outNode("hdg").getPath());
m.node.getNode("pitch-deg-prop", 1).setValue(adjust.outNode("pitch").getPath());
m.node.getNode("roll-deg-prop", 1).setValue(adjust.outNode("roll").getPath());
m.load();
m.flash_count = 0;
m.visible = 1;
return m;
},
del : func {
me.flash(0);
var parent = me.node.getParent();
if (parent != nil) {
parent.removeChild(me.node.getName(), me.node.getIndex());
}
},
make_static : func {
me.del();
return Static.new(me.path, adjust.legendN.getValue(),
adjust.get("lon"), adjust.get("lat"), adjust.get("elev"),
adjust.get("hdg"), adjust.get("pitch"), adjust.get("roll"));
},
distance_from : func(xyz) {
var lon = adjust.get("lon");
var lat = adjust.get("lat");
return coord_dist_sq(xyz, lonlat2xyz([lon, lat]));
},
get_data : func {
me.flash(0);
var n = props.Node.new();
n.getNode("path", 1).setValue(me.path);
props.copy(props.globals.getNode("/data/adjust"), n);
me.legend = adjust.legendN.getValue();
me.add_derived_props(n);
return n;
},
flash : func(v) {
me.flash_count = v;
me._flash_();
},
_flash_ : func {
if (!me.visible) {
adjust.set("elev", adjust.get("elev") + 10000);
me.visible = 1;
} elsif (me.flash_count) {
adjust.set("elev", adjust.get("elev") - 10000);
me.visible = 0;
}
if (me.flash_count) {
me.flash_count -= 1;
settimer(func { me._flash_() }, 0.3);
}
},
};
Static.make_dynamic = func {
me.del();
return Dynamic.new(me.path, me.legend, me.lon, me.lat, me.elev, me.hdg, me.pitch, me.roll);
};
## manages one dynamic and several static objects
#
ModelMgr = {
new : func(path) {
var m = { parents: [ModelMgr] };
var click = props.globals.getNode("/sim/input/click", 1);
m.lonN = click.getNode("longitude-deg", 1);
m.latN = click.getNode("latitude-deg", 1);
m.elevN = click.getNode("elevation-ft", 1);
m.lonN.setValue(0);
m.latN.setValue(0);
m.modelpath = path;
m.dynamic = nil;
m.static = [];
m.import();
return m;
},
click : func {
showStatusDialog(1);
if (KbdCtrl.getBoolValue()) {
me.select();
} elsif (KbdShift.getBoolValue()) {
me.remove_selected();
var hdg = adjust.stk_hdgN.getBoolValue();
var orient = adjust.stk_orientN.getBoolValue();
adjust.stk_hdgN.setBoolValue(1);
adjust.stk_orientN.setBoolValue(1);
me.add_instance();
adjust.stk_hdgN.setBoolValue(hdg);
adjust.stk_orientN.setBoolValue(orient);
} else {
me.add_instance();
}
},
add_instance : func {
if (me.dynamic != nil) {
append(me.static, me.dynamic.make_static());
}
me.dynamic = Dynamic.new(me.modelpath, "", me.lonN.getValue(), me.latN.getValue(),
me.elevN.getValue());
me.display_status(me.modelpath);
},
select : func {
var click_xyz = lonlat2xyz([me.lonN.getValue(), me.latN.getValue()]);
var min_dist = me.dynamic != nil ? me.dynamic.distance_from(click_xyz) : 1000000;
var nearest = nil;
# find nearest static object
forindex (var i; me.static) {
var dist = me.static[i].distance_from(click_xyz);
if (dist < min_dist) {
min_dist = dist;
nearest = i;
}
}
if (nearest != nil) {
# swap dynamic with nearest static
if (me.dynamic != nil) {
var st = me.dynamic.make_static();
me.dynamic = me.static[nearest].make_dynamic();
me.static[nearest] = st;
# actively selected: use this model type
me.modelpath = me.dynamic.path;
} else {
me.dynamic = me.static[nearest].make_dynamic();
var left = subvec(me.static, 0, nearest);
if (nearest + 1 < size(me.static)) {
foreach (var v; subvec(me.static, nearest + 1)) {
append(left, v);
}
}
me.static = left;
}
}
# last object removed?
if (me.dynamic == nil) {
adjust.legendN.setValue("");
} else {
me.dynamic.flash(6);
}
me.display_status(me.modelpath);
},
remove_selected : func {
if (me.dynamic != nil) {
me.dynamic.del();
me.dynamic = nil;
}
},
setmodelpath : func(path) {
me.modelpath = path;
me.display_status(path);
},
display_status : func(p) {
var count = (me.dynamic != nil) + size(me.static);
setprop("/sim/model/ufo/status", "(" ~ count ~ ") " ~ p);
},
get_data : func {
var n = props.Node.new();
if (me.dynamic != nil) {
props.copy(me.dynamic.get_data(), n.getChild("model", 0, 1));
}
forindex (var i; me.static) {
props.copy(me.static[i].get_data(), n.getChild("model", i + 1, 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.setmodelpath(modellist[i]);
if (me.dynamic != nil) {
var st = me.dynamic.make_static();
st.path = me.modelpath;
me.dynamic.del();
me.dynamic = st.make_dynamic();
}
},
import : func {
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());
append(me.static, Static.new(
m.getNode("path").getValue(),
m.getNode("legend").getValue(),
m.getNode("longitude-deg").getValue(),
m.getNode("latitude-deg").getValue(),
m.getNode("elevation-ft").getValue(),
m.getNode("heading-deg").getValue(),
m.getNode("pitch-deg").getValue(),
m.getNode("roll-deg").getValue()));
}
}
me.select();
},
};
incElevator = controls.incElevator;
controls.incElevator = func(step, apstep) {
if (KbdCtrl.getBoolValue()) {
modelmgr.cycle(step > 0 ? 1 : -1);
} else {
incElevator(step, apstep);
}
}
var lastXYZ = lonlat2xyz([getprop("/position/longitude-deg"), getprop("/position/latitude-deg")]);
var lastElev = 0;
printDistance = func {
# print distance to last cursor coordinates (horizontal distance
# doesn't consider elevation and is rather imprecise)
var lon = getprop("/sim/input/click/longitude-deg");
var lat = getprop("/sim/input/click/latitude-deg");
var elev = getprop("/sim/input/click/elevation-ft");
var newXYZ = lonlat2xyz([lon, lat]);
var hdist = math.sqrt(coord_dist_sq(lastXYZ, newXYZ) * ERAD);
var vdist = ft2m(elev - lastElev);
var s = hdist < 4 ? sprintf("%.1f m HOR, %.1f m VERT", hdist * 1000, vdist)
: sprintf("%.1f km HOR, %.1f m VERT", hdist, vdist);
screen.log.write(s);
lastXYZ = newXYZ;
lastElev = elev;
}
scanDirs = 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);
}
printUFOData = 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 - m2ft(elev_m);
printf("Longitude: %.8f deg", lon);
printf("Latitude: %.8f deg", lat);
printf("Altitude ASL: %.4f m (%.4f ft)", ft2m(alt_ft), alt_ft);
printf("Altitude AGL: %.4f m (%.4f ft)", ft2m(agl_ft), agl_ft);
printf("Heading: %.1f deg", normdeg(heading));
printf("Ground Elev: %.4f m (%.4f ft)", elev_m, m2ft(elev_m));
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);
}
printModelData = 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)", ft2m(elev), 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 -----------------------------------------------------------------------------
printData = func {
var rule = "\n------------------------------------------------------------------\n";
print("\n\n");
printUFOData();
var data = modelmgr.get_data();
var selected = data.getChild("model", 0);
if (selected == nil) {
print(rule);
return;
}
printModelData(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);
}
exportData = 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);
}
removeSelectedModel = func { modelmgr.remove_selected(); modelmgr.select(); }
# init --------------------------------------------------------------------------------------------
var modellist = nil;
var adjust = nil;
var modelmgr = nil;
var KbdShift = nil;
var KbdCtrl = nil;
var KbdAlt = nil;
settimer(func {
KbdShift = props.globals.getNode("/devices/status/keyboard/shift");
KbdCtrl = props.globals.getNode("/devices/status/keyboard/ctrl");
KbdAlt = props.globals.getNode("/devices/status/keyboard/alt");
modellist = scanDirs(getprop("/source"));
adjust = Adjust.new("/data");
modelmgr = ModelMgr.new(getprop("/cursor"));
setlistener("/sim/signals/click", func { modelmgr.click() });
#setlistener("/sim/signals/click", printDistance);
if (modelmgr.modelpath != "Aircraft/ufo/Models/cursor.ac") {
showStatusDialog(1);
}
}, 1);
# dialogs -----------------------------------------------------------------------------------------
var dialog = {};
showModelSelectDialog = func {
name = "ufo-model-select-dialog";
if (contains(dialog, name)) {
closeModelSelectDialog();
return;
}
var title = 'Select Model';
dialog[name] = gui.Widget.new();
dialog[name].set("layout", "vbox");
dialog[name].set("name", name);
dialog[name].set("x", -20);
dialog[name].set("pref-width", 600);
# "window" titlebar
titlebar = dialog[name].addChild("group");
titlebar.set("layout", "hbox");
titlebar.addChild("empty").set("stretch", 1);
titlebar.addChild("text").set("label", title);
titlebar.addChild("empty").set("stretch", 1);
dialog[name].addChild("hrule").addChild("dummy");
w = titlebar.addChild("button");
w.set("pref-width", 16);
w.set("pref-height", 16);
w.set("legend", "");
w.set("default", 1);
w.set("keynum", 27);
w.set("border", 1);
w.setBinding("nasal", "ufo.closeModelSelectDialog()");
w = dialog[name].addChild("list");
w.set("halign", "fill");
w.set("pref-height", 300);
w.set("property", "/cursor");
forindex (var i; modellist) {
w.prop().getChild("value", i, 1).setValue(modellist[i]);
}
w.setBinding("dialog-apply");
w.setBinding("nasal", "ufo.modelmgr.setmodelpath(getprop('/cursor'))");
fgcommand("dialog-new", dialog[name].prop());
gui.showDialog(name);
}
closeModelSelectDialog = func {
var name = "ufo-model-select-dialog";
var dlg = props.Node.new({"dialog-name": name});
fgcommand("dialog-apply", dlg);
fgcommand("dialog-close", dlg);
delete(dialog, name);
}
showModelAdjustDialog = func {
name = "ufo-cursor-dialog";
if (contains(dialog, name)) {
fgcommand("dialog-close", props.Node.new({ "dialog-name" : name }));
delete(dialog, name);
return;
}
adjust.center_sliders();
dialog[name] = gui.Widget.new();
dialog[name].set("layout", "vbox");
dialog[name].set("name", name);
dialog[name].set("x", -20);
dialog[name].set("y", -20);
# "window" titlebar
titlebar = dialog[name].addChild("group");
titlebar.set("layout", "hbox");
titlebar.addChild("empty").set("stretch", 1);
titlebar.addChild("text").set("label", "Adjust Model");
titlebar.addChild("empty").set("stretch", 1);
dialog[name].addChild("hrule").addChild("dummy");
w = titlebar.addChild("button");
w.set("pref-width", 16);
w.set("pref-height", 16);
w.set("legend", "");
w.set("default", 1);
w.set("keynum", 27);
w.set("border", 1);
w.setBinding("nasal", "delete(ufo.dialog, \"" ~ name ~ "\")");
w.setBinding("dialog-close");
slider = func(legend, col, coarse, fine) {
group = dialog[name].addChild("group");
group.set("layout", "hbox");
group.set("default-padding", 0);
button = func(leg, step) {
b = group.addChild("button");
b.set("legend", leg);
b.set("pref-width", 22);
b.set("pref-height", 22);
b.set("live", 1);
b.setBinding("nasal", 'ufo.adjust.step("'~legend~'", '~step~')');
return b;
}
cl = button("<<", -coarse);
fl = button("<", -fine);
s = group.addChild("slider");
s.set("property", adjust.offsetNode(legend).getPath());
s.set("legend", legend);
s.set("pref-width", 250);
s.set("live", 1);
s.set("min", -1 * fine);
s.set("max", 1 * fine);
s.setColor(col[0], col[1], col[2]);
s.setBinding("dialog-apply");
fr = button(">", fine);
cr = button(">>", coarse);
}
slider("lon", [1.0, 0.6, 0.6], 0.0004, 0.00004);
slider("lat", [0.6, 1.0, 0.6], 0.0002, 0.00002);
slider("elev", [0.6, 0.6, 1.0], 10, 2);
slider("hdg", [1.0, 1.0, 0.6], 36, 6);
slider("pitch", [1.0, 0.6, 1.0], 36, 6);
slider("roll", [0.6, 1.0, 1.0], 36, 6);
g = dialog[name].addChild("group");
g.set("layout", "hbox");
w = g.addChild("text");
w.set("halign", "left");
w.set("label", "Heading ");
w = g.addChild("text");
w.set("halign", "center");
w.set("label", "Sliders");
w = g.addChild("text");
w.set("halign", "right");
w.set("label", "Orientation");
g = dialog[name].addChild("group");
g.set("layout", "hbox");
g.set("default-padding", 2);
var wide = 60;
var narrow = 55;
w = g.addChild("button");
w.set("halign", "right");
w.set("legend", "Reset");
w.set("pref-height", 22);
w.set("pref-width", wide);
w.setBinding("nasal", "ufo.adjust.orient()");
w = g.addChild("button");
w.set("legend", "Sticky");
w.set("one-shot", 0);
w.set("pref-height", 22);
w.set("pref-width", narrow);
w.set("live", 1);
w.set("property", adjust.stk_hdgN.getPath());
w.setBinding("dialog-apply");
g.addChild("empty").set("stretch", 1);
w = g.addChild("button");
w.set("halign", "center");
w.set("legend", "Center");
w.set("pref-height", 22);
w.set("pref-width", wide);
w.setBinding("nasal", "ufo.adjust.center_sliders()");
g.addChild("empty").set("stretch", 1);
w = g.addChild("button");
w.set("legend", "Sticky");
w.set("one-shot", 0);
w.set("pref-height", 22);
w.set("pref-width", narrow);
w.set("live", 1);
w.set("property", adjust.stk_orientN.getPath());
w.setBinding("dialog-apply");
w = g.addChild("button");
w.set("halign", "left");
w.set("legend", "Reset");
w.set("pref-height", 22);
w.set("pref-width", wide);
w.setBinding("nasal", "ufo.adjust.upright()");
fgcommand("dialog-new", dialog[name].prop());
gui.showDialog(name);
}
showStatusDialog = func(v = nil) {
name = "ufo-status-dialog";
var is_open = contains(dialog, name);
if (v == nil) {
return is_open;
} elsif (!v) {
if (is_open) {
fgcommand("dialog-close", props.Node.new({ "dialog-name" : name }));
delete(dialog, name);
}
return 0;
}
dialog[name] = gui.Widget.new();
dialog[name].set("layout", "vbox");
dialog[name].set("name", name);
dialog[name].set("x", 8);
dialog[name].set("y", 8);
dialog[name].set("pref-width", 300);
dialog[name].set("default-padding", 0);
dialog[name].setColor(0, 0, 0, 0);
dialog[name].setFont("HELVETICA_12");
# legend input field
w = dialog[name].addChild("input");
w.set("halign", "left");
w.set("pref-width", 300);
w.set("live", 1);
w.set("property", adjust.legendN.getPath());
w.setColor(0, 0, 0, 0);
w.prop().setValues({"color-legend": {red:1, green:1, blue:1, alpha:1}});
w.prop().setValues({"color-background": {red:0, green:0, blue:0, alpha:0}});
w.prop().setValues({"color-highlight": {red:0, green:0, blue:0, alpha:0}});
w.setBinding("dialog-apply");
# current object
g = dialog[name].addChild("group");
g.set("layout", "vbox");
g.set("default-padding", 2);
w = g.addChild("text");
w.set("pref-width", 0);
w.set("halign", "left");
w.set("property", "/sim/model/ufo/status");
w.set("live", 1);
w.setColor(0.6, 1, 0.6, 1);
fgcommand("dialog-new", dialog[name].prop());
gui.showDialog(name);
return 1;
}
var stat_dlg = nil;
setlistener("/sim/signals/screenshot", func {
if (cmdarg().getBoolValue()) {
stat_dlg = showStatusDialog();
showStatusDialog(0);
} else {
showStatusDialog(stat_dlg);
}
});