Skip to content

Commit f607991

Browse files
authored
Trigonometric extensions to Angle (#222) (#230)
* Added cos, sin and tan of instance * Update AngleTests.cs * Added static members for common angles * Trig functions for angle and corresponding tests * Replaced all trigonometric calls from Math to Angle
1 parent 2a2ae08 commit f607991

File tree

8 files changed

+271
-78
lines changed

8 files changed

+271
-78
lines changed

src/Spatial.Tests/Units/AngleTests.cs

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,119 @@ public void EqualsWithTolerance()
109109
Assert.AreEqual(false, one.Equals(two, Angle.FromRadians(0.1)));
110110
}
111111

112+
[TestCase(0, 1)]
113+
[TestCase(30, 0.86602540378443871)]
114+
[TestCase(-30, 0.86602540378443871)]
115+
[TestCase(45, 0.70710678118654757)]
116+
[TestCase(-45, 0.70710678118654757)]
117+
[TestCase(60, 0.5)]
118+
[TestCase(-60, 0.5)]
119+
[TestCase(90, 0)]
120+
[TestCase(-90, 0)]
121+
[TestCase(120, -0.5)]
122+
[TestCase(-120, -0.5)]
123+
[TestCase(180, -1)]
124+
[TestCase(-180, -1)]
125+
[TestCase(270, 0)]
126+
public void CosineRoundTrip(double degrees, double cosine)
127+
{
128+
var angle = Angle.FromDegrees(degrees);
129+
Assert.AreEqual(cosine, angle.Cos, 1e-15);
130+
131+
if (degrees >= 0 && degrees <= 180)
132+
{
133+
var recovered = Angle.Acos(cosine);
134+
Assert.AreEqual(angle.Degrees, recovered.Degrees, 1e-6);
135+
}
136+
}
137+
138+
[Test]
139+
public void AcosException()
140+
{
141+
Assert.Throws<ArgumentOutOfRangeException>(() => Angle.Acos(5));
142+
}
143+
144+
[TestCase(0, 0)]
145+
[TestCase(30, 0.5)]
146+
[TestCase(-30, -0.5)]
147+
[TestCase(45, 0.70710678118654757)]
148+
[TestCase(-45, -0.70710678118654757)]
149+
[TestCase(60, 0.86602540378443871)]
150+
[TestCase(-60, -0.86602540378443871)]
151+
[TestCase(90, 1)]
152+
[TestCase(-90, -1)]
153+
[TestCase(120, 0.86602540378443871)]
154+
[TestCase(-120, -0.86602540378443871)]
155+
[TestCase(180, 0)]
156+
[TestCase(-180, 0)]
157+
[TestCase(270, -1)]
158+
public void SineWithRoundTrip(double degrees, double sine)
159+
{
160+
var angle = Angle.FromDegrees(degrees);
161+
Assert.AreEqual(sine, angle.Sin, 1e-15);
162+
163+
if (degrees >= -90 && degrees <= 90)
164+
{
165+
var recovered = Angle.Asin(sine);
166+
Assert.AreEqual(angle.Degrees, recovered.Degrees, 1e-6);
167+
}
168+
}
169+
170+
[Test]
171+
public void AsinException()
172+
{
173+
Assert.Throws<ArgumentOutOfRangeException>(() => Angle.Asin(5));
174+
}
175+
176+
[TestCase(0, 0)]
177+
[TestCase(30, 0.57735026918962573)]
178+
[TestCase(-30, -0.57735026918962573)]
179+
[TestCase(45, 1)]
180+
[TestCase(-45, -1)]
181+
[TestCase(60, 1.7320508075688767)]
182+
[TestCase(-60, -1.7320508075688767)]
183+
[TestCase(120, -1.7320508075688783)]
184+
[TestCase(-120, 1.7320508075688783)]
185+
[TestCase(180, 0)]
186+
[TestCase(-180, 0)]
187+
public void TangentWithRoundTrip(double degrees, double tangent)
188+
{
189+
var angle = Angle.FromDegrees(degrees);
190+
Assert.AreEqual(tangent, angle.Tan, 1e-15);
191+
192+
if (degrees >= -90 && degrees <= 90)
193+
{
194+
var recovered = Angle.Atan(tangent);
195+
Assert.AreEqual(angle.Degrees, recovered.Degrees, 1e-6);
196+
}
197+
}
198+
199+
[TestCase(0, 1, 0)]
200+
[TestCase(30, 1.7320508075688772935274463415059, 1)]
201+
[TestCase(-30, 1.7320508075688772935274463415059, -1)]
202+
[TestCase(45, 1, 1)]
203+
[TestCase(-45, 1, -1)]
204+
[TestCase(60, 1, 1.7320508075688772935274463415059)]
205+
[TestCase(-60, 1, -1.7320508075688772935274463415059)]
206+
[TestCase(90, 0, 1)]
207+
[TestCase(-90, 0, -1)]
208+
[TestCase(120, -1, 1.7320508075688772935274463415059)]
209+
[TestCase(-120, -1, -1.7320508075688772935274463415059)]
210+
[TestCase(150, -1.7320508075688772935274463415059, 1)]
211+
[TestCase(-150, -1.7320508075688772935274463415059, -1)]
212+
[TestCase(180, -1, 0)]
213+
public void Atan2(double degrees, double x, double y)
214+
{
215+
var expected = Angle.FromDegrees(degrees);
216+
var actual = Angle.Atan2(y, x);
217+
Assert.AreEqual(expected.Degrees, actual.Degrees, 1e-10);
218+
}
219+
112220
[TestCase(90, 1.5707963267948966)]
113221
public void FromDegrees(double degrees, double expected)
114222
{
115223
Assert.AreEqual(expected, Angle.FromDegrees(degrees).Radians);
116-
Assert.AreEqual(degrees, Angle.FromDegrees(degrees).Degrees, 1E-6);
224+
Assert.AreEqual(degrees, Angle.FromDegrees(degrees).Degrees);
117225
}
118226

119227
[TestCase(1, 1)]
@@ -122,10 +230,10 @@ public void FromRadians(double radians, double expected)
122230
Assert.AreEqual(expected, Angle.FromRadians(radians).Radians);
123231
}
124232

125-
[TestCase(20, 33, 49, 0.35890271998857842)]
233+
[TestCase(20, 33, 49, 0.35890270667277291)]
126234
public void FromSexagesimal(int degrees, int minutes, double seconds, double expected)
127235
{
128-
Assert.AreEqual(expected, Angle.FromSexagesimal(degrees, minutes, seconds).Radians, 1E-6);
236+
Assert.AreEqual(expected, Angle.FromSexagesimal(degrees, minutes, seconds).Radians);
129237
}
130238

131239
[TestCase("5 °", 5 * DegToRad)]

src/Spatial/Euclidean/CoordinateSystem.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -257,12 +257,12 @@ public static CoordinateSystem Rotation(Angle angle, Vector3D v)
257257
public static CoordinateSystem Rotation(Angle yaw, Angle pitch, Angle roll)
258258
{
259259
var cs = new CoordinateSystem();
260-
var cosY = Math.Cos(yaw.Radians);
261-
var sinY = Math.Sin(yaw.Radians);
262-
var cosP = Math.Cos(pitch.Radians);
263-
var sinP = Math.Sin(pitch.Radians);
264-
var cosR = Math.Cos(roll.Radians);
265-
var sinR = Math.Sin(roll.Radians);
260+
var cosY = yaw.Cos;
261+
var sinY = yaw.Sin;
262+
var cosP = pitch.Cos;
263+
var sinP = pitch.Sin;
264+
var cosR = roll.Cos;
265+
var sinR = roll.Sin;
266266

267267
cs[0, 0] = cosY * cosP;
268268
cs[1, 0] = sinY * cosP;

src/Spatial/Euclidean/Matrix2D.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ public static class Matrix2D
1616
/// <returns>A transform matrix</returns>
1717
public static DenseMatrix Rotation(Angle rotation)
1818
{
19-
double c = Math.Cos(rotation.Radians);
20-
double s = Math.Sin(rotation.Radians);
19+
var c = rotation.Cos;
20+
var s = rotation.Sin;
2121
return Create(c, -s, s, c);
2222
}
2323

src/Spatial/Euclidean/Matrix3D.cs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using MathNet.Numerics.LinearAlgebra;
32
using MathNet.Numerics.LinearAlgebra.Double;
43
using MathNet.Spatial.Units;
@@ -20,10 +19,10 @@ public static Matrix<double> RotationAroundXAxis(Angle angle)
2019
var rotationMatrix = new DenseMatrix(3, 3)
2120
{
2221
[0, 0] = 1,
23-
[1, 1] = Math.Cos(angle.Radians),
24-
[1, 2] = -Math.Sin(angle.Radians),
25-
[2, 1] = Math.Sin(angle.Radians),
26-
[2, 2] = Math.Cos(angle.Radians)
22+
[1, 1] = angle.Cos,
23+
[1, 2] = -angle.Sin,
24+
[2, 1] = angle.Sin,
25+
[2, 2] = angle.Cos
2726
};
2827
return rotationMatrix;
2928
}
@@ -37,11 +36,11 @@ public static Matrix<double> RotationAroundYAxis(Angle angle)
3736
{
3837
var rotationMatrix = new DenseMatrix(3, 3)
3938
{
40-
[0, 0] = Math.Cos(angle.Radians),
41-
[0, 2] = Math.Sin(angle.Radians),
39+
[0, 0] = angle.Cos,
40+
[0, 2] = angle.Sin,
4241
[1, 1] = 1,
43-
[2, 0] = -Math.Sin(angle.Radians),
44-
[2, 2] = Math.Cos(angle.Radians)
42+
[2, 0] = -angle.Sin,
43+
[2, 2] = angle.Cos
4544
};
4645
return rotationMatrix;
4746
}
@@ -55,10 +54,10 @@ public static Matrix<double> RotationAroundZAxis(Angle angle)
5554
{
5655
var rotationMatrix = new DenseMatrix(3, 3)
5756
{
58-
[0, 0] = Math.Cos(angle.Radians),
59-
[0, 1] = -Math.Sin(angle.Radians),
60-
[1, 0] = Math.Sin(angle.Radians),
61-
[1, 1] = Math.Cos(angle.Radians),
57+
[0, 0] = angle.Cos,
58+
[0, 1] = -angle.Sin,
59+
[1, 0] = angle.Sin,
60+
[1, 1] = angle.Cos,
6261
[2, 2] = 1
6362
};
6463
return rotationMatrix;
@@ -123,9 +122,9 @@ public static Matrix<double> RotationAroundArbitraryVector(UnitVector3D aboutVec
123122
var unitTensorProduct = aboutVector.GetUnitTensorProduct();
124123
var crossProductMatrix = aboutVector.CrossProductMatrix;
125124

126-
var r1 = DenseMatrix.CreateIdentity(3).Multiply(Math.Cos(angle.Radians));
127-
var r2 = crossProductMatrix.Multiply(Math.Sin(angle.Radians));
128-
var r3 = unitTensorProduct.Multiply(1 - Math.Cos(angle.Radians));
125+
var r1 = DenseMatrix.CreateIdentity(3).Multiply(angle.Cos);
126+
var r2 = crossProductMatrix.Multiply(angle.Sin);
127+
var r3 = unitTensorProduct.Multiply(1 - angle.Cos);
129128
var totalR = r1.Add(r2).Add(r3);
130129
return totalR;
131130
}

src/Spatial/Euclidean/Point2D.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ public static Point2D FromPolar(double radius, Angle angle)
116116
}
117117

118118
return new Point2D(
119-
radius * Math.Cos(angle.Radians),
120-
radius * Math.Sin(angle.Radians));
119+
radius * angle.Cos,
120+
radius * angle.Sin);
121121
}
122122

123123
/// <summary>

src/Spatial/Euclidean/Vector2D.cs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public Vector2D(double x, double y)
5353
/// Gets the length of the vector
5454
/// </summary>
5555
[Pure]
56-
public double Length => Math.Sqrt((X * X) + (Y * Y));
56+
public double Length => Math.Sqrt(X * X + Y * Y);
5757

5858
/// <summary>
5959
/// Gets a vector orthogonal to this
@@ -168,8 +168,8 @@ public static Vector2D FromPolar(double radius, Angle angle)
168168
}
169169

170170
return new Vector2D(
171-
radius * Math.Cos(angle.Radians),
172-
radius * Math.Sin(angle.Radians));
171+
radius * angle.Cos,
172+
radius * angle.Sin);
173173
}
174174

175175
/// <summary>
@@ -305,8 +305,7 @@ public bool IsPerpendicularTo(Vector2D other, double tolerance = 1e-10)
305305
public bool IsPerpendicularTo(Vector2D other, Angle tolerance)
306306
{
307307
var angle = AngleTo(other);
308-
const double Perpendicular = Math.PI / 2;
309-
return Math.Abs(angle.Radians - Perpendicular) < tolerance.Radians;
308+
return (angle - Angle.HalfPi).Abs() < tolerance;
310309
}
311310

