Back

Meshing

Overview

The Mesh<N> class represents a triangulated surface mesh in N dimensions (2D or 3D). It stores vertex positions and triangle connectivity, and supports per-vertex and per-triangle attributes via embedded Dataframe objects. The module also provides contour extraction functions for isoline/isosurface computation.

Mesh Class

Mesh Class Definition

template <size_t N>
class Mesh {
public:
    // Constructors
    Mesh() = default;
    Mesh(const Serie<Vector<N>> &vertices,
         const Serie<std::array<uint32_t, 3>> &triangles);

    // Accessors
    size_t vertexCount() const;
    size_t triangleCount() const;
    const Serie<Vector<N>> &vertices() const;
    const Serie<std::array<uint32_t, 3>> &triangles() const;

    // Attributes (stored as Dataframe)
    Dataframe &vertexAttributes();
    const Dataframe &vertexAttributes() const;
    Dataframe &triangleAttributes();
    const Dataframe &triangleAttributes() const;

    // Add attributes
    template <typename T>
    void addVertexAttribute(const std::string &name, const Serie<T> &data);

    template <typename T>
    void addTriangleAttribute(const std::string &name, const Serie<T> &data);

    // Topology queries
    std::vector<std::vector<uint32_t>> neighbors() const;
    std::vector<uint32_t> borderNodes() const;

    // Validation
    bool isValid() const;
};

// Type aliases
using Mesh2D = Mesh<2>;
using Mesh3D = Mesh<3>;
Mesh Example

// Create a simple 3D mesh
df::Serie<Vector<3>> vertices{
    {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}
};
df::Serie<std::array<uint32_t, 3>> triangles{
    {0, 1, 2}, {1, 3, 2}
};

df::Mesh3D mesh(vertices, triangles);

std::cout << "Vertices: " << mesh.vertexCount() << std::endl;    // 4
std::cout << "Triangles: " << mesh.triangleCount() << std::endl; // 2
std::cout << "Valid: " << mesh.isValid() << std::endl;           // true

// Add per-vertex scalar attribute
df::Serie<double> elevation{0.0, 0.5, 0.3, 0.8};
mesh.addVertexAttribute("elevation", elevation);

// Add per-triangle attribute
df::Serie<double> quality{0.95, 0.87};
mesh.addTriangleAttribute("quality", quality);

// Access attributes
auto elev = mesh.vertexAttributes().get<double>("elevation");

// Get border nodes
auto border = mesh.borderNodes();

// Get neighbor connectivity
auto nbrs = mesh.neighbors();

Contour Extraction

Contour Functions

// Iso-segment structure: a line segment on the mesh surface at a given iso-value
template <size_t N>
struct IsoSegment {
    Vector<N> p1;     // First endpoint
    Vector<N> p2;     // Second endpoint
    double    value;   // Iso-value
};

// Extract contour lines at a given iso-value
template <size_t N>
std::vector<IsoSegment<N>> contours(
    const Mesh<N>      &mesh,
    const std::string  &attrName,   // Vertex attribute name
    double              isoValue
);

// Generate iso-values evenly spaced by a given spacing
std::vector<double> generateIsosBySpacing(
    double minVal, double maxVal, double spacing
);

// Generate a given number of evenly spaced iso-values
std::vector<double> generateIsosByNumber(
    double minVal, double maxVal, size_t count
);

// Generate all contours for a set of iso-values
template <size_t N>
std::vector<IsoSegment<N>> generateIsos(
    const Mesh<N>              &mesh,
    const std::string          &attrName,
    const std::vector<double>  &isoValues
);
Contour Example

// Create a mesh with an elevation attribute
df::Mesh3D mesh(vertices, triangles);
mesh.addVertexAttribute("elevation", elevation);

// Extract a single contour at elevation = 0.5
auto iso_segments = df::contours(mesh, "elevation", 0.5);

for (const auto &seg : iso_segments) {
    std::cout << "Segment from ("
              << seg.p1[0] << "," << seg.p1[1] << "," << seg.p1[2]
              << ") to ("
              << seg.p2[0] << "," << seg.p2[1] << "," << seg.p2[2]
              << ") at value " << seg.value << std::endl;
}

// Generate multiple contours by spacing
auto iso_vals = df::generateIsosBySpacing(0.0, 1.0, 0.1);
auto all_isos = df::generateIsos(mesh, "elevation", iso_vals);

// Or by count
auto iso_vals2 = df::generateIsosByNumber(0.0, 1.0, 10);
auto all_isos2 = df::generateIsos(mesh, "elevation", iso_vals2);

Complete Example

Mesh Processing Pipeline

#include <dataframe/Serie.h>
#include <dataframe/geo/mesh.h>
#include <dataframe/geo/geometry.h>
#include <iostream>

int main() {
    // Generate a sphere mesh
    auto [verts, tris] = df::geo::generateSphere(3, 1.0);
    df::Mesh3D sphere(verts, tris);

    // Compute normals and add as attribute
    auto norms = df::geo::normals(verts, tris);
    sphere.addTriangleAttribute("normals", norms);

    // Add a scalar field (e.g., height = z-coordinate)
    auto heights = verts | df::map([](const Vector<3> &v, size_t) {
        return v[2];
    });
    sphere.addVertexAttribute("height", heights);

    // Extract contour lines at the equator
    auto equator = df::contours(sphere, "height", 0.0);

    std::cout << "Sphere: " << sphere.vertexCount() << " vertices, "
              << sphere.triangleCount() << " triangles" << std::endl;
    std::cout << "Equator segments: " << equator.size() << std::endl;

    // Check border (sphere has no border)
    auto border = sphere.borderNodes();
    std::cout << "Border nodes: " << border.size() << std::endl;  // 0

    return 0;
}