Coverage Report

Created: 2026-07-15 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/s2geometry/src/s2/s2padded_cell.cc
Line
Count
Source
1
// Copyright 2013 Google Inc. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
//
15
16
// Author: ericv@google.com (Eric Veach)
17
18
#include "s2/s2padded_cell.h"
19
20
#include <algorithm>
21
#include <cfloat>
22
23
#include "absl/base/optimization.h"
24
#include "absl/log/absl_check.h"
25
#include "absl/numeric/bits.h"
26
#include "s2/r1interval.h"
27
#include "s2/r2rect.h"
28
#include "s2/s2cell_id.h"
29
#include "s2/s2coords.h"
30
#include "s2/s2coords_internal.h"
31
#include "s2/s2point.h"
32
33
using std::max;
34
using std::min;
35
using S2::internal::kSwapMask;
36
using S2::internal::kInvertMask;
37
using S2::internal::kIJtoPos;
38
using S2::internal::kPosToOrientation;
39
40
S2PaddedCell::S2PaddedCell(S2CellId id, double padding)
41
0
    : id_(id), padding_(padding) {
42
0
  if (id_.is_face()) {
43
    // Fast path for constructing a top-level face (the most common case).
44
0
    double limit = 1 + padding;
45
0
    bound_ = R2Rect(R1Interval(-limit, limit), R1Interval(-limit, limit));
46
0
    middle_ = R2Rect(R1Interval(-padding, padding),
47
0
                     R1Interval(-padding, padding));
48
0
    ij_lo_[0] = ij_lo_[1] = 0;
49
0
    orientation_ = id_.face() & 1;
50
0
    level_ = 0;
51
0
  } else {
52
0
    int ij[2];
53
0
    id.ToFaceIJOrientation(&ij[0], &ij[1], &orientation_);
54
0
    level_ = id.level();
55
0
    bound_ = S2CellId::IJLevelToBoundUV(ij, level_).Expanded(padding);
56
0
    int ij_size = S2CellId::GetSizeIJ(level_);
57
0
    ij_lo_[0] = ij[0] & -ij_size;
58
0
    ij_lo_[1] = ij[1] & -ij_size;
59
0
  }
60
0
}
61
62
S2PaddedCell::S2PaddedCell(const S2PaddedCell& parent, int i, int j)
63
0
    : padding_(parent.padding_),
64
0
      bound_(parent.bound_),
65
0
      level_(parent.level_ + 1) {
66
  // Compute the position and orientation of the child incrementally from the
67
  // orientation of the parent.
68
0
  int pos = kIJtoPos[parent.orientation_][2*i+j];
69
0
  id_ = parent.id_.child(pos);
70
0
  int ij_size = S2CellId::GetSizeIJ(level_);
71
0
  ij_lo_[0] = parent.ij_lo_[0] + i * ij_size;
72
0
  ij_lo_[1] = parent.ij_lo_[1] + j * ij_size;
73
0
  orientation_ = parent.orientation_ ^ kPosToOrientation[pos];
74
  // For each child, one corner of the bound is taken directly from the parent
75
  // while the diagonally opposite corner is taken from middle().
76
0
  const R2Rect& middle = parent.middle();
77
0
  bound_[0][1-i] = middle[0][1-i];
78
0
  bound_[1][1-j] = middle[1][1-j];
79
0
}
80
81
0
const R2Rect& S2PaddedCell::middle() const {
82
  // We compute this field lazily because it is not needed the majority of the
83
  // time (i.e., for cells where the recursion terminates).
84
0
  if (middle_.is_empty()) {
85
0
    int ij_size = S2CellId::GetSizeIJ(level_);
86
0
    double u = S2::STtoUV(S2::SiTitoST(2 * ij_lo_[0] + ij_size));
87
0
    double v = S2::STtoUV(S2::SiTitoST(2 * ij_lo_[1] + ij_size));
88
0
    middle_ = R2Rect(R1Interval(u - padding_, u + padding_),
89
0
                     R1Interval(v - padding_, v + padding_));
90
0
  }
91
0
  return middle_;
92
0
}
93
94
0
S2Point S2PaddedCell::GetCenter() const {
95
0
  int ij_size = S2CellId::GetSizeIJ(level_);
96
0
  unsigned int si = 2 * ij_lo_[0] + ij_size;
97
0
  unsigned int ti = 2 * ij_lo_[1] + ij_size;
98
0
  return S2::FaceSiTitoXYZ(id_.face(), si, ti).Normalize();
99
0
}
100
101
0
S2Point S2PaddedCell::GetEntryVertex() const {
102
  // The curve enters at the (0,0) vertex unless the axis directions are
103
  // reversed, in which case it enters at the (1,1) vertex.
104
0
  unsigned int i = ij_lo_[0];
105
0
  unsigned int j = ij_lo_[1];
106
0
  if (orientation_ & kInvertMask) {
107
0
    int ij_size = S2CellId::GetSizeIJ(level_);
108
0
    i += ij_size;
109
0
    j += ij_size;
110
0
  }
111
0
  return S2::FaceSiTitoXYZ(id_.face(), 2 * i, 2 * j).Normalize();
112
0
}
113
114
0
S2Point S2PaddedCell::GetExitVertex() const {
115
  // The curve exits at the (1,0) vertex unless the axes are swapped or
116
  // inverted but not both, in which case it exits at the (0,1) vertex.
117
0
  unsigned int i = ij_lo_[0];
118
0
  unsigned int j = ij_lo_[1];
119
0
  int ij_size = S2CellId::GetSizeIJ(level_);
120
0
  if (orientation_ == 0 || orientation_ == kSwapMask + kInvertMask) {
121
0
    i += ij_size;
122
0
  } else {
123
0
    j += ij_size;
124
0
  }
125
0
  return S2::FaceSiTitoXYZ(id_.face(), 2 * i, 2 * j).Normalize();
126
0
}
127
128
0
S2CellId S2PaddedCell::ShrinkToFit(const R2Rect& rect) const {
129
0
  ABSL_DCHECK(bound().Intersects(rect));
130
131
  // Quick rejection test: if "rect" contains the center of this cell along
132
  // either axis, then no further shrinking is possible.
133
0
  int ij_size = S2CellId::GetSizeIJ(level_);
134
0
  if (level_ == 0) {
135
    // Fast path (most calls to this function start with a face cell).
136
0
    if (rect[0].Contains(0) || rect[1].Contains(0)) return id();
137
0
  } else {
138
0
    if (rect[0].Contains(S2::STtoUV(S2::SiTitoST(2 * ij_lo_[0] + ij_size))) ||
139
0
        rect[1].Contains(S2::STtoUV(S2::SiTitoST(2 * ij_lo_[1] + ij_size)))) {
140
0
      return id();
141
0
    }
142
0
  }
143
  // Otherwise we expand "rect" by the given padding() on all sides and find
144
  // the range of coordinates that it spans along the i- and j-axes.  We then
145
  // compute the highest bit position at which the min and max coordinates
146
  // differ.  This corresponds to the first cell level at which at least two
147
  // children intersect "rect".
148
149
  // Increase the padding to compensate for the error in S2::UVtoST().
150
  // (The constant below is a provable upper bound on the additional error.)
151
0
  R2Rect padded = rect.Expanded(padding() + 1.5 * DBL_EPSILON);
152
0
  int ij_min[2];  // Min i- or j- coordinate spanned by "padded"
153
0
  int ij_xor[2];  // XOR of the min and max i- or j-coordinates
154
0
  for (int d = 0; d < 2; ++d) {
155
0
    ij_min[d] = max(ij_lo_[d], S2::STtoIJ(S2::UVtoST(padded[d][0])));
156
0
    int ij_max = min(ij_lo_[d] + ij_size - 1,
157
0
                     S2::STtoIJ(S2::UVtoST(padded[d][1])));
158
0
    ij_xor[d] = ij_min[d] ^ ij_max;
159
0
  }
160
  // Compute the highest bit position where the two i- or j-endpoints differ,
161
  // and then choose the cell level that includes both of these endpoints.  So
162
  // if both pairs of endpoints are equal we choose kMaxLevel; if they differ
163
  // only at bit 0, we choose (kMaxLevel - 1), and so on.
164
0
  unsigned int level_msb = ((ij_xor[0] | ij_xor[1]) << 1) + 1;
165
0
  ABSL_ASSUME(level_msb != 0);
166
0
  int level = S2CellId::kMaxLevel - (absl::bit_width(level_msb) - 1);
167
0
  if (level <= level_) return id();
168
0
  return S2CellId::FromFaceIJ(id().face(), ij_min[0], ij_min[1]).parent(level);
169
0
}