Coverage Report

Created: 2026-09-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h3/src/h3lib/lib/faceijk.c
Line
Count
Source
1
/*
2
 * Copyright 2016-2023, 2026 Uber Technologies, Inc.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *         http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
/** @file faceijk.c
17
 * @brief   Functions for working with icosahedral face-centered hex IJK
18
 *  coordinate systems.
19
 */
20
21
#include "faceijk.h"
22
23
#include <assert.h>
24
#include <math.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
29
#include "constants.h"
30
#include "coordijk.h"
31
#include "h3Index.h"
32
#include "latLng.h"
33
#include "vec3d.h"
34
35
/** square root of 7 and inverse square root of 7 */
36
0
#define M_SQRT7 2.6457513110645905905016157536392604257102
37
0
#define M_RSQRT7 0.37796447300922722721451653623418006081576
38
39
/** @brief icosahedron face centers in x/y/z on the unit sphere */
40
static const Vec3d faceCenterPoint[NUM_ICOSA_FACES] = {
41
    {0.2199307791404606, 0.6583691780274996, 0.7198475378926182},     // face  0
42
    {-0.2139234834501421, 0.1478171829550703, 0.9656017935214205},    // face  1
43
    {0.1092625278784797, -0.4811951572873210, 0.8697775121287253},    // face  2
44
    {0.7428567301586791, -0.3593941678278028, 0.5648005936517033},    // face  3
45
    {0.8112534709140969, 0.3448953237639384, 0.4721387736413930},     // face  4
46
    {-0.1055498149613921, 0.9794457296411413, 0.1718874610009365},    // face  5
47
    {-0.8075407579970092, 0.1533552485898818, 0.5695261994882688},    // face  6
48
    {-0.2846148069787907, -0.8644080972654206, 0.4144792552473539},   // face  7
49
    {0.7405621473854482, -0.6673299564565524, -0.0789837646326737},   // face  8
50
    {0.8512303986474293, 0.4722343788582681, -0.2289137388687808},    // face  9
51
    {-0.7405621473854481, 0.6673299564565524, 0.0789837646326737},    // face 10
52
    {-0.8512303986474292, -0.4722343788582682, 0.2289137388687808},   // face 11
53
    {0.1055498149613919, -0.9794457296411413, -0.1718874610009365},   // face 12
54
    {0.8075407579970092, -0.1533552485898819, -0.5695261994882688},   // face 13
55
    {0.2846148069787908, 0.8644080972654204, -0.4144792552473539},    // face 14
56
    {-0.7428567301586791, 0.3593941678278027, -0.5648005936517033},   // face 15
57
    {-0.8112534709140971, -0.3448953237639382, -0.4721387736413930},  // face 16
58
    {-0.2199307791404607, -0.6583691780274996, -0.7198475378926182},  // face 17
59
    {0.2139234834501420, -0.1478171829550704, -0.9656017935214205},   // face 18
60
    {-0.1092625278784796, 0.4811951572873210, -0.8697775121287253},   // face 19
61
};
62
63
/** @brief icosahedron face ijk axes as azimuth in radians from face center to
64
 * vertex 0/1/2 respectively
65
 */
