GRASS 8 Programmer's Manual 8.6.0dev(2026)-4bb960b182
Loading...
Searching...
No Matches
buffer2.c
Go to the documentation of this file.
1/*!
2 \file lib/vector/Vlib/buffer2.c
3
4 \brief Vector library - nearest, adjust, parallel lines
5
6 Higher level functions for reading/writing/manipulating vectors.
7
8 SPDX-FileCopyrightText: 2001-2009 GRASS Development Team
9 SPDX-License-Identifier: GPL-2.0-or-later
10
11 \author Original author Radim Blazek (see buffer.c)
12 \author Rewritten by Rosen Matev (Google Summer of Code 2008)
13 */
14
15#include <stdlib.h>
16#include <math.h>
17#include <grass/gis.h>
18#include <grass/vector.h>
19#include <grass/glocale.h>
20
21#include "dgraph.h"
22
23#define LENGTH(DX, DY) (sqrt((DX * DX) + (DY * DY)))
24#define PI M_PI
25#define RIGHT_SIDE 1
26#define LEFT_SIDE -1
27#define LOOPED_LINE 1
28#define NON_LOOPED_LINE 0
29
30/* norm_vector() calculates normalized vector form two points */
31static void norm_vector(double x1, double y1, double x2, double y2, double *x,
32 double *y)
33{
34 double dx, dy, l;
35
36 dx = x2 - x1;
37 dy = y2 - y1;
38 if ((dx == 0) && (dy == 0)) {
39 /* assume that dx == dy == 0, which should give (NaN,NaN) */
40 /* without this, very small dx or dy could result in Infinity */
41 *x = 0;
42 *y = 0;
43 return;
44 }
45 l = LENGTH(dx, dy);
46 *x = dx / l;
47 *y = dy / l;
48
49 return;
50}
51
52static void rotate_vector(double x, double y, double cosa, double sina,
53 double *nx, double *ny)
54{
55 *nx = x * cosa - y * sina;
56 *ny = x * sina + y * cosa;
57
58 return;
59}
60
61/*
62 * (x,y) should be normalized vector for common transforms; This func transforms
63 * (x,y) to a vector corresponding to da, db, dalpha params dalpha is in radians
64 */
65static void elliptic_transform(double x, double y, double da, double db,
66 double dalpha, double *nx, double *ny)
67{
68 double cosa = cos(dalpha);
69 double sina = sin(dalpha);
70
71 /* double cc = cosa*cosa;
72 double ss = sina*sina;
73 double t = (da-db)*sina*cosa;
74
75 *nx = (da*cc + db*ss)*x + t*y;
76 *ny = (da*ss + db*cc)*y + t*x;
77 return; */
78
79 double va, vb;
80
81 va = (x * cosa + y * sina) * da;
82 vb = (x * (-sina) + y * cosa) * db;
83 *nx = va * cosa + vb * (-sina);
84 *ny = va * sina + vb * cosa;
85
86 return;
87}
88
89/*
90 * vect(x,y) must be normalized
91 * gives the tangent point of the tangent to ellpise(da,db,dalpha) parallel to
92 * vect(x,y) dalpha is in radians ellipse center is in (0,0)
93 */
94static void elliptic_tangent(double x, double y, double da, double db,
95 double dalpha, double *px, double *py)
96{
97 double cosa = cos(dalpha);
98 double sina = sin(dalpha);
99 double u, v, len;
100
101 /* rotate (x,y) -dalpha radians */
102 rotate_vector(x, y, cosa, -sina, &x, &y);
103 /*u = (x + da*y/db)/2;
104 v = (y - db*x/da)/2; */
105 u = da * da * y;
106 v = -db * db * x;
107 len = da * db / sqrt(da * da * v * v + db * db * u * u);
108 u *= len;
109 v *= len;
110 rotate_vector(u, v, cosa, sina, px, py);
111
112 return;
113}
114
115/*
116 * !!! This is not line in GRASS' sense. See
117 * https://en.wikipedia.org/wiki/Line_%28mathematics%29
118 */
119static void line_coefficients(double x1, double y1, double x2, double y2,
120 double *a, double *b, double *c)
121{
122 *a = y2 - y1;
123 *b = x1 - x2;
124 *c = x2 * y1 - x1 * y2;
125
126 return;
127}
128
129/*
130 * Finds intersection of two straight lines. Returns 0 if the lines are
131 * parallel, 1 if they cross, 2 if they are the same line.
132 * !!!!!!!!!!!!!!!! FIX THIS TOLERANCE CONSTANTS BAD (and UGLY) CODE !!!!!!!!!
133 */
134static int line_intersection(double a1, double b1, double c1, double a2,
135 double b2, double c2, double *x, double *y)
136{
137 double d;
138
139 if (fabs(a2 * b1 - a1 * b2) == 0) {
140 if (fabs(a2 * c1 - a1 * c2) == 0)
141 return 2;
142 else
143 return 0;
144 }
145 else {
146 d = a1 * b2 - a2 * b1;
147 *x = (b1 * c2 - b2 * c1) / d;
148 *y = (c1 * a2 - c2 * a1) / d;
149 return 1;
150 }
151}
152
153static double angular_tolerance(double tol, double da, double db)
154{
155 double a = MAX(da, db);
156
157 if (tol > a)
158 tol = a;
159
160 return 2 * acos(1 - tol / a);
161}
162
163/*
164 * This function generates parallel line (with loops, but not like the old
165 * ones). It is not to be used directly for creating buffers.
166 * + added elliptical buffers/par.lines support
167 *
168 * dalpha - direction of elliptical buffer major axis in degrees
169 * da - distance along major axis
170 * db: distance along minor (perp.) axis
171 * side: side >= 0 - right side, side < 0 - left side
172 * when (da == db) we have plain distances (old case)
173 * round - 1 for round corners, 0 for sharp corners. (tol is used only if round
174 * == 1)
175 */
176static void parallel_line(struct line_pnts *Points, double da, double db,
177 double dalpha, int side, int round, int caps,
178 int looped, double tol, struct line_pnts *nPoints)
179{
180 int i, j, res, np;
181 double *x, *y;
182 double tx, ty, vx, vy, wx, wy, nx, ny, mx, my, rx, ry;
183 double vx1, vy1, wx1, wy1;
184 double a0, b0, c0, a1, b1, c1;
185 double phi1, phi2, delta_phi;
188 vx = 0.0;
189 c1 = 0.0;
190 vy = 0.0;
191 b1 = 0.0;
192 a1 = 0.0;
193
194 G_debug(3, "parallel_line()");
195
196 if (looped && 0) {
197 /* start point != end point */
198 return;
199 }
200
202
203 if (looped) {
204 Vect_append_point(Points, Points->x[1], Points->y[1], Points->z[1]);
205 }
206 np = Points->n_points;
207 x = Points->x;
208 y = Points->y;
209
210 if ((np == 0) || (np == 1))
211 return;
212
213 if ((da == 0) || (db == 0)) {
215 return;
216 }
217
218 side = (side >= 0) ? (1) : (-1); /* normalize variable */
219 dalpha *= PI / 180; /* convert dalpha from degrees to radians */
220 angular_tol = angular_tolerance(tol, da, db);
221
222 for (i = 0; i < np - 1; i++) {
223 /* save the old values */
224 a0 = a1;
225 b0 = b1;
226 c0 = c1;
227 wx = vx;
228 wy = vy;
229
230 norm_vector(x[i], y[i], x[i + 1], y[i + 1], &tx, &ty);
231 if ((tx == 0) && (ty == 0))
232 continue;
233
234 elliptic_tangent(side * tx, side * ty, da, db, dalpha, &vx, &vy);
235
236 nx = x[i] + vx;
237 ny = y[i] + vy;
238
239 mx = x[i + 1] + vx;
240 my = y[i + 1] + vy;
241
242 line_coefficients(nx, ny, mx, my, &a1, &b1, &c1);
243
244 if (i == 0) {
245 if (!looped)
246 Vect_append_point(nPoints, nx, ny, 0);
247 continue;
248 }
249
250 delta_phi = atan2(ty, tx) - atan2(y[i] - y[i - 1], x[i] - x[i - 1]);
251 if (delta_phi > PI)
252 delta_phi -= 2 * PI;
253 else if (delta_phi <= -PI)
254 delta_phi += 2 * PI;
255 /* now delta_phi is in [-pi;pi] */
256 turns360 = (fabs(fabs(delta_phi) - PI) < 1e-15);
257 inner_corner = (side * delta_phi <= 0) && (!turns360);
258
259 if ((turns360) && (!(caps && round))) {
260 if (caps) {
261 norm_vector(0, 0, vx, vy, &tx, &ty);
262 elliptic_tangent(side * tx, side * ty, da, db, dalpha, &tx,
263 &ty);
264 }
265 else {
266 tx = 0;
267 ty = 0;
268 }
269 Vect_append_point(nPoints, x[i] + wx + tx, y[i] + wy + ty, 0);
270 Vect_append_point(nPoints, nx + tx, ny + ty,
271 0); /* nx == x[i] + vx, ny == y[i] + vy */
272 }
273 else if ((!round) || inner_corner) {
274 res = line_intersection(a0, b0, c0, a1, b1, c1, &rx, &ry);
275 /* if (res == 0) {
276 G_debug(4, "a0=%.18f, b0=%.18f, c0=%.18f, a1=%.18f, b1=%.18f,
277 c1=%.18f", a0, b0, c0, a1, b1, c1); G_fatal_error("Two
278 consecutive line segments are parallel, but not on one straight
279 line! This should never happen."); return;
280 } */
281 if (res == 1) {
282 if (!round)
284 else {
285 /* d = dig_distance2_point_to_line(rx,
286 ry, 0, x[i-1], y[i-1], 0, x[i], y[i], 0, 0, NULL, NULL,
287 NULL, NULL, NULL); if ( */
289 }
290 }
291 }
292 else {
293 /* we should draw elliptical arc for outside corner */
294
295 /* inverse transforms */
296 elliptic_transform(wx, wy, 1 / da, 1 / db, dalpha, &wx1, &wy1);
297 elliptic_transform(vx, vy, 1 / da, 1 / db, dalpha, &vx1, &vy1);
298
299 phi1 = atan2(wy1, wx1);
300 phi2 = atan2(vy1, vx1);
301 delta_phi = side * (phi2 - phi1);
302
303 /* make delta_phi in [0, 2pi] */
304 if (delta_phi < 0)
305 delta_phi += 2 * PI;
306
309
310 for (j = 0; j <= nsegments; j++) {
311 elliptic_transform(cos(phi1), sin(phi1), da, db, dalpha, &tx,
312 &ty);
313 Vect_append_point(nPoints, x[i] + tx, y[i] + ty, 0);
315 }
316 }
317
318 if ((!looped) && (i == np - 2)) {
320 }
321 }
322
323 if (looped) {
324 Vect_append_point(nPoints, nPoints->x[0], nPoints->y[0], nPoints->z[0]);
325 }
326
328
329 if (looped) {
330 Vect_line_delete_point(Points, Points->n_points - 1);
331 }
332}
333
334/* input line must be looped */
335static void convolution_line(struct line_pnts *Points, double da, double db,
336 double dalpha, int side, int round, int caps,
337 double tol, struct line_pnts *nPoints)
338{
339 int i, j, res, np;
340 double *x, *y;
341 double tx, ty, vx, vy, wx, wy, nx, ny, mx, my, rx, ry;
342 double vx1, vy1, wx1, wy1;
343 double a0, b0, c0, a1, b1, c1;
344 double phi1, phi2, delta_phi;
346 double angle0, angle1;
348
349 G_debug(3, "convolution_line() side = %d", side);
350
351 np = Points->n_points;
352 x = Points->x;
353 y = Points->y;
354 if ((np == 0) || (np == 1))
355 return;
356 if ((x[0] != x[np - 1]) || (y[0] != y[np - 1])) {
357 G_fatal_error(_("Line is not looped"));
358 return;
359 }
360
362
363 if ((da == 0) || (db == 0)) {
365 return;
366 }
367
368 side = (side >= 0) ? (1) : (-1); /* normalize variable */
369 dalpha *= PI / 180; /* convert dalpha from degrees to radians */
370 angular_tol = angular_tolerance(tol, da, db);
371
372 i = np - 2;
373 norm_vector(x[i], y[i], x[i + 1], y[i + 1], &tx, &ty);
374 elliptic_tangent(side * tx, side * ty, da, db, dalpha, &vx, &vy);
375 angle1 = atan2(ty, tx);
376 nx = x[i] + vx;
377 ny = y[i] + vy;
378 mx = x[i + 1] + vx;
379 my = y[i + 1] + vy;
380 if (!round)
381 line_coefficients(nx, ny, mx, my, &a1, &b1, &c1);
382
383 for (i = 0; i <= np - 2; i++) {
384 G_debug(4, "point %d, segment %d-%d", i, i, i + 1);
385 /* save the old values */
386 if (!round) {
387 a0 = a1;
388 b0 = b1;
389 c0 = c1;
390 }
391 wx = vx;
392 wy = vy;
393 angle0 = angle1;
394
395 norm_vector(x[i], y[i], x[i + 1], y[i + 1], &tx, &ty);
396 if ((tx == 0) && (ty == 0))
397 continue;
398 elliptic_tangent(side * tx, side * ty, da, db, dalpha, &vx, &vy);
399 angle1 = atan2(ty, tx);
400 nx = x[i] + vx;
401 ny = y[i] + vy;
402 mx = x[i + 1] + vx;
403 my = y[i + 1] + vy;
404 if (!round)
405 line_coefficients(nx, ny, mx, my, &a1, &b1, &c1);
406
408 if (delta_phi > PI)
409 delta_phi -= 2 * PI;
410 else if (delta_phi <= -PI)
411 delta_phi += 2 * PI;
412 /* now delta_phi is in [-pi;pi] */
413 turns360 = (fabs(fabs(delta_phi) - PI) < 1e-15);
414 inner_corner = (side * delta_phi <= 0) && (!turns360);
415
416 /* if <line turns 360> and (<caps> and <not round>) */
417 if (turns360 && caps && (!round)) {
418 norm_vector(0, 0, vx, vy, &tx, &ty);
419 elliptic_tangent(side * tx, side * ty, da, db, dalpha, &tx, &ty);
420 Vect_append_point(nPoints, x[i] + wx + tx, y[i] + wy + ty, 0);
421 G_debug(4, " append point (c) x=%.16f y=%.16f", x[i] + wx + tx,
422 y[i] + wy + ty);
423 Vect_append_point(nPoints, nx + tx, ny + ty,
424 0); /* nx == x[i] + vx, ny == y[i] + vy */
425 G_debug(4, " append point (c) x=%.16f y=%.16f", nx + tx, ny + ty);
426 }
427
428 if ((!turns360) && (!round) && (!inner_corner)) {
429 res = line_intersection(a0, b0, c0, a1, b1, c1, &rx, &ry);
430 if (res == 1) {
432 G_debug(4, " append point (o) x=%.16f y=%.16f", rx, ry);
433 }
434 else if (res == 2) {
435 /* no need to append point in this case */
436 }
437 else
439 _("Unexpected result of line_intersection() res = %d"),
440 res);
441 }
442
443 if (round && (!inner_corner) && (!turns360 || caps)) {
444 /* we should draw elliptical arc for outside corner */
445
446 /* inverse transforms */
447 elliptic_transform(wx, wy, 1 / da, 1 / db, dalpha, &wx1, &wy1);
448 elliptic_transform(vx, vy, 1 / da, 1 / db, dalpha, &vx1, &vy1);
449
450 phi1 = atan2(wy1, wx1);
451 phi2 = atan2(vy1, vx1);
452 delta_phi = side * (phi2 - phi1);
453
454 /* make delta_phi in [0, 2pi] */
455 if (delta_phi < 0)
456 delta_phi += 2 * PI;
457
460
462 for (j = 1; j <= nsegments - 1; j++) {
463 elliptic_transform(cos(phi1), sin(phi1), da, db, dalpha, &tx,
464 &ty);
465 Vect_append_point(nPoints, x[i] + tx, y[i] + ty, 0);
466 G_debug(4, " append point (r) x=%.16f y=%.16f", x[i] + tx,
467 y[i] + ty);
469 }
470 }
471
472 Vect_append_point(nPoints, nx, ny, 0);
473 G_debug(4, " append point (s) x=%.16f y=%.16f", nx, ny);
475 G_debug(4, " append point (s) x=%.16f y=%.16f", mx, my);
476 }
477
478 /* close the output line */
479 Vect_append_point(nPoints, nPoints->x[0], nPoints->y[0], nPoints->z[0]);
481}
482
483/*
484 * side: side >= 0 - extracts contour on right side of edge, side < 0 - extracts
485 * contour on left side of edge if the extracted contour is the outer contour,
486 * it is returned in ccw order else if it is inner contour, it is returned in cw
487 * order
488 */
489static void extract_contour(struct planar_graph *pg, struct pg_edge *first,
490 int side, int winding, int stop_at_line_end,
491 struct line_pnts *nPoints)
492{
493 int j;
494 int v; /* current vertex number */
495 int v0;
496 int eside; /* side of the current edge */
497 double eangle; /* current edge angle with Ox (according to the current
498 direction) */
499 struct pg_vertex *vert; /* current vertex */
500 struct pg_vertex *vert0; /* last vertex */
501 struct pg_edge *edge; /* current edge; must be edge of vert */
502
503 /* int cs; */ /* on which side are we turning along the contour */
504 /* we will always turn right and don't need that one */
505 double opt_angle, tangle;
506 int opt_j, opt_side, opt_flag;
507
508 G_debug(3, "extract_contour(): v1=%d, v2=%d, side=%d, stop_at_line_end=%d",
509 first->v1, first->v2, side, stop_at_line_end);
510
512
513 edge = first;
514 if (side >= 0) {
515 eside = 1;
516 v0 = edge->v1;
517 v = edge->v2;
518 }
519 else {
520 eside = -1;
521 v0 = edge->v2;
522 v = edge->v1;
523 }
524 vert0 = &(pg->v[v0]);
525 vert = &(pg->v[v]);
526 eangle = atan2(vert->y - vert0->y, vert->x - vert0->x);
527
528 while (1) {
530 G_debug(4, "ec: v0=%d, v=%d, eside=%d, edge->v1=%d, edge->v2=%d", v0, v,
531 eside, edge->v1, edge->v2);
532 G_debug(4, "ec: append point x=%.18f y=%.18f", vert0->x, vert0->y);
533
534 /* mark current edge as visited on the appropriate side */
535 if (eside == 1) {
536 edge->visited_right = 1;
537 edge->winding_right = winding;
538 }
539 else {
540 edge->visited_left = 1;
541 edge->winding_left = winding;
542 }
543
544 opt_flag = 1;
545 for (j = 0; j < vert->ecount; j++) {
546 /* exclude current edge */
547 if (vert->edges[j] != edge) {
548 tangle = vert->angles[j] - eangle;
549 if (tangle < -PI)
550 tangle += 2 * PI;
551 else if (tangle > PI)
552 tangle -= 2 * PI;
553 /* now tangle is in (-PI, PI) */
554
555 if (opt_flag || (tangle < opt_angle)) {
556 opt_j = j;
557 opt_side = (vert->edges[j]->v1 == v) ? (1) : (-1);
559 opt_flag = 0;
560 }
561 }
562 }
563
564 /*
565 G_debug(4, "ec: opt: side=%d opt_flag=%d opt_angle=%.18f opt_j=%d
566 opt_step=%d", side, opt_flag, opt_angle, opt_j, opt_step);
567 */
568
569 /* if line end is reached (no other edges at curr vertex) */
570 if (opt_flag) {
571 if (stop_at_line_end) {
572 G_debug(3, " end has been reached, will stop here");
573 break;
574 }
575 else {
576 opt_j = 0; /* the only edge of vert is vert->edges[0] */
577 opt_side =
578 -eside; /* go to the other side of the current edge */
579 G_debug(3, " end has been reached, turning around");
580 }
581 }
582
583 /* break condition */
584 if ((vert->edges[opt_j] == first) && (opt_side == side))
585 break;
586 if (opt_side == 1) {
587 if (vert->edges[opt_j]->visited_right) {
588 G_warning(_("Next edge was visited (right) but it is not the "
589 "first one !!! breaking loop"));
590 G_debug(4,
591 "ec: v0=%d, v=%d, eside=%d, edge->v1=%d, edge->v2=%d",
592 v, (edge->v1 == v) ? (edge->v2) : (edge->v1), opt_side,
593 vert->edges[opt_j]->v1, vert->edges[opt_j]->v2);
594 break;
595 }
596 }
597 else {
598 if (vert->edges[opt_j]->visited_left) {
599 G_warning(_("Next edge was visited (left) but it is not the "
600 "first one !!! breaking loop"));
601 G_debug(4,
602 "ec: v0=%d, v=%d, eside=%d, edge->v1=%d, edge->v2=%d",
603 v, (edge->v1 == v) ? (edge->v2) : (edge->v1), opt_side,
604 vert->edges[opt_j]->v1, vert->edges[opt_j]->v2);
605 break;
606 }
607 }
608
609 edge = vert->edges[opt_j];
610 eside = opt_side;
611 v0 = v;
612 v = (edge->v1 == v) ? (edge->v2) : (edge->v1);
613 vert0 = vert;
614 vert = &(pg->v[v]);
615 eangle = vert0->angles[opt_j];
616 }
619 G_debug(4, "ec: append point x=%.18f y=%.18f", vert->x, vert->y);
620
621 return;
622}
623
624/*
625 * This function extracts the outer contour of a (self crossing) line.
626 * It can generate left/right contour if none of the line ends are in a loop.
627 * If one or both of them is in a loop, then there's only one contour
628 *
629 * side: side > 0 - right contour, side < 0 - left contour, side = 0 - outer
630 * contour if side != 0 and there's only one contour, the function returns it
631 *
632 * TODO: Implement side != 0 feature;
633 */
634static void extract_outer_contour(struct planar_graph *pg, int side,
635 struct line_pnts *nPoints)
636{
637 int i;
638 int flag;
639 int v;
640 struct pg_vertex *vert;
641 struct pg_edge *edge;
642 double min_x, min_angle;
643
644 G_debug(3, "extract_outer_contour()");
645
646 if (side != 0) {
647 G_fatal_error(_("side != 0 feature not implemented"));
648 return;
649 }
650
651 /* find a line segment which is on the outer contour */
652 flag = 1;
653 for (i = 0; i < pg->vcount; i++) {
654 if (flag || (pg->v[i].x < min_x)) {
655 v = i;
656 min_x = pg->v[i].x;
657 flag = 0;
658 }
659 }
660 vert = &(pg->v[v]);
661
662 flag = 1;
663 for (i = 0; i < vert->ecount; i++) {
664 if (flag || (vert->angles[i] < min_angle)) {
665 edge = vert->edges[i];
666 min_angle = vert->angles[i];
667 flag = 0;
668 }
669 }
670
671 /* the winding on the outer contour is 0 */
672 extract_contour(pg, edge, (edge->v1 == v) ? RIGHT_SIDE : LEFT_SIDE, 0, 0,
673 nPoints);
674
675 return;
676}
677
678/*
679 * Extracts contours which are not visited.
680 * IMPORTANT: the outer contour must be visited (you should call
681 * extract_outer_contour() to do that), so that extract_inner_contour() doesn't
682 * return it
683 *
684 * returns: 0 when there are no more inner contours; otherwise, 1
685 */
686static int extract_inner_contour(struct planar_graph *pg, int *winding,
687 struct line_pnts *nPoints)
688{
689 int i, w;
690 struct pg_edge *edge;
691
692 G_debug(3, "extract_inner_contour()");
693
694 for (i = 0; i < pg->ecount; i++) {
695 edge = &(pg->e[i]);
696 if (edge->visited_left) {
697 if (!(pg->e[i].visited_right)) {
698 w = edge->winding_left - 1;
699 extract_contour(pg, &(pg->e[i]), RIGHT_SIDE, w, 0, nPoints);
700 *winding = w;
701 return 1;
702 }
703 }
704 else {
705 if (pg->e[i].visited_right) {
706 w = edge->winding_right + 1;
707 extract_contour(pg, &(pg->e[i]), LEFT_SIDE, w, 0, nPoints);
708 *winding = w;
709 return 1;
710 }
711 }
712 }
713
714 return 0;
715}
716
717/* point_in_buf - test if point px,py is in d buffer of Points
718 ** dalpha is in degrees
719 ** returns: 1 in buffer
720 ** 0 not in buffer
721 */
722static int point_in_buf(struct line_pnts *Points, double px, double py,
723 double da, double db, double dalpha)
724{
725 int i, np;
726 double cx, cy;
727 double delta, delta_k, k;
728 double vx, vy, wx, wy, mx, my, nx, ny;
729 double len, tx, ty, d, da2;
730
731 G_debug(3, "point_in_buf()");
732
733 dalpha *= PI / 180; /* convert dalpha from degrees to radians */
734
735 np = Points->n_points;
736 da2 = da * da;
737 for (i = 0; i < np - 1; i++) {
738 vx = Points->x[i];
739 vy = Points->y[i];
740 wx = Points->x[i + 1];
741 wy = Points->y[i + 1];
742
743 if (da != db) {
744 mx = wx - vx;
745 my = wy - vy;
746 len = LENGTH(mx, my);
747 elliptic_tangent(mx / len, my / len, da, db, dalpha, &cx, &cy);
748
749 delta = mx * cy - my * cx;
750 delta_k = (px - vx) * cy - (py - vy) * cx;
751 k = delta_k / delta;
752 /* G_debug(4, "k = %g, k1 = %g", k, (mx * (px - vx) + my
753 * * (py - vy)) / (mx * mx + my * my)); */
754 if (k <= 0) {
755 nx = vx;
756 ny = vy;
757 }
758 else if (k >= 1) {
759 nx = wx;
760 ny = wy;
761 }
762 else {
763 nx = vx + k * mx;
764 ny = vy + k * my;
765 }
766
767 /* inverse transform */
768 elliptic_transform(px - nx, py - ny, 1 / da, 1 / db, dalpha, &tx,
769 &ty);
770
771 d = dig_distance2_point_to_line(nx + tx, ny + ty, 0, vx, vy, 0, wx,
772 wy, 0, 0, NULL, NULL, NULL, NULL,
773 NULL);
774
775 /* G_debug(4, "sqrt(d)*da = %g, len' = %g, olen = %g",
776 * sqrt(d)*da, da*LENGTH(tx,ty), LENGTH((px-nx),(py-ny))); */
777 if (d <= 1) {
778 /* G_debug(1, "d=%g", d); */
779 return 1;
780 }
781 }
782 else {
783 d = dig_distance2_point_to_line(px, py, 0, vx, vy, 0, wx, wy, 0, 0,
784 NULL, NULL, NULL, NULL, NULL);
785 /* G_debug(4, "sqrt(d) = %g", sqrt(d)); */
786 if (d <= da2) {
787 return 1;
788 }
789 }
790 }
791
792 return 0;
793}
794
795/* returns 0 for ccw, non-zero for cw
796 */
797static int get_polygon_orientation(const double *x, const double *y, int n)
798{
799 double x1, y1, x2, y2;
800 double area;
801
802 x2 = x[n - 1];
803 y2 = y[n - 1];
804
805 area = 0;
806 while (--n >= 0) {
807 x1 = x2;
808 y1 = y2;
809
810 x2 = *x++;
811 y2 = *y++;
812
813 area += (y2 + y1) * (x2 - x1);
814 }
815
816 return (area > 0);
817}
818
819/* internal */
820static void add_line_to_array(struct line_pnts *Points,
821 struct line_pnts ***arrPoints, int *count,
822 int *allocated, int more)
823{
824 if (*allocated == *count) {
825 *allocated += more;
826 *arrPoints =
827 G_realloc(*arrPoints, (*allocated) * sizeof(struct line_pnts *));
828 }
829 (*arrPoints)[*count] = Points;
830 (*count)++;
831
832 return;
833}
834
835static void destroy_lines_array(struct line_pnts **arr, int count)
836{
837 int i;
838
839 for (i = 0; i < count; i++)
841 G_free(arr);
842}
843
844/* area_outer and area_isles[i] must be closed non self-intersecting lines
845 side: 0 - auto, 1 - right, -1 left
846 */
847static void buffer_lines(struct line_pnts *area_outer,
848 struct line_pnts **area_isles, int isles_count,
849 int side, double da, double db, double dalpha,
850 int round, int caps, double tol,
851 struct line_pnts **oPoints,
852 struct line_pnts ***iPoints, int *inner_count)
853{
854 struct planar_graph *pg2;
855 struct line_pnts *sPoints, *cPoints;
856 struct line_pnts **arrPoints;
857 int i, count = 0;
858 int res, winding;
859 int auto_side;
860 int more = 8;
861 int allocated = 0;
862 double px, py;
863
864 G_debug(3, "buffer_lines()");
865
866 auto_side = (side == 0);
867
868 /* initializations */
871 arrPoints = NULL;
872
873 /* outer contour */
874 G_debug(3, " processing outer contour");
876 if (auto_side)
877 side = get_polygon_orientation(area_outer->x, area_outer->y,
878 area_outer->n_points - 1)
879 ? LEFT_SIDE
880 : RIGHT_SIDE;
881 convolution_line(area_outer, da, db, dalpha, side, round, caps, tol,
882 sPoints);
884 extract_outer_contour(pg2, 0, *oPoints);
885 res = extract_inner_contour(pg2, &winding, cPoints);
886 while (res != 0) {
887 if (winding == 0) {
888 int check_poly = 1;
889 double area_size;
890
892 if (area_size == 0) {
893 G_warning(_("zero area size"));
894 check_poly = 0;
895 }
896 if (cPoints->x[0] != cPoints->x[cPoints->n_points - 1] ||
897 cPoints->y[0] != cPoints->y[cPoints->n_points - 1]) {
898
899 G_warning(_("Line was not closed"));
900 check_poly = 0;
901 }
902
903 if (check_poly &&
905 if (Vect_get_point_in_poly(cPoints, &px, &py) == 0) {
906 if (!point_in_buf(area_outer, px, py, da, db, dalpha)) {
907 add_line_to_array(cPoints, &arrPoints, &count,
908 &allocated, more);
910 }
911 }
912 else {
913 G_warning(_("Vect_get_point_in_poly() failed"));
914 }
915 }
916 }
917 res = extract_inner_contour(pg2, &winding, cPoints);
918 }
920
921 /* inner contours */
922 G_debug(3, " processing inner contours");
923 for (i = 0; i < isles_count; i++) {
924 if (auto_side)
925 side = get_polygon_orientation(area_isles[i]->x, area_isles[i]->y,
926 area_isles[i]->n_points - 1)
927 ? RIGHT_SIDE
928 : LEFT_SIDE;
929 convolution_line(area_isles[i], da, db, dalpha, side, round, caps, tol,
930 sPoints);
932 extract_outer_contour(pg2, 0, cPoints);
933 res = extract_inner_contour(pg2, &winding, cPoints);
934 while (res != 0) {
935 if (winding == -1) {
936 int check_poly = 1;
937 double area_size;
938
940 if (area_size == 0) {
941 G_warning(_("zero area size"));
942 check_poly = 0;
943 }
944 if (cPoints->x[0] != cPoints->x[cPoints->n_points - 1] ||
945 cPoints->y[0] != cPoints->y[cPoints->n_points - 1]) {
946
947 G_warning(_("Line was not closed"));
948 check_poly = 0;
949 }
950
951 /* we need to check if the area is in the buffer.
952 I've simplified convolution_line(), so that it runs faster,
953 however that leads to occasional problems */
954 if (check_poly &&
956 area_isles[i])) {
957 if (Vect_get_point_in_poly(cPoints, &px, &py) == 0) {
958 if (!point_in_buf(area_isles[i], px, py, da, db,
959 dalpha)) {
960 add_line_to_array(cPoints, &arrPoints, &count,
961 &allocated, more);
963 }
964 }
965 else {
966 G_warning(_("Vect_get_point_in_poly() failed"));
967 }
968 }
969 }
970 res = extract_inner_contour(pg2, &winding, cPoints);
971 }
973 }
974
975 arrPoints = G_realloc(arrPoints, count * sizeof(struct line_pnts *));
978
981
982 G_debug(3, "buffer_lines() ... done");
983
984 return;
985}
986
987/*!
988 \brief Creates buffer around line.
989
990 See also Vect_line_buffer().
991
992 Shape of buffer endings is managed by two parameters - round and cap.
993 Setting round=1, cap=1 gives "classical" buffer, while
994 round=0, cap=1 gives square end, but cap=0 – butt.
995 See v.buffer manual or SVG stroke-linecap for examples.
996
997 To get "classical" buffer, set db equal to da, and dalpha to 0.
998
999 \param Points input line geometry
1000 \param da distance along major axis
1001 \param db distance along minor axis
1002 \param dalpha angle between 0x and major axis
1003 \param round make corners round (0 - square, not 0 - round)
1004 \param caps add caps at line ends (0 - butt, not 0 - caps)
1005 \param tol maximum distance between theoretical arc and output segments
1006 \param[out] oPoints output polygon outer border (ccw order)
1007 \param[out] iPoints array of output polygon's holes (cw order)
1008 \param[out] inner_count number of holes
1009 */
1010void Vect_line_buffer2(const struct line_pnts *Points, double da, double db,
1011 double dalpha, int round, int caps, double tol,
1012 struct line_pnts **oPoints, struct line_pnts ***iPoints,
1013 int *inner_count)
1014{
1015 struct planar_graph *pg;
1016 struct line_pnts *tPoints, *outer;
1017 struct line_pnts **isles;
1018 int isles_count = 0;
1019 int res, winding;
1020 int more = 8;
1021 int isles_allocated = 0;
1022
1023 G_debug(2, "Vect_line_buffer()");
1024
1025 Vect_line_prune((struct line_pnts *)Points);
1026
1027 if (Points->n_points == 1) {
1028 Vect_point_buffer2(Points->x[0], Points->y[0], da, db, dalpha, round,
1029 tol, oPoints);
1030 return;
1031 }
1032
1033 /* initializations */
1035 isles = NULL;
1036 pg = pg_create(Points);
1037
1038 /* outer contour */
1040 extract_outer_contour(pg, 0, outer);
1041
1042 /* inner contours */
1043 res = extract_inner_contour(pg, &winding, tPoints);
1044 while (res != 0) {
1045 add_line_to_array(tPoints, &isles, &isles_count, &isles_allocated,
1046 more);
1048 res = extract_inner_contour(pg, &winding, tPoints);
1049 }
1050
1051 buffer_lines(outer, isles, isles_count, RIGHT_SIDE, da, db, dalpha, round,
1053
1056 destroy_lines_array(isles, isles_count);
1058}
1059
1060/*!
1061 \brief Creates buffer around area.
1062
1063 \param Map vector map
1064 \param area area id
1065 \param da distance along major axis
1066 \param db distance along minor axis
1067 \param dalpha angle between 0x and major axis
1068 \param round make corners round
1069 \param caps add caps at line ends
1070 \param tol maximum distance between theoretical arc and output segments
1071 \param[out] oPoints output polygon outer border (ccw order)
1072 \param[out] inner_count number of holes
1073 \param[out] iPoints array of output polygon's holes (cw order)
1074 */
1075void Vect_area_buffer2(struct Map_info *Map, int area, double da, double db,
1076 double dalpha, int round, int caps, double tol,
1077 struct line_pnts **oPoints, struct line_pnts ***iPoints,
1078 int *inner_count)
1079{
1080 struct line_pnts *tPoints, *outer;
1081 struct line_pnts **isles;
1082 int isles_count = 0, n_isles;
1083 int i, isle;
1084 int more = 8;
1085 int isles_allocated = 0;
1086
1087 G_debug(2, "Vect_area_buffer()");
1088
1089 /* initializations */
1091 n_isles = Vect_get_area_num_isles(Map, area);
1092 isles_allocated = n_isles;
1093 isles = G_malloc(isles_allocated * sizeof(struct line_pnts *));
1094
1095 /* outer contour */
1098 /* does not work with zero length line segments */
1100
1101 /* inner contours */
1102 for (i = 0; i < n_isles; i++) {
1103 isle = Vect_get_area_isle(Map, area, i);
1105
1106 /* Check if the isle is big enough */
1107 /*
1108 if (Vect_line_length(tPoints) < 2*PI*max)
1109 continue;
1110 */
1111 /* does not work with zero length line segments */
1113 add_line_to_array(tPoints, &isles, &isles_count, &isles_allocated,
1114 more);
1116 }
1117
1118 buffer_lines(outer, isles, isles_count, 0, da, db, dalpha, round, caps, tol,
1120
1123 destroy_lines_array(isles, isles_count);
1124
1125 return;
1126}
1127
1128/*!
1129 \brief Creates buffer around the point (px, py).
1130
1131 \param px input point x-coordinate
1132 \param py input point y-coordinate
1133 \param da distance along major axis
1134 \param db distance along minor axis
1135 \param dalpha angle between 0x and major axis
1136 \param round make corners round
1137 \param tol maximum distance between theoretical arc and output segments
1138 \param[out] oPoints output polygon outer border (ccw order)
1139
1140 \note Currently only handles buffers with rounded corners (round = 1)
1141 */
1142void Vect_point_buffer2(double px, double py, double da, double db,
1143 double dalpha, int round, double tol,
1144 struct line_pnts **oPoints)
1145{
1146 double tx, ty;
1147 double angular_tol, angular_step, phi1;
1148 int j, nsegments;
1149
1150 G_debug(2, "%s()", __func__);
1151
1153
1154 dalpha *= PI / 180; /* convert dalpha from degrees to radians */
1155
1156 if (round) {
1157 angular_tol = angular_tolerance(tol, da, db);
1158
1159 nsegments = (int)(2 * PI / angular_tol) + 1;
1160 angular_step = 2 * PI / nsegments;
1161
1162 phi1 = 0;
1163 for (j = 0; j < nsegments; j++) {
1164 elliptic_transform(cos(phi1), sin(phi1), da, db, dalpha, &tx, &ty);
1165 Vect_append_point(*oPoints, px + tx, py + ty, 0);
1166 phi1 += angular_step;
1167 }
1168 }
1169 else {
1170 }
1171
1172 /* close the output line */
1173 Vect_append_point(*oPoints, (*oPoints)->x[0], (*oPoints)->y[0],
1174 (*oPoints)->z[0]);
1175
1176 return;
1177}
1178
1179/*
1180 \brief Create parallel line
1181
1182 See also Vect_line_parallel().
1183
1184 \param InPoints input line geometry
1185 \param da distance along major axis
1186 \param da distance along minor axis
1187 \param dalpha angle between 0x and major axis
1188 \param round make corners round
1189 \param tol maximum distance between theoretical arc and output segments
1190 \param[out] OutPoints output line
1191 */
1192void Vect_line_parallel2(struct line_pnts *InPoints, double da, double db,
1193 double dalpha, int side, int round, double tol,
1194 struct line_pnts *OutPoints)
1195{
1196 G_debug(2,
1197 "Vect_line_parallel(): npoints = %d, da = %f, "
1198 "db = %f, dalpha = %f, side = %d, round_corners = %d, tol = %f",
1199 InPoints->n_points, da, db, dalpha, side, round, tol);
1200
1201 parallel_line(InPoints, da, db, dalpha, side, round, 1, NON_LOOPED_LINE,
1202 tol, OutPoints);
1203
1204 /* if (!loops)
1205 clean_parallel(OutPoints, InPoints, distance, rm_end);
1206 */
1207
1208 return;
1209}
#define LENGTH(DX, DY)
Definition buffer2.c:23
#define NON_LOOPED_LINE
Definition buffer2.c:28
void Vect_line_parallel2(struct line_pnts *InPoints, double da, double db, double dalpha, int side, int round, double tol, struct line_pnts *OutPoints)
Definition buffer2.c:1192
#define PI
Definition buffer2.c:24
void Vect_line_buffer2(const struct line_pnts *Points, double da, double db, double dalpha, int round, int caps, double tol, struct line_pnts **oPoints, struct line_pnts ***iPoints, int *inner_count)
Creates buffer around line.
Definition buffer2.c:1010
void Vect_area_buffer2(struct Map_info *Map, int area, double da, double db, double dalpha, int round, int caps, double tol, struct line_pnts **oPoints, struct line_pnts ***iPoints, int *inner_count)
Creates buffer around area.
Definition buffer2.c:1075
#define RIGHT_SIDE
Definition buffer2.c:25
void Vect_point_buffer2(double px, double py, double da, double db, double dalpha, int round, double tol, struct line_pnts **oPoints)
Creates buffer around the point (px, py).
Definition buffer2.c:1142
#define LEFT_SIDE
Definition buffer2.c:26
#define NULL
Definition ccmath.h:32
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:145
#define G_realloc(p, n)
Definition defs/gis.h:138
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition defs/gis.h:136
int G_debug(int, const char *,...) __attribute__((format(printf
void Vect_destroy_line_struct(struct line_pnts *)
Frees all memory associated with a line_pnts structure, including the structure itself.
Definition line.c:75
int Vect_get_isle_points(struct Map_info *, int, struct line_pnts *)
Returns polygon array of points for given isle.
int Vect_get_area_points(struct Map_info *, int, struct line_pnts *)
Returns polygon array of points (outer ring) of given area.
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
int Vect_get_point_in_poly(const struct line_pnts *, double *, double *)
Get point inside polygon.
Definition Vlib/poly.c:236
int Vect_get_area_isle(struct Map_info *, int, int)
Returns isle id for area.
int Vect_get_area_num_isles(struct Map_info *, int)
Returns number of isles for given area.
int Vect_point_in_poly(double, double, const struct line_pnts *)
Determines if a point (X,Y) is inside a polygon.
Definition Vlib/poly.c:977
int Vect_line_delete_point(struct line_pnts *, int)
Delete point at given index and move all points above down.
Definition line.c:208
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
void pg_destroy_struct(struct planar_graph *pg)
Definition dgraph.c:360
struct planar_graph * pg_create(const struct line_pnts *Points)
Definition dgraph.c:442
double dig_distance2_point_to_line(double, double, double, double, double, double, double, double, double, int, double *, double *, double *, double *, int *)
int dig_find_area_poly(struct line_pnts *, double *)
Definition diglib/poly.c:94
#define MAX(a, b)
Definition gis.h:145
#define _(str)
Definition glocale.h:10
int count
double b
Definition r_raster.c:37
double l
Definition r_raster.c:37
Vector map info.
Feature geometry info - coordinates.
double * y
Array of Y coordinates.
double * x
Array of X coordinates.
int n_points
Number of points.
double * z
Array of Z coordinates.
char winding_left
Definition dgraph.h:11
int v1
Definition dgraph.h:7
int v2
Definition dgraph.h:8
char winding_right
Definition dgraph.h:12
char visited_right
Definition dgraph.h:10
char visited_left
Definition dgraph.h:9
int ecount
Definition dgraph.h:27
struct pg_edge * e
Definition dgraph.h:29
struct pg_vertex * v
Definition dgraph.h:26
int vcount
Definition dgraph.h:25
#define x