Coverage Report

Created: 2025-10-10 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h3/src/h3lib/lib/vertex.c
Line
Count
Source
1
/*
2
 * Copyright 2020-2021 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  vertex.h
17
 *  @brief Functions for working with cell vertexes.
18
 */
19
20
#include "vertex.h"
21
22
#include <assert.h>
23
#include <stdbool.h>
24
25
#include "algos.h"
26
#include "baseCells.h"
27
#include "faceijk.h"
28
#include "h3Assert.h"
29
#include "h3Index.h"
30
#include "latLng.h"
31
32
2.42k
#define DIRECTION_INDEX_OFFSET 2
33
34
/** @brief Table of direction-to-face mapping for each pentagon
35
 *
36
 * Note that faces are in directional order, starting at J_AXES_DIGIT.
37
 * This table is generated by the generatePentagonDirectionFaces script.
38
 */
39
static const PentagonDirectionFaces pentagonDirectionFaces[NUM_PENTAGONS] = {
40
    {4, {4, 0, 2, 1, 3}},       {14, {6, 11, 2, 7, 1}},
41
    {24, {5, 10, 1, 6, 0}},     {38, {7, 12, 3, 8, 2}},
42
    {49, {9, 14, 0, 5, 4}},     {58, {8, 13, 4, 9, 3}},
43
    {63, {11, 6, 15, 10, 16}},  {72, {12, 7, 16, 11, 17}},
44
    {83, {10, 5, 19, 14, 15}},  {97, {13, 8, 17, 12, 18}},
45
    {107, {14, 9, 18, 13, 19}}, {117, {15, 19, 17, 18, 16}},
46
};
47
48
/**
49
 * Get the number of CCW rotations of the cell's vertex numbers
50
 * compared to the directional layout of its neighbors.
51
 * @param out Number of CCW rotations for the cell
52
 */
53
5.89k
static H3Error vertexRotations(H3Index cell, int *out) {
54
    // Get the face and other info for the origin
55
5.89k
    FaceIJK fijk;
56
5.89k
    H3Error err = _h3ToFaceIjk(cell, &fijk);
57
5.89k
    if (err) {
58
6
        return err;
59
6
    }
60
5.88k
    int baseCell = H3_EXPORT(getBaseCellNumber)(cell);
61
5.88k
    int cellLeadingDigit = _h3LeadingNonZeroDigit(cell);
62
63
    // get the base cell face
64
5.88k
    FaceIJK baseFijk;
65
5.88k
    _baseCellToFaceIjk(baseCell, &baseFijk);
66
67
5.88k
    int ccwRot60 = _baseCellToCCWrot60(baseCell, fijk.face);
68
69
5.88k
    if (_isBaseCellPentagon(baseCell)) {
70
        // Find the appropriate direction-to-face mapping
71
2.52k
        PentagonDirectionFaces dirFaces;
72
        // We never hit the end condition
73
2.52k
        int p = 0;
74
        // Don't use a for loop here, for coverage reasons.
75
15.3k
        while (ALWAYS(p < NUM_PENTAGONS)) {
76
15.3k
            if (pentagonDirectionFaces[p].baseCell == baseCell) {
77
2.52k
                dirFaces = pentagonDirectionFaces[p];
78
2.52k
                break;
79
2.52k
            }
80
12.8k
            p++;
81
12.8k
        }
82
2.52k
        if (NEVER(p == NUM_PENTAGONS)) {
83
0
            return E_FAILED;
84
0
        }
85
86
        // additional CCW rotation for polar neighbors or IK neighbors
87
2.52k
        if (fijk.face != baseFijk.face &&
88
2.01k
            (_isBaseCellPolarPentagon(baseCell) ||
89
1.50k
             fijk.face ==
90
1.50k
                 dirFaces.faces[IK_AXES_DIGIT - DIRECTION_INDEX_OFFSET])) {
91
987
            ccwRot60 = (ccwRot60 + 1) % 6;
92
987
        }
93
94
        // Check whether the cell crosses a deleted pentagon subsequence
95
2.52k
        if (cellLeadingDigit == JK_AXES_DIGIT &&
96
481
            fijk.face ==
97
481
                dirFaces.faces[IK_AXES_DIGIT - DIRECTION_INDEX_OFFSET]) {
98
            // Crosses from JK to IK: Rotate CW
99
195
            ccwRot60 = (ccwRot60 + 5) % 6;
100
2.32k
        } else if (cellLeadingDigit == IK_AXES_DIGIT &&
101
441
                   fijk.face ==
102
441
                       dirFaces.faces[JK_AXES_DIGIT - DIRECTION_INDEX_OFFSET]) {
103
            // Crosses from IK to JK: Rotate CCW
104
76
            ccwRot60 = (ccwRot60 + 1) % 6;
105
76
        }
106
2.52k
    }
107
5.88k
    *out = ccwRot60;
108
5.88k
    return E_SUCCESS;
109
5.88k
}
110
111
/** @brief Hexagon direction to vertex number relationships (same face).
112
 *         Note that we don't use direction 0 (center).
113
 */
