383 lines
14 KiB
C++
383 lines
14 KiB
C++
|
#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());
|
||
|
}
|