--- title: Вращаем пространственный крест description: Пишем алгоритм для поворота трёхмерной фигуры вокруг своего центра по всем трём осям сразу. В предыдущем примере мы вращали куб в пространстве — в этом... sections: [Объёмные фигуры,Матрица поворота,Экспериментальная модель] tags: [javascript,онлайн,canvas,геометрия,матрица,графика,изображение,картинка,квадрат,куб,3д,трёхмерный] scripts: [/js/classes-point-cube.js,/js/spinning-spatial-cross.js,/js/spinning-spatial-cross2.js] styles: [/css/pomodoro1.css] canonical_url: /ru/2023/01/15/spinning-spatial-cross.html url_translated: /en/2023/01/16/spinning-spatial-cross.html title_translated: Spinning spatial cross date: 2023.01.15 --- Пишем алгоритм для поворота трёхмерной фигуры вокруг своего центра по всем трём осям сразу. В предыдущем примере мы [вращали куб в пространстве]({{ '/ru/2023/01/10/spinning-cube-in-space.html' | relative_url }}) — в этом примере кубиков будет много, алгоритм будет почти такой же, и формулы будем использовать те же. Для наглядности возьмём два варианта симметричной объёмной фигуры в двух типах проекций — *пространственный крест* и *крест-куб* — рассматриваем разницу между ними. Тестирование экспериментального интерфейса: [Объёмный тетрис]({{ '/ru/2023/01/21/volumetric-tetris.html' | relative_url }}). {% include heading.html text="Пространственный крест" hash="spatial-cross" %}
Параллельная проекция

Холст для отображения результатов вычислений

Перспективная проекция

Холст для отображения результатов вычислений

{% include heading.html text="Крест-куб" hash="cross-cube" %}
Параллельная проекция

Холст для отображения результатов вычислений

Перспективная проекция

Холст для отображения результатов вычислений

*Параллельная проекция* — все кубики одинакового размера. *Перспективная проекция* — кубики выглядят уменьшающимися вдалеке. {% include heading.html text="Экспериментальная модель" hash="experimental-model" %} Слегка усложнённая версия из предыдущего примера — теперь кубиков много. В дополнение к предыдущим настройкам можно поменять: вариант фигуры — *пространственный крест* или *крест-куб*, направление сортировки граней — *линейная перспектива* или *обратная перспектива* и прозрачность стенок кубиков.

Холст для отображения результатов вычислений

Вращение по осям:
Центр на экране наблюдателя:
150
150
125
Удалённость центра проекции:
300
Прозрачность кубиков:
20%
Вариант фигуры:
Перспективная проекция:
{% include heading.html text="Описание алгоритма" hash="algorithm-description" %} Подготавливаем трёхмерную матрицу из нулей и единиц, где единица означает кубик в определенном месте фигуры. Затем обходим эту матрицу и заполняем массив кубиков с соответствующими координатами вершин. После этого запускаем вращение по всем трём осям сразу. На каждом шаге обходим массив кубиков и получаем проекции их граней. Затем сортируем массив граней по удалённости от центра проекции, обходим этот массив и выкидываем из него одинаковые пары — это есть смежные стенки между соседними кубиками внутри фигуры. После этого рисуем полупрозрачным цветом грани кубиков — сначала дальние, а затем ближние, чтобы через ближние грани было видно дальние. {% include heading.html text="Реализация на JavaScript" hash="implementation-in-javascript" %} {% include classes-point-cube-ru.md -%} Создаём объекты по шаблонам и рисуем их проекции на плоскости. ```js '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; xMath.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); } ``` ```js // смежные стенки между соседними кубиками не рисуем function noAdjacent(array) { // сортируем грани по удалённости array.sort((a,b) => b.dist-a.dist); // удаляем смежные стенки между кубиками for (let i=0, j=1; isetInterval(repaint,50)); ```