GRASS 8 Programmer's Manual 8.6.0dev(2026)-c83afef6d3
Loading...
Searching...
No Matches
buffer.c
Go to the documentation of this file.
1/*!
2 \file lib/vector/Vlib/buffer.c
3
4 \brief Vector library - nearest, adjust, parallel lines
5
6 Higher level functions for reading/writing/manipulating vectors.
7
8 See buffer2.c for replacement.
9
10 SPDX-FileCopyrightText: 2001-2009 GRASS Development Team
11 SPDX-License-Identifier: GPL-2.0-or-later
12
13 \author Radim Blazek
14 */
15
16#include <stdlib.h>
17#include <math.h>
18#include <grass/vector.h>
19
20#define LENGTH(DX, DY) (sqrt((DX * DX) + (DY * DY)))
21#define PI M_PI
22
23/* vector() calculates normalized vector form two points */
24static void vect(double x1, double y1, double x2, double y2, double *x,
25 double *y)
26{
27 double dx, dy, l;
28
29 dx = x2 - x1;
30 dy = y2 - y1;
31 l = LENGTH(dx, dy);
32 if (l == 0) {
33 /* assume that dx == dy == 0, which should give (NaN,NaN) */
34 /* without this, very small dx or dy could result in Infinity */
35 dx = dy = 0;
36 }
37 *x = dx / l;
38 *y = dy / l;
39}
40
41/* find_cross find first crossing between segments from s1 to s2 and from s3 to
42 *s4
43 ** s5 is set to first segment and s6 to second
44 ** neighbours are taken as crossing each other only if overlap
45 ** returns: 1 found
46 ** -1 found overlap
47 ** 0 not found
48 */
49static int find_cross(struct line_pnts *Points, int s1, int s2, int s3, int s4,
50 int *s5, int *s6)
51{
52 int i, j, ret;
53 double *x, *y;
54
55 G_debug(5, "find_cross(): npoints = %d, s1 = %d, s2 = %d, s3 = %d, s4 = %d",
56 Points->n_points, s1, s2, s3, s4);
57
58 x = Points->x;
59 y = Points->y;
60
61 for (i = s1; i <= s2; i++) {
62 for (j = s3; j <= s4; j++) {
63 if (j == i) {
64 continue;
65 }
66 ret = dig_test_for_intersection(x[i], y[i], x[i + 1], y[i + 1],
67 x[j], y[j], x[j + 1], y[j + 1]);
68 if (ret == 1 && ((i - j) > 1 || (i - j) < -1)) {
69 *s5 = i;
70 *s6 = j;
71 G_debug(5, " intersection: s5 = %d, s6 = %d", *s5, *s6);
72 return 1;
73 }
74 if (ret == -1) {
75 *s5 = i;
76 *s6 = j;
77 G_debug(5, " overlap: s5 = %d, s6 = %d", *s5, *s6);
78 return -1;
79 }
80 }
81 }
82 G_debug(5, " no intersection");
83 return 0;
84}
85
86/* point_in_buf - test if point px,py is in d buffer of Points
87 ** returns: 1 in buffer
88 ** 0 not in buffer
89 */
90static int point_in_buf(struct line_pnts *Points, double px, double py,
91 double d)
92{
93 int i, np;
94 double sd;
95
96 np = Points->n_points;
97 d *= d;
98 for (i = 0; i < np - 1; i++) {
99 sd = dig_distance2_point_to_line(px, py, 0, Points->x[i], Points->y[i],
100 0, Points->x[i + 1], Points->y[i + 1],
101 0, 0, NULL, NULL, NULL, NULL, NULL);
102 if (sd <= d) {
103 return 1;
104 }
105 }
106 return 0;
107}
108
109/* clean_parallel - clean parallel line created by parallel_line:
110 ** - looking for loops and if loop doesn't contain any other loop
111 ** and centroid of loop is in buffer removes this loop (repeated)
112 ** - optionally removes all end points in buffer
113 * parameters:
114 * Points - parallel line
115 * origPoints - original line
116 * d - offset
117 * rm_end - remove end points in buffer
118 ** note1: on some lines (multiply selfcrossing; lines with end points
119 ** in buffer of line other; some shapes of ends ) may create nosense
120 ** note2: this function is stupid and slow, somebody more clever
121 ** than I am should write paralle_line + clean_parallel
122 ** better; RB March 2000
123 */
124static void clean_parallel(struct line_pnts *Points,
125 struct line_pnts *origPoints, double d, int rm_end)
126{
127 int i, j, np, npn, sa, sb;
128 int sa_max = 0;
129 int first = 0, current, last, lcount;
130 double *x, *y, px, py, ix, iy;
131 static struct line_pnts *sPoints = NULL;
132
133 G_debug(4, "clean_parallel(): npoints = %d, d = %f, rm_end = %d",
134 Points->n_points, d, rm_end);
135
136 x = Points->x;
137 y = Points->y;
138 np = Points->n_points;
139
140 if (sPoints == NULL)
142
144
145 npn = 1;
146
147 /* remove loops */
148 while (first < np - 2) {
149 /* find first loop which doesn't contain any other loop */
150 current = first;
151 last = Points->n_points - 2;
152 lcount = 0;
153 while (find_cross(Points, current, last - 1, current + 1, last, &sa,
154 &sb) != 0) {
155 if (lcount == 0) {
156 first = sa;
157 } /* move first forward */
158
159 current = sa + 1;
160 last = sb;
161 lcount++;
162 G_debug(5, " current = %d, last = %d, lcount = %d", current, last,
163 lcount);
164 }
165 if (lcount == 0) {
166 break;
167 } /* loop not found */
168
169 /* ensure sa is monotonically increasing, so npn doesn't reset low */
170 if (sa > sa_max)
171 sa_max = sa;
172 if (sa < sa_max)
173 break;
174
175 /* remove loop if in buffer */
176 if ((sb - sa) == 1) { /* neighbouring lines overlap */
177 j = sb + 1;
178 npn = sa + 1;
179 }
180 else {
182 dig_find_intersection(x[sa], y[sa], x[sa + 1], y[sa + 1], x[sb],
183 y[sb], x[sb + 1], y[sb + 1], &ix, &iy);
185 for (i = sa + 1; i < sb + 1; i++) { /* create loop polygon */
186 Vect_append_point(sPoints, x[i], y[i], 0);
187 }
189 if (point_in_buf(origPoints, px, py, d)) { /* is loop in buffer ? */
190 npn = sa + 1;
191 x[npn] = ix;
192 y[npn] = iy;
193 j = sb + 1;
194 npn++;
195 if (lcount == 0) {
196 first = sb;
197 }
198 }
199 else { /* loop is not in buffer */
200 first = sb;
201 continue;
202 }
203 }
204
205 for (i = j; i < Points->n_points; i++) { /* move points down */
206 x[npn] = x[i];
207 y[npn] = y[i];
208 npn++;
209 }
210 Points->n_points = npn;
211 }
212
213 if (rm_end) {
214 /* remove points from start in buffer */
215 j = 0;
216 for (i = 0; i < Points->n_points - 1; i++) {
217 px = (x[i] + x[i + 1]) / 2;
218 py = (y[i] + y[i + 1]) / 2;
219 if (point_in_buf(origPoints, x[i], y[i], d * 0.9999) &&
220 point_in_buf(origPoints, px, py, d * 0.9999)) {
221 j++;
222 }
223 else {
224 break;
225 }
226 }
227 if (j > 0) {
228 npn = 0;
229 for (i = j; i < Points->n_points; i++) {
230 x[npn] = x[i];
231 y[npn] = y[i];
232 npn++;
233 }
234 Points->n_points = npn;
235 }
236 /* remove points from end in buffer */
237 j = 0;
238 for (i = Points->n_points - 1; i >= 1; i--) {
239 px = (x[i] + x[i - 1]) / 2;
240 py = (y[i] + y[i - 1]) / 2;
241 if (point_in_buf(origPoints, x[i], y[i], d * 0.9999) &&
242 point_in_buf(origPoints, px, py, d * 0.9999)) {
243 j++;
244 }
245 else {
246 break;
247 }
248 }
249 if (j > 0) {
250 Points->n_points -= j;
251 }
252 }
253}
254
255/* parallel_line - remove duplicate points from input line and
256 * creates new parallel line in 'd' offset distance;
257 * 'tol' is tolerance between arc and polyline;
258 * this function doesn't care about created loops;
259 *
260 * New line is written to existing nPoints structure.
261 */
262static void parallel_line(struct line_pnts *Points, double d, double tol,
263 struct line_pnts *nPoints)
264{
265 int i, j, np, na, side;
266 double *x, *y, nx, ny, tx, ty, vx, vy, ux, uy, wx, wy;
267 double atol, atol2, a, av, aw;
268
269 G_debug(4, "parallel_line()");
270
272
273 Vect_line_prune(Points);
274 np = Points->n_points;
275 x = Points->x;
276 y = Points->y;
277
278 if (np == 0)
279 return;
280
281 if (np == 1) {
283 0); /* ? OK, should make circle for points ? */
284 return;
285 }
286
287 if (d == 0) {
289 return;
290 }
291
292 side = (int)(d / fabs(d));
293 atol = 2 * acos(1 - tol / fabs(d));
294
295 for (i = 0; i < np - 1; i++) {
296 vect(x[i], y[i], x[i + 1], y[i + 1], &tx, &ty);
297 vx = ty * d;
298 vy = -tx * d;
299
300 nx = x[i] + vx;
301 ny = y[i] + vy;
302 Vect_append_point(nPoints, nx, ny, 0);
303
304 nx = x[i + 1] + vx;
305 ny = y[i + 1] + vy;
306 Vect_append_point(nPoints, nx, ny, 0);
307
308 if (i <
309 np - 2) { /* use polyline instead of arc between line segments */
310 vect(x[i + 1], y[i + 1], x[i + 2], y[i + 2], &ux, &uy);
311 wx = uy * d;
312 wy = -ux * d;
313 av = atan2(vy, vx);
314 aw = atan2(wy, wx);
315 a = (aw - av) * side;
316 if (a < 0)
317 a += 2 * PI;
318
319 /* TODO: a <= PI can probably fail because of representation error
320 */
321 if (a <= PI && a > atol) {
322 na = (int)(a / atol);
323 atol2 = a / (na + 1) * side;
324 for (j = 0; j < na; j++) {
325 av += atol2;
326 nx = x[i + 1] + fabs(d) * cos(av);
327 ny = y[i + 1] + fabs(d) * sin(av);
328 Vect_append_point(nPoints, nx, ny, 0);
329 }
330 }
331 }
332 }
334}
335
336/*!
337 \brief Create parallel line
338
339 This function is replaced by Vect_line_parallel2().
340
341 \param InPoints input line
342 \param distance create parallel line in distance
343 \param tolerance maximum distance between theoretical arc and polygon
344 segments \param rm_end remove end points falling into distance \param[out]
345 OutPoints output line
346
347 \return
348 */
349void Vect_line_parallel(struct line_pnts *InPoints, double distance,
350 double tolerance, int rm_end,
351 struct line_pnts *OutPoints)
352{
353 G_debug(4,
354 "Vect_line_parallel(): npoints = %d, distance = %f, tolerance = %f",
355 InPoints->n_points, distance, tolerance);
356
357 parallel_line(InPoints, distance, tolerance, OutPoints);
358
359 clean_parallel(OutPoints, InPoints, distance, rm_end);
360
361 return;
362}
363
364/*!
365 \brief Create buffer around the line line.
366
367 This function is replaced by Vect_line_buffer().
368
369 Buffer is closed counter clockwise polygon. Warning: output line
370 may contain loops!
371
372 \param InPoints input line
373 \param distance create buffer in distance
374 \param tolerance maximum distance between theoretical arc and polygon
375 segments \param[out] OutPoints output line
376 */
377void Vect_line_buffer(const struct line_pnts *InPoints, double distance,
378 double tolerance, struct line_pnts *OutPoints)
379{
380 double dangle;
381 int side, npoints;
382 static struct line_pnts *Points = NULL;
383 static struct line_pnts *PPoints = NULL;
384
385 distance = fabs(distance);
386
387 dangle = 2 * acos(1 - tolerance / fabs(distance)); /* angle step */
388
389 if (Points == NULL)
390 Points = Vect_new_line_struct();
391
392 if (PPoints == NULL)
394
395 /* Copy and prune input */
396 Vect_reset_line(Points);
398 Vect_line_prune(Points);
399
401
402 npoints = Points->n_points;
403 if (npoints <= 0) {
404 return;
405 }
406 else if (npoints == 1) { /* make a circle */
407 double angle, x, y;
408
409 for (angle = 0; angle < 2 * PI; angle += dangle) {
410 x = Points->x[0] + distance * cos(angle);
411 y = Points->y[0] + distance * sin(angle);
413 }
414 /* Close polygon */
416 }
417 else { /* 2 and more points */
418 for (side = 0; side < 2; side++) {
419 double angle, sangle;
420 double lx1, ly1, lx2, ly2;
421 double x, y, nx, ny, sx, sy, ex, ey;
422
423 /* Parallel on one side */
424 if (side == 0) {
425 Vect_line_parallel(Points, distance, tolerance, 0, PPoints);
427 }
428 else {
429 Vect_line_parallel(Points, -distance, tolerance, 0, PPoints);
431 }
432
433 /* Arc at the end */
434 /* 2 points at theend of original line */
435 if (side == 0) {
436 lx1 = Points->x[npoints - 2];
437 ly1 = Points->y[npoints - 2];
438 lx2 = Points->x[npoints - 1];
439 ly2 = Points->y[npoints - 1];
440 }
441 else {
442 lx1 = Points->x[1];
443 ly1 = Points->y[1];
444 lx2 = Points->x[0];
445 ly2 = Points->y[0];
446 }
447
448 /* normalized vector */
449 vect(lx1, ly1, lx2, ly2, &nx, &ny);
450
451 /* starting point */
452 sangle = atan2(-nx, ny); /* starting angle */
453 sx = lx2 + ny * distance;
454 sy = ly2 - nx * distance;
455
456 /* end point */
457 ex = lx2 - ny * distance;
458 ey = ly2 + nx * distance;
459
460 Vect_append_point(OutPoints, sx, sy, 0);
461
462 /* arc */
463 for (angle = dangle; angle < PI; angle += dangle) {
464 x = lx2 + distance * cos(sangle + angle);
465 y = ly2 + distance * sin(sangle + angle);
467 }
468
470 }
471
472 /* Close polygon */
474 }
476
477 return;
478}
#define LENGTH(DX, DY)
Definition buffer.c:20
void Vect_line_parallel(struct line_pnts *InPoints, double distance, double tolerance, int rm_end, struct line_pnts *OutPoints)
Create parallel line.
Definition buffer.c:349
#define PI
Definition buffer.c:21
void Vect_line_buffer(const struct line_pnts *InPoints, double distance, double tolerance, struct line_pnts *OutPoints)
Create buffer around the line line.
Definition buffer.c:377
#define NULL
Definition ccmath.h:32
int G_debug(int, const char *,...) __attribute__((format(printf
int Vect_find_poly_centroid(const struct line_pnts *, double *, double *)
Get centroid of polygon.
Definition Vlib/poly.c:383
int Vect_copy_xyz_to_pnts(struct line_pnts *, const double *, const double *, const double *, int)
Copy points from array to line_pnts structure.
Definition line.c:97
void Vect_reset_line(struct line_pnts *)
Reset line.
Definition line.c:127
int Vect_line_prune(struct line_pnts *)
Remove duplicate points, i.e. zero length segments.
Definition line.c:277
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition line.c:43
int Vect_append_point(struct line_pnts *, double, double, double)
Appends one point to the end of a line.
Definition line.c:146
int Vect_append_points(struct line_pnts *, const struct line_pnts *, int)
Appends points to the end of a line.
Definition line.c:333
#define GV_FORWARD
Line direction indicator forward/backward.
#define GV_BACKWARD
double dig_distance2_point_to_line(double, double, double, double, double, double, double, double, double, int, double *, double *, double *, double *, int *)
int dig_find_intersection(double, double, double, double, double, double, double, double, double *, double *)
Definition linecros.c:178
int dig_test_for_intersection(double, double, double, double, double, double, double, double)
Definition linecros.c:54
double l
Definition r_raster.c:37
Feature geometry info - coordinates.
double * y
Array of Y coordinates.
double * x
Array of X coordinates.
int n_points
Number of points.
#define x