@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,1400 @@
|
|
|
1
|
+
// Copyright 2017 Google Inc. All Rights Reserved.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS-IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
//
|
|
15
|
+
|
|
16
|
+
// Author: ericv@google.com (Eric Veach)
|
|
17
|
+
|
|
18
|
+
#include "s2/s2boolean_operation.h"
|
|
19
|
+
|
|
20
|
+
#include <memory>
|
|
21
|
+
#include <gtest/gtest.h>
|
|
22
|
+
#include "s2/third_party/absl/memory/memory.h"
|
|
23
|
+
#include "s2/third_party/absl/strings/str_split.h"
|
|
24
|
+
#include "s2/third_party/absl/strings/strip.h"
|
|
25
|
+
#include "s2/mutable_s2shape_index.h"
|
|
26
|
+
#include "s2/s2builder.h"
|
|
27
|
+
#include "s2/s2builder_graph.h"
|
|
28
|
+
#include "s2/s2builder_layer.h"
|
|
29
|
+
#include "s2/s2builderutil_lax_polygon_layer.h"
|
|
30
|
+
#include "s2/s2builderutil_s2point_vector_layer.h"
|
|
31
|
+
#include "s2/s2builderutil_s2polyline_vector_layer.h"
|
|
32
|
+
#include "s2/s2builderutil_snap_functions.h"
|
|
33
|
+
#include "s2/s2polygon.h"
|
|
34
|
+
#include "s2/s2text_format.h"
|
|
35
|
+
|
|
36
|
+
namespace {
|
|
37
|
+
|
|
38
|
+
using absl::make_unique;
|
|
39
|
+
using s2builderutil::LaxPolygonLayer;
|
|
40
|
+
using std::unique_ptr;
|
|
41
|
+
using std::vector;
|
|
42
|
+
|
|
43
|
+
using Graph = S2Builder::Graph;
|
|
44
|
+
using GraphOptions = S2Builder::GraphOptions;
|
|
45
|
+
using DegenerateEdges = GraphOptions::DegenerateEdges;
|
|
46
|
+
using DuplicateEdges = GraphOptions::DuplicateEdges;
|
|
47
|
+
using SiblingPairs = GraphOptions::SiblingPairs;
|
|
48
|
+
|
|
49
|
+
using OpType = S2BooleanOperation::OpType;
|
|
50
|
+
using PolygonModel = S2BooleanOperation::PolygonModel;
|
|
51
|
+
using PolylineModel = S2BooleanOperation::PolylineModel;
|
|
52
|
+
|
|
53
|
+
using DegenerateBoundaries = LaxPolygonLayer::Options::DegenerateBoundaries;
|
|
54
|
+
|
|
55
|
+
S2Error::Code INDEXES_DO_NOT_MATCH = S2Error::USER_DEFINED_START;
|
|
56
|
+
|
|
57
|
+
class IndexMatchingLayer : public S2Builder::Layer {
|
|
58
|
+
public:
|
|
59
|
+
explicit IndexMatchingLayer(const S2ShapeIndex* index, int dimension)
|
|
60
|
+
: index_(*index), dimension_(dimension) {
|
|
61
|
+
}
|
|
62
|
+
GraphOptions graph_options() const override {
|
|
63
|
+
return GraphOptions(EdgeType::DIRECTED, DegenerateEdges::KEEP,
|
|
64
|
+
DuplicateEdges::KEEP, SiblingPairs::KEEP);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void Build(const Graph& g, S2Error* error) override;
|
|
68
|
+
|
|
69
|
+
private:
|
|
70
|
+
using EdgeVector = vector<S2Shape::Edge>;
|
|
71
|
+
static string ToString(const EdgeVector& edges);
|
|
72
|
+
|
|
73
|
+
const S2ShapeIndex& index_;
|
|
74
|
+
int dimension_;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
string IndexMatchingLayer::ToString(const EdgeVector& edges) {
|
|
78
|
+
string msg;
|
|
79
|
+
for (const auto& edge : edges) {
|
|
80
|
+
vector<S2Point> vertices{edge.v0, edge.v1};
|
|
81
|
+
msg += s2textformat::ToString(vertices);
|
|
82
|
+
msg += "; ";
|
|
83
|
+
}
|
|
84
|
+
return msg;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
void IndexMatchingLayer::Build(const Graph& g, S2Error* error) {
|
|
88
|
+
vector<S2Shape::Edge> actual, expected;
|
|
89
|
+
for (int e = 0; e < g.num_edges(); ++e) {
|
|
90
|
+
const Graph::Edge& edge = g.edge(e);
|
|
91
|
+
actual.push_back(S2Shape::Edge(g.vertex(edge.first),
|
|
92
|
+
g.vertex(edge.second)));
|
|
93
|
+
}
|
|
94
|
+
for (S2Shape* shape : index_) {
|
|
95
|
+
if (shape == nullptr || shape->dimension() != dimension_) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
for (int e = shape->num_edges(); --e >= 0; ) {
|
|
99
|
+
expected.push_back(shape->edge(e));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
std::sort(actual.begin(), actual.end());
|
|
103
|
+
std::sort(expected.begin(), expected.end());
|
|
104
|
+
|
|
105
|
+
// The edges are a multiset, so we can't use std::set_difference.
|
|
106
|
+
vector<S2Shape::Edge> missing, extra;
|
|
107
|
+
for (auto ai = actual.begin(), ei = expected.begin();
|
|
108
|
+
ai != actual.end() || ei != expected.end(); ) {
|
|
109
|
+
if (ei == expected.end() || (ai != actual.end() && *ai < *ei)) {
|
|
110
|
+
extra.push_back(*ai++);
|
|
111
|
+
} else if (ai == actual.end() || *ei < *ai) {
|
|
112
|
+
missing.push_back(*ei++);
|
|
113
|
+
} else {
|
|
114
|
+
++ai;
|
|
115
|
+
++ei;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!missing.empty() || !extra.empty()) {
|
|
119
|
+
// There may be errors in more than one dimension, so we append to the
|
|
120
|
+
// existing error text.
|
|
121
|
+
error->Init(INDEXES_DO_NOT_MATCH,
|
|
122
|
+
"%sDimension %d: Missing edges: %s Extra edges: %s\n",
|
|
123
|
+
error->text().c_str(), dimension_, ToString(missing).c_str(),
|
|
124
|
+
ToString(extra).c_str());
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
void ExpectResult(S2BooleanOperation::OpType op_type,
|
|
129
|
+
const S2BooleanOperation::Options& options,
|
|
130
|
+
const string& a_str, const string& b_str,
|
|
131
|
+
const string& expected_str) {
|
|
132
|
+
auto a = s2textformat::MakeIndexOrDie(a_str);
|
|
133
|
+
auto b = s2textformat::MakeIndexOrDie(b_str);
|
|
134
|
+
auto expected = s2textformat::MakeIndexOrDie(expected_str);
|
|
135
|
+
vector<unique_ptr<S2Builder::Layer>> layers;
|
|
136
|
+
for (int dim = 0; dim < 3; ++dim) {
|
|
137
|
+
layers.push_back(make_unique<IndexMatchingLayer>(expected.get(), dim));
|
|
138
|
+
}
|
|
139
|
+
S2BooleanOperation op(op_type, std::move(layers), options);
|
|
140
|
+
S2Error error;
|
|
141
|
+
EXPECT_TRUE(op.Build(*a, *b, &error))
|
|
142
|
+
<< S2BooleanOperation::OpTypeToString(op_type) << " failed:\n"
|
|
143
|
+
<< "Expected result: " << expected_str << "\n" << error;
|
|
144
|
+
|
|
145
|
+
// Now try the same thing with boolean output.
|
|
146
|
+
EXPECT_EQ(expected->num_shape_ids() == 0,
|
|
147
|
+
S2BooleanOperation::IsEmpty(op_type, *a, *b, options));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
} // namespace
|
|
151
|
+
|
|
152
|
+
// The intersections in the "expected" data below were computed in lat-lng
|
|
153
|
+
// space (i.e., the rectangular projection), while the actual intersections
|
|
154
|
+
// are computed using geodesics. We can compensate for this by rounding the
|
|
155
|
+
// intersection points to a fixed precision in degrees (e.g., 2 decimals).
|
|
156
|
+
static S2BooleanOperation::Options RoundToE(int exp) {
|
|
157
|
+
S2BooleanOperation::Options options;
|
|
158
|
+
options.set_snap_function(s2builderutil::IntLatLngSnapFunction(exp));
|
|
159
|
+
return options;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// TODO(ericv): Clean up or remove these notes.
|
|
163
|
+
//
|
|
164
|
+
// Options to test:
|
|
165
|
+
// polygon_model: OPEN, SEMI_OPEN, CLOSED
|
|
166
|
+
// polyline_model: OPEN, SEMI_OPEN, CLOSED
|
|
167
|
+
// polyline_loops_have_boundaries: true, false
|
|
168
|
+
// conservative: true, false
|
|
169
|
+
//
|
|
170
|
+
// Geometry combinations to test:
|
|
171
|
+
//
|
|
172
|
+
// Point/point:
|
|
173
|
+
// - disjoint, coincident
|
|
174
|
+
// Point/polyline:
|
|
175
|
+
// - Start vertex, end vertex, interior vertex, degenerate polyline
|
|
176
|
+
// - With polyline_loops_have_boundaries: start/end vertex, degenerate polyline
|
|
177
|
+
// Point/polygon:
|
|
178
|
+
// - Polygon interior, exterior, vertex
|
|
179
|
+
// - Vertex of degenerate sibling pair shell, hole
|
|
180
|
+
// - Vertex of degenerate single point shell, hole
|
|
181
|
+
// Polyline/polyline:
|
|
182
|
+
// - Vertex intersection:
|
|
183
|
+
// - Start, end, interior, degenerate, loop start/end, degenerate loop
|
|
184
|
+
// - Test cases where vertex is not emitted because an incident edge is.
|
|
185
|
+
// - Edge/edge: interior crossing, duplicate, reversed, degenerate
|
|
186
|
+
// - Test that degenerate edges are ignored unless polyline has a single edge.
|
|
187
|
+
// (For example, AA has one edge but AAA has no edges.)
|
|
188
|
+
// Polyline/polygon:
|
|
189
|
+
// - Vertex intersection: polyline vertex cases already covered, but test
|
|
190
|
+
// polygon normal vertex, sibling pair shell/hole, single vertex shell/hole
|
|
191
|
+
// - Also test cases where vertex is not emitted because an edge is.
|
|
192
|
+
// - Edge/edge: interior crossing, duplicate, reversed
|
|
193
|
+
// - Edge/interior: polyline edge in polygon interior, exterior
|
|
194
|
+
// Polygon/polygon:
|
|
195
|
+
// - Vertex intersection:
|
|
196
|
+
// - normal vertex, sibling pair shell/hole, single vertex shell/hole
|
|
197
|
+
// - Also test cases where vertex is not emitted because an edge is.
|
|
198
|
+
// - Test that polygons take priority when there is a polygon vertex and
|
|
199
|
+
// also isolated polyline vertices. (There should not be any points.)
|
|
200
|
+
// - Edge/edge: interior crossing, duplicate, reversed
|
|
201
|
+
// - Interior/interior: polygons in interior/exterior of other polygons
|
|
202
|
+
|
|
203
|
+
TEST(S2BooleanOperation, DegeneratePolylines) {
|
|
204
|
+
// Verify that degenerate polylines are preserved under all boundary models.
|
|
205
|
+
S2BooleanOperation::Options options;
|
|
206
|
+
auto a = "# 0:0, 0:0 #";
|
|
207
|
+
auto b = "# #";
|
|
208
|
+
options.set_polyline_model(PolylineModel::OPEN);
|
|
209
|
+
ExpectResult(OpType::UNION, options, a, b, a);
|
|
210
|
+
options.set_polyline_model(PolylineModel::SEMI_OPEN);
|
|
211
|
+
ExpectResult(OpType::UNION, options, a, b, a);
|
|
212
|
+
options.set_polyline_model(PolylineModel::CLOSED);
|
|
213
|
+
ExpectResult(OpType::UNION, options, a, b, a);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
TEST(S2BooleanOperation, DegeneratePolygons) {
|
|
217
|
+
// Verify that degenerate polygon features (single-vertex and sibling pair
|
|
218
|
+
// shells and holes) are preserved under all boundary models.
|
|
219
|
+
S2BooleanOperation::Options options;
|
|
220
|
+
auto a = "# # 0:0, 0:5, 5:5, 5:0; 1:1; 2:2, 3:3; 6:6; 7:7, 8:8";
|
|
221
|
+
auto b = "# #";
|
|
222
|
+
options.set_polygon_model(PolygonModel::OPEN);
|
|
223
|
+
ExpectResult(OpType::UNION, options, a, b, a);
|
|
224
|
+
options.set_polygon_model(PolygonModel::SEMI_OPEN);
|
|
225
|
+
ExpectResult(OpType::UNION, options, a, b, a);
|
|
226
|
+
options.set_polygon_model(PolygonModel::CLOSED);
|
|
227
|
+
ExpectResult(OpType::UNION, options, a, b, a);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
TEST(S2BooleanOperation, PointPoint) {
|
|
231
|
+
S2BooleanOperation::Options options;
|
|
232
|
+
auto a = "0:0 | 1:0 # #";
|
|
233
|
+
auto b = "0:0 | 2:0 # #";
|
|
234
|
+
// Note that these results have duplicates, which is correct. Clients can
|
|
235
|
+
// eliminated the duplicates with the appropriate GraphOptions.
|
|
236
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
237
|
+
"0:0 | 0:0 | 1:0 | 2:0 # #");
|
|
238
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
239
|
+
"0:0 | 0:0 # #");
|
|
240
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
241
|
+
"1:0 # #");
|
|
242
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
243
|
+
"1:0 | 2:0 # #");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
TEST(S2BooleanOperation, PointOpenPolyline) {
|
|
247
|
+
// Tests operations between an open polyline and its vertices.
|
|
248
|
+
//
|
|
249
|
+
// The polyline "3:0, 3:0" consists of a single degenerate edge and contains
|
|
250
|
+
// no points (since polyline_model() is OPEN). Since S2BooleanOperation
|
|
251
|
+
// preserves degeneracies, this means that the union includes *both* the
|
|
252
|
+
// point 3:0 and the degenerate polyline 3:0, since they do not intersect.
|
|
253
|
+
//
|
|
254
|
+
// This test uses Options::polyline_loops_have_boundaries() == true, which
|
|
255
|
+
// means that the loop "4:0, 5:0, 4:0" does not contain the vertex "4:0".
|
|
256
|
+
S2BooleanOperation::Options options;
|
|
257
|
+
options.set_polyline_model(PolylineModel::OPEN);
|
|
258
|
+
auto a = "0:0 | 1:0 | 2:0 | 3:0 | 4:0 | 5:0 # #";
|
|
259
|
+
auto b = "# 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0, 4:0 #";
|
|
260
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
261
|
+
"0:0 | 2:0 | 3:0 | 4:0 "
|
|
262
|
+
"# 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0, 4:0 #");
|
|
263
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
264
|
+
"1:0 | 5:0 # #");
|
|
265
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
266
|
+
"0:0 | 2:0 | 3:0 | 4:0 # #");
|
|
267
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
268
|
+
"0:0 | 2:0 | 3:0 | 4:0"
|
|
269
|
+
"# 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0, 4:0 #");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
TEST(S2BooleanOperation, PointOpenPolylineLoopBoundariesFalse) {
|
|
273
|
+
// With Options::polyline_loops_have_boundaries() == false, the loop
|
|
274
|
+
// "4:0, 5:0, 4:0" has two vertices, both of which are contained.
|
|
275
|
+
S2BooleanOperation::Options options;
|
|
276
|
+
options.set_polyline_model(PolylineModel::OPEN);
|
|
277
|
+
options.set_polyline_loops_have_boundaries(false);
|
|
278
|
+
auto a = "0:0 | 1:0 | 2:0 | 3:0 | 4:0 | 5:0 # #";
|
|
279
|
+
auto b = "# 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0, 4:0 #";
|
|
280
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
281
|
+
"0:0 | 2:0 | 3:0 "
|
|
282
|
+
"# 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0, 4:0 #");
|
|
283
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
284
|
+
"1:0 | 4:0 | 5:0 # #");
|
|
285
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
286
|
+
"0:0 | 2:0 | 3:0 # #");
|
|
287
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
288
|
+
"0:0 | 2:0 | 3:0 "
|
|
289
|
+
"# 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0, 4:0 #");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
TEST(S2BooleanOperation, PointSemiOpenPolyline) {
|
|
293
|
+
// Degenerate polylines are defined not contain any points under the
|
|
294
|
+
// SEMI_OPEN model either, so again the point 3:0 and the degenerate
|
|
295
|
+
// polyline "3:0, 3:0" do not intersect.
|
|
296
|
+
//
|
|
297
|
+
// The result does not depend on Options::polyline_loops_have_boundaries().
|
|
298
|
+
S2BooleanOperation::Options options;
|
|
299
|
+
options.set_polyline_model(PolylineModel::SEMI_OPEN);
|
|
300
|
+
for (bool bool_value : {false, true}) {
|
|
301
|
+
options.set_polyline_loops_have_boundaries(bool_value);
|
|
302
|
+
auto a = "0:0 | 1:0 | 2:0 | 3:0 | 4:0 | 5:0 # #";
|
|
303
|
+
auto b = "# 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0, 4:0 #";
|
|
304
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
305
|
+
"2:0 | 3:0 # 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0, 4:0 #");
|
|
306
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
307
|
+
"0:0 | 1:0 | 4:0 | 5:0 # #");
|
|
308
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
309
|
+
"2:0 | 3:0 # #");
|
|
310
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
311
|
+
"2:0 | 3:0 # 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0, 4:0 #");
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
TEST(S2BooleanOperation, PointClosedPolyline) {
|
|
316
|
+
// Under the CLOSED model, the degenerate polyline 3:0 does contain its
|
|
317
|
+
// vertex. Since polylines take precedence over points, the union of the
|
|
318
|
+
// point 3:0 and the polyline 3:0 is the polyline only. Similarly, since
|
|
319
|
+
// subtracting a point from a polyline has no effect, the symmetric
|
|
320
|
+
// difference includes only the polyline objects.
|
|
321
|
+
//
|
|
322
|
+
// The result does not depend on Options::polyline_loops_have_boundaries().
|
|
323
|
+
S2BooleanOperation::Options options;
|
|
324
|
+
options.set_polyline_model(PolylineModel::CLOSED);
|
|
325
|
+
for (bool bool_value : {false, true}) {
|
|
326
|
+
options.set_polyline_loops_have_boundaries(bool_value);
|
|
327
|
+
auto a = "0:0 | 1:0 | 2:0 | 3:0 | 4:0 | 5:0 # #";
|
|
328
|
+
auto b = "# 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0, 4:0 #";
|
|
329
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
330
|
+
"# 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0, 4:0 #");
|
|
331
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
332
|
+
"0:0 | 1:0 | 2:0 | 3:0 | 4:0 | 5:0 # #");
|
|
333
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
334
|
+
"# #");
|
|
335
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
336
|
+
"# 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0, 4:0 #");
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
TEST(S2BooleanOperation, PointPolygonInterior) {
|
|
341
|
+
S2BooleanOperation::Options options; // PolygonModel is irrelevant.
|
|
342
|
+
// One interior point and one exterior point.
|
|
343
|
+
auto a = "1:1 | 4:4 # #";
|
|
344
|
+
auto b = "# # 0:0, 0:3, 3:0";
|
|
345
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
346
|
+
"4:4 # # 0:0, 0:3, 3:0");
|
|
347
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
348
|
+
"1:1 # #");
|
|
349
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
350
|
+
"4:4 # #");
|
|
351
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
352
|
+
"4:4 # # 0:0, 0:3, 3:0");
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
TEST(S2BooleanOperation, PointOpenPolygonVertex) {
|
|
356
|
+
S2BooleanOperation::Options options;
|
|
357
|
+
options.set_polygon_model(PolygonModel::OPEN);
|
|
358
|
+
// See notes about the two vertices below.
|
|
359
|
+
auto a = "0:1 | 1:0 # #";
|
|
360
|
+
auto b = "# # 0:0, 0:1, 1:0";
|
|
361
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
362
|
+
"0:1 | 1:0 # # 0:0, 0:1, 1:0");
|
|
363
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
364
|
+
"# #");
|
|
365
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
366
|
+
"0:1 | 1:0 # #");
|
|
367
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
368
|
+
"0:1 | 1:0 # # 0:0, 0:1, 1:0");
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
TEST(S2BooleanOperation, PointSemiOpenPolygonVertex) {
|
|
372
|
+
S2BooleanOperation::Options options;
|
|
373
|
+
options.set_polygon_model(PolygonModel::SEMI_OPEN);
|
|
374
|
+
// The two vertices are chosen such that the polygon contains one vertex but
|
|
375
|
+
// not the other under PolygonModel::SEMI_OPEN. (The same vertices are used
|
|
376
|
+
// for all three PolygonModel options.)
|
|
377
|
+
auto polygon = s2textformat::MakePolygonOrDie("0:0, 0:1, 1:0");
|
|
378
|
+
ASSERT_TRUE(polygon->Contains(s2textformat::MakePoint("0:1")));
|
|
379
|
+
ASSERT_FALSE(polygon->Contains(s2textformat::MakePoint("1:0")));
|
|
380
|
+
auto a = "0:1 | 1:0 # #";
|
|
381
|
+
auto b = "# # 0:0, 0:1, 1:0";
|
|
382
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
383
|
+
"1:0 # # 0:0, 0:1, 1:0");
|
|
384
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
385
|
+
"0:1 # #");
|
|
386
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
387
|
+
"1:0 # #");
|
|
388
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
389
|
+
"1:0 # # 0:0, 0:1, 1:0");
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
TEST(S2BooleanOperation, PointClosedPolygonVertex) {
|
|
393
|
+
S2BooleanOperation::Options options;
|
|
394
|
+
options.set_polygon_model(PolygonModel::CLOSED);
|
|
395
|
+
// See notes about the two vertices above.
|
|
396
|
+
auto a = "0:1 | 1:0 # #";
|
|
397
|
+
auto b = "# # 0:0, 0:1, 1:0";
|
|
398
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
399
|
+
"# # 0:0, 0:1, 1:0");
|
|
400
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
401
|
+
"0:1 | 1:0 # #");
|
|
402
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
403
|
+
"# #");
|
|
404
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
405
|
+
"# # 0:0, 0:1, 1:0");
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
TEST(S2BooleanOperation, PolylineVertexOpenPolylineVertex) {
|
|
409
|
+
// Test first, last, and middle vertices of both polylines. Also test
|
|
410
|
+
// first/last and middle vertices of two polyline loops.
|
|
411
|
+
//
|
|
412
|
+
// Degenerate polylines are tested in PolylineEdgePolylineEdgeOverlap below.
|
|
413
|
+
S2BooleanOperation::Options options;
|
|
414
|
+
options.set_polyline_model(PolylineModel::OPEN);
|
|
415
|
+
auto a = "# 0:0, 0:1, 0:2 | 0:3, 0:4, 0:3 #";
|
|
416
|
+
auto b = "# 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
417
|
+
"| 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #";
|
|
418
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
419
|
+
"# 0:0, 0:1, 0:2 | 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
420
|
+
"| 0:3, 0:4, 0:3 | 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #");
|
|
421
|
+
|
|
422
|
+
// The output consists of the portion of each input polyline that intersects
|
|
423
|
+
// the opposite region, so the intersection vertex is present twice. This
|
|
424
|
+
// allows reassembling the individual polylins that intersect, if desired.
|
|
425
|
+
// (Otherwise duplicates can be removed using DuplicateEdges::MERGE.)
|
|
426
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
427
|
+
"# 0:1, 0:1 | 0:1, 0:1 #");
|
|
428
|
+
|
|
429
|
+
// Note that all operations are defined such that subtracting a
|
|
430
|
+
// lower-dimensional subset of an object has no effect. In this case,
|
|
431
|
+
// subtracting the middle vertex of a polyline has no effect.
|
|
432
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
433
|
+
"# 0:0, 0:1, 0:2 | 0:3, 0:4, 0:3 #");
|
|
434
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
435
|
+
"# 0:0, 0:1, 0:2 | 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
436
|
+
"| 0:3, 0:4, 0:3 | 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #");
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
TEST(S2BooleanOperation, PolylineVertexOpenPolylineVertexLoopBoundariesFalse) {
|
|
440
|
+
// With Options::polyline_loops_have_boundaries() == false, the 3 polyline
|
|
441
|
+
// loops each have two vertices, both of which are contained.
|
|
442
|
+
S2BooleanOperation::Options options;
|
|
443
|
+
options.set_polyline_model(PolylineModel::OPEN);
|
|
444
|
+
options.set_polyline_loops_have_boundaries(false);
|
|
445
|
+
auto a = "# 0:0, 0:1, 0:2 | 0:3, 0:4, 0:3 #";
|
|
446
|
+
auto b = "# 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
447
|
+
"| 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #";
|
|
448
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
449
|
+
"# 0:0, 0:1, 0:2 | 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
450
|
+
"| 0:3, 0:4, 0:3 | 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #");
|
|
451
|
+
|
|
452
|
+
// Note that the polyline "0:3, 0:4, 0:3" only has two vertices, not three.
|
|
453
|
+
// This means that 0:3 is emitted only once for that polyline, plus once for
|
|
454
|
+
// the other polyline, for a total of twice.
|
|
455
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
456
|
+
"# 0:1, 0:1 | 0:1, 0:1 "
|
|
457
|
+
"| 0:3, 0:3 | 0:3, 0:3 | 0:4, 0:4 | 0:4, 0:4 #");
|
|
458
|
+
|
|
459
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
460
|
+
"# 0:0, 0:1, 0:2 | 0:3, 0:4, 0:3 #");
|
|
461
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
462
|
+
"# 0:0, 0:1, 0:2 | 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
463
|
+
"| 0:3, 0:4, 0:3 | 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #");
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
TEST(S2BooleanOperation, PolylineVertexSemiOpenPolylineVertex) {
|
|
467
|
+
// The result does not depend on Options::polyline_loops_have_boundaries().
|
|
468
|
+
S2BooleanOperation::Options options;
|
|
469
|
+
options.set_polyline_model(PolylineModel::SEMI_OPEN);
|
|
470
|
+
for (bool bool_value : {false, true}) {
|
|
471
|
+
options.set_polyline_loops_have_boundaries(bool_value);
|
|
472
|
+
auto a = "# 0:0, 0:1, 0:2 | 0:3, 0:4, 0:3 #";
|
|
473
|
+
auto b = "# 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
474
|
+
"| 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #";
|
|
475
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
476
|
+
"# 0:0, 0:1, 0:2 | 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
477
|
+
"| 0:3, 0:4, 0:3 | 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #");
|
|
478
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
479
|
+
"# 0:0, 0:0 | 0:0, 0:0 | 0:1, 0:1 | 0:1, 0:1 "
|
|
480
|
+
"| 0:3, 0:3 | 0:3, 0:3 | 0:4, 0:4 | 0:4, 0:4 #");
|
|
481
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
482
|
+
"# 0:0, 0:1, 0:2 | 0:3, 0:4, 0:3 #");
|
|
483
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
484
|
+
"# 0:0, 0:1, 0:2 | 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
485
|
+
"| 0:3, 0:4, 0:3 | 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #");
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
TEST(S2BooleanOperation, PolylineVertexClosedPolylineVertex) {
|
|
490
|
+
S2BooleanOperation::Options options;
|
|
491
|
+
options.set_polyline_model(PolylineModel::CLOSED);
|
|
492
|
+
auto a = "# 0:0, 0:1, 0:2 | 0:3, 0:4, 0:3 #";
|
|
493
|
+
auto b = "# 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
494
|
+
"| 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #";
|
|
495
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
496
|
+
"# 0:0, 0:1, 0:2 | 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
497
|
+
"| 0:3, 0:4, 0:3 | 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #");
|
|
498
|
+
|
|
499
|
+
// Since Options::polyline_loops_have_boundaries() == true, the polyline
|
|
500
|
+
// "0:3, 0:4, 0:3" has three vertices. Therefore 0:3 is emitted twice for
|
|
501
|
+
// that polyline, plus once for the other polyline, for a total of thrice.
|
|
502
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
503
|
+
"# 0:0, 0:0 | 0:0, 0:0 | 0:1, 0:1 | 0:1, 0:1 "
|
|
504
|
+
"| 0:2, 0:2 | 0:2, 0:2 "
|
|
505
|
+
"| 0:3, 0:3 | 0:3, 0:3 | 0:3, 0:3 "
|
|
506
|
+
"| 0:4, 0:4 | 0:4, 0:4 | 0:4, 0:4 #");
|
|
507
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
508
|
+
"# 0:0, 0:1, 0:2 | 0:3, 0:4, 0:3 #");
|
|
509
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
510
|
+
"# 0:0, 0:1, 0:2 | 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
511
|
+
"| 0:3, 0:4, 0:3 | 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #");
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
TEST(S2BooleanOperation,
|
|
515
|
+
PolylineVertexClosedPolylineVertexLoopBoundariesFalse) {
|
|
516
|
+
S2BooleanOperation::Options options;
|
|
517
|
+
options.set_polyline_model(PolylineModel::CLOSED);
|
|
518
|
+
options.set_polyline_loops_have_boundaries(false);
|
|
519
|
+
auto a = "# 0:0, 0:1, 0:2 | 0:3, 0:4, 0:3 #";
|
|
520
|
+
auto b = "# 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
521
|
+
"| 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #";
|
|
522
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
523
|
+
"# 0:0, 0:1, 0:2 | 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
524
|
+
"| 0:3, 0:4, 0:3 | 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #");
|
|
525
|
+
|
|
526
|
+
// Since Options::polyline_loops_have_boundaries() == false, the polyline
|
|
527
|
+
// "0:3, 0:4, 0:3" has two vertices. Therefore 0:3 is emitted once for
|
|
528
|
+
// that polyline, plus once for the other polyline, for a total of twice.
|
|
529
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
530
|
+
"# 0:0, 0:0 | 0:0, 0:0 | 0:1, 0:1 | 0:1, 0:1 "
|
|
531
|
+
"| 0:2, 0:2 | 0:2, 0:2 "
|
|
532
|
+
"| 0:3, 0:3 | 0:3, 0:3 | 0:4, 0:4 | 0:4, 0:4 #");
|
|
533
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
534
|
+
"# 0:0, 0:1, 0:2 | 0:3, 0:4, 0:3 #");
|
|
535
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
536
|
+
"# 0:0, 0:1, 0:2 | 0:0, 1:0 | -1:1, 0:1, 1:1 | -1:2, 0:2 "
|
|
537
|
+
"| 0:3, 0:4, 0:3 | 1:3, 0:3, 1:3 | 0:4, 1:4, 0:4 #");
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// The polygon used in the polyline/polygon vertex tests below.
|
|
541
|
+
static string kVertexTestPolygonStr() {
|
|
542
|
+
return "0:0, 0:1, 0:2, 0:3, 0:4, 0:5, 5:5, 5:4, 5:3, 5:2, 5:1, 5:0";
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
TEST(S2BooleanOperation, TestSemiOpenPolygonVerticesContained) {
|
|
546
|
+
// Verify whether certain vertices of the test polygon are contained under
|
|
547
|
+
// the semi-open boundary model (for use in the tests below).
|
|
548
|
+
auto polygon = s2textformat::MakePolygonOrDie(kVertexTestPolygonStr());
|
|
549
|
+
EXPECT_TRUE(polygon->Contains(s2textformat::MakePoint("0:1")));
|
|
550
|
+
EXPECT_TRUE(polygon->Contains(s2textformat::MakePoint("0:2")));
|
|
551
|
+
EXPECT_TRUE(polygon->Contains(s2textformat::MakePoint("0:3")));
|
|
552
|
+
EXPECT_TRUE(polygon->Contains(s2textformat::MakePoint("0:4")));
|
|
553
|
+
EXPECT_FALSE(polygon->Contains(s2textformat::MakePoint("5:1")));
|
|
554
|
+
EXPECT_FALSE(polygon->Contains(s2textformat::MakePoint("5:2")));
|
|
555
|
+
EXPECT_FALSE(polygon->Contains(s2textformat::MakePoint("5:3")));
|
|
556
|
+
EXPECT_FALSE(polygon->Contains(s2textformat::MakePoint("5:4")));
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Don't bother testing every PolylineModel with every PolygonModel for vertex
|
|
560
|
+
// intersection, since we have already tested the PolylineModels individually
|
|
561
|
+
// above. It is sufficient to use PolylineModel::CLOSED with the various
|
|
562
|
+
// PolygonModel options.
|
|
563
|
+
TEST(S2BooleanOperation, PolylineVertexOpenPolygonVertex) {
|
|
564
|
+
S2BooleanOperation::Options options;
|
|
565
|
+
options.set_polygon_model(PolygonModel::OPEN);
|
|
566
|
+
|
|
567
|
+
// Define some constants to reduce code duplication.
|
|
568
|
+
// Test all combinations of polylines that start or end on a polygon vertex,
|
|
569
|
+
// where the polygon vertex is open or closed using semi-open boundaries,
|
|
570
|
+
// and where the incident edge is inside or outside the polygon.
|
|
571
|
+
auto a = ("# 1:1, 0:1 | 0:2, 1:2 | -1:3, 0:3 | 0:4, -1:4 "
|
|
572
|
+
"| 6:1, 5:1 | 5:2, 6:2 | 4:3, 5:3 | 5:4, 4:4 #");
|
|
573
|
+
auto b = "# # " + kVertexTestPolygonStr();
|
|
574
|
+
|
|
575
|
+
const string kDifferenceResult =
|
|
576
|
+
"# 0:1, 0:1 | 0:2, 0:2 | -1:3, 0:3 | 0:4, -1:4"
|
|
577
|
+
"| 6:1, 5:1 | 5:2, 6:2 | 5:3, 5:3 | 5:4, 5:4 #";
|
|
578
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
579
|
+
kDifferenceResult + kVertexTestPolygonStr());
|
|
580
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
581
|
+
"# 1:1, 0:1 | 0:2, 1:2 | 4:3, 5:3 | 5:4, 4:4 #");
|
|
582
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b, kDifferenceResult);
|
|
583
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
584
|
+
kDifferenceResult + kVertexTestPolygonStr());
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Like the test above, except that every polygon vertex is also incident to a
|
|
588
|
+
// closed polyline vertex. This tests that when an open vertex and a closed
|
|
589
|
+
// vertex coincide with each other, the result is considered closed.
|
|
590
|
+
TEST(S2BooleanOperation, PolylineVertexOpenPolygonClosedPolylineVertex) {
|
|
591
|
+
const string kTestGeometrySuffix =
|
|
592
|
+
"-2:0, 0:1 | -2:1, 0:2 | -2:2, 0:3 | -2:3, 0:4 | "
|
|
593
|
+
"7:0, 5:1 | 7:1, 5:2 | 7:2, 5:3 | 7:3, 5:4 # " + kVertexTestPolygonStr();
|
|
594
|
+
|
|
595
|
+
S2BooleanOperation::Options options;
|
|
596
|
+
options.set_polygon_model(PolygonModel::OPEN);
|
|
597
|
+
auto a = ("# 1:1, 0:1 | 0:2, 1:2 | -1:3, 0:3 | 0:4, -1:4 "
|
|
598
|
+
"| 6:1, 5:1 | 5:2, 6:2 | 4:3, 5:3 | 5:4, 4:4 #");
|
|
599
|
+
auto b = ("# " + kTestGeometrySuffix);
|
|
600
|
+
|
|
601
|
+
const string kDifferencePrefix =
|
|
602
|
+
"# -1:3, 0:3 | 0:4, -1:4 | 6:1, 5:1 | 5:2, 6:2";
|
|
603
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
604
|
+
kDifferencePrefix +
|
|
605
|
+
" | 0:1, 0:1 | 0:2, 0:2 | 5:3, 5:3 | 5:4, 5:4 | " +
|
|
606
|
+
kTestGeometrySuffix);
|
|
607
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
608
|
+
"# 1:1, 0:1 | 0:2, 1:2 | 0:3, 0:3 | 0:4, 0:4"
|
|
609
|
+
"| 5:1, 5:1 | 5:2, 5:2 | 4:3, 5:3 | 5:4, 4:4"
|
|
610
|
+
"| 0:1, 0:1 | 0:2, 0:2 | 0:3, 0:3 | 0:4, 0:4"
|
|
611
|
+
"| 5:1, 5:1 | 5:2, 5:2 | 5:3, 5:3 | 5:4, 5:4 #");
|
|
612
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b, kDifferencePrefix + " #");
|
|
613
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
614
|
+
kDifferencePrefix + " | " + kTestGeometrySuffix);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
TEST(S2BooleanOperation, PolylineVertexSemiOpenPolygonVertex) {
|
|
618
|
+
S2BooleanOperation::Options options;
|
|
619
|
+
options.set_polygon_model(PolygonModel::SEMI_OPEN);
|
|
620
|
+
// Test all combinations of polylines that start or end on a polygon vertex,
|
|
621
|
+
// where the polygon vertex is open or closed using semi-open boundaries,
|
|
622
|
+
// and where the incident edge is inside or outside the polygon.
|
|
623
|
+
//
|
|
624
|
+
// The vertices at latitude 0 used below are all closed while the vertices
|
|
625
|
+
// at latitude 5 are all open (see TestSemiOpenPolygonVerticesContained).
|
|
626
|
+
auto a = ("# 1:1, 0:1 | 0:2, 1:2 | -1:3, 0:3 | 0:4, -1:4 "
|
|
627
|
+
"| 6:1, 5:1 | 5:2, 6:2 | 4:3, 5:3 | 5:4, 4:4 #");
|
|
628
|
+
auto b = "# # " + kVertexTestPolygonStr();
|
|
629
|
+
const string kDifferenceResult =
|
|
630
|
+
"# -1:3, 0:3 | 0:4, -1:4 | 6:1, 5:1 | 5:2, 6:2 | 5:3, 5:3 | 5:4, 5:4 #";
|
|
631
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
632
|
+
kDifferenceResult + kVertexTestPolygonStr());
|
|
633
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
634
|
+
"# 1:1, 0:1 | 0:2, 1:2 | 0:3, 0:3 | 0:4, 0:4 "
|
|
635
|
+
"| 4:3, 5:3 | 5:4, 4:4 #");
|
|
636
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b, kDifferenceResult);
|
|
637
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
638
|
+
kDifferenceResult + kVertexTestPolygonStr());
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
TEST(S2BooleanOperation, PolylineVertexClosedPolygonVertex) {
|
|
642
|
+
S2BooleanOperation::Options options;
|
|
643
|
+
options.set_polygon_model(PolygonModel::CLOSED);
|
|
644
|
+
// Test all combinations of polylines that start or end on a polygon vertex,
|
|
645
|
+
// where the polygon vertex is open or closed using semi-open boundaries,
|
|
646
|
+
// and where the incident edge is inside or outside the polygon.
|
|
647
|
+
auto a = ("# 1:1, 0:1 | 0:2, 1:2 | -1:3, 0:3 | 0:4, -1:4 "
|
|
648
|
+
"| 6:1, 5:1 | 5:2, 6:2 | 4:3, 5:3 | 5:4, 4:4 #");
|
|
649
|
+
auto b = "# # " + kVertexTestPolygonStr();
|
|
650
|
+
const string kDifferenceResult =
|
|
651
|
+
"# -1:3, 0:3 | 0:4, -1:4 | 6:1, 5:1 | 5:2, 6:2 #";
|
|
652
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
653
|
+
kDifferenceResult + kVertexTestPolygonStr());
|
|
654
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
655
|
+
"# 1:1, 0:1 | 0:2, 1:2 | 0:3, 0:3 | 0:4, 0:4"
|
|
656
|
+
"| 5:1, 5:1 | 5:2, 5:2 | 4:3, 5:3 | 5:4, 4:4 #");
|
|
657
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b, kDifferenceResult);
|
|
658
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
659
|
+
kDifferenceResult + kVertexTestPolygonStr());
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
TEST(S2BooleanOperation, PolylineEdgePolylineEdgeCrossing) {
|
|
663
|
+
// Two polyline edges that cross at a point interior to both edges.
|
|
664
|
+
S2BooleanOperation::Options options = RoundToE(1);
|
|
665
|
+
auto a = "# 0:0, 2:2 #";
|
|
666
|
+
auto b = "# 2:0, 0:2 #";
|
|
667
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
668
|
+
"# 0:0, 1:1, 2:2 | 2:0, 1:1, 0:2 #");
|
|
669
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
670
|
+
"# 1:1, 1:1 | 1:1, 1:1 #");
|
|
671
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
672
|
+
"# 0:0, 2:2 #");
|
|
673
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
674
|
+
"# 0:0, 1:1, 2:2 | 2:0, 1:1, 0:2 #");
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
TEST(S2BooleanOperation, PolylineEdgePolylineEdgeOverlap) {
|
|
678
|
+
// The PolylineModel does not affect this calculation. In particular the
|
|
679
|
+
// intersection of a degenerate polyline edge with itself is non-empty, even
|
|
680
|
+
// though the edge contains no points in the OPEN and SEMI_OPEN models.
|
|
681
|
+
S2BooleanOperation::Options options;
|
|
682
|
+
options.set_polygon_model(PolygonModel::OPEN);
|
|
683
|
+
// Test edges in the same and reverse directions, and degenerate edges.
|
|
684
|
+
auto a = "# 0:0, 1:0, 2:0, 2:5 | 3:0, 3:0 | 6:0, 5:0, 4:0 #";
|
|
685
|
+
auto b = "# 0:0, 1:0, 2:0 | 3:0, 3:0 | 4:0, 5:0 #";
|
|
686
|
+
// As usual, the expected output includes the relevant portions of *both*
|
|
687
|
+
// input polylines. Duplicates can be removed using GraphOptions.
|
|
688
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
689
|
+
"# 0:0, 1:0, 2:0, 2:5 | 0:0, 1:0, 2:0 | 3:0, 3:0 | 3:0, 3:0 "
|
|
690
|
+
"| 6:0, 5:0, 4:0 | 4:0, 5:0 #");
|
|
691
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
692
|
+
"# 0:0, 1:0, 2:0 | 0:0, 1:0, 2:0 | 3:0, 3:0 | 3:0, 3:0 "
|
|
693
|
+
"| 5:0, 4:0 | 4:0, 5:0 #");
|
|
694
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
695
|
+
"# 2:0, 2:5 | 6:0, 5:0 #");
|
|
696
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
697
|
+
"# 2:0, 2:5 | 6:0, 5:0 #");
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
TEST(S2BooleanOperation, PolylineEdgeOpenPolygonEdgeOverlap) {
|
|
701
|
+
S2BooleanOperation::Options options;
|
|
702
|
+
options.set_polygon_model(PolygonModel::OPEN);
|
|
703
|
+
// A polygon and two polyline edges that coincide with the polygon boundary,
|
|
704
|
+
// one in the same direction and one in the reverse direction.
|
|
705
|
+
auto a = "# 1:1, 1:3, 3:3 | 3:3, 1:3 # ";
|
|
706
|
+
auto b = "# # 1:1, 1:3, 3:3, 3:1";
|
|
707
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
708
|
+
"# 1:1, 1:3, 3:3 | 3:3, 1:3 # 1:1, 1:3, 3:3, 3:1");
|
|
709
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
710
|
+
"# #");
|
|
711
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
712
|
+
"# 1:1, 1:3, 3:3 | 3:3, 1:3 #");
|
|
713
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
714
|
+
"# 1:1, 1:3, 3:3 | 3:3, 1:3 # 1:1, 1:3, 3:3, 3:1");
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
TEST(S2BooleanOperation, PolylineEdgeSemiOpenPolygonEdgeOverlap) {
|
|
718
|
+
auto polygon = s2textformat::MakePolygonOrDie("1:1, 1:3, 3:3, 3:1");
|
|
719
|
+
ASSERT_FALSE(polygon->Contains(s2textformat::MakePoint("1:1")));
|
|
720
|
+
ASSERT_TRUE(polygon->Contains(s2textformat::MakePoint("1:3")));
|
|
721
|
+
ASSERT_FALSE(polygon->Contains(s2textformat::MakePoint("3:3")));
|
|
722
|
+
ASSERT_FALSE(polygon->Contains(s2textformat::MakePoint("3:1")));
|
|
723
|
+
S2BooleanOperation::Options options;
|
|
724
|
+
options.set_polygon_model(PolygonModel::SEMI_OPEN);
|
|
725
|
+
auto a = "# 1:1, 1:3, 3:3 | 3:3, 1:3 # ";
|
|
726
|
+
auto b = "# # 1:1, 1:3, 3:3, 3:1";
|
|
727
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
728
|
+
"# 1:1, 1:1 | 3:3, 3:3 | 3:3, 1:3 # 1:1, 1:3, 3:3, 3:1");
|
|
729
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
730
|
+
"# 1:3, 1:3 | 1:1, 1:3, 3:3 #");
|
|
731
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
732
|
+
"# 1:1, 1:1 | 3:3, 3:3 | 3:3, 1:3 #");
|
|
733
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
734
|
+
"# 1:1, 1:1 | 3:3, 3:3 | 3:3, 1:3 # 1:1, 1:3, 3:3, 3:1");
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
TEST(S2BooleanOperation, PolylineEdgeClosedPolygonEdgeOverlap) {
|
|
738
|
+
S2BooleanOperation::Options options;
|
|
739
|
+
options.set_polygon_model(PolygonModel::CLOSED);
|
|
740
|
+
auto a = "# 1:1, 1:3, 3:3 | 3:3, 1:3 # ";
|
|
741
|
+
auto b = "# # 1:1, 1:3, 3:3, 3:1";
|
|
742
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
743
|
+
"# # 1:1, 1:3, 3:3, 3:1");
|
|
744
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
745
|
+
"# 1:1, 1:3, 3:3 | 3:3, 1:3 #");
|
|
746
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
747
|
+
"# #");
|
|
748
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
749
|
+
"# # 1:1, 1:3, 3:3, 3:1");
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
TEST(S2BooleanOperation, PolygonVertexMatching) {
|
|
753
|
+
// This test shows that CrossingProcessor::ProcessEdgeCrossings() must set
|
|
754
|
+
// a0_matches_polygon and a1_matches_polygon correctly even when (a0, a1)
|
|
755
|
+
// itself is a polygon edge (or its sibling). (It requires degenerate
|
|
756
|
+
// polygon geometry to demonstrate this.)
|
|
757
|
+
S2BooleanOperation::Options options;
|
|
758
|
+
options.set_polyline_model(PolylineModel::CLOSED);
|
|
759
|
+
options.set_polygon_model(PolygonModel::CLOSED);
|
|
760
|
+
auto a = "# 0:0, 1:1 # ";
|
|
761
|
+
auto b = "# # 0:0, 1:1";
|
|
762
|
+
ExpectResult(OpType::UNION, options, a, b, "# # 0:0, 1:1");
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
TEST(S2BooleanOperation, PolylineEdgePolygonInterior) {
|
|
766
|
+
S2BooleanOperation::Options options; // PolygonModel is irrelevant.
|
|
767
|
+
// One normal and one degenerate polyline edge in the polygon interior, and
|
|
768
|
+
// similarly for the polygon exterior.
|
|
769
|
+
auto a = "# 1:1, 2:2 | 3:3, 3:3 | 6:6, 7:7 | 8:8, 8:8 # ";
|
|
770
|
+
auto b = "# # 0:0, 0:5, 5:5, 5:0";
|
|
771
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
772
|
+
"# 6:6, 7:7 | 8:8, 8:8 # 0:0, 0:5, 5:5, 5:0");
|
|
773
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
774
|
+
"# 1:1, 2:2 | 3:3, 3:3 #");
|
|
775
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
776
|
+
"# 6:6, 7:7 | 8:8, 8:8 #");
|
|
777
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
778
|
+
"# 6:6, 7:7 | 8:8, 8:8 # 0:0, 0:5, 5:5, 5:0");
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
TEST(S2BooleanOperation, PolygonVertexOpenPolygonVertex) {
|
|
782
|
+
S2BooleanOperation::Options options;
|
|
783
|
+
options.set_polygon_model(PolygonModel::OPEN);
|
|
784
|
+
auto a = "# # 0:0, 0:5, 1:5, 0:0, 2:5, 3:5";
|
|
785
|
+
auto b = "# # 0:0, 5:3, 5:2";
|
|
786
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
787
|
+
"# # 0:0, 0:5, 1:5, 0:0, 2:5, 3:5, 0:0, 5:3, 5:2");
|
|
788
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
789
|
+
"# #");
|
|
790
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
791
|
+
"# # 0:0, 0:5, 1:5, 0:0, 2:5, 3:5");
|
|
792
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
793
|
+
"# # 0:0, 0:5, 1:5, 0:0, 2:5, 3:5, 0:0, 5:3, 5:2");
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
TEST(S2BooleanOperation, PolygonVertexSemiOpenPolygonVertex) {
|
|
797
|
+
S2BooleanOperation::Options options;
|
|
798
|
+
options.set_polygon_model(PolygonModel::SEMI_OPEN);
|
|
799
|
+
auto a = "# # 0:0, 0:5, 1:5, 0:0, 2:5, 3:5";
|
|
800
|
+
auto b = "# # 0:0, 5:3, 5:2";
|
|
801
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
802
|
+
"# # 0:0, 0:5, 1:5, 0:0, 2:5, 3:5, 0:0, 5:3, 5:2");
|
|
803
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
804
|
+
"# #");
|
|
805
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
806
|
+
"# # 0:0, 0:5, 1:5, 0:0, 2:5, 3:5");
|
|
807
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
808
|
+
"# # 0:0, 0:5, 1:5, 0:0, 2:5, 3:5, 0:0, 5:3, 5:2");
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
TEST(S2BooleanOperation, PolygonVertexClosedPolygonVertex) {
|
|
812
|
+
S2BooleanOperation::Options options;
|
|
813
|
+
options.set_polygon_model(PolygonModel::CLOSED);
|
|
814
|
+
auto a = "# # 0:0, 0:5, 1:5, 0:0, 2:5, 3:5";
|
|
815
|
+
auto b = "# # 0:0, 5:3, 5:2";
|
|
816
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
817
|
+
"# # 0:0, 0:5, 1:5, 0:0, 2:5, 3:5, 0:0, 5:3, 5:2");
|
|
818
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
819
|
+
"# # 0:0");
|
|
820
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
821
|
+
"# # 0:0, 0:5, 1:5, 0:0, 2:5, 3:5");
|
|
822
|
+
ExpectResult(OpType::DIFFERENCE, options, b, a,
|
|
823
|
+
"# # 0:0, 5:3, 5:2");
|
|
824
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
825
|
+
"# # 0:0, 0:5, 1:5, 0:0, 2:5, 3:5, 0:0, 5:3, 5:2");
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
TEST(S2BooleanOperation, PolygonEdgePolygonEdgeCrossing) {
|
|
829
|
+
// Two polygons whose edges cross at points interior to both edges.
|
|
830
|
+
S2BooleanOperation::Options options = RoundToE(2);
|
|
831
|
+
auto a = "# # 0:0, 0:2, 2:2, 2:0";
|
|
832
|
+
auto b = "# # 1:1, 1:3, 3:3, 3:1";
|
|
833
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
834
|
+
"# # 0:0, 0:2, 1:2, 1:3, 3:3, 3:1, 2:1, 2:0");
|
|
835
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
836
|
+
"# # 1:1, 1:2, 2:2, 2:1");
|
|
837
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
838
|
+
"# # 0:0, 0:2, 1:2, 1:1, 2:1, 2:0");
|
|
839
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
840
|
+
"# # 0:0, 0:2, 1:2, 1:1, 2:1, 2:0; "
|
|
841
|
+
"1:2, 1:3, 3:3, 3:1, 2:1, 2:2");
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
TEST(S2BooleanOperation, PolygonEdgeOpenPolygonEdgeOverlap) {
|
|
845
|
+
S2BooleanOperation::Options options;
|
|
846
|
+
// One shape is a rectangle, the other consists of one triangle inside the
|
|
847
|
+
// rectangle and one triangle outside the rectangle, where each triangle
|
|
848
|
+
// shares one edge with the rectangle. This implies that the edges are in
|
|
849
|
+
// the same direction in one case and opposite directions in the other case.
|
|
850
|
+
options.set_polygon_model(PolygonModel::OPEN);
|
|
851
|
+
auto a = "# # 0:0, 0:4, 2:4, 2:0";
|
|
852
|
+
auto b = "# # 0:0, 1:1, 2:0; 0:4, 1:5, 2:4";
|
|
853
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
854
|
+
"# # 0:0, 0:4, 2:4, 2:0; 0:4, 1:5, 2:4");
|
|
855
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
856
|
+
"# # 0:0, 1:1, 2:0");
|
|
857
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
858
|
+
"# # 0:0, 0:4, 2:4, 2:0, 1:1");
|
|
859
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
860
|
+
"# # 0:0, 0:4, 2:4, 2:0, 1:1; 0:4, 1:5, 2:4");
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
TEST(S2BooleanOperation, PolygonEdgeSemiOpenPolygonEdgeOverlap) {
|
|
864
|
+
S2BooleanOperation::Options options;
|
|
865
|
+
options.set_polygon_model(PolygonModel::SEMI_OPEN);
|
|
866
|
+
auto a = "# # 0:0, 0:4, 2:4, 2:0";
|
|
867
|
+
auto b = "# # 0:0, 1:1, 2:0; 0:4, 1:5, 2:4";
|
|
868
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
869
|
+
"# # 0:0, 0:4, 1:5, 2:4, 2:0");
|
|
870
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
871
|
+
"# # 0:0, 1:1, 2:0");
|
|
872
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
873
|
+
"# # 0:0, 0:4, 2:4, 2:0, 1:1");
|
|
874
|
+
// Note that SYMMETRIC_DIFFERENCE does not guarantee that results are
|
|
875
|
+
// normalized, i.e. the output could contain siblings pairs (which can be
|
|
876
|
+
// discarded using S2Builder::GraphOptions).
|
|
877
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
878
|
+
"# # 0:0, 0:4, 2:4, 2:0, 1:1; 0:4, 1:5, 2:4");
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
TEST(S2BooleanOperation, PolygonEdgeClosedPolygonEdgeOverlap) {
|
|
882
|
+
S2BooleanOperation::Options options;
|
|
883
|
+
options.set_polygon_model(PolygonModel::CLOSED);
|
|
884
|
+
auto a = "# # 0:0, 0:4, 2:4, 2:0";
|
|
885
|
+
auto b = "# # 0:0, 1:1, 2:0; 0:4, 1:5, 2:4";
|
|
886
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
887
|
+
"# # 0:0, 0:4, 1:5, 2:4, 2:0");
|
|
888
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
889
|
+
"# # 0:0, 1:1, 2:0; 0:4, 2:4");
|
|
890
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
891
|
+
"# # 0:0, 0:4, 2:4, 2:0, 1:1");
|
|
892
|
+
// Note that SYMMETRIC_DIFFERENCE does not guarantee that results are
|
|
893
|
+
// normalized, i.e. the output could contain siblings pairs.
|
|
894
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
895
|
+
"# # 0:0, 0:4, 2:4, 2:0, 1:1; 0:4, 1:5, 2:4");
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
TEST(S2BooleanOperation, PolygonPolygonInterior) {
|
|
899
|
+
S2BooleanOperation::Options options; // PolygonModel is irrelevant.
|
|
900
|
+
// One loop in the interior of another polygon and one loop in the exterior.
|
|
901
|
+
auto a = "# # 0:0, 0:4, 4:4, 4:0";
|
|
902
|
+
auto b = "# # 1:1, 1:2, 2:2, 2:1; 5:5, 5:6, 6:6, 6:5";
|
|
903
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
904
|
+
"# # 0:0, 0:4, 4:4, 4:0; 5:5, 5:6, 6:6, 6:5");
|
|
905
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
906
|
+
"# # 1:1, 1:2, 2:2, 2:1");
|
|
907
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
908
|
+
"# # 0:0, 0:4, 4:4, 4:0; 2:1, 2:2, 1:2, 1:1");
|
|
909
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
910
|
+
"# # 0:0, 0:4, 4:4, 4:0; 2:1, 2:2, 1:2, 1:1; "
|
|
911
|
+
"5:5, 5:6, 6:6, 6:5");
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
TEST(S2BooleanOperation, PolygonEdgesDegenerateAfterSnapping) {
|
|
915
|
+
S2BooleanOperation::Options options = RoundToE(0);
|
|
916
|
+
auto a = "# # 0:-1, 0:1, 0.1:1, 0.1:-1";
|
|
917
|
+
auto b = "# # -1:0.1, 1:0.1, 1:0, -1:0";
|
|
918
|
+
// When snapping causes an output edge to become degenerate, it is still
|
|
919
|
+
// emitted (since otherwise loops that contract to a single point would be
|
|
920
|
+
// lost). If the output layer doesn't want such edges, they can be removed
|
|
921
|
+
// via DegenerateEdges::DISCARD or DISCARD_EXCESS.
|
|
922
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
923
|
+
"# # 0:-1, 0:-1, 0:0, 0:1, 0:1, 0:0 | "
|
|
924
|
+
"-1:0, -1:0, 0:0, 1:0, 1:0, 0:0");
|
|
925
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
926
|
+
"# # 0:0, 0:0, 0:0, 0:0");
|
|
927
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
928
|
+
"# # 0:-1, 0:-1, 0:0, 0:1, 0:1, 0:0 | 0:0, 0:0");
|
|
929
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
930
|
+
"# # 0:-1, 0:-1, 0:0, 0:1, 0:1, 0:0 | "
|
|
931
|
+
"-1:0, -1:0, 0:0, 1:0, 1:0, 0:0 | 0:0, 0:0, 0:0, 0:0");
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
///////////////////////////////////////////////////////////////////////////
|
|
935
|
+
// The remaining tests are intended to cover combinations of features or
|
|
936
|
+
// interesting special cases.
|
|
937
|
+
|
|
938
|
+
TEST(S2BooleanOperation, ThreeOverlappingBars) {
|
|
939
|
+
// Two vertical bars and a horizontal bar that overlaps both of the other
|
|
940
|
+
// bars and connects them.
|
|
941
|
+
|
|
942
|
+
// Round intersection points to E2 precision because the expected results
|
|
943
|
+
// were computed in lat/lng space rather than using geodesics.
|
|
944
|
+
S2BooleanOperation::Options options = RoundToE(2);
|
|
945
|
+
auto a = "# # 0:0, 0:2, 3:2, 3:0; 0:3, 0:5, 3:5, 3:3";
|
|
946
|
+
auto b = "# # 1:1, 1:4, 2:4, 2:1";
|
|
947
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
948
|
+
"# # 0:0, 0:2, 1:2, 1:3, 0:3, 0:5, 3:5, 3:3, 2:3, 2:2, 3:2, 3:0");
|
|
949
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
950
|
+
"# # 1:1, 1:2, 2:2, 2:1; 1:3, 1:4, 2:4, 2:3");
|
|
951
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
952
|
+
"# # 0:0, 0:2, 1:2, 1:1, 2:1, 2:2, 3:2, 3:0; "
|
|
953
|
+
"0:3, 0:5, 3:5, 3:3, 2:3, 2:4, 1:4, 1:3");
|
|
954
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
955
|
+
"# # 0:0, 0:2, 1:2, 1:1, 2:1, 2:2, 3:2, 3:0; "
|
|
956
|
+
"0:3, 0:5, 3:5, 3:3, 2:3, 2:4, 1:4, 1:3; "
|
|
957
|
+
"1:2, 1:3, 2:3, 2:2");
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
TEST(S2BooleanOperation, FourOverlappingBars) {
|
|
961
|
+
// Two vertical bars and two horizontal bars.
|
|
962
|
+
|
|
963
|
+
// Round intersection points to E2 precision because the expected results
|
|
964
|
+
// were computed in lat/lng space rather than using geodesics.
|
|
965
|
+
S2BooleanOperation::Options options = RoundToE(2);
|
|
966
|
+
auto a = "# # 1:88, 1:93, 2:93, 2:88; -1:88, -1:93, 0:93, 0:88";
|
|
967
|
+
auto b = "# # -2:89, -2:90, 3:90, 3:89; -2:91, -2:92, 3:92, 3:91";
|
|
968
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
969
|
+
"# # -1:88, -1:89, -2:89, -2:90, -1:90, -1:91, -2:91, -2:92, -1:92, "
|
|
970
|
+
"-1:93, 0:93, 0:92, 1:92, 1:93, 2:93, 2:92, 3:92, 3:91, 2:91, "
|
|
971
|
+
"2:90, 3:90, 3:89, 2:89, 2:88, 1:88, 1:89, 0:89, 0:88; "
|
|
972
|
+
"0:90, 1:90, 1:91, 0:91" /*CW*/ );
|
|
973
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
974
|
+
"# # 1:89, 1:90, 2:90, 2:89; 1:91, 1:92, 2:92, 2:91; "
|
|
975
|
+
"-1:89, -1:90, 0:90, 0:89; -1:91, -1:92, 0:92, 0:91");
|
|
976
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
977
|
+
"# # 1:88, 1:89, 2:89, 2:88; 1:90, 1:91, 2:91, 2:90; "
|
|
978
|
+
"1:92, 1:93, 2:93, 2:92; -1:88, -1:89, 0:89, 0:88; "
|
|
979
|
+
"-1:90, -1:91, 0:91, 0:90; -1:92, -1:93, 0:93, 0:92");
|
|
980
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
981
|
+
"# # 1:88, 1:89, 2:89, 2:88; -1:88, -1:89, 0:89, 0:88; "
|
|
982
|
+
"1:90, 1:91, 2:91, 2:90; -1:90, -1:91, 0:91, 0:90; "
|
|
983
|
+
"1:92, 1:93, 2:93, 2:92; -1:92, -1:93, 0:93, 0:92; "
|
|
984
|
+
"-2:89, -2:90, -1:90, -1:89; -2:91, -2:92, -1:92, -1:91; "
|
|
985
|
+
"0:89, 0:90, 1:90, 1:89; 0:91, 0:92, 1:92, 1:91; "
|
|
986
|
+
"2:89, 2:90, 3:90, 3:89; 2:91, 2:92, 3:92, 3:91");
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
TEST(S2BooleanOperation, OverlappingDoughnuts) {
|
|
990
|
+
// Two overlapping square doughnuts whose holes do not overlap.
|
|
991
|
+
// This means that the union polygon has only two holes rather than three.
|
|
992
|
+
|
|
993
|
+
// Round intersection points to E2 precision because the expected results
|
|
994
|
+
// were computed in lat/lng space rather than using geodesics.
|
|
995
|
+
S2BooleanOperation::Options options = RoundToE(1);
|
|
996
|
+
auto a = "# # -1:-93, -1:-89, 3:-89, 3:-93; "
|
|
997
|
+
"0:-92, 2:-92, 2:-90, 0:-90" /*CW*/ ;
|
|
998
|
+
auto b = "# # -3:-91, -3:-87, 1:-87, 1:-91; "
|
|
999
|
+
"-2:-90, 0:-90, 0:-88, -2:-88" /*CW*/ ;
|
|
1000
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
1001
|
+
"# # -1:-93, -1:-91, -3:-91, -3:-87, 1:-87, 1:-89, 3:-89, 3:-93; "
|
|
1002
|
+
"0:-92, 2:-92, 2:-90, 1:-90, 1:-91, 0:-91; " /*CW */
|
|
1003
|
+
"-2:-90, -1:-90, -1:-89, 0:-89, 0:-88, -2:-88" /* CW */ );
|
|
1004
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
1005
|
+
"# # -1:-91, -1:-90, 0:-90, 0:-91; "
|
|
1006
|
+
"0:-90, 0:-89, 1:-89, 1:-90");
|
|
1007
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
1008
|
+
"# # -1:-93, -1:-91, 0:-91, 0:-92, 2:-92, "
|
|
1009
|
+
"2:-90, 1:-90, 1:-89, 3:-89, 3:-93; "
|
|
1010
|
+
"-1:-90, -1:-89, 0:-89, 0:-90");
|
|
1011
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
1012
|
+
"# # -1:-93, -1:-91, 0:-91, 0:-92, 2:-92, "
|
|
1013
|
+
"2:-90, 1:-90, 1:-89, 3:-89, 3:-93; "
|
|
1014
|
+
"-3:-91, -3:-87, 1:-87, 1:-89, 0:-89, 0:-88,-2:-88,-2:-90,-1:-90,-1:-91; "
|
|
1015
|
+
"-1:-90, -1:-89, 0:-89, 0:-90; "
|
|
1016
|
+
"1:-91, 0:-91, 0:-90, 1:-90");
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
TEST(S2BooleanOperation, PolylineEnteringRectangle) {
|
|
1020
|
+
// A polyline that enters a rectangle very close to one of its vertices.
|
|
1021
|
+
S2BooleanOperation::Options options = RoundToE(1);
|
|
1022
|
+
auto a = "# 0:0, 2:2 #";
|
|
1023
|
+
auto b = "# # 1:1, 1:3, 3:3, 3:1";
|
|
1024
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
1025
|
+
"# 0:0, 1:1 # 1:1, 1:3, 3:3, 3:1");
|
|
1026
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
1027
|
+
"# 1:1, 2:2 #");
|
|
1028
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
1029
|
+
"# 0:0, 1:1 #");
|
|
1030
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
1031
|
+
"# 0:0, 1:1 # 1:1, 1:3, 3:3, 3:1");
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
TEST(S2BooleanOperation, PolylineCrossingRectangleTwice) {
|
|
1035
|
+
// A polyline that crosses a rectangle in one direction, then moves to a
|
|
1036
|
+
// different side and crosses the rectangle in the other direction. Note
|
|
1037
|
+
// that an extra vertex is added where the two polyline edges cross.
|
|
1038
|
+
S2BooleanOperation::Options options = RoundToE(1);
|
|
1039
|
+
auto a = "# 0:-5, 0:5, 5:0, -5:0 #";
|
|
1040
|
+
auto b = "# # 1:1, 1:-1, -1:-1, -1:1";
|
|
1041
|
+
ExpectResult(OpType::UNION, options, a, b,
|
|
1042
|
+
"# 0:-5, 0:-1 | 0:1, 0:5, 5:0, 1:0 | -1:0, -5:0 "
|
|
1043
|
+
"# 1:1, 1:0, 1:-1, 0:-1, -1:-1, -1:0, -1:1, 0:1");
|
|
1044
|
+
ExpectResult(OpType::INTERSECTION, options, a, b,
|
|
1045
|
+
"# 0:-1, 0:0, 0:1 | 1:0, 0:0, -1:0 #");
|
|
1046
|
+
ExpectResult(OpType::DIFFERENCE, options, a, b,
|
|
1047
|
+
"# 0:-5, 0:-1 | 0:1, 0:5, 5:0, 1:0 | -1:0, -5:0 #");
|
|
1048
|
+
ExpectResult(OpType::SYMMETRIC_DIFFERENCE, options, a, b,
|
|
1049
|
+
"# 0:-5, 0:-1 | 0:1, 0:5, 5:0, 1:0 | -1:0, -5:0 "
|
|
1050
|
+
"# 1:1, 1:0, 1:-1, 0:-1, -1:-1, -1:0, -1:1, 0:1");
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// Subtracts a degenerate loop along the 180 degree meridian from the given
|
|
1054
|
+
// input geometry, and compares the result to "expected_str". The inputs should
|
|
1055
|
+
// be in the format expected by s2textformat::MakeIndex().
|
|
1056
|
+
void TestMeridianSplitting(const char* input_str, const char* expected_str) {
|
|
1057
|
+
auto input = s2textformat::MakeIndexOrDie(input_str);
|
|
1058
|
+
MutableS2ShapeIndex meridian;
|
|
1059
|
+
vector<vector<S2Point>> loops{{S2Point(0, 0, -1), S2Point(-1, 0, 0),
|
|
1060
|
+
S2Point(0, 0, 1), S2Point(-1, 0, 0)}};
|
|
1061
|
+
meridian.Add(make_unique<S2LaxPolygonShape>(loops));
|
|
1062
|
+
MutableS2ShapeIndex output;
|
|
1063
|
+
vector<unique_ptr<S2Builder::Layer>> layers(3);
|
|
1064
|
+
layers[0] = make_unique<s2builderutil::IndexedS2PointVectorLayer>(&output);
|
|
1065
|
+
// TODO(ericv): Implement s2builderutil::IndexedS2LaxPolylineVectorLayer.
|
|
1066
|
+
layers[1] = make_unique<s2builderutil::IndexedS2PolylineVectorLayer>(&output);
|
|
1067
|
+
layers[2] = make_unique<s2builderutil::IndexedLaxPolygonLayer>(&output);
|
|
1068
|
+
S2BooleanOperation op(OpType::DIFFERENCE, std::move(layers));
|
|
1069
|
+
S2Error error;
|
|
1070
|
+
ASSERT_TRUE(op.Build(*input, meridian, &error)) << error;
|
|
1071
|
+
EXPECT_EQ(expected_str, s2textformat::ToString(output));
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// This test demonstrated that S2 geometry can easily be transformed such that
|
|
1075
|
+
// no edge crosses the 180 degree meridian, as required by formats such as
|
|
1076
|
+
// GeoJSON, by simply subtracting a degenerate loop that follows the 180 degree
|
|
1077
|
+
// meridian. This not only splits polylines along the meridian, it also inserts
|
|
1078
|
+
// the necessary extra vertices at the north/south poles. (The only extra step
|
|
1079
|
+
// is that the vertices along the 180 degree meridian or at the poles may need
|
|
1080
|
+
// to be "doubled" into two vertices, one at longitude 180 and one at longitude
|
|
1081
|
+
// -180, in order to match the longitudes of the adjacent vertices.)
|
|
1082
|
+
TEST(S2BooleanOperation, MeridianSplitting) {
|
|
1083
|
+
// A line along the equator crossing the 180 degree meridian.
|
|
1084
|
+
TestMeridianSplitting("# 0:-160, 0:170 #", "# 0:-160, 0:180, 0:170 #");
|
|
1085
|
+
|
|
1086
|
+
// The northern hemisphere.
|
|
1087
|
+
TestMeridianSplitting("# # 0:0, 0:120, 0:-120",
|
|
1088
|
+
"# # 90:0, 0:180, 0:-120, 0:0, 0:120, 0:180");
|
|
1089
|
+
|
|
1090
|
+
// A small square that crosses the 180th meridian. Notice that one input
|
|
1091
|
+
// loop is split into two output loops.
|
|
1092
|
+
TestMeridianSplitting(
|
|
1093
|
+
"# # 9:179, 9:-179, 10:-179, 10:179",
|
|
1094
|
+
"# # 9.00134850712993:180, 9:-179, 10:-179, 10.0014925269841:-180; "
|
|
1095
|
+
"10.0014925269841:-180, 10:179, 9:179, 9.00134850712993:180");
|
|
1096
|
+
|
|
1097
|
+
// An annulus that crosses the 180th meridian. This turns into two shells.
|
|
1098
|
+
TestMeridianSplitting(
|
|
1099
|
+
"# # 8:178, 8:-178, 11:-178, 11:178; 9:179, 10:179, 10:-179, 9:-179",
|
|
1100
|
+
"# # 10.0014925269841:180, 10:-179, 9:-179, 9.00134850712993:-180, "
|
|
1101
|
+
"8.00481316618607:180, 8:-178, 11:-178, 11.00654129428:-180; "
|
|
1102
|
+
"9.00134850712993:-180, 9:179, 10:179, 10.0014925269841:180, "
|
|
1103
|
+
"11.00654129428:-180, 11:178, 8:178, 8.00481316618607:180");
|
|
1104
|
+
|
|
1105
|
+
// An annulus that crosses the 180th meridian. This turns into two shells.
|
|
1106
|
+
TestMeridianSplitting(
|
|
1107
|
+
"# # 8:178, 8:-178, 11:-178, 11:178; 9:179, 10:179, 10:-179, 9:-179",
|
|
1108
|
+
"# # 10.0014925269841:180, 10:-179, 9:-179, 9.00134850712993:-180, "
|
|
1109
|
+
"8.00481316618607:180, 8:-178, 11:-178, 11.00654129428:-180; "
|
|
1110
|
+
"9.00134850712993:-180, 9:179, 10:179, 10.0014925269841:180, "
|
|
1111
|
+
"11.00654129428:-180, 11:178, 8:178, 8.00481316618607:180");
|
|
1112
|
+
|
|
1113
|
+
// The whole world except for a small square that crosses the 180th meridian.
|
|
1114
|
+
// This is a single loop that visits both poles. The result is correct
|
|
1115
|
+
// except that (1) +180 or -180 needs to be chosen consistently with the
|
|
1116
|
+
// adjacent points, and (2) each pole needs to be duplicated (once with
|
|
1117
|
+
// longitude -180 and once with longitude 180).
|
|
1118
|
+
TestMeridianSplitting(
|
|
1119
|
+
"# # 9:-179, 9:179, 10:179, 10:-179",
|
|
1120
|
+
"# # 0:180, 9.00134850712993:-180, 9:179, 10:179, 10.0014925269841:180, "
|
|
1121
|
+
"90:0, 10.0014925269841:180, 10:-179, 9:-179, 9.00134850712993:-180, "
|
|
1122
|
+
"0:180, -90:0");
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// This test exercises the "special case" documented in
|
|
1126
|
+
// GraphEdgeClipper::GetCrossedVertexIndex().
|
|
1127
|
+
TEST(S2BooleanOperation, GetCrossedVertexIndexBug) {
|
|
1128
|
+
// The first two edges (a0, a1) and (b0, b1) of the following polygons cross
|
|
1129
|
+
// such that after snapping, the corresponding edge chains are:
|
|
1130
|
+
//
|
|
1131
|
+
// a0 a1 -> a0 b0 b1 x a1
|
|
1132
|
+
// b0 b1 -> b0 x b1
|
|
1133
|
+
//
|
|
1134
|
+
// where "x" is the computed intersection point of (a0, a1) and (b0, b1).
|
|
1135
|
+
// Previously there was a bug such that the two edge chains did not choose
|
|
1136
|
+
// the same vertex to represent the point where the two chains cross: the
|
|
1137
|
+
// (a0, a1) chain chose "x" as the crossing point while the (b0, b1) chain
|
|
1138
|
+
// chose "b0". This has been fixed such that both chains now choose "x".
|
|
1139
|
+
// (Both "x" and "b1" happen to be valid choices in this example, but it is
|
|
1140
|
+
// essential that both subchains make the same choice.)
|
|
1141
|
+
|
|
1142
|
+
// S2LatLng coordinates are not accurate enough to reproduce this example.
|
|
1143
|
+
vector<vector<S2Point>> a_loops{{
|
|
1144
|
+
// 51.5131559470858:-0.130381523356724
|
|
1145
|
+
{0.62233331065911901, -0.0014161759526823048, 0.78275107466533156},
|
|
1146
|
+
// 51.5131892038956:-0.130404244210776
|
|
1147
|
+
{0.6223328557578689, -0.0014164217071954736, 0.78275143589379825},
|
|
1148
|
+
s2textformat::MakePointOrDie("51.51317:-0.1306")
|
|
1149
|
+
}};
|
|
1150
|
+
vector<vector<S2Point>> b_loops{{
|
|
1151
|
+
// 51.5131559705551:-0.13038153939079
|
|
1152
|
+
{0.62233331033809591, -0.001416176126110953, 0.78275107492024998},
|
|
1153
|
+
// 51.5131559705551:-0.130381539390786
|
|
1154
|
+
{0.62233331033809591, -0.0014161761261109063, 0.78275107492025009},
|
|
1155
|
+
s2textformat::MakePointOrDie("51.52:-0.12"),
|
|
1156
|
+
s2textformat::MakePointOrDie("51.52:-0.14")
|
|
1157
|
+
}};
|
|
1158
|
+
MutableS2ShapeIndex a, b;
|
|
1159
|
+
a.Add(make_unique<S2LaxPolygonShape>(a_loops));
|
|
1160
|
+
b.Add(make_unique<S2LaxPolygonShape>(b_loops));
|
|
1161
|
+
S2LaxPolygonShape actual;
|
|
1162
|
+
LaxPolygonLayer::Options options;
|
|
1163
|
+
options.set_degenerate_boundaries(
|
|
1164
|
+
LaxPolygonLayer::Options::DegenerateBoundaries::DISCARD);
|
|
1165
|
+
S2BooleanOperation op(OpType::UNION,
|
|
1166
|
+
make_unique<LaxPolygonLayer>(&actual, options));
|
|
1167
|
+
S2Error error;
|
|
1168
|
+
ASSERT_TRUE(op.Build(a, b, &error)) << error;
|
|
1169
|
+
EXPECT_EQ("51.513187135478:-0.130425328888064, "
|
|
1170
|
+
"51.51317:-0.1306, "
|
|
1171
|
+
"51.5131559470858:-0.130381523356724, "
|
|
1172
|
+
"51.5131559705551:-0.13038153939079, "
|
|
1173
|
+
"51.5131559705551:-0.130381539390786, "
|
|
1174
|
+
"51.52:-0.12, "
|
|
1175
|
+
"51.52:-0.14",
|
|
1176
|
+
s2textformat::ToString(actual));
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
// Performs the given operation and compares the result to "expected_str". All
|
|
1180
|
+
// arguments are in s2textformat::MakeLaxPolygon() format.
|
|
1181
|
+
void ExpectPolygon(S2BooleanOperation::OpType op_type,
|
|
1182
|
+
const string& a_str, const string& b_str,
|
|
1183
|
+
const string& expected_str) {
|
|
1184
|
+
auto a = s2textformat::MakeIndexOrDie(string("# # ") + a_str);
|
|
1185
|
+
auto b = s2textformat::MakeIndexOrDie(string("# # ") + b_str);
|
|
1186
|
+
s2builderutil::LaxPolygonLayer::Options polygon_options;
|
|
1187
|
+
polygon_options.set_degenerate_boundaries(DegenerateBoundaries::DISCARD);
|
|
1188
|
+
S2LaxPolygonShape output;
|
|
1189
|
+
S2BooleanOperation op(
|
|
1190
|
+
op_type,
|
|
1191
|
+
make_unique<s2builderutil::LaxPolygonLayer>(&output, polygon_options),
|
|
1192
|
+
S2BooleanOperation::Options{s2builderutil::IdentitySnapFunction{
|
|
1193
|
+
S1Angle::Degrees(1.1)}});
|
|
1194
|
+
S2Error error;
|
|
1195
|
+
ASSERT_TRUE(op.Build(*a, *b, &error)) << error;
|
|
1196
|
+
EXPECT_EQ(expected_str, s2textformat::ToString(output));
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
TEST(S2BooleanOperation, FullAndEmptyResults) {
|
|
1200
|
+
// The following constants are all in s2textformat::MakeLaxPolygon() format.
|
|
1201
|
+
string kEmpty = "";
|
|
1202
|
+
string kFull = "full";
|
|
1203
|
+
|
|
1204
|
+
// Two complementary shell/hole pairs, together with alternative shells that
|
|
1205
|
+
// are slightly smaller or larger than the original.
|
|
1206
|
+
string kShell1 = "10:0, 10:10, 20:10";
|
|
1207
|
+
string kHole1 = "10:0, 20:10, 10:10";
|
|
1208
|
+
string kShell1Minus = "11:2, 11:9, 18:9";
|
|
1209
|
+
string kShell1Plus = "9:-2, 9:11, 22:11";
|
|
1210
|
+
string kShell2 = "10:20, 10:30, 20:30";
|
|
1211
|
+
string kHole2 = "10:20, 20:30, 10:30";
|
|
1212
|
+
|
|
1213
|
+
// The northern and southern hemispheres.
|
|
1214
|
+
string kNorthHemi = "0:0, 0:120, 0:-120";
|
|
1215
|
+
string kSouthHemi = "0:0, 0:-120, 0:120";
|
|
1216
|
+
// These edges deviate from kSouthHemi by slightly more than 1 degree.
|
|
1217
|
+
string kSouthHemiPlus = "0.5:0, 0.5:-120, 0.5:120";
|
|
1218
|
+
|
|
1219
|
+
// A shell and hole that cover complementary hemispheres, such that each
|
|
1220
|
+
// hemisphere intersects all six S2 cube faces. There are also alternative
|
|
1221
|
+
// shells that are slightly smaller or larger than the original.
|
|
1222
|
+
string k6FaceShell1 = "0:-45, 45:0, 45:90, 0:135, -45:180, -45:-90";
|
|
1223
|
+
string k6FaceHole1 = "0:-45, -45:-90, -45:180, 0:135, 45:90, 45:0";
|
|
1224
|
+
string k6FaceShell1Minus = "-1:-45, 44:0, 44:90, -1:135, -46:180, -46:-90";
|
|
1225
|
+
string k6FaceShell1Plus = "1:-45, 46:0, 46:90, 1:135, -44:180, -44:-90";
|
|
1226
|
+
|
|
1227
|
+
// Two complementary shell/hole pairs that are small enough so that they will
|
|
1228
|
+
// disappear when the snap radius chosen above is used.
|
|
1229
|
+
string kAlmostEmpty1 = "2:0, 2:10, 3:0";
|
|
1230
|
+
string kAlmostFull1 = "2:0, 3:0, 2:10";
|
|
1231
|
+
string kAlmostEmpty2 = "4:0, 4:10, 5:0";
|
|
1232
|
+
string kAlmostFull2 = "4:0, 5:0, 4:10";
|
|
1233
|
+
|
|
1234
|
+
// A polygon that intersects all 6 faces such but snaps to an empty polygon.
|
|
1235
|
+
string k6FaceAlmostEmpty1 = k6FaceShell1Minus + "; " + k6FaceHole1;
|
|
1236
|
+
|
|
1237
|
+
// Test empty UNION results.
|
|
1238
|
+
// - Exact result, no input edges.
|
|
1239
|
+
ExpectPolygon(OpType::UNION, kEmpty, kEmpty, kEmpty);
|
|
1240
|
+
// - Empty due to snapping, union does not intersect all 6 cube faces.
|
|
1241
|
+
ExpectPolygon(OpType::UNION, kAlmostEmpty1, kAlmostEmpty2, kEmpty);
|
|
1242
|
+
// - Empty due to snapping, union intersects all 6 cube faces.
|
|
1243
|
+
ExpectPolygon(OpType::UNION, k6FaceAlmostEmpty1, k6FaceAlmostEmpty1, kEmpty);
|
|
1244
|
+
|
|
1245
|
+
// Test full UNION results.
|
|
1246
|
+
// - Exact result, no input edges.
|
|
1247
|
+
ExpectPolygon(OpType::UNION, kEmpty, kFull, kFull);
|
|
1248
|
+
ExpectPolygon(OpType::UNION, kEmpty, kFull, kFull);
|
|
1249
|
+
ExpectPolygon(OpType::UNION, kFull, kFull, kFull);
|
|
1250
|
+
// - Exact result, some input edges.
|
|
1251
|
+
ExpectPolygon(OpType::UNION, kFull, kShell1, kFull);
|
|
1252
|
+
ExpectPolygon(OpType::UNION, kHole1, kHole2, kFull);
|
|
1253
|
+
ExpectPolygon(OpType::UNION, kHole1, kShell1, kFull);
|
|
1254
|
+
// - Full due to snapping, almost complementary polygons.
|
|
1255
|
+
ExpectPolygon(OpType::UNION, kHole1, kShell1Minus, kFull);
|
|
1256
|
+
ExpectPolygon(OpType::UNION, k6FaceHole1, k6FaceShell1Minus, kFull);
|
|
1257
|
+
|
|
1258
|
+
// Test empty INTERSECTION results.
|
|
1259
|
+
// - Exact result, no input edges.
|
|
1260
|
+
ExpectPolygon(OpType::INTERSECTION, kEmpty, kEmpty, kEmpty);
|
|
1261
|
+
ExpectPolygon(OpType::INTERSECTION, kEmpty, kFull, kEmpty);
|
|
1262
|
+
ExpectPolygon(OpType::INTERSECTION, kFull, kEmpty, kEmpty);
|
|
1263
|
+
// - Exact result, inputs do not both intersect all 6 cube faces.
|
|
1264
|
+
ExpectPolygon(OpType::INTERSECTION, kEmpty, kHole1, kEmpty);
|
|
1265
|
+
ExpectPolygon(OpType::INTERSECTION, kShell1, kShell2, kEmpty);
|
|
1266
|
+
ExpectPolygon(OpType::INTERSECTION, kShell1, kHole1, kEmpty);
|
|
1267
|
+
// - Exact result, inputs both intersect all 6 cube faces.
|
|
1268
|
+
ExpectPolygon(OpType::INTERSECTION, k6FaceShell1, k6FaceHole1, kEmpty);
|
|
1269
|
+
// - Empty due to snapping, inputs do not both intersect all 6 cube faces.
|
|
1270
|
+
ExpectPolygon(OpType::INTERSECTION, kShell1Plus, kHole1, kEmpty);
|
|
1271
|
+
// - Empty due to snapping, inputs both intersect all 6 cube faces.
|
|
1272
|
+
ExpectPolygon(OpType::INTERSECTION, k6FaceShell1Plus, k6FaceHole1, kEmpty);
|
|
1273
|
+
|
|
1274
|
+
// Test full INTERSECTION results.
|
|
1275
|
+
// - Exact result, no input edges.
|
|
1276
|
+
ExpectPolygon(OpType::INTERSECTION, kFull, kFull, kFull);
|
|
1277
|
+
// - Full due to snapping, almost full input polygons.
|
|
1278
|
+
ExpectPolygon(OpType::INTERSECTION, kAlmostFull1, kAlmostFull2, kFull);
|
|
1279
|
+
|
|
1280
|
+
// Test empty DIFFERENCE results.
|
|
1281
|
+
// - Exact result, no input edges.
|
|
1282
|
+
ExpectPolygon(OpType::DIFFERENCE, kEmpty, kEmpty, kEmpty);
|
|
1283
|
+
ExpectPolygon(OpType::DIFFERENCE, kEmpty, kFull, kEmpty);
|
|
1284
|
+
ExpectPolygon(OpType::DIFFERENCE, kFull, kFull, kEmpty);
|
|
1285
|
+
// - Exact result, first input does not intersect all 6 cube faces.
|
|
1286
|
+
ExpectPolygon(OpType::DIFFERENCE, kEmpty, kShell1, kEmpty);
|
|
1287
|
+
ExpectPolygon(OpType::DIFFERENCE, kShell1, kFull, kEmpty);
|
|
1288
|
+
ExpectPolygon(OpType::DIFFERENCE, kShell1, kShell1, kEmpty);
|
|
1289
|
+
ExpectPolygon(OpType::DIFFERENCE, kShell1, kHole2, kEmpty);
|
|
1290
|
+
// - Exact result, first input intersects all 6 cube faces.
|
|
1291
|
+
ExpectPolygon(OpType::DIFFERENCE, k6FaceShell1, k6FaceShell1Plus, kEmpty);
|
|
1292
|
+
// - Empty due to snapping, first input does not intersect all 6 cube faces.
|
|
1293
|
+
ExpectPolygon(OpType::DIFFERENCE, kShell1Plus, kShell1, kEmpty);
|
|
1294
|
+
// - Empty due to snapping, first input intersect all 6 cube faces.
|
|
1295
|
+
ExpectPolygon(OpType::DIFFERENCE, k6FaceShell1Plus, k6FaceShell1, kEmpty);
|
|
1296
|
+
|
|
1297
|
+
// Test full DIFFERENCE results.
|
|
1298
|
+
// - Exact result, no input edges.
|
|
1299
|
+
ExpectPolygon(OpType::DIFFERENCE, kFull, kEmpty, kFull);
|
|
1300
|
+
// - Full due to snapping, almost full/empty input polygons.
|
|
1301
|
+
ExpectPolygon(OpType::DIFFERENCE, kAlmostFull1, kAlmostEmpty2, kFull);
|
|
1302
|
+
|
|
1303
|
+
// Test empty SYMMETRIC_DIFFERENCE results.
|
|
1304
|
+
// - Exact result, no input edges.
|
|
1305
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kEmpty, kEmpty, kEmpty);
|
|
1306
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kFull, kFull, kEmpty);
|
|
1307
|
+
// - Exact result, union does not intersect all 6 cube faces.
|
|
1308
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kShell1, kShell1, kEmpty);
|
|
1309
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kNorthHemi, kNorthHemi, kEmpty);
|
|
1310
|
+
// - Exact result, union intersects all 6 cube faces. This case is only
|
|
1311
|
+
// handled correctly due to the kBiasTowardsEmpty heuristic.
|
|
1312
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, k6FaceShell1, k6FaceShell1,
|
|
1313
|
+
kEmpty);
|
|
1314
|
+
// - Empty due to snapping, union does not intersect all 6 cube faces.
|
|
1315
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kShell1Plus, kShell1, kEmpty);
|
|
1316
|
+
// - Empty due to snapping, union intersects all 6 cube faces. This case is
|
|
1317
|
+
// only handled correctly due to the kBiasTowardsEmpty heuristic.
|
|
1318
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, k6FaceShell1Plus, k6FaceShell1,
|
|
1319
|
+
kEmpty);
|
|
1320
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, k6FaceShell1Minus, k6FaceShell1,
|
|
1321
|
+
kEmpty);
|
|
1322
|
+
|
|
1323
|
+
// Test full SYMMETRIC_DIFFERENCE results.
|
|
1324
|
+
// - Exact result, no input edges.
|
|
1325
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kFull, kEmpty, kFull);
|
|
1326
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kEmpty, kFull, kFull);
|
|
1327
|
+
// - Exact result, complementary input polygons.
|
|
1328
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kShell1, kHole1, kFull);
|
|
1329
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kAlmostEmpty1, kAlmostFull1,
|
|
1330
|
+
kFull);
|
|
1331
|
+
// - Full due to snapping, almost complementary input polygons.
|
|
1332
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kShell1Plus, kHole1, kFull);
|
|
1333
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kAlmostFull1, kAlmostEmpty2,
|
|
1334
|
+
kFull);
|
|
1335
|
+
// - Exact result, complementary hemispheres, at least one input does not
|
|
1336
|
+
// intersect all 6 cube faces.
|
|
1337
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kNorthHemi, kSouthHemi, kFull);
|
|
1338
|
+
// - Exact result, almost complementary hemispheres, at least one input does
|
|
1339
|
+
// not intersect all 6 cube faces.
|
|
1340
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, kNorthHemi, kSouthHemiPlus,
|
|
1341
|
+
kFull);
|
|
1342
|
+
|
|
1343
|
+
// TODO(ericv): The following case is not currently implemented.
|
|
1344
|
+
// - Full result, complementary (to within the snap radius) input polygons
|
|
1345
|
+
// each with an area of approximately 2*Pi, and both polygons intersect all
|
|
1346
|
+
// 6 cube faces.
|
|
1347
|
+
#if 0
|
|
1348
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, k6FaceShell1, k6FaceHole1, kFull);
|
|
1349
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, k6FaceShell1Plus, k6FaceHole1,
|
|
1350
|
+
kFull);
|
|
1351
|
+
ExpectPolygon(OpType::SYMMETRIC_DIFFERENCE, k6FaceShell1Minus, k6FaceHole1,
|
|
1352
|
+
kFull);
|
|
1353
|
+
#endif
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
// Tests whether the two S2ShapeIndexes are equal according to
|
|
1357
|
+
// S2BooleanOperation::Equals().
|
|
1358
|
+
bool TestEqual(const string& a_str, const string& b_str) {
|
|
1359
|
+
auto a = s2textformat::MakeIndexOrDie(a_str);
|
|
1360
|
+
auto b = s2textformat::MakeIndexOrDie(b_str);
|
|
1361
|
+
return S2BooleanOperation::Equals(*a, *b);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
// Tests S2BooleanOperation::Equals, which computes the symmetric difference
|
|
1365
|
+
// between two geometries and tests whether the result is empty.
|
|
1366
|
+
//
|
|
1367
|
+
// This also indirectly tests IsEmpty(), which is used to implement Contains()
|
|
1368
|
+
// and Intersects().
|
|
1369
|
+
TEST(S2BooleanOperation, Equals) {
|
|
1370
|
+
EXPECT_TRUE(TestEqual("# #", "# #"));
|
|
1371
|
+
EXPECT_TRUE(TestEqual("# # full", "# # full"));
|
|
1372
|
+
|
|
1373
|
+
EXPECT_FALSE(TestEqual("# #", "# # full"));
|
|
1374
|
+
EXPECT_FALSE(TestEqual("0:0 # #", "# #"));
|
|
1375
|
+
EXPECT_FALSE(TestEqual("0:0 # #", "# # full"));
|
|
1376
|
+
EXPECT_FALSE(TestEqual("# 0:0, 1:1 #", "# #"));
|
|
1377
|
+
EXPECT_FALSE(TestEqual("# 0:0, 1:1 #", "# # full"));
|
|
1378
|
+
EXPECT_FALSE(TestEqual("# # 0:0, 0:1, 1:0 ", "# #"));
|
|
1379
|
+
EXPECT_FALSE(TestEqual("# # 0:0, 0:1, 1:0 ", "# # full"));
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
// Tests Contains() on empty and full geometries.
|
|
1383
|
+
TEST(S2BooleanOperation, ContainsEmptyAndFull) {
|
|
1384
|
+
auto empty = s2textformat::MakeIndexOrDie("# #");
|
|
1385
|
+
auto full = s2textformat::MakeIndexOrDie("# # full");
|
|
1386
|
+
EXPECT_TRUE(S2BooleanOperation::Contains(*empty, *empty));
|
|
1387
|
+
EXPECT_FALSE(S2BooleanOperation::Contains(*empty, *full));
|
|
1388
|
+
EXPECT_TRUE(S2BooleanOperation::Contains(*full, *empty));
|
|
1389
|
+
EXPECT_TRUE(S2BooleanOperation::Contains(*full, *full));
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
// Tests Intersects() on empty and full geometries.
|
|
1393
|
+
TEST(S2BooleanOperation, IntersectsEmptyAndFull) {
|
|
1394
|
+
auto empty = s2textformat::MakeIndexOrDie("# #");
|
|
1395
|
+
auto full = s2textformat::MakeIndexOrDie("# # full");
|
|
1396
|
+
EXPECT_FALSE(S2BooleanOperation::Intersects(*empty, *empty));
|
|
1397
|
+
EXPECT_FALSE(S2BooleanOperation::Intersects(*empty, *full));
|
|
1398
|
+
EXPECT_FALSE(S2BooleanOperation::Intersects(*full, *empty));
|
|
1399
|
+
EXPECT_TRUE(S2BooleanOperation::Intersects(*full, *full));
|
|
1400
|
+
}
|