tags:

views:

30

answers:

1

So I am writing a little mini pseudo 3d engine for the canvas element in html5. In the code below I am drawing a bunch of squares with varying positions and rotations (rotation around the z axis, so no deformation)

Now I want to be able to tell which square the user clicks on. In the objects array the items are supported by the z position starting with the squares the furthest away from the camera (so that they draw properly). So given a 3d point relative to the top left of the corner of the canvas how can I tell which square was clicked?

//Draw objects
for (var i = 0; i < objects.length; i++) {
    var object = objects[i];
    var cz = object.position.z - camera.position.z;

    if (cz > 0) {
        cz = 1 / ((cz - 1) * 0.75 + 1);

        context.save();

        context.translate(halfWidth, halfHeight); //viewport transform
        context.scale(1, -1); //invert y axis
        context.scale(cz, cz); //perspective
        context.translate(-camera.position.x, -camera.position.y); //camera transform
        context.translate(object.position.x, object.position.y); //world transform
        context.rotate(object.rotation);

        context.fillStyle = object.color;
        context.fillRect(-40, -40, 80, 80);

        context.restore();
    }
}

P.S. If I am doing anything weird or backwards and you know of a way to improve, I would love to hear suggestions

A: 

I would suggest that you draw the objects with the same transformations to a hidden canvas of the same size, but give each square a unique color (maybe derived from the index i).

You would do that like this:

var col = index.toString(16);                 // convert to hex
while (col.length < 6) col = "0"+col;     // pad leading 0s
ctx.fillStyle = "#"+col;
ctx.fillRect(-40,-40,80,80);

Then when you get a mouseclick event on the visible canvas, look at that location in your hidden one to get the color (index) of the selected object:

var colData = ctx.getImageData(clickX, clickY, 1, 1).data;
var index = (colData[2]<<16) | (colData[1]<<8) | colData[0];

This will work for up to 16M objects and is fairly simple.

andrewmu
I am hoping to get something more math based, but this is a very creative solution
Mr Bell
It's a fairly common way of doing hit testing for complex scenes.
andrewmu
The alternative would be that you programmatically generate the transformation that canvas is doing on your shapes, perform that transform on the hit point to create a ray through the scene and then work out if any of the shapes intersect the ray (and which if any is closest).
andrewmu