breezy-desktop/gnome/src/math.js

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];
}