Skip to content

Commit 121b6ac

Browse files
authored
Merge pull request #399 from GSharker/mibi/dev/area-of-triangle
Area of a triangle
2 parents f01109a + 20ad4fb commit 121b6ac

File tree

4 files changed

+114
-106
lines changed

4 files changed

+114
-106
lines changed

.github/workflows/create-release.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ jobs:
2121
permissions:
2222
contents: write
2323
pull-requests: read
24-
env:
25-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2624
runs-on: ubuntu-latest
2725
steps:
2826
- name: 🛎 Checkout repo
@@ -31,8 +29,10 @@ jobs:
3129
fetch-depth: 0
3230

3331
- name: 👶 Draft release
34-
uses: release-drafter/release-drafter@v5
3532
id: release
33+
uses: release-drafter/release-drafter@v5
34+
env:
35+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3636

3737
- name: 🚧 Setup .NET Core
3838
uses: actions/setup-dotnet@v3
Lines changed: 71 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,86 @@
1-
using FluentAssertions;
1+
using System.Collections.Generic;
2+
using FluentAssertions;
23
using GShark.Core;
34
using GShark.Geometry;
4-
using System.Collections.Generic;
5-
using GShark.Operation;
65
using Xunit;
76

8-
namespace GShark.Test.XUnit.Core
7+
namespace GShark.Test.XUnit.Core;
8+
9+
public class TrigonometryTests
910
{
10-
public class TrigonometryTests
11+
[Fact]
12+
public void It_Returns_True_If_Points_Are_Planar()
13+
{
14+
// Arrange
15+
Point3 pt1 = new Point3(0.0, 0.0, 0.0);
16+
Point3 pt2 = new Point3(10.0, 0.0, 0.0);
17+
Point3 pt3 = new Point3(5.0, 5.0, 0.0);
18+
Point3 pt4 = new Point3(-5.0, -15.0, 0.0);
19+
List<Point3> points = new List<Point3> {pt1, pt2, pt3, pt4};
20+
21+
// Arrange
22+
Trigonometry.ArePointsCoplanar(points).Should().BeTrue();
23+
}
24+
25+
[Fact]
26+
public void It_Returns_True_If_Three_Points_Are_Collinear()
1127
{
12-
[Fact]
13-
public void It_Returns_True_If_Points_Are_Planar()
14-
{
15-
// Arrange
16-
Point3 pt1 = new Point3(0.0, 0.0, 0.0);
17-
Point3 pt2 = new Point3(10.0, 0.0, 0.0);
18-
Point3 pt3 = new Point3(5.0, 5.0, 0.0);
19-
Point3 pt4 = new Point3(-5.0, -15.0, 0.0);
20-
List<Point3> points = new List<Point3> { pt1, pt2, pt3, pt4 };
28+
// Arrange
29+
Point3 pt1 = new Point3(25.923, 27.057, 0.0);
30+
Point3 pt2 = new Point3(35.964, 31.367, 0.0);
31+
Point3 pt3 = new Point3(51.299, 37.950, 0.0);
32+
33+
// Assert
34+
Trigonometry.ArePointsCollinear(pt1, pt2, pt3).Should().BeTrue();
35+
}
2136

22-
// Arrange
23-
Trigonometry.ArePointsCoplanar(points).Should().BeTrue();
24-
}
37+
[Fact]
38+
public void It_Returns_False_If_Three_Points_Are_Not_Collinear()
39+
{
40+
// Arrange
41+
Point3 pt1 = new Point3(25.923, 27.057, 0.0);
42+
Point3 pt2 = new Point3(35.964, 20.451, 0.0);
43+
Point3 pt3 = new Point3(51.299, 37.950, 0.0);
2544

26-
[Fact]
27-
public void It_Returns_True_If_Three_Points_Are_Collinear()
28-
{
29-
// Arrange
30-
Point3 pt1 = new Point3(25.923, 27.057, 0.0);
31-
Point3 pt2 = new Point3(35.964, 31.367, 0.0);
32-
Point3 pt3 = new Point3(51.299, 37.950, 0.0);
45+
// Assert
46+
Trigonometry.ArePointsCollinear(pt1, pt2, pt3).Should().BeFalse();
47+
}
3348

34-
// Assert
35-
Trigonometry.ArePointsCollinear(pt1, pt2, pt3).Should().BeTrue();
36-
}
49+
[Theory]
50+
[InlineData(new double[] {5, 7, 0}, new double[] {6, 6, 0}, 0.2)]
51+
[InlineData(new double[] {7, 6, 0}, new[] {6.5, 6.5, 0}, 0.3)]
52+
[InlineData(new double[] {5, 9, 0}, new double[] {7, 7, 0}, 0.4)]
53+
public void It_Returns_The_Closest_Point_On_A_Segment(double[] ptToCheck, double[] ptExpected, double tValExpected)
54+
{
55+
// Arrange
56+
// We are not passing a segment like a line or ray but the part that compose the segment,
57+
// t values [0 and 1] and start and end point.
58+
var testPt = new Point3(ptToCheck[0], ptToCheck[1], ptToCheck[2]);
59+
var expectedPt = new Point3(ptExpected[0], ptExpected[1], ptExpected[2]);
60+
Point3 pt0 = new Point3(5, 5, 0);
61+
Point3 pt1 = new Point3(10, 10, 0);
3762

38-
[Fact]
39-
public void It_Returns_False_If_Three_Points_Are_Not_Collinear()
40-
{
41-
// Arrange
42-
Point3 pt1 = new Point3(25.923, 27.057, 0.0);
43-
Point3 pt2 = new Point3(35.964, 20.451, 0.0);
44-
Point3 pt3 = new Point3(51.299, 37.950, 0.0);
63+
// Act
64+
(double tValue, Point3 pt) closestPt = Trigonometry.ClosestPointToSegment(testPt, pt0, pt1, 0, 1);
4565

46-
// Assert
47-
Trigonometry.ArePointsCollinear(pt1, pt2, pt3).Should().BeFalse();
48-
}
66+
// Assert
67+
closestPt.tValue.Should().BeApproximately(tValExpected, GSharkMath.MaxTolerance);
68+
closestPt.pt.EpsilonEquals(expectedPt, GSharkMath.Epsilon).Should().BeTrue();
69+
}
4970

50-
[Theory]
51-
[InlineData(new double[] { 5, 7, 0 }, new double[] { 6, 6, 0 }, 0.2)]
52-
[InlineData(new double[] { 7, 6, 0 }, new double[] { 6.5, 6.5, 0 }, 0.3)]
53-
[InlineData(new double[] { 5, 9, 0 }, new double[] { 7, 7, 0 }, 0.4)]
54-
public void It_Returns_The_Closest_Point_On_A_Segment(double[] ptToCheck, double[] ptExpected, double tValExpected)
55-
{
56-
// Arrange
57-
// We are not passing a segment like a line or ray but the part that compose the segment,
58-
// t values [0 and 1] and start and end point.
59-
var testPt = new Point3(ptToCheck[0], ptToCheck[1], ptToCheck[2]);
60-
var expectedPt = new Point3(ptExpected[0], ptExpected[1], ptExpected[2]);
61-
Point3 pt0 = new Point3(5, 5, 0);
62-
Point3 pt1 = new Point3(10, 10, 0);
71+
[Fact]
72+
public void It_Returns_The_Area_Of_A_Triangle_From_A_Polyline()
73+
{
74+
// Arrange
75+
Point3 pt1 = new Point3(-4.27, 5.90, 5.76);
76+
Point3 pt2 = new Point3(-10, -7.69, 0.0);
77+
Point3 pt3 = new Point3(9.93, -2.65, -5.57);
78+
double expectedArea = 150.865499;
6379

64-
// Act
65-
(double tValue, Point3 pt) closestPt = Trigonometry.ClosestPointToSegment(testPt, pt0, pt1, 0, 1);
80+
// Act
81+
double area = Trigonometry.AreaOfTriangle(pt1, pt2, pt3);
6682

67-
// Assert
68-
closestPt.tValue.Should().BeApproximately(tValExpected, GSharkMath.MaxTolerance);
69-
closestPt.pt.EpsilonEquals(expectedPt, GSharkMath.Epsilon).Should().BeTrue();
70-
}
83+
// Assert
84+
area.Should().BeApproximately(expectedArea, GSharkMath.MaxTolerance);
7185
}
72-
}
86+
}

