summaryrefslogtreecommitdiff
path: root/src/third_party/s2/s2loop.h
blob: 3095799c8c0e046f40ec212ac37cfc14f2da2bb0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
// Copyright 2005 Google Inc. All Rights Reserved.

#ifndef UTIL_GEOMETRY_S2LOOP_H__
#define UTIL_GEOMETRY_S2LOOP_H__

#include <map>
using std::map;
using std::multimap;

#include <vector>
using std::vector;


#include "base/logging.h"
#include "base/macros.h"
#include "s2edgeindex.h"
#include "s2region.h"
#include "s2latlngrect.h"
#include "s2edgeutil.h"

class S2Loop;
// Defined in the cc file. A helper class for AreBoundariesCrossing.
class WedgeProcessor;

// Indexing structure to efficiently compute intersections.
class S2LoopIndex: public S2EdgeIndex {
 public:
  explicit S2LoopIndex(S2Loop const* loop): loop_(loop) {}
  virtual ~S2LoopIndex() {}

  // There is no need to overwrite Reset(), as the only data that's
  // used to implement this class is all contained in the loop data.
  // void Reset();

  virtual S2Point const* edge_from(int index) const;
  virtual S2Point const* edge_to(int index) const;
  virtual int num_edges() const;

 private:
  S2Loop const* loop_;
};

// An S2Loop represents a simple spherical polygon.  It consists of a single
// chain of vertices where the first vertex is implicitly connected to the
// last.  All loops are defined to have a CCW orientation, i.e. the interior
// of the polygon is on the left side of the edges.  This implies that a
// clockwise loop enclosing a small area is interpreted to be a CCW loop
// enclosing a very large area.
//
// Loops are not allowed to have any duplicate vertices (whether adjacent or
// not), and non-adjacent edges are not allowed to intersect.  Loops must have
// at least 3 vertices.  Although these restrictions are not enforced in
// optimized code, you may get unexpected results if they are violated.
//
// Point containment is defined such that if the sphere is subdivided into
// faces (loops), every point is contained by exactly one face.  This implies
// that loops do not necessarily contain all (or any) of their vertices.
//
// TODO(user): When doing operations on two loops, always create the
// edgeindex for the bigger of the two.  Same for polygons.
class S2Loop : public S2Region {
 public:
  // Create an empty S2Loop that should be initialized by calling Init() or
  // Decode().
  S2Loop();

  // Convenience constructor that calls Init() with the given vertices.
  explicit S2Loop(vector<S2Point> const& vertices);

  // Initialize a loop connecting the given vertices.  The last vertex is
  // implicitly connected to the first.  All points should be unit length.
  // Loops must have at least 3 vertices.
  void Init(vector<S2Point> const& vertices);

  // This parameter should be removed as soon as people stop using the
  // deprecated version of IsValid.
  static int const kDefaultMaxAdjacent = 0;

  // Check whether this loop is valid.  Note that in debug mode, validity
  // is checked at loop creation time, so IsValid()
  // should always return true.
  // If err is not NULL, output errors into it.
  bool IsValid(string* err = NULL) const;


  // These two versions are deprecated and ignore max_adjacent.
  // DEPRECATED.
  static bool IsValid(vector<S2Point> const& vertices, int max_adjacent);
  // DEPRECATED.
  bool IsValid(int max_adjacent) const;

  // Initialize a loop corresponding to the given cell.
  explicit S2Loop(S2Cell const& cell);

  ~S2Loop();

  // The depth of a loop is defined as its nesting level within its containing
  // polygon.  "Outer shell" loops have depth 0, holes within those loops have
  // depth 1, shells within those holes have depth 2, etc.  This field is only
  // used by the S2Polygon implementation.
  int depth() const { return depth_; }
  void set_depth(int depth) { depth_ = depth; }

  // Return true if this loop represents a hole in its containing polygon.
  bool is_hole() const { return (depth_ & 1) != 0; }

  // The sign of a loop is -1 if the loop represents a hole in its containing
  // polygon, and +1 otherwise.
  int sign() const { return is_hole() ? -1 : 1; }

  int num_vertices() const { return num_vertices_; }

  // For convenience, we make two entire copies of the vertex list available:
  // vertex(n..2*n-1) is mapped to vertex(0..n-1), where n == num_vertices().
  S2Point const& vertex(int i) const {
    DCHECK_GE(i, 0);
    DCHECK_LT(i, (2 * num_vertices_));
    int j = i - num_vertices();
    return vertices_[j >= 0 ? j : i];
  }

