Skip to content

Commit

Permalink
Merge pull request #849 from hypar-io/mesh-performance
Browse files Browse the repository at this point in the history
Mesh performance
  • Loading branch information
andrewheumann authored Aug 11, 2022
2 parents d975c8e + b6618dd commit f89e275
Show file tree
Hide file tree
Showing 9 changed files with 417 additions and 27 deletions.
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
## 1.2.0

### Added

- Message class along with helper creation methods.
- `Mesh.Intersects(Ray)` (same as `Ray.Intersects(Mesh)`)
- `Ray.NearbyPoints()`
- `PointOctree<T>`
- `Message` class along with helper creation methods.

### Changed

- MeshElement constructor signature modified to be compatible with code generation.
- Improved performance of mesh/ray intersection
- `BBox3.Extend` method is public now
- `AdaptiveGrid.Boundary` can be left null.
- `Obstacle` properties `Points`, `Offset`, `Perimeter` and `Transform` can be modified from outside.
Expand Down
74 changes: 74 additions & 0 deletions Elements.Benchmarks/Mesh.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using Elements.Geometry;

namespace Elements.Benchmarks
{
public class MeshRayIntersection
{
private Mesh _mesh;
private List<Ray> _rays;

public MeshRayIntersection()
{
var random = new Random(10);
_mesh = new Mesh();
_rays = new List<Ray>();
var xCount = 100;
var yCount = 300;
MeshConstruction.BuildRandomMesh(_mesh, random, xCount, yCount);

// create 1000 random rays
for (int i = 0; i < 1000; i++)
{
var ray = new Ray(new Vector3(random.NextDouble() * xCount, random.NextDouble() * yCount, 2.1), new Vector3(random.NextDouble() * 2 - 1, random.NextDouble() * 2 - 1, -1));
_rays.Add(ray);
}
}


[Benchmark(Description = "Intersect 1000 rays with mesh.")]
public void IntersectRays()
{
foreach (var ray in _rays)
{
ray.Intersects(_mesh, out var _);
}
}
}
public class MeshConstruction
{
public static void BuildRandomMesh(Mesh m, Random random, int xCount, int yCount)
{
for (int i = 0; i < xCount; i++)
{
for (int j = 0; j < yCount; j++)
{
var point = new Vector3(i, j, random.NextDouble() * 2);
var c = m.AddVertex(point);
if (i != 0 && j != 0)
{
// add faces
var d = m.Vertices[i * yCount + j - 1];
var a = m.Vertices[(i - 1) * yCount + j - 1];
var b = m.Vertices[(i - 1) * yCount + j];
m.AddTriangle(a, b, c);
m.AddTriangle(c, d, a);
}
}
}
}

[Params(1000, 5000, 10000, 30000)]
public int VertexCount { get; set; }

[Benchmark(Description = "Construct Mesh")]
public void ConstructMesh()
{
var mesh = new Mesh();
BuildRandomMesh(mesh, new Random(10), 100, VertexCount / 100);
}

}
}
3 changes: 2 additions & 1 deletion Elements/src/Geometry/BBox3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public BBox3(IEnumerable<Vector3> points)
/// Extend a bounding box with a new point
/// </summary>
/// <param name="point">The point which should be inside the extended bounding box</param>
public void Extend(Vector3 point)
public BBox3 Extend(Vector3 point)
{
var newMin = new Vector3(Min.X, Min.Y, Min.Z);
if (point.X < this.Min.X) newMin.X = point.X;
Expand All @@ -107,6 +107,7 @@ public void Extend(Vector3 point)
if (point.Y > this.Max.Y) newMax.Y = point.Y;
if (point.Z > this.Max.Z) newMax.Z = point.Z;
this.Max = newMax;
return this;
}

