GRASS 8 Programmer's Manual 8.6.0dev(2026)-1878fdfec5
Loading...
Searching...
No Matches
gsdrape.c
Go to the documentation of this file.
1/*!
2 \file lib/ogsf/gsdrape.c
3
4 \brief OGSF library - functions to intersect line segments with edges of
5 surface polygons
6
7 GRASS OpenGL gsurf OGSF Library
8
9 For efficiency, intersections are found without respect to which
10 specific triangle edge is intersected, but on a broader sense with
11 the horizontal, vertical, and diagonal seams in the grid, then
12 the intersections are ordered. If quadstrips are used for drawing
13 rather than tmesh, triangulation is not consistent; for diagonal
14 intersections, the proper diagonal to intersect would need to be
15 determined according to the algorithm used by qstrip (look at nearby
16 normals). It may be faster to go ahead and find the intersections
17 with the other diagonals using the same methods, then at sorting
18 time determine which diagonal array to look at for each quad.
19 It would also require a mechanism for throwing out unused intersections
20 with the diagonals during the ordering phase.
21 Do intersections in 2D, fill line structure with 3D pts (maybe calling
22 routine will cache for redrawing). Get Z value by using linear interp
23 between corners.
24
25 - check for easy cases:
26 - single point
27 - colinear with horizontal or vertical edges
28 - colinear with diagonal edges of triangles
29 - calculate three arrays of ordered intersections:
30 - with vertical edges
31 - with horizontal edges
32 - with diagonal edges and interpolate Z, using simple linear interpolation.
33 - eliminate duplicate intersections (need only compare one coord for each)
34 - build ordered set of points.
35
36 Return static pointer to 3D set of points. Max number of intersections
37 will be rows + cols + diags, so should allocate this number to initialize.
38 Let calling routine worry about copying points for caching.
39
40 SPDX-FileCopyrightText: 1999-2008 GRASS Development Team
41 SPDX-License-Identifier: GPL-2.0-or-later
42
43 \author Bill Brown UI GMS Lab
44 \author Doxygenized by Martin Landa <landa.martin gmail.com> (May 2008)
45 */
46
47#include <stdlib.h>
48
49#include <grass/ogsf.h>
50#include <grass/glocale.h>
51
52#include "gsget.h"
53#include "rowcol.h"
54#include "math.h"
55
56#define DONT_INTERSECT 0
57#define DO_INTERSECT 1
58#define COLLINEAR 2
59
60#define LERP(a, l, h) ((l) + (((h) - (l)) * (a)))
61#define EQUAL(a, b) (fabs((a) - (b)) < EPSILON)
62#define ISNODE(p, res) (fmod((double)p, (double)res) < EPSILON)
63
64#define SAME_SIGNS(a, b) ((a >= 0 && b >= 0) || (a < 0 && b < 0))
65
66static int drape_line_init(int, int);
67static Point3 *_gsdrape_get_segments(geosurf *, float *, float *, int *);
68static float dist_squared_2d(float *, float *);
69
70/* array of points to be returned */
71static Point3 *I3d;
72
73/* make dependent on resolution? */
74static float EPSILON = 0.000001;
75
76/*vertical, horizontal, & diagonal intersections */
77static Point3 *Vi, *Hi, *Di;
78
79static typbuff *Ebuf; /* elevation buffer */
80static int Flat;
81
82/*!
83 \brief Initialize
84
85 \param[in] rows number of rows
86 \param[in] cols number of columns
87
88 \return -1 on failure
89 \return 1 on success
90 */
91static int drape_line_init(int rows, int cols)
92{
93 /* use G_calloc() [-> G_fatal_error] instead of calloc ? */
94 if (NULL == (I3d = (Point3 *)calloc(2 * (rows + cols), sizeof(Point3)))) {
95 return (-1);
96 }
97
98 if (NULL == (Vi = (Point3 *)calloc(cols, sizeof(Point3)))) {
99 G_free(I3d);
100
101 return (-1);
102 }
103
104 if (NULL == (Hi = (Point3 *)calloc(rows, sizeof(Point3)))) {
105 G_free(I3d);
106 G_free(Vi);
107
108 return (-1);
109 }
110
111 if (NULL == (Di = (Point3 *)calloc(rows + cols, sizeof(Point3)))) {
112 G_free(I3d);
113 G_free(Vi);
114 G_free(Hi);
115
116 return (-1);
117 }
118
119 return (1);
120}
121
122/*!
123 \brief Get segments
124
125 \param gs surface (geosurf)
126 \param[in] bgn begin point
127 \param[in] end end point
128 \param[out] num
129
130 \return pointer to Point3 struct
131 */
132static Point3 *_gsdrape_get_segments(geosurf *gs, float *bgn, float *end,
133 int *num)
134{
135 float f[3], l[3];
136 int vi, hi, di;
137 float dir[2], yres, xres;
138
139 xres = VXRES(gs);
140 yres = VYRES(gs);
141
142 vi = hi = di = 0;
143 GS_v2dir(bgn, end, dir);
144
145 if (dir[X]) {
146 vi = get_vert_intersects(gs, bgn, end, dir);
147 }
148
149 if (dir[Y]) {
150 hi = get_horz_intersects(gs, bgn, end, dir);
151 }
152
153 if (!((end[Y] - bgn[Y]) / (end[X] - bgn[X]) == yres / xres)) {
154 di = get_diag_intersects(gs, bgn, end, dir);
155 }
156
157 interp_first_last(gs, bgn, end, f, l);
158
159 *num = order_intersects(gs, f, l, vi, hi, di);
160 /* fills in return values, eliminates dupes (corners) */
161
162 G_debug(5, "_gsdrape_get_segments vi=%d, hi=%d, di=%d, num=%d", vi, hi, di,
163 *num);
164
165 return (I3d);
166}
167
168/*!
169 \brief Calculate 2D distance
170
171 \param[in] p1 first point
172 \param[in] p2 second point
173
174 \return distance
175 */
176static float dist_squared_2d(float *p1, float *p2)
177{
178 float dx, dy;
179
180 dx = p2[X] - p1[X];
181 dy = p2[Y] - p1[Y];
182
183 return (dx * dx + dy * dy);
184}
185
186/*!
187 \brief ADD
188
189 \param gs surface (geosurf)
190
191 \return -1 on failure
192 \return 1 on success
193 */
195{
196 static int first = 1;
197
198 if (first) {
199 first = 0;
200
201 if (0 > drape_line_init(gs->rows, gs->cols)) {
202 G_warning(_("Unable to process vector map - out of memory"));
203 Ebuf = NULL;
204
205 return (-1);
206 }
207 }
208
209 Ebuf = gs_get_att_typbuff(gs, ATT_TOPO, 0);
210
211 return (1);
212}
213
214/*!
215 \brief Check if segment intersect vector region
216
217 Clipping performed:
218 - bgn and end are replaced so that both points are within viewregion
219 - if seg intersects
220
221 \param gs surface (geosurf)
222 \param[in,out] bgn begin point
223 \param[in,out] end end point
224
225 \return 0 if segment doesn't intersect the viewregion, or intersects only at
226 corner
227 \return otherwise returns 1
228 */
229int seg_intersect_vregion(geosurf *gs, float *bgn, float *end)
230{
231 float *replace, xl, yb, xr, yt, xi, yi;
232 int inside = 0;
233
234 xl = 0.0;
235 xr = VCOL2X(gs, VCOLS(gs));
236 yt = VROW2Y(gs, 0);
237 yb = VROW2Y(gs, VROWS(gs));
238
239 if (in_vregion(gs, bgn)) {
240 replace = end;
241 inside++;
242 }
243
244 if (in_vregion(gs, end)) {
245 replace = bgn;
246 inside++;
247 }
248
249 if (inside == 2) {
250 return (1);
251 }
252 else if (inside) {
253 /* one in & one out - replace gets first intersection */
254 if (segs_intersect(bgn[X], bgn[Y], end[X], end[Y], xl, yb, xl, yt, &xi,
255 &yi)) {
256 /* left */
257 }
258 else if (segs_intersect(bgn[X], bgn[Y], end[X], end[Y], xr, yb, xr, yt,
259 &xi, &yi)) {
260 /* right */
261 }
262 else if (segs_intersect(bgn[X], bgn[Y], end[X], end[Y], xl, yb, xr, yb,
263 &xi, &yi)) {
264 /* bottom */
265 }
266 else if (segs_intersect(bgn[X], bgn[Y], end[X], end[Y], xl, yt, xr, yt,
267 &xi, &yi)) {
268 /* top */
269 }
270
271 replace[X] = xi;
272 replace[Y] = yi;
273 }
274 else {
275 /* both out - find 2 intersects & replace both */
276 float pt1[2], pt2[2];
277
278 replace = pt1;
279 if (segs_intersect(bgn[X], bgn[Y], end[X], end[Y], xl, yb, xl, yt, &xi,
280 &yi)) {
281 replace[X] = xi;
282 replace[Y] = yi;
283 replace = pt2;
284 inside++;
285 }
286
287 if (segs_intersect(bgn[X], bgn[Y], end[X], end[Y], xr, yb, xr, yt, &xi,
288 &yi)) {
289 replace[X] = xi;
290 replace[Y] = yi;
291 replace = pt2;
292 inside++;
293 }
294
295 if (inside < 2) {
296 if (segs_intersect(bgn[X], bgn[Y], end[X], end[Y], xl, yb, xr, yb,
297 &xi, &yi)) {
298 replace[X] = xi;
299 replace[Y] = yi;
300 replace = pt2;
301 inside++;
302 }
303 }
304
305 if (inside < 2) {
306 if (segs_intersect(bgn[X], bgn[Y], end[X], end[Y], xl, yt, xr, yt,
307 &xi, &yi)) {
308 replace[X] = xi;
309 replace[Y] = yi;
310 inside++;
311 }
312 }
313
314 if (inside < 2) {
315 return (0); /* no intersect or only 1 point on corner */
316 }
317
318 /* compare dist of intersects to bgn - closest replaces bgn */
320 bgn[X] = pt1[X];
321 bgn[Y] = pt1[Y];
322 end[X] = pt2[X];
323 end[Y] = pt2[Y];
324 }
325 else {
326 bgn[X] = pt2[X];
327 bgn[Y] = pt2[Y];
328 end[X] = pt1[X];
329 end[Y] = pt1[Y];
330 }
331 }
332
333 return (1);
334}
335
336/*!
337 \brief ADD
338
339 \param gs surface (geosurf)
340 \param[in,out] bgn begin point (x,y)
341 \param[in,out] end end point (x,y)
342 \param[out] num
343
344 \return pointer to Point3 struct
345 */
346Point3 *gsdrape_get_segments(geosurf *gs, float *bgn, float *end, int *num)
347{
349
350 if (!seg_intersect_vregion(gs, bgn, end)) {
351 *num = 0;
352
353 return (NULL);
354 }
355
357 /* will probably want a force_drape option to get all intersects */
358 I3d[0][X] = bgn[X];
359 I3d[0][Y] = bgn[Y];
360 I3d[0][Z] = gs->att[ATT_TOPO].constant;
361 I3d[1][X] = end[X];
362 I3d[1][Y] = end[Y];
363 I3d[1][Z] = gs->att[ATT_TOPO].constant;
364 *num = 2;
365
366 return (I3d);
367 }
368
369 if (bgn[X] == end[X] && bgn[Y] == end[Y]) {
370 float f[3], l[3];
371
372 interp_first_last(gs, bgn, end, f, l);
373 GS_v3eq(I3d[0], f);
374 GS_v3eq(I3d[1], l);
375
376 /* CHANGE (*num = 1) to reflect degenerate line ? */
377 *num = 2;
378
379 return (I3d);
380 }
381
382 Flat = 0;
383 return (_gsdrape_get_segments(gs, bgn, end, num));
384}
385
386/*!
387 \brief Get all segments
388
389 \param gs surface (geosurf)
390 \param[in,out] bgn begin point
391 \param[in,out] end end point
392 \param[out] num
393
394 \return pointer to Point3 struct
395 */
396Point3 *gsdrape_get_allsegments(geosurf *gs, float *bgn, float *end, int *num)
397{
399
400 if (!seg_intersect_vregion(gs, bgn, end)) {
401 *num = 0;
402 return (NULL);
403 }
404
405 if (bgn[X] == end[X] && bgn[Y] == end[Y]) {
406 float f[3], l[3];
407
408 interp_first_last(gs, bgn, end, f, l);
409 GS_v3eq(I3d[0], f);
410 GS_v3eq(I3d[1], l);
411 *num = 2;
412
413 return (I3d);
414 }
415
417 Flat = 1;
418 }
419 else {
420 Flat = 0;
421 }
422
423 return (_gsdrape_get_segments(gs, bgn, end, num));
424}
425
426/*!
427 \brief ADD
428
429 \param gs surface (geosurf)
430 \param[in] bgn begin point
431 \param[in] end end point
432 \param[out] f first
433 \param[out] l last
434 */
435void interp_first_last(geosurf *gs, float *bgn, float *end, Point3 f, Point3 l)
436{
437 f[X] = bgn[X];
438 f[Y] = bgn[Y];
439
440 l[X] = end[X];
441 l[Y] = end[Y];
442
443 if (Flat) {
444 f[Z] = l[Z] = gs->att[ATT_TOPO].constant;
445 }
446 else {
447 viewcell_tri_interp(gs, Ebuf, f, 0);
448 viewcell_tri_interp(gs, Ebuf, l, 0);
449 }
450
451 return;
452}
453
454/*!
455 \brief ADD
456
457 \param gs surface (geosurf)
458 \param[in,out] pt
459 */
461{
462 typbuff *buf;
463
464 buf = gs_get_att_typbuff(gs, ATT_TOPO, 0);
465
466 return (viewcell_tri_interp(gs, buf, pt, 0));
467}
468
469/*!
470 \brief ADD
471
472 In gsd_surf, tmesh draws polys like so:
473 <pre>
474 --------------
475 | /|
476 | / |
477 | / |
478 | / |
479 | / |
480 | / |
481 | / |
482 | / |
483 | / |
484 | / |
485 | / |
486 |/ |
487 --------------
488 </pre>
489
490 UNLESS the top right or bottom left point is masked, in which case a
491 single triangle with the opposite diagonal is drawn. This case is
492 not yet handled here & should only occur on edges.
493 pt has X & Y coordinates in it, we interpolate Z here
494
495 This could probably be much shorter, but not much faster.
496
497 \param gs
498 \param buf
499 \param[in,out] pt
500 \param[in] check_mask
501
502 \return 1 if point is in view region
503 \return otherwise 0 (if masked)
504 */
506{
507 Point3 p1, p2, p3;
508 int offset, drow, dcol, vrow, vcol;
509 float xmax, ymin, ymax, alpha;
510
511 xmax = VCOL2X(gs, VCOLS(gs));
512 ymax = VROW2Y(gs, 0);
513 ymin = VROW2Y(gs, VROWS(gs));
514
515 if (check_mask) {
516 if (gs_point_is_masked(gs, pt)) {
517 return (0);
518 }
519 }
520
521 if (pt[X] < 0.0 || pt[Y] > ymax) {
522 /* outside on left or top */
523 return (0);
524 }
525
526 if (pt[Y] < ymin || pt[X] > xmax) {
527 /* outside on bottom or right */
528 return (0);
529 }
530
532 pt[Z] = gs->att[ATT_TOPO].constant;
533
534 return (1);
535 }
536 else if (MAP_ATT != gs_get_att_src(gs, ATT_TOPO)) {
537 return (0);
538 }
539
540 vrow = Y2VROW(gs, pt[Y]);
541 vcol = X2VCOL(gs, pt[X]);
542
543 if (vrow < VROWS(gs) && vcol < VCOLS(gs)) {
544 /*not on bottom or right edge */
545 if (pt[X] > 0.0 && pt[Y] < ymax) {
546 /* not on left or top edge */
547 p1[X] = VCOL2X(gs, vcol + 1);
548 p1[Y] = VROW2Y(gs, vrow);
549 drow = VROW2DROW(gs, vrow);
550 dcol = VCOL2DCOL(gs, vcol + 1);
551 offset = DRC2OFF(gs, drow, dcol);
552 GET_MAPATT(buf, offset, p1[Z]); /* top right */
553
554 p2[X] = VCOL2X(gs, vcol);
555 p2[Y] = VROW2Y(gs, vrow + 1);
556 drow = VROW2DROW(gs, vrow + 1);
557 dcol = VCOL2DCOL(gs, vcol);
558 offset = DRC2OFF(gs, drow, dcol);
559 GET_MAPATT(buf, offset, p2[Z]); /* bottom left */
560
561 if ((pt[X] - p2[X]) / VXRES(gs) > (pt[Y] - p2[Y]) / VYRES(gs)) {
562 /* lower triangle */
563 p3[X] = VCOL2X(gs, vcol + 1);
564 p3[Y] = VROW2Y(gs, vrow + 1);
565 drow = VROW2DROW(gs, vrow + 1);
566 dcol = VCOL2DCOL(gs, vcol + 1);
567 offset = DRC2OFF(gs, drow, dcol);
568 GET_MAPATT(buf, offset, p3[Z]); /* bottom right */
569 }
570 else {
571 /* upper triangle */
572 p3[X] = VCOL2X(gs, vcol);
573 p3[Y] = VROW2Y(gs, vrow);
574 drow = VROW2DROW(gs, vrow);
575 dcol = VCOL2DCOL(gs, vcol);
576 offset = DRC2OFF(gs, drow, dcol);
577 GET_MAPATT(buf, offset, p3[Z]); /* top left */
578 }
579
580 return (Point_on_plane(p1, p2, p3, pt));
581 }
582 else if (pt[X] == 0.0) {
583 /* on left edge */
584 if (pt[Y] < ymax) {
585 vrow = Y2VROW(gs, pt[Y]);
586 drow = VROW2DROW(gs, vrow);
587 offset = DRC2OFF(gs, drow, 0);
588 GET_MAPATT(buf, offset, p1[Z]);
589
590 drow = VROW2DROW(gs, vrow + 1);
591 offset = DRC2OFF(gs, drow, 0);
592 GET_MAPATT(buf, offset, p2[Z]);
593
594 alpha = (VROW2Y(gs, vrow) - pt[Y]) / VYRES(gs);
595 pt[Z] = LERP(alpha, p1[Z], p2[Z]);
596 }
597 else {
598 /* top left corner */
599 GET_MAPATT(buf, 0, pt[Z]);
600 }
601
602 return (1);
603 }
604 else if (pt[Y] == gs->yrange) {
605 /* on top edge, not a corner */
606 vcol = X2VCOL(gs, pt[X]);
607 dcol = VCOL2DCOL(gs, vcol);
608 GET_MAPATT(buf, dcol, p1[Z]);
609
610 dcol = VCOL2DCOL(gs, vcol + 1);
611 GET_MAPATT(buf, dcol, p2[Z]);
612
613 alpha = (pt[X] - VCOL2X(gs, vcol)) / VXRES(gs);
614 pt[Z] = LERP(alpha, p1[Z], p2[Z]);
615
616 return (1);
617 }
618 }
619 else if (vrow == VROWS(gs)) {
620 /* on bottom edge */
621 drow = VROW2DROW(gs, VROWS(gs));
622
623 if (pt[X] > 0.0 && pt[X] < xmax) {
624 /* not a corner */
625 vcol = X2VCOL(gs, pt[X]);
626 dcol = VCOL2DCOL(gs, vcol);
627 offset = DRC2OFF(gs, drow, dcol);
628 GET_MAPATT(buf, offset, p1[Z]);
629
630 dcol = VCOL2DCOL(gs, vcol + 1);
631 offset = DRC2OFF(gs, drow, dcol);
632 GET_MAPATT(buf, offset, p2[Z]);
633
634 alpha = (pt[X] - VCOL2X(gs, vcol)) / VXRES(gs);
635 pt[Z] = LERP(alpha, p1[Z], p2[Z]);
636
637 return (1);
638 }
639 else if (pt[X] == 0.0) {
640 /* bottom left corner */
641 offset = DRC2OFF(gs, drow, 0);
642 GET_MAPATT(buf, offset, pt[Z]);
643
644 return (1);
645 }
646 else {
647 /* bottom right corner */
648 dcol = VCOL2DCOL(gs, VCOLS(gs));
649 offset = DRC2OFF(gs, drow, dcol);
650 GET_MAPATT(buf, offset, pt[Z]);
651
652 return (1);
653 }
654 }
655 else {
656 /* on right edge, not bottom corner */
657 dcol = VCOL2DCOL(gs, VCOLS(gs));
658
659 if (pt[Y] < ymax) {
660 vrow = Y2VROW(gs, pt[Y]);
661 drow = VROW2DROW(gs, vrow);
662 offset = DRC2OFF(gs, drow, dcol);
663 GET_MAPATT(buf, offset, p1[Z]);
664
665 drow = VROW2DROW(gs, vrow + 1);
666 offset = DRC2OFF(gs, drow, dcol);
667 GET_MAPATT(buf, offset, p2[Z]);
668
669 alpha = (VROW2Y(gs, vrow) - pt[Y]) / VYRES(gs);
670 pt[Z] = LERP(alpha, p1[Z], p2[Z]);
671
672 return (1);
673 }
674 else {
675 /* top right corner */
676 GET_MAPATT(buf, dcol, pt[Z]);
677
678 return (1);
679 }
680 }
681
682 return (0);
683}
684
685/*!
686 \brief ADD
687
688 \param gs surface (geosurf)
689 \param[in] pt
690
691 \return 1
692 \return 0
693 */
694int in_vregion(geosurf *gs, float *pt)
695{
696 if (pt[X] >= 0.0 && pt[Y] <= gs->yrange) {
697 if (pt[X] <= VCOL2X(gs, VCOLS(gs))) {
698 return (pt[Y] >= VROW2Y(gs, VROWS(gs)));
699 }
700 }
701
702 return (0);
703}
704
705/*!
706 \brief ADD
707
708 After all the intersections between the segment and triangle
709 edges have been found, they are in three lists. (intersections
710 with vertical, horizontal, and diagonal triangle edges)
711
712 Each list is ordered in space from first to last segment points,
713 but now the lists need to be woven together. This routine
714 starts with the first point of the segment and then checks the
715 next point in each list to find the closest, eliminating duplicates
716 along the way and storing the result in I3d.
717
718 \param gs surface (geosurf)
719 \param[in] first first point
720 \param[in] last last point
721 \param[in] vi
722 \param[in] hi
723 \param[in] di
724
725 \return
726 */
727int order_intersects(geosurf *gs, Point3 first, Point3 last, int vi, int hi,
728 int di)
729{
730 int num, i, found, cv, ch, cd, cnum;
731 float dv, dh, dd, big, cpoint[2];
732
733 cv = ch = cd = cnum = 0;
734 num = vi + hi + di;
735
736 cpoint[X] = first[X];
737 cpoint[Y] = first[Y];
738
739 if (in_vregion(gs, first)) {
740 I3d[cnum][X] = first[X];
741 I3d[cnum][Y] = first[Y];
742 I3d[cnum][Z] = first[Z];
743 cnum++;
744 }
745
746 /* TODO: big could still be less than first dist */
747 big = gs->yrange * gs->yrange + gs->xrange * gs->xrange; /*BIG distance */
748 dv = dh = dd = big;
749
750 for (i = 0; i < num; i = cv + ch + cd) {
751 if (cv < vi) {
752 dv = dist_squared_2d(Vi[cv], cpoint);
753
754 if (dv < EPSILON) {
755 cv++;
756 continue;
757 }
758 }
759 else {
760 dv = big;
761 }
762
763 if (ch < hi) {
764 dh = dist_squared_2d(Hi[ch], cpoint);
765
766 if (dh < EPSILON) {
767 ch++;
768 continue;
769 }
770 }
771 else {
772 dh = big;
773 }
774
775 if (cd < di) {
776 dd = dist_squared_2d(Di[cd], cpoint);
777
778 if (dd < EPSILON) {
779 cd++;
780 continue;
781 }
782 }
783 else {
784 dd = big;
785 }
786
787 found = 0;
788
789 if (cd < di) {
790 if (dd <= dv && dd <= dh) {
791 found = 1;
792 cpoint[X] = I3d[cnum][X] = Di[cd][X];
793 cpoint[Y] = I3d[cnum][Y] = Di[cd][Y];
794 I3d[cnum][Z] = Di[cd][Z];
795 cnum++;
796
797 if (EQUAL(dd, dv)) {
798 cv++;
799 }
800
801 if (EQUAL(dd, dh)) {
802 ch++;
803 }
804
805 cd++;
806 }
807 }
808
809 if (!found) {
810 if (cv < vi) {
811 if (dv <= dh) {
812 found = 1;
813 cpoint[X] = I3d[cnum][X] = Vi[cv][X];
814 cpoint[Y] = I3d[cnum][Y] = Vi[cv][Y];
815 I3d[cnum][Z] = Vi[cv][Z];
816 cnum++;
817
818 if (EQUAL(dv, dh)) {
819 ch++;
820 }
821
822 cv++;
823 }
824 }
825 }
826
827 if (!found) {
828 if (ch < hi) {
829 cpoint[X] = I3d[cnum][X] = Hi[ch][X];
830 cpoint[Y] = I3d[cnum][Y] = Hi[ch][Y];
831 I3d[cnum][Z] = Hi[ch][Z];
832 cnum++;
833 ch++;
834 }
835 }
836
837 if (i == cv + ch + cd) {
838 G_debug(5, "order_intersects(): stuck on %d", cnum);
839 G_debug(5, "order_intersects(): cv = %d, ch = %d, cd = %d", cv, ch,
840 cd);
841 G_debug(5, "order_intersects(): dv = %f, dh = %f, dd = %f", dv, dh,
842 dd);
843
844 break;
845 }
846 }
847
848 if (EQUAL(last[X], cpoint[X]) && EQUAL(last[Y], cpoint[Y])) {
849 return (cnum);
850 }
851
852 if (in_vregion(gs, last)) {
853 /* TODO: check for last point on corner ? */
854 I3d[cnum][X] = last[X];
855 I3d[cnum][Y] = last[Y];
856 I3d[cnum][Z] = last[Z];
857 ++cnum;
858 }
859
860 return (cnum);
861}
862
863/*!
864 \brief ADD
865
866 \todo For consistency, need to decide how last row & last column are
867 displayed - would it look funny to always draw last row/col with
868 finer resolution if necessary, or would it be better to only show
869 full rows/cols?
870
871 Colinear already eliminated
872
873 \param gs surface (geosurf)
874 \param[in] bgn begin point
875 \param[in] end end point
876 \param[in] dir direction
877
878 \return
879 */
880int get_vert_intersects(geosurf *gs, float *bgn, float *end, float *dir)
881{
882 int fcol, lcol, incr, hits, num, offset, drow1, drow2;
883 float xl, yb, xr, yt, z1, z2, alpha;
884 float yres, xi, yi;
885 int bgncol, endcol, cols, rows;
886
887 yres = VYRES(gs);
888 cols = VCOLS(gs);
889 rows = VROWS(gs);
890
891 bgncol = X2VCOL(gs, bgn[X]);
892 endcol = X2VCOL(gs, end[X]);
893
894 if (bgncol > cols && endcol > cols) {
895 return 0;
896 }
897
898 if (bgncol == endcol) {
899 return 0;
900 }
901
902 fcol = dir[X] > 0 ? bgncol + 1 : bgncol;
903 lcol = dir[X] > 0 ? endcol : endcol + 1;
904
905 /* assuming only showing FULL cols */
906 incr = lcol - fcol > 0 ? 1 : -1;
907
908 while (fcol > cols || fcol < 0) {
909 fcol += incr;
910 }
911
912 while (lcol > cols || lcol < 0) {
913 lcol -= incr;
914 }
915
916 num = abs(lcol - fcol) + 1;
917
918 yb = gs->yrange - (yres * rows) - EPSILON;
919 yt = gs->yrange + EPSILON;
920
921 for (hits = 0; hits < num; hits++) {
922 xl = xr = VCOL2X(gs, fcol);
923
924 if (segs_intersect(bgn[X], bgn[Y], end[X], end[Y], xl, yt, xr, yb, &xi,
925 &yi)) {
926 Vi[hits][X] = xi;
927 Vi[hits][Y] = yi;
928
929 /* find data rows */
930 if (Flat) {
931 Vi[hits][Z] = gs->att[ATT_TOPO].constant;
932 }
933 else {
934 drow1 = Y2VROW(gs, Vi[hits][Y]) * gs->y_mod;
935 drow2 = (1 + Y2VROW(gs, Vi[hits][Y])) * gs->y_mod;
936
937 if (drow2 >= gs->rows) {
938 drow2 = gs->rows - 1; /*bottom edge */
939 }
940
941 alpha = ((gs->yrange - drow1 * gs->yres) - Vi[hits][Y]) / yres;
942
943 offset = DRC2OFF(gs, drow1, fcol * gs->x_mod);
944 GET_MAPATT(Ebuf, offset, z1);
945 offset = DRC2OFF(gs, drow2, fcol * gs->x_mod);
946 GET_MAPATT(Ebuf, offset, z2);
947 Vi[hits][Z] = LERP(alpha, z1, z2);
948 }
949 }
950
951 /* if they don't intersect, something's wrong! */
952 /* should only happen on endpoint, so it will be added later */
953 else {
954 hits--;
955 num--;
956 }
957
958 fcol += incr;
959 }
960
961 return (hits);
962}
963
964/*!
965 \brief Get horizontal intersects
966
967 \param gs surface (geosurf)
968 \param[in] bgn begin point
969 \param[in] end end point
970 \param[in] dir
971
972 \return number of intersects
973 */
974int get_horz_intersects(geosurf *gs, float *bgn, float *end, float *dir)
975{
976 int frow, lrow, incr, hits, num, offset, dcol1, dcol2;
977 float xl, yb, xr, yt, z1, z2, alpha;
978 float xres, xi, yi;
979 int bgnrow, endrow, rows, cols;
980
981 xres = VXRES(gs);
982 cols = VCOLS(gs);
983 rows = VROWS(gs);
984
985 bgnrow = Y2VROW(gs, bgn[Y]);
986 endrow = Y2VROW(gs, end[Y]);
987 if (bgnrow == endrow) {
988 return 0;
989 }
990
991 if (bgnrow > rows && endrow > rows) {
992 return 0;
993 }
994
995 frow = dir[Y] > 0 ? bgnrow : bgnrow + 1;
996 lrow = dir[Y] > 0 ? endrow + 1 : endrow;
997
998 /* assuming only showing FULL rows */
999 incr = lrow - frow > 0 ? 1 : -1;
1000
1001 while (frow > rows || frow < 0) {
1002 frow += incr;
1003 }
1004
1005 while (lrow > rows || lrow < 0) {
1006 lrow -= incr;
1007 }
1008
1009 num = abs(lrow - frow) + 1;
1010
1011 xl = 0.0 - EPSILON;
1012 xr = xres * cols + EPSILON;
1013
1014 for (hits = 0; hits < num; hits++) {
1015 yb = yt = VROW2Y(gs, frow);
1016
1017 if (segs_intersect(bgn[X], bgn[Y], end[X], end[Y], xl, yt, xr, yb, &xi,
1018 &yi)) {
1019 Hi[hits][X] = xi;
1020 Hi[hits][Y] = yi;
1021
1022 /* find data cols */
1023 if (Flat) {
1024 Hi[hits][Z] = gs->att[ATT_TOPO].constant;
1025 }
1026 else {
1027 dcol1 = X2VCOL(gs, Hi[hits][X]) * gs->x_mod;
1028 dcol2 = (1 + X2VCOL(gs, Hi[hits][X])) * gs->x_mod;
1029
1030 if (dcol2 >= gs->cols) {
1031 dcol2 = gs->cols - 1; /* right edge */
1032 }
1033
1034 alpha = (Hi[hits][X] - (dcol1 * gs->xres)) / xres;
1035
1036 offset = DRC2OFF(gs, frow * gs->y_mod, dcol1);
1037 GET_MAPATT(Ebuf, offset, z1);
1038 offset = DRC2OFF(gs, frow * gs->y_mod, dcol2);
1039 GET_MAPATT(Ebuf, offset, z2);
1040 Hi[hits][Z] = LERP(alpha, z1, z2);
1041 }
1042 }
1043
1044 /* if they don't intersect, something's wrong! */
1045 /* should only happen on endpoint, so it will be added later */
1046 else {
1047 hits--;
1048 num--;
1049 }
1050
1051 frow += incr;
1052 }
1053
1054 return (hits);
1055}
1056
1057/*!
1058 \brief Get diagonal intersects
1059
1060 Colinear already eliminated
1061
1062 \param gs surface (geosurf)
1063 \param[in] bgn begin point
1064 \param[in] end end point
1065 \param dir ? (unused)
1066
1067 \return number of intersects
1068 */
1069int get_diag_intersects(geosurf *gs, float *bgn, float *end,
1070 float *dir G_UNUSED)
1071{
1072 int fdig, ldig, incr, hits, num, offset;
1073 int vrow, vcol, drow1, drow2, dcol1, dcol2;
1074 float xl, yb, xr, yt, z1, z2, alpha;
1075 float xres, yres, xi, yi, dx, dy;
1076 int diags, cols, rows, lower;
1077 Point3 pt;
1078
1079 xres = VXRES(gs);
1080 yres = VYRES(gs);
1081 cols = VCOLS(gs);
1082 rows = VROWS(gs);
1083 diags = rows + cols; /* -1 ? */
1084
1085 /* determine upper/lower triangle for last */
1086 vrow = Y2VROW(gs, end[Y]);
1087 vcol = X2VCOL(gs, end[X]);
1088 pt[X] = VCOL2X(gs, vcol);
1089 pt[Y] = VROW2Y(gs, vrow + 1);
1090 lower = ((end[X] - pt[X]) / xres > (end[Y] - pt[Y]) / yres);
1091 ldig = lower ? vrow + vcol + 1 : vrow + vcol;
1092
1093 /* determine upper/lower triangle for first */
1094 vrow = Y2VROW(gs, bgn[Y]);
1095 vcol = X2VCOL(gs, bgn[X]);
1096 pt[X] = VCOL2X(gs, vcol);
1097 pt[Y] = VROW2Y(gs, vrow + 1);
1098 lower = ((bgn[X] - pt[X]) / xres > (bgn[Y] - pt[Y]) / yres);
1099 fdig = lower ? vrow + vcol + 1 : vrow + vcol;
1100
1101 /* adjust according to direction */
1102 if (ldig > fdig) {
1103 fdig++;
1104 }
1105
1106 if (fdig > ldig) {
1107 ldig++;
1108 }
1109
1110 incr = ldig - fdig > 0 ? 1 : -1;
1111
1112 while (fdig > diags || fdig < 0) {
1113 fdig += incr;
1114 }
1115
1116 while (ldig > diags || ldig < 0) {
1117 ldig -= incr;
1118 }
1119
1120 num = abs(ldig - fdig) + 1;
1121
1122 for (hits = 0; hits < num; hits++) {
1123 yb = gs->yrange - (yres * (fdig < rows ? fdig : rows)) - EPSILON;
1124 xl = VCOL2X(gs, (fdig < rows ? 0 : fdig - rows)) - EPSILON;
1125 yt = gs->yrange - (yres * (fdig < cols ? 0 : fdig - cols)) + EPSILON;
1126 xr = VCOL2X(gs, (fdig < cols ? fdig : cols)) + EPSILON;
1127
1128 if (segs_intersect(bgn[X], bgn[Y], end[X], end[Y], xl, yb, xr, yt, &xi,
1129 &yi)) {
1130 Di[hits][X] = xi;
1131 Di[hits][Y] = yi;
1132
1133 if (ISNODE(xi, xres)) {
1134 /* then it's also a ynode */
1135 num--;
1136 hits--;
1137 continue;
1138 }
1139
1140 /* find data rows */
1141 drow1 = Y2VROW(gs, Di[hits][Y]) * gs->y_mod;
1142 drow2 = (1 + Y2VROW(gs, Di[hits][Y])) * gs->y_mod;
1143
1144 if (drow2 >= gs->rows) {
1145 drow2 = gs->rows - 1; /* bottom edge */
1146 }
1147
1148 /* find data cols */
1149 if (Flat) {
1150 Di[hits][Z] = gs->att[ATT_TOPO].constant;
1151 }
1152 else {
1153 dcol1 = X2VCOL(gs, Di[hits][X]) * gs->x_mod;
1154 dcol2 = (1 + X2VCOL(gs, Di[hits][X])) * gs->x_mod;
1155
1156 if (dcol2 >= gs->cols) {
1157 dcol2 = gs->cols - 1; /* right edge */
1158 }
1159
1160 dx = DCOL2X(gs, dcol2) - Di[hits][X];
1161 dy = DROW2Y(gs, drow1) - Di[hits][Y];
1162 alpha =
1163 sqrt(dx * dx + dy * dy) / sqrt(xres * xres + yres * yres);
1164
1165 offset = DRC2OFF(gs, drow1, dcol2);
1166 GET_MAPATT(Ebuf, offset, z1);
1167 offset = DRC2OFF(gs, drow2, dcol1);
1168 GET_MAPATT(Ebuf, offset, z2);
1169 Di[hits][Z] = LERP(alpha, z1, z2);
1170 }
1171 }
1172
1173 /* if they don't intersect, something's wrong! */
1174 /* should only happen on endpoint, so it will be added later */
1175 else {
1176 hits--;
1177 num--;
1178 }
1179
1180 fdig += incr;
1181 }
1182
1183 return (hits);
1184}
1185
1186/*!
1187 \brief Line intersect
1188
1189 Author: Mukesh Prasad
1190 Modified for floating point: Bill Brown
1191
1192 This function computes whether two line segments,
1193 respectively joining the input points (x1,y1) -- (x2,y2)
1194 and the input points (x3,y3) -- (x4,y4) intersect.
1195 If the lines intersect, the output variables x, y are
1196 set to coordinates of the point of intersection.
1197
1198 \param[in] x1,y1,x2,y2 coordinates of endpoints of one segment
1199 \param[in] x3,y3,x4,y4 coordinates of endpoints of other segment
1200 \param[out] x,y coordinates of intersection point
1201
1202 \return 0 no intersection
1203 \return 1 intersect
1204 \return 2 collinear
1205 */
1206int segs_intersect(float x1, float y1, float x2, float y2, float x3, float y3,
1207 float x4, float y4, float *x, float *y)
1208{
1209 float a1, a2, b1, b2, c1, c2; /* Coefficients of line eqns. */
1210 float r1, r2, r3, r4; /* 'Sign' values */
1211 float denom, /* offset, */ num; /* Intermediate values */
1212
1213 /* Compute a1, b1, c1, where line joining points 1 and 2
1214 * is "a1 x + b1 y + c1 = 0".
1215 */
1216 a1 = y2 - y1;
1217 b1 = x1 - x2;
1218 c1 = x2 * y1 - x1 * y2;
1219
1220 /* Compute r3 and r4.
1221 */
1222 r3 = a1 * x3 + b1 * y3 + c1;
1223 r4 = a1 * x4 + b1 * y4 + c1;
1224
1225 /* Check signs of r3 and r4. If both point 3 and point 4 lie on
1226 * same side of line 1, the line segments do not intersect.
1227 */
1228
1229 if (!EQUAL(r3, 0.0) && !EQUAL(r4, 0.0) && SAME_SIGNS(r3, r4)) {
1230 return (DONT_INTERSECT);
1231 }
1232
1233 /* Compute a2, b2, c2 */
1234 a2 = y4 - y3;
1235 b2 = x3 - x4;
1236 c2 = x4 * y3 - x3 * y4;
1237
1238 /* Compute r1 and r2 */
1239 r1 = a2 * x1 + b2 * y1 + c2;
1240 r2 = a2 * x2 + b2 * y2 + c2;
1241
1242 /* Check signs of r1 and r2. If both point 1 and point 2 lie
1243 * on same side of second line segment, the line segments do
1244 * not intersect.
1245 */
1246
1247 if (!EQUAL(r1, 0.0) && !EQUAL(r2, 0.0) && SAME_SIGNS(r1, r2)) {
1248 return (DONT_INTERSECT);
1249 }
1250
1251 /* Line segments intersect: compute intersection point.
1252 */
1253 denom = a1 * b2 - a2 * b1;
1254
1255 if (denom == 0) {
1256 return (COLLINEAR);
1257 }
1258
1259 /* offset = denom < 0 ? -denom / 2 : denom / 2; */
1260
1261 /* The denom/2 is to get rounding instead of truncating. It
1262 * is added or subtracted to the numerator, depending upon the
1263 * sign of the numerator.
1264 */
1265 num = b1 * c2 - b2 * c1;
1266
1267 *x = num / denom;
1268
1269 num = a2 * c1 - a1 * c2;
1270 *y = num / denom;
1271
1272 return (DO_INTERSECT);
1273}
1274
1275/*!
1276 \brief Check if point is on plane
1277
1278 Plane defined by three points here; user fills in unk[X] & unk[Y]
1279
1280 \param[in,out] p1,p2,p3 points defining plane
1281 \param[in,out] unk point
1282
1283 \return 1 point on plane
1284 \return 0 point not on plane
1285 */
1287{
1288 float plane[4];
1289
1290 P3toPlane(p1, p2, p3, plane);
1291
1292 return (XY_intersect_plane(unk, plane));
1293}
1294
1295/*!
1296 \brief Check for intersection (point and plane)
1297
1298 Ax + By + Cz + D = 0, so z = (Ax + By + D) / -C
1299
1300 User fills in intersect[X] & intersect[Y]
1301
1302 \param[in,out] intersect intersect coordinates
1303 \param[in] plane plane definition
1304
1305 \return 0 doesn't intersect
1306 \return 1 intesects
1307 */
1308int XY_intersect_plane(float *intersect, float *plane)
1309{
1310 float x, y;
1311
1312 if (!plane[Z]) {
1313 return (0); /* doesn't intersect */
1314 }
1315
1316 x = intersect[X];
1317 y = intersect[Y];
1318 intersect[Z] = (plane[X] * x + plane[Y] * y + plane[W]) / -plane[Z];
1319
1320 return (1);
1321}
1322
1323/*!
1324 \brief Define plane
1325
1326 \param[in] p1,p2,p3 three point on plane
1327 \param[out] plane plane definition
1328
1329 \return 1
1330 */
1331int P3toPlane(Point3 p1, Point3 p2, Point3 p3, float *plane)
1332{
1333 Point3 v1, v2, norm;
1334
1335 v1[X] = p1[X] - p3[X];
1336 v1[Y] = p1[Y] - p3[Y];
1337 v1[Z] = p1[Z] - p3[Z];
1338
1339 v2[X] = p2[X] - p3[X];
1340 v2[Y] = p2[Y] - p3[Y];
1341 v2[Z] = p2[Z] - p3[Z];
1342
1343 V3Cross(v1, v2, norm);
1344
1345 plane[X] = norm[X];
1346 plane[Y] = norm[Y];
1347 plane[Z] = norm[Z];
1348 plane[W] = -p3[X] * norm[X] - p3[Y] * norm[Y] - p3[Z] * norm[Z];
1349
1350 return (1);
1351}
1352
1353/*!
1354 \brief Get cross product
1355
1356 \param[in] a,b
1357 \param[out] c
1358
1359 \return cross product c = a cross b
1360 */
1362{
1363 c[X] = (a[Y] * b[Z]) - (a[Z] * b[Y]);
1364 c[Y] = (a[Z] * b[X]) - (a[X] * b[Z]);
1365 c[Z] = (a[X] * b[Y]) - (a[Y] * b[X]);
1366
1367 return (1);
1368}
#define EPSILON
#define NULL
Definition ccmath.h:32
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:145
void G_warning(const char *,...) __attribute__((format(printf
int G_debug(int, const char *,...) __attribute__((format(printf
int gs_point_is_masked(geosurf *, float *)
Check if point is masked.
Definition gs.c:1310
float GS_P2distance(float *, float *)
Calculate distance in plane.
Definition gs_util.c:156
int gs_get_att_src(geosurf *, int)
Get attribute source.
Definition gs.c:652
void GS_v3eq(float *, float *)
Copy vector values.
Definition gs_util.c:174
typbuff * gs_get_att_typbuff(geosurf *, int, int)
Get attribute data buffer.
Definition gs.c:677
void GS_v2dir(float *, float *, float *)
Get a normalized direction from v1 to v2, store in v3 (2D)
Definition gs_util.c:378
#define G_UNUSED
A macro for an attribute, if attached to a variable, indicating that the variable is not used.
Definition gis.h:43
#define _(str)
Definition glocale.h:10
#define COLLINEAR
Definition gsdrape.c:58
int P3toPlane(Point3 p1, Point3 p2, Point3 p3, float *plane)
Define plane.
Definition gsdrape.c:1331
#define ISNODE(p, res)
Definition gsdrape.c:62
Point3 * gsdrape_get_segments(geosurf *gs, float *bgn, float *end, int *num)
ADD.
Definition gsdrape.c:346
int in_vregion(geosurf *gs, float *pt)
ADD.
Definition gsdrape.c:694
#define SAME_SIGNS(a, b)
Definition gsdrape.c:64
int order_intersects(geosurf *gs, Point3 first, Point3 last, int vi, int hi, int di)
ADD.
Definition gsdrape.c:727
int _viewcell_tri_interp(geosurf *gs, Point3 pt)
ADD.
Definition gsdrape.c:460
int gsdrape_set_surface(geosurf *gs)
ADD.
Definition gsdrape.c:194
#define EQUAL(a, b)
Definition gsdrape.c:61
void interp_first_last(geosurf *gs, float *bgn, float *end, Point3 f, Point3 l)
ADD.
Definition gsdrape.c:435
#define DONT_INTERSECT
Definition gsdrape.c:56
int V3Cross(Point3 a, Point3 b, Point3 c)
Get cross product.
Definition gsdrape.c:1361
int viewcell_tri_interp(geosurf *gs, typbuff *buf, Point3 pt, int check_mask)
ADD.
Definition gsdrape.c:505
int segs_intersect(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float *x, float *y)
Line intersect.
Definition gsdrape.c:1206
int get_horz_intersects(geosurf *gs, float *bgn, float *end, float *dir)
Get horizontal intersects.
Definition gsdrape.c:974
#define LERP(a, l, h)
Definition gsdrape.c:60
int get_vert_intersects(geosurf *gs, float *bgn, float *end, float *dir)
ADD.
Definition gsdrape.c:880
int get_diag_intersects(geosurf *gs, float *bgn, float *end, float *dir)
Get diagonal intersects.
Definition gsdrape.c:1069
int seg_intersect_vregion(geosurf *gs, float *bgn, float *end)
Check if segment intersect vector region.
Definition gsdrape.c:229
#define DO_INTERSECT
Definition gsdrape.c:57
Point3 * gsdrape_get_allsegments(geosurf *gs, float *bgn, float *end, int *num)
Get all segments.
Definition gsdrape.c:396
int XY_intersect_plane(float *intersect, float *plane)
Check for intersection (point and plane)
Definition gsdrape.c:1308
int Point_on_plane(Point3 p1, Point3 p2, Point3 p3, Point3 unk)
Check if point is on plane.
Definition gsdrape.c:1286
#define GET_MAPATT(buff, offset, att)
Definition gsget.h:29
OGSF header file (structures)
#define X
Definition ogsf.h:141
#define ATT_TOPO
Definition ogsf.h:76
float Point3[3]
Definition ogsf.h:206
#define Z
Definition ogsf.h:143
#define W
Definition ogsf.h:144
#define Y
Definition ogsf.h:142
#define MAP_ATT
Definition ogsf.h:86
#define CONST_ATT
Definition ogsf.h:87
double b
Definition r_raster.c:37
double l
Definition r_raster.c:37
#define VYRES(gs)
Definition rowcol.h:10
#define Y2VROW(gs, py)
Definition rowcol.h:27
#define VXRES(gs)
Definition rowcol.h:9
#define VCOL2X(gs, vcol)
Definition rowcol.h:40
#define VCOLS(gs)
Definition rowcol.h:14
#define VROWS(gs)
Definition rowcol.h:13
#define DRC2OFF(gs, drow, dcol)
Definition rowcol.h:17
#define DCOL2X(gs, dcol)
Definition rowcol.h:36
#define VROW2Y(gs, vrow)
Definition rowcol.h:39
#define VROW2DROW(gs, vrow)
Definition rowcol.h:31
#define X2VCOL(gs, px)
Definition rowcol.h:28
#define VCOL2DCOL(gs, vcol)
Definition rowcol.h:32
#define DROW2Y(gs, drow)
Definition rowcol.h:35
Definition ogsf.h:267
Definition clip.h:6
#define x