@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.
Files changed (471) hide show
  1. package/.circleci/config.yml +45 -0
  2. package/.dockerignore +1 -0
  3. package/.gitmodules +3 -0
  4. package/CHANGELOG.md +33 -0
  5. package/LICENSE +201 -0
  6. package/README.md +147 -0
  7. package/binding.gyp +170 -0
  8. package/docker/Dockerfile.node20.test +8 -0
  9. package/docker/Dockerfile.node22.test +8 -0
  10. package/docker/Dockerfile.node24.test +8 -0
  11. package/index.d.ts +117 -0
  12. package/index.js +6 -0
  13. package/jest.config.js +184 -0
  14. package/package.json +43 -0
  15. package/publish-linux.sh +18 -0
  16. package/publish-osx.sh +19 -0
  17. package/src/builder.cc +84 -0
  18. package/src/builder.h +29 -0
  19. package/src/cell.cc +71 -0
  20. package/src/cell.h +26 -0
  21. package/src/cell_id.cc +210 -0
  22. package/src/cell_id.h +44 -0
  23. package/src/cell_union.cc +237 -0
  24. package/src/cell_union.h +34 -0
  25. package/src/earth.cc +185 -0
  26. package/src/earth.h +33 -0
  27. package/src/latlng.cc +132 -0
  28. package/src/latlng.h +28 -0
  29. package/src/loop.cc +51 -0
  30. package/src/loop.h +21 -0
  31. package/src/point.cc +69 -0
  32. package/src/point.h +23 -0
  33. package/src/polygon.cc +36 -0
  34. package/src/polygon.h +20 -0
  35. package/src/polyline.cc +186 -0
  36. package/src/polyline.h +34 -0
  37. package/src/region_coverer.cc +450 -0
  38. package/src/region_coverer.h +56 -0
  39. package/src/s2.cc +27 -0
  40. package/test/Cell.test.js +37 -0
  41. package/test/CellId.test.js +135 -0
  42. package/test/CellUnion.test.js +150 -0
  43. package/test/Earth.test.js +62 -0
  44. package/test/LatLng.test.js +45 -0
  45. package/test/Point.test.js +14 -0
  46. package/test/Polyline.test.js +78 -0
  47. package/test/RegionCoverer.test.js +301 -0
  48. package/test.sh +16 -0
  49. package/third_party/s2geometry/.travis.yml +163 -0
  50. package/third_party/s2geometry/AUTHORS +13 -0
  51. package/third_party/s2geometry/CONTRIBUTING.md +65 -0
  52. package/third_party/s2geometry/CONTRIBUTORS +30 -0
  53. package/third_party/s2geometry/LICENSE +202 -0
  54. package/third_party/s2geometry/NOTICE +5 -0
  55. package/third_party/s2geometry/README.md +127 -0
  56. package/third_party/s2geometry/doc/examples/point_index.cc +44 -0
  57. package/third_party/s2geometry/doc/examples/term_index.cc +99 -0
  58. package/third_party/s2geometry/doc/examples/term_index.py +101 -0
  59. package/third_party/s2geometry/src/python/coder.i +125 -0
  60. package/third_party/s2geometry/src/python/pywraps2_test.py +786 -0
  61. package/third_party/s2geometry/src/python/s2.i +37 -0
  62. package/third_party/s2geometry/src/python/s2_common.i +756 -0
  63. package/third_party/s2geometry/src/s2/_fp_contract_off.h +60 -0
  64. package/third_party/s2geometry/src/s2/base/casts.h +318 -0
  65. package/third_party/s2geometry/src/s2/base/commandlineflags.h +67 -0
  66. package/third_party/s2geometry/src/s2/base/integral_types.h +31 -0
  67. package/third_party/s2geometry/src/s2/base/log_severity.h +40 -0
  68. package/third_party/s2geometry/src/s2/base/logging.h +173 -0
  69. package/third_party/s2geometry/src/s2/base/mutex.h +61 -0
  70. package/third_party/s2geometry/src/s2/base/port.h +999 -0
  71. package/third_party/s2geometry/src/s2/base/spinlock.h +60 -0
  72. package/third_party/s2geometry/src/s2/base/stringprintf.cc +107 -0
  73. package/third_party/s2geometry/src/s2/base/stringprintf.h +53 -0
  74. package/third_party/s2geometry/src/s2/base/strtoint.cc +65 -0
  75. package/third_party/s2geometry/src/s2/base/strtoint.h +106 -0
  76. package/third_party/s2geometry/src/s2/base/timer.h +50 -0
  77. package/third_party/s2geometry/src/s2/encoded_s2cell_id_vector.cc +164 -0
  78. package/third_party/s2geometry/src/s2/encoded_s2cell_id_vector.h +110 -0
  79. package/third_party/s2geometry/src/s2/encoded_s2cell_id_vector_test.cc +232 -0
  80. package/third_party/s2geometry/src/s2/encoded_s2point_vector.cc +838 -0
  81. package/third_party/s2geometry/src/s2/encoded_s2point_vector.h +140 -0
  82. package/third_party/s2geometry/src/s2/encoded_s2point_vector_test.cc +344 -0
  83. package/third_party/s2geometry/src/s2/encoded_s2shape_index.cc +181 -0
  84. package/third_party/s2geometry/src/s2/encoded_s2shape_index.h +276 -0
  85. package/third_party/s2geometry/src/s2/encoded_s2shape_index_test.cc +244 -0
  86. package/third_party/s2geometry/src/s2/encoded_string_vector.cc +66 -0
  87. package/third_party/s2geometry/src/s2/encoded_string_vector.h +164 -0
  88. package/third_party/s2geometry/src/s2/encoded_string_vector_test.cc +69 -0
  89. package/third_party/s2geometry/src/s2/encoded_uint_vector.h +299 -0
  90. package/third_party/s2geometry/src/s2/encoded_uint_vector_test.cc +124 -0
  91. package/third_party/s2geometry/src/s2/id_set_lexicon.cc +81 -0
  92. package/third_party/s2geometry/src/s2/id_set_lexicon.h +199 -0
  93. package/third_party/s2geometry/src/s2/id_set_lexicon_test.cc +70 -0
  94. package/third_party/s2geometry/src/s2/mutable_s2shape_index.cc +1585 -0
  95. package/third_party/s2geometry/src/s2/mutable_s2shape_index.h +600 -0
  96. package/third_party/s2geometry/src/s2/mutable_s2shape_index_test.cc +589 -0
  97. package/third_party/s2geometry/src/s2/r1interval.h +220 -0
  98. package/third_party/s2geometry/src/s2/r1interval_test.cc +185 -0
  99. package/third_party/s2geometry/src/s2/r2.h +26 -0
  100. package/third_party/s2geometry/src/s2/r2rect.cc +93 -0
  101. package/third_party/s2geometry/src/s2/r2rect.h +234 -0
  102. package/third_party/s2geometry/src/s2/r2rect_test.cc +228 -0
  103. package/third_party/s2geometry/src/s2/s1angle.cc +54 -0
  104. package/third_party/s2geometry/src/s2/s1angle.h +336 -0
  105. package/third_party/s2geometry/src/s2/s1angle_test.cc +185 -0
  106. package/third_party/s2geometry/src/s2/s1chord_angle.cc +159 -0
  107. package/third_party/s2geometry/src/s2/s1chord_angle.h +369 -0
  108. package/third_party/s2geometry/src/s2/s1chord_angle_test.cc +207 -0
  109. package/third_party/s2geometry/src/s2/s1interval.cc +296 -0
  110. package/third_party/s2geometry/src/s2/s1interval.h +266 -0
  111. package/third_party/s2geometry/src/s2/s1interval_test.cc +469 -0
  112. package/third_party/s2geometry/src/s2/s2boolean_operation.cc +2391 -0
  113. package/third_party/s2geometry/src/s2/s2boolean_operation.h +501 -0
  114. package/third_party/s2geometry/src/s2/s2boolean_operation_test.cc +1400 -0
  115. package/third_party/s2geometry/src/s2/s2builder.cc +1828 -0
  116. package/third_party/s2geometry/src/s2/s2builder.h +1057 -0
  117. package/third_party/s2geometry/src/s2/s2builder_graph.cc +1084 -0
  118. package/third_party/s2geometry/src/s2/s2builder_graph.h +799 -0
  119. package/third_party/s2geometry/src/s2/s2builder_graph_test.cc +462 -0
  120. package/third_party/s2geometry/src/s2/s2builder_layer.h +50 -0
  121. package/third_party/s2geometry/src/s2/s2builder_test.cc +1329 -0
  122. package/third_party/s2geometry/src/s2/s2builderutil_closed_set_normalizer.cc +313 -0
  123. package/third_party/s2geometry/src/s2/s2builderutil_closed_set_normalizer.h +221 -0
  124. package/third_party/s2geometry/src/s2/s2builderutil_closed_set_normalizer_test.cc +261 -0
  125. package/third_party/s2geometry/src/s2/s2builderutil_find_polygon_degeneracies.cc +392 -0
  126. package/third_party/s2geometry/src/s2/s2builderutil_find_polygon_degeneracies.h +86 -0
  127. package/third_party/s2geometry/src/s2/s2builderutil_find_polygon_degeneracies_test.cc +182 -0
  128. package/third_party/s2geometry/src/s2/s2builderutil_graph_shape.h +57 -0
  129. package/third_party/s2geometry/src/s2/s2builderutil_lax_polygon_layer.cc +212 -0
  130. package/third_party/s2geometry/src/s2/s2builderutil_lax_polygon_layer.h +218 -0
  131. package/third_party/s2geometry/src/s2/s2builderutil_lax_polygon_layer_test.cc +367 -0
  132. package/third_party/s2geometry/src/s2/s2builderutil_s2point_vector_layer.cc +74 -0
  133. package/third_party/s2geometry/src/s2/s2builderutil_s2point_vector_layer.h +122 -0
  134. package/third_party/s2geometry/src/s2/s2builderutil_s2point_vector_layer_test.cc +167 -0
  135. package/third_party/s2geometry/src/s2/s2builderutil_s2polygon_layer.cc +191 -0
  136. package/third_party/s2geometry/src/s2/s2builderutil_s2polygon_layer.h +211 -0
  137. package/third_party/s2geometry/src/s2/s2builderutil_s2polygon_layer_test.cc +312 -0
  138. package/third_party/s2geometry/src/s2/s2builderutil_s2polyline_layer.cc +105 -0
  139. package/third_party/s2geometry/src/s2/s2builderutil_s2polyline_layer.h +174 -0
  140. package/third_party/s2geometry/src/s2/s2builderutil_s2polyline_layer_test.cc +220 -0
  141. package/third_party/s2geometry/src/s2/s2builderutil_s2polyline_vector_layer.cc +98 -0
  142. package/third_party/s2geometry/src/s2/s2builderutil_s2polyline_vector_layer.h +292 -0
  143. package/third_party/s2geometry/src/s2/s2builderutil_s2polyline_vector_layer_test.cc +233 -0
  144. package/third_party/s2geometry/src/s2/s2builderutil_snap_functions.cc +354 -0
  145. package/third_party/s2geometry/src/s2/s2builderutil_snap_functions.h +239 -0
  146. package/third_party/s2geometry/src/s2/s2builderutil_snap_functions_test.cc +716 -0
  147. package/third_party/s2geometry/src/s2/s2builderutil_testing.cc +37 -0
  148. package/third_party/s2geometry/src/s2/s2builderutil_testing.h +100 -0
  149. package/third_party/s2geometry/src/s2/s2builderutil_testing_test.cc +85 -0
  150. package/third_party/s2geometry/src/s2/s2cap.cc +347 -0
  151. package/third_party/s2geometry/src/s2/s2cap.h +286 -0
  152. package/third_party/s2geometry/src/s2/s2cap_test.cc +379 -0
  153. package/third_party/s2geometry/src/s2/s2cell.cc +552 -0
  154. package/third_party/s2geometry/src/s2/s2cell.h +249 -0
  155. package/third_party/s2geometry/src/s2/s2cell_id.cc +619 -0
  156. package/third_party/s2geometry/src/s2/s2cell_id.h +705 -0
  157. package/third_party/s2geometry/src/s2/s2cell_id_test.cc +633 -0
  158. package/third_party/s2geometry/src/s2/s2cell_index.cc +149 -0
  159. package/third_party/s2geometry/src/s2/s2cell_index.h +660 -0
  160. package/third_party/s2geometry/src/s2/s2cell_index_test.cc +411 -0
  161. package/third_party/s2geometry/src/s2/s2cell_test.cc +687 -0
  162. package/third_party/s2geometry/src/s2/s2cell_union.cc +515 -0
  163. package/third_party/s2geometry/src/s2/s2cell_union.h +399 -0
  164. package/third_party/s2geometry/src/s2/s2cell_union_test.cc +598 -0
  165. package/third_party/s2geometry/src/s2/s2centroids.cc +84 -0
  166. package/third_party/s2geometry/src/s2/s2centroids.h +87 -0
  167. package/third_party/s2geometry/src/s2/s2centroids_test.cc +82 -0
  168. package/third_party/s2geometry/src/s2/s2closest_cell_query.cc +123 -0
  169. package/third_party/s2geometry/src/s2/s2closest_cell_query.h +385 -0
  170. package/third_party/s2geometry/src/s2/s2closest_cell_query_base.h +841 -0
  171. package/third_party/s2geometry/src/s2/s2closest_cell_query_base_test.cc +63 -0
  172. package/third_party/s2geometry/src/s2/s2closest_cell_query_test.cc +412 -0
  173. package/third_party/s2geometry/src/s2/s2closest_edge_query.cc +106 -0
  174. package/third_party/s2geometry/src/s2/s2closest_edge_query.h +421 -0
  175. package/third_party/s2geometry/src/s2/s2closest_edge_query_base.h +946 -0
  176. package/third_party/s2geometry/src/s2/s2closest_edge_query_base_test.cc +59 -0
  177. package/third_party/s2geometry/src/s2/s2closest_edge_query_test.cc +505 -0
  178. package/third_party/s2geometry/src/s2/s2closest_edge_query_testing.h +91 -0
  179. package/third_party/s2geometry/src/s2/s2closest_point_query.cc +66 -0
  180. package/third_party/s2geometry/src/s2/s2closest_point_query.h +465 -0
  181. package/third_party/s2geometry/src/s2/s2closest_point_query_base.h +767 -0
  182. package/third_party/s2geometry/src/s2/s2closest_point_query_base_test.cc +63 -0
  183. package/third_party/s2geometry/src/s2/s2closest_point_query_test.cc +312 -0
  184. package/third_party/s2geometry/src/s2/s2contains_point_query.h +328 -0
  185. package/third_party/s2geometry/src/s2/s2contains_point_query_test.cc +159 -0
  186. package/third_party/s2geometry/src/s2/s2contains_vertex_query.cc +39 -0
  187. package/third_party/s2geometry/src/s2/s2contains_vertex_query.h +66 -0
  188. package/third_party/s2geometry/src/s2/s2contains_vertex_query_test.cc +67 -0
  189. package/third_party/s2geometry/src/s2/s2convex_hull_query.cc +198 -0
  190. package/third_party/s2geometry/src/s2/s2convex_hull_query.h +110 -0
  191. package/third_party/s2geometry/src/s2/s2convex_hull_query_test.cc +208 -0
  192. package/third_party/s2geometry/src/s2/s2coords.cc +146 -0
  193. package/third_party/s2geometry/src/s2/s2coords.h +459 -0
  194. package/third_party/s2geometry/src/s2/s2coords_internal.h +71 -0
  195. package/third_party/s2geometry/src/s2/s2coords_test.cc +218 -0
  196. package/third_party/s2geometry/src/s2/s2crossing_edge_query.cc +380 -0
  197. package/third_party/s2geometry/src/s2/s2crossing_edge_query.h +220 -0
  198. package/third_party/s2geometry/src/s2/s2crossing_edge_query_test.cc +382 -0
  199. package/third_party/s2geometry/src/s2/s2debug.cc +23 -0
  200. package/third_party/s2geometry/src/s2/s2debug.h +69 -0
  201. package/third_party/s2geometry/src/s2/s2distance_target.h +165 -0
  202. package/third_party/s2geometry/src/s2/s2earth.cc +52 -0
  203. package/third_party/s2geometry/src/s2/s2earth.h +268 -0
  204. package/third_party/s2geometry/src/s2/s2earth_test.cc +146 -0
  205. package/third_party/s2geometry/src/s2/s2edge_clipping.cc +462 -0
  206. package/third_party/s2geometry/src/s2/s2edge_clipping.h +183 -0
  207. package/third_party/s2geometry/src/s2/s2edge_clipping_test.cc +335 -0
  208. package/third_party/s2geometry/src/s2/s2edge_crosser.cc +85 -0
  209. package/third_party/s2geometry/src/s2/s2edge_crosser.h +343 -0
  210. package/third_party/s2geometry/src/s2/s2edge_crosser_test.cc +264 -0
  211. package/third_party/s2geometry/src/s2/s2edge_crossings.cc +515 -0
  212. package/third_party/s2geometry/src/s2/s2edge_crossings.h +138 -0
  213. package/third_party/s2geometry/src/s2/s2edge_crossings_internal.h +59 -0
  214. package/third_party/s2geometry/src/s2/s2edge_crossings_test.cc +246 -0
  215. package/third_party/s2geometry/src/s2/s2edge_distances.cc +419 -0
  216. package/third_party/s2geometry/src/s2/s2edge_distances.h +192 -0
  217. package/third_party/s2geometry/src/s2/s2edge_distances_test.cc +539 -0
  218. package/third_party/s2geometry/src/s2/s2edge_tessellator.cc +276 -0
  219. package/third_party/s2geometry/src/s2/s2edge_tessellator.h +101 -0
  220. package/third_party/s2geometry/src/s2/s2edge_tessellator_test.cc +492 -0
  221. package/third_party/s2geometry/src/s2/s2edge_vector_shape.h +85 -0
  222. package/third_party/s2geometry/src/s2/s2edge_vector_shape_test.cc +66 -0
  223. package/third_party/s2geometry/src/s2/s2error.cc +29 -0
  224. package/third_party/s2geometry/src/s2/s2error.h +147 -0
  225. package/third_party/s2geometry/src/s2/s2error_test.cc +31 -0
  226. package/third_party/s2geometry/src/s2/s2furthest_edge_query.cc +117 -0
  227. package/third_party/s2geometry/src/s2/s2furthest_edge_query.h +439 -0
  228. package/third_party/s2geometry/src/s2/s2furthest_edge_query_test.cc +487 -0
  229. package/third_party/s2geometry/src/s2/s2latlng.cc +90 -0
  230. package/third_party/s2geometry/src/s2/s2latlng.h +234 -0
  231. package/third_party/s2geometry/src/s2/s2latlng_rect.cc +727 -0
  232. package/third_party/s2geometry/src/s2/s2latlng_rect.h +434 -0
  233. package/third_party/s2geometry/src/s2/s2latlng_rect_bounder.cc +344 -0
  234. package/third_party/s2geometry/src/s2/s2latlng_rect_bounder.h +89 -0
  235. package/third_party/s2geometry/src/s2/s2latlng_rect_bounder_test.cc +306 -0
  236. package/third_party/s2geometry/src/s2/s2latlng_rect_test.cc +1030 -0
  237. package/third_party/s2geometry/src/s2/s2latlng_test.cc +165 -0
  238. package/third_party/s2geometry/src/s2/s2lax_loop_shape.cc +104 -0
  239. package/third_party/s2geometry/src/s2/s2lax_loop_shape.h +153 -0
  240. package/third_party/s2geometry/src/s2/s2lax_loop_shape_test.cc +101 -0
  241. package/third_party/s2geometry/src/s2/s2lax_polygon_shape.cc +348 -0
  242. package/third_party/s2geometry/src/s2/s2lax_polygon_shape.h +183 -0
  243. package/third_party/s2geometry/src/s2/s2lax_polygon_shape_test.cc +234 -0
  244. package/third_party/s2geometry/src/s2/s2lax_polyline_shape.cc +118 -0
  245. package/third_party/s2geometry/src/s2/s2lax_polyline_shape.h +124 -0
  246. package/third_party/s2geometry/src/s2/s2lax_polyline_shape_test.cc +62 -0
  247. package/third_party/s2geometry/src/s2/s2loop.cc +1509 -0
  248. package/third_party/s2geometry/src/s2/s2loop.h +711 -0
  249. package/third_party/s2geometry/src/s2/s2loop_measures.cc +313 -0
  250. package/third_party/s2geometry/src/s2/s2loop_measures.h +280 -0
  251. package/third_party/s2geometry/src/s2/s2loop_measures_test.cc +367 -0
  252. package/third_party/s2geometry/src/s2/s2loop_test.cc +1371 -0
  253. package/third_party/s2geometry/src/s2/s2max_distance_targets.cc +265 -0
  254. package/third_party/s2geometry/src/s2/s2max_distance_targets.h +241 -0
  255. package/third_party/s2geometry/src/s2/s2max_distance_targets_test.cc +367 -0
  256. package/third_party/s2geometry/src/s2/s2measures.cc +128 -0
  257. package/third_party/s2geometry/src/s2/s2measures.h +78 -0
  258. package/third_party/s2geometry/src/s2/s2measures_test.cc +135 -0
  259. package/third_party/s2geometry/src/s2/s2metrics.cc +122 -0
  260. package/third_party/s2geometry/src/s2/s2metrics.h +199 -0
  261. package/third_party/s2geometry/src/s2/s2metrics_test.cc +127 -0
  262. package/third_party/s2geometry/src/s2/s2min_distance_targets.cc +295 -0
  263. package/third_party/s2geometry/src/s2/s2min_distance_targets.h +273 -0
  264. package/third_party/s2geometry/src/s2/s2min_distance_targets_test.cc +239 -0
  265. package/third_party/s2geometry/src/s2/s2padded_cell.cc +162 -0
  266. package/third_party/s2geometry/src/s2/s2padded_cell.h +108 -0
  267. package/third_party/s2geometry/src/s2/s2padded_cell_test.cc +138 -0
  268. package/third_party/s2geometry/src/s2/s2point.h +38 -0
  269. package/third_party/s2geometry/src/s2/s2point_compression.cc +388 -0
  270. package/third_party/s2geometry/src/s2/s2point_compression.h +78 -0
  271. package/third_party/s2geometry/src/s2/s2point_compression_test.cc +305 -0
  272. package/third_party/s2geometry/src/s2/s2point_index.h +345 -0
  273. package/third_party/s2geometry/src/s2/s2point_index_test.cc +147 -0
  274. package/third_party/s2geometry/src/s2/s2point_region.cc +72 -0
  275. package/third_party/s2geometry/src/s2/s2point_region.h +76 -0
  276. package/third_party/s2geometry/src/s2/s2point_region_test.cc +100 -0
  277. package/third_party/s2geometry/src/s2/s2point_span.h +57 -0
  278. package/third_party/s2geometry/src/s2/s2point_test.cc +47 -0
  279. package/third_party/s2geometry/src/s2/s2point_vector_shape.h +127 -0
  280. package/third_party/s2geometry/src/s2/s2point_vector_shape_test.cc +59 -0
  281. package/third_party/s2geometry/src/s2/s2pointutil.cc +131 -0
  282. package/third_party/s2geometry/src/s2/s2pointutil.h +138 -0
  283. package/third_party/s2geometry/src/s2/s2pointutil_test.cc +157 -0
  284. package/third_party/s2geometry/src/s2/s2polygon.cc +1569 -0
  285. package/third_party/s2geometry/src/s2/s2polygon.h +934 -0
  286. package/third_party/s2geometry/src/s2/s2polygon_test.cc +3025 -0
  287. package/third_party/s2geometry/src/s2/s2polyline.cc +645 -0
  288. package/third_party/s2geometry/src/s2/s2polyline.h +379 -0
  289. package/third_party/s2geometry/src/s2/s2polyline_alignment.cc +414 -0
  290. package/third_party/s2geometry/src/s2/s2polyline_alignment.h +245 -0
  291. package/third_party/s2geometry/src/s2/s2polyline_alignment_internal.h +158 -0
  292. package/third_party/s2geometry/src/s2/s2polyline_alignment_test.cc +610 -0
  293. package/third_party/s2geometry/src/s2/s2polyline_measures.cc +42 -0
  294. package/third_party/s2geometry/src/s2/s2polyline_measures.h +53 -0
  295. package/third_party/s2geometry/src/s2/s2polyline_measures_test.cc +57 -0
  296. package/third_party/s2geometry/src/s2/s2polyline_simplifier.cc +187 -0
  297. package/third_party/s2geometry/src/s2/s2polyline_simplifier.h +109 -0
  298. package/third_party/s2geometry/src/s2/s2polyline_simplifier_test.cc +165 -0
  299. package/third_party/s2geometry/src/s2/s2polyline_test.cc +554 -0
  300. package/third_party/s2geometry/src/s2/s2predicates.cc +1486 -0
  301. package/third_party/s2geometry/src/s2/s2predicates.h +282 -0
  302. package/third_party/s2geometry/src/s2/s2predicates_internal.h +135 -0
  303. package/third_party/s2geometry/src/s2/s2predicates_test.cc +1427 -0
  304. package/third_party/s2geometry/src/s2/s2projections.cc +109 -0
  305. package/third_party/s2geometry/src/s2/s2projections.h +161 -0
  306. package/third_party/s2geometry/src/s2/s2projections_test.cc +78 -0
  307. package/third_party/s2geometry/src/s2/s2r2rect.cc +88 -0
  308. package/third_party/s2geometry/src/s2/s2r2rect.h +292 -0
  309. package/third_party/s2geometry/src/s2/s2r2rect_test.cc +312 -0
  310. package/third_party/s2geometry/src/s2/s2region.cc +26 -0
  311. package/third_party/s2geometry/src/s2/s2region.h +142 -0
  312. package/third_party/s2geometry/src/s2/s2region_coverer.cc +514 -0
  313. package/third_party/s2geometry/src/s2/s2region_coverer.h +356 -0
  314. package/third_party/s2geometry/src/s2/s2region_coverer_test.cc +509 -0
  315. package/third_party/s2geometry/src/s2/s2region_intersection.cc +84 -0
  316. package/third_party/s2geometry/src/s2/s2region_intersection.h +79 -0
  317. package/third_party/s2geometry/src/s2/s2region_term_indexer.cc +270 -0
  318. package/third_party/s2geometry/src/s2/s2region_term_indexer.h +299 -0
  319. package/third_party/s2geometry/src/s2/s2region_term_indexer_test.cc +209 -0
  320. package/third_party/s2geometry/src/s2/s2region_test.cc +370 -0
  321. package/third_party/s2geometry/src/s2/s2region_union.cc +90 -0
  322. package/third_party/s2geometry/src/s2/s2region_union.h +83 -0
  323. package/third_party/s2geometry/src/s2/s2region_union_test.cc +89 -0
  324. package/third_party/s2geometry/src/s2/s2shape.h +283 -0
  325. package/third_party/s2geometry/src/s2/s2shape_index.cc +321 -0
  326. package/third_party/s2geometry/src/s2/s2shape_index.h +781 -0
  327. package/third_party/s2geometry/src/s2/s2shape_index_buffered_region.cc +113 -0
  328. package/third_party/s2geometry/src/s2/s2shape_index_buffered_region.h +135 -0
  329. package/third_party/s2geometry/src/s2/s2shape_index_buffered_region_test.cc +162 -0
  330. package/third_party/s2geometry/src/s2/s2shape_index_measures.cc +92 -0
  331. package/third_party/s2geometry/src/s2/s2shape_index_measures.h +100 -0
  332. package/third_party/s2geometry/src/s2/s2shape_index_measures_test.cc +136 -0
  333. package/third_party/s2geometry/src/s2/s2shape_index_region.h +350 -0
  334. package/third_party/s2geometry/src/s2/s2shape_index_region_test.cc +161 -0
  335. package/third_party/s2geometry/src/s2/s2shape_index_test.cc +24 -0
  336. package/third_party/s2geometry/src/s2/s2shape_measures.cc +138 -0
  337. package/third_party/s2geometry/src/s2/s2shape_measures.h +95 -0
  338. package/third_party/s2geometry/src/s2/s2shape_measures_test.cc +139 -0
  339. package/third_party/s2geometry/src/s2/s2shapeutil_build_polygon_boundaries.cc +120 -0
  340. package/third_party/s2geometry/src/s2/s2shapeutil_build_polygon_boundaries.h +66 -0
  341. package/third_party/s2geometry/src/s2/s2shapeutil_build_polygon_boundaries_test.cc +170 -0
  342. package/third_party/s2geometry/src/s2/s2shapeutil_coding.cc +253 -0
  343. package/third_party/s2geometry/src/s2/s2shapeutil_coding.h +283 -0
  344. package/third_party/s2geometry/src/s2/s2shapeutil_coding_test.cc +54 -0
  345. package/third_party/s2geometry/src/s2/s2shapeutil_contains_brute_force.cc +40 -0
  346. package/third_party/s2geometry/src/s2/s2shapeutil_contains_brute_force.h +41 -0
  347. package/third_party/s2geometry/src/s2/s2shapeutil_contains_brute_force_test.cc +55 -0
  348. package/third_party/s2geometry/src/s2/s2shapeutil_count_edges.h +57 -0
  349. package/third_party/s2geometry/src/s2/s2shapeutil_count_edges_test.cc +43 -0
  350. package/third_party/s2geometry/src/s2/s2shapeutil_edge_iterator.cc +45 -0
  351. package/third_party/s2geometry/src/s2/s2shapeutil_edge_iterator.h +72 -0
  352. package/third_party/s2geometry/src/s2/s2shapeutil_edge_iterator_test.cc +116 -0
  353. package/third_party/s2geometry/src/s2/s2shapeutil_get_reference_point.cc +107 -0
  354. package/third_party/s2geometry/src/s2/s2shapeutil_get_reference_point.h +48 -0
  355. package/third_party/s2geometry/src/s2/s2shapeutil_get_reference_point_test.cc +104 -0
  356. package/third_party/s2geometry/src/s2/s2shapeutil_range_iterator.cc +58 -0
  357. package/third_party/s2geometry/src/s2/s2shapeutil_range_iterator.h +65 -0
  358. package/third_party/s2geometry/src/s2/s2shapeutil_range_iterator_test.cc +61 -0
  359. package/third_party/s2geometry/src/s2/s2shapeutil_shape_edge.h +58 -0
  360. package/third_party/s2geometry/src/s2/s2shapeutil_shape_edge_id.h +97 -0
  361. package/third_party/s2geometry/src/s2/s2shapeutil_testing.cc +104 -0
  362. package/third_party/s2geometry/src/s2/s2shapeutil_testing.h +36 -0
  363. package/third_party/s2geometry/src/s2/s2shapeutil_visit_crossing_edge_pairs.cc +440 -0
  364. package/third_party/s2geometry/src/s2/s2shapeutil_visit_crossing_edge_pairs.h +72 -0
  365. package/third_party/s2geometry/src/s2/s2shapeutil_visit_crossing_edge_pairs_test.cc +184 -0
  366. package/third_party/s2geometry/src/s2/s2testing.cc +464 -0
  367. package/third_party/s2geometry/src/s2/s2testing.h +385 -0
  368. package/third_party/s2geometry/src/s2/s2testing_test.cc +166 -0
  369. package/third_party/s2geometry/src/s2/s2text_format.cc +506 -0
  370. package/third_party/s2geometry/src/s2/s2text_format.h +289 -0
  371. package/third_party/s2geometry/src/s2/s2text_format_test.cc +417 -0
  372. package/third_party/s2geometry/src/s2/s2wedge_relations.cc +80 -0
  373. package/third_party/s2geometry/src/s2/s2wedge_relations.h +64 -0
  374. package/third_party/s2geometry/src/s2/s2wedge_relations_test.cc +89 -0
  375. package/third_party/s2geometry/src/s2/sequence_lexicon.h +296 -0
  376. package/third_party/s2geometry/src/s2/sequence_lexicon_test.cc +113 -0
  377. package/third_party/s2geometry/src/s2/strings/ostringstream.cc +35 -0
  378. package/third_party/s2geometry/src/s2/strings/ostringstream.h +105 -0
  379. package/third_party/s2geometry/src/s2/strings/serialize.cc +46 -0
  380. package/third_party/s2geometry/src/s2/strings/serialize.h +40 -0
  381. package/third_party/s2geometry/src/s2/third_party/absl/algorithm/algorithm.h +187 -0
  382. package/third_party/s2geometry/src/s2/third_party/absl/base/attributes.h +666 -0
  383. package/third_party/s2geometry/src/s2/third_party/absl/base/casts.h +189 -0
  384. package/third_party/s2geometry/src/s2/third_party/absl/base/config.h +462 -0
  385. package/third_party/s2geometry/src/s2/third_party/absl/base/dynamic_annotations.cc +129 -0
  386. package/third_party/s2geometry/src/s2/third_party/absl/base/dynamic_annotations.h +394 -0
  387. package/third_party/s2geometry/src/s2/third_party/absl/base/internal/atomic_hook.h +168 -0
  388. package/third_party/s2geometry/src/s2/third_party/absl/base/internal/identity.h +33 -0
  389. package/third_party/s2geometry/src/s2/third_party/absl/base/internal/inline_variable.h +117 -0
  390. package/third_party/s2geometry/src/s2/third_party/absl/base/internal/invoke.h +188 -0
  391. package/third_party/s2geometry/src/s2/third_party/absl/base/internal/raw_logging.cc +254 -0
  392. package/third_party/s2geometry/src/s2/third_party/absl/base/internal/raw_logging.h +205 -0
  393. package/third_party/s2geometry/src/s2/third_party/absl/base/internal/throw_delegate.cc +106 -0
  394. package/third_party/s2geometry/src/s2/third_party/absl/base/internal/throw_delegate.h +71 -0
  395. package/third_party/s2geometry/src/s2/third_party/absl/base/internal/unaligned_access.h +322 -0
  396. package/third_party/s2geometry/src/s2/third_party/absl/base/log_severity.h +77 -0
  397. package/third_party/s2geometry/src/s2/third_party/absl/base/macros.h +236 -0
  398. package/third_party/s2geometry/src/s2/third_party/absl/base/optimization.h +177 -0
  399. package/third_party/s2geometry/src/s2/third_party/absl/base/policy_checks.h +124 -0
  400. package/third_party/s2geometry/src/s2/third_party/absl/base/port.h +97 -0
  401. package/third_party/s2geometry/src/s2/third_party/absl/base/thread_annotations.h +277 -0
  402. package/third_party/s2geometry/src/s2/third_party/absl/container/fixed_array.h +523 -0
  403. package/third_party/s2geometry/src/s2/third_party/absl/container/inlined_vector.h +1453 -0
  404. package/third_party/s2geometry/src/s2/third_party/absl/container/internal/compressed_tuple.h +191 -0
  405. package/third_party/s2geometry/src/s2/third_party/absl/container/internal/container_memory.h +424 -0
  406. package/third_party/s2geometry/src/s2/third_party/absl/container/internal/layout.h +739 -0
  407. package/third_party/s2geometry/src/s2/third_party/absl/memory/memory.h +755 -0
  408. package/third_party/s2geometry/src/s2/third_party/absl/meta/type_traits.h +436 -0
  409. package/third_party/s2geometry/src/s2/third_party/absl/numeric/int128.cc +232 -0
  410. package/third_party/s2geometry/src/s2/third_party/absl/numeric/int128.h +656 -0
  411. package/third_party/s2geometry/src/s2/third_party/absl/numeric/int128_have_intrinsic.inc +3 -0
  412. package/third_party/s2geometry/src/s2/third_party/absl/numeric/int128_no_intrinsic.inc +3 -0
  413. package/third_party/s2geometry/src/s2/third_party/absl/strings/ascii.cc +198 -0
  414. package/third_party/s2geometry/src/s2/third_party/absl/strings/ascii.h +239 -0
  415. package/third_party/s2geometry/src/s2/third_party/absl/strings/ascii_ctype.h +66 -0
  416. package/third_party/s2geometry/src/s2/third_party/absl/strings/internal/bits.h +53 -0
  417. package/third_party/s2geometry/src/s2/third_party/absl/strings/internal/memutil.cc +110 -0
  418. package/third_party/s2geometry/src/s2/third_party/absl/strings/internal/memutil.h +146 -0
  419. package/third_party/s2geometry/src/s2/third_party/absl/strings/internal/resize_uninitialized.h +72 -0
  420. package/third_party/s2geometry/src/s2/third_party/absl/strings/match.cc +38 -0
  421. package/third_party/s2geometry/src/s2/third_party/absl/strings/match.h +89 -0
  422. package/third_party/s2geometry/src/s2/third_party/absl/strings/numbers.cc +909 -0
  423. package/third_party/s2geometry/src/s2/third_party/absl/strings/numbers.h +187 -0
  424. package/third_party/s2geometry/src/s2/third_party/absl/strings/str_cat.cc +240 -0
  425. package/third_party/s2geometry/src/s2/third_party/absl/strings/str_cat.h +398 -0
  426. package/third_party/s2geometry/src/s2/third_party/absl/strings/str_join.h +22 -0
  427. package/third_party/s2geometry/src/s2/third_party/absl/strings/str_split.cc +47 -0
  428. package/third_party/s2geometry/src/s2/third_party/absl/strings/str_split.h +43 -0
  429. package/third_party/s2geometry/src/s2/third_party/absl/strings/string_view.cc +245 -0
  430. package/third_party/s2geometry/src/s2/third_party/absl/strings/string_view.h +602 -0
  431. package/third_party/s2geometry/src/s2/third_party/absl/strings/strip.cc +42 -0
  432. package/third_party/s2geometry/src/s2/third_party/absl/strings/strip.h +130 -0
  433. package/third_party/s2geometry/src/s2/third_party/absl/types/span.h +793 -0
  434. package/third_party/s2geometry/src/s2/third_party/absl/utility/utility.h +299 -0
  435. package/third_party/s2geometry/src/s2/util/bits/bit-interleave.cc +274 -0
  436. package/third_party/s2geometry/src/s2/util/bits/bit-interleave.h +53 -0
  437. package/third_party/s2geometry/src/s2/util/bits/bits.cc +155 -0
  438. package/third_party/s2geometry/src/s2/util/bits/bits.h +745 -0
  439. package/third_party/s2geometry/src/s2/util/coding/coder.cc +83 -0
  440. package/third_party/s2geometry/src/s2/util/coding/coder.h +553 -0
  441. package/third_party/s2geometry/src/s2/util/coding/nth-derivative.h +134 -0
  442. package/third_party/s2geometry/src/s2/util/coding/transforms.h +62 -0
  443. package/third_party/s2geometry/src/s2/util/coding/varint.cc +289 -0
  444. package/third_party/s2geometry/src/s2/util/coding/varint.h +476 -0
  445. package/third_party/s2geometry/src/s2/util/endian/endian.h +859 -0
  446. package/third_party/s2geometry/src/s2/util/gtl/btree.h +2471 -0
  447. package/third_party/s2geometry/src/s2/util/gtl/btree_container.h +411 -0
  448. package/third_party/s2geometry/src/s2/util/gtl/btree_map.h +79 -0
  449. package/third_party/s2geometry/src/s2/util/gtl/btree_set.h +73 -0
  450. package/third_party/s2geometry/src/s2/util/gtl/compact_array.h +653 -0
  451. package/third_party/s2geometry/src/s2/util/gtl/container_logging.h +291 -0
  452. package/third_party/s2geometry/src/s2/util/gtl/dense_hash_set.h +358 -0
  453. package/third_party/s2geometry/src/s2/util/gtl/densehashtable.h +1493 -0
  454. package/third_party/s2geometry/src/s2/util/gtl/hashtable_common.h +253 -0
  455. package/third_party/s2geometry/src/s2/util/gtl/layout.h +28 -0
  456. package/third_party/s2geometry/src/s2/util/gtl/legacy_random_shuffle.h +77 -0
  457. package/third_party/s2geometry/src/s2/util/hash/mix.h +76 -0
  458. package/third_party/s2geometry/src/s2/util/math/exactfloat/exactfloat.cc +832 -0
  459. package/third_party/s2geometry/src/s2/util/math/exactfloat/exactfloat.h +646 -0
  460. package/third_party/s2geometry/src/s2/util/math/mathutil.cc +75 -0
  461. package/third_party/s2geometry/src/s2/util/math/mathutil.h +189 -0
  462. package/third_party/s2geometry/src/s2/util/math/matrix3x3.h +574 -0
  463. package/third_party/s2geometry/src/s2/util/math/vector.h +569 -0
  464. package/third_party/s2geometry/src/s2/util/math/vector3_hash.h +54 -0
  465. package/third_party/s2geometry/src/s2/util/units/length-units.cc +21 -0
  466. package/third_party/s2geometry/src/s2/util/units/length-units.h +135 -0
  467. package/third_party/s2geometry/src/s2/util/units/physical-units.h +313 -0
  468. package/third_party/s2geometry/src/s2/value_lexicon.h +234 -0
  469. package/third_party/s2geometry/src/s2/value_lexicon_test.cc +121 -0
  470. package/third_party/s2geometry/third_party/cmake/FindGFlags.cmake +48 -0
  471. package/third_party/s2geometry/third_party/cmake/FindGlog.cmake +48 -0
