Initial commit

This commit is contained in:
Iason 2020-11-27 12:47:21 +02:00
commit 9668d030ea
23 changed files with 6085 additions and 0 deletions

57
beam.hpp Normal file
View File

@ -0,0 +1,57 @@
#ifndef BEAM_HPP
#define BEAM_HPP
#include <Eigen/Dense>
#include <assert.h>
#include <cmath>
#include <iostream>
struct RectangularBeamDimensions {
float b;
float h;
RectangularBeamDimensions(const float &width, const float &height)
: b(width), h(height) {
assert(width > 0 && height > 0);
}
RectangularBeamDimensions() : b(1), h(1) {}
};
struct CylindricalElementDimensions {
float od; // Cylinder outside diameter
float
id; // Cylinder inside diameter
// https://www.engineeringtoolbox.com/area-moment-inertia-d_1328.html
CylindricalElementDimensions(const float &outsideDiameter,
const float &insideDiameter)
: od(outsideDiameter), id(insideDiameter) {
assert(outsideDiameter > 0 && insideDiameter > 0 &&
outsideDiameter > insideDiameter);
}
CylindricalElementDimensions() : od(0.03), id(0.026) {}
};
struct ElementMaterial {
float poissonsRatio;
float youngsModulusGPascal;
ElementMaterial(const float &poissonsRatio, const float &youngsModulusGPascal)
: poissonsRatio(poissonsRatio),
youngsModulusGPascal(youngsModulusGPascal) {
assert(poissonsRatio <= 0.5 && poissonsRatio >= -1);
}
ElementMaterial() : poissonsRatio(0.3), youngsModulusGPascal(7.5) {}
};
struct BeamProperties {
float crossArea;
float I2;
float I3;
float polarInertia;
BeamProperties(const RectangularBeamDimensions &dimensions,
const ElementMaterial &material) {
crossArea = (dimensions.b * dimensions.h);
I2 = dimensions.b * std::pow(dimensions.h, 3) / 12;
I3 = dimensions.h * std::pow(dimensions.b, 3) / 12;
polarInertia = (I2 + I3);
}
};
#endif // BEAM_HPP

1796
beamformfinder.cpp Normal file

File diff suppressed because it is too large Load Diff

224
beamformfinder.hpp Normal file
View File

@ -0,0 +1,224 @@
#ifndef BEAMFORMFINDER_HPP
#define BEAMFORMFINDER_HPP
#include "elementalmesh.hpp"
#include "polyscope/curve_network.h"
#include "polyscope/polyscope.h"
#include "simulationresult.hpp"
#include <Eigen/Dense>
#include <filesystem>
#include <unordered_set>
struct SimulationJob;
enum DoF { Ux = 0, Uy, Uz, Nx, Ny, Nr, NumDoF };
using DoFType = int;
using EdgeType = VCGEdgeMesh::EdgeType;
using VertexType = VCGEdgeMesh::VertexType;
struct DifferentiateWithRespectTo {
const VertexType &v;
const DoFType &dofi;
};
class FormFinder {
private:
const double Dtini{0.1};
double Dt{Dtini};
double t{0};
const double xi{0.9969};
const double totalResidualForcesNormThreshold{300};
size_t currentSimulationStep{0};
int mDrawingStep{1};
std::unique_ptr<SimulationMesh> mesh;
std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
constrainedVertices;
SimulationHistory history;
// Eigen::Tensor<double, 4> theta3Derivatives;
// std::unordered_map<MyKeyType, double, key_hash> theta3Derivatives;
bool shouldApplyInitialDistortion = false;
std::unordered_set<VertexIndex> rigidSupports;
void reset();
void updateNodalInternalForces(
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
&fixedVertices);
void updateNodalExternalForces(
const std::unordered_map<VertexIndex, Vector6d> &nodalForces,
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
&fixedVertices);
void updateResidualForces();
void updateRotationalDisplacements();
void updateElementalLengths();
void updateNodalMasses();
void updateNodalAccelerations();
void updateNodalVelocities();
void updateNodalDisplacements();
void applyForcedDisplacements(
const std::unordered_map<VertexIndex, Eigen::Vector3d>
nodalForcedDisplacements);
::Vector6d computeElementTorsionalForce(
const Element &element, const Vector6d &displacementDifference,
const std::unordered_set<DoFType> &constrainedDof);
// BeamFormFinder::Vector6d computeElementFirstBendingForce(
// const Element &element, const Vector6d &displacementDifference,
// const std::unordered_set<gsl::index> &constrainedDof);
// BeamFormFinder::Vector6d computeElementSecondBendingForce(
// const Element &element, const Vector6d &displacementDifference,
// const std::unordered_set<gsl::index> &constrainedDof);
void updateKineticEnergy();
void resetVelocities();
SimulationResults computeResults(const SimulationMesh &initialiMesh);
void updateNodePosition(
VertexType &v,
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
&fixedVertices);
void applyDisplacements(
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
&fixedVertices);
void draw(const string &screenshotsFolder);
void
updateNodalInternalForce(Vector6d &nodalInternalForce,
const Vector6d &elementInternalForce,
const std::unordered_set<DoFType> &nodalFixedDof);
::Vector6d computeElementInternalForce(
const Element &elem, const Node &n0, const Node &n1,
const std::unordered_set<DoFType> &n0ConstrainedDof,
const std::unordered_set<DoFType> &n1ConstrainedDof);
::Vector6d computeElementAxialForce(const ::EdgeType &e) const;
VectorType computeDisplacementDifferenceDerivative(
const EdgeType &e, const DifferentiateWithRespectTo &dui) const;
double
computeDerivativeElementLength(const EdgeType &e,
const DifferentiateWithRespectTo &dui) const;
VectorType computeDerivativeT1(const EdgeType &e,
const DifferentiateWithRespectTo &dui) const;
VectorType
computeDerivativeOfNormal(const VertexType &v,
const DifferentiateWithRespectTo &dui) const;
VectorType computeDerivativeT3(const EdgeType &e,
const DifferentiateWithRespectTo &dui) const;
VectorType computeDerivativeT2(const EdgeType &e,
const DifferentiateWithRespectTo &dui) const;
double computeDerivativeTheta2(const EdgeType &e, const VertexIndex &evi,
const VertexIndex &dwrt_evi,
const DoFType &dwrt_dofi) const;
void updateElementalFrames();
VectorType computeDerivativeOfR(const EdgeType &e,
const DifferentiateWithRespectTo &dui) const;
bool isRigidSupport(const VertexType &v) const;
static double computeDerivativeOfNorm(const VectorType &x,
const VectorType &derivativeOfX);
static VectorType computeDerivativeOfCrossProduct(
const VectorType &a, const VectorType &derivativeOfA, const VectorType &b,
const VectorType &derivativeOfB);
double computeTheta3(const EdgeType &e, const VertexType &v);
double computeDerivativeTheta3(const EdgeType &e, const VertexType &v,
const DifferentiateWithRespectTo &dui) const;
double computeTotalPotentialEnergy() const;
void computeRigidSupports();
void updateNormalDerivatives();
void updateT1Derivatives();
void updateT2Derivatives();
void updateT3Derivatives();
void updateRDerivatives();
double computeDerivativeTheta1(const EdgeType &e, const VertexIndex &evi,
const VertexIndex &dwrt_evi,
const DoFType &dwrt_dofi) const;
// void updatePositionsOnTheFly(
// const std::unordered_map<VertexIndex,
// std::unordered_set<gsl::index>>
// &fixedVertices);
void updateResidualForcesOnTheFly(
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
&fixedVertices);
void updatePositionsOnTheFly(
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
&fixedVertices);
public:
FormFinder();
SimulationResults executeSimulation(
const SimulationJob &job, const bool &beVerbose = false,
const bool &shouldDraw = false,
const size_t &maxDRMIterations = FormFinder::maxDRMIterations);
inline static const size_t maxDRMIterations{50000};
};
template <typename PointType> PointType Cross(PointType p1, PointType p2) {
return p1 ^ p2;
}
inline size_t currentStep{0};
inline bool TriggerBreakpoint(const VertexIndex &vi, const EdgeIndex &ei,
const DoFType &dofi) {
const size_t numberOfVertices = 10;
const VertexIndex middleNodeIndex = numberOfVertices / 2;
// return vi == middleNodeIndex && dofi == 1;
return dofi == 1 && ((vi == 1 && ei == 0) || (vi == 9 && ei == 9));
}
inline bool TriggerBreakpoint(const VertexIndex &vi, const EdgeIndex &ei) {
const size_t numberOfVertices = 10;
const VertexIndex middleNodeIndex = numberOfVertices / 2;
return (vi == middleNodeIndex);
// return (vi == 0 || vi == numberOfVertices - 1) && currentStep == 1;
return (vi == 1 && ei == 0) || (vi == 9 && ei == 9);
}
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
#endif // BEAMFORMFINDER_HPP

382
edgemesh.cpp Normal file
View File

@ -0,0 +1,382 @@
#include "edgemesh.hpp"
#include "vcg/simplex/face/topology.h"
Eigen::MatrixX2i VCGEdgeMesh::getEigenEdges() const { return eigenEdges; }
Eigen::MatrixX3d VCGEdgeMesh::getEigenVertices() const {
// getVertices(eigenVertices);
return eigenVertices;
}
Eigen::MatrixX3d VCGEdgeMesh::getEigenEdgeNormals() const {
return eigenEdgeNormals;
}
std::vector<CylindricalElementDimensions>
VCGEdgeMesh::getBeamDimensions() const {
return handleBeamDimensions._handle->data;
}
std::vector<ElementMaterial> VCGEdgeMesh::getBeamMaterial() const {
return handleBeamMaterial._handle->data;
}
bool VCGEdgeMesh::savePly(const std::string plyFilename) {
nanoply::NanoPlyWrapper<VCGEdgeMesh>::CustomAttributeDescriptor customAttrib;
customAttrib.GetMeshAttrib(plyFilename);
customAttrib.AddEdgeAttribDescriptor<CylindricalElementDimensions, float, 2>(
plyPropertyBeamDimensionsID, nanoply::NNP_LIST_UINT8_FLOAT32,
&handleBeamDimensions[0]);
customAttrib.AddEdgeAttribDescriptor<vcg::Point2f, float, 2>(
plyPropertyBeamMaterialID, nanoply::NNP_LIST_UINT8_FLOAT32,
&handleBeamMaterial[0]);
// Load the ply file
unsigned int mask = 0;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEATTRIB;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::SaveModel(
plyFilename.c_str(), *this, mask, customAttrib, false) != 0) {
return false;
}
return true;
}
void VCGEdgeMesh::GeneratedRegularSquaredPattern(
const double angleDeg, std::vector<std::vector<vcg::Point2d>> &pattern,
const size_t &desiredNumberOfSamples) {
static const size_t piSamples = 10;
// generate a pattern in a 1x1 quad
const vcg::Point2d offset(0, 0);
const size_t samplesNo = desiredNumberOfSamples;
// std::max(desiredNumberOfSamples, size_t(piSamples * (angleDeg /
// 180)));
const double angle = vcg::math::ToRad(angleDeg);
pattern.clear();
// first arm
std::vector<vcg::Point2d> arm;
{
for (int k = 0; k <= samplesNo; k++) {
const double t = double(k) / samplesNo;
const double a = (1 - t) * angle;
// const double r = vcg::math::Sin(t*M_PI_2) /*(1-((1-t)*(1-t)))*/ * 0.5;
const double r = t * 0.5; // linear
vcg::Point2d p(vcg::math::Cos(a), vcg::math::Sin(a));
arm.push_back((p * r));
}
pattern.push_back(arm);
}
// other arms
for (int i = 0; i < 3; i++) {
for (vcg::Point2d &p : arm) {
p = vcg::Point2d(-p.Y(), p.X());
}
pattern.push_back(arm);
}
assert(pattern.size() == 4);
// offset all
for (auto &arm : pattern) {
for (vcg::Point2d &p : arm) {
p += offset;
}
}
}
void VCGEdgeMesh::createSpiral(const float &degreesOfArm,
const size_t &numberOfSamples) {
std::vector<std::vector<vcg::Point2d>> spiralPoints;
GeneratedRegularSquaredPattern(degreesOfArm, spiralPoints, numberOfSamples);
for (size_t armIndex = 0; armIndex < spiralPoints.size(); armIndex++) {
for (size_t pointIndex = 0; pointIndex < spiralPoints[armIndex].size() - 1;
pointIndex++) {
const vcg::Point2d p0 = spiralPoints[armIndex][pointIndex];
const vcg::Point2d p1 = spiralPoints[armIndex][pointIndex + 1];
CoordType n(0, 0, 1);
auto ei = vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(
*this, VCGEdgeMesh::CoordType(p0.X(), p0.Y(), 0),
VCGEdgeMesh::CoordType(p1.X(), p1.Y(), 0));
ei->cV(0)->N() = n;
ei->cV(1)->N() = n;
}
}
// setDefaultAttributes();
}
bool VCGEdgeMesh::createGrid(const size_t squareGridDimension) {
return createGrid(squareGridDimension, squareGridDimension);
}
bool VCGEdgeMesh::createGrid(const size_t desiredWidth,
const size_t desiredHeight) {
std::cout << "Grid of dimensions:" << desiredWidth << "," << desiredHeight
<< std::endl;
const VCGEdgeMesh::CoordType n(0, 0, 1);
int x = 0;
int y = 0;
// for (size_t vi = 0; vi < numberOfVertices; vi++) {
while (y <= desiredHeight) {
// std::cout << x << " " << y << std::endl;
auto p = VCGEdgeMesh::CoordType(x, y, 0);
vcg::tri::Allocator<VCGEdgeMesh>::AddVertex(*this, p, n);
x++;
if (x > desiredWidth) {
x = 0;
y++;
}
}
for (size_t vi = 0; vi < VN(); vi++) {
int x = vi % (desiredWidth + 1);
int y = vi / (desiredWidth + 1);
const bool isCornerNode = (y == 0 && x == 0) ||
(y == 0 && x == desiredWidth) ||
(y == desiredHeight && x == 0) ||
(y == desiredHeight && x == desiredWidth);
if (isCornerNode) {
continue;
}
if (y == 0) { // row 0.Connect with node above
vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi,
vi + desiredWidth + 1);
continue;
} else if (x == 0) { // col 0.Connect with node to the right
vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi, vi + 1);
continue;
} else if (y == desiredHeight) { // row 0.Connect with node below
// vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi,
// vi - (desiredWidth +
// 1));
continue;
} else if (x == desiredWidth) { // row 0.Connect with node to the left
// vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi, vi - 1);
continue;
}
vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi, vi + desiredWidth + 1);
vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi, vi + 1);
// vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi,
// vi - (desiredWidth + 1));
// vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi, vi - 1);
}
vcg::tri::Allocator<VCGEdgeMesh>::DeleteVertex(*this, vert[0]);
vcg::tri::Allocator<VCGEdgeMesh>::DeleteVertex(*this, vert[desiredWidth]);
vcg::tri::Allocator<VCGEdgeMesh>::DeleteVertex(
*this, vert[desiredHeight * (desiredWidth + 1)]);
vcg::tri::Allocator<VCGEdgeMesh>::DeleteVertex(
*this, vert[(desiredHeight + 1) * (desiredWidth + 1) - 1]);
vcg::tri::Allocator<VCGEdgeMesh>::CompactVertexVector(*this);
getEdges(eigenEdges);
getVertices(eigenVertices);
// vcg::tri::Allocator<VCGEdgeMesh>::CompactEdgeVector(*this);
// const size_t numberOfEdges =
// desiredHeight * (desiredWidth - 1) + desiredWidth * (desiredHeight -
// 1);
// handleBeamDimensions._handle->data.resize(
// numberOfEdges, CylindricalElementDimensions(0.03, 0.026));
// handleBeamMaterial._handle->data.resize(numberOfEdges,
// ElementMaterial(0.3, 200));
return true;
}
bool VCGEdgeMesh::loadFromPly(const std::string plyFilename) {
std::string usedPath = plyFilename;
if (std::filesystem::path(plyFilename).is_relative()) {
usedPath = std::filesystem::absolute(plyFilename).string();
}
assert(std::filesystem::exists(usedPath));
this->Clear();
const bool useDefaultImporter = false;
if (useDefaultImporter) {
if (!loadUsingDefaultLoader(usedPath)) {
return false;
}
eigenEdgeNormals.resize(EN(), 3);
for (int i = 0; i < EN(); i++) {
eigenEdgeNormals.row(i) = Eigen::Vector3d(0, 1, 0);
}
} else {
if (!loadUsingNanoply(usedPath)) {
std::cerr << "Error: Unable to open " + usedPath << std::endl;
return false;
}
}
getEdges(eigenEdges);
getVertices(eigenVertices);
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
std::cout << plyFilename << " was loaded successfuly." << std::endl;
std::cout << "Mesh has " << EN() << " edges." << std::endl;
return true;
}
bool VCGEdgeMesh::loadUsingNanoply(const std::string &plyFilename) {
this->Clear();
// assert(plyFileHasAllRequiredFields(plyFilename));
nanoply::NanoPlyWrapper<VCGEdgeMesh>::CustomAttributeDescriptor customAttrib;
customAttrib.GetMeshAttrib(plyFilename);
customAttrib.AddEdgeAttribDescriptor<RectangularBeamDimensions, float, 2>(
plyPropertyBeamDimensionsID, nanoply::NNP_LIST_UINT8_FLOAT32, nullptr);
customAttrib.AddEdgeAttribDescriptor<vcg::Point2f, float, 2>(
plyPropertyBeamMaterialID, nanoply::NNP_LIST_UINT8_FLOAT32, nullptr);
// Load the ply file
unsigned int mask = 0;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEATTRIB;
if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::LoadModel(
plyFilename.c_str(), *this, mask, customAttrib) != 0) {
return false;
}
return true;
}
bool VCGEdgeMesh::plyFileHasAllRequiredFields(const std::string &plyFilename) {
const nanoply::Info info(plyFilename);
const std::vector<nanoply::PlyElement>::const_iterator edgeElemIt =
std::find_if(info.elemVec.begin(), info.elemVec.end(),
[&](const nanoply::PlyElement &plyElem) {
return plyElem.plyElem == nanoply::NNP_EDGE_ELEM;
});
if (edgeElemIt == info.elemVec.end()) {
std::cerr << "Ply file is missing edge elements." << std::endl;
return false;
}
const std::vector<nanoply::PlyProperty> &edgePropertyVector =
edgeElemIt->propVec;
return hasPlyEdgeProperty(plyFilename, edgePropertyVector,
plyPropertyBeamDimensionsID) &&
hasPlyEdgeProperty(plyFilename, edgePropertyVector,
plyPropertyBeamMaterialID);
}
bool VCGEdgeMesh::hasPlyEdgeProperty(
const std::string &plyFilename,
const std::vector<nanoply::PlyProperty> &edgeProperties,
const std::string &plyEdgePropertyName) {
const bool hasEdgeProperty = hasProperty(edgeProperties, plyEdgePropertyName);
if (!hasEdgeProperty) {
std::cerr << "Ply file " + plyFilename +
" is missing the propertry:" + plyEdgePropertyName
<< std::endl;
return false;
}
return true;
}
bool VCGEdgeMesh::hasProperty(const std::vector<nanoply::PlyProperty> &v,
const std::string &propertyName) {
return v.end() != std::find_if(v.begin(), v.end(),
[&](const nanoply::PlyProperty &plyProperty) {
return plyProperty.name == propertyName;
});
}
Eigen::MatrixX3d VCGEdgeMesh::getNormals() const {
Eigen::MatrixX3d vertexNormals;
vertexNormals.resize(VN(), 3);
for (int vertexIndex = 0; vertexIndex < VN(); vertexIndex++) {
VCGEdgeMesh::CoordType vertexNormal =
vert[static_cast<size_t>(vertexIndex)].cN();
vertexNormals.row(vertexIndex) =
vertexNormal.ToEigenVector<Eigen::Vector3d>();
}
return vertexNormals;
}
void VCGEdgeMesh::getEdges(Eigen::MatrixX3d &edgeStartingPoints,
Eigen::MatrixX3d &edgeEndingPoints) const {
edgeStartingPoints.resize(EN(), 3);
edgeEndingPoints.resize(EN(), 3);
for (int edgeIndex = 0; edgeIndex < EN(); edgeIndex++) {
const VCGEdgeMesh::EdgeType &edge = this->edge[edgeIndex];
edgeStartingPoints.row(edgeIndex) =
edge.cP(0).ToEigenVector<Eigen::Vector3d>();
edgeEndingPoints.row(edgeIndex) =
edge.cP(1).ToEigenVector<Eigen::Vector3d>();
}
}
VCGEdgeMesh::VCGEdgeMesh() {
handleBeamDimensions = vcg::tri::Allocator<VCGEdgeMesh>::AddPerEdgeAttribute<
CylindricalElementDimensions>(*this, plyPropertyBeamDimensionsID);
handleBeamMaterial =
vcg::tri::Allocator<VCGEdgeMesh>::AddPerEdgeAttribute<ElementMaterial>(
*this, plyPropertyBeamMaterialID);
}
void VCGEdgeMesh::updateEigenEdgeAndVertices() {
getEdges(eigenEdges);
getVertices(eigenVertices);
}
void VCGEdgeMesh::copy(VCGEdgeMesh &mesh) {
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::MeshCopy(*this, mesh);
label = mesh.getLabel();
eigenEdges = mesh.getEigenEdges();
if (eigenEdges.rows() == 0) {
getEdges(eigenEdges);
}
eigenVertices = mesh.getEigenVertices();
if (eigenVertices.rows() == 0) {
getVertices(eigenVertices);
}
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
}
void VCGEdgeMesh::getVertices(Eigen::MatrixX3d &vertices) {
vertices = Eigen::MatrixX3d();
vertices.resize(VN(), 3);
for (int vi = 0; vi < VN(); vi++) {
if (vert[vi].IsD()) {
continue;
}
VCGEdgeMesh::CoordType vertexCoordinates =
vert[static_cast<size_t>(vi)].cP();
vertices.row(vi) = vertexCoordinates.ToEigenVector<Eigen::Vector3d>();
}
}
std::string VCGEdgeMesh::getLabel() const { return label; }
void VCGEdgeMesh::setLabel(const std::string &value) { label = value; }
void VCGEdgeMesh::getEdges(Eigen::MatrixX2i &edges) {
edges = Eigen::MatrixX2i();
edges.resize(EN(), 2);
for (int edgeIndex = 0; edgeIndex < EN(); edgeIndex++) {
const VCGEdgeMesh::EdgeType &edge = this->edge[edgeIndex];
const size_t nodeIndex0 = vcg::tri::Index<VCGEdgeMesh>(*this, edge.cV(0));
const size_t nodeIndex1 = vcg::tri::Index<VCGEdgeMesh>(*this, edge.cV(1));
edges.row(edgeIndex) = Eigen::Vector2i(nodeIndex0, nodeIndex1);
}
}
void VCGEdgeMesh::printVertexCoordinates(const size_t &vi) const {
std::cout << "vi:" << vi << " " << vert[vi].cP()[0] << " " << vert[vi].cP()[1]
<< " " << vert[vi].cP()[2] << std::endl;
}
void VCGEdgeMesh::registerForDrawing() const {
initPolyscope();
polyscope::registerCurveNetwork(label, getEigenVertices(), getEigenEdges());
}

