import type { Box3Like } from './box3'; import type { Mat4Like, Vec2, Vec3, Vec3Like } from './types'; /** * Result of a ray-triangle intersection test (Moller-Trumbore). * `t` = ray parameter, `u`/`v` = barycentric coordinates. */ export interface RayTriResult { hit: boolean; t: number; u: number; v: number; } /** * Ray storage: Float32Array length 6 [ox, oy, oz, dx, dy, dz], direction normalized. * Local brand (not part of the seven-piece SSOT; same rationale as Box3). */ export type Ray = Float32Array & { readonly __ray: void; }; /** * Ray readable input: ArrayLike of length 6 (ordering identical to Ray). */ export type RayLike = ArrayLike; /** * Create a Ray. Defaults to origin=(0,0,0), direction=(0,0,-1) normalized. * * When `out` is not provided, a new Float32Array is allocated. * When `origin` / `direction` are provided, direction is normalized * (zero-length direction falls back to (0,0,0) per gl-matrix style). */ export declare function create(out?: Float32Array, origin?: Vec3Like, direction?: Vec3Like): Ray; /** * Copy the origin of `r` into `out`. Returns `out`. */ export declare function getOrigin(out: Vec3, r: RayLike): Vec3; /** * Copy the direction of `r` into `out`. Returns `out`. */ export declare function getDirection(out: Vec3, r: RayLike): Vec3; /** * Overwrite the origin of `r` with `o`. Returns `r`. */ export declare function setOrigin(r: Ray, o: Vec3Like): Ray; /** * Overwrite the direction of `r` with `d` and normalize it. * Zero-length direction falls back to (0,0,0) (gl-matrix style). * Returns `r`. */ export declare function setDirection(r: Ray, d: Vec3Like): Ray; /** * Result of a ray-AABB intersection test. */ export interface RayAabbResult { hit: boolean; /** Entry distance along the ray (clamped to 0 when origin is inside the box). */ tmin: number; } /** * Test whether a ray intersects an axis-aligned bounding box using the * slab (Kay-Kajiya) method. * * Six degenerate cases per research Finding 2: * 1. hit from outside → `tmin > 0` * 2. hit from inside → `tmin = 0` * 3. miss (ray away) → `hit = false` * 4. parallel-axis miss → natural via ±Inf propagation * 5. edge/corner NaN-safe → guard `t1 > t2` NaN scenarios with `Number.isNaN` * 6. thin box (1D/2D) → correct via interval shrink * * Algorithm: precompute `inv = 1/D`, use `inv[i] < 0` (not direction[i] < 0) * to handle `-0` direction correctly. On each axis compute `(min - O)*inv` and * `(max - O)*inv`, swap when `inv < 0`, then accumulate `t_near = max(t_near, t1)`, * `t_far = min(t_far, t2)`. Hit when `t_far >= max(t_near, 0)`. * * Input: `r` (Ray with normalized direction) + `aabb` (Box3Like, 6 floats). * Output: `{ hit: boolean, tmin: number }`. */ export declare function rayAabbIntersects(r: RayLike, aabb: Box3Like): RayAabbResult; /** * Project a screen-space coordinate into a world-space Ray. * * `screenX` / `screenY` are in pixels relative to the top-left corner of the viewport * (DOM convention: y-down). They are clamped to `[0, vpWidth]` / `[0, vpHeight]` and * sanitized against NaN/Inf. * * `kind` discriminates the projection type so callers can branch on the result if needed; * both paths use the same two-point unproject algorithm. * * @param out The Ray to write into. * @param screenX Horizontal pixel coordinate (y-down origin, top-left). * @param screenY Vertical pixel coordinate. * @param vpWidth Viewport width in pixels. * @param vpHeight Viewport height in pixels. * @param view Camera view matrix (world→eye). * @param proj Camera projection matrix (eye→clip). * @param kind `'perspective'` or `'orthographic'`. * @returns `out` (same Ray instance). */ export declare function screenToRay(out: Ray, screenX: number, screenY: number, vpWidth: number, vpHeight: number, view: Mat4Like, proj: Mat4Like, _kind: 'perspective' | 'orthographic'): Ray; /** Result of projecting a world-space point to screen-space pixel coordinates. */ export interface WorldToScreenResult { /** True if the NDC coordinates are within clip-space bounds. */ onScreen: boolean; /** True if the original world-space point is behind the camera (w < 0). * When true, `out` is meaningless (requirements §7). */ behind: boolean; } /** * Project a world-space point to screen-space pixel coordinates. * * Performs a full mat4 * vec4 internally to capture the pre-divide `w` component * (projectPoint discards `w`, so it cannot report `behind`). * * Pixel mapping: top-left origin, y-down (DOM convention). * px = (ndc.x * 0.5 + 0.5) * canvasW * py = (1 - (ndc.y * 0.5 + 0.5)) * canvasH * * Degenerate viewport (canvasW <= 0 || canvasH <= 0): returns { onScreen: false, behind: false }, * leaves `out` untouched. * * @param out Vec2 to write pixel coordinates into (y-down, top-left origin). * @param worldPos World-space point (3 floats). * @param viewProj Combined view-projection matrix (proj * view, 16 floats). * @param canvasW Canvas width in pixels. * @param canvasH Canvas height in pixels. * @returns { onScreen: boolean, behind: boolean } — pure data flags, no allocation. */ export declare function worldToScreen(out: Vec2, worldPos: Vec3Like, viewProj: Mat4Like, canvasW: number, canvasH: number): WorldToScreenResult; /** * Test whether a ray intersects a triangle using the Moller-Trumbore algorithm. * * Double-sided: both front and back faces are checked (abs(det) < epsilon). * Degenerate triangles (collinear / zero-area) return hit=false. * Ray origin behind the triangle (t <= 0) is rejected. * NaN inputs are guarded against — any NaN input returns hit=false. * * @param r Ray (RayLike, 6 floats: ox,oy,oz,dx,dy,dz with normalized direction). * @param a Triangle vertex A (Vec3Like, 3 floats). * @param b Triangle vertex B (Vec3Like, 3 floats). * @param c Triangle vertex C (Vec3Like, 3 floats). * @returns RayTriResult with hit flag, t (ray parameter), and u/v (barycentric coordinates). */ export declare function rayTriangleIntersects(r: RayLike, a: Vec3Like, b: Vec3Like, c: Vec3Like): RayTriResult; //# sourceMappingURL=ray.d.ts.map