@genart-dev/projection 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/vec3.ts","../src/mat4.ts","../src/camera.ts","../src/project.ts","../src/spatial.ts","../src/sorting.ts","../src/presets.ts","../src/interpolation.ts"],"sourcesContent":["// Types\nexport type {\n Vec2,\n Vec3,\n Mat4,\n Camera,\n Viewport,\n ProjectedPoint,\n BoundingBox3D,\n} from \"./types.js\";\n\n// Vec3 operations\nexport {\n vec3,\n add,\n sub,\n scale as scaleVec3,\n dot,\n cross,\n length,\n lengthSq,\n normalize,\n negate,\n lerp as lerpVec3,\n distance,\n distanceSq,\n} from \"./vec3.js\";\n\n// Mat4 operations\nexport {\n identity,\n lookAt,\n perspectiveMatrix,\n orthographicMatrix,\n multiply,\n invert,\n transformPoint,\n transformDirection,\n transformPointRaw,\n} from \"./mat4.js\";\n\n// Camera\nexport { createCamera, updateCamera, viewProjectionMatrix } from \"./camera.js\";\n\n// Projection\nexport {\n project,\n projectWithMatrix,\n projectMany,\n unproject,\n} from \"./project.js\";\n\n// Spatial queries\nexport {\n frustumContainsPoint,\n frustumContainsBox,\n isBackFace,\n scaleAtDepth,\n normalizedDepth,\n distanceTo,\n horizonScreenY,\n deriveVanishingPoints,\n} from \"./spatial.js\";\n\n// Depth sorting\nexport { depthSort, depthSortIndices } from \"./sorting.js\";\n\n// Camera presets\nexport {\n landscapeCamera,\n aerialCamera,\n streetCamera,\n wormEyeCamera,\n isometricCamera,\n} from \"./presets.js\";\n\n// Camera interpolation\nexport {\n lerpCamera,\n orbitCamera,\n dollyCamera,\n panCamera,\n} from \"./interpolation.js\";\n","import type { Vec3 } from \"./types.js\";\n\n/** Create a Vec3. */\nexport function vec3(x: number, y: number, z: number): Vec3 {\n return { x, y, z };\n}\n\n/** Add two vectors. */\nexport function add(a: Vec3, b: Vec3): Vec3 {\n return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z };\n}\n\n/** Subtract b from a. */\nexport function sub(a: Vec3, b: Vec3): Vec3 {\n return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };\n}\n\n/** Scale a vector by a scalar. */\nexport function scale(v: Vec3, s: number): Vec3 {\n return { x: v.x * s, y: v.y * s, z: v.z * s };\n}\n\n/** Dot product. */\nexport function dot(a: Vec3, b: Vec3): number {\n return a.x * b.x + a.y * b.y + a.z * b.z;\n}\n\n/** Cross product. */\nexport function cross(a: Vec3, b: Vec3): Vec3 {\n return {\n x: a.y * b.z - a.z * b.y,\n y: a.z * b.x - a.x * b.z,\n z: a.x * b.y - a.y * b.x,\n };\n}\n\n/** Vector length. */\nexport function length(v: Vec3): number {\n return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);\n}\n\n/** Squared length (avoids sqrt). */\nexport function lengthSq(v: Vec3): number {\n return v.x * v.x + v.y * v.y + v.z * v.z;\n}\n\n/** Normalize to unit length. Returns zero vector if input is zero length. */\nexport function normalize(v: Vec3): Vec3 {\n const len = length(v);\n if (len === 0) return { x: 0, y: 0, z: 0 };\n return { x: v.x / len, y: v.y / len, z: v.z / len };\n}\n\n/** Negate a vector. */\nexport function negate(v: Vec3): Vec3 {\n return { x: -v.x, y: -v.y, z: -v.z };\n}\n\n/** Linear interpolation between two vectors. */\nexport function lerp(a: Vec3, b: Vec3, t: number): Vec3 {\n return {\n x: a.x + (b.x - a.x) * t,\n y: a.y + (b.y - a.y) * t,\n z: a.z + (b.z - a.z) * t,\n };\n}\n\n/** Distance between two points. */\nexport function distance(a: Vec3, b: Vec3): number {\n return length(sub(a, b));\n}\n\n/** Squared distance (avoids sqrt). */\nexport function distanceSq(a: Vec3, b: Vec3): number {\n return lengthSq(sub(a, b));\n}\n","import type { Mat4, Vec3 } from \"./types.js\";\nimport { cross, dot, normalize, sub } from \"./vec3.js\";\n\n/** Create an identity matrix. */\nexport function identity(): Mat4 {\n const m = new Float64Array(16);\n m[0] = 1;\n m[5] = 1;\n m[10] = 1;\n m[15] = 1;\n return m;\n}\n\n/**\n * Build a view matrix (world-to-camera transform).\n * Uses right-handed convention: camera looks along -Z in view space.\n */\nexport function lookAt(eye: Vec3, target: Vec3, up: Vec3): Mat4 {\n const f = normalize(sub(target, eye)); // forward (toward target)\n const s = normalize(cross(up, f)); // right (+X when looking along +Z)\n const u = cross(f, s); // recalculated up\n\n const m = new Float64Array(16);\n m[0] = s.x;\n m[1] = u.x;\n m[2] = -f.x;\n m[3] = 0;\n m[4] = s.y;\n m[5] = u.y;\n m[6] = -f.y;\n m[7] = 0;\n m[8] = s.z;\n m[9] = u.z;\n m[10] = -f.z;\n m[11] = 0;\n m[12] = -dot(s, eye);\n m[13] = -dot(u, eye);\n m[14] = dot(f, eye);\n m[15] = 1;\n return m;\n}\n\n/** Build a perspective projection matrix. */\nexport function perspectiveMatrix(\n fovDegrees: number,\n aspect: number,\n near: number,\n far: number,\n): Mat4 {\n const fovRad = (fovDegrees * Math.PI) / 180;\n const f = 1 / Math.tan(fovRad / 2);\n const rangeInv = 1 / (near - far);\n\n const m = new Float64Array(16);\n m[0] = f / aspect;\n m[5] = f;\n m[10] = (far + near) * rangeInv;\n m[11] = -1;\n m[14] = 2 * far * near * rangeInv;\n return m;\n}\n\n/** Build an orthographic projection matrix. */\nexport function orthographicMatrix(\n left: number,\n right: number,\n bottom: number,\n top: number,\n near: number,\n far: number,\n): Mat4 {\n const m = new Float64Array(16);\n m[0] = 2 / (right - left);\n m[5] = 2 / (top - bottom);\n m[10] = -2 / (far - near);\n m[12] = -(right + left) / (right - left);\n m[13] = -(top + bottom) / (top - bottom);\n m[14] = -(far + near) / (far - near);\n m[15] = 1;\n return m;\n}\n\n/** Multiply two 4x4 matrices: result = a * b. */\nexport function multiply(a: Mat4, b: Mat4): Mat4 {\n const r = new Float64Array(16);\n for (let col = 0; col < 4; col++) {\n const c0 = b[col * 4]!;\n const c1 = b[col * 4 + 1]!;\n const c2 = b[col * 4 + 2]!;\n const c3 = b[col * 4 + 3]!;\n for (let row = 0; row < 4; row++) {\n r[col * 4 + row] =\n a[row]! * c0 +\n a[4 + row]! * c1 +\n a[8 + row]! * c2 +\n a[12 + row]! * c3;\n }\n }\n return r;\n}\n\n/**\n * Invert a 4x4 matrix. Returns null if the matrix is singular.\n * Uses cofactor expansion.\n */\nexport function invert(m: Mat4): Mat4 | null {\n const a00 = m[0]!,\n a01 = m[1]!,\n a02 = m[2]!,\n a03 = m[3]!;\n const a10 = m[4]!,\n a11 = m[5]!,\n a12 = m[6]!,\n a13 = m[7]!;\n const a20 = m[8]!,\n a21 = m[9]!,\n a22 = m[10]!,\n a23 = m[11]!;\n const a30 = m[12]!,\n a31 = m[13]!,\n a32 = m[14]!,\n a33 = m[15]!;\n\n const b00 = a00 * a11 - a01 * a10;\n const b01 = a00 * a12 - a02 * a10;\n const b02 = a00 * a13 - a03 * a10;\n const b03 = a01 * a12 - a02 * a11;\n const b04 = a01 * a13 - a03 * a11;\n const b05 = a02 * a13 - a03 * a12;\n const b06 = a20 * a31 - a21 * a30;\n const b07 = a20 * a32 - a22 * a30;\n const b08 = a20 * a33 - a23 * a30;\n const b09 = a21 * a32 - a22 * a31;\n const b10 = a21 * a33 - a23 * a31;\n const b11 = a22 * a33 - a23 * a32;\n\n const det =\n b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;\n if (Math.abs(det) < 1e-12) return null;\n\n const invDet = 1 / det;\n const r = new Float64Array(16);\n r[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet;\n r[1] = (a02 * b10 - a01 * b11 - a03 * b09) * invDet;\n r[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet;\n r[3] = (a22 * b04 - a21 * b05 - a23 * b03) * invDet;\n r[4] = (a12 * b08 - a10 * b11 - a13 * b07) * invDet;\n r[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet;\n r[6] = (a32 * b02 - a30 * b05 - a33 * b01) * invDet;\n r[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet;\n r[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet;\n r[9] = (a01 * b08 - a00 * b10 - a03 * b06) * invDet;\n r[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet;\n r[11] = (a21 * b02 - a20 * b04 - a23 * b00) * invDet;\n r[12] = (a11 * b07 - a10 * b09 - a12 * b06) * invDet;\n r[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet;\n r[14] = (a31 * b01 - a30 * b03 - a32 * b00) * invDet;\n r[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet;\n return r;\n}\n\n/** Transform a Vec3 by a Mat4 as a point (w=1). Returns the projected Vec3 after w-divide. */\nexport function transformPoint(m: Mat4, v: Vec3): Vec3 {\n const w = m[3]! * v.x + m[7]! * v.y + m[11]! * v.z + m[15]!;\n return {\n x: (m[0]! * v.x + m[4]! * v.y + m[8]! * v.z + m[12]!) / w,\n y: (m[1]! * v.x + m[5]! * v.y + m[9]! * v.z + m[13]!) / w,\n z: (m[2]! * v.x + m[6]! * v.y + m[10]! * v.z + m[14]!) / w,\n };\n}\n\n/** Transform a Vec3 by a Mat4 as a direction (w=0). No w-divide. */\nexport function transformDirection(m: Mat4, v: Vec3): Vec3 {\n return {\n x: m[0]! * v.x + m[4]! * v.y + m[8]! * v.z,\n y: m[1]! * v.x + m[5]! * v.y + m[9]! * v.z,\n z: m[2]! * v.x + m[6]! * v.y + m[10]! * v.z,\n };\n}\n\n/**\n * Transform a Vec3 by a Mat4 and return the raw clip-space coordinates\n * (x, y, z, w) before w-divide. Used by projection.\n */\nexport function transformPointRaw(\n m: Mat4,\n v: Vec3,\n): { x: number; y: number; z: number; w: number } {\n return {\n x: m[0]! * v.x + m[4]! * v.y + m[8]! * v.z + m[12]!,\n y: m[1]! * v.x + m[5]! * v.y + m[9]! * v.z + m[13]!,\n z: m[2]! * v.x + m[6]! * v.y + m[10]! * v.z + m[14]!,\n w: m[3]! * v.x + m[7]! * v.y + m[11]! * v.z + m[15]!,\n };\n}\n","import type { Camera, Mat4, Viewport } from \"./types.js\";\nimport { lookAt, multiply, orthographicMatrix, perspectiveMatrix } from \"./mat4.js\";\n\nconst DEFAULT_UP = { x: 0, y: 1, z: 0 } as const;\n\n/** Create a camera with sensible defaults. */\nexport function createCamera(options?: Partial<Camera>): Camera {\n return {\n position: options?.position ?? { x: 0, y: 1.7, z: 0 },\n target: options?.target ?? { x: 0, y: 0.5, z: 100 },\n up: options?.up ?? DEFAULT_UP,\n fov: options?.fov ?? 60,\n near: options?.near ?? 0.1,\n far: options?.far ?? 10000,\n projection: options?.projection ?? \"perspective\",\n orthoScale: options?.orthoScale,\n };\n}\n\n/** Create a new camera with updated properties. */\nexport function updateCamera(camera: Camera, updates: Partial<Camera>): Camera {\n return { ...camera, ...updates };\n}\n\n/** Compute the combined view-projection matrix for a camera and viewport. */\nexport function viewProjectionMatrix(camera: Camera, viewport: Viewport): Mat4 {\n const view = lookAt(camera.position, camera.target, camera.up);\n const aspect = viewport.width / viewport.height;\n\n let proj: Mat4;\n if (camera.projection === \"orthographic\") {\n const scale = camera.orthoScale ?? 10;\n const halfH = scale / 2;\n const halfW = halfH * aspect;\n proj = orthographicMatrix(-halfW, halfW, -halfH, halfH, camera.near, camera.far);\n } else {\n proj = perspectiveMatrix(camera.fov, aspect, camera.near, camera.far);\n }\n\n return multiply(proj, view);\n}\n","import type { Camera, Mat4, ProjectedPoint, Vec3, Viewport } from \"./types.js\";\nimport { transformPointRaw } from \"./mat4.js\";\nimport { viewProjectionMatrix } from \"./camera.js\";\nimport { distance as vec3Distance } from \"./vec3.js\";\nimport { invert } from \"./mat4.js\";\n\n/**\n * Project a world-space point to screen coordinates.\n *\n * Screen coordinates: (0,0) = top-left of viewport, (width, height) = bottom-right.\n * Depth: 0 = near plane, 1 = far plane.\n */\nexport function project(\n point: Vec3,\n camera: Camera,\n viewport: Viewport,\n): ProjectedPoint {\n const vp = viewProjectionMatrix(camera, viewport);\n return projectWithMatrix(point, vp, camera, viewport);\n}\n\n/**\n * Project a world-space point using a precomputed view-projection matrix.\n * Use this when projecting many points with the same camera to avoid\n * recomputing the matrix for each point.\n */\nexport function projectWithMatrix(\n point: Vec3,\n vpMatrix: Mat4,\n camera: Camera,\n viewport: Viewport,\n): ProjectedPoint {\n const clip = transformPointRaw(vpMatrix, point);\n\n // Behind the camera\n if (clip.w <= 0) {\n return {\n x: 0,\n y: 0,\n depth: -1,\n scale: 0,\n visible: false,\n distance: vec3Distance(point, camera.position),\n };\n }\n\n // NDC: -1 to +1\n const ndcX = clip.x / clip.w;\n const ndcY = clip.y / clip.w;\n const ndcZ = clip.z / clip.w;\n\n // Screen coordinates (Y flipped: NDC +Y is up, screen +Y is down)\n const screenX = viewport.x + ((ndcX + 1) / 2) * viewport.width;\n const screenY = viewport.y + ((1 - ndcY) / 2) * viewport.height;\n\n // Normalized depth 0..1\n const depth = (ndcZ + 1) / 2;\n\n // Visibility: inside NDC cube\n const visible =\n ndcX >= -1 && ndcX <= 1 && ndcY >= -1 && ndcY <= 1 && ndcZ >= -1 && ndcZ <= 1;\n\n // Scale factor: how many screen pixels per world unit at this depth.\n // For perspective, scale = (viewport.height / 2) * projectionMatrix[5] / clip.w\n // This gives us the vertical scale; we use it as a general scale factor.\n const fovRad = (camera.fov * Math.PI) / 180;\n const projScale =\n camera.projection === \"perspective\"\n ? (viewport.height / 2) * (1 / Math.tan(fovRad / 2)) / clip.w\n : (viewport.height / (camera.orthoScale ?? 10));\n\n return {\n x: screenX,\n y: screenY,\n depth,\n scale: projScale,\n visible,\n distance: vec3Distance(point, camera.position),\n };\n}\n\n/**\n * Project many points efficiently using a single precomputed matrix.\n */\nexport function projectMany(\n points: Vec3[],\n camera: Camera,\n viewport: Viewport,\n): ProjectedPoint[] {\n const vp = viewProjectionMatrix(camera, viewport);\n return points.map((p) => projectWithMatrix(p, vp, camera, viewport));\n}\n\n/**\n * Unproject a screen point back to world space.\n *\n * @param screenX - Screen X coordinate\n * @param screenY - Screen Y coordinate\n * @param depth - Normalized depth 0 (near) to 1 (far)\n * @param camera - Camera\n * @param viewport - Viewport\n * @returns World-space position, or null if the matrix is singular\n */\nexport function unproject(\n screenX: number,\n screenY: number,\n depth: number,\n camera: Camera,\n viewport: Viewport,\n): Vec3 | null {\n const vp = viewProjectionMatrix(camera, viewport);\n const inv = invert(vp);\n if (!inv) return null;\n\n // Screen to NDC\n const ndcX = ((screenX - viewport.x) / viewport.width) * 2 - 1;\n const ndcY = 1 - ((screenY - viewport.y) / viewport.height) * 2;\n const ndcZ = depth * 2 - 1;\n\n const clip = transformPointRaw(inv, { x: ndcX, y: ndcY, z: ndcZ });\n if (Math.abs(clip.w) < 1e-12) return null;\n\n return {\n x: clip.x / clip.w,\n y: clip.y / clip.w,\n z: clip.z / clip.w,\n };\n}\n","import type { BoundingBox3D, Camera, Vec2, Vec3, Viewport } from \"./types.js\";\nimport { viewProjectionMatrix } from \"./camera.js\";\nimport { transformPointRaw } from \"./mat4.js\";\nimport { dot, normalize, sub, distance as vec3Distance } from \"./vec3.js\";\n\n/**\n * Test if a point is inside the camera frustum (after projection, inside NDC cube).\n */\nexport function frustumContainsPoint(point: Vec3, camera: Camera, viewport: Viewport): boolean {\n const vp = viewProjectionMatrix(camera, viewport);\n const clip = transformPointRaw(vp, point);\n if (clip.w <= 0) return false;\n const ndcX = clip.x / clip.w;\n const ndcY = clip.y / clip.w;\n const ndcZ = clip.z / clip.w;\n return ndcX >= -1 && ndcX <= 1 && ndcY >= -1 && ndcY <= 1 && ndcZ >= -1 && ndcZ <= 1;\n}\n\n/**\n * Test a bounding box against the camera frustum.\n * Returns 'inside' if fully contained, 'intersect' if partially visible, 'outside' if not visible.\n *\n * Uses a conservative test: projects all 8 corners and checks NDC bounds.\n */\nexport function frustumContainsBox(\n box: BoundingBox3D,\n camera: Camera,\n viewport: Viewport,\n): \"inside\" | \"intersect\" | \"outside\" {\n const vp = viewProjectionMatrix(camera, viewport);\n const corners: Vec3[] = [\n { x: box.min.x, y: box.min.y, z: box.min.z },\n { x: box.max.x, y: box.min.y, z: box.min.z },\n { x: box.min.x, y: box.max.y, z: box.min.z },\n { x: box.max.x, y: box.max.y, z: box.min.z },\n { x: box.min.x, y: box.min.y, z: box.max.z },\n { x: box.max.x, y: box.min.y, z: box.max.z },\n { x: box.min.x, y: box.max.y, z: box.max.z },\n { x: box.max.x, y: box.max.y, z: box.max.z },\n ];\n\n let allInside = true;\n let anyInside = false;\n\n for (const corner of corners) {\n const clip = transformPointRaw(vp, corner);\n if (clip.w <= 0) {\n allInside = false;\n continue;\n }\n const ndcX = clip.x / clip.w;\n const ndcY = clip.y / clip.w;\n const ndcZ = clip.z / clip.w;\n const inside =\n ndcX >= -1 && ndcX <= 1 && ndcY >= -1 && ndcY <= 1 && ndcZ >= -1 && ndcZ <= 1;\n if (inside) {\n anyInside = true;\n } else {\n allInside = false;\n }\n }\n\n if (allInside) return \"inside\";\n if (anyInside) return \"intersect\";\n\n // Even if no corners are inside, the box could still intersect the frustum\n // (e.g. a large box surrounding the camera). For a conservative test,\n // check if the projected AABB of all corners overlaps the NDC cube.\n let minNdcX = Infinity,\n maxNdcX = -Infinity;\n let minNdcY = Infinity,\n maxNdcY = -Infinity;\n let behindCount = 0;\n\n for (const corner of corners) {\n const clip = transformPointRaw(vp, corner);\n if (clip.w <= 0) {\n behindCount++;\n continue;\n }\n const ndcX = clip.x / clip.w;\n const ndcY = clip.y / clip.w;\n minNdcX = Math.min(minNdcX, ndcX);\n maxNdcX = Math.max(maxNdcX, ndcX);\n minNdcY = Math.min(minNdcY, ndcY);\n maxNdcY = Math.max(maxNdcY, ndcY);\n }\n\n // If some corners are behind and some in front, the box straddles the near plane\n if (behindCount > 0 && behindCount < 8) return \"intersect\";\n\n // All behind camera\n if (behindCount === 8) return \"outside\";\n\n // Check 2D overlap between projected AABB and NDC square\n if (maxNdcX < -1 || minNdcX > 1 || maxNdcY < -1 || minNdcY > 1) return \"outside\";\n return \"intersect\";\n}\n\n/**\n * Test if a face is a back face (facing away from the camera).\n * Uses the dot product between the face normal and the camera-to-face direction.\n */\nexport function isBackFace(faceNormal: Vec3, faceCenter: Vec3, camera: Camera): boolean {\n const toCamera = sub(camera.position, faceCenter);\n return dot(faceNormal, toCamera) <= 0;\n}\n\n/**\n * Get the world-to-screen scale factor at a given world point.\n * Returns how many screen pixels correspond to 1 world unit at that depth.\n */\nexport function scaleAtDepth(\n worldPoint: Vec3,\n camera: Camera,\n viewport: Viewport,\n): number {\n const vp = viewProjectionMatrix(camera, viewport);\n const clip = transformPointRaw(vp, worldPoint);\n if (clip.w <= 0) return 0;\n\n if (camera.projection === \"perspective\") {\n const fovRad = (camera.fov * Math.PI) / 180;\n return (viewport.height / 2) * (1 / Math.tan(fovRad / 2)) / clip.w;\n }\n return viewport.height / (camera.orthoScale ?? 10);\n}\n\n/**\n * Get the normalized depth (0 = near plane, 1 = far plane) of a world point.\n */\nexport function normalizedDepth(point: Vec3, camera: Camera, viewport: Viewport): number {\n const vp = viewProjectionMatrix(camera, viewport);\n const clip = transformPointRaw(vp, point);\n if (clip.w <= 0) return -1;\n const ndcZ = clip.z / clip.w;\n return (ndcZ + 1) / 2;\n}\n\n/**\n * Get the world-unit distance from the camera to a point.\n */\nexport function distanceTo(point: Vec3, camera: Camera): number {\n return vec3Distance(point, camera.position);\n}\n\n/**\n * Compute the screen Y coordinate of the horizon line.\n * The horizon is where the camera's look direction intersects Y = camera.position.y\n * at infinite distance, projected to screen space.\n */\nexport function horizonScreenY(camera: Camera, viewport: Viewport): number {\n // The horizon is the projection of a point at eye level, infinitely far away.\n // We approximate by projecting a point very far along the horizontal\n // component of the look direction.\n const lookDir = normalize(sub(camera.target, camera.position));\n\n // Horizontal direction (project lookDir onto XZ plane)\n const horizDir = normalize({ x: lookDir.x, y: 0, z: lookDir.z });\n if (horizDir.x === 0 && horizDir.z === 0) {\n // Looking straight up or down — horizon is at screen center\n return viewport.y + viewport.height / 2;\n }\n\n const farPoint: Vec3 = {\n x: camera.position.x + horizDir.x * camera.far * 0.99,\n y: camera.position.y,\n z: camera.position.z + horizDir.z * camera.far * 0.99,\n };\n\n const vp = viewProjectionMatrix(camera, viewport);\n const clip = transformPointRaw(vp, farPoint);\n if (clip.w <= 0) return viewport.y + viewport.height / 2;\n\n const ndcY = clip.y / clip.w;\n return viewport.y + ((1 - ndcY) / 2) * viewport.height;\n}\n\n/**\n * Derive vanishing point screen positions from a camera.\n *\n * - `left`: VP for lines parallel to the world X axis (projects to screen position)\n * - `right`: VP for lines parallel to the world -X axis (same point, opposite direction)\n * - `vertical`: VP for lines parallel to the world Y axis (only meaningful for 3-point perspective)\n *\n * Returns undefined for a VP direction if it projects behind the camera.\n */\nexport function deriveVanishingPoints(\n camera: Camera,\n viewport: Viewport,\n): { left?: Vec2; right?: Vec2; vertical?: Vec2 } {\n const vp = viewProjectionMatrix(camera, viewport);\n\n // A VP is where a world-space direction converges on screen.\n // Project a point very far along each axis from the camera.\n const far = camera.far * 0.99;\n\n const projectDir = (dir: Vec3): Vec2 | undefined => {\n const point: Vec3 = {\n x: camera.position.x + dir.x * far,\n y: camera.position.y + dir.y * far,\n z: camera.position.z + dir.z * far,\n };\n const clip = transformPointRaw(vp, point);\n if (clip.w <= 0) return undefined;\n const ndcX = clip.x / clip.w;\n const ndcY = clip.y / clip.w;\n return {\n x: viewport.x + ((ndcX + 1) / 2) * viewport.width,\n y: viewport.y + ((1 - ndcY) / 2) * viewport.height,\n };\n };\n\n return {\n left: projectDir({ x: 1, y: 0, z: 0 }),\n right: projectDir({ x: -1, y: 0, z: 0 }),\n vertical: projectDir({ x: 0, y: 1, z: 0 }),\n };\n}\n","import type { Camera, Vec3 } from \"./types.js\";\nimport { distanceSq } from \"./vec3.js\";\n\n/**\n * Sort items by depth (back-to-front by default: farthest first).\n * Uses squared distance to avoid sqrt per item.\n */\nexport function depthSort<T>(\n items: T[],\n positionFn: (item: T) => Vec3,\n camera: Camera,\n): T[] {\n const camPos = camera.position;\n return [...items].sort((a, b) => {\n const da = distanceSq(positionFn(a), camPos);\n const db = distanceSq(positionFn(b), camPos);\n return db - da; // farthest first (painter's algorithm)\n });\n}\n\n/**\n * Return indices sorted by depth (back-to-front: farthest first).\n */\nexport function depthSortIndices(positions: Vec3[], camera: Camera): number[] {\n const camPos = camera.position;\n const indices = positions.map((_, i) => i);\n indices.sort((a, b) => {\n const pa = positions[a]!;\n const pb = positions[b]!;\n return distanceSq(pb, camPos) - distanceSq(pa, camPos);\n });\n return indices;\n}\n","import type { Camera } from \"./types.js\";\nimport { createCamera } from \"./camera.js\";\n\n/**\n * Landscape camera — standing eye-height, looking toward distant horizon.\n * Default: 1.7m eye height, 60° FOV, looking slightly above horizon.\n */\nexport function landscapeCamera(options?: {\n eyeHeight?: number;\n lookDistance?: number;\n lookElevation?: number;\n fov?: number;\n}): Camera {\n const eyeHeight = options?.eyeHeight ?? 1.7;\n const lookDistance = options?.lookDistance ?? 100;\n const lookElevation = options?.lookElevation ?? 0.3;\n return createCamera({\n position: { x: 0, y: eyeHeight, z: 0 },\n target: { x: 0, y: lookElevation, z: lookDistance },\n fov: options?.fov ?? 60,\n });\n}\n\n/**\n * Aerial camera — elevated position looking down at an angle.\n * Default: 200m altitude, 45° down, 50° FOV.\n */\nexport function aerialCamera(options?: {\n altitude?: number;\n lookAngle?: number;\n fov?: number;\n}): Camera {\n const altitude = options?.altitude ?? 200;\n const angleDeg = options?.lookAngle ?? 45;\n const angleRad = (angleDeg * Math.PI) / 180;\n const lookDistance = altitude / Math.tan(angleRad);\n return createCamera({\n position: { x: 0, y: altitude, z: 0 },\n target: { x: 0, y: 0, z: lookDistance },\n fov: options?.fov ?? 50,\n });\n}\n\n/**\n * Street-level camera — eye-height, optionally rotated horizontally.\n * Default: 1.7m, straight ahead, 55° FOV (natural perspective).\n */\nexport function streetCamera(options?: {\n eyeHeight?: number;\n lookDirection?: number;\n fov?: number;\n}): Camera {\n const eyeHeight = options?.eyeHeight ?? 1.7;\n const angleDeg = options?.lookDirection ?? 0;\n const angleRad = (angleDeg * Math.PI) / 180;\n const lookDist = 50;\n return createCamera({\n position: { x: 0, y: eyeHeight, z: 0 },\n target: {\n x: Math.sin(angleRad) * lookDist,\n y: eyeHeight * 0.8,\n z: Math.cos(angleRad) * lookDist,\n },\n fov: options?.fov ?? 55,\n });\n}\n\n/**\n * Worm's-eye camera — low position looking upward.\n * Default: 0.3m height, 60° tilt upward, 75° FOV (dramatic).\n */\nexport function wormEyeCamera(options?: {\n groundLevel?: number;\n tiltAngle?: number;\n fov?: number;\n}): Camera {\n const groundLevel = options?.groundLevel ?? 0.3;\n const tiltDeg = options?.tiltAngle ?? 60;\n const tiltRad = (tiltDeg * Math.PI) / 180;\n const lookDist = 30;\n return createCamera({\n position: { x: 0, y: groundLevel, z: 0 },\n target: {\n x: 0,\n y: groundLevel + Math.sin(tiltRad) * lookDist,\n z: Math.cos(tiltRad) * lookDist,\n },\n fov: options?.fov ?? 75,\n });\n}\n\n/**\n * Isometric camera — orthographic projection at a fixed angle.\n * Default: 30° angle, 100 units visible vertically.\n */\nexport function isometricCamera(options?: {\n angle?: number;\n distance?: number;\n orthoScale?: number;\n}): Camera {\n const angleDeg = options?.angle ?? 30;\n const angleRad = (angleDeg * Math.PI) / 180;\n const dist = options?.distance ?? 200;\n return createCamera({\n position: {\n x: -Math.cos(angleRad) * dist,\n y: Math.sin(angleRad) * dist + dist * 0.5,\n z: -Math.cos(angleRad) * dist,\n },\n target: { x: 0, y: 0, z: 0 },\n projection: \"orthographic\",\n orthoScale: options?.orthoScale ?? 100,\n });\n}\n","import type { Camera, Vec3 } from \"./types.js\";\nimport { add, cross, normalize, scale, sub } from \"./vec3.js\";\nimport { lerp as lerpVec3 } from \"./vec3.js\";\n\n/** Linear interpolation between two cameras. */\nexport function lerpCamera(a: Camera, b: Camera, t: number): Camera {\n return {\n position: lerpVec3(a.position, b.position, t),\n target: lerpVec3(a.target, b.target, t),\n up: normalize(lerpVec3(a.up, b.up, t)),\n fov: a.fov + (b.fov - a.fov) * t,\n near: a.near + (b.near - a.near) * t,\n far: a.far + (b.far - a.far) * t,\n projection: t < 0.5 ? a.projection : b.projection,\n orthoScale:\n a.orthoScale != null && b.orthoScale != null\n ? a.orthoScale + (b.orthoScale - a.orthoScale) * t\n : (t < 0.5 ? a.orthoScale : b.orthoScale),\n };\n}\n\n/**\n * Orbit the camera around a center point.\n *\n * @param camera - Current camera\n * @param angleX - Horizontal rotation in radians (positive = rotate right)\n * @param angleY - Vertical rotation in radians (positive = rotate up)\n * @param center - Orbit center (defaults to camera target)\n */\nexport function orbitCamera(\n camera: Camera,\n angleX: number,\n angleY: number,\n center?: Vec3,\n): Camera {\n const pivot = center ?? camera.target;\n const offset = sub(camera.position, pivot);\n\n // Horizontal rotation (around world Y axis)\n const cosX = Math.cos(angleX);\n const sinX = Math.sin(angleX);\n let rotated: Vec3 = {\n x: offset.x * cosX + offset.z * sinX,\n y: offset.y,\n z: -offset.x * sinX + offset.z * cosX,\n };\n\n // Vertical rotation (around the camera's right vector)\n const forward = normalize(sub(pivot, add(pivot, rotated)));\n const right = normalize(cross(camera.up, forward));\n const cosY = Math.cos(angleY);\n const sinY = Math.sin(angleY);\n\n // Rodrigues rotation around `right` axis\n const dotRR = right.x * rotated.x + right.y * rotated.y + right.z * rotated.z;\n const crossRR = cross(right, rotated);\n rotated = {\n x: rotated.x * cosY + crossRR.x * sinY + right.x * dotRR * (1 - cosY),\n y: rotated.y * cosY + crossRR.y * sinY + right.y * dotRR * (1 - cosY),\n z: rotated.z * cosY + crossRR.z * sinY + right.z * dotRR * (1 - cosY),\n };\n\n return {\n ...camera,\n position: add(pivot, rotated),\n };\n}\n\n/**\n * Dolly the camera forward/backward along its look direction.\n *\n * @param camera - Current camera\n * @param distance - Positive = move toward target, negative = move away\n */\nexport function dollyCamera(camera: Camera, distance: number): Camera {\n const dir = normalize(sub(camera.target, camera.position));\n const move = scale(dir, distance);\n return {\n ...camera,\n position: add(camera.position, move),\n target: add(camera.target, move),\n };\n}\n\n/**\n * Pan the camera (shift left/right/up/down) without changing the look direction.\n *\n * @param camera - Current camera\n * @param dx - World units to move right (negative = left)\n * @param dy - World units to move up (negative = down)\n */\nexport function panCamera(camera: Camera, dx: number, dy: number): Camera {\n const forward = normalize(sub(camera.target, camera.position));\n const right = normalize(cross(camera.up, forward));\n const up = cross(forward, right);\n const offset = add(scale(right, dx), scale(up, dy));\n return {\n ...camera,\n position: add(camera.position, offset),\n target: add(camera.target, offset),\n };\n}\n"],"mappings":"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","names":["scale","distance"]}
@@ -0,0 +1,301 @@
1
+ /** 2D vector. */
2
+ interface Vec2 {
3
+ readonly x: number;
4
+ readonly y: number;
5
+ }
6
+ /** 3D vector. Y-up, right-handed coordinate system. */
7
+ interface Vec3 {
8
+ readonly x: number;
9
+ readonly y: number;
10
+ readonly z: number;
11
+ }
12
+ /**
13
+ * 4x4 matrix stored as a 16-element Float64Array in column-major order.
14
+ *
15
+ * Column-major layout (OpenGL convention):
16
+ * ```
17
+ * | m[0] m[4] m[8] m[12] |
18
+ * | m[1] m[5] m[9] m[13] |
19
+ * | m[2] m[6] m[10] m[14] |
20
+ * | m[3] m[7] m[11] m[15] |
21
+ * ```
22
+ */
23
+ type Mat4 = Float64Array;
24
+ /** Camera definition. Perspective or orthographic projection. */
25
+ interface Camera {
26
+ /** Camera position in world space. */
27
+ readonly position: Vec3;
28
+ /** Look-at target in world space. */
29
+ readonly target: Vec3;
30
+ /** Up vector. Default (0, 1, 0). */
31
+ readonly up: Vec3;
32
+ /** Vertical field of view in degrees (perspective mode). */
33
+ readonly fov: number;
34
+ /** Near clip distance. */
35
+ readonly near: number;
36
+ /** Far clip distance. */
37
+ readonly far: number;
38
+ /** Projection type. */
39
+ readonly projection: "perspective" | "orthographic";
40
+ /** World units visible vertically (orthographic mode only). */
41
+ readonly orthoScale?: number;
42
+ }
43
+ /** Screen viewport rectangle. */
44
+ interface Viewport {
45
+ readonly x: number;
46
+ readonly y: number;
47
+ readonly width: number;
48
+ readonly height: number;
49
+ }
50
+ /** Result of projecting a 3D point to screen space. */
51
+ interface ProjectedPoint {
52
+ /** Screen X coordinate. */
53
+ readonly x: number;
54
+ /** Screen Y coordinate. */
55
+ readonly y: number;
56
+ /** Normalized depth: 0 (near) to 1 (far). For sorting and atmosphere. */
57
+ readonly depth: number;
58
+ /** World-to-screen scale factor at this depth. */
59
+ readonly scale: number;
60
+ /** Whether the point is within the camera frustum. */
61
+ readonly visible: boolean;
62
+ /** World-unit distance from camera position. */
63
+ readonly distance: number;
64
+ }
65
+ /** Axis-aligned bounding box in 3D. */
66
+ interface BoundingBox3D {
67
+ readonly min: Vec3;
68
+ readonly max: Vec3;
69
+ }
70
+
71
+ /** Create a Vec3. */
72
+ declare function vec3(x: number, y: number, z: number): Vec3;
73
+ /** Add two vectors. */
74
+ declare function add(a: Vec3, b: Vec3): Vec3;
75
+ /** Subtract b from a. */
76
+ declare function sub(a: Vec3, b: Vec3): Vec3;
77
+ /** Scale a vector by a scalar. */
78
+ declare function scale(v: Vec3, s: number): Vec3;
79
+ /** Dot product. */
80
+ declare function dot(a: Vec3, b: Vec3): number;
81
+ /** Cross product. */
82
+ declare function cross(a: Vec3, b: Vec3): Vec3;
83
+ /** Vector length. */
84
+ declare function length(v: Vec3): number;
85
+ /** Squared length (avoids sqrt). */
86
+ declare function lengthSq(v: Vec3): number;
87
+ /** Normalize to unit length. Returns zero vector if input is zero length. */
88
+ declare function normalize(v: Vec3): Vec3;
89
+ /** Negate a vector. */
90
+ declare function negate(v: Vec3): Vec3;
91
+ /** Linear interpolation between two vectors. */
92
+ declare function lerp(a: Vec3, b: Vec3, t: number): Vec3;
93
+ /** Distance between two points. */
94
+ declare function distance(a: Vec3, b: Vec3): number;
95
+ /** Squared distance (avoids sqrt). */
96
+ declare function distanceSq(a: Vec3, b: Vec3): number;
97
+
98
+ /** Create an identity matrix. */
99
+ declare function identity(): Mat4;
100
+ /**
101
+ * Build a view matrix (world-to-camera transform).
102
+ * Uses right-handed convention: camera looks along -Z in view space.
103
+ */
104
+ declare function lookAt(eye: Vec3, target: Vec3, up: Vec3): Mat4;
105
+ /** Build a perspective projection matrix. */
106
+ declare function perspectiveMatrix(fovDegrees: number, aspect: number, near: number, far: number): Mat4;
107
+ /** Build an orthographic projection matrix. */
108
+ declare function orthographicMatrix(left: number, right: number, bottom: number, top: number, near: number, far: number): Mat4;
109
+ /** Multiply two 4x4 matrices: result = a * b. */
110
+ declare function multiply(a: Mat4, b: Mat4): Mat4;
111
+ /**
112
+ * Invert a 4x4 matrix. Returns null if the matrix is singular.
113
+ * Uses cofactor expansion.
114
+ */
115
+ declare function invert(m: Mat4): Mat4 | null;
116
+ /** Transform a Vec3 by a Mat4 as a point (w=1). Returns the projected Vec3 after w-divide. */
117
+ declare function transformPoint(m: Mat4, v: Vec3): Vec3;
118
+ /** Transform a Vec3 by a Mat4 as a direction (w=0). No w-divide. */
119
+ declare function transformDirection(m: Mat4, v: Vec3): Vec3;
120
+ /**
121
+ * Transform a Vec3 by a Mat4 and return the raw clip-space coordinates
122
+ * (x, y, z, w) before w-divide. Used by projection.
123
+ */
124
+ declare function transformPointRaw(m: Mat4, v: Vec3): {
125
+ x: number;
126
+ y: number;
127
+ z: number;
128
+ w: number;
129
+ };
130
+
131
+ /** Create a camera with sensible defaults. */
132
+ declare function createCamera(options?: Partial<Camera>): Camera;
133
+ /** Create a new camera with updated properties. */
134
+ declare function updateCamera(camera: Camera, updates: Partial<Camera>): Camera;
135
+ /** Compute the combined view-projection matrix for a camera and viewport. */
136
+ declare function viewProjectionMatrix(camera: Camera, viewport: Viewport): Mat4;
137
+
138
+ /**
139
+ * Project a world-space point to screen coordinates.
140
+ *
141
+ * Screen coordinates: (0,0) = top-left of viewport, (width, height) = bottom-right.
142
+ * Depth: 0 = near plane, 1 = far plane.
143
+ */
144
+ declare function project(point: Vec3, camera: Camera, viewport: Viewport): ProjectedPoint;
145
+ /**
146
+ * Project a world-space point using a precomputed view-projection matrix.
147
+ * Use this when projecting many points with the same camera to avoid
148
+ * recomputing the matrix for each point.
149
+ */
150
+ declare function projectWithMatrix(point: Vec3, vpMatrix: Mat4, camera: Camera, viewport: Viewport): ProjectedPoint;
151
+ /**
152
+ * Project many points efficiently using a single precomputed matrix.
153
+ */
154
+ declare function projectMany(points: Vec3[], camera: Camera, viewport: Viewport): ProjectedPoint[];
155
+ /**
156
+ * Unproject a screen point back to world space.
157
+ *
158
+ * @param screenX - Screen X coordinate
159
+ * @param screenY - Screen Y coordinate
160
+ * @param depth - Normalized depth 0 (near) to 1 (far)
161
+ * @param camera - Camera
162
+ * @param viewport - Viewport
163
+ * @returns World-space position, or null if the matrix is singular
164
+ */
165
+ declare function unproject(screenX: number, screenY: number, depth: number, camera: Camera, viewport: Viewport): Vec3 | null;
166
+
167
+ /**
168
+ * Test if a point is inside the camera frustum (after projection, inside NDC cube).
169
+ */
170
+ declare function frustumContainsPoint(point: Vec3, camera: Camera, viewport: Viewport): boolean;
171
+ /**
172
+ * Test a bounding box against the camera frustum.
173
+ * Returns 'inside' if fully contained, 'intersect' if partially visible, 'outside' if not visible.
174
+ *
175
+ * Uses a conservative test: projects all 8 corners and checks NDC bounds.
176
+ */
177
+ declare function frustumContainsBox(box: BoundingBox3D, camera: Camera, viewport: Viewport): "inside" | "intersect" | "outside";
178
+ /**
179
+ * Test if a face is a back face (facing away from the camera).
180
+ * Uses the dot product between the face normal and the camera-to-face direction.
181
+ */
182
+ declare function isBackFace(faceNormal: Vec3, faceCenter: Vec3, camera: Camera): boolean;
183
+ /**
184
+ * Get the world-to-screen scale factor at a given world point.
185
+ * Returns how many screen pixels correspond to 1 world unit at that depth.
186
+ */
187
+ declare function scaleAtDepth(worldPoint: Vec3, camera: Camera, viewport: Viewport): number;
188
+ /**
189
+ * Get the normalized depth (0 = near plane, 1 = far plane) of a world point.
190
+ */
191
+ declare function normalizedDepth(point: Vec3, camera: Camera, viewport: Viewport): number;
192
+ /**
193
+ * Get the world-unit distance from the camera to a point.
194
+ */
195
+ declare function distanceTo(point: Vec3, camera: Camera): number;
196
+ /**
197
+ * Compute the screen Y coordinate of the horizon line.
198
+ * The horizon is where the camera's look direction intersects Y = camera.position.y
199
+ * at infinite distance, projected to screen space.
200
+ */
201
+ declare function horizonScreenY(camera: Camera, viewport: Viewport): number;
202
+ /**
203
+ * Derive vanishing point screen positions from a camera.
204
+ *
205
+ * - `left`: VP for lines parallel to the world X axis (projects to screen position)
206
+ * - `right`: VP for lines parallel to the world -X axis (same point, opposite direction)
207
+ * - `vertical`: VP for lines parallel to the world Y axis (only meaningful for 3-point perspective)
208
+ *
209
+ * Returns undefined for a VP direction if it projects behind the camera.
210
+ */
211
+ declare function deriveVanishingPoints(camera: Camera, viewport: Viewport): {
212
+ left?: Vec2;
213
+ right?: Vec2;
214
+ vertical?: Vec2;
215
+ };
216
+
217
+ /**
218
+ * Sort items by depth (back-to-front by default: farthest first).
219
+ * Uses squared distance to avoid sqrt per item.
220
+ */
221
+ declare function depthSort<T>(items: T[], positionFn: (item: T) => Vec3, camera: Camera): T[];
222
+ /**
223
+ * Return indices sorted by depth (back-to-front: farthest first).
224
+ */
225
+ declare function depthSortIndices(positions: Vec3[], camera: Camera): number[];
226
+
227
+ /**
228
+ * Landscape camera — standing eye-height, looking toward distant horizon.
229
+ * Default: 1.7m eye height, 60° FOV, looking slightly above horizon.
230
+ */
231
+ declare function landscapeCamera(options?: {
232
+ eyeHeight?: number;
233
+ lookDistance?: number;
234
+ lookElevation?: number;
235
+ fov?: number;
236
+ }): Camera;
237
+ /**
238
+ * Aerial camera — elevated position looking down at an angle.
239
+ * Default: 200m altitude, 45° down, 50° FOV.
240
+ */
241
+ declare function aerialCamera(options?: {
242
+ altitude?: number;
243
+ lookAngle?: number;
244
+ fov?: number;
245
+ }): Camera;
246
+ /**
247
+ * Street-level camera — eye-height, optionally rotated horizontally.
248
+ * Default: 1.7m, straight ahead, 55° FOV (natural perspective).
249
+ */
250
+ declare function streetCamera(options?: {
251
+ eyeHeight?: number;
252
+ lookDirection?: number;
253
+ fov?: number;
254
+ }): Camera;
255
+ /**
256
+ * Worm's-eye camera — low position looking upward.
257
+ * Default: 0.3m height, 60° tilt upward, 75° FOV (dramatic).
258
+ */
259
+ declare function wormEyeCamera(options?: {
260
+ groundLevel?: number;
261
+ tiltAngle?: number;
262
+ fov?: number;
263
+ }): Camera;
264
+ /**
265
+ * Isometric camera — orthographic projection at a fixed angle.
266
+ * Default: 30° angle, 100 units visible vertically.
267
+ */
268
+ declare function isometricCamera(options?: {
269
+ angle?: number;
270
+ distance?: number;
271
+ orthoScale?: number;
272
+ }): Camera;
273
+
274
+ /** Linear interpolation between two cameras. */
275
+ declare function lerpCamera(a: Camera, b: Camera, t: number): Camera;
276
+ /**
277
+ * Orbit the camera around a center point.
278
+ *
279
+ * @param camera - Current camera
280
+ * @param angleX - Horizontal rotation in radians (positive = rotate right)
281
+ * @param angleY - Vertical rotation in radians (positive = rotate up)
282
+ * @param center - Orbit center (defaults to camera target)
283
+ */
284
+ declare function orbitCamera(camera: Camera, angleX: number, angleY: number, center?: Vec3): Camera;
285
+ /**
286
+ * Dolly the camera forward/backward along its look direction.
287
+ *
288
+ * @param camera - Current camera
289
+ * @param distance - Positive = move toward target, negative = move away
290
+ */
291
+ declare function dollyCamera(camera: Camera, distance: number): Camera;
292
+ /**
293
+ * Pan the camera (shift left/right/up/down) without changing the look direction.
294
+ *
295
+ * @param camera - Current camera
296
+ * @param dx - World units to move right (negative = left)
297
+ * @param dy - World units to move up (negative = down)
298
+ */
299
+ declare function panCamera(camera: Camera, dx: number, dy: number): Camera;
300
+
301
+ export { type BoundingBox3D, type Camera, type Mat4, type ProjectedPoint, type Vec2, type Vec3, type Viewport, add, aerialCamera, createCamera, cross, depthSort, depthSortIndices, deriveVanishingPoints, distance, distanceSq, distanceTo, dollyCamera, dot, frustumContainsBox, frustumContainsPoint, horizonScreenY, identity, invert, isBackFace, isometricCamera, landscapeCamera, length, lengthSq, lerpCamera, lerp as lerpVec3, lookAt, multiply, negate, normalize, normalizedDepth, orbitCamera, orthographicMatrix, panCamera, perspectiveMatrix, project, projectMany, projectWithMatrix, scaleAtDepth, scale as scaleVec3, streetCamera, sub, transformDirection, transformPoint, transformPointRaw, unproject, updateCamera, vec3, viewProjectionMatrix, wormEyeCamera };