105
edgemesh.hpp Normal file
View File

@ -0,0 +1,105 @@
#ifndef EDGEMESH_HPP
#define EDGEMESH_HPP
#include "beam.hpp"
#include "polymesh.hpp"
#include "utilities.hpp"
#include "vcgtrimesh.hpp"
#include <vcg/complex/complex.h>
#include <vector>
#include <wrap/io_trimesh/import.h>
#include <wrap/nanoply/include/nanoplyWrapper.hpp>
using EdgeIndex = size_t;
class VCGEdgeMeshEdgeType;
class VCGEdgeMeshVertexType;
struct VCGEdgeMeshUsedTypes
: public vcg::UsedTypes<vcg::Use<VCGEdgeMeshVertexType>::AsVertexType,
vcg::Use<VCGEdgeMeshEdgeType>::AsEdgeType> {};
class VCGEdgeMeshVertexType
: public vcg::Vertex<VCGEdgeMeshUsedTypes, vcg::vertex::Coord3d,
vcg::vertex::Normal3d, vcg::vertex::BitFlags,
vcg::vertex::VEAdj> {};
class VCGEdgeMeshEdgeType
: public vcg::Edge<VCGEdgeMeshUsedTypes, vcg::edge::VertexRef,
vcg::edge::BitFlags, vcg::edge::EEAdj,
vcg::edge::VEAdj> {};
class VCGEdgeMesh : public vcg::tri::TriMesh<std::vector<VCGEdgeMeshVertexType>,
std::vector<VCGEdgeMeshEdgeType>> {
const std::string plyPropertyBeamDimensionsID{"beam_dimensions"};
const std::string plyPropertyBeamMaterialID{"beam_material"};
VCGEdgeMesh::PerEdgeAttributeHandle<CylindricalElementDimensions>
handleBeamDimensions;
VCGEdgeMesh::PerEdgeAttributeHandle<ElementMaterial> handleBeamMaterial;
protected:
Eigen::MatrixX2i eigenEdges;
Eigen::MatrixX3d eigenVertices;
Eigen::MatrixX3d eigenEdgeNormals;
std::string label{"No_name"};
void getEdges(Eigen::MatrixX2i &edges);
void getVertices(Eigen::MatrixX3d &vertices);
public:
VCGEdgeMesh();
template <typename MeshElement>
size_t getIndex(const MeshElement &meshElement) {
return vcg::tri::Index<VCGEdgeMesh>(*this, meshElement);
}
void updateEigenEdgeAndVertices();
void copy(VCGEdgeMesh &mesh);
void getEdges(Eigen::MatrixX3d &edgeStartingPoints,
Eigen::MatrixX3d &edgeEndingPoints) const;
Eigen::MatrixX3d getNormals() const;
bool loadUsingDefaultLoader(const std::string &plyFilename);
bool hasProperty(const std::vector<nanoply::PlyProperty> &v,
const std::string &propertyName);
bool
hasPlyEdgeProperty(const std::string &plyFilename,
const std::vector<nanoply::PlyProperty> &edgeProperties,
const std::string &plyEdgePropertyName);
bool plyFileHasAllRequiredFields(const std::string &plyFilename);
bool loadUsingNanoply(const std::string &plyFilename);
bool loadFromPly(const std::string plyFilename);
bool savePly(const std::string plyFilename);
bool createGrid(const size_t squareGridDimension);
bool createGrid(const size_t desiredWidth, const size_t desiredHeight);
void createSpiral(const float &degreesOfArm, const size_t &numberOfSamples);
Eigen::MatrixX2i getEigenEdges() const;
Eigen::MatrixX3d getEigenVertices() const;
Eigen::MatrixX3d getEigenEdgeNormals() const;
std::vector<CylindricalElementDimensions> getBeamDimensions() const;
std::vector<ElementMaterial> getBeamMaterial() const;
void printVertexCoordinates(const size_t &vi) const;
void registerForDrawing() const;
std::string getLabel() const;
void setLabel(const std::string &value);
private:
void GeneratedRegularSquaredPattern(
const double angleDeg, std::vector<std::vector<vcg::Point2d>> &pattern,
const size_t &desiredNumberOfSamples);
};
using VectorType = VCGEdgeMesh::CoordType;
using CoordType = VCGEdgeMesh::CoordType;
using VertexPointer = VCGEdgeMesh::VertexPointer;
using EdgePointer = VCGEdgeMesh::EdgePointer;
using ConstVCGEdgeMesh = VCGEdgeMesh;
#endif // EDGEMESH_HPP

209
elementalmesh.cpp Normal file
View File

@ -0,0 +1,209 @@
#include "elementalmesh.hpp"
SimulationMesh::SimulationMesh(FlatPattern &pattern) {
vcg::tri::MeshAssert<FlatPattern>::VertexNormalNormalized(pattern);
vcg::tri::Append<VCGEdgeMesh, ConstVCGEdgeMesh>::MeshCopy(*this, pattern);
elements = vcg::tri::Allocator<VCGEdgeMesh>::GetPerEdgeAttribute<Element>(
*this, std::string("Elements"));
nodes = vcg::tri::Allocator<VCGEdgeMesh>::GetPerVertexAttribute<Node>(
*this, std::string("Nodes"));
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
initializeNodes();
initializeElements();
label = pattern.getLabel();
eigenEdges = pattern.getEigenEdges();
eigenVertices = pattern.getEigenVertices();
}
SimulationMesh::SimulationMesh(SimulationMesh &elementalMesh) {
vcg::tri::Append<VCGEdgeMesh, ConstVCGEdgeMesh>::MeshCopy(*this,
elementalMesh);
elements = vcg::tri::Allocator<VCGEdgeMesh>::GetPerEdgeAttribute<Element>(
*this, std::string("Elements"));
nodes = vcg::tri::Allocator<VCGEdgeMesh>::GetPerVertexAttribute<Node>(
*this, std::string("Nodes"));
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
initializeNodes();
for (size_t ei = 0; ei < EN(); ei++) {
elements[ei] = elementalMesh.elements[ei];
}
label = label;
eigenEdges = elementalMesh.getEigenEdges();
eigenVertices = elementalMesh.getEigenVertices();
}
void SimulationMesh::computeElementalProperties() {
const std::vector<CylindricalElementDimensions> elementalDimensions =
getBeamDimensions();
const std::vector<ElementMaterial> elementalMaterials = getBeamMaterial();
assert(EN() == elementalDimensions.size() &&
elementalDimensions.size() == elementalMaterials.size());
for (const EdgeType &e : edge) {
const EdgeIndex ei = getIndex(e);
elements[e].properties =
Element::Properties{elementalDimensions[ei], elementalMaterials[ei]};
}
}
void SimulationMesh::initializeNodes() {
// set initial and previous locations,
for (const VertexType &v : vert) {
const VertexIndex vi = getIndex(v);
Node &node = nodes[v];
node.vi = vi;
node.initialLocation = v.cP();
node.previousLocation = v.cP();
node.initialNormal = v.cN();
node.derivativeOfNormal.resize(6, VectorType(0, 0, 0));
node.displacements[3] =
v.cN()[0]; // initialize nx diplacement with vertex normal x
// component
node.displacements[4] =
v.cN()[1]; // initialize ny(in the paper) diplacement with vertex
// normal
// y component.
// Initialize incident elements
std::vector<VCGEdgeMesh::EdgePointer> incidentElements;
vcg::edge::VEStarVE(&v, incidentElements);
assert(
vcg::tri::IsValidPointer<SimulationMesh>(*this, incidentElements[0]) &&
incidentElements.size() > 0);
nodes[v].incidentElements = std::move(incidentElements);
node.referenceElement = getReferenceElement(v);
// Initialze alpha angles
const EdgeType &referenceElement = *getReferenceElement(v);
const VectorType t01 =
computeT1Vector(referenceElement.cP(0), referenceElement.cP(1));
const VectorType f01 = (t01 - (v.cN() * (t01.dot(v.cN())))).Normalize();
for (const VCGEdgeMesh::EdgePointer &ep : nodes[v].incidentElements) {
assert(referenceElement.cV(0) == ep->cV(0) ||
referenceElement.cV(0) == ep->cV(1) ||
referenceElement.cV(1) == ep->cV(0) ||
referenceElement.cV(1) == ep->cV(1));
const VectorType t1 = computeT1Vector(*ep);
const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize();
const EdgeIndex ei = getIndex(ep);
const double alphaAngle = computeAngle(f01, f1, v.cN());
node.alphaAngles[ei] = alphaAngle;
}
}
}
void SimulationMesh::initializeElements() {
computeElementalProperties();
for (const EdgeType &e : edge) {
Element &element = elements[e];
element.ei = getIndex(e);
// Initialize lengths
const VCGEdgeMesh::CoordType p0 = e.cP(0);
const VCGEdgeMesh::CoordType p1 = e.cP(1);
const vcg::Segment3<double> s(p0, p1);
element.initialLength = s.Length();
element.length = element.initialLength;
// Initialize const factors
element.axialConstFactor =
element.properties.E * element.properties.A / element.initialLength;
element.torsionConstFactor =
element.properties.G * element.properties.J / element.initialLength;
element.firstBendingConstFactor = 2 * element.properties.E *
element.properties.I2 /
element.initialLength;
element.secondBendingConstFactor = 2 * element.properties.E *
element.properties.I3 /
element.initialLength;
element.derivativeT1.resize(
2, std::vector<VectorType>(6, VectorType(0, 0, 0)));
element.derivativeT2.resize(
2, std::vector<VectorType>(6, VectorType(0, 0, 0)));
element.derivativeT3.resize(
2, std::vector<VectorType>(6, VectorType(0, 0, 0)));
element.derivativeT1_jplus1.resize(6);
element.derivativeT1_j.resize(6);
element.derivativeT1_jplus1.resize(6);
element.derivativeT2_j.resize(6);
element.derivativeT2_jplus1.resize(6);
element.derivativeT3_j.resize(6);
element.derivativeT3_jplus1.resize(6);
element.derivativeR_j.resize(6);
element.derivativeR_jplus1.resize(6);
}
}
void SimulationMesh::updateElementalLengths() {
for (const EdgeType &e : edge) {
const EdgeIndex ei = getIndex(e);
const VertexIndex vi0 = getIndex(e.cV(0));
const VCGEdgeMesh::CoordType p0 = e.cP(0);
const VertexIndex vi1 = getIndex(e.cV(1));
const VCGEdgeMesh::CoordType p1 = e.cP(1);
const vcg::Segment3<double> s(p0, p1);
const double elementLength = s.Length();
elements[e].length = elementLength;
int i = 0;
i++;
}
}
SimulationMesh::EdgePointer
SimulationMesh::getReferenceElement(const VCGEdgeMesh::VertexType &v) {
const VertexIndex vi = getIndex(v);
// return nodes[v].incidentElements[0];
// if (vi == 0 || vi == 1) {
// return nodes[0].incidentElements[0];
// }
return nodes[v].incidentElements[0];
}
VectorType computeT1Vector(const SimulationMesh::EdgeType &e) {
return computeT1Vector(e.cP(0), e.cP(1));
}
VectorType computeT1Vector(const CoordType &p0, const CoordType &p1) {
const VectorType t1 = (p1 - p0).Normalize();
return t1;
}
Element::LocalFrame computeElementFrame(const CoordType &p0,
const CoordType &p1,
const VectorType &elementNormal) {
const VectorType t1 = computeT1Vector(p0, p1);
const VectorType t2 = (elementNormal ^ t1).Normalize();
const VectorType t3 = (t1 ^ t2).Normalize();
return Element::LocalFrame{t1, t2, t3};
}
double computeAngle(const VectorType &vector0, const VectorType &vector1,
const VectorType &normalVector) {
double cosAngle = vector0.dot(vector1);
const double epsilon = std::pow(10, -6);
if (abs(cosAngle) > 1 && abs(cosAngle) < 1 + epsilon) {
if (cosAngle > 0) {
cosAngle = 1;
} else {
cosAngle = -1;
}
}
assert(abs(cosAngle) <= 1);
const double angle =
acos(cosAngle); // NOTE: I compute the alpha angle not between
// two consecutive elements but rather between
// the first and the ith. Is this correct?
assert(!std::isnan(angle));
const VectorType cp = vector0 ^ vector1;
if (cp.dot(normalVector) < 0) {
return -angle;
}
return angle;
}

