diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 73d442b..a942e69 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -24,6 +24,8 @@ jobs: name: ${{ matrix.preset.os }} / ${{matrix.preset.name}} / ${{matrix.build-type}} runs-on: ${{ matrix.preset.os }} + env: + VCPKG_BINARY_SOURCES: clear steps: - name: checkout repository diff --git a/CMakePresets.json b/CMakePresets.json index 322d7c9..853ceb1 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -9,7 +9,9 @@ "CMAKE_TOOLCHAIN_FILE": { "type": "FILEPATH", "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" - } + }, + "TESSELLATOR_ENABLE_CGAL": true + } }, { diff --git a/src/app/launcher.cpp b/src/app/launcher.cpp index b8dd444..6f4cb2c 100644 --- a/src/app/launcher.cpp +++ b/src/app/launcher.cpp @@ -95,6 +95,20 @@ std::string readExtension(const std::string &fn) } } +meshlib::meshers::StaircaseMesherOptions readStaircaseMesherOptions(const std::string &fn) +{ + nlohmann::json j; + { + std::ifstream i(fn); + i >> j; + } + meshlib::meshers::StaircaseMesherOptions res; + if (j["object"].contains("volume")) { + res.isVolume = j["object"]["volume"]; + } + return res; +} + meshlib::meshers::ConformalMesherOptions readConformalMesherOptions(const std::string &fn) { nlohmann::json j; @@ -103,6 +117,11 @@ meshlib::meshers::ConformalMesherOptions readConformalMesherOptions(const std::s i >> j; } meshlib::meshers::ConformalMesherOptions res; + if (j["object"].contains("volume")) { + res.isVolume = j["object"]["volume"]; + } + + if (j["mesher"].contains("options")) { res.snapperOptions.edgePoints = j["mesher"]["options"]["edgePoints"]; res.snapperOptions.forbiddenLength = j["mesher"]["options"]["forbiddenLength"]; @@ -113,7 +132,7 @@ std::unique_ptr buildMesher(const Mesh &in, const { auto mesherType = readMesherType(fn); if (mesherType == meshlib::app::staircase_mesher) { - return std::make_unique(meshlib::meshers::StaircaseMesher{in}); + return std::make_unique(meshlib::meshers::StaircaseMesher{in, 4, readStaircaseMesherOptions(fn)}); } else if (mesherType == meshlib::app::conformal_mesher) { return std::make_unique(meshlib::meshers::ConformalMesher{in, readConformalMesherOptions(fn)}); } else { diff --git a/src/meshers/CMakeLists.txt b/src/meshers/CMakeLists.txt index dfdd88c..32eed77 100644 --- a/src/meshers/CMakeLists.txt +++ b/src/meshers/CMakeLists.txt @@ -6,7 +6,11 @@ add_library(tessellator-meshers "OffgridMesher.cpp" "ConformalMesher.cpp" ) -target_link_libraries(tessellator-meshers tessellator-core tessellator-utils) +target_link_libraries(tessellator-meshers + tessellator-core + tessellator-utils + tessellator-cgal + CGAL::CGAL) if(TESSELLATOR_EXECUTION_POLICIES) add_definitions(-DTESSELLATOR_EXECUTION_POLICIES) diff --git a/src/meshers/ConformalMesherOptions.h b/src/meshers/ConformalMesherOptions.h index 86f31d2..5a38c18 100644 --- a/src/meshers/ConformalMesherOptions.h +++ b/src/meshers/ConformalMesherOptions.h @@ -1,14 +1,15 @@ #pragma once #include "types/Mesh.h" +#include "MesherBaseOptions.h" #include "core/SnapperOptions.h" namespace meshlib::meshers { -class ConformalMesherOptions { +class ConformalMesherOptions : public MesherBaseOptions { public: core::SnapperOptions snapperOptions; - std::set volumeGroups{}; + // std::set volumeGroups{}; }; } diff --git a/src/meshers/MesherBase.h b/src/meshers/MesherBase.h index a688c29..938f949 100644 --- a/src/meshers/MesherBase.h +++ b/src/meshers/MesherBase.h @@ -1,6 +1,7 @@ #pragma once #include "types/Mesh.h" +#include "MesherBaseOptions.h" namespace meshlib { namespace meshers { @@ -29,6 +30,9 @@ class MesherBase { Grid originalGrid_; Grid enlargedGrid_; + MesherBaseOptions opts_; + + }; } diff --git a/src/meshers/MesherBaseOptions.h b/src/meshers/MesherBaseOptions.h new file mode 100644 index 0000000..7403619 --- /dev/null +++ b/src/meshers/MesherBaseOptions.h @@ -0,0 +1,14 @@ +#pragma once + +#include "types/Mesh.h" +#include "core/SnapperOptions.h" + +namespace meshlib::meshers { + +class MesherBaseOptions { +public: + bool isVolume = false; + std::set volumeGroups{}; +}; + +} diff --git a/src/meshers/StaircaseMesher.cpp b/src/meshers/StaircaseMesher.cpp index 7ad6458..cd9f753 100644 --- a/src/meshers/StaircaseMesher.cpp +++ b/src/meshers/StaircaseMesher.cpp @@ -7,6 +7,8 @@ #include "core/Collapser.h" #include "core/Staircaser.h" +#include "cgal/filler/Filler.h" + #include "utils/RedundancyCleaner.h" #include "utils/MeshTools.h" #include "utils/GridTools.h" @@ -17,9 +19,10 @@ using namespace utils; using namespace core; using namespace meshTools; -StaircaseMesher::StaircaseMesher(const Mesh& inputMesh, int decimalPlacesInCollapser) : +StaircaseMesher::StaircaseMesher(const Mesh& inputMesh, int decimalPlacesInCollapser, StaircaseMesherOptions opts) : MesherBase(inputMesh), - decimalPlacesInCollapser_(decimalPlacesInCollapser) + decimalPlacesInCollapser_(decimalPlacesInCollapser), + opts_(opts) { log("Preparing surfaces."); surfaceMesh_ = buildMeshFilteringElements(inputMesh, isNotTetrahedron); @@ -49,6 +52,16 @@ void StaircaseMesher::process(Mesh& mesh) const auto dimensions = getHighestDimensionByGroup(mesh); + if (opts_.isVolume){ + if (meshTools::isAClosedTopology(mesh.groups[0].elements)){ + meshlib::cgal::filler::Filler f{ mesh }; + auto filling = f.getMeshFilling(); + mergeMesh(mesh, filling); + } else { + throw std::runtime_error("Input object marked to be meshed as a volume, but surface is not closed"); + } + } + log("Slicing.", 1); mesh.grid = slicingGrid; mesh = Slicer{ mesh, dimensions }.getMesh(); diff --git a/src/meshers/StaircaseMesher.h b/src/meshers/StaircaseMesher.h index bbaffa1..df0b9bb 100644 --- a/src/meshers/StaircaseMesher.h +++ b/src/meshers/StaircaseMesher.h @@ -2,12 +2,13 @@ #include "types/Mesh.h" #include "MesherBase.h" +#include "StaircaseMesherOptions.h" namespace meshlib::meshers { class StaircaseMesher : public MesherBase { public: - StaircaseMesher(const Mesh& in, int decimalPlacesInCollapser = 4); + StaircaseMesher(const Mesh& in, int decimalPlacesInCollapser = 4, StaircaseMesherOptions opts = StaircaseMesherOptions()); virtual ~StaircaseMesher() = default; Mesh mesh() const; @@ -15,6 +16,7 @@ class StaircaseMesher : public MesherBase { int decimalPlacesInCollapser_; Mesh surfaceMesh_; + StaircaseMesherOptions opts_; virtual Mesh buildSurfaceMesh(const Mesh& inputMesh, const Mesh& volumeSurface); void process(Mesh&) const; diff --git a/src/meshers/StaircaseMesherOptions.h b/src/meshers/StaircaseMesherOptions.h new file mode 100644 index 0000000..dc06781 --- /dev/null +++ b/src/meshers/StaircaseMesherOptions.h @@ -0,0 +1,13 @@ +#pragma once + +#include "types/Mesh.h" +#include "MesherBaseOptions.h" +#include "core/SnapperOptions.h" + +namespace meshlib::meshers { + +class StaircaseMesherOptions : public MesherBaseOptions { +public: +}; + +} diff --git a/test/app/launcherTest.cpp b/test/app/launcherTest.cpp index 3942428..e5dd3f7 100644 --- a/test/app/launcherTest.cpp +++ b/test/app/launcherTest.cpp @@ -77,6 +77,15 @@ TEST_F(LauncherTest, launches_sphere_case) EXPECT_EQ(exitCode, EXIT_SUCCESS); } +TEST_F(LauncherTest, launches_closed_sphere_case) +{ + int ac = 3; + const char* av[] = { NULL, "-i", "testData/cases/sphere/closed_sphere.tessellator.json"}; + int exitCode; + EXPECT_NO_THROW(exitCode = meshlib::app::launcher(ac, av)); + EXPECT_EQ(exitCode, EXIT_SUCCESS); +} + TEST_F(LauncherTest, launches_conformal_sphere_case) { int ac = 3; diff --git a/test/meshers/StaircaseMesherTest.cpp b/test/meshers/StaircaseMesherTest.cpp index 53d7373..3b7419b 100644 --- a/test/meshers/StaircaseMesherTest.cpp +++ b/test/meshers/StaircaseMesherTest.cpp @@ -317,6 +317,31 @@ TEST_F(StaircaseMesherTest, DISABLED_testStaircaseTriangleWithUniformGrid) EXPECT_EQ(6, countMeshElementsIf(resultMesh, isNode)); } +TEST_F(StaircaseMesherTest, fills_closed_volume_with_quads) +{ + auto mesh = vtkIO::readInputMesh("testData/cases/sphere/sphere.stl"); + + mesh.grid[X] = utils::GridTools::linspace(-100.0, 100.0, 51); + mesh.grid[Y] = utils::GridTools::linspace(-100.0, 100.0, 51); + mesh.grid[Z] = utils::GridTools::linspace(-100.0, 100.0, 51); + + meshlib::meshers::StaircaseMesherOptions opts; + opts.isVolume = false; + auto staircasedMesh = StaircaseMesher{mesh, 4, opts }.mesh(); + + opts.isVolume = true; + auto staircasedMeshVolume = StaircaseMesher{mesh, 4, opts }.mesh(); + + EXPECT_EQ(0, countMeshElementsIf(staircasedMesh, isTriangle)); + EXPECT_EQ(0, countMeshElementsIf(staircasedMesh, isTetrahedron)); + + EXPECT_EQ(0, countMeshElementsIf(staircasedMeshVolume, isTriangle)); + EXPECT_EQ(0, countMeshElementsIf(staircasedMeshVolume, isTetrahedron)); + + EXPECT_TRUE(countMeshElementsIf(staircasedMeshVolume, isQuad) > countMeshElementsIf(staircasedMesh, isQuad)); + +} + TEST_F(StaircaseMesherTest, preserves_topological_closedness_for_alhambra) { auto mesh = vtkIO::readInputMesh("testData/cases/alhambra/alhambra.stl"); diff --git a/testData/cases/sphere/closed_sphere.tessellator.json b/testData/cases/sphere/closed_sphere.tessellator.json new file mode 100644 index 0000000..d95ebfe --- /dev/null +++ b/testData/cases/sphere/closed_sphere.tessellator.json @@ -0,0 +1,10 @@ +{ + "grid": { + "numberOfCells": [50, 50, 50], + "boundingBox": [ + [-100.0, -100.0, -100.0], + [ 100.0, 100.0, 100.0] + ] + }, + "object": {"filename": "sphere.stl", "volume" : true} +} \ No newline at end of file