#include "flatpattern.hpp" #include "trianglepatterngeometry.hpp" #include FlatPattern::FlatPattern() {} FlatPattern::FlatPattern(const string &filename, bool addNormalsIfAbsent) { assert(std::filesystem::exists(std::filesystem::path(filename))); loadFromPly(filename); if (addNormalsIfAbsent) { bool normalsAreAbsent = vert[0].cN().Norm() < 0.000001; if (normalsAreAbsent) { for (auto &v : vert) { v.N() = CoordType(0, 0, 1); } } } vcg::tri::UpdateTopology::VertexEdge(*this); scale(); updateEigenEdgeAndVertices(); } FlatPattern::FlatPattern(const std::vector &numberOfNodesPerSlot, const std::vector &edges) { add(numberOfNodesPerSlot, edges); // add normals bool normalsAreAbsent = vert[0].cN().Norm() < 0.000001; if (normalsAreAbsent) { for (auto &v : vert) { v.N() = CoordType(0, 0, 1); } } scale(); updateEigenEdgeAndVertices(); } bool FlatPattern::createHoneycombAtom() { VCGEdgeMesh honeycombQuarter; const VCGEdgeMesh::CoordType n(0, 0, 1); const double H = 0.2; const double height = 1.5 * H; const double width = 0.2; const double theta = 70; const double dy = tan(vcg::math::ToRad(90 - theta)) * width / 2; vcg::tri::Allocator::AddVertex( honeycombQuarter, VCGEdgeMesh::CoordType(0, height / 2, 0), n); vcg::tri::Allocator::AddVertex( honeycombQuarter, VCGEdgeMesh::CoordType(0, H / 2 - dy, 0), n); vcg::tri::Allocator::AddVertex( honeycombQuarter, VCGEdgeMesh::CoordType(width / 2, H / 2, 0), n); vcg::tri::Allocator::AddVertex( honeycombQuarter, VCGEdgeMesh::CoordType(width / 2, 0, 0), n); vcg::tri::Allocator::AddEdge(honeycombQuarter, 0, 1); vcg::tri::Allocator::AddEdge(honeycombQuarter, 1, 2); vcg::tri::Allocator::AddEdge(honeycombQuarter, 2, 3); VCGEdgeMesh honeycombAtom; // Top right vcg::tri::Append::MeshCopy(honeycombAtom, honeycombQuarter); // Bottom right vcg::Matrix44d rotM; rotM.SetRotateDeg(180, vcg::Point3d(1, 0, 0)); vcg::tri::UpdatePosition::Matrix(honeycombQuarter, rotM); vcg::tri::Append::Mesh(honeycombAtom, honeycombQuarter); // Bottom left rotM.SetRotateDeg(180, vcg::Point3d(0, 1, 0)); vcg::tri::UpdatePosition::Matrix(honeycombQuarter, rotM); vcg::tri::Append::Mesh(honeycombAtom, honeycombQuarter); // Top left rotM.SetRotateDeg(180, vcg::Point3d(1, 0, 0)); vcg::tri::UpdatePosition::Matrix(honeycombQuarter, rotM); vcg::tri::Append::Mesh(honeycombAtom, honeycombQuarter); for (VertexType &v : honeycombAtom.vert) { v.P()[2] = 0; } return true; } void FlatPattern::deleteDanglingEdges() { for (VertexType &v : vert) { std::vector incidentElements; vcg::edge::VEStarVE(&v, incidentElements); if (incidentElements.size() == 1) { vcg::tri::Allocator::DeleteEdge(*this, *incidentElements[0]); } if (incidentElements.size() == 1) { vcg::tri::Allocator::DeleteVertex(*this, v); } } vcg::tri::Clean::RemoveDegenerateVertex(*this); vcg::tri::Clean::RemoveDegenerateEdge(*this); vcg::tri::Allocator::CompactEveryVector(*this); } void FlatPattern::scale() { const double baseTriangleCentralEdgeSize = (vert[0].cP() - vert[3].cP()).Norm(); const double scaleRatio = desiredBaseTriangleCentralEdgeSize / baseTriangleCentralEdgeSize; vcg::tri::UpdatePosition::Scale(*this, scaleRatio); } void FlatPattern::deleteDanglingVertices() { vcg::tri::Allocator::PointerUpdater pu; deleteDanglingVertices(pu); } void FlatPattern::deleteDanglingVertices( vcg::tri::Allocator::PointerUpdater &pu) { for (VertexType &v : vert) { std::vector incidentElements; vcg::edge::VEStarVE(&v, incidentElements); if (incidentElements.size() == 0) { vcg::tri::Allocator::DeleteVertex(*this, v); } } vcg::tri::Allocator::CompactVertexVector(*this, pu); vcg::tri::Allocator::CompactEdgeVector(*this); updateEigenEdgeAndVertices(); } void FlatPattern::tilePattern(VCGEdgeMesh &pattern, VCGPolyMesh &tileInto, const bool &shouldDeleteDanglingEdges) { const size_t middleIndex = 3; double xOffset = vcg::Distance(pattern.vert[0].cP(), pattern.vert[middleIndex].cP()) / std::tan(M_PI / 3); CoordType patternCoord0 = pattern.vert[0].cP(); CoordType patternBottomRight = pattern.vert[middleIndex].cP() + CoordType(xOffset, 0, 0); CoordType patternBottomLeft = pattern.vert[middleIndex].cP() - CoordType(xOffset, 0, 0); std::vector patternTrianglePoints{ patternCoord0, patternBottomRight, patternBottomLeft}; CoordType pointOnPattern = patternCoord0 + (patternBottomLeft - patternCoord0) ^ (patternBottomRight - patternCoord0); std::vector faceCenters(FN()); VCGTriMesh tileIntoEdgeMesh; for (VCGPolyMesh::FaceType &f : tileInto.face) { std::vector incidentVertices; vcg::face::VFIterator vfi( &f, 0); // initialize the iterator to the first face // vcg::face::Pos p(vfi.F(), f.cV(0)); // vcg::face::VVOrderedStarFF(p, incidentVertices); size_t numberOfNodes = 0; CoordType centerOfFace(0, 0, 0); for (size_t vi = 0; vi < f.VN(); vi++) { numberOfNodes++; centerOfFace = centerOfFace + f.cP(vi); } centerOfFace /= f.VN(); vcg::tri::Allocator::AddVertex(tileIntoEdgeMesh, centerOfFace, vcg::Color4b::Yellow); // const size_t vi = vcg::tri::Index(tileInto, v); // std::cout << "vertex " << vi << " has incident vertices:" << // std::endl; for (size_t vi = 0; vi < f.VN(); vi++) { // size_t f = 0; // std::cout << vcg::tri::Index(tileInto, // / incidentVertices[f]) / << std::endl; // Compute transformation matrix M // vcg::Matrix44d M; std::vector meshTrianglePoints{ centerOfFace, f.cP(vi), vi + 1 == f.VN() ? f.cP(0) : f.cP(vi + 1)}; CoordType faceNormal = ((meshTrianglePoints[1] - meshTrianglePoints[0]) ^ (meshTrianglePoints[2] - meshTrianglePoints[0])) .Normalize(); auto fit = vcg::tri::Allocator::AddFace( tileIntoEdgeMesh, meshTrianglePoints[0], meshTrianglePoints[1], meshTrianglePoints[2]); fit->N() = faceNormal; CoordType pointOnTriMesh = meshTrianglePoints[0] + (meshTrianglePoints[1] - meshTrianglePoints[0]) ^ (meshTrianglePoints[2] - meshTrianglePoints[0]); vcg::Matrix44d M; // vcg::ComputeRigidMatchMatrix(meshTrianglePoints, // patternTrianglePoints, // M); vcg::Matrix44d A_prime; A_prime[0][0] = meshTrianglePoints[0][0]; A_prime[1][0] = meshTrianglePoints[0][1]; A_prime[2][0] = meshTrianglePoints[0][2]; A_prime[3][0] = 1; A_prime[0][1] = meshTrianglePoints[1][0]; A_prime[1][1] = meshTrianglePoints[1][1]; A_prime[2][1] = meshTrianglePoints[1][2]; A_prime[3][1] = 1; A_prime[0][2] = meshTrianglePoints[2][0]; A_prime[1][2] = meshTrianglePoints[2][1]; A_prime[2][2] = meshTrianglePoints[2][2]; A_prime[3][2] = 1; A_prime[0][3] = pointOnTriMesh[0]; A_prime[1][3] = pointOnTriMesh[1]; A_prime[2][3] = pointOnTriMesh[2]; A_prime[3][3] = 1; vcg::Matrix44d A; A[0][0] = patternTrianglePoints[0][0]; A[1][0] = patternTrianglePoints[0][1]; A[2][0] = patternTrianglePoints[0][2]; A[3][0] = 1; A[0][1] = patternTrianglePoints[1][0]; A[1][1] = patternTrianglePoints[1][1]; A[2][1] = patternTrianglePoints[1][2]; A[3][1] = 1; A[0][2] = patternTrianglePoints[2][0]; A[1][2] = patternTrianglePoints[2][1]; A[2][2] = patternTrianglePoints[2][2]; A[3][2] = 1; A[0][3] = pointOnPattern[0]; A[1][3] = pointOnPattern[1]; A[2][3] = pointOnPattern[2]; A[3][3] = 1; M = A_prime * vcg::Inverse(A); VCGEdgeMesh transformedPattern; vcg::tri::Append::MeshCopy(transformedPattern, pattern); vcg::tri::UpdatePosition::Matrix(transformedPattern, M); for (VertexType &v : transformedPattern.vert) { v.N() = faceNormal; } vcg::tri::Append::Mesh(*this, transformedPattern); } } // vcg::tri::Clean::MergeCloseVertex(*this, 0.0000000001); // vcg::tri::Clean::RemoveDegenerateVertex(*this); // vcg::tri::Clean::RemoveDegenerateEdge(*this); // vcg::tri::Allocator::CompactEveryVector(*this); vcg::tri::UpdateTopology::VertexEdge(*this); vcg::tri::Clean::MergeCloseVertex(*this, 0.0000000001); deleteDanglingVertices(); deleteDanglingEdges(); vcg::tri::Clean::RemoveDegenerateVertex(*this); vcg::tri::Clean::RemoveDegenerateEdge(*this); vcg::tri::Allocator::CompactEveryVector(*this); updateEigenEdgeAndVertices(); savePly("tiledPattern.ply"); vcg::tri::Clean::MergeCloseVertex(tileIntoEdgeMesh, 0.0000000001); vcg::tri::Clean::RemoveDegenerateVertex(tileIntoEdgeMesh); vcg::tri::Clean::RemoveDegenerateEdge(tileIntoEdgeMesh); tileIntoEdgeMesh.savePly("tileIntoTriMesh.ply"); } void FlatPattern::createFan(const size_t &fanSize) { FlatPattern rotatedPattern; vcg::tri::Append::MeshCopy(rotatedPattern, *this); 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(*this, rotatedPattern); } removeDuplicateVertices(); updateEigenEdgeAndVertices(); } void FlatPattern::removeDuplicateVertices() { vcg::tri::Clean::MergeCloseVertex(*this, 0.0000000001); vcg::tri::Clean::RemoveDegenerateVertex(*this); vcg::tri::Clean::RemoveDegenerateEdge(*this); vcg::tri::Allocator::CompactEveryVector(*this); vcg::tri::UpdateTopology::VertexEdge(*this); } void FlatPattern::tilePattern(VCGEdgeMesh &pattern, VCGTriMesh &tileInto) { const size_t middleIndex = 3; double xOffset = vcg::Distance(pattern.vert[0].cP(), pattern.vert[middleIndex].cP()) / std::tan(M_PI / 3); CoordType patternCoord0 = pattern.vert[0].cP(); CoordType patternBottomRight = pattern.vert[middleIndex].cP() + CoordType(xOffset, 0, 0); CoordType patternBottomLeft = pattern.vert[middleIndex].cP() - CoordType(xOffset, 0, 0); std::vector patternTrianglePoints{ patternCoord0, patternBottomRight, patternBottomLeft}; CoordType pointOnPattern = patternCoord0 + (patternBottomLeft - patternCoord0) ^ (patternBottomRight - patternCoord0); for (VCGTriMesh::VertexType &v : tileInto.vert) { const auto centralNodeColor = vcg::Color4(64, 64, 64, 255); const bool isCentralNode = v.cC() == centralNodeColor; if (isCentralNode) { std::vector incidentVertices; vcg::face::VFIterator vfi( &v); // initialize the iterator tohe first face vcg::face::Pos p(vfi.F(), &v); vcg::face::VVOrderedStarFF(p, incidentVertices); const size_t vi = vcg::tri::Index(tileInto, v); std::cout << "vertex " << vi << " has incident vertices:" << std::endl; for (size_t f = 0; f < incidentVertices.size(); f++) { // size_t f = 0; std::cout << vcg::tri::Index(tileInto, incidentVertices[f]) << std::endl; // Compute transformation matrix M // vcg::Matrix44d M; std::vector meshTrianglePoints{ v.cP(), incidentVertices[f]->cP(), f + 1 == incidentVertices.size() ? incidentVertices[0]->cP() : incidentVertices[f + 1]->cP()}; CoordType faceNormal = ((meshTrianglePoints[1] - meshTrianglePoints[0]) ^ (meshTrianglePoints[2] - meshTrianglePoints[0])) .Normalize(); CoordType pointOnTriMesh = meshTrianglePoints[0] + (meshTrianglePoints[1] - meshTrianglePoints[0]) ^ (meshTrianglePoints[2] - meshTrianglePoints[0]); vcg::Matrix44d M; // vcg::ComputeRigidMatchMatrix(meshTrianglePoints, // patternTrianglePoints, // M); vcg::Matrix44d A_prime; A_prime[0][0] = meshTrianglePoints[0][0]; A_prime[1][0] = meshTrianglePoints[0][1]; A_prime[2][0] = meshTrianglePoints[0][2]; A_prime[3][0] = 1; A_prime[0][1] = meshTrianglePoints[1][0]; A_prime[1][1] = meshTrianglePoints[1][1]; A_prime[2][1] = meshTrianglePoints[1][2]; A_prime[3][1] = 1; A_prime[0][2] = meshTrianglePoints[2][0]; A_prime[1][2] = meshTrianglePoints[2][1]; A_prime[2][2] = meshTrianglePoints[2][2]; A_prime[3][2] = 1; A_prime[0][3] = pointOnTriMesh[0]; A_prime[1][3] = pointOnTriMesh[1]; A_prime[2][3] = pointOnTriMesh[2]; A_prime[3][3] = 1; vcg::Matrix44d A; A[0][0] = patternTrianglePoints[0][0]; A[1][0] = patternTrianglePoints[0][1]; A[2][0] = patternTrianglePoints[0][2]; A[3][0] = 1; A[0][1] = patternTrianglePoints[1][0]; A[1][1] = patternTrianglePoints[1][1]; A[2][1] = patternTrianglePoints[1][2]; A[3][1] = 1; A[0][2] = patternTrianglePoints[2][0]; A[1][2] = patternTrianglePoints[2][1]; A[2][2] = patternTrianglePoints[2][2]; A[3][2] = 1; A[0][3] = pointOnPattern[0]; A[1][3] = pointOnPattern[1]; A[2][3] = pointOnPattern[2]; A[3][3] = 1; M = A_prime * vcg::Inverse(A); VCGEdgeMesh transformedPattern; vcg::tri::Append::MeshCopy(transformedPattern, pattern); vcg::tri::UpdatePosition::Matrix(transformedPattern, M); for (VertexType &v : transformedPattern.vert) { v.N() = faceNormal; } vcg::tri::Append::Mesh(*this, transformedPattern); } } } vcg::tri::UpdateTopology::VertexEdge(*this); deleteDanglingVertices(); deleteDanglingEdges(); vcg::tri::Allocator::CompactEveryVector(*this); updateEigenEdgeAndVertices(); }