diff --git a/Aircraft/Generic/Logos/pumpkin.png b/Aircraft/Generic/Logos/pumpkin.png
new file mode 100644
index 000000000..98edaa7b7
Binary files /dev/null and b/Aircraft/Generic/Logos/pumpkin.png differ
diff --git a/Aircraft/Generic/Logos/pumpkin.xml b/Aircraft/Generic/Logos/pumpkin.xml
new file mode 100644
index 000000000..fe685794d
--- /dev/null
+++ b/Aircraft/Generic/Logos/pumpkin.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+ Pumpkin
+ ../../Generic/Logos/pumpkin.png
+
+
+
+
diff --git a/Aircraft/Generic/WalkView/walk-view-keys.xml b/Aircraft/Generic/WalkView/walk-view-keys.xml
new file mode 100644
index 000000000..5471e9026
--- /dev/null
+++ b/Aircraft/Generic/WalkView/walk-view-keys.xml
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
+
+ W
+ Walk view: Run forward.
+
+ nasal
+
+
+
+
+ nasal
+
+
+
+
+
+
+ w
+ Walk view: Walk forward.
+
+ nasal
+
+
+
+
+ nasal
+
+
+
+
+
+
+ d
+ Walk view: Side step right.
+ true
+
+ nasal
+
+
+
+
+ nasal
+
+
+
+
+
+
+ D
+ Walk view: Fast side step right.
+ true
+
+ nasal
+
+
+
+
+ nasal
+
+
+
+
+
+
+ a
+ Walk view: Side step left.
+ true
+
+ nasal
+
+
+
+
+ nasal
+
+
+
+
+
+ A
+ Walk view: Fast side step left.
+ true
+
+ nasal
+
+
+
+
+ nasal
+
+
+
+
+
+
+ s
+ Walk view: Walk backwards.
+
+ nasal
+
+
+
+
+ nasal
+
+
+
+
+
+
+
+
diff --git a/Aircraft/Generic/WalkView/walkview.nas b/Aircraft/Generic/WalkView/walkview.nas
index cecd081c6..b7bad57c1 100644
--- a/Aircraft/Generic/WalkView/walkview.nas
+++ b/Aircraft/Generic/WalkView/walkview.nas
@@ -83,7 +83,8 @@ var active_walker = func {
# speed ... speed in m/sec : double
#
# set_pos(pos)
-# get_pos() : position
+# pos ... position in meter : [double, double, double]
+# get_pos() : position ([meter, meter, meter])
#
# set_eye_height(h)
# get_eye_height() : int (meter)
@@ -97,13 +98,15 @@ var active_walker = func {
# [19.5, 0.3, -8.85]);
# var walker = walkview.walker.new("Passenger View", constraint);
#
+# See Aircraft/Nordstern, Aircraft/Short_Empire and Aircraft/ZLT-NT
+# for working examples of walk views.
+#
# NOTES:
# Currently there can only be one view manager per view so the
# walk view should not have any other view manager.
-#
-var walker = {
+var Walker = {
new : func (view_name, constraints = nil, managers = nil) {
- var obj = { parents : [walker] };
+ var obj = { parents : [Walker] };
obj.view = view.views[view.indexof(view_name)];
obj.constraints = constraints;
obj.managers = managers;
@@ -124,7 +127,7 @@ var walker = {
view.manager.register(view_name, obj);
walkers[obj.view.getPath()] = obj;
- debug.dump(obj);
+ #debug.dump(obj);
return obj;
},
active : func {
@@ -216,13 +219,52 @@ var walker = {
###############################################################################
# Constraint classes. Determines where the view can walk.
+#
+
+# Convenience functions.
+
+# Build a UnionConstraint hierarchy from a list of constraints.
+# cs - list of constraints : [constraint]
+var makeUnionConstraint = func (cs) {
+ if (size(cs) < 2) return cs[0];
+
+ var ret = cs[0];
+ for (var i = 1; i < size(cs); i += 1) {
+ ret = UnionConstraint.new(ret, cs[i]);
+ }
+ return ret;
+}
+
+# Build a UnionConstraint hierachy that represents a polyline path
+# with a certain width. Each internal point gets a circular surface.
+# points - list of points : [position] ([[meter, meter, meter]])
+# width - width of the path : length (meter)
+# round_ends - put a circle also on the first and last points : bool
+var makePolylinePath = func (points, width, round_ends = 0) {
+ if (size(points) < 2) return nil;
+ var ret = LinePlane.new(points[0], points[1], width);
+ if (round_ends) {
+ ret = UnionConstraint.new(line,
+ CircularXYSurface.new(points[0], width/2));
+ }
+ for (var i = 2; i < size(points); i += 1) {
+ var line = LinePlane.new(points[i-1], points[i], width);
+ if (i + 1 < size(points) or round_ends) {
+ line = UnionConstraint.new
+ (line,
+ CircularXYSurface.new(points[i], width/2));
+ }
+ ret = UnionConstraint.new(line, ret);
+ }
+ return ret;
+}
# The union of two constraints.
# c1, c2 - the constraints : constraint
# NOTE: Assumes that the constraints are convex.
-var unionConstraint = {
+var UnionConstraint = {
new : func (c1, c2) {
- var obj = { parents : [unionConstraint] };
+ var obj = { parents : [UnionConstraint] };
obj.c1 = c1;
obj.c2 = c2;
return obj;
@@ -244,40 +286,82 @@ var unionConstraint = {
}
};
-# Build a unionConstraint hierarchy from a list of constraints.
-# cs - list of constraints : [constraint]
-var makeUnionConstraint = func (cs) {
- if (size(cs) < 2) return cs[0];
-
- var ret = cs[0];
- for (var i = 1; i < size(cs); i += 1) {
- ret = unionConstraint.new(ret, cs[i]);
- }
- return ret;
-}
+# Rectangular plane defined by a straight line and a width.
+# The line is extruded horizontally on each side by width/2 into a
+# planar surface.
+# p1, p2 - the line endpoints. : position ([meter, meter, meter])
+# width - total width of the plane. : length (meter)
+var LinePlane = {
+ new : func (p1, p2, width) {
+ var obj = { parents : [LinePlane] };
+ obj.p1 = p1;
+ obj.p2 = p2;
+ obj.halfwidth = width/2;
+ obj.length = vec2.length(vec2.sub(p2, p1));
+ obj.e1 = vec2.normalize(vec2.sub(p2, p1));
+ obj.e2 = [obj.e1[1], -obj.e1[0]];
+ obj.k = (p2[2] - p1[2]) / obj.length;
-# Mostly aligned plane sloping along the X axis.
-# minp - the X,Y minimum point : position (meter)
-# maxp - the X,Y maximum point : position (meter)
-var slopingYAlignedPlane = {
- new : func (minp, maxp) {
- var obj = { parents : [slopingYAlignedPlane] };
- obj.minp = minp;
- obj.maxp = maxp;
- obj.kxz = (maxp[2] - minp[2])/(maxp[0] - minp[0]);
return obj;
},
constrain : func (pos) {
- var p = [pos[0], pos[1], pos[2]];
- if (pos[0] < me.minp[0]) p[0] = me.minp[0];
- if (pos[0] > me.maxp[0]) p[0] = me.maxp[0];
- if (pos[1] < me.minp[1]) p[1] = me.minp[1];
- if (pos[1] > me.maxp[1]) p[1] = me.maxp[1];
- p[2] = me.minp[2] + me.kxz * (pos[0] - me.minp[0]);
+ var p = [pos[0], pos[1], pos[2]];
+ var pXY = vec2.sub(pos, me.p1);
+ var along = vec2.dot(pXY, me.e1);
+ var across = vec2.dot(pXY, me.e2);
+
+ var along2 = max(0, min(along, me.length));
+ var across2 = max(-me.halfwidth, min(across, me.halfwidth));
+ if (along2 != along or across2 != across) {
+ # Compute new XY position.
+ var t = vec2.add(vec2.mul(along2, me.e1), vec2.mul(across2, me.e2));
+ p[0] = me.p1[0] + t[0];
+ p[1] = me.p1[1] + t[1];
+ }
+
+ # Compute Z positition.
+ p[2] = me.p1[2] + me.k * along2;
+ return p;
+ }
+};
+
+# Circular surface aligned with the XY plane
+# center - the center point : position ([meter, meter, meter])
+# radius - radius in the XY plane : length (meter)
+var CircularXYSurface = {
+ new : func (center, radius) {
+ var obj = { parents : [CircularXYSurface] };
+ obj.center = center;
+ obj.radius = radius;
+
+ return obj;
+ },
+ constrain : func (pos) {
+ var p = [pos[0], pos[1], me.center[2]];
+ var pXY = vec2.sub(pos, me.center);
+ var lXY = vec2.length(pXY);
+
+ if (lXY > me.radius) {
+ var t = vec2.add(me.center, vec2.mul(me.radius/lXY, pXY));
+ p[0] = t[0];
+ p[1] = t[1];
+ }
return p;
},
};
+# Mostly aligned plane sloping along the X axis.
+# NOTE: Obsolete. Use linePlane instead.
+# minp - the X,Y minimum point : position ([meter, meter, meter])
+# maxp - the X,Y maximum point : position ([meter, meter, meter])
+var SlopingYAlignedPlane = {
+ new : func (minp, maxp) {
+ return LinePlane.new([minp[0], (minp[1] + maxp[1])/2, minp[2]],
+ [maxp[0], (minp[1] + maxp[1])/2, maxp[2]],
+ (maxp[1] - minp[1]));
+ }
+};
+
# Action constraint
# Triggers an action when entering or exiting the constraint.
# constraint - the area in question : constraint
@@ -285,9 +369,9 @@ var slopingYAlignedPlane = {
# on_exit(x, y) - function that is called when the walker leaves the area.
# x and y are <0, 0 or >0 depending on in which direction(s)
# the walker left the constraint.
-var actionConstraint = {
+var ActionConstraint = {
new : func (constraint, on_enter = nil, on_exit = nil) {
- var obj = { parents : [actionConstraint] };
+ var obj = { parents : [ActionConstraint] };
obj.constraint = constraint;
obj.on_enter = on_enter;
obj.on_exit = on_exit;
@@ -366,3 +450,33 @@ var closerXY = func (pos, p1, p2) {
var l2 = [p2[0] - pos[0], p2[1] - pos[1]];
return (l1[0]*l1[0] + l1[1]*l1[1]) - (l2[0]*l2[0] + l2[1]*l2[1]);
}
+
+var max = func (a, b) {
+ return b > a ? b : a;
+}
+var min = func (a, b) {
+ return a > b ? b : a;
+}
+
+# 2D vector math.
+var vec2 = {
+ add : func (a, b) {
+ return [a[0] + b[0], a[1] + b[1]];
+ },
+ sub : func (a, b) {
+ return [a[0] - b[0], a[1] - b[1]];
+ },
+ mul : func (k, a) {
+ return [k * a[0], k * a[1]];
+ },
+ length : func (a) {
+ return math.sqrt(a[0]*a[0] + a[1]*a[1]);
+ },
+ dot : func (a, b) {
+ return a[0]*b[0] + a[1]*b[1];
+ },
+ normalize : func (a) {
+ var s = 1/vec2.length(a);
+ return [s * a[0], s * a[1]];
+ }
+}
diff --git a/gui/dialogs/location-in-air.xml b/gui/dialogs/location-in-air.xml
index 4e2fa953a..ce16f3fae 100644
--- a/gui/dialogs/location-in-air.xml
+++ b/gui/dialogs/location-in-air.xml
@@ -261,7 +261,7 @@
nasal