diff --git a/src/Mod/Mesh/App/Exporter.h b/src/Mod/Mesh/App/Exporter.h index 005fc5ed5b..3085f7bba2 100644 --- a/src/Mod/Mesh/App/Exporter.h +++ b/src/Mod/Mesh/App/Exporter.h @@ -43,7 +43,7 @@ namespace Mesh * If objects are meant to be combined into a single file, then the file should * be saved from the derived class' destructor. */ -class Exporter +class MeshExport Exporter { public: Exporter(); @@ -76,7 +76,7 @@ protected: }; /// Creates a single mesh, in a file, from one or more objects -class MergeExporter: public Exporter +class MeshExport MergeExporter: public Exporter { public: MergeExporter(std::string fileName, MeshCore::MeshIO::Format fmt); @@ -202,7 +202,7 @@ private: * The constructor and destructor write the beginning and end of the 3MF, * addObject() is used to add geometry */ -class Exporter3MF: public Exporter +class MeshExport Exporter3MF: public Exporter { public: Exporter3MF(std::string fileName, const std::vector& = {}); @@ -235,7 +235,7 @@ private: * The constructor and destructor write the beginning and end of the AMF, * addObject() is used to add geometry */ -class ExporterAMF: public Exporter +class MeshExport ExporterAMF: public Exporter { public: /// Writes AMF header diff --git a/tests/src/Mod/Mesh/App/CMakeLists.txt b/tests/src/Mod/Mesh/App/CMakeLists.txt index 7b7d537599..4b177dad2d 100644 --- a/tests/src/Mod/Mesh/App/CMakeLists.txt +++ b/tests/src/Mod/Mesh/App/CMakeLists.txt @@ -2,5 +2,6 @@ target_sources( Mesh_tests_run PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Core/KDTree.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Exporter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Mesh.cpp ) diff --git a/tests/src/Mod/Mesh/App/Exporter.cpp b/tests/src/Mod/Mesh/App/Exporter.cpp new file mode 100644 index 0000000000..7f39340e41 --- /dev/null +++ b/tests/src/Mod/Mesh/App/Exporter.cpp @@ -0,0 +1,150 @@ +#include "gtest/gtest.h" +#include +#include +#include +#include +#include +#include +#include +#include + +class ExporterTest: public ::testing::Test +{ +protected: + static void SetUpTestSuite() + { + tests::initApplication(); + } + + void SetUp() override + { + document = App::GetApplication().newDocument("MeshExport"); + fileInfo.setFile(Base::FileInfo::getTempFileName() + ".stl"); + + const double value = 10.0; + cube1 = dynamic_cast(document->addObject("Mesh::Cube", "Cube1")); + cube1->Length.setValue(value); + cube1->Width.setValue(value); + cube1->Height.setValue(value); + cube2 = dynamic_cast(document->addObject("Mesh::Cube", "Cube2")); + cube2->Length.setValue(value); + cube2->Width.setValue(value); + cube2->Height.setValue(value); + + document->recompute(); + } + + void TearDown() override + { + App::GetApplication().closeDocument(document->getName()); + fileInfo.deleteFile(); + } + + App::Document* getDocument() const + { + return document; + } + + std::vector getObjects() const + { + return {cube1, cube2}; + } + + App::DocumentObject* getMesh2() const + { + return cube2; + } + + void setPlacementTo2ndCube(const Base::Placement& plm) + { + cube2->Placement.setValue(plm); + } + + std::string getFileName() const + { + return fileInfo.filePath(); + } + +private: + Base::FileInfo fileInfo; + App::Document* document {}; + Mesh::Cube* cube1 {}; + Mesh::Cube* cube2 {}; +}; + +// NOLINTBEGIN(cppcoreguidelines-*,readability-*) +TEST_F(ExporterTest, TestSingleMesh) +{ + Base::Placement plm; + plm.setPosition(Base::Vector3d(10, 5, 2)); + setPlacementTo2ndCube(plm); + + // add extra scope because the file will be written when destroying the exporter + { + Mesh::MergeExporter exporter(getFileName(), MeshCore::MeshIO::Format::STL); + exporter.addObject(getMesh2(), 0.1F); + } + + Mesh::MeshObject kernel; + EXPECT_TRUE(kernel.load(getFileName().c_str())); + auto bbox = kernel.getBoundBox(); + EXPECT_DOUBLE_EQ(bbox.MinX, 5.0); + EXPECT_DOUBLE_EQ(bbox.MaxX, 15.0); + EXPECT_DOUBLE_EQ(bbox.MinY, 0.0); + EXPECT_DOUBLE_EQ(bbox.MaxY, 10.0); + EXPECT_DOUBLE_EQ(bbox.MinZ, -3.0); + EXPECT_DOUBLE_EQ(bbox.MaxZ, 7.0); +} + +// Test for https://github.com/FreeCAD/FreeCAD/pull/11539 +TEST_F(ExporterTest, TestMultipleMeshes) +{ + Base::Placement plm; + plm.setPosition(Base::Vector3d(10, 5, 2)); + setPlacementTo2ndCube(plm); + + // add extra scope because the file will be written when destroying the exporter + { + Mesh::MergeExporter exporter(getFileName(), MeshCore::MeshIO::Format::STL); + for (auto obj : getObjects()) { + exporter.addObject(obj, 0.1F); + } + } + + Mesh::MeshObject kernel; + EXPECT_TRUE(kernel.load(getFileName().c_str())); + auto bbox = kernel.getBoundBox(); + EXPECT_DOUBLE_EQ(bbox.MinX, -5.0); + EXPECT_DOUBLE_EQ(bbox.MaxX, 15.0); + EXPECT_DOUBLE_EQ(bbox.MinY, -5.0); + EXPECT_DOUBLE_EQ(bbox.MaxY, 10.0); + EXPECT_DOUBLE_EQ(bbox.MinZ, -5.0); + EXPECT_DOUBLE_EQ(bbox.MaxZ, 7.0); +} + +TEST_F(ExporterTest, TestMeshesInPart) +{ + Base::Placement plm; + plm.setPosition(Base::Vector3d(10, 5, 2)); + setPlacementTo2ndCube(plm); + + // add extra scope because the file will be written when destroying the exporter + { + auto part = dynamic_cast(getDocument()->addObject("App::Part", "Part")); + part->placement().setValue(plm); + part->addObjects(getObjects()); + Mesh::MergeExporter exporter(getFileName(), MeshCore::MeshIO::Format::STL); + exporter.addObject(part, 0.1F); + } + + Mesh::MeshObject kernel; + EXPECT_TRUE(kernel.load(getFileName().c_str())); + auto bbox = kernel.getBoundBox(); + EXPECT_DOUBLE_EQ(bbox.MinX, 5.0); + EXPECT_DOUBLE_EQ(bbox.MaxX, 25.0); + EXPECT_DOUBLE_EQ(bbox.MinY, 0.0); + EXPECT_DOUBLE_EQ(bbox.MaxY, 15.0); + EXPECT_DOUBLE_EQ(bbox.MinZ, -3.0); + EXPECT_DOUBLE_EQ(bbox.MaxZ, 9.0); +} +// NOLINTEND(cppcoreguidelines-*,readability-*) diff --git a/tests/src/Mod/Mesh/App/Mesh.cpp b/tests/src/Mod/Mesh/App/Mesh.cpp index 9f578782af..045ffadd52 100644 --- a/tests/src/Mod/Mesh/App/Mesh.cpp +++ b/tests/src/Mod/Mesh/App/Mesh.cpp @@ -5,11 +5,7 @@ TEST(MeshTest, TestDefault) { MeshCore::MeshKernel kernel; - Base::Vector3f p1 { - 0, - 0, - 0, - }; + Base::Vector3f p1 {0, 0, 0}; Base::Vector3f p2 {0, 0, 1}; Base::Vector3f p3 {0, 1, 0}; kernel.AddFacet(MeshCore::MeshGeomFacet(p1, p2, p3));