aboutsummaryrefslogtreecommitdiff
path: root/src/models/electric-flux/physics.ts
diff options
context:
space:
mode:
authorvictor2026-10-24 15:57:01 +0200
committervictor2026-10-24 15:57:01 +0200
commit4533910bce394e7a0ee6debfc05ca1221ee51899 (patch)
tree73aaa5b2c7a48dbf226e6b81d499ba1a8ecc33a1 /src/models/electric-flux/physics.ts
parentTilføj dansk (diff)
download3D-Electrophysics-4533910bce394e7a0ee6debfc05ca1221ee51899.tar.gz
3D-Electrophysics-4533910bce394e7a0ee6debfc05ca1221ee51899.zip
Astra!ny-start
Diffstat (limited to '')
-rw-r--r--src/models/electric-flux/physics.ts53
1 files changed, 53 insertions, 0 deletions
diff --git a/src/models/electric-flux/physics.ts b/src/models/electric-flux/physics.ts
new file mode 100644
index 0000000..78c21d0
--- /dev/null
+++ b/src/models/electric-flux/physics.ts
@@ -0,0 +1,53 @@
+/** 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,
+ };
+}