Hello, I’m exploring some algorithms to compute curvatures on surfaces and I’m using VTK under the hoods to manage the triangulation of a level-set surface (and data structures).
The problem I’m encountering is a relatively poor quality of the triangulated surfaces once the distance function fields get triangulated (I’m using vtkMarchingCubes but I also tried the vtkExtractSurface and vtkFlyingEdges3D filters too).
Here a MWE to create a spherical surface from its level-set:
/** This example is a MWE to be posted on the VTK discourse to understand how
 * to use the vtkCleanPolyDataFilter to avoid having duplicate points when
 * triangulating a sphere with a Marching Cube on a given level-set field
 */
#include <vtkCleanPolyData.h>
#include <vtkImageData.h>
#include <vtkMarchingCubes.h>
#include <vtkXMLImageDataWriter.h>
#include <vtkXMLPolyDataWriter.h>
#include "Surface.h"
using namespace hgve;
/** @brief The distance function for a sphere */
static double SphereDistance(double x, double y, double z, double R) {
    return x*x + y*y + z*z - R*R;
}
int main(int argc, char *argv[])
{
    // Let's define a uniform grid on which to compute our distance function
    vtkSmartPointer<vtkImageData> volume =
        vtkSmartPointer<vtkImageData>::New();
    volume->SetDimensions(100, 100, 100);
    volume->AllocateScalars(VTK_DOUBLE, 1);
    volume->SetSpacing(0.05, 0.05, 0.05);
    // Let's compute the value of the distance function for the sphere in all
    // the domain (i.e. for each x, y, z)
    static const double sphereRadius = 2;
    static const double sphereCenter[3] = {2.5, 2.5, 2.5};
    auto dims = volume->GetDimensions();
    for (int i = 0; i < dims[0]; i++) {
        for (int j = 0; j < dims[1]; j++) {
            for (int k = 0; k < dims[2]; k++) {
                // Let's merge coordinate index in an array
                int ijk[3] = {i, j, k};
                // Retrieve the pointId from its ijk indices
                auto pId = volume->ComputePointId(ijk);
                // Retrieve x, y, z coordinates
                auto coords = volume->GetPoint(pId);
                // Set the value of the distance at (x, y, z)
                volume->SetScalarComponentFromDouble(i, j, k, 0,
                        SphereDistance(
                            coords[0] - sphereCenter[0],
                            coords[1] - sphereCenter[1],
                            coords[2] - sphereCenter[2],
                            sphereRadius)
                        );
            }
        }
    }
    vtkSmartPointer<vtkXMLImageDataWriter> iwriter =
        vtkSmartPointer<vtkXMLImageDataWriter>::New();
    iwriter->SetInputData(volume);
    iwriter->SetFileName("sphere.vti");
    iwriter->Write();
    // Now we have the distance in the domain, let's reconstruct the surface
    // using the marching cube
    vtkSmartPointer<vtkMarchingCubes> triangulizer =
        vtkSmartPointer<vtkMarchingCubes>::New();
    triangulizer->SetInputData(volume);
    triangulizer->Update();
    // The surface obtained with the Marching Cubes algorithm contains
    // duplicate points, so let's clean it
    vtkSmartPointer<vtkCleanPolyData> cleaner =
        vtkSmartPointer<vtkCleanPolyData>::New();
    cleaner->SetInputData(triangulizer->GetOutput());
    cleaner->SetAbsoluteTolerance(1E-3);
    cleaner->Update();
    Surface surface(cleaner->GetOutput());
    surface.ComputeCurvatures();
    surface.Write("sphere.vtp");
} // main
Is this the best I can do or not? Because in the resulting surface I get very skewed triangles. Any suggestion would be very appreciated.