66
static const double faceAxesAzRadsCII[NUM_ICOSA_FACES][3] = {
67
    {5.619958268523939882, 3.525563166130744542,
68
     1.431168063737548730},  // face  0
69
    {5.760339081714187279, 3.665943979320991689,
70
     1.571548876927796127},  // face  1
71
    {0.780213654393430055, 4.969003859179821079,
72
     2.874608756786625655},  // face  2
73
    {0.430469363979999913, 4.619259568766391033,
74
     2.524864466373195467},  // face  3
75
    {6.130269123335111400, 4.035874020941915804,
76
     1.941478918548720291},  // face  4
77
    {2.692877706530642877, 0.598482604137447119,
78
     4.787272808923838195},  // face  5
79
    {2.982963003477243874, 0.888567901084048369,
80
     5.077358105870439581},  // face  6
81
    {3.532912002790141181, 1.438516900396945656,
82
     5.627307105183336758},  // face  7
83
    {3.494305004259568154, 1.399909901866372864,
84
     5.588700106652763840},  // face  8
85
    {3.003214169499538391, 0.908819067106342928,
86
     5.097609271892733906},  // face  9
87
    {5.930472956509811562, 3.836077854116615875,
88
     1.741682751723420374},  // face 10
89
    {0.138378484090254847, 4.327168688876645809,
90
     2.232773586483450311},  // face 11
91
    {0.448714947059150361, 4.637505151845541521,
92
     2.543110049452346120},  // face 12
93
    {0.158629650112549365, 4.347419854898940135,
94
     2.253024752505744869},  // face 13
95
    {5.891865957979238535, 3.797470855586042958,
96
     1.703075753192847583},  // face 14
97
    {2.711123289609793325, 0.616728187216597771,
98
     4.805518392002988683},  // face 15
99
    {3.294508837434268316, 1.200113735041072948,
100
     5.388903939827463911},  // face 16
101
    {3.804819692245439833, 1.710424589852244509,
102
     5.899214794638635174},  // face 17
103
    {3.664438879055192436, 1.570043776661997111,
104
     5.758833981448388027},  // face 18
105
    {2.361378999196363184, 0.266983896803167583,
106
     4.455774101589558636},  // face 19
107
};
108
109
/** @brief Definition of which faces neighbor each other. */
110
static const FaceOrientIJK faceNeighbors[NUM_ICOSA_FACES][4] = {
111
    {
112
        // face 0
113
        {0, {0, 0, 0}, 0},  // central face
114
        {4, {2, 0, 2}, 1},  // ij quadrant
115
        {1, {2, 2, 0}, 5},  // ki quadrant
116
        {5, {0, 2, 2}, 3}   // jk quadrant
117
    },
118
    {
119
        // face 1
120
        {1, {0, 0, 0}, 0},  // central face
121
        {0, {2, 0, 2}, 1},  // ij quadrant
122
        {2, {2, 2, 0}, 5},  // ki quadrant
123
        {6, {0, 2, 2}, 3}   // jk quadrant
124
    },
125
    {
126
        // face 2
127
        {2, {0, 0, 0}, 0},  // central face
128
        {1, {2, 0, 2}, 1},  // ij quadrant
129
        {3, {2, 2, 0}, 5},  // ki quadrant
130
        {7, {0, 2, 2}, 3}   // jk quadrant
131
    },
132
    {
133
        // face 3
134
        {3, {0, 0, 0}, 0},  // central face
135
        {2, {2, 0, 2}, 1},  // ij quadrant
136
        {4, {2, 2, 0}, 5},  // ki quadrant
137
        {8, {0, 2, 2}, 3}   // jk quadrant
138
    },
139
    {
140
        // face 4
141
        {4, {0, 0, 0}, 0},  // central face
142
        {3, {2, 0, 2}, 1},  // ij quadrant
143
        {0, {2, 2, 0}, 5},  // ki quadrant
144
        {9, {0, 2, 2}, 3}   // jk quadrant
145
    },
146
    {
147
        // face 5
148
        {5, {0, 0, 0}, 0},   // central face
149
        {10, {2, 2, 0}, 3},  // ij quadrant
150
        {14, {2, 0, 2}, 3},  // ki quadrant
151
        {0, {0, 2, 2}, 3}    // jk quadrant
152
    },
153
    {
154
        // face 6
155
        {6, {0, 0, 0}, 0},   // central face
156
        {11, {2, 2, 0}, 3},  // ij quadrant
157
        {10, {2, 0, 2}, 3},  // ki quadrant
158
        {1, {0, 2, 2}, 3}    // jk quadrant
159
    },
160
    {
161
        // face 7
162
        {7, {0, 0, 0}, 0},   // central face
163
        {12, {2, 2, 0}, 3},  // ij quadrant
164
        {11, {2, 0, 2}, 3},  // ki quadrant
165
        {2, {0, 2, 2}, 3}    // jk quadrant
166
    },
167
    {
168
        // face 8
169
        {8, {0, 0, 0}, 0},   // central face
170
        {13, {2, 2, 0}, 3},  // ij quadrant
171
        {12, {2, 0, 2}, 3},  // ki quadrant
172
        {3, {0, 2, 2}, 3}    // jk quadrant
173
    },
174
    {
175
        // face 9
176
        {9, {0, 0, 0}, 0},   // central face
177
        {14, {2, 2, 0}, 3},  // ij quadrant
178
        {13, {2, 0, 2}, 3},  // ki quadrant
179
        {4, {0, 2, 2}, 3}    // jk quadrant
180
    },
181
    {
182
        // face 10
183
        {10, {0, 0, 0}, 0},  // central face
184
        {5, {2, 2, 0}, 3},   // ij quadrant
185
        {6, {2, 0, 2}, 3},   // ki quadrant
186
        {15, {0, 2, 2}, 3}   // jk quadrant
187
    },
188
    {
189
        // face 11
190
        {11, {0, 0, 0}, 0},  // central face
191
        {6, {2, 2, 0}, 3},   // ij quadrant
192
        {7, {2, 0, 2}, 3},   // ki quadrant
193
        {16, {0, 2, 2}, 3}   // jk quadrant
194
    },
195
    {
196
        // face 12
197
        {12, {0, 0, 0}, 0},  // central face
198
        {7, {2, 2, 0}, 3},   // ij quadrant
199
        {8, {2, 0, 2}, 3},   // ki quadrant
200
        {17, {0, 2, 2}, 3}   // jk quadrant
201
    },
202
    {
203
        // face 13
204
        {13, {0, 0, 0}, 0},  // central face
205
        {8, {2, 2, 0}, 3},   // ij quadrant
206
        {9, {2, 0, 2}, 3},   // ki quadrant
207
        {18, {0, 2, 2}, 3}   // jk quadrant
208
    },
209
    {
210
        // face 14
211
        {14, {0, 0, 0}, 0},  // central face
212
        {9, {2, 2, 0}, 3},   // ij quadrant
213
        {5, {2, 0, 2}, 3},   // ki quadrant
214
        {19, {0, 2, 2}, 3}   // jk quadrant
215
    },
216
    {
217
        // face 15
218
        {15, {0, 0, 0}, 0},  // central face
219
        {16, {2, 0, 2}, 1},  // ij quadrant
220
        {19, {2, 2, 0}, 5},  // ki quadrant
221
        {10, {0, 2, 2}, 3}   // jk quadrant
222
    },
223
    {
224
        // face 16
225
        {16, {0, 0, 0}, 0},  // central face
226
        {17, {2, 0, 2}, 1},  // ij quadrant
227
        {15, {2, 2, 0}, 5},  // ki quadrant
228
        {11, {0, 2, 2}, 3}   // jk quadrant
229
    },
230
    {
231
        // face 17
232
        {17, {0, 0, 0}, 0},  // central face
233
        {18, {2, 0, 2}, 1},  // ij quadrant
234
        {16, {2, 2, 0}, 5},  // ki quadrant
235
        {12, {0, 2, 2}, 3}   // jk quadrant
236
    },
237
    {
238
        // face 18
239
        {18, {0, 0, 0}, 0},  // central face
240
        {19, {2, 0, 2}, 1},  // ij quadrant
241
        {17, {2, 2, 0}, 5},  // ki quadrant
242
        {13, {0, 2, 2}, 3}   // jk quadrant
243
    },
244
    {
245
        // face 19
246
        {19, {0, 0, 0}, 0},  // central face
247
        {15, {2, 0, 2}, 1},  // ij quadrant
248
        {18, {2, 2, 0}, 5},  // ki quadrant
249
        {14, {0, 2, 2}, 3}   // jk quadrant
250
    }};
251
252
/** @brief direction from the origin face to the destination face, relative to
253
 * the origin face's coordinate system, or -1 if not adjacent.
254
 */
255
static const int adjacentFaceDir[NUM_ICOSA_FACES][NUM_ICOSA_FACES] = {
256
    {0,  KI, -1, -1, IJ, JK, -1, -1, -1, -1,
257
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},  // face 0
258
    {IJ, 0,  KI, -1, -1, -1, JK, -1, -1, -1,
259
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},  // face 1
260
    {-1, IJ, 0,  KI, -1, -1, -1, JK, -1, -1,
261
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},  // face 2
262
    {-1, -1, IJ, 0,  KI, -1, -1, -1, JK, -1,
263
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},  // face 3
264
    {KI, -1, -1, IJ, 0,  -1, -1, -1, -1, JK,
265
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},  // face 4
266
    {JK, -1, -1, -1, -1, 0,  -1, -1, -1, -1,
267
     IJ, -1, -1, -1, KI, -1, -1, -1, -1, -1},  // face 5
268
    {-1, JK, -1, -1, -1, -1, 0,  -1, -1, -1,
269
     KI, IJ, -1, -1, -1, -1, -1, -1, -1, -1},  // face 6
270
    {-1, -1, JK, -1, -1, -1, -1, 0,  -1, -1,
271
     -1, KI, IJ, -1, -1, -1, -1, -1, -1, -1},  // face 7
272
    {-1, -1, -1, JK, -1, -1, -1, -1, 0,  -1,
273
     -1, -1, KI, IJ, -1, -1, -1, -1, -1, -1},  // face 8
274
    {-1, -1, -1, -1, JK, -1, -1, -1, -1, 0,
275
     -1, -1, -1, KI, IJ, -1, -1, -1, -1, -1},  // face 9
276
    {-1, -1, -1, -1, -1, IJ, KI, -1, -1, -1,
277
     0,  -1, -1, -1, -1, JK, -1, -1, -1, -1},  // face 10
278
    {-1, -1, -1, -1, -1, -1, IJ, KI, -1, -1,
279
     -1, 0,  -1, -1, -1, -1, JK, -1, -1, -1},  // face 11
280
    {-1, -1, -1, -1, -1, -1, -1, IJ, KI, -1,
281
     -1, -1, 0,  -1, -1, -1, -1, JK, -1, -1},  // face 12
282
    {-1, -1, -1, -1, -1, -1, -1, -1, IJ, KI,
283
     -1, -1, -1, 0,  -1, -1, -1, -1, JK, -1},  // face 13
284
    {-1, -1, -1, -1, -1, KI, -1, -1, -1, IJ,
285
     -1, -1, -1, -1, 0,  -1, -1, -1, -1, JK},  // face 14
286
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
287
     JK, -1, -1, -1, -1, 0,  IJ, -1, -1, KI},  // face 15
288
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
289
     -1, JK, -1, -1, -1, KI, 0,  IJ, -1, -1},  // face 16
290
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
291
     -1, -1, JK, -1, -1, -1, KI, 0,  IJ, -1},  // face 17
292
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
293
     -1, -1, -1, JK, -1, -1, -1, KI, 0,  IJ},  // face 18
294
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
295
     -1, -1, -1, -1, JK, IJ, -1, -1, KI, 0}  // face 19
296
};
297
298
/** @brief overage distance table */
299
static const int maxDimByCIIres[] = {
300
    2,        // res  0
301
    -1,       // res  1
302
    14,       // res  2
303
    -1,       // res  3
304
    98,       // res  4
305
    -1,       // res  5
306
    686,      // res  6
307
    -1,       // res  7
308
    4802,     // res  8
309
    -1,       // res  9
310
    33614,    // res 10
311
    -1,       // res 11
312
    235298,   // res 12
313
    -1,       // res 13
314
    1647086,  // res 14
315
    -1,       // res 15
316
    11529602  // res 16
317
};
318
319
/** @brief unit scale distance table */
320
static const int unitScaleByCIIres[] = {
321
    1,       // res  0
322
    -1,      // res  1
323
    7,       // res  2
324
    -1,      // res  3
325
    49,      // res  4
326
    -1,      // res  5
327
    343,     // res  6
328
    -1,      // res  7
329
    2401,    // res  8
330
    -1,      // res  9
331
    16807,   // res 10
332
    -1,      // res 11
333
    117649,  // res 12
334
    -1,      // res 13
335
    823543,  // res 14
336
    -1,      // res 15
337
    5764801  // res 16
338
};
339
340
// Forward declares to make diff nicer
341
// TODO: remove and reorder functions after landing
342
static void _vec3ToHex2d(const Vec3d *p, int res, int *face, Vec2d *v);
343
static void _vec3ToClosestFace(const Vec3d *v, int *face, double *sqd);
344
345
/**
346
 * Encodes a Vec3d coordinate to the FaceIJK address of the containing
347
 * cell at the specified resolution.
348
 *
349
 * Vec3d p is expected to be on the unit sphere.
350
 *
351
 * @param p The Vec3d coordinates to encode.
352
 * @param res The desired H3 resolution for the encoding.
353
 * @param h Output: FaceIJK address of the containing cell at resolution res.
354
 */
355
0
void _vec3ToFaceIjk(Vec3d p, int res, FaceIJK *h) {
356
    // first convert to hex2d
357
0
    Vec2d v;
358
0
    _vec3ToHex2d(&p, res, &h->face, &v);
359
360
    // then convert to ijk+
361
0
    _hex2dToCoordIJK(&v, &h->coord);
362
0
}
363
364
/**
365
 * Compute the local north and east directions on the tangent plane
366
 * at a point on the unit sphere.
367
 *
368
 * Will not work if p is at a pole, but icosahedron face centers
369
 * are never at the poles.
370
 *
371
 * @param p Unit vector on the sphere.
372
 * @param north Output: local north direction on tangent plane.
373
 * @param east Output: local east direction on tangent plane.
374
 */
375
0
static inline void _vec3TangentBasis(Vec3d p, Vec3d *north, Vec3d *east) {
376
0
    Vec3d northPole = {0.0, 0.0, 1.0};
377
0
    *north = vec3LinComb(1.0, northPole, -vec3Dot(northPole, p), p);
378
0
    vec3Normalize(north);
379
0
    *east = vec3Cross(*north, p);
380
0
}
381
382
/**
383
 * Calculates the azimuth from p1 to p2.
384
 * @param p1 The first vector.
385
 * @param p2 The second vector.
386
 * @return The azimuth in radians.
387
 */
388
0
static inline double _vec3AzimuthRads(Vec3d p1, Vec3d p2) {
389
0
    Vec3d northDir, eastDir;
390
0
    _vec3TangentBasis(p1, &northDir, &eastDir);
391
392
    // project p2 onto tangent plane at p1
393
0
    Vec3d p2Proj = vec3LinComb(1.0, p2, -vec3Dot(p2, p1), p1);
394
0
    vec3Normalize(&p2Proj);
395
396
0
    return atan2(vec3Dot(p2Proj, eastDir), vec3Dot(p2Proj, northDir));
397
0
}
398
399
/**
400
 * Encodes a coordinate on the sphere to the corresponding icosahedral face and
401
 * containing 2D hex coordinates relative to that face center.
402
 *
403
 * Vec3d p is expected to be on the unit sphere.
404
 *
405
 * @param p The Vec3d coordinates to encode.
406
 * @param res The desired H3 resolution for the encoding.
407
 * @param face Output: The icosahedral face containing the coordinates.
408
 * @param v Output: The 2D hex coordinates of the cell containing the point.
409
 */
410
0
static void _vec3ToHex2d(const Vec3d *p, int res, int *face, Vec2d *v) {
411
    // determine the icosahedron face
412
0
    double sqd;
413
0
    _vec3ToClosestFace(p, face, &sqd);
414
415
    // cos(r) = 1 - 2 * sin^2(r/2) = 1 - 2 * (sqd / 4) = 1 - sqd/2
416
0
    double r = acos(1 - sqd * 0.5);
417
418
0
    if (r < EPSILON) {
419
0
        v->x = v->y = 0.0;
420
0
        return;
421
0
    }
422
423
    // now have face and r, now find CCW theta from CII i-axis
424
0
    double theta = _posAngleRads(
425
0
        faceAxesAzRadsCII[*face][0] -
426
0
        _posAngleRads(_vec3AzimuthRads(faceCenterPoint[*face], *p)));
427
428
    // adjust theta for Class III (odd resolutions)
429
0
    if (isResolutionClassIII(res))
430
0
        theta = _posAngleRads(theta - M_AP7_ROT_RADS);
431
432
    // perform gnomonic scaling of r
433
0
    r = tan(r);
434
435
    // scale for current resolution length u
436
0
    r *= INV_RES0_U_GNOMONIC;
437
0
    for (int i = 0; i < res; i++) r *= M_SQRT7;
438
439
    // we now have (r, theta) in hex2d with theta ccw from x-axes
440
441
    // convert to local x,y
442
0
    v->x = r * cos(theta);
443
0
    v->y = r * sin(theta);
444
0
}
445
446
/**
447
 * Determines the 3D coordinates of a cell given by 2D
448
 * hex coordinates on a particular icosahedral face.
449
 *
450
 * @param v The 2D hex coordinates of the cell.
451
 * @param face The icosahedral face upon which the 2D hex coordinate system is
452
 *             centered.
453
 * @param res The H3 resolution of the cell.
454
 * @param substrate Indicates whether or not this grid is actually a substrate
455
 *        grid relative to the specified resolution.
456
 * @param v3 Output: the 3D coordinates of the cell center point
457
 */
458
static void _hex2dToVec3(const Vec2d *v, int face, int res, int substrate,
459
0
                         Vec3d *v3) {
460
    // calculate (r, theta) in hex2d
461
0
    double r = _v2dMag(v);
462
463
0
    if (r < EPSILON) {
464
0
        *v3 = faceCenterPoint[face];
465
0
        return;
466
0
    }
467
468
0
    double theta = atan2(v->y, v->x);
469
470
    // scale for current resolution length u
471
0
    for (int i = 0; i < res; i++) r *= M_RSQRT7;
472
473
    // scale accordingly if this is a substrate grid
474
0
    if (substrate) {
475
0
        r *= M_ONETHIRD;
476
        // Never occurs because every case where this function is called with
477
        // substrate=1, the res has been adjusted by _faceIjkPentToVerts, which
478
        // adjusts the res by +1, making it no longer Class III.
479
0
        if (NEVER(isResolutionClassIII(res))) {
480
0
            r *= M_RSQRT7;
481
0
        }
482
0
    }
483
484
0
    r *= RES0_U_GNOMONIC;
485
486
    // perform inverse gnomonic scaling of r
487
0
    r = atan(r);
488
489
    // adjust theta for Class III
490
    // if a substrate grid, then it's already been adjusted for Class III
491
0
    if (!substrate && isResolutionClassIII(res))
492
0
        theta = _posAngleRads(theta + M_AP7_ROT_RADS);
493
494
    // find theta as an azimuth
495
0
    theta = _posAngleRads(faceAxesAzRadsCII[face][0] - theta);
496
497
    // now find the point at (r,theta) from the face center
498
0
    Vec3d northDir, eastDir;
499
0
    _vec3TangentBasis(faceCenterPoint[face], &northDir, &eastDir);
500
501
0
    Vec3d dir = vec3LinComb(cos(theta), northDir, sin(theta), eastDir);
502
503
0
    *v3 = vec3LinComb(cos(r), faceCenterPoint[face], sin(r), dir);
504
0
    vec3Normalize(v3);
505
0
}
506
507
/**
508
 * Determines the center point in 3D coordinates of a cell given by
509
 * a FaceIJK address at a specified resolution.
510
 *
511
 * @param h The FaceIJK address of the cell.
512
 * @param res The H3 resolution of the cell.
513
 * @param g Output: The 3D coordinates of the cell center point.
514
 */
515
0
void _faceIjkToVec3(const FaceIJK *h, int res, Vec3d *g) {
516
0
    Vec2d v;
517
0
    _ijkToHex2d(&h->coord, &v);
518
0
    _hex2dToVec3(&v, h->face, res, 0, g);
519
0
}
520
521
/**
522
 * Generates the cell boundary in spherical coordinates for a pentagonal cell
523
 * given by a FaceIJK address at a specified resolution.
524
 *
525
 * @param h The FaceIJK address of the pentagonal cell.
526
 * @param res The H3 resolution of the cell.
527
 * @param start The first topological vertex to return.
528
 * @param length The number of topological vertexes to return.
529
 * @param g Output: The spherical coordinates of the cell boundary.
530
 */
531
void _faceIjkPentToCellBoundary(const FaceIJK *h, int res, int start,
532
0
                                int length, CellBoundary *g) {
533
0
    int adjRes = res;
534
0
    FaceIJK centerIJK = *h;
535
0
    FaceIJK fijkVerts[NUM_PENT_VERTS];
536
0
    _faceIjkPentToVerts(&centerIJK, &adjRes, fijkVerts);
537
538
    // If we're returning the entire loop, we need one more iteration in case
539
    // of a distortion vertex on the last edge
540
0
    int additionalIteration = length == NUM_PENT_VERTS ? 1 : 0;
541
542
    // convert each vertex to lat/lng
543
    // adjust the face of each vertex as appropriate and introduce
544
    // edge-crossing vertices as needed
545
0
    g->numVerts = 0;
546
0
    FaceIJK lastFijk = {0};
547
0
    for (int vert = start; vert < start + length + additionalIteration;
548
0
         vert++) {
549
0
        int v = vert % NUM_PENT_VERTS;
550
551
0
        FaceIJK fijk = fijkVerts[v];
552
553
0
        _adjustPentVertOverage(&fijk, adjRes);
554
555
        // all Class III pentagon edges cross icosa edges
556
        // note that Class II pentagons have vertices on the edge,
557
        // not edge intersections
558
0
        if (isResolutionClassIII(res) && vert > start) {
559
            // find hex2d of the two vertexes on the last face
560
561
0
            FaceIJK tmpFijk = fijk;
562
563
0
            Vec2d orig2d0;
564
0
            _ijkToHex2d(&lastFijk.coord, &orig2d0);
565
566
0
            int currentToLastDir = adjacentFaceDir[tmpFijk.face][lastFijk.face];
567
568
0
            const FaceOrientIJK *fijkOrient =
569
0
                &faceNeighbors[tmpFijk.face][currentToLastDir];
570
571
0
            tmpFijk.face = fijkOrient->face;
572
0
            CoordIJK *ijk = &tmpFijk.coord;
573
574
            // rotate and translate for adjacent face
575
0
            for (int i = 0; i < fijkOrient->ccwRot60; i++) _ijkRotate60ccw(ijk);
576
577
0
            CoordIJK transVec = fijkOrient->translate;
578
0
            _ijkScale(&transVec, unitScaleByCIIres[adjRes] * 3);
579
0
            _ijkAdd(ijk, &transVec, ijk);
580
0
            _ijkNormalize(ijk);
581
582
0
            Vec2d orig2d1;
583
0
            _ijkToHex2d(ijk, &orig2d1);
584
585
            // find the appropriate icosa face edge vertexes
586
0
            int maxDim = maxDimByCIIres[adjRes];
587
0
            Vec2d v0 = {3.0 * maxDim, 0.0};
588
0
            Vec2d v1 = {-1.5 * maxDim, 3.0 * M_SQRT3_2 * maxDim};
589
0
            Vec2d v2 = {-1.5 * maxDim, -3.0 * M_SQRT3_2 * maxDim};
590
591
0
            Vec2d *edge0;
592
0
            Vec2d *edge1;
593
0
            switch (adjacentFaceDir[tmpFijk.face][fijk.face]) {
594
0
                case IJ:
595
0
                    edge0 = &v0;
596
0
                    edge1 = &v1;
597
0
                    break;
598
0
                case JK:
599
0
                    edge0 = &v1;
600
0
                    edge1 = &v2;
601
0
                    break;
602
0
                case KI:
603
0
                default:
604
0
                    assert(adjacentFaceDir[tmpFijk.face][fijk.face] == KI);
605
0
                    edge0 = &v2;
606
0
                    edge1 = &v0;
607
0
                    break;
608
0
            }
609
610
            // find the intersection and add the lat/lng point to the result
611
0
            Vec2d inter;
612
0
            _v2dIntersect(&orig2d0, &orig2d1, edge0, edge1, &inter);
613
0
            Vec3d v3;
614
0
            _hex2dToVec3(&inter, tmpFijk.face, adjRes, 1, &v3);
615
0
            g->verts[g->numVerts] = vec3ToLatLng(v3);
616
0
            g->numVerts++;
617
0
        }
618
619
        // convert vertex to lat/lng and add to the result
620
        // vert == start + NUM_PENT_VERTS is only used to test for possible
621
        // intersection on last edge
622
0
        if (vert < start + NUM_PENT_VERTS) {
623
0
            Vec2d vec;
624
0
            _ijkToHex2d(&fijk.coord, &vec);
625
0
            Vec3d v3;
626
0
            _hex2dToVec3(&vec, fijk.face, adjRes, 1, &v3);
627
0
            g->verts[g->numVerts] = vec3ToLatLng(v3);
628
0
            g->numVerts++;
629
0
        }
630
631
0
        lastFijk = fijk;
632
0
    }
633
0
}
634
635
/**
636
 * Get the vertices of a pentagon cell as substrate FaceIJK addresses
637
 *
638
 * @param fijk The FaceIJK address of the cell.
639
 * @param res In/out: the H3 resolution of the cell, adjusted for substrate.
640
 * @param fijkVerts Output: array for the vertices.
641
 */
642
0
void _faceIjkPentToVerts(FaceIJK *fijk, int *res, FaceIJK *fijkVerts) {
643
    // the vertexes of an origin-centered pentagon in a Class II resolution on a
644
    // substrate grid with aperture sequence 33r. The aperture 3 gets us the
645
    // vertices, and the 3r gets us back to Class II.
646
    // vertices listed ccw from the i-axes
647
0
    CoordIJK vertsCII[NUM_PENT_VERTS] = {
648
0
        {2, 1, 0},  // 0
649
0
        {1, 2, 0},  // 1
650
0
        {0, 2, 1},  // 2
651
0
        {0, 1, 2},  // 3
652
0
        {1, 0, 2},  // 4
653
0
    };
654
655
    // the vertexes of an origin-centered pentagon in a Class III resolution on
656
    // a substrate grid with aperture sequence 33r7r. The aperture 3 gets us the
657
    // vertices, and the 3r7r gets us to Class II. vertices listed ccw from the
658
    // i-axes
659
0
    CoordIJK vertsCIII[NUM_PENT_VERTS] = {
660
0
        {5, 4, 0},  // 0
661
0
        {1, 5, 0},  // 1
662
0
        {0, 5, 4},  // 2
663
0
        {0, 1, 5},  // 3
664
0
        {4, 0, 5},  // 4
665
0
    };
666
667
    // get the correct set of substrate vertices for this resolution
668
0
    CoordIJK *verts;
669
0
    if (isResolutionClassIII(*res))
670
0
        verts = vertsCIII;
671
0
    else
672
0
        verts = vertsCII;
673
674
    // adjust the center point to be in an aperture 33r substrate grid
675
    // these should be composed for speed
676
0
    _downAp3(&fijk->coord);
677
0
    _downAp3r(&fijk->coord);
678
679
    // if res is Class III we need to add a cw aperture 7 to get to
680
    // icosahedral Class II
681
0
    if (isResolutionClassIII(*res)) {
682
0
        _downAp7r(&fijk->coord);
683
0
        *res += 1;
684
0
    }
685
686
    // The center point is now in the same substrate grid as the origin
687
    // cell vertices. Add the center point substate coordinates
688
    // to each vertex to translate the vertices to that cell.
689
0
    for (int v = 0; v < NUM_PENT_VERTS; v++) {
690
0
        fijkVerts[v].face = fijk->face;
691
0
        _ijkAdd(&fijk->coord, &verts[v], &fijkVerts[v].coord);
692
0
        _ijkNormalize(&fijkVerts[v].coord);
693
0
    }
694
0
}
695
696
/**
697
 * Generates the cell boundary in spherical coordinates for a cell given by a
698
 * FaceIJK address at a specified resolution.
699
 *
700
 * @param h The FaceIJK address of the cell.
701
 * @param res The H3 resolution of the cell.
702
 * @param start The first topological vertex to return.
703
 * @param length The number of topological vertexes to return.
704
 * @param g Output: The spherical coordinates of the cell boundary.
705
 */
706
void _faceIjkToCellBoundary(const FaceIJK *h, int res, int start, int length,
707
0
                            CellBoundary *g) {
708
0
    int adjRes = res;
709
0
    FaceIJK centerIJK = *h;
710
0
    FaceIJK fijkVerts[NUM_HEX_VERTS];
711
0
    _faceIjkToVerts(&centerIJK, &adjRes, fijkVerts);
712
713
    // If we're returning the entire loop, we need one more iteration in case
714
    // of a distortion vertex on the last edge
715
0
    int additionalIteration = length == NUM_HEX_VERTS ? 1 : 0;
716
717
    // convert each vertex to lat/lng
718
    // adjust the face of each vertex as appropriate and introduce
719
    // edge-crossing vertices as needed
720
0
    g->numVerts = 0;
721
0
    int lastFace = -1;
722
0
    Overage lastOverage = NO_OVERAGE;
723
0
    for (int vert = start; vert < start + length + additionalIteration;
724
0
         vert++) {
725
0
        int v = vert % NUM_HEX_VERTS;
726
727
0
        FaceIJK fijk = fijkVerts[v];
728
729
0
        const int pentLeading4 = 0;
730
0
        Overage overage = _adjustOverageClassII(&fijk, adjRes, pentLeading4, 1);
731
732
        /*
733
        Check for edge-crossing. Each face of the underlying icosahedron is a
734
        different projection plane. So if an edge of the hexagon crosses an
735
        icosahedron edge, an additional vertex must be introduced at that
736
        intersection point. Then each half of the cell edge can be projected
737
        to geographic coordinates using the appropriate icosahedron face
738
        projection. Note that Class II cell edges have vertices on the face
739
        edge, with no edge line intersections.
740
        */
741
0
        if (isResolutionClassIII(res) && vert > start &&
742
0
            fijk.face != lastFace && lastOverage != FACE_EDGE) {
743
            // find hex2d of the two vertexes on original face
744
0
            int lastV = (v + 5) % NUM_HEX_VERTS;
745
0
            Vec2d orig2d0;
746
0
            _ijkToHex2d(&fijkVerts[lastV].coord, &orig2d0);
747
748
0
            Vec2d orig2d1;
749
0
            _ijkToHex2d(&fijkVerts[v].coord, &orig2d1);
750
751
            // find the appropriate icosa face edge vertexes
752
0
            int maxDim = maxDimByCIIres[adjRes];
753
0
            Vec2d v0 = {3.0 * maxDim, 0.0};
754
0
            Vec2d v1 = {-1.5 * maxDim, 3.0 * M_SQRT3_2 * maxDim};
755
0
            Vec2d v2 = {-1.5 * maxDim, -3.0 * M_SQRT3_2 * maxDim};
756
757
0
            int face2 = ((lastFace == centerIJK.face) ? fijk.face : lastFace);
758
0
            Vec2d *edge0;
759
0
            Vec2d *edge1;
760
0
            switch (adjacentFaceDir[centerIJK.face][face2]) {
761
0
                case IJ:
762
0
                    edge0 = &v0;
763
0
                    edge1 = &v1;
764
0
                    break;
765
0
                case JK:
766
0
                    edge0 = &v1;
767
0
                    edge1 = &v2;
768
0
                    break;
769
                // case KI:
770
0
                default:
771
0
                    assert(adjacentFaceDir[centerIJK.face][face2] == KI);
772
0
                    edge0 = &v2;
773
0
                    edge1 = &v0;
774
0
                    break;
775
0
            }
776
777
            // find the intersection and add the lat/lng point to the result
778
0
            Vec2d inter;
779
0
            _v2dIntersect(&orig2d0, &orig2d1, edge0, edge1, &inter);
780
            /*
781
            If a point of intersection occurs at a hexagon vertex, then each
782
            adjacent hexagon edge will lie completely on a single icosahedron
783
            face, and no additional vertex is required.
784
            */
785
0
            bool isIntersectionAtVertex = _v2dAlmostEquals(&orig2d0, &inter) ||
786
0
                                          _v2dAlmostEquals(&orig2d1, &inter);
787
0
            if (!isIntersectionAtVertex) {
788
0
                Vec3d v3;
789
0
                _hex2dToVec3(&inter, centerIJK.face, adjRes, 1, &v3);
790
0
                g->verts[g->numVerts] = vec3ToLatLng(v3);
791
0
                g->numVerts++;
792
0
            }
793
0
        }
794
795
        // convert vertex to lat/lng and add to the result
796
        // vert == start + NUM_HEX_VERTS is only used to test for possible
797
        // intersection on last edge
798
0
        if (vert < start + NUM_HEX_VERTS) {
799
0
            Vec2d vec;
800
0
            _ijkToHex2d(&fijk.coord, &vec);
801
0
            Vec3d v3;
802
0
            _hex2dToVec3(&vec, fijk.face, adjRes, 1, &v3);
803
0
            g->verts[g->numVerts] = vec3ToLatLng(v3);
804
0
            g->numVerts++;
805
0
        }
806
807
0
        lastFace = fijk.face;
808
0
        lastOverage = overage;
809
0
    }
810
0
}
811
812
/**
813
 * Get the vertices of a cell as substrate FaceIJK addresses
814
 *
815
 * @param fijk The FaceIJK address of the cell.
816
 * @param res In/out: the H3 resolution of the cell, adjusted for substrate.
817
 * @param fijkVerts Output: array for the vertices.
818
 */
819
0
void _faceIjkToVerts(FaceIJK *fijk, int *res, FaceIJK *fijkVerts) {
820
    // the vertexes of an origin-centered cell in a Class II resolution on a
821
    // substrate grid with aperture sequence 33r. The aperture 3 gets us the
822
    // vertices, and the 3r gets us back to Class II.
823
    // vertices listed ccw from the i-axes
824
0
    CoordIJK vertsCII[NUM_HEX_VERTS] = {
825
0
        {2, 1, 0},  // 0
826
0
        {1, 2, 0},  // 1
827
0
        {0, 2, 1},  // 2
828
0
        {0, 1, 2},  // 3
829
0
        {1, 0, 2},  // 4
830
0
        {2, 0, 1}   // 5
831
0
    };
832
833
    // the vertexes of an origin-centered cell in a Class III resolution on a
834
    // substrate grid with aperture sequence 33r7r. The aperture 3 gets us the
835
    // vertices, and the 3r7r gets us to Class II.
836
    // vertices listed ccw from the i-axes
837
0
    CoordIJK vertsCIII[NUM_HEX_VERTS] = {
838
0
        {5, 4, 0},  // 0
839
0
        {1, 5, 0},  // 1
840
0
        {0, 5, 4},  // 2
841
0
        {0, 1, 5},  // 3
842
0
        {4, 0, 5},  // 4
843
0
        {5, 0, 1}   // 5
844
0
    };
845
846
    // get the correct set of substrate vertices for this resolution
847
0
    CoordIJK *verts;
848
0
    if (isResolutionClassIII(*res))
849
0
        verts = vertsCIII;
850
0
    else
851
0
        verts = vertsCII;
852
853
    // adjust the center point to be in an aperture 33r substrate grid
854
    // these should be composed for speed
855
0
    _downAp3(&fijk->coord);
856
0
    _downAp3r(&fijk->coord);
857
858
    // if res is Class III we need to add a cw aperture 7 to get to
859
    // icosahedral Class II
860
0
    if (isResolutionClassIII(*res)) {
861
0
        _downAp7r(&fijk->coord);
862
0
        *res += 1;
863
0
    }
864
865
    // The center point is now in the same substrate grid as the origin
866
    // cell vertices. Add the center point substate coordinates
867
    // to each vertex to translate the vertices to that cell.
868
0
    for (int v = 0; v < NUM_HEX_VERTS; v++) {
869
0
        fijkVerts[v].face = fijk->face;
870
0
        _ijkAdd(&fijk->coord, &verts[v], &fijkVerts[v].coord);
871
0
        _ijkNormalize(&fijkVerts[v].coord);
872
0
    }
873
0
}
874
875
/**
876
 * Adjusts a FaceIJK address in place so that the resulting cell address is
877
 * relative to the correct icosahedral face.
878
 *
879
 * @param fijk The FaceIJK address of the cell.
880
 * @param res The H3 resolution of the cell.
881
 * @param pentLeading4 Whether or not the cell is a pentagon with a leading
882
 *        digit 4.
883
 * @param substrate Whether or not the cell is in a substrate grid.
884
 * @return 0 if on original face (no overage); 1 if on face edge (only occurs
885
 *         on substrate grids); 2 if overage on new face interior
886
 */
887
Overage _adjustOverageClassII(FaceIJK *fijk, int res, int pentLeading4,
888
0
                              int substrate) {
889
0
    Overage overage = NO_OVERAGE;
890
891
0
    CoordIJK *ijk = &fijk->coord;
892
893
    // get the maximum dimension value; scale if a substrate grid
894
0
    int maxDim = maxDimByCIIres[res];
895
0
    if (substrate) maxDim *= 3;
896
897
    // check for overage
898
0
    if (substrate && ijk->i + ijk->j + ijk->k == maxDim)  // on edge
899
0
        overage = FACE_EDGE;
900
0
    else if (ijk->i + ijk->j + ijk->k > maxDim)  // overage
901
0
    {
902
0
        overage = NEW_FACE;
903
904
0
        const FaceOrientIJK *fijkOrient;
905
0
        if (ijk->k > 0) {
906
0
            if (ijk->j > 0)  // jk "quadrant"
907
0
                fijkOrient = &faceNeighbors[fijk->face][JK];
908
0
            else  // ik "quadrant"
909
0
            {
910
0
                fijkOrient = &faceNeighbors[fijk->face][KI];
911
912
                // adjust for the pentagonal missing sequence
913
0
                if (pentLeading4) {
914
                    // translate origin to center of pentagon
915
0
                    CoordIJK origin;
916
0
                    _setIJK(&origin, maxDim, 0, 0);
917
0
                    CoordIJK tmp;
918
0
                    _ijkSub(ijk, &origin, &tmp);
919
                    // rotate to adjust for the missing sequence
920
0
                    _ijkRotate60cw(&tmp);
921
                    // translate the origin back to the center of the triangle
922
0
                    _ijkAdd(&tmp, &origin, ijk);
923
0
                }
924
0
            }
925
0
        } else  // ij "quadrant"
926
0
            fijkOrient = &faceNeighbors[fijk->face][IJ];
927
928
0
        fijk->face = fijkOrient->face;
929
930
        // rotate and translate for adjacent face
931
0
        for (int i = 0; i < fijkOrient->ccwRot60; i++) _ijkRotate60ccw(ijk);
932
933
0
        CoordIJK transVec = fijkOrient->translate;
934
0
        int unitScale = unitScaleByCIIres[res];
935
0
        if (substrate) unitScale *= 3;
936
0
        _ijkScale(&transVec, unitScale);
937
0
        _ijkAdd(ijk, &transVec, ijk);
938
0
        _ijkNormalize(ijk);
939
940
        // overage points on pentagon boundaries can end up on edges
941
0
        if (substrate && ijk->i + ijk->j + ijk->k == maxDim)  // on edge
942
0
            overage = FACE_EDGE;
943
0
    }
944
945
0
    return overage;
946
0
}
947
948
/**
949
 * Adjusts a FaceIJK address for a pentagon vertex in a substrate grid in
950
 * place so that the resulting cell address is relative to the correct
951
 * icosahedral face.
952
 *
953
 * @param fijk The FaceIJK address of the cell.
954
 * @param res The H3 resolution of the cell.
955
 */
956
0
Overage _adjustPentVertOverage(FaceIJK *fijk, int res) {
957
0
    int pentLeading4 = 0;
958
0
    Overage overage;
959
0
    do {
960
0
        overage = _adjustOverageClassII(fijk, res, pentLeading4, 1);
961
0
    } while (overage == NEW_FACE);
962
0
    return overage;
963
0
}
964
965
/**
966
 * Encodes a coordinate on the sphere to the corresponding icosahedral face and
967
 * containing the squared euclidean distance to that face center.
968
 *
969
 * Vec3d v is expected to be on the unit sphere.
970
 *
971
 * @param v The Vec3d coordinates to encode.
972
 * @param face Output: The icosahedral face containing the coordinates.
973
 * @param sqd Output: The squared euclidean distance to its face center.
974
 */
975
0
static void _vec3ToClosestFace(const Vec3d *v, int *face, double *sqd) {
976
0
    *face = 0;
977
    // The distance between two farthest points is 2.0, therefore the square of
978
    // the distance between two points should always be less or equal than 4.0 .
979
0
    *sqd = 5.0;
980
0
    for (int f = 0; f < NUM_ICOSA_FACES; ++f) {
981
0
        double sqdT = vec3DistSq(faceCenterPoint[f], *v);
982
0
        if (sqdT < *sqd) {
983
0
            *face = f;
984
0
            *sqd = sqdT;
985
0
        }
986
0
    }
987
0
}