You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

intersection_util.cpp 10 kB

2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. #include "geo/private/util/range_util.h"
  2. #include "geo/private/util/intersection_util.h"
  3. namespace ns
  4. {
  5. namespace geo
  6. {
  7. /**
  8. * {first index of segment, second index of segment, intersect point}
  9. */
  10. typedef std::tuple<Eigen::Index, Eigen::Index, Eigen::Vector2d> LineSegmentPair;
  11. namespace
  12. {
  13. Eigen::Matrix2Xd resample_polyline(const Eigen::Matrix2Xd& pl, double dist)
  14. {
  15. (void)(dist);
  16. return pl;
  17. // Eigen::Matrix2Xd resample;
  18. // for (int idx = 1; idx < pl.cols(); idx++) {
  19. // Eigen::Vector2d p1 = pl.col(idx - 1);
  20. // Eigen::Vector2d p2 = pl.col(idx);
  21. // int last_cols = pl.cols();
  22. // int sub_cols = std::floor((p1 - p2).norm() / dist);
  23. // resample.conservativeResize(2, last_cols + sub_cols);
  24. // for (int jdx = 0; jdx < sub_cols; jdx++) {
  25. // resample.col(last_cols + jdx) = p1 + jdx * (p2 - p1) / sub_cols;
  26. // }
  27. // }
  28. // return resample;
  29. }
  30. /**
  31. * calculate distance from point to polyline
  32. * if point can't project on polyline, the cloest point will be first or last
  33. * node
  34. *
  35. * @return
  36. * 1) the neighbour node's index of closest point lies on polyline
  37. * 2) the ratio of the project point between polyline nodes
  38. * 3) closest point's distance
  39. */
  40. std::tuple<std::size_t, double, double> p2pl_distance(
  41. const Eigen::Matrix2Xd& polyline, const Eigen::Vector2d& point)
  42. {
  43. auto sqr = [](double x) { return x * x; };
  44. const auto count = polyline.cols();
  45. assert(count > 0);
  46. Eigen::Vector2d b = polyline.col(0);
  47. Eigen::Vector2d dbq = b - point;
  48. auto p_idx = -1;
  49. auto p_dt = 0.;
  50. double dist = dbq.squaredNorm();
  51. for (auto i = 1; i < count; ++i)
  52. {
  53. Eigen::Vector2d a = b;
  54. Eigen::Vector2d daq = dbq;
  55. b = polyline.col(i);
  56. dbq = b - point;
  57. const Eigen::Vector2d dab = a - b;
  58. const double inv_sqrlen = 1. / dab.squaredNorm();
  59. const double t = (dab(0) * daq(0) + dab(1) * daq(1)) * inv_sqrlen;
  60. if (t < 0.)
  61. {
  62. continue;
  63. }
  64. double current_dist;
  65. if (t <= 1.)
  66. {
  67. current_dist = sqr(dab(0) * dbq(1) - dab(1) * dbq(0)) * inv_sqrlen;
  68. }
  69. else
  70. {
  71. // t>1.
  72. current_dist = dbq.squaredNorm();
  73. }
  74. if (current_dist < dist)
  75. {
  76. dist = current_dist;
  77. p_idx = i;
  78. p_dt = t;
  79. }
  80. }
  81. return std::make_tuple(p_idx, p_dt, dist);
  82. }
  83. } // namespace
  84. bool l2l_intersect(const Eigen::Matrix2d& l1, const Eigen::Matrix2d& l2)
  85. {
  86. Eigen::Vector2d pt;
  87. return l2l_intersect(l1, l2, pt);
  88. }
  89. bool l2l_intersect(const Eigen::Matrix2d& l1, const Eigen::Matrix2d& l2,
  90. Eigen::Vector2d& intersect_point)
  91. {
  92. Eigen::Vector2d v1 = l1.col(1) - l1.col(0);
  93. Eigen::Vector2d v2 = l2.col(1) - l2.col(0);
  94. Eigen::Matrix2d m;
  95. m << v1, v2;
  96. double denominator = m.determinant();
  97. if (std::abs(denominator) < 1e-16)
  98. {
  99. // parallel
  100. return false;
  101. }
  102. m << v2, l1.col(0) - l2.col(0);
  103. double r = m.determinant() / denominator;
  104. if (r < 0 || r > 1)
  105. {
  106. return false;
  107. }
  108. m.col(0) = v1;
  109. double s = m.determinant() / denominator;
  110. if (s < 0 || s > 1)
  111. {
  112. return false;
  113. }
  114. intersect_point = l1.col(0) + r * v1;
  115. return true;
  116. }
  117. /**
  118. * get intersection points among i-th line segment
  119. * @param line_segments represented by points, eg. (0, 1) is the first line
  120. * segment, (2, 3) is the second, (2i, 2i+1) is the i-th segment
  121. * @return a list of LineSegmentPair
  122. */
  123. std::vector<LineSegmentPair> get_intersection(
  124. const Eigen::Matrix2Xd& line_segments)
  125. {
  126. assert(line_segments.cols() % 2 == 0);
  127. std::vector<LineSegmentPair> ret;
  128. int n = static_cast<int>(line_segments.cols() / 2);
  129. for (int i = 0; i < n - 1; ++i)
  130. {
  131. for (int j = i + 1; j < n; ++j)
  132. {
  133. Eigen::Vector2d intersect_pnt;
  134. const Eigen::Matrix2d& l1 =
  135. line_segments.block(0, static_cast<Eigen::Index>(2 * i), 2, 2);
  136. const Eigen::Matrix2d& l2 = line_segments.block(0, static_cast<Eigen::Index>(2 * j), 2, 2);
  137. if (l2l_intersect(l1, l2, intersect_pnt))
  138. {
  139. ret.emplace_back(i, j, intersect_pnt);
  140. }
  141. }
  142. }
  143. return ret;
  144. }
  145. /**
  146. * get self-intersection of a polyline
  147. * @param polyline polyline represented by matrix
  148. * @return a list of LineSegmentPair
  149. */
  150. std::vector<LineSegmentPair> get_self_intersection(
  151. const Eigen::Matrix2Xd& polyline)
  152. {
  153. Eigen::Index n = polyline.cols();
  154. Eigen::Index m = n - 1;
  155. Eigen::Matrix2Xd segments(2, 2 * m);
  156. for (Eigen::Index i = 0; i < n - 1; ++i)
  157. {
  158. Eigen::Index j = 2 * i;
  159. segments.middleCols(j, 2) = polyline.middleCols(i, 2);
  160. }
  161. std::vector<LineSegmentPair> ret;
  162. auto intersects = get_intersection(segments);
  163. for (const LineSegmentPair& item : intersects)
  164. {
  165. Eigen::Index i, j;
  166. std::tie(i, j, std::ignore) = item;
  167. if (j != i + 1 && j != i - 1)
  168. {
  169. ret.push_back(item);
  170. }
  171. }
  172. return ret;
  173. }
  174. void remove_zero_length_segments(Eigen::Matrix2Xd& polyline)
  175. {
  176. Eigen::Index n = polyline.cols();
  177. if (n < 2)
  178. {
  179. return;
  180. }
  181. Eigen::VectorXd length =
  182. (polyline.rightCols(n - 1) - polyline.leftCols(n - 1)).colwise().norm();
  183. std::vector<Eigen::Index> zero_nodes;
  184. for (Eigen::Index i = 0; i < n - 1; ++i)
  185. {
  186. if (length(i) < 1.e-6)
  187. {
  188. zero_nodes.push_back(i);
  189. }
  190. }
  191. Eigen::Matrix2Xd new_polyline;
  192. if (!zero_nodes.empty())
  193. {
  194. Eigen::Index m = zero_nodes.size();
  195. Eigen::Index j = zero_nodes.front();
  196. Eigen::Index cnt;
  197. for (Eigen::Index i = 0; i < m - 1; ++i)
  198. {
  199. cnt = zero_nodes[i + 1] - zero_nodes[i] - 1;
  200. polyline.middleCols(j, cnt) =
  201. polyline.middleCols(zero_nodes[i] + 1, cnt);
  202. j += cnt;
  203. }
  204. cnt = n - zero_nodes.back() - 1;
  205. polyline.middleCols(j, cnt) = polyline.rightCols(cnt);
  206. polyline.conservativeResize(2, n - m);
  207. }
  208. }
  209. void resolve_self_intersection(Eigen::Matrix2Xd& polyline)
  210. {
  211. remove_zero_length_segments(polyline);
  212. Eigen::Index n = polyline.cols();
  213. if (n <= 3)
  214. {
  215. return;
  216. }
  217. // the result is sorted by the index
  218. std::vector<LineSegmentPair> intersections =
  219. get_self_intersection(polyline);
  220. if (intersections.empty())
  221. {
  222. return;
  223. }
  224. Eigen::Matrix2Xd new_polyline(2, n);
  225. Eigen::Index read_pos = 0;
  226. Eigen::Index write_pos = 0;
  227. for (std::size_t i = 0; i < intersections.size(); ++i)
  228. {
  229. const LineSegmentPair& rcd = intersections[i];
  230. Eigen::Index idx = std::get<0>(rcd);
  231. /// if current intersection and next one has same first segment,
  232. /// continue
  233. if ((i + 1) != intersections.size() &&
  234. std::get<0>(intersections[i + 1]) == idx)
  235. {
  236. continue;
  237. }
  238. if (i > 0 && (read_pos - 1) == idx)
  239. {
  240. new_polyline.col(write_pos) = std::get<2>(rcd);
  241. ++write_pos;
  242. read_pos = std::get<1>(rcd) + 1;
  243. }
  244. else if (idx >= read_pos)
  245. {
  246. Eigen::Index cnt = idx - read_pos + 1;
  247. new_polyline.middleCols(write_pos, cnt) =
  248. polyline.middleCols(read_pos, cnt);
  249. write_pos += cnt;
  250. new_polyline.col(write_pos) = std::get<2>(rcd);
  251. ++write_pos;
  252. read_pos = std::get<1>(rcd) + 1;
  253. }
  254. }
  255. // copy the rest of the polyline
  256. Eigen::Index cnt = n - read_pos;
  257. new_polyline.middleCols(write_pos, cnt) = polyline.middleCols(read_pos, cnt);
  258. write_pos += cnt;
  259. new_polyline.conservativeResize(2, write_pos);
  260. polyline.swap(new_polyline);
  261. }
  262. bool is_path_intersect(const Eigen::Matrix2Xd& path_1,
  263. const Eigen::Matrix2Xd& path_2,
  264. Eigen::Vector2d* intersect_point)
  265. {
  266. Eigen::Index n = path_1.cols();
  267. Eigen::Index m = path_2.cols();
  268. Eigen::Matrix2Xd segments(2, 2 * (n - 1 + m - 1));
  269. for (Eigen::Index i = 0; i < n - 1; ++i)
  270. {
  271. segments.middleCols(2 * i, 2) = path_1.middleCols(i, 2);
  272. }
  273. for (Eigen::Index i = 0; i < m - 1; ++i)
  274. {
  275. segments.middleCols(2 * (n - 1 + i), 2) = path_2.middleCols(i, 2);
  276. }
  277. auto intersects = get_intersection(segments);
  278. for (auto item : intersects)
  279. {
  280. Eigen::Index i = std::get<0>(item);
  281. Eigen::Index j = std::get<1>(item);
  282. if (i < n - 1 && j >= n - 1)
  283. {
  284. if (intersect_point)
  285. {
  286. *intersect_point = std::get<2>(item);
  287. }
  288. return true;
  289. }
  290. }
  291. return false;
  292. }
  293. bool is_path_intersect_dis(const Eigen::Matrix2Xd& path1,
  294. const Eigen::Matrix2Xd& path2,
  295. Eigen::Vector2d* point1, Eigen::Vector2d* point2,
  296. double distance_threshold)
  297. {
  298. distance_threshold = std::max(distance_threshold, 0.5);
  299. Eigen::Matrix2Xd resample_path1 =
  300. resample_polyline(path1, distance_threshold);
  301. Eigen::Matrix2Xd resample_path2 =
  302. resample_polyline(path2, distance_threshold);
  303. // it is a n * m implementation currently
  304. auto sqr_threshold = distance_threshold * distance_threshold;
  305. const auto n = resample_path1.cols();
  306. for (auto i = 0; i < n; ++i)
  307. {
  308. double dist;
  309. int index;
  310. std::tie(index, std::ignore, dist) =
  311. p2pl_distance(resample_path2, resample_path1.col(i));
  312. if (dist < sqr_threshold && index >= 0)
  313. {
  314. *point1 = resample_path1.col(i);
  315. *point2 = resample_path2.col(index);
  316. return true;
  317. }
  318. }
  319. return false;
  320. }
  321. } // namespace geo
  322. } // namespace ns