buffer.c 12 KB

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