JeroenD - 3 months ago 13

Javascript Question

I am trying to make a cube in 3D, made out of all seperate, little div's. Lets say we have a cube of 3*3*3 div's. The things given about all of the div's:

- X, Y, Z coordinate (of the 3D cube)
- Rotation angle around the X and Y axis

This should technically be enough to calculate the 2D perspective projection of the cube.

The question now is: How can I calculate the X and Y coordinates of each div?

Ps. A similar example of such a cube is in this link: http://maettig.com/code/javascript/3d_dots.html. The two things I don't like in this example are:

- The way how the cube rotates. For example, if I push the mouse down, the cube rotates up. Also, for horizontal movement of the mouse the cube rotates always around the same axis, for vertical movement of the mouse the cube rotates relatively to the horizontal rotation.
- The cube has only a "+" at the corners, I want a filled (massive) cube.

Answer

I think there are two different answers to what you're asking: a **direct answer to your question**, and an **answer to your problem**:

*Note - The following answer is a reformulation of a post I made for the thread Transform GPS-Points to Screen-Points with Perspective Projection in Android. You can also chech the Wikipedia article "3D projection" for a more generic answer.*

You will need a bit more information to execute you perspective projection, such as the position/orientation of your *camera/eye*, its angle of view, and the surface you want to project your cube on.

With all this, you should be able to loop on your `div`

elements, then on their 4 corner vertices to apply your rotation transform and project each of them, to finally use the 2D coordinates you get to render the elements.

Let's simplify the situation. We have:

- A vertex A(x_0, y_0, z_0), one of the corners of your
`div`

*θ*and*δ*the angles defining the rotation you want to apply, resp. the*pitch angle*and the*yaw angle*(Tayt-Brian angles)

... and we want:

- D(x,y,z), the position of our rotated element

Thus your linearized equations are:

`x = sin(δ) * y_0 + cos(δ) * x_0`

`y = sin(θ) * z_0 + cos(θ) * (cos(δ) * y_0 − sin(δ) * x_0)`

`z = cos(θ) * z_0 i sin(θ) * (cos(δ) * y_0 − sin(δ) * x_0)`

Now we have:

- Our point D(x,y,z)
*w***h*, the dimension of the surface you want to project on (`innerWidth`

*`innerHeight`

for instance in your case)- A half-angle of view
*α*

... and we want:

- The coordinates of B in the surface plane (let's call them
**X**and**Y**)

A schema for the X-screen-coordinates:

E is the position of our "eye" in this configuration, which I chose as origin to simplify. If it is not the case and/or if you want to also rotate your camera, you'll need to apply again the corresponding translation and/or rotation transform(s) to D before the next steps.

The focal length *f* can be estimated knowing that:

`tan(α) = (w/2) / f`

*(1)*

You can see on the picture that the triangles *ECD* and *EBM* are **similar**, so using the Side-Splitter Theorem, we get:

`MB / CD = EM / EC`

<=>`X / x = f / z`

*(2)*

With both ** (1)** and

`X = (x / z) * ( (w / 2) / tan(α) )`

Note: Same reasoning for

Y.

Some remarks:

- Usually,
*α = 45deg*is used, which means`tan(α) = 1`

. That's why this term doesn't appear in many implementations. If you want to preserve the ratio of the elements you display, keep

*f*constant for both**X**and**Y**, ie instead of calculating:`X = (x / z) * ( (w / 2) / tan(α) )`

and`Y = (y / z) * ( (h / 2) / tan(α) )`

... do:

`X = (x / z) * ( size / 2) / tan(α) )`

and`Y = (y / z) * ( (size / 2) / tan(α) )`

with`size`

a constant value you defined (`size = min(w,h)`

or`size = (w+h)/2`

are often used for instance). It will only affect the focal, and thus the angle of view.

As you may have noticed on the picture above, the screen coordinates are here defined between

*[-w/2 ; w/2]*for X and*[-h/2 ; h/2]*for Y, but you probably want*[0 ; w]*and*[0 ; h]*instead.`X += w/2`

and`Y += h/2`

- Problem solved.

`div`

As I see the situation, there is a flaw with the method described above. Sure you can get the 2D coordinates defining your rotated and projected `div`

elements, but ** how can you use it to render them?**.

Once projected, your `div`

won't probably look rectangular anymore, making it hard to render using simple CSS, especially if your `div`

elements contain complex stuff.

So if your real purpose is to display a 3D DOM cube, with rotations and perspective, I recommend you to use **CSS3 3D transforms**, letting the browser do the computations.

For instance, you'll find here a tutorial to implement such a cube with only HTML and CSS3.

The advantages are multiple:

- You benefit of the browsers GPU acceleration, making it smoother than done using plain JS.
- It affects the contents of your
`div`

(rotation, perpespective) - No maths to implement

You may only have to worry about the browsers compatibility of you aim for older versions (http://caniuse.com/transforms3d).

If you want to dynamically rotate your cube (for instance when the mouse moves), just use JS to edit your CSS transforms

I quickly made this JSFiddle, simply copying the implementation from the tutorial and adding an `onmousemove`

handler to update the rotation.

Hope it helped, bye!