Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Geometry_Engine: Add join mesh method and fix MergeVertices #2947

Merged
merged 9 commits into from
Nov 15, 2022
1 change: 1 addition & 0 deletions .ci/Datasets/Geometry_Engine/Compute/Join.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .ci/Datasets/Geometry_Engine/Modify/MergeVertices.json

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions Geometry_Engine/Compute/Join.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using BH.oM.Base.Attributes;
using BH.oM.Quantities.Attributes;

namespace BH.Engine.Geometry
{
Expand Down Expand Up @@ -204,6 +207,52 @@ public static List<PolyCurve> IJoin(this List<ICurve> curves, double tolerance =
return sections;
}

/***************************************************/
/**** Join meshes ****/
/***************************************************/

[Description("Joins a list of Meshes into a single Mesh. Disjointed Meshes are allowed to be joined into one mesh.")]
IsakNaslundBh marked this conversation as resolved.
Show resolved Hide resolved
[Input("meshes", "The meshes to join.")]
[Input("mergeVertices", "If true, duplicate vertices will be merged. If false, duplicate vertices will be kept.")]
[Input("tolerance", "Only used if mergeVertices is true. The maximum allowable distance between two vertices for them to be deemed the same vertex.", typeof(Length))]
[Output("mesh", "The joined meshes as a single mesh.")]
public static Mesh Join(this List<Mesh> meshes, bool mergeVertices = false, double tolerance = Tolerance.Distance)
pawelbaran marked this conversation as resolved.
Show resolved Hide resolved
{
if (meshes == null || meshes.Count == 0) //No meshes provided, return null
return null;

Mesh returnMesh = new Mesh() //Set to copy of first mesh as starting point
{
Vertices = meshes[0].Vertices.ToList(),
Faces = meshes[0].Faces.ToList()
};

for (int i = 1; i < meshes.Count; i++) //Add on the rest
{
Mesh mesh = meshes[i];
int vertexCount = returnMesh.Vertices.Count;

returnMesh.Vertices.AddRange(mesh.Vertices);
pawelbaran marked this conversation as resolved.
Show resolved Hide resolved
foreach (Face face in mesh.Faces)
{
returnMesh.Faces.Add(new Face
{
A = face.A + vertexCount,
B = face.B + vertexCount,
C = face.C + vertexCount,
D = face.D == -1 ? -1 : face.D + vertexCount,
});
}
}

if (mergeVertices)
{
returnMesh = returnMesh.MergeVertices(tolerance);
}

return returnMesh;
}

/***************************************************/
}
}
Expand Down
133 changes: 56 additions & 77 deletions Geometry_Engine/Modify/MergeVertices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@
*/

using BH.Engine.Base;
using BH.oM.Data.Collections;
using BH.oM.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using BH.Engine.Data;
using BH.oM.Base.Attributes;
using System.ComponentModel;
using BH.oM.Quantities.Attributes;

