Coverage Report

Created: 2025-07-17 06:34

/src/geos/src/shape/fractal/HilbertCode.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca>
7
 *
8
 * This is free software; you can redistribute and/or modify it under
9
 * the terms of the GNU Lesser General Public Licence as published
10
 * by the Free Software Foundation.
11
 * See the COPYING file for more information.
12
 *
13
 **********************************************************************/
14
15
#include <geos/shape/fractal/HilbertCode.h>
16
#include <geos/geom/Coordinate.h>
17
#include <geos/util/IllegalArgumentException.h>
18
19
#include <cstdint>
20
21
namespace geos {
22
namespace shape {   // geos.shape
23
namespace fractal { // geos.shape.fractal
24
25
// Fast Hilbert curve algorithm by http://threadlocalmutex.com/
26
// From C++ https://github.com/rawrunprotected/hilbert_curves
27
//(public domain)
28
29
/*public static*/
30
uint32_t HilbertCode::levelSize(uint32_t level)
31
0
{
32
0
    return (uint32_t)std::pow(2, 2*level);
33
0
}
34
35
/*public static*/
36
uint32_t HilbertCode::maxOrdinate(uint32_t level)
37
0
{
38
0
    return (uint32_t)std::pow(2, level) - 1;
39
0
}
40
41
/*public static*/
42
uint32_t HilbertCode::level(uint32_t numPoints)
43
0
{
44
0
    uint32_t pow2 = (uint32_t) ((std::log(numPoints)/std::log(2)));
45
0
    uint32_t level = pow2 / 2;
46
0
    uint32_t size = levelSize(level);
47
0
    if (size < numPoints)
48
0
        level += 1;
49
0
    return level;
50
0
}
51
52
53
uint32_t
54
HilbertCode::deinterleave(uint32_t x)
55
0
{
56
0
    x = x & 0x55555555;
57
0
    x = (x | (x >> 1)) & 0x33333333;
58
0
    x = (x | (x >> 2)) & 0x0F0F0F0F;
59
0
    x = (x | (x >> 4)) & 0x00FF00FF;
60
0
    x = (x | (x >> 8)) & 0x0000FFFF;
61
0
    return x;
62
0
}
63
64
uint32_t
65
HilbertCode::interleave(uint32_t x)
66
0
{
67
0
    x = (x | (x << 8)) & 0x00FF00FF;
68
0
    x = (x | (x << 4)) & 0x0F0F0F0F;
69
0
    x = (x | (x << 2)) & 0x33333333;
70
0
    x = (x | (x << 1)) & 0x55555555;
71
0
    return x;
72
0
}
73
74
uint32_t
75
HilbertCode::prefixScan(uint32_t x)
76
0
{
77
0
    x = (x >> 8) ^ x;
78
0
    x = (x >> 4) ^ x;
79
0
    x = (x >> 2) ^ x;
80
0
    x = (x >> 1) ^ x;
81
0
    return x;
82
0
}
83
84
uint32_t
85
HilbertCode::descan(uint32_t x)
86
0
{
87
0
    return x ^ (x >> 1);
88
0
}
89
90
/*private static*/
91
void
92
HilbertCode::checkLevel(uint32_t level)
93
0
{
94
0
    if (level > MAX_LEVEL) {
95
0
        throw util::IllegalArgumentException("Level out of range");
96
0
    }
97
0
}
98
99
/* public static */
100
geom::Coordinate
101
HilbertCode::decode(uint32_t level, uint32_t i)
102
0
{
103
0
    checkLevel(level);
104
0
    i = i << (32 - 2 * level);
105
106
0
    uint32_t i0 = deinterleave(i);
107
0
    uint32_t i1 = deinterleave(i >> 1);
108
109
0
    uint32_t t0 = (i0 | i1) ^ 0xFFFF;
110
0
    uint32_t t1 = i0 & i1;
111
112
0
    uint32_t prefixT0 = prefixScan(t0);
113
0
    uint32_t prefixT1 = prefixScan(t1);
114
115
0
    uint32_t a = (((i0 ^ 0xFFFF) & prefixT1) | (i0 & prefixT0));
116
117
0
    geom::Coordinate c;
118
0
    c.x = (a ^ i1) >> (16 - level);
119
0
    c.y = (a ^ i0 ^ i1) >> (16 - level);
120
0
    return c;
121
0
}
122
123
/* public static */
124
uint32_t
125
HilbertCode::encode(uint32_t level, uint32_t x, uint32_t y)
126
0
{
127
0
    checkLevel(level);
128
0
    x = x << (16 - level);
129
0
    y = y << (16 - level);
130
131
0
    uint32_t A, B, C, D;
132
133
    // Initial prefix scan round, prime with x and y
134
0
    {
135
0
        uint32_t a = x ^ y;
136
0
        uint32_t b = 0xFFFF ^ a;
137
0
        uint32_t c = 0xFFFF ^ (x | y);
138
0
        uint32_t d = x & (y ^ 0xFFFF);
139
140
0
        A = a | (b >> 1);
141
0
        B = (a >> 1) ^ a;
142
143
0
        C = ((c >> 1) ^ (b & (d >> 1))) ^ c;
144
0
        D = ((a & (c >> 1)) ^ (d >> 1)) ^ d;
145
0
    }
146
147
0
    {
148
0
        uint32_t a = A;
149
0
        uint32_t b = B;
150
0
        uint32_t c = C;
151
0
        uint32_t d = D;
152
153
0
        A = ((a & (a >> 2)) ^ (b & (b >> 2)));
154
0
        B = ((a & (b >> 2)) ^ (b & ((a ^ b) >> 2)));
155
156
0
        C ^= ((a & (c >> 2)) ^ (b & (d >> 2)));
157
0
        D ^= ((b & (c >> 2)) ^ ((a ^ b) & (d >> 2)));
158
0
    }
159
160
0
    {
161
0
        uint32_t a = A;
162
0
        uint32_t b = B;
163
0
        uint32_t c = C;
164
0
        uint32_t d = D;
165
166
0
        A = ((a & (a >> 4)) ^ (b & (b >> 4)));
167
0
        B = ((a & (b >> 4)) ^ (b & ((a ^ b) >> 4)));
168
169
0
        C ^= ((a & (c >> 4)) ^ (b & (d >> 4)));
170
0
        D ^= ((b & (c >> 4)) ^ ((a ^ b) & (d >> 4)));
171
0
    }
172
173
    // Final round and projection
174
0
    {
175
0
        uint32_t a = A;
176
0
        uint32_t b = B;
177
0
        uint32_t c = C;
178
0
        uint32_t d = D;
179
180
0
        C ^= ((a & (c >> 8)) ^ (b & (d >> 8)));
181
0
        D ^= ((b & (c >> 8)) ^ ((a ^ b) & (d >> 8)));
182
0
    }
183
184
    // Undo transformation prefix scan
185
0
    uint32_t a = C ^ (C >> 1);
186
0
    uint32_t b = D ^ (D >> 1);
187
188
    // Recover index bits
189
0
    uint32_t i0 = x ^ y;
190
0
    uint32_t i1 = b | (0xFFFF ^ (i0 | a));
191
192
0
    return ((interleave(i1) << 1) | interleave(i0)) >> (32 - 2 * level);
193
0
}
194
195
196
197
} // namespace geos.shape.fractal
198
} // namespace geos.shape
199
} // namespace geos