Skip to content

Commit

Permalink
Merge pull request #1016 from DmytroMuravskyi/dmuravskyi/frames
Browse files Browse the repository at this point in the history
Regression fix for Elements.Geometry.Solids.Sweep (#1016)
  • Loading branch information
andrewheumann authored Oct 5, 2023
2 parents ee2f72d + 03ca7ad commit 926ca5e
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 41 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@

- `Polygon.Contains3D` passed wrong `out Containment containment` parameter in some cases.
- Code generation supports `Vector3?` and `Color?` types.
- `IndexedPolycurve.GetSubdivisionParameters` now works correctly with `startSetbackDistance` and `endSetbackDistance` parameters.
- `Polyline.Frames` now works correctly with `startSetbackDistance` and `endSetbackDistance` parameters.
- `Polygon.Frames` now works correctly with `startSetbackDistance` and `endSetbackDistance` parameters.


### Changed
Expand Down
25 changes: 18 additions & 7 deletions Elements/src/Geometry/IndexedPolycurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,29 +277,40 @@ public override BBox3 Bounds()
public override double[] GetSubdivisionParameters(double startSetbackDistance = 0,
double endSetbackDistance = 0)
{
var parameters = new List<double>();
for (var i = 0; i < _curves.Count; i++)
var startParam = ParameterAtDistanceFromParameter(startSetbackDistance, Domain.Min);
var endParam = ParameterAtDistanceFromParameter(Length() - endSetbackDistance, Domain.Min);

var startCurveIndex = (int)Math.Floor(startParam);
var endCurveIndex = Math.Min(_curves.Count - 1, (int)Math.Floor(endParam));

var parameters = new List<double>() { startParam };
for (var i = startCurveIndex; i < endCurveIndex + 1; i++)
{
var startParam = i;
var endParam = i + 1;
var curve = _curves[i];
var localParameters = curve.GetSubdivisionParameters();
var localDomain = new Domain1d(startParam, endParam);
var localDomain = new Domain1d(i, i + 1);
for (var j = 0; j < localParameters.Length; j++)
{
var localParameter = localParameters[j];
// The curve domain may be 0->2Pi. We need to map that
// into a domain that is a subsection of the larger domain.
var remapped = localParameter.MapBetweenDomains(curve.Domain, localDomain);

// De-duplicate the end vertices.
if (parameters.Count > 0 && parameters[parameters.Count - 1].ApproximatelyEquals(remapped))
// Filter parameters outside of setbacks and de-duplicate the end vertices.
if (remapped < startParam || remapped > endParam ||
parameters[parameters.Count - 1].ApproximatelyEquals(remapped))
{
continue;
}
parameters.Add(remapped);
}
}

if (!parameters[parameters.Count - 1].ApproximatelyEquals(endParam))
{
parameters.Add(endParam);
}

return parameters.ToArray();
}

Expand Down
71 changes: 60 additions & 11 deletions Elements/src/Geometry/Polygon.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ClipperLib;
using Elements.Geometry.Profiles;
using Elements.Search;
using Elements.Spatial;
using Newtonsoft.Json;
Expand Down Expand Up @@ -2071,26 +2072,74 @@ public Polygon CollinearPointsRemoved(double tolerance = Vector3.EPSILON)
}

