diff --git a/Submodules/MatterSlice b/Submodules/MatterSlice index e0f012a1d..6cb7f7949 160000 --- a/Submodules/MatterSlice +++ b/Submodules/MatterSlice @@ -1 +1 @@ -Subproject commit e0f012a1d6ceb8f41927a7756d892ec382123893 +Subproject commit 6cb7f7949ccfc62096bf5a3cbedc39fe8fe56ece diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index d6c9f76c5..b478487a1 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit d6c9f76c5c5eeae49cc645c8977c3c7182ff39a6 +Subproject commit b478487a1456855b9c30412901334d2e177f3cab diff --git a/Tests/MatterControl.Tests/MatterControl.Tests.csproj b/Tests/MatterControl.Tests/MatterControl.Tests.csproj index 113db7954..eae17513c 100644 --- a/Tests/MatterControl.Tests/MatterControl.Tests.csproj +++ b/Tests/MatterControl.Tests/MatterControl.Tests.csproj @@ -58,6 +58,7 @@ + diff --git a/Tests/MatterControl.Tests/MatterControl/MeshCsgTests.cs b/Tests/MatterControl.Tests/MatterControl/MeshCsgTests.cs new file mode 100644 index 000000000..79fd37f0a --- /dev/null +++ b/Tests/MatterControl.Tests/MatterControl/MeshCsgTests.cs @@ -0,0 +1,240 @@ +/* +Copyright (c) 2014, Lars Brubaker +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. +*/ +#define DEBUG_INTO_TGAS + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using MatterHackers.Agg; +using MatterHackers.Agg.Platform; +using MatterHackers.DataConverters3D; +using MatterHackers.MatterControl.DesignTools; +using MatterHackers.MatterControl.DesignTools.Operations; +using MatterHackers.MatterControl.Tests.Automation; +using MatterHackers.PolygonMesh.Csg; +using MatterHackers.PolygonMesh.Processors; +using MatterHackers.VectorMath; +using NUnit.Framework; + +namespace MatterHackers.PolygonMesh.UnitTests +{ + [TestFixture, Category("Agg.PolygonMesh.Csg")] + public class MeshCsgTests + { + //[Test] + public void CylinderMinusCylinder() + { + AggContext.StaticData = new FileSystemStaticData(TestContext.CurrentContext.ResolveProjectPath(4, "StaticData")); + MatterControlUtilities.OverrideAppDataLocation(TestContext.CurrentContext.ResolveProjectPath(4)); + + AggContext.StaticData = new FileSystemStaticData(TestContext.CurrentContext.ResolveProjectPath(5, "MatterControl", "StaticData")); + MatterControlUtilities.OverrideAppDataLocation(TestContext.CurrentContext.ResolveProjectPath(5)); + + int sides = 3; + IObject3D keep = CylinderAdvancedObject3D.Create(20, 20, sides); + IObject3D subtract = CylinderAdvancedObject3D.Create(10, 21, sides); + IObject3D subtractCentered = new SetCenter(subtract, keep.GetCenter()); + + var keepMesh = keep.Mesh; + var subtractMesh = subtract.Mesh; + subtractMesh.Transform(subtract.WorldMatrix()); + + var split1 = new DebugFace() + { + EvaluateHeight = 10, + FileName = "Split1" + }; + + var resultMesh = keepMesh.Subtract(subtractMesh, null, CancellationToken.None, + split1.Split, split1.Result); + + split1.FinishOutput(); + + resultMesh.Save("c:/temp/mesh.stl", CancellationToken.None); + } + } + + public class DebugFace + { + private int currentIndex; + private StringBuilder allPolygonDebug = new StringBuilder(); + private StringBuilder htmlContent = new StringBuilder(); + private StringBuilder individualPolygonDebug = new StringBuilder(); + + public string FileName { get; set; } = "DebugFace"; + public double EvaluateHeight { get; set; } = -3; + int svgHeight = 540; + int svgWidth = 540; + + public void Result(List splitResults) + { + if (splitResults.Count > 0 + && FaceAtHeight(splitResults[0], EvaluateHeight)) + { + individualPolygonDebug.AppendLine($"
Result: {currentIndex}
"); + individualPolygonDebug.AppendLine($""); + foreach (var face in splitResults) + { + individualPolygonDebug.AppendLine(GetCoords(face)); + } + individualPolygonDebug.AppendLine(""); + } + } + + public void Split(Vector3[] faceToSplit, Vector3[] splitAtFace) + { + if (FaceAtHeight(faceToSplit, EvaluateHeight)) + { + string faceToSplitCoords = GetCoords(faceToSplit); + string splitAtFaceCoords = GetCoords(splitAtFace); + + allPolygonDebug.AppendLine(faceToSplitCoords); + + if (currentIndex == 0) + { + htmlContent.AppendLine(""); + htmlContent.AppendLine(""); + htmlContent.AppendLine(""); + htmlContent.AppendLine("
Full
"); + } + + currentIndex++; + individualPolygonDebug.AppendLine($"
{currentIndex}
"); + individualPolygonDebug.AppendLine($""); + individualPolygonDebug.AppendLine(faceToSplitCoords); + individualPolygonDebug.AppendLine(splitAtFaceCoords); + individualPolygonDebug.AppendLine(""); + } + } + + public void FinishOutput() + { + htmlContent.AppendLine($""); + + htmlContent.Append(allPolygonDebug.ToString()); + + htmlContent.AppendLine(""); + + htmlContent.Append(individualPolygonDebug.ToString()); + + htmlContent.AppendLine(""); + htmlContent.AppendLine(""); + + File.WriteAllText($"C:/Temp/{FileName}.html", htmlContent.ToString()); + } + + public static bool AreEqual(double a, double b, double errorRange = .001) + { + if (a < b + errorRange + && a > b - errorRange) + { + return true; + } + + return false; + } + + public static bool FaceAtHeight(Vector3[] face, double height) + { + if (!AreEqual(face[0].Z, height)) + { + return false; + } + + if (!AreEqual(face[1].Z, height)) + { + return false; + } + + if (!AreEqual(face[2].Z, height)) + { + return false; + } + + return true; + } + + public static bool FaceAtXy(Vector3[] face, double x, double y) + { + if (!AreEqual(face[0].X, x) + || !AreEqual(face[0].Y, y)) + { + return false; + } + + if (!AreEqual(face[1].X, x) + || !AreEqual(face[1].Y, y)) + { + return false; + } + + if (!AreEqual(face[2].X, x) + || !AreEqual(face[2].Y, y)) + { + return false; + } + + return true; + } + + public static string GetCoords(Vector3[] face) + { + var offset = new Vector2(10, 15); + var scale = 15; + 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; + 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 $""; + } + + public static bool HasPosition(Vector3[] face, Vector3 position) + { + if (face[0].Equals(position, .0001)) + { + return true; + } + if (face[1].Equals(position, .0001)) + { + return true; + } + if (face[2].Equals(position, .0001)) + { + return true; + } + + return false; + } + } +} \ No newline at end of file