312311
/// <summary>
@@ -366,8 +365,8 @@ public Angle AngleTo(Vector2D other)
366365
return Angle.FromRadians(
367366
Math.Abs(
368367
Math.Atan2(
369-
(X * other.Y) - (other.X * Y),
370-
(X * other.X) + (Y * other.Y))));
368+
X * other.Y - other.X * Y,
369+
X * other.X + Y * other.Y)));
371370
}
372371

373372
/// <summary>
@@ -378,10 +377,10 @@ public Angle AngleTo(Vector2D other)
378377
[Pure]
379378
public Vector2D Rotate(Angle angle)
380379
{
381-
var cs = Math.Cos(angle.Radians);
382-
var sn = Math.Sin(angle.Radians);
383-
var x = (X * cs) - (Y * sn);
384-
var y = (X * sn) + (Y * cs);
380+
var cs = angle.Cos;
381+
var sn = angle.Sin;
382+
var x = X * cs - Y * sn;
383+
var y = X * sn + Y * cs;
385384
return new Vector2D(x, y);
386385
}
387386

@@ -393,7 +392,7 @@ public Vector2D Rotate(Angle angle)
393392
[Pure]
394393
public double DotProduct(Vector2D other)
395394
{
396-
return (X * other.X) + (Y * other.Y);
395+
return X * other.X + Y * other.Y;
397396
}
398397

399398
/// <summary>
@@ -408,7 +407,7 @@ public double CrossProduct(Vector2D other)
408407
{
409408
// Though the cross product is undefined in 2D space, this is a useful mathematical operation to
410409
// determine angular direction and to compute the area of 2D shapes
411-
return (X * other.Y) - (Y * other.X);
410+
return X * other.Y - Y * other.X;
412411
}
413412

414413
/// <summary>

