Added sqrt to Scalar and Vector

This commit is contained in:
adrien 2026-04-22 15:43:17 +02:00
parent 0dd9e02f59
commit a2d46e3f55
3 changed files with 71 additions and 8 deletions

View File

@ -68,7 +68,6 @@ pub fn add(comptime a: Self, comptime b: Self) Self {
/// Subtract exponents component-wise. Used internally by `divBy`. /// Subtract exponents component-wise. Used internally by `divBy`.
pub fn sub(comptime a: Self, comptime b: Self) Self { pub fn sub(comptime a: Self, comptime b: Self) Self {
@setEvalBranchQuota(10_000);
var result = Self.initFill(0); var result = Self.initFill(0);
inline for (std.enums.values(Dimension)) |d| inline for (std.enums.values(Dimension)) |d|
result.set(d, a.get(d) - b.get(d)); result.set(d, a.get(d) - b.get(d));
@ -77,13 +76,19 @@ pub fn sub(comptime a: Self, comptime b: Self) Self {
/// Multiply exponents by a scalar integer. Used internally by `pow` in Scalar. /// Multiply exponents by a scalar integer. Used internally by `pow` in Scalar.
pub fn scale(comptime a: Self, comptime exp: comptime_int) Self { pub fn scale(comptime a: Self, comptime exp: comptime_int) Self {
@setEvalBranchQuota(10_000);
var result = Self.initFill(0); var result = Self.initFill(0);
inline for (std.enums.values(Dimension)) |d| inline for (std.enums.values(Dimension)) |d|
result.set(d, a.get(d) * exp); result.set(d, a.get(d) * exp);
return result; return result;
} }
pub fn div(comptime a: Self, comptime exp: comptime_int) Self {
var result = Self.initFill(0);
inline for (std.enums.values(Dimension)) |d|
result.set(d, a.get(d) / exp);
return result;
}
/// Returns true if every dimension exponent is equal. Used to enforce type compatibility in `add`, `sub`, `to`. /// Returns true if every dimension exponent is equal. Used to enforce type compatibility in `add`, `sub`, `to`.
pub fn eql(comptime a: Self, comptime b: Self) bool { pub fn eql(comptime a: Self, comptime b: Self) bool {
inline for (std.enums.values(Dimension)) |d| inline for (std.enums.values(Dimension)) |d|
@ -91,6 +96,12 @@ pub fn eql(comptime a: Self, comptime b: Self) bool {
return true; return true;
} }
pub fn isSquare(comptime a: Self) bool {
inline for (std.enums.values(Dimension)) |d|
if (a.get(d) % 2 != 0) return false;
return true;
}
pub fn str(comptime a: Self) []const u8 { pub fn str(comptime a: Self) []const u8 {
var out: []const u8 = ""; var out: []const u8 = "";
const dims = std.enums.values(Dimension); const dims = std.enums.values(Dimension);

View File

@ -7,10 +7,8 @@ const UnitScale = Scales.UnitScale;
const Dimensions = @import("Dimensions.zig"); const Dimensions = @import("Dimensions.zig");
const Dimension = Dimensions.Dimension; const Dimension = Dimensions.Dimension;
// TODO: Add those operation: // TODO: Be able to use comptime float and int and T for mulBy ect
// - abs: Absolut value // Which endup being Dimension less
// - pow: Scalar power another
// - log: Scalar log another
/// 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.
@ -129,6 +127,24 @@ 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(
T,
dims.div(2),
s,
) {
if (comptime !dims.isSquare()) // Check if all exponents are divisible by 2
@compileError("Cannot take sqrt of " ++ dims.str() ++ ": exponents must be even.");
if (self.value < 0) return .{ .value = 0 };
if (comptime @typeInfo(T) == .int) {
const UnsignedT = @Int(.unsigned, @typeInfo(T).int.bits);
const u_len_sq = @as(UnsignedT, @intCast(self.value));
return .{ .value = @as(T, @intCast(std.math.sqrt(u_len_sq))) };
} else {
return .{ .value = @sqrt(self.value) };
}
}
/// Convert to a compatible unit type. The scale ratio is computed at comptime. /// Convert to a compatible unit type. The scale ratio is computed at comptime.
/// Compile error if dimensions don't match. /// Compile error if dimensions don't match.
pub inline fn to(self: Self, comptime Dest: type) Dest { pub inline fn to(self: Self, comptime Dest: type) Dest {
@ -463,6 +479,26 @@ test "MulBy dimensionless" {
try std.testing.expectEqual(1, @TypeOf(scaled).dims.get(.L)); try std.testing.expectEqual(1, @TypeOf(scaled).dims.get(.L));
} }
test "Sqrt" {
const MeterSquare = Scalar(i128, Dimensions.init(.{ .L = 2 }), Scales.init(.{}));
var d = MeterSquare{ .value = 9 };
var scaled = d.sqrt();
try std.testing.expectEqual(3, scaled.value);
try std.testing.expectEqual(1, @TypeOf(scaled).dims.get(.L));
d = MeterSquare{ .value = -5 };
scaled = d.sqrt();
try std.testing.expectEqual(0, scaled.value);
try std.testing.expectEqual(1, @TypeOf(scaled).dims.get(.L));
const MeterSquare_f = Scalar(f64, Dimensions.init(.{ .L = 2 }), Scales.init(.{}));
const d2 = MeterSquare_f{ .value = 20 };
const scaled2 = d2.sqrt();
try std.testing.expectApproxEqAbs(4.472135955, scaled2.value, 1e-4);
try std.testing.expectEqual(1, @TypeOf(scaled2).dims.get(.L));
}
test "Chained: velocity and acceleration" { test "Chained: velocity and acceleration" {
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
const Second = Scalar(f32, Dimensions.init(.{ .T = 1 }), Scales.init(.{})); const Second = Scalar(f32, Dimensions.init(.{ .T = 1 }), Scales.init(.{}));

View File

@ -190,6 +190,16 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
return res; return res;
} }
/// Returns a vector where each component is the absolute value of the original.
pub inline fn sqrt(self: Self) Self {
var res: Self = undefined;
inline for (self.data, 0..) |v, i| {
const q = Q{ .value = v };
res.data[i] = q.sqrt().value;
}
return res;
}
/// 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(
@ -549,7 +559,7 @@ test "VecX Length" {
const MeterInt = Scalar(i32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const MeterInt = Scalar(i32, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
const MeterFloat = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const MeterFloat = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
// Integer length (using your custom isqrt) // Integer length
// 3-4-5 triangle on XY plane // 3-4-5 triangle on XY plane
const v_int = MeterInt.Vec3{ .data = .{ 3, 4, 0 } }; const v_int = MeterInt.Vec3{ .data = .{ 3, 4, 0 } };
try std.testing.expectEqual(25, v_int.lengthSqr()); try std.testing.expectEqual(25, v_int.lengthSqr());
@ -634,7 +644,7 @@ test "Vector Dot and Cross Products" {
try std.testing.expectEqual(2, @TypeOf(torque).dims.get(.L)); try std.testing.expectEqual(2, @TypeOf(torque).dims.get(.L));
} }
test "Vector Abs, Pow, and Product" { test "Vector Abs, Pow, Sqrt and Product" {
const Meter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); const Meter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
const v1 = Meter.Vec3{ .data = .{ -2.0, 3.0, -4.0 } }; const v1 = Meter.Vec3{ .data = .{ -2.0, 3.0, -4.0 } };
@ -654,4 +664,10 @@ test "Vector Abs, Pow, and Product" {
try std.testing.expectEqual(4.0, area_vec.data[0]); try std.testing.expectEqual(4.0, area_vec.data[0]);
try std.testing.expectEqual(16.0, area_vec.data[2]); try std.testing.expectEqual(16.0, area_vec.data[2]);
try std.testing.expectEqual(2, @TypeOf(area_vec).dims.get(.L)); try std.testing.expectEqual(2, @TypeOf(area_vec).dims.get(.L));
// 4. Sqrt
const sqrted = area_vec.sqrt();
try std.testing.expectEqual(2, sqrted.data[0]);
try std.testing.expectEqual(4, sqrted.data[2]);
try std.testing.expectEqual(2, @TypeOf(sqrted).dims.get(.L));
} }