JavaScript
'use strict';
// Рисовать будем сразу две картинки,
// объект у нас будет один, а проеций будет много
var canvas1 = document.getElementById('canvas1');
var canvas2 = document.getElementById('canvas2');
var canvas_context1 = canvas1.getContext('2d');
var canvas_context2 = canvas2.getContext('2d');
// Сместим начало координат в центр блока
canvas_context1.translate(canvas1.width / 2, canvas1.height / 2);
canvas_context2.translate(canvas2.width / 2, canvas2.height / 2);
// Фигуру будем задавать как совокупность четырех плоскостей,
// каждая плоскость - совокупность четырех точек
var arr_t = [
// Передняя стенка
[{x:-100, y:-100, z:-100},
{x:100, y:-100, z:-100},
{x:100, y:100, z:-100},
{x:-100, y:100, z:-100}],
// Правая стенка
[{x:100, y:-100, z:-100},
{x:100, y:-100, z:100},
{x:100, y:100, z:100},
{x:100, y:100, z:-100}],
// Левая стенка
[{x:-100, y:-100, z:-100},
{x:-100, y:-100, z:100},
{x:-100, y:100, z:100},
{x:-100, y:100, z:-100}],
// Задняя стенка
[{x:-100, y:-100, z:100},
{x:100, y:-100, z:100},
{x:100, y:100, z:100},
{x:-100, y:100, z:100}]];
// Центр фигуры.
// Вокуг него будем осуществлять поворот
var t0 = {x:0, y:0, z:0};
// Угол поворота в градусах
var deg = 0;
// Для перспективной проекции:
// Удаленность наблюдателя от объекта.
// Не может быть меньше размеров объекта,
// в нашем случае 200px
var a = 300;
// Размер объекта на экране в (%)
var k = 70;
// Текущая фигура
var arr_tt = [];
for (var i = 0; i < arr_t.length; i++) {
arr_tt[i] = arr_t[i].slice();
}
// Текущая проекция
var arrProj;
// Задаем функцию обновления отображения
// с поворотом фигуры.
// Затем эту функцию надо будет вставить
// в переменную window.onload
function repaint() {
// Сначала очистим экран - закрасим белым цветом
clearScreen(canvas1, canvas_context1, 'rgb(255,255,255)');
clearScreen(canvas2, canvas_context2, 'rgb(255,255,255)');
// Теперь повернем исходную фигуру на угол
// по оси Y и перезапишем текущую фигуру
for (var i = 0; i < arr_t.length; i++) {
for (var j = 0; j < arr_t[0].length; j++) {
arr_tt[i][j] = rotateOnDegreeY(t0, arr_t[i][j], deg);
}
}
// Получим параллельную проекцию
arrProj = getParallelProjection(arr_tt);
// Нарисуем ее
canvas_context1.lineWidth = 2; // черный полупрозрачный
strokeFigure(canvas_context1, arrProj, 'rgba(0,0,0,0.5)');
// Получим перспективную проекцию
arrProj = getPerspectiveProjection(arr_tt);
// Нарисуем ее тоже в соседнем окошке
canvas_context2.lineWidth = 2; // черный полупрозрачный
strokeFigure(canvas_context2, arrProj, 'rgba(0,0,0,0.5)');
// Увеличиваем угол
deg += 1;
}
// Далее идут вспомогательные функции
// Получаем параллельную проекцию фигуры на плоскость экрана
function getParallelProjection(arr) {
var arr_new = [];
for (var i = 0; i < arr.length; i++) {
arr_new[i] = [];
for (var j = 0; j < arr[0].length; j++) {
arr_new[i][j] = {};
arr_new[i][j].x = arr[i][j].x;
arr_new[i][j].y = arr[i][j].y + arr[i][j].z / 4;
}
}
return arr_new;
}
// Получаем перспективную проекцию
function getPerspectiveProjection(arr) {
var arr_new = [];
for (var i = 0; i < arr.length; i++) {
arr_new[i] = [];
for (var j = 0; j < arr[0].length; j++) {
arr_new[i][j] = {};
arr_new[i][j].x = (a / 100 * k) * arr[i][j].x / (arr[i][j].z + a);
arr_new[i][j].y = (a / 100 * k) * arr[i][j].y / (arr[i][j].z + a);
}
}
return arr_new;
}
// Закрашиваем весь экран определенным цветом
function clearScreen(canvas, context, color) {
context.fillStyle = color;
context.beginPath();
context.fillRect(- canvas.width / 2,
- canvas.height / 2,
canvas.width,
canvas.height);
context.closePath();
context.fill();
}
// Рисуем фигру по точкам из массива
function strokeFigure(context, arr, color) {
context.strokeStyle = color;
context.fillStyle = color;
for (var i = 0; i < arr.length; i++) {
context.beginPath();
for (var j = 0; j < arr[0].length; j++) {
if (j == 0) {
context.moveTo(arr[i][j].x, arr[i][j].y);
} else {
context.lineTo(arr[i][j].x, arr[i][j].y);
}
}
context.closePath();
context.stroke();
}
}
// Поворачиваем точку t(x,y,z) на угол (deg) по оси (Y)
// относительно точки t0(x,y,z)
function rotateOnDegreeY(t0, t, deg) {
var t_new = {};
// Переводим угол поворота из градусов в радианы
var rad = (Math.PI / 180) * deg;
// Рассчитываем координаты новой точки по формуле
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 t_new;
}
// Устанавливаем интервал обновления отображения
window.onload = function() {
setInterval(repaint, 100);
};