diff --git a/src/SIM/ModelGenerator.C b/src/SIM/ModelGenerator.C new file mode 100644 index 00000000..e6257737 --- /dev/null +++ b/src/SIM/ModelGenerator.C @@ -0,0 +1,300 @@ +// $Id$ +//============================================================================== +//! +//! \file ModelGenerator.C +//! +//! \date Sep 2 2016 +//! +//! \author Arne Morten Kvarving / SINTEF +//! +//! \brief Base class for model generators for NURBS-based FEM simulators. +//! +//============================================================================== + +#include "ModelGenerator.h" +#include "IFEM.h" +#include "SIMbase.h" +#include "Utilities.h" +#include "Vec3.h" +#include "Vec3Oper.h" +#include "tinyxml.h" + + +ModelGenerator::ModelGenerator (const TiXmlElement* elem) : + sets(false), geo(elem) +{ + utl::getAttribute(geo, "sets", sets); +} + + +std::string DefaultGeometry1D::createG2 (int nsd) const +{ + std::string g2("100 1 0 0\n"); + g2.append(1,'0'+nsd); + + bool rational=false; + utl::getAttribute(geo,"rational",rational); + if (rational) + IFEM::cout << "\t Rational basis\n"; + g2.append(rational?" 1":" 0"); + g2.append("\n2 2\n0 0 1 1\n"); + + unsigned char d; + std::string XYZ; + if (utl::getAttribute(geo,"X0",XYZ)) + { + IFEM::cout <<" X0 = "<< XYZ << std::endl; + g2.append(XYZ); + } + else + { + g2.append("0.0"); + for (d = 1; d < nsd; d++) + g2.append(" 0.0"); + } + if (rational) + g2.append(" 1.0"); + g2.append("\n"); + if (utl::getAttribute(geo,"X1",XYZ)) + { + IFEM::cout <<"\tX1 = "<< XYZ << std::endl; + g2.append(XYZ); + } + else + { + XYZ = "1.0"; + if (utl::getAttribute(geo,"L",XYZ)) + IFEM::cout <<" Length scale: "<< XYZ << std::endl; + g2.append(XYZ); + for (d = 1; d < nsd; d++) + g2.append(" 0.0"); + } + if (rational) + g2.append(" 1.0"); + g2.append("\n"); + + return g2; +} + + +SIMdependency::PatchVec DefaultGeometry1D::createGeometry (const SIMbase& sim) const +{ + std::istringstream unitLine(this->createG2(sim.getNoSpaceDim())); + SIMdependency::PatchVec result; + sim.readPatches(unitLine,result,"\t"); + return result; +} + + +TopologySet DefaultGeometry1D::createTopologySets (const SIMbase&) const +{ + if (!sets) + return TopologySet(); + + TopologySet result; + result["Vertex1"].insert(TopItem(1,1,0)); + result["Vertex2"].insert(TopItem(1,2,0)); + result["Boundary"].insert(TopItem(1,1,0)); + result["Boundary"].insert(TopItem(1,2,0)); + result["Corners"].insert(TopItem(1,1,0)); + result["Corners"].insert(TopItem(1,2,0)); + + return result; +} + + +std::string DefaultGeometry2D::createG2 (int nsd) const +{ + std::string g2("200 1 0 0\n"); + g2.append(nsd > 2 ? "3" : "2"); + bool rational=false; + utl::getAttribute(geo,"rational",rational); + if (rational) + IFEM::cout << "\t Rational basis\n"; + g2.append(rational?" 1":" 0"); + g2.append("\n2 2\n0 0 1 1\n2 2\n0 0 1 1"); + + Vec3 X0; + std::string corner; + if (utl::getAttribute(geo,"X0",corner)) { + std::stringstream str(corner); str >> X0; + IFEM::cout <<" Corner: "<< X0 << std::endl; + } + + double scale = 1.0; + if (utl::getAttribute(geo,"scale",scale)) + IFEM::cout <<" Scale: "<< scale << std::endl; + + double Lx = 1.0, Ly = 1.0; + if (utl::getAttribute(geo,"Lx",Lx)) + IFEM::cout <<" Length in X: "<< Lx << std::endl; + Lx *= scale; + if (utl::getAttribute(geo,"Ly",Ly)) + IFEM::cout <<" Length in Y: "<< Ly << std::endl; + Ly *= scale; + + std::stringstream str; + str <<"\n"<< X0.x <<" "<< X0.y; + if (nsd > 2) str <<" 0.0"; + if (rational) str << " 1.0"; + g2.append(str.str()); + str.str(""); + str <<"\n"<< X0.x+Lx <<" "<< X0.y; + if (nsd > 2) str <<" 0.0"; + if (rational) str << " 1.0"; + g2.append(str.str()); + str.str(""); + str <<"\n"<< X0.x <<" "<< X0.y+Ly; + if (nsd > 2) str <<" 0.0"; + if (rational) str << " 1.0"; + g2.append(str.str()); + str.str(""); + str <<"\n"<< X0.x+Lx <<" "<< X0.y+Ly; + if (nsd > 2) str <<" 0.0"; + if (rational) str << " 1.0"; + g2.append(str.str()); + g2.append("\n"); + + return g2; +} + + +SIMdependency::PatchVec DefaultGeometry2D::createGeometry (const SIMbase& sim) const +{ + std::istringstream unitSquare(this->createG2(sim.getNoSpaceDim())); + SIMdependency::PatchVec result; + sim.readPatches(unitSquare,result,"\t"); + return result; +} + + +TopologySet DefaultGeometry2D::createTopologySets (const SIMbase&) const +{ + if (!sets) + return TopologySet(); + + TopologySet result; + std::string vert = "Vertex1"; + std::string edge = "Edge1"; + for (size_t i = 1; i <= 4; ++i, ++vert.back(), ++edge.back()) { + result[vert].insert(TopItem(1,i,0)); + result[edge].insert(TopItem(1,i,1)); + result["Corners"].insert(TopItem(1,i,0)); + result["Boundary"].insert(TopItem(1,i,1)); + } + + return result; +} + + +std::string DefaultGeometry3D::createG2 (int) const +{ + std::string g2("700 1 0 0\n3 "); + + bool rational = false; + utl::getAttribute(geo,"rational",rational); + if (rational) + IFEM::cout <<" Rational basis"<< std::endl; + + g2.append(rational ? "1\n" : "0\n"); + g2.append("2 2\n0 0 1 1\n" + "2 2\n0 0 1 1\n" + "2 2\n0 0 1 1\n"); + + std::array nodes = + {{ 0.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 1.0, 1.0, 0.0, + 0.0, 0.0, 1.0, + 1.0, 0.0, 1.0, + 0.0, 1.0, 1.0, + 1.0, 1.0, 1.0 }}; + + double scale = 1.0; + if (utl::getAttribute(geo,"scale",scale)) + IFEM::cout <<"\tscale = "<< scale << std::endl; + + double Lx = 1.0, Ly = 1.0, Lz = 1.0; + if (utl::getAttribute(geo,"Lx",Lx)) + IFEM::cout <<"\tLength in X: "<< Lx << std::endl; + Lx *= scale; + if (utl::getAttribute(geo,"Ly",Ly)) + IFEM::cout <<"\tLength in Y: "<< Ly << std::endl; + Ly *= scale; + if (utl::getAttribute(geo,"Lz",Lz)) + IFEM::cout <<"\tLength in Z: "<< Lz << std::endl; + Lz *= scale; + + if (Lx != 1.0) + nodes[3] = nodes[9] = nodes[15] = nodes[21] = Lx; + if (Ly != 1.0) + nodes[7] = nodes[10] = nodes[19] = nodes[22] = Ly; + if (Lz != 1.0) + nodes[14] = nodes[17] = nodes[20] = nodes[23] = Lz; + + std::string corner; + if (utl::getAttribute(geo,"X0",corner)) { + std::stringstream str(corner); + Vec3 X0; + str >> X0; + IFEM::cout <<"\tCorner: "<< X0 << std::endl; + for (size_t i = 0; i < nodes.size(); i += 3) + { + nodes[i] += X0.x; + nodes[i+1] += X0.y; + nodes[i+2] += X0.z; + } + } + + for (size_t i = 0; i < nodes.size(); i += 3) + { + std::stringstream str; + for (size_t j = 0; j < 3; j++) + str << nodes[i+j] <<" "; + g2.append(str.str()); + g2.append(rational ? "1.0\n" : "\n"); + } + + return g2; +} + + +SIMdependency::PatchVec DefaultGeometry3D::createGeometry (const SIMbase& sim) const +{ + std::istringstream unitCube(this->createG2()); + SIMdependency::PatchVec result; + sim.readPatches(unitCube,result,"\t"); + return result; +} + + +TopologySet DefaultGeometry3D::createTopologySets (const SIMbase&) const +{ + if (!sets) + return TopologySet(); + + TopologySet result; + + std::string face = "Face1"; + for (size_t i = 1; i <= 6; ++i, ++face.back()) { + result[face].insert(TopItem(1,i,2)); + result["Boundary"].insert(TopItem(1,i,2)); + } + + std::string edge = "Edge1"; + for (size_t i = 1; i <= 12; ++i, ++edge.back()) { + result[edge].insert(TopItem(1,i,1)); + result["Frame"].insert(TopItem(1,i,1)); + if (i == 9) + edge = "Edge1/"; // '/' + 1 == '0' + } + + std::string vert = "Vertex1"; + for (size_t i = 1; i <= 8; ++i, ++vert.back()) { + result[vert].insert(TopItem(1,i,0)); + result["Corners"].insert(TopItem(1,i,0)); + } + + return result; +} diff --git a/src/SIM/ModelGenerator.h b/src/SIM/ModelGenerator.h new file mode 100644 index 00000000..ea6d5c2d --- /dev/null +++ b/src/SIM/ModelGenerator.h @@ -0,0 +1,147 @@ +// $Id$ +//============================================================================== +//! +//! \file ModelGenerator.h +//! +//! \date Sep 2 2016 +//! +//! \author Arne Morten Kvarving / SINTEF +//! +//! \brief Base class for model generators for NURBS-based FEM simulators. +//! +//============================================================================== + +#ifndef _MODEL_GENERATOR_H +#define _MODEL_GENERATOR_H + +#include "SIMdependency.h" +#include "TopologySet.h" +#include + +class SIMbase; +class TiXmlElement; + +/*! + \brief Base class for model generators for FEM simulators. +*/ + +class ModelGenerator +{ +public: + //! \brief Constructor initializes common members + //!\ param elem XML element to parse + ModelGenerator(const TiXmlElement* elem); + + //! \brief Empty destructor. + virtual ~ModelGenerator() {} + + //! \brief Creates a geometry. + //! \param[in] sim SIM with patch read function to use + virtual SIMdependency::PatchVec createGeometry(const SIMbase& sim) const = 0; + + //! \brief Creates topology for geometry. + //! \param[in] geo XML element containing geometry defintion + //! \param sim Simulator to apply topology to + virtual bool createTopology(SIMbase& sim) const = 0; + + //! \brief Creates topology sets for geometry. + //! \param[in] sim Simulator with patch ownerships + virtual TopologySet createTopologySets(const SIMbase& sim) const = 0; + +protected: + bool sets; //!< Whether to generate topologysets or not + const TiXmlElement* geo; //!< Pointer to xml element describing geometry +}; + + +/*! + \brief Default model generator for 1D FEM simulators. + \details Generates a line. +*/ + +class DefaultGeometry1D : public ModelGenerator { +public: + //! \brief The constructor forwards to the base class. + //! \param[in] geo XML element containing geometry defintion + DefaultGeometry1D(const TiXmlElement* geo) : ModelGenerator(geo) {} + + //! \brief Creates a 1D single-patch geometry. + //! \param[in] sim SIM with patch read function to use + SIMdependency::PatchVec createGeometry(const SIMbase& sim) const override; + + //! \brief Creates the topology + //! \details No topology information for single patch models + bool createTopology(SIMbase&) const override + { return true; } + + //! \brief Creates topology sets for geometry. + TopologySet createTopologySets(const SIMbase&) const override; + +protected: + //! \brief Generates the G2 description of the geometry. + //! \param nsd Number of spatial dimension + std::string createG2 (int nsd = 2) const; +}; + + +/*! + \brief Default model generator for 2D FEM simulators. + \details Generates a rectangle. +*/ + +class DefaultGeometry2D : public ModelGenerator { +public: + //! \brief The constructor forwards to the base class. + //! \param[in] geo XML element containing geometry defintion + DefaultGeometry2D(const TiXmlElement* geo) : ModelGenerator(geo) {} + + //! \brief Creates a 2D rectangular single-patch geometry. + //! \param[in] sim SIM with patch read function to use + SIMdependency::PatchVec createGeometry(const SIMbase& sim) const override; + + //! \brief Creates the topology + //! \param sim Simulator to apply topology to + //! \details No topology information for single patch models + bool createTopology(SIMbase&) const override + { return true; } + + //! \brief Creates topology sets for geometry. + TopologySet createTopologySets(const SIMbase&) const override; + +protected: + //! \brief Generates the G2 description of the geometry. + //! \param nsd Number of spatial dimension + std::string createG2 (int nsd = 3) const; +}; + + +/*! + \brief Default model generator for 3D FEM simulators. + \details Generates a hexahedra. +*/ + +class DefaultGeometry3D : public ModelGenerator { +public: + //! \brief The constructor forwards to the base class. + //! \param[in] geo XML element containing geometry defintion + DefaultGeometry3D(const TiXmlElement* geo) : ModelGenerator(geo) {} + + //! \brief Creates a 3D hexahedral single-patch geometry. + //! \param[in] sim Simulator with patch read function to use + SIMdependency::PatchVec createGeometry(const SIMbase& sim) const override; + + //! \brief Creates the topology + //! \param sim Simulator to apply topology to + //! \details No topology information for single patch models + bool createTopology(SIMbase&) const override + { return true; } + + //! \brief Creates topology sets for geometry. + TopologySet createTopologySets(const SIMbase&) const override; + +protected: + //! \brief Generates the G2 description of the geometry. + std::string createG2 (int = 3) const; +}; + +#endif diff --git a/src/SIM/Test/TestModelGenerator.C b/src/SIM/Test/TestModelGenerator.C new file mode 100644 index 00000000..1c756717 --- /dev/null +++ b/src/SIM/Test/TestModelGenerator.C @@ -0,0 +1,468 @@ +//============================================================================== +//! +//! \file TestModelGenerator.C +//! +//! \date Sep 2 2016 +//! +//! \author Arne Morten Kvarving / SINTEF +//! +//! \brief Tests for default model generators. +//! +//============================================================================== + +#include "IFEM.h" +#include "ModelGenerator.h" +#include "SIM1D.h" +#include "SIM2D.h" +#include "SIM3D.h" +#include "TopologySet.h" + +#include "gtest/gtest.h" +#include "tinyxml.h" + + +template +class TestModelGeneratorWrapper : public Generator { +public: + TestModelGeneratorWrapper(const TiXmlElement* geo) : Generator(geo) {} + std::string createG2(int nsd) + { + return Generator::createG2(nsd); + } +}; + +struct DefaultGeomTest { + std::string xml; + int dim; + std::string g2; + std::string sets; +}; + + +class TestModelGenerator1D : + public testing::Test, + public testing::WithParamInterface +{ +}; + + +class TestModelGenerator2D : + public testing::Test, + public testing::WithParamInterface +{ +}; + + +class TestModelGenerator3D : + public testing::Test, + public testing::WithParamInterface +{ +}; + + +auto&& DoTest = [](const DefaultGeomTest& ref, const std::string& gen, + const TopologySet& sets) +{ + ASSERT_STREQ(gen.c_str(), ref.g2.c_str()); + + if (!ref.sets.empty()) { + std::string gsets; + for (auto& it : sets) { + gsets += it.first + ": "; + for (auto& it2 : it.second) { + std::stringstream str; + str << it2.patch << " " << it2.item << " " << it2.idim << " "; + gsets += str.str(); + } + gsets += "\n"; + } + ASSERT_STREQ(gsets.c_str(), ref.sets.c_str()); + } +}; + + +TEST_P(TestModelGenerator1D, Generate) +{ + TiXmlDocument doc; + doc.Parse(GetParam().xml.c_str()); + TestModelGeneratorWrapper gen(doc.RootElement()); + std::string g2 = gen.createG2(GetParam().dim); + SIM1D sim; + TopologySet sets = gen.createTopologySets(sim); + DoTest(GetParam(), g2, sets); +} + + +TEST_P(TestModelGenerator2D, Generate) +{ + TiXmlDocument doc; + doc.Parse(GetParam().xml.c_str()); + TestModelGeneratorWrapper gen(doc.RootElement()); + std::string g2 = gen.createG2(GetParam().dim); + SIM2D sim; + TopologySet sets = gen.createTopologySets(sim); + DoTest(GetParam(), g2, sets); +} + + +TEST_P(TestModelGenerator3D, Generate) +{ + TiXmlDocument doc; + doc.Parse(GetParam().xml.c_str()); + TestModelGeneratorWrapper gen(doc.RootElement()); + std::string g2 = gen.createG2(GetParam().dim); + SIM3D sim; + TopologySet sets = gen.createTopologySets(sim); + DoTest(GetParam(), g2, sets); +} + + +const std::vector geometry1D = + {{"", 1, + "100 1 0 0\n" + "1 0\n" + "2 2\n" + "0 0 1 1\n" + "0.0\n" + "1.0\n", + "Boundary: 1 1 0 1 2 0 \n" + "Corners: 1 1 0 1 2 0 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 1 2 0 \n"}, + + {"", 3, + "100 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "0.0 0.0 0.0\n" + "1.0 0.0 0.0\n", + ""}, + + {"", 3, + "100 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "1.0 1.0 0.0\n" + "1.0 2.0 0.0\n", + ""}, + + {"", 1, + "100 1 0 0\n" + "1 0\n" + "2 2\n" + "0 0 1 1\n" + "0.0\n" + "2.0\n", + ""}, + + {"", 1, + "100 1 0 0\n" + "1 1\n" + "2 2\n" + "0 0 1 1\n" + "0.0 1.0\n" + "1.0 1.0\n", + "Boundary: 1 1 0 1 2 0 \n" + "Corners: 1 1 0 1 2 0 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 1 2 0 \n"}}; + + +INSTANTIATE_TEST_CASE_P(TestModelGenerator1D, TestModelGenerator1D, testing::ValuesIn(geometry1D)); + + +const std::vector geometry2D = + {{"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0\n" + "1 0\n" + "0 1\n" + "1 1\n", + "Boundary: 1 1 1 1 2 1 1 3 1 1 4 1 \n" + "Corners: 1 1 0 1 2 0 1 3 0 1 4 0 \n" + "Edge1: 1 1 1 \n" + "Edge2: 1 2 1 \n" + "Edge3: 1 3 1 \n" + "Edge4: 1 4 1 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 1 2 0 \n" + "Vertex3: 1 3 0 \n" + "Vertex4: 1 4 0 \n"}, + + {"", 2, + "200 1 0 0\n" + "2 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 1.0\n" + "1 0 1.0\n" + "0 1 1.0\n" + "1 1 1.0\n", ""}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0\n" + "2 0\n" + "0 2\n" + "2 2\n", ""}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 0\n" + "3 0\n" + "2 1\n" + "3 1\n"}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 2\n" + "1 2\n" + "0 3\n" + "1 3\n", ""}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0\n" + "2 0\n" + "0 1\n" + "2 1\n", ""}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0\n" + "1 0\n" + "0 2\n" + "1 2\n", ""}}; + + +INSTANTIATE_TEST_CASE_P(TestModelGenerator2D, TestModelGenerator2D, testing::ValuesIn(geometry2D)); + + +const std::vector geometry3D = + {{"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0 \n" + "1 0 0 \n" + "0 1 0 \n" + "1 1 0 \n" + "0 0 1 \n" + "1 0 1 \n" + "0 1 1 \n" + "1 1 1 \n", + "Boundary: 1 1 2 1 2 2 1 3 2 1 4 2 1 5 2 1 6 2 \n" + "Corners: 1 1 0 1 2 0 1 3 0 1 4 0 1 5 0 1 6 0 1 7 0 1 8 0 \n" + "Edge1: 1 1 1 \n" + "Edge10: 1 10 1 \n" + "Edge11: 1 11 1 \n" + "Edge12: 1 12 1 \n" + "Edge2: 1 2 1 \n" + "Edge3: 1 3 1 \n" + "Edge4: 1 4 1 \n" + "Edge5: 1 5 1 \n" + "Edge6: 1 6 1 \n" + "Edge7: 1 7 1 \n" + "Edge8: 1 8 1 \n" + "Edge9: 1 9 1 \n" + "Face1: 1 1 2 \n" + "Face2: 1 2 2 \n" + "Face3: 1 3 2 \n" + "Face4: 1 4 2 \n" + "Face5: 1 5 2 \n" + "Face6: 1 6 2 \n" + "Frame: 1 1 1 1 2 1 1 3 1 1 4 1 1 5 1 1 6 1 1 7 1 1 8 1 1 9 1 1 10 1 1 11 1 1 12 1 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 1 2 0 \n" + "Vertex3: 1 3 0 \n" + "Vertex4: 1 4 0 \n" + "Vertex5: 1 5 0 \n" + "Vertex6: 1 6 0 \n" + "Vertex7: 1 7 0 \n" + "Vertex8: 1 8 0 \n"}, + + {"", 3, + "700 1 0 0\n" + "3 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0 1.0\n" + "1 0 0 1.0\n" + "0 1 0 1.0\n" + "1 1 0 1.0\n" + "0 0 1 1.0\n" + "1 0 1 1.0\n" + "0 1 1 1.0\n" + "1 1 1 1.0\n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0 \n" + "2 0 0 \n" + "0 2 0 \n" + "2 2 0 \n" + "0 0 2 \n" + "2 0 2 \n" + "0 2 2 \n" + "2 2 2 \n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 0 0 \n" + "3 0 0 \n" + "2 1 0 \n" + "3 1 0 \n" + "2 0 1 \n" + "3 0 1 \n" + "2 1 1 \n" + "3 1 1 \n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 2 0 \n" + "1 2 0 \n" + "0 3 0 \n" + "1 3 0 \n" + "0 2 1 \n" + "1 2 1 \n" + "0 3 1 \n" + "1 3 1 \n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 2 \n" + "1 0 2 \n" + "0 1 2 \n" + "1 1 2 \n" + "0 0 3 \n" + "1 0 3 \n" + "0 1 3 \n" + "1 1 3 \n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0 \n" + "2 0 0 \n" + "0 1 0 \n" + "2 1 0 \n" + "0 0 1 \n" + "2 0 1 \n" + "0 1 1 \n" + "2 1 1 \n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0 \n" + "1 0 0 \n" + "0 2 0 \n" + "1 2 0 \n" + "0 0 1 \n" + "1 0 1 \n" + "0 2 1 \n" + "1 2 1 \n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0 \n" + "1 0 0 \n" + "0 1 0 \n" + "1 1 0 \n" + "0 0 2 \n" + "1 0 2 \n" + "0 1 2 \n" + "1 1 2 \n", ""}}; + + +INSTANTIATE_TEST_CASE_P(TestModelGenerator3D, TestModelGenerator3D, testing::ValuesIn(geometry3D));