Joshua Moerman
8 years ago
5 changed files with 267 additions and 22 deletions
@ -0,0 +1,100 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <climits> |
||||
|
#include <cstdint> |
||||
|
#include <utility> |
||||
|
|
||||
|
namespace remap { |
||||
|
// there are some more efficient ways in
|
||||
|
// http://graphics.stanford.edu/~seander/bithacks.html#InterleaveTableObvious
|
||||
|
// but I was too lazy to understand them
|
||||
|
|
||||
|
// some templates dealing with sizes
|
||||
|
// clang-format off
|
||||
|
template <typename T> struct unsigned_size {}; |
||||
|
template <> struct unsigned_size<uint8_t> { using twice = uint16_t; /* 4 bits */ }; |
||||
|
template <> struct unsigned_size<uint16_t> { using twice = uint32_t; using half = uint8_t; }; |
||||
|
template <> struct unsigned_size<uint32_t> { using twice = uint64_t; using half = uint16_t; }; |
||||
|
template <> struct unsigned_size<uint64_t> { /* 128 bits */ using half = uint32_t; }; |
||||
|
// clang-format on
|
||||
|
|
||||
|
// shortcuts
|
||||
|
template <typename T> |
||||
|
using twice = typename unsigned_size<T>::twice; |
||||
|
template <typename T> |
||||
|
using half = typename unsigned_size<T>::half; |
||||
|
|
||||
|
// converts x,y coordinates to z order
|
||||
|
template <typename T> |
||||
|
twice<T> to_z_order(const T xin, const T yin) { |
||||
|
const twice<T> x = xin; |
||||
|
const twice<T> y = yin; |
||||
|
twice<T> z = 0; |
||||
|
for (int i = 0; i < sizeof(T) * CHAR_BIT; i++) { |
||||
|
z |= (x & (1U << i)) << i | (y & (1U << i)) << (i + 1); |
||||
|
} |
||||
|
return z; |
||||
|
} |
||||
|
|
||||
|
// converts z to x,y
|
||||
|
template <typename T> |
||||
|
std::pair<half<T>, half<T>> from_z_order(const T z) { |
||||
|
half<T> x = 0; |
||||
|
half<T> y = 0; |
||||
|
for (int i = 0; i < sizeof(T) * CHAR_BIT; i += 2) { |
||||
|
x |= (z & (1U << i)) >> (i / 2); |
||||
|
y |= (z & (1U << (i + 1))) >> (i / 2 + 1); |
||||
|
} |
||||
|
return {x, y}; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// from wikipedia
|
||||
|
// rotate/flip a quadrant appropriately
|
||||
|
template <typename T> |
||||
|
void hilbert_rot(T n, T * x, T * y, T rx, T ry) { |
||||
|
if (ry == 0) { |
||||
|
if (rx == 1) { |
||||
|
*x = n - 1 - *x; |
||||
|
*y = n - 1 - *y; |
||||
|
} |
||||
|
|
||||
|
// Swap x and y
|
||||
|
int t = *x; |
||||
|
*x = *y; |
||||
|
*y = t; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <typename T> |
||||
|
twice<T> to_hilbert(twice<T> n, T xin, T yin) { |
||||
|
twice<T> x = xin; |
||||
|
twice<T> y = yin; |
||||
|
twice<T> d = 0; |
||||
|
for (twice<T> s = n / 2; s > 0; s /= 2) { |
||||
|
twice<T> rx = (x & s) > 0; |
||||
|
twice<T> ry = (y & s) > 0; |
||||
|
d += s * s * ((3 * rx) ^ ry); |
||||
|
hilbert_rot(s, &x, &y, rx, ry); |
||||
|
} |
||||
|
return d; |
||||
|
} |
||||
|
|
||||
|
// convert d to (x,y)
|
||||
|
template <typename T> |
||||
|
std::pair<half<T>, half<T>> from_hilbert(T n, T d) { |
||||
|
T t = d; |
||||
|
T x = 0; |
||||
|
T y = 0; |
||||
|
for (T s = 1; s < n; s *= 2) { |
||||
|
T rx = 1 & (t / 2); |
||||
|
T ry = 1 & (t ^ rx); |
||||
|
hilbert_rot(s, &x, &y, rx, ry); |
||||
|
x += s * rx; |
||||
|
y += s * ry; |
||||
|
t /= 4; |
||||
|
} |
||||
|
return {half<T>(x), half<T>(y)}; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
#include "remap.hpp" |
||||
|
|
||||
|
#include <iostream> |
||||
|
#include <limits> |
||||
|
|
||||
|
using namespace std; |
||||
|
|
||||
|
template <typename T> |
||||
|
static void test_z_order(uint64_t xmax, uint64_t ymax){ |
||||
|
uint64_t c = 10; |
||||
|
for (uint64_t x2 = 0; x2 <= xmax; ++x2) { |
||||
|
for (uint64_t y2 = 0; y2 <= ymax; ++y2) { |
||||
|
const T x = x2; |
||||
|
const T y = y2; |
||||
|
const auto z = remap::to_z_order(x, y); |
||||
|
const auto p = remap::from_z_order(z); |
||||
|
|
||||
|
if (x != p.first || y != p.second) { |
||||
|
cout << +x << ' ' << +y << " -> " << z << " -> " << +p.first << ' ' << +p.second << '\n'; |
||||
|
c--; |
||||
|
if(!c) return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <typename T> |
||||
|
static void test_z_order_max(){ |
||||
|
return test_z_order<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()); |
||||
|
} |
||||
|
|
||||
|
template <typename T> |
||||
|
static void test_hilbert(uint64_t xmax){ |
||||
|
uint64_t c = 10; |
||||
|
for (uint64_t x2 = 0; x2 <= xmax; ++x2) { |
||||
|
for (uint64_t y2 = 0; y2 <= xmax; ++y2) { |
||||
|
const T x = x2; |
||||
|
const T y = y2; |
||||
|
const remap::twice<T> xmax2 = xmax+1; |
||||
|
const auto z = remap::to_hilbert(xmax2, x, y); |
||||
|
const auto p = remap::from_hilbert(xmax2, z); |
||||
|
|
||||
|
if (x != p.first || y != p.second) { |
||||
|
cout << +x << ' ' << +y << " -> " << z << " -> " << +p.first << ' ' << +p.second << '\n'; |
||||
|
c--; |
||||
|
if(!c) return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <typename T> |
||||
|
static void test_hilbert_max(){ |
||||
|
return test_hilbert<T>(std::numeric_limits<T>::max()); |
||||
|
} |
||||
|
|
||||
|
int main(int argc, char * argv[]) { |
||||
|
//cout << "Z order: 8" << endl;
|
||||
|
//test_z_order_max<uint8_t>();
|
||||
|
cout << "Hilbert: 8" << endl; |
||||
|
test_hilbert_max<uint8_t>(); |
||||
|
//cout << "Z order: 16" << endl;
|
||||
|
//test_z_order_max<uint16_t>();
|
||||
|
cout << "Hilbert: 16" << endl; |
||||
|
test_hilbert_max<uint16_t>(); |
||||
|
} |
Reference in new issue