#include "trianglepatterngeometry.hpp" #include "trianglepattterntopology.hpp" #include #include #include #include #include #include #include size_t FlatPatternGeometry::computeTiledValence( const size_t &nodeIndex, const std::vector &numberOfNodesPerSlot) const { std::vector connectedEdges; vcg::edge::VEStarVE(&vert[nodeIndex], connectedEdges); const size_t nodeValence = connectedEdges.size(); assert(nodeSlot.count(nodeIndex) != 0); const size_t nodeSlotIndex = nodeSlot.at(nodeIndex); if (nodeSlotIndex == 0) { return nodeValence * fanSize; } else if (nodeSlotIndex == 1 || nodeSlotIndex == 2) { size_t correspondingNodeIndex; if (nodeSlotIndex == 1) { correspondingNodeIndex = nodeIndex + 1; } else { correspondingNodeIndex = nodeIndex - 1; } std::vector connectedEdgesCorrespondingNode; vcg::edge::VEStarVE(&vert[correspondingNodeIndex], connectedEdgesCorrespondingNode); size_t correspondingNodeValence = connectedEdgesCorrespondingNode.size(); return fanSize / 2 * nodeValence + fanSize / 2 * correspondingNodeValence; } else if (nodeSlotIndex == 3 || nodeSlotIndex == 5) { size_t correspondingNodeIndex; size_t numberOfNodesBefore; size_t numberOfNodesAfter; if (nodeSlotIndex == 3) { numberOfNodesBefore = nodeIndex - std::accumulate(numberOfNodesPerSlot.begin(), numberOfNodesPerSlot.begin() + 3, 0); correspondingNodeIndex = std::accumulate(numberOfNodesPerSlot.begin(), numberOfNodesPerSlot.begin() + 6, 0) - 1 - numberOfNodesBefore; } else { numberOfNodesAfter = std::accumulate(numberOfNodesPerSlot.begin(), numberOfNodesPerSlot.begin() + 6, 0) - 1 - nodeIndex; correspondingNodeIndex = numberOfNodesAfter + std::accumulate(numberOfNodesPerSlot.begin(), numberOfNodesPerSlot.begin() + 3, 0); } assert(correspondingNodeIndex < vn); std::vector connectedEdgesCorrespondingNode; vcg::edge::VEStarVE(&vert[correspondingNodeIndex], connectedEdgesCorrespondingNode); size_t correspondingNodeValence = connectedEdgesCorrespondingNode.size(); return nodeValence + correspondingNodeValence; } else if (nodeSlotIndex == 4) { return 2 * nodeValence; } else if (nodeSlotIndex == 6) { return nodeValence; } else { std::cerr << "Error in slot index computation" << std::endl; } assert(false); return 0; } size_t FlatPatternGeometry::getFanSize() const { return fanSize; } double FlatPatternGeometry::getTriangleEdgeSize() const { return triangleEdgeSize; } FlatPatternGeometry::FlatPatternGeometry() {} std::vector FlatPatternGeometry::getVertices() const { std::vector verts(VN()); for (size_t vi = 0; vi < VN(); vi++) { verts[vi] = vert[vi].cP(); } return verts; } FlatPatternGeometry FlatPatternGeometry::createTile(FlatPatternGeometry &pattern) { const size_t fanSize = FlatPatternGeometry().getFanSize(); FlatPatternGeometry fan(createFan(pattern)); FlatPatternGeometry tile(fan); if (fanSize % 2 == 1) { vcg::Matrix44d R; auto rotationAxis = vcg::Point3d(0, 0, 1); R.SetRotateDeg(180, rotationAxis); vcg::tri::UpdatePosition::Matrix(fan, R); } vcg::Matrix44d T; const double centerAngle = 2 * M_PI / fanSize; const double triangleHeight = std::sin((M_PI - centerAngle) / 2) * FlatPatternGeometry().triangleEdgeSize; T.SetTranslate(0, -2 * triangleHeight, 0); vcg::tri::UpdatePosition::Matrix(fan, T); FlatPatternGeometry fanOfFan = createFan(fan); vcg::tri::Append::Mesh(tile, fanOfFan); vcg::tri::Clean::MergeCloseVertex(tile, 0.0000005); vcg::tri::Allocator::CompactEveryVector(tile); vcg::tri::UpdateTopology::VertexEdge(tile); vcg::tri::UpdateTopology::EdgeEdge(tile); for (size_t vi = 0; vi < pattern.vn; vi++) { tile.vert[vi].C() = vcg::Color4b::Blue; } return tile; } FlatPatternGeometry FlatPatternGeometry::createFan(FlatPatternGeometry &pattern) { const size_t fanSize = FlatPatternGeometry().getFanSize(); FlatPatternGeometry fan(pattern); FlatPatternGeometry rotatedPattern(pattern); for (int rotationCounter = 1; rotationCounter < fanSize; rotationCounter++) { vcg::Matrix44d R; auto rotationAxis = vcg::Point3d(0, 0, 1); R.SetRotateDeg(360 / fanSize, rotationAxis); vcg::tri::UpdatePosition::Matrix(rotatedPattern, R); vcg::tri::Append::Mesh( fan, rotatedPattern); } return fan; } FlatPatternGeometry::FlatPatternGeometry(FlatPatternGeometry &other) { vcg::tri::Append::MeshCopy(*this, other); this->vertices = other.getVertices(); vcg::tri::UpdateTopology::VertexEdge(*this); vcg::tri::UpdateTopology::EdgeEdge(*this); } bool FlatPatternGeometry::savePly(const std::string plyFilename) { int returnValue = vcg::tri::io::ExporterPLY::Save( *this, plyFilename.c_str(), vcg::tri::io::Mask::IOM_EDGEINDEX | vcg::tri::io::Mask::IOM_VERTCOLOR, false); if (returnValue != 0) { std::cerr << vcg::tri::io::ExporterPLY::ErrorMsg( returnValue) << std::endl; } return static_cast(returnValue); } void FlatPatternGeometry::add(const std::vector &vertices) { this->vertices = vertices; std::for_each(vertices.begin(), vertices.end(), [&](const vcg::Point3d &p) { vcg::tri::Allocator::AddVertex(*this, p); }); vcg::tri::UpdateTopology::VertexEdge(*this); vcg::tri::UpdateTopology::EdgeEdge(*this); updateEigenEdgeAndVertices(); } void FlatPatternGeometry::add(const std::vector &edges) { std::for_each(edges.begin(), edges.end(), [&](const vcg::Point2i &e) { vcg::tri::Allocator::AddEdge(*this, e[0], e[1]); }); vcg::tri::UpdateTopology::VertexEdge(*this); vcg::tri::UpdateTopology::EdgeEdge(*this); updateEigenEdgeAndVertices(); } void FlatPatternGeometry::add(const std::vector &vertices, const std::vector &edges) { add(vertices); add(edges); updateEigenEdgeAndVertices(); } void FlatPatternGeometry::add(const std::vector &numberOfNodesPerSlot, const std::vector &edges) { assert(numberOfNodesPerSlot.size() == 7); auto vertices = constructVertexVector(numberOfNodesPerSlot, fanSize, triangleEdgeSize); add(vertices, edges); vcg::tri::UpdateTopology::VertexEdge(*this); vcg::tri::UpdateTopology::EdgeEdge(*this); updateEigenEdgeAndVertices(); } std::vector FlatPatternGeometry::constructVertexVector( const std::vector &numberOfNodesPerSlot, const size_t &fanSize, const double &triangleEdgeSize) { std::vector vertices; const double centerAngle = 2 * M_PI / fanSize; const double triangleHeight = std::sin((M_PI - centerAngle) / 2) * triangleEdgeSize; const double baseEdgeSize = 2 * triangleEdgeSize * std::cos((M_PI - centerAngle) / 2); const vcg::Point3d triangleV0 = vcg::Point3d(0, 0, 0); const vcg::Point3d triangleV1 = vcg::Point3d(-baseEdgeSize / 2, -triangleHeight, 0); const vcg::Point3d triangleV2 = vcg::Point3d(baseEdgeSize / 2, -triangleHeight, 0); // Nodes if (numberOfNodesPerSlot[0] == 1) { // vertices[0] = triangleV0; vertices.push_back(triangleV0); } if (numberOfNodesPerSlot[1] == 1) { // vertices[1] = triangleV1; vertices.push_back(triangleV1); } if (numberOfNodesPerSlot[2] == 1) { // vertices[2] = triangleV2; vertices.push_back(triangleV2); } // Edges if (numberOfNodesPerSlot[3] != 0) { const double offset0 = 1.0 / (numberOfNodesPerSlot[3] + 1); const vcg::Point3d edgeVector0(triangleV1 - triangleV0); for (size_t vertexIndex = 0; vertexIndex < numberOfNodesPerSlot[3]; vertexIndex++) { // vertices[std::accumulate(numberOfNodesPerSlot.begin(), // numberOfNodesPerSlot.begin() + 2, 0) + // vertexIndex] = vertices.push_back(triangleV0 + edgeVector0.operator*((vertexIndex + 1) * offset0)); } } if (numberOfNodesPerSlot[4] == 1) { vertices.push_back(vcg::Point3d(0, -triangleHeight, 0)); } else if (numberOfNodesPerSlot[4] != 0) { const double offset1 = 1.0 / (numberOfNodesPerSlot[4] + 1); const vcg::Point3d edgeVector1(triangleV2 - triangleV1); for (size_t vertexIndex = 0; vertexIndex < numberOfNodesPerSlot[4]; vertexIndex++) { // vertices[std::accumulate(numberOfNodesPerSlot.begin(), // numberOfNodesPerSlot.begin() + 3, 0) + // vertexIndex] = vertices.push_back(triangleV1 + edgeVector1.operator*((vertexIndex + 1) * offset1)); } } if (numberOfNodesPerSlot[5] != 0) { const double offset2 = 1.0 / (numberOfNodesPerSlot[5] + 1); const vcg::Point3d edgeVector2(triangleV0 - triangleV2); for (size_t vertexIndex = 0; vertexIndex < numberOfNodesPerSlot[5]; vertexIndex++) { // vertices[std::accumulate(numberOfNodesPerSlot.begin(), // numberOfNodesPerSlot.begin() + 4, 0) + // vertexIndex] = vertices.push_back(triangleV2 + edgeVector2.operator*((vertexIndex + 1) * offset2)); } } // Face if (numberOfNodesPerSlot[6] != 0) { const vcg::Point3d triangleCenter((triangleV0 + triangleV1 + triangleV2) / 3); const double radius = 0.1; const double offsetRad = 2 * M_PI / numberOfNodesPerSlot[6]; for (size_t vertexIndex = 0; vertexIndex < numberOfNodesPerSlot[6]; vertexIndex++) { const double pX = triangleCenter[0] + radius * std::cos(vertexIndex * offsetRad); const double pY = triangleCenter[1] + radius * std::sin(vertexIndex * offsetRad); /*vertices[std::accumulate(numberOfNodesPerSlot.begin(), numberOfNodesPerSlot.begin() + 5, 0) + vertexIndex] =*/ vertices.push_back(vcg::Point3d(pX, pY, 0)); } } return vertices; } void FlatPatternGeometry::constructNodeToTiledValenceMap( const std::vector &numberOfNodesPerSlot) { for (size_t nodeIndex = 0; nodeIndex < vn; nodeIndex++) { const size_t tiledValence = computeTiledValence(nodeIndex, numberOfNodesPerSlot); nodeTiledValence[nodeIndex] = tiledValence; } } bool FlatPatternGeometry::hasDanglingEdges( const std::vector &numberOfNodesPerSlot) { if (nodeSlot.empty()) { FlatPatternTopology::constructNodeToSlotMap(numberOfNodesPerSlot, nodeSlot); } if (correspondingNode.empty()) { constructCorresponginNodeMap(numberOfNodesPerSlot); } if (nodeTiledValence.empty()) { constructNodeToTiledValenceMap(numberOfNodesPerSlot); } bool hasDanglingEdges = false; for (size_t nodeIndex = 0; nodeIndex < vn; nodeIndex++) { const size_t tiledValence = nodeTiledValence[nodeIndex]; if (tiledValence == 1) { vert[nodeIndex].C() = vcg::Color4b::Red; hasDanglingEdges = true; } } return hasDanglingEdges; } bool FlatPatternGeometry::hasUntiledDanglingEdges() { // vcg::tri::Clean::MergeCloseVertex(*this, // 0.0000005); // vcg::tri::Allocator::CompactEveryVector(*this); // vcg::tri::UpdateTopology::VertexEdge(*this); // vcg::tri::UpdateTopology::EdgeEdge(*this); bool hasDanglingEdges = false; for (size_t vi = 0; vi < vn; vi++) { std::vector connectedEdges; vcg::edge::VEStarVE(&vert[vi], connectedEdges); const size_t nodeValence = connectedEdges.size(); if (nodeValence == 1) { if (vert[vi].C().operator==(vcg::Color4b(vcg::Color4b::Red))) { } else { vert[vi].C() = vcg::Color4b::Blue; } hasDanglingEdges = true; } } return hasDanglingEdges; } // TODO: The function expects that the numberOfNodesPerSlot follows a specific // format and that the vertex container was populated in a particular order. void FlatPatternGeometry::constructCorresponginNodeMap( const std::vector &numberOfNodesPerSlot) { assert(vn != 0 && !nodeSlot.empty() && correspondingNode.empty() && numberOfNodesPerSlot.size() == 7); for (size_t nodeIndex = 0; nodeIndex < vn; nodeIndex++) { const size_t slotIndex = nodeSlot[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); } } } bool FlatPatternGeometry::isFullyConnectedWhenTiled() { assert(vn != 0 /* && !correspondingNode.empty()*/); // TrianglePatternGeometry copyOfPattern(*this); // If bottom interface nodes have a valence of zero there definetely more than // a single cc bool bottomInterfaceIsConnected = false; for (size_t nodeIndex = 0; nodeIndex < vn; nodeIndex++) { if (nodeSlot[nodeIndex] == 1 || nodeSlot[nodeIndex] == 4 || nodeSlot[nodeIndex] == 2) { std::vector connectedEdges; vcg::edge::VEStarVE(&vert[nodeIndex], connectedEdges); const size_t nodeValence = connectedEdges.size(); if (nodeValence != 0) { bottomInterfaceIsConnected = true; break; } } } if (!bottomInterfaceIsConnected) { return false; } FlatPatternGeometry fanedPattern = createFan(*this); vcg::tri::Clean::MergeCloseVertex(fanedPattern, 0.000000005); vcg::tri::Allocator::CompactEveryVector(fanedPattern); vcg::tri::UpdateTopology::VertexEdge(fanedPattern); vcg::tri::UpdateTopology::EdgeEdge(fanedPattern); std::vector> eCC; vcg::tri::Clean::edgeMeshConnectedComponents( fanedPattern, eCC); const bool sideInterfaceIsConnected = 1 == eCC.size(); if (!sideInterfaceIsConnected) { return false; } // for (size_t nodeIndex = 0; nodeIndex < vn; nodeIndex++) { // if (nodeTiledValence[nodeIndex] == 0) // continue; // const size_t slotIndex = nodeSlot[nodeIndex]; // // connect nodes belonging to first or third slot(nodes on the first // // triangle edge) // // if (nodeTiledValence[nodeIndex] == 0 && slotIndex == 3) { // // continue; // // } // if (slotIndex == 1 || slotIndex == 3) { // assert(correspondingNode.count(nodeIndex) != 0); // const size_t correspondingNodeIndex = // correspondingNode[nodeIndex]; // std::vector starEdges; // vcg::edge::VEStarVE(&vert[nodeIndex], starEdges); // bool containsEdge = false; // for (TrianglePatternGeometry::EdgeType *e : starEdges) { // assert(vcg::tri::Index(*this, e->V(0)) == nodeIndex || // vcg::tri::Index(*this, e->V(1)) == nodeIndex); // if (vcg::tri::Index(*this, e->V(0)) == nodeIndex) { // if (vcg::tri::Index(*this, e->V(1)) == correspondingNodeIndex) { // containsEdge = true; // break; // } // } else if (vcg::tri::Index(*this, e->V(1)) == nodeIndex) { // if (vcg::tri::Index(*this, e->V(0)) == correspondingNodeIndex) { // containsEdge = true; // break; // } // } // } // if (!containsEdge) { // vcg::tri::Allocator::AddEdge( // copyOfPattern, nodeIndex, correspondingNodeIndex); // } // } else if (slotIndex == 2 || slotIndex == 5) { // assert(correspondingNode.count(nodeIndex) != 0); // } else { // assert(correspondingNode.count(nodeIndex) == 0); // } // } // std::vector> eCC; // vcg::tri::Clean::edgeMeshConnectedComponents( // copyOfPattern, eCC); // size_t numberOfCC_edgeBased = eCC.size(); // size_t numberOfCC_vertexBased = numberOfCC_edgeBased; // if (numberOfCC_edgeBased == 1) { // vcg::tri::UpdateTopology::VertexEdge( // copyOfPattern); // vcg::tri::UpdateTopology::EdgeEdge(copyOfPattern); // vcg::tri::UpdateFlags::VertexSetV(copyOfPattern); // vcg::tri::UpdateFlags::VertexClear(copyOfPattern); // for (size_t ei = 0; ei < copyOfPattern.EN(); ei++) { // copyOfPattern.edge[ei].V(0)->SetV(); // copyOfPattern.edge[ei].V(1)->SetV(); // } // for (size_t vi = 0; vi < copyOfPattern.VN(); vi++) { // if (!copyOfPattern.vert[vi].IsV()) { // numberOfCC_vertexBased++; // } // } // return numberOfCC_vertexBased; // } // return numberOfCC_edgeBased == 1; // TODO: not good return true; } bool FlatPatternGeometry::hasIntersectingEdges( const std::string &patternBinaryRepresentation, const std::unordered_map> &intersectingEdges) { bool containsIntersectingEdges = false; for (size_t validEdgeIndex = 0; validEdgeIndex < patternBinaryRepresentation.size(); validEdgeIndex++) { if (patternBinaryRepresentation[validEdgeIndex] == '1' && intersectingEdges.count(validEdgeIndex) != 0) { for (auto edgeIndexIt = intersectingEdges.find(validEdgeIndex)->second.begin(); edgeIndexIt != intersectingEdges.find(validEdgeIndex)->second.end(); edgeIndexIt++) { if (patternBinaryRepresentation[*edgeIndexIt] == '1') { containsIntersectingEdges = true; // statistics.numberOfIntersectingEdgesOverAllPatterns++; break; // should be uncommented in order to improve // performance } } if (containsIntersectingEdges) { break; // should be uncommented in order to improve performance } } } return containsIntersectingEdges; } std::unordered_map> FlatPatternGeometry::getIntersectingEdges( size_t &numberOfIntersectingEdgePairs) const { std::unordered_map> intersectingEdges; numberOfIntersectingEdgePairs = 0; size_t numberOfEdgePairs; if (en == 0 || en == 1) { numberOfEdgePairs = 0; } else { numberOfEdgePairs = binomialCoefficient(en, 2); } for (size_t edgePairIndex = 0; edgePairIndex < numberOfEdgePairs; edgePairIndex++) { size_t ei0 = en - 2 - floor(sqrt(-8 * edgePairIndex + 4 * en * (en - 1) - 7) / 2.0 - 0.5); size_t ei1 = edgePairIndex + ei0 + 1 - en * (en - 1) / 2 + (en - ei0) * ((en - ei0) - 1) / 2; const vcg::Point2d p0(edge[ei0].cP(0)[0], edge[ei0].cP(0)[1]); const float epsilon = 0.000005; vcg::Box2d bb0(p0 - vcg::Point2d(epsilon, epsilon), p0 + vcg::Point2d(epsilon, epsilon)); const vcg::Point2d p1(edge[ei0].cP(1)[0], edge[ei0].cP(1)[1]); vcg::Box2d bb1(p1 - vcg::Point2d(epsilon, epsilon), p1 + vcg::Point2d(epsilon, epsilon)); const vcg::Point2d p2(edge[ei1].cP(0)[0], edge[ei1].cP(0)[1]); vcg::Box2d bb2(p2 - vcg::Point2d(epsilon, epsilon), p2 + vcg::Point2d(epsilon, epsilon)); if (bb2.Collide(bb1) || bb2.Collide(bb0)) continue; const vcg::Point2d p3(edge[ei1].cP(1)[0], edge[ei1].cP(1)[1]); vcg::Box2d bb3(p3 - vcg::Point2d(epsilon, epsilon), p3 + vcg::Point2d(epsilon, epsilon)); if (bb3.Collide(bb1) || bb3.Collide(bb0)) continue; const vcg::Segment2d s0(p0, p1); const vcg::Segment2d s1(p2, p3); vcg::Point2d intersectionPoint; const bool edgesIntersect = vcg::SegmentSegmentIntersection(s0, s1, intersectionPoint); if (edgesIntersect) { numberOfIntersectingEdgePairs++; intersectingEdges[ei0].insert(ei1); intersectingEdges[ei1].insert(ei0); } } return intersectingEdges; }