164
elementalmesh.hpp Normal file
View File

@ -0,0 +1,164 @@
#ifndef ELEMENTALMESH_HPP
#define ELEMENTALMESH_HPP
#include "Eigen/Dense"
#include "edgemesh.hpp"
#include "flatpattern.hpp"
struct Element;
struct Node;
class SimulationMesh : public VCGEdgeMesh {
private:
void computeElementalProperties();
void initializeNodes();
void initializeElements();
EdgePointer getReferenceElement(const VertexType &v);
public:
PerEdgeAttributeHandle<Element> elements;
PerVertexAttributeHandle<Node> nodes;
SimulationMesh(FlatPattern &pattern);
SimulationMesh(ConstVCGEdgeMesh &edgeMesh);
SimulationMesh(SimulationMesh &elementalMesh);
void updateElementalLengths();
std::vector<VCGEdgeMesh::EdgePointer>
getIncidentElements(const VCGEdgeMesh::VertexType &v);
double previousTotalKineticEnergy{0};
double previousTotalResidualForcesNorm{0};
double currentTotalKineticEnergy{0};
double totalResidualForcesNorm{0};
double totalPotentialEnergykN{0};
};
struct Element {
struct Properties {
double E{0}; // youngs modulus in pascal
double G{0}; // shear modulus
double A{0}; // cross sectional area
double I2{0}; // second moment of inertia
double I3{0}; // third moment of inertia
double J{0}; // torsional constant (polar moment of inertia)
void computeMaterialProperties(const ElementMaterial &material) {
E = material.youngsModulusGPascal * std::pow(10, 9);
G = E / (2 * (1 + material.poissonsRatio));
}
void
computeDimensionsProperties(const RectangularBeamDimensions &dimensions) {
A = (dimensions.b * dimensions.h);
I2 = dimensions.b * std::pow(dimensions.h, 3) / 12;
I3 = dimensions.h * std::pow(dimensions.b, 3) / 12;
}
void computeDimensionsProperties(
const CylindricalElementDimensions &dimensions) {
A = M_PI *
(std::pow(dimensions.od / 2, 2) - std::pow(dimensions.id / 2, 2));
I2 =
M_PI * (std::pow(dimensions.od, 4) - std::pow(dimensions.id, 4)) / 64;
I3 = I2;
}
Properties(const RectangularBeamDimensions &dimensions,
const ElementMaterial &material) {
computeDimensionsProperties(dimensions);
computeMaterialProperties(material);
J = I2 + I3;
}
Properties(const CylindricalElementDimensions &dimensions,
const ElementMaterial &material) {
computeDimensionsProperties(dimensions);
computeMaterialProperties(material);
J = I2 + I3;
}
Properties() {}
};
struct LocalFrame {
VectorType t1;
VectorType t2;
VectorType t3;
};
EdgeIndex ei;
double length{0};
Properties properties;
double initialLength;
LocalFrame frame;
double axialConstFactor;
double torsionConstFactor;
double firstBendingConstFactor;
double secondBendingConstFactor;
VectorType f1_j;
VectorType f1_jplus1;
VectorType f2_j;
VectorType f2_jplus1;
VectorType f3_j;
VectorType f3_jplus1;
double cosRotationAngle_j;
double cosRotationAngle_jplus1;
double sinRotationAngle_j;
double sinRotationAngle_jplus1;
std::vector<std::vector<VectorType>> derivativeT1;
std::vector<std::vector<VectorType>> derivativeT2;
std::vector<std::vector<VectorType>> derivativeT3;
std::vector<VectorType> derivativeT1_j;
std::vector<VectorType> derivativeT1_jplus1;
std::vector<VectorType> derivativeT2_j;
std::vector<VectorType> derivativeT2_jplus1;
std::vector<VectorType> derivativeT3_j;
std::vector<VectorType> derivativeT3_jplus1;
std::vector<VectorType> derivativeR_j;
std::vector<VectorType> derivativeR_jplus1;
struct RotationalDisplacements {
double theta1{0}, theta2{0}, theta3{0};
};
RotationalDisplacements rotationalDisplacements_j;
RotationalDisplacements rotationalDisplacements_jplus1;
};
struct Node {
struct Forces {
Vector6d external{0};
Vector6d internal{0};
Vector6d residual{0};
Vector6d internalAxial{0};
Vector6d internalFirstBending{0};
Vector6d internalSecondBending{0};
bool hasExternalForce() const { return external.isZero(); }
};
VertexIndex vi;
CoordType initialLocation;
CoordType previousLocation;
CoordType initialNormal;
double translationalMass;
double rotationalMass_I2;
double rotationalMass_I3;
double rotationalMass_J;
Vector6d acceleration{0};
Forces force;
Vector6d velocity{0};
double kineticEnergy{0};
Vector6d displacements{0};
double nR{0};
std::unordered_map<EdgeIndex, double>
alphaAngles; // contains the initial angles between the first star element
// incident to this node and the other elements of the star
// has size equal to the valence of the vertex
std::vector<VCGEdgeMesh::EdgePointer> incidentElements;
std::vector<VectorType> derivativeOfNormal;
SimulationMesh::EdgePointer referenceElement;
};
Element::LocalFrame computeElementFrame(const CoordType &p0,
const CoordType &p1,
const VectorType &elementNormal);
VectorType computeT1Vector(const ::SimulationMesh::EdgeType &e);
VectorType computeT1Vector(const CoordType &p0, const CoordType &p1);
double computeAngle(const VectorType &vector0, const VectorType &vector1,
const VectorType &normalVector);
#endif // ELEMENTALMESH_HPP

397
flatpattern.cpp Normal file
View File

@ -0,0 +1,397 @@
#include "flatpattern.hpp"
#include "trianglepatterngeometry.hpp"
#include <filesystem>
FlatPattern::FlatPattern() {}
FlatPattern::FlatPattern(const string &filename, bool addNormalsIfAbsent) {
assert(std::filesystem::exists(std::filesystem::path(filename)));
loadFromPly(filename);
if (addNormalsIfAbsent) {
bool normalsAreAbsent = vert[0].cN().Norm() < 0.000001;
if (normalsAreAbsent) {
for (auto &v : vert) {
v.N() = CoordType(0, 0, 1);
}
}
}
vcg::tri::UpdateTopology<FlatPattern>::VertexEdge(*this);
// scale();
}
FlatPattern::FlatPattern(const std::vector<size_t> &numberOfNodesPerSlot,
const std::vector<vcg::Point2i> &edges) {
add(numberOfNodesPerSlot, edges);
// add normals
bool normalsAreAbsent = vert[0].cN().Norm() < 0.000001;
if (normalsAreAbsent) {
for (auto &v : vert) {
v.N() = CoordType(0, 0, 1);
}
}
updateEigenEdgeAndVertices();
}
bool FlatPattern::createHoneycombAtom() {
VCGEdgeMesh honeycombQuarter;
const VCGEdgeMesh::CoordType n(0, 0, 1);
const double H = 0.2;
const double height = 1.5 * H;
const double width = 0.2;
const double theta = 70;
const double dy = tan(vcg::math::ToRad(90 - theta)) * width / 2;
vcg::tri::Allocator<VCGEdgeMesh>::AddVertex(
honeycombQuarter, VCGEdgeMesh::CoordType(0, height / 2, 0), n);
vcg::tri::Allocator<VCGEdgeMesh>::AddVertex(
honeycombQuarter, VCGEdgeMesh::CoordType(0, H / 2 - dy, 0), n);
vcg::tri::Allocator<VCGEdgeMesh>::AddVertex(
honeycombQuarter, VCGEdgeMesh::CoordType(width / 2, H / 2, 0), n);
vcg::tri::Allocator<VCGEdgeMesh>::AddVertex(
honeycombQuarter, VCGEdgeMesh::CoordType(width / 2, 0, 0), n);
vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(honeycombQuarter, 0, 1);
vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(honeycombQuarter, 1, 2);
vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(honeycombQuarter, 2, 3);
VCGEdgeMesh honeycombAtom;
// Top right
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::MeshCopy(honeycombAtom,
honeycombQuarter);
// Bottom right
vcg::Matrix44d rotM;
rotM.SetRotateDeg(180, vcg::Point3d(1, 0, 0));
vcg::tri::UpdatePosition<VCGEdgeMesh>::Matrix(honeycombQuarter, rotM);
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::Mesh(honeycombAtom,
honeycombQuarter);
// Bottom left
rotM.SetRotateDeg(180, vcg::Point3d(0, 1, 0));
vcg::tri::UpdatePosition<VCGEdgeMesh>::Matrix(honeycombQuarter, rotM);
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::Mesh(honeycombAtom,
honeycombQuarter);
// Top left
rotM.SetRotateDeg(180, vcg::Point3d(1, 0, 0));
vcg::tri::UpdatePosition<VCGEdgeMesh>::Matrix(honeycombQuarter, rotM);
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::Mesh(honeycombAtom,
honeycombQuarter);
for (VertexType &v : honeycombAtom.vert) {
v.P()[2] = 0;
}
return true;
}
void FlatPattern::deleteDanglingEdges() {
for (VertexType &v : vert) {
std::vector<VCGEdgeMesh::EdgePointer> incidentElements;
vcg::edge::VEStarVE(&v, incidentElements);
if (incidentElements.size() == 1) {
vcg::tri::Allocator<VCGEdgeMesh>::DeleteEdge(*this, *incidentElements[0]);
}
if (incidentElements.size() == 1) {
vcg::tri::Allocator<VCGEdgeMesh>::DeleteVertex(*this, v);
}
}
vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateVertex(*this);
vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateEdge(*this);
vcg::tri::Allocator<VCGEdgeMesh>::CompactEveryVector(*this);
}
void FlatPattern::scale() {
const double baseTriangleCentralEdgeSize =
(vert[0].cP() - vert[3].cP()).Norm();
const double scaleRatio =
desiredBaseTriangleCentralEdgeSize / baseTriangleCentralEdgeSize;
vcg::tri::UpdatePosition<VCGEdgeMesh>::Scale(*this, scaleRatio);
}
void FlatPattern::deleteDanglingVertices() {
vcg::tri::Allocator<FlatPattern>::PointerUpdater<VertexPointer> pu;
deleteDanglingVertices(pu);
}
void FlatPattern::deleteDanglingVertices(
vcg::tri::Allocator<FlatPattern>::PointerUpdater<VertexPointer> &pu) {
for (VertexType &v : vert) {
std::vector<FlatPattern::EdgePointer> incidentElements;
vcg::edge::VEStarVE(&v, incidentElements);
if (incidentElements.size() == 0) {
vcg::tri::Allocator<FlatPattern>::DeleteVertex(*this, v);
}
}
vcg::tri::Allocator<FlatPattern>::CompactVertexVector(*this, pu);
vcg::tri::Allocator<FlatPattern>::CompactEdgeVector(*this);
updateEigenEdgeAndVertices();
}
void FlatPattern::tilePattern(VCGEdgeMesh &pattern, VCGPolyMesh &tileInto,
const bool &shouldDeleteDanglingEdges) {
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);
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::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
vcg::tri::Clean<VCGEdgeMesh>::MergeCloseVertex(*this, 0.0000000001);
deleteDanglingVertices();
deleteDanglingEdges();
vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateVertex(*this);
vcg::tri::Clean<VCGEdgeMesh>::RemoveDegenerateEdge(*this);
vcg::tri::Allocator<VCGEdgeMesh>::CompactEveryVector(*this);
updateEigenEdgeAndVertices();
savePly("tiledPattern.ply");
vcg::tri::Clean<VCGTriMesh>::MergeCloseVertex(tileIntoEdgeMesh, 0.0000000001);
vcg::tri::Clean<VCGTriMesh>::RemoveDegenerateVertex(tileIntoEdgeMesh);
vcg::tri::Clean<VCGTriMesh>::RemoveDegenerateEdge(tileIntoEdgeMesh);
tileIntoEdgeMesh.savePly("tileIntoTriMesh.ply");
}
void FlatPattern::createFan(const size_t &fanSize) {
FlatPattern rotatedPattern;
vcg::tri::Append<FlatPattern, FlatPattern>::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<FlatPattern>::Matrix(rotatedPattern, R);
vcg::tri::Append<FlatPattern, FlatPattern>::Mesh(*this, rotatedPattern);
}
removeDuplicateVertices();
updateEigenEdgeAndVertices();
}
void FlatPattern::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<FlatPattern>::VertexEdge(*this);
}
void FlatPattern::tilePattern(VCGEdgeMesh &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();
}

33
flatpattern.hpp Normal file
View File

@ -0,0 +1,33 @@
#ifndef FLATPATTERN_HPP
#define FLATPATTERN_HPP
#include "trianglepatterngeometry.hpp"
class FlatPattern : public FlatPatternGeometry {
public:
FlatPattern();
FlatPattern(const std::string &filename, bool addNormalsIfAbsent = true);
FlatPattern(const std::vector<size_t> &numberOfNodesPerSlot,
const std::vector<vcg::Point2i> &edges);
bool createHoneycombAtom();
void tilePattern(VCGEdgeMesh &pattern, VCGTriMesh &tileInto);
void tilePattern(VCGEdgeMesh &pattern, VCGPolyMesh &tileInto,
const bool &shouldDeleteDanglingEdges);
void createFan(const size_t &fanSize = 6);
void deleteDanglingVertices(
vcg::tri::Allocator<FlatPattern>::PointerUpdater<VertexPointer> &pu);
void deleteDanglingVertices();
private:
void deleteDanglingEdges();
void removeDuplicateVertices();
void scale();
const double desiredBaseTriangleCentralEdgeSize{0.25 / std::tan(M_PI / 6)};
};
#endif // FLATPATTERN_HPP

116
patternIO.cpp Normal file
View File

@ -0,0 +1,116 @@
#include "patternIO.hpp"
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
PatternIO::PatternIO() {}
void PatternIO::save(const std::string &filepath,
const PatternSet &patternSet) {
std::ofstream fileStream;
if (std::filesystem::exists(filepath)) {
fileStream.open(filepath, std::ios_base::app);
} else {
fileStream.open(filepath);
fileStream << "#Nodes" << std::endl;
for (vcg::Point2d node : patternSet.nodes) {
fileStream << "n " << node.X() << " " << node.Y() << std::endl;
}
fileStream << "#Patterns" << std::endl;
}
for (const Pattern &pattern : patternSet.patterns) {
fileStream << "p " << pattern.labels.size() << " " << pattern.edges.size()
<< " ";
for (size_t labelIndex = 0; labelIndex < pattern.labels.size() - 1;
labelIndex++) {
fileStream << pattern.labels[labelIndex] << " ";
}
fileStream << pattern.labels[pattern.labels.size() - 1] << " ";
for (const vcg::Point2i &edge : pattern.edges) {
fileStream << " " << edge[0] << " " << edge[1] << " ";
}
fileStream << std::endl;
}
}
void PatternIO::save(const std::string &filepath, const Pattern &pattern) {
std::ofstream fileStream;
if (std::filesystem::exists(filepath)) {
fileStream.open(filepath, std::ios_base::app);
} else {
fileStream.open(filepath);
fileStream << "#Nodes" << std::endl;
fileStream << "#Patterns" << std::endl;
}
fileStream << "p " << pattern.labels.size() << " " << pattern.edges.size()
<< " ";
for (size_t labelIndex = 0; labelIndex < pattern.labels.size() - 1;
labelIndex++) {
fileStream << pattern.labels[labelIndex] << " ";
}
fileStream << pattern.labels[pattern.labels.size() - 1] << " ";
for (const vcg::Point2i &edge : pattern.edges) {
fileStream << " " << edge[0] << " " << edge[1] << " ";
}
fileStream << std::endl;
}
void PatternIO::load(const std::string &filepath, PatternSet &patternSet,
const std::vector<PatternLabel> &targetLabels) {
std::ifstream fileStream(filepath);
std::string line;
std::vector<PatternLabel> sortedTargetPatternLabels(targetLabels);
std::sort(sortedTargetPatternLabels.begin(), sortedTargetPatternLabels.end());
while (std::getline(fileStream, line)) {
std::cout << line << std::endl;
std::istringstream iss(line);
char lineID;
iss >> lineID;
if (lineID == 'n') {
double x, y;
iss >> x >> y;
std::cout << x << " " << y << std::endl;
} else if (lineID == 'p') {
Pattern pattern;
size_t numberOfLabels, numberOfEdges;
iss >> numberOfLabels >> numberOfEdges;
std::cout << "NumberOfLabels:" << numberOfLabels << std::endl;
std::cout << "NumberOfEdges:" << numberOfEdges << std::endl;
std::vector<PatternLabel> patternLabels;
for (size_t labelIndex = 0; labelIndex < numberOfLabels; labelIndex++) {
size_t patternLabel;
iss >> patternLabel;
PatternLabel pl = static_cast<PatternLabel>(patternLabel);
std::cout << "Pattern label read:" << patternLabel << std::endl;
patternLabels.push_back(pl);
}
if (!targetLabels.empty()) {
std::sort(patternLabels.begin(), patternLabels.end());
std::vector<PatternLabel> labelIntersection;
std::set_intersection(patternLabels.begin(), patternLabels.end(),
sortedTargetPatternLabels.begin(),
sortedTargetPatternLabels.end(),
labelIntersection.begin());
if (!labelIntersection.empty()) {
pattern.labels = patternLabels;
} else {
continue;
}
} else {
pattern.labels = patternLabels;
}
for (size_t edgeIndex = 0; edgeIndex < numberOfEdges; edgeIndex++) {
size_t ni0, ni1;
iss >> ni0 >> ni1;
vcg::Point2i edge(ni0, ni1);
pattern.edges.push_back(edge);
std::cout << "Node indices read:" << ni0 << "," << ni1 << std::endl;
}
}
}
}

40
patternIO.hpp Normal file
View File

@ -0,0 +1,40 @@
#ifndef PATTERNEXPORTER_HPP
#define PATTERNEXPORTER_HPP
#include <fstream>
#include <string>
#include <vcg/space/deprecated_point2.h>
#include <vector>
enum PatternLabel {
Valid = 0,
MultipleCC,
DanglingEdge,
IntersectingEdges,
ArticulationPoints
};
struct Pattern {
std::vector<vcg::Point2i> edges;
std::vector<PatternLabel> labels;
};
/*
* A set of planar patterns using the same nodes
* */
struct PatternSet {
std::vector<vcg::Point2d> nodes;
std::vector<Pattern> patterns;
};
class PatternIO {
public:
PatternIO();
static void save(const std::string &filepath, const Pattern &pattern);
static void save(const std::string &filepath, const PatternSet &patterns);
static void load(const std::string &filepath, PatternSet &patternSet,
const std::vector<PatternLabel> &targetLabels = {});
};
#endif // PATTERNEXPORTER_HPP

64
polymesh.hpp Normal file
View File

@ -0,0 +1,64 @@
#ifndef POLYMESH_HPP
#define POLYMESH_HPP
#include "vcg/complex/complex.h"
#include <filesystem>
#include <wrap/io_trimesh/import.h>
//#include <wrap/nanoply/include/nanoplyWrapper.hpp>
class PFace;
class PVertex;
struct PUsedTypes : public vcg::UsedTypes<vcg::Use<PVertex>::AsVertexType,
vcg::Use<PFace>::AsFaceType> {};
class PVertex : public vcg::Vertex<PUsedTypes, vcg::vertex::Coord3d,
vcg::vertex::Normal3d, vcg::vertex::Mark,
vcg::vertex::Qualityf, vcg::vertex::BitFlags,
vcg::vertex::VFAdj> {};
class PFace
: public vcg::Face<
PUsedTypes,
vcg::face::PolyInfo // this is necessary if you use component in
// vcg/simplex/face/component_polygon.h
// It says "this class is a polygon and the memory for its components
// (e.g. pointer to its vertices will be allocated dynamically")
,
vcg::face::PFVAdj // Pointer to the vertices (just like FVAdj )
,
vcg::face::PFVAdj,
vcg::face::PFFAdj // Pointer to edge-adjacent face (just like FFAdj )
,
vcg::face::BitFlags // bit flags
,
vcg::face::Qualityf // quality
,
vcg::face::Normal3f // normal
> {};
class VCGPolyMesh
: public vcg::tri::TriMesh<std::vector<PVertex>, // the vector of vertices
std::vector<PFace> // the vector of faces
> {
public:
void loadFromPlyFile(const std::string &filename) {
vcg::tri::io::ImporterOBJ<VCGPolyMesh>::Info info;
vcg::tri::io::ImporterOBJ<VCGPolyMesh>::Open(*this, filename.c_str(), info);
// unsigned int mask = 0;
// mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_VERTCOORD;
// mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_VERTNORMAL;
// mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_VERTCOLOR;
// mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_EDGEINDEX;
// mask |= nanoply::NanoPlyWrapper<VCGPolyMesh>::IO_FACEINDEX;
// if (nanoply::NanoPlyWrapper<VCGPolyMesh>::LoadModel(
// std::filesystem::absolute(filename).c_str(), *this, mask) !=
// 0) {
// std::cout << "Could not load tri mesh" << std::endl;
// }
vcg::tri::UpdateTopology<VCGPolyMesh>::FaceFace(*this);
// vcg::tri::UpdateTopology<VCGPolyMesh>::VertexFace(*this);
vcg::tri::UpdateNormal<VCGPolyMesh>::PerVertexNormalized(*this);
}
};
#endif // POLYMESH_HPP

View File

@ -0,0 +1,145 @@
#ifndef SIMULATIONHISTORYPLOTTER_HPP
#define SIMULATIONHISTORYPLOTTER_HPP
#include "elementalmesh.hpp"
#include "simulationresult.hpp"
#include "utilities.hpp"
#include <algorithm>
#include <matplot/matplot.h>
struct SimulationResultsReporter {
using VertexType = VCGEdgeMesh::VertexType;
using CoordType = VCGEdgeMesh::CoordType;
using Vector6d = Vector6d;
SimulationResultsReporter() {}
void writeStatistics(const SimulationResults &results,
const std::string &reportFolderPath) {
ofstream file;
file.open(
std::filesystem::path(reportFolderPath).append("results.txt").string());
const size_t numberOfSteps = results.history.numberOfSteps;
file << "Number of steps " << numberOfSteps << "\n";
// file << "Force threshold used " << 1000 << "\n";
// assert(numberOfSteps == results.history.potentialEnergy.size() &&
// numberOfSteps == results.history.residualForces.size());
// Write kinetic energies
const SimulationHistory &history = results.history;
if (!history.kineticEnergy.empty()) {
file << "Kinetic energies"
<< "\n";
for (size_t step = 0; step < numberOfSteps; step++) {
file << history.kineticEnergy[step] << "\n";
}
file << "\n";
}
if (!history.residualForces.empty()) {
file << "Residual forces"
<< "\n";
for (size_t step = 0; step < numberOfSteps; step++) {
file << history.residualForces[step] << "\n";
}
file << "\n";
}
if (!history.potentialEnergies.empty()) {
file << "Potential energies"
<< "\n";
for (size_t step = 0; step < numberOfSteps; step++) {
file << history.potentialEnergies[step] << "\n";
}
file << "\n";
}
file.close();
}
void reportResults(
const std::vector<SimulationResults> &results,
const std::string &reportFolderPath,
const std::string &graphSuffix = std::string(),
const SimulationResults &groundOfTruthResults = SimulationResults()) {
if (results.empty()) {
return;
}
// std::filesystem::remove_all(debuggerFolder);
std::filesystem::create_directory(reportFolderPath);
for (const SimulationResults &simulationResult : results) {
const auto simulationResultPath =
std::filesystem::path(reportFolderPath)
.append(simulationResult.simulationLabel);
std::filesystem::create_directory(simulationResultPath.string());
createPlots(simulationResult.history, simulationResultPath.string(),
graphSuffix);
writeStatistics(simulationResult, simulationResultPath);
}
}
static void createPlot(const std::string &xLabel, const std::string &yLabel,
const std::vector<double> &YvaluesToPlot,
const std::string &saveTo = {}) {
matplot::xlabel(xLabel);
matplot::ylabel(yLabel);
matplot::figure(true);
// matplot::hold(matplot::on);
matplot::grid(matplot::on);
auto x = matplot::linspace(0, YvaluesToPlot.size(), YvaluesToPlot.size());
matplot::scatter(x, YvaluesToPlot)
// ->marker_indices(history.redMarks)
// ->marker_indices(truncatedRedMarks)
// .marker_color("r")
->marker_size(1);
// auto greenMarksY = matplot::transform(
// history.greenMarks, [&](auto x) { return history.kineticEnergy[x];
// });
// matplot::scatter(history.greenMarks, greenMarksY)
// ->color("green")
// .marker_size(10);
// matplot::hold(matplot::off);
if (!saveTo.empty()) {
matplot::save(saveTo);
}
}
void createPlots(const SimulationHistory &history,
const std::string &reportFolderPath,
const std::string &graphSuffix) {
const auto graphsFolder =
std::filesystem::path(reportFolderPath).append("Graphs");
std::filesystem::remove_all(graphsFolder);
std::filesystem::create_directory(graphsFolder.string());
if (!history.kineticEnergy.empty()) {
createPlot("Number of Iterations", "Log of Kinetic Energy",
history.kineticEnergy,
std::filesystem::path(graphsFolder)
.append("KineticEnergy" + graphSuffix + ".png")
.string());
}
if (!history.residualForces.empty()) {
createPlot("Number of Iterations", "Residual Forces norm",
history.residualForces,
std::filesystem::path(graphsFolder)
.append("ResidualForces" + graphSuffix + ".png")
.string());
}
if (!history.potentialEnergies.empty()) {
createPlot("Number of Iterations", "Potential energy",
history.potentialEnergies,
std::filesystem::path(graphsFolder)
.append("PotentialEnergy" + graphSuffix + ".png")
.string());
}
}
};
#endif // SIMULATIONHISTORYPLOTTER_HPP

199
simulationresult.hpp Normal file
View File

@ -0,0 +1,199 @@
#ifndef SIMULATIONHISTORY_HPP
#define SIMULATIONHISTORY_HPP
#include "elementalmesh.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();
}
};
struct SimulationJob {
std::shared_ptr<SimulationMesh> mesh;
std::unordered_map<VertexIndex, std::unordered_set<int>> fixedVertices;
std::unordered_map<VertexIndex, Vector6d> nodalExternalForces;
std::unordered_map<VertexIndex, Eigen::Vector3d> nodalForcedDisplacements;
void registerForDrawing() const {
initPolyscope();
const std::string polyscopeName = mesh->getLabel() + "_Simulation Job";
polyscope::registerCurveNetwork(polyscopeName, mesh->getEigenVertices(),
mesh->getEigenEdges());
// polyscope::registerPointCloud("Undeformed edge mesh PC",
// job.edgeMesh.getEigenVertices());
std::vector<std::array<double, 3>> nodeColors(mesh->VN());
for (auto fixedVertex : fixedVertices) {
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;
}
});
if (!nodeColors.empty()) {
polyscope::getCurveNetwork(polyscopeName)
->addNodeColorQuantity("Boundary conditions", nodeColors);
polyscope::getCurveNetwork(polyscopeName)
->getQuantity("Boundary conditions")
->setEnabled(true);
}
// per node external forces
std::vector<std::array<double, 3>> externalForces(mesh->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(polyscopeName)
->addNodeVectorQuantity("External force", externalForces);
polyscope::getCurveNetwork(polyscopeName)
->getQuantity("External force")
->setEnabled(true);
}
}
};
struct SimulationResults {
SimulationHistory history;
std::vector<Vector6d> displacements;
double executionTime{0};
std::string simulationLabel{"NoLabel"};
void registerForDrawing(const SimulationJob &job) 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_" + simulationLabel;
polyscope::registerCurveNetwork(undeformedMeshName,
job.mesh->getEigenVertices(),
job.mesh->getEigenEdges());
const std::string deformedMeshName = "Deformed_" + simulationLabel;
polyscope::registerCurveNetwork(deformedMeshName,
job.mesh->getEigenVertices(),
job.mesh->getEigenEdges());
Eigen::MatrixX3d nodalDisplacements(job.mesh->VN(), 3);
for (VertexIndex vi = 0; vi < job.mesh->VN(); vi++) {
const Vector6d &nodalDisplacement = displacements[vi];
nodalDisplacements.row(vi) = Eigen::Vector3d(
nodalDisplacement[0], nodalDisplacement[1], nodalDisplacement[2]);
}
polyscope::getCurveNetwork(deformedMeshName)
->updateNodePositions(job.mesh->getEigenVertices() +
nodalDisplacements);
std::vector<std::array<double, 3>> nodeColors(job.mesh->VN());
for (auto fixedVertex : job.fixedVertices) {
nodeColors[fixedVertex.first] = {0, 0, 1};
}
if (!job.nodalForcedDisplacements.empty()) {
for (std::pair<VertexIndex, Eigen::Vector3d> viDisplPair :
job.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;
}
});
// per node external forces
std::vector<std::array<double, 3>> externalForces(job.mesh->VN());
for (const auto &forcePair : job.nodalExternalForces) {
auto index = forcePair.first;
auto force = forcePair.second;
externalForces[index] = {force[0], force[1], force[2]};
}
polyscope::getCurveNetwork(undeformedMeshName)
->addNodeColorQuantity("Boundary conditions", nodeColors);
polyscope::getCurveNetwork(undeformedMeshName)
->getQuantity("Boundary conditions")
->setEnabled(true);
polyscope::getCurveNetwork(undeformedMeshName)
->addNodeVectorQuantity("External force", externalForces);
polyscope::getCurveNetwork(undeformedMeshName)
->getQuantity("External force")
->setEnabled(true);
polyscope::getCurveNetwork(deformedMeshName)
->addNodeColorQuantity("Boundary conditions", nodeColors);
polyscope::getCurveNetwork(deformedMeshName)
->getQuantity("Boundary conditions")
->setEnabled(false);
polyscope::getCurveNetwork(deformedMeshName)
->addNodeVectorQuantity("External force", externalForces);
polyscope::getCurveNetwork(deformedMeshName)
->getQuantity("External force")
->setEnabled(true);
// polyscope::show();
}
};
#endif // SIMULATIONHISTORY_HPP

609
topologyenumerator.cpp Normal file
View File

