Refactoring CSG operations

This commit is contained in:
LarsBrubaker 2021-06-17 11:50:58 -07:00
parent 9b70680cce
commit 94befb957c
9 changed files with 80 additions and 57 deletions

View file

@ -29,6 +29,7 @@ either expressed or implied, of the FreeBSD Project.
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using MatterHackers.Agg; using MatterHackers.Agg;
@ -37,6 +38,13 @@ using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{ {
public enum CsgModes
{
Union,
Subtract,
Intersect
}
public static class BooleanProcessing public static class BooleanProcessing
{ {
private const string BooleanAssembly = "609_Boolean_bin.dll"; private const string BooleanAssembly = "609_Boolean_bin.dll";
@ -50,13 +58,58 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
[DllImport(BooleanAssembly, CallingConvention = CallingConvention.Cdecl)] [DllImport(BooleanAssembly, CallingConvention = CallingConvention.Cdecl)]
public static extern void DoBooleanOperation(double[] va, int vaCount, int[] fa, int faCount, double[] vb, int vbCount, int[] fb, int fbCount, int operation, out IntPtr pVc, out int vcCount, out IntPtr pVf, out int vfCount); public static extern void DoBooleanOperation(double[] va, int vaCount, int[] fa, int faCount, double[] vb, int vbCount, int[] fb, int fbCount, int operation, out IntPtr pVc, out int vcCount, out IntPtr pVf, out int vfCount);
public static Mesh DoArray(System.Collections.Generic.IEnumerable<(Mesh mesh, Matrix4X4 matrix)> items,
CsgModes operation,
IProgress<ProgressStatus> reporter,
CancellationToken cancellationToken)
{
var progressStatus = new ProgressStatus();
var totalOperations = items.Count() - 1;
double amountPerOperation = 1.0 / totalOperations;
double percentCompleted = 0;
var first = items.First();
var resultsMesh = first.mesh;
var firstWorldMatrix = first.matrix;
foreach (var item in items)
{
if (item != first)
{
var itemWorldMatrix = item.matrix;
resultsMesh = BooleanProcessing.Do(item.mesh,
itemWorldMatrix,
// other mesh
resultsMesh,
firstWorldMatrix,
// operation
operation,
// reporting
reporter,
amountPerOperation,
percentCompleted,
progressStatus,
cancellationToken);
// after the first union we are working with the transformed mesh and don't need the first transform
firstWorldMatrix = Matrix4X4.Identity;
percentCompleted += amountPerOperation;
progressStatus.Progress0To1 = percentCompleted;
reporter?.Report(progressStatus);
}
}
return resultsMesh;
}
public static Mesh Do(Mesh inMeshA, public static Mesh Do(Mesh inMeshA,
Matrix4X4 matrixA, Matrix4X4 matrixA,
// mesh B // mesh B
Mesh inMeshB, Mesh inMeshB,
Matrix4X4 matrixB, Matrix4X4 matrixB,
// operation // operation
int operation, CsgModes operation,
// reporting // reporting
IProgress<ProgressStatus> reporter, IProgress<ProgressStatus> reporter,
double amountPerOperation, double amountPerOperation,
@ -91,7 +144,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
fb, fb,
fb.Length, fb.Length,
// operation // operation
operation, (int)operation,
// results // results
out pVc, out pVc,
out int vcCount, out int vcCount,
@ -143,7 +196,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
switch (operation) switch (operation)
{ {
case 0: case CsgModes.Union:
return PolygonMesh.Csg.CsgOperations.Union(meshA, return PolygonMesh.Csg.CsgOperations.Union(meshA,
meshB, meshB,
(status, progress0To1) => (status, progress0To1) =>
@ -157,7 +210,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
}, },
cancellationToken); cancellationToken);
case 1: case CsgModes.Subtract:
return PolygonMesh.Csg.CsgOperations.Subtract(meshA, return PolygonMesh.Csg.CsgOperations.Subtract(meshA,
meshB, meshB,
(status, progress0To1) => (status, progress0To1) =>
@ -171,7 +224,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
}, },
cancellationToken); cancellationToken);
case 2: case CsgModes.Intersect:
return PolygonMesh.Csg.CsgOperations.Intersect(meshA, return PolygonMesh.Csg.CsgOperations.Intersect(meshA,
meshB, meshB,
(status, progress0To1) => (status, progress0To1) =>

View file

@ -119,7 +119,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{ {
var result = BooleanProcessing.Do(remove.Mesh, remove.WorldMatrix(), var result = BooleanProcessing.Do(remove.Mesh, remove.WorldMatrix(),
first.Mesh, first.WorldMatrix(), first.Mesh, first.WorldMatrix(),
2, reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken); CsgModes.Intersect, reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken);
var inverse = first.WorldMatrix(); var inverse = first.WorldMatrix();
inverse.Invert(); inverse.Invert();

View file

@ -51,6 +51,8 @@ namespace MatterHackers.MatterControl.DesignTools
public double Distance { get; set; } = 2; public double Distance { get; set; } = 2;
public int NumCells { get; set; } = 64;
private static DMesh3 GenerateMeshF(BoundedImplicitFunction3d root, int numcells) private static DMesh3 GenerateMeshF(BoundedImplicitFunction3d root, int numcells)
{ {
var bounds = root.Bounds(); var bounds = root.Bounds();
@ -71,14 +73,13 @@ namespace MatterHackers.MatterControl.DesignTools
return c.Mesh; return c.Mesh;
} }
public static Mesh HollowOut(Mesh inMesh, double distance) public static Mesh HollowOut(Mesh inMesh, double distance, int numCells)
{ {
// Convert to DMesh3 // Convert to DMesh3
var mesh = inMesh.ToDMesh3(); var mesh = inMesh.ToDMesh3();
// Create instance of BoundedImplicitFunction3d interface // Create instance of BoundedImplicitFunction3d interface
int numcells = 64; double meshCellsize = mesh.CachedBounds.MaxDim / numCells;
double meshCellsize = mesh.CachedBounds.MaxDim / numcells;
var levelSet = new MeshSignedDistanceGrid(mesh, meshCellsize) var levelSet = new MeshSignedDistanceGrid(mesh, meshCellsize)
{ {
@ -96,11 +97,11 @@ namespace MatterHackers.MatterControl.DesignTools
A = implicitMesh, A = implicitMesh,
Offset = -distance Offset = -distance
}, },
128); numCells);
// make sure it is a reasonable number of polygons // make sure it is a reasonable number of polygons
var reducer = new Reducer(insetMesh); // var reducer = new Reducer(insetMesh);
reducer.ReduceToTriangleCount(Math.Max(inMesh.Faces.Count / 2, insetMesh.TriangleCount / 10)); // reducer.ReduceToTriangleCount(Math.Max(inMesh.Faces.Count / 2, insetMesh.TriangleCount / 10));
// Convert to PolygonMesh and reverse faces // Convert to PolygonMesh and reverse faces
var interior = insetMesh.ToMesh(); var interior = insetMesh.ToMesh();
@ -132,7 +133,7 @@ namespace MatterHackers.MatterControl.DesignTools
{ {
var newMesh = new Object3D() var newMesh = new Object3D()
{ {
Mesh = HollowOut(sourceItem.Mesh, this.Distance) Mesh = HollowOut(sourceItem.Mesh, this.Distance, this.NumCells)
}; };
newMesh.CopyProperties(sourceItem, Object3DPropertyFlags.All); newMesh.CopyProperties(sourceItem, Object3DPropertyFlags.All);
this.Children.Add(newMesh); this.Children.Add(newMesh);

View file

@ -103,48 +103,17 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
return; return;
} }
var first = participants.First(); var items = participants.Select(i => (i.Mesh, i.WorldMatrix(SourceContainer)));
var resultsMesh = first.Mesh; var resultsMesh = BooleanProcessing.DoArray(items,
var firstWorldMatrix = first.WorldMatrix(SourceContainer); CsgModes.Union,
reporter,
var totalOperations = participants.Count() - 1; cancellationToken);
double amountPerOperation = 1.0 / totalOperations;
double percentCompleted = 0;
var progressStatus = new ProgressStatus();
foreach (var item in participants)
{
if (item != first)
{
var itemWorldMatrix = item.WorldMatrix(SourceContainer);
resultsMesh = BooleanProcessing.Do(item.Mesh,
itemWorldMatrix,
// other mesh
resultsMesh,
firstWorldMatrix,
// operation
0,
// reporting
reporter,
amountPerOperation,
percentCompleted,
progressStatus,
cancellationToken);
// after the first union we are working with the transformed mesh and don't need the first transform
firstWorldMatrix = Matrix4X4.Identity;
percentCompleted += amountPerOperation;
progressStatus.Progress0To1 = percentCompleted;
reporter?.Report(progressStatus);
}
}
var resultsItem = new Object3D() var resultsItem = new Object3D()
{ {
Mesh = resultsMesh Mesh = resultsMesh
}; };
resultsItem.CopyProperties(first, Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix)); resultsItem.CopyProperties(participants.First(), Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix));
this.Children.Add(resultsItem); this.Children.Add(resultsItem);
SourceContainer.Visible = false; SourceContainer.Visible = false;
} }

View file

@ -119,7 +119,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
var itemWorldMatrix = item.WorldMatrix(SourceContainer); var itemWorldMatrix = item.WorldMatrix(SourceContainer);
resultsMesh = BooleanProcessing.Do(item.Mesh, itemWorldMatrix, resultsMesh = BooleanProcessing.Do(item.Mesh, itemWorldMatrix,
resultsMesh, firstWorldMatrix, resultsMesh, firstWorldMatrix,
2, CsgModes.Intersect,
reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken); reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken);
// after the first union we are working with the transformed mesh and don't need the first transform // after the first union we are working with the transformed mesh and don't need the first transform
firstWorldMatrix = Matrix4X4.Identity; firstWorldMatrix = Matrix4X4.Identity;

View file

@ -121,9 +121,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
// remove the paint from the original // remove the paint from the original
var subtract = BooleanProcessing.Do(keep.obj3D.Mesh, keep.matrix, var subtract = BooleanProcessing.Do(keep.obj3D.Mesh, keep.matrix,
paint.obj3D.Mesh, paint.matrix, 1, reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken); paint.obj3D.Mesh, paint.matrix, CsgModes.Subtract, reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken);
var intersect = BooleanProcessing.Do(keep.obj3D.Mesh, keep.matrix, var intersect = BooleanProcessing.Do(keep.obj3D.Mesh, keep.matrix,
paint.obj3D.Mesh, paint.matrix, 2, reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken); paint.obj3D.Mesh, paint.matrix, CsgModes.Intersect, reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken);
var inverseKeep = keep.matrix.Inverted; var inverseKeep = keep.matrix.Inverted;
subtract.Transform(inverseKeep); subtract.Transform(inverseKeep);

View file

@ -267,7 +267,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
paint.Mesh, paint.Mesh,
paint.WorldMatrix(SourceContainer), paint.WorldMatrix(SourceContainer),
// operation type // operation type
2, CsgModes.Intersect,
// reporting data // reporting data
reporter, reporter,
amountPerOperation, amountPerOperation,
@ -281,7 +281,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
paint.Mesh, paint.Mesh,
paint.WorldMatrix(SourceContainer), paint.WorldMatrix(SourceContainer),
// operation type // operation type
1, CsgModes.Subtract,
// reporting data // reporting data
reporter, reporter,
amountPerOperation, amountPerOperation,

View file

@ -161,7 +161,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
progressStatus.Status = "Do CSG"; progressStatus.Status = "Do CSG";
reporter?.Report(progressStatus); reporter?.Report(progressStatus);
var result = BooleanProcessing.Do(keep.obj3D.Mesh, keep.matrix, var result = BooleanProcessing.Do(keep.obj3D.Mesh, keep.matrix,
remove.obj3D.Mesh, remove.matrix, 1, reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken); remove.obj3D.Mesh, remove.matrix, CsgModes.Subtract, reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken);
var inverse = keep.matrix.Inverted; var inverse = keep.matrix.Inverted;
result.Transform(inverse); result.Transform(inverse);

View file

@ -209,7 +209,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
remove.Mesh, remove.Mesh,
remove.WorldMatrix(SourceContainer), remove.WorldMatrix(SourceContainer),
// operation type // operation type
1, CsgModes.Subtract,
// reporting // reporting
reporter, reporter,
amountPerOperation, amountPerOperation,