203 lines
7.0 KiB
JavaScript
203 lines
7.0 KiB
JavaScript
const Vector2D = require('./Vector2')
|
|
const Vector3D = require('./Vector3')
|
|
const Line2D = require('./Line2')
|
|
const Line3D = require('./Line3')
|
|
const Plane = require('./Plane')
|
|
|
|
// # class OrthoNormalBasis
|
|
// Reprojects points on a 3D plane onto a 2D plane
|
|
// or from a 2D plane back onto the 3D plane
|
|
const OrthoNormalBasis = function (plane, rightvector) {
|
|
if (arguments.length < 2) {
|
|
// choose an arbitrary right hand vector, making sure it is somewhat orthogonal to the plane normal:
|
|
rightvector = plane.normal.randomNonParallelVector()
|
|
} else {
|
|
rightvector = new Vector3D(rightvector)
|
|
}
|
|
this.v = plane.normal.cross(rightvector).unit()
|
|
this.u = this.v.cross(plane.normal)
|
|
this.plane = plane
|
|
this.planeorigin = plane.normal.times(plane.w)
|
|
}
|
|
|
|
// Get an orthonormal basis for the standard XYZ planes.
|
|
// Parameters: the names of two 3D axes. The 2d x axis will map to the first given 3D axis, the 2d y
|
|
// axis will map to the second.
|
|
// Prepend the axis with a "-" to invert the direction of this axis.
|
|
// For example: OrthoNormalBasis.GetCartesian("-Y","Z")
|
|
// will return an orthonormal basis where the 2d X axis maps to the 3D inverted Y axis, and
|
|
// the 2d Y axis maps to the 3D Z axis.
|
|
OrthoNormalBasis.GetCartesian = function (xaxisid, yaxisid) {
|
|
let axisid = xaxisid + '/' + yaxisid
|
|
let planenormal, rightvector
|
|
if (axisid === 'X/Y') {
|
|
planenormal = [0, 0, 1]
|
|
rightvector = [1, 0, 0]
|
|
} else if (axisid === 'Y/-X') {
|
|
planenormal = [0, 0, 1]
|
|
rightvector = [0, 1, 0]
|
|
} else if (axisid === '-X/-Y') {
|
|
planenormal = [0, 0, 1]
|
|
rightvector = [-1, 0, 0]
|
|
} else if (axisid === '-Y/X') {
|
|
planenormal = [0, 0, 1]
|
|
rightvector = [0, -1, 0]
|
|
} else if (axisid === '-X/Y') {
|
|
planenormal = [0, 0, -1]
|
|
rightvector = [-1, 0, 0]
|
|
} else if (axisid === '-Y/-X') {
|
|
planenormal = [0, 0, -1]
|
|
rightvector = [0, -1, 0]
|
|
} else if (axisid === 'X/-Y') {
|
|
planenormal = [0, 0, -1]
|
|
rightvector = [1, 0, 0]
|
|
} else if (axisid === 'Y/X') {
|
|
planenormal = [0, 0, -1]
|
|
rightvector = [0, 1, 0]
|
|
} else if (axisid === 'X/Z') {
|
|
planenormal = [0, -1, 0]
|
|
rightvector = [1, 0, 0]
|
|
} else if (axisid === 'Z/-X') {
|
|
planenormal = [0, -1, 0]
|
|
rightvector = [0, 0, 1]
|
|
} else if (axisid === '-X/-Z') {
|
|
planenormal = [0, -1, 0]
|
|
rightvector = [-1, 0, 0]
|
|
} else if (axisid === '-Z/X') {
|
|
planenormal = [0, -1, 0]
|
|
rightvector = [0, 0, -1]
|
|
} else if (axisid === '-X/Z') {
|
|
planenormal = [0, 1, 0]
|
|
rightvector = [-1, 0, 0]
|
|
} else if (axisid === '-Z/-X') {
|
|
planenormal = [0, 1, 0]
|
|
rightvector = [0, 0, -1]
|
|
} else if (axisid === 'X/-Z') {
|
|
planenormal = [0, 1, 0]
|
|
rightvector = [1, 0, 0]
|
|
} else if (axisid === 'Z/X') {
|
|
planenormal = [0, 1, 0]
|
|
rightvector = [0, 0, 1]
|
|
} else if (axisid === 'Y/Z') {
|
|
planenormal = [1, 0, 0]
|
|
rightvector = [0, 1, 0]
|
|
} else if (axisid === 'Z/-Y') {
|
|
planenormal = [1, 0, 0]
|
|
rightvector = [0, 0, 1]
|
|
} else if (axisid === '-Y/-Z') {
|
|
planenormal = [1, 0, 0]
|
|
rightvector = [0, -1, 0]
|
|
} else if (axisid === '-Z/Y') {
|
|
planenormal = [1, 0, 0]
|
|
rightvector = [0, 0, -1]
|
|
} else if (axisid === '-Y/Z') {
|
|
planenormal = [-1, 0, 0]
|
|
rightvector = [0, -1, 0]
|
|
} else if (axisid === '-Z/-Y') {
|
|
planenormal = [-1, 0, 0]
|
|
rightvector = [0, 0, -1]
|
|
} else if (axisid === 'Y/-Z') {
|
|
planenormal = [-1, 0, 0]
|
|
rightvector = [0, 1, 0]
|
|
} else if (axisid === 'Z/Y') {
|
|
planenormal = [-1, 0, 0]
|
|
rightvector = [0, 0, 1]
|
|
} else {
|
|
throw new Error('OrthoNormalBasis.GetCartesian: invalid combination of axis identifiers. Should pass two string arguments from [X,Y,Z,-X,-Y,-Z], being two different axes.')
|
|
}
|
|
return new OrthoNormalBasis(new Plane(new Vector3D(planenormal), 0), new Vector3D(rightvector))
|
|
}
|
|
|
|
/*
|
|
// test code for OrthoNormalBasis.GetCartesian()
|
|
OrthoNormalBasis.GetCartesian_Test=function() {
|
|
let axisnames=["X","Y","Z","-X","-Y","-Z"];
|
|
let axisvectors=[[1,0,0], [0,1,0], [0,0,1], [-1,0,0], [0,-1,0], [0,0,-1]];
|
|
for(let axis1=0; axis1 < 3; axis1++) {
|
|
for(let axis1inverted=0; axis1inverted < 2; axis1inverted++) {
|
|
let axis1name=axisnames[axis1+3*axis1inverted];
|
|
let axis1vector=axisvectors[axis1+3*axis1inverted];
|
|
for(let axis2=0; axis2 < 3; axis2++) {
|
|
if(axis2 != axis1) {
|
|
for(let axis2inverted=0; axis2inverted < 2; axis2inverted++) {
|
|
let axis2name=axisnames[axis2+3*axis2inverted];
|
|
let axis2vector=axisvectors[axis2+3*axis2inverted];
|
|
let orthobasis=OrthoNormalBasis.GetCartesian(axis1name, axis2name);
|
|
let test1=orthobasis.to3D(new Vector2D([1,0]));
|
|
let test2=orthobasis.to3D(new Vector2D([0,1]));
|
|
let expected1=new Vector3D(axis1vector);
|
|
let expected2=new Vector3D(axis2vector);
|
|
let d1=test1.distanceTo(expected1);
|
|
let d2=test2.distanceTo(expected2);
|
|
if( (d1 > 0.01) || (d2 > 0.01) ) {
|
|
throw new Error("Wrong!");
|
|
}}}}}}
|
|
throw new Error("OK");
|
|
};
|
|
*/
|
|
|
|
// The z=0 plane, with the 3D x and y vectors mapped to the 2D x and y vector
|
|
OrthoNormalBasis.Z0Plane = function () {
|
|
let plane = new Plane(new Vector3D([0, 0, 1]), 0)
|
|
return new OrthoNormalBasis(plane, new Vector3D([1, 0, 0]))
|
|
}
|
|
|
|
OrthoNormalBasis.prototype = {
|
|
getProjectionMatrix: function () {
|
|
const Matrix4x4 = require('./Matrix4') // FIXME: circular dependencies Matrix=>OrthoNormalBasis => Matrix
|
|
return new Matrix4x4([
|
|
this.u.x, this.v.x, this.plane.normal.x, 0,
|
|
this.u.y, this.v.y, this.plane.normal.y, 0,
|
|
this.u.z, this.v.z, this.plane.normal.z, 0,
|
|
0, 0, -this.plane.w, 1
|
|
])
|
|
},
|
|
|
|
getInverseProjectionMatrix: function () {
|
|
const Matrix4x4 = require('./Matrix4') // FIXME: circular dependencies Matrix=>OrthoNormalBasis => Matrix
|
|
let p = this.plane.normal.times(this.plane.w)
|
|
return new Matrix4x4([
|
|
this.u.x, this.u.y, this.u.z, 0,
|
|
this.v.x, this.v.y, this.v.z, 0,
|
|
this.plane.normal.x, this.plane.normal.y, this.plane.normal.z, 0,
|
|
p.x, p.y, p.z, 1
|
|
])
|
|
},
|
|
|
|
to2D: function (vec3) {
|
|
return new Vector2D(vec3.dot(this.u), vec3.dot(this.v))
|
|
},
|
|
|
|
to3D: function (vec2) {
|
|
return this.planeorigin.plus(this.u.times(vec2.x)).plus(this.v.times(vec2.y))
|
|
},
|
|
|
|
line3Dto2D: function (line3d) {
|
|
let a = line3d.point
|
|
let b = line3d.direction.plus(a)
|
|
let a2d = this.to2D(a)
|
|
let b2d = this.to2D(b)
|
|
return Line2D.fromPoints(a2d, b2d)
|
|
},
|
|
|
|
line2Dto3D: function (line2d) {
|
|
let a = line2d.origin()
|
|
let b = line2d.direction().plus(a)
|
|
let a3d = this.to3D(a)
|
|
let b3d = this.to3D(b)
|
|
return Line3D.fromPoints(a3d, b3d)
|
|
},
|
|
|
|
transform: function (matrix4x4) {
|
|
// todo: this may not work properly in case of mirroring
|
|
let newplane = this.plane.transform(matrix4x4)
|
|
let rightpointTransformed = this.u.transform(matrix4x4)
|
|
let originTransformed = new Vector3D(0, 0, 0).transform(matrix4x4)
|
|
let newrighthandvector = rightpointTransformed.minus(originTransformed)
|
|
let newbasis = new OrthoNormalBasis(newplane, newrighthandvector)
|
|
return newbasis
|
|
}
|
|
}
|
|
|
|
module.exports = OrthoNormalBasis
|