78 lines
3.8 KiB
JavaScript
78 lines
3.8 KiB
JavaScript
export function degreeToRadian(degree) {
|
|
return degree * Math.PI / 180;
|
|
}
|
|
|
|
// FOV in radians is spherical, so doesn't follow Pythagoras' theorem
|
|
export function diagonalToCrossFOVs(diagonalFOVRadians, aspectRatio) {
|
|
// first convert from a spherical FOV to a diagonal FOV on a flat plane at a unit distance of 1.0
|
|
const diagonalLengthUnitDistance = 2 * Math.tan(diagonalFOVRadians / 2);
|
|
|
|
// then convert to flat plane horizontal and vertical FOVs
|
|
const heightUnitDistance = diagonalLengthUnitDistance / Math.sqrt(1 + aspectRatio * aspectRatio);
|
|
const widthUnitDistance = heightUnitDistance * aspectRatio;
|
|
|
|
return {
|
|
// then convert back to spherical FOV
|
|
diagonalRadians: diagonalFOVRadians,
|
|
horizontalRadians: 2 * Math.atan(widthUnitDistance / 2),
|
|
verticalRadians: 2 * Math.atan(heightUnitDistance / 2),
|
|
|
|
// flat values are relative to a unit distance of 1.0
|
|
diagonalLengthUnitDistance,
|
|
widthUnitDistance,
|
|
heightUnitDistance
|
|
}
|
|
}
|
|
|
|
const segmentsPerRadian = 20.0 / degreeToRadian(90.0);
|
|
|
|
// displays are placed around a circle, these functions help determine radians and distances from the original
|
|
// FOV measurements scaled to the display dimensions
|
|
export const fovConversionFns = {
|
|
// convert curved FOV for flat displays
|
|
flat: {
|
|
// distance to an edge is the hypothenuse of the triangle where the opposite side is half the width of the reference fov screen
|
|
centerToFovEdgeDistance: (centerDistance, fovLength) => Math.sqrt(Math.pow(fovLength / 2, 2) + Math.pow(centerDistance, 2)),
|
|
fovEdgeToScreenCenterDistance: (edgeDistance, screenLength) => Math.sqrt(Math.pow(edgeDistance, 2) - Math.pow(screenLength / 2, 2)),
|
|
lengthToRadians: (fovRadians, fovLength, screenEdgeDistance, toLength) => Math.asin(toLength / 2 / screenEdgeDistance) * 2,
|
|
angleToLength: (fovRadians, fovLength, screenDistance, toAngleOpposite, toAngleAdjacent) => {
|
|
return toAngleOpposite / toAngleAdjacent * screenDistance;
|
|
},
|
|
fovRadiansAtDistance: (fovRadians, unitLength, newScreenDistance) => {
|
|
return 2 * Math.atan(unitLength / 2 / newScreenDistance);
|
|
},
|
|
radiansToSegments: (screenRadians) => 1
|
|
},
|
|
|
|
// convert curved FOV for curved displays, scaling either involves no change or is linear
|
|
curved: {
|
|
centerToFovEdgeDistance: (centerDistance, fovLength) => centerDistance,
|
|
fovEdgeToScreenCenterDistance: (edgeDistance, screenLength) => edgeDistance,
|
|
lengthToRadians: (fovRadians, fovLength, screenEdgeDistance, toLength) => fovRadians / fovLength * toLength,
|
|
angleToLength: (fovRadians, fovLength, screenDistance, toAngleOpposite, toAngleAdjacent) => fovLength / fovRadians * Math.atan2(toAngleOpposite, toAngleAdjacent),
|
|
fovRadiansAtDistance: (fovRadians, unitLength, newScreenDistance) => fovRadians / newScreenDistance,
|
|
radiansToSegments: (screenRadians) => Math.ceil(screenRadians * segmentsPerRadian)
|
|
}
|
|
}
|
|
|
|
export const applyQuaternionToVector = (vector, quaternion) => {
|
|
const t = [
|
|
2.0 * (quaternion[1] * vector[2] - quaternion[2] * vector[1]),
|
|
2.0 * (quaternion[2] * vector[0] - quaternion[0] * vector[2]),
|
|
2.0 * (quaternion[0] * vector[1] - quaternion[1] * vector[0])
|
|
];
|
|
return [
|
|
vector[0] + quaternion[3] * t[0] + quaternion[1] * t[2] - quaternion[2] * t[1],
|
|
vector[1] + quaternion[3] * t[1] + quaternion[2] * t[0] - quaternion[0] * t[2],
|
|
vector[2] + quaternion[3] * t[2] + quaternion[0] * t[1] - quaternion[1] * t[0]
|
|
];
|
|
}
|
|
|
|
export const vectorMagnitude = (vector) => {
|
|
return Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
|
|
}
|
|
|
|
export const normalizeVector = (vector) => {
|
|
const length = vectorMagnitude(vector);
|
|
return [vector[0] / length, vector[1] / length, vector[2] / length];
|
|
} |