Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Vector3.DistanceTo(Polygon). Add custom implementations of ToPolyline (issue #495) #1046

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
- `Profile.ThickenedEdgePolygons`
- `Elements.MEP`
- `GeometricElement.RepresentationInstances`
- Custom implementations of `ToPolyline` for `IndexedPolycurve`, `Polyline` and `Polygon`.

### Fixed

Expand All @@ -58,6 +59,7 @@
- `Polyline.Frames` now works correctly with `startSetbackDistance` and `endSetbackDistance` parameters.
- `Polygon.Frames` now works correctly with `startSetbackDistance` and `endSetbackDistance` parameters.
- `BoundedCurve.ToPolyline` now works correctly for `EllipticalArc` class.
- `Vector3.DistanceToDistanceTo(Polygon polygon, out Vector3 closestPoint)` now also checks the last edge of the polygon.


### Changed
Expand Down
11 changes: 10 additions & 1 deletion Elements/src/Geometry/BoundedCurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,21 @@ public virtual Vector3 Mid()
return transforms;
}

/// <summary>
/// Create a polyline through a set of 10 segments along the curve.
/// </summary>
/// <returns>A polyline.</returns>
public virtual Polyline ToPolyline()
{
return ToPolyline(10);
}

/// <summary>
/// Create a polyline through a set of points along the curve.
/// </summary>
/// <param name="divisions">The number of divisions of the curve.</param>
/// <returns>A polyline.</returns>
public virtual Polyline ToPolyline(int divisions = 10)
public virtual Polyline ToPolyline(int divisions)
{
var pts = new List<Vector3>(divisions + 1);
var step = this.Domain.Length / divisions;
Expand Down
88 changes: 88 additions & 0 deletions Elements/src/Geometry/IndexedPolycurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,94 @@ internal static void CheckSelfIntersectionAndThrow(Transform t, IEnumerable<(Vec
}
}

/// <summary>
/// Create a polyline through curves of IndexedPolycurve, interpolating any curves that are not lines.
/// </summary>
/// <returns>A polyline.</returns>
public override Polyline ToPolyline()
{
List<Vector3> vertices = new List<Vector3>();
foreach (var curve in _curves)
{
if (curve is Line)
{
vertices.Add(curve.Start);
}
else
{
var pl = curve.ToPolyline();
vertices.AddRange(pl.Vertices.Take(pl.Vertices.Count - 1));
}
}
vertices.Add(End);
return new Polyline(vertices);
}

/// <summary>
/// Create a polyline through a set of points along the curve.
/// End points of curves are added first and then non straight curves are divided uniformly.
/// If number of divisions is less than number of curves - points are uniformly distributed
/// though whole domain, deviating heavily from original shape.
/// </summary>
/// <param name="divisions">The number of divisions of the curve.
/// This can lead to highly distorted result.</param>
/// <returns>A polyline.</returns>
public override Polyline ToPolyline(int divisions)
{
//
if (divisions < _curves.Count)
{
return base.ToPolyline(divisions);
}

double extraSplits = divisions - _curves.Count;
List<Vector3> vertices = new List<Vector3>(divisions + 1) { Start };
var numArcs = _curves.Count(c => !(c is Line));
if (numArcs > 0)
{
// If polycurve consists of lines and curves - excess divisions are uniformly distributed
// among curves as dividing the lines wont add any new details in Polyline.
foreach (var curve in _curves)
{
if (!(curve is Line))
{
var splitsPerArc = Math.Ceiling(1.0d * extraSplits / numArcs);
var splits = Math.Min(splitsPerArc, extraSplits);
var step = curve.Domain.Length / (splitsPerArc + 1);
for (int i = 1; i <= splitsPerArc; i++)
{
var t = curve.Domain.Min + i * step;
vertices.Add(curve.PointAt(t));
}
extraSplits -= splitsPerArc;
numArcs--;
}
vertices.Add(curve.End);
}
}
else
{
// If polycurve consists only of lines - divisions are distributed uniformly among them.
var linesLeft = _curves.Count;
foreach (var curve in _curves)
{
var splitsPerLine = Math.Ceiling(1.0d * extraSplits / linesLeft);
var splits = Math.Min(splitsPerLine, extraSplits);
var step = curve.Domain.Length / (splitsPerLine + 1);
for (int i = 1; i <= splitsPerLine; i++)
{
var t = curve.Domain.Min + i * step;
vertices.Add(curve.PointAt(t));
}
vertices.Add(curve.End);
extraSplits -= splitsPerLine;
linesLeft--;
}
}
vertices.Add(End);
return new Polyline(vertices);
}

/// <summary>
/// Get the enumerator for this indexed polycurve.
/// </summary>
Expand Down
12 changes: 12 additions & 0 deletions Elements/src/Geometry/Polygon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1823,6 +1823,18 @@ internal new static Line[] SegmentsInternal(IList<Vector3> vertices)
return lines;
}

