Program Listing for File SurfaceMesh.h

Return to documentation for file (include/gamer/SurfaceMesh.h)

/*
 * ***************************************************************************
 * This file is part of the GAMer software.
 * Copyright (C) 2016-2018
 * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon,
 *    and Michael Holst
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * ***************************************************************************
 */

#pragma once

#include <iostream>
#include <stdexcept>
#include <string>
#include <memory>
#include <unordered_set>
#include <utility>

#include <casc/casc>
#include <Eigen/Dense>
#include <Eigen/Eigenvalues>

#include "gamer/Vertex.h"


namespace gamer
{

struct SMGlobal
{
    int marker;
    float volumeConstraint;
    bool useVolumeConstraint;
    bool ishole;

    SMGlobal(int marker = -1, float volumeConstraint = -1, bool useVolumeConstraint = false, bool ishole = false) :
        marker(marker), volumeConstraint(volumeConstraint), useVolumeConstraint(useVolumeConstraint), ishole(ishole) {
    }
};

struct SMVertex : Vertex {
    Vector normal;
    using Vertex::Vertex;
};

struct SMEdge {
    bool selected;

    SMEdge() : SMEdge(0) {
    }

    SMEdge(bool select) : selected(select) {
    }
};

struct SMFaceProperties
{
    int marker;
    bool selected;
    Vector normal;
    SMFaceProperties(int marker, bool selected) :
        marker(marker), selected(selected) {
    }
};

struct SMFace : casc::Orientable, SMFaceProperties
{
    SMFace() : SMFace(Orientable{0}, SMFaceProperties{-1, false}) {
    }

    SMFace(int marker, bool selected) : SMFace(Orientable{0}, SMFaceProperties{marker, selected}) {
    }

    SMFace(int orient, int marker, bool selected) : SMFace(Orientable{orient}, SMFaceProperties{marker, selected}) {
    }

    SMFace(Orientable orient, SMFaceProperties prop)
        : Orientable(orient), SMFaceProperties(prop)
    {
    }

    friend std::ostream& operator<<(std::ostream& output, const SMFace& f)
    {
        output << "SMFace("
               << "m:" << f.marker
               << ";sel:" << std::boolalpha << f.selected
               << ";o:" << f.orientation << ")";
        return output;
    }

