export function initializeContext(canvasId) { const canvas = document.getElementById(canvasId); const ctx = canvas.getContext("webgl2", { antialias: false }); return ctx; } export class Vec2 { x; y; constructor(x, y) { this.x = x; this.y = y; } static ZERO() { return new Vec2(0, 0); } static ID() { return new Vec2(1, 1); } copy() { return new Vec2(this.x, this.y); } add(other) { this.x += other.x; this.y += other.y; } addScalar(scalar) { this.x += scalar; this.y += scalar; } addNew(other) { return new Vec2(this.x + other.x, this.y + other.y); } addScalarNew(scalar) { return new Vec2(this.x + scalar, this.y + scalar); } sub(other) { this.x -= other.x; this.y -= other.y; } subNew(other) { let vec = this.copy(); vec.x -= other.x; vec.y -= other.y; return vec; } multScalar(scalar) { this.x *= scalar; this.y *= scalar; } multScalarNew(scalar) { return new Vec2(this.x * scalar, this.y * scalar); } div(other) { if (other.x == 0 || other.y == 0) { throw new Error("Division by zero in Vec4"); } this.x /= other.x; this.y /= other.y; } divNew(other) { if (other.x == 0 || other.y == 0) { throw new Error("Division by zero in Vec4"); } let vec = this.copy(); vec.x /= other.x; vec.y /= other.y; return vec; } reduce() { throw new Error("Can't reduce Vec2!"); } extend(value) { return new Vec3(this.x, this.y, value); } splatToArray() { return [this.x, this.y]; } static angle(angle) { const eps = 1e-6; let x = Math.cos(angle); let y = Math.sin(angle); if ((x > 0 && x < eps) || (x < 0 && x > -eps)) x = 0; if ((y > 0 && y < eps) || (y < 0 && y > -eps)) y = 0; return new Vec2(x, y); } } export class Vec3 { x; y; z; constructor(x, y, z) { this.x = x; this.y = y; this.z = z; } addScalar(scalar) { this.x += scalar; this.y += scalar; this.z += scalar; } addScalarNew(scalar) { let vec = this.copy(); vec.x += scalar; vec.y += scalar; vec.z += scalar; return vec; } static ZERO() { return new Vec3(0, 0, 0); } static ID() { return new Vec3(1, 1, 1); } copy() { return new Vec3(this.x, this.y, this.z); } add(other) { this.x += other.x; this.y += other.y; this.z += other.z; } addNew(other) { let vec = this.copy(); vec.x += other.x; vec.y += other.y; vec.z += other.z; return vec; } sub(other) { this.x -= other.x; this.y -= other.y; this.z -= other.z; } subNew(other) { let vec = this.copy(); vec.x -= other.x; vec.y -= other.y; vec.z -= other.z; return vec; } multScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; } multScalarNew(scalar) { let vec = this.copy(); vec.x *= scalar; vec.y *= scalar; vec.z *= scalar; return vec; } div(other) { if (other.x == 0 || other.y == 0 || other.z == 0) { throw new Error("Division by zero in Vec4"); } this.x /= other.x; this.y /= other.y; this.z /= other.z; } divNew(other) { if (other.x == 0 || other.y == 0 || other.z == 0) { throw new Error("Division by zero in Vec4"); } let vec = this.copy(); vec.x /= other.x; vec.y /= other.y; vec.z /= other.z; return vec; } reduce() { return new Vec2(this.x, this.y); } extend(value) { return new Vec4(this.x, this.y, this.z, value); } splatToArray() { return [this.x, this.y, this.z]; } } export class Vec4 { x; y; z; w; constructor(x, y, z, w) { this.x = x; this.y = y; this.z = z; this.w = w; } static ZERO() { return new Vec4(0, 0, 0, 0); } static ID() { return new Vec4(1, 1, 1, 1); } copy() { return new Vec4(this.x, this.y, this.z, this.w); } add(other) { this.x += other.x; this.y += other.y; this.z += other.z; this.w += other.w; } addNew(other) { let vec = this.copy(); vec.x += other.x; vec.y += other.y; vec.z += other.z; vec.w += other.w; return vec; } addScalar(scalar) { this.x += scalar; this.y += scalar; this.z += scalar; this.w += scalar; } addScalarNew(scalar) { let vec = this.copy(); vec.x += scalar; vec.y += scalar; vec.z += scalar; vec.w += scalar; return vec; } sub(other) { this.x -= other.x; this.y -= other.y; this.z -= other.z; this.w -= other.w; } subNew(other) { let vec = this.copy(); vec.x -= other.x; vec.y -= other.y; vec.z -= other.z; vec.w -= other.w; return vec; } multScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; } multScalarNew(scalar) { let vec = this.copy(); vec.x *= scalar; vec.y *= scalar; vec.z *= scalar; vec.w *= scalar; return vec; } div(other) { if (other.x == 0 || other.y == 0 || other.z == 0 || other.w == 0) { throw new Error("Division by zero in Vec4"); } this.x /= other.x; this.y /= other.y; this.z /= other.z; this.w /= other.w; } divNew(other) { if (other.x == 0 || other.y == 0 || other.z == 0 || other.w == 0) { throw new Error("Division by zero in Vec4"); } let vec = this.copy(); vec.x /= other.x; vec.y /= other.y; vec.z /= other.z; vec.w /= other.w; return vec; } reduce() { return new Vec3(this.x, this.y, this.z); } extend(_value) { throw new Error("Can't extend Vec4"); } } export class Mat4 { data; constructor(i) { if (i instanceof Float32Array) { console.assert(i.length == 16, "Mat4 has to have 16 elements"); this.data = i; return; } else if (typeof (i) === 'number') { this.data = new Float32Array(16).fill(i); return; } this.data = new Float32Array(16); } static IDENTITY() { return new Mat4(new Float32Array([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, ])); } static orthographic(left, right, bottom, top, near, far) { let data = new Float32Array([ 2 / (right - left), 0, 0, 0, 0, 2 / (top - bottom), 0, 0, 0, 0, -2 / (far - near), 0, -(right + left) / (right - left), -(top + bottom) / (top - bottom), -(far + near) / (far - near), 1, ]); return new Mat4(data); } static isometric() { return new Mat4(new Float32Array([ 1, -1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, ])); } static isometric_inverse() { return new Mat4(new Float32Array([ 0.5, -0.5, 0, 0, 0.5, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, ])); } static rotation_x(angle) { let data = new Float32Array([ 1, 0, 0, 0, 0, Math.cos(angle), -Math.sin(angle), 0, 0, Math.sin(angle), Math.cos(angle), 0, 0, 0, 0, 1, ]); return new Mat4(data); } static rotation_y(angle) { let data = new Float32Array([ Math.cos(angle), 0, Math.sin(angle), 0, 0, 1, 0, 0, -Math.sin(angle), 0, Math.cos(angle), 0, 0, 0, 0, 1, ]); return new Mat4(data); } static rotation_z(angle) { let data = new Float32Array([ Math.cos(angle), -Math.sin(angle), 0, 0, Math.sin(angle), Math.cos(angle), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, ]); return new Mat4(data); } static translate(t) { return new Mat4(new Float32Array([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, t.z, 1, ])); } static scale(scales) { return new Mat4(new Float32Array([ scales.x, 0, 0, 0, 0, scales.y, 0, 0, 0, 0, scales.z, 0, 0, 0, 0, 1, ])); } x(n) { return this.data[n]; } y(n) { return this.data[n + 4]; } z(n) { return this.data[n + 8]; } w(n) { return this.data[n + 12]; } splat() { return Array.from(this.data); } row(row, data) { for (let i = 0; i < data.length; ++i) this.data[i + 4 * row] = data[i]; } col(col, data) { for (let i = 0; i < data.length; ++i) this.data[i * 4 + col] = data[i]; } transform(v) { let x = v.x * this.x(0) + v.y * this.y(0) + v.z * this.z(0) + v.w * this.w(0); let y = v.x * this.x(1) + v.y * this.y(1) + v.z * this.z(1) + v.w * this.w(1); let z = v.x * this.x(2) + v.y * this.y(2) + v.z * this.z(2) + v.w * this.w(2); let w = v.x * this.x(3) + v.y * this.y(3) + v.z * this.z(3) + v.w * this.w(3); v.x = x; v.y = y; v.z = z; v.w = w; } transformNew(v) { let vec = v.copy(); let x = v.x * this.x(0) + v.y * this.y(0) + v.z * this.z(0) + v.w * this.w(0); let y = v.x * this.x(1) + v.y * this.y(1) + v.z * this.z(1) + v.w * this.w(1); let z = v.x * this.x(2) + v.y * this.y(2) + v.z * this.z(2) + v.w * this.w(2); let w = v.x * this.x(3) + v.y * this.y(3) + v.z * this.z(3) + v.w * this.w(3); vec.x = x; vec.y = y; vec.z = z; vec.w = w; return vec; } multNew(other) { let m = new Mat4(0); for (let i = 0; i < 4; ++i) { for (let j = 0; j < 4; ++j) { m.data[(i * 4) + j] += this.data[(i * 4) + 0] * other.data[j + 0]; m.data[(i * 4) + j] += this.data[(i * 4) + 1] * other.data[j + 4]; m.data[(i * 4) + j] += this.data[(i * 4) + 2] * other.data[j + 8]; m.data[(i * 4) + j] += this.data[(i * 4) + 3] * other.data[j + 12]; } } return m; } multScalar(scalar) { for (let i = 0; i < this.data.length; ++i) { this.data[i] *= scalar; } } }