Skip to content

File UniformAreaSampler.cpp

File List > algos > mesh_to_pointcloud > UniformAreaSampler.cpp

Go to the documentation of this file

#include "mesh_to_pointcloud/UniformAreaSampler.h"

namespace Argos {

    double UniformAreaSampler::computeMeshSurfaceArea(const Mesh& mesh) const
    {
        const auto& faces = mesh.getFaces();
        const auto& vertices = mesh.getVertices();

        double totalArea = 0.0;

        for (const Face& face : faces)
        {
            const auto& indices = face.getIndices();

            if (indices.size() < 3)
                continue;

            for (std::size_t i = 1; i < indices.size() - 1; ++i)
            {
                const auto& A = vertices[indices[0]];
                const auto& B = vertices[indices[i]];
                const auto& C = vertices[indices[i + 1]];

                auto AB = B - A;
                auto AC = C - A;

                double area = 0.5 * (AB ^ AC).norm();
                totalArea += area;
            }
        }

        //std::cout << "Total area: " << totalArea << std::endl; // DEBUG

        return totalArea;
    }

    std::vector<std::size_t> UniformAreaSampler::distributePointsOverFaces(const Mesh& mesh, double totalArea) const 
    {
        const auto& faces = mesh.getFaces();
        const auto& vertices = mesh.getVertices();

        std::vector<double> faceAreas(faces.size(), 0.0);
        std::vector<std::size_t> pointsPerFace(faces.size(), 0);

        //double totalAreaComputed = 0.0; // DEBUG

        if (totalArea == 0.0)
            return pointsPerFace;

        // Calcul des aires par face
        for (std::size_t f = 0; f < faces.size(); ++f)
        {
            const auto& indices = faces[f].getIndices();

            if (indices.size() < 3)
                continue;

            double area = 0.0;

            for (std::size_t i = 1; i < indices.size() - 1; ++i)
            {
                const auto& A = vertices[indices[0]];
                const auto& B = vertices[indices[i]];
                const auto& C = vertices[indices[i + 1]];

                auto AB = B - A;
                auto AC = C - A;

                area += 0.5 * (AB ^ AC).norm();  // on additionne les aires des triangles de la face
            }

            faceAreas[f] = area;  // on stocke l'aire totale de la face courrente 

            //totalAreaComputed += area; // DEBUG
        }

        //std::cout << "Total area computed: " << totalAreaComputed << std::endl; // DEBUG

        // Repartition proportionnelle
        std::size_t assigned = 0;

        for (std::size_t f = 0; f < faces.size(); ++f)
        {
            std::size_t count = static_cast<std::size_t>(m_pointCount * (faceAreas[f] / totalArea));

            pointsPerFace[f] = count;
            //std::cout << "Face " << f << ": area = " << faceAreas[f] << ", points assigned = " << count << std::endl; // DEBUG
            assigned += count;
        }

        // Distribution du reste
        std::size_t remainder = m_pointCount - assigned;

        for (std::size_t f = 0; f < faces.size() && remainder > 0; ++f)
        {
            if (faceAreas[f] > 0.0)
            {
                ++pointsPerFace[f];
                --remainder;
            }
        }

        return pointsPerFace;
    }

    void UniformAreaSampler::sampleTriangleSurface(
        const Vector3D<double>& A,
        const Vector3D<double>& B,
        const Vector3D<double>& C,
        std::size_t pointCount,
        PointCloud& cloud
    ) const
    {
        for (std::size_t i = 0; i < pointCount; ++i)
        {
            double u = static_cast<double>(std::rand()) / RAND_MAX;
            double v = static_cast<double>(std::rand()) / RAND_MAX;

            if (u + v > 1.0)
            {
                u = 1.0 - u;
                v = 1.0 - v;
            }

            double alpha = 1.0 - u - v;
            double beta = u;
            double gamma = v;

            auto P = A * alpha + B * beta + C * gamma;
            cloud.addPoint(P);
        }
    }

    void UniformAreaSampler::sampleFaceSurface(
        const Mesh& mesh,
        const Face& face,
        std::size_t pointCount,
        PointCloud& cloud
    ) const
    {
        const auto& vertices = mesh.getVertices();
        const auto& indices = face.getIndices();

        if (indices.size() < 3)
            return;

        for (std::size_t i = 0; i < pointCount; ++i)
        {
            std::size_t triIndex = 1 + std::rand() % (indices.size() - 2);

            const auto& A = vertices[indices[0]];
            const auto& B = vertices[indices[triIndex]];
            const auto& C = vertices[indices[triIndex + 1]];

            sampleTriangleSurface(A, B, C, 1, cloud);
        }
    }

    PointCloud UniformAreaSampler::sample(const Mesh& mesh)
    {
        PointCloud cloud;
        cloud.reserve(m_pointCount);

        const auto& faces = mesh.getFaces();
        //std::cout << "Faces: " << faces.size() << std::endl; // DEBUG

        if (faces.empty() || m_pointCount == 0)
            return cloud;

        double totalArea = computeMeshSurfaceArea(mesh);
        //std::cout << "Total area: " << totalArea << std::endl; // DEBUG

        if (totalArea == 0.0)
            return cloud;

        // repartition des points par face
        std::vector<std::size_t> pointsPerFace = distributePointsOverFaces(mesh, totalArea);

        // echantillonnage face par face
        for (std::size_t f = 0; f < faces.size(); ++f)
        {
            if (pointsPerFace[f] == 0)
                continue;

            sampleFaceSurface(mesh, faces[f], pointsPerFace[f], cloud);
        }
        return cloud;
    }
}