914 lines
38 KiB
C++
Executable File
914 lines
38 KiB
C++
Executable File
#include "topologyenumerator.hpp"
|
|
#include <math.h>
|
|
#include <algorithm>
|
|
#include <boost/graph/biconnected_components.hpp>
|
|
#include <iostream>
|
|
#include <numeric>
|
|
#include <thread>
|
|
#include <unordered_set>
|
|
|
|
const bool debugIsOn{true};
|
|
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<vcg::Point3d> vertices,
|
|
// const std::filesystem::path &savePath) const {
|
|
// const std::string allOnes(patternTopology.getNumberOfPossibleEdges(), '1');
|
|
// const std::vector<vcg::Point2i> allEdges =
|
|
// TrianglePatternTopology::convertToEdges(allOnes, vertices.size());
|
|
// TrianglePatternGeometry labelMesh;
|
|
// std::vector<vcg::Point3d> 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<size_t>& 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<size_t> 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<vcg::Point2i> 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<vcg::Point2i> validEdges =
|
|
getValidEdges(numberOfNodesPerSlot, resultsPath, patternGeometryAllEdges,
|
|
allPossibleEdges);
|
|
PatternGeometry patternAllValidEdges;
|
|
patternAllValidEdges.add(patternGeometryAllEdges.computeVertices(),
|
|
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<size_t, std::unordered_set<size_t>>
|
|
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<PatternGeometry>::AddEdge(
|
|
intersectingEdgePair,
|
|
patternGeometryAllEdges.computeVertices()[validEdges[ei0][0]],
|
|
patternGeometryAllEdges.computeVertices()[validEdges[ei0][1]]);
|
|
vcg::tri::Allocator<PatternGeometry>::AddEdge(
|
|
intersectingEdgePair,
|
|
patternGeometryAllEdges.computeVertices()[validEdges[ei1][0]],
|
|
patternGeometryAllEdges.computeVertices()[validEdges[ei1][1]]);
|
|
intersectingEdgePair.save(std::filesystem::path(intersectingEdgesPath)
|
|
.append(std::to_string(mapIt->first) +
|
|
"_" + std::to_string(*setIt) +
|
|
".ply")
|
|
.string());
|
|
}
|
|
}
|
|
}
|
|
|
|
const std::unordered_set<VertexIndex> interfaceNodes =
|
|
patternGeometryAllEdges.getInterfaceNodes(numberOfNodesPerSlot);
|
|
|
|
// assert(validEdges.size() == allPossibleEdges.size() -
|
|
// coincideEdges.size() -
|
|
// duplicateEdges.size());
|
|
|
|
// PatternSet patternSet;
|
|
// const std::vector<vcg::Point3d> 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.computeVertices(),
|
|
intersectingEdges, validEdges, interfaceNodes);
|
|
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.computeVertices(),
|
|
intersectingEdges, validEdges, interfaceNodes);
|
|
statistics.print(setupString, perEdgeResultPath);
|
|
}
|
|
}
|
|
|
|
void TopologyEnumerator::computeEdgeNodes(
|
|
const std::vector<size_t>& numberOfNodesPerSlot,
|
|
std::vector<size_t>& nodesEdge0,
|
|
std::vector<size_t>& nodesEdge1,
|
|
std::vector<size_t>& 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<size_t> TopologyEnumerator::computeCoincideEdges(
|
|
const std::vector<size_t>& 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<size_t> nodesEdge0; // left edge
|
|
std::vector<size_t> nodesEdge1; // bottom edge
|
|
std::vector<size_t> nodesEdge2; // right edge
|
|
computeEdgeNodes(numberOfNodesPerSlot, nodesEdge0, nodesEdge1, nodesEdge2);
|
|
|
|
std::vector<size_t> coincideEdges0 = getCoincideEdges(nodesEdge0);
|
|
std::vector<size_t> coincideEdges1 = getCoincideEdges(nodesEdge1);
|
|
std::vector<size_t> coincideEdges2 = getCoincideEdges(nodesEdge2);
|
|
std::unordered_set<size_t> 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<size_t> TopologyEnumerator::computeDuplicateEdges(
|
|
const std::vector<size_t>& numberOfNodesPerSlot) {
|
|
/*
|
|
* A duplicate edges are all edges the "right" edge since due to rotational
|
|
* symmetry "left" edge=="right" edge
|
|
* */
|
|
std::unordered_set<size_t> duplicateEdges;
|
|
std::vector<size_t> nodesEdge0; // left edge
|
|
std::vector<size_t> nodesEdge1; // bottom edge
|
|
std::vector<size_t> 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<vcg::Point2i> TopologyEnumerator::getValidEdges(
|
|
const std::vector<size_t>& numberOfNodesPerSlot,
|
|
const std::filesystem::path& resultsPath,
|
|
const PatternGeometry& patternGeometryAllEdges,
|
|
const std::vector<vcg::Point2i>& allPossibleEdges) {
|
|
std::unordered_set<size_t> 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<vcg::Point3d> edgeVertices;
|
|
edgeVertices.push_back(p0);
|
|
edgeVertices.push_back(p1);
|
|
singleEdgeMesh.add(edgeVertices);
|
|
singleEdgeMesh.add(std::vector<vcg::Point2i>{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<size_t> 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<PatternGeometry>::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<vcg::Point2i> 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<size_t>& numberOfNodesPerSlot,
|
|
const size_t& numberOfDesiredEdges,
|
|
const std::filesystem::path& resultsPath,
|
|
const std::vector<vcg::Point3d>& allVertices,
|
|
const std::unordered_map<size_t, std::unordered_set<size_t>>&
|
|
intersectingEdges,
|
|
const std::vector<vcg::Point2i>& validEdges,
|
|
const std::unordered_set<VertexIndex>& interfaceNodes) {
|
|
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;
|
|
constexpr bool exportTilledPattern = false;
|
|
constexpr bool saveCompressedFormat = false;
|
|
do {
|
|
patternIndex++;
|
|
const std::string patternName = std::to_string(numberOfDesiredEdges) + "_" +
|
|
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<vcg::Point2i> 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];
|
|
}
|
|
}
|
|
|
|
// DEBUG: Export only part of the pattern set
|
|
// if (statistics.numberOfValidPatterns > 1e4) {
|
|
// break;
|
|
// }
|
|
// const bool patternContainsCentroid =
|
|
// std::find_if(patternEdges.begin(), patternEdges.end(),
|
|
// [](const vcg::Point2i& edge) {
|
|
// if (edge[0] == 0 || edge[1] == 0) {
|
|
// return true;
|
|
// }
|
|
// return false;
|
|
// }) != patternEdges.end();
|
|
// if (!patternContainsCentroid) {
|
|
// continue;
|
|
// }
|
|
|
|
PatternGeometry patternGeometry;
|
|
patternGeometry.add(allVertices, patternEdges);
|
|
patternGeometry.setLabel(patternName);
|
|
|
|
#ifdef POLYSCOPE_DEFINED
|
|
// 1st example
|
|
// const bool shouldBreak =
|
|
// patternIndex == 1970494; // 398 const bool shouldBreak =
|
|
// patternBinaryRepresentation == "10000010101110110";//13036 const
|
|
// bool shouldBreak = patternBinaryRepresentation ==
|
|
// "00010111000010100"; //2481 const bool shouldBreak =
|
|
// patternBinaryRepresentation == "10000101100110010"; //12116 const
|
|
// bool shouldBreak = patternBinaryRepresentation ==
|
|
// "10010111000000110"; //13915
|
|
// 2nd example
|
|
// const bool shouldBreak = patternBinaryRepresentation ==
|
|
// "00001011100010011"; //7_1203 const bool shouldBreak =
|
|
// patternBinaryRepresentation == "00110001100100111"; //4865 const
|
|
// bool shouldBreak = patternBinaryRepresentation ==
|
|
// "00010000101000110"; //1380 const bool shouldBreak =
|
|
// patternBinaryRepresentation == "00000010100010111"; //268
|
|
// 3rd
|
|
// const bool shouldBreak = patternBinaryRepresentation ==
|
|
// "10011011100000010"; //14272 const bool shouldBreak =
|
|
// patternBinaryRepresentation == "10000111100110110"; //11877 const
|
|
// bool shouldBreak = patternBinaryRepresentation ==
|
|
// "00001011100010011"; //1203 const bool shouldBreak =
|
|
// patternBinaryRepresentation == "00010101000110000"; //12117
|
|
|
|
// const bool shouldBreak = patternBinaryRepresentation ==
|
|
// "10000101100110100"; //12117
|
|
// std::thread drawingThread([&]() {
|
|
// if (shouldBreak) {
|
|
// patternGeometry.registerForDrawing();
|
|
// polyscope::show();
|
|
// patternGeometry.unregister();
|
|
// }
|
|
// });
|
|
#endif
|
|
|
|
// Check if pattern contains intersecting edges
|
|
const bool isInterfaceConnected =
|
|
patternGeometry.isInterfaceConnected(interfaceNodes);
|
|
// Export the tiled ply file if it contains intersecting edges
|
|
if (!isInterfaceConnected) {
|
|
// create the tiled geometry of the pattern
|
|
statistics.numberOfPatternViolatingInterfaceEnforcement++;
|
|
if (debugIsOn) {
|
|
if (savePlyFiles) {
|
|
exportPattern(
|
|
std::filesystem::path(resultsPath).append("InterfaceEnforcement"),
|
|
patternGeometry, exportTilledPattern);
|
|
}
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// const bool shouldBreak = numberOfDesiredEdges == 4 && patternIndex
|
|
// == 53;
|
|
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.isFullyConnectedWhenFanned();
|
|
|
|
PatternGeometry fanPatternGeometry =
|
|
PatternGeometry::createFan(patternGeometry);
|
|
const int interfaceNodeVi = 3;
|
|
std::vector<PatternGeometry::EdgeType*> connectedEdges;
|
|
vcg::edge::VEStarVE(&fanPatternGeometry.vert[interfaceNodeVi],
|
|
connectedEdges);
|
|
if (!connectedEdges.empty()) {
|
|
for (int i = 1; i < 6; i++) {
|
|
vcg::tri::Allocator<PatternGeometry>::AddEdge(
|
|
fanPatternGeometry,
|
|
interfaceNodeVi + (i - 1) * patternGeometry.VN(),
|
|
interfaceNodeVi + i * patternGeometry.VN());
|
|
}
|
|
}
|
|
vcg::tri::Clean<PatternGeometry>::MergeCloseVertex(fanPatternGeometry,
|
|
0.0000005);
|
|
vcg::tri::Allocator<PatternGeometry>::CompactEveryVector(
|
|
fanPatternGeometry);
|
|
vcg::tri::UpdateTopology<PatternGeometry>::VertexEdge(fanPatternGeometry);
|
|
vcg::tri::UpdateTopology<PatternGeometry>::EdgeEdge(fanPatternGeometry);
|
|
// for (PatternGeometry::VertexType &v : tilledPatternGeometry.vert)
|
|
// {
|
|
// std::vector<PatternGeometry::EdgeType *> connectedEdges;
|
|
// vcg::edge::VEStarVE(&v, connectedEdges);
|
|
// if (connectedEdges.size() == 1) {
|
|
// vcg::tri::Allocator<PatternGeometry>::DeleteVertex(tilledPatternGeometry,
|
|
// v);
|
|
// vcg::tri::Allocator<PatternGeometry>::DeleteEdge(tilledPatternGeometry,
|
|
// *connectedEdges[0]);
|
|
// }
|
|
// }
|
|
// //
|
|
// vcg::tri::Allocator<PatternGeometry>::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<vertex_t> articulationPoints;
|
|
boost::articulation_points(fanPatternGraph,
|
|
std::back_inserter(articulationPoints));
|
|
const bool hasArticulationPoints = !articulationPoints.empty();
|
|
// if (!hasArticulationPoints && tiledPatternHasDanglingEdges) {
|
|
// PatternGeometry tilledPatternGeometry =
|
|
// PatternGeometry::createTile(patternGeometry);
|
|
// tilledPatternGeometry.updateEigenEdgeAndVertices();
|
|
// tilledPatternGeometry.registerForDrawing();
|
|
// // ->addNodeColorQuantity("de_noAp_tilled",
|
|
// fanVertexColors)
|
|
// // ->setEnabled(true);
|
|
// polyscope::show();
|
|
// tilledPatternGeometry.unregister();
|
|
// }
|
|
// if (hasArticulationPoints && !tiledPatternHasDanglingEdges/*&&
|
|
// !patternContainsIntersectingEdges
|
|
// && !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<glm::vec3>
|
|
// 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<std::pair<int, PatternGeometry::EdgePointer>> eCC;
|
|
vcg::tri::Clean<PatternGeometry>::edgeMeshConnectedComponents(
|
|
tiledPatternGeometry, eCC);
|
|
vcg::tri::UpdateFlags<PatternGeometry>::EdgeClear(
|
|
tiledPatternGeometry);
|
|
const size_t numberOfCC_edgeBased = eCC.size();
|
|
std::sort(eCC.begin(), eCC.end(),
|
|
[](const std::pair<int, PatternGeometry::EdgePointer>& a,
|
|
const std::pair<int, PatternGeometry::EdgePointer>& b) {
|
|
return a.first > b.first;
|
|
});
|
|
|
|
PatternGeometry::EdgePointer& ep = eCC[0].second;
|
|
size_t colorsRegistered = 0;
|
|
std::stack<EdgePointer> stack;
|
|
stack.push(ep);
|
|
while (!stack.empty()) {
|
|
EdgePointer ep = stack.top();
|
|
stack.pop();
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
vcg::edge::VEIterator<PatternGeometry::EdgeType> 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 &&
|
|
isInterfaceConnected
|
|
/*&& !tiledPatternHasDanglingEdges*/
|
|
&& !hasFloatingComponents && !hasArticulationPoints &&
|
|
!tiledPatternHasNodeWithValenceGreaterThanDesired &&
|
|
!tiledPatternHasEdgesWithAngleSmallerThanThreshold;
|
|
// if (!hasArticulationPoints && !patternContainsIntersectingEdges
|
|
// && !tiledPatternHasDanglingEdges && !hasFloatingComponents
|
|
// && !tiledPatternHasNodeWithValenceGreaterThanDesired
|
|
// && tiledPatternHasEdgesWithAngleSmallerThanThreshold &&
|
|
// numberOfDesiredEdges > 4) { std::cout << "Pattern found:" <<
|
|
// patternName << std::endl;
|
|
// patternGeometry.registerForDrawing();
|
|
// polyscope::show();
|
|
// patternGeometry.unregister();
|
|
// }
|
|
if (isValidPattern) {
|
|
// if(patternName=='2055'){
|
|
// PatternGeometry tiledPatternGeometry =
|
|
// PatternGeometry::createTile(
|
|
// patternGeometry); // the marked nodes of
|
|
// hasDanglingEdges are
|
|
// tiledPatternGeometry.registerForDrawing(RGBColor{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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// if (drawingThread.joinable()) {
|
|
// drawingThread.join();
|
|
// }
|
|
|
|
// 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<size_t> TopologyEnumerator::getCoincideEdges(
|
|
const std::vector<size_t>& edgeNodeIndices) const {
|
|
std::vector<size_t> 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<vcg::Point2i>& validEdges,
|
|
const size_t& numberOfDesiredEdges) const {
|
|
return true;
|
|
}
|