/// <inheritdoc/>
public override Transform[] Frames(double startSetback = 0.0,
double endSetback = 0.0,
public override Transform[] Frames(double startSetbackDistance = 0.0,
double endSetbackDistance = 0.0,
double additionalRotation = 0.0)
{
// Create an array of transforms with the same
// number of items as the vertices.
var result = new Transform[this.Vertices.Count];
var startParam = ParameterAtDistanceFromParameter(startSetbackDistance, Domain.Min);
var endParam = ParameterAtDistanceFromParameter(Length() - endSetbackDistance, Domain.Min);

// Cache the normal so we don't have to recalculate
// using Newell for every frame.
if (startParam >= endParam)
{
return new Transform[0];
}

var startIndex = (int)Math.Ceiling(startParam);
var endIndex = (int)Math.Floor(endParam);
bool startAtVertex = false;
bool endsAtVertex = false;

// Calculate number of frames. 2 frames corresponding to end parameters.
// 1 if startIndex == endIndex.
var length = endIndex - startIndex + 3;

// startIndex is set to the first distinct vertex after startParam.
if (startParam.ApproximatelyEquals(startIndex))
{
startAtVertex = true;
length--;
}

// endIndex is set to the first distinct vertex before endParam.
if (endParam.ApproximatelyEquals(endIndex))
{
endsAtVertex = true;
length--;
}

var result = new Transform[length];
var up = this.Normal();
for (var i = 0; i < result.Length; i++)

int index = 0;
if (!startAtVertex)
{
var tangent = (Vertices[startIndex - 1] - Vertices[startIndex]).Unitized();
result[0] = new Transform(PointAt(startParam), up.Cross(tangent), tangent);
index++;
}

// CreateMiterTransform expects index of previous and next vertex, that's why index must be adjusted.
for (var i = startIndex; i <= endIndex; i++, index++)
{
var a = this.Vertices[i];
result[i] = CreateMiterTransform(i, a, up);
if (additionalRotation != 0.0)
var adjusted = i == Vertices.Count ? 0 : i;
result[index] = CreateMiterTransform(adjusted, Vertices[adjusted], up);
}

if (!endsAtVertex)
{
var nextIndex = (endIndex + 1) % Vertices.Count;
var tangent = (Vertices[endIndex] - Vertices[nextIndex]).Unitized();
result[index] = new Transform(PointAt(endParam), up.Cross(tangent), tangent);
}

if (additionalRotation != 0.0)
{
for (int i = 0; i < result.Length; i++)
{
result[i].RotateAboutPoint(result[i].Origin, result[i].ZAxis, additionalRotation);
}
}

return result;
}

Expand Down
78 changes: 57 additions & 21 deletions Elements/src/Geometry/Polyline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public virtual Line[] Segments()
/// Get the transform at the specified parameter along the polyline.
/// </summary>
/// <param name="u">The parameter on the polygon between 0.0 and length.</param>
/// <returns>A transform with its Z axis aligned trangent to the polyline.</returns>
/// <returns>A transform with its Z axis aligned tangent to the polyline.</returns>
public override Transform TransformAt(double u)
{
if (!Domain.Includes(u, true))
Expand Down Expand Up @@ -288,32 +288,68 @@ public override Transform[] Frames(double startSetbackDistance = 0.0,
double endSetbackDistance = 0.0,
double additionalRotation = 0.0)
{
var startParam = ParameterAtDistanceFromParameter(startSetbackDistance, Domain.Min);
var endParam = ParameterAtDistanceFromParameter(Length() - endSetbackDistance, Domain.Min);

if (startParam >= endParam)
{
return new Transform[0];
}

var startIndex = (int)Math.Ceiling(startParam);
var endIndex = (int)Math.Floor(endParam);
bool startAtVertex = false;
bool endsAtVertex = false;

// Calculate number of frames. 2 frames corresponding to end parameters.
// 1 if startIndex == endIndex.
var length = endIndex - startIndex + 3;

// startIndex is set to the first distinct vertex after startParam.
if (startParam.ApproximatelyEquals(startIndex))
{
startAtVertex = true;
length--;
}

// endIndex is set to the first distinct vertex before endParam.
if (endParam.ApproximatelyEquals(endIndex))
{
endsAtVertex = true;
length--;
}

var result = new Transform[length];
var normals = this.NormalsAtVertices();

// Create an array of transforms with the same number of items as the vertices.
var result = new Transform[this.Vertices.Count];
var l = Length();
for (var i = 0; i < result.Length; i++)
int index = 0;
// CreateOrthogonalTransform expects index of previous vertex, that's why index must be adjusted.
if (!startAtVertex)
{
Vector3 a;
if (i == 0)
{
a = PointAt(ParameterAtDistanceFromParameter(startSetbackDistance, this.Domain.Min));
}
else if (i == Vertices.Count - 1)
{
a = PointAt(ParameterAtDistanceFromParameter(l - endSetbackDistance, this.Domain.Min));
}
else
{
a = this.Vertices[i];
}
result[i] = CreateOrthogonalTransform(i, a, normals[i]);
if (additionalRotation != 0.0)
var tangent = (Vertices[startIndex - 1] - Vertices[startIndex]).Unitized();
result[0] = new Transform(PointAt(startParam), normals[startIndex - 1].Cross(tangent), tangent);
index++;
}

for (var i = startIndex; i <= endIndex; i++, index++)
{
result[index] = CreateOrthogonalTransform(i, Vertices[i], normals[i]);
}

if (!endsAtVertex)
{
var tangent = (Vertices[endIndex] - Vertices[endIndex + 1]).Unitized();
result[index] = new Transform(PointAt(endParam), normals[endIndex].Cross(tangent), tangent);
}

if (additionalRotation != 0.0)
{
for (int i = 0; i < result.Length; i++)
{
result[i].RotateAboutPoint(result[i].Origin, result[i].ZAxis, additionalRotation);
}
}

return result;
}

Expand Down Expand Up @@ -351,7 +387,7 @@ protected Transform CreateMiterTransform(int i, Vector3 a, Vector3 up)
var x1 = l1.Cross(up);
var x2 = l2.Cross(up);
var x = x1.Average(x2);
return new Transform(this.Vertices[i], x, x.Cross(up));
return new Transform(a, x, x.Cross(up));
}

private Transform CreateOrthogonalTransform(int i, Vector3 origin, Vector3 up)
Expand Down
4 changes: 2 additions & 2 deletions Elements/src/Geometry/Solids/Solid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ public static Solid SweepFaceAlongCurve(Polygon perimeter,

if (curve is Polygon)
{
for (var i = 0; i < transforms.Length; i++)
for (var i = 0; i < transforms.Length - 1; i++)
{
var next = i == transforms.Length - 1 ? transforms[0] : transforms[i + 1];
var next = transforms[i + 1];
solid.SweepPolygonBetweenPlanes(perimeter, transforms[i], next);
}
}
Expand Down
41 changes: 41 additions & 0 deletions Elements/test/IndexedPolyCurveTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,5 +186,46 @@ public void Intersects()
Assert.Contains(new Vector3(5, 2), results);
Assert.Contains(new Vector3(2.5, 7.5), results);
}

[Fact]
public void GetSubdivisionParameters()
{
var pc = new IndexedPolycurve(new List<Vector3>()
{
(0, 0), (2, 0), (2, 2), (0, 2), (0, 3)
});
var parameters = pc.GetSubdivisionParameters();
Assert.Equal(5, parameters.Count());
Assert.Equal(0.0, parameters[0]);
Assert.Equal(1.0, parameters[1]);
Assert.Equal(2.0, parameters[2]);
Assert.Equal(3.0, parameters[3]);
Assert.Equal(4.0, parameters[4]);

parameters = pc.GetSubdivisionParameters(1.5, 1.5);
Assert.Equal(4, parameters.Count());
Assert.Equal(0.75, parameters[0]);
Assert.Equal(1.0, parameters[1]);
Assert.Equal(2.0, parameters[2]);
Assert.Equal(2.75, parameters[3]);

parameters = pc.GetSubdivisionParameters(1, 1);
Assert.Equal(4, parameters.Count());
Assert.Equal(0.5, parameters[0]);
Assert.Equal(1.0, parameters[1]);
Assert.Equal(2.0, parameters[2]);
Assert.Equal(3.0, parameters[3]);

parameters = pc.GetSubdivisionParameters(2, 2);
Assert.Equal(3, parameters.Count());
Assert.Equal(1.0, parameters[0]);
Assert.Equal(2.0, parameters[1]);
Assert.Equal(2.5, parameters[2]);

parameters = pc.GetSubdivisionParameters(2.5, 3.5);
Assert.Equal(2, parameters.Count());
Assert.Equal(1.25, parameters[0]);
Assert.Equal(1.75, parameters[1]);
}
}
}
45 changes: 45 additions & 0 deletions Elements/test/PolygonTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2213,5 +2213,50 @@ public void BigRectangleContainsSmallRectangle()
var r2 = Polygon.Rectangle(1, 1).TransformedPolygon(new Transform(new Vector3(0.5, 0.5), Vector3.ZAxis));
Assert.True(r1.Contains3D(r2));
}