@@ -0,0 +1,2391 @@
1
+ // Copyright 2017 Google Inc. All Rights Reserved.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS-IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+
16
+ // Author: ericv@google.com (Eric Veach)
17
+ //
18
+ // Boolean operations are implemented by constructing the boundary of the
19
+ // result and then using S2Builder to assemble the edges. The boundary is
20
+ // obtained by clipping each of the two input regions to the interior or
21
+ // exterior of the other region. For example, to compute the union of A and
22
+ // B, we clip the boundary of A to the exterior of B and the boundary of B to
23
+ // the exterior of A; the resulting set of edges defines the union of the two
24
+ // regions.
25
+ //
26
+ // We use exact predicates, but inexact constructions (e.g. computing the
27
+ // intersection point of two edges). Nevertheless, the following algorithm is
28
+ // guaranteed to be 100% robust, in that the computed boundary stays within a
29
+ // small tolerance (snap_radius + S2::kIntersectionError) of the exact
30
+ // result, and also preserves the correct topology (i.e., no crossing edges).
31
+ //
32
+ // Unfortunately this robustness cannot quite be achieved using the strategy
33
+ // outlined above (clipping the two input regions and assembling the
34
+ // resulting edges). Since computed intersection points are not exact, the
35
+ // input geometry passed to S2Builder might contain self-intersections, and
36
+ // these self-intersections cannot be eliminated reliably by snap rounding.
37
+ //
38
+ // So instead, we pass S2Builder the entire set of input edges where at least
39
+ // some portion of each edge belongs to the output boundary. We allow
40
+ // S2Builder to compute the intersection points and snap round the edges
41
+ // (which it does in a way that is guaranteed to preserve the input topology).
42
+ // Then once this is finished, we remove the portions of each edge that would
43
+ // have been clipped if we had done the clipping first. This step only
44
+ // involves deciding whether to keep or discard each edge in the output, since
45
+ // all intersection points have already been resolved, and therefore there is
46
+ // no risk of creating new self-intersections.
47
+ //
48
+ // This is implemented using the following classes:
49
+ //
50
+ // - S2BooleanOperation::Impl: the top-level class that clips each of
51
+ // the two regions to the other region.
52
+ //
53
+ // - CrossingProcessor: a class that processes edge crossings and maintains
54
+ // the necessary state in order to clip the boundary
55
+ // of one region to the interior or exterior of the
56
+ // other region.
57
+ //
58
+ // - EdgeClippingLayer: an S2Builder::Layer that removes graph edges that
59
+ // correspond to clipped portions of input edges, and
60
+ // passes the result to another layer for assembly.
61
+ //
62
+ // - GraphEdgeClipper: a helper class that does the actual work of the
63
+ // EdgeClippingLayer.
64
+
65
+ #include "s2/s2boolean_operation.h"
66
+
67
+ #include <algorithm>
68
+ #include <limits>
69
+ #include <memory>
70
+ #include <utility>
71
+
72
+ #include "s2/util/gtl/btree_map.h"
73
+ #include "s2/third_party/absl/memory/memory.h"
74
+ #include "s2/s2builder.h"
75
+ #include "s2/s2builder_layer.h"
76
+ #include "s2/s2builderutil_snap_functions.h"
77
+ #include "s2/s2contains_point_query.h"
78
+ #include "s2/s2crossing_edge_query.h"
79
+ #include "s2/s2edge_crosser.h"
80
+ #include "s2/s2edge_crossings.h"
81
+ #include "s2/s2measures.h"
82
+ #include "s2/s2predicates.h"
83
+ #include "s2/s2shape_index_measures.h"
84
+ #include "s2/s2shapeutil_visit_crossing_edge_pairs.h"
85
+
86
+ // TODO(ericv): Remove this debugging output at some point.
87
+ extern bool s2builder_verbose;
88
+
89
+ namespace { // Anonymous namespace for helper classes.
90
+
91
+ using absl::make_unique;
92
+ using std::make_pair;
93
+ using std::max;
94
+ using std::min;
95
+ using std::pair;
96
+ using std::swap;
97
+ using std::unique_ptr;
98
+ using std::vector;
99
+
100
+ using EdgeType = S2Builder::EdgeType;
101
+ using SnapFunction = S2Builder::SnapFunction;
102
+ using GraphOptions = S2Builder::GraphOptions;
103
+ using DegenerateEdges = GraphOptions::DegenerateEdges;
104
+ using DuplicateEdges = GraphOptions::DuplicateEdges;
105
+ using SiblingPairs = GraphOptions::SiblingPairs;
106
+
107
+ using Graph = S2Builder::Graph;
108
+ using EdgeId = Graph::EdgeId;
109
+ using VertexId = Graph::VertexId;
110
+ using InputEdgeId = Graph::InputEdgeId;
111
+ using InputEdgeIdSetId = Graph::InputEdgeIdSetId;
112
+
113
+ using PolygonModel = S2BooleanOperation::PolygonModel;
114
+ using PolylineModel = S2BooleanOperation::PolylineModel;
115
+ using Precision = S2BooleanOperation::Precision;
116
+
117
+ // A collection of special InputEdgeIds that allow the GraphEdgeClipper state
118
+ // modifications to be inserted into the list of edge crossings.
119
+ static const InputEdgeId kSetInside = -1;
120
+ static const InputEdgeId kSetInvertB = -2;
121
+ static const InputEdgeId kSetReverseA = -3;
122
+
123
+ // CrossingInputEdge represents an input edge B that crosses some other input
124
+ // edge A. It stores the input edge id of edge B and also whether it crosses
125
+ // edge A from left to right (or vice versa).
126
+ class CrossingInputEdge {
127
+ public:
128
+ // Indicates that input edge "input_id" crosses another edge (from left to
129
+ // right if "left_to_right" is true).
130
+ CrossingInputEdge(InputEdgeId input_id, bool left_to_right)
131
+ : left_to_right_(left_to_right), input_id_(input_id) {
132
+ }
133
+
134
+ InputEdgeId input_id() const { return input_id_; }
135
+ bool left_to_right() const { return left_to_right_; }
136
+
137
+ bool operator<(const CrossingInputEdge& other) const {
138
+ return input_id_ < other.input_id_;
139
+ }
140
+ bool operator<(const InputEdgeId& other) const {
141
+ return input_id_ < other;
142
+ }
143
+
144
+ private:
145
+ bool left_to_right_ : 1;
146
+ InputEdgeId input_id_ : 31;
147
+ };
148
+
149
+ // InputEdgeCrossings represents all pairs of intersecting input edges.
150
+ // It is sorted in lexicographic order.
151
+ using InputEdgeCrossings = vector<pair<InputEdgeId, CrossingInputEdge>>;
152
+
153
+ // Given two input edges A and B that intersect, suppose that A maps to a
154
+ // chain of snapped edges A_0, A_1, ..., A_m and B maps to a chain of snapped
155
+ // edges B_0, B_1, ..., B_n. CrossingGraphEdge represents an edge from chain
156
+ // B that shares a vertex with chain A. It is used as a temporary data
157
+ // representation while processing chain A. The arguments are:
158
+ //
159
+ // "id" - the Graph::EdgeId of an edge from chain B.
160
+ // "a_index" - the index of the vertex (A_i) that is shared with chain A.
161
+ // "outgoing" - true if the shared vertex is the first vertex of the B edge.
162
+ // "dst" - the Graph::VertexId of the vertex that is not shared with chain A.
163
+ //
164
+ // Note that if an edge from the B chain shares both vertices with the A
165
+ // chain, there will be two entries: an outgoing edge that treats its first
166
+ // vertex as being shared, and an incoming edge that treats its second vertex
167
+ // as being shared.
168
+ struct CrossingGraphEdge {
169
+ CrossingGraphEdge(EdgeId _id, int _a_index, bool _outgoing, VertexId _dst)
170
+ : id(_id), a_index(_a_index), outgoing(_outgoing), dst(_dst) {
171
+ }
172
+ EdgeId id;
173
+ int a_index;
174
+ bool outgoing;
175
+ VertexId dst;
176
+ };
177
+ using CrossingGraphEdgeVector = absl::InlinedVector<CrossingGraphEdge, 2>;
178
+
179
+ // Returns a vector of EdgeIds sorted by input edge id. When more than one
180
+ // output edge has the same input edge id (i.e., the input edge snapped to a
181
+ // chain of edges), the edges are sorted so that they form a directed edge
182
+ // chain.
183
+ //
184
+ // This function could possibily be moved to S2Builder::Graph, but note that
185
+ // it has special requirements. Namely, duplicate edges and sibling pairs
186
+ // must be kept in order to ensure that every output edge corresponds to
187
+ // exactly one input edge. (See also S2Builder::Graph::GetInputEdgeOrder.)
188
+ static vector<EdgeId> GetInputEdgeChainOrder(
189
+ const Graph& g, const vector<InputEdgeId>& input_ids) {
190
+
191
+ S2_DCHECK(g.options().edge_type() == EdgeType::DIRECTED);
192
+ S2_DCHECK(g.options().duplicate_edges() == DuplicateEdges::KEEP);
193
+ S2_DCHECK(g.options().sibling_pairs() == SiblingPairs::KEEP);
194
+
195
+ // First, sort the edges so that the edges corresponding to each input edge
196
+ // are consecutive. (Each input edge was snapped to a chain of output
197
+ // edges, or two chains in the case of undirected input edges.)
198
+ vector<EdgeId> order = g.GetInputEdgeOrder(input_ids);
199
+
200
+ // Now sort the group of edges corresponding to each input edge in edge
201
+ // chain order (e.g. AB, BC, CD).
202
+ vector<pair<VertexId, EdgeId>> vmap; // Map from source vertex to edge id.
203
+ vector<int> indegree(g.num_vertices()); // Restricted to current input edge.
204
+ for (int end, begin = 0; begin < order.size(); begin = end) {
205
+ // Gather the edges that came from a single input edge.
206
+ InputEdgeId input_id = input_ids[order[begin]];
207
+ for (end = begin; end < order.size(); ++end) {
208
+ if (input_ids[order[end]] != input_id) break;
209
+ }
210
+ if (end - begin == 1) continue;
211
+
212
+ // Build a map from the source vertex of each edge to its edge id,
213
+ // and also compute the indegree at each vertex considering only the edges
214
+ // that came from the current input edge.
215
+ for (int i = begin; i < end; ++i) {
216
+ EdgeId e = order[i];
217
+ vmap.push_back(make_pair(g.edge(e).first, e));
218
+ indegree[g.edge(e).second] += 1;
219
+ }
220
+ std::sort(vmap.begin(), vmap.end());
221
+
222
+ // Find the starting edge for building the edge chain.
223
+ EdgeId next = g.num_edges();
224
+ for (int i = begin; i < end; ++i) {
225
+ EdgeId e = order[i];
226
+ if (indegree[g.edge(e).first] == 0) next = e;
227
+ }
228
+ // Build the edge chain.
229
+ for (int i = begin; ;) {
230
+ order[i] = next;
231
+ VertexId v = g.edge(next).second;
232
+ indegree[v] = 0; // Clear as we go along.
233
+ if (++i == end) break;
234
+ auto out = lower_bound(vmap.begin(), vmap.end(), make_pair(v, 0));
235
+ S2_DCHECK_EQ(v, out->first);
236
+ next = out->second;
237
+ }
238
+ vmap.clear();
239
+ }
240
+ return order;
241
+ }
242
+
243
+ // Given a set of clipping instructions encoded as a set of InputEdgeCrossings,
244
+ // GraphEdgeClipper determines which graph edges correspond to clipped
245
+ // portions of input edges and removes them.
246
+ //
247
+ // The clipping model is as follows. The input consists of edge chains. The
248
+ // clipper maintains an "inside" boolean state as it clips each chain, and
249
+ // toggles this state whenever an input edge is crossed. Any edges that are
250
+ // deemed to be "outside" after clipping are removed.
251
+ //
252
+ // The "inside" state can be reset when necessary (e.g., when jumping to the
253
+ // start of a new chain) by adding a special crossing marked kSetInside.
254
+ // There are also two other special "crossings" that modify the clipping
255
+ // parameters: kSetInvertB specifies that edges should be clipped to the
256
+ // exterior of the other region, and kSetReverseA specifies that edges should
257
+ // be reversed before emitting them (which is needed to implement difference
258
+ // operations).
259
+ class GraphEdgeClipper {
260
+ public:
261
+ // "input_dimensions" is a vector specifying the dimension of each input
262
+ // edge (0, 1, or 2). "input_crossings" is the set of all crossings to be
263
+ // used when clipping the edges of "g", sorted in lexicographic order.
264
+ //
265
+ // The clipped set of edges and their corresponding set of input edge ids
266
+ // are returned in "new_edges" and "new_input_edge_ids". (These can be used
267
+ // to construct a new S2Builder::Graph.)
268
+ GraphEdgeClipper(const Graph& g, const vector<int8>& input_dimensions,
269
+ const InputEdgeCrossings& input_crossings,
270
+ vector<Graph::Edge>* new_edges,
271
+ vector<InputEdgeIdSetId>* new_input_edge_ids);
272
+ void Run();
273
+
274
+ private:
275
+ void AddEdge(Graph::Edge edge, InputEdgeId input_edge_id);
276
+ void GatherIncidentEdges(
277
+ const vector<VertexId>& a, int ai,
278
+ const vector<CrossingInputEdge>& b_input_edges,
279
+ vector<CrossingGraphEdgeVector>* b_edges) const;
280
+ int GetCrossedVertexIndex(
281
+ const vector<VertexId>& a, const CrossingGraphEdgeVector& b,
282
+ bool left_to_right) const;
283
+ int GetVertexRank(const CrossingGraphEdge& e) const;
284
+ bool EdgeChainOnLeft(const vector<VertexId>& a,
285
+ EdgeId b_first, EdgeId b_last) const;
286
+
287
+ const Graph& g_;
288
+ Graph::VertexInMap in_;
289
+ Graph::VertexOutMap out_;
290
+ const vector<int8>& input_dimensions_;
291
+ const InputEdgeCrossings& input_crossings_;
292
+ vector<Graph::Edge>* new_edges_;
293
+ vector<InputEdgeIdSetId>* new_input_edge_ids_;
294
+
295
+ // Every graph edge is associated with exactly one input edge in our case,
296
+ // which means that we can declare g_.input_edge_id_set_ids() as a vector of
297
+ // InputEdgeIds rather than a vector of InputEdgeIdSetIds. (This also takes
298
+ // advantage of the fact that IdSetLexicon represents a singleton set as the
299
+ // value of its single element.)
300
+ const vector<InputEdgeId>& input_ids_;
301
+
302
+ vector<EdgeId> order_; // Graph edges sorted in input edge id order.
303
+ vector<int> rank_; // The rank of each graph edge within order_.
304
+ };
305
+
306
+ GraphEdgeClipper::GraphEdgeClipper(
307
+ const Graph& g, const vector<int8>& input_dimensions,
308
+ const InputEdgeCrossings& input_crossings,
309
+ vector<Graph::Edge>* new_edges,
310
+ vector<InputEdgeIdSetId>* new_input_edge_ids)
311
+ : g_(g), in_(g), out_(g),
312
+ input_dimensions_(input_dimensions),
313
+ input_crossings_(input_crossings),
314
+ new_edges_(new_edges),
315
+ new_input_edge_ids_(new_input_edge_ids),
316
+ input_ids_(g.input_edge_id_set_ids()),
317
+ order_(GetInputEdgeChainOrder(g_, input_ids_)),
318
+ rank_(order_.size()) {
319
+ for (int i = 0; i < order_.size(); ++i) {
320
+ rank_[order_[i]] = i;
321
+ }
322
+ }
323
+
324
+ inline void GraphEdgeClipper::AddEdge(Graph::Edge edge,
325
+ InputEdgeId input_edge_id) {
326
+ new_edges_->push_back(edge);
327
+ new_input_edge_ids_->push_back(input_edge_id);
328
+ }
329
+
330
+ void GraphEdgeClipper::Run() {
331
+ // Declare vectors here and reuse them to avoid reallocation.
332
+ vector<VertexId> a_vertices;
333
+ vector<int> a_num_crossings;
334
+ vector<bool> a_isolated;
335
+ vector<CrossingInputEdge> b_input_edges;
336
+ vector<CrossingGraphEdgeVector> b_edges;
337
+
338
+ bool inside = false;
339
+ bool invert_b = false;
340
+ bool reverse_a = false;
341
+ auto next = input_crossings_.begin();
342
+ for (int i = 0; i < order_.size(); ++i) {
343
+ // For each input edge (the "A" input edge), gather all the input edges
344
+ // that cross it (the "B" input edges).
345
+ InputEdgeId a_input_id = input_ids_[order_[i]];
346
+ const Graph::Edge& edge0 = g_.edge(order_[i]);
347
+ b_input_edges.clear();
348
+ for (; next != input_crossings_.end(); ++next) {
349
+ if (next->first != a_input_id) break;
350
+ if (next->second.input_id() >= 0) {
351
+ b_input_edges.push_back(next->second);
352
+ } else if (next->second.input_id() == kSetInside) {
353
+ inside = next->second.left_to_right();
354
+ } else if (next->second.input_id() == kSetInvertB) {
355
+ invert_b = next->second.left_to_right();
356
+ } else {
357
+ S2_DCHECK_EQ(next->second.input_id(), kSetReverseA);
358
+ reverse_a = next->second.left_to_right();
359
+ }
360
+ }
361
+ // Optimization for degenerate edges.
362
+ // TODO(ericv): If the output layer for this edge dimension specifies
363
+ // DegenerateEdges::DISCARD, then remove the edge here.
364
+ if (edge0.first == edge0.second) {
365
+ inside ^= (b_input_edges.size() & 1);
366
+ AddEdge(edge0, a_input_id);
367
+ continue;
368
+ }
369
+ // Optimization for the case where there are no crossings.
370
+ if (b_input_edges.empty()) {
371
+ // In general the caller only passes edges that are part of the output
372
+ // (i.e., we could S2_DCHECK(inside) here). The one exception is for
373
+ // polyline/polygon operations, where the polygon edges are needed to
374
+ // compute the polyline output but are not emitted themselves.
375
+ if (inside) {
376
+ AddEdge(reverse_a ? Graph::reverse(edge0) : edge0, a_input_id);
377
+ }
378
+ continue;
379
+ }
380
+ // Walk along the chain of snapped edges for input edge A, and at each
381
+ // vertex collect all the incident edges that belong to one of the
382
+ // crossing edge chains (the "B" input edges).
383
+ a_vertices.clear();
384
+ a_vertices.push_back(edge0.first);
385
+ b_edges.clear();
386
+ b_edges.resize(b_input_edges.size());
387
+ GatherIncidentEdges(a_vertices, 0, b_input_edges, &b_edges);
388
+ for (; i < order_.size() && input_ids_[order_[i]] == a_input_id; ++i) {
389
+ a_vertices.push_back(g_.edge(order_[i]).second);
390
+ GatherIncidentEdges(a_vertices, a_vertices.size() - 1, b_input_edges,
391
+ &b_edges);
392
+ }
393
+ --i;
394
+ if (s2builder_verbose) {
395
+ std::cout << "input edge " << a_input_id << " (inside=" << inside << "):";
396
+ for (VertexId id : a_vertices) std::cout << " " << id;
397
+ }
398
+ // Now for each B edge chain, decide which vertex of the A chain it
399
+ // crosses, and keep track of the number of signed crossings at each A
400
+ // vertex. The sign of a crossing depends on whether the other edge
401
+ // crosses from left to right or right to left.
402
+ //
403
+ // This would not be necessary if all calculations were done in exact
404
+ // arithmetic, because crossings would have strictly alternating signs.
405
+ // But because we have already snapped the result, some crossing locations
406
+ // are ambiguous, and GetCrossedVertexIndex() handles this by choosing a
407
+ // candidate vertex arbitrarily. The end result is that rarely, we may
408
+ // see two crossings in a row with the same sign. We correct for this by
409
+ // adding extra output edges that essentially link up the crossings in the
410
+ // correct (alternating sign) order. Compared to the "correct" behavior,
411
+ // the only difference is that we have added some extra sibling pairs
412
+ // (consisting of an edge and its corresponding reverse edge) which do not
413
+ // affect the result.
414
+ a_num_crossings.clear();
415
+ a_num_crossings.resize(a_vertices.size());
416
+ a_isolated.clear();
417
+ a_isolated.resize(a_vertices.size());
418
+ for (int bi = 0; bi < b_input_edges.size(); ++bi) {
419
+ bool left_to_right = b_input_edges[bi].left_to_right();
420
+ int a_index = GetCrossedVertexIndex(a_vertices, b_edges[bi],
421
+ left_to_right);
422
+ if (a_index >= 0) {
423
+ if (s2builder_verbose) {
424
+ std::cout << std::endl << " " << "b input edge "
425
+ << b_input_edges[bi].input_id() << " (l2r=" << left_to_right
426
+ << ", crossing=" << a_vertices[a_index] << ")";
427
+ for (const auto& x : b_edges[bi]) {
428
+ const Graph::Edge& e = g_.edge(x.id);
429
+ std::cout << " (" << e.first << ", " << e.second << ")";
430
+ }
431
+ }
432
+ // Keep track of the number of signed crossings (see above).
433
+ bool is_line = input_dimensions_[b_input_edges[bi].input_id()] == 1;
434
+ int sign = is_line ? 0 : (left_to_right == invert_b) ? -1 : 1;
435
+ a_num_crossings[a_index] += sign;
436
+
437
+ // Any polyline or polygon vertex that has at least one crossing but no
438
+ // adjacent emitted edge may be emitted as an isolated vertex.
439
+ a_isolated[a_index] = true;
440
+ } else {
441
+ // TODO(b/112043775): fix this condition.
442
+ S2_LOG(DFATAL) << "Failed to get crossed vertex index.";
443
+ }
444
+ }
445
+ if (s2builder_verbose) std::cout << std::endl;
446
+
447
+ // Finally, we iterate through the A edge chain, keeping track of the
448
+ // number of signed crossings as we go along. The "multiplicity" is
449
+ // defined as the cumulative number of signed crossings, and indicates how
450
+ // many edges should be output (and in which direction) in order to link
451
+ // up the edge crossings in the correct order. (The multiplicity is
452
+ // almost always either 0 or 1 except in very rare cases.)
453
+ int multiplicity = inside + a_num_crossings[0];
454
+ for (int ai = 1; ai < a_vertices.size(); ++ai) {
455
+ if (multiplicity != 0) {
456
+ a_isolated[ai - 1] = a_isolated[ai] = false;
457
+ }
458
+ int edge_count = reverse_a ? -multiplicity : multiplicity;
459
+ // Output any forward edges required.
460
+ for (int i = 0; i < edge_count; ++i) {
461
+ AddEdge(Graph::Edge(a_vertices[ai - 1], a_vertices[ai]), a_input_id);
462
+ }
463
+ // Output any reverse edges required.
464
+ for (int i = edge_count; i < 0; ++i) {
465
+ AddEdge(Graph::Edge(a_vertices[ai], a_vertices[ai - 1]), a_input_id);
466
+ }
467
+ multiplicity += a_num_crossings[ai];
468
+ }
469
+ // Multiplicities other than 0 or 1 can only occur in the edge interior.
470
+ S2_DCHECK(multiplicity == 0 || multiplicity == 1);
471
+ inside = (multiplicity != 0);
472
+
473
+ // Output any isolated polyline vertices.
474
+ // TODO(ericv): Only do this if an output layer wants degenerate edges.
475
+ if (input_dimensions_[a_input_id] != 0) {
476
+ for (int ai = 0; ai < a_vertices.size(); ++ai) {
477
+ if (a_isolated[ai]) {
478
+ AddEdge(Graph::Edge(a_vertices[ai], a_vertices[ai]), a_input_id);
479
+ }
480
+ }
481
+ }
482
+ }
483
+ }
484
+
485
+ // Given the vertices of the snapped edge chain for an input edge A and the
486
+ // set of input edges B that cross input edge A, this method gathers all of
487
+ // the snapped edges of B that are incident to a given snapped vertex of A.
488
+ // The incident edges for each input edge of B are appended to a separate
489
+ // output vector. (A and B can refer to either the input edge or the
490
+ // corresponding snapped edge chain.)
491
+ void GraphEdgeClipper::GatherIncidentEdges(
492
+ const vector<VertexId>& a, int ai,
493
+ const vector<CrossingInputEdge>& b_input_edges,
494
+ vector<CrossingGraphEdgeVector>* b_edges) const {
495
+ // Examine all of the edges incident to the given vertex of A. If any edge
496
+ // comes from a B input edge, append it to the appropriate vector.
497
+ S2_DCHECK_EQ(b_input_edges.size(), b_edges->size());
498
+ for (EdgeId e : in_.edge_ids(a[ai])) {
499
+ InputEdgeId id = input_ids_[e];
500
+ auto it = lower_bound(b_input_edges.begin(), b_input_edges.end(), id);
501
+ if (it != b_input_edges.end() && it->input_id() == id) {
502
+ auto& edges = (*b_edges)[it - b_input_edges.begin()];
503
+ edges.push_back(CrossingGraphEdge(e, ai, false, g_.edge(e).first));
504
+ }
505
+ }
506
+ for (EdgeId e : out_.edge_ids(a[ai])) {
507
+ InputEdgeId id = input_ids_[e];
508
+ auto it = lower_bound(b_input_edges.begin(), b_input_edges.end(), id);
509
+ if (it != b_input_edges.end() && it->input_id() == id) {
510
+ auto& edges = (*b_edges)[it - b_input_edges.begin()];
511
+ edges.push_back(CrossingGraphEdge(e, ai, true, g_.edge(e).second));
512
+ }
513
+ }
514
+ }
515
+
516
+ // Returns the "vertex rank" of the shared vertex associated with the given
517
+ // CrossingGraphEdge. Recall that graph edges are sorted in input edge order,
518
+ // and that the rank of an edge is its position in this order (rank_[e]).
519
+ // VertexRank(e) is defined such that VertexRank(e.src) == rank_[e] and
520
+ // VertexRank(e.dst) == rank_[e] + 1. Note that the concept of "vertex rank"
521
+ // is only defined within a single edge chain (since different edge chains can
522
+ // have overlapping vertex ranks).
523
+ int GraphEdgeClipper::GetVertexRank(const CrossingGraphEdge& e) const {
524
+ return rank_[e.id] + !e.outgoing;
525
+ }
526
+
527
+ // Given an edge chain A that is crossed by another edge chain B (where
528
+ // "left_to_right" indicates whether B crosses A from left to right), this
529
+ // method decides which vertex of A the crossing takes place at. The
530
+ // parameters are the vertices of the A chain ("a") and the set of edges in
531
+ // the B chain ("b") that are incident to vertices of A. The B chain edges
532
+ // are sorted in increasing order of (a_index, outgoing) tuple.
533
+ int GraphEdgeClipper::GetCrossedVertexIndex(
534
+ const vector<VertexId>& a, const CrossingGraphEdgeVector& b,
535
+ bool left_to_right) const {
536
+ S2_DCHECK(!a.empty());
537
+ S2_DCHECK(!b.empty());
538
+
539
+ // The reason this calculation is tricky is that after snapping, the A and B
540
+ // chains may meet and separate several times. For example, if B crosses A
541
+ // from left to right, then B may touch A, make an excursion to the left of
542
+ // A, come back to A, then make an excursion to the right of A and come back
543
+ // to A again, like this:
544
+ //
545
+ // *--B--*-\ /-*-\
546
+ // B-\ /-B B-\ 6 7 8 9
547
+ // *--A--*--A--*-A,B-*--A--*--A--*-A,B-*--A--*--A--*-A,B-*
548
+ // 0 1 2 3 4 5 \-B B-/
549
+ // \-*-/
550
+ //
551
+ // (where "*" is a vertex, and "A" and "B" are edge labels). Note that B
552
+ // may also follow A for one or more edges whenever they touch (e.g. between
553
+ // vertices 2 and 3). In this case the only vertices of A where the
554
+ // crossing could take place are 5 and 6, i.e. after all excursions of B to
555
+ // the left of A, and before all excursions of B to the right of A.
556
+ //
557
+ // Other factors to consider are that the portion of B before and/or after
558
+ // the crossing may be degenerate, and some or all of the B edges may be
559
+ // reversed relative to the A edges.
560
+
561
+ // First, check whether edge A is degenerate.
562
+ int n = a.size();
563
+ if (n == 1) return 0;
564
+
565
+ // If edge chain B is incident to only one vertex of A, we're done.
566
+ if (b[0].a_index == b.back().a_index) return b[0].a_index;
567
+
568
+ // Determine whether the B chain visits the first and last vertices that it
569
+ // shares with the A chain in the same order or the reverse order. This is
570
+ // only needed to implement one special case (see below).
571
+ bool b_reversed = GetVertexRank(b[0]) > GetVertexRank(b.back());
572
+
573
+ // Examine each incident B edge and use it to narrow the range of positions
574
+ // where the crossing could occur in the B chain. Vertex positions are
575
+ // represented as a range [lo, hi] of vertex ranks in the B chain (see
576
+ // GetVertexRank).
577
+ //
578
+ // Note that if an edge of B is incident to the first or last vertex of A,
579
+ // we can't test which side of the A chain it is on. (An s2pred::Sign test
580
+ // doesn't work; e.g. if the B edge is XY and the first edge of A is YZ,
581
+ // then snapping can change the sign of XYZ while maintaining topological
582
+ // guarantees.) There can be up to 4 such edges (one incoming and one
583
+ // outgoing edge at each endpoint of A). Two of these edges logically
584
+ // extend past the end of the A chain and place no restrictions on the
585
+ // crossing vertex. The other two edges define the ends of the subchain
586
+ // where B shares vertices with A. We save these edges in order to handle a
587
+ // special case (see below).
588
+ int lo = -1, hi = order_.size(); // Vertex ranks of acceptable crossings
589
+ EdgeId b_first = -1, b_last = -1; // "b" subchain connecting "a" endpoints
590
+ for (const auto& e : b) {
591
+ int ai = e.a_index;
592
+ if (ai == 0) {
593
+ if (e.outgoing != b_reversed && e.dst != a[1]) b_first = e.id;
594
+ } else if (ai == n - 1) {
595
+ if (e.outgoing == b_reversed && e.dst != a[n - 2]) b_last = e.id;
596
+ } else {
597
+ // This B edge is incident to an interior vertex of the A chain. First
598
+ // check whether this edge is identical (or reversed) to an edge in the
599
+ // A chain, in which case it does not create any restrictions.
600
+ if (e.dst == a[ai - 1] || e.dst == a[ai + 1]) continue;
601
+
602
+ // Otherwise we can test which side of the A chain the edge lies on.
603
+ bool on_left = s2pred::OrderedCCW(g_.vertex(a[ai + 1]), g_.vertex(e.dst),
604
+ g_.vertex(a[ai - 1]), g_.vertex(a[ai]));
605
+
606
+ // Every B edge that is incident to an interior vertex of the A chain
607
+ // places some restriction on where the crossing vertex could be.
608
+ if (left_to_right == on_left) {
609
+ // This is a pre-crossing edge, so the crossing cannot be before the
610
+ // destination vertex of this edge. (For example, the input B edge
611
+ // crosses the input A edge from left to right and this edge of the B
612
+ // chain is to the left of the A chain.)
613
+ lo = max(lo, rank_[e.id] + 1);
614
+ } else {
615
+ // This is a post-crossing edge, so the crossing cannot be after the
616
+ // source vertex of this edge.
617
+ hi = min(hi, rank_[e.id]);
618
+ }
619
+ }
620
+ }
621
+ // There is one special case. If a subchain of B connects the first and
622
+ // last vertices of A, then together with the edges of A this forms a loop
623
+ // whose orientation can be tested to determine whether B is on the left or
624
+ // right side of A. This is only possible (and only necessary) if the B
625
+ // subchain does not include any interior vertices of A, since otherwise the
626
+ // B chain might cross from one side of A to the other.
627
+ //
628
+ // Note that it would be possible to avoid this test in some situations by
629
+ // checking whether either endpoint of the A chain has two incident B edges,
630
+ // in which case we could check which side of the B chain the A edge is on
631
+ // and use this to limit the possible crossing locations.
632
+ if (b_first >= 0 && b_last >= 0) {
633
+ // The B subchain connects the first and last vertices of A. Test whether
634
+ // the chain includes any interior vertices of A. We do this indirectly
635
+ // by testing whether any edge of B has restricted the range of allowable
636
+ // crossing vertices (since any interior edge of the B subchain incident
637
+ // to any interior edge of A is guaranteed to do so).
638
+ int min_rank = order_.size(), max_rank = -1;
639
+ for (const auto& e : b) {
640
+ min_rank = min(min_rank, GetVertexRank(e));
641
+ max_rank = max(max_rank, GetVertexRank(e));
642
+ }
643
+ if (lo <= min_rank && hi >= max_rank) {
644
+ // The B subchain is not incident to any interior vertex of A.
645
+ // Swap the edges if necessary so that they are in B chain order.
646
+ if (b_reversed) swap(b_first, b_last);
647
+ bool on_left = EdgeChainOnLeft(a, b_first, b_last);
648
+ if (left_to_right == on_left) {
649
+ lo = max(lo, rank_[b_last] + 1);
650
+ } else {
651
+ hi = min(hi, rank_[b_first]);
652
+ }
653
+ }
654
+ }
655
+
656
+ // Otherwise we choose the smallest shared VertexId in the acceptable range,
657
+ // in order to ensure that both chains choose the same crossing vertex.
658
+ int best = -1;
659
+ S2_DCHECK_LE(lo, hi);
660
+ for (const auto& e : b) {
661
+ int ai = e.a_index;
662
+ int vrank = GetVertexRank(e);
663
+ if (vrank >= lo && vrank <= hi && (best < 0 || a[ai] < a[best])) {
664
+ best = ai;
665
+ }
666
+ }
667
+ return best;
668
+ }
669
+
670
+ // Given edge chains A and B that form a loop (after possibly reversing the
671
+ // direction of chain B), returns true if chain B is to the left of chain A.
672
+ // Chain A is given as a sequence of vertices, while chain B is specified as
673
+ // the first and last edges of the chain.
674
+ bool GraphEdgeClipper::EdgeChainOnLeft(
675
+ const vector<VertexId>& a, EdgeId b_first, EdgeId b_last) const {
676
+ // Gather all the interior vertices of the B subchain.
677
+ vector<VertexId> loop;
678
+ for (int i = rank_[b_first]; i < rank_[b_last]; ++i) {
679
+ loop.push_back(g_.edge(order_[i]).second);
680
+ }
681
+ // Possibly reverse the chain so that it forms a loop when "a" is appended.
682
+ if (g_.edge(b_last).second != a[0]) std::reverse(loop.begin(), loop.end());
683
+ loop.insert(loop.end(), a.begin(), a.end());
684
+ // Duplicate the first two vertices to simplify vertex indexing.
685
+ for (int j = 0; j < 2; j++) {
686
+ loop.insert(loop.end(), *(loop.begin() + j));
687
+ }
688
+ // Now B is to the left of A if and only if the loop is counterclockwise.
689
+ double sum = 0;
690
+ for (int i = 2; i < loop.size(); ++i) {
691
+ sum += S2::TurnAngle(g_.vertex(loop[i - 2]), g_.vertex(loop[i - 1]),
692
+ g_.vertex(loop[i]));
693
+ }
694
+ return sum > 0;
695
+ }
696
+
697
+ // Given a set of clipping instructions encoded as a set of intersections
698
+ // between input edges, EdgeClippingLayer determines which graph edges
699
+ // correspond to clipped portions of input edges and removes them. It
700
+ // assembles the remaining edges into a new S2Builder::Graph and passes the
701
+ // result to the given output layer for assembly.
702
+ class EdgeClippingLayer : public S2Builder::Layer {
703
+ public:
704
+ EdgeClippingLayer(const vector<unique_ptr<S2Builder::Layer>>* layers,
705
+ const vector<int8>* input_dimensions,
706
+ const InputEdgeCrossings* input_crossings)
707
+ : layers_(*layers),
708
+ input_dimensions_(*input_dimensions),
709
+ input_crossings_(*input_crossings) {
710
+ }
711
+
712
+ // Layer interface:
713
+ GraphOptions graph_options() const override;
714
+ void Build(const Graph& g, S2Error* error) override;
715
+
716
+ private:
717
+ const vector<unique_ptr<S2Builder::Layer>>& layers_;
718
+ const vector<int8>& input_dimensions_;
719
+ const InputEdgeCrossings& input_crossings_;
720
+ };
721
+
722
+ GraphOptions EdgeClippingLayer::graph_options() const {
723
+ // We keep all edges, including degenerate ones, so that we can figure out
724
+ // the correspondence between input edge crossings and output edge
725
+ // crossings.
726
+ return GraphOptions(EdgeType::DIRECTED, DegenerateEdges::KEEP,
727
+ DuplicateEdges::KEEP, SiblingPairs::KEEP);
728
+ }
729
+
730
+ // Helper function (in anonymous namespace) to create an S2Builder::Graph from
731
+ // a vector of edges.
732
+ Graph MakeGraph(
733
+ const Graph& g, GraphOptions* options, vector<Graph::Edge>* new_edges,
734
+ vector<InputEdgeIdSetId>* new_input_edge_ids,
735
+ IdSetLexicon* new_input_edge_id_set_lexicon, S2Error* error) {
736
+ if (options->edge_type() == EdgeType::UNDIRECTED) {
737
+ // Create a reversed edge for every edge.
738
+ int n = new_edges->size();
739
+ new_edges->reserve(2 * n);
740
+ new_input_edge_ids->reserve(2 * n);
741
+ for (int i = 0; i < n; ++i) {
742
+ new_edges->push_back(Graph::reverse((*new_edges)[i]));
743
+ new_input_edge_ids->push_back(IdSetLexicon::EmptySetId());
744
+ }
745
+ }
746
+ Graph::ProcessEdges(options, new_edges, new_input_edge_ids,
747
+ new_input_edge_id_set_lexicon, error);
748
+ return Graph(*options, &g.vertices(), new_edges, new_input_edge_ids,
749
+ new_input_edge_id_set_lexicon, &g.label_set_ids(),
750
+ &g.label_set_lexicon(), g.is_full_polygon_predicate());
751
+ }
752
+
753
+ void EdgeClippingLayer::Build(const Graph& g, S2Error* error) {
754
+ // The bulk of the work is handled by GraphEdgeClipper.
755
+ vector<Graph::Edge> new_edges;
756
+ vector<InputEdgeIdSetId> new_input_edge_ids;
757
+ // Destroy the GraphEdgeClipper immediately to save memory.
758
+ GraphEdgeClipper(g, input_dimensions_, input_crossings_,
759
+ &new_edges, &new_input_edge_ids).Run();
760
+ if (s2builder_verbose) {
761
+ std::cout << "Edges after clipping: " << std::endl;
762
+ for (int i = 0; i < new_edges.size(); ++i) {
763
+ std::cout << " " << new_input_edge_ids[i] << " (" << new_edges[i].first
764
+ << ", " << new_edges[i].second << ")" << std::endl;
765
+ }
766
+ }
767
+ // Construct one or more graphs from the clipped edges and pass them to the
768
+ // given output layer(s).
769
+ IdSetLexicon new_input_edge_id_set_lexicon;
770
+ if (layers_.size() == 1) {
771
+ GraphOptions options = layers_[0]->graph_options();
772
+ Graph new_graph = MakeGraph(g, &options, &new_edges, &new_input_edge_ids,
773
+ &new_input_edge_id_set_lexicon, error);
774
+ layers_[0]->Build(new_graph, error);
775
+ } else {
776
+ // The Graph objects must be valid until the last Build() call completes,
777
+ // so we store all of the graph data in arrays with 3 elements.
778
+ S2_DCHECK_EQ(3, layers_.size());
779
+ vector<Graph::Edge> layer_edges[3];
780
+ vector<InputEdgeIdSetId> layer_input_edge_ids[3];
781
+ S2Builder::GraphOptions layer_options[3];
782
+ vector<S2Builder::Graph> layer_graphs; // No default constructor.
783
+ layer_graphs.reserve(3);
784
+ // Separate the edges according to their dimension.
785
+ for (int i = 0; i < new_edges.size(); ++i) {
786
+ int d = input_dimensions_[new_input_edge_ids[i]];
787
+ layer_edges[d].push_back(new_edges[i]);
788
+ layer_input_edge_ids[d].push_back(new_input_edge_ids[i]);
789
+ }
790
+ // Clear variables to save space.
791
+ vector<Graph::Edge>().swap(new_edges);
792
+ vector<InputEdgeIdSetId>().swap(new_input_edge_ids);
793
+ for (int d = 0; d < 3; ++d) {
794
+ layer_options[d] = layers_[d]->graph_options();
795
+ layer_graphs.push_back(MakeGraph(
796
+ g, &layer_options[d], &layer_edges[d], &layer_input_edge_ids[d],
797
+ &new_input_edge_id_set_lexicon, error));
798
+ layers_[d]->Build(layer_graphs[d], error);
799
+ }
800
+ }
801
+ }
802
+
803
+ } // namespace
804
+
805
+ class S2BooleanOperation::Impl {
806
+ public:
807
+ explicit Impl(S2BooleanOperation* op)
808
+ : op_(op), index_crossings_first_region_id_(-1) {
809
+ }
810
+ bool Build(S2Error* error);
811
+
812
+ private:
813
+ class CrossingIterator;
814
+ class CrossingProcessor;
815
+ using ShapeEdge = s2shapeutil::ShapeEdge;
816
+ using ShapeEdgeId = s2shapeutil::ShapeEdgeId;
817
+
818
+ // An IndexCrossing represents a pair of intersecting S2ShapeIndex edges
819
+ // ("a_edge" and "b_edge"). We store all such intersections because the
820
+ // algorithm needs them twice, once when processing the boundary of region A
821
+ // and once when processing the boundary of region B.
822
+ struct IndexCrossing {
823
+ ShapeEdgeId a, b;
824
+
825
+ // True if S2::CrossingSign(a_edge, b_edge) > 0.
826
+ uint32 is_interior_crossing : 1;
827
+
828
+ // True if "a_edge" crosses "b_edge" from left to right. Undefined if
829
+ // is_interior_crossing is false.
830
+ uint32 left_to_right: 1;
831
+
832
+ // Equal to S2::VertexCrossing(a_edge, b_edge). Undefined if "a_edge" and
833
+ // "b_edge" do not share exactly one vertex or either edge is degenerate.
834
+ uint32 is_vertex_crossing : 1;
835
+
836
+ // All flags are "false" by default.
837
+ IndexCrossing(ShapeEdgeId _a, ShapeEdgeId _b)
838
+ : a(_a), b(_b), is_interior_crossing(false), left_to_right(false),
839
+ is_vertex_crossing(false) {
840
+ }
841
+
842
+ friend bool operator==(const IndexCrossing& x, const IndexCrossing& y) {
843
+ return x.a == y.a && x.b == y.b;
844
+ }
845
+ friend bool operator<(const IndexCrossing& x, const IndexCrossing& y) {
846
+ // The compiler (2017) doesn't optimize the following as well:
847
+ // return x.a < y.a || (x.a == y.a && x.b < y.b);
848
+ if (x.a.shape_id < y.a.shape_id) return true;
849
+ if (y.a.shape_id < x.a.shape_id) return false;
850
+ if (x.a.edge_id < y.a.edge_id) return true;
851
+ if (y.a.edge_id < x.a.edge_id) return false;
852
+ if (x.b.shape_id < y.b.shape_id) return true;
853
+ if (y.b.shape_id < x.b.shape_id) return false;
854
+ return x.b.edge_id < y.b.edge_id;
855
+ }
856
+ };
857
+ using IndexCrossings = vector<IndexCrossing>;
858
+
859
+ bool is_boolean_output() const { return op_->result_empty_ != nullptr; }
860
+
861
+ // All of the methods below support "early exit" in the case of boolean
862
+ // results by returning "false" as soon as the result is known to be
863
+ // non-empty.
864
+ bool AddBoundary(int a_region_id, bool invert_a, bool invert_b,
865
+ bool invert_result,
866
+ const vector<ShapeEdgeId>& a_chain_starts,
867
+ CrossingProcessor* cp);
868
+ bool GetChainStarts(int a_region_id, bool invert_a, bool invert_b,
869
+ bool invert_result, CrossingProcessor* cp,
870
+ vector<ShapeEdgeId>* chain_starts);
871
+ bool ProcessIncidentEdges(const ShapeEdge& a,
872
+ S2ContainsPointQuery<S2ShapeIndex>* query,
873
+ CrossingProcessor* cp);
874
+ static bool HasInterior(const S2ShapeIndex& index);
875
+ static bool AddIndexCrossing(const ShapeEdge& a, const ShapeEdge& b,
876
+ bool is_interior, IndexCrossings* crossings);
877
+ bool GetIndexCrossings(int region_id);
878
+ bool AddBoundaryPair(bool invert_a, bool invert_b, bool invert_result,
879
+ CrossingProcessor* cp);
880
+ bool AreRegionsIdentical() const;
881
+ bool BuildOpType(OpType op_type);
882
+ bool IsFullPolygonResult(const S2Builder::Graph& g, S2Error* error) const;
883
+ bool IsFullPolygonUnion(const S2ShapeIndex& a,
884
+ const S2ShapeIndex& b) const;
885
+ bool IsFullPolygonIntersection(const S2ShapeIndex& a,
886
+ const S2ShapeIndex& b) const;
887
+ bool IsFullPolygonDifference(const S2ShapeIndex& a,
888
+ const S2ShapeIndex& b) const;
889
+ bool IsFullPolygonSymmetricDifference(const S2ShapeIndex& a,
890
+ const S2ShapeIndex& b) const;
891
+
892
+ // A bit mask representing all six faces of the S2 cube.
893
+ static constexpr uint8 kAllFacesMask = 0x3f;
894
+
895
+ S2BooleanOperation* op_;
896
+
897
+ // The S2Builder used to construct the output.
898
+ unique_ptr<S2Builder> builder_;
899
+
900
+ // A vector specifying the dimension of each edge added to S2Builder.
901
+ vector<int8> input_dimensions_;
902
+
903
+ // The set of all input edge crossings, which is used by EdgeClippingLayer
904
+ // to construct the clipped output polygon.
905
+ InputEdgeCrossings input_crossings_;
906
+
907
+ // kSentinel is a sentinel value used to mark the end of vectors.
908
+ static const ShapeEdgeId kSentinel;
909
+
910
+ // A vector containing all pairs of crossing edges from the two input
911
+ // regions (including edge pairs that share a common vertex). The first
912
+ // element of each pair is an edge from "index_crossings_first_region_id_",
913
+ // while the second element of each pair is an edge from the other region.
914
+ IndexCrossings index_crossings_;
915
+
916
+ // Indicates that the first element of each crossing edge pair in
917
+ // "index_crossings_" corresponds to an edge from the given region.
918
+ // This field is negative if index_crossings_ has not been computed yet.
919
+ int index_crossings_first_region_id_;
920
+
921
+ // Temporary storage used in GetChainStarts(), declared here to avoid
922
+ // repeatedly allocating memory.
923
+ IndexCrossings tmp_crossings_;
924
+ };
925
+
926
+ const s2shapeutil::ShapeEdgeId S2BooleanOperation::Impl::kSentinel(
927
+ std::numeric_limits<int32>::max(), 0);
928
+
929
+ // A helper class for iterating through the edges from region B that cross a
930
+ // particular edge from region A. It caches information from the current
931
+ // shape, chain, and edge so that it doesn't need to be looked up repeatedly.
932
+ // Typical usage:
933
+ //
934
+ // void SomeFunction(ShapeEdgeId a_id, CrossingIterator *it) {
935
+ // // Iterate through the edges that cross edge "a_id".
936
+ // for (; !it->Done(a_id); it->Next()) {
937
+ // ... use it->b_shape(), it->b_edge(), etc ...
938
+ // }
939
+ class S2BooleanOperation::Impl::CrossingIterator {
940
+ public:
941
+ // Creates an iterator over crossing edge pairs (a, b) where "b" is an edge
942
+ // from "b_index". "crossings_complete" indicates that "crossings" contains
943
+ // all edge crossings between the two regions (rather than a subset).
944
+ CrossingIterator(const S2ShapeIndex* b_index,
945
+ const IndexCrossings* crossings, bool crossings_complete)
946
+ : b_index_(*b_index), it_(crossings->begin()), b_shape_id_(-1),
947
+ crossings_complete_(crossings_complete) {
948
+ Update();
949
+ }
950
+ void Next() {
951
+ ++it_;
952
+ Update();
953
+ }
954
+ bool Done(ShapeEdgeId id) const { return a_id() != id; }
955
+
956
+ // True if all edge crossings are available (see above).
957
+ bool crossings_complete() const { return crossings_complete_; }
958
+
959
+ // True if this crossing occurs at a point interior to both edges.
960
+ bool is_interior_crossing() const { return it_->is_interior_crossing; }
961
+
962
+ // Equal to S2::VertexCrossing(a_edge, b_edge), provided that a_edge and
963
+ // b_edge have exactly one vertex in common and neither edge is degenerate.
964
+ bool is_vertex_crossing() const { return it_->is_vertex_crossing; }
965
+
966
+ // True if a_edge crosses b_edge from left to right (for interior crossings).
967
+ bool left_to_right() const { return it_->left_to_right; }
968
+
969
+ ShapeEdgeId a_id() const { return it_->a; }
970
+ ShapeEdgeId b_id() const { return it_->b; }
971
+ const S2ShapeIndex& b_index() const { return b_index_; }
972
+ const S2Shape& b_shape() const { return *b_shape_; }
973
+ int b_dimension() const { return b_dimension_; }
974
+ int b_shape_id() const { return b_shape_id_; }
975
+ int b_edge_id() const { return b_id().edge_id; }
976
+
977
+ S2Shape::Edge b_edge() const {
978
+ return b_shape_->edge(b_edge_id()); // Opportunity to cache this.
979
+ }
980
+
981
+ // Information about the chain to which an edge belongs.
982
+ struct ChainInfo {
983
+ int chain_id; // chain id
984
+ int start; // starting edge id
985
+ int limit; // limit edge id
986
+ };
987
+ // Returns a description of the chain to which the current B edge belongs.
988
+ const ChainInfo& b_chain_info() const {
989
+ if (b_info_.chain_id < 0) {
990
+ b_info_.chain_id = b_shape().chain_position(b_edge_id()).chain_id;
991
+ auto chain = b_shape().chain(b_info_.chain_id);
992
+ b_info_.start = chain.start;
993
+ b_info_.limit = chain.start + chain.length;
994
+ }
995
+ return b_info_;
996
+ }
997
+
998
+ private:
999
+ // Updates information about the B shape whenever it changes.
1000
+ void Update() {
1001
+ if (a_id() != kSentinel && b_id().shape_id != b_shape_id_) {
1002
+ b_shape_id_ = b_id().shape_id;
1003
+ b_shape_ = b_index_.shape(b_shape_id_);
1004
+ b_dimension_ = b_shape_->dimension();
1005
+ b_info_.chain_id = -1; // Computed on demand.
1006
+ }
1007
+ }
1008
+
1009
+ const S2ShapeIndex& b_index_;
1010
+ IndexCrossings::const_iterator it_;
1011
+ const S2Shape* b_shape_;
1012
+ int b_shape_id_;
1013
+ int b_dimension_;
1014
+ mutable ChainInfo b_info_; // Computed on demand.
1015
+ bool crossings_complete_;
1016
+ };
1017
+
1018
+ // CrossingProcessor is a helper class that processes all the edges from one
1019
+ // region that cross a specific edge of the other region. It outputs the
1020
+ // appropriate edges to an S2Builder, and outputs other information required
1021
+ // by GraphEdgeClipper to the given vectors.
1022
+ class S2BooleanOperation::Impl::CrossingProcessor {
1023
+ public:
1024
+ // Prepares to build output for the given polygon and polyline boundary
1025
+ // models. Edges are emitted to "builder", while other auxiliary data is
1026
+ // appended to the given vectors.
1027
+ //
1028
+ // If a predicate is being evaluated (i.e., we do not need to construct the
1029
+ // actual result), then "builder" and the various output vectors should all
1030
+ // be nullptr.
1031
+ CrossingProcessor(const PolygonModel& polygon_model,
1032
+ const PolylineModel& polyline_model,
1033
+ bool polyline_loops_have_boundaries,
1034
+ S2Builder* builder,
1035
+ vector<int8>* input_dimensions,
1036
+ InputEdgeCrossings *input_crossings)
1037
+ : polygon_model_(polygon_model), polyline_model_(polyline_model),
1038
+ polyline_loops_have_boundaries_(polyline_loops_have_boundaries),
1039
+ builder_(builder), input_dimensions_(input_dimensions),
1040
+ input_crossings_(input_crossings), prev_inside_(false) {
1041
+ }
1042
+
1043
+ // Starts processing edges from the given region. "invert_a", "invert_b",
1044
+ // and "invert_result" indicate whether region A, region B, and/or the
1045
+ // result should be inverted, which allows operations such as union and
1046
+ // difference to be implemented. For example, union is ~(~A & ~B).
1047
+ //
1048
+ // This method should be called in pairs, once to process the edges from
1049
+ // region A and once to process the edges from region B.
1050
+ void StartBoundary(int a_region_id, bool invert_a, bool invert_b,
1051
+ bool invert_result);
1052
+
1053
+ // Starts processing edges from the given shape.
1054
+ void StartShape(const S2Shape* a_shape);
1055
+
1056
+ // Starts processing edges from the given chain.
1057
+ void StartChain(int chain_id, S2Shape::Chain chain, bool inside);
1058
+
1059
+ // Processes the given edge "a_id". "it" should be positioned to the set of
1060
+ // edges from the other region that cross "a_id" (if any).
1061
+ //
1062
+ // Supports "early exit" in the case of boolean results by returning false
1063
+ // as soon as the result is known to be non-empty.
1064
+ bool ProcessEdge(ShapeEdgeId a_id, CrossingIterator* it);
1065
+
1066
+ // This method should be called after each pair of calls to StartBoundary.
1067
+ // (The only operation that processes more than one pair of boundaries is
1068
+ // SYMMETRIC_DIFFERENCE, which computes the union of A-B and B-A.)
1069
+ //
1070
+ // Resets the state of the CrossingProcessor.
1071
+ void DoneBoundaryPair();
1072
+
1073
+ // Indicates whether the point being processed along the current edge chain
1074
+ // is in the polygonal interior of the opposite region, using semi-open
1075
+ // boundaries. If "invert_b_" is true then this field is inverted.
1076
+ //
1077
+ // This value along with the set of incident edges can be used to compute
1078
+ // whether the opposite region contains this point under any of the
1079
+ // supported boundary models (PolylineModel::CLOSED, etc).
1080
+ bool inside() const { return inside_; }
1081
+
1082
+ private:
1083
+ // SourceEdgeCrossing represents an input edge that crosses some other
1084
+ // edge; it crosses the edge from left to right iff the second parameter
1085
+ // is "true".
1086
+ using SourceEdgeCrossing = pair<SourceId, bool>;
1087
+ struct PointCrossingResult;
1088
+ struct EdgeCrossingResult;
1089
+
1090
+ InputEdgeId input_edge_id() const { return input_dimensions_->size(); }
1091
+
1092
+ // Returns true if the edges on either side of the first vertex of the
1093
+ // current edge have not been emitted.
1094
+ //
1095
+ // REQUIRES: This method is called just after updating "inside_" for "v0".
1096
+ bool is_v0_isolated(ShapeEdgeId a_id) const {
1097
+ return !inside_ && v0_emitted_max_edge_id_ < a_id.edge_id;
1098
+ }
1099
+
1100
+ // Returns true if "a_id" is the last edge of the current chain, and the
1101
+ // edges on either side of the last vertex have not been emitted (including
1102
+ // the possibility that the chain forms a loop).
1103
+ bool is_chain_last_vertex_isolated(ShapeEdgeId a_id) const {
1104
+ return (a_id.edge_id == chain_limit_ - 1 && !chain_v0_emitted_ &&
1105
+ v0_emitted_max_edge_id_ <= a_id.edge_id);
1106
+ }
1107
+
1108
+ // Returns true if the given polyline edge contains "v0", taking into
1109
+ // account the specified PolylineModel.
1110
+ bool polyline_contains_v0(int edge_id, int chain_start) const {
1111
+ return (polyline_model_ != PolylineModel::OPEN || edge_id > chain_start);
1112
+ }
1113
+
1114
+ void AddCrossing(const SourceEdgeCrossing& crossing) {
1115
+ source_edge_crossings_.push_back(make_pair(input_edge_id(), crossing));
1116
+ }
1117
+
1118
+ void SetClippingState(InputEdgeId parameter, bool state) {
1119
+ AddCrossing(SourceEdgeCrossing(SourceId(parameter), state));
1120
+ }
1121
+
1122
+ // Supports "early exit" in the case of boolean results by returning false
1123
+ // as soon as the result is known to be non-empty.
1124
+ bool AddEdge(ShapeEdgeId a_id, const S2Shape::Edge& a,
1125
+ int dimension, int interior_crossings) {
1126
+ if (builder_ == nullptr) return false; // Boolean output.
1127
+ if (interior_crossings > 0) {
1128
+ // Build a map that translates temporary edge ids (SourceId) to
1129
+ // the representation used by EdgeClippingLayer (InputEdgeId).
1130
+ SourceId src_id(a_region_id_, a_id.shape_id, a_id.edge_id);
1131
+ source_id_map_[src_id] = input_edge_id();
1132
+ }
1133
+ // Set the GraphEdgeClipper's "inside" state to match ours.
1134
+ if (inside_ != prev_inside_) SetClippingState(kSetInside, inside_);
1135
+ input_dimensions_->push_back(dimension);
1136
+ builder_->AddEdge(a.v0, a.v1);
1137
+ inside_ ^= (interior_crossings & 1);
1138
+ prev_inside_ = inside_;
1139
+ return true;
1140
+ }
1141
+
1142
+ // Supports "early exit" in the case of boolean results by returning false
1143
+ // as soon as the result is known to be non-empty.
1144
+ bool AddPointEdge(const S2Point& p, int dimension) {
1145
+ if (builder_ == nullptr) return false; // Boolean output.
1146
+ if (!prev_inside_) SetClippingState(kSetInside, true);
1147
+ input_dimensions_->push_back(dimension);
1148
+ builder_->AddEdge(p, p);
1149
+ prev_inside_ = true;
1150
+ return true;
1151
+ }
1152
+
1153
+ bool ProcessEdge0(ShapeEdgeId a_id, const S2Shape::Edge& a,
1154
+ CrossingIterator* it);
1155
+ bool ProcessEdge1(ShapeEdgeId a_id, const S2Shape::Edge& a,
1156
+ CrossingIterator* it);
1157
+ bool ProcessEdge2(ShapeEdgeId a_id, const S2Shape::Edge& a,
1158
+ CrossingIterator* it);
1159
+
1160
+ void SkipCrossings(ShapeEdgeId a_id, CrossingIterator* it);
1161
+ PointCrossingResult ProcessPointCrossings(
1162
+ ShapeEdgeId a_id, const S2Point& a0, CrossingIterator* it) const;
1163
+ EdgeCrossingResult ProcessEdgeCrossings(
1164
+ ShapeEdgeId a_id, const S2Shape::Edge& a, CrossingIterator* it);
1165
+
1166
+ bool IsPolylineVertexInside(bool matches_polyline,
1167
+ bool matches_polygon) const;
1168
+ bool IsPolylineEdgeInside(const EdgeCrossingResult& r) const;
1169
+ bool PolylineEdgeContainsVertex(const S2Point& v,
1170
+ const CrossingIterator& it) const;
1171
+
1172
+ // Constructor parameters:
1173
+
1174
+ PolygonModel polygon_model_;
1175
+ PolylineModel polyline_model_;
1176
+ bool polyline_loops_have_boundaries_;
1177
+
1178
+ // The output of the CrossingProcessor consists of a subset of the input
1179
+ // edges that are emitted to "builder_", and some auxiliary information
1180
+ // that allows GraphEdgeClipper to determine which segments of those input
1181
+ // edges belong to the output. The auxiliary information consists of the
1182
+ // dimension of each input edge, and set of input edges from the other
1183
+ // region that cross each input input edge.
1184
+ S2Builder* builder_;
1185
+ vector<int8>* input_dimensions_;
1186
+ InputEdgeCrossings* input_crossings_;
1187
+
1188
+ // Fields set by StartBoundary:
1189
+
1190
+ int a_region_id_, b_region_id_;
1191
+ bool invert_a_, invert_b_, invert_result_;
1192
+ bool is_union_; // True if this is a UNION operation.
1193
+
1194
+ // Fields set by StartShape:
1195
+
1196
+ const S2Shape* a_shape_;
1197
+ int a_dimension_;
1198
+
1199
+ // Fields set by StartChain:
1200
+
1201
+ int chain_id_;
1202
+ int chain_start_;
1203
+ int chain_limit_;
1204
+
1205
+ // Fields updated by ProcessEdge:
1206
+
1207
+ // A temporary representation of input_crossings_ that is used internally
1208
+ // until all necessary edges from *both* polygons have been emitted to the
1209
+ // S2Builder. This field is then converted by DoneBoundaryPair() into
1210
+ // the InputEdgeCrossings format expected by GraphEdgeClipper.
1211
+ //
1212
+ // The reason that we can't construct input_crossings_ directly is that it
1213
+ // uses InputEdgeIds to identify the edges from both polygons, and when we
1214
+ // are processing edges from the first polygon, InputEdgeIds have not yet
1215
+ // been assigned to the second polygon. So instead this field identifies
1216
+ // edges from the first polygon using an InputEdgeId, and edges from the
1217
+ // second polygon using a (region_id, shape_id, edge_id) tuple (i.e., a
1218
+ // SourceId).
1219
+ //
1220
+ // All crossings are represented twice, once to indicate that an edge from
1221
+ // polygon 0 is crossed by an edge from polygon 1, and once to indicate that
1222
+ // an edge from polygon 1 is crossed by an edge from polygon 0.
1223
+ using SourceEdgeCrossings = vector<pair<InputEdgeId, SourceEdgeCrossing>>;
1224
+ SourceEdgeCrossings source_edge_crossings_;
1225
+
1226
+ // A map that translates from SourceId (the (region_id, shape_id,
1227
+ // edge_id) triple that identifies an S2ShapeIndex edge) to InputEdgeId (the
1228
+ // sequentially increasing numbers assigned to input edges by S2Builder).
1229
+ using SourceIdMap = gtl::btree_map<SourceId, InputEdgeId>;
1230
+ SourceIdMap source_id_map_;
1231
+
1232
+ // Indicates whether the point being processed along the current edge chain
1233
+ // is in the polygonal interior of the opposite region, using semi-open
1234
+ // boundaries. If "invert_b_" is true then this field is inverted.
1235
+ //
1236
+ // Equal to: b_index_.Contains(current point) ^ invert_b_
1237
+ bool inside_;
1238
+
1239
+ // The value of that "inside_" would have just before the end of the
1240
+ // previous edge added to S2Builder. This value is used to determine
1241
+ // whether the GraphEdgeClipper state needs to be updated when jumping from
1242
+ // one edge chain to another.
1243
+ bool prev_inside_;
1244
+
1245
+ // The maximum edge id of any edge in the current chain whose v0 vertex has
1246
+ // already been emitted. This is used to determine when an isolated vertex
1247
+ // needs to be emitted, e.g. when two closed polygons share only a vertex.
1248
+ int v0_emitted_max_edge_id_;
1249
+
1250
+ // True if the first vertex of the current chain has been emitted. This is
1251
+ // used when processing loops in order to determine whether the first/last
1252
+ // vertex of the loop should be emitted as an isolated vertex.
1253
+ bool chain_v0_emitted_;
1254
+ };
1255
+
1256
+ // See documentation above.
1257
+ void S2BooleanOperation::Impl::CrossingProcessor::StartBoundary(
1258
+ int a_region_id, bool invert_a, bool invert_b, bool invert_result) {
1259
+ a_region_id_ = a_region_id;
1260
+ b_region_id_ = 1 - a_region_id;
1261
+ invert_a_ = invert_a;
1262
+ invert_b_ = invert_b;
1263
+ invert_result_ = invert_result;
1264
+ is_union_ = invert_b && invert_result;
1265
+
1266
+ // Specify to GraphEdgeClipper how these edges should be clipped.
1267
+ SetClippingState(kSetReverseA, invert_a != invert_result);
1268
+ SetClippingState(kSetInvertB, invert_b);
1269
+ }
1270
+
1271
+ // See documentation above.
1272
+ inline void S2BooleanOperation::Impl::CrossingProcessor::StartShape(
1273
+ const S2Shape* a_shape) {
1274
+ a_shape_ = a_shape;
1275
+ a_dimension_ = a_shape->dimension();
1276
+ }
1277
+
1278
+ // See documentation above.
1279
+ inline void S2BooleanOperation::Impl::CrossingProcessor::StartChain(
1280
+ int chain_id, S2Shape::Chain chain, bool inside) {
1281
+ chain_id_ = chain_id;
1282
+ chain_start_ = chain.start;
1283
+ chain_limit_ = chain.start + chain.length;
1284
+ inside_ = inside;
1285
+ v0_emitted_max_edge_id_ = chain.start - 1; // No edges emitted yet.
1286
+ chain_v0_emitted_ = false;
1287
+ }
1288
+
1289
+ // See documentation above.
1290
+ bool S2BooleanOperation::Impl::CrossingProcessor::ProcessEdge(
1291
+ ShapeEdgeId a_id, CrossingIterator* it) {
1292
+ // chain_edge() is faster than edge() when there are multiple chains.
1293
+ auto a = a_shape_->chain_edge(chain_id_, a_id.edge_id - chain_start_);
1294
+ if (a_dimension_ == 0) {
1295
+ return ProcessEdge0(a_id, a, it);
1296
+ } else if (a_dimension_ == 1) {
1297
+ return ProcessEdge1(a_id, a, it);
1298
+ } else {
1299
+ S2_DCHECK_EQ(2, a_dimension_);
1300
+ return ProcessEdge2(a_id, a, it);
1301
+ }
1302
+ }
1303
+
1304
+ // PointCrossingResult describes the relationship between a point from region A
1305
+ // and a set of crossing edges from region B. For example, "matches_polygon"
1306
+ // indicates whether a polygon vertex from region B matches the given point.
1307
+ struct S2BooleanOperation::Impl::CrossingProcessor::PointCrossingResult {
1308
+ PointCrossingResult()
1309
+ : matches_point(false), matches_polyline(false), matches_polygon(false) {
1310
+ }
1311
+ // Note that "matches_polyline" is true only if the point matches a polyline
1312
+ // vertex of B *and* the polyline contains that vertex, whereas
1313
+ // "matches_polygon" is true if the point matches any polygon vertex.
1314
+ bool matches_point; // Matches point.
1315
+ bool matches_polyline; // Matches contained polyline vertex.
1316
+ bool matches_polygon; // Matches polygon vertex.
1317
+ };
1318
+
1319
+ // Processes an edge of dimension 0 (i.e., a point) from region A.
1320
+ //
1321
+ // Supports "early exit" in the case of boolean results by returning false
1322
+ // as soon as the result is known to be non-empty.
1323
+ bool S2BooleanOperation::Impl::CrossingProcessor::ProcessEdge0(
1324
+ ShapeEdgeId a_id, const S2Shape::Edge& a, CrossingIterator* it) {
1325
+ S2_DCHECK_EQ(a.v0, a.v1);
1326
+ // When a region is inverted, all points and polylines are discarded.
1327
+ if (invert_a_ != invert_result_) {
1328
+ SkipCrossings(a_id, it);
1329
+ return true;
1330
+ }
1331
+ PointCrossingResult r = ProcessPointCrossings(a_id, a.v0, it);
1332
+
1333
+ // "contained" indicates whether the current point is inside the polygonal
1334
+ // interior of the opposite region, using semi-open boundaries.
1335
+ bool contained = inside_ ^ invert_b_;
1336
+ if (r.matches_polygon && polygon_model_ != PolygonModel::SEMI_OPEN) {
1337
+ contained = (polygon_model_ == PolygonModel::CLOSED);
1338
+ }
1339
+ if (r.matches_polyline) contained = true;
1340
+
1341
+ // The output of UNION includes duplicate values, so ensure that points are
1342
+ // not suppressed by other points.
1343
+ if (r.matches_point && !is_union_) contained = true;
1344
+
1345
+ // Test whether the point is contained after region B is inverted.
1346
+ if (contained == invert_b_) return true; // Don't exit early.
1347
+ return AddPointEdge(a.v0, 0);
1348
+ }
1349
+
1350
+ // Skip any crossings that were not needed to determine the result.
1351
+ inline void S2BooleanOperation::Impl::CrossingProcessor::SkipCrossings(
1352
+ ShapeEdgeId a_id, CrossingIterator* it) {
1353
+ while (!it->Done(a_id)) it->Next();
1354
+ }
1355
+
1356
+ // Returns a summary of the relationship between a point from region A and
1357
+ // a set of crossing edges from region B (see PointCrossingResult).
1358
+ S2BooleanOperation::Impl::CrossingProcessor::PointCrossingResult
1359
+ S2BooleanOperation::Impl::CrossingProcessor::ProcessPointCrossings(
1360
+ ShapeEdgeId a_id, const S2Point& a0, CrossingIterator* it) const {
1361
+ PointCrossingResult r;
1362
+ for (; !it->Done(a_id); it->Next()) {
1363
+ if (it->b_dimension() == 0) {
1364
+ r.matches_point = true;
1365
+ } else if (it->b_dimension() == 1) {
1366
+ if (PolylineEdgeContainsVertex(a0, *it)) {
1367
+ r.matches_polyline = true;
1368
+ }
1369
+ } else {
1370
+ r.matches_polygon = true;
1371
+ }
1372
+ }
1373
+ return r;
1374
+ }
1375
+
1376
+ // EdgeCrossingResult describes the relationship between an edge from region A
1377
+ // ("a_edge") and a set of crossing edges from region B. For example,
1378
+ // "matches_polygon" indicates whether "a_edge" matches a polygon edge from
1379
+ // region B.
1380
+ struct S2BooleanOperation::Impl::CrossingProcessor::EdgeCrossingResult {
1381
+ EdgeCrossingResult()
1382
+ : matches_polyline(false), matches_polygon(false), matches_sibling(false),
1383
+ a0_matches_polyline(false), a1_matches_polyline(false),
1384
+ a0_matches_polygon(false), a1_matches_polygon(false),
1385
+ a0_crossings(0), a1_crossings(0), interior_crossings(0) {
1386
+ }
1387
+ // These fields indicate that "a_edge" exactly matches an edge of B.
1388
+ bool matches_polyline; // Matches polyline edge (either direction).
1389
+ bool matches_polygon; // Matches polygon edge (same direction).
1390
+ bool matches_sibling; // Matches polygon edge (reverse direction).
1391
+
1392
+ // These fields indicate that a vertex of "a_edge" matches a polyline vertex
1393
+ // of B *and* the polyline contains that vertex.
1394
+ bool a0_matches_polyline; // Start vertex matches contained polyline vertex.
1395
+ bool a1_matches_polyline; // End vertex matches contained polyline vertex.
1396
+
1397
+ // These fields indicate that a vertex of "a_edge" matches a polygon vertex
1398
+ // of B. (Unlike with polylines, the polygon may not contain that vertex.)
1399
+ bool a0_matches_polygon; // Start vertex matches polygon vertex.
1400
+ bool a1_matches_polygon; // End vertex matches polygon vertex.
1401
+
1402
+ // These fields count the number of edge crossings at the start vertex, end
1403
+ // vertex, and interior of "a_edge".
1404
+ int a0_crossings; // Count of polygon crossings at start vertex.
1405
+ int a1_crossings; // Count of polygon crossings at end vertex.
1406
+ int interior_crossings; // Count of polygon crossings in edge interior.
1407
+ };
1408
+
1409
+ // Processes an edge of dimension 1 (i.e., a polyline edge) from region A.
1410
+ //
1411
+ // Supports "early exit" in the case of boolean results by returning false
1412
+ // as soon as the result is known to be non-empty.
1413
+ bool S2BooleanOperation::Impl::CrossingProcessor::ProcessEdge1(
1414
+ ShapeEdgeId a_id, const S2Shape::Edge& a, CrossingIterator* it) {
1415
+ // When a region is inverted, all points and polylines are discarded.
1416
+ if (invert_a_ != invert_result_) {
1417
+ SkipCrossings(a_id, it);
1418
+ return true;
1419
+ }
1420
+ // Evaluate whether the start vertex should belong to the output, in case it
1421
+ // needs to be emitted as an isolated vertex.
1422
+ EdgeCrossingResult r = ProcessEdgeCrossings(a_id, a, it);
1423
+ bool a0_inside = IsPolylineVertexInside(r.a0_matches_polyline,
1424
+ r.a0_matches_polygon);
1425
+
1426
+ // Test whether the entire polyline edge should be emitted (or not emitted)
1427
+ // because it matches a polyline or polygon edge.
1428
+ inside_ ^= (r.a0_crossings & 1);
1429
+ if (inside_ != IsPolylineEdgeInside(r)) {
1430
+ inside_ ^= true; // Invert the inside_ state.
1431
+ ++r.a1_crossings; // Restore the correct (semi-open) state later.
1432
+ }
1433
+
1434
+ // If neither edge adjacent to v0 was emitted, and this polyline contains
1435
+ // v0, and the other region contains v0, then emit an isolated vertex.
1436
+ if (!polyline_loops_have_boundaries_ && a_id.edge_id == chain_start_ &&
1437
+ a.v0 == a_shape_->chain_edge(chain_id_,
1438
+ chain_limit_ - chain_start_ - 1).v1) {
1439
+ // This is the first vertex of a polyline loop, so we can't decide if it
1440
+ // is isolated until we process the last polyline edge.
1441
+ chain_v0_emitted_ = inside_;
1442
+ } else if (is_v0_isolated(a_id) &&
1443
+ polyline_contains_v0(a_id.edge_id, chain_start_) && a0_inside) {
1444
+ if (!AddPointEdge(a.v0, 1)) return false;
1445
+ }
1446
+
1447
+ // Test whether the entire edge or any part of it belongs to the output.
1448
+ if (inside_ || r.interior_crossings > 0) {
1449
+ // Note: updates "inside_" to correspond to the state just before a1.
1450
+ if (!AddEdge(a_id, a, 1 /*dimension*/, r.interior_crossings)) {
1451
+ return false;
1452
+ }
1453
+ }
1454
+ // Remember whether the edge portion just before "a1" was emitted, so that
1455
+ // we can decide whether "a1" need to be emitted as an isolated vertex.
1456
+ if (inside_) v0_emitted_max_edge_id_ = a_id.edge_id + 1;
1457
+
1458
+ // Verify that edge crossings are being counted correctly.
1459
+ inside_ ^= (r.a1_crossings & 1);
1460
+ if (it->crossings_complete()) {
1461
+ S2_DCHECK_EQ(MakeS2ContainsPointQuery(&it->b_index()).Contains(a.v1),
1462
+ inside_ ^ invert_b_);
1463
+ }
1464
+
1465
+ // Special case to test whether the last vertex of a polyline should be
1466
+ // emitted as an isolated vertex.
1467
+ if (it->crossings_complete() && is_chain_last_vertex_isolated(a_id) &&
1468
+ (polyline_model_ == PolylineModel::CLOSED ||
1469
+ (!polyline_loops_have_boundaries_ &&
1470
+ a.v1 == a_shape_->chain_edge(chain_id_, chain_start_).v0)) &&
1471
+ IsPolylineVertexInside(r.a1_matches_polyline, r.a1_matches_polygon)) {
1472
+ if (!AddPointEdge(a.v1, 1)) return false;
1473
+ }
1474
+ return true;
1475
+ }
1476
+
1477
+ // Returns true if the current point being processed (which must be a polyline
1478
+ // vertex) is contained by the opposite region (after inversion if "invert_b_"
1479
+ // is true). "matches_polyline" and "matches_polygon" indicate whether the
1480
+ // vertex matches a polyline/polygon vertex of the opposite region.
1481
+ bool S2BooleanOperation::Impl::CrossingProcessor::IsPolylineVertexInside(
1482
+ bool matches_polyline, bool matches_polygon) const {
1483
+ // "contained" indicates whether the current point is inside the polygonal
1484
+ // interior of the opposite region using semi-open boundaries.
1485
+ bool contained = inside_ ^ invert_b_;
1486
+
1487
+ // For UNION the output includes duplicate polylines. The test below
1488
+ // ensures that isolated polyline vertices are not suppressed by other
1489
+ // polyline vertices in the output.
1490
+ if (matches_polyline && !is_union_) {
1491
+ contained = true;
1492
+ } else if (matches_polygon && polygon_model_ != PolygonModel::SEMI_OPEN) {
1493
+ contained = (polygon_model_ == PolygonModel::CLOSED);
1494
+ }
1495
+ // Finally, invert the result if the opposite region should be inverted.
1496
+ return contained ^ invert_b_;
1497
+ }
1498
+
1499
+ // Returns true if the current polyline edge is contained by the opposite
1500
+ // region (after inversion if "invert_b_" is true).
1501
+ inline bool S2BooleanOperation::Impl::CrossingProcessor::IsPolylineEdgeInside(
1502
+ const EdgeCrossingResult& r) const {
1503
+ // "contained" indicates whether the current point is inside the polygonal
1504
+ // interior of the opposite region using semi-open boundaries.
1505
+ bool contained = inside_ ^ invert_b_;
1506
+ if (r.matches_polyline && !is_union_) {
1507
+ contained = true;
1508
+ } else if (r.matches_polygon) {
1509
+ // In the SEMI_OPEN model, polygon sibling pairs cancel each other and
1510
+ // have no effect on point or edge containment.
1511
+ if (!(r.matches_sibling && polygon_model_ == PolygonModel::SEMI_OPEN)) {
1512
+ contained = (polygon_model_ != PolygonModel::OPEN);
1513
+ }
1514
+ } else if (r.matches_sibling) {
1515
+ contained = (polygon_model_ == PolygonModel::CLOSED);
1516
+ }
1517
+ // Finally, invert the result if the opposite region should be inverted.
1518
+ return contained ^ invert_b_;
1519
+ }
1520
+
1521
+ // Processes an edge of dimension 2 (i.e., a polygon edge) from region A.
1522
+ //
1523
+ // Supports "early exit" in the case of boolean results by returning false
1524
+ // as soon as the result is known to be non-empty.
1525
+ bool S2BooleanOperation::Impl::CrossingProcessor::ProcessEdge2(
1526
+ ShapeEdgeId a_id, const S2Shape::Edge& a, CrossingIterator* it) {
1527
+ // In order to keep only one copy of any shared polygon edges, we only
1528
+ // output shared edges when processing the second region.
1529
+ bool emit_shared = (a_region_id_ == 1);
1530
+
1531
+ // Degeneracies such as isolated vertices and sibling pairs can only be
1532
+ // created by intersecting CLOSED polygons or unioning OPEN polygons.
1533
+ bool emit_degenerate =
1534
+ (polygon_model_ == PolygonModel::CLOSED && !invert_a_ && !invert_b_) ||
1535
+ (polygon_model_ == PolygonModel::OPEN && invert_a_ && invert_b_);
1536
+
1537
+ EdgeCrossingResult r = ProcessEdgeCrossings(a_id, a, it);
1538
+ S2_DCHECK(!r.matches_polyline);
1539
+ inside_ ^= (r.a0_crossings & 1);
1540
+
1541
+ // If only one region is inverted, matching/sibling relations are reversed.
1542
+ // TODO(ericv): Update the following code to handle degenerate loops.
1543
+ S2_DCHECK(!r.matches_polygon || !r.matches_sibling);
1544
+ if (invert_a_ != invert_b_) swap(r.matches_polygon, r.matches_sibling);
1545
+
1546
+ // Test whether the entire polygon edge should be emitted (or not emitted)
1547
+ // because it matches a polygon edge or its sibling.
1548
+ bool new_inside = inside_;
1549
+
1550
+ // Shared edge are emitted only while processing the second region.
1551
+ if (r.matches_polygon) new_inside = emit_shared;
1552
+
1553
+ // Sibling pairs are emitted only when degeneracies are desired.
1554
+ if (r.matches_sibling) new_inside = emit_degenerate;
1555
+ if (inside_ != new_inside) {
1556
+ inside_ ^= true; // Invert the inside_ state.
1557
+ ++r.a1_crossings; // Restore the correct (semi-open) state later.
1558
+ }
1559
+
1560
+ // Test whether the first vertex of this edge should be emitted as an
1561
+ // isolated degenerate vertex.
1562
+ if (a_id.edge_id == chain_start_) {
1563
+ chain_v0_emitted_ = inside_;
1564
+ } else if (emit_shared && emit_degenerate && r.a0_matches_polygon &&
1565
+ is_v0_isolated(a_id)) {
1566
+ if (!AddPointEdge(a.v0, 2)) return false;
1567
+ }
1568
+
1569
+ // Test whether the entire edge or any part of it belongs to the output.
1570
+ if (inside_ || r.interior_crossings > 0) {
1571
+ // Note: updates "inside_" to correspond to the state just before a1.
1572
+ if (!AddEdge(a_id, a, 2 /*dimension*/, r.interior_crossings)) {
1573
+ return false;
1574
+ }
1575
+ }
1576
+
1577
+ // Remember whether the edge portion just before "a1" was emitted, so that
1578
+ // we can decide whether "a1" need to be emitted as an isolated vertex.
1579
+ if (inside_) v0_emitted_max_edge_id_ = a_id.edge_id + 1;
1580
+ inside_ ^= (r.a1_crossings & 1);
1581
+
1582
+ // Verify that edge crossings are being counted correctly.
1583
+ if (it->crossings_complete()) {
1584
+ S2_DCHECK_EQ(MakeS2ContainsPointQuery(&it->b_index()).Contains(a.v1),
1585
+ inside_ ^ invert_b_);
1586
+ }
1587
+
1588
+ // Special case to test whether the last vertex of a loop should be emitted
1589
+ // as an isolated degenerate vertex.
1590
+ if (emit_shared && emit_degenerate && r.a1_matches_polygon &&
1591
+ it->crossings_complete() && is_chain_last_vertex_isolated(a_id)) {
1592
+ if (!AddPointEdge(a.v1, 2)) return false;
1593
+ }
1594
+ return true;
1595
+ }
1596
+
1597
+ // Returns a summary of the relationship between a test edge from region A and
1598
+ // a set of crossing edges from region B (see EdgeCrossingResult).
1599
+ //
1600
+ // NOTE(ericv): We could save a bit of work when matching polygon vertices by
1601
+ // passing in a flag saying whether this information is needed. For example
1602
+ // if is only needed in ProcessEdge2 when (emit_shared && emit_degenerate).
1603
+ S2BooleanOperation::Impl::CrossingProcessor::EdgeCrossingResult
1604
+ S2BooleanOperation::Impl::CrossingProcessor::ProcessEdgeCrossings(
1605
+ ShapeEdgeId a_id, const S2Shape::Edge& a, CrossingIterator* it) {
1606
+ EdgeCrossingResult r;
1607
+ if (it->Done(a_id)) return r;
1608
+
1609
+ // TODO(ericv): bool a_degenerate = (a.v0 == a.v1);
1610
+ for (; !it->Done(a_id); it->Next()) {
1611
+ // Polylines and polygons are not affected by point geometry.
1612
+ if (it->b_dimension() == 0) continue;
1613
+ S2Shape::Edge b = it->b_edge();
1614
+ if (it->is_interior_crossing()) {
1615
+ // The crossing occurs in the edge interior. The condition below says
1616
+ // that (1) polyline crossings don't affect polygon output, and (2)
1617
+ // subtracting a crossing polyline from a polyline has no effect.
1618
+ if (a_dimension_ <= it->b_dimension() &&
1619
+ !(invert_b_ != invert_result_ && it->b_dimension() == 1)) {
1620
+ SourceId src_id(b_region_id_, it->b_shape_id(), it->b_edge_id());
1621
+ AddCrossing(make_pair(src_id, it->left_to_right()));
1622
+ }
1623
+ r.interior_crossings += (it->b_dimension() == 1) ? 2 : 1;
1624
+ } else if (it->b_dimension() == 1) {
1625
+ // Polygons are not affected by polyline geometry.
1626
+ if (a_dimension_ == 2) continue;
1627
+ if ((a.v0 == b.v0 && a.v1 == b.v1) || (a.v0 == b.v1 && a.v1 == b.v0)) {
1628
+ r.matches_polyline = true;
1629
+ }
1630
+ if ((a.v0 == b.v0 || a.v0 == b.v1) &&
1631
+ PolylineEdgeContainsVertex(a.v0, *it)) {
1632
+ r.a0_matches_polyline = true;
1633
+ }
1634
+ if ((a.v1 == b.v0 || a.v1 == b.v1) &&
1635
+ PolylineEdgeContainsVertex(a.v1, *it)) {
1636
+ r.a1_matches_polyline = true;
1637
+ }
1638
+ } else {
1639
+ S2_DCHECK_EQ(2, it->b_dimension());
1640
+ if (a.v0 == b.v0 && a.v1 == b.v1) {
1641
+ ++r.a0_crossings;
1642
+ r.matches_polygon = true;
1643
+ } else if (a.v0 == b.v1 && a.v1 == b.v0) {
1644
+ ++r.a0_crossings;
1645
+ r.matches_sibling = true;
1646
+ } else if (it->is_vertex_crossing()) {
1647
+ if (a.v0 == b.v0 || a.v0 == b.v1) {
1648
+ ++r.a0_crossings;
1649
+ } else {
1650
+ ++r.a1_crossings;
1651
+ }
1652
+ }
1653
+ if (a.v0 == b.v0 || a.v0 == b.v1) {
1654
+ r.a0_matches_polygon = true;
1655
+ }
1656
+ if (a.v1 == b.v0 || a.v1 == b.v1) {
1657
+ r.a1_matches_polygon = true;
1658
+ }
1659
+ }
1660
+ }
1661
+ return r;
1662
+ }
1663
+
1664
+ // Returns true if the vertex "v" is contained by the polyline edge referred
1665
+ // to by the CrossingIterator "it", taking into account the PolylineModel.
1666
+ //
1667
+ // REQUIRES: it.b_dimension() == 1
1668
+ // REQUIRES: "v" is an endpoint of it.b_edge()
1669
+ bool S2BooleanOperation::Impl::CrossingProcessor::PolylineEdgeContainsVertex(
1670
+ const S2Point& v, const CrossingIterator& it) const {
1671
+ S2_DCHECK_EQ(1, it.b_dimension());
1672
+ S2_DCHECK(it.b_edge().v0 == v || it.b_edge().v1 == v);
1673
+
1674
+ // Closed polylines contain all their vertices.
1675
+ if (polyline_model_ == PolylineModel::CLOSED) return true;
1676
+
1677
+ // Note that the code below is structured so that it.b_edge() is not usually
1678
+ // needed (since accessing the edge can be relatively expensive).
1679
+ const auto& b_chain = it.b_chain_info();
1680
+ int b_edge_id = it.b_edge_id();
1681
+
1682
+ // The last polyline vertex is never contained. (For polyline loops, it is
1683
+ // sufficient to treat the first vertex as begin contained.) This case also
1684
+ // handles degenerate polylines (polylines with one edge where v0 == v1),
1685
+ // which do not contain any points.
1686
+ if (b_edge_id == b_chain.limit - 1 && v == it.b_edge().v1) return false;
1687
+
1688
+ // Otherwise all interior vertices are contained. The first polyline
1689
+ // vertex is contained if either the polyline model is not OPEN, or the
1690
+ // polyline forms a loop and polyline_loops_have_boundaries_ is false.
1691
+ if (polyline_contains_v0(b_edge_id, b_chain.start)) return true;
1692
+ if (v != it.b_edge().v0) return true;
1693
+ if (polyline_loops_have_boundaries_) return false;
1694
+ return v == it.b_shape().chain_edge(b_chain.chain_id,
1695
+ b_chain.limit - b_chain.start - 1).v1;
1696
+ }
1697
+
1698
+ // Translates the temporary representation of crossing edges (SourceId) into
1699
+ // the format expected by EdgeClippingLayer (InputEdgeId).
1700
+ void S2BooleanOperation::Impl::CrossingProcessor::DoneBoundaryPair() {
1701
+ // Add entries that translate the "special" crossings.
1702
+ source_id_map_[SourceId(kSetInside)] = kSetInside;
1703
+ source_id_map_[SourceId(kSetInvertB)] = kSetInvertB;
1704
+ source_id_map_[SourceId(kSetReverseA)] = kSetReverseA;
1705
+ input_crossings_->reserve(input_crossings_->size() +
1706
+ source_edge_crossings_.size());
1707
+ for (const auto& tmp : source_edge_crossings_) {
1708
+ auto it = source_id_map_.find(tmp.second.first);
1709
+ S2_DCHECK(it != source_id_map_.end());
1710
+ input_crossings_->push_back(make_pair(
1711
+ tmp.first, CrossingInputEdge(it->second, tmp.second.second)));
1712
+ }
1713
+ source_edge_crossings_.clear();
1714
+ source_id_map_.clear();
1715
+ }
1716
+
1717
+ // Clips the boundary of A to the interior of the opposite region B and adds
1718
+ // the resulting edges to the output. Optionally, any combination of region
1719
+ // A, region B, and the result may be inverted, which allows operations such
1720
+ // as union and difference to be implemented.
1721
+ //
1722
+ // Note that when an input region is inverted with respect to the output
1723
+ // (e.g., invert_a != invert_result), all polygon edges are reversed and all
1724
+ // points and polylines are discarded, since the complement of such objects
1725
+ // cannot be represented. (If you want to compute the complement of points
1726
+ // or polylines, you can use S2LaxPolygonShape to represent your geometry as
1727
+ // degenerate polygons instead.)
1728
+ //
1729
+ // This method must be called an even number of times (first to clip A to B
1730
+ // and then to clip B to A), calling DoneBoundaryPair() after each pair.
1731
+ //
1732
+ // Supports "early exit" in the case of boolean results by returning false
1733
+ // as soon as the result is known to be non-empty.
1734
+ bool S2BooleanOperation::Impl::AddBoundary(
1735
+ int a_region_id, bool invert_a, bool invert_b, bool invert_result,
1736
+ const vector<ShapeEdgeId>& a_chain_starts, CrossingProcessor* cp) {
1737
+ const S2ShapeIndex& a_index = *op_->regions_[a_region_id];
1738
+ const S2ShapeIndex& b_index = *op_->regions_[1 - a_region_id];
1739
+ if (!GetIndexCrossings(a_region_id)) return false;
1740
+ cp->StartBoundary(a_region_id, invert_a, invert_b, invert_result);
1741
+
1742
+ // Walk the boundary of region A and build a list of all edge crossings.
1743
+ // We also keep track of whether the current vertex is inside region B.
1744
+ auto next_start = a_chain_starts.begin();
1745
+ CrossingIterator next_crossing(&b_index, &index_crossings_,
1746
+ true /*crossings_complete*/);
1747
+ ShapeEdgeId next_id = min(*next_start, next_crossing.a_id());
1748
+ while (next_id != kSentinel) {
1749
+ int a_shape_id = next_id.shape_id;
1750
+ const S2Shape& a_shape = *a_index.shape(a_shape_id);
1751
+ cp->StartShape(&a_shape);
1752
+ while (next_id.shape_id == a_shape_id) {
1753
+ // TODO(ericv): Special handling of dimension 0? Can omit most of this
1754
+ // code, including the loop, since all chains are of length 1.
1755
+ int edge_id = next_id.edge_id;
1756
+ S2Shape::ChainPosition chain_position = a_shape.chain_position(edge_id);
1757
+ int chain_id = chain_position.chain_id;
1758
+ S2Shape::Chain chain = a_shape.chain(chain_id);
1759
+ bool start_inside = (next_id == *next_start);
1760
+ if (start_inside) ++next_start;
1761
+ cp->StartChain(chain_id, chain, start_inside);
1762
+ int chain_limit = chain.start + chain.length;
1763
+ while (edge_id < chain_limit) {
1764
+ ShapeEdgeId a_id(a_shape_id, edge_id);
1765
+ S2_DCHECK(cp->inside() || next_crossing.a_id() == a_id);
1766
+ if (!cp->ProcessEdge(a_id, &next_crossing)) {
1767
+ return false;
1768
+ }
1769
+ if (cp->inside()) {
1770
+ ++edge_id;
1771
+ } else if (next_crossing.a_id().shape_id == a_shape_id &&
1772
+ next_crossing.a_id().edge_id < chain_limit) {
1773
+ edge_id = next_crossing.a_id().edge_id;
1774
+ } else {
1775
+ break;
1776
+ }
1777
+ }
1778
+ next_id = min(*next_start, next_crossing.a_id());
1779
+ }
1780
+ }
1781
+ return true;
1782
+ }
1783
+
1784
+ // Returns the first edge of each edge chain from "a_region_id" whose first
1785
+ // vertex is contained by opposite region's polygons (using the semi-open
1786
+ // boundary model). Each input region and the result region are inverted as
1787
+ // specified (invert_a, invert_b, and invert_result) before testing for
1788
+ // containment. The algorithm uses these "chain starts" in order to clip the
1789
+ // boundary of A to the interior of B in an output-senstive way.
1790
+ //
1791
+ // This method supports "early exit" in the case where a boolean predicate is
1792
+ // being evaluated and the algorithm discovers that the result region will be
1793
+ // non-empty.
1794
+ bool S2BooleanOperation::Impl::GetChainStarts(
1795
+ int a_region_id, bool invert_a, bool invert_b, bool invert_result,
1796
+ CrossingProcessor* cp, vector<ShapeEdgeId>* chain_starts) {
1797
+ const S2ShapeIndex& a_index = *op_->regions_[a_region_id];
1798
+ const S2ShapeIndex& b_index = *op_->regions_[1 - a_region_id];
1799
+
1800
+ if (is_boolean_output()) {
1801
+ // If boolean output is requested, then we use the CrossingProcessor to
1802
+ // determine whether the first edge of each chain will be emitted to the
1803
+ // output region. This lets us terminate the operation early in many
1804
+ // cases.
1805
+ cp->StartBoundary(a_region_id, invert_a, invert_b, invert_result);
1806
+ }
1807
+
1808
+ // If region B has no two-dimensional shapes and is not inverted, then by
1809
+ // definition no chain starts are contained. However if boolean output is
1810
+ // requested then we check for containment anyway, since as a side effect we
1811
+ // may discover that the result region is non-empty and terminate the entire
1812
+ // operation early.
1813
+ bool b_has_interior = HasInterior(b_index);
1814
+ if (b_has_interior || invert_b || is_boolean_output()) {
1815
+ auto query = MakeS2ContainsPointQuery(&b_index);
1816
+ int num_shape_ids = a_index.num_shape_ids();
1817
+ for (int shape_id = 0; shape_id < num_shape_ids; ++shape_id) {
1818
+ S2Shape* a_shape = a_index.shape(shape_id);
1819
+ if (a_shape == nullptr) continue;
1820
+
1821
+ // If region A is being subtracted from region B, points and polylines
1822
+ // in region A can be ignored since these shapes never contribute to the
1823
+ // output (they can only remove edges from region B).
1824
+ if (invert_a != invert_result && a_shape->dimension() < 2) continue;
1825
+
1826
+ if (is_boolean_output()) cp->StartShape(a_shape);
1827
+ int num_chains = a_shape->num_chains();
1828
+ for (int chain_id = 0; chain_id < num_chains; ++chain_id) {
1829
+ S2Shape::Chain chain = a_shape->chain(chain_id);
1830
+ if (chain.length == 0) continue;
1831
+ ShapeEdge a(shape_id, chain.start, a_shape->chain_edge(chain_id, 0));
1832
+ bool inside = (b_has_interior && query.Contains(a.v0())) != invert_b;
1833
+ if (inside) {
1834
+ chain_starts->push_back(ShapeEdgeId(shape_id, chain.start));
1835
+ }
1836
+ if (is_boolean_output()) {
1837
+ cp->StartChain(chain_id, chain, inside);
1838
+ if (!ProcessIncidentEdges(a, &query, cp)) return false;
1839
+ }
1840
+ }
1841
+ }
1842
+ }
1843
+ chain_starts->push_back(kSentinel);
1844
+ return true;
1845
+ }
1846
+
1847
+ bool S2BooleanOperation::Impl::ProcessIncidentEdges(
1848
+ const ShapeEdge& a, S2ContainsPointQuery<S2ShapeIndex>* query,
1849
+ CrossingProcessor* cp) {
1850
+ tmp_crossings_.clear();
1851
+ query->VisitIncidentEdges(a.v0(), [&a, this](const ShapeEdge& b) {
1852
+ return AddIndexCrossing(a, b, false /*is_interior*/, &tmp_crossings_);
1853
+ });
1854
+ // Fast path for the common case where there are no incident edges. We
1855
+ // return false (terminating early) if the first chain edge will be emitted.
1856
+ if (tmp_crossings_.empty()) {
1857
+ return !cp->inside();
1858
+ }
1859
+ // Otherwise we invoke the full CrossingProcessor logic to determine whether
1860
+ // the first chain edge will be emitted.
1861
+ if (tmp_crossings_.size() > 1) {
1862
+ std::sort(tmp_crossings_.begin(), tmp_crossings_.end());
1863
+ // VisitIncidentEdges() should not generate any duplicate values.
1864
+ S2_DCHECK(std::adjacent_find(tmp_crossings_.begin(), tmp_crossings_.end()) ==
1865
+ tmp_crossings_.end());
1866
+ }
1867
+ tmp_crossings_.push_back(IndexCrossing(kSentinel, kSentinel));
1868
+ CrossingIterator next_crossing(&query->index(), &tmp_crossings_,
1869
+ false /*crossings_complete*/);
1870
+ return cp->ProcessEdge(a.id(), &next_crossing);
1871
+ }
1872
+
1873
+ bool S2BooleanOperation::Impl::HasInterior(const S2ShapeIndex& index) {
1874
+ for (int s = index.num_shape_ids(); --s >= 0; ) {
1875
+ S2Shape* shape = index.shape(s);
1876
+ if (shape && shape->dimension() == 2) return true;
1877
+ }
1878
+ return false;
1879
+ }
1880
+
1881
+ inline bool S2BooleanOperation::Impl::AddIndexCrossing(
1882
+ const ShapeEdge& a, const ShapeEdge& b, bool is_interior,
1883
+ IndexCrossings* crossings) {
1884
+ crossings->push_back(IndexCrossing(a.id(), b.id()));
1885
+ IndexCrossing* crossing = &crossings->back();
1886
+ if (is_interior) {
1887
+ crossing->is_interior_crossing = true;
1888
+ if (s2pred::Sign(a.v0(), a.v1(), b.v0()) > 0) {
1889
+ crossing->left_to_right = true;
1890
+ }
1891
+ } else {
1892
+ // TODO(ericv): This field isn't used unless one shape is a polygon and
1893
+ // the other is a polyline or polygon, but we don't have the shape
1894
+ // dimension information readily available here.
1895
+ if (S2::VertexCrossing(a.v0(), a.v1(), b.v0(), b.v1())) {
1896
+ crossing->is_vertex_crossing = true;
1897
+ }
1898
+ }
1899
+ return true; // Continue visiting.
1900
+ }
1901
+
1902
+ // Initialize index_crossings_ to the set of crossing edge pairs such that the
1903
+ // first element of each pair is an edge from "region_id".
1904
+ //
1905
+ // Supports "early exit" in the case of boolean results by returning false
1906
+ // as soon as the result is known to be non-empty.
1907
+ bool S2BooleanOperation::Impl::GetIndexCrossings(int region_id) {
1908
+ if (region_id == index_crossings_first_region_id_) return true;
1909
+ if (index_crossings_first_region_id_ < 0) {
1910
+ S2_DCHECK_EQ(region_id, 0); // For efficiency, not correctness.
1911
+ if (!s2shapeutil::VisitCrossingEdgePairs(
1912
+ *op_->regions_[0], *op_->regions_[1],
1913
+ s2shapeutil::CrossingType::ALL,
1914
+ [this](const ShapeEdge& a, const ShapeEdge& b, bool is_interior) {
1915
+ // For all supported operations (union, intersection, and
1916
+ // difference), if the input edges have an interior crossing
1917
+ // then the output is guaranteed to have at least one edge.
1918
+ if (is_interior && is_boolean_output()) return false;
1919
+ return AddIndexCrossing(a, b, is_interior, &index_crossings_);
1920
+ })) {
1921
+ return false;
1922
+ }
1923
+ if (index_crossings_.size() > 1) {
1924
+ std::sort(index_crossings_.begin(), index_crossings_.end());
1925
+ index_crossings_.erase(
1926
+ std::unique(index_crossings_.begin(), index_crossings_.end()),
1927
+ index_crossings_.end());
1928
+ }
1929
+ // Add a sentinel value to simplify the loop logic.
1930
+ index_crossings_.push_back(IndexCrossing(kSentinel, kSentinel));
1931
+ index_crossings_first_region_id_ = 0;
1932
+ }
1933
+ if (region_id != index_crossings_first_region_id_) {
1934
+ for (auto& crossing : index_crossings_) {
1935
+ swap(crossing.a, crossing.b);
1936
+ // The following predicates get inverted when the edges are swapped.
1937
+ crossing.left_to_right ^= true;
1938
+ crossing.is_vertex_crossing ^= true;
1939
+ }
1940
+ std::sort(index_crossings_.begin(), index_crossings_.end());
1941
+ index_crossings_first_region_id_ = region_id;
1942
+ }
1943
+ return true;
1944
+ }
1945
+
1946
+ // Supports "early exit" in the case of boolean results by returning false
1947
+ // as soon as the result is known to be non-empty.
1948
+ bool S2BooleanOperation::Impl::AddBoundaryPair(
1949
+ bool invert_a, bool invert_b, bool invert_result, CrossingProcessor* cp) {
1950
+ // Optimization: if the operation is DIFFERENCE or SYMMETRIC_DIFFERENCE,
1951
+ // it is worthwhile checking whether the two regions are identical (in which
1952
+ // case the output is empty).
1953
+ //
1954
+ // TODO(ericv): When boolean output is requested there are other quick
1955
+ // checks that could be done here, such as checking whether a full cell from
1956
+ // one S2ShapeIndex intersects a non-empty cell of the other S2ShapeIndex.
1957
+ auto type = op_->op_type();
1958
+ if (type == OpType::DIFFERENCE || type == OpType::SYMMETRIC_DIFFERENCE) {
1959
+ if (AreRegionsIdentical()) return true;
1960
+ } else if (!is_boolean_output()) {
1961
+ }
1962
+ vector<ShapeEdgeId> a_starts, b_starts;
1963
+ if (!GetChainStarts(0, invert_a, invert_b, invert_result, cp, &a_starts) ||
1964
+ !GetChainStarts(1, invert_b, invert_a, invert_result, cp, &b_starts) ||
1965
+ !AddBoundary(0, invert_a, invert_b, invert_result, a_starts, cp) ||
1966
+ !AddBoundary(1, invert_b, invert_a, invert_result, b_starts, cp)) {
1967
+ return false;
1968
+ }
1969
+ if (!is_boolean_output()) cp->DoneBoundaryPair();
1970
+ return true;
1971
+ }
1972
+
1973
+ // Supports "early exit" in the case of boolean results by returning false
1974
+ // as soon as the result is known to be non-empty.
1975
+ bool S2BooleanOperation::Impl::BuildOpType(OpType op_type) {
1976
+ // CrossingProcessor does the real work of emitting the output edges.
1977
+ CrossingProcessor cp(op_->options_.polygon_model(),
1978
+ op_->options_.polyline_model(),
1979
+ op_->options_.polyline_loops_have_boundaries(),
1980
+ builder_.get(), &input_dimensions_, &input_crossings_);
1981
+ switch (op_type) {
1982
+ case OpType::UNION:
1983
+ // A | B == ~(~A & ~B)
1984
+ return AddBoundaryPair(true, true, true, &cp);
1985
+
1986
+ case OpType::INTERSECTION:
1987
+ // A & B
1988
+ return AddBoundaryPair(false, false, false, &cp);
1989
+
1990
+ case OpType::DIFFERENCE:
1991
+ // A - B = A & ~B
1992
+ return AddBoundaryPair(false, true, false, &cp);
1993
+
1994
+ case OpType::SYMMETRIC_DIFFERENCE:
1995
+ // Compute the union of (A - B) and (B - A).
1996
+ return (AddBoundaryPair(false, true, false, &cp) &&
1997
+ AddBoundaryPair(true, false, false, &cp));
1998
+ }
1999
+ S2_LOG(FATAL) << "Invalid S2BooleanOperation::OpType";
2000
+ return false;
2001
+ }
2002
+
2003
+ // Returns a bit mask indicating which of the 6 S2 cube faces intersect the
2004
+ // index contents.
2005
+ uint8 GetFaceMask(const S2ShapeIndex& index) {
2006
+ uint8 mask = 0;
2007
+ S2ShapeIndex::Iterator it(&index, S2ShapeIndex::BEGIN);
2008
+ while (!it.done()) {
2009
+ int face = it.id().face();
2010
+ mask |= 1 << face;
2011
+ it.Seek(S2CellId::FromFace(face + 1).range_min());
2012
+ }
2013
+ return mask;
2014
+ }
2015
+
2016
+ // Given a polygon edge graph containing only degenerate edges and sibling edge
2017
+ // pairs, the purpose of this function is to decide whether the polygon is empty
2018
+ // or full except for the degeneracies, i.e. whether the degeneracies represent
2019
+ // shells or holes.
2020
+ bool S2BooleanOperation::Impl::IsFullPolygonResult(
2021
+ const S2Builder::Graph& g, S2Error* error) const {
2022
+ // If there are no edges of dimension 2, the result could be either the
2023
+ // empty polygon or the full polygon. Note that this is harder to determine
2024
+ // than you might think due to snapping. For example, the union of two
2025
+ // non-empty polygons can be empty, because both polygons consist of tiny
2026
+ // loops that are eliminated by snapping. Similarly, even if two polygons
2027
+ // both contain a common point their intersection can still be empty.
2028
+ //
2029
+ // We distinguish empty from full results using two heuristics:
2030
+ //
2031
+ // 1. We compute a bit mask representing the subset of the six S2 cube faces
2032
+ // intersected by each input geometry, and use this to determine if only
2033
+ // one of the two results is possible. (This test is very fast.) Note
2034
+ // that snapping will never cause the result to cover an entire extra
2035
+ // cube face because the maximum allowed snap radius is too small.
2036
+ S2_DCHECK_LE(S2Builder::SnapFunction::kMaxSnapRadius().degrees(), 70);
2037
+ //
2038
+ // 2. We compute the area of each input geometry, and use this to bound the
2039
+ // minimum and maximum area of the result. If only one of {0, 4*Pi} is
2040
+ // possible then we are done. If neither is possible then we choose the
2041
+ // one that is closest to being possible (since snapping can change the
2042
+ // result area). Both results are possible only when computing the
2043
+ // symmetric difference of two regions of area 2*Pi each, in which case we
2044
+ // must resort to additional heuristics (see below).
2045
+ //
2046
+ // TODO(ericv): Implement a predicate that uses the results of edge snapping
2047
+ // directly, rather than computing areas. This would not only be much faster
2048
+ // but would also allows all cases to be handled 100% robustly.
2049
+ const S2ShapeIndex& a = *op_->regions_[0];
2050
+ const S2ShapeIndex& b = *op_->regions_[1];
2051
+ switch (op_->op_type()) {
2052
+ case OpType::UNION:
2053
+ return IsFullPolygonUnion(a, b);
2054
+
2055
+ case OpType::INTERSECTION:
2056
+ return IsFullPolygonIntersection(a, b);
2057
+
2058
+ case OpType::DIFFERENCE:
2059
+ return IsFullPolygonDifference(a, b);
2060
+
2061
+ case OpType::SYMMETRIC_DIFFERENCE:
2062
+ return IsFullPolygonSymmetricDifference(a, b);
2063
+
2064
+ default:
2065
+ S2_LOG(FATAL) << "Invalid S2BooleanOperation::OpType";
2066
+ return false;
2067
+ }
2068
+ }
2069
+
2070
+ bool S2BooleanOperation::Impl::IsFullPolygonUnion(
2071
+ const S2ShapeIndex& a, const S2ShapeIndex& b) const {
2072
+ // See comments in IsFullPolygonResult(). The most common case is that
2073
+ // neither input polygon is empty but the result is empty due to snapping.
2074
+
2075
+ // The result can be full only if the union of the two input geometries
2076
+ // intersects all six faces of the S2 cube. This test is fast.
2077
+ if ((GetFaceMask(a) | GetFaceMask(b)) != kAllFacesMask) return false;
2078
+
2079
+ // The union area satisfies:
2080
+ //
2081
+ // max(A, B) <= Union(A, B) <= min(4*Pi, A + B)
2082
+ //
2083
+ // where A, B can refer to a polygon or its area. We then choose the result
2084
+ // that assumes the smallest amount of error.
2085
+ double a_area = S2::GetArea(a), b_area = S2::GetArea(b);
2086
+ double min_area = max(a_area, b_area);
2087
+ double max_area = min(4 * M_PI, a_area + b_area);
2088
+ return min_area > 4 * M_PI - max_area;
2089
+ }
2090
+
2091
+ bool S2BooleanOperation::Impl::IsFullPolygonIntersection(
2092
+ const S2ShapeIndex& a, const S2ShapeIndex& b) const {
2093
+ // See comments in IsFullPolygonResult(). By far the most common case is
2094
+ // that the result is empty.
2095
+
2096
+ // The result can be full only if each of the two input geometries
2097
+ // intersects all six faces of the S2 cube. This test is fast.
2098
+ if ((GetFaceMask(a) & GetFaceMask(b)) != kAllFacesMask) return false;
2099
+
2100
+ // The intersection area satisfies:
2101
+ //
2102
+ // max(0, A + B - 4*Pi) <= Intersection(A, B) <= min(A, B)
2103
+ //
2104
+ // where A, B can refer to a polygon or its area. We then choose the result
2105
+ // that assumes the smallest amount of error.
2106
+ double a_area = S2::GetArea(a), b_area = S2::GetArea(b);
2107
+ double min_area = max(0.0, a_area + b_area - 4 * M_PI);
2108
+ double max_area = min(a_area, b_area);
2109
+ return min_area > 4 * M_PI - max_area;
2110
+ }
2111
+
2112
+ bool S2BooleanOperation::Impl::IsFullPolygonDifference(
2113
+ const S2ShapeIndex& a, const S2ShapeIndex& b) const {
2114
+ // See comments in IsFullPolygonResult(). By far the most common case is
2115
+ // that the result is empty.
2116
+
2117
+ // The result can be full only if each cube face is intersected by the first
2118
+ // geometry. (The second geometry is irrelevant, since for example it could
2119
+ // consist of a tiny loop on each S2 cube face.) This test is fast.
2120
+ if (GetFaceMask(a) != kAllFacesMask) return false;
2121
+
2122
+ // The difference area satisfies:
2123
+ //
2124
+ // max(0, A - B) <= Difference(A, B) <= min(A, 4*Pi - B)
2125
+ //
2126
+ // where A, B can refer to a polygon or its area. We then choose the result
2127
+ // that assumes the smallest amount of error.
2128
+ double a_area = S2::GetArea(a), b_area = S2::GetArea(b);
2129
+ double min_area = max(0.0, a_area - b_area);
2130
+ double max_area = min(a_area, 4 * M_PI - b_area);
2131
+ return min_area > 4 * M_PI - max_area;
2132
+ }
2133
+
2134
+ bool S2BooleanOperation::Impl::IsFullPolygonSymmetricDifference(
2135
+ const S2ShapeIndex& a, const S2ShapeIndex& b) const {
2136
+ // See comments in IsFullPolygonResult(). By far the most common case is
2137
+ // that the result is empty.
2138
+
2139
+ // The result can be full only if the union of the two input geometries
2140
+ // intersects all six faces of the S2 cube. This test is fast.
2141
+ uint8 a_mask = GetFaceMask(a);
2142
+ uint8 b_mask = GetFaceMask(b);
2143
+ if ((a_mask | b_mask) != kAllFacesMask) return false;
2144
+
2145
+ // The symmetric difference area satisfies:
2146
+ //
2147
+ // |A - B| <= SymmetricDifference(A, B) <= 4*Pi - |4*Pi - (A + B)|
2148
+ //
2149
+ // where A, B can refer to a polygon or its area.
2150
+ double a_area = S2::GetArea(a), b_area = S2::GetArea(b);
2151
+ double min_area = fabs(a_area - b_area);
2152
+ double max_area = 4 * M_PI - fabs(4 * M_PI - (a_area + b_area));
2153
+
2154
+ // Now we choose the result that assumes the smallest amount of error
2155
+ // (min_area in the empty case, and (4*Pi - max_area) in the full case).
2156
+ // However in the case of symmetric difference these two errors may be equal,
2157
+ // meaning that the result is ambiguous. This happens when both polygons have
2158
+ // area 2*Pi. Furthermore, this can happen even when the areas are not
2159
+ // exactly 2*Pi due to snapping and area calculation errors.
2160
+ //
2161
+ // To determine whether the result is ambiguous, we compute a rough estimate
2162
+ // of the maximum expected area error (including errors due to snapping),
2163
+ // using the worst-case error bound for a hemisphere defined by 4 vertices.
2164
+ auto edge_snap_radius = op_->options_.snap_function().snap_radius() +
2165
+ S2::kIntersectionError; // split_crossing_edges
2166
+ double hemisphere_area_error = 2 * M_PI * edge_snap_radius.radians() +
2167
+ 40 * DBL_EPSILON; // GetCurvatureMaxError
2168
+
2169
+ // The following sign is the difference between the error needed for an empty
2170
+ // result and the error needed for a full result. It is negative if an
2171
+ // empty result is possible, positive if a full result is possible, and zero
2172
+ // if both results are possible.
2173
+ double error_sign = min_area - (4 * M_PI - max_area);
2174
+ if (fabs(error_sign) <= hemisphere_area_error) {
2175
+ // Handling the ambiguous case correctly requires a more sophisticated
2176
+ // algorithm (see below), but we can at least handle the simple cases by
2177
+ // testing whether both input geometries intersect all 6 cube faces. If
2178
+ // not, then the result is definitely full.
2179
+ if ((a_mask & b_mask) != kAllFacesMask) return true;
2180
+
2181
+ // Otherwise both regions have area 2*Pi and intersect all 6 cube faces.
2182
+ // We choose "empty" in this case under the assumption that it is more
2183
+ // likely that the user is computing the difference between two nearly
2184
+ // identical polygons.
2185
+ //
2186
+ // TODO(ericv): Implement a robust algorithm based on examining the edge
2187
+ // snapping results directly, or alternatively add another heuristic (such
2188
+ // as testing containment of random points, or using a larger bit mask in
2189
+ // the tests above, e.g. a 24-bit mask representing all level 1 cells).
2190
+ return false;
2191
+ }
2192
+ return error_sign > 0;
2193
+ }
2194
+
2195
+ bool S2BooleanOperation::Impl::AreRegionsIdentical() const {
2196
+ const S2ShapeIndex* a = op_->regions_[0];
2197
+ const S2ShapeIndex* b = op_->regions_[1];
2198
+ if (a == b) return true;
2199
+ int num_shape_ids = a->num_shape_ids();
2200
+ if (num_shape_ids != b->num_shape_ids()) return false;
2201
+ for (int s = 0; s < num_shape_ids; ++s) {
2202
+ const S2Shape* a_shape = a->shape(s);
2203
+ const S2Shape* b_shape = b->shape(s);
2204
+ if (a_shape->dimension() != b_shape->dimension()) return false;
2205
+ if (a_shape->dimension() == 2) {
2206
+ auto a_ref = a_shape->GetReferencePoint();
2207
+ auto b_ref = b_shape->GetReferencePoint();
2208
+ if (a_ref.point != b_ref.point) return false;
2209
+ if (a_ref.contained != b_ref.contained) return false;
2210
+ }
2211
+ int num_chains = a_shape->num_chains();
2212
+ if (num_chains != b_shape->num_chains()) return false;
2213
+ for (int c = 0; c < num_chains; ++c) {
2214
+ S2Shape::Chain a_chain = a_shape->chain(c);
2215
+ S2Shape::Chain b_chain = b_shape->chain(c);
2216
+ S2_DCHECK_EQ(a_chain.start, b_chain.start);
2217
+ if (a_chain.length != b_chain.length) return false;
2218
+ for (int i = 0; i < a_chain.length; ++i) {
2219
+ S2Shape::Edge a_edge = a_shape->chain_edge(c, i);
2220
+ S2Shape::Edge b_edge = b_shape->chain_edge(c, i);
2221
+ if (a_edge.v0 != b_edge.v0) return false;
2222
+ if (a_edge.v1 != b_edge.v1) return false;
2223
+ }
2224
+ }
2225
+ }
2226
+ return true;
2227
+ }
2228
+
2229
+ bool S2BooleanOperation::Impl::Build(S2Error* error) {
2230
+ error->Clear();
2231
+ if (is_boolean_output()) {
2232
+ // BuildOpType() returns true if and only if the result has no edges.
2233
+ S2Builder::Graph g; // Unused by IsFullPolygonResult() implementation.
2234
+ *op_->result_empty_ =
2235
+ BuildOpType(op_->op_type()) && !IsFullPolygonResult(g, error);
2236
+ return true;
2237
+ }
2238
+ // TODO(ericv): Rather than having S2Builder split the edges, it would be
2239
+ // faster to call AddVertex() in this class and have a new S2Builder
2240
+ // option that increases the edge_snap_radius_ to account for errors in
2241
+ // the intersection point (the way that split_crossing_edges does).
2242
+ S2Builder::Options options(op_->options_.snap_function());
2243
+ options.set_split_crossing_edges(true);
2244
+
2245
+ // TODO(ericv): Ideally idempotent() should be true, but existing clients
2246
+ // expect vertices closer than the full "snap_radius" to be snapped.
2247
+ options.set_idempotent(false);
2248
+ builder_ = make_unique<S2Builder>(options);
2249
+ builder_->StartLayer(make_unique<EdgeClippingLayer>(
2250
+ &op_->layers_, &input_dimensions_, &input_crossings_));
2251
+
2252
+ // Add a predicate that decides whether a result with no polygon edges should
2253
+ // be interpreted as the empty polygon or the full polygon.
2254
+ builder_->AddIsFullPolygonPredicate(
2255
+ [this](const S2Builder::Graph& g, S2Error* error) {
2256
+ return IsFullPolygonResult(g, error);
2257
+ });
2258
+ (void) BuildOpType(op_->op_type());
2259
+ return builder_->Build(error);
2260
+ }
2261
+
2262
+ S2BooleanOperation::Options::Options()
2263
+ : snap_function_(make_unique<s2builderutil::IdentitySnapFunction>(
2264
+ S1Angle::Zero())) {
2265
+ }
2266
+
2267
+ S2BooleanOperation::Options::Options(const SnapFunction& snap_function)
2268
+ : snap_function_(snap_function.Clone()) {
2269
+ }
2270
+
2271
+ S2BooleanOperation::Options::Options(const Options& options)
2272
+ : snap_function_(options.snap_function_->Clone()),
2273
+ polygon_model_(options.polygon_model_),
2274
+ polyline_model_(options.polyline_model_),
2275
+ polyline_loops_have_boundaries_(options.polyline_loops_have_boundaries_),
2276
+ precision_(options.precision_),
2277
+ conservative_output_(options.conservative_output_),
2278
+ source_id_lexicon_(options.source_id_lexicon_) {
2279
+ }
2280
+
2281
+ S2BooleanOperation::Options& S2BooleanOperation::Options::operator=(
2282
+ const Options& options) {
2283
+ snap_function_ = options.snap_function_->Clone();
2284
+ polygon_model_ = options.polygon_model_;
2285
+ polyline_model_ = options.polyline_model_;
2286
+ polyline_loops_have_boundaries_ = options.polyline_loops_have_boundaries_;
2287
+ precision_ = options.precision_;
2288
+ conservative_output_ = options.conservative_output_;
2289
+ source_id_lexicon_ = options.source_id_lexicon_;
2290
+ return *this;
2291
+ }
2292
+
2293
+ const SnapFunction& S2BooleanOperation::Options::snap_function() const {
2294
+ return *snap_function_;
2295
+ }
2296
+
2297
+ void S2BooleanOperation::Options::set_snap_function(
2298
+ const SnapFunction& snap_function) {
2299
+ snap_function_ = snap_function.Clone();
2300
+ }
2301
+
2302
+ PolygonModel S2BooleanOperation::Options::polygon_model() const {
2303
+ return polygon_model_;
2304
+ }
2305
+
2306
+ void S2BooleanOperation::Options::set_polygon_model(PolygonModel model) {
2307
+ polygon_model_ = model;
2308
+ }
2309
+
2310
+ PolylineModel S2BooleanOperation::Options::polyline_model() const {
2311
+ return polyline_model_;
2312
+ }
2313
+
2314
+ void S2BooleanOperation::Options::set_polyline_model(PolylineModel model) {
2315
+ polyline_model_ = model;
2316
+ }
2317
+
2318
+ bool S2BooleanOperation::Options::polyline_loops_have_boundaries() const {
2319
+ return polyline_loops_have_boundaries_;
2320
+ }
2321
+
2322
+ void S2BooleanOperation::Options::set_polyline_loops_have_boundaries(
2323
+ bool value) {
2324
+ polyline_loops_have_boundaries_ = value;
2325
+ }
2326
+
2327
+ Precision S2BooleanOperation::Options::precision() const {
2328
+ return precision_;
2329
+ }
2330
+
2331
+ bool S2BooleanOperation::Options::conservative_output() const {
2332
+ return conservative_output_;
2333
+ }
2334
+
2335
+ ValueLexicon<S2BooleanOperation::SourceId>*
2336
+ S2BooleanOperation::Options::source_id_lexicon() const {
2337
+ return source_id_lexicon_;
2338
+ }
2339
+
2340
+ const char* S2BooleanOperation::OpTypeToString(OpType op_type) {
2341
+ switch (op_type) {
2342
+ case OpType::UNION: return "UNION";
2343
+ case OpType::INTERSECTION: return "INTERSECTION";
2344
+ case OpType::DIFFERENCE: return "DIFFERENCE";
2345
+ case OpType::SYMMETRIC_DIFFERENCE: return "SYMMETRIC DIFFERENCE";
2346
+ default: return "Unknown OpType";
2347
+ }
2348
+ }
2349
+
2350
+ S2BooleanOperation::S2BooleanOperation(OpType op_type,
2351
+ const Options& options)
2352
+ : op_type_(op_type), options_(options), result_empty_(nullptr) {
2353
+ }
2354
+
2355
+ S2BooleanOperation::S2BooleanOperation(OpType op_type, bool* result_empty,
2356
+ const Options& options)
2357
+ : op_type_(op_type), options_(options),
2358
+ result_empty_(result_empty) {
2359
+ }
2360
+
2361
+ S2BooleanOperation::S2BooleanOperation(
2362
+ OpType op_type, unique_ptr<S2Builder::Layer> layer, const Options& options)
2363
+ : op_type_(op_type), options_(options), result_empty_(nullptr) {
2364
+ layers_.push_back(std::move(layer));
2365
+ }
2366
+
2367
+ S2BooleanOperation::S2BooleanOperation(
2368
+ OpType op_type, vector<unique_ptr<S2Builder::Layer>> layers,
2369
+ const Options& options)
2370
+ : op_type_(op_type), options_(options), layers_(std::move(layers)),
2371
+ result_empty_(nullptr) {
2372
+ }
2373
+
2374
+ bool S2BooleanOperation::Build(const S2ShapeIndex& a,
2375
+ const S2ShapeIndex& b,
2376
+ S2Error* error) {
2377
+ regions_[0] = &a;
2378
+ regions_[1] = &b;
2379
+ return Impl(this).Build(error);
2380
+ }
2381
+
2382
+ bool S2BooleanOperation::IsEmpty(
2383
+ OpType op_type, const S2ShapeIndex& a, const S2ShapeIndex& b,
2384
+ const Options& options) {
2385
+ bool result_empty;
2386
+ S2BooleanOperation op(op_type, &result_empty, options);
2387
+ S2Error error;
2388
+ op.Build(a, b, &error);
2389
+ S2_DCHECK(error.ok());
2390
+ return result_empty;
2391
+ }