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")
|
set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
|
||||||
endif()
|
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
|
##vcglib devel branch
|
||||||
download_project(PROJ vcglib_devel
|
download_project(PROJ vcglib_devel
|
||||||
GIT_REPOSITORY https://github.com/IasonManolas/vcglib.git
|
GIT_REPOSITORY https://github.com/IasonManolas/vcglib.git
|
||||||
GIT_TAG devel
|
GIT_TAG devel
|
||||||
PREFIX ${CMAKE_CURRENT_LIST_DIR}/build/external/
|
PREFIX ${EXTERNAL_DEPS_DIR}
|
||||||
${UPDATE_DISCONNECTED_IF_AVAILABLE}
|
${UPDATE_DISCONNECTED_IF_AVAILABLE}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -22,11 +29,21 @@ download_project(PROJ vcglib_devel
|
||||||
download_project(PROJ MATPLOTPLUSPLUS
|
download_project(PROJ MATPLOTPLUSPLUS
|
||||||
GIT_REPOSITORY https://github.com/alandefreitas/matplotplusplus
|
GIT_REPOSITORY https://github.com/alandefreitas/matplotplusplus
|
||||||
GIT_TAG master
|
GIT_TAG master
|
||||||
PREFIX ${CMAKE_CURRENT_LIST_DIR}/build/external/
|
PREFIX ${EXTERNAL_DEPS_DIR}
|
||||||
${UPDATE_DISCONNECTED_IF_AVAILABLE}
|
${UPDATE_DISCONNECTED_IF_AVAILABLE}
|
||||||
)
|
)
|
||||||
add_subdirectory(${MATPLOTPLUSPLUS_SOURCE_DIR} ${MATPLOTPLUSPLUS_BINARY_DIR})
|
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
|
###Eigen 3 NOTE: Eigen is required on the system the code is ran
|
||||||
find_package(Eigen3 3.3 REQUIRED)
|
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)
|
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)
|
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}
|
target_include_directories(${PROJECT_NAME}
|
||||||
PUBLIC ${CMAKE_CURRENT_LIST_DIR}/boost_graph
|
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}
|
PUBLIC ${MySourcesFiles}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(${USE_POLYSCOPE})
|
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()
|
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) {}
|
CylindricalBeamDimensions() : od(0.03), id(0.026) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ElementMaterial {
|
struct ElementMaterial
|
||||||
double poissonsRatio; // NOTE: if I make this double the unit
|
{
|
||||||
// tests produced different results and thus fail
|
double poissonsRatio;
|
||||||
double youngsModulus;
|
double youngsModulus;
|
||||||
ElementMaterial(const double &poissonsRatio, const double &youngsModulus)
|
ElementMaterial(const double &poissonsRatio, const double &youngsModulus)
|
||||||
: poissonsRatio(poissonsRatio), youngsModulus(youngsModulus) {
|
: poissonsRatio(poissonsRatio), youngsModulus(youngsModulus)
|
||||||
assert(poissonsRatio <= 0.5 && poissonsRatio >= -1);
|
{
|
||||||
}
|
assert(poissonsRatio <= 0.5 && poissonsRatio >= -1);
|
||||||
ElementMaterial() : poissonsRatio(0.3), youngsModulus(200) {}
|
}
|
||||||
std::string toString() const {
|
ElementMaterial() : poissonsRatio(0.3), youngsModulus(200 * 1e9) {}
|
||||||
return std::string("Material:") + std::string("\nPoisson's ratio=") +
|
std::string toString() const
|
||||||
std::to_string(poissonsRatio) +
|
{
|
||||||
std::string("\nYoung's Modulus(GPa)=") +
|
return std::string("Material:") + std::string("\nPoisson's ratio=")
|
||||||
std::to_string(youngsModulus / 1e9);
|
+ std::to_string(poissonsRatio) + std::string("\nYoung's Modulus(GPa)=")
|
||||||
}
|
+ std::to_string(youngsModulus / 1e9);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BEAM_HPP
|
#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
|
#ifndef CSVFILE_HPP
|
||||||
#define CSVFILE_HPP
|
#define CSVFILE_HPP
|
||||||
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,9 +1,9 @@
|
||||||
#ifndef BEAMFORMFINDER_HPP
|
#ifndef BEAMFORMFINDER_HPP
|
||||||
#define BEAMFORMFINDER_HPP
|
#define BEAMFORMFINDER_HPP
|
||||||
|
|
||||||
#include "elementalmesh.hpp"
|
#include "simulationmesh.hpp"
|
||||||
#include "matplot/matplot.h"
|
#include "matplot/matplot.h"
|
||||||
#include "simulationresult.hpp"
|
#include "simulation_structs.hpp"
|
||||||
#include <Eigen/Dense>
|
#include <Eigen/Dense>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
@ -20,7 +20,8 @@ struct DifferentiateWithRespectTo {
|
||||||
const DoFType &dofi;
|
const DoFType &dofi;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FormFinder {
|
class DRMSimulationModel
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
struct Settings {
|
struct Settings {
|
||||||
bool debugMessages{false};
|
bool debugMessages{false};
|
||||||
|
|
@ -108,7 +109,7 @@ private:
|
||||||
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
|
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
|
||||||
&fixedVertices);
|
&fixedVertices);
|
||||||
|
|
||||||
void draw(const string &screenshotsFolder);
|
void draw(const string &screenshotsFolder= {});
|
||||||
|
|
||||||
void
|
void
|
||||||
updateNodalInternalForce(Vector6d &nodalInternalForce,
|
updateNodalInternalForce(Vector6d &nodalInternalForce,
|
||||||
|
|
@ -160,7 +161,7 @@ private:
|
||||||
double computeTheta3(const EdgeType &e, const VertexType &v);
|
double computeTheta3(const EdgeType &e, const VertexType &v);
|
||||||
double computeDerivativeTheta3(const EdgeType &e, const VertexType &v,
|
double computeDerivativeTheta3(const EdgeType &e, const VertexType &v,
|
||||||
const DifferentiateWithRespectTo &dui) const;
|
const DifferentiateWithRespectTo &dui) const;
|
||||||
double computeTotalPotentialEnergy() const;
|
double computeTotalPotentialEnergy();
|
||||||
void computeRigidSupports();
|
void computeRigidSupports();
|
||||||
void updateNormalDerivatives();
|
void updateNormalDerivatives();
|
||||||
void updateT1Derivatives();
|
void updateT1Derivatives();
|
||||||
|
|
@ -197,8 +198,10 @@ private:
|
||||||
|
|
||||||
void printDebugInfo() const;
|
void printDebugInfo() const;
|
||||||
|
|
||||||
public:
|
double computeTotalInternalPotentialEnergy();
|
||||||
FormFinder();
|
|
||||||
|
public:
|
||||||
|
DRMSimulationModel();
|
||||||
SimulationResults
|
SimulationResults
|
||||||
executeSimulation(const std::shared_ptr<SimulationJob> &pJob,
|
executeSimulation(const std::shared_ptr<SimulationJob> &pJob,
|
||||||
const Settings &settings = Settings());
|
const Settings &settings = Settings());
|
||||||
128
edgemesh.cpp
128
edgemesh.cpp
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
Eigen::MatrixX2i VCGEdgeMesh::getEigenEdges() const { return eigenEdges; }
|
Eigen::MatrixX2i VCGEdgeMesh::getEigenEdges() const { return eigenEdges; }
|
||||||
|
|
||||||
Eigen::MatrixX3d VCGEdgeMesh::getEigenVertices() const {
|
Eigen::MatrixX3d VCGEdgeMesh::getEigenVertices() {
|
||||||
// getVertices(eigenVertices);
|
getVertices(eigenVertices);
|
||||||
return eigenVertices;
|
return eigenVertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -13,16 +13,18 @@ Eigen::MatrixX3d VCGEdgeMesh::getEigenEdgeNormals() const {
|
||||||
return eigenEdgeNormals;
|
return eigenEdgeNormals;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VCGEdgeMesh::savePly(const std::string plyFilename) {
|
bool VCGEdgeMesh::save(const string &plyFilename)
|
||||||
unsigned int mask = 0;
|
{
|
||||||
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
|
assert(std::filesystem::path(plyFilename).extension() == ".ply");
|
||||||
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
|
unsigned int mask = 0;
|
||||||
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
|
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
|
||||||
if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::SaveModel(
|
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
|
||||||
plyFilename.c_str(), *this, mask, false) != 0) {
|
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
|
||||||
return false;
|
if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::SaveModel(plyFilename.c_str(), *this, mask, false)
|
||||||
}
|
!= 0) {
|
||||||
return true;
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCGEdgeMesh::GeneratedRegularSquaredPattern(
|
void VCGEdgeMesh::GeneratedRegularSquaredPattern(
|
||||||
|
|
@ -199,6 +201,8 @@ bool VCGEdgeMesh::load(const string &plyFilename) {
|
||||||
std::cout << plyFilename << " was loaded successfuly." << std::endl;
|
std::cout << plyFilename << " was loaded successfuly." << std::endl;
|
||||||
std::cout << "Mesh has " << EN() << " edges." << std::endl;
|
std::cout << "Mesh has " << EN() << " edges." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label=std::filesystem::path(plyFilename).stem();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -275,7 +279,7 @@ void VCGEdgeMesh::updateEigenEdgeAndVertices() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCGEdgeMesh::copy(VCGEdgeMesh &mesh) {
|
bool VCGEdgeMesh::copy(VCGEdgeMesh &mesh) {
|
||||||
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::MeshCopy(*this, mesh);
|
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::MeshCopy(*this, mesh);
|
||||||
label = mesh.getLabel();
|
label = mesh.getLabel();
|
||||||
eigenEdges = mesh.getEigenEdges();
|
eigenEdges = mesh.getEigenEdges();
|
||||||
|
|
@ -287,19 +291,47 @@ void VCGEdgeMesh::copy(VCGEdgeMesh &mesh) {
|
||||||
getVertices(eigenVertices);
|
getVertices(eigenVertices);
|
||||||
}
|
}
|
||||||
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
|
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) {
|
void VCGEdgeMesh::getVertices(Eigen::MatrixX3d &vertices) {
|
||||||
vertices = Eigen::MatrixX3d();
|
vertices = Eigen::MatrixX3d();
|
||||||
vertices.resize(VN(), 3);
|
vertices.resize(VN(), 3);
|
||||||
for (int vi = 0; vi < VN(); vi++) {
|
for (int vi = 0; vi < VN(); vi++) {
|
||||||
if (vert[vi].IsD()) {
|
if (vert[vi].IsD()) {
|
||||||
continue;
|
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; }
|
std::string VCGEdgeMesh::getLabel() const { return label; }
|
||||||
|
|
@ -329,18 +361,25 @@ void VCGEdgeMesh::printVertexCoordinates(const size_t &vi) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef POLYSCOPE_DEFINED
|
#ifdef POLYSCOPE_DEFINED
|
||||||
void VCGEdgeMesh::registerForDrawing(
|
//TODO: make const getEigenVertices is not
|
||||||
const std::optional<glm::vec3> &desiredColor,
|
polyscope::CurveNetwork *VCGEdgeMesh::registerForDrawing(
|
||||||
const bool &shouldEnable) const {
|
const std::optional<std::array<double, 3>> &desiredColor, const bool &shouldEnable)
|
||||||
initPolyscope();
|
{
|
||||||
const double drawingRadius = 0.002;
|
initPolyscope();
|
||||||
auto polyscopeHandle_edgeMesh = polyscope::registerCurveNetwork(
|
const double drawingRadius = 0.002;
|
||||||
label, getEigenVertices(), getEigenEdges());
|
polyscope::CurveNetwork *polyscopeHandle_edgeMesh(
|
||||||
polyscopeHandle_edgeMesh->setEnabled(shouldEnable);
|
polyscope::registerCurveNetwork(label, getEigenVertices(), getEigenEdges()));
|
||||||
polyscopeHandle_edgeMesh->setRadius(drawingRadius, true);
|
|
||||||
if (desiredColor.has_value()) {
|
polyscopeHandle_edgeMesh->setEnabled(shouldEnable);
|
||||||
polyscopeHandle_edgeMesh->setColor(desiredColor.value());
|
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 {
|
void VCGEdgeMesh::unregister() const {
|
||||||
|
|
@ -352,4 +391,25 @@ void VCGEdgeMesh::unregister() const {
|
||||||
}
|
}
|
||||||
polyscope::removeCurveNetwork(label);
|
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
|
#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);
|
return vcg::tri::Index<VCGEdgeMesh>(*this, meshElement);
|
||||||
}
|
}
|
||||||
void updateEigenEdgeAndVertices();
|
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,
|
void getEdges(Eigen::MatrixX3d &edgeStartingPoints,
|
||||||
Eigen::MatrixX3d &edgeEndingPoints) const;
|
Eigen::MatrixX3d &edgeEndingPoints) const;
|
||||||
|
|
||||||
Eigen::MatrixX3d getNormals() 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);
|
Eigen::MatrixX2i getEigenEdges() const;
|
||||||
bool createSpanGrid(const size_t desiredWidth, const size_t desiredHeight);
|
Eigen::MatrixX3d getEigenVertices();
|
||||||
void createSpiral(const float °reesOfArm, const size_t &numberOfSamples);
|
|
||||||
|
|
||||||
Eigen::MatrixX2i getEigenEdges() const;
|
|
||||||
Eigen::MatrixX3d getEigenVertices() const;
|
|
||||||
Eigen::MatrixX3d getEigenEdgeNormals() const;
|
Eigen::MatrixX3d getEigenEdgeNormals() const;
|
||||||
void printVertexCoordinates(const size_t &vi) const;
|
void printVertexCoordinates(const size_t &vi) const;
|
||||||
#ifdef POLYSCOPE_DEFINED
|
#ifdef POLYSCOPE_DEFINED
|
||||||
void registerForDrawing(
|
polyscope::CurveNetwork *registerForDrawing(
|
||||||
const std::optional<glm::vec3> &desiredColor = std::nullopt,
|
const std::optional<std::array<double, 3>> &desiredColor = std::nullopt,
|
||||||
const bool &shouldEnable = true) const;
|
const bool &shouldEnable = true);
|
||||||
void unregister() const;
|
void unregister() const;
|
||||||
|
void drawInitialFrames(polyscope::CurveNetwork *polyscopeHandle_initialMesh) const;
|
||||||
#endif
|
#endif
|
||||||
std::string getLabel() const;
|
std::string getLabel() const;
|
||||||
void setLabel(const std::string &value);
|
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
|
#ifndef MESH_HPP
|
||||||
#define MESH_HPP
|
#define MESH_HPP
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class Mesh {
|
class Mesh
|
||||||
std::string label;
|
{
|
||||||
|
protected:
|
||||||
|
std::string label;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~Mesh() = default;
|
virtual ~Mesh() = default;
|
||||||
virtual bool load(const std::string &filePath) { return false; }
|
virtual bool load(const std::string &filePath) { return false; }
|
||||||
|
virtual bool save(const std::string &filePath) { return false; }
|
||||||
|
|
||||||
std::string getLabel() const;
|
std::string getLabel() const;
|
||||||
void setLabel(const std::string &newLabel);
|
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;
|
PatternGeometry p;
|
||||||
p.add(vertices, patterns[patternIndex].edges);
|
p.add(vertices, patterns[patternIndex].edges);
|
||||||
auto tiled = p.createTile(p);
|
auto tiled = p.createTile(p);
|
||||||
p.savePly(
|
p.save(
|
||||||
std::filesystem::path(outputDirectoryPath)
|
std::filesystem::path(outputDirectoryPath)
|
||||||
.append(std::to_string(patterns[patternIndex].name) + ".ply"));
|
.append(std::to_string(patterns[patternIndex].name) + ".ply"));
|
||||||
tiled.savePly(std::filesystem::path(outputDirectoryPath)
|
tiled.save(std::filesystem::path(outputDirectoryPath)
|
||||||
.append("tiled_" +
|
.append("tiled_" +
|
||||||
std::to_string(patterns[patternIndex].name) +
|
std::to_string(patterns[patternIndex].name) +
|
||||||
".ply"));
|
".ply"));
|
||||||
|
|
|
||||||
233
polymesh.hpp
233
polymesh.hpp
|
|
@ -3,73 +3,206 @@
|
||||||
#include "mesh.hpp"
|
#include "mesh.hpp"
|
||||||
#include "vcg/complex/complex.h"
|
#include "vcg/complex/complex.h"
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <polyscope/surface_mesh.h>
|
||||||
#include <wrap/io_trimesh/import.h>
|
#include <wrap/io_trimesh/import.h>
|
||||||
|
|
||||||
class PFace;
|
class PFace;
|
||||||
class PVertex;
|
class PVertex;
|
||||||
|
class PEdge;
|
||||||
|
|
||||||
struct PUsedTypes : public vcg::UsedTypes<vcg::Use<PVertex>::AsVertexType,
|
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,
|
class PVertex : public vcg::Vertex<PUsedTypes,
|
||||||
vcg::vertex::Normal3d, vcg::vertex::Mark,
|
vcg::vertex::Coord3d,
|
||||||
vcg::vertex::Qualityf, vcg::vertex::BitFlags,
|
vcg::vertex::Normal3d,
|
||||||
vcg::vertex::VFAdj> {};
|
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
|
class PFace : public vcg::Face<PUsedTypes,
|
||||||
: public vcg::Face<
|
vcg::face::PolyInfo // this is necessary if you use component in
|
||||||
PUsedTypes,
|
// vcg/simplex/face/component_polygon.h
|
||||||
vcg::face::PolyInfo // this is necessary if you use component in
|
// It says "this class is a polygon and the memory for its components
|
||||||
// vcg/simplex/face/component_polygon.h
|
// (e.g. pointer to its vertices will be allocated dynamically")
|
||||||
// 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 // Pointer to the vertices (just like FVAdj )
|
vcg::face::PFVAdj,
|
||||||
,
|
// vcg::face::PVFAdj,
|
||||||
vcg::face::PFVAdj,
|
vcg::face::PFFAdj // Pointer to edge-adjacent face (just like FFAdj )
|
||||||
vcg::face::PFFAdj // Pointer to edge-adjacent face (just like FFAdj )
|
,
|
||||||
,
|
vcg::face::BitFlags // bit flags
|
||||||
vcg::face::BitFlags // bit flags
|
,
|
||||||
,
|
vcg::face::Qualityf // quality
|
||||||
vcg::face::Qualityf // quality
|
,
|
||||||
,
|
vcg::face::Normal3f // normal
|
||||||
vcg::face::Normal3f // normal
|
>
|
||||||
> {};
|
{};
|
||||||
|
|
||||||
class VCGPolyMesh
|
class VCGPolyMesh
|
||||||
: public vcg::tri::TriMesh<std::vector<PVertex>, std::vector<PFace>>,
|
: public vcg::tri::TriMesh<std::vector<PVertex>, std::vector<PFace>, std::vector<PEdge>>,
|
||||||
public Mesh {
|
public Mesh
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
virtual bool load(const std::string &filename) override {
|
// virtual bool load(const std::string &filename) override {
|
||||||
int mask;
|
// int mask;
|
||||||
vcg::tri::io::Importer<VCGPolyMesh>::LoadMask(filename.c_str(), mask);
|
// vcg::tri::io::Importer<VCGPolyMesh>::LoadMask(filename.c_str(), mask);
|
||||||
int error = vcg::tri::io::Importer<VCGPolyMesh>::Open(
|
// int error = vcg::tri::io::Importer<VCGPolyMesh>::Open(
|
||||||
*this, filename.c_str(), mask);
|
// *this, filename.c_str(), mask);
|
||||||
if (error != 0) {
|
// if (error != 0) {
|
||||||
return false;
|
// 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;
|
|
||||||
// }
|
// }
|
||||||
vcg::tri::UpdateTopology<VCGPolyMesh>::FaceFace(*this);
|
// // vcg::tri::io::ImporterOBJ<VCGPolyMesh>::Open();
|
||||||
// vcg::tri::UpdateTopology<VCGPolyMesh>::VertexFace(*this);
|
// // unsigned int mask = 0;
|
||||||
vcg::tri::UpdateNormal<VCGPolyMesh>::PerVertexNormalized(*this);
|
// // mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_VERTCOORD;
|
||||||
return true;
|
// // 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
|
#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
|
#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
|
#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
|
#ifndef SIMULATIONSTRUCTS_HPP
|
||||||
#define SIMULATIONHISTORY_HPP
|
#define SIMULATIONSTRUCTS_HPP
|
||||||
|
|
||||||
#include "elementalmesh.hpp"
|
#include "simulationmesh.hpp"
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
|
|
||||||
struct SimulationHistory {
|
struct SimulationHistory {
|
||||||
|
|
@ -32,6 +32,7 @@ struct SimulationHistory {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
namespace nlohmann {
|
namespace nlohmann {
|
||||||
|
|
||||||
template <> struct adl_serializer<std::unordered_map<VertexIndex, Vector6d>> {
|
template <> struct adl_serializer<std::unordered_map<VertexIndex, Vector6d>> {
|
||||||
|
|
@ -202,7 +203,7 @@ public:
|
||||||
std::filesystem::path(pathFolderDirectory)))
|
std::filesystem::path(pathFolderDirectory)))
|
||||||
.append(pMesh->getLabel() + ".ply")
|
.append(pMesh->getLabel() + ".ply")
|
||||||
.string();
|
.string();
|
||||||
returnValue = pMesh->savePly(meshFilename);
|
returnValue = pMesh->save(meshFilename);
|
||||||
nlohmann::json json;
|
nlohmann::json json;
|
||||||
json[jsonLabels.meshFilename] = std::filesystem::relative(
|
json[jsonLabels.meshFilename] = std::filesystem::relative(
|
||||||
std::filesystem::path(meshFilename),
|
std::filesystem::path(meshFilename),
|
||||||
|
|
@ -333,6 +334,8 @@ struct SimulationResults {
|
||||||
std::shared_ptr<SimulationJob> job;
|
std::shared_ptr<SimulationJob> job;
|
||||||
SimulationHistory history;
|
SimulationHistory history;
|
||||||
std::vector<Vector6d> displacements;
|
std::vector<Vector6d> displacements;
|
||||||
|
std::vector<Eigen::Quaternion<double>> rotationalDisplacementQuaternion; //per vertex
|
||||||
|
double internalPotentialEnergy{0};
|
||||||
double executionTime{0};
|
double executionTime{0};
|
||||||
std::string labelPrefix{"deformed"};
|
std::string labelPrefix{"deformed"};
|
||||||
inline static char deliminator{' '};
|
inline static char deliminator{' '};
|
||||||
|
|
@ -364,7 +367,7 @@ struct SimulationResults {
|
||||||
m.vert[vi].P() + CoordType(displacements[vi][0], displacements[vi][1],
|
m.vert[vi].P() + CoordType(displacements[vi][0], displacements[vi][1],
|
||||||
displacements[vi][2]);
|
displacements[vi][2]);
|
||||||
}
|
}
|
||||||
m.savePly(getLabel() + ".ply");
|
m.save(getLabel() + ".ply");
|
||||||
}
|
}
|
||||||
|
|
||||||
void save() {
|
void save() {
|
||||||
|
|
@ -395,41 +398,71 @@ struct SimulationResults {
|
||||||
}
|
}
|
||||||
polyscope::removeCurveNetwork(getLabel());
|
polyscope::removeCurveNetwork(getLabel());
|
||||||
}
|
}
|
||||||
void registerForDrawing(
|
void registerForDrawing(const std::optional<std::array<double, 3>> &desiredColor = std::nullopt,
|
||||||
const std::optional<glm::vec3> &desiredColor = std::nullopt,
|
const bool &shouldEnable = true,
|
||||||
const bool &shouldEnable = true) const {
|
const bool &showFrames = false) const
|
||||||
polyscope::options::groundPlaneEnabled = false;
|
{
|
||||||
polyscope::view::upDir = polyscope::view::UpDir::ZUp;
|
polyscope::options::groundPlaneEnabled = false;
|
||||||
const std::string branchName = "Branch:Polyscope";
|
polyscope::view::upDir = polyscope::view::UpDir::ZUp;
|
||||||
polyscope::options::programName = branchName;
|
if (!polyscope::state::initialized) {
|
||||||
if (!polyscope::state::initialized) {
|
polyscope::init();
|
||||||
polyscope::init();
|
} /* else {
|
||||||
} /* else {
|
|
||||||
polyscope::removeAllStructures();
|
polyscope::removeAllStructures();
|
||||||
}*/
|
}*/
|
||||||
// const std::string undeformedMeshName = "Undeformed_" + label;
|
// const std::string undeformedMeshName = "Undeformed_" + label;
|
||||||
// polyscope::registerCurveNetwork(
|
// polyscope::registerCurveNetwork(
|
||||||
// undeformedMeshName, mesh->getEigenVertices(),
|
// undeformedMeshName, mesh->getEigenVertices(),
|
||||||
// mesh->getEigenEdges());
|
// mesh->getEigenEdges());
|
||||||
|
|
||||||
const std::shared_ptr<SimulationMesh> &mesh = job->pMesh;
|
const std::shared_ptr<SimulationMesh> &mesh = job->pMesh;
|
||||||
auto polyscopeHandle_deformedEdmeMesh = polyscope::registerCurveNetwork(
|
auto polyscopeHandle_deformedEdmeMesh
|
||||||
getLabel(), mesh->getEigenVertices(), mesh->getEigenEdges());
|
= polyscope::registerCurveNetwork(getLabel(),
|
||||||
polyscopeHandle_deformedEdmeMesh->setEnabled(shouldEnable);
|
mesh->getEigenVertices(),
|
||||||
polyscopeHandle_deformedEdmeMesh->setRadius(0.002, true);
|
mesh->getEigenEdges());
|
||||||
if (desiredColor.has_value()) {
|
polyscopeHandle_deformedEdmeMesh->setEnabled(shouldEnable);
|
||||||
polyscopeHandle_deformedEdmeMesh->setColor(desiredColor.value());
|
polyscopeHandle_deformedEdmeMesh->setRadius(0.002, true);
|
||||||
}
|
if (desiredColor.has_value()) {
|
||||||
Eigen::MatrixX3d nodalDisplacements(mesh->VN(), 3);
|
const glm::vec3 desiredColor_glm(desiredColor.value()[0],
|
||||||
for (VertexIndex vi = 0; vi < mesh->VN(); vi++) {
|
desiredColor.value()[1],
|
||||||
const Vector6d &nodalDisplacement = displacements[vi];
|
desiredColor.value()[2]);
|
||||||
nodalDisplacements.row(vi) = Eigen::Vector3d(
|
polyscopeHandle_deformedEdmeMesh->setColor(desiredColor_glm);
|
||||||
nodalDisplacement[0], nodalDisplacement[1], nodalDisplacement[2]);
|
}
|
||||||
}
|
Eigen::MatrixX3d nodalDisplacements(mesh->VN(), 3);
|
||||||
polyscopeHandle_deformedEdmeMesh->updateNodePositions(
|
Eigen::MatrixX3d framesX(mesh->VN(), 3);
|
||||||
mesh->getEigenVertices() + nodalDisplacements);
|
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
|
#endif
|
||||||
};
|
};
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
#ifndef SIMULATIONHISTORYPLOTTER_HPP
|
#ifndef SIMULATIONHISTORYPLOTTER_HPP
|
||||||
#define SIMULATIONHISTORYPLOTTER_HPP
|
#define SIMULATIONHISTORYPLOTTER_HPP
|
||||||
|
|
||||||
#include "elementalmesh.hpp"
|
#include "simulationmesh.hpp"
|
||||||
#include "simulationresult.hpp"
|
#include "simulation_structs.hpp"
|
||||||
#include "utilities.hpp"
|
#include "utilities.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <matplot/matplot.h>
|
#include <matplot/matplot.h>
|
||||||
|
|
|
||||||
|
|
@ -1,83 +1,59 @@
|
||||||
#include "elementalmesh.hpp"
|
#include "simulationmesh.hpp"
|
||||||
#include <wrap/nanoply/include/nanoplyWrapper.hpp>
|
#include <wrap/nanoply/include/nanoplyWrapper.hpp>
|
||||||
|
|
||||||
SimulationMesh::SimulationMesh() {
|
SimulationMesh::SimulationMesh() {
|
||||||
elements = vcg::tri::Allocator<VCGEdgeMesh>::GetPerEdgeAttribute<Element>(
|
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(
|
||||||
*this, std::string("Elements"));
|
*this, std::string("Elements"));
|
||||||
nodes = vcg::tri::Allocator<VCGEdgeMesh>::GetPerVertexAttribute<Node>(
|
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(
|
||||||
*this, std::string("Nodes"));
|
*this, std::string("Nodes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
SimulationMesh::SimulationMesh(VCGEdgeMesh &mesh) {
|
SimulationMesh::SimulationMesh(VCGEdgeMesh &mesh) {
|
||||||
vcg::tri::MeshAssert<VCGEdgeMesh>::VertexNormalNormalized(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) {
|
VCGEdgeMesh::copy(mesh);
|
||||||
// for (VertexIterator vi = mesh.vert.begin(); vi != mesh.vert.end(); ++vi)
|
|
||||||
// if (!vi->IsD()) {
|
|
||||||
// vi->N() = CoordType(1, 0, 0);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
vcg::tri::Append<VCGEdgeMesh, ConstVCGEdgeMesh>::MeshCopy(*this, mesh);
|
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(
|
||||||
elements = vcg::tri::Allocator<VCGEdgeMesh>::GetPerEdgeAttribute<Element>(
|
|
||||||
*this, std::string("Elements"));
|
*this, std::string("Elements"));
|
||||||
nodes = vcg::tri::Allocator<VCGEdgeMesh>::GetPerVertexAttribute<Node>(
|
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(
|
||||||
*this, std::string("Nodes"));
|
*this, std::string("Nodes"));
|
||||||
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
|
|
||||||
initializeNodes();
|
initializeNodes();
|
||||||
initializeElements();
|
initializeElements();
|
||||||
label = mesh.getLabel();
|
|
||||||
eigenEdges = mesh.getEigenEdges();
|
|
||||||
eigenVertices = mesh.getEigenVertices();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SimulationMesh::~SimulationMesh() {
|
SimulationMesh::~SimulationMesh() {
|
||||||
vcg::tri::Allocator<VCGEdgeMesh>::DeletePerEdgeAttribute<Element>(*this,
|
vcg::tri::Allocator<SimulationMesh>::DeletePerEdgeAttribute<Element>(*this,
|
||||||
elements);
|
elements);
|
||||||
vcg::tri::Allocator<VCGEdgeMesh>::DeletePerVertexAttribute<Node>(*this,
|
vcg::tri::Allocator<SimulationMesh>::DeletePerVertexAttribute<Node>(*this,
|
||||||
nodes);
|
nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
SimulationMesh::SimulationMesh(PatternGeometry &pattern) {
|
SimulationMesh::SimulationMesh(PatternGeometry &pattern) {
|
||||||
vcg::tri::MeshAssert<PatternGeometry>::VertexNormalNormalized(pattern);
|
vcg::tri::MeshAssert<PatternGeometry>::VertexNormalNormalized(pattern);
|
||||||
|
VCGEdgeMesh::copy(pattern);
|
||||||
|
|
||||||
vcg::tri::Append<VCGEdgeMesh, ConstVCGEdgeMesh>::MeshCopy(*this, pattern);
|
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(
|
||||||
elements = vcg::tri::Allocator<VCGEdgeMesh>::GetPerEdgeAttribute<Element>(
|
|
||||||
*this, std::string("Elements"));
|
*this, std::string("Elements"));
|
||||||
nodes = vcg::tri::Allocator<VCGEdgeMesh>::GetPerVertexAttribute<Node>(
|
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(
|
||||||
*this, std::string("Nodes"));
|
*this, std::string("Nodes"));
|
||||||
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
|
|
||||||
initializeNodes();
|
initializeNodes();
|
||||||
initializeElements();
|
initializeElements();
|
||||||
label = pattern.getLabel();
|
|
||||||
eigenEdges = pattern.getEigenEdges();
|
|
||||||
eigenVertices = pattern.getEigenVertices();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SimulationMesh::SimulationMesh(SimulationMesh &mesh) {
|
SimulationMesh::SimulationMesh(SimulationMesh &mesh) {
|
||||||
vcg::tri::Append<VCGEdgeMesh, ConstVCGEdgeMesh>::MeshCopy(*this, mesh);
|
vcg::tri::MeshAssert<SimulationMesh>::VertexNormalNormalized(mesh);
|
||||||
elements = vcg::tri::Allocator<VCGEdgeMesh>::GetPerEdgeAttribute<Element>(
|
VCGEdgeMesh::copy(mesh);
|
||||||
|
|
||||||
|
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(
|
||||||
*this, std::string("Elements"));
|
*this, std::string("Elements"));
|
||||||
nodes = vcg::tri::Allocator<VCGEdgeMesh>::GetPerVertexAttribute<Node>(
|
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(
|
||||||
*this, std::string("Nodes"));
|
*this, std::string("Nodes"));
|
||||||
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
|
|
||||||
initializeNodes();
|
initializeNodes();
|
||||||
|
|
||||||
for (size_t ei = 0; ei < EN(); ei++) {
|
for (size_t ei = 0; ei < EN(); ei++) {
|
||||||
elements[ei] = mesh.elements[ei];
|
elements[ei] = mesh.elements[ei];
|
||||||
}
|
}
|
||||||
|
reset();
|
||||||
label = mesh.label;
|
|
||||||
eigenEdges = mesh.getEigenEdges();
|
|
||||||
eigenVertices = mesh.getEigenVertices();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulationMesh::computeElementalProperties() {
|
void SimulationMesh::computeElementalProperties() {
|
||||||
|
|
@ -106,36 +82,48 @@ void SimulationMesh::initializeNodes() {
|
||||||
node.displacements[3] =
|
node.displacements[3] =
|
||||||
v.cN()[0]; // initialize nx diplacement with vertex normal x
|
v.cN()[0]; // initialize nx diplacement with vertex normal x
|
||||||
// component
|
// component
|
||||||
node.displacements[4] =
|
node.displacements[4] = v.cN()[1]; // initialize ny(in the paper) diplacement with vertex
|
||||||
v.cN()[1]; // initialize ny(in the paper) diplacement with vertex
|
|
||||||
// normal
|
// normal
|
||||||
// y component.
|
// y component.
|
||||||
|
|
||||||
// Initialize incident elements
|
// Initialize incident elements
|
||||||
std::vector<VCGEdgeMesh::EdgePointer> incidentElements;
|
std::vector<VCGEdgeMesh::EdgePointer> incidentElements;
|
||||||
vcg::edge::VEStarVE(&v, incidentElements);
|
vcg::edge::VEStarVE(&v, incidentElements);
|
||||||
assert(
|
// assert(
|
||||||
vcg::tri::IsValidPointer<SimulationMesh>(*this, incidentElements[0]) &&
|
// vcg::tri::IsValidPointer<SimulationMesh>(*this, incidentElements[0]) &&
|
||||||
incidentElements.size() > 0);
|
// incidentElements.size() > 0);
|
||||||
nodes[v].incidentElements = incidentElements;
|
if (incidentElements.size() != 0) {
|
||||||
node.referenceElement = getReferenceElement(v);
|
nodes[v].incidentElements = incidentElements;
|
||||||
// Initialze alpha angles
|
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 EdgeType &referenceElement = *node.referenceElement;
|
||||||
const VectorType t01 =
|
const VectorType t01 = computeT1Vector(referenceElement.cP(0), referenceElement.cP(1));
|
||||||
computeT1Vector(referenceElement.cP(0), referenceElement.cP(1));
|
const VectorType f01 = (t01 - (v.cN() * (t01.dot(v.cN())))).Normalize();
|
||||||
const VectorType f01 = (t01 - (v.cN() * (t01.dot(v.cN())))).Normalize();
|
|
||||||
|
|
||||||
for (const VCGEdgeMesh::EdgePointer &ep : nodes[v].incidentElements) {
|
for (const VCGEdgeMesh::EdgePointer &ep : nodes[v].incidentElements) {
|
||||||
assert(referenceElement.cV(0) == ep->cV(0) ||
|
assert(referenceElement.cV(0) == ep->cV(0) || referenceElement.cV(0) == ep->cV(1)
|
||||||
referenceElement.cV(0) == ep->cV(1) ||
|
|| referenceElement.cV(1) == ep->cV(0) || referenceElement.cV(1) == ep->cV(1));
|
||||||
referenceElement.cV(1) == ep->cV(0) ||
|
const VectorType t1 = computeT1Vector(*ep);
|
||||||
referenceElement.cV(1) == ep->cV(1));
|
const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize();
|
||||||
const VectorType t1 = computeT1Vector(*ep);
|
const EdgeIndex ei = getIndex(ep);
|
||||||
const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize();
|
const double alphaAngle = computeAngle(f01, f1, v.cN());
|
||||||
const EdgeIndex ei = getIndex(ep);
|
node.alphaAngles[ei] = alphaAngle;
|
||||||
const double alphaAngle = computeAngle(f01, f1, v.cN());
|
}
|
||||||
node.alphaAngles[ei] = alphaAngle;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -159,29 +147,24 @@ void SimulationMesh::reset() {
|
||||||
node.initialNormal = v.cN();
|
node.initialNormal = v.cN();
|
||||||
node.derivativeOfNormal.resize(6, VectorType(0, 0, 0));
|
node.derivativeOfNormal.resize(6, VectorType(0, 0, 0));
|
||||||
|
|
||||||
node.displacements[3] =
|
node.displacements[3] = v.cN()[0]; // initialize nx diplacement with vertex normal x
|
||||||
v.cN()[0]; // initialize nx diplacement with vertex normal x
|
// component
|
||||||
// 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
|
// normal
|
||||||
// y component.
|
// y component.
|
||||||
|
|
||||||
const EdgeType &referenceElement = *getReferenceElement(v);
|
const EdgeType &referenceElement = *getReferenceElement(v);
|
||||||
const VectorType t01 =
|
const VectorType t01 = computeT1Vector(referenceElement.cP(0), referenceElement.cP(1));
|
||||||
computeT1Vector(referenceElement.cP(0), referenceElement.cP(1));
|
|
||||||
const VectorType f01 = (t01 - (v.cN() * (t01.dot(v.cN())))).Normalize();
|
const VectorType f01 = (t01 - (v.cN() * (t01.dot(v.cN())))).Normalize();
|
||||||
|
|
||||||
for (const VCGEdgeMesh::EdgePointer &ep : nodes[v].incidentElements) {
|
for (const VCGEdgeMesh::EdgePointer &ep : nodes[v].incidentElements) {
|
||||||
assert(referenceElement.cV(0) == ep->cV(0) ||
|
assert(referenceElement.cV(0) == ep->cV(0) || referenceElement.cV(0) == ep->cV(1)
|
||||||
referenceElement.cV(0) == ep->cV(1) ||
|
|| referenceElement.cV(1) == ep->cV(0) || referenceElement.cV(1) == ep->cV(1));
|
||||||
referenceElement.cV(1) == ep->cV(0) ||
|
const VectorType t1 = computeT1Vector(*ep);
|
||||||
referenceElement.cV(1) == ep->cV(1));
|
const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize();
|
||||||
const VectorType t1 = computeT1Vector(*ep);
|
const EdgeIndex ei = getIndex(ep);
|
||||||
const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize();
|
const double alphaAngle = computeAngle(f01, f1, v.cN());
|
||||||
const EdgeIndex ei = getIndex(ep);
|
node.alphaAngles[ei] = alphaAngle;
|
||||||
const double alphaAngle = computeAngle(f01, f1, v.cN());
|
|
||||||
node.alphaAngles[ei] = alphaAngle;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -358,38 +341,44 @@ bool SimulationMesh::load(const string &plyFilename) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimulationMesh::savePly(const std::string &plyFilename) {
|
bool SimulationMesh::save(const std::string &plyFilename)
|
||||||
nanoply::NanoPlyWrapper<VCGEdgeMesh>::CustomAttributeDescriptor customAttrib;
|
{
|
||||||
customAttrib.GetMeshAttrib(plyFilename);
|
nanoply::NanoPlyWrapper<VCGEdgeMesh>::CustomAttributeDescriptor customAttrib;
|
||||||
|
customAttrib.GetMeshAttrib(plyFilename);
|
||||||
|
|
||||||
std::vector<CrossSectionType> dimensions = getBeamDimensions();
|
std::vector<CrossSectionType> dimensions = getBeamDimensions();
|
||||||
customAttrib.AddEdgeAttribDescriptor<CrossSectionType, double, 2>(
|
customAttrib.AddEdgeAttribDescriptor<CrossSectionType, double, 2>(plyPropertyBeamDimensionsID,
|
||||||
plyPropertyBeamDimensionsID, nanoply::NNP_LIST_INT8_FLOAT64,
|
nanoply::NNP_LIST_INT8_FLOAT64,
|
||||||
dimensions.data());
|
dimensions.data());
|
||||||
std::vector<ElementMaterial> material = getBeamMaterial();
|
std::vector<ElementMaterial> material = getBeamMaterial();
|
||||||
customAttrib.AddEdgeAttribDescriptor<vcg::Point2d, double, 2>(
|
customAttrib.AddEdgeAttribDescriptor<vcg::Point2d, double, 2>(plyPropertyBeamMaterialID,
|
||||||
plyPropertyBeamMaterialID, nanoply::NNP_LIST_INT8_FLOAT64,
|
nanoply::NNP_LIST_INT8_FLOAT64,
|
||||||
material.data());
|
material.data());
|
||||||
unsigned int mask = 0;
|
unsigned int mask = 0;
|
||||||
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
|
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
|
||||||
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
|
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
|
||||||
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEATTRIB;
|
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEATTRIB;
|
||||||
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
|
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
|
||||||
if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::SaveModel(
|
if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::SaveModel(plyFilename.c_str(),
|
||||||
plyFilename.c_str(), *this, mask, customAttrib, false) != 1) {
|
*this,
|
||||||
return false;
|
mask,
|
||||||
}
|
customAttrib,
|
||||||
return true;
|
false)
|
||||||
|
!= 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SimulationMesh::EdgePointer
|
SimulationMesh::EdgePointer
|
||||||
SimulationMesh::getReferenceElement(const VCGEdgeMesh::VertexType &v) {
|
SimulationMesh::getReferenceElement(const VCGEdgeMesh::VertexType &v) {
|
||||||
const VertexIndex vi = getIndex(v);
|
const VertexIndex vi = getIndex(v);
|
||||||
return nodes[v].incidentElements[0];
|
return nodes[v].incidentElements[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
VectorType computeT1Vector(const SimulationMesh::EdgeType &e) {
|
VectorType computeT1Vector(const SimulationMesh::EdgeType &e)
|
||||||
return computeT1Vector(e.cP(0), e.cP(1));
|
{
|
||||||
|
return computeT1Vector(e.cP(0), e.cP(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
VectorType computeT1Vector(const CoordType &p0, const CoordType &p1) {
|
VectorType computeT1Vector(const CoordType &p0, const CoordType &p1) {
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
#ifndef ELEMENTALMESH_HPP
|
#ifndef SIMULATIONMESH_HPP
|
||||||
#define ELEMENTALMESH_HPP
|
#define SIMULATIONMESH_HPP
|
||||||
|
|
||||||
#include "Eigen/Dense"
|
#include "Eigen/Dense"
|
||||||
#include "edgemesh.hpp"
|
#include "edgemesh.hpp"
|
||||||
#include "trianglepatterngeometry.hpp"
|
#include "trianglepatterngeometry.hpp"
|
||||||
|
|
||||||
|
//extern bool drawGlobal;
|
||||||
struct Element;
|
struct Element;
|
||||||
struct Node;
|
struct Node;
|
||||||
using CrossSectionType = RectangularBeamDimensions;
|
using CrossSectionType = RectangularBeamDimensions;
|
||||||
|
|
@ -28,6 +29,7 @@ public:
|
||||||
SimulationMesh(ConstVCGEdgeMesh &edgeMesh);
|
SimulationMesh(ConstVCGEdgeMesh &edgeMesh);
|
||||||
SimulationMesh(SimulationMesh &elementalMesh);
|
SimulationMesh(SimulationMesh &elementalMesh);
|
||||||
void updateElementalLengths();
|
void updateElementalLengths();
|
||||||
|
void updateIncidentElements();
|
||||||
|
|
||||||
std::vector<VCGEdgeMesh::EdgePointer>
|
std::vector<VCGEdgeMesh::EdgePointer>
|
||||||
getIncidentElements(const VCGEdgeMesh::VertexType &v);
|
getIncidentElements(const VCGEdgeMesh::VertexType &v);
|
||||||
|
|
@ -42,7 +44,7 @@ public:
|
||||||
double totalResidualForcesNorm{0};
|
double totalResidualForcesNorm{0};
|
||||||
double currentTotalPotentialEnergykN{0};
|
double currentTotalPotentialEnergykN{0};
|
||||||
double previousTotalPotentialEnergykN{0};
|
double previousTotalPotentialEnergykN{0};
|
||||||
bool savePly(const std::string &plyFilename);
|
bool save(const std::string &plyFilename);
|
||||||
void setBeamCrossSection(const CrossSectionType &beamDimensions);
|
void setBeamCrossSection(const CrossSectionType &beamDimensions);
|
||||||
void setBeamMaterial(const double &pr, const double &ym);
|
void setBeamMaterial(const double &pr, const double &ym);
|
||||||
void reset();
|
void reset();
|
||||||
|
|
@ -162,7 +164,7 @@ struct Node {
|
||||||
Element::LocalFrame computeElementFrame(const CoordType &p0,
|
Element::LocalFrame computeElementFrame(const CoordType &p0,
|
||||||
const CoordType &p1,
|
const CoordType &p1,
|
||||||
const VectorType &elementNormal);
|
const VectorType &elementNormal);
|
||||||
VectorType computeT1Vector(const ::SimulationMesh::EdgeType &e);
|
VectorType computeT1Vector(const SimulationMesh::EdgeType &e);
|
||||||
|
|
||||||
VectorType computeT1Vector(const CoordType &p0, const CoordType &p1);
|
VectorType computeT1Vector(const CoordType &p0, const CoordType &p1);
|
||||||
double computeAngle(const VectorType &vector0, const VectorType &vector1,
|
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();
|
TopologyEnumerator();
|
||||||
|
|
||||||
void
|
void
|
||||||
computeValidPatterns(const std::vector<size_t> &reducedNumberOfNodesPerSlot);
|
computeValidPatterns(const std::vector<size_t> &reducedNumberOfNodesPerSlot, const std::string &desiredResultsPath, const int& numberOfDesiredEdges=-1);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<size_t>
|
std::vector<size_t>
|
||||||
|
|
@ -176,6 +176,5 @@ private:
|
||||||
const std::unordered_map<size_t, std::unordered_set<size_t>>
|
const std::unordered_map<size_t, std::unordered_set<size_t>>
|
||||||
&intersectingEdges,
|
&intersectingEdges,
|
||||||
const std::vector<vcg::Point2i> &validEdges);
|
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);
|
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(
|
int returnValue = vcg::tri::io::ExporterPLY<PatternGeometry>::Save(
|
||||||
*this, plyFilename.c_str(),
|
*this, plyFilename.c_str(),
|
||||||
|
|
@ -584,8 +584,9 @@ PatternGeometry::PatternGeometry(const std::string &filename,
|
||||||
updateEigenEdgeAndVertices();
|
updateEigenEdgeAndVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
double PatternGeometry::computeBaseTriangleHeight() const {
|
double PatternGeometry::computeBaseTriangleHeight() const
|
||||||
return (vert[0].cP() - vert[3].cP()).Norm();
|
{
|
||||||
|
return vcg::Distance(vert[0].cP(), vert[interfaceNodeIndex].cP());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatternGeometry::addNormals() {
|
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(
|
PatternGeometry::PatternGeometry(
|
||||||
const std::vector<size_t> &numberOfNodesPerSlot,
|
const std::vector<size_t> &numberOfNodesPerSlot,
|
||||||
const std::vector<vcg::Point2i> &edges) {
|
const std::vector<vcg::Point2i> &edges) {
|
||||||
|
|
@ -659,336 +671,53 @@ void PatternGeometry::copy(PatternGeometry ©From) {
|
||||||
baseTriangleHeight = copyFrom.getBaseTriangleHeight();
|
baseTriangleHeight = copyFrom.getBaseTriangleHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatternGeometry::deleteDanglingEdges() {
|
void PatternGeometry::scale(const double &desiredBaseTriangleCentralEdgeSize,const int& interfaceNodeIndex) {
|
||||||
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) {
|
|
||||||
this->baseTriangleHeight = desiredBaseTriangleCentralEdgeSize;
|
this->baseTriangleHeight = desiredBaseTriangleCentralEdgeSize;
|
||||||
const double baseTriangleCentralEdgeSize =
|
const double baseTriangleCentralEdgeSize = getBaseTriangleHeight();
|
||||||
(vert[0].cP() - vert[3].cP()).Norm();
|
|
||||||
const double scaleRatio =
|
const double scaleRatio =
|
||||||
desiredBaseTriangleCentralEdgeSize / baseTriangleCentralEdgeSize;
|
desiredBaseTriangleCentralEdgeSize / baseTriangleCentralEdgeSize;
|
||||||
|
|
||||||
vcg::tri::UpdatePosition<VCGEdgeMesh>::Scale(*this, scaleRatio);
|
vcg::tri::UpdatePosition<VCGEdgeMesh>::Scale(*this, scaleRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatternGeometry::deleteDanglingVertices() {
|
void PatternGeometry::createFan(const std::vector<int> &connectToNeighborsVi, const size_t &fanSize)
|
||||||
vcg::tri::Allocator<PatternGeometry>::PointerUpdater<VertexPointer> pu;
|
{
|
||||||
deleteDanglingVertices(pu);
|
PatternGeometry rotatedPattern;
|
||||||
}
|
vcg::tri::Append<PatternGeometry, PatternGeometry>::MeshCopy(rotatedPattern, *this);
|
||||||
|
for (int rotationCounter = 1; rotationCounter < fanSize; rotationCounter++) {
|
||||||
void PatternGeometry::deleteDanglingVertices(
|
vcg::Matrix44d R;
|
||||||
vcg::tri::Allocator<PatternGeometry>::PointerUpdater<VertexPointer> &pu) {
|
auto rotationAxis = vcg::Point3d(0, 0, 1);
|
||||||
for (VertexType &v : vert) {
|
R.SetRotateDeg(360 / fanSize, rotationAxis);
|
||||||
std::vector<PatternGeometry::EdgePointer> incidentElements;
|
vcg::tri::UpdatePosition<PatternGeometry>::Matrix(rotatedPattern, R);
|
||||||
vcg::edge::VEStarVE(&v, incidentElements);
|
vcg::tri::Append<PatternGeometry, PatternGeometry>::Mesh(*this, rotatedPattern);
|
||||||
if (incidentElements.size() == 0 && !v.IsD()) {
|
//Add edges for connecting the desired vertices
|
||||||
vcg::tri::Allocator<PatternGeometry>::DeleteVertex(*this, v);
|
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 {
|
double PatternGeometry::getBaseTriangleHeight() const {
|
||||||
return baseTriangleHeight;
|
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;
|
size_t getNodeSlot(const size_t &nodeIndex) const;
|
||||||
|
|
||||||
void addNormals();
|
void addNormals();
|
||||||
void deleteDanglingEdges();
|
|
||||||
void removeDuplicateVertices();
|
|
||||||
double baseTriangleHeight;
|
double baseTriangleHeight;
|
||||||
double computeBaseTriangleHeight() const;
|
double computeBaseTriangleHeight() const;
|
||||||
|
|
||||||
|
const int interfaceNodeIndex{3};
|
||||||
const size_t fanSize{6};
|
const size_t fanSize{6};
|
||||||
std::vector<VCGEdgeMesh::CoordType> vertices;
|
std::vector<VCGEdgeMesh::CoordType> vertices;
|
||||||
const double triangleEdgeSize{1}; // radius edge
|
const double triangleEdgeSize{1}; // radius edge
|
||||||
|
|
@ -41,7 +40,7 @@ public:
|
||||||
* vcglib interface.
|
* vcglib interface.
|
||||||
* */
|
* */
|
||||||
PatternGeometry(PatternGeometry &other);
|
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::Point3d> &vertices);
|
||||||
void add(const std::vector<vcg::Point2i> &edges);
|
void add(const std::vector<vcg::Point2i> &edges);
|
||||||
void add(const std::vector<vcg::Point3d> &vertices,
|
void add(const std::vector<vcg::Point3d> &vertices,
|
||||||
|
|
@ -83,20 +82,251 @@ public:
|
||||||
void copy(PatternGeometry ©From);
|
void copy(PatternGeometry ©From);
|
||||||
|
|
||||||
void tilePattern(PatternGeometry &pattern, VCGTriMesh &tileInto);
|
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);
|
const bool &shouldDeleteDanglingEdges);
|
||||||
|
|
||||||
void createFan(const size_t &fanSize = 6);
|
void scale(const double &desiredBaseTriangleCentralEdgeSize, const int &interfaceNodeIndex);
|
||||||
|
|
||||||
void deleteDanglingVertices(
|
|
||||||
vcg::tri::Allocator<PatternGeometry>::PointerUpdater<VertexPointer> &pu);
|
|
||||||
void deleteDanglingVertices();
|
|
||||||
void scale(const double &desiredBaseTriangleCentralEdgeSize);
|
|
||||||
|
|
||||||
double getBaseTriangleHeight() const;
|
double getBaseTriangleHeight() const;
|
||||||
|
vcg::Triangle3<double> getBaseTriangle() const;
|
||||||
|
|
||||||
PatternGeometry(const std::vector<vcg::Point2d> &vertices,
|
PatternGeometry(const std::vector<vcg::Point2d> &vertices, const std::vector<vcg::Point2i> &edges);
|
||||||
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
|
#endif // FLATPATTERNGEOMETRY_HPP
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,7 @@ inline void deinitPolyscope() {
|
||||||
|
|
||||||
polyscope::shutdown();
|
polyscope::shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void initPolyscope() {
|
inline void initPolyscope() {
|
||||||
if (polyscope::state::initialized) {
|
if (polyscope::state::initialized) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
187
vcgtrimesh.cpp
187
vcgtrimesh.cpp
|
|
@ -1,79 +1,140 @@
|
||||||
#include "vcgtrimesh.hpp"
|
#include "vcgtrimesh.hpp"
|
||||||
#include <wrap/nanoply/include/nanoplyWrapper.hpp>
|
#include "utilities.hpp"
|
||||||
#include "wrap/io_trimesh/import_obj.h"
|
#include "wrap/io_trimesh/import_obj.h"
|
||||||
#include "wrap/io_trimesh/import_off.h"
|
#include "wrap/io_trimesh/import_off.h"
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <wrap/io_trimesh/import.h>
|
||||||
|
#include <wrap/nanoply/include/nanoplyWrapper.hpp>
|
||||||
|
|
||||||
void VCGTriMesh::loadFromPlyFile(const std::string &filename) {
|
bool VCGTriMesh::load(const std::string &filename) {
|
||||||
unsigned int mask = 0;
|
// unsigned int mask = 0;
|
||||||
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOORD;
|
// mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOORD;
|
||||||
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTNORMAL;
|
// mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTNORMAL;
|
||||||
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOLOR;
|
// mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOLOR;
|
||||||
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_EDGEINDEX;
|
// mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_EDGEINDEX;
|
||||||
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_FACEINDEX;
|
// mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_FACEINDEX;
|
||||||
if (nanoply::NanoPlyWrapper<VCGTriMesh>::LoadModel(
|
|
||||||
std::filesystem::absolute(filename).string().c_str(), *this, mask) != 0) {
|
// if (nanoply::NanoPlyWrapper<VCGTriMesh>::LoadModel(
|
||||||
std::cout << "Could not load tri mesh" << std::endl;
|
// 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>::FaceFace(*this);
|
||||||
vcg::tri::UpdateTopology<VCGTriMesh>::VertexFace(*this);
|
vcg::tri::UpdateTopology<VCGTriMesh>::VertexFace(*this);
|
||||||
vcg::tri::UpdateNormal<VCGTriMesh>::PerVertexNormalized(*this);
|
vcg::tri::UpdateNormal<VCGTriMesh>::PerVertexNormalized(*this);
|
||||||
}
|
|
||||||
|
|
||||||
Eigen::MatrixX3d VCGTriMesh::getVertices() const {
|
label = std::filesystem::path(filename).stem();
|
||||||
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;
|
|
||||||
}
|
|
||||||
return true;
|
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() {}
|
||||||
|
|
||||||
VCGTriMesh::VCGTriMesh(const std::string &filename) {
|
VCGTriMesh::VCGTriMesh(const std::string &filename)
|
||||||
const std::string extension = std::filesystem::path(filename).extension().string();
|
{
|
||||||
if (extension == ".ply") {
|
const std::string extension = std::filesystem::path(filename).extension().string();
|
||||||
loadFromPlyFile(filename);
|
if (extension == ".ply") {
|
||||||
} else if (extension == ".obj") {
|
load(filename);
|
||||||
vcg::tri::io::ImporterOBJ<VCGTriMesh>::Info info;
|
} else if (extension == ".obj") {
|
||||||
vcg::tri::io::ImporterOBJ<VCGTriMesh>::Open(*this, filename.c_str(), info);
|
vcg::tri::io::ImporterOBJ<VCGTriMesh>::Info info;
|
||||||
} else if (extension == ".off") {
|
vcg::tri::io::ImporterOBJ<VCGTriMesh>::Open(*this, filename.c_str(), info);
|
||||||
vcg::tri::io::ImporterOFF<VCGTriMesh>::Open(*this, filename.c_str());
|
} else if (extension == ".off") {
|
||||||
|
vcg::tri::io::ImporterOFF<VCGTriMesh>::Open(*this, filename.c_str());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Uknown file extension " << extension << ". Could not open "
|
std::cerr << "Uknown file extension " << extension << ". Could not open " << filename
|
||||||
<< filename << std::endl;
|
<< std::endl;
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
vcg::tri::UpdateTopology<VCGTriMesh>::AllocateEdge(*this);
|
vcg::tri::UpdateTopology<VCGTriMesh>::AllocateEdge(*this);
|
||||||
vcg::tri::UpdateTopology<VCGTriMesh>::FaceFace(*this);
|
vcg::tri::UpdateTopology<VCGTriMesh>::FaceFace(*this);
|
||||||
vcg::tri::UpdateTopology<VCGTriMesh>::VertexFace(*this);
|
vcg::tri::UpdateTopology<VCGTriMesh>::VertexFace(*this);
|
||||||
vcg::tri::UpdateNormal<VCGTriMesh>::PerVertexNormalized(*this);
|
vcg::tri::UpdateNormal<VCGTriMesh>::PerVertexNormalized(*this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#ifndef VCGTRIMESH_HPP
|
#ifndef VCGTRIMESH_HPP
|
||||||
#define VCGTRIMESH_HPP
|
#define VCGTRIMESH_HPP
|
||||||
|
#include "mesh.hpp"
|
||||||
|
#include <polyscope/surface_mesh.h>
|
||||||
#include <vcg/complex/complex.h>
|
#include <vcg/complex/complex.h>
|
||||||
|
|
||||||
using VertexIndex = size_t;
|
using VertexIndex = size_t;
|
||||||
|
|
@ -10,10 +12,14 @@ struct VCGTriMeshUsedTypes
|
||||||
: public vcg::UsedTypes<vcg::Use<VCGTriMeshVertex>::AsVertexType,
|
: public vcg::UsedTypes<vcg::Use<VCGTriMeshVertex>::AsVertexType,
|
||||||
vcg::Use<VCGTriMeshEdge>::AsEdgeType,
|
vcg::Use<VCGTriMeshEdge>::AsEdgeType,
|
||||||
vcg::Use<VCGTriMeshFace>::AsFaceType> {};
|
vcg::Use<VCGTriMeshFace>::AsFaceType> {};
|
||||||
class VCGTriMeshVertex
|
class VCGTriMeshVertex : public vcg::Vertex<VCGTriMeshUsedTypes,
|
||||||
: public vcg::Vertex<VCGTriMeshUsedTypes, vcg::vertex::Coord3d,
|
vcg::vertex::Coord3d,
|
||||||
vcg::vertex::Normal3d, vcg::vertex::BitFlags,
|
vcg::vertex::Normal3d,
|
||||||
vcg::vertex::Color4b, vcg::vertex::VFAdj> {};
|
vcg::vertex::BitFlags,
|
||||||
|
vcg::vertex::Color4b,
|
||||||
|
vcg::vertex::VFAdj,
|
||||||
|
vcg::vertex::Qualityd>
|
||||||
|
{};
|
||||||
class VCGTriMeshFace
|
class VCGTriMeshFace
|
||||||
: public vcg::Face<VCGTriMeshUsedTypes, vcg::face::FFAdj, vcg::face::VFAdj,
|
: public vcg::Face<VCGTriMeshUsedTypes, vcg::face::FFAdj, vcg::face::VFAdj,
|
||||||
vcg::face::VertexRef, vcg::face::BitFlags,
|
vcg::face::VertexRef, vcg::face::BitFlags,
|
||||||
|
|
@ -23,17 +29,26 @@ class VCGTriMeshEdge
|
||||||
|
|
||||||
class VCGTriMesh : public vcg::tri::TriMesh<std::vector<VCGTriMeshVertex>,
|
class VCGTriMesh : public vcg::tri::TriMesh<std::vector<VCGTriMeshVertex>,
|
||||||
std::vector<VCGTriMeshFace>,
|
std::vector<VCGTriMeshFace>,
|
||||||
std::vector<VCGTriMeshEdge>> {
|
std::vector<VCGTriMeshEdge>>,
|
||||||
|
public Mesh
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
VCGTriMesh();
|
VCGTriMesh();
|
||||||
VCGTriMesh(const std::string &filename);
|
VCGTriMesh(const std::string &filename);
|
||||||
void loadFromPlyFile(const std::string &filename);
|
bool load(const std::string &filename) override;
|
||||||
Eigen::MatrixX3d getVertices() const;
|
Eigen::MatrixX3d getVertices() const;
|
||||||
Eigen::MatrixX3i getFaces() 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) {
|
template <typename MeshElement> size_t getIndex(const MeshElement &element) {
|
||||||
return vcg::tri::Index<VCGTriMesh>(*this, 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
|
#endif // VCGTRIMESH_HPP
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue