prune.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /*
  2. ****************************************************************************
  3. *
  4. * MODULE: Vector library
  5. *
  6. * AUTHOR(S): Original author CERL, probably Dave Gerdes.
  7. * Update to GRASS 5.7 Radim Blazek.
  8. *
  9. * PURPOSE: Lower level functions for reading/writing/manipulating vectors.
  10. *
  11. * COPYRIGHT: (C) 2001 by the GRASS Development Team
  12. *
  13. * This program is free software under the GNU General Public
  14. * License (>=v2). Read the file COPYING that comes with GRASS
  15. * for details.
  16. *
  17. *****************************************************************************/
  18. /* @(#)prune.c 3.0 2/19/98 */
  19. /* by Michel Wurtz for GRASS 4.2.1 - <mw@engees.u-strasbg.fr>
  20. * This is a complete rewriting of the previous dig_prune subroutine.
  21. * The goal remains : it resamples a dense string of x,y coordinates to
  22. * produce a set of coordinates that approaches hand digitizing.
  23. * That is, the density of points is very low on straight lines, and
  24. * highest on tight curves.
  25. *
  26. * The algorithm used is very different, and based on the suppression
  27. * of intermediate points, when they are closer than thresh from a
  28. * moving straight line.
  29. *
  30. * The distance between a point M -> ->
  31. * and a AD segment is given || AM ^ AD ||
  32. * by the following formula : d = ---------------
  33. * ->
  34. * || AD ||
  35. *
  36. * -> -> ->
  37. * When comparing || AM ^ AD || and t = thresh * || AD ||
  38. *
  39. * -> -> -> ->
  40. * we call sqdist = | AM ^ AD | = | OA ^ OM + beta |
  41. *
  42. * -> ->
  43. * with beta = OA ^ OD
  44. *
  45. * The implementation is based on an old integer routine (optimised
  46. * for machine without math coprocessor), itself inspired by a PL/1
  47. * routine written after a fortran program on some prehistoric
  48. * hardware (IBM 360 probably). Yeah, I'm older than before :-)
  49. *
  50. * The algorithm used doesn't eliminate "duplicate" points (following
  51. * points with same coordinates). So we should clean the set of points
  52. * before. As a side effect, dig_prune can be called with a null thresh
  53. * value. In this case only cleaning is made. The command v.prune is to
  54. * be modified accordingly.
  55. *
  56. * Another important note : Don't try too big threshold, this subroutine
  57. * may produce strange things with some pattern (mainly loops, or crossing
  58. * of level curves): Try the set { 0,0 -5,0 -4,10 -6,20 -5,30 -5,20 10,10}
  59. * with a thershold of 5. This isn't a programmation, but a conceptal bug ;-)
  60. *
  61. * Input parameters :
  62. * points->x, ->y - double precision sets of coordinates.
  63. * points->n_points - the total number of points in the set.
  64. * thresh - the distance that a string must wander from a straight
  65. * line before another point is selected.
  66. *
  67. * Value returned : - the final number of points in the pruned set.
  68. */
  69. #include <stdio.h>
  70. #include <grass/vector.h>
  71. #include <math.h>
  72. int dig_prune(struct line_pnts *points, double thresh)
  73. {
  74. double *ox, *oy, *nx, *ny;
  75. double cur_x, cur_y;
  76. int o_num;
  77. int n_num; /* points left */
  78. int at_num;
  79. int ij = 0, /* position of farest point */
  80. ja, jd, i, j, k, n, inu, it; /* indicateur de parcours du segment */
  81. double sqdist; /* square of distance */
  82. double fpdist; /* square of distance from chord to farest point */
  83. double t, beta; /* as explained in commented algorithm */
  84. double dx, dy; /* temporary variables */
  85. double sx[18], sy[18]; /* temporary table for processing points */
  86. int nt[17], nu[17];
  87. /* nothing to do if less than 3 points ! */
  88. if (points->n_points <= 2)
  89. return (points->n_points);
  90. ox = points->x;
  91. oy = points->y;
  92. nx = points->x;
  93. ny = points->y;
  94. o_num = points->n_points;
  95. n_num = 0;
  96. /* Eliminate duplicate points */
  97. at_num = 0;
  98. while (at_num < o_num) {
  99. if (nx != ox) {
  100. *nx = *ox++;
  101. *ny = *oy++;
  102. }
  103. else {
  104. ox++;
  105. oy++;
  106. }
  107. cur_x = *nx++;
  108. cur_y = *ny++;
  109. n_num++;
  110. at_num++;
  111. while (*ox == cur_x && *oy == cur_y) {
  112. if (at_num == o_num)
  113. break;
  114. at_num++;
  115. ox++;
  116. oy++;
  117. }
  118. }
  119. /* Return if less than 3 points left. When all points are identical,
  120. * output only one point (is this valid for calling function ?) */
  121. if (n_num <= 2)
  122. return n_num;
  123. if (thresh == 0.0) /* Thresh is null, nothing more to do */
  124. return n_num;
  125. /* some (re)initialisations */
  126. o_num = n_num;
  127. ox = points->x;
  128. oy = points->y;
  129. sx[0] = ox[0];
  130. sy[0] = oy[0];
  131. n_num = 1;
  132. at_num = 2;
  133. k = 1;
  134. sx[1] = ox[1];
  135. sy[1] = oy[1];
  136. nu[0] = 9;
  137. nu[1] = 0;
  138. inu = 2;
  139. while (at_num < o_num) { /* Position of last point to be */
  140. if (o_num - at_num > 14) /* processed in a segment. */
  141. n = at_num + 9; /* There must be at least 6 points */
  142. else /* in the current segment. */
  143. n = o_num;
  144. sx[0] = sx[nu[1]]; /* Last point written becomes */
  145. sy[0] = sy[nu[1]]; /* first of new segment. */
  146. if (inu > 1) { /* One point was keeped in the *//* previous segment : */
  147. sx[1] = sx[k]; /* Last point of the old segment */
  148. sy[1] = sy[k]; /* becomes second of the new one. */
  149. k = 1;
  150. }
  151. else { /* No point keeped : farest point */
  152. sx[1] = sx[ij]; /* is loaded in second position */
  153. sy[1] = sy[ij]; /* to avoid cutting lines with */
  154. sx[2] = sx[k]; /* small cuvature. */
  155. sy[2] = sy[k]; /* First point of previous segment */
  156. k = 2; /* becomes the third one. */
  157. }
  158. /* Loding remaining points */
  159. for (j = at_num; j < n; j++) {
  160. k++;
  161. sx[k] = ox[j];
  162. sy[k] = oy[j];
  163. }
  164. jd = 0;
  165. ja = k;
  166. nt[0] = 0;
  167. nu[0] = k;
  168. inu = 0;
  169. it = 0;
  170. for (;;) {
  171. if (jd + 1 == ja) /* Exploration of segment terminated */
  172. goto endseg;
  173. dx = sx[ja] - sx[jd];
  174. dy = sy[ja] - sy[jd];
  175. t = thresh * hypot(dx, dy);
  176. beta = sx[jd] * sy[ja] - sx[ja] * sy[jd];
  177. /* Initializing ij, we don't take 0 as initial value
  178. ** for fpdist, in case ja and jd are the same
  179. */
  180. ij = (ja + jd + 1) >> 1;
  181. fpdist = 1.0;
  182. for (j = jd + 1; j < ja; j++) {
  183. sqdist = fabs(dx * sy[j] - dy * sx[j] + beta);
  184. if (sqdist > fpdist) {
  185. ij = j;
  186. fpdist = sqdist;
  187. }
  188. }
  189. if (fpdist > t) { /* We found a point to be keeped *//* Restart from farest point */
  190. jd = ij;
  191. nt[++it] = ij;
  192. }
  193. else
  194. endseg:{ /* All points are inside threshold. */
  195. /* Former start becomes new end */
  196. nu[++inu] = jd;
  197. if (--it < 0)
  198. break;
  199. ja = jd;
  200. jd = nt[it];
  201. }
  202. }
  203. for (j = inu - 1; j > 0; j--) { /* Copy of segment's keeped points */
  204. i = nu[j];
  205. ox[n_num] = sx[i];
  206. oy[n_num] = sy[i];
  207. n_num++;
  208. }
  209. at_num = n;
  210. }
  211. i = nu[0];
  212. ox[n_num] = sx[i];
  213. oy[n_num] = sy[i];
  214. n_num++;
  215. return n_num;
  216. }