prune.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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 programm 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 <grass/config.h>
  70. #include <stdio.h>
  71. #include <grass/Vect.h>
  72. #include <math.h>
  73. int dig_prune(struct line_pnts *points, double thresh)
  74. {
  75. double *ox, *oy, *nx, *ny;
  76. double cur_x, cur_y;
  77. int o_num;
  78. int n_num; /* points left */
  79. int at_num;
  80. int ij = 0, /* position of farest point */
  81. ja, jd, i, j, k, n, inu, it; /* indicateur de parcours du segment */
  82. double sqdist; /* square of distance */
  83. double fpdist; /* square of distance from chord to farest point */
  84. double t, beta; /* as explained in commented algorithm */
  85. double dx, dy; /* temporary variables */
  86. double sx[18], sy[18]; /* temporary table for processing points */
  87. int nt[17], nu[17];
  88. /* nothing to do if less than 3 points ! */
  89. if (points->n_points <= 2)
  90. return (points->n_points);
  91. ox = points->x;
  92. oy = points->y;
  93. nx = points->x;
  94. ny = points->y;
  95. o_num = points->n_points;
  96. n_num = 0;
  97. /* Eliminate duplicate points */
  98. at_num = 0;
  99. while (at_num < o_num) {
  100. if (nx != ox) {
  101. *nx = *ox++;
  102. *ny = *oy++;
  103. }
  104. else {
  105. ox++;
  106. oy++;
  107. }
  108. cur_x = *nx++;
  109. cur_y = *ny++;
  110. n_num++;
  111. at_num++;
  112. while (*ox == cur_x && *oy == cur_y) {
  113. if (at_num == o_num)
  114. break;
  115. at_num++;
  116. ox++;
  117. oy++;
  118. }
  119. }
  120. /* Return if less than 3 points left. When all points are identical,
  121. * output only one point (is this valid for calling function ?) */
  122. if (n_num <= 2)
  123. return n_num;
  124. if (thresh == 0.0) /* Thresh is null, nothing more to do */
  125. return n_num;
  126. /* some (re)initialisations */
  127. o_num = n_num;
  128. ox = points->x;
  129. oy = points->y;
  130. sx[0] = ox[0];
  131. sy[0] = oy[0];
  132. n_num = 1;
  133. at_num = 2;
  134. k = 1;
  135. sx[1] = ox[1];
  136. sy[1] = oy[1];
  137. nu[0] = 9;
  138. nu[1] = 0;
  139. inu = 2;
  140. while (at_num < o_num) { /* Position of last point to be */
  141. if (o_num - at_num > 14) /* processed in a segment. */
  142. n = at_num + 9; /* There must be at least 6 points */
  143. else /* in the current segment. */
  144. n = o_num;
  145. sx[0] = sx[nu[1]]; /* Last point written becomes */
  146. sy[0] = sy[nu[1]]; /* first of new segment. */
  147. if (inu > 1) { /* One point was keeped in the *//* previous segment : */
  148. sx[1] = sx[k]; /* Last point of the old segment */
  149. sy[1] = sy[k]; /* becomes second of the new one. */
  150. k = 1;
  151. }
  152. else { /* No point keeped : farest point */
  153. sx[1] = sx[ij]; /* is loaded in second position */
  154. sy[1] = sy[ij]; /* to avoid cutting lines with */
  155. sx[2] = sx[k]; /* small cuvature. */
  156. sy[2] = sy[k]; /* First point of previous segment */
  157. k = 2; /* becomes the third one. */
  158. }
  159. /* Loding remaining points */
  160. for (j = at_num; j < n; j++) {
  161. k++;
  162. sx[k] = ox[j];
  163. sy[k] = oy[j];
  164. }
  165. jd = 0;
  166. ja = k;
  167. nt[0] = 0;
  168. nu[0] = k;
  169. inu = 0;
  170. it = 0;
  171. for (;;) {
  172. if (jd + 1 == ja) /* Exploration of segment terminated */
  173. goto endseg;
  174. dx = sx[ja] - sx[jd];
  175. dy = sy[ja] - sy[jd];
  176. t = thresh * hypot(dx, dy);
  177. beta = sx[jd] * sy[ja] - sx[ja] * sy[jd];
  178. /* Initializing ij, we don't take 0 as initial value
  179. ** for fpdist, in case ja and jd are the same
  180. */
  181. ij = (ja + jd + 1) >> 1;
  182. fpdist = 1.0;
  183. for (j = jd + 1; j < ja; j++) {
  184. sqdist = fabs(dx * sy[j] - dy * sx[j] + beta);
  185. if (sqdist > fpdist) {
  186. ij = j;
  187. fpdist = sqdist;
  188. }
  189. }
  190. if (fpdist > t) { /* We found a point to be keeped *//* Restart from farest point */
  191. jd = ij;
  192. nt[++it] = ij;
  193. }
  194. else
  195. endseg:{ /* All points are inside threshold. */
  196. /* Former start becomes new end */
  197. nu[++inu] = jd;
  198. if (--it < 0)
  199. break;
  200. ja = jd;
  201. jd = nt[it];
  202. }
  203. }
  204. for (j = inu - 1; j > 0; j--) { /* Copy of segment's keeped points */
  205. i = nu[j];
  206. ox[n_num] = sx[i];
  207. oy[n_num] = sy[i];
  208. n_num++;
  209. }
  210. at_num = n;
  211. }
  212. i = nu[0];
  213. ox[n_num] = sx[i];
  214. oy[n_num] = sy[i];
  215. n_num++;
  216. return n_num;
  217. }