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