Accelerating csg operations

This commit is contained in:
LarsBrubaker 2021-11-20 07:25:47 -08:00
parent aa461bc764
commit fa7447688f
4 changed files with 165 additions and 57 deletions

View file

@ -245,7 +245,7 @@ namespace MatterHackers.PolygonMesh
return implicitResult;
}
private static Mesh ExactBySlicing(IEnumerable<(Mesh mesh, Matrix4X4 matrix)> items2,
private static Mesh ExactBySlicing(IEnumerable<(Mesh mesh, Matrix4X4 matrix)> meshAndMatrix,
CsgModes operation,
IProgress<ProgressStatus> reporter,
CancellationToken cancellationToken,
@ -255,13 +255,36 @@ namespace MatterHackers.PolygonMesh
var progressStatus = new ProgressStatus();
var totalOperations = 0;
var transformedMeshes = new List<Mesh>();
foreach (var (mesh, matrix) in items2)
foreach (var (mesh, matrix) in meshAndMatrix)
{
totalOperations += mesh.Faces.Count;
transformedMeshes.Add(mesh.Copy(CancellationToken.None));
transformedMeshes.Last().Transform(matrix);
}
var plansByMesh = new List<List<Plane>>();
var uniquePlanes = new HashSet<Plane>();
for (int i = 0; i < transformedMeshes.Count; i++)
{
var mesh = transformedMeshes[i];
plansByMesh.Add(new List<Plane>());
for (int j = 0; j < transformedMeshes[i].Faces.Count; j++)
{
var face = mesh.Faces[j];
var cutPlane = new Plane(mesh.Vertices[face.v0].AsVector3(), mesh.Vertices[face.v1].AsVector3(), mesh.Vertices[face.v2].AsVector3());
plansByMesh[i].Add(cutPlane);
uniquePlanes.Add(cutPlane);
}
}
PlaneNormalXSorter planeSorter = new PlaneNormalXSorter(uniquePlanes);
var transformTo0Planes = new Dictionary<Plane, (Matrix4X4 matrix, Matrix4X4 inverted)>();
foreach(var plane in uniquePlanes)
{
var matrix = SliceLayer.GetTransformTo0Plane(plane);
transformTo0Planes[plane] = (matrix, matrix.Inverted);
}
double amountPerOperation = 1.0 / totalOperations * amountPerOperationIn;
double percentCompleted = percentCompletedIn;
@ -279,12 +302,11 @@ namespace MatterHackers.PolygonMesh
{
var face = mesh1.Faces[faceIndex];
var cutPlane = new Plane(mesh1.Vertices[face.v0].AsVector3(), mesh1.Vertices[face.v1].AsVector3(), mesh1.Vertices[face.v2].AsVector3());
var flattenedMatrix = CoPlanarFaces.GetFlattenedMatrix(cutPlane);
var flattenedMatrixInverted = flattenedMatrix.Inverted;
var cutPlane = plansByMesh[mesh1Index][faceIndex];
var totalSlice = new Polygons();
var firstSlice = true;
var transformTo0Plane = transformTo0Planes[cutPlane].matrix;
for (var sliceMeshIndex = 0; sliceMeshIndex < transformedMeshes.Count; sliceMeshIndex++)
{
if (mesh1Index == sliceMeshIndex)
@ -294,7 +316,7 @@ namespace MatterHackers.PolygonMesh
var mesh2 = transformedMeshes[sliceMeshIndex];
// calculate and add the PWN face from the loops
var slice = SliceLayer.CreateSlice(mesh2, cutPlane);
var slice = SliceLayer.CreateSlice(mesh2, cutPlane, transformTo0Plane);
if (firstSlice)
{
totalSlice = slice;
@ -308,7 +330,7 @@ namespace MatterHackers.PolygonMesh
// now we have the total loops that this polygon can intersect from the other meshes
// make a polygon for this face
var facePolygon = CoPlanarFaces.GetFacePolygon(mesh1, faceIndex, cutPlane, flattenedMatrix);
var facePolygon = CoPlanarFaces.GetFacePolygon(mesh1, faceIndex, transformTo0Plane);
var polygonShape = new Polygons();
// clip against the slice based on the parameters
@ -355,7 +377,7 @@ namespace MatterHackers.PolygonMesh
{
var preAddCount = resultsMesh.Vertices.Count;
// mesh the new polygon and add it to the resultsMesh
polygonShape.Vertices().TriangulateFaces(null, resultsMesh, 0, flattenedMatrixInverted);
polygonShape.Vertices(1).TriangulateFaces(null, resultsMesh, 0, transformTo0Planes[cutPlane].inverted);
// TODO: map all the added vertices that can be back to the original polygon positions
// for (int i = preAddCount; i< resultsMesh.Vertices.Count; i++)
@ -369,7 +391,7 @@ namespace MatterHackers.PolygonMesh
// keep track of the adds so we can process the coplanar faces after
for (int i = faceCountPreAdd; i < resultsMesh.Faces.Count; i++)
{
coPlanarFaces.StoreFaceAdd(cutPlane, mesh1Index, faceIndex, i);
coPlanarFaces.StoreFaceAdd(planeSorter, cutPlane, mesh1Index, faceIndex, i);
// make sure our added faces are the right direction
if (resultsMesh.Faces[i].normal.Dot(expectedFaceNormal) < 0)
{
@ -379,7 +401,7 @@ namespace MatterHackers.PolygonMesh
}
else // we did not add any faces but we will still keep track of this polygons plan
{
coPlanarFaces.StoreFaceAdd(cutPlane, mesh1Index, faceIndex, -1);
coPlanarFaces.StoreFaceAdd(planeSorter, cutPlane, mesh1Index, faceIndex, -1);
}
percentCompleted += amountPerOperation;
@ -393,6 +415,7 @@ namespace MatterHackers.PolygonMesh
}
}
// handle the co-planar faces
var faceIndicesToRemove = new HashSet<int>();
foreach (var plane in coPlanarFaces.Planes)
{
@ -400,7 +423,7 @@ namespace MatterHackers.PolygonMesh
if (meshIndices.Count() > 1)
{
// check if more than one mesh has this polygons on this plan
var flattenedMatrix = CoPlanarFaces.GetFlattenedMatrix(plane);
var flattenedMatrix = transformTo0Planes[plane].matrix;
// depending on the operation add or remove polygons that are planar
switch (operation)

View file

@ -36,10 +36,10 @@ using MatterHackers.VectorMath;
namespace MatterHackers.PolygonMesh
{
using Polygon = List<IntPoint>;
using Polygons = List<List<IntPoint>>;
public class CoPlanarFaces
using Polygon = List<IntPoint>;
using Polygons = List<List<IntPoint>>;
public class CoPlanarFaces
{
private new Dictionary<Plane, Dictionary<int, List<(int sourceFaceIndex, int destFaceIndex)>>> coPlanarFaces
= new Dictionary<Plane, Dictionary<int, List<(int sourceFaceIndex, int destFaceIndex)>>>();
@ -64,7 +64,7 @@ namespace MatterHackers.PolygonMesh
}
public IEnumerable<(int sourceFaceIndex, int destFaceIndex)> FacesSetsForPlaneAndMesh(Plane plane, int meshIndex)
{
{
if (coPlanarFaces[plane].ContainsKey(meshIndex))
{
foreach (var faceIndices in coPlanarFaces[plane][meshIndex])
@ -72,20 +72,10 @@ namespace MatterHackers.PolygonMesh
yield return faceIndices;
}
}
}
public static Matrix4X4 GetFlattenedMatrix(Plane cutPlane)
{
var rotation = new Quaternion(cutPlane.Normal, Vector3.UnitZ);
var flattenedMatrix = Matrix4X4.CreateRotation(rotation);
flattenedMatrix *= Matrix4X4.CreateTranslation(0, 0, -cutPlane.DistanceFromOrigin);
return flattenedMatrix;
}
public static Polygon GetFacePolygon(Mesh mesh1, int faceIndex, Plane cutPlane, Matrix4X4 flattenedMatrix)
public static Polygon GetFacePolygon(Mesh mesh1, int faceIndex, Matrix4X4 meshTo0Plane)
{
var meshTo0Plane = flattenedMatrix * Matrix4X4.CreateScale(1000);
var facePolygon = new Polygon();
var vertices = mesh1.Vertices;
var face = mesh1.Faces[faceIndex];
@ -119,29 +109,29 @@ namespace MatterHackers.PolygonMesh
meshesWithFaces.Sort();
// add the faces that we should
foreach(var meshIndex in meshesWithFaces)
{
foreach(var faces in FacesSetsForPlaneAndMesh(plane, meshIndex))
{
foreach (var meshIndex in meshesWithFaces)
{
foreach (var faces in FacesSetsForPlaneAndMesh(plane, meshIndex))
{
faceIndicesToRemove.Add(faces.destFaceIndex);
}
}
}
}
// subtract every face from the mesh 0 faces
// teselate and add what is left
var keepPolygons = new Polygons();
foreach (var keepFaceSets in FacesSetsForPlaneAndMesh(plane, 0))
{
keepPolygons = keepPolygons.Union(GetFacePolygon(transformedMeshes[0], keepFaceSets.sourceFaceIndex, plane, flattenedMatrix));
keepPolygons = keepPolygons.Union(GetFacePolygon(transformedMeshes[0], keepFaceSets.sourceFaceIndex, flattenedMatrix));
}
// iterate all the meshes that need to be subtracted
var removePoygons = new Polygons();
for (int removeMeshIndex= 1; removeMeshIndex < meshesWithFaces.Count; removeMeshIndex++)
{
for (int removeMeshIndex = 1; removeMeshIndex < meshesWithFaces.Count; removeMeshIndex++)
{
foreach (var removeFaceSets in FacesSetsForPlaneAndMesh(plane, removeMeshIndex))
{
removePoygons = removePoygons.Union(GetFacePolygon(transformedMeshes[removeMeshIndex], removeFaceSets.sourceFaceIndex, plane, flattenedMatrix));
removePoygons = removePoygons.Union(GetFacePolygon(transformedMeshes[removeMeshIndex], removeFaceSets.sourceFaceIndex, flattenedMatrix));
}
}
@ -152,7 +142,7 @@ namespace MatterHackers.PolygonMesh
clipper.Execute(ClipType.ctDifference, polygonShape);
// teselate and add all the new polygons
polygonShape.Vertices().TriangulateFaces(null, resultsMesh, 0, flattenedMatrix.Inverted);
polygonShape.Vertices(1).TriangulateFaces(null, resultsMesh, 0, flattenedMatrix.Inverted);
}
public void IntersectFaces(Plane plane, List<Mesh> transformedMeshes, Mesh resultsMesh, Matrix4X4 flattenedMatrix, HashSet<int> faceIndicesToRemove)
@ -183,14 +173,14 @@ namespace MatterHackers.PolygonMesh
var unionedPoygons = new Polygons();
foreach (var removeFaceSets in FacesSetsForPlaneAndMesh(plane, meshIndex))
{
unionedPoygons = unionedPoygons.Union(GetFacePolygon(transformedMeshes[meshIndex], removeFaceSets.sourceFaceIndex, plane, flattenedMatrix));
unionedPoygons = unionedPoygons.Union(GetFacePolygon(transformedMeshes[meshIndex], removeFaceSets.sourceFaceIndex, flattenedMatrix));
}
polygonsByMesh.Add(unionedPoygons);
}
var total = new Polygons(polygonsByMesh[0]);
for (int i=1; i<polygonsByMesh.Count; i++)
for (int i = 1; i < polygonsByMesh.Count; i++)
{
var polygonShape = new Polygons();
var clipper = new Clipper();
@ -202,7 +192,7 @@ namespace MatterHackers.PolygonMesh
}
// teselate and add all the new polygons
total.Vertices().TriangulateFaces(null, resultsMesh, 0, flattenedMatrix.Inverted);
total.Vertices(1).TriangulateFaces(null, resultsMesh, 0, flattenedMatrix.Inverted);
}
public void UnionFaces(Plane plane, List<Mesh> transformedMeshes, Mesh resultsMesh, Matrix4X4 flattenedMatrix)
@ -211,10 +201,10 @@ namespace MatterHackers.PolygonMesh
var meshesWithFaces = MeshIndicesForPlane(plane).ToList();
if (meshesWithFaces.Count < 2)
{
{
// no faces to add
return;
}
}
// sort them so we can process each group into intersections
meshesWithFaces.Sort();
@ -228,7 +218,7 @@ namespace MatterHackers.PolygonMesh
{
if (!addedFaces.Contains(sourceFaceIndex))
{
meshPolygons[i].Add(GetFacePolygon(transformedMeshes[i], sourceFaceIndex, plane, flattenedMatrix));
meshPolygons[i].Add(GetFacePolygon(transformedMeshes[i], sourceFaceIndex, flattenedMatrix));
addedFaces.Add(sourceFaceIndex);
}
}
@ -239,8 +229,8 @@ namespace MatterHackers.PolygonMesh
for (int i = 0; i < meshesWithFaces.Count; i++)
{
// add all the faces for mesh j
for (int j=i+1; j<meshesWithFaces.Count; j++)
{
for (int j = i + 1; j < meshesWithFaces.Count; j++)
{
var clipper = new Clipper();
clipper.AddPaths(meshPolygons[i], PolyType.ptSubject, true);
clipper.AddPaths(meshPolygons[j], PolyType.ptClip, true);
@ -255,7 +245,7 @@ namespace MatterHackers.PolygonMesh
// now union all the intersections
var totalSlices = new Polygons(intersectionSets[0]);
for (int i = 1; i < intersectionSets.Count; i++)
{
{
// clip against the slice based on the parameters
var clipper = new Clipper();
clipper.AddPaths(totalSlices, PolyType.ptSubject, true);
@ -264,23 +254,25 @@ namespace MatterHackers.PolygonMesh
}
// teselate and add all the new polygons
totalSlices.Vertices().TriangulateFaces(null, resultsMesh, 0, flattenedMatrix.Inverted);
totalSlices.Vertices(1).TriangulateFaces(null, resultsMesh, 0, flattenedMatrix.Inverted);
}
public void StoreFaceAdd(Plane facePlane,
public void StoreFaceAdd(PlaneNormalXSorter planeSorter,
Plane facePlane,
int sourceMeshIndex,
int sourceFaceIndex,
int destFaceIndex)
{
foreach (var plane in coPlanarFaces.Keys)
// look through all the planes that are close to this one
var plane = planeSorter.FindPlane(facePlane, .02, .0002);
if (plane != null)
{
// check if they are close enough
if (facePlane.Equals(plane))
{
facePlane = plane;
break;
}
facePlane = plane.Value;
}
else
{
int a = 0;
}
if (!coPlanarFaces.ContainsKey(facePlane))
{

View file

@ -0,0 +1,93 @@
/*
Copyright (c) 2019, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using ClipperLib;
using MatterHackers.VectorMath;
namespace MatterHackers.PolygonMesh
{
public class PlaneNormalXSorter : IComparer<Plane>
{
private readonly List<Plane> planes;
public PlaneNormalXSorter(IEnumerable<Plane> inputPlanes)
{
planes = new List<Plane>(inputPlanes);
planes.Sort(this);
}
public int Compare(Plane a, Plane b)
{
return a.Normal.X.CompareTo(b.Normal.X);
}
public Plane? FindPlane(Plane searchPlane,
double distanceErrorValue = .01,
double normalErrorValue = .0001)
{
Plane testPlane = searchPlane;
int index = planes.BinarySearch(testPlane, this);
if (index < 0)
{
index = ~index;
}
// we have the starting index now get all the vertices that are close enough starting from here
for (int i = index; i < planes.Count; i++)
{
if (Math.Abs(planes[i].Normal.X - searchPlane.Normal.X) > normalErrorValue)
{
// we are too far away in x, we are done with this direction
break;
}
if (planes[i].Equals(searchPlane, distanceErrorValue, normalErrorValue))
{
return planes[i];
}
}
for (int i = index - 1; i >= 0; i--)
{
if (Math.Abs(planes[i].Normal.X - searchPlane.Normal.X) > normalErrorValue)
{
// we are too far away in x, we are done with this direction
break;
}
if (planes[i].Equals(searchPlane, distanceErrorValue, normalErrorValue))
{
return planes[i];
}
}
return null;
}
}
}

@ -1 +1 @@
Subproject commit b380ed2929a7fbcc1dd757b5a697d8b1e535456a
Subproject commit beea93ece48c0ec46fbf0b037106aa3a9e3baf5a