From bbb8962f577d65a6da230bb4c26cb4ff84249468 Mon Sep 17 00:00:00 2001 From: carlosgonzalez-elemwave Date: Thu, 23 Oct 2025 10:16:42 +0200 Subject: [PATCH 1/5] Tessellator | Get dimension for every mesh group --- src/utils/MeshTools.cpp | 18 +++++++ src/utils/MeshTools.h | 1 + test/utils/MeshToolsTest.cpp | 97 ++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) diff --git a/src/utils/MeshTools.cpp b/src/utils/MeshTools.cpp index dff365a..b2a25f4 100644 --- a/src/utils/MeshTools.cpp +++ b/src/utils/MeshTools.cpp @@ -22,6 +22,24 @@ std::size_t countMeshElementsIf(const Mesh& mesh, std::function getHighestDimensionByGroup(const Mesh& mesh) { + std::vector highestDimensions; + + highestDimensions.reserve(mesh.groups.size()); + + for (auto & group : mesh.groups) { + auto highestValue = Element::Type::None; + for (auto& element : group.elements) { + if (highestValue < element.type) { + highestValue = element.type; + } + } + highestDimensions.push_back(highestValue); + } + + return highestDimensions; +} + Mesh duplicateCoordinatesUsedByDifferentGroups(const Mesh& mesh) { Mesh res = mesh; diff --git a/src/utils/MeshTools.h b/src/utils/MeshTools.h index 6faaabf..688c0cb 100644 --- a/src/utils/MeshTools.h +++ b/src/utils/MeshTools.h @@ -21,6 +21,7 @@ static bool isTetrahedron(const Element& e) { return e.isTetrahedron(); } static bool isNotTetrahedron(const Element& e) { return !e.isTetrahedron(); } std::size_t countMeshElementsIf(const Mesh& mesh, std::function countFilter); +std::vector getHighestDimensionByGroup(const Mesh& mesh); Mesh buildMeshFilteringElements( const Mesh& in, std::function filter); diff --git a/test/utils/MeshToolsTest.cpp b/test/utils/MeshToolsTest.cpp index 14cad5c..b46c30a 100644 --- a/test/utils/MeshToolsTest.cpp +++ b/test/utils/MeshToolsTest.cpp @@ -113,6 +113,103 @@ TEST_F(MeshToolsTest, countElements) ASSERT_EQ(38, countMeshElementsIf(inputMesh, isNotTetrahedron)); } + + +TEST_F(MeshToolsTest, canGetHighestDimensionByGroup) +{ + float lowerCoordinateValue = -0.5; + float upperCoordinateValue = 0.5; + int numberOfCells = 4; + float step = 0.25; + assert((upperCoordinateValue - lowerCoordinateValue) / (numberOfCells) == step); + + Mesh inputMesh; + inputMesh.grid = GridTools::buildCartesianGrid(lowerCoordinateValue, upperCoordinateValue, numberOfCells + 1); + inputMesh.coordinates = { + Coordinate({-0.20, -0.20, -0.20}), // 0 + Coordinate({-0.20, 0.20, -0.20}), // 1 + Coordinate({ 0.20, 0.20, -0.20}), // 2 + Coordinate({ 0.20, -0.20, -0.20}), // 3 + Coordinate({-0.20, -0.20, 0.20}), // 4 + Coordinate({-0.20, 0.20, 0.20}), // 5 + Coordinate({ 0.20, 0.20, 0.20}), // 6 + Coordinate({ 0.20, -0.20, 0.20}), // 7 + }; + + inputMesh.groups.resize(11); + inputMesh.groups[0].elements = { + Element({0}, Element::Type::Node), + }; + inputMesh.groups[1].elements = { + Element({1}, Element::Type::Node), + Element({0, 1}, Element::Type::Line), + }; + inputMesh.groups[2].elements = { + Element({2}, Element::Type::Node), + Element({1, 2}, Element::Type::Line), + Element({0, 1, 2}, Element::Type::Surface), + }; + inputMesh.groups[3].elements = { + Element({3}, Element::Type::Node), + Element({2, 3}, Element::Type::Line), + Element({1, 2, 3}, Element::Type::Surface), + Element({0, 1, 2, 3}, Element::Type::Surface), + }; + inputMesh.groups[4].elements = { + Element({4}, Element::Type::Node), + Element({0, 4}, Element::Type::Line), + Element({0, 1, 4}, Element::Type::Surface), + Element({0, 1, 3, 4}, Element::Type::Volume), + }; + inputMesh.groups[5].elements = { + Element({5}, Element::Type::Node), + Element({1, 5}, Element::Type::Line), + Element({1, 5, 4}, Element::Type::Surface), + Element({0, 1, 5, 4}, Element::Type::Surface), + Element({0, 1, 3, 4}, Element::Type::Volume), + }; + inputMesh.groups[6].elements = { + Element({1, 3, 4, 6}, Element::Type::Volume), + }; + inputMesh.groups[7].elements = { + Element({3, 6, 7, 4}, Element::Type::Volume), + Element({4, 5, 6, 7}, Element::Type::Surface), + Element({0, 4, 7}, Element::Type::Surface), + Element({7, 4}, Element::Type::Line), + Element({7}, Element::Type::Node), + }; + inputMesh.groups[8].elements = { + Element({3, 6, 7, 4}, Element::Type::Volume), + Element({4, 5, 6, 7}, Element::Type::Surface), + }; + inputMesh.groups[9].elements = { + Element({4, 5, 6, 7}, Element::Type::Surface), + Element({0, 4, 7}, Element::Type::Surface), + }; + // 43 + + std::vector expectedList({ + Element::Type::Node, + Element::Type::Line, + Element::Type::Surface, + Element::Type::Surface, + Element::Type::Volume, + Element::Type::Volume, + Element::Type::Volume, + Element::Type::Volume, + Element::Type::Volume, + Element::Type::Surface, + Element::Type::None, + }); + + auto highestDimensions = getHighestDimensionByGroup(inputMesh); + + ASSERT_EQ(expectedList.size(), highestDimensions.size()); + for (GroupId index = 0; index < expectedList.size(); ++index) { + ASSERT_EQ(expectedList[index], highestDimensions[index]) << "Current Group: #" << index << std::endl; + } +} + TEST_F(MeshToolsTest, checkNoCellsAreCrossed_tris_do_cross) { From 537a97ed5340c9e798944e73775faaad5fd5c5da Mon Sep 17 00:00:00 2001 From: carlosgonzalez-elemwave Date: Thu, 23 Oct 2025 12:41:26 +0200 Subject: [PATCH 2/5] Tessellator | Clean overlapped elements by dimension --- src/utils/RedundancyCleaner.cpp | 215 +++++++++++-------- src/utils/RedundancyCleaner.h | 1 + test/utils/RedundancyCleanerTest.cpp | 306 +++++++++++++++++++++++++++ 3 files changed, 431 insertions(+), 91 deletions(-) diff --git a/src/utils/RedundancyCleaner.cpp b/src/utils/RedundancyCleaner.cpp index 3f89e0e..e36161e 100644 --- a/src/utils/RedundancyCleaner.cpp +++ b/src/utils/RedundancyCleaner.cpp @@ -58,130 +58,163 @@ void RedundancyCleaner::removeRepeatedElements(Mesh& m) removeElements(m, toRemove); } +void getOverlappedDimensionZeroElementsAndIdenticalLines(const Group& group, std::set& overlappedElements); +void getOverlappedDimensionOneAndLowerElementsAndEquivalentSurfaces(const Group& group, const std::vector& meshCoordinates, std::set& overlappedElements); + void RedundancyCleaner::removeOverlappedDimensionZeroElementsAndIdenticalLines(Mesh & mesh) { std::vector> toRemove(mesh.groups.size()); for (std::size_t g = 0; g < mesh.groups.size(); ++g) { auto & group = mesh.groups[g]; - std::set usedCoordinates; - std::vector nodesToCheck; - - for (std::size_t e = 0; e < group.elements.size(); ++e){ - auto& element = group.elements[e]; - if(element.isLine()){ - usedCoordinates.insert(element.vertices[0]); - usedCoordinates.insert(element.vertices[1]); - } - else if (element.isNode()){ - nodesToCheck.push_back(e); - } - } - - for(auto e : nodesToCheck){ - auto & node = group.elements[e]; - if (usedCoordinates.count(node.vertices[0]) == 0){ - usedCoordinates.insert(node.vertices[0]); - } - else{ - toRemove[g].insert(e); - } - } + getOverlappedDimensionZeroElementsAndIdenticalLines(group, toRemove[g]); } removeElements(mesh, toRemove); } -void RedundancyCleaner::removeOverlappedDimensionOneAndLowerElementsAndEquivalentSurfaces(Mesh & mesh) +void RedundancyCleaner::removeOverlappedDimensionOneAndLowerElementsAndEquivalentSurfaces(Mesh& mesh) { std::vector> toRemove(mesh.groups.size()); for (std::size_t g = 0; g < mesh.groups.size(); ++g) { - auto & group = mesh.groups[g]; + auto& group = mesh.groups[g]; + getOverlappedDimensionOneAndLowerElementsAndEquivalentSurfaces(group, mesh.coordinates, toRemove[g]); + } - std::set usedCoordinatesFromSurface; - std::set usedCoordinatePairsFromSurface; - std::set usedCoordinates; - std::vector linesToCheck; - std::vector nodesToCheck; + removeElements(mesh, toRemove); +} - for (std::size_t e = 0; e < group.elements.size(); ++e){ - auto& element = group.elements[e]; - CoordinateIds vIds{ element.vertices }; - if(vIds.size() >= 2){ - std::rotate(vIds.begin(), std::min_element(vIds.begin(), vIds.end()), vIds.end()); - for (std::size_t v = 0; v < vIds.size(); ++v){ - usedCoordinates.insert(vIds[v]); - } +void getOverlappedDimensionZeroElementsAndIdenticalLines(const Group& group, std::set& overlappedElements) { + std::set usedCoordinates; + std::vector nodesToCheck; + + for (std::size_t e = 0; e < group.elements.size(); ++e) { + auto& element = group.elements[e]; + if (element.isLine()) { + usedCoordinates.insert(element.vertices[0]); + usedCoordinates.insert(element.vertices[1]); + } + else if (element.isNode()) { + nodesToCheck.push_back(e); + } + } + + for (auto e : nodesToCheck) { + auto& node = group.elements[e]; + if (usedCoordinates.count(node.vertices[0]) == 0) { + usedCoordinates.insert(node.vertices[0]); + } + else { + overlappedElements.insert(e); + } + } +} + +void getOverlappedDimensionOneAndLowerElementsAndEquivalentSurfaces(const Group& group, const std::vector & meshCoordinates, std::set& overlappedElements) +{ + std::set usedCoordinatesFromSurface; + std::set usedCoordinatePairsFromSurface; + std::set usedCoordinates; + std::vector linesToCheck; + std::vector nodesToCheck; + + for (std::size_t e = 0; e < group.elements.size(); ++e) { + auto& element = group.elements[e]; + CoordinateIds vIds{ element.vertices }; + if (vIds.size() >= 2) { + std::rotate(vIds.begin(), std::min_element(vIds.begin(), vIds.end()), vIds.end()); + for (std::size_t v = 0; v < vIds.size(); ++v) { + usedCoordinates.insert(vIds[v]); } - if(element.isQuad() || element.isTriangle()){ - if (usedCoordinatesFromSurface.count(vIds) == 0){ - usedCoordinatesFromSurface.insert(vIds); - for (std::size_t v = 0; v < vIds.size(); ++v){ - auto firstCoordinateId = vIds[v]; - auto secondCoordinateId = vIds[(v + 1) % vIds.size()]; - - if (secondCoordinateId < firstCoordinateId){ - std::swap(firstCoordinateId, secondCoordinateId); - } - usedCoordinatePairsFromSurface.insert({firstCoordinateId, secondCoordinateId}); + } + if (element.isQuad() || element.isTriangle()) { + if (usedCoordinatesFromSurface.count(vIds) == 0) { + usedCoordinatesFromSurface.insert(vIds); + for (std::size_t v = 0; v < vIds.size(); ++v) { + auto firstCoordinateId = vIds[v]; + auto secondCoordinateId = vIds[(v + 1) % vIds.size()]; + + if (secondCoordinateId < firstCoordinateId) { + std::swap(firstCoordinateId, secondCoordinateId); } + usedCoordinatePairsFromSurface.insert({ firstCoordinateId, secondCoordinateId }); } - else{ - toRemove[g].insert(e); - } - } - else if (element.isLine()){ - linesToCheck.push_back(e); } - else if (element.isNode()){ - nodesToCheck.push_back(e); + else { + overlappedElements.insert(e); } } - - std::map usedCoordinatePairsFromLine; + else if (element.isLine()) { + linesToCheck.push_back(e); + } + else if (element.isNode()) { + nodesToCheck.push_back(e); + } + } - for (auto e : linesToCheck){ - auto & line = group.elements[e]; - CoordinateIds vIds{ line.vertices }; - std::rotate(vIds.begin(), std::min_element(vIds.begin(), vIds.end()), vIds.end()); + std::map usedCoordinatePairsFromLine; - if (usedCoordinatePairsFromSurface.count(vIds)){ - toRemove[g].insert(e); - } - else if(usedCoordinatePairsFromLine.count(vIds) == 0){ - usedCoordinatePairsFromLine.emplace(vIds, e); - } - else{ - auto & originalLine = group.elements[usedCoordinatePairsFromLine[vIds]]; - RelativeDir direction = 0; - RelativeDir originalDirection = 0; - for (auto axis = X; axis <= Z; ++axis){ - direction += mesh.coordinates[line.vertices[1]][axis] - mesh.coordinates[line.vertices[0]][axis]; - originalDirection += mesh.coordinates[originalLine.vertices[1]][axis] - mesh.coordinates[originalLine.vertices[0]][axis]; - } + for (auto e : linesToCheck) { + auto& line = group.elements[e]; + CoordinateIds vIds{ line.vertices }; + std::rotate(vIds.begin(), std::min_element(vIds.begin(), vIds.end()), vIds.end()); - if (direction > originalDirection){ - toRemove[g].insert(usedCoordinatePairsFromLine[vIds]); - usedCoordinatePairsFromLine[vIds] = e; - } - else{ - toRemove[g].insert(e); - } - } + if (usedCoordinatePairsFromSurface.count(vIds)) { + overlappedElements.insert(e); + } + else if (usedCoordinatePairsFromLine.count(vIds) == 0) { + usedCoordinatePairsFromLine.emplace(vIds, e); } + else { + auto& originalLine = group.elements[usedCoordinatePairsFromLine[vIds]]; + RelativeDir direction = 0; + RelativeDir originalDirection = 0; + for (auto axis = X; axis <= Z; ++axis) { + direction += meshCoordinates[line.vertices[1]][axis] - meshCoordinates[line.vertices[0]][axis]; + originalDirection += meshCoordinates[originalLine.vertices[1]][axis] - meshCoordinates[originalLine.vertices[0]][axis]; + } - for(auto e : nodesToCheck){ - auto & node = group.elements[e]; - if (usedCoordinates.count(node.vertices[0]) == 0){ - usedCoordinates.insert(node.vertices[0]); + if (direction > originalDirection) { + overlappedElements.insert(usedCoordinatePairsFromLine[vIds]); + usedCoordinatePairsFromLine[vIds] = e; } - else{ - toRemove[g].insert(e); + else { + overlappedElements.insert(e); } } } + for (auto e : nodesToCheck) { + auto& node = group.elements[e]; + if (usedCoordinates.count(node.vertices[0]) == 0) { + usedCoordinates.insert(node.vertices[0]); + } + else { + overlappedElements.insert(e); + } + } +} + + +void RedundancyCleaner::removeOverlappedElementsByDimension(Mesh& mesh, const std::vector& highestDimensions) +{ + std::vector> toRemove(mesh.groups.size()); + + for (std::size_t g = 0; g < mesh.groups.size(); ++g) { + auto & group = mesh.groups[g]; + + switch (highestDimensions[g]) { + case Element::Type::Surface: + getOverlappedDimensionOneAndLowerElementsAndEquivalentSurfaces(group, mesh.coordinates, toRemove[g]); + break; + case Element::Type::Line: + getOverlappedDimensionZeroElementsAndIdenticalLines(group, toRemove[g]); + default: + break; + } + } + removeElements(mesh, toRemove); } diff --git a/src/utils/RedundancyCleaner.h b/src/utils/RedundancyCleaner.h index 4b52a41..9a91f40 100644 --- a/src/utils/RedundancyCleaner.h +++ b/src/utils/RedundancyCleaner.h @@ -18,6 +18,7 @@ class RedundancyCleaner { static void removeRepeatedElementsIgnoringOrientation(Mesh&); static void removeOverlappedDimensionZeroElementsAndIdenticalLines(Mesh&); static void removeOverlappedDimensionOneAndLowerElementsAndEquivalentSurfaces(Mesh&); + static void removeOverlappedElementsByDimension(Mesh&, const std::vector&); static void removeElements(Mesh&, const std::vector&); private: static Elements findDegenerateElements_(const Group&, const Coordinates&); diff --git a/test/utils/RedundancyCleanerTest.cpp b/test/utils/RedundancyCleanerTest.cpp index 7c2c87c..25e1dcb 100644 --- a/test/utils/RedundancyCleanerTest.cpp +++ b/test/utils/RedundancyCleanerTest.cpp @@ -338,6 +338,312 @@ TEST_F(RedundancyCleanerTest, removeOverlappedElementsWhenSurfaceMeshing) } } +TEST_F(RedundancyCleanerTest, removeOverlappedElementsbyDimension) { + Mesh mesh; + mesh.grid = buildUnitLengthGrid(0.2); + mesh.coordinates = { + Coordinate({0.0, 0.0, 0.0}), // 0 + Coordinate({0.0, 1.0, 0.0}), // 1 + Coordinate({1.0, 1.0, 0.0}), // 2 + Coordinate({1.0, 0.0, 0.0}), // 3 + Coordinate({0.0, 0.0, 1.0}), // 4 + Coordinate({0.0, 1.0, 1.0}), // 5 + Coordinate({1.0, 1.0, 1.0}), // 6 + Coordinate({1.0, 0.0, 1.0}), // 7 + }; + mesh.groups.resize(15); + mesh.groups[0].elements = { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2}, Element::Type::Node), + Element({3}, Element::Type::Node), + Element({4}, Element::Type::Node), + Element({5}, Element::Type::Node), + Element({5}, Element::Type::Node), + Element({2, 3}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + Element({7, 6}, Element::Type::Line), + Element({0, 1, 2, 3}, Element::Type::Surface), + }; + mesh.groups[1].elements = { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2}, Element::Type::Node), + Element({3}, Element::Type::Node), + Element({4}, Element::Type::Node), + Element({5}, Element::Type::Node), + Element({5}, Element::Type::Node), + Element({2, 3}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + Element({7, 6}, Element::Type::Line), + }; + mesh.groups[2].elements = { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2}, Element::Type::Node), + Element({3}, Element::Type::Node), + Element({4}, Element::Type::Node), + Element({5}, Element::Type::Node), + Element({5}, Element::Type::Node), + Element({2, 3}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + Element({7, 6}, Element::Type::Line), + }; + mesh.groups[3].elements = { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2, 3}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + }; + mesh.groups[4].elements = { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2, 3}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + }; + mesh.groups[5].elements = { + Element({2, 3}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + }; + mesh.groups[6].elements = { + Element({2, 3}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + }; + mesh.groups[7].elements = { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({0, 1}, Element::Type::Line), + }; + mesh.groups[8].elements = { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({0, 1}, Element::Type::Line), + }; + mesh.groups[9].elements = { + Element({0, 4}, Element::Type::Line), + Element({5, 1}, Element::Type::Line), + Element({0, 1, 2, 3}, Element::Type::Surface), + }; + mesh.groups[10].elements = { + Element({3, 0}, Element::Type::Line), + Element({2, 1}, Element::Type::Line), + Element({5, 6}, Element::Type::Line), + Element({4, 7}, Element::Type::Line), + Element({0, 1, 2, 3}, Element::Type::Surface), + Element({4, 5, 6, 7}, Element::Type::Surface), + Element({0, 4}, Element::Type::Line), + Element({5, 1}, Element::Type::Line), + }; + mesh.groups[11].elements = { + Element({0, 1, 2, 3}, Element::Type::Surface), + Element({0, 1, 5, 4}, Element::Type::Surface), + Element({1, 2, 6, 5}, Element::Type::Surface), + Element({2, 3, 7, 6}, Element::Type::Surface), + Element({3, 0, 4, 7}, Element::Type::Surface), + Element({4, 5, 6, 7}, Element::Type::Surface), + + Element({1, 2, 3, 0}, Element::Type::Surface), + Element({2, 3, 0, 1}, Element::Type::Surface), + Element({3, 0, 1, 2}, Element::Type::Surface), + Element({2, 1, 0, 3}, Element::Type::Surface), + }; + mesh.groups[12].elements = { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2}, Element::Type::Node), + Element({3}, Element::Type::Node), + Element({3}, Element::Type::Node), + }; + mesh.groups[13].elements = { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2}, Element::Type::Node), + Element({3}, Element::Type::Node), + Element({3}, Element::Type::Node), + }; + mesh.groups[14].elements = { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2}, Element::Type::Node), + Element({3}, Element::Type::Node), + Element({3}, Element::Type::Node), + }; + + std::vector dimensions = { + Element::Type::Surface, + Element::Type::Line, + Element::Type::Surface, + Element::Type::Line, + Element::Type::Surface, + Element::Type::Line, + Element::Type::Surface, + Element::Type::Line, + Element::Type::Surface, + Element::Type::Surface, + Element::Type::Surface, + Element::Type::Surface, + Element::Type::Node, + Element::Type::Line, + Element::Type::Surface + }; + + Coordinates expectedCoordinates = { + Coordinate({0.0, 0.0, 0.0}), // 0 + Coordinate({0.0, 1.0, 0.0}), // 1 + Coordinate({1.0, 1.0, 0.0}), // 2 + Coordinate({1.0, 0.0, 0.0}), // 3 + Coordinate({0.0, 0.0, 1.0}), // 4 + Coordinate({0.0, 1.0, 1.0}), // 5 + Coordinate({1.0, 1.0, 1.0}), // 6 + Coordinate({1.0, 0.0, 1.0}), // 7 + }; + + std::vector expectedElementsList = { + { + Element({4}, Element::Type::Node), + Element({5}, Element::Type::Node), + Element({7, 6}, Element::Type::Line), + Element({0, 1, 2, 3}, Element::Type::Surface), + }, + { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({4}, Element::Type::Node), + Element({5}, Element::Type::Node), + Element({2, 3}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + Element({7, 6}, Element::Type::Line), + }, + { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({4}, Element::Type::Node), + Element({5}, Element::Type::Node), + Element({3, 2}, Element::Type::Line), + Element({7, 6}, Element::Type::Line), + }, + { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2, 3}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + }, + { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2, 3}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + }, + { + Element({2, 3}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + }, + { + Element({3, 2}, Element::Type::Line), + Element({6, 7}, Element::Type::Line), + }, + { + Element({0, 1}, Element::Type::Line), + }, + { + Element({0, 1}, Element::Type::Line), + }, + { + Element({0, 4}, Element::Type::Line), + Element({5, 1}, Element::Type::Line), + Element({0, 1, 2, 3}, Element::Type::Surface), + }, + { + Element({0, 1, 2, 3}, Element::Type::Surface), + Element({4, 5, 6, 7}, Element::Type::Surface), + Element({0, 4}, Element::Type::Line), + Element({5, 1}, Element::Type::Line), + }, + { + Element({0, 1, 2, 3}, Element::Type::Surface), + Element({0, 1, 5, 4}, Element::Type::Surface), + Element({1, 2, 6, 5}, Element::Type::Surface), + Element({2, 3, 7, 6}, Element::Type::Surface), + Element({3, 0, 4, 7}, Element::Type::Surface), + Element({4, 5, 6, 7}, Element::Type::Surface), + + Element({2, 1, 0, 3}, Element::Type::Surface), + }, + { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2}, Element::Type::Node), + Element({3}, Element::Type::Node), + Element({3}, Element::Type::Node), + }, + { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2}, Element::Type::Node), + Element({3}, Element::Type::Node), + }, + { + Element({0}, Element::Type::Node), + Element({1}, Element::Type::Node), + Element({2}, Element::Type::Node), + Element({3}, Element::Type::Node), + }, + }; + + RedundancyCleaner::removeOverlappedElementsByDimension(mesh, dimensions); + + EXPECT_EQ(mesh.coordinates.size(), expectedCoordinates.size()); + ASSERT_EQ(mesh.groups.size(), expectedElementsList.size()); + + for (std::size_t index = 0; index < mesh.coordinates.size(); ++index) { + const auto& resultCoordinate = mesh.coordinates[index]; + const auto& expectedCoordinate = expectedCoordinates[index]; + + for (Axis axis = X; axis <= Z; ++axis) { + EXPECT_FLOAT_EQ(resultCoordinate[axis], expectedCoordinate[axis]) << "Current Coordinate: #" << index << std::endl; + } + } + + for (std::size_t g = 0; g < mesh.groups.size(); ++g) { + const auto& resultElements = mesh.groups[g].elements; + const auto& expectedElements = expectedElementsList[g]; + + ASSERT_EQ(resultElements.size(), expectedElements.size()) + << "Current Group: #" << g << std::endl; + + for (std::size_t e = 0; e < resultElements.size(); ++e) { + const auto& resultElement = resultElements[e]; + const auto& expectedElement = expectedElements[e]; + + EXPECT_EQ(resultElement.type, expectedElement.type); + ASSERT_EQ(resultElement.vertices.size(), expectedElement.vertices.size()) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl; + + for (std::size_t v = 0; v < resultElement.vertices.size(); ++v) { + EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl + << "Current Vertex: #" << v << std::endl; + } + } + } +} + + TEST_F(RedundancyCleanerTest, doNotRemoveOppositeLines) { Mesh m; From e52fd070973f44b4fba9457d60a1c49d0d46e37e Mon Sep 17 00:00:00 2001 From: carlosgonzalez-elemwave Date: Thu, 23 Oct 2025 13:24:36 +0200 Subject: [PATCH 3/5] Tessellator | Rename staircase mesher --- src/app/launcher.cpp | 4 +- src/meshers/CMakeLists.txt | 2 +- ...ructuredMesher.cpp => StaircaseMesher.cpp} | 10 ++--- .../{StructuredMesher.h => StaircaseMesher.h} | 6 +-- test/CMakeLists.txt | 2 +- ...MesherTest.cpp => StaircaseMesherTest.cpp} | 44 +++++++++---------- 6 files changed, 34 insertions(+), 34 deletions(-) rename src/meshers/{StructuredMesher.cpp => StaircaseMesher.cpp} (87%) rename src/meshers/{StructuredMesher.h => StaircaseMesher.h} (66%) rename test/meshers/{StructuredMesherTest.cpp => StaircaseMesherTest.cpp} (90%) diff --git a/src/app/launcher.cpp b/src/app/launcher.cpp index 42e2805..76730a1 100644 --- a/src/app/launcher.cpp +++ b/src/app/launcher.cpp @@ -1,7 +1,7 @@ #include "launcher.h" #include "vtkIO.h" -#include "meshers/StructuredMesher.h" +#include "meshers/StaircaseMesher.h" #include "utils/GridTools.h" #include @@ -85,7 +85,7 @@ int launcher(int argc, const char* argv[]) Mesh mesh = readMesh(inputFilename); // Mesh - meshlib::meshers::StructuredMesher mesher{mesh}; + meshlib::meshers::StaircaseMesher mesher{mesh}; Mesh resultMesh = mesher.mesh(); std::filesystem::path outputFolder = getFolder(inputFilename); diff --git a/src/meshers/CMakeLists.txt b/src/meshers/CMakeLists.txt index 5ca42ac..dfdd88c 100644 --- a/src/meshers/CMakeLists.txt +++ b/src/meshers/CMakeLists.txt @@ -2,7 +2,7 @@ message(STATUS "Creating build system for tessellator-meshers") add_library(tessellator-meshers "MesherBase.cpp" - "StructuredMesher.cpp" + "StaircaseMesher.cpp" "OffgridMesher.cpp" "ConformalMesher.cpp" ) diff --git a/src/meshers/StructuredMesher.cpp b/src/meshers/StaircaseMesher.cpp similarity index 87% rename from src/meshers/StructuredMesher.cpp rename to src/meshers/StaircaseMesher.cpp index 3d3d6ae..fe6ee61 100644 --- a/src/meshers/StructuredMesher.cpp +++ b/src/meshers/StaircaseMesher.cpp @@ -1,4 +1,4 @@ -#include "StructuredMesher.h" +#include "StaircaseMesher.h" #include @@ -17,7 +17,7 @@ using namespace utils; using namespace core; using namespace meshTools; -StructuredMesher::StructuredMesher(const Mesh& inputMesh, int decimalPlacesInCollapser) : +StaircaseMesher::StaircaseMesher(const Mesh& inputMesh, int decimalPlacesInCollapser) : MesherBase(inputMesh), decimalPlacesInCollapser_(decimalPlacesInCollapser) { @@ -30,14 +30,14 @@ StructuredMesher::StructuredMesher(const Mesh& inputMesh, int decimalPlacesInCol log("Surface mesh built succesfully.", 1); } -Mesh StructuredMesher::buildSurfaceMesh(const Mesh& inputMesh, const Mesh & volumeSurface) +Mesh StaircaseMesher::buildSurfaceMesh(const Mesh& inputMesh, const Mesh & volumeSurface) { auto resultMesh = buildMeshFilteringElements(inputMesh, isNotTetrahedron); mergeMesh(resultMesh, volumeSurface); return resultMesh; } -void StructuredMesher::process(Mesh& mesh) const +void StaircaseMesher::process(Mesh& mesh) const { const auto slicingGrid{ buildSlicingGrid(originalGrid_, enlargedGrid_) }; @@ -82,7 +82,7 @@ void StructuredMesher::process(Mesh& mesh) const } -Mesh StructuredMesher::mesh() const +Mesh StaircaseMesher::mesh() const { return surfaceMesh_; } diff --git a/src/meshers/StructuredMesher.h b/src/meshers/StaircaseMesher.h similarity index 66% rename from src/meshers/StructuredMesher.h rename to src/meshers/StaircaseMesher.h index 79f82c0..bbaffa1 100644 --- a/src/meshers/StructuredMesher.h +++ b/src/meshers/StaircaseMesher.h @@ -5,10 +5,10 @@ namespace meshlib::meshers { -class StructuredMesher : public MesherBase { +class StaircaseMesher : public MesherBase { public: - StructuredMesher(const Mesh& in, int decimalPlacesInCollapser = 4); - virtual ~StructuredMesher() = default; + StaircaseMesher(const Mesh& in, int decimalPlacesInCollapser = 4); + virtual ~StaircaseMesher() = default; Mesh mesh() const; private: diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1602ca4..cea5caa 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -31,7 +31,7 @@ add_executable(tessellator_tests "utils/GridToolsTest.cpp" "utils/MeshToolsTest.cpp" "utils/RedundancyCleanerTest.cpp" - "meshers/StructuredMesherTest.cpp" + "meshers/StaircaseMesherTest.cpp" "meshers/OffgridMesherTest.cpp" "meshers/ConformalMesherTest.cpp" ) diff --git a/test/meshers/StructuredMesherTest.cpp b/test/meshers/StaircaseMesherTest.cpp similarity index 90% rename from test/meshers/StructuredMesherTest.cpp rename to test/meshers/StaircaseMesherTest.cpp index 880629b..7a4c1df 100644 --- a/test/meshers/StructuredMesherTest.cpp +++ b/test/meshers/StaircaseMesherTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "MeshFixtures.h" -#include "meshers/StructuredMesher.h" +#include "meshers/StaircaseMesher.h" #include "Staircaser.h" #include "core/Slicer.h" @@ -21,7 +21,7 @@ using namespace utils; using namespace meshTools; -class StructuredMesherTest : public ::testing::Test { +class StaircaseMesherTest : public ::testing::Test { public: static std::size_t countRepeatedElements(const Mesh& mesh) { @@ -82,7 +82,7 @@ class StructuredMesherTest : public ::testing::Test { } }; -TEST_F(StructuredMesherTest, testStructuredLinesWithUniformGrid) +TEST_F(StaircaseMesherTest, testStaircaseLinesWithUniformGrid) { const int numberOfCells = 4; @@ -136,7 +136,7 @@ TEST_F(StructuredMesherTest, testStructuredLinesWithUniformGrid) }; Mesh resultMesh; - ASSERT_NO_THROW(resultMesh = StructuredMesher(inputMesh, 2).mesh()); + ASSERT_NO_THROW(resultMesh = StaircaseMesher(inputMesh, 2).mesh()); EXPECT_EQ(0, countRepeatedElements(resultMesh)); @@ -144,7 +144,7 @@ TEST_F(StructuredMesherTest, testStructuredLinesWithUniformGrid) } -TEST_F(StructuredMesherTest, testStructuredLinesWithRectilinearGrid) +TEST_F(StaircaseMesherTest, testStaircaseLinesWithRectilinearGrid) { Mesh inputMesh; inputMesh.grid = Grid( @@ -190,17 +190,17 @@ TEST_F(StructuredMesherTest, testStructuredLinesWithRectilinearGrid) }; Mesh resultMesh; - ASSERT_NO_THROW(resultMesh = StructuredMesher(inputMesh, 2).mesh()); + ASSERT_NO_THROW(resultMesh = StaircaseMesher(inputMesh, 2).mesh()); EXPECT_EQ(0, countRepeatedElements(resultMesh)); assertMeshEqual(resultMesh, expectedMesh); } -TEST_F(StructuredMesherTest, testTriNonUniformGridStructured) +TEST_F(StaircaseMesherTest, testTriNonUniformGridStaircase) { Mesh out; - ASSERT_NO_THROW(out = StructuredMesher(buildTriNonUniformGridMesh(), 4).mesh()); + ASSERT_NO_THROW(out = StaircaseMesher(buildTriNonUniformGridMesh(), 4).mesh()); EXPECT_EQ(0, countRepeatedElements(out)); EXPECT_EQ(6, out.groups[0].elements.size()); @@ -211,7 +211,7 @@ TEST_F(StructuredMesherTest, testTriNonUniformGridStructured) // FOR DEBUG ONLY / OBTAIN VISUAL REPRESENTATION -TEST_F(StructuredMesherTest, DISABLED_visualSelectiveStructurerCone) +TEST_F(StaircaseMesherTest, DISABLED_visualSelectiveStructurerCone) { // Input const std::string inputFilename = "testData/cases/cone/cone.stl"; @@ -267,7 +267,7 @@ TEST_F(StructuredMesherTest, DISABLED_visualSelectiveStructurerCone) -TEST_F(StructuredMesherTest, DISABLED_testStructuredTriangleWithUniformGrid) +TEST_F(StaircaseMesherTest, DISABLED_testStaircaseTriangleWithUniformGrid) { float lowerCoordinateValue = -0.5; @@ -289,7 +289,7 @@ TEST_F(StructuredMesherTest, DISABLED_testStructuredTriangleWithUniformGrid) }; Mesh resultMesh; - ASSERT_NO_THROW(resultMesh = StructuredMesher(inputMesh, 2).mesh()); + ASSERT_NO_THROW(resultMesh = StaircaseMesher(inputMesh, 2).mesh()); EXPECT_EQ(0, countRepeatedElements(resultMesh)); EXPECT_EQ(48, resultMesh.groups[0].elements.size()); @@ -298,40 +298,40 @@ TEST_F(StructuredMesherTest, DISABLED_testStructuredTriangleWithUniformGrid) EXPECT_EQ(6, countMeshElementsIf(resultMesh, isNode)); } -TEST_F(StructuredMesherTest, preserves_topological_closedness_for_alhambra) +TEST_F(StaircaseMesherTest, preserves_topological_closedness_for_alhambra) { auto mesh = vtkIO::readInputMesh("testData/cases/alhambra/alhambra.stl"); mesh.grid[X] = utils::GridTools::linspace(-60.0, 60.0, 61); mesh.grid[Y] = utils::GridTools::linspace(-60.0, 60.0, 61); mesh.grid[Z] = utils::GridTools::linspace(-1.872734, 11.236404, 8); - auto structuredMesh = StructuredMesher{mesh}.mesh(); + auto staircasedMesh = StaircaseMesher{mesh}.mesh(); EXPECT_TRUE(meshTools::isAClosedTopology(mesh.groups[0].elements)); - EXPECT_TRUE(meshTools::isAClosedTopology(structuredMesh.groups[0].elements)); + EXPECT_TRUE(meshTools::isAClosedTopology(staircasedMesh.groups[0].elements)); } -TEST_F(StructuredMesherTest, preserves_topological_closedness_for_sphere) +TEST_F(StaircaseMesherTest, preserves_topological_closedness_for_sphere) { auto mesh = vtkIO::readInputMesh("testData/cases/sphere/sphere.stl"); for (auto x: {X,Y,Z}) { mesh.grid[x] = utils::GridTools::linspace(-50.0, 50.0, 26); } - auto structuredMesh = StructuredMesher{mesh}.mesh(); + auto staircasedMesh = StaircaseMesher{mesh}.mesh(); EXPECT_TRUE(meshTools::isAClosedTopology(mesh.groups[0].elements)); - EXPECT_TRUE(meshTools::isAClosedTopology(structuredMesh.groups[0].elements)); + EXPECT_TRUE(meshTools::isAClosedTopology(staircasedMesh.groups[0].elements)); // //For debugging. - // meshTools::convertToAbsoluteCoordinates(structuredMesh); - // vtkIO::exportMeshToVTU("testData/cases/sphere/sphere.sliced.vtk", structuredMesh); + // meshTools::convertToAbsoluteCoordinates(staircasedMesh); + // vtkIO::exportMeshToVTU("testData/cases/sphere/sphere.sliced.vtk", staircasedMesh); - // auto contourMesh = meshTools::buildMeshFromContours(structuredMesh); + // auto contourMesh = meshTools::buildMeshFromContours(staircasedMesh); // vtkIO::exportMeshToVTU("testData/cases/sphere/sphere.contour.vtk", contourMesh); } -TEST_F(StructuredMesherTest, selectiveStructurer_preserves_topological_closedness_for_sphere) +TEST_F(StaircaseMesherTest, selectiveStructurer_preserves_topological_closedness_for_sphere) { const std::string inputFilename = "testData/cases/sphere/sphere.stl"; auto mesh = vtkIO::readInputMesh("testData/cases/sphere/sphere.stl"); @@ -381,7 +381,7 @@ TEST_F(StructuredMesherTest, selectiveStructurer_preserves_topological_closednes // meshlib::vtkIO::exportGridToVTU(outputFolder / (basename + ".tessellator.selective.grid.vtk"), resultMesh.grid); } -TEST_F(StructuredMesherTest, selectiveStructurer_preserves_topological_closedness_for_alhambra) +TEST_F(StaircaseMesherTest, selectiveStructurer_preserves_topological_closedness_for_alhambra) { const std::string inputFilename = "testData/cases/alhambra/alhambra.stl"; auto mesh = vtkIO::readInputMesh("testData/cases/alhambra/alhambra.stl"); From 57a96f250c8a55d796b1700bd9d2ee70bf9a6344 Mon Sep 17 00:00:00 2001 From: carlosgonzalez-elemwave Date: Fri, 24 Oct 2025 13:51:45 +0200 Subject: [PATCH 4/5] Tessellator | Mesh adjusted for highest dimension --- src/core/Collapser.cpp | 13 +- src/core/Collapser.h | 4 +- src/core/Slicer.cpp | 4 +- src/core/Slicer.h | 3 +- src/meshers/StaircaseMesher.cpp | 8 +- test/core/CollapserTest.cpp | 172 +++++++++++++++-- test/core/SlicerTest.cpp | 266 +++++++++++++++++++++++++-- test/core/StaircaserTest.cpp | 2 +- test/meshers/StaircaseMesherTest.cpp | 53 ++++-- 9 files changed, 472 insertions(+), 53 deletions(-) diff --git a/src/core/Collapser.cpp b/src/core/Collapser.cpp index 45e7d5b..6a2e8c4 100644 --- a/src/core/Collapser.cpp +++ b/src/core/Collapser.cpp @@ -11,8 +11,15 @@ namespace core { using namespace utils; -Collapser::Collapser(const Mesh& in, int decimalPlaces) -{ +Collapser::Collapser(const Mesh& in, int decimalPlaces, const std::vector& dimensionPolicy) +{ + if (dimensionPolicy.size() == 0) { + dimensionPolicy_ = std::vector(in.groups.size(), Element::Type::Surface); + } + else { + dimensionPolicy_ = dimensionPolicy; + } + mesh_ = in; double factor = std::pow(10.0, decimalPlaces); for (auto& v : mesh_.coordinates) { @@ -23,7 +30,7 @@ Collapser::Collapser(const Mesh& in, int decimalPlaces) RedundancyCleaner::cleanCoords(mesh_); collapseDegenerateElements(mesh_, 0.4 / (factor * factor)); - RedundancyCleaner::removeOverlappedDimensionOneAndLowerElementsAndEquivalentSurfaces(mesh_); + RedundancyCleaner::removeOverlappedElementsByDimension(mesh_, dimensionPolicy_); utils::meshTools::checkNoNullAreasExist(mesh_); } diff --git a/src/core/Collapser.h b/src/core/Collapser.h index a595fb2..39da1f2 100644 --- a/src/core/Collapser.h +++ b/src/core/Collapser.h @@ -7,12 +7,14 @@ namespace core { class Collapser { public: - Collapser(const Mesh&, int decimalPlaces); + Collapser(const Mesh&, int decimalPlaces, const std::vector& dimensionPolicy = {}); Mesh getMesh() const { return mesh_; } private: Mesh mesh_; + std::vector dimensionPolicy_; + void collapseDegenerateElements(Mesh& m, const double& areaThreshold); }; diff --git a/src/core/Slicer.cpp b/src/core/Slicer.cpp index 740181e..fb5d7a2 100644 --- a/src/core/Slicer.cpp +++ b/src/core/Slicer.cpp @@ -39,14 +39,14 @@ void orient(const Coordinates& coords, } -Slicer::Slicer(const Mesh& input, const SlicerOptions& opts) : +Slicer::Slicer(const Mesh& input, const std::vector& dimensionPolicy, const SlicerOptions& opts) : GridTools(input.grid), opts_(opts) { // Ensures that all coordinates have a fixed number of decimal places. Mesh collapsed = input; collapsed.coordinates = absoluteToRelative(collapsed.coordinates); - collapsed = Collapser{ collapsed, opts_.initialCollapsingDecimalPlaces }.getMesh(); + collapsed = Collapser{ collapsed, opts_.initialCollapsingDecimalPlaces, dimensionPolicy }.getMesh(); collapsed.coordinates = relativeToAbsolute(collapsed.coordinates); // Slices. diff --git a/src/core/Slicer.h b/src/core/Slicer.h index ed15cdd..e0bae73 100644 --- a/src/core/Slicer.h +++ b/src/core/Slicer.h @@ -22,7 +22,7 @@ class Slicer : public utils::GridTools { typedef std::vector PlaneAlignedPolylines; typedef std::vector PolylineV; - Slicer(const Mesh&, const SlicerOptions& opts = SlicerOptions()); + Slicer(const Mesh&, const std::vector& dimensionPolicy = {}, const SlicerOptions& opts = SlicerOptions()); Mesh getMesh() const { return mesh_; }; @@ -31,6 +31,7 @@ class Slicer : public utils::GridTools { private: std::mutex writingCoordinates_; std::mutex writingElements_; + Mesh mesh_; SlicerOptions opts_; diff --git a/src/meshers/StaircaseMesher.cpp b/src/meshers/StaircaseMesher.cpp index fe6ee61..7ad6458 100644 --- a/src/meshers/StaircaseMesher.cpp +++ b/src/meshers/StaircaseMesher.cpp @@ -47,14 +47,16 @@ void StaircaseMesher::process(Mesh& mesh) const return; } + auto dimensions = getHighestDimensionByGroup(mesh); + log("Slicing.", 1); mesh.grid = slicingGrid; - mesh = Slicer{ mesh }.getMesh(); + mesh = Slicer{ mesh, dimensions }.getMesh(); logNumberOfTriangles(countMeshElementsIf(mesh, isTriangle)); log("Collapsing.", 1); - mesh = Collapser(mesh, decimalPlacesInCollapser_).getMesh(); + mesh = Collapser(mesh, decimalPlacesInCollapser_, dimensions).getMesh(); logNumberOfTriangles(countMeshElementsIf(mesh, isTriangle)); @@ -65,7 +67,7 @@ void StaircaseMesher::process(Mesh& mesh) const logNumberOfLines(countMeshElementsIf(mesh, isLine)); log("Removing repeated and overlapping elements.", 1); - RedundancyCleaner::removeOverlappedDimensionOneAndLowerElementsAndEquivalentSurfaces(mesh); + RedundancyCleaner::removeOverlappedElementsByDimension(mesh, dimensions); logNumberOfQuads(countMeshElementsIf(mesh, isQuad)); logNumberOfLines(countMeshElementsIf(mesh, isLine)); diff --git a/test/core/CollapserTest.cpp b/test/core/CollapserTest.cpp index e807ee7..126f887 100644 --- a/test/core/CollapserTest.cpp +++ b/test/core/CollapserTest.cpp @@ -126,7 +126,7 @@ TEST_F(CollapserTest, collapser_3) EXPECT_EQ(m.groups, r.groups); for (std::size_t i = 0; i < m.coordinates.size(); i++) { for (std::size_t d = 0; d < 3; d++) { - EXPECT_NEAR(m.coordinates[i](d), r.coordinates[i](d), 1e-8); + EXPECT_NEAR(m.coordinates[i](d), r.coordinates[i](d), 1e-8) << "Current Coordinate: #" << i << std::endl; } } } @@ -253,7 +253,9 @@ TEST_F(CollapserTest, round_lines_to_tolerance) auto& resultCoordinate = resultMesh.coordinates[c]; for (Axis axis = X; axis <= Z; ++axis) { - EXPECT_EQ(resultCoordinate[axis], expectedCoordinate[axis]); + EXPECT_EQ(resultCoordinate[axis], expectedCoordinate[axis]) + << "Current coordinate: #" << c << std::endl + << "Current Axis: #" << axis << std::endl; } } @@ -264,16 +266,21 @@ TEST_F(CollapserTest, round_lines_to_tolerance) auto& expectedGroup = expectedMesh.groups[g]; auto& resultGroup = resultMesh.groups[g]; - ASSERT_EQ(resultGroup.elements.size(), expectedGroup.elements.size()); + ASSERT_EQ(resultGroup.elements.size(), expectedGroup.elements.size()) << "Current Group: #" << g << std::endl; for (std::size_t e = 0; e < resultGroup.elements.size(); ++e) { auto & expectedElement = expectedGroup.elements[e]; auto & resultElement = resultGroup.elements[e]; - ASSERT_EQ(resultElement.vertices.size(), expectedElement.vertices.size()); + ASSERT_EQ(resultElement.vertices.size(), expectedElement.vertices.size()) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl; for (std::size_t v = 0; v < resultElement.vertices.size(); ++v) { - EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]); + EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl + << "Current Vertex: #" << v << std::endl; } } } @@ -320,7 +327,9 @@ TEST_F(CollapserTest, collapse_individual_lines_below_tolerance) auto& resultCoordinate = resultMesh.coordinates[c]; for (Axis axis = X; axis <= Z; ++axis) { - EXPECT_EQ(resultCoordinate[axis], expectedCoordinate[axis]); + EXPECT_EQ(resultCoordinate[axis], expectedCoordinate[axis]) + << "Current coordinate: #" << c << std::endl + << "Current Axis: #" << axis << std::endl; } } @@ -333,23 +342,28 @@ TEST_F(CollapserTest, collapse_individual_lines_below_tolerance) auto& expectedGroup = expectedMesh.groups[g]; auto& resultGroup = resultMesh.groups[g]; - ASSERT_EQ(resultGroup.elements.size(), expectedGroup.elements.size()); + ASSERT_EQ(resultGroup.elements.size(), expectedGroup.elements.size()) << "Current Group: #" << g << std::endl; for (std::size_t e = 0; e < resultGroup.elements.size(); ++e) { auto& expectedElement = expectedGroup.elements[e]; auto& resultElement = resultGroup.elements[e]; - ASSERT_EQ(resultElement.vertices.size(), expectedElement.vertices.size()); + ASSERT_EQ(resultElement.vertices.size(), expectedElement.vertices.size()) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl; for (std::size_t v = 0; v < resultElement.vertices.size(); ++v) { - EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]); + EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl + << "Current Vertex: #" << v << std::endl; } } } } -TEST_F(CollapserTest, equal_lines_get_erased) +TEST_F(CollapserTest, equal_lines_get_erased_with_surface_policy) { int decimalPlaces = 2; auto tolerance = std::pow(10.0, decimalPlaces); @@ -372,6 +386,11 @@ TEST_F(CollapserTest, equal_lines_get_erased) Element({2, 3}, Element::Type::Line), Element({1, 4}, Element::Type::Line), Element({4, 5}, Element::Type::Line), + Element({5, 4}, Element::Type::Line), + Element({4, 1}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({2, 1}, Element::Type::Line), + Element({1, 0}, Element::Type::Line), }; mesh.groups[1].elements = { Element({0, 1}, Element::Type::Line), @@ -379,6 +398,11 @@ TEST_F(CollapserTest, equal_lines_get_erased) Element({2, 3}, Element::Type::Line), Element({3, 4}, Element::Type::Line), Element({4, 5}, Element::Type::Line), + Element({5, 4}, Element::Type::Line), + Element({4, 3}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({2, 1}, Element::Type::Line), + Element({1, 0}, Element::Type::Line), }; Mesh expectedMesh; @@ -411,7 +435,9 @@ TEST_F(CollapserTest, equal_lines_get_erased) auto& resultCoordinate = resultMesh.coordinates[c]; for (Axis axis = X; axis <= Z; ++axis) { - EXPECT_EQ(resultCoordinate[axis], expectedCoordinate[axis]); + EXPECT_EQ(resultCoordinate[axis], expectedCoordinate[axis]) + << "Current coordinate: #" << c << std::endl + << "Current Axis: #" << axis << std::endl; } } @@ -422,16 +448,134 @@ TEST_F(CollapserTest, equal_lines_get_erased) auto& expectedGroup = expectedMesh.groups[g]; auto& resultGroup = resultMesh.groups[g]; - ASSERT_EQ(resultGroup.elements.size(), expectedGroup.elements.size()); + ASSERT_EQ(resultGroup.elements.size(), expectedGroup.elements.size()) << "Current Group: #" << g << std::endl; for (std::size_t e = 0; e < resultGroup.elements.size(); ++e) { auto& expectedElement = expectedGroup.elements[e]; auto& resultElement = resultGroup.elements[e]; - ASSERT_EQ(resultElement.vertices.size(), expectedElement.vertices.size()); + ASSERT_EQ(resultElement.vertices.size(), expectedElement.vertices.size()) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl; for (std::size_t v = 0; v < resultElement.vertices.size(); ++v) { - EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]); + EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl + << "Current Vertex: #" << v << std::endl; + } + } + } +} + + +TEST_F(CollapserTest, equal_lines_get_erased_but_not_inverted_with_lines_policy) +{ + int decimalPlaces = 2; + auto tolerance = std::pow(10.0, decimalPlaces); + + Mesh mesh; + mesh.grid = buildGridSize2(); + + mesh.coordinates = { + Coordinate({ 0.000, 0.000, 0.000}), + Coordinate({ 0.325, 0.325, 0.325}), + Coordinate({ 1.557, 1.556, 1.584}), + Coordinate({ 1.995, 3.226, 0.112}), + Coordinate({ 1.562, 1.562, 1.579}), + Coordinate({ 2.004, 3.230, 0.109}), + }; + mesh.groups.resize(2); + mesh.groups[0].elements = { + Element({0, 1}, Element::Type::Line), + Element({1, 2}, Element::Type::Line), + Element({2, 3}, Element::Type::Line), + Element({1, 4}, Element::Type::Line), + Element({4, 5}, Element::Type::Line), + Element({5, 4}, Element::Type::Line), + Element({4, 1}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({2, 1}, Element::Type::Line), + Element({1, 0}, Element::Type::Line), + }; + mesh.groups[1].elements = { + Element({0, 1}, Element::Type::Line), + Element({1, 2}, Element::Type::Line), + Element({2, 3}, Element::Type::Line), + Element({3, 4}, Element::Type::Line), + Element({4, 5}, Element::Type::Line), + Element({5, 4}, Element::Type::Line), + Element({4, 3}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({2, 1}, Element::Type::Line), + Element({1, 0}, Element::Type::Line), + }; + + Mesh expectedMesh; + expectedMesh.grid = buildGridSize2(); + expectedMesh.coordinates = { + Coordinate({ 0.00, 0.00, 0.00}), + Coordinate({ 0.33, 0.33, 0.33}), + Coordinate({ 1.56, 1.56, 1.58}), + Coordinate({ 2.00, 3.23, 0.11}), + }; + expectedMesh.groups.resize(2); + expectedMesh.groups[0].elements = { + Element({0, 1}, Element::Type::Line), + Element({1, 2}, Element::Type::Line), + Element({2, 3}, Element::Type::Line), + Element({1, 2}, Element::Type::Line), + Element({2, 3}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({2, 1}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({2, 1}, Element::Type::Line), + Element({1, 0}, Element::Type::Line), + }; + expectedMesh.groups[1].elements = { + Element({0, 1}, Element::Type::Line), + Element({1, 2}, Element::Type::Line), + Element({2, 3}, Element::Type::Line), + }; + + + auto resultMesh = Collapser(mesh, decimalPlaces, {Element::Type::Line, Element::Type::Surface}).getMesh(); + + ASSERT_EQ(resultMesh.coordinates.size(), expectedMesh.coordinates.size()); + + for (std::size_t c = 0; c < resultMesh.coordinates.size(); ++c) { + auto& expectedCoordinate = expectedMesh.coordinates[c]; + auto& resultCoordinate = resultMesh.coordinates[c]; + + for (Axis axis = X; axis <= Z; ++axis) { + EXPECT_EQ(resultCoordinate[axis], expectedCoordinate[axis]) + << "Current coordinate: #" << c << std::endl + << "Current Axis: #" << axis << std::endl; + } + } + + ASSERT_EQ(resultMesh.groups.size(), expectedMesh.groups.size()); + ASSERT_EQ(resultMesh.groups.size(), mesh.groups.size()); + + for (std::size_t g = 0; g < resultMesh.groups.size(); ++g) { + auto& expectedGroup = expectedMesh.groups[g]; + auto& resultGroup = resultMesh.groups[g]; + + ASSERT_EQ(resultGroup.elements.size(), expectedGroup.elements.size()) << "Current Group: #" << g << std::endl; + + for (std::size_t e = 0; e < resultGroup.elements.size(); ++e) { + auto& expectedElement = expectedGroup.elements[e]; + auto& resultElement = resultGroup.elements[e]; + + ASSERT_EQ(resultElement.vertices.size(), expectedElement.vertices.size()) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl; + + for (std::size_t v = 0; v < resultElement.vertices.size(); ++v) { + EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl + << "Current Vertex: #" << v << std::endl; } } } diff --git a/test/core/SlicerTest.cpp b/test/core/SlicerTest.cpp index 9a3ba2a..1ae8c6e 100644 --- a/test/core/SlicerTest.cpp +++ b/test/core/SlicerTest.cpp @@ -426,7 +426,9 @@ TEST_F(SlicerTest, canSliceLinesInAdjacentCellsFromTheSamePlane) for (std::size_t i = 0; i < expectedRelatives.size(); ++i) { for (std::size_t axis = 0; axis < 3; ++axis) { - EXPECT_DOUBLE_EQ(resultMesh.coordinates[i][axis], expectedRelatives[i][axis]); + EXPECT_DOUBLE_EQ(resultMesh.coordinates[i][axis], expectedRelatives[i][axis]) + << "Current coordinate: #" << i << std::endl + << "Current Axis: #" << axis << std::endl; } } @@ -434,15 +436,19 @@ TEST_F(SlicerTest, canSliceLinesInAdjacentCellsFromTheSamePlane) auto& resultGroup = resultMesh.groups[g]; auto& expectedGroup = expectedElements[g]; - EXPECT_TRUE(resultGroup.elements[0].isLine()); - EXPECT_TRUE(resultGroup.elements[1].isLine()); - for (std::size_t e = 0; e < expectedGroup.size(); ++e) { auto& resultElement = resultGroup.elements[e]; auto& expectedElement = expectedGroup[e]; + EXPECT_TRUE(resultElement.isLine()) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl; + for (std::size_t v = 0; v < expectedElement.vertices.size(); ++v) { - EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]); + EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl + << "Current Vertex: #" << v << std::endl; } } } @@ -545,7 +551,9 @@ TEST_F(SlicerTest, canSliceLinesInAdjacentCellThatPassThoroughPoints) for (std::size_t i = 0; i < expectedRelatives.size(); ++i) { for (std::size_t axis = 0; axis < 3; ++axis) { - EXPECT_DOUBLE_EQ(resultMesh.coordinates[i][axis], expectedRelatives[i][axis]); + EXPECT_DOUBLE_EQ(resultMesh.coordinates[i][axis], expectedRelatives[i][axis]) + << "Current coordinate: #" << i << std::endl + << "Current Axis: #" << axis << std::endl; } } @@ -561,7 +569,10 @@ TEST_F(SlicerTest, canSliceLinesInAdjacentCellThatPassThoroughPoints) auto& expectedElement = expectedGroup[e]; for (std::size_t v = 0; v < expectedElement.vertices.size(); ++v) { - EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]); + EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl + << "Current Vertex: #" << v << std::endl; } } } @@ -628,7 +639,9 @@ TEST_F(SlicerTest, keepsNodesIntact) for (std::size_t i = 0; i < m.coordinates.size(); ++i) { for (std::size_t axis = 0; axis < 3; ++axis) { - EXPECT_DOUBLE_EQ(resultMesh.coordinates[i][axis], expectedRelatives[i][axis]); + EXPECT_DOUBLE_EQ(resultMesh.coordinates[i][axis], expectedRelatives[i][axis]) + << "Current coordinate: #" << i << std::endl + << "Current Axis: #" << axis << std::endl; } } @@ -661,7 +674,7 @@ TEST_F(SlicerTest, canSliceLinesInAdjacentCellsWithThreeDimensionalMovement) // +5*---┼---┼-----*---┼---┼/----*¦ | / | +5*---┼---┼-----*---┼---┼/-┼--*¦ | / | // | |/ | | |/ /| |¦ |/ | | |/ | | |/ /| ¦ |¦ |/ | // | *---┼-----┼---*/--┼-----┼┼--* | | *---┼-----┼---*/--┼--┼--┼┼--* | - // | /| +5|Y | ⫽| ⎸ ⎸¦ /⎸ ⎸ -> ⎸ ┈┈┼┈┈┈┼Y┈┈┈┈┼┈┈+{2} ⎸ ¦ ⎸¦ /⎸ ⎸ + // | /| +5|Y | ⫽⎸ ⎸ ⎸¦ /⎸ ⎸ -> ⎸ ┈┈┼┈┈┈┼Y┈┈┈┈┼┈┈+{2} ⎸ ¦ ⎸¦ /⎸ ⎸ // | / | *-----┼//-┼---*-----┼┼/-┼---* | / | +5*-----┼//-┼---*--┼--┼┼/-┼---* // |/ | / /|/ | / |¦ | / |/ | / +{1} | / ¦ |¦ | / // *---┼------/--*---┼---------* | / *---┼------/-┼*---┼------¦--* | / @@ -736,16 +749,247 @@ TEST_F(SlicerTest, canSliceLinesInAdjacentCellsWithThreeDimensionalMovement) auto& resultElement = resultGroup.elements[e]; auto& expectedElement = expectedElements[e]; - EXPECT_TRUE(expectedElement.isLine()); + EXPECT_TRUE(expectedElement.isLine()) << "Current Element: #" << e << std::endl; for (std::size_t v = 0; v < expectedElement.vertices.size(); ++v) { - EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]); + EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]) + << "Current Element: #" << e << std::endl + << "Current Vertex: #" << v << std::endl; } } } +TEST_F(SlicerTest, canKeepOppositeLinesInLineDimensionPolicy) +{ + + // z z + // *-------------*-------------* *-------------*----------------* + // | ⎹ | | | ⎸ + // | ⎹ 1 | | | {1->3} ⎸ + // | ⎹⫽ ⎸ ⎸ ⎸⫽ ⎸ + // | ⩘ ⫽⎸ 3 ⎹ ⎹ {0.66->2} {3->6} ⎸ + // | / ⫽/| ⫽⩘ | | ⫽ ⎸ ⫽ ⎹ + // | ⫽ ⩗ ⎸⫽ / | | ⫽ ⎸ ⫽ ⎹ + // *-------⫽-----*-------------* -> *-{0.33->1}--{2.5->5}---------* + // | ⫽ ⫽⎹ ⎹ ⎹ ⫽ ⫽ ⎸ | + // | 0 ⫽ ⎸ ⎸ ⎸ 0 ⫽ | | + // | 2 | | | {2->4} | | + // | | | | | | + // | | | | | | + // | <- | | | | | + // *--------4====*====5--------* x *------{4->7}===*===={5->8}----* x + // -> + // + // + // *-------------*--------------* *-------------*-------------* + // /| /| /⎸ /| /| /| + // / | / | / ⎸ / | / | / | + // / | / | / ⎸ / | / | / | + // *---┼---------*---┼----------* ⎸ *---┼---------*---┼---------* | + // /| | /| | 7 /⎸ ⎸ /| | /| | 10/| | + // Z / | *-------/-┼---*----⫽-┼/-┼---* Z / | *-------/-┼---*----⫽-┼/-┼---* + // / | /| / | /| ⫽ / | /| / | /| / | /| +{9}/ ⎹ /⎹ + // +5*---┼---┼-----*---┼---┼⫽----*¦ | / | +5*---┼---┼-----*---┼- -┼⫽-┼--*¦ | / | + // | |/ | | |/ ⫽| |¦ |/ | | |/ | | |/⫽⎹ ¦ |¦ |/ | + // | *---┼-----┼---*⫽--┼-----┼┼--* | | *---┼-----┼---*⫽-┼---┼--┼┼--* | + // | /| +5|Y | ⫻⎸ ⎸ ⎸¦ /⎸ ⎸ -> ⎸ ┈┈┼┈┈┈┼Y┈┈┈┈┼┈┈+{8}⎹ ¦ ⎹⎹ /⎸ ⎹ + // | / | *-----┼⫽/-┼---*-----┼┼/-┼---* | / | +5*-----┼⫽/-┼--*---┼--┼┼/-┼---* + // |/ | / ⫽⎸/ | / |¦ | / |/ | / +{7} | / ¦ ⎹¦ ⎹ / + // *---┼------⫽--*---┼---------* | / *---┼------⫽-┼*--┼------¦---* | / + // | |/ 6 | ⎹/ | ⎹/ | |/ 6 ¦| |/ | |/ + // | *-----┼---┼---*---------┼---* | *----┼---┼┼---*----------┼---* + // | / ¦ | / | / | / ¦ | / | / + // | / | / | / | / | / | / + // |/ |/ |/ |/ |/ |/ + // -5*-------------*-------------* +5 X -5*-------------*--------------* +5 X + + Mesh m; + m.grid = { + std::vector({-5.0, 0.0, 5.0}), + std::vector({-5.0, 0.0, 5.0}), + std::vector({-5.0, 0.0, 5.0}) + }; + m.coordinates = { + Coordinate({ -4.5, -5.0, -1.5 }), // First Line, Vertex 0, Second Line, Vertex 0 + Coordinate({ +1.5, -5.0, +4.5 }), // First Line, Vertex 1, Second Line, Vertex 1 + Coordinate({ -3.0, -5.0, -3.0 }), // Third Line, Vertex 1, Fourth Line, Vertex 0 + Coordinate({ +3.0, -5.0, +3.0 }), // Third Line, Vertex 0, Fourth Line, Vertex 1 + Coordinate({ -4.5, -5.0, -5.0 }), // Fifth Line, Vertex 0, Sixth Line, Vertex 0 + Coordinate({ +4.5, -5.0, -5.0 }), // Fifth Line, Vertex 1, Sixth Line, Vertex 1 + Coordinate({ -2.75, -2.75, -1.50 }), // Seventh Line, Vertex 0, Eighth Line, Vertex 0 + Coordinate({ +2.40, +1.50, +1.50 }), // Seventh Line, Vertex 1, Eighth Line, Vertex 1 + }; + + m.groups.resize(2); + m.groups[0].elements = { + Element{ {0, 1}, Element::Type::Line }, // First Line + Element{ {1, 0}, Element::Type::Line }, // Second Line + Element{ {2, 3}, Element::Type::Line }, // Third Line + Element{ {3, 2}, Element::Type::Line }, // Fourth Line + Element{ {4, 5}, Element::Type::Line }, // Fifth Line + Element{ {5, 4}, Element::Type::Line }, // Sixth Line + Element{ {6, 7}, Element::Type::Line }, // Seventh Line + Element{ {7, 6}, Element::Type::Line }, // Eighth Line + }; + m.groups[1].elements = { + Element{ {0, 1}, Element::Type::Line }, // First Line + Element{ {1, 0}, Element::Type::Line }, // Second Line + Element{ {2, 3}, Element::Type::Line }, // Third Line + Element{ {3, 2}, Element::Type::Line }, // Fourth Line + Element{ {4, 5}, Element::Type::Line }, // Fifth Line + Element{ {5, 4}, Element::Type::Line }, // Sixth Line + Element{ {6, 7}, Element::Type::Line }, // Seventh Line + Element{ {7, 6}, Element::Type::Line }, // Eighth Line + }; + GridTools tools(m.grid); + + Coordinate distanceFirstLine = m.coordinates[1] - m.coordinates[0]; + CoordinateDir firstPointXComponent = 0.0 - m.coordinates[0][0]; + CoordinateDir firstPointZComponent = firstPointXComponent * distanceFirstLine[2] / distanceFirstLine[0]; + CoordinateDir secondPointZComponent = 0.0 - m.coordinates[1][2]; + CoordinateDir secondPointXComponent = secondPointZComponent * distanceFirstLine[0] / distanceFirstLine[2]; + Coordinate firstIntersectionPointFirstLine = Coordinate({ secondPointXComponent + m.coordinates[1][0], -5.0, 0.0 }); + Coordinate secondIntersectionPointFirstLine = Coordinate({ 0.0, -5.0, firstPointZComponent + m.coordinates[0][2] }); + + Coordinates expectedCoordinates = { + m.coordinates[0], // 0 First Line, First Point + firstIntersectionPointFirstLine, // 1 First Line, First Intersection Point + secondIntersectionPointFirstLine, // 2 First Line, Second Intersection Point + m.coordinates[1], // 3 First Line, Final Point + m.coordinates[2], // 4 Third Line, First Point + Coordinate({ 0, -5.0, 0 }), // 5 Third Line, Intersection Point + m.coordinates[3], // 6 Third Line, Final Point + m.coordinates[4], // 7 Fifth Line, Final Point + Coordinate({ 0, -5.0, -5.0 }), // 8 Fifth Line, Intersection Point + m.coordinates[5], // 9 Fifth Line, Final Point + m.coordinates[6], // 10 Seventh Line, First Point + Coordinate({0, 0, 0}), // 11 Seventh Line, First Intersection Point + Coordinate({0, 0, 0}), // 12 Seventh Line, Second Intersection Point + Coordinate({0, 0, 0}), // 13 Seventh Line, Third Intersection Point + m.coordinates[7], // 14 Seventh Line, Final Point + }; + + Relatives expectedRelatives = tools.absoluteToRelative(expectedCoordinates); + + std::vector expectedElements = { + { + Element({0, 1}, Element::Type::Line), + Element({1, 2}, Element::Type::Line), + Element({2, 3}, Element::Type::Line), + + Element({3, 2}, Element::Type::Line), + Element({2, 1}, Element::Type::Line), + Element({1, 0}, Element::Type::Line), + + Element({4, 5}, Element::Type::Line), + Element({5, 6}, Element::Type::Line), + + Element({6, 5}, Element::Type::Line), + Element({5, 4}, Element::Type::Line), + + Element({7, 8}, Element::Type::Line), + Element({8, 9}, Element::Type::Line), + + Element({9, 8}, Element::Type::Line), + Element({8, 7}, Element::Type::Line), + + Element({10, 11}, Element::Type::Line), + Element({11, 12}, Element::Type::Line), + Element({12, 13}, Element::Type::Line), + Element({13, 14}, Element::Type::Line), + + Element({14, 13}, Element::Type::Line), + Element({13, 12}, Element::Type::Line), + Element({12, 11}, Element::Type::Line), + Element({11, 10}, Element::Type::Line), + }, + { + + Element({0, 1}, Element::Type::Line), + Element({1, 2}, Element::Type::Line), + Element({2, 3}, Element::Type::Line), + + Element({4, 5}, Element::Type::Line), + Element({5, 6}, Element::Type::Line), + + Element({7, 8}, Element::Type::Line), + Element({8, 9}, Element::Type::Line), + + Element({10, 11}, Element::Type::Line), + Element({11, 12}, Element::Type::Line), + Element({12, 13}, Element::Type::Line), + Element({13, 14}, Element::Type::Line), + } + }; + + Mesh resultMesh; + + Relatives relativeCoordinates = tools.absoluteToRelative(m.coordinates); + + std::vector dimensions({ Element::Type::Line, Element::Type::Surface }); + + ASSERT_NO_THROW(resultMesh = Slicer(m, dimensions).getMesh()); + + EXPECT_FALSE(containsDegenerateTriangles(resultMesh)); + RedundancyCleaner::cleanCoords(resultMesh); + ASSERT_EQ(resultMesh.coordinates.size(), expectedCoordinates.size()); + + + + ASSERT_EQ(resultMesh.groups.size(), expectedElements.size()); + + for (std::size_t i = 0; i < (m.coordinates.size() - 4); ++i) { + for (std::size_t axis = 0; axis < 3; ++axis) { + EXPECT_DOUBLE_EQ(resultMesh.coordinates[i][axis], expectedRelatives[i][axis]) + << "Current coordinate: #" << i << std::endl + << "Current Axis: #" << axis << std::endl; + } + } + + EXPECT_LT(resultMesh.coordinates[11][X], 1.0); + EXPECT_LT(resultMesh.coordinates[11][Y], 1.0); + EXPECT_EQ(resultMesh.coordinates[11][Z], 1.0); + + EXPECT_EQ(resultMesh.coordinates[12][X], 1.0); + EXPECT_LT(resultMesh.coordinates[12][Y], 1.0); + EXPECT_GT(resultMesh.coordinates[12][Z], 1.0); + + EXPECT_GT(resultMesh.coordinates[13][X], 1.0); + EXPECT_EQ(resultMesh.coordinates[13][Y], 1.0); + EXPECT_GT(resultMesh.coordinates[13][Z], 1.0); + + EXPECT_DOUBLE_EQ(resultMesh.coordinates[14][X], expectedRelatives[14][X]); + EXPECT_DOUBLE_EQ(resultMesh.coordinates[14][Y], expectedRelatives[14][Y]); + EXPECT_DOUBLE_EQ(resultMesh.coordinates[14][Z], expectedRelatives[14][Z]); + + + + for (GroupId g = 0; g < resultMesh.groups.size(); ++g) { + auto& resultGroup = resultMesh.groups[g]; + auto& expectedGroup = expectedElements[g]; + + ASSERT_EQ(resultGroup.elements.size(), expectedGroup.size()) << "Current Group: #" << g << std::endl; + + for (ElementId e = 0; e < expectedGroup.size(); ++e) { + auto& resultElement = resultGroup.elements[e]; + auto& expectedElement = expectedGroup[e]; + + EXPECT_TRUE(expectedElement.isLine()) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl; + + for (CoordinateId v = 0; v < expectedElement.vertices.size(); ++v) { + EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl + << "Current Vertex: #" << v << std::endl; + } + } + } +} + TEST_F(SlicerTest, preserves_topological_closedness_for_alhambra) { diff --git a/test/core/StaircaserTest.cpp b/test/core/StaircaserTest.cpp index 8f96a95..8b9f94e 100644 --- a/test/core/StaircaserTest.cpp +++ b/test/core/StaircaserTest.cpp @@ -732,7 +732,7 @@ TEST_F(StaircaserTest, transformSingleSegmentsWithinDiagonalIntoThreeStaircasedE // | *--╱------┼-┼-* | *---------{0.67->2} // | / ╱ | / | / | ⫽ // | /╱ | / | / | ⫽ - // |⌿0 |/ |/ |⫽ + // |⌿0 |/ |/ |⫽ // *-------------* x 0========={0.33->1} x float lowerRelativeValue = -5.0; diff --git a/test/meshers/StaircaseMesherTest.cpp b/test/meshers/StaircaseMesherTest.cpp index 7a4c1df..fb3af48 100644 --- a/test/meshers/StaircaseMesherTest.cpp +++ b/test/meshers/StaircaseMesherTest.cpp @@ -45,10 +45,12 @@ class StaircaseMesherTest : public ::testing::Test { auto& leftGridAxisPlanes = leftMesh.grid[axis]; auto& rightGridAxisPlanes = rightMesh.grid[axis]; - EXPECT_EQ(leftGridAxisPlanes.size(), rightGridAxisPlanes.size()); + EXPECT_EQ(leftGridAxisPlanes.size(), rightGridAxisPlanes.size()) << "Current Axis: #" << axis << std::endl; for (std::size_t plane = 0; plane != leftGridAxisPlanes.size(); ++plane) { - EXPECT_EQ(leftGridAxisPlanes[plane], rightGridAxisPlanes[plane]); + EXPECT_EQ(leftGridAxisPlanes[plane], rightGridAxisPlanes[plane]) + << "Current Axis: #" << axis << std::endl + << "Current Plane: #" << plane << std::endl; } } @@ -64,17 +66,22 @@ class StaircaseMesherTest : public ::testing::Test { for (std::size_t g = 0; g < leftMesh.groups.size(); ++g) { auto& leftGroup = leftMesh.groups[g]; - auto& rightGroup = leftMesh.groups[g]; + auto& rightGroup = rightMesh.groups[g]; - ASSERT_EQ(leftGroup.elements.size(), rightGroup.elements.size()); + ASSERT_EQ(leftGroup.elements.size(), rightGroup.elements.size()) << "Current Group: #" << g << std::endl; for (std::size_t e = 0; e < leftGroup.elements.size(); ++e) { auto& resultElement = leftGroup.elements[e]; auto& expectedElement = rightGroup.elements[e]; - EXPECT_EQ(resultElement.type, expectedElement.type); + EXPECT_EQ(resultElement.type, expectedElement.type) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl; for (CoordinateId v = 0; v != resultElement.vertices.size(); ++v) { - EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]); + EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]) + << "Current Group: #" << g << std::endl + << "Current Element: #" << e << std::endl + << "Current Vertex: #" << v << std::endl; } } @@ -100,7 +107,8 @@ TEST_F(StaircaseMesherTest, testStaircaseLinesWithUniformGrid) }; inputMesh.groups.resize(1); inputMesh.groups[0].elements = { - Element({0, 1}, Element::Type::Line) + Element({0, 1}, Element::Type::Line), + Element({1, 0}, Element::Type::Line) }; Mesh expectedMesh; @@ -122,17 +130,22 @@ TEST_F(StaircaseMesherTest, testStaircaseLinesWithUniformGrid) } expectedMesh.groups.resize(1); expectedMesh.groups[0].elements = { - Element({0}, Element::Type::Node), Element({0, 1}, Element::Type::Line), Element({1, 2}, Element::Type::Line), - Element({2}, Element::Type::Node), Element({2, 3}, Element::Type::Line), Element({3, 4}, Element::Type::Line), Element({4, 5}, Element::Type::Line), Element({5, 6}, Element::Type::Line), - Element({6}, Element::Type::Node), Element({6, 7}, Element::Type::Line), Element({7, 8}, Element::Type::Line), + Element({8, 7}, Element::Type::Line), + Element({7, 6}, Element::Type::Line), + Element({6, 5}, Element::Type::Line), + Element({5, 4}, Element::Type::Line), + Element({4, 3}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({2, 1}, Element::Type::Line), + Element({1, 0}, Element::Type::Line), }; Mesh resultMesh; @@ -164,6 +177,8 @@ TEST_F(StaircaseMesherTest, testStaircaseLinesWithRectilinearGrid) inputMesh.groups[0].elements = { Element({0, 1}, Element::Type::Line), Element({1, 2}, Element::Type::Line), + Element({2, 1}, Element::Type::Line), + Element({1, 0}, Element::Type::Line), }; @@ -184,9 +199,13 @@ TEST_F(StaircaseMesherTest, testStaircaseLinesWithRectilinearGrid) Element({0, 1}, Element::Type::Line), Element({1, 2}, Element::Type::Line), Element({2, 3}, Element::Type::Line), - Element({3}, Element::Type::Node), Element({3, 4}, Element::Type::Line), Element({4, 5}, Element::Type::Line), + Element({5, 4}, Element::Type::Line), + Element({4, 3}, Element::Type::Line), + Element({3, 2}, Element::Type::Line), + Element({2, 1}, Element::Type::Line), + Element({1, 0}, Element::Type::Line), }; Mesh resultMesh; @@ -211,7 +230,7 @@ TEST_F(StaircaseMesherTest, testTriNonUniformGridStaircase) // FOR DEBUG ONLY / OBTAIN VISUAL REPRESENTATION -TEST_F(StaircaseMesherTest, DISABLED_visualSelectiveStructurerCone) +TEST_F(StaircaseMesherTest, DISABLED_visualSelectiveStaircaserCone) { // Input const std::string inputFilename = "testData/cases/cone/cone.stl"; @@ -233,7 +252,7 @@ TEST_F(StaircaseMesherTest, DISABLED_visualSelectiveStructurerCone) auto collapsedMesh = meshlib::core::Collapser{slicedMesh, 4}.getMesh(); - // Selection the specific cells to structure and generate the result Mesh + // Selection the specific cells to staircase and generate the result Mesh std::set cellSet; @@ -331,7 +350,7 @@ TEST_F(StaircaseMesherTest, preserves_topological_closedness_for_sphere) // vtkIO::exportMeshToVTU("testData/cases/sphere/sphere.contour.vtk", contourMesh); } -TEST_F(StaircaseMesherTest, selectiveStructurer_preserves_topological_closedness_for_sphere) +TEST_F(StaircaseMesherTest, selectiveStaircaser_preserves_topological_closedness_for_sphere) { const std::string inputFilename = "testData/cases/sphere/sphere.stl"; auto mesh = vtkIO::readInputMesh("testData/cases/sphere/sphere.stl"); @@ -351,7 +370,7 @@ TEST_F(StaircaseMesherTest, selectiveStructurer_preserves_topological_closedness auto collapsedMesh = meshlib::core::Collapser{slicedMesh, 4}.getMesh(); - // Selection the specific cells to structure and generate the result Mesh + // Selection the specific cells to staircase and generate the result Mesh std::set cellSet; @@ -381,7 +400,7 @@ TEST_F(StaircaseMesherTest, selectiveStructurer_preserves_topological_closedness // meshlib::vtkIO::exportGridToVTU(outputFolder / (basename + ".tessellator.selective.grid.vtk"), resultMesh.grid); } -TEST_F(StaircaseMesherTest, selectiveStructurer_preserves_topological_closedness_for_alhambra) +TEST_F(StaircaseMesherTest, selectiveStaircaser_preserves_topological_closedness_for_alhambra) { const std::string inputFilename = "testData/cases/alhambra/alhambra.stl"; auto mesh = vtkIO::readInputMesh("testData/cases/alhambra/alhambra.stl"); @@ -402,7 +421,7 @@ TEST_F(StaircaseMesherTest, selectiveStructurer_preserves_topological_closedness auto collapsedMesh = meshlib::core::Collapser{slicedMesh, 2}.getMesh(); - // Selection the specific cells to structure and generate the result Mesh + // Selection the specific cells to staircase and generate the result Mesh std::set cellSet; From 0feaa7465cc1d3b7c09db253689457ce26e9db96 Mon Sep 17 00:00:00 2001 From: carlosgonzalez-elemwave Date: Fri, 24 Oct 2025 13:52:29 +0200 Subject: [PATCH 5/5] Testing | Test case of coiling polyline --- test/app/launcherTest.cpp | 11 +++- test/meshers/StaircaseMesherTest.cpp | 53 ++++++++++++++++++- .../longPolyline.tessellator.json | 10 ++++ testData/cases/longPolyline/longPolyline.vtu | 25 +++++++++ 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 testData/cases/longPolyline/longPolyline.tessellator.json create mode 100644 testData/cases/longPolyline/longPolyline.vtu diff --git a/test/app/launcherTest.cpp b/test/app/launcherTest.cpp index 4188f2d..9612a25 100644 --- a/test/app/launcherTest.cpp +++ b/test/app/launcherTest.cpp @@ -46,7 +46,16 @@ TEST_F(LauncherTest, launches_thinCylinder_case) TEST_F(LauncherTest, launches_cone_case) { int ac = 3; - const char* av[] = { NULL, "-i", "testData/cases/cone/cone.tessellator.json"}; + const char* av[] = { NULL, "-i", "testData/cases/cone/cone.tessellator.json" }; + int exitCode; + EXPECT_NO_THROW(exitCode = meshlib::app::launcher(ac, av)); + EXPECT_EQ(exitCode, EXIT_SUCCESS); +} + +TEST_F(LauncherTest, launches_long_polyline_case) +{ + int ac = 3; + const char* av[] = { NULL, "-i", "testData/cases/longPolyline/longPolyline.tessellator.json" }; int exitCode; EXPECT_NO_THROW(exitCode = meshlib::app::launcher(ac, av)); EXPECT_EQ(exitCode, EXIT_SUCCESS); diff --git a/test/meshers/StaircaseMesherTest.cpp b/test/meshers/StaircaseMesherTest.cpp index fb3af48..53d7373 100644 --- a/test/meshers/StaircaseMesherTest.cpp +++ b/test/meshers/StaircaseMesherTest.cpp @@ -449,4 +449,55 @@ TEST_F(StaircaseMesherTest, selectiveStaircaser_preserves_topological_closedness // meshlib::vtkIO::exportGridToVTU(outputFolder / (basename + ".tessellator.selective.grid.vtk"), resultMesh.grid); } -} \ No newline at end of file +TEST_F(StaircaseMesherTest, staircaser_reads_wires_correctly) +{ + const std::string inputFilename = "testData/cases/longPolyline/longPolyline.vtu"; + auto mesh = vtkIO::readInputMesh("testData/cases/longPolyline/longPolyline.vtu"); + + mesh.grid[X] = utils::GridTools::linspace(600, 1100.0, 20); + mesh.grid[Y] = utils::GridTools::linspace(600, 1100.0, 20); + mesh.grid[Z] = utils::GridTools::linspace(600, 1100.0, 20); + + std::vector dimensions = { Element::Type::Line }; + + // SurfaceMesh + + auto surfaceMesh = meshlib::utils::meshTools::buildMeshFilteringElements(mesh, meshlib::utils::meshTools::isNotTetrahedron); + + // Slicer + + auto slicedMesh = meshlib::core::Slicer{ surfaceMesh, dimensions }.getMesh(); + + // Collapser + + auto collapsedMesh = meshlib::core::Collapser{ slicedMesh, 2, dimensions }.getMesh(); + + // Selection the specific cells to staircase and generate the result Mesh + + std::set cellSet; + + for (int x = 0; x < 20; ++x) { + for (int y = 0; y < 20; ++y) { + for (int z = 0; z < 20; ++z) { + cellSet.insert(Cell{ x, y, z }); + } + } + } + + auto resultMesh = meshlib::core::Staircaser{ collapsedMesh }.getMesh(); + + RedundancyCleaner::removeOverlappedElementsByDimension(resultMesh, dimensions); + utils::meshTools::reduceGrid(resultMesh, mesh.grid); + utils::meshTools::convertToAbsoluteCoordinates(resultMesh); + + assertMeshEqual(StaircaseMesher(mesh).mesh(), resultMesh); + + // // For debugging + // std::filesystem::path outputFolder = meshlib::vtkIO::getFolder(inputFilename); + // auto basename = meshlib::vtkIO::getBasename(inputFilename); + // meshlib::vtkIO::exportMeshToVTU(outputFolder / (basename + ".tessellator.selective.vtk"), resultMesh); + // meshlib::vtkIO::exportGridToVTU(outputFolder / (basename + ".tessellator.selective.grid.vtk"), resultMesh.grid); +} + +} + diff --git a/testData/cases/longPolyline/longPolyline.tessellator.json b/testData/cases/longPolyline/longPolyline.tessellator.json new file mode 100644 index 0000000..1c672f3 --- /dev/null +++ b/testData/cases/longPolyline/longPolyline.tessellator.json @@ -0,0 +1,10 @@ +{ + "grid": { + "numberOfCells": [20, 20, 20], + "boundingBox": [ + [ 600, 600, 600], + [1100, 1100, 1100] + ] + }, + "object": {"filename": "longPolyline.vtu"} +} \ No newline at end of file diff --git a/testData/cases/longPolyline/longPolyline.vtu b/testData/cases/longPolyline/longPolyline.vtu new file mode 100644 index 0000000..eb3dc7b --- /dev/null +++ b/testData/cases/longPolyline/longPolyline.vtu @@ -0,0 +1,25 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET UNSTRUCTURED_GRID +POINTS 6 float +1000 695 800 1002 696 801 1004 697 802 +1004 700 822 1004 750 860 1050 700 800 +CELLS 11 20 +OFFSETS vtktypeint64 +0 2 4 6 8 10 12 14 16 18 20 +CONNECTIVITY vtktypeint64 +0 1 1 2 2 3 3 4 4 +5 5 4 4 3 3 2 2 1 +1 0 +CELL_TYPES 10 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3