// © Головин Г.Г., Код с комментариями, 2023 'use strict'; // Класс Точка трёхмерного пространства содержит методы для поворотов на угол и для получения проекций // на плоскость. При получении проекций, вычисляется расстояние от точки до центра проекции. Точка также // содержит статический метод для сравнения двух проекций точек. class Point { // координаты точки constructor(x,y,z) { this.x=x; this.y=y; this.z=z; } // поворачиваем эту точку на угол (deg) // по осям (x,y,z) относительно точки (t0) rotate(deg, t0) { // функции для получения синуса и косинуса угла в радианах const sin = (deg) => Math.sin((Math.PI/180)*deg); const cos = (deg) => Math.cos((Math.PI/180)*deg); // получаем новые координаты точки по формулам // матрицы поворота для трёхмерного пространства let x,y,z; // поворот по оси 'x' y = t0.y+(this.y-t0.y)*cos(deg.x)-(this.z-t0.z)*sin(deg.x); z = t0.z+(this.y-t0.y)*sin(deg.x)+(this.z-t0.z)*cos(deg.x); this.y=y; this.z=z; // поворот по оси 'y' x = t0.x+(this.x-t0.x)*cos(deg.y)-(this.z-t0.z)*sin(deg.y); z = t0.z+(this.x-t0.x)*sin(deg.y)+(this.z-t0.z)*cos(deg.y); this.x=x; this.z=z; // поворот по оси 'z' x = t0.x+(this.x-t0.x)*cos(deg.z)-(this.y-t0.y)*sin(deg.z); y = t0.y+(this.x-t0.x)*sin(deg.z)+(this.y-t0.y)*cos(deg.z); this.x=x; this.y=y; } // получаем проекцию типа (type) с расстояния (d) // на плоскость экрана наблюдателя (tv) projection(type, tv, d) { let proj = {}; // получаем проекцию по экспериментальным формулам switch (type) { case 'parallel': { proj.x = this.x; proj.y = this.y+(tv.y-this.z)/4; break; } case 'perspective': { proj.x = tv.x+d*(this.x-tv.x)/(this.z-tv.z+d); proj.y = tv.y+d*(this.y-tv.y)/(this.z-tv.z+d); break; } } // вычисляем расстояние до центра проекции proj.dist = Math.sqrt((this.x-tv.x)*(this.x-tv.x) +(this.y-tv.y)*(this.y-tv.y) +(this.z-tv.z+d)*(this.z-tv.z+d)); return proj; } // сравниваем две проекции точек (p1,p2), // координаты (x,y) должны совпадать static pEquals(p1, p2) { return Math.abs(p1.x-p2.x)<0.0001 && Math.abs(p1.y-p2.y)<0.0001; } }; // Класс Куб содержит коллекцию вершин класса Точка и массив граней. Каждая грань — это массив из 4 вершин, // выходящих из одной точки и идущих по часовой стрелке. Куб содержит методы для поворота всех вершин на угол // и для получения проекций всех граней на плоскость. При получении проекций, вычисляется наклон грани — это // удалённость от плоскости проекции. Куб также содержит два статических метода для сравнения двух проекций // граней: для определения равноудалённых граней от центра проекции и смежных стенок между соседними кубиками. class Cube { // левая верхняя ближняя координата и размер constructor(x,y,z,size) { // правая нижняя дальняя координата let xs=x+size,ys=y+size,zs=z+size; let v={ // вершины t000: new Point(x,y,z), // верх t001: new Point(x,y,zs), // верх t010: new Point(x,ys,z), // низ t011: new Point(x,ys,zs), // низ t100: new Point(xs,y,z), // верх t101: new Point(xs,y,zs), // верх t110: new Point(xs,ys,z), // низ t111: new Point(xs,ys,zs)};// низ this.vertices=v; this.faces=[ // грани [v.t000,v.t100,v.t110,v.t010], // передняя [v.t000,v.t010,v.t011,v.t001], // левая [v.t000,v.t001,v.t101,v.t100], // верхняя [v.t001,v.t011,v.t111,v.t101], // задняя [v.t100,v.t101,v.t111,v.t110], // правая [v.t010,v.t110,v.t111,v.t011]];// нижняя } // поворачиваем вершины куба на угол (deg) // по осям (x,y,z) относительно точки (t0) rotate(deg, t0) { for (let vertex in this.vertices) this.vertices[vertex].rotate(deg, t0); } // получаем проекции типа (type) с расстояния (d) // на плоскость экрана наблюдателя (tv) projection(type, tv, d) { let proj = []; for (let face of this.faces) { // проекция грани, массив вершин let p = []; // кумулятивная удалённость вершин p.dist = 0; // обходим вершины грани for (let vertex of face) { // получаем проекции вершин let proj = vertex.projection(type, tv, d); // накапливаем удалённость вершин p.dist+=proj.dist; // добавляем в массив вершин p.push(proj); } // вычисляем наклон грани, удалённость от плоскости проекции p.clock = ((p[1].x-p[0].x)*(p[2].y-p[0].y) -(p[1].y-p[0].y)*(p[2].x-p[0].x))<0; proj.push(p); } return proj; } // сравниваем две проекции граней (f1,f2), вершины // должны быть равноудалены от центра проекции static pEquidistant(f1, f2) { return Math.abs(f1.dist-f2.dist)<0.0001; } // сравниваем две проекции граней (f1,f2), координаты // точек по главной диагонали (p0,p2) должны совпадать static pAdjacent(f1, f2) { return Point.pEquals(f1[0],f2[0]) && Point.pEquals(f1[2],f2[2]); } };