|
GRASS Programmer's Manual
6.5.svn(2012)-r51648
|
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 }