170 lines
3.8 KiB
JavaScript
170 lines
3.8 KiB
JavaScript
async function main() {
|
|
const canvas = document.getElementById("canvas");
|
|
|
|
/**
|
|
* @type RenderingContext
|
|
*/
|
|
const ctx = canvas.getContext("2d");
|
|
|
|
const { models, scene } = await fetch("test.json").then(res => res.json());
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
main();
|
|
|
|
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);
|
|
}
|
|
}
|