@ -0,0 +1,609 @@
#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{true};
// 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) {
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);
}
FlatPatternGeometry patternGeometryAllEdges;
patternGeometryAllEdges.add(numberOfNodesPerSlot, allPossibleEdges);
// Create Results path
auto resultPath =
// std::filesystem::path("/home/iason/Documents/PhD/Research/Enumerating\\
// "
// "2d\\ connections\\ of\\ nodes");
std::filesystem::current_path()
.parent_path()
.parent_path()
.parent_path()
.parent_path();
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(FlatPatternGeometry().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.savePly(std::filesystem::path(resultsPath)
.append("allPossibleEdges.ply")
.string());
}
// statistics.numberOfPossibleEdges = numberOfAllPossibleEdges;
std::vector<vcg::Point2i> validEdges =
getValidEdges(numberOfNodesPerSlot, resultsPath, patternGeometryAllEdges,
allPossibleEdges);
FlatPatternGeometry patternAllValidEdges;
patternAllValidEdges.add(patternGeometryAllEdges.getVertices(), validEdges);
if (debugIsOn) {
// Export all valid edges in a ply
patternAllValidEdges.savePly(
std::filesystem::path(resultsPath).append("allValidEdges.ply"));
}
// 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++) {
FlatPatternGeometry intersectingEdgePair;
const size_t ei0 = mapIt->first;
const size_t ei1 = *setIt;
vcg::tri::Allocator<FlatPatternGeometry>::AddEdge(
intersectingEdgePair,
patternGeometryAllEdges.getVertices()[validEdges[ei0][0]],
patternGeometryAllEdges.getVertices()[validEdges[ei0][1]]);
vcg::tri::Allocator<FlatPatternGeometry>::AddEdge(
intersectingEdgePair,
patternGeometryAllEdges.getVertices()[validEdges[ei1][0]],
patternGeometryAllEdges.getVertices()[validEdges[ei1][1]]);
intersectingEdgePair.savePly(
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::Point2d(nodes[nodeIndex][0], nodes[nodeIndex][1]);
}
if (std::filesystem::exists(std::filesystem::path(resultsPath)
.append("patterns.patt")
.string())) {
std::filesystem::remove(
std::filesystem::path(resultsPath).append("patterns.patt"));
}
for (size_t numberOfEdges = 2; numberOfEdges < validEdges.size();
numberOfEdges++) {
// for (size_t numberOfEdges = 1; numberOfEdges < 3; 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, patternSet);
// statistics.print(setupString, perEdgeResultPath);
PatternIO::save(
std::filesystem::path(resultsPath).append("patterns.patt").string(),
patternSet);
}
}
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 FlatPatternGeometry &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) {
FlatPatternGeometry::EdgeType e =
patternGeometryAllEdges.edge[coincideEdgeIndex];
FlatPatternGeometry 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.savePly(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);
FlatPatternGeometry patternDuplicateEdges;
for (auto duplicateEdgeIndex : duplicateEdges) {
FlatPatternGeometry::EdgeType e =
patternGeometryAllEdges.edge[duplicateEdgeIndex];
vcg::Point3d p0 = e.cP(0);
vcg::Point3d p1 = e.cP(1);
vcg::tri::Allocator<FlatPatternGeometry>::AddEdge(
patternDuplicateEdges, p0, p1);
}
patternDuplicateEdges.savePly(
std::filesystem::path(duplicateEdgesPath).append("duplicateEdges.ply"));
}
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, PatternSet &patternsSet) {
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);
const size_t numberOfPatterns = FlatPatternGeometry::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());
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];
}
}
Pattern pattern;
pattern.edges = patternEdges;
FlatPatternGeometry 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) {
FlatPatternGeometry tiledPatternGeometry =
FlatPatternGeometry::createTile(patternGeometry);
auto intersectingPatternsPath =
std::filesystem::path(resultsPath).append("Intersecting");
std::filesystem::create_directory(intersectingPatternsPath);
patternGeometry.savePly(
std::filesystem::path(intersectingPatternsPath)
.append(patternName)
.string() +
".ply");
tiledPatternGeometry.savePly(
std::filesystem::path(intersectingPatternsPath)
.append(patternName + "_tiled")
.string() +
".ply");
}
pattern.labels.push_back(PatternLabel::IntersectingEdges);
} 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();
FlatPatternGeometry tiledPatternGeometry =
FlatPatternGeometry::createTile(
patternGeometry); // the marked nodes of hasDanglingEdges are
// 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.savePly(std::filesystem::path(danglingEdgesPath)
.append(patternName)
.string() +
".ply");
tiledPatternGeometry.savePly(std::filesystem::path(danglingEdgesPath)
.append(patternName + "_tiled")
.string() +
".ply");
}
pattern.labels.push_back(PatternLabel::DanglingEdge);
} 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.savePly(std::filesystem::path(moreThanOneCCPath)
.append(patternName)
.string() +
".ply");
tiledPatternGeometry.savePly(std::filesystem::path(moreThanOneCCPath)
.append(patternName + "_tiled")
.string() +
".ply");
}
pattern.labels.push_back(PatternLabel::MultipleCC);
} 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.savePly(std::filesystem::path(articulationPointsPath)
.append(patternName)
.string() +
".ply");
tiledPatternGeometry.savePly(
std::filesystem::path(articulationPointsPath)
.append(patternName + "_tiled")
.string() +
".ply");
// std::cout << "Pattern:" << patternName << std::endl;
}
pattern.labels.push_back(PatternLabel::ArticulationPoints);
} 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.savePly(std::filesystem::path(validPatternsPath)
.append(patternName)
.string() +
".ply");
tiledPatternGeometry.savePly(std::filesystem::path(validPatternsPath)
.append(patternName + "_tiled")
.string() +
".ply");
}
pattern.labels.push_back(PatternLabel::Valid);
}
assert(!pattern.labels.empty());
patternsSet.patterns.push_back(pattern);
// assert(vcg_tiledPatternHasDangling == tiledPatternHasDanglingEdges);
} while (std::next_permutation(patternBinaryRepresentation.begin(),
patternBinaryRepresentation.end()));
}
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;
}

180
topologyenumerator.hpp Normal file
View File

@ -0,0 +1,180 @@
#ifndef TOPOLOGYENUMERATOR_HPP
#define TOPOLOGYENUMERATOR_HPP
#include "patternIO.hpp"
#include "trianglepatterngeometry.hpp"
#include "trianglepattterntopology.hpp"
#include <filesystem>
#include <fstream>
#include <iostream>
//#include <nlohmann/json.hpp>
#include <string>
#include <vector>
using GraphEdge = std::pair<size_t, size_t>;
/*
* Represents a graph formed by slots on a triangle that can either be filled or
* not. The slots are three and are: 1) one slot per vertex. Each slot can hold
* a signle node. 2) one slot per edge. Each slot can hold many nodes. 3) one
* slot in the inside of the triangle. Each slot can hold multiple nodes.
* */
class TopologyEnumerator {
/*
* Holds the in a CCW order the vertex and the edge slots and then the face
* slot. [0],[1],[2] can either be 0 or 1 [3],[4],[5] are integers in [0,n]
* [6] an integer [0,n]
* */
/*
reduced nodes per slot
0
/\
/ \
2 2
/ 4 \
/ \
1----3-----1
nodes per slot
0
/\
/ \
3 5
/ 6 \
/ \
1----4-----2
slot=0 -> vi=0
slot=1 -> vi=1
slot=2 -> vi=2
...
see TrianglePatternGeometry::constructVertexVector
*/
struct TopologicalStatistics {
size_t numberOfPossibleEdges{0};
size_t numberOfCoincideEdges{0};
size_t numberOfDuplicateEdges{0};
size_t numberOfValidEdges{0};
size_t numberOfIntersectingEdgePairs{0};
size_t numberOfPatterns{0};
// size_t numberOfIntersectingEdgesOverAllPatterns{0};
size_t numberOfPatternsWithIntersectingEdges{0};
size_t numberOfPatternsWithMoreThanASingleCC{0};
size_t numberOfPatternsWithADanglingEdgeOrNode{0};
size_t numberOfPatternsWithArticulationPoints{0};
size_t numberOfValidPatterns{0};
// nlohmann::json convertToJson() const {
// nlohmann::json json;
// json["numPossibleEdges"] = numberOfPossibleEdges;
// json["numCoincideEdges"] = numberOfCoincideEdges;
// json["numDuplicateEdges"] = numberOfDuplicateEdges;
// json["numValidEdges"] = numberOfValidEdges;
// json["numIntersectingEdgePairs"] = numberOfIntersectingEdgePairs;
// json["numPatterns"] = numberOfPatterns;
// // json["numIntersectingEdgesOverAllPatterns"] =
// // numberOfIntersectingEdgesOverAllPatterns;
// json["numPatternsWithIntersectingEdges"] =
// numberOfPatternsWithIntersectingEdges;
// json["numPatternsWithNotASingleCC"] =
// numberOfPatternsWithMoreThanASingleCC;
// json["numPatternsWithDangling"] =
// numberOfPatternsWithADanglingEdgeOrNode;
// json["numPatternsWithArticulationPoints"] =
// numberOfPatternsWithArticulationPoints;
// json["numValidPatterns"] = numberOfValidPatterns;
// return json;
// }
void print(const std::string &setupString,
const std::filesystem::path &directoryPath) const {
std::cout << "The setup " << setupString << std::endl;
std::cout << "Has following statistics:" << std::endl;
std::cout << numberOfPossibleEdges
<< " possible edges with the given arrangement of nodes"
<< std::endl;
std::cout << numberOfCoincideEdges << " coincide edges" << std::endl;
std::cout << numberOfDuplicateEdges << " duplicate edges" << std::endl;
std::cout << numberOfValidEdges << " valid edges" << std::endl;
std::cout << numberOfIntersectingEdgePairs << "intersecting edge pairs "
<< std::endl;
std::cout << numberOfPatterns
<< " patterns can be generated with the given setup"
<< std::endl;
// std::cout << numberOfIntersectingEdgesOverAllPatterns
// << " intersecting edges found over all patterns" <<
// std::endl;
std::cout << numberOfPatternsWithIntersectingEdges
<< " patterns found with intersecting edges" << std::endl;
std::cout << numberOfPatternsWithMoreThanASingleCC
<< " patterns found with more than one connected components"
<< std::endl;
std::cout << numberOfPatternsWithADanglingEdgeOrNode
<< " patterns found with a dangling node or edge" << std::endl;
std::cout << numberOfPatternsWithArticulationPoints
<< " patterns found with an articulation point" << std::endl;
std::cout << numberOfValidPatterns << " valid patterns were found"
<< std::endl;
// if (!directoryPath.empty()) {
// auto json = convertToJson();
// std::ofstream file;
// file.open(std::filesystem::path(directoryPath)
// .append("statistics.csv")
// .string());
// file << "setup," << setupString << "\n";
// for (const auto &el : json.items()) {
// file << el.key() << "," << el.value() << "\n";
// }
// }
}
};
TopologicalStatistics statistics;
std::vector<size_t> numberOfNodesPerSlot;
size_t numberOfNodes;
size_t
computeCoincideEdges(const std::vector<size_t> &numberOfNodesPerSlot) const;
size_t computeNumberOfPossibleEdges(
const std::vector<size_t> &numberOfNodesPerSlot) const;
void createLabelMesh(const std::vector<vcg::Point3d> vertices,
const std::filesystem::path &savePath) const;
size_t getEdgeIndex(size_t ni0, size_t ni1) const;
public:
TopologyEnumerator();
void
computeValidPatterns(const std::vector<size_t> &reducedNumberOfNodesPerSlot);
private:
std::vector<size_t>
getCoincideEdges(const std::vector<size_t> &edgeNodeIndices) const;
bool isValidPattern(const std::string &patternIndex,
const std::vector<vcg::Point2i> &validEdges,
const size_t &numberOfDesiredEdges) const;
std::vector<vcg::Point2i>
getValidEdges(const std::vector<size_t> &numberOfNodesPerSlot,
const std::filesystem::__cxx11::path &resultsPath,
const FlatPatternGeometry &patternGeometryAllEdges,
const std::vector<vcg::Point2i> &allPossibleEdges);
std::unordered_set<size_t> computeDuplicateEdges();
std::unordered_set<size_t>
computeCoincideEdges(const std::vector<size_t> &numberOfNodesPerSlot);
void computeEdgeNodes(const std::vector<size_t> &numberOfNodesPerSlot,
std::vector<size_t> &nodesEdge0,
std::vector<size_t> &nodesEdge1,
std::vector<size_t> &nodesEdge2);
std::unordered_set<size_t>
computeDuplicateEdges(const std::vector<size_t> &numberOfNodesPerSlot);
void computeValidPatterns(
const std::vector<size_t> &numberOfNodesPerSlot,
const size_t &numberOfDesiredEdges,
const std::filesystem::path &resultsPath,
const std::vector<vcg::Point3d> &vertices,
const std::unordered_map<size_t, std::unordered_set<size_t>>
&intersectingEdges,
const std::vector<vcg::Point2i> &validEdges, PatternSet &patternsSet);
};
#endif // TOPOLOGYENUMERATOR_HPP

560
trianglepatterngeometry.cpp Normal file
View File

