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