commit ff56a51b8238da67e31f9d03c122e04a412aa6d8 Author: Gašper Dobrovoljc Date: Wed Oct 23 15:24:56 2024 +0200 Naloga 1 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..ff175f5 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..581c216 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..68442d8 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/rg.iml b/.idea/rg.iml new file mode 100644 index 0000000..24643cc --- /dev/null +++ b/.idea/rg.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/naloga_1/.prettierrc b/naloga_1/.prettierrc new file mode 100644 index 0000000..2105c60 --- /dev/null +++ b/naloga_1/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": true, + "trailingComma": "all", + "tabWidth": 4 +} \ No newline at end of file diff --git a/naloga_1/index.html b/naloga_1/index.html new file mode 100644 index 0000000..cf333de --- /dev/null +++ b/naloga_1/index.html @@ -0,0 +1,13 @@ + + + + + + Domača Naloga 1 + + + + + + + \ No newline at end of file diff --git a/naloga_1/script.js b/naloga_1/script.js new file mode 100644 index 0000000..10254e4 --- /dev/null +++ b/naloga_1/script.js @@ -0,0 +1,169 @@ +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); + } +} diff --git a/naloga_1/test.json b/naloga_1/test.json new file mode 100644 index 0000000..23ced03 --- /dev/null +++ b/naloga_1/test.json @@ -0,0 +1,198 @@ +{ + "models": [ + [ + [[-1, -1], [1, -1]], + [[1, -1], [0, 1]], + [[0, 1], [-1, -1]] + ], + [ + [[0, 1], [-0.5878, -0.8090]], + [[-0.5878, -0.8090], [0.9511, 0.3090]], + [[0.9511, 0.3090], [-0.9511, 0.3090]], + [[-0.9511, 0.3090], [0.5878, -0.8090]], + [[0.5878, -0.8090], [0, 1]] + ] + ], + "scene": [ + { + "model": 0, + "transforms": [ + { "type": "scale", "factor": [0.01, 0.01] }, + { "type": "rotate", "angle": 1 }, + { "type": "translate", "vector": [0.05, 0] }, + { "type": "rotate", "angle": 0.5 } + ] + }, + { + "model": 1, + "transforms": [ + { "type": "scale", "factor": [0.02, 0.02] }, + { "type": "rotate", "angle": 2 }, + { "type": "translate", "vector": [0.1, 0] }, + { "type": "rotate", "angle": 1 } + ] + }, + { + "model": 0, + "transforms": [ + { "type": "scale", "factor": [0.03, 0.03] }, + { "type": "rotate", "angle": 3 }, + { "type": "translate", "vector": [0.15, 0] }, + { "type": "rotate", "angle": 1.5 } + ] + }, + { + "model": 1, + "transforms": [ + { "type": "scale", "factor": [0.04, 0.04] }, + { "type": "rotate", "angle": 4 }, + { "type": "translate", "vector": [0.2, 0] }, + { "type": "rotate", "angle": 2 } + ] + }, + { + "model": 0, + "transforms": [ + { "type": "scale", "factor": [0.05, 0.05] }, + { "type": "rotate", "angle": 5 }, + { "type": "translate", "vector": [0.25, 0] }, + { "type": "rotate", "angle": 2.5 } + ] + }, + { + "model": 1, + "transforms": [ + { "type": "scale", "factor": [0.06, 0.06] }, + { "type": "rotate", "angle": 6 }, + { "type": "translate", "vector": [0.3, 0] }, + { "type": "rotate", "angle": 3 } + ] + }, + { + "model": 0, + "transforms": [ + { "type": "scale", "factor": [0.07, 0.07] }, + { "type": "rotate", "angle": 7 }, + { "type": "translate", "vector": [0.35, 0] }, + { "type": "rotate", "angle": 3.5 } + ] + }, + { + "model": 1, + "transforms": [ + { "type": "scale", "factor": [0.08, 0.08] }, + { "type": "rotate", "angle": 8 }, + { "type": "translate", "vector": [0.4, 0] }, + { "type": "rotate", "angle": 4 } + ] + }, + { + "model": 0, + "transforms": [ + { "type": "scale", "factor": [0.09, 0.09] }, + { "type": "rotate", "angle": 9 }, + { "type": "translate", "vector": [0.45, 0] }, + { "type": "rotate", "angle": 4.5 } + ] + }, + { + "model": 1, + "transforms": [ + { "type": "scale", "factor": [0.1, 0.1] }, + { "type": "rotate", "angle": 10 }, + { "type": "translate", "vector": [0.5, 0] }, + { "type": "rotate", "angle": 5 } + ] + }, + { + "model": 0, + "transforms": [ + { "type": "scale", "factor": [0.11, 0.11] }, + { "type": "rotate", "angle": 11 }, + { "type": "translate", "vector": [0.55, 0] }, + { "type": "rotate", "angle": 5.5 } + ] + }, + { + "model": 1, + "transforms": [ + { "type": "scale", "factor": [0.12, 0.12] }, + { "type": "rotate", "angle": 12 }, + { "type": "translate", "vector": [0.6, 0] }, + { "type": "rotate", "angle": 6 } + ] + }, + { + "model": 0, + "transforms": [ + { "type": "scale", "factor": [0.13, 0.13] }, + { "type": "rotate", "angle": 13 }, + { "type": "translate", "vector": [0.65, 0] }, + { "type": "rotate", "angle": 6.5 } + ] + }, + { + "model": 1, + "transforms": [ + { "type": "scale", "factor": [0.14, 0.14] }, + { "type": "rotate", "angle": 14 }, + { "type": "translate", "vector": [0.7, 0] }, + { "type": "rotate", "angle": 7 } + ] + }, + { + "model": 0, + "transforms": [ + { "type": "scale", "factor": [0.15, 0.15] }, + { "type": "rotate", "angle": 15 }, + { "type": "translate", "vector": [0.75, 0] }, + { "type": "rotate", "angle": 7.5 } + ] + }, + { + "model": 1, + "transforms": [ + { "type": "scale", "factor": [0.16, 0.16] }, + { "type": "rotate", "angle": 16 }, + { "type": "translate", "vector": [0.8, 0] }, + { "type": "rotate", "angle": 8 } + ] + }, + { + "model": 0, + "transforms": [ + { "type": "scale", "factor": [0.17, 0.17] }, + { "type": "rotate", "angle": 17 }, + { "type": "translate", "vector": [0.85, 0] }, + { "type": "rotate", "angle": 8.5 } + ] + }, + { + "model": 1, + "transforms": [ + { "type": "scale", "factor": [0.18, 0.18] }, + { "type": "rotate", "angle": 18 }, + { "type": "translate", "vector": [0.9, 0] }, + { "type": "rotate", "angle": 9 } + ] + }, + { + "model": 0, + "transforms": [ + { "type": "scale", "factor": [0.19, 0.19] }, + { "type": "rotate", "angle": 19 }, + { "type": "translate", "vector": [0.95, 0] }, + { "type": "rotate", "angle": 9.5 } + ] + }, + { + "model": 1, + "transforms": [ + { "type": "scale", "factor": [0.2, 0.2] }, + { "type": "rotate", "angle": 20 }, + { "type": "translate", "vector": [1, 0] }, + { "type": "rotate", "angle": 10 } + ] + } + ] +}