1 /*!
2    \file lib/ogsf/gs2.c
3 
4    \brief OGSF library - loading and manipulating surfaces (higher level functions)
5 
6    GRASS OpenGL gsurf OGSF Library
7 
8    Plans for handling color maps:
9    NOW:
10    if able to load as unsigned char, make lookup table containing palette
11    otherwise, load directly as packed color, set lookup = NULL
12    MAYBE LATER:
13    if able to load as POSITIVE short, make lookup table containing palette
14    - may want to calculate savings first (ie,  numcells > 32768)
15    (not exactly, it's Friday & time to go home - figure it later)
16    otherwise, load directly as packed color, set lookup = NULL
17    MESSY! - need to fix up!
18 
19    (C) 1999-2008 by the GRASS Development Team
20 
21    This program is free software under the
22    GNU General Public License (>=v2).
23    Read the file COPYING that comes with GRASS
24    for details.
25 
26    \author Bill Brown USACERL (1993)
27    \author Pierre de Mouveaux <p_de_mouveaux hotmail.com> (updated October 1999)
28    \author Doxygenized by Martin Landa <landa.martin gmail.com> (May 2008)
29  */
30 
31 #include <stdlib.h>
32 #include <string.h>
33 #include <math.h>
34 
35 #include <grass/config.h>
36 
37 #if defined(OPENGL_X11) || defined(OPENGL_WINDOWS)
38 #include <GL/gl.h>
39 #include <GL/glu.h>
40 #elif defined(OPENGL_AQUA)
41 #include <OpenGL/gl.h>
42 #include <OpenGL/glu.h>
43 #endif
44 
45 #include <grass/gis.h>
46 #include <grass/raster.h>
47 #include <grass/ogsf.h>
48 #include <grass/glocale.h>
49 
50 #include "gsget.h"
51 #include "rowcol.h"
52 #include "rgbpack.h"
53 
54 /* Hack to make NVIZ2.2 query functions.("What's Here" and "Look at")
55  * to work.
56  * Uses gs_los_intersect1() instead of gs_los_intersect().
57  * Pierre de Mouveaux - 31 oct. 1999. p_de_mouveaux@hotmail.com.
58  */
59 #define NVIZ_HACK 1
60 
61 int gsd_getViewport(GLint *, GLint *);
62 
63 /* array of surface ids */
64 static int Surf_ID[MAX_SURFS];
65 static int Next_surf = 0;
66 static int SDref_surf = 0;
67 
68 /* attributes array */
69 static float Default_const[MAX_ATTS];
70 static float Default_nulls[MAX_ATTS];
71 
72 /* largest dimension */
73 static float Longdim;
74 
75 /* N, S, W, E */
76 static float Region[4];
77 static geoview Gv;
78 static geodisplay Gd;
79 static struct Cell_head wind;
80 static int Buffermode;
81 static int Numlights = 0;
82 static int Resetlight = 1;
83 static int Modelshowing = 0;
84 
void_func(void)85 void void_func(void)
86 {
87     return;
88 }
89 
90 /*!
91    \brief Initialize OGSF library
92 
93    Get region settings - wind
94 
95    Set Region (NSWE array) and compute scale
96  */
GS_libinit(void)97 void GS_libinit(void)
98 {
99     static int first = 1;
100 
101     G_get_set_window(&wind);
102 
103     Region[0] = wind.north;
104     Region[1] = wind.south;
105     Region[2] = wind.west;
106     Region[3] = wind.east;
107 
108     /* scale largest dimension to GS_UNIT_SIZE */
109     if ((wind.east - wind.west) > (wind.north - wind.south)) {
110 	Longdim = (wind.east - wind.west);
111     }
112     else {
113 	Longdim = (wind.north - wind.south);
114     }
115 
116     Gv.scale = GS_UNIT_SIZE / Longdim;
117 
118     G_debug(1, "GS_libinit(): n=%f s=%f w=%f e=%f scale=%f first=%d",
119 	    Region[0], Region[1], Region[2], Region[3], Gv.scale, first);
120 
121     Cxl_func = void_func;
122 
123 
124     if (first) {
125 	gs_init();
126     }
127 
128     first = 0;
129 
130     return;
131 }
132 
133 /*!
134    \brief Get largest dimension
135 
136    \param[out] dim dimension
137 
138    \return 1
139  */
GS_get_longdim(float * dim)140 int GS_get_longdim(float *dim)
141 {
142     *dim = Longdim;
143 
144     G_debug(3, "GS_get_longdim(): dim=%g", *dim);
145 
146     return (1);
147 }
148 
149 /*!
150    \brief Get 2D region extent
151 
152    \param[out] n,s,w,e extent values
153 
154    \return 1
155  */
GS_get_region(float * n,float * s,float * w,float * e)156 int GS_get_region(float *n, float *s, float *w, float *e)
157 {
158     *n = Region[0];
159     *s = Region[1];
160     *w = Region[2];
161     *e = Region[3];
162 
163     return (1);
164 }
165 
166 /*!
167    \brief Set default attributes for map objects
168 
169    \param defs attributes array (dim MAX_ATTS)
170    \param null_defs null attributes array (dim MAX_ATTS)
171  */
GS_set_att_defaults(float * defs,float * null_defs)172 void GS_set_att_defaults(float *defs, float *null_defs)
173 {
174     int i;
175 
176     G_debug(3, "GS_set_att_defaults");
177 
178     for (i = 0; i < MAX_ATTS; i++) {
179 	Default_const[i] = defs[i];
180 	Default_nulls[i] = null_defs[i];
181     }
182 
183     return;
184 }
185 
186 /*!
187    Check if surface exists
188 
189    \param id surface id
190 
191    \return 0 not found
192    \return 1 found
193  */
GS_surf_exists(int id)194 int GS_surf_exists(int id)
195 {
196     int i, found = 0;
197 
198     G_debug(3, "GS_surf_exists(): id=%d", id);
199 
200 
201     if (NULL == gs_get_surf(id)) {
202 	return (0);
203     }
204 
205     for (i = 0; i < Next_surf && !found; i++) {
206 	if (Surf_ID[i] == id) {
207 	    found = 1;
208 	}
209     }
210 
211     return (found);
212 }
213 
214 /*!
215    \brief Add new surface
216 
217    Note that origin has 1/2 cell added to represent center of cells
218    because library assumes that east - west = (cols - 1) * ew_res,
219    since left and right columns are on the edges.
220 
221    \return surface id
222    \return -1 on error (MAX_SURFS exceded)
223  */
GS_new_surface(void)224 int GS_new_surface(void)
225 {
226     geosurf *ns;
227 
228     G_debug(3, "GS_new_surface():");
229 
230     if (Next_surf < MAX_SURFS) {
231 	ns = gs_get_new_surface();
232 	gs_init_surf(ns, wind.west + wind.ew_res / 2.,
233 		     wind.south + wind.ns_res / 2., wind.rows, wind.cols,
234 		     wind.ew_res, wind.ns_res);
235 	gs_set_defaults(ns, Default_const, Default_nulls);
236 
237 	/* make default shine current */
238 	gs_set_att_src(ns, ATT_SHINE, CONST_ATT);
239 
240 	Surf_ID[Next_surf] = ns->gsurf_id;
241 	++Next_surf;
242 
243 	G_debug(3, "    id=%d", ns->gsurf_id);
244 
245 	return (ns->gsurf_id);
246     }
247 
248 
249 
250     return (-1);
251 }
GS_set_light_reset(int i)252 void GS_set_light_reset(int i)
253 {
254     Resetlight = i;
255     if (i)
256 	Numlights = 0;
257 }
GS_get_light_reset(void)258 int GS_get_light_reset(void)
259 {
260     return Resetlight;
261 }
262 /*!
263    \brief Add new model light
264 
265    \return light model id
266    \return -1 on error (MAX_LIGHTS exceded)
267  */
GS_new_light(void)268 int GS_new_light(void)
269 {
270     int i;
271 
272     if (GS_get_light_reset()) {
273 
274 	GS_set_light_reset(0);
275 
276 	for (i = 0; i < MAX_LIGHTS; i++) {
277 	    Gv.lights[i].position[X] = Gv.lights[i].position[Y] = 0.0;
278 	    Gv.lights[i].position[Z] = 1.0;
279 	    Gv.lights[i].position[W] = 0.0;	/* infinite */
280 	    Gv.lights[i].color[0] = Gv.lights[i].color[1] =
281 		Gv.lights[i].color[2] = 1.0;
282 	    Gv.lights[i].ambient[0] = Gv.lights[i].ambient[1] =
283 		Gv.lights[i].ambient[2] = 0.2;
284 	    Gv.lights[i].shine = 32.0;
285 	}
286 
287 	gsd_init_lightmodel();
288     }
289 
290     if (Numlights < MAX_LIGHTS) {
291 	gsd_deflight(Numlights + 1, &(Gv.lights[Numlights]));
292 	gsd_switchlight(Numlights + 1, 1);
293 
294 	return ++Numlights;
295     }
296 
297     return -1;
298 }
299 
300 /*!
301    \brief Set light position
302 
303    \bug I think lights array doesnt match sgi_light array
304 
305    \param num light id (starts with 1)
306    \param xpos,ypos,zpos coordinates (model)
307    \param local local coordinate (for viewport)
308  */
GS_setlight_position(int num,float xpos,float ypos,float zpos,int local)309 void GS_setlight_position(int num, float xpos, float ypos, float zpos,
310 			  int local)
311 {
312     if (num) {
313 	num -= 1;
314 	if (num < Numlights) {
315 	    Gv.lights[num].position[X] = xpos;
316 	    Gv.lights[num].position[Y] = ypos;
317 	    Gv.lights[num].position[Z] = zpos;
318 	    Gv.lights[num].position[W] = (float)local;
319 
320 	    gsd_deflight(num + 1, &(Gv.lights[num]));
321 	}
322     }
323 
324     return;
325 }
326 
327 
328 /*!
329    \brief Get light position
330 
331    \param num light id (starts at 1)
332    \param[out] xpos,ypos,zpos coordinates
333    \param[out] local ?
334  */
GS_getlight_position(int num,float * xpos,float * ypos,float * zpos,int * local)335 void GS_getlight_position(int num, float *xpos, float *ypos, float *zpos,
336 			  int *local)
337 {
338     if (num) {
339 	num -= 1;
340 	if (num < Numlights) {
341 	    *xpos = Gv.lights[num].position[X];
342 	    *ypos = Gv.lights[num].position[Y];
343 	    *zpos = Gv.lights[num].position[Z];
344 	    *local = (int)Gv.lights[num].position[W];
345 
346 	}
347     }
348 
349     return;
350 }
351 
352 /*!
353    \brief Set light color
354 
355    \param num light id (starts at 1)
356    \param red,green,blue color values (from 0.0 to 1.0)
357  */
GS_setlight_color(int num,float red,float green,float blue)358 void GS_setlight_color(int num, float red, float green, float blue)
359 {
360     if (num) {
361 	num -= 1;
362 	if (num < Numlights) {
363 	    Gv.lights[num].color[0] = red;
364 	    Gv.lights[num].color[1] = green;
365 	    Gv.lights[num].color[2] = blue;
366 
367 	    gsd_deflight(num + 1, &(Gv.lights[num]));
368 	}
369     }
370 
371     return;
372 }
373 
374 /*!
375    \brief Get light color
376 
377    \param num light id (starts at 1)
378    \param[out] red,green,blue color values
379  */
GS_getlight_color(int num,float * red,float * green,float * blue)380 void GS_getlight_color(int num, float *red, float *green, float *blue)
381 {
382     if (num) {
383 	num -= 1;
384 	if (num < Numlights) {
385 	    *red = Gv.lights[num].color[0];
386 	    *green = Gv.lights[num].color[1];
387 	    *blue = Gv.lights[num].color[2];
388 	}
389     }
390 
391     return;
392 }
393 
394 /*!
395    \brief Set light ambient
396 
397    Red, green, blue from 0.0 to 1.0
398 
399    \param num light id (starts at 1)
400    \param red,green,blue color values
401  */
GS_setlight_ambient(int num,float red,float green,float blue)402 void GS_setlight_ambient(int num, float red, float green, float blue)
403 {
404     if (num) {
405 	num -= 1;
406 	if (num < Numlights) {
407 	    Gv.lights[num].ambient[0] = red;
408 	    Gv.lights[num].ambient[1] = green;
409 	    Gv.lights[num].ambient[2] = blue;
410 
411 	    gsd_deflight(num + 1, &(Gv.lights[num]));
412 	}
413     }
414 
415     return;
416 }
417 
418 /*!
419    \brief Get light ambient
420 
421    \param num light id (starts at 1)
422    \param[out] red,green,blue color values
423  */
GS_getlight_ambient(int num,float * red,float * green,float * blue)424 void GS_getlight_ambient(int num, float *red, float *green, float *blue)
425 {
426     if (num) {
427 	num -= 1;
428 	if (num < Numlights) {
429 	    *red = Gv.lights[num].ambient[0];
430 	    *green = Gv.lights[num].ambient[1];
431 	    *blue = Gv.lights[num].ambient[2];
432 	}
433     }
434 
435     return;
436 }
437 
438 
439 /*!
440    \brief Switch off all lights
441  */
GS_lights_off(void)442 void GS_lights_off(void)
443 {
444     int i;
445 
446     for (i = 0; i < Numlights; i++) {
447 	gsd_switchlight(i + 1, 0);
448     }
449 
450     return;
451 }
452 
453 /*!
454    \brief Switch on all lights
455  */
GS_lights_on(void)456 void GS_lights_on(void)
457 {
458     int i;
459 
460     for (i = 0; i < Numlights; i++) {
461 	gsd_switchlight(i + 1, 1);
462     }
463 
464     return;
465 }
466 
467 /*!
468    \brief Switch on/off light
469 
470    \param num light id (starts at 1)
471    \param on non-zero for 'on' otherwise 'off'
472  */
GS_switchlight(int num,int on)473 void GS_switchlight(int num, int on)
474 {
475     if (num) {
476 	num -= 1;
477 
478 	if (num < Numlights) {
479 	    gsd_switchlight(num + 1, on);
480 	}
481     }
482 
483     return;
484 }
485 
486 /*!
487    \brief Check if transparency is set
488 
489    \return 0 transparency not set
490    \return 1 transparency is set
491  */
GS_transp_is_set(void)492 int GS_transp_is_set(void)
493 {
494     return (gs_att_is_set(NULL, ATT_TRANSP) || (FC_GREY == gsd_getfc()));
495 }
496 
497 /*!
498    \brief Retrieves coordinates for lighting model position, at center of view
499 
500    \param pos[out] coordinates
501  */
GS_get_modelposition1(float pos[])502 void GS_get_modelposition1(float pos[])
503 {
504     /* TODO: Still needs work to handle other cases */
505     /* this is a quick hack to get lighting adjustments debugged */
506     /*
507        GS_v3dir(Gv.from_to[FROM], Gv.from_to[TO], center);
508        GS_v3mult(center, 1000);
509        GS_v3add(center, Gv.from_to[FROM]);
510      */
511 
512     gs_get_datacenter(pos);
513     gs_get_data_avg_zmax(&(pos[Z]));
514 
515     G_debug(1, "GS_get_modelposition1(): model position: %f %f %f",
516 	    pos[X], pos[Y], pos[Z]);
517 
518     return;
519 }
520 
521 /*!
522    \brief Retrieves coordinates for lighting model position, at center of view
523 
524    Position at nearclip * 2: tried nearclip + siz, but since need to
525    know position to calculate size, have two dependent variables
526    (nearclip * 2) from eye.
527 
528    \param siz[out] size
529    \param pos[out] coordinates (X, Y, Z)
530  */
GS_get_modelposition(float * siz,float * pos)531 void GS_get_modelposition(float *siz, float *pos)
532 {
533     float dist, near_h, dir[3];
534 
535     dist = 2. * Gd.nearclip;
536 
537     near_h = 2.0 * tan(4.0 * atan(1.) * Gv.fov / 3600.) * dist;
538     *siz = near_h / 8.0;
539 
540     /* prevent clipping - would only happen if fov > ~127 degrees, at
541        fov = 2.0 * atan(2.0) */
542 
543     if (*siz > Gd.nearclip) {
544 	*siz = Gd.nearclip;
545     }
546 
547     GS_v3dir(Gv.from_to[FROM], Gv.from_to[TO], dir);
548 
549     pos[X] = Gv.from_to[FROM][X] + dir[X] * dist;
550     pos[Y] = Gv.from_to[FROM][Y] + dir[Y] * dist;
551     pos[Z] = Gv.from_to[FROM][Z] + dir[Z] * dist;
552 
553     return;
554 }
555 
556 
557 /*!
558    \brief Set decoration, north arrow ??
559 
560    \todo scale used to calculate len of arrow still needs work
561    needs go function that returns center / eye distance
562    gsd_get_los function is not working correctly ??
563 
564    \param pt point value in true world coordinates (?)
565    \param id surface id
566    \param[out] pos2 output coordinates
567  */
GS_set_Narrow(int * pt,int id,float * pos2)568 void GS_set_Narrow(int *pt, int id, float *pos2)
569 {
570     geosurf *gs;
571     float x, y, z;
572     GLdouble modelMatrix[16], projMatrix[16];
573     GLint viewport[4];
574 
575     if (GS_get_selected_point_on_surface(pt[X], pt[Y], &id, &x, &y, &z)) {
576 	gs = gs_get_surf(id);
577 	if (gs) {
578 	    z = gs->zmax;
579 	    pos2[X] = (float)x - gs->ox + gs->x_trans;
580 	    pos2[Y] = (float)y - gs->oy + gs->y_trans;
581 	    pos2[Z] = (float)z + gs->z_trans;
582 
583 	    return;
584 	}
585     }
586     else {
587 	gs = gs_get_surf(id);
588 
589 	/* Need to get model matrix, etc
590 	 * to run gluUnProject
591 	 */
592 	gsd_pushmatrix();
593 	gsd_do_scale(1);
594 	glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
595 	glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
596 	glGetIntegerv(GL_VIEWPORT, viewport);
597 
598 	if (gs) {
599 	    GLdouble out_near[3], out_far[3];
600 	    GLdouble factor;
601 	    GLdouble out[3];
602 
603 	    z = (float)gs->zmax + gs->z_trans;
604 
605 	    gluUnProject((GLdouble) pt[X], (GLdouble) pt[Y], (GLdouble) 0.,
606 			 modelMatrix, projMatrix, viewport,
607 			 &out_near[X], &out_near[Y], &out_near[Z]);
608 	    gluUnProject((GLdouble) pt[X], (GLdouble) pt[Y], (GLdouble) 1.,
609 			 modelMatrix, projMatrix, viewport,
610 			 &out_far[X], &out_far[Y], &out_far[Z]);
611 
612 	    glPopMatrix();
613 
614 	    factor = (out_near[Z] - z) / (out_near[Z] - out_far[Z]);
615 
616 	    out[X] = out_near[X] - ((out_near[X] - out_far[X]) * factor);
617 	    out[Y] = out_near[Y] - ((out_near[Y] - out_far[Y]) * factor);
618 	    out[Z] = z;
619 
620 	    pos2[X] = (float)out[X];
621 	    pos2[Y] = (float)out[Y];
622 	    pos2[Z] = (float)out[Z];
623 
624 	    return;
625 
626 	}
627     }
628     return;
629 }
630 
631 /*!
632    \brief Draw place marker
633 
634    Used to display query point for raster queries.
635 
636    \param id surface id
637    \param pt point, X, Y value in true world coordinates
638  */
GS_draw_X(int id,float * pt)639 void GS_draw_X(int id, float *pt)
640 {
641     geosurf *gs;
642     Point3 pos;
643     float siz;
644     gvstyle style;
645 
646     if ((gs = gs_get_surf(id))) {
647 	GS_get_longdim(&siz);
648 	style.size = siz / 200.;
649 	pos[X] = pt[X] - gs->ox;
650 	pos[Y] = pt[Y] - gs->oy;
651 	_viewcell_tri_interp(gs, pos);
652 
653 	gsd_pushmatrix();
654 
655 	gsd_do_scale(1);
656 	gsd_translate(gs->x_trans, gs->y_trans, gs->z_trans);
657 	gsd_linewidth(1);
658 
659 	if (CONST_ATT == gs_get_att_src(gs, ATT_TOPO)) {
660 	    pos[Z] = gs->att[ATT_TOPO].constant;
661 	    gs = NULL;		/* tells gpd_obj to use given Z val */
662 	}
663 	style.color = Gd.bgcol;
664 	style.symbol = ST_GYRO;
665 	gpd_obj(gs, &style, pos);
666 	gsd_flush();
667 
668 	gsd_popmatrix();
669     }
670 
671     return;
672 }
673 
674 /*!
675    \brief Draw line on surface
676 
677    \param id surface id
678    \param x1,y1,x2,y2 line nodes
679  */
GS_draw_line_onsurf(int id,float x1,float y1,float x2,float y2)680 void GS_draw_line_onsurf(int id, float x1, float y1, float x2, float y2)
681 {
682     float p1[2], p2[2];
683     geosurf *gs;
684 
685     if ((gs = gs_get_surf(id))) {
686 	p1[X] = x1 - gs->ox;
687 	p1[Y] = y1 - gs->oy;
688 	p2[X] = x2 - gs->ox;
689 	p2[Y] = y2 - gs->oy;
690 
691 	gsd_pushmatrix();
692 
693 	gsd_do_scale(1);
694 	gsd_translate(gs->x_trans, gs->y_trans, gs->z_trans);
695 	gsd_linewidth(1);
696 
697 	gsd_color_func(GS_default_draw_color());
698 	gsd_line_onsurf(gs, p1, p2);
699 
700 	gsd_popmatrix();
701 	gsd_flush();
702     }
703 
704     return;
705 }
706 
707 /*!
708    \brief Draw multiline on surface
709 
710    Like above but limits points in line to n or points found in segment,
711    whichever is smaller.
712 
713    \param id surface id
714    \param x1,y1,x2,y2 line nodes
715 
716    \return number of points used
717  */
GS_draw_nline_onsurf(int id,float x1,float y1,float x2,float y2,float * lasp,int n)718 int GS_draw_nline_onsurf(int id, float x1, float y1, float x2, float y2,
719 			 float *lasp, int n)
720 {
721     float p1[2], p2[2];
722     geosurf *gs;
723     int ret = 0;
724 
725     if ((gs = gs_get_surf(id))) {
726 	p1[X] = x1 - gs->ox;
727 	p1[Y] = y1 - gs->oy;
728 	p2[X] = x2 - gs->ox;
729 	p2[Y] = y2 - gs->oy;
730 
731 	gsd_pushmatrix();
732 
733 	gsd_do_scale(1);
734 	gsd_translate(gs->x_trans, gs->y_trans, gs->z_trans);
735 	gsd_linewidth(1);
736 	gsd_color_func(GS_default_draw_color());
737 	ret = gsd_nline_onsurf(gs, p1, p2, lasp, n);
738 	gsd_surf2real(gs, lasp);
739 
740 	gsd_popmatrix();
741 	gsd_flush();
742     }
743 
744     return (ret);
745 }
746 
747 /*!
748    \brief Draw flow-line on surace
749 
750    This is slow - should be moved to gs_ but GS_ good for testing
751    and useful for app programmer
752 
753    \param id surface id
754    \param x,y coordinates of flow-line
755  */
GS_draw_flowline_at_xy(int id,float x,float y)756 void GS_draw_flowline_at_xy(int id, float x, float y)
757 {
758     geosurf *gs;
759     float nv[3], pdir[2], mult;
760     float p1[2], p2[2], next[2];
761     int i = 0;
762 
763     if ((gs = gs_get_surf(id))) {
764 	p1[X] = x;
765 	p1[Y] = y;
766 	/* multiply by 1.5 resolutions to ensure a crossing ? */
767 	mult = .1 * (VXRES(gs) > VYRES(gs) ? VXRES(gs) : VYRES(gs));
768 
769 	GS_coordpair_repeats(p1, p1, 50);
770 
771 	while (1 == GS_get_norm_at_xy(id, p1[X], p1[Y], nv)) {
772 	    if (nv[Z] == 1.0) {
773 		if (pdir[X] == 0.0 && pdir[Y] == 0.0) {
774 		    break;
775 		}
776 
777 		p2[X] = p1[X] + (pdir[X] * mult);
778 		p2[Y] = p1[Y] + (pdir[Y] * mult);
779 	    }
780 	    else {
781 		/* use previous direction */
782 		GS_v2norm(nv);
783 		p2[X] = p1[X] + (nv[X] * mult);
784 		p2[Y] = p1[Y] + (nv[Y] * mult);
785 		pdir[X] = nv[X];
786 		pdir[Y] = nv[Y];
787 	    }
788 
789 	    if (i > 2000) {
790 		break;
791 	    }
792 
793 	    if (GS_coordpair_repeats(p1, p2, 0)) {
794 		break;
795 	    }
796 
797 	    /* Think about this: */
798 	    /* degenerate line means edge or level edge ? */
799 	    /* next is filled with last point drawn */
800 	    if (2 > GS_draw_nline_onsurf(id, p1[X], p1[Y],
801 					 p2[X], p2[Y], next, 3)) {
802 		break;
803 	    }
804 
805 	    p1[X] = next[X];
806 	    p1[Y] = next[Y];
807 	}
808 
809 	G_debug(3, "GS_draw_flowline_at_xy(): dir: %f %f", nv[X], nv[Y]);
810     }
811 
812     return;
813 }
814 
815 /*!
816    \brief Draw fringe around data (surface) at selected corners
817 
818    \param id surface id
819    \param clr color
820    \param elev elevation value
821    \param where nw/ne/sw/se edges - 0 (turn off) 1 (turn on)
822  */
GS_draw_fringe(int id,unsigned long clr,float elev,int * where)823 void GS_draw_fringe(int id, unsigned long clr, float elev, int *where)
824 {
825     geosurf *gs;
826 
827     G_debug(3, "GS_draw_fringe(): id: %d clr: %ld elev %f edges: %d %d %d %d",
828 	    id, clr, elev, where[0], where[1], where[2], where[3]);
829     if ((gs = gs_get_surf(id)))
830 	gsd_display_fringe(gs, clr, elev, where);
831 
832 }
833 
834 
835 /*!
836    \brief Draw legend
837 
838    \todo add legend from list option
839    make font loading more flexible
840 
841    \param name legend name
842    \param fontbase font-base
843    \param size ?
844    \param flags legend flags
845    \param range values range
846    \param pt ?
847  */
GS_draw_legend(const char * name,GLuint fontbase,int size,int * flags,float * range,int * pt)848 int GS_draw_legend(const char *name, GLuint fontbase, int size, int *flags,
849 		   float *range, int *pt)
850 {
851     int list_no;
852 
853     list_no = gsd_put_legend(name, fontbase, size, flags, range, pt);
854 
855     return (list_no);
856 }
857 
858 /*!
859    \brief Draw pre-defined list
860 
861    Uses glFlush() to ensure all drawing is complete
862    before returning
863 
864    \param list_id list id
865  */
GS_draw_list(GLuint list_id)866 void GS_draw_list(GLuint list_id)
867 {
868     gsd_calllist(list_id);
869     glFlush();
870     return;
871 }
872 
873 /*!
874    \brief Draw all glLists
875 
876    Uses glFlush() to ensure all drawing is complete
877    before returning
878  */
GS_draw_all_list(void)879 void GS_draw_all_list(void)
880 {
881     gsd_calllists(0);		/* not sure if 0 is right - MN */
882     glFlush();
883     return;
884 }
885 
886 /*!
887    \brief Delete pre-defined list
888 
889    \param list_id list id
890  */
GS_delete_list(GLuint list_id)891 void GS_delete_list(GLuint list_id)
892 {
893     gsd_deletelist(list_id, 1);
894 
895     return;
896 }
897 
898 /*!
899    \brief Draw lighting model
900  */
GS_draw_lighting_model1(void)901 void GS_draw_lighting_model1(void)
902 {
903     static float center[3];
904     float tcenter[3];
905 
906     if (!Modelshowing) {
907 	GS_get_modelposition1(center);
908     }
909 
910     GS_v3eq(tcenter, center);
911 
912     gsd_zwritemask(0x0);
913     gsd_backface(1);
914 
915     gsd_colormode(CM_AD);
916     gsd_shademodel(DM_GOURAUD);
917     gsd_pushmatrix();
918     gsd_do_scale(1);
919 
920     if (Gv.vert_exag) {
921 	tcenter[Z] *= Gv.vert_exag;
922 	gsd_scale(1.0, 1.0, 1. / Gv.vert_exag);
923     }
924 
925     gsd_drawsphere(tcenter, 0xDDDDDD, (float)(Longdim / 10.));
926     gsd_popmatrix();
927     Modelshowing = 1;
928 
929     gsd_backface(0);
930     gsd_zwritemask(0xffffffff);
931 
932     return;
933 }
934 
935 /*!
936    \brief Draw lighting model
937 
938    Just turn off any cutting planes and draw it just outside near
939    clipping plane, since lighting is infinite now
940  */
GS_draw_lighting_model(void)941 void GS_draw_lighting_model(void)
942 {
943     static float center[3], size;
944     float tcenter[3], tsize;
945     int i, wason[MAX_CPLANES];
946 
947     gsd_get_cplanes_state(wason);
948 
949     for (i = 0; i < MAX_CPLANES; i++) {
950 	if (wason[i]) {
951 	    gsd_cplane_off(i);
952 	}
953     }
954 
955 
956     if (!Modelshowing) {
957 	GS_get_modelposition(&size, center);
958     }
959 
960     GS_v3eq(tcenter, center);
961     tsize = size;
962 
963     gsd_zwritemask(0x0);
964     gsd_backface(1);
965 
966     gsd_colormode(CM_DIFFUSE);
967     gsd_shademodel(DM_GOURAUD);
968     gsd_pushmatrix();
969     gsd_drawsphere(tcenter, 0xDDDDDD, tsize);
970     gsd_popmatrix();
971     Modelshowing = 1;
972 
973     gsd_backface(0);
974     gsd_zwritemask(0xffffffff);
975 
976     for (i = 0; i < MAX_CPLANES; i++) {
977 	if (wason[i]) {
978 	    gsd_cplane_on(i);
979 	}
980     }
981 
982     gsd_flush();
983 
984     return;
985 }
986 
987 /*!
988    \brief Update current mask
989 
990    May be called to update total mask for a surface at convenient times
991    instead of waiting until ready to redraw surface
992 
993    \param id surface id
994 
995    \return ?
996  */
GS_update_curmask(int id)997 int GS_update_curmask(int id)
998 {
999     geosurf *gs;
1000 
1001     gs = gs_get_surf(id);
1002     return (gs_update_curmask(gs));
1003 }
1004 
1005 /*!
1006    \brief Check if point is masked ?
1007 
1008    \param id surface id
1009    \param pt point
1010 
1011    \return 1 masked
1012    \return 0 not masked
1013    \return -1 on error, invalid surface id
1014  */
GS_is_masked(int id,float * pt)1015 int GS_is_masked(int id, float *pt)
1016 {
1017     geosurf *gs;
1018     Point3 tmp;
1019 
1020     if ((gs = gs_get_surf(id))) {
1021 	tmp[X] = pt[X] - gs->ox;
1022 	tmp[Y] = pt[Y] - gs->oy;
1023 
1024 	return (gs_point_is_masked(gs, tmp));
1025     }
1026 
1027     return (-1);
1028 }
1029 
1030 /*!
1031    \brief Unset Scaled Difference surface
1032  */
GS_unset_SDsurf(void)1033 void GS_unset_SDsurf(void)
1034 {
1035     gsdiff_set_SDref(NULL);
1036     SDref_surf = 0;
1037 
1038     return;
1039 }
1040 
1041 /*!
1042    \brief Set surface as Scaled Difference surface
1043 
1044    \param id surface id
1045 
1046    \return 1 on success
1047    \return 0 on error, invalid surface id
1048  */
GS_set_SDsurf(int id)1049 int GS_set_SDsurf(int id)
1050 {
1051     geosurf *gs;
1052 
1053     if ((gs = gs_get_surf(id))) {
1054 	gsdiff_set_SDref(gs);
1055 	SDref_surf = id;
1056 
1057 	return (1);
1058     }
1059 
1060     return (0);
1061 }
1062 
1063 /*!
1064    \brief Set ?
1065 
1066    \param scale scale value
1067 
1068    \return 1
1069  */
GS_set_SDscale(float scale)1070 int GS_set_SDscale(float scale)
1071 {
1072     gsdiff_set_SDscale(scale);
1073 
1074     return (1);
1075 }
1076 
1077 /*!
1078    \brief Get ?
1079 
1080    \param[out] id ?
1081 
1082    \return 1 on success
1083    \return 0 on error
1084  */
GS_get_SDsurf(int * id)1085 int GS_get_SDsurf(int *id)
1086 {
1087     geosurf *gs;
1088 
1089     if ((gs = gsdiff_get_SDref())) {
1090 	*id = SDref_surf;
1091 
1092 	return (1);
1093     }
1094 
1095     return (0);
1096 }
1097 
1098 /*!
1099    \brief Get ?
1100 
1101    \param[out] scale value
1102 
1103    \return 1
1104  */
GS_get_SDscale(float * scale)1105 int GS_get_SDscale(float *scale)
1106 {
1107     *scale = gsdiff_get_SDscale();
1108 
1109     return (1);
1110 }
1111 
1112 /*!
1113    \brief Update normals
1114 
1115    \param id surface id
1116 
1117    \return ?
1118  */
GS_update_normals(int id)1119 int GS_update_normals(int id)
1120 {
1121     geosurf *gs;
1122 
1123     gs = gs_get_surf(id);
1124 
1125     return (gs_calc_normals(gs));
1126 }
1127 
1128 /*!
1129    \brief Get attributes
1130 
1131    \param id surface id
1132    \param att
1133    \param[out] set
1134    \param[out] constant
1135    \param[out] mapname
1136 
1137    \return 1 on success
1138    \return -1 on error (invalid surface id)
1139  */
GS_get_att(int id,int att,int * set,float * constant,char * mapname)1140 int GS_get_att(int id, int att, int *set, float *constant, char *mapname)
1141 {
1142     int src;
1143     geosurf *gs;
1144 
1145     gs = gs_get_surf(id);
1146     if (gs) {
1147 	if (-1 != (src = gs_get_att_src(gs, att))) {
1148 	    *set = src;
1149 
1150 	    if (src == CONST_ATT) {
1151 		*constant = gs->att[att].constant;
1152 	    }
1153 	    else if (src == MAP_ATT) {
1154 		strcpy(mapname, gsds_get_name(gs->att[att].hdata));
1155 	    }
1156 
1157 	    return (1);
1158 	}
1159 
1160 	return (-1);
1161     }
1162 
1163     return (-1);
1164 }
1165 
1166 /*!
1167    \brief Get surface category on given position
1168 
1169    Prints "no data" or a description (i.e., "coniferous forest") to
1170    <i>catstr</i>. Usually call after GS_get_selected_point_on_surface().
1171    Define <i>att</i> as MAP_ATT
1172 
1173    \todo Allocate catstr using G_store()
1174 
1175    \param id surface id
1176    \param att attribute id (MAP_ATT)
1177    \param catstr cat string (must be allocated, dim?)
1178    \param x,y real coordinates
1179 
1180    \return -1 if no category info or point outside of window
1181    \return 1 on success
1182 */
GS_get_cat_at_xy(int id,int att,char * catstr,float x,float y)1183 int GS_get_cat_at_xy(int id, int att, char *catstr, float x, float y)
1184 {
1185     int offset, drow, dcol, vrow, vcol;
1186     float ftmp, pt[3];
1187     typbuff *buff;
1188     geosurf *gs;
1189 
1190     *catstr = '\0';
1191     gs = gs_get_surf(id);
1192 
1193     if (NULL == gs) {
1194 	return -1;
1195     }
1196 
1197     pt[X] = x;
1198     pt[Y] = y;
1199 
1200     gsd_real2surf(gs, pt);
1201     if (gs_point_is_masked(gs, pt)) {
1202 	return -1;
1203     }
1204 
1205     if (!in_vregion(gs, pt)) {
1206 	return -1;
1207     }
1208 
1209     if (MAP_ATT != gs_get_att_src(gs, att)) {
1210 	sprintf(catstr, _("no category info"));
1211 	return -1;
1212     }
1213 
1214     buff = gs_get_att_typbuff(gs, att, 0);
1215 
1216     vrow = Y2VROW(gs, pt[Y]);
1217     vcol = X2VCOL(gs, pt[X]);
1218     drow = VROW2DROW(gs, vrow);
1219     dcol = VCOL2DCOL(gs, vcol);
1220 
1221     offset = DRC2OFF(gs, drow, dcol);
1222 
1223     if (GET_MAPATT(buff, offset, ftmp)) {
1224 	return
1225 	    (Gs_get_cat_label(gsds_get_name(gs->att[att].hdata),
1226 			      drow, dcol, catstr));
1227     }
1228 
1229     sprintf(catstr, _("no data"));
1230 
1231     return 1;
1232 }
1233 
1234 /*!
1235    \brief Get surface normal at x,y (real coordinates)
1236 
1237    Usually call after GS_get_selected_point_on_surface()
1238 
1239    \param id surface id
1240    \param x,y real coordinates
1241    \param[out] nv surface normal
1242 
1243    \return -1 if point outside of window or masked
1244    \return 1 on success
1245  */
GS_get_norm_at_xy(int id,float x,float y,float * nv)1246 int GS_get_norm_at_xy(int id, float x, float y, float *nv)
1247 {
1248     int offset, drow, dcol, vrow, vcol;
1249     float pt[3];
1250     geosurf *gs;
1251 
1252     gs = gs_get_surf(id);
1253 
1254     if (NULL == gs) {
1255 	return (-1);
1256     }
1257 
1258     if (gs->norm_needupdate) {
1259 	gs_calc_normals(gs);
1260     }
1261 
1262     pt[X] = x;
1263     pt[Y] = y;
1264 
1265     gsd_real2surf(gs, pt);
1266     if (gs_point_is_masked(gs, pt)) {
1267 	return (-1);
1268     }
1269 
1270     if (!in_vregion(gs, pt)) {
1271 	return (-1);
1272     }
1273 
1274     vrow = Y2VROW(gs, pt[Y]);
1275     vcol = X2VCOL(gs, pt[X]);
1276     drow = VROW2DROW(gs, vrow);
1277     dcol = VCOL2DCOL(gs, vcol);
1278 
1279     offset = DRC2OFF(gs, drow, dcol);
1280 
1281     if (gs->norms) {
1282 	FNORM(gs->norms[offset], nv);
1283     }
1284     else {
1285 	/* otherwise must be a constant */
1286 	nv[0] = 0.0;
1287 	nv[1] = 0.0;
1288 	nv[2] = 1.0;
1289     }
1290 
1291     return (1);
1292 }
1293 
1294 /*!
1295    \brief Get RGB color at given point
1296 
1297    Colors are translated to rgb and returned as Rxxx Gxxx Bxxx Usually
1298    call after GS_get_selected_point_on_surface().
1299 
1300    Prints NULL or the value (i.e., "921.5") to valstr
1301 
1302    \param id surface id
1303    \param att attribute id
1304    \param[out] valstr value string (allocated, dim?)
1305    \param x,y real coordinates
1306 
1307    \return -1 if point outside of window or masked
1308    \return 1 on success
1309  */
GS_get_val_at_xy(int id,int att,char * valstr,float x,float y)1310 int GS_get_val_at_xy(int id, int att, char *valstr, float x, float y)
1311 {
1312     int offset, drow, dcol, vrow, vcol;
1313     float ftmp, pt[3];
1314     typbuff *buff;
1315     geosurf *gs;
1316 
1317     *valstr = '\0';
1318     gs = gs_get_surf(id);
1319 
1320     if (NULL == gs) {
1321 	return -1;
1322     }
1323 
1324     pt[X] = x;
1325     pt[Y] = y;
1326 
1327     gsd_real2surf(gs, pt);
1328 
1329     if (gs_point_is_masked(gs, pt)) {
1330 	return -1;
1331     }
1332 
1333     if (!in_vregion(gs, pt)) {
1334 	return (-1);
1335     }
1336 
1337     if (CONST_ATT == gs_get_att_src(gs, att)) {
1338 	if (att == ATT_COLOR) {
1339 	    int r, g, b, i;
1340 
1341 	    i = gs->att[att].constant;
1342 	    sprintf(valstr, "R%d G%d B%d",
1343 		    INT_TO_RED(i, r), INT_TO_GRN(i, g), INT_TO_BLU(i, b));
1344 	}
1345 	else {
1346 	    sprintf(valstr, "%f", gs->att[att].constant);
1347 	}
1348 
1349 	return 1;
1350     }
1351     else if (MAP_ATT != gs_get_att_src(gs, att)) {
1352 	return -1;
1353     }
1354 
1355     buff = gs_get_att_typbuff(gs, att, 0);
1356 
1357     vrow = Y2VROW(gs, pt[Y]);
1358     vcol = X2VCOL(gs, pt[X]);
1359     drow = VROW2DROW(gs, vrow);
1360     dcol = VCOL2DCOL(gs, vcol);
1361 
1362     offset = DRC2OFF(gs, drow, dcol);
1363 
1364     if (GET_MAPATT(buff, offset, ftmp)) {
1365 	if (att == ATT_COLOR) {
1366 	    int r, g, b, i;
1367 
1368 	    i = gs_mapcolor(gs_get_att_typbuff(gs, ATT_COLOR, 0),
1369 			    &(gs->att[ATT_COLOR]), offset);
1370 	    sprintf(valstr, "R%d G%d B%d",
1371 		    INT_TO_RED(i, r), INT_TO_GRN(i, g), INT_TO_BLU(i, b));
1372 	}
1373 	else {
1374 	    sprintf(valstr, "%f", ftmp);
1375 	}
1376 
1377 	return (1);
1378     }
1379 
1380     sprintf(valstr, "NULL");
1381 
1382     return (1);
1383 }
1384 
1385 /*!
1386    \brief Unset attribute
1387 
1388    \param id surface id
1389    \param att attribute id
1390 
1391    \return ?
1392  */
GS_unset_att(int id,int att)1393 int GS_unset_att(int id, int att)
1394 {
1395     geosurf *gs;
1396 
1397     gs = gs_get_surf(id);
1398     gs->mask_needupdate = 1;
1399 
1400     return (gs_set_att_src(gs, att, NOTSET_ATT));
1401 }
1402 
1403 /*!
1404    \brief Set attribute constant
1405 
1406    \param id surface id
1407    \param att attribute id
1408    \param constant value
1409 
1410    \return ?
1411  */
GS_set_att_const(int id,int att,float constant)1412 int GS_set_att_const(int id, int att, float constant)
1413 {
1414     geosurf *gs;
1415     int ret;
1416 
1417     gs = gs_get_surf(id);
1418     ret = (gs_set_att_const(gs, att, constant));
1419 
1420     Gs_update_attrange(gs, att);
1421 
1422     return (ret);
1423 }
1424 
1425 /*!
1426    \brief Set mask mode
1427 
1428    Mask attribute special: constant is set to indicate invert or no
1429 
1430    \param id surface id
1431    \param mode id
1432 
1433    \return mode id
1434    \return -1 on error (invalid surface id)
1435  */
GS_set_maskmode(int id,int mode)1436 int GS_set_maskmode(int id, int mode)
1437 {
1438     geosurf *gs;
1439 
1440     gs = gs_get_surf(id);
1441 
1442     if (gs) {
1443 	gs->att[ATT_MASK].constant = mode;
1444 	gs->mask_needupdate = 1;
1445 
1446 	return (mode);
1447     }
1448 
1449     return (-1);
1450 }
1451 
1452 /*!
1453    \brief Get mask mode
1454 
1455    \param id surface id
1456    \param[out] mode id
1457 
1458    \return 1 on success
1459    \return -1 on error (invalid surface id)
1460  */
GS_get_maskmode(int id,int * mode)1461 int GS_get_maskmode(int id, int *mode)
1462 {
1463     geosurf *gs;
1464 
1465     gs = gs_get_surf(id);
1466 
1467     if (gs) {
1468 	*mode = gs->att[ATT_MASK].constant;
1469 
1470 	return (1);
1471     }
1472 
1473     return (-1);
1474 }
1475 
1476 /*!
1477    \brief Set client data
1478 
1479    \param id surface id
1480    \param clientd pointer to client data struct
1481 
1482    \return 1 on success
1483    \return -1 on error (invalid surface id)
1484  */
GS_Set_ClientData(int id,void * clientd)1485 int GS_Set_ClientData(int id, void *clientd)
1486 {
1487     geosurf *gs;
1488 
1489     gs = gs_get_surf(id);
1490     if (gs) {
1491 	gs->clientdata = clientd;
1492 
1493 	return (1);
1494     }
1495 
1496     return (-1);
1497 }
1498 
1499 /*!
1500    \brief Get client data
1501 
1502    \param id surface id
1503 
1504    \return pointer to client data
1505    \return NULL on error
1506  */
GS_Get_ClientData(int id)1507 void *GS_Get_ClientData(int id)
1508 {
1509     geosurf *gs;
1510 
1511     gs = gs_get_surf(id);
1512     if (gs) {
1513 	return (gs->clientdata);
1514     }
1515 
1516     return (NULL);
1517 }
1518 
1519 /*!
1520    \brief Get number of surfaces
1521 
1522    \return number of surfaces
1523  */
GS_num_surfs(void)1524 int GS_num_surfs(void)
1525 {
1526     return (gs_num_surfaces());
1527 }
1528 
1529 /*!
1530    \brief Get surface list
1531 
1532    Must be freed when not neeed!
1533 
1534    \param[out] numsurf number of available surfaces
1535 
1536    \return pointer to surface array
1537    \return NULL on error
1538  */
GS_get_surf_list(int * numsurfs)1539 int *GS_get_surf_list(int *numsurfs)
1540 {
1541     int i, *ret;
1542 
1543     *numsurfs = Next_surf;
1544 
1545     if (Next_surf) {
1546 	ret = (int *)G_malloc(Next_surf * sizeof(int));
1547 
1548 	for (i = 0; i < Next_surf; i++) {
1549 	    ret[i] = Surf_ID[i];
1550 	}
1551 
1552 	return (ret);
1553     }
1554 
1555     return (NULL);
1556 }
1557 
1558 /*!
1559    \brief Delete surface
1560 
1561    \param id surface id
1562 
1563    \return 1 on success
1564    \return -1 on error
1565  */
GS_delete_surface(int id)1566 int GS_delete_surface(int id)
1567 {
1568     int i, j, found;
1569 
1570     found = FALSE;
1571 
1572     G_debug(1, "GS_delete_surface(): id=%d", id);
1573 
1574     if (GS_surf_exists(id)) {
1575 	gs_delete_surf(id);
1576 	for (i = 0; i < Next_surf && !found; i++) {
1577 	    if (Surf_ID[i] == id) {
1578 		found = TRUE;
1579 
1580 		for (j = i; j < Next_surf; j++) {
1581 		    Surf_ID[j] = Surf_ID[j + 1];
1582 		}
1583 	    }
1584 	}
1585 
1586 	gv_update_drapesurfs();
1587 
1588 	if (found) {
1589 	    --Next_surf;
1590 	    return 1;
1591 	}
1592     }
1593 
1594     return -1;
1595 }
1596 
1597 
1598 /*!
1599    \brief Load raster map as attribute
1600 
1601    \param id surface id
1602    \param filename filename
1603    \param att attribute descriptor
1604 
1605    \return -1 on error (invalid surface id)
1606    \return ?
1607  */
GS_load_att_map(int id,const char * filename,int att)1608 int GS_load_att_map(int id, const char *filename, int att)
1609 {
1610     geosurf *gs;
1611     unsigned int changed;
1612     unsigned int atty;
1613     const char *mapset;
1614     struct Cell_head rast_head;
1615     int reuse, begin, hdata, ret, neg, has_null;
1616     typbuff *tbuff;
1617 
1618     G_debug(3, "GS_load_att_map(): map=%s", filename);
1619 
1620     reuse = ret = neg = has_null = 0;
1621     gs = gs_get_surf(id);
1622 
1623     if (NULL == gs) {
1624 	return -1;
1625     }
1626 
1627     gs->mask_needupdate = (ATT_MASK == att || ATT_TOPO == att ||
1628 			   (gs->nz_topo && ATT_TOPO == att) ||
1629 			   (gs->nz_color && ATT_COLOR == att));
1630 
1631     gs_set_att_src(gs, att, MAP_ATT);
1632 
1633     /* Check against maps already loaded in memory   */
1634     /* if to be color attribute:
1635        - if packed color for another surface, OK to reuse
1636        - if unchanged, ok to reuse IF it's of type char (will have lookup)
1637      */
1638     begin = hdata = 1;
1639 
1640     /* Get MAPSET to ensure names are fully qualified */
1641     mapset = G_find_raster2(filename, "");
1642     if (mapset == NULL) {
1643 	/* Check for valid filename */
1644 	G_warning("Raster map <%s> not found", filename);
1645 	return -1;
1646     }
1647 
1648     /* Check to see if map is in Region */
1649     Rast_get_cellhd(filename, mapset, &rast_head);
1650     if (rast_head.north <= wind.south ||
1651 	rast_head.south >= wind.north ||
1652 	rast_head.east <= wind.west || rast_head.west >= wind.east) {
1653 
1654 	G_warning(_("Raster map <%s> is outside of current region. Load failed."),
1655 		  G_fully_qualified_name(filename, mapset));
1656     }
1657 
1658     while (!reuse && (0 < hdata)) {
1659 	changed = CF_COLOR_PACKED;
1660 	atty = ATTY_FLOAT | ATTY_CHAR | ATTY_INT | ATTY_SHORT | ATTY_MASK;
1661 
1662 	if (0 < (hdata = gsds_findh(filename, &changed, &atty, begin))) {
1663 
1664 	    G_debug(3, "GS_load_att_map(): %s already has data handle %d.CF=%x",
1665 		    filename, hdata, changed);
1666 
1667 	    /* handle found */
1668 	    if (ATT_COLOR == att) {
1669 		if ((changed == CF_COLOR_PACKED) ||
1670 		    (!changed && atty == ATTY_CHAR)) {
1671 		    reuse = 1;
1672 		}
1673 	    }
1674 	    else if (atty == ATTY_MASK && att != ATT_MASK) {
1675 		reuse = 0;
1676 		/* should also free mask data & share new - but need backward
1677 		   reference? */
1678 	    }
1679 	    else if (!changed) {
1680 		reuse = 1;
1681 	    }
1682 	}
1683 
1684 	begin = 0;
1685     }
1686 
1687     if (reuse) {
1688 	gs->att[att].hdata = hdata;
1689 	gs_set_att_type(gs, att, atty);	/* ?? */
1690 
1691 	/* free lookup  & set to NULL! */
1692 	if (atty == ATTY_INT) {
1693 	    if (gs->att[att].lookup) {
1694 		free(gs->att[att].lookup);
1695 		gs->att[att].lookup = NULL;
1696 	    }
1697 	}
1698 	/* TODO: FIX THIS stuff with lookup sharing! */
1699 
1700 	G_debug(3, "GS_load_att_map(): %s is being reused. hdata=%d",
1701 		filename, hdata);
1702     }
1703     else {
1704 	G_debug(3, "GS_load_att_map(): %s not loaded in correct form - loading now",
1705 		filename);
1706 
1707 	/* not loaded - need to get new dataset handle */
1708 	gs->att[att].hdata = gsds_newh(filename);
1709 
1710 	tbuff = gs_get_att_typbuff(gs, att, 1);
1711 
1712 	/* TODO: Provide mechanism for loading certain attributes at
1713 	   specified sizes, allow scaling or capping, or scale non-zero */
1714 	if (ATT_MASK == att) {
1715 	    atty = ATTY_MASK;
1716 	}
1717 	else {
1718 	    atty = Gs_numtype(filename, &neg);
1719 	}
1720 
1721 #ifdef MAYBE_LATER
1722 	if (att == ATT_COLOR && atty == ATTY_SHORT) {
1723 	    atty = (neg ? ATTY_INT : ATTY_SHORT);
1724 	}
1725 #endif
1726 
1727 	if (att == ATT_COLOR && atty == ATTY_SHORT) {
1728 	    atty = ATTY_INT;
1729 	}
1730 
1731 	if (0 == gs_malloc_att_buff(gs, att, ATTY_NULL)) {
1732 	    G_fatal_error(_("GS_load_att_map(): Out of memory. Unable to load map"));
1733 	}
1734 
1735 	switch (atty) {
1736 	case ATTY_MASK:
1737 	    if (0 == gs_malloc_att_buff(gs, att, ATTY_MASK)) {
1738 		G_fatal_error(_("GS_load_att_map(): Out of memory. Unable to load map"));
1739 	    }
1740 
1741 	    ret = Gs_loadmap_as_bitmap(&wind, filename, tbuff->bm);
1742 
1743 	    break;
1744 	case ATTY_CHAR:
1745 	    if (0 == gs_malloc_att_buff(gs, att, ATTY_CHAR)) {
1746 		G_fatal_error(_("GS_load_att_map(): Out of memory. Unable to load map"));
1747 	    }
1748 
1749 	    ret = Gs_loadmap_as_char(&wind, filename, tbuff->cb,
1750 				     tbuff->nm, &has_null);
1751 
1752 	    break;
1753 	case ATTY_SHORT:
1754 	    if (0 == gs_malloc_att_buff(gs, att, ATTY_SHORT)) {
1755 		G_fatal_error(_("GS_load_att_map(): Out of memory. Unable to load map"));
1756 	    }
1757 
1758 	    ret = Gs_loadmap_as_short(&wind, filename, tbuff->sb,
1759 				      tbuff->nm, &has_null);
1760 	    break;
1761 	case ATTY_FLOAT:
1762 	    if (0 == gs_malloc_att_buff(gs, att, ATTY_FLOAT)) {
1763 		G_fatal_error(_("GS_load_att_map(): Out of memory. Unable to load map"));
1764 	    }
1765 
1766 	    ret = Gs_loadmap_as_float(&wind, filename, tbuff->fb,
1767 				      tbuff->nm, &has_null);
1768 
1769 	    break;
1770 	case ATTY_INT:
1771 	default:
1772 	    if (0 == gs_malloc_att_buff(gs, att, ATTY_INT)) {
1773 		G_fatal_error(_("GS_load_att_map(): Out of memory. Unable to load map"));
1774 	    }
1775 
1776 	    ret = Gs_loadmap_as_int(&wind, filename, tbuff->ib,
1777 				    tbuff->nm, &has_null);
1778 	    break;
1779 
1780 	}			/* Done with switch */
1781 
1782 	if (ret == -1) {
1783 	    gsds_free_data_buff(gs->att[att].hdata, ATTY_NULL);
1784 	    return -1;
1785 	}
1786 
1787 	G_debug(4, "  has_null=%d", has_null);
1788 
1789 	if (!has_null) {
1790 	    gsds_free_data_buff(gs->att[att].hdata, ATTY_NULL);
1791 	}
1792 	else {
1793 	    gs_update_curmask(gs);
1794 	}
1795 
1796     }				/* end if not reuse */
1797 
1798     if (ATT_COLOR == att) {
1799 #ifdef MAYBE_LATER
1800 	if (ATTY_INT == atty) {
1801 	    Gs_pack_colors(filename, tbuff->ib, gs->rows, gs->cols);
1802 	    gsds_set_changed(gs->att[att].hdata, CF_COLOR_PACKED);
1803 	    gs->att[att].lookup = NULL;
1804 	}
1805 	else {
1806 	    gs_malloc_lookup(gs, att);
1807 	    Gs_build_lookup(filename, gs->att[att].lookup);
1808 	}
1809 #else
1810 
1811 	if (ATTY_CHAR == atty) {
1812 	    if (!gs->att[att].lookup) {
1813 		/* might already exist if reusing */
1814 		gs_malloc_lookup(gs, att);
1815 		Gs_build_256lookup(filename, gs->att[att].lookup);
1816 	    }
1817 	}
1818 	else if (ATTY_FLOAT == atty) {
1819 	    if (!reuse) {
1820 		if (0 == gs_malloc_att_buff(gs, att, ATTY_INT)) {
1821 		    G_fatal_error(_("GS_load_att_map(): Out of memory. Unable to load map"));
1822 		}
1823 
1824 		Gs_pack_colors_float(filename, tbuff->fb, tbuff->ib,
1825 				     gs->rows, gs->cols);
1826 		gsds_set_changed(gs->att[att].hdata, CF_COLOR_PACKED);
1827 		gsds_free_data_buff(gs->att[att].hdata, ATTY_FLOAT);
1828 		gs->att[att].lookup = NULL;
1829 	    }
1830 	}
1831 	else {
1832 	    if (!reuse) {
1833 		Gs_pack_colors(filename, tbuff->ib, gs->rows, gs->cols);
1834 		gsds_set_changed(gs->att[att].hdata, CF_COLOR_PACKED);
1835 		gs->att[att].lookup = NULL;
1836 	    }
1837 	}
1838 #endif
1839     }
1840 
1841     if (ATT_TOPO == att) {
1842 	gs_init_normbuff(gs);
1843 	/* S_DIFF: should also check here to see if this surface is a
1844 	   reference surface for scaled differences, if so update references
1845 	   to it */
1846     }
1847 
1848     if (ret < 0) {
1849 	G_warning(_("Loading failed"));
1850     }
1851 
1852     if (-1 == Gs_update_attrange(gs, att)) {
1853 	G_warning(_("Error finding range"));
1854     }
1855 
1856     return ret;
1857 }
1858 
1859 /*!
1860    \brief Draw surface
1861 
1862    \param id surface id
1863  */
GS_draw_surf(int id)1864 void GS_draw_surf(int id)
1865 {
1866     geosurf *gs;
1867 
1868     G_debug(3, "GS_draw_surf(): id=%d", id);
1869 
1870     gs = gs_get_surf(id);
1871     if (gs) {
1872 	gsd_shademodel(gs->draw_mode & DM_GOURAUD);
1873 
1874 	if (gs->draw_mode & DM_POLY) {
1875 	    gsd_surf(gs);
1876 	}
1877 
1878 	if (gs->draw_mode & DM_WIRE) {
1879 	    gsd_wire_surf(gs);
1880 	}
1881 
1882 	/* TODO: write wire/poly draw routines */
1883 	if (gs->draw_mode & DM_WIRE_POLY) {
1884 	    gsd_surf(gs);
1885 	    gsd_wire_surf(gs);
1886 	}
1887     }
1888 
1889     return;
1890 }
1891 
1892 /*!
1893    \brief Draw surface wire
1894 
1895    Overrides draw_mode for fast display
1896 
1897    \param id surface id
1898  */
GS_draw_wire(int id)1899 void GS_draw_wire(int id)
1900 {
1901     geosurf *gs;
1902 
1903     G_debug(3, "GS_draw_wire(): id=%d", id);
1904 
1905     gs = gs_get_surf(id);
1906 
1907     if (gs) {
1908 	gsd_wire_surf(gs);
1909     }
1910 
1911     return;
1912 }
1913 
1914 /*!
1915    \brief Draw all wires
1916 
1917    Overrides draw_mode for fast display
1918  */
GS_alldraw_wire(void)1919 void GS_alldraw_wire(void)
1920 {
1921     geosurf *gs;
1922     int i;
1923 
1924     for (i = 0; i < Next_surf; i++) {
1925 	if ((gs = gs_get_surf(Surf_ID[i]))) {
1926 	    gsd_wire_surf(gs);
1927 	}
1928     }
1929 
1930     return;
1931 }
1932 
1933 /*!
1934    \brief Draw all surfaces
1935  */
GS_alldraw_surf(void)1936 void GS_alldraw_surf(void)
1937 {
1938     int i;
1939 
1940     for (i = 0; i < Next_surf; i++) {
1941 	GS_draw_surf(Surf_ID[i]);
1942     }
1943 
1944     return;
1945 }
1946 
1947 /*!
1948    \brief Set Z exag for surface
1949 
1950    \param id surface id
1951    \param exag z-exag value
1952  */
GS_set_exag(int id,float exag)1953 void GS_set_exag(int id, float exag)
1954 {
1955     geosurf *gs;
1956 
1957     G_debug(3, "GS_set_exag");
1958 
1959     gs = gs_get_surf(id);
1960 
1961     if (gs) {
1962 	if (gs->z_exag != exag) {
1963 	    gs->norm_needupdate = 1;
1964 	}
1965 
1966 	gs->z_exag = exag;
1967     }
1968 
1969     return;
1970 }
1971 
1972 /*!
1973    \brief Set global z-exag value
1974 
1975    \param exag exag value to be set up
1976  */
GS_set_global_exag(float exag)1977 void GS_set_global_exag(float exag)
1978 {
1979 
1980     G_debug(3, "GS_set_global_exag");
1981 
1982     Gv.vert_exag = exag;
1983     /* GL_NORMALIZE */
1984     /* Only need to update norms gs_norms.c
1985      * if exag is used in norm equation which
1986      * it is not! If GL_NORMALIZE is disabled
1987      * will need to include.
1988      gs_setall_norm_needupdate();
1989      */
1990 
1991     return;
1992 }
1993 
1994 /*!
1995    \brief Get global z-exag value
1996 
1997    \return value
1998  */
GS_global_exag(void)1999 float GS_global_exag(void)
2000 {
2001     G_debug(3, "GS_global_exag(): %g", Gv.vert_exag);
2002 
2003     return (Gv.vert_exag);
2004 }
2005 
2006 /*!
2007    \brief Set wire color
2008 
2009    \todo error-handling
2010 
2011    \param id surface id
2012    \param colr color value
2013  */
GS_set_wire_color(int id,int colr)2014 void GS_set_wire_color(int id, int colr)
2015 {
2016     geosurf *gs;
2017 
2018     G_debug(3, "GS_set_wire_color");
2019 
2020     gs = gs_get_surf(id);
2021 
2022     if (gs) {
2023 	gs->wire_color = colr;
2024     }
2025 
2026     return;
2027 }
2028 
2029 /*!
2030    \brief Get wire color
2031 
2032    \param id surface id
2033    \param[out] colr color value
2034 
2035    \return 1 on success
2036    \return -1 on error
2037  */
GS_get_wire_color(int id,int * colr)2038 int GS_get_wire_color(int id, int *colr)
2039 {
2040     geosurf *gs;
2041 
2042     gs = gs_get_surf(id);
2043 
2044     if (gs) {
2045 	*colr = gs->wire_color;
2046 
2047 	return (1);
2048     }
2049 
2050     return (-1);
2051 }
2052 
2053 /*!
2054    \brief Set all draw-modes
2055 
2056    \param mode mode id
2057 
2058    \return 0 on success
2059    \return -1 on error
2060  */
GS_setall_drawmode(int mode)2061 int GS_setall_drawmode(int mode)
2062 {
2063     int i;
2064 
2065     for (i = 0; i < Next_surf; i++) {
2066 	if (0 != GS_set_drawmode(Surf_ID[i], mode)) {
2067 	    return (-1);
2068 	}
2069     }
2070 
2071     return (0);
2072 }
2073 
2074 /*!
2075    \brief Set draw mode
2076 
2077    \param id surface id
2078    \param mode mode type(s)
2079 
2080    \return 0 on success
2081    \return -1 on error (invalid surface id)
2082  */
GS_set_drawmode(int id,int mode)2083 int GS_set_drawmode(int id, int mode)
2084 {
2085     geosurf *gs;
2086 
2087     G_debug(3, "GS_set_drawmode(): id=%d mode=%d", id, mode);
2088 
2089     gs = gs_get_surf(id);
2090 
2091     if (gs) {
2092 	gs->draw_mode = mode;
2093 
2094 	return (0);
2095     }
2096 
2097     return (-1);
2098 }
2099 
2100 /*!
2101    \brief Get draw mode
2102 
2103    \param id surface id
2104    \param[out] mode mode id
2105 
2106    \return 1 on success
2107    \return -1 on error (invalid surface id)
2108  */
GS_get_drawmode(int id,int * mode)2109 int GS_get_drawmode(int id, int *mode)
2110 {
2111     geosurf *gs;
2112 
2113     gs = gs_get_surf(id);
2114 
2115     if (gs) {
2116 	*mode = gs->draw_mode;
2117 
2118 	return (1);
2119     }
2120 
2121     return (-1);
2122 }
2123 
2124 /*!
2125    \brief Set no-zero ?
2126 
2127    \param id surface id
2128    \param att attribute id
2129    \param mode mode id
2130  */
GS_set_nozero(int id,int att,int mode)2131 void GS_set_nozero(int id, int att, int mode)
2132 {
2133     geosurf *gs;
2134 
2135     G_debug(3, "GS_set_nozero");
2136 
2137     gs = gs_get_surf(id);
2138 
2139     if (gs) {
2140 	if (att == ATT_TOPO) {
2141 	    gs->nz_topo = mode;
2142 	    gs->mask_needupdate = 1;
2143 	}
2144 
2145 	if (att == ATT_COLOR) {
2146 	    gs->nz_color = mode;
2147 	    gs->mask_needupdate = 1;
2148 	}
2149     }
2150 
2151     return;
2152 }
2153 
2154 /*!
2155    \brief Get no-zero ?
2156 
2157    \param id surface id
2158    \param att attribute id
2159    \param[out] mode mode id
2160 
2161    \return -1 on error (invalid surface id)
2162    \return 1 on success
2163  */
GS_get_nozero(int id,int att,int * mode)2164 int GS_get_nozero(int id, int att, int *mode)
2165 {
2166     geosurf *gs;
2167 
2168     G_debug(3, "GS_set_nozero");
2169 
2170     gs = gs_get_surf(id);
2171 
2172     if (gs) {
2173 	if (att == ATT_TOPO) {
2174 	    *mode = gs->nz_topo;
2175 	}
2176 	else if (att == ATT_COLOR) {
2177 	    *mode = gs->nz_color;
2178 	}
2179 	else {
2180 	    return (-1);
2181 	}
2182 
2183 	return (1);
2184     }
2185 
2186     return (-1);
2187 }
2188 
2189 /*!
2190    \brief Set all draw resolutions
2191 
2192    \param xres,yres x/y resolution value
2193    \param xwire,ywire x/y wire value
2194 
2195    \return 0 on success
2196    \return -1 on error
2197  */
GS_setall_drawres(int xres,int yres,int xwire,int ywire)2198 int GS_setall_drawres(int xres, int yres, int xwire, int ywire)
2199 {
2200     int i;
2201 
2202     for (i = 0; i < Next_surf; i++) {
2203 	if (0 != GS_set_drawres(Surf_ID[i], xres, yres, xwire, ywire)) {
2204 	    return (-1);
2205 	}
2206     }
2207 
2208     return (0);
2209 }
2210 
2211 /*!
2212    \brief Set draw resolution for surface
2213 
2214    \param id surface id
2215    \param xres,yres x/y resolution value
2216    \param xwire,ywire x/y wire value
2217 
2218    \return -1 on error
2219    \return 0 on success
2220  */
GS_set_drawres(int id,int xres,int yres,int xwire,int ywire)2221 int GS_set_drawres(int id, int xres, int yres, int xwire, int ywire)
2222 {
2223     geosurf *gs;
2224 
2225     G_debug(3, "GS_set_drawres() id=%d xyres=%d/%d xywire=%d/%d",
2226 	    id, xres, yres, xwire, ywire);
2227 
2228     if (xres < 1 || yres < 1 || xwire < 1 || ywire < 1) {
2229 	return (-1);
2230     }
2231 
2232     gs = gs_get_surf(id);
2233 
2234     if (gs) {
2235 	if (gs->x_mod != xres || gs->y_mod != yres) {
2236 	    gs->norm_needupdate = 1;
2237 	}
2238 
2239 	gs->x_mod = xres;
2240 	gs->y_mod = yres;
2241 	gs->x_modw = xwire;
2242 	gs->y_modw = ywire;
2243     }
2244 
2245     return (0);
2246 }
2247 
2248 /*!
2249    \brief Get draw resolution of surface
2250 
2251    \param id surface id
2252    \param[out] xres,yres x/y resolution value
2253    \param[out] xwire,ywire x/y wire value
2254  */
GS_get_drawres(int id,int * xres,int * yres,int * xwire,int * ywire)2255 void GS_get_drawres(int id, int *xres, int *yres, int *xwire, int *ywire)
2256 {
2257     geosurf *gs;
2258 
2259     G_debug(3, "GS_get_drawres");
2260 
2261     gs = gs_get_surf(id);
2262 
2263     if (gs) {
2264 	*xres = gs->x_mod;
2265 	*yres = gs->y_mod;
2266 	*xwire = gs->x_modw;
2267 	*ywire = gs->y_modw;
2268     }
2269 
2270     return;
2271 }
2272 
2273 /*!
2274    \brief Get dimension of surface
2275 
2276    \param id surface id
2277    \param[out] rows,cols number of rows/cols
2278  */
GS_get_dims(int id,int * rows,int * cols)2279 void GS_get_dims(int id, int *rows, int *cols)
2280 {
2281     geosurf *gs;
2282 
2283     gs = gs_get_surf(id);
2284 
2285     if (gs) {
2286 	*rows = gs->rows;
2287 	*cols = gs->cols;
2288     }
2289 
2290     return;
2291 }
2292 
2293 /*!
2294    \brief Get exag-value guess
2295 
2296    Use no_zero range because if zero IS data, then range won't be that
2297    much off (it's just a GUESS, after all), but if zero is NO data, could
2298    drastically affect guess
2299 
2300    \param id surface id
2301    \param[out] exag exag value
2302 
2303    \return 1 on success
2304    \return -1 on error
2305  */
GS_get_exag_guess(int id,float * exag)2306 int GS_get_exag_guess(int id, float *exag)
2307 {
2308     geosurf *gs;
2309     float guess;
2310 
2311     gs = gs_get_surf(id);
2312     guess = 1.0;
2313 
2314     /* if gs is type const return guess = 1.0 */
2315     if (CONST_ATT == gs_get_att_src(gs, ATT_TOPO)) {
2316 	*exag = guess;
2317 	return (1);
2318     }
2319 
2320     if (gs) {
2321 	if (gs->zrange_nz == 0.0) {
2322 	    *exag = 0.0;
2323 
2324 	    return (1);
2325 	}
2326 
2327 	G_debug(3, "GS_get_exag_guess(): %f %f", gs->zrange_nz, Longdim);
2328 
2329 	while (gs->zrange_nz * guess / Longdim >= .25) {
2330 	    guess *= .1;
2331 
2332 	    G_debug(3, "GS_get_exag_guess(): %f", guess);
2333 	}
2334 
2335 	while (gs->zrange_nz * guess / Longdim < .025) {
2336 	    guess *= 10.;
2337 
2338 	    G_debug(3, "GS_get_exag_guess(): %f", guess);
2339 	}
2340 
2341 	*exag = guess;
2342 
2343 	return (1);
2344     }
2345 
2346     return (-1);
2347 }
2348 
2349 /*!
2350    \brief Get Z extents for all loaded surfaces
2351 
2352    Treating zeros as "no data"
2353 
2354    \param[out] min min value
2355    \param[out] max max value
2356  */
GS_get_zrange_nz(float * min,float * max)2357 void GS_get_zrange_nz(float *min, float *max)
2358 {
2359     int i, first = 1;
2360     geosurf *gs;
2361 
2362     for (i = 0; i < Next_surf; i++) {
2363 	if ((gs = gs_get_surf(Surf_ID[i]))) {
2364 	    if (first) {
2365 		first = 0;
2366 		*min = gs->zmin_nz;
2367 		*max = gs->zmax_nz;
2368 	    }
2369 
2370 	    if (gs->zmin_nz < *min) {
2371 		*min = gs->zmin_nz;
2372 	    }
2373 
2374 	    if (gs->zmax_nz > *max) {
2375 		*max = gs->zmax_nz;
2376 	    }
2377 	}
2378     }
2379 
2380     G_debug(3, "GS_get_zrange_nz(): min=%g max=%g", *min, *max);
2381 
2382     return;
2383 }
2384 
2385 /*!
2386    \brief Set translation (surface position)
2387 
2388    \param id surface id
2389    \param xtrans,ytrans,ztrans translation values
2390  */
GS_set_trans(int id,float xtrans,float ytrans,float ztrans)2391 void GS_set_trans(int id, float xtrans, float ytrans, float ztrans)
2392 {
2393     geosurf *gs;
2394 
2395     gs = gs_get_surf(id);
2396 
2397     if (gs) {
2398 	gs->x_trans = xtrans;
2399 	gs->y_trans = ytrans;
2400 	gs->z_trans = ztrans;
2401     }
2402 
2403     G_debug(3, "GS_set_trans(): id=%d, x=%f, y=%f, z=%f",
2404 	    id, xtrans, ytrans, ztrans);
2405 
2406     return;
2407 }
2408 
2409 /*!
2410    \brief Get translation values (surface position)
2411 
2412    \param id surface id
2413    \param[out] xtrans,ytrans,ztrans trans values
2414  */
GS_get_trans(int id,float * xtrans,float * ytrans,float * ztrans)2415 void GS_get_trans(int id, float *xtrans, float *ytrans, float *ztrans)
2416 {
2417     geosurf *gs;
2418 
2419     gs = gs_get_surf(id);
2420 
2421     if (gs) {
2422 	*xtrans = gs->x_trans;
2423 	*ytrans = gs->y_trans;
2424 	*ztrans = gs->z_trans;
2425     }
2426 
2427     G_debug(3, "GS_get_trans: id=%d, x=%f, y=%f, z=%f",
2428 	    id, *xtrans, *ytrans, *ztrans);
2429 
2430     return;
2431 }
2432 
2433 
2434 /*!
2435    \brief Get default draw color
2436 
2437    \return color value
2438  */
GS_default_draw_color(void)2439 unsigned int GS_default_draw_color(void)
2440 {
2441 
2442     G_debug(3, "GS_default_draw_color");
2443 
2444     return ((unsigned int)Gd.bgcol);
2445 }
2446 
2447 /*!
2448    \brief Get background color
2449 
2450    \return color value
2451  */
GS_background_color(void)2452 unsigned int GS_background_color(void)
2453 {
2454     return ((unsigned int)Gd.bgcol);
2455 }
2456 
2457 /*!
2458    \brief Sets which buffer to draw to
2459 
2460    \param where GSD_BOTH, GSD_FRONT, GSD_BACK
2461  */
GS_set_draw(int where)2462 void GS_set_draw(int where)
2463 {
2464     Buffermode = where;
2465 
2466     switch (where) {
2467     case GSD_BOTH:
2468 	gsd_bothbuffers();
2469 
2470 	break;
2471     case GSD_FRONT:
2472 	gsd_frontbuffer();
2473 
2474 	break;
2475     case GSD_BACK:
2476     default:
2477 	gsd_backbuffer();
2478 
2479 	break;
2480     }
2481 
2482     return;
2483 }
2484 
2485 /*
2486    \brief Ready to draw
2487  */
GS_ready_draw(void)2488 void GS_ready_draw(void)
2489 {
2490 
2491     G_debug(3, "GS_ready_draw");
2492 
2493     gsd_set_view(&Gv, &Gd);
2494 
2495     return;
2496 }
2497 
2498 /*!
2499    \brief Draw done, swap buffers
2500  */
GS_done_draw(void)2501 void GS_done_draw(void)
2502 {
2503 
2504     G_debug(3, "GS_done_draw");
2505 
2506     if (GSD_BACK == Buffermode) {
2507 	gsd_swapbuffers();
2508     }
2509 
2510     gsd_flush();
2511 
2512     return;
2513 }
2514 
2515 /*!
2516    \brief Set focus
2517 
2518    \param realto real coordinates to
2519  */
GS_set_focus(float * realto)2520 void GS_set_focus(float *realto)
2521 {
2522 
2523     G_debug(3, "GS_set_focus(): %f,%f,%f", realto[0], realto[1], realto[2]);
2524 
2525     Gv.infocus = 1;
2526     GS_v3eq(Gv.real_to, realto);
2527 
2528     gsd_set_view(&Gv, &Gd);
2529 
2530     return;
2531 }
2532 
2533 /*!
2534    \brief Set real focus
2535 
2536    \param realto real coordinates to
2537  */
GS_set_focus_real(float * realto)2538 void GS_set_focus_real(float *realto)
2539 {
2540 
2541     G_get_set_window(&wind);
2542     realto[X] = realto[X] - wind.west - (wind.ew_res / 2.);
2543     realto[Y] = realto[Y] - wind.south - (wind.ns_res / 2.);
2544 
2545     Gv.infocus = 1;
2546     GS_v3eq(Gv.real_to, realto);
2547 
2548     gsd_set_view(&Gv, &Gd);
2549 
2550     return;
2551 }
2552 
2553 
2554 /*!
2555    \brief Get focus
2556 
2557    OK to call with NULL argument if just want to check state
2558 
2559    \param realto real coordinates to
2560 
2561    \return ?
2562  */
GS_get_focus(float * realto)2563 int GS_get_focus(float *realto)
2564 {
2565 
2566     G_debug(3, "GS_get_focus");
2567 
2568     if (Gv.infocus) {
2569 	if (realto) {
2570 	    GS_v3eq(realto, Gv.real_to);
2571 	}
2572     }
2573 
2574     return (Gv.infocus);
2575 }
2576 
2577 /*!
2578    \brief Set focus to map center
2579 
2580    \param id surface id
2581  */
GS_set_focus_center_map(int id)2582 void GS_set_focus_center_map(int id)
2583 {
2584     float center[3];
2585     geosurf *gs;
2586 
2587     G_debug(3, "GS_set_focus_center_map");
2588 
2589     gs = gs_get_surf(id);
2590 
2591     if (gs) {
2592 	center[X] = (gs->xmax - gs->xmin) / 2.;
2593 	center[Y] = (gs->ymax - gs->ymin) / 2.;
2594 	center[Z] = (gs->zmax_nz + gs->zmin_nz) / 2.;
2595 
2596 	/* not yet working
2597 	   buff = gs_get_att_typbuff(gs, ATT_TOPO, 0);
2598 	   offset = gs->rows*gs->cols/2 + gs->cols/2;
2599 	   if (buff)
2600 	   {
2601 	   if (GET_MAPATT(buff, offset, tmp))
2602 	   {
2603 	   center[Z] = tmp;
2604 	   }
2605 	   }
2606 	 */
2607 
2608 	GS_set_focus(center);
2609     }
2610 }
2611 
2612 /*!
2613    \brief Move viewpoint
2614 
2615    \param pt 'from' model coordinates
2616  */
GS_moveto(float * pt)2617 void GS_moveto(float *pt)
2618 {
2619     float ft[3];
2620 
2621     G_debug(3, "GS_moveto(): %f,%f,%f", pt[0], pt[1], pt[2]);
2622 
2623     if (Gv.infocus) {
2624 	GS_v3eq(Gv.from_to[FROM], pt);
2625 	/*
2626 	   GS_v3eq(Gv.from_to[TO], Gv.real_to);
2627 	 */
2628 	GS_v3normalize(Gv.from_to[FROM], Gv.from_to[TO]);
2629 	/* update inclination, look_dir if we're keeping these */
2630     }
2631     else {
2632 	GS_v3eq(ft, Gv.from_to[TO]);
2633 	GS_v3sub(ft, Gv.from_to[FROM]);
2634 	GS_v3eq(Gv.from_to[FROM], pt);
2635 	GS_v3eq(Gv.from_to[TO], pt);
2636 	GS_v3add(Gv.from_to[TO], ft);
2637     }
2638 
2639     return;
2640 }
2641 
2642 /*!
2643    \brief Move position to (real)
2644 
2645    \param pt point real coordinates
2646  */
GS_moveto_real(float * pt)2647 void GS_moveto_real(float *pt)
2648 {
2649     gsd_real2model(pt);
2650     GS_moveto(pt);
2651 
2652     return;
2653 }
2654 
2655 /*!
2656    \brief Get z-extent for a single surface
2657 
2658    \param id surface id
2659    \param[out] min min z-value
2660    \param[out] max max z-value
2661    \param[out] mid middle z-value
2662 
2663    \return -1 on error (invalid surface id)
2664    \return ?
2665  */
GS_get_zextents(int id,float * min,float * max,float * mid)2666 int GS_get_zextents(int id, float *min, float *max, float *mid)
2667 {
2668     geosurf *gs;
2669 
2670     if (NULL == (gs = gs_get_surf(id))) {
2671 	return (-1);
2672     }
2673 
2674     G_debug(3, "GS_get_zextents(): id=%d", id);
2675 
2676     return (gs_get_zextents(gs, min, max, mid));
2677 }
2678 
2679 /*!
2680    \brief Get z-extent for all loaded surfaces
2681 
2682    \param[out] min min z-value
2683    \param[out] max max z-value
2684    \param doexag use z-exaggeration
2685 
2686    \return 1 on success
2687    \return -1 on error
2688  */
GS_get_zrange(float * min,float * max,int doexag)2689 int GS_get_zrange(float *min, float *max, int doexag)
2690 {
2691     int ret_surf, ret_vol;
2692     float surf_min, surf_max;
2693     float vol_min, vol_max;
2694 
2695     ret_surf = gs_get_zrange(&surf_min, &surf_max);
2696     ret_vol = gvl_get_zrange(&vol_min, &vol_max);
2697 
2698     if (ret_surf > 0 && ret_vol > 0) {
2699 	*min = (surf_min < vol_min) ? surf_min : vol_min;
2700 	*max = (surf_max < vol_max) ? surf_max : vol_max;
2701     }
2702     else if (ret_surf > 0) {
2703 	*min = surf_min;
2704 	*max = surf_max;
2705     }
2706     else if (ret_vol > 0) {
2707 	*min = vol_min;
2708 	*max = vol_max;
2709     }
2710 
2711     if (doexag) {
2712 	*min *= Gv.vert_exag;
2713 	*max *= Gv.vert_exag;
2714     }
2715 
2716     G_debug(3, "GS_get_zrange(): min=%g max=%g", *min, *max);
2717     return ((ret_surf > 0 || ret_vol > 0) ? (1) : (-1));
2718 }
2719 
2720 /*!
2721    \brief Get viewpoint 'from' position
2722 
2723    \param[out] fr from model coordinates
2724  */
GS_get_from(float * fr)2725 void GS_get_from(float *fr)
2726 {
2727     GS_v3eq(fr, Gv.from_to[FROM]);
2728 
2729     G_debug(3, "GS_get_from(): %f,%f,%f", fr[0], fr[1], fr[2]);
2730 
2731     return;
2732 }
2733 
2734 /*!
2735    \brief Get viewpoint 'from' real coordinates
2736 
2737    \param[out] fr 'from' real coordinates
2738  */
GS_get_from_real(float * fr)2739 void GS_get_from_real(float *fr)
2740 {
2741     GS_v3eq(fr, Gv.from_to[FROM]);
2742     gsd_model2real(fr);
2743 
2744     return;
2745 }
2746 
2747 /*!
2748    \brief Get 'to' real coordinates
2749 
2750    \param[out] to 'to' real coordinates
2751  */
GS_get_to_real(float * to)2752 void GS_get_to_real(float *to)
2753 {
2754     float realto[3];
2755 
2756     G_get_set_window(&wind);
2757     GS_get_focus(realto);
2758     to[X] = realto[X] + wind.west + (wind.ew_res / 2.);
2759     to[Y] = realto[Y] + wind.south + (wind.ns_res / 2.);
2760     to[Z] = realto[Z];
2761 
2762     return;
2763 }
2764 
2765 
2766 /*!
2767    \brief Get zoom setup
2768 
2769    \param[out] a,b,c,d current viewport settings
2770    \param[out] maxx,maxy max viewport size
2771  */
GS_zoom_setup(int * a,int * b,int * c,int * d,int * maxx,int * maxy)2772 void GS_zoom_setup(int *a, int *b, int *c, int *d, int *maxx, int *maxy)
2773 {
2774     GLint tmp[4];
2775     GLint num[2];
2776 
2777     gsd_getViewport(tmp, num);
2778     *a = tmp[0];
2779     *b = tmp[1];
2780     *c = tmp[2];
2781     *d = tmp[3];
2782     *maxx = num[0];
2783     *maxy = num[1];
2784 
2785     return;
2786 }
2787 
2788 /*!
2789    \brief Get 'to' model coordinates
2790 
2791    \todo need set_to? - just use viewdir?
2792 
2793    \param[out] to 'to' model coordinates
2794  */
GS_get_to(float * to)2795 void GS_get_to(float *to)
2796 {
2797     G_debug(3, "GS_get_to");
2798 
2799     GS_v3eq(to, Gv.from_to[TO]);
2800 
2801     return;
2802 }
2803 
2804 /*!
2805    \brief Get viewdir
2806 
2807    \param[out] dir viewdir value
2808  */
GS_get_viewdir(float * dir)2809 void GS_get_viewdir(float *dir)
2810 {
2811     GS_v3dir(Gv.from_to[FROM], Gv.from_to[TO], dir);
2812 
2813     return;
2814 }
2815 
2816 /*!
2817    \brief Set viewdir
2818 
2819    Automatically turns off focus
2820 
2821    \param dir viewdir value
2822  */
GS_set_viewdir(float * dir)2823 void GS_set_viewdir(float *dir)
2824 {
2825     float tmp[3];
2826 
2827     GS_v3eq(tmp, dir);
2828     GS_v3norm(tmp);
2829     GS_v3eq(Gv.from_to[TO], Gv.from_to[FROM]);
2830     GS_v3add(Gv.from_to[TO], tmp);
2831 
2832     GS_set_nofocus();
2833     gsd_set_view(&Gv, &Gd);
2834 
2835     return;
2836 }
2837 
2838 /*!
2839    \brief Set field of view
2840 
2841    \param fov fov value
2842  */
GS_set_fov(int fov)2843 void GS_set_fov(int fov)
2844 {
2845     Gv.fov = fov;
2846 
2847     return;
2848 }
2849 
2850 /*!
2851    \brief Get fied of view
2852 
2853    \return field of view, in 10ths of degrees
2854  */
GS_get_fov(void)2855 int GS_get_fov(void)
2856 {
2857     return (Gv.fov);
2858 }
2859 
2860 /*!
2861    \brief Get twist value
2862 
2863    10ths of degrees off twelve o'clock
2864  */
GS_get_twist(void)2865 int GS_get_twist(void)
2866 {
2867     return (Gv.twist);
2868 }
2869 
2870 /*!
2871    \brief Set viewpoint twist value
2872 
2873    10ths of degrees off twelve o'clock
2874 
2875    \param t tenths of degrees clockwise from 12:00.
2876  */
GS_set_twist(int t)2877 void GS_set_twist(int t)
2878 {
2879     Gv.twist = t;
2880 
2881     return;
2882 }
2883 
2884 /*!
2885    \brief Set rotation params
2886  */
GS_set_rotation(double angle,double x,double y,double z)2887 void GS_set_rotation(double angle, double x, double y, double z)
2888 {
2889     Gv.rotate.rot_angle = angle;
2890     Gv.rotate.rot_axes[0] = x;
2891     Gv.rotate.rot_axes[1] = y;
2892     Gv.rotate.rot_axes[2] = z;
2893     Gv.rotate.do_rot = 1;
2894 
2895     return;
2896 }
2897 
2898 /*!
2899    \brief Stop scene rotation
2900  */
GS_unset_rotation(void)2901 void GS_unset_rotation(void)
2902 {
2903     Gv.rotate.do_rot = 0;
2904 }
2905 
2906 /*!
2907    \brief Reset scene rotation
2908  */
GS_init_rotation(void)2909 void GS_init_rotation(void)
2910 {
2911     int i;
2912 
2913     for (i = 0; i < 16; i++) {
2914 	if (i == 0 || i == 5 || i == 10 || i == 15)
2915 	    Gv.rotate.rotMatrix[i] = 1.0;
2916 	else
2917 	    Gv.rotate.rotMatrix[i] = 0.0;
2918     }
2919     Gv.rotate.rot_angle = 0.0;
2920     Gv.rotate.rot_axes[0] = 0.0;
2921     Gv.rotate.rot_axes[1] = 0.0;
2922     Gv.rotate.rot_axes[2] = 0.0;
2923     Gv.rotate.do_rot = 0;
2924 
2925 }
2926 /*!
2927  * \brief Get rotation matrix
2928  */
GS_get_rotation_matrix(double * matrix)2929 void GS_get_rotation_matrix(double *matrix)
2930 {
2931     int i;
2932 
2933     for (i = 0; i < 16; i++) {
2934 	matrix[i] = Gv.rotate.rotMatrix[i];
2935     }
2936 }
2937 
2938 /*!
2939  * \brief Set rotation matrix
2940  */
GS_set_rotation_matrix(double * matrix)2941 void GS_set_rotation_matrix(double *matrix)
2942 {
2943     int i;
2944 
2945     for (i = 0; i < 16; i++) {
2946 	Gv.rotate.rotMatrix[i] = matrix[i];
2947     }
2948 }
2949 
2950 /*!
2951    \brief Unset focus
2952  */
GS_set_nofocus(void)2953 void GS_set_nofocus(void)
2954 {
2955     G_debug(3, "GS_set_nofocus");
2956 
2957     Gv.infocus = 0;
2958 
2959     return;
2960 }
2961 
2962 /*!
2963    \brief Set focus
2964 
2965    Make sure that the center of view is set
2966  */
GS_set_infocus(void)2967 void GS_set_infocus(void)
2968 {
2969     G_debug(3, "GS_set_infocus");
2970 
2971     Gv.infocus = 1;
2972 
2973     return;
2974 }
2975 
2976 /*!
2977    \brief Set viewport
2978 
2979    \param left,right,bottom,top viewport extent values
2980  */
GS_set_viewport(int left,int right,int bottom,int top)2981 void GS_set_viewport(int left, int right, int bottom, int top)
2982 {
2983     G_debug(3, "GS_set_viewport(): left=%d, right=%d, "
2984 	    "bottom=%d, top=%d", left, right, bottom, top);
2985 
2986     gsd_viewport(left, right, bottom, top);
2987 
2988     return;
2989 }
2990 
2991 /*!
2992    \brief Send screen coords sx and sy, lib traces through surfaces; sets
2993    new center to point of nearest intersection.
2994 
2995    If no intersection, uses line of sight with length of current view
2996    ray (eye to center) to set new center.
2997 
2998    Reset center of view to screen coordinates sx, sy.
2999 
3000    \param sx,sy screen coordinates
3001 
3002    \return 1 on success
3003    \return 0 on error (invalid surface id)
3004  */
GS_look_here(int sx,int sy)3005 int GS_look_here(int sx, int sy)
3006 {
3007     float x, y, z, len, los[2][3];
3008     Point3 realto, dir;
3009     int id;
3010     geosurf *gs;
3011 
3012     if (GS_get_selected_point_on_surface(sx, sy, &id, &x, &y, &z)) {
3013 	gs = gs_get_surf(id);
3014 	if (gs) {
3015 	    realto[X] = x - gs->ox + gs->x_trans;
3016 	    realto[Y] = y - gs->oy + gs->y_trans;
3017 	    realto[Z] = z + gs->z_trans;
3018 	    GS_set_focus(realto);
3019 
3020 	    return (1);
3021 	}
3022     }
3023     else {
3024 	if (gsd_get_los(los, (short)sx, (short)sy)) {
3025 	    len = GS_distance(Gv.from_to[FROM], Gv.real_to);
3026 	    GS_v3dir(los[FROM], los[TO], dir);
3027 	    GS_v3mult(dir, len);
3028 	    realto[X] = Gv.from_to[FROM][X] + dir[X];
3029 	    realto[Y] = Gv.from_to[FROM][Y] + dir[Y];
3030 	    realto[Z] = Gv.from_to[FROM][Z] + dir[Z];
3031 	    GS_set_focus(realto);
3032 
3033 	    return (1);
3034 	}
3035     }
3036 
3037     return (0);
3038 }
3039 
3040 /*!
3041    \brief Get selected point of surface
3042 
3043    Given screen coordinates sx and sy, find closest intersection of
3044    view ray with surfaces and return coordinates of intersection in x, y,
3045    z, and identifier of surface in id.
3046 
3047    \param sx,sy screen coordinates
3048    \param[out] id surface id
3049    \param[out] x,y,z point on surface (model coordinates?)
3050 
3051    \returns 0 if no intersections found
3052    \return number of intersections
3053  */
GS_get_selected_point_on_surface(int sx,int sy,int * id,float * x,float * y,float * z)3054 int GS_get_selected_point_on_surface(int sx, int sy, int *id, float *x,
3055 				     float *y, float *z)
3056 {
3057     float los[2][3], find_dist[MAX_SURFS], closest;
3058     Point3 point, tmp, finds[MAX_SURFS];
3059     int surfs[MAX_SURFS], i, iclose, numhits = 0;
3060     geosurf *gs;
3061 
3062     /* returns surface-world coords */
3063     gsd_get_los(los, (short)sx, (short)sy);
3064 
3065     if (!gs_setlos_enterdata(los)) {
3066 	G_debug(3, "gs_setlos_enterdata(los): returns false");
3067 	return (0);
3068     }
3069 
3070     for (i = 0; i < Next_surf; i++) {
3071 	G_debug(3, "id=%d", i);
3072 
3073 	gs = gs_get_surf(Surf_ID[i]);
3074 
3075 	/* los_intersect expects surf-world coords (xy transl, no scaling) */
3076 
3077 #if NVIZ_HACK
3078 	if (gs_los_intersect1(Surf_ID[i], los, point)) {
3079 #else
3080 	if (gs_los_intersect(Surf_ID[i], los, point)) {
3081 #endif
3082 	    if (!gs_point_is_masked(gs, point)) {
3083 		GS_v3eq(tmp, point);
3084 		tmp[X] += gs->x_trans;
3085 		tmp[Y] += gs->y_trans;
3086 		tmp[Z] += gs->z_trans;
3087 		find_dist[numhits] = GS_distance(los[FROM], tmp);
3088 		gsd_surf2real(gs, point);
3089 		GS_v3eq(finds[numhits], point);
3090 		surfs[numhits] = Surf_ID[i];
3091 		numhits++;
3092 	    }
3093 	}
3094     }
3095 
3096     for (i = iclose = 0; i < numhits; i++) {
3097 	closest = find_dist[iclose];
3098 
3099 	if (find_dist[i] < closest) {
3100 	    iclose = i;
3101 	}
3102     }
3103 
3104     if (numhits) {
3105 	*x = finds[iclose][X];
3106 	*y = finds[iclose][Y];
3107 	*z = finds[iclose][Z];
3108 	*id = surfs[iclose];
3109     }
3110 
3111     G_debug(3, "NumHits %d, next %d", numhits, Next_surf);
3112 
3113     return (numhits);
3114 }
3115 
3116 /*!
3117    \brief Set cplace rotation
3118 
3119    \param num cplace id
3120    \param dx,dy,dz rotation values
3121  */
3122 void GS_set_cplane_rot(int num, float dx, float dy, float dz)
3123 {
3124     gsd_cplane_setrot(num, dx, dy, dz);
3125 
3126     return;
3127 }
3128 
3129 /*!
3130    \brief Set cplace trans
3131 
3132    \param num cplace id
3133    \param dx,dy,dz rotation values
3134  */
3135 void GS_set_cplane_trans(int num, float dx, float dy, float dz)
3136 {
3137     gsd_cplane_settrans(num, dx, dy, dz);
3138 
3139     return;
3140 }
3141 
3142 
3143 /*!
3144    \brief Draw cplace
3145 
3146    \param num cplace id
3147  */
3148 void GS_draw_cplane(int num)
3149 {
3150     geosurf *gsurfs[MAX_SURFS];
3151     int nsurfs;
3152 
3153     nsurfs = gs_num_surfaces();
3154     if (2 == nsurfs) {
3155 	/* testing */
3156 	gs_getall_surfaces(gsurfs);
3157 	gsd_draw_cplane_fence(gsurfs[0], gsurfs[1], num);
3158     }
3159     else {
3160 	gsd_draw_cplane(num);
3161     }
3162 
3163     return;
3164 }
3165 
3166 /*!
3167    \brief Draw cplace fence ?
3168 
3169    \param hs1,hs2
3170    \param num cplane id
3171 
3172    \return 0 on error
3173    \return 1 on success
3174  */
3175 int GS_draw_cplane_fence(int hs1, int hs2, int num)
3176 {
3177     geosurf *gs1, *gs2;
3178 
3179     if (NULL == (gs1 = gs_get_surf(hs1))) {
3180 	return (0);
3181     }
3182 
3183     if (NULL == (gs2 = gs_get_surf(hs2))) {
3184 	return (0);
3185     }
3186 
3187     gsd_draw_cplane_fence(gs1, gs2, num);
3188 
3189     return (1);
3190 }
3191 
3192 /*!
3193    \brief Draw all cplace fences ?
3194  */
3195 void GS_alldraw_cplane_fences(void)
3196 {
3197     int onstate[MAX_CPLANES], i;
3198 
3199     gsd_get_cplanes_state(onstate);
3200 
3201     for (i = 0; i < MAX_CPLANES; i++) {
3202 	if (onstate[i]) {
3203 	    GS_draw_cplane_fence(Surf_ID[0], Surf_ID[1], i);
3204 	}
3205     }
3206 
3207     return;
3208 }
3209 
3210 /*!
3211    \brief Set cplace
3212 
3213    \param num cplane id
3214  */
3215 void GS_set_cplane(int num)
3216 {
3217     gsd_cplane_on(num);
3218 
3219     return;
3220 }
3221 
3222 /*!
3223    \brief Unset clip place (turn off)
3224 
3225    \param num cplane id
3226  */
3227 void GS_unset_cplane(int num)
3228 {
3229     gsd_cplane_off(num);
3230 
3231     return;
3232 }
3233 
3234 /*!
3235    \brief Get axis scale
3236 
3237    \param sx,sy,sz x/y/z scale values
3238    \param doexag use vertical exaggeration
3239  */
3240 void GS_get_scale(float *sx, float *sy, float *sz, int doexag)
3241 {
3242     float zexag;
3243 
3244     zexag = doexag ? Gv.vert_exag : 1.;
3245     *sx = *sy = Gv.scale;
3246     *sz = Gv.scale * zexag;
3247 
3248     return;
3249 }
3250 
3251 /*!
3252    \brief Set fence color
3253 
3254    \param mode mode id
3255  */
3256 void GS_set_fencecolor(int mode)
3257 {
3258     gsd_setfc(mode);
3259 
3260     return;
3261 }
3262 
3263 /*!
3264    \brief Get fence color
3265 
3266    \return color value
3267  */
3268 int GS_get_fencecolor(void)
3269 {
3270     return gsd_getfc();
3271 }
3272 
3273 /*!
3274    \brief Measure distance "as the ball rolls" between two points on
3275    surface
3276 
3277    \param hs surface id
3278    \param x1,y1,x2,y2 two points on surface
3279    \param[out] dist measured distance
3280    \param use_exag use exag. surface
3281 
3282    \return 0 on error or if one or more points is not in region
3283    \return distance following terrain
3284  */
3285 int GS_get_distance_alongsurf(int hs, float x1, float y1, float x2, float y2,
3286 			      float *dist, int use_exag)
3287 {
3288     geosurf *gs;
3289     float p1[2], p2[2];
3290 
3291     gs = gs_get_surf(hs);
3292     if (gs == NULL) {
3293 	return 0;
3294     }
3295 
3296     p1[X] = x1;
3297     p1[Y] = y1;
3298     p2[X] = x2;
3299     p2[Y] = y2;
3300     gsd_real2surf(gs, p1);
3301     gsd_real2surf(gs, p2);
3302 
3303     G_debug(3, "GS_get_distance_alongsurf(): hs=%d p1=%f,%f p2=%f,%f",
3304 	    hs, x1, y1, x2, y2);
3305     return gs_distance_onsurf(gs, p1, p2, dist, use_exag);
3306 }
3307 
3308 /*!
3309    \brief Save 3d view
3310 
3311    \param vname view file name
3312    \param surfid surface id
3313 
3314    \return ?
3315  */
3316 int GS_save_3dview(const char *vname, int surfid)
3317 {
3318     return (Gs_save_3dview(vname, &Gv, &Gd, &wind, gs_get_surf(surfid)));
3319 }
3320 
3321 /*!
3322    \brief Load 3d view
3323 
3324    \param vname view file name
3325    \param surfid surface id
3326 
3327    \return ?
3328  */
3329 int GS_load_3dview(const char *vname, int surfid)
3330 {
3331 
3332     return (Gs_load_3dview(vname, &Gv, &Gd, &wind, gs_get_surf(surfid)));
3333 
3334     /* what to do about lights - I guess, delete all &
3335        create any that exist in 3dview file */
3336 }
3337 
3338 /************************************************************************
3339 * Following routines use Graphics Library
3340 ************************************************************************/
3341 
3342 /*!
3343    \brief Init viewpoint
3344 
3345    \todo allow setting center?
3346  */
3347 void GS_init_view(void)
3348 {
3349     static int first = 1;
3350 
3351     G_debug(3, "GS_init_view");
3352 
3353     if (first) {
3354 	first = 0;
3355 	glMatrixMode(GL_MODELVIEW);
3356 
3357 	/* OGLXXX doublebuffer: use GLX_DOUBLEBUFFER in attriblist */
3358 	/* glxChooseVisual(*dpy, screen, *attriblist); */
3359 	/* OGLXXX
3360 	 * ZMIN not needed -- always 0.
3361 	 * ZMAX not needed -- always 1.
3362 	 * getgdesc other posiblilties:
3363 	 *      glxGetConfig();
3364 	 *      glxGetCurrentContext();
3365 	 *      glxGetCurrentDrawable();
3366 	 * GLint gdtmp;
3367 	 * getgdesc other posiblilties:
3368 	 *      glxGetConfig();
3369 	 *      glxGetCurrentContext();
3370 	 *      glxGetCurrentDrawable();
3371 	 * GLint gdtmp;
3372 	 * glDepthRange params must be scaled to [0, 1]
3373 	 */
3374 	glDepthRange(0.0, 1.0);
3375 	glEnable(GL_DEPTH_TEST);
3376 	glDepthFunc(GL_LEQUAL);
3377 	/* } */
3378 
3379 	/* replace these with something meaningful */
3380 	Gv.fov = 450;
3381 	Gv.twist = 0;
3382 
3383 	GS_init_rotation();
3384 
3385 	Gv.from_to[FROM][X] = Gv.from_to[FROM][Y] =
3386 	    Gv.from_to[FROM][Z] = GS_UNIT_SIZE / 2.;
3387 
3388 	Gv.from_to[TO][X] = GS_UNIT_SIZE / 2.;
3389 	Gv.from_to[TO][Y] = GS_UNIT_SIZE / 2.;
3390 	Gv.from_to[TO][Z] = 0.;
3391 	Gv.from_to[TO][W] = Gv.from_to[FROM][W] = 1.;
3392 
3393 	Gv.real_to[W] = 1.;
3394 	Gv.vert_exag = 1.;
3395 
3396 	GS_v3eq(Gv.real_to, Gv.from_to[TO]);
3397 	GS_v3normalize(Gv.from_to[FROM], Gv.from_to[TO]);
3398 
3399 	/*
3400 	   Gd.nearclip = 50;
3401 	   Gd.farclip = 10000.;
3402 	 */
3403 	Gd.nearclip = 10.;
3404 	Gd.farclip = 10000.;
3405 	Gd.aspect = (float)GS_get_aspect();
3406 
3407 	GS_set_focus(Gv.real_to);
3408     }
3409 
3410     return;
3411 }
3412 
3413 /*!
3414    \brief Clear view
3415 
3416    \param col color value
3417  */
3418 void GS_clear(int col)
3419 {
3420     G_debug(3, "GS_clear");
3421 
3422     col = col | 0xFF000000;
3423 
3424     /* OGLXXX
3425      * change glClearDepth parameter to be in [0, 1]
3426      * ZMAX not needed -- always 1.
3427      * getgdesc other posiblilties:
3428      *      glxGetConfig();
3429      *      glxGetCurrentContext();
3430      *      glxGetCurrentDrawable();
3431      * GLint gdtmp;
3432      */
3433     glClearDepth(1.0);
3434     glClearColor(((float)((col) & 0xff)) / 255.,
3435 		 (float)((col) >> 8 & 0xff) / 255.,
3436 		 (float)((col) >> 16 & 0xff) / 255.,
3437 		 (float)((col) >> 24 & 0xff) / 255.);
3438     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
3439 
3440     Gd.bgcol = col;
3441     Modelshowing = 0;
3442     gsd_flush();
3443 
3444     return;
3445 }
3446 
3447 /*!
3448    \brief Get aspect value
3449 
3450    \return aspect value
3451  */
3452 double GS_get_aspect(void)
3453 {
3454     int left, right, bottom, top;
3455     GLint tmp[4];
3456 
3457     /* OGLXXX
3458      * get GL_VIEWPORT:
3459      * You can probably do better than this.
3460      */
3461     glGetIntegerv(GL_VIEWPORT, tmp);
3462     left = tmp[0];
3463     right = tmp[0] + tmp[2] - 1;
3464     bottom = tmp[1];
3465     top = tmp[1] + tmp[3] - 1;
3466 
3467     G_debug(3, "GS_get_aspect(): left=%d, right=%d, top=%d, bottom=%d",
3468 	    left, right, top, bottom);
3469 
3470     return ((double)(right - left) / (top - bottom));
3471 }
3472 
3473 /*!
3474    \brief Check for transparency
3475 
3476    Disabled.
3477 
3478    \return 1
3479  */
3480 int GS_has_transparency(void)
3481 {
3482     /* OGLXXX
3483      * getgdesc other posiblilties:
3484      *      glxGetConfig();
3485      *      glxGetCurrentContext();
3486      *      glxGetCurrentDrawable();
3487      * GLint gdtmp;
3488      * blending is ALWAYS supported.
3489      * This function returns whether it is enabled.
3490      * return((glGetIntegerv(GL_BLEND, &gdtmp), gdtmp));
3491      */
3492 
3493     return (1);
3494 }
3495