@ -0,0 +1,560 @@
#include "trianglepatterngeometry.hpp"
#include "trianglepattterntopology.hpp"
#include <algorithm>
#include <iterator>
#include <numeric>
#include <vcg/complex/algorithms/update/position.h>
#include <vcg/simplex/edge/topology.h>
#include <vcg/space/intersection2.h>
#include <wrap/io_trimesh/export.h>
size_t FlatPatternGeometry::computeTiledValence(
const size_t &nodeIndex,
const std::vector<size_t> &numberOfNodesPerSlot) const {
std::vector<FlatPatternGeometry::EdgeType *> connectedEdges;
vcg::edge::VEStarVE(&vert[nodeIndex], connectedEdges);
const size_t nodeValence = connectedEdges.size();
assert(nodeSlot.count(nodeIndex) != 0);
const size_t nodeSlotIndex = nodeSlot.at(nodeIndex);
if (nodeSlotIndex == 0) {
return nodeValence * fanSize;
} else if (nodeSlotIndex == 1 || nodeSlotIndex == 2) {
size_t correspondingNodeIndex;
if (nodeSlotIndex == 1) {
correspondingNodeIndex = nodeIndex + 1;
} else {
correspondingNodeIndex = nodeIndex - 1;
}
std::vector<FlatPatternGeometry::EdgeType *>
connectedEdgesCorrespondingNode;
vcg::edge::VEStarVE(&vert[correspondingNodeIndex],
connectedEdgesCorrespondingNode);
size_t correspondingNodeValence = connectedEdgesCorrespondingNode.size();
return fanSize / 2 * nodeValence + fanSize / 2 * correspondingNodeValence;
} else if (nodeSlotIndex == 3 || nodeSlotIndex == 5) {
size_t correspondingNodeIndex;
size_t numberOfNodesBefore;
size_t numberOfNodesAfter;
if (nodeSlotIndex == 3) {
numberOfNodesBefore =
nodeIndex - std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 3, 0);
correspondingNodeIndex =
std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 6, 0) -
1 - numberOfNodesBefore;
} else {
numberOfNodesAfter =
std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 6, 0) -
1 - nodeIndex;
correspondingNodeIndex =
numberOfNodesAfter + std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 3,
0);
}
assert(correspondingNodeIndex < vn);
std::vector<FlatPatternGeometry::EdgeType *>
connectedEdgesCorrespondingNode;
vcg::edge::VEStarVE(&vert[correspondingNodeIndex],
connectedEdgesCorrespondingNode);
size_t correspondingNodeValence = connectedEdgesCorrespondingNode.size();
return nodeValence + correspondingNodeValence;
} else if (nodeSlotIndex == 4) {
return 2 * nodeValence;
} else if (nodeSlotIndex == 6) {
return nodeValence;
} else {
std::cerr << "Error in slot index computation" << std::endl;
}
assert(false);
return 0;
}
size_t FlatPatternGeometry::getFanSize() const { return fanSize; }
double FlatPatternGeometry::getTriangleEdgeSize() const {
return triangleEdgeSize;
}
FlatPatternGeometry::FlatPatternGeometry() {}
std::vector<vcg::Point3d> FlatPatternGeometry::getVertices() const {}
FlatPatternGeometry
FlatPatternGeometry::createTile(FlatPatternGeometry &pattern) {
const size_t fanSize = FlatPatternGeometry().getFanSize();
FlatPatternGeometry fan(createFan(pattern));
FlatPatternGeometry tile(fan);
if (fanSize % 2 == 1) {
vcg::Matrix44d R;
auto rotationAxis = vcg::Point3d(0, 0, 1);
R.SetRotateDeg(180, rotationAxis);
vcg::tri::UpdatePosition<FlatPatternGeometry>::Matrix(fan, R);
}
vcg::Matrix44d T;
const double centerAngle = 2 * M_PI / fanSize;
const double triangleHeight = std::sin((M_PI - centerAngle) / 2) *
FlatPatternGeometry().triangleEdgeSize;
T.SetTranslate(0, -2 * triangleHeight, 0);
vcg::tri::UpdatePosition<FlatPatternGeometry>::Matrix(fan, T);
FlatPatternGeometry fanOfFan = createFan(fan);
vcg::tri::Append<FlatPatternGeometry, FlatPatternGeometry>::Mesh(tile,
fanOfFan);
vcg::tri::Clean<FlatPatternGeometry>::MergeCloseVertex(tile, 0.0000005);
vcg::tri::Allocator<FlatPatternGeometry>::CompactEveryVector(tile);
vcg::tri::UpdateTopology<FlatPatternGeometry>::VertexEdge(tile);
vcg::tri::UpdateTopology<FlatPatternGeometry>::EdgeEdge(tile);
for (size_t vi = 0; vi < pattern.vn; vi++) {
tile.vert[vi].C() = vcg::Color4b::Blue;
}
return tile;
}
FlatPatternGeometry
FlatPatternGeometry::createFan(FlatPatternGeometry &pattern) {
const size_t fanSize = FlatPatternGeometry().getFanSize();
FlatPatternGeometry fan(pattern);
FlatPatternGeometry rotatedPattern(pattern);
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<FlatPatternGeometry>::Matrix(rotatedPattern, R);
vcg::tri::Append<FlatPatternGeometry, FlatPatternGeometry>::Mesh(
fan, rotatedPattern);
}
return fan;
}
FlatPatternGeometry::FlatPatternGeometry(FlatPatternGeometry &other) {
vcg::tri::Append<FlatPatternGeometry, FlatPatternGeometry>::MeshCopy(*this,
other);
this->vertices = other.getVertices();
vcg::tri::UpdateTopology<FlatPatternGeometry>::VertexEdge(*this);
vcg::tri::UpdateTopology<FlatPatternGeometry>::EdgeEdge(*this);
}
bool FlatPatternGeometry::savePly(const std::string plyFilename) {
int returnValue = vcg::tri::io::ExporterPLY<FlatPatternGeometry>::Save(
*this, plyFilename.c_str(),
vcg::tri::io::Mask::IOM_EDGEINDEX | vcg::tri::io::Mask::IOM_VERTCOLOR,
false);
if (returnValue != 0) {
std::cerr << vcg::tri::io::ExporterPLY<FlatPatternGeometry>::ErrorMsg(
returnValue)
<< std::endl;
}
return static_cast<bool>(returnValue);
}
void FlatPatternGeometry::add(const std::vector<vcg::Point3d> &vertices) {
this->vertices = vertices;
std::for_each(vertices.begin(), vertices.end(), [&](const vcg::Point3d &p) {
vcg::tri::Allocator<FlatPatternGeometry>::AddVertex(*this, p);
});
vcg::tri::UpdateTopology<FlatPatternGeometry>::VertexEdge(*this);
vcg::tri::UpdateTopology<FlatPatternGeometry>::EdgeEdge(*this);
}
void FlatPatternGeometry::add(const std::vector<vcg::Point2i> &edges) {
std::for_each(edges.begin(), edges.end(), [&](const vcg::Point2i &e) {
vcg::tri::Allocator<FlatPatternGeometry>::AddEdge(*this, e[0], e[1]);
});
vcg::tri::UpdateTopology<FlatPatternGeometry>::VertexEdge(*this);
vcg::tri::UpdateTopology<FlatPatternGeometry>::EdgeEdge(*this);
}
void FlatPatternGeometry::add(const std::vector<vcg::Point3d> &vertices,
const std::vector<vcg::Point2i> &edges) {
add(vertices);
add(edges);
}
void FlatPatternGeometry::add(const std::vector<size_t> &numberOfNodesPerSlot,
const std::vector<vcg::Point2i> &edges) {
assert(numberOfNodesPerSlot.size() == 7);
auto vertices =
constructVertexVector(numberOfNodesPerSlot, fanSize, triangleEdgeSize);
add(vertices, edges);
vcg::tri::UpdateTopology<FlatPatternGeometry>::VertexEdge(*this);
vcg::tri::UpdateTopology<FlatPatternGeometry>::EdgeEdge(*this);
updateEigenEdgeAndVertices();
}
std::vector<vcg::Point3d> FlatPatternGeometry::constructVertexVector(
const std::vector<size_t> &numberOfNodesPerSlot, const size_t &fanSize,
const double &triangleEdgeSize) {
std::vector<vcg::Point3d> vertices;
const double centerAngle = 2 * M_PI / fanSize;
const double triangleHeight =
std::sin((M_PI - centerAngle) / 2) * triangleEdgeSize;
const double baseEdgeSize =
2 * triangleEdgeSize * std::cos((M_PI - centerAngle) / 2);
const vcg::Point3d triangleV0 = vcg::Point3d(0, 0, 0);
const vcg::Point3d triangleV1 =
vcg::Point3d(-baseEdgeSize / 2, -triangleHeight, 0);
const vcg::Point3d triangleV2 =
vcg::Point3d(baseEdgeSize / 2, -triangleHeight, 0);
// Nodes
if (numberOfNodesPerSlot[0] == 1) {
// vertices[0] = triangleV0;
vertices.push_back(triangleV0);
}
if (numberOfNodesPerSlot[1] == 1) {
// vertices[1] = triangleV1;
vertices.push_back(triangleV1);
}
if (numberOfNodesPerSlot[2] == 1) {
// vertices[2] = triangleV2;
vertices.push_back(triangleV2);
}
// Edges
if (numberOfNodesPerSlot[3] != 0) {
const double offset0 = 1.0 / (numberOfNodesPerSlot[3] + 1);
const vcg::Point3d edgeVector0(triangleV1 - triangleV0);
for (size_t vertexIndex = 0; vertexIndex < numberOfNodesPerSlot[3];
vertexIndex++) {
// vertices[std::accumulate(numberOfNodesPerSlot.begin(),
// numberOfNodesPerSlot.begin() + 2, 0) +
// vertexIndex] =
vertices.push_back(triangleV0 +
edgeVector0.operator*((vertexIndex + 1) * offset0));
}
}
if (numberOfNodesPerSlot[4] != 0) {
const double offset1 = 1.0 / (numberOfNodesPerSlot[4] + 1);
const vcg::Point3d edgeVector1(triangleV2 - triangleV1);
for (size_t vertexIndex = 0; vertexIndex < numberOfNodesPerSlot[4];
vertexIndex++) {
// vertices[std::accumulate(numberOfNodesPerSlot.begin(),
// numberOfNodesPerSlot.begin() + 3, 0) +
// vertexIndex] =
vertices.push_back(triangleV1 +
edgeVector1.operator*((vertexIndex + 1) * offset1));
}
}
if (numberOfNodesPerSlot[5] != 0) {
const double offset2 = 1.0 / (numberOfNodesPerSlot[5] + 1);
const vcg::Point3d edgeVector2(triangleV0 - triangleV2);
for (size_t vertexIndex = 0; vertexIndex < numberOfNodesPerSlot[5];
vertexIndex++) {
// vertices[std::accumulate(numberOfNodesPerSlot.begin(),
// numberOfNodesPerSlot.begin() + 4, 0) +
// vertexIndex] =
vertices.push_back(triangleV2 +
edgeVector2.operator*((vertexIndex + 1) * offset2));
}
}
// Face
if (numberOfNodesPerSlot[6] != 0) {
const vcg::Point3d triangleCenter((triangleV0 + triangleV1 + triangleV2) /
3);
const double radius = 0.1;
const double offsetRad = 2 * M_PI / numberOfNodesPerSlot[6];
for (size_t vertexIndex = 0; vertexIndex < numberOfNodesPerSlot[6];
vertexIndex++) {
const double pX =
triangleCenter[0] + radius * std::cos(vertexIndex * offsetRad);
const double pY =
triangleCenter[1] + radius * std::sin(vertexIndex * offsetRad);
/*vertices[std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 5, 0) +
vertexIndex] =*/
vertices.push_back(vcg::Point3d(pX, pY, 0));
}
}
return vertices;
}
void FlatPatternGeometry::constructNodeToTiledValenceMap(
const std::vector<size_t> &numberOfNodesPerSlot) {
for (size_t nodeIndex = 0; nodeIndex < vn; nodeIndex++) {
const size_t tiledValence =
computeTiledValence(nodeIndex, numberOfNodesPerSlot);
nodeTiledValence[nodeIndex] = tiledValence;
}
}
bool FlatPatternGeometry::hasDanglingEdges(
const std::vector<size_t> &numberOfNodesPerSlot) {
if (nodeSlot.empty()) {
FlatPatternTopology::constructNodeToSlotMap(numberOfNodesPerSlot, nodeSlot);
}
if (correspondingNode.empty()) {
constructCorresponginNodeMap(numberOfNodesPerSlot);
}
if (nodeTiledValence.empty()) {
constructNodeToTiledValenceMap(numberOfNodesPerSlot);
}
bool hasDanglingEdges = false;
for (size_t nodeIndex = 0; nodeIndex < vn; nodeIndex++) {
const size_t tiledValence = nodeTiledValence[nodeIndex];
if (tiledValence == 1) {
vert[nodeIndex].C() = vcg::Color4b::Red;
hasDanglingEdges = true;
}
}
return hasDanglingEdges;
}
bool FlatPatternGeometry::hasUntiledDanglingEdges() {
// vcg::tri::Clean<TrianglePatternGeometry>::MergeCloseVertex(*this,
// 0.0000005);
// vcg::tri::Allocator<TrianglePatternGeometry>::CompactEveryVector(*this);
// vcg::tri::UpdateTopology<TrianglePatternGeometry>::VertexEdge(*this);
// vcg::tri::UpdateTopology<TrianglePatternGeometry>::EdgeEdge(*this);
bool hasDanglingEdges = false;
for (size_t vi = 0; vi < vn; vi++) {
std::vector<FlatPatternGeometry::EdgeType *> connectedEdges;
vcg::edge::VEStarVE(&vert[vi], connectedEdges);
const size_t nodeValence = connectedEdges.size();
if (nodeValence == 1) {
if (vert[vi].C().operator==(vcg::Color4b(vcg::Color4b::Red))) {
} else {
vert[vi].C() = vcg::Color4b::Blue;
}
hasDanglingEdges = true;
}
}
return hasDanglingEdges;
}
// TODO: The function expects that the numberOfNodesPerSlot follows a specific
// format and that the vertex container was populated in a particular order.
void FlatPatternGeometry::constructCorresponginNodeMap(
const std::vector<size_t> &numberOfNodesPerSlot) {
assert(vn != 0 && !nodeSlot.empty() && correspondingNode.empty() &&
numberOfNodesPerSlot.size() == 7);
for (size_t nodeIndex = 0; nodeIndex < vn; nodeIndex++) {
const size_t slotIndex = nodeSlot[nodeIndex];
if (slotIndex == 1) {
correspondingNode[nodeIndex] = nodeIndex + 1;
} else if (slotIndex == 2) {
correspondingNode[nodeIndex] = nodeIndex - 1;
} else if (slotIndex == 3) {
const size_t numberOfNodesBefore =
nodeIndex - std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 3, 0);
correspondingNode[nodeIndex] =
std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 6, 0) -
1 - numberOfNodesBefore;
} else if (slotIndex == 5) {
const size_t numberOfNodesAfter =
std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 6, 0) -
1 - nodeIndex;
correspondingNode[nodeIndex] =
numberOfNodesAfter + std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 3,
0);
}
}
}
bool FlatPatternGeometry::isFullyConnectedWhenTiled() {
assert(vn != 0 /* && !correspondingNode.empty()*/);
// TrianglePatternGeometry copyOfPattern(*this);
// If bottom interface nodes have a valence of zero there definetely more than
// a single cc
bool bottomInterfaceIsConnected = false;
for (size_t nodeIndex = 0; nodeIndex < vn; nodeIndex++) {
if (nodeSlot[nodeIndex] == 1 || nodeSlot[nodeIndex] == 4 ||
nodeSlot[nodeIndex] == 2) {
std::vector<FlatPatternGeometry::EdgeType *> connectedEdges;
vcg::edge::VEStarVE(&vert[nodeIndex], connectedEdges);
const size_t nodeValence = connectedEdges.size();
if (nodeValence != 0) {
bottomInterfaceIsConnected = true;
break;
}
}
}
if (!bottomInterfaceIsConnected) {
return false;
}
FlatPatternGeometry fanedPattern = createFan(*this);
vcg::tri::Clean<FlatPatternGeometry>::MergeCloseVertex(fanedPattern,
0.000000005);
vcg::tri::Allocator<FlatPatternGeometry>::CompactEveryVector(fanedPattern);
vcg::tri::UpdateTopology<FlatPatternGeometry>::VertexEdge(fanedPattern);
vcg::tri::UpdateTopology<FlatPatternGeometry>::EdgeEdge(fanedPattern);
std::vector<std::pair<int, FlatPatternGeometry::EdgePointer>> eCC;
vcg::tri::Clean<FlatPatternGeometry>::edgeMeshConnectedComponents(
fanedPattern, eCC);
const bool sideInterfaceIsConnected = 1 == eCC.size();
if (!sideInterfaceIsConnected) {
return false;
}
// for (size_t nodeIndex = 0; nodeIndex < vn; nodeIndex++) {
// if (nodeTiledValence[nodeIndex] == 0)
// continue;
// const size_t slotIndex = nodeSlot[nodeIndex];
// // connect nodes belonging to first or third slot(nodes on the first
// // triangle edge)
// // if (nodeTiledValence[nodeIndex] == 0 && slotIndex == 3) {
// // continue;
// // }
// if (slotIndex == 1 || slotIndex == 3) {
// assert(correspondingNode.count(nodeIndex) != 0);
// const size_t correspondingNodeIndex =
// correspondingNode[nodeIndex];
// std::vector<TrianglePatternGeometry::EdgeType *> starEdges;
// vcg::edge::VEStarVE(&vert[nodeIndex], starEdges);
// bool containsEdge = false;
// for (TrianglePatternGeometry::EdgeType *e : starEdges) {
// assert(vcg::tri::Index(*this, e->V(0)) == nodeIndex ||
// vcg::tri::Index(*this, e->V(1)) == nodeIndex);
// if (vcg::tri::Index(*this, e->V(0)) == nodeIndex) {
// if (vcg::tri::Index(*this, e->V(1)) == correspondingNodeIndex) {
// containsEdge = true;
// break;
// }
// } else if (vcg::tri::Index(*this, e->V(1)) == nodeIndex) {
// if (vcg::tri::Index(*this, e->V(0)) == correspondingNodeIndex) {
// containsEdge = true;
// break;
// }
// }
// }
// if (!containsEdge) {
// vcg::tri::Allocator<TrianglePatternGeometry>::AddEdge(
// copyOfPattern, nodeIndex, correspondingNodeIndex);
// }
// } else if (slotIndex == 2 || slotIndex == 5) {
// assert(correspondingNode.count(nodeIndex) != 0);
// } else {
// assert(correspondingNode.count(nodeIndex) == 0);
// }
// }
// std::vector<std::pair<int, TrianglePatternGeometry::EdgePointer>> eCC;
// vcg::tri::Clean<TrianglePatternGeometry>::edgeMeshConnectedComponents(
// copyOfPattern, eCC);
// size_t numberOfCC_edgeBased = eCC.size();
// size_t numberOfCC_vertexBased = numberOfCC_edgeBased;
// if (numberOfCC_edgeBased == 1) {
// vcg::tri::UpdateTopology<TrianglePatternGeometry>::VertexEdge(
// copyOfPattern);
// vcg::tri::UpdateTopology<TrianglePatternGeometry>::EdgeEdge(copyOfPattern);
// vcg::tri::UpdateFlags<TrianglePatternGeometry>::VertexSetV(copyOfPattern);
// vcg::tri::UpdateFlags<TrianglePatternGeometry>::VertexClear(copyOfPattern);
// for (size_t ei = 0; ei < copyOfPattern.EN(); ei++) {
// copyOfPattern.edge[ei].V(0)->SetV();
// copyOfPattern.edge[ei].V(1)->SetV();
// }
// for (size_t vi = 0; vi < copyOfPattern.VN(); vi++) {
// if (!copyOfPattern.vert[vi].IsV()) {
// numberOfCC_vertexBased++;
// }
// }
// return numberOfCC_vertexBased;
// }
// return numberOfCC_edgeBased == 1; // TODO: not good
return true;
}
bool FlatPatternGeometry::hasIntersectingEdges(
const std::string &patternBinaryRepresentation,
const std::unordered_map<size_t, std::unordered_set<size_t>>
&intersectingEdges) {
bool containsIntersectingEdges = false;
for (size_t validEdgeIndex = 0;
validEdgeIndex < patternBinaryRepresentation.size(); validEdgeIndex++) {
if (patternBinaryRepresentation[validEdgeIndex] == '1' &&
intersectingEdges.count(validEdgeIndex) != 0) {
for (auto edgeIndexIt =
intersectingEdges.find(validEdgeIndex)->second.begin();
edgeIndexIt != intersectingEdges.find(validEdgeIndex)->second.end();
edgeIndexIt++) {
if (patternBinaryRepresentation[*edgeIndexIt] == '1') {
containsIntersectingEdges = true;
// statistics.numberOfIntersectingEdgesOverAllPatterns++;
break; // should be uncommented in order to improve
// performance
}
}
if (containsIntersectingEdges) {
break; // should be uncommented in order to improve performance
}
}
}
return containsIntersectingEdges;
}
std::unordered_map<size_t, std::unordered_set<size_t>>
FlatPatternGeometry::getIntersectingEdges(
size_t &numberOfIntersectingEdgePairs) const {
std::unordered_map<size_t, std::unordered_set<size_t>> intersectingEdges;
numberOfIntersectingEdgePairs = 0;
size_t numberOfEdgePairs;
if (en == 0 || en == 1) {
numberOfEdgePairs = 0;
} else {
numberOfEdgePairs = binomialCoefficient(en, 2);
}
for (size_t edgePairIndex = 0; edgePairIndex < numberOfEdgePairs;
edgePairIndex++) {
size_t ei0 =
en - 2 -
floor(sqrt(-8 * edgePairIndex + 4 * en * (en - 1) - 7) / 2.0 - 0.5);
size_t ei1 = edgePairIndex + ei0 + 1 - en * (en - 1) / 2 +
(en - ei0) * ((en - ei0) - 1) / 2;
const vcg::Point2d p0(edge[ei0].cP(0)[0], edge[ei0].cP(0)[1]);
const float epsilon = 0.000005;
vcg::Box2d bb0(p0 - vcg::Point2d(epsilon, epsilon),
p0 + vcg::Point2d(epsilon, epsilon));
const vcg::Point2d p1(edge[ei0].cP(1)[0], edge[ei0].cP(1)[1]);
vcg::Box2d bb1(p1 - vcg::Point2d(epsilon, epsilon),
p1 + vcg::Point2d(epsilon, epsilon));
const vcg::Point2d p2(edge[ei1].cP(0)[0], edge[ei1].cP(0)[1]);
vcg::Box2d bb2(p2 - vcg::Point2d(epsilon, epsilon),
p2 + vcg::Point2d(epsilon, epsilon));
if (bb2.Collide(bb1) || bb2.Collide(bb0))
continue;
const vcg::Point2d p3(edge[ei1].cP(1)[0], edge[ei1].cP(1)[1]);
vcg::Box2d bb3(p3 - vcg::Point2d(epsilon, epsilon),
p3 + vcg::Point2d(epsilon, epsilon));
if (bb3.Collide(bb1) || bb3.Collide(bb0))
continue;
const vcg::Segment2d s0(p0, p1);
const vcg::Segment2d s1(p2, p3);
vcg::Point2d intersectionPoint;
const bool edgesIntersect =
vcg::SegmentSegmentIntersection(s0, s1, intersectionPoint);
if (edgesIntersect) {
numberOfIntersectingEdgePairs++;
intersectingEdges[ei0].insert(ei1);
intersectingEdges[ei1].insert(ei0);
}
}
return intersectingEdges;
}

