#include "edgemesh.hpp" #include "vcg/simplex/face/topology.h" #include Eigen::MatrixX2i VCGEdgeMesh::getEigenEdges() const { return eigenEdges; } Eigen::MatrixX3d VCGEdgeMesh::getEigenVertices() { getVertices(eigenVertices); return eigenVertices; } Eigen::MatrixX3d VCGEdgeMesh::getEigenEdgeNormals() const { return eigenEdgeNormals; } bool VCGEdgeMesh::save(const string &plyFilename) { std::string filename = plyFilename; if (filename.empty()) { filename = std::filesystem::current_path().append(getLabel() + ".ply").string(); } else if (std::filesystem::is_directory(std::filesystem::path(plyFilename))) { filename = std::filesystem::path(plyFilename).append(getLabel() + ".ply").string(); } assert(std::filesystem::path(filename).extension().string() == ".ply"); unsigned int mask = 0; mask |= nanoply::NanoPlyWrapper::IO_VERTCOORD; mask |= nanoply::NanoPlyWrapper::IO_EDGEINDEX; mask |= nanoply::NanoPlyWrapper::IO_VERTNORMAL; mask |= vcg::tri::io::Mask::IOM_VERTCOLOR; if (nanoply::NanoPlyWrapper::SaveModel(filename.c_str(), *this, mask, false) != 0) { return false; } return true; } void VCGEdgeMesh::GeneratedRegularSquaredPattern( const double angleDeg, std::vector> &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 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> 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::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::createSpanGrid(const size_t squareGridDimension) { return createSpanGrid(squareGridDimension, squareGridDimension); } bool VCGEdgeMesh::createSpanGrid(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::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::AddEdge(*this, vi, vi + desiredWidth + 1); continue; } else if (x == 0) { // col 0.Connect with node to the right vcg::tri::Allocator::AddEdge(*this, vi, vi + 1); continue; } else if (y == desiredHeight) { // row 0.Connect with node below // vcg::tri::Allocator::AddEdge(*this, vi, // vi - (desiredWidth + // 1)); continue; } else if (x == desiredWidth) { // row 0.Connect with node to the left // vcg::tri::Allocator::AddEdge(*this, vi, vi - 1); continue; } vcg::tri::Allocator::AddEdge(*this, vi, vi + desiredWidth + 1); vcg::tri::Allocator::AddEdge(*this, vi, vi + 1); // vcg::tri::Allocator::AddEdge(*this, vi, // vi - (desiredWidth + 1)); // vcg::tri::Allocator::AddEdge(*this, vi, vi - 1); } vcg::tri::Allocator::DeleteVertex(*this, vert[0]); vcg::tri::Allocator::DeleteVertex(*this, vert[desiredWidth]); vcg::tri::Allocator::DeleteVertex( *this, vert[desiredHeight * (desiredWidth + 1)]); vcg::tri::Allocator::DeleteVertex( *this, vert[(desiredHeight + 1) * (desiredWidth + 1) - 1]); vcg::tri::Allocator::CompactVertexVector(*this); getEdges(eigenEdges); getVertices(eigenVertices); // vcg::tri::Allocator::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::load(const 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(); if (!loadUsingNanoply(usedPath)) { std::cerr << "Error: Unable to open " + usedPath << std::endl; return false; } getEdges(eigenEdges); getVertices(eigenVertices); vcg::tri::UpdateTopology::VertexEdge(*this); label = std::filesystem::path(plyFilename).stem().string(); const bool printDebugInfo = false; if (printDebugInfo) { std::cout << plyFilename << " was loaded successfuly." << std::endl; std::cout << "Mesh has " << EN() << " edges." << std::endl; } label=std::filesystem::path(plyFilename).stem().string(); return true; } bool VCGEdgeMesh::loadUsingNanoply(const std::string &plyFilename) { this->Clear(); // assert(plyFileHasAllRequiredFields(plyFilename)); // Load the ply file unsigned int mask = 0; mask |= nanoply::NanoPlyWrapper::IO_VERTCOORD; mask |= nanoply::NanoPlyWrapper::IO_VERTNORMAL; mask |= nanoply::NanoPlyWrapper::IO_VERTCOLOR; mask |= nanoply::NanoPlyWrapper::IO_EDGEINDEX; if (nanoply::NanoPlyWrapper::LoadModel(plyFilename.c_str(), *this, mask) != 0) { return false; } return true; } // bool VCGEdgeMesh::plyFileHasAllRequiredFields(const std::string // &plyFilename) // { // const nanoply::Info info(plyFilename); // const std::vector::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 &edgePropertyVector = // edgeElemIt->propVec; // return hasPlyEdgeProperty(plyFilename, edgePropertyVector, // plyPropertyBeamDimensionsID) && // hasPlyEdgeProperty(plyFilename, edgePropertyVector, // plyPropertyBeamMaterialID); //} 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(vertexIndex)].cN(); vertexNormals.row(vertexIndex) = vertexNormal.ToEigenVector(); } 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(); edgeEndingPoints.row(edgeIndex) = edge.cP(1).ToEigenVector(); } } VCGEdgeMesh::VCGEdgeMesh() {} void VCGEdgeMesh::updateEigenEdgeAndVertices() { #ifdef POLYSCOPE_DEFINED getEdges(eigenEdges); getVertices(eigenVertices); #endif } bool VCGEdgeMesh::copy(VCGEdgeMesh &mesh) { vcg::tri::Append::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::VertexEdge(*this); return true; } void VCGEdgeMesh::removeDuplicateVertices() { vcg::tri::Clean::MergeCloseVertex(*this, 0.000061524); vcg::tri::Allocator::CompactVertexVector(*this); vcg::tri::UpdateTopology::VertexEdge(*this); } void VCGEdgeMesh::removeDuplicateVertices( vcg::tri::Allocator::PointerUpdater &pu_vertices, vcg::tri::Allocator::PointerUpdater &pu_edges) { vcg::tri::Clean::MergeCloseVertex(*this, 0.000061524); vcg::tri::Allocator::CompactVertexVector(*this, pu_vertices); vcg::tri::Allocator::CompactEdgeVector(*this, pu_edges); vcg::tri::UpdateTopology::VertexEdge(*this); } void VCGEdgeMesh::deleteDanglingVertices() { vcg::tri::Allocator::PointerUpdater pu; deleteDanglingVertices(pu); } void VCGEdgeMesh::deleteDanglingVertices(vcg::tri::Allocator::PointerUpdater &pu) { for (VertexType &v : vert) { std::vector incidentElements; vcg::edge::VEStarVE(&v, incidentElements); if (incidentElements.size() == 0 && !v.IsD()) { vcg::tri::Allocator::DeleteVertex(*this, v); } } vcg::tri::Allocator::CompactVertexVector(*this, pu); vcg::tri::Allocator::CompactEdgeVector(*this); updateEigenEdgeAndVertices(); } void VCGEdgeMesh::getVertices(Eigen::MatrixX3d &vertices) { vertices = Eigen::MatrixX3d(); vertices.resize(VN(), 3); for (int vi = 0; vi < VN(); vi++) { if (vert[vi].IsD()) { continue; } VCGEdgeMesh::CoordType vertexCoordinates = vert[static_cast(vi)].cP(); vertices.row(vi) = vertexCoordinates.ToEigenVector(); } } 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]; assert(!edge.IsD()); auto vp0 = edge.cV(0); auto vp1 = edge.cV(1); assert(vcg::tri::IsValidPointer(*this, vp0) && vcg::tri::IsValidPointer(*this, vp1)); const size_t vi0 = vcg::tri::Index(*this, vp0); const size_t vi1 = vcg::tri::Index(*this, vp1); assert(vi0 != -1 && vi1 != -1); edges.row(edgeIndex) = Eigen::Vector2i(vi0, vi1); } } 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; } #ifdef POLYSCOPE_DEFINED //TODO: make const getEigenVertices is not polyscope::CurveNetwork *VCGEdgeMesh::registerForDrawing( const std::optional> &desiredColor, const bool &shouldEnable) { PolyscopeInterface::init(); const double drawingRadius = 0.002; polyscope::CurveNetwork *polyscopeHandle_edgeMesh = polyscope::registerCurveNetwork(label, getEigenVertices(), getEigenEdges()); polyscopeHandle_edgeMesh->setEnabled(shouldEnable); polyscopeHandle_edgeMesh->setRadius(drawingRadius, true); if (desiredColor.has_value()) { const glm::vec3 desiredColor_glm(desiredColor.value()[0], desiredColor.value()[1], desiredColor.value()[2]); polyscopeHandle_edgeMesh->setColor(desiredColor_glm); } return polyscopeHandle_edgeMesh; } void VCGEdgeMesh::unregister() const { if (!polyscope::hasCurveNetwork(label)) { std::cerr << "No curve network registered with a name: " << getLabel() << std::endl; std::cerr << "Nothing to remove." << std::endl; return; } polyscope::removeCurveNetwork(label); } void VCGEdgeMesh::drawInitialFrames(polyscope::CurveNetwork *polyscopeHandle_initialMesh) const { Eigen::MatrixX3d frameInitialX(VN(), 3); Eigen::MatrixX3d frameInitialY(VN(), 3); Eigen::MatrixX3d frameInitialZ(VN(), 3); for (int vi = 0; vi < VN(); vi++) { frameInitialX.row(vi) = Eigen::Vector3d(1, 0, 0); frameInitialY.row(vi) = Eigen::Vector3d(0, 1, 0); frameInitialZ.row(vi) = Eigen::Vector3d(0, 0, 1); } polyscopeHandle_initialMesh->addNodeVectorQuantity("FrameX", frameInitialX) ->setVectorColor(glm::vec3(1, 0, 0)) ->setEnabled(true); polyscopeHandle_initialMesh->addNodeVectorQuantity("FrameY", frameInitialY) ->setVectorColor(glm::vec3(0, 1, 0)) ->setEnabled(true); polyscopeHandle_initialMesh->addNodeVectorQuantity("FrameZ", frameInitialZ) ->setVectorColor(glm::vec3(0, 0, 1)) ->setEnabled(true); } #endif