@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,3025 @@
|
|
|
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
|
+
|
|
19
|
+
#include "s2/s2polygon.h"
|
|
20
|
+
|
|
21
|
+
#include <algorithm>
|
|
22
|
+
#include <cmath>
|
|
23
|
+
#include <cstdlib>
|
|
24
|
+
#include <limits>
|
|
25
|
+
#include <memory>
|
|
26
|
+
#include <set>
|
|
27
|
+
#include <string>
|
|
28
|
+
#include <utility>
|
|
29
|
+
#include <vector>
|
|
30
|
+
|
|
31
|
+
#include <gtest/gtest.h>
|
|
32
|
+
|
|
33
|
+
#include "s2/base/casts.h"
|
|
34
|
+
#include "s2/base/commandlineflags.h"
|
|
35
|
+
#include "s2/base/logging.h"
|
|
36
|
+
#include "s2/mutable_s2shape_index.h"
|
|
37
|
+
#include "s2/r1interval.h"
|
|
38
|
+
#include "s2/s1angle.h"
|
|
39
|
+
#include "s2/s2builder.h"
|
|
40
|
+
#include "s2/s2builderutil_s2polygon_layer.h"
|
|
41
|
+
#include "s2/s2builderutil_snap_functions.h"
|
|
42
|
+
#include "s2/s2cap.h"
|
|
43
|
+
#include "s2/s2cell.h"
|
|
44
|
+
#include "s2/s2cell_id.h"
|
|
45
|
+
#include "s2/s2cell_union.h"
|
|
46
|
+
#include "s2/s2closest_edge_query.h"
|
|
47
|
+
#include "s2/s2coords.h"
|
|
48
|
+
#include "s2/s2debug.h"
|
|
49
|
+
#include "s2/s2edge_crossings.h"
|
|
50
|
+
#include "s2/s2edge_distances.h"
|
|
51
|
+
#include "s2/s2error.h"
|
|
52
|
+
#include "s2/s2latlng.h"
|
|
53
|
+
#include "s2/s2loop.h"
|
|
54
|
+
#include "s2/s2metrics.h"
|
|
55
|
+
#include "s2/s2padded_cell.h"
|
|
56
|
+
#include "s2/s2pointutil.h"
|
|
57
|
+
#include "s2/s2polyline.h"
|
|
58
|
+
#include "s2/s2region_coverer.h"
|
|
59
|
+
#include "s2/s2testing.h"
|
|
60
|
+
#include "s2/s2text_format.h"
|
|
61
|
+
#include "s2/strings/serialize.h"
|
|
62
|
+
#include "s2/third_party/absl/base/macros.h"
|
|
63
|
+
#include "s2/third_party/absl/container/fixed_array.h"
|
|
64
|
+
#include "s2/third_party/absl/memory/memory.h"
|
|
65
|
+
#include "s2/third_party/absl/strings/str_cat.h"
|
|
66
|
+
#include "s2/util/coding/coder.h"
|
|
67
|
+
#include "s2/util/gtl/legacy_random_shuffle.h"
|
|
68
|
+
#include "s2/util/math/matrix3x3.h"
|
|
69
|
+
|
|
70
|
+
using absl::StrCat;
|
|
71
|
+
using absl::make_unique;
|
|
72
|
+
using s2builderutil::IntLatLngSnapFunction;
|
|
73
|
+
using s2builderutil::S2PolygonLayer;
|
|
74
|
+
using std::max;
|
|
75
|
+
using std::min;
|
|
76
|
+
using std::numeric_limits;
|
|
77
|
+
using std::swap;
|
|
78
|
+
using std::unique_ptr;
|
|
79
|
+
using std::vector;
|
|
80
|
+
|
|
81
|
+
// A set of nested loops around the point 0:0 (lat:lng).
|
|
82
|
+
// Every vertex of kNear0 is a vertex of kNear1.
|
|
83
|
+
const char kNearPoint[] = "0:0";
|
|
84
|
+
const string kNear0 = "-1:0, 0:1, 1:0, 0:-1;";
|
|
85
|
+
const string kNear1 = "-1:-1, -1:0, -1:1, 0:1, 1:1, 1:0, 1:-1, 0:-1;";
|
|
86
|
+
const string kNear2 = "-1:-2, -2:5, 5:-2;";
|
|
87
|
+
const string kNear3 = "-2:-2, -3:6, 6:-3;";
|
|
88
|
+
const string kNearHemi = "0:-90, -90:0, 0:90, 90:0;";
|
|
89
|
+
|
|
90
|
+
// A set of nested loops around the point 0:180 (lat:lng).
|
|
91
|
+
// Every vertex of kFar0 and kFar2 belongs to kFar1, and all
|
|
92
|
+
// the loops except kFar2 are non-convex.
|
|
93
|
+
const string kFar0 = "0:179, 1:180, 0:-179, 2:-180;";
|
|
94
|
+
const string kFar1 =
|
|
95
|
+
"0:179, -1:179, 1:180, -1:-179, 0:-179, 3:-178, 2:-180, 3:178;";
|
|
96
|
+
const string kFar2 = "3:-178, 3:178, -1:179, -1:-179;";
|
|
97
|
+
const string kFar3 = "-3:-178, 4:-177, 4:177, -3:178, -2:179;";
|
|
98
|
+
const string kFarHemi = "0:-90, 60:90, -60:90;";
|
|
99
|
+
|
|
100
|
+
// A set of nested loops around the point -90:0 (lat:lng).
|
|
101
|
+
const string kSouthPoint = "-89.9999:0.001";
|
|
102
|
+
const string kSouth0a = "-90:0, -89.99:0.01, -89.99:0;";
|
|
103
|
+
const string kSouth0b = "-90:0, -89.99:0.03, -89.99:0.02;";
|
|
104
|
+
const string kSouth0c = "-90:0, -89.99:0.05, -89.99:0.04;";
|
|
105
|
+
const string kSouth1 = "-90:0, -89.9:0.1, -89.9:-0.1;";
|
|
106
|
+
const string kSouth2 = "-90:0, -89.8:0.2, -89.8:-0.2;";
|
|
107
|
+
const string kSouthHemi = "0:-180, 0:60, 0:-60;";
|
|
108
|
+
|
|
109
|
+
// Two different loops that surround all the Near and Far loops except
|
|
110
|
+
// for the hemispheres.
|
|
111
|
+
const string kNearFar1 = "-1:-9, -9:-9, -9:9, 9:9, 9:-9, 1:-9, "
|
|
112
|
+
"1:-175, 9:-175, 9:175, -9:175, -9:-175, -1:-175;";
|
|
113
|
+
const string kNearFar2 = "-2:15, -2:170, -8:-175, 8:-175, "
|
|
114
|
+
"2:170, 2:15, 8:-4, -8:-4;";
|
|
115
|
+
|
|
116
|
+
// Loops that result from intersection of other loops.
|
|
117
|
+
const string kFarHSouthH = "0:-180, 0:90, -60:90, 0:-90;";
|
|
118
|
+
|
|
119
|
+
// Rectangles that form a cross, with only shared vertices, no crossing edges.
|
|
120
|
+
// Optional holes outside the intersecting region.
|
|
121
|
+
const string kCross1 = "-2:1, -1:1, 1:1, 2:1, 2:-1, 1:-1, -1:-1, -2:-1;";
|
|
122
|
+
const string kCross1SideHole = "-1.5:0.5, -1.2:0.5, -1.2:-0.5, -1.5:-0.5;";
|
|
123
|
+
const string kCross2 = "1:-2, 1:-1, 1:1, 1:2, -1:2, -1:1, -1:-1, -1:-2;";
|
|
124
|
+
const string kCross2SideHole = "0.5:-1.5, 0.5:-1.2, -0.5:-1.2, -0.5:-1.5;";
|
|
125
|
+
const string kCrossCenterHole = "-0.5:0.5, 0.5:0.5, 0.5:-0.5, -0.5:-0.5;";
|
|
126
|
+
|
|
127
|
+
// Two rectangles that intersect, but no edges cross and there's always
|
|
128
|
+
// local containment (rather than crossing) at each shared vertex.
|
|
129
|
+
// In this ugly ASCII art, 1 is A+B, 2 is B+C:
|
|
130
|
+
// +---+---+---+
|
|
131
|
+
// | A | B | C |
|
|
132
|
+
// +---+---+---+
|
|
133
|
+
const string kOverlap1 = "0:1, 1:1, 2:1, 2:0, 1:0, 0:0;";
|
|
134
|
+
const string kOverlap1SideHole = "0.2:0.8, 0.8:0.8, 0.8:0.2, 0.2:0.2;";
|
|
135
|
+
const string kOverlap2 = "1:1, 2:1, 3:1, 3:0, 2:0, 1:0;";
|
|
136
|
+
const string kOverlap2SideHole = "2.2:0.8, 2.8:0.8, 2.8:0.2, 2.2:0.2;";
|
|
137
|
+
const string kOverlapCenterHole = "1.2:0.8, 1.8:0.8, 1.8:0.2, 1.2:0.2;";
|
|
138
|
+
|
|
139
|
+
// An empty polygon.
|
|
140
|
+
const string kEmpty = "";
|
|
141
|
+
// By symmetry, the intersection of the two polygons has almost half the area
|
|
142
|
+
// of either polygon.
|
|
143
|
+
const string kOverlap3 = "-10:10, 0:10, 0:-10, -10:-10, -10:0";
|
|
144
|
+
const string kOverlap4 = "-10:0, 10:0, 10:-10, -10:-10";
|
|
145
|
+
|
|
146
|
+
class S2PolygonTestBase : public testing::Test {
|
|
147
|
+
public:
|
|
148
|
+
S2PolygonTestBase();
|
|
149
|
+
|
|
150
|
+
protected:
|
|
151
|
+
// Some standard polygons to use in the tests.
|
|
152
|
+
const unique_ptr<const S2Polygon> empty_;
|
|
153
|
+
const unique_ptr<const S2Polygon> full_;
|
|
154
|
+
const unique_ptr<const S2Polygon> near_0_;
|
|
155
|
+
const unique_ptr<const S2Polygon> near_10_;
|
|
156
|
+
const unique_ptr<const S2Polygon> near_30_;
|
|
157
|
+
const unique_ptr<const S2Polygon> near_32_;
|
|
158
|
+
const unique_ptr<const S2Polygon> near_3210_;
|
|
159
|
+
const unique_ptr<const S2Polygon> near_H3210_;
|
|
160
|
+
|
|
161
|
+
const unique_ptr<const S2Polygon> far_10_;
|
|
162
|
+
const unique_ptr<const S2Polygon> far_21_;
|
|
163
|
+
const unique_ptr<const S2Polygon> far_321_;
|
|
164
|
+
const unique_ptr<const S2Polygon> far_H20_;
|
|
165
|
+
const unique_ptr<const S2Polygon> far_H3210_;
|
|
166
|
+
|
|
167
|
+
const unique_ptr<const S2Polygon> south_0ab_;
|
|
168
|
+
const unique_ptr<const S2Polygon> south_2_;
|
|
169
|
+
const unique_ptr<const S2Polygon> south_210b_;
|
|
170
|
+
const unique_ptr<const S2Polygon> south_H21_;
|
|
171
|
+
const unique_ptr<const S2Polygon> south_H20abc_;
|
|
172
|
+
|
|
173
|
+
const unique_ptr<const S2Polygon> nf1_n10_f2_s10abc_;
|
|
174
|
+
|
|
175
|
+
const unique_ptr<const S2Polygon> nf2_n2_f210_s210ab_;
|
|
176
|
+
|
|
177
|
+
const unique_ptr<const S2Polygon> f32_n0_;
|
|
178
|
+
const unique_ptr<const S2Polygon> n32_s0b_;
|
|
179
|
+
|
|
180
|
+
const unique_ptr<const S2Polygon> cross1_;
|
|
181
|
+
const unique_ptr<const S2Polygon> cross1_side_hole_;
|
|
182
|
+
const unique_ptr<const S2Polygon> cross1_center_hole_;
|
|
183
|
+
const unique_ptr<const S2Polygon> cross2_;
|
|
184
|
+
const unique_ptr<const S2Polygon> cross2_side_hole_;
|
|
185
|
+
const unique_ptr<const S2Polygon> cross2_center_hole_;
|
|
186
|
+
|
|
187
|
+
const unique_ptr<const S2Polygon> overlap1_;
|
|
188
|
+
const unique_ptr<const S2Polygon> overlap1_side_hole_;
|
|
189
|
+
const unique_ptr<const S2Polygon> overlap1_center_hole_;
|
|
190
|
+
const unique_ptr<const S2Polygon> overlap2_;
|
|
191
|
+
const unique_ptr<const S2Polygon> overlap2_side_hole_;
|
|
192
|
+
const unique_ptr<const S2Polygon> overlap2_center_hole_;
|
|
193
|
+
|
|
194
|
+
const unique_ptr<const S2Polygon> far_H_;
|
|
195
|
+
const unique_ptr<const S2Polygon> south_H_;
|
|
196
|
+
const unique_ptr<const S2Polygon> far_H_south_H_;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
static bool TestEncodeDecode(const S2Polygon* src) {
|
|
200
|
+
Encoder encoder;
|
|
201
|
+
src->Encode(&encoder);
|
|
202
|
+
Decoder decoder(encoder.base(), encoder.length());
|
|
203
|
+
S2Polygon dst;
|
|
204
|
+
dst.Decode(&decoder);
|
|
205
|
+
return src->Equals(&dst);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
static unique_ptr<S2Polygon> MakePolygon(const string& str) {
|
|
209
|
+
unique_ptr<S2Polygon> polygon(s2textformat::MakeVerbatimPolygon(str));
|
|
210
|
+
|
|
211
|
+
// Check that InitToSnapped() is idempotent.
|
|
212
|
+
S2Polygon snapped1, snapped2;
|
|
213
|
+
snapped1.InitToSnapped(polygon.get());
|
|
214
|
+
snapped2.InitToSnapped(&snapped1);
|
|
215
|
+
EXPECT_TRUE(snapped1.Equals(&snapped2));
|
|
216
|
+
|
|
217
|
+
// Check that Decode(Encode(x)) is the identity function.
|
|
218
|
+
EXPECT_TRUE(TestEncodeDecode(polygon.get()));
|
|
219
|
+
return polygon;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
static void CheckContains(const string& a_str, const string& b_str) {
|
|
223
|
+
unique_ptr<S2Polygon> a = MakePolygon(a_str);
|
|
224
|
+
unique_ptr<S2Polygon> b = MakePolygon(b_str);
|
|
225
|
+
EXPECT_TRUE(a->Contains(b.get()));
|
|
226
|
+
EXPECT_TRUE(a->ApproxContains(b.get(), S1Angle::Radians(1e-15)));
|
|
227
|
+
EXPECT_FALSE(a->ApproxDisjoint(b.get(), S1Angle::Radians(1e-15)));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
static void CheckContainsPoint(const string& a_str, const string& b_str) {
|
|
231
|
+
unique_ptr<S2Polygon> a(s2textformat::MakePolygon(a_str));
|
|
232
|
+
EXPECT_TRUE(a->Contains(s2textformat::MakePoint(b_str)))
|
|
233
|
+
<< " " << a_str << " did not contain " << b_str;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
TEST(S2Polygon, Init) {
|
|
237
|
+
CheckContains(kNear1, kNear0);
|
|
238
|
+
CheckContains(kNear2, kNear1);
|
|
239
|
+
CheckContains(kNear3, kNear2);
|
|
240
|
+
CheckContains(kNearHemi, kNear3);
|
|
241
|
+
CheckContains(kFar1, kFar0);
|
|
242
|
+
CheckContains(kFar2, kFar1);
|
|
243
|
+
CheckContains(kFar3, kFar2);
|
|
244
|
+
CheckContains(kFarHemi, kFar3);
|
|
245
|
+
CheckContains(kSouth1, kSouth0a);
|
|
246
|
+
CheckContains(kSouth1, kSouth0b);
|
|
247
|
+
CheckContains(kSouth1, kSouth0c);
|
|
248
|
+
CheckContains(kSouthHemi, kSouth2);
|
|
249
|
+
CheckContains(kNearFar1, kNear3);
|
|
250
|
+
CheckContains(kNearFar1, kFar3);
|
|
251
|
+
CheckContains(kNearFar2, kNear3);
|
|
252
|
+
CheckContains(kNearFar2, kFar3);
|
|
253
|
+
|
|
254
|
+
CheckContainsPoint(kNear0, kNearPoint);
|
|
255
|
+
CheckContainsPoint(kNear1, kNearPoint);
|
|
256
|
+
CheckContainsPoint(kNear2, kNearPoint);
|
|
257
|
+
CheckContainsPoint(kNear3, kNearPoint);
|
|
258
|
+
CheckContainsPoint(kNearHemi, kNearPoint);
|
|
259
|
+
CheckContainsPoint(kSouth0a, kSouthPoint);
|
|
260
|
+
CheckContainsPoint(kSouth1, kSouthPoint);
|
|
261
|
+
CheckContainsPoint(kSouth2, kSouthPoint);
|
|
262
|
+
CheckContainsPoint(kSouthHemi, kSouthPoint);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
TEST(S2Polygon, OverlapFractions) {
|
|
266
|
+
unique_ptr<S2Polygon> a(MakePolygon(kEmpty));
|
|
267
|
+
unique_ptr<S2Polygon> b(MakePolygon(kEmpty));
|
|
268
|
+
auto result = S2Polygon::GetOverlapFractions(a.get(), b.get());
|
|
269
|
+
EXPECT_DOUBLE_EQ(1.0, result.first);
|
|
270
|
+
EXPECT_DOUBLE_EQ(1.0, result.second);
|
|
271
|
+
|
|
272
|
+
b = MakePolygon(kOverlap3);
|
|
273
|
+
result = S2Polygon::GetOverlapFractions(a.get(), b.get());
|
|
274
|
+
EXPECT_DOUBLE_EQ(1.0, result.first);
|
|
275
|
+
EXPECT_DOUBLE_EQ(0.0, result.second);
|
|
276
|
+
|
|
277
|
+
a = MakePolygon(kOverlap4);
|
|
278
|
+
result = S2Polygon::GetOverlapFractions(a.get(), b.get());
|
|
279
|
+
EXPECT_NEAR(0.5, result.first, 1e-14);
|
|
280
|
+
EXPECT_NEAR(0.5, result.second, 1e-14);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
TEST(S2Polygon, OriginNearPole) {
|
|
284
|
+
// S2Polygon operations are more efficient if S2::Origin() is near a pole.
|
|
285
|
+
// (Loops that contain a pole tend to have very loose bounding boxes because
|
|
286
|
+
// they span the full longitude range. S2Polygon canonicalizes all loops so
|
|
287
|
+
// that they don't contain S2::Origin(), thus by placing S2::Origin() near a
|
|
288
|
+
// pole we minimize the number of canonical loops which contain that pole.)
|
|
289
|
+
EXPECT_GE(S2LatLng::Latitude(S2::Origin()).degrees(), 80);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
S2PolygonTestBase::S2PolygonTestBase()
|
|
293
|
+
: empty_(new S2Polygon()),
|
|
294
|
+
full_(MakePolygon("full")),
|
|
295
|
+
near_0_(MakePolygon(kNear0)),
|
|
296
|
+
near_10_(MakePolygon(kNear0 + kNear1)),
|
|
297
|
+
near_30_(MakePolygon(kNear3 + kNear0)),
|
|
298
|
+
near_32_(MakePolygon(kNear2 + kNear3)),
|
|
299
|
+
near_3210_(MakePolygon(kNear0 + kNear2 + kNear3 + kNear1)),
|
|
300
|
+
near_H3210_(MakePolygon(kNear0 + kNear2 + kNear3 + kNearHemi + kNear1)),
|
|
301
|
+
|
|
302
|
+
far_10_(MakePolygon(kFar0 + kFar1)),
|
|
303
|
+
far_21_(MakePolygon(kFar2 + kFar1)),
|
|
304
|
+
far_321_(MakePolygon(kFar2 + kFar3 + kFar1)),
|
|
305
|
+
far_H20_(MakePolygon(kFar2 + kFarHemi + kFar0)),
|
|
306
|
+
far_H3210_(MakePolygon(kFar2 + kFarHemi + kFar0 + kFar1 + kFar3)),
|
|
307
|
+
|
|
308
|
+
south_0ab_(MakePolygon(kSouth0a + kSouth0b)),
|
|
309
|
+
south_2_(MakePolygon(kSouth2)),
|
|
310
|
+
south_210b_(MakePolygon(kSouth2 + kSouth0b + kSouth1)),
|
|
311
|
+
south_H21_(MakePolygon(kSouth2 + kSouthHemi + kSouth1)),
|
|
312
|
+
south_H20abc_(MakePolygon(kSouth2 + kSouth0b + kSouthHemi +
|
|
313
|
+
kSouth0a + kSouth0c)),
|
|
314
|
+
|
|
315
|
+
nf1_n10_f2_s10abc_(MakePolygon(kSouth0c + kFar2 + kNear1 + kNearFar1 +
|
|
316
|
+
kNear0 + kSouth1 + kSouth0b + kSouth0a)),
|
|
317
|
+
|
|
318
|
+
nf2_n2_f210_s210ab_(MakePolygon(kFar2 + kSouth0a + kFar1 + kSouth1 + kFar0 +
|
|
319
|
+
kSouth0b + kNearFar2 + kSouth2 + kNear2)),
|
|
320
|
+
|
|
321
|
+
f32_n0_(MakePolygon(kFar2 + kNear0 + kFar3)),
|
|
322
|
+
n32_s0b_(MakePolygon(kNear3 + kSouth0b + kNear2)),
|
|
323
|
+
|
|
324
|
+
cross1_(MakePolygon(kCross1)),
|
|
325
|
+
cross1_side_hole_(MakePolygon(kCross1 + kCross1SideHole)),
|
|
326
|
+
cross1_center_hole_(MakePolygon(kCross1 + kCrossCenterHole)),
|
|
327
|
+
cross2_(MakePolygon(kCross2)),
|
|
328
|
+
cross2_side_hole_(MakePolygon(kCross2 + kCross2SideHole)),
|
|
329
|
+
cross2_center_hole_(MakePolygon(kCross2 + kCrossCenterHole)),
|
|
330
|
+
|
|
331
|
+
overlap1_(MakePolygon(kOverlap1)),
|
|
332
|
+
overlap1_side_hole_(MakePolygon(kOverlap1 + kOverlap1SideHole)),
|
|
333
|
+
overlap1_center_hole_(MakePolygon(kOverlap1 + kOverlapCenterHole)),
|
|
334
|
+
overlap2_(MakePolygon(kOverlap2)),
|
|
335
|
+
overlap2_side_hole_(MakePolygon(kOverlap2 + kOverlap2SideHole)),
|
|
336
|
+
overlap2_center_hole_(MakePolygon(kOverlap2 + kOverlapCenterHole)),
|
|
337
|
+
|
|
338
|
+
far_H_(MakePolygon(kFarHemi)),
|
|
339
|
+
south_H_(MakePolygon(kSouthHemi)),
|
|
340
|
+
far_H_south_H_(MakePolygon(kFarHSouthH)) {
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
static void CheckEqual(const S2Polygon& a, const S2Polygon& b,
|
|
344
|
+
S1Angle max_error = S1Angle::Zero()) {
|
|
345
|
+
if (a.BoundaryApproxEquals(b, max_error)) return;
|
|
346
|
+
S2Builder builder{S2Builder::Options()};
|
|
347
|
+
S2Polygon a2, b2;
|
|
348
|
+
builder.StartLayer(make_unique<S2PolygonLayer>(&a2));
|
|
349
|
+
builder.AddPolygon(a);
|
|
350
|
+
S2Error error;
|
|
351
|
+
ASSERT_TRUE(builder.Build(&error)) << error;
|
|
352
|
+
builder.StartLayer(make_unique<S2PolygonLayer>(&b2));
|
|
353
|
+
builder.AddPolygon(b);
|
|
354
|
+
ASSERT_TRUE(builder.Build(&error)) << error;
|
|
355
|
+
EXPECT_TRUE(a2.BoundaryApproxEquals(b2, max_error))
|
|
356
|
+
<< "\na: " << s2textformat::ToString(a)
|
|
357
|
+
<< "\nb: " << s2textformat::ToString(b)
|
|
358
|
+
<< "\na2: " << s2textformat::ToString(a2)
|
|
359
|
+
<< "\nb2: " << s2textformat::ToString(b2);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
static void CheckComplementary(const S2Polygon& a, const S2Polygon& b) {
|
|
363
|
+
S2Polygon b1;
|
|
364
|
+
b1.InitToComplement(&b);
|
|
365
|
+
CheckEqual(a, b1);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
TEST(S2Polygon, TestApproxContainsAndDisjoint) {
|
|
369
|
+
// We repeatedly choose a random cell id and intersect its bounding polygon
|
|
370
|
+
// "A" with the bounding polygon "B" of one its child cells. The result may
|
|
371
|
+
// not be contained by either A or B, because the vertices of B near the
|
|
372
|
+
// edge midpoints of A may be slightly outside A, and even when the crossing
|
|
373
|
+
// edges are intersected, the intersection point may also be slightly
|
|
374
|
+
// outside A and/or B.
|
|
375
|
+
//
|
|
376
|
+
// We repeat the test many times and expect that some fraction of the exact
|
|
377
|
+
// tests should fail, while all of the approximate test should succeed.
|
|
378
|
+
const int kIters = 1000;
|
|
379
|
+
int exact_contains = 0, exact_disjoint = 0;
|
|
380
|
+
S2Testing::rnd.Reset(FLAGS_s2_random_seed);
|
|
381
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
382
|
+
S2CellId id = S2Testing::GetRandomCellId(10);
|
|
383
|
+
S2Polygon parent_polygon((S2Cell(id)));
|
|
384
|
+
S2Polygon child_polygon(S2Cell(id.child(0)));
|
|
385
|
+
|
|
386
|
+
// Get the intersection. There is no guarantee that the intersection will
|
|
387
|
+
// be contained by A or B. Similarly, the intersection may slightly
|
|
388
|
+
// overlap an adjacent disjoint polygon C.
|
|
389
|
+
S2Polygon intersection;
|
|
390
|
+
intersection.InitToIntersection(&parent_polygon, &child_polygon);
|
|
391
|
+
if (parent_polygon.Contains(&intersection)) {
|
|
392
|
+
++exact_contains;
|
|
393
|
+
}
|
|
394
|
+
EXPECT_TRUE(parent_polygon.ApproxContains(
|
|
395
|
+
&intersection, S2::kIntersectionMergeRadius));
|
|
396
|
+
|
|
397
|
+
S2Polygon adjacent_polygon(S2Cell(id.child(1)));
|
|
398
|
+
if (!adjacent_polygon.Intersects(&intersection)) {
|
|
399
|
+
++exact_disjoint;
|
|
400
|
+
}
|
|
401
|
+
EXPECT_TRUE(adjacent_polygon.ApproxDisjoint(
|
|
402
|
+
&intersection, S2::kIntersectionMergeRadius));
|
|
403
|
+
}
|
|
404
|
+
// All of the approximate results are true, so we check that at least some
|
|
405
|
+
// of the exact results are false in order to make sure that this test
|
|
406
|
+
// actually tests something.
|
|
407
|
+
//
|
|
408
|
+
// There are two vertices in each child cell that have a 50% chance of being
|
|
409
|
+
// outside the parent cell. When a vertex is outside, an intersection point
|
|
410
|
+
// is computed near that vertex that also has a 50% chance of being
|
|
411
|
+
// outside. Snapping used to choose one of these vertices at random, but
|
|
412
|
+
// currently the vertex whose S2CellId is smaller is always chosen. For the
|
|
413
|
+
// exact containment test, it turns out that one vertex is adjacent to a
|
|
414
|
+
// lower-numbered S2CellId and the other is adjacent to a higher-numbered
|
|
415
|
+
// S2CellId, which means that one vertex will always be chosen outside the
|
|
416
|
+
// parent if possible, and the other will always be chosen inside if
|
|
417
|
+
// possible. This works out to an expectation that 0.5 * 0.75 = 37.5% of
|
|
418
|
+
// the exact containment tests will succeed.
|
|
419
|
+
//
|
|
420
|
+
// For the exact disjoint test, there is one shared vertex that might be
|
|
421
|
+
// replaced by a computed intersection point. The shared vertex is inside
|
|
422
|
+
// the parent 50% of the time. Otherwise there is a 50% chance that the
|
|
423
|
+
// intersection point will not be chosen for snapping because it has a
|
|
424
|
+
// higher S2CellId that the shared vertex, and otherwise there is still a
|
|
425
|
+
// 50% chance that intersection point is on the side of the shared edge that
|
|
426
|
+
// results in no intersection. This works out to an expectation that
|
|
427
|
+
// (1 - 0.5 * 0.5 * 0.5) = 87.5% of the exact disjoint tests will succeed.
|
|
428
|
+
EXPECT_LT(exact_contains, 0.40 * kIters); // about 37.5% succeed
|
|
429
|
+
EXPECT_LT(exact_disjoint, 0.90 * kIters); // about 87.5% succeed
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Given a pair of polygons where A contains B, check that various identities
|
|
433
|
+
// involving union, intersection, and difference operations hold true.
|
|
434
|
+
static void TestOneNestedPair(const S2Polygon& a, const S2Polygon& b) {
|
|
435
|
+
EXPECT_TRUE(a.Contains(&b));
|
|
436
|
+
EXPECT_EQ(!b.is_empty(), a.Intersects(&b));
|
|
437
|
+
EXPECT_EQ(!b.is_empty(), b.Intersects(&a));
|
|
438
|
+
|
|
439
|
+
S2Polygon c, d, e, f, g;
|
|
440
|
+
c.InitToUnion(&a, &b);
|
|
441
|
+
CheckEqual(c, a);
|
|
442
|
+
|
|
443
|
+
d.InitToIntersection(&a, &b);
|
|
444
|
+
CheckEqual(d, b);
|
|
445
|
+
|
|
446
|
+
e.InitToDifference(&b, &a);
|
|
447
|
+
EXPECT_TRUE(e.is_empty());
|
|
448
|
+
|
|
449
|
+
f.InitToDifference(&a, &b);
|
|
450
|
+
g.InitToSymmetricDifference(&a, &b);
|
|
451
|
+
CheckEqual(f, g);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Given a pair of disjoint polygons A and B, check that various identities
|
|
455
|
+
// involving union, intersection, and difference operations hold true.
|
|
456
|
+
static void TestOneDisjointPair(const S2Polygon& a, const S2Polygon& b) {
|
|
457
|
+
EXPECT_FALSE(a.Intersects(&b));
|
|
458
|
+
EXPECT_FALSE(b.Intersects(&a));
|
|
459
|
+
EXPECT_EQ(b.is_empty(), a.Contains(&b));
|
|
460
|
+
EXPECT_EQ(a.is_empty(), b.Contains(&a));
|
|
461
|
+
|
|
462
|
+
S2Polygon ab, c, d, e, f, g;
|
|
463
|
+
S2Builder builder{S2Builder::Options()};
|
|
464
|
+
builder.StartLayer(make_unique<S2PolygonLayer>(&ab));
|
|
465
|
+
builder.AddPolygon(a);
|
|
466
|
+
builder.AddPolygon(b);
|
|
467
|
+
S2Error error;
|
|
468
|
+
ASSERT_TRUE(builder.Build(&error)) << error;
|
|
469
|
+
|
|
470
|
+
c.InitToUnion(&a, &b);
|
|
471
|
+
CheckEqual(c, ab);
|
|
472
|
+
|
|
473
|
+
d.InitToIntersection(&a, &b);
|
|
474
|
+
EXPECT_TRUE(d.is_empty());
|
|
475
|
+
|
|
476
|
+
e.InitToDifference(&a, &b);
|
|
477
|
+
CheckEqual(e, a);
|
|
478
|
+
|
|
479
|
+
f.InitToDifference(&b, &a);
|
|
480
|
+
CheckEqual(f, b);
|
|
481
|
+
|
|
482
|
+
g.InitToSymmetricDifference(&a, &b);
|
|
483
|
+
CheckEqual(g, ab);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Given polygons A and B whose union covers the sphere, check that various
|
|
487
|
+
// identities involving union, intersection, and difference hold true.
|
|
488
|
+
static void TestOneCoveringPair(const S2Polygon& a, const S2Polygon& b) {
|
|
489
|
+
EXPECT_EQ(a.is_full(), a.Contains(&b));
|
|
490
|
+
EXPECT_EQ(b.is_full(), b.Contains(&a));
|
|
491
|
+
|
|
492
|
+
S2Polygon c, d, e, f;
|
|
493
|
+
c.InitToUnion(&a, &b);
|
|
494
|
+
EXPECT_TRUE(c.is_full());
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Given polygons A and B such that both A and its complement intersect both B
|
|
498
|
+
// and its complement, check that various identities involving union,
|
|
499
|
+
// intersection, and difference hold true.
|
|
500
|
+
static void TestOneOverlappingPair(const S2Polygon& a, const S2Polygon& b) {
|
|
501
|
+
EXPECT_FALSE(a.Contains(&b));
|
|
502
|
+
EXPECT_FALSE(b.Contains(&a));
|
|
503
|
+
EXPECT_TRUE(a.Intersects(&b));
|
|
504
|
+
|
|
505
|
+
S2Polygon c, d, e, f, g, h;
|
|
506
|
+
c.InitToUnion(&a, &b);
|
|
507
|
+
EXPECT_FALSE(c.is_full());
|
|
508
|
+
|
|
509
|
+
d.InitToIntersection(&a, &b);
|
|
510
|
+
EXPECT_FALSE(d.is_empty());
|
|
511
|
+
|
|
512
|
+
e.InitToDifference(&b, &a);
|
|
513
|
+
EXPECT_FALSE(e.is_empty());
|
|
514
|
+
|
|
515
|
+
f.InitToDifference(&a, &b);
|
|
516
|
+
g.InitToUnion(&e, &f);
|
|
517
|
+
h.InitToSymmetricDifference(&a, &b);
|
|
518
|
+
CheckEqual(g, h);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Given a pair of polygons where A contains B, test various identities
|
|
522
|
+
// involving A, B, and their complements.
|
|
523
|
+
static void TestNestedPair(const S2Polygon& a, const S2Polygon& b) {
|
|
524
|
+
S2Polygon a1, b1;
|
|
525
|
+
a1.InitToComplement(&a);
|
|
526
|
+
b1.InitToComplement(&b);
|
|
527
|
+
|
|
528
|
+
TestOneNestedPair(a, b);
|
|
529
|
+
TestOneNestedPair(b1, a1);
|
|
530
|
+
TestOneDisjointPair(a1, b);
|
|
531
|
+
TestOneCoveringPair(a, b1);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Given a pair of disjoint polygons A and B, test various identities
|
|
535
|
+
// involving A, B, and their complements.
|
|
536
|
+
static void TestDisjointPair(const S2Polygon& a, const S2Polygon& b) {
|
|
537
|
+
S2Polygon a1, b1;
|
|
538
|
+
a1.InitToComplement(&a);
|
|
539
|
+
b1.InitToComplement(&b);
|
|
540
|
+
|
|
541
|
+
TestOneDisjointPair(a, b);
|
|
542
|
+
TestOneCoveringPair(a1, b1);
|
|
543
|
+
TestOneNestedPair(a1, b);
|
|
544
|
+
TestOneNestedPair(b1, a);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Given polygons A and B such that both A and its complement intersect both B
|
|
548
|
+
// and its complement, test various identities involving these four polygons.
|
|
549
|
+
static void TestOverlappingPair(const S2Polygon& a, const S2Polygon& b) {
|
|
550
|
+
S2Polygon a1, b1;
|
|
551
|
+
a1.InitToComplement(&a);
|
|
552
|
+
b1.InitToComplement(&b);
|
|
553
|
+
|
|
554
|
+
TestOneOverlappingPair(a, b);
|
|
555
|
+
TestOneOverlappingPair(a1, b1);
|
|
556
|
+
TestOneOverlappingPair(a1, b);
|
|
557
|
+
TestOneOverlappingPair(a, b1);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// "a1" is the complement of "a", and "b1" is the complement of "b".
|
|
561
|
+
static void TestOneComplementPair(const S2Polygon& a, const S2Polygon& a1,
|
|
562
|
+
const S2Polygon& b, const S2Polygon& b1) {
|
|
563
|
+
// Check DeMorgan's Law and that subtraction is the same as intersection
|
|
564
|
+
// with the complement. This function is called multiple times in order to
|
|
565
|
+
// test the various combinations of complements.
|
|
566
|
+
|
|
567
|
+
S2Polygon a1_or_b, a_and_b1, a_minus_b;
|
|
568
|
+
a_and_b1.InitToIntersection(&a, &b1);
|
|
569
|
+
a1_or_b.InitToUnion(&a1, &b);
|
|
570
|
+
a_minus_b.InitToDifference(&a, &b);
|
|
571
|
+
|
|
572
|
+
CheckComplementary(a1_or_b, a_and_b1);
|
|
573
|
+
CheckEqual(a_minus_b, a_and_b1);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Test identities that should hold for any pair of polygons A, B and their
|
|
577
|
+
// complements.
|
|
578
|
+
static void TestComplements(const S2Polygon& a, const S2Polygon& b) {
|
|
579
|
+
S2Polygon a1, b1;
|
|
580
|
+
a1.InitToComplement(&a);
|
|
581
|
+
b1.InitToComplement(&b);
|
|
582
|
+
|
|
583
|
+
TestOneComplementPair(a, a1, b, b1);
|
|
584
|
+
TestOneComplementPair(a1, a, b, b1);
|
|
585
|
+
TestOneComplementPair(a, a1, b1, b);
|
|
586
|
+
TestOneComplementPair(a1, a, b1, b);
|
|
587
|
+
|
|
588
|
+
// There is a lot of redundancy if we do this test for each complementary
|
|
589
|
+
// pair, so we just do it once instead.
|
|
590
|
+
S2Polygon a_xor_b1, a1_xor_b;
|
|
591
|
+
a_xor_b1.InitToSymmetricDifference(&a, &b1);
|
|
592
|
+
a1_xor_b.InitToSymmetricDifference(&a1, &b);
|
|
593
|
+
CheckEqual(a_xor_b1, a1_xor_b);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
static void TestDestructiveUnion(const S2Polygon& a, const S2Polygon& b) {
|
|
597
|
+
S2Polygon c;
|
|
598
|
+
c.InitToUnion(&a, &b);
|
|
599
|
+
vector<unique_ptr<S2Polygon>> polygons;
|
|
600
|
+
polygons.emplace_back(a.Clone());
|
|
601
|
+
polygons.emplace_back(b.Clone());
|
|
602
|
+
unique_ptr<S2Polygon> c_destructive =
|
|
603
|
+
S2Polygon::DestructiveUnion(std::move(polygons));
|
|
604
|
+
CheckEqual(c, *c_destructive);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
static void TestRelationWithDesc(const S2Polygon& a, const S2Polygon& b,
|
|
608
|
+
bool contains, bool contained,
|
|
609
|
+
bool intersects, const char* description) {
|
|
610
|
+
SCOPED_TRACE(description);
|
|
611
|
+
EXPECT_EQ(contains, a.Contains(&b));
|
|
612
|
+
EXPECT_EQ(contained, b.Contains(&a));
|
|
613
|
+
EXPECT_EQ(intersects, a.Intersects(&b));
|
|
614
|
+
if (contains) TestNestedPair(a, b);
|
|
615
|
+
if (contained) TestNestedPair(b, a);
|
|
616
|
+
if (!intersects) TestDisjointPair(a, b);
|
|
617
|
+
if (intersects && !(contains | contained)) {
|
|
618
|
+
TestOverlappingPair(a, b); // See TestOverlappingPair for definition
|
|
619
|
+
}
|
|
620
|
+
TestDestructiveUnion(a, b);
|
|
621
|
+
TestComplements(a, b);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
TEST_F(S2PolygonTestBase, Relations) {
|
|
625
|
+
#define TestRelation(a, b, contains, contained, intersects) \
|
|
626
|
+
TestRelationWithDesc(a, b, contains, contained, intersects, \
|
|
627
|
+
"args " #a ", " #b)
|
|
628
|
+
|
|
629
|
+
TestRelation(*near_10_, *empty_, true, false, false);
|
|
630
|
+
TestRelation(*near_10_, *near_10_, true, true, true);
|
|
631
|
+
TestRelation(*full_, *near_10_, true, false, true);
|
|
632
|
+
TestRelation(*near_10_, *near_30_, false, true, true);
|
|
633
|
+
TestRelation(*near_10_, *near_32_, false, false, false);
|
|
634
|
+
TestRelation(*near_10_, *near_3210_, false, true, true);
|
|
635
|
+
TestRelation(*near_10_, *near_H3210_, false, false, false);
|
|
636
|
+
TestRelation(*near_30_, *near_32_, true, false, true);
|
|
637
|
+
TestRelation(*near_30_, *near_3210_, true, false, true);
|
|
638
|
+
TestRelation(*near_30_, *near_H3210_, false, false, true);
|
|
639
|
+
TestRelation(*near_32_, *near_3210_, false, true, true);
|
|
640
|
+
TestRelation(*near_32_, *near_H3210_, false, false, false);
|
|
641
|
+
TestRelation(*near_3210_, *near_H3210_, false, false, false);
|
|
642
|
+
|
|
643
|
+
TestRelation(*far_10_, *far_21_, false, false, false);
|
|
644
|
+
TestRelation(*far_10_, *far_321_, false, true, true);
|
|
645
|
+
TestRelation(*far_10_, *far_H20_, false, false, false);
|
|
646
|
+
TestRelation(*far_10_, *far_H3210_, false, false, false);
|
|
647
|
+
TestRelation(*far_21_, *far_321_, false, false, false);
|
|
648
|
+
TestRelation(*far_21_, *far_H20_, false, false, false);
|
|
649
|
+
TestRelation(*far_21_, *far_H3210_, false, true, true);
|
|
650
|
+
TestRelation(*far_321_, *far_H20_, false, false, true);
|
|
651
|
+
TestRelation(*far_321_, *far_H3210_, false, false, true);
|
|
652
|
+
TestRelation(*far_H20_, *far_H3210_, false, false, true);
|
|
653
|
+
|
|
654
|
+
TestRelation(*south_0ab_, *south_2_, false, true, true);
|
|
655
|
+
TestRelation(*south_0ab_, *south_210b_, false, false, true);
|
|
656
|
+
TestRelation(*south_0ab_, *south_H21_, false, true, true);
|
|
657
|
+
TestRelation(*south_0ab_, *south_H20abc_, false, true, true);
|
|
658
|
+
TestRelation(*south_2_, *south_210b_, true, false, true);
|
|
659
|
+
TestRelation(*south_2_, *south_H21_, false, false, true);
|
|
660
|
+
TestRelation(*south_2_, *south_H20abc_, false, false, true);
|
|
661
|
+
TestRelation(*south_210b_, *south_H21_, false, false, true);
|
|
662
|
+
TestRelation(*south_210b_, *south_H20abc_, false, false, true);
|
|
663
|
+
TestRelation(*south_H21_, *south_H20abc_, true, false, true);
|
|
664
|
+
|
|
665
|
+
TestRelation(*nf1_n10_f2_s10abc_, *nf2_n2_f210_s210ab_, false, false, true);
|
|
666
|
+
TestRelation(*nf1_n10_f2_s10abc_, *near_32_, true, false, true);
|
|
667
|
+
TestRelation(*nf1_n10_f2_s10abc_, *far_21_, false, false, false);
|
|
668
|
+
TestRelation(*nf1_n10_f2_s10abc_, *south_0ab_, false, false, false);
|
|
669
|
+
TestRelation(*nf1_n10_f2_s10abc_, *f32_n0_, true, false, true);
|
|
670
|
+
|
|
671
|
+
TestRelation(*nf2_n2_f210_s210ab_, *near_10_, false, false, false);
|
|
672
|
+
TestRelation(*nf2_n2_f210_s210ab_, *far_10_, true, false, true);
|
|
673
|
+
TestRelation(*nf2_n2_f210_s210ab_, *south_210b_, true, false, true);
|
|
674
|
+
TestRelation(*nf2_n2_f210_s210ab_, *south_0ab_, true, false, true);
|
|
675
|
+
TestRelation(*nf2_n2_f210_s210ab_, *n32_s0b_, true, false, true);
|
|
676
|
+
|
|
677
|
+
TestRelation(*cross1_, *cross2_, false, false, true);
|
|
678
|
+
TestRelation(*cross1_side_hole_, *cross2_, false, false, true);
|
|
679
|
+
TestRelation(*cross1_center_hole_, *cross2_, false, false, true);
|
|
680
|
+
TestRelation(*cross1_, *cross2_side_hole_, false, false, true);
|
|
681
|
+
TestRelation(*cross1_, *cross2_center_hole_, false, false, true);
|
|
682
|
+
TestRelation(*cross1_side_hole_, *cross2_side_hole_, false, false, true);
|
|
683
|
+
TestRelation(*cross1_center_hole_, *cross2_side_hole_, false, false, true);
|
|
684
|
+
TestRelation(*cross1_side_hole_, *cross2_center_hole_, false, false, true);
|
|
685
|
+
TestRelation(*cross1_center_hole_, *cross2_center_hole_, false, false, true);
|
|
686
|
+
|
|
687
|
+
// These cases_, when either polygon has a hole, test a different code path
|
|
688
|
+
// from the other cases.
|
|
689
|
+
TestRelation(*overlap1_, *overlap2_, false, false, true);
|
|
690
|
+
TestRelation(*overlap1_side_hole_, *overlap2_, false, false, true);
|
|
691
|
+
TestRelation(*overlap1_center_hole_, *overlap2_, false, false, true);
|
|
692
|
+
TestRelation(*overlap1_, *overlap2_side_hole_, false, false, true);
|
|
693
|
+
TestRelation(*overlap1_, *overlap2_center_hole_, false, false, true);
|
|
694
|
+
TestRelation(*overlap1_side_hole_, *overlap2_side_hole_, false, false, true);
|
|
695
|
+
TestRelation(*overlap1_center_hole_, *overlap2_side_hole_,
|
|
696
|
+
false, false, true);
|
|
697
|
+
TestRelation(*overlap1_side_hole_, *overlap2_center_hole_,
|
|
698
|
+
false, false, true);
|
|
699
|
+
TestRelation(*overlap1_center_hole_, *overlap2_center_hole_,
|
|
700
|
+
false, false, true);
|
|
701
|
+
#undef TestRelation
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
TEST_F(S2PolygonTestBase, EmptyAndFull) {
|
|
705
|
+
EXPECT_TRUE(empty_->is_empty());
|
|
706
|
+
EXPECT_FALSE(full_->is_empty());
|
|
707
|
+
EXPECT_FALSE(empty_->is_full());
|
|
708
|
+
EXPECT_TRUE(full_->is_full());
|
|
709
|
+
|
|
710
|
+
TestNestedPair(*empty_, *empty_);
|
|
711
|
+
TestNestedPair(*full_, *empty_);
|
|
712
|
+
TestNestedPair(*full_, *full_);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
struct TestCase {
|
|
716
|
+
const char* a;
|
|
717
|
+
const char* b;
|
|
718
|
+
const char* a_and_b;
|
|
719
|
+
const char* a_or_b;
|
|
720
|
+
const char* a_minus_b;
|
|
721
|
+
const char* a_xor_b;
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
TestCase test_cases[] = {
|
|
725
|
+
// Two triangles that share an edge.
|
|
726
|
+
{ "4:2, 3:1, 3:3;",
|
|
727
|
+
|
|
728
|
+
"3:1, 2:2, 3:3;",
|
|
729
|
+
|
|
730
|
+
"", // and
|
|
731
|
+
|
|
732
|
+
"4:2, 3:1, 2:2, 3:3;", // or
|
|
733
|
+
|
|
734
|
+
"4:2, 3:1, 3:3;", // minus
|
|
735
|
+
|
|
736
|
+
"4:2, 3:1, 2:2, 3:3;" // xor
|
|
737
|
+
},
|
|
738
|
+
|
|
739
|
+
// Two vertical bars and a horizontal bar connecting them.
|
|
740
|
+
{ "0:0, 0:2, 3:2, 3:0; 0:3, 0:5, 3:5, 3:3;",
|
|
741
|
+
|
|
742
|
+
"1:1, 1:4, 2:4, 2:1;",
|
|
743
|
+
|
|
744
|
+
"1:1, 1:2, 2:2, 2:1; 1:3, 1:4, 2:4, 2:3;", // and
|
|
745
|
+
|
|
746
|
+
"0:0, 0:2, 1:2, 1:3, 0:3, 0:5, 3:5, 3:3, 2:3, 2:2, 3:2, 3:0;", // or
|
|
747
|
+
|
|
748
|
+
"0:0, 0:2, 1:2, 1:1, 2:1, 2:2, 3:2, 3:0; " // minus
|
|
749
|
+
"0:3, 0:5, 3:5, 3:3, 2:3, 2:4, 1:4, 1:3;",
|
|
750
|
+
|
|
751
|
+
"0:0, 0:2, 1:2, 1:1, 2:1, 2:2, 3:2, 3:0; " // xor
|
|
752
|
+
"0:3, 0:5, 3:5, 3:3, 2:3, 2:4, 1:4, 1:3; "
|
|
753
|
+
"1:2, 1:3, 2:3, 2:2"
|
|
754
|
+
},
|
|
755
|
+
|
|
756
|
+
// Two vertical bars and two horizontal bars.
|
|
757
|
+
{ "1:88, 1:93, 2:93, 2:88; -1:88, -1:93, 0:93, 0:88;",
|
|
758
|
+
|
|
759
|
+
"-2:89, -2:90, 3:90, 3:89; -2:91, -2:92, 3:92, 3:91;",
|
|
760
|
+
|
|
761
|
+
"1:89, 1:90, 2:90, 2:89; 1:91, 1:92, 2:92, 2:91; " // and
|
|
762
|
+
"-1:89, -1:90, 0:90, 0:89; -1:91, -1:92, 0:92, 0:91;",
|
|
763
|
+
|
|
764
|
+
"-1:88, -1:89, -2:89, -2:90, -1:90, -1:91, -2:91, -2:92, -1:92, " // or
|
|
765
|
+
"-1:93, 0:93, 0:92, 1:92, 1:93, 2:93, 2:92, 3:92, 3:91, 2:91, "
|
|
766
|
+
"2:90, 3:90, 3:89, 2:89, 2:88, 1:88, 1:89, 0:89, 0:88; "
|
|
767
|
+
"0:90, 0:91, 1:91, 1:90;",
|
|
768
|
+
|
|
769
|
+
"1:88, 1:89, 2:89, 2:88; 1:90, 1:91, 2:91, 2:90; " // minus
|
|
770
|
+
"1:92, 1:93, 2:93, 2:92; -1:88, -1:89, 0:89, 0:88; "
|
|
771
|
+
"-1:90, -1:91, 0:91, 0:90; -1:92, -1:93, 0:93, 0:92;",
|
|
772
|
+
|
|
773
|
+
"1:88, 1:89, 2:89, 2:88; -1:88, -1:89, 0:89, 0:88; " // xor
|
|
774
|
+
"1:90, 1:91, 2:91, 2:90; -1:90, -1:91, 0:91, 0:90; "
|
|
775
|
+
"1:92, 1:93, 2:93, 2:92; -1:92, -1:93, 0:93, 0:92; "
|
|
776
|
+
"-2:89, -2:90, -1:90, -1:89; -2:91, -2:92, -1:92, -1:91; "
|
|
777
|
+
"0:89, 0:90, 1:90, 1:89; 0:91, 0:92, 1:92, 1:91; "
|
|
778
|
+
"2:89, 2:90, 3:90, 3:89; 2:91, 2:92, 3:92, 3:91;"
|
|
779
|
+
},
|
|
780
|
+
|
|
781
|
+
// Two interlocking square doughnuts.
|
|
782
|
+
{ "-1:-93, -1:-89, 3:-89, 3:-93; 0:-92, 0:-90, 2:-90, 2:-92;",
|
|
783
|
+
|
|
784
|
+
"-3:-91, -3:-87, 1:-87, 1:-91; -2:-90, -2:-88, 0:-88, 0:-90;",
|
|
785
|
+
|
|
786
|
+
"-1:-91, -1:-90, 0:-90, 0:-91; 0:-90, 0:-89, 1:-89, 1:-90;", // and
|
|
787
|
+
|
|
788
|
+
"-1:-93, -1:-91, -3:-91, -3:-87, 1:-87, 1:-89, 3:-89, 3:-93; " // or
|
|
789
|
+
"0:-92, 0:-91, 1:-91, 1:-90, 2:-90, 2:-92; "
|
|
790
|
+
"-2:-90, -2:-88, 0:-88, 0:-89, -1:-89, -1:-90;",
|
|
791
|
+
|
|
792
|
+
"-1:-93, -1:-91, 0:-91, 0:-92, 2:-92, 2:-90, " // minus
|
|
793
|
+
"1:-90, 1:-89, 3:-89, 3:-93; "
|
|
794
|
+
"-1:-90, -1:-89, 0:-89, 0:-90;",
|
|
795
|
+
|
|
796
|
+
"-1:-93, -1:-91, 0:-91, 0:-92, 2:-92, 2:-90, " // xor
|
|
797
|
+
"1:-90, 1:-89, 3:-89, 3:-93; "
|
|
798
|
+
"-3:-91, -3:-87, 1:-87, 1:-89, 0:-89, 0:-88, "
|
|
799
|
+
"-2:-88, -2:-90, -1:-90, -1:-91; "
|
|
800
|
+
"-1:-90, -1:-89, 0:-89, 0:-90; "
|
|
801
|
+
"1:-91, 0:-91, 0:-90, 1:-90;"
|
|
802
|
+
},
|
|
803
|
+
|
|
804
|
+
// An incredibly thin triangle intersecting a square, such that the two
|
|
805
|
+
// intersection points of the triangle with the square are identical.
|
|
806
|
+
// This results in a degenerate loop that needs to be handled correctly.
|
|
807
|
+
{ "10:44, 10:46, 12:46, 12:44;",
|
|
808
|
+
|
|
809
|
+
"11:45, 89:45.00000000000001, 90:45;",
|
|
810
|
+
|
|
811
|
+
"", // Empty intersection!
|
|
812
|
+
|
|
813
|
+
// Original square with extra vertex, and triangle disappears (due to
|
|
814
|
+
// default vertex_merge_radius of S2::kIntersectionMergeRadius).
|
|
815
|
+
"10:44, 10:46, 12:46, 12:45.001774937, 12:44;", // or
|
|
816
|
+
|
|
817
|
+
"10:44, 10:46, 12:46, 12:45.001774937, 12:44;", // minus
|
|
818
|
+
|
|
819
|
+
"10:44, 10:46, 12:46, 12:45.001774937, 12:44;", // xor
|
|
820
|
+
},
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
TEST_F(S2PolygonTestBase, Operations) {
|
|
824
|
+
S2Polygon far_south;
|
|
825
|
+
far_south.InitToIntersection(far_H_.get(), south_H_.get());
|
|
826
|
+
CheckEqual(far_south, *far_H_south_H_, S1Angle::Radians(1e-15));
|
|
827
|
+
|
|
828
|
+
int i = 0;
|
|
829
|
+
for (const TestCase& test : test_cases) {
|
|
830
|
+
SCOPED_TRACE(StrCat("Polygon operation test case ", i++));
|
|
831
|
+
unique_ptr<S2Polygon> a(MakePolygon(test.a));
|
|
832
|
+
unique_ptr<S2Polygon> b(MakePolygon(test.b));
|
|
833
|
+
unique_ptr<S2Polygon> expected_a_and_b(MakePolygon(test.a_and_b));
|
|
834
|
+
unique_ptr<S2Polygon> expected_a_or_b(MakePolygon(test.a_or_b));
|
|
835
|
+
unique_ptr<S2Polygon> expected_a_minus_b(MakePolygon(test.a_minus_b));
|
|
836
|
+
unique_ptr<S2Polygon> expected_a_xor_b(MakePolygon(test.a_xor_b));
|
|
837
|
+
|
|
838
|
+
// The intersections in the "expected" data were computed in lat-lng
|
|
839
|
+
// space, while the actual intersections are computed using geodesics.
|
|
840
|
+
// The error due to this depends on the length and direction of the line
|
|
841
|
+
// segment being intersected, and how close the intersection is to the
|
|
842
|
+
// endpoints of the segment. The worst case is for a line segment between
|
|
843
|
+
// two points at the same latitude, where the intersection point is in the
|
|
844
|
+
// middle of the segment. In this case the error is approximately
|
|
845
|
+
// (p * t^2) / 8, where "p" is the absolute latitude in radians, "t" is
|
|
846
|
+
// the longitude difference in radians, and both "p" and "t" are small.
|
|
847
|
+
// The test cases all have small latitude and longitude differences.
|
|
848
|
+
// If "p" and "t" are converted to degrees, the following error bound is
|
|
849
|
+
// valid as long as (p * t^2 < 150).
|
|
850
|
+
|
|
851
|
+
static const S1Angle kMaxError = S1Angle::Radians(1e-4);
|
|
852
|
+
|
|
853
|
+
S2Polygon a_and_b, a_or_b, a_minus_b, a_xor_b;
|
|
854
|
+
a_and_b.InitToIntersection(a.get(), b.get());
|
|
855
|
+
CheckEqual(a_and_b, *expected_a_and_b, kMaxError);
|
|
856
|
+
a_or_b.InitToUnion(a.get(), b.get());
|
|
857
|
+
CheckEqual(a_or_b, *expected_a_or_b, kMaxError);
|
|
858
|
+
TestDestructiveUnion(*a, *b);
|
|
859
|
+
a_minus_b.InitToDifference(a.get(), b.get());
|
|
860
|
+
CheckEqual(a_minus_b, *expected_a_minus_b, kMaxError);
|
|
861
|
+
a_xor_b.InitToSymmetricDifference(a.get(), b.get());
|
|
862
|
+
CheckEqual(a_xor_b, *expected_a_xor_b, kMaxError);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
TEST(S2Polygon, IntersectionSnapFunction) {
|
|
867
|
+
// This tests that an intersection point is rounded to the nearest allowable
|
|
868
|
+
// vertex position (using E0 coordinates, i.e. integer lat/lng values).
|
|
869
|
+
unique_ptr<S2Polygon> a = MakePolygon("0:0, 0:10, 1:10, 1:0");
|
|
870
|
+
unique_ptr<S2Polygon> b = MakePolygon("0:0, 0:10, 3:0");
|
|
871
|
+
unique_ptr<S2Polygon> expected = MakePolygon("0:0, 0:10, 1:7, 1:0");
|
|
872
|
+
S2Polygon actual;
|
|
873
|
+
actual.InitToIntersection(*a, *b, IntLatLngSnapFunction(0)); // E0 coords
|
|
874
|
+
CheckEqual(*expected, actual);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
TEST(S2Polygon, IntersectionPreservesLoopOrder) {
|
|
878
|
+
unique_ptr<S2Polygon> a = MakePolygon("0:0, 0:10, 10:10, 10:0");
|
|
879
|
+
unique_ptr<S2Polygon> b = MakePolygon("1:1, 1:9, 9:5; 2:2, 2:8, 8:5");
|
|
880
|
+
S2Polygon actual;
|
|
881
|
+
actual.InitToIntersection(a.get(), b.get());
|
|
882
|
+
EXPECT_EQ(s2textformat::ToString(*b), s2textformat::ToString(actual));
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Verifies that S2Polygon does not destroy or replace pointers to S2Loop, so
|
|
886
|
+
// caller can rely on using raw pointers.
|
|
887
|
+
TEST(S2Polygon, LoopPointers) {
|
|
888
|
+
vector<unique_ptr<S2Loop>> loops;
|
|
889
|
+
loops.emplace_back(s2textformat::MakeLoop("4:4, 4:6, 6:6, 6:4"));
|
|
890
|
+
loops.emplace_back(s2textformat::MakeLoop("3:3, 3:7, 7:7, 7:3"));
|
|
891
|
+
loops.emplace_back(s2textformat::MakeLoop("2:2, 2:8, 8:8, 8:2"));
|
|
892
|
+
loops.emplace_back(s2textformat::MakeLoop("1:1, 1:9, 9:9, 9:1"));
|
|
893
|
+
loops.emplace_back(s2textformat::MakeLoop("10:10, 15:15, 20:10"));
|
|
894
|
+
loops.emplace_back(s2textformat::MakeLoop("-1:-1, -9:-1, -9:-9, -1:-9"));
|
|
895
|
+
loops.emplace_back(s2textformat::MakeLoop("-5:-5, -6:-5, -6:-6, -5:-6"));
|
|
896
|
+
|
|
897
|
+
std::set<const S2Loop*> loops_raw_ptrs;
|
|
898
|
+
for (auto& loop : loops) {
|
|
899
|
+
loops_raw_ptrs.insert(loop.get());
|
|
900
|
+
}
|
|
901
|
+
S2Polygon polygon(std::move(loops));
|
|
902
|
+
|
|
903
|
+
// Check that loop pointers didn't change (but could've gotten reordered).
|
|
904
|
+
EXPECT_EQ(loops_raw_ptrs.size(), polygon.num_loops());
|
|
905
|
+
for (int i = 0; i < polygon.num_loops(); i++) {
|
|
906
|
+
EXPECT_EQ(1, loops_raw_ptrs.count(polygon.loop(i))) << "loop " << i;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
static vector<unique_ptr<S2Loop>> MakeLoops(
|
|
911
|
+
const vector<vector<S2Point>>& loop_vertices) {
|
|
912
|
+
vector<unique_ptr<S2Loop>> result;
|
|
913
|
+
for (const auto& vertices : loop_vertices) {
|
|
914
|
+
result.emplace_back(new S2Loop(vertices));
|
|
915
|
+
S2Error error;
|
|
916
|
+
EXPECT_FALSE(result.back()->FindValidationError(&error))
|
|
917
|
+
<< "Loop " << result.size() - 1 << ": " << error;
|
|
918
|
+
}
|
|
919
|
+
return result;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// The "Bug" tests are regression tests from previous versions of the algorithm.
|
|
923
|
+
TEST(S2Polygon, Bug1) {
|
|
924
|
+
vector<vector<S2Point>> a_vertices = {
|
|
925
|
+
{
|
|
926
|
+
{-0.10531193335759943, -0.80522214810955617, 0.58354664670985534},
|
|
927
|
+
{-0.10531194840431297, -0.80522215192439039, 0.58354663873039425},
|
|
928
|
+
{-0.10531192794033867, -0.80522217497559767, 0.58354661061568747},
|
|
929
|
+
{-0.10531191284235047, -0.80522217121852058, 0.58354661852470402}
|
|
930
|
+
},
|
|
931
|
+
};
|
|
932
|
+
vector<vector<S2Point>> b_vertices = {
|
|
933
|
+
{
|
|
934
|
+
{-0.10531174240075937, -0.80522236320875284, 0.58354638436119843},
|
|
935
|
+
{-0.1053119128423491, -0.80522217121852213, 0.58354661852470235},
|
|
936
|
+
{-0.10531192039134209, -0.80522217309706012, 0.58354661457019508}, // A
|
|
937
|
+
{-0.10531191288915481, -0.80522217116640804, 0.5835466185881667}, // B
|
|
938
|
+
{-0.10531191288915592, -0.8052221711664066, 0.58354661858816803}, // B
|
|
939
|
+
{-0.10531192039151964, -0.80522217309710431, 0.58354661457010204}, // A
|
|
940
|
+
{-0.10531192794033779, -0.80522217497559878, 0.58354661061568636},
|
|
941
|
+
{-0.1053117575499668, -0.80522236690813498, 0.58354637652254981},
|
|
942
|
+
},
|
|
943
|
+
};
|
|
944
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
945
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
946
|
+
S2Polygon c;
|
|
947
|
+
c.InitToUnion(&a, &b);
|
|
948
|
+
// Given edges do not form loops (indegree != outdegree)
|
|
949
|
+
EXPECT_FALSE(c.is_empty())
|
|
950
|
+
<< "\nS2Polygon: " << s2textformat::ToString(a)
|
|
951
|
+
<< "\nS2Polygon: " << s2textformat::ToString(b);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
TEST(S2Polygon, Bug2) {
|
|
955
|
+
vector<vector<S2Point>> a_vertices = {
|
|
956
|
+
{
|
|
957
|
+
{-0.10618951389689163, -0.80546461394606728, 0.58305277875939732},
|
|
958
|
+
{-0.10618904764039243, -0.8054645437464607, 0.58305296065497536},
|
|
959
|
+
{-0.10618862643748632, -0.80546451917975415, 0.58305307130470341},
|
|
960
|
+
{-0.10617606798507535, -0.80544758470051458, 0.58307875187433833},
|
|
961
|
+
},
|
|
962
|
+
};
|
|
963
|
+
vector<vector<S2Point>> b_vertices = {
|
|
964
|
+
{
|
|
965
|
+
{-0.10618668131028208, -0.80544613076731553, 0.58307882755616247},
|
|
966
|
+
{-0.10618910658843225, -0.80546454998744921, 0.58305294129732887},
|
|
967
|
+
{-0.10618904764039225, -0.80546454374646081, 0.58305296065497536},
|
|
968
|
+
{-0.10618898834264634, -0.80546453817003949, 0.58305297915823251},
|
|
969
|
+
},
|
|
970
|
+
};
|
|
971
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
972
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
973
|
+
S2Polygon c;
|
|
974
|
+
c.InitToUnion(&a, &b);
|
|
975
|
+
// Given edges do not form loops (indegree != outdegree)
|
|
976
|
+
EXPECT_FALSE(c.is_empty())
|
|
977
|
+
<< "\nS2Polygon: " << s2textformat::ToString(a)
|
|
978
|
+
<< "\nS2Polygon: " << s2textformat::ToString(b);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
TEST(S2Polygon, Bug3) {
|
|
982
|
+
vector<vector<S2Point>> a_vertices = {
|
|
983
|
+
{
|
|
984
|
+
{-0.10703494861068318, -0.80542232562508131, 0.58295659972299307},
|
|
985
|
+
{-0.10703494998722708, -0.80542232255642865, 0.58295660370995028},
|
|
986
|
+
{-0.10703495367938694, -0.80542232008675829, 0.58295660644418046},
|
|
987
|
+
{-0.10703495869785147, -0.80542231887781635, 0.58295660719304865},
|
|
988
|
+
{-0.10703496369792719, -0.80542231925353791, 0.58295660575589636},
|
|
989
|
+
{-0.10703496733984781, -0.80542232111324863, 0.58295660251780734},
|
|
990
|
+
{-0.10703496864776367, -0.80542232395864055, 0.58295659834642488},
|
|
991
|
+
{-0.10703496727121976, -0.80542232702729322, 0.58295659435946767},
|
|
992
|
+
{-0.10703496357905991, -0.80542232949696357, 0.5829565916252375},
|
|
993
|
+
{-0.10703495856059538, -0.80542233070590552, 0.58295659087636931},
|
|
994
|
+
{-0.10703495356051966, -0.80542233033018396, 0.58295659231352159},
|
|
995
|
+
{-0.10703494991859903, -0.80542232847047324, 0.58295659555161061},
|
|
996
|
+
},
|
|
997
|
+
};
|
|
998
|
+
vector<vector<S2Point>> b_vertices = {
|
|
999
|
+
{
|
|
1000
|
+
{-0.10703494861068762, -0.80542232562508098, 0.58295659972299274},
|
|
1001
|
+
{-0.10703494998723152, -0.80542232255642832, 0.58295660370994995},
|
|
1002
|
+
{-0.10703495367939138, -0.80542232008675796, 0.58295660644418013},
|
|
1003
|
+
{-0.10703495869785591, -0.80542231887781601, 0.58295660719304832},
|
|
1004
|
+
{-0.10703496369793163, -0.80542231925353758, 0.58295660575589603},
|
|
1005
|
+
{-0.10703496733985225, -0.8054223211132483, 0.58295660251780701},
|
|
1006
|
+
{-0.10703496864776811, -0.80542232395864022, 0.58295659834642455},
|
|
1007
|
+
{-0.1070349672712242, -0.80542232702729288, 0.58295659435946734},
|
|
1008
|
+
{-0.10703496357906438, -0.80542232949696346, 0.58295659162523727},
|
|
1009
|
+
{-0.10703495856059982, -0.80542233070590519, 0.58295659087636897},
|
|
1010
|
+
{-0.1070349535605241, -0.80542233033018362, 0.58295659231352126},
|
|
1011
|
+
{-0.10703494991860348, -0.8054223284704729, 0.58295659555161028},
|
|
1012
|
+
},
|
|
1013
|
+
};
|
|
1014
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
1015
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
1016
|
+
S2Polygon c;
|
|
1017
|
+
c.InitToUnion(&a, &b);
|
|
1018
|
+
// Given edges do not form loops (indegree != outdegree)
|
|
1019
|
+
EXPECT_FALSE(c.is_empty())
|
|
1020
|
+
<< "\nS2Polygon: " << s2textformat::ToString(a)
|
|
1021
|
+
<< "\nS2Polygon: " << s2textformat::ToString(b);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
TEST(S2Polygon, Bug4) {
|
|
1025
|
+
vector<vector<S2Point>> a_vertices = {
|
|
1026
|
+
{
|
|
1027
|
+
{-0.10667065556339718, -0.80657502337947207, 0.58142764201754193},
|
|
1028
|
+
{-0.10667064691895933, -0.80657502457251051, 0.58142764194845853},
|
|
1029
|
+
{-0.10667064691930939, -0.80657502457246333, 0.58142764194845975},
|
|
1030
|
+
{-0.10667065556339746, -0.80657502337947395, 0.5814276420175396},
|
|
1031
|
+
{-0.10667077559567185, -0.80657589269604968, 0.58142641405029793},
|
|
1032
|
+
{-0.10667077059539463, -0.80657589232162286, 0.58142641548708696},
|
|
1033
|
+
{-0.10667063827452879, -0.80657502576554818, 0.58142764187937435},
|
|
1034
|
+
{-0.10667063169531328, -0.80657498170361974, 0.58142770421053058},
|
|
1035
|
+
{-0.10667064898418178, -0.8065749793175444, 0.58142770434869739},
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
{-0.10667064691897719, -0.80657502457250896, 0.58142764194845697},
|
|
1039
|
+
{-0.10667063827452879, -0.80657502576554818, 0.58142764187937435},
|
|
1040
|
+
{-0.10667064691861985, -0.80657502457255736, 0.58142764194845586},
|
|
1041
|
+
},
|
|
1042
|
+
};
|
|
1043
|
+
vector<vector<S2Point>> b_vertices = {
|
|
1044
|
+
{
|
|
1045
|
+
{-0.10667064691896312, -0.80657502457251107, 0.58142764194845697},
|
|
1046
|
+
{-0.10667064691896297, -0.80657502457251007, 0.58142764194845853},
|
|
1047
|
+
{-0.10667064033974753, -0.80657498051058207, 0.58142770427961399},
|
|
1048
|
+
{-0.10667064076268165, -0.80657498045444342, 0.58142770427989865},
|
|
1049
|
+
{-0.10667051785242875, -0.80657409963649807, 0.58142894872603923},
|
|
1050
|
+
{-0.1066707756642685, -0.80657588679775971, 0.58142642222003538},
|
|
1051
|
+
},
|
|
1052
|
+
};
|
|
1053
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
1054
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
1055
|
+
S2Polygon c;
|
|
1056
|
+
c.InitToUnion(&a, &b);
|
|
1057
|
+
// Loop 1: Edge 1 crosses edge 3
|
|
1058
|
+
EXPECT_FALSE(c.is_empty())
|
|
1059
|
+
<< "\nS2Polygon: " << s2textformat::ToString(a)
|
|
1060
|
+
<< "\nS2Polygon: " << s2textformat::ToString(b);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
TEST(S2Polygon, Bug5) {
|
|
1064
|
+
vector<vector<S2Point>> a_vertices = {
|
|
1065
|
+
{
|
|
1066
|
+
{-0.10574444273627338, -0.80816264611829447, 0.57938868667714882},
|
|
1067
|
+
{-0.10574444845633162, -0.80816268110163325, 0.57938863683652475},
|
|
1068
|
+
{-0.10574444825833453, -0.80816268112970524, 0.57938863683350494},
|
|
1069
|
+
{-0.10574444253827629, -0.80816264614636646, 0.57938868667412902},
|
|
1070
|
+
{-0.10574408792844124, -0.80816047738475361, 0.57939177648757634},
|
|
1071
|
+
{-0.10574408812643833, -0.80816047735668162, 0.57939177649059592},
|
|
1072
|
+
},
|
|
1073
|
+
};
|
|
1074
|
+
vector<vector<S2Point>> b_vertices = {
|
|
1075
|
+
{
|
|
1076
|
+
{-0.1057440881264381, -0.80816047735668017, 0.57939177649059825},
|
|
1077
|
+
{-0.10574408802743954, -0.80816047737071606, 0.57939177648908835},
|
|
1078
|
+
{-0.10574408812649677, -0.8081604773570521, 0.57939177649006868},
|
|
1079
|
+
{-0.10574408812649701, -0.80816047735705354, 0.57939177649006646},
|
|
1080
|
+
{-0.10574408802703171, -0.80816047737077379, 0.57939177648908202},
|
|
1081
|
+
{-0.10574408792844098, -0.80816047738475194, 0.57939177648757834},
|
|
1082
|
+
{-0.10574408792838257, -0.80816047738438168, 0.5793917764881058},
|
|
1083
|
+
{-0.1057440879283823, -0.80816047738438002, 0.57939177648810791},
|
|
1084
|
+
{-0.10574407993470979, -0.80816042849578984, 0.57939184613891748},
|
|
1085
|
+
{-0.10574408013270691, -0.80816042846771807, 0.57939184614193739},
|
|
1086
|
+
},
|
|
1087
|
+
};
|
|
1088
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
1089
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
1090
|
+
S2Polygon c;
|
|
1091
|
+
c.InitToUnion(&a, &b);
|
|
1092
|
+
// Loop 0 edge 8 crosses loop 1 edge 0
|
|
1093
|
+
EXPECT_FALSE(c.is_empty())
|
|
1094
|
+
<< "\nS2Polygon: " << s2textformat::ToString(a)
|
|
1095
|
+
<< "\nS2Polygon: " << s2textformat::ToString(b);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
TEST(S2Polygon, Bug6) {
|
|
1099
|
+
vector<vector<S2Point>> a_vertices = {
|
|
1100
|
+
{
|
|
1101
|
+
{-0.10618849949725141, -0.80552159562437586, 0.58297423747304822},
|
|
1102
|
+
{-0.10618849959636036, -0.80552159561106063, 0.58297423747339361},
|
|
1103
|
+
{-0.10618849949722192, -0.80552159562415893, 0.5829742374733532},
|
|
1104
|
+
{-0.10618834540082922, -0.80552043435619214, 0.58297587011440333},
|
|
1105
|
+
{-0.10618834559910612, -0.80552043432999554, 0.58297587011448437},
|
|
1106
|
+
{-0.10618849969546933, -0.80552159559774539, 0.58297423747373922},
|
|
1107
|
+
{-0.10618849969546955, -0.80552159559774716, 0.582974237473737},
|
|
1108
|
+
{-0.10618849969549882, -0.80552159559796233, 0.58297423747343424},
|
|
1109
|
+
{-0.10618849959710704, -0.80552159561096182, 0.58297423747339394},
|
|
1110
|
+
{-0.10618849949725161, -0.80552159562437742, 0.58297423747304589},
|
|
1111
|
+
},
|
|
1112
|
+
};
|
|
1113
|
+
vector<vector<S2Point>> b_vertices = {
|
|
1114
|
+
{
|
|
1115
|
+
{-0.10618856154870562, -0.80552206324314812, 0.58297358004005528},
|
|
1116
|
+
{-0.10618849949722212, -0.80552159562416048, 0.58297423747335086},
|
|
1117
|
+
{-0.10618849969549901, -0.80552159559796388, 0.58297423747343191},
|
|
1118
|
+
{-0.10618856174698249, -0.8055220632169513, 0.58297358004013622},
|
|
1119
|
+
{-0.10618857104277038, -0.80552213326985989, 0.58297348155149287},
|
|
1120
|
+
{-0.10618857084449349, -0.80552213329605649, 0.58297348155141182},
|
|
1121
|
+
},
|
|
1122
|
+
};
|
|
1123
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
1124
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
1125
|
+
S2Polygon c;
|
|
1126
|
+
c.InitToUnion(&a, &b);
|
|
1127
|
+
// Loop 0 edge 0 crosses loop 1 edge 4
|
|
1128
|
+
EXPECT_FALSE(c.is_empty())
|
|
1129
|
+
<< "\nS2Polygon: " << s2textformat::ToString(a)
|
|
1130
|
+
<< "\nS2Polygon: " << s2textformat::ToString(b);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
TEST(S2Polygon, Bug7) {
|
|
1134
|
+
vector<vector<S2Point>> a_vertices = {
|
|
1135
|
+
{
|
|
1136
|
+
{-0.10651728339354898, -0.80806023027835039, 0.57938996589599123},
|
|
1137
|
+
{-0.10651728368541774, -0.80806023024121265, 0.57938996589412783},
|
|
1138
|
+
{-0.10651743884289547, -0.80806147782022508, 0.5793881973990701},
|
|
1139
|
+
{-0.1065172793067945, -0.80806153133252501, 0.5793881520963412},
|
|
1140
|
+
{-0.10651707335497011, -0.80806158532388361, 0.57938811465868356},
|
|
1141
|
+
{-0.10651593657771009, -0.80806167503227055, 0.57938819853274059},
|
|
1142
|
+
{-0.10651567693742285, -0.80806182530835402, 0.57938803667826444},
|
|
1143
|
+
{-0.10651496089498214, -0.80806213485510237, 0.57938773659696563},
|
|
1144
|
+
{-0.10651453461919227, -0.80806229235522298, 0.57938759530083062},
|
|
1145
|
+
{-0.10651448583749658, -0.80806230280784852, 0.57938758969074455},
|
|
1146
|
+
{-0.10651428153471061, -0.80806061225022852, 0.57938998503506256},
|
|
1147
|
+
{-0.10651428161845182, -0.8080606122395747, 0.57938998503452654},
|
|
1148
|
+
{-0.10651427761078044, -0.80806057978063328, 0.57939003104095654},
|
|
1149
|
+
{-0.10651427761077951, -0.80806057978062562, 0.57939003104096709},
|
|
1150
|
+
{-0.10651387099203104, -0.8080572864940091, 0.5793946988282096},
|
|
1151
|
+
{-0.10651387099202798, -0.80805728649398445, 0.57939469882824468},
|
|
1152
|
+
{-0.10651386444607201, -0.80805723347699177, 0.57939477397218053},
|
|
1153
|
+
{-0.10651386444607169, -0.8080572334769891, 0.57939477397218409},
|
|
1154
|
+
{-0.106513765993723, -0.80805643609199118, 0.57939590414857456},
|
|
1155
|
+
{-0.10651376671438624, -0.8080564359989727, 0.57939590414581921},
|
|
1156
|
+
{-0.10651368187839319, -0.80805575808078389, 0.57939686520139033},
|
|
1157
|
+
{-0.10651465698432123, -0.80805552598235797, 0.57939700963750851},
|
|
1158
|
+
{-0.1065149024434091, -0.80805548225095913, 0.57939702550292815},
|
|
1159
|
+
{-0.10651504788182964, -0.80805555533715756, 0.5793968968362615},
|
|
1160
|
+
{-0.10651511658091152, -0.80805559604710031, 0.57939682743066534},
|
|
1161
|
+
{-0.10651517919248171, -0.80805562751022852, 0.57939677204023521},
|
|
1162
|
+
{-0.10651528575974038, -0.80805561374213786, 0.57939677165077275},
|
|
1163
|
+
{-0.10651648823358072, -0.80805539171529139, 0.57939686023850034},
|
|
1164
|
+
{-0.10651666406737116, -0.80805537863686483, 0.57939684615295572},
|
|
1165
|
+
{-0.10651674780673852, -0.80805605121551227, 0.57939589274577097},
|
|
1166
|
+
{-0.10651674667750256, -0.80805605136137271, 0.57939589274994641},
|
|
1167
|
+
{-0.10651678418140036, -0.80805634336988752, 0.57939547860450136},
|
|
1168
|
+
{-0.10651680240261223, -0.80805648524178364, 0.57939527739240138},
|
|
1169
|
+
{-0.10651680240261237, -0.80805648524178486, 0.57939527739239993},
|
|
1170
|
+
},
|
|
1171
|
+
};
|
|
1172
|
+
vector<vector<S2Point>> b_vertices = {
|
|
1173
|
+
{
|
|
1174
|
+
{-0.10651727337444802, -0.80806023111043901, 0.57938996657744879},
|
|
1175
|
+
{-0.10651727440799089, -0.80806022882029649, 0.57938996958144073},
|
|
1176
|
+
{-0.10651679374955145, -0.80805648637258243, 0.57939527740611751},
|
|
1177
|
+
{-0.10651677552833975, -0.80805634450068775, 0.57939547861821594},
|
|
1178
|
+
{-0.10651673802444192, -0.80805605249217261, 0.57939589276366099},
|
|
1179
|
+
{-0.10651674651102909, -0.80805605138312775, 0.5793958927502102},
|
|
1180
|
+
{-0.10651673915225639, -0.80805605233507238, 0.57939589277542292},
|
|
1181
|
+
{-0.10651665541288889, -0.80805537975642383, 0.57939684618260878},
|
|
1182
|
+
{-0.10651667272185343, -0.80805537751730583, 0.57939684612330267},
|
|
1183
|
+
{-0.1065167564612207, -0.8080560500959526, 0.57939589271611924},
|
|
1184
|
+
{-0.1065167553320342, -0.80805605024202609, 0.57939589271998793},
|
|
1185
|
+
{-0.10651679283446101, -0.80805634223908773, 0.57939547859078699},
|
|
1186
|
+
{-0.10651681105567287, -0.80805648411098374, 0.57939527737868723},
|
|
1187
|
+
{-0.10651680240318392, -0.80805648524170914, 0.5793952773924006},
|
|
1188
|
+
{-0.10651680240261234, -0.80805648524178475, 0.57939527739239982},
|
|
1189
|
+
{-0.1065168110556733, -0.80805648411098718, 0.57939527737868224},
|
|
1190
|
+
{-0.10651729169518892, -0.80806022641135866, 0.57938996976297907},
|
|
1191
|
+
{-0.10651729210462238, -0.80806022661896348, 0.579389969398166},
|
|
1192
|
+
{-0.1065172934126499, -0.80806022944626155, 0.57938996521453356},
|
|
1193
|
+
{-0.10651729203606744, -0.80806023249651726, 0.57938996121349717},
|
|
1194
|
+
{-0.1065172883437291, -0.80806023495241674, 0.57938995846713126},
|
|
1195
|
+
{-0.10651728332499401, -0.80806023615590394, 0.5793899577113224},
|
|
1196
|
+
{-0.10651727832462815, -0.80806023578450537, 0.57938995914858893},
|
|
1197
|
+
{-0.10651727468247554, -0.80806023393773707, 0.57938996239381635},
|
|
1198
|
+
},
|
|
1199
|
+
{
|
|
1200
|
+
{-0.10651680240204828, -0.80805648524185858, 0.57939527739240082},
|
|
1201
|
+
{-0.10651679861449742, -0.80805648573682254, 0.57939527739840524},
|
|
1202
|
+
{-0.10651680240261419, -0.80805648524178353, 0.57939527739240138},
|
|
1203
|
+
},
|
|
1204
|
+
};
|
|
1205
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
1206
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
1207
|
+
S2Polygon c;
|
|
1208
|
+
c.InitToUnion(&a, &b);
|
|
1209
|
+
// Loop 0: Edge 33 crosses edge 35
|
|
1210
|
+
EXPECT_FALSE(c.is_empty())
|
|
1211
|
+
<< "\nS2Polygon: " << s2textformat::ToString(a)
|
|
1212
|
+
<< "\nS2Polygon: " << s2textformat::ToString(b);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
TEST(S2Polygon, Bug8) {
|
|
1216
|
+
vector<vector<S2Point>> a_vertices = {
|
|
1217
|
+
{
|
|
1218
|
+
{-0.10703872198218529, -0.80846112144645677, 0.57873424566545062},
|
|
1219
|
+
{-0.10703872122182066, -0.80846111957630917, 0.57873424841857957},
|
|
1220
|
+
{-0.10703873813385757, -0.80846111582010538, 0.57873425053786276},
|
|
1221
|
+
{-0.1070387388942222, -0.80846111769025297, 0.57873424778473381},
|
|
1222
|
+
{-0.10703873050793056, -0.80846111955286837, 0.57873424673382978},
|
|
1223
|
+
{-0.1070387388942227, -0.80846111769025419, 0.57873424778473193},
|
|
1224
|
+
{-0.10703919382477994, -0.80846223660916783, 0.57873260056976505},
|
|
1225
|
+
{-0.10703917691274406, -0.80846224036537406, 0.57873259845047831},
|
|
1226
|
+
},
|
|
1227
|
+
};
|
|
1228
|
+
vector<vector<S2Point>> b_vertices = {
|
|
1229
|
+
{
|
|
1230
|
+
{-0.10703917691274355, -0.80846224036537273, 0.57873259845047997},
|
|
1231
|
+
{-0.1070391853685064, -0.8084622384873289, 0.57873259951008804},
|
|
1232
|
+
{-0.10703919381027188, -0.80846223657409677, 0.57873260062144094},
|
|
1233
|
+
{-0.10703919381027233, -0.80846223657409788, 0.57873260062143939},
|
|
1234
|
+
{-0.10703918536876245, -0.80846223848727206, 0.57873259951012024},
|
|
1235
|
+
{-0.10703919382478132, -0.80846223660917116, 0.57873260056976017},
|
|
1236
|
+
{-0.10703957146434441, -0.80846316542623331, 0.57873123320737097},
|
|
1237
|
+
{-0.10703955455230836, -0.8084631691824391, 0.57873123108808489},
|
|
1238
|
+
},
|
|
1239
|
+
};
|
|
1240
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
1241
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
1242
|
+
S2_VLOG(1) << "\nS2Polygon: " << s2textformat::ToString(a);
|
|
1243
|
+
S2_VLOG(1) << "\nS2Polygon: " << s2textformat::ToString(b);
|
|
1244
|
+
S2Polygon c;
|
|
1245
|
+
c.InitToUnion(&a, &b);
|
|
1246
|
+
// Loop 1: Edge 1 crosses edge 3
|
|
1247
|
+
S2_VLOG(1) << "\nS2Polygon: " << s2textformat::ToString(c);
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
TEST(S2Polygon, Bug9) {
|
|
1251
|
+
vector<vector<S2Point>> a_vertices = {
|
|
1252
|
+
{
|
|
1253
|
+
{-0.10639937100501309, -0.80810205676564995, 0.57935329437301375},
|
|
1254
|
+
{-0.10639937101137514, -0.80810205688156922, 0.57935329421015713},
|
|
1255
|
+
{-0.10639937101137305, -0.80810205688156944, 0.57935329421015713},
|
|
1256
|
+
{-0.106399371005011, -0.80810205676565017, 0.57935329437301375},
|
|
1257
|
+
},
|
|
1258
|
+
};
|
|
1259
|
+
vector<vector<S2Point>> b_vertices = {
|
|
1260
|
+
{
|
|
1261
|
+
{-0.10639937099530022, -0.8081020567669569, 0.57935329437297489},
|
|
1262
|
+
{-0.10639937102108385, -0.80810205688026293, 0.5793532942101961},
|
|
1263
|
+
{-0.10639937102108181, -0.80810205688026326, 0.5793532942101961},
|
|
1264
|
+
{-0.10639937099529816, -0.80810205676695701, 0.57935329437297478},
|
|
1265
|
+
},
|
|
1266
|
+
};
|
|
1267
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
1268
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
1269
|
+
S2Polygon c;
|
|
1270
|
+
c.InitToUnion(&a, &b);
|
|
1271
|
+
// Given edges do not form loops (indegree != outdegree)
|
|
1272
|
+
EXPECT_FALSE(c.is_empty())
|
|
1273
|
+
<< "\nS2Polygon: " << s2textformat::ToString(a)
|
|
1274
|
+
<< "\nS2Polygon: " << s2textformat::ToString(b);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
TEST(S2Polygon, Bug10) {
|
|
1278
|
+
vector<vector<S2Point>> a_vertices = {
|
|
1279
|
+
{
|
|
1280
|
+
{-0.10592889932808099, -0.80701394501854917, 0.58095400922339757},
|
|
1281
|
+
{-0.10592787800899696, -0.8070140771413753, 0.58095401191158469},
|
|
1282
|
+
{-0.1059270044681431, -0.80701419014619669, 0.58095401421031945},
|
|
1283
|
+
{-0.10592685562894633, -0.80701420940058122, 0.58095401460194696},
|
|
1284
|
+
{-0.10592685502239066, -0.80701420947920588, 0.58095401460332308},
|
|
1285
|
+
{-0.10592681668594067, -0.80701421444855337, 0.5809540146902914},
|
|
1286
|
+
{-0.10592586497682262, -0.8070143378130904, 0.58095401684902004},
|
|
1287
|
+
{-0.10592586434121586, -0.80701433789547994, 0.58095401685046155},
|
|
1288
|
+
{-0.10592585898876766, -0.80701428569270217, 0.58095409034224832},
|
|
1289
|
+
{-0.10592585898876755, -0.80701428569270128, 0.58095409034224987},
|
|
1290
|
+
{-0.10592571912106936, -0.8070129215545373, 0.58095601078971082},
|
|
1291
|
+
{-0.10592571912106795, -0.80701292155452331, 0.58095601078973025},
|
|
1292
|
+
{-0.10592546626664477, -0.80701045545315664, 0.58095948256783148},
|
|
1293
|
+
{-0.10592546630689463, -0.80701045544795602, 0.58095948256771723},
|
|
1294
|
+
{-0.10592538513536764, -0.80700975616910509, 0.58096046873415197},
|
|
1295
|
+
{-0.10592564439344856, -0.80700971612782446, 0.58096047708524956},
|
|
1296
|
+
{-0.1059267844512099, -0.80700966174311928, 0.58096034476466896},
|
|
1297
|
+
{-0.10592686088387009, -0.80700965393230761, 0.58096034167862642},
|
|
1298
|
+
{-0.10592691331665709, -0.80700961093727019, 0.58096039184274961},
|
|
1299
|
+
{-0.10592705773734933, -0.80700947507458121, 0.58096055423665138},
|
|
1300
|
+
{-0.10592721940752658, -0.80700934249808198, 0.58096070892049412},
|
|
1301
|
+
{-0.10592756003095027, -0.80700933299293154, 0.58096066001769275},
|
|
1302
|
+
{-0.10592832507751106, -0.80700935762745474, 0.58096048630521868},
|
|
1303
|
+
{-0.1059284165295875, -0.80701007424011018, 0.58095947418602778},
|
|
1304
|
+
{-0.10592841614913188, -0.80701007428931704, 0.58095947418704452},
|
|
1305
|
+
{-0.10592864947042728, -0.8070119434176124, 0.58095683523192998},
|
|
1306
|
+
{-0.1059286884898481, -0.80701225600079662, 0.58095639390519271},
|
|
1307
|
+
{-0.10592868927069989, -0.80701225581371527, 0.58095639402269295},
|
|
1308
|
+
{-0.10592869427137827, -0.80701225619024619, 0.58095639258785126},
|
|
1309
|
+
{-0.10592869791375134, -0.80701225804491505, 0.58095638934738025},
|
|
1310
|
+
{-0.10592869922184817, -0.80701226088076483, 0.5809563851695615},
|
|
1311
|
+
{-0.10592869922184843, -0.80701226088076705, 0.58095638516955805},
|
|
1312
|
+
{-0.10592869784516552, -0.80701226393793402, 0.58095638117383475},
|
|
1313
|
+
{-0.10592869415258396, -0.80701226639725276, 0.58095637843085768},
|
|
1314
|
+
{-0.10592868991437976, -0.80701226741266929, 0.58095637779310561},
|
|
1315
|
+
},
|
|
1316
|
+
};
|
|
1317
|
+
vector<vector<S2Point>> b_vertices = {
|
|
1318
|
+
{
|
|
1319
|
+
{-0.10592564460843924, -0.80700972122716552, 0.58096046996257766},
|
|
1320
|
+
{-0.10592539435053176, -0.80700975987840939, 0.58096046190138972},
|
|
1321
|
+
{-0.10592547496472972, -0.80701045435596641, 0.58095948250602925},
|
|
1322
|
+
{-0.10592546630689462, -0.80701045544795591, 0.58095948256771723},
|
|
1323
|
+
{-0.10592546630693271, -0.80701045544826022, 0.58095948256728758},
|
|
1324
|
+
{-0.1059254749287661, -0.80701045440038255, 0.5809594824508878},
|
|
1325
|
+
{-0.10592572778318898, -0.80701292050174633, 0.58095601067279068},
|
|
1326
|
+
{-0.1059257191207934, -0.80701292155455673, 0.58095601078973391},
|
|
1327
|
+
{-0.1059257194541381, -0.80701292151405679, 0.58095601078521419},
|
|
1328
|
+
{-0.10592572778319062, -0.80701292050176254, 0.58095601067276803},
|
|
1329
|
+
{-0.10592586765088864, -0.80701428463992497, 0.58095409022530931},
|
|
1330
|
+
{-0.10592585899855227, -0.80701428569151201, 0.58095409034211776},
|
|
1331
|
+
{-0.10592585898857355, -0.80701428569272593, 0.58095409034225098},
|
|
1332
|
+
{-0.10592586765088888, -0.80701428463992686, 0.58095409022530675},
|
|
1333
|
+
{-0.10592587247896063, -0.80701433172842685, 0.58095402393347073},
|
|
1334
|
+
{-0.10592681605007616, -0.80701420941876889, 0.58095402179319922},
|
|
1335
|
+
{-0.10592685438651758, -0.80701420444942229, 0.58095402170623067},
|
|
1336
|
+
{-0.10592685499307326, -0.80701420437079774, 0.58095402170485466},
|
|
1337
|
+
{-0.10592685562894634, -0.80701420940058122, 0.58095401460194696},
|
|
1338
|
+
{-0.10592685499689927, -0.80701420437030225, 0.58095402170484534},
|
|
1339
|
+
{-0.10592700383609792, -0.80701418511591771, 0.58095402131321794},
|
|
1340
|
+
{-0.10592787737695626, -0.80701407211109533, 0.58095401901448296},
|
|
1341
|
+
{-0.10592889869604118, -0.80701393998826909, 0.58095401632629584},
|
|
1342
|
+
{-0.10592889996012077, -0.80701395004882903, 0.58095400212049919},
|
|
1343
|
+
{-0.10592787864104941, -0.80701408217165349, 0.58095400480868631},
|
|
1344
|
+
{-0.10592787800903029, -0.80701407714164064, 0.58095401191120999},
|
|
1345
|
+
{-0.10592787864103763, -0.80701408217165482, 0.5809540048086862},
|
|
1346
|
+
{-0.10592700510019466, -0.80701419517647521, 0.58095400710742118},
|
|
1347
|
+
{-0.1059270044681431, -0.80701419014619669, 0.58095401421031934},
|
|
1348
|
+
{-0.10592700510018833, -0.8070141951764761, 0.58095400710742118},
|
|
1349
|
+
{-0.10592685626275877, -0.80701421443063182, 0.58095400749904391},
|
|
1350
|
+
{-0.10592685565826369, -0.80701421450898914, 0.58095400750041526},
|
|
1351
|
+
{-0.10592685502239063, -0.80701420947920566, 0.58095401460332308},
|
|
1352
|
+
{-0.10592685565826078, -0.80701421450898947, 0.58095400750041526},
|
|
1353
|
+
{-0.10592681732181129, -0.80701421947833718, 0.58095400758738369},
|
|
1354
|
+
{-0.10592681668594069, -0.80701421444855348, 0.58095401469029151},
|
|
1355
|
+
{-0.10592681732180521, -0.80701421947833796, 0.58095400758738369},
|
|
1356
|
+
{-0.10592586561269894, -0.80701434284287321, 0.58095400974611222},
|
|
1357
|
+
{-0.10592586497746249, -0.80701433781815202, 0.58095401684187198},
|
|
1358
|
+
{-0.10592586561268771, -0.80701434284287465, 0.58095400974611222},
|
|
1359
|
+
{-0.10592586497708102, -0.80701434292526464, 0.58095400974755396},
|
|
1360
|
+
{-0.10592586434121586, -0.80701433789548005, 0.58095401685046166},
|
|
1361
|
+
{-0.10592585567909471, -0.80701433894825569, 0.58095401696740323},
|
|
1362
|
+
{-0.1059258503266465, -0.80701428674547793, 0.58095409045919011},
|
|
1363
|
+
{-0.10592571045894811, -0.80701292260731206, 0.58095601090665361},
|
|
1364
|
+
{-0.10592571912060067, -0.80701292155459425, 0.58095601078971715},
|
|
1365
|
+
{-0.10592571878923682, -0.80701292159485349, 0.58095601079421},
|
|
1366
|
+
{-0.10592571045894694, -0.80701292260730051, 0.58095601090666993},
|
|
1367
|
+
{-0.10592545760452345, -0.80701045650593073, 0.58095948268477515},
|
|
1368
|
+
{-0.10592545764454649, -0.80701045650106651, 0.58095948268423492},
|
|
1369
|
+
{-0.10592537647753246, -0.80700975726109381, 0.58096046879584118},
|
|
1370
|
+
{-0.10592538513536764, -0.80700975616910509, 0.58096046873415197},
|
|
1371
|
+
{-0.10592538413784101, -0.80700975119062324, 0.58096047583161736},
|
|
1372
|
+
{-0.10592564339592514, -0.80700971114934217, 0.58096048418271495},
|
|
1373
|
+
{-0.10592564439344856, -0.80700971612782446, 0.58096047708524956},
|
|
1374
|
+
{-0.10592564496449927, -0.80700971099098684, 0.58096048411668999},
|
|
1375
|
+
{-0.10592678502227458, -0.80700965660628099, 0.58096035179610783},
|
|
1376
|
+
{-0.10592678388014524, -0.80700966687995779, 0.58096033773323019},
|
|
1377
|
+
},
|
|
1378
|
+
{
|
|
1379
|
+
{-0.10592585898876757, -0.80701428569270128, 0.58095409034224987},
|
|
1380
|
+
{-0.10592585897888845, -0.80701428569390288, 0.58095409034238166},
|
|
1381
|
+
{-0.1059258503266465, -0.80701428674547793, 0.58095409045919011},
|
|
1382
|
+
},
|
|
1383
|
+
{
|
|
1384
|
+
{-0.10592546626664477, -0.80701045545315664, 0.58095948256783148},
|
|
1385
|
+
{-0.10592546623958927, -0.8070104554564449, 0.58095948256819674},
|
|
1386
|
+
{-0.10592546626662946, -0.80701045545303429, 0.580959482568004},
|
|
1387
|
+
},
|
|
1388
|
+
};
|
|
1389
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
1390
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
1391
|
+
S2_VLOG(1) << "\nS2Polygon: " << s2textformat::ToString(a);
|
|
1392
|
+
S2_VLOG(1) << "\nS2Polygon: " << s2textformat::ToString(b);
|
|
1393
|
+
S2Polygon c;
|
|
1394
|
+
c.InitToUnion(&a, &b);
|
|
1395
|
+
// Inconsistent loop orientations detected
|
|
1396
|
+
S2_VLOG(1) << "\nS2Polygon: " << s2textformat::ToString(c);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
TEST(S2Polygon, Bug11) {
|
|
1400
|
+
vector<vector<S2Point>> a_vertices = {
|
|
1401
|
+
{
|
|
1402
|
+
{-0.10727349803435572, -0.80875763107088172, 0.57827631008375979},
|
|
1403
|
+
{-0.10727349807040805, -0.80875763112192245, 0.57827631000568813},
|
|
1404
|
+
{-0.10727349807040625, -0.80875763112192278, 0.57827631000568813},
|
|
1405
|
+
},
|
|
1406
|
+
{
|
|
1407
|
+
{-0.1072729603486537, -0.80875606054879057, 0.57827860629945249},
|
|
1408
|
+
{-0.10727299870478688, -0.80875633377729705, 0.57827821705818028},
|
|
1409
|
+
{-0.10727299875560981, -0.80875633413933223, 0.57827821654242495},
|
|
1410
|
+
{-0.10727309272230967, -0.80875700360375646, 0.57827726282438607},
|
|
1411
|
+
{-0.10727318660000487, -0.80875767243400742, 0.57827631000742785},
|
|
1412
|
+
{-0.10727349802669105, -0.80875763101356435, 0.57827631016534387},
|
|
1413
|
+
{-0.10727349803435525, -0.80875763107087817, 0.57827631008376468},
|
|
1414
|
+
{-0.10727349803435572, -0.80875763107088172, 0.57827631008375979},
|
|
1415
|
+
{-0.1072734980420204, -0.80875763112819909, 0.57827631000217561},
|
|
1416
|
+
{-0.10727318657570066, -0.80875767255391384, 0.57827630984423972},
|
|
1417
|
+
{-0.10727318651657966, -0.80875767256177711, 0.57827630984420975},
|
|
1418
|
+
{-0.10727318650891528, -0.80875767250445951, 0.57827630992579371},
|
|
1419
|
+
{-0.10727318640981781, -0.80875767251785957, 0.57827630992543622},
|
|
1420
|
+
{-0.10727309252411468, -0.80875700363055636, 0.57827726282367087},
|
|
1421
|
+
{-0.10727299855741491, -0.8087563341661328, 0.57827821654170874},
|
|
1422
|
+
{-0.10727299850659211, -0.8087563338040985, 0.57827821705746318},
|
|
1423
|
+
{-0.10727296014242577, -0.80875606051836801, 0.57827860638025652},
|
|
1424
|
+
{-0.10727296024152315, -0.80875606050496729, 0.57827860638061501},
|
|
1425
|
+
{-0.10727296023340849, -0.8087560604477102, 0.57827860646219797},
|
|
1426
|
+
{-0.10727348576547496, -0.80875598914629976, 0.57827860869282954},
|
|
1427
|
+
{-0.1072734857817042, -0.80875598926081438, 0.57827860852966395},
|
|
1428
|
+
},
|
|
1429
|
+
};
|
|
1430
|
+
vector<vector<S2Point>> b_vertices = {
|
|
1431
|
+
{
|
|
1432
|
+
{-0.1072734857735896, -0.80875598920355718, 0.5782786086112468},
|
|
1433
|
+
{-0.10727348576547457, -0.80875598914629976, 0.57827860869282954},
|
|
1434
|
+
{-0.10727839137361543, -0.80875532356817348, 0.57827862950694298},
|
|
1435
|
+
{-0.10727839137881608, -0.80875532356471602, 0.57827862951081388},
|
|
1436
|
+
{-0.10727839143632178, -0.80875532355090063, 0.5782786295194674},
|
|
1437
|
+
{-0.10727839149361706, -0.80875532355509905, 0.57827862950296649},
|
|
1438
|
+
{-0.1072783915353497, -0.80875532357618651, 0.57827862946573261},
|
|
1439
|
+
{-0.10727839154773799, -0.80875532360290581, 0.57827862942606567},
|
|
1440
|
+
{-0.10727848921795155, -0.80875531035110082, 0.57827862984032907},
|
|
1441
|
+
{-0.1072784892332832, -0.80875531046514559, 0.57827862967798682},
|
|
1442
|
+
{-0.10727971608197531, -0.8087551454635169, 0.57827863284376713},
|
|
1443
|
+
{-0.10727986275126807, -0.80875539440654376, 0.57827825747332484},
|
|
1444
|
+
{-0.10727959167812619, -0.80875599171505064, 0.57827747239052929},
|
|
1445
|
+
{-0.10727974196569352, -0.80875625444235633, 0.57827707706958686},
|
|
1446
|
+
{-0.10727993501555312, -0.80875677560355186, 0.57827631237878363},
|
|
1447
|
+
{-0.10727870858143702, -0.80875693828645479, 0.57827631237896882},
|
|
1448
|
+
{-0.1072787085493927, -0.80875693804871851, 0.5782763127174031},
|
|
1449
|
+
{-0.10727615977928232, -0.80875727704955946, 0.57827631143112901},
|
|
1450
|
+
{-0.10727615977915911, -0.80875727704957578, 0.57827631143112901},
|
|
1451
|
+
{-0.10727349803435751, -0.80875763107088128, 0.57827631008375968},
|
|
1452
|
+
{-0.10727349803435574, -0.80875763107088183, 0.57827631008375979},
|
|
1453
|
+
{-0.10727318656803594, -0.80875767249659658, 0.57827630992582391},
|
|
1454
|
+
{-0.10727318650891531, -0.80875767250445962, 0.57827630992579382},
|
|
1455
|
+
{-0.10727309262321218, -0.80875700361715641, 0.57827726282402847},
|
|
1456
|
+
{-0.10727299865651231, -0.80875633415273218, 0.57827821654206735},
|
|
1457
|
+
{-0.10727299860568951, -0.80875633379069789, 0.57827821705782179},
|
|
1458
|
+
{-0.10727296024152314, -0.80875606050496718, 0.57827860638061501},
|
|
1459
|
+
},
|
|
1460
|
+
};
|
|
1461
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
1462
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
1463
|
+
S2Polygon c;
|
|
1464
|
+
c.InitToUnion(&a, &b);
|
|
1465
|
+
// Given edges do not form loops (indegree != outdegree)
|
|
1466
|
+
EXPECT_FALSE(c.is_empty())
|
|
1467
|
+
<< "\nS2Polygon: " << s2textformat::ToString(a)
|
|
1468
|
+
<< "\nS2Polygon: " << s2textformat::ToString(b);
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
TEST(S2Polygon, Bug12) {
|
|
1472
|
+
vector<vector<S2Point>> a_vertices = {
|
|
1473
|
+
{
|
|
1474
|
+
{-0.10772916872905106, -0.80699542608967267, 0.58064861015531188},
|
|
1475
|
+
{-0.10772916892726483, -0.80699542606300401, 0.58064861015560143},
|
|
1476
|
+
{-0.10772916892726613, -0.80699542606301333, 0.58064861015558844},
|
|
1477
|
+
{-0.10772916872905235, -0.806995426089682, 0.58064861015529889},
|
|
1478
|
+
},
|
|
1479
|
+
};
|
|
1480
|
+
vector<vector<S2Point>> b_vertices = {
|
|
1481
|
+
{
|
|
1482
|
+
{-0.10772916872905348, -0.80699542608969022, 0.58064861015528724},
|
|
1483
|
+
{-0.10772916892726496, -0.80699542606300489, 0.58064861015559999},
|
|
1484
|
+
{-0.10772930108168739, -0.80699639165138115, 0.58064724364290399},
|
|
1485
|
+
{-0.10772930088347589, -0.80699639167806647, 0.58064724364259113},
|
|
1486
|
+
},
|
|
1487
|
+
};
|
|
1488
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
1489
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
1490
|
+
S2Polygon c;
|
|
1491
|
+
c.InitToUnion(&a, &b);
|
|
1492
|
+
// Given edges do not form loops (indegree != outdegree)
|
|
1493
|
+
EXPECT_FALSE(c.is_empty())
|
|
1494
|
+
<< "\nS2Polygon: " << s2textformat::ToString(a)
|
|
1495
|
+
<< "\nS2Polygon: " << s2textformat::ToString(b);
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
TEST(S2Polygon, Bug13) {
|
|
1499
|
+
// This test exercises a rare special case in GetCrossedVertexIndex where
|
|
1500
|
+
// two crossing edge chains snap to a different permutation of the same
|
|
1501
|
+
// vertices. In this example one input edge crosses another edge from right
|
|
1502
|
+
// to left, the first edge snaps to BCD and the second snaps to ABDC, and
|
|
1503
|
+
// triangle BCD is CCW. Since BCD is to the right of BD, this means that
|
|
1504
|
+
// the first edge has not yet crossed the second at vertex B, leaving C or D
|
|
1505
|
+
// as the possible crossing vertices.
|
|
1506
|
+
vector<vector<S2Point>> a_vertices = {
|
|
1507
|
+
{
|
|
1508
|
+
{-0.38306437985388492, -0.74921955334206214, 0.54030708099846292},
|
|
1509
|
+
{-0.3830643798552798, -0.74921955334134249, 0.5403070809984718},
|
|
1510
|
+
{-0.38306437985529124, -0.74921955334136414, 0.54030708099843361},
|
|
1511
|
+
{-0.38306437985389635, -0.74921955334208379, 0.54030708099842473},
|
|
1512
|
+
},
|
|
1513
|
+
};
|
|
1514
|
+
vector<vector<S2Point>> b_vertices = {
|
|
1515
|
+
{
|
|
1516
|
+
{-0.38306437985390962, -0.74921955334210588, 0.54030708099838465},
|
|
1517
|
+
{-0.38306437985527797, -0.74921955334134205, 0.54030708099847369},
|
|
1518
|
+
{-0.38306437985527941, -0.74921955334134405, 0.54030708099847014},
|
|
1519
|
+
{-0.38306437985391095, -0.74921955334210777, 0.54030708099838098},
|
|
1520
|
+
},
|
|
1521
|
+
};
|
|
1522
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
1523
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
1524
|
+
S2Polygon c;
|
|
1525
|
+
c.InitToUnion(&a, &b);
|
|
1526
|
+
// Given edges do not form loops (indegree != outdegree)
|
|
1527
|
+
EXPECT_FALSE(c.is_empty())
|
|
1528
|
+
<< "\nS2Polygon: " << s2textformat::ToString(a)
|
|
1529
|
+
<< "\nS2Polygon: " << s2textformat::ToString(b);
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
TEST(S2Polygon, Bug14) {
|
|
1533
|
+
// This test exercises another rare case where the crossing vertices chosen
|
|
1534
|
+
// by GetCrossedVertexIndex() are not ordered correctly along the edge being
|
|
1535
|
+
// crossed. This is handled by adding extra edges to the output in order to
|
|
1536
|
+
// link up the crossings in the correct order.
|
|
1537
|
+
vector<vector<S2Point>> a_vertices = {
|
|
1538
|
+
{
|
|
1539
|
+
{-0.3837392878495085, -0.7477800800281974, 0.5418201831546835},
|
|
1540
|
+
{-0.38373928785696076, -0.7477800800212292, 0.54182018315902258},
|
|
1541
|
+
{-0.38373928785701278, -0.74778008002124685, 0.5418201831589613},
|
|
1542
|
+
{-0.38373928785703426, -0.7477800800212544, 0.54182018315893576},
|
|
1543
|
+
{-0.38373947205489456, -0.74778014227795497, 0.5418199667802881},
|
|
1544
|
+
{-0.38373947204434411, -0.74778014228781997, 0.54181996677414512},
|
|
1545
|
+
{-0.38373947205872994, -0.74778014228185352, 0.54181996677219124},
|
|
1546
|
+
{-0.38373947218468357, -0.74778014288930306, 0.54181996584462788},
|
|
1547
|
+
{-0.3837396702525171, -0.74778021044361542, 0.54181973233114322},
|
|
1548
|
+
{-0.38373967023137123, -0.74778021046333043, 0.54181973231891067},
|
|
1549
|
+
{-0.38373947216030285, -0.74778014290791484, 0.54181996583620895},
|
|
1550
|
+
{-0.38373947217087578, -0.74778014289805739, 0.54181996584232528},
|
|
1551
|
+
{-0.38373947215649007, -0.74778014290402395, 0.54181996584427927},
|
|
1552
|
+
{-0.3837394720305386, -0.74778014229658485, 0.5418199667718262},
|
|
1553
|
+
{-0.38373928783585998, -0.74778008004095942, 0.54182018314673686},
|
|
1554
|
+
{-0.38373928784641037, -0.7477800800310942, 0.54182018315287972},
|
|
1555
|
+
{-0.38373928783578648, -0.74778008004093421, 0.54182018314682368},
|
|
1556
|
+
{-0.383739287835765, -0.74778008004092666, 0.54182018314684921},
|
|
1557
|
+
},
|
|
1558
|
+
};
|
|
1559
|
+
vector<vector<S2Point>> b_vertices = {
|
|
1560
|
+
{
|
|
1561
|
+
{-0.38373923813692823, -0.7477800632164362, 0.54182024156551456},
|
|
1562
|
+
{-0.3837392878569364, -0.74778008002122087, 0.54182018315905123},
|
|
1563
|
+
{-0.38373928784640354, -0.74778008003106944, 0.54182018315291858},
|
|
1564
|
+
{-0.38373928784638789, -0.74778008003108642, 0.54182018315290648},
|
|
1565
|
+
{-0.38373928784638023, -0.74778008003109453, 0.54182018315290048},
|
|
1566
|
+
{-0.38373928783692102, -0.74778008004124585, 0.54182018314559},
|
|
1567
|
+
{-0.38373928783691913, -0.74778008004124541, 0.54182018314559188},
|
|
1568
|
+
{-0.38373928784636568, -0.74778008003110774, 0.54182018315289271},
|
|
1569
|
+
{-0.38373928784637329, -0.74778008003109953, 0.54182018315289848},
|
|
1570
|
+
{-0.38373928783583561, -0.74778008004095109, 0.5418201831467655},
|
|
1571
|
+
{-0.38373923811582744, -0.74778006323616641, 0.54182024155322883},
|
|
1572
|
+
{-0.38373857650312843, -0.74777983961840766, 0.54182101875399913},
|
|
1573
|
+
{-0.38373857652422921, -0.74777983959867744, 0.54182101876628486},
|
|
1574
|
+
},
|
|
1575
|
+
};
|
|
1576
|
+
S2Polygon a(MakeLoops(a_vertices));
|
|
1577
|
+
S2Polygon b(MakeLoops(b_vertices));
|
|
1578
|
+
S2Polygon c;
|
|
1579
|
+
c.InitToUnion(&a, &b);
|
|
1580
|
+
// Given edges do not form loops (indegree != outdegree)
|
|
1581
|
+
EXPECT_FALSE(c.is_empty())
|
|
1582
|
+
<< "\nS2Polygon: " << s2textformat::ToString(a)
|
|
1583
|
+
<< "\nS2Polygon: " << s2textformat::ToString(b);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
static void PolylineIntersectionSharedEdgeTest(const S2Polygon& p,
|
|
1587
|
+
int start_vertex,
|
|
1588
|
+
int direction) {
|
|
1589
|
+
SCOPED_TRACE(StrCat("Polyline intersection shared edge test"
|
|
1590
|
+
" start=", start_vertex,
|
|
1591
|
+
" direction=", direction));
|
|
1592
|
+
vector<S2Point> points = {p.loop(0)->vertex(start_vertex),
|
|
1593
|
+
p.loop(0)->vertex(start_vertex + direction)};
|
|
1594
|
+
S2Polyline polyline(points);
|
|
1595
|
+
vector<unique_ptr<S2Polyline>> polylines;
|
|
1596
|
+
if (direction < 0) {
|
|
1597
|
+
polylines = p.IntersectWithPolyline(polyline);
|
|
1598
|
+
EXPECT_EQ(0, polylines.size());
|
|
1599
|
+
polylines = p.SubtractFromPolyline(polyline);
|
|
1600
|
+
ASSERT_EQ(1, polylines.size());
|
|
1601
|
+
ASSERT_EQ(2, polylines[0]->num_vertices());
|
|
1602
|
+
EXPECT_EQ(points[0], polylines[0]->vertex(0));
|
|
1603
|
+
EXPECT_EQ(points[1], polylines[0]->vertex(1));
|
|
1604
|
+
EXPECT_FALSE(p.Intersects(polyline));
|
|
1605
|
+
EXPECT_FALSE(p.Contains(polyline));
|
|
1606
|
+
} else {
|
|
1607
|
+
polylines = p.IntersectWithPolyline(polyline);
|
|
1608
|
+
ASSERT_EQ(1, polylines.size());
|
|
1609
|
+
ASSERT_EQ(2, polylines[0]->num_vertices());
|
|
1610
|
+
EXPECT_EQ(points[0], polylines[0]->vertex(0));
|
|
1611
|
+
EXPECT_EQ(points[1], polylines[0]->vertex(1));
|
|
1612
|
+
polylines = p.SubtractFromPolyline(polyline);
|
|
1613
|
+
EXPECT_EQ(0, polylines.size());
|
|
1614
|
+
EXPECT_TRUE(p.Intersects(polyline));
|
|
1615
|
+
EXPECT_TRUE(p.Contains(polyline));
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
// This tests polygon-polyline intersections.
|
|
1620
|
+
// It covers the same edge cases as TestOperations and also adds some
|
|
1621
|
+
// extra tests for shared edges.
|
|
1622
|
+
TEST_F(S2PolygonTestBase, PolylineIntersection) {
|
|
1623
|
+
for (int v = 0; v < 3; ++v) {
|
|
1624
|
+
PolylineIntersectionSharedEdgeTest(*cross1_, v, 1);
|
|
1625
|
+
PolylineIntersectionSharedEdgeTest(*cross1_, v + 1, -1);
|
|
1626
|
+
PolylineIntersectionSharedEdgeTest(*cross1_side_hole_, v, 1);
|
|
1627
|
+
PolylineIntersectionSharedEdgeTest(*cross1_side_hole_, v + 1, -1);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
// See comments in TestOperations about the vlue of this constant.
|
|
1631
|
+
static const S1Angle kMaxError = S1Angle::Radians(1e-4);
|
|
1632
|
+
|
|
1633
|
+
// This duplicates some of the tests in TestOperations by
|
|
1634
|
+
// converting the outline of polygon A to a polyline then intersecting
|
|
1635
|
+
// it with the polygon B. It then converts B to a polyline and intersects
|
|
1636
|
+
// it with A. It then feeds all of the results into a polygon builder and
|
|
1637
|
+
// tests that the output is equal to doing an intersection between A and B.
|
|
1638
|
+
int i = 0;
|
|
1639
|
+
for (const TestCase& test : test_cases) {
|
|
1640
|
+
SCOPED_TRACE(StrCat("Polyline intersection test case ", i++));
|
|
1641
|
+
unique_ptr<S2Polygon> a(MakePolygon(test.a));
|
|
1642
|
+
unique_ptr<S2Polygon> b(MakePolygon(test.b));
|
|
1643
|
+
unique_ptr<S2Polygon> expected_a_and_b(MakePolygon(test.a_and_b));
|
|
1644
|
+
|
|
1645
|
+
vector<S2Point> points;
|
|
1646
|
+
vector<unique_ptr<S2Polyline>> polylines;
|
|
1647
|
+
for (int ab = 0; ab < 2; ab++) {
|
|
1648
|
+
S2Polygon *tmp = ab ? a.get() : b.get();
|
|
1649
|
+
S2Polygon *tmp2 = ab ? b.get() : a.get();
|
|
1650
|
+
for (int l = 0; l < tmp->num_loops(); l++) {
|
|
1651
|
+
points.clear();
|
|
1652
|
+
if (tmp->loop(l)->is_hole()) {
|
|
1653
|
+
for (int v = tmp->loop(l)->num_vertices(); v >=0 ; v--) {
|
|
1654
|
+
points.push_back(tmp->loop(l)->vertex(v));
|
|
1655
|
+
}
|
|
1656
|
+
} else {
|
|
1657
|
+
for (int v = 0; v <= tmp->loop(l)->num_vertices(); v++) {
|
|
1658
|
+
points.push_back(tmp->loop(l)->vertex(v));
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
S2Polyline polyline(points);
|
|
1662
|
+
vector<unique_ptr<S2Polyline>> tmp =
|
|
1663
|
+
tmp2->IntersectWithPolyline(polyline);
|
|
1664
|
+
polylines.insert(polylines.end(),
|
|
1665
|
+
std::make_move_iterator(tmp.begin()),
|
|
1666
|
+
std::make_move_iterator(tmp.end()));
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
S2Builder builder{S2Builder::Options()};
|
|
1671
|
+
S2Polygon a_and_b;
|
|
1672
|
+
builder.StartLayer(make_unique<s2builderutil::S2PolygonLayer>(&a_and_b));
|
|
1673
|
+
for (const auto& polyline : polylines) {
|
|
1674
|
+
builder.AddPolyline(*polyline);
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
S2Error error;
|
|
1678
|
+
ASSERT_TRUE(builder.Build(&error)) << error;
|
|
1679
|
+
CheckEqual(a_and_b, *expected_a_and_b, kMaxError);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
static void CheckCoveringIsConservative(const S2Polygon& polygon,
|
|
1684
|
+
const vector<S2CellId>& cells) {
|
|
1685
|
+
// Check that Contains(S2Cell) and MayIntersect(S2Cell) are implemented
|
|
1686
|
+
// conservatively, by comparing against the Contains/Intersect result with
|
|
1687
|
+
// the "cell polygon" defined by the four cell vertices. Please note that
|
|
1688
|
+
// the cell polygon is *not* an exact representation of the S2Cell: cell
|
|
1689
|
+
// vertices are rounded from their true mathematical positions, which leads
|
|
1690
|
+
// to tiny cracks and overlaps between the cell polygons at different cell
|
|
1691
|
+
// levels. That is why Contains(S2Cell) and MayIntersect(S2Cell) cannot be
|
|
1692
|
+
// implemented by simply converting the cell to an S2Polygon. But it is
|
|
1693
|
+
// still useful to do this as a sanity check. In particular:
|
|
1694
|
+
//
|
|
1695
|
+
// - If Contains(cell) is true, the polygon must contain the cell polygon.
|
|
1696
|
+
// - If the polygon intersects the cell polygon, then MayIntersect(cell)
|
|
1697
|
+
// must return true.
|
|
1698
|
+
//
|
|
1699
|
+
for (S2CellId cell_id : cells) {
|
|
1700
|
+
S2Cell cell(cell_id);
|
|
1701
|
+
S2Polygon cell_poly(cell);
|
|
1702
|
+
if (polygon.Contains(cell)) {
|
|
1703
|
+
EXPECT_TRUE(polygon.Contains(&cell_poly));
|
|
1704
|
+
}
|
|
1705
|
+
if (polygon.Intersects(&cell_poly)) {
|
|
1706
|
+
EXPECT_TRUE(polygon.MayIntersect(cell));
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
// Remove a random polygon from "pieces" and return it.
|
|
1712
|
+
static unique_ptr<S2Polygon> ChoosePiece(
|
|
1713
|
+
vector<unique_ptr<S2Polygon>> *pieces) {
|
|
1714
|
+
int i = S2Testing::rnd.Uniform(pieces->size());
|
|
1715
|
+
unique_ptr<S2Polygon> result = std::move((*pieces)[i]);
|
|
1716
|
+
pieces->erase(pieces->begin() + i);
|
|
1717
|
+
return result;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
static void SplitAndAssemble(const S2Polygon& polygon) {
|
|
1721
|
+
// Normalize the polygon's loop structure by rebuilding it with S2Builder.
|
|
1722
|
+
S2Builder builder{S2Builder::Options()};
|
|
1723
|
+
S2Polygon expected;
|
|
1724
|
+
builder.StartLayer(make_unique<s2builderutil::S2PolygonLayer>(&expected));
|
|
1725
|
+
builder.AddPolygon(polygon);
|
|
1726
|
+
|
|
1727
|
+
S2Error error;
|
|
1728
|
+
ASSERT_TRUE(builder.Build(&error)) << error;
|
|
1729
|
+
|
|
1730
|
+
for (int iter = 0; iter < (google::DEBUG_MODE ? 3 : 10); ++iter) {
|
|
1731
|
+
S2RegionCoverer coverer;
|
|
1732
|
+
// Compute the minimum level such that the polygon's bounding
|
|
1733
|
+
// cap is guaranteed to be cut.
|
|
1734
|
+
double diameter = 2 * polygon.GetCapBound().GetRadius().radians();
|
|
1735
|
+
int min_level = S2::kMaxWidth.GetLevelForMaxValue(diameter);
|
|
1736
|
+
|
|
1737
|
+
// Now choose a level that has up to 500 cells in the covering.
|
|
1738
|
+
int level = min_level + S2Testing::rnd.Uniform(google::DEBUG_MODE ? 4 : 6);
|
|
1739
|
+
coverer.mutable_options()->set_min_level(min_level);
|
|
1740
|
+
coverer.mutable_options()->set_max_level(level);
|
|
1741
|
+
coverer.mutable_options()->set_max_cells(500);
|
|
1742
|
+
|
|
1743
|
+
vector<S2CellId> cells;
|
|
1744
|
+
coverer.GetCovering(polygon, &cells);
|
|
1745
|
+
S2CellUnion covering;
|
|
1746
|
+
covering.Init(cells);
|
|
1747
|
+
S2Testing::CheckCovering(polygon, covering, false);
|
|
1748
|
+
CheckCoveringIsConservative(polygon, cells);
|
|
1749
|
+
S2_VLOG(2) << cells.size() << " cells in covering";
|
|
1750
|
+
vector<unique_ptr<S2Polygon>> pieces;
|
|
1751
|
+
int i = 0;
|
|
1752
|
+
for (S2CellId cell_id : cells) {
|
|
1753
|
+
S2Cell cell(cell_id);
|
|
1754
|
+
S2Polygon window(cell);
|
|
1755
|
+
auto piece = make_unique<S2Polygon>();
|
|
1756
|
+
piece->InitToIntersection(&polygon, &window);
|
|
1757
|
+
S2_VLOG(4) << "\nPiece " << i++ << ":\n Window: "
|
|
1758
|
+
<< s2textformat::ToString(window)
|
|
1759
|
+
<< "\n Piece: " << s2textformat::ToString(*piece);
|
|
1760
|
+
pieces.push_back(std::move(piece));
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
// Now we repeatedly remove two random pieces, compute their union, and
|
|
1764
|
+
// insert the result as a new piece until only one piece is left.
|
|
1765
|
+
//
|
|
1766
|
+
// We don't use S2Polygon::DestructiveUnion() because it joins the pieces
|
|
1767
|
+
// in a mostly deterministic order. We don't just call random_shuffle()
|
|
1768
|
+
// on the pieces and repeatedly join the last two pieces in the vector
|
|
1769
|
+
// because this always joins a single original piece to the current union
|
|
1770
|
+
// rather than doing the unions according to a random tree structure.
|
|
1771
|
+
while (pieces.size() > 1) {
|
|
1772
|
+
unique_ptr<S2Polygon> a(ChoosePiece(&pieces));
|
|
1773
|
+
unique_ptr<S2Polygon> b(ChoosePiece(&pieces));
|
|
1774
|
+
auto c = make_unique<S2Polygon>();
|
|
1775
|
+
c->InitToUnion(a.get(), b.get());
|
|
1776
|
+
S2_VLOG(4) << "\nJoining piece a: " << s2textformat::ToString(*a)
|
|
1777
|
+
<< "\n With piece b: " << s2textformat::ToString(*b)
|
|
1778
|
+
<< "\n To get piece c: " << s2textformat::ToString(*c);
|
|
1779
|
+
pieces.push_back(std::move(c));
|
|
1780
|
+
}
|
|
1781
|
+
unique_ptr<S2Polygon> result(std::move(pieces[0]));
|
|
1782
|
+
pieces.pop_back();
|
|
1783
|
+
|
|
1784
|
+
// The moment of truth!
|
|
1785
|
+
EXPECT_TRUE(expected.BoundaryNear(*result, S1Angle::Radians(2e-15)))
|
|
1786
|
+
<< "\nActual:\n" << s2textformat::ToString(*result)
|
|
1787
|
+
<< "\nExpected:\n" << s2textformat::ToString(expected);
|
|
1788
|
+
|
|
1789
|
+
// Check that ApproxEquals produces the same result.
|
|
1790
|
+
if (!expected.ApproxEquals(result.get(),
|
|
1791
|
+
S2::kIntersectionMergeRadius)) {
|
|
1792
|
+
S2Polygon symmetric_difference;
|
|
1793
|
+
symmetric_difference.InitToApproxSymmetricDifference(
|
|
1794
|
+
&expected, result.get(), S2::kIntersectionMergeRadius);
|
|
1795
|
+
ADD_FAILURE() << s2textformat::ToString(symmetric_difference);
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
TEST_F(S2PolygonTestBase, Splitting) {
|
|
1801
|
+
// It takes too long to test all the polygons in debug mode, so we just pick
|
|
1802
|
+
// out some of the more interesting ones.
|
|
1803
|
+
|
|
1804
|
+
SplitAndAssemble(*near_10_);
|
|
1805
|
+
SplitAndAssemble(*near_H3210_);
|
|
1806
|
+
SplitAndAssemble(*far_H3210_);
|
|
1807
|
+
SplitAndAssemble(*south_0ab_);
|
|
1808
|
+
SplitAndAssemble(*south_210b_);
|
|
1809
|
+
SplitAndAssemble(*south_H20abc_);
|
|
1810
|
+
SplitAndAssemble(*nf1_n10_f2_s10abc_);
|
|
1811
|
+
SplitAndAssemble(*nf2_n2_f210_s210ab_);
|
|
1812
|
+
SplitAndAssemble(*far_H_);
|
|
1813
|
+
SplitAndAssemble(*south_H_);
|
|
1814
|
+
SplitAndAssemble(*far_H_south_H_);
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
TEST(S2Polygon, InitToCellUnionBorder) {
|
|
1818
|
+
// Test S2Polygon::InitToCellUnionBorder().
|
|
1819
|
+
// The main thing to check is that adjacent cells of different sizes get
|
|
1820
|
+
// merged correctly. To do this we generate two random adjacent cells,
|
|
1821
|
+
// convert to polygon, and make sure the polygon only has a single loop.
|
|
1822
|
+
for (int iter = 0; iter < 200; ++iter) {
|
|
1823
|
+
SCOPED_TRACE(StrCat("Iteration ", iter));
|
|
1824
|
+
|
|
1825
|
+
// Choose a random non-leaf cell.
|
|
1826
|
+
S2CellId big_cell =
|
|
1827
|
+
S2Testing::GetRandomCellId(S2Testing::rnd.Uniform(S2CellId::kMaxLevel));
|
|
1828
|
+
// Get all neighbors at some smaller level.
|
|
1829
|
+
int small_level = big_cell.level() +
|
|
1830
|
+
S2Testing::rnd.Uniform(min(16, S2CellId::kMaxLevel - big_cell.level()));
|
|
1831
|
+
vector<S2CellId> neighbors;
|
|
1832
|
+
big_cell.AppendAllNeighbors(small_level, &neighbors);
|
|
1833
|
+
// Pick one at random.
|
|
1834
|
+
S2CellId small_cell = neighbors[S2Testing::rnd.Uniform(neighbors.size())];
|
|
1835
|
+
// If it's diagonally adjacent, bail out.
|
|
1836
|
+
S2CellId edge_neighbors[4];
|
|
1837
|
+
big_cell.GetEdgeNeighbors(edge_neighbors);
|
|
1838
|
+
bool diagonal = true;
|
|
1839
|
+
for (int i = 0; i < 4; ++i) {
|
|
1840
|
+
if (edge_neighbors[i].contains(small_cell)) {
|
|
1841
|
+
diagonal = false;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
S2_VLOG(3) << iter << ": big_cell " << big_cell <<
|
|
1845
|
+
" small_cell " << small_cell;
|
|
1846
|
+
if (diagonal) {
|
|
1847
|
+
S2_VLOG(3) << " diagonal - bailing out!";
|
|
1848
|
+
continue;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
vector<S2CellId> cells;
|
|
1852
|
+
cells.push_back(big_cell);
|
|
1853
|
+
cells.push_back(small_cell);
|
|
1854
|
+
S2CellUnion cell_union;
|
|
1855
|
+
cell_union.Init(cells);
|
|
1856
|
+
EXPECT_EQ(2, cell_union.num_cells());
|
|
1857
|
+
S2Polygon poly;
|
|
1858
|
+
poly.InitToCellUnionBorder(cell_union);
|
|
1859
|
+
EXPECT_EQ(1, poly.num_loops());
|
|
1860
|
+
// If the conversion were perfect we could test containment, but due to
|
|
1861
|
+
// rounding the polygon won't always exactly contain both cells. We can
|
|
1862
|
+
// at least test intersection.
|
|
1863
|
+
EXPECT_TRUE(poly.MayIntersect(S2Cell(big_cell)));
|
|
1864
|
+
EXPECT_TRUE(poly.MayIntersect(S2Cell(small_cell)));
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
TEST(S2Polygon, UnionWithAmbgiuousCrossings) {
|
|
1869
|
+
vector<S2Point> a_vertices = {
|
|
1870
|
+
S2Point(0.044856812877680216, -0.80679210859571904, 0.5891301722422051),
|
|
1871
|
+
S2Point(0.044851868273159699, -0.80679240802900054, 0.5891301386444033),
|
|
1872
|
+
S2Point(0.044854246527738666, -0.80679240292188514, 0.58912996457145106)
|
|
1873
|
+
};
|
|
1874
|
+
vector<S2Point> b_vertices = {
|
|
1875
|
+
S2Point(0.044849715793028468, -0.80679253837178111, 0.58913012401412856),
|
|
1876
|
+
S2Point(0.044855344598821352, -0.80679219751320641, 0.589130162266992),
|
|
1877
|
+
S2Point(0.044854017712818696, -0.80679210327223405, 0.58913039235179754)
|
|
1878
|
+
};
|
|
1879
|
+
S2Polygon a(make_unique<S2Loop>(a_vertices));
|
|
1880
|
+
S2Polygon b(make_unique<S2Loop>(b_vertices));
|
|
1881
|
+
S2Polygon c;
|
|
1882
|
+
c.InitToUnion(&a, &b);
|
|
1883
|
+
EXPECT_FALSE(c.is_empty());
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
TEST(S2Polygon, InitToSloppySupportsEmptyPolygons) {
|
|
1887
|
+
S2Polygon empty_polygon;
|
|
1888
|
+
S2Polygon polygon;
|
|
1889
|
+
polygon.InitToSnapped(&empty_polygon);
|
|
1890
|
+
// InitToSloppy is further tested by SnapSplitsPolygon.
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
TEST(S2Polygon, InitToSnappedDoesNotRotateVertices) {
|
|
1894
|
+
// This particular example came from MapFacts, but in fact InitToSnapped
|
|
1895
|
+
// used to cyclically rotate the vertices of all "hole" loops.
|
|
1896
|
+
unique_ptr<S2Polygon> polygon(s2textformat::MakePolygon(
|
|
1897
|
+
"49.9305505:-124.8345463, 49.9307448:-124.8299657, "
|
|
1898
|
+
"49.9332101:-124.8301996, 49.9331224:-124.8341368; "
|
|
1899
|
+
"49.9311087:-124.8327042, 49.9318176:-124.8312621, "
|
|
1900
|
+
"49.9318866:-124.8334451"));
|
|
1901
|
+
S2Polygon polygon2, polygon3;
|
|
1902
|
+
polygon2.InitToSnapped(polygon.get());
|
|
1903
|
+
|
|
1904
|
+
// Check that the first vertex is the same when converted to E7.
|
|
1905
|
+
EXPECT_EQ(S2LatLng::Latitude(polygon->loop(0)->vertex(0)).e7(),
|
|
1906
|
+
S2LatLng::Latitude(polygon2.loop(0)->vertex(0)).e7());
|
|
1907
|
+
EXPECT_EQ(S2LatLng::Longitude(polygon->loop(0)->vertex(0)).e7(),
|
|
1908
|
+
S2LatLng::Longitude(polygon2.loop(0)->vertex(0)).e7());
|
|
1909
|
+
|
|
1910
|
+
// Check that snapping twice doesn't rotate the vertices.
|
|
1911
|
+
polygon3.InitToSnapped(&polygon2);
|
|
1912
|
+
EXPECT_TRUE(polygon2.Equals(&polygon3));
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
TEST(S2Polygon, InitToSnappedWithSnapLevel) {
|
|
1916
|
+
const unique_ptr<const S2Polygon> polygon(
|
|
1917
|
+
s2textformat::MakePolygon("0:0, 0:2, 2:0; 0:0, 0:-2, -2:-2, -2:0"));
|
|
1918
|
+
for (int level = 0; level <= S2CellId::kMaxLevel; ++level) {
|
|
1919
|
+
S2Polygon snapped_polygon;
|
|
1920
|
+
snapped_polygon.InitToSnapped(polygon.get(), level);
|
|
1921
|
+
EXPECT_TRUE(snapped_polygon.IsValid());
|
|
1922
|
+
S1Angle merge_radius = min(S1Angle::Radians(S2::kMaxDiag.GetValue(level)),
|
|
1923
|
+
S2Builder::SnapFunction::kMaxSnapRadius());
|
|
1924
|
+
EXPECT_TRUE(snapped_polygon.ApproxContains(polygon.get(), merge_radius));
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
TEST(S2Polygon, InitToSnappedIsValid_A) {
|
|
1929
|
+
std::unique_ptr<S2Polygon> poly(s2textformat::MakePolygon(
|
|
1930
|
+
"53.1328020478452:6.39444903453293, 53.1328019:6.394449, "
|
|
1931
|
+
"53.1327091:6.3961766, 53.1313753:6.3958652, 53.1312825:6.3975924, "
|
|
1932
|
+
"53.132616:6.3979042, 53.1326161348736:6.39790423150577"));
|
|
1933
|
+
S2_LOG(INFO) << "\nInput: " << s2textformat::ToString(*poly);
|
|
1934
|
+
EXPECT_TRUE(poly->IsValid());
|
|
1935
|
+
S2Polygon poly_snapped;
|
|
1936
|
+
poly_snapped.set_s2debug_override(S2Debug::DISABLE);
|
|
1937
|
+
poly_snapped.InitToSnapped(poly.get());
|
|
1938
|
+
S2_LOG(INFO) << "\nSnapped: " << s2textformat::ToString(poly_snapped);
|
|
1939
|
+
S2Error error;
|
|
1940
|
+
EXPECT_FALSE(poly_snapped.FindValidationError(&error)) << error;
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
TEST(S2Polygon, InitToSnappedIsValid_B) {
|
|
1944
|
+
std::unique_ptr<S2Polygon> poly(s2textformat::MakePolygon(
|
|
1945
|
+
"51.6621651:4.9858102, 51.6620965:4.9874227, 51.662028:4.9890355, "
|
|
1946
|
+
"51.6619796006122:4.99017864445347, 51.6622335420397:4.98419752545216, "
|
|
1947
|
+
"51.6622334:4.9841975; 51.66189957578:4.99206198576131, "
|
|
1948
|
+
"51.6618911:4.9922612, 51.6618224:4.9938741, 51.6605122:4.993639, "
|
|
1949
|
+
"51.6604437:4.9952519, 51.6603751:4.9968648, 51.6603064:4.9984777, "
|
|
1950
|
+
"51.6602379:5.0000907, 51.660169:5.0017037, 51.6601003:5.0033165, "
|
|
1951
|
+
"51.6600318:5.0049298, 51.659963:5.0065427, 51.6598943:5.0081561, "
|
|
1952
|
+
"51.6612044207178:5.00839208571886, 51.6612732068132:5.00677860122814, "
|
|
1953
|
+
"51.6612732:5.0067786, 51.6613418:5.0051654, 51.6614106:5.0035525, "
|
|
1954
|
+
"51.6614793:5.0019393, 51.6615479:5.0003263, "
|
|
1955
|
+
"51.6615946694783:4.99923124520759, 51.6616389353165:4.99819106536521, "
|
|
1956
|
+
"51.6616852:4.9971, 51.6617538:4.995487, "
|
|
1957
|
+
"51.661753964726:4.99548702962593"));
|
|
1958
|
+
S2_LOG(INFO) << "\nInput: " << s2textformat::ToString(*poly);
|
|
1959
|
+
EXPECT_TRUE(poly->IsValid());
|
|
1960
|
+
S2Polygon poly_snapped;
|
|
1961
|
+
poly_snapped.set_s2debug_override(S2Debug::DISABLE);
|
|
1962
|
+
poly_snapped.InitToSnapped(poly.get());
|
|
1963
|
+
S2_LOG(INFO) << "\nSnapped: " << s2textformat::ToString(poly_snapped);
|
|
1964
|
+
S2Error error;
|
|
1965
|
+
EXPECT_FALSE(poly_snapped.FindValidationError(&error)) << error;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
TEST(S2Polygon, InitToSnappedIsValid_C) {
|
|
1969
|
+
std::unique_ptr<S2Polygon> poly(s2textformat::MakePolygon(
|
|
1970
|
+
"53.5316236236404:19.5841192796855, 53.5416584:19.5915903, "
|
|
1971
|
+
"53.5416584189104:19.5915901888287; 53.5416584:19.5915903, "
|
|
1972
|
+
"53.5363122:19.62299, 53.5562817:19.6378935, 53.5616342:19.606474; "
|
|
1973
|
+
"53.5616342:19.606474, 53.5916039:19.6288326, 53.5912689:19.6307982, "
|
|
1974
|
+
"53.5925176:19.6317308, 53.5928526:19.6297652, 53.6015949:19.6362943, "
|
|
1975
|
+
"53.6015950436033:19.6362944072725, 53.6015950814439:19.6362941852262, "
|
|
1976
|
+
"53.5616342380536:19.6064737764314"));
|
|
1977
|
+
S2_LOG(INFO) << "\nInput: " << s2textformat::ToString(*poly);
|
|
1978
|
+
EXPECT_TRUE(poly->IsValid());
|
|
1979
|
+
S2Polygon poly_snapped;
|
|
1980
|
+
poly_snapped.set_s2debug_override(S2Debug::DISABLE);
|
|
1981
|
+
poly_snapped.InitToSnapped(poly.get());
|
|
1982
|
+
S2_LOG(INFO) << "\nSnapped: " << s2textformat::ToString(poly_snapped);
|
|
1983
|
+
S2Error error;
|
|
1984
|
+
EXPECT_FALSE(poly_snapped.FindValidationError(&error)) << error;
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
TEST(S2Polygon, InitToSnappedIsValid_D) {
|
|
1988
|
+
std::unique_ptr<S2Polygon> poly(s2textformat::MakePolygon(
|
|
1989
|
+
"52.0909316:4.8673826, 52.0909317627574:4.86738262858533, "
|
|
1990
|
+
"52.0911338452911:4.86248482549567, 52.0911337:4.8624848, "
|
|
1991
|
+
"52.0910665:4.8641176, 52.090999:4.8657502"));
|
|
1992
|
+
S2_LOG(INFO) << "\nInput: " << s2textformat::ToString(*poly);
|
|
1993
|
+
EXPECT_TRUE(poly->IsValid());
|
|
1994
|
+
S2Polygon poly_snapped;
|
|
1995
|
+
poly_snapped.set_s2debug_override(S2Debug::DISABLE);
|
|
1996
|
+
poly_snapped.InitToSnapped(poly.get());
|
|
1997
|
+
S2_LOG(INFO) << "\nSnapped: " << s2textformat::ToString(poly_snapped);
|
|
1998
|
+
S2Error error;
|
|
1999
|
+
EXPECT_FALSE(poly_snapped.FindValidationError(&error)) << error;
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
TEST(S2Polygon, MultipleInit) {
|
|
2003
|
+
unique_ptr<S2Polygon> polygon(s2textformat::MakePolygon("0:0, 0:2, 2:0"));
|
|
2004
|
+
EXPECT_EQ(1, polygon->num_loops());
|
|
2005
|
+
EXPECT_EQ(3, polygon->num_vertices());
|
|
2006
|
+
S2LatLngRect bound1 = polygon->GetRectBound();
|
|
2007
|
+
|
|
2008
|
+
vector<unique_ptr<S2Loop>> loops;
|
|
2009
|
+
loops.push_back(s2textformat::MakeLoop("10:0, -10:-20, -10:20"));
|
|
2010
|
+
loops.push_back(s2textformat::MakeLoop("40:30, 20:10, 20:50"));
|
|
2011
|
+
polygon->InitNested(std::move(loops));
|
|
2012
|
+
EXPECT_TRUE(polygon->IsValid());
|
|
2013
|
+
EXPECT_EQ(2, polygon->num_loops());
|
|
2014
|
+
EXPECT_EQ(6, polygon->num_vertices());
|
|
2015
|
+
EXPECT_TRUE(bound1 != polygon->GetRectBound());
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
TEST(S2Polygon, InitSingleLoop) {
|
|
2019
|
+
S2Polygon polygon(make_unique<S2Loop>(S2Loop::kEmpty()));
|
|
2020
|
+
EXPECT_TRUE(polygon.is_empty());
|
|
2021
|
+
polygon.Init(make_unique<S2Loop>(S2Loop::kFull()));
|
|
2022
|
+
EXPECT_TRUE(polygon.is_full());
|
|
2023
|
+
polygon.Init(s2textformat::MakeLoop("0:0, 0:10, 10:0"));
|
|
2024
|
+
EXPECT_EQ(3, polygon.num_vertices());
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
TEST_F(S2PolygonTestBase, TestSimpleEncodeDecode) {
|
|
2028
|
+
Encoder encoder;
|
|
2029
|
+
cross1_->Encode(&encoder);
|
|
2030
|
+
Decoder decoder(encoder.base(), encoder.length());
|
|
2031
|
+
S2Polygon decoded_polygon;
|
|
2032
|
+
ASSERT_TRUE(decoded_polygon.Decode(&decoder));
|
|
2033
|
+
EXPECT_TRUE(cross1_->BoundaryEquals(&decoded_polygon));
|
|
2034
|
+
EXPECT_EQ(cross1_->GetRectBound(), decoded_polygon.GetRectBound());
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
TEST(S2Polygon, TestEncodeDecodeDefaultPolygon) {
|
|
2038
|
+
S2Polygon polygon;
|
|
2039
|
+
EXPECT_TRUE(TestEncodeDecode(&polygon));
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
TEST(S2Polygon, CompressedEmptyPolygonRequires3Bytes) {
|
|
2043
|
+
S2Polygon empty_polygon;
|
|
2044
|
+
Encoder encoder;
|
|
2045
|
+
|
|
2046
|
+
S2Polygon snapped_empty_polygon;
|
|
2047
|
+
snapped_empty_polygon.InitToSnapped(&empty_polygon);
|
|
2048
|
+
|
|
2049
|
+
snapped_empty_polygon.Encode(&encoder);
|
|
2050
|
+
// 1 byte for version, 1 for the level, 1 for the length.
|
|
2051
|
+
EXPECT_EQ(1 + 1 + 1, encoder.length());
|
|
2052
|
+
|
|
2053
|
+
EXPECT_TRUE(snapped_empty_polygon.is_empty());
|
|
2054
|
+
EXPECT_EQ(S2LatLngRect::Empty(), snapped_empty_polygon.GetRectBound());
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
TEST(S2Polygon, CompressedEncodedPolygonRequires69Bytes) {
|
|
2058
|
+
const unique_ptr<const S2Polygon> polygon(
|
|
2059
|
+
s2textformat::MakePolygon("0:0, 0:2, 2:0; 0:0, 0:-2, -2:-2, -2:0"));
|
|
2060
|
+
|
|
2061
|
+
S2Polygon snapped_polygon;
|
|
2062
|
+
snapped_polygon.InitToSnapped(polygon.get());
|
|
2063
|
+
|
|
2064
|
+
Encoder encoder;
|
|
2065
|
+
snapped_polygon.Encode(&encoder);
|
|
2066
|
+
|
|
2067
|
+
// 2 loops, one with 3 vertices, one with 4.
|
|
2068
|
+
// Polygon:
|
|
2069
|
+
// 1 byte for version
|
|
2070
|
+
// 1 byte for level
|
|
2071
|
+
// 1 byte for num_loops
|
|
2072
|
+
// Loops:
|
|
2073
|
+
// 5 bytes overhead
|
|
2074
|
+
// 8 bytes per vertex
|
|
2075
|
+
EXPECT_EQ(1 + 1 + 1 + 2 * 5 + 7 * 8, encoder.length());
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
TEST_F(S2PolygonTestBase, CompressedEncodedPolygonDecodesApproxEqual) {
|
|
2079
|
+
// To compare the boundaries, etc we want to snap first.
|
|
2080
|
+
S2Polygon snapped;
|
|
2081
|
+
snapped.InitToSnapped(near_30_.get());
|
|
2082
|
+
ASSERT_EQ(2, snapped.num_loops());
|
|
2083
|
+
EXPECT_EQ(0, snapped.loop(0)->depth());
|
|
2084
|
+
EXPECT_EQ(1, snapped.loop(1)->depth());
|
|
2085
|
+
|
|
2086
|
+
Encoder encoder;
|
|
2087
|
+
snapped.Encode(&encoder);
|
|
2088
|
+
|
|
2089
|
+
Decoder decoder(encoder.base(), encoder.length());
|
|
2090
|
+
|
|
2091
|
+
S2Polygon decoded_polygon;
|
|
2092
|
+
ASSERT_TRUE(decoded_polygon.Decode(&decoder));
|
|
2093
|
+
ASSERT_TRUE(decoded_polygon.IsValid());
|
|
2094
|
+
EXPECT_TRUE(snapped.BoundaryEquals(&decoded_polygon));
|
|
2095
|
+
EXPECT_EQ(snapped.GetRectBound(), decoded_polygon.GetRectBound());
|
|
2096
|
+
EXPECT_EQ(snapped.num_vertices(), decoded_polygon.num_vertices());
|
|
2097
|
+
EXPECT_EQ(2, decoded_polygon.num_loops());
|
|
2098
|
+
EXPECT_EQ(0, decoded_polygon.loop(0)->depth());
|
|
2099
|
+
EXPECT_EQ(1, decoded_polygon.loop(1)->depth());
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
// This test checks that S2Polygons created directly from S2Cells behave
|
|
2103
|
+
// identically to S2Polygons created from the vertices of those cells; this
|
|
2104
|
+
// previously was not the case, because S2Cells calculate their bounding
|
|
2105
|
+
// rectangles slightly differently, and S2Polygons created from them just
|
|
2106
|
+
// copied the S2Cell bounds.
|
|
2107
|
+
TEST(S2Polygon, TestS2CellConstructorAndContains) {
|
|
2108
|
+
S2LatLng latlng(S1Angle::E6(40565459), S1Angle::E6(-74645276));
|
|
2109
|
+
S2Cell cell(latlng);
|
|
2110
|
+
S2Polygon cell_as_polygon(cell);
|
|
2111
|
+
S2Polygon empty;
|
|
2112
|
+
S2Polygon polygon_copy;
|
|
2113
|
+
polygon_copy.InitToUnion(&cell_as_polygon, &empty);
|
|
2114
|
+
EXPECT_TRUE(polygon_copy.Contains(&cell_as_polygon));
|
|
2115
|
+
EXPECT_TRUE(cell_as_polygon.Contains(&polygon_copy));
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
TEST(S2PolygonTest, Project) {
|
|
2119
|
+
unique_ptr<S2Polygon> polygon(MakePolygon(kNear0 + kNear2));
|
|
2120
|
+
S2Point point;
|
|
2121
|
+
S2Point projected;
|
|
2122
|
+
|
|
2123
|
+
// The point inside the polygon should be projected into itself.
|
|
2124
|
+
point = s2textformat::MakePoint("1.1:0");
|
|
2125
|
+
projected = polygon->Project(point);
|
|
2126
|
+
EXPECT_TRUE(S2::ApproxEquals(point, projected));
|
|
2127
|
+
|
|
2128
|
+
// The point is on the outside of the polygon.
|
|
2129
|
+
point = s2textformat::MakePoint("5.1:-2");
|
|
2130
|
+
projected = polygon->Project(point);
|
|
2131
|
+
EXPECT_TRUE(S2::ApproxEquals(s2textformat::MakePoint("5:-2"), projected));
|
|
2132
|
+
|
|
2133
|
+
// The point is inside the hole in the polygon.
|
|
2134
|
+
point = s2textformat::MakePoint("-0.49:-0.49");
|
|
2135
|
+
projected = polygon->Project(point);
|
|
2136
|
+
EXPECT_TRUE(S2::ApproxEquals(s2textformat::MakePoint("-0.5:-0.5"),
|
|
2137
|
+
projected, S1Angle::Radians(1e-6)));
|
|
2138
|
+
|
|
2139
|
+
point = s2textformat::MakePoint("0:-3");
|
|
2140
|
+
projected = polygon->Project(point);
|
|
2141
|
+
EXPECT_TRUE(S2::ApproxEquals(s2textformat::MakePoint("0:-2"), projected));
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
// Helper function for testing the distance methods. "boundary_x" is the
|
|
2145
|
+
// expected result of projecting "x" onto the polygon boundary. For
|
|
2146
|
+
// convenience it can be set to S2Point() to indicate that (boundary_x == x).
|
|
2147
|
+
static void TestDistanceMethods(const S2Polygon& polygon, const S2Point& x,
|
|
2148
|
+
S2Point boundary_x) {
|
|
2149
|
+
// This error is not guaranteed by the implementation but is okay for tests.
|
|
2150
|
+
const S1Angle kMaxError = S1Angle::Radians(1e-15);
|
|
2151
|
+
|
|
2152
|
+
if (boundary_x == S2Point()) boundary_x = x;
|
|
2153
|
+
EXPECT_LE(S1Angle(boundary_x, polygon.ProjectToBoundary(x)), kMaxError);
|
|
2154
|
+
|
|
2155
|
+
if (polygon.is_empty() || polygon.is_full()) {
|
|
2156
|
+
EXPECT_EQ(S1Angle::Infinity(), polygon.GetDistanceToBoundary(x));
|
|
2157
|
+
} else {
|
|
2158
|
+
// EXPECT_NEAR only works with doubles.
|
|
2159
|
+
EXPECT_NEAR(S1Angle(x, boundary_x).degrees(),
|
|
2160
|
+
polygon.GetDistanceToBoundary(x).degrees(),
|
|
2161
|
+
kMaxError.degrees());
|
|
2162
|
+
}
|
|
2163
|
+
if (polygon.Contains(x)) {
|
|
2164
|
+
EXPECT_EQ(S1Angle::Zero(), polygon.GetDistance(x));
|
|
2165
|
+
EXPECT_EQ(x, polygon.Project(x));
|
|
2166
|
+
} else {
|
|
2167
|
+
EXPECT_EQ(polygon.GetDistanceToBoundary(x), polygon.GetDistance(x));
|
|
2168
|
+
EXPECT_EQ(polygon.ProjectToBoundary(x), polygon.Project(x));
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
TEST_F(S2PolygonTestBase, GetDistance) {
|
|
2173
|
+
// The empty and full loops don't have boundaries.
|
|
2174
|
+
TestDistanceMethods(*empty_, S2Point(0, 1, 0), S2Point());
|
|
2175
|
+
TestDistanceMethods(*full_, S2Point(0, 1, 0), S2Point());
|
|
2176
|
+
|
|
2177
|
+
// A polygon consisting of two nested rectangles centered around
|
|
2178
|
+
// S2LatLng(0,0). Note that because lines of latitude are curved on the
|
|
2179
|
+
// sphere, it is not straightforward to project points onto any edge except
|
|
2180
|
+
// along the equator. (The equator is the only line of latitude that is
|
|
2181
|
+
// also a geodesic.)
|
|
2182
|
+
unique_ptr<S2Polygon> nested(s2textformat::MakePolygon(
|
|
2183
|
+
"3:1, 3:-1, -3:-1, -3:1; 4:2, 4:-2, -4:-2, -4:2;"));
|
|
2184
|
+
|
|
2185
|
+
// All points on the boundary of the polygon should be at distance zero.
|
|
2186
|
+
for (int i = 0; i < nested->num_loops(); i++) {
|
|
2187
|
+
const S2Loop* loop = nested->loop(i);
|
|
2188
|
+
for (int j = 0; j < loop->num_vertices(); j++) {
|
|
2189
|
+
// A vertex.
|
|
2190
|
+
TestDistanceMethods(*nested, loop->vertex(j), S2Point());
|
|
2191
|
+
// A point along an edge.
|
|
2192
|
+
TestDistanceMethods(*nested, S2::Interpolate(
|
|
2193
|
+
S2Testing::rnd.RandDouble(), loop->vertex(j), loop->vertex(j+1)),
|
|
2194
|
+
S2Point());
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
// A point outside the outer shell that projects to an edge.
|
|
2198
|
+
TestDistanceMethods(*nested, S2LatLng::FromDegrees(0, -4.7).ToPoint(),
|
|
2199
|
+
S2LatLng::FromDegrees(0, -2).ToPoint());
|
|
2200
|
+
// A point outside the outer shell that projects to a vertex.
|
|
2201
|
+
TestDistanceMethods(*nested, S2LatLng::FromDegrees(6, -3).ToPoint(),
|
|
2202
|
+
S2LatLng::FromDegrees(4, -2).ToPoint());
|
|
2203
|
+
// A point inside the polygon that projects to an outer edge.
|
|
2204
|
+
TestDistanceMethods(*nested, S2LatLng::FromDegrees(0, 1.7).ToPoint(),
|
|
2205
|
+
S2LatLng::FromDegrees(0, 2).ToPoint());
|
|
2206
|
+
// A point inside the polygon that projects to an inner vertex.
|
|
2207
|
+
TestDistanceMethods(*nested, S2LatLng::FromDegrees(-3.3, -1.3).ToPoint(),
|
|
2208
|
+
S2LatLng::FromDegrees(-3, -1).ToPoint());
|
|
2209
|
+
// A point inside the inner hole.
|
|
2210
|
+
TestDistanceMethods(*nested, S2LatLng::FromDegrees(0, 0.1).ToPoint(),
|
|
2211
|
+
S2LatLng::FromDegrees(0, 1).ToPoint());
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
TEST_F(S2PolygonTestBase, Area) {
|
|
2215
|
+
EXPECT_DOUBLE_EQ(0.0, empty_->GetArea());
|
|
2216
|
+
EXPECT_DOUBLE_EQ(4 * M_PI, full_->GetArea());
|
|
2217
|
+
EXPECT_DOUBLE_EQ(2 * M_PI, south_H_->GetArea());
|
|
2218
|
+
EXPECT_DOUBLE_EQ(M_PI, far_H_south_H_->GetArea());
|
|
2219
|
+
|
|
2220
|
+
unique_ptr<S2Polygon> two_shells(
|
|
2221
|
+
MakePolygon(kCross1SideHole + kCrossCenterHole));
|
|
2222
|
+
EXPECT_DOUBLE_EQ(
|
|
2223
|
+
two_shells->loop(0)->GetArea() + two_shells->loop(1)->GetArea(),
|
|
2224
|
+
two_shells->GetArea());
|
|
2225
|
+
|
|
2226
|
+
unique_ptr<S2Polygon> holey_shell(MakePolygon(kCross1 + kCrossCenterHole));
|
|
2227
|
+
EXPECT_DOUBLE_EQ(
|
|
2228
|
+
holey_shell->loop(0)->GetArea() - holey_shell->loop(1)->GetArea(),
|
|
2229
|
+
holey_shell->GetArea());
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
TEST(S2Polygon, UninitializedIsValid) {
|
|
2233
|
+
S2Polygon polygon;
|
|
2234
|
+
EXPECT_TRUE(polygon.IsValid());
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
class IsValidTest : public testing::Test {
|
|
2238
|
+
public:
|
|
2239
|
+
IsValidTest() {
|
|
2240
|
+
init_oriented_ = false;
|
|
2241
|
+
modify_polygon_hook_ = nullptr;
|
|
2242
|
+
rnd_ = &S2Testing::rnd;
|
|
2243
|
+
rnd_->Reset(FLAGS_s2_random_seed);
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
~IsValidTest() override { Reset(); }
|
|
2247
|
+
|
|
2248
|
+
vector<S2Point>* AddLoop() {
|
|
2249
|
+
vloops_.push_back(make_unique<vector<S2Point>>());
|
|
2250
|
+
return vloops_.back().get();
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
// Create "num_loops" nested regular loops around a common center point.
|
|
2254
|
+
// All loops have the same number of vertices (at least "min_vertices").
|
|
2255
|
+
// Furthermore, the vertices at the same index position are collinear with
|
|
2256
|
+
// the common center point of all the loops. The loop radii decrease
|
|
2257
|
+
// exponentially in order to prevent accidental loop crossings when one of
|
|
2258
|
+
// the loops is modified.
|
|
2259
|
+
void AddConcentricLoops(int num_loops, int min_vertices) {
|
|
2260
|
+
S2_DCHECK_LE(num_loops, 10); // Because radii decrease exponentially.
|
|
2261
|
+
S2Point center = S2Testing::RandomPoint();
|
|
2262
|
+
int num_vertices = min_vertices + rnd_->Uniform(10);
|
|
2263
|
+
for (int i = 0; i < num_loops; ++i) {
|
|
2264
|
+
S1Angle radius = S1Angle::Degrees(80 * pow(0.1, i));
|
|
2265
|
+
*AddLoop() = S2Testing::MakeRegularPoints(center, radius, num_vertices);
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
void Reset() {
|
|
2270
|
+
vloops_.clear();
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
void CheckInvalid(const string& snippet) {
|
|
2274
|
+
vector<unique_ptr<S2Loop>> loops;
|
|
2275
|
+
for (const auto& vloop : vloops_) {
|
|
2276
|
+
loops.push_back(make_unique<S2Loop>(*vloop, S2Debug::DISABLE));
|
|
2277
|
+
}
|
|
2278
|
+
// Cannot replace with std::shuffle (b/65670707) since this uses an
|
|
2279
|
+
// incompatible random source which is also used as a source of randomness
|
|
2280
|
+
// in the surrounding code.
|
|
2281
|
+
// NOLINTNEXTLINE
|
|
2282
|
+
gtl::legacy_random_shuffle(loops.begin(), loops.end(), *rnd_);
|
|
2283
|
+
S2Polygon polygon;
|
|
2284
|
+
polygon.set_s2debug_override(S2Debug::DISABLE);
|
|
2285
|
+
if (init_oriented_) {
|
|
2286
|
+
polygon.InitOriented(std::move(loops));
|
|
2287
|
+
} else {
|
|
2288
|
+
polygon.InitNested(std::move(loops));
|
|
2289
|
+
}
|
|
2290
|
+
if (modify_polygon_hook_) (*modify_polygon_hook_)(&polygon);
|
|
2291
|
+
S2Error error;
|
|
2292
|
+
EXPECT_TRUE(polygon.FindValidationError(&error));
|
|
2293
|
+
EXPECT_TRUE(error.text().find(snippet) != string::npos)
|
|
2294
|
+
<< "\nActual error: " << error << "\nExpected substring: " << snippet;
|
|
2295
|
+
Reset();
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
protected:
|
|
2299
|
+
static const int kIters = 100;
|
|
2300
|
+
|
|
2301
|
+
bool init_oriented_;
|
|
2302
|
+
void (*modify_polygon_hook_)(S2Polygon*);
|
|
2303
|
+
S2Testing::Random* rnd_;
|
|
2304
|
+
vector<unique_ptr<vector<S2Point>>> vloops_;
|
|
2305
|
+
};
|
|
2306
|
+
|
|
2307
|
+
TEST_F(IsValidTest, UnitLength) {
|
|
2308
|
+
// This test can only be run in optimized builds because there are
|
|
2309
|
+
// S2_DCHECK(IsUnitLength()) calls scattered throughout the S2 code.
|
|
2310
|
+
if (google::DEBUG_MODE) return;
|
|
2311
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
2312
|
+
AddConcentricLoops(1 + rnd_->Uniform(6), 3 /*min_vertices*/);
|
|
2313
|
+
vector<S2Point>* vloop = vloops_[rnd_->Uniform(vloops_.size())].get();
|
|
2314
|
+
S2Point* p = &(*vloop)[rnd_->Uniform(vloop->size())];
|
|
2315
|
+
switch (rnd_->Uniform(3)) {
|
|
2316
|
+
case 0: *p = S2Point(0, 0, 0); break;
|
|
2317
|
+
case 1: *p *= 1e-30 * pow(1e60, rnd_->RandDouble()); break;
|
|
2318
|
+
case 2: *p = numeric_limits<double>::quiet_NaN() * S2Point(); break;
|
|
2319
|
+
}
|
|
2320
|
+
CheckInvalid("unit length");
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
TEST_F(IsValidTest, VertexCount) {
|
|
2325
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
2326
|
+
vector<S2Point>* vloop = AddLoop();
|
|
2327
|
+
if (rnd_->OneIn(2)) {
|
|
2328
|
+
vloop->push_back(S2Testing::RandomPoint());
|
|
2329
|
+
vloop->push_back(S2Testing::RandomPoint());
|
|
2330
|
+
}
|
|
2331
|
+
CheckInvalid("at least 3 vertices");
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
TEST_F(IsValidTest, DuplicateVertex) {
|
|
2336
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
2337
|
+
AddConcentricLoops(1, 3 /*min_vertices*/);
|
|
2338
|
+
vector<S2Point>* vloop = vloops_[0].get();
|
|
2339
|
+
int n = vloop->size();
|
|
2340
|
+
int i = rnd_->Uniform(n);
|
|
2341
|
+
int j = rnd_->Uniform(n - 1);
|
|
2342
|
+
(*vloop)[i] = (*vloop)[j + (j >= i)];
|
|
2343
|
+
CheckInvalid("duplicate vertex");
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
TEST_F(IsValidTest, SelfIntersection) {
|
|
2348
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
2349
|
+
// Use multiple loops so that we can test both holes and shells. We need
|
|
2350
|
+
// at least 5 vertices so that the modified edges don't intersect any
|
|
2351
|
+
// nested loops.
|
|
2352
|
+
AddConcentricLoops(1 + rnd_->Uniform(6), 5 /*min_vertices*/);
|
|
2353
|
+
vector<S2Point>* vloop = vloops_[rnd_->Uniform(vloops_.size())].get();
|
|
2354
|
+
int n = vloop->size();
|
|
2355
|
+
int i = rnd_->Uniform(n);
|
|
2356
|
+
swap((*vloop)[i], (*vloop)[(i+1) % n]);
|
|
2357
|
+
CheckInvalid("crosses edge");
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
TEST_F(IsValidTest, EmptyLoop) {
|
|
2362
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
2363
|
+
AddConcentricLoops(rnd_->Uniform(5), 3 /*min_vertices*/);
|
|
2364
|
+
*AddLoop() = S2Loop::kEmpty();
|
|
2365
|
+
CheckInvalid("empty loop");
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
TEST_F(IsValidTest, FullLoop) {
|
|
2370
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
2371
|
+
// This is only an error if there is at least one other loop.
|
|
2372
|
+
AddConcentricLoops(1 + rnd_->Uniform(5), 3 /*min_vertices*/);
|
|
2373
|
+
*AddLoop() = S2Loop::kFull();
|
|
2374
|
+
CheckInvalid("full loop");
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
TEST_F(IsValidTest, LoopsCrossing) {
|
|
2379
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
2380
|
+
AddConcentricLoops(2, 4 /*min_vertices*/);
|
|
2381
|
+
// Both loops have the same number of vertices, and vertices at the same
|
|
2382
|
+
// index position are collinear with the center point, so we can create a
|
|
2383
|
+
// crossing by simply exchanging two vertices at the same index position.
|
|
2384
|
+
int n = vloops_[0]->size();
|
|
2385
|
+
int i = rnd_->Uniform(n);
|
|
2386
|
+
swap((*vloops_[0])[i], (*vloops_[1])[i]);
|
|
2387
|
+
if (rnd_->OneIn(2)) {
|
|
2388
|
+
// By copy the two adjacent vertices from one loop to the other, we can
|
|
2389
|
+
// ensure that the crossings happen at vertices rather than edges.
|
|
2390
|
+
(*vloops_[0])[(i+1) % n] = (*vloops_[1])[(i+1) % n];
|
|
2391
|
+
(*vloops_[0])[(i+n-1) % n] = (*vloops_[1])[(i+n-1) % n];
|
|
2392
|
+
}
|
|
2393
|
+
CheckInvalid("crosses loop");
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
TEST_F(IsValidTest, DuplicateEdge) {
|
|
2398
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
2399
|
+
AddConcentricLoops(2, 4 /*min_vertices*/);
|
|
2400
|
+
int n = vloops_[0]->size();
|
|
2401
|
+
if (rnd_->OneIn(2)) {
|
|
2402
|
+
// Create a shared edge (same direction in both loops).
|
|
2403
|
+
int i = rnd_->Uniform(n);
|
|
2404
|
+
(*vloops_[0])[i] = (*vloops_[1])[i];
|
|
2405
|
+
(*vloops_[0])[(i+1) % n] = (*vloops_[1])[(i+1) % n];
|
|
2406
|
+
} else {
|
|
2407
|
+
// Create a reversed edge (opposite direction in each loop) by cutting
|
|
2408
|
+
// loop 0 into two halves along one of its diagonals and replacing both
|
|
2409
|
+
// loops with the result.
|
|
2410
|
+
int split = 2 + rnd_->Uniform(n - 3);
|
|
2411
|
+
vloops_[1]->clear();
|
|
2412
|
+
vloops_[1]->push_back((*vloops_[0])[0]);
|
|
2413
|
+
for (int i = split; i < n; ++i) {
|
|
2414
|
+
vloops_[1]->push_back((*vloops_[0])[i]);
|
|
2415
|
+
}
|
|
2416
|
+
vloops_[0]->resize(split + 1);
|
|
2417
|
+
}
|
|
2418
|
+
CheckInvalid("has duplicate");
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
TEST_F(IsValidTest, InconsistentOrientations) {
|
|
2423
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
2424
|
+
AddConcentricLoops(2 + rnd_->Uniform(5), 3 /*min_vertices*/);
|
|
2425
|
+
init_oriented_ = true;
|
|
2426
|
+
CheckInvalid("Inconsistent loop orientations");
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
static void SetInvalidLoopDepth(S2Polygon* polygon) {
|
|
2431
|
+
int i = S2Testing::rnd.Uniform(polygon->num_loops());
|
|
2432
|
+
if (i == 0 || S2Testing::rnd.OneIn(3)) {
|
|
2433
|
+
polygon->loop(i)->set_depth(-1);
|
|
2434
|
+
} else {
|
|
2435
|
+
polygon->loop(i)->set_depth(polygon->loop(i-1)->depth() + 2);
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
TEST_F(IsValidTest, LoopDepthNegative) {
|
|
2440
|
+
modify_polygon_hook_ = SetInvalidLoopDepth;
|
|
2441
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
2442
|
+
AddConcentricLoops(1 + rnd_->Uniform(4), 3 /*min_vertices*/);
|
|
2443
|
+
CheckInvalid("invalid loop depth");
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
static void SetInvalidLoopNesting(S2Polygon* polygon) {
|
|
2448
|
+
int i = S2Testing::rnd.Uniform(polygon->num_loops());
|
|
2449
|
+
polygon->loop(i)->Invert();
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
TEST_F(IsValidTest, LoopNestingInvalid) {
|
|
2453
|
+
modify_polygon_hook_ = SetInvalidLoopNesting;
|
|
2454
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
2455
|
+
AddConcentricLoops(2 + rnd_->Uniform(4), 3 /*min_vertices*/);
|
|
2456
|
+
// Randomly invert all the loops in order to generate cases where the
|
|
2457
|
+
// outer loop encompasses almost the entire sphere. This tests different
|
|
2458
|
+
// code paths because bounding box checks are not as useful.
|
|
2459
|
+
if (rnd_->OneIn(2)) {
|
|
2460
|
+
for (const auto& loop : vloops_) {
|
|
2461
|
+
std::reverse(loop->begin(), loop->end());
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
CheckInvalid("Invalid nesting");
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
TEST_F(IsValidTest, FuzzTest) {
|
|
2469
|
+
// Check that the S2Loop/S2Polygon constructors and IsValid() don't crash
|
|
2470
|
+
// when they receive arbitrary invalid input. (We don't test large inputs;
|
|
2471
|
+
// it is assumed that the client enforces their own size limits before even
|
|
2472
|
+
// attempting to construct geometric objects.)
|
|
2473
|
+
if (google::DEBUG_MODE)
|
|
2474
|
+
return; // Requires unit length vertices.
|
|
2475
|
+
for (int iter = 0; iter < kIters; ++iter) {
|
|
2476
|
+
int num_loops = 1 + rnd_->Uniform(10);
|
|
2477
|
+
for (int i = 0; i < num_loops; ++i) {
|
|
2478
|
+
int num_vertices = rnd_->Uniform(10);
|
|
2479
|
+
vector<S2Point>* vloop = AddLoop();
|
|
2480
|
+
while (vloop->size() < num_vertices) {
|
|
2481
|
+
// Since the number of vertices is random, we automatically test empty
|
|
2482
|
+
// loops, full loops, and invalid vertex counts. Also since most
|
|
2483
|
+
// vertices are random, we automatically get self-intersections and
|
|
2484
|
+
// loop crossings. That leaves zero and NaN vertices, duplicate
|
|
2485
|
+
// vertices, and duplicate edges to be created explicitly.
|
|
2486
|
+
if (rnd_->OneIn(10)) {
|
|
2487
|
+
// Zero vertex.
|
|
2488
|
+
vloop->push_back(S2Point(0, 0, 0));
|
|
2489
|
+
} else if (rnd_->OneIn(10)) {
|
|
2490
|
+
// NaN vertex.
|
|
2491
|
+
vloop->push_back(numeric_limits<double>::quiet_NaN() * S2Point());
|
|
2492
|
+
} else if (rnd_->OneIn(10) && !vloop->empty()) {
|
|
2493
|
+
// Duplicate vertex.
|
|
2494
|
+
vloop->push_back((*vloop)[rnd_->Uniform(vloop->size())]);
|
|
2495
|
+
} else if (rnd_->OneIn(10) && vloop->size() + 2 <= num_vertices) {
|
|
2496
|
+
// Try to copy an edge from a random loop.
|
|
2497
|
+
vector<S2Point>* other = vloops_[rnd_->Uniform(vloops_.size())].get();
|
|
2498
|
+
int n = other->size();
|
|
2499
|
+
if (n >= 2) {
|
|
2500
|
+
int k0 = rnd_->Uniform(n);
|
|
2501
|
+
int k1 = (k0 + 1) % n;
|
|
2502
|
+
if (rnd_->OneIn(2)) swap(k0, k1); // Copy reversed edge.
|
|
2503
|
+
vloop->push_back((*other)[k0]);
|
|
2504
|
+
vloop->push_back((*other)[k1]);
|
|
2505
|
+
}
|
|
2506
|
+
} else {
|
|
2507
|
+
// Random non-unit-length point.
|
|
2508
|
+
S2Point p = S2Testing::RandomPoint();
|
|
2509
|
+
vloop->push_back(1e-30 * pow(1e60, rnd_->RandDouble()) * p);
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
CheckInvalid(""); // We could get any error message.
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
// Returns the diameter of a loop (maximum distance between any two
|
|
2518
|
+
// points in the loop).
|
|
2519
|
+
S1Angle LoopDiameter(const S2Loop& loop) {
|
|
2520
|
+
S1Angle diameter;
|
|
2521
|
+
for (int i = 0; i < loop.num_vertices(); ++i) {
|
|
2522
|
+
S2Point test_point = loop.vertex(i);
|
|
2523
|
+
for (int j = i + 1; j < loop.num_vertices(); ++j) {
|
|
2524
|
+
diameter = max(diameter,
|
|
2525
|
+
S2::GetDistance(test_point, loop.vertex(j),
|
|
2526
|
+
loop.vertex(j+1)));
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
return diameter;
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
// Returns the maximum distance from any vertex of poly_a to poly_b, that is,
|
|
2533
|
+
// the directed Haussdorf distance of the set of vertices of poly_a to the
|
|
2534
|
+
// boundary of poly_b.
|
|
2535
|
+
//
|
|
2536
|
+
// Doesn't consider loops from poly_a that have diameter less than min_diameter
|
|
2537
|
+
// in degrees.
|
|
2538
|
+
double MaximumDistanceInDegrees(const S2Polygon& poly_a,
|
|
2539
|
+
const S2Polygon& poly_b,
|
|
2540
|
+
double min_diameter_in_degrees) {
|
|
2541
|
+
double min_distance = 360;
|
|
2542
|
+
bool has_big_loops = false;
|
|
2543
|
+
for (int l = 0; l < poly_a.num_loops(); ++l) {
|
|
2544
|
+
const S2Loop* a_loop = poly_a.loop(l);
|
|
2545
|
+
if (LoopDiameter(*a_loop).degrees() <= min_diameter_in_degrees) {
|
|
2546
|
+
continue;
|
|
2547
|
+
}
|
|
2548
|
+
has_big_loops = true;
|
|
2549
|
+
for (int v = 0; v < a_loop->num_vertices(); ++v) {
|
|
2550
|
+
double distance = poly_b.GetDistance(a_loop->vertex(v)).degrees();
|
|
2551
|
+
if (distance < min_distance) {
|
|
2552
|
+
min_distance = distance;
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
if (has_big_loops) {
|
|
2557
|
+
return min_distance;
|
|
2558
|
+
} else {
|
|
2559
|
+
return 0.; // As if the first polygon were empty.
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
class S2PolygonSimplifierTest : public ::testing::Test {
|
|
2564
|
+
protected:
|
|
2565
|
+
void SetInput(unique_ptr<S2Polygon> poly, double tolerance_in_degrees) {
|
|
2566
|
+
original = std::move(poly);
|
|
2567
|
+
|
|
2568
|
+
simplified = make_unique<S2Polygon>();
|
|
2569
|
+
simplified->InitToSimplified(*original,
|
|
2570
|
+
s2builderutil::IdentitySnapFunction(
|
|
2571
|
+
S1Angle::Degrees(tolerance_in_degrees)));
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
void SetInput(const string& poly, double tolerance_in_degrees) {
|
|
2575
|
+
SetInput(s2textformat::MakePolygon(poly), tolerance_in_degrees);
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
unique_ptr<S2Polygon> simplified;
|
|
2579
|
+
unique_ptr<S2Polygon> original;
|
|
2580
|
+
};
|
|
2581
|
+
|
|
2582
|
+
TEST_F(S2PolygonSimplifierTest, NoSimplification) {
|
|
2583
|
+
SetInput("0:0, 0:20, 20:20, 20:0", 1.0);
|
|
2584
|
+
EXPECT_EQ(4, simplified->num_vertices());
|
|
2585
|
+
|
|
2586
|
+
EXPECT_EQ(0, MaximumDistanceInDegrees(*simplified, *original, 0));
|
|
2587
|
+
EXPECT_EQ(0, MaximumDistanceInDegrees(*original, *simplified, 0));
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
// Here, 10:-2 will be removed and 0:0-20:0 will intersect two edges.
|
|
2591
|
+
// (The resulting polygon will in fact probably have more edges.)
|
|
2592
|
+
TEST_F(S2PolygonSimplifierTest, SimplifiedLoopSelfIntersects) {
|
|
2593
|
+
SetInput("0:0, 0:20, 10:-0.1, 20:20, 20:0, 10:-0.2", 0.22);
|
|
2594
|
+
|
|
2595
|
+
// The simplified polygon has the same number of vertices but it should now
|
|
2596
|
+
// consists of two loops rather than one.
|
|
2597
|
+
EXPECT_EQ(2, simplified->num_loops());
|
|
2598
|
+
EXPECT_GE(0.22, MaximumDistanceInDegrees(*simplified, *original, 0));
|
|
2599
|
+
EXPECT_GE(0.22, MaximumDistanceInDegrees(*original, *simplified, 0.22));
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
TEST_F(S2PolygonSimplifierTest, NoSimplificationManyLoops) {
|
|
2603
|
+
SetInput("0:0, 0:1, 1:0; 0:20, 0:21, 1:20; "
|
|
2604
|
+
"20:20, 20:21, 21:20; 20:0, 20:1, 21:0", 0.01);
|
|
2605
|
+
EXPECT_EQ(0, MaximumDistanceInDegrees(*simplified, *original, 0));
|
|
2606
|
+
EXPECT_EQ(0, MaximumDistanceInDegrees(*original, *simplified, 0));
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
TEST_F(S2PolygonSimplifierTest, TinyLoopDisappears) {
|
|
2610
|
+
SetInput("0:0, 0:1, 1:1, 1:0", 1.1);
|
|
2611
|
+
EXPECT_TRUE(simplified->is_empty());
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
TEST_F(S2PolygonSimplifierTest, StraightLinesAreSimplified) {
|
|
2615
|
+
SetInput("0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0,"
|
|
2616
|
+
"6:1, 5:1, 4:1, 3:1, 2:1, 1:1, 0:1", 0.01);
|
|
2617
|
+
EXPECT_EQ(4, simplified->num_vertices());
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
TEST_F(S2PolygonSimplifierTest, EdgeSplitInManyPieces) {
|
|
2621
|
+
// near_square's right four-point side will be simplified to a vertical
|
|
2622
|
+
// line at lng=7.9, that will cut the 9 teeth of the saw (the edge will
|
|
2623
|
+
// therefore be broken into 19 pieces).
|
|
2624
|
+
const string saw =
|
|
2625
|
+
"1:1, 1:8, 2:2, 2:8, 3:2, 3:8, 4:2, 4:8, 5:2, 5:8,"
|
|
2626
|
+
"6:2, 6:8, 7:2, 7:8, 8:2, 8:8, 9:2, 9:8, 10:1";
|
|
2627
|
+
const string near_square =
|
|
2628
|
+
"0:0, 0:7.9, 1:8.1, 10:8.1, 11:7.9, 11:0";
|
|
2629
|
+
SetInput(saw + ";" + near_square, 0.21);
|
|
2630
|
+
|
|
2631
|
+
EXPECT_TRUE(simplified->IsValid());
|
|
2632
|
+
EXPECT_GE(0.11, MaximumDistanceInDegrees(*simplified, *original, 0));
|
|
2633
|
+
EXPECT_GE(0.11, MaximumDistanceInDegrees(*original, *simplified, 0));
|
|
2634
|
+
// The resulting polygon's 9 little teeth are very small and disappear
|
|
2635
|
+
// due to the vertex_merge_radius of the polygon builder. There remains
|
|
2636
|
+
// nine loops.
|
|
2637
|
+
EXPECT_EQ(9, simplified->num_loops());
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
TEST_F(S2PolygonSimplifierTest, EdgesOverlap) {
|
|
2641
|
+
// Two loops, One edge of the second one ([0:1 - 0:2]) is part of an
|
|
2642
|
+
// edge of the first one..
|
|
2643
|
+
SetInput("0:0, 0:3, 1:0; 0:1, -1:1, 0:2", 0.01);
|
|
2644
|
+
unique_ptr<S2Polygon> true_poly(
|
|
2645
|
+
s2textformat::MakePolygon("0:3, 1:0, 0:0, 0:1, -1:1, 0:2"));
|
|
2646
|
+
EXPECT_TRUE(simplified->BoundaryApproxEquals(*true_poly,
|
|
2647
|
+
S1Angle::Radians(1e-15)));
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
// Creates a polygon from loops specified as a comma separated list of u:v
|
|
2651
|
+
// coordinates relative to a cell. The loop "0:0, 1:0, 1:1, 0:1" is
|
|
2652
|
+
// counter-clockwise.
|
|
2653
|
+
unique_ptr<S2Polygon> MakeCellPolygon(
|
|
2654
|
+
const S2Cell& cell, const vector<const char *>& strs) {
|
|
2655
|
+
vector<unique_ptr<S2Loop>> loops;
|
|
2656
|
+
for (auto str : strs) {
|
|
2657
|
+
vector<S2LatLng> points = s2textformat::ParseLatLngs(str);
|
|
2658
|
+
vector<S2Point> loop_vertices;
|
|
2659
|
+
R2Rect uv = cell.GetBoundUV();
|
|
2660
|
+
for (const S2LatLng& p : points) {
|
|
2661
|
+
double u = p.lat().degrees(), v = p.lng().degrees();
|
|
2662
|
+
loop_vertices.push_back(
|
|
2663
|
+
S2::FaceUVtoXYZ(cell.face(),
|
|
2664
|
+
uv[0][0] * (1 - u) + uv[0][1] * u,
|
|
2665
|
+
uv[1][0] * (1 - v) + uv[1][1] * v).Normalize());
|
|
2666
|
+
}
|
|
2667
|
+
loops.emplace_back(new S2Loop(loop_vertices));
|
|
2668
|
+
}
|
|
2669
|
+
return make_unique<S2Polygon>(std::move(loops));
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
TEST(InitToSimplifiedInCell, PointsOnCellBoundaryKept) {
|
|
2673
|
+
S2Cell cell(S2CellId::FromToken("89c25c"));
|
|
2674
|
+
auto polygon = MakeCellPolygon(cell, {"0.1:0, 0.2:0, 0.2:0.5"});
|
|
2675
|
+
S1Angle tolerance =
|
|
2676
|
+
S1Angle(polygon->loop(0)->vertex(0), polygon->loop(0)->vertex(1)) * 1.1;
|
|
2677
|
+
S2Polygon simplified;
|
|
2678
|
+
simplified.InitToSimplified(*polygon,
|
|
2679
|
+
s2builderutil::IdentitySnapFunction(tolerance));
|
|
2680
|
+
EXPECT_TRUE(simplified.is_empty());
|
|
2681
|
+
S2Polygon simplified_in_cell;
|
|
2682
|
+
simplified_in_cell.InitToSimplifiedInCell(polygon.get(), cell, tolerance);
|
|
2683
|
+
EXPECT_TRUE(simplified_in_cell.BoundaryEquals(polygon.get()));
|
|
2684
|
+
EXPECT_EQ(3, simplified_in_cell.num_vertices());
|
|
2685
|
+
EXPECT_EQ(-1, simplified.GetSnapLevel());
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
TEST(InitToSimplifiedInCell, PointsInsideCellSimplified) {
|
|
2689
|
+
S2CellId cell_id = S2CellId::FromToken("89c25c");
|
|
2690
|
+
S2Cell cell(cell_id);
|
|
2691
|
+
auto polygon = MakeCellPolygon(
|
|
2692
|
+
cell, {"0.3:0, 0.4:0, 0.4:0.5, 0.4:0.8, 0.2:0.8"});
|
|
2693
|
+
S1Angle tolerance =
|
|
2694
|
+
S1Angle(polygon->loop(0)->vertex(0), polygon->loop(0)->vertex(1)) * 1.1;
|
|
2695
|
+
S2Polygon simplified;
|
|
2696
|
+
simplified.InitToSimplifiedInCell(polygon.get(), cell, tolerance);
|
|
2697
|
+
EXPECT_TRUE(simplified.BoundaryNear(*polygon, S1Angle::Radians(1e-15)));
|
|
2698
|
+
EXPECT_EQ(4, simplified.num_vertices());
|
|
2699
|
+
EXPECT_EQ(-1, simplified.GetSnapLevel());
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
TEST(InitToSimplifiedInCell, CellCornerKept) {
|
|
2703
|
+
S2Cell cell(S2CellId::FromToken("00001"));
|
|
2704
|
+
auto input = MakeCellPolygon(cell, {"1:0, 1:0.05, 0.99:0"});
|
|
2705
|
+
S1Angle tolerance = 0.02 * S1Angle(cell.GetVertex(0), cell.GetVertex(1));
|
|
2706
|
+
S2Polygon simplified;
|
|
2707
|
+
simplified.InitToSimplifiedInCell(input.get(), cell, tolerance);
|
|
2708
|
+
EXPECT_TRUE(simplified.BoundaryNear(*input, S1Angle::Radians(1e-15)));
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
TEST(InitToSimplifiedInCell, NarrowStripRemoved) {
|
|
2712
|
+
S2Cell cell(S2CellId::FromToken("00001"));
|
|
2713
|
+
auto input = MakeCellPolygon(cell, {"0.9:0, 0.91:0, 0.91:1, 0.9:1"});
|
|
2714
|
+
S1Angle tolerance = 0.02 * S1Angle(cell.GetVertex(0), cell.GetVertex(1));
|
|
2715
|
+
S2Polygon simplified;
|
|
2716
|
+
simplified.InitToSimplifiedInCell(input.get(), cell, tolerance);
|
|
2717
|
+
EXPECT_TRUE(simplified.is_empty());
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
TEST(InitToSimplifiedInCell, NarrowGapRemoved) {
|
|
2721
|
+
S2Cell cell(S2CellId::FromToken("00001"));
|
|
2722
|
+
auto input = MakeCellPolygon(
|
|
2723
|
+
cell, {"0.7:0, 0.75:0, 0.75:1, 0.7:1", "0.76:0, 0.8:0, 0.8:1, 0.76:1"});
|
|
2724
|
+
auto expected = MakeCellPolygon(cell, {"0.7:0, 0.8:0, 0.8:1, 0.7:1"});
|
|
2725
|
+
S1Angle tolerance = 0.02 * S1Angle(cell.GetVertex(0), cell.GetVertex(1));
|
|
2726
|
+
S2Polygon simplified;
|
|
2727
|
+
simplified.InitToSimplifiedInCell(input.get(), cell, tolerance);
|
|
2728
|
+
EXPECT_TRUE(simplified.BoundaryNear(*expected, S1Angle::Radians(1e-15)));
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
TEST(InitToSimplifiedInCell, CloselySpacedEdgeVerticesKept) {
|
|
2732
|
+
S2Cell cell(S2CellId::FromToken("00001"));
|
|
2733
|
+
auto input = MakeCellPolygon(
|
|
2734
|
+
cell, {"0:0.303, 0:0.302, 0:0.301, 0:0.3, 0.1:0.3, 0.1:0.4"});
|
|
2735
|
+
S1Angle tolerance = 0.02 * S1Angle(cell.GetVertex(0), cell.GetVertex(1));
|
|
2736
|
+
S2Polygon simplified;
|
|
2737
|
+
simplified.InitToSimplifiedInCell(input.get(), cell, tolerance);
|
|
2738
|
+
EXPECT_TRUE(simplified.BoundaryApproxEquals(*input, S1Angle::Radians(1e-15)));
|
|
2739
|
+
}
|
|
2740
|
+
|
|
2741
|
+
TEST(InitToSimplifiedInCell, PolylineAssemblyBug) {
|
|
2742
|
+
S2Cell cell(S2CellId::FromToken("5701"));
|
|
2743
|
+
auto polygon = MakePolygon(
|
|
2744
|
+
"55.8699252:-163.9412145, " // South-west corner of 5701
|
|
2745
|
+
"54.7672352:-166.7579678, " // North-east corner of 5701
|
|
2746
|
+
/* Offending part: a tiny triangle near south-east corner */
|
|
2747
|
+
"54.7109214:-164.6376338, " // forced vertex, on edge 4
|
|
2748
|
+
"54.7140193:-164.6398404, "
|
|
2749
|
+
"54.7113202:-164.6374015"); // forced vertex, on edge 4
|
|
2750
|
+
S1Angle tolerance = S1Angle::Radians(2.138358e-05); // 136.235m
|
|
2751
|
+
S1Angle max_dist = S1Angle::Radians(2.821947e-09); // 18mm
|
|
2752
|
+
S2Polygon simplified_in_cell;
|
|
2753
|
+
simplified_in_cell.InitToSimplifiedInCell(polygon.get(), cell, tolerance,
|
|
2754
|
+
max_dist);
|
|
2755
|
+
EXPECT_FALSE(simplified_in_cell.is_empty());
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
TEST(InitToSimplifiedInCell, InteriorEdgesSnappedToBoundary) {
|
|
2759
|
+
auto polygon = s2textformat::MakePolygonOrDie(
|
|
2760
|
+
"37.8011672:-122.3247322, 37.8011648:-122.3247399, "
|
|
2761
|
+
"37.8011647:-122.3247403, 37.8011646:-122.3247408, "
|
|
2762
|
+
"37.8011645:-122.3247411, 37.8011633:-122.3247449, "
|
|
2763
|
+
"37.8011621:-122.3247334");
|
|
2764
|
+
S2Cell cell(S2CellId::FromDebugString("4/001013300"));
|
|
2765
|
+
S1Angle snap_radius = S2Testing::MetersToAngle(1.0);
|
|
2766
|
+
S1Angle boundary_tolerance =
|
|
2767
|
+
S1Angle::Radians(0.5 * S2::kMaxWidth.GetValue(S2CellId::kMaxLevel - 1)) +
|
|
2768
|
+
s2builderutil::IntLatLngSnapFunction::MinSnapRadiusForExponent(7);
|
|
2769
|
+
S2Polygon simplified_polygon;
|
|
2770
|
+
simplified_polygon.set_s2debug_override(S2Debug::DISABLE);
|
|
2771
|
+
simplified_polygon.InitToSimplifiedInCell(polygon.get(), cell, snap_radius,
|
|
2772
|
+
boundary_tolerance);
|
|
2773
|
+
S2Error error;
|
|
2774
|
+
EXPECT_FALSE(simplified_polygon.FindValidationError(&error)) << error;
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
|
|
2778
|
+
unique_ptr<S2Polygon> MakeRegularPolygon(
|
|
2779
|
+
const string& center, int num_points, double radius_in_degrees) {
|
|
2780
|
+
S1Angle radius = S1Angle::Degrees(radius_in_degrees);
|
|
2781
|
+
return make_unique<S2Polygon>(S2Loop::MakeRegularLoop(
|
|
2782
|
+
s2textformat::MakePoint(center), radius, num_points));
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2785
|
+
// Tests that a regular polygon with many points gets simplified
|
|
2786
|
+
// enough.
|
|
2787
|
+
TEST_F(S2PolygonSimplifierTest, LargeRegularPolygon) {
|
|
2788
|
+
const double kRadius = 2.; // in degrees
|
|
2789
|
+
const int num_initial_points = 1000;
|
|
2790
|
+
const int num_desired_points = 250;
|
|
2791
|
+
double tolerance = 1.05 * kRadius * (1 - cos(M_PI / num_desired_points));
|
|
2792
|
+
|
|
2793
|
+
SetInput(MakeRegularPolygon("0:0", num_initial_points, kRadius), tolerance);
|
|
2794
|
+
|
|
2795
|
+
EXPECT_GE(tolerance, MaximumDistanceInDegrees(*simplified, *original, 0));
|
|
2796
|
+
EXPECT_GE(tolerance, MaximumDistanceInDegrees(*original, *simplified, 0));
|
|
2797
|
+
EXPECT_GE(250, simplified->num_vertices());
|
|
2798
|
+
EXPECT_LE(200, simplified->num_vertices());
|
|
2799
|
+
}
|
|
2800
|
+
|
|
2801
|
+
class S2PolygonDecodeTest : public ::testing::Test {
|
|
2802
|
+
protected:
|
|
2803
|
+
S2PolygonDecodeTest() : data_array_(kMaxBytes) {
|
|
2804
|
+
encoder_.reset(data_array_.data(), kMaxBytes);
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
~S2PolygonDecodeTest() override {}
|
|
2808
|
+
|
|
2809
|
+
void AppendByte(int value) {
|
|
2810
|
+
encoder_.put8(value);
|
|
2811
|
+
}
|
|
2812
|
+
|
|
2813
|
+
void AppendInt32(int value) {
|
|
2814
|
+
encoder_.put32(value);
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
void AppendRandomData(int size) {
|
|
2818
|
+
for (int i = 0; i < size && encoder_.avail() > 0; ++i) {
|
|
2819
|
+
AppendByte(random_.Uniform(256));
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
void AppendRandomData() {
|
|
2824
|
+
AppendRandomData(random_.Uniform(kMaxBytes));
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
void AppendFakeUncompressedEncodingData() {
|
|
2828
|
+
AppendByte(1); // polygon number
|
|
2829
|
+
AppendByte(0); // unused
|
|
2830
|
+
AppendByte(0); // "has holes" flag
|
|
2831
|
+
AppendInt32(PickRandomCount()); // num loops
|
|
2832
|
+
AppendByte(1); // loop version
|
|
2833
|
+
AppendInt32(PickRandomCount()); // num vertices
|
|
2834
|
+
AppendRandomData(); // junk to fill out the buffer
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2837
|
+
void AppendFakeCompressedEncodingData() {
|
|
2838
|
+
AppendByte(4); // polygon number
|
|
2839
|
+
AppendByte(random_.Uniform(50)); // snap level
|
|
2840
|
+
AppendInt32(PickRandomCount()); // num loops
|
|
2841
|
+
AppendInt32(PickRandomCount()); // num vertices
|
|
2842
|
+
AppendRandomData(); // junk to fill out the buffer
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2845
|
+
int32 PickRandomCount() {
|
|
2846
|
+
if (random_.OneIn(10)) {
|
|
2847
|
+
return -1;
|
|
2848
|
+
}
|
|
2849
|
+
if (random_.OneIn(10)) {
|
|
2850
|
+
return 0;
|
|
2851
|
+
}
|
|
2852
|
+
if (random_.OneIn(10)) {
|
|
2853
|
+
return 1000000000;
|
|
2854
|
+
}
|
|
2855
|
+
if (random_.OneIn(2)) {
|
|
2856
|
+
return random_.Uniform(1000000000);
|
|
2857
|
+
}
|
|
2858
|
+
return random_.Uniform(1000);
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2861
|
+
bool Test() {
|
|
2862
|
+
decoder_.reset(data_array_.data(), encoder_.length());
|
|
2863
|
+
encoder_.clear();
|
|
2864
|
+
S2Polygon polygon;
|
|
2865
|
+
polygon.set_s2debug_override(S2Debug::DISABLE);
|
|
2866
|
+
return polygon.Decode(&decoder_);
|
|
2867
|
+
}
|
|
2868
|
+
|
|
2869
|
+
// Random number generator.
|
|
2870
|
+
S2Testing::Random random_;
|
|
2871
|
+
|
|
2872
|
+
// Maximum size of the data array.
|
|
2873
|
+
const int kMaxBytes = 256;
|
|
2874
|
+
|
|
2875
|
+
// The data array.
|
|
2876
|
+
absl::FixedArray<int8> data_array_;
|
|
2877
|
+
|
|
2878
|
+
// Encoder that is used to put data into the array.
|
|
2879
|
+
Encoder encoder_;
|
|
2880
|
+
|
|
2881
|
+
// Decoder used to extract data from the array.
|
|
2882
|
+
Decoder decoder_;
|
|
2883
|
+
};
|
|
2884
|
+
|
|
2885
|
+
TEST_F(S2PolygonDecodeTest, FuzzUncompressedEncoding) {
|
|
2886
|
+
// Some parts of the S2 library S2_DCHECK on invalid data, even if we set
|
|
2887
|
+
// FLAGS_s2debug to false or use S2Polygon::set_s2debug_override. So we
|
|
2888
|
+
// only run this test in opt mode.
|
|
2889
|
+
#ifdef NDEBUG
|
|
2890
|
+
for (int i = 0; i < 100000; ++i) {
|
|
2891
|
+
AppendFakeUncompressedEncodingData();
|
|
2892
|
+
Test();
|
|
2893
|
+
}
|
|
2894
|
+
#endif
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
TEST_F(S2PolygonDecodeTest, FuzzCompressedEncoding) {
|
|
2898
|
+
// Some parts of the S2 library S2_DCHECK on invalid data, even if we set
|
|
2899
|
+
// FLAGS_s2debug to false or use S2Polygon::set_s2debug_override. So we
|
|
2900
|
+
// only run this test in opt mode.
|
|
2901
|
+
#ifdef NDEBUG
|
|
2902
|
+
for (int i = 0; i < 100000; ++i) {
|
|
2903
|
+
AppendFakeCompressedEncodingData();
|
|
2904
|
+
Test();
|
|
2905
|
+
}
|
|
2906
|
+
#endif
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
TEST_F(S2PolygonDecodeTest, FuzzEverything) {
|
|
2910
|
+
// Some parts of the S2 library S2_DCHECK on invalid data, even if we set
|
|
2911
|
+
// FLAGS_s2debug to false or use S2Polygon::set_s2debug_override. So we
|
|
2912
|
+
// only run this test in opt mode.
|
|
2913
|
+
#ifdef NDEBUG
|
|
2914
|
+
for (int i = 0; i < 100000; ++i) {
|
|
2915
|
+
AppendRandomData();
|
|
2916
|
+
Test();
|
|
2917
|
+
}
|
|
2918
|
+
#endif
|
|
2919
|
+
}
|
|
2920
|
+
|
|
2921
|
+
TEST_F(S2PolygonTestBase, FullPolygonShape) {
|
|
2922
|
+
S2Polygon::Shape shape(full_.get());
|
|
2923
|
+
EXPECT_EQ(0, shape.num_edges());
|
|
2924
|
+
EXPECT_EQ(2, shape.dimension());
|
|
2925
|
+
EXPECT_FALSE(shape.is_empty());
|
|
2926
|
+
EXPECT_TRUE(shape.is_full());
|
|
2927
|
+
EXPECT_EQ(1, shape.num_chains());
|
|
2928
|
+
EXPECT_EQ(0, shape.chain(0).start);
|
|
2929
|
+
EXPECT_EQ(0, shape.chain(0).length);
|
|
2930
|
+
EXPECT_TRUE(shape.GetReferencePoint().contained);
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
TEST_F(S2PolygonTestBase, EmptyPolygonShape) {
|
|
2934
|
+
S2Polygon::Shape shape(empty_.get());
|
|
2935
|
+
EXPECT_EQ(0, shape.num_edges());
|
|
2936
|
+
EXPECT_EQ(2, shape.dimension());
|
|
2937
|
+
EXPECT_TRUE(shape.is_empty());
|
|
2938
|
+
EXPECT_FALSE(shape.is_full());
|
|
2939
|
+
EXPECT_EQ(0, shape.num_chains());
|
|
2940
|
+
EXPECT_FALSE(shape.GetReferencePoint().contained);
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
void TestPolygonShape(const S2Polygon& polygon) {
|
|
2944
|
+
S2_DCHECK(!polygon.is_full());
|
|
2945
|
+
S2Polygon::Shape shape(&polygon);
|
|
2946
|
+
EXPECT_EQ(&polygon, shape.polygon());
|
|
2947
|
+
EXPECT_EQ(polygon.num_vertices(), shape.num_edges());
|
|
2948
|
+
EXPECT_EQ(polygon.num_loops(), shape.num_chains());
|
|
2949
|
+
for (int e = 0, i = 0; i < polygon.num_loops(); ++i) {
|
|
2950
|
+
const S2Loop* loop_i = polygon.loop(i);
|
|
2951
|
+
EXPECT_EQ(e, shape.chain(i).start);
|
|
2952
|
+
EXPECT_EQ(loop_i->num_vertices(), shape.chain(i).length);
|
|
2953
|
+
for (int j = 0; j < loop_i->num_vertices(); ++j, ++e) {
|
|
2954
|
+
auto edge = shape.edge(e);
|
|
2955
|
+
EXPECT_EQ(loop_i->oriented_vertex(j), edge.v0);
|
|
2956
|
+
EXPECT_EQ(loop_i->oriented_vertex(j+1), edge.v1);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
EXPECT_EQ(2, shape.dimension());
|
|
2960
|
+
EXPECT_FALSE(shape.is_empty());
|
|
2961
|
+
EXPECT_FALSE(shape.is_full());
|
|
2962
|
+
EXPECT_EQ(polygon.Contains(S2::Origin()),
|
|
2963
|
+
shape.GetReferencePoint().contained);
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
TEST_F(S2PolygonTestBase, OneLoopPolygonShape) {
|
|
2967
|
+
TestPolygonShape(*near_0_);
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
TEST_F(S2PolygonTestBase, SeveralLoopPolygonShape) {
|
|
2971
|
+
TestPolygonShape(*near_3210_);
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
TEST(S2Polygon, ManyLoopPolygonShape) {
|
|
2975
|
+
const int kNumLoops = 100;
|
|
2976
|
+
const int kNumVerticesPerLoop = 6;
|
|
2977
|
+
S2Polygon polygon;
|
|
2978
|
+
S2Testing::ConcentricLoopsPolygon(S2Point(1, 0, 0), kNumLoops,
|
|
2979
|
+
kNumVerticesPerLoop, &polygon);
|
|
2980
|
+
TestPolygonShape(polygon);
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
TEST(S2PolygonOwningShape, Ownership) {
|
|
2984
|
+
// Debug mode builds will catch any memory leak below.
|
|
2985
|
+
vector<unique_ptr<S2Loop>> loops;
|
|
2986
|
+
auto polygon = make_unique<S2Polygon>(std::move(loops));
|
|
2987
|
+
S2Polygon::OwningShape shape(std::move(polygon));
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
TEST(S2Polygon, PointInBigLoop) {
|
|
2991
|
+
// This code used to demonstrate a bug in S2ShapeIndex.
|
|
2992
|
+
S2LatLng center = S2LatLng::FromRadians(0.3, 2);
|
|
2993
|
+
S1Angle radius = S1Angle::Degrees(80);
|
|
2994
|
+
S2Polygon poly(S2Loop::MakeRegularLoop(center.ToPoint(), radius, 10));
|
|
2995
|
+
EXPECT_TRUE(poly.MayIntersect(S2Cell(S2CellId(center))));
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
TEST(S2Polygon, Sizes) {
|
|
2999
|
+
// This isn't really a test. It just prints the sizes of various classes.
|
|
3000
|
+
S2_LOG(INFO) << "sizeof(S2Loop): " << sizeof(S2Loop);
|
|
3001
|
+
S2_LOG(INFO) << "sizeof(S2Polygon): " << sizeof(S2Polygon);
|
|
3002
|
+
S2_LOG(INFO) << "sizeof(S2Polyline): " << sizeof(S2Polyline);
|
|
3003
|
+
S2_LOG(INFO) << "sizeof(MutableS2ShapeIndex): " << sizeof(MutableS2ShapeIndex);
|
|
3004
|
+
S2_LOG(INFO) << "sizeof(S2Polygon::Shape): " << sizeof(S2Polygon::Shape);
|
|
3005
|
+
S2_LOG(INFO) << "sizeof(S2Cell): " << sizeof(S2Cell);
|
|
3006
|
+
S2_LOG(INFO) << "sizeof(S2PaddedCell): " << sizeof(S2PaddedCell);
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
TEST_F(S2PolygonTestBase, IndexContainsOnePolygonShape) {
|
|
3010
|
+
const MutableS2ShapeIndex& index = near_0_->index();
|
|
3011
|
+
ASSERT_EQ(1, index.num_shape_ids());
|
|
3012
|
+
S2Polygon::Shape* shape = down_cast<S2Polygon::Shape*>(index.shape(0));
|
|
3013
|
+
EXPECT_EQ(near_0_.get(), shape->polygon());
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
TEST_F(S2PolygonTestBase, PolygonPolygonDistance) {
|
|
3017
|
+
// Verify that the example code for S2Polygon::index() actually works.
|
|
3018
|
+
const S2Polygon& polygon1 = *near_0_;
|
|
3019
|
+
const S2Polygon& polygon2 = *far_10_;
|
|
3020
|
+
S2ClosestEdgeQuery query(&polygon1.index());
|
|
3021
|
+
S2ClosestEdgeQuery::ShapeIndexTarget target(&polygon2.index());
|
|
3022
|
+
S1ChordAngle distance = query.GetDistance(&target);
|
|
3023
|
+
EXPECT_GT(distance, S1ChordAngle(S1Angle::Degrees(175)));
|
|
3024
|
+
}
|
|
3025
|
+
|