Refactoring CSG operations
This commit is contained in:
parent
9b70680cce
commit
94befb957c
9 changed files with 80 additions and 57 deletions
|
|
@ -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) =>
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue