1 // Copyright Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS-IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 
16 #include "s2/s2max_distance_targets.h"
17 
18 #include <memory>
19 #include "absl/memory/memory.h"
20 #include "s2/s1angle.h"
21 #include "s2/s2cap.h"
22 #include "s2/s2cell.h"
23 #include "s2/s2edge_distances.h"
24 #include "s2/s2furthest_edge_query.h"
25 #include "s2/s2shape_index_region.h"
26 #include "s2/s2text_format.h"
27 
28 //////////////////   Point Target   ////////////////////
29 
30 // This method returns an S2Cap that bounds the antipode of the target.  (This
31 // is the set of points whose S2MaxDistance to the target is
32 // S2MaxDistance::Zero().)
GetCapBound()33 S2Cap S2MaxDistancePointTarget::GetCapBound() {
34   return S2Cap(-point_, S1ChordAngle::Zero());
35 }
36 
UpdateMinDistance(const S2Point & p,S2MaxDistance * min_dist)37 bool S2MaxDistancePointTarget::UpdateMinDistance(
38     const S2Point& p, S2MaxDistance* min_dist) {
39   return min_dist->UpdateMin(S2MaxDistance(S1ChordAngle(p, point_)));
40 }
41 
UpdateMinDistance(const S2Point & v0,const S2Point & v1,S2MaxDistance * min_dist)42 bool S2MaxDistancePointTarget::UpdateMinDistance(
43     const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) {
44   S1ChordAngle dist(*min_dist);
45   if (S2::UpdateMaxDistance(point_, v0, v1, &dist)) {
46     min_dist->UpdateMin(S2MaxDistance(dist));
47     return true;
48   }
49   return false;
50 }
51 
UpdateMinDistance(const S2Cell & cell,S2MaxDistance * min_dist)52 bool S2MaxDistancePointTarget::UpdateMinDistance(
53     const S2Cell& cell, S2MaxDistance* min_dist) {
54   return min_dist->UpdateMin(S2MaxDistance(cell.GetMaxDistance(point_)));
55 }
56 
VisitContainingShapes(const S2ShapeIndex & index,const ShapeVisitor & visitor)57 bool S2MaxDistancePointTarget::VisitContainingShapes(
58     const S2ShapeIndex& index, const ShapeVisitor& visitor) {
59   // For furthest points, we visit the polygons whose interior contains the
60   // antipode of the target point.  (These are the polygons whose
61   // S2MaxDistance to the target is S2MaxDistance::Zero().)
62   return MakeS2ContainsPointQuery(&index).VisitContainingShapes(
63       -point_, [this, &visitor](S2Shape* shape) {
64         return visitor(shape, point_);
65       });
66 }
67 
68 //////////////////   Edge Target   ////////////////////
69 
70 // This method returns an S2Cap that bounds the antipode of the target.  (This
71 // is the set of points whose S2MaxDistance to the target is
72 // S2MaxDistance::Zero().)
GetCapBound()73 S2Cap S2MaxDistanceEdgeTarget::GetCapBound() {
74   // The following computes a radius equal to half the edge length in an
75   // efficient and numerically stable way.
76   double d2 = S1ChordAngle(a_, b_).length2();
77   double r2 = (0.5 * d2) / (1 + sqrt(1 - 0.25 * d2));
78   return S2Cap(-(a_ + b_).Normalize(), S1ChordAngle::FromLength2(r2));
79 }
80 
UpdateMinDistance(const S2Point & p,S2MaxDistance * min_dist)81 bool S2MaxDistanceEdgeTarget::UpdateMinDistance(
82     const S2Point& p, S2MaxDistance* min_dist) {
83   S1ChordAngle dist(*min_dist);
84   if (S2::UpdateMaxDistance(p, a_, b_, &dist)) {
85     min_dist->UpdateMin(S2MaxDistance(dist));
86     return true;
87   }
88   return false;
89 }
90 
UpdateMinDistance(const S2Point & v0,const S2Point & v1,S2MaxDistance * min_dist)91 bool S2MaxDistanceEdgeTarget::UpdateMinDistance(
92     const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) {
93   S1ChordAngle dist(*min_dist);
94   if (S2::UpdateEdgePairMaxDistance(a_, b_, v0, v1, &dist)) {
95     min_dist->UpdateMin(S2MaxDistance(dist));
96     return true;
97   }
98   return false;
99 }
100 
UpdateMinDistance(const S2Cell & cell,S2MaxDistance * min_dist)101 bool S2MaxDistanceEdgeTarget::UpdateMinDistance(
102     const S2Cell& cell, S2MaxDistance* min_dist) {
103   return min_dist->UpdateMin(S2MaxDistance(cell.GetMaxDistance(a_, b_)));
104 }
105 
VisitContainingShapes(const S2ShapeIndex & index,const ShapeVisitor & visitor)106 bool S2MaxDistanceEdgeTarget::VisitContainingShapes(
107     const S2ShapeIndex& index, const ShapeVisitor& visitor) {
108   // We only need to test one edge point.  That is because the method *must*
109   // visit a polygon if it fully contains the target, and *is allowed* to
110   // visit a polygon if it intersects the target.  If the tested vertex is not
111   // contained, we know the full edge is not contained; if the tested vertex is
112   // contained, then the edge either is fully contained (must be visited) or it
113   // intersects (is allowed to be visited).  We visit the center of the edge so
114   // that edge AB gives identical results to BA.
115   S2MaxDistancePointTarget target((a_ + b_).Normalize());
116   return target.VisitContainingShapes(index, visitor);
117 }
118 
119 //////////////////   Cell Target   ////////////////////
120 
121 // This method returns an S2Cap that bounds the antipode of the target.  (This
122 // is the set of points whose S2MaxDistance to the target is
123 // S2MaxDistance::Zero().)
GetCapBound()124 S2Cap S2MaxDistanceCellTarget::GetCapBound() {
125   S2Cap cap = cell_.GetCapBound();
126   return S2Cap(-cap.center(), cap.radius());
127 }
128 
UpdateMinDistance(const S2Point & p,S2MaxDistance * min_dist)129 bool S2MaxDistanceCellTarget::UpdateMinDistance(
130     const S2Point& p, S2MaxDistance* min_dist) {
131   return min_dist->UpdateMin(S2MaxDistance(cell_.GetMaxDistance(p)));
132 }
133 
UpdateMinDistance(const S2Point & v0,const S2Point & v1,S2MaxDistance * min_dist)134 bool S2MaxDistanceCellTarget::UpdateMinDistance(
135     const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) {
136   return min_dist->UpdateMin(S2MaxDistance(cell_.GetMaxDistance(v0, v1)));
137 }
138 
UpdateMinDistance(const S2Cell & cell,S2MaxDistance * min_dist)139 bool S2MaxDistanceCellTarget::UpdateMinDistance(
140     const S2Cell& cell, S2MaxDistance* min_dist) {
141   return min_dist->UpdateMin(S2MaxDistance(cell_.GetMaxDistance(cell)));
142 }
143 
VisitContainingShapes(const S2ShapeIndex & index,const ShapeVisitor & visitor)144 bool S2MaxDistanceCellTarget::VisitContainingShapes(
145     const S2ShapeIndex& index, const ShapeVisitor& visitor) {
146   // We only need to check one point here - cell center is simplest.
147   // See comment at S2MaxDistanceEdgeTarget::VisitContainingShapes.
148   S2MaxDistancePointTarget target(cell_.GetCenter());
149   return target.VisitContainingShapes(index, visitor);
150 }
151 
152 //////////////////   Index Target   ////////////////////
153 
S2MaxDistanceShapeIndexTarget(const S2ShapeIndex * index)154 S2MaxDistanceShapeIndexTarget::S2MaxDistanceShapeIndexTarget(
155     const S2ShapeIndex* index)
156     : index_(index), query_(absl::make_unique<S2FurthestEdgeQuery>(index)) {
157 }
158 
~S2MaxDistanceShapeIndexTarget()159 S2MaxDistanceShapeIndexTarget::~S2MaxDistanceShapeIndexTarget() {
160 }
161 
include_interiors() const162 bool S2MaxDistanceShapeIndexTarget::include_interiors() const {
163   return query_->options().include_interiors();
164 }
165 
set_include_interiors(bool include_interiors)166 void S2MaxDistanceShapeIndexTarget::set_include_interiors(
167     bool include_interiors) {
168   query_->mutable_options()->set_include_interiors(include_interiors);
169 }
170 
use_brute_force() const171 bool S2MaxDistanceShapeIndexTarget::use_brute_force() const {
172   return query_->options().use_brute_force();
173 }
174 
set_use_brute_force(bool use_brute_force)175 void S2MaxDistanceShapeIndexTarget::set_use_brute_force(
176     bool use_brute_force) {
177   query_->mutable_options()->set_use_brute_force(use_brute_force);
178 }
179 
set_max_error(const S1ChordAngle & max_error)180 bool S2MaxDistanceShapeIndexTarget::set_max_error(
181     const S1ChordAngle& max_error) {
182   query_->mutable_options()->set_max_error(max_error);
183   return true;  // Indicates that we may return suboptimal results.
184 }
185 
186 // This method returns an S2Cap that bounds the antipode of the target.  (This
187 // is the set of points whose S2MaxDistance to the target is
188 // S2MaxDistance::Zero().)
GetCapBound()189 S2Cap S2MaxDistanceShapeIndexTarget::GetCapBound() {
190   S2Cap cap = MakeS2ShapeIndexRegion(index_).GetCapBound();
191   return S2Cap(-cap.center(), cap.radius());
192 }
193 
UpdateMinDistance(const S2Point & p,S2MaxDistance * min_dist)194 bool S2MaxDistanceShapeIndexTarget::UpdateMinDistance(
195     const S2Point& p, S2MaxDistance* min_dist) {
196   query_->mutable_options()->set_min_distance(S1ChordAngle(*min_dist));
197   S2FurthestEdgeQuery::PointTarget target(p);
198   S2FurthestEdgeQuery::Result r = query_->FindFurthestEdge(&target);
199   if (r.shape_id() < 0) {
200     return false;
201   }
202   *min_dist = S2MaxDistance(r.distance());
203   return true;
204 }
205 
UpdateMinDistance(const S2Point & v0,const S2Point & v1,S2MaxDistance * min_dist)206 bool S2MaxDistanceShapeIndexTarget::UpdateMinDistance(
207     const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) {
208   query_->mutable_options()->set_min_distance(S1ChordAngle(*min_dist));
209   S2FurthestEdgeQuery::EdgeTarget target(v0, v1);
210   S2FurthestEdgeQuery::Result r = query_->FindFurthestEdge(&target);
211   if (r.shape_id() < 0) return false;
212   *min_dist = S2MaxDistance(r.distance());
213   return true;
214 }
215 
UpdateMinDistance(const S2Cell & cell,S2MaxDistance * min_dist)216 bool S2MaxDistanceShapeIndexTarget::UpdateMinDistance(
217     const S2Cell& cell, S2MaxDistance* min_dist) {
218   query_->mutable_options()->set_min_distance(S1ChordAngle(*min_dist));
219   S2FurthestEdgeQuery::CellTarget target(cell);
220   S2FurthestEdgeQuery::Result r = query_->FindFurthestEdge(&target);
221   if (r.shape_id() < 0) return false;
222   *min_dist = S2MaxDistance(r.distance());
223   return true;
224 }
225 
226 // For target types consisting of multiple connected components (such as
227 // S2MaxDistanceShapeIndexTarget), this method should return the
228 // polygons containing the antipodal reflection of *any* connected
229 // component.  (It is sufficient to test containment of one vertex per
230 // connected component, since the API allows us to also return any polygon
231 // whose boundary has S2MaxDistance::Zero() to the target.)
VisitContainingShapes(const S2ShapeIndex & query_index,const ShapeVisitor & visitor)232 bool S2MaxDistanceShapeIndexTarget::VisitContainingShapes(
233     const S2ShapeIndex& query_index, const ShapeVisitor& visitor) {
234   // It is sufficient to find the set of chain starts in the target index
235   // (i.e., one vertex per connected component of edges) that are contained by
236   // the query index, except for one special case to handle full polygons.
237   //
238   // TODO(ericv): Do this by merge-joining the two S2ShapeIndexes, and share
239   // the code with S2BooleanOperation.
240   for (S2Shape* shape : *index_) {
241     if (shape == nullptr) continue;
242     int num_chains = shape->num_chains();
243     // Shapes that don't have any edges require a special case (below).
244     bool tested_point = false;
245     for (int c = 0; c < num_chains; ++c) {
246       S2Shape::Chain chain = shape->chain(c);
247       if (chain.length == 0) continue;
248       tested_point = true;
249       S2MaxDistancePointTarget target(shape->chain_edge(c, 0).v0);
250       if (!target.VisitContainingShapes(query_index, visitor)) {
251         return false;
252       }
253     }
254     if (!tested_point) {
255       // Special case to handle full polygons.
256       S2Shape::ReferencePoint ref = shape->GetReferencePoint();
257       if (!ref.contained) continue;
258       S2MaxDistancePointTarget target(ref.point);
259       if (!target.VisitContainingShapes(query_index, visitor)) {
260         return false;
261       }
262     }
263   }
264   return true;
265 }
266