Skip to content

Commit

Permalink
Added pointAtLength and tangentAtLength for arc.
Browse files Browse the repository at this point in the history
  • Loading branch information
sonomirco committed Aug 20, 2021
1 parent c80b44e commit 3449b97
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 37 deletions.
44 changes: 40 additions & 4 deletions src/GShark.Test.XUnit/Geometry/ArcTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ public void It_Returns_The_BoundingBox_Of_The_Arc()
}

[Theory]
[InlineData(1.2, new double[] { 70.334926, 18.808863, 2.762032 })]
[InlineData(2.5, new double[] { 87.505564, 8.962333, 5.203339 })]
[InlineData(0.15, new double[] { 69.334566, 27.421862, 0.510428 })]
[InlineData(0.5, new double[] { 82.012463, 9.246222, 5.174356 })]
[InlineData(0.72, new double[] { 96.621782, 12.93396, 4.08574 })]
public void It_Returns_A_Point_On_The_Arc_At_The_Given_Parameter(double t, double[] pts)
{
// Arrange
Expand All @@ -109,6 +110,40 @@ public void It_Returns_A_Point_On_The_Arc_At_The_Given_Parameter(double t, doubl
pt.EpsilonEquals(expectedPt, 1e-6).Should().BeTrue();
}

[Theory]
[InlineData(10, new double[] { 69.463068, 28.087104, 0.334816 })]
[InlineData(17.5, new double[] { 69.624396, 20.896489, 2.220164 })]
[InlineData(22.5, new double[] { 71.568605, 16.456984, 3.368902 })]
public void It_Returns_A_Point_On_The_Arc_At_The_Given_Length(double length, double[] pts)
{
// Arrange
var expectedPt = new Point3(pts[0], pts[1], pts[2]);
Arc arc = _exampleArc3D;

// Act
var pt = arc.PointAtLength(length);

// Assert
pt.EpsilonEquals(expectedPt, 1e-6).Should().BeTrue();
}

[Theory]
[InlineData(10, new double[] { -0.20443, -0.946415, 0.250015 })]
[InlineData(17.5, new double[] { 0.246705, -0.937861, 0.244034 })]
[InlineData(22.5, new double[] { 0.525002, -0.824293, 0.21193 })]
public void It_Returns_A_Tangent_On_The_Arc_At_The_Given_Length(double length, double[] tangent)
{
// Arrange
var expectedTan = new Vector3(tangent[0], tangent[1], tangent[2]);
Arc arc = _exampleArc3D;

// Act
var tan = arc.TangentAtLength(length);

// Assert
tan.EpsilonEquals(expectedTan, 1e-6).Should().BeTrue();
}

[Theory]
[InlineData(new double[] { 82.248292, 15.836914, 3.443127 }, new double[] { 80.001066, 9.815219, 5.041724 })]
[InlineData(new double[] { 85.591741, 24.79606, 1.064717 }, new double[] { 74.264416, 36.39316, -1.884313 })]
Expand Down Expand Up @@ -144,8 +179,9 @@ public void It_Returns_A_Transformed_Arc()
}

[Theory]
[InlineData(0.0, new double[] { -0.726183, -0.663492, 0.180104 })]
[InlineData(1.2, new double[] { 0.377597, -0.896416, 0.232075 })]
[InlineData(0.15, new double[] { -0.162674, -0.954043, 0.251671 })]
[InlineData(0.5, new double[] { 0.976088, -0.212106, 0.047567 })]
[InlineData(0.72, new double[] { 0.742847, 0.646006, -0.175654 })]
public void It_Returns_The_Tangent_At_The_Give_Parameter_T(double t, double[] pts)
{
// Arrange
Expand Down
125 changes: 92 additions & 33 deletions src/GShark/Geometry/Arc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using GShark.Operation;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

namespace GShark.Geometry
Expand Down Expand Up @@ -224,30 +225,81 @@ public static Arc ByStartEndDirection(Point3 ptStart, Point3 ptEnd, Vector3 dir)
}