  // Return true if the loop area is at most 2*Pi.  Degenerate loops are
  // handled consistently with S2::RobustCCW(), i.e., if a loop can be
  // expressed as the union of degenerate or nearly-degenerate CCW triangles,
  // then it will always be considered normalized.
  bool IsNormalized() const;

  // Invert the loop if necessary so that the area enclosed by the loop is at
  // most 2*Pi.
  void Normalize();

  // Reverse the order of the loop vertices, effectively complementing
  // the region represented by the loop.
  void Invert();

  // Return the area of the loop interior, i.e. the region on the left side of
  // the loop.  The return value is between 0 and 4*Pi.  (Note that the return
  // value is not affected by whether this loop is a "hole" or a "shell".)
  double GetArea() const;

  // Return the true centroid of the loop multiplied by the area of the loop
  // (see s2.h for details on centroids).  The result is not unit length, so
  // you may want to normalize it.  Also note that in general, the centroid
  // may not be contained by the loop.
  //
  // We prescale by the loop area for two reasons: (1) it is cheaper to
  // compute this way, and (2) it makes it easier to compute the centroid of
  // more complicated shapes (by splitting them into disjoint regions and
  // adding their centroids).
  //
  // Note that the return value is not affected by whether this loop is a
  // "hole" or a "shell".
  S2Point GetCentroid() const;

  // Return the sum of the turning angles at each vertex.  The return value is
  // positive if the loop is counter-clockwise, negative if the loop is
  // clockwise, and zero if the loop is a great circle.  Degenerate and
  // nearly-degenerate loops are handled consistently with S2::RobustCCW().
  // So for example, if a loop has zero area (i.e., it is a very small CCW
  // loop) then the turning angle will always be negative.
  //
  // This quantity is also called the "geodesic curvature" of the loop.
  double GetTurningAngle() const;

  // Return true if the region contained by this loop is a superset of the
  // region contained by the given other loop.
  bool Contains(S2Loop const* b) const;

  // Return true if the region contained by this loop intersects the region
  // contained by the given other loop.
  bool Intersects(S2Loop const* b) const;

  // Given two loops of a polygon (see s2polygon.h for requirements), return
  // true if A contains B.  This version of Contains() is much cheaper since
  // it does not need to check whether the boundaries of the two loops cross.
  bool ContainsNested(S2Loop const* b) const;

  // Return +1 if A contains B (i.e. the interior of B is a subset of the
  // interior of A), -1 if the boundaries of A and B cross, and 0 otherwise.
  // Requires that A does not properly contain the complement of B, i.e.
  // A and B do not contain each other's boundaries.  This method is used
  // for testing whether multi-loop polygons contain each other.
  //
  // WARNING: there is a bug in this function - it does not detect loop
  // crossings in certain situations involving shared edges.  CL 2926852 works
  // around this bug for polygon intersection, but it probably effects other
  // tests.  TODO: fix ContainsOrCrosses() and revert CL 2926852.
  int ContainsOrCrosses(S2Loop const* b) const;

  // Return true if two loops have the same boundary.  This is true if and
  // only if the loops have the same vertices in the same cyclic order.
  // (For testing purposes.)
  bool BoundaryEquals(S2Loop const* b) const;

  // Return true if two loops have the same boundary except for vertex
  // perturbations.  More precisely, the vertices in the two loops must be in
  // the same cyclic order, and corresponding vertex pairs must be separated
  // by no more than "max_error".  (For testing purposes.)
  bool BoundaryApproxEquals(S2Loop const* b, double max_error = 1e-15) const;

  // Return true if the two loop boundaries are within "max_error" of each
  // other along their entire lengths.  The two loops may have different
  // numbers of vertices.  More precisely, this method returns true if the two
  // loops have parameterizations a:[0,1] -> S^2, b:[0,1] -> S^2 such that
  // distance(a(t), b(t)) <= max_error for all t.  You can think of this as
  // testing whether it is possible to drive two cars all the way around the
  // two loops such that no car ever goes backward and the cars are always
  // within "max_error" of each other.  (For testing purposes.)
  bool BoundaryNear(S2Loop const* b, double max_error = 1e-15) const;

  // This method computes the oriented surface integral of some quantity f(x)
  // over the loop interior, given a function f_tri(A,B,C) that returns the
  // corresponding integral over the spherical triangle ABC.  Here "oriented
  // surface integral" means:
  //
  // (1) f_tri(A,B,C) must be the integral of f if ABC is counterclockwise,
  //     and the integral of -f if ABC is clockwise.
  //
  // (2) The result of this function is *either* the integral of f over the
  //     loop interior, or the integral of (-f) over the loop exterior.
  //
  // Note that there are at least two common situations where it easy to work
  // around property (2) above:
  //
  //  - If the integral of f over the entire sphere is zero, then it doesn't
  //    matter which case is returned because they are always equal.
  //
  //  - If f is non-negative, then it is easy to detect when the integral over
  //    the loop exterior has been returned, and the integral over the loop
  //    interior can be obtained by adding the integral of f over the entire
  //    unit sphere (a constant) to the result.
  //
  // Also requires that the default constructor for T must initialize the
  // value to zero.  (This is true for built-in types such as "double".)
  template <class T>
  T GetSurfaceIntegral(T f_tri(S2Point const&, S2Point const&, S2Point const&))
      const;

