@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/vec3.ts","../src/mat4.ts","../src/camera.ts","../src/project.ts","../src/spatial.ts","../src/sorting.ts","../src/presets.ts","../src/interpolation.ts"],"sourcesContent":["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":";AAGO,SAAS,KAAK,GAAW,GAAW,GAAiB;AAC1D,SAAO,EAAE,GAAG,GAAG,EAAE;AACnB;AAGO,SAAS,IAAI,GAAS,GAAe;AAC1C,SAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,EAAE;AACpD;AAGO,SAAS,IAAI,GAAS,GAAe;AAC1C,SAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,EAAE;AACpD;AAGO,SAAS,MAAM,GAAS,GAAiB;AAC9C,SAAO,EAAE,GAAG,EAAE,IAAI,GAAG,GAAG,EAAE,IAAI,GAAG,GAAG,EAAE,IAAI,EAAE;AAC9C;AAGO,SAAS,IAAI,GAAS,GAAiB;AAC5C,SAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;AACzC;AAGO,SAAS,MAAM,GAAS,GAAe;AAC5C,SAAO;AAAA,IACL,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;AAAA,IACvB,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;AAAA,IACvB,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;AAAA,EACzB;AACF;AAGO,SAAS,OAAO,GAAiB;AACtC,SAAO,KAAK,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACpD;AAGO,SAAS,SAAS,GAAiB;AACxC,SAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;AACzC;AAGO,SAAS,UAAU,GAAe;AACvC,QAAM,MAAM,OAAO,CAAC;AACpB,MAAI,QAAQ,EAAG,QAAO,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE;AACzC,SAAO,EAAE,GAAG,EAAE,IAAI,KAAK,GAAG,EAAE,IAAI,KAAK,GAAG,EAAE,IAAI,IAAI;AACpD;AAGO,SAAS,OAAO,GAAe;AACpC,SAAO,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,EAAE;AACrC;AAGO,SAAS,KAAK,GAAS,GAAS,GAAiB;AACtD,SAAO;AAAA,IACL,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;AAAA,IACvB,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;AAAA,IACvB,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;AAAA,EACzB;AACF;AAGO,SAAS,SAAS,GAAS,GAAiB;AACjD,SAAO,OAAO,IAAI,GAAG,CAAC,CAAC;AACzB;AAGO,SAAS,WAAW,GAAS,GAAiB;AACnD,SAAO,SAAS,IAAI,GAAG,CAAC,CAAC;AAC3B;;;ACvEO,SAAS,WAAiB;AAC/B,QAAM,IAAI,IAAI,aAAa,EAAE;AAC7B,IAAE,CAAC,IAAI;AACP,IAAE,CAAC,IAAI;AACP,IAAE,EAAE,IAAI;AACR,IAAE,EAAE,IAAI;AACR,SAAO;AACT;AAMO,SAAS,OAAO,KAAW,QAAc,IAAgB;AAC9D,QAAM,IAAI,UAAU,IAAI,QAAQ,GAAG,CAAC;AACpC,QAAM,IAAI,UAAU,MAAM,IAAI,CAAC,CAAC;AAChC,QAAM,IAAI,MAAM,GAAG,CAAC;AAEpB,QAAM,IAAI,IAAI,aAAa,EAAE;AAC7B,IAAE,CAAC,IAAI,EAAE;AACT,IAAE,CAAC,IAAI,EAAE;AACT,IAAE,CAAC,IAAI,CAAC,EAAE;AACV,IAAE,CAAC,IAAI;AACP,IAAE,CAAC,IAAI,EAAE;AACT,IAAE,CAAC,IAAI,EAAE;AACT,IAAE,CAAC,IAAI,CAAC,EAAE;AACV,IAAE,CAAC,IAAI;AACP,IAAE,CAAC,IAAI,EAAE;AACT,IAAE,CAAC,IAAI,EAAE;AACT,IAAE,EAAE,IAAI,CAAC,EAAE;AACX,IAAE,EAAE,IAAI;AACR,IAAE,EAAE,IAAI,CAAC,IAAI,GAAG,GAAG;AACnB,IAAE,EAAE,IAAI,CAAC,IAAI,GAAG,GAAG;AACnB,IAAE,EAAE,IAAI,IAAI,GAAG,GAAG;AAClB,IAAE,EAAE,IAAI;AACR,SAAO;AACT;AAGO,SAAS,kBACd,YACA,QACA,MACA,KACM;AACN,QAAM,SAAU,aAAa,KAAK,KAAM;AACxC,QAAM,IAAI,IAAI,KAAK,IAAI,SAAS,CAAC;AACjC,QAAM,WAAW,KAAK,OAAO;AAE7B,QAAM,IAAI,IAAI,aAAa,EAAE;AAC7B,IAAE,CAAC,IAAI,IAAI;AACX,IAAE,CAAC,IAAI;AACP,IAAE,EAAE,KAAK,MAAM,QAAQ;AACvB,IAAE,EAAE,IAAI;AACR,IAAE,EAAE,IAAI,IAAI,MAAM,OAAO;AACzB,SAAO;AACT;AAGO,SAAS,mBACd,MACA,OACA,QACA,KACA,MACA,KACM;AACN,QAAM,IAAI,IAAI,aAAa,EAAE;AAC7B,IAAE,CAAC,IAAI,KAAK,QAAQ;AACpB,IAAE,CAAC,IAAI,KAAK,MAAM;AAClB,IAAE,EAAE,IAAI,MAAM,MAAM;AACpB,IAAE,EAAE,IAAI,EAAE,QAAQ,SAAS,QAAQ;AACnC,IAAE,EAAE,IAAI,EAAE,MAAM,WAAW,MAAM;AACjC,IAAE,EAAE,IAAI,EAAE,MAAM,SAAS,MAAM;AAC/B,IAAE,EAAE,IAAI;AACR,SAAO;AACT;AAGO,SAAS,SAAS,GAAS,GAAe;AAC/C,QAAM,IAAI,IAAI,aAAa,EAAE;AAC7B,WAAS,MAAM,GAAG,MAAM,GAAG,OAAO;AAChC,UAAM,KAAK,EAAE,MAAM,CAAC;AACpB,UAAM,KAAK,EAAE,MAAM,IAAI,CAAC;AACxB,UAAM,KAAK,EAAE,MAAM,IAAI,CAAC;AACxB,UAAM,KAAK,EAAE,MAAM,IAAI,CAAC;AACxB,aAAS,MAAM,GAAG,MAAM,GAAG,OAAO;AAChC,QAAE,MAAM,IAAI,GAAG,IACb,EAAE,GAAG,IAAK,KACV,EAAE,IAAI,GAAG,IAAK,KACd,EAAE,IAAI,GAAG,IAAK,KACd,EAAE,KAAK,GAAG,IAAK;AAAA,IACnB;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,OAAO,GAAsB;AAC3C,QAAM,MAAM,EAAE,CAAC,GACb,MAAM,EAAE,CAAC,GACT,MAAM,EAAE,CAAC,GACT,MAAM,EAAE,CAAC;AACX,QAAM,MAAM,EAAE,CAAC,GACb,MAAM,EAAE,CAAC,GACT,MAAM,EAAE,CAAC,GACT,MAAM,EAAE,CAAC;AACX,QAAM,MAAM,EAAE,CAAC,GACb,MAAM,EAAE,CAAC,GACT,MAAM,EAAE,EAAE,GACV,MAAM,EAAE,EAAE;AACZ,QAAM,MAAM,EAAE,EAAE,GACd,MAAM,EAAE,EAAE,GACV,MAAM,EAAE,EAAE,GACV,MAAM,EAAE,EAAE;AAEZ,QAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,QAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,QAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,QAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,QAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,QAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,QAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,QAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,QAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,QAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,QAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,QAAM,MAAM,MAAM,MAAM,MAAM;AAE9B,QAAM,MACJ,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM;AACpE,MAAI,KAAK,IAAI,GAAG,IAAI,MAAO,QAAO;AAElC,QAAM,SAAS,IAAI;AACnB,QAAM,IAAI,IAAI,aAAa,EAAE;AAC7B,IAAE,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC7C,IAAE,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC7C,IAAE,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC7C,IAAE,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC7C,IAAE,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC7C,IAAE,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC7C,IAAE,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC7C,IAAE,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC7C,IAAE,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC7C,IAAE,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC7C,IAAE,EAAE,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC9C,IAAE,EAAE,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC9C,IAAE,EAAE,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC9C,IAAE,EAAE,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC9C,IAAE,EAAE,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC9C,IAAE,EAAE,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO;AAC9C,SAAO;AACT;AAGO,SAAS,eAAe,GAAS,GAAe;AACrD,QAAM,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,EAAE,IAAK,EAAE,IAAI,EAAE,EAAE;AACzD,SAAO;AAAA,IACL,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,EAAE,KAAM;AAAA,IACxD,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,EAAE,KAAM;AAAA,IACxD,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,EAAE,IAAK,EAAE,IAAI,EAAE,EAAE,KAAM;AAAA,EAC3D;AACF;AAGO,SAAS,mBAAmB,GAAS,GAAe;AACzD,SAAO;AAAA,IACL,GAAG,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE;AAAA,IACzC,GAAG,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE;AAAA,IACzC,GAAG,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,EAAE,IAAK,EAAE;AAAA,EAC5C;AACF;AAMO,SAAS,kBACd,GACA,GACgD;AAChD,SAAO;AAAA,IACL,GAAG,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,EAAE;AAAA,IACjD,GAAG,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,EAAE;AAAA,IACjD,GAAG,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,EAAE,IAAK,EAAE,IAAI,EAAE,EAAE;AAAA,IAClD,GAAG,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,CAAC,IAAK,EAAE,IAAI,EAAE,EAAE,IAAK,EAAE,IAAI,EAAE,EAAE;AAAA,EACpD;AACF;;;AC/LA,IAAM,aAAa,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE;AAG/B,SAAS,aAAa,SAAmC;AAC9D,SAAO;AAAA,IACL,UAAU,SAAS,YAAY,EAAE,GAAG,GAAG,GAAG,KAAK,GAAG,EAAE;AAAA,IACpD,QAAQ,SAAS,UAAU,EAAE,GAAG,GAAG,GAAG,KAAK,GAAG,IAAI;AAAA,IAClD,IAAI,SAAS,MAAM;AAAA,IACnB,KAAK,SAAS,OAAO;AAAA,IACrB,MAAM,SAAS,QAAQ;AAAA,IACvB,KAAK,SAAS,OAAO;AAAA,IACrB,YAAY,SAAS,cAAc;AAAA,IACnC,YAAY,SAAS;AAAA,EACvB;AACF;AAGO,SAAS,aAAa,QAAgB,SAAkC;AAC7E,SAAO,EAAE,GAAG,QAAQ,GAAG,QAAQ;AACjC;AAGO,SAAS,qBAAqB,QAAgB,UAA0B;AAC7E,QAAM,OAAO,OAAO,OAAO,UAAU,OAAO,QAAQ,OAAO,EAAE;AAC7D,QAAM,SAAS,SAAS,QAAQ,SAAS;AAEzC,MAAI;AACJ,MAAI,OAAO,eAAe,gBAAgB;AACxC,UAAMA,SAAQ,OAAO,cAAc;AACnC,UAAM,QAAQA,SAAQ;AACtB,UAAM,QAAQ,QAAQ;AACtB,WAAO,mBAAmB,CAAC,OAAO,OAAO,CAAC,OAAO,OAAO,OAAO,MAAM,OAAO,GAAG;AAAA,EACjF,OAAO;AACL,WAAO,kBAAkB,OAAO,KAAK,QAAQ,OAAO,MAAM,OAAO,GAAG;AAAA,EACtE;AAEA,SAAO,SAAS,MAAM,IAAI;AAC5B;;;AC5BO,SAAS,QACd,OACA,QACA,UACgB;AAChB,QAAM,KAAK,qBAAqB,QAAQ,QAAQ;AAChD,SAAO,kBAAkB,OAAO,IAAI,QAAQ,QAAQ;AACtD;AAOO,SAAS,kBACd,OACA,UACA,QACA,UACgB;AAChB,QAAM,OAAO,kBAAkB,UAAU,KAAK;AAG9C,MAAI,KAAK,KAAK,GAAG;AACf,WAAO;AAAA,MACL,GAAG;AAAA,MACH,GAAG;AAAA,MACH,OAAO;AAAA,MACP,OAAO;AAAA,MACP,SAAS;AAAA,MACT,UAAU,SAAa,OAAO,OAAO,QAAQ;AAAA,IAC/C;AAAA,EACF;AAGA,QAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,QAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,QAAM,OAAO,KAAK,IAAI,KAAK;AAG3B,QAAM,UAAU,SAAS,KAAM,OAAO,KAAK,IAAK,SAAS;AACzD,QAAM,UAAU,SAAS,KAAM,IAAI,QAAQ,IAAK,SAAS;AAGzD,QAAM,SAAS,OAAO,KAAK;AAG3B,QAAM,UACJ,QAAQ,MAAM,QAAQ,KAAK,QAAQ,MAAM,QAAQ,KAAK,QAAQ,MAAM,QAAQ;AAK9E,QAAM,SAAU,OAAO,MAAM,KAAK,KAAM;AACxC,QAAM,YACJ,OAAO,eAAe,gBACjB,SAAS,SAAS,KAAM,IAAI,KAAK,IAAI,SAAS,CAAC,KAAK,KAAK,IACzD,SAAS,UAAU,OAAO,cAAc;AAE/C,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,IACH;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA,UAAU,SAAa,OAAO,OAAO,QAAQ;AAAA,EAC/C;AACF;AAKO,SAAS,YACd,QACA,QACA,UACkB;AAClB,QAAM,KAAK,qBAAqB,QAAQ,QAAQ;AAChD,SAAO,OAAO,IAAI,CAAC,MAAM,kBAAkB,GAAG,IAAI,QAAQ,QAAQ,CAAC;AACrE;AAYO,SAAS,UACd,SACA,SACA,OACA,QACA,UACa;AACb,QAAM,KAAK,qBAAqB,QAAQ,QAAQ;AAChD,QAAM,MAAM,OAAO,EAAE;AACrB,MAAI,CAAC,IAAK,QAAO;AAGjB,QAAM,QAAS,UAAU,SAAS,KAAK,SAAS,QAAS,IAAI;AAC7D,QAAM,OAAO,KAAM,UAAU,SAAS,KAAK,SAAS,SAAU;AAC9D,QAAM,OAAO,QAAQ,IAAI;AAEzB,QAAM,OAAO,kBAAkB,KAAK,EAAE,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AACjE,MAAI,KAAK,IAAI,KAAK,CAAC,IAAI,MAAO,QAAO;AAErC,SAAO;AAAA,IACL,GAAG,KAAK,IAAI,KAAK;AAAA,IACjB,GAAG,KAAK,IAAI,KAAK;AAAA,IACjB,GAAG,KAAK,IAAI,KAAK;AAAA,EACnB;AACF;;;ACvHO,SAAS,qBAAqB,OAAa,QAAgB,UAA6B;AAC7F,QAAM,KAAK,qBAAqB,QAAQ,QAAQ;AAChD,QAAM,OAAO,kBAAkB,IAAI,KAAK;AACxC,MAAI,KAAK,KAAK,EAAG,QAAO;AACxB,QAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,QAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,QAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,SAAO,QAAQ,MAAM,QAAQ,KAAK,QAAQ,MAAM,QAAQ,KAAK,QAAQ,MAAM,QAAQ;AACrF;AAQO,SAAS,mBACd,KACA,QACA,UACoC;AACpC,QAAM,KAAK,qBAAqB,QAAQ,QAAQ;AAChD,QAAM,UAAkB;AAAA,IACtB,EAAE,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE;AAAA,IAC3C,EAAE,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE;AAAA,IAC3C,EAAE,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE;AAAA,IAC3C,EAAE,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE;AAAA,IAC3C,EAAE,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE;AAAA,IAC3C,EAAE,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE;AAAA,IAC3C,EAAE,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE;AAAA,IAC3C,EAAE,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE;AAAA,EAC7C;AAEA,MAAI,YAAY;AAChB,MAAI,YAAY;AAEhB,aAAW,UAAU,SAAS;AAC5B,UAAM,OAAO,kBAAkB,IAAI,MAAM;AACzC,QAAI,KAAK,KAAK,GAAG;AACf,kBAAY;AACZ;AAAA,IACF;AACA,UAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,UAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,UAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,UAAM,SACJ,QAAQ,MAAM,QAAQ,KAAK,QAAQ,MAAM,QAAQ,KAAK,QAAQ,MAAM,QAAQ;AAC9E,QAAI,QAAQ;AACV,kBAAY;AAAA,IACd,OAAO;AACL,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,MAAI,UAAW,QAAO;AACtB,MAAI,UAAW,QAAO;AAKtB,MAAI,UAAU,UACZ,UAAU;AACZ,MAAI,UAAU,UACZ,UAAU;AACZ,MAAI,cAAc;AAElB,aAAW,UAAU,SAAS;AAC5B,UAAM,OAAO,kBAAkB,IAAI,MAAM;AACzC,QAAI,KAAK,KAAK,GAAG;AACf;AACA;AAAA,IACF;AACA,UAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,UAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,cAAU,KAAK,IAAI,SAAS,IAAI;AAChC,cAAU,KAAK,IAAI,SAAS,IAAI;AAChC,cAAU,KAAK,IAAI,SAAS,IAAI;AAChC,cAAU,KAAK,IAAI,SAAS,IAAI;AAAA,EAClC;AAGA,MAAI,cAAc,KAAK,cAAc,EAAG,QAAO;AAG/C,MAAI,gBAAgB,EAAG,QAAO;AAG9B,MAAI,UAAU,MAAM,UAAU,KAAK,UAAU,MAAM,UAAU,EAAG,QAAO;AACvE,SAAO;AACT;AAMO,SAAS,WAAW,YAAkB,YAAkB,QAAyB;AACtF,QAAM,WAAW,IAAI,OAAO,UAAU,UAAU;AAChD,SAAO,IAAI,YAAY,QAAQ,KAAK;AACtC;AAMO,SAAS,aACd,YACA,QACA,UACQ;AACR,QAAM,KAAK,qBAAqB,QAAQ,QAAQ;AAChD,QAAM,OAAO,kBAAkB,IAAI,UAAU;AAC7C,MAAI,KAAK,KAAK,EAAG,QAAO;AAExB,MAAI,OAAO,eAAe,eAAe;AACvC,UAAM,SAAU,OAAO,MAAM,KAAK,KAAM;AACxC,WAAQ,SAAS,SAAS,KAAM,IAAI,KAAK,IAAI,SAAS,CAAC,KAAK,KAAK;AAAA,EACnE;AACA,SAAO,SAAS,UAAU,OAAO,cAAc;AACjD;AAKO,SAAS,gBAAgB,OAAa,QAAgB,UAA4B;AACvF,QAAM,KAAK,qBAAqB,QAAQ,QAAQ;AAChD,QAAM,OAAO,kBAAkB,IAAI,KAAK;AACxC,MAAI,KAAK,KAAK,EAAG,QAAO;AACxB,QAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,UAAQ,OAAO,KAAK;AACtB;AAKO,SAAS,WAAW,OAAa,QAAwB;AAC9D,SAAO,SAAa,OAAO,OAAO,QAAQ;AAC5C;AAOO,SAAS,eAAe,QAAgB,UAA4B;AAIzE,QAAM,UAAU,UAAU,IAAI,OAAO,QAAQ,OAAO,QAAQ,CAAC;AAG7D,QAAM,WAAW,UAAU,EAAE,GAAG,QAAQ,GAAG,GAAG,GAAG,GAAG,QAAQ,EAAE,CAAC;AAC/D,MAAI,SAAS,MAAM,KAAK,SAAS,MAAM,GAAG;AAExC,WAAO,SAAS,IAAI,SAAS,SAAS;AAAA,EACxC;AAEA,QAAM,WAAiB;AAAA,IACrB,GAAG,OAAO,SAAS,IAAI,SAAS,IAAI,OAAO,MAAM;AAAA,IACjD,GAAG,OAAO,SAAS;AAAA,IACnB,GAAG,OAAO,SAAS,IAAI,SAAS,IAAI,OAAO,MAAM;AAAA,EACnD;AAEA,QAAM,KAAK,qBAAqB,QAAQ,QAAQ;AAChD,QAAM,OAAO,kBAAkB,IAAI,QAAQ;AAC3C,MAAI,KAAK,KAAK,EAAG,QAAO,SAAS,IAAI,SAAS,SAAS;AAEvD,QAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,SAAO,SAAS,KAAM,IAAI,QAAQ,IAAK,SAAS;AAClD;AAWO,SAAS,sBACd,QACA,UACgD;AAChD,QAAM,KAAK,qBAAqB,QAAQ,QAAQ;AAIhD,QAAM,MAAM,OAAO,MAAM;AAEzB,QAAM,aAAa,CAAC,QAAgC;AAClD,UAAM,QAAc;AAAA,MAClB,GAAG,OAAO,SAAS,IAAI,IAAI,IAAI;AAAA,MAC/B,GAAG,OAAO,SAAS,IAAI,IAAI,IAAI;AAAA,MAC/B,GAAG,OAAO,SAAS,IAAI,IAAI,IAAI;AAAA,IACjC;AACA,UAAM,OAAO,kBAAkB,IAAI,KAAK;AACxC,QAAI,KAAK,KAAK,EAAG,QAAO;AACxB,UAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,UAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,WAAO;AAAA,MACL,GAAG,SAAS,KAAM,OAAO,KAAK,IAAK,SAAS;AAAA,MAC5C,GAAG,SAAS,KAAM,IAAI,QAAQ,IAAK,SAAS;AAAA,IAC9C;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,WAAW,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE,CAAC;AAAA,IACrC,OAAO,WAAW,EAAE,GAAG,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC;AAAA,IACvC,UAAU,WAAW,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE,CAAC;AAAA,EAC3C;AACF;;;ACnNO,SAAS,UACd,OACA,YACA,QACK;AACL,QAAM,SAAS,OAAO;AACtB,SAAO,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM;AAC/B,UAAM,KAAK,WAAW,WAAW,CAAC,GAAG,MAAM;AAC3C,UAAM,KAAK,WAAW,WAAW,CAAC,GAAG,MAAM;AAC3C,WAAO,KAAK;AAAA,EACd,CAAC;AACH;AAKO,SAAS,iBAAiB,WAAmB,QAA0B;AAC5E,QAAM,SAAS,OAAO;AACtB,QAAM,UAAU,UAAU,IAAI,CAAC,GAAG,MAAM,CAAC;AACzC,UAAQ,KAAK,CAAC,GAAG,MAAM;AACrB,UAAM,KAAK,UAAU,CAAC;AACtB,UAAM,KAAK,UAAU,CAAC;AACtB,WAAO,WAAW,IAAI,MAAM,IAAI,WAAW,IAAI,MAAM;AAAA,EACvD,CAAC;AACD,SAAO;AACT;;;ACzBO,SAAS,gBAAgB,SAKrB;AACT,QAAM,YAAY,SAAS,aAAa;AACxC,QAAM,eAAe,SAAS,gBAAgB;AAC9C,QAAM,gBAAgB,SAAS,iBAAiB;AAChD,SAAO,aAAa;AAAA,IAClB,UAAU,EAAE,GAAG,GAAG,GAAG,WAAW,GAAG,EAAE;AAAA,IACrC,QAAQ,EAAE,GAAG,GAAG,GAAG,eAAe,GAAG,aAAa;AAAA,IAClD,KAAK,SAAS,OAAO;AAAA,EACvB,CAAC;AACH;AAMO,SAAS,aAAa,SAIlB;AACT,QAAM,WAAW,SAAS,YAAY;AACtC,QAAM,WAAW,SAAS,aAAa;AACvC,QAAM,WAAY,WAAW,KAAK,KAAM;AACxC,QAAM,eAAe,WAAW,KAAK,IAAI,QAAQ;AACjD,SAAO,aAAa;AAAA,IAClB,UAAU,EAAE,GAAG,GAAG,GAAG,UAAU,GAAG,EAAE;AAAA,IACpC,QAAQ,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,aAAa;AAAA,IACtC,KAAK,SAAS,OAAO;AAAA,EACvB,CAAC;AACH;AAMO,SAAS,aAAa,SAIlB;AACT,QAAM,YAAY,SAAS,aAAa;AACxC,QAAM,WAAW,SAAS,iBAAiB;AAC3C,QAAM,WAAY,WAAW,KAAK,KAAM;AACxC,QAAM,WAAW;AACjB,SAAO,aAAa;AAAA,IAClB,UAAU,EAAE,GAAG,GAAG,GAAG,WAAW,GAAG,EAAE;AAAA,IACrC,QAAQ;AAAA,MACN,GAAG,KAAK,IAAI,QAAQ,IAAI;AAAA,MACxB,GAAG,YAAY;AAAA,MACf,GAAG,KAAK,IAAI,QAAQ,IAAI;AAAA,IAC1B;AAAA,IACA,KAAK,SAAS,OAAO;AAAA,EACvB,CAAC;AACH;AAMO,SAAS,cAAc,SAInB;AACT,QAAM,cAAc,SAAS,eAAe;AAC5C,QAAM,UAAU,SAAS,aAAa;AACtC,QAAM,UAAW,UAAU,KAAK,KAAM;AACtC,QAAM,WAAW;AACjB,SAAO,aAAa;AAAA,IAClB,UAAU,EAAE,GAAG,GAAG,GAAG,aAAa,GAAG,EAAE;AAAA,IACvC,QAAQ;AAAA,MACN,GAAG;AAAA,MACH,GAAG,cAAc,KAAK,IAAI,OAAO,IAAI;AAAA,MACrC,GAAG,KAAK,IAAI,OAAO,IAAI;AAAA,IACzB;AAAA,IACA,KAAK,SAAS,OAAO;AAAA,EACvB,CAAC;AACH;AAMO,SAAS,gBAAgB,SAIrB;AACT,QAAM,WAAW,SAAS,SAAS;AACnC,QAAM,WAAY,WAAW,KAAK,KAAM;AACxC,QAAM,OAAO,SAAS,YAAY;AAClC,SAAO,aAAa;AAAA,IAClB,UAAU;AAAA,MACR,GAAG,CAAC,KAAK,IAAI,QAAQ,IAAI;AAAA,MACzB,GAAG,KAAK,IAAI,QAAQ,IAAI,OAAO,OAAO;AAAA,MACtC,GAAG,CAAC,KAAK,IAAI,QAAQ,IAAI;AAAA,IAC3B;AAAA,IACA,QAAQ,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE;AAAA,IAC3B,YAAY;AAAA,IACZ,YAAY,SAAS,cAAc;AAAA,EACrC,CAAC;AACH;;;AC5GO,SAAS,WAAW,GAAW,GAAW,GAAmB;AAClE,SAAO;AAAA,IACL,UAAU,KAAS,EAAE,UAAU,EAAE,UAAU,CAAC;AAAA,IAC5C,QAAQ,KAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,IACtC,IAAI,UAAU,KAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAAA,IACrC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO;AAAA,IAC/B,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ;AAAA,IACnC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO;AAAA,IAC/B,YAAY,IAAI,MAAM,EAAE,aAAa,EAAE;AAAA,IACvC,YACE,EAAE,cAAc,QAAQ,EAAE,cAAc,OACpC,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,IAC9C,IAAI,MAAM,EAAE,aAAa,EAAE;AAAA,EACpC;AACF;AAUO,SAAS,YACd,QACA,QACA,QACA,QACQ;AACR,QAAM,QAAQ,UAAU,OAAO;AAC/B,QAAM,SAAS,IAAI,OAAO,UAAU,KAAK;AAGzC,QAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,QAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,MAAI,UAAgB;AAAA,IAClB,GAAG,OAAO,IAAI,OAAO,OAAO,IAAI;AAAA,IAChC,GAAG,OAAO;AAAA,IACV,GAAG,CAAC,OAAO,IAAI,OAAO,OAAO,IAAI;AAAA,EACnC;AAGA,QAAM,UAAU,UAAU,IAAI,OAAO,IAAI,OAAO,OAAO,CAAC,CAAC;AACzD,QAAM,QAAQ,UAAU,MAAM,OAAO,IAAI,OAAO,CAAC;AACjD,QAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,QAAM,OAAO,KAAK,IAAI,MAAM;AAG5B,QAAM,QAAQ,MAAM,IAAI,QAAQ,IAAI,MAAM,IAAI,QAAQ,IAAI,MAAM,IAAI,QAAQ;AAC5E,QAAM,UAAU,MAAM,OAAO,OAAO;AACpC,YAAU;AAAA,IACR,GAAG,QAAQ,IAAI,OAAO,QAAQ,IAAI,OAAO,MAAM,IAAI,SAAS,IAAI;AAAA,IAChE,GAAG,QAAQ,IAAI,OAAO,QAAQ,IAAI,OAAO,MAAM,IAAI,SAAS,IAAI;AAAA,IAChE,GAAG,QAAQ,IAAI,OAAO,QAAQ,IAAI,OAAO,MAAM,IAAI,SAAS,IAAI;AAAA,EAClE;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU,IAAI,OAAO,OAAO;AAAA,EAC9B;AACF;AAQO,SAAS,YAAY,QAAgBC,WAA0B;AACpE,QAAM,MAAM,UAAU,IAAI,OAAO,QAAQ,OAAO,QAAQ,CAAC;AACzD,QAAM,OAAO,MAAM,KAAKA,SAAQ;AAChC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU,IAAI,OAAO,UAAU,IAAI;AAAA,IACnC,QAAQ,IAAI,OAAO,QAAQ,IAAI;AAAA,EACjC;AACF;AASO,SAAS,UAAU,QAAgB,IAAY,IAAoB;AACxE,QAAM,UAAU,UAAU,IAAI,OAAO,QAAQ,OAAO,QAAQ,CAAC;AAC7D,QAAM,QAAQ,UAAU,MAAM,OAAO,IAAI,OAAO,CAAC;AACjD,QAAM,KAAK,MAAM,SAAS,KAAK;AAC/B,QAAM,SAAS,IAAI,MAAM,OAAO,EAAE,GAAG,MAAM,IAAI,EAAE,CAAC;AAClD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU,IAAI,OAAO,UAAU,MAAM;AAAA,IACrC,QAAQ,IAAI,OAAO,QAAQ,MAAM;AAAA,EACnC;AACF;","names":["scale","distance"]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@genart-dev/projection",
3
+ "version": "0.1.0",
4
+ "description": "Camera projection math for genart.dev — 3D-to-2D projection, frustum culling, depth sorting, camera presets",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "license": "MIT",
25
+ "author": {
26
+ "name": "genart.dev",
27
+ "email": "support@genart.dev",
28
+ "url": "https://genart.dev"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/genart-dev/projection.git"
33
+ },
34
+ "homepage": "https://genart.dev",
35
+ "keywords": [
36
+ "genart",
37
+ "generative-art",
38
+ "projection",
39
+ "camera",
40
+ "3d",
41
+ "perspective",
42
+ "matrix"
43
+ ],
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup",
49
+ "postbuild": "node -e \"const fs=require('fs'); const f='dist/index.d.ts'; if(!fs.existsSync(f)){console.error('ERROR: '+f+' missing — publish would break downstream builds'); process.exit(1)} console.log('Verified: DTS files present')\"",
50
+ "test": "vitest run",
51
+ "test:watch": "vitest",
52
+ "clean": "rm -rf dist",
53
+ "prepublishOnly": "pnpm build"
54
+ },
55
+ "devDependencies": {
56
+ "tsup": "^8.3.6",
57
+ "typescript": "^5.7.3",
58
+ "vitest": "^3.0.5"
59
+ }
60
+ }