/src/h3/src/h3lib/lib/vertex.c
Line | Count | Source (jump to first uncovered line) |
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 | 191 | #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 | 343 | static H3Error vertexRotations(H3Index cell, int *out) { |
54 | | // Get the face and other info for the origin |
55 | 343 | FaceIJK fijk; |
56 | 343 | H3Error err = _h3ToFaceIjk(cell, &fijk); |
57 | 343 | if (err) { |
58 | 1 | return err; |
59 | 1 | } |
60 | 342 | int baseCell = H3_EXPORT(getBaseCellNumber)(cell); |
61 | 342 | int cellLeadingDigit = _h3LeadingNonZeroDigit(cell); |
62 | | |
63 | | // get the base cell face |
64 | 342 | FaceIJK baseFijk; |
65 | 342 | _baseCellToFaceIjk(baseCell, &baseFijk); |
66 | | |
67 | 342 | int ccwRot60 = _baseCellToCCWrot60(baseCell, fijk.face); |
68 | | |
69 | 342 | if (_isBaseCellPentagon(baseCell)) { |
70 | | // Find the appropriate direction-to-face mapping |
71 | 194 | PentagonDirectionFaces dirFaces; |
72 | | // We never hit the end condition |
73 | 194 | int p = 0; |
74 | | // Don't use a for loop here, for coverage reasons. |
75 | 1.25k | while (ALWAYS(p < NUM_PENTAGONS)) { |
76 | 1.25k | if (pentagonDirectionFaces[p].baseCell == baseCell) { |
77 | 194 | dirFaces = pentagonDirectionFaces[p]; |
78 | 194 | break; |
79 | 194 | } |
80 | 1.06k | p++; |
81 | 1.06k | } |
82 | 1.25k | if (NEVER(p == NUM_PENTAGONS)) { |
83 | 0 | return E_FAILED; |
84 | 0 | } |
85 | | |
86 | | // additional CCW rotation for polar neighbors or IK neighbors |
87 | 194 | if (fijk.face != baseFijk.face && |
88 | 194 | (_isBaseCellPolarPentagon(baseCell) || |
89 | 169 | fijk.face == |
90 | 143 | dirFaces.faces[IK_AXES_DIGIT - DIRECTION_INDEX_OFFSET])) { |
91 | 64 | ccwRot60 = (ccwRot60 + 1) % 6; |
92 | 64 | } |
93 | | |
94 | | // Check whether the cell crosses a deleted pentagon subsequence |
95 | 194 | if (cellLeadingDigit == JK_AXES_DIGIT && |
96 | 194 | fijk.face == |
97 | 19 | dirFaces.faces[IK_AXES_DIGIT - DIRECTION_INDEX_OFFSET]) { |
98 | | // Crosses from JK to IK: Rotate CW |
99 | 4 | ccwRot60 = (ccwRot60 + 5) % 6; |
100 | 190 | } else if (cellLeadingDigit == IK_AXES_DIGIT && |
101 | 190 | fijk.face == |
102 | 29 | dirFaces.faces[JK_AXES_DIGIT - DIRECTION_INDEX_OFFSET]) { |
103 | | // Crosses from IK to JK: Rotate CCW |
104 | 1 | ccwRot60 = (ccwRot60 + 1) % 6; |
105 | 1 | } |
106 | 194 | } |
107 | 342 | *out = ccwRot60; |
108 | 342 | return E_SUCCESS; |
109 | 342 | } |
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 | 348 | int vertexNumForDirection(const H3Index origin, const Direction direction) { |
131 | 348 | int isPent = H3_EXPORT(isPentagon)(origin); |
132 | | // Check for invalid directions |
133 | 348 | if (direction == CENTER_DIGIT || direction >= INVALID_DIGIT || |
134 | 348 | (isPent && direction == K_AXES_DIGIT)) |
135 | 5 | return INVALID_VERTEX_NUM; |
136 | | |
137 | | // Determine the vertex rotations for this cell |
138 | 343 | int rotations; |
139 | 343 | H3Error err = vertexRotations(origin, &rotations); |
140 | 343 | if (err) { |
141 | 1 | return INVALID_VERTEX_NUM; |
142 | 1 | } |
143 | | |
144 | | // Find the appropriate vertex, rotating CCW if necessary |
145 | 342 | if (isPent) { |
146 | 8 | return (directionToVertexNumPent[direction] + NUM_PENT_VERTS - |
147 | 8 | rotations) % |
148 | 8 | NUM_PENT_VERTS; |
149 | 334 | } else { |
150 | 334 | return (directionToVertexNumHex[direction] + NUM_HEX_VERTS - |
151 | 334 | rotations) % |
152 | 334 | NUM_HEX_VERTS; |
153 | 334 | } |
154 | 342 | } |
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 | 0 | Direction directionForVertexNum(const H3Index origin, const int vertexNum) { |
174 | 0 | int isPent = H3_EXPORT(isPentagon)(origin); |
175 | | // Check for invalid vertexes |
176 | 0 | if (vertexNum < 0 || |
177 | 0 | vertexNum > (isPent ? NUM_PENT_VERTS : NUM_HEX_VERTS) - 1) |
178 | 0 | return INVALID_DIGIT; |
179 | | |
180 | | // Determine the vertex rotations for this cell |
181 | 0 | int rotations; |
182 | 0 | H3Error err = vertexRotations(origin, &rotations); |
183 | 0 | if (err) { |
184 | 0 | return INVALID_DIGIT; |
185 | 0 | } |
186 | | |
187 | | // Find the appropriate direction, rotating CW if necessary |
188 | 0 | return isPent ? vertexNumToDirectionPent[(vertexNum + rotations) % |
189 | 0 | NUM_PENT_VERTS] |
190 | 0 | : vertexNumToDirectionHex[(vertexNum + rotations) % |
191 | 0 | NUM_HEX_VERTS]; |
192 | 0 | } |
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 | 0 | H3Error H3_EXPORT(cellToVertex)(H3Index cell, int vertexNum, H3Index *out) { |
213 | 0 | int cellIsPentagon = H3_EXPORT(isPentagon)(cell); |
214 | 0 | int cellNumVerts = cellIsPentagon ? NUM_PENT_VERTS : NUM_HEX_VERTS; |
215 | 0 | int res = H3_GET_RESOLUTION(cell); |
216 | | |
217 | | // Check for invalid vertexes |
218 | 0 | if (vertexNum < 0 || vertexNum > cellNumVerts - 1) return E_DOMAIN; |
219 | | |
220 | | // Default the owner and vertex number to the input cell |
221 | 0 | H3Index owner = cell; |
222 | 0 | 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 | 0 | if (res == 0 || H3_GET_INDEX_DIGIT(cell, res) != CENTER_DIGIT) { |
230 | | // Get the left neighbor of the vertex, with its rotations |
231 | 0 | Direction left = directionForVertexNum(cell, vertexNum); |
232 | 0 | if (left == INVALID_DIGIT) return E_FAILED; |
233 | 0 | int lRotations = 0; |
234 | 0 | H3Index leftNeighbor; |
235 | 0 | H3Error leftNeighborError = |
236 | 0 | h3NeighborRotations(cell, left, &lRotations, &leftNeighbor); |
237 | 0 | if (leftNeighborError) return leftNeighborError; |
238 | | // Set to owner if lowest index |
239 | 0 | if (leftNeighbor < owner) owner = leftNeighbor; |
240 | | |
241 | | // As above, skip the right neighbor if the left is known lowest |
242 | 0 | 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 | 0 | Direction right = directionForVertexNum( |
246 | 0 | cell, (vertexNum - 1 + cellNumVerts) % cellNumVerts); |
247 | | // This case should be unreachable; invalid verts fail earlier |
248 | 0 | if (NEVER(right == INVALID_DIGIT)) return E_FAILED; |
249 | 0 | int rRotations = 0; |
250 | 0 | H3Index rightNeighbor; |
251 | 0 | H3Error rightNeighborError = |
252 | 0 | h3NeighborRotations(cell, right, &rRotations, &rightNeighbor); |
253 | 0 | if (rightNeighborError) return rightNeighborError; |
254 | | // Set to owner if lowest index |
255 | 0 | if (rightNeighbor < owner) { |
256 | 0 | owner = rightNeighbor; |
257 | 0 | Direction dir = |
258 | 0 | H3_EXPORT(isPentagon)(owner) |
259 | 0 | ? directionForNeighbor(owner, cell) |
260 | 0 | : DIRECTIONS[(revNeighborDirectionsHex[right] + |
261 | 0 | rRotations) % |
262 | 0 | NUM_HEX_VERTS]; |
263 | 0 | ownerVertexNum = vertexNumForDirection(owner, dir); |
264 | 0 | } |
265 | 0 | } |
266 | | |
267 | | // Determine the vertex number for the left neighbor |
268 | 0 | if (owner == leftNeighbor) { |
269 | 0 | int ownerIsPentagon = H3_EXPORT(isPentagon)(owner); |
270 | 0 | Direction dir = |
271 | 0 | ownerIsPentagon |
272 | 0 | ? directionForNeighbor(owner, cell) |
273 | 0 | : DIRECTIONS[(revNeighborDirectionsHex[left] + lRotations) % |
274 | 0 | 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 | 0 | ownerVertexNum = vertexNumForDirection(owner, dir) + 1; |
279 | 0 | if (ownerVertexNum == NUM_HEX_VERTS || |
280 | 0 | (ownerIsPentagon && ownerVertexNum == NUM_PENT_VERTS)) { |
281 | 0 | ownerVertexNum = 0; |
282 | 0 | } |
283 | 0 | } |
284 | 0 | } |
285 | | |
286 | | // Create the vertex index |
287 | 0 | H3Index vertex = owner; |
288 | 0 | H3_SET_MODE(vertex, H3_VERTEX_MODE); |
289 | 0 | H3_SET_RESERVED_BITS(vertex, ownerVertexNum); |
290 | 0 | *out = vertex; |
291 | |
|
292 | 0 | return E_SUCCESS; |
293 | 0 | } |
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 | 0 | 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 | 0 | bool isPent = H3_EXPORT(isPentagon)(cell); |
304 | 0 | for (int i = 0; i < NUM_HEX_VERTS; i++) { |
305 | 0 | if (i == 5 && isPent) { |
306 | 0 | vertexes[i] = H3_NULL; |
307 | 0 | } else { |
308 | 0 | H3Error cellError = H3_EXPORT(cellToVertex)(cell, i, &vertexes[i]); |
309 | 0 | if (cellError) { |
310 | 0 | return cellError; |
311 | 0 | } |
312 | 0 | } |
313 | 0 | } |
314 | 0 | return E_SUCCESS; |
315 | 0 | } |
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 | 0 | H3Error H3_EXPORT(vertexToLatLng)(H3Index vertex, LatLng *coord) { |
323 | | // Get the vertex number and owner from the vertex |
324 | 0 | int vertexNum = H3_GET_RESERVED_BITS(vertex); |
325 | 0 | H3Index owner = vertex; |
326 | 0 | H3_SET_MODE(owner, H3_CELL_MODE); |
327 | 0 | H3_SET_RESERVED_BITS(owner, 0); |
328 | | |
329 | | // Get the single vertex from the boundary |
330 | 0 | CellBoundary gb; |
331 | 0 | FaceIJK fijk; |
332 | 0 | H3Error fijkError = _h3ToFaceIjk(owner, &fijk); |
333 | 0 | if (fijkError) { |
334 | 0 | return fijkError; |
335 | 0 | } |
336 | 0 | int res = H3_GET_RESOLUTION(owner); |
337 | |
|
338 | 0 | if (H3_EXPORT(isPentagon)(owner)) { |
339 | 0 | _faceIjkPentToCellBoundary(&fijk, res, vertexNum, 1, &gb); |
340 | 0 | } else { |
341 | 0 | _faceIjkToCellBoundary(&fijk, res, vertexNum, 1, &gb); |
342 | 0 | } |
343 | | |
344 | | // Copy from boundary to output coord |
345 | 0 | *coord = gb.verts[0]; |
346 | 0 | return E_SUCCESS; |
347 | 0 | } |
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 | 0 | int H3_EXPORT(isValidVertex)(H3Index vertex) { |
355 | 0 | if (H3_GET_MODE(vertex) != H3_VERTEX_MODE) { |
356 | 0 | return 0; |
357 | 0 | } |
358 | | |
359 | 0 | int vertexNum = H3_GET_RESERVED_BITS(vertex); |
360 | 0 | H3Index owner = vertex; |
361 | 0 | H3_SET_MODE(owner, H3_CELL_MODE); |
362 | 0 | H3_SET_RESERVED_BITS(owner, 0); |
363 | |
|
364 | 0 | if (!H3_EXPORT(isValidCell)(owner)) { |
365 | 0 | return 0; |
366 | 0 | } |
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 | 0 | H3Index canonical; |
371 | 0 | if (H3_EXPORT(cellToVertex)(owner, vertexNum, &canonical)) { |
372 | 0 | return 0; |
373 | 0 | } |
374 | | |
375 | 0 | return vertex == canonical ? 1 : 0; |
376 | 0 | } |