149 lines
5.3 KiB
Markdown
149 lines
5.3 KiB
Markdown
|
The Point class of the three-dimensional space contains methods for rotations by an angle and for
|
||
|
obtaining projections onto a plane. When obtaining projections, the distance from the point to the
|
||
|
projection center is calculated. Point also contains a static method to compare two projections
|
||
|
of points.
|
||
|
|
||
|
{% capture collapsed_md %}
|
||
|
```js
|
||
|
class Point {
|
||
|
// point coordinates
|
||
|
constructor(x,y,z) {
|
||
|
this.x=x;
|
||
|
this.y=y;
|
||
|
this.z=z;
|
||
|
}
|
||
|
// rotate this point by an angle (deg) along
|
||
|
// axes (x,y,z) relative to the point (t0)
|
||
|
rotate(deg, t0) {
|
||
|
// functions to obtain sine and cosine of angle in radians
|
||
|
const sin = (deg) => Math.sin((Math.PI/180)*deg);
|
||
|
const cos = (deg) => Math.cos((Math.PI/180)*deg);
|
||
|
// calculate new coordinates of point using the formulas
|
||
|
// of the rotation matrix for three-dimensional space
|
||
|
let x,y,z;
|
||
|
// rotation along 'x' axis
|
||
|
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;
|
||
|
// rotation along 'y' axis
|
||
|
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;
|
||
|
// rotation along 'z' axis
|
||
|
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;
|
||
|
}
|
||
|
// get a projection of (type) from a distance (d)
|
||
|
// onto the plane of the observer screen (tv)
|
||
|
projection(type, tv, d) {
|
||
|
let proj = {};
|
||
|
// obtain a projection using experimental formulas
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
// calculate distance to projection center
|
||
|
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;
|
||
|
}
|
||
|
// compare two projections of points (p1,p2),
|
||
|
// coordinates (x,y) should match
|
||
|
static pEquals(p1, p2) {
|
||
|
return Math.abs(p1.x-p2.x)<0.0001
|
||
|
&& Math.abs(p1.y-p2.y)<0.0001;
|
||
|
}
|
||
|
};
|
||
|
```
|
||
|
{% endcapture %}
|
||
|
{%- include collapsed_block.html summary="class Point" content=collapsed_md -%}
|
||
|
|
||
|
The Cube class contains a collection of vertices of the Point class and an array of faces. Each face is an
|
||
|
array of 4 vertices, coming from the same point and going clockwise. The Cube contains methods for rotating
|
||
|
all vertices by an angle and for obtaining projections of all faces onto a plane. When obtaining projections,
|
||
|
the tilt of the face is calculated — this is the remoteness from the projection plane. The cube also contains
|
||
|
two static methods for comparing two face projections: for defining the equidistant faces from the projection
|
||
|
center and adjacent walls between neighboring cubes.
|
||
|
|
||
|
{% capture collapsed_md %}
|
||
|
```js
|
||
|
class Cube {
|
||
|
// left upper near coordinate and size
|
||
|
constructor(x,y,z,size) {
|
||
|
// right lower distant coordinate
|
||
|
let xs=x+size,ys=y+size,zs=z+size;
|
||
|
let v={ // vertices
|
||
|
t000: new Point(x,y,z), // top
|
||
|
t001: new Point(x,y,zs), // top
|
||
|
t010: new Point(x,ys,z), // bottom
|
||
|
t011: new Point(x,ys,zs), // bottom
|
||
|
t100: new Point(xs,y,z), // top
|
||
|
t101: new Point(xs,y,zs), // top
|
||
|
t110: new Point(xs,ys,z), // bottom
|
||
|
t111: new Point(xs,ys,zs)};// bottom
|
||
|
this.vertices=v;
|
||
|
this.faces=[ // faces
|
||
|
[v.t000,v.t100,v.t110,v.t010], // front
|
||
|
[v.t000,v.t010,v.t011,v.t001], // left
|
||
|
[v.t000,v.t001,v.t101,v.t100], // upper
|
||
|
[v.t001,v.t011,v.t111,v.t101], // rear
|
||
|
[v.t100,v.t101,v.t111,v.t110], // right
|
||
|
[v.t010,v.t110,v.t111,v.t011]];// lower
|
||
|
}
|
||
|
// rotate vertices of the cube by an angle (deg)
|
||
|
// along axes (x,y,z) relative to the point (t0)
|
||
|
rotate(deg, t0) {
|
||
|
for (let vertex in this.vertices)
|
||
|
this.vertices[vertex].rotate(deg, t0);
|
||
|
}
|
||
|
// get projections of (type) from a distance (d)
|
||
|
// onto the plane of the observer screen (tv)
|
||
|
projection(type, tv, d) {
|
||
|
let proj = [];
|
||
|
for (let face of this.faces) {
|
||
|
// face projection, array of vertices
|
||
|
let p = [];
|
||
|
// cumulative remoteness of vertices
|
||
|
p.dist = 0;
|
||
|
// bypass the vertices of the face
|
||
|
for (let vertex of face) {
|
||
|
// obtain the projections of the vertices
|
||
|
let proj = vertex.projection(type, tv, d);
|
||
|
// accumulate the remoteness of vertices
|
||
|
p.dist+=proj.dist;
|
||
|
// add to array of vertices
|
||
|
p.push(proj);
|
||
|
}
|
||
|
// calculate face tilt, remoteness from the projection plane
|
||
|
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;
|
||
|
}
|
||
|
// compare two projections of faces (f1,f2), vertices
|
||
|
// should be equidistant from the center of projection
|
||
|
static pEquidistant(f1, f2) {
|
||
|
return Math.abs(f1.dist-f2.dist)<0.0001;
|
||
|
}
|
||
|
// compare two projections of faces (f1,f2), coordinates
|
||
|
// of points along the main diagonal (p0,p2) should match
|
||
|
static pAdjacent(f1, f2) {
|
||
|
return Point.pEquals(f1[0],f2[0])
|
||
|
&& Point.pEquals(f1[2],f2[2]);
|
||
|
}
|
||
|
};
|
||
|
```
|
||
|
{% endcapture %}
|
||
|
{%- include collapsed_block.html summary="class Cube" content=collapsed_md -%}
|