Skip to content

Commit

Permalink
feat(modeling): rework orthonormal formula
Browse files Browse the repository at this point in the history
  • Loading branch information
z3dev authored Mar 25, 2023
1 parent d17a9d8 commit 008a462
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 213 deletions.
6 changes: 3 additions & 3 deletions packages/modeling/src/connectors/transformationBetween.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as plane from '../maths/plane/index.js'
import * as vec2 from '../maths/vec2/index.js'
import * as vec3 from '../maths/vec3/index.js'

import { OrthoNormalBasis } from '../maths/OrthoNormalBasis.js'
import { OrthonormalFormula } from '../maths/utils/index.js'

import { transform } from './transform.js'

Expand Down Expand Up @@ -31,7 +31,7 @@ export const transformationBetween = (options, from, to) => {

// align the axis
const axesPlane = plane.fromPointsRandom(plane.create(), vec3.create(), from.axis, to.axis)
const axesBasis = new OrthoNormalBasis(axesPlane)
const axesBasis = new OrthonormalFormula(axesPlane)

let angle1 = vec2.angleRadians(axesBasis.to2D(from.axis))
let angle2 = vec2.angleRadians(axesBasis.to2D(to.axis))
Expand All @@ -48,7 +48,7 @@ export const transformationBetween = (options, from, to) => {

// align the normals
const normalsPlane = plane.fromNormalAndPoint(plane.create(), to.axis, vec3.create())
const normalsBasis = new OrthoNormalBasis(normalsPlane)
const normalsBasis = new OrthonormalFormula(normalsPlane)

angle1 = vec2.angleRadians(normalsBasis.to2D(usAxesAligned.normal))
angle2 = vec2.angleRadians(normalsBasis.to2D(to.normal))
Expand Down
205 changes: 0 additions & 205 deletions packages/modeling/src/maths/OrthoNormalBasis.js

This file was deleted.

3 changes: 3 additions & 0 deletions packages/modeling/src/maths/utils/OrthonormalFormula.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Plane } from '../plane/type'

export class OrthonormalFormula(plane: Plane)
82 changes: 82 additions & 0 deletions packages/modeling/src/maths/utils/OrthonormalFormula.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as mat4 from '../mat4/index.js'

import * as vec2 from '../vec2/index.js'
import * as vec3 from '../vec3/index.js'

/*
* Class that defines the formula for convertion to/from orthonomal basis vectors.
* @see https://www.kristakingmath.com/blog/orthonormal-basis-for-a-vector-set
*/
export class OrthonormalFormula {
/**
* Construct the standard basis formula from the given plane.
* @param {plane} the plane of which to convert vertices to/from the orthonormal basis
*/
constructor (plane) {
// plane normal is one component
this.plane = plane
// orthogonal vector to plane normal is one component
const rightVector = vec3.orthogonal(vec3.create(), plane)
this.v = vec3.normalize(rightVector, vec3.cross(rightVector, plane, rightVector))
// cross between plane normal and orthogonal vector is one component
this.u = vec3.cross(vec3.create(), this.v, plane)

this.planeOrigin = vec3.scale(vec3.create(), plane, plane[3])
this.basisMap = new Map()
}

/**
* Convert the basis formula to a projection matrix.
* return {mat4} matrix which can be used to convert 3D vertices to 2D points
*/
getProjectionMatrix () {
return mat4.fromValues(
this.u[0], this.v[0], this.plane[0], 0,
this.u[1], this.v[1], this.plane[1], 0,
this.u[2], this.v[2], this.plane[2], 0,
0, 0, -this.plane[3], 1
)
}

/**
* Convert the basis formula to an inverse projection matrix.
* return {mat4} matrix which can be used to convert 2D points to 3D vertices
*/
getInverseProjectionMatrix () {
return mat4.fromValues(
this.u[0], this.u[1], this.u[2], 0,
this.v[0], this.v[1], this.v[2], 0,
this.plane[0], this.plane[1], this.plane[2], 0,
this.planeOrigin[0], this.planeOrigin[1], this.planeOrigin[2], 1
)
}

/**
* Convert the given 3D vertex to a 2D point which exists in the orthonormal basis
* @param {vec3} - 3D vertex which lies within the original basis (set)
* @return {vec2} - 2D point which lies within the orthonormal basis
*/
to2D (vertex) {
const point = vec2.fromValues(vec3.dot(vertex, this.u), vec3.dot(vertex, this.v))
this.basisMap.set(point, vertex)
return point
}

/**
* Convert the given 2D point to a 3D vertex which exists in the original basis (set)
* @param {vec2} - 2D point which lies within the orthonormal basis
* @return {vec3} - 3D vertex which lies within the original basis (set)
*/
to3D (point) {
// return the original vertex if possible, i.e. no floating point error
const original = this.basisMap.get(point)
if (original) return original

// calculate a new 3D vertex from the orthonormal basis formula
const v1 = vec3.scale(vec3.create(), this.u, point[0])
const v2 = vec3.scale(vec3.create(), this.v, point[1])
const v3 = vec3.add(v1, v1, this.planeOrigin)
const v4 = vec3.add(v2, v2, v3)
return v4
}
}
71 changes: 71 additions & 0 deletions packages/modeling/src/maths/utils/OrthonormalFormula.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import test from 'ava'

import { plane } from '../index.js'

import { OrthonormalFormula } from './index.js'

test('utils: OrthonormalFormula constructor', (t) => {
const p1 = plane.fromNormalAndPoint(plane.create(), [5, 0, 0], [0, 0, 0])
const p2 = plane.fromNormalAndPoint(plane.create(), [0, 0, 5], [5, 5, 5])

const o1 = new OrthonormalFormula(p1)
t.deepEqual(o1.u, [0, -1, 0])
t.deepEqual(o1.v, [0, 0, -1])

const o2 = new OrthonormalFormula(p2)
t.deepEqual(o2.u, [-1, 0, 0])
t.deepEqual(o2.v, [0, -1, 0])
})

test('utils: OrthonormalFormula methods', (t) => {
const p1 = plane.fromNormalAndPoint(plane.create(), [5, 0, 0], [5, 0, 0])
const o1 = new OrthonormalFormula(p1)

const v1 = [5, 0, 0]
const v2 = [0, 5, 0]
const v3 = [0, 0, 5]
const v4 = [-5, 0, 0]
const v5 = [0, -5, 0]
const v6 = [0, 0, -5]

const t1 = o1.to2D(v1)
t.deepEqual(t1, [0, 0])
const t2 = o1.to2D(v2)
t.deepEqual(t2, [-5, 0])
const t3 = o1.to2D(v3)
t.deepEqual(t3, [0, -5])
const t4 = o1.to2D(v4)
t.deepEqual(t4, [0, 0])
const t5 = o1.to2D(v5)
t.deepEqual(t5, [5, 0])
const t6 = o1.to2D(v6)
t.deepEqual(t3, [0, -5])

const r1 = o1.to3D(t1)
t.deepEqual(r1, v1)
const r2 = o1.to3D(t2)
t.deepEqual(r2, v2)
const r3 = o1.to3D(t3)
t.deepEqual(r3, v3)
const r4 = o1.to3D(t4)
t.deepEqual(r4, v4)
const r5 = o1.to3D(t5)
t.deepEqual(r5, v5)
const r6 = o1.to3D(t6)
t.deepEqual(r6, v6)

const m1 = o1.getProjectionMatrix()
t.deepEqual(m1, [
0, 0, 1, 0,
-1, 0, 0, 0,
0, -1, 0, 0,
0, 0, -5, 1
])
const m2 = o1.getInverseProjectionMatrix()
t.deepEqual(m2, [
0, -1, 0, 0,
0, 0, -1, 0,
1, 0, 0, 0,
5, 0, 0, 1
])
})
1 change: 1 addition & 0 deletions packages/modeling/src/maths/utils/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export { interpolateBetween2DPointsForY } from './interpolateBetween2DPointsForY
export { intersect } from './intersect'
export { solve2Linear } from './solve2Linear'
export { sin, cos } from './trigonometry'
export { OrthonormalFormula } from './OrthonormalFormula'

export as namespace utils
Loading

0 comments on commit 008a462

Please sign in to comment.