/// <summary>
/// Create a polyline through vertices of a polygon.
/// </summary>
/// <returns>A polyline.</returns>
public override Polyline ToPolyline()
{
var vertices = new List<Vector3>(Vertices.Count + 1);
vertices.AddRange(Vertices);
vertices.Add(End);
return new Polyline(vertices);
}

/// <summary>
/// Reverse the direction of a polygon.
/// </summary>
Expand Down
9 changes: 9 additions & 0 deletions Elements/src/Geometry/Polyline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ public virtual Line[] Segments()
return SegmentsInternal(this.Vertices);
}

/// <summary>
/// Create a copy of a polyline.
/// </summary>
/// <returns>A polyline.</returns>
public override Polyline ToPolyline()
{
return new Polyline(Vertices.ToList());
}

/// <summary>
/// Get the transform at the specified parameter along the polyline.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion Elements/src/Geometry/Vector3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ public double DistanceTo(Polygon polygon, out Vector3 closestPoint)
}
else
{
return this.DistanceTo(new Polyline(polygon.Vertices), out closestPoint);
return this.DistanceTo(polygon as Polyline, out closestPoint);
}
}

Expand Down
59 changes: 59 additions & 0 deletions Elements/test/IndexedPolyCurveTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,5 +227,64 @@ public void GetSubdivisionParameters()
Assert.Equal(1.25, parameters[0]);
Assert.Equal(1.75, parameters[1]);
}

[Fact]
public void ToPolyyline()
{
var arc0 = new Arc(new Vector3(2.5, 5), 2.5, 0, 180);
var arc1 = new Arc(new Vector3(-2.5, 0), 2.5, 360, 180);
var a = new Vector3(5, 0, 0);
var b = new Vector3(5, 5, 0);
var c = arc0.Mid();
var d = new Vector3(0, 5, 0);
var e = Vector3.Origin;
var f = arc1.Mid();
var g = new Vector3(-5, 0, 0);
var vertices = new[] { a, b, c, d, e, f, g };
var indices = new[]{
new[]{0,1},
new[]{1,2,3},
new[]{3,4},
new[]{4,5,6}
};
var pc = new IndexedPolycurve(vertices, indices);

var pl = pc.ToPolyline(indices.Count());
Assert.Equal(5, pl.Vertices.Count);
Assert.True(a.IsAlmostEqualTo(pl.Vertices[0]));
Assert.True(b.IsAlmostEqualTo(pl.Vertices[1]));
Assert.True(d.IsAlmostEqualTo(pl.Vertices[2]));
Assert.True(e.IsAlmostEqualTo(pl.Vertices[3]));
Assert.True(g.IsAlmostEqualTo(pl.Vertices.Last()));

pl = pc.ToPolyline();
Assert.Equal(arc0.ToPolyline().Vertices.Count + arc1.ToPolyline().Vertices.Count + 1,
pl.Vertices.Count);
Assert.True(a.IsAlmostEqualTo(pl.Vertices[0]));
Assert.True(b.IsAlmostEqualTo(pl.Vertices[1]));
Assert.True(g.IsAlmostEqualTo(pl.Vertices.Last()));

pl = pc.ToPolyline(9);
Assert.Equal(10, pl.Vertices.Count);
Assert.True(a.IsAlmostEqualTo(pl.Vertices[0]));
Assert.True(b.IsAlmostEqualTo(pl.Vertices[1]));
Assert.True(arc0.PointAtNormalized(0.25).IsAlmostEqualTo(pl.Vertices[2]));
Assert.True(arc0.PointAtNormalized(0.50).IsAlmostEqualTo(pl.Vertices[3]));
Assert.True(arc0.PointAtNormalized(0.75).IsAlmostEqualTo(pl.Vertices[4]));
Assert.True(d.IsAlmostEqualTo(pl.Vertices[5]));
Assert.True(e.IsAlmostEqualTo(pl.Vertices[6]));
Assert.True(arc1.PointAtNormalized(1.0 / 3).IsAlmostEqualTo(pl.Vertices[7]));
Assert.True(arc1.PointAtNormalized(2.0 / 3).IsAlmostEqualTo(pl.Vertices[8]));
Assert.True(g.IsAlmostEqualTo(pl.Vertices[9]));

pl = pc.ToPolyline(5);
Assert.Equal(6, pl.Vertices.Count);
Assert.True(a.IsAlmostEqualTo(pl.Vertices[0]));
Assert.True(b.IsAlmostEqualTo(pl.Vertices[1]));
Assert.True(arc0.PointAtNormalized(0.50).IsAlmostEqualTo(pl.Vertices[2]));
Assert.True(d.IsAlmostEqualTo(pl.Vertices[3]));
Assert.True(e.IsAlmostEqualTo(pl.Vertices[4]));
Assert.True(g.IsAlmostEqualTo(pl.Vertices[5]));
}
}
}
41 changes: 41 additions & 0 deletions Elements/test/PolygonTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2391,5 +2391,46 @@ public void Frames()
Assert.Equal((1, 2), frames[3].Origin);
Assert.True(Vector3.XAxis.IsParallelTo(frames[3].ZAxis));
}

