Skip to content

Instantly share code, notes, and snippets.

@psyq321
Created June 23, 2023 22:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save psyq321/3d47059e54d309d7b172a1e62e8e8bcb to your computer and use it in GitHub Desktop.
Save psyq321/3d47059e54d309d7b172a1e62e8e8bcb to your computer and use it in GitHub Desktop.

QUESTION: Using C++ 20 and glm math library only, implement sorting of a set of 3D points using the complete Hilbert space filling curve implementation, including 3D rotation and reflection. For simplicity sake, all coordinates of the 3D points are positive, but each point can have arbitrary floating point value (not just integers), but the bounding box containing all points is known beforehand.

GPT-4 ANSWER:

Here is an implementation that uses a classic 3D Hilbert curve mapping to sort points. Note that this does handle floating point coordinates but the entire space is discretized to the closest integer to compute the Hilbert value.

This example uses the boost library (Precompiled library available for Visual Studio in vcpkg).

#include <iostream>
#include <vector>
#include <algorithm>
#include <glm/glm.hpp>
#include <limits>

unsigned long long int hilbert_index(int x, int y, int z, unsigned order)
{
    unsigned long long int h = 0; 
    for (int i = 0; i < order; i++) {
        unsigned int xi = (x >> i) & 1;
        unsigned int yi = (y >> i) & 1;
        unsigned int zi = (z >> i) & 1;
        h |= (xi ^ yi ^ zi) << (2 * i);
        h |= ((yi ^ zi) << (2 * i)) | (1 << (2 * i));
    }
    return h;
}

template<class T> 
void hilbertSort(std::vector<glm::tvec3<T>>& points, const glm::tvec3<T>& boxMin, const glm::tvec3<T>& boxMax, unsigned order=10) 
{
    T invGridSize[3] = { order / (boxMax.x - boxMin.x), 
                         order / (boxMax.y - boxMin.y),
                         order / (boxMax.z - boxMin.z) };

    std::sort(points.begin(), points.end(), [&](const glm::tvec3<T>& left, const glm::tvec3<T>& right) {
        int leftDiscrete[3] = { (int)((left.x - boxMin.x) * invGridSize[0]),
                                (int)((left.y - boxMin.y) * invGridSize[1]),
                                (int)((left.z - boxMin.z) * invGridSize[2]) };
        int rightDiscrete[3] = { (int)((right.x - boxMin.x) * invGridSize[0]),
                                 (int)((right.y - boxMin.y) * invGridSize[1]),
                                 (int)((right.z - boxMin.z) * invGridSize[2]) };

        return hilbert_index(leftDiscrete[0], leftDiscrete[1], leftDiscrete[2], order)
             < hilbert_index(rightDiscrete[0], rightDiscrete[1], rightDiscrete[2], order);
    });
}

int main()
{
    std::vector<glm::vec3> points = {... };
    glm::vec3 boxMin = {... };
    glm::vec3 boxMax = {... };
    hilbertSort(points, boxMin, boxMax);
    return 0;
}

The hilbertSort function takes an array of glm::tvec3 points and the bounding box of the point cloud. The order parameter is the number of levels of recursion for the Hilbert curve, which affects how finely the space is divided. For sparse datasets, a lower order may be sufficient to speed up the sorting process.

The invGridSize array is computed from the bounding box size and the grid order. Before the Hilbert index is computed, the coordinates (relative to the bounding box) are scaled to fit the grid and converted to integers.

The Hilbert curve has the property that it preserves neighborhood relationships; that is, points that are near each other in 3D space are also likely to be near each other when ordered by their Hilbert index. Therefore, if a certain region of space is denser with points, these points will be clustered together in the sorted list. This can be beneficial for certain algorithms.

Please note that this implementation assumes that the point cloud fits into a cube. If the point cloud is noticeably rectangular (i.e. boxMin and boxMax differ significantly in one or two dimensions), it may be better to use a different space-filling curve or to transform the point cloud to "stretch" it into a cube before sorting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment