Skip to content

Commit

Permalink
Merge pull request #1015 from DmytroMuravskyi/dmuravskyi/surface-inte…
Browse files Browse the repository at this point in the history
…rsetion-lines

Add Polygon.IntersectionLines for Polygons not on the same plane (#1015)
  • Loading branch information
wynged authored Oct 6, 2023
2 parents 926ca5e + 29cf17c commit e6c3dde
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
- `Bezier.Intersects(Ellipse ellipse, out List<Vector3> results)`
- `Bezier.Intersects(Bezier other, out List<Vector3> results)`
- `Elements.Geometry.ThickenedPolyline`
- `Polygon.IntersectionLines`
- `Model.ToGlTF(MemoryStream stream...)`
- `Model.ToIFC(MemoryStream stream ...)`
- `Model.ToJson(MemoryStream stream ...)`
Expand Down
68 changes: 68 additions & 0 deletions Elements/src/Geometry/Polygon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1723,6 +1723,74 @@ public IList<Polygon> XOR(Polygon polygon, double tolerance = Vector3.EPSILON)
return polygons;
}

/// <summary>
/// Find all intersection lines between two polygons that are not on the same plane.
/// </summary>
/// <param name="polygon">The intersecting polygon.</param>
/// <param name="includeIntersectionAtEdge">Include intersection at edge of one or both polygons.</param>
/// <returns>A list of the segment(s) what are inside both polygons.</returns>
public List<Line> IntersectionLines(Polygon polygon, bool includeIntersectionAtEdge = false)
{
var lines = new List<Line>();
if (!Plane().Intersects(polygon.Plane(), out InfiniteLine intersectionLine))
{
return lines;
}

var tempLine = new Line(intersectionLine.Origin, intersectionLine.Origin + intersectionLine.Direction);
var insideSegmentsLeft = tempLine.Trim(this, out _, includeIntersectionAtEdge, true);
if (!insideSegmentsLeft.Any())
{
return lines;
}

var insideSegmentsRight = tempLine.Trim(polygon, out _, includeIntersectionAtEdge, true);
if (!insideSegmentsRight.Any())
{
return lines;
}

int left = 0;
int right = 0;
// Trim produced ordered lines. Both lists have segments on the same infinite line.
// Iterate though both lists and find overlapping line segments that belong to both Polygons.
while (left < insideSegmentsLeft.Count && right < insideSegmentsRight.Count)
{
intersectionLine.ParameterAt(insideSegmentsLeft[left].Start, out var p0min);
intersectionLine.ParameterAt(insideSegmentsRight[right].End, out var p1max);
if (p1max < p0min)
{
right++;
continue;
}

intersectionLine.ParameterAt(insideSegmentsLeft[left].End, out var p0max);
intersectionLine.ParameterAt(insideSegmentsRight[right].Start, out var p1min);
if (p0max < p1min)
{
left++;
continue;
}

bool leftStart = p0min > p1min;
bool leftEnd = p1max > p0max;
var start = leftStart ? insideSegmentsLeft[left].Start : insideSegmentsRight[right].Start;
var end = leftEnd ? insideSegmentsLeft[left].End : insideSegmentsRight[right].End;
lines.Add(new Line(start, end));

if (leftEnd)
{
left++;
}
else
{
right++;
}
}

return lines;
}

/// <summary>
/// Offset this polygon by the specified amount.
/// </summary>
Expand Down
133 changes: 133 additions & 0 deletions Elements/test/PolygonTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,139 @@ public void XOR()
Assert.Contains(vertices, p => p.IsAlmostEqualTo(4.0, 0.0));
}

[Fact]
public void IntersectionLines()
{
// Two complex shapes
var p1 = new Polygon
(
new[]
{
new Vector3(0, -1),
new Vector3(2, -1),
new Vector3(2, 1),
new Vector3(5, 1),
new Vector3(5, -1),
new Vector3(8, -1),
new Vector3(8, 1),
new Vector3(12, 1),
new Vector3(12, -1),
new Vector3(17, -1),
new Vector3(17, 1),
new Vector3(19, 1),
new Vector3(19, -1),
new Vector3(24, -1),
new Vector3(24, 3),
new Vector3(0, 3)
}
);
var p2 = new Polygon
(
new[]
{
new Vector3(1, 0, -1),
new Vector3(6, 0, -1),
new Vector3(6, 0, 1),
new Vector3(9, 0, 1),
new Vector3(9, 0, -1),
new Vector3(11, 0, -1),
new Vector3(11, 0, 1),
new Vector3(13, 0, 1),
new Vector3(13, 0, -1),
new Vector3(16, 0, -1),
new Vector3(16, 0, 1),
new Vector3(18, 0, 1),
new Vector3(18, 0, -1),
new Vector3(21, 0, -1),
new Vector3(21, 0, 1),
new Vector3(22, 0, 1),
new Vector3(22, 0, -1),
new Vector3(26, 0, -1),
new Vector3(26, 0, 3),
new Vector3(1, 0, 3)
}
);

var lines = p1.IntersectionLines(p2);
Assert.Equal(5, lines.Count());
Assert.Contains(lines, l => l.IsAlmostEqualTo(new Line((1, 0), (2, 0)), false));
Assert.Contains(lines, l => l.IsAlmostEqualTo(new Line((5, 0), (6, 0)), false));
Assert.Contains(lines, l => l.IsAlmostEqualTo(new Line((13, 0), (16, 0)), false));
Assert.Contains(lines, l => l.IsAlmostEqualTo(new Line((19, 0), (21, 0)), false));
Assert.Contains(lines, l => l.IsAlmostEqualTo(new Line((22, 0), (24, 0)), false));

// Overlapping polygons on the same plane
p1 = new Polygon
(
new[]
{
new Vector3(0, 0),
new Vector3(2, 0),
new Vector3(2, 2),
new Vector3(0, 2)
}
);
p2 = new Polygon
(
new[]
{
new Vector3(1, 1),
new Vector3(3, 1),
new Vector3(3, 3),
new Vector3(1, 3)
}
);
lines = p1.IntersectionLines(p2);
Assert.Empty(lines);

// Polygons on parallel planes
p2 = new Polygon
(
new[]
{
new Vector3(2, 0, 2),
new Vector3(2, 2, 2),
new Vector3(4, 2, 2),
new Vector3(4, 0, 2)
}
);
lines = p1.IntersectionLines(p2);
Assert.Empty(lines);

// Touching polygons on the same plane
p2 = new Polygon
(
new[]
{
new Vector3(2, 0),
new Vector3(2, 2),
new Vector3(4, 2),
new Vector3(4, 0)
}
);
lines = p1.IntersectionLines(p2);
Assert.Empty(lines);
lines = p1.IntersectionLines(p2, true);
Assert.Empty(lines);

// Touching polygons non parallel planes
p2 = new Polygon
(
new[]
{
new Vector3(2, 0),
new Vector3(2, 2),
new Vector3(2, 2, 2),
new Vector3(2, 0, 2)
}
);
lines = p1.IntersectionLines(p2);
Assert.Empty(lines);
lines = p1.IntersectionLines(p2, true);
Assert.Single(lines);
Assert.Contains(lines, l => l.IsAlmostEqualTo(new Line((2, 0), (2, 2)), false));
}

[Fact]
public void Offset()
{
Expand Down

0 comments on commit e6c3dde

Please sign in to comment.