diff --git a/MatterControl.MeshOperations/BooleanProcessing.cs b/MatterControl.MeshOperations/BooleanProcessing.cs index 14d9616ef..af8dac407 100644 --- a/MatterControl.MeshOperations/BooleanProcessing.cs +++ b/MatterControl.MeshOperations/BooleanProcessing.cs @@ -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 reporter, CancellationToken cancellationToken, @@ -255,13 +255,36 @@ namespace MatterHackers.PolygonMesh var progressStatus = new ProgressStatus(); var totalOperations = 0; var transformedMeshes = new List(); - 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>(); + var uniquePlanes = new HashSet(); + for (int i = 0; i < transformedMeshes.Count; i++) + { + var mesh = transformedMeshes[i]; + plansByMesh.Add(new List()); + 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(); + 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(); 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) diff --git a/MatterControl.MeshOperations/CoPlanarFaces.cs b/MatterControl.MeshOperations/CoPlanarFaces.cs index dd1c7b3f6..979b13a6f 100644 --- a/MatterControl.MeshOperations/CoPlanarFaces.cs +++ b/MatterControl.MeshOperations/CoPlanarFaces.cs @@ -36,10 +36,10 @@ using MatterHackers.VectorMath; namespace MatterHackers.PolygonMesh { - using Polygon = List; - using Polygons = List>; - - public class CoPlanarFaces + using Polygon = List; + using Polygons = List>; + + public class CoPlanarFaces { private new Dictionary>> coPlanarFaces = new Dictionary>>(); @@ -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 transformedMeshes, Mesh resultsMesh, Matrix4X4 flattenedMatrix, HashSet 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 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 + { + private readonly List planes; + + public PlaneNormalXSorter(IEnumerable inputPlanes) + { + planes = new List(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; + } + } +} \ No newline at end of file diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index b380ed292..beea93ece 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit b380ed2929a7fbcc1dd757b5a697d8b1e535456a +Subproject commit beea93ece48c0ec46fbf0b037106aa3a9e3baf5a