buffer.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. /*!
  2. \file buffer.c
  3. \brief Vector library - nearest, adjust_line, parallel_line
  4. Higher level functions for reading/writing/manipulating vectors.
  5. (C) 2001-2008 by the GRASS Development Team
  6. This program is free software under the
  7. GNU General Public License (>=v2).
  8. Read the file COPYING that comes with GRASS
  9. for details.
  10. \author Radim Blazek
  11. \date 2001-2008
  12. */
  13. #include <stdlib.h>
  14. #include <math.h>
  15. #include <grass/Vect.h>
  16. #include <grass/gis.h>
  17. #define LENGTH(DX, DY) ( sqrt( (DX*DX)+(DY*DY) ) )
  18. #define PI M_PI
  19. /* vector() calculates normalized vector form two points */
  20. static void vect(double x1, double y1, double x2, double y2, double *x,
  21. double *y)
  22. {
  23. double dx, dy, l;
  24. dx = x2 - x1;
  25. dy = y2 - y1;
  26. l = LENGTH(dx, dy);
  27. if (l == 0) {
  28. /* assume that dx == dy == 0, which should give (NaN,NaN) */
  29. /* without this, very small dx or dy could result in Infinity */
  30. dx = dy = 0;
  31. }
  32. *x = dx / l;
  33. *y = dy / l;
  34. }
  35. /* find_cross find first crossing between segments from s1 to s2 and from s3 to s4
  36. ** s5 is set to first segment and s6 to second
  37. ** neighbours are taken as crossing each other only if overlap
  38. ** returns: 1 found
  39. ** -1 found overlap
  40. ** 0 not found
  41. */
  42. static int find_cross(struct line_pnts *Points, int s1, int s2, int s3,
  43. int s4, int *s5, int *s6)
  44. {
  45. int i, j, np, ret;
  46. double *x, *y;
  47. G_debug(5,
  48. "find_cross(): npoints = %d, s1 = %d, s2 = %d, s3 = %d, s4 = %d",
  49. Points->n_points, s1, s2, s3, s4);
  50. x = Points->x;
  51. y = Points->y;
  52. np = Points->n_points;
  53. for (i = s1; i <= s2; i++) {
  54. for (j = s3; j <= s4; j++) {
  55. if (j == i) {
  56. continue;
  57. }
  58. ret =
  59. dig_test_for_intersection(x[i], y[i], x[i + 1], y[i + 1],
  60. x[j], y[j], x[j + 1], y[j + 1]);
  61. if (ret == 1 && ((i - j) > 1 || (i - j) < -1)) {
  62. *s5 = i;
  63. *s6 = j;
  64. G_debug(5, " intersection: s5 = %d, s6 = %d", *s5, *s6);
  65. return 1;
  66. }
  67. if (ret == -1) {
  68. *s5 = i;
  69. *s6 = j;
  70. G_debug(5, " overlap: s5 = %d, s6 = %d", *s5, *s6);
  71. return -1;
  72. }
  73. }
  74. }
  75. G_debug(5, " no intersection");
  76. return 0;
  77. }
  78. /* point_in_buf - test if point px,py is in d buffer of Points
  79. ** returns: 1 in buffer
  80. ** 0 not in buffer
  81. */
  82. static int point_in_buf(struct line_pnts *Points, double px, double py,
  83. double d)
  84. {
  85. int i, np;
  86. double sd;
  87. np = Points->n_points;
  88. d *= d;
  89. for (i = 0; i < np - 1; i++) {
  90. sd = dig_distance2_point_to_line(px, py, 0,
  91. Points->x[i], Points->y[i], 0,
  92. Points->x[i + 1], Points->y[i + 1],
  93. 0, 0, NULL, NULL, NULL, NULL, NULL);
  94. if (sd <= d) {
  95. return 1;
  96. }
  97. }
  98. return 0;
  99. }
  100. /* clean_parallel - clean parallel line created by parallel_line:
  101. ** - looking for loops and if loop doesn't contain any other loop
  102. ** and centroid of loop is in buffer removes this loop (repeated)
  103. ** - optionally removes all end points in buffer
  104. * parameters:
  105. * Points - parallel line
  106. * origPoints - original line
  107. * d - offset
  108. * rm_end - remove end points in buffer
  109. ** note1: on some lines (multiply selfcrossing; lines with end points
  110. ** in buffer of line other; some shapes of ends ) may create nosense
  111. ** note2: this function is stupid and slow, somebody more clever
  112. ** than I am should write paralle_line + clean_parallel
  113. ** better; RB March 2000
  114. */
  115. static void clean_parallel(struct line_pnts *Points,
  116. struct line_pnts *origPoints, double d, int rm_end)
  117. {
  118. int i, j, np, npn, sa, sb;
  119. int sa_max = 0;
  120. int first = 0, current, last, lcount;
  121. double *x, *y, px, py, ix, iy;
  122. static struct line_pnts *sPoints = NULL;
  123. G_debug(4, "clean_parallel(): npoints = %d, d = %f, rm_end = %d",
  124. Points->n_points, d, rm_end);
  125. x = Points->x;
  126. y = Points->y;
  127. np = Points->n_points;
  128. if (sPoints == NULL)
  129. sPoints = Vect_new_line_struct();
  130. Vect_reset_line(sPoints);
  131. npn = 1;
  132. /* remove loops */
  133. while (first < np - 2) {
  134. /* find first loop which doesn't contain any other loop */
  135. current = first;
  136. last = Points->n_points - 2;
  137. lcount = 0;
  138. while (find_cross
  139. (Points, current, last - 1, current + 1, last, &sa,
  140. &sb) != 0) {
  141. if (lcount == 0) {
  142. first = sa;
  143. } /* move first forward */
  144. current = sa + 1;
  145. last = sb;
  146. lcount++;
  147. G_debug(5, " current = %d, last = %d, lcount = %d", current,
  148. last, lcount);
  149. }
  150. if (lcount == 0) {
  151. break;
  152. } /* loop not found */
  153. /* ensure sa is monotonically increasing, so npn doesn't reset low */
  154. if (sa > sa_max)
  155. sa_max = sa;
  156. if (sa < sa_max)
  157. break;
  158. /* remove loop if in buffer */
  159. if ((sb - sa) == 1) { /* neighbouring lines overlap */
  160. j = sb + 1;
  161. npn = sa + 1;
  162. }
  163. else {
  164. Vect_reset_line(sPoints);
  165. dig_find_intersection(x[sa], y[sa], x[sa + 1], y[sa + 1], x[sb],
  166. y[sb], x[sb + 1], y[sb + 1], &ix, &iy);
  167. Vect_append_point(sPoints, ix, iy, 0);
  168. for (i = sa + 1; i < sb + 1; i++) { /* create loop polygon */
  169. Vect_append_point(sPoints, x[i], y[i], 0);
  170. }
  171. Vect_find_poly_centroid(sPoints, &px, &py);
  172. if (point_in_buf(origPoints, px, py, d)) { /* is loop in buffer ? */
  173. npn = sa + 1;
  174. x[npn] = ix;
  175. y[npn] = iy;
  176. j = sb + 1;
  177. npn++;
  178. if (lcount == 0) {
  179. first = sb;
  180. }
  181. }
  182. else { /* loop is not in buffer */
  183. first = sb;
  184. continue;
  185. }
  186. }
  187. for (i = j; i < Points->n_points; i++) { /* move points down */
  188. x[npn] = x[i];
  189. y[npn] = y[i];
  190. npn++;
  191. }
  192. Points->n_points = npn;
  193. }
  194. if (rm_end) {
  195. /* remove points from start in buffer */
  196. j = 0;
  197. for (i = 0; i < Points->n_points - 1; i++) {
  198. px = (x[i] + x[i + 1]) / 2;
  199. py = (y[i] + y[i + 1]) / 2;
  200. if (point_in_buf(origPoints, x[i], y[i], d * 0.9999)
  201. && point_in_buf(origPoints, px, py, d * 0.9999)) {
  202. j++;
  203. }
  204. else {
  205. break;
  206. }
  207. }
  208. if (j > 0) {
  209. npn = 0;
  210. for (i = j; i < Points->n_points; i++) {
  211. x[npn] = x[i];
  212. y[npn] = y[i];
  213. npn++;
  214. }
  215. Points->n_points = npn;
  216. }
  217. /* remove points from end in buffer */
  218. j = 0;
  219. for (i = Points->n_points - 1; i >= 1; i--) {
  220. px = (x[i] + x[i - 1]) / 2;
  221. py = (y[i] + y[i - 1]) / 2;
  222. if (point_in_buf(origPoints, x[i], y[i], d * 0.9999)
  223. && point_in_buf(origPoints, px, py, d * 0.9999)) {
  224. j++;
  225. }
  226. else {
  227. break;
  228. }
  229. }
  230. if (j > 0) {
  231. Points->n_points -= j;
  232. }
  233. }
  234. }
  235. /* parallel_line - remove duplicate points from input line and
  236. * creates new parallel line in 'd' offset distance;
  237. * 'tol' is tolerance between arc and polyline;
  238. * this function doesn't care about created loops;
  239. *
  240. * New line is written to existing nPoints structure.
  241. */
  242. static void parallel_line(struct line_pnts *Points, double d, double tol,
  243. struct line_pnts *nPoints)
  244. {
  245. int i, j, np, na, side;
  246. double *x, *y, nx, ny, tx, ty, vx, vy, ux, uy, wx, wy;
  247. double atol, atol2, a, av, aw;
  248. G_debug(4, "parallel_line()");
  249. Vect_reset_line(nPoints);
  250. Vect_line_prune(Points);
  251. np = Points->n_points;
  252. x = Points->x;
  253. y = Points->y;
  254. if (np == 0)
  255. return;
  256. if (np == 1) {
  257. Vect_append_point(nPoints, x[0], y[0], 0); /* ? OK, should make circle for points ? */
  258. return;
  259. }
  260. if (d == 0) {
  261. Vect_copy_xyz_to_pnts(nPoints, x, y, NULL, np);
  262. return;
  263. }
  264. side = (int)(d / fabs(d));
  265. atol = 2 * acos(1 - tol / fabs(d));
  266. for (i = 0; i < np - 1; i++) {
  267. vect(x[i], y[i], x[i + 1], y[i + 1], &tx, &ty);
  268. vx = ty * d;
  269. vy = -tx * d;
  270. nx = x[i] + vx;
  271. ny = y[i] + vy;
  272. Vect_append_point(nPoints, nx, ny, 0);
  273. nx = x[i + 1] + vx;
  274. ny = y[i + 1] + vy;
  275. Vect_append_point(nPoints, nx, ny, 0);
  276. if (i < np - 2) { /* use polyline instead of arc between line segments */
  277. vect(x[i + 1], y[i + 1], x[i + 2], y[i + 2], &ux, &uy);
  278. wx = uy * d;
  279. wy = -ux * d;
  280. av = atan2(vy, vx);
  281. aw = atan2(wy, wx);
  282. a = (aw - av) * side;
  283. if (a < 0)
  284. a += 2 * PI;
  285. /* TODO: a <= PI can probably fail because of representation error */
  286. if (a <= PI && a > atol) {
  287. na = (int)(a / atol);
  288. atol2 = a / (na + 1) * side;
  289. for (j = 0; j < na; j++) {
  290. av += atol2;
  291. nx = x[i + 1] + fabs(d) * cos(av);
  292. ny = y[i + 1] + fabs(d) * sin(av);
  293. Vect_append_point(nPoints, nx, ny, 0);
  294. }
  295. }
  296. }
  297. }
  298. Vect_line_prune(nPoints);
  299. }
  300. /*!
  301. \brief Create parrallel line
  302. \param InPoints input line
  303. \param distance create parrallel line in distance
  304. \param tolerance maximum distance between theoretical arc and polygon segments
  305. \param rm_end remove end points falling into distance
  306. \param[out] OutPoints output line
  307. \return
  308. */
  309. void
  310. Vect_line_parallel(struct line_pnts *InPoints, double distance,
  311. double tolerance, int rm_end, struct line_pnts *OutPoints)
  312. {
  313. G_debug(4,
  314. "Vect_line_parallel(): npoints = %d, distance = %f, tolerance = %f",
  315. InPoints->n_points, distance, tolerance);
  316. parallel_line(InPoints, distance, tolerance, OutPoints);
  317. clean_parallel(OutPoints, InPoints, distance, rm_end);
  318. return;
  319. }
  320. /*!
  321. \brief Create buffer around the line line.
  322. Buffer is closed counter clockwise polygon.
  323. Warning: output line may contain loops!
  324. \param InPoints input line
  325. \param distance create buffer in distance
  326. \param tolerance maximum distance between theoretical arc and polygon segments
  327. \param[out] OutPoints output line
  328. */
  329. void
  330. Vect_line_buffer(struct line_pnts *InPoints, double distance,
  331. double tolerance, struct line_pnts *OutPoints)
  332. {
  333. double dangle;
  334. int side, npoints;
  335. static struct line_pnts *Points = NULL;
  336. static struct line_pnts *PPoints = NULL;
  337. distance = fabs(distance);
  338. dangle = 2 * acos(1 - tolerance / fabs(distance)); /* angle step */
  339. if (Points == NULL)
  340. Points = Vect_new_line_struct();
  341. if (PPoints == NULL)
  342. PPoints = Vect_new_line_struct();
  343. /* Copy and prune input */
  344. Vect_reset_line(Points);
  345. Vect_append_points(Points, InPoints, GV_FORWARD);
  346. Vect_line_prune(Points);
  347. Vect_reset_line(OutPoints);
  348. npoints = Points->n_points;
  349. if (npoints <= 0) {
  350. return;
  351. }
  352. else if (npoints == 1) { /* make a circle */
  353. double angle, x, y;
  354. for (angle = 0; angle < 2 * PI; angle += dangle) {
  355. x = Points->x[0] + distance * cos(angle);
  356. y = Points->y[0] + distance * sin(angle);
  357. Vect_append_point(OutPoints, x, y, 0);
  358. }
  359. /* Close polygon */
  360. Vect_append_point(OutPoints, OutPoints->x[0], OutPoints->y[0], 0);
  361. }
  362. else { /* 2 and more points */
  363. for (side = 0; side < 2; side++) {
  364. double angle, sangle;
  365. double lx1, ly1, lx2, ly2;
  366. double x, y, nx, ny, sx, sy, ex, ey;
  367. /* Parallel on one side */
  368. if (side == 0) {
  369. Vect_line_parallel(Points, distance, tolerance, 0, PPoints);
  370. Vect_append_points(OutPoints, PPoints, GV_FORWARD);
  371. }
  372. else {
  373. Vect_line_parallel(Points, -distance, tolerance, 0, PPoints);
  374. Vect_append_points(OutPoints, PPoints, GV_BACKWARD);
  375. }
  376. /* Arc at the end */
  377. /* 2 points at theend of original line */
  378. if (side == 0) {
  379. lx1 = Points->x[npoints - 2];
  380. ly1 = Points->y[npoints - 2];
  381. lx2 = Points->x[npoints - 1];
  382. ly2 = Points->y[npoints - 1];
  383. }
  384. else {
  385. lx1 = Points->x[1];
  386. ly1 = Points->y[1];
  387. lx2 = Points->x[0];
  388. ly2 = Points->y[0];
  389. }
  390. /* normalized vector */
  391. vect(lx1, ly1, lx2, ly2, &nx, &ny);
  392. /* starting point */
  393. sangle = atan2(-nx, ny); /* starting angle */
  394. sx = lx2 + ny * distance;
  395. sy = ly2 - nx * distance;
  396. /* end point */
  397. ex = lx2 - ny * distance;
  398. ey = ly2 + nx * distance;
  399. Vect_append_point(OutPoints, sx, sy, 0);
  400. /* arc */
  401. for (angle = dangle; angle < PI; angle += dangle) {
  402. x = lx2 + distance * cos(sangle + angle);
  403. y = ly2 + distance * sin(sangle + angle);
  404. Vect_append_point(OutPoints, x, y, 0);
  405. }
  406. Vect_append_point(OutPoints, ex, ey, 0);
  407. }
  408. /* Close polygon */
  409. Vect_append_point(OutPoints, OutPoints->x[0], OutPoints->y[0], 0);
  410. }
  411. Vect_line_prune(OutPoints);
  412. return;
  413. }