/******************************************************** * ██████╗ ██████╗████████╗██╗ * ██╔════╝ ██╔════╝╚══██╔══╝██║ * ██║ ███╗██║ ██║ ██║ * ██║ ██║██║ ██║ ██║ * ╚██████╔╝╚██████╗ ██║ ███████╗ * ╚═════╝ ╚═════╝ ╚═╝ ╚══════╝ * Geophysical Computational Tools & Library (GCTL) * * Copyright (c) 2023 Yi Zhang (yizhang-geo@zju.edu.cn) * * GCTL is distributed under a dual licensing scheme. You can redistribute * it and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either version 2 * of the License, or (at your option) any later version. You should have * received a copy of the GNU Lesser General Public License along with this * program. If not, see . * * If the terms and conditions of the LGPL v.2. would prevent you from using * the GCTL, please consider the option to obtain a commercial license for a * fee. These licenses are offered by the GCTL's original author. As a rule, * licenses are provided "as-is", unlimited in time for a one time fee. Please * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget * to include some description of your company and the realm of its activities. * Also add information on how to contact you by electronic and paper mail. ******************************************************/ #ifndef _GCTL_TETGEN_IO_H #define _GCTL_TETGEN_IO_H #include "../core/matrix.h" #include "../poly/triangle.h" #include "../poly/tetrahedron.h" #include "../poly/edge.h" #include "file_io.h" namespace gctl { /** * @brief Read Tetrahedron's .node file into a 3D vertex array. * * First line: <# of points> <# of attributes> * Remaining lines list # of points: * [attributes] [boundary marker] * All tetgen files are of ASCII form and may contain comments prefixed by the * character ’#’. Points, tetrahedra, facets, edges, holes, and maximum volume constraints must * be numbered consecutively, starting from either 1 or 0. However, all input files must be * consistent. TetGen automatically detects your choice while reading the .node * (or .poly or .smesh) file. When calling TetGen from another program, * use the -z switch if you wish to number objects from zero. * * @param[in] out_node Output 3D vertex array. Returned by quote. * @param filename Input filename without any extensions. * @param[in] packed Indicates whether the starting index of vertice is zero or not. The * vertex's ordering is assumed to be start with one if this option is set to false. * The default value is true. * @param[in] bound_tag Pointer of the boundary marker array. * @param[in] attri Pointer of the attributes array. */ template void read_Tetgen_node(std::string filename, array> &out_node, index_packed_e packed = Packed, array *bound_tag = nullptr, _2d_matrix *attri = nullptr) { std::ifstream tetin; open_infile(tetin, filename, ".node"); std::string err_str, tmp_str; std::stringstream tmp_ss; int node_size = 0, node_dimen = 3, attri_num = 0, boundary_mark = 0; // read head info do { std::getline(tetin, tmp_str); if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> node_size >> node_dimen >> attri_num >> boundary_mark; if (tmp_ss.fail() || node_dimen != 3 || (boundary_mark != 0 && boundary_mark != 1) || attri_num < 0) { err_str = "Wrong head-info found in " + filename + ".node. From void gctl::read_Tetgen_node(...)"; throw runtime_error(err_str); } } } while (tmp_str[0] == '#'); if (attri_num != 0 && attri != nullptr) { attri->resize(node_size, attri_num); } else if (attri_num == 0 && attri != nullptr) { err_str = "No attributes found in " + filename + ".node. From void gctl::read_Tetgen_node(...)"; throw runtime_error(err_str); } if (boundary_mark != 0 && bound_tag != nullptr) { bound_tag->resize(node_size); } else if (boundary_mark == 0 && bound_tag != nullptr) { err_str = "No boundary marks found in " + filename + ".node. From void gctl::read_Tetgen_node(...)"; throw runtime_error(err_str); } vertex tmp_vert; out_node.resize(node_size); if (boundary_mark+attri_num) { array attri_line(boundary_mark+attri_num); _2d_matrix tmp_attri(node_size, boundary_mark+attri_num); while (std::getline(tetin, tmp_str)) { if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> tmp_vert.id >> tmp_vert.x >> tmp_vert.y >> tmp_vert.z; for (int a = 0; a < boundary_mark+attri_num; a++) tmp_ss >> attri_line[a]; if (tmp_ss.fail()) { err_str = "Wrong file format found in " + filename + ".node. From void gctl::read_Tetgen_node(...)"; throw runtime_error(err_str); } if (packed == NotPacked) tmp_vert.id -= 1; out_node[tmp_vert.id] = tmp_vert; for (int a = 0; a < boundary_mark+attri_num; a++) tmp_attri[tmp_vert.id][a] = attri_line[a]; } } if (attri != nullptr) { for (int i = 0; i < node_size; i++) { for (int a = 0; a < attri_num; a++) { attri->at(i, a) = tmp_attri[i][a]; } } } if (bound_tag != nullptr) { for (int i = 0; i < node_size; i++) { bound_tag->at(i) = (int) tmp_attri[i][attri_num]; } } } else { while (std::getline(tetin, tmp_str)) { if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> tmp_vert.id >> tmp_vert.x >> tmp_vert.y >> tmp_vert.z; if (tmp_ss.fail()) { err_str = "Wrong file format found in " + filename + ".node. From void gctl::read_Tetgen_node(...)"; throw runtime_error(err_str); } if (packed == NotPacked) tmp_vert.id -= 1; out_node[tmp_vert.id] = tmp_vert; } } } tetin.close(); return; } /** * @brief Read Tetgen's .ele file into a 3D tetrahedron array. * * First line: <# of tetrahedra> * Remaining lines list # of tetrahedra: * ... [attribute] * * @param out_tri Output 3D tetrahedron array. Returned by quote. * @param[in] filename Input filename without any extensions. * @param[in] in_node 3D vertex array which contains vertex's information of the tetrahedrons. * @param[in] packed Indicates whether the starting index of vertice is zero or not. The * vertex's ordering is assumed to be start with one if this option is set to false. * The default value is true. * @param[in] region_attri If the in the first line is 1, * each tetrahedra has an additional region attribute (an integer) in the last column. */ template void read_Tetgen_element(std::string filename, array> &out_tet, const array> &in_node, index_packed_e packed = Packed, array *region_attri = nullptr) { std::ifstream tetin; open_infile(tetin, filename, ".ele"); std::string err_str, tmp_str; std::stringstream tmp_ss; int ele_size = 0, node_per_ele = 4, region_mark = 0; // read head info do { std::getline(tetin, tmp_str); if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> ele_size >> node_per_ele >> region_mark; if (tmp_ss.fail() || node_per_ele != 4 || (region_mark != 0 && region_mark != 1)) { err_str = "Wrong head-info found in " + filename + ".ele. From void gctl::read_Tetgen_element(...)"; throw runtime_error(err_str); } } } while (tmp_str[0] == '#'); if (region_mark != 0 && region_attri != nullptr) { region_attri->resize(ele_size); } else if (region_mark == 0 && region_attri != nullptr) { err_str = "No attributes found in " + filename + ".ele. From void gctl::read_Tetgen_element(...)"; throw runtime_error(err_str); } int tmp_int, tmp_order[4], tmp_region; out_tet.resize(ele_size); if (region_mark) { while (std::getline(tetin, tmp_str)) { if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> tmp_int >> tmp_order[0] >> tmp_order[1] >> tmp_order[2] >> tmp_order[3] >> tmp_region; if (tmp_ss.fail()) { err_str = "Wrong file format found in " + filename + ".ele. From void gctl::read_Tetgen_element(...)"; throw runtime_error(err_str); } if (packed == NotPacked) { tmp_int -= 1; out_tet[tmp_int].id = tmp_int; for (int j = 0; j < 4; j++) { out_tet[tmp_int].vert[j] = in_node.get(tmp_order[j]-1); } out_tet[tmp_int].deter_vert_order(); if (region_attri != nullptr) { region_attri->at(tmp_int) = tmp_region; } } else { out_tet[tmp_int].id = tmp_int; for (int j = 0; j < 4; j++) { out_tet[tmp_int].vert[j] = in_node.get(tmp_order[j]); } out_tet[tmp_int].deter_vert_order(); if (region_attri != nullptr) { region_attri->at(tmp_int) = tmp_region; } } } } } else { while (std::getline(tetin, tmp_str)) { if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> tmp_int >> tmp_order[0] >> tmp_order[1] >> tmp_order[2] >> tmp_order[3]; if (tmp_ss.fail()) { err_str = "Wrong file format found in " + filename + ".ele. From void gctl::read_Tetgen_element(...)"; throw runtime_error(err_str); } if (packed == NotPacked) { tmp_int -= 1; out_tet[tmp_int].id = tmp_int; for (int j = 0; j < 4; j++) { out_tet[tmp_int].vert[j] = in_node.get(tmp_order[j]-1); } out_tet[tmp_int].deter_vert_order(); } else { out_tet[tmp_int].id = tmp_int; for (int j = 0; j < 4; j++) { out_tet[tmp_int].vert[j] = in_node.get(tmp_order[j]); } out_tet[tmp_int].deter_vert_order(); } } } } tetin.close(); return; } /** * @brief Read neighbor file generated by the Tetgen program. * * First line: <# of tetrahedra> 4 * Following lines list # of neighbors: * * An index of −1 indicates no neighbor (because the tetrahedron is * on boundary of a mesh domain). * * @param out_tet The output tetrahedron array. * @param[in] filename The filename of a .neigh file generated by the Tetgen program. * @param[in] packed Indicates whether the index in the neighbor file starts from zero. The * index is deemed to be started with one if this option is false. The default value of this * variable is true. */ template void read_Tetgen_neighbor(std::string filename, array> &out_tet, index_packed_e packed = Packed) { std::ifstream tetin; open_infile(tetin, filename, ".neigh"); std::string err_str, tmp_str; std::stringstream tmp_ss; int ele_size, neigh_pre_ele; // read head info do { std::getline(tetin, tmp_str); if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> ele_size >> neigh_pre_ele; if (tmp_ss.fail() || neigh_pre_ele != 4 || ele_size != out_tet.size()) { err_str = "Wrong head-info found in " + filename + ".neigh. From void gctl::read_Tetgen_neighbor(...)"; throw runtime_error(err_str); } } } while (tmp_str[0] == '#'); int tmp_int, tmp_order[4]; while (std::getline(tetin, tmp_str)) { if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> tmp_int >> tmp_order[0] >> tmp_order[1] >> tmp_order[2] >> tmp_order[3]; if (tmp_ss.fail()) { err_str = "Wrong file format found in " + filename + ".neigh. From void gctl::read_Tetgen_neighbor(...)"; throw runtime_error(err_str); } if (packed == NotPacked) { tmp_int -= 1; for (int j = 0; j < 4; j++) { if (tmp_order[j] != -1) out_tet[tmp_int].neigh[j] = out_tet.get(tmp_order[j]-1); else out_tet[tmp_int].neigh[j] = nullptr; } } else { for (int j = 0; j < 4; j++) { if (tmp_order[j] != -1) out_tet[tmp_int].neigh[j] = out_tet.get(tmp_order[j]); else out_tet[tmp_int].neigh[j] = nullptr; } } } } tetin.close(); return; } /** * @brief Read edge file generated by the Tetgen program. * * First line: <# of edges> * Remaining lines list # of edges: * [boundary marker] * * @param out_edge The output edge array. * @param[in] filename The filename of a .edge file generated by the Tetgen program. * @param[in] in_node 3D vertex array which contains vertex's information of the tetrahedrons. * @param[in] packed Indicates whether the starting index of vertice is zero or not. The * vertex's ordering is assumed to be start with one if this option is set to false. * The default value is true. * @param[in] bound_tag Pointer of the boundary marker array. */ template void read_Tetgen_edge(std::string filename, array> &out_edge, const array> &in_node, index_packed_e packed = Packed, array *bound_tag = nullptr) { std::ifstream tetin; open_infile(tetin, filename, ".edge"); std::string err_str, tmp_str; std::stringstream tmp_ss; int edge_size = 0, boundary_mark = 0; // read head info do { std::getline(tetin, tmp_str); if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> edge_size >> boundary_mark; if (tmp_ss.fail() || (boundary_mark != 0 && boundary_mark != 1)) { err_str = "Wrong head-info found in " + filename + ".edge. From void gctl::read_Tetgen_edge(...)"; throw runtime_error(err_str); } } } while (tmp_str[0] == '#'); if (boundary_mark != 0 && bound_tag != nullptr) { bound_tag->resize(edge_size); } else if (boundary_mark == 0 && bound_tag != nullptr) { err_str = "No boundary marks found in " + filename + ".edge. From void gctl::read_Tetgen_edge(...)"; throw runtime_error(err_str); } int tmp_int, tmp_mark, tmp_order[2]; out_edge.resize(edge_size); if (boundary_mark) { while (std::getline(tetin, tmp_str)) { if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> tmp_int >> tmp_order[0] >> tmp_order[1] >> tmp_mark; if (tmp_ss.fail()) { err_str = "Wrong file format found in " + filename + ".edge. From void gctl::read_Tetgen_edge(...)"; throw runtime_error(err_str); } if (packed == NotPacked) { tmp_int -= 1; out_edge[tmp_int].id = tmp_int; for (int j = 0; j < 2; j++) { out_edge[tmp_int].vert[j] = in_node.get(tmp_order[j]-1); } } else { out_edge[tmp_int].id = tmp_int; for (int j = 0; j < 2; j++) { out_edge[tmp_int].vert[j] = in_node.get(tmp_order[j]); } } bound_tag->at(tmp_int) = tmp_mark; } } } else { while (std::getline(tetin, tmp_str)) { if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> tmp_int >> tmp_order[0] >> tmp_order[1]; if (tmp_ss.fail()) { err_str = "Wrong file format found in " + filename + ".edge. From void gctl::read_Tetgen_edge(...)"; throw runtime_error(err_str); } if (packed == NotPacked) { tmp_int -= 1; out_edge[tmp_int].id = tmp_int; for (int j = 0; j < 2; j++) { out_edge[tmp_int].vert[j] = in_node.get(tmp_order[j]-1); } } else { out_edge[tmp_int].id = tmp_int; for (int j = 0; j < 2; j++) { out_edge[tmp_int].vert[j] = in_node.get(tmp_order[j]); } } } } } tetin.close(); return; } /** * @brief Read face file generated by the Tetgen program. * * First line: <# of faces> * Remaining lines list # of faces: * ... [boundary marker] ... * * @param out_tri The output face array. * @param[in] filename The filename of a .face file generated by the Tetgen program. * @param[in] in_node 3D vertex array which contains vertex's information of the tetrahedrons. * @param[in] packed Indicates whether the starting index of vertice is zero or not. The * vertex's ordering is assumed to be start with one if this option is set to false. * The default value is true. * @param[in] bound_tag Pointer of the boundary marker array. */ template void read_Tetgen_face(std::string filename, array> &out_tri, const array> &in_node, index_packed_e packed = Packed, array *bound_tag = nullptr) { std::ifstream tetin; open_infile(tetin, filename, ".face"); std::string err_str, tmp_str; std::stringstream tmp_ss; int face_size = 0, boundary_mark = 0; // read head info do { std::getline(tetin, tmp_str); if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> face_size >> boundary_mark; if (tmp_ss.fail() || (boundary_mark != 0 && boundary_mark != 1)) { err_str = "Wrong head-info found in " + filename + ".face. From void gctl::read_Tetgen_face(...)"; throw runtime_error(err_str); } } } while (tmp_str[0] == '#'); if (boundary_mark != 0 && bound_tag != nullptr) { bound_tag->resize(face_size); } else if (boundary_mark == 0 && bound_tag != nullptr) { err_str = "No boundary marks found in " + filename + ".face. From void gctl::read_Tetgen_face(...)"; throw runtime_error(err_str); } int tmp_int, tmp_mark, tmp_order[3]; out_tri.resize(face_size); if (boundary_mark) { while (std::getline(tetin, tmp_str)) { if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> tmp_int >> tmp_order[0] >> tmp_order[1] >> tmp_order[2] >> tmp_mark; if (tmp_ss.fail()) { err_str = "Wrong file format found in " + filename + ".face. From void gctl::read_Tetgen_face(...)"; throw runtime_error(err_str); } if (packed == NotPacked) { tmp_int -= 1; out_tri[tmp_int].id = tmp_int; for (int j = 0; j < 3; j++) { out_tri[tmp_int].vert[j] = in_node.get(tmp_order[j]-1); } } else { out_tri[tmp_int].id = tmp_int; for (int j = 0; j < 3; j++) { out_tri[tmp_int].vert[j] = in_node.get(tmp_order[j]); } } if (bound_tag != nullptr) { bound_tag->at(tmp_int) = tmp_mark; } } } } else { while (std::getline(tetin, tmp_str)) { if (tmp_str[0] != '#') { str2ss(tmp_str, tmp_ss); tmp_ss >> tmp_int >> tmp_order[0] >> tmp_order[1] >> tmp_order[2]; if (tmp_ss.fail()) { err_str = "Wrong file format found in " + filename + ".face. From void gctl::read_Tetgen_face(...)"; throw runtime_error(err_str); } if (packed == NotPacked) { tmp_int -= 1; out_tri[tmp_int].id = tmp_int; for (int j = 0; j < 3; j++) { out_tri[tmp_int].vert[j] = in_node.get(tmp_order[j]-1); } } else { out_tri[tmp_int].id = tmp_int; for (int j = 0; j < 3; j++) { out_tri[tmp_int].vert[j] = in_node.get(tmp_order[j]); } } } } } tetin.close(); return; } /** * @brief Saves a Tetgen node file. * * @param[in] filename Output filename. * @param[in] out_node Output nodes. * @param[in] packed Indicates whether the starting index of vertice is zero or not. The * vertex's ordering is assumed to be start with one if this option is set to false. * The default value is true. */ template void save_Tetgen_node(std::string filename, const array> &out_node, index_packed_e packed = Packed, const array *bound_tag = nullptr, const _2d_matrix *attri = nullptr) { std::ofstream triout; open_outfile(triout, filename, ".node"); std::string err_str; int attri_num = 0, boundary_mark = 0; if (bound_tag != nullptr) { boundary_mark = 1; if (out_node.size() != bound_tag->size()) { err_str = "Size of the boundary marks does not match. From void gctl::save_Tetgen_node(...)"; throw runtime_error(err_str); } } if (attri != nullptr) { attri_num = attri->col_size(); if (out_node.size() != attri->row_size()) { err_str = "Size of the attributes do not match. From void gctl::save_Tetgen_node(...)"; throw runtime_error(err_str); } } time_t now = time(0); char *dt = ctime(&now); triout << "# generated by the GCTL package on " << dt; triout << "# node_num node_dimension attri_num boundary_mark" << std::endl; triout << out_node.size() << " 3 " << attri_num << " " << boundary_mark << std::endl; triout << "# node_index x y z attributes mark" << std::endl; if (attri != nullptr && bound_tag != nullptr) { if (packed == Packed) { for (int i = 0; i < out_node.size(); i++) { triout << i << " " << std::setprecision(16) << out_node[i].x << " " << out_node[i].y << " " << out_node[i].z; for (int j = 0; j < attri_num; j++) { triout << " " << attri->at(i, j); } triout << " " << bound_tag->at(i) << std::endl; } } else { for (int i = 0; i < out_node.size(); i++) { triout << i+1 << " " << std::setprecision(16) << out_node[i].x << " " << out_node[i].y << " " << out_node[i].z; for (int j = 0; j < attri_num; j++) { triout << " " << attri->at(i, j); } triout << " " << bound_tag->at(i) << std::endl; } } } else if (attri != nullptr) { if (packed == Packed) { for (int i = 0; i < out_node.size(); i++) { triout << i << " " << std::setprecision(16) << out_node[i].x << " " << out_node[i].y << " " << out_node[i].z; for (int j = 0; j < attri_num; j++) { triout << " " << attri->at(i, j); } triout << std::endl; } } else { for (int i = 0; i < out_node.size(); i++) { triout << i+1 << " " << std::setprecision(16) << out_node[i].x << " " << out_node[i].y << " " << out_node[i].z; for (int j = 0; j < attri_num; j++) { triout << " " << attri->at(i, j); } triout << std::endl; } } } else if (bound_tag != nullptr) { if (packed == Packed) { for (int i = 0; i < out_node.size(); i++) { triout << i << " " << std::setprecision(16) << out_node[i].x << " " << out_node[i].y << " " << out_node[i].z; triout << " " << bound_tag->at(i) << std::endl; } } else { for (int i = 0; i < out_node.size(); i++) { triout << i+1 << " " << std::setprecision(16) << out_node[i].x << " " << out_node[i].y << " " << out_node[i].z; triout << " " << bound_tag->at(i) << std::endl; } } } else { if (packed == Packed) { for (int i = 0; i < out_node.size(); i++) { triout << i << " " << std::setprecision(16) << out_node[i].x << " " << out_node[i].y << " " << out_node[i].z << std::endl; } } else { for (int i = 0; i < out_node.size(); i++) { triout << i+1 << " " << std::setprecision(16) << out_node[i].x << " " << out_node[i].y << " " << out_node[i].z << std::endl; } } } triout.close(); return; } /** * @brief Saves a Tetgen element file. * * @param[in] filename Output filename. * @param[in] out_tet Output tetrahedral elements. * @param[in] packed Indicates whether the starting index of vertice is zero or not. The * vertex's ordering is assumed to be start with one if this option is set to false. * The default value is true. */ template void save_Tetgen_element(std::string filename, const array> &out_tet, index_packed_e packed = Packed, const array *region_attri = nullptr) { std::ofstream tetout; open_outfile(tetout, filename, ".ele"); std::string err_str; int region_mark = 0; if (region_attri != nullptr) { region_mark = 1; if (out_tet.size() != region_attri->size()) { err_str = "Size of the region attribute does not match. From void gctl::save_Tetgen_element(...)"; throw runtime_error(err_str); } } time_t now = time(0); char *dt = ctime(&now); tetout << "# generated by the GCTL package on " << dt; tetout << "# element_num index_num region_mark" << std::endl; tetout << out_tet.size() << " 4 " << region_mark << std::endl; tetout << "# element_index node_index attributes" << std::endl; if (region_attri != nullptr) { if (packed == Packed) { for (int i = 0; i < out_tet.size(); i++) { tetout << i; for (int j = 0; j < 4; j++) { tetout << " " << out_tet[i].vert[j]->id; } tetout << " " << region_attri->at(i) << std::endl; } } else { for (int i = 0; i < out_tet.size(); i++) { tetout << i + 1; for (int j = 0; j < 4; j++) { tetout << " " << out_tet[i].vert[j]->id + 1; } tetout << " " << region_attri->at(i) << std::endl; } } } else { if (packed == Packed) { for (int i = 0; i < out_tet.size(); i++) { tetout << i; for (int j = 0; j < 4; j++) { tetout << " " << out_tet[i].vert[j]->id; } tetout << std::endl; } } else { for (int i = 0; i < out_tet.size(); i++) { tetout << i + 1; for (int j = 0; j < 4; j++) { tetout << " " << out_tet[i].vert[j]->id + 1; } tetout << std::endl; } } } tetout.close(); return; } /** * @brief Saves a Tetgen neighbor file. * * @param[in] filename Output filename. * @param[in] out_tet Output tetrahedral elements. * @param[in] packed Indicates whether the starting index of vertice is zero or not. The * vertex's ordering is assumed to be start with one if this option is set to false. * The default value is true. */ template void save_Tetgen_neighbor(std::string filename, const array> &out_tet, index_packed_e packed = Packed) { std::ofstream tetout; open_outfile(tetout, filename, ".neigh"); time_t now = time(0); char *dt = ctime(&now); tetout << "# generated by the GCTL package on " << dt; tetout << "# element_num index_num" << std::endl; tetout << out_tet.size() << " 4" << std::endl; tetout << "# element_index element_index" << std::endl; if (packed == Packed) { for (int i = 0; i < out_tet.size(); i++) { tetout << i; for (int j = 0; j < 4; j++) { if (out_tet[i].neigh[j] != nullptr) tetout << " " << out_tet[i].neigh[j]->id; else tetout << " -1"; } tetout << std::endl; } } else { for (int i = 0; i < out_tet.size(); i++) { tetout << i + 1; for (int j = 0; j < 4; j++) { if (out_tet[i].neigh[j] != nullptr) tetout << " " << out_tet[i].neigh[j]->id + 1; else tetout << " -1"; } tetout << std::endl; } } tetout.close(); return; } /** * @brief Saves a Tetgen edge file. * * @param[in] filename Output filename. * @param out_edge Output edge elements. * @param[in] packed Indicates whether the starting index of vertice is zero or not. The * vertex's ordering is assumed to be start with one if this option is set to false. * The default value is true. */ template void save_Tetgen_edge(std::string filename, const array> &out_edge, index_packed_e packed = Packed, const array *bound_tag = nullptr) { std::ofstream tetout; open_outfile(tetout, filename, ".edge"); std::string err_str; int boundary_mark = 0; if (bound_tag != nullptr) { boundary_mark = 1; if (out_edge.size() != bound_tag->size()) { err_str = "Size of the boundary marks does not match. From void gctl::save_Tetgen_edge(...)"; throw runtime_error(err_str); } } time_t now = time(0); char *dt = ctime(&now); tetout << "# generated by the GCTL package on " << dt; tetout << "# edge_num index_num" << std::endl; tetout << out_edge.size() << " " << boundary_mark << std::endl; tetout << "# edge_index node_index boundary_mark" << std::endl; if (bound_tag != nullptr) { if (packed == Packed) { for (int i = 0; i < out_edge.size(); i++) { tetout << i; for (int j = 0; j < 2; j++) { tetout << " " << out_edge[i].vert[j]->id; } tetout << " " << bound_tag->at(i) << std::endl; } } else { for (int i = 0; i < out_edge.size(); i++) { tetout << i + 1; for (int j = 0; j < 2; j++) { tetout << " " << out_edge[i].vert[j]->id + 1; } tetout << " " << bound_tag->at(i) << std::endl; } } } else { if (packed == Packed) { for (int i = 0; i < out_edge.size(); i++) { tetout << i; for (int j = 0; j < 2; j++) { tetout << " " << out_edge[i].vert[j]->id; } tetout << std::endl; } } else { for (int i = 0; i < out_edge.size(); i++) { tetout << i + 1; for (int j = 0; j < 2; j++) { tetout << " " << out_edge[i].vert[j]->id + 1; } tetout << std::endl; } } } tetout.close(); return; } /** * @brief Saves a Tetgen face face. * * @param[in] filename Output filename. * @param[in] out_tri Output triangle elements. * @param[in] packed Indicates whether the starting index of vertice is zero or not. The * vertex's ordering is assumed to be start with one if this option is set to false. * The default value is true. */ template void save_Tetgen_face(std::string filename, const array> &out_tri, index_packed_e packed = Packed, const array *bound_tag = nullptr) { std::ofstream tetout; open_outfile(tetout, filename, ".face"); std::string err_str; int boundary_mark = 0; if (bound_tag != nullptr) { boundary_mark = 1; if (out_tri.size() != bound_tag->size()) { err_str = "Size of the boundary marks does not match. From void gctl::save_Tetgen_face(...)"; throw runtime_error(err_str); } } time_t now = time(0); char *dt = ctime(&now); tetout << "# generated by the GCTL package on " << dt; tetout << "# face_num index_num" << std::endl; tetout << out_tri.size() << " " << boundary_mark << std::endl; tetout << "# face_index node_index boundary_mark" << std::endl; if (bound_tag != nullptr) { if (packed == Packed) { for (int i = 0; i < out_tri.size(); i++) { tetout << i; for (int j = 0; j < 3; j++) { tetout << " " << out_tri[i].vert[j]->id; } tetout << " " << bound_tag->at(i) << std::endl; } } else { for (int i = 0; i < out_tri.size(); i++) { tetout << i + 1; for (int j = 0; j < 3; j++) { tetout << " " << out_tri[i].vert[j]->id + 1; } tetout << " " << bound_tag->at(i) << std::endl; } } } else { if (packed == Packed) { for (int i = 0; i < out_tri.size(); i++) { tetout << i; for (int j = 0; j < 3; j++) { tetout << " " << out_tri[i].vert[j]->id; } tetout << std::endl; } } else { for (int i = 0; i < out_tri.size(); i++) { tetout << i + 1; for (int j = 0; j < 3; j++) { tetout << " " << out_tri[i].vert[j]->id + 1; } tetout << std::endl; } } } tetout.close(); return; } }; #endif //_GCTL_TETGEN_IO_H