From 99b74961e9c55693e2241da4a5364aeb50d6a5dd Mon Sep 17 00:00:00 2001 From: Misode Date: Tue, 14 Feb 2023 18:24:30 +0100 Subject: [PATCH] Use deepslate matrix abstraction --- package-lock.json | 14 +-- package.json | 2 +- src/app/Utils.ts | 157 ++++++++++++++----------------- src/app/pages/Transformation.tsx | 72 +++++++------- 4 files changed, 116 insertions(+), 129 deletions(-) diff --git a/package-lock.json b/package-lock.json index 04920f36..79ed4322 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "brace": "^0.11.1", "buffer": "^6.0.3", "comment-json": "^4.1.1", - "deepslate": "^0.17.1", + "deepslate": "^0.17.2", "deepslate-1.18": "npm:deepslate@^0.9.0-beta.9", "deepslate-1.18.2": "npm:deepslate@^0.9.0-beta.13", "highlight.js": "^11.5.1", @@ -1985,9 +1985,9 @@ "dev": true }, "node_modules/deepslate": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.17.1.tgz", - "integrity": "sha512-/NL5JIZTSrbWRxbkSKDZPxDHmB49VuFj0ybj83Ae47JyTN1cfmIMPH7liK3lYfhkjLnxqElpif/2sf3wEjXGig==", + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.17.2.tgz", + "integrity": "sha512-kQDX2mm0ieKN6HLI+fu+0BBiDqZdTjPsv0uUbpePoDIQd9JxMbvugEpZluQa8ellyEyK9vJOCTT1Wnz+RV8fBQ==", "dependencies": { "gl-matrix": "^3.3.0", "md5": "^2.3.0", @@ -6752,9 +6752,9 @@ "dev": true }, "deepslate": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.17.1.tgz", - "integrity": "sha512-/NL5JIZTSrbWRxbkSKDZPxDHmB49VuFj0ybj83Ae47JyTN1cfmIMPH7liK3lYfhkjLnxqElpif/2sf3wEjXGig==", + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.17.2.tgz", + "integrity": "sha512-kQDX2mm0ieKN6HLI+fu+0BBiDqZdTjPsv0uUbpePoDIQd9JxMbvugEpZluQa8ellyEyK9vJOCTT1Wnz+RV8fBQ==", "requires": { "gl-matrix": "^3.3.0", "md5": "^2.3.0", diff --git a/package.json b/package.json index 3fdbe7cf..2021a4f1 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "brace": "^0.11.1", "buffer": "^6.0.3", "comment-json": "^4.1.1", - "deepslate": "^0.17.1", + "deepslate": "^0.17.2", "deepslate-1.18": "npm:deepslate@^0.9.0-beta.9", "deepslate-1.18.2": "npm:deepslate@^0.9.0-beta.13", "highlight.js": "^11.5.1", diff --git a/src/app/Utils.ts b/src/app/Utils.ts index b83dcf71..57a34b96 100644 --- a/src/app/Utils.ts +++ b/src/app/Utils.ts @@ -1,8 +1,10 @@ import type { DataModel } from '@mcschema/core' import { Path } from '@mcschema/core' import * as zip from '@zip.js/zip.js' -import type { Random } from 'deepslate/core' -import { mat3, mat4, quat, vec2, vec3 } from 'gl-matrix' +import type { Random } from 'deepslate' +import { Matrix3, Matrix4, Vector } from 'deepslate' +import type { mat3 } from 'gl-matrix' +import { quat, vec2 } from 'gl-matrix' import yaml from 'js-yaml' import { route } from 'preact-router' import rfdc from 'rfdc' @@ -406,28 +408,27 @@ function qrGivensQuat(a: number, b: number) { } // modifies the passed mat3 -function stepJacobi(m: mat3): quat { - const n = mat3.create() +function stepJacobi(m: Matrix3): quat { + const n = new Matrix3() const q = quat.create() - if (m[1] * m[1] + m[3] * m[3] > 1e-6) { - const [a, b] = approxGivensQuat(m[0], 0.5 * (m[1] + m[3]), m[4]) + if (m.m01 * m.m01 + m.m10 * m.m10 > 1e-6) { + const [a, b] = approxGivensQuat(m.m00, 0.5 * (m.m01 + m.m10), m.m11) const r = quat.fromValues(0, 0, a, b) const c = b * b - a * a const d = -2 * a * b const e = b * b + a * a quat.mul(q, q, r) - n[0] = c - n[4] = c - n[1] = -d - n[3] = d - n[8] = e - mat3.mul(m, m, n) - mat3.transpose(n, n) - mat3.mul(n, n, m) - mat3.copy(m, n) + n.m00 = c + n.m11 = c + n.m01 = -d + n.m10 = d + n.m22 = e + m.mul(n) + n.transpose().mul(m) + m.copy(n) } - if (m[2] * m[2] + m[6] * m[6] > 1e-6) { - const pair = approxGivensQuat(m[0], 0.5 * (m[2] + m[6]), m[8]) + if (m.m02 * m.m02 + m.m20 * m.m20 > 1e-6) { + const pair = approxGivensQuat(m.m00, 0.5 * (m.m02 + m.m20), m.m22) const a = -pair[0] const b = pair[1] const r = quat.fromValues(0, a, 0, b) @@ -435,68 +436,65 @@ function stepJacobi(m: mat3): quat { const d = -2 * a * b const e = b * b + a * a quat.mul(q, q, r) - n[0] = c - n[8] = c - n[2] = d - n[6] = -d - n[4] = e - mat3.mul(m, m, n) - mat3.transpose(n, n) - mat3.mul(n, n, m) - mat3.copy(m, n) + n.m00 = c + n.m22 = c + n.m02 = d + n.m20 = -d + n.m11 = e + m.mul(n) + n.transpose().mul(m) + m.copy(n) } - if (m[5] * m[5] + m[7] * m[7] > 1e-6) { - const [a, b] = approxGivensQuat(m[4], 0.5 * (m[5] + m[7]), m[8]) + if (m.m12 * m.m12 + m.m21 * m.m21 > 1e-6) { + const [a, b] = approxGivensQuat(m.m11, 0.5 * (m.m12 + m.m21), m.m22) const r = quat.fromValues(a, 0, 0, b) const c = b * b - a * a const d = -2 * a * b const e = b * b + a * a quat.mul(q, q, r) - n[4] = c - n[8] = c - n[5] = -d - n[7] = d - n[0] = e - mat3.mul(m, m, n) - mat3.transpose(n, n) - mat3.mul(n, n, m) - mat3.copy(m, n) + n.m11 = c + n.m22 = c + n.m12 = -d + n.m21 = d + n.m00 = e + m.mul(n) + n.transpose().mul(m) + m.copy(n) } return q } -export function svdDecompose(m: mat3): [quat, vec3, quat] { +export function svdDecompose(m: Matrix3): [quat, Vector, quat] { const q = quat.create() const r = quat.create() - const n = mat3.create() - mat3.transpose(n, m) - mat3.mul(n, n, m) + const n = m.clone() + .transpose() + .mul(m) for (let i = 0; i < 5; i += 1) { quat.mul(r, r, stepJacobi(n)) } quat.normalize(r, r) - const p0 = mat3.create() - mat3.fromQuat(p0, r) - mat3.mul(p0, m, p0) + const p0 = m.clone() + .mul(Matrix3.fromQuat(r)) let f = 1 - const [a1, b1] = qrGivensQuat(p0[0], p0[1]) + const [a1, b1] = qrGivensQuat(p0.m00, p0.m01) const c1 = b1 * b1 - a1 * a1 const d1 = -2 * a1 * b1 const e1 = b1 * b1 + a1 * a1 const s1 = quat.fromValues(0, 0, a1, b1) quat.mul(q, q, s1) - const p1 = mat3.create() - p1[0] = c1 - p1[4] = c1 - p1[1] = d1 - p1[3] = -d1 - p1[8] = e1 + const p1 = new Matrix3() + p1.m00 = c1 + p1.m11 = c1 + p1.m01 = d1 + p1.m10 = -d1 + p1.m22 = e1 f *= e1 - mat3.mul(p1, p1, p0) + p1.mul(p0) - const pair = qrGivensQuat(p1[0], p1[2]) + const pair = qrGivensQuat(p1.m00, p1.m02) const a2 = -pair[0] const b2 = pair[1] const c2 = b2 * b2 - a2 * a2 @@ -504,49 +502,40 @@ export function svdDecompose(m: mat3): [quat, vec3, quat] { const e2 = b2 * b2 + a2 * a2 const s2 = quat.fromValues(0, a2, 0, b2) quat.mul(q, q, s2) - const p2 = mat3.create() - p2[0] = c2 - p2[8] = c2 - p2[2] = -d2 - p2[6] = d2 - p2[4] = e2 + const p2 = new Matrix3() + p2.m00 = c2 + p2.m22 = c2 + p2.m02 = -d2 + p2.m20 = d2 + p2.m11 = e2 f *= e2 - mat3.mul(p2, p2, p1) + p2.mul(p1) - const [a3, b3] = qrGivensQuat(p2[4], p2[5]) + const [a3, b3] = qrGivensQuat(p2.m11, p2.m12) const c3 = b3 * b3 - a3 * a3 const d3 = -2 * a3 * b3 const e3 = b3 * b3 + a3 * a3 const s3 = quat.fromValues(a3, 0, 0, b3) quat.mul(q, q, s3) - const p3 = mat3.create() - p3[4] = c3 - p3[8] = c3 - p3[5] = d3 - p3[7] = -d3 - p3[0] = e3 + const p3 = new Matrix3() + p3.m11 = c3 + p3.m22 = c3 + p3.m12 = d3 + p3.m21 = -d3 + p3.m00 = e3 f *= e3 - mat3.mul(p3, p3, p2) + p3.mul(p2) f = 1 / f quat.scale(q, q, Math.sqrt(f)) - const scale = vec3.fromValues(p3[0] * f, p3[4] * f, p3[8] * f) + const scale = new Vector(p3.m00 * f, p3.m11 * f, p3.m22 * f) return [q, scale, r] } -export function toAffine(m: mat4): mat4 { - if (m[15] === 0) m[15] = 1 - const a = 1 / m[15] - const n = mat4.clone(m) - mat4.scale(n, n, [a, a, a]) - return n -} - -export function composeMatrix(translation: vec3, leftRotation: quat, scale: vec3, rightRotation: quat) { - const m = mat4.create() - mat4.translate(m, m, translation) - mat4.mul(m, m, mat4.fromQuat(mat4.create(), leftRotation)) - mat4.scale(m, m, scale) - mat4.mul(m, m, mat4.fromQuat(mat4.create(), rightRotation)) - return m +export function composeMatrix(translation: Vector, leftRotation: quat, scale: Vector, rightRotation: quat) { + return new Matrix4() + .translate(translation) + .mul(Matrix4.fromQuat(leftRotation)) + .scale(scale) + .mul(Matrix4.fromQuat(rightRotation)) } diff --git a/src/app/pages/Transformation.tsx b/src/app/pages/Transformation.tsx index 77571d2c..2fa70f15 100644 --- a/src/app/pages/Transformation.tsx +++ b/src/app/pages/Transformation.tsx @@ -1,12 +1,15 @@ -import { Mesh, Quad, Renderer, ShaderProgram, Vector, Vertex } from 'deepslate' -import { mat3, mat4, quat, vec3 } from 'gl-matrix' +import { Matrix3, Matrix4, Mesh, Quad, Renderer, ShaderProgram, Vector, Vertex } from 'deepslate' +import { mat4, quat } from 'gl-matrix' import { useCallback, useMemo, useRef, useState } from 'preact/hooks' import { Footer, NumberInput, Octicon, RangeInput } from '../components/index.js' import { InteractiveCanvas3D } from '../components/previews/InteractiveCanvas3D.jsx' import { useLocale, useTitle } from '../contexts/index.js' import { useAsync } from '../hooks/useAsync.js' import { loadImage } from '../services/DataFetcher.js' -import { composeMatrix, svdDecompose, toAffine } from '../Utils.js' +import { composeMatrix, svdDecompose } from '../Utils.js' + +const XYZ = ['x', 'y', 'z'] as const +type XYZ = typeof XYZ[number] interface Props { path?: string, @@ -26,11 +29,11 @@ export function Transformation({}: Props) { return data }) - const [matrix, setMatrix] = useState(mat4.create()) - const [translation, setTranslation] = useState(vec3.create()) - const [leftRotation, setLeftRotation] = useState(quat.create()) - const [scale, setScale] = useState(vec3.fromValues(1, 1, 1)) - const [rightRotation, setRightRotation] = useState(quat.create()) + const [matrix, setMatrix] = useState(new Matrix4()) + const [translation, setTranslation] = useState(new Vector(0, 0, 0)) + const [leftRotation, setLeftRotation] = useState(quat.create()) + const [scale, setScale] = useState(new Vector(1, 1, 1)) + const [rightRotation, setRightRotation] = useState(quat.create()) const [normalizeLeft, setNormalizeLeft] = useState(true) const [normalizeRight, setNormalizeRight] = useState(true) @@ -44,10 +47,10 @@ export function Transformation({}: Props) { return composeMatrix(translation, leftRotation, scale, rightRotation) }, [matrix, useMatrixOverride]) - const updateMatrix = useCallback((m: mat4) => { - const affine = toAffine(m) - const newTranslation = mat4.getTranslation(vec3.create(), affine) - const [newLeftRotation, newScale, newRightRotation] = svdDecompose(mat3.fromMat4(mat3.create(), affine)) + const updateMatrix = useCallback((m: Matrix4) => { + const affine = m.clone().affine() + const newTranslation = affine.getTranslation() + const [newLeftRotation, newScale, newRightRotation] = svdDecompose(Matrix3.fromMatrix4(affine)) setTranslation(newTranslation) setLeftRotation(newLeftRotation) setScale(newScale) @@ -56,20 +59,18 @@ export function Transformation({}: Props) { }, []) const changeMatrix = useCallback((i: number, value: number) => { - const m = mat4.clone(matrix) - m[i] = value + const m = matrix.clone() + m.data[i] = value updateMatrix(m) }, [matrix]) - const updateTranslation = useCallback((value: vec3) => { + const updateTranslation = useCallback((value: Vector) => { setTranslation(value) setMatrix(composeMatrix(value, leftRotation, scale, rightRotation)) }, [leftRotation, scale, rightRotation]) - const changeTranslation = useCallback((i: number, value: number) => { - const copy = vec3.clone(translation) - copy[i] = value - updateTranslation(copy) + const changeTranslation = useCallback((c: XYZ, v: number) => { + updateTranslation(new Vector(c === 'x' ? v : translation.x, c === 'y' ? v : translation.y, c === 'z' ? v : translation.z)) }, [translation, updateTranslation]) const updateLeftRotation = useCallback((value: quat) => { @@ -84,16 +85,13 @@ export function Transformation({}: Props) { updateLeftRotation(copy) }, [leftRotation, normalizeLeft, updateLeftRotation]) - const updateScale = useCallback((value: vec3) => { + const updateScale = useCallback((value: Vector) => { setScale(value) setMatrix(composeMatrix(translation, leftRotation, value, rightRotation)) }, [translation, leftRotation, rightRotation]) - const changeScale = useCallback((i: number, value: number) => { - const copy = vec3.clone(scale) - copy[i] = value - setScale(copy) - setMatrix(composeMatrix(translation, leftRotation, scale, rightRotation)) + const changeScale = useCallback((c: XYZ, v: number) => { + updateScale(new Vector(c === 'x' ? v : scale.x, c === 'y' ? v : scale.y, c === 'z' ? v : scale.z)) }, [scale, updateScale]) const updateRightRotation = useCallback((value: quat) => { @@ -120,7 +118,7 @@ export function Transformation({}: Props) { renderer.current?.setViewport(0, 0, width, height) }, [cubeTexture]) const onDraw = useCallback((view: mat4) => { - renderer.current?.draw(view, usedMatrix) + renderer.current?.draw(view, usedMatrix.data) }, [usedMatrix]) return
@@ -129,11 +127,11 @@ export function Transformation({}: Props) {
{locale('transformation.translation')} - +
- {Array(3).fill(0).map((_, i) =>
- changeTranslation(i, v)} /> - changeTranslation(i, v)} /> + {XYZ.map((c) =>
+ changeTranslation(c, v)} /> + changeTranslation(c, v)} />
)}
@@ -150,11 +148,11 @@ export function Transformation({}: Props) {
{locale('transformation.scale')} - +
- {Array(3).fill(0).map((_, i) =>
- changeScale(i, v)} /> - changeScale(i, v)} /> + {XYZ.map((c) =>
+ changeScale(c, v)} /> + changeScale(c, v)} />
)}
@@ -173,12 +171,12 @@ export function Transformation({}: Props) {
{locale('transformation.matrix')} - +
{Array(16).fill(0).map((_, i) =>
- changeMatrix(i, v)} /> - changeMatrix(i, v)} /> + changeMatrix(i, v)} /> + changeMatrix(i, v)} />
)}