blob: ce9bccfb9c287854312c099274af89f56758d1e6 [file] [log] [blame]
// Copyright 2005 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: ericv@google.com (Eric Veach)
#include "s2/s2coords.h"
#include "s2/util/bits/bits.h"
namespace S2 {
namespace internal {
// Define the "extern" constants in s2coords-internal.h.
static_assert(kSwapMask == 0x01 && kInvertMask == 0x02, "masks changed");
// kIJtoPos[orientation][ij] -> pos
int const kIJtoPos[4][4] = {
// (0,0) (0,1) (1,0) (1,1)
{0, 1, 3, 2}, // canonical order
{0, 3, 1, 2}, // axes swapped
{2, 3, 1, 0}, // bits inverted
{2, 1, 3, 0}, // swapped & inverted
};
// kPosToIJ[orientation][pos] -> ij
int const kPosToIJ[4][4] = {
// 0 1 2 3
{0, 1, 3, 2}, // canonical order: (0,0), (0,1), (1,1), (1,0)
{0, 2, 3, 1}, // axes swapped: (0,0), (1,0), (1,1), (0,1)
{3, 2, 0, 1}, // bits inverted: (1,1), (1,0), (0,0), (0,1)
{3, 1, 0, 2}, // swapped & inverted: (1,1), (0,1), (0,0), (1,0)
};
// kPosToOrientation[pos] -> orientation_modifier
int const kPosToOrientation[4] = {
kSwapMask, 0, 0, kInvertMask + kSwapMask,
};
int const kFaceUVWFaces[6][3][2] = {
{{4, 1}, {5, 2}, {3, 0}}, {{0, 3}, {5, 2}, {4, 1}},
{{0, 3}, {1, 4}, {5, 2}}, {{2, 5}, {1, 4}, {0, 3}},
{{2, 5}, {3, 0}, {1, 4}}, {{4, 1}, {3, 0}, {2, 5}}};
double const kFaceUVWAxes[6][3][3] = {
{{0, 1, 0}, {0, 0, 1}, {1, 0, 0}}, {{-1, 0, 0}, {0, 0, 1}, {0, 1, 0}},
{{-1, 0, 0}, {0, -1, 0}, {0, 0, 1}}, {{0, 0, -1}, {0, -1, 0}, {-1, 0, 0}},
{{0, 0, -1}, {1, 0, 0}, {0, -1, 0}}, {{0, 1, 0}, {1, 0, 0}, {0, 0, -1}}};
} // namespace internal
S2Point FaceXYZtoUVW(int face, S2Point const& p) {
// The result coordinates are simply the dot products of P with the (u,v,w)
// axes for the given face (see kFaceUVWAxes).
switch (face) {
case 0:
return S2Point(p.y(), p.z(), p.x());
case 1:
return S2Point(-p.x(), p.z(), p.y());
case 2:
return S2Point(-p.x(), -p.y(), p.z());
case 3:
return S2Point(-p.z(), -p.y(), -p.x());
case 4:
return S2Point(-p.z(), p.x(), -p.y());
default:
return S2Point(p.y(), p.x(), -p.z());
}
}
int XYZtoFaceSiTi(S2Point const& p,
int* face,
unsigned int* si,
unsigned int* ti) {
double u, v;
*face = XYZtoFaceUV(p, &u, &v);
*si = STtoSiTi(UVtoST(u));
*ti = STtoSiTi(UVtoST(v));
// If the levels corresponding to si,ti are not equal, then p is not a cell
// center. The si,ti values 0 and kMaxSiTi need to be handled specially
// because they do not correspond to cell centers at any valid level; they
// are mapped to level -1 by the code below.
int level = kMaxCellLevel - Bits::FindLSBSetNonZero(*si | kMaxSiTi);
if (level < 0 ||
level != kMaxCellLevel - Bits::FindLSBSetNonZero(*ti | kMaxSiTi)) {
return -1;
}
DCHECK_LE(level, kMaxCellLevel);
// In infinite precision, this test could be changed to ST == SiTi. However,
// due to rounding errors, UVtoST(XYZtoFaceUV(FaceUVtoXYZ(STtoUV(...)))) is
// not idempotent. On the other hand, center_raw is computed exactly the same
// way p was originally computed (if it is indeed the center of an S2Cell):
// the comparison can be exact.
S2Point center = FaceSiTitoXYZ(*face, *si, *ti).Normalize();
return p == center ? level : -1;
}
S2Point FaceSiTitoXYZ(int face, unsigned int si, unsigned int ti) {
double u = STtoUV(SiTitoST(si));
double v = STtoUV(SiTitoST(ti));
return FaceUVtoXYZ(face, u, v);
}
} // namespace S2