in h3_algos.c [352:499]
H3Error h3NeighborRotations(H3Index origin, Direction dir, int *rotations,
H3Index *out) {
H3Index current = origin;
if (dir < CENTER_DIGIT || dir >= INVALID_DIGIT) {
return E_FAILED;
}
// Ensure that rotations is modulo'd by 6 before any possible addition,
// to protect against signed integer overflow.
*rotations = *rotations % 6;
for (int i = 0; i < *rotations; i++) {
dir = _rotate60ccw(dir);
}
int newRotations = 0;
int oldBaseCell = H3_GET_BASE_CELL(current);
if (NEVER(oldBaseCell < 0) || oldBaseCell >= NUM_BASE_CELLS) {
// Base cells less than zero can not be represented in an index
return E_CELL_INVALID;
}
Direction oldLeadingDigit = _h3LeadingNonZeroDigit(current);
// Adjust the indexing digits and, if needed, the base cell.
int r = H3_GET_RESOLUTION(current) - 1;
while (true) {
if (r == -1) {
H3_SET_BASE_CELL(current, baseCellNeighbors[oldBaseCell][dir]);
newRotations = baseCellNeighbor60CCWRots[oldBaseCell][dir];
if (H3_GET_BASE_CELL(current) == INVALID_BASE_CELL) {
// Adjust for the deleted k vertex at the base cell level.
// This edge actually borders a different neighbor.
H3_SET_BASE_CELL(current,
baseCellNeighbors[oldBaseCell][IK_AXES_DIGIT]);
newRotations =
baseCellNeighbor60CCWRots[oldBaseCell][IK_AXES_DIGIT];
// perform the adjustment for the k-subsequence we're skipping
// over.
current = _h3Rotate60ccw(current);
*rotations = *rotations + 1;
}
break;
} else {
Direction oldDigit = H3_GET_INDEX_DIGIT(current, r + 1);
Direction nextDir;
if (oldDigit == INVALID_DIGIT) {
// Only possible on invalid input
return E_CELL_INVALID;
} else if (isResolutionClassIII(r + 1)) {
H3_SET_INDEX_DIGIT(current, r + 1, NEW_DIGIT_II[oldDigit][dir]);
nextDir = NEW_ADJUSTMENT_II[oldDigit][dir];
} else {
H3_SET_INDEX_DIGIT(current, r + 1,
NEW_DIGIT_III[oldDigit][dir]);
nextDir = NEW_ADJUSTMENT_III[oldDigit][dir];
}
if (nextDir != CENTER_DIGIT) {
dir = nextDir;
r--;
} else {
// No more adjustment to perform
break;
}
}
}
int newBaseCell = H3_GET_BASE_CELL(current);
if (_isBaseCellPentagon(newBaseCell)) {
int alreadyAdjustedKSubsequence = 0;
// force rotation out of missing k-axes sub-sequence
if (_h3LeadingNonZeroDigit(current) == K_AXES_DIGIT) {
if (oldBaseCell != newBaseCell) {
// in this case, we traversed into the deleted
// k subsequence of a pentagon base cell.
// We need to rotate out of that case depending
// on how we got here.
// check for a cw/ccw offset face; default is ccw
if (ALWAYS(_baseCellIsCwOffset(
newBaseCell,
baseCellData[oldBaseCell].homeFijk.face))) {
current = _h3Rotate60cw(current);
} else {
// See cwOffsetPent in testGridDisk.c for why this is
// unreachable.
current = _h3Rotate60ccw(current);
}
alreadyAdjustedKSubsequence = 1;
} else {
// In this case, we traversed into the deleted
// k subsequence from within the same pentagon
// base cell.
if (oldLeadingDigit == CENTER_DIGIT) {
// Undefined: the k direction is deleted from here
return E_PENTAGON;
} else if (oldLeadingDigit == JK_AXES_DIGIT) {
// Rotate out of the deleted k subsequence
// We also need an additional change to the direction we're
// moving in
current = _h3Rotate60ccw(current);
*rotations = *rotations + 1;
} else if (oldLeadingDigit == IK_AXES_DIGIT) {
// Rotate out of the deleted k subsequence
// We also need an additional change to the direction we're
// moving in
current = _h3Rotate60cw(current);
*rotations = *rotations + 5;
} else {
// TODO: Should never occur, but is reachable by fuzzer
return E_FAILED;
}
}
}
for (int i = 0; i < newRotations; i++)
current = _h3RotatePent60ccw(current);
// Account for differing orientation of the base cells (this edge
// might not follow properties of some other edges.)
if (oldBaseCell != newBaseCell) {
if (_isBaseCellPolarPentagon(newBaseCell)) {
// 'polar' base cells behave differently because they have all
// i neighbors.
if (oldBaseCell != 118 && oldBaseCell != 8 &&
_h3LeadingNonZeroDigit(current) != JK_AXES_DIGIT) {
*rotations = *rotations + 1;
}
} else if (_h3LeadingNonZeroDigit(current) == IK_AXES_DIGIT &&
!alreadyAdjustedKSubsequence) {
// account for distortion introduced to the 5 neighbor by the
// deleted k subsequence.
*rotations = *rotations + 1;
}
}
} else {
for (int i = 0; i < newRotations; i++)
current = _h3Rotate60ccw(current);
}
*rotations = (*rotations + newRotations) % 6;
*out = current;
return E_SUCCESS;
}