src/GShark.Test.XUnit/GShark.Test.XUnit.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
</PropertyGroup>
1919

2020
<ItemGroup>
21-
<PackageReference Include="FluentAssertions" Version="5.10.3" />
22-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
21+
<PackageReference Include="FluentAssertions" Version="6.8.0" />
22+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
2323
<PackageReference Include="xunit" Version="2.4.1" />
2424
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
2525
<PrivateAssets>all</PrivateAssets>

src/GShark/Core/Trigonometry.cs

Lines changed: 38 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,24 @@
1-
using GShark.Geometry;
2-
using System;
1+
using System;
32
using System.Collections.Generic;
4-
using System.Drawing;
5-
using System.Linq;
3+
using GShark.Geometry;
64

75
namespace GShark.Core
86
{
97
/// <summary>
10-
/// Provides basic trigonometry methods.
8+
/// Provides basic trigonometry methods.
119
/// </summary>
1210
public class Trigonometry
1311
{
1412
/// <summary>
15-
/// Determines if the provide points are on the same plane within specified tolerance.
13+
/// Determines if the given points lie on the same plane within the specified tolerance.
1614
/// </summary>
1715
/// <param name="points">Points to check.</param>
1816
/// <param name="tolerance">Tolerance.</param>
1917
/// <returns>True if all points are coplanar.</returns>
2018
public static bool ArePointsCoplanar(IList<Point3> points, double tolerance = GSharkMath.Epsilon)
2119
{
2220
// https://en.wikipedia.org/wiki/Triple_product
23-
if (points.Count <= 3)
24-
{
25-
return true;
26-
}
21+
if (points.Count <= 3) return true;
2722

2823
var vec1 = points[1] - points[0];
2924
var vec2 = points[2] - points[0];
@@ -32,19 +27,16 @@ public static bool ArePointsCoplanar(IList<Point3> points, double tolerance = GS
3227
{
3328
var vec3 = points[i] - points[0];
3429
double tripleProduct = Vector3.DotProduct(Vector3.CrossProduct(vec3, vec2), vec1);
35-
if (Math.Abs(tripleProduct) > GSharkMath.Epsilon)
36-
{
37-
return false;
38-
}
30+
if (Math.Abs(tripleProduct) > GSharkMath.Epsilon) return false;
3931
}
4032

4133
return true;
4234
}
4335

4436
/// <summary>
45-
/// Determines if three points form a straight line (are collinear) within a given tolerance.<br/>
46-
/// Find the area of the triangle without using square root and multiply it for 0.5.<br/>
47-
/// http://www.stumblingrobot.com/2016/05/01/use-cross-product-compute-area-triangles-given-vertices/
37+
/// Determines if three points form a straight line (are collinear) within a given tolerance.<br />
38+
/// Find the area of the triangle without using square root and multiply it for 0.5.<br />
39+
/// http://www.stumblingrobot.com/2016/05/01/use-cross-product-compute-area-triangles-given-vertices/
4840
/// </summary>
4941
/// <param name="pt1">First point.</param>
5042
/// <param name="pt2">Second point.</param>
@@ -62,7 +54,8 @@ public static bool ArePointsCollinear(Point3 pt1, Point3 pt2, Point3 pt3, double
6254
}
6355

6456
/// <summary>
65-
/// Calculates the point at the equal distance from the three points, it can be also described as the center of a circle.
57+
/// Calculates the point at the equal distance from the three points, it can be also described as the center of a
58+
/// circle.
6659
/// </summary>
6760
/// <param name="pt1">First point.</param>
6861
/// <param name="pt2">Second point.</param>
@@ -88,8 +81,8 @@ public static Point3 EquidistantPoint(Point3 pt1, Point3 pt2, Point3 pt3)
8881
}
8982

9083
/// <summary>
91-
/// Gets the orientation between tree points in the plane.<br/>
92-
/// https://math.stackexchange.com/questions/2386810/orientation-of-three-points-in-3d-space
84+
/// Gets the orientation between three points in the plane.<br />
85+
/// https://math.stackexchange.com/questions/2386810/orientation-of-three-points-in-3d-space
9386
/// </summary>
9487
/// <param name="pt1">First point.</param>
9588
/// <param name="pt2">Second point.</param>
@@ -101,54 +94,55 @@ public static int PointOrder(Point3 pt1, Point3 pt2, Point3 pt3)
10194
Vector3 n = Vector3.CrossProduct(pt2 - pt1, pt3 - pt1);
10295
double result = Vector3.DotProduct(pl.ZAxis, n.Unitize());
10396

104-
if (Math.Abs(result) < GSharkMath.Epsilon)
105-
{
106-
return 0;
107-
}
97+
if (Math.Abs(result) < GSharkMath.Epsilon) return 0;
10898

109-
return (result < 0) ? 1 : 2;
99+
return result < 0 ? 1 : 2;
110100
}
111101

112102
/// <summary>
113-
/// Finds the closest point on a segment.<br/>
114-
/// It is not a segment like a line or ray but the part that compose the segment.<br/>
115-
/// The segment is deconstruct in two points and two t values.
103+
/// Finds the closest point on a segment.<br />
104+
/// It is not a segment like a line or ray but the part that compose the segment.<br />
105+
/// The segment is deconstruct in two points and two t values.
116106
/// </summary>
117107
/// <param name="point">Point to project.</param>
118108
/// <param name="segmentPt0">First point of the segment.</param>
119109
/// <param name="segmentPt1">Second point of the segment.</param>
120110
/// <param name="valueT0">First t value of the segment.</param>
121111
/// <param name="valueT1">Second t value of the segment.</param>
122112
/// <returns>Tuple with the closest point its corresponding tValue on the curve.</returns>
123-
public static (double tValue, Point3 pt) ClosestPointToSegment(Point3 point, Point3 segmentPt0, Point3 segmentPt1, double valueT0, double valueT1)
113+
public static (double tValue, Point3 pt) ClosestPointToSegment(Point3 point, Point3 segmentPt0,
114+
Point3 segmentPt1, double valueT0, double valueT1)
124115
{
125116
Vector3 direction = segmentPt1 - segmentPt0;
126117
double length = direction.Length;
127118

128-
if (length < GSharkMath.Epsilon)
129-
{
130-
return (tValue: valueT0, pt: segmentPt0);
131-
}
119+
if (length < GSharkMath.Epsilon) return (tValue: valueT0, pt: segmentPt0);
132120

133121
Vector3 vecUnitized = direction.Unitize();
134122
Vector3 ptToSegPt0 = point - segmentPt0;
135123
double dotResult = Vector3.DotProduct(ptToSegPt0, vecUnitized);
136124

137-
if (dotResult < 0.0)
138-
{
139-
return (tValue: valueT0, pt: segmentPt0);
140-
}
125+
if (dotResult < 0.0) return (tValue: valueT0, pt: segmentPt0);
141126

142-
if (dotResult > length)
143-
{
144-
return (tValue: valueT1, pt: segmentPt1);
145-
}
127+
if (dotResult > length) return (tValue: valueT1, pt: segmentPt1);
146128

147-
Point3 pointResult = segmentPt0 + (vecUnitized * dotResult);
129+
Point3 pointResult = segmentPt0 + vecUnitized * dotResult;
148130
double tValueResult = valueT0 + (valueT1 - valueT0) * dotResult / length;
149131
return (tValue: tValueResult, pt: pointResult);
150132
}
151133

152-
134+
/// <summary>
135+
/// Calculates the area of a triangle.
136+
/// </summary>
137+
/// <param name="pt1">First point of the triangle.</param>
138+
/// <param name="pt2">Second point of the triangle.</param>
139+
/// <param name="pt3">Third point of the triangle.</param>
140+
/// <returns>The area of the triangle.</returns>
141+
public static double AreaOfTriangle(Point3 pt1, Point3 pt2, Point3 pt3)
142+
{
143+
Vector3 v1 = pt1 - pt2;
144+
Vector3 v2 = pt1 - pt3;
145+
return 0.5 * Vector3.CrossProduct(v1, v2).Length;
146+
}
153147
}
154-
}
148+
}

0 commit comments

Comments
 (0)