19.12.2017

Two variants of the same figure. Spatial cross in parallel projection. Rotation of three-dimensional objects on all three axes at once.

Your browser does not support Canvas

Your browser does not support Canvas

The algorithm is universal and very simple: take three-dimensional matrix, and build the cubes by its points. Join the cubes in a single figure, and start a rotation. The matrix may be different - the essence remains the same. For this example I took two variants of the spatial cross.

1. Matrix - spatial cross (y, x, z)

// figure let figure = { shape: [ [[0,0,0,0,0], [0,0,0,0,0], [0,0,1,0,0], [0,0,0,0,0], [0,0,0,0,0]], [[0,0,0,0,0], [0,0,0,0,0], [0,0,1,0,0], [0,0,0,0,0], [0,0,0,0,0]], [[0,0,1,0,0], [0,0,1,0,0], [1,1,1,1,1], [0,0,1,0,0], [0,0,1,0,0]], [[0,0,0,0,0], [0,0,0,0,0], [0,0,1,0,0], [0,0,0,0,0], [0,0,0,0,0]], [[0,0,0,0,0], [0,0,0,0,0], [0,0,1,0,0], [0,0,0,0,0], [0,0,0,0,0]] ] }; // center of the figure let t0 = { y:figure.shape.length / 2 * size, x:figure.shape[0].length / 2 * size, z:figure.shape[0][0].length / 2 * size };

2. Launch the loop through the matrix (three-dimensional array), which builds cubes for each non-zero point.

for (let y = 0; y < figure.shape.length; y++) { for (let x = 0; x < figure.shape[0].length; x++) { for (let z = 0; z < figure.shape[0][0].length; z++) { ... // build a cube as array of 6 faces ... } } }

3. Build each cube as array of 6 faces. For example, take the left face. If there is another cube to the left of it, then don't draw this face, to not have intermediate faces inside the figure - the corresponding face of another cube don't draw too. All other faces are build similar way.

// size of the cube let size = 30; // left face face = [ {x:x*size, y:y*size, z:z*size}, {x:x*size, y:y*size, z:z*size + size}, {x:x*size, y:y*size + size, z:z*size + size}, {x:x*size, y:y*size + size, z:z*size}, ]; // central coordinate // it is needed to calculate the distance from axes (X) and (Z) face[4] = getFaceCentralCoordinate(face); // if there is something to the left, then don't draw this face if (x > 0 && figure.shape[y][x - 1][z] === 1) { face[5] = {display:false}; }

Now we have three-dimensional object, which consist of cubes, which consist of faces which consist of points. Start the rotation - now at each step first rotate the figure by the angle, then make two-dimensional projection, and in the end draw the projection on the screen.

~~Don't draw three-dimensional hologram.~~

4. Launch the loop through all points of which the object consists and turn each of them onto the angle on each axis. For example, the rotation on axis (Y). Similarly the rotation on axis (X) and (Z) performs.

// turn point t(x,y,z) onto the angle (deg) on axis (Y) // relative to the point t0(x,y,z) function rotateOnDegreeY(t0, t) { let t_new = {}; // translate the angle of rotation from degrees to radians let rad = (Math.PI / 180) * deg; // calculate coordinates of new point according to formula t_new.x = t0.x + (t.x - t0.x) * Math.cos(rad) - (t.z - t0.z) * Math.sin(rad); t_new.y = t.y; t_new.z = t0.z + (t.x - t0.x) * Math.sin(rad) + (t.z - t0.z) * Math.cos(rad); // return the resulting value return t_new; }

Now the projection of the resulting figure need to be made. There are only two variants: parallel projection and perspective projection. The difference between them we discussed in a previous article:Rotate the cube in spaceFor the current example, I chose a parallel projection.

5. Build a parallel projection for each point of the figure.

// parallel projection of point function getPointParallelProjection(point) { return { x:point.x, y:point.y + point.z / 4}; }

6. Working with projection. Draw each projection of the face of the figure by the points of which it consists.

// draw figure by the points of array function fillFigure(canvas_context, arr) { canvas_context.lineWidth = 2; canvas_context.strokeStyle = 'rgba(250,250,100,0.3)'; for (let i = 0; i < arr.length; i++) { if (arr[i].length < 5) { canvas_context.beginPath(); if (i < 3) { canvas_context.fillStyle = 'rgba(0,200,0,0.9)'; } else { canvas_context.fillStyle = 'rgba(0,200,0,0.5)'; } for (let j = 0; j < arr[i].length; j++) { if (j === 0) { canvas_context.moveTo(arr[i][j].x, arr[i][j].y); } else { canvas_context.lineTo(arr[i][j].x, arr[i][j].y); } } canvas_context.closePath(); canvas_context.fill(); canvas_context.stroke(); } } }

There were major steps of the algorithm. I hope it became clear how it works. The entire code can be viewed here:View code

01.01.1970 03:00 (MSK)

There are no comments yet.. You can be the first..