import type { Vector3 } from '@holoscript/core'; import type { IPhysicsWorld } from './PhysicsTypes'; /** * JointSystem.ts * * Physics joint types: hinge, ball, slider, spring, distance, fixed. * Constraint solving, breakable joints, and motor forces. * * @module physics */ export type JointType = 'hinge' | 'ball' | 'slider' | 'spring' | 'distance' | 'fixed'; export interface JointDef { id: string; type: JointType; bodyA: string; bodyB: string; anchorA: Vector3; anchorB: Vector3; axis?: Vector3; limits?: { min: number; max: number; }; breakForce: number; stiffness: number; damping: number; motorSpeed: number; motorForce: number; broken: boolean; enabled: boolean; /** Stored rest length for distance joints (set at creation from anchor separation). */ restLength: number; } export interface JointState { currentForce: number; currentAngle: number; currentDistance: number; /** Previous distance used to estimate relative velocity for spring damping. */ previousDistance: number; } export declare class JointSystem { private joints; private states; private bodyJoints; createJoint(type: JointType, bodyA: string, bodyB: string, config?: Partial): JointDef; removeJoint(id: string): boolean; solve(dt: number): void; /** * Couple computed joint forces into the rigid-body world (zgcn). * * `solve()` computes only a scalar `currentForce` magnitude in a vacuum — it * reads neither body positions nor applies anything, so on its own a joint * moves no body (the structural gap audited 2026-06-10). This method closes * the loop for the LINEAR constraint laws (spring, distance): for each enabled * such joint it reads the live positions/velocities of bodyA and bodyB, * computes the constraint force VECTOR along the A→B axis (Hooke restoring term * + axial damping that opposes the separation rate), and applies equal and * opposite forces to the two bodies via the world. * * Force on A points toward B when the bodies are stretched beyond rest length * (pulling them together) and away when compressed; B receives the negation — * so the pair conserves linear momentum. Coincident bodies (no defined axis) * are skipped. Hinge/slider angular DOFs are not force-coupled here — their * rotational constraint is the ConstraintSolver's responsibility; this method * applies exactly the linear law that `currentForce` represents. * * Call once per substep before `world.step(dt)`. Updates joint state * (currentDistance, previousDistance, currentForce) and the break check, so it * subsumes `solve()` for spring/distance joints when a world is present. * * @param world Rigid-body world exposing getBody / applyForce by body id. * @param dt Substep duration (seconds); reserved for state bookkeeping. */ applyForcesToWorld(world: IPhysicsWorld, _dt: number): void; setMotor(id: string, speed: number, force: number): void; setEnabled(id: string, enabled: boolean): void; setAngle(id: string, angle: number): void; setDistance(id: string, dist: number): void; getJoint(id: string): JointDef | undefined; getState(id: string): JointState | undefined; getJointCount(): number; getBrokenJoints(): JointDef[]; getJointsForBody(bodyId: string): JointDef[]; private distance3D; } //# sourceMappingURL=JointSystem.d.ts.map