/** All physics values use SI units. No rendering or localization dependencies. */ export const VACUUM_PERMITTIVITY = 8.8541878128e-12; export const COULOMB_CONSTANT = 1 / (4 * Math.PI * VACUUM_PERMITTIVITY); export type Vector3 = Readonly<{ x: number; y: number; z: number }>; export type SurfaceShape = 'sphere' | 'cube'; export type Membership = 'inside' | 'outside' | 'boundary'; export interface ClosedSurface { shape: SurfaceShape; /** Sphere radius or cube half side length, in metres. Center is the origin. */ size: number; } export interface PointCharge { coulombs: number; position: Vector3; } export function surfaceMembership(position: Vector3, surface: ClosedSurface): Membership { const distance = surface.shape === 'sphere' ? Math.hypot(position.x, position.y, position.z) : Math.max(Math.abs(position.x), Math.abs(position.y), Math.abs(position.z)); // Absorb floating-point slider arithmetic without hiding a physical boundary region. if (Math.abs(distance - surface.size) < 1e-9) return 'boundary'; return distance < surface.size ? 'inside' : 'outside'; } /** Returns null at the singular source of a nonzero ideal point charge. */ export function electricFieldAt(point: Vector3, charge: PointCharge): Vector3 | null { if (charge.coulombs === 0) return { x: 0, y: 0, z: 0 }; const x = point.x - charge.position.x; const y = point.y - charge.position.y; const z = point.z - charge.position.z; const r = Math.hypot(x, y, z); if (r === 0) return null; const scale = COULOMB_CONSTANT * charge.coulombs / r ** 3; return { x: x * scale, y: y * scale, z: z * scale }; } export function measureFlux(charge: PointCharge, surface: ClosedSurface) { const membership = surfaceMembership(charge.position, surface); const area = surface.shape === 'sphere' ? 4 * Math.PI * surface.size ** 2 : 24 * surface.size ** 2; const enclosedCharge = charge.coulombs === 0 ? 0 : membership === 'boundary' ? null : membership === 'inside' ? charge.coulombs : 0; return { membership, area, enclosedCharge, netFlux: enclosedCharge === null ? null : enclosedCharge / VACUUM_PERMITTIVITY, }; }