Initial commit
This commit is contained in:
commit
9668d030ea
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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 °reesOfArm,
|
||||
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());
|
||||
}
|
|
@ -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 °reesOfArm, 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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue