diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1e1cd132c..4518233ae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@
- `Profile.ThickenedEdgePolygons`
- `Elements.MEP`
- `GeometricElement.RepresentationInstances`
+- Custom implementations of `ToPolyline` for `IndexedPolycurve`, `Polyline` and `Polygon`.
### Fixed
@@ -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
diff --git a/Elements/src/Geometry/BoundedCurve.cs b/Elements/src/Geometry/BoundedCurve.cs
index c8e79b858..1fedb6de6 100644
--- a/Elements/src/Geometry/BoundedCurve.cs
+++ b/Elements/src/Geometry/BoundedCurve.cs
@@ -84,12 +84,21 @@ public virtual Transform[] Frames(double startSetbackDistance = 0.0,
return transforms;
}
+ ///
+ /// Create a polyline through a set of 10 segments along the curve.
+ ///
+ /// A polyline.
+ public virtual Polyline ToPolyline()
+ {
+ return ToPolyline(10);
+ }
+
///
/// Create a polyline through a set of points along the curve.
///
/// The number of divisions of the curve.
/// A polyline.
- public virtual Polyline ToPolyline(int divisions = 10)
+ public virtual Polyline ToPolyline(int divisions)
{
var pts = new List(divisions + 1);
var step = this.Domain.Length / divisions;
diff --git a/Elements/src/Geometry/IndexedPolycurve.cs b/Elements/src/Geometry/IndexedPolycurve.cs
index 0af0a9603..b34303275 100644
--- a/Elements/src/Geometry/IndexedPolycurve.cs
+++ b/Elements/src/Geometry/IndexedPolycurve.cs
@@ -666,6 +666,94 @@ internal static void CheckSelfIntersectionAndThrow(Transform t, IEnumerable<(Vec
}
}
+ ///
+ /// Create a polyline through curves of IndexedPolycurve, interpolating any curves that are not lines.
+ ///
+ /// A polyline.
+ public override Polyline ToPolyline()
+ {
+ List vertices = new List();
+ 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);
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The number of divisions of the curve.
+ /// This can lead to highly distorted result.
+ /// A polyline.
+ public override Polyline ToPolyline(int divisions)
+ {
+ //
+ if (divisions < _curves.Count)
+ {
+ return base.ToPolyline(divisions);
+ }
+
+ double extraSplits = divisions - _curves.Count;
+ List vertices = new List(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);
+ }
+
///
/// Get the enumerator for this indexed polycurve.
///
diff --git a/Elements/src/Geometry/Polygon.cs b/Elements/src/Geometry/Polygon.cs
index 1f92b8a98..da4f35794 100644
--- a/Elements/src/Geometry/Polygon.cs
+++ b/Elements/src/Geometry/Polygon.cs
@@ -1823,6 +1823,18 @@ public override Line[] Segments()
return lines;
}
+ ///
+ /// Create a polyline through vertices of a polygon.
+ ///
+ /// A polyline.
+ public override Polyline ToPolyline()
+ {
+ var vertices = new List(Vertices.Count + 1);
+ vertices.AddRange(Vertices);
+ vertices.Add(End);
+ return new Polyline(vertices);
+ }
+
///
/// Reverse the direction of a polygon.
///
diff --git a/Elements/src/Geometry/Polyline.cs b/Elements/src/Geometry/Polyline.cs
index 0af338570..cce7c09f5 100644
--- a/Elements/src/Geometry/Polyline.cs
+++ b/Elements/src/Geometry/Polyline.cs
@@ -84,6 +84,15 @@ public virtual Line[] Segments()
return SegmentsInternal(this.Vertices);
}
+ ///
+ /// Create a copy of a polyline.
+ ///
+ /// A polyline.
+ public override Polyline ToPolyline()
+ {
+ return new Polyline(Vertices.ToList());
+ }
+
///
/// Get the transform at the specified parameter along the polyline.
///
diff --git a/Elements/src/Geometry/Vector3.cs b/Elements/src/Geometry/Vector3.cs
index 051c2faf9..2072a8ca6 100644
--- a/Elements/src/Geometry/Vector3.cs
+++ b/Elements/src/Geometry/Vector3.cs
@@ -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);
}
}
diff --git a/Elements/test/IndexedPolyCurveTests.cs b/Elements/test/IndexedPolyCurveTests.cs
index 2ddf1a349..093aca9f8 100644
--- a/Elements/test/IndexedPolyCurveTests.cs
+++ b/Elements/test/IndexedPolyCurveTests.cs
@@ -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]));
+ }
}
}
\ No newline at end of file
diff --git a/Elements/test/PolygonTests.cs b/Elements/test/PolygonTests.cs
index 959199c26..c3f5ab3c9 100644
--- a/Elements/test/PolygonTests.cs
+++ b/Elements/test/PolygonTests.cs
@@ -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]));
+ }
}
}
\ No newline at end of file
diff --git a/Elements/test/VectorTests.cs b/Elements/test/VectorTests.cs
index 44957050e..70fa7a585 100644
--- a/Elements/test/VectorTests.cs
+++ b/Elements/test/VectorTests.cs
@@ -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()
{