View File

@ -0,0 +1,68 @@
#ifndef FLATPATTERNGEOMETRY_HPP
#define FLATPATTERNGEOMETRY_HPP
#include "edgemesh.hpp"
#include <string>
#include <unordered_map>
#include <unordered_set>
class FlatPatternGeometry : public VCGEdgeMesh {
private:
size_t
computeTiledValence(const size_t &nodeIndex,
const std::vector<size_t> &numberOfNodesPerSlot) const;
size_t getNodeValence(const size_t &nodeIndex) const;
size_t getNodeSlot(const size_t &nodeIndex) const;
const size_t fanSize{6};
std::vector<VCGEdgeMesh::CoordType> vertices;
const double triangleEdgeSize{1}; // radius edge
std::unordered_map<size_t, size_t> nodeSlot;
std::unordered_map<size_t, size_t> correspondingNode;
std::unordered_map<size_t, size_t> nodeTiledValence;
void
constructCorresponginNodeMap(const std::vector<size_t> &numberOfNodesPerSlot);
void constructNodeToTiledValenceMap(
const std::vector<size_t> &numberOfNodesPerSlot);
public:
FlatPatternGeometry();
/*The following function should be a copy constructor with
* a const argument but this is impossible due to the
* vcglib interface.
* */
FlatPatternGeometry(FlatPatternGeometry &other);
bool savePly(const std::string plyFilename);
void add(const std::vector<vcg::Point3d> &vertices);
void add(const std::vector<vcg::Point2i> &edges);
void add(const std::vector<vcg::Point3d> &vertices,
const std::vector<vcg::Point2i> &edges);
void add(const std::vector<size_t> &numberOfNodesPerSlot,
const std::vector<vcg::Point2i> &edges);
static std::vector<vcg::Point3d>
constructVertexVector(const std::vector<size_t> &numberOfNodesPerSlot,
const size_t &fanSize, const double &triangleEdgeSize);
bool hasDanglingEdges(const std::vector<size_t> &numberOfNodesPerSlot);
std::vector<vcg::Point3d> getVertices() const;
static FlatPatternGeometry createFan(FlatPatternGeometry &pattern);
static FlatPatternGeometry createTile(FlatPatternGeometry &pattern);
double getTriangleEdgeSize() const;
bool hasUntiledDanglingEdges();
std::unordered_map<size_t, std::unordered_set<size_t>>
getIntersectingEdges(size_t &numberOfIntersectingEdgePairs) const;
static size_t binomialCoefficient(size_t n, size_t m) {
assert(n >= m);
return tgamma(n + 1) / (tgamma(m + 1) * tgamma(n - m + 1));
}
bool isFullyConnectedWhenTiled();
bool hasIntersectingEdges(
const std::string &patternBinaryRepresentation,
const std::unordered_map<size_t, std::unordered_set<size_t>>
&intersectingEdges);
bool isPatternValid();
size_t getFanSize() const;
};
#endif // FLATPATTERNGEOMETRY_HPP

View File

@ -0,0 +1,298 @@
#include "trianglepattterntopology.hpp"
#include <boost/graph/biconnected_components.hpp>
#include <boost/graph/connected_components.hpp>
#include <numeric>
FlatPatternTopology::FlatPatternTopology() {}
FlatPatternTopology::FlatPatternTopology(
const std::vector<size_t> &numberOfNodesPerSlot,
const std::vector<vcg::Point2i> &edges)
: numberOfNodesPerSlot(numberOfNodesPerSlot) {
pattern = BoostGraph(std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.end(), 0));
for (const vcg::Point2i &e : edges) {
boost::add_edge(e[0], e[1], pattern);
}
constructNodeToSlotMap();
constructSlotToNodeMap();
constructCorresponginNodeMap();
}
bool FlatPatternTopology::containsArticulationPoints() const {
assert(numberOfNodesPerSlot.size() == 7 && numberOfNodesPerSlot[4] < 2 &&
!nodeToSlot.empty() && !correspondingNode.empty());
BoostGraph copyOfPattern(pattern);
// std::cout << std::endl;
std::vector<int> componentsBefore(boost::num_vertices(copyOfPattern));
size_t num_components =
boost::connected_components(copyOfPattern, &componentsBefore[0]);
// std::cout << "Number of cc before:" << num_components << std::endl;
// printGraph(copyOfPattern);
copyOfPattern = constructRotationallySymmetricPattern(
copyOfPattern, slotToNode, nodeToSlot, correspondingNode);
// // Remove edges connected to the bottom edge node
// assert(slotToNode.find(4) != slotToNode.end());
// std::unordered_set<size_t> bottomEdgeNodeSet =
// (slotToNode.find(4))->second;
// size_t bottomEdgeNodeIndex = *bottomEdgeNodeSet.begin();
// boost::clear_vertex(bottomEdgeNodeIndex, copyOfPattern);
std::vector<int> componentsAfter(boost::num_vertices(copyOfPattern));
num_components =
boost::connected_components(copyOfPattern, &componentsAfter[0]);
// std::cout << "Number of cc after:" << num_components << std::endl;
// printGraph(copyOfPattern);
// Compute articulation points on the edited graph
std::vector<vertex_t> articulationPoints;
boost::articulation_points(copyOfPattern,
std::back_inserter(articulationPoints));
// std::cout << "Found " << articulationPoints.size()
// << " articulation points.\n";
// size_t numberOfNonValidArticulationPoints = 0;
for (std::size_t i = 0; i < articulationPoints.size(); ++i) {
// std::cout << articulationPoints[i] << std::endl;
// if (boost::out_degree(articulationPoints[i], copyOfPattern) < 3) {
// numberOfNonValidArticulationPoints++;
// }
}
// if (numberOfNonValidArticulationPoints == articulationPoints.size()) {
// return false;
// }
return !articulationPoints.empty();
}
void FlatPatternTopology::constructNodeToSlotMap(
const std::vector<size_t> &numberOfNodesPerSlot,
std::unordered_map<size_t, size_t> &nodeToSlot) {
const size_t numberOfNodes = std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.end(), 0);
assert(numberOfNodes != 0 && nodeToSlot.empty() &&
numberOfNodesPerSlot.size() == 7);
std::vector<size_t> maxNodeIndexPerSlot(numberOfNodesPerSlot.size());
for (size_t i = 0; i < maxNodeIndexPerSlot.size(); ++i) {
maxNodeIndexPerSlot[i] = std::accumulate(
numberOfNodesPerSlot.begin(), numberOfNodesPerSlot.begin() + i + 1, 0);
}
for (size_t nodeIndex = 0; nodeIndex < numberOfNodes; nodeIndex++) {
const size_t slotIndex =
std::distance(maxNodeIndexPerSlot.begin(),
std::upper_bound(maxNodeIndexPerSlot.begin(),
maxNodeIndexPerSlot.end(), nodeIndex));
nodeToSlot[nodeIndex] = slotIndex;
}
}
void FlatPatternTopology::constructNodeToSlotMap() {
constructNodeToSlotMap(numberOfNodesPerSlot, nodeToSlot);
}
void FlatPatternTopology::constructSlotToNodeMap() {
constructSlotToNodeMap(nodeToSlot, slotToNode);
}
void FlatPatternTopology::constructSlotToNodeMap(
const std::unordered_map<size_t, size_t> &nodeToSlot,
std::unordered_map<size_t, std::unordered_set<size_t>> &slotToNode) {
assert(!nodeToSlot.empty());
for (size_t nodeIndex = 0; nodeIndex < nodeToSlot.size(); nodeIndex++) {
slotToNode[nodeToSlot.at(nodeIndex)].insert(nodeIndex);
}
}
// TODO: The function expects that the numberOfNodesPerSlot follows a
// specific format and that the vertex container was populated in a
// particular order.
void FlatPatternTopology::constructCorresponginNodeMap() {
assert(!nodeToSlot.empty() && correspondingNode.empty() &&
numberOfNodesPerSlot.size() == 7);
for (size_t nodeIndex = 0; nodeIndex < boost::num_vertices(pattern);
nodeIndex++) {
const size_t slotIndex = nodeToSlot[nodeIndex];
if (slotIndex == 1) {
correspondingNode[nodeIndex] = nodeIndex + 1;
} else if (slotIndex == 2) {
correspondingNode[nodeIndex] = nodeIndex - 1;
} else if (slotIndex == 3) {
const size_t numberOfNodesBefore =
nodeIndex - std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 3, 0);
correspondingNode[nodeIndex] =
std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 6, 0) -
1 - numberOfNodesBefore;
} else if (slotIndex == 5) {
const size_t numberOfNodesAfter =
std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 6, 0) -
1 - nodeIndex;
correspondingNode[nodeIndex] =
numberOfNodesAfter + std::accumulate(numberOfNodesPerSlot.begin(),
numberOfNodesPerSlot.begin() + 3,
0);
}
}
}
/*
* In this function I create an "extended" pattern of the one in the base
* triangle by:
* 1)copying the edges from left edge to the right edge and vice versa
* 2)I label all nodes that lay on the boarder of the base triangle as
* "external" and add edges connecting them to each other (this is wrong in the
* case in which all "external" nodes are connected via a single node!))
* */
BoostGraph FlatPatternTopology::constructRotationallySymmetricPattern(
const BoostGraph &pattern,
const std::unordered_map<size_t, std::unordered_set<size_t>> &slotToNodes,
const std::unordered_map<size_t, size_t> &nodeToSlot,
const std::unordered_map<size_t, size_t> &correspondingNode) {
BoostGraph rotationallySymmetricPattern(pattern);
boost::graph_traits<BoostGraph>::out_edge_iterator ei, ei_end;
// Copy edges that lay on the left edge to the right edge
const auto slot3NodesPairIt = slotToNodes.find(3);
if (slot3NodesPairIt != slotToNodes.end()) {
for (const size_t &nodeIndex : slot3NodesPairIt->second) {
for (boost::tie(ei, ei_end) = boost::out_edges(nodeIndex, pattern);
ei != ei_end; ++ei) {
auto vt = boost::target(*ei, pattern);
const auto vtNodeSlotPairIt = nodeToSlot.find(vt);
assert(vtNodeSlotPairIt != nodeToSlot.end());
const size_t vtSlot = vtNodeSlotPairIt->second;
if (vtSlot == 3 || vtSlot == 1 || vtSlot == 0) {
// Connect the corresponding nodes on the opposite edge
auto correspondingNodeIndexIt = correspondingNode.find(nodeIndex);
assert(correspondingNodeIndexIt != correspondingNode.end());
auto correspondingVtIt = correspondingNode.find(vt);
assert(correspondingVtIt != correspondingNode.end() || vtSlot == 0);
const size_t &correspondingNodeIndex =
correspondingNodeIndexIt->second;
size_t correspondingVt = 0;
if (correspondingVtIt != correspondingNode.end()) {
correspondingVt = correspondingVtIt->second;
}
boost::add_edge(correspondingNodeIndex, correspondingVt,
rotationallySymmetricPattern);
}
}
}
}
// Copy edges that lay on the right edge to the left edge
const auto slot5NodesPairIt = slotToNodes.find(5);
if (slot5NodesPairIt != slotToNodes.end()) {
for (const size_t &nodeIndex : slot5NodesPairIt->second) {
for (boost::tie(ei, ei_end) = boost::out_edges(nodeIndex, pattern);
ei != ei_end; ++ei) {
auto vt = boost::target(*ei, pattern);
const auto vtNodeSlotPairIt = nodeToSlot.find(vt);
assert(vtNodeSlotPairIt != nodeToSlot.end());
const size_t vtSlot = vtNodeSlotPairIt->second;
if (vtSlot == 5 || vtSlot == 2 || vtSlot == 0) {
// Connect the corresponding nodes on the opposite edge
auto correspondingNodeIndexIt = correspondingNode.find(nodeIndex);
assert(correspondingNodeIndexIt != correspondingNode.end());
auto correspondingVtIt = correspondingNode.find(vt);
assert(correspondingVtIt != correspondingNode.end() || vtSlot == 0);
const size_t &correspondingNodeIndex =
correspondingNodeIndexIt->second;
size_t correspondingVt = 0;
if (correspondingVtIt != correspondingNode.end()) {
correspondingVt = correspondingVtIt->second;
}
boost::add_edge(correspondingNodeIndex, correspondingVt,
rotationallySymmetricPattern);
}
}
}
}
// NOTE: The problem with that approach is that I connect !all! "external"
// nodes with each other, which might not be entirely true. If the number of
// cc of the tilled configuration is not 1 this might not label patterns as
// having an articulation node although they might have one. Create set of
// nodes connected with "external" edges
std::unordered_set<size_t> externallyConnectedNodes;
// Mark the star node as external
const auto slot0NodesPairIt = slotToNodes.find(0);
if (slot0NodesPairIt != slotToNodes.end()) {
externallyConnectedNodes.insert(slot0NodesPairIt->second.begin(),
slot0NodesPairIt->second.end());
}
// Mark all bottom nodes as external since they are allways connected to the
// south-neighbouring pattern
const auto slot4NodesPairIt = slotToNodes.find(4);
if (slot4NodesPairIt != slotToNodes.end()) {
externallyConnectedNodes.insert(slot4NodesPairIt->second.begin(),
slot4NodesPairIt->second.end());
}
// Add all slot3 nodes that have a connection to the "inside"
if (slot3NodesPairIt != slotToNodes.end()) {
for (const size_t &nodeIndex : slot3NodesPairIt->second) {
for (boost::tie(ei, ei_end) = boost::out_edges(nodeIndex, pattern);
ei != ei_end; ++ei) {
auto vt = boost::target(*ei, pattern);
const auto vtNodeSlotPairIt = nodeToSlot.find(vt);
assert(vtNodeSlotPairIt != nodeToSlot.end());
const size_t vtSlot = vtNodeSlotPairIt->second;
if (vtSlot != 3) {
auto correspondingNodePairIt = correspondingNode.find(nodeIndex);
assert(correspondingNodePairIt != correspondingNode.end());
externallyConnectedNodes.insert(correspondingNodePairIt->second);
}
}
}
}
// Add all slot5 nodes that have a connection to the "inside"
if (slot5NodesPairIt != slotToNodes.end()) {
for (const size_t &nodeIndex : slot5NodesPairIt->second) {
for (boost::tie(ei, ei_end) = boost::out_edges(nodeIndex, pattern);
ei != ei_end; ++ei) {
auto vt = boost::target(*ei, pattern);
const auto vtNodeSlotPairIt = nodeToSlot.find(vt);
assert(vtNodeSlotPairIt != nodeToSlot.end());
const size_t vtSlot = vtNodeSlotPairIt->second;
if (vtSlot != 5) {
auto correspondingNodePairIt = correspondingNode.find(nodeIndex);
assert(correspondingNodePairIt != correspondingNode.end());
externallyConnectedNodes.insert(correspondingNodePairIt->second);
}
}
}
}
// connecting all is wrong. Maybe I should check whether the external nodes
// are connected via a single node? If so this node is an articulation point.
// I could test this by checking if it filters out only the falsely labeled
// pattern 2367
const size_t &n = externallyConnectedNodes.size();
const size_t numberOfExternalEdges = n * (n - 1) / 2;
// Connect all external nodes with each other
for (size_t edgeIndex = 0; edgeIndex < numberOfExternalEdges; edgeIndex++) {
const int sei0 =
n - 2 -
std::floor(std::sqrt(-8 * edgeIndex + 4 * n * (n - 1) - 7) / 2.0 - 0.5);
const int sei1 = edgeIndex + sei0 + 1 - n * (n - 1) / 2 +
(n - sei0) * ((n - sei0) - 1) / 2;
const size_t ni0 = *std::next(externallyConnectedNodes.begin(), sei0);
const size_t ni1 = *std::next(externallyConnectedNodes.begin(), sei1);
boost::add_edge(ni0, ni1, rotationallySymmetricPattern);
}
return rotationallySymmetricPattern;
}
void FlatPatternTopology::printGraph(const BoostGraph &g) const {
boost::graph_traits<BoostGraph>::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = boost::edges(g); ei != ei_end; ++ei)
std::cout << (char)(boost::source(*ei, g) + 'A') << " -- "
<< (char)(boost::target(*ei, g) + 'A');
std::cout << std::endl;
}

