#include "trianglepattterntopology.hpp" #include #include #include FlatPatternTopology::FlatPatternTopology() {} FlatPatternTopology::FlatPatternTopology( const std::vector &numberOfNodesPerSlot, const std::vector &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 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 bottomEdgeNodeSet = // (slotToNode.find(4))->second; // size_t bottomEdgeNodeIndex = *bottomEdgeNodeSet.begin(); // boost::clear_vertex(bottomEdgeNodeIndex, copyOfPattern); std::vector 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 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 &numberOfNodesPerSlot, std::unordered_map &nodeToSlot) { const size_t numberOfNodes = std::accumulate(numberOfNodesPerSlot.begin(), numberOfNodesPerSlot.end(), 0); assert(numberOfNodes != 0 && nodeToSlot.empty() && numberOfNodesPerSlot.size() == 7); std::vector 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 &nodeToSlot, std::unordered_map> &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> &slotToNodes, const std::unordered_map &nodeToSlot, const std::unordered_map &correspondingNode) { BoostGraph rotationallySymmetricPattern(pattern); boost::graph_traits::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 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::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; }