#include "topologyenumerator.hpp" #include #include #include #include #include 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 vertices, // const std::filesystem::path &savePath) const { // const std::string allOnes(patternTopology.getNumberOfPossibleEdges(), '1'); // const std::vector allEdges = // TrianglePatternTopology::convertToEdges(allOnes, vertices.size()); // TrianglePatternGeometry labelMesh; // std::vector 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 &reducedNumberOfNodesPerSlot) { assert(reducedNumberOfNodesPerSlot.size() == 5); assert(reducedNumberOfNodesPerSlot[0] == 0 || reducedNumberOfNodesPerSlot[0] == 1); assert(reducedNumberOfNodesPerSlot[1] == 0 || reducedNumberOfNodesPerSlot[1] == 1); std::vector 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 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 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").string()); } // statistics.numberOfValidEdges = validEdges.size(); // Find pairs of intersecting edges std::unordered_map> 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::AddEdge( intersectingEdgePair, patternGeometryAllEdges.getVertices()[validEdges[ei0][0]], patternGeometryAllEdges.getVertices()[validEdges[ei0][1]]); vcg::tri::Allocator::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 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 &numberOfNodesPerSlot, std::vector &nodesEdge0, std::vector &nodesEdge1, std::vector &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 TopologyEnumerator::computeCoincideEdges( const std::vector &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 nodesEdge0; // left edge std::vector nodesEdge1; // bottom edge std::vector nodesEdge2; // right edge computeEdgeNodes(numberOfNodesPerSlot, nodesEdge0, nodesEdge1, nodesEdge2); std::vector coincideEdges0 = getCoincideEdges(nodesEdge0); std::vector coincideEdges1 = getCoincideEdges(nodesEdge1); std::vector coincideEdges2 = getCoincideEdges(nodesEdge2); std::unordered_set 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 TopologyEnumerator::computeDuplicateEdges( const std::vector &numberOfNodesPerSlot) { /* * A duplicate edges are all edges the "right" edge since due to rotational * symmetry "left" edge=="right" edge * */ std::unordered_set duplicateEdges; std::vector nodesEdge0; // left edge std::vector nodesEdge1; // bottom edge std::vector 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 TopologyEnumerator::getValidEdges( const std::vector &numberOfNodesPerSlot, const std::filesystem::path &resultsPath, const FlatPatternGeometry &patternGeometryAllEdges, const std::vector &allPossibleEdges) { std::unordered_set 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 edgeVertices; edgeVertices.push_back(p0); edgeVertices.push_back(p1); singleEdgeMesh.add(edgeVertices); singleEdgeMesh.add(std::vector{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 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::AddEdge( patternDuplicateEdges, p0, p1); } patternDuplicateEdges.savePly( std::filesystem::path(duplicateEdgesPath).append("duplicateEdges.ply").string()); } statistics.numberOfDuplicateEdges = duplicateEdges.size(); // Create the set of all possible edges without coincide and duplicate edges std::vector 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 &numberOfNodesPerSlot, const size_t &numberOfDesiredEdges, const std::filesystem::path &resultsPath, const std::vector &allVertices, const std::unordered_map> &intersectingEdges, const std::vector &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 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 TopologyEnumerator::getCoincideEdges( const std::vector &edgeNodeIndices) const { std::vector 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 &validEdges, const size_t &numberOfDesiredEdges) const { return true; }