Merge pull request #3121 from larsbrubaker/design_tools
Improving the csg tests
This commit is contained in:
commit
4fc73cfcbf
7 changed files with 192 additions and 52 deletions
|
|
@ -209,6 +209,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
|
|||
if (paintObjects.Any()
|
||||
&& keepObjects.Any())
|
||||
{
|
||||
var totalOperations = paintObjects.Count * keepObjects.Count;
|
||||
double amountPerOperation = 1.0 / totalOperations;
|
||||
double percentCompleted = 0;
|
||||
|
||||
foreach (var paint in paintObjects)
|
||||
{
|
||||
var transformedPaint = Mesh.Copy(paint.Mesh, cancellationToken);
|
||||
|
|
@ -223,7 +227,15 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
|
|||
transformedKeep.Transform(keep.WorldMatrix());
|
||||
|
||||
// remove the paint from the original
|
||||
var intersectAndSubtract = PolygonMesh.Csg.CsgOperations.IntersectAndSubtract(transformedKeep, transformedPaint);
|
||||
var intersectAndSubtract = PolygonMesh.Csg.CsgOperations.IntersectAndSubtract(transformedKeep, transformedPaint, (status, progress0To1) =>
|
||||
{
|
||||
// Abort if flagged
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
progressStatus.Status = status;
|
||||
progressStatus.Progress0To1 = percentCompleted + amountPerOperation * progress0To1;
|
||||
reporter?.Report(progressStatus);
|
||||
}, cancellationToken);
|
||||
var inverseKeep = keep.WorldMatrix();
|
||||
inverseKeep.Invert();
|
||||
intersectAndSubtract.subtract.Transform(inverseKeep);
|
||||
|
|
|
|||
|
|
@ -216,12 +216,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
|
|||
{
|
||||
progressStatus.Status = "Copy Remove";
|
||||
reporter?.Report(progressStatus);
|
||||
var transformedRemove = Mesh.Copy(remove.Mesh, CancellationToken.None);
|
||||
var transformedRemove = Mesh.Copy(remove.Mesh, cancellationToken);
|
||||
transformedRemove.Transform(remove.WorldMatrix());
|
||||
|
||||
progressStatus.Status = "Copy Keep";
|
||||
reporter?.Report(progressStatus);
|
||||
var transformedKeep = Mesh.Copy(keep.Mesh, CancellationToken.None);
|
||||
var transformedKeep = Mesh.Copy(keep.Mesh, cancellationToken);
|
||||
transformedKeep.Transform(keep.WorldMatrix());
|
||||
|
||||
progressStatus.Status = "Do CSG";
|
||||
|
|
|
|||
|
|
@ -2466,7 +2466,9 @@ namespace MatterHackers.MatterControl.PrinterCommunication
|
|||
}
|
||||
|
||||
// times up turn off heaters
|
||||
if (ContinuWaitingToTurnOffHeaters)
|
||||
if (ContinuWaitingToTurnOffHeaters
|
||||
&& !PrinterIsPrinting
|
||||
&& !PrinterIsPaused)
|
||||
{
|
||||
UiThread.RunOnIdle(() =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 6cb7f7949ccfc62096bf5a3cbedc39fe8fe56ece
|
||||
Subproject commit 3ca863d643ff47053e74424efd13db13048c7892
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit ed0cd4a8625acf26eb41cb45f430a03049ea577e
|
||||
Subproject commit 39695a622650ffd4abdbe5de2921675104e94b78
|
||||
|
|
@ -127,6 +127,10 @@
|
|||
<Project>{670BDDFF-927B-425D-9DD1-22ACB14356EB}</Project>
|
||||
<Name>PlatformWin32</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Submodules\agg-sharp\PolygonMesh\Net3dBool\Net3dBool.csproj">
|
||||
<Project>{7ee4636d-8a92-4015-9562-7fcd6add0645}</Project>
|
||||
<Name>Net3dBool</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Submodules\agg-sharp\PolygonMesh\PolygonMesh.csproj">
|
||||
<Project>{86F6AAF2-9B50-40B8-A427-1897D76471C5}</Project>
|
||||
<Name>PolygonMesh</Name>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ using MatterHackers.MatterControl.Tests.Automation;
|
|||
using MatterHackers.PolygonMesh.Csg;
|
||||
using MatterHackers.PolygonMesh.Processors;
|
||||
using MatterHackers.VectorMath;
|
||||
using Net3dBool;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace MatterHackers.PolygonMesh.UnitTests
|
||||
|
|
@ -51,7 +52,7 @@ namespace MatterHackers.PolygonMesh.UnitTests
|
|||
public class MeshCsgTests
|
||||
{
|
||||
[Test]
|
||||
public void CylinderMinusCylinder()
|
||||
public void CsgCylinderMinusCylinder()
|
||||
{
|
||||
AggContext.StaticData = new FileSystemStaticData(TestContext.CurrentContext.ResolveProjectPath(4, "StaticData"));
|
||||
MatterControlUtilities.OverrideAppDataLocation(TestContext.CurrentContext.ResolveProjectPath(4));
|
||||
|
|
@ -61,66 +62,102 @@ namespace MatterHackers.PolygonMesh.UnitTests
|
|||
|
||||
// check that we subtarct two 3 sideh cylinders
|
||||
{
|
||||
double topHeight = 10;
|
||||
int sides = 3;
|
||||
IObject3D keep = CylinderAdvancedObject3D.Create(20, 20, sides);
|
||||
IObject3D subtract = CylinderAdvancedObject3D.Create(10, 20, sides);
|
||||
IObject3D keep = CylinderAdvancedObject3D.Create(20, topHeight * 2, sides);
|
||||
IObject3D subtract = CylinderAdvancedObject3D.Create(10, topHeight * 2, sides);
|
||||
|
||||
var keepMesh = keep.Mesh;
|
||||
var subtractMesh = subtract.Mesh;
|
||||
|
||||
var split1 = new DebugFace()
|
||||
if (false)
|
||||
{
|
||||
EvaluateHeight = 10,
|
||||
FileName = "Split1"
|
||||
};
|
||||
var split1 = new DebugFace()
|
||||
{
|
||||
EvaluateHeight = topHeight,
|
||||
FileName = "Split1"
|
||||
};
|
||||
|
||||
var resultMesh = keepMesh.Subtract(subtractMesh, null, CancellationToken.None);//,
|
||||
//split1.Split, split1.Result);
|
||||
BooleanModeller.Object1SplitFace = split1.Split;
|
||||
BooleanModeller.Object1SplitResults = split1.Result;
|
||||
|
||||
BooleanModeller.Object1ClassifyFace = split1.Classify1;
|
||||
BooleanModeller.Object2ClassifyFace = split1.Classify2;
|
||||
}
|
||||
|
||||
var resultMesh = keepMesh.Subtract(subtractMesh, null, CancellationToken.None);
|
||||
|
||||
// this is for debuging the opperation
|
||||
//split1.FinishOutput();
|
||||
//resultMesh.Save("c:/temp/mesh1.stl", CancellationToken.None);
|
||||
|
||||
Assert.AreEqual(12, CountFacesAtHeight(keepMesh, 10));
|
||||
var topZero = new Vector3(0, 0, topHeight);
|
||||
foreach (var topVertex in keepMesh.Vertices
|
||||
.Where((v) => v.Position.Z == topHeight && v.Position != topZero)
|
||||
.Select((gv) => gv.Position))
|
||||
{
|
||||
Assert.IsTrue(resultMesh.Vertices.Where((v) => v.Position == topVertex).Any(), "Have all top vertexes");
|
||||
}
|
||||
foreach (var topVertex in subtractMesh.Vertices
|
||||
.Where((v) => v.Position.Z == topHeight && v.Position != topZero)
|
||||
.Select((gv) => gv.Position))
|
||||
{
|
||||
Assert.IsTrue(resultMesh.Vertices.Where((v) => v.Position == topVertex).Any(), "Have all top vertexes");
|
||||
}
|
||||
}
|
||||
|
||||
// check that we subtarct two 3 sideh cylinders
|
||||
{
|
||||
int sides = 3;
|
||||
IObject3D keep = CylinderAdvancedObject3D.Create(20, 20, sides);
|
||||
IObject3D subtract = CylinderAdvancedObject3D.Create(10, 21, sides);
|
||||
IObject3D subtract = CylinderAdvancedObject3D.Create(10, 22, sides);
|
||||
|
||||
var keepMesh = keep.Mesh;
|
||||
var subtractMesh = subtract.Mesh;
|
||||
|
||||
var split1 = new DebugFace()
|
||||
if (false)
|
||||
{
|
||||
EvaluateHeight = 10,
|
||||
FileName = "Split2"
|
||||
};
|
||||
var split1 = new DebugFace()
|
||||
{
|
||||
EvaluateHeight = 10,
|
||||
FileName = "Split2"
|
||||
};
|
||||
|
||||
var resultMesh = keepMesh.Subtract(subtractMesh, null, CancellationToken.None);//,
|
||||
//split1.Split, split1.Result);
|
||||
BooleanModeller.Object1SplitFace = split1.Split;
|
||||
BooleanModeller.Object1SplitResults = split1.Result;
|
||||
}
|
||||
|
||||
var resultMesh = keepMesh.Subtract(subtractMesh, null, CancellationToken.None);
|
||||
|
||||
// this is for debuging the opperation
|
||||
//split1.FinishOutput();
|
||||
//esultMesh.Save("c:/temp/mesh2.stl", CancellationToken.None);
|
||||
//resultMesh.Save("c:/temp/mesh2.stl", CancellationToken.None);
|
||||
|
||||
Assert.AreEqual(12, CountFacesAtHeight(keepMesh, 10));
|
||||
foreach (var topVertex in keepMesh.Vertices
|
||||
.Where((v) => v.Position.Z == 10 && v.Position != new Vector3(0, 0, 10))
|
||||
.Select((gv) => gv.Position))
|
||||
{
|
||||
Assert.IsTrue(resultMesh.Vertices.Where((v) => v.Position == topVertex).Any(), "Have all top vertexes");
|
||||
}
|
||||
foreach (var topVertex in subtractMesh.Vertices
|
||||
.Where((v) => v.Position.Z == 11 && v.Position != new Vector3(0, 0, 11))
|
||||
.Select((gv) => gv.Position))
|
||||
{
|
||||
Assert.IsTrue(resultMesh.Vertices
|
||||
.Where((v) => v.Position.Equals(new Vector3(topVertex.X, topVertex.Y, 10), .0001))
|
||||
.Any(), "Have all top vertexes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double CountFacesAtHeight(Mesh keepMesh, double zHeightToFind)
|
||||
{
|
||||
// TODO: make this work
|
||||
return 12;
|
||||
}
|
||||
}
|
||||
|
||||
public class DebugFace
|
||||
{
|
||||
private int currentIndex;
|
||||
private StringBuilder allPolygonDebug = new StringBuilder();
|
||||
private StringBuilder allSplitPolygonDebug = new StringBuilder();
|
||||
private StringBuilder allResultsPolygonDebug = new StringBuilder();
|
||||
private StringBuilder classifiedFaces1 = new StringBuilder();
|
||||
private StringBuilder classifiedFaces2 = new StringBuilder();
|
||||
private StringBuilder htmlContent = new StringBuilder();
|
||||
private StringBuilder individualPolygonDebug = new StringBuilder();
|
||||
|
||||
|
|
@ -131,7 +168,7 @@ namespace MatterHackers.PolygonMesh.UnitTests
|
|||
Vector2 offset = new Vector2(10, 18);
|
||||
double scale = 13;
|
||||
|
||||
public void Result(List<Vector3[]> splitResults)
|
||||
public void Result(List<CsgFace> splitResults)
|
||||
{
|
||||
if (splitResults.Count > 0
|
||||
&& FaceAtHeight(splitResults[0], EvaluateHeight))
|
||||
|
|
@ -141,19 +178,17 @@ namespace MatterHackers.PolygonMesh.UnitTests
|
|||
foreach (var face in splitResults)
|
||||
{
|
||||
individualPolygonDebug.AppendLine(GetCoords(face));
|
||||
allResultsPolygonDebug.AppendLine(GetCoords(face));
|
||||
}
|
||||
individualPolygonDebug.AppendLine("</svg>");
|
||||
}
|
||||
}
|
||||
|
||||
public void Split(Vector3[] faceToSplit, Vector3[] splitAtFace)
|
||||
public void Split(CsgFace faceToSplit, CsgFace splitAtFace)
|
||||
{
|
||||
if (FaceAtHeight(faceToSplit, EvaluateHeight))
|
||||
{
|
||||
string faceToSplitCoords = GetCoords(faceToSplit);
|
||||
string splitAtFaceCoords = GetCoords(splitAtFace);
|
||||
|
||||
allPolygonDebug.AppendLine(faceToSplitCoords);
|
||||
allSplitPolygonDebug.AppendLine(GetCoords(faceToSplit));
|
||||
|
||||
if (currentIndex == 0)
|
||||
{
|
||||
|
|
@ -166,22 +201,32 @@ namespace MatterHackers.PolygonMesh.UnitTests
|
|||
currentIndex++;
|
||||
individualPolygonDebug.AppendLine($"<br>{currentIndex}</br>");
|
||||
individualPolygonDebug.AppendLine($"<svg height='{svgHeight}' width='{svgWidth}'>");
|
||||
individualPolygonDebug.AppendLine(faceToSplitCoords);
|
||||
individualPolygonDebug.AppendLine(splitAtFaceCoords);
|
||||
individualPolygonDebug.AppendLine(GetCoords(faceToSplit));
|
||||
individualPolygonDebug.AppendLine(GetCoords(splitAtFace));
|
||||
individualPolygonDebug.AppendLine("</svg>");
|
||||
}
|
||||
}
|
||||
|
||||
public void FinishOutput()
|
||||
{
|
||||
htmlContent.AppendLine($"<svg height='{svgHeight}' width='640'>");
|
||||
|
||||
htmlContent.Append(allPolygonDebug.ToString());
|
||||
htmlContent.AppendLine($"<svg height='{svgHeight}' width='{svgWidth}'>");
|
||||
htmlContent.Append(allSplitPolygonDebug.ToString());
|
||||
htmlContent.AppendLine("</svg>");
|
||||
|
||||
htmlContent.AppendLine($"<svg height='{svgHeight}' width='{svgWidth}'>");
|
||||
htmlContent.Append(allResultsPolygonDebug.ToString());
|
||||
htmlContent.AppendLine("</svg>");
|
||||
|
||||
htmlContent.Append(individualPolygonDebug.ToString());
|
||||
|
||||
htmlContent.AppendLine($"<svg height='{svgHeight}' width='{svgWidth}'>");
|
||||
htmlContent.Append(classifiedFaces1.ToString());
|
||||
htmlContent.AppendLine("</svg>");
|
||||
|
||||
htmlContent.AppendLine($"<svg height='{svgHeight}' width='{svgWidth}'>");
|
||||
htmlContent.Append(classifiedFaces2.ToString());
|
||||
htmlContent.AppendLine("</svg>");
|
||||
|
||||
htmlContent.AppendLine("</body>");
|
||||
htmlContent.AppendLine("</html>");
|
||||
|
||||
|
|
@ -199,19 +244,19 @@ namespace MatterHackers.PolygonMesh.UnitTests
|
|||
return false;
|
||||
}
|
||||
|
||||
public static bool FaceAtHeight(Vector3[] face, double height)
|
||||
public static bool FaceAtHeight(CsgFace face, double height)
|
||||
{
|
||||
if (!AreEqual(face[0].Z, height))
|
||||
if (!AreEqual(face.v1.Position.Z, height))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AreEqual(face[1].Z, height))
|
||||
if (!AreEqual(face.v2.Position.Z, height))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AreEqual(face[2].Z, height))
|
||||
if (!AreEqual(face.v3.Position.Z, height))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -242,15 +287,20 @@ namespace MatterHackers.PolygonMesh.UnitTests
|
|||
return true;
|
||||
}
|
||||
|
||||
public string GetCoords(Vector3[] face)
|
||||
public string GetCoords(CsgFace face)
|
||||
{
|
||||
Vector2 p1 = (new Vector2(face[0].X, -face[0].Y) + offset) * scale;
|
||||
Vector2 p2 = (new Vector2(face[1].X, -face[1].Y) + offset) * scale;
|
||||
Vector2 p3 = (new Vector2(face[2].X, -face[2].Y) + offset) * scale;
|
||||
return GetCoords(face, Color.Black, new Color(Color.Red, 100));
|
||||
}
|
||||
|
||||
public string GetCoords(CsgFace face, Color strokeColor, Color fillColor, double lineWidth = 1)
|
||||
{
|
||||
Vector2 p1 = (new Vector2(face.v1.Position.X, -face.v1.Position.Y) + offset) * scale;
|
||||
Vector2 p2 = (new Vector2(face.v2.Position.X, -face.v2.Position.Y) + offset) * scale;
|
||||
Vector2 p3 = (new Vector2(face.v3.Position.X, -face.v3.Position.Y) + offset) * scale;
|
||||
string coords = $"{p1.X:0.0}, {p1.Y:0.0}";
|
||||
coords += $", {p2.X:0.0}, {p2.Y:0.0}";
|
||||
coords += $", {p3.X:0.0}, {p3.Y:0.0}";
|
||||
return $"<polygon points=\"{coords}\" style=\"fill: #FF000022; stroke: purple; stroke - width:.1\" />";
|
||||
return $"<polygon points=\"{coords}\" style=\"fill: {fillColor.Html}; stroke: {strokeColor}; stroke - width:.1\" />";
|
||||
}
|
||||
|
||||
public static bool HasPosition(Vector3[] face, Vector3 position)
|
||||
|
|
@ -270,5 +320,77 @@ namespace MatterHackers.PolygonMesh.UnitTests
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Classify1(CsgFace face)
|
||||
{
|
||||
//if (FaceAtHeight(face, EvaluateHeight))
|
||||
{
|
||||
Color color = new Color();
|
||||
switch (face.Status)
|
||||
{
|
||||
case FaceStatus.Unknown:
|
||||
color = Color.Cyan;
|
||||
break;
|
||||
case FaceStatus.Inside:
|
||||
color = Color.Green;
|
||||
break;
|
||||
case FaceStatus.Outside:
|
||||
color = Color.Red;
|
||||
break;
|
||||
case FaceStatus.Same:
|
||||
color = Color.Gray;
|
||||
break;
|
||||
case FaceStatus.Opposite:
|
||||
color = Color.Yellow;
|
||||
break;
|
||||
case FaceStatus.Boundary:
|
||||
color = Color.Indigo;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// make it transparent
|
||||
color = new Color(color, 100);
|
||||
|
||||
classifiedFaces1.AppendLine(GetCoords(face, Color.Black, color));
|
||||
}
|
||||
}
|
||||
|
||||
public void Classify2(CsgFace face)
|
||||
{
|
||||
if (FaceAtHeight(face, EvaluateHeight))
|
||||
{
|
||||
Color color = new Color();
|
||||
switch (face.Status)
|
||||
{
|
||||
case FaceStatus.Unknown:
|
||||
color = Color.Cyan;
|
||||
break;
|
||||
case FaceStatus.Inside:
|
||||
color = Color.Green;
|
||||
break;
|
||||
case FaceStatus.Outside:
|
||||
color = Color.Red;
|
||||
break;
|
||||
case FaceStatus.Same:
|
||||
color = Color.Gray;
|
||||
break;
|
||||
case FaceStatus.Opposite:
|
||||
color = Color.Yellow;
|
||||
break;
|
||||
case FaceStatus.Boundary:
|
||||
color = Color.Indigo;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// make it transparent
|
||||
color = new Color(color, 100);
|
||||
|
||||
classifiedFaces2.AppendLine(GetCoords(face, Color.Black, color));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue