Skip to content

Commit

Permalink
Merge branch 'real-mid-and-more' of https://github.com/hypar-io/Elements
Browse files Browse the repository at this point in the history
 into real-mid-and-more
  • Loading branch information
jamesbradleym committed Nov 13, 2023
2 parents 0a255a7 + 4cac172 commit 132c945
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 268 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
- `Polyline` is now parameterized 0->length.
- `Polyline` now implements the `IHasArcLength` interface.
- `Arc` now inherits from `TrimmedCurve<Circle>`.
- `Arc` is now parameterized 0->2Pi
- `Arc` is now parameterized 0->2Pi.
- `Line` now inherits from `TrimmedCurve<InfiniteLine>`.
- `Line` is now parameterized 0->length.
- `Line` now implements the `IHasArcLength` interface.
Expand Down
77 changes: 77 additions & 0 deletions Elements/src/Geometry/Circle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,46 @@ private Vector3 PointAtUntransformed(double u)
return new Vector3(x, y);
}

/// <summary>
/// Calculates and returns the point on the circle at a specific arc length.
/// </summary>
/// <param name="length">The arc length along the circumference of the circle.</param>
/// <returns>The point on the circle at the specified arc length.</returns>
public Vector3 PointAtLength(double length)
{
double parameter = (length / Circumference) * 2 * Math.PI;
return PointAt(parameter);
}

/// <summary>
/// Calculates and returns the point on the circle at a normalized arc length.
/// </summary>
/// <param name="normalizedLength">The normalized arc length between 0 and 1.</param>
/// <returns>The point on the circle at the specified normalized arc length.</returns>
public Vector3 PointAtNormalizedLength(double normalizedLength)
{
double parameter = normalizedLength * 2 * Math.PI;
return PointAt(parameter);
}

/// <summary>
/// Calculates the parameter within the range of 0 to 2π at a given point on the circle.
/// </summary>
/// <param name="point">The point on the circle.</param>
/// <returns>The parameter within the range of 0 to 2π at the given point on the circle.</returns>
public double GetParameterAt(Vector3 point)
{
Vector3 relativePoint = point - Center;

double theta = Math.Atan2(relativePoint.Y, relativePoint.X);

if (theta < 0)
{
theta += 2 * Math.PI;
}
return theta;
}

/// <summary>
/// Return transform on the arc at parameter u.
/// </summary>
Expand Down Expand Up @@ -261,5 +301,42 @@ public override double ParameterAtDistanceFromParameter(double distance, double

return start + theta;
}

/// <summary>
/// Divides the circle into segments of the specified length and returns a list of points representing the division.
/// </summary>
/// <param name="length">The length of each segment.</param>
/// <returns>A list of points representing the division of the circle.</returns>
public Vector3[] DivideByLength(double length)
{
List<Vector3> points = new List<Vector3>();
double circumference = 2 * Math.PI * Radius;
int segmentCount = (int)Math.Ceiling(circumference / length);
double segmentLength = circumference / segmentCount;

for (int i = 0; i < segmentCount; i++)
{
double parameter = i * segmentLength / circumference;
points.Add(PointAtNormalizedLength(parameter));
}

return points.ToArray();
}

/// <summary>
/// Checks if a given point lies on a circle within a specified tolerance.
/// </summary>
/// <param name="point">The point to be checked.</param>
/// <param name="circle">The circle to check against.</param>
/// <param name="tolerance">The tolerance value (optional). Default is 1E-05.</param>
/// <returns>True if the point lies on the circle within the tolerance, otherwise false.</returns>
public static bool PointOnCircle(Vector3 point, Circle circle, double tolerance = 1E-05)
{
Vector3 centerToPoint = point - circle.Center;
double distanceToCenter = centerToPoint.Length();

// Check if the distance from the point to the center is within the tolerance of the circle's radius
return Math.Abs(distanceToCenter - circle.Radius) < tolerance;
}
}
}
267 changes: 0 additions & 267 deletions Elements/test/BezierTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,272 +67,5 @@ public void Bezier_Length_OffsetFromOrigin()
var polylineLength = bezier.ToPolyline(divisions).Length();
Assert.Equal(polylineLength, bezier.Length());
}

[Fact]
public void IntersectsLine()
{
var a = Vector3.Origin;
var b = new Vector3(0, 5);
var c = new Vector3(5, 5);
var d = new Vector3(5, 0);
var ctrlPts = new List<Vector3> { a, b, c, d };
var bezier = new Bezier(ctrlPts);

var line = new Line(new Vector3(0, 2), new Vector3(5, 2));
Assert.True(bezier.Intersects(line, out var results));
Assert.Equal(2, results.Count);
Assert.All(results, r => Assert.True(r.DistanceTo(line) < Vector3.EPSILON));

line = new Line(new Vector3(5, 0, 5), new Vector3(5, 0, 0));
Assert.True(bezier.Intersects(line, out results));
Assert.Single(results);
Assert.Contains(new Vector3(5, 0), results);

line = new Line(new Vector3(5, 5), new Vector3(0, 5));
Assert.False(bezier.Intersects(line, out results));
}