namespace BH.Engine.Geometry
{
Expand All @@ -34,104 +39,78 @@ public static partial class Modify
/**** Public Methods ****/
/***************************************************/

public static Mesh MergedVertices(this Mesh mesh, double tolerance = Tolerance.Distance) //TODO: use the point matrix
[PreviousVersion("6.0", "BH.Engine.Geometry.Modify.MergedVertices(BH.oM.Geometry.Mesh, System.Double)")]
[Description("Merges duplicate vertices of the mesh and ensures faces are updated to have their indecies pointig at the index of the new merged vertex.")]
[Input("mesh", "The mesh to merge duplicate vertices of.")]
[Input("tolerance", "The maximum allowable distance between two vertices for them to be deemed the same vertex.", typeof(Length))]
[Output("mesh", "Mesh with merged vertices.")]
public static Mesh MergeVertices(this Mesh mesh, double tolerance = Tolerance.Distance)
{
List<Face> faces = mesh.Faces.Select(x => x.DeepClone()).ToList();
List<VertexIndex> vertices = mesh.Vertices.Select((x, i) => new VertexIndex(x.DeepClone(), i)).ToList();

foreach( Face face in faces)
{
vertices[face.A].Faces.Add(face);
vertices[face.B].Faces.Add(face);
vertices[face.C].Faces.Add(face);
if (mesh == null)
return null;

if (face.IsQuad())
vertices[face.A].Faces.Add(face);
}
//Set up list on structs containing location and index
List<Tuple<Point,int>> vertices = mesh.Vertices.Select((x, i) => new Tuple<Point, int>(x, i)).ToList();
pawelbaran marked this conversation as resolved.
Show resolved Hide resolved

vertices.Sort(delegate (VertexIndex v1, VertexIndex v2)
//Find duplicate vertex indecies using the same methodology as utilised by cull duplicates
double sqDist = tolerance * tolerance;
Func<Tuple<Point, int>, DomainBox> toDomainBox = a => new DomainBox()
{
return v1.Location.SquareDistance(Point.Origin).CompareTo(v2.Location.SquareDistance(Point.Origin));
});
Domains = new Domain[] {
new Domain(a.Item1.X, a.Item1.X),
new Domain(a.Item1.Y, a.Item1.Y),
new Domain(a.Item1.Z, a.Item1.Z),
}
};
Func<DomainBox, DomainBox, bool> treeFunction = (a, b) => a.SquareDistance(b) < sqDist;
Func<Tuple<Point, int>, Tuple<Point, int>, bool> itemFunction = (a, b) => true; // The distance between the boxes is enough to determine if a Point is in range
//Clusters the points within distance tolerance of each other. This is utilising a DB scan methodology to find duplicate vertices
List<List<Tuple<Point, int>>> clusteredVertices = Data.Compute.DomainTreeClusters(vertices, toDomainBox, treeFunction, itemFunction, 1);

//Map of oldIndex -> newindex
Dictionary<int, int> indexMap = new Dictionary<int, int>();

List<int> culledIndices = new List<int>();
double sqTol = tolerance * tolerance;
//Mesh to be returned
Mesh returnMesh = new Mesh();

for (int i = 0; i < vertices.Count; i++)
for (int i = 0; i < clusteredVertices.Count; i++)
{
double distance = vertices[i].Location.Distance(Point.Origin);
int j = i + 1;
while (j < vertices.Count && Math.Abs(vertices[j].Location.Distance(Point.Origin) - distance) < tolerance)
List<Tuple<Point, int>> current = clusteredVertices[i]; //Current list of duplicate vertices
returnMesh.Vertices.Add(current.Select(x => x.Item1).Average()); //Add average point of duplicates

foreach (Tuple<Point, int> vertex in current) //Loop through all the vertices, setting from previous (vertix.Index) to current (current list index)
{
VertexIndex v2 = vertices[j];
if (vertices[i].Location.SquareDistance(vertices[j].Location) < sqTol)
{
SetFaceIndex(v2.Faces, vertices[j].Index, vertices[i].Index);
culledIndices.Add(vertices[j].Index);
v2.Index = vertices[i].Index;
break;
}
j++;
indexMap[vertex.Item2] = i;
}
}

for (int i = 0; i < faces.Count; i++)
foreach (Face face in mesh.Faces) //Loop through all faces, make use of index map to find new index value
{
for (int k = 0; k < culledIndices.Count; k++)
Face newFace = new Face();
int a, b, c, d;
if (indexMap.TryGetValue(face.A, out a))
newFace.A = a;
if (indexMap.TryGetValue(face.B, out b))
newFace.B = b;
if (indexMap.TryGetValue(face.C, out c))
newFace.C = c;

if (face.D != -1) //if -1, keep as -1, as this indicates a triangular face
{
if (faces[i].A > culledIndices[k])
faces[i].A--;
if (faces[i].B > culledIndices[k])
faces[i].B--;
if (faces[i].C > culledIndices[k])
faces[i].C--;
if (faces[i].D > culledIndices[k])
faces[i].D--;
if (indexMap.TryGetValue(face.D, out d))
newFace.D = d;
}
}

return new Mesh { Vertices = vertices.Select(x => x.Location).ToList(), Faces = faces };
}


/***************************************************/
/**** Private Methods ****/
/***************************************************/
else
newFace.D = -1;

private static void SetFaceIndex(List<Face> faces, int from, int to)
{
foreach (Face f in faces)
{
if (f.A == from)
f.A = to;
else if (f.B == from)
f.B = to;
else if (f.C == from)
f.C = to;
else if (f.D == from)
f.D = to;
returnMesh.Faces.Add(newFace);
}
}

return returnMesh;

/***************************************************/
/**** Private Definitions ****/
/***************************************************/

private struct VertexIndex
{
public VertexIndex(Point point, int index)
{
Location = point;
Index = index;
Faces = new List<Face>();
}
public List<Face> Faces;
public Point Location;
public int Index;
}


/***************************************************/
}
}
Expand Down