You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
7.3 KiB
232 lines
7.3 KiB
2 years ago
|
#include <iostream>
|
||
|
#include <cmath>
|
||
|
#include <fstream>
|
||
|
#include <iomanip>
|
||
|
#include <spdlog/spdlog.h>
|
||
|
#include <boost/filesystem.hpp>
|
||
|
#include <mshio/mshio.h>
|
||
|
|
||
|
#include "io_utils.h"
|
||
|
|
||
|
#include "sha-base-framework/frame.h"
|
||
|
|
||
|
namespace da::sha {
|
||
|
|
||
|
void WriteTriVTK(const std::string &path, const Eigen::MatrixXd &V, const Eigen::MatrixXi &T,
|
||
|
const std::vector<double> &cell_data, const std::vector<double> &v_data){
|
||
|
std::ofstream out(path);
|
||
|
out << "# vtk DataFile Version 3.0\n"
|
||
|
"Volume Mesh\n"
|
||
|
"ASCII\n"
|
||
|
"DATASET UNSTRUCTURED_GRID" << std::endl;
|
||
|
out << "POINTS " << V.rows() << " double" << std::endl;
|
||
|
for (int i = 0; i < V.rows(); ++i) {
|
||
|
out << std::setprecision(18) << V.row(i).x() << " " << V.row(i).y() << " " << V.row(i).z() << std::endl;
|
||
|
}
|
||
|
out << "CELLS " << T.rows() << " " << T.rows() * (3 + 1) << std::endl;
|
||
|
for (int i = 0; i < T.rows(); ++i) {
|
||
|
out << "3 " << T.row(i).x() << " " << T.row(i).y() << " " << T.row(i).z() << std::endl;
|
||
|
}
|
||
|
out << "CELL_TYPES " << T.rows() << std::endl;
|
||
|
for (int i = 0; i < T.rows(); ++i) {
|
||
|
out << 5 << std::endl;
|
||
|
}
|
||
|
|
||
|
if(!cell_data.empty()){
|
||
|
out << "CELL_DATA " << cell_data.size() << "\n" <<
|
||
|
"SCALARS cell_scalars double 1\n" <<
|
||
|
"LOOKUP_TABLE default" << std::endl;
|
||
|
for (auto &d: cell_data) {
|
||
|
out << d << std::endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!v_data.empty()){
|
||
|
out << "POINT_DATA " << v_data.size() << "\n" <<
|
||
|
"SCALARS point_scalars double 1\n" <<
|
||
|
"LOOKUP_TABLE default" << std::endl;
|
||
|
for (auto &d: v_data) {
|
||
|
out << d << std::endl;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WriteTetVTK(const std::string &path, const Eigen::MatrixXd &V, const Eigen::MatrixXi &T,
|
||
|
const std::vector<double> &cell_data, const std::vector<double> &v_data){
|
||
|
std::ofstream out(path);
|
||
|
out << "# vtk DataFile Version 3.0\n"
|
||
|
"Volume Mesh\n"
|
||
|
"ASCII\n"
|
||
|
"DATASET UNSTRUCTURED_GRID" << std::endl;
|
||
|
out << "POINTS " << V.rows() << " double" << std::endl;
|
||
|
for (int i = 0; i < V.rows(); ++i) {
|
||
|
out << std::setprecision(18) << V.row(i).x() << " " << V.row(i).y() << " " << V.row(i).z() << std::endl;
|
||
|
}
|
||
|
out << "CELLS " << T.rows() << " " << T.rows() * (4 + 1) << std::endl;
|
||
|
for (int i = 0; i < T.rows(); ++i) {
|
||
|
out << "4 " << T.row(i).x() << " " << T.row(i).y() << " " << T.row(i).z() << " " << T.row(i).w() << std::endl;
|
||
|
}
|
||
|
out << "CELL_TYPES " << T.rows() << std::endl;
|
||
|
for (int i = 0; i < T.rows(); ++i) {
|
||
|
out << 10 << std::endl;
|
||
|
}
|
||
|
|
||
|
if(!cell_data.empty()){
|
||
|
out << "CELL_DATA " << cell_data.size() << "\n" <<
|
||
|
"SCALARS cell_scalars double 1\n" <<
|
||
|
"LOOKUP_TABLE default" << std::endl;
|
||
|
for (auto &d: cell_data) {
|
||
|
out << d << std::endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!v_data.empty()){
|
||
|
out << "POINT_DATA " << v_data.size() << "\n" <<
|
||
|
"SCALARS point_scalars double 1\n" <<
|
||
|
"LOOKUP_TABLE default" << std::endl;
|
||
|
for (auto &d: v_data) {
|
||
|
out << d << std::endl;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WritePntVTK(const std::string &path, const Eigen::MatrixXd &V) {
|
||
|
std::ofstream out(path);
|
||
|
out << "# vtk DataFile Version 3.0\n"
|
||
|
"Volume Mesh\n"
|
||
|
"ASCII\n"
|
||
|
"DATASET UNSTRUCTURED_GRID" << std::endl;
|
||
|
out << "POINTS " << V.rows() << " float" << std::endl;
|
||
|
for (int i = 0; i < V.rows(); ++i) {
|
||
|
out << std::setprecision(4) << V.row(i).x() << " " << V.row(i).y() << " " << V.row(i).z() << std::endl;
|
||
|
}
|
||
|
out << "CELLS " << V.rows() << " " << V.rows() * (1 + 1) << std::endl;
|
||
|
for (int i = 0; i < V.rows(); ++i) {
|
||
|
out << "1 " << i << std::endl;
|
||
|
}
|
||
|
out << "CELL_TYPES " << V.rows() << std::endl;
|
||
|
for (int i = 0; i < V.rows(); ++i) {
|
||
|
out << 1 << std::endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FindSurfTriForTet(const Eigen::MatrixXi& TT, Eigen::MatrixXi& SF) {
|
||
|
std::map<std::tuple<int, int, int>, int> tri2Tet;
|
||
|
for (int elemI = 0; elemI < TT.rows(); elemI++) {
|
||
|
const Eigen::RowVector4i& elemVInd = TT.row(elemI);
|
||
|
tri2Tet[std::make_tuple(elemVInd[0], elemVInd[2], elemVInd[1])] = elemI;
|
||
|
tri2Tet[std::make_tuple(elemVInd[0], elemVInd[3], elemVInd[2])] = elemI;
|
||
|
tri2Tet[std::make_tuple(elemVInd[0], elemVInd[1], elemVInd[3])] = elemI;
|
||
|
tri2Tet[std::make_tuple(elemVInd[1], elemVInd[2], elemVInd[3])] = elemI;
|
||
|
}
|
||
|
|
||
|
//TODO: parallelize
|
||
|
std::vector<Eigen::RowVector3i> tmpF;
|
||
|
for (const auto& triI : tri2Tet) {
|
||
|
const auto& triVInd = triI.first;
|
||
|
// find dual triangle with reversed indices:
|
||
|
bool isSurfaceTriangle = //
|
||
|
tri2Tet.find(std::make_tuple(std::get<2>(triVInd), std::get<1>(triVInd), std::get<0>(triVInd))) == tri2Tet.end()
|
||
|
&& tri2Tet.find(std::make_tuple(std::get<1>(triVInd), std::get<0>(triVInd), std::get<2>(triVInd))) == tri2Tet.end()
|
||
|
&& tri2Tet.find(std::make_tuple(std::get<0>(triVInd), std::get<2>(triVInd), std::get<1>(triVInd))) == tri2Tet.end();
|
||
|
if (isSurfaceTriangle) {
|
||
|
tmpF.emplace_back(std::get<0>(triVInd), std::get<1>(triVInd), std::get<2>(triVInd));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SF.resize(tmpF.size(), 3);
|
||
|
for (int i = 0; i < SF.rows(); i++) {
|
||
|
SF.row(i) = tmpF[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool ReadTetMesh(const std::string& filePath, Eigen::MatrixXd& TV, Eigen::MatrixXi& TT,
|
||
|
Eigen::MatrixXi& SF) {
|
||
|
using namespace boost::filesystem;
|
||
|
if (!exists(path(filePath))) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
mshio::MshSpec spec;
|
||
|
try {
|
||
|
spec = mshio::load_msh(filePath);
|
||
|
}
|
||
|
catch (...) {
|
||
|
spdlog::error("MshIO only supports MSH 2.2 and 4.1");
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
const auto& nodes = spec.nodes;
|
||
|
const auto& els = spec.elements;
|
||
|
const int vAmt = nodes.num_nodes;
|
||
|
int elemAmt = 0;
|
||
|
for (const auto& e : els.entity_blocks) {
|
||
|
assert(e.entity_dim == 3);
|
||
|
assert(e.element_type == 4); // linear tet
|
||
|
elemAmt += e.num_elements_in_block;
|
||
|
}
|
||
|
|
||
|
TV.resize(vAmt, 3);
|
||
|
int index = 0;
|
||
|
for (const auto& n : nodes.entity_blocks) {
|
||
|
for (int i = 0; i < n.num_nodes_in_block * 3; i += 3) {
|
||
|
TV.row(index) << n.data[i], n.data[i + 1], n.data[i + 2];
|
||
|
++index;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TT.resize(elemAmt, 4);
|
||
|
int elm_index = 0;
|
||
|
for (const auto& e : els.entity_blocks) {
|
||
|
for (int i = 0; i < e.data.size(); i += 5) {
|
||
|
index = 0;
|
||
|
for (int j = i + 1; j <= i + 4; ++j) {
|
||
|
TT(elm_index, index++) = e.data[j] - 1;
|
||
|
}
|
||
|
++elm_index;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// finding the surface because $Surface is not supported by MshIO
|
||
|
spdlog::info("Finding the surface triangle mesh for {:s}", filePath);
|
||
|
FindSurfTriForTet(TT, SF);
|
||
|
|
||
|
spdlog::info("tet mesh loaded with {:d} particles, {:d} tets, and {:d} surface triangles.",
|
||
|
TV.rows(), TT.rows(), SF.rows());
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void WriteOBJ(const std::string &path, const Eigen::MatrixXd &V, const Eigen::MatrixXi &F) {
|
||
|
std::ofstream out(path);
|
||
|
for (int i = 0; i < V.rows(); ++i) {
|
||
|
out << std::setprecision(18) << "v " << V.row(i).x() << " " << V.row(i).y() << " " << V.row(i).z() << std::endl;
|
||
|
}
|
||
|
|
||
|
int cols = static_cast<int>(F.cols());
|
||
|
char type;
|
||
|
switch (cols) {
|
||
|
case 1:
|
||
|
type = 'p';
|
||
|
break;
|
||
|
case 2:
|
||
|
type = 'l';
|
||
|
break;
|
||
|
case 3:
|
||
|
type = 'f';
|
||
|
break;
|
||
|
default:
|
||
|
std::cout << "function WriteOBJ: fail to write obj file, F.cols must in {1, 2, 3}" << std::endl;
|
||
|
return;
|
||
|
}
|
||
|
for (int i = 0; i < F.rows(); ++i) {
|
||
|
out << type;
|
||
|
for (int j = 0; j < cols; ++j) {
|
||
|
out << " " << F(i, j) + 1; // index start from 1 in obj file
|
||
|
}
|
||
|
out << std::endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace da::sha
|