Changed how Scalar is generated to use Dimensions.ArgOpts and Scales.ArgOpts

This commit is contained in:
adrien 2026-04-22 16:31:07 +02:00
parent a2d46e3f55
commit 82bdb96746
5 changed files with 174 additions and 128 deletions

View File

@ -5,22 +5,32 @@ const Dimensions = @import("Dimensions.zig");
const Scales = @import("Scales.zig"); const Scales = @import("Scales.zig");
const Scalar = @import("Scalar.zig").Scalar; const Scalar = @import("Scalar.zig").Scalar;
/// Helper function to create a clean namespace for each physical dimension. fn PhysicalConstant(comptime d: Dimensions.ArgOpts, comptime val: f64, comptime s: Scales.ArgOpts) type {
/// It exposes the raw dimensions, and easy type-creators for Base or Scaled variants.
pub fn BaseScalar(comptime d: anytype) type {
return struct { return struct {
pub const dims = Dimensions.init(d); const dims = Dimensions.init(d);
const scales = Scales.init(s);
/// Instantiates the constant into a specific numeric type.
pub fn Of(comptime T: type) Scalar(T, d, s) {
return .{ .value = @as(T, @floatCast(val)) };
}
};
}
fn BaseScalar(comptime d: Dimensions.ArgOpts) type {
return struct {
const dims = Dimensions.init(d);
/// Creates a Scalar of this dimension using default scales. /// Creates a Scalar of this dimension using default scales.
/// Example: const V = Quantities.Velocity.Base(f32); /// Example: const V = Quantities.Velocity.Base(f32);
pub fn Of(comptime T: type) type { pub fn Of(comptime T: type) type {
return Scalar(T, dims, Scales.init(.{})); return Scalar(T, d, .{});
} }
/// Creates a Scalar of this dimension using custom scales. /// Creates a Scalar of this dimension using custom scales.
/// Example: const Kmh = Quantities.Velocity.Scaled(f32, Scales.init(.{ .L = .k, .T = .hour })); /// Example: const Kmh = Quantities.Velocity.Scaled(f32, Scales.init(.{ .L = .k, .T = .hour }));
pub fn Scaled(comptime T: type, comptime s: Scales) type { pub fn Scaled(comptime T: type, comptime s: Scales.ArgOpts) type {
return Scalar(T, dims, s); return Scalar(T, d, s);
} }
}; };
} }
@ -95,6 +105,31 @@ pub const Frequency = BaseScalar(.{ .T = -1 });
pub const Viscosity = BaseScalar(.{ .M = 1, .L = -1, .T = -1 }); pub const Viscosity = BaseScalar(.{ .M = 1, .L = -1, .T = -1 });
pub const SurfaceTension = BaseScalar(.{ .M = 1, .T = -2 }); // Corrected from MT-2a pub const SurfaceTension = BaseScalar(.{ .M = 1, .T = -2 }); // Corrected from MT-2a
// ==========================================
// Physical Constants
// ==========================================
pub const Constants = struct {
/// Speed of light in vacuum
pub const c = Speed.Constant(299792458.0, Scales.init(.{}));
/// Standard gravity
pub const g = Acceleration.Constant(9.80665, Scales.init(.{}));
/// Newton's Gravitational Constant: L³ M¹ T²
pub const GravitationalConstant = PhysicalConstant(
.{ .L = 3, .M = -1, .T = -2 },
6.67430e-11,
Scales.init(.{}),
);
/// Planck Constant: M L² T¹
pub const Planck = PhysicalConstant(
.{ .M = 1, .L = 2, .T = -1 },
6.62607015e-34,
Scales.init(.{}),
);
};
test "BaseQuantities - Core dimensions instantiation" { test "BaseQuantities - Core dimensions instantiation" {
// Basic types via generic wrappers // Basic types via generic wrappers
const M = Meter.Of(f32); const M = Meter.Of(f32);
@ -104,7 +139,7 @@ test "BaseQuantities - Core dimensions instantiation" {
try std.testing.expectEqual(0, M.dims.get(.T)); try std.testing.expectEqual(0, M.dims.get(.T));
// Test specific scale variants // Test specific scale variants
const Kmh = Speed.Scaled(f32, Scales.init(.{ .L = .k, .T = .hour })); const Kmh = Speed.Scaled(f32, .{ .L = .k, .T = .hour });
const speed = Kmh{ .value = 120.0 }; const speed = Kmh{ .value = 120.0 };
try std.testing.expectEqual(120.0, speed.value); try std.testing.expectEqual(120.0, speed.value);
try std.testing.expectEqual(.k, @TypeOf(speed).scales.get(.L)); try std.testing.expectEqual(.k, @TypeOf(speed).scales.get(.L));
@ -128,7 +163,7 @@ test "BaseQuantities - Kinematics equations" {
test "BaseQuantities - Dynamics (Force and Work)" { test "BaseQuantities - Dynamics (Force and Work)" {
// 10 kg // 10 kg
const m = Gramm.Scaled(f32, Scales.init(.{ .M = .k })){ .value = 10.0 }; const m = Gramm.Scaled(f32, .{ .M = .k }){ .value = 10.0 };
// 9.8 m/s^2 // 9.8 m/s^2
const a = Acceleration.Of(f32){ .value = 9.8 }; const a = Acceleration.Of(f32){ .value = 9.8 };

View File

@ -1,5 +1,15 @@
const std = @import("std"); const std = @import("std");
pub const ArgOpts = struct {
L: comptime_int = 0,
M: comptime_int = 0,
T: comptime_int = 0,
I: comptime_int = 0,
Tp: comptime_int = 0,
N: comptime_int = 0,
J: comptime_int = 0,
};
pub const Dimension = enum { pub const Dimension = enum {
/// Length /// Length
L, L,
@ -37,9 +47,9 @@ const Self = @This();
data: std.EnumArray(Dimension, comptime_int), data: std.EnumArray(Dimension, comptime_int),
/// Create a `Dimensions` from an anonymous struct literal, e.g. `.{ .L = 1, .T = -1 }`. /// Create a `Dimensions` from a struct literal, e.g. `.{ .L = 1, .T = -1 }`.
/// Unspecified dimensions default to 0. /// Unspecified dimensions default to 0.
pub fn init(comptime init_val: anytype) Self { pub fn init(comptime init_val: ArgOpts) Self {
var s = Self{ .data = std.EnumArray(Dimension, comptime_int).initFill(0) }; var s = Self{ .data = std.EnumArray(Dimension, comptime_int).initFill(0) };
inline for (std.meta.fields(@TypeOf(init_val))) |f| inline for (std.meta.fields(@TypeOf(init_val))) |f|
s.data.set(@field(Dimension, f.name), @field(init_val, f.name)); s.data.set(@field(Dimension, f.name), @field(init_val, f.name));

View File

@ -7,12 +7,17 @@ const UnitScale = Scales.UnitScale;
const Dimensions = @import("Dimensions.zig"); const Dimensions = @import("Dimensions.zig");
const Dimension = Dimensions.Dimension; const Dimension = Dimensions.Dimension;
// TODO: Be able to use comptime float and int and T for mulBy ect // TODO:
// Which endup being Dimension less // - Be able to use comptime float and int and T for mulBy ect
// Which endup being Dimension less
/// A dimensioned scalar value. `T` is the numeric type, `d` the dimension exponents, `s` the SI scales. /// A dimensioned scalar value. `T` is the numeric type, `d` the dimension exponents, `s` the SI scales.
/// All dimension and unit tracking is resolved at comptime zero runtime overhead. /// All dimension and unit tracking is resolved at comptime zero runtime overhead.
pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type { pub fn Scalar(comptime T: type, comptime d_opt: Dimensions.ArgOpts, comptime s_opt: Scales.ArgOpts) type {
return Scalar_(T, Dimensions.init(d_opt), Scales.init(s_opt));
}
pub fn Scalar_(comptime T: type, comptime d: Dimensions, comptime s: Scales) type {
@setEvalBranchQuota(10_000_000); @setEvalBranchQuota(10_000_000);
return struct { return struct {
value: T, value: T,
@ -33,7 +38,7 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
/// Add two quantities. Dimensions must match compile error otherwise. /// Add two quantities. Dimensions must match compile error otherwise.
/// Scales are auto-resolved to the finer of the two. /// Scales are auto-resolved to the finer of the two.
pub inline fn add(self: Self, rhs: anytype) Scalar( pub inline fn add(self: Self, rhs: anytype) Scalar_(
T, T,
dims, dims,
hlp.finerScales(Self, @TypeOf(rhs)), hlp.finerScales(Self, @TypeOf(rhs)),
@ -43,7 +48,7 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
if (comptime @TypeOf(rhs) == Self) if (comptime @TypeOf(rhs) == Self)
return .{ .value = self.value + rhs.value }; return .{ .value = self.value + rhs.value };
const TargetType = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); const TargetType = Scalar_(T, dims, hlp.finerScales(Self, @TypeOf(rhs)));
const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value; const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value;
const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value; const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value;
@ -52,7 +57,7 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
/// Subtract two quantities. Dimensions must match compile error otherwise. /// Subtract two quantities. Dimensions must match compile error otherwise.
/// Scales are auto-resolved to the finer of the two. /// Scales are auto-resolved to the finer of the two.
pub inline fn sub(self: Self, rhs: anytype) Scalar( pub inline fn sub(self: Self, rhs: anytype) Scalar_(
T, T,
dims, dims,
hlp.finerScales(Self, @TypeOf(rhs)), hlp.finerScales(Self, @TypeOf(rhs)),
@ -62,7 +67,7 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
if (comptime @TypeOf(rhs) == Self) if (comptime @TypeOf(rhs) == Self)
return .{ .value = self.value - rhs.value }; return .{ .value = self.value - rhs.value };
const TargetType = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); const TargetType = Scalar_(T, dims, hlp.finerScales(Self, @TypeOf(rhs)));
const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value; const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value;
const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value; const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value;
@ -70,14 +75,14 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
} }
/// Multiply two quantities. Dimension exponents are summed: `L¹ * T¹ L¹T¹`. /// Multiply two quantities. Dimension exponents are summed: `L¹ * T¹ L¹T¹`.
pub inline fn mulBy(self: Self, rhs: anytype) Scalar( pub inline fn mulBy(self: Self, rhs: anytype) Scalar_(
T, T,
dims.add(@TypeOf(rhs).dims), dims.add(@TypeOf(rhs).dims),
hlp.finerScales(Self, @TypeOf(rhs)), hlp.finerScales(Self, @TypeOf(rhs)),
) { ) {
const RhsType = @TypeOf(rhs); const RhsType = @TypeOf(rhs);
const SelfNorm = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); const SelfNorm = Scalar_(T, dims, hlp.finerScales(Self, @TypeOf(rhs)));
const RhsNorm = Scalar(T, RhsType.dims, hlp.finerScales(Self, @TypeOf(rhs))); const RhsNorm = Scalar_(T, RhsType.dims, hlp.finerScales(Self, @TypeOf(rhs)));
if (comptime Self == SelfNorm and RhsType == RhsNorm) if (comptime Self == SelfNorm and RhsType == RhsNorm)
return .{ .value = self.value * rhs.value }; return .{ .value = self.value * rhs.value };
@ -88,14 +93,14 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
/// Divide two quantities. Dimension exponents are subtracted: `L¹ / T¹ L¹T¹`. /// Divide two quantities. Dimension exponents are subtracted: `L¹ / T¹ L¹T¹`.
/// Integer types use truncating division. /// Integer types use truncating division.
pub inline fn divBy(self: Self, rhs: anytype) Scalar( pub inline fn divBy(self: Self, rhs: anytype) Scalar_(
T, T,
dims.sub(@TypeOf(rhs).dims), dims.sub(@TypeOf(rhs).dims),
hlp.finerScales(Self, @TypeOf(rhs)), hlp.finerScales(Self, @TypeOf(rhs)),
) { ) {
const RhsType = @TypeOf(rhs); const RhsType = @TypeOf(rhs);
const SelfNorm = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); const SelfNorm = Scalar_(T, dims, hlp.finerScales(Self, @TypeOf(rhs)));
const RhsNorm = Scalar(T, RhsType.dims, hlp.finerScales(Self, @TypeOf(rhs))); const RhsNorm = Scalar_(T, RhsType.dims, hlp.finerScales(Self, @TypeOf(rhs)));
const lhs_val = if (comptime Self == SelfNorm) self.value else self.to(SelfNorm).value; const lhs_val = if (comptime Self == SelfNorm) self.value else self.to(SelfNorm).value;
const rhs_val = if (comptime RhsType == RhsNorm) rhs.value else rhs.to(RhsNorm).value; const rhs_val = if (comptime RhsType == RhsNorm) rhs.value else rhs.to(RhsNorm).value;
if (comptime @typeInfo(T) == .int) { if (comptime @typeInfo(T) == .int) {
@ -116,7 +121,7 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
/// Raises the quantity to a compile-time integer exponent. /// Raises the quantity to a compile-time integer exponent.
/// Dimension exponents are multiplied by the exponent: `(L²)³ L`. /// Dimension exponents are multiplied by the exponent: `(L²)³ L`.
pub inline fn pow(self: Self, comptime exp: comptime_int) Scalar( pub inline fn pow(self: Self, comptime exp: comptime_int) Scalar_(
T, T,
dims.scale(exp), dims.scale(exp),
s, s,
@ -127,7 +132,7 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
return .{ .value = std.math.pow(T, self.value, @as(T, @floatFromInt(exp))) }; return .{ .value = std.math.pow(T, self.value, @as(T, @floatFromInt(exp))) };
} }
pub inline fn sqrt(self: Self) Scalar( pub inline fn sqrt(self: Self) Scalar_(
T, T,
dims.div(2), dims.div(2),
s, s,
@ -196,7 +201,7 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
if (comptime @TypeOf(rhs) == Self) if (comptime @TypeOf(rhs) == Self)
return self.value == rhs.value; return self.value == rhs.value;
const TargetType = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); const TargetType = Scalar_(T, dims, hlp.finerScales(Self, @TypeOf(rhs)));
const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value; const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value;
const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value; const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value;
@ -211,7 +216,7 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
if (comptime @TypeOf(rhs) == Self) if (comptime @TypeOf(rhs) == Self)
return self.value != rhs.value; return self.value != rhs.value;
const TargetType = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); const TargetType = Scalar_(T, dims, hlp.finerScales(Self, @TypeOf(rhs)));
const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value; const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value;
const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value; const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value;
@ -226,7 +231,7 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
if (comptime @TypeOf(rhs) == Self) if (comptime @TypeOf(rhs) == Self)
return self.value > rhs.value; return self.value > rhs.value;
const TargetType = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); const TargetType = Scalar_(T, dims, hlp.finerScales(Self, @TypeOf(rhs)));
const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value; const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value;
const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value; const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value;
@ -240,7 +245,7 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
if (comptime @TypeOf(rhs) == Self) if (comptime @TypeOf(rhs) == Self)
return self.value >= rhs.value; return self.value >= rhs.value;
const TargetType = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); const TargetType = Scalar_(T, dims, hlp.finerScales(Self, @TypeOf(rhs)));
const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value; const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value;
const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value; const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value;
@ -255,7 +260,7 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
if (comptime @TypeOf(rhs) == Self) if (comptime @TypeOf(rhs) == Self)
return self.value < rhs.value; return self.value < rhs.value;
const TargetType = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); const TargetType = Scalar_(T, dims, hlp.finerScales(Self, @TypeOf(rhs)));
const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value; const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value;
const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value; const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value;
@ -269,7 +274,7 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
if (comptime @TypeOf(rhs) == Self) if (comptime @TypeOf(rhs) == Self)
return self.value <= rhs.value; return self.value <= rhs.value;
const TargetType = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); const TargetType = Scalar_(T, dims, hlp.finerScales(Self, @TypeOf(rhs)));
const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value; const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value;
const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value; const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value;
@ -329,8 +334,8 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
} }
test "Generate quantity" { test "Generate quantity" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = -3 })); const Meter = Scalar(i128, .{ .L = 1 }, .{ .L = @enumFromInt(-3) });
const Second = Scalar(f32, Dimensions.init(.{ .T = 1 }), Scales.init(.{ .T = .n })); const Second = Scalar(f32, .{ .T = 1 }, .{ .T = .n });
const distance = Meter{ .value = 10 }; const distance = Meter{ .value = 10 };
const time = Second{ .value = 2 }; const time = Second{ .value = 2 };
@ -340,8 +345,8 @@ test "Generate quantity" {
} }
test "Comparisons (eq, ne, gt, gte, lt, lte)" { test "Comparisons (eq, ne, gt, gte, lt, lte)" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i128, .{ .L = 1 }, .{});
const KiloMeter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); const KiloMeter = Scalar(i128, .{ .L = 1 }, .{ .L = .k });
const m1000 = Meter{ .value = 1000 }; const m1000 = Meter{ .value = 1000 };
const km1 = KiloMeter{ .value = 1 }; const km1 = KiloMeter{ .value = 1 };
@ -366,7 +371,7 @@ test "Comparisons (eq, ne, gt, gte, lt, lte)" {
} }
test "Add" { test "Add" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i128, .{ .L = 1 }, .{});
const distance = Meter{ .value = 10 }; const distance = Meter{ .value = 10 };
const distance2 = Meter{ .value = 20 }; const distance2 = Meter{ .value = 20 };
@ -375,7 +380,7 @@ test "Add" {
try std.testing.expectEqual(30, added.value); try std.testing.expectEqual(30, added.value);
try std.testing.expectEqual(1, @TypeOf(added).dims.get(.L)); try std.testing.expectEqual(1, @TypeOf(added).dims.get(.L));
const KiloMeter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); const KiloMeter = Scalar(i128, .{ .L = 1 }, .{ .L = .k });
const distance3 = KiloMeter{ .value = 2 }; const distance3 = KiloMeter{ .value = 2 };
const added2 = distance.add(distance3); const added2 = distance.add(distance3);
try std.testing.expectEqual(2010, added2.value); try std.testing.expectEqual(2010, added2.value);
@ -385,7 +390,7 @@ test "Add" {
try std.testing.expectEqual(2, added3.value); try std.testing.expectEqual(2, added3.value);
try std.testing.expectEqual(1, @TypeOf(added3).dims.get(.L)); try std.testing.expectEqual(1, @TypeOf(added3).dims.get(.L));
const KiloMeter_f = Scalar(f64, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); const KiloMeter_f = Scalar(f64, .{ .L = 1 }, .{ .L = .k });
const distance4 = KiloMeter_f{ .value = 2 }; const distance4 = KiloMeter_f{ .value = 2 };
const added4 = distance4.add(distance).to(KiloMeter_f); const added4 = distance4.add(distance).to(KiloMeter_f);
try std.testing.expectApproxEqAbs(2.01, added4.value, 0.000001); try std.testing.expectApproxEqAbs(2.01, added4.value, 0.000001);
@ -393,8 +398,8 @@ test "Add" {
} }
test "Sub" { test "Sub" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i128, .{ .L = 1 }, .{});
const KiloMeter_f = Scalar(f64, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); const KiloMeter_f = Scalar(f64, .{ .L = 1 }, .{ .L = .k });
const a = Meter{ .value = 500 }; const a = Meter{ .value = 500 };
const b = Meter{ .value = 200 }; const b = Meter{ .value = 200 };
@ -410,8 +415,8 @@ test "Sub" {
} }
test "MulBy" { test "MulBy" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i128, .{ .L = 1 }, .{});
const Second = Scalar(f32, Dimensions.init(.{ .T = 1 }), Scales.init(.{})); const Second = Scalar(f32, .{ .T = 1 }, .{});
const d = Meter{ .value = 3.0 }; const d = Meter{ .value = 3.0 };
const t = Second{ .value = 4.0 }; const t = Second{ .value = 4.0 };
@ -429,8 +434,8 @@ test "MulBy" {
} }
test "MulBy with scale" { test "MulBy with scale" {
const KiloMeter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); const KiloMeter = Scalar(f32, .{ .L = 1 }, .{ .L = .k });
const KiloGram = Scalar(f32, Dimensions.init(.{ .M = 1 }), Scales.init(.{ .M = .k })); const KiloGram = Scalar(f32, .{ .M = 1 }, .{ .M = .k });
const dist = KiloMeter{ .value = 2.0 }; const dist = KiloMeter{ .value = 2.0 };
const mass = KiloGram{ .value = 3.0 }; const mass = KiloGram{ .value = 3.0 };
@ -440,10 +445,10 @@ test "MulBy with scale" {
} }
test "MulBy with type change" { test "MulBy with type change" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); const Meter = Scalar(i128, .{ .L = 1 }, .{ .L = .k });
const Second = Scalar(f64, Dimensions.init(.{ .T = 1 }), Scales.init(.{})); const Second = Scalar(f64, .{ .T = 1 }, .{});
const KmSec = Scalar(i64, Dimensions.init(.{ .L = 1, .T = 1 }), Scales.init(.{ .L = .k })); const KmSec = Scalar(i64, .{ .L = 1, .T = 1 }, .{ .L = .k });
const KmSec_f = Scalar(f32, Dimensions.init(.{ .L = 1, .T = 1 }), Scales.init(.{ .L = .k })); const KmSec_f = Scalar(f32, .{ .L = 1, .T = 1 }, .{ .L = .k });
const d = Meter{ .value = 3.0 }; const d = Meter{ .value = 3.0 };
const t = Second{ .value = 4.0 }; const t = Second{ .value = 4.0 };
@ -457,8 +462,8 @@ test "MulBy with type change" {
} }
test "MulBy small" { test "MulBy small" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .n })); const Meter = Scalar(i128, .{ .L = 1 }, .{ .L = .n });
const Second = Scalar(f32, Dimensions.init(.{ .T = 1 }), Scales.init(.{})); const Second = Scalar(f32, .{ .T = 1 }, .{});
const d = Meter{ .value = 3.0 }; const d = Meter{ .value = 3.0 };
const t = Second{ .value = 4.0 }; const t = Second{ .value = 4.0 };
@ -470,8 +475,8 @@ test "MulBy small" {
} }
test "MulBy dimensionless" { test "MulBy dimensionless" {
const DimLess = Scalar(i128, Dimensions.init(.{}), Scales.init(.{})); const DimLess = Scalar(i128, .{}, .{});
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i128, .{ .L = 1 }, .{});
const d = Meter{ .value = 7 }; const d = Meter{ .value = 7 };
const scaled = d.mulBy(DimLess{ .value = 3 }); const scaled = d.mulBy(DimLess{ .value = 3 });
@ -480,7 +485,7 @@ test "MulBy dimensionless" {
} }
test "Sqrt" { test "Sqrt" {
const MeterSquare = Scalar(i128, Dimensions.init(.{ .L = 2 }), Scales.init(.{})); const MeterSquare = Scalar(i128, .{ .L = 2 }, .{});
var d = MeterSquare{ .value = 9 }; var d = MeterSquare{ .value = 9 };
var scaled = d.sqrt(); var scaled = d.sqrt();
@ -492,7 +497,7 @@ test "Sqrt" {
try std.testing.expectEqual(0, scaled.value); try std.testing.expectEqual(0, scaled.value);
try std.testing.expectEqual(1, @TypeOf(scaled).dims.get(.L)); try std.testing.expectEqual(1, @TypeOf(scaled).dims.get(.L));
const MeterSquare_f = Scalar(f64, Dimensions.init(.{ .L = 2 }), Scales.init(.{})); const MeterSquare_f = Scalar(f64, .{ .L = 2 }, .{});
const d2 = MeterSquare_f{ .value = 20 }; const d2 = MeterSquare_f{ .value = 20 };
const scaled2 = d2.sqrt(); const scaled2 = d2.sqrt();
try std.testing.expectApproxEqAbs(4.472135955, scaled2.value, 1e-4); try std.testing.expectApproxEqAbs(4.472135955, scaled2.value, 1e-4);
@ -500,8 +505,8 @@ test "Sqrt" {
} }
test "Chained: velocity and acceleration" { test "Chained: velocity and acceleration" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i128, .{ .L = 1 }, .{});
const Second = Scalar(f32, Dimensions.init(.{ .T = 1 }), Scales.init(.{})); const Second = Scalar(f32, .{ .T = 1 }, .{});
const dist = Meter{ .value = 100.0 }; const dist = Meter{ .value = 100.0 };
const t1 = Second{ .value = 5.0 }; const t1 = Second{ .value = 5.0 };
@ -518,8 +523,8 @@ test "Chained: velocity and acceleration" {
} }
test "DivBy integer exact" { test "DivBy integer exact" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i128, .{ .L = 1 }, .{});
const Second = Scalar(f32, Dimensions.init(.{ .T = 1 }), Scales.init(.{})); const Second = Scalar(f32, .{ .T = 1 }, .{});
const dist = Meter{ .value = 120 }; const dist = Meter{ .value = 120 };
const time = Second{ .value = 4 }; const time = Second{ .value = 4 };
@ -531,8 +536,8 @@ test "DivBy integer exact" {
} }
test "Finer scales skip dim 0" { test "Finer scales skip dim 0" {
const Dimless = Scalar(i128, Dimensions.init(.{}), Scales.init(.{})); const Dimless = Scalar(i128, .{}, .{});
const KiloMetre = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); const KiloMetre = Scalar(i128, .{ .L = 1 }, .{ .L = .k });
const r = Dimless{ .value = 30 }; const r = Dimless{ .value = 30 };
const time = KiloMetre{ .value = 4 }; const time = KiloMetre{ .value = 4 };
@ -543,9 +548,9 @@ test "Finer scales skip dim 0" {
} }
test "Conversion chain: km -> m -> cm" { test "Conversion chain: km -> m -> cm" {
const KiloMeter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); const KiloMeter = Scalar(i128, .{ .L = 1 }, .{ .L = .k });
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i128, .{ .L = 1 }, .{});
const CentiMeter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .c })); const CentiMeter = Scalar(i128, .{ .L = 1 }, .{ .L = .c });
const km = KiloMeter{ .value = 15 }; const km = KiloMeter{ .value = 15 };
const m = km.to(Meter); const m = km.to(Meter);
@ -556,9 +561,9 @@ test "Conversion chain: km -> m -> cm" {
} }
test "Conversion: hours -> minutes -> seconds" { test "Conversion: hours -> minutes -> seconds" {
const Hour = Scalar(i128, Dimensions.init(.{ .T = 1 }), Scales.init(.{ .T = .hour })); const Hour = Scalar(i128, .{ .T = 1 }, .{ .T = .hour });
const Minute = Scalar(i128, Dimensions.init(.{ .T = 1 }), Scales.init(.{ .T = .min })); const Minute = Scalar(i128, .{ .T = 1 }, .{ .T = .min });
const Second = Scalar(i128, Dimensions.init(.{ .T = 1 }), Scales.init(.{})); const Second = Scalar(i128, .{ .T = 1 }, .{});
const h = Hour{ .value = 1.0 }; const h = Hour{ .value = 1.0 };
const min = h.to(Minute); const min = h.to(Minute);
@ -569,7 +574,7 @@ test "Conversion: hours -> minutes -> seconds" {
} }
test "Negative values" { test "Negative values" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i128, .{ .L = 1 }, .{});
const a = Meter{ .value = 5 }; const a = Meter{ .value = 5 };
const b = Meter{ .value = 20 }; const b = Meter{ .value = 20 };
@ -578,17 +583,9 @@ test "Negative values" {
} }
test "Format Scalar" { test "Format Scalar" {
const MeterPerSecondSq = Scalar( const MeterPerSecondSq = Scalar(f32, .{ .L = 1, .T = -2 }, .{ .T = .n });
f32, const KgMeterPerSecond = Scalar(f32, .{ .M = 1, .L = 1, .T = -1 }, .{ .M = .k });
Dimensions.init(.{ .L = 1, .T = -2 }), const Meter = Scalar(f32, .{ .L = 1 }, .{});
Scales.init(.{ .T = .n }),
);
const KgMeterPerSecond = Scalar(
f32,
Dimensions.init(.{ .M = 1, .L = 1, .T = -1 }),
Scales.init(.{ .M = .k }),
);
const Meter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
const m = Meter{ .value = 1.23456 }; const m = Meter{ .value = 1.23456 };
const accel = MeterPerSecondSq{ .value = 9.81 }; const accel = MeterPerSecondSq{ .value = 9.81 };
@ -609,20 +606,20 @@ test "Format Scalar" {
} }
test "Abs" { test "Abs" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i128, .{ .L = 1 }, .{});
const m1 = Meter{ .value = -50 }; const m1 = Meter{ .value = -50 };
const m2 = m1.abs(); const m2 = m1.abs();
try std.testing.expectEqual(50, m2.value); try std.testing.expectEqual(50, m2.value);
try std.testing.expectEqual(1, @TypeOf(m2).dims.get(.L)); try std.testing.expectEqual(1, @TypeOf(m2).dims.get(.L));
const m_float = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const m_float = Scalar(f32, .{ .L = 1 }, .{});
const m3 = m_float{ .value = -42.5 }; const m3 = m_float{ .value = -42.5 };
try std.testing.expectEqual(42.5, m3.abs().value); try std.testing.expectEqual(42.5, m3.abs().value);
} }
test "Pow" { test "Pow" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i128, .{ .L = 1 }, .{});
const d = Meter{ .value = 4 }; const d = Meter{ .value = 4 };
const area = d.pow(2); const area = d.pow(2);
@ -634,7 +631,7 @@ test "Pow" {
try std.testing.expectEqual(3, @TypeOf(volume).dims.get(.L)); try std.testing.expectEqual(3, @TypeOf(volume).dims.get(.L));
// Float test // Float test
const MeterF = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const MeterF = Scalar(f32, .{ .L = 1 }, .{});
const d_f = MeterF{ .value = 2.0 }; const d_f = MeterF{ .value = 2.0 };
const area_f = d_f.pow(3); const area_f = d_f.pow(3);
try std.testing.expectEqual(8.0, area_f.value); try std.testing.expectEqual(8.0, area_f.value);

View File

@ -3,6 +3,17 @@ const hlp = @import("helper.zig");
const Dimensions = @import("Dimensions.zig"); const Dimensions = @import("Dimensions.zig");
const Dimension = @import("Dimensions.zig").Dimension; const Dimension = @import("Dimensions.zig").Dimension;
/// Use to initiate Scalar and Scales type
pub const ArgOpts = struct {
L: UnitScale = .none,
M: UnitScale = .none,
T: UnitScale = .none,
I: UnitScale = .none,
Tp: UnitScale = .none,
N: UnitScale = .none,
J: UnitScale = .none,
};
/// SI prefix (picopeta) plus time-unit aliases (min, hour, year). /// SI prefix (picopeta) plus time-unit aliases (min, hour, year).
/// The integer value encodes the exponent for SI prefixes (e.g. `k = 3` 10³), /// The integer value encodes the exponent for SI prefixes (e.g. `k = 3` 10³),
/// and the literal factor for time units (e.g. `hour = 3600`). /// and the literal factor for time units (e.g. `hour = 3600`).
@ -62,9 +73,9 @@ const Scales = @This();
data: std.EnumArray(Dimension, UnitScale), data: std.EnumArray(Dimension, UnitScale),
/// Create a `Scales` from an anonymous struct literal, e.g. `.{ .L = .k, .T = .hour }`. /// Create a `Scales` from a struct literal, e.g. `.{ .L = .k, .T = .hour }`.
/// Unspecified dimensions default to `.none` (factor 1). /// Unspecified dimensions default to `.none` (factor 1).
pub fn init(comptime init_val: anytype) Scales { pub fn init(comptime init_val: ArgOpts) Scales {
comptime var s = Scales{ .data = std.EnumArray(Dimension, UnitScale).initFill(.none) }; comptime var s = Scales{ .data = std.EnumArray(Dimension, UnitScale).initFill(.none) };
inline for (std.meta.fields(@TypeOf(init_val))) |f| { inline for (std.meta.fields(@TypeOf(init_val))) |f| {
if (comptime hlp.isInt(@TypeOf(@field(init_val, f.name)))) if (comptime hlp.isInt(@TypeOf(@field(init_val, f.name))))

View File

@ -2,6 +2,7 @@ const std = @import("std");
const hlp = @import("helper.zig"); const hlp = @import("helper.zig");
const Scalar = @import("Scalar.zig").Scalar; const Scalar = @import("Scalar.zig").Scalar;
const Scalar_ = @import("Scalar.zig").Scalar_;
const Scales = @import("Scales.zig"); const Scales = @import("Scales.zig");
const UnitScale = Scales.UnitScale; const UnitScale = Scales.UnitScale;
const Dimensions = @import("Dimensions.zig"); const Dimensions = @import("Dimensions.zig");
@ -32,13 +33,13 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
} }
/// Element-wise addition. Dimensions must match; scales resolve to the finer of the two. /// Element-wise addition. Dimensions must match; scales resolve to the finer of the two.
pub inline fn add(self: Self, rhs: anytype) Vector(len, Scalar( pub inline fn add(self: Self, rhs: anytype) Vector(len, Scalar_(
T, T,
dims, dims,
hlp.finerScales(Self, @TypeOf(rhs)), hlp.finerScales(Self, @TypeOf(rhs)),
)) { )) {
const Tr = @TypeOf(rhs); const Tr = @TypeOf(rhs);
var res: Vector(len, Scalar(T, d, hlp.finerScales(Self, @TypeOf(rhs)))) = undefined; var res: Vector(len, Scalar_(T, d, hlp.finerScales(Self, @TypeOf(rhs)))) = undefined;
inline for (self.data, 0..) |v, i| { inline for (self.data, 0..) |v, i| {
const q = (Q{ .value = v }).add(Tr.ScalarType{ .value = rhs.data[i] }); const q = (Q{ .value = v }).add(Tr.ScalarType{ .value = rhs.data[i] });
res.data[i] = q.value; res.data[i] = q.value;
@ -46,13 +47,13 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
return res; return res;
} }
/// Element-wise subtraction. Dimensions must match; scales resolve to the finer of the two. /// Element-wise subtraction. Dimensions must match; scales resolve to the finer of the two.
pub inline fn sub(self: Self, rhs: anytype) Vector(len, Scalar( pub inline fn sub(self: Self, rhs: anytype) Vector(len, Scalar_(
T, T,
dims, dims,
hlp.finerScales(Self, @TypeOf(rhs)), hlp.finerScales(Self, @TypeOf(rhs)),
)) { )) {
const Tr = @TypeOf(rhs); const Tr = @TypeOf(rhs);
var res: Vector(len, Scalar(T, d, hlp.finerScales(Self, @TypeOf(rhs)))) = undefined; var res: Vector(len, Scalar_(T, d, hlp.finerScales(Self, @TypeOf(rhs)))) = undefined;
inline for (self.data, 0..) |v, i| { inline for (self.data, 0..) |v, i| {
const q = (Q{ .value = v }).sub(Tr.ScalarType{ .value = rhs.data[i] }); const q = (Q{ .value = v }).sub(Tr.ScalarType{ .value = rhs.data[i] });
res.data[i] = q.value; res.data[i] = q.value;
@ -64,13 +65,13 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
pub inline fn divBy( pub inline fn divBy(
self: Self, self: Self,
rhs: anytype, rhs: anytype,
) Vector(len, Scalar( ) Vector(len, Scalar_(
T, T,
dims.sub(@TypeOf(rhs).dims), dims.sub(@TypeOf(rhs).dims),
hlp.finerScales(Self, @TypeOf(rhs)), hlp.finerScales(Self, @TypeOf(rhs)),
)) { )) {
const Tr = @TypeOf(rhs); const Tr = @TypeOf(rhs);
var res: Vector(len, Scalar(T, d.sub(Tr.dims), hlp.finerScales(Self, @TypeOf(rhs)))) = undefined; var res: Vector(len, Scalar_(T, d.sub(Tr.dims), hlp.finerScales(Self, @TypeOf(rhs)))) = undefined;
inline for (self.data, 0..) |v, i| { inline for (self.data, 0..) |v, i| {
const q = (Q{ .value = v }).divBy(Tr.ScalarType{ .value = rhs.data[i] }); const q = (Q{ .value = v }).divBy(Tr.ScalarType{ .value = rhs.data[i] });
res.data[i] = q.value; res.data[i] = q.value;
@ -82,13 +83,13 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
pub inline fn mulBy( pub inline fn mulBy(
self: Self, self: Self,
rhs: anytype, rhs: anytype,
) Vector(len, Scalar( ) Vector(len, Scalar_(
T, T,
dims.add(@TypeOf(rhs).dims), dims.add(@TypeOf(rhs).dims),
hlp.finerScales(Self, @TypeOf(rhs)), hlp.finerScales(Self, @TypeOf(rhs)),
)) { )) {
const Tr = @TypeOf(rhs); const Tr = @TypeOf(rhs);
var res: Vector(len, Scalar(T, d.add(Tr.dims), hlp.finerScales(Self, @TypeOf(rhs)))) = undefined; var res: Vector(len, Scalar_(T, d.add(Tr.dims), hlp.finerScales(Self, @TypeOf(rhs)))) = undefined;
inline for (self.data, 0..) |v, i| { inline for (self.data, 0..) |v, i| {
const q = (Q{ .value = v }).mulBy(Tr.ScalarType{ .value = rhs.data[i] }); const q = (Q{ .value = v }).mulBy(Tr.ScalarType{ .value = rhs.data[i] });
res.data[i] = q.value; res.data[i] = q.value;
@ -100,12 +101,12 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
pub inline fn divByScalar( pub inline fn divByScalar(
self: Self, self: Self,
scalar: anytype, scalar: anytype,
) Vector(len, Scalar( ) Vector(len, Scalar_(
T, T,
dims.sub(@TypeOf(scalar).dims), dims.sub(@TypeOf(scalar).dims),
hlp.finerScales(Self, @TypeOf(scalar)), hlp.finerScales(Self, @TypeOf(scalar)),
)) { )) {
var res: Vector(len, Scalar(T, d.sub(@TypeOf(scalar).dims), hlp.finerScales(Self, @TypeOf(scalar)))) = undefined; var res: Vector(len, Scalar_(T, d.sub(@TypeOf(scalar).dims), hlp.finerScales(Self, @TypeOf(scalar)))) = undefined;
inline for (self.data, 0..) |v, i| { inline for (self.data, 0..) |v, i| {
const q = Q{ .value = v }; const q = Q{ .value = v };
res.data[i] = q.divBy(scalar).value; res.data[i] = q.divBy(scalar).value;
@ -117,12 +118,12 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
pub inline fn mulByScalar( pub inline fn mulByScalar(
self: Self, self: Self,
scalar: anytype, scalar: anytype,
) Vector(len, Scalar( ) Vector(len, Scalar_(
T, T,
dims.add(@TypeOf(scalar).dims), dims.add(@TypeOf(scalar).dims),
hlp.finerScales(Self, @TypeOf(scalar)), hlp.finerScales(Self, @TypeOf(scalar)),
)) { )) {
var res: Vector(len, Scalar(T, d.add(@TypeOf(scalar).dims), hlp.finerScales(Self, @TypeOf(scalar)))) = undefined; var res: Vector(len, Scalar_(T, d.add(@TypeOf(scalar).dims), hlp.finerScales(Self, @TypeOf(scalar)))) = undefined;
inline for (self.data, 0..) |v, i| { inline for (self.data, 0..) |v, i| {
const q = Q{ .value = v }; const q = Q{ .value = v };
res.data[i] = q.mulBy(scalar).value; res.data[i] = q.mulBy(scalar).value;
@ -132,7 +133,7 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
/// Standard dot product. Dimensions are summed (e.g., Force * Distance = Energy). /// Standard dot product. Dimensions are summed (e.g., Force * Distance = Energy).
/// Returns a Scalar type with the combined dimensions and finest scale. /// Returns a Scalar type with the combined dimensions and finest scale.
pub inline fn dot(self: Self, rhs: anytype) Scalar( pub inline fn dot(self: Self, rhs: anytype) Scalar_(
T, T,
dims.add(@TypeOf(rhs).dims), dims.add(@TypeOf(rhs).dims),
hlp.finerScales(Self, @TypeOf(rhs)), hlp.finerScales(Self, @TypeOf(rhs)),
@ -150,7 +151,7 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
/// 3D Cross product. Dimensions are summed. /// 3D Cross product. Dimensions are summed.
/// Only valid for vectors of length 3. /// Only valid for vectors of length 3.
pub inline fn cross(self: Self, rhs: anytype) Vector(3, Scalar( pub inline fn cross(self: Self, rhs: anytype) Vector(3, Scalar_(
T, T,
dims.add(@TypeOf(rhs).dims), dims.add(@TypeOf(rhs).dims),
hlp.finerScales(Self, @TypeOf(rhs)), hlp.finerScales(Self, @TypeOf(rhs)),
@ -159,7 +160,7 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
@compileError("Cross product is only defined for Vector(3, ...)"); @compileError("Cross product is only defined for Vector(3, ...)");
const Tr = @TypeOf(rhs); const Tr = @TypeOf(rhs);
const ResScalar = Scalar(T, d.add(Tr.dims), hlp.finerScales(Self, Tr)); const ResScalar = Scalar_(T, d.add(Tr.dims), hlp.finerScales(Self, Tr));
const ResVec = Vector(3, ResScalar); const ResVec = Vector(3, ResScalar);
// Calculation: [y1*z2 - z1*y2, z1*x2 - x1*z2, x1*y2 - y1*x2] // Calculation: [y1*z2 - z1*y2, z1*x2 - x1*z2, x1*y2 - y1*x2]
@ -202,7 +203,7 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
/// Multiplies all components of the vector together. /// Multiplies all components of the vector together.
/// Resulting dimensions are (Original Dims * len). /// Resulting dimensions are (Original Dims * len).
pub inline fn product(self: Self) Scalar( pub inline fn product(self: Self) Scalar_(
T, T,
dims.scale(len), dims.scale(len),
scales, scales,
@ -215,12 +216,12 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
/// Raises every component to a compile-time integer power. /// Raises every component to a compile-time integer power.
/// Dimensions are scaled by the exponent. /// Dimensions are scaled by the exponent.
pub inline fn pow(self: Self, comptime exp: comptime_int) Vector(len, Scalar( pub inline fn pow(self: Self, comptime exp: comptime_int) Vector(len, Scalar_(
T, T,
dims.scale(exp), dims.scale(exp),
scales, scales,
)) { )) {
const ResScalar = Scalar(T, dims.scale(exp), s); const ResScalar = Scalar_(T, dims.scale(exp), s);
var res: Vector(len, ResScalar) = undefined; var res: Vector(len, ResScalar) = undefined;
inline for (self.data, 0..) |v, i| { inline for (self.data, 0..) |v, i| {
const q = Q{ .value = v }; const q = Q{ .value = v };
@ -436,16 +437,8 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
} }
test "Format VectorX" { test "Format VectorX" {
const MeterPerSecondSq = Scalar( const MeterPerSecondSq = Scalar(f32, .{ .L = 1, .T = -2 }, .{ .T = .n });
f32, const KgMeterPerSecond = Scalar(f32, .{ .M = 1, .L = 1, .T = -1 }, .{ .M = .k });
Dimensions.init(.{ .L = 1, .T = -2 }),
Scales.init(.{ .T = .n }),
);
const KgMeterPerSecond = Scalar(
f32,
Dimensions.init(.{ .M = 1, .L = 1, .T = -1 }),
Scales.init(.{ .M = .k }),
);
const accel = MeterPerSecondSq.Vec3.initDefault(9.81); const accel = MeterPerSecondSq.Vec3.initDefault(9.81);
const momentum = KgMeterPerSecond.Vec3{ .data = .{ 43, 0, 11 } }; const momentum = KgMeterPerSecond.Vec3{ .data = .{ 43, 0, 11 } };
@ -459,7 +452,7 @@ test "Format VectorX" {
} }
test "VecX Init and Basic Arithmetic" { test "VecX Init and Basic Arithmetic" {
const Meter = Scalar(i32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i32, .{ .L = 1 }, .{});
const Vec3M = Meter.Vec3; const Vec3M = Meter.Vec3;
// Test zero, one, initDefault // Test zero, one, initDefault
@ -500,8 +493,8 @@ test "VecX Init and Basic Arithmetic" {
} }
test "VecX Kinematics (Scalar Mul/Div)" { test "VecX Kinematics (Scalar Mul/Div)" {
const Meter = Scalar(i32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i32, .{ .L = 1 }, .{});
const Second = Scalar(i32, Dimensions.init(.{ .T = 1 }), Scales.init(.{})); const Second = Scalar(i32, .{ .T = 1 }, .{});
const Vec3M = Meter.Vec3; const Vec3M = Meter.Vec3;
const pos = Vec3M{ .data = .{ 100, 200, 300 } }; const pos = Vec3M{ .data = .{ 100, 200, 300 } };
@ -525,7 +518,7 @@ test "VecX Kinematics (Scalar Mul/Div)" {
} }
test "VecX Element-wise Math and Scaling" { test "VecX Element-wise Math and Scaling" {
const Meter = Scalar(i32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i32, .{ .L = 1 }, .{});
const Vec3M = Meter.Vec3; const Vec3M = Meter.Vec3;
const v1 = Vec3M{ .data = .{ 10, 20, 30 } }; const v1 = Vec3M{ .data = .{ 10, 20, 30 } };
@ -540,8 +533,8 @@ test "VecX Element-wise Math and Scaling" {
} }
test "VecX Conversions" { test "VecX Conversions" {
const KiloMeter = Scalar(i32, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); const KiloMeter = Scalar(i32, .{ .L = 1 }, .{ .L = .k });
const Meter = Scalar(i32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i32, .{ .L = 1 }, .{});
const v_km = KiloMeter.Vec3{ .data = .{ 1, 2, 3 } }; const v_km = KiloMeter.Vec3{ .data = .{ 1, 2, 3 } };
const v_m = v_km.to(Meter); const v_m = v_km.to(Meter);
@ -556,8 +549,8 @@ test "VecX Conversions" {
} }
test "VecX Length" { test "VecX Length" {
const MeterInt = Scalar(i32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const MeterInt = Scalar(i32, .{ .L = 1 }, .{});
const MeterFloat = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const MeterFloat = Scalar(f32, .{ .L = 1 }, .{});
// Integer length // Integer length
// 3-4-5 triangle on XY plane // 3-4-5 triangle on XY plane
@ -572,8 +565,8 @@ test "VecX Length" {
} }
test "Vector Comparisons" { test "Vector Comparisons" {
const Meter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(f32, .{ .L = 1 }, .{});
const KiloMeter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); const KiloMeter = Scalar(f32, .{ .L = 1 }, .{ .L = .k });
const v1 = Meter.Vec3{ .data = .{ 1000.0, 500.0, 0.0 } }; const v1 = Meter.Vec3{ .data = .{ 1000.0, 500.0, 0.0 } };
const v2 = KiloMeter.Vec3{ .data = .{ 1.0, 0.5, 0.0 } }; const v2 = KiloMeter.Vec3{ .data = .{ 1.0, 0.5, 0.0 } };
@ -601,8 +594,8 @@ test "Vector Comparisons" {
} }
test "Vector vs Scalar Comparisons" { test "Vector vs Scalar Comparisons" {
const Meter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(f32, .{ .L = 1 }, .{});
const KiloMeter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); const KiloMeter = Scalar(f32, .{ .L = 1 }, .{ .L = .k });
const positions = Meter.Vec3{ .data = .{ 500.0, 1200.0, 3000.0 } }; const positions = Meter.Vec3{ .data = .{ 500.0, 1200.0, 3000.0 } };
const threshold = KiloMeter{ .value = 1.0 }; // 1km (1000m) const threshold = KiloMeter{ .value = 1.0 }; // 1km (1000m)
@ -621,8 +614,8 @@ test "Vector vs Scalar Comparisons" {
} }
test "Vector Dot and Cross Products" { test "Vector Dot and Cross Products" {
const Meter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(f32, .{ .L = 1 }, .{});
const Newton = Scalar(f32, Dimensions.init(.{ .M = 1, .L = 1, .T = -2 }), Scales.init(.{})); const Newton = Scalar(f32, .{ .M = 1, .L = 1, .T = -2 }, .{});
const pos = Meter.Vec3{ .data = .{ 10.0, 0.0, 0.0 } }; const pos = Meter.Vec3{ .data = .{ 10.0, 0.0, 0.0 } };
const force = Newton.Vec3{ .data = .{ 5.0, 5.0, 0.0 } }; const force = Newton.Vec3{ .data = .{ 5.0, 5.0, 0.0 } };
@ -645,7 +638,7 @@ test "Vector Dot and Cross Products" {
} }
test "Vector Abs, Pow, Sqrt and Product" { test "Vector Abs, Pow, Sqrt and Product" {
const Meter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(f32, .{ .L = 1 }, .{});
const v1 = Meter.Vec3{ .data = .{ -2.0, 3.0, -4.0 } }; const v1 = Meter.Vec3{ .data = .{ -2.0, 3.0, -4.0 } };