[Fact]
public void Frames()
{
var polygon = new Polygon((0, 0), (2, 0), (2, 2), (0, 2));
var frames = polygon.Frames();
Assert.Equal(5, frames.Count());
Assert.Equal(polygon.Vertices[0], frames[0].Origin);
Assert.True((Vector3.XAxis - Vector3.YAxis).Unitized().Negate().IsAlmostEqualTo(frames[0].ZAxis));
Assert.Equal(polygon.Vertices[1], frames[1].Origin);
Assert.True((Vector3.XAxis + Vector3.YAxis).Unitized().Negate().IsAlmostEqualTo(frames[1].ZAxis));
Assert.Equal(polygon.Vertices[2], frames[2].Origin);
Assert.True((Vector3.YAxis - Vector3.XAxis).Unitized().Negate().IsAlmostEqualTo(frames[2].ZAxis));
Assert.Equal(polygon.Vertices[3], frames[3].Origin);
Assert.True((Vector3.XAxis + Vector3.YAxis).Unitized().IsAlmostEqualTo(frames[3].ZAxis));
Assert.Equal(polygon.Vertices[0], frames[4].Origin);
Assert.True((Vector3.XAxis - Vector3.YAxis).Unitized().Negate().IsAlmostEqualTo(frames[4].ZAxis));

frames = polygon.Frames(1, 1);
Assert.Equal(5, frames.Count());
Assert.Equal((1, 0), frames[0].Origin);
Assert.True(Vector3.XAxis.Negate().IsAlmostEqualTo(frames[0].ZAxis));
Assert.Equal((0, 1), frames[4].Origin);
Assert.True(Vector3.YAxis.IsAlmostEqualTo(frames[4].ZAxis));

frames = polygon.Frames(2, 2);
Assert.Equal(3, frames.Count());
Assert.Equal(polygon.Vertices[1], frames[0].Origin);
Assert.True((Vector3.XAxis + Vector3.YAxis).Unitized().Negate().IsAlmostEqualTo(frames[0].ZAxis));
Assert.Equal(polygon.Vertices[2], frames[1].Origin);
Assert.True((Vector3.YAxis - Vector3.XAxis).Unitized().Negate().IsAlmostEqualTo(frames[1].ZAxis));
Assert.Equal(polygon.Vertices[3], frames[2].Origin);
Assert.True((Vector3.XAxis + Vector3.YAxis).Unitized().IsAlmostEqualTo(frames[2].ZAxis));

frames = polygon.Frames(1, 3);
Assert.Equal(4, frames.Count());
Assert.Equal((1, 0), frames[0].Origin);
Assert.True(Vector3.XAxis.IsParallelTo(frames[0].ZAxis));
Assert.Equal(polygon.Vertices[1], frames[1].Origin);
Assert.True((Vector3.XAxis + Vector3.YAxis).Unitized().IsParallelTo(frames[1].ZAxis));
Assert.Equal(polygon.Vertices[2], frames[2].Origin);
Assert.True((Vector3.XAxis - Vector3.YAxis).Unitized().IsParallelTo(frames[2].ZAxis));
Assert.Equal((1, 2), frames[3].Origin);
Assert.True(Vector3.XAxis.IsParallelTo(frames[3].ZAxis));
}
}
}
Loading

0 comments on commit 926ca5e

Please sign in to comment.