/// <summary>
/// Returns the point at the parameter t on the arc.
/// Evaluates the point at the parameter t on the arc.
/// </summary>
/// <param name="t">A parameter between 0.0 to 1.0 or between the angle domain.></param>
/// <returns>Point on the arc.</returns>
public Point3 PointAt(double t)
{
Vector3 xDir = Plane.XAxis * Math.Cos(t) * Radius;
Vector3 yDir = Plane.YAxis * Math.Sin(t) * Radius;
if (t > 1.0 || t < 0.0)
{
throw new ArgumentOutOfRangeException($"The value provided for parameter t, {t}, must be between 0.0 and 1.0.");
}

double theta = Domain.T0 + (Domain.T1 - Domain.T0) * t;
Vector3 xDir = Plane.XAxis * Math.Cos(theta) * Radius;
Vector3 yDir = Plane.YAxis * Math.Sin(theta) * Radius;

return Plane.Origin + xDir + yDir;
}

/// <summary>
/// Returns the tangent at the parameter t on the arc.
/// Evaluates the point at the specific length.
/// </summary>
/// <param name="t">A parameter between 0.0 to 1.0 or between the angle domain.</param>
/// <param name="length">The length where to evaluate the point.</param>
/// <returns>The point at the length.</returns>
public Point3 PointAtLength(double length)
{
if (length < 0)
{
throw new Exception("Length factor cannot be less than zero.");
}

if (length > Length)
{
throw new Exception("Length factor cannot be larger than the length of the curve.");
}

double angleLength = GSharkMath.ToRadians((length * 360) / (Math.PI * 2 * Radius));

Vector3 xDir = Plane.XAxis * Math.Cos(angleLength) * Radius;
Vector3 yDir = Plane.YAxis * Math.Sin(angleLength) * Radius;

return Plane.Origin + xDir + yDir;
}

/// <summary>
/// Evaluates the tangent at the specific length.
/// </summary>
/// <param name="length">The length where to evaluate the tangent.</param>
/// <returns>The unitize tangent at the length.</returns>
public Vector3 TangentAtLength(double length)
{
Point3 pt = PointAtLength(length);
(double u, double v) = Plane.ClosestParameters(pt);
double t = EvaluateParameter(u, v, true);
return TangentAt(t);
}

/// <summary>
/// Calculates the tangent at the parameter t on the arc.
/// </summary>
/// <param name="t">A parameter between 0.0 to 1.0.</param>
/// <returns>Tangent vector at the t parameter.</returns>
public Vector3 TangentAt(double t)
{
if (t > 1.0 || t < 0.0)
{
throw new ArgumentOutOfRangeException($"The value provided, {t}, must be between 0.0 and 1.0.");
}

double theta = Domain.T0 + (Domain.T1 - Domain.T0) * t;

double r1 = Radius;
double r2 = Radius;

r1 *= -Math.Sin(t);
r2 *= Math.Cos(t);
r1 *= -Math.Sin(theta);
r2 *= Math.Cos(theta);

Vector3 vector = Plane.XAxis * r1 + Plane.YAxis * r2;

Expand All @@ -261,39 +313,15 @@ public Vector3 TangentAt(double t)
/// <returns>The point on the arc that is close to the test point.</returns>
public Point3 ClosestPoint(Point3 pt)
{
double twoPi = 2.0 * Math.PI;

(double u, double v) = Plane.ClosestParameters(pt);
if (Math.Abs(u) < GSharkMath.MinTolerance && Math.Abs(v) < GSharkMath.MinTolerance)
{
return PointAt(0.0);
}

double t = Math.Atan2(v, u);
if (t < 0.0)
{
t += twoPi;
}

t -= Domain.T0;

while (t < 0.0)
{
t += twoPi;
}
double t = EvaluateParameter(u, v, true);

while (t >= twoPi)
{
t -= twoPi;
}

double t1 = Domain.Length;
if (t > t1)
{
t = t > 0.5 * t1 + Math.PI ? 0.0 : t1;
}

return PointAt(Domain.T0 + t);
return PointAt(t);
}

/// <summary>
Expand Down Expand Up @@ -478,5 +506,36 @@ private static double AngularDiff(double theta1, double theta2)

return dif;
}

private double EvaluateParameter(double u, double v, bool parametrize = false)
{
double twoPi = 2.0 * Math.PI;

double t = Math.Atan2(v, u);
if (t < 0.0)
{
t += twoPi;
}

t -= Domain.T0;

while (t < 0.0)
{
t += twoPi;
}

while (t >= twoPi)
{
t -= twoPi;
}

double t1 = Domain.Length;
if (t > t1)
{
t = t > 0.5 * t1 + Math.PI ? 0.0 : t1;
}

return (parametrize) ? (t - Domain.T0) / (Domain.T1 - Domain.T0) : t;
}
}
}

0 comments on commit 3449b97

Please sign in to comment.