123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- /*
- ****************************************************************************
- *
- * MODULE: Vector library
- *
- * AUTHOR(S): Original author CERL, probably Dave Gerdes.
- * Update to GRASS 5.7 Radim Blazek.
- *
- * PURPOSE: Lower level functions for reading/writing/manipulating vectors.
- *
- * COPYRIGHT: (C) 2001 by the GRASS Development Team
- *
- * This program is free software under the GNU General Public
- * License (>=v2). Read the file COPYING that comes with GRASS
- * for details.
- *
- *****************************************************************************/
- /* @(#)prune.c 3.0 2/19/98 */
- /* by Michel Wurtz for GRASS 4.2.1 - <mw@engees.u-strasbg.fr>
- * This is a complete rewriting of the previous dig_prune subroutine.
- * The goal remains : it resamples a dense string of x,y coordinates to
- * produce a set of coordinates that approaches hand digitizing.
- * That is, the density of points is very low on straight lines, and
- * highest on tight curves.
- *
- * The algorithm used is very different, and based on the suppression
- * of intermediate points, when they are closer than thresh from a
- * moving straight line.
- *
- * The distance between a point M -> ->
- * and a AD segment is given || AM ^ AD ||
- * by the following formula : d = ---------------
- * ->
- * || AD ||
- *
- * -> -> ->
- * When comparing || AM ^ AD || and t = thresh * || AD ||
- *
- * -> -> -> ->
- * we call sqdist = | AM ^ AD | = | OA ^ OM + beta |
- *
- * -> ->
- * with beta = OA ^ OD
- *
- * The implementation is based on an old integer routine (optimised
- * for machine without math coprocessor), itself inspired by a PL/1
- * routine written after a fortran program on some prehistoric
- * hardware (IBM 360 probably). Yeah, I'm older than before :-)
- *
- * The algorithm used doesn't eliminate "duplicate" points (following
- * points with same coordinates). So we should clean the set of points
- * before. As a side effect, dig_prune can be called with a null thresh
- * value. In this case only cleaning is made. The command v.prune is to
- * be modified accordingly.
- *
- * Another important note : Don't try too big threshold, this subroutine
- * may produce strange things with some pattern (mainly loops, or crossing
- * of level curves): Try the set { 0,0 -5,0 -4,10 -6,20 -5,30 -5,20 10,10}
- * with a thershold of 5. This isn't a programmation, but a conceptal bug ;-)
- *
- * Input parameters :
- * points->x, ->y - double precision sets of coordinates.
- * points->n_points - the total number of points in the set.
- * thresh - the distance that a string must wander from a straight
- * line before another point is selected.
- *
- * Value returned : - the final number of points in the pruned set.
- */
- #include <stdio.h>
- #include <grass/vector.h>
- #include <math.h>
- int dig_prune(struct line_pnts *points, double thresh)
- {
- double *ox, *oy, *nx, *ny;
- double cur_x, cur_y;
- int o_num;
- int n_num; /* points left */
- int at_num;
- int ij = 0, /* position of farest point */
- ja, jd, i, j, k, n, inu, it; /* indicateur de parcours du segment */
- double sqdist; /* square of distance */
- double fpdist; /* square of distance from chord to farest point */
- double t, beta; /* as explained in commented algorithm */
- double dx, dy; /* temporary variables */
- double sx[18], sy[18]; /* temporary table for processing points */
- int nt[17], nu[17];
- /* nothing to do if less than 3 points ! */
- if (points->n_points <= 2)
- return (points->n_points);
- ox = points->x;
- oy = points->y;
- nx = points->x;
- ny = points->y;
- o_num = points->n_points;
- n_num = 0;
- /* Eliminate duplicate points */
- at_num = 0;
- while (at_num < o_num) {
- if (nx != ox) {
- *nx = *ox++;
- *ny = *oy++;
- }
- else {
- ox++;
- oy++;
- }
- cur_x = *nx++;
- cur_y = *ny++;
- n_num++;
- at_num++;
- while (*ox == cur_x && *oy == cur_y) {
- if (at_num == o_num)
- break;
- at_num++;
- ox++;
- oy++;
- }
- }
- /* Return if less than 3 points left. When all points are identical,
- * output only one point (is this valid for calling function ?) */
- if (n_num <= 2)
- return n_num;
- if (thresh == 0.0) /* Thresh is null, nothing more to do */
- return n_num;
- /* some (re)initialisations */
- o_num = n_num;
- ox = points->x;
- oy = points->y;
- sx[0] = ox[0];
- sy[0] = oy[0];
- n_num = 1;
- at_num = 2;
- k = 1;
- sx[1] = ox[1];
- sy[1] = oy[1];
- nu[0] = 9;
- nu[1] = 0;
- inu = 2;
- while (at_num < o_num) { /* Position of last point to be */
- if (o_num - at_num > 14) /* processed in a segment. */
- n = at_num + 9; /* There must be at least 6 points */
- else /* in the current segment. */
- n = o_num;
- sx[0] = sx[nu[1]]; /* Last point written becomes */
- sy[0] = sy[nu[1]]; /* first of new segment. */
- if (inu > 1) { /* One point was keeped in the *//* previous segment : */
- sx[1] = sx[k]; /* Last point of the old segment */
- sy[1] = sy[k]; /* becomes second of the new one. */
- k = 1;
- }
- else { /* No point keeped : farest point */
- sx[1] = sx[ij]; /* is loaded in second position */
- sy[1] = sy[ij]; /* to avoid cutting lines with */
- sx[2] = sx[k]; /* small cuvature. */
- sy[2] = sy[k]; /* First point of previous segment */
- k = 2; /* becomes the third one. */
- }
- /* Loding remaining points */
- for (j = at_num; j < n; j++) {
- k++;
- sx[k] = ox[j];
- sy[k] = oy[j];
- }
- jd = 0;
- ja = k;
- nt[0] = 0;
- nu[0] = k;
- inu = 0;
- it = 0;
- for (;;) {
- if (jd + 1 == ja) /* Exploration of segment terminated */
- goto endseg;
- dx = sx[ja] - sx[jd];
- dy = sy[ja] - sy[jd];
- t = thresh * hypot(dx, dy);
- beta = sx[jd] * sy[ja] - sx[ja] * sy[jd];
- /* Initializing ij, we don't take 0 as initial value
- ** for fpdist, in case ja and jd are the same
- */
- ij = (ja + jd + 1) >> 1;
- fpdist = 1.0;
- for (j = jd + 1; j < ja; j++) {
- sqdist = fabs(dx * sy[j] - dy * sx[j] + beta);
- if (sqdist > fpdist) {
- ij = j;
- fpdist = sqdist;
- }
- }
- if (fpdist > t) { /* We found a point to be keeped *//* Restart from farest point */
- jd = ij;
- nt[++it] = ij;
- }
- else
- endseg:{ /* All points are inside threshold. */
- /* Former start becomes new end */
- nu[++inu] = jd;
- if (--it < 0)
- break;
- ja = jd;
- jd = nt[it];
- }
- }
- for (j = inu - 1; j > 0; j--) { /* Copy of segment's keeped points */
- i = nu[j];
- ox[n_num] = sx[i];
- oy[n_num] = sy[i];
- n_num++;
- }
- at_num = n;
- }
- i = nu[0];
- ox[n_num] = sx[i];
- oy[n_num] = sy[i];
- n_num++;
- return n_num;
- }
|