/// <summary>
Expand Down
4 changes: 1 addition & 3 deletions Elements/src/Geometry/Line.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System.Net.Sockets;
using System.Numerics;
using Elements.Validators;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -510,7 +508,7 @@ public static bool PointOnLine(Vector3 point, Vector3 start, Vector3 end, bool i

var delta = end - start;
var lambda = (point - start).Dot(delta) / (end - start).Dot(delta);
if( lambda > 0 && lambda < 1)
if (lambda > 0 && lambda < 1)
{
var pointOnLine = start + lambda * delta;
return pointOnLine.IsAlmostEqualTo(point);
Expand Down
95 changes: 84 additions & 11 deletions Elements/src/Geometry/Mesh.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Elements.Search;
using Elements.Serialization.JSON;
using LibTessDotNet.Double;
using Newtonsoft.Json;
using Octree;
using System;
using System.Collections.Generic;
using System.IO;
Expand All @@ -16,16 +16,24 @@ namespace Elements.Geometry
[JsonConverter(typeof(MeshConverter))]
public partial class Mesh
{
private PointOctree<Vertex> _octree = new PointOctree<Vertex>(100000, new Octree.Point(0f, 0f, 0f), (float)Vector3.EPSILON);

/// <summary>The mesh' vertices.</summary>
private double _maxTriangleSize = 0;
private PointOctree<Vertex> _octree = null;

/// <summary>The mesh's vertices.</summary>
[JsonProperty("Vertices", Required = Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public IList<Vertex> Vertices { get; set; }

/// <summary>The mesh' triangles.</summary>
/// <summary>The mesh's triangles.</summary>
[JsonProperty("Triangles", Required = Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public IList<Triangle> Triangles { get; set; }

private BBox3 _bbox = new BBox3();
/// <summary>
/// The mesh's bounding box.
/// </summary>
public BBox3 BoundingBox => _bbox;

/// <summary>
/// Construct a mesh.
/// </summary>
Expand All @@ -34,8 +42,16 @@ public partial class Mesh
[JsonConstructor]
public Mesh(IList<Vertex> @vertices, IList<Triangle> @triangles)
{
this.Vertices = @vertices;
this.Triangles = @triangles;
Vertices = new List<Vertex>();
Triangles = new List<Triangle>();
foreach (var v in @vertices)
{
AddVertex(v);
}
foreach (var t in @triangles)
{
AddTriangle(t);
}
}

/// <summary>
Expand Down Expand Up @@ -182,10 +198,18 @@ public GraphicsBuffers GetBuffers()
public Triangle AddTriangle(Vertex a, Vertex b, Vertex c)
{
var t = new Triangle(a, b, c);
if (t.HasDuplicatedVertices(out Vector3 duplicate))
if (!Validators.Validator.DisableValidationOnConstruction && t.HasDuplicatedVertices(out Vector3 duplicate))
{
throw new ArgumentException($"Not a valid Triangle. Duplicate vertex at {duplicate}.");
}
for (int i = 0; i < 3; i++)
{
var sideLength = t.Vertices[i].Position.DistanceTo(t.Vertices[(i + 1) % 3].Position);
if (sideLength > this._maxTriangleSize)
{
this._maxTriangleSize = sideLength;
}
}
this.Triangles.Add(t);
return t;
}
Expand All @@ -200,6 +224,14 @@ public Triangle AddTriangle(Triangle t)
{
throw new ArgumentException($"Not a valid Triangle. Duplicate vertex at {duplicate}.");
}
for (int i = 0; i < 3; i++)
{
var sideLength = t.Vertices[i].Position.DistanceTo(t.Vertices[(i + 1) % 3].Position);
if (sideLength > this._maxTriangleSize)
{
this._maxTriangleSize = sideLength;
}
}
this.Triangles.Add(t);
return t;
}
Expand All @@ -223,11 +255,12 @@ public Vertex AddVertex(Vector3 position,
bool merge = false,
double edgeAngle = 30.0)
{
var p = new Octree.Point((float)position.X, (float)position.Y, (float)position.Z);
var v = new Vertex(position, normal, color);


if (merge)
{
var search = this._octree.GetNearby(p, (float)Vector3.EPSILON);
var search = GetOctree().GetNearby(position, Vector3.EPSILON);
if (search.Length > 0)
{
var angle = search[0].Normal.AngleTo(normal);
Expand All @@ -237,12 +270,13 @@ public Vertex AddVertex(Vector3 position,
}
}
}
// If the octree is null, do nothing — we'll build it when we need it. If we've already constructed it, let's keep it up to date.
this._octree?.Add(v, position);

var v = new Vertex(position, normal, color);
v.UV = uv;
this.Vertices.Add(v);
v.Index = (this.Vertices.Count) - 1;
this._octree.Add(v, p);
this._bbox = this._bbox.Extend(v.Position);
return v;
}

Expand All @@ -253,10 +287,27 @@ public Vertex AddVertex(Vector3 position,
public Vertex AddVertex(Vertex v)
{
this.Vertices.Add(v);
// If the octree is null, do nothing — we'll build it when we need it. If we've already constructed it, let's keep it up to date.
this._octree?.Add(v, v.Position);
this._bbox = this._bbox.Extend(v.Position);
v.Index = (this.Vertices.Count) - 1;
return v;
}

private PointOctree<Vertex> GetOctree()
{
if (_octree == null)
{
_octree = new PointOctree<Vertex>(Math.Max(_bbox.Max.DistanceTo(_bbox.Min), 100), _bbox.PointAt(0.5, 0.5, 0.5), Vector3.EPSILON);
// make sure existing vertices are added to the octree — we're initializing it for the first time
foreach (var v in Vertices)
{
_octree.Add(v, v.Position);
}
}
return _octree;
}

/// <summary>
/// Calculate the volume of the mesh.
/// This value will be inexact for open meshes.
Expand Down Expand Up @@ -335,6 +386,28 @@ public List<Polyline> GetNakedBoundaries()
return polygons;
}

/// <summary>
/// Does the provided ray intersect this mesh mesh?
/// </summary>
/// <param name="ray">The Ray to intersect.</param>
/// <param name="intersection">The location of intersection.</param>
/// <returns>True if an intersection result occurs.
/// False if no intersection occurs.</returns>
public bool Intersects(Ray ray, out Vector3 intersection)
{
var nearbyVertices = GetOctree().GetNearby(ray, _maxTriangleSize).ToList();
var nearbyTriangles = nearbyVertices.SelectMany(v => v.Triangles).Distinct();
intersection = default;
foreach (var t in nearbyTriangles)
{
if (ray.Intersects(t, out intersection))
{
return true;
}
}
return false;
}

private double SignedVolumeOfTriangle(Triangle t)
{
var p1 = t.Vertices[0].Position;
Expand Down
29 changes: 20 additions & 9 deletions Elements/src/Geometry/Ray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Elements.Geometry.Solids;
using Elements.Search;

namespace Elements.Geometry
{
Expand Down Expand Up @@ -282,15 +283,7 @@ public bool Intersects(Topography topo, out Vector3 result)

public bool Intersects(Mesh mesh, out Vector3 result)
{
result = default;
foreach (var t in mesh.Triangles)
{
if (this.Intersects(t, out result))
{
return true;
}
}
return false;
return mesh.Intersects(this, out result);
}

/// <summary>
Expand Down Expand Up @@ -370,6 +363,24 @@ public bool Intersects(Vector3 start, Vector3 end, out Vector3 result)
return false;
}

/// <summary>
/// Find points in the collection that are within the provided distance of this ray.
/// </summary>
/// <param name="points">The collection of points to search</param>
/// <param name="distance">The maximum distance from the ray.</param>
/// <returns>Points that are within the given distance of the ray.</returns>
public Vector3[] NearbyPoints(IEnumerable<Vector3> points, double distance)
{
// TODO: calibrate these values
var octree = new PointOctree<Vector3>(10000, (0, 0, 0), (float)Vector3.EPSILON * 100);
foreach (var point in points)
{
octree.Add(point, point);
}
var nearbyPoints = octree.GetNearby(this, (float)distance);
return nearbyPoints;
}

/// <summary>
/// Is this ray equal to the provided ray?
/// </summary>
Expand Down
Loading

0 comments on commit f89e275

Please sign in to comment.