1/jekyll_site/js/classes-point-cube.js
2023-12-17 07:55:25 +03:00

135 lines
6.6 KiB
JavaScript
Raw Permalink 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';
// Класс Точка трёхмерного пространства содержит методы для поворотов на угол и для получения проекций
// на плоскость. При получении проекций, вычисляется расстояние от точки до центра проекции. Точка также
// содержит статический метод для сравнения двух проекций точек.
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]);
}
};