|
Notes for Thursday April 16 class -- Mouse events and Catmull Rom paths
Mouse event handling
I added basic event handling into the library,
by building a layer on top of the browser's native support layer for
detecting mouse events.
The resulting API enables the application programmer (you)
to attach callback methods to your canvas for
down, drag, up and move events.
The implementation follows:
addEventListenersToCanvas = function(canvas) {
let toX = x => 2 * x / canvas.width - 1,
toY = y => (canvas.height - 2 * y) / canvas.width;
if (! canvas.onDrag ) canvas.onDrag = (x, y) => { };
if (! canvas.onMove ) canvas.onMove = (x, y) => { };
if (! canvas.onPress ) canvas.onPress = (x, y) => { };
if (! canvas.onRelease) canvas.onRelease = (x, y) => { };
canvas.addEventListener('mousemove', function(e) { this._response = this._isDown ? this.onDrag : this.onMove;
this._response(toX(e.clientX), toY(e.clientY)); }, false);
canvas.addEventListener('mousedown', function(e) { this.onPress (toX(e.clientX), toY(e.clientY)); this._isDown = true ;}, false);
canvas.addEventListener('mouseup' , function(e) { this.onRelease(toX(e.clientX), toY(e.clientY)); this._isDown = false;}, false);
}
Once you have access to the mouse, you can
create the ability for an artist to
do many things with it,
including draw curves,
create, select or delete objects,
move or otherwise transform objects
in the scene,
or change properties for an object such as color or texture.
The example we implemented in class rotates the user's view
of the scene as the mouse is dragged.
The implementation for this consisted of two parts.
The first part uses the results of mouse dragging to
generate two rotation values, rotX and rotY :
// COMPUTE VIEW ROTATION ANGLES WHEN USER DRAGS MOUSE.
let dragging, rotX = 0, rotY = 0, x0, y0;
let rotX=0, rotY=0, x0, y0;
canvas1.onPress = (x, y) => { x0 = x; y0 = y; }
canvas1.onDrag = (x, y) => { rotX += x - x0; x0 = x;
rotY += y - y0; y0 = y; }
The second part is applying these two values
to transform the root object just before drawing it:
root.identity()
.rotateY(rotX)
.rotateX(rotY);
root.draw();
Translation by a vector
Sometimes you want to just translate a matrix by a vector,
instead of by three separate x,y,z arguments.
To support this, we added a line at the start of the
translate() method of
class Matrix , so that the application programmer
can simply pass in a single vector argument,
rather than three separate floating point arguments.
The added code is in blue:
this.translate = function(x, y, z) {
if (Array.isArray(x)) { z = x[2]; y = x[1]; x = x[0]; }
let m = [1,0,0,0, 0,1,0,0, 0,0,1,0, x,y,z,1]; // (1) CREATE TRANSLATION MATRIX.
value = multiply(value, m); // (2) MULTIPLY TO CHANGE OBJECT value.
return this;
}
Evaluating a Catmull Rom spline
To implement Catmull Rom splines, we first created
a Catmull Rom basis matrix, to transform
a sequence of four interpolating key points into
the four coefficients of a cubic polynomial:
let crBasis = new Matrix();
crBasis.setValue([ -1/2,1,-1/2,0, 3/2,-5/2,0,1, -3/2,2,1/2,0, 1/2,-1/2,0,0 ]);
Then we implemented a function that takes
an array of key points as its first argument,
and a parameter
t as its second argument,
which indicates how far the position the path we sample,
where 0 ≤ t ≤ 1 .
let evalCRSpline = (keys, t) => {
t = Math.max(1/1000, Math.min(1-1/1000, t));
let n = keys.length - 1,
i = Math.floor(n * t),
f = n * t % 1;
let P0 = keys[i > 0 ? i-1 : V3.equal(keys[0], keys[n]) ? n-1 : 0];
P1 = keys[i];
P2 = keys[i+1];
P3 = keys[i+1 < n ? i+2 : V3.equal(keys[0], keys[n]) ? 1 : n];
let p = [];
for (let k = 0 ; k < 3 ; k++) {
let C = crBasis.transform([ P0[k], P1[k], P2[k], P3[k] ]);
p.push( f*f*f*C[0] + f*f*C[1] + f*C[2] + C[3] );
}
return p;
}
Then in
index.html
we applied this to an example:
// SAMPLE A CATMULL ROM SPLINE PATH.
let P = [
[-1, -1, -1],
[-2/3, 1/3, 1],
[ 1/3, -1/3, 1],
[ 1, 1, -1],
[-1, -1, -1], // IF THE LAST KEY EQUALS THE FIRST, IT'S A LOOP.
];
for (let n = 0 ; n < P.length ; n++) // SHOW KEYS AS LARGE SPHERES.
test.add(sphere, green_plastic)
.translate(P[n])
.scale(.13);
for (let n = 0 ; n <= 100 ; n++) { // SHOW PATH AS SMALL SPHERES.
let p = evalCRSpline(P, n/100);
test.add(sphere, red_plastic)
.translate(p)
.scale(.05);
}
Link to cool video
We discussed the Utah Teapot, and Brandon pointed us to
a link to a video about the Utah Teapot by Tom Scott.
You can find that video here.
hw8, due before the start of class, Thursday April 23
Starting with the code base we ended up with
at the end of class,
which is in hw8.zip, do the following:
- Use the mouse to interact with your 3D scene in some
interesting way.
For example, you can try drawing a path to create
a string of pearls.
Or you can use the mouse to move or rotate individual objects
that the mouse happens to be over.
Or you can create a simple interactive game.
There are plenty of possibilities.
- Create an animation using the Catmull Rom spline.
For example, you can use the result of evaluating
the Catmull Rom spline to control the positoin
or rotation angles over time of the limbs of a jointed animated
character.
- Do anything else you would like to do,
for extra credit.
You have a lot of tools now. Use them
to make something awesome!
| |