@sentryware/s2-node 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.circleci/config.yml +45 -0
- package/.dockerignore +1 -0
- package/.gitmodules +3 -0
- package/CHANGELOG.md +33 -0
- package/LICENSE +201 -0
- package/README.md +147 -0
- package/binding.gyp +170 -0
- package/docker/Dockerfile.node20.test +8 -0
- package/docker/Dockerfile.node22.test +8 -0
- package/docker/Dockerfile.node24.test +8 -0
- package/index.d.ts +117 -0
- package/index.js +6 -0
- package/jest.config.js +184 -0
- package/package.json +43 -0
- package/publish-linux.sh +18 -0
- package/publish-osx.sh +19 -0
- package/src/builder.cc +84 -0
- package/src/builder.h +29 -0
- package/src/cell.cc +71 -0
- package/src/cell.h +26 -0
- package/src/cell_id.cc +210 -0
- package/src/cell_id.h +44 -0
- package/src/cell_union.cc +237 -0
- package/src/cell_union.h +34 -0
- package/src/earth.cc +185 -0
- package/src/earth.h +33 -0
- package/src/latlng.cc +132 -0
- package/src/latlng.h +28 -0
- package/src/loop.cc +51 -0
- package/src/loop.h +21 -0
- package/src/point.cc +69 -0
- package/src/point.h +23 -0
- package/src/polygon.cc +36 -0
- package/src/polygon.h +20 -0
- package/src/polyline.cc +186 -0
- package/src/polyline.h +34 -0
- package/src/region_coverer.cc +450 -0
- package/src/region_coverer.h +56 -0
- package/src/s2.cc +27 -0
- package/test/Cell.test.js +37 -0
- package/test/CellId.test.js +135 -0
- package/test/CellUnion.test.js +150 -0
- package/test/Earth.test.js +62 -0
- package/test/LatLng.test.js +45 -0
- package/test/Point.test.js +14 -0
- package/test/Polyline.test.js +78 -0
- package/test/RegionCoverer.test.js +301 -0
- package/test.sh +16 -0
- package/third_party/s2geometry/.travis.yml +163 -0
- package/third_party/s2geometry/AUTHORS +13 -0
- package/third_party/s2geometry/CONTRIBUTING.md +65 -0
- package/third_party/s2geometry/CONTRIBUTORS +30 -0
- package/third_party/s2geometry/LICENSE +202 -0
- package/third_party/s2geometry/NOTICE +5 -0
- package/third_party/s2geometry/README.md +127 -0
- package/third_party/s2geometry/doc/examples/point_index.cc +44 -0
- package/third_party/s2geometry/doc/examples/term_index.cc +99 -0
- package/third_party/s2geometry/doc/examples/term_index.py +101 -0
- package/third_party/s2geometry/src/python/coder.i +125 -0
- package/third_party/s2geometry/src/python/pywraps2_test.py +786 -0
- package/third_party/s2geometry/src/python/s2.i +37 -0
- package/third_party/s2geometry/src/python/s2_common.i +756 -0
- package/third_party/s2geometry/src/s2/_fp_contract_off.h +60 -0
- package/third_party/s2geometry/src/s2/base/casts.h +318 -0
- package/third_party/s2geometry/src/s2/base/commandlineflags.h +67 -0
- package/third_party/s2geometry/src/s2/base/integral_types.h +31 -0
- package/third_party/s2geometry/src/s2/base/log_severity.h +40 -0
- package/third_party/s2geometry/src/s2/base/logging.h +173 -0
- package/third_party/s2geometry/src/s2/base/mutex.h +61 -0
- package/third_party/s2geometry/src/s2/base/port.h +999 -0
- package/third_party/s2geometry/src/s2/base/spinlock.h +60 -0
- package/third_party/s2geometry/src/s2/base/stringprintf.cc +107 -0
- package/third_party/s2geometry/src/s2/base/stringprintf.h +53 -0
- package/third_party/s2geometry/src/s2/base/strtoint.cc +65 -0
- package/third_party/s2geometry/src/s2/base/strtoint.h +106 -0
- package/third_party/s2geometry/src/s2/base/timer.h +50 -0
- package/third_party/s2geometry/src/s2/encoded_s2cell_id_vector.cc +164 -0
- package/third_party/s2geometry/src/s2/encoded_s2cell_id_vector.h +110 -0
- package/third_party/s2geometry/src/s2/encoded_s2cell_id_vector_test.cc +232 -0
- package/third_party/s2geometry/src/s2/encoded_s2point_vector.cc +838 -0
- package/third_party/s2geometry/src/s2/encoded_s2point_vector.h +140 -0
- package/third_party/s2geometry/src/s2/encoded_s2point_vector_test.cc +344 -0
- package/third_party/s2geometry/src/s2/encoded_s2shape_index.cc +181 -0
- package/third_party/s2geometry/src/s2/encoded_s2shape_index.h +276 -0
- package/third_party/s2geometry/src/s2/encoded_s2shape_index_test.cc +244 -0
- package/third_party/s2geometry/src/s2/encoded_string_vector.cc +66 -0
- package/third_party/s2geometry/src/s2/encoded_string_vector.h +164 -0
- package/third_party/s2geometry/src/s2/encoded_string_vector_test.cc +69 -0
- package/third_party/s2geometry/src/s2/encoded_uint_vector.h +299 -0
- package/third_party/s2geometry/src/s2/encoded_uint_vector_test.cc +124 -0
- package/third_party/s2geometry/src/s2/id_set_lexicon.cc +81 -0
- package/third_party/s2geometry/src/s2/id_set_lexicon.h +199 -0
- package/third_party/s2geometry/src/s2/id_set_lexicon_test.cc +70 -0
- package/third_party/s2geometry/src/s2/mutable_s2shape_index.cc +1585 -0
- package/third_party/s2geometry/src/s2/mutable_s2shape_index.h +600 -0
- package/third_party/s2geometry/src/s2/mutable_s2shape_index_test.cc +589 -0
- package/third_party/s2geometry/src/s2/r1interval.h +220 -0
- package/third_party/s2geometry/src/s2/r1interval_test.cc +185 -0
- package/third_party/s2geometry/src/s2/r2.h +26 -0
- package/third_party/s2geometry/src/s2/r2rect.cc +93 -0
- package/third_party/s2geometry/src/s2/r2rect.h +234 -0
- package/third_party/s2geometry/src/s2/r2rect_test.cc +228 -0
- package/third_party/s2geometry/src/s2/s1angle.cc +54 -0
- package/third_party/s2geometry/src/s2/s1angle.h +336 -0
- package/third_party/s2geometry/src/s2/s1angle_test.cc +185 -0
- package/third_party/s2geometry/src/s2/s1chord_angle.cc +159 -0
- package/third_party/s2geometry/src/s2/s1chord_angle.h +369 -0
- package/third_party/s2geometry/src/s2/s1chord_angle_test.cc +207 -0
- package/third_party/s2geometry/src/s2/s1interval.cc +296 -0
- package/third_party/s2geometry/src/s2/s1interval.h +266 -0
- package/third_party/s2geometry/src/s2/s1interval_test.cc +469 -0
- package/third_party/s2geometry/src/s2/s2boolean_operation.cc +2391 -0
- package/third_party/s2geometry/src/s2/s2boolean_operation.h +501 -0
- package/third_party/s2geometry/src/s2/s2boolean_operation_test.cc +1400 -0
- package/third_party/s2geometry/src/s2/s2builder.cc +1828 -0
- package/third_party/s2geometry/src/s2/s2builder.h +1057 -0
- package/third_party/s2geometry/src/s2/s2builder_graph.cc +1084 -0
- package/third_party/s2geometry/src/s2/s2builder_graph.h +799 -0
- package/third_party/s2geometry/src/s2/s2builder_graph_test.cc +462 -0
- package/third_party/s2geometry/src/s2/s2builder_layer.h +50 -0
- package/third_party/s2geometry/src/s2/s2builder_test.cc +1329 -0
- package/third_party/s2geometry/src/s2/s2builderutil_closed_set_normalizer.cc +313 -0
- package/third_party/s2geometry/src/s2/s2builderutil_closed_set_normalizer.h +221 -0
- package/third_party/s2geometry/src/s2/s2builderutil_closed_set_normalizer_test.cc +261 -0
- package/third_party/s2geometry/src/s2/s2builderutil_find_polygon_degeneracies.cc +392 -0
- package/third_party/s2geometry/src/s2/s2builderutil_find_polygon_degeneracies.h +86 -0
- package/third_party/s2geometry/src/s2/s2builderutil_find_polygon_degeneracies_test.cc +182 -0
- package/third_party/s2geometry/src/s2/s2builderutil_graph_shape.h +57 -0
- package/third_party/s2geometry/src/s2/s2builderutil_lax_polygon_layer.cc +212 -0
- package/third_party/s2geometry/src/s2/s2builderutil_lax_polygon_layer.h +218 -0
- package/third_party/s2geometry/src/s2/s2builderutil_lax_polygon_layer_test.cc +367 -0
- package/third_party/s2geometry/src/s2/s2builderutil_s2point_vector_layer.cc +74 -0
- package/third_party/s2geometry/src/s2/s2builderutil_s2point_vector_layer.h +122 -0
- package/third_party/s2geometry/src/s2/s2builderutil_s2point_vector_layer_test.cc +167 -0
- package/third_party/s2geometry/src/s2/s2builderutil_s2polygon_layer.cc +191 -0
- package/third_party/s2geometry/src/s2/s2builderutil_s2polygon_layer.h +211 -0
- package/third_party/s2geometry/src/s2/s2builderutil_s2polygon_layer_test.cc +312 -0
- package/third_party/s2geometry/src/s2/s2builderutil_s2polyline_layer.cc +105 -0
- package/third_party/s2geometry/src/s2/s2builderutil_s2polyline_layer.h +174 -0
- package/third_party/s2geometry/src/s2/s2builderutil_s2polyline_layer_test.cc +220 -0
- package/third_party/s2geometry/src/s2/s2builderutil_s2polyline_vector_layer.cc +98 -0
- package/third_party/s2geometry/src/s2/s2builderutil_s2polyline_vector_layer.h +292 -0
- package/third_party/s2geometry/src/s2/s2builderutil_s2polyline_vector_layer_test.cc +233 -0
- package/third_party/s2geometry/src/s2/s2builderutil_snap_functions.cc +354 -0
- package/third_party/s2geometry/src/s2/s2builderutil_snap_functions.h +239 -0
- package/third_party/s2geometry/src/s2/s2builderutil_snap_functions_test.cc +716 -0
- package/third_party/s2geometry/src/s2/s2builderutil_testing.cc +37 -0
- package/third_party/s2geometry/src/s2/s2builderutil_testing.h +100 -0
- package/third_party/s2geometry/src/s2/s2builderutil_testing_test.cc +85 -0
- package/third_party/s2geometry/src/s2/s2cap.cc +347 -0
- package/third_party/s2geometry/src/s2/s2cap.h +286 -0
- package/third_party/s2geometry/src/s2/s2cap_test.cc +379 -0
- package/third_party/s2geometry/src/s2/s2cell.cc +552 -0
- package/third_party/s2geometry/src/s2/s2cell.h +249 -0
- package/third_party/s2geometry/src/s2/s2cell_id.cc +619 -0
- package/third_party/s2geometry/src/s2/s2cell_id.h +705 -0
- package/third_party/s2geometry/src/s2/s2cell_id_test.cc +633 -0
- package/third_party/s2geometry/src/s2/s2cell_index.cc +149 -0
- package/third_party/s2geometry/src/s2/s2cell_index.h +660 -0
- package/third_party/s2geometry/src/s2/s2cell_index_test.cc +411 -0
- package/third_party/s2geometry/src/s2/s2cell_test.cc +687 -0
- package/third_party/s2geometry/src/s2/s2cell_union.cc +515 -0
- package/third_party/s2geometry/src/s2/s2cell_union.h +399 -0
- package/third_party/s2geometry/src/s2/s2cell_union_test.cc +598 -0
- package/third_party/s2geometry/src/s2/s2centroids.cc +84 -0
- package/third_party/s2geometry/src/s2/s2centroids.h +87 -0
- package/third_party/s2geometry/src/s2/s2centroids_test.cc +82 -0
- package/third_party/s2geometry/src/s2/s2closest_cell_query.cc +123 -0
- package/third_party/s2geometry/src/s2/s2closest_cell_query.h +385 -0
- package/third_party/s2geometry/src/s2/s2closest_cell_query_base.h +841 -0
- package/third_party/s2geometry/src/s2/s2closest_cell_query_base_test.cc +63 -0
- package/third_party/s2geometry/src/s2/s2closest_cell_query_test.cc +412 -0
- package/third_party/s2geometry/src/s2/s2closest_edge_query.cc +106 -0
- package/third_party/s2geometry/src/s2/s2closest_edge_query.h +421 -0
- package/third_party/s2geometry/src/s2/s2closest_edge_query_base.h +946 -0
- package/third_party/s2geometry/src/s2/s2closest_edge_query_base_test.cc +59 -0
- package/third_party/s2geometry/src/s2/s2closest_edge_query_test.cc +505 -0
- package/third_party/s2geometry/src/s2/s2closest_edge_query_testing.h +91 -0
- package/third_party/s2geometry/src/s2/s2closest_point_query.cc +66 -0
- package/third_party/s2geometry/src/s2/s2closest_point_query.h +465 -0
- package/third_party/s2geometry/src/s2/s2closest_point_query_base.h +767 -0
- package/third_party/s2geometry/src/s2/s2closest_point_query_base_test.cc +63 -0
- package/third_party/s2geometry/src/s2/s2closest_point_query_test.cc +312 -0
- package/third_party/s2geometry/src/s2/s2contains_point_query.h +328 -0
- package/third_party/s2geometry/src/s2/s2contains_point_query_test.cc +159 -0
- package/third_party/s2geometry/src/s2/s2contains_vertex_query.cc +39 -0
- package/third_party/s2geometry/src/s2/s2contains_vertex_query.h +66 -0
- package/third_party/s2geometry/src/s2/s2contains_vertex_query_test.cc +67 -0
- package/third_party/s2geometry/src/s2/s2convex_hull_query.cc +198 -0
- package/third_party/s2geometry/src/s2/s2convex_hull_query.h +110 -0
- package/third_party/s2geometry/src/s2/s2convex_hull_query_test.cc +208 -0
- package/third_party/s2geometry/src/s2/s2coords.cc +146 -0
- package/third_party/s2geometry/src/s2/s2coords.h +459 -0
- package/third_party/s2geometry/src/s2/s2coords_internal.h +71 -0
- package/third_party/s2geometry/src/s2/s2coords_test.cc +218 -0
- package/third_party/s2geometry/src/s2/s2crossing_edge_query.cc +380 -0
- package/third_party/s2geometry/src/s2/s2crossing_edge_query.h +220 -0
- package/third_party/s2geometry/src/s2/s2crossing_edge_query_test.cc +382 -0
- package/third_party/s2geometry/src/s2/s2debug.cc +23 -0
- package/third_party/s2geometry/src/s2/s2debug.h +69 -0
- package/third_party/s2geometry/src/s2/s2distance_target.h +165 -0
- package/third_party/s2geometry/src/s2/s2earth.cc +52 -0
- package/third_party/s2geometry/src/s2/s2earth.h +268 -0
- package/third_party/s2geometry/src/s2/s2earth_test.cc +146 -0
- package/third_party/s2geometry/src/s2/s2edge_clipping.cc +462 -0
- package/third_party/s2geometry/src/s2/s2edge_clipping.h +183 -0
- package/third_party/s2geometry/src/s2/s2edge_clipping_test.cc +335 -0
- package/third_party/s2geometry/src/s2/s2edge_crosser.cc +85 -0
- package/third_party/s2geometry/src/s2/s2edge_crosser.h +343 -0
- package/third_party/s2geometry/src/s2/s2edge_crosser_test.cc +264 -0
- package/third_party/s2geometry/src/s2/s2edge_crossings.cc +515 -0
- package/third_party/s2geometry/src/s2/s2edge_crossings.h +138 -0
- package/third_party/s2geometry/src/s2/s2edge_crossings_internal.h +59 -0
- package/third_party/s2geometry/src/s2/s2edge_crossings_test.cc +246 -0
- package/third_party/s2geometry/src/s2/s2edge_distances.cc +419 -0
- package/third_party/s2geometry/src/s2/s2edge_distances.h +192 -0
- package/third_party/s2geometry/src/s2/s2edge_distances_test.cc +539 -0
- package/third_party/s2geometry/src/s2/s2edge_tessellator.cc +276 -0
- package/third_party/s2geometry/src/s2/s2edge_tessellator.h +101 -0
- package/third_party/s2geometry/src/s2/s2edge_tessellator_test.cc +492 -0
- package/third_party/s2geometry/src/s2/s2edge_vector_shape.h +85 -0
- package/third_party/s2geometry/src/s2/s2edge_vector_shape_test.cc +66 -0
- package/third_party/s2geometry/src/s2/s2error.cc +29 -0
- package/third_party/s2geometry/src/s2/s2error.h +147 -0
- package/third_party/s2geometry/src/s2/s2error_test.cc +31 -0
- package/third_party/s2geometry/src/s2/s2furthest_edge_query.cc +117 -0
- package/third_party/s2geometry/src/s2/s2furthest_edge_query.h +439 -0
- package/third_party/s2geometry/src/s2/s2furthest_edge_query_test.cc +487 -0
- package/third_party/s2geometry/src/s2/s2latlng.cc +90 -0
- package/third_party/s2geometry/src/s2/s2latlng.h +234 -0
- package/third_party/s2geometry/src/s2/s2latlng_rect.cc +727 -0
- package/third_party/s2geometry/src/s2/s2latlng_rect.h +434 -0
- package/third_party/s2geometry/src/s2/s2latlng_rect_bounder.cc +344 -0
- package/third_party/s2geometry/src/s2/s2latlng_rect_bounder.h +89 -0
- package/third_party/s2geometry/src/s2/s2latlng_rect_bounder_test.cc +306 -0
- package/third_party/s2geometry/src/s2/s2latlng_rect_test.cc +1030 -0
- package/third_party/s2geometry/src/s2/s2latlng_test.cc +165 -0
- package/third_party/s2geometry/src/s2/s2lax_loop_shape.cc +104 -0
- package/third_party/s2geometry/src/s2/s2lax_loop_shape.h +153 -0
- package/third_party/s2geometry/src/s2/s2lax_loop_shape_test.cc +101 -0
- package/third_party/s2geometry/src/s2/s2lax_polygon_shape.cc +348 -0
- package/third_party/s2geometry/src/s2/s2lax_polygon_shape.h +183 -0
- package/third_party/s2geometry/src/s2/s2lax_polygon_shape_test.cc +234 -0
- package/third_party/s2geometry/src/s2/s2lax_polyline_shape.cc +118 -0
- package/third_party/s2geometry/src/s2/s2lax_polyline_shape.h +124 -0
- package/third_party/s2geometry/src/s2/s2lax_polyline_shape_test.cc +62 -0
- package/third_party/s2geometry/src/s2/s2loop.cc +1509 -0
- package/third_party/s2geometry/src/s2/s2loop.h +711 -0
- package/third_party/s2geometry/src/s2/s2loop_measures.cc +313 -0
- package/third_party/s2geometry/src/s2/s2loop_measures.h +280 -0
- package/third_party/s2geometry/src/s2/s2loop_measures_test.cc +367 -0
- package/third_party/s2geometry/src/s2/s2loop_test.cc +1371 -0
- package/third_party/s2geometry/src/s2/s2max_distance_targets.cc +265 -0
- package/third_party/s2geometry/src/s2/s2max_distance_targets.h +241 -0
- package/third_party/s2geometry/src/s2/s2max_distance_targets_test.cc +367 -0
- package/third_party/s2geometry/src/s2/s2measures.cc +128 -0
- package/third_party/s2geometry/src/s2/s2measures.h +78 -0
- package/third_party/s2geometry/src/s2/s2measures_test.cc +135 -0
- package/third_party/s2geometry/src/s2/s2metrics.cc +122 -0
- package/third_party/s2geometry/src/s2/s2metrics.h +199 -0
- package/third_party/s2geometry/src/s2/s2metrics_test.cc +127 -0
- package/third_party/s2geometry/src/s2/s2min_distance_targets.cc +295 -0
- package/third_party/s2geometry/src/s2/s2min_distance_targets.h +273 -0
- package/third_party/s2geometry/src/s2/s2min_distance_targets_test.cc +239 -0
- package/third_party/s2geometry/src/s2/s2padded_cell.cc +162 -0
- package/third_party/s2geometry/src/s2/s2padded_cell.h +108 -0
- package/third_party/s2geometry/src/s2/s2padded_cell_test.cc +138 -0
- package/third_party/s2geometry/src/s2/s2point.h +38 -0
- package/third_party/s2geometry/src/s2/s2point_compression.cc +388 -0
- package/third_party/s2geometry/src/s2/s2point_compression.h +78 -0
- package/third_party/s2geometry/src/s2/s2point_compression_test.cc +305 -0
- package/third_party/s2geometry/src/s2/s2point_index.h +345 -0
- package/third_party/s2geometry/src/s2/s2point_index_test.cc +147 -0
- package/third_party/s2geometry/src/s2/s2point_region.cc +72 -0
- package/third_party/s2geometry/src/s2/s2point_region.h +76 -0
- package/third_party/s2geometry/src/s2/s2point_region_test.cc +100 -0
- package/third_party/s2geometry/src/s2/s2point_span.h +57 -0
- package/third_party/s2geometry/src/s2/s2point_test.cc +47 -0
- package/third_party/s2geometry/src/s2/s2point_vector_shape.h +127 -0
- package/third_party/s2geometry/src/s2/s2point_vector_shape_test.cc +59 -0
- package/third_party/s2geometry/src/s2/s2pointutil.cc +131 -0
- package/third_party/s2geometry/src/s2/s2pointutil.h +138 -0
- package/third_party/s2geometry/src/s2/s2pointutil_test.cc +157 -0
- package/third_party/s2geometry/src/s2/s2polygon.cc +1569 -0
- package/third_party/s2geometry/src/s2/s2polygon.h +934 -0
- package/third_party/s2geometry/src/s2/s2polygon_test.cc +3025 -0
- package/third_party/s2geometry/src/s2/s2polyline.cc +645 -0
- package/third_party/s2geometry/src/s2/s2polyline.h +379 -0
- package/third_party/s2geometry/src/s2/s2polyline_alignment.cc +414 -0
- package/third_party/s2geometry/src/s2/s2polyline_alignment.h +245 -0
- package/third_party/s2geometry/src/s2/s2polyline_alignment_internal.h +158 -0
- package/third_party/s2geometry/src/s2/s2polyline_alignment_test.cc +610 -0
- package/third_party/s2geometry/src/s2/s2polyline_measures.cc +42 -0
- package/third_party/s2geometry/src/s2/s2polyline_measures.h +53 -0
- package/third_party/s2geometry/src/s2/s2polyline_measures_test.cc +57 -0
- package/third_party/s2geometry/src/s2/s2polyline_simplifier.cc +187 -0
- package/third_party/s2geometry/src/s2/s2polyline_simplifier.h +109 -0
- package/third_party/s2geometry/src/s2/s2polyline_simplifier_test.cc +165 -0
- package/third_party/s2geometry/src/s2/s2polyline_test.cc +554 -0
- package/third_party/s2geometry/src/s2/s2predicates.cc +1486 -0
- package/third_party/s2geometry/src/s2/s2predicates.h +282 -0
- package/third_party/s2geometry/src/s2/s2predicates_internal.h +135 -0
- package/third_party/s2geometry/src/s2/s2predicates_test.cc +1427 -0
- package/third_party/s2geometry/src/s2/s2projections.cc +109 -0
- package/third_party/s2geometry/src/s2/s2projections.h +161 -0
- package/third_party/s2geometry/src/s2/s2projections_test.cc +78 -0
- package/third_party/s2geometry/src/s2/s2r2rect.cc +88 -0
- package/third_party/s2geometry/src/s2/s2r2rect.h +292 -0
- package/third_party/s2geometry/src/s2/s2r2rect_test.cc +312 -0
- package/third_party/s2geometry/src/s2/s2region.cc +26 -0
- package/third_party/s2geometry/src/s2/s2region.h +142 -0
- package/third_party/s2geometry/src/s2/s2region_coverer.cc +514 -0
- package/third_party/s2geometry/src/s2/s2region_coverer.h +356 -0
- package/third_party/s2geometry/src/s2/s2region_coverer_test.cc +509 -0
- package/third_party/s2geometry/src/s2/s2region_intersection.cc +84 -0
- package/third_party/s2geometry/src/s2/s2region_intersection.h +79 -0
- package/third_party/s2geometry/src/s2/s2region_term_indexer.cc +270 -0
- package/third_party/s2geometry/src/s2/s2region_term_indexer.h +299 -0
- package/third_party/s2geometry/src/s2/s2region_term_indexer_test.cc +209 -0
- package/third_party/s2geometry/src/s2/s2region_test.cc +370 -0
- package/third_party/s2geometry/src/s2/s2region_union.cc +90 -0
- package/third_party/s2geometry/src/s2/s2region_union.h +83 -0
- package/third_party/s2geometry/src/s2/s2region_union_test.cc +89 -0
- package/third_party/s2geometry/src/s2/s2shape.h +283 -0
- package/third_party/s2geometry/src/s2/s2shape_index.cc +321 -0
- package/third_party/s2geometry/src/s2/s2shape_index.h +781 -0
- package/third_party/s2geometry/src/s2/s2shape_index_buffered_region.cc +113 -0
- package/third_party/s2geometry/src/s2/s2shape_index_buffered_region.h +135 -0
- package/third_party/s2geometry/src/s2/s2shape_index_buffered_region_test.cc +162 -0
- package/third_party/s2geometry/src/s2/s2shape_index_measures.cc +92 -0
- package/third_party/s2geometry/src/s2/s2shape_index_measures.h +100 -0
- package/third_party/s2geometry/src/s2/s2shape_index_measures_test.cc +136 -0
- package/third_party/s2geometry/src/s2/s2shape_index_region.h +350 -0
- package/third_party/s2geometry/src/s2/s2shape_index_region_test.cc +161 -0
- package/third_party/s2geometry/src/s2/s2shape_index_test.cc +24 -0
- package/third_party/s2geometry/src/s2/s2shape_measures.cc +138 -0
- package/third_party/s2geometry/src/s2/s2shape_measures.h +95 -0
- package/third_party/s2geometry/src/s2/s2shape_measures_test.cc +139 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_build_polygon_boundaries.cc +120 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_build_polygon_boundaries.h +66 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_build_polygon_boundaries_test.cc +170 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_coding.cc +253 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_coding.h +283 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_coding_test.cc +54 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_contains_brute_force.cc +40 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_contains_brute_force.h +41 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_contains_brute_force_test.cc +55 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_count_edges.h +57 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_count_edges_test.cc +43 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_edge_iterator.cc +45 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_edge_iterator.h +72 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_edge_iterator_test.cc +116 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_get_reference_point.cc +107 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_get_reference_point.h +48 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_get_reference_point_test.cc +104 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_range_iterator.cc +58 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_range_iterator.h +65 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_range_iterator_test.cc +61 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_shape_edge.h +58 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_shape_edge_id.h +97 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_testing.cc +104 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_testing.h +36 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_visit_crossing_edge_pairs.cc +440 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_visit_crossing_edge_pairs.h +72 -0
- package/third_party/s2geometry/src/s2/s2shapeutil_visit_crossing_edge_pairs_test.cc +184 -0
- package/third_party/s2geometry/src/s2/s2testing.cc +464 -0
- package/third_party/s2geometry/src/s2/s2testing.h +385 -0
- package/third_party/s2geometry/src/s2/s2testing_test.cc +166 -0
- package/third_party/s2geometry/src/s2/s2text_format.cc +506 -0
- package/third_party/s2geometry/src/s2/s2text_format.h +289 -0
- package/third_party/s2geometry/src/s2/s2text_format_test.cc +417 -0
- package/third_party/s2geometry/src/s2/s2wedge_relations.cc +80 -0
- package/third_party/s2geometry/src/s2/s2wedge_relations.h +64 -0
- package/third_party/s2geometry/src/s2/s2wedge_relations_test.cc +89 -0
- package/third_party/s2geometry/src/s2/sequence_lexicon.h +296 -0
- package/third_party/s2geometry/src/s2/sequence_lexicon_test.cc +113 -0
- package/third_party/s2geometry/src/s2/strings/ostringstream.cc +35 -0
- package/third_party/s2geometry/src/s2/strings/ostringstream.h +105 -0
- package/third_party/s2geometry/src/s2/strings/serialize.cc +46 -0
- package/third_party/s2geometry/src/s2/strings/serialize.h +40 -0
- package/third_party/s2geometry/src/s2/third_party/absl/algorithm/algorithm.h +187 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/attributes.h +666 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/casts.h +189 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/config.h +462 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/dynamic_annotations.cc +129 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/dynamic_annotations.h +394 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/internal/atomic_hook.h +168 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/internal/identity.h +33 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/internal/inline_variable.h +117 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/internal/invoke.h +188 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/internal/raw_logging.cc +254 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/internal/raw_logging.h +205 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/internal/throw_delegate.cc +106 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/internal/throw_delegate.h +71 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/internal/unaligned_access.h +322 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/log_severity.h +77 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/macros.h +236 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/optimization.h +177 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/policy_checks.h +124 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/port.h +97 -0
- package/third_party/s2geometry/src/s2/third_party/absl/base/thread_annotations.h +277 -0
- package/third_party/s2geometry/src/s2/third_party/absl/container/fixed_array.h +523 -0
- package/third_party/s2geometry/src/s2/third_party/absl/container/inlined_vector.h +1453 -0
- package/third_party/s2geometry/src/s2/third_party/absl/container/internal/compressed_tuple.h +191 -0
- package/third_party/s2geometry/src/s2/third_party/absl/container/internal/container_memory.h +424 -0
- package/third_party/s2geometry/src/s2/third_party/absl/container/internal/layout.h +739 -0
- package/third_party/s2geometry/src/s2/third_party/absl/memory/memory.h +755 -0
- package/third_party/s2geometry/src/s2/third_party/absl/meta/type_traits.h +436 -0
- package/third_party/s2geometry/src/s2/third_party/absl/numeric/int128.cc +232 -0
- package/third_party/s2geometry/src/s2/third_party/absl/numeric/int128.h +656 -0
- package/third_party/s2geometry/src/s2/third_party/absl/numeric/int128_have_intrinsic.inc +3 -0
- package/third_party/s2geometry/src/s2/third_party/absl/numeric/int128_no_intrinsic.inc +3 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/ascii.cc +198 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/ascii.h +239 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/ascii_ctype.h +66 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/internal/bits.h +53 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/internal/memutil.cc +110 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/internal/memutil.h +146 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/internal/resize_uninitialized.h +72 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/match.cc +38 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/match.h +89 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/numbers.cc +909 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/numbers.h +187 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/str_cat.cc +240 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/str_cat.h +398 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/str_join.h +22 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/str_split.cc +47 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/str_split.h +43 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/string_view.cc +245 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/string_view.h +602 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/strip.cc +42 -0
- package/third_party/s2geometry/src/s2/third_party/absl/strings/strip.h +130 -0
- package/third_party/s2geometry/src/s2/third_party/absl/types/span.h +793 -0
- package/third_party/s2geometry/src/s2/third_party/absl/utility/utility.h +299 -0
- package/third_party/s2geometry/src/s2/util/bits/bit-interleave.cc +274 -0
- package/third_party/s2geometry/src/s2/util/bits/bit-interleave.h +53 -0
- package/third_party/s2geometry/src/s2/util/bits/bits.cc +155 -0
- package/third_party/s2geometry/src/s2/util/bits/bits.h +745 -0
- package/third_party/s2geometry/src/s2/util/coding/coder.cc +83 -0
- package/third_party/s2geometry/src/s2/util/coding/coder.h +553 -0
- package/third_party/s2geometry/src/s2/util/coding/nth-derivative.h +134 -0
- package/third_party/s2geometry/src/s2/util/coding/transforms.h +62 -0
- package/third_party/s2geometry/src/s2/util/coding/varint.cc +289 -0
- package/third_party/s2geometry/src/s2/util/coding/varint.h +476 -0
- package/third_party/s2geometry/src/s2/util/endian/endian.h +859 -0
- package/third_party/s2geometry/src/s2/util/gtl/btree.h +2471 -0
- package/third_party/s2geometry/src/s2/util/gtl/btree_container.h +411 -0
- package/third_party/s2geometry/src/s2/util/gtl/btree_map.h +79 -0
- package/third_party/s2geometry/src/s2/util/gtl/btree_set.h +73 -0
- package/third_party/s2geometry/src/s2/util/gtl/compact_array.h +653 -0
- package/third_party/s2geometry/src/s2/util/gtl/container_logging.h +291 -0
- package/third_party/s2geometry/src/s2/util/gtl/dense_hash_set.h +358 -0
- package/third_party/s2geometry/src/s2/util/gtl/densehashtable.h +1493 -0
- package/third_party/s2geometry/src/s2/util/gtl/hashtable_common.h +253 -0
- package/third_party/s2geometry/src/s2/util/gtl/layout.h +28 -0
- package/third_party/s2geometry/src/s2/util/gtl/legacy_random_shuffle.h +77 -0
- package/third_party/s2geometry/src/s2/util/hash/mix.h +76 -0
- package/third_party/s2geometry/src/s2/util/math/exactfloat/exactfloat.cc +832 -0
- package/third_party/s2geometry/src/s2/util/math/exactfloat/exactfloat.h +646 -0
- package/third_party/s2geometry/src/s2/util/math/mathutil.cc +75 -0
- package/third_party/s2geometry/src/s2/util/math/mathutil.h +189 -0
- package/third_party/s2geometry/src/s2/util/math/matrix3x3.h +574 -0
- package/third_party/s2geometry/src/s2/util/math/vector.h +569 -0
- package/third_party/s2geometry/src/s2/util/math/vector3_hash.h +54 -0
- package/third_party/s2geometry/src/s2/util/units/length-units.cc +21 -0
- package/third_party/s2geometry/src/s2/util/units/length-units.h +135 -0
- package/third_party/s2geometry/src/s2/util/units/physical-units.h +313 -0
- package/third_party/s2geometry/src/s2/value_lexicon.h +234 -0
- package/third_party/s2geometry/src/s2/value_lexicon_test.cc +121 -0
- package/third_party/s2geometry/third_party/cmake/FindGFlags.cmake +48 -0
- package/third_party/s2geometry/third_party/cmake/FindGlog.cmake +48 -0
|
@@ -0,0 +1,1371 @@
|
|
|
1
|
+
// Copyright 2005 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
|
+
#include "s2/s2loop.h"
|
|
19
|
+
|
|
20
|
+
#include <algorithm>
|
|
21
|
+
#include <cmath>
|
|
22
|
+
#include <cstdio>
|
|
23
|
+
#include <map>
|
|
24
|
+
#include <memory>
|
|
25
|
+
#include <set>
|
|
26
|
+
#include <string>
|
|
27
|
+
#include <utility>
|
|
28
|
+
#include <vector>
|
|
29
|
+
|
|
30
|
+
#include "s2/base/commandlineflags.h"
|
|
31
|
+
#include "s2/base/logging.h"
|
|
32
|
+
#include <gtest/gtest.h>
|
|
33
|
+
|
|
34
|
+
#include "s2/util/coding/coder.h"
|
|
35
|
+
#include "s2/r1interval.h"
|
|
36
|
+
#include "s2/s1angle.h"
|
|
37
|
+
#include "s2/s1interval.h"
|
|
38
|
+
#include "s2/s2cell.h"
|
|
39
|
+
#include "s2/s2cell_id.h"
|
|
40
|
+
#include "s2/s2debug.h"
|
|
41
|
+
#include "s2/s2edge_crossings.h"
|
|
42
|
+
#include "s2/s2edge_distances.h"
|
|
43
|
+
#include "s2/s2error.h"
|
|
44
|
+
#include "s2/s2latlng.h"
|
|
45
|
+
#include "s2/s2latlng_rect_bounder.h"
|
|
46
|
+
#include "s2/s2measures.h"
|
|
47
|
+
#include "s2/s2point_compression.h"
|
|
48
|
+
#include "s2/s2pointutil.h"
|
|
49
|
+
#include "s2/s2predicates.h"
|
|
50
|
+
#include "s2/s2testing.h"
|
|
51
|
+
#include "s2/s2text_format.h"
|
|
52
|
+
#include "s2/third_party/absl/container/fixed_array.h"
|
|
53
|
+
#include "s2/third_party/absl/memory/memory.h"
|
|
54
|
+
#include "s2/util/math/matrix3x3.h"
|
|
55
|
+
#include "s2/util/math/vector.h"
|
|
56
|
+
|
|
57
|
+
using absl::make_unique;
|
|
58
|
+
using std::fabs;
|
|
59
|
+
using std::map;
|
|
60
|
+
using std::max;
|
|
61
|
+
using std::min;
|
|
62
|
+
using std::set;
|
|
63
|
+
using std::unique_ptr;
|
|
64
|
+
using std::vector;
|
|
65
|
+
|
|
66
|
+
class S2LoopTestBase : public testing::Test {
|
|
67
|
+
protected:
|
|
68
|
+
// The set of all loops declared below.
|
|
69
|
+
vector<const S2Loop*> all_loops;
|
|
70
|
+
|
|
71
|
+
// Some standard loops to use in the tests (see descriptions below).
|
|
72
|
+
const unique_ptr<const S2Loop> empty_;
|
|
73
|
+
const unique_ptr<const S2Loop> full_;
|
|
74
|
+
const unique_ptr<const S2Loop> north_hemi_;
|
|
75
|
+
const unique_ptr<const S2Loop> north_hemi3_;
|
|
76
|
+
const unique_ptr<const S2Loop> south_hemi_;
|
|
77
|
+
const unique_ptr<const S2Loop> west_hemi_;
|
|
78
|
+
const unique_ptr<const S2Loop> east_hemi_;
|
|
79
|
+
const unique_ptr<const S2Loop> near_hemi_;
|
|
80
|
+
const unique_ptr<const S2Loop> far_hemi_;
|
|
81
|
+
const unique_ptr<const S2Loop> candy_cane_;
|
|
82
|
+
const unique_ptr<const S2Loop> small_ne_cw_;
|
|
83
|
+
const unique_ptr<const S2Loop> arctic_80_;
|
|
84
|
+
const unique_ptr<const S2Loop> antarctic_80_;
|
|
85
|
+
const unique_ptr<const S2Loop> line_triangle_;
|
|
86
|
+
const unique_ptr<const S2Loop> skinny_chevron_;
|
|
87
|
+
const unique_ptr<const S2Loop> loop_a_;
|
|
88
|
+
const unique_ptr<const S2Loop> loop_b_;
|
|
89
|
+
const unique_ptr<const S2Loop> a_intersect_b_;
|
|
90
|
+
const unique_ptr<const S2Loop> a_union_b_;
|
|
91
|
+
const unique_ptr<const S2Loop> a_minus_b_;
|
|
92
|
+
const unique_ptr<const S2Loop> b_minus_a_;
|
|
93
|
+
const unique_ptr<const S2Loop> loop_c_;
|
|
94
|
+
const unique_ptr<const S2Loop> loop_d_;
|
|
95
|
+
const unique_ptr<const S2Loop> loop_e_;
|
|
96
|
+
const unique_ptr<const S2Loop> loop_f_;
|
|
97
|
+
const unique_ptr<const S2Loop> loop_g_;
|
|
98
|
+
const unique_ptr<const S2Loop> loop_h_;
|
|
99
|
+
const unique_ptr<const S2Loop> loop_i_;
|
|
100
|
+
unique_ptr<const S2Loop> snapped_loop_a_;
|
|
101
|
+
|
|
102
|
+
private:
|
|
103
|
+
unique_ptr<const S2Loop> AddLoop(const string& str) {
|
|
104
|
+
return AddLoop(s2textformat::MakeLoop(str));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
unique_ptr<const S2Loop> AddLoop(std::unique_ptr<const S2Loop> loop) {
|
|
108
|
+
all_loops.push_back(&*loop);
|
|
109
|
+
return loop;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public:
|
|
113
|
+
S2LoopTestBase()
|
|
114
|
+
// The empty loop.
|
|
115
|
+
: empty_(AddLoop(make_unique<S2Loop>(S2Loop::kEmpty()))),
|
|
116
|
+
|
|
117
|
+
// The full loop.
|
|
118
|
+
full_(AddLoop(make_unique<S2Loop>(S2Loop::kFull()))),
|
|
119
|
+
|
|
120
|
+
// The northern hemisphere, defined using two pairs of antipodal points.
|
|
121
|
+
north_hemi_(AddLoop("0:-180, 0:-90, 0:0, 0:90")),
|
|
122
|
+
|
|
123
|
+
// The northern hemisphere, defined using three points 120 degrees apart.
|
|
124
|
+
north_hemi3_(AddLoop("0:-180, 0:-60, 0:60")),
|
|
125
|
+
|
|
126
|
+
// The southern hemisphere, defined using two pairs of antipodal points.
|
|
127
|
+
south_hemi_(AddLoop("0:90, 0:0, 0:-90, 0:-180")),
|
|
128
|
+
|
|
129
|
+
// The western hemisphere, defined using two pairs of antipodal points.
|
|
130
|
+
west_hemi_(AddLoop("0:-180, -90:0, 0:0, 90:0")),
|
|
131
|
+
|
|
132
|
+
// The eastern hemisphere, defined using two pairs of antipodal points.
|
|
133
|
+
east_hemi_(AddLoop("90:0, 0:0, -90:0, 0:-180")),
|
|
134
|
+
|
|
135
|
+
// The "near" hemisphere, defined using two pairs of antipodal points.
|
|
136
|
+
near_hemi_(AddLoop("0:-90, -90:0, 0:90, 90:0")),
|
|
137
|
+
|
|
138
|
+
// The "far" hemisphere, defined using two pairs of antipodal points.
|
|
139
|
+
far_hemi_(AddLoop("90:0, 0:90, -90:0, 0:-90")),
|
|
140
|
+
|
|
141
|
+
// A spiral stripe that slightly over-wraps the equator.
|
|
142
|
+
candy_cane_(AddLoop("-20:150, -20:-70, 0:70, 10:-150, 10:70, -10:-70")),
|
|
143
|
+
|
|
144
|
+
// A small clockwise loop in the northern & eastern hemisperes.
|
|
145
|
+
small_ne_cw_(AddLoop("35:20, 45:20, 40:25")),
|
|
146
|
+
|
|
147
|
+
// Loop around the north pole at 80 degrees.
|
|
148
|
+
arctic_80_(AddLoop("80:-150, 80:-30, 80:90")),
|
|
149
|
+
|
|
150
|
+
// Loop around the south pole at 80 degrees.
|
|
151
|
+
antarctic_80_(AddLoop("-80:120, -80:0, -80:-120")),
|
|
152
|
+
|
|
153
|
+
// A completely degenerate triangle along the equator that Sign()
|
|
154
|
+
// considers to be CCW.
|
|
155
|
+
line_triangle_(AddLoop("0:1, 0:2, 0:3")),
|
|
156
|
+
|
|
157
|
+
// A nearly-degenerate CCW chevron near the equator with very long sides
|
|
158
|
+
// (about 80 degrees). Its area is less than 1e-640, which is too small
|
|
159
|
+
// to represent in double precision.
|
|
160
|
+
skinny_chevron_(AddLoop("0:0, -1e-320:80, 0:1e-320, 1e-320:80")),
|
|
161
|
+
|
|
162
|
+
// A diamond-shaped loop around the point 0:180.
|
|
163
|
+
loop_a_(AddLoop("0:178, -1:180, 0:-179, 1:-180")),
|
|
164
|
+
|
|
165
|
+
// Another diamond-shaped loop around the point 0:180.
|
|
166
|
+
loop_b_(AddLoop("0:179, -1:180, 0:-178, 1:-180")),
|
|
167
|
+
|
|
168
|
+
// The intersection of A and B.
|
|
169
|
+
a_intersect_b_(AddLoop("0:179, -1:180, 0:-179, 1:-180")),
|
|
170
|
+
|
|
171
|
+
// The union of A and B.
|
|
172
|
+
a_union_b_(AddLoop("0:178, -1:180, 0:-178, 1:-180")),
|
|
173
|
+
|
|
174
|
+
// A minus B (concave).
|
|
175
|
+
a_minus_b_(AddLoop("0:178, -1:180, 0:179, 1:-180")),
|
|
176
|
+
|
|
177
|
+
// B minus A (concave).
|
|
178
|
+
b_minus_a_(AddLoop("0:-179, -1:180, 0:-178, 1:-180")),
|
|
179
|
+
|
|
180
|
+
// A shape gotten from A by adding a triangle to one edge, and
|
|
181
|
+
// subtracting a triangle from the opposite edge.
|
|
182
|
+
loop_c_(AddLoop("0:178, 0:180, -1:180, 0:-179, 1:-179, 1:-180")),
|
|
183
|
+
|
|
184
|
+
// A shape gotten from A by adding a triangle to one edge, and
|
|
185
|
+
// adding another triangle to the opposite edge.
|
|
186
|
+
loop_d_(AddLoop("0:178, -1:178, -1:180, 0:-179, 1:-179, 1:-180")),
|
|
187
|
+
|
|
188
|
+
// 3------------2
|
|
189
|
+
// | | ^
|
|
190
|
+
// | 7-8 b-c | |
|
|
191
|
+
// | | | | | | Latitude |
|
|
192
|
+
// 0--6-9--a-d--1 |
|
|
193
|
+
// | | | | |
|
|
194
|
+
// | f-e | +----------->
|
|
195
|
+
// | | Longitude
|
|
196
|
+
// 4------------5
|
|
197
|
+
//
|
|
198
|
+
// Important: It is not okay to skip over collinear vertices when
|
|
199
|
+
// defining these loops (e.g. to define loop E as "0,1,2,3") because S2
|
|
200
|
+
// uses symbolic perturbations to ensure that no three vertices are
|
|
201
|
+
// *ever* considered collinear (e.g., vertices 0, 6, 9 are not
|
|
202
|
+
// collinear). In other words, it is unpredictable (modulo knowing the
|
|
203
|
+
// details of the symbolic perturbations) whether 0123 contains 06123,
|
|
204
|
+
// for example.
|
|
205
|
+
//
|
|
206
|
+
// Loop E: 0,6,9,a,d,1,2,3
|
|
207
|
+
// Loop F: 0,4,5,1,d,a,9,6
|
|
208
|
+
// Loop G: 0,6,7,8,9,a,b,c,d,1,2,3
|
|
209
|
+
// Loop H: 0,6,f,e,9,a,b,c,d,1,2,3
|
|
210
|
+
// Loop I: 7,6,f,e,9,8
|
|
211
|
+
loop_e_(AddLoop("0:30, 0:34, 0:36, 0:39, 0:41, 0:44, 30:44, 30:30")),
|
|
212
|
+
loop_f_(AddLoop("0:30, -30:30, -30:44, 0:44, 0:41, 0:39, 0:36, 0:34")),
|
|
213
|
+
loop_g_(AddLoop("0:30, 0:34, 10:34, 10:36, 0:36, 0:39, 10:39, "
|
|
214
|
+
"10:41, 0:41, 0:44, 30:44, 30:30")),
|
|
215
|
+
loop_h_(AddLoop("0:30, 0:34, -10:34, -10:36, 0:36, 0:39, "
|
|
216
|
+
"10:39, 10:41, 0:41, 0:44, 30:44, 30:30")),
|
|
217
|
+
loop_i_(AddLoop("10:34, 0:34, -10:34, -10:36, 0:36, 10:36")) {
|
|
218
|
+
// Like loop_a, but the vertices are at leaf cell centers.
|
|
219
|
+
vector<S2Point> snapped_loop_a_vertices = {
|
|
220
|
+
S2CellId(s2textformat::MakePoint("0:178")).ToPoint(),
|
|
221
|
+
S2CellId(s2textformat::MakePoint("-1:180")).ToPoint(),
|
|
222
|
+
S2CellId(s2textformat::MakePoint("0:-179")).ToPoint(),
|
|
223
|
+
S2CellId(s2textformat::MakePoint("1:-180")).ToPoint()};
|
|
224
|
+
snapped_loop_a_ = AddLoop(make_unique<S2Loop>(snapped_loop_a_vertices));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Wrapper function that encodes "loop" into "encoder" using the private
|
|
228
|
+
// EncodeCompressed() method.
|
|
229
|
+
void TestEncodeCompressed(const S2Loop& loop, int level, Encoder* encoder) {
|
|
230
|
+
absl::FixedArray<S2XYZFaceSiTi> points(loop.num_vertices());
|
|
231
|
+
loop.GetXYZFaceSiTiVertices(points.data());
|
|
232
|
+
loop.EncodeCompressed(encoder, points.data(), level);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Wrapper function that decodes the contents of "encoder" into "loop" using
|
|
236
|
+
// the private DecodeCompressed() method.
|
|
237
|
+
void TestDecodeCompressed(const Encoder& encoder, int level, S2Loop* loop) {
|
|
238
|
+
Decoder decoder(encoder.base(), encoder.length());
|
|
239
|
+
ASSERT_TRUE(loop->DecodeCompressed(&decoder, level));
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
static const S2LatLng kRectError = S2LatLngRectBounder::MaxErrorForTests();
|
|
244
|
+
|
|
245
|
+
TEST_F(S2LoopTestBase, GetRectBound) {
|
|
246
|
+
EXPECT_TRUE(empty_->GetRectBound().is_empty());
|
|
247
|
+
EXPECT_TRUE(full_->GetRectBound().is_full());
|
|
248
|
+
EXPECT_TRUE(candy_cane_->GetRectBound().lng().is_full());
|
|
249
|
+
EXPECT_LT(candy_cane_->GetRectBound().lat_lo().degrees(), -20);
|
|
250
|
+
EXPECT_GT(candy_cane_->GetRectBound().lat_hi().degrees(), 10);
|
|
251
|
+
EXPECT_TRUE(small_ne_cw_->GetRectBound().is_full());
|
|
252
|
+
EXPECT_TRUE(arctic_80_->GetRectBound().ApproxEquals(
|
|
253
|
+
S2LatLngRect(S2LatLng::FromDegrees(80, -180),
|
|
254
|
+
S2LatLng::FromDegrees(90, 180)), kRectError));
|
|
255
|
+
EXPECT_TRUE(antarctic_80_->GetRectBound().ApproxEquals(
|
|
256
|
+
S2LatLngRect(S2LatLng::FromDegrees(-90, -180),
|
|
257
|
+
S2LatLng::FromDegrees(-80, 180)), kRectError));
|
|
258
|
+
|
|
259
|
+
// Create a loop that contains the complement of the "arctic_80" loop.
|
|
260
|
+
unique_ptr<S2Loop> arctic_80_inv(arctic_80_->Clone());
|
|
261
|
+
arctic_80_inv->Invert();
|
|
262
|
+
// The highest latitude of each edge is attained at its midpoint.
|
|
263
|
+
S2Point mid = 0.5 * (arctic_80_inv->vertex(0) + arctic_80_inv->vertex(1));
|
|
264
|
+
EXPECT_NEAR(arctic_80_inv->GetRectBound().lat_hi().radians(),
|
|
265
|
+
S2LatLng(mid).lat().radians(), kRectError.lat().radians());
|
|
266
|
+
|
|
267
|
+
EXPECT_TRUE(south_hemi_->GetRectBound().lng().is_full());
|
|
268
|
+
EXPECT_TRUE(south_hemi_->GetRectBound().lat().ApproxEquals(
|
|
269
|
+
R1Interval(-M_PI_2, 0), kRectError.lat().radians()));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
static void Rotate(unique_ptr<S2Loop>* ptr) {
|
|
273
|
+
S2Loop* loop = ptr->get();
|
|
274
|
+
vector<S2Point> vertices;
|
|
275
|
+
for (int i = 1; i <= loop->num_vertices(); ++i) {
|
|
276
|
+
vertices.push_back(loop->vertex(i));
|
|
277
|
+
}
|
|
278
|
+
ptr->reset(new S2Loop(vertices));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
TEST_F(S2LoopTestBase, AreaConsistentWithCurvature) {
|
|
282
|
+
// Check that the area computed using GetArea() is consistent with the
|
|
283
|
+
// curvature of the loop computed using GetTurnAngle(). According to
|
|
284
|
+
// the Gauss-Bonnet theorem, the area of the loop should be equal to 2*Pi
|
|
285
|
+
// minus its curvature.
|
|
286
|
+
for (const S2Loop* loop : all_loops) {
|
|
287
|
+
double area = loop->GetArea();
|
|
288
|
+
double gauss_area = 2 * M_PI - loop->GetCurvature();
|
|
289
|
+
// The error bound is sufficient for current tests but not guaranteed.
|
|
290
|
+
EXPECT_LE(fabs(area - gauss_area), 1e-14)
|
|
291
|
+
<< "Failed loop: " << s2textformat::ToString(*loop)
|
|
292
|
+
<< "\nArea = " << area << ", Gauss Area = " << gauss_area;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
TEST_F(S2LoopTestBase, GetAreaConsistentWithSign) {
|
|
297
|
+
// Test that GetArea() returns an area near 0 for degenerate loops that
|
|
298
|
+
// contain almost no points, and an area near 4*Pi for degenerate loops that
|
|
299
|
+
// contain almost all points.
|
|
300
|
+
|
|
301
|
+
S2Testing::Random* rnd = &S2Testing::rnd;
|
|
302
|
+
static const int kMaxVertices = 6;
|
|
303
|
+
for (int i = 0; i < 50; ++i) {
|
|
304
|
+
int num_vertices = 3 + rnd->Uniform(kMaxVertices - 3 + 1);
|
|
305
|
+
// Repeatedly choose N vertices that are exactly on the equator until we
|
|
306
|
+
// find some that form a valid loop.
|
|
307
|
+
S2Loop loop;
|
|
308
|
+
loop.set_s2debug_override(S2Debug::DISABLE);
|
|
309
|
+
do {
|
|
310
|
+
vector<S2Point> vertices;
|
|
311
|
+
for (int i = 0; i < num_vertices; ++i) {
|
|
312
|
+
// We limit longitude to the range [0, 90] to ensure that the loop is
|
|
313
|
+
// degenerate (as opposed to following the entire equator).
|
|
314
|
+
vertices.push_back(
|
|
315
|
+
S2LatLng::FromRadians(0, rnd->RandDouble() * M_PI_2).ToPoint());
|
|
316
|
+
}
|
|
317
|
+
loop.Init(vertices);
|
|
318
|
+
} while (!loop.IsValid());
|
|
319
|
+
bool ccw = loop.IsNormalized();
|
|
320
|
+
EXPECT_NEAR(ccw ? 0 : 4 * M_PI, loop.GetArea(), 1e-15)
|
|
321
|
+
<< "Failed loop " << i << ": " << s2textformat::ToString(loop);
|
|
322
|
+
EXPECT_EQ(!ccw, loop.Contains(S2Point(0, 0, 1)));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
TEST_F(S2LoopTestBase, GetAreaAccuracy) {
|
|
327
|
+
// TODO(ericv): Test that GetArea() has an accuracy significantly better
|
|
328
|
+
// than 1e-15 on loops whose area is small.
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
TEST_F(S2LoopTestBase, GetAreaAndCentroid) {
|
|
332
|
+
EXPECT_EQ(0.0, empty_->GetArea());
|
|
333
|
+
EXPECT_EQ(4 * M_PI, full_->GetArea());
|
|
334
|
+
EXPECT_EQ(S2Point(0, 0, 0), empty_->GetCentroid());
|
|
335
|
+
EXPECT_EQ(S2Point(0, 0, 0), full_->GetCentroid());
|
|
336
|
+
|
|
337
|
+
EXPECT_DOUBLE_EQ(north_hemi_->GetArea(), 2 * M_PI);
|
|
338
|
+
EXPECT_NEAR(east_hemi_->GetArea(), 2 * M_PI, 1e-15);
|
|
339
|
+
|
|
340
|
+
// Construct spherical caps of random height, and approximate their boundary
|
|
341
|
+
// with closely spaces vertices. Then check that the area and centroid are
|
|
342
|
+
// correct.
|
|
343
|
+
|
|
344
|
+
for (int i = 0; i < 50; ++i) {
|
|
345
|
+
// Choose a coordinate frame for the spherical cap.
|
|
346
|
+
Vector3_d x, y, z;
|
|
347
|
+
S2Testing::GetRandomFrame(&x, &y, &z);
|
|
348
|
+
|
|
349
|
+
// Given two points at latitude phi and whose longitudes differ by dtheta,
|
|
350
|
+
// the geodesic between the two points has a maximum latitude of
|
|
351
|
+
// atan(tan(phi) / cos(dtheta/2)). This can be derived by positioning
|
|
352
|
+
// the two points at (-dtheta/2, phi) and (dtheta/2, phi).
|
|
353
|
+
//
|
|
354
|
+
// We want to position the vertices close enough together so that their
|
|
355
|
+
// maximum distance from the boundary of the spherical cap is kMaxDist.
|
|
356
|
+
// Thus we want fabs(atan(tan(phi) / cos(dtheta/2)) - phi) <= kMaxDist.
|
|
357
|
+
static const double kMaxDist = 1e-6;
|
|
358
|
+
double height = 2 * S2Testing::rnd.RandDouble();
|
|
359
|
+
double phi = asin(1 - height);
|
|
360
|
+
double max_dtheta = 2 * acos(tan(fabs(phi)) / tan(fabs(phi) + kMaxDist));
|
|
361
|
+
max_dtheta = min(M_PI, max_dtheta); // At least 3 vertices.
|
|
362
|
+
|
|
363
|
+
vector<S2Point> vertices;
|
|
364
|
+
for (double theta = 0; theta < 2 * M_PI;
|
|
365
|
+
theta += S2Testing::rnd.RandDouble() * max_dtheta) {
|
|
366
|
+
vertices.push_back(cos(theta) * cos(phi) * x +
|
|
367
|
+
sin(theta) * cos(phi) * y +
|
|
368
|
+
sin(phi) * z);
|
|
369
|
+
}
|
|
370
|
+
S2Loop loop(vertices);
|
|
371
|
+
double area = loop.GetArea();
|
|
372
|
+
S2Point centroid = loop.GetCentroid();
|
|
373
|
+
double expected_area = 2 * M_PI * height;
|
|
374
|
+
EXPECT_LE(fabs(area - expected_area), 2 * M_PI * kMaxDist);
|
|
375
|
+
S2Point expected_centroid = expected_area * (1 - 0.5 * height) * z;
|
|
376
|
+
EXPECT_LE((centroid - expected_centroid).Norm(), 2 * kMaxDist);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Check that the curvature is *identical* when the vertex order is
|
|
381
|
+
// rotated, and that the sign is inverted when the vertices are reversed.
|
|
382
|
+
static void CheckCurvatureInvariants(const S2Loop& loop) {
|
|
383
|
+
double expected = loop.GetCurvature();
|
|
384
|
+
unique_ptr<S2Loop> loop_copy(loop.Clone());
|
|
385
|
+
for (int i = 0; i < loop.num_vertices(); ++i) {
|
|
386
|
+
loop_copy->Invert();
|
|
387
|
+
EXPECT_EQ(-expected, loop_copy->GetCurvature());
|
|
388
|
+
loop_copy->Invert();
|
|
389
|
+
Rotate(&loop_copy);
|
|
390
|
+
EXPECT_EQ(expected, loop_copy->GetCurvature());
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
TEST_F(S2LoopTestBase, GetCurvature) {
|
|
395
|
+
EXPECT_EQ(2 * M_PI, empty_->GetCurvature());
|
|
396
|
+
EXPECT_EQ(-2 * M_PI, full_->GetCurvature());
|
|
397
|
+
|
|
398
|
+
EXPECT_NEAR(0, north_hemi3_->GetCurvature(), 1e-15);
|
|
399
|
+
CheckCurvatureInvariants(*north_hemi3_);
|
|
400
|
+
|
|
401
|
+
EXPECT_NEAR(0, west_hemi_->GetCurvature(), 1e-15);
|
|
402
|
+
CheckCurvatureInvariants(*west_hemi_);
|
|
403
|
+
|
|
404
|
+
// We don't have an easy way to estimate the curvature of this loop, but
|
|
405
|
+
// we can still check that the expected invariants hold.
|
|
406
|
+
CheckCurvatureInvariants(*candy_cane_);
|
|
407
|
+
|
|
408
|
+
EXPECT_DOUBLE_EQ(2 * M_PI, line_triangle_->GetCurvature());
|
|
409
|
+
CheckCurvatureInvariants(*line_triangle_);
|
|
410
|
+
|
|
411
|
+
EXPECT_DOUBLE_EQ(2 * M_PI, skinny_chevron_->GetCurvature());
|
|
412
|
+
CheckCurvatureInvariants(*skinny_chevron_);
|
|
413
|
+
|
|
414
|
+
// Build a narrow spiral loop starting at the north pole. This is designed
|
|
415
|
+
// to test that the error in GetCurvature is linear in the number of
|
|
416
|
+
// vertices even when the partial sum of the curvatures gets very large.
|
|
417
|
+
// The spiral consists of two "arms" defining opposite sides of the loop.
|
|
418
|
+
const int kArmPoints = 10000; // Number of vertices in each "arm"
|
|
419
|
+
const double kArmRadius = 0.01; // Radius of spiral.
|
|
420
|
+
vector<S2Point> vertices(2 * kArmPoints);
|
|
421
|
+
vertices[kArmPoints] = S2Point(0, 0, 1);
|
|
422
|
+
for (int i = 0; i < kArmPoints; ++i) {
|
|
423
|
+
double angle = (2 * M_PI / 3) * i;
|
|
424
|
+
double x = cos(angle);
|
|
425
|
+
double y = sin(angle);
|
|
426
|
+
double r1 = i * kArmRadius / kArmPoints;
|
|
427
|
+
double r2 = (i + 1.5) * kArmRadius / kArmPoints;
|
|
428
|
+
vertices[kArmPoints - i - 1] = S2Point(r1 * x, r1 * y, 1).Normalize();
|
|
429
|
+
vertices[kArmPoints + i] = S2Point(r2 * x, r2 * y, 1).Normalize();
|
|
430
|
+
}
|
|
431
|
+
// This is a pathological loop that contains many long parallel edges, and
|
|
432
|
+
// takes tens of seconds to validate in debug mode.
|
|
433
|
+
S2Loop spiral(vertices, S2Debug::DISABLE);
|
|
434
|
+
|
|
435
|
+
// Check that GetCurvature() is consistent with GetArea() to within the
|
|
436
|
+
// error bound of the former. We actually use a tiny fraction of the
|
|
437
|
+
// worst-case error bound, since the worst case only happens when all the
|
|
438
|
+
// roundoff errors happen in the same direction and this test is not
|
|
439
|
+
// designed to achieve that. The error in GetArea() can be ignored for the
|
|
440
|
+
// purposes of this test since it is generally much smaller.
|
|
441
|
+
EXPECT_NEAR(2 * M_PI - spiral.GetArea(), spiral.GetCurvature(),
|
|
442
|
+
0.01 * spiral.GetCurvatureMaxError());
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Checks that if a loop is normalized, it doesn't contain a
|
|
446
|
+
// point outside of it, and vice versa.
|
|
447
|
+
static void CheckNormalizeAndContains(const S2Loop& loop) {
|
|
448
|
+
S2Point p = s2textformat::MakePoint("40:40");
|
|
449
|
+
|
|
450
|
+
unique_ptr<S2Loop> flip(loop.Clone());
|
|
451
|
+
flip->Invert();
|
|
452
|
+
EXPECT_TRUE(loop.IsNormalized() ^ loop.Contains(p));
|
|
453
|
+
EXPECT_TRUE(flip->IsNormalized() ^ flip->Contains(p));
|
|
454
|
+
|
|
455
|
+
EXPECT_TRUE(loop.IsNormalized() ^ flip->IsNormalized());
|
|
456
|
+
|
|
457
|
+
flip->Normalize();
|
|
458
|
+
EXPECT_FALSE(flip->Contains(p));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
TEST_F(S2LoopTestBase, NormalizedCompatibleWithContains) {
|
|
462
|
+
CheckNormalizeAndContains(*line_triangle_);
|
|
463
|
+
CheckNormalizeAndContains(*skinny_chevron_);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
TEST_F(S2LoopTestBase, Contains) {
|
|
467
|
+
// Check the full and empty loops have the correct containment relationship
|
|
468
|
+
// with the special "vertex" that defines them.
|
|
469
|
+
EXPECT_FALSE(empty_->Contains(S2Loop::kEmpty()[0]));
|
|
470
|
+
EXPECT_TRUE(full_->Contains(S2Loop::kFull()[0]));
|
|
471
|
+
|
|
472
|
+
EXPECT_TRUE(candy_cane_->Contains(S2LatLng::FromDegrees(5, 71).ToPoint()));
|
|
473
|
+
|
|
474
|
+
// Create copies of these loops so that we can change the vertex order.
|
|
475
|
+
unique_ptr<S2Loop> north_copy(north_hemi_->Clone());
|
|
476
|
+
unique_ptr<S2Loop> south_copy(south_hemi_->Clone());
|
|
477
|
+
unique_ptr<S2Loop> west_copy(west_hemi_->Clone());
|
|
478
|
+
unique_ptr<S2Loop> east_copy(east_hemi_->Clone());
|
|
479
|
+
for (int i = 0; i < 4; ++i) {
|
|
480
|
+
EXPECT_TRUE(north_copy->Contains(S2Point(0, 0, 1)));
|
|
481
|
+
EXPECT_FALSE(north_copy->Contains(S2Point(0, 0, -1)));
|
|
482
|
+
EXPECT_FALSE(south_copy->Contains(S2Point(0, 0, 1)));
|
|
483
|
+
EXPECT_TRUE(south_copy->Contains(S2Point(0, 0, -1)));
|
|
484
|
+
EXPECT_FALSE(west_copy->Contains(S2Point(0, 1, 0)));
|
|
485
|
+
EXPECT_TRUE(west_copy->Contains(S2Point(0, -1, 0)));
|
|
486
|
+
EXPECT_TRUE(east_copy->Contains(S2Point(0, 1, 0)));
|
|
487
|
+
EXPECT_FALSE(east_copy->Contains(S2Point(0, -1, 0)));
|
|
488
|
+
Rotate(&north_copy);
|
|
489
|
+
Rotate(&south_copy);
|
|
490
|
+
Rotate(&east_copy);
|
|
491
|
+
Rotate(&west_copy);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// This code checks each cell vertex is contained by exactly one of
|
|
495
|
+
// the adjacent cells.
|
|
496
|
+
for (int level = 0; level < 3; ++level) {
|
|
497
|
+
vector<unique_ptr<S2Loop>> loops;
|
|
498
|
+
vector<S2Point> loop_vertices;
|
|
499
|
+
set<S2Point> points;
|
|
500
|
+
for (S2CellId id = S2CellId::Begin(level);
|
|
501
|
+
id != S2CellId::End(level); id = id.next()) {
|
|
502
|
+
S2Cell cell(id);
|
|
503
|
+
points.insert(cell.GetCenter());
|
|
504
|
+
for (int k = 0; k < 4; ++k) {
|
|
505
|
+
loop_vertices.push_back(cell.GetVertex(k));
|
|
506
|
+
points.insert(cell.GetVertex(k));
|
|
507
|
+
}
|
|
508
|
+
loops.push_back(make_unique<S2Loop>(loop_vertices));
|
|
509
|
+
loop_vertices.clear();
|
|
510
|
+
}
|
|
511
|
+
for (const S2Point& point : points) {
|
|
512
|
+
int count = 0;
|
|
513
|
+
for (const auto& loop : loops) {
|
|
514
|
+
if (loop->Contains(point)) ++count;
|
|
515
|
+
}
|
|
516
|
+
EXPECT_EQ(count, 1);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
TEST(S2Loop, ContainsMatchesCrossingSign) {
|
|
522
|
+
// This test demonstrates a former incompatibility between CrossingSign()
|
|
523
|
+
// and Contains(const S2Point&). It constructs an S2Cell-based loop L and
|
|
524
|
+
// an edge E from Origin to a0 that crosses exactly one edge of L. Yet
|
|
525
|
+
// previously, Contains() returned false for both endpoints of E.
|
|
526
|
+
//
|
|
527
|
+
// The reason for the bug was that the loop bound was sometimes too tight.
|
|
528
|
+
// The Contains() code for a0 bailed out early because a0 was found not to
|
|
529
|
+
// be inside the bound of L.
|
|
530
|
+
|
|
531
|
+
// Start with a cell that ends up producing the problem.
|
|
532
|
+
const S2CellId cell_id = S2CellId(S2Point(1, 1, 1)).parent(21);
|
|
533
|
+
|
|
534
|
+
S2Cell children[4];
|
|
535
|
+
S2Cell(cell_id).Subdivide(children);
|
|
536
|
+
|
|
537
|
+
vector<S2Point> points(4);
|
|
538
|
+
for (int i = 0; i < 4; ++i) {
|
|
539
|
+
// Note extra normalization. GetCenter() is already normalized.
|
|
540
|
+
// The test results will no longer be inconsistent if the extra
|
|
541
|
+
// Normalize() is removed.
|
|
542
|
+
points[i] = children[i].GetCenter().Normalize();
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const S2Loop loop(points);
|
|
546
|
+
|
|
547
|
+
// Get a vertex from a grandchild cell.
|
|
548
|
+
// +---------------+---------------+
|
|
549
|
+
// | | |
|
|
550
|
+
// | points[3] | points[2] |
|
|
551
|
+
// | v | v |
|
|
552
|
+
// | +-------+------ + |
|
|
553
|
+
// | | | | |
|
|
554
|
+
// | | | | |
|
|
555
|
+
// | | | | |
|
|
556
|
+
// +-------+-------+-------+-------+
|
|
557
|
+
// | | | | |
|
|
558
|
+
// | | <----------------------- grandchild_cell
|
|
559
|
+
// | | | | |
|
|
560
|
+
// | +-------+------ + |
|
|
561
|
+
// | ^ | ^ | <-- cell
|
|
562
|
+
// | points[0]/a0 | points[1] |
|
|
563
|
+
// | | |
|
|
564
|
+
// +---------------+---------------+
|
|
565
|
+
const S2Cell grandchild_cell(cell_id.child(0).child(2));
|
|
566
|
+
const S2Point a0 = grandchild_cell.GetVertex(0);
|
|
567
|
+
|
|
568
|
+
// If this doesn't hold, the rest of the test is pointless.
|
|
569
|
+
ASSERT_NE(points[0], a0)
|
|
570
|
+
<< "This test depends on rounding errors that should make "
|
|
571
|
+
"a0 slightly different from points[0]"
|
|
572
|
+
<< std::setprecision(20) << "\npoints[0]:" << points[0]
|
|
573
|
+
<< "\n a0:" << a0;
|
|
574
|
+
|
|
575
|
+
// The edge from a0 to the origin crosses one boundary.
|
|
576
|
+
EXPECT_EQ(-1, S2::CrossingSign(a0, S2::Origin(),
|
|
577
|
+
loop.vertex(0), loop.vertex(1)));
|
|
578
|
+
EXPECT_EQ(1, S2::CrossingSign(a0, S2::Origin(),
|
|
579
|
+
loop.vertex(1), loop.vertex(2)));
|
|
580
|
+
EXPECT_EQ(-1, S2::CrossingSign(a0, S2::Origin(),
|
|
581
|
+
loop.vertex(2), loop.vertex(3)));
|
|
582
|
+
EXPECT_EQ(-1, S2::CrossingSign(a0, S2::Origin(),
|
|
583
|
+
loop.vertex(3), loop.vertex(4)));
|
|
584
|
+
|
|
585
|
+
// Contains should return false for the origin, and true for a0.
|
|
586
|
+
EXPECT_FALSE(loop.Contains(S2::Origin()));
|
|
587
|
+
EXPECT_TRUE(loop.Contains(a0));
|
|
588
|
+
|
|
589
|
+
// Since a0 is inside the loop, it should be inside the bound.
|
|
590
|
+
const S2LatLngRect& bound = loop.GetRectBound();
|
|
591
|
+
EXPECT_TRUE(bound.Contains(a0));
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Given a pair of loops where A contains B, check various identities.
|
|
595
|
+
static void TestOneNestedPair(const S2Loop& a, const S2Loop& b) {
|
|
596
|
+
EXPECT_TRUE(a.Contains(&b));
|
|
597
|
+
EXPECT_EQ(a.BoundaryEquals(&b), b.Contains(&a));
|
|
598
|
+
EXPECT_EQ(!b.is_empty(), a.Intersects(&b));
|
|
599
|
+
EXPECT_EQ(!b.is_empty(), b.Intersects(&a));
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Given a pair of disjoint loops A and B, check various identities.
|
|
603
|
+
static void TestOneDisjointPair(const S2Loop& a, const S2Loop& b) {
|
|
604
|
+
EXPECT_FALSE(a.Intersects(&b));
|
|
605
|
+
EXPECT_FALSE(b.Intersects(&a));
|
|
606
|
+
EXPECT_EQ(b.is_empty(), a.Contains(&b));
|
|
607
|
+
EXPECT_EQ(a.is_empty(), b.Contains(&a));
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Given loops A and B whose union covers the sphere, check various identities.
|
|
611
|
+
static void TestOneCoveringPair(const S2Loop& a, const S2Loop& b) {
|
|
612
|
+
EXPECT_EQ(a.is_full(), a.Contains(&b));
|
|
613
|
+
EXPECT_EQ(b.is_full(), b.Contains(&a));
|
|
614
|
+
unique_ptr<S2Loop> a1(a.Clone());
|
|
615
|
+
a1->Invert();
|
|
616
|
+
bool complementary = a1->BoundaryEquals(&b);
|
|
617
|
+
EXPECT_EQ(!complementary, a.Intersects(&b));
|
|
618
|
+
EXPECT_EQ(!complementary, b.Intersects(&a));
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Given loops A and B such that both A and its complement intersect both B
|
|
622
|
+
// and its complement, check various identities.
|
|
623
|
+
static void TestOneOverlappingPair(const S2Loop& a, const S2Loop& b) {
|
|
624
|
+
EXPECT_FALSE(a.Contains(&b));
|
|
625
|
+
EXPECT_FALSE(b.Contains(&a));
|
|
626
|
+
EXPECT_TRUE(a.Intersects(&b));
|
|
627
|
+
EXPECT_TRUE(b.Intersects(&a));
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Given a pair of loops where A contains B, test various identities
|
|
631
|
+
// involving A, B, and their complements.
|
|
632
|
+
static void TestNestedPair(const S2Loop& a, const S2Loop& b) {
|
|
633
|
+
unique_ptr<S2Loop> a1(a.Clone());
|
|
634
|
+
unique_ptr<S2Loop> b1(b.Clone());
|
|
635
|
+
a1->Invert();
|
|
636
|
+
b1->Invert();
|
|
637
|
+
TestOneNestedPair(a, b);
|
|
638
|
+
TestOneNestedPair(*b1, *a1);
|
|
639
|
+
TestOneDisjointPair(*a1, b);
|
|
640
|
+
TestOneCoveringPair(a, *b1);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Given a pair of disjoint loops A and B, test various identities
|
|
644
|
+
// involving A, B, and their complements.
|
|
645
|
+
static void TestDisjointPair(const S2Loop& a, const S2Loop& b) {
|
|
646
|
+
unique_ptr<S2Loop> a1(a.Clone());
|
|
647
|
+
a1->Invert();
|
|
648
|
+
TestNestedPair(*a1, b);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Given loops A and B whose union covers the sphere, test various identities
|
|
652
|
+
// involving A, B, and their complements.
|
|
653
|
+
static void TestCoveringPair(const S2Loop& a, const S2Loop& b) {
|
|
654
|
+
unique_ptr<S2Loop> b1(b.Clone());
|
|
655
|
+
b1->Invert();
|
|
656
|
+
TestNestedPair(a, *b1);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Given loops A and B such that both A and its complement intersect both B
|
|
660
|
+
// and its complement, test various identities involving these four loops.
|
|
661
|
+
static void TestOverlappingPair(const S2Loop& a, const S2Loop& b) {
|
|
662
|
+
unique_ptr<S2Loop> a1(a.Clone());
|
|
663
|
+
unique_ptr<S2Loop> b1(b.Clone());
|
|
664
|
+
a1->Invert();
|
|
665
|
+
b1->Invert();
|
|
666
|
+
TestOneOverlappingPair(a, b);
|
|
667
|
+
TestOneOverlappingPair(*a1, *b1);
|
|
668
|
+
TestOneOverlappingPair(*a1, b);
|
|
669
|
+
TestOneOverlappingPair(a, *b1);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
enum RelationFlags {
|
|
673
|
+
CONTAINS = 0x01, // A contains B
|
|
674
|
+
CONTAINED = 0x02, // B contains A
|
|
675
|
+
DISJOINT = 0x04, // A and B are disjoint (intersection is empty)
|
|
676
|
+
COVERS = 0x08, // (A union B) covers the entire sphere
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
// Verify the relationship between two loops A and B. "flags" is the set of
|
|
680
|
+
// RelationFlags that apply. "shared_edge" means that the loops share at
|
|
681
|
+
// least one edge (possibly reversed).
|
|
682
|
+
static void TestRelationWithDesc(const S2Loop& a, const S2Loop& b,
|
|
683
|
+
int flags, bool shared_edge,
|
|
684
|
+
const char* test_description) {
|
|
685
|
+
SCOPED_TRACE(test_description);
|
|
686
|
+
if (flags & CONTAINS) {
|
|
687
|
+
TestNestedPair(a, b);
|
|
688
|
+
}
|
|
689
|
+
if (flags & CONTAINED) {
|
|
690
|
+
TestNestedPair(b, a);
|
|
691
|
+
}
|
|
692
|
+
if (flags & COVERS) {
|
|
693
|
+
TestCoveringPair(a, b);
|
|
694
|
+
}
|
|
695
|
+
if (flags & DISJOINT) {
|
|
696
|
+
TestDisjointPair(a, b);
|
|
697
|
+
} else if (!(flags & (CONTAINS|CONTAINED|COVERS))) {
|
|
698
|
+
TestOverlappingPair(a, b);
|
|
699
|
+
}
|
|
700
|
+
if (!shared_edge && (flags & (CONTAINS|CONTAINED|DISJOINT))) {
|
|
701
|
+
EXPECT_EQ(a.Contains(&b), a.ContainsNested(&b));
|
|
702
|
+
}
|
|
703
|
+
// A contains the boundary of B if either A contains B, or the two loops
|
|
704
|
+
// contain each other's boundaries and there are no shared edges (since at
|
|
705
|
+
// least one such edge must be reversed, and therefore is not considered to
|
|
706
|
+
// be contained according to the rules of CompareBoundary).
|
|
707
|
+
int comparison = 0;
|
|
708
|
+
if ((flags & CONTAINS) || ((flags & COVERS) && !shared_edge)) {
|
|
709
|
+
comparison = 1;
|
|
710
|
+
}
|
|
711
|
+
// Similarly, A excludes the boundary of B if either A and B are disjoint,
|
|
712
|
+
// or B contains A and there are no shared edges (since A is considered to
|
|
713
|
+
// contain such edges according to the rules of CompareBoundary).
|
|
714
|
+
if ((flags & DISJOINT) || ((flags & CONTAINED) && !shared_edge)) {
|
|
715
|
+
comparison = -1;
|
|
716
|
+
}
|
|
717
|
+
// CompareBoundary requires that neither loop is empty.
|
|
718
|
+
if (!a.is_empty() && !b.is_empty()) {
|
|
719
|
+
EXPECT_EQ(comparison, a.CompareBoundary(&b));
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
#define TestRelation(a, b, flags, shared_edge) \
|
|
724
|
+
TestRelationWithDesc(*a, *b, flags, shared_edge, "args " #a ", " #b)
|
|
725
|
+
|
|
726
|
+
TEST_F(S2LoopTestBase, LoopRelations) {
|
|
727
|
+
// Check full and empty relationships with normal loops and each other.
|
|
728
|
+
TestRelation(full_, full_, CONTAINS|CONTAINED|COVERS, true);
|
|
729
|
+
TestRelation(full_, north_hemi_, CONTAINS|COVERS, false);
|
|
730
|
+
TestRelation(full_, empty_, CONTAINS|DISJOINT|COVERS, false);
|
|
731
|
+
TestRelation(north_hemi_, full_, CONTAINED|COVERS, false);
|
|
732
|
+
TestRelation(north_hemi_, empty_, CONTAINS|DISJOINT, false);
|
|
733
|
+
TestRelation(empty_, full_, CONTAINED|DISJOINT|COVERS, false);
|
|
734
|
+
TestRelation(empty_, north_hemi_, CONTAINED|DISJOINT, false);
|
|
735
|
+
TestRelation(empty_, empty_, CONTAINS|CONTAINED|DISJOINT, false);
|
|
736
|
+
|
|
737
|
+
TestRelation(north_hemi_, north_hemi_, CONTAINS|CONTAINED, true);
|
|
738
|
+
TestRelation(north_hemi_, south_hemi_, DISJOINT|COVERS, true);
|
|
739
|
+
TestRelation(north_hemi_, east_hemi_, 0, false);
|
|
740
|
+
TestRelation(north_hemi_, arctic_80_, CONTAINS, false);
|
|
741
|
+
TestRelation(north_hemi_, antarctic_80_, DISJOINT, false);
|
|
742
|
+
TestRelation(north_hemi_, candy_cane_, 0, false);
|
|
743
|
+
|
|
744
|
+
// We can't compare north_hemi3 vs. north_hemi or south_hemi because the
|
|
745
|
+
// result depends on the "simulation of simplicity" implementation details.
|
|
746
|
+
TestRelation(north_hemi3_, north_hemi3_, CONTAINS|CONTAINED, true);
|
|
747
|
+
TestRelation(north_hemi3_, east_hemi_, 0, false);
|
|
748
|
+
TestRelation(north_hemi3_, arctic_80_, CONTAINS, false);
|
|
749
|
+
TestRelation(north_hemi3_, antarctic_80_, DISJOINT, false);
|
|
750
|
+
TestRelation(north_hemi3_, candy_cane_, 0, false);
|
|
751
|
+
|
|
752
|
+
TestRelation(south_hemi_, north_hemi_, DISJOINT|COVERS, true);
|
|
753
|
+
TestRelation(south_hemi_, south_hemi_, CONTAINS|CONTAINED, true);
|
|
754
|
+
TestRelation(south_hemi_, far_hemi_, 0, false);
|
|
755
|
+
TestRelation(south_hemi_, arctic_80_, DISJOINT, false);
|
|
756
|
+
TestRelation(south_hemi_, antarctic_80_, CONTAINS, false);
|
|
757
|
+
TestRelation(south_hemi_, candy_cane_, 0, false);
|
|
758
|
+
|
|
759
|
+
TestRelation(candy_cane_, north_hemi_, 0, false);
|
|
760
|
+
TestRelation(candy_cane_, south_hemi_, 0, false);
|
|
761
|
+
TestRelation(candy_cane_, arctic_80_, DISJOINT, false);
|
|
762
|
+
TestRelation(candy_cane_, antarctic_80_, DISJOINT, false);
|
|
763
|
+
TestRelation(candy_cane_, candy_cane_, CONTAINS|CONTAINED, true);
|
|
764
|
+
|
|
765
|
+
TestRelation(near_hemi_, west_hemi_, 0, false);
|
|
766
|
+
|
|
767
|
+
TestRelation(small_ne_cw_, south_hemi_, CONTAINS, false);
|
|
768
|
+
TestRelation(small_ne_cw_, west_hemi_, CONTAINS, false);
|
|
769
|
+
|
|
770
|
+
TestRelation(small_ne_cw_, north_hemi_, COVERS, false);
|
|
771
|
+
TestRelation(small_ne_cw_, east_hemi_, COVERS, false);
|
|
772
|
+
|
|
773
|
+
TestRelation(loop_a_, loop_a_, CONTAINS|CONTAINED, true);
|
|
774
|
+
TestRelation(loop_a_, loop_b_, 0, false);
|
|
775
|
+
TestRelation(loop_a_, a_intersect_b_, CONTAINS, true);
|
|
776
|
+
TestRelation(loop_a_, a_union_b_, CONTAINED, true);
|
|
777
|
+
TestRelation(loop_a_, a_minus_b_, CONTAINS, true);
|
|
778
|
+
TestRelation(loop_a_, b_minus_a_, DISJOINT, true);
|
|
779
|
+
|
|
780
|
+
TestRelation(loop_b_, loop_a_, 0, false);
|
|
781
|
+
TestRelation(loop_b_, loop_b_, CONTAINS|CONTAINED, true);
|
|
782
|
+
TestRelation(loop_b_, a_intersect_b_, CONTAINS, true);
|
|
783
|
+
TestRelation(loop_b_, a_union_b_, CONTAINED, true);
|
|
784
|
+
TestRelation(loop_b_, a_minus_b_, DISJOINT, true);
|
|
785
|
+
TestRelation(loop_b_, b_minus_a_, CONTAINS, true);
|
|
786
|
+
|
|
787
|
+
TestRelation(a_intersect_b_, loop_a_, CONTAINED, true);
|
|
788
|
+
TestRelation(a_intersect_b_, loop_b_, CONTAINED, true);
|
|
789
|
+
TestRelation(a_intersect_b_, a_intersect_b_, CONTAINS|CONTAINED, true);
|
|
790
|
+
TestRelation(a_intersect_b_, a_union_b_, CONTAINED, false);
|
|
791
|
+
TestRelation(a_intersect_b_, a_minus_b_, DISJOINT, true);
|
|
792
|
+
TestRelation(a_intersect_b_, b_minus_a_, DISJOINT, true);
|
|
793
|
+
|
|
794
|
+
TestRelation(a_union_b_, loop_a_, CONTAINS, true);
|
|
795
|
+
TestRelation(a_union_b_, loop_b_, CONTAINS, true);
|
|
796
|
+
TestRelation(a_union_b_, a_intersect_b_, CONTAINS, false);
|
|
797
|
+
TestRelation(a_union_b_, a_union_b_, CONTAINS|CONTAINED, true);
|
|
798
|
+
TestRelation(a_union_b_, a_minus_b_, CONTAINS, true);
|
|
799
|
+
TestRelation(a_union_b_, b_minus_a_, CONTAINS, true);
|
|
800
|
+
|
|
801
|
+
TestRelation(a_minus_b_, loop_a_, CONTAINED, true);
|
|
802
|
+
TestRelation(a_minus_b_, loop_b_, DISJOINT, true);
|
|
803
|
+
TestRelation(a_minus_b_, a_intersect_b_, DISJOINT, true);
|
|
804
|
+
TestRelation(a_minus_b_, a_union_b_, CONTAINED, true);
|
|
805
|
+
TestRelation(a_minus_b_, a_minus_b_, CONTAINS|CONTAINED, true);
|
|
806
|
+
TestRelation(a_minus_b_, b_minus_a_, DISJOINT, false);
|
|
807
|
+
|
|
808
|
+
TestRelation(b_minus_a_, loop_a_, DISJOINT, true);
|
|
809
|
+
TestRelation(b_minus_a_, loop_b_, CONTAINED, true);
|
|
810
|
+
TestRelation(b_minus_a_, a_intersect_b_, DISJOINT, true);
|
|
811
|
+
TestRelation(b_minus_a_, a_union_b_, CONTAINED, true);
|
|
812
|
+
TestRelation(b_minus_a_, a_minus_b_, DISJOINT, false);
|
|
813
|
+
TestRelation(b_minus_a_, b_minus_a_, CONTAINS|CONTAINED, true);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// Make sure the relations are correct if the loop crossing happens on
|
|
817
|
+
// two ends of a shared boundary segment.
|
|
818
|
+
TEST_F(S2LoopTestBase, LoopRelationsWhenSameExceptPiecesStickingOutAndIn) {
|
|
819
|
+
TestRelation(loop_a_, loop_c_, 0, true);
|
|
820
|
+
TestRelation(loop_c_, loop_a_, 0, true);
|
|
821
|
+
TestRelation(loop_a_, loop_d_, CONTAINED, true);
|
|
822
|
+
TestRelation(loop_d_, loop_a_, CONTAINS, true);
|
|
823
|
+
TestRelation(loop_e_, loop_f_, DISJOINT, true);
|
|
824
|
+
TestRelation(loop_e_, loop_g_, CONTAINS, true);
|
|
825
|
+
TestRelation(loop_e_, loop_h_, 0, true);
|
|
826
|
+
TestRelation(loop_e_, loop_i_, 0, false);
|
|
827
|
+
TestRelation(loop_f_, loop_g_, DISJOINT, true);
|
|
828
|
+
TestRelation(loop_f_, loop_h_, 0, true);
|
|
829
|
+
TestRelation(loop_f_, loop_i_, 0, false);
|
|
830
|
+
TestRelation(loop_g_, loop_h_, CONTAINED, true);
|
|
831
|
+
TestRelation(loop_h_, loop_g_, CONTAINS, true);
|
|
832
|
+
TestRelation(loop_g_, loop_i_, DISJOINT, true);
|
|
833
|
+
TestRelation(loop_h_, loop_i_, CONTAINS, true);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
#undef TestRelation
|
|
837
|
+
|
|
838
|
+
static unique_ptr<S2Loop> MakeCellLoop(S2CellId begin, S2CellId end) {
|
|
839
|
+
// Construct a CCW polygon whose boundary is the union of the cell ids
|
|
840
|
+
// in the range [begin, end). We add the edges one by one, removing
|
|
841
|
+
// any edges that are already present in the opposite direction.
|
|
842
|
+
|
|
843
|
+
map<S2Point, set<S2Point>> edges;
|
|
844
|
+
for (S2CellId id = begin; id != end; id = id.next()) {
|
|
845
|
+
S2Cell cell(id);
|
|
846
|
+
for (int k = 0; k < 4; ++k) {
|
|
847
|
+
S2Point a = cell.GetVertex(k);
|
|
848
|
+
S2Point b = cell.GetVertex(k + 1);
|
|
849
|
+
if (edges[b].erase(a) == 0) {
|
|
850
|
+
edges[a].insert(b);
|
|
851
|
+
} else if (edges[b].empty()) {
|
|
852
|
+
edges.erase(b);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// The remaining edges form a single loop. We simply follow it starting
|
|
858
|
+
// at an arbitrary vertex and build up a list of vertices.
|
|
859
|
+
|
|
860
|
+
vector<S2Point> vertices;
|
|
861
|
+
S2Point p = edges.begin()->first;
|
|
862
|
+
while (!edges.empty()) {
|
|
863
|
+
S2_DCHECK_EQ(1, edges[p].size());
|
|
864
|
+
S2Point next = *edges[p].begin();
|
|
865
|
+
vertices.push_back(p);
|
|
866
|
+
edges.erase(p);
|
|
867
|
+
p = next;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return make_unique<S2Loop>(vertices);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
TEST(S2Loop, LoopRelations2) {
|
|
874
|
+
// Construct polygons consisting of a sequence of adjacent cell ids
|
|
875
|
+
// at some fixed level. Comparing two polygons at the same level
|
|
876
|
+
// ensures that there are no T-vertices.
|
|
877
|
+
for (int iter = 0; iter < 1000; ++iter) {
|
|
878
|
+
S2Testing::Random& rnd = S2Testing::rnd;
|
|
879
|
+
S2CellId begin = S2CellId(rnd.Rand64() | 1);
|
|
880
|
+
if (!begin.is_valid()) continue;
|
|
881
|
+
begin = begin.parent(rnd.Uniform(S2CellId::kMaxLevel));
|
|
882
|
+
S2CellId a_begin = begin.advance(rnd.Skewed(6));
|
|
883
|
+
S2CellId a_end = a_begin.advance(rnd.Skewed(6) + 1);
|
|
884
|
+
S2CellId b_begin = begin.advance(rnd.Skewed(6));
|
|
885
|
+
S2CellId b_end = b_begin.advance(rnd.Skewed(6) + 1);
|
|
886
|
+
if (!a_end.is_valid() || !b_end.is_valid()) continue;
|
|
887
|
+
|
|
888
|
+
unique_ptr<S2Loop> a(MakeCellLoop(a_begin, a_end));
|
|
889
|
+
unique_ptr<S2Loop> b(MakeCellLoop(b_begin, b_end));
|
|
890
|
+
if (a.get() && b.get()) {
|
|
891
|
+
bool contained = (a_begin <= b_begin && b_end <= a_end);
|
|
892
|
+
bool intersects = (a_begin < b_end && b_begin < a_end);
|
|
893
|
+
S2_VLOG(1) << "Checking " << a->num_vertices() << " vs. "
|
|
894
|
+
<< b->num_vertices() << ", contained = " << contained
|
|
895
|
+
<< ", intersects = " << intersects;
|
|
896
|
+
EXPECT_EQ(a->Contains(b.get()), contained);
|
|
897
|
+
EXPECT_EQ(a->Intersects(b.get()), intersects);
|
|
898
|
+
} else {
|
|
899
|
+
S2_VLOG(1) << "MakeCellLoop failed to create a loop.";
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
TEST(S2Loop, BoundsForLoopContainment) {
|
|
905
|
+
// To reliably test whether one loop contains another, the bounds of the
|
|
906
|
+
// outer loop are expanded slightly. This test constructs examples where
|
|
907
|
+
// this expansion is necessary and verifies that it is sufficient.
|
|
908
|
+
|
|
909
|
+
S2Testing::Random* rnd = &S2Testing::rnd;
|
|
910
|
+
for (int iter = 0; iter < 1000; ++iter) {
|
|
911
|
+
// We construct a triangle ABC such that A,B,C are nearly colinear, B is
|
|
912
|
+
// the point of maximum latitude, and the edge AC passes very slightly
|
|
913
|
+
// below B (i.e., ABC is CCW).
|
|
914
|
+
S2Point b = (S2Testing::RandomPoint() + S2Point(0, 0, 1)).Normalize();
|
|
915
|
+
S2Point v = b.CrossProd(S2Point(0, 0, 1)).Normalize();
|
|
916
|
+
S2Point a = S2::Interpolate(rnd->RandDouble(), -v, b);
|
|
917
|
+
S2Point c = S2::Interpolate(rnd->RandDouble(), b, v);
|
|
918
|
+
if (s2pred::Sign(a, b, c) < 0) {
|
|
919
|
+
--iter; continue;
|
|
920
|
+
}
|
|
921
|
+
// Now construct another point D directly below B, and create two loops
|
|
922
|
+
// ABCD and ACD.
|
|
923
|
+
S2Point d = S2Point(b.x(), b.y(), 0).Normalize();
|
|
924
|
+
S2Point vertices[] = { c, d, a, b }; // Reordered for convenience
|
|
925
|
+
S2Loop outer(vector<S2Point>(vertices, vertices + 4));
|
|
926
|
+
S2Loop inner(vector<S2Point>(vertices, vertices + 3));
|
|
927
|
+
// Now because the bounds calculation is less accurate when the maximum is
|
|
928
|
+
// attained along an edge (rather than at a vertex), sometimes the inner
|
|
929
|
+
// loop will have a *larger* bounding box than the outer loop. We look
|
|
930
|
+
// only for those cases.
|
|
931
|
+
if (outer.GetRectBound().Contains(inner.GetRectBound())) {
|
|
932
|
+
--iter; continue;
|
|
933
|
+
}
|
|
934
|
+
EXPECT_TRUE(outer.Contains(&inner));
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
void DebugDumpCrossings(const S2Loop& loop) {
|
|
939
|
+
// This function is useful for debugging.
|
|
940
|
+
|
|
941
|
+
S2_LOG(INFO) << "Ortho(v1): " << S2::Ortho(loop.vertex(1));
|
|
942
|
+
printf("Contains(kOrigin): %d\n", loop.Contains(S2::Origin()));
|
|
943
|
+
for (int i = 1; i <= loop.num_vertices(); ++i) {
|
|
944
|
+
S2Point a = S2::Ortho(loop.vertex(i));
|
|
945
|
+
S2Point b = loop.vertex(i-1);
|
|
946
|
+
S2Point c = loop.vertex(i+1);
|
|
947
|
+
S2Point o = loop.vertex(i);
|
|
948
|
+
printf("Vertex %d: [%.17g, %.17g, %.17g], "
|
|
949
|
+
"%d%dR=%d, %d%d%d=%d, R%d%d=%d, inside: %d\n",
|
|
950
|
+
i, loop.vertex(i).x(), loop.vertex(i).y(), loop.vertex(i).z(),
|
|
951
|
+
i - 1, i, s2pred::Sign(b, o, a),
|
|
952
|
+
i + 1, i, i - 1, s2pred::Sign(c, o, b),
|
|
953
|
+
i, i + 1, s2pred::Sign(a, o, c),
|
|
954
|
+
s2pred::OrderedCCW(a, b, c, o));
|
|
955
|
+
}
|
|
956
|
+
for (int i = 0; i < loop.num_vertices() + 2; ++i) {
|
|
957
|
+
S2Point orig = S2::Origin();
|
|
958
|
+
S2Point dest;
|
|
959
|
+
if (i < loop.num_vertices()) {
|
|
960
|
+
dest = loop.vertex(i);
|
|
961
|
+
printf("Origin->%d crosses:", i);
|
|
962
|
+
} else {
|
|
963
|
+
dest = S2Point(0, 0, 1);
|
|
964
|
+
if (i == loop.num_vertices() + 1) orig = loop.vertex(1);
|
|
965
|
+
printf("Case %d:", i);
|
|
966
|
+
}
|
|
967
|
+
for (int j = 0; j < loop.num_vertices(); ++j) {
|
|
968
|
+
printf(" %d", S2::EdgeOrVertexCrossing(
|
|
969
|
+
orig, dest, loop.vertex(j), loop.vertex(j+1)));
|
|
970
|
+
}
|
|
971
|
+
printf("\n");
|
|
972
|
+
}
|
|
973
|
+
for (int i = 0; i <= 2; i += 2) {
|
|
974
|
+
printf("Origin->v1 crossing v%d->v1: ", i);
|
|
975
|
+
S2Point a = S2::Ortho(loop.vertex(1));
|
|
976
|
+
S2Point b = loop.vertex(i);
|
|
977
|
+
S2Point c = S2::Origin();
|
|
978
|
+
S2Point o = loop.vertex(1);
|
|
979
|
+
printf("%d1R=%d, M1%d=%d, R1M=%d, crosses: %d\n",
|
|
980
|
+
i, s2pred::Sign(b, o, a),
|
|
981
|
+
i, s2pred::Sign(c, o, b),
|
|
982
|
+
s2pred::Sign(a, o, c),
|
|
983
|
+
S2::EdgeOrVertexCrossing(c, o, b, a));
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
static void TestNear(const char* a_str, const char* b_str,
|
|
988
|
+
S1Angle max_error, bool expected) {
|
|
989
|
+
unique_ptr<S2Loop> a(s2textformat::MakeLoop(a_str));
|
|
990
|
+
unique_ptr<S2Loop> b(s2textformat::MakeLoop(b_str));
|
|
991
|
+
EXPECT_EQ(a->BoundaryNear(*b, max_error), expected);
|
|
992
|
+
EXPECT_EQ(b->BoundaryNear(*a, max_error), expected);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
TEST(S2Loop, BoundaryNear) {
|
|
996
|
+
S1Angle degree = S1Angle::Degrees(1);
|
|
997
|
+
|
|
998
|
+
TestNear("0:0, 0:10, 5:5",
|
|
999
|
+
"0:0.1, -0.1:9.9, 5:5.2",
|
|
1000
|
+
0.5 * degree, true);
|
|
1001
|
+
TestNear("0:0, 0:3, 0:7, 0:10, 3:7, 5:5",
|
|
1002
|
+
"0:0, 0:10, 2:8, 5:5, 4:4, 3:3, 1:1",
|
|
1003
|
+
S1Angle::Radians(1e-3), true);
|
|
1004
|
+
|
|
1005
|
+
// All vertices close to some edge, but not equivalent.
|
|
1006
|
+
TestNear("0:0, 0:2, 2:2, 2:0",
|
|
1007
|
+
"0:0, 1.9999:1, 0:2, 2:2, 2:0",
|
|
1008
|
+
0.5 * degree, false);
|
|
1009
|
+
|
|
1010
|
+
// Two triangles that backtrack a bit on different edges. A simple
|
|
1011
|
+
// greedy matching algorithm would fail on this example.
|
|
1012
|
+
const char* t1 = "0.1:0, 0.1:1, 0.1:2, 0.1:3, 0.1:4, 1:4, 2:4, 3:4, "
|
|
1013
|
+
"2:4.1, 1:4.1, 2:4.2, 3:4.2, 4:4.2, 5:4.2";
|
|
1014
|
+
const char* t2 = "0:0, 0:1, 0:2, 0:3, 0.1:2, 0.1:1, 0.2:2, 0.2:3, "
|
|
1015
|
+
"0.2:4, 1:4.1, 2:4, 3:4, 4:4, 5:4";
|
|
1016
|
+
TestNear(t1, t2, 1.5 * degree, true);
|
|
1017
|
+
TestNear(t1, t2, 0.5 * degree, false);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
static void CheckIdentical(const S2Loop& loop, const S2Loop& loop2) {
|
|
1021
|
+
EXPECT_EQ(loop.depth(), loop2.depth());
|
|
1022
|
+
EXPECT_EQ(loop.num_vertices(), loop2.num_vertices());
|
|
1023
|
+
for (int i = 0; i < loop.num_vertices(); ++i) {
|
|
1024
|
+
EXPECT_EQ(loop.vertex(i), loop2.vertex(i));
|
|
1025
|
+
}
|
|
1026
|
+
EXPECT_EQ(loop.is_empty(), loop2.is_empty());
|
|
1027
|
+
EXPECT_EQ(loop.is_full(), loop2.is_full());
|
|
1028
|
+
EXPECT_EQ(loop.depth(), loop2.depth());
|
|
1029
|
+
EXPECT_EQ(loop.IsNormalized(), loop2.IsNormalized());
|
|
1030
|
+
EXPECT_EQ(loop.Contains(S2::Origin()), loop2.Contains(S2::Origin()));
|
|
1031
|
+
EXPECT_EQ(loop.GetRectBound(), loop2.GetRectBound());
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
static void TestEncodeDecode(const S2Loop& loop) {
|
|
1035
|
+
Encoder encoder;
|
|
1036
|
+
loop.Encode(&encoder);
|
|
1037
|
+
Decoder decoder(encoder.base(), encoder.length());
|
|
1038
|
+
S2Loop loop2;
|
|
1039
|
+
loop2.set_s2debug_override(loop.s2debug_override());
|
|
1040
|
+
ASSERT_TRUE(loop2.Decode(&decoder));
|
|
1041
|
+
CheckIdentical(loop, loop2);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
TEST(S2Loop, EncodeDecode) {
|
|
1045
|
+
unique_ptr<S2Loop> l(s2textformat::MakeLoop("30:20, 40:20, 39:43, 33:35"));
|
|
1046
|
+
l->set_depth(3);
|
|
1047
|
+
TestEncodeDecode(*l);
|
|
1048
|
+
|
|
1049
|
+
S2Loop empty(S2Loop::kEmpty());
|
|
1050
|
+
TestEncodeDecode(empty);
|
|
1051
|
+
S2Loop full(S2Loop::kFull());
|
|
1052
|
+
TestEncodeDecode(full);
|
|
1053
|
+
|
|
1054
|
+
S2Loop uninitialized;
|
|
1055
|
+
TestEncodeDecode(uninitialized);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
static void TestEmptyFullSnapped(const S2Loop& loop, int level) {
|
|
1059
|
+
S2_CHECK(loop.is_empty_or_full());
|
|
1060
|
+
S2CellId cellid = S2CellId(loop.vertex(0)).parent(level);
|
|
1061
|
+
vector<S2Point> vertices = {cellid.ToPoint()};
|
|
1062
|
+
S2Loop loop2(vertices);
|
|
1063
|
+
EXPECT_TRUE(loop.BoundaryEquals(&loop2));
|
|
1064
|
+
EXPECT_TRUE(loop.BoundaryApproxEquals(loop2));
|
|
1065
|
+
EXPECT_TRUE(loop.BoundaryNear(loop2));
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// Test converting the empty/full loops to S2LatLng representations. (We
|
|
1069
|
+
// don't bother testing E5/E6/E7 because that test is less demanding.)
|
|
1070
|
+
static void TestEmptyFullLatLng(const S2Loop& loop) {
|
|
1071
|
+
S2_CHECK(loop.is_empty_or_full());
|
|
1072
|
+
vector<S2Point> vertices = {S2LatLng(loop.vertex(0)).ToPoint()};
|
|
1073
|
+
S2Loop loop2(vertices);
|
|
1074
|
+
EXPECT_TRUE(loop.BoundaryEquals(&loop2));
|
|
1075
|
+
EXPECT_TRUE(loop.BoundaryApproxEquals(loop2));
|
|
1076
|
+
EXPECT_TRUE(loop.BoundaryNear(loop2));
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
static void TestEmptyFullConversions(const S2Loop& loop) {
|
|
1080
|
+
TestEmptyFullSnapped(loop, S2CellId::kMaxLevel);
|
|
1081
|
+
TestEmptyFullSnapped(loop, 1); // Worst case for approximation
|
|
1082
|
+
TestEmptyFullSnapped(loop, 0);
|
|
1083
|
+
TestEmptyFullLatLng(loop);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
TEST(S2Loop, EmptyFullLossyConversions) {
|
|
1087
|
+
// Verify that the empty and full loops can be encoded lossily.
|
|
1088
|
+
S2Loop empty(S2Loop::kEmpty());
|
|
1089
|
+
TestEmptyFullConversions(empty);
|
|
1090
|
+
|
|
1091
|
+
S2Loop full(S2Loop::kFull());
|
|
1092
|
+
TestEmptyFullConversions(full);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
TEST(S2Loop, EncodeDecodeWithinScope) {
|
|
1096
|
+
unique_ptr<S2Loop> l(s2textformat::MakeLoop("30:20, 40:20, 39:43, 33:35"));
|
|
1097
|
+
l->set_depth(3);
|
|
1098
|
+
Encoder encoder;
|
|
1099
|
+
l->Encode(&encoder);
|
|
1100
|
+
Decoder decoder1(encoder.base(), encoder.length());
|
|
1101
|
+
|
|
1102
|
+
// Initialize the loop using DecodeWithinScope and check that it is the
|
|
1103
|
+
// same as the original loop.
|
|
1104
|
+
S2Loop loop1;
|
|
1105
|
+
ASSERT_TRUE(loop1.DecodeWithinScope(&decoder1));
|
|
1106
|
+
EXPECT_TRUE(l->BoundaryEquals(&loop1));
|
|
1107
|
+
EXPECT_EQ(l->depth(), loop1.depth());
|
|
1108
|
+
EXPECT_EQ(l->GetRectBound(), loop1.GetRectBound());
|
|
1109
|
+
|
|
1110
|
+
// Initialize the same loop using Init with a vector of vertices, and
|
|
1111
|
+
// check that it doesn't deallocate the original memory.
|
|
1112
|
+
vector<S2Point> vertices = {loop1.vertex(0), loop1.vertex(2),
|
|
1113
|
+
loop1.vertex(3)};
|
|
1114
|
+
loop1.Init(vertices);
|
|
1115
|
+
Decoder decoder2(encoder.base(), encoder.length());
|
|
1116
|
+
S2Loop loop2;
|
|
1117
|
+
ASSERT_TRUE(loop2.DecodeWithinScope(&decoder2));
|
|
1118
|
+
EXPECT_TRUE(l->BoundaryEquals(&loop2));
|
|
1119
|
+
EXPECT_EQ(l->vertex(1), loop2.vertex(1));
|
|
1120
|
+
EXPECT_NE(loop1.vertex(1), loop2.vertex(1));
|
|
1121
|
+
|
|
1122
|
+
// Initialize loop2 using Decode with a decoder on different data.
|
|
1123
|
+
// Check that the original memory is not deallocated or overwritten.
|
|
1124
|
+
unique_ptr<S2Loop> l2(s2textformat::MakeLoop("30:40, 40:75, 39:43, 80:35"));
|
|
1125
|
+
l2->set_depth(2);
|
|
1126
|
+
Encoder encoder2;
|
|
1127
|
+
l2->Encode(&encoder2);
|
|
1128
|
+
Decoder decoder3(encoder2.base(), encoder2.length());
|
|
1129
|
+
ASSERT_TRUE(loop2.Decode(&decoder3));
|
|
1130
|
+
Decoder decoder4(encoder.base(), encoder.length());
|
|
1131
|
+
ASSERT_TRUE(loop1.DecodeWithinScope(&decoder4));
|
|
1132
|
+
EXPECT_TRUE(l->BoundaryEquals(&loop1));
|
|
1133
|
+
EXPECT_EQ(l->vertex(1), loop1.vertex(1));
|
|
1134
|
+
EXPECT_NE(loop1.vertex(1), loop2.vertex(1));
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
TEST_F(S2LoopTestBase, FourVertexCompressedLoopRequires36Bytes) {
|
|
1138
|
+
Encoder encoder;
|
|
1139
|
+
TestEncodeCompressed(*snapped_loop_a_, S2CellId::kMaxLevel, &encoder);
|
|
1140
|
+
|
|
1141
|
+
// 1 byte for num_vertices
|
|
1142
|
+
// 1 byte for origin_inside and boolean indicating we did not
|
|
1143
|
+
// encode the bound
|
|
1144
|
+
// 1 byte for depth
|
|
1145
|
+
// Vertices:
|
|
1146
|
+
// 1 byte for faces
|
|
1147
|
+
// 8 bytes for each vertex.
|
|
1148
|
+
// 1 byte indicating that there is no unsnapped vertex.
|
|
1149
|
+
EXPECT_EQ(37, encoder.length());
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
TEST_F(S2LoopTestBase, CompressedEncodedLoopDecodesApproxEqual) {
|
|
1153
|
+
unique_ptr<S2Loop> loop(snapped_loop_a_->Clone());
|
|
1154
|
+
loop->set_depth(3);
|
|
1155
|
+
|
|
1156
|
+
Encoder encoder;
|
|
1157
|
+
TestEncodeCompressed(*loop, S2CellId::kMaxLevel, &encoder);
|
|
1158
|
+
S2Loop decoded_loop;
|
|
1159
|
+
TestDecodeCompressed(encoder, S2CellId::kMaxLevel, &decoded_loop);
|
|
1160
|
+
CheckIdentical(*loop, decoded_loop);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// This test checks that S2Loops created directly from S2Cells behave
|
|
1164
|
+
// identically to S2Loops created from the vertices of those cells; this
|
|
1165
|
+
// previously was not the case, because S2Cells calculate their bounding
|
|
1166
|
+
// rectangles slightly differently, and S2Loops created from them just copied
|
|
1167
|
+
// the S2Cell bounds.
|
|
1168
|
+
TEST(S2Loop, S2CellConstructorAndContains) {
|
|
1169
|
+
S2Cell cell(S2CellId(S2LatLng::FromE6(40565459, -74645276)));
|
|
1170
|
+
S2Loop cell_as_loop(cell);
|
|
1171
|
+
|
|
1172
|
+
vector<S2Point> vertices;
|
|
1173
|
+
for (int i = 0; i < cell_as_loop.num_vertices(); ++i) {
|
|
1174
|
+
vertices.push_back(cell_as_loop.vertex(i));
|
|
1175
|
+
}
|
|
1176
|
+
S2Loop loop_copy(vertices);
|
|
1177
|
+
EXPECT_TRUE(loop_copy.Contains(&cell_as_loop));
|
|
1178
|
+
EXPECT_TRUE(cell_as_loop.Contains(&loop_copy));
|
|
1179
|
+
|
|
1180
|
+
// Demonstrates the reason for this test; the cell bounds are more
|
|
1181
|
+
// conservative than the resulting loop bounds.
|
|
1182
|
+
EXPECT_FALSE(loop_copy.GetRectBound().Contains(cell.GetRectBound()));
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// Construct a loop using s2textformat::MakeLoop(str) and check that it
|
|
1186
|
+
// produces a validation error that includes "snippet".
|
|
1187
|
+
static void CheckLoopIsInvalid(const string& str, const string& snippet) {
|
|
1188
|
+
unique_ptr<S2Loop> loop(s2textformat::MakeLoop(str, S2Debug::DISABLE));
|
|
1189
|
+
S2Error error;
|
|
1190
|
+
EXPECT_TRUE(loop->FindValidationError(&error));
|
|
1191
|
+
EXPECT_NE(string::npos, error.text().find(snippet));
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
static void CheckLoopIsInvalid(const vector<S2Point>& points,
|
|
1195
|
+
const string& snippet) {
|
|
1196
|
+
S2Loop l(points, S2Debug::DISABLE);
|
|
1197
|
+
S2Error error;
|
|
1198
|
+
EXPECT_TRUE(l.FindValidationError(&error));
|
|
1199
|
+
EXPECT_NE(string::npos, error.text().find(snippet));
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
TEST(S2Loop, IsValidDetectsInvalidLoops) {
|
|
1203
|
+
// Not enough vertices. Note that all single-vertex loops are valid; they
|
|
1204
|
+
// are interpreted as being either empty or full.
|
|
1205
|
+
CheckLoopIsInvalid("", "at least 3 vertices");
|
|
1206
|
+
CheckLoopIsInvalid("20:20, 21:21", "at least 3 vertices");
|
|
1207
|
+
|
|
1208
|
+
// There is a degenerate edge
|
|
1209
|
+
CheckLoopIsInvalid("20:20, 20:20, 20:21", "degenerate");
|
|
1210
|
+
CheckLoopIsInvalid("20:20, 20:21, 20:20", "degenerate");
|
|
1211
|
+
|
|
1212
|
+
// There is a duplicate vertex
|
|
1213
|
+
CheckLoopIsInvalid("20:20, 21:21, 21:20, 20:20, 20:21", "duplicate vertex");
|
|
1214
|
+
|
|
1215
|
+
// Some edges cross
|
|
1216
|
+
CheckLoopIsInvalid("20:20, 21:21, 21:20.5, 21:20, 20:21", "crosses");
|
|
1217
|
+
|
|
1218
|
+
// Points with non-unit length (triggers S2_DCHECK failure in debug)
|
|
1219
|
+
EXPECT_DEBUG_DEATH(
|
|
1220
|
+
CheckLoopIsInvalid({S2Point(2, 0, 0), S2Point(0, 1, 0), S2Point(0, 0, 1)},
|
|
1221
|
+
"unit length"),
|
|
1222
|
+
"IsUnitLength");
|
|
1223
|
+
|
|
1224
|
+
// Adjacent antipodal vertices
|
|
1225
|
+
CheckLoopIsInvalid({S2Point(1, 0, 0), S2Point(-1, 0, 0), S2Point(0, 0, 1)},
|
|
1226
|
+
"antipodal");
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// Helper function for testing the distance methods. "boundary_x" is the
|
|
1230
|
+
// expected result of projecting "x" onto the loop boundary. For convenience
|
|
1231
|
+
// it can be set to S2Point() to indicate that (boundary_x == x).
|
|
1232
|
+
static void TestDistanceMethods(const S2Loop& loop, const S2Point& x,
|
|
1233
|
+
S2Point boundary_x) {
|
|
1234
|
+
// This error is not guaranteed by the implementation but is okay for tests.
|
|
1235
|
+
const S1Angle kMaxError = S1Angle::Radians(1e-15);
|
|
1236
|
+
|
|
1237
|
+
if (boundary_x == S2Point()) boundary_x = x;
|
|
1238
|
+
EXPECT_LE(S1Angle(boundary_x, loop.ProjectToBoundary(x)), kMaxError);
|
|
1239
|
+
|
|
1240
|
+
if (loop.is_empty_or_full()) {
|
|
1241
|
+
EXPECT_EQ(S1Angle::Infinity(), loop.GetDistanceToBoundary(x));
|
|
1242
|
+
} else {
|
|
1243
|
+
// EXPECT_NEAR only works with doubles.
|
|
1244
|
+
EXPECT_NEAR(S1Angle(x, boundary_x).degrees(),
|
|
1245
|
+
loop.GetDistanceToBoundary(x).degrees(), kMaxError.degrees());
|
|
1246
|
+
}
|
|
1247
|
+
if (loop.Contains(x)) {
|
|
1248
|
+
EXPECT_EQ(S1Angle::Zero(), loop.GetDistance(x));
|
|
1249
|
+
EXPECT_EQ(x, loop.Project(x));
|
|
1250
|
+
} else {
|
|
1251
|
+
EXPECT_EQ(loop.GetDistanceToBoundary(x), loop.GetDistance(x));
|
|
1252
|
+
EXPECT_EQ(loop.ProjectToBoundary(x), loop.Project(x));
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
TEST_F(S2LoopTestBase, DistanceMethods) {
|
|
1257
|
+
// S2ClosestEdgeQuery is already tested, so just do a bit of sanity checking.
|
|
1258
|
+
|
|
1259
|
+
// The empty and full loops don't have boundaries.
|
|
1260
|
+
TestDistanceMethods(*empty_, S2Point(0, 1, 0), S2Point());
|
|
1261
|
+
TestDistanceMethods(*full_, S2Point(0, 1, 0), S2Point());
|
|
1262
|
+
|
|
1263
|
+
// A CCW square around the S2LatLng point (0,0). Note that because lines of
|
|
1264
|
+
// latitude are curved on the sphere, it is not straightforward to project
|
|
1265
|
+
// points onto any edge except along the equator. (The equator is the only
|
|
1266
|
+
// line of latitude that is also a geodesic.)
|
|
1267
|
+
unique_ptr<S2Loop> square(s2textformat::MakeLoop("-1:-1, -1:1, 1:1, 1:-1"));
|
|
1268
|
+
EXPECT_TRUE(square->IsNormalized());
|
|
1269
|
+
|
|
1270
|
+
// A vertex.
|
|
1271
|
+
TestDistanceMethods(*square, S2LatLng::FromDegrees(1, -1).ToPoint(),
|
|
1272
|
+
S2Point());
|
|
1273
|
+
// A point on one of the edges.
|
|
1274
|
+
TestDistanceMethods(*square, S2LatLng::FromDegrees(0.5, 1).ToPoint(),
|
|
1275
|
+
S2Point());
|
|
1276
|
+
// A point inside the square.
|
|
1277
|
+
TestDistanceMethods(*square, S2LatLng::FromDegrees(0, 0.5).ToPoint(),
|
|
1278
|
+
S2LatLng::FromDegrees(0, 1).ToPoint());
|
|
1279
|
+
// A point outside the square that projects onto an edge.
|
|
1280
|
+
TestDistanceMethods(*square, S2LatLng::FromDegrees(0, -2).ToPoint(),
|
|
1281
|
+
S2LatLng::FromDegrees(0, -1).ToPoint());
|
|
1282
|
+
// A point outside the square that projects onto a vertex.
|
|
1283
|
+
TestDistanceMethods(*square, S2LatLng::FromDegrees(3, 4).ToPoint(),
|
|
1284
|
+
S2LatLng::FromDegrees(1, 1).ToPoint());
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
TEST_F(S2LoopTestBase, MakeRegularLoop) {
|
|
1288
|
+
S2Point center = S2LatLng::FromDegrees(80, 135).ToPoint();
|
|
1289
|
+
S1Angle radius = S1Angle::Degrees(20);
|
|
1290
|
+
unique_ptr<S2Loop> loop(S2Loop::MakeRegularLoop(center, radius, 4));
|
|
1291
|
+
|
|
1292
|
+
ASSERT_EQ(4, loop->num_vertices());
|
|
1293
|
+
S2Point p0 = loop->vertex(0);
|
|
1294
|
+
S2Point p1 = loop->vertex(1);
|
|
1295
|
+
S2Point p2 = loop->vertex(2);
|
|
1296
|
+
S2Point p3 = loop->vertex(3);
|
|
1297
|
+
// Make sure that the radius is correct.
|
|
1298
|
+
EXPECT_DOUBLE_EQ(20.0, S2LatLng(center).GetDistance(S2LatLng(p0)).degrees());
|
|
1299
|
+
EXPECT_DOUBLE_EQ(20.0, S2LatLng(center).GetDistance(S2LatLng(p1)).degrees());
|
|
1300
|
+
EXPECT_DOUBLE_EQ(20.0, S2LatLng(center).GetDistance(S2LatLng(p2)).degrees());
|
|
1301
|
+
EXPECT_DOUBLE_EQ(20.0, S2LatLng(center).GetDistance(S2LatLng(p3)).degrees());
|
|
1302
|
+
// Make sure that all angles of the polygon are the same.
|
|
1303
|
+
EXPECT_DOUBLE_EQ(M_PI_2, (p1 - p0).Angle(p3 - p0));
|
|
1304
|
+
EXPECT_DOUBLE_EQ(M_PI_2, (p2 - p1).Angle(p0 - p1));
|
|
1305
|
+
EXPECT_DOUBLE_EQ(M_PI_2, (p3 - p2).Angle(p1 - p2));
|
|
1306
|
+
EXPECT_DOUBLE_EQ(M_PI_2, (p0 - p3).Angle(p2 - p3));
|
|
1307
|
+
// Make sure that all edges of the polygon have the same length.
|
|
1308
|
+
EXPECT_DOUBLE_EQ(27.990890717782829,
|
|
1309
|
+
S2LatLng(p0).GetDistance(S2LatLng(p1)).degrees());
|
|
1310
|
+
EXPECT_DOUBLE_EQ(27.990890717782829,
|
|
1311
|
+
S2LatLng(p1).GetDistance(S2LatLng(p2)).degrees());
|
|
1312
|
+
EXPECT_DOUBLE_EQ(27.990890717782829,
|
|
1313
|
+
S2LatLng(p2).GetDistance(S2LatLng(p3)).degrees());
|
|
1314
|
+
EXPECT_DOUBLE_EQ(27.990890717782829,
|
|
1315
|
+
S2LatLng(p3).GetDistance(S2LatLng(p0)).degrees());
|
|
1316
|
+
|
|
1317
|
+
// Check actual coordinates. This may change if we switch the algorithm
|
|
1318
|
+
// intentionally.
|
|
1319
|
+
EXPECT_DOUBLE_EQ(62.162880741097204, S2LatLng(p0).lat().degrees());
|
|
1320
|
+
EXPECT_DOUBLE_EQ(103.11051028343407, S2LatLng(p0).lng().degrees());
|
|
1321
|
+
EXPECT_DOUBLE_EQ(61.955157772928345, S2LatLng(p1).lat().degrees());
|
|
1322
|
+
EXPECT_DOUBLE_EQ(165.25681963683536, S2LatLng(p1).lng().degrees());
|
|
1323
|
+
EXPECT_DOUBLE_EQ(75.139812547718478, S2LatLng(p2).lat().degrees());
|
|
1324
|
+
EXPECT_DOUBLE_EQ(-119.13042521187423, S2LatLng(p2).lng().degrees());
|
|
1325
|
+
EXPECT_DOUBLE_EQ(75.524190079054392, S2LatLng(p3).lat().degrees());
|
|
1326
|
+
EXPECT_DOUBLE_EQ(26.392175948257943, S2LatLng(p3).lng().degrees());
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
TEST(S2LoopShape, Basic) {
|
|
1330
|
+
unique_ptr<S2Loop> loop = s2textformat::MakeLoop("0:0, 0:1, 1:0");
|
|
1331
|
+
S2Loop::Shape shape(loop.get());
|
|
1332
|
+
EXPECT_EQ(loop.get(), shape.loop());
|
|
1333
|
+
EXPECT_EQ(3, shape.num_edges());
|
|
1334
|
+
EXPECT_EQ(1, shape.num_chains());
|
|
1335
|
+
EXPECT_EQ(0, shape.chain(0).start);
|
|
1336
|
+
EXPECT_EQ(3, shape.chain(0).length);
|
|
1337
|
+
auto edge2 = shape.edge(2);
|
|
1338
|
+
EXPECT_EQ("1:0", s2textformat::ToString(edge2.v0));
|
|
1339
|
+
EXPECT_EQ("0:0", s2textformat::ToString(edge2.v1));
|
|
1340
|
+
EXPECT_EQ(2, shape.dimension());
|
|
1341
|
+
EXPECT_FALSE(shape.is_empty());
|
|
1342
|
+
EXPECT_FALSE(shape.is_full());
|
|
1343
|
+
EXPECT_FALSE(shape.GetReferencePoint().contained);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
TEST(S2LoopShape, EmptyLoop) {
|
|
1347
|
+
S2Loop loop(S2Loop::kEmpty());
|
|
1348
|
+
S2Loop::Shape shape(&loop);
|
|
1349
|
+
EXPECT_EQ(0, shape.num_edges());
|
|
1350
|
+
EXPECT_EQ(0, shape.num_chains());
|
|
1351
|
+
EXPECT_TRUE(shape.is_empty());
|
|
1352
|
+
EXPECT_FALSE(shape.is_full());
|
|
1353
|
+
EXPECT_FALSE(shape.GetReferencePoint().contained);
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
TEST(S2LoopShape, FullLoop) {
|
|
1357
|
+
S2Loop loop(S2Loop::kFull());
|
|
1358
|
+
S2Loop::Shape shape(&loop);
|
|
1359
|
+
EXPECT_EQ(0, shape.num_edges());
|
|
1360
|
+
EXPECT_EQ(1, shape.num_chains());
|
|
1361
|
+
EXPECT_FALSE(shape.is_empty());
|
|
1362
|
+
EXPECT_TRUE(shape.is_full());
|
|
1363
|
+
EXPECT_TRUE(shape.GetReferencePoint().contained);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
TEST(S2LoopOwningShape, Ownership) {
|
|
1367
|
+
// Debug mode builds will catch any memory leak below.
|
|
1368
|
+
auto loop = make_unique<S2Loop>(S2Loop::kEmpty());
|
|
1369
|
+
S2Loop::OwningShape shape(std::move(loop));
|
|
1370
|
+
}
|
|
1371
|
+
|