extend.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /*!
  2. \file lib/vector/vedit/extend.c
  3. \brief Vedit library - extend lines (adopted from break.c)
  4. (C) 2017 by the GRASS Development Team
  5. This program is free software under the GNU General Public License
  6. (>=v2). Read the file COPYING that comes with GRASS for details.
  7. \author Huidae Cho <grass4u gmail.com>
  8. */
  9. #include <math.h>
  10. #include <grass/vedit.h>
  11. #define TOL 1e-9
  12. static int extend_lines(struct Map_info *, int, int, int, int, double,
  13. struct ilist *);
  14. static int find_extended_intersection(double, double, double, double, double,
  15. double, double *, double *);
  16. static int check_extended_direction(double, double, double, int, double,
  17. double);
  18. /*!
  19. \brief Extend lines in given threshold
  20. \code
  21. 1. Extend first line only
  22. \ \
  23. id1 \ -> \
  24. \
  25. id2 ---------- -----+----
  26. 2. Extend both lines
  27. \ \
  28. id1 \ -> \
  29. \
  30. id2 --- +----
  31. 3. Extend first line when both are on the same line
  32. id1 --- --- id2 -> -----+----
  33. 4. Connect two parallel lines (parallel=1)
  34. id1 ------ -------
  35. -> /
  36. id2 ------ +-----
  37. 5. Don't connect two parallel lines (parallel=0)
  38. id1 ------ ------
  39. ->
  40. id2 ------ ------
  41. \endcode
  42. \param Map pointer to Map_info
  43. \param List list of selected lines
  44. \param nodes 1 for start node, 2 for end node, other for both
  45. \param parallel connect parallel lines
  46. \param thresh threshold value
  47. \return number of modified lines
  48. */
  49. int Vedit_extend_lines(struct Map_info *Map, struct ilist *List, int nodes,
  50. int parallel, double thresh)
  51. {
  52. int nlines_modified;
  53. int i, j, first_node, n_nodes;
  54. struct ilist *List_exclude, *List_found;
  55. nlines_modified = 0;
  56. List_exclude = Vect_new_list();
  57. List_found = Vect_new_list();
  58. first_node = 0;
  59. n_nodes = 2;
  60. switch (nodes) {
  61. case 1:
  62. n_nodes = 1;
  63. break;
  64. case 2:
  65. first_node = 1;
  66. break;
  67. }
  68. /* collect lines to be modified */
  69. for (i = 0; i < List->n_values; i++) {
  70. int line, extended, node[2];
  71. line = List->value[i];
  72. if (!Vect_line_alive(Map, line))
  73. continue;
  74. if (Vect_get_line_type(Map, line) & GV_POINTS)
  75. continue;
  76. node[0] = node[1] = -1;
  77. Vect_get_line_nodes(Map, line, &(node[0]), &(node[1]));
  78. if (node[0] < 0 || node[1] < 0)
  79. continue;
  80. extended = 0;
  81. Vect_reset_list(List_exclude);
  82. Vect_list_append(List_exclude, line);
  83. for (j = first_node; j < n_nodes && !extended; j++) {
  84. double x, y, z;
  85. /* for each line node find lines in threshold */
  86. Vect_get_node_coor(Map, node[j], &x, &y, &z);
  87. do {
  88. int found;
  89. /* find first nearest line */
  90. found = Vect_find_line_list(Map, x, y, z,
  91. GV_LINES, thresh, WITHOUT_Z,
  92. List_exclude, List_found);
  93. if (found > 0 && Vect_line_alive(Map, found)) {
  94. /* try to extend lines (given node) */
  95. G_debug(3, "Vedit_extend_lines(): lines=%d,%d", line, found);
  96. if (extend_lines(Map, !j, line, found, parallel, thresh,
  97. List)) {
  98. G_debug(3, "Vedit_extend_lines(): lines=%d,%d -> extended",
  99. line, found);
  100. nlines_modified += 2;
  101. extended = 1;
  102. }
  103. }
  104. Vect_list_append(List_exclude, found);
  105. } while(List_found->n_values > 0 && !extended);
  106. }
  107. }
  108. Vect_destroy_list(List_exclude);
  109. Vect_destroy_list(List_found);
  110. return nlines_modified;
  111. }
  112. int extend_lines(struct Map_info *Map, int first, int line_from, int line_to,
  113. int parallel, double thresh, struct ilist *List)
  114. {
  115. /* TODO: If line_from extends to the i'th segment of line_to but the
  116. * line_from node is closest to the j'th segment of line_to, this function
  117. * wouldn't work because it only checks intersection of the start/end
  118. * segment of line_from and the closest segment of line_to (i'th segment).
  119. */
  120. int line_new;
  121. int type_from, type_to;
  122. struct line_pnts *Points_from, *Points_to, *Points_final;
  123. struct line_cats *Cats_from, *Cats_to;
  124. Points_from = Vect_new_line_struct();
  125. Points_to = Vect_new_line_struct();
  126. Points_final = Vect_new_line_struct();
  127. Cats_from = Vect_new_cats_struct();
  128. Cats_to = Vect_new_cats_struct();
  129. type_from = Vect_read_line(Map, Points_from, Cats_from, line_from);
  130. type_to = Vect_read_line(Map, Points_to, Cats_to, line_to);
  131. line_new = 0;
  132. if (!(type_from & GV_LINES) || !(type_to & GV_LINES))
  133. line_new = -1;
  134. /* avoid too much indentation */
  135. do {
  136. int n_points, seg, is, line_to_extended;
  137. double x, y, px, py, x1, y1;
  138. double dist, spdist, lpdist, length;
  139. double angle_t, angle_f;
  140. if (line_new == -1)
  141. break;
  142. n_points = Points_from->n_points - 1;
  143. if (first) {
  144. x = Points_from->x[0];
  145. y = Points_from->y[0];
  146. }
  147. else {
  148. x = Points_from->x[n_points];
  149. y = Points_from->y[n_points];
  150. }
  151. seg = Vect_line_distance(Points_to, x, y, 0.0, WITHOUT_Z,
  152. &px, &py, NULL, &dist, &spdist, &lpdist);
  153. if (!(seg > 0 && dist > 0.0 && (thresh < 0. || dist <= thresh)))
  154. break;
  155. /* lines in threshold */
  156. length = first ? 0 : Vect_line_length(Points_from);
  157. /* find angles */
  158. if (!Vect_point_on_line(Points_from, length, NULL, NULL, NULL, &angle_f,
  159. NULL) ||
  160. !Vect_point_on_line(Points_to, lpdist, NULL, NULL, NULL, &angle_t,
  161. NULL))
  162. break;
  163. line_to_extended = 0;
  164. /* extend both lines and find intersection */
  165. if (!find_extended_intersection(x, y, angle_f, px, py, angle_t,
  166. &x1, &y1)) {
  167. /* parallel lines */
  168. if (!parallel)
  169. break;
  170. x1 = px;
  171. y1 = py;
  172. if (first)
  173. Vect_line_insert_point(Points_from, 0, x1, y1, 0.0);
  174. else
  175. Vect_append_point(Points_from, x1, y1, 0.0);
  176. } else {
  177. /* skip if extended into the wrong direction */
  178. if (!check_extended_direction(x, y, angle_f, first, x1, y1))
  179. break;
  180. /* skip if extended too far from line_from */
  181. if (!Vect_line_distance(Points_from, x1, y1, 0.0, WITHOUT_Z, NULL,
  182. NULL, NULL, &dist, NULL, NULL) ||
  183. dist > thresh)
  184. break;
  185. Vect_line_distance(Points_to, x1, y1, 0.0, WITHOUT_Z, NULL, NULL,
  186. NULL, &dist, NULL, NULL);
  187. /* if intersection point is not on line_to */
  188. if (dist > TOL) {
  189. double x2, y2;
  190. /* skip if not extended from a line_to node */
  191. if (seg > 1 && seg < Points_to->n_points - 1)
  192. break;
  193. if (seg == 1) {
  194. line_to_extended = 1;
  195. x2 = Points_to->x[0];
  196. y2 = Points_to->y[0];
  197. } else {
  198. line_to_extended = 2;
  199. x2 = Points_to->x[Points_to->n_points - 1];
  200. y2 = Points_to->y[Points_to->n_points - 1];
  201. }
  202. /* skip if extended into the wrong direction */
  203. if (!check_extended_direction(x2, y2, angle_t, seg == 1, x1,
  204. y1))
  205. break;
  206. }
  207. /* otherwise, split line_to later */
  208. /* lines extended -> extend/split line_to */
  209. /* update line_from */
  210. if (first) {
  211. Points_from->x[0] = x1;
  212. Points_from->y[0] = y1;
  213. } else {
  214. Points_from->x[n_points] = x1;
  215. Points_from->y[n_points] = y1;
  216. }
  217. }
  218. line_new = Vect_rewrite_line(Map, line_from, type_from, Points_from,
  219. Cats_from);
  220. /* Vect_list_append(List, line_new); */
  221. Vect_reset_line(Points_final);
  222. if (line_to_extended == 1) {
  223. /* extend line_to start node */
  224. Vect_append_point(Points_final, x1, y1, 0.0);
  225. for (is = 0; is < Points_to->n_points; is++)
  226. Vect_append_point(Points_final, Points_to->x[is],
  227. Points_to->y[is], Points_to->z[is]);
  228. line_new = Vect_rewrite_line(Map, line_to, type_to, Points_final,
  229. Cats_to);
  230. } else if (line_to_extended == 2) {
  231. /* extend line_to end node */
  232. for (is = 0; is < Points_to->n_points; is++)
  233. Vect_append_point(Points_final, Points_to->x[is],
  234. Points_to->y[is], Points_to->z[is]);
  235. Vect_append_point(Points_final, x1, y1, 0.0);
  236. line_new = Vect_rewrite_line(Map, line_to, type_to, Points_final,
  237. Cats_to);
  238. } else {
  239. int n_parts = 0;
  240. /* break line_to */
  241. /* update line_to -- first part */
  242. for (is = 0; is < seg; is++)
  243. Vect_append_point(Points_final, Points_to->x[is],
  244. Points_to->y[is], Points_to->z[is]);
  245. Vect_append_point(Points_final, x1, y1, 0.0);
  246. if (Vect_line_length(Points_final) > 0) {
  247. n_parts++;
  248. line_new = Vect_rewrite_line(Map, line_to, type_to,
  249. Points_final, Cats_to);
  250. /* Vect_list_append(List, line_new); */
  251. }
  252. /* write second part */
  253. Vect_reset_line(Points_final);
  254. Vect_append_point(Points_final, x1, y1, 0.0);
  255. for (is = seg; is < Points_to->n_points; is++)
  256. Vect_append_point(Points_final, Points_to->x[is],
  257. Points_to->y[is], Points_to->z[is]);
  258. if (Vect_line_length(Points_final) > 0) {
  259. if (n_parts > 0)
  260. line_new = Vect_write_line(Map, type_to, Points_final,
  261. Cats_to);
  262. else
  263. line_new = Vect_rewrite_line(Map, line_to, type_to,
  264. Points_final, Cats_to);
  265. /* Vect_list_append(List, line_new); */
  266. }
  267. }
  268. } while(0);
  269. Vect_destroy_line_struct(Points_from);
  270. Vect_destroy_line_struct(Points_to);
  271. Vect_destroy_line_struct(Points_final);
  272. Vect_destroy_cats_struct(Cats_from);
  273. Vect_destroy_cats_struct(Cats_to);
  274. return line_new > 0 ? 1 : 0;
  275. }
  276. static int find_extended_intersection(double x1, double y1, double angle1,
  277. double x2, double y2, double angle2,
  278. double *x, double *y)
  279. {
  280. double c1, s1, c2, s2, d, a;
  281. if (fabs(sin(angle1 - angle2)) <= TOL) {
  282. /* two lines are parallel */
  283. double angle;
  284. angle = atan2(y2 - y1, x2 - x1);
  285. if (fabs(sin(angle - angle1)) <= TOL) {
  286. /* they are on the same line */
  287. *x = x2;
  288. *y = y2;
  289. return 1;
  290. }
  291. /* two lines don't intersect */
  292. return 0;
  293. }
  294. c1 = cos(angle1);
  295. s1 = sin(angle1);
  296. c2 = cos(angle2);
  297. s2 = sin(angle2);
  298. d = -c1 * s2 + c2 * s1;
  299. if (d == 0.0)
  300. /* shouldn't happen again */
  301. return 0;
  302. a = (-s2 * (x2 - x1) + c2 * (y2 - y1)) / d;
  303. *x = x1 + a * c1;
  304. *y = y1 + a * s1;
  305. return 1;
  306. }
  307. static int check_extended_direction(double x, double y, double angle,
  308. int start_node, double extx, double exty)
  309. {
  310. double tmp;
  311. int xdir, ydir, xext, yext;
  312. if (start_node)
  313. angle += M_PI;
  314. /* expected directions */
  315. tmp = cos(angle);
  316. xdir = (fabs(tmp) <= TOL ? 0 : (tmp > 0 ? 1 : -1));
  317. tmp = sin(angle);
  318. ydir = (fabs(tmp) <= TOL ? 0 : (tmp > 0 ? 1 : -1));
  319. /* extended directions */
  320. tmp = extx - x;
  321. xext = (fabs(tmp) <= TOL ? 0 : (tmp > 0 ? 1 : -1));
  322. tmp = exty - y;
  323. yext = (fabs(tmp) <= TOL ? 0 : (tmp > 0 ? 1 : -1));
  324. if (xext != 0 && yext != 0) {
  325. /* no if extended into the wrong direction */
  326. if (xdir / xext <= 0 || ydir / yext <= 0)
  327. return 0;
  328. /* otherwise, ok */
  329. } else if (xext == 0 && yext == 0) {
  330. /* snapped to the node, ok */
  331. } else if (xext == 0) {
  332. /* vertical extension */
  333. /* no if not expected or extended into the wrong direction */
  334. if (xdir != 0 || ydir / yext <= 0)
  335. return 0;
  336. /* otherwise, ok */
  337. } else {
  338. /* horizontal extension */
  339. /* no if not expected or extended into the wrong direction */
  340. if (ydir != 0 || xdir / xext <= 0)
  341. return 0;
  342. /* otherwise, ok */
  343. }
  344. return 1;
  345. }