-
Notifications
You must be signed in to change notification settings - Fork 513
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(modeling): rework orthonormal formula
- Loading branch information
Showing
8 changed files
with
165 additions
and
213 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { Plane } from '../plane/type' | ||
|
||
export class OrthonormalFormula(plane: Plane) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
71
packages/modeling/src/maths/utils/OrthonormalFormula.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
]) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.