    std::string to_string() const
    {
        std::ostringstream output;
        output << *this;
        return output.str();
    }

};


namespace surfmesh_detail
{
struct surfmesh_traits
{
    using KeyType = int;
    using NodeTypes = util::type_holder<SMGlobal, SMVertex, SMEdge, SMFace>;
    using EdgeTypes = util::type_holder<casc::Orientable, casc::Orientable, casc::Orientable>;
};
} // end namespace surfmesh_detail

using SurfaceMesh = casc::simplicial_complex<surfmesh_detail::surfmesh_traits>;

tensor<double, 3, 2> getTangent(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID);

tensor<double, 3, 2> getTangent(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<3> faceID);

Vector getNormalFromTangent(const tensor<double, 3, 2> tangent);

Vector getNormal(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID);

Vector getNormal(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<3> faceID);

namespace surfacemesh_detail
{
void decimateVertex(SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID, std::size_t rings = 2);

tensor<double, 3, 2> computeLocalStructureTensor(
    const SurfaceMesh& mesh,
    const SurfaceMesh::SimplexID<1> vertexID,
    const int rings);


tensor<double, 3, 2> computeLSTFromCache(
    const SurfaceMesh& mesh,
    const SurfaceMesh::SimplexID<1> vertexID,
    const int rings);


template <std::size_t dimension>
auto getTangentH(const SurfaceMesh& mesh,
                 const tensor<double, dimension, 1>& origin,
                 SurfaceMesh::SimplexID<SurfaceMesh::topLevel> curr)
{
    return (*curr).orientation;
}

template <std::size_t level, std::size_t dimension>
auto getTangentH(const SurfaceMesh& mesh,
                 const tensor<double, dimension, 1>& origin,
                 SurfaceMesh::SimplexID<level> curr)
{
    tensor<double, dimension, SurfaceMesh::topLevel - level> rval;
    auto cover = mesh.get_cover(curr);
    for (auto alpha : cover)
    {
        auto edge = *mesh.get_edge_up(curr, alpha);
        const auto& v = (*mesh.get_simplex_up({alpha})).position;
        auto next = mesh.get_simplex_up(curr, alpha);
        rval += edge.orientation * (v-origin) * getTangentH(mesh, origin, next);
    }
    return rval/cover.size();
}

template <std::size_t dimension>
auto getTangentF(const SurfaceMesh& mesh,
                 const tensor<double, dimension, 1>& origin,
                 SurfaceMesh::SimplexID<SurfaceMesh::topLevel> curr,
                 std::set<SurfaceMesh::KeyType>& cover)
{
    return (*curr).orientation;
}

template <std::size_t level, std::size_t dimension>
auto getTangentF(const SurfaceMesh& mesh,
                 const tensor<double, dimension, 1>& origin,
                 SurfaceMesh::SimplexID<level> curr,
                 std::set<SurfaceMesh::KeyType>& cover)
{
    tensor<double, dimension, SurfaceMesh::topLevel - level> rval;
    for (auto alpha : cover)
    {
        auto edge = *mesh.get_edge_up(curr, alpha);
        const auto& v = (*mesh.get_simplex_up({alpha})).position;
        auto next = mesh.get_simplex_up(curr, alpha);
        auto coverup = cover;
        coverup.erase(alpha);
        rval += edge.orientation * (v-origin) * getTangentF(mesh, origin, next, coverup);
    }
    return rval/cover.size();
}

template <class K>
struct initLocalOrientation {};

template <std::size_t k>
struct initLocalOrientation<std::integral_constant<std::size_t, k> >
{
    template <typename Iterator>
    static void apply(SurfaceMesh& mesh,
                      const std::set<int>&& names,
                      Iterator begin,
                      Iterator end)
    {
        std::vector<SurfaceMesh::SimplexID<k+1> > next;
        for (auto curr = begin; curr != end; ++curr)
        {
            auto currSimplexID = *curr;
            for (auto a : mesh.get_cover(currSimplexID))
            {
                // Look for key a in names
                auto find = names.find(a);
                if (find != names.end())
                {
                    next.push_back(mesh.get_simplex_up(currSimplexID, a));

                    int orient = 1;
                    for (auto b : mesh.get_name(currSimplexID))
                    {
                        if (a > b)
                        {
                            if (a > b)
                            {
                                orient *= -1;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    (*mesh.get_edge_up(currSimplexID, a)).orientation = orient;
                }
            }
        }
        initLocalOrientation<std::integral_constant<std::size_t, k+1> >::apply(mesh, std::move(names), next.begin(), next.end());
    }
};

template <>
struct initLocalOrientation<std::integral_constant<std::size_t, SurfaceMesh::topLevel> > {
    template <typename Iterator>
    static void apply(SurfaceMesh& mesh, const std::set<int>&& names, Iterator begin, Iterator end){
    }
};

bool computeLocalOrientation(SurfaceMesh& mesh, const std::vector<SurfaceMesh::SimplexID<2> >& edgeList);

void weightedVertexSmooth(SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID, int rings);

Vector weightedVertexSmoothCache(SurfaceMesh& mesh,
                                 SurfaceMesh::SimplexID<1> vertexID,
                                 std::size_t rings);

void barycenterVertexSmooth(SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID);

void edgeFlip(SurfaceMesh& mesh, SurfaceMesh::SimplexID<2> edgeID);

void edgeFlipCache(SurfaceMesh& mesh, SurfaceMesh::SimplexID<2> edgeID);

template <class Inserter>
void selectFlipEdges(const SurfaceMesh& mesh,
                     bool preserveRidges,
                     std::function<bool(const SurfaceMesh&, const SurfaceMesh::SimplexID<2> &)>&& checkFlip,
                     Inserter iter)
{
    casc::NodeSet<SurfaceMesh::SimplexID<2> > ignoredEdges;

    for (auto edgeID : mesh.get_level_id<2>())
    {
        if ((*edgeID).selected == true)
        {
            if (!ignoredEdges.count(edgeID))
            {
                auto up = mesh.get_cover(edgeID);
                // The mesh is not a surface mesh...
                if (up.size() > 2)
                {
                    // std::cerr << "This edge participates in more than 2
                    // faces. "
                    //           << "Returning..." << std::endl;
                    throw std::runtime_error("SurfaceMesh is not pseudomanifold. Found an edge connected to more than 2 faces.");
                }
                else if (up.size() < 2) // Edge is a boundary
                {
                    // std::cerr << "This edge participates in fewer than 2
                    // faces. "
                    //           << "Returning..." << std::endl;
                    continue;
                }

                // Check if the edge is a part of a tetrahedron.
                if (mesh.exists<2>({up[0], up[1]}))
                {
                    // std::cerr << "Found a tetrahedron cannot edge flip."
                    //           << std::endl;
                    continue;
                }

                // Check if we're on a ridge. This prevents folding also.
                if (preserveRidges)
                {
                    auto a   = getNormal(mesh, mesh.get_simplex_up(edgeID, up[0]));
                    auto b   = getNormal(mesh, mesh.get_simplex_up(edgeID, up[1]));
                    auto val = angle(a, b);
                    if (val > 60)
                    {
                        continue;
                    }
                }

                // Check the flip using user function
                if (checkFlip(mesh, edgeID))
                {
                    *iter++ = edgeID;   // Insert into edges to flip

                    // The local topology will be changed by edge flip.
                    // Don't flip edges which share a common face.
                    std::set<SurfaceMesh::SimplexID<2> > tmpIgnored;
                    kneighbors(mesh, edgeID, 3, tmpIgnored);
                    ignoredEdges.insert(tmpIgnored.begin(), tmpIgnored.end());

                    // Local neighborhood append. Larger neighborhood selected
                    // above appears to work better...
                    // neighbors(mesh, edgeID, std::inserter(ignoredEdges,
                    // ignoredEdges.end()));
                }
            }
        }
    }
}

bool checkEdgeFlip(const SurfaceMesh& mesh,
                   bool preserveRidges,
                   SurfaceMesh::SimplexID<2> edgeID,
                   std::function<bool(const SurfaceMesh&, const SurfaceMesh::SimplexID<2> &)>&& checkFlip
                   );
bool checkFlipAngle(const SurfaceMesh& mesh, const SurfaceMesh::SimplexID<2>& edgeID);

int checkFlipValenceExcess(const SurfaceMesh& mesh, const SurfaceMesh::SimplexID<2>& edgeID);

void normalSmoothH(SurfaceMesh& mesh, const SurfaceMesh::SimplexID<1> vertexID, const double k);

void findHoles(const SurfaceMesh& mesh,
               std::vector<std::vector<SurfaceMesh::SimplexID<2> > >& holeList);

bool orderBoundaryEdgeRing(const SurfaceMesh& mesh,
                           std::set<SurfaceMesh::SimplexID<2> >& unvisitedBdryEdges,
                           std::vector<SurfaceMesh::SimplexID<1> >& visitedVerts,
                           std::vector<SurfaceMesh::SimplexID<2> >& bdryRing);

void edgeRingToVertices(const SurfaceMesh& mesh,
                        std::vector<SurfaceMesh::SimplexID<2> >& edgeRing,
                        std::back_insert_iterator<std::vector<SurfaceMesh::SimplexID<1> > > iter);

void triangulateHoleHelper(SurfaceMesh& mesh,
                           std::vector<SurfaceMesh::SimplexID<1> >& boundary,
                           const SMFace& fdata,
                           std::back_insert_iterator<std::vector<SurfaceMesh::SimplexID<2> > > iter);

void triangulateHole(SurfaceMesh& mesh,
                     std::vector<SurfaceMesh::SimplexID<1> >& sortedVerts,
                     const SMFace& fdata,
                     std::vector<SurfaceMesh::SimplexID<2> >& edgeList);

template <typename Complex>
struct CopyHelper
{
    using SimplexSet = typename casc::SimplexSet<Complex>;
    using KeyType = typename Complex::KeyType;

    template <std::size_t k>
    static void apply(Complex& before,
                      Complex& after,
                      const SimplexSet& S)
    {
        for (auto sID : casc::get<k>(S))
        {
            auto name = before.get_name(sID);
            auto data = *sID;
            after.insert(name, data);
        }
    }
};

template <typename Iterator>
void vertexGrabber(const SurfaceMesh& F,
                   int need,
                   std::vector<SurfaceMesh::SimplexID<1>>& nbors,
                   Iterator begin,
                   Iterator end)
{
    if (need <= 0) return;
    std::set<SurfaceMesh::SimplexID<1>> next;
    for (; begin != end; ++begin)
    {
        for (auto a : F.get_cover(*begin))
        {
            auto id = F.get_simplex_up(*begin, a);
            for (auto b : F.get_name(id))
            {
                if (b != a){
                    auto nbor = F.get_simplex_down(id, b);
                    if (std::find(nbors.begin(), nbors.end(), nbor) == nbors.end())
                    {
                        // Haven't visited so push into next ring and
                        nbors.push_back(nbor);
                        next.insert(nbor);
                        if (--need == 0) return;
                    }
                }
            }
        }
    }
    vertexGrabber(F, need, nbors, next.begin(), next.end());
}

template <class Complex>
void vertexGrabber(const Complex& F,
                   int need,
                   std::vector<typename Complex::template SimplexID<1>>& nbors,
                   typename Complex::template SimplexID<1> vid)
{
    if (need <= 0) return;
    std::set<SurfaceMesh::SimplexID<1>> next;
    for (auto a : F.get_cover(vid))
    {
        auto id = F.get_simplex_up(vid, a);
        for (auto b : F.get_name(id))
        {
            if (b != a){
                auto nbor = F.get_simplex_down(id, b);
                if (std::find(nbors.begin(), nbors.end(), nbor) == nbors.end())
                {
                    // Haven't visited so push into next ring and
                    nbors.push_back(nbor);
                    next.insert(nbor);
                    if (--need == 0) return;
                }
            }
        }
    }
    vertexGrabber(F, need, nbors, next.begin(), next.end());
}
} // end namespace surfacemesh_detail

std::unique_ptr<SurfaceMesh> readOFF(const std::string& filename);


void writeOFF(const std::string& filename, const SurfaceMesh& mesh);

std::unique_ptr<SurfaceMesh> readOBJ(const std::string& filename);


void writeOBJ(const std::string& filename, const SurfaceMesh& mesh);

void print(const SurfaceMesh& mesh);

void printQualityInfo(const std::string& filename, const SurfaceMesh& mesh);

void generateHistogram(const SurfaceMesh& mesh);

std::tuple<double, double, int, int> getMinMaxAngles(const SurfaceMesh& mesh,
                                                     double maxMinAngle, double minMaxAngle);

double getArea(const SurfaceMesh& mesh);

double getArea(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<3> faceID);

double getArea(Vertex a, Vertex b, Vertex c);

REAL getArea(std::array<Vertex, 3> t);

double getVolume(const SurfaceMesh& mesh);

bool hasHole(const SurfaceMesh& mesh);

inline std::size_t getValence(const SurfaceMesh& mesh, const SurfaceMesh::SimplexID<1> vertexID){
    return mesh.get_cover(vertexID).size();
}

std::tuple<REAL*, REAL*, REAL*, REAL*, std::map<typename SurfaceMesh::KeyType, typename SurfaceMesh::KeyType> >
curvatureViaMDSB(const SurfaceMesh& mesh);

std::tuple<REAL*, REAL*, REAL*, REAL*, std::map<typename SurfaceMesh::KeyType, typename SurfaceMesh::KeyType> >
curvatureViaJets(const SurfaceMesh& mesh, std::size_t dJet = 2, std::size_t dPrime = 2);

// void osculatingJets(const SurfaceMesh&mesh, std::size_t dJet = 2, std::size_t
// dPrime = 2);

//
// @param      mesh  The mesh
// @param[in]  v     Displacement vector
//
void translate(SurfaceMesh& mesh, Vector v);
void translate(SurfaceMesh& mesh, double dx, double dy, double dz);
void scale(SurfaceMesh& mesh, Vector v);
void scale(SurfaceMesh& mesh, double sx, double sy, double sz);
void scale(SurfaceMesh& mesh, double s);

std::pair<Vector, double> getCenterRadius(SurfaceMesh& mesh);
void centeralize(SurfaceMesh& mesh);

void smoothMesh(SurfaceMesh& mesh, int maxIter, bool preserveRidges, std::size_t rings = 2, bool verbose = false);

void coarse(SurfaceMesh& mesh, double coarseRate, double flatRate, double denseWeight, std::size_t rings = 2, bool verbose = false);

void coarse_dense(SurfaceMesh& mesh, REAL threshold, REAL weight, std::size_t rings = 2, bool verbose = false);

void coarse_flat(SurfaceMesh& mesh, REAL threshold, REAL weight, std::size_t rings = 2, bool verbose = false);

void normalSmooth(SurfaceMesh& mesh);

void fillHoles(SurfaceMesh& mesh);

void flipNormals(SurfaceMesh& mesh);

std::unique_ptr<SurfaceMesh> refineMesh(const SurfaceMesh& mesh);

std::unique_ptr<SurfaceMesh> sphere(int order);

std::unique_ptr<SurfaceMesh> cube(int order);

std::vector<std::unique_ptr<SurfaceMesh> > splitSurfaces(SurfaceMesh& mesh);

void cacheNormals(SurfaceMesh& mesh);

std::tuple<bool, int, int, int> getBettiNumbers(SurfaceMesh& mesh);
} // end namespace gamer