Program Listing for File RegularTriangleSampling.cpp

Return to documentation for file (SPlisHSPlasH/Utilities/RegularTriangleSampling.cpp)

#include "RegularTriangleSampling.h"

#include <set>
#include <vector>

using namespace SPH;

RegularTriangleSampling::RegularTriangleSampling()
{
}

void RegularTriangleSampling::sampleMesh(const unsigned int numVertices, const Vector3r * vertices, const unsigned int numFaces, const unsigned int * faces, const Real maxDistance, std::vector<Vector3r>& samples)
{
    const std::vector<Vector2ui> edges = uniqueEdges(numFaces, faces);

    appendVertexSamples(numVertices, vertices, samples);
    appendEdgeSamples(maxDistance, vertices, edges, samples);
    appendFaceSamples(maxDistance, vertices, numFaces, faces, samples);
}

void RegularTriangleSampling::appendVertexSamples(const unsigned int  numVertices, const Vector3r* vertices,
    std::vector<Vector3r>& samples)
{
    samples.insert(samples.end(), vertices, vertices + numVertices);
}

void RegularTriangleSampling::appendEdgeSamples(const Real d, const Vector3r* vertices, const std::vector<Vector2ui> & edges,
    std::vector<Vector3r>& samples, bool skipVertices)
{
    const auto iSkip = static_cast<unsigned int>(skipVertices);
    for (const Vector2ui & edge : edges)
    {
        const Vector3r & v0 = vertices[edge[0]];
        const Vector3r dir = vertices[edge[1]] - v0;
        const Real l = dir.norm();
        // ceil for oversampling, +1 to get from end to end
        const auto numSamples = static_cast<unsigned int>(std::ceil(l / d)) + 1u;
        // check for special case: zero-length edge
        if (numSamples == 1)
        {
            samples.emplace_back(v0);
            continue;
        }
        for (unsigned i = iSkip; i < numSamples - iSkip; i++)
            samples.emplace_back(v0 + static_cast<Real>(i) / (numSamples - 1) * dir);
    }
}

void RegularTriangleSampling::appendFaceSamples(const Real d, const Vector3r* vertices, const unsigned numFaces,
    const unsigned int * faces, std::vector<Vector3r>& samples, bool skipEdges)
{
    const auto iSkip = static_cast<unsigned int>(skipEdges);
    for (unsigned int iFace = 0; iFace < numFaces; iFace++)
    {
        using Matrix3ui = Eigen::Matrix<unsigned int, 3, 1, Eigen::DontAlign>;
        const auto face = Eigen::Map<const Matrix3ui>(faces + 3 * iFace);
        // chose longest edge as base
        Vector3r v0, base, toTop;
        Real l2 = 0;
        for (unsigned int c = 0; c < 3; c++)
        {
            const Vector3r v = vertices[face[(c + 1) % 3]] - vertices[face[c]];
            const Real le2 = v.squaredNorm();
            if (le2 > l2)
            {
                l2 = le2;
                v0 = vertices[face[c]];
                base = v;
                // edge to top of the triangle
                toTop = vertices[face[(c + 2) % 3]] - vertices[face[c]];
            }
        }
        const Real l = std::sqrt(l2);
        const Vector3r bn = base / l;
        // height of the triangle
        const Real h = (toTop - bn.dot(toTop) * bn).norm();
        // ceil for oversampling, +1 to get from end to end
        const auto numLines = static_cast<unsigned int>(std::ceil(h / d)) + 1u;
        for (unsigned int line = iSkip; line < numLines - iSkip; line++)
        {
            Vector3r lineStart = v0;
            Real lineLength = 0;
            if (numLines > 0)
            {
                const Real alpha = static_cast<Real>(line) / (numLines - 1);
                lineStart += alpha * toTop;
                lineLength = (1 - alpha) * l;
            }
            // ceil for oversampling, +1 to get from end to end
            const unsigned int numSamples = static_cast<unsigned int>(std::ceil(lineLength / d)) + 1u;
            for (unsigned i = iSkip; i < numSamples - iSkip; i++)
                samples.emplace_back(lineStart + (i * lineLength) / (numSamples - 1) * bn);
        }
    }
}

std::vector<RegularTriangleSampling::Vector2ui> RegularTriangleSampling::uniqueEdges(unsigned int numFaces, const unsigned int * faces)
{
    std::vector<Vector2ui> edges;
    edges.reserve(3 * numFaces);
    for (unsigned int i = 0; i < numFaces; ++i)
    {
        for (unsigned int c = 0; c < 3; c++)
        {
            unsigned int ia = faces[3 * i + c];
            unsigned int ib = faces[3 * i + (c + 1) % 3];
            // smaller index first
            if (ia > ib)
                std::swap(ia, ib);
            edges.emplace_back(ia, ib);
        }
    }
    const auto less = [](const Vector2ui & a, const Vector2ui & b) -> bool { return (a[0] < b[0]) || (a[0] == b[0] && a[1] < b[1]); };
    std::sort(edges.begin(), edges.end(), less);
    edges.erase(std::unique(edges.begin(), edges.end()), edges.end());
    return edges;
}