Coverage Report

Created: 2025-10-12 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/s2geometry/src/s2/internal/s2index_cell_data.cc
Line
Count
Source
1
// Copyright 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
#include "s2/internal/s2index_cell_data.h"
17
18
#include <algorithm>
19
#include <atomic>
20
#include <utility>
21
22
#include "absl/log/absl_check.h"
23
#include "s2/mutable_s2shape_index.h"
24
#include "s2/s2cell.h"
25
#include "s2/s2padded_cell.h"
26
#include "s2/s2shape.h"
27
28
namespace internal {
29
30
void S2IndexCellData::LoadCell(const S2ShapeIndex* index, S2CellId id,
31
0
                               const S2ShapeIndexCell* cell) {
32
0
  ABSL_DCHECK_NE(index, nullptr);
33
34
0
  if (index_ == index && cell_id_ == id) {
35
0
    return;
36
0
  }
37
38
0
  index_ = index;
39
40
  // Cache cell information.
41
0
  cell_ = cell;
42
0
  cell_id_ = id;
43
44
  // Reset atomic flags so we'll recompute cached values.  These form a write
45
  // barrier with the write to cell_id_ above and so should stay below it.
46
0
  s2cell_set_.store(false, std::memory_order_release);
47
0
  center_set_.store(false, std::memory_order_release);
48
49
  // Clear previous edges.  Reserve one slot in case we don't decode _any_ edges
50
  // (e.g. an interior cell or all the dimensions are unwanted, so we don't
51
  // try to create spans with a nullptr).
52
0
  edges_.clear();
53
0
  shape_regions_.clear();
54
55
  // Reset per-dimension region information.
56
0
  for (auto& region : dim_regions_) {
57
0
    region = Region();
58
0
  }
59
60
0
  int min_dim = 0;
61
0
  while (min_dim <= 2 && !dim_wanted(min_dim)) {
62
0
    ++min_dim;
63
0
  }
64
65
0
  int max_dim = 2;
66
0
  while (max_dim >= 0 && !dim_wanted(max_dim)) {
67
0
    --max_dim;
68
0
  }
69
70
  // No dimensions wanted, we're done.
71
0
  if (min_dim > 2 || max_dim < 0) {
72
0
    return;
73
0
  }
74
75
0
  for (int dim = min_dim; dim <= max_dim; ++dim) {
76
0
    int dim_start = edges_.size();
77
78
0
    for (const S2ClippedShape& clipped : cell_->clipped_shapes()) {
79
0
      int shape_id = clipped.shape_id();
80
0
      const S2Shape& shape = *index->shape(shape_id);
81
82
      // Only process the current dimension.
83
0
      if (shape.dimension() != dim) {
84
0
        continue;
85
0
      }
86
87
      // In the event we wanted dimensions 0 and 2, but not 1.
88
0
      if (!dim_wanted(dim)) {
89
0
        continue;
90
0
      }
91
92
      // Materialize clipped shape edges into the edges vector. Track where we
93
      // start so we can add information about the region for this shape.
94
0
      int shape_start = edges_.size();
95
0
      for (int i = 0; i < clipped.num_edges(); ++i) {
96
0
        int edge_id = clipped.edge(i);
97
98
        // Looking up an edge requires looking up which chain it's in, which is
99
        // often a binary search.  So let's manually lookup the chain
100
        // information and use that to find the edge, so we only have to do that
101
        // search once.
102
0
        S2Shape::ChainPosition position = shape.chain_position(edge_id);
103
0
        S2Shape::Edge edge =
104
0
            shape.chain_edge(position.chain_id, position.offset);
105
0
        edges_.emplace_back(edge, edge_id, position);
106
0
      }
107
108
      // Note which block of edges belongs to the shape.
109
0
      shape_regions_.emplace_back(std::make_pair(
110
0
          shape_id, Region{shape_start, edges_.size() - shape_start}));
111
0
    }
112
113
    // Save region information for the current dimension.
114
0
    dim_regions_[dim] = {dim_start, edges_.size() - dim_start};
115
0
  }
116
0
}
117
118
absl::Span<const S2IndexCellData::EdgeAndIdChain> S2IndexCellData::shape_edges(
119
0
    int id) const {
120
0
  for (const auto& iter : shape_regions_) {
121
0
    if (iter.first == id) {
122
0
      Region region = iter.second;
123
0
      if (static_cast<size_t>(region.start) < edges_.size()) {
124
0
        return {&edges_[region.start], region.size};
125
0
      }
126
0
      return {};
127
0
    }
128
0
  }
129
0
  return {};
130
0
}
131
132
absl::Span<const S2IndexCellData::EdgeAndIdChain> S2IndexCellData::dim_edges(
133
0
    int dim) const {
134
0
  ABSL_DCHECK(0 <= dim && dim <= 2);
135
0
  const Region& region = dim_regions_[dim];
136
0
  if (static_cast<size_t>(region.start) < edges_.size()) {
137
0
    return {&edges_[region.start], region.size};
138
0
  }
139
0
  return {};
140
0
}
141
142
absl::Span<const S2IndexCellData::EdgeAndIdChain>
143
0
S2IndexCellData::dim_range_edges(int dim0, int dim1) const {
144
0
  ABSL_DCHECK_LE(dim0, dim1);
145
0
  ABSL_DCHECK(0 <= dim0 && dim0 <= 2);
146
0
  ABSL_DCHECK(0 <= dim1 && dim1 <= 2);
147
148
0
  size_t start = dim_regions_[dim0].start;
149
0
  size_t size = 0;
150
0
  for (int dim = dim0; dim <= dim1; ++dim) {
151
0
    start = std::min(start, static_cast<size_t>(dim_regions_[dim].start));
152
0
    size += dim_regions_[dim].size;
153
0
  }
154
155
0
  if (start < edges_.size()) {
156
0
    return {&edges_[start], size};
157
0
  }
158
0
  return {};
159
0
}
160
161
bool S2IndexCellData::ShapeContains(const S2ClippedShape& clipped,
162
                                    const S2Point& point,
163
0
                                    S2VertexModel model) const {
164
0
#ifndef NDEBUG
165
0
  R2Rect bounds = S2PaddedCell(id(), MutableS2ShapeIndex::kCellPadding).bound();
166
167
0
  R2Point uv;
168
0
  S2::ValidFaceXYZtoUV(S2Cell(id()).face(), point, &uv);
169
0
  ABSL_DCHECK(bounds.Contains(uv));
170
0
#endif
171
172
  // Points and polylines don't contain anything except under the CLOSED model.
173
0
  const S2Shape& shape = *index_->shape(clipped.shape_id());
174
0
  if (shape.dimension() < 2) {
175
0
    if (model != S2VertexModel::CLOSED) {
176
0
      return false;
177
0
    }
178
179
    // Point/polyline only contains point if it's a vertex.
180
0
    for (const EdgeAndIdChain& edge : shape_edges(clipped.shape_id())) {
181
0
      if (edge.v0 == point || edge.v1 == point) {
182
0
        return true;
183
0
      }
184
0
    }
185
0
    return false;
186
0
  }
187
188
  // Test containment by drawing a line segment from the cell center to the
189
  // given point and counting edge crossings.
190
0
  S2Point center = this->center();
191
0
  S2EdgeCrosser crosser(&center, &point);
192
193
0
  bool inside = clipped.contains_center();
194
0
  for (const EdgeAndIdChain& edge : shape_edges(clipped.shape_id())) {
195
0
    int sign = crosser.CrossingSign(&edge.v0, &edge.v1);
196
0
    if (sign < 0) continue;
197
0
    if (sign == 0) {
198
      // For the OPEN and CLOSED models, check whether point is a vertex.
199
0
      if (model != S2VertexModel::SEMI_OPEN &&
200
0
          (edge.v0 == point || edge.v1 == point)) {
201
0
        return (model == S2VertexModel::CLOSED);
202
0
      }
203
0
      sign = S2::VertexCrossing(*crosser.a(), *crosser.b(), edge.v0, edge.v1);
204
0
    }
205
0
    inside ^= sign;
206
0
  }
207
0
  return inside;
208
0
}
209
210
}  // namespace internal