Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(modeling): rework orthonormal formula #1214

Merged
merged 8 commits into from
Mar 25, 2023
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