rg/naloga_1/script.js
2024-10-23 15:43:29 +02:00

177 lines
4.2 KiB
JavaScript

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const dropZone = document.getElementById("drop-zone");
dropZone.addEventListener("dragenter", (e) => e.preventDefault());
dropZone.addEventListener("dragover", (e) => e.preventDefault());
dropZone.addEventListener("drop", (e) => {
const reader = new FileReader();
reader.addEventListener("loadend", async () => {
const data = JSON.parse(reader.result);
await run(data);
});
reader.readAsText(e.dataTransfer.files[0]);
e.preventDefault();
});
async function run({ models, scene }) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const { model, transforms } of scene) {
let m = Matrix.identity();
for (const transform of transforms) {
switch (transform.type) {
case "scale":
m = m.scale(transform.factor[0], transform.factor[1]);
break;
case "rotate":
m = m.rotate(transform.angle);
break;
case "translate":
m = m.translate(transform.vector[0], transform.vector[1]);
break;
}
}
m = m.screen(0, 0, canvas.width, canvas.height);
for (const line of models[model]) {
const p1 = m.multiply(Matrix.vector(...line[0], 1));
ctx.moveTo(p1.vector[0], p1.vector[1]);
const p2 = m.multiply(Matrix.vector(...line[1], 1));
ctx.lineTo(p2.vector[0], p2.vector[1]);
}
ctx.stroke();
}
}
class Matrix extends Array {
/**
* @param {number[][]} matrix
*/
constructor(matrix) {
super();
this.push(...matrix);
}
static identity() {
return new Matrix([
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
]);
}
/**
* @param {number} vector
*/
static vector(...vector) {
const matrix = Array(vector.length);
for (let i = 0; i < vector.length; i++) {
matrix[i] = [vector[i]];
}
return new Matrix(matrix);
}
get width() {
if (this.length === 0) {
return 0;
}
return this[0].length;
}
get height() {
return this.length;
}
get vector() {
const v = [];
for (const l of this) {
v.push(l[0]);
}
return v;
}
toString() {
let str = "";
for (const line of this) {
str += line.join(", ") + "\n";
}
return str;
}
/**
* @param {Matrix} matrix
*/
multiply(matrix) {
if (this.width !== matrix.height) {
throw Error("Can not multiply");
}
const m = Array.from({ length: this.height },
() => Array.from(
{ length: matrix.width }, () => 0,
),
);
for (let i = 0; i < m.length; i++) {
for (let j = 0; j < m[0].length; j++) {
let sum = 0;
for (let k = 0; k < this.width; k++) {
sum += this[i][k] * matrix[k][j];
}
m[i][j] = sum;
}
}
return new Matrix(m);
}
/**
* @param {number} x
* @param {number} y
*/
scale(x, y) {
return new Matrix([
[x, 0, 0],
[0, y, 0],
[0, 0, 1],
]).multiply(this);
}
/**
* @param {number} angle
*/
rotate(angle) {
return new Matrix([
[Math.cos(angle), -Math.sin(angle), 0],
[Math.sin(angle), Math.cos(angle), 0],
[0, 0, 1],
]).multiply(this);
}
translate(x, y) {
return new Matrix([
[1, 0, x],
[0, 1, y],
[0, 0, 1],
]).multiply(this);
}
/**
* @param {number} x0
* @param {number} y0
* @param {number} x1
* @param {number} y1
*/
screen(x0, y0, x1, y1) {
return new Matrix([
[(x1 - x0) / 2, 0, (x0 + x1) / 2],
[0, -(y1 - y0) / 2, (y0 + y1) / 2],
[0, 0, 1],
]).multiply(this);
}
}