Coverage Report

Created: 2025-08-25 06:55

/src/s2geometry/src/s2/s2validation_query.h
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2022 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: smcallis@google.com (Sean McAllister)
17
18
#ifndef S2_S2VALIDATION_QUERY_H_
19
#define S2_S2VALIDATION_QUERY_H_
20
21
#include <algorithm>
22
#include <cmath>
23
#include <cstdint>
24
#include <cstdlib>
25
#include <limits>
26
#include <utility>
27
#include <vector>
28
29
#include "absl/algorithm/container.h"
30
#include "absl/container/flat_hash_set.h"
31
#include "absl/container/inlined_vector.h"
32
#include "absl/log/absl_check.h"
33
#include "absl/strings/str_format.h"
34
#include "absl/types/span.h"
35
#include "s2/_fp_contract_off.h"  // IWYU pragma: keep
36
#include "s2/internal/s2disjoint_set.h"
37
#include "s2/internal/s2incident_edge_tracker.h"
38
#include "s2/internal/s2index_cell_data.h"
39
#include "s2/s2cell_id.h"
40
#include "s2/s2contains_point_query.h"
41
#include "s2/s2contains_vertex_query.h"
42
#include "s2/s2edge_crosser.h"
43
#include "s2/s2error.h"
44
#include "s2/s2point.h"
45
#include "s2/s2pointutil.h"
46
#include "s2/s2predicates.h"
47
#include "s2/s2shape.h"
48
#include "s2/s2shape_index.h"
49
#include "s2/s2shapeutil_edge_wrap.h"
50
51
// This header defines the following queries which each have slightly different
52
// correctness semantics for their particular domain.
53
54
// clang-format off
55
template <typename IndexType> class S2ValidQuery;
56
template <typename IndexType> class S2LegacyValidQuery;
57
// clang-format on
58
59
// Base class for validating geometry contained in an index according to a
60
// configurable model of correctness.  There are several different notions of
61
// "valid" geometry that we have to address, including the basic requirements
62
// for S2BooleanOperation, S2Polygon specific checks, OGC simple geometry
63
// standards, and STLib's requirements.
64
//
65
// Classes of geometry can be thought of as sets of all shapes that have certain
66
// invariants (such as interior-on-left or no-degenerate-edges).  Classes then
67
// naturally build on other classes by adding more rules to further restrict the
68
// allowed shapes.
69
//
70
// This extends-upon structure naturally lends itself to an inheritance based
71
// implementation.  Beginning with the most permissive class of geometry which
72
// just meets the criteria for S2BooleanOperation (S2ValidQuery), we can then
73
// build subsequent classes of geometry by inheriting from and extending the
74
// checks that are performed.
75
//
76
// Validation queries should be templated over the exact index type and can
77
// overload the virtual methods in the subclass API below.
78
//
79
// The Validate() driver function will call these functions in this order:
80
//
81
//   - Start()
82
//     - CheckShape()
83
//     - StartCell()
84
//       - StartShape()
85
//         - CheckEdge()
86
//       - FinishShape()
87
//   - Finish()
88
//
89
// Hoisting the loops into the base class like this allows us to fuse all the
90
// inner loops of the various queries so that we only have to iterate over
91
// the index and its geometry once.
92
//
93
// Several protected member functions are defined to give subclasses access to
94
// the data being validated:
95
//
96
//   const IndexType& Index();
97
//     -- Returns a reference to the current index being operated on.
98
//
99
//   const S2IndexCellData& CurrentCell();
100
//     -- Returns a reference to the decoded data for the current cell.
101
//
102
//   const IncidentEdgeSet& IncidentEdges();
103
//     -- Returns a reference to the current incident edge set.  We promise that
104
//     this set is updated with the current cell's edges before StartCell() is
105
//     called.
106
//
107
// A query then has a `bool Validate(const IndexType& index, S2Error* error)`
108
// method which validates the index and returns true if it's valid, otherwise
109
// false, with the validation failure details provided through the error
110
// parameter.
111
//
112
// This example validates an index as containing valid geometry for
113
// use with S2Polygon/S2Polyline:
114
//
115
//   S2LegacyValidQuery<MutableS2ShapeIndex> query;
116
//   if (!query.Validate(index, error)) {
117
//     ...
118
//   }
119
//
120
template <typename IndexType>
121
class S2ValidationQueryBase {
122
 public:
123
  using IncidentEdgeSet = internal::IncidentEdgeSet;
124
  using EdgeAndIdChain = typename internal::S2IndexCellData::EdgeAndIdChain;
125
  using S2IndexCellData = internal::S2IndexCellData;
126
127
0
  S2ValidationQueryBase() = default;
128
0
  virtual ~S2ValidationQueryBase() = default;
129
130
  // Validate the index by calling the hooks in the derived class.
131
  bool Validate(const IndexType& index, S2Error* error);
132
133
 protected:
134
  using Iterator = typename IndexType::Iterator;
135
136
  // Subclass API
137
  // Starts the validation process; called once per query.
138
0
  virtual bool Start(S2Error*) { return true; }
139
140
  // Validates each individual shape in the index; called once per shape.
141
  //
142
  // A reference to the current iterator state is passed in.  The function may
143
  // reposition the iterator in order to do shape checking.
144
  virtual bool CheckShape(Iterator& iter, const S2Shape& shape, int shape_id,
145
0
                          S2Error*) {
146
0
    return true;
147
0
  }
148
149
  // Starts processing of a cell in the index; called once per cell.
150
0
  virtual bool StartCell(S2Error*) { return true; }
151
152
  // Marks start of a clipped shape; called once per clipped shape in a cell.
153
0
  virtual bool StartShape(const S2Shape&, const S2ClippedShape&, S2Error*) {
154
0
    return true;
155
0
  }
156
157
  // Validates a single edge of a given shape; called at least once per edge of
158
  // each shape.
159
  virtual bool CheckEdge(const S2Shape&, const S2ClippedShape&,
160
0
                         const EdgeAndIdChain&, S2Error*) {
161
0
    return true;
162
0
  }
163
164
  // Marks end of a clipped shape; called once per clipped shape in a cell.
165
0
  virtual bool FinishShape(const S2Shape&, const S2ClippedShape&, S2Error*) {
166
0
    return true;
167
0
  }
168
169
  // Marks end of validation; called once per query.
170
0
  virtual bool Finish(S2Error* error) { return true; }
171
172
  // Returns a reference to the index we're validating.
173
0
  const IndexType& Index() const {
174
0
    ABSL_DCHECK(index_ != nullptr);
175
0
    return *index_;
176
0
  }
177
178
  // Returns current incident edge map.
179
0
  const IncidentEdgeSet& IncidentEdges() const {
180
0
    return incident_edge_tracker_.IncidentEdges();
181
0
  }
182
183
  // Returns a reference to the data for the current cell.
184
0
  const S2IndexCellData& CurrentCell() const { return cell_buffer_; }
185
186
 private:
187
  // Disallow copying and assignment through an abstract reference.
188
  S2ValidationQueryBase(const S2ValidationQueryBase&) = delete;
189
  S2ValidationQueryBase& operator=(const S2ValidationQueryBase&) = delete;
190
191
  // Sets the current index we're operating on, accessible through Index();
192
0
  void SetIndex(const IndexType* index) { index_ = index; }
193
194
  // Sets the current cell that we're operating on and loads its data.
195
0
  void SetCurrentCell(const Iterator& iter) {
196
0
    cell_buffer_.LoadCell(index_, iter.id(), &iter.cell());
197
0
  }
198
199
  S2IndexCellData cell_buffer_;
200
  internal::S2IncidentEdgeTracker incident_edge_tracker_;
201
  const IndexType* index_ = nullptr;
202
};
203
204
// This class represents the least strict class of geometry and corresponds to
205
// the requirements for compatibility with S2BooleanOperation, with these
206
// specific constraints:
207
//
208
// == General ==
209
//   * Points must be unit magnitude (according to S2::IsUnitLength()).
210
//   * Points must be finite (neither inf or nan).
211
//   * Edges must not have antipodal vertices.
212
//   * Degenerate edges of the form {A,A} are allowed by default.
213
//   * Reverse-duplicate edges of the form {A,B},{B,A} are allowed by default.
214
//   * Shape chains must have at least one edge, excluding the full and empty
215
//     polygon shapes, which may have at most one empty chain.
216
//
217
// == Polygons ==
218
//   * Polygon interiors must be disjoint from all other geometry.
219
//     * Polygon edges thus cannot cross any other edges, including at vertices.
220
//     * No geometry may test as contained in another polygon.
221
//   * Polygons can never have duplicate edges even among different polygons.
222
//   * Polygon edges must be connected and form a closed loop per chain.
223
//   * Polygon chains must oriented so that the interior is always on the left.
224
//
225
// == Polylines ==
226
//   * Polyline edges can cross by default.
227
//   * Polylines can have duplicate edges by default.
228
//
229
template <typename IndexType>
230
class S2ValidQuery : public S2ValidationQueryBase<IndexType> {
231
 protected:
232
  // Protected because options are only for subclasses to configure behavior.
233
  class Options {
234
   public:
235
    // Types of single-vertex touches allowed between shapes.  This is
236
    // configurable for each combination of dimension.  E.g. we can configure
237
    // polyline-polyline (1D-1D) and polyline-polygon (1D-2D) touches
238
    // separately.
239
    enum TouchType {
240
      kTouchNone = 0b00,      // Allow no touches between shapes.
241
      kTouchInterior = 0b01,  // Interior point may touch the other shape.
242
      kTouchBoundary = 0b10,  // Boundary point may touch the other shape.
243
      kTouchAny = 0b11        // Allow any touches between shapes.
244
    };
245
246
0
    Options() {
247
      // Default is to allow any touches between geometry.
248
0
      for (int i = 0; i < 3; ++i) {
249
0
        for (int j = 0; j < 3; ++j) {
250
0
          set_allowed_touches(i, j, std::make_pair(kTouchAny, kTouchAny));
251
0
        }
252
0
      }
253
0
    }
254
255
    // Returns a TouchType with fields set indicating what types of vertex from
256
    // dimension A are allowed to touch vertices from dimension B.
257
0
    std::pair<TouchType, TouchType> allowed_touches(int dima, int dimb) const {
258
0
      ABSL_DCHECK_GE(dima, 0);
259
0
      ABSL_DCHECK_LE(dima, 2);
260
0
      ABSL_DCHECK_GE(dimb, 0);
261
0
      ABSL_DCHECK_LE(dimb, 2);
262
263
      // Just get the top half of the matrix.
264
0
      if (dima > dimb) {
265
0
        std::swap(dima, dimb);
266
0
      }
267
268
0
      return allowed_touches_[dima][dimb];
269
0
    }
270
271
    // Set the allowed touches based on geometry dimension.  We require that the
272
    // matrix of combinations be symmetric, so set_allowed_touches(1, 2, X, Y)
273
    // and set_allowed_touches(2, 1, Y, X) are equivalent.
274
    Options& set_allowed_touches(  //
275
0
        int dima, int dimb, std::pair<TouchType, TouchType> types) {
276
0
      ABSL_DCHECK(0 <= dima && dima <= 2);
277
0
      ABSL_DCHECK(0 <= dimb && dimb <= 2);
278
279
      // Just set the top half of the matrix.
280
0
      if (dima > dimb) {
281
0
        std::swap(dima, dimb);
282
0
      }
283
284
0
      allowed_touches_[dima][dimb] = types;
285
0
      return *this;
286
0
    }
287
288
    // Configures whether polylines can have duplicate edges.
289
0
    bool allow_duplicate_polyline_edges() const {
290
0
      return allow_duplicate_polyline_edges_;
291
0
    }
292
293
    Options& set_allow_duplicate_polyline_edges(bool flag) {
294
      allow_duplicate_polyline_edges_ = flag;
295
      return *this;
296
    }
297
298
    // Configures whether polyline edges can cross.
299
0
    bool allow_polyline_interior_crossings() const {
300
0
      return allow_polyline_interior_crossings_;
301
0
    }
302
303
    Options& set_allow_polyline_interior_crossings(bool flag) {
304
      allow_polyline_interior_crossings_ = flag;
305
      return *this;
306
    }
307
308
    // Configures whether reverse duplicate edges are allowed.
309
0
    bool allow_reverse_duplicates() const { return allow_reverse_duplicates_; }
310
311
0
    Options& set_allow_reverse_duplicates(bool flag) {
312
0
      allow_reverse_duplicates_ = flag;
313
0
      return *this;
314
0
    }
315
316
    // Configures whether degenerate (zero-length) edges are allowed.
317
0
    bool allow_degenerate_edges() const { return allow_degenerate_edges_; }
318
319
0
    Options& set_allow_degenerate_edges(bool flag) {
320
0
      allow_degenerate_edges_ = flag;
321
0
      return *this;
322
0
    }
323
324
   private:
325
    bool allow_degenerate_edges_ = true;
326
    bool allow_duplicate_polyline_edges_ = true;
327
    bool allow_reverse_duplicates_ = true;
328
    bool allow_polyline_interior_crossings_ = true;
329
330
    std::pair<TouchType, TouchType> allowed_touches_[3][3];
331
  };
332
333
0
  const Options& options() const { return options_; }
334
0
  Options& mutable_options() { return options_; }
335
336
  // We have to include unqualified names manually due to template inheritance.
337
  using Base = S2ValidationQueryBase<IndexType>;
338
  using Base::CurrentCell;
339
  using Base::IncidentEdges;
340
  using Base::Index;
341
  using typename Base::EdgeAndIdChain;
342
  using typename Base::Iterator;
343
  using typename Base::S2IndexCellData;
344
345
  bool CheckShape(Iterator& iter, const S2Shape& shape, int shape_id,
346
                  S2Error*) override;
347
  bool StartCell(S2Error*) override;
348
  bool CheckEdge(const S2Shape& shape, const S2ClippedShape& clipped,
349
                 const EdgeAndIdChain& edge, S2Error*) override;
350
  bool Finish(S2Error* error) override;
351
352
 private:
353
  // Returns true if the given S2Point is valid, meaning none of its components
354
  // are inf or NaN.
355
0
  static inline bool ValidPoint(const S2Point& p) {
356
0
    return std::isfinite(p.x()) && std::isfinite(p.y()) && std::isfinite(p.z());
357
0
  }
358
359
  // Checks that a point is fully outside any polygons.
360
  //
361
  // Returns false if a point is not interior to any polygons, true otherwise
362
  // with error populated.
363
  bool PointContained(S2CellId, int shape_id, const S2Point&, S2Error*);
364
365
  // Checks if any edge in the current cell is a duplicate (or reverse
366
  // duplicate).
367
  bool CheckForDuplicateEdges(S2Error* error) const;
368
369
  // Checks if any edges in the current cell have an interior crossing.
370
  bool CheckForInteriorCrossings(S2Error* error) const;
371
372
  // Checks that a given chain of a polygon is oriented properly relative to one
373
  // cell center in the index.  We only need to check one center because we
374
  // assume that the transition between cell indices is consistent, thus we just
375
  // need to make sure that the interior state isn't flipped.
376
  //
377
  // Returns false when the chain isn't properly oriented with S2Error set with
378
  // the details, true otherwise.
379
  //
380
  // REQUIRES: Shape must be two dimensional.
381
  // REQUIRES: Chain must be closed.
382
  // REQUIRES: Chain edges must be connected (no gaps).
383
  // REQUIRES: Chain must have at least three distinct points (cover some area).
384
  bool CheckChainOrientation(  //
385
      Iterator&, const S2Shape&, int shape_id, int chain_id, S2Error*);
386
387
  // Information on one vertex of an edge, including whether it's on the
388
  // boundary of its shape.  This is used by CheckTouchesAreValid() when we need
389
  // to check that vertex touches are valid.
390
  struct TestVertex {
391
    S2Point vertex;
392
    int32_t edge_id;
393
    int32_t shape_id;
394
    int32_t dim;
395
    bool on_boundary;
396
  };
397
  std::vector<TestVertex> test_vertices_;
398
399
  // Checks the shapes in the current cell to ensure that any touch points are
400
  // allowed under the configured semantics.
401
  //
402
  // Returns false if any vertices touch at an invalid point with the error
403
  // value set, true otherwise.
404
  bool CheckTouchesAreValid(S2Error*);
405
406
  Options options_;
407
};
408
409
//////////////////   Utility Implementation   ////////////////////
410
411
// Sorts a container of S2Shape::Edges in counter-clockwise order around an
412
// origin point.  By itself, ordering this way is ambiguous in terms of the
413
// starting point, so we take an edge to form a reference point to form a total
414
// ordering.
415
//
416
// Reverse duplicate edges are ordered so that the one with origin as v0 comes
417
// before the other.
418
//
419
// The edges and first edge must all contain origin as one of their vertices.
420
//
421
// Can sort any container with elements that are convertible to an S2Shape::Edge
422
// reference.  Both the origin point and first edge are taken by value so that
423
// it's safe to call using elements of the container for those values.
424
//
425
// Takes a Container to the start and end of the container.  std::sort requires
426
// random iterators and thus so do we.
427
template <typename Container>
428
0
void SortEdgesCcw(S2Point origin, S2Shape::Edge first, Container& data) {
429
0
  ABSL_DCHECK(first.v0 == origin || first.v1 == origin);
430
0
  const S2Point& first_vertex = (first.v0 == origin) ? first.v1 : first.v0;
431
0
  ABSL_DCHECK(first_vertex != origin);
432
433
0
  absl::c_sort(  //
434
0
      data,      //
435
0
      [&](const S2Shape::Edge& a, const S2Shape::Edge& b) {
436
0
        ABSL_DCHECK(a.v0 == origin || a.v1 == origin);
437
0
        ABSL_DCHECK(b.v0 == origin || b.v1 == origin);
438
439
        // `OrderedCCW` will return `true` if `a == b`, which will violate the
440
        // irreflexivity requirement of a strict weak ordering.
441
0
        if (a == b) {
442
0
          return false;
443
0
        }
444
445
        // Order reverse duplicates so that the one with edge.v0 ==
446
        // origin is first.
447
0
        if (a == b.Reversed()) {
448
0
          return a.v0 == origin;
449
0
        }
450
451
0
        if (a == first || b == first) {
452
          // If either edge is the first edge, compare so that the
453
          // first edge always comes first in the sorted output.
454
0
          return a == first;
455
0
        }
456
457
        // Otherwise check orientation of vertices.
458
0
        S2Point apnt = (a.v0 == origin) ? a.v1 : a.v0;
459
0
        S2Point bpnt = (b.v0 == origin) ? b.v1 : b.v0;
460
0
        return s2pred::OrderedCCW(first_vertex, apnt, bpnt, origin);
461
0
      });
462
0
}
463
464
template <typename IndexType>
465
bool S2ValidationQueryBase<IndexType>::Validate(const IndexType& index,
466
0
                                                S2Error* error) {
467
0
  SetIndex(&index);
468
0
  incident_edge_tracker_.Reset();
469
0
  cell_buffer_.Reset();
470
471
0
  if (!Start(error)) {
472
0
    return false;
473
0
  }
474
475
  // Run basic checks on individual shapes in the index.
476
0
  Iterator iter(&index, S2ShapeIndex::BEGIN);
477
0
  for (int shape_id = 0; shape_id < index.num_shape_ids(); ++shape_id) {
478
0
    const S2Shape* shape = index.shape(shape_id);
479
0
    if (shape != nullptr) {
480
0
      if (!CheckShape(iter, *shape, shape_id, error)) {
481
0
        return false;
482
0
      }
483
0
    }
484
0
  }
485
486
0
  for (iter.Begin(); !iter.done(); iter.Next()) {
487
0
    SetCurrentCell(iter);
488
489
    // Add two dimensional shape edges to the incident edge tracker to support
490
    // checks for things like crossing polygon chains and split interiors.
491
0
    for (const S2ClippedShape& clipped : CurrentCell().clipped_shapes()) {
492
0
      const int shape_id = clipped.shape_id();
493
0
      const S2Shape& shape = CurrentCell().shape(clipped);
494
0
      if (shape.dimension() < 2) {
495
0
        continue;
496
0
      }
497
498
0
      incident_edge_tracker_.StartShape(shape_id);
499
0
      for (const auto& edge : CurrentCell().shape_edges(shape_id)) {
500
0
        incident_edge_tracker_.AddEdge(edge.id, edge);
501
0
      }
502
0
      incident_edge_tracker_.FinishShape();
503
0
    }
504
505
    // Now notify that we're starting this cell.
506
0
    if (!StartCell(error)) {
507
0
      return false;
508
0
    }
509
510
    // Iterate the shapes and edges of the cell.
511
0
    for (const S2ClippedShape& clipped : CurrentCell().clipped_shapes()) {
512
0
      const int shape_id = clipped.shape_id();
513
0
      const S2Shape& shape = CurrentCell().shape(clipped);
514
0
      if (!StartShape(shape, clipped, error)) {
515
0
        return false;
516
0
      }
517
518
0
      for (const auto& edge : CurrentCell().shape_edges(shape_id)) {
519
0
        if (!CheckEdge(shape, clipped, edge, error)) {
520
0
          return false;
521
0
        }
522
0
      }
523
524
0
      if (!FinishShape(shape, clipped, error)) {
525
0
        return false;
526
0
      }
527
0
    }
528
0
  }
529
530
  // Run any final checks and finish validation
531
0
  return Finish(error);
532
0
}
533
534
// This class represents a semantic class of geometry that's compatible with the
535
// requirements of the S2Polygon and S2Polyline IsValid() methods.  It extends
536
// the S2ValidQuery requirements, specifically:
537
//
538
// == General ==
539
//   * Degenerate edges of the form {A,A} are not allowed.
540
//   * All the shapes in an S2ShapeIndex must be the same dimensionality.
541
//   * Duplicate vertices in a chain are not allowed.
542
//     I.e. a chain cannot touch itself even at one point.
543
//   * Different chains may touch, but only in such a way they don't create
544
//     duplicate edges.
545
//
546
template <typename IndexType>
547
class S2LegacyValidQuery final : public S2ValidQuery<IndexType> {
548
 public:
549
  S2LegacyValidQuery();
550
551
 protected:
552
  // We have to include unqualified names manually due to template inheritance.
553
  using Base = S2ValidQuery<IndexType>;
554
  using Base::CurrentCell;
555
  using Base::IncidentEdges;
556
  using Base::Index;
557
  using typename Base::EdgeAndIdChain;
558
  using typename Base::Iterator;
559
  using typename Base::S2IndexCellData;
560
561
  bool Start(S2Error*) final;
562
  bool CheckShape(Iterator&, const S2Shape& shape, int shape_id,
563
                  S2Error*) final;
564
  bool StartCell(S2Error*) final;
565
  bool CheckEdge(const S2Shape& shape, const S2ClippedShape& clipped,
566
                 const EdgeAndIdChain& edge, S2Error*) override;
567
568
 private:
569
  // Tuple of (shape, chain, vertex) for detecting duplicate vertices in the
570
  // same chain.
571
  struct ShapeChainVertex {
572
    // Constructor just to twiddle order of fields.
573
    ShapeChainVertex(int shape, int chain, S2Point vertex)
574
        : vertex(vertex), chain(chain), shape(shape) {}
575
576
    S2Point vertex;
577
    int chain;
578
    int shape;
579
580
    // Makes type hashable for use in Abseil containers.
581
    template <typename H>
582
    friend H AbslHashValue(H h, const ShapeChainVertex& a) {
583
      return H::combine(std::move(h), a.vertex, a.shape, a.chain);
584
    }
585
  };
586
};
587
588
//////////////////   S2ValidQuery Implementation   ////////////////////
589
590
template <typename IndexType>
591
bool S2ValidQuery<IndexType>::CheckShape(Iterator& iter, const S2Shape& shape,
592
0
                                         int shape_id, S2Error* error) {
593
  // Verify that shape isn't outright lying to us about its dimension.
594
0
  const int dim = shape.dimension();
595
0
  if (dim < 0 || dim > 2) {
596
0
    *error = S2Error(
597
0
        S2Error::INVALID_DIMENSION,
598
0
        absl::StrFormat("Shape %d has invalid dimension: %d", shape_id, dim));
599
0
    return false;
600
0
  }
601
602
0
  absl::InlinedVector<int, 4> chains_to_check;
603
0
  for (int chain_id = 0; chain_id < shape.num_chains(); ++chain_id) {
604
0
    const S2Shape::Chain& chain = shape.chain(chain_id);
605
606
    // Check that the first and last edges in a polygon chain connect to close
607
    // the chain.  This is true even for degenerate chains with one edge that's
608
    // a point, or two edges that are reverse duplicates.  Both are considered
609
    // closed.
610
0
    if (dim == 2 && chain.length > 0) {
611
0
      int edge_id = chain.start;
612
0
      int prev_id = s2shapeutil::PrevEdgeWrap(shape, edge_id);
613
614
0
      if (shape.edge(prev_id).v1 != shape.edge(edge_id).v0) {
615
0
        *error = S2Error(S2Error::LOOP_NOT_ENOUGH_VERTICES,
616
0
                         absl::StrFormat("Chain %d of shape %d isn't closed",
617
0
                                         chain_id, shape_id));
618
0
        return false;
619
0
      }
620
0
    }
621
622
0
    for (int offset = 0; offset < chain.length; ++offset) {
623
0
      const S2Shape::Edge& edge = shape.chain_edge(chain_id, offset);
624
625
      // Check that coordinates aren't inf/nan.
626
0
      if (!ValidPoint(edge.v0) || !ValidPoint(edge.v1)) {
627
0
        *error = S2Error(
628
0
            S2Error::INVALID_VERTEX,
629
0
            absl::StrFormat("Shape %d has invalid coordinates", shape_id));
630
0
        return false;
631
0
      }
632
633
      // Check that vertices are unit length.
634
0
      if (!S2::IsUnitLength(edge.v0) || !S2::IsUnitLength(edge.v1)) {
635
0
        *error = S2Error(
636
0
            S2Error::NOT_UNIT_LENGTH,
637
0
            absl::StrFormat("Shape %d has non-unit length vertices", shape_id));
638
0
        return false;
639
0
      }
640
641
      // (Optional) check polyline and polygon edges for degeneracy.
642
0
      if (dim > 0 && !options().allow_degenerate_edges()) {
643
0
        if (edge.IsDegenerate()) {
644
0
          *error = S2Error(
645
0
              S2Error::DUPLICATE_VERTICES,
646
0
              absl::StrFormat("Shape %d: chain %d, edge %d is degenerate",
647
0
                              shape_id, chain_id, chain.start + offset));
648
0
          return false;
649
0
        }
650
0
      }
651
652
      // Check that edge doesn't have antipodal vertices.
653
0
      if (edge.v0 == -edge.v1) {
654
0
        *error =
655
0
            S2Error(S2Error::ANTIPODAL_VERTICES,
656
0
                    absl::StrFormat("Shape %d has adjacent antipodal vertices",
657
0
                                    shape_id));
658
0
        return false;
659
0
      }
660
661
      // Check that chain edges are connected for polylines and polygons.
662
0
      if (dim > 0 && chain.length >= 2 && offset > 0) {
663
0
        const S2Shape::Edge last = shape.chain_edge(chain_id, offset - 1);
664
0
        if (last.v1 != edge.v0) {
665
0
          *error =
666
0
              S2Error(S2Error::NOT_CONTINUOUS,
667
0
                      absl::StrFormat("Chain %d of shape %d has neighboring "
668
0
                                      "edges that don't connect.",
669
0
                                      chain_id, shape_id));
670
0
          return false;
671
0
        }
672
0
      }
673
0
    }
674
675
    // The rest of the checks are for non-empty polygon chains only.
676
0
    if (dim != 2 || chain.length == 0) {
677
0
      continue;
678
0
    }
679
680
    // We need at least two distinct points in a chain before we can check its
681
    // orientation vs the cell center.  Scan until we find a vertex different
682
    // than the first.
683
0
    int unique_count = 1;
684
0
    const S2Point first = shape.chain_edge(chain_id, 0).v0;
685
0
    for (const S2Point& vertex : shape.vertices(chain_id)) {
686
0
      if (vertex != first) {
687
0
        ++unique_count;
688
0
        break;
689
0
      }
690
0
    }
691
692
    // Only a single unique point. A degenerate edge will never test as a vertex
693
    // crossing (because 3 out of 4 vertices to S2::VertexCrossing() would be
694
    // equal making it false), so they can't toggle interior state and we can
695
    // ignore them.
696
0
    if (unique_count == 1) {
697
0
      continue;
698
0
    }
699
700
0
    chains_to_check.emplace_back(chain_id);
701
0
  }
702
703
  // Check the selected chain to verify chain orientation.
704
0
  for (int chain_id : chains_to_check) {
705
0
    if (!CheckChainOrientation(iter, shape, shape_id, chain_id, error)) {
706
0
      return false;
707
0
    }
708
0
  }
709
0
  return true;
710
0
}
711
712
template <typename IndexType>
713
0
bool S2ValidQuery<IndexType>::CheckForDuplicateEdges(S2Error* error) const {
714
0
  int dim0 = options().allow_duplicate_polyline_edges() ? 2 : 1;
715
0
  int dim1 = 2;
716
717
  // This is O(N^2) but cells don't have many edges in them and benchmarks show
718
  // this to be faster than trying to sort the whole array ahead of time.
719
0
  absl::Span<const EdgeAndIdChain> edges =
720
0
      CurrentCell().dim_range_edges(dim0, dim1);
721
0
  int num_edges = edges.size();
722
0
  for (int i = 0; i < num_edges; ++i) {
723
0
    for (int j = i + 1; j < num_edges; ++j) {
724
0
      bool duplicate = (edges[i] == edges[j]);
725
726
0
      if (!options().allow_reverse_duplicates()) {
727
0
        duplicate |= (edges[i].Reversed() == edges[j]);
728
0
      }
729
730
0
      if (duplicate) {
731
0
        *error = S2Error(S2Error::OVERLAPPING_GEOMETRY,
732
0
                         "One or more duplicate polygon edges detected");
733
0
        return false;
734
0
      }
735
0
    }
736
0
  }
737
738
0
  return true;
739
0
}
740
741
template <typename IndexType>
742
0
bool S2ValidQuery<IndexType>::CheckForInteriorCrossings(S2Error* error) const {
743
  // Get all the polyline and polygon edges.
744
0
  absl::Span<const EdgeAndIdChain> edges = CurrentCell().dim_range_edges(1, 2);
745
746
  // If we're allowing polyline edges to cross polyline edges, then we only have
747
  // to check against polygon edges.
748
0
  size_t check_start = 0;
749
0
  if (options().allow_polyline_interior_crossings()) {
750
0
    check_start = CurrentCell().dim_edges(1).size();
751
0
  }
752
753
  // This can happen when we're allowing polyline crossings and only have
754
  // polylines, there's nothing to do.
755
0
  if (check_start >= edges.size()) {
756
0
    return true;
757
0
  }
758
759
0
  const size_t num_edges = edges.size();
760
0
  for (size_t i = 0; i + 1 < num_edges; ++i) {
761
    // We never have to check against edges at a lower index, because if we
762
    // intersect them, we'll have already checked that at this point.
763
0
    size_t j = std::max(check_start, i + 1);
764
765
    // We can skip adjacent edges.
766
0
    if (edges[i].v1 == edges[j].v0) {
767
0
      if (++j >= num_edges) {
768
0
        break;
769
0
      }
770
0
    }
771
772
0
    S2EdgeCrosser crosser(&edges[i].v0, &edges[i].v1);
773
0
    for (; j < num_edges; ++j) {
774
0
      if (crosser.c() == nullptr || *crosser.c() != edges[j].v0) {
775
0
        crosser.RestartAt(&edges[j].v0);
776
0
      }
777
778
0
      if (crosser.CrossingSign(&edges[j].v1) > 0) {
779
0
        *error =
780
0
            S2Error(S2Error::OVERLAPPING_GEOMETRY,
781
0
                    absl::StrFormat("Chain %d edge %d crosses chain %d edge %d",
782
0
                                    edges[i].chain, edges[i].offset,
783
0
                                    edges[j].chain, edges[j].offset));
784
0
        return false;
785
0
      }
786
0
    }
787
0
  }
788
0
  return true;
789
0
}
790
791
// Returns true if a vertex (0 or 1) of an edge of a polyline is a boundary
792
// point or not.  This is only true if the vertex is either the start or end
793
// point of a chain and the chain is open.  Returns false otherwise.
794
//
795
// REQUIRES: vertex == 0 or vertex == 1
796
inline bool PolylineVertexIsBoundaryPoint(
797
    const S2Shape& shape, const internal::S2IndexCellData::EdgeAndIdChain& edge,
798
0
    int vertex) {
799
0
  ABSL_DCHECK(vertex == 0 || vertex == 1);
800
801
0
  if (edge.offset == 0 && vertex == 0) {
802
    // First vertex of first edge
803
0
    return s2shapeutil::PrevEdgeWrap(shape, edge.id) == -1;
804
0
  } else if (edge.offset == shape.chain(edge.chain).length - 1 && vertex == 1) {
805
    // Last vertex of last edge
806
0
    return s2shapeutil::NextEdgeWrap(shape, edge.id) == -1;
807
0
  }
808
809
0
  return false;
810
0
}
811
812
template <typename IndexType>
813
0
bool S2ValidQuery<IndexType>::CheckTouchesAreValid(S2Error* error) {
814
0
  using TouchType = typename Options::TouchType;
815
816
0
  const auto kAny = Options::kTouchAny;
817
818
0
  bool need_dim[3] = {true, true, true};
819
0
  for (int i = 0; i < 3; ++i) {
820
0
    for (int j = 0; j < 3; ++j) {
821
0
      bool any_allowed =
822
0
          (options().allowed_touches(i, j) == std::make_pair(kAny, kAny));
823
0
      need_dim[i] &= !any_allowed;
824
0
    }
825
0
  }
826
827
  // If all touches are allowed, then we don't have to check, easy.
828
0
  if (!need_dim[0] && !need_dim[1] && !need_dim[2]) {
829
0
    return true;
830
0
  }
831
832
  // Gather unique vertices from each dimension that needs to be checked.
833
0
  test_vertices_.clear();
834
0
  for (const S2ClippedShape& clipped : CurrentCell().clipped_shapes()) {
835
0
    const int shape_id = clipped.shape_id();
836
0
    const S2Shape& shape = *Index().shape(shape_id);
837
0
    const int dim = shape.dimension();
838
839
    // Skip if we're not checking this dimension.
840
0
    if (!need_dim[dim]) {
841
0
      continue;
842
0
    }
843
844
0
    for (const EdgeAndIdChain& edge : CurrentCell().shape_edges(shape_id)) {
845
      // For polylines, we have to handle the start and ending edges specially,
846
      // since we can have open chains that have boundary points.
847
0
      if (dim == 1) {
848
0
        bool on_boundary = PolylineVertexIsBoundaryPoint(shape, edge, 0);
849
0
        test_vertices_.push_back(
850
0
            {edge.v0, edge.id, shape_id, dim, on_boundary});
851
852
        // Check vertex 1 too only if it's a boundary point, otherwise we would
853
        // test v1 twice after we grab v0 of the next edge.
854
0
        on_boundary = PolylineVertexIsBoundaryPoint(shape, edge, 1);
855
0
        if (on_boundary) {
856
0
          test_vertices_.push_back({edge.v1, edge.id, shape_id, dim, true});
857
0
        }
858
0
      } else {
859
        // All polygon vertices are on the boundary, all point vertices are not.
860
0
        test_vertices_.push_back({edge.v0, edge.id, shape_id, dim, dim == 2});
861
0
      }
862
0
    }
863
0
  }
864
865
  // For each test vertex, scan over the other shapes and verify that any vertex
866
  // touches are allowed under the current semantics.
867
0
  for (const TestVertex& testpnt : test_vertices_) {
868
0
    for (const S2ClippedShape& clipped : CurrentCell().clipped_shapes()) {
869
0
      const int shape_id = clipped.shape_id();
870
0
      const S2Shape& shape = *Index().shape(shape_id);
871
0
      const int dim = shape.dimension();
872
873
0
      for (const EdgeAndIdChain& edge : CurrentCell().shape_edges(shape_id)) {
874
        // Don't compare an edge against itself.
875
0
        if (testpnt.shape_id == shape_id && testpnt.edge_id == edge.id) {
876
0
          continue;
877
0
        }
878
879
        // Figure out which (if any) vertex of this edge we touched.
880
0
        int vertidx = -1;
881
0
        if (testpnt.vertex == edge.v0) vertidx = 0;
882
0
        if (testpnt.vertex == edge.v1) vertidx = 1;
883
0
        if (vertidx < 0) {
884
0
          continue;
885
0
        }
886
887
        // Closed polylines are always allowed, so if we get a hit on the same
888
        // shape and it's a polyline, check that it's not from the closure.
889
0
        if (testpnt.shape_id == shape_id && dim == 1) {
890
0
          if (vertidx == 0 &&
891
0
              s2shapeutil::PrevEdgeWrap(shape, edge.id) == testpnt.edge_id) {
892
0
            continue;
893
0
          }
894
895
0
          if (vertidx == 1 &&
896
0
              s2shapeutil::NextEdgeWrap(shape, edge.id) == testpnt.edge_id) {
897
0
            continue;
898
0
          }
899
0
        }
900
901
        // Points vertices are always interior, Polygon vertices are always
902
        // boundary, and it may be either for polylines.
903
0
        bool on_boundary = (dim == 2);
904
0
        if (dim == 1) {
905
0
          on_boundary = PolylineVertexIsBoundaryPoint(shape, edge, vertidx);
906
0
        }
907
908
0
        TouchType typea = Options::kTouchInterior;
909
0
        if (testpnt.on_boundary) {
910
0
          typea = Options::kTouchBoundary;
911
0
        }
912
913
0
        TouchType typeb = Options::kTouchInterior;
914
0
        if (on_boundary) {
915
0
          typeb = Options::kTouchBoundary;
916
0
        }
917
918
0
        using TouchPair = std::pair<TouchType, TouchType>;
919
0
        TouchPair allowed = options().allowed_touches(testpnt.dim, dim);
920
921
        // Returns true if both touches match the given allowed touches.
922
0
        const auto PermittedTouches = [](TouchPair allowed, TouchType typea,
923
0
                                         TouchType typeb) {
924
0
          return (allowed.first & typea) && (allowed.second & typeb);
925
0
        };
926
927
        // We require that touches be symmetric, so that the touch is invalid
928
        // only if it's invalid from A->B and B->A.  This lets us support
929
        // behavior such as requiring that one or the other point be an interior
930
        // point.
931
0
        if (!PermittedTouches(allowed, typea, typeb) &&
932
0
            !PermittedTouches(allowed, typeb, typea)) {
933
0
          *error = S2Error(S2Error::OVERLAPPING_GEOMETRY,
934
0
                           "Index has geometry with invalid vertex touches.");
935
0
          return false;
936
0
        }
937
0
      }
938
0
    }
939
0
  }
940
0
  return true;
941
0
}
942
943
template <typename IndexType>
944
0
bool S2ValidQuery<IndexType>::StartCell(S2Error* error) {
945
0
  if (!CheckForDuplicateEdges(error) || !CheckForInteriorCrossings(error)) {
946
0
    return false;
947
0
  }
948
949
0
  if (!CheckTouchesAreValid(error)) {
950
0
    return false;
951
0
  }
952
953
0
  return true;
954
0
}
955
956
template <typename IndexType>
957
bool S2ValidQuery<IndexType>::PointContained(S2CellId cell_id, int shape_id,
958
                                             const S2Point& point,
959
0
                                             S2Error* error) {
960
0
  const S2IndexCellData& cell = CurrentCell();
961
0
  for (const S2ClippedShape& clipped : cell.clipped_shapes()) {
962
0
    if (clipped.shape_id() == shape_id) {
963
0
      continue;
964
0
    }
965
966
0
    const S2Shape& shape = *Index().shape(clipped.shape_id());
967
968
    // Only check for containment in polygons.
969
0
    if (shape.dimension() != 2) {
970
0
      continue;
971
0
    }
972
973
0
    if (cell.ShapeContains(clipped, point)) {
974
0
      *error = S2Error(
975
0
          S2Error::OVERLAPPING_GEOMETRY,
976
0
          absl::StrFormat(
977
0
              "Shape %d has one or more edges contained in another shape.",
978
0
              shape_id));
979
0
      return true;
980
0
    }
981
0
  }
982
983
0
  return false;
984
0
}
985
986
template <typename IndexType>
987
bool S2ValidQuery<IndexType>::CheckChainOrientation(Iterator& iter,
988
                                                    const S2Shape& shape,
989
                                                    int shape_id, int chain_id,
990
0
                                                    S2Error* error) {
991
0
  const S2Shape::Chain& chain = shape.chain(chain_id);
992
993
  // Given that:
994
  //  1. Edges in the chain are connected continuously.
995
  //  2. The chain is closed.
996
  //  3. The chain at least two distinct points.
997
  //
998
  // Then we can test whether the chain is oriented properly relative to the
999
  // cell center by testing one edge of the chain for proper orientation.
1000
1001
0
  S2ContainsVertexQuery query;
1002
0
  for (int offset = 0; offset < chain.length; ++offset) {
1003
0
    const S2Point& vertex = shape.chain_edge(chain_id, offset).v0;
1004
0
    query.Init(vertex);
1005
1006
    // Seek to the cell containing vertex and get the clipped shape.
1007
0
    if (!iter.Locate(vertex)) {
1008
0
      *error = S2Error::DataLoss("Shape vertex was not indexed");
1009
0
      return false;
1010
0
    }
1011
0
    const S2Point& center = iter.id().ToPoint();
1012
1013
0
    const S2ClippedShape* clipped = iter.cell().find_clipped(shape_id);
1014
0
    ABSL_DCHECK_NE(clipped, nullptr);
1015
1016
    // Compute winding number and vertex sign at the same time.
1017
0
    int winding = clipped->contains_center();
1018
0
    S2CopyingEdgeCrosser crosser(center, vertex);
1019
1020
0
    for (int i = 0; i < clipped->num_edges(); ++i) {
1021
0
      const S2Shape::Edge& edge = shape.edge(clipped->edge(i));
1022
1023
      // Tally up the total change in winding number from center to vertex.
1024
0
      winding += crosser.SignedEdgeOrVertexCrossing(edge.v0, edge.v1);
1025
1026
      // Include any edges incident on vertex in the contains vertex query.
1027
0
      if (edge.IncidentOn(vertex)) {
1028
0
        if (vertex == edge.v0) {
1029
0
          query.AddEdge(edge.v1, +1);
1030
0
        } else {
1031
0
          query.AddEdge(edge.v0, -1);
1032
0
        }
1033
0
      }
1034
0
    }
1035
1036
0
    bool duplicates = query.DuplicateEdges();
1037
0
    int sign = 0;
1038
1039
    // If we have a sign of zero on the vertex, all the edges incident on it
1040
    // were reverse duplicates and we can't use it to test orientation, continue
1041
    // trying to find another vertex.
1042
0
    if (!duplicates) {
1043
0
      sign = query.ContainsSign();
1044
0
      if (sign == 0) {
1045
0
        continue;
1046
0
      }
1047
0
    }
1048
1049
    // The sign bit obtained by crossing edges should be consistent with the
1050
    // sign produced by the S2ContainsVertexQuery.
1051
0
    if (duplicates || winding != (sign < 0 ? 0 : 1)) {
1052
0
      *error = S2Error(
1053
0
          S2Error::POLYGON_INCONSISTENT_LOOP_ORIENTATIONS,
1054
0
          absl::StrFormat(
1055
0
              "Shape %d has one or more edges with interior on the right.",
1056
0
              shape_id));
1057
0
      return false;
1058
0
    }
1059
0
    return true;
1060
0
  }
1061
0
  return true;
1062
0
}
1063
1064
template <typename IndexType>
1065
bool S2ValidQuery<IndexType>::CheckEdge(const S2Shape& shape,
1066
                                        const S2ClippedShape& clipped,
1067
                                        const EdgeAndIdChain& edge,
1068
0
                                        S2Error* error) {
1069
0
  const S2IndexCellData& cell = CurrentCell();
1070
0
  const int dim = shape.dimension();
1071
1072
  // For points, we can check that they're not contained in any other polygons
1073
  // locally within the cell by crossing edges to the cell center.
1074
0
  if (dim == 0 &&
1075
0
      PointContained(cell.id(), clipped.shape_id(), edge.v0, error)) {
1076
0
    return false;
1077
0
  }
1078
1079
  // Edge is OK
1080
0
  return true;
1081
0
}
1082
1083
// Checks that edges of the given shape incident on vertex are ordered such that
1084
// the incident chains do not cross.
1085
//
1086
// We can check this by looking at all the incident edges and making sure that,
1087
// for each incoming edge, as we move counter-clockwise around the vertex, we
1088
// encounter matching pairs of incoming/outgoing edges for each chain.
1089
//
1090
// Returns true if chains do not cross at the vertex, false otherwise.
1091
inline bool CheckVertexCrossings(const S2Point& vertex, const S2Shape& shape,
1092
                                 int shape_id,
1093
                                 const absl::flat_hash_set<int32_t>& edge_ids,
1094
0
                                 S2Error* error) {
1095
  // Extend S2Shape::Edge to wrap an edge with its chain, id and previous id.
1096
0
  struct EdgeWithInfo : public S2Shape::Edge {
1097
0
    EdgeWithInfo(S2Shape::Edge edge, int id, int chain, int prev, int sign)
1098
0
        : S2Shape::Edge(edge), id(id), chain(chain), prev(prev), sign(sign) {}
1099
1100
0
    int id;
1101
0
    int chain;
1102
0
    int prev;
1103
0
    int sign;  // +1 for incoming and -1 for outgoing edges.
1104
0
  };
1105
1106
  // Aggregate edges of the current shape and sort them CCW around the vertex.
1107
0
  absl::InlinedVector<EdgeWithInfo, 6> edges;
1108
0
  for (int32_t edge_id : edge_ids) {
1109
0
    S2Shape::ChainPosition pos = shape.chain_position(edge_id);
1110
0
    const S2Shape::Edge& edge = shape.edge(edge_id);
1111
0
    edges.push_back({
1112
0
        edge,                                       //
1113
0
        edge_id,                                    //
1114
0
        pos.chain_id,                               //
1115
0
        s2shapeutil::PrevEdgeWrap(shape, edge_id),  //
1116
0
        edge.v0 == vertex ? -1 : +1,                //
1117
0
    });
1118
0
  }
1119
0
  SortEdgesCcw(vertex, edges[0], edges);
1120
1121
  // Mapping from chain_id => count.  We'll scan through and sum the signs on
1122
  // the edges for each chain.  When we reach the incoming edge the sums should
1123
  // be zero for every chain.
1124
0
  absl::flat_hash_map<int, int> chain_sums;
1125
0
  chain_sums.reserve(16);
1126
1127
0
  for (size_t i = 0; i < edges.size(); ++i) {
1128
0
    const EdgeWithInfo& curr = edges[i];
1129
1130
    // Skip forward to next outgoing edge.
1131
0
    if (curr.sign > 0) {
1132
0
      continue;
1133
0
    }
1134
1135
    // Scan until we find our incoming edge and tally chain counts.
1136
0
    chain_sums.clear();
1137
0
    size_t j;
1138
0
    for (j = 1; j < edges.size(); ++j) {
1139
0
      const EdgeWithInfo& edge = edges[(i + j) % edges.size()];
1140
0
      if (curr.chain == edge.chain && curr.prev == edge.id) {
1141
0
        for (const auto& sum : chain_sums) {
1142
0
          if (sum.second != 0) {
1143
0
            *error = S2Error(
1144
0
                S2Error::OVERLAPPING_GEOMETRY,
1145
0
                absl::StrFormat(
1146
0
                    "Shape %d has one or more chains that cross at a vertex",
1147
0
                    shape_id));
1148
0
            return false;
1149
0
          }
1150
0
        }
1151
0
        break;
1152
0
      }
1153
1154
0
      chain_sums[edge.chain] += edge.sign;
1155
0
    }
1156
1157
    // If we went all the way around and didn't find an incoming edge, then
1158
    // the geometry must be malformed, and thus isn't valid.
1159
0
    if (j == edges.size()) {
1160
0
      *error = S2Error(S2Error::INVALID_VERTEX,
1161
0
                       "Outgoing edge with no incoming edge");
1162
0
      return false;
1163
0
    }
1164
0
  }
1165
1166
0
  return true;
1167
0
}
1168
1169
template <typename IndexType>
1170
0
bool S2ValidQuery<IndexType>::Finish(S2Error* error) {
1171
  // We've checked edges having interiors on the right, and for crossings at
1172
  // interior points.  The only case left is to check for chains that cross at a
1173
  // vertex.
1174
0
  for (const auto& item : IncidentEdges()) {
1175
0
    const S2Shape& shape = *Index().shape(item.first.shape_id);
1176
0
    if (shape.dimension() == 2) {
1177
0
      if (!CheckVertexCrossings(item.first.vertex, shape, item.first.shape_id,
1178
0
                                item.second, error)) {
1179
0
        return false;
1180
0
      }
1181
0
    }
1182
0
  }
1183
1184
  // If we get to this point we know that polygon edges don't cross any other
1185
  // edges and that edges are properly oriented with the interior on the left.
1186
  //
1187
  // Since edges don't cross, any given chain must be entirely inside or outside
1188
  // any other polygons.  Thus, to determine that polygon interiors are
1189
  // disjoint, we only have to check one vertex of each chain in each shape for
1190
  // containment.
1191
  //
1192
  // We use the OPEN containment model because we check elsewhere if the vertex
1193
  // lands on another vertex.
1194
0
  S2ContainsPointQueryOptions options;
1195
0
  options.set_vertex_model(S2VertexModel::OPEN);
1196
1197
0
  S2ContainsPointQuery<IndexType> query(&Index(), options);
1198
0
  for (int shape_id = 0; shape_id < Index().num_shape_ids(); ++shape_id) {
1199
0
    const S2Shape* shape = Index().shape(shape_id);
1200
0
    if (shape == nullptr || shape->dimension() == 0) {
1201
0
      continue;
1202
0
    }
1203
1204
0
    for (int chain = 0; chain < shape->num_chains(); ++chain) {
1205
0
      if (shape->chain(chain).length < 1) {
1206
0
        continue;
1207
0
      }
1208
1209
0
      S2Point vertex = shape->chain_edge(chain, 0).v0;
1210
0
      if (query.Contains(vertex)) {
1211
0
        *error = S2Error(
1212
0
            S2Error::OVERLAPPING_GEOMETRY,
1213
0
            absl::StrFormat(
1214
0
                "Shape %d has one or more edges contained in another shape.",
1215
0
                shape_id));
1216
0
        return false;
1217
0
      }
1218
0
    }
1219
0
  }
1220
0
  return true;
1221
0
}
1222
1223
//////////////////   S2LegacyValidQuery Implementation   ////////////////////
1224
1225
template <typename IndexType>
1226
0
S2LegacyValidQuery<IndexType>::S2LegacyValidQuery() : Base() {
1227
  // Don't allow degenerate or reverse duplicates edges for legacy semantics.
1228
0
  Base::mutable_options().set_allow_degenerate_edges(false);
1229
0
  Base::mutable_options().set_allow_reverse_duplicates(false);
1230
0
}
1231
1232
template <typename IndexType>
1233
0
bool S2LegacyValidQuery<IndexType>::Start(S2Error* error) {
1234
0
  if (!Base::Start(error)) {
1235
0
    return false;
1236
0
  }
1237
1238
  // We can't mix dimensions under legacy semantics.
1239
0
  int dim = -1;
1240
0
  for (const S2Shape* shape : Index()) {
1241
0
    if (dim < 0) {
1242
0
      dim = shape->dimension();
1243
0
    }
1244
1245
0
    if (dim != shape->dimension()) {
1246
0
      *error = S2Error(
1247
0
          S2Error::INVALID_DIMENSION,
1248
0
          "Mixed dimensional geometry is invalid for legacy semantics.");
1249
0
      return false;
1250
0
    }
1251
0
  }
1252
1253
0
  return true;
1254
0
}
1255
1256
template <typename IndexType>
1257
bool S2LegacyValidQuery<IndexType>::CheckShape(Iterator& iter,
1258
                                               const S2Shape& shape,
1259
0
                                               int shape_id, S2Error* error) {
1260
  // Count the number of empty chains.  Non-empty chains must have at least
1261
  // three vertices.
1262
0
  if (shape.dimension() == 2) {
1263
0
    bool has_empty_loops = false;
1264
0
    for (const S2Shape::Chain& chain : shape.chains()) {
1265
0
      if (chain.length == 0) {
1266
0
        has_empty_loops = true;
1267
0
      } else if (chain.length < 3) {
1268
0
        *error = S2Error(
1269
0
            S2Error::LOOP_NOT_ENOUGH_VERTICES,
1270
0
            absl::StrFormat(
1271
0
                "Shape %d has a non-empty chain with less than three edges.",
1272
0
                shape_id));
1273
0
        return false;
1274
0
      }
1275
0
    }
1276
1277
0
    if (has_empty_loops && shape.num_chains() > 1) {
1278
0
      *error = S2Error(
1279
0
          S2Error::POLYGON_EMPTY_LOOP,
1280
0
          absl::StrFormat("Shape %d has too many empty chains", shape_id));
1281
0
      return false;
1282
0
    }
1283
0
  }
1284
1285
0
  if (!Base::CheckShape(iter, shape, shape_id, error)) {
1286
0
    return false;
1287
0
  }
1288
1289
0
  return true;
1290
0
}
1291
1292
template <typename IndexType>
1293
0
bool S2LegacyValidQuery<IndexType>::StartCell(S2Error* error) {
1294
  // Check for duplicate vertices within a chain.
1295
0
  const S2IndexCellData& cell = CurrentCell();
1296
0
  for (const S2ClippedShape& clipped : cell.clipped_shapes()) {
1297
0
    absl::Span<const EdgeAndIdChain> edges =
1298
0
        cell.shape_edges(clipped.shape_id());
1299
1300
0
    for (size_t i = 0; i < edges.size(); ++i) {
1301
0
      for (size_t j = i + 1; j < edges.size(); ++j) {
1302
0
        if (edges[j].chain != edges[i].chain) {
1303
0
          continue;
1304
0
        }
1305
1306
0
        if (edges[j].v0 == edges[i].v0) {
1307
0
          *error = S2Error(
1308
0
              S2Error::DUPLICATE_VERTICES,
1309
0
              absl::StrFormat("Chain %d of shape %d has duplicate vertices",
1310
0
                              edges[i].chain, clipped.shape_id()));
1311
0
          return false;
1312
0
        }
1313
0
      }
1314
0
    }
1315
0
  }
1316
1317
0
  return Base::StartCell(error);
1318
0
}
1319
1320
template <typename IndexType>
1321
bool S2LegacyValidQuery<IndexType>::CheckEdge(const S2Shape& shape,
1322
                                              const S2ClippedShape& clipped,
1323
                                              const EdgeAndIdChain& edge,
1324
0
                                              S2Error* error) {
1325
0
  if (!Base::CheckEdge(shape, clipped, edge, error)) {
1326
0
    return false;
1327
0
  }
1328
0
  return true;
1329
0
}
1330
1331
#endif  // S2_S2VALIDATION_QUERY_H_