114
static const int directionToVertexNumHex[NUM_DIGITS] = {
115
    INVALID_DIGIT, 3, 1, 2, 5, 4, 0};
116
117
/** @brief Pentagon direction to vertex number relationships (same face).
118
 *         Note that we don't use directions 0 (center) or 1 (deleted K axis).
119
 */
120
static const int directionToVertexNumPent[NUM_DIGITS] = {
121
    INVALID_DIGIT, INVALID_DIGIT, 1, 2, 4, 3, 0};
122
123
/**
124
 * Get the first vertex number for a given direction. The neighbor in this
125
 * direction is located between this vertex number and the next number in
126
 * sequence.
127
 * @returns The number for the first topological vertex, or INVALID_VERTEX_NUM
128
 *          if the direction is not valid for this cell
129
 */
130
1.71k
int vertexNumForDirection(const H3Index origin, const Direction direction) {
131
1.71k
    int isPent = H3_EXPORT(isPentagon)(origin);
132
    // Check for invalid directions
133
1.71k
    if (direction == CENTER_DIGIT || direction >= INVALID_DIGIT ||
134
1.69k
        (isPent && direction == K_AXES_DIGIT))
135
23
        return INVALID_VERTEX_NUM;
136
137
    // Determine the vertex rotations for this cell
138
1.69k
    int rotations;
139
1.69k
    H3Error err = vertexRotations(origin, &rotations);
140
1.69k
    if (err) {
141
0
        return INVALID_VERTEX_NUM;
142
0
    }
143
144
    // Find the appropriate vertex, rotating CCW if necessary
145
1.69k
    if (isPent) {
146
97
        return (directionToVertexNumPent[direction] + NUM_PENT_VERTS -
147
97
                rotations) %
148
97
               NUM_PENT_VERTS;
149
1.59k
    } else {
150
1.59k
        return (directionToVertexNumHex[direction] + NUM_HEX_VERTS -
151
1.59k
                rotations) %
152
1.59k
               NUM_HEX_VERTS;
153
1.59k
    }
154
1.69k
}
155
156
/** @brief Vertex number to hexagon direction relationships (same face).
157
 */
158
static const Direction vertexNumToDirectionHex[NUM_HEX_VERTS] = {
159
    IJ_AXES_DIGIT, J_AXES_DIGIT,  JK_AXES_DIGIT,
160
    K_AXES_DIGIT,  IK_AXES_DIGIT, I_AXES_DIGIT};
161
162
/** @brief Vertex number to pentagon direction relationships (same face).
163
 */
164
static const Direction vertexNumToDirectionPent[NUM_PENT_VERTS] = {
165
    IJ_AXES_DIGIT, J_AXES_DIGIT, JK_AXES_DIGIT, IK_AXES_DIGIT, I_AXES_DIGIT};
166
167
/**
168
 * Get the direction for a given vertex number. This returns the direction for
169
 * the neighbor between the given vertex number and the next number in sequence.
170
 * @returns The direction for this vertex, or INVALID_DIGIT if the vertex
171
 * number is invalid.
172
 */
173
4.19k
Direction directionForVertexNum(const H3Index origin, const int vertexNum) {
174
4.19k
    int isPent = H3_EXPORT(isPentagon)(origin);
175
    // Check for invalid vertexes
176
4.19k
    if (vertexNum < 0 ||
177
4.19k
        vertexNum > (isPent ? NUM_PENT_VERTS : NUM_HEX_VERTS) - 1)
178
0
        return INVALID_DIGIT;
179
180
    // Determine the vertex rotations for this cell
181
4.19k
    int rotations;
182
4.19k
    H3Error err = vertexRotations(origin, &rotations);
183
4.19k
    if (err) {
184
6
        return INVALID_DIGIT;
185
6
    }
186
187
    // Find the appropriate direction, rotating CW if necessary
188
4.19k
    return isPent ? vertexNumToDirectionPent[(vertexNum + rotations) %
189
56
                                             NUM_PENT_VERTS]
190
4.19k
                  : vertexNumToDirectionHex[(vertexNum + rotations) %
191
4.13k
                                            NUM_HEX_VERTS];
192
4.19k
}
193
194
/** @brief Directions in CCW order */
195
static const Direction DIRECTIONS[NUM_HEX_VERTS] = {
196
    J_AXES_DIGIT,  JK_AXES_DIGIT, K_AXES_DIGIT,
197
    IK_AXES_DIGIT, I_AXES_DIGIT,  IJ_AXES_DIGIT};
198
199
/** @brief Reverse direction from neighbor in each direction,
200
 *         given as an index into DIRECTIONS to facilitate rotation
201
 */
202
static const int revNeighborDirectionsHex[NUM_DIGITS] = {
203
    INVALID_DIGIT, 5, 3, 4, 1, 0, 2};
204
205
/**
206
 * Get a single vertex for a given cell, as an H3 index, or
207
 * H3_NULL if the vertex is invalid
208
 * @param cell    Cell to get the vertex for
209
 * @param vertexNum Number (index) of the vertex to calculate
210
 * @param out Output: The vertex index
211
 */
212
2.85k
H3Error H3_EXPORT(cellToVertex)(H3Index cell, int vertexNum, H3Index *out) {
213
2.85k
    int cellIsPentagon = H3_EXPORT(isPentagon)(cell);
214
2.85k
    int cellNumVerts = cellIsPentagon ? NUM_PENT_VERTS : NUM_HEX_VERTS;
215
2.85k
    int res = H3_GET_RESOLUTION(cell);
216
217
    // Check for invalid vertexes
218
2.85k
    if (vertexNum < 0 || vertexNum > cellNumVerts - 1) return E_DOMAIN;
219
220
    // Default the owner and vertex number to the input cell
221
2.50k
    H3Index owner = cell;
222
2.50k
    int ownerVertexNum = vertexNum;
223
224
    // Determine the owner, looking at the three cells that share the vertex.
225
    // By convention, the owner is the cell with the lowest numerical index.
226
227
    // If the cell is the center child of its parent, it will always have
228
    // the lowest index of any neighbor, so we can skip determining the owner
229
2.50k
    if (res == 0 || H3_GET_INDEX_DIGIT(cell, res) != CENTER_DIGIT) {
230
        // Get the left neighbor of the vertex, with its rotations
231
2.31k
        Direction left = directionForVertexNum(cell, vertexNum);
232
2.31k
        if (left == INVALID_DIGIT) return E_FAILED;
233
2.30k
        int lRotations = 0;
234
2.30k
        H3Index leftNeighbor;
235
2.30k
        H3Error leftNeighborError =
236
2.30k
            h3NeighborRotations(cell, left, &lRotations, &leftNeighbor);
237
2.30k
        if (leftNeighborError) return leftNeighborError;
238
        // Set to owner if lowest index
239
2.19k
        if (leftNeighbor < owner) owner = leftNeighbor;
240
241
        // As above, skip the right neighbor if the left is known lowest
242
2.19k
        if (res == 0 || H3_GET_INDEX_DIGIT(leftNeighbor, res) != CENTER_DIGIT) {
243
            // Get the right neighbor of the vertex, with its rotations
244
            // Note that vertex - 1 is the right side, as vertex numbers are CCW
245
1.88k
            Direction right = directionForVertexNum(
246
1.88k
                cell, (vertexNum - 1 + cellNumVerts) % cellNumVerts);
247
            // This case should be unreachable; invalid verts fail earlier
248
1.88k
            if (NEVER(right == INVALID_DIGIT)) return E_FAILED;
249
1.88k
            int rRotations = 0;
250
1.88k
            H3Index rightNeighbor;
251
1.88k
            H3Error rightNeighborError =
252
1.88k
                h3NeighborRotations(cell, right, &rRotations, &rightNeighbor);
253
1.88k
            if (rightNeighborError) return rightNeighborError;
254
            // Set to owner if lowest index
255
1.86k
            if (rightNeighbor < owner) {
256
850
                owner = rightNeighbor;
257
850
                Direction dir =
258
850
                    H3_EXPORT(isPentagon)(owner)
259
850
                        ? directionForNeighbor(owner, cell)
260
850
                        : DIRECTIONS[(revNeighborDirectionsHex[right] +
261
791
                                      rRotations) %
262
791
                                     NUM_HEX_VERTS];
263
850
                ownerVertexNum = vertexNumForDirection(owner, dir);
264
850
            }
265
1.86k
        }
266
267
        // Determine the vertex number for the left neighbor
268
2.17k
        if (owner == leftNeighbor) {
269
869
            int ownerIsPentagon = H3_EXPORT(isPentagon)(owner);
270
869
            Direction dir =
271
869
                ownerIsPentagon
272
869
                    ? directionForNeighbor(owner, cell)
273
869
                    : DIRECTIONS[(revNeighborDirectionsHex[left] + lRotations) %
274
808
                                 NUM_HEX_VERTS];
275
276
            // For the left neighbor, we need the second vertex of the
277
            // edge, which may involve looping around the vertex nums
278
869
            ownerVertexNum = vertexNumForDirection(owner, dir) + 1;
279
869
            if (ownerVertexNum == NUM_HEX_VERTS ||
280
712
                (ownerIsPentagon && ownerVertexNum == NUM_PENT_VERTS)) {
281
168
                ownerVertexNum = 0;
282
168
            }
283
869
        }
284
2.17k
    }
285
286
    // Create the vertex index
287
2.37k
    H3Index vertex = owner;
288
2.37k
    H3_SET_MODE(vertex, H3_VERTEX_MODE);
289
2.37k
    H3_SET_RESERVED_BITS(vertex, ownerVertexNum);
290
2.37k
    *out = vertex;
291
292
2.37k
    return E_SUCCESS;
293
2.50k
}
294
295
/**
296
 * Get all vertexes for the given cell
297
 * @param cell      Cell to get the vertexes for
298
 * @param vertexes  Array to hold vertex output. Must have length >= 6.
299
 */
300
469
H3Error H3_EXPORT(cellToVertexes)(H3Index cell, H3Index *vertexes) {
301
    // Get all vertexes. If the cell is a pentagon, will fill the final slot
302
    // with H3_NULL.
303
469
    bool isPent = H3_EXPORT(isPentagon)(cell);
304
2.63k
    for (int i = 0; i < NUM_HEX_VERTS; i++) {
305
2.28k
        if (i == 5 && isPent) {
306
10
            vertexes[i] = H3_NULL;
307
2.27k
        } else {
308
2.27k
            H3Error cellError = H3_EXPORT(cellToVertex)(cell, i, &vertexes[i]);
309
2.27k
            if (cellError) {
310
114
                return cellError;
311
114
            }
312
2.27k
        }
313
2.28k
    }
314
355
    return E_SUCCESS;
315
469
}
316
317
/**
318
 * Get the geocoordinates of an H3 vertex
319
 * @param vertex H3 index describing a vertex
320
 * @param coord  Output geo coordinate
321
 */
322
469
H3Error H3_EXPORT(vertexToLatLng)(H3Index vertex, LatLng *coord) {
323
    // Get the vertex number and owner from the vertex
324
469
    int vertexNum = H3_GET_RESERVED_BITS(vertex);
325
469
    H3Index owner = vertex;
326
469
    H3_SET_MODE(owner, H3_CELL_MODE);
327
469
    H3_SET_RESERVED_BITS(owner, 0);
328
329
    // Get the single vertex from the boundary
330
469
    CellBoundary gb;
331
469
    FaceIJK fijk;
332
469
    H3Error fijkError = _h3ToFaceIjk(owner, &fijk);
333
469
    if (fijkError) {
334
6
        return fijkError;
335
6
    }
336
463
    int res = H3_GET_RESOLUTION(owner);
337
338
463
    if (H3_EXPORT(isPentagon)(owner)) {
339
10
        _faceIjkPentToCellBoundary(&fijk, res, vertexNum, 1, &gb);
340
453
    } else {
341
453
        _faceIjkToCellBoundary(&fijk, res, vertexNum, 1, &gb);
342
453
    }
343
344
    // Copy from boundary to output coord
345
463
    *coord = gb.verts[0];
346
463
    return E_SUCCESS;
347
469
}
348
349
/**
350
 * Whether the input is a valid H3 vertex
351
 * @param  vertex H3 index possibly describing a vertex
352
 * @return        Whether the input is valid
353
 */
354
469
int H3_EXPORT(isValidVertex)(H3Index vertex) {
355
469
    if (H3_GET_MODE(vertex) != H3_VERTEX_MODE) {
356
243
        return 0;
357
243
    }
358
359
226
    int vertexNum = H3_GET_RESERVED_BITS(vertex);
360
226
    H3Index owner = vertex;
361
226
    H3_SET_MODE(owner, H3_CELL_MODE);
362
226
    H3_SET_RESERVED_BITS(owner, 0);
363
364
226
    if (!H3_EXPORT(isValidCell)(owner)) {
365
109
        return 0;
366
109
    }
367
368
    // The easiest way to ensure that the owner + vertex number is valid,
369
    // and that the vertex is canonical, is to recreate and compare.
370
117
    H3Index canonical;
371
117
    if (H3_EXPORT(cellToVertex)(owner, vertexNum, &canonical)) {
372
5
        return 0;
373
5
    }
374
375
112
    return vertex == canonical ? 1 : 0;
376
117
}