diff --git a/CHANGELOG.md b/CHANGELOG.md index 90dcd32b7..10394cefa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,14 +3,21 @@ ## 1.2.0 ### Added + - Message class along with helper creation methods. ### Changed + - MeshElement constructor signature modified to be compatible with code generation. - `BBox3.Extend` method is public now +- `AdaptiveGrid.Boundary` can be left null. +- `Obstacle` properties `Points`, `Offset`, `Perimeter` and `Transform` can be modified from outside. ### Fixed + - Fixed a bug where `Polyline.Frames` would return inconsistently-oriented transforms. +- `Obstacle.FromBox` works properly with `AdaptiveGrid` transformation. +- `AdaptiveGrid.SubtractObstacle` worked incorrectly in `AdaptiveGrid.Boundary` had elevation. ## 1.1.0 @@ -57,7 +64,7 @@ - Fixed a mathematical error in `MercatorProjection.MetersToLatLon`, which was returning longitude values that were skewed. - `Grid2d.IsTrimmed` would occasionally return `true` for cells that were not actually trimmed. - `Vector3[].AreCoplanar()` computed its tolerance for deviation incorrectly, this is fixed. -- `Polyline.Intersects(Line line, out List intersections, bool infinite = false, bool includeEnds = false)` fix wrong results when infinite flag is set, fix for overlapping points when include ends is set +- `Polyline.Intersects(Line line, out List intersections, bool infinite = false, bool includeEnds = false)` fix wrong results when infinite flag is set, fix for overlapping points when include ends is set. ## 1.0.1 diff --git a/Elements/src/Spatial/AdaptiveGrid/AdaptiveGrid.cs b/Elements/src/Spatial/AdaptiveGrid/AdaptiveGrid.cs index ea88a6ee2..152f8949a 100644 --- a/Elements/src/Spatial/AdaptiveGrid/AdaptiveGrid.cs +++ b/Elements/src/Spatial/AdaptiveGrid/AdaptiveGrid.cs @@ -951,15 +951,19 @@ private List AddEdgesInBetween(Vertex start, Vertex end, IEnumerable candidates) { - var inside = new Line(new Vector3(start.X, start.Y), new Vector3(end.X, end.Y)).Trim(Boundaries, out var _); - if (!inside.Any()) + if (Boundaries != null) { - return; - } + var boundary2d = new Polygon(Boundaries.Vertices.Select(v => new Vector3(v.X, v.Y)).ToList()); + var inside = new Line(new Vector3(start.X, start.Y), new Vector3(end.X, end.Y)).Trim(boundary2d, out var _); + if (!inside.Any()) + { + return; + } - var fi = inside.First(); - start = new Vector3(fi.Start.X, fi.Start.Y, start.Z); - end = new Vector3(fi.End.X, fi.End.Y, end.Z); + var fi = inside.First(); + start = new Vector3(fi.Start.X, fi.Start.Y, start.Z); + end = new Vector3(fi.End.X, fi.End.Y, end.Z); + } var onLine = candidates.Where(x => Line.PointOnLine(x, start, end)); var ordered = onLine.OrderBy(x => (x - start).Dot(end - start)); diff --git a/Elements/src/Spatial/AdaptiveGrid/Obstacle.cs b/Elements/src/Spatial/AdaptiveGrid/Obstacle.cs index 2e8c5226d..3bb10c78f 100644 --- a/Elements/src/Spatial/AdaptiveGrid/Obstacle.cs +++ b/Elements/src/Spatial/AdaptiveGrid/Obstacle.cs @@ -64,8 +64,7 @@ public static Obstacle FromWall(StandardWall wall, double offset = 0, bool perim /// New obstacle object. public static Obstacle FromBBox(BBox3 box, double offset = 0, bool perimeter = false) { - List points = new List() { box.Min, box.Max }; - return new Obstacle(points, offset, perimeter, null); + return new Obstacle(box.Corners(), offset, perimeter, null); } /// @@ -132,23 +131,23 @@ public Obstacle(List points, double offset, bool perimeter, Transform t /// /// List of points defining obstacle. /// - public List Points { get; private set; } + public List Points { get; set; } /// /// Offset of bounding box created from the list of points. /// - public double Offset { get; private set; } + public double Offset { get; set; } /// /// Should edges be created around obstacle. /// If false - any intersected edges are just discarded. /// If true - intersected edges are cut to obstacle and perimeter edges are inserted. /// - public bool Perimeter { get; private set; } + public bool Perimeter { get; set; } /// /// Transformation of bounding box created from the list of points. /// - public Transform Transform { get; private set; } + public Transform Transform { get; set; } } } diff --git a/Elements/test/AdaptiveGridTests.cs b/Elements/test/AdaptiveGridTests.cs index 0e2dac43f..6997c8bbe 100644 --- a/Elements/test/AdaptiveGridTests.cs +++ b/Elements/test/AdaptiveGridTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using Xunit; using Vertex = Elements.Spatial.AdaptiveGrid.Vertex; @@ -16,9 +17,7 @@ public class AdaptiveGridTests : ModelTest [Fact, Trait("Category", "Examples")] public void AdaptiveGridPolygonKeyPointsExample() { - this.Name = "Elements_Spatial_AdaptiveGrid_AdaptiveGrid"; // - var random = new Random(); var adaptiveGrid = new AdaptiveGrid(); var points = new List() @@ -32,19 +31,15 @@ public void AdaptiveGridPolygonKeyPointsExample() adaptiveGrid.AddFromPolygon(Polygon.Rectangle(15, 10).TransformedPolygon( new Transform(new Vector3(), new Vector3(10, 0, 10))), points); - foreach (var edge in adaptiveGrid.GetEdges()) - { - Model.AddElement(new ModelCurve(adaptiveGrid.GetLine(edge), material: random.NextMaterial())); - } // + + WriteToModelWithRandomMaterials(adaptiveGrid, "Elements_Spatial_AdaptiveGrid_AdaptiveGrid"); } [Fact] public void AdaptiveGridBboxKeyPointsExample() { - this.Name = "Elements_Spatial_AdaptiveGrid_AdaptiveGridBboxKeyPoints"; // - var random = new Random(); var adaptiveGrid = new AdaptiveGrid(); var points = new List() @@ -75,11 +70,9 @@ public void AdaptiveGridBboxKeyPointsExample() }; adaptiveGrid.AddFromPolygon(rectangle.TransformedPolygon(new Transform(new Vector3(0, 0, 2))), points); - foreach (var edge in adaptiveGrid.GetEdges()) - { - Model.AddElement(new ModelCurve(adaptiveGrid.GetLine(edge), material: random.NextMaterial())); - } // + + WriteToModelWithRandomMaterials(adaptiveGrid, "Elements_Spatial_AdaptiveGrid_AdaptiveGridBboxKeyPoints"); } [Fact] @@ -221,6 +214,166 @@ public void AdaptiveGridSubtractMisalignedPolygon() Assert.Equal(edgesCount - 9, adaptiveGrid.GetEdges().Count); Assert.Equal(verticiesCount - 2, adaptiveGrid.GetVertices().Count); } + + [Fact] + public void AdaptiveGridSubstructRotatedBox() + { + var polygon = Polygon.Rectangle(new Vector3(0, 0), new Vector3(10, 10)); + var transfrom = new Transform().Rotated(Vector3.ZAxis, 45); + + var points = new List(); + for (int i = 1; i < 10; i++) + { + for (int j = 1; j < 10; j++) + { + points.Add(new Vector3(i, j)); + } + } + + var adaptiveGrid = new AdaptiveGrid(transfrom); + adaptiveGrid.AddFromPolygon(polygon, points); + + //Obstacle aligned with adaptive grid transformation. + //Forms big (3;1) -> (5;3) -> (3;5) -> (1;3) rectangle. + var bbox = new BBox3(new Vector3(2, 2), new Vector3(4, 4)); + var withoutTransfrom = Obstacle.FromBBox(bbox, perimeter: true); + adaptiveGrid.SubtractObstacle(withoutTransfrom); + + Assert.False(adaptiveGrid.TryGetVertexIndex(new Vector3(3, 3), out _, adaptiveGrid.Tolerance)); + Assert.False(adaptiveGrid.TryGetVertexIndex(new Vector3(3, 2), out _, adaptiveGrid.Tolerance)); + Assert.False(adaptiveGrid.TryGetVertexIndex(new Vector3(3, 4), out _, adaptiveGrid.Tolerance)); + Assert.False(adaptiveGrid.TryGetVertexIndex(new Vector3(2, 3), out _, adaptiveGrid.Tolerance)); + Assert.False(adaptiveGrid.TryGetVertexIndex(new Vector3(4, 3), out _, adaptiveGrid.Tolerance)); + + Assert.True(adaptiveGrid.TryGetVertexIndex(new Vector3(2, 2), out var id, adaptiveGrid.Tolerance)); + var v = adaptiveGrid.GetVertex(id); + Assert.Equal(3, v.Edges.Count); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(1.5, 2.5), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(2.5, 1.5), adaptiveGrid.Tolerance)); + + Assert.True(adaptiveGrid.TryGetVertexIndex(new Vector3(4, 2), out id, adaptiveGrid.Tolerance)); + v = adaptiveGrid.GetVertex(id); + Assert.Equal(3, v.Edges.Count); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(3.5, 1.5), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(4.5, 2.5), adaptiveGrid.Tolerance)); + + Assert.True(adaptiveGrid.TryGetVertexIndex(new Vector3(4, 4), out id, adaptiveGrid.Tolerance)); + v = adaptiveGrid.GetVertex(id); + Assert.Equal(3, v.Edges.Count); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(3.5, 4.5), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(4.5, 3.5), adaptiveGrid.Tolerance)); + + Assert.True(adaptiveGrid.TryGetVertexIndex(new Vector3(2, 4), out id, adaptiveGrid.Tolerance)); + v = adaptiveGrid.GetVertex(id); + Assert.Equal(3, v.Edges.Count); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(1.5, 3.5), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(2.5, 4.5), adaptiveGrid.Tolerance)); + + //Obstacle aligned with global transformation. + //Forms small (6;6) -> (8;6) -> (8;8) -> (6;8) rectangle. + bbox = new BBox3(new Vector3(6, 6), new Vector3(8, 8)); + var withTransform = Obstacle.FromBBox(bbox, perimeter: true); + withTransform.Transform = new Transform(); + adaptiveGrid.SubtractObstacle(withTransform); + + Assert.False(adaptiveGrid.TryGetVertexIndex(new Vector3(7, 7), out _, adaptiveGrid.Tolerance)); + + Assert.True(adaptiveGrid.TryGetVertexIndex(new Vector3(6, 6), out id, adaptiveGrid.Tolerance)); + v = adaptiveGrid.GetVertex(id); + Assert.Equal(5, v.Edges.Count); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(6, 7), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(5.5, 6.5), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(7, 6), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(6.5, 5.5), adaptiveGrid.Tolerance)); + + Assert.True(adaptiveGrid.TryGetVertexIndex(new Vector3(8, 6), out id, adaptiveGrid.Tolerance)); + v = adaptiveGrid.GetVertex(id); + Assert.Equal(5, v.Edges.Count); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(7, 6), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(7.5, 5.5), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(8, 7), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(8.5, 6.5), adaptiveGrid.Tolerance)); + + Assert.True(adaptiveGrid.TryGetVertexIndex(new Vector3(8, 8), out id, adaptiveGrid.Tolerance)); + v = adaptiveGrid.GetVertex(id); + Assert.Equal(5, v.Edges.Count); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(8, 7), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(8.5, 7.5), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(7, 8), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(7.5, 8.5), adaptiveGrid.Tolerance)); + + Assert.True(adaptiveGrid.TryGetVertexIndex(new Vector3(6, 8), out id, adaptiveGrid.Tolerance)); + v = adaptiveGrid.GetVertex(id); + Assert.Equal(5, v.Edges.Count); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(7, 8), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(6.5, 8.5), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(6, 7), adaptiveGrid.Tolerance)); + Assert.Contains(v.Edges, e => adaptiveGrid.GetVertex( + e.OtherVertexId(v.Id)).Point.IsAlmostEqualTo(new Vector3(5.5, 7.5), adaptiveGrid.Tolerance)); + + WriteToModelWithRandomMaterials(adaptiveGrid); + } + + [Fact] + public void BrokenSubtractionForMisalignedPolygon() + { + var boundaryVerticies = new List + { + new Vector3(0.241, -40, 7), + new Vector3(0.241, -60, 7), + new Vector3(80, -60.000000000000014, 7), + new Vector3(80, -40.000000000000014, 7) + }; + + var boundary = new Polygon(boundaryVerticies); + + var grid = new AdaptiveGrid() + { + Boundaries = boundary + }; + + grid.AddFromPolygon(boundary, new[] { Vector3.Origin }); + + var profile = Polygon.Rectangle(0.2, 0.2); + var column = new Column( + new Vector3(0.5, -56.22727272727274), + 10, + new Line(new Vector3(0.5, -56.22727272727274, 10), new Vector3(0.5, -56.22727272727274, 0)), + profile); + + var obstacle = Obstacle.FromColumn(column, 0.2, true); + var result = grid.SubtractObstacle(obstacle); + + Assert.True(result); + Assert.Equal(8, grid.GetEdges().Count); + Assert.All(grid.GetVertices(), x => Assert.Equal(2, x.Edges.Count)); + + WriteToModelWithRandomMaterials(grid); + } + [Fact] public void AdaptiveGridLongSectionDoNowThrow() { @@ -724,5 +877,15 @@ private AdaptiveGrid SampleGrid() grid.AddVertex(new Vector3(5, 2), new ConnectVertexStrategy(strip[1]), cut: false); //5 return grid; } + + private void WriteToModelWithRandomMaterials(AdaptiveGrid grid, [CallerMemberName] string memberName = "") + { + var random = new Random(); + Name = memberName; + foreach (var edge in grid.GetEdges()) + { + Model.AddElement(new ModelCurve(grid.GetLine(edge), material: random.NextMaterial())); + } + } } }