I am always looking for ways to enrich the interactions between user and computer. In studying mathematics, physics, and economics, students are asked to graph functions. This is a complex mental activity, integrating many pieces of information into a global picture.
In on-line learning, however, graphing is hard to implement. Most often, students are shown several graphs and asked to choose the correct one. This is a poor substitute for actually creating the graph yourself. Outside of school, we don't live in a multiple-choice world. The question is, how can a program "read" a student's graph, and "understand" it sufficiently to provide useful feedback?
This Flash movie illustrates one approach to interacting with a user-drawn graph. A different approach is used in the still-unfinished rational-function sketching tool.
This biker has also been integrated into a birthday card. Everything in that card is controlled by ActionScript, with the exception of the biker's feet pedaling. The only part that requires hand-work is filling in some of the spaces between letters with invisible content, to keep the biker from falling into the cracks.
/*************************************************************************\
Actionscript 1.0 code for aligning a "bike" movieclip with the visible
content of a "ground" movieclip, including dynamically drawn content.
© 2004 Michael Kantor.
You are free to re-use this code for any purpose, or
develop other code based on it. Acknowledgement, and a link
to flashgizmo.com, are appreciated but not required.
\**************************************************************************/
var mousing; // boolean. true when using mouse
ground.swapDepths(10);
var st = 10; // step size (speed)
var deg = Math.PI / 180;
// we need the sine and cosine of 5 degrees, so save them here
var s5 = Math.sin(5 * Math.PI / 180);
var c5 = Math.cos(5 * Math.PI / 180);
rideOnLine = function() {
var y, nx, n;
var k = Key.getCode();
if((k != Key.LEFT) && (k != Key.RIGHT /*&& k != Key.UP*/) && !mousing) return;
var isKey = Key.isDown(Key.LEFT) || Key.isDown(Key.RIGHT);
if (isKey) { // if keys are down, direction is determined by which key
if (Key.isDown(Key.LEFT)) {
this._xscale = -100; // bike points to the left
s5 = -Math.abs(s5); // change rotation for sensing
}
if (Key.isDown(Key.RIGHT)) {
this._xscale = 100;
s5 = Math.abs(s5);
}
var k = Key.getCode();
// move by step amount st in direction bike is pointing
if (k == Key.RIGHT) {
this._x += st * Math.cos(this._rotation * deg);
this._y += st * Math.sin(this._rotation * deg);
}
else if (k == Key.LEFT) {
this._x -= st * Math.cos(this._rotation * deg);
this._y -= st * Math.sin(this._rotation * deg);
}
// set up starting location for sensing the ground
y = this._y - 50;
x = this._x;
} else { // we're draggin with the mouse
if ((this._x > _root._xmouse && this._xscale > 0) || (this._x < _root._xmouse && this._xscale < 0)) {
// find direction by sign of x difference with mouse
this._xscale *= -1; // point in the right direction
s5 *= -1; // set sensing rotation
}
// set up starting location for sensing the ground
x = _root._xmouse;
y = _root._ymouse;
//if(x != this._x) this.play(); else this.stop();
if(x > Stage.width - 5) x = Stage.width - 5;
if(x < 5) x = 5;
this._x = x;
/****************************************************************\
Ground-detection process, part I. From the starting (x, y), we
increment y until we either hit the ground or leave the stage.
This gives the point where the rear wheel hits the ground.
\******************************************************************/
while(!ground.hitTest(this._x , ++y, true) && y < Stage.height);
this._y = y;
if (y == Stage.height) { // on window edge
x = this._x + 30 * this._xscale / 100;
this._rotation = 0;
} else { // not on bottom window edge, so need to find tilt
/****************************************************************\
Ground-detection process, part II. We move 30 pixels straight up
from the rear wheel's contact point. Then we sweep out an arc of
radius 30, moving down and toward the front of the bike, until we
hit the ground. That determines where the front wheel goes.
Basically, we pop a wheelie and then come down. The rotation
process is optimized by using the trigonometric identities
sin(x + y) = sin(x) * cos(y) + cos(x) * sin(y)
cos(x + y) = cos(x) * cos(y) - sin(x) * sin(y)
Here, "x" is the current rotation, and "y" is 5 degrees.
By this dodge, we avoid approximately 36 trig function evaluations
per frame.
\******************************************************************/
y = -30;
x = 0;
n = -90; // start at angle of -90 degrees (straight up)
var nx;
while(!ground.hitTest(this._x + ( nx = x * c5 - y * s5), this._y + (y = y * c5 + x * s5), true) && n < 270) {
x = nx;
n += 5;
}
}
} // end of Mouse-driven option
if(isKey) { // key-driven motion
// set position "up" in rider's frame of reference
var dy = Math.cos(this._rotation * Math.PI / 180);
var dx = -Math.sin(this._rotation * Math.PI / 180);
// move 10 pixels "up" in rider's frame of reference
y = this._y - 10 * dy ;
x = this._x - 10 * dx;
// move "down" in rider's frame until we hit ground
while(!ground.hitTest(x += dx, y += dy, true) && x > 0 && x < Stage.width && y > 0 && y < Stage.height);
this._x = x;
this._y = y;
// again, set position "up" in rider's frame
y = -30 * Math.cos((this._rotation - 5) * deg); // this 30 is the wheelbase!
x = 30 * Math.sin((this._rotation - 5) * deg);
n = this._rotation - 95;
// now rotate in 5-degree increments until front wheel hits the ground
var incr = 5 * this._xscale / 100;
var limit = 72;
var c = 0;
while(!ground.hitTest(this._x + ( nx = x * c5 - y * s5), this._y + (y = y * c5 + x * s5), true) && c ++ < limit) {
x = nx;
n += incr;
}
if (this._xscale < 0) n = 180 - n;
} // end of key option
this._rotation = (s5 < 0 ? -n : n); // a bit of a kludge
// if we're on the Stage edge, align appropriately
if(this._y >= Stage.height) this._rotation = 0;
if(this._y <= 0) this._rotation = 180;
if(this._x >= Stage.width) this._rotation = -90;
if(this._x <= 0) this._rotation = 90;
updateAfterEvent(); // only relevant for mouse motion
}
// make the bike respond to mouse drags and key presses
bike.onPress = function() {
mousing = true;
this.onMouseMove = rideOnLine;
}
bike.onRelease = bike.onReleaseOutside = function() {
delete this.onMouseMove;
}
bike.onKeyDown = function() {
mousing = false;
this.onEnterFrame = rideOnLine;
}
Key.addListener(bike);
bike.onKeyUp = function() {
this.onEnterFrame = null;
}
// make the pencil draw in movie clip with the special name "ground,"
// that is used in the function rideOnLine()
pencil.onPress = function() {
_root.createEmptyMovieClip("ground", 10); // wipes out previous drawing
ground.lineStyle(4, 0x880044);
ground.moveTo(this._x = _xmouse, this._y = _ymouse);
this.onMouseMove = function() {
ground.lineTo(this._x = _xmouse, this._y = _ymouse);
}
Mouse.hide();
}
pencil.onRelease = pencil.onReleaseOutside = function() {
delete this.onMouseMove;
Mouse.show();
}
| © Michael J. Kantor 3/2004 | FlashGizmo.com |