  ////////////////////////////////////////////////////////////////////////
  // S2Region interface (see s2region.h for details):

  // GetRectBound() is guaranteed to return exact results, while GetCapBound()
  // is conservative.
  virtual S2Loop* Clone() const;
  virtual S2Cap GetCapBound() const;
  virtual S2LatLngRect GetRectBound() const { return bound_; }

  virtual bool Contains(S2Cell const& cell) const;
  virtual bool MayIntersect(S2Cell const& cell) const;
  virtual bool VirtualContainsPoint(S2Point const& p) const {
    return Contains(p);  // The same as Contains() below, just virtual.
  }

  // The point 'p' does not need to be normalized.
  bool Contains(S2Point const& p) const;

  virtual void Encode(Encoder* const encoder) const;
  virtual bool Decode(Decoder* const decoder);
  virtual bool DecodeWithinScope(Decoder* const decoder);

 private:
  // Internal constructor used only by Clone() that makes a deep copy of
  // its argument.
  explicit S2Loop(S2Loop const* src);

  void InitOrigin();
  void InitBound();

  // Internal implementation of the Decode and DecodeWithinScope methods above.
  // If within_scope is true, memory is allocated for vertices_ and data
  // is copied from the decoder using memcpy. If it is false, vertices_
  // will point to the memory area inside the decoder, and the field
  // owns_vertices_ is set to false.
  bool DecodeInternal(Decoder* const decoder,
                      bool within_scope);

  // Internal implementation of the Intersects() method above.
  bool IntersectsInternal(S2Loop const* b) const;

  // Return an index "first" and a direction "dir" (either +1 or -1) such that
  // the vertex sequence (first, first+dir, ..., first+(n-1)*dir) does not
  // change when the loop vertex order is rotated or inverted.  This allows
  // the loop vertices to be traversed in a canonical order.  The return
  // values are chosen such that (first, ..., first+n*dir) are in the range
  // [0, 2*n-1] as expected by the vertex() method.
  int GetCanonicalFirstVertex(int* dir) const;

  // Return the index of a vertex at point "p", or -1 if not found.
  // The return value is in the range 1..num_vertices_ if found.
  int FindVertex(S2Point const& p) const;

  // This method checks all edges of this loop (A) for intersection
  // against all edges of B.  If there is any shared vertex , the
  // wedges centered at this vertex are sent to wedge_processor.
  //
  // Returns true only when the edges intersect in the sense of
  // S2EdgeUtil::RobustCrossing, returns false immediately when the
  // wedge_processor returns true: this means the wedge processor
  // knows the value of the property that the caller wants to compute,
  // and no further inspection is needed.  For instance, if the
  // property is "loops intersect", then a wedge intersection is all
  // it takes to return true.
  //
  // See Contains(), Intersects() and ContainsOrCrosses() for the
  // three uses of this function.
  bool AreBoundariesCrossing(
      S2Loop const* b, WedgeProcessor* wedge_processor) const;

  // When the loop is modified (the only cae being Invert() at this time),
  // the indexing structures need to be deleted as they become invalid.
  void ResetMutableFields();

  // We store the vertices in an array rather than a vector because we don't
  // need any STL methods, and computing the number of vertices using size()
  // would be relatively expensive (due to division by sizeof(S2Point) == 24).
  // When DecodeWithinScope is used to initialize the loop, we do not
  // take ownership of the memory for vertices_, and the owns_vertices_ field
  // is used to prevent deallocation and overwriting.
  int num_vertices_;
  S2Point* vertices_;
  bool owns_vertices_;

  S2LatLngRect bound_;
  bool origin_inside_;
  int depth_;

  // Quadtree index structure of this loop's edges.
  mutable S2LoopIndex index_;

  // Map for speeding up FindVertex: We will compute a map from vertex to
  // index in the vertex array as soon as there has been enough calls.
  mutable int num_find_vertex_calls_;
  mutable map<S2Point, int> vertex_to_index_;

  DISALLOW_EVIL_CONSTRUCTORS(S2Loop);
};

//////////////////// Implementation Details Follow ////////////////////////

