1
0
Fork 0

Bugfix to globals.fgcommand().

New Node.setValues() method which sets whole property trees from Nasal
data.

A view.nas module, which takes over handling of the X/x zoom keys.  It
clamps the FOV to a dynamically calculated maximum corresponding to
typical human visual accuity, and pops up a pretty dialog informing
you of the new FOV.
This commit is contained in:
andy 2003-12-08 02:09:19 +00:00
parent 14ad64676d
commit 3677b96fad
4 changed files with 162 additions and 30 deletions

View file

@ -20,8 +20,8 @@ isa = func {
# tree.
#
fgcommand = func {
if(isa(arg[1], props.Node)) { _fgcommand(arg[0], arg[1]._g) }
_fgcommand(arg[0], propTree);
if(isa(arg[1], props.Node)) { arg[1] = arg[1]._g }
_fgcommand(arg[0], arg[1]);
}
##

View file

@ -30,14 +30,47 @@ Node = {
};
# Static constructor. Accepts a hash as an argument and duplicates
# its contents in the property node. ex:
# Node.new({ value : 1.0, units : "ms" });
##
# Static constructor for a Node object. Accepts a Nasal hash
# expression to initialize the object a-la setValues().
#
Node.new = func {
result = wrapNode(_new());
if(typeof(arg[0]) == "hash") {
foreach(k; keys(arg[0])) {
result.getNode(k, 1).setValue(arg[0][k]);
if(size(arg) >= 0 and typeof(arg[0]) == "hash") {
result.setValues(arg[0]);
}
return result;
}
##
# Useful utility. Sets a whole property tree from a Nasal hash
# object, such that scalars become leafs in the property tree, hashes
# become named subnodes, and vectors become indexed subnodes. This
# works recursively, so you can define whole property trees with
# syntax like:
#
# dialog = {
# name : "exit", width : 180, height : 100, modal : 0,
# text : { x : 10, y : 70, label : "Hello World!" } };
#
Node.setValues = func {
foreach(k; keys(arg[0])) { me._setChildren(k, arg[0][k]); }
}
##
# Private function to do the work of setValues().
# The first argument is a child name, the second a nasal scalar,
# vector, or hash.
#
Node._setChildren = func {
name = arg[0]; val = arg[1];
subnode = me.getNode(name, 1);
if(typeof(val) == "scalar") { subnode.setValue(val); }
elsif(typeof(val) == "hash") { subnode.setValues(val); }
elsif(typeof(val) == "vector") {
for(i=0; i<size(val); i=i+1) {
iname = name ~ "[" ~ i ~ "]";
me._setChildren(iname, val[i]);
}
}
}

117
Nasal/view.nas Normal file
View file

@ -0,0 +1,117 @@
##
## view.nas
##
## Nasal code for implementing view-specific functionality. Right
## now, it does intelligent FOV scaling in the view.increase() and
## view.decrease() handlers.
##
#
# This is a neat trick. We want these globals to be initialized at
# startup, but there is no guarantee that the props.nas module will be
# loaded yet when we are run. So set the values to nil at startup (so
# that there is a value in the lexical environment -- otherwise
# assigning them in INIT() will only make local variables),
# and then assign them from inside a timer that we set to run
# immediately *after* startup.
#
# Nifty hacks notwithstanding, this really isn't the right way to do
# this. There ought to be an "import" mechanism we can use to resolve
# dependencies between modules.
#
fovProp = nil;
screenProp = nil;
popupNode = nil;
labelNode = nil;
fovDialog = nil;
INIT = func {
print("view.INIT()");
fovProp = props.globals.getNode("/sim/current-view/field-of-view");
screenProp = props.globals.getNode("/sim/startup/xsize");
# Set up the dialog property node:
tmpl = { name : "fov", modal : 0, width : "120", height : "40",
text : { x : 10, y : 6, label : "FOV:" } };
popupNode = props.Node.new(tmpl);
text = popupNode.getNode("text", 1);
labelNode = popupNode.getNode("text/label");
fgcommand("dialog-new", popupNode);
# Cache the FGCommand argument
fovDialog = props.Node.new({ "dialog-name" : "fov" });
}
settimer(INIT, 0);
# Dynamically calculate limits so that it takes STEPS iterations to
# traverse the whole range, the maximum FOV is fixed at 120 degrees,
# and the minimum corresponds to normal maximum human visual acuity
# (~1 arc minute of resolution, although apparently people vary widely
# in this ability). Quick derivation of the math:
#
# mul^steps = max/min
# steps * ln(mul) = ln(max/min)
# mul = exp(ln(max/min) / steps)
STEPS = 40;
ACUITY = 1/60; # Maximum angle subtended by one pixel (== 1 arc minute)
max = min = mul = 0;
calcMul = func {
max = 120; # Fixed at 120 degrees
min = screenProp.getValue() * ACUITY;
mul = math.exp(math.ln(max/min) / STEPS);
}
##
# Hackish round-to-one-decimal-place function. Nasal needs a
# sprintf() interface, or something similar...
#
format = func {
val = int(arg[0]);
frac = int(10 * (arg[0] - val) + 0.5);
return val ~ "." ~ substr("" ~ frac, 0, 1);
}
##
# Handler. Increase FOV by one step
#
increase = func {
calcMul();
val = fovProp.getValue() * mul;
if(val == max) { return; }
if(val > max) { val = max }
fovProp.setDoubleValue(val);
popup(val);
}
##
# Handler. Decrease FOV by one step
#
decrease = func {
calcMul();
val = fovProp.getValue() / mul;
if(val == min) { return; }
if(val < min) { val = min }
fovProp.setDoubleValue(val);
popup(val);
}
##
# Pop up the "fov" dialog for a moment.
#
popdown = func { fgcommand("dialog-close", fovDialog); }
popup = func {
fov = arg[0];
fov = format(fov);
labelNode.setValue("FOV: " ~ fov);
# Create it, show it
popdown();
fgcommand("dialog-show", fovDialog);
# And kill it automatically after a second
settimer(popdown, 1);
}

View file

@ -557,22 +557,6 @@ calculated by adding 256 to the GLUT key value in glut.h.
</binding>
</key>
<key n="67">
<name>C</name>
<desc>scripting test</desc>
<binding>
<command>script</command>
<script>
int main ()
{
print("Longitude: ", get_property("/position/longitude-deg"), " deg\n");
print("Latitude: ", get_property("/position/latitude-deg"), " deg\n");
print("Altitude: ", get_property("/position/altitude-ft"), " ft\n");
}
</script>
</binding>
</key>
<key n="71">
<name>G</name>
<desc>Gear down.</desc>
@ -638,9 +622,8 @@ calculated by adding 256 to the GLUT key value in glut.h.
<name>X</name>
<desc>Increase field of view.</desc>
<binding>
<command>property-multiply</command>
<property>/sim/current-view/field-of-view</property>
<factor type="double">1.05</factor>
<command>nasal</command>
<script>view.increase()</script>
</binding>
</key>
@ -885,9 +868,8 @@ calculated by adding 256 to the GLUT key value in glut.h.
<name>x</name>
<desc>Decrease field of view.</desc>
<binding>
<command>property-multiply</command>
<property>/sim/current-view/field-of-view</property>
<factor type="double">0.952380952380</factor>
<command>nasal</command>
<script>view.decrease()</script>
</binding>
</key>