3D Projection Engine



              const BACKGROUND = "black";
              const FOREGROUND = "green";
              const FPS = 60;
              game.width = 600;
              game.height = 800;

              const ctx = game.getContext("2d");
              console.log({ ctx });
              function clear() {
                ctx.fillStyle = BACKGROUND;
                ctx.fillRect(0, 0, game.width, game.height);
              }
              function point(size = 20, { x, y }) {
                ctx.fillStyle = FOREGROUND;
                ctx.fillRect(x - size / 2, y - size / 2, size, size);
              }

              function screen(p) {
                // -1..1 => 0.ww/h
                return {
                  x: ((p.x + 1) / 2) * game.width,
                  y: (1 - (p.y + 1) / 2) * game.height,
                };
              }

              function project({ x, y, z }) {
                return {
                  x: x / z,
                  y: y / z,
                };
              }
              const vs = [
                { x: 0.25, y: 0.25, z: 0.5 },
                { x: -0.25, y: 0.25, z: 0.5 },
                { x: -0.25, y: -0.25, z: 0.5 },
                { x: 0.25, y: -0.25, z: 0.5 },

                { x: 0.25, y: 0.25, z: -0.5 },
                { x: -0.25, y: 0.25, z: -0.5 },
                { x: -0.25, y: -0.25, z: -0.5 },
                { x: 0.25, y: -0.25, z: -0.5 },
              ];

              const faces = [
                [0, 1, 2, 3],
                [4, 5, 6, 7],
                [0, 4],
                [1, 5],
                [2, 6],
                [3, 7],
              ];

              function translateZ({ x, y, z }, dz) {
                return { x: x, y: y, z: z + dz };
              }

              function rotate_xz({ x, y, z }, angle) {
                let x_dash = x * Math.cos(angle) - z * Math.sin(angle);
                let z_dash = z * Math.cos(angle) + x * Math.sin(angle);
                return { x: x_dash, y, z: z_dash };
              }

              function line(p1, p2) {
                ctx.strokeStyle = FOREGROUND;
                ctx.lineWidth = 3;
                ctx.beginPath();
                ctx.moveTo(p1.x, p1.y);
                ctx.lineTo(p2.x, p2.y);
                ctx.stroke();
              }

              let dz = 1;
              let angle = 0;

              function frame() {
                const dt = 1 / FPS;
                //dz += 1 * dt;
                angle += Math.PI * dt;
                clear();
                //  for (const v of vs) {
                //  point(20, screen(project(translateZ(rotate_xz(v, angle), dz))));
                //}
                for (const face of faces) {
                  for (let i = 0; i < face.length; i++) {
                    const a = vs[face[i]];
                    const b = vs[face[(i + 1) % face.length]];
                    line(
                      screen(project(translateZ(rotate_xz(a, angle), dz))),
                      screen(project(translateZ(rotate_xz(b, angle), dz))),
                    );
                  }
                }
                setTimeout(frame, 1000 / FPS);
              }

              setTimeout(frame, 1000 / FPS);
              ashish@1:~$