Coverage Report

Created: 2026-05-30 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/s2geometry/src/s2/s2boolean_operation.h
Line
Count
Source
1
// Copyright 2017 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
#ifndef S2_S2BOOLEAN_OPERATION_H_
19
#define S2_S2BOOLEAN_OPERATION_H_
20
21
#include <cstdint>
22
#include <memory>
23
#include <utility>
24
#include <vector>
25
26
#include "absl/base/nullability.h"
27
#include "absl/log/absl_log.h"
28
#include "s2/_fp_contract_off.h"  // IWYU pragma: keep
29
#include "s2/s2builder.h"
30
#include "s2/s2builder_graph.h"
31
#include "s2/s2builder_layer.h"
32
#include "s2/s2error.h"
33
#include "s2/s2memory_tracker.h"
34
#include "s2/s2shape_index.h"
35
#include "s2/value_lexicon.h"
36
37
// This class implements boolean operations (intersection, union, difference,
38
// and symmetric difference) for regions whose boundaries are defined by
39
// geodesic edges.
40
//
41
// S2BooleanOperation operates on exactly two input regions at a time.  Each
42
// region is represented as an S2ShapeIndex and may contain any number of
43
// points, polylines, and polygons.  The region is essentially the union of
44
// these objects, except that polygon interiors must be disjoint from all
45
// other geometry (including other polygon interiors).  If the input geometry
46
// for a region does not meet this condition, it can be normalized by
47
// computing its union first.  Duplicate polygon edges are not allowed (even
48
// among different polygons), however polylines may have duplicate edges and
49
// may even be self-intersecting.  Note that points or polylines are allowed
50
// to coincide with the boundaries of polygons.
51
//
52
// Degeneracies are fully supported.  Supported degeneracy types include the
53
// following:
54
//
55
//  - Point polylines consisting of a single degenerate edge AA.
56
//
57
//  - Point loops consisting of a single vertex A.  Such loops may represent
58
//    either shells or holes according to whether the loop adds to or
59
//    subtracts from the surrounding region of the polygon.
60
//
61
//  - Sibling edge pairs of the form {AB, BA}.  Such sibling pairs may
62
//    represent either shells or holes according to whether they add to or
63
//    subtract from the surrounding region.  The edges of a sibling pair may
64
//    belong to the same polygon loop (e.g. a loop AB) or to different polygon
65
//    loops or polygons (e.g. the polygons {ABC, CBD}).
66
//
67
// A big advantage of degeneracy support is that geometry may be simplified
68
// without completely losing small details.  For example, if a polygon
69
// representing a land area with many lakes and rivers is simplified using a
70
// tolerance of 1 kilometer, every water feature in the input is guaranteed to
71
// be within 1 kilometer of some water feature in the input (even if some
72
// lakes and rivers are merged and/or reduced to degenerate point or sibling
73
// edge pair holes).  Mathematically speaking, degeneracy support allows
74
// geometry to be simplified while guaranteeing that the Hausdorff distance
75
// between the boundaries of the original and simplified geometries is at
76
// most the simplification tolerance.  It also allows geometry to be
77
// simplified without changing its dimension, thus preserving boundary
78
// semantics.  (Note that the boundary of a polyline ABCD is {A,D}, whereas
79
// the boundary of a degenerate shell ABCDCB is its entire set of vertices and
80
// edges.)
81
//
82
// Points and polyline edges are treated as multisets: if the same point or
83
// polyline edge appears multiple times in the input, it will appear multiple
84
// times in the output.  For example, the union of a point with an identical
85
// point consists of two points.  This feature is useful for modeling large
86
// sets of points or polylines as a single region while maintaining their
87
// distinct identities, even when the points or polylines intersect each
88
// other.  It is also useful for reconstructing polylines that loop back on
89
// themselves (e.g., time series such as GPS tracks).  If duplicate geometry
90
// is not desired, it can easily be removed by choosing the appropriate
91
// S2Builder output layer options.
92
//
93
// Self-intersecting polylines can be manipulated without materializing new
94
// vertices at the self-intersection points.  This feature is important when
95
// processing polylines with large numbers of self-intersections such as GPS
96
// tracks (e.g., consider the path of a race car in the Indy 500).
97
//
98
// Polylines are always considered to be directed.  Polyline edges between the
99
// same pair of vertices are defined to intersect even if the two edges are in
100
// opposite directions.  (Undirected polylines can be modeled by specifying
101
// GraphOptions::EdgeType::UNDIRECTED in the S2Builder output layer.)
102
//
103
// The output of each operation is sent to an S2Builder::Layer provided by the
104
// client.  This allows clients to build any representation of the geometry
105
// they choose.  It also allows the client to do additional postprocessing of
106
// the output before building data structures; for example, the client can
107
// easily discard degeneracies or convert them to another data type.
108
//
109
// The boundaries of polygons and polylines can be modeled as open, semi-open,
110
// or closed.  Polyline boundaries are controlled by the PolylineModel class,
111
// whose options are as follows:
112
//
113
//  - In the OPEN model, polylines do not contain their first or last vertex
114
//    except for one special case: namely, if the polyline forms a loop and
115
//    the polyline_loops_have_boundaries() option is set to false, then the
116
//    first/last vertex is contained.
117
//
118
//  - In the SEMI_OPEN model, polylines contain all vertices except the last.
119
//    Therefore if one polyline starts where another polyline stops, the two
120
//    polylines do not intersect.
121
//
122
//  - In the CLOSED model, polylines contain all of their vertices.
123
//
124
// When multiple polylines are present, they are processed independently and
125
// have no effect on each other.  For example, in the OPEN boundary model the
126
// polyline ABC contains the vertex B, while set of polylines {AB, BC} does
127
// not.  (If you want to treat the polylines as a union instead, with
128
// boundaries merged according to the "mod 2" rule, this can be achieved by
129
// reassembling the edges into maximal polylines using S2PolylineVectorLayer
130
// with EdgeType::UNDIRECTED, DuplicateEdges::MERGE, and PolylineType::WALK.)
131
//
132
// Polygon boundaries are controlled by the PolygonModel class, which has the
133
// following options:
134
//
135
//  - In the OPEN model, polygons do not contain their vertices or edges.
136
//    This implies that a polyline that follows the boundary of a polygon will
137
//    not intersect it.
138
//
139
//  - In the SEMI_OPEN model, polygon point containment is defined such that
140
//    if several polygons tile the region around a vertex, then exactly one of
141
//    those polygons contains that vertex.  Similarly polygons contain all of
142
//    their edges, but none of their reversed edges.  This implies that a
143
//    polyline and polygon edge with the same endpoints intersect if and only
144
//    if they are in the same direction.  (This rule ensures that if a
145
//    polyline is intersected with a polygon and its complement, the two
146
//    resulting polylines do not have any edges in common.)
147
//
148
//  - In the CLOSED model, polygons contain all their vertices, edges, and
149
//    reversed edges.  This implies that a polyline that shares an edge (in
150
//    either direction) with a polygon is defined to intersect it.  Similarly,
151
//    this is the only model where polygons that touch at a vertex or along an
152
//    edge intersect.
153
//
154
// Note that PolylineModel and PolygonModel are defined as separate classes in
155
// order to allow for possible future extensions.
156
//
157
// Operations between geometry of different dimensions are defined as follows:
158
//
159
//  - For UNION, the higher-dimensional shape always wins.  For example the
160
//    union of a closed polygon A with a polyline B that coincides with the
161
//    boundary of A consists only of the polygon A.
162
//
163
//  - For INTERSECTION, the lower-dimensional shape always wins.  For example,
164
//    the intersection of a closed polygon A with a point B that coincides
165
//    with a vertex of A consists only of the point B.
166
//
167
//  - For DIFFERENCE, higher-dimensional shapes are not affected by
168
//    subtracting lower-dimensional shapes.  For example, subtracting a point
169
//    or polyline from a polygon A yields the original polygon A.  This rule
170
//    exists because in general, it is impossible to represent the output
171
//    using the specified boundary model(s).  (Consider subtracting one vertex
172
//    from a PolylineModel::CLOSED polyline, or subtracting one edge from a
173
//    PolygonModel::CLOSED polygon.)  If you want to perform operations like
174
//    this, consider representing all boundaries explicitly (topological
175
//    boundaries) using OPEN boundary models.  Another option for polygons is
176
//    to subtract a degenerate loop, which yields a polygon with a degenerate
177
//    hole (see S2LaxPolygonShape).
178
//
179
// Note that in the case of Precision::EXACT operations, the above remarks
180
// only apply to the output before snapping.  Snapping may cause nearby
181
// distinct edges to become coincident, e.g. a polyline may become coincident
182
// with a polygon boundary.  However also note that S2BooleanOperation is
183
// perfectly happy to accept such geometry as input.
184
//
185
// Example usage:
186
//   S2ShapeIndex a, b;  // Input geometry, e.g. containing polygons.
187
//   S2Polygon polygon;  // Output geometry.
188
//   S2BooleanOperation::Options options;
189
//   options.set_snap_function(snap_function);
190
//   S2BooleanOperation op(S2BooleanOperation::OpType::INTERSECTION,
191
//                         std::make_unique<S2PolygonLayer>(&polygon),
192
//                         options);
193
//   S2Error error;
194
//   if (!op.Build(a, b, &error)) {
195
//     ABSL_LOG(ERROR) << error;
196
//     ...
197
//   }
198
//
199
// If the output includes objects of different dimensions, they can be
200
// assembled into different layers with code like this:
201
//
202
//   vector<S2Point> points;
203
//   vector<unique_ptr<S2Polyline>> polylines;
204
//   S2Polygon polygon;
205
//   S2BooleanOperation op(
206
//       S2BooleanOperation::OpType::UNION,
207
//       std::make_unique<s2builderutil::S2PointVectorLayer>(&points),
208
//       std::make_unique<s2builderutil::S2PolylineVectorLayer>(&polylines),
209
//       std::make_unique<S2PolygonLayer>(&polygon));
210
211
class S2BooleanOperation {
212
 public:
213
  // The supported operation types.
214
  enum class OpType : uint8_t {
215
    UNION,                // Contained by either region.
216
    INTERSECTION,         // Contained by both regions.
217
    DIFFERENCE,           // Contained by the first region but not the second.
218
    SYMMETRIC_DIFFERENCE  // Contained by one region but not the other.
219
  };
220
  // Translates OpType to one of the strings above.
221
  static absl::string_view OpTypeToString(OpType op_type);
222
223
  // Defines whether polygons are considered to contain their vertices and/or
224
  // edges (see definitions above).
225
  enum class PolygonModel : uint8_t { OPEN, SEMI_OPEN, CLOSED };
226
227
  // Translates PolygonModel to one of the strings above.
228
  static absl::string_view PolygonModelToString(PolygonModel model);
229
230
  // Defines whether polylines are considered to contain their endpoints
231
  // (see definitions above).
232
  enum class PolylineModel : uint8_t { OPEN, SEMI_OPEN, CLOSED };
233
234
  // Translates PolylineModel to one of the strings above.
235
  static absl::string_view PolylineModelToString(PolylineModel model);
236
237
  // With Precision::EXACT, the operation is evaluated using the exact input
238
  // geometry.  Predicates that use this option will produce exact results;
239
  // for example, they can distinguish between a polyline that barely
240
  // intersects a polygon from one that barely misses it.  Constructive
241
  // operations (ones that yield new geometry, as opposed to predicates) are
242
  // implemented by computing the exact result and then snap rounding it
243
  // according to the given snap_function() (see below).  This is as close as
244
  // it is possible to get to the exact result while requiring that vertex
245
  // coordinates have type "double".
246
  //
247
  // With Precision::SNAPPED, the input regions are snapped together *before*
248
  // the operation is evaluated.  So for example, two polygons that overlap
249
  // slightly will be treated as though they share a common boundary, and
250
  // similarly two polygons that are slightly separated from each other will
251
  // be treated as though they share a common boundary.  Snapped results are
252
  // useful for dealing with points, since in S2 the only points that lie
253
  // exactly on a polyline or polygon edge are the endpoints of that edge.
254
  //
255
  // Conceptually, the difference between these two options is that with
256
  // Precision::SNAPPED, the inputs are snap rounded (together), whereas with
257
  // Precision::EXACT only the result is snap rounded.
258
  enum class Precision : uint8_t { EXACT, SNAPPED };
259
260
  // SourceId identifies an edge from one of the two input S2ShapeIndexes.
261
  // It consists of a region id (0 or 1), a shape id within that region's
262
  // S2ShapeIndex, and an edge id within that shape.
263
  class SourceId {
264
   public:
265
    SourceId();
266
    SourceId(int region_id, int32_t shape_id, int32_t edge_id);
267
    explicit SourceId(int32_t special_edge_id);
268
0
    int region_id() const { return region_id_; }
269
0
    int32_t shape_id() const { return shape_id_; }
270
0
    int32_t edge_id() const { return edge_id_; }
271
    // TODO(ericv): Convert to functions, define all 6 comparisons.
272
    bool operator==(SourceId other) const;
273
    bool operator<(SourceId other) const;
274
275
   private:
276
    // TODO(user): Use in-class initializers when C++20 is allowed in
277
    // opensource.
278
    uint32_t region_id_ : 1;
279
    uint32_t shape_id_ : 31;
280
    int32_t edge_id_;
281
  };
282
283
  class Options {
284
   public:
285
    Options();
286
287
    // Convenience constructor that calls set_snap_function().
288
    explicit Options(const S2Builder::SnapFunction& snap_function);
289
290
    // Specifies the function to be used for snap rounding.
291
    //
292
    // DEFAULT: s2builderutil::IdentitySnapFunction(S1Angle::Zero())
293
    //  - This does no snapping and preserves all input vertices exactly unless
294
    //    there are crossing edges, in which case the snap radius is increased
295
    //    to the maximum intersection point error (S2::kIntersectionError).
296
    const S2Builder::SnapFunction& snap_function() const;
297
    void set_snap_function(const S2Builder::SnapFunction& snap_function);
298
299
    // Defines whether polygons are considered to contain their vertices
300
    // and/or edges (see comments above).
301
    //
302
    // DEFAULT: PolygonModel::SEMI_OPEN
303
    PolygonModel polygon_model() const;
304
    void set_polygon_model(PolygonModel model);
305
306
    // Defines whether polylines are considered to contain their vertices (see
307
    // comments above).
308
    //
309
    // DEFAULT: PolylineModel::CLOSED
310
    PolylineModel polyline_model() const;
311
    void set_polyline_model(PolylineModel model);
312
313
    // Specifies whether a polyline loop is considered to have a non-empty
314
    // boundary.  By default this option is true, meaning that even if the
315
    // first and last vertices of a polyline are the same, the polyline is
316
    // considered to have a well-defined "start" and "end".  For example, if
317
    // the polyline boundary model is OPEN then the polyline loop would not
318
    // include the start/end vertices.  These are the best semantics for most
319
    // applications, such as GPS tracks or road network segments.
320
    //
321
    // If the polyline forms a loop and this option is set to false, then
322
    // instead the first and last vertices are considered to represent a
323
    // single vertex in the interior of the polyline.  In this case the
324
    // boundary of the polyline is empty, meaning that the first/last vertex
325
    // will be contained by the polyline even if the boundary model is OPEN.
326
    // (Note that this option also has a small effect on the CLOSED boundary
327
    // model, because the first/last vertices of a polyline loop are
328
    // considered to represent one vertex rather than two.)
329
    //
330
    // The main reason for this option is to implement the "mod 2 union"
331
    // boundary semantics of the OpenGIS Simple Features spec.  This can be
332
    // achieved by making sure that all polylines are constructed using
333
    // S2Builder::Graph::PolylineType::WALK (which ensures that all polylines
334
    // are as long as possible), and then setting this option to false.
335
    //
336
    // DEFAULT: true
337
    bool polyline_loops_have_boundaries() const;
338
    void set_polyline_loops_have_boundaries(bool value);
339
340
    // Specifies that a new vertex should be added whenever a polyline edge
341
    // crosses another polyline edge.  Note that this can cause the size of
342
    // polylines with many self-intersections to increase quadratically.
343
    //
344
    // If false, new vertices are added only when a polyline from one input
345
    // region cross a polyline from the other input region.  This allows
346
    // self-intersecting input polylines to be modified as little as possible.
347
    //
348
    // DEFAULT: false
349
    bool split_all_crossing_polyline_edges() const;
350
    void set_split_all_crossing_polyline_edges(bool value);
351
352
    // Specifies whether the operation should use the exact input geometry
353
    // (Precision::EXACT), or whether the two input regions should be snapped
354
    // together first (Precision::SNAPPED).
355
    //
356
    // DEFAULT: Precision::EXACT
357
    Precision precision() const;
358
    // TODO(b/78590369): Precision.SNAPPED is not yet implemented.
359
    // void set_precision(Precision precision);
360
361
    // If true, the input geometry is interpreted as representing nearby
362
    // geometry that has been snapped or simplified.  It then outputs a
363
    // conservative result based on the value of polygon_model() and
364
    // polyline_model().  For the most part, this only affects the handling of
365
    // degeneracies.
366
    //
367
    // - If the model is OPEN, the result is as open as possible.  For
368
    //   example, the intersection of two identical degenerate shells is empty
369
    //   under PolygonModel::OPEN because they could have been disjoint before
370
    //   snapping.  Similarly, two identical degenerate polylines have an
371
    //   empty intersection under PolylineModel::OPEN.
372
    //
373
    // - If the model is CLOSED, the result is as closed as possible.  In the
374
    //   case of the DIFFERENCE operation, this is equivalent to evaluating
375
    //   A - B as Closure(A) - Interior(B).  For other operations, it affects
376
    //   only the handling of degeneracies.  For example, the union of two
377
    //   identical degenerate holes is empty under PolygonModel::CLOSED
378
    //   (i.e., the hole disappears) because the holes could have been
379
    //   disjoint before snapping.
380
    //
381
    // - If the model is SEMI_OPEN, the result is as degenerate as possible.
382
    //   New degeneracies will not be created, but all degeneracies that
383
    //   coincide with the opposite region's boundary are retained unless this
384
    //   would cause a duplicate polygon edge to be created.  This model is
385
    //   is very useful for working with input data that has both positive and
386
    //   negative degeneracies (i.e., degenerate shells and holes).
387
    //
388
    // DEFAULT: false
389
    bool conservative_output() const;
390
    // TODO(b/315152896): Implement support for conservative output.
391
    // void set_conservative_output(bool conservative);
392
393
    // If specified, then each output edge will be labelled with one or more
394
    // SourceIds indicating which input edge(s) it corresponds to.  This
395
    // can be useful if your input geometry has additional data that needs to
396
    // be propagated from the input to the output (e.g., elevations).
397
    //
398
    // You can access the labels by using an S2Builder::Layer type that
399
    // supports labels, such as S2PolygonLayer.  The layer outputs a
400
    // "label_set_lexicon" and an "label_set_id" for each edge.  You can then
401
    // look up the source information for each edge like this:
402
    //
403
    // for (int32_t label : label_set_lexicon.id_set(label_set_id)) {
404
    //   const SourceId& src = source_id_lexicon.value(label);
405
    //   // region_id() specifies which S2ShapeIndex the edge is from (0 or 1).
406
    //   DoSomething(src.region_id(), src.shape_id(), src.edge_id());
407
    // }
408
    //
409
    // DEFAULT: nullptr
410
    ValueLexicon<SourceId>* source_id_lexicon() const;
411
    // TODO(b/314819022): Implement support for source_id_lexicon.
412
    // void set_source_id_lexicon(ValueLexicon<SourceId>* source_id_lexicon);
413
414
    // Specifies that internal memory usage should be tracked using the given
415
    // S2MemoryTracker.  If a memory limit is specified and more more memory
416
    // than this is required then an error will be returned.  Example usage:
417
    //
418
    //   S2MemoryTracker tracker;
419
    //   tracker.set_limit(500 << 20);  // 500 MB
420
    //   S2BooleanOperation::Options options;
421
    //   options.set_memory_tracker(&tracker);
422
    //   S2BooleanOperation op(..., options);
423
    //   ...
424
    //   S2Error error;
425
    //   if (!op.Build(..., &error)) {
426
    //     if (error.code() == S2Error::RESOURCE_EXHAUSTED) {
427
    //       ABSL_LOG(ERROR) << error;  // Memory limit exceeded
428
    //     }
429
    //   }
430
    //
431
    // CAVEATS:
432
    //
433
    //  - Memory used by the input S2ShapeIndexes and the output S2Builder
434
    //    layers is not counted towards the total.
435
    //
436
    //  - While memory tracking is reasonably complete and accurate, it does
437
    //    not account for every last byte.  It is intended only for the
438
    //    purpose of preventing clients from running out of memory.
439
    //
440
    // DEFAULT: nullptr (memory tracking disabled)
441
    S2MemoryTracker* memory_tracker() const;
442
    void set_memory_tracker(S2MemoryTracker* tracker);
443
444
    // Options may be assigned and copied.
445
    Options(const Options& options);
446
    Options& operator=(const Options& options);
447
448
   private:
449
    std::unique_ptr<S2Builder::SnapFunction> snap_function_;
450
    PolygonModel polygon_model_ = PolygonModel::SEMI_OPEN;
451
    PolylineModel polyline_model_ = PolylineModel::CLOSED;
452
    bool polyline_loops_have_boundaries_ = true;
453
    bool split_all_crossing_polyline_edges_ = false;
454
    Precision precision_ = Precision::EXACT;
455
    bool conservative_output_ = false;
456
    ValueLexicon<SourceId>* source_id_lexicon_ = nullptr;
457
    S2MemoryTracker* memory_tracker_ = nullptr;
458
  };
459
460
#ifndef SWIG
461
  // Specifies that the output boundary edges should be sent to a single
462
  // S2Builder layer.  This version can be used when the dimension of the
463
  // output geometry is known (e.g., intersecting two polygons to yield a
464
  // third polygon).
465
  S2BooleanOperation(OpType op_type,
466
                     std::unique_ptr<S2Builder::Layer> layer,
467
                     const Options& options = Options());
468
469
  // Specifies that the output boundary edges should be sent to three
470
  // different layers according to their dimension.  Points (represented by
471
  // degenerate edges) are sent to layer 0, polyline edges are sent to
472
  // layer 1, and polygon edges are sent to layer 2.
473
  //
474
  // The dimension of an edge is defined as the minimum dimension of the two
475
  // input edges that produced it.  For example, the intersection of two
476
  // crossing polyline edges is a considered to be a degenerate polyline
477
  // rather than a point, so it is sent to layer 1.  Clients can easily
478
  // reclassify such polylines as points if desired, but this rule makes it
479
  // easier for clients that want to process point, polyline, and polygon
480
  // inputs differently.
481
  //
482
  // The layers are always built in the order 0, 1, 2, and all arguments to
483
  // the Build() calls are guaranteed to be valid until the last call returns.
484
  // All Graph objects have the same set of vertices and the same lexicon
485
  // objects, in order to make it easier to write classes that process all the
486
  // edges in parallel.
487
  S2BooleanOperation(OpType op_type,
488
                     std::vector<std::unique_ptr<S2Builder::Layer>> layers,
489
                     const Options& options = Options());
490
#endif
491
492
0
  OpType op_type() const { return op_type_; }
493
0
  const Options& options() const { return options_; }
494
495
  // Executes the given operation.  Returns true on success, and otherwise
496
  // sets "error" appropriately.  (This class does not generate any errors
497
  // itself, but the S2Builder::Layer might.)
498
  bool Build(const S2ShapeIndex& a, const S2ShapeIndex& b,
499
             S2Error* absl_nonnull error);
500
501
  // Convenience method that returns true if the result of the given operation
502
  // is empty.
503
  static bool IsEmpty(OpType op_type,
504
                      const S2ShapeIndex& a, const S2ShapeIndex& b,
505
                      const Options& options = Options());
506
507
  // Convenience method that returns true if A intersects B.
508
  static bool Intersects(const S2ShapeIndex& a, const S2ShapeIndex& b,
509
0
                         const Options& options = Options()) {
510
0
    return !IsEmpty(OpType::INTERSECTION, b, a, options);
511
0
  }
512
513
  // Convenience method that returns true if A contains B, i.e., if the
514
  // difference (B - A) is empty.
515
  static bool Contains(const S2ShapeIndex& a, const S2ShapeIndex& b,
516
0
                       const Options& options = Options()) {
517
0
    return IsEmpty(OpType::DIFFERENCE, b, a, options);
518
0
  }
519
520
  // Convenience method that returns true if the symmetric difference of A and
521
  // B is empty.  (Note that A and B may still not be identical, e.g. A may
522
  // contain two copies of a polyline while B contains one.)
523
  static bool Equals(const S2ShapeIndex& a, const S2ShapeIndex& b,
524
0
                     const Options& options = Options()) {
525
0
    return IsEmpty(OpType::SYMMETRIC_DIFFERENCE, b, a, options);
526
0
  }
527
528
 private:
529
  class Impl;  // The actual implementation.
530
531
  // Internal constructor to reduce code duplication.
532
  S2BooleanOperation(OpType op_type, const Options& options);
533
534
  // Specifies that "result_empty" should be set to indicate whether the exact
535
  // result of the operation is empty.  This constructor is used to efficiently
536
  // test boolean relationships (see IsEmpty above).
537
  S2BooleanOperation(OpType op_type, bool* result_empty,
538
                     const Options& options = Options());
539
540
  Options options_;
541
  OpType op_type_;
542
543
  // The input regions.
544
  const S2ShapeIndex* regions_[2];
545
546
  // The output consists either of zero layers, one layer, or three layers.
547
  std::vector<std::unique_ptr<S2Builder::Layer>> layers_;
548
549
  // The following field is set if and only if there are no output layers.
550
  bool* result_empty_ = nullptr;
551
};
552
553
554
//////////////////   Implementation details follow   ////////////////////
555
556
557
inline S2BooleanOperation::SourceId::SourceId()
558
    : region_id_(0), shape_id_(0), edge_id_(-1) {
559
}
560
561
inline S2BooleanOperation::SourceId::SourceId(int region_id, int32_t shape_id,
562
                                              int32_t edge_id)
563
0
    : region_id_(region_id), shape_id_(shape_id), edge_id_(edge_id) {}
564
565
inline S2BooleanOperation::SourceId::SourceId(int special_edge_id)
566
0
    : region_id_(0), shape_id_(0), edge_id_(special_edge_id) {
567
0
}
568
569
0
inline bool S2BooleanOperation::SourceId::operator==(SourceId other) const {
570
0
  return (region_id_ == other.region_id_ &&
571
0
          shape_id_ == other.shape_id_ &&
572
0
          edge_id_ == other.edge_id_);
573
0
}
574
575
0
inline bool S2BooleanOperation::SourceId::operator<(SourceId other) const {
576
0
  if (region_id_ < other.region_id_) return true;
577
0
  if (region_id_ > other.region_id_) return false;
578
0
  if (shape_id_ < other.shape_id_) return true;
579
0
  if (shape_id_ > other.shape_id_) return false;
580
0
  return edge_id_ < other.edge_id_;
581
0
}
582
583
#endif  // S2_S2BOOLEAN_OPERATION_H_