[Fact]
public void IntersectsCircle()
{
var a = Vector3.Origin;
var b = new Vector3(0, 5);
var c = new Vector3(5, 5);
var d = new Vector3(5, 0);
var ctrlPts = new List<Vector3> { a, b, c, d };
var bezier = new Bezier(ctrlPts);

var arc = new Arc(new Vector3(2.5, 2.5), 2.5, 180, 270);
Assert.True(bezier.Intersects(arc, out var results));
Assert.Single(results);

Transform t = new Transform(new Vector3(2.5, 0), Vector3.YAxis);
arc = new Arc(new Vector3(2.5, 0), 2.5, 0, -180);
Assert.True(bezier.Intersects(arc, out results));
Assert.Equal(2, results.Count);
Assert.Contains(new Vector3(5, 0), results);
Assert.Contains(new Vector3(0, 0), results);
}

[Fact]
public void IntersectsEllipse()
{
var a = Vector3.Origin;
var b = new Vector3(0, 5);
var c = new Vector3(5, 5);
var d = new Vector3(5, 0);
var ctrlPts = new List<Vector3> { a, b, c, d };
var bezier = new Bezier(ctrlPts);

var arc = new EllipticalArc(new Vector3(2.5, 2), 3.5, 1, 0, 270);
Assert.True(bezier.Intersects(arc, out var results));
Assert.Equal(3, results.Count);

arc = new EllipticalArc(new Vector3(2.5, 0), 2.5, 2, 0, -180);
Assert.True(bezier.Intersects(arc, out results));
Assert.Equal(2, results.Count);
Assert.Contains(new Vector3(5, 0), results);
Assert.Contains(new Vector3(0, 0), results);
}

[Fact]
public void IntersectsPolycurve()
{
var a = Vector3.Origin;
var b = new Vector3(0, 5);
var c = new Vector3(5, 5);
var d = new Vector3(5, 0);
var ctrlPts = new List<Vector3> { a, b, c, d };
var bezier = new Bezier(ctrlPts);

var polygon = new Polygon(new Vector3[]{
(0, 3), (6, 3), (4, 1), (-2, 1)
});

Assert.True(bezier.Intersects(polygon, out var results));
Assert.Equal(4, results.Count);
Assert.Contains(new Vector3(0.93475, 3), results);
Assert.Contains(new Vector3(4.06525, 3), results);
Assert.Contains(new Vector3(4.75132, 1.75133), results);
Assert.Contains(new Vector3(0.07367, 1), results);
}

[Fact]
public void IntersectsBezier()
{
var a = Vector3.Origin;
var b = new Vector3(0, 5);
var c = new Vector3(5, 5);
var d = new Vector3(5, 0);
var ctrlPts = new List<Vector3> { a, b, c, d };
var bezier = new Bezier(ctrlPts);

var other = new Bezier(new List<Vector3> { b, a, d, c });
Assert.True(bezier.Intersects(other, out var results));
Assert.Equal(2, results.Count);
Assert.Contains(new Vector3(0.5755, 2.5), results);
Assert.Contains(new Vector3(4.4245, 2.5), results);
}

public void Bezier_ArcLength()
{
var a = new Vector3(50, 150, 0);
var b = new Vector3(105, 66, 0);
var c = new Vector3(170, 230, 0);
var d = new Vector3(200, 150, 0);
var ctrlPts = new List<Vector3> { a, b, c, d };
var bezier = new Bezier(ctrlPts);

var expectedLength = 184.38886379602502; // approximation as the integral function used for calculating length is not 100% accurate
Assert.Equal(expectedLength, bezier.ArcLength(0.0, 1.0), 2);
}

[Fact]
public void GetParameterAt()
{
var tolerance = 0.00001;

var a = new Vector3(1, 5, 0);
var b = new Vector3(5, 20, 0);
var c = new Vector3(5, -10, 0);
var d = new Vector3(9, 5, 0);
var ctrlPts = new List<Vector3> { a, b, c, d };
var bezier = new Bezier(ctrlPts);

var samplePt = new Vector3(0, 0, 0);
Assert.Null(bezier.ParameterAt(samplePt, tolerance));

samplePt = new Vector3(6.625, 0.7812, 0.0);
Assert.True((double)bezier.ParameterAt(samplePt, tolerance) - 0.75 <= tolerance * 10);
}

[Fact]
public void GetPointAt()
{
var a = new Vector3(1, 5, 0);
var b = new Vector3(5, 20, 0);
var c = new Vector3(5, -10, 0);
var d = new Vector3(9, 5, 0);
var ctrlPts = new List<Vector3> { a, b, c, d };
var bezier = new Bezier(ctrlPts);

var testPt = new Vector3(3.3750, 9.21875, 0.0000);
Assert.True(testPt.Equals(bezier.PointAt(0.25)));

testPt = new Vector3(4.699, 6.11375, 0.0000);
Assert.True(testPt.Equals(bezier.PointAtNormalized(0.45)));

testPt = new Vector3(4.515904, 6.75392, 0.0000);
Assert.True(testPt.Equals(bezier.PointAtLength(8.0)));

testPt = new Vector3(3.048823, 9.329262, 0.0000);
Assert.True(testPt.Equals(bezier.PointAtNormalizedLength(0.25)));
}

