diff --git a/package-lock.json b/package-lock.json index 6b02501..1a1b28e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "sr-puzzlegen", - "version": "1.0.0", + "version": "1.0.3-beta.22", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3342,11 +3342,6 @@ "assert-plus": "^1.0.0" } }, - "gl-matrix": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.3.0.tgz", - "integrity": "sha512-COb7LDz+SXaHtl/h4LeaFcNdJdAQSDeVqjiIihSXNrkWObZLhDI4hIkZC11Aeqp7bcE72clzB0BnDXr2SmslRA==" - }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", diff --git a/package.json b/package.json index 94bb61a..8b50835 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sr-puzzlegen", - "version": "1.0.3-beta.22", + "version": "1.0.3-beta.23", "description": "", "main": "dist/lib/index.js", "types": "dist/lib/index.d.ts", @@ -32,9 +32,7 @@ "webpack-cli": "^4.3.1", "webpack-dev-server": "^4.0.0-beta.0" }, - "dependencies": { - "gl-matrix": "^3.3.0" - }, + "dependencies": {}, "repository": { "type": "git", "url": "git://github.com/tdecker91/puzzle-visualizer.git" diff --git a/src/algorithms/cube.ts b/src/algorithms/cube.ts index 51ef526..c622975 100644 --- a/src/algorithms/cube.ts +++ b/src/algorithms/cube.ts @@ -98,7 +98,9 @@ function getSlices(rawSlices, outerBlockIndicator): number { return intValue; } - throw new Error(`Invalid outer block move (${intValue}) must be greater than 1`); + throw new Error( + `Invalid outer block move (${intValue}) must be greater than 1` + ); } } diff --git a/src/demos/cameraValues/cameraValues.ts b/src/demos/cameraValues/cameraValues.ts index bd6ddcb..b2b6078 100644 --- a/src/demos/cameraValues/cameraValues.ts +++ b/src/demos/cameraValues/cameraValues.ts @@ -2,7 +2,6 @@ import { MegaminxSimulator } from './../../simulator/megaminx/megaminxSimulator' import { PINK, LIGHT_YELLOW, GREY, LIGHT_GREEN, PURPLE, DARK_BLUE } from './../../puzzles/colors'; import { PyraminxSimulator } from '../../simulator/pyraminx/pyraminxSimulator'; import { RED, BLUE, WHITE, ORANGE, GREEN } from '../../puzzles/colors'; -import { mat4 } from 'gl-matrix'; import { HtmlSvgRenderer } from '../../rendering/htmlSvgRenderer'; import { Square1Net } from '../../puzzles/square1/square1Net'; import { Square1 } from '../../puzzles/square1/square1'; @@ -19,6 +18,7 @@ import { Group } from '../../geometry/group'; import { Camera } from '../../rendering/camera'; import { Scene } from '../../rendering/scene'; import { YELLOW } from '../../puzzles/colors'; +import { Matrix4 } from '../../math/matrix'; let camera: Camera = new Camera(); let g: Group = new Group(); @@ -48,7 +48,7 @@ let strokeWidth: number = .02; function setInputs() { if (camera && camera.matrix) { - camera.matrix.forEach((value, index) => { + camera.matrix.values.forEach((value, index) => { (document.getElementById(`c${index + 1}`)).value = value; }); } @@ -83,7 +83,7 @@ export function getInputs() { let m15 = parseFloat((document.getElementById(`c15`)).value); let m16 = parseFloat((document.getElementById(`c16`)).value); - camera.matrix = mat4.fromValues(m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16) + camera.matrix = Matrix4.fromValues(m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16) width = parseFloat((document.getElementById(`width`)).value); height = parseFloat((document.getElementById(`height`)).value); @@ -193,10 +193,10 @@ export function renderDemo() { let downX = 0; let downY = 0; - let identity = mat4.create(); - let accRot = mat4.create(); - let xRot = mat4.create(); - let yRot = mat4.create(); + let identity = new Matrix4(); + let accRot = new Matrix4(); + let xRot = new Matrix4(); + let yRot = new Matrix4(); renderer.domElement.addEventListener('mousedown', e => { down = true; @@ -221,11 +221,11 @@ export function renderDemo() { const [diffX, diffY] = [e.x - downX, e.y - downY]; [downX, downY] = [e.x, e.y]; - mat4.fromRotation(xRot, diffY/128, [1,0,0]); - mat4.fromRotation(yRot, diffX/128, [0,1,0]); + xRot = Matrix4.fromXRotation(diffY / 128); + yRot = Matrix4.fromYRotation(diffX / 128); - mat4.multiply(g.matrix, g.matrix, xRot); - mat4.multiply(g.matrix, g.matrix, yRot); + g.matrix.multiply(xRot); + g.matrix.multiply(yRot); window.requestAnimationFrame(() => renderer.render(scene, camera)); } @@ -263,7 +263,7 @@ export function svgStep() { ].forEach(puzzle => { if (puzzle && puzzle.group) { - puzzle.group.rotate(Math.PI/32, [1,1, 0]); + puzzle.group.rotate(Math.PI / 32, 1, 1, 0); } }); diff --git a/src/demos/mouseRotation/mouseRotation.ts b/src/demos/mouseRotation/mouseRotation.ts index 7e4eeaf..cddef0c 100644 --- a/src/demos/mouseRotation/mouseRotation.ts +++ b/src/demos/mouseRotation/mouseRotation.ts @@ -1,9 +1,10 @@ -import { mat4 } from 'gl-matrix'; import { HtmlSvgRenderer } from './../../rendering/htmlSvgRenderer'; import { Group } from "../../geometry/group"; import { RubiksCube } from "../../puzzles/rubiksCube/rubiksCube"; import { Camera } from "../../rendering/camera"; import { Scene } from "../../rendering/scene"; +import { createSquare1 } from '../../visualizer/puzzleCreator'; +import { Matrix4 } from '../../math/matrix'; let renderer: HtmlSvgRenderer; let camera: Camera; @@ -60,7 +61,7 @@ function throttle(callback, interval) { document.addEventListener("DOMContentLoaded", function (event) { renderDemo(); - let originalMat: mat4 = mat4.create(); + let originalMat: Matrix4 = new Matrix4(); let frameOut: HTMLSpanElement = document.getElementById("frame-out"); let start = new Date().getTime(); @@ -68,7 +69,7 @@ document.addEventListener("DOMContentLoaded", function (event) { renderer.svgElement.addEventListener('mousedown', e => { mouseDown = true; start = new Date().getTime(); - mat4.copy(originalMat, rotationGroup.matrix); + Matrix4.copy(originalMat, rotationGroup.matrix); x = e.x; y = e.y; }); @@ -82,18 +83,16 @@ document.addEventListener("DOMContentLoaded", function (event) { start = new Date().getTime(); const [diffX, diffY] = [e.x - x, e.y - y]; - let xRotation = mat4.fromXRotation(mat4.create(), diffY/128); - let yRotation = mat4.fromYRotation(mat4.create(), diffX/128); + let xRotation = Matrix4.fromXRotation(diffY / 128); + let yRotation = Matrix4.fromYRotation(diffX / 128); // Matrix to accumulate x and y rotations from mouse movements - let acc: mat4 = mat4.create(); + let acc: Matrix4 = new Matrix4(); - mat4.multiply(acc, acc, xRotation); - mat4.multiply(acc, acc, yRotation); + acc.multiply(xRotation); + acc.multiply(yRotation); - rotationGroup.matrix = mat4.multiply(rotationGroup.matrix, acc, originalMat); - - valuesElement.innerHTML = mat4String(rotationGroup.matrix); + Matrix4.multiply(rotationGroup.matrix, acc, originalMat); renderer.render(scene, camera); const time = new Date().getTime() - start; @@ -111,15 +110,16 @@ function round(n: number): string { return n.toFixed(3); } -function mat4String(m: mat4) { - return `${round(m[0])}\t\t${round(m[1])}\t\t${round(m[2])}\t\t${round(m[3])}\n` + - `${round(m[4])}\t\t${round(m[5])}\t\t${round(m[6])}\t\t${round(m[7])}\n` + - `${round(m[8])}\t\t${round(m[9])}\t\t${round(m[10])}\t\t${round(m[11])}\n` + - `${round(m[12])}\t\t${round(m[13])}\t\t${round(m[14])}\t\t${round(m[15])}\n`; +function mat4String(m: Matrix4) { + return `${round(m.values[0])}\t\t${round(m.values[1])}\t\t${round(m.values[2])}\t\t${round(m.values[3])}\n` + + `${round(m.values[4])}\t\t${round(m.values[5])}\t\t${round(m.values[6])}\t\t${round(m.values[7])}\n` + + `${round(m.values[8])}\t\t${round(m.values[9])}\t\t${round(m.values[10])}\t\t${round(m.values[11])}\n` + + `${round(m.values[12])}\t\t${round(m.values[13])}\t\t${round(m.values[14])}\t\t${round(m.values[15])}\n`; } export function reset() { - rotationGroup.matrix = mat4.create(); + + rotationGroup.matrix = new Matrix4(); valuesElement.innerHTML = mat4String(rotationGroup.matrix); renderer.render(scene, camera); diff --git a/src/demos/puzzles/puzzles.ts b/src/demos/puzzles/puzzles.ts index 850cda9..6fd56ce 100644 --- a/src/demos/puzzles/puzzles.ts +++ b/src/demos/puzzles/puzzles.ts @@ -1,6 +1,6 @@ import { SVGVisualizerOptions } from './../../visualizer/svg'; import { VisualizerType } from './../../visualizer/enum'; -import { PNG, PNGVisualizerOptions } from '../../visualizer/png'; +import { PNG } from '../../visualizer/png'; function renderDefault() { const options: SVGVisualizerOptions = { diff --git a/src/demos/teapot/teapot.ts b/src/demos/teapot/teapot.ts index 0e3a976..3a325b9 100644 --- a/src/demos/teapot/teapot.ts +++ b/src/demos/teapot/teapot.ts @@ -1,11 +1,12 @@ import { faces, vertices } from './data'; import { Geometry } from '../../geometry/geometry'; -import { mat4, vec3 } from 'gl-matrix'; import { Face } from '../../geometry/face'; import { Camera } from '../../rendering/camera'; import { Scene } from '../../rendering/scene'; import { Group } from '../../geometry/group'; import { HtmlCanvasRenderer } from '../../rendering/htmlCanvasRenderer'; +import { Vector3 } from '../../math/vector'; +import { Matrix4 } from '../../math/matrix'; let camera: Camera = new Camera(); let g: Group = new Group(); @@ -20,7 +21,7 @@ let width: number = 500; let height: number = 500; const teapot = createTeapot(); -mat4.translate(camera.matrix, camera.matrix, [0, -1.5, -10]); +camera.matrix.translate(0, -1.5, -10) scene = new Scene(); renderer = new HtmlCanvasRenderer(width, height, .25); @@ -28,14 +29,14 @@ g.addObject(teapot); scene.add(g); export function renderDemo() { - teapot.matrix = mat4.rotate(teapot.matrix, teapot.matrix, Math.PI / 16, [1, 1, 0]); + teapot.matrix.rotate(Math.PI / 16, 1, 1, 0); renderer.render(scene, camera); } function createTeapot(): Geometry { const v = vertices.map(vertex => { const [_, x, y, z] = vertex.split(" "); - return vec3.fromValues(parseFloat(x), parseFloat(y), parseFloat(z)); + return Vector3.fromValues(parseFloat(x), parseFloat(y), parseFloat(z)); }) let outline = { value: "#DDD", stroke: "#FFF" }; @@ -65,13 +66,13 @@ document.addEventListener("DOMContentLoaded", function (event) { document.getElementById('teapot').appendChild(renderer.domElement); renderDemo(); - let originalMat: mat4 = mat4.create(); + let originalMat: Matrix4 = new Matrix4(); let start = new Date().getTime(); renderer.domElement.addEventListener('mousedown', e => { mouseDown = true; start = new Date().getTime(); - mat4.copy(originalMat, g.matrix); + Matrix4.copy(originalMat, g.matrix); x = e.x; y = e.y; }); @@ -85,16 +86,16 @@ document.addEventListener("DOMContentLoaded", function (event) { start = new Date().getTime(); const [diffX, diffY] = [e.x - x, e.y - y]; - let xRotation = mat4.fromXRotation(mat4.create(), diffY / 128); - let yRotation = mat4.fromYRotation(mat4.create(), diffX / 128); + let xRotation = Matrix4.fromXRotation(diffY / 128); + let yRotation = Matrix4.fromYRotation(diffX / 128); // Matrix to accumulate x and y rotations from mouse movements - let acc: mat4 = mat4.create(); + let acc: Matrix4 = new Matrix4(); - mat4.multiply(acc, acc, xRotation); - mat4.multiply(acc, acc, yRotation); + acc.multiply(xRotation); + acc.multiply(yRotation); - g.matrix = mat4.multiply(g.matrix, acc, originalMat); + Matrix4.multiply(g.matrix, acc, originalMat); renderer.render(scene, camera); } }, 10)); diff --git a/src/geometry/arrow.ts b/src/geometry/arrow.ts index 80ff9cf..fb6d06a 100644 --- a/src/geometry/arrow.ts +++ b/src/geometry/arrow.ts @@ -1,12 +1,12 @@ -import { vec3 } from "gl-matrix"; import { calculateCentroid } from "../math/utils"; +import { Vector3 } from "../math/vector"; import { Object3D } from "./object3d"; export class Arrow extends Object3D { - p1: vec3; - p2: vec3; + p1: Vector3; + p2: Vector3; - constructor(p1: vec3, p2: vec3) { + constructor(p1: Vector3, p2: Vector3) { super(); this.p1 = p1; diff --git a/src/geometry/cube.ts b/src/geometry/cube.ts index 2d813c7..00a4c73 100644 --- a/src/geometry/cube.ts +++ b/src/geometry/cube.ts @@ -1,6 +1,6 @@ import { Geometry } from "./geometry"; -import { vec3 } from "gl-matrix"; import { IFace, Face } from "./face"; +import { Vector3 } from "../math/vector"; export class Cube extends Geometry { /** @@ -13,15 +13,15 @@ export class Cube extends Geometry { * 7 - - - 6 */ constructor(width: number) { - const vertices: vec3[] = [ - [-width / 2, width / 2, width / 2], - [width / 2, width / 2, width / 2], - [width / 2, -width / 2, width / 2], - [-width / 2, -width / 2, width / 2], - [-width / 2, width / 2, -width / 2], - [width / 2, width / 2, -width / 2], - [width / 2, -width / 2, -width / 2], - [-width / 2, -width / 2, -width / 2], + const vertices: Vector3[] = [ + Vector3.fromValues(-width / 2, width / 2, width / 2), + Vector3.fromValues(width / 2, width / 2, width / 2), + Vector3.fromValues(width / 2, -width / 2, width / 2), + Vector3.fromValues(-width / 2, -width / 2, width / 2), + Vector3.fromValues(-width / 2, width / 2, -width / 2), + Vector3.fromValues(width / 2, width / 2, -width / 2), + Vector3.fromValues(width / 2, -width / 2, -width / 2), + Vector3.fromValues(-width / 2, -width / 2, -width / 2), ]; const faces: IFace[] = [ new Face([0, 1, 2, 3], vertices), @@ -31,6 +31,7 @@ export class Cube extends Geometry { new Face([1, 0, 4, 5], vertices), new Face([0, 3, 7, 4], vertices), ]; + super(vertices, faces); } } diff --git a/src/geometry/dividedPentagon.ts b/src/geometry/dividedPentagon.ts index 071b6c4..ea3e4cb 100644 --- a/src/geometry/dividedPentagon.ts +++ b/src/geometry/dividedPentagon.ts @@ -1,5 +1,4 @@ import { IColor } from "./color"; -import { vec2, vec3 } from "gl-matrix"; import { IFace, Face } from "./../geometry/face"; import { Geometry } from "../geometry/geometry"; import { @@ -8,6 +7,7 @@ import { pentagonInRadius, pentagonOutRadius, } from "../math/utils"; +import { Vector2, Vector3 } from "../math/vector"; export class DividedPentagon extends Geometry { /** @@ -90,7 +90,11 @@ function layerVertexNumbers(layer) { return vertexNumbers; } -function makeFaces(layers: number, color: IColor, vertices: vec3[]): IFace[] { +function makeFaces( + layers: number, + color: IColor, + vertices: Vector3[] +): IFace[] { let faces: IFace[] = []; const firstLayerFace = new Face([0, 1, 2, 3, 4], vertices, color); @@ -141,41 +145,41 @@ function makeFaces(layers: number, color: IColor, vertices: vec3[]): IFace[] { * @param segments how many points to extrapolate from each direction p1 -> p2 and p2 -> p1 */ function segmentPoints( - p1: vec2, - p2: vec2, + p1: Vector2, + p2: Vector2, segments: number, layerWidth: number -): vec3[] { +): Vector3[] { if (segments === 0) { return [ - [p1[0], p1[1], 0], - [p2[0], p2[1], 0], + Vector3.fromValues(p1.x, p1.y, 0), + Vector3.fromValues(p2.x, p2.y, 0), ]; } const length: number = lineSegmentLength(p1, p2); - let points: vec3[] = []; + let points: Vector3[] = []; for (let i = segments; i > 0; i--) { - // extrapolate from p1 - let a: vec3 = [ - p1[0] + ((p2[0] - p1[0]) / length) * layerWidth * i, - p1[1] + ((p2[1] - p1[1]) / length) * layerWidth * i, - 0, - ]; + // extrapolate from p1.v + let a: Vector3 = Vector3.fromValues( + p1.x + ((p2.x - p1.x) / length) * layerWidth * i, + p1.y + ((p2.y - p1.y) / length) * layerWidth * i, + 0 + ); points.unshift(a); - // extrapolate from p2 - let b: vec3 = [ - p2[0] + ((p1[0] - p2[0]) / length) * layerWidth * i, - p2[1] + ((p1[1] - p2[1]) / length) * layerWidth * i, - 0, - ]; + // extrapolate from p2.v + let b: Vector3 = Vector3.fromValues( + p2.x + ((p1.x - p2.x) / length) * layerWidth * i, + p2.y + ((p1.y - p2.y) / length) * layerWidth * i, + 0 + ); points.push(b); } - points.unshift([p1[0], p1[1], 0]); - points.push([p2[0], p2[1], 0]); + points.unshift(Vector3.fromValues(p1.x, p1.y, 0)); + points.push(Vector3.fromValues(p2.x, p2.y, 0)); return points; } @@ -184,8 +188,8 @@ function layerVerticies( layer: number, radius: number, layerWidth: number -): vec3[] { - let verticies: vec3[] = []; +): Vector3[] { + let verticies: Vector3[] = []; for (let i = 0; i < 5; i++) { const theta = (i * (2 * Math.PI)) / 5 - Math.PI / 10; @@ -194,7 +198,7 @@ function layerVerticies( if (verticies.length > 0) { const lastPoint = verticies[verticies.length - 1]; const points = segmentPoints( - [lastPoint[0], lastPoint[1]], + Vector2.fromValues(lastPoint.x, lastPoint.y), v, layer, layerWidth @@ -203,7 +207,7 @@ function layerVerticies( verticies = verticies.concat(points); } else { - verticies.push([v[0], v[1], 0]); + verticies.push(Vector3.fromValues(v.x, v.y, 0)); } } @@ -211,8 +215,8 @@ function layerVerticies( const first = verticies[0]; const last = verticies[verticies.length - 1]; const points = segmentPoints( - [last[0], last[1]], - [first[0], first[1]], + Vector2.fromValues(last.x, last.y), + Vector2.fromValues(first.x, first.y), layer, layerWidth ); @@ -228,8 +232,8 @@ function faceVerticies( radius: number, radiusDiff: number, layerWidth: number -): vec3[] { - let verticies: vec3[] = []; +): Vector3[] { + let verticies: Vector3[] = []; for (let i = 0; i < layers; i++) { const r = radius + radiusDiff * i; diff --git a/src/geometry/face.ts b/src/geometry/face.ts index f8e0467..aa7ede0 100644 --- a/src/geometry/face.ts +++ b/src/geometry/face.ts @@ -1,11 +1,11 @@ -import { vec3 } from "gl-matrix"; import { calculateCentroid } from "../math/utils"; +import { Vector3 } from "../math/vector"; import { IColor } from "./color"; import { generateUid } from "./uid"; export interface IFace { indices: number[]; - centroid: vec3; + centroid: Vector3; color?: IColor; uid: number; } @@ -16,7 +16,7 @@ export interface IFace { export class Face implements IFace { indices: number[]; color?: IColor; - centroid: vec3; + centroid: Vector3; uid: number; /** @@ -24,7 +24,7 @@ export class Face implements IFace { * @param vertices vertices of the geometry to calculate centroid from * @param color color of the sticker */ - constructor(indices: number[], vertices: vec3[], color?: IColor) { + constructor(indices: number[], vertices: Vector3[], color?: IColor) { this.indices = indices; this.color = color; this.uid = generateUid(); @@ -37,7 +37,7 @@ export class Face implements IFace { /** * recalculate the centroid of the face. */ - calculateCentroid(vertices: vec3[]) { + calculateCentroid(vertices: Vector3[]) { this.centroid = calculateCentroid( // Calculate centroid from vertices included in the face vertices.filter((v, i) => this.indices.includes(i)) diff --git a/src/geometry/geometry.ts b/src/geometry/geometry.ts index d8d92f9..7ef53d6 100644 --- a/src/geometry/geometry.ts +++ b/src/geometry/geometry.ts @@ -1,13 +1,13 @@ -import { vec3 } from "gl-matrix"; import { IFace } from "./face"; import { Object3D } from "./object3d"; import { calculateCentroid } from "../math/utils"; +import { Vector3 } from "../math/vector"; export class Geometry extends Object3D { - vertices: vec3[]; + vertices: Vector3[]; faces: IFace[]; - constructor(vertices: vec3[], faces: IFace[]) { + constructor(vertices: Vector3[], faces: IFace[]) { super(); this.vertices = vertices; diff --git a/src/geometry/grid.ts b/src/geometry/grid.ts index 246ea12..a55b489 100644 --- a/src/geometry/grid.ts +++ b/src/geometry/grid.ts @@ -1,7 +1,7 @@ import { Face } from "./face"; -import { vec3 } from "gl-matrix"; import { Geometry } from "./geometry"; import { IColor } from "./color"; +import { Vector3 } from "../math/vector"; /** * Makes a (size*size) grid of colored planes for the @@ -61,11 +61,27 @@ export function makeRow( for (let i = 0; i < size; i++) { let hOffset = -halfLength + halfElementWidth + elementWidth * i; - let vertices: vec3[] = [ - [-halfElementWidth + hOffset, halfElementWidth + vOffset, 0], - [halfElementWidth + hOffset, halfElementWidth + vOffset, 0], - [halfElementWidth + hOffset, -halfElementWidth + vOffset, 0], - [-halfElementWidth + hOffset, -halfElementWidth + vOffset, 0], + let vertices: Vector3[] = [ + Vector3.fromValues( + -halfElementWidth + hOffset, + halfElementWidth + vOffset, + 0 + ), + Vector3.fromValues( + halfElementWidth + hOffset, + halfElementWidth + vOffset, + 0 + ), + Vector3.fromValues( + halfElementWidth + hOffset, + -halfElementWidth + vOffset, + 0 + ), + Vector3.fromValues( + -halfElementWidth + hOffset, + -halfElementWidth + vOffset, + 0 + ), ]; let faces = [new Face([0, 1, 2, 3], vertices, color)]; stickers.push(new Geometry(vertices, faces)); diff --git a/src/geometry/group.ts b/src/geometry/group.ts index e54c486..231ce78 100644 --- a/src/geometry/group.ts +++ b/src/geometry/group.ts @@ -1,6 +1,5 @@ import { Object3D } from "./object3d"; -import { Geometry } from "./geometry"; -import { vec3 } from "gl-matrix"; +import { Vector3 } from "../math/vector"; export class Group extends Object3D { objects: Object3D[]; @@ -18,7 +17,7 @@ export class Group extends Object3D { this.objects.push(object); } - setCentroid(vector: vec3) { + setCentroid(vector: Vector3) { this.centroid = vector; } } diff --git a/src/geometry/object3d.ts b/src/geometry/object3d.ts index 1611031..6fca8da 100644 --- a/src/geometry/object3d.ts +++ b/src/geometry/object3d.ts @@ -1,29 +1,30 @@ -import { mat4, ReadonlyVec3, vec3 } from "gl-matrix"; import { IColor } from "./color"; import { generateUid } from "./uid"; +import { Vector3 } from "../math/vector"; +import { Matrix4 } from "../math/matrix"; export class Object3D { - matrix: mat4; + matrix: Matrix4; uid: number; color?: IColor; - centroid: vec3; + centroid: Vector3; constructor() { this.uid = generateUid(); - this.matrix = mat4.create(); - this.centroid = [0, 0, 0]; + this.matrix = new Matrix4(); + this.centroid = Vector3.fromValues(0, 0, 0); } - translate(vector: ReadonlyVec3) { - mat4.translate(this.matrix, this.matrix, vector); + translate(x: number, y: number, z: number) { + this.matrix.translate(x, y, z); } - rotate(rad: number, axis: ReadonlyVec3) { - mat4.rotate(this.matrix, this.matrix, rad, axis); + rotate(rad: number, x: number, y: number, z: number) { + this.matrix.rotate(rad, x, y, z); } - scale(v: vec3) { - mat4.scale(this.matrix, this.matrix, v); + scale(x: number, y: number, z: number) { + this.matrix.scale(x, y, z); } setColor(color: IColor) { diff --git a/src/geometry/pentagon.ts b/src/geometry/pentagon.ts index 20bafef..711b2d7 100644 --- a/src/geometry/pentagon.ts +++ b/src/geometry/pentagon.ts @@ -1,16 +1,16 @@ import { Face, IFace } from "./face"; -import { vec3 } from "gl-matrix"; import { Geometry } from "./geometry"; import { polarToCartesian } from "../math/utils"; +import { Vector3 } from "../math/vector"; export class Pentagon extends Geometry { constructor(radius: number) { - let vertices: vec3[] = []; + let vertices: Vector3[] = []; for (let i = 0; i < 5; i++) { const theta = (i * (2 * Math.PI)) / 5 - Math.PI / 10; const v = polarToCartesian(radius, theta); - vertices.push([v[0], v[1], 0]); + vertices.push(Vector3.fromValues(v.x, v.y, 0)); } let faces: IFace[] = [ diff --git a/src/geometry/plane.ts b/src/geometry/plane.ts index edcf7d2..d6a1978 100644 --- a/src/geometry/plane.ts +++ b/src/geometry/plane.ts @@ -1,15 +1,15 @@ import { Geometry } from "./geometry"; -import { vec3 } from "gl-matrix"; import { IFace, Face } from "./face"; import { IColor } from "./color"; +import { Vector3 } from "../math/vector"; export class Plane extends Geometry { constructor(width: number, height: number, color: IColor) { - let vertices: vec3[] = [ - vec3.clone([0, 0, 0]), - vec3.clone([width, 0, 0]), - vec3.clone([width, -height, 0]), - vec3.clone([0, -height, 0]), + let vertices: Vector3[] = [ + Vector3.fromValues(0, 0, 0), + Vector3.fromValues(width, 0, 0), + Vector3.fromValues(width, -height, 0), + Vector3.fromValues(0, -height, 0), ]; let faces: IFace[] = [new Face([0, 1, 2, 3], vertices, color)]; super(vertices, faces); diff --git a/src/geometry/triangle.ts b/src/geometry/triangle.ts index 0e37d70..de2c14d 100644 --- a/src/geometry/triangle.ts +++ b/src/geometry/triangle.ts @@ -1,12 +1,12 @@ import { Geometry } from "./geometry"; -import { vec3 } from "gl-matrix"; import { IColor } from "./color"; import { Face, IFace } from "./face"; import { SQRT_3 } from "../math/constants"; +import { Vector3 } from "../math/vector"; export class Triangle extends Geometry { - constructor(a: vec3, b: vec3, c: vec3, color: IColor) { - let verticies: vec3[] = [a, b, c]; + constructor(a: Vector3, b: Vector3, c: Vector3, color: IColor) { + let verticies: Vector3[] = [a, b, c]; let faces: IFace[] = [new Face([0, 1, 2], verticies, color)]; super(verticies, faces); } @@ -16,6 +16,11 @@ export class EquilateralTriangle extends Triangle { constructor(base: number, color: IColor) { let height = base * (SQRT_3 / 2); - super([0, 0, 0], [base / 2, height, 0], [base, 0, 0], color); + super( + Vector3.fromValues(0, 0, 0), + Vector3.fromValues(base / 2, height, 0), + Vector3.fromValues(base, 0, 0), + color + ); } } diff --git a/src/geometry/triangleLattice.ts b/src/geometry/triangleLattice.ts index 262a46b..4002e89 100644 --- a/src/geometry/triangleLattice.ts +++ b/src/geometry/triangleLattice.ts @@ -1,8 +1,8 @@ import { IColor } from "./color"; import { IFace, Face } from "./face"; -import { vec3 } from "gl-matrix"; import { Geometry } from "./geometry"; import { SQRT_3 } from "../math/constants"; +import { Vector3 } from "../math/vector"; /** * Geometry to build a triangle lattice for the @@ -16,7 +16,7 @@ export class TriangleLattice extends Geometry { const triangleHeight = fullHeight / size; const inradius = fullHeight / 3; - let vertices: vec3[] = []; + let vertices: Vector3[] = []; let faces: Face[] = []; /** @@ -39,7 +39,7 @@ export class TriangleLattice extends Geometry { triangleBase * vertex + (layer * triangleBase) / 2 + horizontalOffset; const y = triangleHeight * layer + verticalOffset; - vertices.push([x, y, 0]); + vertices.push(Vector3.fromValues(x, y, 0)); if (layer > 0) { // down triangle diff --git a/src/math/matrix.ts b/src/math/matrix.ts new file mode 100644 index 0000000..804ff23 --- /dev/null +++ b/src/math/matrix.ts @@ -0,0 +1,334 @@ +/** + * Credit to logic https://github.com/toji/gl-matrix/blob/master/src/mat4.js + */ + +import { Quaternion } from "./quaternion"; + +const EPSILON = 0.000001; + +export class Matrix4 { + public values: number[]; + + /** + * Returns a 4x4 matrix with the given values + */ + static fromValues( + m1: number, + m2: number, + m3: number, + m4: number, + m5: number, + m6: number, + m7: number, + m8: number, + m9: number, + m10: number, + m11: number, + m12: number, + m13: number, + m14: number, + m15: number, + m16: number + ): Matrix4 { + return new Matrix4([ + m1, + m2, + m3, + m4, + m5, + m6, + m7, + m8, + m9, + m10, + m11, + m12, + m13, + m14, + m15, + m16, + ]); + } + + static fromQuaternion(q: Quaternion): Matrix4 { + let { a: x, b: y, c: z, d: w } = q; + + let x2 = x + x; + let y2 = y + y; + let z2 = z + z; + + let xx = x * x2; + let yx = y * x2; + let yy = y * y2; + let zx = z * x2; + let zy = z * y2; + let zz = z * z2; + let wx = w * x2; + let wy = w * y2; + let wz = w * z2; + + return Matrix4.fromValues( + 1 - yy - zz, + yx + wz, + zx - wy, + 0, + yx - wz, + 1 - xx - zz, + zy + wx, + 0, + zx + wy, + zy - wx, + 1 - xx - yy, + 0, + 0, + 0, + 0, + 1 + ); + } + + static fromTranslation(x: number, y: number, z: number) { + return Matrix4.fromValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1); + } + + static fromXRotation(radians: number) { + let s = Math.sin(radians); + let c = Math.cos(radians); + + return Matrix4.fromValues(1, 0, 0, 0, 0, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1); + } + + static fromYRotation(radians: number) { + let s = Math.sin(radians); + let c = Math.cos(radians); + + return Matrix4.fromValues(c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1); + } + + /** + * copy values from one matrix to another + */ + static copy(out: Matrix4, matrix: Matrix4) { + out.values[0] = matrix.values[0]; + out.values[1] = matrix.values[1]; + out.values[2] = matrix.values[2]; + out.values[3] = matrix.values[3]; + out.values[4] = matrix.values[4]; + out.values[5] = matrix.values[5]; + out.values[6] = matrix.values[6]; + out.values[7] = matrix.values[7]; + out.values[8] = matrix.values[8]; + out.values[9] = matrix.values[9]; + out.values[10] = matrix.values[10]; + out.values[11] = matrix.values[11]; + out.values[12] = matrix.values[12]; + out.values[13] = matrix.values[13]; + out.values[14] = matrix.values[14]; + out.values[15] = matrix.values[15]; + } + + static multiply(out: Matrix4, a: Matrix4, b: Matrix4): Matrix4 { + let a00 = a.values[0], + a01 = a.values[1], + a02 = a.values[2], + a03 = a.values[3]; + let a10 = a.values[4], + a11 = a.values[5], + a12 = a.values[6], + a13 = a.values[7]; + let a20 = a.values[8], + a21 = a.values[9], + a22 = a.values[10], + a23 = a.values[11]; + let a30 = a.values[12], + a31 = a.values[13], + a32 = a.values[14], + a33 = a.values[15]; + + // Cache only the current line of the second matrix + let b0 = b.values[0], + b1 = b.values[1], + b2 = b.values[2], + b3 = b.values[3]; + out.values[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out.values[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out.values[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out.values[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + + b0 = b.values[4]; + b1 = b.values[5]; + b2 = b.values[6]; + b3 = b.values[7]; + out.values[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out.values[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out.values[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out.values[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + + b0 = b.values[8]; + b1 = b.values[9]; + b2 = b.values[10]; + b3 = b.values[11]; + out.values[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out.values[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out.values[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out.values[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + + b0 = b.values[12]; + b1 = b.values[13]; + b2 = b.values[14]; + b3 = b.values[15]; + out.values[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out.values[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out.values[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out.values[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + return out; + } + + /** + * Generates a perspective projection matrix with the given bounds. + * The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1], + * which matches WebGL/OpenGL's clip volume. + * Passing null/undefined/no value for far will generate infinite projection matrix. + * + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum, can be null or Infinity + */ + static perspective( + fovy: number, + aspect: number, + near: number, + far: number + ): Matrix4 { + const f = 1.0 / Math.tan(fovy / 2); + const values = [f / aspect, 0, 0, 0, 0, f, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0]; + + if (far != null && far !== Infinity) { + const nf = 1 / (near - far); + values[10] = (far + near) * nf; + values[14] = 2 * far * near * nf; + } else { + values[10] = -1; + values[14] = -2 * near; + } + + return new Matrix4(values); + } + + constructor(values?: number[]) { + if (Array.isArray(values) && values.length == 16) { + this.values = values; + } else { + this.values = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; + } + } + + translate(x: number, y: number, z: number) { + this.values[12] = + this.values[0] * x + + this.values[4] * y + + this.values[8] * z + + this.values[12]; + this.values[13] = + this.values[1] * x + + this.values[5] * y + + this.values[9] * z + + this.values[13]; + this.values[14] = + this.values[2] * x + + this.values[6] * y + + this.values[10] * z + + this.values[14]; + this.values[15] = + this.values[3] * x + + this.values[7] * y + + this.values[11] * z + + this.values[15]; + } + + scale(x: number, y: number, z: number) { + this.values[0] = this.values[0] * x; + this.values[1] = this.values[1] * x; + this.values[2] = this.values[2] * x; + this.values[3] = this.values[3] * x; + this.values[4] = this.values[4] * y; + this.values[5] = this.values[5] * y; + this.values[6] = this.values[6] * y; + this.values[7] = this.values[7] * y; + this.values[8] = this.values[8] * z; + this.values[9] = this.values[9] * z; + this.values[10] = this.values[10] * z; + this.values[11] = this.values[11] * z; + } + + /** + * Rotates the matrix by the given angle around the axis (x, y, z) + */ + rotate(radians: number, x: number, y: number, z: number) { + let len = Math.hypot(x, y, z); + + if (len < EPSILON) { + return; + } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + let s = Math.sin(radians); + let c = Math.cos(radians); + let t = 1 - c; + + let a00, a01, a02, a03; + let a10, a11, a12, a13; + let a20, a21, a22, a23; + + let b00, b01, b02; + let b10, b11, b12; + let b20, b21, b22; + + a00 = this.values[0]; + a01 = this.values[1]; + a02 = this.values[2]; + a03 = this.values[3]; + a10 = this.values[4]; + a11 = this.values[5]; + a12 = this.values[6]; + a13 = this.values[7]; + a20 = this.values[8]; + a21 = this.values[9]; + a22 = this.values[10]; + a23 = this.values[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; + b01 = y * x * t + z * s; + b02 = z * x * t - y * s; + b10 = x * y * t - z * s; + b11 = y * y * t + c; + b12 = z * y * t + x * s; + b20 = x * z * t + y * s; + b21 = y * z * t - x * s; + b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + this.values[0] = a00 * b00 + a10 * b01 + a20 * b02; + this.values[1] = a01 * b00 + a11 * b01 + a21 * b02; + this.values[2] = a02 * b00 + a12 * b01 + a22 * b02; + this.values[3] = a03 * b00 + a13 * b01 + a23 * b02; + this.values[4] = a00 * b10 + a10 * b11 + a20 * b12; + this.values[5] = a01 * b10 + a11 * b11 + a21 * b12; + this.values[6] = a02 * b10 + a12 * b11 + a22 * b12; + this.values[7] = a03 * b10 + a13 * b11 + a23 * b12; + this.values[8] = a00 * b20 + a10 * b21 + a20 * b22; + this.values[9] = a01 * b20 + a11 * b21 + a21 * b22; + this.values[10] = a02 * b20 + a12 * b21 + a22 * b22; + this.values[11] = a03 * b20 + a13 * b21 + a23 * b22; + } + + multiply(b: Matrix4) { + Matrix4.multiply(this, this, b); + } +} diff --git a/src/math/quaternion.ts b/src/math/quaternion.ts new file mode 100644 index 0000000..2e4c2b9 --- /dev/null +++ b/src/math/quaternion.ts @@ -0,0 +1,32 @@ +/** + * Credit to logic https://github.com/toji/gl-matrix/blob/master/src/quat.js + */ +export class Quaternion { + static fromEuler(x: number, y: number, z: number): Quaternion { + let halfToRad = Math.PI / 360; + x *= halfToRad; + z *= halfToRad; + y *= halfToRad; + + let sx = Math.sin(x); + let cx = Math.cos(x); + let sy = Math.sin(y); + let cy = Math.cos(y); + let sz = Math.sin(z); + let cz = Math.cos(z); + + let a = sx * cy * cz - cx * sy * sz; + let b = cx * sy * cz + sx * cy * sz; + let c = cx * cy * sz - sx * sy * cz; + let d = cx * cy * cz + sx * sy * sz; + + return new Quaternion(a, b, c, d); + } + + constructor( + public a: number, + public b: number, + public c: number, + public d: number + ) {} +} diff --git a/src/math/utils.ts b/src/math/utils.ts index d9731bf..a158319 100644 --- a/src/math/utils.ts +++ b/src/math/utils.ts @@ -1,18 +1,18 @@ -import { vec2, vec3 } from "gl-matrix"; +import { Vector2, Vector3 } from "./vector"; export function degreesToRadians(degrees: number): number { return (Math.PI * degrees) / 180; } -export function polarToCartesian(radius: number, theta: number): vec2 { +export function polarToCartesian(radius: number, theta: number): Vector2 { const x = radius * Math.cos(theta); const y = radius * Math.sin(theta); - return [x, y]; + return Vector2.fromValues(x, y); } -export function lineSegmentLength(p1: vec2, p2: vec2): number { - return Math.sqrt(Math.pow(p2[1] - p1[1], 2) + Math.pow(p2[0] - p1[0], 2)); +export function lineSegmentLength(p1: Vector2, p2: Vector2): number { + return Math.sqrt(Math.pow(p2.y - p1.y, 2) + Math.pow(p2.x - p1.x, 2)); } /** @@ -39,21 +39,21 @@ export function dodecahedronInRadius(length: number): number { return (length / 2) * Math.sqrt(5 / 2 + (11 / 10) * Math.sqrt(5)); } -export function calculateCentroid(vertices: vec3[]): vec3 { +export function calculateCentroid(vertices: Vector3[]): Vector3 { let cx = 0, cy = 0, cz = 0; vertices.forEach((vertex) => { - cx += vertex[0]; - cy += vertex[1]; - cz += vertex[2]; + cx += vertex.x; + cy += vertex.y; + cz += vertex.z; }); cx /= vertices.length; cy /= vertices.length; cz /= vertices.length; - return vec3.clone([cx, cy, cz]); + return Vector3.fromValues(cx, cy, cz); } /** diff --git a/src/math/vector.ts b/src/math/vector.ts new file mode 100644 index 0000000..560add1 --- /dev/null +++ b/src/math/vector.ts @@ -0,0 +1,113 @@ +/** + * Credit to logic https://github.com/toji/gl-matrix/blob/master/src/vec3.js + */ +import { Matrix4 } from "./matrix"; + +export class Vector3 { + public x: number; + public y: number; + public z: number; + + static fromValues(x: number, y: number, z: number) { + return new Vector3(x, y, z); + } + + constructor(x: number, y: number, z: number) { + this.x = x; + this.y = y; + this.z = z; + } + + transformMat4(m: Matrix4) { + let w = + m.values[3] * this.x + + m.values[7] * this.y + + m.values[11] * this.z + + m.values[15]; + w = w || 1.0; + + const x = + (m.values[0] * this.x + + m.values[4] * this.y + + m.values[8] * this.z + + m.values[12]) / + w; + const y = + (m.values[1] * this.x + + m.values[5] * this.y + + m.values[9] * this.z + + m.values[13]) / + w; + const z = + (m.values[2] * this.x + + m.values[6] * this.y + + m.values[10] * this.z + + m.values[14]) / + w; + this.x = x; + this.y = y; + this.z = z; + } + + multiply(x: number, y: number, z: number) { + this.x = this.x * x; + this.y = this.y * y; + this.z = this.z * z; + } + + rotateX(origin: Vector3, radians: number) { + // translate point to origin + let x = this.x - origin.x; + let y = this.y - origin.y; + let z = this.z - origin.z; + + // rotate + this.x = x; + this.y = y * Math.cos(radians) - z * Math.sin(radians); + this.z = y * Math.sin(radians) + z * Math.cos(radians); + + // translate back + this.x += origin.x; + this.y += origin.y; + this.z += origin.z; + + return this; + } + + rotateZ(origin: Vector3, radians: number) { + // translate point to origin + let x = this.x - origin.x; + let y = this.y - origin.y; + let z = this.z - origin.z; + + // rotate + this.x = x * Math.cos(radians) - y * Math.sin(radians); + this.y = x * Math.sin(radians) + y * Math.cos(radians); + this.z = z; + + // translate back + this.x += origin.x; + this.y += origin.y; + this.z += origin.z; + + return this; + } + + clone() { + return Vector3.fromValues(this.x, this.y, this.z); + } +} + +export class Vector2 { + public x: number; + public y: number; + + static fromValues(x: number, y: number) { + return new Vector2(x, y); + } + + constructor(x: number, y: number) { + this.x = x; + this.y = y; + } +} diff --git a/src/puzzles/megaminx.ts b/src/puzzles/megaminx.ts index c8bb8f9..1053009 100644 --- a/src/puzzles/megaminx.ts +++ b/src/puzzles/megaminx.ts @@ -73,55 +73,55 @@ export class Megaminx { this.bl = new DividedPentagon(DARK_BLUE, layers, length, layerWidth); this.b = new DividedPentagon(ORANGE, layers, length, layerWidth); - this.F.translate([0, 0, megaminxRadius]); - - this.b.rotate(Math.PI, [0, 0, 1]); - this.b.rotate(Math.PI, [0, 1, 0]); - this.b.translate([0, 0, megaminxRadius]); - - this.U.rotate(Math.PI, [0, 0, 1]); - this.U.rotate(((180 - 116.57) * Math.PI) / 180, [1, 0, 0]); - this.U.translate([0, 0, megaminxRadius]); - - this.L.rotate((72 * Math.PI) / 180, [0, 0, 1]); - this.L.rotate(Math.PI, [0, 0, 1]); - this.L.rotate(((180 - 116.57) * Math.PI) / 180, [1, 0, 0]); - this.L.translate([0, 0, megaminxRadius]); - - this.R.rotate((72 * Math.PI) / 180, [0, 0, 1]); - this.R.rotate(Math.PI / 5, [0, 0, 1]); - this.R.rotate(((180 - 116.57) * Math.PI) / 180, [1, 0, 0]); - this.R.translate([0, 0, megaminxRadius]); - - this.dr.rotate((72 * Math.PI) / 180, [0, 0, 1]); - this.dr.rotate(-Math.PI / 5, [0, 0, 1]); - this.dr.rotate(((180 - 116.57) * Math.PI) / 180, [1, 0, 0]); - this.dr.translate([0, 0, megaminxRadius]); - - this.dl.rotate((72 * Math.PI) / 180, [0, 0, 1]); - this.dl.rotate((-3 * Math.PI) / 5, [0, 0, 1]); - this.dl.rotate(((180 - 116.57) * Math.PI) / 180, [1, 0, 0]); - this.dl.translate([0, 0, megaminxRadius]); - - this.BL.rotate(Math.PI / 5, [0, 0, 1]); - this.BL.rotate((-116.57 * Math.PI) / 180, [1, 0, 0]); - this.BL.translate([0, 0, megaminxRadius]); - - this.BR.rotate(-Math.PI / 5, [0, 0, 1]); - this.BR.rotate((-116.57 * Math.PI) / 180, [1, 0, 0]); - this.BR.translate([0, 0, megaminxRadius]); - - this.bl.rotate((3 * Math.PI) / 5, [0, 0, 1]); - this.bl.rotate((-116.57 * Math.PI) / 180, [1, 0, 0]); - this.bl.translate([0, 0, megaminxRadius]); - - this.d.rotate((5 * Math.PI) / 5, [0, 0, 1]); - this.d.rotate((-116.57 * Math.PI) / 180, [1, 0, 0]); - this.d.translate([0, 0, megaminxRadius]); - - this.br.rotate((7 * Math.PI) / 5, [0, 0, 1]); - this.br.rotate((-116.57 * Math.PI) / 180, [1, 0, 0]); - this.br.translate([0, 0, megaminxRadius]); + this.F.translate(0, 0, megaminxRadius); + + this.b.rotate(Math.PI, 0, 0, 1); + this.b.rotate(Math.PI, 0, 1, 0); + this.b.translate(0, 0, megaminxRadius); + + this.U.rotate(Math.PI, 0, 0, 1); + this.U.rotate(((180 - 116.57) * Math.PI) / 180, 1, 0, 0); + this.U.translate(0, 0, megaminxRadius); + + this.L.rotate((72 * Math.PI) / 180, 0, 0, 1); + this.L.rotate(Math.PI, 0, 0, 1); + this.L.rotate(((180 - 116.57) * Math.PI) / 180, 1, 0, 0); + this.L.translate(0, 0, megaminxRadius); + + this.R.rotate((72 * Math.PI) / 180, 0, 0, 1); + this.R.rotate(Math.PI / 5, 0, 0, 1); + this.R.rotate(((180 - 116.57) * Math.PI) / 180, 1, 0, 0); + this.R.translate(0, 0, megaminxRadius); + + this.dr.rotate((72 * Math.PI) / 180, 0, 0, 1); + this.dr.rotate(-Math.PI / 5, 0, 0, 1); + this.dr.rotate(((180 - 116.57) * Math.PI) / 180, 1, 0, 0); + this.dr.translate(0, 0, megaminxRadius); + + this.dl.rotate((72 * Math.PI) / 180, 0, 0, 1); + this.dl.rotate((-3 * Math.PI) / 5, 0, 0, 1); + this.dl.rotate(((180 - 116.57) * Math.PI) / 180, 1, 0, 0); + this.dl.translate(0, 0, megaminxRadius); + + this.BL.rotate(Math.PI / 5, 0, 0, 1); + this.BL.rotate((-116.57 * Math.PI) / 180, 1, 0, 0); + this.BL.translate(0, 0, megaminxRadius); + + this.BR.rotate(-Math.PI / 5, 0, 0, 1); + this.BR.rotate((-116.57 * Math.PI) / 180, 1, 0, 0); + this.BR.translate(0, 0, megaminxRadius); + + this.bl.rotate((3 * Math.PI) / 5, 0, 0, 1); + this.bl.rotate((-116.57 * Math.PI) / 180, 1, 0, 0); + this.bl.translate(0, 0, megaminxRadius); + + this.d.rotate((5 * Math.PI) / 5, 0, 0, 1); + this.d.rotate((-116.57 * Math.PI) / 180, 1, 0, 0); + this.d.translate(0, 0, megaminxRadius); + + this.br.rotate((7 * Math.PI) / 5, 0, 0, 1); + this.br.rotate((-116.57 * Math.PI) / 180, 1, 0, 0); + this.br.translate(0, 0, megaminxRadius); this.stickers = [ this.U, diff --git a/src/puzzles/megaminxNet.ts b/src/puzzles/megaminxNet.ts index 541d356..8d151c3 100644 --- a/src/puzzles/megaminxNet.ts +++ b/src/puzzles/megaminxNet.ts @@ -1,4 +1,3 @@ -import { mat4 } from "gl-matrix"; import { Geometry } from "./../geometry/geometry"; import { Face } from "./../geometry/face"; import { IColor } from "./../geometry/color"; @@ -18,10 +17,10 @@ import { BLACK, } from "./colors"; import { Group } from "./../geometry/group"; -import { Object3D } from "./../geometry/object3d"; import { DividedPentagon } from "./../geometry/dividedPentagon"; import { pentagonInRadius } from "../math/utils"; import { chunkArray } from "../utils/arrays"; +import { Matrix4 } from "../math/matrix"; const DEG_36_RADIANS = (36 * Math.PI) / 180; const DEG_72_RADIANS = (72 * Math.PI) / 180; @@ -84,61 +83,57 @@ export class MegaminxNet { const ind = 2 * pentagonInRadius(sideLength); // Left - this.U.translate([0, ind, 0]); - this.U.rotate(5 * DEG_36_RADIANS, [0, 0, 1]); + this.U.translate(0, ind, 0); + this.U.rotate(5 * DEG_36_RADIANS, 0, 0, 1); - this.R.rotate(-DEG_72_RADIANS, [0, 0, 1]); - this.R.translate([0, ind, 0]); - this.R.rotate(5 * DEG_36_RADIANS, [0, 0, 1]); + this.R.rotate(-DEG_72_RADIANS, 0, 0, 1); + this.R.translate(0, ind, 0); + this.R.rotate(5 * DEG_36_RADIANS, 0, 0, 1); - this.L.rotate(DEG_72_RADIANS, [0, 0, 1]); - this.L.translate([0, ind, 0]); - this.L.rotate(-5 * DEG_36_RADIANS, [0, 0, 1]); + this.L.rotate(DEG_72_RADIANS, 0, 0, 1); + this.L.translate(0, ind, 0); + this.L.rotate(-5 * DEG_36_RADIANS, 0, 0, 1); - this.dl.rotate(2 * DEG_72_RADIANS, [0, 0, 1]); - this.dl.translate([0, ind, 0]); - this.dl.rotate(-5 * DEG_36_RADIANS, [0, 0, 1]); + this.dl.rotate(2 * DEG_72_RADIANS, 0, 0, 1); + this.dl.translate(0, ind, 0); + this.dl.rotate(-5 * DEG_36_RADIANS, 0, 0, 1); - this.dr.rotate(-2 * DEG_72_RADIANS, [0, 0, 1]); - this.dr.translate([0, ind, 0]); - this.dr.rotate(-5 * DEG_36_RADIANS, [0, 0, 1]); + this.dr.rotate(-2 * DEG_72_RADIANS, 0, 0, 1); + this.dr.translate(0, ind, 0); + this.dr.rotate(-5 * DEG_36_RADIANS, 0, 0, 1); // Right - this.b.rotate(Math.PI, [0, 0, 1]); - this.b.rotate(-2 * DEG_36_RADIANS, [0, 0, 1]); - - this.d.rotate(3 * DEG_36_RADIANS, [0, 0, 1]); - this.d.translate([0, ind, 0]); - this.d.rotate(5 * DEG_36_RADIANS, [0, 0, 1]); - - this.br.rotate(DEG_36_RADIANS, [0, 0, 1]); - this.br.translate([0, ind, 0]); - this.br.rotate(5 * DEG_36_RADIANS, [0, 0, 1]); - - this.BR.rotate(-DEG_36_RADIANS, [0, 0, 1]); - this.BR.translate([0, ind, 0]); - this.BR.rotate(-5 * DEG_36_RADIANS, [0, 0, 1]); - - this.BL.rotate(-3 * DEG_36_RADIANS, [0, 0, 1]); - this.BL.translate([0, ind, 0]); - this.BL.rotate(5 * DEG_36_RADIANS, [0, 0, 1]); - - this.bl.rotate(5 * DEG_36_RADIANS, [0, 0, 1]); - this.bl.translate([0, ind, 0]); - this.bl.rotate(-5 * DEG_36_RADIANS, [0, 0, 1]); - - let bottomTransforms = mat4.create(); - mat4.rotate(bottomTransforms, bottomTransforms, -DEG_72_RADIANS, [0, 0, 1]); - mat4.translate(bottomTransforms, bottomTransforms, [0, 2 * ind, 0]); - mat4.rotate(bottomTransforms, bottomTransforms, 2 * DEG_72_RADIANS, [ - 0, - 0, - 1, - ]); - mat4.translate(bottomTransforms, bottomTransforms, [0, -ind, 0]); + this.b.rotate(Math.PI, 0, 0, 1); + this.b.rotate(-2 * DEG_36_RADIANS, 0, 0, 1); + + this.d.rotate(3 * DEG_36_RADIANS, 0, 0, 1); + this.d.translate(0, ind, 0); + this.d.rotate(5 * DEG_36_RADIANS, 0, 0, 1); + + this.br.rotate(DEG_36_RADIANS, 0, 0, 1); + this.br.translate(0, ind, 0); + this.br.rotate(5 * DEG_36_RADIANS, 0, 0, 1); + + this.BR.rotate(-DEG_36_RADIANS, 0, 0, 1); + this.BR.translate(0, ind, 0); + this.BR.rotate(-5 * DEG_36_RADIANS, 0, 0, 1); + + this.BL.rotate(-3 * DEG_36_RADIANS, 0, 0, 1); + this.BL.translate(0, ind, 0); + this.BL.rotate(5 * DEG_36_RADIANS, 0, 0, 1); + + this.bl.rotate(5 * DEG_36_RADIANS, 0, 0, 1); + this.bl.translate(0, ind, 0); + this.bl.rotate(-5 * DEG_36_RADIANS, 0, 0, 1); + + let bottomTransforms = new Matrix4(); + bottomTransforms.rotate(-DEG_72_RADIANS, 0, 0, 1); + bottomTransforms.translate(0, 2 * ind, 0); + bottomTransforms.rotate(2 * DEG_72_RADIANS, 0, 0, 1); + bottomTransforms.translate(0, -ind, 0); [this.d, this.bl, this.BL, this.BR, this.br, this.b].forEach((face) => { - mat4.mul(face.matrix, bottomTransforms, face.matrix); + Matrix4.multiply(face.matrix, bottomTransforms, face.matrix); }); this.faces = { @@ -170,8 +165,8 @@ export class MegaminxNet { this.br, this.b, ]); - this.group.scale([0.33, 0.33, 0.33]); - this.group.translate([-1.75 * sideLength, 0, 0]); + this.group.scale(0.33, 0.33, 0.33); + this.group.translate(-1.75 * sideLength, 0, 0); } setColors(colors: { [face: string]: IColor[] }) { diff --git a/src/puzzles/megaminxTop.ts b/src/puzzles/megaminxTop.ts index 9101771..f1c13a1 100644 --- a/src/puzzles/megaminxTop.ts +++ b/src/puzzles/megaminxTop.ts @@ -1,9 +1,4 @@ import { Face } from "./../geometry/face"; -import { - DEG_30_RADIANS, - DEG_36_RADIANS, - DEG_72_RADIANS, -} from "./../math/constants"; import { IColor } from "./../geometry/color"; import { WHITE, RED, BLUE, GREEN, BLACK, YELLOW, PURPLE } from "./colors"; import { DividedPentagon } from "./../geometry/dividedPentagon"; @@ -49,29 +44,29 @@ export class MegaminxTop { this.BR = new DividedPentagon(YELLOW, layers, length, layerWidth); this.BL = new DividedPentagon(PURPLE, layers, length, layerWidth); - this.F.translate([0, 0, megaminxRadius]); + this.F.translate(0, 0, megaminxRadius); - this.U.rotate(Math.PI, [0, 0, 1]); - this.U.rotate(((180 - 116.57) * Math.PI) / 180, [1, 0, 0]); - this.U.translate([0, 0, megaminxRadius]); + this.U.rotate(Math.PI, 0, 0, 1); + this.U.rotate(((180 - 116.57) * Math.PI) / 180, 1, 0, 0); + this.U.translate(0, 0, megaminxRadius); - this.L.rotate((72 * Math.PI) / 180, [0, 0, 1]); - this.L.rotate(Math.PI, [0, 0, 1]); - this.L.rotate(((180 - 116.57) * Math.PI) / 180, [1, 0, 0]); - this.L.translate([0, 0, megaminxRadius]); + this.L.rotate((72 * Math.PI) / 180, 0, 0, 1); + this.L.rotate(Math.PI, 0, 0, 1); + this.L.rotate(((180 - 116.57) * Math.PI) / 180, 1, 0, 0); + this.L.translate(0, 0, megaminxRadius); - this.R.rotate((72 * Math.PI) / 180, [0, 0, 1]); - this.R.rotate(Math.PI / 5, [0, 0, 1]); - this.R.rotate(((180 - 116.57) * Math.PI) / 180, [1, 0, 0]); - this.R.translate([0, 0, megaminxRadius]); + this.R.rotate((72 * Math.PI) / 180, 0, 0, 1); + this.R.rotate(Math.PI / 5, 0, 0, 1); + this.R.rotate(((180 - 116.57) * Math.PI) / 180, 1, 0, 0); + this.R.translate(0, 0, megaminxRadius); - this.BL.rotate(Math.PI / 5, [0, 0, 1]); - this.BL.rotate((-116.57 * Math.PI) / 180, [1, 0, 0]); - this.BL.translate([0, 0, megaminxRadius]); + this.BL.rotate(Math.PI / 5, 0, 0, 1); + this.BL.rotate((-116.57 * Math.PI) / 180, 1, 0, 0); + this.BL.translate(0, 0, megaminxRadius); - this.BR.rotate(-Math.PI / 5, [0, 0, 1]); - this.BR.rotate((-116.57 * Math.PI) / 180, [1, 0, 0]); - this.BR.translate([0, 0, megaminxRadius]); + this.BR.rotate(-Math.PI / 5, 0, 0, 1); + this.BR.rotate((-116.57 * Math.PI) / 180, 1, 0, 0); + this.BR.translate(0, 0, megaminxRadius); this.faces = { U: this.U, @@ -83,7 +78,7 @@ export class MegaminxTop { }; this.group = new Group([this.U, this.F, this.R, this.BR, this.BL, this.L]); - this.group.rotate(degreesToRadians(63), [1, 0, 0]); + this.group.rotate(degreesToRadians(63), 1, 0, 0); } setColors(colors: { [face: string]: IColor[] }) { diff --git a/src/puzzles/pyraminx.ts b/src/puzzles/pyraminx.ts index c37b99e..6eecb1c 100644 --- a/src/puzzles/pyraminx.ts +++ b/src/puzzles/pyraminx.ts @@ -3,7 +3,7 @@ import { IColor } from "./../geometry/color"; import { GREEN, RED, BLUE, YELLOW, BLACK } from "./colors"; import { TriangleLattice } from "./../geometry/triangleLattice"; import { Group } from "./../geometry/group"; -import { Object3D } from "./../geometry/object3d"; +import { Vector3 } from "../math/vector"; const ARC_COS_THIRD = Math.acos(1 / 3); const DEG_120_RADIANS = (120 * Math.PI) / 180; @@ -35,19 +35,19 @@ export class Pyraminx { this.U = U; this.B = B; - U.rotate(DEG_120_RADIANS, [0, 0, 1]); - U.rotate(ARC_COS_THIRD, [1, 0, 0]); - U.translate([0, 0, insphereRadius]); + U.rotate(DEG_120_RADIANS, 0, 0, 1); + U.rotate(ARC_COS_THIRD, 1, 0, 0); + U.translate(0, 0, insphereRadius); - R.rotate(ARC_COS_THIRD, [1, 0, 0]); - R.translate([0, 0, insphereRadius]); + R.rotate(ARC_COS_THIRD, 1, 0, 0); + R.translate(0, 0, insphereRadius); - L.rotate(-DEG_120_RADIANS, [0, 0, 1]); - L.rotate(ARC_COS_THIRD, [1, 0, 0]); - L.translate([0, 0, insphereRadius]); + L.rotate(-DEG_120_RADIANS, 0, 0, 1); + L.rotate(ARC_COS_THIRD, 1, 0, 0); + L.translate(0, 0, insphereRadius); - B.rotate(Math.PI, [0, 1, 0]); - B.translate([0, 0, insphereRadius]); + B.rotate(Math.PI, 0, 1, 0); + B.translate(0, 0, insphereRadius); this.faces = { top: this.U, diff --git a/src/puzzles/pyraminxNet.ts b/src/puzzles/pyraminxNet.ts index 427270c..1bb39e9 100644 --- a/src/puzzles/pyraminxNet.ts +++ b/src/puzzles/pyraminxNet.ts @@ -35,17 +35,17 @@ export class PyraminxNet { this.U = U; this.B = B; - R.rotate(-DEG_60_RADIANS, [0, 0, 1]); - R.translate([0, inDiameter + faceSpacing, 0]); - R.rotate(2 * DEG_60_RADIANS, [0, 0, 1]); + R.rotate(-DEG_60_RADIANS, 0, 0, 1); + R.translate(0, inDiameter + faceSpacing, 0); + R.rotate(2 * DEG_60_RADIANS, 0, 0, 1); - U.rotate(DEG_60_RADIANS, [0, 0, 1]); - U.translate([0, inDiameter + faceSpacing, 0]); - U.rotate(-2 * DEG_60_RADIANS, [0, 0, 1]); + U.rotate(DEG_60_RADIANS, 0, 0, 1); + U.translate(0, inDiameter + faceSpacing, 0); + U.rotate(-2 * DEG_60_RADIANS, 0, 0, 1); - B.rotate(3 * DEG_60_RADIANS, [0, 0, 1]); - B.translate([0, inDiameter + faceSpacing, 0]); - B.rotate(-2 * DEG_60_RADIANS, [0, 0, 1]); + B.rotate(3 * DEG_60_RADIANS, 0, 0, 1); + B.translate(0, inDiameter + faceSpacing, 0); + B.rotate(-2 * DEG_60_RADIANS, 0, 0, 1); this.faces = { top: this.U, diff --git a/src/puzzles/rubiksCube/rubiksCube.ts b/src/puzzles/rubiksCube/rubiksCube.ts index 796e582..66e4706 100644 --- a/src/puzzles/rubiksCube/rubiksCube.ts +++ b/src/puzzles/rubiksCube/rubiksCube.ts @@ -31,24 +31,24 @@ export class RubiksCube { this.L = new Group(makeGrid(cubeWidth, size, ORANGE)); this.B = new Group(makeGrid(cubeWidth, size, GREEN)); - this.U.rotate(-Math.PI / 2, [0, 1, 0]); - this.U.rotate(-Math.PI / 2, [1, 0, 0]); - this.U.translate([0, 0, halfWidth]); + this.U.rotate(-Math.PI / 2, 0, 1, 0); + this.U.rotate(-Math.PI / 2, 1, 0, 0); + this.U.translate(0, 0, halfWidth); - this.R.translate([0, 0, halfWidth]); + this.R.translate(0, 0, halfWidth); - this.F.rotate(-Math.PI / 2, [0, 1, 0]); - this.F.translate([0, 0, halfWidth]); + this.F.rotate(-Math.PI / 2, 0, 1, 0); + this.F.translate(0, 0, halfWidth); - this.D.rotate(-Math.PI / 2, [0, 1, 0]); - this.D.rotate(Math.PI / 2, [1, 0, 0]); - this.D.translate([0, 0, halfWidth]); + this.D.rotate(-Math.PI / 2, 0, 1, 0); + this.D.rotate(Math.PI / 2, 1, 0, 0); + this.D.translate(0, 0, halfWidth); - this.L.rotate(-Math.PI, [0, 1, 0]); - this.L.translate([0, 0, halfWidth]); + this.L.rotate(-Math.PI, 0, 1, 0); + this.L.translate(0, 0, halfWidth); - this.B.rotate(Math.PI / 2, [0, 1, 0]); - this.B.translate([0, 0, halfWidth]); + this.B.rotate(Math.PI / 2, 0, 1, 0); + this.B.translate(0, 0, halfWidth); this.stickers = [this.U, this.R, this.F, this.D, this.L, this.B]; diff --git a/src/puzzles/rubiksCube/rubiksCubeNet.ts b/src/puzzles/rubiksCube/rubiksCubeNet.ts index dc3afc7..5b02763 100644 --- a/src/puzzles/rubiksCube/rubiksCubeNet.ts +++ b/src/puzzles/rubiksCube/rubiksCubeNet.ts @@ -1,18 +1,10 @@ import { Geometry } from "./../../geometry/geometry"; import { IColor } from "./../../geometry/color"; -import { - YELLOW, - RED, - BLUE, - WHITE, - ORANGE, - GREEN, - BLACK, -} from "./../colors"; +import { YELLOW, RED, BLUE, WHITE, ORANGE, GREEN, BLACK } from "./../colors"; import { Group } from "./../../geometry/group"; import { Object3D } from "./../../geometry/object3d"; import { makeGrid } from "../../geometry/grid"; -import { chunkArray } from "../../utils/arrays"; +import { Vector3 } from "../../math/vector"; export class RubiksCubeNet { stickers: Object3D[]; @@ -40,21 +32,21 @@ export class RubiksCubeNet { const B = makeGrid(cubeWidth, size, GREEN); this.U = new Group(U); - this.U.translate([0, cubeWidth, 0]); + this.U.translate(0, cubeWidth, 0); this.R = new Group(R); - this.R.translate([cubeWidth, 0, 0]); + this.R.translate(cubeWidth, 0, 0); this.F = new Group(F); this.D = new Group(D); - this.D.translate([0, -cubeWidth, 0]); + this.D.translate(0, -cubeWidth, 0); this.L = new Group(L); - this.L.translate([-cubeWidth, 0, 0]); + this.L.translate(-cubeWidth, 0, 0); this.B = new Group(B); - this.B.translate([2 * cubeWidth, 0, 0]); + this.B.translate(2 * cubeWidth, 0, 0); this.stickers = [this.U, this.R, this.F, this.D, this.L, this.B]; @@ -68,8 +60,8 @@ export class RubiksCubeNet { }; this.group = new Group(this.stickers); - this.group.translate([-cubeWidth / 4, 0, 0]); - this.group.scale([0.5, 0.5, 0.5]); + this.group.translate(-cubeWidth / 4, 0, 0); + this.group.scale(0.5, 0.5, 0.5); } private setFaceColors(faceStickers: Group, colors: IColor[]) { diff --git a/src/puzzles/rubiksCube/rubiksCubeTop.ts b/src/puzzles/rubiksCube/rubiksCubeTop.ts index 89bc394..c6faaa4 100644 --- a/src/puzzles/rubiksCube/rubiksCubeTop.ts +++ b/src/puzzles/rubiksCube/rubiksCubeTop.ts @@ -1,18 +1,11 @@ import { IColor } from "./../../geometry/color"; -import { vec3 } from "gl-matrix"; -import { - YELLOW, - RED, - BLUE, - GREEN, - ORANGE, - BLACK, -} from "./../colors"; +import { YELLOW, RED, BLUE, GREEN, ORANGE, BLACK } from "./../colors"; import { Geometry } from "./../../geometry/geometry"; import { Group } from "./../../geometry/group"; import { Object3D } from "./../../geometry/object3d"; import { makeGrid, makeRow } from "./../../geometry/grid"; import { calculateCentroid } from "../../math/utils"; +import { Vector3 } from "../../math/vector"; export class RubiksCubeTopLayer { stickers: Object3D[]; @@ -46,16 +39,16 @@ export class RubiksCubeTopLayer { this.L = new Group(makeRow(this.cubeWidth, this.size, ORANGE)); const borderOffset = this.halfCubeWidth + this.halfStickerWidth; - this.B.translate([0, borderOffset, 0]); - this.B.rotate(Math.PI, [0, 0, 1]); + this.B.translate(0, borderOffset, 0); + this.B.rotate(Math.PI, 0, 0, 1); - this.F.translate([0, -borderOffset, 0]); + this.F.translate(0, -borderOffset, 0); - this.R.translate([borderOffset, 0, 0]); - this.R.rotate(Math.PI / 2, [0, 0, 1]); + this.R.translate(borderOffset, 0, 0); + this.R.rotate(Math.PI / 2, 0, 0, 1); - this.L.translate([-borderOffset, 0, 0]); - this.L.rotate(-Math.PI / 2, [0, 0, 1]); + this.L.translate(-borderOffset, 0, 0); + this.L.rotate(-Math.PI / 2, 0, 0, 1); this.rotateBorder(this.F.objects as Geometry[], rotationAngle); this.rotateBorder(this.R.objects as Geometry[], rotationAngle); @@ -101,10 +94,8 @@ export class RubiksCubeTopLayer { private rotateBorder(stickers: Geometry[], radians: number) { stickers.forEach((sticker) => { sticker.vertices = sticker.vertices.map((vertex) => { - return vec3.rotateX( - vertex, - vertex, - [0, this.halfStickerWidth, 0], + return vertex.rotateX( + Vector3.fromValues(0, this.halfStickerWidth, 0), radians ); }); diff --git a/src/puzzles/skewb.ts b/src/puzzles/skewb.ts index 3ba6c68..b719e82 100644 --- a/src/puzzles/skewb.ts +++ b/src/puzzles/skewb.ts @@ -3,9 +3,9 @@ import { Object3D } from "./../geometry/object3d"; import { RED, YELLOW, BLUE, ORANGE, GREEN, WHITE, BLACK } from "./colors"; import { Group } from "../geometry/group"; import { IColor } from "../geometry/color"; -import { vec3 } from "gl-matrix"; import { Plane } from "../geometry/plane"; import { Triangle } from "../geometry/triangle"; +import { Vector3 } from "../math/vector"; export class Skewb { stickers: Object3D[]; @@ -25,11 +25,19 @@ export class Skewb { const halfWidth = cubeWidth / 2; const red = new Group(this.makeStickers(RED, centerWidth)); - const yellow = new Group(this.makeStickers(YELLOW, centerWidth, [1, 0, 0])); - const blue = new Group(this.makeStickers(BLUE, centerWidth, [0, 1, 0])); + const yellow = new Group( + this.makeStickers(YELLOW, centerWidth, Vector3.fromValues(1, 0, 0)) + ); + const blue = new Group( + this.makeStickers(BLUE, centerWidth, Vector3.fromValues(0, 1, 0)) + ); const orange = new Group(this.makeStickers(ORANGE, centerWidth)); - const green = new Group(this.makeStickers(GREEN, centerWidth, [0, 1, 0])); - const white = new Group(this.makeStickers(WHITE, centerWidth, [1, 0, 0])); + const green = new Group( + this.makeStickers(GREEN, centerWidth, Vector3.fromValues(0, 1, 0)) + ); + const white = new Group( + this.makeStickers(WHITE, centerWidth, Vector3.fromValues(1, 0, 0)) + ); this.U = yellow; this.R = red; @@ -47,51 +55,55 @@ export class Skewb { bottom: this.D, }; - red.translate([0, 0, halfWidth]); - red.rotate(Math.PI, [1, 0, 0]); - red.rotate(Math.PI / 2, [0, 0, 1]); + red.translate(0, 0, halfWidth); + red.rotate(Math.PI, 1, 0, 0); + red.rotate(Math.PI / 2, 0, 0, 1); - orange.rotate(-Math.PI / 2, [0, 0, 1]); - orange.translate([0, 0, -halfWidth]); + orange.rotate(-Math.PI / 2, 0, 0, 1); + orange.translate(0, 0, -halfWidth); - blue.rotate(-Math.PI / 2, [1, 0, 0]); - blue.translate([-halfWidth, 0, 0]); + blue.rotate(-Math.PI / 2, 1, 0, 0); + blue.translate(-halfWidth, 0, 0); - green.translate([halfWidth, 0, 0]); - green.rotate(Math.PI, [0, 1, 0]); - green.rotate(-Math.PI / 2, [1, 0, 0]); + green.translate(halfWidth, 0, 0); + green.rotate(Math.PI, 0, 1, 0); + green.rotate(-Math.PI / 2, 1, 0, 0); - yellow.rotate(Math.PI, [0, 1, 0]); - yellow.translate([0, halfWidth, 0]); + yellow.rotate(Math.PI, 0, 1, 0); + yellow.translate(0, halfWidth, 0); - white.translate([0, -halfWidth, 0]); - white.rotate(Math.PI, [1, 0, 0]); + white.translate(0, -halfWidth, 0); + white.rotate(Math.PI, 1, 0, 0); this.stickers = [red, yellow, blue, orange, green, white]; this.group = new Group(this.stickers); } - private makeStickers(color: IColor, width: number, axis?: vec3): Geometry[] { + private makeStickers( + color: IColor, + width: number, + axis?: Vector3 + ): Geometry[] { const center = new Plane(width, width, color); if (axis) { - center.rotate(Math.PI / 2, axis); + center.rotate(Math.PI / 2, axis.x, axis.y, axis.z); } - center.rotate(Math.PI / 4, [0, 0, 1]); - center.translate([-width / 2, width / 2, 0]); + center.rotate(Math.PI / 4, 0, 0, 1); + center.translate(-width / 2, width / 2, 0); const triangles = []; for (let i = 0; i < 4; i++) { const triangle = new Triangle( - [-width / 2, width / 2, 0], - [0, width, 0], - [width / 2, width / 2, 0], + Vector3.fromValues(-width / 2, width / 2, 0), + Vector3.fromValues(0, width, 0), + Vector3.fromValues(width / 2, width / 2, 0), color ); if (axis) { - triangle.rotate(Math.PI / 2, axis); + triangle.rotate(Math.PI / 2, axis.x, axis.y, axis.z); } - triangle.rotate((Math.PI / 2) * i, [0, 0, 1]); - triangle.rotate(Math.PI / 4, [0, 0, 1]); + triangle.rotate((Math.PI / 2) * i, 0, 0, 1); + triangle.rotate(Math.PI / 4, 0, 0, 1); triangles.push(triangle); } diff --git a/src/puzzles/skewbNet.ts b/src/puzzles/skewbNet.ts index 476b3f6..abd66a6 100644 --- a/src/puzzles/skewbNet.ts +++ b/src/puzzles/skewbNet.ts @@ -2,11 +2,10 @@ import { Object3D } from "./../geometry/object3d"; import { RED, YELLOW, BLUE, ORANGE, GREEN, WHITE, BLACK } from "./colors"; import { Group } from "../geometry/group"; import { IColor } from "../geometry/color"; -import { vec3 } from "gl-matrix"; import { Plane } from "../geometry/plane"; import { Geometry } from "../geometry/geometry"; import { Triangle } from "../geometry/triangle"; -import { chunkArray } from "../utils/arrays"; +import { Vector3 } from "../math/vector"; export class SkewbNet { stickers: Object3D[]; @@ -25,21 +24,39 @@ export class SkewbNet { const centerWidth = Math.sqrt(Math.pow(cubeWidth / 2, 2) * 2); const orange = new Group( - this.makeStickers(ORANGE, centerWidth, [-cubeWidth, 0, 0]) + this.makeStickers( + ORANGE, + centerWidth, + Vector3.fromValues(-cubeWidth, 0, 0) + ) ); const green = new Group( - this.makeStickers(GREEN, centerWidth, [2 * cubeWidth, 0, 0]) + this.makeStickers( + GREEN, + centerWidth, + Vector3.fromValues(2 * cubeWidth, 0, 0) + ) ); const white = new Group( - this.makeStickers(WHITE, centerWidth, [0, -cubeWidth, 0]) + this.makeStickers( + WHITE, + centerWidth, + Vector3.fromValues(0, -cubeWidth, 0) + ) ); const red = new Group( - this.makeStickers(RED, centerWidth, [cubeWidth, 0, 0]) + this.makeStickers(RED, centerWidth, Vector3.fromValues(cubeWidth, 0, 0)) ); const yellow = new Group( - this.makeStickers(YELLOW, centerWidth, [0, cubeWidth, 0]) + this.makeStickers( + YELLOW, + centerWidth, + Vector3.fromValues(0, cubeWidth, 0) + ) + ); + const blue = new Group( + this.makeStickers(BLUE, centerWidth, Vector3.fromValues(0, 0, 0)) ); - const blue = new Group(this.makeStickers(BLUE, centerWidth, [0, 0, 0])); this.U = yellow; this.R = red; @@ -60,33 +77,33 @@ export class SkewbNet { this.stickers = [red, yellow, blue, orange, green, white]; this.group = new Group(this.stickers); - this.group.translate([-cubeWidth / 4, 0, 0]); - this.group.scale([0.5, 0.5, 0.5]); + this.group.translate(-cubeWidth / 4, 0, 0); + this.group.scale(0.5, 0.5, 0.5); } private makeStickers( color: IColor, width: number, - translate: vec3 + translate: Vector3 ): Geometry[] { const center = new Plane(width, width, color); - center.translate(translate); - center.rotate(Math.PI / 4, [0, 0, 1]); - center.translate([-width / 2, width / 2, 0]); + center.translate(translate.x, translate.y, translate.z); + center.rotate(Math.PI / 4, 0, 0, 1); + center.translate(-width / 2, width / 2, 0); const triangles = []; for (let i = 0; i < 4; i++) { const triangle = new Triangle( - [-width / 2, width / 2, 0], - [0, width, 0], - [width / 2, width / 2, 0], + Vector3.fromValues(-width / 2, width / 2, 0), + Vector3.fromValues(0, width, 0), + Vector3.fromValues(width / 2, width / 2, 0), color ); - triangle.translate(translate); - triangle.rotate((-Math.PI / 2) * i, [0, 0, 1]); - triangle.rotate(Math.PI / 4, [0, 0, 1]); + triangle.translate(translate.x, translate.y, translate.z); + triangle.rotate((-Math.PI / 2) * i, 0, 0, 1); + triangle.rotate(Math.PI / 4, 0, 0, 1); triangles.push(triangle); } diff --git a/src/puzzles/square1/constants.ts b/src/puzzles/square1/constants.ts index 3d3c87b..8e94714 100644 --- a/src/puzzles/square1/constants.ts +++ b/src/puzzles/square1/constants.ts @@ -2,9 +2,13 @@ import { WHITE, RED, BLUE, GREEN, ORANGE, YELLOW } from "./../colors"; import { IColor } from "./../../geometry/color"; import { PIECE_TYPE } from "./enum"; import { Sqaure1Piece } from "./interface"; -import { vec3 } from "gl-matrix"; +import { Vector3 } from "../../math/vector"; -export const ROTATION_VECTOR: vec3 = [0.92875, -0.24803, 0]; +export const ROTATION_VECTOR: Vector3 = Vector3.fromValues( + 0.92875, + -0.24803, + 0 +); export const TOP_COLOR: IColor = YELLOW; export const BOTTOM_COLOR: IColor = WHITE; diff --git a/src/puzzles/square1/interface.ts b/src/puzzles/square1/interface.ts index 802d1c3..18ce0bd 100644 --- a/src/puzzles/square1/interface.ts +++ b/src/puzzles/square1/interface.ts @@ -73,13 +73,13 @@ export abstract class Square1Builder { piece.colors[1], piece.colors[2] ); - corner.rotate(angle, [0, 0, 1]); + corner.rotate(angle, 0, 0, 1); geometry.push(corner); angle -= DEG_60_RADIANS; break; case PIECE_TYPE.EDGE: const edge = this.square1Edge(piece.colors[0], piece.colors[1]); - edge.rotate(angle - DEG_60_RADIANS, [0, 0, 1]); + edge.rotate(angle - DEG_60_RADIANS, 0, 0, 1); geometry.push(edge); angle -= DEG_30_RADIANS; break; diff --git a/src/puzzles/square1/square1.ts b/src/puzzles/square1/square1.ts index dbdb0dd..f3d543b 100644 --- a/src/puzzles/square1/square1.ts +++ b/src/puzzles/square1/square1.ts @@ -12,11 +12,11 @@ import { Sqaure1Piece, Square1Builder } from "./interface"; import { DEG_30_RADIANS } from "./../../math/constants"; import { Geometry } from "./../../geometry/geometry"; import { IFace, Face } from "./../../geometry/face"; -import { vec3 } from "gl-matrix"; import { IColor } from "./../../geometry/color"; import { Group } from "../../geometry/group"; +import { Vector3 } from "../../math/vector"; -const INNER_FACE_COLOR = { value: '#333', stroke: '#333' }; +const INNER_FACE_COLOR = { value: "#333", stroke: "#333" }; export class Square1 extends Square1Builder { constructor( @@ -29,18 +29,30 @@ export class Square1 extends Square1Builder { } square1Corner(top: IColor, side1: IColor, side2: IColor): Geometry { - const points: vec3[] = [ + const points: Vector3[] = [ // Top - [0, 0, this.halfSide], - [this.halfSide, this.halfEdgePiece, this.halfSide], - [this.halfSide, this.halfSide, this.halfSide], - [this.halfEdgePiece, this.halfSide, this.halfSide], + Vector3.fromValues(0, 0, this.halfSide), + Vector3.fromValues(this.halfSide, this.halfEdgePiece, this.halfSide), + Vector3.fromValues(this.halfSide, this.halfSide, this.halfSide), + Vector3.fromValues(this.halfEdgePiece, this.halfSide, this.halfSide), // Bottom - [0, 0, this.halfSide - this.layerWidth], - [this.halfSide, this.halfEdgePiece, this.halfSide - this.layerWidth], - [this.halfSide, this.halfSide, this.halfSide - this.layerWidth], - [this.halfEdgePiece, this.halfSide, this.halfSide - this.layerWidth], + Vector3.fromValues(0, 0, this.halfSide - this.layerWidth), + Vector3.fromValues( + this.halfSide, + this.halfEdgePiece, + this.halfSide - this.layerWidth + ), + Vector3.fromValues( + this.halfSide, + this.halfSide, + this.halfSide - this.layerWidth + ), + Vector3.fromValues( + this.halfEdgePiece, + this.halfSide, + this.halfSide - this.layerWidth + ), ]; const faces: IFace[] = [ @@ -56,7 +68,11 @@ export class Square1 extends Square1Builder { new Face([0, 3, 7, 4], points, INNER_FACE_COLOR), ]; - const innerCentroid = vec3.fromValues(this.halfSide / 2, this.halfSide / 2, this.halfSide / 2); + const innerCentroid = Vector3.fromValues( + this.halfSide / 2, + this.halfSide / 2, + this.halfSide / 2 + ); faces[1].centroid = innerCentroid; faces[2].centroid = innerCentroid; faces[5].centroid = innerCentroid; @@ -65,46 +81,38 @@ export class Square1 extends Square1Builder { } square1Edge(top: IColor, side: IColor): Geometry { - const points: vec3[] = [ + const points: Vector3[] = [ // Top - vec3.rotateZ( - vec3.create(), - [0, 0, this.halfSide], - [0, 0, 0], - DEG_30_RADIANS - ), - vec3.rotateZ( - vec3.create(), - [this.halfEdgePiece, this.halfSide, this.halfSide], - [0, 0, 0], - DEG_30_RADIANS - ), - vec3.rotateZ( - vec3.create(), - [-this.halfEdgePiece, this.halfSide, this.halfSide], - [0, 0, 0], + Vector3.fromValues(0, 0, this.halfSide).rotateZ( + Vector3.fromValues(0, 0, 0), DEG_30_RADIANS ), + Vector3.fromValues( + this.halfEdgePiece, + this.halfSide, + this.halfSide + ).rotateZ(Vector3.fromValues(0, 0, 0), DEG_30_RADIANS), + Vector3.fromValues( + -this.halfEdgePiece, + this.halfSide, + this.halfSide + ).rotateZ(Vector3.fromValues(0, 0, 0), DEG_30_RADIANS), // Bottom - vec3.rotateZ( - vec3.create(), - [0, 0, this.halfSide - this.layerWidth], - [0, 0, 0], - DEG_30_RADIANS - ), - vec3.rotateZ( - vec3.create(), - [this.halfEdgePiece, this.halfSide, this.halfSide - this.layerWidth], - [0, 0, 0], - DEG_30_RADIANS - ), - vec3.rotateZ( - vec3.create(), - [-this.halfEdgePiece, this.halfSide, this.halfSide - this.layerWidth], - [0, 0, 0], + Vector3.fromValues(0, 0, this.halfSide - this.layerWidth).rotateZ( + Vector3.fromValues(0, 0, 0), DEG_30_RADIANS ), + Vector3.fromValues( + this.halfEdgePiece, + this.halfSide, + this.halfSide - this.layerWidth + ).rotateZ(Vector3.fromValues(0, 0, 0), DEG_30_RADIANS), + Vector3.fromValues( + -this.halfEdgePiece, + this.halfSide, + this.halfSide - this.layerWidth + ).rotateZ(Vector3.fromValues(0, 0, 0), DEG_30_RADIANS), ]; const faces: IFace[] = [ @@ -115,12 +123,11 @@ export class Square1 extends Square1Builder { new Face([0, 2, 5, 3], points, INNER_FACE_COLOR), ]; - const innerFaceCentroid = vec3.rotateZ( - vec3.create(), - [0, this.halfSide / 2, this.halfSide / 2], - [0, 0, 0], - DEG_30_RADIANS - ); + const innerFaceCentroid = Vector3.fromValues( + 0, + this.halfSide / 2, + this.halfSide / 2 + ).rotateZ(Vector3.fromValues(0, 0, 0), DEG_30_RADIANS); // Override centroid to avoid drawing over outside stickers faces[1].centroid = innerFaceCentroid; @@ -131,16 +138,32 @@ export class Square1 extends Square1Builder { } square1Middle(front: IColor, side: IColor, back: IColor): Geometry { - const vertices: vec3[] = [ - [-this.halfSide, -this.halfSide, this.halfMiddleWidth], - [-this.halfSide, this.halfSide, this.halfMiddleWidth], - [this.halfEdgePiece, this.halfSide, this.halfMiddleWidth], - [-this.halfEdgePiece, -this.halfSide, this.halfMiddleWidth], - - [-this.halfSide, -this.halfSide, -this.halfMiddleWidth], - [-this.halfSide, this.halfSide, -this.halfMiddleWidth], - [this.halfEdgePiece, this.halfSide, -this.halfMiddleWidth], - [-this.halfEdgePiece, -this.halfSide, -this.halfMiddleWidth], + const vertices: Vector3[] = [ + Vector3.fromValues(-this.halfSide, -this.halfSide, this.halfMiddleWidth), + Vector3.fromValues(-this.halfSide, this.halfSide, this.halfMiddleWidth), + Vector3.fromValues( + this.halfEdgePiece, + this.halfSide, + this.halfMiddleWidth + ), + Vector3.fromValues( + -this.halfEdgePiece, + -this.halfSide, + this.halfMiddleWidth + ), + + Vector3.fromValues(-this.halfSide, -this.halfSide, -this.halfMiddleWidth), + Vector3.fromValues(-this.halfSide, this.halfSide, -this.halfMiddleWidth), + Vector3.fromValues( + this.halfEdgePiece, + this.halfSide, + -this.halfMiddleWidth + ), + Vector3.fromValues( + -this.halfEdgePiece, + -this.halfSide, + -this.halfMiddleWidth + ), ]; const faces: IFace[] = [ @@ -152,12 +175,16 @@ export class Square1 extends Square1Builder { new Face([0, 3, 7, 4], vertices, front), ]; - const innerFaceCentroid = vec3.fromValues(-this.halfSide / 2, 0, 0); + const innerFaceCentroid = Vector3.fromValues(-this.halfSide / 2, 0, 0); // Override centroid to avoid drawing over outside stickers faces[0].centroid = innerFaceCentroid; faces[1].centroid = innerFaceCentroid; - faces[2].centroid = vec3.fromValues(-(this.halfSide + (this.halfSide * .45)), 0, 0); + faces[2].centroid = Vector3.fromValues( + -(this.halfSide + this.halfSide * 0.45), + 0, + 0 + ); return new Geometry(vertices, faces); } @@ -170,17 +197,22 @@ export class Square1 extends Square1Builder { const topLayer = new Group(this.makeLayer(top)); const bottomLayer = new Group(this.makeLayer(bottom)); - bottomLayer.rotate(Math.PI, [1, 0, 0]); - bottomLayer.rotate(DEG_30_RADIANS, [0, 0, 1]); + bottomLayer.rotate(Math.PI, 1, 0, 0); + bottomLayer.rotate(DEG_30_RADIANS, 0, 0, 1); const pieces: Object3D[] = [topLayer, bottomLayer]; const m1 = this.square1Middle(FRONT_COLOR, LEFT_COLOR, BACK_COLOR); const m2 = this.square1Middle(BACK_COLOR, RIGHT_COLOR, FRONT_COLOR); - m2.rotate(Math.PI, [0, 0, 1]); + m2.rotate(Math.PI, 0, 0, 1); if (middleRotated) { - m2.rotate(Math.PI, ROTATION_VECTOR); + m2.rotate( + Math.PI, + ROTATION_VECTOR.x, + ROTATION_VECTOR.y, + ROTATION_VECTOR.z + ); } pieces.push(m1); diff --git a/src/puzzles/square1/square1Net.ts b/src/puzzles/square1/square1Net.ts index d6b1907..e424418 100644 --- a/src/puzzles/square1/square1Net.ts +++ b/src/puzzles/square1/square1Net.ts @@ -1,26 +1,26 @@ import { Sqaure1Piece, Square1Builder } from "./interface"; import { DEG_30_RADIANS } from "./../../math/constants"; import { IFace, Face } from "./../../geometry/face"; -import { vec3 } from "gl-matrix"; import { IColor } from "./../../geometry/color"; import { Geometry } from "./../../geometry/geometry"; import { Group } from "./../../geometry/group"; import { Object3D } from "./../../geometry/object3d"; import { FRONT_COLOR, RIGHT_COLOR, BACK_COLOR } from "./constants"; +import { Vector3 } from "../../math/vector"; export class Square1Net extends Square1Builder { square1Corner(top: IColor, side1: IColor, side2: IColor): Geometry { - const points: vec3[] = [ + const points: Vector3[] = [ // Top - [0, 0, 0], - [this.halfSide, this.halfEdgePiece, 0], - [this.halfSide, this.halfSide, 0], - [this.halfEdgePiece, this.halfSide, 0], + Vector3.fromValues(0, 0, 0), + Vector3.fromValues(this.halfSide, this.halfEdgePiece, 0), + Vector3.fromValues(this.halfSide, this.halfSide, 0), + Vector3.fromValues(this.halfEdgePiece, this.halfSide, 0), // Sides - [this.outerHalfSide, this.outerHalfEdgePiece, 0], - [this.outerHalfSide, this.outerHalfSide, 0], - [this.outerHalfEdgePiece, this.outerHalfSide, 0], + Vector3.fromValues(this.outerHalfSide, this.outerHalfEdgePiece, 0), + Vector3.fromValues(this.outerHalfSide, this.outerHalfSide, 0), + Vector3.fromValues(this.outerHalfEdgePiece, this.outerHalfSide, 0), ]; const faces: IFace[] = [ @@ -33,35 +33,32 @@ export class Square1Net extends Square1Builder { } square1Edge(top: IColor, side: IColor): Geometry { - const points: vec3[] = [ + const points: Vector3[] = [ // Top - vec3.rotateZ(vec3.create(), [0, 0, 0], [0, 0, 0], DEG_30_RADIANS), - vec3.rotateZ( - vec3.create(), - [this.halfEdgePiece, this.halfSide, 0], - [0, 0, 0], + Vector3.fromValues(0, 0, 0).rotateZ( + Vector3.fromValues(0, 0, 0), DEG_30_RADIANS ), - vec3.rotateZ( - vec3.create(), - [-this.halfEdgePiece, this.halfSide, 0], - [0, 0, 0], + Vector3.fromValues(this.halfEdgePiece, this.halfSide, 0).rotateZ( + Vector3.fromValues(0, 0, 0), DEG_30_RADIANS ), - - // Side - vec3.rotateZ( - vec3.create(), - [this.outerHalfEdgePiece, this.outerHalfSide, 0], - [0, 0, 0], - DEG_30_RADIANS - ), - vec3.rotateZ( - vec3.create(), - [-this.outerHalfEdgePiece, this.outerHalfSide, 0], - [0, 0, 0], + Vector3.fromValues(-this.halfEdgePiece, this.halfSide, 0).rotateZ( + Vector3.fromValues(0, 0, 0), DEG_30_RADIANS ), + + // Side + Vector3.fromValues( + this.outerHalfEdgePiece, + this.outerHalfSide, + 0 + ).rotateZ(Vector3.fromValues(0, 0, 0), DEG_30_RADIANS), + Vector3.fromValues( + -this.outerHalfEdgePiece, + this.outerHalfSide, + 0 + ).rotateZ(Vector3.fromValues(0, 0, 0), DEG_30_RADIANS), ]; const faces: IFace[] = [ @@ -83,20 +80,20 @@ export class Square1Net extends Square1Builder { const halfMiddleHeight = middleHeight / 2; const cornerLength = this.outerHalfSide - this.outerHalfEdgePiece; - const vertices: vec3[] = [ - [-this.outerHalfSide, halfMiddleHeight, -0.01], - [-this.outerHalfEdgePiece, halfMiddleHeight, -0.01], - [this.outerHalfSide, halfMiddleHeight, -0.01], + const vertices: Vector3[] = [ + Vector3.fromValues(-this.outerHalfSide, halfMiddleHeight, -0.01), + Vector3.fromValues(-this.outerHalfEdgePiece, halfMiddleHeight, -0.01), + Vector3.fromValues(this.outerHalfSide, halfMiddleHeight, -0.01), - [-this.outerHalfSide, -halfMiddleHeight, -0.01], - [-this.outerHalfEdgePiece, -halfMiddleHeight, -0.01], - [this.outerHalfSide, -halfMiddleHeight, -0.01], + Vector3.fromValues(-this.outerHalfSide, -halfMiddleHeight, -0.01), + Vector3.fromValues(-this.outerHalfEdgePiece, -halfMiddleHeight, -0.01), + Vector3.fromValues(this.outerHalfSide, -halfMiddleHeight, -0.01), // Points for when middle is rotated - [2 * this.outerHalfEdgePiece, halfMiddleHeight, -0.01], - [2 * this.outerHalfEdgePiece, -halfMiddleHeight, -0.01], - [2 * cornerLength, halfMiddleHeight, -0.01], - [2 * cornerLength, -halfMiddleHeight, -0.01], + Vector3.fromValues(2 * this.outerHalfEdgePiece, halfMiddleHeight, -0.01), + Vector3.fromValues(2 * this.outerHalfEdgePiece, -halfMiddleHeight, -0.01), + Vector3.fromValues(2 * cornerLength, halfMiddleHeight, -0.01), + Vector3.fromValues(2 * cornerLength, -halfMiddleHeight, -0.01), ]; // Left @@ -127,9 +124,9 @@ export class Square1Net extends Square1Builder { const topLayer = new Group(this.makeLayer(top)); const bottomLayer = new Group(this.makeLayer(bottom)); - topLayer.translate([0, this.outerHalfSide + halfMiddleHeight, 0]); - bottomLayer.translate([0, -(this.outerHalfSide + halfMiddleHeight), 0]); - bottomLayer.rotate(DEG_30_RADIANS, [0, 0, 1]); + topLayer.translate(0, this.outerHalfSide + halfMiddleHeight, 0); + bottomLayer.translate(0, -(this.outerHalfSide + halfMiddleHeight), 0); + bottomLayer.rotate(DEG_30_RADIANS, 0, 0, 1); pieces = [topLayer, bottomLayer]; diff --git a/src/rendering/camera.ts b/src/rendering/camera.ts index 3751346..c8e440d 100644 --- a/src/rendering/camera.ts +++ b/src/rendering/camera.ts @@ -1,12 +1,11 @@ -import { mat4 } from "gl-matrix"; +import { Matrix4 } from "../math/matrix"; export class Camera { - matrix: mat4; + matrix: Matrix4; constructor() { - this.matrix = mat4.create(); - mat4.perspective(this.matrix, Math.PI / 2, 1, 0.1, 1000); - mat4.translate(this.matrix, this.matrix, [0, 0, -5]); - mat4.scale(this.matrix, this.matrix, [4, 4, 1]); + this.matrix = Matrix4.perspective(Math.PI / 2, 1, 0.1, 1000); + this.matrix.translate(0, 0, -5); + this.matrix.scale(4, 4, 1); } } diff --git a/src/rendering/htmlCanvasRenderer.ts b/src/rendering/htmlCanvasRenderer.ts index fc592ac..134d7e1 100644 --- a/src/rendering/htmlCanvasRenderer.ts +++ b/src/rendering/htmlCanvasRenderer.ts @@ -1,5 +1,5 @@ -import { vec3 } from "gl-matrix"; import { IColor } from "../geometry/color"; +import { Vector3 } from "../math/vector"; import { BLACK } from "../puzzles/colors"; import { Polygon, PolygonRenderer } from "./polygonRenderer"; @@ -64,15 +64,15 @@ export class HtmlCanvasRenderer extends PolygonRenderer { this.ctx.strokeStyle = "#000000"; this.ctx.moveTo( - this.convertRange(polygon.points[0][0], this.width), - this.convertRange(polygon.points[0][1], this.height) + this.convertRange(polygon.points[0].x, this.width), + this.convertRange(polygon.points[0].y, this.height) ); this.ctx.beginPath(); for (let i = 0; i <= polygon.points.length; i++) { let point = polygon.points[(i + 1) % polygon.points.length]; this.ctx.lineTo( - this.convertRange(point[0], this.width), - this.convertRange(point[1], this.height) + this.convertRange(point.x, this.width), + this.convertRange(point.y, this.height) ); } @@ -81,11 +81,11 @@ export class HtmlCanvasRenderer extends PolygonRenderer { this.ctx.stroke(); } - drawArrow(p1: vec3, p2: vec3, uid: string) { - const toX = this.convertRange(p2[0], this.width); - const toY = this.convertRange(-p2[1], this.height); - const fromX = this.convertRange(p1[0], this.width); - const fromY = this.convertRange(-p1[1], this.height); + drawArrow(p1: Vector3, p2: Vector3, uid: string) { + const toX = this.convertRange(p2.x, this.width); + const toY = this.convertRange(-p2.y, this.height); + const fromX = this.convertRange(p1.x, this.width); + const fromY = this.convertRange(-p1.y, this.height); const headlen = 20; // length of head in pixels const dx = toX - fromX; const dy = toY - fromY; diff --git a/src/rendering/htmlSvgRenderer.ts b/src/rendering/htmlSvgRenderer.ts index e927e2d..86c46b8 100644 --- a/src/rendering/htmlSvgRenderer.ts +++ b/src/rendering/htmlSvgRenderer.ts @@ -1,5 +1,4 @@ import { BLACK } from "./../puzzles/colors"; -import { vec3 } from "gl-matrix"; import { IColor } from "../geometry/color"; import { Polygon, PolygonRenderer } from "./polygonRenderer"; import { @@ -9,6 +8,7 @@ import { createArrowLineElement, createMarkers, } from "../svg/svg"; +import { Vector3 } from "../math/vector"; /** * Renderer to draw puzzles using html svg elements @@ -70,7 +70,7 @@ export class HtmlSvgRenderer extends PolygonRenderer { this.domElement.appendChild(this.svgElement); } - onBeforeRender() { } + onBeforeRender() {} drawPolygon({ points, face, object }: Polygon) { if (!this.uidToPolygon[face.uid]) { @@ -94,7 +94,7 @@ export class HtmlSvgRenderer extends PolygonRenderer { this.svgElement.appendChild(this.uidToPolygon[face.uid]); } - drawArrow(p1Screen: vec3, p2Screen: vec3, uid: string) { + drawArrow(p1Screen: Vector3, p2Screen: Vector3, uid: string) { let arrow: SVGLineElement; if (!this.uidToLine[uid]) { diff --git a/src/rendering/polygonRenderer.ts b/src/rendering/polygonRenderer.ts index 482d71a..54a028e 100644 --- a/src/rendering/polygonRenderer.ts +++ b/src/rendering/polygonRenderer.ts @@ -1,4 +1,3 @@ -import { mat4, vec3 } from "gl-matrix"; import { Arrow } from "./../geometry/arrow"; import { Object3D } from "../geometry/object3d"; import { Geometry } from "../geometry/geometry"; @@ -8,12 +7,14 @@ import { Renderer } from "./renderer"; import { Scene } from "./scene"; import { applyTransformations } from "./utils"; import { Face, IFace } from "../geometry/face"; +import { Vector3 } from "../math/vector"; +import { Matrix4 } from "../math/matrix"; export interface Polygon { - points: vec3[]; // Screen points for the polygon to render + points: Vector3[]; // Screen points for the polygon to render face: Face; // original geometry face the polygon comes from object: Geometry; // parent geometry - centroid: vec3; // centroid of the face in 3d space + centroid: Vector3; // centroid of the face in 3d space } /** @@ -32,11 +33,11 @@ export interface Polygon { * - onComplete - handle any final logic */ export abstract class PolygonRenderer implements Renderer { - protected polygons = []; + protected polygons: Polygon[] = []; protected arrows = []; abstract drawPolygon(polygon: Polygon); - abstract drawArrow(p1: vec3, p2: vec3, uid: string); + abstract drawArrow(p1: Vector3, p2: Vector3, uid: string); abstract onBeforeRender(); abstract onComplete(); @@ -54,8 +55,8 @@ export abstract class PolygonRenderer implements Renderer { } protected renderPolygons() { - this.polygons.sort((a, b) => { - return a.centroid[2] - b.centroid[2]; + this.polygons.sort((a: Polygon, b: Polygon) => { + return a.centroid.z - b.centroid.z; }); this.polygons.forEach((p) => this.drawPolygon(p)); @@ -70,7 +71,7 @@ export abstract class PolygonRenderer implements Renderer { protected renderObject3D( object: Object3D, camera: Camera, - transformations: mat4[] + transformations: Matrix4[] ) { if (object instanceof Geometry) { this.renderGeometry(object, camera, transformations); @@ -78,10 +79,10 @@ export abstract class PolygonRenderer implements Renderer { this.renderArrow(object, camera, transformations); } else if (object instanceof Group) { let group = object; - this.sortObjects(group.objects, camera, [ - group.matrix, - ...transformations, - ]); + // let sorted = this.sortObjects(group.objects, camera, [ + // group.matrix, + // ...transformations, + // ]); group.objects.forEach((object) => { this.renderObject3D(object, camera, [group.matrix, ...transformations]); }); @@ -91,12 +92,12 @@ export abstract class PolygonRenderer implements Renderer { protected renderGeometry( object: Geometry, camera: Camera, - transformations: mat4[] + transformations: Matrix4[] ) { // this.sortFaces(object.faces, object, transformations); object.faces.forEach((face) => { - let points: vec3[] = []; + let points: Vector3[] = []; face.indices .map((index) => object.vertices[index]) .forEach((vertex) => { @@ -105,10 +106,10 @@ export abstract class PolygonRenderer implements Renderer { ...transformations, camera.matrix, ]; - let v: vec3 = applyTransformations(vertex, objectToScreen); + let screenPoint: Vector3 = applyTransformations(vertex, objectToScreen); // Need to flip y to look correct on svg viewbox - let screenPoint = vec3.multiply(v, v, [1, -1, 1]); + screenPoint.multiply(1, -1, 1); points.push(screenPoint); }); @@ -119,7 +120,7 @@ export abstract class PolygonRenderer implements Renderer { protected renderArrow( object: Arrow, camera: Camera, - transformations: mat4[] + transformations: Matrix4[] ) { let objectToScreen = [object.matrix, ...transformations, camera.matrix]; let p1Screen = applyTransformations(object.p1, objectToScreen); @@ -129,10 +130,10 @@ export abstract class PolygonRenderer implements Renderer { } protected addPolygon( - points, + points: Vector3[], face: IFace, object: Geometry, - transformations: mat4[] + transformations: Matrix4[] ) { this.polygons.push({ points, @@ -148,17 +149,19 @@ export abstract class PolygonRenderer implements Renderer { protected sortObjects( objects: Object3D[], camera: Camera, - transformations: mat4[] - ) { - [...objects].sort((a, b) => { + transformations: Matrix4[] + ): Object3D[] { + let sorted = [...objects]; + sorted.sort((a, b) => { let aToWorld = [a.matrix, ...transformations]; let bToWorld = [b.matrix, ...transformations]; - let aCentroid: vec3 = applyTransformations(a.centroid, aToWorld); - let bCentroid: vec3 = applyTransformations(b.centroid, bToWorld); + let aCentroid: Vector3 = applyTransformations(a.centroid, aToWorld); + let bCentroid: Vector3 = applyTransformations(b.centroid, bToWorld); // TODO actually use camera, currently only sorting by Z - return aCentroid[2] - bCentroid[2]; + return aCentroid.z - bCentroid.z; }); + return sorted; } } diff --git a/src/rendering/utils.ts b/src/rendering/utils.ts index 8cfa68d..c0500bb 100644 --- a/src/rendering/utils.ts +++ b/src/rendering/utils.ts @@ -1,7 +1,13 @@ -import { vec3, mat4 } from "gl-matrix"; +import { Matrix4 } from "../math/matrix"; +import { Vector3 } from "../math/vector"; -export function applyTransformations(vertex: vec3, transforms: mat4[]): vec3 { - return transforms.reduce((v, t) => { - return vec3.transformMat4(v, v, t); - }, vec3.clone(vertex)); +export function applyTransformations( + vertex: Vector3, + transforms: Matrix4[] +): Vector3 { + let v = vertex.clone(); + transforms.forEach((m, i) => { + v.transformMat4(m); + }); + return v; } diff --git a/src/svg/svg.ts b/src/svg/svg.ts index f9668f6..a887c9d 100644 --- a/src/svg/svg.ts +++ b/src/svg/svg.ts @@ -1,5 +1,5 @@ -import { vec3 } from "gl-matrix"; import { IColor } from "../geometry/color"; +import { Vector3 } from "../math/vector"; import { BLACK } from "../puzzles/colors"; export function createSVGElement( @@ -27,7 +27,7 @@ export function createSVGElement( } export function createPolygonElement( - points: vec3[], + points: Vector3[], color?: IColor, strokeWidth?: string ): SVGPolygonElement { @@ -40,8 +40,8 @@ export function createPolygonElement( } export function createArrowLineElement( - start: vec3, - end: vec3, + start: Vector3, + end: Vector3, color?: IColor, strokeWidth?: string ): SVGLineElement { @@ -49,10 +49,10 @@ export function createArrowLineElement( let strokeColor = color ? color.value : BLACK.value; - line.setAttributeNS(null, "x1", start[0].toString()); - line.setAttributeNS(null, "y1", (-start[1]).toString()); - line.setAttributeNS(null, "x2", end[0].toString()); - line.setAttributeNS(null, "y2", (-end[1]).toString()); + line.setAttributeNS(null, "x1", start.x.toString()); + line.setAttributeNS(null, "y1", (-start.y).toString()); + line.setAttributeNS(null, "x2", end.x.toString()); + line.setAttributeNS(null, "y2", (-end.y).toString()); line.setAttributeNS(null, "stroke", strokeColor); line.setAttributeNS(null, "marker-end", "url(#arrowhead)"); @@ -65,7 +65,7 @@ export function createArrowLineElement( export function updatePolygonElement( polygon: SVGPolygonElement, - points: vec3[], + points: Vector3[], color?: IColor, strokeWidth?: string ) { @@ -113,8 +113,8 @@ export function createMarkers(color: IColor): SVGDefsElement { return defs; } -function makePointsAttributeValue(points: vec3[]): string { +function makePointsAttributeValue(points: Vector3[]): string { return points.reduce((pointString, point) => { - return `${pointString ? pointString + " " : ""}${point[0]}, ${point[1]}`; + return `${pointString ? pointString + " " : ""}${point.x}, ${point.y}`; }, ""); } diff --git a/src/visualizer/puzzleCreator.ts b/src/visualizer/puzzleCreator.ts index 384136c..0029871 100644 --- a/src/visualizer/puzzleCreator.ts +++ b/src/visualizer/puzzleCreator.ts @@ -90,9 +90,9 @@ export function getPuzzleGeometry( /** * Returns a puzzle simulator for the type of puzzle - * - * @param type - * @param options + * + * @param type + * @param options */ export function getPuzzleSimulator( type: VisualizerType, @@ -119,37 +119,27 @@ export function getPuzzleSimulator( } } -export function createCube( - options: CubeOptions = {} -): RubiksCube { +export function createCube(options: CubeOptions = {}): RubiksCube { if (!geometryCache[VisualizerType.CUBE][options.size]) { geometryCache[VisualizerType.CUBE][options.size] = new RubiksCube( options.size ); } - return geometryCache[VisualizerType.CUBE][ - options.size - ] as RubiksCube; + return geometryCache[VisualizerType.CUBE][options.size] as RubiksCube; } -export function createCubeNet( - options: CubeOptions = {} -): RubiksCubeNet { +export function createCubeNet(options: CubeOptions = {}): RubiksCubeNet { if (!geometryCache[VisualizerType.CUBE_NET][options.size]) { geometryCache[VisualizerType.CUBE_NET][options.size] = new RubiksCubeNet( options.size ); } - return geometryCache[VisualizerType.CUBE_NET][ - options.size - ] as RubiksCubeNet; + return geometryCache[VisualizerType.CUBE_NET][options.size] as RubiksCubeNet; } -export function createCubeTop( - options: CubeOptions = {} -): RubiksCubeTopLayer { +export function createCubeTop(options: CubeOptions = {}): RubiksCubeTopLayer { if (!geometryCache[VisualizerType.CUBE_TOP][options.size]) { geometryCache[VisualizerType.CUBE_TOP][ options.size @@ -161,23 +151,17 @@ export function createCubeTop( ] as RubiksCubeTopLayer; } -export function createMegaminx( - options: MegaminxOptions = {} -): Megaminx { +export function createMegaminx(options: MegaminxOptions = {}): Megaminx { if (!geometryCache[VisualizerType.MEGAMINX][options.size]) { geometryCache[VisualizerType.MEGAMINX][options.size] = new Megaminx( options.size ); } - return geometryCache[VisualizerType.MEGAMINX][ - options.size - ] as Megaminx; + return geometryCache[VisualizerType.MEGAMINX][options.size] as Megaminx; } -export function createMegaminxNet( - options: MegaminxOptions = {} -): MegaminxNet { +export function createMegaminxNet(options: MegaminxOptions = {}): MegaminxNet { if (!geometryCache[VisualizerType.MEGAMINX_NET][options.size]) { geometryCache[VisualizerType.MEGAMINX_NET][options.size] = new MegaminxNet( options.size @@ -189,9 +173,7 @@ export function createMegaminxNet( ] as MegaminxNet; } -export function createMegaminxTop( - options: MegaminxOptions = {} -): MegaminxTop { +export function createMegaminxTop(options: MegaminxOptions = {}): MegaminxTop { if (!geometryCache[VisualizerType.MEGAMINX_TOP][2]) { // megaminx top size not supported, so just cache by size 2 geometryCache[VisualizerType.MEGAMINX_TOP][2] = new MegaminxTop(); @@ -200,23 +182,17 @@ export function createMegaminxTop( return geometryCache[VisualizerType.MEGAMINX_TOP][2] as MegaminxTop; } -export function createPyraminx( - options: PyraminxOptions = {} -): Pyraminx { +export function createPyraminx(options: PyraminxOptions = {}): Pyraminx { if (!geometryCache[VisualizerType.PYRAMINX][options.size]) { geometryCache[VisualizerType.PYRAMINX][options.size] = new Pyraminx( options.size ); } - return geometryCache[VisualizerType.PYRAMINX][ - options.size - ] as Pyraminx; + return geometryCache[VisualizerType.PYRAMINX][options.size] as Pyraminx; } -export function createPyraminxNet( - options: PyraminxOptions = {} -): PyraminxNet { +export function createPyraminxNet(options: PyraminxOptions = {}): PyraminxNet { if (!geometryCache[VisualizerType.PYRAMINX_NET][options.size]) { geometryCache[VisualizerType.PYRAMINX_NET][options.size] = new PyraminxNet( options.size @@ -228,9 +204,7 @@ export function createPyraminxNet( ] as PyraminxNet; } -export function createSkewb( - options: SkewbOptions = {} -): Skewb { +export function createSkewb(options: SkewbOptions = {}): Skewb { if (!geometryCache[VisualizerType.SKEWB][1]) { // Skewb size not supported, so just cache by size 1 geometryCache[VisualizerType.SKEWB][1] = new Skewb(); @@ -239,9 +213,7 @@ export function createSkewb( return geometryCache[VisualizerType.SKEWB][1] as Skewb; } -export function createSkewbNet( - options: SkewbOptions = {} -): SkewbNet { +export function createSkewbNet(options: SkewbOptions = {}): SkewbNet { if (!geometryCache[VisualizerType.SKEWB_NET][1]) { // Skewb size not supported, so just cache by size 1 geometryCache[VisualizerType.SKEWB_NET][1] = new SkewbNet(); @@ -250,9 +222,7 @@ export function createSkewbNet( return geometryCache[VisualizerType.SKEWB_NET][1] as SkewbNet; } -export function createSquare1( - options: Square1Options = {} -): Square1 { +export function createSquare1(options: Square1Options = {}): Square1 { const simulator = initSquare1Simulator(options); const geometry = new Square1( simulator.topLayer, @@ -263,9 +233,7 @@ export function createSquare1( return geometry; } -export function createSquare1Net( - options: Square1Options = {} -): Square1Net { +export function createSquare1Net(options: Square1Options = {}): Square1Net { const simulator = initSquare1Simulator(options); const geometry = new Square1Net( simulator.topLayer, diff --git a/src/visualizer/visualizer.ts b/src/visualizer/visualizer.ts index 8a6e45c..aa56485 100644 --- a/src/visualizer/visualizer.ts +++ b/src/visualizer/visualizer.ts @@ -2,7 +2,6 @@ import { Square1 } from "./../puzzles/square1/square1"; import { Geometry } from "./../geometry/geometry"; import { Arrow } from "./../geometry/arrow"; import { getDefaultOptions } from "./options"; -import { mat4, quat, vec3 } from "gl-matrix"; import { MASK_COLOR } from "./../puzzles/colors"; import { VisualizerType } from "./enum"; import { PuzzleGeometry } from "./../puzzles/puzzleGeometry"; @@ -20,6 +19,9 @@ import { IColor } from "../geometry/color"; import { applyTransformations } from "../rendering/utils"; import { Group } from "../geometry/group"; import { getPuzzleGeometry, getPuzzleSimulator } from "./puzzleCreator"; +import { Vector3 } from "../math/vector"; +import { Matrix4 } from "../math/matrix"; +import { Quaternion } from "../math/quaternion"; /** * Applies a color scheme to simulator values @@ -71,7 +73,11 @@ function canApplySimulatorColors(type: VisualizerType, size: number) { return true; } -function createArrow(a: ArrowDefinition, puzzle: PuzzleGeometry, group: Group): Arrow { +function createArrow( + a: ArrowDefinition, + puzzle: PuzzleGeometry, + group: Group +): Arrow { // Get the face the arrow is pointing to let startFace = puzzle.faces[a.start.face]; let endFace = puzzle.faces[a.end.face]; @@ -81,11 +87,15 @@ function createArrow(a: ArrowDefinition, puzzle: PuzzleGeometry, group: Group): } // Transform from sticker coordinates to group coordinates - let startTransformations = [startFace.matrix, puzzle.group.matrix, group.matrix]; + let startTransformations = [ + startFace.matrix, + puzzle.group.matrix, + group.matrix, + ]; let endTransformations = [endFace.matrix, puzzle.group.matrix, group.matrix]; - let start: vec3; - let end: vec3; + let start: Vector3; + let end: Vector3; // Get the stickers on the face if (startFace instanceof Geometry && endFace instanceof Geometry) { @@ -221,45 +231,31 @@ export class Visualizer { * image. */ private buildGroupMatrix() { - this.group.matrix = mat4.create(); + this.group.matrix = new Matrix4(); // Rotate the group matrix if (this.options.rotations) { this.options.rotations.forEach((rotation) => { const { x = 0, y = 0, z = 0 } = rotation; - mat4.mul( - this.group.matrix, - mat4.fromQuat(mat4.create(), quat.fromEuler(quat.create(), x, y, z)), - this.group.matrix + let rotationMatrix = Matrix4.fromQuaternion( + Quaternion.fromEuler(x, y, z) ); + Matrix4.multiply(this.group.matrix, rotationMatrix, this.group.matrix); }); - - // mat4.mul(this.group.matrix, this.puzzleGeometry.group.matrix, this.group.matrix); } // Scale the group matrix if (this.options.scale) { let scale = this.options.scale; - mat4.scale( - this.group.matrix, - this.group.matrix, - vec3.fromValues(scale, scale, scale) - ); + this.group.matrix.scale(scale, scale, scale); } // Translate the group matrix if (this.options.translation) { const { x = 0, y = 0, z = 0 } = this.options.translation; - let translationMatrix = mat4.fromTranslation( - mat4.create(), - vec3.fromValues(x, y, z) - ); + let translationMatrix = Matrix4.fromTranslation(x, y, z); - mat4.mul( - this.group.matrix, - translationMatrix, - this.group.matrix - ); + Matrix4.multiply(this.group.matrix, translationMatrix, this.group.matrix); } } @@ -272,7 +268,7 @@ export class Visualizer { try { this.scene.add(createArrow(arrow, this.puzzleGeometry, this.group)); } catch (e) { - console.error(e) + console.error(e); console.warn(`Invalid arrow ${JSON.stringify(arrow)}`); } });