moon-sighting-dart/lib/src/math.dart
2026-03-08 13:07:24 -04:00

65 lines
1.7 KiB
Dart

/// Vector math and angle utilities.
///
/// All computation is pure (no I/O, no state).
library;
import 'dart:math' as math;
/// Degrees to radians conversion factor.
const double deg2rad = math.pi / 180;
/// Radians to degrees conversion factor.
const double rad2deg = 180 / math.pi;
/// A three-element vector (position or velocity).
typedef Vec3 = (double, double, double);
/// Add two 3-vectors.
Vec3 vadd(Vec3 a, Vec3 b) => (a.$1 + b.$1, a.$2 + b.$2, a.$3 + b.$3);
/// Subtract b from a.
Vec3 vsub(Vec3 a, Vec3 b) => (a.$1 - b.$1, a.$2 - b.$2, a.$3 - b.$3);
/// Scale a 3-vector.
Vec3 vscale(Vec3 a, double s) => (a.$1 * s, a.$2 * s, a.$3 * s);
/// Dot product.
double vdot(Vec3 a, Vec3 b) => a.$1 * b.$1 + a.$2 * b.$2 + a.$3 * b.$3;
/// Euclidean norm.
double vnorm(Vec3 a) => math.sqrt(vdot(a, a));
/// Cross product.
Vec3 vcross(Vec3 a, Vec3 b) => (
a.$2 * b.$3 - a.$3 * b.$2,
a.$3 * b.$1 - a.$1 * b.$3,
a.$1 * b.$2 - a.$2 * b.$1,
);
/// Unit vector (normalized). Throws if zero vector.
Vec3 vunit(Vec3 a) {
final n = vnorm(a);
if (n == 0) throw RangeError('Cannot normalize a zero vector');
return vscale(a, 1 / n);
}
/// Angular separation between two direction vectors in radians.
double angularSep(Vec3 a, Vec3 b) {
final cosAngle = (vdot(vunit(a), vunit(b))).clamp(-1.0, 1.0);
return math.acos(cosAngle);
}
/// Normalize an angle to [0, 2*pi).
double mod2pi(double angle) {
const twoPi = 2 * math.pi;
return ((angle % twoPi) + twoPi) % twoPi;
}
/// Normalize an angle in degrees to [0, 360).
double mod360(double deg) => ((deg % 360) + 360) % 360;
/// Normalize an angle in degrees to [-180, 180).
double normalizeDeg180(double deg) {
deg = mod360(deg);
return deg >= 180 ? deg - 360 : deg;
}