@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,1828 @@
|
|
|
1
|
+
// Copyright 2016 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
|
+
// The algorithm is based on the idea of choosing a set of sites and computing
|
|
19
|
+
// their "limited radius Voronoi diagram", which is obtained by intersecting
|
|
20
|
+
// each Voronoi region with a disc of fixed radius (the "snap radius")
|
|
21
|
+
// centered around the corresponding site.
|
|
22
|
+
//
|
|
23
|
+
// For each input edge, we then determine the sequence of Voronoi regions
|
|
24
|
+
// crossed by that edge, and snap the edge to the corresponding sequence of
|
|
25
|
+
// sites. (In other words, each input edge is replaced by an edge chain.)
|
|
26
|
+
//
|
|
27
|
+
// The sites are chosen by starting with the set of input vertices, optionally
|
|
28
|
+
// snapping them to discrete point set (such as S2CellId centers or lat/lng E7
|
|
29
|
+
// coordinates), and then choosing a subset such that no two sites are closer
|
|
30
|
+
// than the given "snap_radius". Note that the sites do not need to be spaced
|
|
31
|
+
// regularly -- their positions are completely arbitrary.
|
|
32
|
+
//
|
|
33
|
+
// Rather than computing the full limited radius Voronoi diagram, instead we
|
|
34
|
+
// compute on demand the sequence of Voronoi regions intersected by each edge.
|
|
35
|
+
// We do this by first finding all the sites that are within "snap_radius" of
|
|
36
|
+
// the edge, sorting them by distance from the edge origin, and then using an
|
|
37
|
+
// incremental algorithm.
|
|
38
|
+
//
|
|
39
|
+
// We implement the minimum edge-vertex separation property by snapping all
|
|
40
|
+
// the input edges and checking whether any site (the "site to avoid") would
|
|
41
|
+
// then be too close to the edge. If so we add another site (the "separation
|
|
42
|
+
// site") along the input edge, positioned so that the new snapped edge is
|
|
43
|
+
// guaranteed to be far enough away from the site to avoid. We then find all
|
|
44
|
+
// the input edges that are within "snap_radius" of the new site, and resnap
|
|
45
|
+
// those edges. (It is very rare that we need to add a separation site, even
|
|
46
|
+
// when sites are densely spaced.)
|
|
47
|
+
//
|
|
48
|
+
// Idempotency is implemented by explicitly checking whether the input
|
|
49
|
+
// geometry already meets the output criteria. This is not as sophisticated
|
|
50
|
+
// as Stable Snap Rounding (Hershberger); I have worked out the details and it
|
|
51
|
+
// is possible to adapt those ideas here, but it would make the implementation
|
|
52
|
+
// significantly more complex.
|
|
53
|
+
//
|
|
54
|
+
// The only way that different output layers interact is in the choice of
|
|
55
|
+
// Voronoi sites:
|
|
56
|
+
//
|
|
57
|
+
// - Vertices from all layers contribute to the initial selection of sites.
|
|
58
|
+
//
|
|
59
|
+
// - Edges in any layer that pass too close to a site can cause new sites to
|
|
60
|
+
// be added (which affects snapping in all layers).
|
|
61
|
+
//
|
|
62
|
+
// - Simplification can be thought of as removing sites. A site can be
|
|
63
|
+
// removed only if the snapped edges stay within the error bounds of the
|
|
64
|
+
// corresponding input edges in all layers.
|
|
65
|
+
//
|
|
66
|
+
// Otherwise all layers are processed independently. For example, sibling
|
|
67
|
+
// edge pairs can only cancel each other within a single layer (if desired).
|
|
68
|
+
|
|
69
|
+
#include "s2/s2builder.h"
|
|
70
|
+
|
|
71
|
+
#include <algorithm>
|
|
72
|
+
#include <cfloat>
|
|
73
|
+
#include <cmath>
|
|
74
|
+
#include <iostream>
|
|
75
|
+
#include <memory>
|
|
76
|
+
#include <numeric>
|
|
77
|
+
#include <string>
|
|
78
|
+
#include <vector>
|
|
79
|
+
|
|
80
|
+
#include "s2/base/casts.h"
|
|
81
|
+
#include "s2/base/logging.h"
|
|
82
|
+
#include "s2/third_party/absl/memory/memory.h"
|
|
83
|
+
#include "s2/util/bits/bits.h"
|
|
84
|
+
#include "s2/id_set_lexicon.h"
|
|
85
|
+
#include "s2/mutable_s2shape_index.h"
|
|
86
|
+
#include "s2/s1angle.h"
|
|
87
|
+
#include "s2/s1chord_angle.h"
|
|
88
|
+
#include "s2/s2builder_graph.h"
|
|
89
|
+
#include "s2/s2builder_layer.h"
|
|
90
|
+
#include "s2/s2builderutil_snap_functions.h"
|
|
91
|
+
#include "s2/s2closest_edge_query.h"
|
|
92
|
+
#include "s2/s2closest_point_query.h"
|
|
93
|
+
#include "s2/s2edge_crossings.h"
|
|
94
|
+
#include "s2/s2edge_distances.h"
|
|
95
|
+
#include "s2/s2error.h"
|
|
96
|
+
#include "s2/s2loop.h"
|
|
97
|
+
#include "s2/s2point_index.h"
|
|
98
|
+
#include "s2/s2pointutil.h"
|
|
99
|
+
#include "s2/s2polygon.h"
|
|
100
|
+
#include "s2/s2polyline.h"
|
|
101
|
+
#include "s2/s2polyline_simplifier.h"
|
|
102
|
+
#include "s2/s2predicates.h"
|
|
103
|
+
#include "s2/s2shapeutil_visit_crossing_edge_pairs.h"
|
|
104
|
+
#include "s2/s2text_format.h"
|
|
105
|
+
|
|
106
|
+
using absl::make_unique;
|
|
107
|
+
using gtl::compact_array;
|
|
108
|
+
using std::max;
|
|
109
|
+
using std::pair;
|
|
110
|
+
using std::unique_ptr;
|
|
111
|
+
using std::vector;
|
|
112
|
+
|
|
113
|
+
// Internal flag intended to be set from within a debugger.
|
|
114
|
+
bool s2builder_verbose = false;
|
|
115
|
+
|
|
116
|
+
S1Angle S2Builder::SnapFunction::max_edge_deviation() const {
|
|
117
|
+
// We want max_edge_deviation() to be large enough compared to snap_radius()
|
|
118
|
+
// such that edge splitting is rare.
|
|
119
|
+
//
|
|
120
|
+
// Using spherical trigonometry, if the endpoints of an edge of length L
|
|
121
|
+
// move by at most a distance R, the center of the edge moves by at most
|
|
122
|
+
// asin(sin(R) / cos(L / 2)). Thus the (max_edge_deviation / snap_radius)
|
|
123
|
+
// ratio increases with both the snap radius R and the edge length L.
|
|
124
|
+
//
|
|
125
|
+
// We arbitrarily limit the edge deviation to be at most 10% more than the
|
|
126
|
+
// snap radius. With the maximum allowed snap radius of 70 degrees, this
|
|
127
|
+
// means that edges up to 30.6 degrees long are never split. For smaller
|
|
128
|
+
// snap radii, edges up to 49 degrees long are never split. (Edges of any
|
|
129
|
+
// length are not split unless their endpoints move far enough so that the
|
|
130
|
+
// actual edge deviation exceeds the limit; in practice, splitting is rare
|
|
131
|
+
// even with long edges.) Note that it is always possible to split edges
|
|
132
|
+
// when max_edge_deviation() is exceeded; see MaybeAddExtraSites().
|
|
133
|
+
S2_DCHECK_LE(snap_radius(), kMaxSnapRadius());
|
|
134
|
+
const double kMaxEdgeDeviationRatio = 1.1;
|
|
135
|
+
return kMaxEdgeDeviationRatio * snap_radius();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
S2Builder::Options::Options()
|
|
139
|
+
: snap_function_(
|
|
140
|
+
make_unique<s2builderutil::IdentitySnapFunction>(S1Angle::Zero())) {
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
S2Builder::Options::Options(const SnapFunction& snap_function)
|
|
144
|
+
: snap_function_(snap_function.Clone()) {
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
S2Builder::Options::Options(const Options& options)
|
|
148
|
+
: snap_function_(options.snap_function_->Clone()),
|
|
149
|
+
split_crossing_edges_(options.split_crossing_edges_),
|
|
150
|
+
simplify_edge_chains_(options.simplify_edge_chains_),
|
|
151
|
+
idempotent_(options.idempotent_) {
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
S2Builder::Options& S2Builder::Options::operator=(const Options& options) {
|
|
155
|
+
snap_function_ = options.snap_function_->Clone();
|
|
156
|
+
split_crossing_edges_ = options.split_crossing_edges_;
|
|
157
|
+
simplify_edge_chains_ = options.simplify_edge_chains_;
|
|
158
|
+
idempotent_ = options.idempotent_;
|
|
159
|
+
return *this;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
bool operator==(const S2Builder::GraphOptions& x,
|
|
163
|
+
const S2Builder::GraphOptions& y) {
|
|
164
|
+
return (x.edge_type() == y.edge_type() &&
|
|
165
|
+
x.degenerate_edges() == y.degenerate_edges() &&
|
|
166
|
+
x.duplicate_edges() == y.duplicate_edges() &&
|
|
167
|
+
x.sibling_pairs() == y.sibling_pairs() &&
|
|
168
|
+
x.allow_vertex_filtering() == y.allow_vertex_filtering());
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Helper functions for computing error bounds:
|
|
172
|
+
|
|
173
|
+
static S1ChordAngle RoundUp(S1Angle a) {
|
|
174
|
+
S1ChordAngle ca(a);
|
|
175
|
+
return ca.PlusError(ca.GetS1AngleConstructorMaxError());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
static S1ChordAngle AddPointToPointError(S1ChordAngle ca) {
|
|
179
|
+
return ca.PlusError(ca.GetS2PointConstructorMaxError());
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
static S1ChordAngle AddPointToEdgeError(S1ChordAngle ca) {
|
|
183
|
+
return ca.PlusError(S2::GetUpdateMinDistanceMaxError(ca));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
S2Builder::S2Builder() {
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
S2Builder::S2Builder(const Options& options) {
|
|
190
|
+
Init(options);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
void S2Builder::Init(const Options& options) {
|
|
194
|
+
options_ = options;
|
|
195
|
+
const SnapFunction& snap_function = options.snap_function();
|
|
196
|
+
S1Angle snap_radius = snap_function.snap_radius();
|
|
197
|
+
S2_DCHECK_LE(snap_radius, SnapFunction::kMaxSnapRadius());
|
|
198
|
+
|
|
199
|
+
// Convert the snap radius to an S1ChordAngle. This is the "true snap
|
|
200
|
+
// radius" used when evaluating exact predicates (s2predicates.h).
|
|
201
|
+
site_snap_radius_ca_ = S1ChordAngle(snap_radius);
|
|
202
|
+
|
|
203
|
+
// When split_crossing_edges() is true, we need to use a larger snap radius
|
|
204
|
+
// for edges than for vertices to ensure that both edges are snapped to the
|
|
205
|
+
// edge intersection location. This is because the computed intersection
|
|
206
|
+
// point is not exact; it may be up to kIntersectionError away from its true
|
|
207
|
+
// position. The computed intersection point might then be snapped to some
|
|
208
|
+
// other vertex up to snap_radius away. So to ensure that both edges are
|
|
209
|
+
// snapped to a common vertex, we need to increase the snap radius for edges
|
|
210
|
+
// to at least the sum of these two values (calculated conservatively).
|
|
211
|
+
S1Angle edge_snap_radius = snap_radius;
|
|
212
|
+
if (!options.split_crossing_edges()) {
|
|
213
|
+
edge_snap_radius_ca_ = site_snap_radius_ca_;
|
|
214
|
+
} else {
|
|
215
|
+
edge_snap_radius += S2::kIntersectionError;
|
|
216
|
+
edge_snap_radius_ca_ = RoundUp(edge_snap_radius);
|
|
217
|
+
}
|
|
218
|
+
snapping_requested_ = (edge_snap_radius > S1Angle::Zero());
|
|
219
|
+
|
|
220
|
+
// Compute the maximum distance that a vertex can be separated from an
|
|
221
|
+
// edge while still affecting how that edge is snapped.
|
|
222
|
+
max_edge_deviation_ = snap_function.max_edge_deviation();
|
|
223
|
+
edge_site_query_radius_ca_ = S1ChordAngle(
|
|
224
|
+
max_edge_deviation_ + snap_function.min_edge_vertex_separation());
|
|
225
|
+
|
|
226
|
+
// Compute the maximum edge length such that even if both endpoints move by
|
|
227
|
+
// the maximum distance allowed (i.e., snap_radius), the center of the edge
|
|
228
|
+
// will still move by less than max_edge_deviation(). This saves us a lot
|
|
229
|
+
// of work since then we don't need to check the actual deviation.
|
|
230
|
+
min_edge_length_to_split_ca_ = S1ChordAngle::Radians(
|
|
231
|
+
2 * acos(sin(snap_radius) / sin(max_edge_deviation_)));
|
|
232
|
+
|
|
233
|
+
// If the condition below is violated, then AddExtraSites() needs to be
|
|
234
|
+
// modified to check that snapped edges pass on the same side of each "site
|
|
235
|
+
// to avoid" as the input edge. Currently it doesn't need to do this
|
|
236
|
+
// because the condition below guarantees that if the snapped edge passes on
|
|
237
|
+
// the wrong side of the site then it is also too close, which will cause a
|
|
238
|
+
// separation site to be added.
|
|
239
|
+
//
|
|
240
|
+
// Currently max_edge_deviation() is at most 1.1 * snap_radius(), whereas
|
|
241
|
+
// min_edge_vertex_separation() is at least 0.219 * snap_radius() (based on
|
|
242
|
+
// S2CellIdSnapFunction, which is currently the worst case).
|
|
243
|
+
S2_DCHECK_LE(snap_function.max_edge_deviation(),
|
|
244
|
+
snap_function.snap_radius() +
|
|
245
|
+
snap_function.min_edge_vertex_separation());
|
|
246
|
+
|
|
247
|
+
// To implement idempotency, we check whether the input geometry could
|
|
248
|
+
// possibly be the output of a previous S2Builder invocation. This involves
|
|
249
|
+
// testing whether any site/site or edge/site pairs are too close together.
|
|
250
|
+
// This is done using exact predicates, which require converting the minimum
|
|
251
|
+
// separation values to an S1ChordAngle.
|
|
252
|
+
min_site_separation_ = snap_function.min_vertex_separation();
|
|
253
|
+
min_site_separation_ca_ = S1ChordAngle(min_site_separation_);
|
|
254
|
+
min_edge_site_separation_ca_ =
|
|
255
|
+
S1ChordAngle(snap_function.min_edge_vertex_separation());
|
|
256
|
+
|
|
257
|
+
// This is an upper bound on the distance computed by S2ClosestPointQuery
|
|
258
|
+
// where the true distance might be less than min_edge_site_separation_ca_.
|
|
259
|
+
min_edge_site_separation_ca_limit_ =
|
|
260
|
+
AddPointToEdgeError(min_edge_site_separation_ca_);
|
|
261
|
+
|
|
262
|
+
// Compute the maximum possible distance between two sites whose Voronoi
|
|
263
|
+
// regions touch. (The maximum radius of each Voronoi region is
|
|
264
|
+
// edge_snap_radius_.) Then increase this bound to account for errors.
|
|
265
|
+
max_adjacent_site_separation_ca_ =
|
|
266
|
+
AddPointToPointError(RoundUp(2 * edge_snap_radius));
|
|
267
|
+
|
|
268
|
+
// Finally, we also precompute sin^2(edge_snap_radius), which is simply the
|
|
269
|
+
// squared distance between a vertex and an edge measured perpendicular to
|
|
270
|
+
// the plane containing the edge, and increase this value by the maximum
|
|
271
|
+
// error in the calculation to compare this distance against the bound.
|
|
272
|
+
double d = sin(edge_snap_radius);
|
|
273
|
+
edge_snap_radius_sin2_ = d * d;
|
|
274
|
+
edge_snap_radius_sin2_ += ((9.5 * d + 2.5 + 2 * sqrt(3)) * d +
|
|
275
|
+
9 * DBL_EPSILON) * DBL_EPSILON;
|
|
276
|
+
|
|
277
|
+
// Initialize the current label set.
|
|
278
|
+
label_set_id_ = label_set_lexicon_.EmptySetId();
|
|
279
|
+
label_set_modified_ = false;
|
|
280
|
+
|
|
281
|
+
// If snapping was requested, we try to determine whether the input geometry
|
|
282
|
+
// already meets the output requirements. This is necessary for
|
|
283
|
+
// idempotency, and can also save work. If we discover any reason that the
|
|
284
|
+
// input geometry needs to be modified, snapping_needed_ is set to true.
|
|
285
|
+
snapping_needed_ = false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
void S2Builder::clear_labels() {
|
|
289
|
+
label_set_.clear();
|
|
290
|
+
label_set_modified_ = true;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
void S2Builder::push_label(Label label) {
|
|
294
|
+
S2_DCHECK_GE(label, 0);
|
|
295
|
+
label_set_.push_back(label);
|
|
296
|
+
label_set_modified_ = true;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
void S2Builder::pop_label() {
|
|
300
|
+
label_set_.pop_back();
|
|
301
|
+
label_set_modified_ = true;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
void S2Builder::set_label(Label label) {
|
|
305
|
+
S2_DCHECK_GE(label, 0);
|
|
306
|
+
label_set_.resize(1);
|
|
307
|
+
label_set_[0] = label;
|
|
308
|
+
label_set_modified_ = true;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
bool S2Builder::IsFullPolygonUnspecified(const S2Builder::Graph& g,
|
|
312
|
+
S2Error* error) {
|
|
313
|
+
error->Init(S2Error::BUILDER_IS_FULL_PREDICATE_NOT_SPECIFIED,
|
|
314
|
+
"A degenerate polygon was found, but no predicate was specified "
|
|
315
|
+
"to determine whether the polygon is empty or full. Call "
|
|
316
|
+
"S2Builder::AddIsFullPolygonPredicate() to fix this problem.");
|
|
317
|
+
return false; // Assumes the polygon is empty.
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
S2Builder::IsFullPolygonPredicate S2Builder::IsFullPolygon(bool is_full) {
|
|
321
|
+
return [is_full](const S2Builder::Graph& g, S2Error* error) {
|
|
322
|
+
return is_full;
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
void S2Builder::StartLayer(unique_ptr<Layer> layer) {
|
|
327
|
+
layer_options_.push_back(layer->graph_options());
|
|
328
|
+
layer_begins_.push_back(input_edges_.size());
|
|
329
|
+
layer_is_full_polygon_predicates_.push_back(IsFullPolygon(false));
|
|
330
|
+
layers_.push_back(std::move(layer));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Input vertices are stored in a vector, with some removal of duplicates.
|
|
334
|
+
// Edges are represented as (VertexId, VertexId) pairs. All edges are stored
|
|
335
|
+
// in a single vector; each layer corresponds to a contiguous range.
|
|
336
|
+
|
|
337
|
+
S2Builder::InputVertexId S2Builder::AddVertex(const S2Point& v) {
|
|
338
|
+
// Remove duplicate vertices that follow the pattern AB, BC, CD. If we want
|
|
339
|
+
// to do anything more sophisticated, either use a ValueLexicon, or sort the
|
|
340
|
+
// vertices once they have all been added, remove duplicates, and update the
|
|
341
|
+
// edges.
|
|
342
|
+
if (input_vertices_.empty() || v != input_vertices_.back()) {
|
|
343
|
+
input_vertices_.push_back(v);
|
|
344
|
+
}
|
|
345
|
+
return input_vertices_.size() - 1;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
void S2Builder::AddEdge(const S2Point& v0, const S2Point& v1) {
|
|
349
|
+
S2_DCHECK(!layers_.empty()) << "Call StartLayer before adding any edges";
|
|
350
|
+
|
|
351
|
+
if (v0 == v1 && (layer_options_.back().degenerate_edges() ==
|
|
352
|
+
GraphOptions::DegenerateEdges::DISCARD)) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
InputVertexId j0 = AddVertex(v0);
|
|
356
|
+
InputVertexId j1 = AddVertex(v1);
|
|
357
|
+
input_edges_.push_back(InputEdge(j0, j1));
|
|
358
|
+
|
|
359
|
+
// If there are any labels, then attach them to this input edge.
|
|
360
|
+
if (label_set_modified_) {
|
|
361
|
+
if (label_set_ids_.empty()) {
|
|
362
|
+
// Populate the missing entries with empty label sets.
|
|
363
|
+
label_set_ids_.assign(input_edges_.size() - 1, label_set_id_);
|
|
364
|
+
}
|
|
365
|
+
label_set_id_ = label_set_lexicon_.Add(label_set_);
|
|
366
|
+
label_set_ids_.push_back(label_set_id_);
|
|
367
|
+
label_set_modified_ = false;
|
|
368
|
+
} else if (!label_set_ids_.empty()) {
|
|
369
|
+
label_set_ids_.push_back(label_set_id_);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
void S2Builder::AddPolyline(const S2Polyline& polyline) {
|
|
374
|
+
const int n = polyline.num_vertices();
|
|
375
|
+
for (int i = 1; i < n; ++i) {
|
|
376
|
+
AddEdge(polyline.vertex(i - 1), polyline.vertex(i));
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
void S2Builder::AddLoop(const S2Loop& loop) {
|
|
381
|
+
// Ignore loops that do not have a boundary.
|
|
382
|
+
if (loop.is_empty_or_full()) return;
|
|
383
|
+
|
|
384
|
+
// For loops that represent holes, we add the edge from vertex n-1 to vertex
|
|
385
|
+
// n-2 first. This is because these edges will be assembled into a
|
|
386
|
+
// clockwise loop, which will eventually be normalized in S2Polygon by
|
|
387
|
+
// calling S2Loop::Invert(). S2Loop::Invert() reverses the order of the
|
|
388
|
+
// vertices, so to end up with the original vertex order (0, 1, ..., n-1) we
|
|
389
|
+
// need to build a clockwise loop with vertex order (n-1, n-2, ..., 0).
|
|
390
|
+
// This is done by adding the edge (n-1, n-2) first, and then ensuring that
|
|
391
|
+
// Build() assembles loops starting from edges in the order they were added.
|
|
392
|
+
const int n = loop.num_vertices();
|
|
393
|
+
for (int i = 0; i < n; ++i) {
|
|
394
|
+
AddEdge(loop.oriented_vertex(i), loop.oriented_vertex(i + 1));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
void S2Builder::AddPolygon(const S2Polygon& polygon) {
|
|
399
|
+
for (int i = 0; i < polygon.num_loops(); ++i) {
|
|
400
|
+
AddLoop(*polygon.loop(i));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
void S2Builder::AddShape(const S2Shape& shape) {
|
|
405
|
+
for (int e = 0, n = shape.num_edges(); e < n; ++e) {
|
|
406
|
+
S2Shape::Edge edge = shape.edge(e);
|
|
407
|
+
AddEdge(edge.v0, edge.v1);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
void S2Builder::AddIsFullPolygonPredicate(IsFullPolygonPredicate predicate) {
|
|
412
|
+
layer_is_full_polygon_predicates_.back() = std::move(predicate);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
void S2Builder::ForceVertex(const S2Point& vertex) {
|
|
416
|
+
sites_.push_back(vertex);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// An S2Shape used to represent the entire collection of S2Builder input edges.
|
|
420
|
+
// Vertices are specified as indices into a vertex vector to save space.
|
|
421
|
+
namespace {
|
|
422
|
+
class VertexIdEdgeVectorShape final : public S2Shape {
|
|
423
|
+
public:
|
|
424
|
+
// Requires that "edges" is constant for the lifetime of this object.
|
|
425
|
+
VertexIdEdgeVectorShape(const vector<pair<int32, int32>>& edges,
|
|
426
|
+
const vector<S2Point>& vertices)
|
|
427
|
+
: edges_(edges), vertices_(vertices) {
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const S2Point& vertex0(int e) const { return vertex(edges_[e].first); }
|
|
431
|
+
const S2Point& vertex1(int e) const { return vertex(edges_[e].second); }
|
|
432
|
+
|
|
433
|
+
// S2Shape interface:
|
|
434
|
+
int num_edges() const override { return edges_.size(); }
|
|
435
|
+
Edge edge(int e) const override {
|
|
436
|
+
return Edge(vertices_[edges_[e].first], vertices_[edges_[e].second]);
|
|
437
|
+
}
|
|
438
|
+
int dimension() const override { return 1; }
|
|
439
|
+
ReferencePoint GetReferencePoint() const override {
|
|
440
|
+
return ReferencePoint::Contained(false);
|
|
441
|
+
}
|
|
442
|
+
int num_chains() const override { return edges_.size(); }
|
|
443
|
+
Chain chain(int i) const override { return Chain(i, 1); }
|
|
444
|
+
Edge chain_edge(int i, int j) const override { return edge(i); }
|
|
445
|
+
ChainPosition chain_position(int e) const override {
|
|
446
|
+
return ChainPosition(e, 0);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private:
|
|
450
|
+
const S2Point& vertex(int i) const { return vertices_[i]; }
|
|
451
|
+
|
|
452
|
+
const vector<std::pair<int32, int32>>& edges_;
|
|
453
|
+
const vector<S2Point>& vertices_;
|
|
454
|
+
};
|
|
455
|
+
} // namespace
|
|
456
|
+
|
|
457
|
+
bool S2Builder::Build(S2Error* error) {
|
|
458
|
+
// S2_CHECK rather than S2_DCHECK because this is friendlier than crashing on the
|
|
459
|
+
// "error->ok()" call below. It would be easy to allow (error == nullptr)
|
|
460
|
+
// by declaring a local "tmp_error", but it seems better to make clients
|
|
461
|
+
// think about error handling.
|
|
462
|
+
S2_CHECK(error != nullptr);
|
|
463
|
+
error->Clear();
|
|
464
|
+
error_ = error;
|
|
465
|
+
|
|
466
|
+
// Mark the end of the last layer.
|
|
467
|
+
layer_begins_.push_back(input_edges_.size());
|
|
468
|
+
|
|
469
|
+
// See the algorithm overview at the top of this file.
|
|
470
|
+
if (snapping_requested_ && !options_.idempotent()) {
|
|
471
|
+
snapping_needed_ = true;
|
|
472
|
+
}
|
|
473
|
+
ChooseSites();
|
|
474
|
+
BuildLayers();
|
|
475
|
+
Reset();
|
|
476
|
+
return error->ok();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
void S2Builder::Reset() {
|
|
480
|
+
input_vertices_.clear();
|
|
481
|
+
input_edges_.clear();
|
|
482
|
+
layers_.clear();
|
|
483
|
+
layer_options_.clear();
|
|
484
|
+
layer_begins_.clear();
|
|
485
|
+
layer_is_full_polygon_predicates_.clear();
|
|
486
|
+
label_set_ids_.clear();
|
|
487
|
+
label_set_lexicon_.Clear();
|
|
488
|
+
label_set_.clear();
|
|
489
|
+
label_set_modified_ = false;
|
|
490
|
+
sites_.clear();
|
|
491
|
+
edge_sites_.clear();
|
|
492
|
+
snapping_needed_ = false;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
void S2Builder::ChooseSites() {
|
|
496
|
+
if (input_vertices_.empty()) return;
|
|
497
|
+
|
|
498
|
+
MutableS2ShapeIndex input_edge_index;
|
|
499
|
+
input_edge_index.Add(make_unique<VertexIdEdgeVectorShape>(
|
|
500
|
+
input_edges_, input_vertices_));
|
|
501
|
+
if (options_.split_crossing_edges()) {
|
|
502
|
+
AddEdgeCrossings(input_edge_index);
|
|
503
|
+
}
|
|
504
|
+
if (snapping_requested_) {
|
|
505
|
+
S2PointIndex<SiteId> site_index;
|
|
506
|
+
AddForcedSites(&site_index);
|
|
507
|
+
ChooseInitialSites(&site_index);
|
|
508
|
+
CollectSiteEdges(site_index);
|
|
509
|
+
}
|
|
510
|
+
if (snapping_needed_) {
|
|
511
|
+
AddExtraSites(input_edge_index);
|
|
512
|
+
} else {
|
|
513
|
+
CopyInputEdges();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
void S2Builder::CopyInputEdges() {
|
|
518
|
+
// Sort the input vertices, discard duplicates, and update the input edges
|
|
519
|
+
// to refer to the pruned vertex list. (We sort in the same order used by
|
|
520
|
+
// ChooseInitialSites() to avoid inconsistencies in tests.)
|
|
521
|
+
vector<InputVertexKey> sorted = SortInputVertices();
|
|
522
|
+
vector<InputVertexId> vmap(input_vertices_.size());
|
|
523
|
+
sites_.clear();
|
|
524
|
+
sites_.reserve(input_vertices_.size());
|
|
525
|
+
for (int in = 0; in < sorted.size(); ) {
|
|
526
|
+
const S2Point& site = input_vertices_[sorted[in].second];
|
|
527
|
+
vmap[sorted[in].second] = sites_.size();
|
|
528
|
+
while (++in < sorted.size() && input_vertices_[sorted[in].second] == site) {
|
|
529
|
+
vmap[sorted[in].second] = sites_.size();
|
|
530
|
+
}
|
|
531
|
+
sites_.push_back(site);
|
|
532
|
+
}
|
|
533
|
+
input_vertices_ = sites_;
|
|
534
|
+
for (InputEdge& e : input_edges_) {
|
|
535
|
+
e.first = vmap[e.first];
|
|
536
|
+
e.second = vmap[e.second];
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
vector<S2Builder::InputVertexKey> S2Builder::SortInputVertices() {
|
|
541
|
+
// Sort all the input vertices in the order that we wish to consider them as
|
|
542
|
+
// candidate Voronoi sites. Any sort order will produce correct output, so
|
|
543
|
+
// we have complete flexibility in choosing the sort key. We could even
|
|
544
|
+
// leave them unsorted, although this would have the disadvantage that
|
|
545
|
+
// changing the order of the input edges could cause S2Builder to snap to a
|
|
546
|
+
// different set of Voronoi sites.
|
|
547
|
+
//
|
|
548
|
+
// We have chosen to sort them primarily by S2CellId since this improves the
|
|
549
|
+
// performance of many S2Builder phases (due to better spatial locality).
|
|
550
|
+
// It also allows the possibility of replacing the current S2PointIndex
|
|
551
|
+
// approach with a more efficient recursive divide-and-conquer algorithm.
|
|
552
|
+
//
|
|
553
|
+
// However, sorting by leaf S2CellId alone has two small disadvantages in
|
|
554
|
+
// the case where the candidate sites are densely spaced relative to the
|
|
555
|
+
// snap radius (e.g., when using the IdentitySnapFunction, or when snapping
|
|
556
|
+
// to E6/E7 near the poles, or snapping to S2CellId/E6/E7 using a snap
|
|
557
|
+
// radius larger than the minimum value required):
|
|
558
|
+
//
|
|
559
|
+
// - First, it tends to bias the Voronoi site locations towards points that
|
|
560
|
+
// are earlier on the S2CellId Hilbert curve. For example, suppose that
|
|
561
|
+
// there are two parallel rows of input vertices on opposite sides of the
|
|
562
|
+
// edge between two large S2Cells, and the rows are separated by less
|
|
563
|
+
// than the snap radius. Then only vertices from the cell with the
|
|
564
|
+
// smaller S2CellId are selected, because they are considered first and
|
|
565
|
+
// prevent us from selecting the sites from the other cell (because they
|
|
566
|
+
// are closer than "snap_radius" to an existing site).
|
|
567
|
+
//
|
|
568
|
+
// - Second, it tends to choose more Voronoi sites than necessary, because
|
|
569
|
+
// at each step we choose the first site along the Hilbert curve that is
|
|
570
|
+
// at least "snap_radius" away from all previously selected sites. This
|
|
571
|
+
// tends to yield sites whose "coverage discs" overlap quite a bit,
|
|
572
|
+
// whereas it would be better to cover all the input vertices with a
|
|
573
|
+
// smaller set of coverage discs that don't overlap as much. (This is
|
|
574
|
+
// the "geometric set cover problem", which is NP-hard.)
|
|
575
|
+
//
|
|
576
|
+
// It is not worth going to much trouble to fix these problems, because they
|
|
577
|
+
// really aren't that important (and don't affect the guarantees made by the
|
|
578
|
+
// algorithm), but here are a couple of heuristics that might help:
|
|
579
|
+
//
|
|
580
|
+
// 1. Sort the input vertices by S2CellId at a coarse level (down to cells
|
|
581
|
+
// that are O(snap_radius) in size), and then sort by a fingerprint of the
|
|
582
|
+
// S2Point coordinates (i.e., quasi-randomly). This would retain most of
|
|
583
|
+
// the advantages of S2CellId sorting, but makes it more likely that we will
|
|
584
|
+
// select sites that are further apart.
|
|
585
|
+
//
|
|
586
|
+
// 2. Rather than choosing the first uncovered input vertex and snapping it
|
|
587
|
+
// to obtain the next Voronoi site, instead look ahead through following
|
|
588
|
+
// candidates in S2CellId order and choose the furthest candidate whose
|
|
589
|
+
// snapped location covers all previous uncovered input vertices.
|
|
590
|
+
//
|
|
591
|
+
// TODO(ericv): Experiment with these approaches.
|
|
592
|
+
|
|
593
|
+
vector<InputVertexKey> keys;
|
|
594
|
+
keys.reserve(input_vertices_.size());
|
|
595
|
+
for (InputVertexId i = 0; i < input_vertices_.size(); ++i) {
|
|
596
|
+
keys.push_back(InputVertexKey(S2CellId(input_vertices_[i]), i));
|
|
597
|
+
}
|
|
598
|
+
std::sort(keys.begin(), keys.end(),
|
|
599
|
+
[this](const InputVertexKey& a, const InputVertexKey& b) {
|
|
600
|
+
if (a.first < b.first) return true;
|
|
601
|
+
if (b.first < a.first) return false;
|
|
602
|
+
return input_vertices_[a.second] < input_vertices_[b.second];
|
|
603
|
+
});
|
|
604
|
+
return keys;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Check all edge pairs for crossings, and add the corresponding intersection
|
|
608
|
+
// points to input_vertices_. (The intersection points will be snapped and
|
|
609
|
+
// merged with the other vertices during site selection.)
|
|
610
|
+
void S2Builder::AddEdgeCrossings(const MutableS2ShapeIndex& input_edge_index) {
|
|
611
|
+
// We need to build a list of intersections and add them afterwards so that
|
|
612
|
+
// we don't reallocate vertices_ during the VisitCrossings() call.
|
|
613
|
+
vector<S2Point> new_vertices;
|
|
614
|
+
s2shapeutil::VisitCrossingEdgePairs(
|
|
615
|
+
input_edge_index, s2shapeutil::CrossingType::INTERIOR,
|
|
616
|
+
[&new_vertices](const s2shapeutil::ShapeEdge& a,
|
|
617
|
+
const s2shapeutil::ShapeEdge& b, bool) {
|
|
618
|
+
new_vertices.push_back(
|
|
619
|
+
S2::GetIntersection(a.v0(), a.v1(), b.v0(), b.v1()));
|
|
620
|
+
return true; // Continue visiting.
|
|
621
|
+
});
|
|
622
|
+
if (!new_vertices.empty()) {
|
|
623
|
+
snapping_needed_ = true;
|
|
624
|
+
for (const auto& vertex : new_vertices) AddVertex(vertex);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
void S2Builder::AddForcedSites(S2PointIndex<SiteId>* site_index) {
|
|
629
|
+
// Sort the forced sites and remove duplicates.
|
|
630
|
+
std::sort(sites_.begin(), sites_.end());
|
|
631
|
+
sites_.erase(std::unique(sites_.begin(), sites_.end()), sites_.end());
|
|
632
|
+
// Add the forced sites to the index.
|
|
633
|
+
for (SiteId id = 0; id < sites_.size(); ++id) {
|
|
634
|
+
site_index->Add(sites_[id], id);
|
|
635
|
+
}
|
|
636
|
+
num_forced_sites_ = sites_.size();
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
void S2Builder::ChooseInitialSites(S2PointIndex<SiteId>* site_index) {
|
|
640
|
+
// Find all points whose distance is <= min_site_separation_ca_.
|
|
641
|
+
S2ClosestPointQueryOptions options;
|
|
642
|
+
options.set_conservative_max_distance(min_site_separation_ca_);
|
|
643
|
+
S2ClosestPointQuery<SiteId> site_query(site_index, options);
|
|
644
|
+
vector<S2ClosestPointQuery<SiteId>::Result> results;
|
|
645
|
+
|
|
646
|
+
// Apply the snap_function() to each input vertex, then check whether any
|
|
647
|
+
// existing site is closer than min_vertex_separation(). If not, then add a
|
|
648
|
+
// new site.
|
|
649
|
+
//
|
|
650
|
+
// NOTE(ericv): There are actually two reasonable algorithms, which we call
|
|
651
|
+
// "snap first" (the one above) and "snap last". The latter checks for each
|
|
652
|
+
// input vertex whether any existing site is closer than snap_radius(), and
|
|
653
|
+
// only then applies the snap_function() and adds a new site. "Snap last"
|
|
654
|
+
// can yield slightly fewer sites in some cases, but it is also more
|
|
655
|
+
// expensive and can produce surprising results. For example, if you snap
|
|
656
|
+
// the polyline "0:0, 0:0.7" using IntLatLngSnapFunction(0), the result is
|
|
657
|
+
// "0:0, 0:0" rather than the expected "0:0, 0:1", because the snap radius
|
|
658
|
+
// is approximately sqrt(2) degrees and therefore it is legal to snap both
|
|
659
|
+
// input points to "0:0". "Snap first" produces "0:0, 0:1" as expected.
|
|
660
|
+
for (const InputVertexKey& key : SortInputVertices()) {
|
|
661
|
+
const S2Point& vertex = input_vertices_[key.second];
|
|
662
|
+
S2Point site = SnapSite(vertex);
|
|
663
|
+
// If any vertex moves when snapped, the output cannot be idempotent.
|
|
664
|
+
snapping_needed_ = snapping_needed_ || site != vertex;
|
|
665
|
+
|
|
666
|
+
// FindClosestPoints() measures distances conservatively, so we need to
|
|
667
|
+
// recheck the distances using exact predicates.
|
|
668
|
+
//
|
|
669
|
+
// NOTE(ericv): When the snap radius is large compared to the average
|
|
670
|
+
// vertex spacing, we could possibly avoid the call the FindClosestPoints
|
|
671
|
+
// by checking whether sites_.back() is close enough.
|
|
672
|
+
S2ClosestPointQueryPointTarget target(site);
|
|
673
|
+
site_query.FindClosestPoints(&target, &results);
|
|
674
|
+
bool add_site = true;
|
|
675
|
+
for (const auto& result : results) {
|
|
676
|
+
if (s2pred::CompareDistance(site, result.point(),
|
|
677
|
+
min_site_separation_ca_) <= 0) {
|
|
678
|
+
add_site = false;
|
|
679
|
+
// This pair of sites is too close. If the sites are distinct, then
|
|
680
|
+
// the output cannot be idempotent.
|
|
681
|
+
snapping_needed_ = snapping_needed_ || site != result.point();
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
if (add_site) {
|
|
685
|
+
site_index->Add(site, sites_.size());
|
|
686
|
+
sites_.push_back(site);
|
|
687
|
+
site_query.ReInit();
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
S2Point S2Builder::SnapSite(const S2Point& point) const {
|
|
693
|
+
if (!snapping_requested_) return point;
|
|
694
|
+
S2Point site = options_.snap_function().SnapPoint(point);
|
|
695
|
+
S1ChordAngle dist_moved(site, point);
|
|
696
|
+
if (dist_moved > site_snap_radius_ca_) {
|
|
697
|
+
error_->Init(S2Error::BUILDER_SNAP_RADIUS_TOO_SMALL,
|
|
698
|
+
"Snap function moved vertex (%.15g, %.15g, %.15g) "
|
|
699
|
+
"by %.15g, which is more than the specified snap "
|
|
700
|
+
"radius of %.15g", point.x(), point.y(), point.z(),
|
|
701
|
+
dist_moved.ToAngle().radians(),
|
|
702
|
+
site_snap_radius_ca_.ToAngle().radians());
|
|
703
|
+
}
|
|
704
|
+
return site;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// For each edge, find all sites within min_edge_site_query_radius_ca_ and
|
|
708
|
+
// store them in edge_sites_. Also, to implement idempotency this method also
|
|
709
|
+
// checks whether the input vertices and edges may already satisfy the output
|
|
710
|
+
// criteria. If any problems are found then snapping_needed_ is set to true.
|
|
711
|
+
void S2Builder::CollectSiteEdges(const S2PointIndex<SiteId>& site_index) {
|
|
712
|
+
// Find all points whose distance is <= edge_site_query_radius_ca_.
|
|
713
|
+
S2ClosestPointQueryOptions options;
|
|
714
|
+
options.set_conservative_max_distance(edge_site_query_radius_ca_);
|
|
715
|
+
S2ClosestPointQuery<SiteId> site_query(&site_index, options);
|
|
716
|
+
vector<S2ClosestPointQuery<SiteId>::Result> results;
|
|
717
|
+
edge_sites_.resize(input_edges_.size());
|
|
718
|
+
for (InputEdgeId e = 0; e < input_edges_.size(); ++e) {
|
|
719
|
+
const InputEdge& edge = input_edges_[e];
|
|
720
|
+
const S2Point& v0 = input_vertices_[edge.first];
|
|
721
|
+
const S2Point& v1 = input_vertices_[edge.second];
|
|
722
|
+
if (s2builder_verbose) {
|
|
723
|
+
std::cout << "S2Polyline: " << s2textformat::ToString(v0)
|
|
724
|
+
<< ", " << s2textformat::ToString(v1) << "\n";
|
|
725
|
+
}
|
|
726
|
+
S2ClosestPointQueryEdgeTarget target(v0, v1);
|
|
727
|
+
site_query.FindClosestPoints(&target, &results);
|
|
728
|
+
auto* sites = &edge_sites_[e];
|
|
729
|
+
sites->reserve(results.size());
|
|
730
|
+
for (const auto& result : results) {
|
|
731
|
+
sites->push_back(result.data());
|
|
732
|
+
if (!snapping_needed_ &&
|
|
733
|
+
result.distance() < min_edge_site_separation_ca_limit_ &&
|
|
734
|
+
result.point() != v0 && result.point() != v1 &&
|
|
735
|
+
s2pred::CompareEdgeDistance(result.point(), v0, v1,
|
|
736
|
+
min_edge_site_separation_ca_) < 0) {
|
|
737
|
+
snapping_needed_ = true;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
SortSitesByDistance(v0, sites);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
void S2Builder::SortSitesByDistance(const S2Point& x,
|
|
745
|
+
compact_array<SiteId>* sites) const {
|
|
746
|
+
// Sort sites in increasing order of distance to X.
|
|
747
|
+
std::sort(sites->begin(), sites->end(),
|
|
748
|
+
[&x, this](SiteId i, SiteId j) {
|
|
749
|
+
return s2pred::CompareDistances(x, sites_[i], sites_[j]) < 0;
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// There are two situatons where we need to add extra Voronoi sites in order
|
|
754
|
+
// to ensure that the snapped edges meet the output requirements:
|
|
755
|
+
//
|
|
756
|
+
// (1) If a snapped edge deviates from its input edge by more than
|
|
757
|
+
// max_edge_deviation(), we add a new site on the input edge near the
|
|
758
|
+
// middle of the snapped edge. This causes the snapped edge to split
|
|
759
|
+
// into two pieces, so that it follows the input edge more closely.
|
|
760
|
+
//
|
|
761
|
+
// (2) If a snapped edge is closer than min_edge_vertex_separation() to any
|
|
762
|
+
// nearby site (the "site to avoid"), then we add a new site (the
|
|
763
|
+
// "separation site") on the input edge near the site to avoid. This
|
|
764
|
+
// causes the snapped edge to follow the input edge more closely and is
|
|
765
|
+
// guaranteed to increase the separation to the required distance.
|
|
766
|
+
//
|
|
767
|
+
// We check these conditions by snapping all the input edges to a chain of
|
|
768
|
+
// Voronoi sites and then testing each edge in the chain. If a site needs to
|
|
769
|
+
// be added, we mark all nearby edges for re-snapping.
|
|
770
|
+
void S2Builder::AddExtraSites(const MutableS2ShapeIndex& input_edge_index) {
|
|
771
|
+
// When options_.split_crossing_edges() is true, this function may be called
|
|
772
|
+
// even when site_snap_radius_ca_ == 0 (because edge_snap_radius_ca_ > 0).
|
|
773
|
+
// However neither of the conditions above apply in that case.
|
|
774
|
+
if (site_snap_radius_ca_ == S1ChordAngle::Zero()) return;
|
|
775
|
+
|
|
776
|
+
vector<SiteId> chain; // Temporary
|
|
777
|
+
vector<InputEdgeId> snap_queue;
|
|
778
|
+
for (InputEdgeId max_e = 0; max_e < input_edges_.size(); ++max_e) {
|
|
779
|
+
snap_queue.push_back(max_e);
|
|
780
|
+
while (!snap_queue.empty()) {
|
|
781
|
+
InputEdgeId e = snap_queue.back();
|
|
782
|
+
snap_queue.pop_back();
|
|
783
|
+
SnapEdge(e, &chain);
|
|
784
|
+
// We could save the snapped chain here in a snapped_chains_ vector, to
|
|
785
|
+
// avoid resnapping it in AddSnappedEdges() below, however currently
|
|
786
|
+
// SnapEdge only accounts for less than 5% of the runtime.
|
|
787
|
+
MaybeAddExtraSites(e, max_e, chain, input_edge_index, &snap_queue);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
void S2Builder::MaybeAddExtraSites(InputEdgeId edge_id,
|
|
793
|
+
InputEdgeId max_edge_id,
|
|
794
|
+
const vector<SiteId>& chain,
|
|
795
|
+
const MutableS2ShapeIndex& input_edge_index,
|
|
796
|
+
vector<InputEdgeId>* snap_queue) {
|
|
797
|
+
// The snapped chain is always a *subsequence* of the nearby sites
|
|
798
|
+
// (edge_sites_), so we walk through the two arrays in parallel looking for
|
|
799
|
+
// sites that weren't snapped. We also keep track of the current snapped
|
|
800
|
+
// edge, since it is the only edge that can be too close.
|
|
801
|
+
int i = 0;
|
|
802
|
+
for (SiteId id : edge_sites_[edge_id]) {
|
|
803
|
+
if (id == chain[i]) {
|
|
804
|
+
if (++i == chain.size()) break;
|
|
805
|
+
// Check whether this snapped edge deviates too far from its original
|
|
806
|
+
// position. If so, we split the edge by adding an extra site.
|
|
807
|
+
const S2Point& v0 = sites_[chain[i - 1]];
|
|
808
|
+
const S2Point& v1 = sites_[chain[i]];
|
|
809
|
+
if (S1ChordAngle(v0, v1) < min_edge_length_to_split_ca_) continue;
|
|
810
|
+
|
|
811
|
+
const InputEdge& edge = input_edges_[edge_id];
|
|
812
|
+
const S2Point& a0 = input_vertices_[edge.first];
|
|
813
|
+
const S2Point& a1 = input_vertices_[edge.second];
|
|
814
|
+
if (!S2::IsEdgeBNearEdgeA(a0, a1, v0, v1, max_edge_deviation_)) {
|
|
815
|
+
// Add a new site on the input edge, positioned so that it splits the
|
|
816
|
+
// snapped edge into two approximately equal pieces. Then we find all
|
|
817
|
+
// the edges near the new site (including this one) and add them to
|
|
818
|
+
// the snap queue.
|
|
819
|
+
//
|
|
820
|
+
// Note that with large snap radii, it is possible that the snapped
|
|
821
|
+
// edge wraps around the sphere the "wrong way". To handle this we
|
|
822
|
+
// find the preferred split location by projecting both endpoints onto
|
|
823
|
+
// the input edge and taking their midpoint.
|
|
824
|
+
S2Point mid = (S2::Project(v0, a0, a1) +
|
|
825
|
+
S2::Project(v1, a0, a1)).Normalize();
|
|
826
|
+
S2Point new_site = GetSeparationSite(mid, v0, v1, edge_id);
|
|
827
|
+
AddExtraSite(new_site, max_edge_id, input_edge_index, snap_queue);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
} else if (i > 0 && id >= num_forced_sites_) {
|
|
831
|
+
// Check whether this "site to avoid" is closer to the snapped edge than
|
|
832
|
+
// min_edge_vertex_separation(). Note that this is the only edge of the
|
|
833
|
+
// chain that can be too close because its vertices must span the point
|
|
834
|
+
// where "site_to_avoid" projects onto the input edge XY (this claim
|
|
835
|
+
// relies on the fact that all sites are separated by at least the snap
|
|
836
|
+
// radius). We don't try to avoid sites added using ForceVertex()
|
|
837
|
+
// because we don't guarantee any minimum separation from such sites.
|
|
838
|
+
const S2Point& site_to_avoid = sites_[id];
|
|
839
|
+
const S2Point& v0 = sites_[chain[i - 1]];
|
|
840
|
+
const S2Point& v1 = sites_[chain[i]];
|
|
841
|
+
if (s2pred::CompareEdgeDistance(
|
|
842
|
+
site_to_avoid, v0, v1, min_edge_site_separation_ca_) < 0) {
|
|
843
|
+
// A snapped edge can only approach a site too closely when there are
|
|
844
|
+
// no sites near the input edge near that point. We fix that by
|
|
845
|
+
// adding a new site along the input edge (a "separation site"), then
|
|
846
|
+
// we find all the edges near the new site (including this one) and
|
|
847
|
+
// add them to the snap queue.
|
|
848
|
+
S2Point new_site = GetSeparationSite(site_to_avoid, v0, v1, edge_id);
|
|
849
|
+
S2_DCHECK_NE(site_to_avoid, new_site);
|
|
850
|
+
AddExtraSite(new_site, max_edge_id, input_edge_index, snap_queue);
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// Adds a new site, then updates "edge_sites"_ for all edges near the new site
|
|
858
|
+
// and adds them to "snap_queue" for resnapping (unless their edge id exceeds
|
|
859
|
+
// "max_edge_id", since those edges have not been snapped the first time yet).
|
|
860
|
+
void S2Builder::AddExtraSite(const S2Point& new_site,
|
|
861
|
+
InputEdgeId max_edge_id,
|
|
862
|
+
const MutableS2ShapeIndex& input_edge_index,
|
|
863
|
+
vector<InputEdgeId>* snap_queue) {
|
|
864
|
+
SiteId new_site_id = sites_.size();
|
|
865
|
+
sites_.push_back(new_site);
|
|
866
|
+
// Find all edges whose distance is <= edge_site_query_radius_ca_.
|
|
867
|
+
S2ClosestEdgeQuery::Options options;
|
|
868
|
+
options.set_conservative_max_distance(edge_site_query_radius_ca_);
|
|
869
|
+
options.set_include_interiors(false);
|
|
870
|
+
S2ClosestEdgeQuery query(&input_edge_index, options);
|
|
871
|
+
S2ClosestEdgeQuery::PointTarget target(new_site);
|
|
872
|
+
for (const auto& result : query.FindClosestEdges(&target)) {
|
|
873
|
+
InputEdgeId e = result.edge_id();
|
|
874
|
+
auto* site_ids = &edge_sites_[e];
|
|
875
|
+
site_ids->push_back(new_site_id);
|
|
876
|
+
SortSitesByDistance(input_vertices_[input_edges_[e].first], site_ids);
|
|
877
|
+
if (e <= max_edge_id) snap_queue->push_back(e);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
S2Point S2Builder::GetSeparationSite(const S2Point& site_to_avoid,
|
|
882
|
+
const S2Point& v0, const S2Point& v1,
|
|
883
|
+
InputEdgeId input_edge_id) const {
|
|
884
|
+
// Define the "coverage disc" of a site S to be the disc centered at S with
|
|
885
|
+
// radius "snap_radius". Similarly, define the "coverage interval" of S for
|
|
886
|
+
// an edge XY to be the intersection of XY with the coverage disc of S. The
|
|
887
|
+
// SnapFunction implementations guarantee that the only way that a snapped
|
|
888
|
+
// edge can be closer than min_edge_vertex_separation() to a non-snapped
|
|
889
|
+
// site (i.e., site_to_avoid) if is there is a gap in the coverage of XY
|
|
890
|
+
// near this site. We can fix this problem simply by adding a new site to
|
|
891
|
+
// fill this gap, located as closely as possible to the site to avoid.
|
|
892
|
+
//
|
|
893
|
+
// To calculate the coverage gap, we look at the two snapped sites on
|
|
894
|
+
// either side of site_to_avoid, and find the endpoints of their coverage
|
|
895
|
+
// intervals. The we place a new site in the gap, located as closely as
|
|
896
|
+
// possible to the site to avoid. Note that the new site may move when it
|
|
897
|
+
// is snapped by the snap_function, but it is guaranteed not to move by
|
|
898
|
+
// more than snap_radius and therefore its coverage interval will still
|
|
899
|
+
// intersect the gap.
|
|
900
|
+
const InputEdge& edge = input_edges_[input_edge_id];
|
|
901
|
+
const S2Point& x = input_vertices_[edge.first];
|
|
902
|
+
const S2Point& y = input_vertices_[edge.second];
|
|
903
|
+
Vector3_d xy_dir = y - x;
|
|
904
|
+
S2Point n = S2::RobustCrossProd(x, y);
|
|
905
|
+
S2Point new_site = S2::Project(site_to_avoid, x, y, n);
|
|
906
|
+
S2Point gap_min = GetCoverageEndpoint(v0, x, y, n);
|
|
907
|
+
S2Point gap_max = GetCoverageEndpoint(v1, y, x, -n);
|
|
908
|
+
if ((new_site - gap_min).DotProd(xy_dir) < 0) {
|
|
909
|
+
new_site = gap_min;
|
|
910
|
+
} else if ((gap_max - new_site).DotProd(xy_dir) < 0) {
|
|
911
|
+
new_site = gap_max;
|
|
912
|
+
}
|
|
913
|
+
new_site = SnapSite(new_site);
|
|
914
|
+
S2_DCHECK_NE(v0, new_site);
|
|
915
|
+
S2_DCHECK_NE(v1, new_site);
|
|
916
|
+
return new_site;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// Given a site P and an edge XY with normal N, intersect XY with the disc of
|
|
920
|
+
// radius snap_radius() around P, and return the intersection point that is
|
|
921
|
+
// further along the edge XY toward Y.
|
|
922
|
+
S2Point S2Builder::GetCoverageEndpoint(const S2Point& p, const S2Point& x,
|
|
923
|
+
const S2Point& y, const S2Point& n)
|
|
924
|
+
const {
|
|
925
|
+
// Consider the plane perpendicular to P that cuts off a spherical cap of
|
|
926
|
+
// radius snap_radius(). This plane intersects the plane through the edge
|
|
927
|
+
// XY (perpendicular to N) along a line, and that line intersects the unit
|
|
928
|
+
// sphere at two points Q and R, and we want to return the point R that is
|
|
929
|
+
// further along the edge XY toward Y.
|
|
930
|
+
//
|
|
931
|
+
// Let M be the midpoint of QR. This is the point along QR that is closest
|
|
932
|
+
// to P. We can now express R as the sum of two perpendicular vectors OM
|
|
933
|
+
// and MR in the plane XY. Vector MR is in the direction N x P, while
|
|
934
|
+
// vector OM is in the direction (N x P) x N, where N = X x Y.
|
|
935
|
+
//
|
|
936
|
+
// The length of OM can be found using the Pythagorean theorem on triangle
|
|
937
|
+
// OPM, and the length of MR can be found using the Pythagorean theorem on
|
|
938
|
+
// triangle OMR.
|
|
939
|
+
//
|
|
940
|
+
// In the calculations below, we save some work by scaling all the vectors
|
|
941
|
+
// by n.CrossProd(p).Norm2(), and normalizing at the end.
|
|
942
|
+
double n2 = n.Norm2();
|
|
943
|
+
double nDp = n.DotProd(p);
|
|
944
|
+
S2Point nXp = n.CrossProd(p);
|
|
945
|
+
S2Point nXpXn = n2 * p - nDp * n;
|
|
946
|
+
Vector3_d om = sqrt(1 - edge_snap_radius_sin2_) * nXpXn;
|
|
947
|
+
double mr2 = edge_snap_radius_sin2_ * n2 - nDp * nDp;
|
|
948
|
+
|
|
949
|
+
// MR is constructed so that it points toward Y (rather than X).
|
|
950
|
+
Vector3_d mr = sqrt(max(0.0, mr2)) * nXp;
|
|
951
|
+
return (om + mr).Normalize();
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
void S2Builder::SnapEdge(InputEdgeId e, vector<SiteId>* chain) const {
|
|
955
|
+
chain->clear();
|
|
956
|
+
const InputEdge& edge = input_edges_[e];
|
|
957
|
+
if (!snapping_needed_) {
|
|
958
|
+
chain->push_back(edge.first);
|
|
959
|
+
chain->push_back(edge.second);
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
const S2Point& x = input_vertices_[edge.first];
|
|
964
|
+
const S2Point& y = input_vertices_[edge.second];
|
|
965
|
+
|
|
966
|
+
// Optimization: if there is only one nearby site, return.
|
|
967
|
+
// Optimization: if there are exactly two nearby sites, and one is close
|
|
968
|
+
// enough to each vertex, then return.
|
|
969
|
+
|
|
970
|
+
// Now iterate through the sites. We keep track of the sequence of sites
|
|
971
|
+
// that are visited.
|
|
972
|
+
const auto& candidates = edge_sites_[e];
|
|
973
|
+
for (SiteId site_id : candidates) {
|
|
974
|
+
const S2Point& c = sites_[site_id];
|
|
975
|
+
// Skip any sites that are too far away. (There will be some of these,
|
|
976
|
+
// because we also keep track of "sites to avoid".) Note that some sites
|
|
977
|
+
// may be close enough to the line containing the edge, but not to the
|
|
978
|
+
// edge itself, so we can just use the dot product with the edge normal.
|
|
979
|
+
if (s2pred::CompareEdgeDistance(c, x, y, edge_snap_radius_ca_) > 0) {
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
// Check whether the new site C excludes the previous site B. If so,
|
|
983
|
+
// repeat with the previous site, and so on.
|
|
984
|
+
bool add_site_c = true;
|
|
985
|
+
for (; !chain->empty(); chain->pop_back()) {
|
|
986
|
+
S2Point b = sites_[chain->back()];
|
|
987
|
+
|
|
988
|
+
// First, check whether B and C are so far apart that their clipped
|
|
989
|
+
// Voronoi regions can't intersect.
|
|
990
|
+
S1ChordAngle bc(b, c);
|
|
991
|
+
if (bc >= max_adjacent_site_separation_ca_) break;
|
|
992
|
+
|
|
993
|
+
// Otherwise, we want to check whether site C prevents the Voronoi
|
|
994
|
+
// region of B from intersecting XY, or vice versa. This can be
|
|
995
|
+
// determined by computing the "coverage interval" (the segment of XY
|
|
996
|
+
// intersected by the coverage disc of radius snap_radius) for each
|
|
997
|
+
// site. If the coverage interval of one site contains the coverage
|
|
998
|
+
// interval of the other, then the contained site can be excluded.
|
|
999
|
+
s2pred::Excluded result = s2pred::GetVoronoiSiteExclusion(
|
|
1000
|
+
b, c, x, y, edge_snap_radius_ca_);
|
|
1001
|
+
if (result == s2pred::Excluded::FIRST) continue; // Site B excluded by C
|
|
1002
|
+
if (result == s2pred::Excluded::SECOND) {
|
|
1003
|
+
add_site_c = false; // Site C is excluded by B.
|
|
1004
|
+
break;
|
|
1005
|
+
}
|
|
1006
|
+
S2_DCHECK_EQ(s2pred::Excluded::NEITHER, result);
|
|
1007
|
+
|
|
1008
|
+
// Otherwise check whether the previous site A is close enough to B and
|
|
1009
|
+
// C that it might further clip the Voronoi region of B.
|
|
1010
|
+
if (chain->size() < 2) break;
|
|
1011
|
+
S2Point a = sites_[chain->end()[-2]];
|
|
1012
|
+
S1ChordAngle ac(a, c);
|
|
1013
|
+
if (ac >= max_adjacent_site_separation_ca_) break;
|
|
1014
|
+
|
|
1015
|
+
// If triangles ABC and XYB have the same orientation, the circumcenter
|
|
1016
|
+
// Z of ABC is guaranteed to be on the same side of XY as B.
|
|
1017
|
+
int xyb = s2pred::Sign(x, y, b);
|
|
1018
|
+
if (s2pred::Sign(a, b, c) == xyb) {
|
|
1019
|
+
break; // The circumcenter is on the same side as B but further away.
|
|
1020
|
+
}
|
|
1021
|
+
// Other possible optimizations:
|
|
1022
|
+
// - if AB > max_adjacent_site_separation_ca_ then keep B.
|
|
1023
|
+
// - if d(B, XY) < 0.5 * min(AB, BC) then keep B.
|
|
1024
|
+
|
|
1025
|
+
// If the circumcenter of ABC is on the same side of XY as B, then B is
|
|
1026
|
+
// excluded by A and C combined. Otherwise B is needed and we can exit.
|
|
1027
|
+
if (s2pred::EdgeCircumcenterSign(x, y, a, b, c) != xyb) break;
|
|
1028
|
+
}
|
|
1029
|
+
if (add_site_c) {
|
|
1030
|
+
chain->push_back(site_id);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
S2_DCHECK(!chain->empty());
|
|
1034
|
+
if (google::DEBUG_MODE) {
|
|
1035
|
+
for (SiteId site_id : candidates) {
|
|
1036
|
+
if (s2pred::CompareDistances(y, sites_[chain->back()],
|
|
1037
|
+
sites_[site_id]) > 0) {
|
|
1038
|
+
S2_LOG(ERROR) << "Snapping invariant broken!";
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
if (s2builder_verbose) {
|
|
1043
|
+
std::cout << "(" << edge.first << "," << edge.second << "): ";
|
|
1044
|
+
for (SiteId id : *chain) std::cout << id << " ";
|
|
1045
|
+
std::cout << std::endl;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
void S2Builder::BuildLayers() {
|
|
1050
|
+
// Each output edge has an "input edge id set id" (an int32) representing
|
|
1051
|
+
// the set of input edge ids that were snapped to this edge. The actual
|
|
1052
|
+
// InputEdgeIds can be retrieved using "input_edge_id_set_lexicon".
|
|
1053
|
+
vector<vector<Edge>> layer_edges;
|
|
1054
|
+
vector<vector<InputEdgeIdSetId>> layer_input_edge_ids;
|
|
1055
|
+
IdSetLexicon input_edge_id_set_lexicon;
|
|
1056
|
+
BuildLayerEdges(&layer_edges, &layer_input_edge_ids,
|
|
1057
|
+
&input_edge_id_set_lexicon);
|
|
1058
|
+
|
|
1059
|
+
// At this point we have no further need for the input geometry or nearby
|
|
1060
|
+
// site data, so we clear those fields to save space.
|
|
1061
|
+
vector<S2Point>().swap(input_vertices_);
|
|
1062
|
+
vector<InputEdge>().swap(input_edges_);
|
|
1063
|
+
vector<compact_array<SiteId>>().swap(edge_sites_);
|
|
1064
|
+
|
|
1065
|
+
// If there are a large number of layers, then we build a minimal subset of
|
|
1066
|
+
// vertices for each layer. This ensures that layer types that iterate over
|
|
1067
|
+
// vertices will run in time proportional to the size of that layer rather
|
|
1068
|
+
// than the size of all layers combined.
|
|
1069
|
+
vector<vector<S2Point>> layer_vertices;
|
|
1070
|
+
static const int kMinLayersForVertexFiltering = 10;
|
|
1071
|
+
if (layers_.size() >= kMinLayersForVertexFiltering) {
|
|
1072
|
+
// Disable vertex filtering if it is disallowed by any layer. (This could
|
|
1073
|
+
// be optimized, but in current applications either all layers allow
|
|
1074
|
+
// filtering or none of them do.)
|
|
1075
|
+
bool allow_vertex_filtering = false;
|
|
1076
|
+
for (const auto& options : layer_options_) {
|
|
1077
|
+
allow_vertex_filtering &= options.allow_vertex_filtering();
|
|
1078
|
+
}
|
|
1079
|
+
if (allow_vertex_filtering) {
|
|
1080
|
+
vector<Graph::VertexId> filter_tmp; // Temporary used by FilterVertices.
|
|
1081
|
+
layer_vertices.resize(layers_.size());
|
|
1082
|
+
for (int i = 0; i < layers_.size(); ++i) {
|
|
1083
|
+
layer_vertices[i] = Graph::FilterVertices(sites_, &layer_edges[i],
|
|
1084
|
+
&filter_tmp);
|
|
1085
|
+
}
|
|
1086
|
+
vector<S2Point>().swap(sites_); // Release memory
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
for (int i = 0; i < layers_.size(); ++i) {
|
|
1090
|
+
const vector<S2Point>& vertices = (layer_vertices.empty() ?
|
|
1091
|
+
sites_ : layer_vertices[i]);
|
|
1092
|
+
Graph graph(layer_options_[i], &vertices, &layer_edges[i],
|
|
1093
|
+
&layer_input_edge_ids[i], &input_edge_id_set_lexicon,
|
|
1094
|
+
&label_set_ids_, &label_set_lexicon_,
|
|
1095
|
+
layer_is_full_polygon_predicates_[i]);
|
|
1096
|
+
layers_[i]->Build(graph, error_);
|
|
1097
|
+
// Don't free the layer data until all layers have been built, in order to
|
|
1098
|
+
// support building multiple layers at once (e.g. ClosedSetNormalizer).
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
static void DumpEdges(const vector<S2Builder::Graph::Edge>& edges,
|
|
1103
|
+
const vector<S2Point>& vertices) {
|
|
1104
|
+
for (const auto& e : edges) {
|
|
1105
|
+
vector<S2Point> v;
|
|
1106
|
+
v.push_back(vertices[e.first]);
|
|
1107
|
+
v.push_back(vertices[e.second]);
|
|
1108
|
+
std::cout << "S2Polyline: " << s2textformat::ToString(v)
|
|
1109
|
+
<< "(" << e.first << "," << e.second << ")" << std::endl;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// Snaps and possibly simplifies the edges for each layer, populating the
|
|
1114
|
+
// given output arguments. The resulting edges can be used to construct an
|
|
1115
|
+
// S2Builder::Graph directly (no further processing is necessary).
|
|
1116
|
+
//
|
|
1117
|
+
// This method is not "const" because Graph::ProcessEdges can modify
|
|
1118
|
+
// layer_options_ in some cases (changing undirected edges to directed ones).
|
|
1119
|
+
void S2Builder::BuildLayerEdges(
|
|
1120
|
+
vector<vector<Edge>>* layer_edges,
|
|
1121
|
+
vector<vector<InputEdgeIdSetId>>* layer_input_edge_ids,
|
|
1122
|
+
IdSetLexicon* input_edge_id_set_lexicon) {
|
|
1123
|
+
// Edge chains are simplified only when a non-zero snap radius is specified.
|
|
1124
|
+
// If so, we build a map from each site to the set of input vertices that
|
|
1125
|
+
// snapped to that site.
|
|
1126
|
+
vector<compact_array<InputVertexId>> site_vertices;
|
|
1127
|
+
bool simplify = snapping_needed_ && options_.simplify_edge_chains();
|
|
1128
|
+
if (simplify) site_vertices.resize(sites_.size());
|
|
1129
|
+
|
|
1130
|
+
layer_edges->resize(layers_.size());
|
|
1131
|
+
layer_input_edge_ids->resize(layers_.size());
|
|
1132
|
+
for (int i = 0; i < layers_.size(); ++i) {
|
|
1133
|
+
AddSnappedEdges(layer_begins_[i], layer_begins_[i+1], layer_options_[i],
|
|
1134
|
+
&(*layer_edges)[i], &(*layer_input_edge_ids)[i],
|
|
1135
|
+
input_edge_id_set_lexicon, &site_vertices);
|
|
1136
|
+
}
|
|
1137
|
+
if (simplify) {
|
|
1138
|
+
SimplifyEdgeChains(site_vertices, layer_edges, layer_input_edge_ids,
|
|
1139
|
+
input_edge_id_set_lexicon);
|
|
1140
|
+
}
|
|
1141
|
+
// We simplify edge chains before processing the per-layer GraphOptions
|
|
1142
|
+
// because simplification can create duplicate edges and/or sibling edge
|
|
1143
|
+
// pairs which may need to be removed.
|
|
1144
|
+
for (int i = 0; i < layers_.size(); ++i) {
|
|
1145
|
+
// The errors generated by ProcessEdges are really warnings, so we simply
|
|
1146
|
+
// record them and continue.
|
|
1147
|
+
Graph::ProcessEdges(&layer_options_[i], &(*layer_edges)[i],
|
|
1148
|
+
&(*layer_input_edge_ids)[i],
|
|
1149
|
+
input_edge_id_set_lexicon, error_);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// Snaps all the input edges for a given layer, populating the given output
|
|
1154
|
+
// arguments. If (*site_vertices) is non-empty then it is updated so that
|
|
1155
|
+
// (*site_vertices)[site] contains a list of all input vertices that were
|
|
1156
|
+
// snapped to that site.
|
|
1157
|
+
void S2Builder::AddSnappedEdges(
|
|
1158
|
+
InputEdgeId begin, InputEdgeId end, const GraphOptions& options,
|
|
1159
|
+
vector<Edge>* edges, vector<InputEdgeIdSetId>* input_edge_ids,
|
|
1160
|
+
IdSetLexicon* input_edge_id_set_lexicon,
|
|
1161
|
+
vector<compact_array<InputVertexId>>* site_vertices) const {
|
|
1162
|
+
bool discard_degenerate_edges = (options.degenerate_edges() ==
|
|
1163
|
+
GraphOptions::DegenerateEdges::DISCARD);
|
|
1164
|
+
vector<SiteId> chain;
|
|
1165
|
+
for (InputEdgeId e = begin; e < end; ++e) {
|
|
1166
|
+
InputEdgeIdSetId id = input_edge_id_set_lexicon->AddSingleton(e);
|
|
1167
|
+
SnapEdge(e, &chain);
|
|
1168
|
+
MaybeAddInputVertex(input_edges_[e].first, chain[0], site_vertices);
|
|
1169
|
+
if (chain.size() == 1) {
|
|
1170
|
+
if (discard_degenerate_edges) continue;
|
|
1171
|
+
AddSnappedEdge(chain[0], chain[0], id, options.edge_type(),
|
|
1172
|
+
edges, input_edge_ids);
|
|
1173
|
+
} else {
|
|
1174
|
+
MaybeAddInputVertex(input_edges_[e].second, chain.back(), site_vertices);
|
|
1175
|
+
for (int i = 1; i < chain.size(); ++i) {
|
|
1176
|
+
AddSnappedEdge(chain[i-1], chain[i], id, options.edge_type(),
|
|
1177
|
+
edges, input_edge_ids);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
if (s2builder_verbose) DumpEdges(*edges, sites_);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// If "site_vertices" is non-empty, ensures that (*site_vertices)[id] contains
|
|
1185
|
+
// "v". Duplicate entries are allowed.
|
|
1186
|
+
inline void S2Builder::MaybeAddInputVertex(
|
|
1187
|
+
InputVertexId v, SiteId id,
|
|
1188
|
+
vector<compact_array<InputVertexId>>* site_vertices) const {
|
|
1189
|
+
if (site_vertices->empty()) return;
|
|
1190
|
+
|
|
1191
|
+
// Optimization: check if we just added this vertex. This is worthwhile
|
|
1192
|
+
// because the input edges usually form a continuous chain, i.e. the
|
|
1193
|
+
// destination of one edge is the same as the source of the next edge.
|
|
1194
|
+
auto& vertices = (*site_vertices)[id];
|
|
1195
|
+
if (vertices.empty() || vertices.back() != v) {
|
|
1196
|
+
vertices.push_back(v);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// Adds the given edge to "edges" and "input_edge_ids". If undirected edges
|
|
1201
|
+
// are being used, also adds an edge in the opposite direction.
|
|
1202
|
+
inline void S2Builder::AddSnappedEdge(
|
|
1203
|
+
SiteId src, SiteId dst, InputEdgeIdSetId id, EdgeType edge_type,
|
|
1204
|
+
vector<Edge>* edges, vector<InputEdgeIdSetId>* input_edge_ids) const {
|
|
1205
|
+
edges->push_back(Edge(src, dst));
|
|
1206
|
+
input_edge_ids->push_back(id);
|
|
1207
|
+
if (edge_type == EdgeType::UNDIRECTED) {
|
|
1208
|
+
edges->push_back(Edge(dst, src));
|
|
1209
|
+
// Automatically created edges do not have input edge ids or labels. This
|
|
1210
|
+
// can be used to distinguish the original direction of the undirected edge.
|
|
1211
|
+
input_edge_ids->push_back(IdSetLexicon::EmptySetId());
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// A class that encapsulates the state needed for simplifying edge chains.
|
|
1216
|
+
class S2Builder::EdgeChainSimplifier {
|
|
1217
|
+
public:
|
|
1218
|
+
// The graph "g" contains all edges from all layers. "edge_layers"
|
|
1219
|
+
// indicates the original layer for each edge. "site_vertices" is a map
|
|
1220
|
+
// from SiteId to the set of InputVertexIds that were snapped to that site.
|
|
1221
|
+
// "layer_edges" and "layer_input_edge_ids" are output arguments where the
|
|
1222
|
+
// simplified edge chains will be placed. The input and output edges are
|
|
1223
|
+
// not sorted.
|
|
1224
|
+
EdgeChainSimplifier(
|
|
1225
|
+
const S2Builder& builder, const Graph& g,
|
|
1226
|
+
const vector<int>& edge_layers,
|
|
1227
|
+
const vector<compact_array<InputVertexId>>& site_vertices,
|
|
1228
|
+
vector<vector<Edge>>* layer_edges,
|
|
1229
|
+
vector<vector<InputEdgeIdSetId>>* layer_input_edge_ids,
|
|
1230
|
+
IdSetLexicon* input_edge_id_set_lexicon);
|
|
1231
|
+
|
|
1232
|
+
void Run();
|
|
1233
|
+
|
|
1234
|
+
private:
|
|
1235
|
+
using VertexId = Graph::VertexId;
|
|
1236
|
+
|
|
1237
|
+
class InteriorVertexMatcher;
|
|
1238
|
+
void OutputEdge(EdgeId e);
|
|
1239
|
+
int graph_edge_layer(EdgeId e) const;
|
|
1240
|
+
int input_edge_layer(InputEdgeId id) const;
|
|
1241
|
+
bool IsInterior(VertexId v);
|
|
1242
|
+
void SimplifyChain(VertexId v0, VertexId v1);
|
|
1243
|
+
Graph::VertexId FollowChain(VertexId v0, VertexId v1) const;
|
|
1244
|
+
void OutputAllEdges(VertexId v0, VertexId v1);
|
|
1245
|
+
bool TargetInputVertices(VertexId v, S2PolylineSimplifier* simplifier) const;
|
|
1246
|
+
bool AvoidSites(VertexId v0, VertexId v1, VertexId v2,
|
|
1247
|
+
S2PolylineSimplifier* simplifier) const;
|
|
1248
|
+
void MergeChain(const vector<VertexId>& vertices);
|
|
1249
|
+
void AssignDegenerateEdges(
|
|
1250
|
+
const vector<InputEdgeId>& degenerate_ids,
|
|
1251
|
+
vector<vector<InputEdgeId>>* merged_input_ids) const;
|
|
1252
|
+
|
|
1253
|
+
const S2Builder& builder_;
|
|
1254
|
+
const Graph& g_;
|
|
1255
|
+
Graph::VertexInMap in_;
|
|
1256
|
+
Graph::VertexOutMap out_;
|
|
1257
|
+
vector<int> edge_layers_;
|
|
1258
|
+
const vector<compact_array<InputVertexId>>& site_vertices_;
|
|
1259
|
+
vector<vector<Edge>>* layer_edges_;
|
|
1260
|
+
vector<vector<InputEdgeIdSetId>>* layer_input_edge_ids_;
|
|
1261
|
+
IdSetLexicon* input_edge_id_set_lexicon_;
|
|
1262
|
+
|
|
1263
|
+
// Convenience member copied from builder_.
|
|
1264
|
+
const std::vector<InputEdgeId>& layer_begins_;
|
|
1265
|
+
|
|
1266
|
+
// is_interior_[v] indicates that VertexId "v" is eligible to be an interior
|
|
1267
|
+
// vertex of a simplified edge chain. You can think of it as vertex whose
|
|
1268
|
+
// indegree and outdegree are both 1 (although the actual definition is a
|
|
1269
|
+
// bit more complicated because of duplicate edges and layers).
|
|
1270
|
+
vector<bool> is_interior_;
|
|
1271
|
+
|
|
1272
|
+
// used_[e] indicates that EdgeId "e" has already been processed.
|
|
1273
|
+
vector<bool> used_;
|
|
1274
|
+
|
|
1275
|
+
// Temporary vectors, declared here to avoid repeated allocation.
|
|
1276
|
+
vector<VertexId> tmp_vertices_;
|
|
1277
|
+
vector<EdgeId> tmp_edges_;
|
|
1278
|
+
|
|
1279
|
+
// The output edges after simplification.
|
|
1280
|
+
vector<Edge> new_edges_;
|
|
1281
|
+
vector<InputEdgeIdSetId> new_input_edge_ids_;
|
|
1282
|
+
vector<int> new_edge_layers_;
|
|
1283
|
+
};
|
|
1284
|
+
|
|
1285
|
+
// Simplifies edge chains, updating its input/output arguments as necessary.
|
|
1286
|
+
void S2Builder::SimplifyEdgeChains(
|
|
1287
|
+
const vector<compact_array<InputVertexId>>& site_vertices,
|
|
1288
|
+
vector<vector<Edge>>* layer_edges,
|
|
1289
|
+
vector<vector<InputEdgeIdSetId>>* layer_input_edge_ids,
|
|
1290
|
+
IdSetLexicon* input_edge_id_set_lexicon) const {
|
|
1291
|
+
if (layers_.empty()) return;
|
|
1292
|
+
|
|
1293
|
+
// Merge the edges from all layers (in order to build a single graph).
|
|
1294
|
+
vector<Edge> merged_edges;
|
|
1295
|
+
vector<InputEdgeIdSetId> merged_input_edge_ids;
|
|
1296
|
+
vector<int> merged_edge_layers;
|
|
1297
|
+
MergeLayerEdges(*layer_edges, *layer_input_edge_ids,
|
|
1298
|
+
&merged_edges, &merged_input_edge_ids, &merged_edge_layers);
|
|
1299
|
+
|
|
1300
|
+
// The following fields will be reconstructed by EdgeChainSimplifier.
|
|
1301
|
+
for (auto& edges : *layer_edges) edges.clear();
|
|
1302
|
+
for (auto& input_edge_ids : *layer_input_edge_ids) input_edge_ids.clear();
|
|
1303
|
+
|
|
1304
|
+
// The graph options are irrelevant for edge chain simplification, but we
|
|
1305
|
+
// try to set them appropriately anyway.
|
|
1306
|
+
S2Builder::GraphOptions graph_options(EdgeType::DIRECTED,
|
|
1307
|
+
GraphOptions::DegenerateEdges::KEEP,
|
|
1308
|
+
GraphOptions::DuplicateEdges::KEEP,
|
|
1309
|
+
GraphOptions::SiblingPairs::KEEP);
|
|
1310
|
+
Graph graph(graph_options, &sites_, &merged_edges, &merged_input_edge_ids,
|
|
1311
|
+
input_edge_id_set_lexicon, nullptr, nullptr,
|
|
1312
|
+
IsFullPolygonPredicate());
|
|
1313
|
+
EdgeChainSimplifier simplifier(
|
|
1314
|
+
*this, graph, merged_edge_layers, site_vertices,
|
|
1315
|
+
layer_edges, layer_input_edge_ids, input_edge_id_set_lexicon);
|
|
1316
|
+
simplifier.Run();
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
// Merges the edges from all layers and sorts them in lexicographic order so
|
|
1320
|
+
// that we can construct a single graph. The sort is stable, which means that
|
|
1321
|
+
// any duplicate edges within each layer will still be sorted by InputEdgeId.
|
|
1322
|
+
void S2Builder::MergeLayerEdges(
|
|
1323
|
+
const vector<vector<Edge>>& layer_edges,
|
|
1324
|
+
const vector<vector<InputEdgeIdSetId>>& layer_input_edge_ids,
|
|
1325
|
+
vector<Edge>* edges, vector<InputEdgeIdSetId>* input_edge_ids,
|
|
1326
|
+
vector<int>* edge_layers) const {
|
|
1327
|
+
vector<LayerEdgeId> order;
|
|
1328
|
+
for (int i = 0; i < layer_edges.size(); ++i) {
|
|
1329
|
+
for (int e = 0; e < layer_edges[i].size(); ++e) {
|
|
1330
|
+
order.push_back(LayerEdgeId(i, e));
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
std::sort(order.begin(), order.end(),
|
|
1334
|
+
[&layer_edges](const LayerEdgeId& ai, const LayerEdgeId& bi) {
|
|
1335
|
+
return StableLessThan(layer_edges[ai.first][ai.second],
|
|
1336
|
+
layer_edges[bi.first][bi.second], ai, bi);
|
|
1337
|
+
});
|
|
1338
|
+
edges->reserve(order.size());
|
|
1339
|
+
input_edge_ids->reserve(order.size());
|
|
1340
|
+
edge_layers->reserve(order.size());
|
|
1341
|
+
for (const LayerEdgeId& id : order) {
|
|
1342
|
+
edges->push_back(layer_edges[id.first][id.second]);
|
|
1343
|
+
input_edge_ids->push_back(layer_input_edge_ids[id.first][id.second]);
|
|
1344
|
+
edge_layers->push_back(id.first);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// A comparison function that allows stable sorting with std::sort (which is
|
|
1349
|
+
// fast but not stable). It breaks ties between equal edges by comparing
|
|
1350
|
+
// their LayerEdgeIds.
|
|
1351
|
+
inline bool S2Builder::StableLessThan(
|
|
1352
|
+
const Edge& a, const Edge& b,
|
|
1353
|
+
const LayerEdgeId& ai, const LayerEdgeId& bi) {
|
|
1354
|
+
// The compiler doesn't optimize this as well as it should:
|
|
1355
|
+
// return make_pair(a, ai) < make_pair(b, bi);
|
|
1356
|
+
if (a.first < b.first) return true;
|
|
1357
|
+
if (b.first < a.first) return false;
|
|
1358
|
+
if (a.second < b.second) return true;
|
|
1359
|
+
if (b.second < a.second) return false;
|
|
1360
|
+
return ai < bi; // Stable sort.
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
S2Builder::EdgeChainSimplifier::EdgeChainSimplifier(
|
|
1364
|
+
const S2Builder& builder, const Graph& g, const vector<int>& edge_layers,
|
|
1365
|
+
const vector<compact_array<InputVertexId>>& site_vertices,
|
|
1366
|
+
vector<vector<Edge>>* layer_edges,
|
|
1367
|
+
vector<vector<InputEdgeIdSetId>>* layer_input_edge_ids,
|
|
1368
|
+
IdSetLexicon* input_edge_id_set_lexicon)
|
|
1369
|
+
: builder_(builder), g_(g), in_(g), out_(g), edge_layers_(edge_layers),
|
|
1370
|
+
site_vertices_(site_vertices), layer_edges_(layer_edges),
|
|
1371
|
+
layer_input_edge_ids_(layer_input_edge_ids),
|
|
1372
|
+
input_edge_id_set_lexicon_(input_edge_id_set_lexicon),
|
|
1373
|
+
layer_begins_(builder_.layer_begins_),
|
|
1374
|
+
is_interior_(g.num_vertices()), used_(g.num_edges()) {
|
|
1375
|
+
new_edges_.reserve(g.num_edges());
|
|
1376
|
+
new_input_edge_ids_.reserve(g.num_edges());
|
|
1377
|
+
new_edge_layers_.reserve(g.num_edges());
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
void S2Builder::EdgeChainSimplifier::Run() {
|
|
1381
|
+
// Determine which vertices can be interior vertices of an edge chain.
|
|
1382
|
+
for (VertexId v = 0; v < g_.num_vertices(); ++v) {
|
|
1383
|
+
is_interior_[v] = IsInterior(v);
|
|
1384
|
+
}
|
|
1385
|
+
// Attempt to simplify all edge chains that start from a non-interior
|
|
1386
|
+
// vertex. (This takes care of all chains except loops.)
|
|
1387
|
+
for (EdgeId e = 0; e < g_.num_edges(); ++e) {
|
|
1388
|
+
if (used_[e]) continue;
|
|
1389
|
+
Edge edge = g_.edge(e);
|
|
1390
|
+
if (is_interior_[edge.first]) continue;
|
|
1391
|
+
if (!is_interior_[edge.second]) {
|
|
1392
|
+
OutputEdge(e); // An edge between two non-interior vertices.
|
|
1393
|
+
} else {
|
|
1394
|
+
SimplifyChain(edge.first, edge.second);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
// If there are any edges left, they form one or more disjoint loops where
|
|
1398
|
+
// all vertices are interior vertices.
|
|
1399
|
+
//
|
|
1400
|
+
// TODO(ericv): It would be better to start from the edge with the smallest
|
|
1401
|
+
// min_input_edge_id(), since that would make the output more predictable
|
|
1402
|
+
// for testing purposes. It also means that we won't create an edge that
|
|
1403
|
+
// spans the start and end of a polyline if the polyline is snapped into a
|
|
1404
|
+
// loop. (Unfortunately there are pathological examples that prevent us
|
|
1405
|
+
// from guaranteeing this in general, e.g. there could be two polylines in
|
|
1406
|
+
// different layers that snap to the same loop but start at different
|
|
1407
|
+
// positions. In general we only consider input edge ids to be a hint
|
|
1408
|
+
// towards the preferred output ordering.)
|
|
1409
|
+
for (EdgeId e = 0; e < g_.num_edges(); ++e) {
|
|
1410
|
+
if (used_[e]) continue;
|
|
1411
|
+
Edge edge = g_.edge(e);
|
|
1412
|
+
if (edge.first == edge.second) {
|
|
1413
|
+
// Note that it is safe to output degenerate edges as we go along,
|
|
1414
|
+
// because this vertex has at least one non-degenerate outgoing edge and
|
|
1415
|
+
// therefore we will (or just did) start an edge chain here.
|
|
1416
|
+
OutputEdge(e);
|
|
1417
|
+
} else {
|
|
1418
|
+
SimplifyChain(edge.first, edge.second);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
// Finally, copy the output edges into the appropriate layers. They don't
|
|
1423
|
+
// need to be sorted because the input edges were also unsorted.
|
|
1424
|
+
for (int e = 0; e < new_edges_.size(); ++e) {
|
|
1425
|
+
int layer = new_edge_layers_[e];
|
|
1426
|
+
(*layer_edges_)[layer].push_back(new_edges_[e]);
|
|
1427
|
+
(*layer_input_edge_ids_)[layer].push_back(new_input_edge_ids_[e]);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// Copies the given edge to the output and marks it as used.
|
|
1432
|
+
inline void S2Builder::EdgeChainSimplifier::OutputEdge(EdgeId e) {
|
|
1433
|
+
new_edges_.push_back(g_.edge(e));
|
|
1434
|
+
new_input_edge_ids_.push_back(g_.input_edge_id_set_id(e));
|
|
1435
|
+
new_edge_layers_.push_back(edge_layers_[e]);
|
|
1436
|
+
used_[e] = true;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// Returns the layer that a given graph edge belongs to.
|
|
1440
|
+
inline int S2Builder::EdgeChainSimplifier::graph_edge_layer(EdgeId e) const {
|
|
1441
|
+
return edge_layers_[e];
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
// Returns the layer than a given input edge belongs to.
|
|
1445
|
+
int S2Builder::EdgeChainSimplifier::input_edge_layer(InputEdgeId id) const {
|
|
1446
|
+
// NOTE(ericv): If this method shows up in profiling, the result could be
|
|
1447
|
+
// stored with each edge (i.e., edge_layers_ and new_edge_layers_).
|
|
1448
|
+
S2_DCHECK_GE(id, 0);
|
|
1449
|
+
return (std::upper_bound(layer_begins_.begin(), layer_begins_.end(), id) -
|
|
1450
|
+
(layer_begins_.begin() + 1));
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
// A helper class for determining whether a vertex can be an interior vertex
|
|
1454
|
+
// of a simplified edge chain. Such a vertex must be adjacent to exactly two
|
|
1455
|
+
// vertices (across all layers combined), and in each layer the number of
|
|
1456
|
+
// incoming edges from one vertex must equal the number of outgoing edges to
|
|
1457
|
+
// the other vertex (in both directions). Furthermore the vertex cannot have
|
|
1458
|
+
// any degenerate edges in a given layer unless it has at least one
|
|
1459
|
+
// non-degenerate edge in that layer as well. (Note that usually there will
|
|
1460
|
+
// not be any degenerate edges at all, since most layer types discard them.)
|
|
1461
|
+
//
|
|
1462
|
+
// The last condition is necessary to prevent the following: suppose that one
|
|
1463
|
+
// layer has a chain ABC and another layer has a degenerate edge BB (with no
|
|
1464
|
+
// other edges incident to B). Then we can't simplify ABC to AC because there
|
|
1465
|
+
// would be no suitable replacement for the edge BB (since the input edge that
|
|
1466
|
+
// mapped to BB can't be replaced by any of the edges AA, AC, or CC without
|
|
1467
|
+
// moving further than snap_radius).
|
|
1468
|
+
class S2Builder::EdgeChainSimplifier::InteriorVertexMatcher {
|
|
1469
|
+
public:
|
|
1470
|
+
// Checks whether "v0" can be an interior vertex of an edge chain.
|
|
1471
|
+
explicit InteriorVertexMatcher(VertexId v0)
|
|
1472
|
+
: v0_(v0), v1_(-1), v2_(-1), n0_(0), n1_(0), n2_(0), excess_out_(0),
|
|
1473
|
+
too_many_endpoints_(false) {
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// Starts analyzing the edges of a new layer.
|
|
1477
|
+
void StartLayer() {
|
|
1478
|
+
excess_out_ = n0_ = n1_ = n2_ = 0;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// This method should be called for each edge incident to "v0" in a given
|
|
1482
|
+
// layer. (For degenerate edges, it should be called twice.)
|
|
1483
|
+
void Tally(VertexId v, bool outgoing) {
|
|
1484
|
+
excess_out_ += outgoing ? 1 : -1; // outdegree - indegree
|
|
1485
|
+
if (v == v0_) {
|
|
1486
|
+
++n0_; // Counts both endpoints of each degenerate edge.
|
|
1487
|
+
} else {
|
|
1488
|
+
// We keep track of the total number of edges (incoming or outgoing)
|
|
1489
|
+
// connecting v0 to up to two adjacent vertices.
|
|
1490
|
+
if (v1_ < 0) v1_ = v;
|
|
1491
|
+
if (v1_ == v) {
|
|
1492
|
+
++n1_;
|
|
1493
|
+
} else {
|
|
1494
|
+
if (v2_ < 0) v2_ = v;
|
|
1495
|
+
if (v2_ == v) {
|
|
1496
|
+
++n2_;
|
|
1497
|
+
} else {
|
|
1498
|
+
too_many_endpoints_ = true;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
// This method should be called after processing the edges for each layer.
|
|
1505
|
+
// It returns true if "v0" is an interior vertex based on the edges so far.
|
|
1506
|
+
bool Matches() const {
|
|
1507
|
+
// We check that there are the same number of incoming and outgoing edges
|
|
1508
|
+
// in each direction by verifying that (1) indegree(v0) == outdegree(v0)
|
|
1509
|
+
// and (2) the total number of edges (incoming and outgoing) to "v1" and
|
|
1510
|
+
// "v2" are equal. We also check the condition on degenerate edges that
|
|
1511
|
+
// is documented above.
|
|
1512
|
+
return (!too_many_endpoints_ && excess_out_ == 0 && n1_ == n2_ &&
|
|
1513
|
+
(n0_ == 0 || n1_ > 0));
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
private:
|
|
1517
|
+
VertexId v0_, v1_, v2_;
|
|
1518
|
+
int n0_, n1_, n2_;
|
|
1519
|
+
int excess_out_; // outdegree(v0) - indegree(v0)
|
|
1520
|
+
bool too_many_endpoints_; // Have we seen more than two adjacent vertices?
|
|
1521
|
+
};
|
|
1522
|
+
|
|
1523
|
+
// Returns true if VertexId "v" can be an interior vertex of a simplified edge
|
|
1524
|
+
// chain. (See the InteriorVertexMatcher class for what this implies.)
|
|
1525
|
+
bool S2Builder::EdgeChainSimplifier::IsInterior(VertexId v) {
|
|
1526
|
+
// Check a few simple prerequisites.
|
|
1527
|
+
if (out_.degree(v) == 0) return false;
|
|
1528
|
+
if (out_.degree(v) != in_.degree(v)) return false;
|
|
1529
|
+
if (v < builder_.num_forced_sites_) return false; // Keep forced vertices.
|
|
1530
|
+
|
|
1531
|
+
// Sort the edges so that they are grouped by layer.
|
|
1532
|
+
vector<EdgeId>& edges = tmp_edges_; // Avoid allocating each time.
|
|
1533
|
+
edges.clear();
|
|
1534
|
+
for (EdgeId e : out_.edge_ids(v)) edges.push_back(e);
|
|
1535
|
+
for (EdgeId e : in_.edge_ids(v)) edges.push_back(e);
|
|
1536
|
+
std::sort(edges.begin(), edges.end(), [this](EdgeId x, EdgeId y) {
|
|
1537
|
+
return graph_edge_layer(x) < graph_edge_layer(y);
|
|
1538
|
+
});
|
|
1539
|
+
// Now feed the edges in each layer to the InteriorVertexMatcher.
|
|
1540
|
+
InteriorVertexMatcher matcher(v);
|
|
1541
|
+
for (auto e = edges.begin(); e != edges.end(); ) {
|
|
1542
|
+
int layer = graph_edge_layer(*e);
|
|
1543
|
+
matcher.StartLayer();
|
|
1544
|
+
for (; e != edges.end() && graph_edge_layer(*e) == layer; ++e) {
|
|
1545
|
+
Edge edge = g_.edge(*e);
|
|
1546
|
+
if (edge.first == v) matcher.Tally(edge.second, true /*outgoing*/);
|
|
1547
|
+
if (edge.second == v) matcher.Tally(edge.first, false /*outgoing*/);
|
|
1548
|
+
}
|
|
1549
|
+
if (!matcher.Matches()) return false;
|
|
1550
|
+
}
|
|
1551
|
+
return true;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
// Follows the edge chain starting with (v0, v1) until either we find a
|
|
1555
|
+
// non-interior vertex or we return to the original vertex v0. At each vertex
|
|
1556
|
+
// we simplify a subchain of edges that is as long as possible.
|
|
1557
|
+
void S2Builder::EdgeChainSimplifier::SimplifyChain(VertexId v0, VertexId v1) {
|
|
1558
|
+
// Avoid allocating "chain" each time by reusing it.
|
|
1559
|
+
vector<VertexId>& chain = tmp_vertices_;
|
|
1560
|
+
S2PolylineSimplifier simplifier;
|
|
1561
|
+
VertexId vstart = v0;
|
|
1562
|
+
bool done = false;
|
|
1563
|
+
do {
|
|
1564
|
+
// Simplify a subchain of edges starting (v0, v1).
|
|
1565
|
+
simplifier.Init(g_.vertex(v0));
|
|
1566
|
+
AvoidSites(v0, v0, v1, &simplifier);
|
|
1567
|
+
chain.push_back(v0);
|
|
1568
|
+
do {
|
|
1569
|
+
chain.push_back(v1);
|
|
1570
|
+
done = !is_interior_[v1] || v1 == vstart;
|
|
1571
|
+
if (done) break;
|
|
1572
|
+
|
|
1573
|
+
// Attempt to extend the chain to the next vertex.
|
|
1574
|
+
VertexId vprev = v0;
|
|
1575
|
+
v0 = v1;
|
|
1576
|
+
v1 = FollowChain(vprev, v0);
|
|
1577
|
+
} while (TargetInputVertices(v0, &simplifier) &&
|
|
1578
|
+
AvoidSites(chain[0], v0, v1, &simplifier) &&
|
|
1579
|
+
simplifier.Extend(g_.vertex(v1)));
|
|
1580
|
+
|
|
1581
|
+
if (chain.size() == 2) {
|
|
1582
|
+
OutputAllEdges(chain[0], chain[1]); // Could not simplify.
|
|
1583
|
+
} else {
|
|
1584
|
+
MergeChain(chain);
|
|
1585
|
+
}
|
|
1586
|
+
// Note that any degenerate edges that were not merged into a chain are
|
|
1587
|
+
// output by EdgeChainSimplifier::Run().
|
|
1588
|
+
chain.clear();
|
|
1589
|
+
} while (!done);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
// Given an edge (v0, v1) where v1 is an interior vertex, returns the (unique)
|
|
1593
|
+
// next vertex in the edge chain.
|
|
1594
|
+
S2Builder::Graph::VertexId S2Builder::EdgeChainSimplifier::FollowChain(
|
|
1595
|
+
VertexId v0, VertexId v1) const {
|
|
1596
|
+
S2_DCHECK(is_interior_[v1]);
|
|
1597
|
+
for (EdgeId e : out_.edge_ids(v1)) {
|
|
1598
|
+
VertexId v = g_.edge(e).second;
|
|
1599
|
+
if (v != v0 && v != v1) return v;
|
|
1600
|
+
}
|
|
1601
|
+
S2_LOG(FATAL) << "Could not find next edge in edge chain";
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
// Copies all input edges between v0 and v1 (in both directions) to the output.
|
|
1605
|
+
void S2Builder::EdgeChainSimplifier::OutputAllEdges(VertexId v0, VertexId v1) {
|
|
1606
|
+
for (EdgeId e : out_.edge_ids(v0, v1)) OutputEdge(e);
|
|
1607
|
+
for (EdgeId e : out_.edge_ids(v1, v0)) OutputEdge(e);
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// Ensures that the simplified edge passes within "edge_snap_radius" of all
|
|
1611
|
+
// the *input* vertices that snapped to the given vertex "v".
|
|
1612
|
+
bool S2Builder::EdgeChainSimplifier::TargetInputVertices(
|
|
1613
|
+
VertexId v, S2PolylineSimplifier* simplifier) const {
|
|
1614
|
+
for (InputVertexId i : site_vertices_[v]) {
|
|
1615
|
+
if (!simplifier->TargetDisc(builder_.input_vertices_[i],
|
|
1616
|
+
builder_.edge_snap_radius_ca_)) {
|
|
1617
|
+
return false;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
return true;
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// Given the starting vertex v0 and last edge (v1, v2) of an edge chain,
|
|
1624
|
+
// restricts the allowable range of angles in order to ensure that all sites
|
|
1625
|
+
// near the edge (v1, v2) are avoided by at least min_edge_vertex_separation.
|
|
1626
|
+
bool S2Builder::EdgeChainSimplifier::AvoidSites(
|
|
1627
|
+
VertexId v0, VertexId v1, VertexId v2,
|
|
1628
|
+
S2PolylineSimplifier* simplifier) const {
|
|
1629
|
+
const S2Point& p0 = g_.vertex(v0);
|
|
1630
|
+
const S2Point& p1 = g_.vertex(v1);
|
|
1631
|
+
const S2Point& p2 = g_.vertex(v2);
|
|
1632
|
+
S1ChordAngle r1(p0, p1);
|
|
1633
|
+
S1ChordAngle r2(p0, p2);
|
|
1634
|
+
|
|
1635
|
+
// The distance from the start of the edge chain must increase monotonically
|
|
1636
|
+
// for each vertex, since we don't want to simplify chains that backtrack on
|
|
1637
|
+
// themselves (we want a parametric approximation, not a geometric one).
|
|
1638
|
+
if (r2 < r1) return false;
|
|
1639
|
+
|
|
1640
|
+
// We also limit the maximum edge length in order to guarantee that the
|
|
1641
|
+
// simplified edge stays with max_edge_deviation() of all the input edges
|
|
1642
|
+
// that snap to it.
|
|
1643
|
+
if (r2 >= builder_.min_edge_length_to_split_ca_) return false;
|
|
1644
|
+
|
|
1645
|
+
// Otherwise it is sufficient to consider the nearby sites (edge_sites_) for
|
|
1646
|
+
// a single input edge that snapped to (v1, v2) or (v2, v1). This is
|
|
1647
|
+
// because each edge has a list of all sites within (max_edge_deviation +
|
|
1648
|
+
// min_edge_vertex_separation), and since the output edge is within
|
|
1649
|
+
// max_edge_deviation of all input edges, this list includes all sites
|
|
1650
|
+
// within min_edge_vertex_separation of the output edge.
|
|
1651
|
+
//
|
|
1652
|
+
// Usually there is only one edge to choose from, but it's not much more
|
|
1653
|
+
// effort to choose the edge with the shortest list of edge_sites_.
|
|
1654
|
+
InputEdgeId best = -1;
|
|
1655
|
+
const auto& edge_sites = builder_.edge_sites_;
|
|
1656
|
+
for (EdgeId e : out_.edge_ids(v1, v2)) {
|
|
1657
|
+
for (InputEdgeId id : g_.input_edge_ids(e)) {
|
|
1658
|
+
if (best < 0 || edge_sites[id].size() < edge_sites[best].size())
|
|
1659
|
+
best = id;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
for (EdgeId e : out_.edge_ids(v2, v1)) {
|
|
1663
|
+
for (InputEdgeId id : g_.input_edge_ids(e)) {
|
|
1664
|
+
if (best < 0 || edge_sites[id].size() < edge_sites[best].size())
|
|
1665
|
+
best = id;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
S2_DCHECK_GE(best, 0); // Because there is at least one outgoing edge.
|
|
1669
|
+
|
|
1670
|
+
for (VertexId v : edge_sites[best]) {
|
|
1671
|
+
// This test is optional since these sites are excluded below anyway.
|
|
1672
|
+
if (v == v0 || v == v1 || v == v2) continue;
|
|
1673
|
+
|
|
1674
|
+
// We are only interested in sites whose distance from "p0" is in the
|
|
1675
|
+
// range (r1, r2). Sites closer than "r1" have already been processed,
|
|
1676
|
+
// and sites further than "r2" aren't relevant yet.
|
|
1677
|
+
const S2Point& p = g_.vertex(v);
|
|
1678
|
+
S1ChordAngle r(p0, p);
|
|
1679
|
+
if (r <= r1 || r >= r2) continue;
|
|
1680
|
+
|
|
1681
|
+
// We need to figure out whether this site is to the left or right of the
|
|
1682
|
+
// edge chain. For the first edge this is easy. Otherwise, since we are
|
|
1683
|
+
// only considering sites in the radius range (r1, r2), we can do this by
|
|
1684
|
+
// checking whether the site is to the left of the wedge (p0, p1, p2).
|
|
1685
|
+
bool disc_on_left = (v1 == v0) ? (s2pred::Sign(p1, p2, p) > 0)
|
|
1686
|
+
: s2pred::OrderedCCW(p0, p2, p, p1);
|
|
1687
|
+
if (!simplifier->AvoidDisc(p, builder_.min_edge_site_separation_ca_,
|
|
1688
|
+
disc_on_left)) {
|
|
1689
|
+
return false;
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
return true;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
// Given the vertices in a simplified edge chain, adds the corresponding
|
|
1696
|
+
// simplified edge(s) to the output. Note that (1) the edge chain may exist
|
|
1697
|
+
// in multiple layers, (2) the edge chain may exist in both directions, (3)
|
|
1698
|
+
// there may be more than one copy of an edge chain (in either direction)
|
|
1699
|
+
// within a single layer.
|
|
1700
|
+
void S2Builder::EdgeChainSimplifier::MergeChain(
|
|
1701
|
+
const vector<VertexId>& vertices) {
|
|
1702
|
+
// Suppose that all interior vertices have M outgoing edges and N incoming
|
|
1703
|
+
// edges. Our goal is to group the edges into M outgoing chains and N
|
|
1704
|
+
// incoming chains, and then replace each chain by a single edge.
|
|
1705
|
+
vector<vector<InputEdgeId>> merged_input_ids;
|
|
1706
|
+
vector<InputEdgeId> degenerate_ids;
|
|
1707
|
+
int num_out; // Edge count in the outgoing direction.
|
|
1708
|
+
for (int i = 1; i < vertices.size(); ++i) {
|
|
1709
|
+
VertexId v0 = vertices[i-1];
|
|
1710
|
+
VertexId v1 = vertices[i];
|
|
1711
|
+
auto out_edges = out_.edge_ids(v0, v1);
|
|
1712
|
+
auto in_edges = out_.edge_ids(v1, v0);
|
|
1713
|
+
if (i == 1) {
|
|
1714
|
+
// Allocate space to store the input edge ids associated with each edge.
|
|
1715
|
+
num_out = out_edges.size();
|
|
1716
|
+
merged_input_ids.resize(num_out + in_edges.size());
|
|
1717
|
+
for (vector<InputEdgeId>& ids : merged_input_ids) {
|
|
1718
|
+
ids.reserve(vertices.size() - 1);
|
|
1719
|
+
}
|
|
1720
|
+
} else {
|
|
1721
|
+
// For each interior vertex, we build a list of input edge ids
|
|
1722
|
+
// associated with degenerate edges. Each input edge ids will be
|
|
1723
|
+
// assigned to one of the output edges later. (Normally there are no
|
|
1724
|
+
// degenerate edges at all since most layer types don't want them.)
|
|
1725
|
+
S2_DCHECK(is_interior_[v0]);
|
|
1726
|
+
for (EdgeId e : out_.edge_ids(v0, v0)) {
|
|
1727
|
+
for (InputEdgeId id : g_.input_edge_ids(e)) {
|
|
1728
|
+
degenerate_ids.push_back(id);
|
|
1729
|
+
}
|
|
1730
|
+
used_[e] = true;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
// Because the edges were created in layer order, and all sorts used are
|
|
1734
|
+
// stable, the edges are still in layer order. Therefore we can simply
|
|
1735
|
+
// merge together all the edges in the same relative position.
|
|
1736
|
+
int j = 0;
|
|
1737
|
+
for (EdgeId e : out_edges) {
|
|
1738
|
+
for (InputEdgeId id : g_.input_edge_ids(e)) {
|
|
1739
|
+
merged_input_ids[j].push_back(id);
|
|
1740
|
+
}
|
|
1741
|
+
used_[e] = true;
|
|
1742
|
+
++j;
|
|
1743
|
+
}
|
|
1744
|
+
for (EdgeId e : in_edges) {
|
|
1745
|
+
for (InputEdgeId id : g_.input_edge_ids(e)) {
|
|
1746
|
+
merged_input_ids[j].push_back(id);
|
|
1747
|
+
}
|
|
1748
|
+
used_[e] = true;
|
|
1749
|
+
++j;
|
|
1750
|
+
}
|
|
1751
|
+
S2_DCHECK_EQ(merged_input_ids.size(), j);
|
|
1752
|
+
}
|
|
1753
|
+
if (!degenerate_ids.empty()) {
|
|
1754
|
+
std::sort(degenerate_ids.begin(), degenerate_ids.end());
|
|
1755
|
+
AssignDegenerateEdges(degenerate_ids, &merged_input_ids);
|
|
1756
|
+
}
|
|
1757
|
+
// Output the merged edges.
|
|
1758
|
+
VertexId v0 = vertices[0], v1 = vertices[1], vb = vertices.back();
|
|
1759
|
+
for (EdgeId e : out_.edge_ids(v0, v1)) {
|
|
1760
|
+
new_edges_.push_back(Edge(v0, vb));
|
|
1761
|
+
new_edge_layers_.push_back(graph_edge_layer(e));
|
|
1762
|
+
}
|
|
1763
|
+
for (EdgeId e : out_.edge_ids(v1, v0)) {
|
|
1764
|
+
new_edges_.push_back(Edge(vb, v0));
|
|
1765
|
+
new_edge_layers_.push_back(graph_edge_layer(e));
|
|
1766
|
+
}
|
|
1767
|
+
for (const auto& ids : merged_input_ids) {
|
|
1768
|
+
new_input_edge_ids_.push_back(input_edge_id_set_lexicon_->Add(ids));
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
// Given a list of the input edge ids associated with degenerate edges in the
|
|
1773
|
+
// interior of an edge chain, assigns each input edge id to one of the output
|
|
1774
|
+
// edges.
|
|
1775
|
+
void S2Builder::EdgeChainSimplifier::AssignDegenerateEdges(
|
|
1776
|
+
const vector<InputEdgeId>& degenerate_ids,
|
|
1777
|
+
vector<vector<InputEdgeId>>* merged_ids) const {
|
|
1778
|
+
// Each degenerate edge is assigned to an output edge in the appropriate
|
|
1779
|
+
// layer. If there is more than one candidate, we use heuristics so that if
|
|
1780
|
+
// the input consists of a chain of edges provided in consecutive order
|
|
1781
|
+
// (some of which became degenerate), then all those input edges are
|
|
1782
|
+
// assigned to the same output edge. For example, suppose that one output
|
|
1783
|
+
// edge is labeled with input edges 3,4,7,8, while another output edge is
|
|
1784
|
+
// labeled with input edges 50,51,54,57. Then if we encounter degenerate
|
|
1785
|
+
// edges labeled with input edges 5 and 6, we would prefer to assign them to
|
|
1786
|
+
// the first edge (yielding the continuous range 3,4,5,6,7,8).
|
|
1787
|
+
//
|
|
1788
|
+
// The heuristic below is only smart enough to handle the case where the
|
|
1789
|
+
// candidate output edges have non-overlapping ranges of input edges.
|
|
1790
|
+
// (Otherwise there is probably not a good heuristic for assigning the
|
|
1791
|
+
// degenerate edges in any case.)
|
|
1792
|
+
|
|
1793
|
+
// Duplicate edge ids don't affect the heuristic below, so we don't bother
|
|
1794
|
+
// removing them. (They will be removed by IdSetLexicon::Add.)
|
|
1795
|
+
for (auto& ids : *merged_ids) std::sort(ids.begin(), ids.end());
|
|
1796
|
+
|
|
1797
|
+
// Sort the output edges by their minimum input edge id. This is sufficient
|
|
1798
|
+
// for the purpose of determining which layer they belong to. With
|
|
1799
|
+
// EdgeType::UNDIRECTED, some edges might not have any input edge ids (i.e.,
|
|
1800
|
+
// if they consist entirely of siblings of input edges). We simply remove
|
|
1801
|
+
// such edges from the lists of candidates.
|
|
1802
|
+
vector<unsigned> order;
|
|
1803
|
+
order.reserve(merged_ids->size());
|
|
1804
|
+
for (int i = 0; i < merged_ids->size(); ++i) {
|
|
1805
|
+
if (!(*merged_ids)[i].empty()) order.push_back(i);
|
|
1806
|
+
}
|
|
1807
|
+
std::sort(order.begin(), order.end(), [&merged_ids](int i, int j) {
|
|
1808
|
+
return (*merged_ids)[i][0] < (*merged_ids)[j][0];
|
|
1809
|
+
});
|
|
1810
|
+
|
|
1811
|
+
// Now determine where each degenerate edge should be assigned.
|
|
1812
|
+
for (InputEdgeId degenerate_id : degenerate_ids) {
|
|
1813
|
+
int layer = input_edge_layer(degenerate_id);
|
|
1814
|
+
|
|
1815
|
+
// Find the first output edge whose range of input edge ids starts after
|
|
1816
|
+
// "degenerate_id". If the previous edge belongs to the correct layer,
|
|
1817
|
+
// then we assign the degenerate edge to it.
|
|
1818
|
+
auto it = std::upper_bound(order.begin(), order.end(), degenerate_id,
|
|
1819
|
+
[&merged_ids](InputEdgeId x, unsigned y) {
|
|
1820
|
+
return x < (*merged_ids)[y][0];
|
|
1821
|
+
});
|
|
1822
|
+
if (it != order.begin()) {
|
|
1823
|
+
if ((*merged_ids)[it[-1]][0] >= layer_begins_[layer]) --it;
|
|
1824
|
+
}
|
|
1825
|
+
S2_DCHECK_EQ(layer, input_edge_layer((*merged_ids)[it[0]][0]));
|
|
1826
|
+
(*merged_ids)[it[0]].push_back(degenerate_id);
|
|
1827
|
+
}
|
|
1828
|
+
}
|