1/jekyll_site/js/spinning-spatial-cross.js
2023-12-17 08:08:38 +03:00

116 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// © Головин Г.Г., Код с комментариями, 2023
'use strict';
// матрицы-шаблоны для кубиков
const shape1 = [ // пространственный крест
[[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]]];
const shape2 = [ // крест-куб
[[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,1,0,0], [0,0,0,0,0], [1,0,0,0,1], [0,0,0,0,0], [0,0,1,0,0]],
[[1,1,1,1,1], [1,0,0,0,1], [1,0,0,0,1], [1,0,0,0,1], [1,1,1,1,1]],
[[0,0,1,0,0], [0,0,0,0,0], [1,0,0,0,1], [0,0,0,0,0], [0,0,1,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]]];
// размер кубика, количество кубиков в ряду, отступ
const size = 40, row = 5, gap = 50;
// массивы для кубиков
const cubes1 = [], cubes2 = [];
// обходим матрицы, заполняем массивы кубиками
for (let x=0; x<row; x++)
for (let y=0; y<row; y++)
for (let z=0; z<row; z++) {
if (shape1[x][y][z]==1)
cubes1.push(new Cube(x*size+gap,y*size+gap,z*size+gap,size));
if (shape2[x][y][z]==1)
cubes2.push(new Cube(x*size+gap,y*size+gap,z*size+gap,size));
}
// центр фигуры, вокруг него будем выполнять поворот
const t0 = new Point(150,150,150);
// удалённость центра проекции
const d = 300;
// положение экрана наблюдателя
const tv = new Point(150,150,125);
// угол поворота в градусах
const deg = {x:1,y:1,z:1};
// рисовать будем по две картинки для каждой фигуры
const canvas1 = document.getElementById('canvas1');
const canvas2 = document.getElementById('canvas2');
const canvas3 = document.getElementById('canvas3');
const canvas4 = document.getElementById('canvas4');
// обновление изображения
function repaint() {
// пространственный крест
processFigure(cubes1,canvas1,canvas2);
// крест-куб
processFigure(cubes2,canvas3,canvas4);
}
// поворачиваем фигуру и получаем проекции
function processFigure(cubes,cnv1,cnv2) {
// массивы проекций граней кубиков
let parallel = [], perspective = [];
// поворачиваем кубики и получаем проекции
for (let cube of cubes) {
cube.rotate(deg, t0);
parallel = parallel.concat(cube.projection('parallel',tv,d));
perspective = perspective.concat(cube.projection('perspective',tv,d));
}
// смежные стенки между соседними кубиками не рисуем
noAdjacent(parallel);
noAdjacent(perspective);
// сортируем грани разных кубиков по удалённости и внутри одного кубика по наклону
parallel.sort((a,b)=>Math.abs(b.dist-a.dist)>size ? b.dist-a.dist : b.clock-a.clock);
// сортируем грани по удалённости от центра проекции
perspective.sort((a,b)=>b.dist-a.dist);
// рисуем параллельную проекцию
drawFigure(cnv1, parallel);
// рисуем перспективную проекцию
drawFigure(cnv2, perspective);
}
// смежные стенки между соседними кубиками не рисуем
function noAdjacent(array) {
// сортируем грани по удалённости
array.sort((a,b) => b.dist-a.dist);
// удаляем смежные стенки между кубиками
for (let i=0, j=1; i<array.length-1; j=++i+1)
while (j<array.length && Cube.pEquidistant(array[i],array[j]))
if (Cube.pAdjacent(array[i],array[j])) {
array.splice(j,1);
array.splice(i,1);
i--; j=array.length;
} else j++;
}
// рисуем фигуру по точкам из массива
function drawFigure(canvas, proj, alpha=0.8) {
const context = canvas.getContext('2d');
// очищаем весь холст целиком
context.clearRect(0, 0, canvas.width, canvas.height);
// обходим массив граней куба
for (let i = 0; i < proj.length; i++) {
// обходим массив точек и соединяем их линиями
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();
// рисуем грань куба вместе с рёбрами
context.lineWidth = 1.9;
context.lineJoin = 'round';
context.fillStyle = 'rgba(200,230,201,'+alpha+')';
context.strokeStyle = 'rgba(102,187,106,'+(0.2+alpha)+')';
context.fill();
context.stroke();
}
}
// после загрузки страницы, задаём частоту обновления изображения 20 Гц
document.addEventListener('DOMContentLoaded',()=>setInterval(repaint,50));