46constexpr uint8_t UNUSED = 255;
48alignas(64) uint8_t
const FACE_NUM[64] = {
49 4, 4, 4, 4, UNUSED, 3, UNUSED, UNUSED,
50 UNUSED, UNUSED, 0, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
51 UNUSED, UNUSED, UNUSED, 2, UNUSED, 3, UNUSED, 2,
52 UNUSED, UNUSED, 0, 2, UNUSED, UNUSED, UNUSED, 2,
53 5, UNUSED, UNUSED, UNUSED, 5, 3, UNUSED, UNUSED,
54 5, UNUSED, 0, UNUSED, 5, UNUSED, UNUSED, UNUSED,
55 UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, 3, UNUSED, UNUSED,
56 UNUSED, UNUSED, 0, UNUSED, 1, 1, 1, 1
59uint8_t
const FACE_COMP[6][4] = {
60 {0, 1, 2, UNUSED}, {1, 2, 0, UNUSED}, {2, 0, 1, UNUSED},
61 {0, 1, 2, UNUSED}, {1, 2, 0, UNUSED}, {2, 0, 1, UNUSED}
64double const FACE_CONST[6][4] = {
65 { 1.0, 1.0, -1.0, 0.0},
66 { 1.0, 1.0, 1.0, 0.0},
67 { 1.0, -1.0, 1.0, 0.0},
68 {-1.0, -1.0, 1.0, 0.0},
69 {-1.0, -1.0, -1.0, 0.0},
70 {-1.0, 1.0, -1.0, 0.0}
74constexpr double DILATION = 1.0e-15;
79uint64_t wrapIndex(
int level,
84 uint32_t
const stMax = (
static_cast<uint32_t
>(1) << level) - 1;
87 if (s ==
static_cast<uint32_t
>(-1)) {
88 face = (face + 4) % 6;
92 }
else if (s > stMax) {
93 face = (face + 1) % 6;
97 }
else if (t ==
static_cast<uint32_t
>(-1)) {
98 face = (face + 5) % 6;
102 }
else if (t > stMax) {
103 face = (face + 2) % 6;
110 return (
static_cast<uint64_t
>(face + 10) << (2 * level)) |
114int findNeighborhood(
int level, uint64_t i, uint64_t * dst) {
115 int const face =
static_cast<int>(i >> (2 * level)) - 10;
118 dst[0] = wrapIndex(level, face, s - 1, t - 1);
119 dst[1] = wrapIndex(level, face, s , t - 1);
120 dst[2] = wrapIndex(level, face, s + 1, t - 1);
121 dst[3] = wrapIndex(level, face, s - 1, t);
123 dst[5] = wrapIndex(level, face, s + 1, t);
124 dst[6] = wrapIndex(level, face, s - 1, t + 1);
125 dst[7] = wrapIndex(level, face, s , t + 1);
126 dst[8] = wrapIndex(level, face, s + 1, t + 1);
128 return static_cast<int>(
std::unique(dst, dst + 9) - dst);
131#if defined(NO_SIMD) || !defined(__x86_64__)
132 void makeQuad(uint64_t i,
int level, UnitVector3d * verts) {
133 int const face =
static_cast<int>(i >> (2 * level)) - 10;
134 double const faceScale = FACE_SCALE[level];
139 level,
static_cast<int32_t
>(s),
static_cast<int32_t
>(t));
140 double u1 = (u0 + faceScale) + DILATION;
141 double v1 = (v0 + faceScale) + DILATION;
144 std::tie(u0, v0) = atanApproxInverse(u0, v0);
145 std::tie(u1, v1) = atanApproxInverse(u1, v1);
146 verts[0] = faceToSphere(face, u0, v0, FACE_COMP, FACE_CONST);
147 verts[1] = faceToSphere(face, u1, v0, FACE_COMP, FACE_CONST);
148 verts[2] = faceToSphere(face, u1, v1, FACE_COMP, FACE_CONST);
149 verts[3] = faceToSphere(face, u0, v1, FACE_COMP, FACE_CONST);
154 if ((face & 1) == 0) {
159 void makeQuad(uint64_t i,
int level, UnitVector3d * verts) {
160 int const face =
static_cast<int>(i >> (2 * level)) - 10;
161 __m128d faceScale = _mm_set1_pd(FACE_SCALE[level]);
162 __m128d dilation = _mm_set1_pd(DILATION);
163 __m128d u0v0 = gridToFace(level, hilbertIndexInverseSimd(i, level));
164 __m128d u1v1 = _mm_add_pd(u0v0, faceScale);
165 u0v0 = atanApproxInverse(_mm_sub_pd(u0v0, dilation));
166 u1v1 = atanApproxInverse(_mm_add_pd(u1v1, dilation));
167 verts[0] = faceToSphere(face, u0v0, FACE_COMP, FACE_CONST);
168 verts[1] = faceToSphere(face, _mm_shuffle_pd(u1v1, u0v0, 2),
169 FACE_COMP, FACE_CONST);
170 verts[2] = faceToSphere(face, u1v1, FACE_COMP, FACE_CONST);
171 verts[3] = faceToSphere(face, _mm_shuffle_pd(u0v0, u1v1, 2),
172 FACE_COMP, FACE_CONST);
173 if ((face & 1) == 0) {
197template <
typename RegionType,
bool InteriorOnly>
198class Mq3cPixelFinder:
public detail::PixelFinder<
199 Mq3cPixelFinder<RegionType, InteriorOnly>, RegionType, InteriorOnly, 4>
202 using Base = detail::PixelFinder<
203 Mq3cPixelFinder<RegionType, InteriorOnly>, RegionType, InteriorOnly, 4>;
207 Mq3cPixelFinder(RangeSet & ranges,
208 RegionType
const & region,
211 Base(ranges, region, level, maxRanges)
215 UnitVector3d
pixel[4];
217 for (uint64_t f = 10; f < 16; ++f) {
218 makeQuad(f, 0, pixel);
223 void subdivide(UnitVector3d
const *, uint64_t i,
int level) {
224 UnitVector3d
pixel[4];
226 for (uint64_t c = i * 4; c != i * 4 + 4; ++c) {
227 makeQuad(c, level, pixel);
228 visit(pixel, c, level);
244 if ((j & 1) == 0 || (j == 1) || ((i >> (j - 3)) < 10)) {
256 makeQuad(i, l, verts);
257 return ConvexPolygon(verts[0], verts[1], verts[2], verts[3]);
266 int n = findNeighborhood(l, i, indexes);
271 static char const FACE_NORM[6][2] = {
272 {
'-',
'Z'}, {
'+',
'X'}, {
'+',
'Y'},
273 {
'+',
'Z'}, {
'-',
'X'}, {
'-',
'Y'},
281 char * p = s + (
sizeof(s) - 1);
282 for (; l > 0; --l, --p, i >>= 2) {
287 p[0] = FACE_NORM[i - 10][0];
288 p[1] = FACE_NORM[i - 10][1];
289 return std::string(p,
sizeof(s) -
static_cast<size_t>(p - s));
295 "Modified-Q3C subdivision level not in [0, 30]");
300 uint64_t f = i >> (2 * _level);
301 if (f < 10 || f > 15) {
305 makeQuad(i, _level, verts);
310#if defined(NO_SIMD) || !defined(__x86_64__)
312 int face = faceNumber(p, FACE_NUM);
314 double u = (p(FACE_COMP[face][0]) /
w) * FACE_CONST[face][0];
315 double v = (p(FACE_COMP[face][1]) /
w) * FACE_CONST[face][1];
318 uint64_t h =
hilbertIndex(
static_cast<uint32_t
>(std::get<0>(g)),
319 static_cast<uint32_t
>(std::get<1>(g)),
321 return (
static_cast<uint64_t
>(face + 10) << (2 * _level)) | h;
325 int face = faceNumber(p, FACE_NUM);
326 __m128d ww = _mm_set1_pd(p(FACE_COMP[face][2]));
327 __m128d uv = _mm_set_pd(p(FACE_COMP[face][1]), p(FACE_COMP[face][0]));
329 _mm_div_pd(uv, _mm_andnot_pd(_mm_set_pd(-0.0, -0.0), ww)),
330 _mm_set_pd(FACE_CONST[face][1], FACE_CONST[face][0])
332 __m128i st = faceToGrid(_level, atanApprox(uv));
334 return (
static_cast<uint64_t
>(face + 10) << (2 * _level)) | h;
338RangeSet Mq3cPixelization::_envelope(Region
const & r,
size_t maxRanges)
const {
339 return detail::findPixels<Mq3cPixelFinder, false>(r, maxRanges, _level);
342RangeSet Mq3cPixelization::_interior(Region
const & r,
size_t maxRanges)
const {
343 return detail::findPixels<Mq3cPixelFinder, true>(r, maxRanges, _level);
This file declares a class for representing convex polygons with great circle edges on the unit spher...
table::PointKey< int > pixel
This file declares a Pixelization subclass for the modified Q3C indexing scheme.
This file provides a base class for pixel finders.
This file contains functions used by Q3C pixelization implementations.
This file declares a class for representing unit vectors in ℝ³.
ConvexPolygon is a closed convex polygon on the unit sphere.
static int level(uint64_t i)
level returns the subdivision level of the given modified Q3C index.
static constexpr int MAX_LEVEL
The maximum supported cube-face grid resolution is 2^30 by 2^30.
static ConvexPolygon quad(uint64_t i)
quad returns the quadrilateral corresponding to the modified Q3C pixel with index i.
static std::string asString(uint64_t i)
toString converts the given modified-Q3C index to a human readable string.
uint64_t index(UnitVector3d const &v) const override
index computes the index of the pixel for v.
Mq3cPixelization(int level)
This constructor creates a modified Q3C pixelization of the sphere with the given subdivision level.
static std::vector< uint64_t > neighborhood(uint64_t i)
neighborhood returns the indexes of all pixels that share a vertex with pixel i (including i itself).
std::unique_ptr< Region > pixel(uint64_t i) const override
pixel returns the spherical region corresponding to the pixel with index i.
UnitVector3d is a unit vector in ℝ³ with components stored in double precision.
void visit(UnitVector3d const *pixel, uint64_t index, int level)
This file contains functions for space-filling curves.
std::tuple< uint32_t, uint32_t > hilbertIndexInverse(uint64_t h, int m)
hilbertIndexInverse returns the point (x, y) with Hilbert index h, where x and y are m bit integers.
uint64_t hilbertIndex(uint32_t x, uint32_t y, int m)
hilbertIndex returns the index of (x, y) in a 2-D Hilbert curve.