diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 28708c2e..00b9961a 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -2,6 +2,7 @@
- Added angle conversion utilities for ranges
- Added Ray3.Transformed overloads
- Added Ray3.Normalized
+- Added Vec.AngleBetweenSigned for 2D vectors
### 5.3.5
- [Base] added IsEmpty/IsEmptyOrNull overloads for Array/ICollection with efficient implementation
diff --git a/src/Aardvark.Base/Geometry/Types/Ray/Ray2_auto.cs b/src/Aardvark.Base/Geometry/Types/Ray/Ray2_auto.cs
index 587a95b2..ad2403b4 100644
--- a/src/Aardvark.Base/Geometry/Types/Ray/Ray2_auto.cs
+++ b/src/Aardvark.Base/Geometry/Types/Ray/Ray2_auto.cs
@@ -123,6 +123,11 @@ public readonly Ray2f Reversed
get => new Ray2f(Origin, -Direction);
}
+ ///
+ /// Returns the ray with its directional normalized.
+ ///
+ public readonly Ray2f Normalized => new(Origin, Direction.Normalized);
+
#endregion
#region Ray Arithmetics
@@ -217,6 +222,14 @@ public readonly float AngleBetweenFast(Ray2f r)
public readonly float AngleBetween(Ray2f r)
=> Direction.AngleBetween(r.Direction);
+ ///
+ /// Returns the signed angle between this and the given in radians.
+ /// The direction vectors of the input rays have to be normalized.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public readonly float AngleBetweenSigned(Ray2f r)
+ => Direction.AngleBetweenSigned(r.Direction);
+
///
/// Returns a signed value where left is negative and right positive.
/// The magnitude is equal to the float size of the triangle the ray + direction and p.
@@ -650,6 +663,11 @@ public readonly Ray2d Reversed
get => new Ray2d(Origin, -Direction);
}
+ ///
+ /// Returns the ray with its directional normalized.
+ ///
+ public readonly Ray2d Normalized => new(Origin, Direction.Normalized);
+
#endregion
#region Ray Arithmetics
@@ -744,6 +762,14 @@ public readonly double AngleBetweenFast(Ray2d r)
public readonly double AngleBetween(Ray2d r)
=> Direction.AngleBetween(r.Direction);
+ ///
+ /// Returns the signed angle between this and the given in radians.
+ /// The direction vectors of the input rays have to be normalized.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public readonly double AngleBetweenSigned(Ray2d r)
+ => Direction.AngleBetweenSigned(r.Direction);
+
///
/// Returns a signed value where left is negative and right positive.
/// The magnitude is equal to the double size of the triangle the ray + direction and p.
diff --git a/src/Aardvark.Base/Geometry/Types/Ray/Ray2_template.cs b/src/Aardvark.Base/Geometry/Types/Ray/Ray2_template.cs
index faea8d8f..0bcca925 100644
--- a/src/Aardvark.Base/Geometry/Types/Ray/Ray2_template.cs
+++ b/src/Aardvark.Base/Geometry/Types/Ray/Ray2_template.cs
@@ -139,6 +139,11 @@ public readonly __ray2t__ Reversed
get => new __ray2t__(Origin, -Direction);
}
+ ///
+ /// Returns the ray with its directional normalized.
+ ///
+ public readonly __ray2t__ Normalized => new(Origin, Direction.Normalized);
+
#endregion
#region Ray Arithmetics
@@ -233,6 +238,14 @@ public readonly __ftype__ AngleBetweenFast(__ray2t__ r)
public readonly __ftype__ AngleBetween(__ray2t__ r)
=> Direction.AngleBetween(r.Direction);
+ ///
+ /// Returns the signed angle between this and the given in radians.
+ /// The direction vectors of the input rays have to be normalized.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public readonly __ftype__ AngleBetweenSigned(__ray2t__ r)
+ => Direction.AngleBetweenSigned(r.Direction);
+
///
/// Returns a signed value where left is negative and right positive.
/// The magnitude is equal to the __ftype__ size of the triangle the ray + direction and p.
diff --git a/src/Aardvark.Base/Math/Vectors/Vector_auto.cs b/src/Aardvark.Base/Math/Vectors/Vector_auto.cs
index 57c4dacd..b632b8fb 100644
--- a/src/Aardvark.Base/Math/Vectors/Vector_auto.cs
+++ b/src/Aardvark.Base/Math/Vectors/Vector_auto.cs
@@ -20277,6 +20277,17 @@ public static float AngleBetween(this V2f x, V2f y)
return 2 * Fun.Atan2(b.Length, a.Length);
}
+ ///
+ /// Computes the signed angle between two given vectors in radians. The input vectors have to be normalized.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float AngleBetweenSigned(this V2f x, V2f y)
+ {
+ var a = x.X * y.Y - x.Y * y.X;
+ var b = x.X * y.X + x.Y * y.Y;
+ return Fun.Atan2(a, b);
+ }
+
#endregion
#region AnyTiny, AllTiny
@@ -26481,6 +26492,17 @@ public static double AngleBetween(this V2d x, V2d y)
return 2 * Fun.Atan2(b.Length, a.Length);
}
+ ///
+ /// Computes the signed angle between two given vectors in radians. The input vectors have to be normalized.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double AngleBetweenSigned(this V2d x, V2d y)
+ {
+ var a = x.X * y.Y - x.Y * y.X;
+ var b = x.X * y.X + x.Y * y.Y;
+ return Fun.Atan2(a, b);
+ }
+
#endregion
#region AnyTiny, AllTiny
diff --git a/src/Aardvark.Base/Math/Vectors/Vector_template.cs b/src/Aardvark.Base/Math/Vectors/Vector_template.cs
index 323f8e0f..4edf540c 100644
--- a/src/Aardvark.Base/Math/Vectors/Vector_template.cs
+++ b/src/Aardvark.Base/Math/Vectors/Vector_template.cs
@@ -2376,6 +2376,19 @@ public static __ctype__ AngleBetween(this __vtype__ x, __vtype__ y)
return 2 * Fun.Atan2(b.Length, a.Length);
}
+ //# if (d == 2) {
+ ///
+ /// Computes the signed angle between two given vectors in radians. The input vectors have to be normalized.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static __ctype__ AngleBetweenSigned(this __vtype__ x, __vtype__ y)
+ {
+ var a = x.X * y.Y - x.Y * y.X;
+ var b = x.X * y.X + x.Y * y.Y;
+ return Fun.Atan2(a, b);
+ }
+
+ //# }
#endregion
//# }
diff --git a/src/Tests/Aardvark.Base.FSharp.Tests/Aardvark.Base.FSharp.Tests.fsproj b/src/Tests/Aardvark.Base.FSharp.Tests/Aardvark.Base.FSharp.Tests.fsproj
index 94770f8d..6153fe97 100644
--- a/src/Tests/Aardvark.Base.FSharp.Tests/Aardvark.Base.FSharp.Tests.fsproj
+++ b/src/Tests/Aardvark.Base.FSharp.Tests/Aardvark.Base.FSharp.Tests.fsproj
@@ -36,6 +36,7 @@
+
diff --git a/src/Tests/Aardvark.Base.FSharp.Tests/Math/MathTests.fs b/src/Tests/Aardvark.Base.FSharp.Tests/Math/MathTests.fs
index a7c1b6fd..75f02de5 100644
--- a/src/Tests/Aardvark.Base.FSharp.Tests/Math/MathTests.fs
+++ b/src/Tests/Aardvark.Base.FSharp.Tests/Math/MathTests.fs
@@ -4,9 +4,48 @@ open Aardvark.Base
open NUnit.Framework
open FsUnit
+open FsCheck
+open FsCheck.NUnit
+open System
module MathTests =
+ let equalWithin x eps = (new NUnit.Framework.Constraints.EqualConstraint(x)).Within(eps)
+
+ type RotationTestCase2D =
+ {
+ Src: V2d
+ Dst: V2d
+ Angle: float
+ }
+
+ module Gen =
+ let floatUnit =
+ gen {
+ let! n = Arb.generate
+ return float n / float UInt32.MaxValue
+ }
+
+ let direction2d =
+ gen {
+ let! t = floatUnit
+ return Rot2d(t * Constant.PiTimesTwo) * V2d.XAxis
+ }
+
+ type Generator private () =
+ static member AngleTestCase =
+ gen {
+ let! t = Gen.floatUnit
+ let angle = (t - 0.5) * Constant.PiTimesTwo
+
+ let! src = Gen.direction2d
+ let rot = Rot2d angle
+ let dst = rot * src
+
+ return { Src = src; Dst = dst; Angle = angle }
+ }
+ |> Arb.fromGen
+
[]
let ``[Math] lerp`` () =
// Just making sure the parameter order is correct :S
@@ -50,4 +89,19 @@ module MathTests =
lerp (V2d(50)) (V2d(100)) 0.5 |> should equal (Fun.Lerp(0.5, V2d(50), V2d(100)))
lerp (V2d(50)) (V2d(100)) (V2d(0.5)) |> should equal (Fun.Lerp(V2d(0.5), V2d(50), V2d(100)))
- lerp (C4d(50.0)) (C4d(100.0)) 0.5 |> should equal (Fun.Lerp(0.5, C4d(50.0), C4d(100.0)))
\ No newline at end of file
+ lerp (C4d(50.0)) (C4d(100.0)) 0.5 |> should equal (Fun.Lerp(0.5, C4d(50.0), C4d(100.0)))
+
+ [ |])>]
+ let ``[Math] AngleBetween 2D`` (input: RotationTestCase2D) =
+ let result = Vec.AngleBetween(input.Src, input.Dst)
+ result |> should be (equalWithin (abs input.Angle) 0.00001)
+
+ [ |])>]
+ let ``[Math] AngleBetweenFast 2D`` (input: RotationTestCase2D) =
+ let result = Vec.AngleBetweenFast(input.Src, input.Dst)
+ result |> should be (equalWithin (abs input.Angle) 0.00001)
+
+ [ |])>]
+ let ``[Math] AngleBetweenSigned 2D`` (input: RotationTestCase2D) =
+ let result = Vec.AngleBetweenSigned(input.Src, input.Dst)
+ result |> should be (equalWithin input.Angle 0.00001)
\ No newline at end of file