src/Spatial/Projective/Matrix3DHomogeneous.cs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ public static Matrix3DHomogeneous CreateScale(double sx, double sy, double sz)
162162
public static Matrix3DHomogeneous RotationAroundXAxis(Angle angle)
163163
{
164164
var result = new Matrix3DHomogeneous();
165-
var sinAngle = Math.Sin(angle.Radians);
166-
var cosAngle = Math.Cos(angle.Radians);
165+
var sinAngle = angle.Sin;
166+
var cosAngle = angle.Cos;
167167
result.matrix[1, 1] = cosAngle;
168168
result.matrix[1, 2] = -sinAngle;
169169
result.matrix[2, 1] = sinAngle;
@@ -179,8 +179,8 @@ public static Matrix3DHomogeneous RotationAroundXAxis(Angle angle)
179179
public static Matrix3DHomogeneous RotationAroundYAxis(Angle angle)
180180
{
181181
var result = new Matrix3DHomogeneous();
182-
var sinAngle = Math.Sin(angle.Radians);
183-
var cosAngle = Math.Cos(angle.Radians);
182+
var sinAngle = angle.Sin;
183+
var cosAngle = angle.Cos;
184184
result.matrix[0, 0] = cosAngle;
185185
result.matrix[0, 2] = sinAngle;
186186
result.matrix[2, 0] = -sinAngle;
@@ -196,8 +196,8 @@ public static Matrix3DHomogeneous RotationAroundYAxis(Angle angle)
196196
public static Matrix3DHomogeneous RotationAroundZAxis(Angle angle)
197197
{
198198
var result = new Matrix3DHomogeneous();
199-
var sinAngle = Math.Sin(angle.Radians);
200-
var cosAngle = Math.Cos(angle.Radians);
199+
var sinAngle = angle.Sin;
200+
var cosAngle = angle.Cos;
201201
result.matrix[0, 0] = cosAngle;
202202
result.matrix[0, 1] = -sinAngle;
203203
result.matrix[1, 0] = sinAngle;
@@ -281,10 +281,10 @@ public static Matrix3DHomogeneous TopView()
281281
public static Matrix3DHomogeneous Axonometric(Angle alpha, Angle beta)
282282
{
283283
var result = new Matrix3DHomogeneous();
284-
var sna = Math.Sin(alpha.Radians);
285-
var cosAlpha = Math.Cos(alpha.Radians);
286-
var sinBeta = Math.Sin(beta.Radians);
287-
var cosBeta = Math.Cos(beta.Radians);
284+
var sna = alpha.Sin;
285+
var cosAlpha = alpha.Cos;
286+
var sinBeta = beta.Sin;
287+
var cosBeta = beta.Cos;
288288
result.matrix[0, 0] = cosBeta;
289289
result.matrix[0, 2] = sinBeta;
290290
result.matrix[1, 0] = sna * sinBeta;
@@ -303,9 +303,9 @@ public static Matrix3DHomogeneous Axonometric(Angle alpha, Angle beta)
303303
public static Matrix3DHomogeneous Oblique(Angle alpha, Angle theta)
304304
{
305305
var result = new Matrix3DHomogeneous();
306-
var tanAlpha = Math.Tan(alpha.Radians);
307-
var sinTheta = Math.Sin(theta.Radians);
308-
var cosTheta = Math.Cos(theta.Radians);
306+
var tanAlpha = alpha.Tan;
307+
var sinTheta = theta.Sin;
308+
var cosTheta = theta.Cos;
309309
result.matrix[0, 2] = -cosTheta / tanAlpha;
310310
result.matrix[1, 2] = -sinTheta / tanAlpha;
311311
result.matrix[2, 2] = 0;
@@ -323,12 +323,12 @@ public static Matrix3DHomogeneous Euler(Angle alpha, Angle beta, Angle gamma)
323323
{
324324
var result = new Matrix3DHomogeneous();
325325

326-
var sinAlpha = Math.Sin(alpha.Radians);
327-
var cosAlpha = Math.Cos(alpha.Radians);
328-
var sinBeta = Math.Sin(beta.Radians);
329-
var cosBeta = Math.Cos(beta.Radians);
330-
var sinGamma = Math.Sin(gamma.Radians);
331-
var cosGamma = Math.Cos(gamma.Radians);
326+
var sinAlpha = alpha.Sin;
327+
var cosAlpha = alpha.Cos;
328+
var sinBeta = beta.Sin;
329+
var cosBeta = beta.Cos;
330+
var sinGamma = gamma.Sin;
331+
var cosGamma = gamma.Cos;
332332

333333
result.matrix[0, 0] = (cosAlpha * cosGamma) - (sinAlpha * sinBeta * sinGamma);
334334
result.matrix[0, 1] = -sinBeta * sinGamma;

0 commit comments

Comments
 (0)