[Fact]
public void DivideByLength()
{
var a = new Vector3(50, 150, 0);
var b = new Vector3(105, 66, 0);
var c = new Vector3(170, 230, 0);
var d = new Vector3(200, 150, 0);
var ctrlPts = new List<Vector3> { a, b, c, d };
var bezier = new Bezier(ctrlPts);

var testPts = new List<Vector3>(){
new Vector3(50.00, 150.00, 0.00),
new Vector3(90.705919, 125.572992, 0.00),
new Vector3(134.607122, 148.677130, 0.00),
new Vector3(177.600231, 172.675064, 0.00),
new Vector3(200.00, 150.00, 0.00),
};

var ptsFromBezier = bezier.DivideByLength(50.0);

for (int i = 0; i < ptsFromBezier.Length; i++)
{
Assert.True(ptsFromBezier[i].Equals(testPts[i]));
}
}

[Fact]
public void Split()
{
var a = new Vector3(50, 150, 0);
var b = new Vector3(105, 66, 0);
var c = new Vector3(170, 230, 0);
var d = new Vector3(200, 150, 0);
var ctrlPts = new List<Vector3> { a, b, c, d };
var bezier = new Bezier(ctrlPts);

var testBeziers = new List<Bezier>(){
new Bezier(new List<Vector3>() {
new Vector3(50.00, 150.00, 0.00),
new Vector3(63.75, 129.00, 0.00),
new Vector3(78.1250, 123.50, 0.00),
new Vector3(92.421875, 125.8125, 0.00)
}
),
new Bezier(new List<Vector3>() {
new Vector3(92.421875, 125.8125, 0.00),
new Vector3(121.015625, 130.4375, 0.00),
new Vector3(149.296875, 166.3125, 0.00),
new Vector3(171.640625, 171.9375, 0.00)
}
),
new Bezier(new List<Vector3>() {
new Vector3(171.640625, 171.9375, 0.00),
new Vector3(182.8125, 174.7500, 0.00),
new Vector3(192.50, 170.00, 0.00),
new Vector3(200.00, 150.00, 0.00)
}
),
};

var beziers = bezier.Split(new List<double>() { 0.25, 0.75 });

for (int i = 0; i < beziers.Count; i++)
{
Assert.True(beziers[i].ControlPoints[0].Equals(testBeziers[i].ControlPoints[0]));
Assert.True(beziers[i].ControlPoints[1].Equals(testBeziers[i].ControlPoints[1]));
Assert.True(beziers[i].ControlPoints[2].Equals(testBeziers[i].ControlPoints[2]));
Assert.True(beziers[i].ControlPoints[3].Equals(testBeziers[i].ControlPoints[3]));
}

testBeziers = new List<Bezier>(){
new Bezier(new List<Vector3>() {
new Vector3(50.00, 150.00, 0.00),
new Vector3(61.88, 131.856, 0.00),
new Vector3(74.22656, 125.282688, 0.00),
new Vector3(86.586183, 125.321837, 0.00)
}
),
new Bezier(new List<Vector3>() {
new Vector3(86.586183, 125.321837, 0.00),
new Vector3(114.738659, 125.411011, 0.00),
new Vector3(142.958913, 159.807432, 0.00),
new Vector3(165.887648, 169.916119, 0.00)
}
),
new Bezier(new List<Vector3>() {
new Vector3(165.887648, 169.916119, 0.00),
new Vector3(179.49576, 175.915584, 0.00),
new Vector3(191.24, 173.36, 0.00),
new Vector3(200.00, 150.00, 0.00)
}
),
};

var normalizedBeziers = bezier.Split(new List<double>() { 0.25, 0.75 }, true);

for (int i = 0; i < normalizedBeziers.Count; i++)
{
Assert.True(normalizedBeziers[i].ControlPoints[0].Equals(testBeziers[i].ControlPoints[0]));
Assert.True(normalizedBeziers[i].ControlPoints[1].Equals(testBeziers[i].ControlPoints[1]));
Assert.True(normalizedBeziers[i].ControlPoints[2].Equals(testBeziers[i].ControlPoints[2]));
Assert.True(normalizedBeziers[i].ControlPoints[3].Equals(testBeziers[i].ControlPoints[3]));
}
>>>>>>> 374fe346(Polish Bezier, IHasCurveLength)
}
}
}
5 changes: 5 additions & 0 deletions Elements/test/CircleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ public void AdjustRadian()
Assert.Equal(Units.DegreesToRadians(-100), Units.AdjustRadian(Units.DegreesToRadians(-100), reference), 6);
}

public CircleTests()
{
this.GenerateIfc = false;
}

[Fact, Trait("Category", "Examples")]
public void CircleExample()
{
Expand Down

0 comments on commit 132c945

Please sign in to comment.