43 lines
1.6 KiB
TypeScript
43 lines
1.6 KiB
TypeScript
import type { SurfaceBounds } from "./B04_wf1_Surface_Api_Fetch";
|
|
|
|
export const SURFACE_CAMERA_FOV = 50;
|
|
|
|
export interface SurfaceCameraState {
|
|
direction: [number, number, number];
|
|
distanceMeters: number;
|
|
targetMeters: [number, number, number];
|
|
}
|
|
|
|
export function getReferenceCenter(bounds: SurfaceBounds): [number, number, number] {
|
|
return [
|
|
(bounds.x_min + bounds.x_max) / 2,
|
|
(bounds.y_min + bounds.y_max) / 2,
|
|
(bounds.z_min + bounds.z_max) / 2,
|
|
];
|
|
}
|
|
|
|
export function getTopFitDistance(bounds: SurfaceBounds, aspect: number): number {
|
|
const width = Math.max(bounds.x_max - bounds.x_min, 1);
|
|
const depth = Math.max(bounds.y_max - bounds.y_min, 1);
|
|
const verticalFov = (SURFACE_CAMERA_FOV * Math.PI) / 180;
|
|
const horizontalFov = 2 * Math.atan(Math.tan(verticalFov / 2) * Math.max(aspect, 0.1));
|
|
const verticalDistance = depth / (2 * Math.tan(verticalFov / 2));
|
|
const horizontalDistance = width / (2 * Math.tan(horizontalFov / 2));
|
|
return Math.max(verticalDistance, horizontalDistance, 1) * 1.12;
|
|
}
|
|
|
|
export function targetPlaneMetersPerPixel(distanceMeters: number, viewportHeight: number): number {
|
|
const verticalFov = (SURFACE_CAMERA_FOV * Math.PI) / 180;
|
|
return (
|
|
(2 * Math.tan(verticalFov / 2) * Math.max(distanceMeters, 0.001)) / Math.max(viewportHeight, 1)
|
|
);
|
|
}
|
|
|
|
export function niceScaleDistance(roughMeters: number): number {
|
|
const exponent = Math.floor(Math.log10(Math.max(roughMeters, 0.001)));
|
|
const base = 10 ** exponent;
|
|
const normalized = roughMeters / base;
|
|
const step = normalized <= 1 ? 1 : normalized <= 2 ? 2 : normalized <= 5 ? 5 : 10;
|
|
return step * base;
|
|
}
|