Я пытаюсь создать маленькую 3D-систему, используя учебник, который я нашел в Интернете. Учебное пособие никогда не объясняет, как вращать все объекты как группу, и у каждого объекта есть свое собственное вращение вокруг его центра.
Я пытался добавить систему группировки самостоятельно, но никогда не мог понять это. В настоящее время он просто добавляет позицию синего куба к позиции создаваемого им объекта.
const _ZOOM = 5;
var Vertex = function(x, y, z) {
this.x = parseFloat(x);
this.y = parseFloat(y);
this.z = parseFloat(z);
};
var Vertex2D = function(x, y) {
this.x = parseFloat(x);
this.y = parseFloat(y);
};
var Cube = function(center, side,colorData,groupParent) {
// Generate the vertices
var d = side / 2;
this.center = center;
this.stroke = colorData.strokeStyle;
this.fill = colorData.fillStyle;
this.center = center;
if (groupParent){
this.center.x -= groupParent.center.x;
this.center.y -= groupParent.center.y;
this.center.z -= groupParent.center.z;
}
this.vertices = [
new Vertex(center.x - d, center.y - d, center.z + d),
new Vertex(center.x - d, center.y - d, center.z - d),
new Vertex(center.x + d, center.y - d, center.z - d),
new Vertex(center.x + d, center.y - d, center.z + d),
new Vertex(center.x + d, center.y + d, center.z + d),
new Vertex(center.x + d, center.y + d, center.z - d),
new Vertex(center.x - d, center.y + d, center.z - d),
new Vertex(center.x - d, center.y + d, center.z + d)
];
if (groupParent){
for (let item in this.vertices){
let index = this.vertices[item];
index.x += groupParent.center.x;
index.y += groupParent.center.y;
index.z += groupParent.center.z;
}
}
// Generate the faces
this.faces = [
[this.vertices[0], this.vertices[1], this.vertices[2], this.vertices[3]],
[this.vertices[3], this.vertices[2], this.vertices[5], this.vertices[4]],
[this.vertices[4], this.vertices[5], this.vertices[6], this.vertices[7]],
[this.vertices[7], this.vertices[6], this.vertices[1], this.vertices[0]],
[this.vertices[7], this.vertices[0], this.vertices[3], this.vertices[4]],
[this.vertices[1], this.vertices[6], this.vertices[5], this.vertices[2]]
];
};
var Rectangle = function(center,side1,side2,colorData,groupParent) {
// Generate the vertices
var d = side1 / 2;
var d2 = side2 / 2;
this.center = center;
if (groupParent){
this.center.x -= groupParent.center.x;
this.center.y -= groupParent.center.y;
this.center.z -= groupParent.center.z;
}
this.stroke = colorData.strokeStyle;
this.fill = colorData.fillStyle;
this.vertices = [
new Vertex(center.x - d, center.y - d2, center.z + d),
new Vertex(center.x - d, center.y - d2, center.z - d),
new Vertex(center.x + d, center.y - d2, center.z - d),
new Vertex(center.x + d, center.y - d2, center.z + d),
new Vertex(center.x + d, center.y + d2, center.z + d),
new Vertex(center.x + d, center.y + d2, center.z - d),
new Vertex(center.x - d, center.y + d2, center.z - d),
new Vertex(center.x - d, center.y + d2, center.z + d)
];
if (groupParent){
for (let item in this.vertices){
let index = this.vertices[item];
index.x += groupParent.center.x;
index.y += groupParent.center.y;
index.z += groupParent.center.z;
}
}
// Generate the faces
this.faces = [
[this.vertices[0], this.vertices[1], this.vertices[2], this.vertices[3]],
[this.vertices[3], this.vertices[2], this.vertices[5], this.vertices[4]],
[this.vertices[4], this.vertices[5], this.vertices[6], this.vertices[7]],
[this.vertices[7], this.vertices[6], this.vertices[1], this.vertices[0]],
[this.vertices[7], this.vertices[0], this.vertices[3], this.vertices[4]],
[this.vertices[1], this.vertices[6], this.vertices[5], this.vertices[2]]
];
};
var Pyramid = function(center,width,height,colorData,groupParent) {
// Generate the vertices
var w = width / 2;
var h = height / 2;
this.center = center;
this.stroke = colorData.strokeStyle;
this.fill = colorData.fillStyle;
this.center = center;
if (groupParent){
this.center.x -= groupParent.center.x;
this.center.y -= groupParent.center.y;
this.center.z -= groupParent.center.z;
}
this.vertices = [
new Vertex(center.x - w, center.y + w, center.z + w),
new Vertex(center.x - w, center.y + w, center.z - w),
new Vertex(center.x + w, center.y + w, center.z - w),
new Vertex(center.x + w, center.y + w, center.z + w),
new Vertex(center.x, center.y - h, center.z),
];
if (groupParent){
for (let item in this.vertices){
let index = this.vertices[item];
index.x += groupParent.center.x;
index.y += groupParent.center.y;
index.z += groupParent.center.z;
}
}
// Generate the faces
this.faces = [
[this.vertices[1], this.vertices[0], this.vertices[4]],
[this.vertices[1], this.vertices[2], this.vertices[4]],
[this.vertices[2], this.vertices[3], this.vertices[4]],
[this.vertices[3], this.vertices[0], this.vertices[4]],
[this.vertices[1], this.vertices[2],this.vertices[3],this.vertices[0]],
];
};
var Prism = function(center,width,height,colorData,groupParent) {
// Generate the vertices
var w = width / 2;
var h = height / 2;
this.center = center;
this.stroke = colorData.strokeStyle;
this.fill = colorData.fillStyle;
this.center = center;
if (groupParent){
this.center.x -= groupParent.center.x;
this.center.y -= groupParent.center.y;
this.center.z -= groupParent.center.z;
}
this.vertices = [
new Vertex(center.x + w, center.y, center.z - w),
new Vertex(center.x - w, center.y, center.z),
new Vertex(center.x + w, center.y, center.z + w),
new Vertex(center.x + w, center.y - h, center.z - w),
new Vertex(center.x - w, center.y - h, center.z),
new Vertex(center.x + w, center.y - h, center.z + w),
];
if (groupParent){
for (let item in this.vertices){
let index = this.vertices[item];
index.x += groupParent.center.x;
index.y += groupParent.center.y;
index.z += groupParent.center.z;
}
}
// Generate the faces
this.faces = [
[this.vertices[1], this.vertices[0], this.vertices[2]],
[this.vertices[3], this.vertices[4], this.vertices[5]],
[this.vertices[1], this.vertices[4], this.vertices[3],this.vertices[0]],
[this.vertices[4], this.vertices[5], this.vertices[2],this.vertices[1]],
[this.vertices[5], this.vertices[3], this.vertices[0],this.vertices[2]],
];
};
function project(M) {
// Distance between the camera and the plane
var d = 200;
var r = d / M.y;
return new Vertex2D(r * M.x, r * M.z);
}
function render(objects, ctx, dx, dy) {
// Clear the previous frame
ctx.clearRect(0, 0, 2*dx, 2*dy);
// For each object
for (var i = 0, n_obj = objects.length; i < n_obj; ++i) {
// For each face
for (var j = 0, n_faces = objects[i].faces.length; j < n_faces; ++j) {
// Current face
var face = objects[i].faces[j];
// Draw the first vertex
ctx.fillStyle = objects[i].fill;
ctx.strokeStyle = objects[i].stroke;
var P = project(face[0]);
ctx.beginPath();
ctx.moveTo(P.x + dx, -P.y + dy);
// Draw the other vertices
for (var k = 1, n_vertices = face.length; k < n_vertices; ++k) {
P = project(face[k]);
ctx.lineTo(P.x + dx, -P.y + dy);
}
// Close the path and draw the face
ctx.closePath();
ctx.stroke();
ctx.fill();
}
}
}
(function() {
// Fix the canvas width and height
var canvas = document.getElementById('cnv');
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
var dx = canvas.width / 2;
var dy = canvas.height / 2;
// Objects style
var ctx = canvas.getContext('2d');
// Create the cube
var cube_center = new Vertex(0,10*dy/_ZOOM, 0);
var cube = new Cube(cube_center, dy,{
strokeStyle:"rgba(0,0,0,1)",
fillStyle:"rgba(0,150,255,0.3)",
});
var cube_center2 = new Vertex(0,20*dy/_ZOOM,0);
var cube2 = new Rectangle(cube_center2, dy,50,{
strokeStyle:"rgba(0,0,0,1)",
fillStyle:"rgba(255,150,0,0.3)",
});
var pyramid_center = new Vertex(dx + 150,20*dy/_ZOOM,0);
var pyramid = new Pyramid(pyramid_center,200,400,{
strokeStyle:"rgba(0,0,0,1)",
fillStyle:"rgba(255,255,0,0.3)",
},cube);
var prism_center = new Vertex(0,20*dy/_ZOOM,0);
var prism = new Prism(prism_center,200,400,{
strokeStyle:"rgba(0,0,0,1)",
fillStyle:"rgba(255,255,0,0.3)",
});
var objects = [cube,cube2,pyramid,prism];
// First render
render(objects, ctx, dx, dy);
// Events
var mousedown = false;
var mx = 0;
var my = 0;
canvas.addEventListener('mousedown', initMove);
document.addEventListener('mousemove', move);
document.addEventListener('mouseup', stopMove);
// Rotate a vertice
function rotate(M, center, theta, phi) {
// Rotation matrix coefficients
var ct = Math.cos(theta);
var st = Math.sin(theta);
var cp = Math.cos(phi);
var sp = Math.sin(phi);
// Rotation
var x = M.x - center.x;
var y = M.y - center.y;
var z = M.z - center.z;
M.x = ct * x - st * cp * y + st * sp * z + center.x;
M.y = st * x + ct * cp * y - ct * sp * z + center.y;
M.z = sp * y + cp * z + center.z;
}
// Initialize the movement
function initMove(evt) {
clearTimeout(autorotate_timeout);
mousedown = true;
mx = evt.clientX;
my = evt.clientY;
}
function rotObjs(objs,theta,phi){
for (let item in objs){
let index = objs[item];
for (var i = 0; i < index.vertices.length; ++i){
rotate(index.vertices[i],index.center, theta, phi);
}
}
}
function move(evt) {
if (mousedown) {
var theta = (evt.clientX - mx) * Math.PI / 360;
var phi = (evt.clientY - my) * Math.PI / 360;
rotObjs(objects,theta,phi);
mx = evt.clientX;
my = evt.clientY;
render(objects, ctx, dx, dy);
}
}
function stopMove() {
mousedown = false;
autorotate_timeout = setTimeout(autorotate, 2000);
}
function autorotate() {
let theta = -Math.PI / 720;
let phi = Math.PI / 720;
rotObjs(objects,theta,phi);
render(objects, ctx, dx, dy);
autorotate_timeout = setTimeout(autorotate,30);
}
autorotate_timeout = setTimeout(autorotate, 2000);
})();```