function initializeContext(canvasId: string): WebGL2RenderingContext | null { const canvas = document.getElementById(canvasId) as HTMLCanvasElement; const ctx = canvas.getContext("webgl2", {antialias: false}); return ctx; } type Color = [number, number, number, number] // TODO: Make all vectors follow one interface interface Vector { } class Vec2 { x: number; y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } add(other: Vec2) { this.x += other.x; this.y += other.y; } addScalar(scalar: number) { this.x += scalar; this.y += scalar; } addNew(other: Vec2): Vec2 { return new Vec2(this.x + other.x, this.y + other.y); } addScalarNew(scalar: number): Vec2 { return new Vec2(this.x + scalar, this.y + scalar); } sub(other: Vec2) { this.x -= other.x; this.y -= other.y; } mult(scalar: number) { this.x *= scalar; this.y *= scalar; } multNew(scalar: number): Vec2 { return new Vec2(this.x * scalar, this.y * scalar); } splatToArray(): Array { return [this.x, this.y]; } static angle(angle: number): Vec2 { 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); } } class Vec3 { x: number; y: number; z: number; constructor(x: number, y: number, z: number) { this.x = x; this.y = y; this.z = z; } add(other: Vec3) { this.x += other.x; this.y += other.y; this.z += other.z; } sub(other: Vec3) { this.x -= other.x; this.y -= other.y; this.z -= other.z; } multScalar(scalar: number) { this.x *= scalar; this.y *= scalar; this.z *= scalar; } multScalarNew(scalar: number): Vec3 { let vec = new Vec3(this.x, this.y, this.z); vec.x *= scalar; vec.y *= scalar; vec.z *= scalar; return vec; } splatToArray(): Array { return [this.x, this.y, this.z]; } } class Vec4 { x: number; y: number; z: number; w: number; constructor(x: number, y: number, z: number, w: number) { this.x = x; this.y = y; this.z = z; this.w = w; } add(other: Vec4) { this.x += other.x; this.y += other.y; this.z += other.z; this.w += other.w; } sub(other: Vec4) { this.x -= other.x; this.y -= other.y; this.z -= other.z; this.w -= other.w; } multScalar(scalar: number) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; } multScalarNew(scalar: number): Vec4 { let vec = new Vec4(this.x, this.y, this.z, this.w); vec.x *= scalar; vec.y *= scalar; vec.z *= scalar; vec.w *= scalar; return vec; } div(other: Vec4) { 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: Vec4): Vec4 { if ( other.x == 0 || other.y == 0 || other.z == 0 || other.w == 0) { throw new Error("Division by zero in Vec4"); } let vec = new Vec4(this.x, this.y, this.z, this.w); vec.x /= other.x; vec.y /= other.y; vec.z /= other.z; vec.w /= other.w; return vec; } reduce(): Vec3 { return new Vec3(this.x, this.y, this.z); } } type Mat4Init = number | Float32Array; type Mat4Row = 0 | 1 | 2 | 3 type Mat4Col = Mat4Row type Mat4X = Mat4Row type Mat4Y = Mat4Row type Mat4Z = Mat4Row type Mat4W = Mat4Row class Mat4 { data: Float32Array; constructor(i: Mat4Init) { if (i instanceof Float32Array) { this.data = i; return; } else if (typeof(i) === 'number') { this.data = new Float32Array(16).fill(i); return; } this.data = new Float32Array(16); } static orthographic(left: number, right: number, bottom: number, top: number, near: number, far: number): Mat4 { 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(): Mat4 { let m = new Mat4(new Float32Array([ 1, -1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, ])); return m; } static rotation_x(angle: number): Mat4 { 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: number): Mat4 { 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: number): Mat4 { 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: Vec3): Mat4 { return new Mat4(new Float32Array([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, t.z, 1, ])); } x(n: Mat4X) { return this.data[n]; } y(n: Mat4Y) { return this.data[n + 4]; } z(n: Mat4Z) { return this.data[n + 8]; } w(n: Mat4W) { return this.data[n + 12]; } splat() { return Array.from(this.data); } row(row: Mat4Row, data: [number, number, number, number]) { for (let i = 0; i < data.length; ++i) this.data[i + 4 * row] = data[i]; } col(col: Mat4Col, data: [number, number, number, number]) { for (let i = 0; i < data.length; ++i) this.data[i*4 + col] = data[i]; } transform(v: Vec4) { let x = v.x * this.x(0) + v.x * this.x(1) + v.x * this.x(2) + v.x * this.x(3); let y = v.y * this.y(0) + v.y * this.y(1) + v.y * this.y(2) + v.y * this.y(3); let z = v.z * this.z(0) + v.z * this.z(1) + v.z * this.z(2) + v.z * this.z(3); let w = v.w * this.w(0) + v.w * this.w(1) + v.w * this.w(2) + v.w * this.w(3); v.x = x; v.y = y; v.z = z; v.w = w; } transformNew(v: Vec4): Vec4 { let vec = new Vec4(v.x, v.y, v.z, v.w); let x = v.x * this.x(0) + v.y * this.x(1) + v.z * this.x(2) + v.w * this.x(3); let y = v.x * this.y(0) + v.y * this.y(1) + v.z * this.y(2) + v.w * this.y(3); let z = v.x * this.z(0) + v.y * this.z(1) + v.z * this.z(2) + v.w * this.z(3); let w = v.x * this.w(0) + v.y * this.w(1) + v.z * this.w(2) + v.w * this.w(3); vec.x = x; vec.y = y; vec.z = z; vec.w = w; return vec; } multNew(other: Mat4): Mat4 { 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: number) { for (let i = 0; i < this.data.length; ++i) { this.data[i] *= scalar; } } } export { initializeContext, Mat4, Vec2, Vec3, Vec4, Color };