Skip to content

Instantly share code, notes, and snippets.

@kyythane
Created March 26, 2020 17:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kyythane/366a6d793b15919685158576f7bde1ab to your computer and use it in GitHub Desktop.
Save kyythane/366a6d793b15919685158576f7bde1ab to your computer and use it in GitHub Desktop.
Non-recursive hilbert curve algotithm in C#. Taken from a defunct side project in Unity, so the code as it stands is using Unity's Vector3Int type. But that should be a relatively easy replacement.
using System;
using UnityEngine;
namespace Assets.SunsetIsland.Utilities
{
/// <summary>
/// Convert between Hilbert index and N-dimensional points.
/// The Hilbert index is expressed as an array of transposed bits.
/// Example: 5 bits for each of n=3 coordinates.
/// 15-bit Hilbert integer = A B C D E F G H I J K L M N O is stored
/// as its Transpose ^
/// X[0] = A D G J M X[1]| 7
/// X[1] = B E H K N <-------> | /X[2]
/// X[2] = C F I L O axes |/
/// high low 0------> X[0]
/// NOTE: This algorithm is derived from work done by John Skilling and published in "Programming the Hilbert curve".
/// (c) 2004 American Institute of Physics.
/// </summary>
public static class HilbertCurve
{
private static Vector3Uint BuildTranspose(uint index, int bits)
{
var transpose = new Vector3Uint();
var mask = (2U << (bits - 1)) - 1;
uint row = 0;
for (var j = 0; j < bits; ++j)
{
var source = j * 3 + 2;
row |= ((index >> source) & 1) << j;
}
transpose.x = row & mask;
row = 0;
for (var j = 0; j < bits; ++j)
{
var source = j * 3 + 1;
row |= ((index >> source) & 1) << j;
}
transpose.y = row & mask;
row = 0;
for (var j = 0; j < bits; ++j)
{
var source = j * 3;
row |= ((index >> source) & 1) << j;
}
transpose.z = row & mask;
return transpose;
}
public static Vector3Int HilbertAxes(uint index, int bits = 5)
{
const int dimensions = 3;
var vector = BuildTranspose(index, 5);
uint q;
// Gray decode by H ^ (H/2)
var grayCode = vector.z >> 1;
vector.z ^= vector.y;
vector.y ^= vector.x;
vector.x ^= grayCode;
// Undo excess work
var n = 2U << (bits - 1);
for (q = 2; q != n; q <<= 1)
{
var p = q - 1;
int dimension;
for (dimension = dimensions - 1; dimension >= 0; dimension--)
if ((vector[dimension] & q) != 0U)
{
vector.x ^= p; // invert
}
else
{
grayCode = (vector.x ^ vector[dimension]) & p;
vector.x ^= grayCode;
vector[dimension] ^= grayCode;
}
} // exchange
return vector;
}
private struct Vector3Uint
{
public uint x;
public uint y;
public uint z;
public uint this[int i]
{
get
{
switch (i)
{
case 0: return x;
case 1: return y;
case 2: return z;
default: throw new IndexOutOfRangeException();
}
}
set
{
switch (i)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
default: throw new IndexOutOfRangeException();
}
}
}
public static implicit operator Vector3Int(Vector3Uint source)
{
return new Vector3Int((int) source.x, (int) source.y, (int) source.z);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment