--- title: Spinning cube in space description: We consider the difference between parallel and perspective projection. Both are widely used in practice for various purposes. In the previous example, we... sections: [Linear perspective,Rotation matrix,Experimental model] tags: [javascript,canvas,geometry,graphics,image,picture,square,cube] scripts: [/js/classes-point-cube.js,/js/spinning-cube.js,/js/spinning-cube2.js] styles: [/css/pomodoro1.css] canonical_url: /en/2023/01/11/spinning-cube-in-space.html url_translated: /ru/2023/01/10/spinning-cube-in-space.html title_translated: Вращаем куб в пространстве date: 2023.01.11 lang: en --- We consider the difference between parallel and perspective projection. Both are widely used in practice for various purposes. In the previous example, we [rotated square on plane]({{ '/en/2023/01/06/spinning-square-on-plane.html' | relative_url }}) — we pass into three-dimensional space. Now, to display the rotation of a three-dimensional object on the screen plane, we first need to create a three-dimensional object, rotate it by an angle, draw a projection from it and display already the projection on the screen. Complicated model, many cubes: [Spinning spatial cross]({{ '/en/2023/01/16/spinning-spatial-cross.html' | relative_url }}).
Parallel projection

Your browser does not support Canvas

Perspective projection

Your browser does not support Canvas

*Parallel projection* — the projection center is infinitely distant from the plane of the observer screen, dimensions of the objects look the same. *Perspective projection* — parallel lines converge in the center of the perspective, objects appear to shrink in the distance. ## Experimental model {#experimental-model} Cube size 200, canvas size 300, origin of coordinates is in the upper left corner. The center of the figure is in the middle of the canvas. The `X` axis is directed to the right, the `Y` axis is directed downwards, the `Z` axis is directed to the distance. The rotation is performed sequentially around all three axes: first around the `X` axis, then around the `Y` axis and then around the `Z` axis. Model settings can be controlled, for example, you can switch off redundant rotation around the axes and change the position of the projection center onto the observer screen.

Your browser does not support Canvas

Rotation around axes:
Center onto observer screen:
150
150
60
Remoteness of projection center:
300
## Point rotation in space {#point-rotation-in-space} We calculate the new coordinates of the point using the formulas of the rotation matrix for three-dimensional space. We rotate the point `t` relative to the point `t0` — we get the point `t'`. *Rotation along `X` axis.* {% include image_svg.html src="/img/column-vector3dx.svg" style="width: 242.619pt; height: 59.0768pt;" alt="&x'=x,&\\&y'=y_0+(y-y_0)cos\varphi-(z-z_0)sin\varphi,&\\&z'=z_0+(y-y_0)sin\varphi+(z-z_0)cos\varphi.&\\" %} *Rotation along `Y` axis.* {% include image_svg.html src="/img/column-vector3dy.svg" style="width: 246.251pt; height: 59.0768pt;" alt="&x'=x_0+(x-x_0)cos\varphi-(z-z_0)sin\varphi,&\\&y'=y,&\\&z'=z_0+(x-x_0)sin\varphi+(z-z_0)cos\varphi.&\\" %} *Rotation along `Z` axis.* {% include image_svg.html src="/img/column-vector3dz.svg" style="width: 246.793pt; height: 55.4753pt;" alt="&x'=x_0+(x-x_0)cos\varphi-(y-y_0)sin\varphi,&\\&y'=y_0+(x-x_0)sin\varphi+(y-y_0)cos\varphi,&\\&z'=z.&\\" %} ## Point projection {#point-projection} Experimental formulas with the possibility of shifting the projection center `d0` on the observer screen `tv`. We map the point of space `t` to the plane of the screen — we get the point `t'`. *Parallel projection.* {% include image_svg.html src="/img/oblique-projection.svg" style="width: 123.97pt; height: 37.2836pt;" alt="&x'=x,&\\&y'=y+(y_v-z)/4.&\\" %} *Perspective projection.* {% include image_svg.html src="/img/central-projection.svg" style="width: 231.924pt; height: 37.2836pt;" alt="&x'=x_v+d_0\cdot(x-x_v)/(z-z_v+d_0),&\\&y'=y_v+d_0\cdot(y-y_v)/(z-z_v+d_0).&\\" %} *Distance from the point to the projection center.* {% include image_svg.html src="/img/euclidean-distance.svg" style="width: 319.911pt; height: 17.9328pt;" alt="d(t,d_0)=\sqrt{(x-x_v)^2+(y-y_v)^2+(z-z_v+d_0)^2}." %} ## Face sorting {#face-sorting} When creating a cube, we set the vertices of each face clockwise. When obtaining a projection, we substitute three consecutive vertices into the equation of a line, to determine the tilt of the face and its remoteness from the projection plane. *Equation of the line, that passes through two points.* {% include image_svg.html src="/img/linear-equation.svg" style="width: 137.171pt; height: 35.3194pt;" alt="{(x-x_1)\over(y-y_1)}={(x_2-x_1)\over(y_2-y_1)}." %} ## Algorithm description {#algorithm-description} First, we bypass the vertices of the cube and rotate them by an angle relative to the center point. Then we bypass the faces of the cube and get projections of the vertices included in them. After that, we sort the projections of the faces by remoteness. Then we draw projections on the plane — we link the points with lines. We draw with a translucent color first the far faces and atop them the near ones, so that the far faces can be seen through the near ones. At each step of displaying the figure, we repeat the sorting of the faces by remoteness, since with a change in the angle of rotation, the coordinates shift, and the near faces become far. ## Implementation in JavaScript {#implementation-in-javascript} {% include classes-point-cube-en.md -%} Create an object and draw two projections on the plane. ```js 'use strict'; // we will draw two pictures at once, there will be // one object, and there will be many projections const canvas1 = document.getElementById('canvas1'); const canvas2 = document.getElementById('canvas2'); // create an object const cube = new Cube(50,50,50,200); // figure center, we'll perform a rotation around it const t0 = new Point(150,150,150); // remoteness of the projection center const d = 300; // observer screen position const tv = new Point(150,150,80); // rotation angle in degrees const deg = {x:0,y:1,z:0}; ``` ```js // figure rotation and image update function repaint() { cube.rotate(deg, t0); // draw parallel projection drawFigure(canvas1, cube.projection('parallel', tv)); // draw perspective projection drawFigure(canvas2, cube.projection('perspective', tv, d)); } ``` ```js // draw a figure by points from an array function drawFigure(canvas, proj) { let context = canvas.getContext('2d'); // sort the faces by their tilt proj.sort((a,b) => b.clock-a.clock); // clear the entire canvas context.clearRect(0, 0, canvas.width, canvas.height); // bypass the array of cube faces for (let i = 0; i < proj.length; i++) { // bypass the array of points and link them with lines context.beginPath(); for (let j = 0; j < proj[i].length; j++) { if (j == 0) { context.moveTo(proj[i][j].x, proj[i][j].y); } else { context.lineTo(proj[i][j].x, proj[i][j].y); } } context.closePath(); // draw the face of the cube along with the edges context.lineWidth = 2.2; context.lineJoin = 'round'; context.fillStyle = '#fff9'; context.strokeStyle = '#222'; context.fill(); context.stroke(); } } ``` ```js // after loading the page, set the image refresh interval document.addEventListener('DOMContentLoaded',()=>setInterval(repaint,50)); ```