[Fact]
public void ToPolyline()
{
var L = Polygon.L(10, 15, 5);
var pl = L.ToPolyline();
Assert.Equal(L.Vertices.Count + 1, pl.Vertices.Count);
for (int i = 0; i < L.Vertices.Count; i++)
{
Assert.True(L.Vertices[i].IsAlmostEqualTo(pl.Vertices[i]));
}
Assert.True(pl.Vertices.Last().IsAlmostEqualTo(L.End));

pl = L.ToPolyline(4);
var segments = L.Segments();
Assert.Equal(5, pl.Vertices.Count);
Assert.True(L.Start.IsAlmostEqualTo(pl.Vertices[0]));
Assert.True(segments[1].Mid().IsAlmostEqualTo(pl.Vertices[1]));
Assert.True(L.Vertices[3].IsAlmostEqualTo(pl.Vertices[2]));
Assert.True(segments[4].Mid().IsAlmostEqualTo(pl.Vertices[3]));
Assert.True(L.End.IsAlmostEqualTo(pl.Vertices[4]));

pl = L.ToPolyline(13);
segments = L.Segments();
Assert.Equal(pl.Length(), L.Length());
Assert.Equal(14, pl.Vertices.Count);
Assert.True(L.Start.IsAlmostEqualTo(pl.Vertices[0]));
Assert.True(segments[0].PointAtNormalized(1.0 / 3).IsAlmostEqualTo(pl.Vertices[1]));
Assert.True(segments[0].PointAtNormalized(2.0 / 3).IsAlmostEqualTo(pl.Vertices[2]));
Assert.True(segments[1].Start.IsAlmostEqualTo(pl.Vertices[3]));
Assert.True(segments[1].Mid().IsAlmostEqualTo(pl.Vertices[4]));
Assert.True(segments[2].Start.IsAlmostEqualTo(pl.Vertices[5]));
Assert.True(segments[2].Mid().IsAlmostEqualTo(pl.Vertices[6]));
Assert.True(segments[3].Start.IsAlmostEqualTo(pl.Vertices[7]));
Assert.True(segments[3].Mid().IsAlmostEqualTo(pl.Vertices[8]));
Assert.True(segments[4].Start.IsAlmostEqualTo(pl.Vertices[9]));
Assert.True(segments[4].Mid().IsAlmostEqualTo(pl.Vertices[10]));
Assert.True(segments[5].Start.IsAlmostEqualTo(pl.Vertices[11]));
Assert.True(segments[5].Mid().IsAlmostEqualTo(pl.Vertices[12]));
Assert.True(L.End.IsAlmostEqualTo(pl.Vertices[13]));
}
}
}
13 changes: 13 additions & 0 deletions Elements/test/VectorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,19 @@ public void UniqueAverageWithinTolerance()
x => x.IsAlmostEqualTo(new Vector3(5, 5)));
}

[Fact]
public void DistanceToL()
{
var L = Polygon.L(10, 15, 5);
var position = new Vector3(-1, 3);
var distance = position.DistanceTo(L.ToPolyline(), out var closestA);
var distancePolygon = position.DistanceTo(L, out var closestB);
Assert.Equal(1, distance);
Assert.Equal(1, distancePolygon);
Assert.Equal(new Vector3(0, 3), closestA);
Assert.Equal(new Vector3(0, 3), closestB);
}

[Fact]
public void PointsAreCoplanarWithinTolerance()
{
Expand Down
Loading