diff --git a/src/app/launcher.cpp b/src/app/launcher.cpp index 76730a1..8ff0c0c 100644 --- a/src/app/launcher.cpp +++ b/src/app/launcher.cpp @@ -2,6 +2,7 @@ #include "vtkIO.h" #include "meshers/StaircaseMesher.h" +#include "meshers/ConformalMesher.h" #include "utils/GridTools.h" #include @@ -11,12 +12,13 @@ #include #include #include - +#include namespace meshlib::app { using namespace vtkIO; + namespace po = boost::program_options; Grid parseGridFromJSON(const nlohmann::json &j) @@ -61,6 +63,59 @@ Mesh readMesh(const std::string &fn) return res; } + +std::string readMesherType(const std::string &fn) +{ + nlohmann::json j; + { + std::ifstream i(fn); + i >> j; + } + if (j["mesher"].contains("type")) { + return j["mesher"]["type"]; + } else { + return meshlib::app::staircase_mesher; + } +} + +std::string readExtension(const std::string &fn) +{ + auto mesherType = readMesherType(fn); + if (mesherType == meshlib::app::staircase_mesher) { + return "str"; + } else if (mesherType == meshlib::app::conformal_mesher) { + return "cmsh"; + } else { + throw std::runtime_error("Unsupported mesher type"); + } +} + +meshlib::meshers::ConformalMesherOptions readConformalMesherOptions(const std::string &fn) +{ + nlohmann::json j; + { + std::ifstream i(fn); + i >> j; + } + meshlib::meshers::ConformalMesherOptions res; + if (j["mesher"].contains("options")) { + res.snapperOptions.edgePoints = j["mesher"]["options"]["edgePoints"]; + res.snapperOptions.forbiddenLength = j["mesher"]["options"]["forbiddenLength"]; + } + return res; +} +std::unique_ptr buildMesher(const Mesh &in, const std::string &fn) +{ + auto mesherType = readMesherType(fn); + if (mesherType == meshlib::app::staircase_mesher) { + return std::make_unique(meshlib::meshers::StaircaseMesher{in}); + } else if (mesherType == meshlib::app::conformal_mesher) { + return std::make_unique(meshlib::meshers::ConformalMesher{in, readConformalMesherOptions(fn)}); + } else { + throw std::runtime_error("Unsupported mesher type"); + } +} + int launcher(int argc, const char* argv[]) { po::options_description desc("Allowed options"); @@ -84,13 +139,16 @@ int launcher(int argc, const char* argv[]) Mesh mesh = readMesh(inputFilename); + // Mesh - meshlib::meshers::StaircaseMesher mesher{mesh}; - Mesh resultMesh = mesher.mesh(); + auto mesher = buildMesher(mesh, inputFilename); + Mesh resultMesh = mesher->mesh(); std::filesystem::path outputFolder = getFolder(inputFilename); auto basename = getBasename(inputFilename); - exportMeshToVTU(outputFolder / (basename + ".tessellator.str.vtk"), resultMesh); + auto extension = readExtension(inputFilename); + + exportMeshToVTU(outputFolder / (basename + ".tessellator." + extension + ".vtk"), resultMesh); exportGridToVTU(outputFolder / (basename + ".tessellator.grid.vtk"), resultMesh.grid); return EXIT_SUCCESS; diff --git a/src/app/launcher.h b/src/app/launcher.h index 4e33199..e54b2f5 100644 --- a/src/app/launcher.h +++ b/src/app/launcher.h @@ -1,9 +1,13 @@ #pragma once #include "types/Mesh.h" +#include namespace meshlib::app { +const std::string conformal_mesher ("conformal"); +const std::string staircase_mesher ("staircase"); + int launcher(int argc, const char* argv[]); } \ No newline at end of file diff --git a/src/core/Staircaser.cpp b/src/core/Staircaser.cpp index 7f48ef7..40c9d00 100644 --- a/src/core/Staircaser.cpp +++ b/src/core/Staircaser.cpp @@ -75,31 +75,41 @@ IdSet findCommonNeighborsVertices(const Mesh& mesh, const std::pairvertices; - auto it = std::find(elementVertices.begin(), elementVertices.end(), vertex1); - - if (it != elementVertices.end()) { - auto pos = std::distance(elementVertices.begin(), it); - auto n = elementVertices.size(); - - neighborsOfVertex1.insert(elementVertices[(pos + n - 1) % n]); - neighborsOfVertex1.insert(elementVertices[(pos + 1) % n]); - } + auto it = cellElemMap.find(c); + if (it == cellElemMap.end()) { + continue; + } else { + for (const auto& e : cellElemMap.at(c)) { + const auto& elementVertices = e->vertices; + auto it = std::find(elementVertices.begin(), elementVertices.end(), vertex1); + + if (it != elementVertices.end()) { + auto pos = std::distance(elementVertices.begin(), it); + auto n = elementVertices.size(); + + neighborsOfVertex1.insert(elementVertices[(pos + n - 1) % n]); + neighborsOfVertex1.insert(elementVertices[(pos + 1) % n]); + } + } } } for (const auto& c : cellsCoord2) { - for (const auto& e : cellElemMap.at(c)) { - const auto& elementVertices = e->vertices; - auto it = std::find(elementVertices.begin(), elementVertices.end(), vertex2); - - if (it != elementVertices.end()) { - auto pos = std::distance(elementVertices.begin(), it); - auto n = elementVertices.size(); - - neighborsOfVertex2.insert(elementVertices[(pos + n - 1) % n]); - neighborsOfVertex2.insert(elementVertices[(pos + 1) % n]); + auto it = cellElemMap.find(c); + if (it == cellElemMap.end()) { + continue; + } else { + for (const auto& e : cellElemMap.at(c)) { + const auto& elementVertices = e->vertices; + auto it = std::find(elementVertices.begin(), elementVertices.end(), vertex2); + + if (it != elementVertices.end()) { + auto pos = std::distance(elementVertices.begin(), it); + auto n = elementVertices.size(); + + neighborsOfVertex2.insert(elementVertices[(pos + n - 1) % n]); + neighborsOfVertex2.insert(elementVertices[(pos + 1) % n]); + } } } } @@ -136,6 +146,7 @@ Mesh Staircaser::getSelectiveMesh(const std::set& cellsToStructure, GapsFi fillerType_ = type; RelativePairSet boundaryCoordinatePairs; + std::vector> toRemove(mesh_.groups.size()); for (std::size_t g = 0; g < mesh_.groups.size(); ++g) { auto& inputGroup = inputMesh_.groups[g]; @@ -158,73 +169,22 @@ Mesh Staircaser::getSelectiveMesh(const std::set& cellsToStructure, GapsFi } } + meshGroup.elements.reserve(meshGroup.elements.size() + inputGroup.elements.size()); + std::map> cellElemMap_withNewElements; CoordinateMap coordinateMap = buildCoordinateMap(mesh_.coordinates); - meshGroup.elements.reserve(meshGroup.elements.size() + inputGroup.elements.size()); for (const auto& [cell, elements] : cellElemMap) { if (cellsToStructure.count(cell) ) { continue; } for (const auto e : elements) { - Element newElement; - newElement.type = e->type; - newElement.vertices.reserve(e->vertices.size()); - - Relatives boundaryCoordinates; - - for (const auto& vertexIndex : e->vertices) { - const auto& vertexCoord = inputMesh_.coordinates[vertexIndex]; - - bool isOnCellBoundary = false; - auto touchingCells = GridTools::getTouchingCells(vertexCoord); - - for (const auto& touchingCell : touchingCells) { - if (cellsToStructure.count(touchingCell)) { - isOnCellBoundary = true; - break; - } - } - - CoordinateId newIndex; - if (isOnCellBoundary) { - auto it = coordinateMap.find(toRelative(calculateStaircasedCell(vertexCoord))); - newIndex = it->second; - boundaryCoordinates.push_back(toRelative(calculateStaircasedCell(vertexCoord))); - } else { - auto it = coordinateMap.find(vertexCoord); - if (it == coordinateMap.end()) { - mesh_.coordinates.push_back(vertexCoord); - newIndex = int (mesh_.coordinates.size() - 1); - coordinateMap.emplace(vertexCoord, newIndex); - } else { - newIndex = it->second; - } - } - newElement.vertices.push_back(newIndex); - } - - bool isAllCoordinatesOnCellBoundary = true; - - for (const auto& vertex : newElement.vertices) { - const auto& vertexCoord = mesh_.coordinates[vertex]; - auto touchingCells = GridTools::getTouchingCells(vertexCoord); - - bool vertexOnBoundary = false; - for (const auto& touchingCell : touchingCells) { - if (cellsToStructure.count(touchingCell)) { - vertexOnBoundary = true; - break; - } - } + auto [newElement, boundaryCoordinates] = obtainNewIndexForElement(*e, cellsToStructure, coordinateMap); - if(!vertexOnBoundary) { - isAllCoordinatesOnCellBoundary = false; - break; - } - } + auto allCoordsOnBoundary = isAllCoordinatesOnTheSameCellBoundary(newElement, cellsToStructure); - if (!isAllCoordinatesOnCellBoundary) { + if (!allCoordsOnBoundary) { meshGroup.elements.push_back(newElement); + cellElemMap_withNewElements[cell].push_back(&meshGroup.elements.back()); for (size_t i = 0; i < boundaryCoordinates.size(); ++i) { for (size_t j = i + 1; j < boundaryCoordinates.size(); ++j) { @@ -233,11 +193,20 @@ Mesh Staircaser::getSelectiveMesh(const std::set& cellsToStructure, GapsFi } } } + + auto itCell = cellElemMap_withNewElements.find(cell); + if (itCell != cellElemMap_withNewElements.end()) { + + auto elementsConvertedInLines = getElementsConvertedInLines(cell, cellElemMap_withNewElements); + splitLinesWithNeighborTriangle(g, elementsConvertedInLines, meshGroup, cell, cellElemMap_withNewElements, toRemove); + + } } } - + RedundancyCleaner::fuseCoords(mesh_); RedundancyCleaner::removeDegenerateElements(mesh_); + RedundancyCleaner::removeRepeatedElements(mesh_); RedundancyCleaner::cleanCoords(mesh_); for (auto it = boundaryCoordinatePairs.begin(); it != boundaryCoordinatePairs.end();) { @@ -396,6 +365,220 @@ void Staircaser::fillGaps(const RelativePairSet boundaryCoordinatePairs) { RedundancyCleaner::removeDegenerateElements(mesh_); } +std::pair Staircaser::obtainNewIndexForElement(const Element& e, const std::set& cellsToStructure, CoordinateMap& coordinateMap) { + Element newElement; + newElement.type = e.type; + newElement.vertices.reserve(e.vertices.size()); + + Relatives boundaryCoordinates; + + for (const auto& vertexIndex : e.vertices) { + const auto& vertexCoord = inputMesh_.coordinates[vertexIndex]; + + bool isOnCellBoundary = false; + auto touchingCells = GridTools::getTouchingCells(vertexCoord); + + for (const auto& touchingCell : touchingCells) { + if (cellsToStructure.count(touchingCell)) { + isOnCellBoundary = true; + break; + } + } + + CoordinateId newIndex; + if (isOnCellBoundary) { + auto relCoord = toRelative(calculateStaircasedCell(vertexCoord)); + auto it = coordinateMap.find(relCoord); + + if (it == coordinateMap.end()) { + mesh_.coordinates.push_back(relCoord); + newIndex = int(mesh_.coordinates.size() - 1); + coordinateMap.emplace(relCoord, newIndex); + } else { + newIndex = it->second; + } + + boundaryCoordinates.push_back(relCoord); + } else { + auto it = coordinateMap.find(vertexCoord); + if (it == coordinateMap.end()) { + mesh_.coordinates.push_back(vertexCoord); + newIndex = int(mesh_.coordinates.size() - 1); + coordinateMap.emplace(vertexCoord, newIndex); + } else { + newIndex = it->second; + } + } + + newElement.vertices.push_back(newIndex); + } + + return {newElement, boundaryCoordinates}; +} + +bool Staircaser::isAllCoordinatesOnTheSameCellBoundary(const Element& newElement, const std::set& cellsToStructure) { + std::set commonStructuredCells; + bool firstVertex = true; + + for (const auto& vertex : newElement.vertices) { + const auto& vertexCoord = mesh_.coordinates[vertex]; + auto touchingCells = GridTools::getTouchingCells(vertexCoord); + + std::set structuredTouchingCells; + for (const auto& c : touchingCells) { + if (cellsToStructure.count(c)) { + structuredTouchingCells.insert(c); + } + } + + if (firstVertex) { + commonStructuredCells = std::move(structuredTouchingCells); + firstVertex = false; + } else { + std::set intersection; + std::set_intersection(commonStructuredCells.begin(), commonStructuredCells.end(), + structuredTouchingCells.begin(), structuredTouchingCells.end(), + std::inserter(intersection, intersection.begin())); + commonStructuredCells = std::move(intersection); + } + + if (commonStructuredCells.empty()) break; + } + + return !commonStructuredCells.empty(); +} + +std::set Staircaser::getElementsConvertedInLines(Cell cell, std::map> cellElemMap) { + std::set elementsConvertedInLines; + + for (const auto& e : cellElemMap.at(cell)) { + if (!e->vertices.empty()) { + bool allVerticesAreEqual = std::all_of( + e->vertices.begin() + 1, + e->vertices.end(), + [&](int v) { return v == e->vertices.front(); } + ); + + if (allVerticesAreEqual) { + continue; + } + } + + if (e->isTriangle()) { + const auto& firstVertexCoords = mesh_.coordinates[e->vertices[0]]; + const auto& secondVertexCoords = mesh_.coordinates[e->vertices[1]]; + const auto& thirdVertexCoords = mesh_.coordinates[e->vertices[2]]; + + int equalCoords = 0; + + for (int dim = 0; dim < 3; ++dim) { + if (firstVertexCoords[dim] == secondVertexCoords[dim] && + secondVertexCoords[dim] == thirdVertexCoords[dim]) { + ++equalCoords; + } + } + + bool areTwoVerticesEqual = + (firstVertexCoords == secondVertexCoords) || + (secondVertexCoords == thirdVertexCoords) || + (firstVertexCoords == thirdVertexCoords); + + if (equalCoords == 2 && !areTwoVerticesEqual) { + elementsConvertedInLines.insert(*e); + } + } + } + + return elementsConvertedInLines; +} + +void Staircaser::splitLinesWithNeighborTriangle(const size_t groupIndex, std::set elementsConvertedInLines, Group& meshGroup, const Cell cell, std::map> cellElemMap, std::vector> toRemove) { + for (const auto& e: elementsConvertedInLines) { + bool processed = false; + for (const auto& otherElementsInCell: cellElemMap.at(cell)) { + if (e == *otherElementsInCell) { + continue; + } + + IdSet sharedVertices; + int sharedCount = 0; + bool correctOrientation = false; + + for (int i = 0; i < otherElementsInCell->vertices.size(); ++i) { + int vOther = otherElementsInCell->vertices[i]; + + if (std::find(e.vertices.begin(), e.vertices.end(), vOther) != e.vertices.end()) { + sharedVertices.insert(vOther); + ++sharedCount; + } + + } + + bool shareExactlyTwo = (sharedCount == 2); + + if(shareExactlyTwo && otherElementsInCell->isTriangle()) { + + auto v1 = *sharedVertices.begin(); + auto v2 = *std::next(sharedVertices.begin()); + + auto itV1 = std::find(e.vertices.begin(), e.vertices.end(), v1); + auto itV2 = std::find(e.vertices.begin(), e.vertices.end(), v2); + correctOrientation = (itV1 < itV2); + + if(!correctOrientation) { + std::swap(v1, v2); + } + + CoordinateId v4 = -1; + for (const auto& v : e.vertices) { + if (v != v1 && v != v2) { + v4 = v; + break; + } + } + + CoordinateId v3 = -1; + for (const auto& v : otherElementsInCell->vertices) { + if (v != v1 && v != v2) { + v3 = v; + break; + } + } + + Element triangle1; + triangle1.type = Element::Type::Surface; + triangle1.vertices = { v3, v2, v4 }; + + Element triangle2; + triangle2.type = Element::Type::Surface; + triangle2.vertices = { v4, v1, v3 }; + + auto itOtherElement = std::find(meshGroup.elements.begin(), meshGroup.elements.end(), *otherElementsInCell); + if (itOtherElement != meshGroup.elements.end()) { + ElementId otherElementId = std::distance(meshGroup.elements.begin(), itOtherElement); + toRemove[groupIndex].insert(otherElementId); + } + + auto itElement = std::find(meshGroup.elements.begin(), meshGroup.elements.end(), e); + if (itElement != meshGroup.elements.end()) { + ElementId elementId = std::distance(meshGroup.elements.begin(), itElement); + toRemove[groupIndex].insert(elementId); + } + + meshGroup.elements.push_back(triangle1); + meshGroup.elements.push_back(triangle2); + + RedundancyCleaner::removeElements(mesh_, toRemove); + toRemove[groupIndex].clear(); + + processed = true; + break; + } + } + if(processed) {continue;} + } +} + void Staircaser::processTriangleAndAddToGroup(const Element& triangle, const Relatives& originalRelatives, Group& group){ Group edges; diff --git a/src/core/Staircaser.h b/src/core/Staircaser.h index da4c42e..a0a94e4 100644 --- a/src/core/Staircaser.h +++ b/src/core/Staircaser.h @@ -76,6 +76,10 @@ class Staircaser : public utils::GridTools { std::vector calculateMiddleCellsBetweenTwoRelatives(Relative& startExtreme, Relative& endExtreme); void calculateRelativeIdSetByCellSurface(const Relatives& relatives, std::map& relativesByCellSurface); void fillGaps(const RelativePairSet boundaryCoordinatePairs); + std::pair obtainNewIndexForElement(const Element& e, const std::set& cellsToStructure, CoordinateMap& coordinateMap); + bool isAllCoordinatesOnTheSameCellBoundary(const Element& newElement, const std::set& cellsToStructure); + std::set getElementsConvertedInLines(const Cell cell, std::map> cellElemMap); + void splitLinesWithNeighborTriangle(const size_t groupIndex, std::set elementsConvertedInLines, Group& meshGroup, const Cell cell, std::map> cellElemMap, std::vector> toRemove); }; diff --git a/src/meshers/ConformalMesher.cpp b/src/meshers/ConformalMesher.cpp index 6811c55..dca455c 100644 --- a/src/meshers/ConformalMesher.cpp +++ b/src/meshers/ConformalMesher.cpp @@ -5,10 +5,12 @@ #include "core/Collapser.h" #include "core/Smoother.h" #include "core/Snapper.h" +#include "core/Staircaser.h" #include "utils/GridTools.h" #include "utils/MeshTools.h" #include "utils/CoordGraph.h" +#include "utils/RedundancyCleaner.h" namespace meshlib::meshers { @@ -193,10 +195,18 @@ Mesh ConformalMesher::mesh() const log("Non-conformal cells found: " + std::to_string(nonConformalCells.size()), 1); // Calls structurer to mesh only those cells. - + log("Structuring non-conformal cells.", 1); + res = Staircaser{ res }.getSelectiveMesh(nonConformalCells,Staircaser::GapsFillingType::Insert); + RedundancyCleaner::removeOverlappedDimensionOneAndLowerElementsAndEquivalentSurfaces(res); + logNumberOfTriangles(countMeshElementsIf(res, isTriangle)); + + // Find cells which break conformal FDTD rules after selective structuring. + nonConformalCells = findNonConformalCells(res); + log("Non-conformal cells found after Selective Structuring: " + std::to_string(nonConformalCells.size()), 1); + + // Merges triangles which are on same cell face. - reduceGrid(res, originalGrid_); // Converts relatives to absolutes. diff --git a/test/app/launcherTest.cpp b/test/app/launcherTest.cpp index 9612a25..9abbf1b 100644 --- a/test/app/launcherTest.cpp +++ b/test/app/launcherTest.cpp @@ -24,6 +24,15 @@ TEST_F(LauncherTest, launches_alhambra_case) EXPECT_EQ(exitCode, EXIT_SUCCESS); } +TEST_F(LauncherTest, launches_conformal_alhambra_case) +{ + int ac = 3; + const char* av[] = { NULL, "-i", "testData/cases/alhambra/alhambra.conformal.tessellator.json"}; + int exitCode; + EXPECT_NO_THROW(exitCode = meshlib::app::launcher(ac, av)); + EXPECT_EQ(exitCode, EXIT_SUCCESS); +} + TEST_F(LauncherTest, launches_sphere_case) { @@ -34,6 +43,24 @@ TEST_F(LauncherTest, launches_sphere_case) EXPECT_EQ(exitCode, EXIT_SUCCESS); } +TEST_F(LauncherTest, launches_conformal_sphere_case) +{ + int ac = 3; + const char* av[] = { NULL, "-i", "testData/cases/sphere/sphere.conformal.tessellator.json"}; + int exitCode; + EXPECT_NO_THROW(exitCode = meshlib::app::launcher(ac, av)); + EXPECT_EQ(exitCode, EXIT_SUCCESS); +} + +TEST_F(LauncherTest, launches_conformal_thinCylinder_case) +{ + int ac = 3; + const char* av[] = { NULL, "-i", "testData/cases/thinCylinder/thinCylinder.conformal.tessellator.json"}; + int exitCode; + EXPECT_NO_THROW(exitCode = meshlib::app::launcher(ac, av)); + EXPECT_EQ(exitCode, EXIT_SUCCESS); +} + TEST_F(LauncherTest, launches_thinCylinder_case) { int ac = 3; @@ -59,4 +86,14 @@ TEST_F(LauncherTest, launches_long_polyline_case) int exitCode; EXPECT_NO_THROW(exitCode = meshlib::app::launcher(ac, av)); EXPECT_EQ(exitCode, EXIT_SUCCESS); -} \ No newline at end of file +} + +TEST_F(LauncherTest, launches_conformal_cone_case) +{ + int ac = 3; + const char* av[] = { NULL, "-i", "testData/cases/cone/cone.conformal.tessellator.json"}; + int exitCode; + EXPECT_NO_THROW(exitCode = meshlib::app::launcher(ac, av)); + EXPECT_EQ(exitCode, EXIT_SUCCESS); +} + diff --git a/test/core/StaircaserTest.cpp b/test/core/StaircaserTest.cpp index 8b9f94e..4975e5f 100644 --- a/test/core/StaircaserTest.cpp +++ b/test/core/StaircaserTest.cpp @@ -2741,3 +2741,105 @@ TEST_F(StaircaserTest, selectiveStructurerFillingGapsInFrontier_Insert) } } } + +TEST_F(StaircaserTest, selectiveStructurer_SplitLinesWithNeighborTriangle) +{ + // *----0========2=============5 *---(0->3)==(2->5)========(5->6) + // | ‾-_ ║|\ ║ | ‾-_ ║ \ ║ + // | ‾-_ ║ | \ ║ | ‾-_ ║ \ ║ + // | - ║ | \ ║ | - ║ \ ║ + // | ‾1 | \ ║ | (1->4) \ ║ + // | |‾- | \ ║ | ║‾‾--__ \ ║ + // | | ‾-_| \ ║ | ║ ‾‾--_\ ║ + // *-------------*-----3=======4 -> *-----------(3->0)========(4->1) + // | | \ | | | ║ + // | | \ | | | ║ + // | | \ | | | ║ + // | | \ | | | ║ + // | | \ | | | ║ + // | | \ | | | ║ + // *-------------*-------------6 *-------------*-----------(6->2) + + float lowerCoordinateValue = -5.0; + float upperCoordinateValue = 5.0; + int numberOfCells = 3; + float step = 5.0; + assert((upperCoordinateValue - lowerCoordinateValue) / (numberOfCells - 1) == step); + + std::set cellSet; + cellSet.insert(Cell({1, 0, 0})); + + Mesh mesh; + mesh.grid = GridTools::buildCartesianGrid(lowerCoordinateValue, upperCoordinateValue, numberOfCells); + mesh.coordinates = { + Relative({ 0.4, 2.0, 1.0 }), // 0 + Relative({ 1.0, 1.4, 1.0 }), // 1 + Relative({ 1.0, 2.0, 1.0 }), // 2 + Relative({ 1.4, 1.0, 1.0 }), // 3 + Relative({ 2.0, 1.0, 1.0 }), // 4 + Relative({ 2.0, 2.0, 1.0 }), // 5 + Relative({ 2.0, 0.0, 1.0 }), // 6 + }; + + mesh.groups.resize(1); + mesh.groups[0].elements = { + Element({0, 1, 2}, Element::Type::Surface), + Element({1, 3, 2}, Element::Type::Surface), + Element({3, 4, 2}, Element::Type::Surface), + Element({2, 4, 5}, Element::Type::Surface), + Element({3, 6, 4}, Element::Type::Surface), + }; + + Relatives expectedRelatives = { + Relative({ 1.0, 1.0, 1.0 }), // (3->0) + Relative({ 2.0, 1.0, 1.0 }), // (4->1) + Relative({ 2.0, 0.0, 1.0 }), // (6->2) + Relative({ 0.4, 2.0, 1.0 }), // (0->3) + Relative({ 1.0, 1.4, 1.0 }), // (1->4) + Relative({ 1.0, 2.0, 1.0 }), // (2->5) + Relative({ 2.0, 2.0, 1.0 }), // (5->6) + }; + + Elements expectedElements = { + Element({0, 1}, Element::Type::Line), + Element({1, 2}, Element::Type::Line), + Element({2, 1}, Element::Type::Line), + Element({1, 0}, Element::Type::Line), + Element({3, 4, 5}, Element::Type::Surface), + Element({5, 1, 6}, Element::Type::Surface), + Element({1, 5, 4}, Element::Type::Surface), + Element({4, 0, 1}, Element::Type::Surface), + }; + + auto resultMesh = Staircaser{ mesh }.getSelectiveMesh(cellSet); + + ASSERT_EQ(resultMesh.coordinates.size(), expectedRelatives.size()); + ASSERT_EQ(resultMesh.groups.size(), 1); + ASSERT_EQ(resultMesh.groups[0].elements.size(), expectedElements.size()); + + for (std::size_t i = 0; i < resultMesh.coordinates.size(); ++i) { + for (std::size_t axis = 0; axis < 3; ++axis) { + EXPECT_EQ(resultMesh.coordinates[i][axis], expectedRelatives[i][axis]); + } + } + + ASSERT_TRUE(resultMesh.groups[0].elements[0].isLine()); + ASSERT_TRUE(resultMesh.groups[0].elements[1].isLine()); + ASSERT_TRUE(resultMesh.groups[0].elements[2].isLine()); + ASSERT_TRUE(resultMesh.groups[0].elements[3].isLine()); + + ASSERT_TRUE(resultMesh.groups[0].elements[4].isTriangle()); + ASSERT_TRUE(resultMesh.groups[0].elements[5].isTriangle()); + ASSERT_TRUE(resultMesh.groups[0].elements[6].isTriangle()); + ASSERT_TRUE(resultMesh.groups[0].elements[7].isTriangle()); + + for (std::size_t e = 0; e < expectedElements.size(); ++e) { + auto& resultElement = resultMesh.groups[0].elements[e]; + auto& expectedElement = expectedElements[e]; + + for (std::size_t v = 0; v < expectedElement.vertices.size(); ++v) { + EXPECT_EQ(resultElement.vertices[v], expectedElement.vertices[v]); + } + } + +} diff --git a/testData/cases/alhambra/alhambra.conformal.tessellator.json b/testData/cases/alhambra/alhambra.conformal.tessellator.json new file mode 100644 index 0000000..f2b6c20 --- /dev/null +++ b/testData/cases/alhambra/alhambra.conformal.tessellator.json @@ -0,0 +1,17 @@ +{ + "grid": { + "numberOfCells": [60, 60, 10], + "boundingBox": [ + [-60.0, -60.0, -10.0], + [ 60.0, 60.0, 10.0] + ] + }, + "object": {"filename": "alhambra.stl"}, + "mesher": { + "type" : "conformal", + "options" : { + "edgePoints" : 6, + "forbiddenLength" : 0.15 + } + } +} \ No newline at end of file diff --git a/testData/cases/cone/cone.conformal.tessellator.json b/testData/cases/cone/cone.conformal.tessellator.json new file mode 100644 index 0000000..dac46e0 --- /dev/null +++ b/testData/cases/cone/cone.conformal.tessellator.json @@ -0,0 +1,17 @@ +{ + "grid": { + "numberOfCells": [40, 40, 120], + "boundingBox": [ + [-2, -2, -1], + [ 2, 2, 11] + ] + }, + "object": {"filename": "cone.stl"}, + "mesher": { + "type" : "conformal", + "options" : { + "edgePoints" : 4, + "forbiddenLength" : 0.25 + } + } +} \ No newline at end of file diff --git a/testData/cases/sphere/sphere.conformal.tessellator.json b/testData/cases/sphere/sphere.conformal.tessellator.json new file mode 100644 index 0000000..c32cc53 --- /dev/null +++ b/testData/cases/sphere/sphere.conformal.tessellator.json @@ -0,0 +1,17 @@ +{ + "grid": { + "numberOfCells": [30, 30, 30], + "boundingBox": [ + [-100.0, -100.0, -100.0], + [ 100.0, 100.0, 100.0] + ] + }, + "object": {"filename": "sphere.stl"}, + "mesher": { + "type" : "conformal", + "options" : { + "edgePoints" : 4, + "forbiddenLength" : 0.25 + } + } +} \ No newline at end of file diff --git a/testData/cases/thinCylinder/thinCylinder.conformal.tessellator.json b/testData/cases/thinCylinder/thinCylinder.conformal.tessellator.json new file mode 100644 index 0000000..d76cd2d --- /dev/null +++ b/testData/cases/thinCylinder/thinCylinder.conformal.tessellator.json @@ -0,0 +1,17 @@ +{ + "grid": { + "numberOfCells": [20, 20, 30], + "boundingBox": [ + [-1, -1, -1], + [ 1, 1, 2] + ] + }, + "object": {"filename": "thinCylinder.stl"}, + "mesher": { + "type" : "conformal", + "options" : { + "edgePoints" : 4, + "forbiddenLength" : 0.25 + } + } +} \ No newline at end of file diff --git a/tmp/sphere.tessellator.json b/tmp/sphere.tessellator.json new file mode 100644 index 0000000..8601a88 --- /dev/null +++ b/tmp/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"} +} \ No newline at end of file