Refactoring, linear model, renaming
This commit is contained in:
parent
fd67b1b4c9
commit
e6fe102574
|
@ -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)
|
||||
|
|
31
beam.hpp
31
beam.hpp
|
@ -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
|
||||
|
|
1803
beamformfinder.cpp
1803
beamformfinder.cpp
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,6 @@
|
|||
#ifndef CSVFILE_HPP
|
||||
#define CSVFILE_HPP
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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());
|
128
edgemesh.cpp
128
edgemesh.cpp
|
@ -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
|
||||
|
|
40
edgemesh.hpp
40
edgemesh.hpp
|
@ -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 °reesOfArm, const size_t &numberOfSamples);
|
||||
|
||||
bool createSpanGrid(const size_t squareGridDimension);
|
||||
bool createSpanGrid(const size_t desiredWidth, const size_t desiredHeight);
|
||||
void createSpiral(const float °reesOfArm, 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);
|
||||
|
|
|
@ -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
|
9
mesh.hpp
9
mesh.hpp
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
#ifndef OPTIMIZATIONSTRUCTS_HPP
|
||||
#define OPTIMIZATIONSTRUCTS_HPP
|
||||
#include "beamformfinder.hpp"
|
||||
|
||||
#endif // OPTIMIZATIONSTRUCTS_HPP
|
|
@ -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"));
|
||||
|
|
233
polymesh.hpp
233
polymesh.hpp
|
@ -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 ©From)
|
||||
{
|
||||
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 ©From) { copy(copyFrom); }
|
||||
VCGPolyMesh() {}
|
||||
template<typename MeshElement>
|
||||
size_t getIndex(const MeshElement &meshElement) const
|
||||
{
|
||||
return vcg::tri::Index<VCGPolyMesh>(*this, meshElement);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // POLYMESH_HPP
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
};
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
|
@ -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,
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 ©From) {
|
|||
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();
|
||||
}
|
||||
|
|
|
@ -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 ©From);
|
||||
|
||||
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
|
||||
|
|
|
@ -176,6 +176,7 @@ inline void deinitPolyscope() {
|
|||
|
||||
polyscope::shutdown();
|
||||
}
|
||||
|
||||
inline void initPolyscope() {
|
||||
if (polyscope::state::initialized) {
|
||||
return;
|
||||
|
|
187
vcgtrimesh.cpp
187
vcgtrimesh.cpp
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue