#include "topologyenumerator.hpp" #include #include #include #include #include #include const bool debugIsOn{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, const std::string &desiredResultsPath, const int &numberOfDesiredEdges) { 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); } PatternGeometry patternGeometryAllEdges; patternGeometryAllEdges.add(numberOfNodesPerSlot, allPossibleEdges); // Create Results path auto resultPath = std::filesystem::path(desiredResultsPath); 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(PatternGeometry().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.save( std::filesystem::path(resultsPath).append("allPossibleEdges.ply").string()); } // statistics.numberOfPossibleEdges = numberOfAllPossibleEdges; std::vector validEdges = getValidEdges(numberOfNodesPerSlot, resultsPath, patternGeometryAllEdges, allPossibleEdges); PatternGeometry patternAllValidEdges; patternAllValidEdges.add(patternGeometryAllEdges.getVertices(), validEdges); if (debugIsOn) { // Export all valid edges in a ply patternAllValidEdges.save( std::filesystem::path(resultsPath).append("allValidEdges.ply").string()); } // statistics.numberOfValidEdges = validEdges.size(); // Find pairs of intersecting edges const 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++) { PatternGeometry 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.save(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::Point3d(nodes[nodeIndex][0], nodes[nodeIndex][1],0); // } // if (std::filesystem::exists(std::filesystem::path(resultsPath) // .append("patterns.patt") // .string())) { // std::filesystem::remove( // std::filesystem::path(resultsPath).append("patterns.patt")); // } if (numberOfDesiredEdges == -1) { for (size_t numberOfEdges = 2; numberOfEdges <= validEdges.size(); 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)) { // if (debugIsOn) { std::filesystem::remove_all(perEdgeResultPath); // } else { // continue; // } } std::filesystem::create_directory(perEdgeResultPath); computeValidPatterns(numberOfNodesPerSlot, numberOfEdges, perEdgeResultPath, patternGeometryAllEdges.getVertices(), intersectingEdges, validEdges); statistics.print(setupString, perEdgeResultPath); statistics.reset(); } } else { std::cout << "Computing " + setupString << " with " << numberOfDesiredEdges << " edges." << std::endl; auto perEdgeResultPath = std::filesystem::path(resultsPath) .append(std::to_string(numberOfDesiredEdges)); if (std::filesystem::exists(perEdgeResultPath)) { // return; std::filesystem::remove_all(perEdgeResultPath); } std::filesystem::create_directory(perEdgeResultPath); computeValidPatterns(numberOfNodesPerSlot, numberOfDesiredEdges, perEdgeResultPath, patternGeometryAllEdges.getVertices(), intersectingEdges, validEdges); statistics.print(setupString, perEdgeResultPath); } } 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 PatternGeometry &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) { PatternGeometry::EdgeType e = patternGeometryAllEdges.edge[coincideEdgeIndex]; PatternGeometry 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.save(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); PatternGeometry patternDuplicateEdges; for (auto duplicateEdgeIndex : duplicateEdges) { PatternGeometry::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.save( 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::exportPattern(const std::filesystem::path &saveToPath, PatternGeometry &patternGeometry, const bool saveTilledPattern) const { const std::string patternName = patternGeometry.getLabel(); std::filesystem::create_directory(saveToPath); patternGeometry.save(std::filesystem::path(saveToPath).append(patternName).string() + ".ply"); if (saveTilledPattern) { PatternGeometry tiledPatternGeometry = PatternGeometry::createTile(patternGeometry); tiledPatternGeometry.save( std::filesystem::path(saveToPath).append(patternName + "_tiled").string() + ".ply"); } } 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) { 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 const auto validPatternsPath = std::filesystem::path(resultsPath).append("Valid"); const bool validPathCreatedSuccesfully = std::filesystem::create_directories(validPatternsPath); assert(validPathCreatedSuccesfully && std::filesystem::exists(validPatternsPath)); // std::ofstream validPatternsFileStream; // validPatternsFileStream.open( // validPatternsPath.append("patterns.patt").string()); const std::string compressedPatternsFilePath = std::filesystem::path(validPatternsPath).append("patterns.patt").string(); PatternIO::PatternSet patternSet; patternSet.nodes = allVertices; const int patternSetBufferSize = 10000; const size_t numberOfPatterns = PatternGeometry::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()); /*TODO: Performance could be improved by changing the patternGeometry with * respect to the previous one. Maybe I could xor the binaryRepresentation * to the previous one.*/ // std::string previousPatternBinaryRepresentation(validEdges.size(),'0'); size_t patternIndex = 0; bool validPatternsExist = false; const bool exportTilledPattern = debugIsOn; const bool saveCompressedFormat = false; 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]; } } PatternGeometry patternGeometry; patternGeometry.add(allVertices, patternEdges); patternGeometry.setLabel(patternName); // 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) { exportPattern(std::filesystem::path(resultsPath).append("Intersecting"), patternGeometry, exportTilledPattern); } } else { continue; // should be uncommented in order to improve performance } } const bool tiledPatternHasEdgesWithAngleSmallerThanThreshold = patternGeometry.hasAngleSmallerThanThreshold(numberOfNodesPerSlot, 15); if (tiledPatternHasEdgesWithAngleSmallerThanThreshold) { statistics.numberOfPatternsViolatingAngleThreshold++; if (debugIsOn /*|| savePlyFiles*/) { if (savePlyFiles) { exportPattern(std::filesystem::path(resultsPath) .append("ExceedingAngleThreshold"), patternGeometry, exportTilledPattern); } } else { continue; } } const bool tiledPatternHasNodeWithValenceGreaterThanDesired = patternGeometry.hasValenceGreaterThan(numberOfNodesPerSlot, 6); if (tiledPatternHasNodeWithValenceGreaterThanDesired) { statistics.numberOfPatternsViolatingValenceThreshold++; if (debugIsOn) { if (savePlyFiles) { auto highValencePath = std::filesystem::path(resultsPath) .append("HighValencePatterns"); exportPattern(highValencePath, patternGeometry, exportTilledPattern); } } else { continue; } } // 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(); PatternGeometry fanPatternGeometry = PatternGeometry::createFan(patternGeometry); const int interfaceNodeVi = 3; std::vector connectedEdges; vcg::edge::VEStarVE(&fanPatternGeometry.vert[interfaceNodeVi], connectedEdges); if (!connectedEdges.empty()) { for (int i = 1; i < 6; i++) { vcg::tri::Allocator::AddEdge(fanPatternGeometry, interfaceNodeVi + (i - 1) * patternGeometry.VN(), interfaceNodeVi + i * patternGeometry.VN()); } } vcg::tri::Clean::MergeCloseVertex(fanPatternGeometry, 0.0000005); vcg::tri::Allocator::CompactEveryVector(fanPatternGeometry); vcg::tri::UpdateTopology::VertexEdge(fanPatternGeometry); vcg::tri::UpdateTopology::EdgeEdge(fanPatternGeometry); // for (PatternGeometry::VertexType &v : tilledPatternGeometry.vert) { // std::vector connectedEdges; // vcg::edge::VEStarVE(&v, connectedEdges); // if (connectedEdges.size() == 1) { // vcg::tri::Allocator::DeleteVertex(tilledPatternGeometry, v); // vcg::tri::Allocator::DeleteEdge(tilledPatternGeometry, // *connectedEdges[0]); // } // } // // vcg::tri::Allocator::CompactEveryVector(tilledPatternGeometry); // fanPatternGeometry.updateEigenEdgeAndVertices(); BoostGraph fanPatternGraph(fanPatternGeometry.VN()); // std::cout << "Edges:"; for (const PatternGeometry::EdgeType &e : fanPatternGeometry.edge) { if (e.IsD() || e.cV(0)->IsD() || e.cV(1)->IsD()) { continue; } const int vi0 = fanPatternGeometry.getIndex(e.cV(0)); const int vi1 = fanPatternGeometry.getIndex(e.cV(1)); boost::add_edge(vi0, vi1, fanPatternGraph); // std::cout << vi0 << "," << vi1 << " "; } // std::cout << std::endl; std::vector articulationPoints; boost::articulation_points(fanPatternGraph, std::back_inserter(articulationPoints)); const bool hasArticulationPoints = !articulationPoints.empty(); // if (hasArticulationPoints /*&& !patternContainsIntersectingEdges // && !tiledPatternHasDanglingEdges && !hasFloatingComponents // && !tiledPatternHasNodeWithValenceGreaterThanDesired // && !tiledPatternHasEdgesWithAngleSmallerThanThreshold*/) { // for (PatternGeometry::VertexType &v : patternGeometry.vert) { // v.C() = vcg::Color4b::Yellow; // } // // std::cout << "AP:"; // for (const int articulationPointVi : articulationPoints) { // if (articulationPointVi >= patternGeometry.VN()) { // continue; // } // // std::cout << articulationPointVi << " "; // patternGeometry.vert[articulationPointVi].C() = vcg::Color4b::Red; // } // PatternGeometry tilledPatternGeometry = PatternGeometry::createTile(patternGeometry); // // std::cout << std::endl; // std::vector fanVertexColors(tilledPatternGeometry.VN(), glm::vec3(0, 0, 1)); // for (const PatternGeometry::VertexType &v : tilledPatternGeometry.vert) { // const auto vColor = glm::vec3(v.cC()[0] / 255, v.cC()[1] / 255, v.cC()[2] / 255); // const auto vi = tilledPatternGeometry.getIndex(v); // fanVertexColors[vi] = vColor; // } // // tilledPatternGeometry.updateEigenEdgeAndVertices(); // // tilledPatternGeometry.registerForDrawing() // // ->addNodeColorQuantity("ap_tilled", fanVertexColors) // // ->setEnabled(true); // // polyscope::show(); // // tilledPatternGeometry.unregister(); // } // 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"); exportPattern(danglingEdgesPath, patternGeometry, exportTilledPattern); } } 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.save( std::filesystem::path(moreThanOneCCPath).append(patternName).string() + ".ply"); PatternGeometry tiledPatternGeometry = PatternGeometry::createTile( patternGeometry); // the marked nodes of hasDanglingEdges are std::vector> eCC; vcg::tri::Clean::edgeMeshConnectedComponents(tiledPatternGeometry, eCC); vcg::tri::UpdateFlags::EdgeClear(tiledPatternGeometry); const size_t numberOfCC_edgeBased = eCC.size(); std::sort(eCC.begin(), eCC.end(), [](const std::pair &a, const std::pair &b) { return a.first > b.first; }); PatternGeometry::EdgePointer &ep = eCC[0].second; size_t colorsRegistered = 0; std::stack stack; stack.push(ep); while (!stack.empty()) { EdgePointer ep = stack.top(); stack.pop(); for (int i = 0; i < 2; ++i) { vcg::edge::VEIterator vei(ep->V(i)); while (!vei.End()) { if (!vei.E()->IsV()) { vei.E()->SetV(); stack.push(vei.E()); tiledPatternGeometry .vert[tiledPatternGeometry.getIndex(vei.V1())] .C() = vcg::Color4b::Blue; tiledPatternGeometry .vert[tiledPatternGeometry.getIndex(vei.V0())] .C() = vcg::Color4b::Blue; colorsRegistered++; } ++vei; } } } assert(colorsRegistered == eCC[0].first); if (exportTilledPattern) { tiledPatternGeometry.save(std::filesystem::path(moreThanOneCCPath) .append(patternName + "_tiled") .string() + ".ply"); } } } else { continue; } } if (hasArticulationPoints /*&& !hasFloatingComponents && !tiledPatternHasDanglingEdges */) { statistics.numberOfPatternsWithArticulationPoints++; if (debugIsOn) { if (savePlyFiles) { auto articulationPointsPath = std::filesystem::path(resultsPath) .append("ArticulationPoints"); exportPattern(articulationPointsPath, patternGeometry, exportTilledPattern); } } else { continue; } } const bool isValidPattern = !patternContainsIntersectingEdges && !tiledPatternHasDanglingEdges && !hasFloatingComponents && !hasArticulationPoints && !tiledPatternHasNodeWithValenceGreaterThanDesired && !tiledPatternHasEdgesWithAngleSmallerThanThreshold; if (isValidPattern) { // if(patternName=='2055'){ // PatternGeometry tiledPatternGeometry = PatternGeometry::createTile( // patternGeometry); // the marked nodes of hasDanglingEdges are // tiledPatternGeometry.registerForDrawing(std::array{0, 0, 1}); // polyscope::show(); // tiledPatternGeometry.unregister(); // } statistics.numberOfValidPatterns++; validPatternsExist = true; if (savePlyFiles) { exportPattern(validPatternsPath, patternGeometry, exportTilledPattern); } if (saveCompressedFormat) { PatternIO::Pattern pattern; pattern.edges = patternEdges; pattern.name = patternIndex; patternSet.patterns.emplace_back(pattern); // Save valid patterns // if (patternIndex% patternSetBufferSize == 0) { if (statistics.numberOfValidPatterns % patternSetBufferSize == 0) { PatternIO::save(compressedPatternsFilePath, patternSet); patternSet.patterns.clear(); patternSet.patterns.reserve(patternSetBufferSize); } } } // assert(vcg_tiledPatternHasDangling == tiledPatternHasDanglingEdges); } while (std::next_permutation(patternBinaryRepresentation.begin(), patternBinaryRepresentation.end())); if (!patternSet.patterns.empty() && saveCompressedFormat) { PatternIO::save(compressedPatternsFilePath, patternSet); } if (!validPatternsExist) { std::filesystem::remove_all(validPatternsPath); if (!debugIsOn) { std::filesystem::remove_all(resultsPath); } } } 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; }