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