1/*
2 * Copyright 2018 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 localij.c
17 * @brief Local IJ coordinate space functions
18 *
19 * These functions try to provide a useful coordinate space in the vicinity of
20 * an origin index.
21 */
22#include <faceijk.h>
23#include <inttypes.h>
24#include <math.h>
25#include <stdlib.h>
26#include <string.h>
27#include "baseCells.h"
28#include "faceijk.h"
29#include "h3Index.h"
30#include "mathExtensions.h"
31#include "stackAlloc.h"
32
33/**
34 * Origin leading digit -> index leading digit -> rotations 60 cw
35 * Either being 1 (K axis) is invalid.
36 * No good default at 0.
37 */
38const int PENTAGON_ROTATIONS[7][7] = {
39 {0, -1, 0, 0, 0, 0, 0}, // 0
40 {-1, -1, -1, -1, -1, -1, -1}, // 1
41 {0, -1, 0, 0, 0, 1, 0}, // 2
42 {0, -1, 0, 0, 1, 1, 0}, // 3
43 {0, -1, 0, 5, 0, 0, 0}, // 4
44 {0, -1, 5, 5, 0, 0, 0}, // 5
45 {0, -1, 0, 0, 0, 0, 0}, // 6
46};
47/**
48 * Reverse base cell direction -> leading index digit -> rotations 60 ccw.
49 * For reversing the rotation introduced in PENTAGON_ROTATIONS when
50 * the origin is on a pentagon (regardless of the base cell of the index.)
51 */
52const int PENTAGON_ROTATIONS_REVERSE[7][7] = {
53 {0, 0, 0, 0, 0, 0, 0}, // 0
54 {-1, -1, -1, -1, -1, -1, -1}, // 1
55 {0, 1, 0, 0, 0, 0, 0}, // 2
56 {0, 1, 0, 0, 0, 1, 0}, // 3
57 {0, 5, 0, 0, 0, 0, 0}, // 4
58 {0, 5, 0, 5, 0, 0, 0}, // 5
59 {0, 0, 0, 0, 0, 0, 0}, // 6
60};
61/**
62 * Reverse base cell direction -> leading index digit -> rotations 60 ccw.
63 * For reversing the rotation introduced in PENTAGON_ROTATIONS when the index is
64 * on a pentagon and the origin is not.
65 */
66const int PENTAGON_ROTATIONS_REVERSE_NONPOLAR[7][7] = {
67 {0, 0, 0, 0, 0, 0, 0}, // 0
68 {-1, -1, -1, -1, -1, -1, -1}, // 1
69 {0, 1, 0, 0, 0, 0, 0}, // 2
70 {0, 1, 0, 0, 0, 1, 0}, // 3
71 {0, 5, 0, 0, 0, 0, 0}, // 4
72 {0, 1, 0, 5, 1, 1, 0}, // 5
73 {0, 0, 0, 0, 0, 0, 0}, // 6
74};
75/**
76 * Reverse base cell direction -> leading index digit -> rotations 60 ccw.
77 * For reversing the rotation introduced in PENTAGON_ROTATIONS when the index is
78 * on a polar pentagon and the origin is not.
79 */
80const int PENTAGON_ROTATIONS_REVERSE_POLAR[7][7] = {
81 {0, 0, 0, 0, 0, 0, 0}, // 0
82 {-1, -1, -1, -1, -1, -1, -1}, // 1
83 {0, 1, 1, 1, 1, 1, 1}, // 2
84 {0, 1, 0, 0, 0, 1, 0}, // 3
85 {0, 1, 0, 0, 1, 1, 1}, // 4
86 {0, 1, 0, 5, 1, 1, 0}, // 5
87 {0, 1, 1, 0, 1, 1, 1}, // 6
88};
89
90// Simply prohibit many pentagon distortion cases rather than handling them.
91const bool FAILED_DIRECTIONS_II[7][7] = {
92 {false, false, false, false, false, false, false}, // 0
93 {false, false, false, false, false, false, false}, // 1
94 {false, false, false, false, true, false, false}, // 2
95 {false, false, false, false, false, false, true}, // 3
96 {false, false, false, true, false, false, false}, // 4
97 {false, false, true, false, false, false, false}, // 5
98 {false, false, false, false, false, true, false}, // 6
99};
100const bool FAILED_DIRECTIONS_III[7][7] = {
101 {false, false, false, false, false, false, false}, // 0
102 {false, false, false, false, false, false, false}, // 1
103 {false, false, false, false, false, true, false}, // 2
104 {false, false, false, false, true, false, false}, // 3
105 {false, false, true, false, false, false, false}, // 4
106 {false, false, false, false, false, false, true}, // 5
107 {false, false, false, true, false, false, false}, // 6
108};
109
110/**
111 * Produces ijk+ coordinates for an index anchored by an origin.
112 *
113 * The coordinate space used by this function may have deleted
114 * regions or warping due to pentagonal distortion.
115 *
116 * Coordinates are only comparable if they come from the same
117 * origin index.
118 *
119 * Failure may occur if the index is too far away from the origin
120 * or if the index is on the other side of a pentagon.
121 *
122 * @param origin An anchoring index for the ijk+ coordinate system.
123 * @param index Index to find the coordinates of
124 * @param out ijk+ coordinates of the index will be placed here on success
125 * @return 0 on success, or another value on failure.
126 */
127int h3ToLocalIjk(H3Index origin, H3Index h3, CoordIJK* out) {
128 int res = H3_GET_RESOLUTION(origin);
129
130 if (res != H3_GET_RESOLUTION(h3)) {
131 return 1;
132 }
133
134 int originBaseCell = H3_GET_BASE_CELL(origin);
135 int baseCell = H3_GET_BASE_CELL(h3);
136
137 // Direction from origin base cell to index base cell
138 Direction dir = 0;
139 Direction revDir = 0;
140 if (originBaseCell != baseCell) {
141 dir = _getBaseCellDirection(originBaseCell, baseCell);
142 if (dir == INVALID_DIGIT) {
143 // Base cells are not neighbors, can't unfold.
144 return 2;
145 }
146 revDir = _getBaseCellDirection(baseCell, originBaseCell);
147 assert(revDir != INVALID_DIGIT);
148 }
149
150 int originOnPent = _isBaseCellPentagon(originBaseCell);
151 int indexOnPent = _isBaseCellPentagon(baseCell);
152
153 FaceIJK indexFijk = {0};
154 if (dir != CENTER_DIGIT) {
155 // Rotate index into the orientation of the origin base cell.
156 // cw because we are undoing the rotation into that base cell.
157 int baseCellRotations = baseCellNeighbor60CCWRots[originBaseCell][dir];
158 if (indexOnPent) {
159 for (int i = 0; i < baseCellRotations; i++) {
160 h3 = _h3RotatePent60cw(h3);
161
162 revDir = _rotate60cw(revDir);
163 if (revDir == K_AXES_DIGIT) revDir = _rotate60cw(revDir);
164 }
165 } else {
166 for (int i = 0; i < baseCellRotations; i++) {
167 h3 = _h3Rotate60cw(h3);
168
169 revDir = _rotate60cw(revDir);
170 }
171 }
172 }
173 // Face is unused. This produces coordinates in base cell coordinate space.
174 _h3ToFaceIjkWithInitializedFijk(h3, &indexFijk);
175
176 if (dir != CENTER_DIGIT) {
177 assert(baseCell != originBaseCell);
178 assert(!(originOnPent && indexOnPent));
179
180 int pentagonRotations = 0;
181 int directionRotations = 0;
182
183 if (originOnPent) {
184 int originLeadingDigit = _h3LeadingNonZeroDigit(origin);
185
186 // TODO: This previously included the Class III-based checks
187 // as in the index-on-pentagon case below, but these were
188 // removed due to some failure cases. It is possible that we
189 // could restrict this error to a narrower set of cases.
190 // https://github.com/uber/h3/issues/163
191 if (FAILED_DIRECTIONS_III[originLeadingDigit][dir] ||
192 FAILED_DIRECTIONS_II[originLeadingDigit][dir]) {
193 // TODO this part of the pentagon might not be unfolded
194 // correctly.
195 return 3;
196 }
197
198 directionRotations = PENTAGON_ROTATIONS[originLeadingDigit][dir];
199 pentagonRotations = directionRotations;
200 } else if (indexOnPent) {
201 int indexLeadingDigit = _h3LeadingNonZeroDigit(h3);
202
203 if ((isResClassIII(res) &&
204 FAILED_DIRECTIONS_III[indexLeadingDigit][revDir]) ||
205 (!isResClassIII(res) &&
206 FAILED_DIRECTIONS_II[indexLeadingDigit][revDir])) {
207 // TODO this part of the pentagon might not be unfolded
208 // correctly.
209 return 4;
210 }
211
212 pentagonRotations = PENTAGON_ROTATIONS[revDir][indexLeadingDigit];
213 }
214
215 assert(pentagonRotations >= 0);
216 assert(directionRotations >= 0);
217
218 for (int i = 0; i < pentagonRotations; i++) {
219 _ijkRotate60cw(&indexFijk.coord);
220 }
221
222 CoordIJK offset = {0};
223 _neighbor(&offset, dir);
224 // Scale offset based on resolution
225 for (int r = res - 1; r >= 0; r--) {
226 if (isResClassIII(r + 1)) {
227 // rotate ccw
228 _downAp7(&offset);
229 } else {
230 // rotate cw
231 _downAp7r(&offset);
232 }
233 }
234
235 for (int i = 0; i < directionRotations; i++) {
236 _ijkRotate60cw(&offset);
237 }
238
239 // Perform necessary translation
240 _ijkAdd(&indexFijk.coord, &offset, &indexFijk.coord);
241 _ijkNormalize(&indexFijk.coord);
242 } else if (originOnPent && indexOnPent) {
243 // If the origin and index are on pentagon, and we checked that the base
244 // cells are the same or neighboring, then they must be the same base
245 // cell.
246 assert(baseCell == originBaseCell);
247
248 int originLeadingDigit = _h3LeadingNonZeroDigit(origin);
249 int indexLeadingDigit = _h3LeadingNonZeroDigit(h3);
250
251 if (FAILED_DIRECTIONS_III[originLeadingDigit][indexLeadingDigit] ||
252 FAILED_DIRECTIONS_II[originLeadingDigit][indexLeadingDigit]) {
253 // TODO this part of the pentagon might not be unfolded
254 // correctly.
255 return 5;
256 }
257
258 int withinPentagonRotations =
259 PENTAGON_ROTATIONS[originLeadingDigit][indexLeadingDigit];
260
261 for (int i = 0; i < withinPentagonRotations; i++) {
262 _ijkRotate60cw(&indexFijk.coord);
263 }
264 }
265
266 *out = indexFijk.coord;
267 return 0;
268}
269
270/**
271 * Produces an index for ijk+ coordinates anchored by an origin.
272 *
273 * The coordinate space used by this function may have deleted
274 * regions or warping due to pentagonal distortion.
275 *
276 * Failure may occur if the coordinates are too far away from the origin
277 * or if the index is on the other side of a pentagon.
278 *
279 * @param origin An anchoring index for the ijk+ coordinate system.
280 * @param ijk IJK+ Coordinates to find the index of
281 * @param out The index will be placed here on success
282 * @return 0 on success, or another value on failure.
283 */
284int localIjkToH3(H3Index origin, const CoordIJK* ijk, H3Index* out) {
285 int res = H3_GET_RESOLUTION(origin);
286 int originBaseCell = H3_GET_BASE_CELL(origin);
287 int originOnPent = _isBaseCellPentagon(originBaseCell);
288
289 // This logic is very similar to faceIjkToH3
290 // initialize the index
291 *out = H3_INIT;
292 H3_SET_MODE(*out, H3_HEXAGON_MODE);
293 H3_SET_RESOLUTION(*out, res);
294
295 // check for res 0/base cell
296 if (res == 0) {
297 if (ijk->i > 1 || ijk->i > 1 || ijk->i > 1) {
298 // out of range input
299 return 1;
300 }
301
302 const Direction dir = _unitIjkToDigit(ijk);
303 const int newBaseCell = _getBaseCellNeighbor(originBaseCell, dir);
304 if (newBaseCell == INVALID_BASE_CELL) {
305 // Moving in an invalid direction off a pentagon.
306 return 1;
307 }
308 H3_SET_BASE_CELL(*out, newBaseCell);
309 return 0;
310 }
311
312 // we need to find the correct base cell offset (if any) for this H3 index;
313 // start with the passed in base cell and resolution res ijk coordinates
314 // in that base cell's coordinate system
315 CoordIJK ijkCopy = *ijk;
316
317 // build the H3Index from finest res up
318 // adjust r for the fact that the res 0 base cell offsets the indexing
319 // digits
320 for (int r = res - 1; r >= 0; r--) {
321 CoordIJK lastIJK = ijkCopy;
322 CoordIJK lastCenter;
323 if (isResClassIII(r + 1)) {
324 // rotate ccw
325 _upAp7(&ijkCopy);
326 lastCenter = ijkCopy;
327 _downAp7(&lastCenter);
328 } else {
329 // rotate cw
330 _upAp7r(&ijkCopy);
331 lastCenter = ijkCopy;
332 _downAp7r(&lastCenter);
333 }
334
335 CoordIJK diff;
336 _ijkSub(&lastIJK, &lastCenter, &diff);
337 _ijkNormalize(&diff);
338
339 H3_SET_INDEX_DIGIT(*out, r + 1, _unitIjkToDigit(&diff));
340 }
341
342 // ijkCopy should now hold the IJK of the base cell in the
343 // coordinate system of the current base cell
344
345 if (ijkCopy.i > 1 || ijkCopy.j > 1 || ijkCopy.k > 1) {
346 // out of range input
347 return 2;
348 }
349
350 // lookup the correct base cell
351 Direction dir = _unitIjkToDigit(&ijkCopy);
352 int baseCell = _getBaseCellNeighbor(originBaseCell, dir);
353 // If baseCell is invalid, it must be because the origin base cell is a
354 // pentagon, and because pentagon base cells do not border each other,
355 // baseCell must not be a pentagon.
356 int indexOnPent =
357 (baseCell == INVALID_BASE_CELL ? 0 : _isBaseCellPentagon(baseCell));
358
359 if (dir != CENTER_DIGIT) {
360 // If the index is in a warped direction, we need to unwarp the base
361 // cell direction. There may be further need to rotate the index digits.
362 int pentagonRotations = 0;
363 if (originOnPent) {
364 const Direction originLeadingDigit = _h3LeadingNonZeroDigit(origin);
365 pentagonRotations =
366 PENTAGON_ROTATIONS_REVERSE[originLeadingDigit][dir];
367 for (int i = 0; i < pentagonRotations; i++) {
368 dir = _rotate60ccw(dir);
369 }
370 // The pentagon rotations are being chosen so that dir is not the
371 // deleted direction. If it still happens, it means we're moving
372 // into a deleted subsequence, so there is no index here.
373 if (dir == K_AXES_DIGIT) {
374 return 3;
375 }
376 baseCell = _getBaseCellNeighbor(originBaseCell, dir);
377
378 // indexOnPent does not need to be checked again since no pentagon
379 // base cells border each other.
380 assert(baseCell != INVALID_BASE_CELL);
381 assert(!_isBaseCellPentagon(baseCell));
382 }
383
384 // Now we can determine the relation between the origin and target base
385 // cell.
386 const int baseCellRotations =
387 baseCellNeighbor60CCWRots[originBaseCell][dir];
388 assert(baseCellRotations >= 0);
389
390 // Adjust for pentagon warping within the base cell. The base cell
391 // should be in the right location, so now we need to rotate the index
392 // back. We might not need to check for errors since we would just be
393 // double mapping.
394 if (indexOnPent) {
395 const Direction revDir =
396 _getBaseCellDirection(baseCell, originBaseCell);
397 assert(revDir != INVALID_DIGIT);
398
399 // Adjust for the different coordinate space in the two base cells.
400 // This is done first because we need to do the pentagon rotations
401 // based on the leading digit in the pentagon's coordinate system.
402 for (int i = 0; i < baseCellRotations; i++) {
403 *out = _h3Rotate60ccw(*out);
404 }
405
406 const Direction indexLeadingDigit = _h3LeadingNonZeroDigit(*out);
407 if (_isBaseCellPolarPentagon(baseCell)) {
408 pentagonRotations =
409 PENTAGON_ROTATIONS_REVERSE_POLAR[revDir][indexLeadingDigit];
410 } else {
411 pentagonRotations =
412 PENTAGON_ROTATIONS_REVERSE_NONPOLAR[revDir]
413 [indexLeadingDigit];
414 }
415
416 assert(pentagonRotations >= 0);
417 for (int i = 0; i < pentagonRotations; i++) {
418 *out = _h3RotatePent60ccw(*out);
419 }
420 } else {
421 assert(pentagonRotations >= 0);
422 for (int i = 0; i < pentagonRotations; i++) {
423 *out = _h3Rotate60ccw(*out);
424 }
425
426 // Adjust for the different coordinate space in the two base cells.
427 for (int i = 0; i < baseCellRotations; i++) {
428 *out = _h3Rotate60ccw(*out);
429 }
430 }
431 } else if (originOnPent && indexOnPent) {
432 const int originLeadingDigit = _h3LeadingNonZeroDigit(origin);
433 const int indexLeadingDigit = _h3LeadingNonZeroDigit(*out);
434
435 const int withinPentagonRotations =
436 PENTAGON_ROTATIONS_REVERSE[originLeadingDigit][indexLeadingDigit];
437 assert(withinPentagonRotations >= 0);
438
439 for (int i = 0; i < withinPentagonRotations; i++) {
440 *out = _h3Rotate60ccw(*out);
441 }
442 }
443
444 if (indexOnPent) {
445 // TODO: There are cases in h3ToLocalIjk which are failed but not
446 // accounted for here - instead just fail if the recovered index is
447 // invalid.
448 if (_h3LeadingNonZeroDigit(*out) == K_AXES_DIGIT) {
449 return 4;
450 }
451 }
452
453 H3_SET_BASE_CELL(*out, baseCell);
454 return 0;
455}
456
457/**
458 * Produces ij coordinates for an index anchored by an origin.
459 *
460 * The coordinate space used by this function may have deleted
461 * regions or warping due to pentagonal distortion.
462 *
463 * Coordinates are only comparable if they come from the same
464 * origin index.
465 *
466 * Failure may occur if the index is too far away from the origin
467 * or if the index is on the other side of a pentagon.
468 *
469 * This function is experimental, and its output is not guaranteed
470 * to be compatible across different versions of H3.
471 *
472 * @param origin An anchoring index for the ij coordinate system.
473 * @param index Index to find the coordinates of
474 * @param out ij coordinates of the index will be placed here on success
475 * @return 0 on success, or another value on failure.
476 */
477int H3_EXPORT(experimentalH3ToLocalIj)(H3Index origin, H3Index h3,
478 CoordIJ* out) {
479 // This function is currently experimental. Once ready to be part of the
480 // non-experimental API, this function (with the experimental prefix) will
481 // be marked as deprecated and to be removed in the next major version. It
482 // will be replaced with a non-prefixed function name.
483 CoordIJK ijk;
484 int failed = h3ToLocalIjk(origin, h3, &ijk);
485 if (failed) {
486 return failed;
487 }
488
489 ijkToIj(&ijk, out);
490
491 return 0;
492}
493
494/**
495 * Produces an index for ij coordinates anchored by an origin.
496 *
497 * The coordinate space used by this function may have deleted
498 * regions or warping due to pentagonal distortion.
499 *
500 * Failure may occur if the index is too far away from the origin
501 * or if the index is on the other side of a pentagon.
502 *
503 * This function is experimental, and its output is not guaranteed
504 * to be compatible across different versions of H3.
505 *
506 * @param origin An anchoring index for the ij coordinate system.
507 * @param out ij coordinates to index.
508 * @param index Index will be placed here on success.
509 * @return 0 on success, or another value on failure.
510 */
511int H3_EXPORT(experimentalLocalIjToH3)(H3Index origin, const CoordIJ* ij,
512 H3Index* out) {
513 // This function is currently experimental. Once ready to be part of the
514 // non-experimental API, this function (with the experimental prefix) will
515 // be marked as deprecated and to be removed in the next major version. It
516 // will be replaced with a non-prefixed function name.
517 CoordIJK ijk;
518 ijToIjk(ij, &ijk);
519
520 return localIjkToH3(origin, &ijk, out);
521}
522
523/**
524 * Produces the grid distance between the two indexes.
525 *
526 * This function may fail to find the distance between two indexes, for
527 * example if they are very far apart. It may also fail when finding
528 * distances for indexes on opposite sides of a pentagon.
529 *
530 * @param origin Index to find the distance from.
531 * @param index Index to find the distance to.
532 * @return The distance, or a negative number if the library could not
533 * compute the distance.
534 */
535int H3_EXPORT(h3Distance)(H3Index origin, H3Index h3) {
536 CoordIJK originIjk, h3Ijk;
537 if (h3ToLocalIjk(origin, origin, &originIjk)) {
538 // Currently there are no tests that would cause getting the coordinates
539 // for an index the same as the origin to fail.
540 return -1; // LCOV_EXCL_LINE
541 }
542 if (h3ToLocalIjk(origin, h3, &h3Ijk)) {
543 return -1;
544 }
545
546 return ijkDistance(&originIjk, &h3Ijk);
547}
548
549/**
550 * Number of indexes in a line from the start index to the end index,
551 * to be used for allocating memory. Returns a negative number if the
552 * line cannot be computed.
553 *
554 * @param start Start index of the line
555 * @param end End index of the line
556 * @return Size of the line, or a negative number if the line cannot
557 * be computed.
558 */
559int H3_EXPORT(h3LineSize)(H3Index start, H3Index end) {
560 int distance = H3_EXPORT(h3Distance)(start, end);
561 return distance >= 0 ? distance + 1 : distance;
562}
563
564/**
565 * Given cube coords as doubles, round to valid integer coordinates. Algorithm
566 * from https://www.redblobgames.com/grids/hexagons/#rounding
567 * @param i Floating-point I coord
568 * @param j Floating-point J coord
569 * @param k Floating-point K coord
570 * @param ijk IJK coord struct, modified in place
571 */
572static void cubeRound(double i, double j, double k, CoordIJK* ijk) {
573 int ri = round(i);
574 int rj = round(j);
575 int rk = round(k);
576
577 double iDiff = fabs((double)ri - i);
578 double jDiff = fabs((double)rj - j);
579 double kDiff = fabs((double)rk - k);
580
581 // Round, maintaining valid cube coords
582 if (iDiff > jDiff && iDiff > kDiff) {
583 ri = -rj - rk;
584 } else if (jDiff > kDiff) {
585 rj = -ri - rk;
586 } else {
587 rk = -ri - rj;
588 }
589
590 ijk->i = ri;
591 ijk->j = rj;
592 ijk->k = rk;
593}
594
595/**
596 * Given two H3 indexes, return the line of indexes between them (inclusive).
597 *
598 * This function may fail to find the line between two indexes, for
599 * example if they are very far apart. It may also fail when finding
600 * distances for indexes on opposite sides of a pentagon.
601 *
602 * Notes:
603 *
604 * - The specific output of this function should not be considered stable
605 * across library versions. The only guarantees the library provides are
606 * that the line length will be `h3Distance(start, end) + 1` and that
607 * every index in the line will be a neighbor of the preceding index.
608 * - Lines are drawn in grid space, and may not correspond exactly to either
609 * Cartesian lines or great arcs.
610 *
611 * @param start Start index of the line
612 * @param end End index of the line
613 * @param out Output array, which must be of size h3LineSize(start, end)
614 * @return 0 on success, or another value on failure.
615 */
616int H3_EXPORT(h3Line)(H3Index start, H3Index end, H3Index* out) {
617 int distance = H3_EXPORT(h3Distance)(start, end);
618 // Early exit if we can't calculate the line
619 if (distance < 0) {
620 return distance;
621 }
622
623 // Get IJK coords for the start and end. We've already confirmed
624 // that these can be calculated with the distance check above.
625 CoordIJK startIjk = {0};
626 CoordIJK endIjk = {0};
627
628 // Convert H3 addresses to IJK coords
629 h3ToLocalIjk(start, start, &startIjk);
630 h3ToLocalIjk(start, end, &endIjk);
631
632 // Convert IJK to cube coordinates suitable for linear interpolation
633 ijkToCube(&startIjk);
634 ijkToCube(&endIjk);
635
636 double iStep =
637 distance ? (double)(endIjk.i - startIjk.i) / (double)distance : 0;
638 double jStep =
639 distance ? (double)(endIjk.j - startIjk.j) / (double)distance : 0;
640 double kStep =
641 distance ? (double)(endIjk.k - startIjk.k) / (double)distance : 0;
642
643 CoordIJK currentIjk = {startIjk.i, startIjk.j, startIjk.k};
644 for (int n = 0; n <= distance; n++) {
645 cubeRound((double)startIjk.i + iStep * n,
646 (double)startIjk.j + jStep * n,
647 (double)startIjk.k + kStep * n, &currentIjk);
648 // Convert cube -> ijk -> h3 index
649 cubeToIjk(&currentIjk);
650 localIjkToH3(start, &currentIjk, &out[n]);
651 }
652
653 return 0;
654}
655