buffer.c 12 KB

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