import type { GraphNodeState } from './graph.types'; export interface GraphCamera { /** Rotation about the vertical axis, in radians. */ yaw: number; /** Rotation above and below the horizon, in radians. Clamped near the poles. */ pitch: number; /** Distance from the origin. Smaller is closer. */ distance: number; /** Perspective strength. Larger flattens toward an orthographic view. */ focalLength: number; panX: number; panY: number; } export declare const DEFAULT_CAMERA: GraphCamera; /** Just short of the poles, where yaw would gimbal-lock and the drag flip. */ export declare const MAX_PITCH: number; export interface ProjectedPoint { x: number; y: number; /** Camera-space depth. Larger is further away. */ depth: number; /** Perspective scale factor: 1 at the focal plane, smaller further off. */ scale: number; /** False when the point sits behind the camera and must not be drawn. */ visible: boolean; } /** * Projects a layout coordinate to canvas pixels. * * @description * A graph needs no mesh, material or shader — only points and lines — so the * whole 3D pipeline is this function plus a depth sort. That is the reason a * WebGL dependency buys so little here: it would replace the easy half and leave * the force simulation, which has to be written either way. */ export declare function project(point: { x: number; y: number; z: number; }, camera: GraphCamera, width: number, height: number): ProjectedPoint; /** Fades and shrinks distant nodes so depth reads without stereo cues. */ export declare function depthFade(depth: number, camera: GraphCamera): number; /** Camera distance that frames every node with a little margin. */ export declare function fitDistance(states: GraphNodeState[], camera: GraphCamera, width: number, height: number): number; /** Applies a drag as a rotation, with pitch clamped away from the poles. */ export declare function orbit(camera: GraphCamera, deltaX: number, deltaY: number): GraphCamera; /** Applies a wheel notch as a zoom, multiplicative so it feels even at any distance. */ export declare function zoom(camera: GraphCamera, delta: number): GraphCamera; /** Finds the topmost node under a canvas point. */ export declare function hitTest(states: GraphNodeState[], radii: number[], camera: GraphCamera, width: number, height: number, pointerX: number, pointerY: number): number; /** * Uniform factor that keeps the largest node under a pixel ceiling. * * @description * Node radius is in layout units, so a sparse graph framed to fill the canvas * renders enormous discs while a dense one renders specks. Capping each node * individually would flatten the weight differences that make a graph readable, * so a single factor is applied to all of them: the biggest node lands on the * ceiling and every other keeps its proportion. * * Measured at the origin, so per-node depth still varies size and the depth cue * survives. */ export declare function nodeRadiusScale(radii: number[], camera: GraphCamera, maxRadiusPx: number): number;