Refactoring, linear model, renaming

This commit is contained in:
iasonmanolas 2021-04-08 21:03:23 +03:00
parent fd67b1b4c9
commit e6fe102574
26 changed files with 4432 additions and 2890 deletions

View File

@ -10,11 +10,18 @@ else()
set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
endif()
set(EXTERNAL_DEPS_DIR "/home/iason/Coding/build/external dependencies")
if(NOT EXISTS ${EXTERNAL_DEPS_DIR})
set(EXTERNAL_DEPS_DIR ${CMAKE_CURRENT_BINARY_DIR})
endif()
##Create directory for the external libraries
file(MAKE_DIRECTORY ${EXTERNAL_DEPS_DIR})
##vcglib devel branch
download_project(PROJ vcglib_devel
GIT_REPOSITORY https://github.com/IasonManolas/vcglib.git
GIT_TAG devel
PREFIX ${CMAKE_CURRENT_LIST_DIR}/build/external/
PREFIX ${EXTERNAL_DEPS_DIR}
${UPDATE_DISCONNECTED_IF_AVAILABLE}
)
@ -22,11 +29,21 @@ download_project(PROJ vcglib_devel
download_project(PROJ MATPLOTPLUSPLUS
GIT_REPOSITORY https://github.com/alandefreitas/matplotplusplus
GIT_TAG master
PREFIX ${CMAKE_CURRENT_LIST_DIR}/build/external/
PREFIX ${EXTERNAL_DEPS_DIR}
${UPDATE_DISCONNECTED_IF_AVAILABLE}
)
add_subdirectory(${MATPLOTPLUSPLUS_SOURCE_DIR} ${MATPLOTPLUSPLUS_BINARY_DIR})
##threed-beam-fea
download_project(PROJ threed-beam-fea
GIT_REPOSITORY https://github.com/IasonManolas/threed-beam-fea.git
GIT_TAG master
PREFIX ${EXTERNAL_DEPS_DIR}
${UPDATE_DISCONNECTED_IF_AVAILABLE}
)
add_subdirectory(${threed-beam-fea_SOURCE_DIR} ${threed-beam-fea_BINARY_DIR})
###Eigen 3 NOTE: Eigen is required on the system the code is ran
find_package(Eigen3 3.3 REQUIRED)
@ -34,15 +51,18 @@ find_package(Eigen3 3.3 REQUIRED)
file(GLOB MySourcesFiles ${CMAKE_CURRENT_LIST_DIR}/*.hpp ${CMAKE_CURRENT_LIST_DIR}/*.cpp)
add_library(${PROJECT_NAME} ${MySourcesFiles} ${vcglib_devel_SOURCE_DIR}/wrap/ply/plylib.cpp)
target_link_libraries(${PROJECT_NAME} Eigen3::Eigen matplot)
target_link_directories(MySources PUBLIC ${CMAKE_CURRENT_LIST_DIR}/boost_graph/libs)
target_include_directories(${PROJECT_NAME}
PUBLIC ${CMAKE_CURRENT_LIST_DIR}/boost_graph
PRIVATE ${vcglib_devel_SOURCE_DIR}
PUBLIC ${vcglib_devel_SOURCE_DIR}
PUBLIC ${threed-beam-fea_SOURCE_DIR}
PUBLIC ${MySourcesFiles}
)
if(${USE_POLYSCOPE})
target_link_libraries(${PROJECT_NAME} polyscope glad)
find_package(OpenMP REQUIRED)
target_link_libraries(${PROJECT_NAME} Eigen3::Eigen matplot polyscope glad ThreedBeamFEA OpenMP::OpenMP_CXX)
else()
target_link_libraries(${PROJECT_NAME} -static Eigen3::Eigen matplot ThreedBeamFEA)
endif()
target_link_directories(MySources PUBLIC ${CMAKE_CURRENT_LIST_DIR}/boost_graph/libs)

View File

@ -35,21 +35,22 @@ struct CylindricalBeamDimensions {
CylindricalBeamDimensions() : od(0.03), id(0.026) {}
};
struct ElementMaterial {
double poissonsRatio; // NOTE: if I make this double the unit
// tests produced different results and thus fail
double youngsModulus;
ElementMaterial(const double &poissonsRatio, const double &youngsModulus)
: poissonsRatio(poissonsRatio), youngsModulus(youngsModulus) {
assert(poissonsRatio <= 0.5 && poissonsRatio >= -1);
}
ElementMaterial() : poissonsRatio(0.3), youngsModulus(200) {}
std::string toString() const {
return std::string("Material:") + std::string("\nPoisson's ratio=") +
std::to_string(poissonsRatio) +
std::string("\nYoung's Modulus(GPa)=") +
std::to_string(youngsModulus / 1e9);
}
struct ElementMaterial
{
double poissonsRatio;
double youngsModulus;
ElementMaterial(const double &poissonsRatio, const double &youngsModulus)
: poissonsRatio(poissonsRatio), youngsModulus(youngsModulus)
{
assert(poissonsRatio <= 0.5 && poissonsRatio >= -1);
}
ElementMaterial() : poissonsRatio(0.3), youngsModulus(200 * 1e9) {}
std::string toString() const
{
return std::string("Material:") + std::string("\nPoisson's ratio=")
+ std::to_string(poissonsRatio) + std::string("\nYoung's Modulus(GPa)=")
+ std::to_string(youngsModulus / 1e9);
}
};
#endif // BEAM_HPP

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
#ifndef CSVFILE_HPP
#define CSVFILE_HPP
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>

1977
drmsimulationmodel.cpp Executable file

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
#ifndef BEAMFORMFINDER_HPP
#define BEAMFORMFINDER_HPP
#include "elementalmesh.hpp"
#include "simulationmesh.hpp"
#include "matplot/matplot.h"
#include "simulationresult.hpp"
#include "simulation_structs.hpp"
#include <Eigen/Dense>
#include <filesystem>
#include <unordered_set>
@ -20,7 +20,8 @@ struct DifferentiateWithRespectTo {
const DoFType &dofi;
};
class FormFinder {
class DRMSimulationModel
{
public:
struct Settings {
bool debugMessages{false};
@ -108,7 +109,7 @@ private:
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
&fixedVertices);
void draw(const string &screenshotsFolder);
void draw(const string &screenshotsFolder= {});
void
updateNodalInternalForce(Vector6d &nodalInternalForce,
@ -160,7 +161,7 @@ private:
double computeTheta3(const EdgeType &e, const VertexType &v);
double computeDerivativeTheta3(const EdgeType &e, const VertexType &v,
const DifferentiateWithRespectTo &dui) const;
double computeTotalPotentialEnergy() const;
double computeTotalPotentialEnergy();
void computeRigidSupports();
void updateNormalDerivatives();
void updateT1Derivatives();
@ -197,8 +198,10 @@ private:
void printDebugInfo() const;
public:
FormFinder();
double computeTotalInternalPotentialEnergy();
public:
DRMSimulationModel();
SimulationResults
executeSimulation(const std::shared_ptr<SimulationJob> &pJob,
const Settings &settings = Settings());

View File

@ -4,8 +4,8 @@
Eigen::MatrixX2i VCGEdgeMesh::getEigenEdges() const { return eigenEdges; }
Eigen::MatrixX3d VCGEdgeMesh::getEigenVertices() const {
// getVertices(eigenVertices);
Eigen::MatrixX3d VCGEdgeMesh::getEigenVertices() {
getVertices(eigenVertices);
return eigenVertices;
}
@ -13,16 +13,18 @@ Eigen::MatrixX3d VCGEdgeMesh::getEigenEdgeNormals() const {
return eigenEdgeNormals;
}
bool VCGEdgeMesh::savePly(const std::string plyFilename) {
unsigned int mask = 0;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::SaveModel(
plyFilename.c_str(), *this, mask, false) != 0) {
return false;
}
return true;
bool VCGEdgeMesh::save(const string &plyFilename)
{
assert(std::filesystem::path(plyFilename).extension() == ".ply");
unsigned int mask = 0;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::SaveModel(plyFilename.c_str(), *this, mask, false)
!= 0) {
return false;
}
return true;
}
void VCGEdgeMesh::GeneratedRegularSquaredPattern(
@ -199,6 +201,8 @@ bool VCGEdgeMesh::load(const string &plyFilename) {
std::cout << plyFilename << " was loaded successfuly." << std::endl;
std::cout << "Mesh has " << EN() << " edges." << std::endl;
}
label=std::filesystem::path(plyFilename).stem();
return true;
}
@ -275,7 +279,7 @@ void VCGEdgeMesh::updateEigenEdgeAndVertices() {
#endif
}
void VCGEdgeMesh::copy(VCGEdgeMesh &mesh) {
bool VCGEdgeMesh::copy(VCGEdgeMesh &mesh) {
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::MeshCopy(*this, mesh);
label = mesh.getLabel();
eigenEdges = mesh.getEigenEdges();
@ -287,19 +291,47 @@ void VCGEdgeMesh::copy(VCGEdgeMesh &mesh) {
getVertices(eigenVertices);
}
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
return true;
}
void VCGEdgeMesh::removeDuplicateVertices() {
vcg::tri::Clean<VCGEdgeMesh>::MergeCloseVertex(*this, 0.000061524);
vcg::tri::Allocator<VCGEdgeMesh>::CompactEveryVector(*this);
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
}
void VCGEdgeMesh::deleteDanglingVertices() {
vcg::tri::Allocator<VCGEdgeMesh>::PointerUpdater<VertexPointer> pu;
deleteDanglingVertices(pu);
}
void VCGEdgeMesh::deleteDanglingVertices(vcg::tri::Allocator<VCGEdgeMesh>::PointerUpdater<TriMesh::VertexPointer> &pu) {
for (VertexType &v : vert) {
std::vector<VCGEdgeMesh::EdgePointer> incidentElements;
vcg::edge::VEStarVE(&v, incidentElements);
if (incidentElements.size() == 0 && !v.IsD()) {
vcg::tri::Allocator<VCGEdgeMesh>::DeleteVertex(*this, v);
}
}
vcg::tri::Allocator<VCGEdgeMesh>::CompactVertexVector(*this, pu);
vcg::tri::Allocator<VCGEdgeMesh>::CompactEdgeVector(*this);
updateEigenEdgeAndVertices();
}
void VCGEdgeMesh::getVertices(Eigen::MatrixX3d &vertices) {
vertices = Eigen::MatrixX3d();
vertices.resize(VN(), 3);
for (int vi = 0; vi < VN(); vi++) {
if (vert[vi].IsD()) {
continue;
vertices = Eigen::MatrixX3d();
vertices.resize(VN(), 3);
for (int vi = 0; vi < VN(); vi++) {
if (vert[vi].IsD()) {
continue;
}
VCGEdgeMesh::CoordType vertexCoordinates =
vert[static_cast<size_t>(vi)].cP();
vertices.row(vi) = vertexCoordinates.ToEigenVector<Eigen::Vector3d>();
}
VCGEdgeMesh::CoordType vertexCoordinates =
vert[static_cast<size_t>(vi)].cP();
vertices.row(vi) = vertexCoordinates.ToEigenVector<Eigen::Vector3d>();
}
}
std::string VCGEdgeMesh::getLabel() const { return label; }
@ -329,18 +361,25 @@ void VCGEdgeMesh::printVertexCoordinates(const size_t &vi) const {
}
#ifdef POLYSCOPE_DEFINED
void VCGEdgeMesh::registerForDrawing(
const std::optional<glm::vec3> &desiredColor,
const bool &shouldEnable) const {
initPolyscope();
const double drawingRadius = 0.002;
auto polyscopeHandle_edgeMesh = polyscope::registerCurveNetwork(
label, getEigenVertices(), getEigenEdges());
polyscopeHandle_edgeMesh->setEnabled(shouldEnable);
polyscopeHandle_edgeMesh->setRadius(drawingRadius, true);
if (desiredColor.has_value()) {
polyscopeHandle_edgeMesh->setColor(desiredColor.value());
}
//TODO: make const getEigenVertices is not
polyscope::CurveNetwork *VCGEdgeMesh::registerForDrawing(
const std::optional<std::array<double, 3>> &desiredColor, const bool &shouldEnable)
{
initPolyscope();
const double drawingRadius = 0.002;
polyscope::CurveNetwork *polyscopeHandle_edgeMesh(
polyscope::registerCurveNetwork(label, getEigenVertices(), getEigenEdges()));
polyscopeHandle_edgeMesh->setEnabled(shouldEnable);
polyscopeHandle_edgeMesh->setRadius(drawingRadius, true);
if (desiredColor.has_value()) {
const glm::vec3 desiredColor_glm(desiredColor.value()[0],
desiredColor.value()[1],
desiredColor.value()[2]);
polyscopeHandle_edgeMesh->setColor(desiredColor_glm);
}
return polyscopeHandle_edgeMesh;
}
void VCGEdgeMesh::unregister() const {
@ -352,4 +391,25 @@ void VCGEdgeMesh::unregister() const {
}
polyscope::removeCurveNetwork(label);
}
void VCGEdgeMesh::drawInitialFrames(polyscope::CurveNetwork *polyscopeHandle_initialMesh) const
{
Eigen::MatrixX3d frameInitialX(VN(), 3);
Eigen::MatrixX3d frameInitialY(VN(), 3);
Eigen::MatrixX3d frameInitialZ(VN(), 3);
for (int vi = 0; vi < VN(); vi++) {
frameInitialX.row(vi) = Eigen::Vector3d(1, 0, 0);
frameInitialY.row(vi) = Eigen::Vector3d(0, 1, 0);
frameInitialZ.row(vi) = Eigen::Vector3d(0, 0, 1);
}
polyscopeHandle_initialMesh->addNodeVectorQuantity("FrameX", frameInitialX)
->setVectorColor(glm::vec3(1, 0, 0))
->setEnabled(true);
polyscopeHandle_initialMesh->addNodeVectorQuantity("FrameY", frameInitialY)
->setVectorColor(glm::vec3(0, 1, 0))
->setEnabled(true);
polyscopeHandle_initialMesh->addNodeVectorQuantity("FrameZ", frameInitialZ)
->setVectorColor(glm::vec3(0, 0, 1))
->setEnabled(true);
}
#endif

View File

@ -45,34 +45,44 @@ class VCGEdgeMesh : public vcg::tri::TriMesh<std::vector<VCGEdgeMeshVertexType>,
return vcg::tri::Index<VCGEdgeMesh>(*this, meshElement);
}
void updateEigenEdgeAndVertices();
virtual void copy(VCGEdgeMesh &mesh);
/*
* The copy function shold be a virtual function of the base interface Mesh class.
* https://stackoverflow.com/questions/2354210/can-a-class-member-function-template-be-virtual
* use type erasure (?)
* */
bool copy(VCGEdgeMesh &mesh);
void removeDuplicateVertices();
void deleteDanglingVertices();
void deleteDanglingVertices(vcg::tri::Allocator<VCGEdgeMesh>::PointerUpdater<VertexPointer> &pu);
void getEdges(Eigen::MatrixX3d &edgeStartingPoints,
Eigen::MatrixX3d &edgeEndingPoints) const;
Eigen::MatrixX3d getNormals() const;
bool plyFileHasAllRequiredFields(const std::string &plyFilename);
bool plyFileHasAllRequiredFields(const std::string &plyFilename);
bool loadUsingNanoply(const std::string &plyFilename);
bool loadUsingNanoply(const std::string &plyFilename);
bool load(const std::string &plyFilename) override;
bool load(const std::string &plyFilename) override;
bool save(const std::string &plyFilename) override;
bool savePly(const std::string plyFilename);
bool createSpanGrid(const size_t squareGridDimension);
bool createSpanGrid(const size_t desiredWidth, const size_t desiredHeight);
void createSpiral(const float &degreesOfArm, const size_t &numberOfSamples);
bool createSpanGrid(const size_t squareGridDimension);
bool createSpanGrid(const size_t desiredWidth, const size_t desiredHeight);
void createSpiral(const float &degreesOfArm, const size_t &numberOfSamples);
Eigen::MatrixX2i getEigenEdges() const;
Eigen::MatrixX3d getEigenVertices() const;
Eigen::MatrixX2i getEigenEdges() const;
Eigen::MatrixX3d getEigenVertices();
Eigen::MatrixX3d getEigenEdgeNormals() const;
void printVertexCoordinates(const size_t &vi) const;
#ifdef POLYSCOPE_DEFINED
void registerForDrawing(
const std::optional<glm::vec3> &desiredColor = std::nullopt,
const bool &shouldEnable = true) const;
void unregister() const;
polyscope::CurveNetwork *registerForDrawing(
const std::optional<std::array<double, 3>> &desiredColor = std::nullopt,
const bool &shouldEnable = true);
void unregister() const;
void drawInitialFrames(polyscope::CurveNetwork *polyscopeHandle_initialMesh) const;
#endif
std::string getLabel() const;
void setLabel(const std::string &value);

313
linearsimulationmodel.hpp Normal file
View File

@ -0,0 +1,313 @@
#ifndef LINEARSIMULATIONMODEL_HPP
#define LINEARSIMULATIONMODEL_HPP
//#include "beam.hpp"
#include "simulation_structs.hpp"
#include "threed_beam_fea.h"
#include <filesystem>
#include <vector>
// struct BeamSimulationProperties {
// float crossArea;
// float I2;
// float I3;
// float polarInertia;
// float G;
// // Properties used by fea
// float EA;
// float EIz;
// float EIy;
// float GJ;
// BeamSimulationProperties(const BeamDimensions &dimensions,
// const BeamMaterial &material);
//};
// struct NodalForce {
// int index;
// int dof;
// double magnitude;
//};
// struct SimulationJob {
// Eigen::MatrixX3d nodes;
// Eigen::MatrixX2i elements;
// Eigen::MatrixX3d elementalNormals;
// Eigen::VectorXi fixedNodes;
// std::vector<NodalForce> nodalForces;
// std::vector<BeamDimensions> beamDimensions;
// std::vector<BeamMaterial> beamMaterial;
//};
// struct SimulationResults {
// std::vector<Eigen::VectorXd> edgeForces; ///< Force values per force
// component
// ///< #force components x #edges
// Eigen::MatrixXd
// nodalDisplacements; ///< The displacement of each node #nodes x 3
// SimulationResults(const fea::Summary &feaSummary);
// SimulationResults() {}
//};
class LinearSimulationModel {
public:
LinearSimulationModel(){
}
static std::vector<fea::Elem>
getFeaElements(const std::shared_ptr<SimulationJob> &job) {
const int numberOfEdges = job->pMesh->EN();
std::vector<fea::Elem> elements(numberOfEdges);
for (int edgeIndex = 0; edgeIndex < numberOfEdges; edgeIndex++) {
const SimulationMesh::CoordType &evn0 =
job->pMesh->edge[edgeIndex].cV(0)->cN();
const SimulationMesh::CoordType &evn1 = job->pMesh->edge[edgeIndex].cV(1)->cN();
const std::vector<double> nAverageVector{(evn0[0] + evn1[0]) / 2,
(evn0[1] + evn1[1]) / 2,
(evn0[2] + evn1[2]) / 2};
const Element &element = job->pMesh->elements[edgeIndex];
const double E = element.material.youngsModulus;
fea::Props feaProperties(E * element.A, E * element.I3, E * element.I2,
element.G * element.J, nAverageVector);
const int vi0 = job->pMesh->getIndex(job->pMesh->edge[edgeIndex].cV(0));
const int vi1 = job->pMesh->getIndex(job->pMesh->edge[edgeIndex].cV(1));
elements[edgeIndex] = fea::Elem(vi0, vi1, feaProperties);
}
return elements;
}
static std::vector<fea::Node>
getFeaNodes(const std::shared_ptr<SimulationJob> &job) {
const int numberOfNodes = job->pMesh->VN();
std::vector<fea::Node> feaNodes(numberOfNodes);
for (int vi = 0; vi < numberOfNodes; vi++) {
const CoordType &p = job->pMesh->vert[vi].cP();
feaNodes[vi] = fea::Node(p[0], p[1], p[2]);
}
return feaNodes;
}
static std::vector<fea::BC>
getFeaFixedNodes(const std::shared_ptr<SimulationJob> &job) {
std::vector<fea::BC> boundaryConditions;
boundaryConditions.reserve(job->constrainedVertices.size() * 6
+ job->nodalForcedDisplacements.size() * 3);
for (auto fixedVertex : job->constrainedVertices) {
const int vertexIndex = fixedVertex.first;
for (int dofIndex : fixedVertex.second) {
boundaryConditions.emplace_back(
fea::BC(vertexIndex, static_cast<fea::DOF>(dofIndex), 0));
}
}
for (auto forcedDisplacement : job->nodalForcedDisplacements) {
const int vi = forcedDisplacement.first;
for (int dofIndex = 0; dofIndex < 3; dofIndex++) {
boundaryConditions.emplace_back(fea::BC(vi,
static_cast<fea::DOF>(dofIndex),
forcedDisplacement.second[dofIndex]));
}
}
return boundaryConditions;
}
static std::vector<fea::Force>
getFeaNodalForces(const std::shared_ptr<SimulationJob> &job) {
std::vector<fea::Force> nodalForces;
nodalForces.reserve(job->nodalExternalForces.size() * 6);
for (auto nodalForce : job->nodalExternalForces) {
for (int dofIndex = 0; dofIndex < 6; dofIndex++) {
if (nodalForce.second[dofIndex] == 0) {
continue;
}
nodalForces.emplace_back(
fea::Force(nodalForce.first, dofIndex, nodalForce.second[dofIndex]));
}
}
return nodalForces;
}
static SimulationResults getResults(const fea::Summary &feaSummary,
const std::shared_ptr<SimulationJob> &simulationJob)
{
SimulationResults results;
results.converged = feaSummary.converged;
if (!results.converged) {
return results;
}
results.executionTime = feaSummary.total_time_in_ms * 1000;
// displacements
results.displacements.resize(feaSummary.num_nodes);
results.rotationalDisplacementQuaternion.resize(feaSummary.num_nodes);
for (int vi = 0; vi < feaSummary.num_nodes; vi++) {
results.displacements[vi] = Vector6d(feaSummary.nodal_displacements[vi]);
const Vector6d &nodalDisplacement = results.displacements[vi];
Eigen::Quaternion<double> q_nx;
q_nx = Eigen::AngleAxis<double>(nodalDisplacement[3], Eigen::Vector3d(1, 0, 0));
Eigen::Quaternion<double> q_ny;
q_ny = Eigen::AngleAxis<double>(nodalDisplacement[4], Eigen::Vector3d(0, 1, 0));
Eigen::Quaternion<double> q_nz;
q_nz = Eigen::AngleAxis<double>(nodalDisplacement[5], Eigen::Vector3d(0, 0, 1));
results.rotationalDisplacementQuaternion[vi] = q_nx * q_ny * q_nz;
// results.rotationalDisplacementQuaternion[vi] = q_nz * q_ny * q_nx;
// results.rotationalDisplacementQuaternion[vi] = q_nz * q_nx * q_ny;
}
// // Convert forces
// // Convert to vector of eigen matrices of the form force component-> per
// // Edge
// const int numDof = 6;
// const size_t numberOfEdges = feaSummary.element_forces.size();
// edgeForces =
// std::vector<Eigen::VectorXd>(numDof, Eigen::VectorXd(2 *
// numberOfEdges));
// for (gsl::index edgeIndex = 0; edgeIndex < numberOfEdges; edgeIndex++) {
// for (gsl::index forceComponentIndex = 0; forceComponentIndex < numDof;
// forceComponentIndex++) {
// (edgeForces[forceComponentIndex])(2 * edgeIndex) =
// feaSummary.element_forces[edgeIndex][forceComponentIndex];
// (edgeForces[forceComponentIndex])(2 * edgeIndex + 1) =
// feaSummary.element_forces[edgeIndex][numDof +
// forceComponentIndex];
// }
// }
for (int ei = 0; ei < feaSummary.num_elems; ei++) {
const std::vector<double> &elementForces = feaSummary.element_forces[ei];
const Element &element = simulationJob->pMesh->elements[ei];
//Axial
const double elementPotentialEnergy_axial_v0 = std::pow(elementForces[0], 2)
* element.initialLength
/ (4 * element.material.youngsModulus
* element.A);
const double elementPotentialEnergy_axial_v1 = std::pow(elementForces[6], 2)
* element.initialLength
/ (4 * element.material.youngsModulus
* element.A);
const double elementPotentialEnergy_axial = elementPotentialEnergy_axial_v0
+ elementPotentialEnergy_axial_v1;
//Shear
const double elementPotentialEnergy_shearY_v0 = std::pow(elementForces[1], 2)
* element.initialLength
/ (4 * element.A * element.G);
const double elementPotentialEnergy_shearZ_v0 = std::pow(elementForces[2], 2)
* element.initialLength
/ (4 * element.A * element.G);
const double elementPotentialEnergy_shearY_v1 = std::pow(elementForces[7], 2)
* element.initialLength
/ (4 * element.A * element.G);
const double elementPotentialEnergy_shearZ_v1 = std::pow(elementForces[8], 2)
* element.initialLength
/ (4 * element.A * element.G);
const double elementPotentialEnergy_shear = elementPotentialEnergy_shearY_v0
+ elementPotentialEnergy_shearZ_v0
+ elementPotentialEnergy_shearY_v1
+ elementPotentialEnergy_shearZ_v1;
//Bending
const double elementPotentialEnergy_bendingY_v0 = std::pow(elementForces[4], 2)
* element.initialLength
/ (4 * element.material.youngsModulus
* element.I2);
const double elementPotentialEnergy_bendingZ_v0 = std::pow(elementForces[5], 2)
* element.initialLength
/ (4 * element.material.youngsModulus
* element.I3);
const double elementPotentialEnergy_bendingY_v1 = std::pow(elementForces[10], 2)
* element.initialLength
/ (4 * element.material.youngsModulus
* element.I2);
const double elementPotentialEnergy_bendingZ_v1 = std::pow(elementForces[11], 2)
* element.initialLength
/ (4 * element.material.youngsModulus
* element.I3);
const double elementPotentialEnergy_bending = elementPotentialEnergy_bendingY_v0
+ elementPotentialEnergy_bendingZ_v0
+ elementPotentialEnergy_bendingY_v1
+ elementPotentialEnergy_bendingZ_v1;
//Torsion
const double elementPotentialEnergy_torsion_v0 = std::pow(elementForces[3], 2)
* element.initialLength
/ (4 * element.J * element.G);
const double elementPotentialEnergy_torsion_v1 = std::pow(elementForces[9], 2)
* element.initialLength
/ (4 * element.J * element.G);
const double elementPotentialEnergy_torsion = elementPotentialEnergy_torsion_v0
+ elementPotentialEnergy_torsion_v1;
const double elementInternalPotentialEnergy = elementPotentialEnergy_axial
+ elementPotentialEnergy_shear
+ elementPotentialEnergy_bending
+ elementPotentialEnergy_torsion;
results.internalPotentialEnergy += elementInternalPotentialEnergy;
}
return results;
}
SimulationResults
executeSimulation(const std::shared_ptr<SimulationJob> &simulationJob) {
assert(simulationJob->pMesh->VN() != 0);
fea::Job job(getFeaNodes(simulationJob), getFeaElements(simulationJob));
// printInfo(job);
// create the default options
fea::Options opts;
opts.save_elemental_forces = false;
opts.save_nodal_displacements = false;
opts.save_nodal_forces = false;
opts.save_report = false;
opts.save_tie_forces = false;
// if (!elementalForcesOutputFilepath.empty()) {
// opts.save_elemental_forces = true;
// opts.elemental_forces_filename = elementalForcesOutputFilepath;
// }
// if (!nodalDisplacementsOutputFilepath.empty()) {
// opts.save_nodal_displacements = true;
// opts.nodal_displacements_filename = nodalDisplacementsOutputFilepath;
// }
// have the program output status updates
opts.verbose = false;
// form an empty vector of ties
std::vector<fea::Tie> ties;
// also create an empty list of equations
std::vector<fea::Equation> equations;
auto fixedVertices = getFeaFixedNodes(simulationJob);
auto nodalForces = getFeaNodalForces(simulationJob);
fea::Summary feaResults =
fea::solve(job, fixedVertices, nodalForces, ties, equations, opts);
SimulationResults results = getResults(feaResults, simulationJob);
results.job = simulationJob;
return results;
}
// SimulationResults getResults() const;
// void setResultsNodalDisplacementCSVFilepath(const std::string
// &outputPath); void setResultsElementalForcesCSVFilepath(const std::string
// &outputPath);
private:
// std::string nodalDisplacementsOutputFilepath{"nodal_displacement.csv"};
// std::string elementalForcesOutputFilepath{"elemental_forces.csv"};
// SimulationResults results;
static void printInfo(const fea::Job &job) {
std::cout << "Details regarding the fea::Job:" << std::endl;
std::cout << "Nodes:" << std::endl;
for (fea::Node n : job.nodes) {
std::cout << n << std::endl;
}
std::cout << "Elements:" << std::endl;
for (Eigen::Vector2i e : job.elems) {
std::cout << e << std::endl;
}
}
};
#endif // LINEARSIMULATIONMODEL_HPP

View File

@ -1,14 +1,19 @@
#ifndef MESH_HPP
#define MESH_HPP
#include <Eigen/Core>
#include <string>
class Mesh {
std::string label;
class Mesh
{
protected:
std::string label;
public:
virtual ~Mesh() = default;
virtual bool load(const std::string &filePath) { return false; }
virtual bool save(const std::string &filePath) { return false; }
std::string getLabel() const;
void setLabel(const std::string &newLabel);
};

View File

@ -1,5 +0,0 @@
#ifndef OPTIMIZATIONSTRUCTS_HPP
#define OPTIMIZATIONSTRUCTS_HPP
#include "beamformfinder.hpp"
#endif // OPTIMIZATIONSTRUCTS_HPP

View File

@ -46,10 +46,10 @@ void PatternIO::exportPLY(const std::string &patternSetFilePath,
PatternGeometry p;
p.add(vertices, patterns[patternIndex].edges);
auto tiled = p.createTile(p);
p.savePly(
p.save(
std::filesystem::path(outputDirectoryPath)
.append(std::to_string(patterns[patternIndex].name) + ".ply"));
tiled.savePly(std::filesystem::path(outputDirectoryPath)
tiled.save(std::filesystem::path(outputDirectoryPath)
.append("tiled_" +
std::to_string(patterns[patternIndex].name) +
".ply"));

View File

@ -3,73 +3,206 @@
#include "mesh.hpp"
#include "vcg/complex/complex.h"
#include <filesystem>
#include <polyscope/surface_mesh.h>
#include <wrap/io_trimesh/import.h>
class PFace;
class PVertex;
class PEdge;
struct PUsedTypes : public vcg::UsedTypes<vcg::Use<PVertex>::AsVertexType,
vcg::Use<PFace>::AsFaceType> {};
vcg::Use<PFace>::AsFaceType,
vcg::Use<PEdge>::AsEdgeType>
{};
class PVertex : public vcg::Vertex<PUsedTypes, vcg::vertex::Coord3d,
vcg::vertex::Normal3d, vcg::vertex::Mark,
vcg::vertex::Qualityf, vcg::vertex::BitFlags,
vcg::vertex::VFAdj> {};
class PVertex : public vcg::Vertex<PUsedTypes,
vcg::vertex::Coord3d,
vcg::vertex::Normal3d,
vcg::vertex::Mark,
vcg::vertex::Qualityf,
vcg::vertex::BitFlags,
vcg::vertex::VFAdj,
vcg::vertex::VEAdj>
{};
class PEdge : public vcg::Edge<PUsedTypes,
vcg::edge::VertexRef,
vcg::edge::BitFlags,
vcg::edge::EEAdj,
vcg::edge::VEAdj,
vcg::edge::EVAdj,
vcg::edge::EEAdj
// vcg::edge::EFAdj
>
{};
class PFace
: public vcg::Face<
PUsedTypes,
vcg::face::PolyInfo // this is necessary if you use component in
// vcg/simplex/face/component_polygon.h
// It says "this class is a polygon and the memory for its components
// (e.g. pointer to its vertices will be allocated dynamically")
,
vcg::face::PFVAdj // Pointer to the vertices (just like FVAdj )
,
vcg::face::PFVAdj,
vcg::face::PFFAdj // Pointer to edge-adjacent face (just like FFAdj )
,
vcg::face::BitFlags // bit flags
,
vcg::face::Qualityf // quality
,
vcg::face::Normal3f // normal
> {};
class PFace : public vcg::Face<PUsedTypes,
vcg::face::PolyInfo // this is necessary if you use component in
// vcg/simplex/face/component_polygon.h
// It says "this class is a polygon and the memory for its components
// (e.g. pointer to its vertices will be allocated dynamically")
,
vcg::face::VFAdj,
vcg::face::FEAdj,
vcg::face::PFVAdj,
// vcg::face::PVFAdj,
vcg::face::PFFAdj // Pointer to edge-adjacent face (just like FFAdj )
,
vcg::face::BitFlags // bit flags
,
vcg::face::Qualityf // quality
,
vcg::face::Normal3f // normal
>
{};
class VCGPolyMesh
: public vcg::tri::TriMesh<std::vector<PVertex>, std::vector<PFace>>,
public Mesh {
: public vcg::tri::TriMesh<std::vector<PVertex>, std::vector<PFace>, std::vector<PEdge>>,
public Mesh
{
public:
virtual bool load(const std::string &filename) override {
int mask;
vcg::tri::io::Importer<VCGPolyMesh>::LoadMask(filename.c_str(), mask);
int error = vcg::tri::io::Importer<VCGPolyMesh>::Open(
*this, filename.c_str(), mask);
if (error != 0) {
return false;
}
// vcg::tri::io::ImporterOBJ<VCGPolyMesh>::Open();
// unsigned int mask = 0;
// mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_VERTCOORD;
// mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_VERTNORMAL;
// mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_VERTCOLOR;
// mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_EDGEINDEX;
// mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_FACEINDEX;
// if (nanoply::NanoPlyWrapper<VCGPolyMesh>::LoadModel(
// std::filesystem::absolute(filename).c_str(), *this, mask) !=
// 0) {
// std::cout << "Could not load tri mesh" << std::endl;
// virtual bool load(const std::string &filename) override {
// int mask;
// vcg::tri::io::Importer<VCGPolyMesh>::LoadMask(filename.c_str(), mask);
// int error = vcg::tri::io::Importer<VCGPolyMesh>::Open(
// *this, filename.c_str(), mask);
// if (error != 0) {
// return false;
// }
vcg::tri::UpdateTopology<VCGPolyMesh>::FaceFace(*this);
// vcg::tri::UpdateTopology<VCGPolyMesh>::VertexFace(*this);
vcg::tri::UpdateNormal<VCGPolyMesh>::PerVertexNormalized(*this);
return true;
// // vcg::tri::io::ImporterOBJ<VCGPolyMesh>::Open();
// // unsigned int mask = 0;
// // mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_VERTCOORD;
// // mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_VERTNORMAL;
// // mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_VERTCOLOR;
// // mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_EDGEINDEX;
// // mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_FACEINDEX;
// // if (nanoply::NanoPlyWrapper<VCGPolyMesh>::LoadModel(
// // std::filesystem::absolute(filename).c_str(), *this, mask) !=
// // 0) {
// // std::cout << "Could not load tri mesh" << std::endl;
// // }
// vcg::tri::UpdateTopology<VCGPolyMesh>::FaceFace(*this);
// vcg::tri::UpdateTopology<VCGPolyMesh>::VertexEdge(*this);
// // vcg::tri::UpdateTopology<VCGPolyMesh>::VertexFace(*this);
// vcg::tri::UpdateNormal<VCGPolyMesh>::PerVertexNormalized(*this);
// return true;
// }
Eigen::MatrixX3d getVertices() const
{
Eigen::MatrixX3d vertices(VN(), 3);
for (size_t vi = 0; vi < VN(); vi++) {
VCGPolyMesh::CoordType vertexCoordinates = vert[vi].cP();
vertices.row(vi) = vertexCoordinates.ToEigenVector<Eigen::Vector3d>();
}
return vertices;
}
std::vector<std::vector<int>> getFaces() const
{
std::vector<std::vector<int>> faces(FN());
for (const VCGPolyMesh::FaceType &f : this->face) {
const int fi = vcg::tri::Index<VCGPolyMesh>(*this, f);
for (size_t vi = 0; vi < f.VN(); vi++) {
const size_t viGlobal = vcg::tri::Index<VCGPolyMesh>(*this, f.cV(vi));
faces[fi].push_back(viGlobal);
}
}
return faces;
}
#ifdef POLYSCOPE_DEFINED
void registerForDrawing(){
std::shared_ptr<polyscope::SurfaceMesh> registerForDrawing(
const std::optional<std::array<double, 3>> &desiredColor = std::nullopt,
const bool &shouldEnable = true)
{
auto vertices = getVertices();
auto faces = getFaces();
initPolyscope();
std::shared_ptr<polyscope::SurfaceMesh> polyscopeHandle_mesh(
polyscope::registerSurfaceMesh(label, vertices, faces));
const double drawingRadius = 0.002;
polyscopeHandle_mesh->setEnabled(shouldEnable);
polyscopeHandle_mesh->setEdgeWidth(drawingRadius);
if (desiredColor.has_value()) {
const glm::vec3 desiredColor_glm(desiredColor.value()[0],
desiredColor.value()[1],
desiredColor.value()[2]);
polyscopeHandle_mesh->setSurfaceColor(desiredColor_glm);
}
return polyscopeHandle_mesh;
}
#endif
void moveToCenter()
{
CoordType centerOfMass(0, 0, 0);
for (int vi = 0; vi < VN(); vi++) {
centerOfMass += vert[vi].cP();
}
centerOfMass /= VN();
vcg::tri::UpdatePosition<VCGPolyMesh>::Translate(*this, -centerOfMass);
}
/*
* Returns the average distance from the center of each edge to the center of its face over the whole mesh
* */
double getAverageFaceRadius() const
{
double averageFaceRadius = 0;
for (int fi = 0; fi < FN(); fi++) {
const VCGPolyMesh::FaceType &f = face[fi];
CoordType centerOfFace(0, 0, 0);
for (int vi = 0; vi < f.VN(); vi++) {
centerOfFace = centerOfFace + f.cP(vi);
}
centerOfFace /= f.VN();
double faceRadius = 0;
// for (int face_ei = 0; face_ei < f.EN(); face_ei++) {
// std::cout << "fi:" << getIndex(f) << std::endl;
// auto vps = f.FVp(0);
// auto vpe = vps;
for (int i = 0; i < f.VN(); i++) {
faceRadius += vcg::Distance(centerOfFace, (f.cP0(i) + f.cP1(i)) / 2);
}
// }
const int faceEdges = f.VN(); //NOTE: When does this not hold?
faceRadius /= faceEdges;
averageFaceRadius += faceRadius;
}
averageFaceRadius /= FN();
return averageFaceRadius;
}
bool copy(VCGPolyMesh &copyFrom)
{
vcg::tri::Append<VCGPolyMesh, VCGPolyMesh>::MeshCopy(*this, copyFrom);
label = copyFrom.getLabel();
// eigenEdges = mesh.getEigenEdges();
// if (eigenEdges.rows() == 0) {
// getEdges(eigenEdges);
// }
// eigenVertices = mesh.getEigenVertices();
// if (eigenVertices.rows() == 0) {
// getVertices();
// }
vcg::tri::UpdateTopology<VCGPolyMesh>::VertexEdge(*this);
vcg::tri::UpdateTopology<VCGPolyMesh>::EdgeEdge(*this);
// vcg::tri::UpdateTopology<VCGPolyMesh>::VertexFace(*this);
return true;
}
VCGPolyMesh(VCGPolyMesh &copyFrom) { copy(copyFrom); }
VCGPolyMesh() {}
template<typename MeshElement>
size_t getIndex(const MeshElement &meshElement) const
{
return vcg::tri::Index<VCGPolyMesh>(*this, meshElement);
}
};
#endif // POLYMESH_HPP

View File

@ -0,0 +1,528 @@
#ifndef REDUCEDMODELOPTIMIZER_STRUCTS_HPP
#define REDUCEDMODELOPTIMIZER_STRUCTS_HPP
#include "csvfile.hpp"
#include "drmsimulationmodel.hpp"
#include "linearsimulationmodel.hpp"
#include "simulation_structs.hpp"
#include "unordered_map"
#include <string>
namespace ReducedPatternOptimization {
enum BaseSimulationScenario {
Axial,
Shear,
Bending,
Dome,
Saddle,
NumberOfBaseSimulationScenarios
};
inline static std::vector<std::string> baseSimulationScenarioNames{"Axial",
"Shear",
"Bending",
"Dome",
"Saddle"};
struct Colors
{
inline static std::array<double, 3> fullInitial{0.416666, 0.109804, 0.890196};
inline static std::array<double, 3> fullDeformed{0.583333, 0.890196, 0.109804};
inline static std::array<double, 3> reducedInitial{0.890196, 0.109804, 0.193138};
inline static std::array<double, 3> reducedDeformed{0.109804, 0.890196, 0.806863};
};
struct xRange{
std::string label;
double min;
double max;
std::string toString() const {
return label + "=[" + std::to_string(min) + "," + std::to_string(max) +
"]";
}
};
struct Settings {
enum NormalizationStrategy {
NonNormalized,
Epsilon,
MaxDisplacement,
EqualDisplacements
};
inline static vector<std::string> normalizationStrategyStrings{
"NonNormalized", "Epsilon", "MaxDsiplacement", "EqualDisplacements"};
std::vector<xRange> xRanges;
int numberOfFunctionCalls{100};
double solutionAccuracy{1e-2};
NormalizationStrategy normalizationStrategy{Epsilon};
double normalizationParameter{0.003};
std::string toString() const {
std::string settingsString;
if (!xRanges.empty()) {
std::string xRangesString;
for (const xRange &range : xRanges) {
xRangesString += range.toString() + " ";
}
settingsString += xRangesString;
}
settingsString +=
"FuncCalls=" + std::to_string(numberOfFunctionCalls) +
" Accuracy=" + std::to_string(solutionAccuracy) +
" Norm=" + normalizationStrategyStrings[normalizationStrategy];
return settingsString;
}
void writeHeaderTo(csvFile &os) const {
if (!xRanges.empty()) {
for (const xRange &range : xRanges) {
os << range.label + " max";
os << range.label + " min";
}
}
os << "Function Calls";
os << "Solution Accuracy";
os << "Normalization strategy";
// os << std::endl;
}
void writeSettingsTo(csvFile &os) const {
if (!xRanges.empty()) {
for (const xRange &range : xRanges) {
os << range.max;
os << range.min;
}
}
os << numberOfFunctionCalls;
os << solutionAccuracy;
os << normalizationStrategyStrings[normalizationStrategy] + "_" +
std::to_string(normalizationParameter);
}
};
inline void updateMeshWithOptimalVariables(const std::vector<double> &x, SimulationMesh &m)
{
const double E = x[0];
const double A = x[1];
const double beamWidth = std::sqrt(A);
const double beamHeight = beamWidth;
const double J = x[2];
const double I2 = x[3];
const double I3 = x[4];
for (EdgeIndex ei = 0; ei < m.EN(); ei++) {
Element &e = m.elements[ei];
e.setDimensions(RectangularBeamDimensions(beamWidth, beamHeight));
e.setMaterial(ElementMaterial(e.material.poissonsRatio, E));
e.J = J;
e.I2 = I2;
e.I3 = I3;
}
CoordType center_barycentric(1, 0, 0);
CoordType interfaceEdgeMiddle_barycentric(0, 0.5, 0.5);
CoordType movableVertex_barycentric((center_barycentric + interfaceEdgeMiddle_barycentric)
* x[x.size() - 2]);
CoordType patternCoord0 = CoordType(0, 0, 0);
double bottomEdgeHalfSize = 0.03 / std::tan(M_PI / 3);
CoordType interfaceNodePosition(0, -0.03, 0);
CoordType patternBottomRight = interfaceNodePosition + CoordType(bottomEdgeHalfSize, 0, 0);
CoordType patternBottomLeft = interfaceNodePosition - CoordType(bottomEdgeHalfSize, 0, 0);
vcg::Triangle3<double> baseTriangle(patternCoord0, patternBottomLeft, patternBottomRight);
CoordType baseTriangleMovableVertexPosition = baseTriangle.cP(0)
* movableVertex_barycentric[0]
+ baseTriangle.cP(1)
* movableVertex_barycentric[1]
+ baseTriangle.cP(2)
* movableVertex_barycentric[2];
VectorType patternPlaneNormal(0, 0, 1);
baseTriangleMovableVertexPosition = vcg::RotationMatrix(patternPlaneNormal,
vcg::math::ToRad(x[x.size() - 1]))
* baseTriangleMovableVertexPosition;
const int fanSize = 6;
for (int rotationCounter = 0; rotationCounter < fanSize; rotationCounter++) {
m.vert[2 * rotationCounter + 1].P() = vcg::RotationMatrix(patternPlaneNormal,
vcg::math::ToRad(
60.0 * rotationCounter))
* baseTriangleMovableVertexPosition;
}
m.reset();
}
struct Results
{
double time{-1};
int numberOfSimulationCrashes{0};
struct ObjectiveValues
{
double totalRaw;
double total;
std::vector<double> perSimulationScenario_rawTranslational;
std::vector<double> perSimulationScenario_rawRotational;
std::vector<double> perSimulationScenario_translational;
std::vector<double> perSimulationScenario_rotational;
std::vector<double> perSimulationScenario_total;
} objectiveValue;
// std::vector<std::pair<std::string,double>> optimalXNameValuePairs;
std::vector<std::pair<std::string, double>> optimalXNameValuePairs;
std::vector<std::shared_ptr<SimulationJob>> fullPatternSimulationJobs;
std::vector<std::shared_ptr<SimulationJob>> reducedPatternSimulationJobs;
vcg::Triangle3<double> baseTriangle;
struct JsonKeys
{
inline static std::string filename{"OptimizationResults.json"};
inline static std::string optimizationVariables{"OptimizationVariables"};
inline static std::string baseTriangle{"BaseTriangle"};
};
void save(const string &saveToPath) const
{
assert(std::filesystem::is_directory(saveToPath));
//clear directory
for (const auto &entry : std::filesystem::directory_iterator(saveToPath))
std::filesystem::remove_all(entry.path());
//Save optimal X
nlohmann::json json_optimizationResults;
std::string jsonValue_optimizationVariables;
for (const auto &optimalXNameValuePair : optimalXNameValuePairs) {
jsonValue_optimizationVariables.append(optimalXNameValuePair.first + ",");
}
jsonValue_optimizationVariables.pop_back(); // for deleting the last comma
json_optimizationResults[JsonKeys::optimizationVariables] = jsonValue_optimizationVariables;
for (const auto &optimalXNameValuePair : optimalXNameValuePairs) {
json_optimizationResults[optimalXNameValuePair.first] = optimalXNameValuePair.second;
}
//base triangle
json_optimizationResults[JsonKeys::baseTriangle]
= std::vector<double>{baseTriangle.cP0(0)[0],
baseTriangle.cP0(0)[1],
baseTriangle.cP0(0)[2],
baseTriangle.cP1(0)[0],
baseTriangle.cP1(0)[1],
baseTriangle.cP1(0)[2],
baseTriangle.cP2(0)[0],
baseTriangle.cP2(0)[1],
baseTriangle.cP2(0)[2]};
////Save to json file
std::filesystem::path jsonFilePath(
std::filesystem::path(saveToPath).append(JsonKeys::filename));
std::ofstream jsonFile_optimizationResults(jsonFilePath.string());
jsonFile_optimizationResults << json_optimizationResults;
/*TODO: Refactor since the meshes are saved for each simulation scenario although they do not change*/
//Save jobs and meshes for each simulation scenario
//Save the reduced and full patterns
const int numberOfSimulationJobs = fullPatternSimulationJobs.size();
for (int simulationScenarioIndex = 0; simulationScenarioIndex < numberOfSimulationJobs;
simulationScenarioIndex++) {
const std::shared_ptr<SimulationJob> &pFullPatternSimulationJob
= fullPatternSimulationJobs[simulationScenarioIndex];
std::filesystem::path simulationJobFolderPath(
std::filesystem::path(saveToPath).append(pFullPatternSimulationJob->label));
std::filesystem::create_directory(simulationJobFolderPath);
const auto fullPatternDirectoryPath = std::filesystem::path(simulationJobFolderPath)
.append("Full");
std::filesystem::create_directory(fullPatternDirectoryPath);
pFullPatternSimulationJob->save(fullPatternDirectoryPath.string());
const std::shared_ptr<SimulationJob> &pReducedPatternSimulationJob
= reducedPatternSimulationJobs[simulationScenarioIndex];
const auto reducedPatternDirectoryPath
= std::filesystem::path(simulationJobFolderPath).append("Reduced");
if (!std::filesystem::exists(reducedPatternDirectoryPath)) {
std::filesystem::create_directory(reducedPatternDirectoryPath);
}
pReducedPatternSimulationJob->save(reducedPatternDirectoryPath.string());
}
std::cout << "Saved optimization results to:" << saveToPath << std::endl;
}
template<typename MeshType>
static void applyOptimizationResults_innerHexagon(
const ReducedPatternOptimization::Results &reducedPattern_optimizationResults,
const vcg::Triangle3<double> &patternBaseTriangle,
MeshType &reducedPattern)
{
std::unordered_map<std::string, double>
optimalXVariables(reducedPattern_optimizationResults.optimalXNameValuePairs.begin(),
reducedPattern_optimizationResults.optimalXNameValuePairs.end());
assert(optimalXVariables.contains("HexSize") && optimalXVariables.contains("HexAngle"));
//Set optimal geometrical params of the reduced pattern
CoordType center_barycentric(1, 0, 0);
CoordType interfaceEdgeMiddle_barycentric(0, 0.5, 0.5);
CoordType movableVertex_barycentric((center_barycentric + interfaceEdgeMiddle_barycentric)
* optimalXVariables.at("HexSize"));
reducedPattern.vert[1].P() = patternBaseTriangle.cP(0) * movableVertex_barycentric[0]
+ patternBaseTriangle.cP(1) * movableVertex_barycentric[1]
+ patternBaseTriangle.cP(2) * movableVertex_barycentric[2];
reducedPattern.vert[1].P() = vcg::RotationMatrix(CoordType{0, 0, 1},
vcg::math::ToRad(
optimalXVariables.at("HexAngle")))
* reducedPattern.vert[1].cP();
}
static void applyOptimizationResults_elements(
const ReducedPatternOptimization::Results &reducedPattern_optimizationResults,
const shared_ptr<SimulationMesh> &pTiledReducedPattern_simulationMesh)
{
// Set reduced parameters fron the optimization results
std::unordered_map<std::string, double>
optimalXVariables(reducedPattern_optimizationResults.optimalXNameValuePairs.begin(),
reducedPattern_optimizationResults.optimalXNameValuePairs.end());
const std::string ALabel = "A";
assert(optimalXVariables.contains(ALabel));
const double A = optimalXVariables.at(ALabel);
const double beamWidth = std::sqrt(A);
const double beamHeight = beamWidth;
RectangularBeamDimensions elementDimensions(beamWidth, beamHeight);
const double poissonsRatio = 0.3;
const std::string ymLabel = "E";
assert(optimalXVariables.contains(ymLabel));
const double E = optimalXVariables.at(ymLabel);
const ElementMaterial elementMaterial(poissonsRatio, E);
const std::string JLabel = "J";
assert(optimalXVariables.contains(JLabel));
const double J = optimalXVariables.at(JLabel);
const std::string I2Label = "I2";
assert(optimalXVariables.contains(I2Label));
const double I2 = optimalXVariables.at(I2Label);
const std::string I3Label = "I3";
assert(optimalXVariables.contains(I3Label));
const double I3 = optimalXVariables.at(I3Label);
for (int ei = 0; ei < pTiledReducedPattern_simulationMesh->EN(); ei++) {
Element &e = pTiledReducedPattern_simulationMesh->elements[ei];
e.setDimensions(elementDimensions);
e.setMaterial(elementMaterial);
e.J = J;
e.I2 = I2;
e.I3 = I3;
}
pTiledReducedPattern_simulationMesh->reset();
}
void load(const string &loadFromPath)
{
assert(std::filesystem::is_directory(loadFromPath));
//Load optimal X
nlohmann::json json_optimizationResults;
std::ifstream ifs(std::filesystem::path(loadFromPath).append(JsonKeys::filename));
ifs >> json_optimizationResults;
std::string optimizationVariablesString = *json_optimizationResults.find(
JsonKeys::optimizationVariables);
std::string optimizationVariablesDelimeter = ",";
size_t pos = 0;
std::vector<std::string> optimizationVariablesNames;
while ((pos = optimizationVariablesString.find(optimizationVariablesDelimeter))
!= std::string::npos) {
const std::string variableName = optimizationVariablesString.substr(0, pos);
optimizationVariablesNames.push_back(variableName);
optimizationVariablesString.erase(0, pos + optimizationVariablesDelimeter.length());
}
optimizationVariablesNames.push_back(optimizationVariablesString); //add last variable name
optimalXNameValuePairs.resize(optimizationVariablesNames.size());
for (int xVariable_index = 0; xVariable_index < optimizationVariablesNames.size();
xVariable_index++) {
const std::string xVariable_name = optimizationVariablesNames[xVariable_index];
const double xVariable_value = *json_optimizationResults.find(xVariable_name);
optimalXNameValuePairs[xVariable_index] = std::make_pair(xVariable_name,
xVariable_value);
}
std::vector<double> baseTriangleVertices = json_optimizationResults.at(
JsonKeys::baseTriangle);
baseTriangle.P0(0) = CoordType(baseTriangleVertices[0],
baseTriangleVertices[1],
baseTriangleVertices[2]);
baseTriangle.P1(0) = CoordType(baseTriangleVertices[3],
baseTriangleVertices[4],
baseTriangleVertices[5]);
baseTriangle.P2(0) = CoordType(baseTriangleVertices[6],
baseTriangleVertices[7],
baseTriangleVertices[8]);
for (const auto &directoryEntry : filesystem::directory_iterator(loadFromPath)) {
const auto simulationScenarioPath = directoryEntry.path();
if (!std::filesystem::is_directory(simulationScenarioPath)) {
continue;
}
// Load reduced pattern files
for (const auto &fileEntry : filesystem::directory_iterator(
std::filesystem::path(simulationScenarioPath).append("Full"))) {
const auto filepath = fileEntry.path();
if (filepath.extension() == ".json") {
SimulationJob job;
job.load(filepath.string());
fullPatternSimulationJobs.push_back(std::make_shared<SimulationJob>(job));
}
}
// Load full pattern files
for (const auto &fileEntry : filesystem::directory_iterator(
std::filesystem::path(simulationScenarioPath).append("Reduced"))) {
const auto filepath = fileEntry.path();
if (filepath.extension() == ".json") {
SimulationJob job;
job.load(filepath.string());
applyOptimizationResults_innerHexagon(*this, baseTriangle, *job.pMesh);
applyOptimizationResults_elements(*this, job.pMesh);
reducedPatternSimulationJobs.push_back(std::make_shared<SimulationJob>(job));
}
}
}
}
#if POLYSCOPE_DEFINED
void draw() const {
initPolyscope();
DRMSimulationModel drmSimulator;
LinearSimulationModel linearSimulator;
assert(fullPatternSimulationJobs.size() ==
reducedPatternSimulationJobs.size());
fullPatternSimulationJobs[0]->pMesh->registerForDrawing(
Colors::fullInitial);
reducedPatternSimulationJobs[0]->pMesh->registerForDrawing(
Colors::reducedInitial, false);
const int numberOfSimulationJobs = fullPatternSimulationJobs.size();
for (int simulationJobIndex = 0;
simulationJobIndex < numberOfSimulationJobs; simulationJobIndex++) {
// Drawing of full pattern results
const std::shared_ptr<SimulationJob> &pFullPatternSimulationJob =
fullPatternSimulationJobs[simulationJobIndex];
pFullPatternSimulationJob->registerForDrawing(
fullPatternSimulationJobs[0]->pMesh->getLabel());
SimulationResults fullModelResults = drmSimulator.executeSimulation(
pFullPatternSimulationJob);
fullModelResults.registerForDrawing(Colors::fullDeformed, true, true);
// SimulationResults fullModelLinearResults =
// linearSimulator.executeSimulation(pFullPatternSimulationJob);
// fullModelLinearResults.setLabelPrefix("linear");
// fullModelLinearResults.registerForDrawing(Colors::fullDeformed,false);
// Drawing of reduced pattern results
const std::shared_ptr<SimulationJob> &pReducedPatternSimulationJob
= reducedPatternSimulationJobs[simulationJobIndex];
// SimulationResults reducedModelResults = drmSimulator.executeSimulation(
// pReducedPatternSimulationJob);
// reducedModelResults.registerForDrawing(Colors::reducedDeformed, false);
SimulationResults reducedModelLinearResults =
linearSimulator.executeSimulation(pReducedPatternSimulationJob);
reducedModelLinearResults.setLabelPrefix("linear");
reducedModelLinearResults.registerForDrawing(Colors::reducedDeformed, true, true);
polyscope::options::programName =
fullPatternSimulationJobs[0]->pMesh->getLabel();
polyscope::view::resetCameraToHomeView();
polyscope::show();
// Save a screensh
const std::string screenshotFilename =
"/home/iason/Coding/Projects/Approximating shapes with flat "
"patterns/RodModelOptimizationForPatterns/Results/Images/" +
fullPatternSimulationJobs[0]->pMesh->getLabel() + "_" +
pFullPatternSimulationJob->getLabel();
polyscope::screenshot(screenshotFilename, false);
fullModelResults.unregister();
// reducedModelResults.unregister();
reducedModelLinearResults.unregister();
// fullModelLinearResults.unregister();
// double error = computeError(
// reducedModelResults.displacements,fullModelResults.displacements,
// );
// std::cout << "Error of simulation scenario "
// <<
// simula simulationScenarioStrings[simulationScenarioIndex]
// << " is "
// << error << std::endl;
}
}
#endif // POLYSCOPE_DEFINED
void saveMeshFiles() const {
const int numberOfSimulationJobs = fullPatternSimulationJobs.size();
assert(numberOfSimulationJobs != 0 &&
fullPatternSimulationJobs.size() ==
reducedPatternSimulationJobs.size());
fullPatternSimulationJobs[0]->pMesh->save(
"undeformed " + fullPatternSimulationJobs[0]->pMesh->getLabel() + ".ply");
reducedPatternSimulationJobs[0]->pMesh->save(
"undeformed " + reducedPatternSimulationJobs[0]->pMesh->getLabel() + ".ply");
DRMSimulationModel simulator_drm;
LinearSimulationModel simulator_linear;
for (int simulationJobIndex = 0; simulationJobIndex < numberOfSimulationJobs;
simulationJobIndex++) {
// Drawing of full pattern results
const std::shared_ptr<SimulationJob> &pFullPatternSimulationJob
= fullPatternSimulationJobs[simulationJobIndex];
SimulationResults fullModelResults = simulator_drm.executeSimulation(
pFullPatternSimulationJob);
fullModelResults.saveDeformedModel();
// Drawing of reduced pattern results
const std::shared_ptr<SimulationJob> &pReducedPatternSimulationJob
= reducedPatternSimulationJobs[simulationJobIndex];
SimulationResults reducedModelResults = simulator_linear.executeSimulation(
pReducedPatternSimulationJob);
reducedModelResults.saveDeformedModel();
}
}
void writeHeaderTo(csvFile &os)
{
os << "Total raw Obj value";
os << "Total Obj value";
for (int simulationScenarioIndex = 0;
simulationScenarioIndex < fullPatternSimulationJobs.size();
simulationScenarioIndex++) {
const std::string simulationScenarioName
= fullPatternSimulationJobs[simulationScenarioIndex]->getLabel();
os << "Obj value Trans " + simulationScenarioName;
os << "Obj value Rot " + simulationScenarioName;
os << "Obj value Total " + simulationScenarioName;
}
for (const auto &nameValuePair : optimalXNameValuePairs) {
os << nameValuePair.first;
}
os << "Time(s)";
os << "#Crashes";
}
void writeResultsTo(const Settings &settings_optimization, csvFile &os) const
{
os << objectiveValue.totalRaw;
os << objectiveValue.total;
for (int simulationScenarioIndex = 0;
simulationScenarioIndex < fullPatternSimulationJobs.size();
simulationScenarioIndex++) {
os << objectiveValue.perSimulationScenario_translational[simulationScenarioIndex];
os << objectiveValue.perSimulationScenario_rotational[simulationScenarioIndex];
os << objectiveValue.perSimulationScenario_total[simulationScenarioIndex];
}
for (const auto &optimalXNameValuePair : optimalXNameValuePairs) {
os << optimalXNameValuePair.second;
}
os << time;
if (numberOfSimulationCrashes == 0) {
os << "-";
} else {
os << numberOfSimulationCrashes;
}
}
};
} // namespace ReducedPatternOptimization
#endif // REDUCEDMODELOPTIMIZER_STRUCTS_HPP

View File

@ -1,7 +1,7 @@
#ifndef SIMULATIONHISTORY_HPP
#define SIMULATIONHISTORY_HPP
#ifndef SIMULATIONSTRUCTS_HPP
#define SIMULATIONSTRUCTS_HPP
#include "elementalmesh.hpp"
#include "simulationmesh.hpp"
#include "nlohmann/json.hpp"
struct SimulationHistory {
@ -32,6 +32,7 @@ struct SimulationHistory {
}
};
namespace nlohmann {
template <> struct adl_serializer<std::unordered_map<VertexIndex, Vector6d>> {
@ -202,7 +203,7 @@ public:
std::filesystem::path(pathFolderDirectory)))
.append(pMesh->getLabel() + ".ply")
.string();
returnValue = pMesh->savePly(meshFilename);
returnValue = pMesh->save(meshFilename);
nlohmann::json json;
json[jsonLabels.meshFilename] = std::filesystem::relative(
std::filesystem::path(meshFilename),
@ -333,6 +334,8 @@ struct SimulationResults {
std::shared_ptr<SimulationJob> job;
SimulationHistory history;
std::vector<Vector6d> displacements;
std::vector<Eigen::Quaternion<double>> rotationalDisplacementQuaternion; //per vertex
double internalPotentialEnergy{0};
double executionTime{0};
std::string labelPrefix{"deformed"};
inline static char deliminator{' '};
@ -364,7 +367,7 @@ struct SimulationResults {
m.vert[vi].P() + CoordType(displacements[vi][0], displacements[vi][1],
displacements[vi][2]);
}
m.savePly(getLabel() + ".ply");
m.save(getLabel() + ".ply");
}
void save() {
@ -395,41 +398,71 @@ struct SimulationResults {
}
polyscope::removeCurveNetwork(getLabel());
}
void registerForDrawing(
const std::optional<glm::vec3> &desiredColor = std::nullopt,
const bool &shouldEnable = true) const {
polyscope::options::groundPlaneEnabled = false;
polyscope::view::upDir = polyscope::view::UpDir::ZUp;
const std::string branchName = "Branch:Polyscope";
polyscope::options::programName = branchName;
if (!polyscope::state::initialized) {
polyscope::init();
} /* else {
void registerForDrawing(const std::optional<std::array<double, 3>> &desiredColor = std::nullopt,
const bool &shouldEnable = true,
const bool &showFrames = false) const
{
polyscope::options::groundPlaneEnabled = false;
polyscope::view::upDir = polyscope::view::UpDir::ZUp;
if (!polyscope::state::initialized) {
polyscope::init();
} /* else {
polyscope::removeAllStructures();
}*/
// const std::string undeformedMeshName = "Undeformed_" + label;
// polyscope::registerCurveNetwork(
// undeformedMeshName, mesh->getEigenVertices(),
// mesh->getEigenEdges());
// const std::string undeformedMeshName = "Undeformed_" + label;
// polyscope::registerCurveNetwork(
// undeformedMeshName, mesh->getEigenVertices(),
// mesh->getEigenEdges());
const std::shared_ptr<SimulationMesh> &mesh = job->pMesh;
auto polyscopeHandle_deformedEdmeMesh = polyscope::registerCurveNetwork(
getLabel(), mesh->getEigenVertices(), mesh->getEigenEdges());
polyscopeHandle_deformedEdmeMesh->setEnabled(shouldEnable);
polyscopeHandle_deformedEdmeMesh->setRadius(0.002, true);
if (desiredColor.has_value()) {
polyscopeHandle_deformedEdmeMesh->setColor(desiredColor.value());
}
Eigen::MatrixX3d nodalDisplacements(mesh->VN(), 3);
for (VertexIndex vi = 0; vi < mesh->VN(); vi++) {
const Vector6d &nodalDisplacement = displacements[vi];
nodalDisplacements.row(vi) = Eigen::Vector3d(
nodalDisplacement[0], nodalDisplacement[1], nodalDisplacement[2]);
}
polyscopeHandle_deformedEdmeMesh->updateNodePositions(
mesh->getEigenVertices() + nodalDisplacements);
const std::shared_ptr<SimulationMesh> &mesh = job->pMesh;
auto polyscopeHandle_deformedEdmeMesh
= polyscope::registerCurveNetwork(getLabel(),
mesh->getEigenVertices(),
mesh->getEigenEdges());
polyscopeHandle_deformedEdmeMesh->setEnabled(shouldEnable);
polyscopeHandle_deformedEdmeMesh->setRadius(0.002, true);
if (desiredColor.has_value()) {
const glm::vec3 desiredColor_glm(desiredColor.value()[0],
desiredColor.value()[1],
desiredColor.value()[2]);
polyscopeHandle_deformedEdmeMesh->setColor(desiredColor_glm);
}
Eigen::MatrixX3d nodalDisplacements(mesh->VN(), 3);
Eigen::MatrixX3d framesX(mesh->VN(), 3);
Eigen::MatrixX3d framesY(mesh->VN(), 3);
Eigen::MatrixX3d framesZ(mesh->VN(), 3);
for (VertexIndex vi = 0; vi < mesh->VN(); vi++) {
const Vector6d &nodalDisplacement = displacements[vi];
nodalDisplacements.row(vi) = Eigen::Vector3d(nodalDisplacement[0],
nodalDisplacement[1],
nodalDisplacement[2]);
auto deformedNormal = rotationalDisplacementQuaternion[vi] * Eigen::Vector3d(0, 0, 1);
auto deformedFrameY = rotationalDisplacementQuaternion[vi] * Eigen::Vector3d(0, 1, 0);
auto deformedFrameX = rotationalDisplacementQuaternion[vi] * Eigen::Vector3d(1, 0, 0);
framesX.row(vi) = Eigen::Vector3d(deformedFrameX[0], deformedFrameX[1], deformedFrameX[2]);
framesY.row(vi) = Eigen::Vector3d(deformedFrameY[0], deformedFrameY[1], deformedFrameY[2]);
framesZ.row(vi) = Eigen::Vector3d(deformedNormal[0], deformedNormal[1], deformedNormal[2]);
}
polyscopeHandle_deformedEdmeMesh->updateNodePositions(mesh->getEigenVertices()
+ nodalDisplacements);
job->registerForDrawing(getLabel());
polyscopeHandle_deformedEdmeMesh->addNodeVectorQuantity("FrameX", framesX)
->setVectorLengthScale(0.1)
->setVectorRadius(0.01)
->setVectorColor(/*polyscope::getNextUniqueColor()*/ glm::vec3(1, 0, 0))
->setEnabled(showFrames);
polyscopeHandle_deformedEdmeMesh->addNodeVectorQuantity("FrameY", framesY)
->setVectorLengthScale(0.1)
->setVectorRadius(0.01)
->setVectorColor(/*polyscope::getNextUniqueColor()*/ glm::vec3(0, 1, 0))
->setEnabled(showFrames);
polyscopeHandle_deformedEdmeMesh->addNodeVectorQuantity("FrameZ", framesZ)
->setVectorLengthScale(0.1)
->setVectorRadius(0.01)
->setVectorColor(/*polyscope::getNextUniqueColor()*/ glm::vec3(0, 0, 1))
->setEnabled(showFrames);
job->registerForDrawing(getLabel());
}
#endif
};

View File

@ -1,8 +1,8 @@
#ifndef SIMULATIONHISTORYPLOTTER_HPP
#define SIMULATIONHISTORYPLOTTER_HPP
#include "elementalmesh.hpp"
#include "simulationresult.hpp"
#include "simulationmesh.hpp"
#include "simulation_structs.hpp"
#include "utilities.hpp"
#include <algorithm>
#include <matplot/matplot.h>

View File

@ -1,83 +1,59 @@
#include "elementalmesh.hpp"
#include "simulationmesh.hpp"
#include <wrap/nanoply/include/nanoplyWrapper.hpp>
SimulationMesh::SimulationMesh() {
elements = vcg::tri::Allocator<VCGEdgeMesh>::GetPerEdgeAttribute<Element>(
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(
*this, std::string("Elements"));
nodes = vcg::tri::Allocator<VCGEdgeMesh>::GetPerVertexAttribute<Node>(
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(
*this, std::string("Nodes"));
}
SimulationMesh::SimulationMesh(VCGEdgeMesh &mesh) {
vcg::tri::MeshAssert<VCGEdgeMesh>::VertexNormalNormalized(mesh);
// bool containsNormals = true;
// for (VertexIterator vi = mesh.vert.begin(); vi != mesh.vert.end(); ++vi)
// if (!vi->IsD()) {
// if (fabs(vi->cN().Norm() - 1.0) > 0.000001) {
// containsNormals = false;
// break;
// }
// }
// if (!containsNormals) {
// for (VertexIterator vi = mesh.vert.begin(); vi != mesh.vert.end(); ++vi)
// if (!vi->IsD()) {
// vi->N() = CoordType(1, 0, 0);
// }
// }
VCGEdgeMesh::copy(mesh);
vcg::tri::Append<VCGEdgeMesh, ConstVCGEdgeMesh>::MeshCopy(*this, mesh);
elements = vcg::tri::Allocator<VCGEdgeMesh>::GetPerEdgeAttribute<Element>(
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(
*this, std::string("Elements"));
nodes = vcg::tri::Allocator<VCGEdgeMesh>::GetPerVertexAttribute<Node>(
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(
*this, std::string("Nodes"));
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
initializeNodes();
initializeElements();
label = mesh.getLabel();
eigenEdges = mesh.getEigenEdges();
eigenVertices = mesh.getEigenVertices();
}
SimulationMesh::~SimulationMesh() {
vcg::tri::Allocator<VCGEdgeMesh>::DeletePerEdgeAttribute<Element>(*this,
vcg::tri::Allocator<SimulationMesh>::DeletePerEdgeAttribute<Element>(*this,
elements);
vcg::tri::Allocator<VCGEdgeMesh>::DeletePerVertexAttribute<Node>(*this,
vcg::tri::Allocator<SimulationMesh>::DeletePerVertexAttribute<Node>(*this,
nodes);
}
SimulationMesh::SimulationMesh(PatternGeometry &pattern) {
vcg::tri::MeshAssert<PatternGeometry>::VertexNormalNormalized(pattern);
VCGEdgeMesh::copy(pattern);
vcg::tri::Append<VCGEdgeMesh, ConstVCGEdgeMesh>::MeshCopy(*this, pattern);
elements = vcg::tri::Allocator<VCGEdgeMesh>::GetPerEdgeAttribute<Element>(
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(
*this, std::string("Elements"));
nodes = vcg::tri::Allocator<VCGEdgeMesh>::GetPerVertexAttribute<Node>(
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(
*this, std::string("Nodes"));
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
initializeNodes();
initializeElements();
label = pattern.getLabel();
eigenEdges = pattern.getEigenEdges();
eigenVertices = pattern.getEigenVertices();
}
SimulationMesh::SimulationMesh(SimulationMesh &mesh) {
vcg::tri::Append<VCGEdgeMesh, ConstVCGEdgeMesh>::MeshCopy(*this, mesh);
elements = vcg::tri::Allocator<VCGEdgeMesh>::GetPerEdgeAttribute<Element>(
vcg::tri::MeshAssert<SimulationMesh>::VertexNormalNormalized(mesh);
VCGEdgeMesh::copy(mesh);
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(
*this, std::string("Elements"));
nodes = vcg::tri::Allocator<VCGEdgeMesh>::GetPerVertexAttribute<Node>(
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(
*this, std::string("Nodes"));
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
initializeNodes();
for (size_t ei = 0; ei < EN(); ei++) {
elements[ei] = mesh.elements[ei];
}
label = mesh.label;
eigenEdges = mesh.getEigenEdges();
eigenVertices = mesh.getEigenVertices();
reset();
}
void SimulationMesh::computeElementalProperties() {
@ -106,36 +82,48 @@ void SimulationMesh::initializeNodes() {
node.displacements[3] =
v.cN()[0]; // initialize nx diplacement with vertex normal x
// component
node.displacements[4] =
v.cN()[1]; // initialize ny(in the paper) diplacement with vertex
node.displacements[4] = v.cN()[1]; // initialize ny(in the paper) diplacement with vertex
// normal
// y component.
// Initialize incident elements
std::vector<VCGEdgeMesh::EdgePointer> incidentElements;
vcg::edge::VEStarVE(&v, incidentElements);
assert(
vcg::tri::IsValidPointer<SimulationMesh>(*this, incidentElements[0]) &&
incidentElements.size() > 0);
nodes[v].incidentElements = incidentElements;
node.referenceElement = getReferenceElement(v);
// Initialze alpha angles
// assert(
// vcg::tri::IsValidPointer<SimulationMesh>(*this, incidentElements[0]) &&
// incidentElements.size() > 0);
if (incidentElements.size() != 0) {
nodes[v].incidentElements = incidentElements;
node.referenceElement = getReferenceElement(v);
// std::vector<int> incidentElementsIndices(node.incidentElements.size());
// if (drawGlobal && vi == 5) {
// std::vector<glm::vec3> edgeColors(EN(), glm::vec3(0, 1, 0));
// std::vector<glm::vec3> vertexColors(VN(), glm::vec3(0, 1, 0));
// vertexColors[vi] = glm::vec3(0, 0, 1);
// for (int iei = 0; iei < incidentElementsIndices.size(); iei++) {
// incidentElementsIndices[iei] = this->getIndex(node.incidentElements[iei]);
// edgeColors[incidentElementsIndices[iei]] = glm::vec3(1, 0, 0);
// }
// polyHandle->addEdgeColorQuantity("chosenE", edgeColors)->setEnabled(true);
// polyHandle->addNodeColorQuantity("chosenV", vertexColors)->setEnabled(true);
// draw();
// }
// const int referenceElementIndex = getIndex(node.referenceElement);
// Initialze alpha angles
const EdgeType &referenceElement = *getReferenceElement(v);
const VectorType t01 =
computeT1Vector(referenceElement.cP(0), referenceElement.cP(1));
const VectorType f01 = (t01 - (v.cN() * (t01.dot(v.cN())))).Normalize();
const EdgeType &referenceElement = *node.referenceElement;
const VectorType t01 = computeT1Vector(referenceElement.cP(0), referenceElement.cP(1));
const VectorType f01 = (t01 - (v.cN() * (t01.dot(v.cN())))).Normalize();
for (const VCGEdgeMesh::EdgePointer &ep : nodes[v].incidentElements) {
assert(referenceElement.cV(0) == ep->cV(0) ||
referenceElement.cV(0) == ep->cV(1) ||
referenceElement.cV(1) == ep->cV(0) ||
referenceElement.cV(1) == ep->cV(1));
const VectorType t1 = computeT1Vector(*ep);
const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize();
const EdgeIndex ei = getIndex(ep);
const double alphaAngle = computeAngle(f01, f1, v.cN());
node.alphaAngles[ei] = alphaAngle;
for (const VCGEdgeMesh::EdgePointer &ep : nodes[v].incidentElements) {
assert(referenceElement.cV(0) == ep->cV(0) || referenceElement.cV(0) == ep->cV(1)
|| referenceElement.cV(1) == ep->cV(0) || referenceElement.cV(1) == ep->cV(1));
const VectorType t1 = computeT1Vector(*ep);
const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize();
const EdgeIndex ei = getIndex(ep);
const double alphaAngle = computeAngle(f01, f1, v.cN());
node.alphaAngles[ei] = alphaAngle;
}
}
}
}
@ -159,29 +147,24 @@ void SimulationMesh::reset() {
node.initialNormal = v.cN();
node.derivativeOfNormal.resize(6, VectorType(0, 0, 0));
node.displacements[3] =
v.cN()[0]; // initialize nx diplacement with vertex normal x
// component
node.displacements[4] =
v.cN()[1]; // initialize ny(in the paper) diplacement with vertex
node.displacements[3] = v.cN()[0]; // initialize nx diplacement with vertex normal x
// component
node.displacements[4] = v.cN()[1]; // initialize ny(in the paper) diplacement with vertex
// normal
// y component.
const EdgeType &referenceElement = *getReferenceElement(v);
const VectorType t01 =
computeT1Vector(referenceElement.cP(0), referenceElement.cP(1));
const VectorType t01 = computeT1Vector(referenceElement.cP(0), referenceElement.cP(1));
const VectorType f01 = (t01 - (v.cN() * (t01.dot(v.cN())))).Normalize();
for (const VCGEdgeMesh::EdgePointer &ep : nodes[v].incidentElements) {
assert(referenceElement.cV(0) == ep->cV(0) ||
referenceElement.cV(0) == ep->cV(1) ||
referenceElement.cV(1) == ep->cV(0) ||
referenceElement.cV(1) == ep->cV(1));
const VectorType t1 = computeT1Vector(*ep);
const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize();
const EdgeIndex ei = getIndex(ep);
const double alphaAngle = computeAngle(f01, f1, v.cN());
node.alphaAngles[ei] = alphaAngle;
assert(referenceElement.cV(0) == ep->cV(0) || referenceElement.cV(0) == ep->cV(1)
|| referenceElement.cV(1) == ep->cV(0) || referenceElement.cV(1) == ep->cV(1));
const VectorType t1 = computeT1Vector(*ep);
const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize();
const EdgeIndex ei = getIndex(ep);
const double alphaAngle = computeAngle(f01, f1, v.cN());
node.alphaAngles[ei] = alphaAngle;
}
}
}
@ -358,38 +341,44 @@ bool SimulationMesh::load(const string &plyFilename) {
return true;
}
bool SimulationMesh::savePly(const std::string &plyFilename) {
nanoply::NanoPlyWrapper<VCGEdgeMesh>::CustomAttributeDescriptor customAttrib;
customAttrib.GetMeshAttrib(plyFilename);
bool SimulationMesh::save(const std::string &plyFilename)
{
nanoply::NanoPlyWrapper<VCGEdgeMesh>::CustomAttributeDescriptor customAttrib;
customAttrib.GetMeshAttrib(plyFilename);
std::vector<CrossSectionType> dimensions = getBeamDimensions();
customAttrib.AddEdgeAttribDescriptor<CrossSectionType, double, 2>(
plyPropertyBeamDimensionsID, nanoply::NNP_LIST_INT8_FLOAT64,
dimensions.data());
std::vector<ElementMaterial> material = getBeamMaterial();
customAttrib.AddEdgeAttribDescriptor<vcg::Point2d, double, 2>(
plyPropertyBeamMaterialID, nanoply::NNP_LIST_INT8_FLOAT64,
material.data());
unsigned int mask = 0;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEATTRIB;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::SaveModel(
plyFilename.c_str(), *this, mask, customAttrib, false) != 1) {
return false;
}
return true;
std::vector<CrossSectionType> dimensions = getBeamDimensions();
customAttrib.AddEdgeAttribDescriptor<CrossSectionType, double, 2>(plyPropertyBeamDimensionsID,
nanoply::NNP_LIST_INT8_FLOAT64,
dimensions.data());
std::vector<ElementMaterial> material = getBeamMaterial();
customAttrib.AddEdgeAttribDescriptor<vcg::Point2d, double, 2>(plyPropertyBeamMaterialID,
nanoply::NNP_LIST_INT8_FLOAT64,
material.data());
unsigned int mask = 0;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEATTRIB;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::SaveModel(plyFilename.c_str(),
*this,
mask,
customAttrib,
false)
!= 1) {
return false;
}
return true;
}
SimulationMesh::EdgePointer
SimulationMesh::getReferenceElement(const VCGEdgeMesh::VertexType &v) {
const VertexIndex vi = getIndex(v);
return nodes[v].incidentElements[0];
const VertexIndex vi = getIndex(v);
return nodes[v].incidentElements[0];
}
VectorType computeT1Vector(const SimulationMesh::EdgeType &e) {
return computeT1Vector(e.cP(0), e.cP(1));
VectorType computeT1Vector(const SimulationMesh::EdgeType &e)
{
return computeT1Vector(e.cP(0), e.cP(1));
}
VectorType computeT1Vector(const CoordType &p0, const CoordType &p1) {

View File

@ -1,10 +1,11 @@
#ifndef ELEMENTALMESH_HPP
#define ELEMENTALMESH_HPP
#ifndef SIMULATIONMESH_HPP
#define SIMULATIONMESH_HPP
#include "Eigen/Dense"
#include "edgemesh.hpp"
#include "trianglepatterngeometry.hpp"
//extern bool drawGlobal;
struct Element;
struct Node;
using CrossSectionType = RectangularBeamDimensions;
@ -28,6 +29,7 @@ public:
SimulationMesh(ConstVCGEdgeMesh &edgeMesh);
SimulationMesh(SimulationMesh &elementalMesh);
void updateElementalLengths();
void updateIncidentElements();
std::vector<VCGEdgeMesh::EdgePointer>
getIncidentElements(const VCGEdgeMesh::VertexType &v);
@ -42,7 +44,7 @@ public:
double totalResidualForcesNorm{0};
double currentTotalPotentialEnergykN{0};
double previousTotalPotentialEnergykN{0};
bool savePly(const std::string &plyFilename);
bool save(const std::string &plyFilename);
void setBeamCrossSection(const CrossSectionType &beamDimensions);
void setBeamMaterial(const double &pr, const double &ym);
void reset();
@ -162,7 +164,7 @@ struct Node {
Element::LocalFrame computeElementFrame(const CoordType &p0,
const CoordType &p1,
const VectorType &elementNormal);
VectorType computeT1Vector(const ::SimulationMesh::EdgeType &e);
VectorType computeT1Vector(const SimulationMesh::EdgeType &e);
VectorType computeT1Vector(const CoordType &p0, const CoordType &p1);
double computeAngle(const VectorType &vector0, const VectorType &vector1,

View File

@ -1,397 +0,0 @@
#ifndef SIMULATIONHISTORY_HPP
#define SIMULATIONHISTORY_HPP
#include "elementalmesh.hpp"
#include "nlohmann/json.hpp"
struct SimulationHistory {
SimulationHistory() {}
size_t numberOfSteps{0};
std::string label;
std::vector<double> residualForces;
std::vector<double> kineticEnergy;
std::vector<double> potentialEnergies;
std::vector<size_t> redMarks;
std::vector<double> greenMarks;
void markRed(const size_t &stepNumber) { redMarks.push_back(stepNumber); }
void markGreen(const size_t &stepNumber) { greenMarks.push_back(stepNumber); }
void stepPulse(const SimulationMesh &mesh) {
kineticEnergy.push_back(log(mesh.currentTotalKineticEnergy));
// potentialEnergy.push_back(mesh.totalPotentialEnergykN);
residualForces.push_back(mesh.totalResidualForcesNorm);
}
void clear() {
residualForces.clear();
kineticEnergy.clear();
potentialEnergies.clear();
}
};
namespace nlohmann {
template <> struct adl_serializer<std::unordered_map<VertexIndex, Vector6d>> {
static void to_json(json &j,
const std::unordered_map<VertexIndex, Vector6d> &value) {
// calls the "to_json" method in T's namespace
}
static void from_json(const nlohmann::json &j,
std::unordered_map<VertexIndex, Vector6d> &m) {
std::cout << "Entered." << std::endl;
for (const auto &p : j) {
m.emplace(p.at(0).template get<VertexIndex>(),
p.at(1).template get<std::array<double, 6>>());
}
}
};
} // namespace nlohmann
class SimulationJob {
// const std::unordered_map<VertexIndex, VectorType> nodalForcedNormals;
// json labels
struct JSONLabels {
inline static std::string meshFilename{"mesh filename"};
inline static std::string constrainedVertices{"fixed vertices"};
inline static std::string nodalForces{"forces"};
inline static std::string label{"label"};
inline static std::string meshLabel{
"label"}; // TODO: should be in the savePly function of the simulation
// mesh class
} jsonLabels;
public:
std::shared_ptr<SimulationMesh> pMesh;
std::string label{"empty_job"};
std::unordered_map<VertexIndex, std::unordered_set<int>> constrainedVertices;
std::unordered_map<VertexIndex, Vector6d> nodalExternalForces;
std::unordered_map<VertexIndex, Eigen::Vector3d> nodalForcedDisplacements;
SimulationJob(
const std::shared_ptr<SimulationMesh> &m, const std::string &label,
const std::unordered_map<VertexIndex, std::unordered_set<int>> &cv,
const std::unordered_map<VertexIndex, Vector6d> &ef = {},
const std::unordered_map<VertexIndex, Eigen::Vector3d> &fd = {})
: pMesh(m), label(label), constrainedVertices(cv),
nodalExternalForces(ef), nodalForcedDisplacements(fd) {}
SimulationJob() {}
SimulationJob(const std::string &jsonFilename) { load(jsonFilename); }
std::string getLabel() const { return label; }
std::string toString() const {
nlohmann::json json;
if (!constrainedVertices.empty()) {
json[jsonLabels.constrainedVertices] = constrainedVertices;
}
if (!nodalExternalForces.empty()) {
std::unordered_map<VertexIndex, std::array<double, 6>> arrForces;
for (const auto &f : nodalExternalForces) {
arrForces[f.first] = f.second;
}
json[jsonLabels.nodalForces] = arrForces;
}
return json.dump();
}
void registerForDrawing(const std::string &meshLabel) const {
initPolyscope();
if (meshLabel.empty()) {
assert(false);
std::cerr << "Expects a mesh label on which to draw the simulation job."
<< std::endl;
return;
}
auto structs = polyscope::state::structures;
if (!polyscope::hasCurveNetwork(meshLabel)) {
assert(false);
std::cerr << "Expects mesh already being registered to draw the "
"simulation job. No struct named " +
meshLabel
<< std::endl;
return;
}
std::vector<std::array<double, 3>> nodeColors(pMesh->VN());
for (auto fixedVertex : constrainedVertices) {
nodeColors[fixedVertex.first] = {0, 0, 1};
}
if (!nodalForcedDisplacements.empty()) {
for (std::pair<VertexIndex, Eigen::Vector3d> viDisplPair :
nodalForcedDisplacements) {
const VertexIndex vi = viDisplPair.first;
nodeColors[vi][0] += 1;
nodeColors[vi][0] /= 2;
nodeColors[vi][1] += 0;
nodeColors[vi][1] /= 2;
nodeColors[vi][2] += 0;
nodeColors[vi][2] /= 2;
}
}
std::for_each(nodeColors.begin(), nodeColors.end(),
[](std::array<double, 3> &color) {
const double norm =
sqrt(std::pow(color[0], 2) + std::pow(color[1], 2) +
std::pow(color[2], 2));
if (norm > std::pow(10, -7)) {
color[0] /= norm;
color[1] /= norm;
color[2] /= norm;
}
});
auto cn = polyscope::getCurveNetwork(meshLabel);
if (!nodeColors.empty()) {
polyscope::getCurveNetwork(meshLabel)
->addNodeColorQuantity("Boundary conditions_" + label, nodeColors)
->setEnabled(false);
}
// per node external forces
std::vector<std::array<double, 3>> externalForces(pMesh->VN());
for (const auto &forcePair : nodalExternalForces) {
auto index = forcePair.first;
auto force = forcePair.second;
externalForces[index] = {force[0], force[1], force[2]};
}
if (!externalForces.empty()) {
polyscope::getCurveNetwork(meshLabel)
->addNodeVectorQuantity("External force_" + label, externalForces)
->setEnabled(false);
}
}
bool load(const std::string &jsonFilename) {
if (std::filesystem::path(jsonFilename).extension() != ".json") {
std::cerr << "A json file is expected as input. The given file has the "
"following extension:"
<< std::filesystem::path(jsonFilename).extension() << std::endl;
assert(false);
return false;
}
if (!std::filesystem::exists(std::filesystem::path(jsonFilename))) {
std::cerr << "The json file does not exist. Json file provided:"
<< jsonFilename << std::endl;
assert(false);
return false;
}
std::cout << "Loading json file:" << jsonFilename << std::endl;
nlohmann::json json;
std::ifstream ifs(jsonFilename);
json << ifs;
pMesh = std::make_shared<SimulationMesh>();
if (json.contains(jsonLabels.meshFilename)) {
pMesh->loadPly(json[jsonLabels.meshFilename]);
}
if (json.contains(jsonLabels.constrainedVertices)) {
constrainedVertices =
// auto conV =
<<<<<<< HEAD
std::unordered_map<VertexIndex, std::unordered_set<int>>(
json[jsonLabels.constrainedVertices]);
=======
json[jsonLabel_constrainedVertices].get<std::unordered_map<VertexIndex, std::unordered_set<int>>>();
>>>>>>> 2599d472614f403f8249060272ce288033a919d4
std::cout << "Loaded constrained vertices. Number of constrained "
"vertices found:"
<< constrainedVertices.size() << std::endl;
}
<<<<<<< HEAD
if (json.contains(jsonLabels.nodalForces)) {
auto f = std::unordered_map<VertexIndex, std::array<double, 6>>(
json[jsonLabels.nodalForces]);
=======
if (json.contains(jsonLabel_nodalForces)) {
auto f (
json[jsonLabel_nodalForces].get<std::unordered_map<VertexIndex, std::array<double, 6>>>());
>>>>>>> 2599d472614f403f8249060272ce288033a919d4
for (const auto &forces : f) {
nodalExternalForces[forces.first] = Vector6d(forces.second);
}
std::cout << "Loaded forces. Number of forces found:"
<< nodalExternalForces.size() << std::endl;
}
if (json.contains(jsonLabels.label)) {
label = json[jsonLabels.label];
}
if (json.contains(jsonLabels.meshLabel)) {
pMesh->setLabel(json[jsonLabels.meshLabel]);
}
return true;
}
bool save(const std::string &folderDirectory) const {
const std::filesystem::path pathFolderDirectory(folderDirectory);
if (!std::filesystem::is_directory(pathFolderDirectory)) {
std::cerr << "A folder directory is expected for saving the simulation "
"job. Exiting.."
<< std::endl;
return false;
}
bool returnValue = true;
const std::string meshFilename =
std::filesystem::absolute(
std::filesystem::canonical(
std::filesystem::path(pathFolderDirectory)))
.append(pMesh->getLabel() + ".ply").string();
returnValue = pMesh->savePly(meshFilename);
nlohmann::json json;
json[jsonLabels.meshFilename] = meshFilename;
if (!constrainedVertices.empty()) {
json[jsonLabels.constrainedVertices] = constrainedVertices;
}
if (!nodalExternalForces.empty()) {
std::unordered_map<VertexIndex, std::array<double, 6>> arrForces;
for (const auto &f : nodalExternalForces) {
arrForces[f.first] = f.second;
}
json[jsonLabels.nodalForces] = arrForces;
}
if (!label.empty()) {
json[jsonLabels.label] = label;
}
if (!pMesh->getLabel().empty()) {
json[jsonLabels.meshLabel] = pMesh->getLabel();
}
std::string jsonFilename(
std::filesystem::path(pathFolderDirectory)
<<<<<<< HEAD
.append(label + "_" + pMesh->getLabel() + "_simulationJob.json"));
=======
.append(pMesh->getLabel() + "_simScenario.json").string());
>>>>>>> 2599d472614f403f8249060272ce288033a919d4
std::ofstream jsonFile(jsonFilename);
jsonFile << json;
std::cout << "Saved simulation job as:" << jsonFilename << std::endl;
return returnValue;
}
};
namespace Eigen {
template <class Matrix>
void write_binary(const std::string &filename, const Matrix &matrix) {
std::ofstream out(filename,
std::ios::out | std::ios::binary | std::ios::trunc);
typename Matrix::Index rows = matrix.rows(), cols = matrix.cols();
out.write((char *)(&rows), sizeof(typename Matrix::Index));
out.write((char *)(&cols), sizeof(typename Matrix::Index));
out.write((char *)matrix.data(),
rows * cols * sizeof(typename Matrix::Scalar));
out.close();
}
template <class Matrix>
void read_binary(const std::string &filename, Matrix &matrix) {
std::ifstream in(filename, std::ios::in | std::ios::binary);
typename Matrix::Index rows = 0, cols = 0;
in.read((char *)(&rows), sizeof(typename Matrix::Index));
in.read((char *)(&cols), sizeof(typename Matrix::Index));
matrix.resize(rows, cols);
in.read((char *)matrix.data(), rows * cols * sizeof(typename Matrix::Scalar));
in.close();
}
} // namespace Eigen
struct SimulationResults {
bool converged{true};
std::shared_ptr<SimulationJob> job;
SimulationHistory history;
std::vector<Vector6d> displacements;
double executionTime{0};
std::string labelPrefix{"deformed"};
inline static char deliminator{' '};
void setLabelPrefix(const std::string &lp) {
labelPrefix += deliminator + lp;
}
std::string getLabel() const {
return labelPrefix + deliminator + job->pMesh->getLabel() + deliminator +
job->getLabel();
}
void unregister() const {
if (!polyscope::hasCurveNetwork(getLabel())) {
std::cerr << "No curve network registered with a name: " << getLabel()
<< std::endl;
std::cerr << "Nothing to remove." << std::endl;
return;
}
polyscope::removeCurveNetwork(getLabel());
}
void registerForDrawing() const {
polyscope::options::groundPlaneEnabled = false;
polyscope::view::upDir = polyscope::view::UpDir::ZUp;
const std::string branchName = "Branch:Polyscope";
polyscope::options::programName = branchName;
if (!polyscope::state::initialized) {
polyscope::init();
} /* else {
polyscope::removeAllStructures();
}*/
// const std::string undeformedMeshName = "Undeformed_" + label;
// polyscope::registerCurveNetwork(
// undeformedMeshName, mesh->getEigenVertices(),
// mesh->getEigenEdges());
const std::shared_ptr<SimulationMesh> &mesh = job->pMesh;
polyscope::registerCurveNetwork(getLabel(), mesh->getEigenVertices(),
mesh->getEigenEdges())
->setRadius(0.0007, false);
Eigen::MatrixX3d nodalDisplacements(mesh->VN(), 3);
for (VertexIndex vi = 0; vi < mesh->VN(); vi++) {
const Vector6d &nodalDisplacement = displacements[vi];
nodalDisplacements.row(vi) = Eigen::Vector3d(
nodalDisplacement[0], nodalDisplacement[1], nodalDisplacement[2]);
}
polyscope::getCurveNetwork(getLabel())
->updateNodePositions(mesh->getEigenVertices() + nodalDisplacements);
job->registerForDrawing(getLabel());
}
void saveDeformedModel() {
VCGEdgeMesh m;
vcg::tri::Append<VCGEdgeMesh, SimulationMesh>::MeshCopy(m, *job->pMesh);
for (int vi = 0; vi < m.VN(); vi++) {
m.vert[vi].P() =
m.vert[vi].P() + CoordType(displacements[vi][0], displacements[vi][1],
displacements[vi][2]);
}
m.savePly(getLabel() + ".ply");
}
void save() {
const std::string filename(getLabel() + "_displacements.eigenBin");
Eigen::MatrixXd m = Utilities::toEigenMatrix(displacements);
Eigen::write_binary(filename, m);
}
// The comparison of the results happens comparing the 6-dof nodal
// displacements
bool isEqual(const Eigen::MatrixXd &nodalDisplacements) {
assert(nodalDisplacements.cols() == 6);
Eigen::MatrixXd eigenDisplacements =
Utilities::toEigenMatrix(this->displacements);
const double errorNorm = (eigenDisplacements - nodalDisplacements).norm();
return errorNorm < 1e-10;
// return eigenDisplacements.isApprox(nodalDisplacements);
}
};
#endif // SIMULATIONHISTORY_HPP

637
topologyenumerator.cpp Normal file → Executable file
View File

@ -0,0 +1,637 @@
#include "topologyenumerator.hpp"
#include <algorithm>
#include <iostream>
#include <math.h>
#include <numeric>
#include <unordered_set>
const bool debugIsOn{false};
const bool exportArticulationPointsPatterns{false};
const bool savePlyFiles{false};
// size_t binomialCoefficient(size_t n, size_t m) {
// assert(n > m);
// return tgamma(n + 1) / (tgamma(m + 1) * tgamma(n - m + 1));
//}
// void TopologyEnumerator::createLabelMesh(
// const std::vector<vcg::Point3d> vertices,
// const std::filesystem::path &savePath) const {
// const std::string allOnes(patternTopology.getNumberOfPossibleEdges(), '1');
// const std::vector<vcg::Point2i> allEdges =
// TrianglePatternTopology::convertToEdges(allOnes, vertices.size());
// TrianglePatternGeometry labelMesh;
// std::vector<vcg::Point3d> labelVertices(allEdges.size());
// for (size_t edgeIndex = 0; edgeIndex < allEdges.size(); edgeIndex++) {
// const vcg::Point3d edgeMidpoint =
// (vertices[allEdges[edgeIndex][0]] + vertices[allEdges[edgeIndex][1]])
// / 2;
// labelVertices[edgeIndex] = edgeMidpoint;
// }
// labelMesh.set(labelVertices);
// labelMesh.savePly(std::filesystem::path(savePath)
// .append(std::string("labelMesh.ply"))
// .string());
//}
size_t TopologyEnumerator::getEdgeIndex(size_t ni0, size_t ni1) const {
if (ni1 <= ni0) {
std::swap(ni0, ni1);
}
assert(ni1 > ni0);
const size_t &n = numberOfNodes;
return (n * (n - 1) / 2) - (n - ni0) * ((n - ni0) - 1) / 2 + ni1 - ni0 - 1;
}
TopologyEnumerator::TopologyEnumerator() {}
void TopologyEnumerator::computeValidPatterns(const std::vector<size_t> &reducedNumberOfNodesPerSlot,const std::string& desiredResultsPath, const int &numberOfDesiredEdges) {
assert(reducedNumberOfNodesPerSlot.size() == 5);
assert(reducedNumberOfNodesPerSlot[0] == 0 ||
reducedNumberOfNodesPerSlot[0] == 1);
assert(reducedNumberOfNodesPerSlot[1] == 0 ||
reducedNumberOfNodesPerSlot[1] == 1);
std::vector<size_t> numberOfNodesPerSlot{
reducedNumberOfNodesPerSlot[0], reducedNumberOfNodesPerSlot[1],
reducedNumberOfNodesPerSlot[1], reducedNumberOfNodesPerSlot[2],
reducedNumberOfNodesPerSlot[3], reducedNumberOfNodesPerSlot[2],
reducedNumberOfNodesPerSlot[4]};
// Generate an edge mesh wih all possible edges
numberOfNodes = std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.end(), 0);
const size_t numberOfAllPossibleEdges =
numberOfNodes * (numberOfNodes - 1) / 2;
std::vector<vcg::Point2i> allPossibleEdges(numberOfAllPossibleEdges);
const int &n = numberOfNodes;
for (size_t edgeIndex = 0; edgeIndex < numberOfAllPossibleEdges;
edgeIndex++) {
const int ni0 =
n - 2 -
std::floor(std::sqrt(-8 * edgeIndex + 4 * n * (n - 1) - 7) / 2.0 - 0.5);
const int ni1 =
edgeIndex + ni0 + 1 - n * (n - 1) / 2 + (n - ni0) * ((n - ni0) - 1) / 2;
allPossibleEdges[edgeIndex] = vcg::Point2i(ni0, ni1);
}
PatternGeometry patternGeometryAllEdges;
patternGeometryAllEdges.add(numberOfNodesPerSlot, allPossibleEdges);
// Create Results path
auto resultPath=std::filesystem::path(desiredResultsPath);
assert(std::filesystem::exists(resultPath));
auto allResultsPath = resultPath.append("Results");
std::filesystem::create_directory(allResultsPath);
std::string setupString;
// for (size_t numberOfNodes : reducedNumberOfNodesPerSlot) {
for (size_t numberOfNodesPerSlotIndex = 0;
numberOfNodesPerSlotIndex < reducedNumberOfNodesPerSlot.size();
numberOfNodesPerSlotIndex++) {
std::string elemID;
if (numberOfNodesPerSlotIndex == 0 || numberOfNodesPerSlotIndex == 1) {
elemID = "v";
} else if (numberOfNodesPerSlotIndex == 2 ||
numberOfNodesPerSlotIndex == 3) {
elemID = "e";
} else {
elemID = "c";
}
setupString +=
std::to_string(reducedNumberOfNodesPerSlot[numberOfNodesPerSlotIndex]) +
elemID + "_";
}
setupString += std::to_string(PatternGeometry().getFanSize()) + "fan";
if (debugIsOn) {
setupString += "_debug";
}
auto resultsPath = std::filesystem::path(allResultsPath).append(setupString);
// std::filesystem::remove_all(resultsPath); // delete previous results
std::filesystem::create_directory(resultsPath);
if (debugIsOn) {
patternGeometryAllEdges.save(std::filesystem::path(resultsPath)
.append("allPossibleEdges.ply")
.string());
}
// statistics.numberOfPossibleEdges = numberOfAllPossibleEdges;
std::vector<vcg::Point2i> validEdges =
getValidEdges(numberOfNodesPerSlot, resultsPath, patternGeometryAllEdges,
allPossibleEdges);
PatternGeometry patternAllValidEdges;
patternAllValidEdges.add(patternGeometryAllEdges.getVertices(), validEdges);
if (debugIsOn) {
// Export all valid edges in a ply
patternAllValidEdges.save(std::filesystem::path(resultsPath)
.append("allValidEdges.ply")
.string());
}
// statistics.numberOfValidEdges = validEdges.size();
// Find pairs of intersecting edges
std::unordered_map<size_t, std::unordered_set<size_t>> intersectingEdges =
patternAllValidEdges.getIntersectingEdges(
statistics.numberOfIntersectingEdgePairs);
if (debugIsOn) {
auto intersectingEdgesPath = std::filesystem::path(resultsPath)
.append("All_intersecting_edge_pairs");
std::filesystem::create_directory(intersectingEdgesPath);
// Export intersecting pairs in ply files
for (auto mapIt = intersectingEdges.begin();
mapIt != intersectingEdges.end(); mapIt++) {
for (auto setIt = mapIt->second.begin(); setIt != mapIt->second.end();
setIt++) {
PatternGeometry intersectingEdgePair;
const size_t ei0 = mapIt->first;
const size_t ei1 = *setIt;
vcg::tri::Allocator<PatternGeometry>::AddEdge(
intersectingEdgePair,
patternGeometryAllEdges.getVertices()[validEdges[ei0][0]],
patternGeometryAllEdges.getVertices()[validEdges[ei0][1]]);
vcg::tri::Allocator<PatternGeometry>::AddEdge(
intersectingEdgePair,
patternGeometryAllEdges.getVertices()[validEdges[ei1][0]],
patternGeometryAllEdges.getVertices()[validEdges[ei1][1]]);
intersectingEdgePair.save(
std::filesystem::path(intersectingEdgesPath)
.append(std::to_string(mapIt->first) + "_" +
std::to_string(*setIt) + ".ply")
.string());
}
}
}
// assert(validEdges.size() == allPossibleEdges.size() -
// coincideEdges.size() -
// duplicateEdges.size());
// PatternSet patternSet;
// const std::vector<vcg::Point3d> nodes =
// patternGeometryAllEdges.getVertices(); const size_t numberOfNodes =
// nodes.size(); patternSet.nodes.resize(numberOfNodes); for (size_t
// nodeIndex = 0; nodeIndex < numberOfNodes; nodeIndex++) {
// patternSet.nodes[nodeIndex] =
// vcg::Point3d(nodes[nodeIndex][0], nodes[nodeIndex][1],0);
// }
// if (std::filesystem::exists(std::filesystem::path(resultsPath)
// .append("patterns.patt")
// .string())) {
// std::filesystem::remove(
// std::filesystem::path(resultsPath).append("patterns.patt"));
// }
if(numberOfDesiredEdges==-1){
for (size_t numberOfEdges = 2; numberOfEdges < validEdges.size();
numberOfEdges++) {
std::cout << "Computing " + setupString << " with " << numberOfEdges
<< " edges." << std::endl;
auto perEdgeResultPath = std::filesystem::path(resultsPath)
.append(std::to_string(numberOfEdges));
if (std::filesystem::exists(perEdgeResultPath)) {
continue;
}
std::filesystem::create_directory(perEdgeResultPath);
computeValidPatterns(numberOfNodesPerSlot, numberOfEdges, perEdgeResultPath,
patternGeometryAllEdges.getVertices(),
intersectingEdges, validEdges);
// statistics.print(setupString, perEdgeResultPath);
}
}
else{
std::cout << "Computing " + setupString << " with " << numberOfDesiredEdges
<< " edges." << std::endl;
auto perEdgeResultPath = std::filesystem::path(resultsPath)
.append(std::to_string(numberOfDesiredEdges));
if (std::filesystem::exists(perEdgeResultPath)) {
return;
}
std::filesystem::create_directory(perEdgeResultPath);
computeValidPatterns(numberOfNodesPerSlot,numberOfDesiredEdges, perEdgeResultPath,
patternGeometryAllEdges.getVertices(),
intersectingEdges, validEdges);
}
}
void TopologyEnumerator::computeEdgeNodes(
const std::vector<size_t> &numberOfNodesPerSlot,
std::vector<size_t> &nodesEdge0, std::vector<size_t> &nodesEdge1,
std::vector<size_t> &nodesEdge2) {
// Create vectors holding the node indices of each pattern node of each
// triangle edge
size_t nodeIndex = 0;
if (numberOfNodesPerSlot[0] != 0) {
nodesEdge0.push_back(nodeIndex++);
}
if (numberOfNodesPerSlot[1] != 0)
nodesEdge1.push_back(nodeIndex++);
if (numberOfNodesPerSlot[2] != 0)
nodesEdge2.push_back(nodeIndex++);
if (numberOfNodesPerSlot[3] != 0) {
for (size_t edgeNodeIndex = 0; edgeNodeIndex < numberOfNodesPerSlot[3];
edgeNodeIndex++) {
nodesEdge0.push_back(nodeIndex++);
}
}
if (numberOfNodesPerSlot[4] != 0) {
for (size_t edgeNodeIndex = 0; edgeNodeIndex < numberOfNodesPerSlot[4];
edgeNodeIndex++) {
nodesEdge1.push_back(nodeIndex++);
}
}
if (numberOfNodesPerSlot[5] != 0) {
for (size_t edgeNodeIndex = 0; edgeNodeIndex < numberOfNodesPerSlot[5];
edgeNodeIndex++) {
nodesEdge2.push_back(nodeIndex++);
}
}
if (numberOfNodesPerSlot[1] != 0) {
assert(numberOfNodesPerSlot[2]);
nodesEdge0.push_back(1);
nodesEdge1.push_back(2);
}
if (numberOfNodesPerSlot[0] != 0) {
nodesEdge2.push_back(0);
}
}
std::unordered_set<size_t> TopologyEnumerator::computeCoincideEdges(
const std::vector<size_t> &numberOfNodesPerSlot) {
/*
* A coincide edge is defined as an edge connection between two nodes that lay
* on a triangle edge and which have another node in between
* */
std::vector<size_t> nodesEdge0; // left edge
std::vector<size_t> nodesEdge1; // bottom edge
std::vector<size_t> nodesEdge2; // right edge
computeEdgeNodes(numberOfNodesPerSlot, nodesEdge0, nodesEdge1, nodesEdge2);
std::vector<size_t> coincideEdges0 = getCoincideEdges(nodesEdge0);
std::vector<size_t> coincideEdges1 = getCoincideEdges(nodesEdge1);
std::vector<size_t> coincideEdges2 = getCoincideEdges(nodesEdge2);
std::unordered_set<size_t> coincideEdges{coincideEdges0.begin(),
coincideEdges0.end()};
std::copy(coincideEdges1.begin(), coincideEdges1.end(),
std::inserter(coincideEdges, coincideEdges.end()));
std::copy(coincideEdges2.begin(), coincideEdges2.end(),
std::inserter(coincideEdges, coincideEdges.end()));
if (numberOfNodesPerSlot[0] && numberOfNodesPerSlot[1]) {
coincideEdges.insert(getEdgeIndex(0, 2));
}
if (numberOfNodesPerSlot[0] && numberOfNodesPerSlot[2]) {
assert(numberOfNodesPerSlot[1]);
coincideEdges.insert(getEdgeIndex(0, 3));
}
return coincideEdges;
}
std::unordered_set<size_t> TopologyEnumerator::computeDuplicateEdges(
const std::vector<size_t> &numberOfNodesPerSlot) {
/*
* A duplicate edges are all edges the "right" edge since due to rotational
* symmetry "left" edge=="right" edge
* */
std::unordered_set<size_t> duplicateEdges;
std::vector<size_t> nodesEdge0; // left edge
std::vector<size_t> nodesEdge1; // bottom edge
std::vector<size_t> nodesEdge2; // right edge
computeEdgeNodes(numberOfNodesPerSlot, nodesEdge0, nodesEdge1, nodesEdge2);
if (numberOfNodesPerSlot[5]) {
for (size_t edge2NodeIndex = 0; edge2NodeIndex < nodesEdge2.size() - 1;
edge2NodeIndex++) {
const size_t nodeIndex = nodesEdge2[edge2NodeIndex];
const size_t nextNodeIndex = nodesEdge2[edge2NodeIndex + 1];
duplicateEdges.insert(getEdgeIndex(nodeIndex, nextNodeIndex));
}
}
return duplicateEdges;
}
std::vector<vcg::Point2i> TopologyEnumerator::getValidEdges(
const std::vector<size_t> &numberOfNodesPerSlot,
const std::filesystem::path &resultsPath,
const PatternGeometry &patternGeometryAllEdges,
const std::vector<vcg::Point2i> &allPossibleEdges) {
std::unordered_set<size_t> coincideEdges =
computeCoincideEdges(numberOfNodesPerSlot);
// Export each coincide edge into a ply file
if (!coincideEdges.empty() && debugIsOn) {
auto coincideEdgesPath =
std::filesystem::path(resultsPath).append("Coincide_edges");
std::filesystem::create_directories(coincideEdgesPath);
for (auto coincideEdgeIndex : coincideEdges) {
PatternGeometry::EdgeType e =
patternGeometryAllEdges.edge[coincideEdgeIndex];
PatternGeometry singleEdgeMesh;
vcg::Point3d p0 = e.cP(0);
vcg::Point3d p1 = e.cP(1);
std::vector<vcg::Point3d> edgeVertices;
edgeVertices.push_back(p0);
edgeVertices.push_back(p1);
singleEdgeMesh.add(edgeVertices);
singleEdgeMesh.add(std::vector<vcg::Point2i>{vcg::Point2i{0, 1}});
singleEdgeMesh.save(std::filesystem::path(coincideEdgesPath)
.append(std::to_string(coincideEdgeIndex))
.string() +
".ply");
}
}
statistics.numberOfCoincideEdges = coincideEdges.size();
// Compute duplicate edges
std::unordered_set<size_t> duplicateEdges =
computeDuplicateEdges(numberOfNodesPerSlot);
if (!duplicateEdges.empty() && debugIsOn) {
// Export duplicate edges in a single ply file
auto duplicateEdgesPath =
std::filesystem::path(resultsPath).append("duplicate");
std::filesystem::create_directory(duplicateEdgesPath);
PatternGeometry patternDuplicateEdges;
for (auto duplicateEdgeIndex : duplicateEdges) {
PatternGeometry::EdgeType e =
patternGeometryAllEdges.edge[duplicateEdgeIndex];
vcg::Point3d p0 = e.cP(0);
vcg::Point3d p1 = e.cP(1);
vcg::tri::Allocator<PatternGeometry>::AddEdge(patternDuplicateEdges, p0,
p1);
}
patternDuplicateEdges.save(std::filesystem::path(duplicateEdgesPath)
.append("duplicateEdges.ply")
.string());
}
statistics.numberOfDuplicateEdges = duplicateEdges.size();
// Create the set of all possible edges without coincide and duplicate edges
std::vector<vcg::Point2i> validEdges;
for (size_t edgeIndex = 0; edgeIndex < allPossibleEdges.size(); edgeIndex++) {
if (coincideEdges.count(edgeIndex) == 0 &&
duplicateEdges.count(edgeIndex) == 0) {
validEdges.push_back(allPossibleEdges[edgeIndex]);
}
}
return validEdges;
}
void TopologyEnumerator::computeValidPatterns(
const std::vector<size_t> &numberOfNodesPerSlot,
const size_t &numberOfDesiredEdges,
const std::filesystem::path &resultsPath,
const std::vector<vcg::Point3d> &allVertices,
const std::unordered_map<size_t, std::unordered_set<size_t>>
&intersectingEdges,
const std::vector<vcg::Point2i> &validEdges) {
assert(numberOfNodesPerSlot.size() == 7);
// Iterate over all patterns which have numberOfDesiredEdges edges from
// from the validEdges Identify patterns that contain dangling edges
const bool enoughValidEdgesExist = validEdges.size() >= numberOfDesiredEdges;
if (!enoughValidEdgesExist) {
std::filesystem::remove_all(resultsPath); // delete previous results folder
return;
}
assert(enoughValidEdgesExist);
// Create pattern result paths
auto validPatternsPath = std::filesystem::path(resultsPath).append("Valid");
std::filesystem::create_directory(validPatternsPath);
// std::ofstream validPatternsFileStream;
// validPatternsFileStream.open(
// validPatternsPath.append("patterns.patt").string());
const std::string validPatternsFilePath =
validPatternsPath.append("patterns.patt").string();
PatternIO::PatternSet patternSet;
patternSet.nodes = allVertices;
const int patternSetBufferSize = 10000;
const size_t numberOfPatterns = PatternGeometry::binomialCoefficient(
validEdges.size(), numberOfDesiredEdges);
statistics.numberOfPatterns = numberOfPatterns;
// Initialize pattern binary representation
std::string patternBinaryRepresentation;
patternBinaryRepresentation = std::string(numberOfDesiredEdges, '1');
patternBinaryRepresentation +=
std::string(validEdges.size() - numberOfDesiredEdges, '0');
std::sort(patternBinaryRepresentation.begin(),
patternBinaryRepresentation.end());
/*TODO: Performance could be improved by changing the patternGeometry with
* respect to the previous one. Maybe I could xor the binaryRepresentation
* to the previous one.*/
// std::string previousPatternBinaryRepresentation(validEdges.size(),'0');
size_t patternIndex = 0;
do {
patternIndex++;
const std::string patternName = std::to_string(patternIndex);
// std::cout << "Pattern name:" + patternBinaryRepresentation <<
// std::endl; isValidPattern(patternBinaryRepresentation, validEdges,
// numberOfDesiredEdges);
// Create the geometry of the pattern
// Compute the pattern edges from the binary representation
std::vector<vcg::Point2i> patternEdges(numberOfDesiredEdges);
size_t patternEdgeIndex = 0;
for (size_t validEdgeIndex = 0;
validEdgeIndex < patternBinaryRepresentation.size();
validEdgeIndex++) {
if (patternBinaryRepresentation[validEdgeIndex] == '1') {
assert(patternEdgeIndex < numberOfDesiredEdges);
patternEdges[patternEdgeIndex++] = validEdges[validEdgeIndex];
}
}
PatternGeometry patternGeometry;
patternGeometry.add(allVertices, patternEdges);
// Check if pattern contains intersecting edges
const bool patternContainsIntersectingEdges =
patternGeometry.hasIntersectingEdges(patternBinaryRepresentation,
intersectingEdges);
// Export the tiled ply file if it contains intersecting edges
if (patternContainsIntersectingEdges) {
// create the tiled geometry of the pattern
statistics.numberOfPatternsWithIntersectingEdges++;
if (debugIsOn) {
if (savePlyFiles) {
PatternGeometry tiledPatternGeometry =
PatternGeometry::createTile(patternGeometry);
auto intersectingPatternsPath =
std::filesystem::path(resultsPath).append("Intersecting");
std::filesystem::create_directory(intersectingPatternsPath);
patternGeometry.save(
std::filesystem::path(intersectingPatternsPath)
.append(patternName)
.string() +
".ply");
tiledPatternGeometry.save(
std::filesystem::path(intersectingPatternsPath)
.append(patternName + "_tiled")
.string() +
".ply");
}
} else {
continue; // should be uncommented in order to improve performance
}
}
// Compute the tiled valence
const bool tiledPatternHasDanglingEdges = patternGeometry.hasDanglingEdges(
numberOfNodesPerSlot); // marks the nodes with valence>=1
// Create the tiled geometry of the pattern
const bool hasFloatingComponents =
!patternGeometry.isFullyConnectedWhenTiled();
FlatPatternTopology topology(numberOfNodesPerSlot, patternEdges);
const bool hasArticulationPoints = topology.containsArticulationPoints();
// duplicated here
// Check dangling edges with vcg method
// const bool vcg_tiledPatternHasDangling =
// tiledPatternGeometry.hasUntiledDanglingEdges();
if (tiledPatternHasDanglingEdges /*&& !hasFloatingComponents &&
!hasArticulationPoints*/) {
statistics.numberOfPatternsWithADanglingEdgeOrNode++;
if (debugIsOn) {
if (savePlyFiles) {
auto danglingEdgesPath =
std::filesystem::path(resultsPath).append("Dangling");
std::filesystem::create_directory(danglingEdgesPath);
patternGeometry.save(std::filesystem::path(danglingEdgesPath)
.append(patternName)
.string() +
".ply");
PatternGeometry tiledPatternGeometry = PatternGeometry::createTile(
patternGeometry); // the marked nodes of hasDanglingEdges are
tiledPatternGeometry.save(std::filesystem::path(danglingEdgesPath)
.append(patternName + "_tiled")
.string() +
".ply");
}
} else {
continue;
}
}
if (hasFloatingComponents /*&& !hasArticulationPoints &&
!tiledPatternHasDanglingEdges*/) {
statistics.numberOfPatternsWithMoreThanASingleCC++;
if (debugIsOn) {
if (savePlyFiles) {
auto moreThanOneCCPath =
std::filesystem::path(resultsPath).append("MoreThanOneCC");
std::filesystem::create_directory(moreThanOneCCPath);
patternGeometry.save(std::filesystem::path(moreThanOneCCPath)
.append(patternName)
.string() +
".ply");
PatternGeometry tiledPatternGeometry = PatternGeometry::createTile(
patternGeometry); // the marked nodes of hasDanglingEdges are
tiledPatternGeometry.save(std::filesystem::path(moreThanOneCCPath)
.append(patternName + "_tiled")
.string() +
".ply");
}
} else {
continue;
}
}
if (hasArticulationPoints /*&& !hasFloatingComponents &&
!tiledPatternHasDanglingEdges*/) {
statistics.numberOfPatternsWithArticulationPoints++;
if (exportArticulationPointsPatterns || debugIsOn) {
if (savePlyFiles) {
auto articulationPointsPath =
std::filesystem::path(resultsPath).append("ArticulationPoints");
std::filesystem::create_directory(articulationPointsPath);
patternGeometry.save(std::filesystem::path(articulationPointsPath)
.append(patternName)
.string() +
".ply");
PatternGeometry tiledPatternGeometry = PatternGeometry::createTile(
patternGeometry); // the marked nodes of hasDanglingEdges are
tiledPatternGeometry.save(
std::filesystem::path(articulationPointsPath)
.append(patternName + "_tiled")
.string() +
".ply");
// std::cout << "Pattern:" << patternName << std::endl;
}
} else {
continue;
}
}
const bool isValidPattern =
!patternContainsIntersectingEdges && !tiledPatternHasDanglingEdges &&
!hasFloatingComponents && !hasArticulationPoints;
if (isValidPattern) {
statistics.numberOfValidPatterns++;
if (savePlyFiles) {
// if (numberOfDesiredEdges == 4) {
// std::cout << "Saving:"
// << std::filesystem::path(validPatternsPath)
// .append(patternName)
// .string() +
// ".ply"
// << std::endl;
// }
patternGeometry.save(std::filesystem::path(validPatternsPath)
.append(patternName)
.string() +
".ply");
PatternGeometry tiledPatternGeometry = PatternGeometry::createTile(
patternGeometry); // the marked nodes of hasDanglingEdges are
tiledPatternGeometry.save(std::filesystem::path(validPatternsPath)
.append(patternName + "_tiled")
.string() +
".ply");
}
PatternIO::Pattern pattern;
pattern.edges = patternEdges;
pattern.name = patternIndex;
patternSet.patterns.emplace_back(pattern);
// Save valid patterns
if (patternIndex % patternSetBufferSize == 0) {
PatternIO::save(validPatternsFilePath, patternSet);
patternSet.patterns.clear();
patternSet.patterns.reserve(patternSetBufferSize);
}
}
// assert(vcg_tiledPatternHasDangling == tiledPatternHasDanglingEdges);
} while (std::next_permutation(patternBinaryRepresentation.begin(),
patternBinaryRepresentation.end()));
if (!patternSet.patterns.empty()) {
PatternIO::save(validPatternsFilePath, patternSet);
}
}
std::vector<size_t> TopologyEnumerator::getCoincideEdges(
const std::vector<size_t> &edgeNodeIndices) const {
std::vector<size_t> coincideEdges;
if (edgeNodeIndices.size() < 3)
return coincideEdges;
for (size_t edgeNodeIndex = 0; edgeNodeIndex < edgeNodeIndices.size() - 2;
edgeNodeIndex++) {
const size_t &firstNodeIndex = edgeNodeIndices[edgeNodeIndex];
for (size_t secondEdgeNodeIndex = edgeNodeIndex + 2;
secondEdgeNodeIndex < edgeNodeIndices.size(); secondEdgeNodeIndex++) {
const size_t &secondNodeIndex = edgeNodeIndices[secondEdgeNodeIndex];
coincideEdges.push_back(getEdgeIndex(firstNodeIndex, secondNodeIndex));
}
}
return coincideEdges;
}
bool TopologyEnumerator::isValidPattern(
const std::string &patternBinaryRepresentation,
const std::vector<vcg::Point2i> &validEdges,
const size_t &numberOfDesiredEdges) const {
return true;
}

View File

@ -146,7 +146,7 @@ public:
TopologyEnumerator();
void
computeValidPatterns(const std::vector<size_t> &reducedNumberOfNodesPerSlot);
computeValidPatterns(const std::vector<size_t> &reducedNumberOfNodesPerSlot, const std::string &desiredResultsPath, const int& numberOfDesiredEdges=-1);
private:
std::vector<size_t>
@ -176,6 +176,5 @@ private:
const std::unordered_map<size_t, std::unordered_set<size_t>>
&intersectingEdges,
const std::vector<vcg::Point2i> &validEdges);
}ges, PatternSet &patternsSet);
};
#
#endif // TOPOLOGYENUMERATOR_HPP

View File

@ -140,7 +140,7 @@ PatternGeometry::PatternGeometry(PatternGeometry &other) {
vcg::tri::UpdateTopology<PatternGeometry>::EdgeEdge(*this);
}
bool PatternGeometry::savePly(const std::string plyFilename) {
bool PatternGeometry::save(const std::string plyFilename) {
int returnValue = vcg::tri::io::ExporterPLY<PatternGeometry>::Save(
*this, plyFilename.c_str(),
@ -584,8 +584,9 @@ PatternGeometry::PatternGeometry(const std::string &filename,
updateEigenEdgeAndVertices();
}
double PatternGeometry::computeBaseTriangleHeight() const {
return (vert[0].cP() - vert[3].cP()).Norm();
double PatternGeometry::computeBaseTriangleHeight() const
{
return vcg::Distance(vert[0].cP(), vert[interfaceNodeIndex].cP());
}
void PatternGeometry::addNormals() {
@ -597,6 +598,17 @@ void PatternGeometry::addNormals() {
}
}
vcg::Triangle3<double> PatternGeometry::getBaseTriangle() const
{
double bottomEdgeHalfSize = computeBaseTriangleHeight() / std::tan(M_PI / 3);
CoordType patternCoord0 = vert[0].cP();
CoordType patternBottomRight = vert[interfaceNodeIndex].cP()
+ CoordType(bottomEdgeHalfSize, 0, 0);
CoordType patternBottomLeft = vert[interfaceNodeIndex].cP()
- CoordType(bottomEdgeHalfSize, 0, 0);
return vcg::Triangle3<double>(patternCoord0, patternBottomLeft, patternBottomRight);
}
PatternGeometry::PatternGeometry(
const std::vector<size_t> &numberOfNodesPerSlot,
const std::vector<vcg::Point2i> &edges) {
@ -659,336 +671,53 @@ void PatternGeometry::copy(PatternGeometry &copyFrom) {
baseTriangleHeight = copyFrom.getBaseTriangleHeight();
}
void PatternGeometry::deleteDanglingEdges() {
for (VertexType &v : vert) {
std::vector<VCGEdgeMesh::EdgePointer> incidentElements;
vcg::edge::VEStarVE(&v, incidentElements);
if (incidentElements.size() == 1) {
vcg::tri::Allocator<VCGEdgeMesh>::DeleteEdge(*this, *incidentElements[0]);
}
if (incidentElements.size() == 1) {
vcg::tri::Allocator<VCGEdgeMesh>::DeleteVertex(*this, v);
}
}
vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateVertex(*this);
vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateEdge(*this);
vcg::tri::Allocator<VCGEdgeMesh>::CompactEveryVector(*this);
}
void PatternGeometry::scale(const double &desiredBaseTriangleCentralEdgeSize) {
void PatternGeometry::scale(const double &desiredBaseTriangleCentralEdgeSize,const int& interfaceNodeIndex) {
this->baseTriangleHeight = desiredBaseTriangleCentralEdgeSize;
const double baseTriangleCentralEdgeSize =
(vert[0].cP() - vert[3].cP()).Norm();
const double baseTriangleCentralEdgeSize = getBaseTriangleHeight();
const double scaleRatio =
desiredBaseTriangleCentralEdgeSize / baseTriangleCentralEdgeSize;
vcg::tri::UpdatePosition<VCGEdgeMesh>::Scale(*this, scaleRatio);
}
void PatternGeometry::deleteDanglingVertices() {
vcg::tri::Allocator<PatternGeometry>::PointerUpdater<VertexPointer> pu;
deleteDanglingVertices(pu);
}
void PatternGeometry::deleteDanglingVertices(
vcg::tri::Allocator<PatternGeometry>::PointerUpdater<VertexPointer> &pu) {
for (VertexType &v : vert) {
std::vector<PatternGeometry::EdgePointer> incidentElements;
vcg::edge::VEStarVE(&v, incidentElements);
if (incidentElements.size() == 0 && !v.IsD()) {
vcg::tri::Allocator<PatternGeometry>::DeleteVertex(*this, v);
void PatternGeometry::createFan(const std::vector<int> &connectToNeighborsVi, const size_t &fanSize)
{
PatternGeometry rotatedPattern;
vcg::tri::Append<PatternGeometry, PatternGeometry>::MeshCopy(rotatedPattern, *this);
for (int rotationCounter = 1; rotationCounter < fanSize; rotationCounter++) {
vcg::Matrix44d R;
auto rotationAxis = vcg::Point3d(0, 0, 1);
R.SetRotateDeg(360 / fanSize, rotationAxis);
vcg::tri::UpdatePosition<PatternGeometry>::Matrix(rotatedPattern, R);
vcg::tri::Append<PatternGeometry, PatternGeometry>::Mesh(*this, rotatedPattern);
//Add edges for connecting the desired vertices
if (!connectToNeighborsVi.empty()) {
if (rotationCounter == fanSize - 1) {
for (int connectToNeighborIndex = 0;
connectToNeighborIndex < connectToNeighborsVi.size();
connectToNeighborIndex++) {
vcg::tri::Allocator<PatternGeometry>::AddEdge(
*this,
connectToNeighborsVi[connectToNeighborIndex],
this->VN() - rotatedPattern.VN()
+ connectToNeighborsVi[connectToNeighborIndex]);
}
}
for (int connectToNeighborIndex = 0;
connectToNeighborIndex < connectToNeighborsVi.size();
connectToNeighborIndex++) {
vcg::tri::Allocator<PatternGeometry>::AddEdge(
*this,
this->VN() - 2 * rotatedPattern.VN()
+ connectToNeighborsVi[connectToNeighborIndex],
this->VN() - rotatedPattern.VN() + connectToNeighborsVi[connectToNeighborIndex]);
}
}
removeDuplicateVertices();
updateEigenEdgeAndVertices();
}
}
vcg::tri::Allocator<PatternGeometry>::CompactVertexVector(*this, pu);
vcg::tri::Allocator<PatternGeometry>::CompactEdgeVector(*this);
updateEigenEdgeAndVertices();
}
void PatternGeometry::tilePattern(VCGEdgeMesh& pattern, VCGPolyMesh &tileInto,const int& interfaceNodeIndex,
const bool &shouldDeleteDanglingEdges) {
double xOffset =
vcg::Distance(pattern.vert[0].cP(), pattern.vert[interfaceNodeIndex].cP()) /
std::tan(M_PI / 3);
CoordType patternCoord0 = pattern.vert[0].cP();
CoordType patternBottomRight =
pattern.vert[interfaceNodeIndex].cP() + CoordType(xOffset, 0, 0);
CoordType patternBottomLeft =
pattern.vert[interfaceNodeIndex].cP() - CoordType(xOffset, 0, 0);
std::vector<vcg::Point3d> patternTrianglePoints{
patternCoord0, patternBottomRight, patternBottomLeft};
CoordType pointOnPattern =
patternCoord0 + (patternBottomLeft - patternCoord0) ^
(patternBottomRight - patternCoord0);
std::vector<CoordType> faceCenters(FN());
VCGTriMesh tileIntoEdgeMesh;
for (VCGPolyMesh::FaceType &f : tileInto.face) {
std::vector<VCGPolyMesh::VertexType *> incidentVertices;
vcg::face::VFIterator<PFace> vfi(
&f, 0); // initialize the iterator to the first face
// vcg::face::Pos p(vfi.F(), f.cV(0));
// vcg::face::VVOrderedStarFF(p, incidentVertices);
size_t numberOfNodes = 0;
CoordType centerOfFace(0, 0, 0);
for (size_t vi = 0; vi < f.VN(); vi++) {
numberOfNodes++;
centerOfFace = centerOfFace + f.cP(vi);
}
centerOfFace /= f.VN();
vcg::tri::Allocator<VCGTriMesh>::AddVertex(tileIntoEdgeMesh, centerOfFace,
vcg::Color4b::Yellow);
// const size_t vi = vcg::tri::Index<VCGPolyMesh>(tileInto, v);
// std::cout << "vertex " << vi << " has incident vertices:" <<
// std::endl;
for (size_t vi = 0; vi < f.VN(); vi++) {
// size_t f = 0;
// std::cout << vcg::tri::Index<VCGTriMesh>(tileInto,
// / incidentVertices[f]) / << std::endl;
// Compute transformation matrix M
// vcg::Matrix44d M;
std::vector<vcg::Point3d> meshTrianglePoints{
centerOfFace, f.cP(vi), vi + 1 == f.VN() ? f.cP(0) : f.cP(vi + 1)};
CoordType faceNormal = ((meshTrianglePoints[1] - meshTrianglePoints[0]) ^
(meshTrianglePoints[2] - meshTrianglePoints[0]))
.Normalize();
auto fit = vcg::tri::Allocator<VCGTriMesh>::AddFace(
tileIntoEdgeMesh, meshTrianglePoints[0], meshTrianglePoints[1],
meshTrianglePoints[2]);
fit->N() = faceNormal;
CoordType pointOnTriMesh =
meshTrianglePoints[0] +
(meshTrianglePoints[1] - meshTrianglePoints[0]) ^
(meshTrianglePoints[2] - meshTrianglePoints[0]);
vcg::Matrix44d M;
// vcg::ComputeRigidMatchMatrix(meshTrianglePoints,
// patternTrianglePoints,
// M);
vcg::Matrix44d A_prime;
A_prime[0][0] = meshTrianglePoints[0][0];
A_prime[1][0] = meshTrianglePoints[0][1];
A_prime[2][0] = meshTrianglePoints[0][2];
A_prime[3][0] = 1;
A_prime[0][1] = meshTrianglePoints[1][0];
A_prime[1][1] = meshTrianglePoints[1][1];
A_prime[2][1] = meshTrianglePoints[1][2];
A_prime[3][1] = 1;
A_prime[0][2] = meshTrianglePoints[2][0];
A_prime[1][2] = meshTrianglePoints[2][1];
A_prime[2][2] = meshTrianglePoints[2][2];
A_prime[3][2] = 1;
A_prime[0][3] = pointOnTriMesh[0];
A_prime[1][3] = pointOnTriMesh[1];
A_prime[2][3] = pointOnTriMesh[2];
A_prime[3][3] = 1;
vcg::Matrix44d A;
A[0][0] = patternTrianglePoints[0][0];
A[1][0] = patternTrianglePoints[0][1];
A[2][0] = patternTrianglePoints[0][2];
A[3][0] = 1;
A[0][1] = patternTrianglePoints[1][0];
A[1][1] = patternTrianglePoints[1][1];
A[2][1] = patternTrianglePoints[1][2];
A[3][1] = 1;
A[0][2] = patternTrianglePoints[2][0];
A[1][2] = patternTrianglePoints[2][1];
A[2][2] = patternTrianglePoints[2][2];
A[3][2] = 1;
A[0][3] = pointOnPattern[0];
A[1][3] = pointOnPattern[1];
A[2][3] = pointOnPattern[2];
A[3][3] = 1;
M = A_prime * vcg::Inverse(A);
VCGEdgeMesh transformedPattern;
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::MeshCopy(transformedPattern,
pattern);
vcg::tri::UpdatePosition<VCGEdgeMesh>::Matrix(transformedPattern, M);
for (VertexType &v : transformedPattern.vert) {
v.N() = faceNormal;
}
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::Mesh(*this,
transformedPattern);
}
}
// vcg::tri::Clean<VCGEdgeMesh>::MergeCloseVertex(*this, 0.0000000001);
// vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateVertex(*this);
// vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateEdge(*this);
// vcg::tri::Allocator<VCGEdgeMesh>::CompactEveryVector(*this);
// vcg::tri::Clean<VCGEdgeMesh>::RemoveDuplicateVertex(*this);
vcg::tri::Clean<VCGEdgeMesh>::MergeCloseVertex(*this, 0.000061524);
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
// vcg::tri::Clean<VCGEdgeMesh>::RemoveUnreferencedVertex(*this);
// vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
// vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateVertex(*this);
// vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateEdge(*this);
// vcg::tri::Allocator<VCGEdgeMesh>::CompactEveryVector(*this);
deleteDanglingVertices();
vcg::tri::Allocator<VCGEdgeMesh>::CompactEveryVector(*this);
// vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateVertex(*this);
// vcg::tri::Clean<VCGEdgeMesh>::RemoveUnreferencedVertex(*this);
// vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
// deleteDanglingEdges();
// vcg::tri::Clean<VCGEdgeMesh>::RemoveUnreferencedVertex(*this);
// vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateVertex(*this);
// vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateEdge(*this);
// vcg::tri::Allocator<VCGEdgeMesh>::CompactEveryVector(*this);
updateEigenEdgeAndVertices();
savePly("tiledPattern.ply");
}
void PatternGeometry::createFan(const size_t &fanSize) {
PatternGeometry rotatedPattern;
vcg::tri::Append<PatternGeometry, PatternGeometry>::MeshCopy(rotatedPattern,
*this);
for (int rotationCounter = 1; rotationCounter < fanSize; rotationCounter++) {
vcg::Matrix44d R;
auto rotationAxis = vcg::Point3d(0, 0, 1);
R.SetRotateDeg(360 / fanSize, rotationAxis);
vcg::tri::UpdatePosition<PatternGeometry>::Matrix(rotatedPattern, R);
vcg::tri::Append<PatternGeometry, PatternGeometry>::Mesh(*this,
rotatedPattern);
}
removeDuplicateVertices();
// const double precision = 1e-4;
// for (size_t vi = 0; vi < VN(); vi++) {
// vert[vi].P()[0] = std::round(vert[vi].P()[0] * (1 / precision)) *
// precision; vert[vi].P()[1] = std::round(vert[vi].P()[1] * (1 /
// precision)) * precision; vert[vi].P()[2] = std::round(vert[vi].P()[2]
// * (1 / precision)) * precision;
// }
updateEigenEdgeAndVertices();
}
void PatternGeometry::removeDuplicateVertices() {
vcg::tri::Clean<VCGEdgeMesh>::MergeCloseVertex(*this, 0.0000000001);
vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateVertex(*this);
vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateEdge(*this);
vcg::tri::Allocator<VCGEdgeMesh>::CompactEveryVector(*this);
vcg::tri::UpdateTopology<PatternGeometry>::VertexEdge(*this);
}
double PatternGeometry::getBaseTriangleHeight() const {
return baseTriangleHeight;
}
void PatternGeometry::tilePattern(PatternGeometry &pattern, VCGTriMesh &tileInto) {
const size_t middleIndex = 3;
double xOffset =
vcg::Distance(pattern.vert[0].cP(), pattern.vert[middleIndex].cP()) /
std::tan(M_PI / 3);
CoordType patternCoord0 = pattern.vert[0].cP();
CoordType patternBottomRight =
pattern.vert[middleIndex].cP() + CoordType(xOffset, 0, 0);
CoordType patternBottomLeft =
pattern.vert[middleIndex].cP() - CoordType(xOffset, 0, 0);
std::vector<vcg::Point3d> patternTrianglePoints{
patternCoord0, patternBottomRight, patternBottomLeft};
CoordType pointOnPattern =
patternCoord0 + (patternBottomLeft - patternCoord0) ^
(patternBottomRight - patternCoord0);
for (VCGTriMesh::VertexType &v : tileInto.vert) {
const auto centralNodeColor = vcg::Color4<unsigned char>(64, 64, 64, 255);
const bool isCentralNode = v.cC() == centralNodeColor;
if (isCentralNode) {
std::vector<VCGTriMesh::VertexType *> incidentVertices;
vcg::face::VFIterator<VCGTriMeshFace> vfi(
&v); // initialize the iterator tohe first face
vcg::face::Pos p(vfi.F(), &v);
vcg::face::VVOrderedStarFF(p, incidentVertices);
const size_t vi = vcg::tri::Index<VCGTriMesh>(tileInto, v);
std::cout << "vertex " << vi << " has incident vertices:" << std::endl;
for (size_t f = 0; f < incidentVertices.size(); f++) {
// size_t f = 0;
std::cout << vcg::tri::Index<VCGTriMesh>(tileInto, incidentVertices[f])
<< std::endl;
// Compute transformation matrix M
// vcg::Matrix44d M;
std::vector<vcg::Point3d> meshTrianglePoints{
v.cP(), incidentVertices[f]->cP(),
f + 1 == incidentVertices.size() ? incidentVertices[0]->cP()
: incidentVertices[f + 1]->cP()};
CoordType faceNormal =
((meshTrianglePoints[1] - meshTrianglePoints[0]) ^
(meshTrianglePoints[2] - meshTrianglePoints[0]))
.Normalize();
CoordType pointOnTriMesh =
meshTrianglePoints[0] +
(meshTrianglePoints[1] - meshTrianglePoints[0]) ^
(meshTrianglePoints[2] - meshTrianglePoints[0]);
vcg::Matrix44d M;
// vcg::ComputeRigidMatchMatrix(meshTrianglePoints,
// patternTrianglePoints,
// M);
vcg::Matrix44d A_prime;
A_prime[0][0] = meshTrianglePoints[0][0];
A_prime[1][0] = meshTrianglePoints[0][1];
A_prime[2][0] = meshTrianglePoints[0][2];
A_prime[3][0] = 1;
A_prime[0][1] = meshTrianglePoints[1][0];
A_prime[1][1] = meshTrianglePoints[1][1];
A_prime[2][1] = meshTrianglePoints[1][2];
A_prime[3][1] = 1;
A_prime[0][2] = meshTrianglePoints[2][0];
A_prime[1][2] = meshTrianglePoints[2][1];
A_prime[2][2] = meshTrianglePoints[2][2];
A_prime[3][2] = 1;
A_prime[0][3] = pointOnTriMesh[0];
A_prime[1][3] = pointOnTriMesh[1];
A_prime[2][3] = pointOnTriMesh[2];
A_prime[3][3] = 1;
vcg::Matrix44d A;
A[0][0] = patternTrianglePoints[0][0];
A[1][0] = patternTrianglePoints[0][1];
A[2][0] = patternTrianglePoints[0][2];
A[3][0] = 1;
A[0][1] = patternTrianglePoints[1][0];
A[1][1] = patternTrianglePoints[1][1];
A[2][1] = patternTrianglePoints[1][2];
A[3][1] = 1;
A[0][2] = patternTrianglePoints[2][0];
A[1][2] = patternTrianglePoints[2][1];
A[2][2] = patternTrianglePoints[2][2];
A[3][2] = 1;
A[0][3] = pointOnPattern[0];
A[1][3] = pointOnPattern[1];
A[2][3] = pointOnPattern[2];
A[3][3] = 1;
M = A_prime * vcg::Inverse(A);
VCGEdgeMesh transformedPattern;
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::MeshCopy(transformedPattern,
pattern);
vcg::tri::UpdatePosition<VCGEdgeMesh>::Matrix(transformedPattern, M);
for (VertexType &v : transformedPattern.vert) {
v.N() = faceNormal;
}
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::Mesh(*this,
transformedPattern);
}
}
}
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
deleteDanglingVertices();
deleteDanglingEdges();
vcg::tri::Allocator<VCGEdgeMesh>::CompactEveryVector(*this);
updateEigenEdgeAndVertices();
}

View File

@ -16,11 +16,10 @@ private:
size_t getNodeSlot(const size_t &nodeIndex) const;
void addNormals();
void deleteDanglingEdges();
void removeDuplicateVertices();
double baseTriangleHeight;
double computeBaseTriangleHeight() const;
const int interfaceNodeIndex{3};
const size_t fanSize{6};
std::vector<VCGEdgeMesh::CoordType> vertices;
const double triangleEdgeSize{1}; // radius edge
@ -41,7 +40,7 @@ public:
* vcglib interface.
* */
PatternGeometry(PatternGeometry &other);
bool savePly(const std::string plyFilename);
bool save(const std::string plyFilename);
void add(const std::vector<vcg::Point3d> &vertices);
void add(const std::vector<vcg::Point2i> &edges);
void add(const std::vector<vcg::Point3d> &vertices,
@ -83,20 +82,251 @@ public:
void copy(PatternGeometry &copyFrom);
void tilePattern(PatternGeometry &pattern, VCGTriMesh &tileInto);
void tilePattern(VCGEdgeMesh &pattern, VCGPolyMesh &tileInto, const int &interfaceNodeIndex,
void tilePattern(VCGEdgeMesh &pattern,
VCGPolyMesh &tileInto,
const int &interfaceNodeIndex,
const bool &shouldDeleteDanglingEdges);
void createFan(const size_t &fanSize = 6);
void deleteDanglingVertices(
vcg::tri::Allocator<PatternGeometry>::PointerUpdater<VertexPointer> &pu);
void deleteDanglingVertices();
void scale(const double &desiredBaseTriangleCentralEdgeSize);
void scale(const double &desiredBaseTriangleCentralEdgeSize, const int &interfaceNodeIndex);
double getBaseTriangleHeight() const;
vcg::Triangle3<double> getBaseTriangle() const;
PatternGeometry(const std::vector<vcg::Point2d> &vertices,
const std::vector<vcg::Point2i> &edges);
PatternGeometry(const std::vector<vcg::Point2d> &vertices, const std::vector<vcg::Point2i> &edges);
// static std::shared_ptr<PatternGeometry> tilePattern(PatternGeometry &pattern,
// VCGPolyMesh &tileInto,
// const int &interfaceNodeIndex,
// const bool &shouldDeleteDanglingEdges)
// {
// PatternGeometry tiledPattern;
// double bottomEdgeHalfSize = vcg::Distance(pattern.vert[0].cP(),
// pattern.vert[interfaceNodeIndex].cP())
// / std::tan(M_PI / 3);
// CoordType patternCoord0 = pattern.vert[0].cP();
// CoordType patternBottomRight = pattern.vert[interfaceNodeIndex].cP()
// + CoordType(bottomEdgeHalfSize, 0, 0);
// CoordType patternBottomLeft = pattern.vert[interfaceNodeIndex].cP()
// - CoordType(bottomEdgeHalfSize, 0, 0);
// std::vector<vcg::Point3d> patternTrianglePoints{patternCoord0,
// patternBottomRight,
// patternBottomLeft};
// CoordType pointOnPattern = patternCoord0 + (patternBottomLeft - patternCoord0)
// ^ (patternBottomRight - patternCoord0);
// // patternTrianglePoints.push_back(pointOnPattern);
// VCGTriMesh tileIntoEdgeMesh;
// for (VCGPolyMesh::FaceType &f : tileInto.face) {
// size_t numberOfNodes = 0;
// CoordType centerOfFace(0, 0, 0);
// for (size_t vi = 0; vi < f.VN(); vi++) {
// numberOfNodes++;
// centerOfFace = centerOfFace + f.cP(vi);
// }
// centerOfFace /= f.VN();
// vcg::tri::Allocator<VCGTriMesh>::AddVertex(tileIntoEdgeMesh,
// centerOfFace,
// vcg::Color4b::Yellow);
// for (size_t vi = 0; vi < f.VN(); vi++) {
// // Compute transformation matrix M
// // vcg::Matrix44d M;
// std::vector<vcg::Point3d> meshTrianglePoints{centerOfFace,
// f.cP(vi),
// vi + 1 == f.VN() ? f.cP(0)
// : f.cP(vi + 1)};
// CoordType faceNormal = ((meshTrianglePoints[1] - meshTrianglePoints[0])
// ^ (meshTrianglePoints[2] - meshTrianglePoints[0]))
// .Normalize();
// auto fit = vcg::tri::Allocator<VCGTriMesh>::AddFace(tileIntoEdgeMesh,
// meshTrianglePoints[0],
// meshTrianglePoints[1],
// meshTrianglePoints[2]);
// fit->N() = faceNormal;
// CoordType pointOnTriMesh = meshTrianglePoints[0]
// + (meshTrianglePoints[1] - meshTrianglePoints[0])
// ^ (meshTrianglePoints[2] - meshTrianglePoints[0]);
// vcg::Matrix44d M;
// // meshTrianglePoints.push_back(pointOnTriMesh);
// // vcg::ComputeRigidMatchMatrix(meshTrianglePoints, patternTrianglePoints, M);
// vcg::Matrix44d A_prime;
// A_prime[0][0] = meshTrianglePoints[0][0];
// A_prime[1][0] = meshTrianglePoints[0][1];
// A_prime[2][0] = meshTrianglePoints[0][2];
// A_prime[3][0] = 1;
// A_prime[0][1] = meshTrianglePoints[1][0];
// A_prime[1][1] = meshTrianglePoints[1][1];
// A_prime[2][1] = meshTrianglePoints[1][2];
// A_prime[3][1] = 1;
// A_prime[0][2] = meshTrianglePoints[2][0];
// A_prime[1][2] = meshTrianglePoints[2][1];
// A_prime[2][2] = meshTrianglePoints[2][2];
// A_prime[3][2] = 1;
// A_prime[0][3] = pointOnTriMesh[0];
// A_prime[1][3] = pointOnTriMesh[1];
// A_prime[2][3] = pointOnTriMesh[2];
// A_prime[3][3] = 1;
// vcg::Matrix44d A;
// A[0][0] = patternTrianglePoints[0][0];
// A[1][0] = patternTrianglePoints[0][1];
// A[2][0] = patternTrianglePoints[0][2];
// A[3][0] = 1;
// A[0][1] = patternTrianglePoints[1][0];
// A[1][1] = patternTrianglePoints[1][1];
// A[2][1] = patternTrianglePoints[1][2];
// A[3][1] = 1;
// A[0][2] = patternTrianglePoints[2][0];
// A[1][2] = patternTrianglePoints[2][1];
// A[2][2] = patternTrianglePoints[2][2];
// A[3][2] = 1;
// A[0][3] = pointOnPattern[0];
// A[1][3] = pointOnPattern[1];
// A[2][3] = pointOnPattern[2];
// A[3][3] = 1;
// M = A_prime * vcg::Inverse(A);
// PatternGeometry transformedPattern;
// transformedPattern.copy(pattern);
// vcg::tri::UpdatePosition<PatternGeometry>::Matrix(transformedPattern, M);
// for (VertexType &v : transformedPattern.vert) {
// v.N() = faceNormal;
// }
// // const double innerHexagonInitialRotationAngle = 30;
// // vcg::Matrix44d Rlocal;
// // Rlocal.SetRotateDeg(
// // (0
// // // optimizationResults.optimalXNameValuePairs["HexAngle"]
// // - innerHexagonInitialRotationAngle),
// // VectorType(0, 0, 1));
// // vcg::Matrix44d T;
// // T.SetTranslate(-transformedPattern.vert[0].cP());
// // vcg::Matrix44d Trev;
// // Trev.SetTranslate(transformedPattern.vert[0].cP());
// // auto R = T * Rlocal * Trev;
// // transformedPattern.vert[1].P()
// // = R * meshTrianglePoints[2]
// // * 0.5; //optimizationResults.optimalXNameValuePairs["HexSize"];
// // transformedPattern.vert[5].P()
// // = R * meshTrianglePoints[1]
// // * 0.5; //optimizationResults.optimalXNameValuePairs["HexSize"];
// vcg::tri::Append<MeshType, MeshType>::Mesh(tiledPattern, transformedPattern);
// tiledPattern.updateEigenEdgeAndVertices();
// tiledPattern.registerForDrawing();
// polyscope::show();
// }
// }
// tiledPattern.removeDuplicateVertices();
// tiledPattern.deleteDanglingVertices();
// vcg::tri::Allocator<PatternGeometry>::CompactEveryVector(tiledPattern);
// tiledPattern.updateEigenEdgeAndVertices();
// return std::make_shared<PatternGeometry>(tiledPattern);
// }
static std::shared_ptr<PatternGeometry> tilePattern(PatternGeometry &pattern,
const vcg::Triangle3<double> &baseTriangle,
const std::vector<int> &connectToNeighborsVi,
const VCGPolyMesh &tileInto)
{
std::shared_ptr<PatternGeometry> pTiledPattern(new PatternGeometry);
//Compute the barycentric coords of the verts in the base triangle pattern
std::vector<CoordType> barycentricCoordinates(pattern.VN());
for (int vi = 0; vi < pattern.VN(); vi++) {
CoordType barycentricCoords_vi;
vcg::InterpolationParameters<vcg::Triangle3<double>, double>(baseTriangle,
pattern.vert[vi].cP(),
barycentricCoords_vi);
barycentricCoordinates[vi] = barycentricCoords_vi;
}
VCGTriMesh tileIntoEdgeMesh;
for (const VCGPolyMesh::FaceType &f : tileInto.face) {
CoordType centerOfFace(0, 0, 0);
for (size_t vi = 0; vi < f.VN(); vi++) {
centerOfFace = centerOfFace + f.cP(vi);
}
centerOfFace /= f.VN();
vcg::tri::Allocator<VCGTriMesh>::AddVertex(tileIntoEdgeMesh,
centerOfFace,
vcg::Color4b::Yellow);
std::vector<int> firstInFanConnectToNeighbor_vi(connectToNeighborsVi);
for (int &vi : firstInFanConnectToNeighbor_vi) {
vi += pTiledPattern->VN();
}
for (size_t vi = 0; vi < f.VN(); vi++) {
std::vector<vcg::Point3d> meshTrianglePoints{centerOfFace,
f.cP(vi),
vi + 1 == f.VN() ? f.cP(0)
: f.cP(vi + 1)};
auto fit = vcg::tri::Allocator<VCGTriMesh>::AddFace(tileIntoEdgeMesh,
meshTrianglePoints[0],
meshTrianglePoints[1],
meshTrianglePoints[2]);
CoordType faceNormal = ((meshTrianglePoints[1] - meshTrianglePoints[0])
^ (meshTrianglePoints[2] - meshTrianglePoints[0]))
.Normalize();
fit->N() = faceNormal;
PatternGeometry transformedPattern;
transformedPattern.copy(pattern);
//Transform the base triangle nodes to the mesh triangle using barycentric coords
for (int vi = 0; vi < transformedPattern.VN(); vi++) {
transformedPattern.vert[vi].P() = CoordType(
meshTrianglePoints[0] * barycentricCoordinates[vi][0]
+ meshTrianglePoints[1] * barycentricCoordinates[vi][1]
+ meshTrianglePoints[2] * barycentricCoordinates[vi][2]);
}
for (VertexType &v : transformedPattern.vert) {
v.N() = faceNormal;
}
vcg::tri::Append<PatternGeometry, PatternGeometry>::Mesh(*pTiledPattern,
transformedPattern);
//Add edges for connecting the desired vertices
if (!connectToNeighborsVi.empty()) {
if (vi + 1 == f.VN()) {
for (int connectToNeighborIndex = 0;
connectToNeighborIndex < connectToNeighborsVi.size();
connectToNeighborIndex++) {
vcg::tri::Allocator<PatternGeometry>::AddEdge(
*pTiledPattern,
firstInFanConnectToNeighbor_vi[connectToNeighborIndex],
pTiledPattern->VN() - pattern.VN()
+ connectToNeighborsVi[connectToNeighborIndex]);
}
}
if (vi != 0) {
for (int connectToNeighborIndex = 0;
connectToNeighborIndex < connectToNeighborsVi.size();
connectToNeighborIndex++) {
vcg::tri::Allocator<PatternGeometry>::AddEdge(
*pTiledPattern,
pTiledPattern->VN() - 2 * pattern.VN()
+ connectToNeighborsVi[connectToNeighborIndex],
pTiledPattern->VN() - pattern.VN()
+ connectToNeighborsVi[connectToNeighborIndex]);
}
}
}
// tiledPattern.updateEigenEdgeAndVertices();
// tiledPattern.registerForDrawing();
// polyscope::show();
}
}
pTiledPattern->removeDuplicateVertices();
pTiledPattern->deleteDanglingVertices();
vcg::tri::Allocator<PatternGeometry>::CompactEveryVector(*pTiledPattern);
pTiledPattern->updateEigenEdgeAndVertices();
return pTiledPattern;
}
void createFan(const std::vector<int> &connectToNeighborsVi=std::vector<int>(), const size_t &fanSize=6);
};
#endif // FLATPATTERNGEOMETRY_HPP

View File

@ -176,6 +176,7 @@ inline void deinitPolyscope() {
polyscope::shutdown();
}
inline void initPolyscope() {
if (polyscope::state::initialized) {
return;

View File

@ -1,79 +1,140 @@
#include "vcgtrimesh.hpp"
#include <wrap/nanoply/include/nanoplyWrapper.hpp>
#include "utilities.hpp"
#include "wrap/io_trimesh/import_obj.h"
#include "wrap/io_trimesh/import_off.h"
#include <filesystem>
#include <wrap/io_trimesh/import.h>
#include <wrap/nanoply/include/nanoplyWrapper.hpp>
void VCGTriMesh::loadFromPlyFile(const std::string &filename) {
unsigned int mask = 0;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOORD;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTNORMAL;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOLOR;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_EDGEINDEX;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_FACEINDEX;
if (nanoply::NanoPlyWrapper<VCGTriMesh>::LoadModel(
std::filesystem::absolute(filename).string().c_str(), *this, mask) != 0) {
std::cout << "Could not load tri mesh" << std::endl;
}
bool VCGTriMesh::load(const std::string &filename) {
// unsigned int mask = 0;
// mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOORD;
// mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTNORMAL;
// mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOLOR;
// mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_EDGEINDEX;
// mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_FACEINDEX;
// if (nanoply::NanoPlyWrapper<VCGTriMesh>::LoadModel(
// std::filesystem::absolute(filename).string().c_str(), *this, mask) != 0) {
if (vcg::tri::io::Importer<VCGTriMesh>::Open(*this,
std::filesystem::absolute(filename).string().c_str())
!= 0) {
std::cout << "Could not load tri mesh" << std::endl;
return false;
}
vcg::tri::UpdateTopology<VCGTriMesh>::FaceFace(*this);
vcg::tri::UpdateTopology<VCGTriMesh>::VertexFace(*this);
vcg::tri::UpdateNormal<VCGTriMesh>::PerVertexNormalized(*this);
}
Eigen::MatrixX3d VCGTriMesh::getVertices() const {
Eigen::MatrixX3d vertices(VN(), 3);
for (size_t vi = 0; vi < VN(); vi++) {
VCGTriMesh::CoordType vertexCoordinates = vert[vi].cP();
vertices.row(vi) = vertexCoordinates.ToEigenVector<Eigen::Vector3d>();
}
return vertices;
}
Eigen::MatrixX3i VCGTriMesh::getFaces() const {
Eigen::MatrixX3i faces(FN(), 3);
for (int fi = 0; fi < FN(); fi++) {
const VCGTriMesh::FaceType &face = this->face[fi];
const size_t v0 = vcg::tri::Index<VCGTriMesh>(*this, face.cV(0));
const size_t v1 = vcg::tri::Index<VCGTriMesh>(*this, face.cV(1));
const size_t v2 = vcg::tri::Index<VCGTriMesh>(*this, face.cV(2));
faces.row(fi) = Eigen::Vector3i(v0, v1, v2);
}
return faces;
}
bool VCGTriMesh::savePly(const std::string plyFilename) {
// Load the ply file
unsigned int mask = 0;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOORD;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOLOR;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_FACEINDEX;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_FACENORMAL;
if (nanoply::NanoPlyWrapper<VCGTriMesh>::SaveModel(plyFilename.c_str(), *this,
mask, false) != 0) {
return false;
}
label = std::filesystem::path(filename).stem();
return true;
}
Eigen::MatrixX3d VCGTriMesh::getVertices() const
{
// vcg::tri::Allocator<VCGTriMesh>::CompactVertexVector(m);
Eigen::MatrixX3d vertices(VN(), 3);
for (size_t vi = 0; vi < VN(); vi++) {
VCGTriMesh::CoordType vertexCoordinates = vert[vi].cP();
vertices.row(vi) = vertexCoordinates.ToEigenVector<Eigen::Vector3d>();
}
return vertices;
}
Eigen::MatrixX3i VCGTriMesh::getFaces() const
{
// vcg::tri::Allocator<VCGTriMesh>::CompactFaceVector(m);
Eigen::MatrixX3i faces(FN(), 3);
for (int fi = 0; fi < FN(); fi++) {
const VCGTriMesh::FaceType &face = this->face[fi];
const size_t v0 = vcg::tri::Index<VCGTriMesh>(*this, face.cV(0));
const size_t v1 = vcg::tri::Index<VCGTriMesh>(*this, face.cV(1));
const size_t v2 = vcg::tri::Index<VCGTriMesh>(*this, face.cV(2));
faces.row(fi) = Eigen::Vector3i(v0, v1, v2);
}
return faces;
}
Eigen::MatrixX2i VCGTriMesh::getEdges() const
{
// vcg::tri::Allocator<VCGTriMesh>::CompactFaceVector(m);
Eigen::MatrixX2i edges(EN(), 2);
for (int ei = 0; ei < EN(); ei++) {
const VCGTriMesh::EdgeType &edge = this->edge[ei];
const size_t v0 = vcg::tri::Index<VCGTriMesh>(*this, edge.cV(0));
const size_t v1 = vcg::tri::Index<VCGTriMesh>(*this, edge.cV(1));
edges.row(ei) = Eigen::Vector2i(v0, v1);
}
return edges;
}
bool VCGTriMesh::save(const std::string plyFilename)
{
// vcg::tri::Allocator<VCGTriMesh>::CompactEveryVector(*this);
assert(std::filesystem::path(plyFilename).extension() == ".ply");
// Load the ply file
unsigned int mask = 0;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOORD;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOLOR;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_FACEINDEX;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_FACENORMAL;
if (nanoply::NanoPlyWrapper<VCGTriMesh>::SaveModel(plyFilename.c_str(), *this, mask, false)
!= 0) {
return false;
}
return true;
}
polyscope::SurfaceMesh *VCGTriMesh::registerForDrawing(
const std::optional<std::array<double, 3>> &desiredColor, const bool &shouldEnable) const
{
auto vertices = getVertices();
auto faces = getFaces();
initPolyscope();
if (faces.rows() == 0) {
auto edges = getEdges();
polyscope::CurveNetwork *polyscopeHandle_mesh(
polyscope::registerCurveNetwork(label, vertices, edges));
return nullptr;
}
polyscope::SurfaceMesh *polyscopeHandle_mesh(
polyscope::registerSurfaceMesh(label, vertices, faces));
polyscopeHandle_mesh->setEnabled(shouldEnable);
const double drawingRadius = 0.002;
polyscopeHandle_mesh->setEdgeWidth(drawingRadius);
if (desiredColor.has_value()) {
const glm::vec3 desiredColor_glm(desiredColor.value()[0],
desiredColor.value()[1],
desiredColor.value()[2]);
polyscopeHandle_mesh->setSurfaceColor(desiredColor_glm);
}
return polyscopeHandle_mesh;
}
VCGTriMesh::VCGTriMesh() {}
VCGTriMesh::VCGTriMesh(const std::string &filename) {
const std::string extension = std::filesystem::path(filename).extension().string();
if (extension == ".ply") {
loadFromPlyFile(filename);
} else if (extension == ".obj") {
vcg::tri::io::ImporterOBJ<VCGTriMesh>::Info info;
vcg::tri::io::ImporterOBJ<VCGTriMesh>::Open(*this, filename.c_str(), info);
} else if (extension == ".off") {
vcg::tri::io::ImporterOFF<VCGTriMesh>::Open(*this, filename.c_str());
VCGTriMesh::VCGTriMesh(const std::string &filename)
{
const std::string extension = std::filesystem::path(filename).extension().string();
if (extension == ".ply") {
load(filename);
} else if (extension == ".obj") {
vcg::tri::io::ImporterOBJ<VCGTriMesh>::Info info;
vcg::tri::io::ImporterOBJ<VCGTriMesh>::Open(*this, filename.c_str(), info);
} else if (extension == ".off") {
vcg::tri::io::ImporterOFF<VCGTriMesh>::Open(*this, filename.c_str());
} else {
std::cerr << "Uknown file extension " << extension << ". Could not open "
<< filename << std::endl;
assert(false);
}
vcg::tri::UpdateTopology<VCGTriMesh>::AllocateEdge(*this);
vcg::tri::UpdateTopology<VCGTriMesh>::FaceFace(*this);
vcg::tri::UpdateTopology<VCGTriMesh>::VertexFace(*this);
vcg::tri::UpdateNormal<VCGTriMesh>::PerVertexNormalized(*this);
} else {
std::cerr << "Uknown file extension " << extension << ". Could not open " << filename
<< std::endl;
assert(false);
}
vcg::tri::UpdateTopology<VCGTriMesh>::AllocateEdge(*this);
vcg::tri::UpdateTopology<VCGTriMesh>::FaceFace(*this);
vcg::tri::UpdateTopology<VCGTriMesh>::VertexFace(*this);
vcg::tri::UpdateNormal<VCGTriMesh>::PerVertexNormalized(*this);
}

View File

@ -1,5 +1,7 @@
#ifndef VCGTRIMESH_HPP
#define VCGTRIMESH_HPP
#include "mesh.hpp"
#include <polyscope/surface_mesh.h>
#include <vcg/complex/complex.h>
using VertexIndex = size_t;
@ -10,10 +12,14 @@ struct VCGTriMeshUsedTypes
: public vcg::UsedTypes<vcg::Use<VCGTriMeshVertex>::AsVertexType,
vcg::Use<VCGTriMeshEdge>::AsEdgeType,
vcg::Use<VCGTriMeshFace>::AsFaceType> {};
class VCGTriMeshVertex
: public vcg::Vertex<VCGTriMeshUsedTypes, vcg::vertex::Coord3d,
vcg::vertex::Normal3d, vcg::vertex::BitFlags,
vcg::vertex::Color4b, vcg::vertex::VFAdj> {};
class VCGTriMeshVertex : public vcg::Vertex<VCGTriMeshUsedTypes,
vcg::vertex::Coord3d,
vcg::vertex::Normal3d,
vcg::vertex::BitFlags,
vcg::vertex::Color4b,
vcg::vertex::VFAdj,
vcg::vertex::Qualityd>
{};
class VCGTriMeshFace
: public vcg::Face<VCGTriMeshUsedTypes, vcg::face::FFAdj, vcg::face::VFAdj,
vcg::face::VertexRef, vcg::face::BitFlags,
@ -23,17 +29,26 @@ class VCGTriMeshEdge
class VCGTriMesh : public vcg::tri::TriMesh<std::vector<VCGTriMeshVertex>,
std::vector<VCGTriMeshFace>,
std::vector<VCGTriMeshEdge>> {
std::vector<VCGTriMeshEdge>>,
public Mesh
{
public:
VCGTriMesh();
VCGTriMesh(const std::string &filename);
void loadFromPlyFile(const std::string &filename);
bool load(const std::string &filename) override;
Eigen::MatrixX3d getVertices() const;
Eigen::MatrixX3i getFaces() const;
bool savePly(const std::string plyFilename);
bool save(const std::string plyFilename);
template <typename MeshElement> size_t getIndex(const MeshElement &element) {
return vcg::tri::Index<VCGTriMesh>(*this, element);
}
#ifdef POLYSCOPE_DEFINED
polyscope::SurfaceMesh *registerForDrawing(
const std::optional<std::array<double, 3>> &desiredColor = std::nullopt,
const bool &shouldEnable = true) const;
#endif
Eigen::MatrixX2i getEdges() const;
};
#endif // VCGTRIMESH_HPP