GRASS Programmer's Manual  6.5.svn(2012)-r51648
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
buffer.c
Go to the documentation of this file.
00001 
00021 /* BUGS
00022  * 
00023  * buffering these shapes is unresolved for buffer distances which 
00024  * should create a closed loop in the inside
00025  * 
00026  *  -----------   ------------
00027  *  |          \ /              
00028  *  |
00029  *  |
00030  *  |          / \
00031  *  -----------   ------------
00032  * 
00033  * 
00034  *  -----------
00035  *  |         |
00036  *  |          
00037  *  |         | 
00038  *  -----------
00039  * 
00040  * 
00041  * for certain buffer distances, undefined behaviour for
00042  * 
00043  *  ---------
00044  *  |       |
00045  *  |
00046  *  --------------
00047  *               |
00048  *  --------------
00049  *  |
00050  *  |       |
00051  *  ---------
00052  * 
00053  * MM April 2011
00054  * */
00055 
00056 #include <stdlib.h>
00057 #include <math.h>
00058 #include <grass/Vect.h>
00059 #include <grass/gis.h>
00060 
00061 #define LENGTH(DX, DY)  (  sqrt( (DX*DX)+(DY*DY) )  )
00062 #define PI M_PI
00063 #define D_MULT 0.99999999  /* distance multiplier for point_in_buf() */
00064 
00065 /* vector() calculates normalized vector from two points */
00066 static void vect(double x1, double y1, double x2, double y2, double *x,
00067                  double *y)
00068 {
00069     double dx, dy, l;
00070 
00071     dx = x2 - x1;
00072     dy = y2 - y1;
00073     l = LENGTH(dx, dy);
00074     if (l == 0) {
00075         /* assume that dx == dy == 0, which should give (NaN,NaN) */
00076         /* without this, very small dx or dy could result in Infinity */
00077         dx = dy = 0;
00078     }
00079     *x = dx / l;
00080     *y = dy / l;
00081 }
00082 
00083 /* find_cross find first crossing between segments from s1 to s2 and from s3 to s4
00084  ** s5 is set to first segment and s6 to second
00085  ** neighbours are taken as crossing each other only if overlap
00086  ** returns: 1 found
00087  **         -1 found overlap
00088  **          0 not found
00089  */
00090 static int find_cross(struct line_pnts *Points, int s1, int s2, int s3,
00091                       int s4, int *s5, int *s6)
00092 {
00093     int i, j, jstart, np, ret;
00094     double *x, *y;
00095 
00096     G_debug(5,
00097             "find_cross(): npoints = %d, s1 = %d, s2 = %d, s3 = %d, s4 = %d",
00098             Points->n_points, s1, s2, s3, s4);
00099 
00100     x = Points->x;
00101     y = Points->y;
00102     np = Points->n_points;
00103 
00104     for (i = s1; i <= s2; i++) {
00105         jstart = i + 1 < s3 ? s3 : i + 1;
00106         for (j = jstart; j <= s4; j++) {
00107             if (j == i) {
00108                 continue;
00109             }
00110             ret =
00111                 dig_test_for_intersection(x[i], y[i], x[i + 1], y[i + 1],
00112                                           x[j], y[j], x[j + 1], y[j + 1]);
00113             /* j should always be > i */
00114             if (ret == 1 && ((i - j) > 1 || (i - j) < -1)) {
00115                 *s5 = i;
00116                 *s6 = j;
00117                 G_debug(5, "  intersection: s5 = %d, s6 = %d", *s5, *s6);
00118                 return 1;
00119             }
00120             if (ret == -1) {
00121                 *s5 = i;
00122                 *s6 = j;
00123                 G_debug(5, "  overlap: s5 = %d, s6 = %d", *s5, *s6);
00124                 return -1;
00125             }
00126         }
00127     }
00128     G_debug(5, "find_cross() ->  no intersection");
00129     return 0;
00130 }
00131 
00132 /* find_cross2 find first crossing between segments from s1 to s2 and from s3 to s4
00133  ** proceed from s1 to s2 and from s4 to s3, i.e. forward/backward
00134  ** s5 is set to first segment and s6 to second
00135  ** neighbours are taken as crossing each other only if overlap
00136  ** returns: 1 found
00137  **         -1 found overlap
00138  **          0 not found
00139  */
00140 static int find_cross_reverse(struct line_pnts *Points, int s1, int s2, int s3,
00141                       int s4, int *s5, int *s6)
00142 {
00143     int i, j, jend, np, ret;
00144     double *x, *y;
00145 
00146     G_debug(5, 
00147             "find_cross_reverse(): npoints = %d, s1 = %d, s2 = %d, s3 = %d, s4 = %d",
00148             Points->n_points, s1, s2, s3, s4);
00149 
00150     x = Points->x;
00151     y = Points->y;
00152     np = Points->n_points;
00153 
00154     for (i = s1; i <= s2; i++) {
00155         jend = i + 1 < s3 ? s3 : i + 1;
00156         for (j = s4 - 1; j >= jend; j--) {
00157             if (j == i) {
00158                 continue;
00159             }
00160             ret =
00161                 dig_test_for_intersection(x[i], y[i], x[i + 1], y[i + 1],
00162                                           x[j], y[j], x[j + 1], y[j + 1]);
00163             /* j should always be >= i */
00164             if (ret == 1 && ((i - j) > 1 || (i - j) < -1)) {
00165                 *s5 = i;
00166                 *s6 = j;
00167                 G_debug(5, "  intersection: s5 = %d, s6 = %d", *s5, *s6);
00168                 return 1;
00169             }
00170             if (ret == -1) {
00171                 *s5 = i;
00172                 *s6 = j;
00173                 G_debug(5, "  overlap: s5 = %d, s6 = %d", *s5, *s6);
00174                 return -1;
00175             }
00176         }
00177     }
00178     G_debug(5, "find_cross_reverse() ->  no intersection");
00179     return 0;
00180 }
00181 
00182 /* find_cross_from_start find first crossing of segment s1 to s1 + 1 with all
00183  ** segments from s1 + 1 to Points->n_points - 2
00184  ** ix is set to East crossing and iy to North crossing
00185  ** s2 is set to the intersecting segment
00186  ** neighbours are taken as crossing each other only if overlap
00187  ** returns: 1 found
00188  **          0 not found
00189  */
00190 static int find_cross_from_start(struct line_pnts *Points, int s1, int *s2,
00191                                  double *ix, double *iy)
00192 {
00193     int i, np, ret, found = 0;
00194     double *x, *y, min_dist, new_dist, new_x, new_y, dx, dy;
00195 
00196     G_debug(5, "find_cross_from_start(): npoints = %d, s1 = %d",
00197                                                   Points->n_points, s1);
00198 
00199     x = Points->x;
00200     y = Points->y;
00201     np = Points->n_points;
00202     
00203     min_dist = PORT_DOUBLE_MAX;
00204 
00205     for (i = np - 2; i > s1; i--) {
00206         ret =
00207             dig_test_for_intersection(x[s1], y[s1], x[s1 + 1], y[s1 + 1],
00208                                       x[i], y[i], x[i + 1], y[i + 1]);
00209                                       
00210         if (ret) {
00211             dig_find_intersection(x[s1], y[s1], x[s1 + 1], y[s1 + 1], x[i],
00212                                   y[i], x[i + 1], y[i + 1], &new_x, &new_y);
00213             if ((new_x != x[s1] || new_y != y[s1]) && 
00214                 (new_x != x[s1 + 1] || new_y != y[s1 + 1])) {
00215                 found = 1;
00216                 dx = x[s1] - new_x;
00217                 dy = y[s1] - new_y;
00218                 new_dist = LENGTH(dx, dy);
00219                 if (min_dist > new_dist) {
00220                     min_dist = new_dist;
00221                     *ix = new_x;
00222                     *iy = new_y;
00223                     *s2 = i;
00224                 }
00225             }
00226         }
00227     }
00228 
00229     G_debug(5, "find_cross_from_start(): intersection %s",
00230                                          found ? "found" : "not found");
00231     return found;
00232 }
00233 
00234 /* find_cross_from_end find first crossing of segment s1 to s1 + 1 with all
00235  ** segments from 0 to s1 - 1
00236  ** ix is set to East crossing and iy to North crossing
00237  ** s2 is set to the intersecting segment
00238  ** neighbours are taken as crossing each other only if overlap
00239  ** returns: 1 found
00240  **          0 not found
00241  */
00242 static int find_cross_from_end(struct line_pnts *Points, int s1, int *s2,
00243                                double *ix, double *iy)
00244 {
00245     int i, np, ret, found = 0;
00246     double *x, *y, min_dist, new_dist, new_x, new_y, dx, dy;
00247 
00248     G_debug(5, "find_cross_from_end(): npoints = %d, s1 = %d",
00249                                                   Points->n_points, s1);
00250 
00251     x = Points->x;
00252     y = Points->y;
00253     np = Points->n_points;
00254     
00255     min_dist = PORT_DOUBLE_MAX;
00256 
00257     for (i = 0; i < s1; i++) {
00258         ret =
00259             dig_test_for_intersection(x[s1], y[s1], x[s1 + 1], y[s1 + 1],
00260                                       x[i], y[i], x[i + 1], y[i + 1]);
00261                                       
00262         if (ret) {
00263             dig_find_intersection(x[s1], y[s1], x[s1 + 1], y[s1 + 1], x[i],
00264                                   y[i], x[i + 1], y[i + 1], &new_x, &new_y);
00265             if ((new_x != x[s1] || new_y != y[s1]) && 
00266                 (new_x != x[s1 + 1] || new_y != y[s1 + 1])) {
00267                 found = 1;
00268                 dx = x[s1 + 1] - new_x;
00269                 dy = y[s1 + 1] - new_y;
00270                 new_dist = LENGTH(dx, dy);
00271                 if (min_dist > new_dist) {
00272                     min_dist = new_dist;
00273                     *ix = new_x;
00274                     *iy = new_y;
00275                     *s2 = i;
00276                 }
00277             }
00278         }
00279     }
00280 
00281     G_debug(5, "find_cross_from_end(): intersection %s",
00282                                          found ? "found" : "not found");
00283     return found;
00284 }
00285 
00286 /* point_in_buf - test if point px,py is in d buffer of Points
00287  ** returns:  1 in buffer
00288  **           0 not  in buffer
00289  */
00290 static int point_in_buf(struct line_pnts *Points, double px, double py,
00291                         double d)
00292 {
00293     int i, np;
00294     double sd;
00295 
00296     np = Points->n_points;
00297     /* d must be the squared distance */
00298     for (i = 0; i < np - 1; i++) {
00299         sd = dig_distance2_point_to_line(px, py, 0,
00300                                          Points->x[i], Points->y[i], 0,
00301                                          Points->x[i + 1], Points->y[i + 1],
00302                                          0, 0, NULL, NULL, NULL, NULL, NULL);
00303         if (sd <= d) {
00304             return 1;
00305         }
00306     }
00307     return 0;
00308 }
00309 
00310 /* clean_parallel - clean parallel line created by parallel_line:
00311  ** - looking for loops and if loop doesn't contain any other loop
00312  **   and centroid of loop is in buffer removes this loop (repeated)
00313  ** - optionally removes all end points in buffer
00314  ** - optionally closes output line if input line is looped
00315  *    parameters:
00316  *      Points - parallel line
00317  *      origPoints - original line
00318  *      d - offset
00319  *      rm_end - remove end points in buffer
00320  ** note1: on some lines (multiple selfcrossing; lines with end points
00321  **        in buffer of other line; some shapes of ends ) may create nosense
00322  ** note2: this function is stupid and slow, somebody more clever
00323  **        than I am should write paralle_line + clean_parallel
00324  **        better;    RB March 2000
00325  **        speed a bit improved, more thorough cleaning    MM Feb 2011
00326  */
00327 static void clean_parallel(struct line_pnts *Points,
00328                            struct line_pnts *origPoints,
00329                            double d, double tol, int rm_end, int close_loop)
00330 {
00331     int i, j, np, npn, sa, sb, ret, side;
00332     int sa_max = 0;
00333     int first = 0, current, last, lcount;
00334     double *x, *y, px, py, ix, iy;
00335     static struct line_pnts *sPoints = NULL;
00336     double d2 = d * d * D_MULT;
00337 
00338     G_debug(4, "clean_parallel(): npoints = %d, d = %f, rm_end = %d",
00339             Points->n_points, d, rm_end);
00340 
00341     Vect_line_prune(Points);
00342     x = Points->x;
00343     y = Points->y;
00344     np = Points->n_points;
00345     
00346     if (np < 3)
00347         return;
00348         
00349     /* duplicate consecutive points cause problems for
00350      * dig_test_for_intersection() and dig_find_intersection()
00351      * -> output line must always be pruned */
00352 
00353     if (rm_end) {
00354         int inserted;
00355 
00356         /* remove points from end in buffer */
00357         j = inserted = 0;
00358         for (i = Points->n_points - 1; i >= 1; i--) {
00359             x = Points->x;
00360             y = Points->y;
00361             if (rm_end < 2) {
00362                 px = (x[i] + x[i - 1]) / 2;
00363                 py = (y[i] + y[i - 1]) / 2;
00364             }
00365             else {
00366                 /* this is for the last arc created by Vect_line_buffer() */
00367                 px = x[i - 1];
00368                 py = y[i - 1];
00369             }
00370             if (point_in_buf(origPoints, x[i], y[i], d2)) {
00371                 
00372                 /* check for intersection of this segment */
00373                 /* must not be the first or last point of this segment */
00374                 current = i - 1;
00375                 if (find_cross_from_end(Points, current, &sa, &ix, &iy) != 0) {
00376                     /* insert intersection point into this segment */
00377                     /* and adjust i and Points->n_points */
00378                     int k = 0;
00379 
00380                     if ((ix == x[sa] && iy == y[sa]) ||
00381                         (ix == x[sa + 1] && iy == y[sa + 1])) {
00382                             
00383                         /* do not insert ix,iy into sa segment */
00384                         Vect_append_point(Points, Points->x[Points->n_points - 1],
00385                                           Points->y[Points->n_points - 1], 0);
00386                         
00387                         for (k = Points->n_points - 2; k > current + 1; k--) {
00388                             Points->x[k] = Points->x[k - 1];
00389                             Points->y[k] = Points->y[k - 1];
00390                         }
00391                         Points->x[current + 1] = ix;
00392                         Points->y[current + 1] = iy;
00393                         i += 2;
00394                     }
00395                     else {
00396                         Vect_append_point(Points, Points->x[Points->n_points - 1],
00397                                           Points->y[Points->n_points - 1], 0);
00398                         Vect_append_point(Points, Points->x[Points->n_points - 1],
00399                                           Points->y[Points->n_points - 1], 0);
00400                         
00401                         for (k = Points->n_points - 2; k > current + 2; k--) {
00402                             Points->x[k] = Points->x[k - 2];
00403                             Points->y[k] = Points->y[k - 2];
00404                         }
00405                         Points->x[current + 2] = ix;
00406                         Points->y[current + 2] = iy;
00407                         for (k = current + 1; k > sa + 1; k--) {
00408                             Points->x[k] = Points->x[k - 1];
00409                             Points->y[k] = Points->y[k - 1];
00410                         }
00411                         Points->x[sa + 1] = ix;
00412                         Points->y[sa + 1] = iy;
00413                         i += 3;
00414                     }
00415 
00416                     inserted++;
00417                 }
00418                 else if (point_in_buf(origPoints, px, py, d2)) {
00419                     j++;
00420                     if (inserted)
00421                         close_loop = 0;
00422                 }
00423                 else {
00424                     break;
00425                 }
00426             }
00427             else {
00428                 break;
00429             }
00430         }
00431         if (j > 0) {
00432             Points->n_points -= j;
00433         }
00434 
00435         /* remove points from start in buffer */
00436         j = inserted = 0;
00437         for (i = 0; i < Points->n_points - 1; i++) {
00438             x = Points->x;
00439             y = Points->y;
00440             px = (x[i] + x[i + 1]) / 2;
00441             py = (y[i] + y[i + 1]) / 2;
00442             if (point_in_buf(origPoints, x[i], y[i], d2)) {
00443                 
00444                 /* check for intersection of this segment */
00445                 /* must not be the first or last point of this segment */
00446                 current = i;
00447                 if (find_cross_from_start(Points, current, &sa, &ix, &iy) != 0) {
00448                     /* insert intersection point into this segment */
00449                     /* and adjust i and Points->n_points */
00450                     int k = 0;
00451                     
00452                     if ((ix == x[sa] && iy == y[sa]) ||
00453                         (ix == x[sa + 1] && iy == y[sa + 1])) {
00454                             
00455                         /* do not insert ix,iy into sa segment */
00456                         Vect_append_point(Points, Points->x[Points->n_points - 1],
00457                                           Points->y[Points->n_points - 1], 0);
00458                         
00459                         for (k = Points->n_points - 2; k > current + 1; k--) {
00460                             Points->x[k] = Points->x[k - 1];
00461                             Points->y[k] = Points->y[k - 1];
00462                         }
00463                         Points->x[current + 1] = ix;
00464                         Points->y[current + 1] = iy;
00465                         i--;
00466                     }
00467                     else {
00468                         Vect_append_point(Points, Points->x[Points->n_points - 1],
00469                                           Points->y[Points->n_points - 1], 0);
00470                         Vect_append_point(Points, Points->x[Points->n_points - 1],
00471                                           Points->y[Points->n_points - 1], 0);
00472                         
00473                         for (k = Points->n_points - 2; k > sa + 2; k--) {
00474                             Points->x[k] = Points->x[k - 2];
00475                             Points->y[k] = Points->y[k - 2];
00476                         }
00477                         Points->x[sa + 2] = ix;
00478                         Points->y[sa + 2] = iy;
00479                         for (k = sa + 1; k > current + 1; k--) {
00480                             Points->x[k] = Points->x[k - 1];
00481                             Points->y[k] = Points->y[k - 1];
00482                         }
00483                         Points->x[current + 1] = ix;
00484                         Points->y[current + 1] = iy;
00485                         i--;
00486                     }
00487                     inserted++;
00488                 }
00489                 else if (point_in_buf(origPoints, px, py, d2)) {
00490                     j++;
00491                     if (inserted)
00492                         close_loop = 0;
00493                 }
00494                 else {
00495                     break;
00496                 }
00497             }
00498             else {
00499                 break;
00500             }
00501         }
00502         x = Points->x;
00503         y = Points->y;
00504         if (j > 0) {
00505             npn = 0;
00506             for (i = j; i < Points->n_points; i++) {
00507                 x[npn] = x[i];
00508                 y[npn] = y[i];
00509                 npn++;
00510             }
00511             Points->n_points = npn;
00512         }
00513 
00514         /* test for intersection of end segments */
00515         /* need to do this here, otherwise remove loops below will
00516          * remove everything but the end dangles */
00517         np = Points->n_points;
00518         if (np > 3 && (x[0] != x[np - 1] || y[0] != y[np - 1])) {
00519             i = 0;
00520             j = np - 2;
00521             ret =
00522                 dig_test_for_intersection(x[i], y[i], x[i + 1], y[i + 1],
00523                                           x[j], y[j], x[j + 1], y[j + 1]);
00524                                           
00525             if (ret == -1) {
00526                 /* overlap */
00527                 x[np - 1] = x[0];
00528                 y[np - 1] = y[0];
00529             }
00530             else if (ret == 1) {
00531                 dig_find_intersection(x[i], y[i], x[i + 1], y[i + 1], x[j],
00532                                       y[j], x[j + 1], y[j + 1], &ix, &iy);
00533                 x[0] = ix;
00534                 y[0] = iy;
00535                 x[np - 1] = ix;
00536                 y[np - 1] = iy;
00537             }
00538         }
00539         Vect_line_prune(Points);
00540     }
00541 
00542     if (sPoints == NULL)
00543         sPoints = Vect_new_line_struct();
00544 
00545     Vect_reset_line(sPoints);
00546 
00547     np = Points->n_points;
00548     npn = 1;
00549 
00550     side = (int)(d / fabs(d));
00551 
00552     /* remove loops */
00553     while (np > 3 && first < np - 2) {
00554         /* find first loop which doesn't contain any other loop */
00555         current = first;
00556         G_debug(5, "current: %d", current);
00557         last = Points->n_points - 2;
00558         lcount = 0;
00559         sa = current;
00560         while (find_cross
00561                (Points, current, last - 1, current + 1, last, &sa, &sb) != 0) {
00562 
00563             if (lcount == 0)
00564                 first = sa;  /* move first forward */
00565 
00566             current = sa + 1;
00567             last = sb;
00568             lcount++;
00569             G_debug(5, "  current = %d, last = %d, lcount = %d", current,
00570                     last, lcount);
00571         }
00572         if (lcount == 0) {
00573             break;
00574         }                       /* loop not found */
00575 
00576         /* ensure sa is monotonically increasing, so npn doesn't reset low */
00577         /* disabled, otherwise nested loops are missed
00578         if (sa > sa_max)
00579             sa_max = sa;
00580         if (sa < sa_max)
00581             break;
00582         */
00583         G_debug(4, "sa: %d", sa);
00584 
00585         /* remove loop if in buffer */
00586         if ((sb - sa) == 1) {   /* neighbouring lines overlap */
00587             j = sb + 1;
00588             npn = sa + 1;
00589             /* first = sa; */
00590         }
00591         else {
00592             double area_size;
00593             int in_buffer = 0;
00594 
00595             Vect_reset_line(sPoints);
00596             dig_find_intersection(x[sa], y[sa], x[sa + 1], y[sa + 1], x[sb],
00597                                   y[sb], x[sb + 1], y[sb + 1], &ix, &iy);
00598 
00599             if (ix == x[0] && iy == y[0] && ix == x[np - 1] && iy == y[np - 1])
00600                 break;
00601 
00602             Vect_append_point(sPoints, ix, iy, 0);
00603             for (i = sa + 1; i < sb + 1; i++) { /* create loop polygon */
00604                 Vect_append_point(sPoints, x[i], y[i], 0);
00605             }
00606             /* close polygon */
00607             Vect_append_point(sPoints, ix, iy, 0);
00608             Vect_line_prune(sPoints);
00609 
00610             /* is loop in buffer ? */
00611             dig_find_area_poly(sPoints, &area_size);
00612             in_buffer = area_size * side < 0;
00613             if (!in_buffer && area_size) {
00614                 /* Vect_find_poly_centroid() may produce coords outside polygon */
00615                 Vect_find_poly_centroid(sPoints, &px, &py);
00616                 in_buffer = point_in_buf(origPoints, px, py, d2) != 0;
00617             }
00618 
00619             if (in_buffer) {
00620                 npn = sa + 1;
00621                 /* do not create zero-length segments */
00622                 if (ix != x[sa] && iy != y[sa]) {
00623                     x[npn] = ix;
00624                     y[npn] = iy;
00625                     npn++;
00626                 }
00627                 if (ix != x[sb + 1] && iy != y[sb + 1]) {
00628                     j = sb + 1;
00629                 }
00630                 else {
00631                     j = sb + 2;
00632                 }
00633                 
00634                 /* lcount == 0 can not happen
00635                 if (lcount == 0) {
00636                     first = sa;
00637                 }
00638                 */
00639             }
00640             else {              /* loop is not in buffer */
00641                 first = sb;
00642                 continue;
00643             }
00644         }
00645 
00646         for (i = j; i < Points->n_points; i++) {        /* move points down */
00647             x[npn] = x[i];
00648             y[npn] = y[i];
00649             npn++;
00650         }
00651         Points->n_points = np = npn;
00652     }
00653 
00654     Vect_line_prune(Points);
00655 
00656     /* looped input line ? */
00657     if (origPoints->x[0] == origPoints->x[origPoints->n_points - 1] &&
00658         origPoints->y[0] == origPoints->y[origPoints->n_points - 1] &&
00659         close_loop) {
00660         double tx, ty, vx, vy, ux, uy, wx, wy, a, aw, av;
00661         int sa, sb, side;
00662         double atol, atol2;
00663         double *xorg, *yorg;
00664         
00665         atol = 2 * acos(1 - tol / fabs(d));
00666 
00667         side = (int)(d / fabs(d));
00668 
00669         xorg = origPoints->x;
00670         yorg = origPoints->y;
00671 
00672         np = origPoints->n_points;
00673 
00674         /* last segment */
00675         vect(xorg[np - 2], yorg[np - 2], xorg[np - 1], yorg[np - 1], &tx, &ty);
00676         vx = ty * d;
00677         vy = -tx * d;
00678         /* first segment */
00679         vect(xorg[0], yorg[0], xorg[1], yorg[1], &ux, &uy);
00680         wx = uy * d;
00681         wy = -ux * d;
00682         av = atan2(vy, vx);
00683         aw = atan2(wy, wx);
00684         a = (aw - av) * side;
00685         if (a < 0)
00686             a += 2 * PI;
00687             
00688         if (a > PI) {
00689             G_debug(5, "search intersection");
00690             /* there can be two intersections !!! */
00691             /* a > PI is probably handled by remove ends above */
00692             if (find_cross_reverse(Points, 0, Points->n_points - 2, 1, Points->n_points - 1, &sa,
00693                 &sb) != 0) {
00694                 G_debug(5, "found intersection");
00695                 dig_find_intersection(x[sa], y[sa], x[sa + 1], y[sa + 1], x[sb],
00696                                       y[sb], x[sb + 1], y[sb + 1], &ix, &iy);
00697                 x[0] = ix;
00698                 y[0] = iy;
00699                 npn = 1;
00700                 /* move points down */
00701                 for (i = sa + 1; i <= sb; i++) {
00702                     x[npn] = x[i];
00703                     y[npn] = y[i];
00704                     npn++;
00705                 }
00706                 Points->n_points = npn;
00707             }
00708         }
00709         /* TODO: a <= PI can probably fail because of representation error */
00710         else {
00711             if (a <= PI && a > atol) {
00712                 int na;
00713                 double nx, ny;
00714 
00715                 /* OK to close parallel line ? */
00716                 na = (int)(a / atol);
00717                 atol2 = a / (na + 1) * side;
00718                 for (j = 0; j < na; j++) {
00719                     av += atol2;
00720                     nx = xorg[0] + fabs(d) * cos(av);
00721                     ny = yorg[0] + fabs(d) * sin(av);
00722                     Vect_append_point(Points, nx, ny, 0);
00723                 }
00724             }
00725         }
00726         /* always close for looped input line */
00727         Vect_append_point(Points, Points->x[0], Points->y[0], 0);
00728 
00729         Vect_line_prune(Points);
00730     }
00731 
00732 }
00733 
00734 /* parallel_line - remove duplicate points from input line and
00735  *  creates new parallel line in 'd' offset distance;
00736  *  'tol' is tolerance between arc and polyline;
00737  *  this function doesn't care about created loops;
00738  *
00739  *  New line is written to existing nPoints structure.
00740  */
00741 static void parallel_line(struct line_pnts *Points, double d, double tol,
00742                           struct line_pnts *nPoints)
00743 {
00744     int i, j, np, na, side;
00745     double *x, *y, nx, ny, tx, ty, vx, vy, ux, uy, wx, wy;
00746     double atol, atol2, a, av, aw;
00747 
00748     G_debug(4, "parallel_line()");
00749 
00750     Vect_reset_line(nPoints);
00751 
00752     np = Points->n_points;
00753     x = Points->x;
00754     y = Points->y;
00755 
00756     if (np == 0)
00757         return;
00758 
00759     if (np == 1) {
00760         Vect_append_point(nPoints, x[0], y[0], 0);      /* ? OK, should make circle for points ? */
00761         return;
00762     }
00763 
00764     if (d == 0) {
00765         Vect_copy_xyz_to_pnts(nPoints, x, y, NULL, np);
00766         return;
00767     }
00768 
00769     side = (int)(d / fabs(d));
00770     atol = 2 * acos(1 - tol / fabs(d));
00771 
00772     for (i = 0; i < np - 1; i++) {
00773         vect(x[i], y[i], x[i + 1], y[i + 1], &tx, &ty);
00774         vx = ty * d;
00775         vy = -tx * d;
00776 
00777         nx = x[i] + vx;
00778         ny = y[i] + vy;
00779         Vect_append_point(nPoints, nx, ny, 0);
00780 
00781         nx = x[i + 1] + vx;
00782         ny = y[i + 1] + vy;
00783         Vect_append_point(nPoints, nx, ny, 0);
00784 
00785         if (i < np - 2) {       /* use polyline instead of arc between line segments */
00786             vect(x[i + 1], y[i + 1], x[i + 2], y[i + 2], &ux, &uy);
00787             wx = uy * d;
00788             wy = -ux * d;
00789             av = atan2(vy, vx);
00790             aw = atan2(wy, wx);
00791             a = (aw - av) * side;
00792             if (a < 0)
00793                 a += 2 * PI;
00794 
00795             /* TODO: a <= PI can probably fail because of representation error */
00796             if (a <= PI && a > atol) {
00797                 na = (int)(a / atol);
00798                 atol2 = a / (na + 1) * side;
00799                 for (j = 0; j < na; j++) {
00800                     av += atol2;
00801                     nx = x[i + 1] + fabs(d) * cos(av);
00802                     ny = y[i + 1] + fabs(d) * sin(av);
00803                     Vect_append_point(nPoints, nx, ny, 0);
00804                 }
00805             }
00806         }
00807     }
00808     Vect_line_prune(nPoints);
00809 }
00810 
00822 void
00823 Vect_line_parallel(struct line_pnts *InPoints, double distance,
00824                    double tolerance, int rm_end, struct line_pnts *OutPoints)
00825 {
00826     G_debug(4,
00827             "Vect_line_parallel(): npoints = %d, distance = %f, tolerance = %f",
00828             InPoints->n_points, distance, tolerance);
00829 
00830     parallel_line(InPoints, distance, tolerance, OutPoints);
00831     G_debug(4, "%d outpoints", OutPoints->n_points);
00832 
00833     clean_parallel(OutPoints, InPoints, distance, tolerance, rm_end, 1);
00834     G_debug(4, "%d outpoints after cleaning", OutPoints->n_points);
00835 
00836     return;
00837 }
00838 
00850 void
00851 Vect_line_buffer(struct line_pnts *InPoints, double distance,
00852                  double tolerance, struct line_pnts *OutPoints)
00853 {
00854     double dangle;
00855     int side, npoints;
00856     static struct line_pnts *Points = NULL;
00857     static struct line_pnts *PPoints = NULL;
00858     double d2 = distance * distance * D_MULT;
00859 
00860     distance = fabs(distance);
00861 
00862     dangle = 2 * acos(1 - tolerance / fabs(distance));  /* angle step */
00863 
00864     if (Points == NULL)
00865         Points = Vect_new_line_struct();
00866 
00867     if (PPoints == NULL)
00868         PPoints = Vect_new_line_struct();
00869 
00870     /* Copy input */
00871     Vect_reset_line(Points);
00872     Vect_append_points(Points, InPoints, GV_FORWARD);
00873 
00874     Vect_reset_line(OutPoints);
00875 
00876     npoints = Points->n_points;
00877     if (npoints <= 0) {
00878         return;
00879     }
00880     else if (npoints == 1) {    /* make a circle */
00881         double angle, x, y;
00882 
00883         for (angle = 0; angle < 2 * PI; angle += dangle) {
00884             x = Points->x[0] + distance * cos(angle);
00885             y = Points->y[0] + distance * sin(angle);
00886             Vect_append_point(OutPoints, x, y, 0);
00887         }
00888         /* Close polygon */
00889         Vect_append_point(OutPoints, OutPoints->x[0], OutPoints->y[0], 0);
00890     }
00891     else {                      /* 2 and more points */
00892         for (side = 0; side < 2; side++) {
00893             double angle, sangle;
00894             double lx1, ly1, lx2, ly2;
00895             double x, y, nx, ny, sx, sy, ex, ey;
00896 
00897             /* Parallel on one side */
00898             if (side == 0) {
00899                 /* Vect_line_parallel(Points, distance, tolerance, 0, PPoints); */
00900 
00901                 /* just create parallel line, clean later */
00902                 parallel_line(InPoints, distance, tolerance, PPoints);
00903                 G_debug(4, "%d outpoints", PPoints->n_points);
00904 
00905                 Vect_append_points(OutPoints, PPoints, GV_FORWARD);
00906             }
00907             else {
00908                 /* Vect_line_parallel(Points, -distance, tolerance, 0, PPoints); */
00909 
00910                 parallel_line(InPoints, -distance, tolerance, PPoints);
00911                 G_debug(4, "%d outpoints", PPoints->n_points);
00912 
00913                 Vect_append_points(OutPoints, PPoints, GV_BACKWARD);
00914             }
00915 
00916             /* Arc at the end */
00917             /* 2 points at the end of original line */
00918             if (side == 0) {
00919                 lx1 = Points->x[npoints - 2];
00920                 ly1 = Points->y[npoints - 2];
00921                 lx2 = Points->x[npoints - 1];
00922                 ly2 = Points->y[npoints - 1];
00923             }
00924             else {
00925                 lx1 = Points->x[1];
00926                 ly1 = Points->y[1];
00927                 lx2 = Points->x[0];
00928                 ly2 = Points->y[0];
00929             }
00930 
00931             /* normalized vector */
00932             vect(lx1, ly1, lx2, ly2, &nx, &ny);
00933 
00934             /* starting point */
00935             sangle = atan2(-nx, ny);    /* starting angle */
00936             sx = lx2 + ny * distance;
00937             sy = ly2 - nx * distance;
00938 
00939             /* end point */
00940             ex = lx2 - ny * distance;
00941             ey = ly2 + nx * distance;
00942 
00943             Vect_append_point(OutPoints, sx, sy, 0);
00944 
00945             /* arc */
00946             for (angle = dangle; angle < PI; angle += dangle) {
00947                 x = lx2 + distance * cos(sangle + angle);
00948                 y = ly2 + distance * sin(sangle + angle);
00949                 
00950                 Vect_append_point(OutPoints, x, y, 0);
00951             }
00952 
00953             Vect_append_point(OutPoints, ex, ey, 0);
00954         }
00955 
00956         /* clean up arcs at the end of input lines */
00957         Vect_line_prune(OutPoints);
00958         if (OutPoints->x[0] == OutPoints->x[OutPoints->n_points - 1] &&
00959             OutPoints->y[0] == OutPoints->y[OutPoints->n_points - 1])
00960             OutPoints->n_points--;
00961 
00962         clean_parallel(OutPoints, InPoints, distance, tolerance, 2, 0);
00963 
00964         /* Close polygon */
00965         if (OutPoints->x[0] != OutPoints->x[OutPoints->n_points - 1] ||
00966             OutPoints->y[0] != OutPoints->y[OutPoints->n_points - 1]) {
00967 
00968             if (point_in_buf(InPoints, OutPoints->x[0], OutPoints->y[0], d2)) {
00969                 OutPoints->x[0] = OutPoints->x[OutPoints->n_points - 1];
00970                 OutPoints->y[0] = OutPoints->y[OutPoints->n_points - 1];
00971             }
00972             else if (point_in_buf(InPoints, OutPoints->x[OutPoints->n_points - 1],
00973                                   OutPoints->y[OutPoints->n_points - 1], d2)) {
00974                 OutPoints->x[OutPoints->n_points - 1] = OutPoints->x[0];
00975                 OutPoints->y[OutPoints->n_points - 1] = OutPoints->y[0];
00976             }
00977             else
00978                 Vect_append_point(OutPoints, OutPoints->x[0], OutPoints->y[0], 0);
00979         }
00980     }
00981 
00982     return;
00983 }