Reputation: 353
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:
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:
Upvotes: 1
Views: 8408
Reputation: 15139
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:
div
... and we want:
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:
innerWidth
* innerHeight
for instance in your case)... and we want:
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 (2), we now have:
X = (x / z) * ( (w / 2) / tan(α) )
Note: Same reasoning for Y.
Some remarks:
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:
div
(rotation, perpespective)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!
Upvotes: 13