@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,1585 @@
|
|
|
1
|
+
// Copyright 2012 Google Inc. All Rights Reserved.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS-IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
//
|
|
15
|
+
|
|
16
|
+
// Author: ericv@google.com (Eric Veach)
|
|
17
|
+
|
|
18
|
+
#include "s2/mutable_s2shape_index.h"
|
|
19
|
+
|
|
20
|
+
#include <algorithm>
|
|
21
|
+
#include <atomic>
|
|
22
|
+
#include <cmath>
|
|
23
|
+
|
|
24
|
+
#include "s2/base/casts.h"
|
|
25
|
+
#include "s2/base/commandlineflags.h"
|
|
26
|
+
#include "s2/base/spinlock.h"
|
|
27
|
+
#include "s2/encoded_s2cell_id_vector.h"
|
|
28
|
+
#include "s2/encoded_string_vector.h"
|
|
29
|
+
#include "s2/r1interval.h"
|
|
30
|
+
#include "s2/r2.h"
|
|
31
|
+
#include "s2/r2rect.h"
|
|
32
|
+
#include "s2/s2cell_id.h"
|
|
33
|
+
#include "s2/s2cell_union.h"
|
|
34
|
+
#include "s2/s2coords.h"
|
|
35
|
+
#include "s2/s2edge_clipping.h"
|
|
36
|
+
#include "s2/s2edge_crosser.h"
|
|
37
|
+
#include "s2/s2metrics.h"
|
|
38
|
+
#include "s2/s2padded_cell.h"
|
|
39
|
+
#include "s2/s2pointutil.h"
|
|
40
|
+
#include "s2/s2shapeutil_contains_brute_force.h"
|
|
41
|
+
|
|
42
|
+
using std::fabs;
|
|
43
|
+
using std::max;
|
|
44
|
+
using std::unique_ptr;
|
|
45
|
+
using std::vector;
|
|
46
|
+
|
|
47
|
+
// FLAGS_s2shape_index_default_max_edges_per_cell
|
|
48
|
+
//
|
|
49
|
+
// The default maximum number of edges per cell (not counting "long" edges).
|
|
50
|
+
// If a cell has more than this many edges, and it is not a leaf cell, then it
|
|
51
|
+
// is subdivided. This flag can be overridden via MutableS2ShapeIndex::Options.
|
|
52
|
+
// Reasonable values range from 10 to about 50 or so.
|
|
53
|
+
S2_DEFINE_int32(
|
|
54
|
+
s2shape_index_default_max_edges_per_cell, 10,
|
|
55
|
+
"Default maximum number of edges (not counting 'long' edges) per cell; "
|
|
56
|
+
"reasonable values range from 10 to 50. Small values makes queries "
|
|
57
|
+
"faster, while large values make construction faster and use less "
|
|
58
|
+
"memory.");
|
|
59
|
+
|
|
60
|
+
// FLAGS_s2shape_index_tmp_memory_budget_mb
|
|
61
|
+
//
|
|
62
|
+
// Attempt to limit the amount of temporary memory allocated while building or
|
|
63
|
+
// updating a MutableS2ShapeIndex to at most this value. This is achieved by
|
|
64
|
+
// splitting the updates into multiple batches when necessary. (The memory
|
|
65
|
+
// required is proportional to the number of edges being updated at once.)
|
|
66
|
+
//
|
|
67
|
+
// Note that this limit is not a hard guarantee, for several reasons:
|
|
68
|
+
// (1) the memory estimates are only approximations;
|
|
69
|
+
// (2) all edges in a given shape are added or removed at once, so shapes
|
|
70
|
+
// with huge numbers of edges may exceed the budget;
|
|
71
|
+
// (3) shapes being removed are always processed in a single batch. (This
|
|
72
|
+
// could be fixed, but it seems better to keep the code simpler for now.)
|
|
73
|
+
S2_DEFINE_int32(
|
|
74
|
+
s2shape_index_tmp_memory_budget_mb, 100,
|
|
75
|
+
"Attempts to limit the amount of temporary memory used by "
|
|
76
|
+
"MutableS2ShapeIndex when creating or updating very large indexes "
|
|
77
|
+
"to at most this value. If more memory than this is needed, updates "
|
|
78
|
+
"will automatically be split into batches internally.");
|
|
79
|
+
|
|
80
|
+
// FLAGS_s2shape_index_cell_size_to_long_edge_ratio
|
|
81
|
+
//
|
|
82
|
+
// The cell size relative to the length of an edge at which it is first
|
|
83
|
+
// considered to be "long". Long edges do not contribute toward the decision
|
|
84
|
+
// to subdivide a cell further. For example, a value of 2.0 means that the
|
|
85
|
+
// cell must be at least twice the size of the edge in order for that edge to
|
|
86
|
+
// be counted. There are two reasons for not counting long edges: (1) such
|
|
87
|
+
// edges typically need to be propagated to several children, which increases
|
|
88
|
+
// time and memory costs without much benefit, and (2) in pathological cases,
|
|
89
|
+
// many long edges close together could force subdivision to continue all the
|
|
90
|
+
// way to the leaf cell level.
|
|
91
|
+
S2_DEFINE_double(
|
|
92
|
+
s2shape_index_cell_size_to_long_edge_ratio, 1.0,
|
|
93
|
+
"The cell size relative to the length of an edge at which it is first "
|
|
94
|
+
"considered to be 'long'. Long edges do not contribute to the decision "
|
|
95
|
+
"to subdivide a cell further. The size and speed of the index are "
|
|
96
|
+
"typically not very sensitive to this parameter. Reasonable values range "
|
|
97
|
+
"from 0.1 to 10, with smaller values causing more aggressive subdivision "
|
|
98
|
+
"of long edges grouped closely together.");
|
|
99
|
+
|
|
100
|
+
// The total error when clipping an edge comes from two sources:
|
|
101
|
+
// (1) Clipping the original spherical edge to a cube face (the "face edge").
|
|
102
|
+
// The maximum error in this step is S2::kFaceClipErrorUVCoord.
|
|
103
|
+
// (2) Clipping the face edge to the u- or v-coordinate of a cell boundary.
|
|
104
|
+
// The maximum error in this step is S2::kEdgeClipErrorUVCoord.
|
|
105
|
+
// Finally, since we encounter the same errors when clipping query edges, we
|
|
106
|
+
// double the total error so that we only need to pad edges during indexing
|
|
107
|
+
// and not at query time.
|
|
108
|
+
const double MutableS2ShapeIndex::kCellPadding =
|
|
109
|
+
2 * (S2::kFaceClipErrorUVCoord + S2::kEdgeClipErrorUVCoord);
|
|
110
|
+
|
|
111
|
+
MutableS2ShapeIndex::Options::Options()
|
|
112
|
+
: max_edges_per_cell_(FLAGS_s2shape_index_default_max_edges_per_cell) {
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
void MutableS2ShapeIndex::Options::set_max_edges_per_cell(
|
|
116
|
+
int max_edges_per_cell) {
|
|
117
|
+
max_edges_per_cell_ = max_edges_per_cell;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
bool MutableS2ShapeIndex::Iterator::Locate(const S2Point& target) {
|
|
121
|
+
return LocateImpl(target, this);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
MutableS2ShapeIndex::CellRelation MutableS2ShapeIndex::Iterator::Locate(
|
|
125
|
+
S2CellId target) {
|
|
126
|
+
return LocateImpl(target, this);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const S2ShapeIndexCell* MutableS2ShapeIndex::Iterator::GetCell() const {
|
|
130
|
+
S2_LOG(DFATAL) << "Should never be called";
|
|
131
|
+
return nullptr;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
unique_ptr<MutableS2ShapeIndex::IteratorBase>
|
|
135
|
+
MutableS2ShapeIndex::Iterator::Clone() const {
|
|
136
|
+
return absl::make_unique<Iterator>(*this);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
void MutableS2ShapeIndex::Iterator::Copy(const IteratorBase& other) {
|
|
140
|
+
*this = *down_cast<const Iterator*>(&other);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Defines the initial focus point of MutableS2ShapeIndex::InteriorTracker
|
|
144
|
+
// (the start of the S2CellId space-filling curve).
|
|
145
|
+
//
|
|
146
|
+
// TODO(ericv): Move InteriorTracker here to avoid the need for this method.
|
|
147
|
+
static S2Point kInteriorTrackerOrigin() {
|
|
148
|
+
return S2::FaceUVtoXYZ(0, -1, -1).Normalize();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
MutableS2ShapeIndex::MutableS2ShapeIndex()
|
|
152
|
+
: index_status_(FRESH) {
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
MutableS2ShapeIndex::MutableS2ShapeIndex(const Options& options)
|
|
156
|
+
: options_(options),
|
|
157
|
+
index_status_(FRESH) {
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
void MutableS2ShapeIndex::Init(const Options& options) {
|
|
161
|
+
S2_DCHECK(shapes_.empty());
|
|
162
|
+
options_ = options;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
MutableS2ShapeIndex::~MutableS2ShapeIndex() {
|
|
166
|
+
Clear();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
void MutableS2ShapeIndex::Minimize() {
|
|
170
|
+
// TODO(ericv): Implement. In theory we should be able to discard the
|
|
171
|
+
// entire index and rebuild it the next time it is needed.
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
int MutableS2ShapeIndex::Add(unique_ptr<S2Shape> shape) {
|
|
175
|
+
// Additions are processed lazily by ApplyUpdates().
|
|
176
|
+
const int id = shapes_.size();
|
|
177
|
+
shape->id_ = id;
|
|
178
|
+
shapes_.push_back(std::move(shape));
|
|
179
|
+
index_status_.store(STALE, std::memory_order_relaxed);
|
|
180
|
+
return id;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
unique_ptr<S2Shape> MutableS2ShapeIndex::Release(int shape_id) {
|
|
184
|
+
// This class updates itself lazily, because it is much more efficient to
|
|
185
|
+
// process additions and removals in batches. However this means that when
|
|
186
|
+
// a shape is removed, we need to make a copy of all its edges, since the
|
|
187
|
+
// client is free to delete "shape" once this call is finished.
|
|
188
|
+
|
|
189
|
+
S2_DCHECK(shapes_[shape_id] != nullptr);
|
|
190
|
+
auto shape = std::move(shapes_[shape_id]);
|
|
191
|
+
if (shape_id >= pending_additions_begin_) {
|
|
192
|
+
// We are removing a shape that has not yet been added to the index,
|
|
193
|
+
// so there is nothing else to do.
|
|
194
|
+
} else {
|
|
195
|
+
if (!pending_removals_) {
|
|
196
|
+
pending_removals_.reset(new vector<RemovedShape>);
|
|
197
|
+
}
|
|
198
|
+
// We build the new RemovedShape in place, since it includes a potentially
|
|
199
|
+
// large vector of edges that might be expensive to copy.
|
|
200
|
+
pending_removals_->push_back(RemovedShape());
|
|
201
|
+
RemovedShape* removed = &pending_removals_->back();
|
|
202
|
+
removed->shape_id = shape->id();
|
|
203
|
+
removed->has_interior = (shape->dimension() == 2);
|
|
204
|
+
removed->contains_tracker_origin =
|
|
205
|
+
s2shapeutil::ContainsBruteForce(*shape, kInteriorTrackerOrigin());
|
|
206
|
+
int num_edges = shape->num_edges();
|
|
207
|
+
removed->edges.reserve(num_edges);
|
|
208
|
+
for (int e = 0; e < num_edges; ++e) {
|
|
209
|
+
removed->edges.push_back(shape->edge(e));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
index_status_.store(STALE, std::memory_order_relaxed);
|
|
213
|
+
return shape;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
vector<unique_ptr<S2Shape>> MutableS2ShapeIndex::ReleaseAll() {
|
|
217
|
+
Iterator it;
|
|
218
|
+
for (it.InitStale(this, S2ShapeIndex::BEGIN); !it.done(); it.Next()) {
|
|
219
|
+
delete &it.cell();
|
|
220
|
+
}
|
|
221
|
+
cell_map_.clear();
|
|
222
|
+
pending_additions_begin_ = 0;
|
|
223
|
+
pending_removals_.reset();
|
|
224
|
+
S2_DCHECK(update_state_ == nullptr);
|
|
225
|
+
index_status_.store(FRESH, std::memory_order_relaxed);
|
|
226
|
+
vector<unique_ptr<S2Shape>> result;
|
|
227
|
+
result.swap(shapes_);
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
void MutableS2ShapeIndex::Clear() {
|
|
232
|
+
ReleaseAll();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// FaceEdge and ClippedEdge store temporary edge data while the index is being
|
|
236
|
+
// updated. FaceEdge represents an edge that has been projected onto a given
|
|
237
|
+
// face, while ClippedEdge represents the portion of that edge that has been
|
|
238
|
+
// clipped to a given S2Cell.
|
|
239
|
+
//
|
|
240
|
+
// While it would be possible to combine all the edge information into one
|
|
241
|
+
// structure, there are two good reasons for separating it:
|
|
242
|
+
//
|
|
243
|
+
// - Memory usage. Separating the two classes means that we only need to
|
|
244
|
+
// store one copy of the per-face data no matter how many times an edge is
|
|
245
|
+
// subdivided, and it also lets us delay computing bounding boxes until
|
|
246
|
+
// they are needed for processing each face (when the dataset spans
|
|
247
|
+
// multiple faces).
|
|
248
|
+
//
|
|
249
|
+
// - Performance. UpdateEdges is significantly faster on large polygons when
|
|
250
|
+
// the data is separated, because it often only needs to access the data in
|
|
251
|
+
// ClippedEdge and this data is cached more successfully.
|
|
252
|
+
|
|
253
|
+
struct MutableS2ShapeIndex::FaceEdge {
|
|
254
|
+
int32 shape_id; // The shape that this edge belongs to
|
|
255
|
+
int32 edge_id; // Edge id within that shape
|
|
256
|
+
int32 max_level; // Not desirable to subdivide this edge beyond this level
|
|
257
|
+
bool has_interior; // Belongs to a shape of dimension 2.
|
|
258
|
+
R2Point a, b; // The edge endpoints, clipped to a given face
|
|
259
|
+
S2Shape::Edge edge; // The edge endpoints
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
struct MutableS2ShapeIndex::ClippedEdge {
|
|
263
|
+
const FaceEdge* face_edge; // The original unclipped edge
|
|
264
|
+
R2Rect bound; // Bounding box for the clipped portion
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// Given a set of shapes, InteriorTracker keeps track of which shapes contain
|
|
268
|
+
// a particular point (the "focus"). It provides an efficient way to move the
|
|
269
|
+
// focus from one point to another and incrementally update the set of shapes
|
|
270
|
+
// which contain it. We use this to compute which shapes contain the center
|
|
271
|
+
// of every S2CellId in the index, by advancing the focus from one cell center
|
|
272
|
+
// to the next.
|
|
273
|
+
//
|
|
274
|
+
// Initially the focus is at the start of the S2CellId space-filling curve.
|
|
275
|
+
// We then visit all the cells that are being added to the MutableS2ShapeIndex
|
|
276
|
+
// in increasing order of S2CellId. For each cell, we draw two edges: one
|
|
277
|
+
// from the entry vertex to the center, and another from the center to the
|
|
278
|
+
// exit vertex (where "entry" and "exit" refer to the points where the
|
|
279
|
+
// space-filling curve enters and exits the cell). By counting edge crossings
|
|
280
|
+
// we can incrementally compute which shapes contain the cell center. Note
|
|
281
|
+
// that the same set of shapes will always contain the exit point of one cell
|
|
282
|
+
// and the entry point of the next cell in the index, because either (a) these
|
|
283
|
+
// two points are actually the same, or (b) the intervening cells in S2CellId
|
|
284
|
+
// order are all empty, and therefore there are no edge crossings if we follow
|
|
285
|
+
// this path from one cell to the other.
|
|
286
|
+
class MutableS2ShapeIndex::InteriorTracker {
|
|
287
|
+
public:
|
|
288
|
+
// Constructs the InteriorTracker. You must call AddShape() for each shape
|
|
289
|
+
// that will be tracked before calling MoveTo() or DrawTo().
|
|
290
|
+
InteriorTracker();
|
|
291
|
+
|
|
292
|
+
// Returns the initial focus point when the InteriorTracker is constructed
|
|
293
|
+
// (corresponding to the start of the S2CellId space-filling curve).
|
|
294
|
+
static S2Point Origin();
|
|
295
|
+
|
|
296
|
+
// Returns the current focus point (see above).
|
|
297
|
+
const S2Point& focus() { return b_; }
|
|
298
|
+
|
|
299
|
+
// Returns true if any shapes are being tracked.
|
|
300
|
+
bool is_active() const { return is_active_; }
|
|
301
|
+
|
|
302
|
+
// Adds a shape whose interior should be tracked. "is_inside" indicates
|
|
303
|
+
// whether the current focus point is inside the shape. Alternatively, if
|
|
304
|
+
// the focus point is in the process of being moved (via MoveTo/DrawTo), you
|
|
305
|
+
// can also specify "is_inside" at the old focus point and call TestEdge()
|
|
306
|
+
// for every edge of the shape that might cross the current DrawTo() line.
|
|
307
|
+
// This updates the state to correspond to the new focus point.
|
|
308
|
+
//
|
|
309
|
+
// REQUIRES: shape->dimension() == 2
|
|
310
|
+
void AddShape(int32 shape_id, bool is_inside);
|
|
311
|
+
|
|
312
|
+
// Moves the focus to the given point. This method should only be used when
|
|
313
|
+
// it is known that there are no edge crossings between the old and new
|
|
314
|
+
// focus locations; otherwise use DrawTo().
|
|
315
|
+
void MoveTo(const S2Point& b) { b_ = b; }
|
|
316
|
+
|
|
317
|
+
// Moves the focus to the given point. After this method is called,
|
|
318
|
+
// TestEdge() should be called with all edges that may cross the line
|
|
319
|
+
// segment between the old and new focus locations.
|
|
320
|
+
void DrawTo(const S2Point& b);
|
|
321
|
+
|
|
322
|
+
// Indicates that the given edge of the given shape may cross the line
|
|
323
|
+
// segment between the old and new focus locations (see DrawTo).
|
|
324
|
+
// REQUIRES: shape->dimension() == 2
|
|
325
|
+
inline void TestEdge(int32 shape_id, const S2Shape::Edge& edge);
|
|
326
|
+
|
|
327
|
+
// The set of shape ids that contain the current focus.
|
|
328
|
+
const ShapeIdSet& shape_ids() const { return shape_ids_; }
|
|
329
|
+
|
|
330
|
+
// Indicates that the last argument to MoveTo() or DrawTo() was the entry
|
|
331
|
+
// vertex of the given S2CellId, i.e. the tracker is positioned at the start
|
|
332
|
+
// of this cell. By using this method together with at_cellid(), the caller
|
|
333
|
+
// can avoid calling MoveTo() in cases where the exit vertex of the previous
|
|
334
|
+
// cell is the same as the entry vertex of the current cell.
|
|
335
|
+
void set_next_cellid(S2CellId next_cellid) {
|
|
336
|
+
next_cellid_ = next_cellid.range_min();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Returns true if the focus is already at the entry vertex of the given
|
|
340
|
+
// S2CellId (provided that the caller calls set_next_cellid() as each cell
|
|
341
|
+
// is processed).
|
|
342
|
+
bool at_cellid(S2CellId cellid) const {
|
|
343
|
+
return cellid.range_min() == next_cellid_;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Makes an internal copy of the state for shape ids below the given limit,
|
|
347
|
+
// and then clear the state for those shapes. This is used during
|
|
348
|
+
// incremental updates to track the state of added and removed shapes
|
|
349
|
+
// separately.
|
|
350
|
+
void SaveAndClearStateBefore(int32 limit_shape_id);
|
|
351
|
+
|
|
352
|
+
// Restores the state previously saved by SaveAndClearStateBefore(). This
|
|
353
|
+
// only affects the state for shape_ids below "limit_shape_id".
|
|
354
|
+
void RestoreStateBefore(int32 limit_shape_id);
|
|
355
|
+
|
|
356
|
+
private:
|
|
357
|
+
// Removes "shape_id" from shape_ids_ if it exists, otherwise insert it.
|
|
358
|
+
void ToggleShape(int shape_id);
|
|
359
|
+
|
|
360
|
+
// Returns a pointer to the first entry "x" where x >= shape_id.
|
|
361
|
+
ShapeIdSet::iterator lower_bound(int32 shape_id);
|
|
362
|
+
|
|
363
|
+
bool is_active_;
|
|
364
|
+
S2Point a_, b_;
|
|
365
|
+
S2CellId next_cellid_;
|
|
366
|
+
S2EdgeCrosser crosser_;
|
|
367
|
+
ShapeIdSet shape_ids_;
|
|
368
|
+
|
|
369
|
+
// Shape ids saved by SaveAndClearStateBefore(). The state is never saved
|
|
370
|
+
// recursively so we don't need to worry about maintaining a stack.
|
|
371
|
+
ShapeIdSet saved_ids_;
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
// As shapes are added, we compute which ones contain the start of the
|
|
375
|
+
// S2CellId space-filling curve by drawing an edge from S2::Origin() to this
|
|
376
|
+
// point and counting how many shape edges cross this edge.
|
|
377
|
+
MutableS2ShapeIndex::InteriorTracker::InteriorTracker()
|
|
378
|
+
: is_active_(false), b_(Origin()),
|
|
379
|
+
next_cellid_(S2CellId::Begin(S2CellId::kMaxLevel)) {
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
S2Point MutableS2ShapeIndex::InteriorTracker::Origin() {
|
|
383
|
+
// The start of the S2CellId space-filling curve.
|
|
384
|
+
return S2::FaceUVtoXYZ(0, -1, -1).Normalize();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
void MutableS2ShapeIndex::InteriorTracker::AddShape(int32 shape_id,
|
|
388
|
+
bool contains_focus) {
|
|
389
|
+
is_active_ = true;
|
|
390
|
+
if (contains_focus) {
|
|
391
|
+
ToggleShape(shape_id);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
void MutableS2ShapeIndex::InteriorTracker::ToggleShape(int shape_id) {
|
|
396
|
+
// Since shape_ids_.size() is typically *very* small (0, 1, or 2), it turns
|
|
397
|
+
// out to be significantly faster to maintain a sorted array rather than
|
|
398
|
+
// using an STL set or btree_set.
|
|
399
|
+
if (shape_ids_.empty()) {
|
|
400
|
+
shape_ids_.push_back(shape_id);
|
|
401
|
+
} else if (shape_ids_[0] == shape_id) {
|
|
402
|
+
shape_ids_.erase(shape_ids_.begin());
|
|
403
|
+
} else {
|
|
404
|
+
ShapeIdSet::iterator pos = shape_ids_.begin();
|
|
405
|
+
while (*pos < shape_id) {
|
|
406
|
+
if (++pos == shape_ids_.end()) {
|
|
407
|
+
shape_ids_.push_back(shape_id);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (*pos == shape_id) {
|
|
412
|
+
shape_ids_.erase(pos);
|
|
413
|
+
} else {
|
|
414
|
+
shape_ids_.insert(pos, shape_id);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
void MutableS2ShapeIndex::InteriorTracker::DrawTo(const S2Point& b) {
|
|
420
|
+
a_ = b_;
|
|
421
|
+
b_ = b;
|
|
422
|
+
crosser_.Init(&a_, &b_);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
inline void MutableS2ShapeIndex::InteriorTracker::TestEdge(
|
|
426
|
+
int32 shape_id, const S2Shape::Edge& edge) {
|
|
427
|
+
if (crosser_.EdgeOrVertexCrossing(&edge.v0, &edge.v1)) {
|
|
428
|
+
ToggleShape(shape_id);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Like std::lower_bound(shape_ids_.begin(), shape_ids_.end(), shape_id), but
|
|
433
|
+
// implemented with linear rather than binary search because the number of
|
|
434
|
+
// shapes being tracked is typically very small.
|
|
435
|
+
inline MutableS2ShapeIndex::ShapeIdSet::iterator
|
|
436
|
+
MutableS2ShapeIndex::InteriorTracker::lower_bound(int32 shape_id) {
|
|
437
|
+
ShapeIdSet::iterator pos = shape_ids_.begin();
|
|
438
|
+
while (pos != shape_ids_.end() && *pos < shape_id) { ++pos; }
|
|
439
|
+
return pos;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
void MutableS2ShapeIndex::InteriorTracker::SaveAndClearStateBefore(
|
|
443
|
+
int32 limit_shape_id) {
|
|
444
|
+
S2_DCHECK(saved_ids_.empty());
|
|
445
|
+
ShapeIdSet::iterator limit = lower_bound(limit_shape_id);
|
|
446
|
+
saved_ids_.assign(shape_ids_.begin(), limit);
|
|
447
|
+
shape_ids_.erase(shape_ids_.begin(), limit);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
void MutableS2ShapeIndex::InteriorTracker::RestoreStateBefore(
|
|
451
|
+
int32 limit_shape_id) {
|
|
452
|
+
shape_ids_.erase(shape_ids_.begin(), lower_bound(limit_shape_id));
|
|
453
|
+
shape_ids_.insert(shape_ids_.begin(), saved_ids_.begin(), saved_ids_.end());
|
|
454
|
+
saved_ids_.clear();
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Apply any pending updates in a thread-safe way.
|
|
458
|
+
void MutableS2ShapeIndex::ApplyUpdatesThreadSafe() {
|
|
459
|
+
lock_.Lock();
|
|
460
|
+
if (index_status_.load(std::memory_order_relaxed) == FRESH) {
|
|
461
|
+
lock_.Unlock();
|
|
462
|
+
} else if (index_status_.load(std::memory_order_relaxed) == UPDATING) {
|
|
463
|
+
// Wait until the updating thread is finished. We do this by attempting
|
|
464
|
+
// to lock a mutex that is held by the updating thread. When this mutex
|
|
465
|
+
// is unlocked the index_status_ is guaranteed to be FRESH.
|
|
466
|
+
++update_state_->num_waiting;
|
|
467
|
+
lock_.Unlock();
|
|
468
|
+
update_state_->wait_mutex.Lock();
|
|
469
|
+
lock_.Lock();
|
|
470
|
+
--update_state_->num_waiting;
|
|
471
|
+
UnlockAndSignal(); // Notify other waiting threads.
|
|
472
|
+
} else {
|
|
473
|
+
S2_DCHECK_EQ(STALE, index_status_);
|
|
474
|
+
index_status_.store(UPDATING, std::memory_order_relaxed);
|
|
475
|
+
// Allocate the extra state needed for thread synchronization. We keep
|
|
476
|
+
// the spinlock held while doing this, because (1) memory allocation is
|
|
477
|
+
// fast, so the chance of a context switch while holding the lock is low;
|
|
478
|
+
// (2) by far the most common situation is that there is no contention,
|
|
479
|
+
// and this saves an extra lock and unlock step; (3) even in the rare case
|
|
480
|
+
// where there is contention, the main side effect is that some other
|
|
481
|
+
// thread will burn a few CPU cycles rather than sleeping.
|
|
482
|
+
update_state_.reset(new UpdateState);
|
|
483
|
+
// lock_.Lock wait_mutex *before* calling Unlock() to ensure that all other
|
|
484
|
+
// threads will block on it.
|
|
485
|
+
update_state_->wait_mutex.Lock();
|
|
486
|
+
// Release the spinlock before doing any real work.
|
|
487
|
+
lock_.Unlock();
|
|
488
|
+
ApplyUpdatesInternal();
|
|
489
|
+
lock_.Lock();
|
|
490
|
+
// index_status_ can be updated to FRESH only while locked *and* using
|
|
491
|
+
// an atomic store operation, so that MaybeApplyUpdates() can check
|
|
492
|
+
// whether the index is FRESH without acquiring the spinlock.
|
|
493
|
+
index_status_.store(FRESH, std::memory_order_release);
|
|
494
|
+
UnlockAndSignal(); // Notify any waiting threads.
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Releases lock_ and wakes up any waiting threads by releasing wait_mutex.
|
|
499
|
+
// If this was the last waiting thread, also deletes update_state_.
|
|
500
|
+
// REQUIRES: lock_ is held.
|
|
501
|
+
// REQUIRES: wait_mutex is held.
|
|
502
|
+
inline void MutableS2ShapeIndex::UnlockAndSignal() {
|
|
503
|
+
S2_DCHECK_EQ(FRESH, index_status_);
|
|
504
|
+
int num_waiting = update_state_->num_waiting;
|
|
505
|
+
lock_.Unlock();
|
|
506
|
+
// Allow another waiting thread to proceed. Note that no new threads can
|
|
507
|
+
// start waiting because the index_status_ is now FRESH, and the caller is
|
|
508
|
+
// required to prevent any new mutations from occurring while these const
|
|
509
|
+
// methods are running.
|
|
510
|
+
//
|
|
511
|
+
// We need to unlock wait_mutex before destroying it even if there are no
|
|
512
|
+
// waiting threads.
|
|
513
|
+
update_state_->wait_mutex.Unlock();
|
|
514
|
+
if (num_waiting == 0) {
|
|
515
|
+
update_state_.reset();
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
void MutableS2ShapeIndex::ForceBuild() {
|
|
520
|
+
// No locks required because this is not a const method. It is the client's
|
|
521
|
+
// responsibility to ensure correct thread synchronization.
|
|
522
|
+
if (index_status_.load(std::memory_order_relaxed) != FRESH) {
|
|
523
|
+
ApplyUpdatesInternal();
|
|
524
|
+
index_status_.store(FRESH, std::memory_order_relaxed);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// A BatchDescriptor represents a set of pending updates that will be applied
|
|
529
|
+
// at the same time. The batch consists of all updates with shape ids between
|
|
530
|
+
// the current value of "ShapeIndex::pending_additions_begin_" (inclusive) and
|
|
531
|
+
// "additions_end" (exclusive). The first batch to be processed also
|
|
532
|
+
// implicitly includes all shapes being removed. "num_edges" is the total
|
|
533
|
+
// number of edges that will be added or removed in this batch.
|
|
534
|
+
struct MutableS2ShapeIndex::BatchDescriptor {
|
|
535
|
+
BatchDescriptor(int _additions_end, int _num_edges)
|
|
536
|
+
: additions_end(_additions_end), num_edges(_num_edges) {
|
|
537
|
+
}
|
|
538
|
+
int additions_end;
|
|
539
|
+
int num_edges;
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
// This method updates the index by applying all pending additions and
|
|
543
|
+
// removals. It does *not* update index_status_ (see ApplyUpdatesThreadSafe).
|
|
544
|
+
void MutableS2ShapeIndex::ApplyUpdatesInternal() {
|
|
545
|
+
// Check whether we have so many edges to process that we should process
|
|
546
|
+
// them in multiple batches to save memory. Building the index can use up
|
|
547
|
+
// to 20x as much memory (per edge) as the final index size.
|
|
548
|
+
vector<BatchDescriptor> batches;
|
|
549
|
+
GetUpdateBatches(&batches);
|
|
550
|
+
int i = 0;
|
|
551
|
+
for (const BatchDescriptor& batch : batches) {
|
|
552
|
+
vector<FaceEdge> all_edges[6];
|
|
553
|
+
S2_VLOG(1) << "Batch " << i++ << ": shape_limit=" << batch.additions_end
|
|
554
|
+
<< ", edges=" << batch.num_edges;
|
|
555
|
+
|
|
556
|
+
ReserveSpace(batch, all_edges);
|
|
557
|
+
InteriorTracker tracker;
|
|
558
|
+
if (pending_removals_) {
|
|
559
|
+
// The first batch implicitly includes all shapes being removed.
|
|
560
|
+
for (const auto& pending_removal : *pending_removals_) {
|
|
561
|
+
RemoveShape(pending_removal, all_edges, &tracker);
|
|
562
|
+
}
|
|
563
|
+
pending_removals_.reset(nullptr);
|
|
564
|
+
}
|
|
565
|
+
for (int id = pending_additions_begin_; id < batch.additions_end; ++id) {
|
|
566
|
+
AddShape(id, all_edges, &tracker);
|
|
567
|
+
}
|
|
568
|
+
for (int face = 0; face < 6; ++face) {
|
|
569
|
+
UpdateFaceEdges(face, all_edges[face], &tracker);
|
|
570
|
+
// Save memory by clearing vectors after we are done with them.
|
|
571
|
+
vector<FaceEdge>().swap(all_edges[face]);
|
|
572
|
+
}
|
|
573
|
+
pending_additions_begin_ = batch.additions_end;
|
|
574
|
+
}
|
|
575
|
+
// It is the caller's responsibility to update index_status_.
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Count the number of edges being updated, and break them into several
|
|
579
|
+
// batches if necessary to reduce the amount of memory needed. (See the
|
|
580
|
+
// documentation for FLAGS_s2shape_index_tmp_memory_budget_mb.)
|
|
581
|
+
void MutableS2ShapeIndex::GetUpdateBatches(vector<BatchDescriptor>* batches)
|
|
582
|
+
const {
|
|
583
|
+
// Count the edges being removed and added.
|
|
584
|
+
int num_edges_removed = 0;
|
|
585
|
+
if (pending_removals_) {
|
|
586
|
+
for (const auto& pending_removal : *pending_removals_) {
|
|
587
|
+
num_edges_removed += pending_removal.edges.size();
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
int num_edges_added = 0;
|
|
591
|
+
for (int id = pending_additions_begin_; id < shapes_.size(); ++id) {
|
|
592
|
+
const S2Shape* shape = this->shape(id);
|
|
593
|
+
if (shape == nullptr) continue;
|
|
594
|
+
num_edges_added += shape->num_edges();
|
|
595
|
+
}
|
|
596
|
+
int num_edges = num_edges_removed + num_edges_added;
|
|
597
|
+
|
|
598
|
+
// The following memory estimates are based on heap profiling.
|
|
599
|
+
//
|
|
600
|
+
// The final size of a MutableS2ShapeIndex depends mainly on how finely the
|
|
601
|
+
// index is subdivided, as controlled by Options::max_edges_per_cell() and
|
|
602
|
+
// --s2shape_index_default_max_edges_per_cell. For realistic values of
|
|
603
|
+
// max_edges_per_cell() and shapes with moderate numbers of edges, it is
|
|
604
|
+
// difficult to get much below 8 bytes per edge. [The minimum possible size
|
|
605
|
+
// is 4 bytes per edge (to store a 32-bit edge id in an S2ClippedShape) plus
|
|
606
|
+
// 24 bytes per shape (for the S2ClippedShape itself plus a pointer in the
|
|
607
|
+
// shapes_ vector.]
|
|
608
|
+
//
|
|
609
|
+
// The temporary memory consists mainly of the FaceEdge and ClippedEdge
|
|
610
|
+
// structures plus a ClippedEdge pointer for every level of recursive
|
|
611
|
+
// subdivision. For very large indexes this can be 200 bytes per edge.
|
|
612
|
+
const size_t kFinalBytesPerEdge = 8;
|
|
613
|
+
const size_t kTmpBytesPerEdge = 200;
|
|
614
|
+
const size_t kTmpMemoryBudgetBytes =
|
|
615
|
+
static_cast<size_t>(FLAGS_s2shape_index_tmp_memory_budget_mb) << 20;
|
|
616
|
+
|
|
617
|
+
// We arbitrarily limit the number of batches just as a safety measure.
|
|
618
|
+
// With the current default memory budget of 100 MB, this limit is not
|
|
619
|
+
// reached even when building an index of 350 million edges.
|
|
620
|
+
const int kMaxUpdateBatches = 100;
|
|
621
|
+
|
|
622
|
+
if (num_edges * kTmpBytesPerEdge <= kTmpMemoryBudgetBytes) {
|
|
623
|
+
// We can update all edges at once without exceeding kTmpMemoryBudgetBytes.
|
|
624
|
+
batches->push_back(BatchDescriptor(shapes_.size(), num_edges));
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
// Otherwise, break the updates into up to several batches, where the size
|
|
628
|
+
// of each batch is chosen so that all batches use approximately the same
|
|
629
|
+
// high-water memory. GetBatchSizes() returns the recommended number of
|
|
630
|
+
// edges in each batch.
|
|
631
|
+
vector<int> batch_sizes;
|
|
632
|
+
GetBatchSizes(num_edges, kMaxUpdateBatches, kFinalBytesPerEdge,
|
|
633
|
+
kTmpBytesPerEdge, kTmpMemoryBudgetBytes, &batch_sizes);
|
|
634
|
+
|
|
635
|
+
// We always process removed edges in a single batch, since (1) they already
|
|
636
|
+
// take up a lot of memory because we have copied all their edges, and (2)
|
|
637
|
+
// AbsorbIndexCell() uses (shapes_[id] == nullptr) to detect when a shape is
|
|
638
|
+
// being removed, so in order to split the removals into batches we would
|
|
639
|
+
// need a different approach (e.g., temporarily add fake entries to shapes_
|
|
640
|
+
// and restore them back to nullptr as shapes are actually removed).
|
|
641
|
+
num_edges = 0;
|
|
642
|
+
if (pending_removals_) {
|
|
643
|
+
num_edges += num_edges_removed;
|
|
644
|
+
if (num_edges >= batch_sizes[0]) {
|
|
645
|
+
batches->push_back(BatchDescriptor(pending_additions_begin_, num_edges));
|
|
646
|
+
num_edges = 0;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
// Keep adding shapes to each batch until the recommended number of edges
|
|
650
|
+
// for that batch is reached, then move on to the next batch.
|
|
651
|
+
for (int id = pending_additions_begin_; id < shapes_.size(); ++id) {
|
|
652
|
+
const S2Shape* shape = this->shape(id);
|
|
653
|
+
if (shape == nullptr) continue;
|
|
654
|
+
num_edges += shape->num_edges();
|
|
655
|
+
if (num_edges >= batch_sizes[batches->size()]) {
|
|
656
|
+
batches->push_back(BatchDescriptor(id + 1, num_edges));
|
|
657
|
+
num_edges = 0;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
// Some shapes have no edges. If a shape with no edges is the last shape to
|
|
661
|
+
// be added or removed, then the final batch may not include it, so we fix
|
|
662
|
+
// that problem here.
|
|
663
|
+
batches->back().additions_end = shapes_.size();
|
|
664
|
+
S2_DCHECK_LE(batches->size(), kMaxUpdateBatches);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Given "num_items" items, each of which uses "tmp_bytes_per_item" while it
|
|
668
|
+
// is being updated but only "final_bytes_per_item" in the end, divide the
|
|
669
|
+
// items into batches that have approximately the same *total* memory usage
|
|
670
|
+
// consisting of the temporary memory needed for the items in the current
|
|
671
|
+
// batch plus the final size of all the items that have already been
|
|
672
|
+
// processed. Use the fewest number of batches (but never more than
|
|
673
|
+
// "max_batches") such that the total memory usage does not exceed the
|
|
674
|
+
// combined final size of all the items plus "tmp_memory_budget_bytes".
|
|
675
|
+
/* static */
|
|
676
|
+
void MutableS2ShapeIndex::GetBatchSizes(int num_items, int max_batches,
|
|
677
|
+
double final_bytes_per_item,
|
|
678
|
+
double tmp_bytes_per_item,
|
|
679
|
+
double tmp_memory_budget_bytes,
|
|
680
|
+
vector<int>* batch_sizes) {
|
|
681
|
+
// This code tries to fit all the data into the same memory space
|
|
682
|
+
// ("total_budget_bytes") at every iteration. The data consists of some
|
|
683
|
+
// number of processed items (at "final_bytes_per_item" each), plus some
|
|
684
|
+
// number being updated (at "tmp_bytes_per_item" each). The space occupied
|
|
685
|
+
// by the items being updated is the "free space". At each iteration, the
|
|
686
|
+
// free space is multiplied by (1 - final_bytes_per_item/tmp_bytes_per_item)
|
|
687
|
+
// as the items are converted into their final form.
|
|
688
|
+
double final_bytes = num_items * final_bytes_per_item;
|
|
689
|
+
double final_bytes_ratio = final_bytes_per_item / tmp_bytes_per_item;
|
|
690
|
+
double free_space_multiplier = 1 - final_bytes_ratio;
|
|
691
|
+
|
|
692
|
+
// The total memory budget is the greater of the final size plus the allowed
|
|
693
|
+
// temporary memory, or the minimum amount of memory required to limit the
|
|
694
|
+
// number of batches to "max_batches".
|
|
695
|
+
double total_budget_bytes = max(
|
|
696
|
+
final_bytes + tmp_memory_budget_bytes,
|
|
697
|
+
final_bytes / (1 - pow(free_space_multiplier, max_batches)));
|
|
698
|
+
|
|
699
|
+
// "max_batch_items" is the number of items in the current batch.
|
|
700
|
+
double max_batch_items = total_budget_bytes / tmp_bytes_per_item;
|
|
701
|
+
batch_sizes->clear();
|
|
702
|
+
for (int i = 0; i + 1 < max_batches && num_items > 0; ++i) {
|
|
703
|
+
int batch_items =
|
|
704
|
+
std::min(num_items, static_cast<int>(max_batch_items + 1));
|
|
705
|
+
batch_sizes->push_back(batch_items);
|
|
706
|
+
num_items -= batch_items;
|
|
707
|
+
max_batch_items *= free_space_multiplier;
|
|
708
|
+
}
|
|
709
|
+
S2_DCHECK_LE(batch_sizes->size(), max_batches);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// Reserve an appropriate amount of space for the top-level face edges in the
|
|
713
|
+
// current batch. This data structure uses about half of the temporary memory
|
|
714
|
+
// needed during index construction. Furthermore, if the arrays are grown via
|
|
715
|
+
// push_back() then up to 10% of the total run time consists of copying data
|
|
716
|
+
// as these arrays grow, so it is worthwhile to preallocate space for them.
|
|
717
|
+
void MutableS2ShapeIndex::ReserveSpace(const BatchDescriptor& batch,
|
|
718
|
+
vector<FaceEdge> all_edges[6]) const {
|
|
719
|
+
// If the number of edges is relatively small, then the fastest approach is
|
|
720
|
+
// to simply reserve space on every face for the maximum possible number of
|
|
721
|
+
// edges. We use a different threshold for this calculation than for
|
|
722
|
+
// deciding when to break updates into batches, because the cost/benefit
|
|
723
|
+
// ratio is different. (Here the only extra expense is that we need to
|
|
724
|
+
// sample the edges to estimate how many edges per face there are.)
|
|
725
|
+
const size_t kMaxCheapBytes = 30 << 20; // 30 MB
|
|
726
|
+
const int kMaxCheapEdges = kMaxCheapBytes / (6 * sizeof(FaceEdge));
|
|
727
|
+
if (batch.num_edges <= kMaxCheapEdges) {
|
|
728
|
+
for (int face = 0; face < 6; ++face) {
|
|
729
|
+
all_edges[face].reserve(batch.num_edges);
|
|
730
|
+
}
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
// Otherwise we estimate the number of edges on each face by taking a random
|
|
734
|
+
// sample. The goal is to come up with an estimate that is fast and
|
|
735
|
+
// accurate for non-pathological geometry. If our estimates happen to be
|
|
736
|
+
// wrong, the vector will still grow automatically - the main side effects
|
|
737
|
+
// are that memory usage will be larger (by up to a factor of 3), and
|
|
738
|
+
// constructing the index will be about 10% slower.
|
|
739
|
+
//
|
|
740
|
+
// Given a desired sample size, we choose equally spaced edges from
|
|
741
|
+
// throughout the entire data set. We use a Bresenham-type algorithm to
|
|
742
|
+
// choose the samples.
|
|
743
|
+
const int kDesiredSampleSize = 10000;
|
|
744
|
+
const int sample_interval = max(1, batch.num_edges / kDesiredSampleSize);
|
|
745
|
+
|
|
746
|
+
// Initialize "edge_id" to be midway through the first sample interval.
|
|
747
|
+
// Because samples are equally spaced the actual sample size may differ
|
|
748
|
+
// slightly from the desired sample size.
|
|
749
|
+
int edge_id = sample_interval / 2;
|
|
750
|
+
const int actual_sample_size = (batch.num_edges + edge_id) / sample_interval;
|
|
751
|
+
int face_count[6] = { 0, 0, 0, 0, 0, 0 };
|
|
752
|
+
if (pending_removals_) {
|
|
753
|
+
for (const RemovedShape& removed : *pending_removals_) {
|
|
754
|
+
edge_id += removed.edges.size();
|
|
755
|
+
while (edge_id >= sample_interval) {
|
|
756
|
+
edge_id -= sample_interval;
|
|
757
|
+
face_count[S2::GetFace(removed.edges[edge_id].v0)] += 1;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
for (int id = pending_additions_begin_; id < batch.additions_end; ++id) {
|
|
762
|
+
const S2Shape* shape = this->shape(id);
|
|
763
|
+
if (shape == nullptr) continue;
|
|
764
|
+
edge_id += shape->num_edges();
|
|
765
|
+
while (edge_id >= sample_interval) {
|
|
766
|
+
edge_id -= sample_interval;
|
|
767
|
+
// For speed, we only count the face containing one endpoint of the
|
|
768
|
+
// edge. In general the edge could span all 6 faces (with padding), but
|
|
769
|
+
// it's not worth the expense to compute this more accurately.
|
|
770
|
+
face_count[S2::GetFace(shape->edge(edge_id).v0)] += 1;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
// Now given the raw face counts, compute a confidence interval such that we
|
|
774
|
+
// will be unlikely to allocate too little space. Computing accurate
|
|
775
|
+
// binomial confidence intervals is expensive and not really necessary.
|
|
776
|
+
// Instead we use a simple approximation:
|
|
777
|
+
// - For any face with at least 1 sample, we use at least a 4-sigma
|
|
778
|
+
// confidence interval. (The chosen width is adequate for the worst case
|
|
779
|
+
// accuracy, which occurs when the face contains approximately 50% of the
|
|
780
|
+
// edges.) Assuming that our sample is representative, the probability of
|
|
781
|
+
// reserving too little space is approximately 1 in 30,000.
|
|
782
|
+
// - For faces with no samples at all, we don't bother reserving space.
|
|
783
|
+
// It is quite likely that such faces are truly empty, so we save time
|
|
784
|
+
// and memory this way. If the face does contain some edges, there will
|
|
785
|
+
// only be a few so it is fine to let the vector grow automatically.
|
|
786
|
+
// On average, we reserve 2% extra space for each face that has geometry.
|
|
787
|
+
|
|
788
|
+
// kMaxSemiWidth is the maximum semi-width over all probabilities p of a
|
|
789
|
+
// 4-sigma binomial confidence interval with a sample size of 10,000.
|
|
790
|
+
const double kMaxSemiWidth = 0.02;
|
|
791
|
+
const double sample_ratio = 1.0 / actual_sample_size;
|
|
792
|
+
for (int face = 0; face < 6; ++face) {
|
|
793
|
+
if (face_count[face] == 0) continue;
|
|
794
|
+
double fraction = sample_ratio * face_count[face] + kMaxSemiWidth;
|
|
795
|
+
all_edges[face].reserve(fraction * batch.num_edges);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// Clip all edges of the given shape to the six cube faces, add the clipped
|
|
800
|
+
// edges to "all_edges", and start tracking its interior if necessary.
|
|
801
|
+
void MutableS2ShapeIndex::AddShape(int id, vector<FaceEdge> all_edges[6],
|
|
802
|
+
InteriorTracker* tracker) const {
|
|
803
|
+
const S2Shape* shape = this->shape(id);
|
|
804
|
+
if (shape == nullptr) {
|
|
805
|
+
return; // This shape has already been removed.
|
|
806
|
+
}
|
|
807
|
+
// Construct a template for the edges to be added.
|
|
808
|
+
FaceEdge edge;
|
|
809
|
+
edge.shape_id = id;
|
|
810
|
+
edge.has_interior = (shape->dimension() == 2);
|
|
811
|
+
if (edge.has_interior) {
|
|
812
|
+
tracker->AddShape(id, s2shapeutil::ContainsBruteForce(*shape,
|
|
813
|
+
tracker->focus()));
|
|
814
|
+
}
|
|
815
|
+
int num_edges = shape->num_edges();
|
|
816
|
+
for (int e = 0; e < num_edges; ++e) {
|
|
817
|
+
edge.edge_id = e;
|
|
818
|
+
edge.edge = shape->edge(e);
|
|
819
|
+
edge.max_level = GetEdgeMaxLevel(edge.edge);
|
|
820
|
+
AddFaceEdge(&edge, all_edges);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
void MutableS2ShapeIndex::RemoveShape(const RemovedShape& removed,
|
|
825
|
+
vector<FaceEdge> all_edges[6],
|
|
826
|
+
InteriorTracker* tracker) const {
|
|
827
|
+
FaceEdge edge;
|
|
828
|
+
edge.edge_id = -1; // Not used or needed for removed edges.
|
|
829
|
+
edge.shape_id = removed.shape_id;
|
|
830
|
+
edge.has_interior = removed.has_interior;
|
|
831
|
+
if (edge.has_interior) {
|
|
832
|
+
tracker->AddShape(edge.shape_id, removed.contains_tracker_origin);
|
|
833
|
+
}
|
|
834
|
+
for (const auto& removed_edge : removed.edges) {
|
|
835
|
+
edge.edge = removed_edge;
|
|
836
|
+
edge.max_level = GetEdgeMaxLevel(edge.edge);
|
|
837
|
+
AddFaceEdge(&edge, all_edges);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
inline void MutableS2ShapeIndex::AddFaceEdge(
|
|
842
|
+
FaceEdge* edge, vector<FaceEdge> all_edges[6]) const {
|
|
843
|
+
// Fast path: both endpoints are on the same face, and are far enough from
|
|
844
|
+
// the edge of the face that don't intersect any (padded) adjacent face.
|
|
845
|
+
int a_face = S2::GetFace(edge->edge.v0);
|
|
846
|
+
if (a_face == S2::GetFace(edge->edge.v1)) {
|
|
847
|
+
S2::ValidFaceXYZtoUV(a_face, edge->edge.v0, &edge->a);
|
|
848
|
+
S2::ValidFaceXYZtoUV(a_face, edge->edge.v1, &edge->b);
|
|
849
|
+
const double kMaxUV = 1 - kCellPadding;
|
|
850
|
+
if (fabs(edge->a[0]) <= kMaxUV && fabs(edge->a[1]) <= kMaxUV &&
|
|
851
|
+
fabs(edge->b[0]) <= kMaxUV && fabs(edge->b[1]) <= kMaxUV) {
|
|
852
|
+
all_edges[a_face].push_back(*edge);
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
// Otherwise we simply clip the edge to all six faces.
|
|
857
|
+
for (int face = 0; face < 6; ++face) {
|
|
858
|
+
if (S2::ClipToPaddedFace(edge->edge.v0, edge->edge.v1, face,
|
|
859
|
+
kCellPadding, &edge->a, &edge->b)) {
|
|
860
|
+
all_edges[face].push_back(*edge);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Return the first level at which the edge will *not* contribute towards
|
|
866
|
+
// the decision to subdivide.
|
|
867
|
+
int MutableS2ShapeIndex::GetEdgeMaxLevel(const S2Shape::Edge& edge) const {
|
|
868
|
+
// Compute the maximum cell size for which this edge is considered "long".
|
|
869
|
+
// The calculation does not need to be perfectly accurate, so we use Norm()
|
|
870
|
+
// rather than Angle() for speed.
|
|
871
|
+
double cell_size = ((edge.v0 - edge.v1).Norm() *
|
|
872
|
+
FLAGS_s2shape_index_cell_size_to_long_edge_ratio);
|
|
873
|
+
// Now return the first level encountered during subdivision where the
|
|
874
|
+
// average cell size is at most "cell_size".
|
|
875
|
+
return S2::kAvgEdge.GetLevelForMaxValue(cell_size);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// EdgeAllocator provides temporary storage for new ClippedEdges that are
|
|
879
|
+
// created during indexing. It is essentially a stack model, where edges are
|
|
880
|
+
// allocated as the recursion does down and freed as it comes back up.
|
|
881
|
+
//
|
|
882
|
+
// It also provides a mutable vector of FaceEdges that is used when
|
|
883
|
+
// incrementally updating the index (see AbsorbIndexCell).
|
|
884
|
+
class MutableS2ShapeIndex::EdgeAllocator {
|
|
885
|
+
public:
|
|
886
|
+
EdgeAllocator() : size_(0) {}
|
|
887
|
+
|
|
888
|
+
// Return a pointer to a newly allocated edge. The EdgeAllocator
|
|
889
|
+
// retains ownership.
|
|
890
|
+
ClippedEdge* NewClippedEdge() {
|
|
891
|
+
if (size_ == clipped_edges_.size()) {
|
|
892
|
+
clipped_edges_.emplace_back(new ClippedEdge);
|
|
893
|
+
}
|
|
894
|
+
return clipped_edges_[size_++].get();
|
|
895
|
+
}
|
|
896
|
+
// Return the number of allocated edges.
|
|
897
|
+
size_t size() const { return size_; }
|
|
898
|
+
|
|
899
|
+
// Reset the allocator to only contain the first "size" allocated edges.
|
|
900
|
+
void Reset(size_t size) { size_ = size; }
|
|
901
|
+
|
|
902
|
+
vector<FaceEdge>* mutable_face_edges() {
|
|
903
|
+
return &face_edges_;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
private:
|
|
907
|
+
// We can't use vector<ClippedEdge> because edges are not allowed to move
|
|
908
|
+
// once they have been allocated. Instead we keep a pool of allocated edges
|
|
909
|
+
// that are all deleted together at the end.
|
|
910
|
+
size_t size_;
|
|
911
|
+
vector<unique_ptr<ClippedEdge>> clipped_edges_;
|
|
912
|
+
|
|
913
|
+
// On the other hand, we can use vector<FaceEdge> because they are allocated
|
|
914
|
+
// only at one level during the recursion (namely, the level at which we
|
|
915
|
+
// absorb an existing index cell).
|
|
916
|
+
vector<FaceEdge> face_edges_;
|
|
917
|
+
|
|
918
|
+
EdgeAllocator(const EdgeAllocator&) = delete;
|
|
919
|
+
void operator=(const EdgeAllocator&) = delete;
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
// Given a face and a vector of edges that intersect that face, add or remove
|
|
923
|
+
// all the edges from the index. (An edge is added if shapes_[id] is not
|
|
924
|
+
// nullptr, and removed otherwise.)
|
|
925
|
+
void MutableS2ShapeIndex::UpdateFaceEdges(int face,
|
|
926
|
+
const vector<FaceEdge>& face_edges,
|
|
927
|
+
InteriorTracker* tracker) {
|
|
928
|
+
int num_edges = face_edges.size();
|
|
929
|
+
if (num_edges == 0 && tracker->shape_ids().empty()) return;
|
|
930
|
+
|
|
931
|
+
// Create the initial ClippedEdge for each FaceEdge. Additional clipped
|
|
932
|
+
// edges are created when edges are split between child cells. We create
|
|
933
|
+
// two arrays, one containing the edge data and another containing pointers
|
|
934
|
+
// to those edges, so that during the recursion we only need to copy
|
|
935
|
+
// pointers in order to propagate an edge to the correct child.
|
|
936
|
+
vector<ClippedEdge> clipped_edge_storage;
|
|
937
|
+
vector<const ClippedEdge*> clipped_edges;
|
|
938
|
+
clipped_edge_storage.reserve(num_edges);
|
|
939
|
+
clipped_edges.reserve(num_edges);
|
|
940
|
+
R2Rect bound = R2Rect::Empty();
|
|
941
|
+
for (int e = 0; e < num_edges; ++e) {
|
|
942
|
+
ClippedEdge clipped;
|
|
943
|
+
clipped.face_edge = &face_edges[e];
|
|
944
|
+
clipped.bound = R2Rect::FromPointPair(face_edges[e].a, face_edges[e].b);
|
|
945
|
+
clipped_edge_storage.push_back(clipped);
|
|
946
|
+
clipped_edges.push_back(&clipped_edge_storage.back());
|
|
947
|
+
bound.AddRect(clipped.bound);
|
|
948
|
+
}
|
|
949
|
+
// Construct the initial face cell containing all the edges, and then update
|
|
950
|
+
// all the edges in the index recursively.
|
|
951
|
+
EdgeAllocator alloc;
|
|
952
|
+
S2CellId face_id = S2CellId::FromFace(face);
|
|
953
|
+
S2PaddedCell pcell(face_id, kCellPadding);
|
|
954
|
+
|
|
955
|
+
// "disjoint_from_index" means that the current cell being processed (and
|
|
956
|
+
// all its descendants) are not already present in the index.
|
|
957
|
+
bool disjoint_from_index = is_first_update();
|
|
958
|
+
if (num_edges > 0) {
|
|
959
|
+
S2CellId shrunk_id = ShrinkToFit(pcell, bound);
|
|
960
|
+
if (shrunk_id != pcell.id()) {
|
|
961
|
+
// All the edges are contained by some descendant of the face cell. We
|
|
962
|
+
// can save a lot of work by starting directly with that cell, but if we
|
|
963
|
+
// are in the interior of at least one shape then we need to create
|
|
964
|
+
// index entries for the cells we are skipping over.
|
|
965
|
+
SkipCellRange(face_id.range_min(), shrunk_id.range_min(),
|
|
966
|
+
tracker, &alloc, disjoint_from_index);
|
|
967
|
+
pcell = S2PaddedCell(shrunk_id, kCellPadding);
|
|
968
|
+
UpdateEdges(pcell, &clipped_edges, tracker, &alloc, disjoint_from_index);
|
|
969
|
+
SkipCellRange(shrunk_id.range_max().next(), face_id.range_max().next(),
|
|
970
|
+
tracker, &alloc, disjoint_from_index);
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
// Otherwise (no edges, or no shrinking is possible), subdivide normally.
|
|
975
|
+
UpdateEdges(pcell, &clipped_edges, tracker, &alloc, disjoint_from_index);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
inline S2CellId MutableS2ShapeIndex::ShrinkToFit(const S2PaddedCell& pcell,
|
|
979
|
+
const R2Rect& bound) const {
|
|
980
|
+
S2CellId shrunk_id = pcell.ShrinkToFit(bound);
|
|
981
|
+
if (!is_first_update() && shrunk_id != pcell.id()) {
|
|
982
|
+
// Don't shrink any smaller than the existing index cells, since we need
|
|
983
|
+
// to combine the new edges with those cells.
|
|
984
|
+
// Use InitStale() to avoid applying updated recursively.
|
|
985
|
+
Iterator iter;
|
|
986
|
+
iter.InitStale(this);
|
|
987
|
+
CellRelation r = iter.Locate(shrunk_id);
|
|
988
|
+
if (r == INDEXED) { shrunk_id = iter.id(); }
|
|
989
|
+
}
|
|
990
|
+
return shrunk_id;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// Skip over the cells in the given range, creating index cells if we are
|
|
994
|
+
// currently in the interior of at least one shape.
|
|
995
|
+
void MutableS2ShapeIndex::SkipCellRange(S2CellId begin, S2CellId end,
|
|
996
|
+
InteriorTracker* tracker,
|
|
997
|
+
EdgeAllocator* alloc,
|
|
998
|
+
bool disjoint_from_index) {
|
|
999
|
+
// If we aren't in the interior of a shape, then skipping over cells is easy.
|
|
1000
|
+
if (tracker->shape_ids().empty()) return;
|
|
1001
|
+
|
|
1002
|
+
// Otherwise generate the list of cell ids that we need to visit, and create
|
|
1003
|
+
// an index entry for each one.
|
|
1004
|
+
for (S2CellId skipped_id : S2CellUnion::FromBeginEnd(begin, end)) {
|
|
1005
|
+
vector<const ClippedEdge*> clipped_edges;
|
|
1006
|
+
UpdateEdges(S2PaddedCell(skipped_id, kCellPadding),
|
|
1007
|
+
&clipped_edges, tracker, alloc, disjoint_from_index);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// Given a cell and a set of ClippedEdges whose bounding boxes intersect that
|
|
1012
|
+
// cell, add or remove all the edges from the index. Temporary space for
|
|
1013
|
+
// edges that need to be subdivided is allocated from the given EdgeAllocator.
|
|
1014
|
+
// "disjoint_from_index" is an optimization hint indicating that cell_map_
|
|
1015
|
+
// does not contain any entries that overlap the given cell.
|
|
1016
|
+
void MutableS2ShapeIndex::UpdateEdges(const S2PaddedCell& pcell,
|
|
1017
|
+
vector<const ClippedEdge*>* edges,
|
|
1018
|
+
InteriorTracker* tracker,
|
|
1019
|
+
EdgeAllocator* alloc,
|
|
1020
|
+
bool disjoint_from_index) {
|
|
1021
|
+
// Cases where an index cell is not needed should be detected before this.
|
|
1022
|
+
S2_DCHECK(!edges->empty() || !tracker->shape_ids().empty());
|
|
1023
|
+
|
|
1024
|
+
// This function is recursive with a maximum recursion depth of 30
|
|
1025
|
+
// (S2CellId::kMaxLevel). Note that using an explicit stack does not seem
|
|
1026
|
+
// to be any faster based on profiling.
|
|
1027
|
+
|
|
1028
|
+
// Incremental updates are handled as follows. All edges being added or
|
|
1029
|
+
// removed are combined together in "edges", and all shapes with interiors
|
|
1030
|
+
// are tracked using "tracker". We subdivide recursively as usual until we
|
|
1031
|
+
// encounter an existing index cell. At this point we "absorb" the index
|
|
1032
|
+
// cell as follows:
|
|
1033
|
+
//
|
|
1034
|
+
// - Edges and shapes that are being removed are deleted from "edges" and
|
|
1035
|
+
// "tracker".
|
|
1036
|
+
// - All remaining edges and shapes from the index cell are added to
|
|
1037
|
+
// "edges" and "tracker".
|
|
1038
|
+
// - Continue subdividing recursively, creating new index cells as needed.
|
|
1039
|
+
// - When the recursion gets back to the cell that was absorbed, we
|
|
1040
|
+
// restore "edges" and "tracker" to their previous state.
|
|
1041
|
+
//
|
|
1042
|
+
// Note that the only reason that we include removed shapes in the recursive
|
|
1043
|
+
// subdivision process is so that we can find all of the index cells that
|
|
1044
|
+
// contain those shapes efficiently, without maintaining an explicit list of
|
|
1045
|
+
// index cells for each shape (which would be expensive in terms of memory).
|
|
1046
|
+
bool index_cell_absorbed = false;
|
|
1047
|
+
if (!disjoint_from_index) {
|
|
1048
|
+
// There may be existing index cells contained inside "pcell". If we
|
|
1049
|
+
// encounter such a cell, we need to combine the edges being updated with
|
|
1050
|
+
// the existing cell contents by "absorbing" the cell.
|
|
1051
|
+
// Use InitStale() to avoid applying updated recursively.
|
|
1052
|
+
Iterator iter;
|
|
1053
|
+
iter.InitStale(this);
|
|
1054
|
+
CellRelation r = iter.Locate(pcell.id());
|
|
1055
|
+
if (r == DISJOINT) {
|
|
1056
|
+
disjoint_from_index = true;
|
|
1057
|
+
} else if (r == INDEXED) {
|
|
1058
|
+
// Absorb the index cell by transferring its contents to "edges" and
|
|
1059
|
+
// deleting it. We also start tracking the interior of any new shapes.
|
|
1060
|
+
AbsorbIndexCell(pcell, iter, edges, tracker, alloc);
|
|
1061
|
+
index_cell_absorbed = true;
|
|
1062
|
+
disjoint_from_index = true;
|
|
1063
|
+
} else {
|
|
1064
|
+
S2_DCHECK_EQ(SUBDIVIDED, r);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// If there are existing index cells below us, then we need to keep
|
|
1069
|
+
// subdividing so that we can merge with those cells. Otherwise,
|
|
1070
|
+
// MakeIndexCell checks if the number of edges is small enough, and creates
|
|
1071
|
+
// an index cell if possible (returning true when it does so).
|
|
1072
|
+
if (!disjoint_from_index || !MakeIndexCell(pcell, *edges, tracker)) {
|
|
1073
|
+
// Reserve space for the edges that will be passed to each child. This is
|
|
1074
|
+
// important since otherwise the running time is dominated by the time
|
|
1075
|
+
// required to grow the vectors. The amount of memory involved is
|
|
1076
|
+
// relatively small, so we simply reserve the maximum space for every child.
|
|
1077
|
+
vector<const ClippedEdge*> child_edges[2][2]; // [i][j]
|
|
1078
|
+
int num_edges = edges->size();
|
|
1079
|
+
for (int i = 0; i < 2; ++i) {
|
|
1080
|
+
for (int j = 0; j < 2; ++j) {
|
|
1081
|
+
child_edges[i][j].reserve(num_edges);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// Remember the current size of the EdgeAllocator so that we can free any
|
|
1086
|
+
// edges that are allocated during edge splitting.
|
|
1087
|
+
size_t alloc_size = alloc->size();
|
|
1088
|
+
|
|
1089
|
+
// Compute the middle of the padded cell, defined as the rectangle in
|
|
1090
|
+
// (u,v)-space that belongs to all four (padded) children. By comparing
|
|
1091
|
+
// against the four boundaries of "middle" we can determine which children
|
|
1092
|
+
// each edge needs to be propagated to.
|
|
1093
|
+
const R2Rect& middle = pcell.middle();
|
|
1094
|
+
|
|
1095
|
+
// Build up a vector edges to be passed to each child cell. The (i,j)
|
|
1096
|
+
// directions are left (i=0), right (i=1), lower (j=0), and upper (j=1).
|
|
1097
|
+
// Note that the vast majority of edges are propagated to a single child.
|
|
1098
|
+
// This case is very fast, consisting of between 2 and 4 floating-point
|
|
1099
|
+
// comparisons and copying one pointer. (ClipVAxis is inline.)
|
|
1100
|
+
for (int e = 0; e < num_edges; ++e) {
|
|
1101
|
+
const ClippedEdge* edge = (*edges)[e];
|
|
1102
|
+
if (edge->bound[0].hi() <= middle[0].lo()) {
|
|
1103
|
+
// Edge is entirely contained in the two left children.
|
|
1104
|
+
ClipVAxis(edge, middle[1], child_edges[0], alloc);
|
|
1105
|
+
} else if (edge->bound[0].lo() >= middle[0].hi()) {
|
|
1106
|
+
// Edge is entirely contained in the two right children.
|
|
1107
|
+
ClipVAxis(edge, middle[1], child_edges[1], alloc);
|
|
1108
|
+
} else if (edge->bound[1].hi() <= middle[1].lo()) {
|
|
1109
|
+
// Edge is entirely contained in the two lower children.
|
|
1110
|
+
child_edges[0][0].push_back(ClipUBound(edge, 1, middle[0].hi(), alloc));
|
|
1111
|
+
child_edges[1][0].push_back(ClipUBound(edge, 0, middle[0].lo(), alloc));
|
|
1112
|
+
} else if (edge->bound[1].lo() >= middle[1].hi()) {
|
|
1113
|
+
// Edge is entirely contained in the two upper children.
|
|
1114
|
+
child_edges[0][1].push_back(ClipUBound(edge, 1, middle[0].hi(), alloc));
|
|
1115
|
+
child_edges[1][1].push_back(ClipUBound(edge, 0, middle[0].lo(), alloc));
|
|
1116
|
+
} else {
|
|
1117
|
+
// The edge bound spans all four children. The edge itself intersects
|
|
1118
|
+
// either three or four (padded) children.
|
|
1119
|
+
const ClippedEdge* left = ClipUBound(edge, 1, middle[0].hi(), alloc);
|
|
1120
|
+
ClipVAxis(left, middle[1], child_edges[0], alloc);
|
|
1121
|
+
const ClippedEdge* right = ClipUBound(edge, 0, middle[0].lo(), alloc);
|
|
1122
|
+
ClipVAxis(right, middle[1], child_edges[1], alloc);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
// Free any memory reserved for children that turned out to be empty. This
|
|
1126
|
+
// step is cheap and reduces peak memory usage by about 10% when building
|
|
1127
|
+
// large indexes (> 10M edges).
|
|
1128
|
+
for (int i = 0; i < 2; ++i) {
|
|
1129
|
+
for (int j = 0; j < 2; ++j) {
|
|
1130
|
+
if (child_edges[i][j].empty()) {
|
|
1131
|
+
vector<const ClippedEdge*>().swap(child_edges[i][j]);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
// Now recursively update the edges in each child. We call the children in
|
|
1136
|
+
// increasing order of S2CellId so that when the index is first constructed,
|
|
1137
|
+
// all insertions into cell_map_ are at the end (which is much faster).
|
|
1138
|
+
for (int pos = 0; pos < 4; ++pos) {
|
|
1139
|
+
int i, j;
|
|
1140
|
+
pcell.GetChildIJ(pos, &i, &j);
|
|
1141
|
+
if (!child_edges[i][j].empty() || !tracker->shape_ids().empty()) {
|
|
1142
|
+
UpdateEdges(S2PaddedCell(pcell, i, j), &child_edges[i][j],
|
|
1143
|
+
tracker, alloc, disjoint_from_index);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
// Free any temporary edges that were allocated during clipping.
|
|
1147
|
+
alloc->Reset(alloc_size);
|
|
1148
|
+
}
|
|
1149
|
+
if (index_cell_absorbed) {
|
|
1150
|
+
// Restore the state for any edges being removed that we are tracking.
|
|
1151
|
+
tracker->RestoreStateBefore(pending_additions_begin_);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// Given an edge and an interval "middle" along the v-axis, clip the edge
|
|
1156
|
+
// against the boundaries of "middle" and add the edge to the corresponding
|
|
1157
|
+
// children.
|
|
1158
|
+
/* static */
|
|
1159
|
+
inline void MutableS2ShapeIndex::ClipVAxis(
|
|
1160
|
+
const ClippedEdge* edge,
|
|
1161
|
+
const R1Interval& middle,
|
|
1162
|
+
vector<const ClippedEdge*> child_edges[2],
|
|
1163
|
+
EdgeAllocator* alloc) {
|
|
1164
|
+
if (edge->bound[1].hi() <= middle.lo()) {
|
|
1165
|
+
// Edge is entirely contained in the lower child.
|
|
1166
|
+
child_edges[0].push_back(edge);
|
|
1167
|
+
} else if (edge->bound[1].lo() >= middle.hi()) {
|
|
1168
|
+
// Edge is entirely contained in the upper child.
|
|
1169
|
+
child_edges[1].push_back(edge);
|
|
1170
|
+
} else {
|
|
1171
|
+
// The edge bound spans both children.
|
|
1172
|
+
child_edges[0].push_back(ClipVBound(edge, 1, middle.hi(), alloc));
|
|
1173
|
+
child_edges[1].push_back(ClipVBound(edge, 0, middle.lo(), alloc));
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
// Given an edge, clip the given endpoint (lo=0, hi=1) of the u-axis so that
|
|
1178
|
+
// it does not extend past the given value.
|
|
1179
|
+
/* static */
|
|
1180
|
+
const MutableS2ShapeIndex::ClippedEdge*
|
|
1181
|
+
MutableS2ShapeIndex::ClipUBound(const ClippedEdge* edge, int u_end, double u,
|
|
1182
|
+
EdgeAllocator* alloc) {
|
|
1183
|
+
// First check whether the edge actually requires any clipping. (Sometimes
|
|
1184
|
+
// this method is called when clipping is not necessary, e.g. when one edge
|
|
1185
|
+
// endpoint is in the overlap area between two padded child cells.)
|
|
1186
|
+
if (u_end == 0) {
|
|
1187
|
+
if (edge->bound[0].lo() >= u) return edge;
|
|
1188
|
+
} else {
|
|
1189
|
+
if (edge->bound[0].hi() <= u) return edge;
|
|
1190
|
+
}
|
|
1191
|
+
// We interpolate the new v-value from the endpoints of the original edge.
|
|
1192
|
+
// This has two advantages: (1) we don't need to store the clipped endpoints
|
|
1193
|
+
// at all, just their bounding box; and (2) it avoids the accumulation of
|
|
1194
|
+
// roundoff errors due to repeated interpolations. The result needs to be
|
|
1195
|
+
// clamped to ensure that it is in the appropriate range.
|
|
1196
|
+
const FaceEdge& e = *edge->face_edge;
|
|
1197
|
+
double v = edge->bound[1].Project(
|
|
1198
|
+
S2::InterpolateDouble(u, e.a[0], e.b[0], e.a[1], e.b[1]));
|
|
1199
|
+
|
|
1200
|
+
// Determine which endpoint of the v-axis bound to update. If the edge
|
|
1201
|
+
// slope is positive we update the same endpoint, otherwise we update the
|
|
1202
|
+
// opposite endpoint.
|
|
1203
|
+
int v_end = u_end ^ ((e.a[0] > e.b[0]) != (e.a[1] > e.b[1]));
|
|
1204
|
+
return UpdateBound(edge, u_end, u, v_end, v, alloc);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// Given an edge, clip the given endpoint (lo=0, hi=1) of the v-axis so that
|
|
1208
|
+
// it does not extend past the given value.
|
|
1209
|
+
/* static */
|
|
1210
|
+
const MutableS2ShapeIndex::ClippedEdge*
|
|
1211
|
+
MutableS2ShapeIndex::ClipVBound(const ClippedEdge* edge, int v_end, double v,
|
|
1212
|
+
EdgeAllocator* alloc) {
|
|
1213
|
+
// See comments in ClipUBound.
|
|
1214
|
+
if (v_end == 0) {
|
|
1215
|
+
if (edge->bound[1].lo() >= v) return edge;
|
|
1216
|
+
} else {
|
|
1217
|
+
if (edge->bound[1].hi() <= v) return edge;
|
|
1218
|
+
}
|
|
1219
|
+
const FaceEdge& e = *edge->face_edge;
|
|
1220
|
+
double u = edge->bound[0].Project(
|
|
1221
|
+
S2::InterpolateDouble(v, e.a[1], e.b[1], e.a[0], e.b[0]));
|
|
1222
|
+
int u_end = v_end ^ ((e.a[0] > e.b[0]) != (e.a[1] > e.b[1]));
|
|
1223
|
+
return UpdateBound(edge, u_end, u, v_end, v, alloc);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// Given an edge and two bound endpoints that need to be updated, allocate and
|
|
1227
|
+
// return a new edge with the updated bound.
|
|
1228
|
+
/* static */
|
|
1229
|
+
inline const MutableS2ShapeIndex::ClippedEdge*
|
|
1230
|
+
MutableS2ShapeIndex::UpdateBound(const ClippedEdge* edge, int u_end, double u,
|
|
1231
|
+
int v_end, double v, EdgeAllocator* alloc) {
|
|
1232
|
+
ClippedEdge* clipped = alloc->NewClippedEdge();
|
|
1233
|
+
clipped->face_edge = edge->face_edge;
|
|
1234
|
+
clipped->bound[0][u_end] = u;
|
|
1235
|
+
clipped->bound[1][v_end] = v;
|
|
1236
|
+
clipped->bound[0][1-u_end] = edge->bound[0][1-u_end];
|
|
1237
|
+
clipped->bound[1][1-v_end] = edge->bound[1][1-v_end];
|
|
1238
|
+
S2_DCHECK(!clipped->bound.is_empty());
|
|
1239
|
+
S2_DCHECK(edge->bound.Contains(clipped->bound));
|
|
1240
|
+
return clipped;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// Absorb an index cell by transferring its contents to "edges" and/or
|
|
1244
|
+
// "tracker", and then delete this cell from the index. If "edges" includes
|
|
1245
|
+
// any edges that are being removed, this method also updates their
|
|
1246
|
+
// InteriorTracker state to correspond to the exit vertex of this cell, and
|
|
1247
|
+
// saves the InteriorTracker state by calling SaveAndClearStateBefore(). It
|
|
1248
|
+
// is the caller's responsibility to restore this state by calling
|
|
1249
|
+
// RestoreStateBefore() when processing of this cell is finished.
|
|
1250
|
+
void MutableS2ShapeIndex::AbsorbIndexCell(const S2PaddedCell& pcell,
|
|
1251
|
+
const Iterator& iter,
|
|
1252
|
+
vector<const ClippedEdge*>* edges,
|
|
1253
|
+
InteriorTracker* tracker,
|
|
1254
|
+
EdgeAllocator* alloc) {
|
|
1255
|
+
S2_DCHECK_EQ(pcell.id(), iter.id());
|
|
1256
|
+
|
|
1257
|
+
// When we absorb a cell, we erase all the edges that are being removed.
|
|
1258
|
+
// However when we are finished with this cell, we want to restore the state
|
|
1259
|
+
// of those edges (since that is how we find all the index cells that need
|
|
1260
|
+
// to be updated). The edges themselves are restored automatically when
|
|
1261
|
+
// UpdateEdges returns from its recursive call, but the InteriorTracker
|
|
1262
|
+
// state needs to be restored explicitly.
|
|
1263
|
+
//
|
|
1264
|
+
// Here we first update the InteriorTracker state for removed edges to
|
|
1265
|
+
// correspond to the exit vertex of this cell, and then save the
|
|
1266
|
+
// InteriorTracker state. This state will be restored by UpdateEdges when
|
|
1267
|
+
// it is finished processing the contents of this cell.
|
|
1268
|
+
if (tracker->is_active() && !edges->empty() &&
|
|
1269
|
+
is_shape_being_removed((*edges)[0]->face_edge->shape_id)) {
|
|
1270
|
+
// We probably need to update the InteriorTracker. ("Probably" because
|
|
1271
|
+
// it's possible that all shapes being removed do not have interiors.)
|
|
1272
|
+
if (!tracker->at_cellid(pcell.id())) {
|
|
1273
|
+
tracker->MoveTo(pcell.GetEntryVertex());
|
|
1274
|
+
}
|
|
1275
|
+
tracker->DrawTo(pcell.GetExitVertex());
|
|
1276
|
+
tracker->set_next_cellid(pcell.id().next());
|
|
1277
|
+
for (const ClippedEdge* edge : *edges) {
|
|
1278
|
+
const FaceEdge* face_edge = edge->face_edge;
|
|
1279
|
+
if (!is_shape_being_removed(face_edge->shape_id)) {
|
|
1280
|
+
break; // All shapes being removed come first.
|
|
1281
|
+
}
|
|
1282
|
+
if (face_edge->has_interior) {
|
|
1283
|
+
tracker->TestEdge(face_edge->shape_id, face_edge->edge);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
// Save the state of the edges being removed, so that it can be restored
|
|
1288
|
+
// when we are finished processing this cell and its children. We don't
|
|
1289
|
+
// need to save the state of the edges being added because they aren't being
|
|
1290
|
+
// removed from "edges" and will therefore be updated normally as we visit
|
|
1291
|
+
// this cell and its children.
|
|
1292
|
+
tracker->SaveAndClearStateBefore(pending_additions_begin_);
|
|
1293
|
+
|
|
1294
|
+
// Create a FaceEdge for each edge in this cell that isn't being removed.
|
|
1295
|
+
vector<FaceEdge>* face_edges = alloc->mutable_face_edges();
|
|
1296
|
+
face_edges->clear();
|
|
1297
|
+
bool tracker_moved = false;
|
|
1298
|
+
const S2ShapeIndexCell& cell = iter.cell();
|
|
1299
|
+
for (int s = 0; s < cell.num_clipped(); ++s) {
|
|
1300
|
+
const S2ClippedShape& clipped = cell.clipped(s);
|
|
1301
|
+
int shape_id = clipped.shape_id();
|
|
1302
|
+
const S2Shape* shape = this->shape(shape_id);
|
|
1303
|
+
if (shape == nullptr) continue; // This shape is being removed.
|
|
1304
|
+
int num_edges = clipped.num_edges();
|
|
1305
|
+
|
|
1306
|
+
// If this shape has an interior, start tracking whether we are inside the
|
|
1307
|
+
// shape. UpdateEdges() wants to know whether the entry vertex of this
|
|
1308
|
+
// cell is inside the shape, but we only know whether the center of the
|
|
1309
|
+
// cell is inside the shape, so we need to test all the edges against the
|
|
1310
|
+
// line segment from the cell center to the entry vertex.
|
|
1311
|
+
FaceEdge edge;
|
|
1312
|
+
edge.shape_id = shape->id();
|
|
1313
|
+
edge.has_interior = (shape->dimension() == 2);
|
|
1314
|
+
if (edge.has_interior) {
|
|
1315
|
+
tracker->AddShape(shape_id, clipped.contains_center());
|
|
1316
|
+
// There might not be any edges in this entire cell (i.e., it might be
|
|
1317
|
+
// in the interior of all shapes), so we delay updating the tracker
|
|
1318
|
+
// until we see the first edge.
|
|
1319
|
+
if (!tracker_moved && num_edges > 0) {
|
|
1320
|
+
tracker->MoveTo(pcell.GetCenter());
|
|
1321
|
+
tracker->DrawTo(pcell.GetEntryVertex());
|
|
1322
|
+
tracker->set_next_cellid(pcell.id());
|
|
1323
|
+
tracker_moved = true;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
for (int i = 0; i < num_edges; ++i) {
|
|
1327
|
+
int e = clipped.edge(i);
|
|
1328
|
+
edge.edge_id = e;
|
|
1329
|
+
edge.edge = shape->edge(e);
|
|
1330
|
+
edge.max_level = GetEdgeMaxLevel(edge.edge);
|
|
1331
|
+
if (edge.has_interior) tracker->TestEdge(shape_id, edge.edge);
|
|
1332
|
+
if (!S2::ClipToPaddedFace(edge.edge.v0, edge.edge.v1, pcell.id().face(),
|
|
1333
|
+
kCellPadding, &edge.a, &edge.b)) {
|
|
1334
|
+
S2_LOG(DFATAL) << "Invariant failure in MutableS2ShapeIndex";
|
|
1335
|
+
}
|
|
1336
|
+
face_edges->push_back(edge);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
// Now create a ClippedEdge for each FaceEdge, and put them in "new_edges".
|
|
1340
|
+
vector<const ClippedEdge*> new_edges;
|
|
1341
|
+
for (const FaceEdge& face_edge : *face_edges) {
|
|
1342
|
+
ClippedEdge* clipped = alloc->NewClippedEdge();
|
|
1343
|
+
clipped->face_edge = &face_edge;
|
|
1344
|
+
clipped->bound = S2::GetClippedEdgeBound(face_edge.a, face_edge.b,
|
|
1345
|
+
pcell.bound());
|
|
1346
|
+
new_edges.push_back(clipped);
|
|
1347
|
+
}
|
|
1348
|
+
// Discard any edges from "edges" that are being removed, and append the
|
|
1349
|
+
// remainder to "new_edges". (This keeps the edges sorted by shape id.)
|
|
1350
|
+
for (int i = 0; i < edges->size(); ++i) {
|
|
1351
|
+
const ClippedEdge* clipped = (*edges)[i];
|
|
1352
|
+
if (!is_shape_being_removed(clipped->face_edge->shape_id)) {
|
|
1353
|
+
new_edges.insert(new_edges.end(), edges->begin() + i, edges->end());
|
|
1354
|
+
break;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
// Update the edge list and delete this cell from the index.
|
|
1358
|
+
edges->swap(new_edges);
|
|
1359
|
+
cell_map_.erase(pcell.id());
|
|
1360
|
+
delete &cell;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// Attempt to build an index cell containing the given edges, and return true
|
|
1364
|
+
// if successful. (Otherwise the edges should be subdivided further.)
|
|
1365
|
+
bool MutableS2ShapeIndex::MakeIndexCell(const S2PaddedCell& pcell,
|
|
1366
|
+
const vector<const ClippedEdge*>& edges,
|
|
1367
|
+
InteriorTracker* tracker) {
|
|
1368
|
+
if (edges.empty() && tracker->shape_ids().empty()) {
|
|
1369
|
+
// No index cell is needed. (In most cases this situation is detected
|
|
1370
|
+
// before we get to this point, but this can happen when all shapes in a
|
|
1371
|
+
// cell are removed.)
|
|
1372
|
+
return true;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
// Count the number of edges that have not reached their maximum level yet.
|
|
1376
|
+
// Return false if there are too many such edges.
|
|
1377
|
+
int count = 0;
|
|
1378
|
+
for (const ClippedEdge* edge : edges) {
|
|
1379
|
+
count += (pcell.level() < edge->face_edge->max_level);
|
|
1380
|
+
if (count > options_.max_edges_per_cell())
|
|
1381
|
+
return false;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// Possible optimization: Continue subdividing as long as exactly one child
|
|
1385
|
+
// of "pcell" intersects the given edges. This can be done by finding the
|
|
1386
|
+
// bounding box of all the edges and calling ShrinkToFit():
|
|
1387
|
+
//
|
|
1388
|
+
// S2CellId cellid = pcell.ShrinkToFit(GetRectBound(edges));
|
|
1389
|
+
//
|
|
1390
|
+
// Currently this is not beneficial; it slows down construction by 4-25%
|
|
1391
|
+
// (mainly computing the union of the bounding rectangles) and also slows
|
|
1392
|
+
// down queries (since more recursive clipping is required to get down to
|
|
1393
|
+
// the level of a spatial index cell). But it may be worth trying again
|
|
1394
|
+
// once "contains_center" is computed and all algorithms are modified to
|
|
1395
|
+
// take advantage of it.
|
|
1396
|
+
|
|
1397
|
+
// We update the InteriorTracker as follows. For every S2Cell in the index
|
|
1398
|
+
// we construct two edges: one edge from entry vertex of the cell to its
|
|
1399
|
+
// center, and one from the cell center to its exit vertex. Here "entry"
|
|
1400
|
+
// and "exit" refer the S2CellId ordering, i.e. the order in which points
|
|
1401
|
+
// are encountered along the S2 space-filling curve. The exit vertex then
|
|
1402
|
+
// becomes the entry vertex for the next cell in the index, unless there are
|
|
1403
|
+
// one or more empty intervening cells, in which case the InteriorTracker
|
|
1404
|
+
// state is unchanged because the intervening cells have no edges.
|
|
1405
|
+
|
|
1406
|
+
// Shift the InteriorTracker focus point to the center of the current cell.
|
|
1407
|
+
if (tracker->is_active() && !edges.empty()) {
|
|
1408
|
+
if (!tracker->at_cellid(pcell.id())) {
|
|
1409
|
+
tracker->MoveTo(pcell.GetEntryVertex());
|
|
1410
|
+
}
|
|
1411
|
+
tracker->DrawTo(pcell.GetCenter());
|
|
1412
|
+
TestAllEdges(edges, tracker);
|
|
1413
|
+
}
|
|
1414
|
+
// Allocate and fill a new index cell. To get the total number of shapes we
|
|
1415
|
+
// need to merge the shapes associated with the intersecting edges together
|
|
1416
|
+
// with the shapes that happen to contain the cell center.
|
|
1417
|
+
const ShapeIdSet& cshape_ids = tracker->shape_ids();
|
|
1418
|
+
int num_shapes = CountShapes(edges, cshape_ids);
|
|
1419
|
+
S2ShapeIndexCell* cell = new S2ShapeIndexCell;
|
|
1420
|
+
S2ClippedShape* base = cell->add_shapes(num_shapes);
|
|
1421
|
+
|
|
1422
|
+
// To fill the index cell we merge the two sources of shapes: "edge shapes"
|
|
1423
|
+
// (those that have at least one edge that intersects this cell), and
|
|
1424
|
+
// "containing shapes" (those that contain the cell center). We keep track
|
|
1425
|
+
// of the index of the next intersecting edge and the next containing shape
|
|
1426
|
+
// as we go along. Both sets of shape ids are already sorted.
|
|
1427
|
+
int enext = 0;
|
|
1428
|
+
ShapeIdSet::const_iterator cnext = cshape_ids.begin();
|
|
1429
|
+
for (int i = 0; i < num_shapes; ++i) {
|
|
1430
|
+
S2ClippedShape* clipped = base + i;
|
|
1431
|
+
int eshape_id = num_shape_ids(), cshape_id = eshape_id; // Sentinels
|
|
1432
|
+
if (enext != edges.size()) {
|
|
1433
|
+
eshape_id = edges[enext]->face_edge->shape_id;
|
|
1434
|
+
}
|
|
1435
|
+
if (cnext != cshape_ids.end()) {
|
|
1436
|
+
cshape_id = *cnext;
|
|
1437
|
+
}
|
|
1438
|
+
int ebegin = enext;
|
|
1439
|
+
if (cshape_id < eshape_id) {
|
|
1440
|
+
// The entire cell is in the shape interior.
|
|
1441
|
+
clipped->Init(cshape_id, 0);
|
|
1442
|
+
clipped->set_contains_center(true);
|
|
1443
|
+
++cnext;
|
|
1444
|
+
} else {
|
|
1445
|
+
// Count the number of edges for this shape and allocate space for them.
|
|
1446
|
+
while (enext < edges.size() &&
|
|
1447
|
+
edges[enext]->face_edge->shape_id == eshape_id) {
|
|
1448
|
+
++enext;
|
|
1449
|
+
}
|
|
1450
|
+
clipped->Init(eshape_id, enext - ebegin);
|
|
1451
|
+
for (int e = ebegin; e < enext; ++e) {
|
|
1452
|
+
clipped->set_edge(e - ebegin, edges[e]->face_edge->edge_id);
|
|
1453
|
+
}
|
|
1454
|
+
if (cshape_id == eshape_id) {
|
|
1455
|
+
clipped->set_contains_center(true);
|
|
1456
|
+
++cnext;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
// UpdateEdges() visits cells in increasing order of S2CellId, so during
|
|
1461
|
+
// initial construction of the index all insertions happen at the end. It
|
|
1462
|
+
// is much faster to give an insertion hint in this case. Otherwise the
|
|
1463
|
+
// hint doesn't do much harm. With more effort we could provide a hint even
|
|
1464
|
+
// during incremental updates, but this is probably not worth the effort.
|
|
1465
|
+
cell_map_.insert(cell_map_.end(), std::make_pair(pcell.id(), cell));
|
|
1466
|
+
|
|
1467
|
+
// Shift the InteriorTracker focus point to the exit vertex of this cell.
|
|
1468
|
+
if (tracker->is_active() && !edges.empty()) {
|
|
1469
|
+
tracker->DrawTo(pcell.GetExitVertex());
|
|
1470
|
+
TestAllEdges(edges, tracker);
|
|
1471
|
+
tracker->set_next_cellid(pcell.id().next());
|
|
1472
|
+
}
|
|
1473
|
+
return true;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// Call tracker->TestEdge() on all edges from shapes that have interiors.
|
|
1477
|
+
/* static */
|
|
1478
|
+
void MutableS2ShapeIndex::TestAllEdges(const vector<const ClippedEdge*>& edges,
|
|
1479
|
+
InteriorTracker* tracker) {
|
|
1480
|
+
for (const ClippedEdge* edge : edges) {
|
|
1481
|
+
const FaceEdge* face_edge = edge->face_edge;
|
|
1482
|
+
if (face_edge->has_interior) {
|
|
1483
|
+
tracker->TestEdge(face_edge->shape_id, face_edge->edge);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// Return the number of distinct shapes that are either associated with the
|
|
1489
|
+
// given edges, or that are currently stored in the InteriorTracker.
|
|
1490
|
+
/* static */
|
|
1491
|
+
int MutableS2ShapeIndex::CountShapes(const vector<const ClippedEdge*>& edges,
|
|
1492
|
+
const ShapeIdSet& cshape_ids) {
|
|
1493
|
+
int count = 0;
|
|
1494
|
+
int last_shape_id = -1;
|
|
1495
|
+
ShapeIdSet::const_iterator cnext = cshape_ids.begin(); // Next shape
|
|
1496
|
+
for (const ClippedEdge* edge : edges) {
|
|
1497
|
+
if (edge->face_edge->shape_id != last_shape_id) {
|
|
1498
|
+
++count;
|
|
1499
|
+
last_shape_id = edge->face_edge->shape_id;
|
|
1500
|
+
// Skip over any containing shapes up to and including this one,
|
|
1501
|
+
// updating "count" appropriately.
|
|
1502
|
+
for (; cnext != cshape_ids.end(); ++cnext) {
|
|
1503
|
+
if (*cnext > last_shape_id) break;
|
|
1504
|
+
if (*cnext < last_shape_id) ++count;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
// Count any remaining containing shapes.
|
|
1509
|
+
count += (cshape_ids.end() - cnext);
|
|
1510
|
+
return count;
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
size_t MutableS2ShapeIndex::SpaceUsed() const {
|
|
1514
|
+
size_t size = sizeof(*this);
|
|
1515
|
+
size += shapes_.capacity() * sizeof(std::unique_ptr<S2Shape>);
|
|
1516
|
+
// cell_map_ itself is already included in sizeof(*this).
|
|
1517
|
+
size += cell_map_.bytes_used() - sizeof(cell_map_);
|
|
1518
|
+
size += cell_map_.size() * sizeof(S2ShapeIndexCell);
|
|
1519
|
+
Iterator it;
|
|
1520
|
+
for (it.InitStale(this, S2ShapeIndex::BEGIN); !it.done(); it.Next()) {
|
|
1521
|
+
const S2ShapeIndexCell& cell = it.cell();
|
|
1522
|
+
size += cell.shapes_.capacity() * sizeof(S2ClippedShape);
|
|
1523
|
+
for (int s = 0; s < cell.num_clipped(); ++s) {
|
|
1524
|
+
const S2ClippedShape& clipped = cell.clipped(s);
|
|
1525
|
+
if (!clipped.is_inline()) {
|
|
1526
|
+
size += clipped.num_edges() * sizeof(int32);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
if (pending_removals_ != nullptr) {
|
|
1531
|
+
size += pending_removals_->capacity() * sizeof(RemovedShape);
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
return size;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
void MutableS2ShapeIndex::Encode(Encoder* encoder) const {
|
|
1538
|
+
// The version number is encoded in 2 bits, under the assumption that by the
|
|
1539
|
+
// time we need 5 versions the first version can be permanently retired.
|
|
1540
|
+
// This only saves 1 byte, but that's significant for very small indexes.
|
|
1541
|
+
encoder->Ensure(Varint::kMax64);
|
|
1542
|
+
uint64 max_edges = options_.max_edges_per_cell();
|
|
1543
|
+
encoder->put_varint64(max_edges << 2 | kCurrentEncodingVersionNumber);
|
|
1544
|
+
|
|
1545
|
+
vector<S2CellId> cell_ids;
|
|
1546
|
+
cell_ids.reserve(cell_map_.size());
|
|
1547
|
+
s2coding::StringVectorEncoder encoded_cells;
|
|
1548
|
+
for (Iterator it(this, S2ShapeIndex::BEGIN); !it.done(); it.Next()) {
|
|
1549
|
+
cell_ids.push_back(it.id());
|
|
1550
|
+
it.cell().Encode(num_shape_ids(), encoded_cells.AddViaEncoder());
|
|
1551
|
+
}
|
|
1552
|
+
s2coding::EncodeS2CellIdVector(cell_ids, encoder);
|
|
1553
|
+
encoded_cells.Encode(encoder);
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
bool MutableS2ShapeIndex::Init(Decoder* decoder,
|
|
1557
|
+
const ShapeFactory& shape_factory) {
|
|
1558
|
+
Clear();
|
|
1559
|
+
uint64 max_edges_version;
|
|
1560
|
+
if (!decoder->get_varint64(&max_edges_version)) return false;
|
|
1561
|
+
int version = max_edges_version & 3;
|
|
1562
|
+
if (version != kCurrentEncodingVersionNumber) return false;
|
|
1563
|
+
options_.set_max_edges_per_cell(max_edges_version >> 2);
|
|
1564
|
+
uint32 num_shapes = shape_factory.size();
|
|
1565
|
+
shapes_.reserve(num_shapes);
|
|
1566
|
+
for (int shape_id = 0; shape_id < num_shapes; ++shape_id) {
|
|
1567
|
+
auto shape = shape_factory[shape_id];
|
|
1568
|
+
if (shape) shape->id_ = shape_id;
|
|
1569
|
+
shapes_.push_back(std::move(shape));
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
s2coding::EncodedS2CellIdVector cell_ids;
|
|
1573
|
+
s2coding::EncodedStringVector encoded_cells;
|
|
1574
|
+
if (!cell_ids.Init(decoder)) return false;
|
|
1575
|
+
if (!encoded_cells.Init(decoder)) return false;
|
|
1576
|
+
|
|
1577
|
+
for (int i = 0; i < cell_ids.size(); ++i) {
|
|
1578
|
+
S2CellId id = cell_ids[i];
|
|
1579
|
+
S2ShapeIndexCell* cell = new S2ShapeIndexCell;
|
|
1580
|
+
Decoder decoder = encoded_cells.GetDecoder(i);
|
|
1581
|
+
if (!cell->Decode(num_shapes, &decoder)) return false;
|
|
1582
|
+
cell_map_.insert(cell_map_.end(), std::make_pair(id, cell));
|
|
1583
|
+
}
|
|
1584
|
+
return true;
|
|
1585
|
+
}
|