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