View File

@ -0,0 +1,49 @@
#ifndef FLATPATTTERNTOPOLOGY_HPP
#define FLATPATTTERNTOPOLOGY_HPP
#include <boost/graph/adjacency_list.hpp>
#include <unordered_map>
#include <unordered_set>
#include <vcg/space/point2.h>
#include <vector>
using BoostGraph =
boost::adjacency_list<boost::hash_setS, boost::vecS, boost::undirectedS>;
using vertex_t = boost::graph_traits<BoostGraph>::vertex_descriptor;
class FlatPatternTopology {
public:
bool containsArticulationPoints() const;
FlatPatternTopology(const std::vector<size_t> &numberOfNodesPerSlot,
const std::vector<vcg::Point2i> &edges);
static void
constructNodeToSlotMap(const std::vector<size_t> &numberOfNodesPerSlot,
std::unordered_map<size_t, size_t> &nodeToSlot);
static void constructSlotToNodeMap(
const std::unordered_map<size_t, size_t> &nodeToSlot,
std::unordered_map<size_t, std::unordered_set<size_t>> &slotToNode);
FlatPatternTopology();
private:
BoostGraph pattern;
std::vector<size_t> numberOfNodesPerSlot;
std::unordered_map<size_t, size_t> nodeToSlot;
std::unordered_map<size_t, std::unordered_set<size_t>> slotToNode;
std::unordered_map<size_t, size_t> correspondingNode;
void constructCorresponginNodeMap();
/*
* Creates a pattern which is a copy of the input pattern but with added edges
* that result
* */
void printGraph(const BoostGraph &g) const;
static BoostGraph constructRotationallySymmetricPattern(
const BoostGraph &pattern,
const std::unordered_map<size_t, std::unordered_set<size_t>> &slotToNodes,
const std::unordered_map<size_t, size_t> &nodeToSlot,
const std::unordered_map<size_t, size_t> &correspondingNode);
void constructNodeToSlotMap();
void constructSlotToNodeMap();
};
#endif // FLATPATTTERNTOPOLOGY_HPP

272
utilities.hpp Normal file
View File

@ -0,0 +1,272 @@
#ifndef UTILITIES_H
#define UTILITIES_H
#include <Eigen/Dense>
#include <filesystem>
#include <fstream>
#include <regex>
namespace Utilities {
inline void parseIntegers(const std::string &str, std::vector<size_t> &result) {
typedef std::regex_iterator<std::string::const_iterator> re_iterator;
typedef re_iterator::value_type re_iterated;
std::regex re("(\\d+)");
re_iterator rit(str.begin(), str.end(), re);
re_iterator rend;
std::transform(rit, rend, std::back_inserter(result),
[](const re_iterated &it) { return std::stoi(it[1]); });
}
// std::string convertToLowercase(const std::string &s) {
// std::string lowercase;
// std::transform(s.begin(), s.end(), lowercase.begin(),
// [](unsigned char c) { return std::tolower(c); });
// return lowercase;
//}
// bool hasExtension(const std::string &filename, const std::string &extension)
// {
// const std::filesystem::path path(filename);
// if (!path.has_extension()) {
// std::cerr << "Error: No file extension found in " << filename <<
// std::endl; return false;
// }
// const std::string detectedExtension = path.extension().string();
// if (convertToLowercase(detectedExtension) != convertToLowercase(extension))
// {
// std::cerr << "Error: detected extension is " + detectedExtension +
// " and not " + extension
// << std::endl;
// return false;
// }
// return true;
//}
} // namespace Utilities
struct NodalForce {
size_t index;
size_t dof;
double magnitude;
};
struct Vector6d : public std::array<double, 6> {
Vector6d() {
for (size_t i = 0; i < 6; i++) {
this->operator[](i) = 0;
}
}
Vector6d(const double &d) {
for (size_t i = 0; i < 6; i++) {
this->operator[](i) = d;
}
}
Vector6d(const std::initializer_list<double> &initList) {
std::copy(initList.begin(), initList.end(), this->begin());
}
Vector6d operator*(const double &d) const {
Vector6d result;
for (size_t i = 0; i < 6; i++) {
result[i] = this->operator[](i) * d;
}
return result;
}
Vector6d operator*(const Vector6d &v) const {
Vector6d result;
for (size_t i = 0; i < 6; i++) {
result[i] = this->operator[](i) * v[i];
}
return result;
}
Vector6d operator/(const double &d) const {
Vector6d result;
for (size_t i = 0; i < 6; i++) {
result[i] = this->operator[](i) / d;
}
return result;
}
Vector6d operator+(const Vector6d &v) const {
Vector6d result;
for (size_t i = 0; i < 6; i++) {
result[i] = this->operator[](i) + v[i];
}
return result;
}
Vector6d operator-(const Vector6d &v) const {
Vector6d result;
for (size_t i = 0; i < 6; i++) {
result[i] = this->operator[](i) - v[i];
}
return result;
}
Vector6d inverted() const {
Vector6d result;
for (size_t i = 0; i < 6; i++) {
assert(this->operator[](i) != 0);
result[i] = 1 / this->operator[](i);
}
return result;
}
bool isZero() const {
for (size_t i = 0; i < 6; i++) {
if (this->operator[](i) != 0)
return false;
}
return true;
}
double squaredNorm() const {
double squaredNorm = 0;
std::for_each(begin(), end(),
[&](const double &v) { squaredNorm += pow(v, 2); });
return squaredNorm;
}
double norm() const { return sqrt(squaredNorm()); }
bool isFinite() const {
return std::any_of(begin(), end(), [](const double &v) {
if (!std::isfinite(v)) {
return false;
}
return true;
});
}
};
// namespace ConfigurationFile {
// inline void getPlyFilename(const std::string jsonFilepath,
// std::string &plyFilename) {
// std::ifstream inFile(jsonFilepath);
// std::string jsonContents((std::istreambuf_iterator<char>(inFile)),
// std::istreambuf_iterator<char>());
// nlohmann::json jsonFile(nlohmann::json::parse(jsonContents));
// if (jsonFile.contains("plyFilename")) {
// plyFilename = jsonFile["plyFilename"];
// }
//}
// inline void
// getNodalForces(const std::string jsonFilepath,
// std::unordered_map<size_t, Eigen::Vector3d> &nodalForces) {
// std::ifstream inFile(jsonFilepath);
// std::string jsonContents((std::istreambuf_iterator<char>(inFile)),
// std::istreambuf_iterator<char>());
// nlohmann::json jsonFile(nlohmann::json::parse(jsonContents));
// nodalForces.clear();
// if (jsonFile.contains("forces")) {
// std::vector<std::vector<double>> forces = jsonFile["forces"];
// for (size_t forceIndex = 0; forceIndex < forces.size(); forceIndex++) {
// const BeamFormFinder::NodalForce nf{
// static_cast<size_t>(forces[forceIndex][0]),
// static_cast<size_t>(forces[forceIndex][1]), forces[forceIndex][2]};
// const size_t vertexIndex = forces[forceIndex][0];
// const Eigen::Vector3d forceVector(
// forces[forceIndex][1], forces[forceIndex][2],
// forces[forceIndex][3]);
// assert(forceIndex >= 0 && forceVector.norm() >= 0);
// nodalForces[vertexIndex] = forceVector;
// }
// }
//}
// inline void getFixedVertices(const std::string jsonFilepath,
// std::vector<size_t> &fixedVertices) {
// std::ifstream inFile(jsonFilepath);
// std::string jsonContents((std::istreambuf_iterator<char>(inFile)),
// std::istreambuf_iterator<char>());
// nlohmann::json jsonFile(nlohmann::json::parse(jsonContents));
// fixedVertices.clear();
// if (jsonFile.contains("fixedVertices")) {
// fixedVertices =
// static_cast<std::vector<size_t>>(jsonFile["fixedVertices"]);
// }
//}
// struct SimulationScenario {
// std::string edgeMeshFilename;
// std::vector<size_t> fixedVertices;
// std::unordered_map<size_t, Eigen::Vector3d> nodalForces;
//};
// void to_json(nlohmann::json &json, const SimulationScenario &scenario) {
// json["plyFilename"] = scenario.edgeMeshFilename;
// if (!scenario.fixedVertices.empty()) {
// json["fixedVertices"] = scenario.fixedVertices;
// }
// if (!scenario.nodalForces.empty()) {
// std::vector<std::tuple<size_t, double, double, double>> forces;
// std::transform(scenario.nodalForces.begin(), scenario.nodalForces.end(),
// std::back_inserter(forces),
// [](const std::pair<size_t, Eigen::Vector3d> &f) {
// return std::tuple<size_t, double, double, double>{
// f.first, f.second[0], f.second[1], f.second[2]};
// });
// json["forces"] = forces;
// }
//}
//} // namespace ConfigurationFile
#include "polyscope/curve_network.h"
#include "polyscope/polyscope.h"
inline void initPolyscope() {
if (polyscope::state::initialized) {
return;
}
polyscope::init();
polyscope::options::groundPlaneEnabled = false;
polyscope::view::upDir = polyscope::view::UpDir::ZUp;
}
inline void registerWorldAxes() {
if (!polyscope::state::initialized) {
initPolyscope();
}
Eigen::MatrixX3d axesPositions(4, 3);
axesPositions.row(0) = Eigen::Vector3d(0, 0, 0);
axesPositions.row(1) = Eigen::Vector3d(1, 0, 0);
axesPositions.row(2) = Eigen::Vector3d(0, 1, 0);
axesPositions.row(3) = Eigen::Vector3d(0, 0, 1);
Eigen::MatrixX2i axesEdges(3, 2);
axesEdges.row(0) = Eigen::Vector2i(0, 1);
axesEdges.row(1) = Eigen::Vector2i(0, 2);
axesEdges.row(2) = Eigen::Vector2i(0, 3);
Eigen::MatrixX3d axesColors(3, 3);
axesColors.row(0) = Eigen::Vector3d(1, 0, 0);
axesColors.row(1) = Eigen::Vector3d(0, 1, 0);
axesColors.row(2) = Eigen::Vector3d(0, 0, 1);
const std::string worldAxesName = "World Axes";
polyscope::registerCurveNetwork(worldAxesName, axesPositions, axesEdges);
polyscope::getCurveNetwork(worldAxesName)->setRadius(0.001);
const std::string worldAxesColorName = worldAxesName + " Color";
polyscope::getCurveNetwork(worldAxesName)
->addEdgeColorQuantity(worldAxesColorName, axesColors)
->setEnabled(true);
}
template <typename T1, typename T2>
void constructInverseMap(const T1 &map, T2 &oppositeMap) {
assert(!map.empty());
oppositeMap.clear();
for (const auto &mapIt : map) {
oppositeMap[mapIt.second] = mapIt.first;
}
}
#endif // UTILITIES_H

78
vcgtrimesh.cpp Normal file
View File

@ -0,0 +1,78 @@
#include "vcgtrimesh.hpp"
#include "wrap/io_trimesh/import_obj.h"
#include "wrap/io_trimesh/import_off.h"
#include <filesystem>
void VCGTriMesh::loadFromPlyFile(const std::string &filename) {
unsigned int mask = 0;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOORD;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTNORMAL;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOLOR;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_EDGEINDEX;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_FACEINDEX;
if (nanoply::NanoPlyWrapper<VCGTriMesh>::LoadModel(
std::filesystem::absolute(filename).c_str(), *this, mask) != 0) {
std::cout << "Could not load tri mesh" << std::endl;
}
vcg::tri::UpdateTopology<VCGTriMesh>::FaceFace(*this);
vcg::tri::UpdateTopology<VCGTriMesh>::VertexFace(*this);
vcg::tri::UpdateNormal<VCGTriMesh>::PerVertexNormalized(*this);
}
Eigen::MatrixX3d VCGTriMesh::getVertices() const {
Eigen::MatrixX3d vertices(VN(), 3);
for (size_t vi = 0; vi < VN(); vi++) {
VCGTriMesh::CoordType vertexCoordinates = vert[vi].cP();
vertices.row(vi) = vertexCoordinates.ToEigenVector<Eigen::Vector3d>();
}
return vertices;
}
Eigen::MatrixX3i VCGTriMesh::getFaces() const {
Eigen::MatrixX3i faces(FN(), 3);
for (int fi = 0; fi < FN(); fi++) {
const VCGTriMesh::FaceType &face = this->face[fi];
const size_t v0 = vcg::tri::Index<VCGTriMesh>(*this, face.cV(0));
const size_t v1 = vcg::tri::Index<VCGTriMesh>(*this, face.cV(1));
const size_t v2 = vcg::tri::Index<VCGTriMesh>(*this, face.cV(2));
faces.row(fi) = Eigen::Vector3i(v0, v1, v2);
}
return faces;
}
bool VCGTriMesh::savePly(const std::string plyFilename) {
// Load the ply file
unsigned int mask = 0;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOORD;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_VERTCOLOR;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_FACEINDEX;
mask |= nanoply::NanoPlyWrapper<VCGTriMesh>::IO_FACENORMAL;
if (nanoply::NanoPlyWrapper<VCGTriMesh>::SaveModel(plyFilename.c_str(), *this,
mask, false) != 0) {
return false;
}
return true;
}
VCGTriMesh::VCGTriMesh() {}
VCGTriMesh::VCGTriMesh(const std::string &filename) {
const std::string extension = std::filesystem::path(filename).extension();
if (extension == ".ply") {
loadFromPlyFile(filename);
} else if (extension == ".obj") {
vcg::tri::io::ImporterOBJ<VCGTriMesh>::Info info;
vcg::tri::io::ImporterOBJ<VCGTriMesh>::Open(*this, filename.c_str(), info);
} else if (extension == ".off") {
vcg::tri::io::ImporterOFF<VCGTriMesh>::Open(*this, filename.c_str());
} else {
std::cerr << "Uknown file extension " << extension << ". Could not open "
<< filename << std::endl;
assert(false);
}
vcg::tri::UpdateTopology<VCGTriMesh>::AllocateEdge(*this);
vcg::tri::UpdateTopology<VCGTriMesh>::FaceFace(*this);
vcg::tri::UpdateTopology<VCGTriMesh>::VertexFace(*this);
vcg::tri::UpdateNormal<VCGTriMesh>::PerVertexNormalized(*this);
}

40
vcgtrimesh.hpp Normal file
View File

@ -0,0 +1,40 @@
#ifndef VCGTRIMESH_HPP
#define VCGTRIMESH_HPP
#include <vcg/complex/complex.h>
#include <wrap/nanoply/include/nanoplyWrapper.hpp>
using VertexIndex = size_t;
class VCGTriMeshVertex;
class VCGTriMeshEdge;
class VCGTriMeshFace;
struct VCGTriMeshUsedTypes
: public vcg::UsedTypes<vcg::Use<VCGTriMeshVertex>::AsVertexType,
vcg::Use<VCGTriMeshEdge>::AsEdgeType,
vcg::Use<VCGTriMeshFace>::AsFaceType> {};
class VCGTriMeshVertex
: public vcg::Vertex<VCGTriMeshUsedTypes, vcg::vertex::Coord3d,
vcg::vertex::Normal3d, vcg::vertex::BitFlags,
vcg::vertex::Color4b, vcg::vertex::VFAdj> {};
class VCGTriMeshFace
: public vcg::Face<VCGTriMeshUsedTypes, vcg::face::FFAdj, vcg::face::VFAdj,
vcg::face::VertexRef, vcg::face::BitFlags,
vcg::face::Normal3d> {};
class VCGTriMeshEdge
: public vcg::Edge<VCGTriMeshUsedTypes, vcg::edge::VertexRef> {};
class VCGTriMesh : public vcg::tri::TriMesh<std::vector<VCGTriMeshVertex>,
std::vector<VCGTriMeshFace>,
std::vector<VCGTriMeshEdge>> {
public:
VCGTriMesh();
VCGTriMesh(const std::string &filename);
void loadFromPlyFile(const std::string &filename);
Eigen::MatrixX3d getVertices() const;
Eigen::MatrixX3i getFaces() const;
bool savePly(const std::string plyFilename);
template <typename MeshElement> size_t getIndex(const MeshElement &element) {
return vcg::tri::Index<VCGTriMesh>(*this, element);
}
};
#endif // VCGTRIMESH_HPP