// Since this method is templatized and public, the implementation needs to be
// in the .h file.

template <class T>
T S2Loop::GetSurfaceIntegral(T f_tri(S2Point const&, S2Point const&,
                                     S2Point const&)) const {
  // We sum "f_tri" over a collection T of oriented triangles, possibly
  // overlapping.  Let the sign of a triangle be +1 if it is CCW and -1
  // otherwise, and let the sign of a point "x" be the sum of the signs of the
  // triangles containing "x".  Then the collection of triangles T is chosen
  // such that either:
  //
  //  (1) Each point in the loop interior has sign +1, and sign 0 otherwise; or
  //  (2) Each point in the loop exterior has sign -1, and sign 0 otherwise.
  //
  // The triangles basically consist of a "fan" from vertex 0 to every loop
  // edge that does not include vertex 0.  These triangles will always satisfy
  // either (1) or (2).  However, what makes this a bit tricky is that
  // spherical edges become numerically unstable as their length approaches
  // 180 degrees.  Of course there is not much we can do if the loop itself
  // contains such edges, but we would like to make sure that all the triangle
  // edges under our control (i.e., the non-loop edges) are stable.  For
  // example, consider a loop around the equator consisting of four equally
  // spaced points.  This is a well-defined loop, but we cannot just split it
  // into two triangles by connecting vertex 0 to vertex 2.
  //
  // We handle this type of situation by moving the origin of the triangle fan
  // whenever we are about to create an unstable edge.  We choose a new
  // location for the origin such that all relevant edges are stable.  We also
  // create extra triangles with the appropriate orientation so that the sum
  // of the triangle signs is still correct at every point.

  // The maximum length of an edge for it to be considered numerically stable.
  // The exact value is fairly arbitrary since it depends on the stability of
  // the "f_tri" function.  The value below is quite conservative but could be
  // reduced further if desired.
  static double const kMaxLength = M_PI - 1e-5;

  // The default constructor for T must initialize the value to zero.
  // (This is true for built-in types such as "double".)
  T sum = T();
  S2Point origin = vertex(0);
  for (int i = 1; i + 1 < num_vertices(); ++i) {
    // Let V_i be vertex(i), let O be the current origin, and let length(A,B)
    // be the length of edge (A,B).  At the start of each loop iteration, the
    // "leading edge" of the triangle fan is (O,V_i), and we want to extend
    // the triangle fan so that the leading edge is (O,V_i+1).
    //
    // Invariants:
    //  1. length(O,V_i) < kMaxLength for all (i > 1).
    //  2. Either O == V_0, or O is approximately perpendicular to V_0.
    //  3. "sum" is the oriented integral of f over the area defined by
    //     (O, V_0, V_1, ..., V_i).
    DCHECK(i == 1 || origin.Angle(vertex(i)) < kMaxLength);
    DCHECK(origin == vertex(0) || fabs(origin.DotProd(vertex(0))) < 1e-15);

    if (vertex(i+1).Angle(origin) > kMaxLength) {
      // We are about to create an unstable edge, so choose a new origin O'
      // for the triangle fan.
      S2Point old_origin = origin;
      if (origin == vertex(0)) {
        // The following point is well-separated from V_i and V_0 (and
        // therefore V_i+1 as well).
        origin = S2::RobustCrossProd(vertex(0), vertex(i)).Normalize();
      } else if (vertex(i).Angle(vertex(0)) < kMaxLength) {
        // All edges of the triangle (O, V_0, V_i) are stable, so we can
        // revert to using V_0 as the origin.
        origin = vertex(0);
      } else {
        // (O, V_i+1) and (V_0, V_i) are antipodal pairs, and O and V_0 are
        // perpendicular.  Therefore V_0.CrossProd(O) is approximately
        // perpendicular to all of {O, V_0, V_i, V_i+1}, and we can choose
        // this point O' as the new origin.
        origin = vertex(0).CrossProd(old_origin);

        // Advance the edge (V_0,O) to (V_0,O').
        sum += f_tri(vertex(0), old_origin, origin);
      }
      // Advance the edge (O,V_i) to (O',V_i).
      sum += f_tri(old_origin, vertex(i), origin);
    }
    // Advance the edge (O,V_i) to (O,V_i+1).
    sum += f_tri(origin, vertex(i), vertex(i+1));
  }
  // If the origin is not V_0, we need to sum one more triangle.
  if (origin != vertex(0)) {
    // Advance the edge (O,V_n-1) to (O,V_0).
    sum += f_tri(origin, vertex(num_vertices() - 1), vertex(0));
  }
  return sum;
}

#endif  // UTIL_GEOMETRY_S2LOOP_H__